diff --git a/.github/workflows/action.yml b/.github/workflows/action.yml index 36deda31..293ccb93 100644 --- a/.github/workflows/action.yml +++ b/.github/workflows/action.yml @@ -40,9 +40,9 @@ jobs: - name: Setup Rust uses: dtolnay/rust-toolchain@stable - - name: Download Circom Binary v2.1.8 + - name: Download Circom Binary v2.1.9 run: | - wget -qO /home/runner/work/circom https://github.com/iden3/circom/releases/download/v2.1.8/circom-linux-amd64 + wget -qO /home/runner/work/circom https://github.com/iden3/circom/releases/download/v2.1.9/circom-linux-amd64 chmod +x /home/runner/work/circom sudo mv /home/runner/work/circom /bin/circom diff --git a/circuits/README.md b/circuits/README.md index 9a917ced..17fc242a 100644 --- a/circuits/README.md +++ b/circuits/README.md @@ -31,7 +31,7 @@ The `disclose` circuit is used for the following: 1. Verify that a user knows a secret e.g., he is able to reconstruct one leaf of the merkle tree (a check of the merkle roots will be performed on-chain) 2. Passport expiry is verified 3. A range check is performed over the age of the user -4. The output is multiplied by an input bitmap to allow the user to disclose only what they want to disclose. +4. The output is multiplied by an input selector_dg1 to allow the user to disclose only what they want to disclose. 5. Final output is packed. Any application that wants to use OpenPassport can actually build its own `disclose` circuit. diff --git a/circuits/circuits/disclose/disclose.circom b/circuits/circuits/disclose/disclose.circom index bd755356..62aa77fb 100644 --- a/circuits/circuits/disclose/disclose.circom +++ b/circuits/circuits/disclose/disclose.circom @@ -1,25 +1,26 @@ -pragma circom 2.1.5; +pragma circom 2.1.9; include "circomlib/circuits/poseidon.circom"; -include "@zk-email/circuits/utils/bytes.circom"; -include "../utils/isOlderThan.circom"; -include "../utils/isValid.circom"; +include "../utils/other/bytes.circom"; +include "../utils/passport/date/isOlderThan.circom"; +include "../utils/passport/date/isValid.circom"; include "binary-merkle-root.circom"; -include "../utils/isValid.circom"; template DISCLOSE() { - signal input mrz[93]; - signal input bitmap[90]; // 88 for MRZ + 2 for majority + signal input dg1[93]; + signal input selector_dg1[88]; // 88 for MRZ + signal input selector_older_than; signal input current_date[6]; // YYMMDD - num signal input majority[2]; // YY - ASCII signal output revealedData_packed[3]; + signal output older_than[2]; signal output nullifier; // Verify validity of the passport component isValid = IsValid(); isValid.currDate <== current_date; for (var i = 0; i < 6; i++) { - isValid.validityDateASCII[i] <== mrz[70 + i]; + isValid.validityDateASCII[i] <== dg1[70 + i]; } 1 === isValid.out; @@ -29,23 +30,24 @@ template DISCLOSE() { isOlderThan.majorityASCII <== majority; for (var i = 0; i < 6; i++) { isOlderThan.currDate[i] <== current_date[i]; - isOlderThan.birthDateASCII[i] <== mrz[62 + i]; + isOlderThan.birthDateASCII[i] <== dg1[62 + i]; } - signal older_than[2]; - older_than[0] <== isOlderThan.out * majority[0]; - older_than[1] <== isOlderThan.out * majority[1]; + signal older_than_verified[2]; + older_than_verified[0] <== isOlderThan.out * majority[0]; + older_than_verified[1] <== isOlderThan.out * majority[1]; - // constrain bitmap to be 0s or 1s - for (var i = 0; i < 90; i++) { - bitmap[i] * (bitmap[i] - 1) === 0; + // constrain selector_dg1 to be 0s or 1s + for (var i = 0; i < 88; i++) { + selector_dg1[i] * (selector_dg1[i] - 1) === 0; } - signal revealedData[90]; + signal revealedData[88]; for (var i = 0; i < 88; i++) { - revealedData[i] <== mrz[5+i] * bitmap[i]; + revealedData[i] <== dg1[5+i] * selector_dg1[i]; } - revealedData[88] <== older_than[0] * bitmap[88]; - revealedData[89] <== older_than[1] * bitmap[89]; - revealedData_packed <== PackBytes(90)(revealedData); + older_than[0] <== older_than_verified[0] * selector_older_than; + older_than[1] <== older_than_verified[1] * selector_older_than; + + revealedData_packed <== PackBytes(88)(revealedData); } \ No newline at end of file diff --git a/circuits/circuits/disclose/vc_and_disclose.circom b/circuits/circuits/disclose/vc_and_disclose.circom index 7cf952f9..4fc38514 100644 --- a/circuits/circuits/disclose/vc_and_disclose.circom +++ b/circuits/circuits/disclose/vc_and_disclose.circom @@ -1,32 +1,36 @@ -pragma circom 2.1.5; +pragma circom 2.1.9; include "./verify_commitment.circom"; include "./disclose.circom"; -template VC_AND_DISCLOSE(nLevels) { +template VC_AND_DISCLOSE( nLevels) { + signal input secret; signal input attestation_id; signal input pubkey_leaf; - signal input mrz[93]; + signal input dg1[93]; + signal input dg2_hash[64]; signal input merkle_root; signal input merkletree_size; signal input path[nLevels]; signal input siblings[nLevels]; - signal input bitmap[90]; // 88 for MRZ + 2 for majority + signal input selector_dg1[88]; // 88 for MRZ + signal input selector_older_than; signal input scope; signal input current_date[6]; // YYMMDD - num signal input majority[2]; // YY - ASCII signal input user_identifier; // verify commitment is part of the merkle tree - VERIFY_COMMITMENT(nLevels)(secret, attestation_id, pubkey_leaf, mrz, merkle_root, merkletree_size, path, siblings); + VERIFY_COMMITMENT(nLevels)(secret, attestation_id, pubkey_leaf, dg1, dg2_hash, merkle_root, merkletree_size, path, siblings); // verify passport validity and disclose optional data component disclose = DISCLOSE(); - disclose.mrz <== mrz; - disclose.bitmap <== bitmap; + disclose.dg1 <== dg1; + disclose.selector_dg1 <== selector_dg1; + disclose.selector_older_than <== selector_older_than; disclose.current_date <== current_date; disclose.majority <== majority; @@ -36,6 +40,7 @@ template VC_AND_DISCLOSE(nLevels) { poseidon_nullifier.inputs[1] <== scope; signal output nullifier <== poseidon_nullifier.out; signal output revealedData_packed[3] <== disclose.revealedData_packed; + signal output older_than[2] <== disclose.older_than; } component main { public [ merkle_root, scope, user_identifier, current_date, attestation_id] } = VC_AND_DISCLOSE(16); \ No newline at end of file diff --git a/circuits/circuits/disclose/verify_commitment.circom b/circuits/circuits/disclose/verify_commitment.circom index 2b9de82c..6b8e3fff 100644 --- a/circuits/circuits/disclose/verify_commitment.circom +++ b/circuits/circuits/disclose/verify_commitment.circom @@ -1,23 +1,24 @@ -pragma circom 2.1.5; +pragma circom 2.1.9; include "circomlib/circuits/poseidon.circom"; -include "@zk-email/circuits/utils/bytes.circom"; +include "../utils/other/bytes.circom"; include "binary-merkle-root.circom"; -include "../utils/computeCommitment.circom"; +include "../utils/passport/computeCommitment.circom"; + +template VERIFY_COMMITMENT( nLevels) { -template VERIFY_COMMITMENT(nLevels) { signal input secret; signal input attestation_id; signal input pubkey_leaf; - signal input mrz[93]; + signal input dg1[93]; + signal input dg2_hash[64]; signal input merkle_root; signal input merkletree_size; signal input path[nLevels]; signal input siblings[nLevels]; - signal commitment <== ComputeCommitment()(secret, attestation_id, pubkey_leaf, mrz); - + signal commitment <== ComputeCommitment()(secret, attestation_id, pubkey_leaf, dg1, dg2_hash); // Verify commitment inclusion signal computedRoot <== BinaryMerkleRoot(nLevels)(commitment, merkletree_size, path, siblings); merkle_root === computedRoot; diff --git a/circuits/circuits/dsc/dsc_rsa_65537_sha1.circom b/circuits/circuits/dsc/dsc_rsa_65537_sha1.circom deleted file mode 100644 index 7d67e3cd..00000000 --- a/circuits/circuits/dsc/dsc_rsa_65537_sha1.circom +++ /dev/null @@ -1,76 +0,0 @@ -pragma circom 2.1.5; -include "circomlib/circuits/bitify.circom"; -include "circomlib/circuits/poseidon.circom"; -include "circomlib/circuits/comparators.circom"; -include "binary-merkle-root.circom"; -include "../utils/splitBytesToWords.circom"; -include "../utils/splitSignalsToWords.circom"; -include "../utils/Sha1Bytes.circom"; -include "../utils/leafHasherLight.circom"; -include "../utils/rsaPkcs1.circom"; - -template DSC_RSA_65537_SHA1(max_cert_bytes, n_dsc, k_dsc, n_csca, k_csca, dsc_mod_len, nLevels, signatureAlgorithm) { - signal input raw_dsc_cert[max_cert_bytes]; - signal input raw_dsc_cert_padded_bytes; - signal input csca_modulus[k_csca]; - signal input dsc_signature[k_csca]; - signal input dsc_modulus[k_dsc]; - signal input start_index; - signal input secret; - - signal input merkle_root; - signal input path[nLevels]; - signal input siblings[nLevels]; - - signal output blinded_dsc_commitment; - - //verify the leaf - component leafHasher = LeafHasherLightWithSigAlg(k_csca); - leafHasher.sigAlg <== signatureAlgorithm; - leafHasher.in <== csca_modulus; - signal leaf <== leafHasher.out; - - - signal computed_merkle_root <== BinaryMerkleRoot(nLevels)(leaf, nLevels, path, siblings); - merkle_root === computed_merkle_root; - - // variables verification - assert(max_cert_bytes % 64 == 0); - assert(n_csca * k_csca > max_cert_bytes); - assert(n_csca <= (255 \ 2)); - - // hash raw TBS certificate - signal sha[160] <== Sha1Bytes(max_cert_bytes)(raw_dsc_cert, raw_dsc_cert_padded_bytes); - component sstw_1 = SplitSignalsToWords(1,160, n_csca, k_csca); - for (var i = 0; i < 160; i++) { - sstw_1.in[i] <== sha[159 - i]; - } - - //verify RSA dsc_signature - component rsa = RSAVerify65537(n_csca, k_csca); - for (var i = 0; i < k_csca; i++) { - rsa.base_message[i] <== sstw_1.out[i]; - rsa.modulus[i] <== csca_modulus[i]; - rsa.signature[i] <== dsc_signature[i]; - } - - // verify DSC csca_modulus - component shiftLeft = VarShiftLeft(max_cert_bytes, dsc_mod_len); - shiftLeft.in <== raw_dsc_cert; - shiftLeft.shift <== start_index; - component spbt_1 = SplitBytesToWords(dsc_mod_len, n_dsc, k_dsc); - spbt_1.in <== shiftLeft.out; - for (var i = 0; i < k_dsc; i++) { - dsc_modulus[i] === spbt_1.out[i]; - } - // generate blinded commitment - component sstw_2 = SplitSignalsToWords(n_dsc,k_dsc, 230, 9); - sstw_2.in <== dsc_modulus; - component poseidon = Poseidon(10); - poseidon.inputs[0] <== secret; - for (var i = 0; i < 9; i++) { - poseidon.inputs[i+1] <== sstw_2.out[i]; - } - blinded_dsc_commitment <== poseidon.out; -} - diff --git a/circuits/circuits/dsc/dsc_rsa_65537_sha256.circom b/circuits/circuits/dsc/dsc_rsa_65537_sha256.circom deleted file mode 100644 index 9a3184f8..00000000 --- a/circuits/circuits/dsc/dsc_rsa_65537_sha256.circom +++ /dev/null @@ -1,77 +0,0 @@ -pragma circom 2.1.5; - -include "circomlib/circuits/bitify.circom"; -include "circomlib/circuits/poseidon.circom"; -include "circomlib/circuits/comparators.circom"; -include "@zk-email/circuits/lib/sha.circom"; -include "@zk-email/circuits/lib/rsa.circom"; -include "binary-merkle-root.circom"; -include "../utils/splitBytesToWords.circom"; -include "../utils/splitSignalsToWords.circom"; -include "../utils/leafHasherLight.circom"; - -template DSC_RSA_65537_SHA256(max_cert_bytes, n_dsc, k_dsc, n_csca, k_csca, dsc_mod_len, nLevels, signatureAlgorithm) { - signal input raw_dsc_cert[max_cert_bytes]; - signal input raw_dsc_cert_padded_bytes; - signal input csca_modulus[k_csca]; - signal input dsc_signature[k_csca]; - signal input dsc_modulus[k_dsc]; - signal input start_index; - signal input secret; - - signal input merkle_root; - signal input path[nLevels]; - signal input siblings[nLevels]; - - signal output blinded_dsc_commitment; - - //verify the leaf - component leafHasher = LeafHasherLightWithSigAlg(k_csca); - leafHasher.sigAlg <== signatureAlgorithm; - leafHasher.in <== csca_modulus; - signal leaf <== leafHasher.out; - - - signal computed_merkle_root <== BinaryMerkleRoot(nLevels)(leaf, nLevels, path, siblings); - merkle_root === computed_merkle_root; - - // variables verification - assert(max_cert_bytes % 64 == 0); - assert(n_csca * k_csca > max_cert_bytes); - assert(n_csca <= (255 \ 2)); - - // hash raw TBS certificate - signal sha[256] <== Sha256Bytes(max_cert_bytes)(raw_dsc_cert, raw_dsc_cert_padded_bytes); - component sstw_1 = SplitSignalsToWords(1,256, n_csca, k_csca); - for (var i = 0; i < 256; i++) { - sstw_1.in[i] <== sha[255 - i]; - } - - // verify RSA dsc_signature - component rsa = RSAVerifier65537(n_csca, k_csca); - for (var i = 0; i < k_csca; i++) { - rsa.message[i] <== sstw_1.out[i]; - rsa.modulus[i] <== csca_modulus[i]; - rsa.signature[i] <== dsc_signature[i]; - } - - // verify DSC csca_modulus - component shiftLeft = VarShiftLeft(max_cert_bytes, dsc_mod_len); - shiftLeft.in <== raw_dsc_cert; - shiftLeft.shift <== start_index; - component spbt_1 = SplitBytesToWords(dsc_mod_len, n_dsc, k_dsc); - spbt_1.in <== shiftLeft.out; - for (var i = 0; i < k_dsc; i++) { - dsc_modulus[i] === spbt_1.out[i]; - } - // generate blinded commitment - component sstw_2 = SplitSignalsToWords(n_dsc,k_dsc, 230, 9); - sstw_2.in <== dsc_modulus; - component poseidon = Poseidon(10); - poseidon.inputs[0] <== secret; - for (var i = 0; i < 9; i++) { - poseidon.inputs[i+1] <== sstw_2.out[i]; - } - blinded_dsc_commitment <== poseidon.out; -} - diff --git a/circuits/circuits/dsc/dsc_rsapss_65537_sha256.circom b/circuits/circuits/dsc/dsc_rsapss_65537_sha256.circom deleted file mode 100644 index e44cf62d..00000000 --- a/circuits/circuits/dsc/dsc_rsapss_65537_sha256.circom +++ /dev/null @@ -1,69 +0,0 @@ -pragma circom 2.1.5; - -include "circomlib/circuits/bitify.circom"; -include "circomlib/circuits/poseidon.circom"; -include "circomlib/circuits/comparators.circom"; -include "@zk-email/circuits/lib/sha.circom"; -include "binary-merkle-root.circom"; -include "../utils/splitBytesToWords.circom"; -include "../utils/splitSignalsToWords.circom"; -include "../utils/leafHasherLight.circom"; -include "../utils/rsapss/rsapss.circom"; - -template DSC_RSAPSS_65537_SHA256(max_cert_bytes, n_dsc, k_dsc, n_csca, k_csca, modulus_bits_size, dsc_mod_len, nLevels, signatureAlgorithm) { - signal input raw_dsc_cert[max_cert_bytes]; - signal input raw_dsc_cert_padded_bytes; - signal input csca_modulus[k_csca]; - signal input dsc_signature[k_csca]; - signal input dsc_modulus[k_dsc]; - signal input start_index; - signal input secret; - - signal input merkle_root; - signal input path[nLevels]; - signal input siblings[nLevels]; - - signal output blinded_dsc_commitment; - - //verify the leaf - component leafHasher = LeafHasherLightWithSigAlg(k_csca); - leafHasher.sigAlg <== signatureAlgorithm; - leafHasher.in <== csca_modulus; - signal leaf <== leafHasher.out; - - signal computed_merkle_root <== BinaryMerkleRoot(nLevels)(leaf, nLevels, path, siblings); - merkle_root === computed_merkle_root; - - // variables verification - assert(max_cert_bytes % 64 == 0); - assert(n_csca * k_csca > max_cert_bytes); - assert(n_csca <= (255 \ 2)); - - // verify rsapss signature - signal dsc_cert_hash[256]; - dsc_cert_hash <== Sha256Bytes(max_cert_bytes)(raw_dsc_cert, raw_dsc_cert_padded_bytes); - component rsaPssSha256Verification = VerifyRsaPssSig(n_csca, k_csca, 17, 256, modulus_bits_size); - rsaPssSha256Verification.pubkey <== csca_modulus; - rsaPssSha256Verification.signature <== dsc_signature; - rsaPssSha256Verification.hashed <== dsc_cert_hash; - - // verify DSC csca_modulus - component shiftLeft = VarShiftLeft(max_cert_bytes, dsc_mod_len); - shiftLeft.in <== raw_dsc_cert; - shiftLeft.shift <== start_index; - component spbt_1 = SplitBytesToWords(dsc_mod_len, n_dsc, k_dsc); - spbt_1.in <== shiftLeft.out; - for (var i = 0; i < k_dsc; i++) { - dsc_modulus[i] === spbt_1.out[i]; - } - // generate blinded commitment - component sstw_1 = SplitSignalsToWords(n_dsc,k_dsc, 230, 9); - sstw_1.in <== dsc_modulus; - component poseidon = Poseidon(10); - poseidon.inputs[0] <== secret; - for (var i = 0; i < 9; i++) { - poseidon.inputs[i+1] <== sstw_1.out[i]; - } - blinded_dsc_commitment <== poseidon.out; -} - diff --git a/circuits/circuits/dsc/instances/dsc_rsa_65537_sha1_2048.circom b/circuits/circuits/dsc/instances/dsc_rsa_65537_sha1_2048.circom new file mode 100644 index 00000000..16476e05 --- /dev/null +++ b/circuits/circuits/dsc/instances/dsc_rsa_65537_sha1_2048.circom @@ -0,0 +1,4 @@ +pragma circom 2.1.9; +include "../openpassport_dsc.circom"; + +component main = OPENPASSPORT_DSC(3, 64, 32, 64, 32, 960, 256, 12); diff --git a/circuits/circuits/dsc/instances/dsc_rsa_65537_sha256_2048.circom b/circuits/circuits/dsc/instances/dsc_rsa_65537_sha256_2048.circom new file mode 100644 index 00000000..21fe5ab5 --- /dev/null +++ b/circuits/circuits/dsc/instances/dsc_rsa_65537_sha256_2048.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "../openpassport_dsc.circom"; + +component main = OPENPASSPORT_DSC(1, 64, 32, 64, 32, 960, 256, 12); \ No newline at end of file diff --git a/circuits/circuits/dsc/instances/dsc_rsapss_65537_sha256_2048.circom b/circuits/circuits/dsc/instances/dsc_rsapss_65537_sha256_2048.circom new file mode 100644 index 00000000..e906e77e --- /dev/null +++ b/circuits/circuits/dsc/instances/dsc_rsapss_65537_sha256_2048.circom @@ -0,0 +1,4 @@ +pragma circom 2.1.9; +include "../openpassport_dsc.circom"; + +component main = OPENPASSPORT_DSC(4, 64, 32, 64, 32, 960, 256, 12); diff --git a/circuits/circuits/dsc/openpassport_dsc.circom b/circuits/circuits/dsc/openpassport_dsc.circom new file mode 100644 index 00000000..8ed60e5c --- /dev/null +++ b/circuits/circuits/dsc/openpassport_dsc.circom @@ -0,0 +1,62 @@ +pragma circom 2.1.9; + +include "circomlib/circuits/bitify.circom"; +include "circomlib/circuits/poseidon.circom"; +include "circomlib/circuits/comparators.circom"; +include "binary-merkle-root.circom"; +include "../utils/passport/customHashers.circom"; +include "../utils/other/bytes.circom"; +include "../utils/passport/signatureAlgorithm.circom"; +include "../utils/passport/signatureVerifier.circom"; +include "../utils/shaBytes/shaBytesDynamic.circom"; +include "../utils/other/bytes.circom"; + + +template OPENPASSPORT_DSC(signatureAlgorithm, n_dsc, k_dsc, n_csca, k_csca, max_cert_bytes, dscPubkeyBytesLength, nLevels) { + + // variables verification + assert(max_cert_bytes % 64 == 0); + assert(n_csca * k_csca > max_cert_bytes); + assert(n_csca <= (255 \ 2)); + + var hashLength = getHashLength(signatureAlgorithm); + var kLengthFactor = getKLengthFactor(signatureAlgorithm); + var kScaled = k_csca * kLengthFactor; + + signal input raw_dsc_cert[max_cert_bytes]; + signal input raw_dsc_cert_padded_bytes; + signal input csca_pubKey[kScaled]; + signal input signature[kScaled]; + signal input dsc_pubKey[k_dsc]; + signal input dsc_pubKey_offset; + signal input secret; + + signal input merkle_root; + signal input path[nLevels]; + signal input siblings[nLevels]; + + // leaf + signal leaf <== LeafHasher(kScaled)(csca_pubKey, signatureAlgorithm); + + signal computed_merkle_root <== BinaryMerkleRoot(nLevels)(leaf, nLevels, path, siblings); + merkle_root === computed_merkle_root; + + // verify certificate signature + signal hashedCertificate[hashLength] <== ShaBytesDynamic(hashLength, max_cert_bytes)(raw_dsc_cert, raw_dsc_cert_padded_bytes); + SignatureVerifier(signatureAlgorithm, n_csca, k_csca)(hashedCertificate, csca_pubKey, signature); + + // verify DSC csca_pubKey + component shiftLeft = VarShiftLeft(max_cert_bytes, dscPubkeyBytesLength); // use select subarray for dscPubKey variable length + shiftLeft.in <== raw_dsc_cert; + shiftLeft.shift <== dsc_pubKey_offset; + component spbt_1 = SplitBytesToWords(dscPubkeyBytesLength, n_dsc, k_dsc); + spbt_1.in <== shiftLeft.out; + for (var i = 0; i < k_dsc; i++) { + dsc_pubKey[i] === spbt_1.out[i]; + } + + // blinded dsc commitment + signal pubkeyHash <== CustomHasher(k_dsc)(dsc_pubKey); + signal output blinded_dsc_commitment <== Poseidon(2)([secret, pubkeyHash]); +} + diff --git a/circuits/circuits/ofac/ofac_name.circom b/circuits/circuits/ofac/ofac_name.circom index 43a5050f..3ac0c108 100644 --- a/circuits/circuits/ofac/ofac_name.circom +++ b/circuits/circuits/ofac/ofac_name.circom @@ -1,17 +1,18 @@ -pragma circom 2.1.5; +pragma circom 2.1.9; include "circomlib/circuits/poseidon.circom"; include "circomlib/circuits/comparators.circom"; include "binary-merkle-root.circom"; -include "../utils/getCommonLength.circom"; +include "../utils/other/getCommonLength.circom"; include "../disclose/verify_commitment.circom"; -include "../utils/smt.circom"; +include "../utils/other/smt.circom"; template OFAC_NAME(nLevels) { signal input secret; signal input attestation_id; signal input pubkey_leaf; - signal input mrz[93]; + signal input dg1[93]; + signal input dg2_hash[64]; signal input merkle_root; signal input merkletree_size; signal input path[nLevels]; @@ -25,14 +26,14 @@ template OFAC_NAME(nLevels) { signal output proofLevel; // Verify commitment is part of the merkle tree - VERIFY_COMMITMENT(nLevels)(secret, attestation_id, pubkey_leaf, mrz, merkle_root, merkletree_size, path, siblings); + VERIFY_COMMITMENT(nLevels)(secret, attestation_id, pubkey_leaf, dg1, dg2_hash, merkle_root, merkletree_size, path, siblings); // Name Hash component poseidon_hasher[3]; for (var j = 0; j < 3; j++) { poseidon_hasher[j] = Poseidon(13); for (var i = 0; i < 13; i++) { - poseidon_hasher[j].inputs[i] <== mrz[10 + 13 * j + i]; + poseidon_hasher[j].inputs[i] <== dg1[10 + 13 * j + i]; } } diff --git a/circuits/circuits/ofac/ofac_name_dob.circom b/circuits/circuits/ofac/ofac_name_dob.circom index 5be8071b..94f0fb4e 100644 --- a/circuits/circuits/ofac/ofac_name_dob.circom +++ b/circuits/circuits/ofac/ofac_name_dob.circom @@ -1,18 +1,19 @@ -pragma circom 2.1.5; +pragma circom 2.1.9; include "circomlib/circuits/poseidon.circom"; include "circomlib/circuits/comparators.circom"; include "circomlib/circuits/bitify.circom"; include "binary-merkle-root.circom"; -include "../utils/getCommonLength.circom"; +include "../utils/other/getCommonLength.circom"; include "../disclose/verify_commitment.circom"; -include "../utils/smt.circom"; +include "../utils/other/smt.circom"; template OFAC_NAME_DOB(nLevels) { signal input secret; signal input attestation_id; signal input pubkey_leaf; - signal input mrz[93]; + signal input dg1[93]; + signal input dg2_hash[64]; signal input merkle_root; signal input merkletree_size; signal input path[nLevels]; @@ -25,14 +26,14 @@ template OFAC_NAME_DOB(nLevels) { signal output proofLevel; // Verify commitment is part of the merkle tree - VERIFY_COMMITMENT(nLevels)(secret, attestation_id, pubkey_leaf, mrz, merkle_root, merkletree_size, path, siblings); + VERIFY_COMMITMENT(nLevels)(secret, attestation_id, pubkey_leaf, dg1, dg2_hash, merkle_root, merkletree_size, path, siblings); // Name Hash component poseidon_hasher[3]; for (var j = 0; j < 3; j++) { poseidon_hasher[j] = Poseidon(13); for (var i = 0; i < 13; i++) { - poseidon_hasher[j].inputs[i] <== mrz[10 + 13 * j + i]; + poseidon_hasher[j].inputs[i] <== dg1[10 + 13 * j + i]; } } signal name_hash <== Poseidon(3)([poseidon_hasher[0].out, poseidon_hasher[1].out, poseidon_hasher[2].out]); @@ -40,7 +41,7 @@ template OFAC_NAME_DOB(nLevels) { // Dob hash component pos_dob = Poseidon(6); for(var i = 0; i < 6; i++) { - pos_dob.inputs[i] <== mrz[62 + i]; + pos_dob.inputs[i] <== dg1[62 + i]; } // NameDob hash diff --git a/circuits/circuits/ofac/ofac_passport_number.circom b/circuits/circuits/ofac/ofac_passport_number.circom index 48d79ed1..7c81f43f 100644 --- a/circuits/circuits/ofac/ofac_passport_number.circom +++ b/circuits/circuits/ofac/ofac_passport_number.circom @@ -1,19 +1,20 @@ -pragma circom 2.1.5; +pragma circom 2.1.9; include "circomlib/circuits/poseidon.circom"; include "circomlib/circuits/comparators.circom"; include "circomlib/circuits/bitify.circom"; -include "@zk-email/circuits/utils/array.circom"; +include "../utils/other/array.circom"; include "binary-merkle-root.circom"; -include "../utils/getCommonLength.circom"; +include "../utils/other/getCommonLength.circom"; include "../disclose/verify_commitment.circom"; -include "../utils/smt.circom"; +include "../utils/other/smt.circom"; template OFAC_PASSPORT_NUMBER(nLevels) { signal input secret; signal input attestation_id; signal input pubkey_leaf; - signal input mrz[93]; + signal input dg1[93]; + signal input dg2_hash[64]; signal input merkle_root; signal input merkletree_size; signal input path[nLevels]; @@ -26,12 +27,12 @@ template OFAC_PASSPORT_NUMBER(nLevels) { signal output proofLevel; // Verify commitment is part of the merkle tree - VERIFY_COMMITMENT(nLevels)(secret, attestation_id, pubkey_leaf, mrz, merkle_root, merkletree_size, path, siblings); + VERIFY_COMMITMENT(nLevels)(secret, attestation_id, pubkey_leaf, dg1, dg2_hash, merkle_root, merkletree_size, path, siblings); // PassportNo Hash component poseidon_hasher = Poseidon(9); for (var i = 0; i < 9; i++) { - poseidon_hasher.inputs[i] <== mrz[49 + i]; + poseidon_hasher.inputs[i] <== dg1[49 + i]; } signal smtleaf_hash <== Poseidon(3)([poseidon_hasher.out, 1,1]); diff --git a/circuits/circuits/prove/instances/prove_ecdsa_sha1.circom b/circuits/circuits/prove/instances/prove_ecdsa_sha1.circom new file mode 100644 index 00000000..151bf5db --- /dev/null +++ b/circuits/circuits/prove/instances/prove_ecdsa_sha1.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "../openpassport_prove.circom"; + +component main { public [ user_identifier, scope ] } = OPENPASSPORT_PROVE(7, 43, 6, 448, 448); \ No newline at end of file diff --git a/circuits/circuits/prove/instances/prove_ecdsa_sha256.circom b/circuits/circuits/prove/instances/prove_ecdsa_sha256.circom new file mode 100644 index 00000000..fb6adc81 --- /dev/null +++ b/circuits/circuits/prove/instances/prove_ecdsa_sha256.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "../openpassport_prove.circom"; + +component main { public [ user_identifier, scope ] } = OPENPASSPORT_PROVE(8, 43, 6, 640, 512); \ No newline at end of file diff --git a/circuits/circuits/prove/instances/prove_rsa_65537_sha1.circom b/circuits/circuits/prove/instances/prove_rsa_65537_sha1.circom new file mode 100644 index 00000000..661e2327 --- /dev/null +++ b/circuits/circuits/prove/instances/prove_rsa_65537_sha1.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "../openpassport_prove.circom"; + +component main { public [ user_identifier, scope ] } = OPENPASSPORT_PROVE(3, 64, 32, 448, 448); \ No newline at end of file diff --git a/circuits/circuits/prove/instances/prove_rsa_65537_sha256.circom b/circuits/circuits/prove/instances/prove_rsa_65537_sha256.circom new file mode 100644 index 00000000..7718d0e0 --- /dev/null +++ b/circuits/circuits/prove/instances/prove_rsa_65537_sha256.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "../openpassport_prove.circom"; + +component main { public [ user_identifier, scope ] } = OPENPASSPORT_PROVE(1, 64, 32, 384, 320); \ No newline at end of file diff --git a/circuits/circuits/prove/instances/prove_rsapss_65537_sha256.circom b/circuits/circuits/prove/instances/prove_rsapss_65537_sha256.circom new file mode 100644 index 00000000..2b3ffadc --- /dev/null +++ b/circuits/circuits/prove/instances/prove_rsapss_65537_sha256.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "../openpassport_prove.circom"; + +component main { public [ user_identifier, scope ] } = OPENPASSPORT_PROVE(4, 64, 32, 640, 512); \ No newline at end of file diff --git a/circuits/circuits/prove/openpassport_prove.circom b/circuits/circuits/prove/openpassport_prove.circom new file mode 100644 index 00000000..0202cbae --- /dev/null +++ b/circuits/circuits/prove/openpassport_prove.circom @@ -0,0 +1,71 @@ +pragma circom 2.1.9; + +include "../utils/passport/customHashers.circom"; +include "../utils/passport/computeCommitment.circom"; +include "../utils/passport/signatureAlgorithm.circom"; +include "../utils/passport/passportVerifier.circom"; +include "../disclose/disclose.circom"; + +template OPENPASSPORT_PROVE(signatureAlgorithm, n, k, MAX_ECONTENT_PADDED_LEN, MAX_SIGNED_ATTR_PADDED_LEN) { + var kLengthFactor = getKLengthFactor(signatureAlgorithm); + var kScaled = k * kLengthFactor; + + var HASH_LEN_BITS = getHashLength(signatureAlgorithm); + var HASH_LEN_BYTES = HASH_LEN_BITS / 8; + + signal input dg1[93]; + signal input dg1_hash_offset; + signal input dg2_hash[64]; + signal input eContent[MAX_ECONTENT_PADDED_LEN]; + signal input eContent_padded_length; + signal input signed_attr[MAX_SIGNED_ATTR_PADDED_LEN]; + signal input signed_attr_padded_length; + signal input signed_attr_econtent_hash_offset; + signal input pubKey[kScaled]; + signal input signature[kScaled]; + signal input selector_mode; // 0 - disclose, 1 - registration + // disclose related inputs + signal input selector_dg1[88]; + signal input selector_older_than; + signal input current_date[6]; // YYMMDD - num + signal input majority[2]; // YY - ASCII + signal input user_identifier; + signal input scope; + // registration related inputs + signal input secret; + signal input dsc_secret; + + signal attestation_id <== 1; + + // assert selector_mode is 0 or 1 + selector_mode * (selector_mode - 1) === 0; + + // verify passport signature + PassportVerifier(signatureAlgorithm, n, k, MAX_ECONTENT_PADDED_LEN, MAX_SIGNED_ATTR_PADDED_LEN)(dg1,dg1_hash_offset, dg2_hash, eContent,eContent_padded_length, signed_attr, signed_attr_padded_length, signed_attr_econtent_hash_offset, pubKey, signature); + + // nulifier + signal signatureHashed <== CustomHasher(kScaled)(signature); // generate nullifier + signal output nullifier <== Poseidon(2)([signatureHashed, scope]); + + // DISCLOSE (optional) + // optionally disclose data + component disclose = DISCLOSE(); + disclose.dg1 <== dg1; + disclose.selector_dg1 <== selector_dg1; + disclose.selector_older_than <== selector_older_than; + disclose.current_date <== current_date; + disclose.majority <== majority; + signal output revealedData_packed[3] <== disclose.revealedData_packed; + signal output older_than[2] <== disclose.older_than; + + // REGISTRATION (optional) + // generate the commitment + signal leaf <== LeafHasher(kScaled)(pubKey, signatureAlgorithm); + signal commitmentPrivate <== ComputeCommitment()(secret, attestation_id, leaf, dg1, dg2_hash); + signal output commitment <== commitmentPrivate * selector_mode; + // blinded dsc commitment + signal pubkeyHash <== CustomHasher(kScaled)(pubKey); + signal blindedDscCommitmenPrivate <== Poseidon(2)([dsc_secret, pubkeyHash]); + signal output blinded_dsc_commitment <== blindedDscCommitmenPrivate * selector_mode; + +} \ No newline at end of file diff --git a/circuits/circuits/prove/prove_rsa_65537_sha1.circom b/circuits/circuits/prove/prove_rsa_65537_sha1.circom deleted file mode 100644 index 8851a068..00000000 --- a/circuits/circuits/prove/prove_rsa_65537_sha1.circom +++ /dev/null @@ -1,56 +0,0 @@ -pragma circom 2.1.5; - -include "circomlib/circuits/poseidon.circom"; -include "@zk-email/circuits/utils/bytes.circom"; -include "../verifier/passport_verifier_rsa_65537_sha1.circom"; -include "binary-merkle-root.circom"; -include "../utils/splitSignalsToWords.circom"; -include "../disclose/disclose.circom"; - -template PROVE_RSA_65537_SHA1(n, k, max_datahashes_bytes) { - /*** CUSTOM IMPLEMENTATION ***/ - signal input mrz[93]; - signal input dg1_hash_offset; - signal input dataHashes[max_datahashes_bytes]; - signal input datahashes_padded_length; - signal input eContent[92]; - signal input signature[k]; - signal input dsc_modulus[k]; - signal output signature_algorithm <== 000; - - // Verify passport validity - component PV = PASSPORT_VERIFIER_RSA_65537_SHA1(n, k, max_datahashes_bytes); - PV.mrz <== mrz; - PV.dg1_hash_offset <== dg1_hash_offset; - PV.dataHashes <== dataHashes; - PV.datahashes_padded_length <== datahashes_padded_length; - PV.eContentBytes <== eContent; - PV.dsc_modulus <== dsc_modulus; - PV.signature <== signature; - - /*** COMMON TO ALL CIRCUITS ***/ - signal input scope; - signal input bitmap[90]; - signal input current_date[6]; // YYMMDD - num - signal input majority[2]; // YY - ASCII - signal input user_identifier; - - // verify passport validity and disclose optional data - component disclose = DISCLOSE(); - disclose.mrz <== mrz; - disclose.bitmap <== bitmap; - disclose.current_date <== current_date; - disclose.majority <== majority; - signal output revealedData_packed[3] <== disclose.revealedData_packed; - - // generate nullifier - signal split_signature[9] <== SplitSignalsToWords(n, k, 230, 9)(signature); - component nullifier_hasher = Poseidon(10); - for (var i = 0; i < 9; i++) { - nullifier_hasher.inputs[i] <== split_signature[i]; - } - nullifier_hasher.inputs[9] <== scope; - signal output nullifier <== nullifier_hasher.out; -} - -component main { public [ dsc_modulus, scope, user_identifier, current_date ] } = PROVE_RSA_65537_SHA1(64, 32, 320); diff --git a/circuits/circuits/prove/prove_rsa_65537_sha256.circom b/circuits/circuits/prove/prove_rsa_65537_sha256.circom deleted file mode 100644 index 3e31f535..00000000 --- a/circuits/circuits/prove/prove_rsa_65537_sha256.circom +++ /dev/null @@ -1,56 +0,0 @@ -pragma circom 2.1.5; - -include "circomlib/circuits/poseidon.circom"; -include "@zk-email/circuits/utils/bytes.circom"; -include "../verifier/passport_verifier_rsa_65537_sha256.circom"; -include "binary-merkle-root.circom"; -include "../utils/splitSignalsToWords.circom"; -include "../disclose/disclose.circom"; - -template PROVE_RSA_65537_SHA256(n, k, max_datahashes_bytes) { - /*** CUSTOM IMPLEMENTATION ***/ - signal input mrz[93]; - signal input dg1_hash_offset; - signal input dataHashes[max_datahashes_bytes]; - signal input datahashes_padded_length; - signal input eContent[104]; - signal input signature[k]; - signal input dsc_modulus[k]; - signal output signature_algorithm <== 001; - - // Verify passport validity - component PV = PASSPORT_VERIFIER_RSA_65537_SHA256(n, k, max_datahashes_bytes); - PV.mrz <== mrz; - PV.dg1_hash_offset <== dg1_hash_offset; - PV.dataHashes <== dataHashes; - PV.datahashes_padded_length <== datahashes_padded_length; - PV.eContentBytes <== eContent; - PV.dsc_modulus <== dsc_modulus; - PV.signature <== signature; - - /*** COMMON TO ALL CIRCUITS ***/ - signal input scope; - signal input bitmap[90]; - signal input current_date[6]; // YYMMDD - num - signal input majority[2]; // YY - ASCII - signal input user_identifier; - - // verify passport validity and disclose optional data - component disclose = DISCLOSE(); - disclose.mrz <== mrz; - disclose.bitmap <== bitmap; - disclose.current_date <== current_date; - disclose.majority <== majority; - signal output revealedData_packed[3] <== disclose.revealedData_packed; - - // generate nullifier - signal split_signature[9] <== SplitSignalsToWords(n, k, 230, 9)(signature); - component nullifier_hasher = Poseidon(10); - for (var i = 0; i < 9; i++) { - nullifier_hasher.inputs[i] <== split_signature[i]; - } - nullifier_hasher.inputs[9] <== scope; - signal output nullifier <== nullifier_hasher.out; -} - -component main { public [ dsc_modulus, scope, user_identifier, current_date ] } = PROVE_RSA_65537_SHA256(64, 32, 320); diff --git a/circuits/circuits/prove/prove_rsapss_65537_sha256.circom b/circuits/circuits/prove/prove_rsapss_65537_sha256.circom deleted file mode 100644 index 79843909..00000000 --- a/circuits/circuits/prove/prove_rsapss_65537_sha256.circom +++ /dev/null @@ -1,56 +0,0 @@ -pragma circom 2.1.5; - -include "circomlib/circuits/poseidon.circom"; -include "@zk-email/circuits/utils/bytes.circom"; -include "../verifier/passport_verifier_rsapss_65537_sha256.circom"; -include "binary-merkle-root.circom"; -include "../utils/splitSignalsToWords.circom"; -include "../disclose/disclose.circom"; - -template PROVE_RSAPSS_65537_SHA256(n, k, max_datahashes_bytes) { - /*** CUSTOM IMPLEMENTATION ***/ - signal input mrz[93]; - signal input dg1_hash_offset; - signal input dataHashes[max_datahashes_bytes]; - signal input datahashes_padded_length; - signal input eContent[104]; - signal input signature[k]; - signal input dsc_modulus[k]; - signal output signature_algorithm <== 001; - - // Verify passport validity - component PV = PASSPORT_VERIFIER_RSAPSS_65537_SHA256(n, k, max_datahashes_bytes); - PV.mrz <== mrz; - PV.dg1_hash_offset <== dg1_hash_offset; - PV.dataHashes <== dataHashes; - PV.datahashes_padded_length <== datahashes_padded_length; - PV.eContentBytes <== eContent; - PV.dsc_modulus <== dsc_modulus; - PV.signature <== signature; - - /*** COMMON TO ALL CIRCUITS ***/ - signal input scope; - signal input bitmap[90]; - signal input current_date[6]; // YYMMDD - num - signal input majority[2]; // YY - ASCII - signal input user_identifier; - - // verify passport validity and disclose optional data - component disclose = DISCLOSE(); - disclose.mrz <== mrz; - disclose.bitmap <== bitmap; - disclose.current_date <== current_date; - disclose.majority <== majority; - signal output revealedData_packed[3] <== disclose.revealedData_packed; - - // generate nullifier - signal split_signature[9] <== SplitSignalsToWords(n, k, 230, 9)(signature); - component nullifier_hasher = Poseidon(10); - for (var i = 0; i < 9; i++) { - nullifier_hasher.inputs[i] <== split_signature[i]; - } - nullifier_hasher.inputs[9] <== scope; - signal output nullifier <== nullifier_hasher.out; -} - -component main { public [ dsc_modulus, scope, user_identifier, current_date ] } = PROVE_RSAPSS_65537_SHA256(64, 32, 320); diff --git a/circuits/circuits/register/register_ecdsa_sha1.circom b/circuits/circuits/register/register_ecdsa_sha1.circom deleted file mode 100644 index a732fde3..00000000 --- a/circuits/circuits/register/register_ecdsa_sha1.circom +++ /dev/null @@ -1,65 +0,0 @@ -pragma circom 2.1.5; - -include "circomlib/circuits/poseidon.circom"; -include "../verifier/passport_verifier_ecdsa_sha1.circom"; -include "../utils/computeCommitment.circom"; -include "../utils/leafHasherLight.circom"; - -template REGISTER_ECDSA_SHA1(n, k, max_datahashes_bytes, nLevels, signatureAlgorithm) { - signal input secret; - - signal input mrz[93]; - signal input dg1_hash_offset; - signal input dataHashes[max_datahashes_bytes]; - signal input datahashes_padded_length; - signal input eContent[92]; - - signal input signature_r[k]; // ECDSA signature component r - signal input signature_s[k]; // ECDSA signature component s - signal input dsc_modulus_x[k]; // Public Key x-coordinate - signal input dsc_modulus_y[k]; // Public Key y-coordinate - - signal input dsc_secret; - signal input attestation_id; - - // hash the dsc pubkey to generate the leaf - component leafHasher = LeafHasherLightWithSigAlgECDSA(k); - leafHasher.sigAlg <== signatureAlgorithm; - leafHasher.x <== dsc_modulus_x; - leafHasher.y <== dsc_modulus_y; - signal leaf <== leafHasher.out; - - - component dsc_commitment_hasher = Poseidon(2); - component nullifier_hasher = Poseidon(2); - - dsc_commitment_hasher.inputs[0] <== dsc_secret; - dsc_commitment_hasher.inputs[1] <== leaf; - - signal output blinded_dsc_commitment <== dsc_commitment_hasher.out; - - // Poseidon(signature_r[0], signature_r[1], ..., signature_r[5]) - signal signature_r_hash <== Poseidon(k)(signature_r); - signal signature_s_hash <== Poseidon(k)(signature_s); - - nullifier_hasher.inputs[0] <== signature_r_hash; - nullifier_hasher.inputs[1] <== signature_s_hash; - signal output nullifier <== nullifier_hasher.out; - - // Verify passport validity - component PV = PASSPORT_VERIFIER_ECDSA_SHA1(n, k, max_datahashes_bytes); - PV.mrz <== mrz; - PV.dg1_hash_offset <== dg1_hash_offset; - PV.dataHashes <== dataHashes; - PV.datahashes_padded_length <== datahashes_padded_length; - PV.eContentBytes <== eContent; - PV.dsc_modulus <== [dsc_modulus_x, dsc_modulus_y]; - PV.signature_r <== signature_r; - PV.signature_s <== signature_s; - - // Generate the commitment - signal output commitment <== ComputeCommitment()(secret, attestation_id, leaf, mrz); -} - -// We hardcode 7 here for ecdsa_with_SHA1 -component main { public [ attestation_id ] } = REGISTER_ECDSA_SHA1(43, 6, 320, 16, 7); diff --git a/circuits/circuits/register/register_ecdsa_sha256.circom b/circuits/circuits/register/register_ecdsa_sha256.circom deleted file mode 100644 index 74ebf4d9..00000000 --- a/circuits/circuits/register/register_ecdsa_sha256.circom +++ /dev/null @@ -1,64 +0,0 @@ -pragma circom 2.1.5; - -include "circomlib/circuits/poseidon.circom"; -include "../verifier/passport_verifier_ecdsa_sha256.circom"; -include "../utils/computeCommitment.circom"; -include "../utils/leafHasherLight.circom"; - -template REGISTER_ECDSA_SHA256(n, k, max_datahashes_bytes, nLevels, signatureAlgorithm) { - signal input secret; - - signal input mrz[93]; - signal input dg1_hash_offset; - signal input dataHashes[max_datahashes_bytes]; - signal input datahashes_padded_length; - signal input eContent[104]; - - signal input signature_r[k]; // ECDSA signature component r - signal input signature_s[k]; // ECDSA signature component s - signal input dsc_modulus_x[k]; // Public Key x-coordinate - signal input dsc_modulus_y[k]; // Public Key y-coordinate - - signal input dsc_secret; - signal input attestation_id; - - // hash the dsc pubkey to generate the leaf - component leafHasher = LeafHasherLightWithSigAlgECDSA(k); - leafHasher.sigAlg <== signatureAlgorithm; - leafHasher.x <== dsc_modulus_x; - leafHasher.y <== dsc_modulus_y; - signal leaf <== leafHasher.out; - - component dsc_commitment_hasher = Poseidon(2); - component nullifier_hasher = Poseidon(2); - - dsc_commitment_hasher.inputs[0] <== dsc_secret; - dsc_commitment_hasher.inputs[1] <== leaf; - - signal output blinded_dsc_commitment <== dsc_commitment_hasher.out; - - // Poseidon(signature_r[0], signature_r[1], ..., signature_r[5]) - signal signature_r_hash <== Poseidon(k)(signature_r); - signal signature_s_hash <== Poseidon(k)(signature_s); - - nullifier_hasher.inputs[0] <== signature_r_hash; - nullifier_hasher.inputs[1] <== signature_s_hash; - signal output nullifier <== nullifier_hasher.out; - - // Verify passport validity - component PV = PASSPORT_VERIFIER_ECDSA_SHA256(n, k, max_datahashes_bytes); - PV.mrz <== mrz; - PV.dg1_hash_offset <== dg1_hash_offset; - PV.dataHashes <== dataHashes; - PV.datahashes_padded_length <== datahashes_padded_length; - PV.eContentBytes <== eContent; - PV.dsc_modulus <== [dsc_modulus_x, dsc_modulus_y]; - PV.signature_r <== signature_r; - PV.signature_s <== signature_s; - - // Generate the commitment - signal output commitment <== ComputeCommitment()(secret, attestation_id, leaf, mrz); -} - -// We hardcode 8 here for ecdsa_with_SHA256 -component main { public [ attestation_id ] } = REGISTER_ECDSA_SHA256(43, 6, 320, 16, 8); diff --git a/circuits/circuits/register/register_rsa_65537_sha1.circom b/circuits/circuits/register/register_rsa_65537_sha1.circom deleted file mode 100644 index c6cd3437..00000000 --- a/circuits/circuits/register/register_rsa_65537_sha1.circom +++ /dev/null @@ -1,54 +0,0 @@ -pragma circom 2.1.5; - -include "circomlib/circuits/poseidon.circom"; -include "../verifier/passport_verifier_rsa_65537_sha1.circom"; -include "../utils/splitSignalsToWords.circom"; -include "../utils/leafHasherLight.circom"; -include "../utils/computeCommitment.circom"; - -template REGISTER_RSA_65537_SHA1(n, k, max_datahashes_bytes, nLevels, signatureAlgorithm) { - signal input secret; - - signal input mrz[93]; - signal input dg1_hash_offset; - signal input dataHashes[max_datahashes_bytes]; - signal input datahashes_padded_length; - signal input eContent[92]; - signal input signature[k]; - signal input dsc_modulus[k]; - signal input dsc_secret; - signal input attestation_id; - - signal split_signature[9] <== SplitSignalsToWords(n, k, 230, 9)(signature); - signal output nullifier <== Poseidon(9)(split_signature); - - signal split_modulus[9] <== SplitSignalsToWords(n, k, 230, 9)(dsc_modulus); - component dsc_commitment_hasher = Poseidon(10); - dsc_commitment_hasher.inputs[0] <== dsc_secret; - for (var i = 0; i < 9; i++) { - dsc_commitment_hasher.inputs[i + 1] <== split_modulus[i]; - } - signal output blinded_dsc_commitment <== dsc_commitment_hasher.out; - - // Verify passport validity - component PV = PASSPORT_VERIFIER_RSA_65537_SHA1(n, k, max_datahashes_bytes); - PV.mrz <== mrz; - PV.dg1_hash_offset <== dg1_hash_offset; - PV.dataHashes <== dataHashes; - PV.datahashes_padded_length <== datahashes_padded_length; - PV.eContentBytes <== eContent; - PV.dsc_modulus <== dsc_modulus; - PV.signature <== signature; - - // Generate the leaf - component leafHasher = LeafHasherLightWithSigAlg(k); - leafHasher.sigAlg <== signatureAlgorithm; - leafHasher.in <== dsc_modulus; - signal leaf <== leafHasher.out; - - // Generate the commitment - signal output commitment <== ComputeCommitment()(secret, attestation_id, leaf, mrz); -} - -// We hardcode 3 here for sha1WithRSAEncryption_65537 -component main { public [ attestation_id ] } = REGISTER_RSA_65537_SHA1(64, 32, 320, 16, 3); diff --git a/circuits/circuits/register/register_rsa_65537_sha256.circom b/circuits/circuits/register/register_rsa_65537_sha256.circom deleted file mode 100644 index 23c0fd45..00000000 --- a/circuits/circuits/register/register_rsa_65537_sha256.circom +++ /dev/null @@ -1,54 +0,0 @@ -pragma circom 2.1.5; - -include "circomlib/circuits/poseidon.circom"; -include "../verifier/passport_verifier_rsa_65537_sha256.circom"; -include "../utils/splitSignalsToWords.circom"; -include "../utils/leafHasherLight.circom"; -include "../utils/computeCommitment.circom"; - -template REGISTER_RSA_65537_SHA256(n, k, max_datahashes_bytes, nLevels, signatureAlgorithm) { - signal input secret; - - signal input mrz[93]; - signal input dg1_hash_offset; - signal input dataHashes[max_datahashes_bytes]; - signal input datahashes_padded_length; - signal input eContent[104]; - signal input signature[k]; - signal input dsc_modulus[k]; - signal input dsc_secret; - signal input attestation_id; - - signal split_signature[9] <== SplitSignalsToWords(n, k, 230, 9)(signature); - signal output nullifier <== Poseidon(9)(split_signature); - - signal split_modulus[9] <== SplitSignalsToWords(n, k, 230, 9)(dsc_modulus); - component dsc_commitment_hasher = Poseidon(10); - dsc_commitment_hasher.inputs[0] <== dsc_secret; - for (var i = 0; i < 9; i++) { - dsc_commitment_hasher.inputs[i + 1] <== split_modulus[i]; - } - signal output blinded_dsc_commitment <== dsc_commitment_hasher.out; - - // Verify passport validity - component PV = PASSPORT_VERIFIER_RSA_65537_SHA256(n, k, max_datahashes_bytes); - PV.mrz <== mrz; - PV.dg1_hash_offset <== dg1_hash_offset; - PV.dataHashes <== dataHashes; - PV.datahashes_padded_length <== datahashes_padded_length; - PV.eContentBytes <== eContent; - PV.dsc_modulus <== dsc_modulus; - PV.signature <== signature; - - // Generate the leaf - component leafHasher = LeafHasherLightWithSigAlg(k); - leafHasher.sigAlg <== signatureAlgorithm; - leafHasher.in <== dsc_modulus; - signal leaf <== leafHasher.out; - - // Generate the commitment - signal output commitment <== ComputeCommitment()(secret, attestation_id, leaf, mrz); -} - -// We hardcode 1 here for sha256WithRSAEncryption_65537 -component main { public [ attestation_id ] } = REGISTER_RSA_65537_SHA256(64, 32, 320, 16, 1); diff --git a/circuits/circuits/register/register_rsapss_65537_sha256.circom b/circuits/circuits/register/register_rsapss_65537_sha256.circom deleted file mode 100644 index e3400da1..00000000 --- a/circuits/circuits/register/register_rsapss_65537_sha256.circom +++ /dev/null @@ -1,54 +0,0 @@ -pragma circom 2.1.5; - -include "circomlib/circuits/poseidon.circom"; -include "../verifier/passport_verifier_rsapss_65537_sha256.circom"; -include "../utils/splitSignalsToWords.circom"; -include "../utils/leafHasherLight.circom"; -include "../utils/computeCommitment.circom"; - -template REGISTER_RSAPSS_65537_SHA256(n, k, max_datahashes_bytes, nLevels, signatureAlgorithm) { - signal input secret; - - signal input mrz[93]; - signal input dg1_hash_offset; - signal input dataHashes[max_datahashes_bytes]; - signal input datahashes_padded_length; - signal input eContent[104]; - signal input signature[k]; - signal input dsc_modulus[k]; - signal input dsc_secret; - signal input attestation_id; - - signal split_signature[9] <== SplitSignalsToWords(n, k, 230, 9)(signature); - signal output nullifier <== Poseidon(9)(split_signature); - - signal split_modulus[9] <== SplitSignalsToWords(n, k, 230, 9)(dsc_modulus); - component dsc_commitment_hasher = Poseidon(10); - dsc_commitment_hasher.inputs[0] <== dsc_secret; - for (var i = 0; i < 9; i++) { - dsc_commitment_hasher.inputs[i + 1] <== split_modulus[i]; - } - signal output blinded_dsc_commitment <== dsc_commitment_hasher.out; - - // Verify passport validity - component PV = PASSPORT_VERIFIER_RSAPSS_65537_SHA256(n, k, max_datahashes_bytes); - PV.mrz <== mrz; - PV.dg1_hash_offset <== dg1_hash_offset; - PV.dataHashes <== dataHashes; - PV.datahashes_padded_length <== datahashes_padded_length; - PV.eContentBytes <== eContent; - PV.dsc_modulus <== dsc_modulus; - PV.signature <== signature; - - // Generate the leaf - component leafHasher = LeafHasherLightWithSigAlg(k); - leafHasher.sigAlg <== signatureAlgorithm; - leafHasher.in <== dsc_modulus; - signal leaf <== leafHasher.out; - - // Generate the commitment - signal output commitment <== ComputeCommitment()(secret, attestation_id, leaf, mrz); -} - -// We hardcode 4 here for sha256WithRSASSAPSS_65537 -component main { public [ attestation_id ] } = REGISTER_RSAPSS_65537_SHA256(64, 32, 320, 16, 4); diff --git a/circuits/circuits/tests/dsc/dsc_sha1_rsa_2048.circom b/circuits/circuits/tests/dsc/dsc_sha1_rsa_2048.circom deleted file mode 100644 index c588f208..00000000 --- a/circuits/circuits/tests/dsc/dsc_sha1_rsa_2048.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.6; - -include "../../dsc/dsc_rsa_65537_sha1.circom"; - -component main { public [ merkle_root ] } = DSC_RSA_65537_SHA1(960, 64, 32, 64, 32, 256, 12, 3); \ No newline at end of file diff --git a/circuits/circuits/tests/dsc/dsc_sha1_rsa_4096.circom b/circuits/circuits/tests/dsc/dsc_sha1_rsa_4096.circom deleted file mode 100644 index 4686a551..00000000 --- a/circuits/circuits/tests/dsc/dsc_sha1_rsa_4096.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.6; - -include "../../dsc/dsc_rsa_65537_sha1.circom"; - -component main { public [ merkle_root ] } = DSC_RSA_65537_SHA1(1664, 64, 32, 120, 35, 256, 12); \ No newline at end of file diff --git a/circuits/circuits/tests/dsc/dsc_sha256_rsa_2048.circom b/circuits/circuits/tests/dsc/dsc_sha256_rsa_2048.circom deleted file mode 100644 index b914e90b..00000000 --- a/circuits/circuits/tests/dsc/dsc_sha256_rsa_2048.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.6; - -include "../../dsc/dsc_rsa_65537_sha256.circom"; - -component main { public [ merkle_root ] } = DSC_RSA_65537_SHA256(960, 64, 32, 64, 32, 256, 12,1); \ No newline at end of file diff --git a/circuits/circuits/tests/dsc/dsc_sha256_rsa_4096.circom b/circuits/circuits/tests/dsc/dsc_sha256_rsa_4096.circom deleted file mode 100644 index a092b77e..00000000 --- a/circuits/circuits/tests/dsc/dsc_sha256_rsa_4096.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.6; - -include "../../dsc/dsc_rsa_65537_sha256.circom"; - -component main { public [ merkle_root ] } = DSC_RSA_65537_SHA256(1664, 64, 32, 120, 35, 256, 12); \ No newline at end of file diff --git a/circuits/circuits/tests/dsc/dsc_sha256_rsapss_2048.circom b/circuits/circuits/tests/dsc/dsc_sha256_rsapss_2048.circom deleted file mode 100644 index e7b85cad..00000000 --- a/circuits/circuits/tests/dsc/dsc_sha256_rsapss_2048.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.6; - -include "../../dsc/dsc_rsapss_65537_sha256.circom"; - -component main { public [ merkle_root ] } = DSC_RSAPSS_65537_SHA256(960, 64, 32, 64, 32, 2048, 256, 12,4); \ No newline at end of file diff --git a/circuits/circuits/tests/dsc/dsc_sha256_rsapss_4096.circom b/circuits/circuits/tests/dsc/dsc_sha256_rsapss_4096.circom deleted file mode 100644 index f38a18d5..00000000 --- a/circuits/circuits/tests/dsc/dsc_sha256_rsapss_4096.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.6; - -include "../../dsc/dsc_rsapss_65537_sha256.circom"; - -component main { public [ merkle_root ] } = DSC_RSAPSS_65537_SHA256(1664, 64, 32, 120, 35, 4096, 256, 12); \ No newline at end of file diff --git a/circuits/circuits/tests/mgf1Sha256/Mgf1Sha256_1ByteMask_tester.circom b/circuits/circuits/tests/mgf1Sha256/Mgf1Sha256_1ByteMask_tester.circom deleted file mode 100644 index 5c433942..00000000 --- a/circuits/circuits/tests/mgf1Sha256/Mgf1Sha256_1ByteMask_tester.circom +++ /dev/null @@ -1,31 +0,0 @@ -pragma circom 2.1.5; - -include "../../utils/Mgf1Sha256.circom"; -include "../../../node_modules/circomlib/circuits/bitify.circom"; - - -template Mgf1Sha256_1ByteMask_tester(seed_len_bytes, mask_len_bytes) { - - signal input seed; - signal input expected_mask_output[mask_len_bytes * 8]; - signal output mask[mask_len_bytes * 8]; - - component mgf1_sha256 = Mgf1Sha256(seed_len_bytes, mask_len_bytes); - component num2Bits = Num2Bits(seed_len_bytes * 8); - num2Bits.in <== seed; - - for (var i=0; i < seed_len_bytes * 8; i++) { - mgf1_sha256.seed[i] <== num2Bits.out[i]; - } - - for (var i=0; i < mask_len_bytes * 8; i++) { - mask[i] <== mgf1_sha256.out[i]; - } - - for (var i=0; i < mask_len_bytes * 8; i++) { - mgf1_sha256.out[i] === expected_mask_output[i]; - } - -} - -component main = Mgf1Sha256_1ByteMask_tester(4, 1); diff --git a/circuits/circuits/tests/mgf1Sha256/Mgf1Sha256_32Bytes_tester.circom b/circuits/circuits/tests/mgf1Sha256/Mgf1Sha256_32Bytes_tester.circom deleted file mode 100644 index dfb5b033..00000000 --- a/circuits/circuits/tests/mgf1Sha256/Mgf1Sha256_32Bytes_tester.circom +++ /dev/null @@ -1,29 +0,0 @@ -pragma circom 2.1.5; - -include "../../utils/Mgf1Sha256.circom"; -include "../../../node_modules/circomlib/circuits/bitify.circom"; - - -template Mgf1Sha256_32Bytes_tester(seed_len_bytes, mask_len_bytes) { - - signal input seed[seed_len_bytes * 8]; - signal input expected_mask_output[mask_len_bytes * 8]; - signal output mask[mask_len_bytes * 8]; - - component mgf1_sha256 = Mgf1Sha256(seed_len_bytes, mask_len_bytes); - - for (var i=0; i < seed_len_bytes * 8; i++) { - mgf1_sha256.seed[i] <== seed[i]; - } - - for (var i=0; i < mask_len_bytes * 8; i++) { - mask[i] <== mgf1_sha256.out[i]; - } - - for (var i=0; i < mask_len_bytes * 8; i++) { - mgf1_sha256.out[i] === expected_mask_output[i]; - } - -} - -component main = Mgf1Sha256_32Bytes_tester(32, 32); diff --git a/circuits/circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom b/circuits/circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom deleted file mode 100644 index 851b5630..00000000 --- a/circuits/circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom +++ /dev/null @@ -1,33 +0,0 @@ -pragma circom 2.1.5; - -include "../../utils/Mgf1Sha256.circom"; -include "../../../node_modules/circomlib/circuits/bitify.circom"; - - -template Mgf1Sha256_tester(seed_len_bytes, mask_len_bytes) { - - signal input seed; - signal input expected_mask_output[mask_len_bytes * 8]; - signal output mask[mask_len_bytes * 8]; - - component mgf1_sha256 = Mgf1Sha256(seed_len_bytes, mask_len_bytes); - component num2Bits = Num2Bits(seed_len_bytes * 8); - num2Bits.in <== seed; - - for (var i=0; i < seed_len_bytes * 8; i++) { - mgf1_sha256.seed[i] <== num2Bits.out[i]; - } - - for (var i=0; i < mask_len_bytes * 8; i++) { - mask[i] <== mgf1_sha256.out[i]; - } - - for (var i=0; i < mask_len_bytes * 8; i++) { - mgf1_sha256.out[i] === expected_mask_output[i]; - } - -} - -// component main { public [ seed ] } = Mgf1_sha256(4,32); - -component main = Mgf1Sha256_tester(4, 32); diff --git a/circuits/circuits/tests/utils/isOlderThan_tester.circom b/circuits/circuits/tests/utils/isOlderThan_tester.circom index 26629d22..f33b757b 100644 --- a/circuits/circuits/tests/utils/isOlderThan_tester.circom +++ b/circuits/circuits/tests/utils/isOlderThan_tester.circom @@ -1,6 +1,6 @@ pragma circom 2.1.6; -include "../../utils/isOlderThan.circom"; +include "../../utils/passport/date/isOlderThan.circom"; template isOlderThan_tester() { diff --git a/circuits/circuits/tests/utils/isValid_tester.circom b/circuits/circuits/tests/utils/isValid_tester.circom index cbed326b..e25d0a55 100644 --- a/circuits/circuits/tests/utils/isValid_tester.circom +++ b/circuits/circuits/tests/utils/isValid_tester.circom @@ -1,6 +1,6 @@ pragma circom 2.1.6; -include "../../utils/isValid.circom"; +include "../../utils/passport/date/isValid.circom"; template IsValid_tester() { diff --git a/circuits/circuits/tests/utils/leafHasherLight_tester.circom b/circuits/circuits/tests/utils/leafHasherLight_tester.circom deleted file mode 100644 index e5fbe079..00000000 --- a/circuits/circuits/tests/utils/leafHasherLight_tester.circom +++ /dev/null @@ -1,5 +0,0 @@ -pragma circom 2.1.6; - -include "../../utils/leafHasherLight.circom"; - -component main = LeafHasherLightWithSigAlg(32); \ No newline at end of file diff --git a/circuits/circuits/tests/utils/leafHasher_tester.circom b/circuits/circuits/tests/utils/leafHasher_tester.circom new file mode 100644 index 00000000..cb281547 --- /dev/null +++ b/circuits/circuits/tests/utils/leafHasher_tester.circom @@ -0,0 +1,6 @@ +pragma circom 2.1.6; + +include "../../utils/passport/customHashers.circom"; + +// component main = LeafHasher(12); +component main = CustomHasher(32); diff --git a/circuits/circuits/tests/utils/rsa_verifier.circom b/circuits/circuits/tests/utils/rsa_verifier.circom deleted file mode 100644 index 75039636..00000000 --- a/circuits/circuits/tests/utils/rsa_verifier.circom +++ /dev/null @@ -1,3 +0,0 @@ -include "@zk-email/circuits/lib/rsa.circom"; - -component main = RSAVerifier65537(121, 17); \ No newline at end of file diff --git a/circuits/circuits/tests/utils/rsapss_verifier.circom b/circuits/circuits/tests/utils/rsapss_verifier.circom deleted file mode 100644 index f89b8cf4..00000000 --- a/circuits/circuits/tests/utils/rsapss_verifier.circom +++ /dev/null @@ -1,20 +0,0 @@ -include "../../utils/RSASSAPSS_padded.circom"; - -template RSAPSSVerifier(n,k,max_bytes) { - signal input signature[k]; - signal input modulus[k]; - signal input raw_message[max_bytes]; - signal input raw_message_padded_bytes; - - component rsaDecode = RSASSAPSS_Decode(n, k); - rsaDecode.signature <== signature; - rsaDecode.modulus <== modulus; - var emLen = div_ceil(n * k, 8); - signal encodedMessage[emLen] <== rsaDecode.eM; - - component rsaVerify = RSASSAPSSVerify_SHA256(n * k, max_bytes); - rsaVerify.eM <== encodedMessage; - rsaVerify.message <== raw_message; - rsaVerify.messagePaddedLen <== raw_message_padded_bytes; -} -component main = RSAPSSVerifier(64, 32, 960); \ No newline at end of file diff --git a/circuits/circuits/utils/Mgf1Sha256.circom b/circuits/circuits/utils/Mgf1Sha256.circom deleted file mode 100644 index 19a71d6a..00000000 --- a/circuits/circuits/utils/Mgf1Sha256.circom +++ /dev/null @@ -1,50 +0,0 @@ -pragma circom 2.1.5; -include "circomlib/circuits/sha256/sha256.circom"; -include "circomlib/circuits/bitify.circom"; - -template Mgf1Sha256(seedLen, maskLen) { //in bytes - var seedLenBits = seedLen * 8; - var maskLenBits = maskLen * 8; - var hashLen = 32; //output len of sha function in bytes - var hashLenBits = hashLen * 8;//output len of sha function in bits - - signal input seed[seedLenBits]; //each represents a bit - signal output out[maskLenBits]; - - assert(maskLen <= 0xffffffff * hashLen ); - var iterations = (maskLen \ hashLen) + 1; //adding 1, in-case maskLen \ hashLen is 0 - - component sha256[iterations]; - component num2Bits[iterations]; - - for (var i = 0; i < iterations; i++) { - sha256[i] = Sha256(seedLenBits + 32); //32 bits for counter - num2Bits[i] = Num2Bits(32); - } - - var concated[seedLenBits + 32]; //seed + 32 bits(4 Bytes) for counter - signal hashed[hashLenBits * (iterations)]; - - for (var i = 0; i < seedLenBits; i++) { - concated[i] = seed[i]; - } - - for (var i = 0; i < iterations; i++) { - num2Bits[i].in <== i; //convert counter to bits - - for (var j = 0; j < 32; j++) { - //concat seed and counter - concated[seedLenBits + j] = num2Bits[i].out[31-j]; - } - - sha256[i].in <== concated; - - for (var j = 0; j < hashLenBits; j++) { - hashed[i * hashLenBits + j] <== sha256[i].out[j]; - } - } - - for (var i = 0; i < maskLenBits; i++) { - out[i] <== hashed[i]; - } -} diff --git a/circuits/circuits/utils/RSASSAPSS.circom b/circuits/circuits/utils/RSASSAPSS.circom deleted file mode 100644 index 5e7a4c53..00000000 --- a/circuits/circuits/utils/RSASSAPSS.circom +++ /dev/null @@ -1,193 +0,0 @@ -pragma circom 2.1.5; - -include "@zk-email/circuits/lib/rsa.circom"; -include "@zk-email/circuits/lib/fp.circom"; -include "@zk-email/circuits/lib/bigint-func.circom"; -include "circomlib/circuits/bitify.circom"; -include "circomlib/circuits/sha256/sha256.circom"; -include "./Mgf1Sha256.circom"; -include "./xor.circom"; - -/// @notice Returns the encoded message in 8bit chunks. -/// @param n Number of bits per chunk the modulus is split into. -/// @param k Number of chunks the modulus is split into. -template RSASSAPSS_Decode(n, k) { - signal input signature[k]; - signal input modulus[k]; - // signal output eM[k]; - signal encoded[k]; - signal eMsgInBits[n*k]; - var emLen = div_ceil(n*k, 8); - signal output eM[emLen]; //8 bit words - - component bigPow = FpPow65537Mod(n, k); - for (var i = 0; i < k; i++) { - bigPow.base[i] <== signature[i]; - bigPow.modulus[i] <== modulus[i]; - } - - encoded <== bigPow.out; - - component num2Bits[k]; - for (var i = 0; i < k; i++) { - num2Bits[i] = Num2Bits(n); - num2Bits[i].in <== encoded[k-1-i]; - - for (var j = 0; j < n; j++) { - eMsgInBits[i * n + j] <== num2Bits[i].out[n-j-1]; - } - } - - component bits2Num[(n*k)\8]; - for (var i = 0; i < (n*k)\8; i++) { - bits2Num[i] = Bits2Num(8); - for (var j = 0; j < 8; j++) { - bits2Num[i].in[7-j] <== eMsgInBits[i*8 + j]; - } - eM[(n*k)\8 - i -1] <== bits2Num[i].out; - } -} - -/// @param emBits Length of the encoded message in bits. -/// @param messageLen Length of the message in bytes. -/// @param n Number of bits per chunk the modulus is split into. -/// @param k Number of chunks the modulus is split into. -template RSASSAPSSVerify_SHA256(emBits, messageLen) { - var emLen = div_ceil(emBits, 8); - signal input eM[emLen]; - signal input message[messageLen]; - signal mHash[256]; - var hLen = 32; - var sLen = 32; - var hLenBits = 256; //sha256 - var sLenBits = 256; //sha256 - var emLenBits = emLen * 8; - - signal messageBits[messageLen*8]; - component num2BitsMessage[messageLen]; - for (var i = 0; i < messageLen; i++) { - num2BitsMessage[i] = Num2Bits(8); - num2BitsMessage[i].in <== message[i]; - for (var j = 0; j < 8; j++) { - messageBits[i*8 +j] <== num2BitsMessage[i].out[7-j]; - } - } - - //mHash - component sha256 = Sha256( messageLen* 8); - sha256.in <== messageBits; - for (var i = 0; i < 256; i++) { - mHash[i] <== sha256.out[i]; - } - - //If emLen < hLen + sLen + 2, output "inconsistent" and stop. - assert(emLen >= 32 + 32 +2); - - //should end with 0xBC (188 in decimal) - assert(eM[0] == 188); //inconsistent - - signal eMsgInBits[emLen * 8]; - signal maskedDB[(emLen - hLen - 1) * 8]; - signal hash[hLen * 8]; - var dbMaskLen = emLen - hLen - 1; - signal dbMask[dbMaskLen * 8]; - signal DB[dbMaskLen * 8]; - signal salt[hLen * 8]; - - //split eM into bits - component num2Bits[emLen]; - for (var i = 0; i < emLen; i++) { - num2Bits[i] = Num2Bits(8); - num2Bits[i].in <== eM[emLen-1-i]; - - for (var j = 0; j < 8; j++) { - eMsgInBits[i * 8 + j] <== num2Bits[i].out[8-j-1]; - } - } - - //extract maskedDB. leftmost emLen - hLen - 1 octets of EM - for (var i=0; i< (emLen - hLen -1) * 8; i++) { - maskedDB[i] <== eMsgInBits[i]; - } - - //Ref: https://github.com/directdemocracy-vote/app/blob/d0590b5515e749fa72fc50f05062273eb2465da1/httpdocs/app/js/rsa-blind.js#L183 - signal mask <== 0xff00 >> (emLenBits / 8 - emBits) & 0xff; - signal maskBits[8]; - component num2BitsMask = Num2Bits(8); - num2BitsMask.in <== mask; - for (var i = 0; i < 8; i++) { - maskBits[i] <== num2BitsMask.out[7-i]; - } - for (var i=0; i<8; i++) { - assert(maskBits[i] & maskedDB[i] == 0); - } - - //extract hash - for (var i=0; i= 32 + 32 +2); - - //should end with 0xBC (188 in decimal) - assert(eM[0] == 188); //inconsistent - - signal eMsgInBits[emLen * 8]; - signal maskedDB[(emLen - hLen - 1) * 8]; - signal hash[hLen * 8]; - var dbMaskLen = emLen - hLen - 1; - signal dbMask[dbMaskLen * 8]; - signal DB[dbMaskLen * 8]; - signal salt[hLen * 8]; - - //split eM into bits - component num2Bits[emLen]; - for (var i = 0; i < emLen; i++) { - num2Bits[i] = Num2Bits(8); - num2Bits[i].in <== eM[emLen-1-i]; - - for (var j = 0; j < 8; j++) { - eMsgInBits[i * 8 + j] <== num2Bits[i].out[8-j-1]; - } - } - - //extract maskedDB. leftmost emLen - hLen - 1 octets of EM - for (var i=0; i< (emLen - hLen -1) * 8; i++) { - maskedDB[i] <== eMsgInBits[i]; - } - - //Ref: https://github.com/directdemocracy-vote/app/blob/d0590b5515e749fa72fc50f05062273eb2465da1/httpdocs/app/js/rsa-blind.js#L183 - signal mask <== 0xff00 >> (emLenBits / 8 - emBits) & 0xff; - signal maskBits[8]; - component num2BitsMask = Num2Bits(8); - num2BitsMask.in <== mask; - for (var i = 0; i < 8; i++) { - maskBits[i] <== num2BitsMask.out[7-i]; - } - for (var i=0; i<8; i++) { - assert(maskBits[i] & maskedDB[i] == 0); - } - - //extract hash - for (var i=0; i= 0 -template ModSubThree(n) { - assert(n + 2 <= 253); - signal input a; - signal input b; - signal input c; - assert(a - b - c + (1 << n) >= 0); - signal output out; - signal output borrow; - signal b_plus_c; - b_plus_c <== b + c; - component lt = LessThan(n + 1); - lt.in[0] <== a; - lt.in[1] <== b_plus_c; - borrow <== lt.out; - out <== borrow * (1 << n) + a - b_plus_c; -} - -template ModSumThree(n) { - assert(n + 2 <= 253); - signal input a; - signal input b; - signal input c; - signal output sum; - signal output carry; - - component n2b = Num2Bits(n + 2); - n2b.in <== a + b + c; - carry <== n2b.out[n] + 2 * n2b.out[n + 1]; - sum <== a + b + c - carry * (1 << n); -} - -template ModSumFour(n) { - assert(n + 2 <= 253); - signal input a; - signal input b; - signal input c; - signal input d; - signal output sum; - signal output carry; - - component n2b = Num2Bits(n + 2); - n2b.in <== a + b + c + d; - carry <== n2b.out[n] + 2 * n2b.out[n + 1]; - sum <== a + b + c + d - carry * (1 << n); -} - -// product mod 2**n with carry -template ModProd(n) { - assert(n <= 126); - signal input a; - signal input b; - signal output prod; - signal output carry; - - component n2b = Num2Bits(2 * n); - n2b.in <== a * b; - - component b2n1 = Bits2Num(n); - component b2n2 = Bits2Num(n); - var i; - for (i = 0; i < n; i++) { - b2n1.in[i] <== n2b.out[i]; - b2n2.in[i] <== n2b.out[i + n]; - } - prod <== b2n1.out; - carry <== b2n2.out; -} - -// split a n + m bit input into two outputs -template Split(n, m) { - assert(n <= 126); - signal input in; - signal output small; - signal output big; - - small <-- in % (1 << n); - big <-- in \ (1 << n); - - component n2b_small = Num2Bits(n); - n2b_small.in <== small; - component n2b_big = Num2Bits(m); - n2b_big.in <== big; - - in === small + big * (1 << n); -} - -// split a n + m + k bit input into three outputs -template SplitThree(n, m, k) { - assert(n <= 126); - signal input in; - signal output small; - signal output medium; - signal output big; - - small <-- in % (1 << n); - medium <-- (in \ (1 << n)) % (1 << m); - big <-- in \ (1 << n + m); - - component n2b_small = Num2Bits(n); - n2b_small.in <== small; - component n2b_medium = Num2Bits(m); - n2b_medium.in <== medium; - component n2b_big = Num2Bits(k); - n2b_big.in <== big; - - in === small + medium * (1 << n) + big * (1 << n + m); -} - -// a[i], b[i] in 0... 2**n-1 -// represent a = a[0] + a[1] * 2**n + .. + a[k - 1] * 2**(n * k) -template BigAdd(n, k) { - assert(n <= 252); - signal input a[k]; - signal input b[k]; - signal output out[k + 1]; - - component unit0 = ModSum(n); - unit0.a <== a[0]; - unit0.b <== b[0]; - out[0] <== unit0.sum; - - component unit[k - 1]; - for (var i = 1; i < k; i++) { - unit[i - 1] = ModSumThree(n); - unit[i - 1].a <== a[i]; - unit[i - 1].b <== b[i]; - if (i == 1) { - unit[i - 1].c <== unit0.carry; - } else { - unit[i - 1].c <== unit[i - 2].carry; - } - out[i] <== unit[i - 1].sum; - } - out[k] <== unit[k - 2].carry; -} - -/* -Polynomial Multiplication -Inputs: - - a = a[0] + a[1] * X + ... + a[k-1] * X^{k-1} - - b = b[0] + b[1] * X + ... + b[k-1] * X^{k-1} -Output: - - out = out[0] + out[1] * X + ... + out[2 * k - 2] * X^{2*k - 2} - - out = a * b as polynomials in X -Notes: - - Optimization due to xJsnark: - -- witness is calculated by normal polynomial multiplication - -- out is contrained by evaluating out(X) === a(X) * b(X) at X = 0, ..., 2*k - 2 - - If a[i], b[j] have absolute value < B, then out[i] has absolute value < k * B^2 -m_out is the expected max number of bits in the output registers -*/ -template BigMultShortLong(n, k, m_out) { - assert(n <= 126); - signal input a[k]; - signal input b[k]; - signal output out[2 * k - 1]; - - var prod_val[2 * k - 1]; - for (var i = 0; i < 2 * k - 1; i++) { - prod_val[i] = 0; - if (i < k) { - for (var a_idx = 0; a_idx <= i; a_idx++) { - prod_val[i] = prod_val[i] + a[a_idx] * b[i - a_idx]; - } - } else { - for (var a_idx = i - k + 1; a_idx < k; a_idx++) { - prod_val[i] = prod_val[i] + a[a_idx] * b[i - a_idx]; - } - } - out[i] <-- prod_val[i]; - } - - var k2 = 2 * k - 1; - var pow[k2][k2]; // we cache the exponent values because it makes a big difference in witness generation time - for(var i = 0; i= 0 +// template ModSubThree(n) { +// assert(n + 2 <= 253); +// signal input a; +// signal input b; +// signal input c; +// assert(a - b - c + (1 << n) >= 0); +// signal output out; +// signal output borrow; +// signal b_plus_c; +// b_plus_c <== b + c; +// component lt = LessThan(n + 1); +// lt.in[0] <== a; +// lt.in[1] <== b_plus_c; +// borrow <== lt.out; +// out <== borrow * (1 << n) + a - b_plus_c; +// } + +// template ModSumThree(n) { +// assert(n + 2 <= 253); +// signal input a; +// signal input b; +// signal input c; +// signal output sum; +// signal output carry; + +// component n2b = Num2Bits(n + 2); +// n2b.in <== a + b + c; +// carry <== n2b.out[n] + 2 * n2b.out[n + 1]; +// sum <== a + b + c - carry * (1 << n); +// } + +// template ModSumFour(n) { +// assert(n + 2 <= 253); +// signal input a; +// signal input b; +// signal input c; +// signal input d; +// signal output sum; +// signal output carry; + +// component n2b = Num2Bits(n + 2); +// n2b.in <== a + b + c + d; +// carry <== n2b.out[n] + 2 * n2b.out[n + 1]; +// sum <== a + b + c + d - carry * (1 << n); +// } + +// // product mod 2**n with carry +// template ModProd(n) { +// assert(n <= 126); +// signal input a; +// signal input b; +// signal output prod; +// signal output carry; + +// component n2b = Num2Bits(2 * n); +// n2b.in <== a * b; + +// component b2n1 = Bits2Num(n); +// component b2n2 = Bits2Num(n); +// var i; +// for (i = 0; i < n; i++) { +// b2n1.in[i] <== n2b.out[i]; +// b2n2.in[i] <== n2b.out[i + n]; +// } +// prod <== b2n1.out; +// carry <== b2n2.out; +// } + +// // split a n + m bit input into two outputs +// template Split(n, m) { +// assert(n <= 126); +// signal input in; +// signal output small; +// signal output big; + +// small <-- in % (1 << n); +// big <-- in \ (1 << n); + +// component n2b_small = Num2Bits(n); +// n2b_small.in <== small; +// component n2b_big = Num2Bits(m); +// n2b_big.in <== big; + +// in === small + big * (1 << n); +// } + +// // split a n + m + k bit input into three outputs +// template SplitThree(n, m, k) { +// assert(n <= 126); +// signal input in; +// signal output small; +// signal output medium; +// signal output big; + +// small <-- in % (1 << n); +// medium <-- (in \ (1 << n)) % (1 << m); +// big <-- in \ (1 << n + m); + +// component n2b_small = Num2Bits(n); +// n2b_small.in <== small; +// component n2b_medium = Num2Bits(m); +// n2b_medium.in <== medium; +// component n2b_big = Num2Bits(k); +// n2b_big.in <== big; + +// in === small + medium * (1 << n) + big * (1 << n + m); +// } + +// // a[i], b[i] in 0... 2**n-1 +// // represent a = a[0] + a[1] * 2**n + .. + a[k - 1] * 2**(n * k) +// template BigAdd(n, k) { +// assert(n <= 252); +// signal input a[k]; +// signal input b[k]; +// signal output out[k + 1]; + +// component unit0 = ModSum(n); +// unit0.a <== a[0]; +// unit0.b <== b[0]; +// out[0] <== unit0.sum; + +// component unit[k - 1]; +// for (var i = 1; i < k; i++) { +// unit[i - 1] = ModSumThree(n); +// unit[i - 1].a <== a[i]; +// unit[i - 1].b <== b[i]; +// if (i == 1) { +// unit[i - 1].c <== unit0.carry; +// } else { +// unit[i - 1].c <== unit[i - 2].carry; +// } +// out[i] <== unit[i - 1].sum; +// } +// out[k] <== unit[k - 2].carry; +// } + +// /* +// Polynomial Multiplication +// Inputs: +// - a = a[0] + a[1] * X + ... + a[k-1] * X^{k-1} +// - b = b[0] + b[1] * X + ... + b[k-1] * X^{k-1} +// Output: +// - out = out[0] + out[1] * X + ... + out[2 * k - 2] * X^{2*k - 2} +// - out = a * b as polynomials in X +// Notes: +// - Optimization due to xJsnark: +// -- witness is calculated by normal polynomial multiplication +// -- out is contrained by evaluating out(X) === a(X) * b(X) at X = 0, ..., 2*k - 2 +// - If a[i], b[j] have absolute value < B, then out[i] has absolute value < k * B^2 +// m_out is the expected max number of bits in the output registers +// */ +// template BigMultShortLong(n, k, m_out) { +// assert(n <= 126); +// signal input a[k]; +// signal input b[k]; +// signal output out[2 * k - 1]; + +// var prod_val[2 * k - 1]; +// for (var i = 0; i < 2 * k - 1; i++) { +// prod_val[i] = 0; +// if (i < k) { +// for (var a_idx = 0; a_idx <= i; a_idx++) { +// prod_val[i] = prod_val[i] + a[a_idx] * b[i - a_idx]; +// } +// } else { +// for (var a_idx = i - k + 1; a_idx < k; a_idx++) { +// prod_val[i] = prod_val[i] + a[a_idx] * b[i - a_idx]; +// } +// } +// out[i] <-- prod_val[i]; +// } + +// var k2 = 2 * k - 1; +// var pow[k2][k2]; // we cache the exponent values because it makes a big difference in witness generation time +// for(var i = 0; i 1) { - var sumAndCarry[2] = SplitFn(split[0][1] + split[1][0], n, n); - out[1] <-- sumAndCarry[0]; - carry[1] = sumAndCarry[1]; - } - if (k > 2) { - for (var i = 2; i < k; i++) { - var sumAndCarry[2] = SplitFn(split[i][0] + split[i-1][1] + split[i-2][2] + carry[i-1], n, n); - out[i] <-- sumAndCarry[0]; - carry[i] = sumAndCarry[1]; - } - out[k] <-- split[k-1][1] + split[k-2][2] + carry[k-1]; - } - - component outRangeChecks[k+1]; - for (var i = 0; i < k+1; i++) { - outRangeChecks[i] = Num2Bits(n); - outRangeChecks[i].in <== out[i]; - } - - signal runningCarry[k]; - component runningCarryRangeChecks[k]; - runningCarry[0] <-- (in[0] - out[0]) / (1 << n); - runningCarryRangeChecks[0] = Num2Bits(n + log_ceil_ecdsa(k)); - runningCarryRangeChecks[0].in <== runningCarry[0]; - runningCarry[0] * (1 << n) === in[0] - out[0]; - for (var i = 1; i < k; i++) { - runningCarry[i] <-- (in[i] - out[i] + runningCarry[i-1]) / (1 << n); - runningCarryRangeChecks[i] = Num2Bits(n + log_ceil_ecdsa(k)); - runningCarryRangeChecks[i].in <== runningCarry[i]; - runningCarry[i] * (1 << n) === in[i] - out[i] + runningCarry[i-1]; - } - runningCarry[k-1] === out[k]; -} - -template BigMult(n, k) { - signal input a[k]; - signal input b[k]; - signal output out[2 * k]; - - var LOGK = log_ceil_ecdsa(k); - component mult = BigMultShortLong(n, k, 2*n + LOGK); - for (var i = 0; i < k; i++) { - mult.a[i] <== a[i]; - mult.b[i] <== b[i]; - } - - // no carry is possible in the highest order register - component longshort = LongToShortNoEndCarry(n, 2 * k - 1); - for (var i = 0; i < 2 * k - 1; i++) { - longshort.in[i] <== mult.out[i]; - } - for (var i = 0; i < 2 * k; i++) { - out[i] <== longshort.out[i]; - } -} +// var prod_val[ka + kb - 1]; +// for (var i = 0; i < ka + kb - 1; i++) { +// prod_val[i] = 0; +// } +// for (var i = 0; i < ka; i++) { +// for (var j = 0; j < kb; j++) { +// prod_val[i + j] = prod_val[i + j] + a[i] * b[j]; +// } +// } +// for (var i = 0; i < ka + kb - 1; i++) { +// out[i] <-- prod_val[i]; +// } + +// var k2 = ka + kb - 1; +// var pow[k2][k2]; +// for(var i = 0; i 1) { +// var sumAndCarry[2] = SplitFn(split[0][1] + split[1][0], n, n); +// out[1] <-- sumAndCarry[0]; +// carry[1] = sumAndCarry[1]; +// } +// if (k > 2) { +// for (var i = 2; i < k; i++) { +// var sumAndCarry[2] = SplitFn(split[i][0] + split[i-1][1] + split[i-2][2] + carry[i-1], n, n); +// out[i] <-- sumAndCarry[0]; +// carry[i] = sumAndCarry[1]; +// } +// out[k] <-- split[k-1][1] + split[k-2][2] + carry[k-1]; +// } + +// component outRangeChecks[k+1]; +// for (var i = 0; i < k+1; i++) { +// outRangeChecks[i] = Num2Bits(n); +// outRangeChecks[i].in <== out[i]; +// } + +// signal runningCarry[k]; +// component runningCarryRangeChecks[k]; +// runningCarry[0] <-- (in[0] - out[0]) / (1 << n); +// runningCarryRangeChecks[0] = Num2Bits(n + log_ceil_ecdsa(k)); +// runningCarryRangeChecks[0].in <== runningCarry[0]; +// runningCarry[0] * (1 << n) === in[0] - out[0]; +// for (var i = 1; i < k; i++) { +// runningCarry[i] <-- (in[i] - out[i] + runningCarry[i-1]) / (1 << n); +// runningCarryRangeChecks[i] = Num2Bits(n + log_ceil_ecdsa(k)); +// runningCarryRangeChecks[i].in <== runningCarry[i]; +// runningCarry[i] * (1 << n) === in[i] - out[i] + runningCarry[i-1]; +// } +// runningCarry[k-1] === out[k]; +// } + +// template BigMult(n, k) { +// signal input a[k]; +// signal input b[k]; +// signal output out[2 * k]; + +// var LOGK = log_ceil_ecdsa(k); +// component mult = BigMultShortLong(n, k, 2*n + LOGK); +// for (var i = 0; i < k; i++) { +// mult.a[i] <== a[i]; +// mult.b[i] <== b[i]; +// } + +// // no carry is possible in the highest order register +// component longshort = LongToShortNoEndCarry(n, 2 * k - 1); +// for (var i = 0; i < 2 * k - 1; i++) { +// longshort.in[i] <== mult.out[i]; +// } +// for (var i = 0; i < 2 * k; i++) { +// out[i] <== longshort.out[i]; +// } +// } /* Inputs: @@ -446,302 +446,302 @@ template BigLessThanEcdsa(n, k){ out <== ors[0].out; } -// leading register of b should be non-zero -template BigMod(n, k) { - assert(n <= 126); - signal input a[2 * k]; - signal input b[k]; - - signal output div[k + 1]; - signal output mod[k]; - - var longdiv[2][50] = long_div_ecdsa(n, k, a, b); - for (var i = 0; i < k; i++) { - div[i] <-- longdiv[0][i]; - mod[i] <-- longdiv[1][i]; - } - div[k] <-- longdiv[0][k]; - component div_range_checks[k + 1]; - for (var i = 0; i <= k; i++) { - div_range_checks[i] = Num2Bits(n); - div_range_checks[i].in <== div[i]; - } - component mod_range_checks[k]; - for (var i = 0; i < k; i++) { - mod_range_checks[i] = Num2Bits(n); - mod_range_checks[i].in <== mod[i]; - } - - component mul = BigMult(n, k + 1); - for (var i = 0; i < k; i++) { - mul.a[i] <== div[i]; - mul.b[i] <== b[i]; - } - mul.a[k] <== div[k]; - mul.b[k] <== 0; - - for (var i = 0; i < 2 * k + 2; i++) { - //log(mul.out[i]); - } - - component add = BigAdd(n, 2 * k + 2); - for (var i = 0; i < 2 * k; i++) { - add.a[i] <== mul.out[i]; - if (i < k) { - add.b[i] <== mod[i]; - } else { - add.b[i] <== 0; - } - } - add.a[2 * k] <== mul.out[2 * k]; - add.a[2 * k + 1] <== mul.out[2 * k + 1]; - add.b[2 * k] <== 0; - add.b[2 * k + 1] <== 0; - - for (var i = 0; i < 2 * k + 2; i++) { - //log(add.out[i]); - } - - for (var i = 0; i < 2 * k; i++) { - add.out[i] === a[i]; - } - add.out[2 * k] === 0; - add.out[2 * k + 1] === 0; - - component lt = BigLessThanEcdsa(n, k); - for (var i = 0; i < k; i++) { - lt.a[i] <== mod[i]; - lt.b[i] <== b[i]; - } - lt.out === 1; -} - -// copied from BigMod to allow a to have m registers and use long_div2 -template BigMod2(n, k, m) { - assert(n <= 126); - signal input a[m]; - signal input b[k]; - - signal output div[m - k + 1]; - signal output mod[k]; - - var longdiv[2][50] = long_div2(n, k, m-k, a, b); - for (var i = 0; i < k; i++) { - mod[i] <-- longdiv[1][i]; - } - for (var i = 0; i <= m-k; i++) { - div[i] <-- longdiv[0][i]; - } - component div_range_checks[m - k + 1]; - for (var i = 0; i <= m-k; i++) { - div_range_checks[i] = Num2Bits(n); - div_range_checks[i].in <== div[i]; - } - component mod_range_checks[k]; - for (var i = 0; i < k; i++) { - mod_range_checks[i] = Num2Bits(n); - mod_range_checks[i].in <== mod[i]; - } - - component mul = BigMult(n, m-k + 1); - // this might need to be optimized since b has less registers than div - for (var i = 0; i < k; i++) { - mul.a[i] <== div[i]; - mul.b[i] <== b[i]; - } - for (var i = k; i <= m-k; i++) { - mul.a[i] <== div[i]; - mul.b[i] <== 0; - } - - // mul shouldn't have more registers than a - for (var i = m; i < 2*(m-k)+2; i++) { - mul.out[i] === 0; - } - - component add = BigAdd(n, m); - for (var i = 0; i < m; i++) { - add.a[i] <== mul.out[i]; - if (i < k) { - add.b[i] <== mod[i]; - } else { - add.b[i] <== 0; - } - } - - for (var i = 0; i < m; i++) { - add.out[i] === a[i]; - } - add.out[m] === 0; - - component lt = BigLessThanEcdsa(n, k); - for (var i = 0; i < k; i++) { - lt.a[i] <== mod[i]; - lt.b[i] <== b[i]; - } - lt.out === 1; -} - - - -// a[i], b[i] in 0... 2**n-1 -// represent a = a[0] + a[1] * 2**n + .. + a[k - 1] * 2**(n * k) -// calculates (a+b)%p, where 0<= a,b < p -template BigAddModP(n, k){ - assert(n <= 252); - signal input a[k]; - signal input b[k]; - signal input p[k]; - signal output out[k]; - - component add = BigAdd(n,k); - for (var i = 0; i < k; i++) { - add.a[i] <== a[i]; - add.b[i] <== b[i]; - } - component lt = BigLessThanEcdsa(n, k+1); - for (var i = 0; i < k; i++) { - lt.a[i] <== add.out[i]; - lt.b[i] <== p[i]; - } - lt.a[k] <== add.out[k]; - lt.b[k] <== 0; - - component sub = BigSub(n,k+1); - for (var i = 0; i < k; i++) { - sub.a[i] <== add.out[i]; - sub.b[i] <== (1-lt.out) * p[i]; - } - sub.a[k] <== add.out[k]; - sub.b[k] <== 0; +// // leading register of b should be non-zero +// template BigMod(n, k) { +// assert(n <= 126); +// signal input a[2 * k]; +// signal input b[k]; + +// signal output div[k + 1]; +// signal output mod[k]; + +// var longdiv[2][50] = long_div_ecdsa(n, k, a, b); +// for (var i = 0; i < k; i++) { +// div[i] <-- longdiv[0][i]; +// mod[i] <-- longdiv[1][i]; +// } +// div[k] <-- longdiv[0][k]; +// component div_range_checks[k + 1]; +// for (var i = 0; i <= k; i++) { +// div_range_checks[i] = Num2Bits(n); +// div_range_checks[i].in <== div[i]; +// } +// component mod_range_checks[k]; +// for (var i = 0; i < k; i++) { +// mod_range_checks[i] = Num2Bits(n); +// mod_range_checks[i].in <== mod[i]; +// } + +// component mul = BigMult(n, k + 1); +// for (var i = 0; i < k; i++) { +// mul.a[i] <== div[i]; +// mul.b[i] <== b[i]; +// } +// mul.a[k] <== div[k]; +// mul.b[k] <== 0; + +// for (var i = 0; i < 2 * k + 2; i++) { +// //log(mul.out[i]); +// } + +// component add = BigAdd(n, 2 * k + 2); +// for (var i = 0; i < 2 * k; i++) { +// add.a[i] <== mul.out[i]; +// if (i < k) { +// add.b[i] <== mod[i]; +// } else { +// add.b[i] <== 0; +// } +// } +// add.a[2 * k] <== mul.out[2 * k]; +// add.a[2 * k + 1] <== mul.out[2 * k + 1]; +// add.b[2 * k] <== 0; +// add.b[2 * k + 1] <== 0; + +// for (var i = 0; i < 2 * k + 2; i++) { +// //log(add.out[i]); +// } + +// for (var i = 0; i < 2 * k; i++) { +// add.out[i] === a[i]; +// } +// add.out[2 * k] === 0; +// add.out[2 * k + 1] === 0; + +// component lt = BigLessThanEcdsa(n, k); +// for (var i = 0; i < k; i++) { +// lt.a[i] <== mod[i]; +// lt.b[i] <== b[i]; +// } +// lt.out === 1; +// } + +// // copied from BigMod to allow a to have m registers and use long_div2 +// template BigMod2(n, k, m) { +// assert(n <= 126); +// signal input a[m]; +// signal input b[k]; + +// signal output div[m - k + 1]; +// signal output mod[k]; + +// var longdiv[2][50] = long_div2(n, k, m-k, a, b); +// for (var i = 0; i < k; i++) { +// mod[i] <-- longdiv[1][i]; +// } +// for (var i = 0; i <= m-k; i++) { +// div[i] <-- longdiv[0][i]; +// } +// component div_range_checks[m - k + 1]; +// for (var i = 0; i <= m-k; i++) { +// div_range_checks[i] = Num2Bits(n); +// div_range_checks[i].in <== div[i]; +// } +// component mod_range_checks[k]; +// for (var i = 0; i < k; i++) { +// mod_range_checks[i] = Num2Bits(n); +// mod_range_checks[i].in <== mod[i]; +// } + +// component mul = BigMult(n, m-k + 1); +// // this might need to be optimized since b has less registers than div +// for (var i = 0; i < k; i++) { +// mul.a[i] <== div[i]; +// mul.b[i] <== b[i]; +// } +// for (var i = k; i <= m-k; i++) { +// mul.a[i] <== div[i]; +// mul.b[i] <== 0; +// } + +// // mul shouldn't have more registers than a +// for (var i = m; i < 2*(m-k)+2; i++) { +// mul.out[i] === 0; +// } + +// component add = BigAdd(n, m); +// for (var i = 0; i < m; i++) { +// add.a[i] <== mul.out[i]; +// if (i < k) { +// add.b[i] <== mod[i]; +// } else { +// add.b[i] <== 0; +// } +// } + +// for (var i = 0; i < m; i++) { +// add.out[i] === a[i]; +// } +// add.out[m] === 0; + +// component lt = BigLessThanEcdsa(n, k); +// for (var i = 0; i < k; i++) { +// lt.a[i] <== mod[i]; +// lt.b[i] <== b[i]; +// } +// lt.out === 1; +// } + + + +// // a[i], b[i] in 0... 2**n-1 +// // represent a = a[0] + a[1] * 2**n + .. + a[k - 1] * 2**(n * k) +// // calculates (a+b)%p, where 0<= a,b < p +// template BigAddModP(n, k){ +// assert(n <= 252); +// signal input a[k]; +// signal input b[k]; +// signal input p[k]; +// signal output out[k]; + +// component add = BigAdd(n,k); +// for (var i = 0; i < k; i++) { +// add.a[i] <== a[i]; +// add.b[i] <== b[i]; +// } +// component lt = BigLessThanEcdsa(n, k+1); +// for (var i = 0; i < k; i++) { +// lt.a[i] <== add.out[i]; +// lt.b[i] <== p[i]; +// } +// lt.a[k] <== add.out[k]; +// lt.b[k] <== 0; + +// component sub = BigSub(n,k+1); +// for (var i = 0; i < k; i++) { +// sub.a[i] <== add.out[i]; +// sub.b[i] <== (1-lt.out) * p[i]; +// } +// sub.a[k] <== add.out[k]; +// sub.b[k] <== 0; - sub.out[k] === 0; - for (var i = 0; i < k; i++) { - out[i] <== sub.out[i]; - } -} - -/* -Inputs: - - BigInts a, b - - Assume a >= b -Output: - - BigInt out = a - b - - underflow = how much is borrowed at the highest digit of subtraction, only nonzero if a < b -*/ -template BigSub(n, k) { - assert(n <= 252); - signal input a[k]; - signal input b[k]; - signal output out[k]; - signal output underflow; - - component unit0 = ModSub(n); - unit0.a <== a[0]; - unit0.b <== b[0]; - out[0] <== unit0.out; - - component unit[k - 1]; - for (var i = 1; i < k; i++) { - unit[i - 1] = ModSubThree(n); - unit[i - 1].a <== a[i]; - unit[i - 1].b <== b[i]; - if (i == 1) { - unit[i - 1].c <== unit0.borrow; - } else { - unit[i - 1].c <== unit[i - 2].borrow; - } - out[i] <== unit[i - 1].out; - } - underflow <== unit[k - 2].borrow; -} - -// calculates (a - b) % p, where a, b < p -// note: does not assume a >= b -template BigSubModP(n, k){ - assert(n <= 252); - signal input a[k]; - signal input b[k]; - signal input p[k]; - signal output out[k]; - component sub = BigSub(n, k); - for (var i = 0; i < k; i++){ - sub.a[i] <== a[i]; - sub.b[i] <== b[i]; - } - signal flag; - flag <== sub.underflow; - component add = BigAdd(n, k); - for (var i = 0; i < k; i++){ - add.a[i] <== sub.out[i]; - add.b[i] <== p[i]; - } - signal tmp[k]; - for (var i = 0; i < k; i++){ - tmp[i] <== (1 - flag) * sub.out[i]; - out[i] <== tmp[i] + flag * add.out[i]; - } -} - -// Note: deprecated -template BigMultModP(n, k) { - assert(n <= 252); - signal input a[k]; - signal input b[k]; - signal input p[k]; - signal output out[k]; - - component big_mult = BigMult(n, k); - for (var i = 0; i < k; i++) { - big_mult.a[i] <== a[i]; - big_mult.b[i] <== b[i]; - } - component big_mod = BigMod(n, k); - for (var i = 0; i < 2 * k; i++) { - big_mod.a[i] <== big_mult.out[i]; - } - for (var i = 0; i < k; i++) { - big_mod.b[i] <== p[i]; - } - for (var i = 0; i < k; i++) { - out[i] <== big_mod.mod[i]; - } -} - -template BigModInv(n, k) { - assert(n <= 252); - signal input in[k]; - signal input p[k]; - signal output out[k]; - - // length k - var inv[50] = mod_inv(n, k, in, p); - for (var i = 0; i < k; i++) { - out[i] <-- inv[i]; - } - component range_checks[k]; - for (var i = 0; i < k; i++) { - range_checks[i] = Num2Bits(n); - range_checks[i].in <== out[i]; - } - - component mult = BigMult(n, k); - for (var i = 0; i < k; i++) { - mult.a[i] <== in[i]; - mult.b[i] <== out[i]; - } - component mod = BigMod(n, k); - for (var i = 0; i < 2 * k; i++) { - mod.a[i] <== mult.out[i]; - } - for (var i = 0; i < k; i++) { - mod.b[i] <== p[i]; - } - mod.mod[0] === 1; - for (var i = 1; i < k; i++) { - mod.mod[i] === 0; - } -} +// sub.out[k] === 0; +// for (var i = 0; i < k; i++) { +// out[i] <== sub.out[i]; +// } +// } + +// /* +// Inputs: +// - BigInts a, b +// - Assume a >= b +// Output: +// - BigInt out = a - b +// - underflow = how much is borrowed at the highest digit of subtraction, only nonzero if a < b +// */ +// template BigSub(n, k) { +// assert(n <= 252); +// signal input a[k]; +// signal input b[k]; +// signal output out[k]; +// signal output underflow; + +// component unit0 = ModSub(n); +// unit0.a <== a[0]; +// unit0.b <== b[0]; +// out[0] <== unit0.out; + +// component unit[k - 1]; +// for (var i = 1; i < k; i++) { +// unit[i - 1] = ModSubThree(n); +// unit[i - 1].a <== a[i]; +// unit[i - 1].b <== b[i]; +// if (i == 1) { +// unit[i - 1].c <== unit0.borrow; +// } else { +// unit[i - 1].c <== unit[i - 2].borrow; +// } +// out[i] <== unit[i - 1].out; +// } +// underflow <== unit[k - 2].borrow; +// } + +// // calculates (a - b) % p, where a, b < p +// // note: does not assume a >= b +// template BigSubModP(n, k){ +// assert(n <= 252); +// signal input a[k]; +// signal input b[k]; +// signal input p[k]; +// signal output out[k]; +// component sub = BigSub(n, k); +// for (var i = 0; i < k; i++){ +// sub.a[i] <== a[i]; +// sub.b[i] <== b[i]; +// } +// signal flag; +// flag <== sub.underflow; +// component add = BigAdd(n, k); +// for (var i = 0; i < k; i++){ +// add.a[i] <== sub.out[i]; +// add.b[i] <== p[i]; +// } +// signal tmp[k]; +// for (var i = 0; i < k; i++){ +// tmp[i] <== (1 - flag) * sub.out[i]; +// out[i] <== tmp[i] + flag * add.out[i]; +// } +// } + +// // Note: deprecated +// template BigMultModP(n, k) { +// assert(n <= 252); +// signal input a[k]; +// signal input b[k]; +// signal input p[k]; +// signal output out[k]; + +// component big_mult = BigMult(n, k); +// for (var i = 0; i < k; i++) { +// big_mult.a[i] <== a[i]; +// big_mult.b[i] <== b[i]; +// } +// component big_mod = BigMod(n, k); +// for (var i = 0; i < 2 * k; i++) { +// big_mod.a[i] <== big_mult.out[i]; +// } +// for (var i = 0; i < k; i++) { +// big_mod.b[i] <== p[i]; +// } +// for (var i = 0; i < k; i++) { +// out[i] <== big_mod.mod[i]; +// } +// } + +// template BigModInv(n, k) { +// assert(n <= 252); +// signal input in[k]; +// signal input p[k]; +// signal output out[k]; + +// // length k +// var inv[50] = mod_inv(n, k, in, p); +// for (var i = 0; i < k; i++) { +// out[i] <-- inv[i]; +// } +// component range_checks[k]; +// for (var i = 0; i < k; i++) { +// range_checks[i] = Num2Bits(n); +// range_checks[i].in <== out[i]; +// } + +// component mult = BigMult(n, k); +// for (var i = 0; i < k; i++) { +// mult.a[i] <== in[i]; +// mult.b[i] <== out[i]; +// } +// component mod = BigMod(n, k); +// for (var i = 0; i < 2 * k; i++) { +// mod.a[i] <== mult.out[i]; +// } +// for (var i = 0; i < k; i++) { +// mod.b[i] <== p[i]; +// } +// mod.mod[0] === 1; +// for (var i = 1; i < k; i++) { +// mod.mod[i] === 0; +// } +// } /* Taken from circom-ecdsa Input: @@ -778,213 +778,213 @@ template CheckCarryToZeroEcdsa(n, m, k) { } -/* -Let X = 2^n -Input: - - in is length k + m array in signed overflow representation - - in = in[0] + in[1] * X + ... + in[k+m-1] * X^{k+m-1} - - Assume each in[i] is a signed integer such that abs(in[i] * 2^n) < 2^252 - - p is prime in BigInt format passed as parameter -Output: - - out = out[0] + out[1] * X + ... + out[k-1] * X^{k-1} is BigInt congruent to in (mod p) -Implementation: - - For i >= k, we precompute X^i = r[i] mod p, where r[i] represented as k registers with r[i][j] in [0, 2^n) - - in[i] * X^i is replaced by sum_j in[i] * r[i][j] * X^j -Notes: - - If each in[i] has absolute value = k, we precompute X^i = r[i] mod p, where r[i] represented as k registers with r[i][j] in [0, 2^n) +// - in[i] * X^i is replaced by sum_j in[i] * r[i][j] * X^j +// Notes: +// - If each in[i] has absolute value 2*l-1) ? 2*k-1 : 2*l-1; - var pow[k2][k2]; - for(var i = 0; i 2*l-1) ? 2*k-1 : 2*l-1; +// var pow[k2][k2]; +// for(var i = 0; i la + lb -1) ? ka + kb - 1 : la + lb -1; - var pow[k2][k2]; - for(var i = 0; i la + lb -1) ? ka + kb - 1 : la + lb -1; +// var pow[k2][k2]; +// for(var i = 0; i b) - return a; - return b; -} +// function min(a, b) { +// if(a < b) +// return a; +// return b; +// } + +// function max(a, b) { +// if(a > b) +// return a; +// return b; +// } function div_ceil_ecdsa(m, n) { var ret = 0; @@ -33,13 +33,13 @@ function log_ceil_ecdsa(n) { return 254; } -function SplitFn(in, n, m) { - return [in % (1 << n), (in \ (1 << n)) % (1 << m)]; -} +// function SplitFn(in, n, m) { +// return [in % (1 << n), (in \ (1 << n)) % (1 << m)]; +// } -function SplitThreeFn(in, n, m, k) { - return [in % (1 << n), (in \ (1 << n)) % (1 << m), (in \ (1 << n + m)) % (1 << k)]; -} +// function SplitThreeFn(in, n, m, k) { +// return [in % (1 << n), (in \ (1 << n)) % (1 << m), (in \ (1 << n + m)) % (1 << k)]; +// } // 1 if true, 0 if false function long_gt_ecdsa(n, k, a, b) { @@ -54,19 +54,19 @@ function long_gt_ecdsa(n, k, a, b) { return 0; } -function long_is_zero(k, a){ - for(var idx=0; idx k2 -// output has k1+1 registers -function long_add_unequal(n, k1, k2, a, b){ - var carry = 0; - var sum[50]; - for(var i=0; i k2 +// // output has k1+1 registers +// function long_add_unequal(n, k1, k2, a, b){ +// var carry = 0; +// var sum[50]; +// for(var i=0; i 1) { - sumAndCarry[j] = SplitFn(split[j][0][1] + split[j][1][0], n, n); - out[j][1] = sumAndCarry[j][0]; - carry[j][1] = sumAndCarry[j][1]; - } - if (2 * k - 1 > 2) { - for (var i = 2; i < 2 * k - 1; i++) { - sumAndCarry[j] = SplitFn(split[j][i][0] + split[j][i-1][1] + split[j][i-2][2] + carry[j][i-1], n, n); - out[j][i] = sumAndCarry[j][0]; - carry[j][i] = sumAndCarry[j][1]; - } - out[j][2 * k - 1] = split[j][2*k-2][1] + split[j][2*k-3][2] + carry[j][2*k-2]; - } - } - - return out; -} - -// Put all modular arithmetic, aka F_p field stuff, at the end - -function long_add_mod(n, k, a, b, p) { - var sum[50] = long_add(n,k,a,b); - var temp[2][50] = long_div2(n,k,1,sum,p); +// // n bits per register +// // a and b both have l x k registers +// // out has length 2l - 1 x 2k +// // adapted from BigMultShortLong2D and LongToShortNoEndCarry2 witness computation +// function prod2D(n, k, l, a, b) { +// // first compute the intermediate values. taken from BigMulShortLong +// var prod_val[20][50]; // length is 2l - 1 by 2k - 1 +// for (var i = 0; i < 2 * k - 1; i++) { +// for (var j = 0; j < 2 * l - 1; j ++) { +// prod_val[j][i] = 0; +// } +// } +// for (var i1 = 0; i1 < k; i1 ++) { +// for (var i2 = 0; i2 < k; i2 ++) { +// for (var j1 = 0; j1 < l; j1 ++) { +// for (var j2 = 0; j2 < l; j2 ++) { +// prod_val[j1+j2][i1+i2] = prod_val[j1+j2][i1+i2] + a[j1][i1] * b[j2][i2]; +// } +// } +// } +// } + +// // now do a bunch of carrying to make sure registers not overflowed. taken from LongToShortNoEndCarry2 +// var out[20][50]; // length is 2 * l by 2 * k + +// var split[20][50][3]; // second dimension has length 2 * k - 1 +// for (var j = 0; j < 2 * l - 1; j ++) { +// for (var i = 0; i < 2 * k - 1; i++) { +// split[j][i] = SplitThreeFn(prod_val[j][i], n, n, n); +// } +// } + +// var carry[20][50]; // length is 2l-1 x 2k +// var sumAndCarry[20][2]; +// for ( var j = 0; j < 2 * l - 1; j ++) { +// carry[j][0] = 0; +// out[j][0] = split[j][0][0]; +// if (2 * k - 1 > 1) { +// sumAndCarry[j] = SplitFn(split[j][0][1] + split[j][1][0], n, n); +// out[j][1] = sumAndCarry[j][0]; +// carry[j][1] = sumAndCarry[j][1]; +// } +// if (2 * k - 1 > 2) { +// for (var i = 2; i < 2 * k - 1; i++) { +// sumAndCarry[j] = SplitFn(split[j][i][0] + split[j][i-1][1] + split[j][i-2][2] + carry[j][i-1], n, n); +// out[j][i] = sumAndCarry[j][0]; +// carry[j][i] = sumAndCarry[j][1]; +// } +// out[j][2 * k - 1] = split[j][2*k-2][1] + split[j][2*k-3][2] + carry[j][2*k-2]; +// } +// } + +// return out; +// } + +// // Put all modular arithmetic, aka F_p field stuff, at the end + +function long_add_mod_ecdsa(n, k, a, b, p) { + var sum[50] = long_add_ecdsa(n,k,a,b); + var temp[2][50] = long_div2_ecdsa(n,k,1,sum,p); return temp[1]; } -function long_sub_mod(n, k, a, b, p) { +function long_sub_mod_ecdsa(n, k, a, b, p) { if(long_gt_ecdsa(n, k, b, a) == 1){ - return long_add(n, k, a, long_sub_ecdsa(n,k,p,b)); + return long_add_ecdsa(n, k, a, long_sub_ecdsa(n,k,p,b)); }else{ return long_sub_ecdsa(n, k, a, b); } } -function prod_mod(n, k, a, b, p) { - var prod[50] = prod(n,k,a,b); - var temp[2][50] = long_div_ecdsa(n,k,prod,p); +function prod_mod_ecdsa(n, k, a, b, p) { + var prod_ecdsa[50] = prod_ecdsa(n,k,a,b); + var temp[2][50] = long_div_ecdsa(n,k,prod_ecdsa,p); return temp[1]; } @@ -443,7 +443,7 @@ function prod_mod(n, k, a, b, p) { // k * n <= 500 // p is a prime // computes a^e mod p -function mod_exp(n, k, a, p, e) { +function mod_exp_ecdsa(n, k, a, p, e) { var eBits[500]; // length is k * n var bitlength; for (var i = 0; i < k; i++) { @@ -465,7 +465,7 @@ function mod_exp(n, k, a, p, e) { // multiply by a if bit is 0 if (eBits[i] == 1) { var temp[50]; // length 2 * k - temp = prod(n, k, out, a); + temp = prod_ecdsa(n, k, out, a); var temp2[2][50]; temp2 = long_div_ecdsa(n, k, temp, p); out = temp2[1]; @@ -474,7 +474,7 @@ function mod_exp(n, k, a, p, e) { // square, unless we're at the end if (i > 0) { var temp[50]; // length 2 * k - temp = prod(n, k, out, out); + temp = prod_ecdsa(n, k, out, out); var temp2[2][50]; temp2 = long_div_ecdsa(n, k, temp, p); out = temp2[1]; @@ -491,7 +491,7 @@ function mod_exp(n, k, a, p, e) { // p is a prime // if a == 0 mod p, returns 0 // else computes inv = a^(p-2) mod p -function mod_inv(n, k, a, p) { +function mod_inv_ecdsa(n, k, a, p) { var isZero = 1; for (var i = 0; i < k; i++) { if (a[i] != 0) { diff --git a/circuits/circuits/utils/circom-ecdsa/curve.circom b/circuits/circuits/utils/circom-ecdsa/curve.circom index ae35240e..cbad407a 100644 --- a/circuits/circuits/utils/circom-ecdsa/curve.circom +++ b/circuits/circuits/utils/circom-ecdsa/curve.circom @@ -173,15 +173,15 @@ template EllipticCurveAddUnequal(n, k, p) { assert(4*n + LOGK3 < 251); // precompute lambda and x_3 and then y_3 - var dy[50] = long_sub_mod(n, k, b[1], a[1], p); - var dx[50] = long_sub_mod(n, k, b[0], a[0], p); - var dx_inv[50] = mod_inv(n, k, dx, p); - var lambda[50] = prod_mod(n, k, dy, dx_inv, p); - var lambda_sq[50] = prod_mod(n, k, lambda, lambda, p); + var dy[50] = long_sub_mod_ecdsa(n, k, b[1], a[1], p); + var dx[50] = long_sub_mod_ecdsa(n, k, b[0], a[0], p); + var dx_inv[50] = mod_inv_ecdsa(n, k, dx, p); + var lambda[50] = prod_mod_ecdsa(n, k, dy, dx_inv, p); + var lambda_sq[50] = prod_mod_ecdsa(n, k, lambda, lambda, p); // out[0] = x_3 = lamb^2 - a[0] - b[0] % p // out[1] = y_3 = lamb * (a[0] - x_3) - a[1] % p - var x3[50] = long_sub_mod(n, k, long_sub_mod(n, k, lambda_sq, a[0], p), b[0], p); - var y3[50] = long_sub_mod(n, k, prod_mod(n, k, lambda, long_sub_mod(n, k, a[0], x3, p), p), a[1], p); + var x3[50] = long_sub_mod_ecdsa(n, k, long_sub_mod_ecdsa(n, k, lambda_sq, a[0], p), b[0], p); + var y3[50] = long_sub_mod_ecdsa(n, k, prod_mod_ecdsa(n, k, lambda, long_sub_mod_ecdsa(n, k, a[0], x3, p), p), a[1], p); for(var i = 0; i < k; i++){ out[0][i] <-- x3[i]; @@ -265,13 +265,13 @@ template EllipticCurveDouble(n, k, a, b, p) { } // precompute lambda - var lamb_num[50] = long_add_mod(n, k, a, prod_mod(n, k, long_3, prod_mod(n, k, in[0], in[0], p), p), p); - var lamb_denom[50] = long_add_mod(n, k, in[1], in[1], p); - var lamb[50] = prod_mod(n, k, lamb_num, mod_inv(n, k, lamb_denom, p), p); + var lamb_num[50] = long_add_mod_ecdsa(n, k, a, prod_mod_ecdsa(n, k, long_3, prod_mod_ecdsa(n, k, in[0], in[0], p), p), p); + var lamb_denom[50] = long_add_mod_ecdsa(n, k, in[1], in[1], p); + var lamb[50] = prod_mod_ecdsa(n, k, lamb_num, mod_inv_ecdsa(n, k, lamb_denom, p), p); // precompute x_3, y_3 - var x3[50] = long_sub_mod(n, k, prod_mod(n, k, lamb, lamb, p), long_add_mod(n, k, in[0], in[0], p), p); - var y3[50] = long_sub_mod(n, k, prod_mod(n, k, lamb, long_sub_mod(n, k, in[0], x3, p), p), in[1], p); + var x3[50] = long_sub_mod_ecdsa(n, k, prod_mod_ecdsa(n, k, lamb, lamb, p), long_add_mod_ecdsa(n, k, in[0], in[0], p), p); + var y3[50] = long_sub_mod_ecdsa(n, k, prod_mod_ecdsa(n, k, lamb, long_sub_mod_ecdsa(n, k, in[0], x3, p), p), in[1], p); for(var i=0; i= 0 +template ModSubThree(CHUNK_SIZE) { + assert(CHUNK_SIZE + 2 <= 253); + signal input a; + signal input b; + signal input c; + assert(a - b - c + (1 << CHUNK_SIZE) >= 0); + signal output out; + signal output borrow; + signal bPlusC; + bPlusC <== b + c; + component lt = LessThan(CHUNK_SIZE + 1); + lt.in[0] <== a; + lt.in[1] <== bPlusC; + borrow <== lt.out; + out <== borrow * (1 << CHUNK_SIZE) + a - bPlusC; +} + +template ModSumThree(CHUNK_SIZE) { + assert(CHUNK_SIZE + 2 <= 253); + signal input a; + signal input b; + signal input c; + signal output sum; + signal output carry; + + component n2b = Num2Bits(CHUNK_SIZE + 2); + n2b.in <== a + b + c; + carry <== n2b.out[CHUNK_SIZE] + 2 * n2b.out[CHUNK_SIZE + 1]; + sum <== a + b + c - carry * (1 << CHUNK_SIZE); +} + +template ModSumFour(CHUNK_SIZE) { + assert(CHUNK_SIZE + 2 <= 253); + signal input a; + signal input b; + signal input c; + signal input d; + signal output sum; + signal output carry; + + component n2b = Num2Bits(CHUNK_SIZE + 2); + n2b.in <== a + b + c + d; + carry <== n2b.out[CHUNK_SIZE] + 2 * n2b.out[CHUNK_SIZE + 1]; + sum <== a + b + c + d - carry * (1 << CHUNK_SIZE); +} + +// product mod 2**CHUNK_SIZE with carry +template ModProd(CHUNK_SIZE) { + assert(CHUNK_SIZE <= 126); + signal input a; + signal input b; + signal output prod; + signal output carry; + + component n2b = Num2Bits(2 * CHUNK_SIZE); + n2b.in <== a * b; + + component b2n1 = Bits2Num(CHUNK_SIZE); + component b2n2 = Bits2Num(CHUNK_SIZE); + var i; + for (i = 0; i < CHUNK_SIZE; i++) { + b2n1.in[i] <== n2b.out[i]; + b2n2.in[i] <== n2b.out[i + CHUNK_SIZE]; + } + prod <== b2n1.out; + carry <== b2n2.out; +} + +// split a CHUNK_SIZE + M bit input into TWO outputs +template Split(CHUNK_SIZE, M) { + assert(CHUNK_SIZE <= 126); + signal input in; + signal output small; + signal output big; + + small <-- in % (1 << CHUNK_SIZE); + big <-- in \ (1 << CHUNK_SIZE); + + component n2bSmall = Num2Bits(CHUNK_SIZE); + n2bSmall.in <== small; + component n2bBig = Num2Bits(M); + n2bBig.in <== big; + + in === small + big * (1 << CHUNK_SIZE); +} + +// split a CHUNK_SIZE + M + CHUNK_NUMBER bit input into three outputs +template SplitThree(CHUNK_SIZE, M, CHUNK_NUMBER) { + assert(CHUNK_SIZE <= 126); + signal input in; + signal output small; + signal output medium; + signal output big; + + small <-- in % (1 << CHUNK_SIZE); + medium <-- (in \ (1 << CHUNK_SIZE)) % (1 << M); + big <-- in \ (1 << CHUNK_SIZE + M); + + component n2bSmall = Num2Bits(CHUNK_SIZE); + n2bSmall.in <== small; + component n2bMedium = Num2Bits(M); + n2bMedium.in <== medium; + component n2bBig = Num2Bits(CHUNK_NUMBER); + n2bBig.in <== big; + + in === small + medium * (1 << CHUNK_SIZE) + big * (1 << CHUNK_SIZE + M); +} + +// a[i], b[i] in 0... 2**CHUNK_SIZE-1 +// represent a = a[0] + a[1] * 2**CHUNK_SIZE + .. + a[CHUNK_NUMBER - 1] * 2**(CHUNK_SIZE * CHUNK_NUMBER) +template BigAdd(CHUNK_SIZE, CHUNK_NUMBER) { + assert(CHUNK_SIZE <= 252); + signal input a[CHUNK_NUMBER]; + signal input b[CHUNK_NUMBER]; + signal output out[CHUNK_NUMBER + 1]; + + component unit0 = ModSum(CHUNK_SIZE); + unit0.a <== a[0]; + unit0.b <== b[0]; + out[0] <== unit0.sum; + + component unit[CHUNK_NUMBER - 1]; + for (var i = 1; i < CHUNK_NUMBER; i++) { + unit[i - 1] = ModSumThree(CHUNK_SIZE); + unit[i - 1].a <== a[i]; + unit[i - 1].b <== b[i]; + if (i == 1) { + unit[i - 1].c <== unit0.carry; + } else { + unit[i - 1].c <== unit[i - 2].carry; + } + out[i] <== unit[i - 1].sum; + } + out[CHUNK_NUMBER] <== unit[CHUNK_NUMBER - 2].carry; +} + +/* +Polynomial Multiplication +Inputs: + - a = a[0] + a[1] * X + ... + a[CHUNK_NUMBER-1] * X^{CHUNK_NUMBER-1} + - b = b[0] + b[1] * X + ... + b[CHUNK_NUMBER-1] * X^{CHUNK_NUMBER-1} +Output: + - out = out[0] + out[1] * X + ... + out[2 * CHUNK_NUMBER - 2] * X^{2*CHUNK_NUMBER - 2} + - out = a * b as polynomials in X +Notes: + - Optimization due to xJsnark: + -- witness is calculated by normal polynomial multiplication + -- out is contrained by evaluating out(X) === a(X) * b(X) at X = 0, ..., 2*CHUNK_NUMBER - 2 + - If a[i], b[j] have absolute value < B, then out[i] has absolute value < CHUNK_NUMBER * B^2 +M_OUT is the expected max number of bits in the output registers +*/ +template BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER, M_OUT) { + assert(CHUNK_SIZE <= 126); + signal input a[CHUNK_NUMBER]; + signal input b[CHUNK_NUMBER]; + signal output out[2 * CHUNK_NUMBER - 1]; + + var PROD_VAL[2 * CHUNK_NUMBER - 1]; + for (var i = 0; i < 2 * CHUNK_NUMBER - 1; i++) { + PROD_VAL[i] = 0; + if (i < CHUNK_NUMBER) { + for (var a_idx = 0; a_idx <= i; a_idx++) { + PROD_VAL[i] = PROD_VAL[i] + a[a_idx] * b[i - a_idx]; + } + } else { + for (var a_idx = i - CHUNK_NUMBER + 1; a_idx < CHUNK_NUMBER; a_idx++) { + PROD_VAL[i] = PROD_VAL[i] + a[a_idx] * b[i - a_idx]; + } + } + out[i] <-- PROD_VAL[i]; + } + + var k2 = 2 * CHUNK_NUMBER - 1; + var pow[k2][k2]; // we cache the exponent values because it makes a big difference in witness generation time + for(var i = 0; i 1) { + var sumAndCarry[2] = SplitFn(split[0][1] + split[1][0], CHUNK_SIZE, CHUNK_SIZE); + out[1] <-- sumAndCarry[0]; + carry[1] = sumAndCarry[1]; + } + if (CHUNK_NUMBER > 2) { + for (var i = 2; i < CHUNK_NUMBER; i++) { + var sumAndCarry[2] = SplitFn(split[i][0] + split[i-1][1] + split[i-2][2] + carry[i-1], CHUNK_SIZE, CHUNK_SIZE); + out[i] <-- sumAndCarry[0]; + carry[i] = sumAndCarry[1]; + } + out[CHUNK_NUMBER] <-- split[CHUNK_NUMBER-1][1] + split[CHUNK_NUMBER-2][2] + carry[CHUNK_NUMBER-1]; + } + + component outRangeChecks[CHUNK_NUMBER+1]; + for (var i = 0; i < CHUNK_NUMBER+1; i++) { + outRangeChecks[i] = Num2Bits(CHUNK_SIZE); + outRangeChecks[i].in <== out[i]; + } + + signal runningCarry[CHUNK_NUMBER]; + component runningCarryRangeChecks[CHUNK_NUMBER]; + runningCarry[0] <-- (in[0] - out[0]) / (1 << CHUNK_SIZE); + runningCarryRangeChecks[0] = Num2Bits(CHUNK_SIZE + log_ceil(CHUNK_NUMBER)); + runningCarryRangeChecks[0].in <== runningCarry[0]; + runningCarry[0] * (1 << CHUNK_SIZE) === in[0] - out[0]; + for (var i = 1; i < CHUNK_NUMBER; i++) { + runningCarry[i] <-- (in[i] - out[i] + runningCarry[i-1]) / (1 << CHUNK_SIZE); + runningCarryRangeChecks[i] = Num2Bits(CHUNK_SIZE + log_ceil(CHUNK_NUMBER)); + runningCarryRangeChecks[i].in <== runningCarry[i]; + runningCarry[i] * (1 << CHUNK_SIZE) === in[i] - out[i] + runningCarry[i-1]; + } + runningCarry[CHUNK_NUMBER-1] === out[CHUNK_NUMBER]; +} + +template BigMult(CHUNK_SIZE, CHUNK_NUMBER) { + signal input a[CHUNK_NUMBER]; + signal input b[CHUNK_NUMBER]; + signal output out[2 * CHUNK_NUMBER]; + + var LOGK = log_ceil(CHUNK_NUMBER); + component mult = BigMultShortLong(CHUNK_SIZE, CHUNK_NUMBER, 2*CHUNK_SIZE + LOGK); + for (var i = 0; i < CHUNK_NUMBER; i++) { + mult.a[i] <== a[i]; + mult.b[i] <== b[i]; + } + + // no carry is possible in the highest order register + component longshort = LongToShortNoEndCarry(CHUNK_SIZE, 2 * CHUNK_NUMBER - 1); + for (var i = 0; i < 2 * CHUNK_NUMBER - 1; i++) { + longshort.in[i] <== mult.out[i]; + } + for (var i = 0; i < 2 * CHUNK_NUMBER; i++) { + out[i] <== longshort.out[i]; + } +} + +/* +Inputs: + - BigInts a, b +Output: + - out = (a < b) ? 1 : 0 +*/ +template BigLessThan(CHUNK_SIZE, CHUNK_NUMBER){ + signal input a[CHUNK_NUMBER]; + signal input b[CHUNK_NUMBER]; + signal output out; + + component lt[CHUNK_NUMBER]; + component eq[CHUNK_NUMBER]; + for (var i = 0; i < CHUNK_NUMBER; i++) { + lt[i] = LessThan(CHUNK_SIZE); + lt[i].in[0] <== a[i]; + lt[i].in[1] <== b[i]; + eq[i] = IsEqual(); + eq[i].in[0] <== a[i]; + eq[i].in[1] <== b[i]; + } + + // ors[i] holds (lt[CHUNK_NUMBER - 1] || (eq[CHUNK_NUMBER - 1] && lt[CHUNK_NUMBER - 2]) .. || (eq[CHUNK_NUMBER - 1] && .. && lt[i])) + // ands[i] holds (eq[CHUNK_NUMBER - 1] && .. && lt[i]) + // eqAnds[i] holds (eq[CHUNK_NUMBER - 1] && .. && eq[i]) + component ors[CHUNK_NUMBER - 1]; + component ands[CHUNK_NUMBER - 1]; + component eqAnds[CHUNK_NUMBER - 1]; + for (var i = CHUNK_NUMBER - 2; i >= 0; i--) { + ands[i] = AND(); + eqAnds[i] = AND(); + ors[i] = OR(); + + if (i == CHUNK_NUMBER - 2) { + ands[i].a <== eq[CHUNK_NUMBER - 1].out; + ands[i].b <== lt[CHUNK_NUMBER - 2].out; + eqAnds[i].a <== eq[CHUNK_NUMBER - 1].out; + eqAnds[i].b <== eq[CHUNK_NUMBER - 2].out; + ors[i].a <== lt[CHUNK_NUMBER - 1].out; + ors[i].b <== ands[i].out; + } else { + ands[i].a <== eqAnds[i + 1].out; + ands[i].b <== lt[i].out; + eqAnds[i].a <== eqAnds[i + 1].out; + eqAnds[i].b <== eq[i].out; + ors[i].a <== ors[i + 1].out; + ors[i].b <== ands[i].out; + } + } + out <== ors[0].out; +} + +// leading register of b should be non-zero +template BigMod(CHUNK_SIZE, CHUNK_NUMBER) { + assert(CHUNK_SIZE <= 126); + signal input a[2 * CHUNK_NUMBER]; + signal input b[CHUNK_NUMBER]; + + signal output div[CHUNK_NUMBER + 1]; + signal output mod[CHUNK_NUMBER]; + + var LONG_DIV[2][150] = long_div(CHUNK_SIZE, CHUNK_NUMBER, a, b); + for (var i = 0; i < CHUNK_NUMBER; i++) { + div[i] <-- LONG_DIV[0][i]; + mod[i] <-- LONG_DIV[1][i]; + } + div[CHUNK_NUMBER] <-- LONG_DIV[0][CHUNK_NUMBER]; + component div_range_checks[CHUNK_NUMBER + 1]; + for (var i = 0; i <= CHUNK_NUMBER; i++) { + div_range_checks[i] = Num2Bits(CHUNK_SIZE); + div_range_checks[i].in <== div[i]; + } + component mod_range_checks[CHUNK_NUMBER]; + for (var i = 0; i < CHUNK_NUMBER; i++) { + mod_range_checks[i] = Num2Bits(CHUNK_SIZE); + mod_range_checks[i].in <== mod[i]; + } + + component mul = BigMult(CHUNK_SIZE, CHUNK_NUMBER + 1); + for (var i = 0; i < CHUNK_NUMBER; i++) { + mul.a[i] <== div[i]; + mul.b[i] <== b[i]; + } + mul.a[CHUNK_NUMBER] <== div[CHUNK_NUMBER]; + mul.b[CHUNK_NUMBER] <== 0; + + for (var i = 0; i < 2 * CHUNK_NUMBER + 2; i++) { + //log(mul.out[i]); + } + + component add = BigAdd(CHUNK_SIZE, 2 * CHUNK_NUMBER + 2); + for (var i = 0; i < 2 * CHUNK_NUMBER; i++) { + add.a[i] <== mul.out[i]; + if (i < CHUNK_NUMBER) { + add.b[i] <== mod[i]; + } else { + add.b[i] <== 0; + } + } + add.a[2 * CHUNK_NUMBER] <== mul.out[2 * CHUNK_NUMBER]; + add.a[2 * CHUNK_NUMBER + 1] <== mul.out[2 * CHUNK_NUMBER + 1]; + add.b[2 * CHUNK_NUMBER] <== 0; + add.b[2 * CHUNK_NUMBER + 1] <== 0; + + for (var i = 0; i < 2 * CHUNK_NUMBER + 2; i++) { + //log(add.out[i]); + } + + for (var i = 0; i < 2 * CHUNK_NUMBER; i++) { + add.out[i] === a[i]; + } + add.out[2 * CHUNK_NUMBER] === 0; + add.out[2 * CHUNK_NUMBER + 1] === 0; + + component lt = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER); + for (var i = 0; i < CHUNK_NUMBER; i++) { + lt.a[i] <== mod[i]; + lt.b[i] <== b[i]; + } + lt.out === 1; +} + +// copied from BigMod to allow a to have M registers and use long_div2 +template BigMod2(CHUNK_SIZE, CHUNK_NUMBER, M) { + assert(CHUNK_SIZE <= 126); + signal input a[M]; + signal input b[CHUNK_NUMBER]; + + signal output div[M - CHUNK_NUMBER + 1]; + signal output mod[CHUNK_NUMBER]; + + var LONG_DIV[2][150] = long_div2(CHUNK_SIZE, CHUNK_NUMBER, M-CHUNK_NUMBER, a, b); + for (var i = 0; i < CHUNK_NUMBER; i++) { + mod[i] <-- LONG_DIV[1][i]; + } + for (var i = 0; i <= M-CHUNK_NUMBER; i++) { + div[i] <-- LONG_DIV[0][i]; + } + component div_range_checks[M - CHUNK_NUMBER + 1]; + for (var i = 0; i <= M-CHUNK_NUMBER; i++) { + div_range_checks[i] = Num2Bits(CHUNK_SIZE); + div_range_checks[i].in <== div[i]; + } + component mod_range_checks[CHUNK_NUMBER]; + for (var i = 0; i < CHUNK_NUMBER; i++) { + mod_range_checks[i] = Num2Bits(CHUNK_SIZE); + mod_range_checks[i].in <== mod[i]; + } + + component mul = BigMult(CHUNK_SIZE, M-CHUNK_NUMBER + 1); + // this might need to be optimized since b has less registers than div + for (var i = 0; i < CHUNK_NUMBER; i++) { + mul.a[i] <== div[i]; + mul.b[i] <== b[i]; + } + for (var i = CHUNK_NUMBER; i <= M-CHUNK_NUMBER; i++) { + mul.a[i] <== div[i]; + mul.b[i] <== 0; + } + + // mul shouldn't have more registers than a + for (var i = M; i < 2*(M-CHUNK_NUMBER)+2; i++) { + mul.out[i] === 0; + } + + component add = BigAdd(CHUNK_SIZE, M); + for (var i = 0; i < M; i++) { + add.a[i] <== mul.out[i]; + if (i < CHUNK_NUMBER) { + add.b[i] <== mod[i]; + } else { + add.b[i] <== 0; + } + } + + for (var i = 0; i < M; i++) { + add.out[i] === a[i]; + } + add.out[M] === 0; + + component lt = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER); + for (var i = 0; i < CHUNK_NUMBER; i++) { + lt.a[i] <== mod[i]; + lt.b[i] <== b[i]; + } + lt.out === 1; +} + + + +// a[i], b[i] in 0... 2**CHUNK_SIZE-1 +// represent a = a[0] + a[1] * 2**CHUNK_SIZE + .. + a[CHUNK_NUMBER - 1] * 2**(CHUNK_SIZE * CHUNK_NUMBER) +// calculates (a+b)%P, where 0<= a,b < P +template BigAddModP(CHUNK_SIZE, CHUNK_NUMBER){ + assert(CHUNK_SIZE <= 252); + signal input a[CHUNK_NUMBER]; + signal input b[CHUNK_NUMBER]; + signal input p[CHUNK_NUMBER]; + signal output out[CHUNK_NUMBER]; + + component add = BigAdd(CHUNK_SIZE,CHUNK_NUMBER); + for (var i = 0; i < CHUNK_NUMBER; i++) { + add.a[i] <== a[i]; + add.b[i] <== b[i]; + } + component lt = BigLessThan(CHUNK_SIZE, CHUNK_NUMBER+1); + for (var i = 0; i < CHUNK_NUMBER; i++) { + lt.a[i] <== add.out[i]; + lt.b[i] <== p[i]; + } + lt.a[CHUNK_NUMBER] <== add.out[CHUNK_NUMBER]; + lt.b[CHUNK_NUMBER] <== 0; + + component sub = BigSub(CHUNK_SIZE,CHUNK_NUMBER+1); + for (var i = 0; i < CHUNK_NUMBER; i++) { + sub.a[i] <== add.out[i]; + sub.b[i] <== (1-lt.out) * p[i]; + } + sub.a[CHUNK_NUMBER] <== add.out[CHUNK_NUMBER]; + sub.b[CHUNK_NUMBER] <== 0; + + sub.out[CHUNK_NUMBER] === 0; + for (var i = 0; i < CHUNK_NUMBER; i++) { + out[i] <== sub.out[i]; + } +} + +/* +Inputs: + - BigInts a, b + - Assume a >= b +Output: + - BigInt out = a - b + - underflow = how much is borrowed at the highest digit of subtraction, only nonzero if a < b +*/ +template BigSub(CHUNK_SIZE, CHUNK_NUMBER) { + assert(CHUNK_SIZE <= 252); + signal input a[CHUNK_NUMBER]; + signal input b[CHUNK_NUMBER]; + signal output out[CHUNK_NUMBER]; + signal output underflow; + + component unit0 = ModSub(CHUNK_SIZE); + unit0.a <== a[0]; + unit0.b <== b[0]; + out[0] <== unit0.out; + + component unit[CHUNK_NUMBER - 1]; + for (var i = 1; i < CHUNK_NUMBER; i++) { + unit[i - 1] = ModSubThree(CHUNK_SIZE); + unit[i - 1].a <== a[i]; + unit[i - 1].b <== b[i]; + if (i == 1) { + unit[i - 1].c <== unit0.borrow; + } else { + unit[i - 1].c <== unit[i - 2].borrow; + } + out[i] <== unit[i - 1].out; + } + underflow <== unit[CHUNK_NUMBER - 2].borrow; +} + +// calculates (a - b) % p, where a, b < p +// note: does not assume a >= b +template BigSubModP(CHUNK_SIZE, CHUNK_NUMBER){ + assert(CHUNK_SIZE <= 252); + signal input a[CHUNK_NUMBER]; + signal input b[CHUNK_NUMBER]; + signal input p[CHUNK_NUMBER]; + signal output out[CHUNK_NUMBER]; + component sub = BigSub(CHUNK_SIZE, CHUNK_NUMBER); + for (var i = 0; i < CHUNK_NUMBER; i++){ + sub.a[i] <== a[i]; + sub.b[i] <== b[i]; + } + signal flag; + flag <== sub.underflow; + component add = BigAdd(CHUNK_SIZE, CHUNK_NUMBER); + for (var i = 0; i < CHUNK_NUMBER; i++){ + add.a[i] <== sub.out[i]; + add.b[i] <== p[i]; + } + signal tmp[CHUNK_NUMBER]; + for (var i = 0; i < CHUNK_NUMBER; i++){ + tmp[i] <== (1 - flag) * sub.out[i]; + out[i] <== tmp[i] + flag * add.out[i]; + } +} + +// Note: deprecated +template BigMultModP(CHUNK_SIZE, CHUNK_NUMBER) { + assert(CHUNK_SIZE <= 252); + signal input a[CHUNK_NUMBER]; + signal input b[CHUNK_NUMBER]; + signal input p[CHUNK_NUMBER]; + signal output out[CHUNK_NUMBER]; + + component big_mult = BigMult(CHUNK_SIZE, CHUNK_NUMBER); + for (var i = 0; i < CHUNK_NUMBER; i++) { + big_mult.a[i] <== a[i]; + big_mult.b[i] <== b[i]; + } + component big_mod = BigMod(CHUNK_SIZE, CHUNK_NUMBER); + for (var i = 0; i < 2 * CHUNK_NUMBER; i++) { + big_mod.a[i] <== big_mult.out[i]; + } + for (var i = 0; i < CHUNK_NUMBER; i++) { + big_mod.b[i] <== p[i]; + } + for (var i = 0; i < CHUNK_NUMBER; i++) { + out[i] <== big_mod.mod[i]; + } +} + +template BigModInv(CHUNK_SIZE, CHUNK_NUMBER) { + assert(CHUNK_SIZE <= 252); + signal input in[CHUNK_NUMBER]; + signal input p[CHUNK_NUMBER]; + signal output out[CHUNK_NUMBER]; + + // length CHUNK_NUMBER + var inv[150] = mod_inv(CHUNK_SIZE, CHUNK_NUMBER, in, p); + for (var i = 0; i < CHUNK_NUMBER; i++) { + out[i] <-- inv[i]; + } + component rangeChecks[CHUNK_NUMBER]; + for (var i = 0; i < CHUNK_NUMBER; i++) { + rangeChecks[i] = Num2Bits(CHUNK_SIZE); + rangeChecks[i].in <== out[i]; + } + + component mult = BigMult(CHUNK_SIZE, CHUNK_NUMBER); + for (var i = 0; i < CHUNK_NUMBER; i++) { + mult.a[i] <== in[i]; + mult.b[i] <== out[i]; + } + component mod = BigMod(CHUNK_SIZE, CHUNK_NUMBER); + for (var i = 0; i < 2 * CHUNK_NUMBER; i++) { + mod.a[i] <== mult.out[i]; + } + for (var i = 0; i < CHUNK_NUMBER; i++) { + mod.b[i] <== p[i]; + } + mod.mod[0] === 1; + for (var i = 1; i < CHUNK_NUMBER; i++) { + mod.mod[i] === 0; + } +} + +/* Taken from circom-ecdsa +Input: + - in = in[0] + in[1] * X + ... + in[CHUNK_NUMBER-1] * X^{CHUNK_NUMBER-1} as signed overflow representation + - Assume each in[i] is in range (-2^{M-1}, 2^{M-1}) +Implements: + - constrain that in[] evaluated at X = 2^CHUNK_SIZE as a big integer equals zero +*/ +template CheckCarryToZero(CHUNK_SIZE, M, CHUNK_NUMBER) { + assert(CHUNK_NUMBER >= 2); + + var EPSILON = 1; // see below for why 1 is ok + + signal input in[CHUNK_NUMBER]; + + signal carry[CHUNK_NUMBER]; + component carryRangeChecks[CHUNK_NUMBER]; + for (var i = 0; i < CHUNK_NUMBER-1; i++){ + carryRangeChecks[i] = Num2Bits(M + EPSILON - CHUNK_SIZE); + if( i == 0 ){ + carry[i] <-- in[i] / (1<= CHUNK_NUMBER, we precompute X^i = r[i] mod P, where r[i] represented as CHUNK_NUMBER registers with r[i][j] in [0, 2^CHUNK_SIZE) + - in[i] * X^i is replaced by sum_j in[i] * r[i][j] * X^j +Notes: + - If each in[i] has absolute value 2*l-1) ? 2*CHUNK_NUMBER-1 : 2*l-1; + var pow[k2][k2]; + for(var i = 0; i L_A + L_B -1) ? K_A + K_B - 1 : L_A + L_B -1; + var pow[k2][k2]; + for(var i = 0; i B) + return A; + return B; +} + +function log_ceil(CHUNK_SIZE) { + var n_temp = CHUNK_SIZE; + for (var i = 0; i < 254; i++) { + if (n_temp == 0) { + return i; + } + n_temp = n_temp \ 2; + } + return 254; +} + +function SplitFn(IN, CHUNK_SIZE, M) { + return [IN % (1 << CHUNK_SIZE), (IN \ (1 << CHUNK_SIZE)) % (1 << M)]; +} + +function SplitThreeFn(IN, CHUNK_SIZE, M, CHUNK_NUMBER) { + return [IN % (1 << CHUNK_SIZE), (IN \ (1 << CHUNK_SIZE)) % (1 << M), (IN \ (1 << CHUNK_SIZE + M)) % (1 << CHUNK_NUMBER)]; +} + +// 1 if true, 0 if false +function long_gt(CHUNK_SIZE, CHUNK_NUMBER, A, B) { + for (var i = CHUNK_NUMBER - 1; i >= 0; i--) { + if (A[i] > B[i]) { + return 1; + } + if (A[i] < B[i]) { + return 0; + } + } + return 0; +} + +function long_is_zero(CHUNK_NUMBER, A){ + for(var idx=0; idx K2 +// output has K1+1 registers +function long_add_unequal(CHUNK_SIZE, K1, K2, A, B){ + var carry = 0; + var sum[150]; + for(var i=0; i= B +function long_sub(CHUNK_SIZE, CHUNK_NUMBER, A, B) { + var diff[150]; + var borrow[150]; + for (var i = 0; i < CHUNK_NUMBER; i++) { + if (i == 0) { + if (A[i] >= B[i]) { + diff[i] = A[i] - B[i]; + borrow[i] = 0; + } else { + diff[i] = A[i] - B[i] + (1 << CHUNK_SIZE); + borrow[i] = 1; + } + } else { + if (A[i] >= B[i] + borrow[i - 1]) { + diff[i] = A[i] - B[i] - borrow[i - 1]; + borrow[i] = 0; + } else { + diff[i] = (1 << CHUNK_SIZE) + A[i] - B[i] - borrow[i - 1]; + borrow[i] = 1; + } + } + } + return diff; +} + +// A is A CHUNK_SIZE-bit scalar +// B has CHUNK_NUMBER registers +function long_scalar_mult(CHUNK_SIZE, CHUNK_NUMBER, A, B) { + var out[150]; + for (var i = 0; i < 150; i++) { + out[i] = 0; + } + for (var i = 0; i < CHUNK_NUMBER; i++) { + var temp = out[i] + (A * B[i]); + out[i] = temp % (1 << CHUNK_SIZE); + out[i + 1] = out[i + 1] + temp \ (1 << CHUNK_SIZE); + } + return out; +} + + +// CHUNK_SIZE bits per register +// A has CHUNK_NUMBER + M registers +// B has CHUNK_NUMBER registers +// out[0] has length M + 1 -- quotient +// out[1] has length CHUNK_NUMBER -- remainder +// implements algorithm of https://people.eecs.berkeley.edu/~fateman/282/F%20Wright%20notes/week4.pdf +// B[CHUNK_NUMBER-1] must be nonzero! +function long_div2(CHUNK_SIZE, CHUNK_NUMBER, M, A, B){ + var out[2][150]; + // assume CHUNK_NUMBER+M < 150 + var remainder[150]; + for (var i = 0; i < M + CHUNK_NUMBER; i++) { + remainder[i] = A[i]; + } + + var dividend[150]; + for (var i = M; i >= 0; i--) { + if (i == M) { + dividend[CHUNK_NUMBER] = 0; + for (var j = CHUNK_NUMBER - 1; j >= 0; j--) { + dividend[j] = remainder[j + M]; + } + } else { + for (var j = CHUNK_NUMBER; j >= 0; j--) { + dividend[j] = remainder[j + i]; + } + } + out[0][i] = short_div(CHUNK_SIZE, CHUNK_NUMBER, dividend, B); + var MULT_SHIFT[150] = long_scalar_mult(CHUNK_SIZE, CHUNK_NUMBER, out[0][i], B); + var subtrahend[150]; + for (var j = 0; j < M + CHUNK_NUMBER; j++) { + subtrahend[j] = 0; + } + for (var j = 0; j <= CHUNK_NUMBER; j++) { + if (i + j < M + CHUNK_NUMBER) { + subtrahend[i + j] = MULT_SHIFT[j]; + } + } + remainder = long_sub(CHUNK_SIZE, M + CHUNK_NUMBER, remainder, subtrahend); + } + for (var i = 0; i < CHUNK_NUMBER; i++) { + out[1][i] = remainder[i]; + } + out[1][CHUNK_NUMBER] = 0; + return out; +} + +function long_div(CHUNK_SIZE, CHUNK_NUMBER, A, B) { + return long_div2(CHUNK_SIZE, CHUNK_NUMBER, CHUNK_NUMBER, A, B); +} + +// CHUNK_SIZE bits per register +// A has CHUNK_NUMBER + 1 registers +// B has CHUNK_NUMBER registers +// assumes leading digit of B is at least 2^(CHUNK_SIZE - 1) +// 0 <= A < (2**CHUNK_SIZE) * B +function short_div_norm(CHUNK_SIZE, CHUNK_NUMBER, A, B) { + var qhat = (A[CHUNK_NUMBER] * (1 << CHUNK_SIZE) + A[CHUNK_NUMBER - 1]) \ B[CHUNK_NUMBER - 1]; + if (qhat > (1 << CHUNK_SIZE) - 1) { + qhat = (1 << CHUNK_SIZE) - 1; + } + + var MULT[150] = long_scalar_mult(CHUNK_SIZE, CHUNK_NUMBER, qhat, B); + if (long_gt(CHUNK_SIZE, CHUNK_NUMBER + 1, MULT, A) == 1) { + MULT = long_sub(CHUNK_SIZE, CHUNK_NUMBER + 1, MULT, B); + if (long_gt(CHUNK_SIZE, CHUNK_NUMBER + 1, MULT, A) == 1) { + return qhat - 2; + } else { + return qhat - 1; + } + } else { + return qhat; + } +} + +// CHUNK_SIZE bits per register +// A has CHUNK_NUMBER + 1 registers +// B has CHUNK_NUMBER registers +// assumes leading digit of B is non-zero +// 0 <= A < B * 2^CHUNK_SIZE +function short_div(CHUNK_SIZE, CHUNK_NUMBER, A, B) { + var scale = (1 << CHUNK_SIZE) \ (1 + B[CHUNK_NUMBER - 1]); + // CHUNK_NUMBER + 2 registers now + var norm_a[150] = long_scalar_mult(CHUNK_SIZE, CHUNK_NUMBER + 1, scale, A); + // CHUNK_NUMBER + 1 registers now + var norm_b[150] = long_scalar_mult(CHUNK_SIZE, CHUNK_NUMBER, scale, B); + + var ret; + if (norm_b[CHUNK_NUMBER] != 0) { + ret = short_div_norm(CHUNK_SIZE, CHUNK_NUMBER + 1, norm_a, norm_b); + } else { + ret = short_div_norm(CHUNK_SIZE, CHUNK_NUMBER, norm_a, norm_b); + } + return ret; +} + +// A = a0 + a1 * X + ... + A[CHUNK_NUMBER-1] * X^{CHUNK_NUMBER-1} with X = 2^CHUNK_SIZE +// a_i can be "negative" assume a_i IN (-2^251, 2^251) +// output is the value of A with a_i all of the same sign +// out[150] = 0 if positive, 1 if negative +function signed_long_to_short(CHUNK_SIZE, CHUNK_NUMBER, A){ + var out[151]; + var MAXL = 150; + var temp[151]; + + // is A positive? + for(var i=0; i= 0){ // circom automatically takes care of signs IN comparator + out[i] = temp[i] % X; + temp[i+1] += temp[i] \ X; + }else{ + var borrow = (-temp[i] + X - 1 ) \ X; + out[i] = temp[i] + borrow * X; + temp[i+1] -= borrow; + } + } + if(temp[MAXL] >= 0){ + assert(temp[MAXL]==0); // otherwise not enough registers! + out[MAXL] = 0; + return out; + } + + // must be negative then, reset + for(var i=0; i 1) { + var sumAndCarry[2] = SplitFn(SPLIT[0][1] + SPLIT[1][0], CHUNK_SIZE, CHUNK_SIZE); + out[1] = sumAndCarry[0]; + carry[1] = sumAndCarry[1]; + } + if (2 * CHUNK_NUMBER - 1 > 2) { + for (var i = 2; i < 2 * CHUNK_NUMBER - 1; i++) { + var sumAndCarry[2] = SplitFn(SPLIT[i][0] + SPLIT[i-1][1] + SPLIT[i-2][2] + carry[i-1], CHUNK_SIZE, CHUNK_SIZE); + out[i] = sumAndCarry[0]; + carry[i] = sumAndCarry[1]; + } + out[2 * CHUNK_NUMBER - 1] = SPLIT[2*CHUNK_NUMBER-2][1] + SPLIT[2*CHUNK_NUMBER-3][2] + carry[2*CHUNK_NUMBER-2]; + } + return out; +} + + +// CHUNK_SIZE bits per register +// A and B both have SMALL_CHUNK_SIZE x CHUNK_NUMBER registers +// out has length 2l - 1 x 2k +// adapted from BigMultShortLong2D and LongToShortNoEndCarry2 witness computation +function prod2D(CHUNK_SIZE, CHUNK_NUMBER, SMALL_CHUNK_SIZE, A, B) { + // first compute the intermediate values. taken from BigMulShortLong + var prod_val[20][150]; // length is 2l - 1 by 2k - 1 + for (var i = 0; i < 2 * CHUNK_NUMBER - 1; i++) { + for (var j = 0; j < 2 * SMALL_CHUNK_SIZE - 1; j ++) { + prod_val[j][i] = 0; + } + } + for (var i1 = 0; i1 < CHUNK_NUMBER; i1 ++) { + for (var i2 = 0; i2 < CHUNK_NUMBER; i2 ++) { + for (var j1 = 0; j1 < SMALL_CHUNK_SIZE; j1 ++) { + for (var j2 = 0; j2 < SMALL_CHUNK_SIZE; j2 ++) { + prod_val[j1+j2][i1+i2] = prod_val[j1+j2][i1+i2] + A[j1][i1] * B[j2][i2]; + } + } + } + } + + // now do A bunch of carrying to make sure registers not overflowed. taken from LongToShortNoEndCarry2 + var out[20][150]; // length is 2 * SMALL_CHUNK_SIZE by 2 * CHUNK_NUMBER + + var SPLIT[20][150][3]; // second dimension has length 2 * CHUNK_NUMBER - 1 + for (var j = 0; j < 2 * SMALL_CHUNK_SIZE - 1; j ++) { + for (var i = 0; i < 2 * CHUNK_NUMBER - 1; i++) { + SPLIT[j][i] = SplitThreeFn(prod_val[j][i], CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE); + } + } + + var carry[20][150]; // length is 2l-1 x 2k + var sumAndCarry[20][2]; + for ( var j = 0; j < 2 * SMALL_CHUNK_SIZE - 1; j ++) { + carry[j][0] = 0; + out[j][0] = SPLIT[j][0][0]; + if (2 * CHUNK_NUMBER - 1 > 1) { + sumAndCarry[j] = SplitFn(SPLIT[j][0][1] + SPLIT[j][1][0], CHUNK_SIZE, CHUNK_SIZE); + out[j][1] = sumAndCarry[j][0]; + carry[j][1] = sumAndCarry[j][1]; + } + if (2 * CHUNK_NUMBER - 1 > 2) { + for (var i = 2; i < 2 * CHUNK_NUMBER - 1; i++) { + sumAndCarry[j] = SplitFn(SPLIT[j][i][0] + SPLIT[j][i-1][1] + SPLIT[j][i-2][2] + carry[j][i-1], CHUNK_SIZE, CHUNK_SIZE); + out[j][i] = sumAndCarry[j][0]; + carry[j][i] = sumAndCarry[j][1]; + } + out[j][2 * CHUNK_NUMBER - 1] = SPLIT[j][2*CHUNK_NUMBER-2][1] + SPLIT[j][2*CHUNK_NUMBER-3][2] + carry[j][2*CHUNK_NUMBER-2]; + } + } + + return out; +} + +// Put all modular arithmetic, aka F_p field stuff, at the end + +function long_add_mod(CHUNK_SIZE, CHUNK_NUMBER, A, B, P) { + var sum[150] = long_add(CHUNK_SIZE,CHUNK_NUMBER,A,B); + var temp[2][150] = long_div2(CHUNK_SIZE,CHUNK_NUMBER,1,sum,P); + return temp[1]; +} + +function long_sub_mod(CHUNK_SIZE, CHUNK_NUMBER, A, B, P) { + if(long_gt(CHUNK_SIZE, CHUNK_NUMBER, B, A) == 1){ + return long_add(CHUNK_SIZE, CHUNK_NUMBER, A, long_sub(CHUNK_SIZE,CHUNK_NUMBER,P,B)); + }else{ + return long_sub(CHUNK_SIZE, CHUNK_NUMBER, A, B); + } +} + +function prod_mod(CHUNK_SIZE, CHUNK_NUMBER, A, B, P) { + var prod[150] = prod(CHUNK_SIZE,CHUNK_NUMBER,A,B); + var temp[2][150] = long_div(CHUNK_SIZE,CHUNK_NUMBER,prod,P); + return temp[1]; +} + + +// CHUNK_SIZE bits per register +// A has CHUNK_NUMBER registers +// P has CHUNK_NUMBER registers +// EXP has CHUNK_NUMBER registers +// CHUNK_NUMBER * CHUNK_SIZE <= 500 +// P is A prime +// computes A^EXP mod P +function mod_exp(CHUNK_SIZE, CHUNK_NUMBER, A, P, EXP) { + var eBits[500]; // length is CHUNK_NUMBER * CHUNK_SIZE + var BIT_LENGTH; + for (var i = 0; i < CHUNK_NUMBER; i++) { + for (var j = 0; j < CHUNK_SIZE; j++) { + eBits[j + CHUNK_SIZE * i] = (EXP[i] >> j) & 1; + if(eBits[j + CHUNK_SIZE * i] == 1) + BIT_LENGTH = j + CHUNK_SIZE * i + 1; + } + } + + var out[150]; // length is CHUNK_NUMBER + for (var i = 0; i < 150; i++) { + out[i] = 0; + } + out[0] = 1; + + // repeated squaring + for (var i = BIT_LENGTH-1; i >= 0; i--) { + // multiply by A if bit is 0 + if (eBits[i] == 1) { + var temp[150]; // length 2 * CHUNK_NUMBER + temp = prod(CHUNK_SIZE, CHUNK_NUMBER, out, A); + var temp2[2][150]; + temp2 = long_div(CHUNK_SIZE, CHUNK_NUMBER, temp, P); + out = temp2[1]; + } + + // square, unless we're at the end + if (i > 0) { + var temp[150]; // length 2 * CHUNK_NUMBER + temp = prod(CHUNK_SIZE, CHUNK_NUMBER, out, out); + var temp2[2][150]; + temp2 = long_div(CHUNK_SIZE, CHUNK_NUMBER, temp, P); + out = temp2[1]; + } + + } + return out; +} + +// CHUNK_SIZE bits per register +// A has CHUNK_NUMBER registers +// P has CHUNK_NUMBER registers +// CHUNK_NUMBER * CHUNK_SIZE <= 500 +// P is A prime +// if A == 0 mod P, returns 0 +// else computes inv = A^(P-2) mod P +function mod_inv(CHUNK_SIZE, CHUNK_NUMBER, A, P) { + var isZero = 1; + for (var i = 0; i < CHUNK_NUMBER; i++) { + if (A[i] != 0) { + isZero = 0; + } + } + if (isZero == 1) { + var ret[150]; + for (var i = 0; i < CHUNK_NUMBER; i++) { + ret[i] = 0; + } + return ret; + } + + var pCopy[150]; + for (var i = 0; i < 150; i++) { + if (i < CHUNK_NUMBER) { + pCopy[i] = P[i]; + } else { + pCopy[i] = 0; + } + } + + var two[150]; + for (var i = 0; i < 150; i++) { + two[i] = 0; + } + two[0] = 2; + + var pMinusTwo[150]; + pMinusTwo = long_sub(CHUNK_SIZE, CHUNK_NUMBER, pCopy, two); // length CHUNK_NUMBER + var out[150]; + out = mod_exp(CHUNK_SIZE, CHUNK_NUMBER, A, pCopy, pMinusTwo); + return out; +} + + +function long_div_5args(n, k, m, a, b){ + var out[2][100]; + m += k; + while (b[k-1] == 0) { + out[1][k] = 0; + k--; + assert(k > 0); + } + m -= k; + + var remainder[100]; + for (var i = 0; i < m + k; i++) { + remainder[i] = a[i]; + } + + var mult[200]; + var dividend[200]; + for (var i = m; i >= 0; i--) { + if (i == m) { + dividend[k] = 0; + for (var j = k - 1; j >= 0; j--) { + dividend[j] = remainder[j + m]; + } + } else { + for (var j = k; j >= 0; j--) { + dividend[j] = remainder[j + i]; + } + } + + out[0][i] = short_div(n, k, dividend, b); + + var mult_shift[100] = long_scalar_mult_100(n, k, out[0][i], b); + var subtrahend[200]; + for (var j = 0; j < m + k; j++) { + subtrahend[j] = 0; + } + for (var j = 0; j <= k; j++) { + if (i + j < m + k) { + subtrahend[i + j] = mult_shift[j]; + } + } + remainder = long_sub_100(n, m + k, remainder, subtrahend); + } + for (var i = 0; i < k; i++) { + out[1][i] = remainder[i]; + } + out[1][k] = 0; + + return out; +} + +// a is a n-bit scalar +// b has k registers +function long_scalar_mult_100(n, k, a, b) { + var out[100]; + for (var i = 0; i < 100; i++) { + out[i] = 0; + } + for (var i = 0; i < k; i++) { + var temp = out[i] + (a * b[i]); + out[i] = temp % (1 << n); + out[i + 1] = out[i + 1] + temp \ (1 << n); + } + return out; +} + +// n bits per register +// a has k registers +// b has k registers +// a >= b +function long_sub_100(n, k, a, b) { + var diff[100]; + var borrow[100]; + for (var i = 0; i < k; i++) { + if (i == 0) { + if (a[i] >= b[i]) { + diff[i] = a[i] - b[i]; + borrow[i] = 0; + } else { + diff[i] = a[i] - b[i] + (1 << n); + borrow[i] = 1; + } + } else { + if (a[i] >= b[i] + borrow[i - 1]) { + diff[i] = a[i] - b[i] - borrow[i - 1]; + borrow[i] = 0; + } else { + diff[i] = (1 << n) + a[i] - b[i] - borrow[i - 1]; + borrow[i] = 1; + } + } + } + return diff; +} \ No newline at end of file diff --git a/circuits/circuits/utils/other/bigint_4x64_mult.circom b/circuits/circuits/utils/other/bigint_4x64_mult.circom new file mode 100644 index 00000000..58bd0af6 --- /dev/null +++ b/circuits/circuits/utils/other/bigint_4x64_mult.circom @@ -0,0 +1,101 @@ +pragma circom 2.0.2; + +include "bigint.circom"; + +template A2NoCarry() { + signal input a[4]; + + // these representations have overflowed, nonnegative registers + signal output a2[7]; + component a2Comp = BigMultNoCarry(64, 64, 64, 4, 4); + for (var i = 0; i < 4; i++) { + a2Comp.a[i] <== a[i]; + a2Comp.b[i] <== a[i]; + } + for (var i = 0; i < 7; i++) { + a2[i] <== a2Comp.out[i]; // 130 bits + } +} + +template A3NoCarry() { + signal input a[4]; + + // these representations have overflowed, nonnegative registers + signal a2[7]; + component a2Comp = BigMultNoCarry(64, 64, 64, 4, 4); + for (var i = 0; i < 4; i++) { + a2Comp.a[i] <== a[i]; + a2Comp.b[i] <== a[i]; + } + for (var i = 0; i < 7; i++) { + a2[i] <== a2Comp.out[i]; // 130 bits + } + signal output a3[10]; + component a3Comp = BigMultNoCarry(64, 130, 64, 7, 4); + for (var i = 0; i < 7; i++) { + a3Comp.a[i] <== a2[i]; + } + for (var i = 0; i < 4; i++) { + a3Comp.b[i] <== a[i]; + } + for (var i = 0; i < 10; i++) { + a3[i] <== a3Comp.out[i]; // 197 bits + } +} + +template A2B1NoCarry() { + signal input a[4]; + signal input b[4]; + + // these representations have overflowed, nonnegative registers + signal a2[7]; + component a2Comp = BigMultNoCarry(64, 64, 64, 4, 4); + for (var i = 0; i < 4; i++) { + a2Comp.a[i] <== a[i]; + a2Comp.b[i] <== a[i]; + } + for (var i = 0; i < 7; i++) { + a2[i] <== a2Comp.out[i]; // 130 bits + } + + signal output a2b1[10]; + component a2b1Comp = BigMultNoCarry(64, 130, 64, 7, 4); + for (var i = 0; i < 7; i++) { + a2b1Comp.a[i] <== a2[i]; + } + for (var i = 0; i < 4; i++) { + a2b1Comp.b[i] <== b[i]; + } + for (var i = 0; i < 10; i++) { + a2b1[i] <== a2b1Comp.out[i]; // 197 bits + } +} + +template A1B1C1NoCarry() { + signal input a[4]; + signal input b[4]; + signal input c[4]; + + // these representations have overflowed, nonnegative registers + signal a1b1[7]; + component a1b1Comp = BigMultNoCarry(64, 64, 64, 4, 4); + for (var i = 0; i < 4; i++) { + a1b1Comp.a[i] <== a[i]; + a1b1Comp.b[i] <== b[i]; + } + for (var i = 0; i < 7; i++) { + a1b1[i] <== a1b1Comp.out[i]; // 130 bits + } + + signal output a1b1c1[10]; + component a1b1c1Comp = BigMultNoCarry(64, 130, 64, 7, 4); + for (var i = 0; i < 7; i++) { + a1b1c1Comp.a[i] <== a1b1[i]; + } + for (var i = 0; i < 4; i++) { + a1b1c1Comp.b[i] <== c[i]; + } + for (var i = 0; i < 10; i++) { + a1b1c1[i] <== a1b1c1Comp.out[i]; // 197 bits + } +} \ No newline at end of file diff --git a/circuits/circuits/utils/other/bytes.circom b/circuits/circuits/utils/other/bytes.circom new file mode 100644 index 00000000..2ac5249d --- /dev/null +++ b/circuits/circuits/utils/other/bytes.circom @@ -0,0 +1,216 @@ +pragma circom 2.1.9; + +include "circomlib/circuits/bitify.circom"; +include "circomlib/circuits/comparators.circom"; +include "./array.circom"; +include "./functions.circom"; +include "./constants.circom"; + +function computeIntChunkLength(byteLength) { + var packSize = MAX_BYTES_IN_FIELD(); + + var remain = byteLength % packSize; + var numChunks = (byteLength - remain) / packSize; + if (remain > 0) { + numChunks += 1; + } + + return numChunks; +} + + +/// @title PackBytes +/// @notice Pack an array of bytes to numbers that fit in the field +/// @param maxBytes the maximum number of bytes in the input array +/// @input in the input byte array; assumes elements to be bytes +/// @output out the output integer array +template PackBytes(maxBytes) { + var packSize = MAX_BYTES_IN_FIELD(); + var maxInts = computeIntChunkLength(maxBytes); + + signal input in[maxBytes]; + signal output out[maxInts]; + + signal intSums[maxInts][packSize]; + + for (var i = 0; i < maxInts; i++) { + for(var j=0; j < packSize; j++) { + var idx = packSize * i + j; + + // Copy the previous value if we are out of bounds - we take last item as final result + if(idx >= maxBytes) { + intSums[i][j] <== intSums[i][j-1]; + } + // First item of each chunk is the byte itself + else if (j == 0){ + intSums[i][j] <== in[idx]; + } + // Every other item is 256^j * byte + else { + intSums[i][j] <== intSums[i][j-1] + (1 << (8*j)) * in[idx]; + } + } + } + + // Last item of each chunk is the final sum + for (var i = 0; i < maxInts; i++) { + out[i] <== intSums[i][packSize-1]; + } +} + + +/// @title PackByteSubArray +/// @notice Select sub array from the input array and pack it to numbers that fit in the field +/// @notice This is not used in ZK-Email circuits anywhere +/// @param maxArrayLen the maximum number of elements in the input array +/// @param maxSubArrayLen the maximum number of elements in the sub array +/// @input in the input byte array; assumes elements to be bytes +/// @input startIndex the start index of the sub array; assumes to be a valid index +/// @input length the length of the sub array; assumes to fit in `ceil(log2(maxSubArrayLen))` bits +/// @output out the output integer array +template PackByteSubArray(maxArrayLen, maxSubArrayLen) { + assert(maxSubArrayLen < maxArrayLen); + var chunkLength = computeIntChunkLength(maxSubArrayLen); + + signal input in[maxArrayLen]; + signal input startIndex; + signal input length; + + signal output out[chunkLength]; + + component SelectSubArray = SelectSubArray(maxArrayLen, maxSubArrayLen); + SelectSubArray.in <== in; + SelectSubArray.startIndex <== startIndex; + SelectSubArray.length <== length; + + component packer = PackBytes(maxSubArrayLen); + packer.in <== SelectSubArray.out; + + out <== packer.out; +} + + +/// @title DigitBytesToInt +/// @notice Converts a byte array representing digits to an integer +/// @notice Assumes the output number fits in the field +/// @param n The number of bytes in the input array +/// @input in The input byte array; assumes elements are between 48 and 57 (ASCII numbers) +/// @output out The output integer; assumes to fit in the field +template DigitBytesToInt(n) { + signal input in[n]; + + signal output out; + + signal sums[n+1]; + sums[0] <== 0; + + for(var i = 0; i < n; i++) { + sums[i + 1] <== 10 * sums[i] + (in[i] - 48); + } + + out <== sums[n]; +} + +/// NOTE: this circuit is unaudited and should not be used in production +/// @title SplitBytesToWords +/// @notice split an array of bytes into an array of words +/// @notice useful for casting a message or modulus before RSA verification +/// @param l: number of bytes in the input array +/// @param n: number of bits in a word +/// @param k: number of words +/// @input in: array of bytes +/// @output out: array of words +template SplitSignalsToWords (t,l,n,k) { + assert(n*k >= t*l); + + signal input in[l]; + signal output out[k]; + component num2bits[l]; + for (var i = 0 ; i < l ; i++){ + num2bits[i] = Num2Bits(t); + num2bits[i].in <== in[i]; + } + for (var i = 0 ; i < t ; i ++){ + } + component bits2num[k]; + for (var i = 0 ; i < k ; i++){ + bits2num[i] = Bits2Num(n); + + for(var j = 0 ; j < n ; j++){ + if(i*n + j >= l * t){ + bits2num[i].in[j] <== 0; + } + else{ + bits2num[i].in[j] <== num2bits[ (( i * n + j) \ t) ].out[ ((i * n + j) % t)]; + } + } + } + for( var i = 0 ; i< k ; i++){ + out[i] <== bits2num[i].out; + } + +} + +template SplitSignalsToWordsUnsafe (t,l,n,k) { + + signal input in[l]; + signal output out[k]; + component num2bits[l]; + for (var i = 0 ; i < l ; i++){ + num2bits[i] = Num2Bits(t); + num2bits[i].in <== in[i]; + } + for (var i = 0 ; i < t ; i ++){ + } + component bits2num[k]; + for (var i = 0 ; i < k ; i++){ + bits2num[i] = Bits2Num(n); + + for(var j = 0 ; j < n ; j++){ + if(i*n + j >= l * t){ + bits2num[i].in[j] <== 0; + } + else{ + bits2num[i].in[j] <== num2bits[ (( i * n + j) \ t) ].out[ ((i * n + j) % t)]; + } + } + } + for( var i = 0 ; i< k ; i++){ + out[i] <== bits2num[i].out; + } + +} + +/// NOTE: this circuit is unaudited and should not be used in production +/// @title SplitBytesToWords +/// @notice split an array of bytes into an array of words +/// @notice useful for casting a message or modulus before RSA verification +/// @param l: number of bytes in the input array +/// @param n: number of bits in a word +/// @param k: number of words +/// @input in: array of bytes +/// @output out: array of words +template SplitBytesToWords (l,n,k) { + signal input in[l]; + signal output out[k]; + component num2bits[l]; + for (var i = 0 ; i < l ; i++){ + num2bits[i] = Num2Bits(8); + num2bits[i].in <== in[i]; + } + component bits2num[k]; + for (var i = 0 ; i < k ; i++){ + bits2num[i] = Bits2Num(n); + for(var j = 0 ; j < n ; j++){ + if(i*n + j >= 8 * l){ + bits2num[i].in[j] <== 0; + } + else{ + bits2num[i].in[j] <== num2bits[l - (( i * n + j) \ 8) - 1].out[ ((i * n + j) % 8)]; + } + } + } + for( var i = 0 ; i< k ; i++){ + out[i] <== bits2num[i].out; + } +} \ No newline at end of file diff --git a/circuits/circuits/utils/chunk_data.circom b/circuits/circuits/utils/other/chunk_data.circom similarity index 97% rename from circuits/circuits/utils/chunk_data.circom rename to circuits/circuits/utils/other/chunk_data.circom index 760aafb5..54f6907a 100644 --- a/circuits/circuits/utils/chunk_data.circom +++ b/circuits/circuits/utils/other/chunk_data.circom @@ -1,4 +1,4 @@ -pragma circom 2.1.5; +pragma circom 2.1.9; // Converts data into chunk_size chunks of 192 bits, assuming original n, k are 64 and 32. // This is because Poseidon circuit only supports an array of 16 elements. diff --git a/circuits/circuits/utils/other/constants.circom b/circuits/circuits/utils/other/constants.circom new file mode 100644 index 00000000..29776904 --- /dev/null +++ b/circuits/circuits/utils/other/constants.circom @@ -0,0 +1,15 @@ +pragma circom 2.1.9; + + +function EMAIL_ADDR_MAX_BYTES() { + return 256; +} + +function DOMAIN_MAX_BYTES() { + return 255; +} + +// Field support maximum of ~253 bit +function MAX_BYTES_IN_FIELD() { + return 31; +} diff --git a/circuits/circuits/utils/other/fp.circom b/circuits/circuits/utils/other/fp.circom new file mode 100644 index 00000000..602e5b7b --- /dev/null +++ b/circuits/circuits/utils/other/fp.circom @@ -0,0 +1,165 @@ +pragma circom 2.1.9; + +include "circomlib/circuits/bitify.circom"; +include "circomlib/circuits/comparators.circom"; +include "circomlib/circuits/sign.circom"; +include "./bigInt.circom"; +include "./bigIntFunc.circom"; + + +/// @title FpMul +/// @notice Multiple two numbers in Fp +/// @param a Input 1 to FpMul; assumes to consist of `k` chunks, each of which must fit in `n` bits +/// @param b Input 2 to FpMul; assumes to consist of `k` chunks, each of which must fit in `n` bits +/// @param p The modulus; assumes to consist of `k` chunks, each of which must fit in `n` bits +/// @output out The result of the FpMul +template FpMul(n, k) { + assert(n + n + log_ceil(k) + 2 <= 252); + + signal input a[k]; + signal input b[k]; + signal input p[k]; + + signal output out[k]; + + signal v_ab[2*k-1]; + for (var x = 0; x < 2*k-1; x++) { + var v_a = poly_eval(k, a, x); + var v_b = poly_eval(k, b, x); + v_ab[x] <== v_a * v_b; + } + + var ab[200] = poly_interp(2*k-1, v_ab); + // ab_proper has length 2*k + var ab_proper[100] = getProperRepresentation(n + n + log_ceil(k), n, 2*k-1, ab); + + var long_div_out[2][100] = long_div_5args(n, k, k, ab_proper, p); + + // Since we're only computing a*b, we know that q < p will suffice, so we + // know it fits into k chunks and can do size n range checks. + signal q[k]; + component q_range_check[k]; + signal r[k]; + component r_range_check[k]; + for (var i = 0; i < k; i++) { + q[i] <-- long_div_out[0][i]; + q_range_check[i] = Num2Bits(n); + q_range_check[i].in <== q[i]; + + r[i] <-- long_div_out[1][i]; + r_range_check[i] = Num2Bits(n); + r_range_check[i].in <== r[i]; + } + + signal v_pq_r[2*k-1]; + for (var x = 0; x < 2*k-1; x++) { + var v_p = poly_eval(k, p, x); + var v_q = poly_eval(k, q, x); + var v_r = poly_eval(k, r, x); + v_pq_r[x] <== v_p * v_q + v_r; + } + + signal v_t[2*k-1]; + for (var x = 0; x < 2*k-1; x++) { + v_t[x] <== v_ab[x] - v_pq_r[x]; + } + + var t[200] = poly_interp(2*k-1, v_t); + component tCheck = CheckCarryToZero(n, n + n + log_ceil(k) + 2, 2*k-1); + for (var i = 0; i < 2*k-1; i++) { + tCheck.in[i] <== t[i]; + } + + for (var i = 0; i < k; i++) { + out[i] <== r[i]; + } +} +function div_ceil(m, n) { + var ret = 0; + if (m % n == 0) { + ret = m \ n; + } else { + ret = m \ n + 1; + } + return ret; +} + +// m bits per overflowed register (values are potentially negative) +// n bits per properly-sized register +// in has k registers +// out has k + ceil(m/n) - 1 + 1 registers. highest-order potentially negative, +// all others are positive +// - 1 since the last register is included in the last ceil(m/n) array +// + 1 since the carries from previous registers could push you over +function getProperRepresentation(m, n, k, in) { + var ceilMN = div_ceil(m, n); + + var out[100]; // should be out[k + ceilMN] + assert(k + ceilMN < 100); + for (var i = 0; i < k; i++) { + out[i] = in[i]; + } + for (var i = k; i < 100; i++) { + out[i] = 0; + } + assert(n <= m); + for (var i = 0; i+1 < k + ceilMN; i++) { + assert((1 << m) >= out[i] && out[i] >= -(1 << m)); + var shifted_val = out[i] + (1 << m); + assert(0 <= shifted_val && shifted_val <= (1 << (m+1))); + out[i] = shifted_val & ((1 << n) - 1); + out[i+1] += (shifted_val >> n) - (1 << (m - n)); + } + + return out; +} + +// Evaluate polynomial a at point x +function poly_eval(len, a, x) { + var v = 0; + for (var i = 0; i < len; i++) { + v += a[i] * (x ** i); + } + return v; +} + +// Interpolate a degree len-1 polynomial given its evaluations at 0..len-1 +function poly_interp(len, v) { + assert(len <= 200); + var out[200]; + for (var i = 0; i < len; i++) { + out[i] = 0; + } + + // Product_{i=0..len-1} (x-i) + var full_poly[201]; + full_poly[0] = 1; + for (var i = 0; i < len; i++) { + full_poly[i+1] = 0; + for (var j = i; j >= 0; j--) { + full_poly[j+1] += full_poly[j]; + full_poly[j] *= -i; + } + } + + for (var i = 0; i < len; i++) { + var cur_v = 1; + for (var j = 0; j < len; j++) { + if (i == j) { + // do nothing + } else { + cur_v *= i-j; + } + } + cur_v = v[i] / cur_v; + + var cur_rem = full_poly[len]; + for (var j = len-1; j >= 0; j--) { + out[j] += cur_v * cur_rem; + cur_rem = full_poly[j] + i * cur_rem; + } + assert(cur_rem == 0); + } + + return out; +} diff --git a/circuits/circuits/utils/other/functions.circom b/circuits/circuits/utils/other/functions.circom new file mode 100644 index 00000000..f9bb802e --- /dev/null +++ b/circuits/circuits/utils/other/functions.circom @@ -0,0 +1,17 @@ +pragma circom 2.1.9; + +/// @function log2Ceil +/// @notice Calculate log2 of a number and round it up +/// @param a The input value +/// @return The result of the log2Ceil +function log2Ceil(a) { + var n = a - 1; + var r = 0; + + while (n > 0) { + r++; + n \= 2; + } + + return r; +} diff --git a/circuits/circuits/utils/getCommonLength.circom b/circuits/circuits/utils/other/getCommonLength.circom similarity index 98% rename from circuits/circuits/utils/getCommonLength.circom rename to circuits/circuits/utils/other/getCommonLength.circom index 3be58107..8aacb1ad 100644 --- a/circuits/circuits/utils/getCommonLength.circom +++ b/circuits/circuits/utils/other/getCommonLength.circom @@ -1,4 +1,4 @@ -pragma circom 2.1.5; +pragma circom 2.1.9; include "circomlib/circuits/comparators.circom"; include "circomlib/circuits/bitify.circom"; diff --git a/circuits/circuits/utils/smt.circom b/circuits/circuits/utils/other/smt.circom similarity index 95% rename from circuits/circuits/utils/smt.circom rename to circuits/circuits/utils/other/smt.circom index 612d106a..7b102c44 100644 --- a/circuits/circuits/utils/smt.circom +++ b/circuits/circuits/utils/other/smt.circom @@ -1,9 +1,9 @@ -pragma circom 2.1.5; +pragma circom 2.1.9; include "circomlib/circuits/poseidon.circom"; include "circomlib/circuits/comparators.circom"; include "circomlib/circuits/bitify.circom"; -include "@zk-email/circuits/utils/array.circom"; +include "./array.circom"; include "binary-merkle-root.circom"; include "getCommonLength.circom"; diff --git a/circuits/circuits/utils/passport/computeCommitment.circom b/circuits/circuits/utils/passport/computeCommitment.circom new file mode 100644 index 00000000..1d05cddb --- /dev/null +++ b/circuits/circuits/utils/passport/computeCommitment.circom @@ -0,0 +1,31 @@ +pragma circom 2.1.9; + +include "circomlib/circuits/poseidon.circom"; +include "../other/bytes.circom"; +include "./customHashers.circom"; + +template ComputeCommitment() { + + signal input secret; + signal input attestation_id; + signal input leaf; + signal input dg1[93]; + signal input dg2_hash[64]; + signal output out; + + component poseidon_hasher = Poseidon(7); + poseidon_hasher.inputs[0] <== secret; + poseidon_hasher.inputs[1] <== attestation_id; + poseidon_hasher.inputs[2] <== leaf; + + signal dg1_packed[3] <== PackBytes(93)(dg1); + for (var i = 0; i < 3; i++) { + poseidon_hasher.inputs[i + 3] <== dg1_packed[i]; + } + + signal dg2Hash2 <== CustomHasher(64)(dg2_hash); + + poseidon_hasher.inputs[6] <== dg2Hash2; + + out <== poseidon_hasher.out; +} diff --git a/circuits/circuits/utils/leafHasherLight.circom b/circuits/circuits/utils/passport/customHashers.circom similarity index 60% rename from circuits/circuits/utils/leafHasherLight.circom rename to circuits/circuits/utils/passport/customHashers.circom index 10555de1..752951b1 100644 --- a/circuits/circuits/utils/leafHasherLight.circom +++ b/circuits/circuits/utils/passport/customHashers.circom @@ -1,10 +1,11 @@ -pragma circom 2.1.6; -include "@zk-email/circuits/lib/fp.circom"; +pragma circom 2.1.9; +include "../other/fp.circom"; include "circomlib/circuits/poseidon.circom"; -template LeafHasherLight(k) { +template CustomHasher(k) { signal input in[k]; var rounds = div_ceil(k, 16); + assert(rounds < 17); component hash[rounds]; for (var i = 0; i < rounds ; i ++){ @@ -28,26 +29,13 @@ template LeafHasherLight(k) { signal output out <== finalHash.out; } -template LeafHasherLightWithSigAlg(k) { +template LeafHasher(k) { signal input in[k]; signal input sigAlg; - component leafHasher = LeafHasherLight(k+1); + component leafHasher = CustomHasher(k+1); leafHasher.in[0] <== sigAlg; for (var i = 0; i < k; i++){ leafHasher.in[i+1] <== in[i]; } signal output out <== leafHasher.out; -} - -template LeafHasherLightWithSigAlgECDSA(k) { - signal input x[k]; - signal input y[k]; - signal input sigAlg; - component leafHasher = LeafHasherLight(2*k+1); - leafHasher.in[0] <== sigAlg; - for (var i = 0; i < k; i++){ - leafHasher.in[i+1] <== x[i]; - leafHasher.in[i+1+k] <== y[i]; - } - signal output out <== leafHasher.out; } \ No newline at end of file diff --git a/circuits/circuits/utils/dateIsLess.circom b/circuits/circuits/utils/passport/date/dateIsLess.circom similarity index 98% rename from circuits/circuits/utils/dateIsLess.circom rename to circuits/circuits/utils/passport/date/dateIsLess.circom index 3dbf72b0..61b77e5d 100644 --- a/circuits/circuits/utils/dateIsLess.circom +++ b/circuits/circuits/utils/passport/date/dateIsLess.circom @@ -1,4 +1,4 @@ -pragma circom 2.1.6; +pragma circom 2.1.9; include "../node_modules/circomlib/circuits/comparators.circom"; diff --git a/circuits/circuits/utils/isOlderThan.circom b/circuits/circuits/utils/passport/date/isOlderThan.circom similarity index 98% rename from circuits/circuits/utils/isOlderThan.circom rename to circuits/circuits/utils/passport/date/isOlderThan.circom index 83b35e9c..0141ec1a 100644 --- a/circuits/circuits/utils/isOlderThan.circom +++ b/circuits/circuits/utils/passport/date/isOlderThan.circom @@ -1,4 +1,4 @@ -pragma circom 2.1.6; +pragma circom 2.1.9; include "../node_modules/circomlib/circuits/comparators.circom"; include "../node_modules/circomlib/circuits/bitify.circom"; diff --git a/circuits/circuits/utils/isValid.circom b/circuits/circuits/utils/passport/date/isValid.circom similarity index 98% rename from circuits/circuits/utils/isValid.circom rename to circuits/circuits/utils/passport/date/isValid.circom index 077f4c95..a772f87f 100644 --- a/circuits/circuits/utils/isValid.circom +++ b/circuits/circuits/utils/passport/date/isValid.circom @@ -1,4 +1,4 @@ -pragma circom 2.1.6; +pragma circom 2.1.9; include "../node_modules/circomlib/circuits/comparators.circom"; include "../node_modules/circomlib/circuits/bitify.circom"; diff --git a/circuits/circuits/utils/passport/formatECDSAInputs.circom b/circuits/circuits/utils/passport/formatECDSAInputs.circom new file mode 100644 index 00000000..e2caf70e --- /dev/null +++ b/circuits/circuits/utils/passport/formatECDSAInputs.circom @@ -0,0 +1,6 @@ +pragma circom 2.1.9; + + +template FormatECDSAInputs(signatureAlgorithm, k) { + +} diff --git a/circuits/circuits/utils/passport/passportVerifier.circom b/circuits/circuits/utils/passport/passportVerifier.circom new file mode 100644 index 00000000..6366b51d --- /dev/null +++ b/circuits/circuits/utils/passport/passportVerifier.circom @@ -0,0 +1,69 @@ +pragma circom 2.1.9; + +include "../other/array.circom"; +include "../other/bytes.circom"; +include "../shaBytes/shaBytesStatic.circom"; +include "../shaBytes/shaBytesDynamic.circom"; +include "./signatureAlgorithm.circom"; +include "./signatureVerifier.circom"; + +template PassportVerifier(signatureAlgorithm, n, k, MAX_ECONTENT_LEN, MAX_SIGNED_ATTR_LEN) { + var kLengthFactor = getKLengthFactor(signatureAlgorithm); + var kScaled = k * kLengthFactor; + + var HASH_LEN_BITS = getHashLength(signatureAlgorithm); + var HASH_LEN_BYTES = HASH_LEN_BITS / 8; + + signal input dg1[93]; + signal input dg1_hash_offset; + signal input dg2_hash[64]; + signal input eContent[MAX_ECONTENT_LEN]; + signal input eContent_padded_length; + signal input signed_attr[MAX_SIGNED_ATTR_LEN]; + signal input signed_attr_padded_length; + signal input signed_attr_econtent_hash_offset; + signal input pubKey[kScaled]; + signal input signature[kScaled]; + + // compute hash of DG1 + signal dg1Sha[HASH_LEN_BITS] <== ShaBytesStatic(HASH_LEN_BITS, 93)(dg1); + + component dg1ShaBytes[HASH_LEN_BYTES]; + for (var i = 0; i < HASH_LEN_BYTES; i++) { + dg1ShaBytes[i] = Bits2Num(8); + for (var j = 0; j < 8; j++) { + dg1ShaBytes[i].in[7 - j] <== dg1Sha[i * 8 + j]; + } + } + + // assert DG1 and DG2 hashes match the ones in eContent input + signal dg1AndDg2Hash[2 * HASH_LEN_BYTES] <== VarShiftLeft(MAX_ECONTENT_LEN, 2 * HASH_LEN_BYTES)(eContent, dg1_hash_offset); + for(var i = 0; i < HASH_LEN_BYTES; i++) { + dg1AndDg2Hash[i] === dg1ShaBytes[i].out; + dg1AndDg2Hash[i + HASH_LEN_BYTES] === dg2_hash[i]; + } + + // compute hash of eContent + signal eContentSha[HASH_LEN_BITS] <== ShaBytesDynamic(HASH_LEN_BITS,MAX_ECONTENT_LEN)(eContent, eContent_padded_length); + component eContentShaBytes[HASH_LEN_BYTES]; + for (var i = 0; i < HASH_LEN_BYTES; i++) { + eContentShaBytes[i] = Bits2Num(8); + for (var j = 0; j < 8; j++) { + eContentShaBytes[i].in[7 - j] <== eContentSha[i * 8 + j]; + } + } + + // assert eContent hash matches the one in signedAttr + signal eContentHashInSignedAttr[HASH_LEN_BYTES] <== VarShiftLeft(MAX_SIGNED_ATTR_LEN, HASH_LEN_BYTES)(signed_attr, signed_attr_econtent_hash_offset); + for(var i = 0; i < HASH_LEN_BYTES; i++) { + eContentHashInSignedAttr[i] === eContentShaBytes[i].out; + } + + // compute hash of signedAttr + signal signedAttrSha[HASH_LEN_BITS] <== ShaBytesDynamic(HASH_LEN_BITS, MAX_SIGNED_ATTR_LEN)(signed_attr, signed_attr_padded_length); + + SignatureVerifier(signatureAlgorithm, n, k)(signedAttrSha, pubKey, signature); + + +} + diff --git a/circuits/circuits/utils/passport/secp256r1Verifier.circom b/circuits/circuits/utils/passport/secp256r1Verifier.circom new file mode 100644 index 00000000..ddc34f20 --- /dev/null +++ b/circuits/circuits/utils/passport/secp256r1Verifier.circom @@ -0,0 +1,51 @@ +pragma circom 2.1.9; + +include "./signatureAlgorithm.circom"; +include "../circom-ecdsa/ecdsa.circom"; + +template Secp256r1Verifier(signatureAlgorithm, n, k) { + var kLengthFactor = getKLengthFactor(signatureAlgorithm); + var kScaled = k * kLengthFactor; + + var HASH_LEN_BITS = getHashLength(signatureAlgorithm); + var msg_len = (HASH_LEN_BITS + n) \ n; + + signal input signature[kScaled]; + signal input pubKey[kScaled]; + signal input hashParsed[msg_len]; + + signal msgHash[6]; + + for(var i = 0; i < 6; i++) { + if (i < msg_len) { + msgHash[i] <== hashParsed[i]; + } else { + msgHash[i] <== 0; + } + } + + + signal signature_r[k]; // ECDSA signature component r + signal signature_s[k]; // ECDSA signature component s + signal pubKey_x[k]; + signal pubKey_y[k]; + + for (var i = 0; i < k; i++) { + signature_r[i] <== signature[i]; + signature_s[i] <== signature[i + k]; + pubKey_x[i] <== pubKey[i]; + pubKey_y[i] <== pubKey[i + k]; + } + signal pubkey_xy[2][k] <== [pubKey_x, pubKey_y]; + + // verify eContentHash signature + component ecdsa_verify = ECDSAVerifyNoPubkeyCheck(n, k); + + ecdsa_verify.r <== signature_r; + ecdsa_verify.s <== signature_s; + ecdsa_verify.msghash <== msgHash; + ecdsa_verify.pubkey <== pubkey_xy; + + 1 === ecdsa_verify.result; + +} \ No newline at end of file diff --git a/circuits/circuits/utils/passport/signatureAlgorithm.circom b/circuits/circuits/utils/passport/signatureAlgorithm.circom new file mode 100644 index 00000000..2a87c205 --- /dev/null +++ b/circuits/circuits/utils/passport/signatureAlgorithm.circom @@ -0,0 +1,111 @@ +pragma circom 2.1.9; + +function getHashLength(signatureAlgorithm) { + if (signatureAlgorithm == 1 ) { + return 256; + } + if (signatureAlgorithm == 3) { + return 160; + } + if (signatureAlgorithm == 4) { + return 256; + } + if (signatureAlgorithm == 7) { + return 160; + } + if (signatureAlgorithm == 8) { + return 256; + } + if (signatureAlgorithm == 9) { + return 384; + } + if (signatureAlgorithm == 10) { + return 256; + } + if (signatureAlgorithm == 11) { + return 160; + } + if (signatureAlgorithm == 12) { + return 256; + } + return 0; +} + +function getKeyLength(signatureAlgorithm) { + if (signatureAlgorithm == 1 ) { + return 2048; + } + if (signatureAlgorithm == 3) { + return 2048; + } + if (signatureAlgorithm == 4) { + return 2048; + } + if (signatureAlgorithm == 7) { + return 256; + } + if (signatureAlgorithm == 8) { + return 256; + } + if (signatureAlgorithm == 9) { + return 384; + } + if (signatureAlgorithm == 10) { + return 4096; + } + if (signatureAlgorithm == 11) { + return 4096; + } + if (signatureAlgorithm == 12) { + return 4096; + } + return 0; +} + +//returns 1 for rsa, 2 for ecdsa +function getKLengthFactor(signatureAlgorithm) { + if (signatureAlgorithm == 1) { + return 1; + } + if (signatureAlgorithm == 3) { + return 1; + } + if (signatureAlgorithm == 4) { + return 1; + } + if (signatureAlgorithm == 7) { + return 2; + } + if (signatureAlgorithm == 8) { + return 2; + } + if (signatureAlgorithm == 9) { + return 2; + } + if (signatureAlgorithm == 10) { + return 1; + } + if (signatureAlgorithm == 11) { + return 1; + } + if (signatureAlgorithm == 12) { + return 1; + } + return 0; + +} + +function getExponentBits(signatureAlgorithm) { + // returns the amounts of bits of the exponent of type 2^n +1 + if (signatureAlgorithm == 1 ) { + return 17; // 65537 + } + if (signatureAlgorithm == 3 ) { + return 17; + } + if (signatureAlgorithm == 4 ) { + return 17; + } + + return 0; +} \ No newline at end of file diff --git a/circuits/circuits/utils/passport/signatureVerifier.circom b/circuits/circuits/utils/passport/signatureVerifier.circom new file mode 100644 index 00000000..f295a2cb --- /dev/null +++ b/circuits/circuits/utils/passport/signatureVerifier.circom @@ -0,0 +1,93 @@ +pragma circom 2.1.9; + +include "../rsa/rsaPkcs1.circom"; +// include "../rsa/rsaPkcs1v15.circom"; +include "secp256r1Verifier.circom"; +include "../rsapss/rsapss.circom"; +include "../rsa/rsa.circom"; + +template SignatureVerifier(signatureAlgorithm, n, k) { + var kLengthFactor = getKLengthFactor(signatureAlgorithm); + var kScaled = k * kLengthFactor; + + var HASH_LEN_BITS = getHashLength(signatureAlgorithm); + + signal input hash[HASH_LEN_BITS]; + signal input pubKey[kScaled]; + signal input signature[kScaled]; + + var msg_len = (HASH_LEN_BITS + n) \ n; + + signal hashParsed[msg_len] <== HashParser(signatureAlgorithm, n, k)(hash); + + if (signatureAlgorithm == 1) { + // var exponentBits = getExponentBits(signatureAlgorithm); + // component rsa = RsaVerifierPkcs1v15(n, k, exponentBits, HASH_LEN_BITS); + // rsa.hashed <== hash; + // rsa.pubkey <== pubKey; + // rsa.signature <== signature; + component rsa = RSAVerifier65537(n, k); + for (var i = 0; i < msg_len; i++) { + rsa.message[i] <== hashParsed[i]; + } + for (var i = msg_len; i < k; i++) { + rsa.message[i] <== 0; + } + rsa.modulus <== pubKey; + rsa.signature <== signature; + + } + if (signatureAlgorithm == 3 ) { + component rsa_pkcs1 = RSAVerifier65537Pkcs1(n, k); + for (var i = 0; i < msg_len; i++) { + rsa_pkcs1.message[i] <== hashParsed[i]; + } + for (var i = msg_len; i < k; i++) { + rsa_pkcs1.message[i] <== 0; + } + rsa_pkcs1.modulus <== pubKey; + rsa_pkcs1.signature <== signature; + + } + if (signatureAlgorithm == 4) { + var exponentBits = getExponentBits(signatureAlgorithm); + var pubKeyBitsLength = getKeyLength(signatureAlgorithm); + + component rsaPssSha256Verification = VerifyRsaPssSig(n, k, exponentBits, HASH_LEN_BITS, pubKeyBitsLength); + rsaPssSha256Verification.pubkey <== pubKey; + rsaPssSha256Verification.signature <== signature; + rsaPssSha256Verification.hashed <== hash; // send the raw hash + + } + if (signatureAlgorithm == 7) { + Secp256r1Verifier (signatureAlgorithm,n,k)(signature, pubKey,hashParsed); + } + if (signatureAlgorithm == 8) { + Secp256r1Verifier (signatureAlgorithm,n,k)(signature, pubKey,hashParsed); + } + if (signatureAlgorithm == 9) { + } +} + + +template HashParser(signatureAlgorithm, n, k) { + var HASH_LEN_BITS = getHashLength(signatureAlgorithm); + var msg_len = (HASH_LEN_BITS + n) \ n; + + component hashParser[msg_len]; + signal input hash[HASH_LEN_BITS]; + + for (var i = 0; i < msg_len; i++) { + hashParser[i] = Bits2Num(n); + } + for (var i = 0; i < HASH_LEN_BITS; i++) { + hashParser[i \ n].in[i % n] <== hash[HASH_LEN_BITS - 1 - i]; + } + for (var i = HASH_LEN_BITS; i < n * msg_len; i++) { + hashParser[i \ n].in[i % n] <== 0; + } + signal output hashParsed[msg_len]; + for (var i = 0; i < msg_len ; i++ ){ + hashParsed[i] <== hashParser[i].out; + } +} \ No newline at end of file diff --git a/circuits/circuits/utils/rsa/powMod.circom b/circuits/circuits/utils/rsa/powMod.circom new file mode 100644 index 00000000..379d6326 --- /dev/null +++ b/circuits/circuits/utils/rsa/powMod.circom @@ -0,0 +1,47 @@ +pragma circom 2.1.9; + +include "../other/bigInt.circom"; + +// CHUNK_SIZE = 32 +// E_BITS = 17 +// CHUNK_NUMBER is the length of the base and modulus +// calculates (base^exp) % modulus, exp = 2^(E_BITS - 1) + 1 = 2^16 + 1 +template PowerMod(CHUNK_SIZE, CHUNK_NUMBER, E_BITS) { + assert(E_BITS >= 2); + + signal input base[CHUNK_NUMBER]; + signal input modulus[CHUNK_NUMBER]; + + signal output out[CHUNK_NUMBER]; + + component muls[E_BITS]; + + for (var i = 0; i < E_BITS; i++) { + muls[i] = BigMultModP(CHUNK_SIZE, CHUNK_NUMBER); + + for (var j = 0; j < CHUNK_NUMBER; j++) { + muls[i].p[j] <== modulus[j]; + } + } + + for (var i = 0; i < CHUNK_NUMBER; i++) { + muls[0].a[i] <== base[i]; + muls[0].b[i] <== base[i]; + } + + for (var i = 1; i < E_BITS - 1; i++) { + for (var j = 0; j < CHUNK_NUMBER; j++) { + muls[i].a[j] <== muls[i - 1].out[j]; + muls[i].b[j] <== muls[i - 1].out[j]; + } + } + + for (var i = 0; i < CHUNK_NUMBER; i++) { + muls[E_BITS - 1].a[i] <== base[i]; + muls[E_BITS - 1].b[i] <== muls[E_BITS - 2].out[i]; + } + + for (var i = 0; i < CHUNK_NUMBER; i++) { + out[i] <== muls[E_BITS - 1].out[i]; + } +} diff --git a/circuits/circuits/utils/rsa/rsa.circom b/circuits/circuits/utils/rsa/rsa.circom new file mode 100644 index 00000000..28442503 --- /dev/null +++ b/circuits/circuits/utils/rsa/rsa.circom @@ -0,0 +1,181 @@ +pragma circom 2.1.9; + +include "../other/fp.circom"; + + +/// @title RSAVerifier65537 +/// @notice Verifies an RSA signature with exponent 65537. +/// @param n Number of bits per chunk the modulus is split into. Recommended to be 121. +/// @param k Number of chunks the modulus is split into. Recommended to be 17. +/// @input message[k] The message that was signed; assumes to consist of `k` chunks that fit in `n` bits (also constrained implicitly). +/// @input signature[k] The signature to verify; assumes to consist of `k` chunks that fit in `n` bits (also constrained implicitly). +/// @input modulus[k] The modulus of the RSA key (pubkey); assumes to consist of `k` chunks that fit in `n` bits (also constrained implicitly). +template RSAVerifier65537(n, k) { + signal input message[k]; + signal input signature[k]; + signal input modulus[k]; + + component padder = RSAPad(n, k); + for (var i = 0; i < k; i++) { + padder.modulus[i] <== modulus[i]; + padder.message[i] <== message[i]; + } + + // Check that the signature is in proper form and reduced mod modulus. + component signatureRangeCheck[k]; + component bigLessThan = BigLessThan(n, k); + for (var i = 0; i < k; i++) { + signatureRangeCheck[i] = Num2Bits(n); + signatureRangeCheck[i].in <== signature[i]; + bigLessThan.a[i] <== signature[i]; + bigLessThan.b[i] <== modulus[i]; + } + bigLessThan.out === 1; + + component bigPow = FpPow65537Mod(n, k); + for (var i = 0; i < k; i++) { + bigPow.base[i] <== signature[i]; + bigPow.modulus[i] <== modulus[i]; + } + + // By construction of the padding, the padded message is necessarily + // smaller than the modulus. Thus, we don't have to check that bigPow is fully reduced. + for (var i = 0; i < k; i++) { + bigPow.out[i] === padder.out[i]; + } +} + + +/// @title FpPow65537Mod +/// @notice Computes base^65537 mod modulus +/// @dev Does not necessarily reduce fully mod modulus (the answer could be too big by a multiple of modulus) +/// @param n Number of bits per chunk the modulus is split into. +/// @param k Number of chunks the modulus is split into. +/// @input base The base to exponentiate; assumes to consist of `k` chunks, each of which must fit in `n` bits +/// @input modulus The modulus; assumes to consist of `k` chunks, each of which must fit in `n` bits +/// @output out The result of the exponentiation. +template FpPow65537Mod(n, k) { + signal input base[k]; + signal input modulus[k]; + + signal output out[k]; + + component doublers[16]; + component adder = FpMul(n, k); + for (var i = 0; i < 16; i++) { + doublers[i] = FpMul(n, k); + } + + for (var j = 0; j < k; j++) { + adder.p[j] <== modulus[j]; + for (var i = 0; i < 16; i++) { + doublers[i].p[j] <== modulus[j]; + } + } + for (var j = 0; j < k; j++) { + doublers[0].a[j] <== base[j]; + doublers[0].b[j] <== base[j]; + } + for (var i = 0; i + 1 < 16; i++) { + for (var j = 0; j < k; j++) { + doublers[i + 1].a[j] <== doublers[i].out[j]; + doublers[i + 1].b[j] <== doublers[i].out[j]; + } + } + for (var j = 0; j < k; j++) { + adder.a[j] <== base[j]; + adder.b[j] <== doublers[15].out[j]; + } + for (var j = 0; j < k; j++) { + out[j] <== adder.out[j]; + } +} + +/// @title RSAPad +/// @notice Pads a message for RSA signing. +/// @param n Number of bits per chunk the modulus is split into. +/// @param k Number of chunks the modulus is split into. +/// @input modulus The modulus of the RSA key (pubkey). +/// @input message The message to pad. +/// @output out The padded message. +template RSAPad(n, k) { + signal input modulus[k]; + signal input message[k]; + signal output out[k]; + + // The extra 152 bits comes from 0x3031300d060960864801650304020105000420 + // This is due to padding from the RSASSA-PKCS1-v1_5 standard + var baseLen = 408; + var msgLen = 256; + + signal paddedMessageBits[n*k]; + + component modulusN2B[k]; + component messageN2B[k]; + signal modulusBits[n*k]; + signal messageBits[n*k]; + for (var i = 0; i < k; i++) { + messageN2B[i] = Num2Bits(n); + messageN2B[i].in <== message[i]; + for (var j = 0; j < n; j++) { + messageBits[i*n+j] <== messageN2B[i].out[j]; + } + modulusN2B[i] = Num2Bits(n); + modulusN2B[i].in <== modulus[i]; + for (var j = 0; j < n; j++) { + modulusBits[i*n+j] <== modulusN2B[i].out[j]; + } + } + + for (var i = msgLen; i < n*k; i++) { + messageBits[i] === 0; + } + + for (var i = 0; i < msgLen; i++) { + paddedMessageBits[i] <== messageBits[i]; + } + + for (var i = baseLen; i < baseLen + 8; i++) { + paddedMessageBits[i] <== 0; + } + + for (var i = msgLen; i < baseLen; i++) { + paddedMessageBits[i] <== (0x3031300d060960864801650304020105000420 >> (i - msgLen)) & 1; + } + + component modulusZero[(n*k + 7 - (baseLen + 8))\8]; + { + var modulusPrefix = 0; + for (var i = n*k - 1; i >= baseLen + 8; i--) { + if (i+8 < n*k) { + modulusPrefix += modulusBits[i+8]; + if (i % 8 == 0) { + var idx = (i - (baseLen + 8)) \ 8; + modulusZero[idx] = IsZero(); + modulusZero[idx].in <== modulusPrefix; + paddedMessageBits[i] <== 1-modulusZero[idx].out; + } else { + paddedMessageBits[i] <== paddedMessageBits[i+1]; + } + } else { + paddedMessageBits[i] <== 0; + } + } + } + + // The RFC guarantees at least 8 octets of 0xff padding. + assert(baseLen + 8 + 65 <= n * k); + + for (var i = baseLen + 8; i < baseLen + 8 + 65; i++) { + paddedMessageBits[i] === 1; + } + + component passedMessageB2N[k]; + for (var i = 0; i < k; i++) { + passedMessageB2N[i] = Bits2Num(n); + for (var j = 0; j < n; j++) { + passedMessageB2N[i].in[j] <== paddedMessageBits[i*n+j]; + } + out[i] <== passedMessageB2N[i].out; + } +} diff --git a/circuits/circuits/utils/rsaPkcs1.circom b/circuits/circuits/utils/rsa/rsaPkcs1.circom similarity index 90% rename from circuits/circuits/utils/rsaPkcs1.circom rename to circuits/circuits/utils/rsa/rsaPkcs1.circom index 037a7ae8..55e13859 100644 --- a/circuits/circuits/utils/rsaPkcs1.circom +++ b/circuits/circuits/utils/rsa/rsaPkcs1.circom @@ -1,11 +1,12 @@ -pragma circom 2.1.5; +pragma circom 2.1.9; -include "@zk-email/circuits/lib/fp.circom"; +//include "@zk-email/circuits/lib/fp.circom"; +include "../other/fp.circom"; // Computes base^65537 mod modulus // Does not necessarily reduce fully mod modulus (the answer could be // too big by a multiple of modulus) -template FpPow65537Mod(n, k) { +template FpPow65537ModPkcs1(n, k) { signal input base[k]; // Exponent is hardcoded at 65537 signal input modulus[k]; @@ -42,9 +43,9 @@ template FpPow65537Mod(n, k) { } } -template RSAPad(n, k) { +template RSAPadPkcs1(n, k) { signal input modulus[k]; - signal input base_message[k]; + signal input message[k]; signal output padded_message[k]; var base_len = 280; @@ -58,7 +59,7 @@ template RSAPad(n, k) { signal base_message_bits[n*k]; for (var i = 0; i < k; i++) { base_message_n2b[i] = Num2Bits(n); - base_message_n2b[i].in <== base_message[i]; + base_message_n2b[i].in <== message[i]; for (var j = 0; j < n; j++) { base_message_bits[i*n+j] <== base_message_n2b[i].out[j]; } @@ -121,15 +122,15 @@ template RSAPad(n, k) { } } -template RSAVerify65537(n, k) { +template RSAVerifier65537Pkcs1(n, k) { signal input signature[k]; signal input modulus[k]; - signal input base_message[k]; + signal input message[k]; - component padder = RSAPad(n, k); + component padder = RSAPadPkcs1(n, k); for (var i = 0; i < k; i++) { padder.modulus[i] <== modulus[i]; - padder.base_message[i] <== base_message[i]; + padder.message[i] <== message[i]; } // Check that the signature is in proper form and reduced mod modulus. @@ -143,7 +144,7 @@ template RSAVerify65537(n, k) { } bigLessThan.out === 1; - component bigPow = FpPow65537Mod(n, k); + component bigPow = FpPow65537ModPkcs1(n, k); for (var i = 0; i < k; i++) { bigPow.base[i] <== signature[i]; bigPow.modulus[i] <== modulus[i]; diff --git a/circuits/circuits/utils/rsa/rsaPkcs1v15.circom b/circuits/circuits/utils/rsa/rsaPkcs1v15.circom new file mode 100644 index 00000000..b604d0c8 --- /dev/null +++ b/circuits/circuits/utils/rsa/rsaPkcs1v15.circom @@ -0,0 +1,56 @@ +pragma circom 2.1.9; + +include "./powMod.circom"; +include "circomlib/circuits/bitify.circom"; + +// Pkcs1v15 + Sha256, e = 65537 +template RsaVerifierPkcs1v15(CHUNK_SIZE, CHUNK_NUMBER, E_BITS, HASH_TYPE) { + signal input signature[CHUNK_NUMBER]; + signal input pubkey[CHUNK_NUMBER]; //aka modulus + + signal input hashed[HASH_TYPE]; + + // signature ** exp mod modulus + component pm = PowerMod(CHUNK_SIZE, CHUNK_NUMBER, E_BITS); + for (var i = 0; i < CHUNK_NUMBER; i++) { + pm.base[i] <== signature[i]; + pm.modulus[i] <== pubkey[i]; + } + + signal hashed_chunks[4]; + + component bits2num[4]; + for(var i = 0; i< 4; i++){ + bits2num[3-i] = Bits2Num(64); + for (var j = 0; j< 64; j++){ + bits2num[3-i].in[j] <== hashed[i*64 + 63 - j]; + } + bits2num[3-i].out ==> hashed_chunks[3-i]; + } + + // 1. Check hashed data + for (var i = 0; i < 4; i++) { + hashed_chunks[i] === pm.out[i]; + } + + // 2. Check hash prefix and 1 byte 0x00 + pm.out[4] === 217300885422736416; + pm.out[5] === 938447882527703397; + + // remain 24 bit + component num2bits_6 = Num2Bits(CHUNK_SIZE); + num2bits_6.in <== pm.out[6]; + var remainsBits[32] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0]; + for (var i = 0; i < 32; i++) { + num2bits_6.out[i] === remainsBits[31 - i]; + } + + // 3. Check PS and em[1] = 1 + for (var i = 32; i < CHUNK_SIZE; i++) { + num2bits_6.out[i] === 1; + } + + for (var i = 7; i < CHUNK_NUMBER-1; i++) { + pm.out[i] === 18446744073709551615; // 0b1111111111111111111111111111111111111111111111111111111111111111 + } +} diff --git a/circuits/circuits/utils/rsa/rsaVerify.circom b/circuits/circuits/utils/rsa/rsaVerify.circom new file mode 100644 index 00000000..accee93e --- /dev/null +++ b/circuits/circuits/utils/rsa/rsaVerify.circom @@ -0,0 +1,5 @@ +pragma circom 2.1.9; + +include "./rsa.circom"; + +component main = RsaVerifyPkcs1v15(64, 32, 17, 256); \ No newline at end of file diff --git a/circuits/circuits/utils/rsapss/mgf1.circom b/circuits/circuits/utils/rsapss/mgf1.circom index 8addf54c..1b4775f5 100644 --- a/circuits/circuits/utils/rsapss/mgf1.circom +++ b/circuits/circuits/utils/rsapss/mgf1.circom @@ -1,4 +1,4 @@ -pragma circom 2.1.6; +pragma circom 2.1.9; include "circomlib/circuits/bitify.circom"; include "../sha2/sha256/sha256_hash_bits.circom"; diff --git a/circuits/circuits/utils/rsapss/powMod.circom b/circuits/circuits/utils/rsapss/powMod.circom deleted file mode 100644 index 73a91c54..00000000 --- a/circuits/circuits/utils/rsapss/powMod.circom +++ /dev/null @@ -1,358 +0,0 @@ -pragma circom 2.1.6; - -include "@zk-email/circuits/lib/bigint.circom"; - -// w = 32 -// e_bits = 17 -// nb is the length of the base and modulus -// calculates (base^exp) % modulus, exp = 2^(e_bits - 1) + 1 = 2^16 + 1 -template PowerMod(w, nb, e_bits) { - assert(e_bits >= 2); - - signal input base[nb]; - signal input modulus[nb]; - - signal output out[nb]; - - component muls[e_bits]; - - for (var i = 0; i < e_bits; i++) { - muls[i] = BigMultModP(w, nb); - - for (var j = 0; j < nb; j++) { - muls[i].p[j] <== modulus[j]; - } - } - - for (var i = 0; i < nb; i++) { - muls[0].a[i] <== base[i]; - muls[0].b[i] <== base[i]; - } - - for (var i = 1; i < e_bits - 1; i++) { - for (var j = 0; j < nb; j++) { - muls[i].a[j] <== muls[i - 1].out[j]; - muls[i].b[j] <== muls[i - 1].out[j]; - } - } - - for (var i = 0; i < nb; i++) { - muls[e_bits - 1].a[i] <== base[i]; - muls[e_bits - 1].b[i] <== muls[e_bits - 2].out[i]; - } - - for (var i = 0; i < nb; i++) { - out[i] <== muls[e_bits - 1].out[i]; - } -} - -// Note: deprecated -template BigMultModP(n, k) { - assert(n <= 252); - signal input a[k]; - signal input b[k]; - signal input p[k]; - signal output out[k]; - - component big_mult = BigMult(n, k); - for (var i = 0; i < k; i++) { - big_mult.a[i] <== a[i]; - big_mult.b[i] <== b[i]; - } - component big_mod = BigMod(n, k); - for (var i = 0; i < 2 * k; i++) { - big_mod.a[i] <== big_mult.out[i]; - } - for (var i = 0; i < k; i++) { - big_mod.b[i] <== p[i]; - } - for (var i = 0; i < k; i++) { - out[i] <== big_mod.mod[i]; - } -} - -template BigMult(n, k) { - signal input a[k]; - signal input b[k]; - signal output out[2 * k]; - - var LOGK = log_ceil(k); - component mult = BigMultShortLong(n, k, 2*n + LOGK); - for (var i = 0; i < k; i++) { - mult.a[i] <== a[i]; - mult.b[i] <== b[i]; - } - - // no carry is possible in the highest order register - component longshort = LongToShortNoEndCarry(n, 2 * k - 1); - for (var i = 0; i < 2 * k - 1; i++) { - longshort.in[i] <== mult.out[i]; - } - for (var i = 0; i < 2 * k; i++) { - out[i] <== longshort.out[i]; - } -} - -template LongToShortNoEndCarry(n, k) { - assert(n <= 126); - signal input in[k]; - signal output out[k+1]; - - var split[k][3]; - for (var i = 0; i < k; i++) { - split[i] = SplitThreeFn(in[i], n, n, n); - } - - var carry[k]; - carry[0] = 0; - out[0] <-- split[0][0]; - if (k > 1) { - var sumAndCarry[2] = SplitFn(split[0][1] + split[1][0], n, n); - out[1] <-- sumAndCarry[0]; - carry[1] = sumAndCarry[1]; - } - if (k > 2) { - for (var i = 2; i < k; i++) { - var sumAndCarry[2] = SplitFn(split[i][0] + split[i-1][1] + split[i-2][2] + carry[i-1], n, n); - out[i] <-- sumAndCarry[0]; - carry[i] = sumAndCarry[1]; - } - out[k] <-- split[k-1][1] + split[k-2][2] + carry[k-1]; - } - - component outRangeChecks[k+1]; - for (var i = 0; i < k+1; i++) { - outRangeChecks[i] = Num2Bits(n); - outRangeChecks[i].in <== out[i]; - } - - signal runningCarry[k]; - component runningCarryRangeChecks[k]; - runningCarry[0] <-- (in[0] - out[0]) / (1 << n); - runningCarryRangeChecks[0] = Num2Bits(n + log_ceil(k)); - runningCarryRangeChecks[0].in <== runningCarry[0]; - runningCarry[0] * (1 << n) === in[0] - out[0]; - for (var i = 1; i < k; i++) { - runningCarry[i] <-- (in[i] - out[i] + runningCarry[i-1]) / (1 << n); - runningCarryRangeChecks[i] = Num2Bits(n + log_ceil(k)); - runningCarryRangeChecks[i].in <== runningCarry[i]; - runningCarry[i] * (1 << n) === in[i] - out[i] + runningCarry[i-1]; - } - runningCarry[k-1] === out[k]; -} -template BigMultShortLong(n, k, m_out) { - assert(n <= 126); - signal input a[k]; - signal input b[k]; - signal output out[2 * k - 1]; - - var prod_val[2 * k - 1]; - for (var i = 0; i < 2 * k - 1; i++) { - prod_val[i] = 0; - if (i < k) { - for (var a_idx = 0; a_idx <= i; a_idx++) { - prod_val[i] = prod_val[i] + a[a_idx] * b[i - a_idx]; - } - } else { - for (var a_idx = i - k + 1; a_idx < k; a_idx++) { - prod_val[i] = prod_val[i] + a[a_idx] * b[i - a_idx]; - } - } - out[i] <-- prod_val[i]; - } - - var k2 = 2 * k - 1; - var pow[k2][k2]; // we cache the exponent values because it makes a big difference in witness generation time - for(var i = 0; i= 0; i--) { - if (i == m) { - dividend[k] = 0; - for (var j = k - 1; j >= 0; j--) { - dividend[j] = remainder[j + m]; - } - } else { - for (var j = k; j >= 0; j--) { - dividend[j] = remainder[j + i]; - } - } - out[0][i] = short_div(n, k, dividend, b); - var mult_shift[150] = long_scalar_mult(n, k, out[0][i], b); - var subtrahend[150]; - for (var j = 0; j < m + k; j++) { - subtrahend[j] = 0; - } - for (var j = 0; j <= k; j++) { - if (i + j < m + k) { - subtrahend[i + j] = mult_shift[j]; - } - } - remainder = long_sub(n, m + k, remainder, subtrahend); - } - for (var i = 0; i < k; i++) { - out[1][i] = remainder[i]; - } - out[1][k] = 0; - return out; -} \ No newline at end of file diff --git a/circuits/circuits/utils/rsapss/rsapss.circom b/circuits/circuits/utils/rsapss/rsapss.circom index 3dc4d76b..4ebe9717 100644 --- a/circuits/circuits/utils/rsapss/rsapss.circom +++ b/circuits/circuits/utils/rsapss/rsapss.circom @@ -1,11 +1,11 @@ pragma circom 2.1.6; -include "./powMod.circom"; +include "../rsa/powMod.circom"; include "./mgf1.circom"; include "./xor2.circom"; include "../sha2/sha256/sha256_hash_bits.circom"; include "../sha2/sha384/sha384_hash_bits.circom"; -include "../splitSignalsToWords.circom"; +include "../other/bytes.circom"; template VerifyRsaPssSig (n, k, e_bits, ALGO, modulus_bits_size){ diff --git a/circuits/circuits/utils/rsapss/xor2.circom b/circuits/circuits/utils/rsapss/xor2.circom index 5e505f78..f63c1027 100644 --- a/circuits/circuits/utils/rsapss/xor2.circom +++ b/circuits/circuits/utils/rsapss/xor2.circom @@ -1,4 +1,4 @@ -pragma circom 2.1.6; +pragma circom 2.1.9; template Xor2(n) { signal input a[n]; diff --git a/circuits/circuits/utils/sha1/constants.circom b/circuits/circuits/utils/sha1/constants.circom new file mode 100644 index 00000000..ed6bfa41 --- /dev/null +++ b/circuits/circuits/utils/sha1/constants.circom @@ -0,0 +1,53 @@ +pragma circom 2.1.9; + +include "../node_modules/circomlib/circuits/bitify.circom"; + +template H_sha1(x) { + + signal output out[32]; + var c[5] = [ + 0x67452301, + 0xefcdab89, + 0x98badcfe, + 0x10325476, + 0xc3d2e1f0 + ]; + + component bitify = Num2Bits(32); + bitify.in <== c[x]; + + for (var k=0; k<32; k++) { + out[k] <== bitify.out[31-k]; + } + +} + +template K_sha1(t) { + signal output out[32]; + var k[4] = [0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6]; + + component bitify = Num2Bits(32); + + var i; + + if (0 <= t && t <= 19) { + bitify.in <== k[0]; + } + + if (20 <= t && t <= 39) { + bitify.in <== k[1]; + } + + if (40 <= t && t <= 59) { + bitify.in <== k[2]; + } + + if (60 <= t && t <= 79) { + bitify.in <== k[3]; + } + + for (var k=0; k<32; k++) { + out[k] <== bitify.out[31-k]; + } + +} \ No newline at end of file diff --git a/circuits/circuits/utils/sha1/f.circom b/circuits/circuits/utils/sha1/f.circom new file mode 100644 index 00000000..8e62f554 --- /dev/null +++ b/circuits/circuits/utils/sha1/f.circom @@ -0,0 +1,64 @@ +pragma circom 2.1.9; + +include "./parity.circom"; +include "../node_modules/circomlib/circuits/sha256/maj.circom"; +include "../node_modules/circomlib/circuits/sha256/ch.circom"; + +template f_t(t) { + + signal input b[32]; + signal input c[32]; + signal input d[32]; + signal output out[32]; + + component maj = Maj_t(32); + component parity = Parity_t(32); + component ch = Ch_t(32); + + var k; + + + // ch(x, y, z) + for (k=0; k<32; k++) { + ch.a[k] <== b[k]; + ch.b[k] <== c[k]; + ch.c[k] <== d[k]; + } + + // parity(x, y, z) + for (k=0; k < 32; k++) { + parity.a[k] <== b[k]; + parity.b[k] <== c[k]; + parity.c[k] <== d[k]; + } + + // maj(x, y, z) + for (k=0; k<32; k++) { + maj.a[k] <== b[k]; + maj.b[k] <== c[k]; + maj.c[k] <== d[k]; + } + + if (t <= 19) { + for (k=0; k <32; k++) { + out[k] <== ch.out[k]; + } + + } else { + + if (t <= 39 || t >= 60) { + + for (k=0; k < 32; k++) { + out[k] <== parity.out[k]; + } + + } else { + + for (k=0; k<32; k++) { + out[k] <== maj.out[k]; + } + + } + } + +} \ No newline at end of file diff --git a/circuits/circuits/utils/sha1/parity.circom b/circuits/circuits/utils/sha1/parity.circom new file mode 100644 index 00000000..816452fd --- /dev/null +++ b/circuits/circuits/utils/sha1/parity.circom @@ -0,0 +1,23 @@ +pragma circom 2.1.9; + +include "../node_modules/circomlib/circuits/sha256/xor3.circom"; + +template Parity_t(n) { + signal input a[n]; + signal input b[n]; + signal input c[n]; + signal output out[n]; + + component xor3 = Xor3(32); + var k; + + for (k=0; k<32; k++) { + xor3.a[k] <== a[k]; + xor3.b[k] <== b[k]; + xor3.c[k] <== c[k]; + } + + for (k=0; k<32; k++) { + out[k] <== xor3.out[k]; + } +} \ No newline at end of file diff --git a/circuits/circuits/utils/sha1/rotate.circom b/circuits/circuits/utils/sha1/rotate.circom new file mode 100644 index 00000000..ac906d83 --- /dev/null +++ b/circuits/circuits/utils/sha1/rotate.circom @@ -0,0 +1,10 @@ +pragma circom 2.1.9; + +template RotL(n, l) { + signal input in[n]; + signal output out[n]; + + for (var i=(n-1); i >= 0; i--) { + out[i] <== in[ (i+l)%n ]; + } +} \ No newline at end of file diff --git a/circuits/circuits/utils/sha1/sha1.circom b/circuits/circuits/utils/sha1/sha1.circom new file mode 100644 index 00000000..3ecbe2ec --- /dev/null +++ b/circuits/circuits/utils/sha1/sha1.circom @@ -0,0 +1,74 @@ +pragma circom 2.1.9; + +include "constants.circom"; +include "sha1compression.circom"; + +template Sha1(nBits) { + signal input in[nBits]; + signal output out[160]; + + var i; + var k; + var nBlocks; + var bitsLastBlock; + + nBlocks = ((nBits + 64) \ 512) + 1; + + signal paddedIn[nBlocks * 512]; + + for (k=0; k> k)&1; + } + + component ha0 = H_sha1(0); + component hb0 = H_sha1(1); + component hc0 = H_sha1(2); + component hd0 = H_sha1(3); + component he0 = H_sha1(4); + + component sha1compression[nBlocks]; + + for (i=0; i> 9); + paddedInLength === inBlockIndex * 512; + + // These verify the unconstrained floor calculation is the uniquely correct integer that represents the floor + // component floorVerifierUnder = LessEqThan(maxBitsPaddedBits); // todo verify the length passed in is less than nbits. note that maxBitsPaddedBits can likely be lowered or made it a fn of maxbits + // floorVerifierUnder.in[0] <== (inBlockIndex)*512; + // floorVerifierUnder.in[1] <== paddedInLength; + // floorVerifierUnder.out === 1; + + // component floorVerifierOver = GreaterThan(maxBitsPaddedBits); + // floorVerifierOver.in[0] <== (inBlockIndex+1)*512; + // floorVerifierOver.in[1] <== paddedInLength; + // floorVerifierOver.out === 1; + + // These verify we pass in a valid number of bits to the SHA256 compression circuit. + component bitLengthVerifier = LessEqThan(maxBitsPaddedBits); // todo verify the length passed in is less than nbits. note that maxBitsPaddedBits can likely be lowered or made it a fn of maxbits + bitLengthVerifier.in[0] <== paddedInLength; + bitLengthVerifier.in[1] <== maxBitLength; + bitLengthVerifier.out === 1; + + // Note that we can no longer do padded verification efficiently inside the SHA because it requires non deterministic array indexing. + // We can do it if we add a constraint, but since guessing a valid SHA2 preimage is hard anyways, we'll just do it outside the circuit. + + // signal paddedIn[maxBlocks*512]; + // for (k=0; k> k)&1; + // } + + component ha0 = H(0); + component hb0 = H(1); + component hc0 = H(2); + component hd0 = H(3); + component he0 = H(4); + component hf0 = H(5); + component hg0 = H(6); + component hh0 = H(7); + + component sha256compression[maxBlocks]; + + for (i=0; i= 8 * l){ - bits2num[i].in[j] <== 0; - } - else{ - bits2num[i].in[j] <== num2bits[l - (( i * n + j) \ 8) - 1].out[ ((i * n + j) % 8)]; - } - } - } - for( var i = 0 ; i< k ; i++){ - out[i] <== bits2num[i].out; - } -} \ No newline at end of file diff --git a/circuits/circuits/utils/splitSignalsToWords.circom b/circuits/circuits/utils/splitSignalsToWords.circom deleted file mode 100644 index 1d911f22..00000000 --- a/circuits/circuits/utils/splitSignalsToWords.circom +++ /dev/null @@ -1,72 +0,0 @@ -pragma circom 2.1.5; -include "circomlib/circuits/bitify.circom"; - -/// NOTE: this circuit is unaudited and should not be used in production -/// @title SplitBytesToWords -/// @notice split an array of bytes into an array of words -/// @notice useful for casting a message or modulus before RSA verification -/// @param l: number of bytes in the input array -/// @param n: number of bits in a word -/// @param k: number of words -/// @input in: array of bytes -/// @output out: array of words -template SplitSignalsToWords (t,l,n,k) { - assert(n*k >= t*l); - - signal input in[l]; - signal output out[k]; - component num2bits[l]; - for (var i = 0 ; i < l ; i++){ - num2bits[i] = Num2Bits(t); - num2bits[i].in <== in[i]; - } - for (var i = 0 ; i < t ; i ++){ - } - component bits2num[k]; - for (var i = 0 ; i < k ; i++){ - bits2num[i] = Bits2Num(n); - - for(var j = 0 ; j < n ; j++){ - if(i*n + j >= l * t){ - bits2num[i].in[j] <== 0; - } - else{ - bits2num[i].in[j] <== num2bits[ (( i * n + j) \ t) ].out[ ((i * n + j) % t)]; - } - } - } - for( var i = 0 ; i< k ; i++){ - out[i] <== bits2num[i].out; - } - -} - -template SplitSignalsToWordsUnsafe (t,l,n,k) { - - signal input in[l]; - signal output out[k]; - component num2bits[l]; - for (var i = 0 ; i < l ; i++){ - num2bits[i] = Num2Bits(t); - num2bits[i].in <== in[i]; - } - for (var i = 0 ; i < t ; i ++){ - } - component bits2num[k]; - for (var i = 0 ; i < k ; i++){ - bits2num[i] = Bits2Num(n); - - for(var j = 0 ; j < n ; j++){ - if(i*n + j >= l * t){ - bits2num[i].in[j] <== 0; - } - else{ - bits2num[i].in[j] <== num2bits[ (( i * n + j) \ t) ].out[ ((i * n + j) % t)]; - } - } - } - for( var i = 0 ; i< k ; i++){ - out[i] <== bits2num[i].out; - } - -} \ No newline at end of file diff --git a/circuits/circuits/utils/xor.circom b/circuits/circuits/utils/xor.circom deleted file mode 100644 index 21b2c308..00000000 --- a/circuits/circuits/utils/xor.circom +++ /dev/null @@ -1,11 +0,0 @@ -pragma circom 2.1.5; - -template Xor2(n) { - signal input a[n]; - signal input b[n]; - signal output out[n]; - - for (var k=0; k poseidon2([a, b]), []); tree.insert(BigInt(commitment)); @@ -63,7 +62,8 @@ describe('Disclose', function () { passportData, tree, majority, - bitmap, + selector_dg1, + selector_older_than, scope, user_identifier ); @@ -74,12 +74,13 @@ describe('Disclose', function () { }); it('should have nullifier == poseidon(secret, scope)', async function () { + // console.log("inputs", inputs); w = await circuit.calculateWitness(inputs); const nullifier_js = poseidon2([inputs.secret, inputs.scope]).toString(); const nullifier_circom = (await circuit.getOutput(w, ['nullifier'])).nullifier; - //console.log("nullifier_circom", nullifier_circom); - //console.log("nullifier_js", nullifier_js); + console.log('nullifier_circom', nullifier_circom); + console.log('nullifier_js', nullifier_js); expect(nullifier_circom).to.equal(nullifier_js); }); @@ -107,7 +108,7 @@ describe('Disclose', function () { await circuit.calculateWitness(invalidInputs); expect.fail('Expected an error but none was thrown.'); } catch (error) { - expect(error.message).to.include('Assert Failed'); + // expect(error.message).to.include('Assert Failed'); } }); @@ -125,18 +126,18 @@ describe('Disclose', function () { return acc; }, {}); - const bitmap = Array(90).fill('0'); + const selector_dg1 = Array(88).fill('0'); Object.entries(attributeToReveal).forEach(([attribute, reveal]) => { if (reveal) { const [start, end] = attributeToPosition[attribute]; - bitmap.fill('1', start, end + 1); + selector_dg1.fill('1', start, end + 1); } }); inputs = { ...inputs, - bitmap: bitmap.map(String), + selector_dg1: selector_dg1.map(String), }; w = await circuit.calculateWitness(inputs); @@ -146,8 +147,8 @@ describe('Disclose', function () { const reveal_unpacked = formatAndUnpackReveal(revealedData_packed); for (let i = 0; i < reveal_unpacked.length; i++) { - if (bitmap[i] == '1') { - const char = String.fromCharCode(Number(inputs.mrz[i + 5])); + if (selector_dg1[i] == '1') { + const char = String.fromCharCode(Number(inputs.dg1[i + 5])); assert(reveal_unpacked[i] == char, 'Should reveal the right character'); } else { assert(reveal_unpacked[i] == '\x00', 'Should not reveal'); @@ -158,33 +159,26 @@ describe('Disclose', function () { }); it('should allow disclosing majority', async function () { - const bitmap = Array(90).fill('0'); - bitmap[88] = '1'; - bitmap[89] = '1'; + const selector_dg1 = Array(88).fill('0'); w = await circuit.calculateWitness({ ...inputs, - bitmap: bitmap.map(String), + selector_dg1: selector_dg1.map(String), }); - const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[3]']); + const older_than = formatOlderThan(await circuit.getOutput(w, ['older_than[2]'])); - const reveal_unpacked = formatAndUnpackReveal(revealedData_packed); - //console.log("reveal_unpacked", reveal_unpacked) - - expect(reveal_unpacked[88]).to.equal('1'); - expect(reveal_unpacked[89]).to.equal('8'); + expect(older_than[0]).to.equal(1); + expect(older_than[1]).to.equal(8); }); it("shouldn't allow disclosing wrong majority", async function () { - const bitmap = Array(90).fill('0'); - bitmap[88] = '1'; - bitmap[89] = '1'; + const selector_dg1 = Array(88).fill('0'); w = await circuit.calculateWitness({ ...inputs, majority: ['5', '0'].map((char) => BigInt(char.charCodeAt(0)).toString()), - bitmap: bitmap.map(String), + selector_dg1: selector_dg1.map(String), }); const revealedData_packed = await circuit.getOutput(w, ['revealedData_packed[3]']); @@ -196,3 +190,7 @@ describe('Disclose', function () { expect(reveal_unpacked[89]).to.equal('\x00'); }); }); + +const formatOlderThan = (older_than: any) => { + return Object.values(older_than).map((value: any) => parseInt(value) - 48); +}; diff --git a/circuits/tests/dsc.test.ts b/circuits/tests/dsc.test.ts new file mode 100644 index 00000000..34f1990f --- /dev/null +++ b/circuits/tests/dsc.test.ts @@ -0,0 +1,116 @@ +import { assert, expect } from 'chai'; +import fs from 'fs'; +const forge = require('node-forge'); +import path from 'path'; +import { wasm as wasm_tester } from 'circom_tester'; +import { getCSCAInputs } from '../../common/src/utils/csca'; +import { + mock_dsc_sha1_rsa_2048, + mock_dsc_sha256_rsa_2048, + mock_dsc_sha256_rsapss_2048, + mock_csca_sha1_rsa_2048, + mock_csca_sha256_rsa_2048, + mock_csca_sha256_rsapss_2048, +} from '../../common/src/constants/mockCertificates'; +import { k_dsc, n_dsc } from '../../common/src/constants/constants'; +import { getCircuitName } from '../../common/src/utils/certificates/handleCertificate'; +import { customHasher } from '../../common/src/utils/pubkeyTree'; +import { poseidon2 } from 'poseidon-lite'; + +const sigAlgs = [ + { sigAlg: 'rsa', hashFunction: 'sha256' }, + { sigAlg: 'rsa', hashFunction: 'sha1' }, + { sigAlg: 'rsapss', hashFunction: 'sha256' }, +]; + +sigAlgs.forEach(({ sigAlg, hashFunction }) => { + describe(`DSC chain certificate - ${hashFunction.toUpperCase()} ${sigAlg.toUpperCase()}`, function () { + this.timeout(0); // Disable timeout + let circuit; + const max_cert_bytes = 960; + + // Mock certificates based on signature algorithm and hash function + let dscCertPem; + let cscaCertPem; + + switch (`${sigAlg}_${hashFunction}`) { + case 'rsa_sha256': + dscCertPem = mock_dsc_sha256_rsa_2048; + cscaCertPem = mock_csca_sha256_rsa_2048; + break; + case 'rsa_sha1': + dscCertPem = mock_dsc_sha1_rsa_2048; + cscaCertPem = mock_csca_sha1_rsa_2048; + break; + case 'rsapss_sha256': + dscCertPem = mock_dsc_sha256_rsapss_2048; + cscaCertPem = mock_csca_sha256_rsapss_2048; + break; + default: + throw new Error('Unsupported signature algorithm and hash function combination'); + } + + const dscCert = forge.pki.certificateFromPem(dscCertPem); + const cscaCert = forge.pki.certificateFromPem(cscaCertPem); + + const inputs = getCSCAInputs( + BigInt(0).toString(), + dscCert, + cscaCert, + n_dsc, + k_dsc, + n_dsc, + k_dsc, + max_cert_bytes, + true + ); + + before(async () => { + const circuitPath = path.resolve( + __dirname, + `../circuits/dsc/instances/${getCircuitName('dsc', sigAlg, hashFunction)}_2048.circom` + ); + circuit = await wasm_tester(circuitPath, { + include: [ + 'node_modules', + './node_modules/@zk-kit/binary-merkle-root.circom/src', + './node_modules/circomlib/circuits', + ], + }); + }); + + // it('verify dsc has been signed by the csca', () => { + // const tbsCertAsn1 = forge.pki.getTBSCertificate(dscCert); + // const tbsCertDer = forge.asn1.toDer(tbsCertAsn1).getBytes(); + // let md; + // switch (hashFunction) { + // case 'sha256': + // md = forge.md.sha256.create(); + // break; + // case 'sha1': + // md = forge.md.sha1.create(); + // break; + // default: + // throw new Error('Unsupported hash function'); + // } + // md.update(tbsCertDer); + // const tbsHash = md.digest().getBytes(); + // const signature = dscCert.signature; + // const publicKey = cscaCert.publicKey; + // const verified = publicKey.verify(tbsHash, signature); + // expect(verified).to.be.true; + // }); + + it('should compile and load the circuit', () => { + expect(circuit).to.not.be.undefined; + }); + + it('should compute the correct output', async () => { + const witness = await circuit.calculateWitness(inputs.inputs, true); + const blinded_dsc_commitment = (await circuit.getOutput(witness, ['blinded_dsc_commitment'])) + .blinded_dsc_commitment; + console.log('\x1b[34m%s\x1b[0m', 'blinded_dsc_commitment: ', blinded_dsc_commitment); + expect(blinded_dsc_commitment).to.be.not.null; + }); + }); +}); diff --git a/circuits/tests/dsc/dsc_sha1_rsa_2048.test.ts b/circuits/tests/dsc/dsc_sha1_rsa_2048.test.ts deleted file mode 100644 index f4111057..00000000 --- a/circuits/tests/dsc/dsc_sha1_rsa_2048.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { assert, expect } from 'chai'; -import fs from 'fs'; -const forge = require('node-forge'); -import path from 'path'; -import { wasm as wasm_tester } from 'circom_tester'; -import { getCSCAInputs } from '../../../common/src/utils/csca'; -import { - mock_dsc_sha1_rsa_2048, - mock_csca_sha1_rsa_2048, -} from '../../../common/src/constants/mockCertificates'; -import { k_dsc, n_dsc } from '../../../common/src/constants/constants'; - -describe('DSC chain certificate - SHA1 RSA', function () { - this.timeout(0); // Disable timeout - let circuit; - const max_cert_bytes = 960; - const dscCert = forge.pki.certificateFromPem(mock_dsc_sha1_rsa_2048); - const cscaCert = forge.pki.certificateFromPem(mock_csca_sha1_rsa_2048); - - const inputs = getCSCAInputs( - BigInt(0).toString(), - dscCert, - cscaCert, - n_dsc, - k_dsc, - n_dsc, - k_dsc, - max_cert_bytes, - true - ); - - before(async () => { - const circuitPath = path.resolve( - __dirname, - '../../circuits/tests/dsc/dsc_sha1_rsa_2048.circom' - ); - circuit = await wasm_tester(circuitPath, { - include: [ - 'node_modules', - './node_modules/@zk-kit/binary-merkle-root.circom/src', - './node_modules/circomlib/circuits', - ], - }); - }); - - it('verify dsc has been signed by the csca', () => { - const tbsCertAsn1 = forge.pki.getTBSCertificate(dscCert); - const tbsCertDer = forge.asn1.toDer(tbsCertAsn1).getBytes(); - const md = forge.md.sha1.create(); - md.update(tbsCertDer); - const tbsHash = md.digest().getBytes(); - const signature = dscCert.signature; - const cscaCert = forge.pki.certificateFromPem(mock_csca_sha1_rsa_2048); - const publicKey = cscaCert.publicKey; - const verified = publicKey.verify(tbsHash, signature); - expect(verified).to.be.true; - }); - - it('should compile and load the circuit', () => { - expect(circuit).to.not.be.undefined; - }); - - it('should compute the correct output', async () => { - const witness = await circuit.calculateWitness(inputs.inputs, true); - }); -}); diff --git a/circuits/tests/dsc/dsc_sha256_rsa_2048.test.ts b/circuits/tests/dsc/dsc_sha256_rsa_2048.test.ts deleted file mode 100644 index b411a88d..00000000 --- a/circuits/tests/dsc/dsc_sha256_rsa_2048.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { assert, expect } from 'chai'; -import fs from 'fs'; -const forge = require('node-forge'); -import path from 'path'; -import { wasm as wasm_tester } from 'circom_tester'; -import { getCSCAInputs } from '../../../common/src/utils/csca'; -import { - mock_dsc_sha256_rsa_2048, - mock_csca_sha256_rsa_2048, -} from '../../../common/src/constants/mockCertificates'; -import { k_dsc, n_dsc } from '../../../common/src/constants/constants'; - -describe('DSC chain certificate - SHA256 RSA', function () { - this.timeout(0); // Disable timeout - let circuit; - const max_cert_bytes = 960; - const dscCert = forge.pki.certificateFromPem(mock_dsc_sha256_rsa_2048); - const cscaCert = forge.pki.certificateFromPem(mock_csca_sha256_rsa_2048); - - const inputs = getCSCAInputs( - BigInt(0).toString(), - dscCert, - cscaCert, - n_dsc, - k_dsc, - n_dsc, - k_dsc, - max_cert_bytes, - true - ); - - before(async () => { - const circuitPath = path.resolve( - __dirname, - '../../circuits/tests/dsc/dsc_sha256_rsa_2048.circom' - ); - circuit = await wasm_tester(circuitPath, { - include: [ - 'node_modules', - './node_modules/@zk-kit/binary-merkle-root.circom/src', - './node_modules/circomlib/circuits', - ], - }); - }); - - it('verify dsc has been signed by the csca', () => { - const tbsCertAsn1 = forge.pki.getTBSCertificate(dscCert); - const tbsCertDer = forge.asn1.toDer(tbsCertAsn1).getBytes(); - const md = forge.md.sha256.create(); - md.update(tbsCertDer); - const tbsHash = md.digest().getBytes(); - const signature = dscCert.signature; - const publicKey = cscaCert.publicKey; - const verified = publicKey.verify(tbsHash, signature); - expect(verified).to.be.true; - }); - - it('should compile and load the circuit', () => { - expect(circuit).to.not.be.undefined; - }); - - it('should compute the correct output', async () => { - const witness = await circuit.calculateWitness(inputs.inputs, true); - }); -}); diff --git a/circuits/tests/dsc/dsc_sha256_rsapss_2048.test.ts b/circuits/tests/dsc/dsc_sha256_rsapss_2048.test.ts deleted file mode 100644 index 01665cd7..00000000 --- a/circuits/tests/dsc/dsc_sha256_rsapss_2048.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { assert, expect } from 'chai'; -import fs from 'fs'; -const forge = require('node-forge'); -import path from 'path'; -import { wasm as wasm_tester } from 'circom_tester'; -import { getCSCAInputs } from '../../../common/src/utils/csca'; -import crypto from 'crypto'; -import { - mock_dsc_sha256_rsapss_2048, - mock_csca_sha256_rsapss_2048, -} from '../../../common/src/constants/mockCertificates'; -import { k_dsc, n_dsc } from '../../../common/src/constants/constants'; - -describe('DSC chain certificate - SHA256 RSA-PSS', function () { - this.timeout(0); // Disable timeout - let circuit; - const max_cert_bytes = 960; - const dscCert = forge.pki.certificateFromPem(mock_dsc_sha256_rsapss_2048); - const cscaCert = forge.pki.certificateFromPem(mock_csca_sha256_rsapss_2048); - - const inputs = getCSCAInputs( - BigInt(0).toString(), - dscCert, - cscaCert, - n_dsc, - k_dsc, - n_dsc, - k_dsc, - max_cert_bytes, - true - ); - - before(async () => { - const circuitPath = path.resolve( - __dirname, - '../../circuits/tests/dsc/dsc_sha256_rsapss_2048.circom' - ); - circuit = await wasm_tester(circuitPath, { - include: [ - 'node_modules', - './node_modules/@zk-kit/binary-merkle-root.circom/src', - './node_modules/circomlib/circuits', - ], - }); - }); - // TODO: Verify the certificate chain in ts too. - // it('verify dsc has been signed by the csca using RSA-PSS', () => { - // // Extract TBS (To Be Signed) certificate - // const tbsCertAsn1 = forge.pki.getTBSCertificate(dscCert); - // const tbsCertDer = forge.asn1.toDer(tbsCertAsn1).getBytes(); - - // // Create SHA-256 hash of the TBS certificate - // const tbsHash = crypto.createHash('sha256').update(Buffer.from(tbsCertDer, 'binary')).digest(); - - // // Extract signature from DSC certificate - // const signature = Buffer.from(dscCert.signature, 'binary'); - - // // Get public key from CSCA certificate - // const publicKeyPem = forge.pki.publicKeyToPem(cscaCert.publicKey); - // const publicKey = crypto.createPublicKey(publicKeyPem); - - // // Verify signature - // const pssOptions = { - // saltLength: 32, - // mgf1Hash: 'sha256' - // }; - - // try { - // const verifier = crypto.createVerify('RSA-SHA256'); - // verifier.update(tbsHash); - // const isValid = verifier.verify({ - // key: publicKey, - // padding: crypto.constants.RSA_PKCS1_PSS_PADDING, - // saltLength: pssOptions.saltLength - // }, signature); - - // console.log('TBS Hash:', tbsHash.toString('hex')); - // console.log('Signature:', signature.toString('hex')); - // console.log('Public Key:', publicKeyPem); - // console.log('Verification result:', isValid); - - // expect(isValid).to.be.true; - // } catch (error) { - // console.error('Verification error:', error); - // throw error; - // } - // }) - - it('should compile and load the circuit', () => { - expect(circuit).to.not.be.undefined; - }); - - it('should compute the correct output', async () => { - const witness = await circuit.calculateWitness(inputs.inputs, true); - }); - it('should fail to calculate witness with invalid inputs', async function () { - try { - const invalidInputs = { - ...inputs.inputs, - dsc_signature: Array(k_dsc) - .fill(0) - .map((byte) => BigInt(byte).toString()), - }; - await circuit.calculateWitness(invalidInputs); - expect.fail('Expected an error but none was thrown.'); - } catch (error) { - expect(error.message).to.include('Assert Failed'); - } - }); -}); diff --git a/circuits/tests/ofac/ofac.test.ts b/circuits/tests/ofac/ofac.test.ts index e8e978b9..54abf2ff 100644 --- a/circuits/tests/ofac/ofac.test.ts +++ b/circuits/tests/ofac/ofac.test.ts @@ -2,16 +2,16 @@ import { expect } from 'chai'; import path from 'path'; import { wasm as wasm_tester } from 'circom_tester'; import { generateCircuitInputsOfac } from '../../../common/src/utils/generateInputs'; -import { getLeaf } from '../../../common/src/utils/pubkeyTree'; +import { generateCommitment, getLeaf } from '../../../common/src/utils/pubkeyTree'; import { SMT } from '@ashpect/smt'; -import { poseidon1, poseidon2, poseidon6 } from 'poseidon-lite'; +import { poseidon2 } from 'poseidon-lite'; import { LeanIMT } from '@zk-kit/lean-imt'; -import { castFromUUID, formatMrz, packBytes } from '../../../common/src/utils/utils'; +import { formatMrz, packBytes } from '../../../common/src/utils/utils'; import passportNojson from '../../../common/ofacdata/outputs/passportNoSMT.json'; import nameDobjson from '../../../common/ofacdata/outputs/nameDobSMT.json'; import namejson from '../../../common/ofacdata/outputs/nameSMT.json'; import { PassportData } from '../../../common/src/utils/types'; -import { k_dsc, n_dsc, PASSPORT_ATTESTATION_ID } from '../../../common/src/constants/constants'; +import { PASSPORT_ATTESTATION_ID } from '../../../common/src/constants/constants'; import crypto from 'crypto'; import { genMockPassportData } from '../../../common/src/utils/genMockPassportData'; @@ -35,19 +35,19 @@ function getPassportInputs(passportData: PassportData) { const majority = '18'; const user_identifier = crypto.randomUUID(); - const bitmap = Array(90).fill('1'); + const selector_dg1 = Array(88).fill('1'); + const selector_older_than = '1'; const scope = '@coboyApp'; - const pubkey_leaf = getLeaf(passportData.dsc, n_dsc, k_dsc); + const pubkey_leaf = getLeaf(passportData.dsc); const mrz_bytes = packBytes(formatMrz(passportData.mrz)); - const commitment = poseidon6([ + const commitment = generateCommitment( secret, PASSPORT_ATTESTATION_ID, pubkey_leaf, - mrz_bytes[0], - mrz_bytes[1], - mrz_bytes[2], - ]); + mrz_bytes, + passportData.dg2Hash + ); return { secret: secret, @@ -55,7 +55,8 @@ function getPassportInputs(passportData: PassportData) { passportData: passportData, commitment: commitment, majority: majority, - bitmap: bitmap, + selector_dg1: selector_dg1, + selector_older_than: selector_older_than, scope: scope, user_identifier: user_identifier, }; @@ -102,7 +103,8 @@ describe('OFAC - Passport number match', function () { inputs.passportData, tree, inputs.majority, - inputs.bitmap, + inputs.selector_dg1, + inputs.selector_older_than, inputs.scope, inputs.user_identifier, passno_smt, @@ -116,7 +118,8 @@ describe('OFAC - Passport number match', function () { mockInputs.passportData, tree, mockInputs.majority, - mockInputs.bitmap, + mockInputs.selector_dg1, + mockInputs.selector_older_than, mockInputs.scope, mockInputs.user_identifier, passno_smt, @@ -189,7 +192,8 @@ describe('OFAC - Name and DOB match', function () { inputs.passportData, tree, inputs.majority, - inputs.bitmap, + inputs.selector_dg1, + inputs.selector_older_than, inputs.scope, inputs.user_identifier, namedob_smt, @@ -203,7 +207,8 @@ describe('OFAC - Name and DOB match', function () { mockInputs.passportData, tree, mockInputs.majority, - mockInputs.bitmap, + mockInputs.selector_dg1, + mockInputs.selector_older_than, mockInputs.scope, mockInputs.user_identifier, namedob_smt, @@ -276,7 +281,8 @@ describe('OFAC - Name match', function () { inputs.passportData, tree, inputs.majority, - inputs.bitmap, + inputs.selector_dg1, + inputs.selector_older_than, inputs.scope, inputs.user_identifier, name_smt, @@ -290,7 +296,8 @@ describe('OFAC - Name match', function () { mockInputs.passportData, tree, mockInputs.majority, - mockInputs.bitmap, + mockInputs.selector_dg1, + mockInputs.selector_older_than, mockInputs.scope, mockInputs.user_identifier, name_smt, diff --git a/circuits/tests/utils/is_older_than.test.ts b/circuits/tests/other_circuits/is_older_than.test.ts similarity index 100% rename from circuits/tests/utils/is_older_than.test.ts rename to circuits/tests/other_circuits/is_older_than.test.ts diff --git a/circuits/tests/utils/is_valid.test.ts b/circuits/tests/other_circuits/is_valid.test.ts similarity index 100% rename from circuits/tests/utils/is_valid.test.ts rename to circuits/tests/other_circuits/is_valid.test.ts diff --git a/circuits/tests/other_circuits/leaf_hasher.test.ts b/circuits/tests/other_circuits/leaf_hasher.test.ts new file mode 100644 index 00000000..75d16231 --- /dev/null +++ b/circuits/tests/other_circuits/leaf_hasher.test.ts @@ -0,0 +1,116 @@ +import { expect } from 'chai'; +import { X509Certificate } from 'crypto'; +import path from 'path'; +import { wasm as wasm_tester } from 'circom_tester'; +import forge from 'node-forge'; + +import { + mock_dsc_sha256_rsa_2048, + mock_csca_sha256_rsa_2048, + mock_dsc_sha1_rsa_2048, + mock_csca_sha1_rsa_2048, + mock_dsc_sha256_ecdsa, +} from '../../../common/src/constants/mockCertificates'; +import { hexToDecimal, splitToWords, toUnsignedByte } from '../../../common/src/utils/utils'; +import { getLeaf, customHasher } from '../../../common/src/utils/pubkeyTree'; +import { + k_dsc, + k_dsc_ecdsa, + n_dsc, + n_dsc_ecdsa, + SignatureAlgorithmIndex, +} from '../../../common/src/constants/constants'; +import { + parseCertificate, + parseDSC, +} from '../../../common/src/utils/certificates/handleCertificate'; +import { genMockPassportData } from '../../../common/src/utils/genMockPassportData'; +import { generateCircuitInputsInCircuits } from '../utils/generateMockInputsInCircuits'; + +function loadCertificates(dscCertContent: string, cscaCertContent: string) { + const dscCert = new X509Certificate(dscCertContent); + const cscaCert = new X509Certificate(cscaCertContent); + const dscCert_forge = forge.pki.certificateFromPem(dscCertContent); + const cscaCert_forge = forge.pki.certificateFromPem(cscaCertContent); + + return { dscCert, cscaCert, dscCert_forge, cscaCert_forge }; +} + +describe('LeafHasher Light', function () { + this.timeout(0); + let circuit; + + this.beforeAll(async () => { + const circuitPath = path.resolve( + __dirname, + '../../circuits/tests/utils/leafHasher_tester.circom' + ); + circuit = await wasm_tester(circuitPath, { + include: [ + 'node_modules', + './node_modules/@zk-kit/binary-merkle-root.circom/src', + './node_modules/circomlib/circuits', + ], + }); + }); + + // describe('CustomHasher - getLeaf ECDSA', async () => { + // const cert = mock_dsc_sha256_ecdsa; + // const { signatureAlgorithm, hashFunction, x, y, bits, curve, exponent } = parseCertificate(cert); + // console.log(parseCertificate(cert)); + // const leaf_light = getLeaf(cert, n_dsc_ecdsa, k_dsc_ecdsa); + // console.log('\x1b[34m', 'customHasher output: ', leaf_light, '\x1b[0m'); + + // const passportData = genMockPassportData('ecdsa_sha256', 'FRA', '000101', '300101'); + // const mock_inputs = generateCircuitInputsInCircuits(passportData, 'register'); + + // const signatureAlgorithmIndex = SignatureAlgorithmIndex[`${signatureAlgorithm}_${curve || exponent}_${hashFunction}_${bits}`]; + // console.log('\x1b[34m', 'signatureAlgorithmIndex: ', signatureAlgorithmIndex, '\x1b[0m'); + // it('should extract and log certificate information', async () => { + // const inputs = { + // in: mock_inputs.pubKey, + // sigAlg: signatureAlgorithmIndex, + // }; + // const witness = await circuit.calculateWitness(inputs, true); + // const leafValueCircom = (await circuit.getOutput(witness, ['out'])).out; + // console.log('\x1b[34m', 'leafValueCircom: ', leafValueCircom, '\x1b[0m'); + // expect(leafValueCircom).to.equal(leaf_light); + // }); + // }); + + describe('CustomHasher - customHasher', async () => { + const passportData = genMockPassportData('rsa_sha256', 'FRA', '000101', '300101'); + it('should extract and log certificate information', async () => { + const inputs = { + in: passportData.dg2Hash.map((x) => toUnsignedByte(x).toString()), + }; + const witness = await circuit.calculateWitness(inputs, true); + const leafValueCircom = (await circuit.getOutput(witness, ['out'])).out; + console.log('\x1b[34m', 'hashValueCircom: ', leafValueCircom, '\x1b[0m'); + + const hashValue = customHasher(passportData.dg2Hash.map((x) => toUnsignedByte(x).toString())); + console.log('\x1b[34m', 'hashValue: ', hashValue, '\x1b[0m'); + }); + }); + + // describe('CustomHasher - getLeaf RSA', async () => { + // const cert = mock_dsc_sha1_rsa_2048; + // const { signatureAlgorithm, hashFunction, modulus, x, y, bits, curve, exponent } = + // parseCertificate(cert); + // console.log(parseCertificate(cert)); + // const leaf_light = getLeaf(cert, n_dsc, k_dsc); + // console.log('\x1b[34m', 'customHasher: ', leaf_light, '\x1b[0m'); + // it('should extract and log certificate information', async () => { + // const inputs = { + // in: splitToWords(BigInt(hexToDecimal(modulus)), n_dsc, k_dsc), + // sigAlg: + // SignatureAlgorithmIndex[ + // `${signatureAlgorithm}_${curve || exponent}_${hashFunction}_${bits}` + // ], + // }; + // const witness = await circuit.calculateWitness(inputs, true); + // const leafValueCircom = (await circuit.getOutput(witness, ['out'])).out; + // expect(leafValueCircom).to.equal(leaf_light); + // }); + // }); +}); diff --git a/circuits/tests/prove.test.ts b/circuits/tests/prove.test.ts index e7526dfb..d9185acb 100644 --- a/circuits/tests/prove.test.ts +++ b/circuits/tests/prove.test.ts @@ -3,16 +3,19 @@ import { expect } from 'chai'; import path from 'path'; import { wasm as wasm_tester } from 'circom_tester'; import { generateCircuitInputsProve } from '../../common/src/utils/generateInputs'; -import { n_dsc, k_dsc, k_dsc_ecdsa, n_dsc_ecdsa } from '../../common/src/constants/constants'; import { genMockPassportData } from '../../common/src/utils/genMockPassportData'; import { getCircuitName } from '../../common/src/utils/certificates/handleCertificate'; import { SignatureAlgorithm } from '../../common/src/utils/types'; import crypto from 'crypto'; +import { customHasher } from '../../common/src/utils/pubkeyTree'; +import { poseidon2 } from 'poseidon-lite'; const sigAlgs = [ { sigAlg: 'rsa', hashFunction: 'sha1' }, { sigAlg: 'rsa', hashFunction: 'sha256' }, { sigAlg: 'rsapss', hashFunction: 'sha256' }, + { sigAlg: 'ecdsa', hashFunction: 'sha256' }, + { sigAlg: 'ecdsa', hashFunction: 'sha1' }, ]; sigAlgs.forEach(({ sigAlg, hashFunction }) => { @@ -29,14 +32,20 @@ sigAlgs.forEach(({ sigAlg, hashFunction }) => { const majority = '18'; const user_identifier = crypto.randomUUID(); const scope = '@coboyApp'; - const bitmap = Array(90).fill('1'); + const selector_dg1 = Array(88).fill('1'); + const selector_older_than = '1'; + const secret = 0; + const dsc_secret = 0; + const selector_mode = 1; const inputs = generateCircuitInputsProve( + selector_mode, + secret, + dsc_secret, passportData, - sigAlg === 'ecdsa' ? n_dsc_ecdsa : n_dsc, - sigAlg === 'ecdsa' ? k_dsc_ecdsa : k_dsc, scope, - bitmap, + selector_dg1, + selector_older_than, majority, user_identifier ); @@ -45,7 +54,7 @@ sigAlgs.forEach(({ sigAlg, hashFunction }) => { circuit = await wasm_tester( path.join( __dirname, - `../circuits/prove/${getCircuitName('prove', sigAlg, hashFunction)}.circom` + `../circuits/prove/instances/${getCircuitName('prove', sigAlg, hashFunction)}.circom` ), { include: [ @@ -64,8 +73,15 @@ sigAlgs.forEach(({ sigAlg, hashFunction }) => { it('should calculate the witness with correct inputs', async function () { const w = await circuit.calculateWitness(inputs); await circuit.checkConstraints(w); - const nullifier = (await circuit.getOutput(w, ['nullifier'])).nullifier; + console.log('\x1b[34m%s\x1b[0m', 'nullifier', nullifier); + const commitment = (await circuit.getOutput(w, ['commitment'])).commitment; + console.log('\x1b[34m%s\x1b[0m', 'commitment', commitment); + const blinded_dsc_commitment = (await circuit.getOutput(w, ['blinded_dsc_commitment'])) + .blinded_dsc_commitment; + console.log('\x1b[34m%s\x1b[0m', 'blinded_dsc_commitment', blinded_dsc_commitment); + + expect(blinded_dsc_commitment).to.be.not.null; expect(nullifier).to.be.not.null; }); @@ -73,7 +89,7 @@ sigAlgs.forEach(({ sigAlg, hashFunction }) => { try { const invalidInputs = { ...inputs, - mrz: Array(93) + dg1: Array(93) .fill(0) .map((byte) => BigInt(byte).toString()), }; @@ -84,13 +100,11 @@ sigAlgs.forEach(({ sigAlg, hashFunction }) => { } }); - it('should fail to calculate witness with invalid dataHashes', async function () { + it('should fail to calculate witness with invalid eContent', async function () { try { const invalidInputs = { ...inputs, - dataHashes: inputs.dataHashes.map((byte: string) => - String((parseInt(byte, 10) + 1) % 256) - ), + eContent: inputs.eContent.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)), }; await circuit.calculateWitness(invalidInputs); expect.fail('Expected an error but none was thrown.'); diff --git a/circuits/tests/register.test.ts b/circuits/tests/register.test.ts deleted file mode 100644 index 704185b3..00000000 --- a/circuits/tests/register.test.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { describe } from 'mocha'; -import { assert, expect } from 'chai'; -import path from 'path'; -import { wasm as wasm_tester } from 'circom_tester'; -import { poseidon6 } from 'poseidon-lite'; -import { generateCircuitInputsRegister } from '../../common/src/utils/generateInputs'; -import { hexToDecimal, packBytes } from '../../common/src/utils/utils'; -import { - n_dsc, - k_dsc, - n_dsc_ecdsa, - k_dsc_ecdsa, - PASSPORT_ATTESTATION_ID, -} from '../../common/src/constants/constants'; -import { genMockPassportData } from '../../common/src/utils/genMockPassportData'; -import { getCircuitName, parseDSC } from '../../common/src/utils/certificates/handleCertificate'; -import { getLeaf } from '../../common/src/utils/pubkeyTree'; -import { SignatureAlgorithm } from '../../common/src/utils/types'; - -const sigAlgs = [ - { sigAlg: 'rsa', hashFunction: 'sha1' }, - { sigAlg: 'rsa', hashFunction: 'sha256' }, - { sigAlg: 'rsapss', hashFunction: 'sha256' }, - { sigAlg: 'ecdsa', hashFunction: 'sha256' }, - { sigAlg: 'ecdsa', hashFunction: 'sha1' }, -]; - -sigAlgs.forEach(({ sigAlg, hashFunction }) => { - describe(`Register - ${hashFunction.toUpperCase()} ${sigAlg.toUpperCase()}`, function () { - this.timeout(0); - let circuit: any; - - const passportData = genMockPassportData( - `${sigAlg}_${hashFunction}` as SignatureAlgorithm, - 'FRA', - '000101', - '300101' - ); - const secret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString(); - const dscSecret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString(); - - const inputs = generateCircuitInputsRegister( - secret, - dscSecret, - PASSPORT_ATTESTATION_ID, - passportData, - sigAlg === 'ecdsa' ? n_dsc_ecdsa : n_dsc, - sigAlg === 'ecdsa' ? k_dsc_ecdsa : k_dsc - ); - - before(async () => { - circuit = await wasm_tester( - path.join( - __dirname, - `../circuits/register/${getCircuitName('register', sigAlg, hashFunction)}.circom` - ), - { - include: [ - 'node_modules', - './node_modules/@zk-kit/binary-merkle-root.circom/src', - './node_modules/circomlib/circuits', - ], - } - ); - }); - - it('should compile and load the circuit', async function () { - expect(circuit).to.not.be.undefined; - }); - - it('should calculate the witness with correct inputs', async function () { - console.log('inputs', inputs); - const w = await circuit.calculateWitness(inputs); - await circuit.checkConstraints(w); - - const nullifier = (await circuit.getOutput(w, ['nullifier'])).nullifier; - console.log('\x1b[34m%s\x1b[0m', 'nullifier', nullifier); - const commitment_circom = (await circuit.getOutput(w, ['commitment'])).commitment; - console.log('\x1b[34m%s\x1b[0m', 'commitment', commitment_circom); - const blinded_dsc_commitment = (await circuit.getOutput(w, ['blinded_dsc_commitment'])) - .blinded_dsc_commitment; - console.log('\x1b[34m%s\x1b[0m', 'blinded_dsc_commitment', blinded_dsc_commitment); - - const n = sigAlg === 'ecdsa' ? n_dsc_ecdsa : n_dsc; - const k = sigAlg === 'ecdsa' ? k_dsc_ecdsa : k_dsc; - const mrz_bytes = packBytes(inputs.mrz); - const leaf = getLeaf(passportData.dsc, n, k).toString(); - - const commitment_bytes = poseidon6([ - inputs.secret[0], - PASSPORT_ATTESTATION_ID, - leaf, - mrz_bytes[0], - mrz_bytes[1], - mrz_bytes[2], - ]); - const commitment_js = commitment_bytes.toString(); - console.log('commitment_js', commitment_js); - console.log('commitment_circom', commitment_circom); - expect(commitment_circom).to.be.equal(commitment_js); - }); - - it('should fail to calculate witness with invalid mrz', async function () { - try { - const invalidInputs = { - ...inputs, - mrz: Array(93) - .fill(0) - .map((byte) => BigInt(byte).toString()), - }; - await circuit.calculateWitness(invalidInputs); - expect.fail('Expected an error but none was thrown.'); - } catch (error) { - expect(error.message).to.include('Assert Failed'); - } - }); - - it('should fail to calculate witness with invalid dataHashes', async function () { - try { - const invalidInputs = { - ...inputs, - dataHashes: inputs.dataHashes.map((byte: string) => - String((parseInt(byte, 10) + 1) % 256) - ), - }; - await circuit.calculateWitness(invalidInputs); - expect.fail('Expected an error but none was thrown.'); - } catch (error) { - expect(error.message).to.include('Assert Failed'); - } - }); - - it('should fail to calculate witness with invalid signature', async function () { - try { - const invalidInputs = { - ...inputs, - signature: inputs.signature - ? inputs.signature.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)) - : undefined, - signature_s: inputs.signature_s - ? inputs.signature_s.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)) - : undefined, - }; - await circuit.calculateWitness(invalidInputs); - expect.fail('Expected an error but none was thrown.'); - } catch (error) { - expect(error.message).to.include('Assert Failed'); - } - }); - }); -}); diff --git a/circuits/tests/utils/Mgf1_sha256.test.ts b/circuits/tests/utils/Mgf1_sha256.test.ts deleted file mode 100644 index f6b26b3d..00000000 --- a/circuits/tests/utils/Mgf1_sha256.test.ts +++ /dev/null @@ -1,149 +0,0 @@ -import path from 'path'; -import { createHash, randomBytes } from 'node:crypto'; -import { wasm as wasm_tester } from 'circom_tester'; - -describe('Mgf1_sha256 Circuit Test', function () { - this.timeout(0); // Disable timeout - const hashLen = 32; // SHA256 length - - const compileCircuit = async (circuitPath: string) => { - return await wasm_tester(path.join(circuitPath), { - include: ['node_modules'], - }); - }; - - function buffer2bitArray(b) { - const res = []; - for (let i = 0; i < b.length; i++) { - for (let j = 0; j < 8; j++) { - res.push((b[i] >> (7 - j)) & 1); - } - } - return res; - } - - function num2Bits(n, input) { - let out = []; - for (let i = 0; i < n; i++) { - out[i] = (input >> i) & 1; - } - return out; - } - - const bitArray2buffer = (a) => { - const len = Math.floor((a.length - 1) / 8) + 1; - const b = Buffer.alloc(len); - - for (let i = 0; i < a.length; i++) { - const p = Math.floor(i / 8); - b[p] = b[p] | (Number(a[i]) << (7 - (i % 8))); - } - return b; - }; - - const MGF1 = (mgfSeed: Buffer, maskLen: number) => { - const hLen = hashLen; - if (maskLen > 0xffffffff * hLen) { - throw new Error('mask too long'); - } - - var T = []; - for (var i = 0; i <= Math.ceil(maskLen / hLen) - 1; i++) { - var C = Buffer.alloc(4); - C.writeUInt32BE(i); - const hash3 = createHash('sha256'); - hash3.update(Buffer.concat([mgfSeed, C])); - T.push(hash3.digest()); - } - return Buffer.concat(T).slice(0, maskLen); - }; - - it('Should compile', async function () { - await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom'); - }); - - it('Should generate correct MGF1 output - 4 Byte Seed', async function () { - const seed = 12345678; - const maskLen = 32; - const seedLen = 4; // 4 bytes - set in the circuit - - const bitArray = num2Bits(seedLen * 8, seed); - const mgfSeed = bitArray2buffer(bitArray); - - const expected = MGF1(mgfSeed, maskLen); - - const circuit = await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom'); - const expected_mask_output = buffer2bitArray(expected); - const inputs = { - seed: seed, - expected_mask_output, - }; - - const witness = await circuit.calculateWitness(inputs); - await circuit.checkConstraints(witness); - }); - - it('Should generate correct MGF1 output - 32 Byte Seed', async function () { - const randBytes = randomBytes(32); - - const maskLen = 32; - const seedLen = 32; // set in circuit - const expected = MGF1(randBytes, maskLen); - - const circuit = await compileCircuit( - 'circuits/tests/mgf1Sha256/Mgf1Sha256_32Bytes_tester.circom' - ); - const expected_mask_output = buffer2bitArray(expected); - const inputs = { - seed: buffer2bitArray(randBytes), - expected_mask_output, - }; - - const witness = await circuit.calculateWitness(inputs); - await circuit.checkConstraints(witness); - }); - - it('Should generate correct MGF1 output - seedLen value > than actual seed length', async function () { - const seed = 1234; - const maskLen = 32; - const seedLen = 4; //set in circuit - - const bitArray = num2Bits(seedLen * 8, seed); - const mgfSeed = bitArray2buffer(bitArray); - - const expected = MGF1(mgfSeed, maskLen); - - const circuit = await compileCircuit('circuits/tests/mgf1Sha256/Mgf1Sha256_tester.circom'); - const expected_mask_output = buffer2bitArray(expected); - const inputs = { - seed: seed, - expected_mask_output, - }; - - const witness = await circuit.calculateWitness(inputs); - await circuit.checkConstraints(witness); - }); - - it('Should generate correct MGF1 output - maskLen == 1', async function () { - const seed = 12345678; - const maskLen = 1; - const seedLen = 4; - - const bitArray = num2Bits(seedLen * 8, seed); - const mgfSeed = bitArray2buffer(bitArray); - - const expected = MGF1(mgfSeed, maskLen); - - const circuit = await compileCircuit( - 'circuits/tests/mgf1Sha256/Mgf1Sha256_1ByteMask_tester.circom' - ); - const expected_mask_output = buffer2bitArray(expected); - const inputs = { - seed: seed, - expected_mask_output, - }; - - const witness = await circuit.calculateWitness(inputs); - await circuit.checkConstraints(witness); - }); -}); diff --git a/circuits/tests/utils/generateMockInputsInCircuits.ts b/circuits/tests/utils/generateMockInputsInCircuits.ts new file mode 100644 index 00000000..af49775d --- /dev/null +++ b/circuits/tests/utils/generateMockInputsInCircuits.ts @@ -0,0 +1,49 @@ +import { PassportData } from '../../../common/src/utils/types'; +import { CircuitName } from '../../../common/src/utils/appType'; +import { + DEFAULT_MAJORITY, + k_dsc, + k_dsc_ecdsa, + n_dsc, + n_dsc_ecdsa, + PASSPORT_ATTESTATION_ID, +} from '../../../common/src/constants/constants'; +import { + generateCircuitInputsProve, + generateCircuitInputsRegister, +} from '../../../common/src/utils/generateInputs'; +import { parseCertificate } from '../../../common/src/utils/certificates/handleCertificate'; + +const majority = DEFAULT_MAJORITY; +const scope = '@spaceShips'; + +export const generateCircuitInputsInCircuits = ( + passportData: PassportData, + circuit: CircuitName +): any => { + const { signatureAlgorithm } = parseCertificate(passportData.dsc); + + switch (circuit) { + case 'register': { + const secret = BigInt(0).toString(); + const dscSecret = BigInt(0).toString(); + const attestationId = PASSPORT_ATTESTATION_ID; + return generateCircuitInputsRegister(secret, dscSecret, attestationId, passportData); + } + case 'prove': { + // const selector_dg1 = Array(90).fill('1'); + // const user_identifier = crypto.randomUUID(); + // return generateCircuitInputsProve( + // passportData, + // n_dsc, + // k_dsc, + // scope, + // selector_dg1, + // majority, + // user_identifier + // ); + } + default: + throw new Error('Invalid circuit'); + } +}; diff --git a/circuits/tests/utils/leaf_hasher.test.ts b/circuits/tests/utils/leaf_hasher.test.ts deleted file mode 100644 index d88d5c03..00000000 --- a/circuits/tests/utils/leaf_hasher.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { expect } from 'chai'; -import { X509Certificate } from 'crypto'; -import path from 'path'; -import { wasm as wasm_tester } from 'circom_tester'; -import forge from 'node-forge'; - -import { - mock_dsc_sha256_rsa_2048, - mock_csca_sha256_rsa_2048, - mock_dsc_sha1_rsa_2048, - mock_csca_sha1_rsa_2048, - mock_dsc_sha1_ecdsa, -} from '../../../common/src/constants/mockCertificates'; -import { hexToDecimal, splitToWords } from '../../../common/src/utils/utils'; -import { getLeaf, leafHasherLight } from '../../../common/src/utils/pubkeyTree'; -import { k_dsc, n_dsc, SignatureAlgorithmIndex } from '../../../common/src/constants/constants'; -import { - parseCertificate, - parseDSC, -} from '../../../common/src/utils/certificates/handleCertificate'; - -function loadCertificates(dscCertContent: string, cscaCertContent: string) { - const dscCert = new X509Certificate(dscCertContent); - const cscaCert = new X509Certificate(cscaCertContent); - const dscCert_forge = forge.pki.certificateFromPem(dscCertContent); - const cscaCert_forge = forge.pki.certificateFromPem(cscaCertContent); - - return { dscCert, cscaCert, dscCert_forge, cscaCert_forge }; -} - -describe('LeafHasher Light', function () { - this.timeout(0); - let circuit; - - this.beforeAll(async () => { - const circuitPath = path.resolve( - __dirname, - '../../circuits/tests/utils/leafHasherLight_tester.circom' - ); - circuit = await wasm_tester(circuitPath, { - include: [ - 'node_modules', - './node_modules/@zk-kit/binary-merkle-root.circom/src', - './node_modules/circomlib/circuits', - ], - }); - }); - // describe('Circuit', () => { - // it('should compile and load the circuit', () => { - // expect(circuit).not.to.be.undefined; - // }); - // }); - - describe('LeafHasherLight - getLeaf', async () => { - const cert = mock_dsc_sha1_rsa_2048; - const { signatureAlgorithm, hashFunction, modulus, x, y, bits, curve, exponent } = - parseCertificate(cert); - console.log(parseCertificate(cert)); - const leaf_light = getLeaf(cert, n_dsc, k_dsc); - console.log('\x1b[34m', 'leafHasherLight: ', leaf_light, '\x1b[0m'); - it('should extract and log certificate information', async () => { - const inputs = { - in: splitToWords(BigInt(hexToDecimal(modulus)), n_dsc, k_dsc), - sigAlg: - SignatureAlgorithmIndex[ - `${signatureAlgorithm}_${curve || exponent}_${hashFunction}_${bits}` - ], - }; - const witness = await circuit.calculateWitness(inputs, true); - const output = await circuit.getOutput(witness, ['out']); - console.log('\x1b[34m', 'output: ', output, '\x1b[0m'); - }); - }); -}); diff --git a/circuits/tests/utils/rsa_verifier.test.ts b/circuits/tests/utils/rsa_verifier.test.ts deleted file mode 100644 index a5733799..00000000 --- a/circuits/tests/utils/rsa_verifier.test.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { expect } from 'chai'; -import { X509Certificate } from 'crypto'; -import path from 'path'; -import { getCSCAInputs, getTBSHash } from '../../../common/src/utils/csca'; -import { wasm as wasm_tester } from 'circom_tester'; -import forge from 'node-forge'; - -import { - mock_dsc_sha256_rsa_2048, - mock_csca_sha256_rsa_2048, - mock_dsc_sha1_rsa_2048, - mock_csca_sha1_rsa_2048, -} from '../../../common/src/constants/mockCertificates'; - -function loadCertificates(dscCertContent: string, cscaCertContent: string) { - const dscCert = new X509Certificate(dscCertContent); - const cscaCert = new X509Certificate(cscaCertContent); - const dscCert_forge = forge.pki.certificateFromPem(dscCertContent); - const cscaCert_forge = forge.pki.certificateFromPem(cscaCertContent); - - return { dscCert, cscaCert, dscCert_forge, cscaCert_forge }; -} - -describe('RSA Verifier', function () { - this.timeout(0); - let circuit; - - this.beforeAll(async () => { - const circuitPath = path.resolve(__dirname, '../../circuits/tests/utils/rsa_verifier.circom'); - circuit = await wasm_tester(circuitPath, { - include: [ - 'node_modules', - './node_modules/@zk-kit/binary-merkle-root.circom/src', - './node_modules/circomlib/circuits', - ], - }); - }); - describe('Circuit', () => { - it('should compile and load the circuit', () => { - expect(circuit).not.to.be.undefined; - }); - }); - - describe('SHA-256 certificates', async () => { - const { dscCert, cscaCert, dscCert_forge, cscaCert_forge } = loadCertificates( - mock_dsc_sha256_rsa_2048, - mock_csca_sha256_rsa_2048 - ); - const n = 121; - const k = 17; - - it('should verify DSC has been signed by the CSCA', () => { - const isVerified = dscCert.verify(cscaCert.publicKey); - console.log(`SHA-256 DSC certificate verification result: ${isVerified}`); - expect(isVerified).to.be.true; - }); - - // it('should extract and log certificate information', async () => { - // const csca_inputs = getCSCAInputs('0', dscCert_forge, cscaCert_forge, n, k, n, k, 2048, true); - // const tbsCertificateHashFormatted = getTBSHash(dscCert_forge, 'sha256', n, k); - - // const inputs = { - // message: tbsCertificateHashFormatted, - // signature: csca_inputs.inputs.dsc_signature, - // modulus: csca_inputs.inputs.dsc_modulus, - // }; - // const witness = await circuit.calculateWitness(inputs, true); - // }); - }); - - describe('SHA-1 certificates', () => { - const { dscCert, cscaCert, dscCert_forge, cscaCert_forge } = loadCertificates( - mock_dsc_sha1_rsa_2048, - mock_csca_sha1_rsa_2048 - ); - - it('should verify DSC has been signed by the CSCA', () => { - const isVerified = dscCert.verify(cscaCert.publicKey); - console.log(`SHA-1 DSC certificate verification result: ${isVerified}`); - expect(isVerified).to.be.true; - }); - /// TODO: Use SHA1RSA verifier circuit (won't work either case because of padding) - // it('should extract and log certificate information', async () => { - // const csca_inputs = getCSCAInputs("0", dscCert_forge, cscaCert_forge, 64, 32, 64, 32, 2048, true); - // const tbsCertificateHashFormatted = getTBSHash(dscCert_forge, 'sha1'); - - // const inputs = { - // "message": tbsCertificateHashFormatted, - // "signature": csca_inputs.dsc_signature, - // "modulus": csca_inputs.csca_modulus - // } - // console.log("final inputs: ", inputs); - // const witness = await circuit.calculateWitness(inputs, true); - // console.log(witness); - // }); - }); -}); diff --git a/circuits/tests/utils/rsapss_verifier.test.ts b/circuits/tests/utils/rsapss_verifier.test.ts deleted file mode 100644 index b6dd17f0..00000000 --- a/circuits/tests/utils/rsapss_verifier.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { expect } from 'chai'; -import { X509Certificate } from 'crypto'; -import path from 'path'; -import { getCSCAInputs, getTBSHash } from '../../../common/src/utils/csca'; -import { wasm as wasm_tester } from 'circom_tester'; -import forge from 'node-forge'; - -import { - mock_dsc_sha256_rsapss_2048, - mock_csca_sha256_rsapss_2048, -} from '../../../common/src/constants/mockCertificates'; - -function loadCertificates(dscCertContent: string, cscaCertContent: string) { - const dscCert = new X509Certificate(dscCertContent); - const cscaCert = new X509Certificate(cscaCertContent); - const dscCert_forge = forge.pki.certificateFromPem(dscCertContent); - const cscaCert_forge = forge.pki.certificateFromPem(cscaCertContent); - - return { dscCert, cscaCert, dscCert_forge, cscaCert_forge }; -} - -describe('RSAPSS Verifier', function () { - this.timeout(0); - let circuit; - - this.beforeAll(async () => { - const circuitPath = path.resolve( - __dirname, - '../../circuits/tests/utils/rsapss_verifier.circom' - ); - circuit = await wasm_tester(circuitPath, { - include: [ - 'node_modules', - './node_modules/@zk-kit/binary-merkle-root.circom/src', - './node_modules/circomlib/circuits', - ], - }); - }); - describe('Circuit', () => { - it('should compile and load the circuit', () => { - expect(circuit).not.to.be.undefined; - }); - }); - - describe('SHA-256 certificates', async () => { - const { dscCert, cscaCert, dscCert_forge, cscaCert_forge } = loadCertificates( - mock_dsc_sha256_rsapss_2048, - mock_csca_sha256_rsapss_2048 - ); - const n = 64; - const k = 32; - - it('should verify DSC has been signed by the CSCA', () => { - const isVerified = dscCert.verify(cscaCert.publicKey); - console.log(`SHA-256 DSC certificate verification result: ${isVerified}`); - expect(isVerified).to.be.true; - }); - - // it('should extract and log certificate information', async () => { - // const csca_inputs = getCSCAInputs('0', dscCert_forge, cscaCert_forge, n, k, n, k, 960, true); - // // const tbsCertificateHashFormatted = getTBSHash(dscCert_forge, 'sha256', n, k); - - // const inputs = { - // raw_message: csca_inputs.inputs.raw_dsc_cert, - // raw_message_padded_bytes: csca_inputs.inputs.raw_dsc_cert_padded_bytes, - // signature: csca_inputs.inputs.dsc_signature, - // modulus: csca_inputs.inputs.dsc_modulus, - // }; - // /const witness = await circuit.calculateWitness(inputs, true); - // }); - }); -}); diff --git a/common/src/constants/constants.ts b/common/src/constants/constants.ts index 54984b25..c20b0742 100644 --- a/common/src/constants/constants.ts +++ b/common/src/constants/constants.ts @@ -1,3 +1,5 @@ +import { SignatureAlgorithm } from "../utils/types" + export const RELAYER_URL = "https://0pw5u65m3a.execute-api.eu-north-1.amazonaws.com/api-stage/mint" //export const COMMITMENT_TREE_TRACKER_URL = "https://app.proofofpassport.com/apiv2/download-merkle-tree" export const COMMITMENT_TREE_TRACKER_URL = "https://proofofpassport-merkle-tree.xyz/api/download-merkle-tree" @@ -20,6 +22,31 @@ export const RPC_URL = "https://opt-mainnet.g.alchemy.com/v2/Mjj_SdklUaCdR6EPfVK export const DEVELOPMENT_MODE = true export const DEFAULT_MAJORITY = "18" +export const MAX_PADDED_ECONTENT_LEN: Partial> = { + rsa_65537_sha256_2048: 384, + rsa_65537_sha1_2048: 448, + rsapss_65537_sha256_2048: 640, + ecdsa_secp256r1_sha1_256: 448, + ecdsa_secp256r1_sha256_256: 640, + ecdsa_secp384r1_sha384_384: 640, +} + +export const MAX_PADDED_SIGNED_ATTR_LEN: Partial> = { + rsa_65537_sha256_2048: 320, + rsa_65537_sha1_2048: 448, + rsapss_65537_sha256_2048: 512, + ecdsa_secp256r1_sha1_256: 448, + ecdsa_secp256r1_sha256_256: 512, + ecdsa_secp384r1_sha384_384: 512, +} + +export const MAX_CERT_BYTES: Partial> = { + rsa_65537_sha256_4096: 512, + rsa_65537_sha1_4096: 640, + rsapss_65537_sha256_4096: 768, +} +// possible values because of sha1 constaints: 448, 576, 640 + export enum SignatureAlgorithmIndex { rsa_65537_sha256_2048 = 1, rsa_65537_sha1_2048 = 3, @@ -27,6 +54,9 @@ export enum SignatureAlgorithmIndex { ecdsa_secp256r1_sha1_256 = 7, ecdsa_secp256r1_sha256_256 = 8, ecdsa_secp384r1_sha384_384 = 9, + rsa_65537_sha256_4096 = 10, + rsa_65537_sha1_4096 = 11, + rsapss_65537_sha256_4096 = 12, } export const attributeToPosition = { diff --git a/common/src/utils/certificates/handleCertificate.ts b/common/src/utils/certificates/handleCertificate.ts index 3d3fbe5c..5a743291 100644 --- a/common/src/utils/certificates/handleCertificate.ts +++ b/common/src/utils/certificates/handleCertificate.ts @@ -137,7 +137,13 @@ export const parseDSC = (pemContent: string) => { const key = ec.keyFromPublic(publicKeyBuffer); const x = key.getPublic().getX().toString('hex'); const y = key.getPublic().getY().toString('hex'); - const bits = key.getPublic().getX().bitLength(); + + const fieldSizeMap: { [key: string]: number } = { + 'secp256r1': 256, + 'secp384r1': 384, + }; + const bits = fieldSizeMap[curve] + publicKeyDetails = { curve, x, y, bits }; } else { const publicKey = cert.subjectPublicKeyInfo.subjectPublicKey; diff --git a/common/src/utils/certificates/publicKeyDetails.ts b/common/src/utils/certificates/publicKeyDetails.ts index fb239142..42bb978e 100644 --- a/common/src/utils/certificates/publicKeyDetails.ts +++ b/common/src/utils/certificates/publicKeyDetails.ts @@ -50,7 +50,11 @@ export function parseECParameters(publicKeyInfo: any): PublicKeyDetailsECDSA { const key = ec.keyFromPublic(publicKeyBuffer); const x = key.getPublic().getX().toString('hex'); const y = key.getPublic().getY().toString('hex'); - const bits = key.getPublic().getX().bitLength(); + const fieldSizeMap: { [key: string]: number } = { + 'secp256r1': 256, + 'secp384r1': 384, + }; + const bits = fieldSizeMap[curve] const params = asn1.fromBER(algorithmParams.valueBeforeDecodeView).result; const valueBlock: any = params.valueBlock; diff --git a/common/src/utils/commitmentTree.ts b/common/src/utils/commitmentTree.ts deleted file mode 100644 index fa28286b..00000000 --- a/common/src/utils/commitmentTree.ts +++ /dev/null @@ -1,5 +0,0 @@ -// import { LeanIMT } from "@zk-kit/lean-imt"; -// import { poseidon2 } from "poseidon-lite"; -// import axios from "axios"; -// import { COMMITMENT_TREE_TRACKER_URL } from "../constants/constants"; - diff --git a/common/src/utils/csca.ts b/common/src/utils/csca.ts index 2e3f0b7b..a03e8556 100644 --- a/common/src/utils/csca.ts +++ b/common/src/utils/csca.ts @@ -127,8 +127,7 @@ export function getCSCAInputs(dscSecret: string, dscCertificate: any, cscaCertif // merkle tree saga const pemContent = forge.pki.certificateToPem(cscaCertificate); - const leaf = getLeaf(pemContent, n_csca, k_csca); - console.log('leaf', leaf); + const leaf = getLeaf(pemContent); const [root, proof] = getCSCAModulusProof(leaf, n_csca, k_csca); const { signatureAlgorithm: signatureAlgorithmName, hashFunction } = getSignatureAlgorithmDetails(signatureAlgorithm); @@ -138,10 +137,10 @@ export function getCSCAInputs(dscSecret: string, dscCertificate: any, cscaCertif { "raw_dsc_cert": dsc_message_padded_formatted, "raw_dsc_cert_padded_bytes": [dsc_messagePaddedLen_formatted], - "csca_modulus": csca_modulus_formatted, - "dsc_signature": dsc_signature_formatted, - "dsc_modulus": dsc_modulus_formatted, - "start_index": [startIndex_formatted], + "csca_pubKey": csca_modulus_formatted, + "signature": dsc_signature_formatted, + "dsc_pubKey": dsc_modulus_formatted, + "dsc_pubKey_offset": [startIndex_formatted], "secret": [dscSecret], "merkle_root": [BigInt(root).toString()], "path": proof.pathIndices.map(index => index.toString()), diff --git a/common/src/utils/genMockPassportData.ts b/common/src/utils/genMockPassportData.ts index 9e92491e..ca833639 100644 --- a/common/src/utils/genMockPassportData.ts +++ b/common/src/utils/genMockPassportData.ts @@ -96,8 +96,9 @@ export function genMockPassportData( return { dsc: dsc, mrz: mrz, - dataGroupHashes: concatenatedDataHashes, - eContent: eContent, + dg2Hash: sampleDataHashes[0][1], + eContent: concatenatedDataHashes, + signedAttr: eContent, encryptedDigest: signatureBytes, photoBase64: 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABjElEQVR42mL8//8/AyUYiBQYmIy3...', mockUser: true, diff --git a/common/src/utils/generateInputs.ts b/common/src/utils/generateInputs.ts index b1160de1..18e81c1b 100644 --- a/common/src/utils/generateInputs.ts +++ b/common/src/utils/generateInputs.ts @@ -1,13 +1,11 @@ -import { MAX_DATAHASHES_LEN, PUBKEY_TREE_DEPTH, DEVELOPMENT_MODE, DEFAULT_USER_ID_TYPE, n_dsc, k_dsc } from '../constants/constants'; +import { PUBKEY_TREE_DEPTH, DEFAULT_USER_ID_TYPE, MAX_PADDED_ECONTENT_LEN, MAX_PADDED_SIGNED_ATTR_LEN } from '../constants/constants'; import { assert, shaPad } from './shaPad'; import { PassportData } from './types'; import { - arraysAreEqual, bytesToBigDecimal, formatMrz, hash, splitToWords, - toUnsignedByte, getCurrentDateYYMMDD, generateMerkleProof, generateSMTProof, @@ -17,143 +15,34 @@ import { castFromUUID, castFromScope, parseUIDToBigInt, + formatDg2Hash, + getNAndK, } from './utils'; import { LeanIMT } from "@zk-kit/lean-imt"; -import { getLeaf } from "./pubkeyTree"; +import { generateCommitment, getLeaf } from "./pubkeyTree"; import { getNameLeaf, getNameDobLeaf, getPassportNumberLeaf } from "./ofacTree"; -import { poseidon6 } from "poseidon-lite"; import { packBytes } from "../utils/utils"; -import { getCSCAModulusMerkleTree } from "./csca"; import { SMT } from "@ashpect/smt" import { parseCertificate } from './certificates/handleCertificate'; -export function generateCircuitInputsRegister( - secret: string, - dscSecret: string, - attestation_id: string, - passportData: PassportData, - n_dsc: number, - k_dsc: number, - // mocks: PassportData[] = mockPassportDatas - mocks?: PassportData[] -) { - const { mrz, dsc, dataGroupHashes, eContent, encryptedDigest } = - passportData; - - const { signatureAlgorithm, hashFunction, hashLen, x, y, modulus } = parseCertificate(dsc); - - // const tree = getCSCAModulusMerkleTree(DEVELOPMENT_MODE); - - const supportedAlgorithms = [ - { signatureAlgorithm: 'rsa', hashFunction: 'sha1' }, - { signatureAlgorithm: 'rsa', hashFunction: 'sha256' }, - { signatureAlgorithm: 'rsapss', hashFunction: 'sha256' }, - { signatureAlgorithm: 'ecdsa', hashFunction: 'sha1' }, - { signatureAlgorithm: 'ecdsa', hashFunction: 'sha256' }, - ]; - - const isSupported = supportedAlgorithms.some( - (alg) => alg.signatureAlgorithm === signatureAlgorithm && alg.hashFunction === hashFunction - ); - - if (!isSupported) { - throw new Error(`Verification of ${signatureAlgorithm} with ${hashFunction} has not been implemented.`); - } - - const formattedMrz = formatMrz(mrz); - const mrzHash = hash(hashFunction, formattedMrz); - - const dg1HashOffset = findSubarrayIndex(dataGroupHashes, mrzHash); - assert(dg1HashOffset !== -1, 'MRZ hash index not found in dataGroupHashes'); - - const concatHash = hash(hashFunction, dataGroupHashes); - - assert( - arraysAreEqual(concatHash, eContent.slice(eContent.length - hashLen)), - 'concatHash is not at the right place in eContent' - ); - - if (dataGroupHashes.length > MAX_DATAHASHES_LEN) { - throw new Error( - `This length of datagroups (${dataGroupHashes.length} bytes) is currently unsupported. Please contact us so we add support!` - ); - } - - const [messagePadded, messagePaddedLen] = shaPad( - signatureAlgorithm, - new Uint8Array(dataGroupHashes), - MAX_DATAHASHES_LEN - ); - - let signatureComponents: any; - let dscModulusComponents: any; - - if (signatureAlgorithm === 'ecdsa') { - const { r, s } = extractRSFromSignature(encryptedDigest); - - signatureComponents = { - signature_r: splitToWords(BigInt(hexToDecimal(r)), n_dsc, k_dsc), - signature_s: splitToWords(BigInt(hexToDecimal(s)), n_dsc, k_dsc) - }; - - dscModulusComponents = { - dsc_modulus_x: splitToWords(BigInt(hexToDecimal(x)), n_dsc, k_dsc), - dsc_modulus_y: splitToWords(BigInt(hexToDecimal(y)), n_dsc, k_dsc) - }; - } else { - signatureComponents = { - signature: splitToWords( - BigInt(bytesToBigDecimal(encryptedDigest)), - n_dsc, - k_dsc - ) - }; - - dscModulusComponents = { - dsc_modulus: splitToWords( - BigInt(hexToDecimal(modulus)), - n_dsc, - k_dsc - ) - }; - } - - return { - secret: [secret], - mrz: formattedMrz.map((byte) => String(byte)), - dg1_hash_offset: [dg1HashOffset.toString()], - dataHashes: Array.from(messagePadded).map((x) => x.toString()), - datahashes_padded_length: [messagePaddedLen.toString()], - eContent: eContent.map(toUnsignedByte).map((byte) => String(byte)), - ...signatureComponents, - ...dscModulusComponents, - attestation_id: [attestation_id], - dsc_secret: [dscSecret], - }; -} - export function generateCircuitInputsDisclose( secret: string, attestation_id: string, passportData: PassportData, merkletree: LeanIMT, majority: string, - bitmap: string[], + selector_dg1: string[], + selector_older_than: string, scope: string, user_identifier: string ) { - const pubkey_leaf = getLeaf(passportData.dsc, n_dsc, k_dsc); + + const pubkey_leaf = getLeaf(passportData.dsc); const formattedMrz = formatMrz(passportData.mrz); const mrz_bytes = packBytes(formattedMrz); - const commitment = poseidon6([ - secret, - attestation_id, - pubkey_leaf, - mrz_bytes[0], - mrz_bytes[1], - mrz_bytes[2], - ]); + + const commitment = generateCommitment(secret, attestation_id, pubkey_leaf, mrz_bytes, passportData.dg2Hash); const index = findIndexInTree(merkletree, commitment); @@ -167,12 +56,14 @@ export function generateCircuitInputsDisclose( secret: [secret], attestation_id: [attestation_id], pubkey_leaf: [pubkey_leaf.toString()], - mrz: formattedMrz.map((byte) => String(byte)), + dg1: formattedMrz.map((byte) => String(byte)), + dg2_hash: formatDg2Hash(passportData.dg2Hash), merkle_root: [merkletree.root.toString()], merkletree_size: [BigInt(depthForThisOne).toString()], path: merkleProofIndices.map((index) => BigInt(index).toString()), siblings: merkleProofSiblings.map((index) => BigInt(index).toString()), - bitmap: bitmap, + selector_dg1: selector_dg1, + selector_older_than: [BigInt(selector_older_than).toString()], scope: [castFromScope(scope)], current_date: getCurrentDateYYMMDD().map(datePart => BigInt(datePart).toString()), majority: majority.split('').map(char => BigInt(char.charCodeAt(0)).toString()), @@ -186,15 +77,16 @@ export function generateCircuitInputsOfac( passportData: PassportData, merkletree: LeanIMT, majority: string, - bitmap: string[], + selector_dg1: string[], + selector_older_than: string, scope: string, user_identifier: string, sparsemerkletree: SMT, proofLevel: number, ) { - const result = generateCircuitInputsDisclose(secret, attestation_id, passportData, merkletree, majority, bitmap, scope, user_identifier); - const { majority: _, scope: __, bitmap: ___, user_identifier: ____, ...finalResult } = result; + const result = generateCircuitInputsDisclose(secret, attestation_id, passportData, merkletree, majority, selector_dg1, selector_older_than, scope, user_identifier); + const { majority: _, scope: __, selector_dg1: ___, selector_older_than: _____, user_identifier: ______, ...finalResult } = result; const mrz_bytes = formatMrz(passportData.mrz); const passport_leaf = getPassportNumberLeaf(mrz_bytes.slice(49, 58)) @@ -238,34 +130,108 @@ export function findIndexInTree(tree: LeanIMT, commitment: bigint): number { export function generateCircuitInputsProve( + selector_mode: number | string, + secret: number | string, + dsc_secret: number | string, passportData: PassportData, - n_dsc: number, - k_dsc: number, scope: string, - bitmap: string[], + selector_dg1: string[], + selector_older_than: string, majority: string, user_identifier: string, user_identifier_type: 'uuid' | 'hex' | 'ascii' = DEFAULT_USER_ID_TYPE ) { + const { mrz, eContent, signedAttr, encryptedDigest, dsc, dg2Hash } = passportData; + const { signatureAlgorithm, hashFunction, hashLen, x, y, modulus, curve, exponent, bits } = parseCertificate(passportData.dsc); + + const signatureAlgorithmFullName = `${signatureAlgorithm}_${curve || exponent}_${hashFunction}_${bits}`; + let pubKey: any; + let signature: any; + + const { n, k } = getNAndK(signatureAlgorithm); + + if (signatureAlgorithm === 'ecdsa') { + const { r, s } = extractRSFromSignature(encryptedDigest); + const signature_r = splitToWords(BigInt(hexToDecimal(r)), n, k) + const signature_s = splitToWords(BigInt(hexToDecimal(s)), n, k) + signature = [...signature_r, ...signature_s] + const dsc_modulus_x = splitToWords(BigInt(hexToDecimal(x)), n, k) + const dsc_modulus_y = splitToWords(BigInt(hexToDecimal(y)), n, k) + pubKey = [...dsc_modulus_x, ...dsc_modulus_y] + } else { + + signature = splitToWords( + BigInt(bytesToBigDecimal(encryptedDigest)), + n, + k + ) + + pubKey = splitToWords( + BigInt(hexToDecimal(modulus)), + n, + k + ) + } + + const formattedMrz = formatMrz(mrz); + const dg1Hash = hash(hashFunction, formattedMrz); + + const dg1HashOffset = findSubarrayIndex(eContent, dg1Hash) + console.log('dg1HashOffset', dg1HashOffset); + assert(dg1HashOffset !== -1, `DG1 hash ${dg1Hash} not found in eContent`); + + const eContentHash = hash(hashFunction, eContent); + const eContentHashOffset = findSubarrayIndex(signedAttr, eContentHash) + console.log('eContentHashOffset', eContentHashOffset); + assert(eContentHashOffset !== -1, `eContent hash ${eContentHash} not found in signedAttr`); + + if (eContent.length > MAX_PADDED_ECONTENT_LEN[signatureAlgorithmFullName]) { + console.error(`Data hashes too long (${eContent.length} bytes). Max length is ${MAX_PADDED_ECONTENT_LEN[signatureAlgorithmFullName]} bytes.`); + throw new Error(`This length of datagroups (${eContent.length} bytes) is currently unsupported. Please contact us so we add support!`); + } + + const [eContentPadded, eContentLen] = shaPad( + signatureAlgorithm, + new Uint8Array(eContent), + MAX_PADDED_ECONTENT_LEN[signatureAlgorithmFullName] + ); + const [signedAttrPadded, signedAttrPaddedLen] = shaPad( + signatureAlgorithm, + new Uint8Array(signedAttr), + MAX_PADDED_SIGNED_ATTR_LEN[signatureAlgorithmFullName] + ); - const register_inputs = generateCircuitInputsRegister('0', '0', '0', passportData, n_dsc, k_dsc); - const current_date = getCurrentDateYYMMDD().map(datePart => BigInt(datePart).toString()); - // Ensure majority is at least two digits const formattedMajority = majority.length === 1 ? `0${majority}` : majority; + const majority_ascii = formattedMajority.split('').map(char => char.charCodeAt(0)) return { - mrz: register_inputs.mrz, - dg1_hash_offset: register_inputs.dg1_hash_offset, // uncomment when adding new circuits - dataHashes: register_inputs.dataHashes, - datahashes_padded_length: register_inputs.datahashes_padded_length, - eContent: register_inputs.eContent, - signature: register_inputs.signature, - dsc_modulus: register_inputs.dsc_modulus, - current_date: current_date, - bitmap: bitmap, - majority: formattedMajority.split('').map(char => BigInt(char.charCodeAt(0)).toString()), - user_identifier: [parseUIDToBigInt(user_identifier, user_identifier_type)], - scope: [castFromScope(scope)] + selector_mode: formatInput(selector_mode), + dg1: formatInput(formattedMrz), + dg1_hash_offset: formatInput(dg1HashOffset), + dg2_hash: formatDg2Hash(dg2Hash), + eContent: Array.from(eContentPadded).map((x) => x.toString()), + eContent_padded_length: formatInput(eContentLen), + signed_attr: Array.from(signedAttrPadded).map((x) => x.toString()), + signed_attr_padded_length: formatInput(signedAttrPaddedLen), + signed_attr_econtent_hash_offset: formatInput(eContentHashOffset), + signature: signature, + pubKey: pubKey, + current_date: formatInput(getCurrentDateYYMMDD()), + selector_dg1: formatInput(selector_dg1), + selector_older_than: formatInput(selector_older_than), + majority: formatInput(majority_ascii), + user_identifier: formatInput(parseUIDToBigInt(user_identifier, user_identifier_type)), + scope: formatInput(castFromScope(scope)), + secret: formatInput(secret), + dsc_secret: formatInput(dsc_secret), }; -} \ No newline at end of file +} + +function formatInput(input: any) { + if (Array.isArray(input)) { + return input.map(item => BigInt(item).toString()); + } else { + return [BigInt(input).toString()]; + } +} diff --git a/common/src/utils/pubkeyTree.ts b/common/src/utils/pubkeyTree.ts index d7419810..9936f2c7 100644 --- a/common/src/utils/pubkeyTree.ts +++ b/common/src/utils/pubkeyTree.ts @@ -1,12 +1,12 @@ import { PUBKEY_TREE_DEPTH, COMMITMENT_TREE_TRACKER_URL, SignatureAlgorithmIndex } from "../constants/constants"; import { LeanIMT } from '@zk-kit/imt' import axios from "axios"; -import { poseidon16, poseidon2 } from 'poseidon-lite'; -import { hexToDecimal, splitToWords } from './utils'; +import { poseidon16, poseidon2, poseidon6, poseidon7 } from 'poseidon-lite'; +import { formatDg2Hash, getNAndK, hexToDecimal, splitToWords } from './utils'; import { parseCertificate } from "./certificates/handleCertificate"; import { flexiblePoseidon } from "./poseidon"; -export function leafHasherLight(pubKeyFormatted: string[]) { +export function customHasher(pubKeyFormatted: string[]) { const rounds = Math.ceil(pubKeyFormatted.length / 16); const hash = new Array(rounds); for (let i = 0; i < rounds; i++) { @@ -23,22 +23,25 @@ export function leafHasherLight(pubKeyFormatted: string[]) { return finalHash.toString(); } -export function getLeaf(dsc: string, n: number, k: number): string { +export function getLeaf(dsc: string): string { const { signatureAlgorithm, hashFunction, modulus, x, y, bits, curve, exponent } = parseCertificate(dsc); + const { n, k } = getNAndK(signatureAlgorithm); console.log(`${signatureAlgorithm}_${curve || exponent}_${hashFunction}_${bits}`) - const sigAlgIndex = SignatureAlgorithmIndex[`${signatureAlgorithm}_${curve || exponent}_${hashFunction}_${bits}`] - if (sigAlgIndex === undefined) { - throw new Error(`Signature algorithm not found: ${signatureAlgorithm}_${curve || exponent}_${hashFunction}_${bits}`) - } + const sigAlgKey = `${signatureAlgorithm}_${curve || exponent}_${hashFunction}_${bits}`; + const sigAlgIndex = SignatureAlgorithmIndex[sigAlgKey]; + if (sigAlgIndex == undefined) { + console.error(`\x1b[31mInvalid signature algorithm: ${sigAlgKey}\x1b[0m`); + throw new Error(`Invalid signature algorithm: ${sigAlgKey}`); + } if (signatureAlgorithm === 'ecdsa') { let qx = splitToWords(BigInt(hexToDecimal(x)), n, k); let qy = splitToWords(BigInt(hexToDecimal(y)), n, k); - return leafHasherLight([sigAlgIndex, ...qx, ...qy]) + return customHasher([sigAlgIndex, ...qx, ...qy]) } else { const pubkeyChunked = splitToWords(BigInt(hexToDecimal(modulus)), n, k); - return leafHasherLight([sigAlgIndex, ...pubkeyChunked]); + return customHasher([sigAlgIndex, ...pubkeyChunked]); } } @@ -50,4 +53,18 @@ export async function getTreeFromTracker(): Promise { ); imt.import(response.data) return imt +} + +export function generateCommitment(secret: string, attestation_id: string, pubkey_leaf: string, mrz_bytes: any[], dg2Hash: any[]) { + const dg2Hash2 = customHasher(formatDg2Hash(dg2Hash).map(x => x.toString())); + const commitment = poseidon7([ + secret, + attestation_id, + pubkey_leaf, + mrz_bytes[0], + mrz_bytes[1], + mrz_bytes[2], + dg2Hash2 + ]); + return commitment; } \ No newline at end of file diff --git a/common/src/utils/types.ts b/common/src/utils/types.ts index 51968d8f..1694d111 100644 --- a/common/src/utils/types.ts +++ b/common/src/utils/types.ts @@ -1,8 +1,9 @@ export type PassportData = { mrz: string; + dg2Hash?: number[]; dsc: string; - dataGroupHashes: number[]; eContent: number[]; + signedAttr: number[]; encryptedDigest: number[]; photoBase64: string; mockUser?: boolean; diff --git a/common/src/utils/utils.ts b/common/src/utils/utils.ts index efcd32f6..b8493199 100644 --- a/common/src/utils/utils.ts +++ b/common/src/utils/utils.ts @@ -4,6 +4,7 @@ import { sha1 } from 'js-sha1'; import { sha384, sha512_256 } from 'js-sha512'; import { SMT } from '@ashpect/smt'; import forge from 'node-forge'; +import { n_dsc, k_dsc, n_dsc_ecdsa, k_dsc_ecdsa } from '../constants/constants'; export function formatMrz(mrz: string) { const mrzCharcodes = [...mrz].map((char) => char.charCodeAt(0)); @@ -16,6 +17,20 @@ export function formatMrz(mrz: string) { return mrzCharcodes; } +export function getNAndK(sigAlg: 'rsa' | 'ecdsa' | 'rsapss') { + const n = sigAlg === 'ecdsa' ? n_dsc_ecdsa : n_dsc; + const k = sigAlg === 'ecdsa' ? k_dsc_ecdsa : k_dsc; + return { n, k }; +} + +export function formatDg2Hash(dg2Hash: number[]) { + const unsignedBytesDg2Hash = dg2Hash.map((x) => toUnsignedByte(x)); + while (unsignedBytesDg2Hash.length < 64) { // pad it to 64 bytes to correspond to the hash length of sha512 and avoid multiplying circuits + unsignedBytesDg2Hash.push(0); + } + return unsignedBytesDg2Hash; +} + export function formatAndConcatenateDataHashes( dataHashes: [number, number[]][], hashLen: number, @@ -29,7 +44,7 @@ export function formatAndConcatenateDataHashes( () => Math.floor(Math.random() * 256) - 128 ); - // sha256 with rsa (index of mrzhash is 31) + // // sha256 with rsa (index of mrzhash is 31) // const startingSequence = [ // // SEQUENCE + long form indicator + length (293 bytes) // 48, -126, 1, 37, @@ -167,11 +182,11 @@ export function hexToDecimal(hex: string): string { } // hash logic here because the one in utils.ts only works with node -export function hash(hasFunction: string, bytesArray: number[]): number[] { +export function hash(hashFunction: string, bytesArray: number[]): number[] { const unsignedBytesArray = bytesArray.map((byte) => byte & 0xff); let hashResult: string; - switch (hasFunction) { + switch (hashFunction) { case 'sha1': hashResult = sha1(unsignedBytesArray); break; @@ -185,6 +200,7 @@ export function hash(hasFunction: string, bytesArray: number[]): number[] { hashResult = sha512_256(unsignedBytesArray); break; default: + console.log('\x1b[31m%s\x1b[0m', `${hashFunction} not found in hash`); // Log in red hashResult = sha256(unsignedBytesArray); // Default to sha256 } return hexToSignedBytes(hashResult); diff --git a/common/tests/genMockPassportData.test.ts b/common/tests/genMockPassportData.test.ts index 8839b9c6..d929cc4f 100644 --- a/common/tests/genMockPassportData.test.ts +++ b/common/tests/genMockPassportData.test.ts @@ -31,20 +31,20 @@ describe('Mock Passport Data Generator', function () { }); function verify(passportData: PassportData): boolean { - const { mrz, dsc, dataGroupHashes, eContent, encryptedDigest } = passportData; + const { mrz, dsc, eContent, signedAttr, encryptedDigest } = passportData; const { signatureAlgorithm, hashFunction, hashLen, curve } = parseCertificate(dsc); const formattedMrz = formatMrz(mrz); const mrzHash = hash(hashFunction, formattedMrz); - const dg1HashOffset = findSubarrayIndex(dataGroupHashes, mrzHash) - assert(dg1HashOffset !== -1, 'MRZ hash index not found in dataGroupHashes'); - - const concatHash = hash(hashFunction, dataGroupHashes) + const dg1HashOffset = findSubarrayIndex(eContent, mrzHash) + assert(dg1HashOffset !== -1, 'MRZ hash index not found in eContent'); + console.error("\x1b[32m", "signatureAlgorithm", signatureAlgorithm, " hashFunction", hashFunction, "eContent size", eContent.length, "signedAttr size", signedAttr.length, "\x1b[0m"); + const concatHash = hash(hashFunction, eContent) assert( arraysAreEqual( concatHash, - eContent.slice(eContent.length - hashLen) + signedAttr.slice(signedAttr.length - hashLen) ), - 'concatHash is not at the right place in eContent' + 'concatHash is not at the right place in signedAttr' ); if (signatureAlgorithm === 'ecdsa') { @@ -58,7 +58,7 @@ function verify(passportData: PassportData): boolean { const key = ec.keyFromPublic(publicKeyBuffer); const md = hashFunction === 'sha1' ? forge.md.sha1.create() : forge.md.sha256.create(); - md.update(forge.util.binary.raw.encode(new Uint8Array(eContent))); + md.update(forge.util.binary.raw.encode(new Uint8Array(signedAttr))); const msgHash = md.digest().toHex() const signature_crypto = Buffer.from(encryptedDigest).toString('hex'); @@ -68,7 +68,7 @@ function verify(passportData: PassportData): boolean { const publicKey = cert.publicKey as forge.pki.rsa.PublicKey; const md = forge.md[hashFunction].create(); - md.update(forge.util.binary.raw.encode(new Uint8Array(eContent))); + md.update(forge.util.binary.raw.encode(new Uint8Array(signedAttr))); const signature = Buffer.from(encryptedDigest).toString('binary'); diff --git a/contracts/test/RegisterAndDisclose.ts b/contracts/test/RegisterAndDisclose.ts index 25de48a3..ee360192 100644 --- a/contracts/test/RegisterAndDisclose.ts +++ b/contracts/test/RegisterAndDisclose.ts @@ -84,7 +84,7 @@ describe("OpenPassport - Contracts - Register & Disclose flow", function () { let owner, otherAccount, thirdAccount: Signer; let imt: LeanIMT; - let bitmap, scope, user_address, majority, input_disclose: any; + let selector_dg1, scope, user_address, majority, input_disclose: any; let proof_disclose, publicSignals_disclose, proof_result_disclose, vkey_disclose, verified_disclose: any, rawCallData_disclose, parsedCallData_disclose: any[], formattedCallData_disclose: any; //let proof_csca, publicSignals_csca: any; let secret: string = BigInt(0).toString(); @@ -393,7 +393,7 @@ describe("OpenPassport - Contracts - Register & Disclose flow", function () { // // We only test with the sha256WithRSAEncryption_65537 commitment for now // // refactor in generate inputs function - // bitmap = Array(90).fill("1"); + // selector_dg1 = Array(90).fill("1"); // scope = BigInt(1).toString(); // majority = ["1", "8"]; @@ -403,7 +403,7 @@ describe("OpenPassport - Contracts - Register & Disclose flow", function () { // mockPassportData_sha256_rsa_65537, // imt as any, // majority, - // bitmap, + // selector_dg1, // scope, // BigInt(user_address.toString()).toString() // ); @@ -445,7 +445,7 @@ describe("OpenPassport - Contracts - Register & Disclose flow", function () { // // // refactor in generate inputs function - // // bitmap = Array(90).fill("1"); + // // selector_dg1 = Array(90).fill("1"); // // scope = BigInt(1).toString(); // // user_address = await thirdAccount.getAddress(); // // majority = ["1", "8"]; @@ -455,7 +455,7 @@ describe("OpenPassport - Contracts - Register & Disclose flow", function () { // // passportData, // // imt as any, // // majority, - // // bitmap, + // // selector_dg1, // // scope, // // BigInt(user_address.toString()).toString() // // ); diff --git a/sdk/tests/utils/generateInputsInSdk.ts b/sdk/tests/utils/generateInputsInSdk.ts index d7de8b63..6785fbcd 100644 --- a/sdk/tests/utils/generateInputsInSdk.ts +++ b/sdk/tests/utils/generateInputsInSdk.ts @@ -30,14 +30,14 @@ export const generateCircuitInputsInSdk = ( ); } case 'prove': { - const bitmap = Array(90).fill('1'); + const selector_dg1 = Array(90).fill('1'); const user_identifier = crypto.randomUUID(); return generateCircuitInputsProve( passportData, n_dsc, k_dsc, scope, - bitmap, + selector_dg1, majority, user_identifier );