diff --git a/common/index.ts b/common/index.ts index 15ef480ca..2bf8677f7 100644 --- a/common/index.ts +++ b/common/index.ts @@ -84,6 +84,13 @@ export { stringToBigInt, } from './src/utils/index.js'; +export { + prepareAadhaarRegisterTestData, + prepareAadhaarDiscloseTestData, + prepareAadhaarRegisterData, + prepareAadhaarDiscloseData, +} from './src/utils/aadhaar/mockData.js'; +export { generateTestData, testCustomData } from './src/utils/aadhaar/utils.js'; export { createSelector } from './src/utils/aadhaar/constants.js'; // Hash utilities export { @@ -93,11 +100,3 @@ export { hash, packBytesAndPoseidon, } from './src/utils/hash.js'; - -export { generateTestData, testCustomData } from './src/utils/aadhaar/utils.js'; - -export { - prepareAadhaarDiscloseTestData, - prepareAadhaarRegisterData, - prepareAadhaarRegisterTestData, -} from './src/utils/aadhaar/mockData.js'; diff --git a/common/src/utils/aadhaar/mockData.ts b/common/src/utils/aadhaar/mockData.ts index 5ba5eff5e..e6caa3e19 100644 --- a/common/src/utils/aadhaar/mockData.ts +++ b/common/src/utils/aadhaar/mockData.ts @@ -1,6 +1,5 @@ import forge from 'node-forge'; import { poseidon5 } from 'poseidon-lite'; - import { COMMITMENT_TREE_DEPTH } from '../../constants/constants.js'; import { findIndexInTree, formatInput } from '../circuits/generateInputs.js'; import { packBytesAndPoseidon } from '../hash.js'; @@ -11,24 +10,20 @@ import { getNameYobLeafAahaar, } from '../trees.js'; import { testQRData } from './assets/dataInput.js'; -import { - calculateAge, - extractQRDataFields, - generateTestData, - stringToAsciiArray, - testCustomData, -} from './utils.js'; - +import { calculateAge, generateTestData, stringToAsciiArray, testCustomData } from './utils.js'; +import { extractQRDataFields } from './utils.js'; +import { AadhaarField, createSelector } from './constants.js'; +import { formatCountriesList } from '../circuits/formatInputs.js'; +import { LeanIMT } from '@openpassport/zk-kit-lean-imt'; +import { SMT } from '@openpassport/zk-kit-smt'; import { convertBigIntToByteArray, decompressByteArray, extractPhoto, splitToWords, } from '@anon-aadhaar/core'; -import { LeanIMT } from '@openpassport/zk-kit-lean-imt'; -import { SMT } from '@openpassport/zk-kit-smt'; -import { bufferToHex, Uint8ArrayToCharArray } from '@zk-email/helpers/dist/binary-format.js'; import { sha256Pad } from '@zk-email/helpers/dist/sha-utils.js'; +import { bufferToHex, Uint8ArrayToCharArray } from '@zk-email/helpers/dist/binary-format.js'; // Helper function to compute padded name function computePaddedName(name: string): number[] { @@ -168,6 +163,162 @@ function processQRDataSimple(qrData: string) { }; } +export function prepareAadhaarRegisterTestData( + privKeyPem: string, + pubkeyPem: string, + secret: string, + name?: string, + dateOfBirth?: string, + gender?: string, + pincode?: string, + state?: string, + timestamp?: string +) { + const sharedData = processQRData( + privKeyPem, + name, + dateOfBirth, + gender, + pincode, + state, + timestamp + ); + + const delimiterIndices: number[] = []; + for (let i = 0; i < sharedData.qrDataPadded.length; i++) { + if (sharedData.qrDataPadded[i] === 255) { + delimiterIndices.push(i); + } + if (delimiterIndices.length === 18) { + break; + } + } + let photoEOI = 0; + for (let i = delimiterIndices[17]; i < sharedData.qrDataPadded.length - 1; i++) { + if (sharedData.qrDataPadded[i + 1] === 217 && sharedData.qrDataPadded[i] === 255) { + photoEOI = i + 1; + } + } + if (photoEOI === 0) { + throw new Error('Photo EOI not found'); + } + + const signatureBytes = sharedData.decodedData.slice( + sharedData.decodedData.length - 256, + sharedData.decodedData.length + ); + const signature = BigInt('0x' + bufferToHex(Buffer.from(signatureBytes)).toString()); + + const publicKey = forge.pki.publicKeyFromPem(pubkeyPem); + + const modulusHex = publicKey.n.toString(16); + const pubKey = BigInt('0x' + modulusHex); + + const nullifier = nullifierHash(sharedData.extractedFields); + const packedCommitment = computePackedCommitment(sharedData.extractedFields); + const commitment = computeCommitment( + BigInt(secret), + BigInt(sharedData.qrHash), + nullifier, + packedCommitment, + BigInt(sharedData.photoHash) + ); + + const inputs = { + qrDataPadded: Uint8ArrayToCharArray(sharedData.qrDataPadded), + qrDataPaddedLength: sharedData.qrDataPaddedLen, + delimiterIndices: delimiterIndices, + signature: splitToWords(signature, BigInt(121), BigInt(17)), + pubKey: splitToWords(pubKey, BigInt(121), BigInt(17)), + secret: secret, + photoEOI: photoEOI, + }; + + return { + inputs, + nullifier, + commitment, + }; +} + +export async function prepareAadhaarRegisterData(qrData: string, secret: string, certs: string[]) { + const sharedData = processQRDataSimple(qrData); + const delimiterIndices: number[] = []; + for (let i = 0; i < sharedData.qrDataPadded.length; i++) { + if (sharedData.qrDataPadded[i] === 255) { + delimiterIndices.push(i); + } + if (delimiterIndices.length === 18) { + break; + } + } + let photoEOI = 0; + for (let i = delimiterIndices[17]; i < sharedData.qrDataPadded.length - 1; i++) { + if (sharedData.qrDataPadded[i + 1] === 217 && sharedData.qrDataPadded[i] === 255) { + photoEOI = i + 1; + } + } + if (photoEOI === 0) { + throw new Error('Photo EOI not found'); + } + + const signatureBytes = sharedData.decodedData.slice( + sharedData.decodedData.length - 256, + sharedData.decodedData.length + ); + const signature = BigInt('0x' + bufferToHex(Buffer.from(signatureBytes)).toString()); + + //do promise.all for all certs and pick the one that is valid + const certificates = await Promise.all( + certs.map(async (cert) => { + const certificate = forge.pki.certificateFromPem(cert); + const publicKey = certificate.publicKey as forge.pki.rsa.PublicKey; + + try { + const md = forge.md.sha256.create(); + md.update(forge.util.binary.raw.encode(sharedData.signedData)); + + const isValid = publicKey.verify(md.digest().getBytes(), signatureBytes); + return isValid; + } catch (error) { + return false; + } + }) + ); + + //find the valid cert + const validCert = certificates.indexOf(true); + if (validCert === -1) { + throw new Error('No valid certificate found'); + } + const certPem = certs[validCert]; + const cert = forge.pki.certificateFromPem(certPem); + const modulusHex = (cert.publicKey as forge.pki.rsa.PublicKey).n.toString(16); + const pubKey = BigInt('0x' + modulusHex); + + const nullifier = nullifierHash(sharedData.extractedFields); + const packedCommitment = computePackedCommitment(sharedData.extractedFields); + const commitment = computeCommitment( + BigInt(secret), + BigInt(sharedData.qrHash), + nullifier, + packedCommitment, + BigInt(sharedData.photoHash) + ); + + const inputs = { + qrDataPadded: Uint8ArrayToCharArray(sharedData.qrDataPadded), + qrDataPaddedLength: sharedData.qrDataPaddedLen, + delimiterIndices: delimiterIndices, + signature: splitToWords(signature, BigInt(121), BigInt(17)), + pubKey: splitToWords(pubKey, BigInt(121), BigInt(17)), + secret: secret, + photoEOI: photoEOI, + }; + + return inputs; +} + export function prepareAadhaarDiscloseTestData( privateKeyPem: string, merkletree: LeanIMT, @@ -201,7 +352,6 @@ export function prepareAadhaarDiscloseTestData( sharedData.extractedFields.yob ); - const uppercaseName = computeUppercasePaddedName(sharedData.extractedFields.name); const genderAscii = stringToAsciiArray(sharedData.extractedFields.gender)[0]; const nullifier = nullifierHash(sharedData.extractedFields); const packedCommitment = computePackedCommitment(sharedData.extractedFields); @@ -290,139 +440,34 @@ export function prepareAadhaarDiscloseTestData( }; } -export async function prepareAadhaarRegisterData(qrData: string, secret: string, certs: string[]) { - const sharedData = processQRDataSimple(qrData); - const delimiterIndices: number[] = []; - for (let i = 0; i < sharedData.qrDataPadded.length; i++) { - if (sharedData.qrDataPadded[i] === 255) { - delimiterIndices.push(i); - } - if (delimiterIndices.length === 18) { - break; - } - } - let photoEOI = 0; - for (let i = delimiterIndices[17]; i < sharedData.qrDataPadded.length - 1; i++) { - if (sharedData.qrDataPadded[i + 1] === 217 && sharedData.qrDataPadded[i] === 255) { - photoEOI = i + 1; - } - } - if (photoEOI === 0) { - throw new Error('Photo EOI not found'); - } - - const signatureBytes = sharedData.decodedData.slice( - sharedData.decodedData.length - 256, - sharedData.decodedData.length - ); - const signature = BigInt('0x' + bufferToHex(Buffer.from(signatureBytes)).toString()); - - //do promise.all for all certs and pick the one that is valid - const certificates = await Promise.all( - certs.map(async (cert) => { - const certificate = forge.pki.certificateFromPem(cert); - const publicKey = certificate.publicKey as forge.pki.rsa.PublicKey; - - try { - const md = forge.md.sha256.create(); - md.update(forge.util.binary.raw.encode(sharedData.signedData)); - - const isValid = publicKey.verify(md.digest().getBytes(), signatureBytes); - return isValid; - } catch (error) { - return false; - } - }) - ); - - //find the valid cert - const validCert = certificates.indexOf(true); - if (validCert === -1) { - throw new Error('No valid certificate found'); - } - const certPem = certs[validCert]; - const cert = forge.pki.certificateFromPem(certPem); - const modulusHex = (cert.publicKey as forge.pki.rsa.PublicKey).n.toString(16); - const pubKey = BigInt('0x' + modulusHex); - - const nullifier = nullifierHash(sharedData.extractedFields); - const packedCommitment = computePackedCommitment(sharedData.extractedFields); - const commitment = computeCommitment( - BigInt(secret), - BigInt(sharedData.qrHash), - nullifier, - packedCommitment, - BigInt(sharedData.photoHash) - ); - - const inputs = { - qrDataPadded: Uint8ArrayToCharArray(sharedData.qrDataPadded), - qrDataPaddedLength: sharedData.qrDataPaddedLen, - delimiterIndices: delimiterIndices, - signature: splitToWords(signature, BigInt(121), BigInt(17)), - pubKey: splitToWords(pubKey, BigInt(121), BigInt(17)), - secret: secret, - photoEOI: photoEOI, - }; - - return { - inputs, - nullifier, - commitment, - }; -} - -export function prepareAadhaarRegisterTestData( - privKeyPem: string, - pubkeyPem: string, +export function prepareAadhaarDiscloseData( + qrData: string, + identityTree: LeanIMT, + nameAndDob_smt: SMT, + nameAndYob_smt: SMT, + scope: string, secret: string, - name?: string, - dateOfBirth?: string, - gender?: string, - pincode?: string, - state?: string, - timestamp?: string + user_identifier: string, + discloseAttributes: { + dateOfBirth?: boolean; + name?: boolean; + gender?: boolean; + idNumber?: boolean; + issuingState?: boolean; + minimumAge?: number; + forbiddenCountriesListPacked?: string[]; + ofac?: boolean; + } ) { - const sharedData = processQRData( - privKeyPem, - name, - dateOfBirth, - gender, - pincode, - state, - timestamp + const sharedData = processQRDataSimple(qrData); + + const { currentYear, currentMonth, currentDay } = calculateAge( + sharedData.extractedFields.dob, + sharedData.extractedFields.mob, + sharedData.extractedFields.yob ); - const delimiterIndices: number[] = []; - for (let i = 0; i < sharedData.qrDataPadded.length; i++) { - if (sharedData.qrDataPadded[i] === 255) { - delimiterIndices.push(i); - } - if (delimiterIndices.length === 18) { - break; - } - } - let photoEOI = 0; - for (let i = delimiterIndices[17]; i < sharedData.qrDataPadded.length - 1; i++) { - if (sharedData.qrDataPadded[i + 1] === 217 && sharedData.qrDataPadded[i] === 255) { - photoEOI = i + 1; - } - } - if (photoEOI === 0) { - throw new Error('Photo EOI not found'); - } - - const signatureBytes = sharedData.decodedData.slice( - sharedData.decodedData.length - 256, - sharedData.decodedData.length - ); - const signature = BigInt('0x' + bufferToHex(Buffer.from(signatureBytes)).toString()); - - const publicKey = forge.pki.publicKeyFromPem(pubkeyPem); - - const modulusHex = publicKey.n.toString(16); - const pubKey = BigInt('0x' + modulusHex); - + const genderAscii = stringToAsciiArray(sharedData.extractedFields.gender)[0]; const nullifier = nullifierHash(sharedData.extractedFields); const packedCommitment = computePackedCommitment(sharedData.extractedFields); const commitment = computeCommitment( @@ -433,19 +478,98 @@ export function prepareAadhaarRegisterTestData( BigInt(sharedData.photoHash) ); + const paddedName = computePaddedName(sharedData.extractedFields.name); + + const index = findIndexInTree(identityTree, BigInt(commitment)); + const { + siblings, + path: merkle_path, + leaf_depth, + } = generateMerkleProof(identityTree, index, COMMITMENT_TREE_DEPTH); + + const namedob_leaf = getNameDobLeafAadhaar( + sharedData.extractedFields.name, + sharedData.extractedFields.yob, + sharedData.extractedFields.mob, + sharedData.extractedFields.dob + ); + const nameyob_leaf = getNameYobLeafAahaar( + sharedData.extractedFields.name, + sharedData.extractedFields.yob + ); + + const { + root: ofac_name_dob_smt_root, + closestleaf: ofac_name_dob_smt_leaf_key, + siblings: ofac_name_dob_smt_siblings, + } = generateSMTProof(nameAndDob_smt, namedob_leaf); + + const { + root: ofac_name_yob_smt_root, + closestleaf: ofac_name_yob_smt_leaf_key, + siblings: ofac_name_yob_smt_siblings, + } = generateSMTProof(nameAndYob_smt, nameyob_leaf); + + const selectorArr: AadhaarField[] = []; + if (discloseAttributes.dateOfBirth) { + selectorArr.push('YEAR_OF_BIRTH'); + selectorArr.push('MONTH_OF_BIRTH'); + selectorArr.push('DAY_OF_BIRTH'); + } + if (discloseAttributes.name) { + selectorArr.push('NAME'); + } + if (discloseAttributes.gender) { + selectorArr.push('GENDER'); + } + if (discloseAttributes.idNumber) { + selectorArr.push('AADHAAR_LAST_4_DIGITS'); + } + if (discloseAttributes.issuingState) { + selectorArr.push('STATE'); + } + if (discloseAttributes.ofac) { + selectorArr.push('OFAC_NAME_DOB_CHECK'); + selectorArr.push('OFAC_NAME_YOB_CHECK'); + } + + const selector = createSelector(selectorArr); + const inputs = { - qrDataPadded: Uint8ArrayToCharArray(sharedData.qrDataPadded), - qrDataPaddedLength: sharedData.qrDataPaddedLen, - delimiterIndices: delimiterIndices, - signature: splitToWords(signature, BigInt(121), BigInt(17)), - pubKey: splitToWords(pubKey, BigInt(121), BigInt(17)), - secret: secret, - photoEOI: photoEOI, + attestation_id: '3', + secret, + qrDataHash: BigInt(sharedData.qrHash).toString(), + gender: genderAscii.toString(), + yob: stringToAsciiArray(sharedData.extractedFields.yob), + mob: stringToAsciiArray(sharedData.extractedFields.mob), + dob: stringToAsciiArray(sharedData.extractedFields.dob), + name: formatInput(paddedName), + aadhaar_last_4digits: stringToAsciiArray(sharedData.extractedFields.aadhaarLast4Digits), + pincode: stringToAsciiArray(sharedData.extractedFields.pincode), + state: stringToAsciiArray(sharedData.extractedFields.state.padEnd(31, '\0')), + ph_no_last_4digits: stringToAsciiArray(sharedData.extractedFields.phoneNoLast4Digits), + photoHash: formatInput(BigInt(sharedData.photoHash)), + merkle_root: formatInput(BigInt(identityTree.root)), + leaf_depth: formatInput(leaf_depth), + path: formatInput(merkle_path), + siblings: formatInput(siblings), + ofac_name_dob_smt_leaf_key: formatInput(BigInt(ofac_name_dob_smt_leaf_key)), + ofac_name_dob_smt_root: formatInput(BigInt(ofac_name_dob_smt_root)), + ofac_name_dob_smt_siblings: formatInput(ofac_name_dob_smt_siblings), + ofac_name_yob_smt_leaf_key: formatInput(BigInt(ofac_name_yob_smt_leaf_key)), + ofac_name_yob_smt_root: formatInput(BigInt(ofac_name_yob_smt_root)), + ofac_name_yob_smt_siblings: formatInput(ofac_name_yob_smt_siblings), + selector, + minimumAge: formatInput(discloseAttributes.minimumAge ?? 0), + currentYear: formatInput(currentYear), + currentMonth: formatInput(currentMonth), + currentDay: formatInput(currentDay), + scope: formatInput(BigInt(scope)), + user_identifier: formatInput(BigInt(user_identifier)), + forbidden_countries_list: discloseAttributes.forbiddenCountriesListPacked + ? formatInput(formatCountriesList(discloseAttributes.forbiddenCountriesListPacked)) + : formatInput([...Array(120)].map((_) => '0')), }; - return { - inputs, - nullifier, - commitment, - }; + return inputs; }