diff --git a/app/src/screens/prove/ConfirmBelongingScreen.tsx b/app/src/screens/prove/ConfirmBelongingScreen.tsx index 360d64736..8f1e65f7a 100644 --- a/app/src/screens/prove/ConfirmBelongingScreen.tsx +++ b/app/src/screens/prove/ConfirmBelongingScreen.tsx @@ -8,7 +8,7 @@ import { ActivityIndicator, View } from 'react-native'; import type { StaticScreenProps } from '@react-navigation/native'; import { usePreventRemove } from '@react-navigation/native'; -import { useSelfClient } from '@selfxyz/mobile-sdk-alpha'; +import { loadSelectedDocument, useSelfClient } from '@selfxyz/mobile-sdk-alpha'; import { PassportEvents, ProofEvents, @@ -46,7 +46,22 @@ const ConfirmBelongingScreen: React.FC = () => { const isReadyToProve = currentState === 'ready_to_prove'; useEffect(() => { notificationSuccess(); - init(selfClient, 'dsc'); + + const initializeProving = async () => { + try { + const selectedDocument = await loadSelectedDocument(selfClient); + if (selectedDocument?.data?.documentCategory === 'aadhaar') { + init(selfClient, 'register'); + } else { + init(selfClient, 'dsc'); + } + } catch (error) { + console.error('Error loading selected document:', error); + init(selfClient, 'dsc'); + } + }; + + initializeProving(); }, [init, selfClient]); const onOkPress = async () => { diff --git a/app/src/utils/proving/provingMachine.ts b/app/src/utils/proving/provingMachine.ts index fe452cbd3..bc3757f31 100644 --- a/app/src/utils/proving/provingMachine.ts +++ b/app/src/utils/proving/provingMachine.ts @@ -41,6 +41,7 @@ import { getPayload, getWSDbRelayerUrl, } from '@selfxyz/common/utils/proving'; +import type { IDDocument } from '@selfxyz/common/utils/types'; import type { SelfClient } from '@selfxyz/mobile-sdk-alpha'; import { clearPassportData, @@ -72,10 +73,20 @@ const getMappingKey = ( documentCategory: DocumentCategory, ): string => { if (circuitType === 'disclose') { - return documentCategory === 'passport' ? 'DISCLOSE' : 'DISCLOSE_ID'; + if (documentCategory === 'passport') return 'DISCLOSE'; + if (documentCategory === 'id_card') return 'DISCLOSE_ID'; + if (documentCategory === 'aadhaar') return 'DISCLOSE_AADHAAR'; + throw new Error( + `Unsupported document category for disclose: ${documentCategory}`, + ); } if (circuitType === 'register') { - return documentCategory === 'passport' ? 'REGISTER' : 'REGISTER_ID'; + if (documentCategory === 'passport') return 'REGISTER'; + if (documentCategory === 'id_card') return 'REGISTER_ID'; + if (documentCategory === 'aadhaar') return 'REGISTER_AADHAAR'; + throw new Error( + `Unsupported document category for register: ${documentCategory}`, + ); } // circuitType === 'dsc' return documentCategory === 'passport' ? 'DSC' : 'DSC_ID'; @@ -95,10 +106,10 @@ const resolveWebSocketUrl = ( }; // Helper functions for _generatePayload refactoring -const _generateCircuitInputs = ( +const _generateCircuitInputs = async ( circuitType: 'disclose' | 'register' | 'dsc', secret: string | undefined | null, - passportData: PassportData, + passportData: IDDocument, env: 'prod' | 'stg', ) => { const document: DocumentCategory = passportData.documentCategory; @@ -114,17 +125,19 @@ const _generateCircuitInputs = ( switch (circuitType) { case 'register': ({ inputs, circuitName, endpointType, endpoint } = - generateTEEInputsRegister( + await generateTEEInputsRegister( secret as string, passportData, - protocolStore[document].dsc_tree, + document === 'aadhaar' + ? protocolStore[document].public_keys + : protocolStore[document].dsc_tree, env, )); circuitTypeWithDocumentExtension = `${circuitType}${document === 'passport' ? '' : '_id'}`; break; case 'dsc': ({ inputs, circuitName, endpointType, endpoint } = generateTEEInputsDSC( - passportData, + passportData as PassportData, protocolStore[document].csca_tree as string[][], env, )); @@ -154,7 +167,10 @@ const _generateCircuitInputs = ( } }, )); - circuitTypeWithDocumentExtension = `disclose`; + circuitTypeWithDocumentExtension = getCircuitTypeWithDocumentExtension( + circuitType, + document, + ); break; default: throw new Error('Invalid circuit type:' + circuitType); @@ -1099,8 +1115,10 @@ export const useProvingStore = create((set, get) => { secret as string, { getCommitmentTree, - getAltCSCA: docType => - useProtocolStore.getState()[docType].alternative_csca, + getAltCSCA: (docType: DocumentCategory) => + docType === 'aadhaar' + ? useProtocolStore.getState().aadhaar.public_keys + : useProtocolStore.getState()[docType].alternative_csca, }, ); logProofEvent( @@ -1150,16 +1168,18 @@ export const useProvingStore = create((set, get) => { return; } const document: DocumentCategory = passportData.documentCategory; - const isDscRegistered = await checkIfPassportDscIsInTree( - passportData, - useProtocolStore.getState()[document].dsc_tree, - ); - logProofEvent('info', 'DSC tree check', context, { - dsc_registered: isDscRegistered, - }); - if (isDscRegistered) { - selfClient.trackEvent(ProofEvents.DSC_IN_TREE); - set({ circuitType: 'register' }); + if (document === 'passport' || document === 'id_card') { + const isDscRegistered = await checkIfPassportDscIsInTree( + passportData, + useProtocolStore.getState()[document].dsc_tree, + ); + logProofEvent('info', 'DSC tree check', context, { + dsc_registered: isDscRegistered, + }); + if (isDscRegistered) { + selfClient.trackEvent(ProofEvents.DSC_IN_TREE); + set({ circuitType: 'register' }); + } } logProofEvent('info', 'Validation succeeded', context, { duration_ms: Date.now() - startTime, @@ -1448,7 +1468,7 @@ export const useProvingStore = create((set, get) => { endpointType, endpoint, circuitTypeWithDocumentExtension, - } = _generateCircuitInputs( + } = await _generateCircuitInputs( circuitType as 'disclose' | 'register' | 'dsc', secret, passportData, diff --git a/common/src/utils/aadhaar/utils.ts b/common/src/utils/aadhaar/utils.ts index 772a450ac..a6ca3b900 100644 --- a/common/src/utils/aadhaar/utils.ts +++ b/common/src/utils/aadhaar/utils.ts @@ -94,6 +94,7 @@ export const createCustomV2TestData = ({ photo, name, timestamp, + aadhaarLast4Digits, }: { signedData: Uint8Array; dob?: string; @@ -103,6 +104,7 @@ export const createCustomV2TestData = ({ photo?: boolean; name?: string; timestamp?: string; + aadhaarLast4Digits?: string; }) => { const allDataParsed: number[][] = []; const delimiterIndices: number[] = []; @@ -123,6 +125,18 @@ export const createCustomV2TestData = ({ } } + console.log('createCustomV2TestData', { + signedData, + dob, + pincode, + gender, + state, + photo, + name, + timestamp, + aadhaarLast4Digits, + }); + // Set new timestamp to the time of the signature const newDateString = returnNewDateString(timestamp); const newTimestamp = new TextEncoder().encode(newDateString); @@ -175,6 +189,14 @@ export const createCustomV2TestData = ({ ); } + if (!aadhaarLast4Digits) { + + for (let i = 2; i < 6; i++) { + modifiedSignedData[i] = Math.floor(Math.random() * 10) + 48; + } + + } + if (name) { const newName = new TextEncoder().encode(name); modifiedSignedData = replaceBytesBetween( diff --git a/common/src/utils/circuits/circuitsName.ts b/common/src/utils/circuits/circuitsName.ts index 0da8cdeb0..bb83c3925 100644 --- a/common/src/utils/circuits/circuitsName.ts +++ b/common/src/utils/circuits/circuitsName.ts @@ -1,7 +1,7 @@ -import type { PassportData } from '../types.js'; +import type { IDDocument, PassportData } from '../types.js'; export function getCircuitNameFromPassportData( - passportData: PassportData, + passportData: IDDocument, circuitType: 'register' | 'dsc' ) { if (circuitType === 'register') { @@ -11,9 +11,13 @@ export function getCircuitNameFromPassportData( } } -function getDSCircuitNameFromPassportData(passportData: PassportData) { +function getDSCircuitNameFromPassportData(passportData: IDDocument) { console.log('Getting DSC circuit name from passport data...'); + if (passportData.documentCategory === 'aadhaar') { + throw new Error('Aadhaar does not have a DSC circuit'); + } + if (!passportData.passportMetadata) { console.error('Passport metadata is missing'); throw new Error('Passport data are not parsed'); @@ -76,9 +80,13 @@ function getDSCircuitNameFromPassportData(passportData: PassportData) { } } -function getRegisterNameFromPassportData(passportData: PassportData) { +function getRegisterNameFromPassportData(passportData: IDDocument) { console.log('Getting register circuit name from passport data...'); + if (passportData.documentCategory === 'aadhaar') { + return 'register_aadhaar'; + } + if (!passportData.passportMetadata) { console.error('Passport metadata is missing'); throw new Error('Passport data are not parsed'); diff --git a/common/src/utils/circuits/registerInputs.ts b/common/src/utils/circuits/registerInputs.ts index ae6d73cea..af630de5d 100644 --- a/common/src/utils/circuits/registerInputs.ts +++ b/common/src/utils/circuits/registerInputs.ts @@ -9,7 +9,6 @@ import { PASSPORT_ATTESTATION_ID, } from '../../constants/constants.js'; import type { DocumentCategory, PassportData } from '../../types/index.js'; -import type { AadhaarData } from '../types.js'; import type { SelfApp, SelfAppDisclosureConfig } from '../../utils/appType.js'; import { calculateUserIdentifierHash, @@ -19,104 +18,94 @@ import { getCircuitNameFromPassportData, hashEndpointWithScope, } from '../../utils/index.js'; -import type { OfacTree } from '../../utils/types.js'; +import type { AadhaarData,IDDocument,OfacTree } from '../../utils/types.js'; import { LeanIMT } from '@openpassport/zk-kit-lean-imt'; import { SMT } from '@openpassport/zk-kit-smt'; export { generateCircuitInputsRegister } from './generateInputs.js'; -export function generateTEEInputsDSC( - passportData: PassportData, - cscaTree: string[][], - env: 'prod' | 'stg' -) { - const inputs = generateCircuitInputsDSC(passportData, cscaTree); - const circuitName = getCircuitNameFromPassportData(passportData, 'dsc'); - const endpointType = env === 'stg' ? 'staging_celo' : 'celo'; - const endpoint = 'https://self.xyz'; - return { inputs, circuitName, endpointType, endpoint }; -} - -export function generateTEEInputsDiscloseStateless( +export function generateTEEInputsAadhaarDisclose( secret: string, - passportData: PassportData, + aadhaarData: AadhaarData, selfApp: SelfApp, getTree: ( doc: DocumentCategory, tree: T ) => T extends 'ofac' ? OfacTree : any ) { + const { prepareAadhaarDiscloseData } = require('../aadhaar/mockData.js'); const { scope, disclosures, endpoint, userId, userDefinedData, chainID } = selfApp; const userIdentifierHash = calculateUserIdentifierHash(chainID, userId, userDefinedData); const scope_hash = hashEndpointWithScope(endpoint, scope); - const document: DocumentCategory = passportData.documentCategory; - - const selector_dg1 = getSelectorDg1(document, disclosures); - - const majority = disclosures.minimumAge ? disclosures.minimumAge.toString() : DEFAULT_MAJORITY; - const selector_older_than = disclosures.minimumAge ? '1' : '0'; - - const selector_ofac = disclosures.ofac ? 1 : 0; - const ofac_trees = getTree(document, 'ofac'); + const ofac_trees = getTree('aadhaar', 'ofac'); if (!ofac_trees) { throw new Error('OFAC trees not loaded'); } - // Validate OFAC tree structure if (!ofac_trees.nameAndDob || !ofac_trees.nameAndYob) { throw new Error('Invalid OFAC tree structure: missing required fields'); } - if (document === 'passport' && !ofac_trees.passportNoAndNationality) { - throw new Error('Invalid OFAC tree structure: missing passportNoAndNationality for passport'); - } - let passportNoAndNationalitySMT: SMT | null = null; const nameAndDobSMT = new SMT(poseidon2, true); const nameAndYobSMT = new SMT(poseidon2, true); - if (document === 'passport') { - passportNoAndNationalitySMT = new SMT(poseidon2, true); - passportNoAndNationalitySMT.import(ofac_trees.passportNoAndNationality); - } nameAndDobSMT.import(ofac_trees.nameAndDob); nameAndYobSMT.import(ofac_trees.nameAndYob); - const serialized_tree = getTree(document, 'commitment'); + const serialized_tree = getTree('aadhaar', 'commitment'); const tree = LeanIMT.import((a, b) => poseidon2([a, b]), serialized_tree); - const inputs = generateCircuitInputsVCandDisclose( - secret, - document === 'passport' ? PASSPORT_ATTESTATION_ID : ID_CARD_ATTESTATION_ID, - passportData, - scope_hash, - selector_dg1, - selector_older_than, + + const inputs = prepareAadhaarDiscloseData( + aadhaarData.qrData, tree, - majority, - passportNoAndNationalitySMT, nameAndDobSMT, nameAndYobSMT, - selector_ofac, - disclosures.excludedCountries ?? [], - userIdentifierHash.toString() + scope_hash, + secret, + userIdentifierHash.toString(), + { + dateOfBirth: disclosures.date_of_birth, + name: disclosures.name, + gender: disclosures.gender, + idNumber: disclosures.passport_number, + issuingState: disclosures.issuing_state, + minimumAge: disclosures.minimumAge, + forbiddenCountriesListPacked: disclosures.excludedCountries, + ofac: disclosures.ofac, + } ); + return { inputs, - circuitName: - passportData.documentCategory === 'passport' ? 'vc_and_disclose' : 'vc_and_disclose_id', + circuitName: 'vc_and_disclose_aadhaar', endpointType: selfApp.endpointType, endpoint: selfApp.endpoint, }; } -export function generateTEEInputsRegister( +export async function generateTEEInputsAadhaarRegister( secret: string, + aadhaarData: AadhaarData, + publicKeys: string[], + env: 'prod' | 'stg' +) { + const { prepareAadhaarRegisterData } = require('../aadhaar/mockData.js'); + console.log('publicKeys-aadhaar', publicKeys, 'secret-aadhaar', secret, 'aadhaarData-aadhaar', aadhaarData); + const inputs = await prepareAadhaarRegisterData(aadhaarData.qrData, secret, publicKeys); + const circuitName = 'register_aadhaar'; + const endpointType = env === 'stg' ? 'staging_celo' : 'celo'; + const endpoint = 'https://self.xyz'; + return { inputs, circuitName, endpointType, endpoint }; +} + +export function generateTEEInputsDSC( passportData: PassportData, - dscTree: string, + cscaTree: string[][], env: 'prod' | 'stg' ) { - const inputs = generateCircuitInputsRegister(secret, passportData, dscTree); - const circuitName = getCircuitNameFromPassportData(passportData, 'register'); + const inputs = generateCircuitInputsDSC(passportData, cscaTree); + const circuitName = getCircuitNameFromPassportData(passportData, 'dsc'); const endpointType = env === 'stg' ? 'staging_celo' : 'celo'; const endpoint = 'https://self.xyz'; return { inputs, circuitName, endpointType, endpoint }; @@ -161,75 +150,96 @@ function getSelectorDg1IdCard(disclosures: SelfAppDisclosureConfig) { return selector_dg1; } -export function generateTEEInputsAadhaarRegister( - secret: string, - aadhaarData: AadhaarData, - publicKeys: string[], - env: 'prod' | 'stg' -) { - const { prepareAadhaarRegisterData } = require('../aadhaar/mockData.js'); - const inputs = prepareAadhaarRegisterData(aadhaarData.qrData, secret, publicKeys); - const circuitName = 'register_aadhaar'; - const endpointType = env === 'stg' ? 'staging_celo' : 'celo'; - const endpoint = 'https://self.xyz'; - return { inputs, circuitName, endpointType, endpoint }; -} - -export function generateTEEInputsAadhaarDisclose( +export function generateTEEInputsDiscloseStateless( secret: string, - aadhaarData: AadhaarData, + passportData: PassportData, selfApp: SelfApp, getTree: ( doc: DocumentCategory, tree: T ) => T extends 'ofac' ? OfacTree : any ) { - const { prepareAadhaarDiscloseData } = require('../aadhaar/mockData.js'); const { scope, disclosures, endpoint, userId, userDefinedData, chainID } = selfApp; const userIdentifierHash = calculateUserIdentifierHash(chainID, userId, userDefinedData); const scope_hash = hashEndpointWithScope(endpoint, scope); + const document: DocumentCategory = passportData.documentCategory; - const ofac_trees = getTree('aadhaar', 'ofac'); + const selector_dg1 = getSelectorDg1(document, disclosures); + + const majority = disclosures.minimumAge ? disclosures.minimumAge.toString() : DEFAULT_MAJORITY; + const selector_older_than = disclosures.minimumAge ? '1' : '0'; + + const selector_ofac = disclosures.ofac ? 1 : 0; + + const ofac_trees = getTree(document, 'ofac'); if (!ofac_trees) { throw new Error('OFAC trees not loaded'); } + // Validate OFAC tree structure if (!ofac_trees.nameAndDob || !ofac_trees.nameAndYob) { throw new Error('Invalid OFAC tree structure: missing required fields'); } + if (document === 'passport' && !ofac_trees.passportNoAndNationality) { + throw new Error('Invalid OFAC tree structure: missing passportNoAndNationality for passport'); + } + let passportNoAndNationalitySMT: SMT | null = null; const nameAndDobSMT = new SMT(poseidon2, true); const nameAndYobSMT = new SMT(poseidon2, true); + if (document === 'passport') { + passportNoAndNationalitySMT = new SMT(poseidon2, true); + passportNoAndNationalitySMT.import(ofac_trees.passportNoAndNationality); + } nameAndDobSMT.import(ofac_trees.nameAndDob); nameAndYobSMT.import(ofac_trees.nameAndYob); - const serialized_tree = getTree('aadhaar', 'commitment'); + const serialized_tree = getTree(document, 'commitment'); const tree = LeanIMT.import((a, b) => poseidon2([a, b]), serialized_tree); - - const inputs = prepareAadhaarDiscloseData( - aadhaarData.qrData, + const inputs = generateCircuitInputsVCandDisclose( + secret, + document === 'passport' ? PASSPORT_ATTESTATION_ID : ID_CARD_ATTESTATION_ID, + passportData, + scope_hash, + selector_dg1, + selector_older_than, tree, + majority, + passportNoAndNationalitySMT, nameAndDobSMT, nameAndYobSMT, - scope_hash, - secret, - userIdentifierHash.toString(), - { - dateOfBirth: disclosures.date_of_birth, - name: disclosures.name, - gender: disclosures.gender, - idNumber: disclosures.passport_number, - issuingState: disclosures.issuing_state, - minimumAge: disclosures.minimumAge, - forbiddenCountriesListPacked: disclosures.excludedCountries, - ofac: disclosures.ofac, - } + selector_ofac, + disclosures.excludedCountries ?? [], + userIdentifierHash.toString() ); - return { inputs, - circuitName: 'vc_and_disclose_aadhaar', + circuitName: + passportData.documentCategory === 'passport' ? 'vc_and_disclose' : 'vc_and_disclose_id', endpointType: selfApp.endpointType, endpoint: selfApp.endpoint, }; } + +export async function generateTEEInputsRegister( + secret: string, + passportData: IDDocument, + dscTree: string | string[], + env: 'prod' | 'stg' +) { + + if (passportData.documentCategory === 'aadhaar') { + const { inputs, circuitName, endpointType, endpoint } = await generateTEEInputsAadhaarRegister(secret, passportData, dscTree as string[], env); + console.log('inputs-aadhaar', inputs); + console.log('circuitName-aadhaar', circuitName); + console.log('endpointType-aadhaar', endpointType); + console.log('endpoint-aadhaar', endpoint); + return { inputs, circuitName, endpointType, endpoint }; + } + + const inputs = generateCircuitInputsRegister(secret, passportData, dscTree as string); + const circuitName = getCircuitNameFromPassportData(passportData, 'register'); + const endpointType = env === 'stg' ? 'staging_celo' : 'celo'; + const endpoint = 'https://self.xyz'; + return { inputs, circuitName, endpointType, endpoint }; +} diff --git a/common/src/utils/passports/genMockIdDoc.ts b/common/src/utils/passports/genMockIdDoc.ts index b5e9c35a7..df5a4814d 100644 --- a/common/src/utils/passports/genMockIdDoc.ts +++ b/common/src/utils/passports/genMockIdDoc.ts @@ -53,8 +53,8 @@ const defaultIdDocInput: IdDocInput = { birthDate: '900101', expiryDate: '300101', passportNumber: '123456789', - lastName: 'DOE', - firstName: 'JOHN', + lastName: undefined, + firstName: undefined, sex: 'M', // Aadhaar defaults pincode: '110051', @@ -93,26 +93,42 @@ avCtlYniKHPvSCA/gS2h4fk= `; // TODO: Move it to correct place. -const AADHAAR_MOCK_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv/9mI6uE6RJtbeRZuDDP -/vDIRj3fb0wGaaNOC+x2BW3uGHRZl2QS1x+imy62yt7oTLqvXZqLSYg7cVo3IeX+ -Yh8Kiy1CrEhyZeDK9nhWY++00y5MUxFGBCEqUZvSUiB2EYO5HBT20fgqxLgHvbN/ -6iwwA/OYKgwoqLI+mwqLzC2sh0GIJajdqW2DafGSBWlY9YHZKEIPMAiuTg948uQi -Z7yYqzbolq73US2ePKNNS+2/9gf4rgVraAbTmgkz1mbrQSHCgeqDVCgW2M8BuNDc -VqULQHmOjVtAgv2qalBGVTy3WIo47CxgVKIR07SwlwfwtSgzYdLDU3zFPG5HLS6r -1wIDAQAB ------END PUBLIC KEY----- +const AADHAAR_MOCK_PUBLIC_KEY_PEM = `-----BEGIN CERTIFICATE----- +MIIDjzCCAnegAwIBAgIUZA6u4qBxEjW4dxmbLaLkWnHIybowDQYJKoZIhvcNAQEL +BQAwVzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYDVQQHDARDaXR5 +MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0y +NTA5MTgxMDE2NTlaFw0yNjA5MTgxMDE2NTlaMFcxCzAJBgNVBAYTAlVTMQ4wDAYD +VQQIDAVTdGF0ZTENMAsGA1UEBwwEQ2l0eTEVMBMGA1UECgwMT3JnYW5pemF0aW9u +MRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQC//2Yjq4TpEm1t5Fm4MM/+8MhGPd9vTAZpo04L7HYFbe4YdFmXZBLXH6Kb +LrbK3uhMuq9dmotJiDtxWjch5f5iHwqLLUKsSHJl4Mr2eFZj77TTLkxTEUYEISpR +m9JSIHYRg7kcFPbR+CrEuAe9s3/qLDAD85gqDCiosj6bCovMLayHQYglqN2pbYNp +8ZIFaVj1gdkoQg8wCK5OD3jy5CJnvJirNuiWrvdRLZ48o01L7b/2B/iuBWtoBtOa +CTPWZutBIcKB6oNUKBbYzwG40NxWpQtAeY6NW0CC/apqUEZVPLdYijjsLGBUohHT +tLCXB/C1KDNh0sNTfMU8bkctLqvXAgMBAAGjUzBRMB0GA1UdDgQWBBTGyVMLFNL2 +PRJwtA8vekrtJVu2BTAfBgNVHSMEGDAWgBTGyVMLFNL2PRJwtA8vekrtJVu2BTAP +BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCwcKlyaZw3jxDNtU6j +V8g9tUr77z0LyTrVe0GujxFaa4EKKKqG/lzf6wNDaHGOgyEwhPsi/ui8VU6Y8KTS +SxorUta+2zNHu8jziz1rxYTfgPWvK54B3Q3q4ycRLmYfR0CVvH2+TvTAqfEEvpEh +8tY9mpNzjYsLzlwPszkWU+WpLJjH0VPhVIiFC65EaxuArZrap8IpuK/bSa4Beqbb +7rMo/KmDfhFpVMQcOrvyQJmurtmjo12Esb0EjwZp634nDVRC9gFXEh5YuWBg3IaI +cTCvHQ+MAXTzZMOfc2dWZYdk1PaO6xLTw0YfGAtl6r3x4Csd0i5iwpDo1JXjSpZE +mESQ +-----END CERTIFICATE----- `; // Generate mock Aadhaar document function genMockAadhaarDoc(input: IdDocInput): AadhaarData { const name = input.firstName ? `${input.firstName} ${input.lastName || ''}`.trim() - : 'Sumit Kumar'; + : generateRandomName(); + const gender = input.sex === 'F' ? 'F' : 'M'; const pincode = input.pincode ?? '110051'; const state = input.state ?? 'Delhi'; - const dateOfBirth = input.birthDate ?? '010190'; + const dateOfBirth = input.birthDate ?? '01-01-1990'; + console.log('genMockAadhaarDoc', input); + console.log('dateOfBirth', dateOfBirth); // Generate Aadhaar QR data using processQRData const qrData = processQRData( @@ -135,6 +151,8 @@ function genMockAadhaarDoc(input: IdDocInput): AadhaarData { ); const signature = Array.from(signatureBytes); + console.log('qrData.extractedFields', qrData.extractedFields); + return { documentType: input.idType as DocumentType, documentCategory: 'aadhaar', @@ -151,14 +169,18 @@ export function genMockIdDoc( userInput: Partial = {}, mockDSC?: { dsc: string; privateKeyPem: string } ): PassportData | AadhaarData { + + if (userInput.idType === 'mock_aadhaar') { + return genMockAadhaarDoc(userInput as IdDocInput); + } + const mergedInput: IdDocInput = { ...defaultIdDocInput, ...userInput, }; - if (mergedInput.idType === 'mock_aadhaar') { - return genMockAadhaarDoc(mergedInput); - } + mergedInput.lastName = mergedInput.lastName ?? 'DOE'; + mergedInput.firstName = mergedInput.firstName ?? 'JOHN'; let privateKeyPem: string, dsc: string; if (mockDSC) { @@ -220,6 +242,19 @@ export async function generateMockDSC( return { privateKeyPem: data.data.privateKeyPem, dsc: data.data.dsc }; } +function generateRandomName(): string { + // Generate random letter combinations for first and last name + const generateRandomLetters = (length: number): string => { + const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + return Array.from({ length }, () => letters[Math.floor(Math.random() * letters.length)]).join(''); + }; + + const firstName = generateRandomLetters(4 + Math.floor(Math.random() * 4)); // 4-7 letters + const lastName = generateRandomLetters(5 + Math.floor(Math.random() * 5)); // 5-9 letters + + return `${firstName} ${lastName}`; +} + function generateRandomBytes(length: number): number[] { // Generate numbers between -128 and 127 to match the existing signed byte format return Array.from({ length }, () => Math.floor(Math.random() * 256) - 128); diff --git a/common/src/utils/passports/passport.ts b/common/src/utils/passports/passport.ts index d69f6696f..5456bdcee 100644 --- a/common/src/utils/passports/passport.ts +++ b/common/src/utils/passports/passport.ts @@ -31,7 +31,7 @@ import { findStartIndex, findStartIndexEC } from '../csca.js'; import { hash, packBytesAndPoseidon } from '../hash.js'; import { sha384_512Pad, shaPad } from '../shaPad.js'; import { getLeafDscTree } from '../trees.js'; -import type { DocumentCategory, PassportData, SignatureAlgorithm } from '../types.js'; +import type { DocumentCategory, IDDocument, PassportData, SignatureAlgorithm } from '../types.js'; import { AadhaarData,isAadhaarDocument, isMRZDocument } from '../types.js'; import { formatMrz } from './format.js'; import { parsePassportData } from './passport_parsing/parsePassportData.js'; @@ -190,11 +190,11 @@ function getPassportSignature(passportData: PassportData, n: number, k: number): } } -export function generateNullifier(passportData: PassportData) { +export function generateNullifier(passportData: IDDocument) { - // if (isAadhaarDocument(passportData)) { - // return nullifierHash(passportData.extractedFields); - // } + if (isAadhaarDocument(passportData)) { + return nullifierHash(passportData.extractedFields); + } const signedAttr_shaBytes = hash( passportData.passportMetadata.signedAttrHashFunction, diff --git a/common/src/utils/passports/validate.ts b/common/src/utils/passports/validate.ts index 3e4bb6cab..72a1b5ba7 100644 --- a/common/src/utils/passports/validate.ts +++ b/common/src/utils/passports/validate.ts @@ -16,10 +16,13 @@ import { packBytesAndPoseidon } from '../../utils/hash/poseidon.js'; import { hash } from '../../utils/hash/sha.js'; import { formatMrz } from '../../utils/passports/format.js'; import { getLeafDscTree } from '../../utils/trees.js'; +import { computeCommitment, computePackedCommitment, nullifierHash, processQRDataSimple } from '../aadhaar/mockData.js'; import { + AadhaarData, AttestationIdHex, type DeployedCircuits, type DocumentCategory, + IDDocument, type PassportData, } from '../types.js'; import { generateCommitment, generateNullifier } from './passport.js'; @@ -33,8 +36,34 @@ export type PassportSupportStatus = | 'dsc_circuit_not_supported' | 'passport_supported'; -export async function checkDocumentSupported( +function validateRegistrationCircuit( + passportData: IDDocument, + deployedCircuits: DeployedCircuits +): { isValid: boolean; circuitName: string | null } { + let circuitNameRegister = getCircuitNameFromPassportData(passportData as PassportData, 'register'); + + const isValid = circuitNameRegister && ( + deployedCircuits.REGISTER.includes(circuitNameRegister) || + deployedCircuits.REGISTER_ID.includes(circuitNameRegister) || + deployedCircuits.REGISTER_AADHAAR.includes(circuitNameRegister) + ); + return { isValid: !!isValid, circuitName: circuitNameRegister }; +} + +function validateDscCircuit( passportData: PassportData, + deployedCircuits: DeployedCircuits +): { isValid: boolean; circuitName: string | null } { + const circuitNameDsc = getCircuitNameFromPassportData(passportData, 'dsc'); + const isValid = circuitNameDsc && ( + deployedCircuits.DSC.includes(circuitNameDsc) || + deployedCircuits.DSC_ID.includes(circuitNameDsc) + ); + return { isValid: !!isValid, circuitName: circuitNameDsc }; +} + +export async function checkDocumentSupported( + passportData: IDDocument, opts: { getDeployedCircuits: (docCategory: DocumentCategory) => DeployedCircuits; } @@ -42,8 +71,20 @@ export async function checkDocumentSupported( status: PassportSupportStatus; details: string; }> { + const deployedCircuits = opts.getDeployedCircuits(passportData.documentCategory); + if (passportData.documentCategory === 'aadhaar') { + const { isValid, circuitName } = validateRegistrationCircuit(passportData, deployedCircuits); + + if (!isValid) { + return { + status: 'registration_circuit_not_supported', + details: circuitName, + }; + } + return { status: 'passport_supported', details: circuitName }; + } + const passportMetadata = passportData.passportMetadata; - const document: DocumentCategory = passportData.documentCategory; if (!passportMetadata) { console.warn('Passport metadata is null'); return { status: 'passport_metadata_missing', details: passportData.dsc }; @@ -52,36 +93,26 @@ export async function checkDocumentSupported( console.warn('CSCA not found'); return { status: 'csca_not_found', details: passportData.dsc }; } - const circuitNameRegister = getCircuitNameFromPassportData(passportData, 'register'); - const deployedCircuits = opts.getDeployedCircuits(passportData.documentCategory); - if ( - !circuitNameRegister || - !( - deployedCircuits.REGISTER.includes(circuitNameRegister) || - deployedCircuits.REGISTER_ID.includes(circuitNameRegister) - ) - ) { + + const { isValid: isRegisterValid, circuitName: registerCircuitName } = validateRegistrationCircuit(passportData, deployedCircuits); + if (!isRegisterValid) { return { status: 'registration_circuit_not_supported', - details: circuitNameRegister, + details: registerCircuitName, }; } - const circuitNameDsc = getCircuitNameFromPassportData(passportData, 'dsc'); - if ( - !circuitNameDsc || - !( - deployedCircuits.DSC.includes(circuitNameDsc) || - deployedCircuits.DSC_ID.includes(circuitNameDsc) - ) - ) { - console.warn('DSC circuit not supported:', circuitNameDsc); - return { status: 'dsc_circuit_not_supported', details: circuitNameDsc }; + + const { isValid: isDscValid, circuitName: dscCircuitName } = validateDscCircuit(passportData as PassportData, deployedCircuits); + if (!isDscValid) { + console.warn('DSC circuit not supported:', dscCircuitName); + return { status: 'dsc_circuit_not_supported', details: dscCircuitName }; } - return { status: 'passport_supported', details: 'null' }; + + return { status: 'passport_supported', details: dscCircuitName }; } export async function checkIfPassportDscIsInTree( - passportData: PassportData, + passportData: IDDocument, dscTree: string ): Promise { const hashFunction = (a: bigint, b: bigint) => poseidon2([a, b]); @@ -146,7 +177,51 @@ export function generateCommitmentInApp( return { commitment_list, csca_list }; } -export async function isDocumentNullified(passportData: PassportData) { +export function generateCommitmentInAppAadhaar( + secret: string, + attestation_id: string, + passportData: AadhaarData, + alternativePublicKeys: Record +) { + const nullifier = nullifierHash(passportData.extractedFields); + const packedCommitment = computePackedCommitment(passportData.extractedFields); + const { qrHash } = processQRDataSimple(passportData.qrData); + + const publicKey_list: string[] = []; + const commitment_list: string[] = []; + + // For Aadhaar, we can also use the document's own public key + const allPublicKeys = { + 'document_public_key': passportData.publicKey, + ...alternativePublicKeys + }; + + for (const [keyName, publicKeyValue] of Object.entries(allPublicKeys)) { + try { + + const commitment = computeCommitment( + BigInt(secret), + BigInt(qrHash), + nullifier, + packedCommitment, + BigInt(passportData.photoHash || '0') + ).toString(); + + publicKey_list.push(publicKeyValue); + commitment_list.push(commitment); + } catch (error) { + console.warn(`Failed to process public key for ${keyName}:`, error); + } + } + + if (commitment_list.length === 0) { + console.error('No valid public keys found for Aadhaar'); + } + + return { commitment_list, publicKey_list }; +} + +export async function isDocumentNullified(passportData: IDDocument) { const nullifier = generateNullifier(passportData); const nullifierHex = `0x${BigInt(nullifier).toString(16)}`; const attestationId = @@ -181,17 +256,37 @@ export async function isDocumentNullified(passportData: PassportData) { } export async function isUserRegistered( - passportData: PassportData, + documentData: PassportData | AadhaarData, secret: string, getCommitmentTree: (docCategory: DocumentCategory) => string ) { - if (!passportData) { + if (!documentData) { return false; } - const attestationId = - passportData.documentCategory === 'passport' ? PASSPORT_ATTESTATION_ID : ID_CARD_ATTESTATION_ID; - const commitment = generateCommitment(secret, attestationId, passportData); - const document: DocumentCategory = passportData.documentCategory; + + const document: DocumentCategory = documentData.documentCategory; + let commitment: string; + + if (document === 'aadhaar') { + const aadhaarData = documentData as AadhaarData; + const nullifier = nullifierHash(aadhaarData.extractedFields); + const packedCommitment = computePackedCommitment(aadhaarData.extractedFields); + const { qrHash } = processQRDataSimple(aadhaarData.qrData); + + commitment = computeCommitment( + BigInt(secret), + BigInt(qrHash), + nullifier, + packedCommitment, + BigInt(aadhaarData.photoHash || '0') + ).toString(); + + console.log('commitment', commitment); + } else { + const attestationId = document === 'passport' ? PASSPORT_ATTESTATION_ID : ID_CARD_ATTESTATION_ID; + commitment = generateCommitment(secret, attestationId, documentData as PassportData); + } + const serializedTree = getCommitmentTree(document); const tree = LeanIMT.import((a, b) => poseidon2([a, b]), serializedTree); const index = tree.indexOf(BigInt(commitment)); @@ -199,7 +294,7 @@ export async function isUserRegistered( } export async function isUserRegisteredWithAlternativeCSCA( - passportData: PassportData, + passportData: IDDocument, secret: string, { getCommitmentTree, @@ -214,21 +309,55 @@ export async function isUserRegisteredWithAlternativeCSCA( return { isRegistered: false, csca: null }; } const document: DocumentCategory = passportData.documentCategory; - const alternativeCSCA = getAltCSCA(document); - const { commitment_list, csca_list } = generateCommitmentInApp( - secret, - document === 'passport' ? PASSPORT_ATTESTATION_ID : ID_CARD_ATTESTATION_ID, - passportData, - alternativeCSCA - ); + let commitment_list: string[]; + let csca_list: string[]; + + if (document === 'aadhaar') { + // For Aadhaar, use public keys from protocol store instead of CSCA + const publicKeys = getAltCSCA(document); + if (!publicKeys || Object.keys(publicKeys).length === 0) { + console.error('No public keys available for Aadhaar'); + return { isRegistered: false, csca: null }; + } + + // Create alternative public keys object from protocol store + const alternativePublicKeys: Record = {}; + Object.entries(publicKeys).forEach(([key, value], index) => { + alternativePublicKeys[`public_key_${index}`] = value; + }); + + const result = generateCommitmentInAppAadhaar( + secret, + AttestationIdHex.aadhaar, + passportData as AadhaarData, + alternativePublicKeys + ); + commitment_list = result.commitment_list; + csca_list = result.publicKey_list; + } else { + // For passport/id_card, use CSCA certificates + const alternativeCSCA = getAltCSCA(document); + const result = generateCommitmentInApp( + secret, + document === 'passport' ? PASSPORT_ATTESTATION_ID : ID_CARD_ATTESTATION_ID, + passportData as PassportData, + alternativeCSCA + ); + commitment_list = result.commitment_list; + csca_list = result.csca_list; + } if (commitment_list.length === 0) { - console.error('No valid CSCA certificates could be parsed from alternativeCSCA'); + const errorMsg = document === 'aadhaar' + ? 'No valid public keys could be processed for Aadhaar' + : 'No valid CSCA certificates could be parsed from alternativeCSCA'; + console.error(errorMsg); return { isRegistered: false, csca: null }; } const serializedTree = getCommitmentTree(document); const tree = LeanIMT.import((a, b) => poseidon2([a, b]), serializedTree); + for (let i = 0; i < commitment_list.length; i++) { const commitment = commitment_list[i]; const index = tree.indexOf(BigInt(commitment)); @@ -236,7 +365,11 @@ export async function isUserRegisteredWithAlternativeCSCA( return { isRegistered: true, csca: csca_list[i] }; } } - console.warn('None of the following CSCA correspond to the commitment:', csca_list); + + const warnMsg = document === 'aadhaar' + ? `None of the following public keys correspond to the commitment for Aadhaar: ${csca_list}` + : `None of the following CSCA correspond to the commitment: ${csca_list}`; + console.warn(warnMsg); return { isRegistered: false, csca: null }; } diff --git a/common/src/utils/proving.ts b/common/src/utils/proving.ts index eb6446c82..3e58c6f20 100644 --- a/common/src/utils/proving.ts +++ b/common/src/utils/proving.ts @@ -32,9 +32,9 @@ export const ec = new EC('p256'); // eslint-disable-next-line -- clientKey is created from ec so must be second export const clientKey = ec.genKeyPair(); -type RegisterSuffixes = '' | '_id'; +type RegisterSuffixes = '' | '_id' | '_aadhaar'; type DscSuffixes = '' | '_id'; -type DiscloseSuffixes = '' | '_id'; +type DiscloseSuffixes = '' | '_id' | '_aadhaar'; type ProofTypes = 'register' | 'dsc' | 'disclose'; type RegisterProofType = `${Extract}${RegisterSuffixes}`; type DscProofType = `${Extract}${DscSuffixes}`; @@ -67,8 +67,11 @@ export function getPayload( userDefinedData: string = '' ) { if (circuitType === 'disclose') { + const type = circuitName === 'vc_and_disclose' ? 'disclose' : + circuitName === 'disclose_aadhaar' ? 'disclose_aadhaar' : + 'disclose_id'; const payload: TEEPayloadDisclose = { - type: circuitName === 'vc_and_disclose' ? 'disclose' : 'disclose_id', + type, endpointType: endpointType, endpoint: endpoint, onchain: endpointType === 'celo' ? true : false, @@ -81,8 +84,9 @@ export function getPayload( }; return payload; } else { + const type = circuitName === 'register_aadhaar' ? 'register_aadhaar' : circuitType; const payload: TEEPayload = { - type: circuitType as RegisterProofType | DscProofType, + type: type as RegisterProofType | DscProofType, onchain: true, endpointType: endpointType, circuit: { diff --git a/common/src/utils/types.ts b/common/src/utils/types.ts index bcb09be1f..2ccaf52bf 100644 --- a/common/src/utils/types.ts +++ b/common/src/utils/types.ts @@ -24,6 +24,7 @@ export interface AadhaarData extends BaseIDData { export type DeployedCircuits = { REGISTER: string[]; REGISTER_ID: string[]; + REGISTER_AADHAAR: string[]; DSC: string[]; DSC_ID: string[]; }; @@ -53,6 +54,16 @@ export type DocumentType = | 'mock_id_card' | 'mock_aadhaar'; + +export type IDDocument = AadhaarData | PassportData; + + +export type OfacTree = { + passportNoAndNationality: any; + nameAndDob: any; + nameAndYob: any; +}; + // Define the signature algorithm in "algorithm_hashfunction_domainPapameter_keyLength" export interface PassportData extends BaseIDData { documentCategory: 'passport' | 'id_card'; @@ -67,14 +78,6 @@ export interface PassportData extends BaseIDData { passportMetadata?: PassportMetadata; } -export type IDDocument = AadhaarData | PassportData; - -export type OfacTree = { - passportNoAndNationality: any; - nameAndDob: any; - nameAndYob: any; -}; - export type Proof = { proof: { diff --git a/packages/mobile-sdk-alpha/src/documents/utils.ts b/packages/mobile-sdk-alpha/src/documents/utils.ts index c5ad9ab76..64228717d 100644 --- a/packages/mobile-sdk-alpha/src/documents/utils.ts +++ b/packages/mobile-sdk-alpha/src/documents/utils.ts @@ -3,13 +3,14 @@ // NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE. import { + AadhaarData, brutforceSignatureAlgorithmDsc, + isMRZDocument, parseCertificateSimple, PublicKeyDetailsECDSA, PublicKeyDetailsRSA, } from '@selfxyz/common'; import { calculateContentHash, inferDocumentCategory } from '@selfxyz/common/utils'; -import { AadhaarData, PassportData, isMRZDocument } from '@selfxyz/common'; import { DocumentMetadata, IDDocument } from '@selfxyz/common/utils/types'; import { SelfClient } from '../types/public'; @@ -123,11 +124,10 @@ export async function markCurrentDocumentAsRegistered(selfClient: SelfClient): P } } -export async function reStorePassportDataWithRightCSCA( - selfClient: SelfClient, - passportData: PassportData, - csca: string, -) { +export async function reStorePassportDataWithRightCSCA(selfClient: SelfClient, passportData: IDDocument, csca: string) { + if (passportData.documentCategory === 'aadhaar') { + return; + } const cscaInCurrentPassporData = passportData.passportMetadata?.csca; if (!(csca === cscaInCurrentPassporData)) { const cscaParsed = parseCertificateSimple(csca); diff --git a/packages/mobile-sdk-alpha/src/mock/generator.ts b/packages/mobile-sdk-alpha/src/mock/generator.ts index 62786e49a..efe25c1da 100644 --- a/packages/mobile-sdk-alpha/src/mock/generator.ts +++ b/packages/mobile-sdk-alpha/src/mock/generator.ts @@ -26,7 +26,7 @@ const formatDateToDDMMYYYY = (date: Date): string => { '-' + date.toISOString().slice(5, 7) + '-' + - date.toISOString().slice(2, 4) + date.toISOString().slice(0, 4) ).toString(); }; diff --git a/packages/mobile-sdk-alpha/src/stores/protocolStore.ts b/packages/mobile-sdk-alpha/src/stores/protocolStore.ts index c6d10dcec..580655193 100644 --- a/packages/mobile-sdk-alpha/src/stores/protocolStore.ts +++ b/packages/mobile-sdk-alpha/src/stores/protocolStore.ts @@ -414,9 +414,7 @@ export const useProtocolStore = create((set, get) => ({ if (responseData && typeof responseData === 'object' && 'status' in responseData) { if (responseData.status !== 'success' || !responseData.data) { - throw new Error( - `Failed to fetch tree from ${url}: ${responseData.message || 'Invalid response format'}` - ); + throw new Error(`Failed to fetch tree from ${url}: ${responseData.message || 'Invalid response format'}`); } return responseData.data; } @@ -424,10 +422,7 @@ export const useProtocolStore = create((set, get) => ({ return responseData; }; - const [nameDobData, nameYobData] = await Promise.all([ - fetchTree(nameDobUrl), - fetchTree(nameYobUrl), - ]); + const [nameDobData, nameYobData] = await Promise.all([fetchTree(nameDobUrl), fetchTree(nameYobUrl)]); set({ aadhaar: {