mirror of
https://github.com/selfxyz/self.git
synced 2026-02-19 02:24:25 -05:00
SELF-1938 sumsub integration (#1661)
* Sumsub: Update keychain and types * sumsub: ProvingMachine changes - WIP * fix: remove duplicate identifier * update proving machine * Refactor && Continue onchain registration if user left the app * fix register flow * Add hooks to KycSuccessScreen * Integrate KycVerifiedScreen (#1686) * Integrate KycVerifiedScreen & Fix race conditions * yarn lint * lint * lint * add mock kyc * fix disclose flow * yarn lint * Feat/add kyc home screen card design (#1708) * feat: add new designs to the kycIdCard * refactor: Update KycIdCard design to match IdCard styling * feat: update document cards + dev document * feat: update empty id card for new design * feat: update pending document card design * feat: update expired doc + unregistered doc cards from new design * fix: unregisted id card button links to continue registration screen * fix: logo design on document cards * feat: add 6 different backgrounds for ids deterministically shows 1 of 6 backgrounds for each document | fix: fixed document designs not displaying correctly. * chore: trigger CI rebuild * feat: Integrate PendingIdCard to Homescreen * fix KycIdCard.tsx --------- Co-authored-by: seshanthS <seshanth@protonmail.com> * lint * fix tests * fix: cleanup only on unmount * coderabbit comments * fix: cleanup unused code * fix: edge case for German Passports with D<< nationality code * fix tests * review comments * review comments * lint * Hide duplicated cards in Homescreen * remove console.log * fix patch * remove unused vars * agent updates * agent feedback * abstract colors and formatting * agent feedback * Regenerate Sumsub patch-package patch * fix: handle malformed kyc payload in card background selector * re-add for clean up --------- Co-authored-by: Evi Nova <66773372+Tranquil-Flow@users.noreply.github.com> Co-authored-by: Evi Nova <tranquil_flow@protonmail.com> Co-authored-by: Justin Hernandez <justin.hernandez@self.xyz>
This commit is contained in:
@@ -26,6 +26,7 @@ export type { Environment } from './src/utils/types.js';
|
||||
|
||||
// Utils exports
|
||||
export {
|
||||
AADHAAR_ATTESTATION_ID,
|
||||
API_URL,
|
||||
API_URL_STAGING,
|
||||
CSCA_TREE_URL,
|
||||
@@ -42,9 +43,8 @@ export {
|
||||
IDENTITY_TREE_URL_STAGING,
|
||||
IDENTITY_TREE_URL_STAGING_ID_CARD,
|
||||
ID_CARD_ATTESTATION_ID,
|
||||
PASSPORT_ATTESTATION_ID,
|
||||
AADHAAR_ATTESTATION_ID,
|
||||
KYC_ATTESTATION_ID,
|
||||
PASSPORT_ATTESTATION_ID,
|
||||
PCR0_MANAGER_ADDRESS,
|
||||
REDIRECT_URL,
|
||||
RPC_URL,
|
||||
@@ -102,6 +102,23 @@ export {
|
||||
stringToBigInt,
|
||||
} from './src/utils/index.js';
|
||||
|
||||
export {
|
||||
KYC_ID_NUMBER_INDEX,
|
||||
KYC_ID_NUMBER_LENGTH,
|
||||
KYC_MAX_LENGTH,
|
||||
} from './src/utils/kyc/constants.js';
|
||||
|
||||
export type { KycData } from './src/utils/kyc/types.js';
|
||||
export { serializeKycData } from './src/utils/kyc/types.js';
|
||||
|
||||
export {
|
||||
NON_OFAC_DUMMY_INPUT,
|
||||
OFAC_DUMMY_INPUT,
|
||||
generateKycDiscloseInput,
|
||||
generateKycRegisterInput,
|
||||
generateMockKycRegisterInput,
|
||||
} from './src/utils/kyc/generateInputs.js';
|
||||
|
||||
// Crypto polyfill for cross-platform compatibility
|
||||
export {
|
||||
createHash,
|
||||
@@ -121,10 +138,11 @@ export {
|
||||
hash,
|
||||
packBytesAndPoseidon,
|
||||
} from './src/utils/hash.js';
|
||||
export { deserializeApplicantInfo } from './src/utils/kyc/api.js';
|
||||
|
||||
export { generateTestData, testCustomData } from './src/utils/aadhaar/utils.js';
|
||||
|
||||
export { isAadhaarDocument, isMRZDocument } from './src/utils/index.js';
|
||||
export { isAadhaarDocument, isKycDocument, isMRZDocument } from './src/utils/index.js';
|
||||
|
||||
export {
|
||||
prepareAadhaarDiscloseData,
|
||||
@@ -132,19 +150,3 @@ export {
|
||||
prepareAadhaarRegisterData,
|
||||
prepareAadhaarRegisterTestData,
|
||||
} from './src/utils/aadhaar/mockData.js';
|
||||
|
||||
export {
|
||||
generateKycDiscloseInput,
|
||||
generateMockKycRegisterInput,
|
||||
NON_OFAC_DUMMY_INPUT,
|
||||
OFAC_DUMMY_INPUT,
|
||||
generateKycRegisterInput,
|
||||
} from './src/utils/kyc/generateInputs.js';
|
||||
|
||||
export {
|
||||
KYC_MAX_LENGTH,
|
||||
KYC_ID_NUMBER_INDEX,
|
||||
KYC_ID_NUMBER_LENGTH,
|
||||
} from './src/utils/kyc/constants.js';
|
||||
|
||||
export { serializeKycData, KycData } from './src/utils/kyc/types.js';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { IDDocument, PassportData } from '../types.js';
|
||||
import { type IDDocument, isKycDocument, type PassportData } from '../types.js';
|
||||
|
||||
export function getCircuitNameFromPassportData(
|
||||
passportData: IDDocument,
|
||||
@@ -14,6 +14,10 @@ export function getCircuitNameFromPassportData(
|
||||
function getDSCircuitNameFromPassportData(passportData: IDDocument) {
|
||||
console.log('Getting DSC circuit name from passport data...');
|
||||
|
||||
if (isKycDocument(passportData)) {
|
||||
throw new Error('KYC documents do not have a DSC circuit');
|
||||
}
|
||||
|
||||
if (passportData.documentCategory === 'aadhaar') {
|
||||
throw new Error('Aadhaar does not have a DSC circuit');
|
||||
}
|
||||
@@ -87,6 +91,10 @@ function getRegisterNameFromPassportData(passportData: IDDocument) {
|
||||
return 'register_aadhaar';
|
||||
}
|
||||
|
||||
if (isKycDocument(passportData)) {
|
||||
return 'register_kyc';
|
||||
}
|
||||
|
||||
if (!passportData.passportMetadata) {
|
||||
console.error('Passport metadata is missing');
|
||||
throw new Error('Passport data are not parsed');
|
||||
|
||||
@@ -18,77 +18,24 @@ import {
|
||||
getCircuitNameFromPassportData,
|
||||
hashEndpointWithScope,
|
||||
} from '../../utils/index.js';
|
||||
import type { AadhaarData, Environment, IDDocument, OfacTree } from '../../utils/types.js';
|
||||
import type {
|
||||
AadhaarData,
|
||||
Environment,
|
||||
IDDocument,
|
||||
KycData as KycIDData,
|
||||
OfacTree,
|
||||
} from '../../utils/types.js';
|
||||
import { KycField } from '../kyc/constants.js';
|
||||
import {
|
||||
generateKycDiscloseInputFromData,
|
||||
generateKycRegisterInput,
|
||||
} from '../kyc/generateInputs.js';
|
||||
|
||||
import { LeanIMT } from '@openpassport/zk-kit-lean-imt';
|
||||
import { SMT } from '@openpassport/zk-kit-smt';
|
||||
import { KycField } from '../kyc/constants.js';
|
||||
|
||||
export { generateCircuitInputsRegister } from './generateInputs.js';
|
||||
|
||||
// export function generateTEEInputsKycDisclose( secret: string,
|
||||
// kycData: KycData,
|
||||
// selfApp: SelfApp,
|
||||
// getTree: <T extends 'ofac' | 'commitment'>(
|
||||
// doc: DocumentCategory,
|
||||
// tree: T
|
||||
// ) => T extends 'ofac' ? OfacTree : any
|
||||
|
||||
// ) {
|
||||
|
||||
// const {generateKycInputWithOutSig} = require('../kyc/generateInputs.js');
|
||||
|
||||
// const { scope, disclosures, userId, userDefinedData, chainID } = selfApp;
|
||||
// const userIdentifierHash = calculateUserIdentifierHash(chainID, userId, userDefinedData);
|
||||
|
||||
// // Map SelfAppDisclosureConfig to KycField array
|
||||
// const mapDisclosuresToKycFields = (config: SelfAppDisclosureConfig): KycField[] => {
|
||||
// const mapping: [keyof SelfAppDisclosureConfig, KycField][] = [
|
||||
// ['issuing_state', 'ADDRESS'],
|
||||
// ['nationality', 'COUNTRY'],
|
||||
// ['name', 'FULL_NAME'],
|
||||
// ['passport_number', 'ID_NUMBER'],
|
||||
// ['date_of_birth', 'DOB'],
|
||||
// ['gender', 'GENDER'],
|
||||
// ['expiry_date', 'EXPIRY_DATE'],
|
||||
// ];
|
||||
// return mapping.filter(([key]) => config[key]).map(([_, field]) => field);
|
||||
// };
|
||||
|
||||
// const ofac_trees = getTree('kyc', 'ofac');
|
||||
// if (!ofac_trees) {
|
||||
// throw new Error('OFAC trees not loaded');
|
||||
// }
|
||||
|
||||
// if (!ofac_trees.nameAndDob || !ofac_trees.nameAndYob) {
|
||||
// throw new Error('Invalid OFAC tree structure: missing required fields');
|
||||
// }
|
||||
|
||||
// const nameAndDobSMT = new SMT(poseidon2, true);
|
||||
// const nameAndYobSMT = new SMT(poseidon2, true);
|
||||
// nameAndDobSMT.import(ofac_trees.nameAndDob);
|
||||
// nameAndYobSMT.import(ofac_trees.nameAndYob);
|
||||
|
||||
// const inputs = generateKycInputWithOutSig(
|
||||
// kycData.serializedRealData,
|
||||
// nameAndDobSMT,
|
||||
// nameAndYobSMT,
|
||||
// disclosures.ofac,
|
||||
// scope,
|
||||
// userIdentifierHash.toString(),
|
||||
// mapDisclosuresToKycFields(disclosures),
|
||||
// disclosures.excludedCountries,
|
||||
// disclosures.minimumAge
|
||||
// );
|
||||
|
||||
// return {
|
||||
// inputs,
|
||||
// circuitName: 'vc_and_disclose_kyc',
|
||||
// endpointType: selfApp.endpointType,
|
||||
// endpoint: selfApp.endpoint,
|
||||
// };
|
||||
// }
|
||||
|
||||
export function generateTEEInputsAadhaarDisclose(
|
||||
secret: string,
|
||||
aadhaarData: AadhaarData,
|
||||
@@ -182,45 +129,6 @@ export function generateTEEInputsDSC(
|
||||
return { inputs, circuitName, endpointType, endpoint };
|
||||
}
|
||||
|
||||
/*** DISCLOSURE ***/
|
||||
|
||||
function getSelectorDg1(document: DocumentCategory, disclosures: SelfAppDisclosureConfig) {
|
||||
switch (document) {
|
||||
case 'passport':
|
||||
return getSelectorDg1Passport(disclosures);
|
||||
case 'id_card':
|
||||
return getSelectorDg1IdCard(disclosures);
|
||||
}
|
||||
}
|
||||
|
||||
function getSelectorDg1Passport(disclosures: SelfAppDisclosureConfig) {
|
||||
const selector_dg1 = Array(88).fill('0');
|
||||
Object.entries(disclosures).forEach(([attribute, reveal]) => {
|
||||
if (['ofac', 'excludedCountries', 'minimumAge'].includes(attribute)) {
|
||||
return;
|
||||
}
|
||||
if (reveal) {
|
||||
const [start, end] = attributeToPosition[attribute as keyof typeof attributeToPosition];
|
||||
selector_dg1.fill('1', start, end + 1);
|
||||
}
|
||||
});
|
||||
return selector_dg1;
|
||||
}
|
||||
|
||||
function getSelectorDg1IdCard(disclosures: SelfAppDisclosureConfig) {
|
||||
const selector_dg1 = Array(90).fill('0');
|
||||
Object.entries(disclosures).forEach(([attribute, reveal]) => {
|
||||
if (['ofac', 'excludedCountries', 'minimumAge'].includes(attribute)) {
|
||||
return;
|
||||
}
|
||||
if (reveal) {
|
||||
const [start, end] = attributeToPosition_ID[attribute as keyof typeof attributeToPosition_ID];
|
||||
selector_dg1.fill('1', start, end + 1);
|
||||
}
|
||||
});
|
||||
return selector_dg1;
|
||||
}
|
||||
|
||||
export function generateTEEInputsDiscloseStateless(
|
||||
secret: string,
|
||||
passportData: IDDocument,
|
||||
@@ -239,15 +147,15 @@ export function generateTEEInputsDiscloseStateless(
|
||||
);
|
||||
return { inputs, circuitName, endpointType, endpoint };
|
||||
}
|
||||
// if (passportData.documentCategory === 'kyc') {
|
||||
// const { inputs, circuitName, endpointType, endpoint } = generateTEEInputsKycDisclose(
|
||||
// secret,
|
||||
// passportData,
|
||||
// selfApp,
|
||||
// getTree
|
||||
// );
|
||||
// return { inputs, circuitName, endpointType, endpoint };
|
||||
// }
|
||||
if (passportData.documentCategory === 'kyc') {
|
||||
const { inputs, circuitName, endpointType, endpoint } = generateTEEInputsKycDisclose(
|
||||
secret,
|
||||
passportData,
|
||||
selfApp,
|
||||
getTree
|
||||
);
|
||||
return { inputs, circuitName, endpointType, endpoint };
|
||||
}
|
||||
const { scope, disclosures, endpoint, userId, userDefinedData, chainID } = selfApp;
|
||||
const userIdentifierHash = calculateUserIdentifierHash(chainID, userId, userDefinedData);
|
||||
const scope_hash = hashEndpointWithScope(endpoint, scope);
|
||||
@@ -310,6 +218,111 @@ export function generateTEEInputsDiscloseStateless(
|
||||
};
|
||||
}
|
||||
|
||||
/*** DISCLOSURE ***/
|
||||
|
||||
function getSelectorDg1(document: DocumentCategory, disclosures: SelfAppDisclosureConfig) {
|
||||
switch (document) {
|
||||
case 'passport':
|
||||
return getSelectorDg1Passport(disclosures);
|
||||
case 'id_card':
|
||||
return getSelectorDg1IdCard(disclosures);
|
||||
}
|
||||
}
|
||||
|
||||
function getSelectorDg1Passport(disclosures: SelfAppDisclosureConfig) {
|
||||
const selector_dg1 = Array(88).fill('0');
|
||||
Object.entries(disclosures).forEach(([attribute, reveal]) => {
|
||||
if (['ofac', 'excludedCountries', 'minimumAge'].includes(attribute)) {
|
||||
return;
|
||||
}
|
||||
if (reveal) {
|
||||
const [start, end] = attributeToPosition[attribute as keyof typeof attributeToPosition];
|
||||
selector_dg1.fill('1', start, end + 1);
|
||||
}
|
||||
});
|
||||
return selector_dg1;
|
||||
}
|
||||
|
||||
function getSelectorDg1IdCard(disclosures: SelfAppDisclosureConfig) {
|
||||
const selector_dg1 = Array(90).fill('0');
|
||||
Object.entries(disclosures).forEach(([attribute, reveal]) => {
|
||||
if (['ofac', 'excludedCountries', 'minimumAge'].includes(attribute)) {
|
||||
return;
|
||||
}
|
||||
if (reveal) {
|
||||
const [start, end] = attributeToPosition_ID[attribute as keyof typeof attributeToPosition_ID];
|
||||
selector_dg1.fill('1', start, end + 1);
|
||||
}
|
||||
});
|
||||
return selector_dg1;
|
||||
}
|
||||
|
||||
export function generateTEEInputsKycDisclose(
|
||||
secret: string,
|
||||
kycData: KycIDData,
|
||||
selfApp: SelfApp,
|
||||
getTree: <T extends 'ofac' | 'commitment'>(
|
||||
doc: DocumentCategory,
|
||||
tree: T
|
||||
) => T extends 'ofac' ? OfacTree : any
|
||||
) {
|
||||
const { scope, disclosures, endpoint, userId, userDefinedData, chainID } = selfApp;
|
||||
const userIdentifierHash = calculateUserIdentifierHash(chainID, userId, userDefinedData);
|
||||
const scope_hash = hashEndpointWithScope(endpoint, scope);
|
||||
|
||||
// Map SelfAppDisclosureConfig to KycField array
|
||||
const mapDisclosuresToKycFields = (config: SelfAppDisclosureConfig): KycField[] => {
|
||||
const mapping: [keyof SelfAppDisclosureConfig, KycField][] = [
|
||||
['issuing_state', 'ADDRESS'],
|
||||
['nationality', 'COUNTRY'],
|
||||
['name', 'FULL_NAME'],
|
||||
['passport_number', 'ID_NUMBER'],
|
||||
['date_of_birth', 'DOB'],
|
||||
['gender', 'GENDER'],
|
||||
['expiry_date', 'EXPIRY_DATE'],
|
||||
];
|
||||
return mapping.filter(([key]) => config[key]).map(([_, field]) => field);
|
||||
};
|
||||
|
||||
const ofac_trees = getTree('kyc', 'ofac');
|
||||
if (!ofac_trees) {
|
||||
throw new Error('OFAC trees not loaded');
|
||||
}
|
||||
|
||||
if (!ofac_trees.nameAndDob || !ofac_trees.nameAndYob) {
|
||||
throw new Error('Invalid OFAC tree structure: missing required fields');
|
||||
}
|
||||
|
||||
const nameAndDobSMT = new SMT(poseidon2, true);
|
||||
const nameAndYobSMT = new SMT(poseidon2, true);
|
||||
nameAndDobSMT.import(ofac_trees.nameAndDob);
|
||||
nameAndYobSMT.import(ofac_trees.nameAndYob);
|
||||
|
||||
const serialized_tree = getTree('kyc', 'commitment');
|
||||
const tree = LeanIMT.import((a, b) => poseidon2([a, b]), serialized_tree);
|
||||
|
||||
const inputs = generateKycDiscloseInputFromData(
|
||||
kycData.serializedApplicantInfo,
|
||||
secret,
|
||||
nameAndDobSMT,
|
||||
nameAndYobSMT,
|
||||
tree,
|
||||
disclosures.ofac ?? false,
|
||||
scope_hash,
|
||||
userIdentifierHash.toString(),
|
||||
mapDisclosuresToKycFields(disclosures),
|
||||
disclosures.excludedCountries,
|
||||
disclosures.minimumAge
|
||||
);
|
||||
|
||||
return {
|
||||
inputs,
|
||||
circuitName: 'vc_and_disclose_kyc',
|
||||
endpointType: selfApp.endpointType,
|
||||
endpoint: selfApp.endpoint,
|
||||
};
|
||||
}
|
||||
|
||||
export async function generateTEEInputsRegister(
|
||||
secret: string,
|
||||
passportData: IDDocument,
|
||||
@@ -326,11 +339,26 @@ export async function generateTEEInputsRegister(
|
||||
return { inputs, circuitName, endpointType, endpoint };
|
||||
}
|
||||
|
||||
// if (passportData.documentCategory === 'kyc') {
|
||||
// throw new Error('Kyc does not support registration');
|
||||
// }
|
||||
if (passportData.documentCategory === 'kyc') {
|
||||
const inputs = await generateKycRegisterInput(
|
||||
passportData.serializedApplicantInfo,
|
||||
passportData.signature,
|
||||
[passportData.pubkey[0].toString(), passportData.pubkey[1].toString()],
|
||||
secret
|
||||
);
|
||||
return {
|
||||
inputs,
|
||||
circuitName: getCircuitNameFromPassportData(passportData, 'register'),
|
||||
endpointType: env === 'stg' ? 'staging_celo' : 'celo',
|
||||
endpoint: 'https://self.xyz',
|
||||
};
|
||||
}
|
||||
|
||||
const inputs = generateCircuitInputsRegister(secret, passportData, dscTree as string);
|
||||
const inputs = generateCircuitInputsRegister(
|
||||
secret,
|
||||
passportData as PassportData,
|
||||
dscTree as string
|
||||
);
|
||||
const circuitName = getCircuitNameFromPassportData(passportData, 'register');
|
||||
const endpointType = env === 'stg' ? 'staging_celo' : 'celo';
|
||||
const endpoint = 'https://self.xyz';
|
||||
|
||||
@@ -5,6 +5,7 @@ export type {
|
||||
DocumentCategory,
|
||||
DocumentMetadata,
|
||||
IDDocument,
|
||||
KycData,
|
||||
OfacTree,
|
||||
PassportData,
|
||||
} from './types.js';
|
||||
@@ -70,6 +71,6 @@ export {
|
||||
export { getCircuitNameFromPassportData } from './circuits/circuitsName.js';
|
||||
export { getSKIPEM } from './csca.js';
|
||||
export { initElliptic } from './certificate_parsing/elliptic.js';
|
||||
export { isAadhaarDocument, isMRZDocument } from './types.js';
|
||||
export { isAadhaarDocument, isKycDocument, isMRZDocument } from './types.js';
|
||||
export { parseCertificateSimple } from './certificate_parsing/parseCertificateSimple.js';
|
||||
export { parseDscCertificateData } from './passports/passport_parsing/parseDscCertificateData.js';
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//Helper function to destructure the kyc data from the api response
|
||||
import { Point } from '@zk-kit/baby-jubjub';
|
||||
// SPDX-FileCopyrightText: 2025 Social Connect Labs, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
// NOTE: Converts to Apache-2.0 on 2029-06-11 per LICENSE.
|
||||
|
||||
import {
|
||||
KYC_ADDRESS_INDEX,
|
||||
KYC_ADDRESS_LENGTH,
|
||||
@@ -26,11 +28,7 @@ import {
|
||||
} from './constants.js';
|
||||
import { KycData } from './types.js';
|
||||
|
||||
//accepts a base64 signature and returns a signature object
|
||||
export function deserializeSignature(signature: string): { R: Point<bigint>; s: bigint } {
|
||||
const [Rx, Ry, s] = Buffer.from(signature, 'base64').toString('utf-8').split(',').map(BigInt);
|
||||
return { R: [Rx, Ry] as Point<bigint>, s };
|
||||
}
|
||||
import { Point } from '@zk-kit/baby-jubjub';
|
||||
|
||||
//accepts a base64 applicant info and returns a kyc data object
|
||||
export function deserializeApplicantInfo(
|
||||
@@ -88,3 +86,9 @@ export function deserializeApplicantInfo(
|
||||
address,
|
||||
};
|
||||
}
|
||||
|
||||
//accepts a base64 signature and returns a signature object
|
||||
export function deserializeSignature(signature: string): { R: Point<bigint>; s: bigint } {
|
||||
const [Rx, Ry, s] = Buffer.from(signature, 'base64').toString('utf-8').split(',').map(BigInt);
|
||||
return { R: [Rx, Ry] as Point<bigint>, s };
|
||||
}
|
||||
|
||||
@@ -1,38 +1,23 @@
|
||||
import { SMT } from '@openpassport/zk-kit-smt';
|
||||
import { poseidon2 } from 'poseidon-lite';
|
||||
|
||||
import { COMMITMENT_TREE_DEPTH } from '../../constants/constants.js';
|
||||
import { formatCountriesList } from '../circuits/formatInputs.js';
|
||||
import { findIndexInTree, formatInput } from '../circuits/generateInputs.js';
|
||||
import { packBytesAndPoseidon } from '../hash.js';
|
||||
import {
|
||||
generateMerkleProof,
|
||||
generateSMTProof,
|
||||
getNameDobLeafKyc,
|
||||
getNameYobLeafKyc,
|
||||
} from '../trees.js';
|
||||
import { KycDiscloseInput, KycRegisterInput, serializeKycData, KycData } from './types.js';
|
||||
import { findIndexInTree, formatInput } from '../circuits/generateInputs.js';
|
||||
import { createKycSelector, KYC_MAX_LENGTH, KycField } from './constants.js';
|
||||
import { poseidon2 } from 'poseidon-lite';
|
||||
import { Base8, inCurve, mulPointEscalar, subOrder } from '@zk-kit/baby-jubjub';
|
||||
import { signEdDSA } from './ecdsa/ecdsa.js';
|
||||
import { LeanIMT } from '@openpassport/zk-kit-lean-imt';
|
||||
import { packBytesAndPoseidon } from '../hash.js';
|
||||
import { COMMITMENT_TREE_DEPTH } from '../../constants/constants.js';
|
||||
import { deserializeApplicantInfo, deserializeSignature } from './api.js';
|
||||
import { createKycSelector, KYC_MAX_LENGTH, KycField } from './constants.js';
|
||||
import { signEdDSA } from './ecdsa/ecdsa.js';
|
||||
import { KycData, KycDiscloseInput, KycRegisterInput, serializeKycData } from './types.js';
|
||||
|
||||
export const OFAC_DUMMY_INPUT: KycData = {
|
||||
country: 'KEN',
|
||||
idType: 'NATIONAL ID',
|
||||
idNumber: '12345678901234567890123456789012', //32 digits
|
||||
issuanceDate: '20200101',
|
||||
expiryDate: '20290101',
|
||||
fullName: 'ABBAS ABU',
|
||||
dob: '19481210',
|
||||
photoHash: '1234567890',
|
||||
phoneNumber: '1234567890',
|
||||
gender: 'M',
|
||||
address: '1234567890',
|
||||
user_identifier: '1234567890',
|
||||
current_date: '20250101',
|
||||
majority_age_ASCII: '20',
|
||||
selector_older_than: '1',
|
||||
};
|
||||
import { LeanIMT } from '@openpassport/zk-kit-lean-imt';
|
||||
import { SMT } from '@openpassport/zk-kit-smt';
|
||||
import { Base8, inCurve, mulPointEscalar, subOrder } from '@zk-kit/baby-jubjub';
|
||||
|
||||
export const NON_OFAC_DUMMY_INPUT: KycData = {
|
||||
country: 'KEN',
|
||||
@@ -52,66 +37,29 @@ export const NON_OFAC_DUMMY_INPUT: KycData = {
|
||||
selector_older_than: '1',
|
||||
};
|
||||
|
||||
export const OFAC_DUMMY_INPUT: KycData = {
|
||||
country: 'KEN',
|
||||
idType: 'NATIONAL ID',
|
||||
idNumber: '12345678901234567890123456789012', //32 digits
|
||||
issuanceDate: '20200101',
|
||||
expiryDate: '20290101',
|
||||
fullName: 'ABBAS ABU',
|
||||
dob: '19481210',
|
||||
photoHash: '1234567890',
|
||||
phoneNumber: '1234567890',
|
||||
gender: 'M',
|
||||
address: '1234567890',
|
||||
user_identifier: '1234567890',
|
||||
current_date: '20250101',
|
||||
majority_age_ASCII: '20',
|
||||
selector_older_than: '1',
|
||||
};
|
||||
|
||||
export const createKycDiscloseSelFromFields = (fieldsToReveal: KycField[]): string[] => {
|
||||
const [lowResult, highResult] = createKycSelector(fieldsToReveal);
|
||||
return [lowResult.toString(), highResult.toString()];
|
||||
};
|
||||
|
||||
export const generateMockKycRegisterInput = async (
|
||||
secretKey?: bigint,
|
||||
ofac?: boolean,
|
||||
secret?: string
|
||||
) => {
|
||||
const kycData = ofac ? OFAC_DUMMY_INPUT : NON_OFAC_DUMMY_INPUT;
|
||||
const serializedData = serializeKycData(kycData).padEnd(KYC_MAX_LENGTH, '\0');
|
||||
|
||||
const msgPadded = Array.from(serializedData, (x) => x.charCodeAt(0));
|
||||
|
||||
const sk = secretKey ? secretKey : BigInt(Math.floor(Math.random() * Number(subOrder - 2n))) + 1n;
|
||||
|
||||
const pk = mulPointEscalar(Base8, sk);
|
||||
console.assert(inCurve(pk), 'Point pk not on curve');
|
||||
console.assert(pk[0] != 0n && pk[1] != 0n, 'pk is zero');
|
||||
|
||||
const [sig, pubKey] = signEdDSA(sk, msgPadded);
|
||||
console.assert(BigInt(sig.S) < subOrder, ' s is greater than scalar field');
|
||||
|
||||
const kycRegisterInput: KycRegisterInput = {
|
||||
data_padded: msgPadded.map((x) => Number(x)),
|
||||
s: BigInt(sig.S),
|
||||
R: sig.R8 as [bigint, bigint],
|
||||
pubKey,
|
||||
secret: secret || '1234',
|
||||
};
|
||||
|
||||
return kycRegisterInput;
|
||||
};
|
||||
|
||||
export const generateKycRegisterInput = async (
|
||||
applicantInfoBase64: string,
|
||||
signatureBase64: string,
|
||||
pubkeyStr: [string, string],
|
||||
secret: string
|
||||
) => {
|
||||
const applicantInfo = deserializeApplicantInfo(applicantInfoBase64);
|
||||
const signature = deserializeSignature(signatureBase64);
|
||||
const pubkey = [BigInt(pubkeyStr[0]), BigInt(pubkeyStr[1])] as [bigint, bigint];
|
||||
|
||||
const serializedData = serializeKycData(applicantInfo);
|
||||
|
||||
const msgPadded = Array.from(serializedData, (x) => x.charCodeAt(0));
|
||||
|
||||
const kycRegisterInput: KycRegisterInput = {
|
||||
data_padded: msgPadded.map((x) => Number(x)),
|
||||
s: signature.s,
|
||||
R: signature.R,
|
||||
pubKey: pubkey,
|
||||
secret,
|
||||
};
|
||||
|
||||
return kycRegisterInput;
|
||||
};
|
||||
|
||||
export const generateCircuitInputsOfac = (data: KycData, smt: SMT, proofLevel: number) => {
|
||||
const name = data.fullName;
|
||||
const dob = data.dob;
|
||||
@@ -195,7 +143,9 @@ export const generateKycDiscloseInput = (
|
||||
leaf_depth: formatInput(leaf_depth),
|
||||
path: formatInput(merkle_path),
|
||||
siblings: formatInput(siblings),
|
||||
forbidden_countries_list: forbiddenCountriesList || [...Array(120)].map((x) => '0'),
|
||||
forbidden_countries_list: forbiddenCountriesList
|
||||
? formatInput(formatCountriesList(forbiddenCountriesList))
|
||||
: [...Array(120)].map((x) => '0'),
|
||||
ofac_name_dob_smt_leaf_key: nameDobInputs.smt_leaf_key,
|
||||
ofac_name_dob_smt_root: nameDobInputs.smt_root,
|
||||
ofac_name_dob_smt_siblings: nameDobInputs.smt_siblings,
|
||||
@@ -211,3 +161,141 @@ export const generateKycDiscloseInput = (
|
||||
|
||||
return circuitInput;
|
||||
};
|
||||
|
||||
export const generateKycDiscloseInputFromData = (
|
||||
serializedApplicantInfo: string,
|
||||
secret: string,
|
||||
nameDobSmt: SMT,
|
||||
nameYobSmt: SMT,
|
||||
identityTree: LeanIMT,
|
||||
ofac: boolean,
|
||||
scope: string,
|
||||
userIdentifier: string,
|
||||
fieldsToReveal?: KycField[],
|
||||
forbiddenCountriesList?: string[],
|
||||
minimumAge?: number
|
||||
): KycDiscloseInput => {
|
||||
// Decode base64 applicant info to get raw padded bytes for the circuit
|
||||
const rawData = Buffer.from(serializedApplicantInfo, 'base64').toString('utf-8');
|
||||
const serializedData = rawData.padEnd(KYC_MAX_LENGTH, '\0');
|
||||
const msgPadded = Array.from(serializedData, (x) => x.charCodeAt(0));
|
||||
|
||||
// Compute commitment
|
||||
const commitment = poseidon2([secret, packBytesAndPoseidon(msgPadded)]);
|
||||
|
||||
// Find in tree and generate merkle proof
|
||||
const index = findIndexInTree(identityTree, commitment);
|
||||
const {
|
||||
siblings,
|
||||
path: merkle_path,
|
||||
leaf_depth,
|
||||
} = generateMerkleProof(identityTree, index, COMMITMENT_TREE_DEPTH);
|
||||
|
||||
// Deserialize to get individual fields for OFAC lookups
|
||||
const applicantData = deserializeApplicantInfo(serializedApplicantInfo);
|
||||
const ofacData = {
|
||||
...applicantData,
|
||||
user_identifier: '',
|
||||
current_date: '',
|
||||
majority_age_ASCII: '',
|
||||
selector_older_than: '',
|
||||
} as KycData;
|
||||
const nameDobInputs = generateCircuitInputsOfac(ofacData, nameDobSmt, 2);
|
||||
const nameYobInputs = generateCircuitInputsOfac(ofacData, nameYobSmt, 1);
|
||||
|
||||
// Build disclosure selector
|
||||
const fieldsToRevealFinal = fieldsToReveal || [];
|
||||
const compressed_disclose_sel = createKycDiscloseSelFromFields(fieldsToRevealFinal);
|
||||
|
||||
// Age and date
|
||||
const majorityAgeASCII = minimumAge
|
||||
? minimumAge
|
||||
.toString()
|
||||
.padStart(3, '0')
|
||||
.split('')
|
||||
.map((x) => x.charCodeAt(0))
|
||||
: ['0', '0', '0'].map((x) => x.charCodeAt(0));
|
||||
|
||||
const currentDate = new Date().toISOString().split('T')[0].replace(/-/g, '').split('');
|
||||
|
||||
const circuitInput: KycDiscloseInput = {
|
||||
data_padded: formatInput(msgPadded),
|
||||
compressed_disclose_sel: compressed_disclose_sel,
|
||||
scope: scope,
|
||||
merkle_root: formatInput(BigInt(identityTree.root)),
|
||||
leaf_depth: formatInput(leaf_depth),
|
||||
path: formatInput(merkle_path),
|
||||
siblings: formatInput(siblings),
|
||||
forbidden_countries_list: forbiddenCountriesList
|
||||
? formatInput(formatCountriesList(forbiddenCountriesList))
|
||||
: [...Array(120)].map(() => '0'),
|
||||
ofac_name_dob_smt_leaf_key: nameDobInputs.smt_leaf_key,
|
||||
ofac_name_dob_smt_root: nameDobInputs.smt_root,
|
||||
ofac_name_dob_smt_siblings: nameDobInputs.smt_siblings,
|
||||
ofac_name_yob_smt_leaf_key: nameYobInputs.smt_leaf_key,
|
||||
ofac_name_yob_smt_root: nameYobInputs.smt_root,
|
||||
ofac_name_yob_smt_siblings: nameYobInputs.smt_siblings,
|
||||
selector_ofac: ofac ? ['1'] : ['0'],
|
||||
user_identifier: userIdentifier,
|
||||
current_date: currentDate,
|
||||
majority_age_ASCII: majorityAgeASCII,
|
||||
secret: secret,
|
||||
};
|
||||
|
||||
return circuitInput;
|
||||
};
|
||||
|
||||
export const generateKycRegisterInput = async (
|
||||
applicantInfoBase64: string,
|
||||
signatureBase64: string,
|
||||
pubkeyStr: [string, string],
|
||||
secret: string
|
||||
) => {
|
||||
const applicantInfo = deserializeApplicantInfo(applicantInfoBase64);
|
||||
const signature = deserializeSignature(signatureBase64);
|
||||
const pubkey = [BigInt(pubkeyStr[0]), BigInt(pubkeyStr[1])] as [bigint, bigint];
|
||||
|
||||
const serializedData = serializeKycData(applicantInfo).padEnd(KYC_MAX_LENGTH, '\0');
|
||||
|
||||
const msgPadded = Array.from(serializedData, (x) => x.charCodeAt(0));
|
||||
|
||||
const kycRegisterInput: KycRegisterInput = {
|
||||
data_padded: msgPadded,
|
||||
s: signature.s,
|
||||
R: signature.R,
|
||||
pubKey: pubkey,
|
||||
secret,
|
||||
};
|
||||
|
||||
return kycRegisterInput;
|
||||
};
|
||||
|
||||
export const generateMockKycRegisterInput = async (
|
||||
secretKey?: bigint,
|
||||
ofac?: boolean,
|
||||
secret?: string
|
||||
) => {
|
||||
const kycData = ofac ? OFAC_DUMMY_INPUT : NON_OFAC_DUMMY_INPUT;
|
||||
const serializedData = serializeKycData(kycData).padEnd(KYC_MAX_LENGTH, '\0');
|
||||
|
||||
const msgPadded = Array.from(serializedData, (x) => x.charCodeAt(0));
|
||||
|
||||
const sk = secretKey ? secretKey : BigInt(Math.floor(Math.random() * Number(subOrder - 2n))) + 1n;
|
||||
|
||||
const pk = mulPointEscalar(Base8, sk);
|
||||
console.assert(inCurve(pk), 'Point pk not on curve');
|
||||
console.assert(pk[0] != 0n && pk[1] != 0n, 'pk is zero');
|
||||
|
||||
const [sig, pubKey] = signEdDSA(sk, msgPadded);
|
||||
console.assert(BigInt(sig.S) < subOrder, ' s is greater than scalar field');
|
||||
|
||||
const kycRegisterInput: KycRegisterInput = {
|
||||
data_padded: msgPadded.map((x) => Number(x)),
|
||||
s: BigInt(sig.S),
|
||||
R: sig.R8 as [bigint, bigint],
|
||||
pubKey,
|
||||
secret: secret || '1234',
|
||||
};
|
||||
|
||||
return kycRegisterInput;
|
||||
};
|
||||
|
||||
43
common/src/utils/kyc/utils.ts
Normal file
43
common/src/utils/kyc/utils.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { poseidon2 } from 'poseidon-lite';
|
||||
|
||||
import { packBytesAndPoseidon } from '../hash.js';
|
||||
import { IDDocument, isKycDocument } from '../types.js';
|
||||
import { deserializeApplicantInfo } from './api.js';
|
||||
import {
|
||||
KYC_ID_NUMBER_INDEX,
|
||||
KYC_ID_NUMBER_LENGTH,
|
||||
KYC_ID_TYPE_INDEX,
|
||||
KYC_ID_TYPE_LENGTH,
|
||||
} from './constants.js';
|
||||
import { serializeKycData } from './types.js';
|
||||
|
||||
export const generateKycCommitment = (passportData: IDDocument, secret: string) => {
|
||||
if (isKycDocument(passportData)) {
|
||||
const applicantInfo = deserializeApplicantInfo(passportData.serializedApplicantInfo);
|
||||
const serializedData = serializeKycData(applicantInfo);
|
||||
const msgPadded = Array.from(serializedData, (x) => x.charCodeAt(0));
|
||||
const dataPadded = msgPadded.map((x) => Number(x));
|
||||
const commitment = poseidon2([secret, packBytesAndPoseidon(dataPadded)]);
|
||||
return commitment.toString();
|
||||
}
|
||||
};
|
||||
|
||||
export const generateKycNullifier = (passportData: IDDocument) => {
|
||||
if (isKycDocument(passportData)) {
|
||||
const applicantInfo = deserializeApplicantInfo(passportData.serializedApplicantInfo);
|
||||
const serializedData = serializeKycData(applicantInfo);
|
||||
const msgPadded = Array.from(serializedData, (x) => x.charCodeAt(0));
|
||||
const dataPadded = msgPadded.map((x) => Number(x));
|
||||
const idNumber = dataPadded.slice(
|
||||
KYC_ID_NUMBER_INDEX,
|
||||
KYC_ID_NUMBER_INDEX + KYC_ID_NUMBER_LENGTH
|
||||
);
|
||||
const nullifierInputs = [
|
||||
...'sumsub'.split('').map((x) => x.charCodeAt(0)),
|
||||
...idNumber,
|
||||
...dataPadded.slice(KYC_ID_TYPE_INDEX, KYC_ID_TYPE_INDEX + KYC_ID_TYPE_LENGTH),
|
||||
];
|
||||
const nullifier = packBytesAndPoseidon(nullifierInputs);
|
||||
return nullifier;
|
||||
}
|
||||
};
|
||||
@@ -29,14 +29,22 @@ import {
|
||||
import { formatInput } from '../circuits/generateInputs.js';
|
||||
import { findStartIndex, findStartIndexEC } from '../csca.js';
|
||||
import { hash, packBytesAndPoseidon } from '../hash.js';
|
||||
import { deserializeApplicantInfo } from '../kyc/api.js';
|
||||
import {
|
||||
KYC_ID_NUMBER_INDEX,
|
||||
KYC_ID_NUMBER_LENGTH,
|
||||
KYC_ID_TYPE_INDEX,
|
||||
KYC_ID_TYPE_LENGTH,
|
||||
} from '../kyc/constants.js';
|
||||
import { serializeKycData } from '../kyc/types.js';
|
||||
import { sha384_512Pad, shaPad } from '../shaPad.js';
|
||||
import { getLeafDscTree } from '../trees.js';
|
||||
import type { DocumentCategory, IDDocument, PassportData, SignatureAlgorithm } from '../types.js';
|
||||
import { AadhaarData, isAadhaarDocument, isMRZDocument } from '../types.js';
|
||||
import { AadhaarData, isAadhaarDocument, isKycDocument, isMRZDocument } from '../types.js';
|
||||
import { formatMrz } from './format.js';
|
||||
import { parsePassportData } from './passport_parsing/parsePassportData.js';
|
||||
|
||||
export function calculateContentHash(passportData: PassportData | AadhaarData): string {
|
||||
export function calculateContentHash(passportData: IDDocument): string {
|
||||
if (isMRZDocument(passportData) && passportData.eContent) {
|
||||
// eContent is likely a buffer or array, convert to string properly
|
||||
const eContentStr =
|
||||
@@ -47,6 +55,13 @@ export function calculateContentHash(passportData: PassportData | AadhaarData):
|
||||
return sha256(eContentStr);
|
||||
}
|
||||
|
||||
if (isKycDocument(passportData)) {
|
||||
const serializedData = passportData.serializedApplicantInfo;
|
||||
const parsedApplicantInfo = deserializeApplicantInfo(serializedData);
|
||||
const stableFields = `${parsedApplicantInfo.fullName}${parsedApplicantInfo.dob}${parsedApplicantInfo.country}${parsedApplicantInfo.idType}`;
|
||||
return sha256(stableFields);
|
||||
}
|
||||
|
||||
// For MRZ documents without eContent, hash core stable fields
|
||||
const stableData = {
|
||||
documentType: passportData.documentType,
|
||||
@@ -193,6 +208,23 @@ export function generateNullifier(passportData: IDDocument) {
|
||||
if (isAadhaarDocument(passportData)) {
|
||||
return nullifierHash(passportData.extractedFields);
|
||||
}
|
||||
if (isKycDocument(passportData)) {
|
||||
const applicantInfo = deserializeApplicantInfo(passportData.serializedApplicantInfo);
|
||||
const serializedData = serializeKycData(applicantInfo);
|
||||
const msgPadded = Array.from(serializedData, (x) => x.charCodeAt(0));
|
||||
const dataPadded = msgPadded.map((x) => Number(x));
|
||||
const idNumber = dataPadded.slice(
|
||||
KYC_ID_NUMBER_INDEX,
|
||||
KYC_ID_NUMBER_INDEX + KYC_ID_NUMBER_LENGTH
|
||||
);
|
||||
const nullifierInputs = [
|
||||
...'sumsub'.split('').map((x) => x.charCodeAt(0)),
|
||||
...idNumber,
|
||||
...dataPadded.slice(KYC_ID_TYPE_INDEX, KYC_ID_TYPE_INDEX + KYC_ID_TYPE_LENGTH),
|
||||
];
|
||||
const nullifier = packBytesAndPoseidon(nullifierInputs);
|
||||
return nullifier;
|
||||
}
|
||||
|
||||
const signedAttr_shaBytes = hash(
|
||||
passportData.passportMetadata.signedAttrHashFunction,
|
||||
@@ -318,6 +350,8 @@ export function getSignatureAlgorithmFullName(
|
||||
export function inferDocumentCategory(documentType: string): DocumentCategory {
|
||||
if (documentType.includes('passport')) {
|
||||
return 'passport' as DocumentCategory;
|
||||
} else if (documentType.includes('kyc')) {
|
||||
return 'kyc' as DocumentCategory;
|
||||
} else if (documentType.includes('id')) {
|
||||
return 'id_card' as DocumentCategory;
|
||||
} else if (documentType.includes('aadhaar')) {
|
||||
|
||||
@@ -22,12 +22,15 @@ import {
|
||||
nullifierHash,
|
||||
processQRDataSimple,
|
||||
} from '../aadhaar/mockData.js';
|
||||
import { generateKycCommitment, generateKycNullifier } from '../kyc/utils.js';
|
||||
import {
|
||||
AadhaarData,
|
||||
AttestationIdHex,
|
||||
type DeployedCircuits,
|
||||
type DocumentCategory,
|
||||
IDDocument,
|
||||
isKycDocument,
|
||||
KycData,
|
||||
type PassportData,
|
||||
} from '../types.js';
|
||||
import { generateCommitment, generateNullifier } from './passport.js';
|
||||
@@ -49,7 +52,8 @@ function validateRegistrationCircuit(
|
||||
circuitNameRegister &&
|
||||
(deployedCircuits.REGISTER.includes(circuitNameRegister) ||
|
||||
deployedCircuits.REGISTER_ID.includes(circuitNameRegister) ||
|
||||
deployedCircuits.REGISTER_AADHAAR.includes(circuitNameRegister));
|
||||
deployedCircuits.REGISTER_AADHAAR.includes(circuitNameRegister) ||
|
||||
deployedCircuits.REGISTER_KYC.includes(circuitNameRegister));
|
||||
return { isValid: !!isValid, circuitName: circuitNameRegister };
|
||||
}
|
||||
|
||||
@@ -82,7 +86,7 @@ export async function checkDocumentSupported(
|
||||
details: string;
|
||||
}> {
|
||||
const deployedCircuits = opts.getDeployedCircuits(passportData.documentCategory);
|
||||
if (passportData.documentCategory === 'aadhaar') {
|
||||
if (passportData.documentCategory === 'aadhaar' || passportData.documentCategory === 'kyc') {
|
||||
const { isValid, circuitName } = validateRegistrationCircuit(passportData, deployedCircuits);
|
||||
|
||||
if (!isValid) {
|
||||
@@ -241,7 +245,9 @@ export async function isDocumentNullified(passportData: IDDocument) {
|
||||
? AttestationIdHex.passport
|
||||
: passportData.documentCategory === 'aadhaar'
|
||||
? AttestationIdHex.aadhaar
|
||||
: AttestationIdHex.id_card;
|
||||
: passportData.documentCategory === 'kyc'
|
||||
? AttestationIdHex.kyc
|
||||
: AttestationIdHex.id_card;
|
||||
console.log('checking for nullifier', nullifierHex, attestationId);
|
||||
const baseUrl = passportData.mock === false ? API_URL : API_URL_STAGING;
|
||||
const controller = new AbortController();
|
||||
@@ -270,7 +276,7 @@ export async function isDocumentNullified(passportData: IDDocument) {
|
||||
}
|
||||
|
||||
export async function isUserRegistered(
|
||||
documentData: PassportData | AadhaarData,
|
||||
documentData: IDDocument,
|
||||
secret: string,
|
||||
getCommitmentTree: (docCategory: DocumentCategory) => string
|
||||
) {
|
||||
@@ -281,7 +287,9 @@ export async function isUserRegistered(
|
||||
const document: DocumentCategory = documentData.documentCategory;
|
||||
let commitment: string;
|
||||
|
||||
if (document === 'aadhaar') {
|
||||
if (isKycDocument(documentData)) {
|
||||
commitment = generateKycCommitment(documentData, secret);
|
||||
} else if (document === 'aadhaar') {
|
||||
const aadhaarData = documentData as AadhaarData;
|
||||
const nullifier = nullifierHash(aadhaarData.extractedFields);
|
||||
const packedCommitment = computePackedCommitment(aadhaarData.extractedFields);
|
||||
@@ -327,6 +335,11 @@ export async function isUserRegisteredWithAlternativeCSCA(
|
||||
let commitment_list: string[];
|
||||
let csca_list: string[];
|
||||
|
||||
if (document === 'kyc') {
|
||||
const isRegistered = await isUserRegistered(passportData, secret, getCommitmentTree);
|
||||
return { isRegistered, csca: null };
|
||||
}
|
||||
|
||||
if (document === 'aadhaar') {
|
||||
// For Aadhaar, use public keys from protocol store instead of CSCA
|
||||
const publicKeys = getAltCSCA(document);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import forge from 'node-forge';
|
||||
import { Buffer } from 'buffer';
|
||||
import forge from 'node-forge';
|
||||
|
||||
import { WS_DB_RELAYER, WS_DB_RELAYER_STAGING } from '../constants/index.js';
|
||||
import { initElliptic } from '../utils/certificate_parsing/elliptic.js';
|
||||
@@ -34,9 +34,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' | '_aadhaar';
|
||||
type RegisterSuffixes = '' | '_id' | '_aadhaar' | '_kyc';
|
||||
type DscSuffixes = '' | '_id';
|
||||
type DiscloseSuffixes = '' | '_id' | '_aadhaar';
|
||||
type DiscloseSuffixes = '' | '_id' | '_aadhaar' | '_kyc';
|
||||
type ProofTypes = 'register' | 'dsc' | 'disclose';
|
||||
type RegisterProofType = `${Extract<ProofTypes, 'register'>}${RegisterSuffixes}`;
|
||||
type DscProofType = `${Extract<ProofTypes, 'dsc'>}${DscSuffixes}`;
|
||||
@@ -59,6 +59,10 @@ export function encryptAES256GCM(plaintext: string, key: forge.util.ByteStringBu
|
||||
};
|
||||
}
|
||||
|
||||
function bigIntReplacer(_key: string, value: unknown): unknown {
|
||||
return typeof value === 'bigint' ? value.toString() : value;
|
||||
}
|
||||
|
||||
export function getPayload(
|
||||
inputs: any,
|
||||
circuitType: RegisterProofType | DscProofType | DiscloseProofType,
|
||||
@@ -75,7 +79,9 @@ export function getPayload(
|
||||
? 'disclose'
|
||||
: circuitName === 'vc_and_disclose_aadhaar'
|
||||
? 'disclose_aadhaar'
|
||||
: 'disclose_id';
|
||||
: circuitName === 'vc_and_disclose_kyc'
|
||||
? 'disclose_kyc'
|
||||
: 'disclose_id';
|
||||
const payload: TEEPayloadDisclose = {
|
||||
type,
|
||||
endpointType: endpointType,
|
||||
@@ -83,7 +89,7 @@ export function getPayload(
|
||||
onchain: endpointType === 'celo' ? true : false,
|
||||
circuit: {
|
||||
name: circuitName,
|
||||
inputs: JSON.stringify(inputs),
|
||||
inputs: JSON.stringify(inputs, bigIntReplacer),
|
||||
},
|
||||
version,
|
||||
userDefinedData,
|
||||
@@ -91,14 +97,19 @@ export function getPayload(
|
||||
};
|
||||
return payload;
|
||||
} else {
|
||||
const type = circuitName === 'register_aadhaar' ? 'register_aadhaar' : circuitType;
|
||||
const type =
|
||||
circuitName === 'register_aadhaar'
|
||||
? 'register_aadhaar'
|
||||
: circuitName === 'register_kyc'
|
||||
? 'register_kyc'
|
||||
: circuitType;
|
||||
const payload: TEEPayload = {
|
||||
type: type as RegisterProofType | DscProofType,
|
||||
onchain: true,
|
||||
endpointType: endpointType,
|
||||
circuit: {
|
||||
name: circuitName,
|
||||
inputs: JSON.stringify(inputs),
|
||||
inputs: JSON.stringify(inputs, bigIntReplacer),
|
||||
},
|
||||
};
|
||||
return payload;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { ExtractedQRData } from './aadhaar/utils.js';
|
||||
import type { CertificateData } from './certificate_parsing/dataStructure.js';
|
||||
import type { KycField } from './kyc/constants.js';
|
||||
import type { PassportMetadata } from './passports/passport_parsing/parsePassportData.js';
|
||||
import { KycField } from './kyc/constants.js';
|
||||
|
||||
// Base interface for common fields
|
||||
interface BaseIDData {
|
||||
@@ -22,16 +22,11 @@ export interface AadhaarData extends BaseIDData {
|
||||
photoHash?: string;
|
||||
}
|
||||
|
||||
// export interface KycData extends BaseIDData {
|
||||
// documentCategory: 'kyc';
|
||||
// serializedRealData: string;
|
||||
// kycFields: KycField[];
|
||||
// }
|
||||
|
||||
export type DeployedCircuits = {
|
||||
REGISTER: string[];
|
||||
REGISTER_ID: string[];
|
||||
REGISTER_AADHAAR: string[];
|
||||
REGISTER_KYC: string[];
|
||||
DSC: string[];
|
||||
DSC_ID: string[];
|
||||
};
|
||||
@@ -51,19 +46,28 @@ export interface DocumentMetadata {
|
||||
mock: boolean; // whether this is a mock document
|
||||
isRegistered?: boolean; // whether the document is registered onChain
|
||||
registeredAt?: number; // timestamp (epoch ms) when document was registered
|
||||
idType?: string; // for KYC documents: the ID type used (e.g. "passport", "drivers_licence")
|
||||
}
|
||||
|
||||
export type DocumentType =
|
||||
| 'passport'
|
||||
| 'id_card'
|
||||
| 'aadhaar'
|
||||
| 'drivers_licence'
|
||||
| 'mock_passport'
|
||||
| 'mock_id_card'
|
||||
| 'mock_aadhaar';
|
||||
|
||||
export type Environment = 'prod' | 'stg';
|
||||
|
||||
export type IDDocument = AadhaarData | PassportData;
|
||||
export type IDDocument = AadhaarData | KycData | PassportData;
|
||||
|
||||
export interface KycData extends BaseIDData {
|
||||
documentCategory: 'kyc';
|
||||
serializedApplicantInfo: string;
|
||||
signature: string;
|
||||
pubkey: string[];
|
||||
}
|
||||
|
||||
export type OfacTree = {
|
||||
passportNoAndNationality: any;
|
||||
@@ -85,6 +89,20 @@ export interface PassportData extends BaseIDData {
|
||||
passportMetadata?: PassportMetadata;
|
||||
}
|
||||
|
||||
// pending - pending sumsub verification
|
||||
// processing - sumsub verification completed and pending onchain confirmation
|
||||
// failed - sumsub verification failed
|
||||
export type PendingKycStatus = 'pending' | 'processing' | 'failed';
|
||||
|
||||
export interface PendingKycVerification {
|
||||
userId: string; // Correlation key from fetchAccessToken()
|
||||
createdAt: number; // Timestamp when verification started
|
||||
status: PendingKycStatus; // Current status
|
||||
errorMessage?: string; // Error message if failed
|
||||
timeoutAt: number; // When to consider timed out
|
||||
documentId?: string; // Content hash of stored KYC document
|
||||
}
|
||||
|
||||
export type Proof = {
|
||||
proof: {
|
||||
a: [string, string];
|
||||
@@ -156,6 +174,7 @@ export enum AttestationIdHex {
|
||||
passport = '0x0000000000000000000000000000000000000000000000000000000000000001',
|
||||
id_card = '0x0000000000000000000000000000000000000000000000000000000000000002',
|
||||
aadhaar = '0x0000000000000000000000000000000000000000000000000000000000000003',
|
||||
kyc = '0x0000000000000000000000000000000000000000000000000000000000000004',
|
||||
}
|
||||
|
||||
export function castCSCAProof(proof: any): Proof {
|
||||
@@ -169,15 +188,15 @@ export function castCSCAProof(proof: any): Proof {
|
||||
};
|
||||
}
|
||||
|
||||
export function isAadhaarDocument(
|
||||
passportData: PassportData | AadhaarData
|
||||
): passportData is AadhaarData {
|
||||
export function isAadhaarDocument(passportData: IDDocument): passportData is AadhaarData {
|
||||
return passportData.documentCategory === 'aadhaar';
|
||||
}
|
||||
|
||||
export function isMRZDocument(
|
||||
passportData: PassportData | AadhaarData
|
||||
): passportData is PassportData {
|
||||
export function isKycDocument(passportData: IDDocument): passportData is KycData {
|
||||
return passportData.documentCategory === 'kyc';
|
||||
}
|
||||
|
||||
export function isMRZDocument(passportData: IDDocument): passportData is PassportData {
|
||||
return (
|
||||
passportData.documentCategory === 'passport' || passportData.documentCategory === 'id_card'
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user