mirror of
https://github.com/selfxyz/self.git
synced 2026-04-05 03:00:53 -04:00
committed by
GitHub
parent
2362be8b88
commit
451b99496f
@@ -4,3 +4,5 @@ ios/
|
||||
node_modules/
|
||||
src/assets/animations/
|
||||
witnesscalc/
|
||||
vendor/
|
||||
android/
|
||||
@@ -22,6 +22,7 @@
|
||||
"dependencies": {
|
||||
"@amplitude/analytics-react-native": "^1.4.7",
|
||||
"@ethersproject/shims": "^5.7.0",
|
||||
"@openpassport/zk-kit-lean-imt": "^0.0.6",
|
||||
"@openpassport/zk-kit-smt": "^0.0.1",
|
||||
"@peculiar/asn1-schema": "^2.3.15",
|
||||
"@peculiar/x509": "^1.12.3",
|
||||
|
||||
@@ -10,7 +10,7 @@ import USER_PROFILE from '../images/user_profile.png';
|
||||
import useUserStore from '../stores/userStore';
|
||||
import { bgGreen, textBlack } from '../utils/colors';
|
||||
import { confirmTap } from '../utils/haptic';
|
||||
import { sendRegisterPayload } from '../utils/proving/payload';
|
||||
import { sendDscPayload, sendRegisterPayload } from '../utils/proving/payload';
|
||||
import { formatAttribute, getFirstName, maskString } from '../utils/utils';
|
||||
|
||||
const NextScreen: React.FC = () => {
|
||||
@@ -135,7 +135,15 @@ const NextScreen: React.FC = () => {
|
||||
confirmTap();
|
||||
passportData && (await sendRegisterPayload(passportData));
|
||||
}}
|
||||
text="TEE PROVING"
|
||||
text="TEE PROVING REGISTER"
|
||||
Icon={<Cpu color={textBlack} />}
|
||||
/>
|
||||
<CustomButton
|
||||
onPress={async () => {
|
||||
confirmTap();
|
||||
passportData && (await sendDscPayload(passportData));
|
||||
}}
|
||||
text="TEE PROVING DSC"
|
||||
Icon={<Cpu color={textBlack} />}
|
||||
/>
|
||||
|
||||
|
||||
@@ -4,25 +4,10 @@ import { useNavigation } from '@react-navigation/native';
|
||||
import io, { Socket } from 'socket.io-client';
|
||||
import { Text, YStack } from 'tamagui';
|
||||
|
||||
// import {
|
||||
// DEVELOPMENT_MODE,
|
||||
// MAX_CERT_BYTES,
|
||||
// } from '../../../../common/src/constants/constants';
|
||||
import {
|
||||
ArgumentsProveOffChain,
|
||||
OpenPassportApp,
|
||||
} from '../../../../common/src/utils/appType';
|
||||
import {
|
||||
getCircuitNameOld,
|
||||
parseCertificateSimple,
|
||||
} from '../../../../common/src/utils/certificate_parsing/parseCertificateSimple';
|
||||
// import {
|
||||
// getCSCAFromSKI,
|
||||
// sendCSCARequest,
|
||||
// } from '../../../../common/src/utils/csca';
|
||||
// import { generateCircuitInputsDSC } from '../../../../common/src/utils/circuits/generateInputs';
|
||||
// import { buildAttestation } from '../../../../common/src/utils/openPassportAttestation';
|
||||
import { parsePassportData } from '../../../../common/src/utils/passports/passport_parsing/parsePassportData';
|
||||
import Disclosures from '../../components/Disclosures';
|
||||
import { PrimaryButton } from '../../components/buttons/PrimaryButton';
|
||||
import { BodyText } from '../../components/typography/BodyText';
|
||||
@@ -32,13 +17,10 @@ import useNavigationStore from '../../stores/navigationStore';
|
||||
import useUserStore from '../../stores/userStore';
|
||||
import { black, slate300, white } from '../../utils/colors';
|
||||
import { buttonTap } from '../../utils/haptic';
|
||||
// import { generateCircuitInputsInApp } from '../../utils/generateInputsInApp';
|
||||
// import { generateProof } from '../../utils/prover';
|
||||
import { CircuitName } from '../../utils/zkeyDownload';
|
||||
import { sendVcAndDisclosePayload } from '../../utils/proving/payload';
|
||||
|
||||
const ProveScreen: React.FC = () => {
|
||||
const { navigate } = useNavigation();
|
||||
const [generatingProof, setGeneratingProof] = useState(false);
|
||||
const selectedApp = useNavigationStore(
|
||||
state => state.selectedApp || { args: {} },
|
||||
) as OpenPassportApp;
|
||||
@@ -46,12 +28,10 @@ const ProveScreen: React.FC = () => {
|
||||
selectedApp.mode === 'register'
|
||||
? {}
|
||||
: (selectedApp.args as ArgumentsProveOffChain).disclosureOptions || {};
|
||||
const { isZkeyDownloading } = useNavigationStore();
|
||||
|
||||
const { setProofVerificationResult, passportData } = useUserStore();
|
||||
|
||||
const [socket, setSocket] = useState<Socket | null>(null);
|
||||
const [isConnecting, setIsConnecting] = useState(false);
|
||||
const [_socket, setSocket] = useState<Socket | null>(null);
|
||||
|
||||
if (!passportData) {
|
||||
return (
|
||||
@@ -61,25 +41,12 @@ const ProveScreen: React.FC = () => {
|
||||
);
|
||||
}
|
||||
|
||||
const { signatureAlgorithm } = parseCertificateSimple(passportData.dsc);
|
||||
const parsedPassportData = parsePassportData(passportData);
|
||||
const circuitName = getCircuitNameOld(
|
||||
selectedApp.mode,
|
||||
signatureAlgorithm,
|
||||
parsedPassportData.signedAttrHashFunction,
|
||||
);
|
||||
|
||||
const waitForSocketConnection = (socketInstance: Socket): Promise<void> => {
|
||||
return new Promise(resolve => {
|
||||
if (socketInstance.connected) {
|
||||
resolve();
|
||||
} else {
|
||||
socketInstance.once('connect', () => {
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
function onVerify() {
|
||||
buttonTap();
|
||||
sendVcAndDisclosePayload(passportData).catch(e =>
|
||||
console.log('Error sending VC and disclose payload', e),
|
||||
);
|
||||
}
|
||||
|
||||
function goToErrorScreen() {
|
||||
navigate('WrongProofScreen');
|
||||
@@ -159,101 +126,6 @@ const ProveScreen: React.FC = () => {
|
||||
};
|
||||
}, [selectedApp.userId]);
|
||||
|
||||
const handleProve = async () => {
|
||||
buttonTap();
|
||||
try {
|
||||
setIsConnecting(true);
|
||||
setGeneratingProof(true);
|
||||
|
||||
if (!socket) {
|
||||
throw new Error('Socket not initialized');
|
||||
}
|
||||
|
||||
await waitForSocketConnection(socket);
|
||||
setIsConnecting(false);
|
||||
|
||||
socket.emit('proof_generation_start', {
|
||||
sessionId: selectedApp.sessionId,
|
||||
});
|
||||
|
||||
// const inputs = await generateCircuitInputsInApp(
|
||||
// passportData,
|
||||
// selectedApp,
|
||||
// );
|
||||
// let attestation;
|
||||
// let proof;
|
||||
// let dscProof;
|
||||
|
||||
switch (selectedApp.mode) {
|
||||
case 'prove_onchain':
|
||||
case 'register':
|
||||
// const cscaInputs = generateCircuitInputsDSC(
|
||||
// dscSecret as string,
|
||||
// passportData.dsc,
|
||||
// MAX_CERT_BYTES,
|
||||
// selectedApp.devMode,
|
||||
// );
|
||||
// [dscProof, proof] = await Promise.all([
|
||||
// sendCSCARequest(cscaInputs),
|
||||
// generateProof(circuitName, inputs),
|
||||
// ]);
|
||||
// const cscaPem = getCSCAFromSKI(
|
||||
// authorityKeyIdentifier,
|
||||
// DEVELOPMENT_MODE,
|
||||
// );
|
||||
// const { signatureAlgorithm: signatureAlgorithmDsc } =
|
||||
// parseCertificateSimple(cscaPem);
|
||||
// attestation = buildAttestation({
|
||||
// mode: selectedApp.mode,
|
||||
// proof: proof.proof,
|
||||
// publicSignals: proof.publicSignals,
|
||||
// signatureAlgorithm: signatureAlgorithm,
|
||||
// hashFunction: parsedPassportData.signedAttrHashFunction,
|
||||
// userIdType: selectedApp.userIdType,
|
||||
// dscProof: (dscProof as any).proof,
|
||||
// dscPublicSignals: (dscProof as any).pub_signals,
|
||||
// signatureAlgorithmDsc: signatureAlgorithmDsc,
|
||||
// hashFunctionDsc: parsedPassportData.signedAttrHashFunction,
|
||||
// });
|
||||
// break;
|
||||
// default:
|
||||
// proof = await generateProof(circuitName, inputs);
|
||||
// attestation = buildAttestation({
|
||||
// userIdType: selectedApp.userIdType,
|
||||
// mode: selectedApp.mode,
|
||||
// proof: proof.proof,
|
||||
// publicSignals: proof.publicSignals,
|
||||
// signatureAlgorithm: signatureAlgorithm,
|
||||
// hashFunction: parsedPassportData.signedAttrHashFunction,
|
||||
// dsc: passportData.dsc,
|
||||
// });
|
||||
// break;
|
||||
}
|
||||
// console.log('\x1b[90mattestation\x1b[0m', attestation);
|
||||
// socket.emit('proof_generated', {
|
||||
// sessionId: selectedApp.sessionId,
|
||||
// proof: attestation,
|
||||
// });
|
||||
} catch (error) {
|
||||
console.log('Error', {
|
||||
message: String(error),
|
||||
customData: {
|
||||
type: 'error',
|
||||
},
|
||||
});
|
||||
console.error('Error in handleProve:', error);
|
||||
goToErrorScreen();
|
||||
if (socket) {
|
||||
socket.emit('proof_generation_failed', {
|
||||
sessionId: selectedApp.sessionId,
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
setGeneratingProof(false);
|
||||
setIsConnecting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ExpandableBottomLayout.Layout>
|
||||
<ExpandableBottomLayout.TopSection>
|
||||
@@ -276,16 +148,7 @@ const ProveScreen: React.FC = () => {
|
||||
Self will confirm that these details are accurate and none of your
|
||||
confidential info will be revealed to {selectedApp.appName}
|
||||
</Caption>
|
||||
<PrimaryButton
|
||||
onPress={handleProve}
|
||||
disabled={
|
||||
generatingProof ||
|
||||
isConnecting ||
|
||||
isZkeyDownloading[circuitName as CircuitName]
|
||||
}
|
||||
>
|
||||
Verify with Passcode
|
||||
</PrimaryButton>
|
||||
<PrimaryButton onLongPress={onVerify}>Hold To Verify</PrimaryButton>
|
||||
</ExpandableBottomLayout.BottomSection>
|
||||
</ExpandableBottomLayout.Layout>
|
||||
);
|
||||
|
||||
@@ -2,15 +2,18 @@ import { X509Certificate } from '@peculiar/x509';
|
||||
import { decode } from '@stablelib/cbor';
|
||||
//@ts-ignore
|
||||
import * as asn1 from 'asn1.js';
|
||||
import * as asn1js from 'asn1js';
|
||||
import { Buffer } from 'buffer';
|
||||
import elliptic from 'elliptic';
|
||||
import { Certificate } from 'pkijs';
|
||||
|
||||
import { getCurveForElliptic } from '../../../../common/src/utils/certificate_parsing/curves';
|
||||
import { PublicKeyDetailsECDSA } from '../../../../common/src/utils/certificate_parsing/dataStructure';
|
||||
import { parseCertificateSimple } from '../../../../common/src/utils/certificate_parsing/parseCertificateSimple';
|
||||
import { IMAGE_HASH } from '../../../../common/src/constants/constants';
|
||||
import { AWS_ROOT_PEM } from './awsRootPem';
|
||||
import cose from './cose';
|
||||
|
||||
// The required fields for a valid attestation
|
||||
/**
|
||||
* @notice An array specifying the required fields for a valid attestation.
|
||||
*/
|
||||
export const requiredFields = [
|
||||
'module_id',
|
||||
'digest',
|
||||
@@ -20,7 +23,9 @@ export const requiredFields = [
|
||||
'cabundle',
|
||||
];
|
||||
|
||||
// Define an interface for the ASN.1 context used with asn1.js
|
||||
/**
|
||||
* @notice ASN.1 context interface for use with asn1js.js.
|
||||
*/
|
||||
interface ASN1Context {
|
||||
seq(): ASN1Context;
|
||||
obj(...args: any[]): ASN1Context;
|
||||
@@ -29,7 +34,9 @@ interface ASN1Context {
|
||||
bitstr(): ASN1Context;
|
||||
}
|
||||
|
||||
// Update the ASN.1 definition with proper typing for ECPublicKey
|
||||
/**
|
||||
* @notice ASN.1 definition for an Elliptic Curve Public Key.
|
||||
*/
|
||||
export const ECPublicKeyASN = asn1.define(
|
||||
'ECPublicKey',
|
||||
function (this: ASN1Context) {
|
||||
@@ -42,7 +49,13 @@ export const ECPublicKeyASN = asn1.define(
|
||||
},
|
||||
);
|
||||
|
||||
// Utility function to check if a number is within (start, end] range
|
||||
/**
|
||||
* @notice Utility function to check if a number is within (start, end] range.
|
||||
* @param start The start of the range (exclusive).
|
||||
* @param end The end of the range (inclusive).
|
||||
* @param value The number to check.
|
||||
* @return True if value is within the range; otherwise, false.
|
||||
*/
|
||||
export const numberInRange = (
|
||||
start: number,
|
||||
end: number,
|
||||
@@ -51,6 +64,12 @@ export const numberInRange = (
|
||||
return value > start && value <= end;
|
||||
};
|
||||
|
||||
/**
|
||||
* @notice Verifies a certificate chain against a provided trusted root certificate.
|
||||
* @param rootPem The trusted root certificate in PEM format.
|
||||
* @param certChainStr An array of certificates in PEM format, ordered from leaf to root.
|
||||
* @return True if the certificate chain is valid, false otherwise.
|
||||
*/
|
||||
export const verifyCertChain = (
|
||||
rootPem: string,
|
||||
certChainStr: string[],
|
||||
@@ -93,6 +112,12 @@ export const verifyCertChain = (
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @notice Verifies a TEE attestation document encoded as a COSE_Sign1 structure.
|
||||
* @param attestation An array of numbers representing the COSE_Sign1 encoded attestation document.
|
||||
* @return A promise that resolves to true if the attestation is verified successfully.
|
||||
* @throws Error if the attestation document is improperly formatted or missing required fields.
|
||||
*/
|
||||
export const verifyAttestation = async (attestation: Array<number>) => {
|
||||
const coseSign1 = await decode(Buffer.from(attestation));
|
||||
|
||||
@@ -123,7 +148,7 @@ export const verifyAttestation = async (attestation: Array<number>) => {
|
||||
throw new Error('Invalid timestamp');
|
||||
}
|
||||
|
||||
//for each key, value in pcts
|
||||
// for each key, value in pcrs
|
||||
for (const [key, value] of Object.entries(attestationDoc.pcrs)) {
|
||||
if (parseInt(key, 10) < 0 || parseInt(key, 10) >= 32) {
|
||||
throw new Error('Invalid pcr index');
|
||||
@@ -167,18 +192,17 @@ export const verifyAttestation = async (attestation: Array<number>) => {
|
||||
);
|
||||
|
||||
const cert = derToPem(attestationDoc.certificate);
|
||||
const imageHash = getImageHash(attestation);
|
||||
if (imageHash !== IMAGE_HASH) {
|
||||
throw new Error('Invalid image hash');
|
||||
}
|
||||
console.log('TEE image hash verified');
|
||||
|
||||
if (!verifyCertChain(AWS_ROOT_PEM, [...certChain, cert])) {
|
||||
throw new Error('Invalid certificate chain');
|
||||
}
|
||||
|
||||
const parsed = parseCertificateSimple(cert);
|
||||
const publicKeyDetails = parsed.publicKeyDetails as PublicKeyDetailsECDSA;
|
||||
|
||||
const curve = getCurveForElliptic(publicKeyDetails.curve);
|
||||
|
||||
const x = publicKeyDetails.x;
|
||||
const y = publicKeyDetails.y;
|
||||
const { x, y, curve } = getPublicKeyFromPem(cert);
|
||||
|
||||
const verifier = {
|
||||
key: {
|
||||
@@ -187,12 +211,18 @@ export const verifyAttestation = async (attestation: Array<number>) => {
|
||||
curve,
|
||||
},
|
||||
};
|
||||
console.log('verifier', verifier);
|
||||
await cose.sign.verify(Buffer.from(attestation), verifier, {
|
||||
defaultType: 18,
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @notice Extracts the public key from a TEE attestation document.
|
||||
* @param attestation An array of numbers representing the COSE_Sign1 encoded attestation document.
|
||||
* @return The public key as a string.
|
||||
*/
|
||||
export function getPublicKey(attestation: Array<number>) {
|
||||
const coseSign1 = decode(Buffer.from(attestation));
|
||||
const [_protectedHeaderBytes, _unprotectedHeader, payload, _signature] =
|
||||
@@ -201,19 +231,12 @@ export function getPublicKey(attestation: Array<number>) {
|
||||
return attestationDoc.public_key;
|
||||
}
|
||||
|
||||
// Update the type definition to match the actual data structure
|
||||
type AttestationDoc = {
|
||||
module_id: string;
|
||||
digest: string;
|
||||
timestamp: number;
|
||||
pcrs: { [key: number]: Buffer }; // Changed from Map to object
|
||||
certificate: Buffer;
|
||||
cabundle: Array<Buffer>;
|
||||
public_key: string | null;
|
||||
user_data: string | null;
|
||||
nonce: string | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @notice Converts a DER-encoded certificate to PEM format.
|
||||
* @param der A Buffer containing the DER-encoded certificate.
|
||||
* @return The PEM-formatted certificate string.
|
||||
* @throws Error if the conversion fails.
|
||||
*/
|
||||
export function derToPem(der: Buffer): string {
|
||||
try {
|
||||
const base64 = Buffer.from(der).toString('base64');
|
||||
@@ -227,3 +250,98 @@ export function derToPem(der: Buffer): string {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Extracts the image hash (PCR0) from the attestation document.
|
||||
* @param attestation An array of numbers representing the COSE_Sign1 encoded attestation document.
|
||||
* @return The image hash (PCR0) as a hexadecimal string.
|
||||
* @throws Error if the COSE_Sign1 format is invalid or PCR0 is missing/incorrect.
|
||||
* @see https://docs.aws.amazon.com/enclaves/latest/user/set-up-attestation.html
|
||||
*/
|
||||
export function getImageHash(attestation: Array<number>) {
|
||||
const coseSign1 = decode(Buffer.from(attestation));
|
||||
|
||||
if (!Array.isArray(coseSign1) || coseSign1.length !== 4) {
|
||||
throw new Error('Invalid COSE_Sign1 format');
|
||||
}
|
||||
const [_protectedHeaderBytes, _unprotectedHeader, payload, _signature] =
|
||||
coseSign1;
|
||||
const attestationDoc = decode(payload);
|
||||
if (!attestationDoc.pcrs) {
|
||||
throw new Error('Missing required field: pcrs');
|
||||
}
|
||||
const pcr0 = attestationDoc.pcrs[0];
|
||||
if (!pcr0) {
|
||||
throw new Error('PCR0 (image hash) is missing in the attestation document');
|
||||
}
|
||||
if (pcr0.length !== 48) {
|
||||
// SHA384 produces a 48-byte hash
|
||||
throw new Error(
|
||||
`Invalid PCR0 length - expected 48 bytes, got ${pcr0.length} bytes`,
|
||||
);
|
||||
}
|
||||
return Buffer.from(pcr0).toString('hex');
|
||||
}
|
||||
|
||||
type AttestationDoc = {
|
||||
module_id: string;
|
||||
digest: string;
|
||||
timestamp: number;
|
||||
pcrs: { [key: number]: Buffer };
|
||||
certificate: Buffer;
|
||||
cabundle: Array<Buffer>;
|
||||
public_key: string | null;
|
||||
user_data: string | null;
|
||||
nonce: string | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* @notice Extracts the public key from a PEM formatted certificate.
|
||||
* @param pem A string containing the PEM formatted certificate.
|
||||
* @return An object with the x and y coordinates of the public key and the curve used.
|
||||
* @see https://docs.aws.amazon.com/enclaves/latest/user/set-up-attestation.html for p384 usage
|
||||
* @dev This function parses the certificate using getCertificateFromPem(), then uses the elliptic library
|
||||
* on the "p384" curve to derive the public key's x and y coordinates. This public key is then returned,
|
||||
* ensuring it is padded correctly.
|
||||
*/
|
||||
function getPublicKeyFromPem(pem: string) {
|
||||
const cert = getCertificateFromPem(pem);
|
||||
const curve = 'p384';
|
||||
const publicKeyBuffer =
|
||||
cert.subjectPublicKeyInfo.subjectPublicKey.valueBlock.valueHexView;
|
||||
const ec = new elliptic.ec(curve);
|
||||
const key = ec.keyFromPublic(publicKeyBuffer);
|
||||
const x_point = key.getPublic().getX().toString('hex');
|
||||
const y_point = key.getPublic().getY().toString('hex');
|
||||
|
||||
const x = x_point.length % 2 === 0 ? x_point : '0' + x_point;
|
||||
const y = y_point.length % 2 === 0 ? y_point : '0' + y_point;
|
||||
return { x, y, curve };
|
||||
}
|
||||
|
||||
/**
|
||||
* @notice Converts a PEM formatted certificate to a PKI.js Certificate object.
|
||||
* @param pemContent A string containing the PEM formatted certificate including header/footer markers.
|
||||
* @return A Certificate object parsed from the PEM content.
|
||||
* @dev The function strips the PEM header/footer and line breaks, decodes the base64 content into binary,
|
||||
* creates an ArrayBuffer, and then parses the ASN.1 structure using asn1js.fromBER. Throws an error if parsing fails.
|
||||
*/
|
||||
export function getCertificateFromPem(pemContent: string): Certificate {
|
||||
const pemFormatted = pemContent.replace(
|
||||
/(-----(BEGIN|END) CERTIFICATE-----|\n|\r)/g,
|
||||
'',
|
||||
);
|
||||
const binary = Buffer.from(pemFormatted, 'base64');
|
||||
const arrayBuffer = new ArrayBuffer(binary.length);
|
||||
const view = new Uint8Array(arrayBuffer);
|
||||
for (let i = 0; i < binary.length; i++) {
|
||||
view[i] = binary[i];
|
||||
}
|
||||
|
||||
const asn1Data = asn1js.fromBER(arrayBuffer);
|
||||
if (asn1.offset === -1) {
|
||||
throw new Error(`ASN.1 parsing error: ${asn1Data.result.error}`);
|
||||
}
|
||||
|
||||
return new Certificate({ schema: asn1Data.result });
|
||||
}
|
||||
|
||||
@@ -3,6 +3,20 @@ import { Buffer } from 'buffer';
|
||||
import { ec as EC } from 'elliptic';
|
||||
import { sha384 } from 'js-sha512';
|
||||
|
||||
/**
|
||||
* @notice Verifies a COSE_Sign1 message signature against the provided ECDSA public key.
|
||||
* @param data A Buffer containing the COSE_Sign1 encoded message.
|
||||
* @param verifier An object providing the signature verification properties:
|
||||
* - key.x: The hexadecimal string for the x-coordinate of the public key.
|
||||
* - key.y: The hexadecimal string for the y-coordinate of the public key.
|
||||
* - key.curve: The elliptic curve identifier (e.g., 'p256', 'p384') to be used.
|
||||
* @param _options An object containing options for verification. Currently supports:
|
||||
* - defaultType: The expected type identifier (not actively used in the verification flow).
|
||||
* @return A Promise that resolves if the signature is valid; otherwise, it throws an error.
|
||||
* @notice This function is typically invoked by the attestation verification process in @attest.ts
|
||||
* to ensure that the TEE's COSE_Sign1 attestation document has not been tampered with.
|
||||
* @see https://docs.aws.amazon.com/enclaves/latest/user/set-up-attestation.html for p384 sha384 usage
|
||||
*/
|
||||
export const cose = {
|
||||
sign: {
|
||||
verify: async (
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
import { LeanIMT } from '@openpassport/zk-kit-lean-imt';
|
||||
import { SMT } from '@openpassport/zk-kit-smt';
|
||||
import { poseidon2 } from 'poseidon-lite';
|
||||
|
||||
import namejson from '../../../../common/ofacdata/outputs/nameSMT.json';
|
||||
import { PASSPORT_ATTESTATION_ID } from '../../../../common/src/constants/constants';
|
||||
import { getCircuitNameFromPassportData } from '../../../../common/src/utils/circuits/circuitsName';
|
||||
import { generateCircuitInputsRegister } from '../../../../common/src/utils/circuits/generateInputs';
|
||||
import {
|
||||
generateCircuitInputsDSC,
|
||||
generateCircuitInputsRegister,
|
||||
generateCircuitInputsVCandDisclose,
|
||||
} from '../../../../common/src/utils/circuits/generateInputs';
|
||||
import { generateCommitment } from '../../../../common/src/utils/passports/passport';
|
||||
import { PassportData } from '../../../../common/src/utils/types';
|
||||
import { sendPayload } from './tee';
|
||||
|
||||
const mock_secret = '0'; //TODO: retrieve the secret from keychain
|
||||
|
||||
function generateTeeInputsRegister(secret: string, passportData: PassportData) {
|
||||
const inputs = generateCircuitInputsRegister(secret, passportData);
|
||||
const circuitName = getCircuitNameFromPassportData(passportData, 'register');
|
||||
@@ -41,8 +54,84 @@ export async function sendRegisterPayload(passportData: PassportData) {
|
||||
return;
|
||||
}
|
||||
const { inputs, circuitName } = generateTeeInputsRegister(
|
||||
'0', //TODO: retrieve the secret from keychain
|
||||
mock_secret,
|
||||
passportData,
|
||||
);
|
||||
await sendPayload(inputs, circuitName);
|
||||
}
|
||||
|
||||
function generateTeeInputsDsc(passportData: PassportData) {
|
||||
const inputs = generateCircuitInputsDSC(passportData.dsc);
|
||||
const circuitName = getCircuitNameFromPassportData(passportData, 'dsc');
|
||||
if (circuitName == null) {
|
||||
throw new Error('Circuit name is null');
|
||||
}
|
||||
return { inputs, circuitName };
|
||||
}
|
||||
|
||||
export async function sendDscPayload(passportData: PassportData) {
|
||||
if (!passportData) {
|
||||
return null;
|
||||
}
|
||||
const isSupported = checkPassportSupported(passportData);
|
||||
if (!isSupported) {
|
||||
// TODO: show a screen explaining that the passport is not supported.
|
||||
return;
|
||||
}
|
||||
const { inputs, circuitName } = generateTeeInputsDsc(passportData);
|
||||
console.log('circuitName', circuitName);
|
||||
await sendPayload(inputs, circuitName);
|
||||
}
|
||||
|
||||
function generateTeeInputsVCAndDisclose(passportData: PassportData) {
|
||||
const majority = '18';
|
||||
const user_identifier = crypto.randomUUID();
|
||||
const selector_dg1 = Array(88).fill('1');
|
||||
const selector_older_than = '1';
|
||||
const scope = '@coboyApp';
|
||||
const attestation_id = PASSPORT_ATTESTATION_ID;
|
||||
|
||||
const commitment = generateCommitment(
|
||||
mock_secret,
|
||||
attestation_id,
|
||||
passportData,
|
||||
);
|
||||
const tree = new LeanIMT<bigint>((a, b) => poseidon2([a, b]), []);
|
||||
tree.insert(BigInt(commitment));
|
||||
let smt = new SMT(poseidon2, true);
|
||||
smt.import(namejson);
|
||||
|
||||
const selector_ofac = 1;
|
||||
const forbidden_countries_list = ['ABC', 'DEF'];
|
||||
|
||||
const inputs = generateCircuitInputsVCandDisclose(
|
||||
mock_secret,
|
||||
PASSPORT_ATTESTATION_ID,
|
||||
passportData,
|
||||
scope,
|
||||
selector_dg1,
|
||||
selector_older_than,
|
||||
tree,
|
||||
majority,
|
||||
smt,
|
||||
selector_ofac,
|
||||
forbidden_countries_list,
|
||||
user_identifier,
|
||||
);
|
||||
return { inputs, circuitName: 'vc_and_disclose' };
|
||||
}
|
||||
|
||||
export async function sendVcAndDisclosePayload(
|
||||
passportData: PassportData | null,
|
||||
) {
|
||||
if (!passportData) {
|
||||
return null;
|
||||
}
|
||||
const isSupported = checkPassportSupported(passportData);
|
||||
if (!isSupported) {
|
||||
// TODO: show a screen explaining that the passport is not supported.
|
||||
return;
|
||||
}
|
||||
const { inputs, circuitName } = generateTeeInputsVCAndDisclose(passportData);
|
||||
await sendPayload(inputs, circuitName);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,12 @@ import { getPublicKey, verifyAttestation } from './attest';
|
||||
|
||||
const { ec: EC } = elliptic;
|
||||
|
||||
/**
|
||||
* @notice Encrypts plaintext using AES-256-GCM encryption.
|
||||
* @param plaintext The string to be encrypted.
|
||||
* @param key The encryption key as a forge ByteStringBuffer.
|
||||
* @return An object containing the nonce, cipher_text, and auth_tag as arrays of numbers.
|
||||
*/
|
||||
function encryptAES256GCM(plaintext: string, key: forge.util.ByteStringBuffer) {
|
||||
const iv = forge.random.getBytesSync(12);
|
||||
const cipher = forge.cipher.createCipher('AES-GCM', key);
|
||||
@@ -28,6 +34,15 @@ const pubkey =
|
||||
key1.getPublic().getX().toString('hex').padStart(64, '0') +
|
||||
key1.getPublic().getY().toString('hex').padStart(64, '0');
|
||||
|
||||
/**
|
||||
* @notice Sends a payload over WebSocket connecting to the TEE server, processes the attestation,
|
||||
* and submits a registration request encrypted via a shared key derived using ECDH.
|
||||
* @param inputs The circuit input parameters.
|
||||
* @param circuitName The name of the circuit.
|
||||
* @param timeoutMs The timeout in milliseconds (default is 1200000 ms).
|
||||
* @return A promise that resolves when the request completes or rejects on error/timeout.
|
||||
* @dev This function sets up two WebSocket connections: one for RPC and one for subscription updates.
|
||||
*/
|
||||
export async function sendPayload(
|
||||
inputs: any,
|
||||
circuitName: string,
|
||||
@@ -59,7 +74,6 @@ export async function sendPayload(
|
||||
ws.addEventListener('message', async event => {
|
||||
try {
|
||||
const result = JSON.parse(event.data);
|
||||
console.log('Received message:', result);
|
||||
if (result.result?.attestation !== undefined) {
|
||||
await processAttestation(result);
|
||||
} else {
|
||||
|
||||
@@ -1963,6 +1963,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@openpassport/zk-kit-lean-imt@npm:^0.0.6":
|
||||
version: 0.0.6
|
||||
resolution: "@openpassport/zk-kit-lean-imt@npm:0.0.6"
|
||||
dependencies:
|
||||
"@openpassport/zk-kit-utils": "npm:0.0.1"
|
||||
checksum: 10c0/2cb3f99e216391a325a7050290cccfa12323dc057d7cf4a26baeafe79a34c4ed3013da035fdbe9985395d5a668e37fd81f2b060834b67895bd3f82e7edfe0601
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@openpassport/zk-kit-smt@npm:^0.0.1":
|
||||
version: 0.0.1
|
||||
resolution: "@openpassport/zk-kit-smt@npm:0.0.1"
|
||||
@@ -1970,6 +1979,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@openpassport/zk-kit-utils@npm:0.0.1":
|
||||
version: 0.0.1
|
||||
resolution: "@openpassport/zk-kit-utils@npm:0.0.1"
|
||||
dependencies:
|
||||
buffer: "npm:^6.0.3"
|
||||
checksum: 10c0/3a9adb279cfd5096c44934bb6c73979f21247eb0119a65f8b5c0bb1f457f5500de761fc627e0bd9e72a7cbf5ca65696c144bfffe3dbd1f1ce37a300c239a8e3f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@peculiar/asn1-cms@npm:^2.3.13, @peculiar/asn1-cms@npm:^2.3.15":
|
||||
version: 2.3.15
|
||||
resolution: "@peculiar/asn1-cms@npm:2.3.15"
|
||||
@@ -7210,6 +7228,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"buffer@npm:^6.0.3":
|
||||
version: 6.0.3
|
||||
resolution: "buffer@npm:6.0.3"
|
||||
dependencies:
|
||||
base64-js: "npm:^1.3.1"
|
||||
ieee754: "npm:^1.2.1"
|
||||
checksum: 10c0/2a905fbbcde73cc5d8bd18d1caa23715d5f83a5935867c2329f0ac06104204ba7947be098fe1317fbd8830e26090ff8e764f08cd14fefc977bb248c3487bcbd0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"burnt@npm:^0.12.2":
|
||||
version: 0.12.2
|
||||
resolution: "burnt@npm:0.12.2"
|
||||
@@ -9678,7 +9706,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ieee754@npm:^1.1.13, ieee754@npm:^1.1.4, ieee754@npm:^1.1.8":
|
||||
"ieee754@npm:^1.1.13, ieee754@npm:^1.1.4, ieee754@npm:^1.1.8, ieee754@npm:^1.2.1":
|
||||
version: 1.2.1
|
||||
resolution: "ieee754@npm:1.2.1"
|
||||
checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb
|
||||
@@ -12362,6 +12390,7 @@ __metadata:
|
||||
"@babel/core": "npm:^7.20.0"
|
||||
"@babel/plugin-transform-private-methods": "npm:^7.23.3"
|
||||
"@ethersproject/shims": "npm:^5.7.0"
|
||||
"@openpassport/zk-kit-lean-imt": "npm:^0.0.6"
|
||||
"@openpassport/zk-kit-smt": "npm:^0.0.1"
|
||||
"@peculiar/asn1-schema": "npm:^2.3.15"
|
||||
"@peculiar/x509": "npm:^1.12.3"
|
||||
@@ -13089,7 +13118,7 @@ __metadata:
|
||||
peerDependencies:
|
||||
react: ">= 17.0.1"
|
||||
react-native: ">= 0.64.3"
|
||||
checksum: 10c0/ebb3dfb9a111bf6082866999d31e10f952fcd12bcf2a81210ff9f44a4cb4d25e1b4caac5fdec512b367685e56a37e61de3e6648c2af45b93fcbaf704355dbaa6
|
||||
checksum: 10c0/0e3f83f59dadc337ab46fa0c59bb2b80eb91d4756f0ac67d7b11e0d97044d58bbaca7ee5c7dcd1e0a389af3f16b0eaca12122d59905013a6f5a2f4423ead49d0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user