mirror of
https://github.com/selfxyz/self.git
synced 2026-02-19 02:24:25 -05:00
Feat/sumsub (#1654)
* fix: circuits and contracts * feat: add reverse ofac logic * feat: add onlyRole modifiers to functions * style: replace onlyOwner reference in comment code to role-based access * test: unskip and update governance tests for access control * test: fix PCR0 setup in kyc test --------- Co-authored-by: Evi Nova <tranquil_flow@protonmail.com>
This commit is contained in:
@@ -50,7 +50,7 @@ template VC_AND_DISCLOSE_KYC(
|
||||
var country_length = COUNTRY_LENGTH();
|
||||
var id_number_length = ID_NUMBER_LENGTH();
|
||||
var idNumberIdx = ID_NUMBER_INDEX();
|
||||
var compressed_bit_len = max_length/2;
|
||||
var compressed_bit_len = max_length % 2 == 1 ? (max_length - 1)/2 : max_length / 2;
|
||||
|
||||
signal input data_padded[max_length];
|
||||
signal input compressed_disclose_sel[2];
|
||||
@@ -86,14 +86,14 @@ template VC_AND_DISCLOSE_KYC(
|
||||
low_bits.in <== compressed_disclose_sel[0];
|
||||
|
||||
// Convert disclose_sel_high (next 133 bits) to bit array
|
||||
component high_bits = Num2Bits(compressed_bit_len);
|
||||
component high_bits = Num2Bits(max_length - compressed_bit_len);
|
||||
high_bits.in <== compressed_disclose_sel[1];
|
||||
|
||||
// Combine the bit arrays (little-endian format)
|
||||
for(var i = 0; i < compressed_bit_len; i++){
|
||||
disclose_sel[i] <== low_bits.out[i];
|
||||
}
|
||||
for(var i = 0; i < compressed_bit_len; i++){
|
||||
for(var i = 0; i < max_length - compressed_bit_len; i++){
|
||||
disclose_sel[compressed_bit_len + i] <== high_bits.out[i];
|
||||
}
|
||||
|
||||
@@ -135,3 +135,14 @@ template VC_AND_DISCLOSE_KYC(
|
||||
signal output nullifier <== Poseidon(2)([secret, scope]);
|
||||
signal output attestation_id <== 4;
|
||||
}
|
||||
|
||||
component main {
|
||||
public [
|
||||
scope,
|
||||
merkle_root,
|
||||
ofac_name_dob_smt_root,
|
||||
ofac_name_yob_smt_root,
|
||||
user_identifier,
|
||||
current_date
|
||||
]
|
||||
} = VC_AND_DISCLOSE_KYC(40, 64, 64, 33);
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "./vc_and_disclose_kyc.circom";
|
||||
|
||||
component main {
|
||||
public [
|
||||
scope,
|
||||
merkle_root,
|
||||
ofac_name_dob_smt_root,
|
||||
ofac_name_yob_smt_root,
|
||||
user_identifier,
|
||||
current_date,
|
||||
attestation_id
|
||||
]
|
||||
} = VC_AND_DISCLOSE_KYC(40, 64, 64, 33);
|
||||
@@ -11,6 +11,8 @@ template REGISTER_KYC() {
|
||||
var max_length = KYC_MAX_LENGTH();
|
||||
var country_length = COUNTRY_LENGTH();
|
||||
var id_number_length = ID_NUMBER_LENGTH();
|
||||
var id_type_length = ID_TYPE_LENGTH();
|
||||
var id_type_index = ID_TYPE_INDEX();
|
||||
var idNumberIdx = ID_NUMBER_INDEX();
|
||||
|
||||
signal input data_padded[max_length];
|
||||
@@ -49,7 +51,22 @@ template REGISTER_KYC() {
|
||||
for (var i = 0; i < id_number_length; i++) {
|
||||
id_num[i] <== data_padded[idNumberIdx + i];
|
||||
}
|
||||
signal output nullifier <== PackBytesAndPoseidon(id_number_length)(id_num);
|
||||
|
||||
signal nullifier_inputs[6 + id_number_length + id_type_length];
|
||||
|
||||
nullifier_inputs[0] <== 115; //s
|
||||
nullifier_inputs[1] <== 117; //u
|
||||
nullifier_inputs[2] <== 109; //m
|
||||
nullifier_inputs[3] <== 115; //s
|
||||
nullifier_inputs[4] <== 117; //u
|
||||
nullifier_inputs[5] <== 98; //b
|
||||
for (var i = 0; i < id_number_length; i++) {
|
||||
nullifier_inputs[i + 6] <== id_num[i];
|
||||
}
|
||||
for (var i = 0; i < id_type_length; i++) {
|
||||
nullifier_inputs[i + 6 + id_number_length] <== data_padded[id_type_index + i];
|
||||
}
|
||||
signal output nullifier <== PackBytesAndPoseidon(6 + id_number_length + id_type_length)(nullifier_inputs);
|
||||
signal output commitment <== Poseidon(2)([secret, msg_hasher.out]);
|
||||
|
||||
signal output pubkey_hash <== Poseidon(2)([verifyIdCommSig.Ax, verifyIdCommSig.Ay]);
|
||||
|
||||
@@ -72,20 +72,12 @@ function PHONE_NUMBER_LENGTH() {
|
||||
return 12;
|
||||
}
|
||||
|
||||
function DOCUMENT_INDEX() {
|
||||
function GENDER_INDEX() {
|
||||
return PHONE_NUMBER_INDEX() + PHONE_NUMBER_LENGTH();
|
||||
}
|
||||
|
||||
function DOCUMENT_LENGTH() {
|
||||
return 32;
|
||||
}
|
||||
|
||||
function GENDER_INDEX() {
|
||||
return DOCUMENT_INDEX() + DOCUMENT_LENGTH();
|
||||
}
|
||||
|
||||
function GENDER_LENGTH() {
|
||||
return 6;
|
||||
return 1;
|
||||
}
|
||||
|
||||
function ADDRESS_INDEX() {
|
||||
|
||||
@@ -18,7 +18,7 @@ CIRCUITS=(
|
||||
# "vc_and_disclose:20:true"
|
||||
# "vc_and_disclose_id:20:true"
|
||||
# "vc_and_disclose_aadhaar:20:true"
|
||||
"vc_and_disclose_selfrica:17:true"
|
||||
"vc_and_disclose_kyc:17:true"
|
||||
)
|
||||
|
||||
build_circuits "$CIRCUIT_TYPE" "$OUTPUT_DIR" "${CIRCUITS[@]}"
|
||||
|
||||
@@ -15,7 +15,7 @@ OUTPUT_DIR="build/${CIRCUIT_TYPE}"
|
||||
# Define circuits and their configurations
|
||||
# format: name:poweroftau:build_flag
|
||||
CIRCUITS=(
|
||||
"register_selfrica:15:true"
|
||||
"register_kyc:15:true"
|
||||
)
|
||||
|
||||
build_circuits "$CIRCUIT_TYPE" "$OUTPUT_DIR" "${CIRCUITS[@]}"
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -154,6 +154,33 @@ describe('VC_AND_DISCLOSE KYC Circuit Tests', () => {
|
||||
deepEqual(ofac_results, ['\x00', '\x00']);
|
||||
});
|
||||
|
||||
it('should return 0 for an OFAC person with reverse', async function () {
|
||||
this.timeout(0);
|
||||
const input = generateKycDiscloseInput(
|
||||
true,
|
||||
namedob_smt,
|
||||
nameyob_smt,
|
||||
tree as any,
|
||||
true,
|
||||
'0',
|
||||
'1234567890',
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
true,
|
||||
'1234',
|
||||
true
|
||||
);
|
||||
const witness = await circuit.calculateWitness(input);
|
||||
await circuit.checkConstraints(witness);
|
||||
|
||||
const revealedData_packed = await getRevealedDataPacked(witness);
|
||||
const revealedDataUnpacked = unpackReveal(revealedData_packed, 'id');
|
||||
const ofac_results = revealedDataUnpacked.slice(maxLength, maxLength + 2);
|
||||
|
||||
deepEqual(ofac_results, ['\x00', '\x00']);
|
||||
});
|
||||
|
||||
it('should return 1 for a non OFAC person', async function () {
|
||||
this.timeout(0);
|
||||
const input = generateKycDiscloseInput(
|
||||
@@ -193,7 +220,6 @@ describe('VC_AND_DISCLOSE KYC Circuit Tests', () => {
|
||||
'DOB',
|
||||
'PHOTO_HASH',
|
||||
'PHONE_NUMBER',
|
||||
'DOCUMENT',
|
||||
'GENDER',
|
||||
'ADDRESS',
|
||||
];
|
||||
@@ -251,7 +277,6 @@ describe('VC_AND_DISCLOSE KYC Circuit Tests', () => {
|
||||
'DOB',
|
||||
'PHOTO_HASH',
|
||||
'PHONE_NUMBER',
|
||||
'DOCUMENT',
|
||||
'GENDER',
|
||||
'ADDRESS',
|
||||
];
|
||||
|
||||
@@ -5,7 +5,7 @@ import { packBytesAndPoseidon } from '@selfxyz/common/utils/hash';
|
||||
import { poseidon2 } from 'poseidon-lite';
|
||||
import { generateMockKycRegisterInput } from '@selfxyz/common/utils/kyc/generateInputs.js';
|
||||
import { KycRegisterInput } from '@selfxyz/common/utils/kyc/types';
|
||||
import { KYC_ID_NUMBER_INDEX, KYC_ID_NUMBER_LENGTH } from '@selfxyz/common/utils/kyc/constants';
|
||||
import { KYC_ID_NUMBER_INDEX, KYC_ID_NUMBER_LENGTH, KYC_ID_TYPE_INDEX, KYC_ID_TYPE_LENGTH } from '@selfxyz/common/utils/kyc/constants';
|
||||
|
||||
describe('REGISTER KYC Circuit Tests', () => {
|
||||
let circuit: any;
|
||||
@@ -15,7 +15,7 @@ describe('REGISTER KYC Circuit Tests', () => {
|
||||
this.timeout(0);
|
||||
input = await generateMockKycRegisterInput(null, true, undefined);
|
||||
circuit = await wasmTester(
|
||||
path.join(__dirname, '../../circuits/register/instances/register_selfrica.circom'),
|
||||
path.join(__dirname, '../../circuits/register/instances/register_kyc.circom'),
|
||||
{
|
||||
verbose: true,
|
||||
logOutput: true,
|
||||
@@ -42,7 +42,8 @@ describe('REGISTER KYC Circuit Tests', () => {
|
||||
KYC_ID_NUMBER_INDEX,
|
||||
KYC_ID_NUMBER_INDEX + KYC_ID_NUMBER_LENGTH
|
||||
);
|
||||
const nullifier = packBytesAndPoseidon(idnumber.map((x) => Number(x)));
|
||||
const nullifierInputs = [...'sumsub'.split('').map((x) => x.charCodeAt(0)), ...idnumber, ...input.data_padded.slice(KYC_ID_TYPE_INDEX, KYC_ID_TYPE_INDEX + KYC_ID_TYPE_LENGTH)];
|
||||
const nullifier = packBytesAndPoseidon(nullifierInputs);
|
||||
const commitment = poseidon2([
|
||||
input.secret,
|
||||
packBytesAndPoseidon(input.data_padded.map((x) => Number(x))),
|
||||
|
||||
52
common/src/utils/kyc/build_kyc_ofac_smt.ts
Normal file
52
common/src/utils/kyc/build_kyc_ofac_smt.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
// import * as fs from 'fs';
|
||||
// import { fileURLToPath } from 'url';
|
||||
// import { dirname, join } from 'path';
|
||||
|
||||
// import { buildKycSMT } from '../trees.js';
|
||||
|
||||
// async function build_kyc_ofac_smt() {
|
||||
// let startTime = performance.now();
|
||||
// const __filename = fileURLToPath(import.meta.url);
|
||||
// const __dirname = dirname(__filename);
|
||||
// const baseInputPath = __dirname;
|
||||
// const baseOutputPath = join(__dirname, '../../../../circuits/tests/consts/ofac/');
|
||||
|
||||
// const names = JSON.parse(fs.readFileSync(join(baseInputPath, 'names.json'), 'utf-8'));
|
||||
|
||||
// // -----KYC DATA-----
|
||||
// console.log(`Reading data from ${baseInputPath}`);
|
||||
|
||||
// // -----KYC DATA-----
|
||||
// console.log('\nBuilding KYC SMTs...');
|
||||
// const nameAndDobKycTree = buildKycSMT(names, 'name_and_dob');
|
||||
// const nameAndYobKycTree = buildKycSMT(names, 'name_and_yob');
|
||||
|
||||
// console.log('\n--- Results ---');
|
||||
// console.log(
|
||||
// `KYC - Name & DOB Tree: Processed ${nameAndDobKycTree[0]}/${names.length} entries in ${nameAndDobKycTree[1].toFixed(2)} ms`
|
||||
// );
|
||||
// console.log(
|
||||
// `KYC - Name & YOB Tree: Processed ${nameAndYobKycTree[0]}/${names.length} entries in ${nameAndYobKycTree[1].toFixed(2)} ms`
|
||||
// );
|
||||
// console.log('Total Time:', (performance.now() - startTime).toFixed(2), 'ms');
|
||||
|
||||
// console.log(`\nExporting SMTs to ${baseOutputPath}...`);
|
||||
// const nameAndDobKycOfacJSON = nameAndDobKycTree[2].export();
|
||||
// const nameAndYobKycOfacJSON = nameAndYobKycTree[2].export();
|
||||
|
||||
// fs.writeFileSync(
|
||||
// `${baseOutputPath}nameAndDobKycSMT.json`,
|
||||
// JSON.stringify(nameAndDobKycOfacJSON)
|
||||
// );
|
||||
// fs.writeFileSync(
|
||||
// `${baseOutputPath}nameAndYobKycSMT.json`,
|
||||
// JSON.stringify(nameAndYobKycOfacJSON)
|
||||
// );
|
||||
|
||||
// console.log('✅ SMT export complete.');
|
||||
// }
|
||||
|
||||
// build_kyc_ofac_smt().catch((error) => {
|
||||
// console.error('Error building KYC OFAC SMTs:', error);
|
||||
// process.exit(1);
|
||||
// });
|
||||
@@ -5,7 +5,7 @@ export const KYC_ID_TYPE_INDEX = KYC_COUNTRY_INDEX + KYC_COUNTRY_LENGTH;
|
||||
export const KYC_ID_TYPE_LENGTH = 27;
|
||||
|
||||
export const KYC_ID_NUMBER_INDEX = KYC_ID_TYPE_INDEX + KYC_ID_TYPE_LENGTH;
|
||||
export const KYC_ID_NUMBER_LENGTH = 32; // Updated: max(20, 32) = 32
|
||||
export const KYC_ID_NUMBER_LENGTH = 32;
|
||||
|
||||
export const KYC_ISSUANCE_DATE_INDEX = KYC_ID_NUMBER_INDEX + KYC_ID_NUMBER_LENGTH;
|
||||
export const KYC_ISSUANCE_DATE_LENGTH = 8;
|
||||
@@ -25,11 +25,8 @@ export const KYC_PHOTO_HASH_LENGTH = 32;
|
||||
export const KYC_PHONE_NUMBER_INDEX = KYC_PHOTO_HASH_INDEX + KYC_PHOTO_HASH_LENGTH;
|
||||
export const KYC_PHONE_NUMBER_LENGTH = 12;
|
||||
|
||||
export const KYC_DOCUMENT_INDEX = KYC_PHONE_NUMBER_INDEX + KYC_PHONE_NUMBER_LENGTH;
|
||||
export const KYC_DOCUMENT_LENGTH = 32; // Updated: max(2, 32) = 32
|
||||
|
||||
export const KYC_GENDER_INDEX = KYC_DOCUMENT_INDEX + KYC_DOCUMENT_LENGTH;
|
||||
export const KYC_GENDER_LENGTH = 6;
|
||||
export const KYC_GENDER_INDEX = KYC_PHONE_NUMBER_INDEX + KYC_PHONE_NUMBER_LENGTH;
|
||||
export const KYC_GENDER_LENGTH = 1;
|
||||
|
||||
export const KYC_ADDRESS_INDEX = KYC_GENDER_INDEX + KYC_GENDER_LENGTH;
|
||||
export const KYC_ADDRESS_LENGTH = 100;
|
||||
@@ -49,8 +46,7 @@ export const KYC_FIELD_LENGTHS = {
|
||||
DOB: KYC_DOB_LENGTH, // 8
|
||||
PHOTO_HASH: KYC_PHOTO_HASH_LENGTH, // 32
|
||||
PHONE_NUMBER: KYC_PHONE_NUMBER_LENGTH, // 12
|
||||
DOCUMENT: KYC_DOCUMENT_LENGTH, // 32 (updated)
|
||||
GENDER: KYC_GENDER_LENGTH, // 6
|
||||
GENDER: KYC_GENDER_LENGTH, // 1
|
||||
ADDRESS: KYC_ADDRESS_LENGTH, // 100
|
||||
} as const;
|
||||
|
||||
@@ -67,8 +63,7 @@ export const KYC_REVEAL_DATA_INDICES = {
|
||||
DOB: KYC_FULL_NAME_INDEX + KYC_FULL_NAME_LENGTH, // 142 (updated)
|
||||
PHOTO_HASH: KYC_DOB_INDEX + KYC_DOB_LENGTH, // 150 (updated)
|
||||
PHONE_NUMBER: KYC_PHOTO_HASH_INDEX + KYC_PHOTO_HASH_LENGTH, // 182 (updated)
|
||||
DOCUMENT: KYC_PHONE_NUMBER_INDEX + KYC_PHONE_NUMBER_LENGTH, // 194 (updated)
|
||||
GENDER: KYC_DOCUMENT_INDEX + KYC_DOCUMENT_LENGTH, // 226 (updated)
|
||||
GENDER: KYC_PHONE_NUMBER_INDEX + KYC_PHONE_NUMBER_LENGTH, // 194 (updated)
|
||||
ADDRESS: KYC_GENDER_INDEX + KYC_GENDER_LENGTH, // 232 (updated)
|
||||
} as const;
|
||||
|
||||
@@ -106,14 +101,10 @@ export const KYC_SELECTOR_BITS = {
|
||||
{ length: KYC_PHONE_NUMBER_LENGTH },
|
||||
(_, i) => i + KYC_PHOTO_HASH_INDEX + KYC_PHOTO_HASH_LENGTH
|
||||
) as number[], // 182-193 (updated)
|
||||
DOCUMENT: Array.from(
|
||||
{ length: KYC_DOCUMENT_LENGTH },
|
||||
(_, i) => i + KYC_PHONE_NUMBER_INDEX + KYC_PHONE_NUMBER_LENGTH
|
||||
) as number[], // 194-225 (updated)
|
||||
GENDER: Array.from(
|
||||
{ length: KYC_GENDER_LENGTH },
|
||||
(_, i) => i + KYC_DOCUMENT_INDEX + KYC_DOCUMENT_LENGTH
|
||||
) as number[], // 226-231 (updated)
|
||||
(_, i) => i + KYC_PHONE_NUMBER_INDEX + KYC_PHONE_NUMBER_LENGTH
|
||||
) as number[], // 194-194 (updated)
|
||||
ADDRESS: Array.from(
|
||||
{ length: KYC_ADDRESS_LENGTH },
|
||||
(_, i) => i + KYC_GENDER_INDEX + KYC_GENDER_LENGTH
|
||||
|
||||
@@ -25,8 +25,7 @@ export const OFAC_DUMMY_INPUT: KycData = {
|
||||
dob: '19481210',
|
||||
photoHash: '1234567890',
|
||||
phoneNumber: '1234567890',
|
||||
document: 'ID',
|
||||
gender: 'Male',
|
||||
gender: 'M',
|
||||
address: '1234567890',
|
||||
user_identifier: '1234567890',
|
||||
current_date: '20250101',
|
||||
@@ -44,8 +43,7 @@ export const NON_OFAC_DUMMY_INPUT: KycData = {
|
||||
dob: '19900101',
|
||||
photoHash: '1234567890',
|
||||
phoneNumber: '1234567890',
|
||||
document: 'ID',
|
||||
gender: 'Male',
|
||||
gender: 'M',
|
||||
address: '1234567890',
|
||||
user_identifier: '1234567890',
|
||||
current_date: '20250101',
|
||||
@@ -124,9 +122,16 @@ export const generateKycDiscloseInput = (
|
||||
forbiddenCountriesList?: string[],
|
||||
minimumAge?: number,
|
||||
updateTree?: boolean,
|
||||
secret: string = '1234'
|
||||
secret: string = '1234',
|
||||
reverse?: boolean
|
||||
) => {
|
||||
const data = ofac_input ? OFAC_DUMMY_INPUT : NON_OFAC_DUMMY_INPUT;
|
||||
let data = ofac_input ? OFAC_DUMMY_INPUT : NON_OFAC_DUMMY_INPUT;
|
||||
if (reverse) {
|
||||
data = {
|
||||
...data,
|
||||
fullName: data.fullName.split(' ').reverse().join(' '),
|
||||
};
|
||||
}
|
||||
const serializedData = serializeKycData(data).padEnd(KYC_MAX_LENGTH, '\0');
|
||||
const msgPadded = Array.from(serializedData, (x) => x.charCodeAt(0));
|
||||
const commitment = poseidon2([secret, packBytesAndPoseidon(msgPadded)]);
|
||||
|
||||
@@ -11,7 +11,6 @@ export type KycData = {
|
||||
dob: string;
|
||||
photoHash: string;
|
||||
phoneNumber: string;
|
||||
document: string;
|
||||
gender: string;
|
||||
address: string;
|
||||
user_identifier: string;
|
||||
@@ -32,7 +31,6 @@ export const serializeKycData = (kycData: KycData) => {
|
||||
serializedData += kycData.dob.padEnd(constants.KYC_DOB_LENGTH, '\0');
|
||||
serializedData += kycData.photoHash.padEnd(constants.KYC_PHOTO_HASH_LENGTH, '\0');
|
||||
serializedData += kycData.phoneNumber.padEnd(constants.KYC_PHONE_NUMBER_LENGTH, '\0');
|
||||
serializedData += kycData.document.padEnd(constants.KYC_DOCUMENT_LENGTH, '\0');
|
||||
serializedData += kycData.gender.padEnd(constants.KYC_GENDER_LENGTH, '\0');
|
||||
serializedData += kycData.address.padEnd(constants.KYC_ADDRESS_LENGTH, '\0');
|
||||
|
||||
|
||||
@@ -723,20 +723,29 @@ export function buildKycSMT(field: any[], treetype: string): [number, number, SM
|
||||
console.log(`Processing ${providerName}`, treetype, 'number', i, 'out of', field.length);
|
||||
}
|
||||
|
||||
let leaf = BigInt(0);
|
||||
let leafs = [BigInt(0), BigInt(0)];
|
||||
if (treetype == 'name_and_dob') {
|
||||
leaf = processNameAndDobKyc(entry, i);
|
||||
leafs[0] = processNameAndDobKyc(entry, i, false);
|
||||
leafs[1] = processNameAndDobKyc(entry, i, true);
|
||||
} else if (treetype == 'name_and_yob') {
|
||||
leaf = processNameAndYobKyc(entry, i);
|
||||
leafs[0] = processNameAndYobKyc(entry, i, false);
|
||||
leafs[1] = processNameAndYobKyc(entry, i, true);
|
||||
}
|
||||
|
||||
if (leaf == BigInt(0) || tree.createProof(leaf).membership) {
|
||||
if (leafs[0] == BigInt(0) || tree.createProof(leafs[0]).membership) {
|
||||
console.log('This entry already exists in the tree, skipping...');
|
||||
continue;
|
||||
} else if (leafs[1] == BigInt(0) || tree.createProof(leafs[1]).membership) {
|
||||
console.log('This entry already exists in the tree, skipping...');
|
||||
continue;
|
||||
}
|
||||
|
||||
tree.add(leafs[0], BigInt(1));
|
||||
count += 1;
|
||||
tree.add(leaf, BigInt(1));
|
||||
if (leafs[0] != leafs[1]) {
|
||||
tree.add(leafs[1], BigInt(1));
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Total ${providerName}`, treetype, 'parsed are : ', count, ' over ', field.length);
|
||||
@@ -744,7 +753,7 @@ export function buildKycSMT(field: any[], treetype: string): [number, number, SM
|
||||
return [count, performance.now() - startTime, tree];
|
||||
}
|
||||
|
||||
const processNameAndDobKyc = (entry: any, i: number): bigint => {
|
||||
const processNameAndDobKyc = (entry: any, i: number, reverse: boolean): bigint => {
|
||||
const firstName = entry.First_Name;
|
||||
const lastName = entry.Last_Name;
|
||||
const day = entry.day;
|
||||
@@ -756,7 +765,7 @@ const processNameAndDobKyc = (entry: any, i: number): bigint => {
|
||||
return BigInt(0);
|
||||
}
|
||||
|
||||
const nameHash = processNameKyc(firstName, lastName, i);
|
||||
const nameHash = processNameKyc(firstName, lastName, i, reverse);
|
||||
const dobHash = processDobKyc(day, month, year, i);
|
||||
|
||||
return generateSmallKey(poseidon2([dobHash, nameHash]));
|
||||
@@ -773,7 +782,7 @@ export const getNameDobLeafKyc = (name: string, dob: string) => {
|
||||
return generateSmallKey(poseidon2([dobHash, nameHash]));
|
||||
};
|
||||
|
||||
const processNameKyc = (firstName: string, lastName: string, i: number): bigint => {
|
||||
const processNameKyc = (firstName: string, lastName: string, i: number, reverse: boolean): bigint => {
|
||||
const namePaddingLength = 64;
|
||||
|
||||
firstName = firstName.replace(/'/g, '');
|
||||
@@ -783,8 +792,8 @@ const processNameKyc = (firstName: string, lastName: string, i: number): bigint
|
||||
lastName = lastName.replace(/[- ]/g, '<');
|
||||
lastName = lastName.replace(/\./g, '');
|
||||
|
||||
//TODO: check if smile id does first name and last name || last name and first name
|
||||
const nameArr = (lastName + ' ' + firstName)
|
||||
let nameStr = reverse ? (lastName + ' ' + firstName) : (firstName + ' ' + lastName);
|
||||
const nameArr = nameStr
|
||||
.padEnd(namePaddingLength, '\0')
|
||||
.split('')
|
||||
.map((char) => char.charCodeAt(0));
|
||||
@@ -825,7 +834,7 @@ export const getNameYobLeafKyc = (name: string, yob: string) => {
|
||||
return generateSmallKey(poseidon2([yearHash, nameHash]));
|
||||
};
|
||||
|
||||
const processNameAndYobKyc = (entry: any, i: number): bigint => {
|
||||
const processNameAndYobKyc = (entry: any, i: number, reverse: boolean): bigint => {
|
||||
const firstName = entry.First_Name;
|
||||
const lastName = entry.Last_Name;
|
||||
const year = entry.year;
|
||||
@@ -834,7 +843,7 @@ const processNameAndYobKyc = (entry: any, i: number): bigint => {
|
||||
return BigInt(0);
|
||||
}
|
||||
|
||||
const nameHash = processNameKyc(firstName, lastName, i);
|
||||
const nameHash = processNameKyc(firstName, lastName, i, reverse);
|
||||
const yearHash = processYearKyc(year, i);
|
||||
return generateSmallKey(poseidon2([yearHash, nameHash]));
|
||||
};
|
||||
|
||||
@@ -21,6 +21,7 @@ import {RegisterProofVerifierLib} from "./libraries/RegisterProofVerifierLib.sol
|
||||
import {DscProofVerifierLib} from "./libraries/DscProofVerifierLib.sol";
|
||||
import {RootCheckLib} from "./libraries/RootCheckLib.sol";
|
||||
import {OfacCheckLib} from "./libraries/OfacCheckLib.sol";
|
||||
import {console} from "hardhat/console.sol";
|
||||
|
||||
/**
|
||||
* @title IdentityVerificationHubImplV2
|
||||
|
||||
@@ -138,23 +138,12 @@ library CircuitConstantsV2 {
|
||||
passportNoSmtRootIndex: 99
|
||||
});
|
||||
} else if (attestationId == AttestationId.KYC) {
|
||||
// Selfrica circuit pubSignals layout (30 elements total):
|
||||
// [0-8] revealedData_packed (9 elements)
|
||||
// [9-12] forbidden_countries_list_packed (4 elements)
|
||||
// [13-15] nullifier + padding (3 elements)
|
||||
// [16] scope (public input)
|
||||
// [17] merkle_root (public input)
|
||||
// [18] ofac_name_dob_smt_root (public input)
|
||||
// [19] ofac_name_yob_smt_root (public input)
|
||||
// [20] user_identifier (public input)
|
||||
// [21-28] current_date (8 elements, public input)
|
||||
// [29] attestation_id (public input)
|
||||
return
|
||||
DiscloseIndices({
|
||||
revealedDataPackedIndex: 0,
|
||||
forbiddenCountriesListPackedIndex: 11,
|
||||
nullifierIndex: 15,
|
||||
attestationIdIndex: 29,
|
||||
forbiddenCountriesListPackedIndex: 10,
|
||||
nullifierIndex: 14,
|
||||
attestationIdIndex: 15,
|
||||
merkleRootIndex: 17,
|
||||
currentDateIndex: 21,
|
||||
namedobSmtRootIndex: 18,
|
||||
|
||||
@@ -64,6 +64,6 @@ interface IVcAndDiscloseSelfricaCircuitVerifier {
|
||||
uint256[2] calldata a,
|
||||
uint256[2][2] calldata b,
|
||||
uint256[2] calldata c,
|
||||
uint256[30] calldata pubSignals
|
||||
uint256[29] calldata pubSignals
|
||||
) external view returns (bool);
|
||||
}
|
||||
|
||||
@@ -125,14 +125,14 @@ library CircuitAttributeHandlerV2 {
|
||||
nationalityEnd: 2,
|
||||
dateOfBirthStart: 142,
|
||||
dateOfBirthEnd: 149,
|
||||
genderStart: 226,
|
||||
genderEnd: 232,
|
||||
genderStart: 194,
|
||||
genderEnd: 194,
|
||||
expiryDateStart: 70,
|
||||
expiryDateEnd: 77,
|
||||
olderThanStart: 334,
|
||||
olderThanEnd: 334,
|
||||
ofacStart: 332,
|
||||
ofacEnd: 333
|
||||
olderThanStart: 297,
|
||||
olderThanEnd: 297,
|
||||
ofacStart: 295,
|
||||
ofacEnd: 296
|
||||
});
|
||||
} else {
|
||||
revert("Invalid attestation ID");
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity 0.8.28;
|
||||
|
||||
import {console} from "hardhat/console.sol";
|
||||
|
||||
/**
|
||||
* @title Formatter Library
|
||||
* @notice A library providing utility functions to format names, dates, and encode data.
|
||||
|
||||
@@ -67,8 +67,8 @@ library ProofVerifierLib {
|
||||
revert InvalidVcAndDiscloseProof();
|
||||
}
|
||||
} else if (attestationId == AttestationId.KYC) {
|
||||
uint256[30] memory pubSignals;
|
||||
for (uint256 i = 0; i < 30; i++) {
|
||||
uint256[29] memory pubSignals;
|
||||
for (uint256 i = 0; i < 29; i++) {
|
||||
pubSignals[i] = vcAndDiscloseProof.pubSignals[i];
|
||||
}
|
||||
|
||||
|
||||
@@ -290,7 +290,7 @@ contract IdentityRegistryAadhaarImplV1 is IdentityRegistryAadhaarStorageV1, IIde
|
||||
}
|
||||
|
||||
// ====================================================
|
||||
// External Functions - Only Owner
|
||||
// External Functions - Role-Based Access Control
|
||||
// ====================================================
|
||||
|
||||
/// @notice Updates the hub address.
|
||||
|
||||
@@ -388,7 +388,7 @@ contract IdentityRegistryIdCardImplV1 is IdentityRegistryIdCardStorageV1, IIdent
|
||||
}
|
||||
|
||||
// ====================================================
|
||||
// External Functions - Only Owner
|
||||
// External Functions - Role-Based Access Control
|
||||
// ====================================================
|
||||
|
||||
/**
|
||||
|
||||
@@ -411,7 +411,7 @@ contract IdentityRegistryImplV1 is IdentityRegistryStorageV1, IIdentityRegistryV
|
||||
}
|
||||
|
||||
// ====================================================
|
||||
// External Functions - Only Owner
|
||||
// External Functions - Role-Based Access Control
|
||||
// ====================================================
|
||||
|
||||
/**
|
||||
|
||||
@@ -370,7 +370,7 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
}
|
||||
|
||||
// ====================================================
|
||||
// External Functions - Only Owner TODO: add only role(SECURITY_ROLE) or something
|
||||
// External Functions - Role-Based Access Control
|
||||
// ====================================================
|
||||
|
||||
/**
|
||||
@@ -378,7 +378,7 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
* @dev Callable only via a proxy and restricted to the contract owner.
|
||||
* @param newHubAddress The new address of the hub.
|
||||
*/
|
||||
function updateHub(address newHubAddress) external onlyProxy {
|
||||
function updateHub(address newHubAddress) external onlyProxy onlyRole(SECURITY_ROLE) {
|
||||
if (newHubAddress == address(0)) revert HUB_ADDRESS_ZERO();
|
||||
_hub = newHubAddress;
|
||||
emit HubUpdated(newHubAddress);
|
||||
@@ -389,7 +389,7 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
* @dev Callable only via a proxy and restricted to the contract owner.
|
||||
* @param newPCR0ManagerAddress The new address of the PCR0Manager.
|
||||
*/
|
||||
function updatePCR0Manager(address newPCR0ManagerAddress) external virtual onlyProxy {
|
||||
function updatePCR0Manager(address newPCR0ManagerAddress) external virtual onlyProxy onlyRole(SECURITY_ROLE) {
|
||||
_PCR0Manager = newPCR0ManagerAddress;
|
||||
emit PCR0ManagerUpdated(newPCR0ManagerAddress);
|
||||
}
|
||||
@@ -399,7 +399,7 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
* @dev Callable only via a proxy and restricted to the contract owner.
|
||||
* @param nameAndDobOfacRoot The new name and date of birth OFAC root value.
|
||||
*/
|
||||
function updateNameAndDobOfacRoot(uint256 nameAndDobOfacRoot) external virtual onlyProxy {
|
||||
function updateNameAndDobOfacRoot(uint256 nameAndDobOfacRoot) external virtual onlyProxy onlyRole(OPERATIONS_ROLE) {
|
||||
_nameAndDobOfacRoot = nameAndDobOfacRoot;
|
||||
emit NameAndDobOfacRootUpdated(nameAndDobOfacRoot);
|
||||
}
|
||||
@@ -409,7 +409,7 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
* @dev Callable only via a proxy and restricted to the contract owner.
|
||||
* @param nameAndYobOfacRoot The new name and year of birth OFAC root value.
|
||||
*/
|
||||
function updateNameAndYobOfacRoot(uint256 nameAndYobOfacRoot) external virtual onlyProxy {
|
||||
function updateNameAndYobOfacRoot(uint256 nameAndYobOfacRoot) external virtual onlyProxy onlyRole(OPERATIONS_ROLE) {
|
||||
_nameAndYobOfacRoot = nameAndYobOfacRoot;
|
||||
emit NameAndYobOfacRootUpdated(nameAndYobOfacRoot);
|
||||
}
|
||||
@@ -419,7 +419,7 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
* @dev Callable only via a proxy and restricted to the contract owner.
|
||||
* @param gcpRootCAPubkeyHash The new GCP root CA pubkey hash value.
|
||||
*/
|
||||
function updateGCPRootCAPubkeyHash(uint256 gcpRootCAPubkeyHash) external virtual onlyProxy {
|
||||
function updateGCPRootCAPubkeyHash(uint256 gcpRootCAPubkeyHash) external virtual onlyProxy onlyRole(SECURITY_ROLE) {
|
||||
_gcpRootCAPubkeyHash = gcpRootCAPubkeyHash;
|
||||
emit GCPRootCAPubkeyHashUpdated(gcpRootCAPubkeyHash);
|
||||
}
|
||||
@@ -427,7 +427,7 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
/// @notice Updates the GCP JWT verifier contract address.
|
||||
/// @dev Callable only by the contract owner.
|
||||
/// @param verifier The new GCP JWT verifier address.
|
||||
function updateGCPJWTVerifier(address verifier) external onlyProxy {
|
||||
function updateGCPJWTVerifier(address verifier) external onlyProxy onlyRole(SECURITY_ROLE) {
|
||||
_gcpJwtVerifier = verifier;
|
||||
emit GCPJWTVerifierUpdated(verifier);
|
||||
}
|
||||
@@ -435,7 +435,7 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
/// @notice Updates the TEE address.
|
||||
/// @dev Callable only by the contract owner.
|
||||
/// @param teeAddress The new TEE address.
|
||||
function updateTEE(address teeAddress) external onlyProxy {
|
||||
function updateTEE(address teeAddress) external onlyProxy onlyRole(SECURITY_ROLE) {
|
||||
_tee = teeAddress;
|
||||
emit TEEUpdated(teeAddress);
|
||||
}
|
||||
@@ -491,7 +491,7 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
/// @dev Callable only by the owner for testing or administration.
|
||||
/// @param nullifier The nullifier associated with the identity commitment.
|
||||
/// @param commitment The identity commitment to add.
|
||||
function devAddIdentityCommitment(uint256 nullifier, uint256 commitment) external onlyProxy {
|
||||
function devAddIdentityCommitment(uint256 nullifier, uint256 commitment) external onlyProxy onlyRole(SECURITY_ROLE) {
|
||||
_nullifiers[nullifier] = true;
|
||||
uint256 imt_root = _identityCommitmentIMT._insert(commitment);
|
||||
_rootTimestamps[imt_root] = block.timestamp;
|
||||
@@ -504,7 +504,7 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
/// @param oldLeaf The current identity commitment to update.
|
||||
/// @param newLeaf The new identity commitment.
|
||||
/// @param siblingNodes An array of sibling nodes for Merkle proof generation.
|
||||
function devUpdateCommitment(uint256 oldLeaf, uint256 newLeaf, uint256[] calldata siblingNodes) external onlyProxy {
|
||||
function devUpdateCommitment(uint256 oldLeaf, uint256 newLeaf, uint256[] calldata siblingNodes) external onlyProxy onlyRole(SECURITY_ROLE) {
|
||||
uint256 imt_root = _identityCommitmentIMT._update(oldLeaf, newLeaf, siblingNodes);
|
||||
_rootTimestamps[imt_root] = block.timestamp;
|
||||
emit DevCommitmentUpdated(oldLeaf, newLeaf, imt_root, block.timestamp);
|
||||
@@ -514,7 +514,7 @@ contract IdentityRegistrySelfricaImplV1 is IdentityRegistrySelfricaStorageV1, II
|
||||
/// @dev Caller must be the owner. Provides sibling nodes for proof of position.
|
||||
/// @param oldLeaf The identity commitment to remove.
|
||||
/// @param siblingNodes An array of sibling nodes for Merkle proof generation.
|
||||
function devRemoveCommitment(uint256 oldLeaf, uint256[] calldata siblingNodes) external onlyProxy {
|
||||
function devRemoveCommitment(uint256 oldLeaf, uint256[] calldata siblingNodes) external onlyProxy onlyRole(SECURITY_ROLE) {
|
||||
uint256 imt_root = _identityCommitmentIMT._remove(oldLeaf, siblingNodes);
|
||||
_rootTimestamps[imt_root] = block.timestamp;
|
||||
emit DevCommitmentRemoved(oldLeaf, imt_root, block.timestamp);
|
||||
|
||||
@@ -16,9 +16,9 @@ import RegisterVerifierArtifactLocal from "../../artifacts/contracts/verifiers/l
|
||||
import RegisterIdVerifierArtifactLocal from "../../artifacts/contracts/verifiers/local/staging/register_id/Verifier_register_id_sha256_sha256_sha256_rsa_65537_4096_staging.sol/Verifier_register_id_sha256_sha256_sha256_rsa_65537_4096_staging.json";
|
||||
import RegisterAadhaarVerifierArtifactLocal from "../../artifacts/contracts/verifiers/local/staging/register/Verifier_register_aadhaar_staging.sol/Verifier_register_aadhaar_staging.json";
|
||||
import DscVerifierArtifactLocal from "../../artifacts/contracts/verifiers/local/staging/dsc/Verifier_dsc_sha256_rsa_65537_4096_staging.sol/Verifier_dsc_sha256_rsa_65537_4096_staging.json";
|
||||
import RegisterSelfricaVerifierArtifactLocal from "../../artifacts/contracts/verifiers/local/staging/register/Verifier_register_selfrica_staging.sol/Verifier_register_selfrica_staging.json";
|
||||
import RegisterSelfricaVerifierArtifactLocal from "../../artifacts/contracts/verifiers/local/staging/register/Verifier_register_kyc_staging.sol/Verifier_register_kyc_staging.json";
|
||||
// import GCPJWTVerifierArtifactLocal from "../../artifacts/contracts/verifiers/local/staging/gcp_jwt_verifier/Verifier_gcp_jwt_verifier_staging.sol/Verifier_gcp_jwt_verifier_staging.json";
|
||||
import VcAndDiscloseSelfricaVerifierArtifactLocal from "../../artifacts/contracts/verifiers/local/staging/disclose/Verifier_vc_and_disclose_selfrica_staging.sol/Verifier_vc_and_disclose_selfrica_staging.json";
|
||||
import VcAndDiscloseSelfricaVerifierArtifactLocal from "../../artifacts/contracts/verifiers/local/staging/disclose/Verifier_vc_and_disclose_kyc_staging.sol/Verifier_vc_and_disclose_kyc_staging.json";
|
||||
|
||||
export async function deploySystemFixturesV2(): Promise<DeployedActorsV2> {
|
||||
let identityVerificationHubV2: any;
|
||||
|
||||
@@ -48,11 +48,11 @@ const registerCircuitsAadhaar: CircuitArtifacts = {
|
||||
},
|
||||
};
|
||||
|
||||
const registerCircuitsSelfrica: CircuitArtifacts = {
|
||||
register_selfrica: {
|
||||
wasm: "../circuits/build/register/register_selfrica/register_selfrica_js/register_selfrica.wasm",
|
||||
zkey: "../circuits/build/register/register_selfrica/register_selfrica_final.zkey",
|
||||
vkey: "../circuits/build/register/register_selfrica/register_selfrica_vkey.json",
|
||||
const registerCircuitsKyc: CircuitArtifacts = {
|
||||
register_kyc: {
|
||||
wasm: "../circuits/build/register/register_kyc/register_kyc_js/register_kyc.wasm",
|
||||
zkey: "../circuits/build/register/register_kyc/register_kyc_final.zkey",
|
||||
vkey: "../circuits/build/register/register_kyc/register_kyc_vkey.json",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -88,9 +88,9 @@ const vcAndDiscloseCircuitsAadhaar: CircuitArtifacts = {
|
||||
|
||||
const vcAndDiscloseCircuitsSelfrica: CircuitArtifacts = {
|
||||
vc_and_disclose_selfrica: {
|
||||
wasm: "../circuits/build/disclose/vc_and_disclose_selfrica/vc_and_disclose_selfrica_js/vc_and_disclose_selfrica.wasm",
|
||||
zkey: "../circuits/build/disclose/vc_and_disclose_selfrica/vc_and_disclose_selfrica_final.zkey",
|
||||
vkey: "../circuits/build/disclose/vc_and_disclose_selfrica/vc_and_disclose_selfrica_vkey.json",
|
||||
wasm: "../circuits/build/disclose/vc_and_disclose_kyc/vc_and_disclose_kyc_js/vc_and_disclose_kyc.wasm",
|
||||
zkey: "../circuits/build/disclose/vc_and_disclose_kyc/vc_and_disclose_kyc_final.zkey",
|
||||
vkey: "../circuits/build/disclose/vc_and_disclose_kyc/vc_and_disclose_kyc_vkey.json",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -211,9 +211,9 @@ export async function generateRegisterSelfricaProof(
|
||||
//return type of prepareAadhaarTestData
|
||||
inputs: Awaited<ReturnType<typeof generateMockKycRegisterInput>>,
|
||||
): Promise<GenericProofStructStruct> {
|
||||
const circuitName = "register_selfrica";
|
||||
const circuitName = "register_kyc";
|
||||
|
||||
const circuitArtifacts = registerCircuitsSelfrica;
|
||||
const circuitArtifacts = registerCircuitsKyc;
|
||||
const artifactKey = circuitName;
|
||||
|
||||
const registerProof = await groth16.fullProve(
|
||||
@@ -225,7 +225,7 @@ export async function generateRegisterSelfricaProof(
|
||||
const vKey = JSON.parse(fs.readFileSync(circuitArtifacts[artifactKey].vkey, "utf8"));
|
||||
const isValid = await groth16.verify(vKey, registerProof.publicSignals, registerProof.proof);
|
||||
if (!isValid) {
|
||||
throw new Error("Generated register-selfrica proof verification failed");
|
||||
throw new Error("Generated register-kyc proof verification failed");
|
||||
}
|
||||
|
||||
const rawCallData = await groth16.exportSolidityCallData(registerProof.proof, registerProof.publicSignals);
|
||||
|
||||
@@ -106,12 +106,12 @@ describe("Self Verification Flow V2 - Selfrica", () => {
|
||||
snapshotId = await ethers.provider.send("evm_snapshot", []);
|
||||
});
|
||||
|
||||
describe("Complete V2 Verification Flow - Selfrica", () => {
|
||||
// TODO: Fix test setup - the proof's merkle root needs to be registered in the registry
|
||||
describe("Complete V2 Verification Flow - KYC", () => {
|
||||
// The issue is that generateKycDiscloseInput creates a commitment in the local tree,
|
||||
// TODO: Fix test setup - the proof's merkle root needs to be registered in the registry
|
||||
// but the registry has its own separate tree. The proof uses the local tree's root,
|
||||
// which is not registered in the registry.
|
||||
it("should complete full Selfrica verification flow with proper proof encoding", async () => {
|
||||
it("should complete full KYC verification flow with proper proof encoding", async () => {
|
||||
const destChainId = ethers.zeroPadValue(ethers.toBeHex(31337), 32);
|
||||
const user1Address = await deployedActors.user1.getAddress();
|
||||
const userData = ethers.toUtf8Bytes("test-user-data-for-verification");
|
||||
@@ -6,8 +6,11 @@ import { generateMockKycRegisterInput } from "@selfxyz/common/utils/kyc/generate
|
||||
import { generateRegisterSelfricaProof } from "../utils/generateProof";
|
||||
import { expect } from "chai";
|
||||
|
||||
function getCurrentDateDigitsYYMMDDHHMMSS(): bigint[] {
|
||||
function getCurrentDateDigitsYYMMDDHHMMSS(hoursOffset: number = 0): bigint[] {
|
||||
const now = new Date();
|
||||
if (hoursOffset !== 0) {
|
||||
now.setUTCHours(now.getUTCHours() + hoursOffset);
|
||||
}
|
||||
const pad2 = (n: number) => n.toString().padStart(2, "0");
|
||||
const yy = pad2(now.getUTCFullYear() % 100);
|
||||
const mm = pad2(now.getUTCMonth() + 1);
|
||||
@@ -106,7 +109,7 @@ describe("Selfrica Registration test", function () {
|
||||
|
||||
// Add the corresponding PCR0 (16 zero bytes + 32 hash bytes)
|
||||
const pcr0Bytes = ethers.getBytes(
|
||||
"0x" + "00".repeat(16) + "d2221a0ee83901980c607ceff2edbedf3f6ce5f437eafa5d89be39e9e7487c04",
|
||||
"0x" + "d2221a0ee83901980c607ceff2edbedf3f6ce5f437eafa5d89be39e9e7487c04".padStart(32, "0"),
|
||||
);
|
||||
await deployedActors.pcr0Manager.addPCR0(pcr0Bytes);
|
||||
|
||||
@@ -240,7 +243,7 @@ describe("Selfrica Registration test", function () {
|
||||
it("should not allow non-owner to update GCP root CA pubkey hash", async () => {
|
||||
await expect(
|
||||
deployedActors.registrySelfrica.connect(deployedActors.user1).updateGCPRootCAPubkeyHash(12345n),
|
||||
).to.be.revertedWithCustomError(deployedActors.registrySelfrica, "OwnableUnauthorizedAccount");
|
||||
).to.be.revertedWithCustomError(deployedActors.registrySelfrica, "AccessControlUnauthorizedAccount");
|
||||
});
|
||||
|
||||
it("should fail with INVALID_IMAGE when image hash not in PCR0Manager", async () => {
|
||||
@@ -265,7 +268,7 @@ describe("Selfrica Registration test", function () {
|
||||
deployedActors.registrySelfrica
|
||||
.connect(deployedActors.user1)
|
||||
.updateGCPJWTVerifier(ethers.Wallet.createRandom().address),
|
||||
).to.be.revertedWithCustomError(deployedActors.registrySelfrica, "OwnableUnauthorizedAccount");
|
||||
).to.be.revertedWithCustomError(deployedActors.registrySelfrica, "AccessControlUnauthorizedAccount");
|
||||
});
|
||||
|
||||
it("should allow owner to update GCP JWT verifier", async () => {
|
||||
@@ -296,7 +299,7 @@ describe("Selfrica Registration test", function () {
|
||||
it("should not allow non-owner to update TEE", async () => {
|
||||
await expect(
|
||||
deployedActors.registrySelfrica.connect(deployedActors.user1).updateTEE(ethers.Wallet.createRandom().address),
|
||||
).to.be.revertedWithCustomError(deployedActors.registrySelfrica, "OwnableUnauthorizedAccount");
|
||||
).to.be.revertedWithCustomError(deployedActors.registrySelfrica, "AccessControlUnauthorizedAccount");
|
||||
});
|
||||
|
||||
it("should allow owner to update TEE", async () => {
|
||||
@@ -341,13 +344,24 @@ describe("Selfrica Registration test", function () {
|
||||
});
|
||||
|
||||
it("should fail with INVALID_TIMESTAMP when timestamp is in the past or future", async () => {
|
||||
// Add the PCR0 image hash so the image validation passes and we can test timestamp validation
|
||||
// addPCR0 takes 32 bytes and pads to 48 bytes internally, isPCR0Set requires 48 bytes
|
||||
const pcr0Hash = "d2221a0ee83901980c607ceff2edbedf3f6ce5f437eafa5d89be39e9e7487c04";
|
||||
const pcr0Bytes32 = ethers.getBytes("0x" + pcr0Hash);
|
||||
const pcr0Bytes48 = ethers.getBytes("0x" + "00".repeat(16) + pcr0Hash);
|
||||
// Only add PCR0 if not already set (may have been added by earlier test)
|
||||
const isAlreadySet = await deployedActors.pcr0Manager.isPCR0Set(pcr0Bytes48);
|
||||
if (!isAlreadySet) {
|
||||
await deployedActors.pcr0Manager.addPCR0(pcr0Bytes32);
|
||||
}
|
||||
|
||||
let mockPubkeyCommitment = 12345678901234567890123456789012n;
|
||||
const [p0, p1, p2] = packUint256ToHexFields(BigInt(mockPubkeyCommitment));
|
||||
|
||||
let previousHourDate = getCurrentDateDigitsYYMMDDHHMMSS();
|
||||
previousHourDate[3 * 2] = previousHourDate[3 * 2] - 1n;
|
||||
// Create a timestamp 2 hours in the past (more than 1 hour threshold)
|
||||
const previousHourDate = getCurrentDateDigitsYYMMDDHHMMSS(-2);
|
||||
|
||||
const mockPubSignals = [
|
||||
const mockPubSignalsPast = [
|
||||
GCP_ROOT_CA_PUBKEY_HASH,
|
||||
p0,
|
||||
p1,
|
||||
@@ -363,19 +377,30 @@ describe("Selfrica Registration test", function () {
|
||||
mockProof.a,
|
||||
mockProof.b,
|
||||
mockProof.c,
|
||||
mockPubSignals,
|
||||
mockPubSignalsPast,
|
||||
),
|
||||
).to.be.revertedWithCustomError(deployedActors.registrySelfrica, "INVALID_TIMESTAMP");
|
||||
|
||||
let nextHourDate = getCurrentDateDigitsYYMMDDHHMMSS();
|
||||
nextHourDate[3 * 2] = nextHourDate[3 * 2] + 1n;
|
||||
// Create a timestamp 2 hours in the future (more than 1 hour threshold)
|
||||
const nextHourDate = getCurrentDateDigitsYYMMDDHHMMSS(2);
|
||||
|
||||
const mockPubSignalsFuture = [
|
||||
GCP_ROOT_CA_PUBKEY_HASH,
|
||||
p0,
|
||||
p1,
|
||||
p2,
|
||||
177384435506496807268973340845468654286294928521500580044819492874465981028n,
|
||||
175298970718174405520284770870231222447414486446296682893283627688949855078n,
|
||||
13360n,
|
||||
...nextHourDate,
|
||||
];
|
||||
|
||||
await expect(
|
||||
deployedActors.registrySelfrica.registerPubkeyCommitment(
|
||||
mockProof.a,
|
||||
mockProof.b,
|
||||
mockProof.c,
|
||||
mockPubSignals,
|
||||
mockPubSignalsFuture,
|
||||
),
|
||||
).to.be.revertedWithCustomError(deployedActors.registrySelfrica, "INVALID_TIMESTAMP");
|
||||
});
|
||||
Reference in New Issue
Block a user