-
Notifications
You must be signed in to change notification settings - Fork 220
feat: selfrica circuit and tests #1171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b7c5bc7
ed841d7
a91a955
968a74c
e23322f
838bc49
d51ce02
de37ae4
e5c445e
fd197fe
3e44266
c290010
c39dc21
6e5a4c7
cd761a9
48ff517
750f592
08fdae0
c21543b
7910b87
2804959
a13468a
bced45e
ae6fb3a
fa03e1e
ceff539
c47769f
ec0e41d
8397155
a2bc9c9
dbcbd76
be2f557
770bebe
ae10ae4
043dd4e
74fb2f6
dcea2b8
036646a
2708539
db1435b
ed91479
9f767b0
7ff7d36
9c27329
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| pragma circom 2.1.9; | ||
|
|
||
| include "circomlib/circuits/bitify.circom"; | ||
| include "circomlib/circuits/poseidon.circom"; | ||
| include "@zk-kit/binary-merkle-root.circom/src/binary-merkle-root.circom"; | ||
| include "@openpassport/zk-email-circuits/utils/bytes.circom"; | ||
| include "../utils/passport/customHashers.circom"; | ||
| include "../utils/kyc/disclose/disclose.circom"; | ||
|
|
||
| template VC_AND_DISCLOSE_KYC( | ||
| MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH, | ||
| namedobTreeLevels, | ||
| nameyobTreeLevels, | ||
| n, | ||
| k, | ||
| nLevels | ||
| ) { | ||
| var max_length = KYC_MAX_LENGTH(); | ||
| var country_length = COUNTRY_LENGTH(); | ||
| var id_number_length = ID_NUMBER_LENGTH(); | ||
| var idNumberIdx = ID_NUMBER_INDEX(); | ||
| var compressed_bit_len = max_length/2; | ||
|
|
||
| signal input data_padded[max_length]; | ||
| signal input compressed_disclose_sel[2]; | ||
|
|
||
| signal input scope; | ||
|
|
||
| signal input forbidden_countries_list[MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH * country_length]; | ||
|
|
||
| signal input merkle_root; | ||
| signal input leaf_depth; | ||
| signal input path[nLevels]; | ||
| signal input siblings[nLevels]; | ||
|
|
||
| signal input ofac_name_dob_smt_leaf_key; | ||
| signal input ofac_name_dob_smt_root; | ||
| signal input ofac_name_dob_smt_siblings[namedobTreeLevels]; | ||
|
|
||
| signal input ofac_name_yob_smt_leaf_key; | ||
| signal input ofac_name_yob_smt_root; | ||
| signal input ofac_name_yob_smt_siblings[nameyobTreeLevels]; | ||
|
|
||
| signal input selector_ofac; | ||
| signal input user_identifier; | ||
| signal input current_date[8]; | ||
|
Comment on lines
+45
to
+46
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bind the public
Add an equality constraint before exposing the commitment so the public value matches the hashed signature: signal output identity_commitment <== idCommCal.out;
+ user_identifier === idCommCal.out;Also applies to: 156-157 🤖 Prompt for AI Agents |
||
| signal input majority_age_ASCII[3]; | ||
| signal input secret; | ||
|
|
||
| signal input attestation_id; | ||
|
|
||
| // Convert the two decimal inputs back to bit array | ||
| signal disclose_sel[max_length]; | ||
|
|
||
| // Convert disclose_sel_low (first 133 bits) to bit array | ||
| component low_bits = Num2Bits(compressed_bit_len); | ||
| low_bits.in <== compressed_disclose_sel[0]; | ||
|
|
||
| // Convert disclose_sel_high (next 133 bits) to bit array | ||
| component high_bits = Num2Bits(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++){ | ||
| disclose_sel[compressed_bit_len + i] <== high_bits.out[i]; | ||
| } | ||
|
|
||
| component msg_hasher = PackBytesAndPoseidon(max_length); | ||
| for (var i = 0; i < max_length; i++) { | ||
| msg_hasher.in[i] <== data_padded[i]; | ||
| } | ||
|
|
||
| signal leaf <== Poseidon(2)([secret, msg_hasher.out]); | ||
|
|
||
| signal computedRoot <== BinaryMerkleRoot(nLevels)(leaf, leaf_depth, path, siblings); | ||
| merkle_root === computedRoot; | ||
|
|
||
| signal id_num[id_number_length]; | ||
| for (var i = 0; i < id_number_length; i++) { | ||
| id_num[i] <== data_padded[idNumberIdx + i]; | ||
| } | ||
|
|
||
| component disclose_circuit = DISCLOSE_KYC(MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH, namedobTreeLevels, nameyobTreeLevels); | ||
|
|
||
| for (var i = 0; i < max_length; i++) { | ||
| disclose_circuit.data_padded[i] <== data_padded[i]; | ||
| } | ||
| disclose_circuit.selector_data_padded <== disclose_sel; | ||
| disclose_circuit.forbidden_countries_list <== forbidden_countries_list; | ||
|
|
||
| disclose_circuit.ofac_name_dob_smt_leaf_key <== ofac_name_dob_smt_leaf_key; | ||
| disclose_circuit.ofac_name_dob_smt_root <== ofac_name_dob_smt_root; | ||
| disclose_circuit.ofac_name_dob_smt_siblings <== ofac_name_dob_smt_siblings; | ||
|
|
||
| disclose_circuit.ofac_name_yob_smt_leaf_key <== ofac_name_yob_smt_leaf_key; | ||
| disclose_circuit.ofac_name_yob_smt_root <== ofac_name_yob_smt_root; | ||
| disclose_circuit.ofac_name_yob_smt_siblings <== ofac_name_yob_smt_siblings; | ||
|
|
||
| disclose_circuit.selector_ofac <== selector_ofac; | ||
| disclose_circuit.current_date <== current_date; | ||
| disclose_circuit.majority_age_ASCII <== majority_age_ASCII; | ||
|
|
||
| var revealed_data_packed_chunk_length = computeIntChunkLength(max_length + 2 + 1); | ||
| signal output revealedData_packed[revealed_data_packed_chunk_length] <== disclose_circuit.revealedData_packed; | ||
|
|
||
| var forbidden_countries_list_packed_chunk_length = computeIntChunkLength(MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH * country_length); | ||
| signal output forbidden_countries_list_packed[forbidden_countries_list_packed_chunk_length] <== disclose_circuit.forbidden_countries_list_packed; | ||
| signal output nullifier <== Poseidon(2)([secret, scope]); | ||
| } | ||
|
|
||
| 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, 121, 17, 33); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| pragma circom 2.1.9; | ||
|
|
||
| include "circomlib/circuits/bitify.circom"; | ||
| include "../utils/kyc/constants.circom"; | ||
| include "../utils/passport/customHashers.circom"; | ||
| include "../utils/kyc/verifySignature.circom"; | ||
|
|
||
|
|
||
| template REGISTER_KYC() { | ||
|
|
||
| var max_length = KYC_MAX_LENGTH(); | ||
| var country_length = COUNTRY_LENGTH(); | ||
| var id_number_length = ID_NUMBER_LENGTH(); | ||
| var idNumberIdx = ID_NUMBER_INDEX(); | ||
|
|
||
| var compressed_bit_len = max_length/2; | ||
|
|
||
| signal input data_padded[max_length]; | ||
|
|
||
| signal input s; | ||
| signal input Tx; | ||
| signal input Ty; | ||
| signal input pubKeyX; | ||
| signal input pubKeyY; | ||
| signal input r_inv[4]; | ||
| signal input secret; | ||
| signal input attestation_id; | ||
|
|
||
| //Calculate msg_hash | ||
| component msg_hasher = PackBytesAndPoseidon(max_length); | ||
| for (var i = 0; i < max_length; i++) { | ||
| msg_hasher.in[i] <== data_padded[i]; | ||
| } | ||
|
|
||
| //msg_hash bit decomposition | ||
| //TODO: should we add msg_hash_bits [254] & [255] == 0? | ||
| component bit_decompose = Num2Bits(256); | ||
| bit_decompose.in <== msg_hasher.out; | ||
| signal msg_hash_bits[256] <== bit_decompose.out; | ||
|
|
||
|
|
||
| signal msg_hash_limbs[4]; | ||
| component bits2Num[4]; | ||
|
|
||
| // Convert msg_hash_bits (little-endian) to 4 LE limbs | ||
| for (var i = 0; i < 4; i++) { | ||
| bits2Num[i] = Bits2Num(64); | ||
| for (var j = 0; j < 64; j++) { | ||
| bits2Num[i].in[j] <== msg_hash_bits[i * 64 + j]; | ||
| } | ||
| msg_hash_limbs[i] <== bits2Num[i].out; | ||
| } | ||
|
|
||
|
|
||
| component verifyIdCommSig = VERIFY_KYC_SIGNATURE(); | ||
| verifyIdCommSig.s <== s; | ||
| verifyIdCommSig.r_inv <== r_inv; | ||
| verifyIdCommSig.msg_hash_limbs <== msg_hash_limbs; | ||
| verifyIdCommSig.Tx <== Tx; | ||
| verifyIdCommSig.Ty <== Ty; | ||
| verifyIdCommSig.pubKeyX <== pubKeyX; | ||
| verifyIdCommSig.pubKeyY <== pubKeyY; | ||
|
|
||
| signal id_num[id_number_length]; | ||
| 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 output commitment <== Poseidon(2)([secret, msg_hasher.out]); | ||
|
|
||
| signal output pubkey_hash <== Poseidon(2)([pubKeyX, pubKeyY]); | ||
|
|
||
| } | ||
|
|
||
| component main {public [attestation_id]} = REGISTER_KYC(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| // -------------------------------------------------- | ||
| // Source: https://github.com/cursive-team/babyjubjub-ecdsa | ||
| // File: packages/circuits/baby-jubjub-ecdsa/baby_jubjub_ecdsa.circom | ||
| // License: MIT | ||
| // Author(s): cursive-team | ||
| // Changes: no changes | ||
| // -------------------------------------------------- | ||
| pragma circom 2.1.9; | ||
|
|
||
| include "circomlib/circuits/babyjub.circom"; | ||
| include "circomlib/circuits/bitify.circom"; | ||
| include "circomlib/circuits/escalarmulany.circom"; | ||
| /** | ||
| * BabyJubJubECDSA | ||
| * ==================== | ||
| * | ||
| * Converts inputted efficient ECDSA signature to an public key. There is no | ||
| * public key validation included. Takes in points in Twisted Edwards form | ||
| * and uses Edwards addition and scalar multiplication. Returns computed | ||
| * public key in Edwards form. | ||
| */ | ||
| template BabyJubJubECDSA() { | ||
| var bits = 256; | ||
| signal input s; | ||
| signal input Tx; // T = r^-1 * R | ||
| signal input Ty; // T is represented in Twisted Edwards form | ||
| signal input Ux; // U = -(m * r^-1 * G) | ||
| signal input Uy; // U is represented in Twisted Edwards form | ||
|
|
||
| signal output pubKeyX; // Represented in Twisted Edwards form | ||
| signal output pubKeyY; | ||
|
|
||
| // bitify s | ||
| component sBits = Num2Bits(bits); | ||
| sBits.in <== s; | ||
|
|
||
| // check T, U are on curve | ||
| component checkT = BabyCheck(); | ||
| checkT.x <== Tx; | ||
| checkT.y <== Ty; | ||
| component checkU = BabyCheck(); | ||
| checkU.x <== Ux; | ||
| checkU.y <== Uy; | ||
|
|
||
| // sMultT = s * T | ||
| component sMultT = EscalarMulAny(bits); | ||
| var i; | ||
| for (i=0; i<bits; i++) { | ||
| sMultT.e[i] <== sBits.out[i]; | ||
| } | ||
| sMultT.p[0] <== Tx; | ||
| sMultT.p[1] <== Ty; | ||
|
|
||
| // pubKey = sMultT + U | ||
| component pubKey = BabyAdd(); | ||
| pubKey.x1 <== sMultT.out[0]; | ||
| pubKey.y1 <== sMultT.out[1]; | ||
| pubKey.x2 <== Ux; | ||
| pubKey.y2 <== Uy; | ||
|
|
||
| pubKeyX <== pubKey.xout; | ||
| pubKeyY <== pubKey.yout; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,101 @@ | ||
| pragma circom 2.1.9; | ||
|
|
||
| function COUNTRY_INDEX() { | ||
| return 0; | ||
| } | ||
|
|
||
| function COUNTRY_LENGTH() { | ||
| return 3; | ||
| } | ||
|
|
||
| function ID_TYPE_INDEX() { | ||
| return COUNTRY_INDEX() + COUNTRY_LENGTH(); | ||
| } | ||
|
|
||
| function ID_TYPE_LENGTH() { | ||
| return 27; | ||
| } | ||
|
|
||
| function ID_NUMBER_INDEX() { | ||
| return ID_TYPE_INDEX() + ID_TYPE_LENGTH(); | ||
| } | ||
|
|
||
| function ID_NUMBER_LENGTH() { | ||
| return 32; | ||
| } | ||
|
|
||
| function ISSUANCE_DATE_INDEX() { | ||
| return ID_NUMBER_INDEX() + ID_NUMBER_LENGTH(); | ||
| } | ||
|
|
||
| function ISSUANCE_DATE_LENGTH() { | ||
| return 8; | ||
| } | ||
|
|
||
| function EXPIRATION_DATE_INDEX() { | ||
| return ISSUANCE_DATE_INDEX() + ISSUANCE_DATE_LENGTH(); | ||
| } | ||
|
|
||
| function EXPIRATION_DATE_LENGTH() { | ||
| return 8; | ||
| } | ||
|
|
||
| function FULL_NAME_INDEX() { | ||
| return EXPIRATION_DATE_INDEX() + EXPIRATION_DATE_LENGTH(); | ||
| } | ||
|
|
||
| function FULL_NAME_LENGTH() { | ||
| return 64; | ||
| } | ||
|
|
||
| function DOB_INDEX() { | ||
| return FULL_NAME_INDEX() + FULL_NAME_LENGTH(); | ||
| } | ||
|
|
||
| function DOB_LENGTH() { | ||
| return 8; | ||
| } | ||
|
|
||
| function PHOTO_HASH_INDEX() { | ||
| return DOB_INDEX() + DOB_LENGTH(); | ||
| } | ||
|
|
||
| function PHOTO_HASH_LENGTH() { | ||
| return 32; | ||
| } | ||
|
|
||
| function PHONE_NUMBER_INDEX() { | ||
| return PHOTO_HASH_INDEX() + PHOTO_HASH_LENGTH(); | ||
| } | ||
|
|
||
| function PHONE_NUMBER_LENGTH() { | ||
| return 12; | ||
| } | ||
|
|
||
| function DOCUMENT_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; | ||
| } | ||
|
|
||
| function ADDRESS_INDEX() { | ||
| return GENDER_INDEX() + GENDER_LENGTH(); | ||
| } | ||
|
|
||
| function ADDRESS_LENGTH() { | ||
| return 100; | ||
| } | ||
|
|
||
| function KYC_MAX_LENGTH() { | ||
| return ADDRESS_INDEX() + ADDRESS_LENGTH(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Remove unused RSA parameters
nandk.The template parameters
nandkare declared but never used anywhere in the circuit. There's no RSA signature verification or any component that consumes these parameters.template VC_AND_DISCLOSE( MAX_FORBIDDEN_COUNTRIES_LIST_LENGTH, namedobTreeLevels, nameyobTreeLevels, - n, - k, nLevels ) {And update the main component instantiation:
📝 Committable suggestion
🤖 Prompt for AI Agents