mirror of
https://github.com/selfxyz/self.git
synced 2026-04-27 03:01:15 -04:00
Merge pull request #203 from zk-passport/refactor-circuits
Refactor circuits
This commit is contained in:
4
.github/workflows/action.yml
vendored
4
.github/workflows/action.yml
vendored
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
signal revealedData[90];
|
||||
// constrain selector_dg1 to be 0s or 1s
|
||||
for (var i = 0; i < 88; i++) {
|
||||
revealedData[i] <== mrz[5+i] * bitmap[i];
|
||||
selector_dg1[i] * (selector_dg1[i] - 1) === 0;
|
||||
}
|
||||
revealedData[88] <== older_than[0] * bitmap[88];
|
||||
revealedData[89] <== older_than[1] * bitmap[89];
|
||||
revealedData_packed <== PackBytes(90)(revealedData);
|
||||
|
||||
signal revealedData[88];
|
||||
for (var i = 0; i < 88; i++) {
|
||||
revealedData[i] <== dg1[5+i] * selector_dg1[i];
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
62
circuits/circuits/dsc/openpassport_dsc.circom
Normal file
62
circuits/circuits/dsc/openpassport_dsc.circom
Normal file
@@ -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]);
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
71
circuits/circuits/prove/openpassport_prove.circom
Normal file
71
circuits/circuits/prove/openpassport_prove.circom
Normal file
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -1,6 +1,6 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "../../utils/isOlderThan.circom";
|
||||
include "../../utils/passport/date/isOlderThan.circom";
|
||||
|
||||
template isOlderThan_tester() {
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "../../utils/isValid.circom";
|
||||
include "../../utils/passport/date/isValid.circom";
|
||||
|
||||
template IsValid_tester() {
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "../../utils/leafHasherLight.circom";
|
||||
|
||||
component main = LeafHasherLightWithSigAlg(32);
|
||||
6
circuits/circuits/tests/utils/leafHasher_tester.circom
Normal file
6
circuits/circuits/tests/utils/leafHasher_tester.circom
Normal file
@@ -0,0 +1,6 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "../../utils/passport/customHashers.circom";
|
||||
|
||||
// component main = LeafHasher(12);
|
||||
component main = CustomHasher(32);
|
||||
@@ -1,3 +0,0 @@
|
||||
include "@zk-email/circuits/lib/rsa.circom";
|
||||
|
||||
component main = RSAVerifier65537(121, 17);
|
||||
@@ -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);
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
@@ -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<hLenBits; i++) {
|
||||
hash[i] <== eMsgInBits[(emLenBits) - hLenBits-8 +i];
|
||||
}
|
||||
|
||||
//DbMask MGF1
|
||||
component MGF1 = Mgf1Sha256(hLen, dbMaskLen);
|
||||
for (var i = 0; i < (hLenBits); i++) {
|
||||
MGF1.seed[i] <== hash[i];
|
||||
}
|
||||
for (var i = 0; i < dbMaskLen * 8; i++) {
|
||||
dbMask[i] <== MGF1.out[i];
|
||||
}
|
||||
|
||||
//DB = maskedDB xor dbMask
|
||||
component xor = Xor2(dbMaskLen * 8);
|
||||
for (var i = 0; i < dbMaskLen * 8; i++) {
|
||||
xor.a[i] <== maskedDB[i];
|
||||
xor.b[i] <== dbMask[i];
|
||||
}
|
||||
// Ref: https://github.com/directdemocracy-vote/app/blob/d0590b5515e749fa72fc50f05062273eb2465da1/httpdocs/app/js/rsa-blind.js#L188-L190
|
||||
for (var i = 0; i < dbMaskLen * 8; i++) {
|
||||
//setting the first leftmost byte to 0
|
||||
if (i==0) {
|
||||
DB[i] <== 0;
|
||||
} else {
|
||||
DB[i] <== xor.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
// If the emLen - hLen - sLen - 2 leftmost octets of DB are not
|
||||
// zero, output "inconsistent" and stop.
|
||||
for (var i = 0; i < (emLenBits-528); i++) { //hLenBits + sLenBits + 16 = 256 + 256 + 16 = 528
|
||||
assert(DB[i] == 0);
|
||||
}
|
||||
// if the octet at position emLen - hLen - sLen - 1 (the
|
||||
// leftmost position is "position 1") does not have hexadecimal
|
||||
// value 0x01, output "inconsistent" and stop.
|
||||
component bits2Num = Bits2Num(8);
|
||||
for (var i = 0; i < 8; i++) {
|
||||
bits2Num.in[7-i] <== DB[(emLenBits)- 528 +i]; //emLen - hLen - sLen - 1
|
||||
|
||||
}
|
||||
assert(bits2Num.out == 1);
|
||||
|
||||
//extract salt
|
||||
for (var i = 0; i < sLenBits; i++) {
|
||||
//last sLenBits (256) bits of DB
|
||||
salt[256 -1 - i] <== DB[(dbMaskLen * 8) -1 - i];
|
||||
}
|
||||
|
||||
//M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ;
|
||||
signal mDash[576]; // 8 + hLen + sLen = 8 + 32 + 32 = 72 bytes = 576 bits
|
||||
for (var i = 0; i < 64; i++) {
|
||||
mDash[i] <== 0;
|
||||
}
|
||||
for (var i = 0 ; i < 256; i++) {
|
||||
mDash[64 + i] <== mHash[i];
|
||||
}
|
||||
for (var i = 0; i < 256; i++) {
|
||||
mDash[320 + i] <== salt[i];
|
||||
}
|
||||
|
||||
component hDash = Sha256(576);
|
||||
hDash.in <== mDash;
|
||||
for (var i = 0; i < 256; i++) {
|
||||
assert(hDash.out[i] == hash[i]);
|
||||
}
|
||||
}
|
||||
@@ -1,201 +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";
|
||||
include "@zk-email/circuits/lib/sha.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 input messagePaddedLen;
|
||||
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;
|
||||
*/
|
||||
mHash <== Sha256Bytes(messageLen)(message, messagePaddedLen);
|
||||
|
||||
|
||||
|
||||
// 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<hLenBits; i++) {
|
||||
hash[i] <== eMsgInBits[(emLenBits) - hLenBits-8 +i];
|
||||
}
|
||||
|
||||
//DbMask MGF1
|
||||
component MGF1 = Mgf1Sha256(hLen, dbMaskLen);
|
||||
for (var i = 0; i < (hLenBits); i++) {
|
||||
MGF1.seed[i] <== hash[i];
|
||||
}
|
||||
for (var i = 0; i < dbMaskLen * 8; i++) {
|
||||
dbMask[i] <== MGF1.out[i];
|
||||
}
|
||||
|
||||
//DB = maskedDB xor dbMask
|
||||
component xor = Xor2(dbMaskLen * 8);
|
||||
for (var i = 0; i < dbMaskLen * 8; i++) {
|
||||
xor.a[i] <== maskedDB[i];
|
||||
xor.b[i] <== dbMask[i];
|
||||
}
|
||||
// Ref: https://github.com/directdemocracy-vote/app/blob/d0590b5515e749fa72fc50f05062273eb2465da1/httpdocs/app/js/rsa-blind.js#L188-L190
|
||||
for (var i = 0; i < dbMaskLen * 8; i++) {
|
||||
//setting the first leftmost byte to 0
|
||||
if (i==0) {
|
||||
DB[i] <== 0;
|
||||
} else {
|
||||
DB[i] <== xor.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
// If the emLen - hLen - sLen - 2 leftmost octets of DB are not
|
||||
// zero, output "inconsistent" and stop.
|
||||
for (var i = 0; i < (emLenBits-528); i++) { //hLenBits + sLenBits + 16 = 256 + 256 + 16 = 528
|
||||
assert(DB[i] == 0);
|
||||
}
|
||||
// if the octet at position emLen - hLen - sLen - 1 (the
|
||||
// leftmost position is "position 1") does not have hexadecimal
|
||||
// value 0x01, output "inconsistent" and stop.
|
||||
component bits2Num = Bits2Num(8);
|
||||
for (var i = 0; i < 8; i++) {
|
||||
bits2Num.in[7-i] <== DB[(emLenBits)- 528 +i]; //emLen - hLen - sLen - 1
|
||||
|
||||
}
|
||||
assert(bits2Num.out == 1);
|
||||
|
||||
//extract salt
|
||||
for (var i = 0; i < sLenBits; i++) {
|
||||
//last sLenBits (256) bits of DB
|
||||
salt[256 -1 - i] <== DB[(dbMaskLen * 8) -1 - i];
|
||||
}
|
||||
|
||||
//M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt ;
|
||||
signal mDash[576]; // 8 + hLen + sLen = 8 + 32 + 32 = 72 bytes = 576 bits
|
||||
for (var i = 0; i < 64; i++) {
|
||||
mDash[i] <== 0;
|
||||
}
|
||||
for (var i = 0 ; i < 256; i++) {
|
||||
mDash[64 + i] <== mHash[i];
|
||||
}
|
||||
for (var i = 0; i < 256; i++) {
|
||||
mDash[320 + i] <== salt[i];
|
||||
}
|
||||
|
||||
component hDash = Sha256(576);
|
||||
hDash.in <== mDash;
|
||||
for (var i = 0; i < 256; i++) {
|
||||
assert(hDash.out[i] == hash[i]);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,16 +1,16 @@
|
||||
pragma circom 2.0.3;
|
||||
|
||||
function min(a, b) {
|
||||
if(a < 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 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<k; idx++){
|
||||
if(a[idx] != 0)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
// function long_is_zero(k, a){
|
||||
// for(var idx=0; idx<k; idx++){
|
||||
// if(a[idx] != 0)
|
||||
// return 0;
|
||||
// }
|
||||
// return 1;
|
||||
// }
|
||||
|
||||
// n bits per register
|
||||
// a has k registers
|
||||
// b has k registers
|
||||
// output has k+1 registers
|
||||
function long_add(n, k, a, b){
|
||||
function long_add_ecdsa(n, k, a, b){
|
||||
var carry = 0;
|
||||
var sum[50];
|
||||
for(var i=0; i<k; i++){
|
||||
@@ -78,46 +78,46 @@ function long_add(n, k, a, b){
|
||||
return sum;
|
||||
}
|
||||
|
||||
// n bits per register
|
||||
// a has k registers
|
||||
// b has k registers
|
||||
// c has k registers
|
||||
// d has k registers
|
||||
// output has k+1 registers
|
||||
function long_add4(n, k, a, b, c, d){
|
||||
var carry = 0;
|
||||
var sum[50];
|
||||
for(var i=0; i < k; i++){
|
||||
var sumAndCarry[2] = SplitFn(a[i] + b[i] + c[i] + d[i] + carry, n, n);
|
||||
sum[i] = sumAndCarry[0];
|
||||
carry = sumAndCarry[1];
|
||||
}
|
||||
sum[k] = carry;
|
||||
return sum;
|
||||
}
|
||||
// // n bits per register
|
||||
// // a has k registers
|
||||
// // b has k registers
|
||||
// // c has k registers
|
||||
// // d has k registers
|
||||
// // output has k+1 registers
|
||||
// function long_add4(n, k, a, b, c, d){
|
||||
// var carry = 0;
|
||||
// var sum[50];
|
||||
// for(var i=0; i < k; i++){
|
||||
// var sumAndCarry[2] = SplitFn(a[i] + b[i] + c[i] + d[i] + carry, n, n);
|
||||
// sum[i] = sumAndCarry[0];
|
||||
// carry = sumAndCarry[1];
|
||||
// }
|
||||
// sum[k] = carry;
|
||||
// return sum;
|
||||
// }
|
||||
|
||||
// n bits per register
|
||||
// a has k1 registers
|
||||
// b has k2 registers
|
||||
// assume k1 > 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<k1; i++){
|
||||
if (i < k2) {
|
||||
var sumAndCarry[2] = SplitFn(a[i] + b[i] + carry, n, n);
|
||||
sum[i] = sumAndCarry[0];
|
||||
carry = sumAndCarry[1];
|
||||
} else {
|
||||
var sumAndCarry[2] = SplitFn(a[i] + carry, n, n);
|
||||
sum[i] = sumAndCarry[0];
|
||||
carry = sumAndCarry[1];
|
||||
}
|
||||
}
|
||||
sum[k1] = carry;
|
||||
return sum;
|
||||
}
|
||||
// // n bits per register
|
||||
// // a has k1 registers
|
||||
// // b has k2 registers
|
||||
// // assume k1 > 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<k1; i++){
|
||||
// if (i < k2) {
|
||||
// var sumAndCarry[2] = SplitFn(a[i] + b[i] + carry, n, n);
|
||||
// sum[i] = sumAndCarry[0];
|
||||
// carry = sumAndCarry[1];
|
||||
// } else {
|
||||
// var sumAndCarry[2] = SplitFn(a[i] + carry, n, n);
|
||||
// sum[i] = sumAndCarry[0];
|
||||
// carry = sumAndCarry[1];
|
||||
// }
|
||||
// }
|
||||
// sum[k1] = carry;
|
||||
// return sum;
|
||||
// }
|
||||
|
||||
// n bits per register
|
||||
// a has k registers
|
||||
@@ -171,7 +171,7 @@ function long_scalar_mult_ecdsa(n, k, a, b) {
|
||||
// out[1] has length k -- remainder
|
||||
// implements algorithm of https://people.eecs.berkeley.edu/~fateman/282/F%20Wright%20notes/week4.pdf
|
||||
// b[k-1] must be nonzero!
|
||||
function long_div2(n, k, m, a, b){
|
||||
function long_div2_ecdsa(n, k, m, a, b){
|
||||
var out[2][50];
|
||||
// assume k+m < 50
|
||||
var remainder[50];
|
||||
@@ -212,7 +212,7 @@ function long_div2(n, k, m, a, b){
|
||||
}
|
||||
|
||||
function long_div_ecdsa(n, k, a, b) {
|
||||
return long_div2(n, k, k, a, b);
|
||||
return long_div2_ecdsa(n, k, k, a, b);
|
||||
}
|
||||
|
||||
// n bits per register
|
||||
@@ -264,7 +264,7 @@ function short_div_ecdsa(n, k, a, b) {
|
||||
// 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[50] = 0 if positive, 1 if negative
|
||||
function signed_long_to_short(n, k, a){
|
||||
function signed_long_to_short_ecdsa(n, k, a){
|
||||
var out[51];
|
||||
var MAXL = 50;
|
||||
var temp[51];
|
||||
@@ -314,7 +314,7 @@ function signed_long_to_short(n, k, a){
|
||||
// a and b both have k registers
|
||||
// out[0] has length 2 * k
|
||||
// adapted from BigMulShortLong and LongToShortNoEndCarry witness computation
|
||||
function prod(n, k, a, b) {
|
||||
function prod_ecdsa(n, k, a, b) {
|
||||
// first compute the intermediate values. taken from BigMulShortLong
|
||||
var prod_val[50]; // length is 2 * k - 1
|
||||
for (var i = 0; i < 2 * k - 1; i++) {
|
||||
@@ -358,80 +358,80 @@ function prod(n, k, a, b) {
|
||||
}
|
||||
|
||||
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// // 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
|
||||
// // 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 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];
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
// return out;
|
||||
// }
|
||||
|
||||
// Put all modular arithmetic, aka F_p field stuff, at the end
|
||||
// // 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);
|
||||
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) {
|
||||
|
||||
@@ -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<k; i++){
|
||||
out[0][i] <-- x3[i];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/// This file implements the ECDSA verification algorithm along with public key generation (xG)
|
||||
|
||||
pragma circom 2.1.5;
|
||||
pragma circom 2.1.9;
|
||||
include "../../../node_modules/circomlib/circuits/multiplexer.circom";
|
||||
|
||||
include "p256.circom";
|
||||
@@ -139,7 +139,7 @@ template ECDSAVerifyNoPubkeyCheck(n, k) {
|
||||
var order[100] = get_p256_order(n, k);
|
||||
|
||||
// compute multiplicative inverse of s mod n
|
||||
var sinv_comp[100] = mod_inv(n, k, s, order);
|
||||
var sinv_comp[100] = mod_inv_ecdsa(n, k, s, order);
|
||||
signal sinv[k];
|
||||
component sinv_range_checks[k];
|
||||
for (var idx = 0; idx < k; idx++) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pragma circom 2.1.5;
|
||||
pragma circom 2.1.9;
|
||||
|
||||
function get_g_pow_stride8_table(n, k) {
|
||||
assert(n == 43 && k == 6);
|
||||
|
||||
@@ -15,7 +15,7 @@ function get_fp_sgn0(a){
|
||||
// else computes inv = num^{-1} mod p using extended euclidean algorithm
|
||||
// https://brilliant.org/wiki/extended-euclidean-algorithm/
|
||||
function find_Fp_inverse(n, k, num, p) {
|
||||
var amodp[2][50] = long_div2(n, k, 0, num, p);
|
||||
var amodp[2][50] = long_div2_ecdsa(n, k, 0, num, p);
|
||||
var a[50];
|
||||
var b[50];
|
||||
var x[50];
|
||||
@@ -51,7 +51,7 @@ function find_Fp_inverse(n, k, num, p) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
var r[2][50] = long_div2(n, ka, k - ka, b, a);
|
||||
var r[2][50] = long_div2_ecdsa(n, ka, k - ka, b, a);
|
||||
var q[50];
|
||||
for(var i = 0; i < k - ka + 1; i++)
|
||||
q[i] = r[0][i];
|
||||
@@ -87,7 +87,7 @@ function find_Fp_inverse(n, k, num, p) {
|
||||
// out[1] has k registers in range [0, 2^n)
|
||||
function get_signed_Fp_carry_witness(n, k, m, a, p){
|
||||
var out[2][50];
|
||||
var a_short[51] = signed_long_to_short(n, k, a);
|
||||
var a_short[51] = signed_long_to_short_ecdsa(n, k, a);
|
||||
|
||||
/* // commenting out to improve speed
|
||||
// let me make sure everything is in <= k+m registers
|
||||
@@ -96,13 +96,13 @@ function get_signed_Fp_carry_witness(n, k, m, a, p){
|
||||
*/
|
||||
|
||||
if(a_short[50] == 0){
|
||||
out = long_div2(n, k, m, a_short, p);
|
||||
out = long_div2_ecdsa(n, k, m, a_short, p);
|
||||
}else{
|
||||
var a_pos[50];
|
||||
for(var i=0; i<k+m; i++)
|
||||
a_pos[i] = -a_short[i];
|
||||
|
||||
var X[2][50] = long_div2(n, k, m, a_pos, p);
|
||||
var X[2][50] = long_div2_ecdsa(n, k, m, a_pos, p);
|
||||
// what if X[1] is 0?
|
||||
var Y_is_zero = 1;
|
||||
for(var i=0; i<k; i++){
|
||||
@@ -274,7 +274,7 @@ function find_Fp2_inverse(n, k, a, p) {
|
||||
var sq0[50] = prod(n, k, a[0], a[0]);
|
||||
var sq1[50] = prod(n, k, a[1], a[1]);
|
||||
var sq_sum[50] = long_add(n, 2*k, sq0, sq1);
|
||||
var sq_sum_div[2][50] = long_div2(n, k, k+1, sq_sum, p);
|
||||
var sq_sum_div[2][50] = long_div2_ecdsa(n, k, k+1, sq_sum, p);
|
||||
// lambda = 1/(sq_sum)%p
|
||||
var lambda[50] = mod_inv(n, k, sq_sum_div[1], p);
|
||||
var out0[50] = prod(n, k, lambda, a[0]);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pragma circom 2.1.5;
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "curve.circom";
|
||||
include "p256_func.circom";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/// This file provides methods to grab curve parameter based on various representations
|
||||
|
||||
pragma circom 2.1.5;
|
||||
pragma circom 2.1.9;
|
||||
|
||||
function get_A(n, k) {
|
||||
assert((n == 86 && k == 3) || (n == 64 && k == 4) || (n == 43 && k ==6));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/// UNUSED FILE
|
||||
|
||||
pragma circom 2.1.5;
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "bigInt_func.circom";
|
||||
include "p256_func.circom";
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "circomlib/circuits/poseidon.circom";
|
||||
|
||||
template ComputeCommitment() {
|
||||
signal input secret;
|
||||
signal input attestation_id;
|
||||
signal input leaf;
|
||||
signal input mrz[93];
|
||||
signal output out;
|
||||
|
||||
component poseidon_hasher = Poseidon(6);
|
||||
poseidon_hasher.inputs[0] <== secret;
|
||||
poseidon_hasher.inputs[1] <== attestation_id;
|
||||
poseidon_hasher.inputs[2] <== leaf;
|
||||
|
||||
signal mrz_packed[3] <== PackBytes(93)(mrz);
|
||||
for (var i = 0; i < 3; i++) {
|
||||
poseidon_hasher.inputs[i + 3] <== mrz_packed[i];
|
||||
}
|
||||
out <== poseidon_hasher.out;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
pragma circom 2.1.5;
|
||||
|
||||
include "./chunk_data.circom";
|
||||
|
||||
// chunks the pubkey and hashes it with the signature algorithm
|
||||
template ComputePubkeyLeaf(n, k, signatureAlgorithm) {
|
||||
signal input pubkey[k];
|
||||
|
||||
// Converting pubkey (modulus) into 11 chunks of 192 bits, assuming original n, k are 64 and 32.
|
||||
// This is because Poseidon circuit only supports an array of 16 elements.
|
||||
var chunk_size = 11; // Since ceil(32 / 3) in integer division is 11
|
||||
signal chunk_data[chunk_size] <== ChunkData(n, k, chunk_size)(pubkey);
|
||||
|
||||
signal leaf_hash_input[1 + chunk_size];
|
||||
leaf_hash_input[0] <== signatureAlgorithm;
|
||||
for (var i = 0; i < chunk_size; i++) {
|
||||
leaf_hash_input[i+1] <== chunk_data[i];
|
||||
}
|
||||
signal output leaf <== Poseidon(1 + chunk_size)(leaf_hash_input);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
pragma circom 2.1.6;
|
||||
include "@zk-email/circuits/lib/fp.circom";
|
||||
include "circomlib/circuits/poseidon.circom";
|
||||
include "../utils/splitSignalsToWords.circom";
|
||||
|
||||
template LeafHasher(n, k) {
|
||||
signal input in[k];
|
||||
signal output out;
|
||||
var wordsSize = div_ceil(n * k, 64);
|
||||
component splitSignalsToWords = SplitSignalsToWords(n, k, wordsSize, 64);
|
||||
splitSignalsToWords.in <== in;
|
||||
|
||||
component hash[4];
|
||||
for (var i = 0; i < 4 ; i ++){
|
||||
hash[i] = Poseidon(16);
|
||||
}
|
||||
for (var i = 0; i < 64 ; i ++){
|
||||
hash[ i % 4 ].inputs[ i \ 4 ] <== splitSignalsToWords.out[i];
|
||||
}
|
||||
component finalHash = Poseidon(4);
|
||||
for (var i = 0 ; i < 4 ; i++){
|
||||
finalHash.inputs[i] <== hash[i].out;
|
||||
}
|
||||
out <== finalHash.out;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
pragma circom 2.1.6;
|
||||
|
||||
include "circomlib/circuits/poseidon.circom";
|
||||
|
||||
template LeafHasher (k) {
|
||||
signal input in[k];
|
||||
signal output out;
|
||||
component hash[4];
|
||||
for (var i = 0; i < 4 ; i ++){
|
||||
hash[i] = Poseidon(16);
|
||||
}
|
||||
for (var i = 0; i < 64 ; i ++){
|
||||
if (i < k ){
|
||||
hash[ i % 4 ].inputs[ i \ 4 ] <== in[i];
|
||||
}
|
||||
else{
|
||||
hash[ i % 4 ].inputs[ i \ 4 ] <== 0;
|
||||
}
|
||||
}
|
||||
component finalHash = Poseidon(4);
|
||||
for (var i = 0 ; i < 4 ; i++){
|
||||
finalHash.inputs[i] <== hash[i].out;
|
||||
}
|
||||
log(finalHash.out);
|
||||
out <== finalHash.out;
|
||||
}
|
||||
164
circuits/circuits/utils/other/array.circom
Normal file
164
circuits/circuits/utils/other/array.circom
Normal file
@@ -0,0 +1,164 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
include "./functions.circom";
|
||||
|
||||
|
||||
/// @title ItemAtIndex
|
||||
/// @notice Select item at given index from the input array
|
||||
/// @notice This template that the index is valid
|
||||
/// @notice This is a modified version of QuinSelector from MACI https://github.com/privacy-scaling-explorations/maci/
|
||||
/// @param maxArrayLen The number of elements in the array
|
||||
/// @input in The input array
|
||||
/// @input index The index of the element to select
|
||||
/// @output out The selected element
|
||||
template ItemAtIndex(maxArrayLen) {
|
||||
signal input in[maxArrayLen];
|
||||
signal input index;
|
||||
|
||||
signal output out;
|
||||
|
||||
component calcTotalValue = CalculateTotal(maxArrayLen);
|
||||
component calcTotalIndex = CalculateTotal(maxArrayLen);
|
||||
component eqs[maxArrayLen];
|
||||
|
||||
// For each item, check whether its index equals the input index.
|
||||
for (var i = 0; i < maxArrayLen; i ++) {
|
||||
eqs[i] = IsEqual();
|
||||
eqs[i].in[0] <== i;
|
||||
eqs[i].in[1] <== index;
|
||||
|
||||
// eqs[i].out is 1 if the index matches - so calcTotal is sum of 0s + 1 * valueAtIndex
|
||||
calcTotalValue.nums[i] <== eqs[i].out * in[i];
|
||||
|
||||
// Take the sum of all eqs[i].out and assert that it is at most 1.
|
||||
calcTotalIndex.nums[i] <== eqs[i].out;
|
||||
}
|
||||
|
||||
// Assert that the sum of eqs[i].out is 1. This is to ensure the index passed is valid.
|
||||
calcTotalIndex.sum === 1;
|
||||
|
||||
out <== calcTotalValue.sum;
|
||||
}
|
||||
|
||||
|
||||
/// @title CalculateTotal
|
||||
/// @notice Calculate the sum of an array
|
||||
/// @param n The number of elements in the array
|
||||
/// @input nums The input array; assumes elements are small enough that their sum does not overflow the field
|
||||
/// @output sum The sum of the input array
|
||||
template CalculateTotal(n) {
|
||||
signal input nums[n];
|
||||
|
||||
signal output sum;
|
||||
|
||||
signal sums[n];
|
||||
sums[0] <== nums[0];
|
||||
|
||||
for (var i=1; i < n; i++) {
|
||||
sums[i] <== sums[i - 1] + nums[i];
|
||||
}
|
||||
|
||||
sum <== sums[n - 1];
|
||||
}
|
||||
|
||||
|
||||
/// @title SelectSubArray
|
||||
/// @notice Select sub array from an array given a `startIndex` and `length`
|
||||
/// @notice This is same as `VarShiftLeft` but with elements after `length` set to zero
|
||||
/// @notice This is not used in core ZK-Email circuits at the moment
|
||||
/// @param maxArrayLen: the maximum number of bytes in the input array
|
||||
/// @param maxSubArrayLen: the maximum number of integers in the output array
|
||||
/// @input in: the input array
|
||||
/// @input startIndex: the start index of the sub array; assumes a valid index
|
||||
/// @input length: the length of the sub array; assumes to fit in `ceil(log2(maxArrayLen))` bits
|
||||
/// @output out: array of `maxSubArrayLen` size, items starting from `startIndex`, and items after `length` set to zero
|
||||
template SelectSubArray(maxArrayLen, maxSubArrayLen) {
|
||||
assert(maxSubArrayLen < maxArrayLen);
|
||||
|
||||
signal input in[maxArrayLen];
|
||||
signal input startIndex;
|
||||
signal input length;
|
||||
|
||||
signal output out[maxSubArrayLen];
|
||||
|
||||
component shifter = VarShiftLeft(maxArrayLen, maxSubArrayLen);
|
||||
shifter.in <== in;
|
||||
shifter.shift <== startIndex;
|
||||
|
||||
// Set value after length to zero
|
||||
component gts[maxSubArrayLen];
|
||||
for (var i = 0; i < maxSubArrayLen; i++) {
|
||||
gts[i] = GreaterThan(log2Ceil(maxSubArrayLen));
|
||||
gts[i].in[0] <== length;
|
||||
gts[i].in[1] <== i;
|
||||
|
||||
out[i] <== gts[i].out * shifter.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @title VarShiftLeft
|
||||
/// @notice Shift input array by `shift` indices to the left
|
||||
/// @notice Output array length can be reduced by setting `maxOutArrayLen`
|
||||
/// @notice Based on https://demo.hedgedoc.org/s/Le0R3xUhB
|
||||
/// @param maxArrayLen The maximum length of the input array
|
||||
/// @param maxOutArrayLen The maximum length of the output array
|
||||
/// @input in The input array
|
||||
/// @input shift The number of indices to shift the array to the left
|
||||
/// @output out hifted subarray
|
||||
template VarShiftLeft(maxArrayLen, maxOutArrayLen) {
|
||||
assert(maxOutArrayLen <= maxArrayLen);
|
||||
|
||||
var bitLength = log2Ceil(maxArrayLen);
|
||||
|
||||
signal input in[maxArrayLen];
|
||||
signal input shift;
|
||||
|
||||
signal output out[maxOutArrayLen];
|
||||
|
||||
component n2b = Num2Bits(bitLength);
|
||||
n2b.in <== shift;
|
||||
|
||||
signal tmp[bitLength][maxArrayLen];
|
||||
for (var j = 0; j < bitLength; j++) {
|
||||
for (var i = 0; i < maxArrayLen; i++) {
|
||||
var offset = (i + (1 << j)) % maxArrayLen;
|
||||
// Shift left by 2^j indices if bit is 1
|
||||
if (j == 0) {
|
||||
tmp[j][i] <== n2b.out[j] * (in[offset] - in[i]) + in[i];
|
||||
} else {
|
||||
tmp[j][i] <== n2b.out[j] * (tmp[j-1][offset] - tmp[j-1][i]) + tmp[j-1][i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return last row
|
||||
for (var i = 0; i < maxOutArrayLen; i++) {
|
||||
out[i] <== tmp[bitLength - 1][i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @title AssertZeroPadding
|
||||
/// @notice Assert that the input array is zero-padded from the given `startIndex`
|
||||
/// @param maxArrayLen The maximum number of elements in the input array
|
||||
/// @input in The input array;
|
||||
/// @input startIndex The index from which the elements should be 0; assumes `startIndex - 1` to fit in `ceil(log2(maxArrayLen))` bits
|
||||
template AssertZeroPadding(maxArrayLen) {
|
||||
var bitLength = log2Ceil(maxArrayLen);
|
||||
|
||||
signal input in[maxArrayLen];
|
||||
signal input startIndex;
|
||||
|
||||
component lessThans[maxArrayLen];
|
||||
|
||||
for (var i = 0; i < maxArrayLen; i++) {
|
||||
lessThans[i] = LessThan(bitLength);
|
||||
lessThans[i].in[0] <== startIndex - 1;
|
||||
lessThans[i].in[1] <== i;
|
||||
|
||||
lessThans[i].out * in[i] === 0;
|
||||
}
|
||||
}
|
||||
988
circuits/circuits/utils/other/bigInt.circom
Normal file
988
circuits/circuits/utils/other/bigInt.circom
Normal file
@@ -0,0 +1,988 @@
|
||||
pragma circom 2.0.3;
|
||||
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
include "circomlib/circuits/gates.circom";
|
||||
|
||||
include "bigIntFunc.circom";
|
||||
|
||||
// addition mod 2**CHUNK_SIZE with carry bit
|
||||
template ModSum(CHUNK_SIZE) {
|
||||
assert(CHUNK_SIZE <= 252);
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output sum;
|
||||
signal output carry;
|
||||
|
||||
component n2b = Num2Bits(CHUNK_SIZE + 1);
|
||||
n2b.in <== a + b;
|
||||
carry <== n2b.out[CHUNK_SIZE];
|
||||
sum <== a + b - carry * (1 << CHUNK_SIZE);
|
||||
}
|
||||
|
||||
// check if CHUNK_NUMBER-register variables a, b are equal everywhere
|
||||
template BigIsEqual(CHUNK_NUMBER) {
|
||||
signal input a[CHUNK_NUMBER];
|
||||
signal input b[CHUNK_NUMBER];
|
||||
signal output out;
|
||||
|
||||
component isEquals[CHUNK_NUMBER];
|
||||
var total = CHUNK_NUMBER;
|
||||
for (var i = 0; i < CHUNK_NUMBER; i ++) {
|
||||
isEquals[i] = IsEqual();
|
||||
isEquals[i].in[0] <== a[i];
|
||||
isEquals[i].in[1] <== b[i];
|
||||
total -= isEquals[i].out;
|
||||
}
|
||||
component checkZero = IsZero();
|
||||
checkZero.in <== total;
|
||||
out <== checkZero.out;
|
||||
}
|
||||
|
||||
// check if CHUNK_NUMBER-register variable a is equal to zero
|
||||
template BigIsZero(CHUNK_NUMBER) {
|
||||
signal input in[CHUNK_NUMBER];
|
||||
signal output out;
|
||||
|
||||
component isZeros[CHUNK_NUMBER];
|
||||
var total = CHUNK_NUMBER;
|
||||
for (var i = 0; i < CHUNK_NUMBER; i ++) {
|
||||
isZeros[i] = IsZero();
|
||||
isZeros[i].in <== in[i];
|
||||
total -= isZeros[i].out;
|
||||
}
|
||||
component checkZero = IsZero();
|
||||
checkZero.in <== total;
|
||||
out <== checkZero.out;
|
||||
}
|
||||
|
||||
|
||||
// a - b
|
||||
template ModSub(CHUNK_SIZE) {
|
||||
assert(CHUNK_SIZE <= 252);
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output out;
|
||||
signal output borrow;
|
||||
component lt = LessThan(CHUNK_SIZE);
|
||||
lt.in[0] <== a;
|
||||
lt.in[1] <== b;
|
||||
borrow <== lt.out;
|
||||
out <== borrow * (1 << CHUNK_SIZE) + a - b;
|
||||
}
|
||||
|
||||
// a - b - c
|
||||
// assume a - b - c + 2**CHUNK_SIZE >= 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<k2; i++)for(var j=0; j<k2; j++)
|
||||
pow[i][j] = i ** j;
|
||||
|
||||
var aPoly[2 * CHUNK_NUMBER - 1];
|
||||
var bPoly[2 * CHUNK_NUMBER - 1];
|
||||
var outPoly[2 * CHUNK_NUMBER - 1];
|
||||
for (var i = 0; i < 2 * CHUNK_NUMBER - 1; i++) {
|
||||
outPoly[i] = 0;
|
||||
aPoly[i] = 0;
|
||||
bPoly[i] = 0;
|
||||
for (var j = 0; j < 2 * CHUNK_NUMBER - 1; j++) {
|
||||
outPoly[i] = outPoly[i] + out[j] * pow[i][j];
|
||||
}
|
||||
for (var j = 0; j < CHUNK_NUMBER; j++) {
|
||||
aPoly[i] = aPoly[i] + a[j] * pow[i][j];
|
||||
bPoly[i] = bPoly[i] + b[j] * pow[i][j];
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < 2 * CHUNK_NUMBER - 1; i++) {
|
||||
outPoly[i] === aPoly[i] * bPoly[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
same as BigMultShortLong except a has degree K_A - 1, b has degree K_B - 1
|
||||
- If a[i], b[j] have absolute value < B, then out[i] has absolute value < min(K_A, K_B) * B^2
|
||||
*/
|
||||
template BigMultShortLongUnequal(CHUNK_SIZE, K_A, K_B, M_OUT) {
|
||||
assert(CHUNK_SIZE <= 126);
|
||||
signal input a[K_A];
|
||||
signal input b[K_B];
|
||||
signal output out[K_A + K_B - 1];
|
||||
|
||||
var PROD_VAL[K_A + K_B - 1];
|
||||
for (var i = 0; i < K_A + K_B - 1; i++) {
|
||||
PROD_VAL[i] = 0;
|
||||
}
|
||||
for (var i = 0; i < K_A; i++) {
|
||||
for (var j = 0; j < K_B; j++) {
|
||||
PROD_VAL[i + j] = PROD_VAL[i + j] + a[i] * b[j];
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < K_A + K_B - 1; i++) {
|
||||
out[i] <-- PROD_VAL[i];
|
||||
}
|
||||
|
||||
var k2 = K_A + K_B - 1;
|
||||
var pow[k2][k2];
|
||||
for(var i = 0; i<k2; i++)for(var j=0; j<k2; j++)
|
||||
pow[i][j] = i ** j;
|
||||
|
||||
var aPoly[K_A + K_B - 1];
|
||||
var bPoly[K_A + K_B - 1];
|
||||
var outPoly[K_A + K_B - 1];
|
||||
for (var i = 0; i < K_A + K_B - 1; i++) {
|
||||
outPoly[i] = 0;
|
||||
aPoly[i] = 0;
|
||||
bPoly[i] = 0;
|
||||
for (var j = 0; j < K_A + K_B - 1; j++) {
|
||||
outPoly[i] = outPoly[i] + out[j] * pow[i][j];
|
||||
}
|
||||
for (var j = 0; j < K_A; j++) {
|
||||
aPoly[i] = aPoly[i] + a[j] * pow[i][j];
|
||||
}
|
||||
for (var j = 0; j < K_B; j++) {
|
||||
bPoly[i] = bPoly[i] + b[j] * pow[i][j];
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < K_A + K_B - 1; i++) {
|
||||
outPoly[i] === aPoly[i] * bPoly[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// in[i] contains longs
|
||||
// out[i] contains shorts
|
||||
template LongToShortNoEndCarry(CHUNK_SIZE, CHUNK_NUMBER) {
|
||||
assert(CHUNK_SIZE <= 126);
|
||||
signal input in[CHUNK_NUMBER];
|
||||
signal output out[CHUNK_NUMBER+1];
|
||||
|
||||
var split[CHUNK_NUMBER][3];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
split[i] = SplitThreeFn(in[i], CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE);
|
||||
}
|
||||
|
||||
var carry[CHUNK_NUMBER];
|
||||
carry[0] = 0;
|
||||
out[0] <-- split[0][0];
|
||||
if (CHUNK_NUMBER > 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_SIZE);
|
||||
in[i] === carry[i] * (1<<CHUNK_SIZE);
|
||||
}
|
||||
else{
|
||||
carry[i] <-- (in[i]+carry[i-1]) / (1<<CHUNK_SIZE);
|
||||
in[i] + carry[i-1] === carry[i] * (1<<CHUNK_SIZE);
|
||||
}
|
||||
// checking carry is in the range of -2^(M-CHUNK_SIZE-1+eps), 2^(M-CHUNK_SIZE-1+eps)
|
||||
carryRangeChecks[i].in <== carry[i] + ( 1<< (M + EPSILON - CHUNK_SIZE - 1));
|
||||
// carry[i] is bounded by 2^{M-1} * (2^{-CHUNK_SIZE} + 2^{-2n} + ... ) = 2^{M-CHUNK_SIZE-1} * ( 1/ (1-2^{-CHUNK_SIZE})) < 2^{M-CHUNK_SIZE} by geometric series
|
||||
}
|
||||
|
||||
in[CHUNK_NUMBER-1] + carry[CHUNK_NUMBER-2] === 0;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Let X = 2^CHUNK_SIZE
|
||||
Input:
|
||||
- in is length CHUNK_NUMBER + M array in signed overflow representation
|
||||
- in = in[0] + in[1] * X + ... + in[CHUNK_NUMBER+M-1] * X^{CHUNK_NUMBER+M-1}
|
||||
- Assume each in[i] is a signed integer such that abs(in[i] * 2^CHUNK_SIZE) < 2^252
|
||||
- P is prime in BigInt format passed as parameter
|
||||
Output:
|
||||
- out = out[0] + out[1] * X + ... + out[CHUNK_NUMBER-1] * X^{CHUNK_NUMBER-1} is BigInt congruent to in (mod P)
|
||||
Implementation:
|
||||
- For i >= 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 <B, then out[i] has absolute value < (M+1) * 2^CHUNK_SIZE * B
|
||||
M_OUT is the expected max number of bits in the output registers
|
||||
*/
|
||||
template PrimeReduce(CHUNK_SIZE, CHUNK_NUMBER, M, P, M_OUT){
|
||||
signal input in[M+CHUNK_NUMBER];
|
||||
signal output out[CHUNK_NUMBER];
|
||||
|
||||
var TWO[CHUNK_NUMBER];
|
||||
var E[CHUNK_NUMBER];
|
||||
for(var i=1; i<CHUNK_NUMBER; i++){
|
||||
TWO[i]=0;
|
||||
E[i]=0;
|
||||
}
|
||||
TWO[0] = 2;
|
||||
|
||||
|
||||
E[0] = CHUNK_SIZE;
|
||||
var POW_2_N[150] = mod_exp(CHUNK_SIZE, CHUNK_NUMBER, TWO, P, E);
|
||||
E[0] = CHUNK_NUMBER;
|
||||
assert(CHUNK_NUMBER < (1<<CHUNK_SIZE) );
|
||||
var pow2nk[150] = mod_exp(CHUNK_SIZE, CHUNK_NUMBER, POW_2_N, P, E);
|
||||
|
||||
var r[M][150];
|
||||
for(var i=0; i<M; i++){
|
||||
// r[i] = 2^{CHUNK_SIZE(CHUNK_NUMBER+i)} mod P
|
||||
if(i==0){
|
||||
r[i] = pow2nk;
|
||||
}else{
|
||||
r[i] = prod_mod(CHUNK_SIZE, CHUNK_NUMBER, r[i-1], POW_2_N, P);
|
||||
}
|
||||
}
|
||||
var out_sum[CHUNK_NUMBER];
|
||||
for(var i=0; i<CHUNK_NUMBER; i++)
|
||||
out_sum[i] = in[i];
|
||||
for(var i=0; i<M; i++)
|
||||
for(var j=0; j<CHUNK_NUMBER; j++)
|
||||
out_sum[j] += in[i+CHUNK_NUMBER] * r[i][j]; // linear constraint
|
||||
for(var i=0; i<CHUNK_NUMBER; i++)
|
||||
out[i] <== out_sum[i];
|
||||
/*component rangeChecks[CHUNK_NUMBER];
|
||||
for (var i = 0; i < CHUNK_NUMBER; i++) {
|
||||
rangeChecks[i] = Num2Bits(M_OUT+1);
|
||||
rangeChecks[i].in <== out[i] + (1 << M_OUT);
|
||||
}*/
|
||||
}
|
||||
|
||||
/*
|
||||
Polynomial multiplication in 2 variables
|
||||
Input:
|
||||
- a = sum_{i=0}^{l-1} sum_{j=0}^{CHUNK_NUMBER-1} a[i][j] * w^i * X^j
|
||||
- b = sum_{i=0}^{l-1} sum_{j=0}^{CHUNK_NUMBER-1} b[i][j] * w^i * X^j
|
||||
Output:
|
||||
- out = sum_{i=0}^{2*l-2} sum_{j=0}^{2*CHUNK_NUMBER-1} out[i][j] * w^i * X^j
|
||||
- out = a * b as product of polynomials in TWO variables w, X
|
||||
Notes:
|
||||
- Uses same xJsnark optimization as BigMultShortLong
|
||||
- If a[i][j], b[i][j] have absolute value < B, then out[i][j] has absolute value < l * CHUNK_NUMBER * B^2
|
||||
Use case: one variable will end up being 2^CHUNK_SIZE; the other will be the field extension generator
|
||||
*/
|
||||
template BigMultShortLong2D(CHUNK_SIZE, CHUNK_NUMBER, l) {
|
||||
signal input a[l][CHUNK_NUMBER];
|
||||
signal input b[l][CHUNK_NUMBER];
|
||||
signal output out[2*l-1][2*CHUNK_NUMBER-1];
|
||||
|
||||
var PROD_VAL[2*l-1][2*CHUNK_NUMBER-1];
|
||||
for (var i = 0; i < 2*l-1; i++) {
|
||||
for (var j = 0; j < 2*CHUNK_NUMBER-1; j++) {
|
||||
PROD_VAL[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i1 = 0; i1 < l; i1 ++) {
|
||||
for (var i2 = 0; i2 < l; i2 ++) {
|
||||
for (var j1 = 0; j1 < CHUNK_NUMBER; j1 ++) {
|
||||
for (var j2 = 0; j2 < CHUNK_NUMBER; j2 ++) {
|
||||
var i = i1 + i2;
|
||||
var j = j1 + j2;
|
||||
PROD_VAL[i][j] += a[i1][j1] * b[i2][j2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2*l-1; i++) {
|
||||
for (var j = 0; j < 2*CHUNK_NUMBER-1; j++) {
|
||||
out[i][j] <-- PROD_VAL[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
var k2 = (2*CHUNK_NUMBER-1 > 2*l-1) ? 2*CHUNK_NUMBER-1 : 2*l-1;
|
||||
var pow[k2][k2];
|
||||
for(var i = 0; i<k2; i++)for(var j=0; j<k2; j++)
|
||||
pow[i][j] = i ** j;
|
||||
|
||||
var aPoly[2*l-1][2*CHUNK_NUMBER-1];
|
||||
var bPoly[2*l-1][2*CHUNK_NUMBER-1];
|
||||
var outPoly[2*l-1][2*CHUNK_NUMBER-1];
|
||||
for (var i = 0; i < 2*l-1; i++) {
|
||||
for (var j = 0; j < 2*CHUNK_NUMBER-1; j++) {
|
||||
aPoly[i][j] = 0;
|
||||
bPoly[i][j] = 0;
|
||||
outPoly[i][j] = 0;
|
||||
for (var deg1 = 0; deg1 < l; deg1 ++) {
|
||||
for (var deg2 = 0; deg2 < CHUNK_NUMBER; deg2 ++) {
|
||||
aPoly[i][j] = aPoly[i][j] + a[deg1][deg2] * pow[i][deg1] * pow[j][deg2]; // (i ** deg1) * (j ** deg2);
|
||||
bPoly[i][j] = bPoly[i][j] + b[deg1][deg2] * pow[i][deg1] * pow[j][deg2]; // (i ** deg1) * (j ** deg2);
|
||||
}
|
||||
}
|
||||
for (var deg1 = 0; deg1 < 2*l-1; deg1 ++) {
|
||||
for (var deg2 = 0; deg2 < 2*CHUNK_NUMBER-1; deg2 ++) {
|
||||
outPoly[i][j] = outPoly[i][j] + out[deg1][deg2] * pow[i][deg1] * pow[j][deg2];// (i ** deg1) * (j ** deg2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < 2*l-1; i++) {
|
||||
for (var j = 0; j < 2*CHUNK_NUMBER-1; j++) {
|
||||
outPoly[i][j] === aPoly[i][j] * bPoly[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Same as BigMultShortLong2D except a has degrees L_A - 1, K_A - 1 and b has degrees L_B - 1, K_B - 1
|
||||
Notes:
|
||||
- If a[i][j], b[i][j] have absolute value < B, then out[i][j] has absolute value < min(L_A, L_B) * min(K_A, K_B) * B^2
|
||||
*/
|
||||
template BigMultShortLong2DUnequal(CHUNK_SIZE, K_A, K_B, L_A, L_B) {
|
||||
signal input a[L_A][K_A];
|
||||
signal input b[L_B][K_B];
|
||||
signal output out[L_A + L_B -1][K_A + K_B -1];
|
||||
|
||||
var PROD_VAL[L_A + L_B -1][K_A + K_B -1];
|
||||
for (var i = 0; i < L_A + L_B -1; i++) {
|
||||
for (var j = 0; j < K_A + K_B -1; j++) {
|
||||
PROD_VAL[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i1 = 0; i1 < L_A; i1 ++) {
|
||||
for (var i2 = 0; i2 < L_B; i2 ++) {
|
||||
for (var j1 = 0; j1 < K_A; j1 ++) {
|
||||
for (var j2 = 0; j2 < K_B; j2 ++) {
|
||||
var i = i1 + i2;
|
||||
var j = j1 + j2;
|
||||
PROD_VAL[i][j] += a[i1][j1] * b[i2][j2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < L_A + L_B -1; i++) {
|
||||
for (var j = 0; j < K_A + K_B -1; j++) {
|
||||
out[i][j] <-- PROD_VAL[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
var k2 = (K_A + K_B -1 > L_A + L_B -1) ? K_A + K_B - 1 : L_A + L_B -1;
|
||||
var pow[k2][k2];
|
||||
for(var i = 0; i<k2; i++)for(var j=0; j<k2; j++)
|
||||
pow[i][j] = i ** j;
|
||||
|
||||
var aPoly[L_A + L_B - 1][K_A + K_B -1];
|
||||
var bPoly[L_A + L_B - 1][K_A + K_B -1];
|
||||
var outPoly[L_A + L_B - 1][K_A + K_B -1];
|
||||
for (var i = 0; i < L_A + L_B - 1; i++) {
|
||||
for (var j = 0; j < K_A + K_B - 1; j++) {
|
||||
aPoly[i][j] = 0;
|
||||
bPoly[i][j] = 0;
|
||||
outPoly[i][j] = 0;
|
||||
for (var deg1 = 0; deg1 < L_A + L_B - 1; deg1 ++) {
|
||||
if (deg1 < L_A) {
|
||||
for (var deg2 = 0; deg2 < K_A; deg2 ++) {
|
||||
aPoly[i][j] = aPoly[i][j] + a[deg1][deg2] * pow[i][deg1] * pow[j][deg2]; //(i ** deg1) * (j ** deg2);
|
||||
}
|
||||
}
|
||||
if (deg1 < L_B) {
|
||||
for (var deg2 = 0; deg2 < K_B; deg2 ++) {
|
||||
bPoly[i][j] = bPoly[i][j] + b[deg1][deg2] * pow[i][deg1] * pow[j][deg2]; // (i ** deg1) * (j ** deg2);
|
||||
}
|
||||
}
|
||||
for (var deg2 = 0; deg2 < K_A + K_B -1; deg2 ++) {
|
||||
outPoly[i][j] = outPoly[i][j] + out[deg1][deg2] * pow[i][deg1] * pow[j][deg2]; // (i ** deg1) * (j ** deg2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < L_A + L_B - 1; i++) {
|
||||
for (var j = 0; j < K_A + K_B - 1; j++) {
|
||||
outPoly[i][j] === aPoly[i][j] * bPoly[i][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
615
circuits/circuits/utils/other/bigIntFunc.circom
Normal file
615
circuits/circuits/utils/other/bigIntFunc.circom
Normal file
@@ -0,0 +1,615 @@
|
||||
pragma circom 2.0.3;
|
||||
|
||||
function min(A, B) {
|
||||
if(A < B)
|
||||
return A;
|
||||
return B;
|
||||
}
|
||||
|
||||
function max(A, B) {
|
||||
if(A > 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<CHUNK_NUMBER; idx++){
|
||||
if(A[idx] != 0)
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// CHUNK_SIZE bits per register
|
||||
// A has CHUNK_NUMBER registers
|
||||
// B has CHUNK_NUMBER registers
|
||||
// output has CHUNK_NUMBER+1 registers
|
||||
function long_add(CHUNK_SIZE, CHUNK_NUMBER, A, B){
|
||||
var carry = 0;
|
||||
var sum[150];
|
||||
for(var i=0; i<CHUNK_NUMBER; i++){
|
||||
var sumAndCarry[2] = SplitFn(A[i] + B[i] + carry, CHUNK_SIZE, CHUNK_SIZE);
|
||||
sum[i] = sumAndCarry[0];
|
||||
carry = sumAndCarry[1];
|
||||
}
|
||||
sum[CHUNK_NUMBER] = carry;
|
||||
return sum;
|
||||
}
|
||||
|
||||
// CHUNK_SIZE bits per register
|
||||
// A has CHUNK_NUMBER registers
|
||||
// B has CHUNK_NUMBER registers
|
||||
// c has CHUNK_NUMBER registers
|
||||
// d has CHUNK_NUMBER registers
|
||||
// output has CHUNK_NUMBER+1 registers
|
||||
function long_add4(CHUNK_SIZE, CHUNK_NUMBER, A, B, c, d){
|
||||
var carry = 0;
|
||||
var sum[150];
|
||||
for(var i=0; i < CHUNK_NUMBER; i++){
|
||||
var sumAndCarry[2] = SplitFn(A[i] + B[i] + c[i] + d[i] + carry, CHUNK_SIZE, CHUNK_SIZE);
|
||||
sum[i] = sumAndCarry[0];
|
||||
carry = sumAndCarry[1];
|
||||
}
|
||||
sum[CHUNK_NUMBER] = carry;
|
||||
return sum;
|
||||
}
|
||||
|
||||
// CHUNK_SIZE bits per register
|
||||
// A has K1 registers
|
||||
// B has K2 registers
|
||||
// assume K1 > 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<K1; i++){
|
||||
if (i < K2) {
|
||||
var sumAndCarry[2] = SplitFn(A[i] + B[i] + carry, CHUNK_SIZE, CHUNK_SIZE);
|
||||
sum[i] = sumAndCarry[0];
|
||||
carry = sumAndCarry[1];
|
||||
} else {
|
||||
var sumAndCarry[2] = SplitFn(A[i] + carry, CHUNK_SIZE, CHUNK_SIZE);
|
||||
sum[i] = sumAndCarry[0];
|
||||
carry = sumAndCarry[1];
|
||||
}
|
||||
}
|
||||
sum[K1] = carry;
|
||||
return sum;
|
||||
}
|
||||
|
||||
// CHUNK_SIZE bits per register
|
||||
// A has CHUNK_NUMBER registers
|
||||
// B has CHUNK_NUMBER registers
|
||||
// A >= 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<CHUNK_NUMBER; i++) temp[i] = A[i];
|
||||
for(var i=CHUNK_NUMBER; i<=MAXL; i++) temp[i] = 0;
|
||||
|
||||
var X = (1<<CHUNK_SIZE);
|
||||
for(var i=0; i<MAXL; i++){
|
||||
if(temp[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<CHUNK_NUMBER; i++) temp[i] = A[i];
|
||||
for(var i=CHUNK_NUMBER; i<=MAXL; i++) temp[i] = 0;
|
||||
|
||||
for(var i=0; i<MAXL; i++){
|
||||
if(temp[i] < 0){
|
||||
var carry = (-temp[i]) \ X;
|
||||
out[i] = temp[i] + carry * X;
|
||||
temp[i+1] -= carry;
|
||||
}else{
|
||||
var borrow = (temp[i] + X - 1 ) \ X;
|
||||
out[i] = temp[i] - borrow * X;
|
||||
temp[i+1] += borrow;
|
||||
}
|
||||
}
|
||||
assert( temp[MAXL] == 0 );
|
||||
out[MAXL] = 1;
|
||||
return out;
|
||||
}
|
||||
|
||||
// CHUNK_SIZE bits per register
|
||||
// A and B both have CHUNK_NUMBER registers
|
||||
// out[0] has length 2 * CHUNK_NUMBER
|
||||
// adapted from BigMulShortLong and LongToShortNoEndCarry witness computation
|
||||
function prod(CHUNK_SIZE, CHUNK_NUMBER, A, B) {
|
||||
// first compute the intermediate values. taken from BigMulShortLong
|
||||
var prod_val[150]; // length is 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now do A bunch of carrying to make sure registers not overflowed. taken from LongToShortNoEndCarry
|
||||
var out[150]; // length is 2 * CHUNK_NUMBER
|
||||
|
||||
var SPLIT[150][3]; // first dimension has length 2 * CHUNK_NUMBER - 1
|
||||
for (var i = 0; i < 2 * CHUNK_NUMBER - 1; i++) {
|
||||
SPLIT[i] = SplitThreeFn(prod_val[i], CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE);
|
||||
}
|
||||
|
||||
var carry[150]; // length is 2 * CHUNK_NUMBER - 1
|
||||
carry[0] = 0;
|
||||
out[0] = SPLIT[0][0];
|
||||
if (2 * CHUNK_NUMBER - 1 > 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;
|
||||
}
|
||||
101
circuits/circuits/utils/other/bigint_4x64_mult.circom
Normal file
101
circuits/circuits/utils/other/bigint_4x64_mult.circom
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
216
circuits/circuits/utils/other/bytes.circom
Normal file
216
circuits/circuits/utils/other/bytes.circom
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
15
circuits/circuits/utils/other/constants.circom
Normal file
15
circuits/circuits/utils/other/constants.circom
Normal file
@@ -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;
|
||||
}
|
||||
165
circuits/circuits/utils/other/fp.circom
Normal file
165
circuits/circuits/utils/other/fp.circom
Normal file
@@ -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;
|
||||
}
|
||||
17
circuits/circuits/utils/other/functions.circom
Normal file
17
circuits/circuits/utils/other/functions.circom
Normal file
@@ -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;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
pragma circom 2.1.5;
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
include "circomlib/circuits/bitify.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";
|
||||
|
||||
31
circuits/circuits/utils/passport/computeCommitment.circom
Normal file
31
circuits/circuits/utils/passport/computeCommitment.circom
Normal file
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
pragma circom 2.1.6;
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "../node_modules/circomlib/circuits/comparators.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";
|
||||
@@ -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";
|
||||
@@ -0,0 +1,6 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
|
||||
template FormatECDSAInputs(signatureAlgorithm, k) {
|
||||
|
||||
}
|
||||
69
circuits/circuits/utils/passport/passportVerifier.circom
Normal file
69
circuits/circuits/utils/passport/passportVerifier.circom
Normal file
@@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
51
circuits/circuits/utils/passport/secp256r1Verifier.circom
Normal file
51
circuits/circuits/utils/passport/secp256r1Verifier.circom
Normal file
@@ -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;
|
||||
|
||||
}
|
||||
111
circuits/circuits/utils/passport/signatureAlgorithm.circom
Normal file
111
circuits/circuits/utils/passport/signatureAlgorithm.circom
Normal file
@@ -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;
|
||||
}
|
||||
93
circuits/circuits/utils/passport/signatureVerifier.circom
Normal file
93
circuits/circuits/utils/passport/signatureVerifier.circom
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
47
circuits/circuits/utils/rsa/powMod.circom
Normal file
47
circuits/circuits/utils/rsa/powMod.circom
Normal file
@@ -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];
|
||||
}
|
||||
}
|
||||
181
circuits/circuits/utils/rsa/rsa.circom
Normal file
181
circuits/circuits/utils/rsa/rsa.circom
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
56
circuits/circuits/utils/rsa/rsaPkcs1v15.circom
Normal file
56
circuits/circuits/utils/rsa/rsaPkcs1v15.circom
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
5
circuits/circuits/utils/rsa/rsaVerify.circom
Normal file
5
circuits/circuits/utils/rsa/rsaVerify.circom
Normal file
@@ -0,0 +1,5 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "./rsa.circom";
|
||||
|
||||
component main = RsaVerifyPkcs1v15(64, 32, 17, 256);
|
||||
@@ -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";
|
||||
|
||||
@@ -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<k2; i++)for(var j=0; j<k2; j++)
|
||||
pow[i][j] = i ** j;
|
||||
|
||||
var a_poly[2 * k - 1];
|
||||
var b_poly[2 * k - 1];
|
||||
var out_poly[2 * k - 1];
|
||||
for (var i = 0; i < 2 * k - 1; i++) {
|
||||
out_poly[i] = 0;
|
||||
a_poly[i] = 0;
|
||||
b_poly[i] = 0;
|
||||
for (var j = 0; j < 2 * k - 1; j++) {
|
||||
out_poly[i] = out_poly[i] + out[j] * pow[i][j];
|
||||
}
|
||||
for (var j = 0; j < k; j++) {
|
||||
a_poly[i] = a_poly[i] + a[j] * pow[i][j];
|
||||
b_poly[i] = b_poly[i] + b[j] * pow[i][j];
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < 2 * k - 1; i++) {
|
||||
out_poly[i] === a_poly[i] * b_poly[i];
|
||||
}
|
||||
}
|
||||
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][150] = long_div_2(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 = BigLessThan(n, k);
|
||||
for (var i = 0; i < k; i++) {
|
||||
lt.a[i] <== mod[i];
|
||||
lt.b[i] <== b[i];
|
||||
}
|
||||
lt.out === 1;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
template ModSum(n) {
|
||||
assert(n <= 252);
|
||||
signal input a;
|
||||
signal input b;
|
||||
signal output sum;
|
||||
signal output carry;
|
||||
|
||||
component n2b = Num2Bits(n + 1);
|
||||
n2b.in <== a + b;
|
||||
carry <== n2b.out[n];
|
||||
sum <== a + b - carry * (1 << n);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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 long_div_2(n, k, a, b) {
|
||||
return long_div2(n, k, k, a, b);
|
||||
}
|
||||
|
||||
function long_div2(n, k, m, a, b){
|
||||
var out[2][150];
|
||||
// assume k+m < 150
|
||||
var remainder[150];
|
||||
for (var i = 0; i < m + k; i++) {
|
||||
remainder[i] = a[i];
|
||||
}
|
||||
|
||||
var dividend[150];
|
||||
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[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;
|
||||
}
|
||||
@@ -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){
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
pragma circom 2.1.6;
|
||||
pragma circom 2.1.9;
|
||||
|
||||
template Xor2(n) {
|
||||
signal input a[n];
|
||||
|
||||
53
circuits/circuits/utils/sha1/constants.circom
Normal file
53
circuits/circuits/utils/sha1/constants.circom
Normal file
@@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
64
circuits/circuits/utils/sha1/f.circom
Normal file
64
circuits/circuits/utils/sha1/f.circom
Normal file
@@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
23
circuits/circuits/utils/sha1/parity.circom
Normal file
23
circuits/circuits/utils/sha1/parity.circom
Normal file
@@ -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];
|
||||
}
|
||||
}
|
||||
10
circuits/circuits/utils/sha1/rotate.circom
Normal file
10
circuits/circuits/utils/sha1/rotate.circom
Normal file
@@ -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 ];
|
||||
}
|
||||
}
|
||||
74
circuits/circuits/utils/sha1/sha1.circom
Normal file
74
circuits/circuits/utils/sha1/sha1.circom
Normal file
@@ -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<nBits; k++) {
|
||||
paddedIn[k] <== in[k];
|
||||
}
|
||||
|
||||
paddedIn[nBits] <== 1;
|
||||
|
||||
for (k=nBits+1; k<nBlocks*512-64; k++) {
|
||||
paddedIn[k] <== 0;
|
||||
}
|
||||
|
||||
for (k = 0; k< 64; k++) {
|
||||
paddedIn[nBlocks*512 - k -1] <== (nBits >> 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<nBlocks; i++) {
|
||||
|
||||
sha1compression[i] = Sha1compression();
|
||||
|
||||
if (i==0) {
|
||||
for (k=0; k<32; k++) {
|
||||
sha1compression[i].hin[0*32+k] <== ha0.out[k];
|
||||
sha1compression[i].hin[1*32+k] <== hb0.out[k];
|
||||
sha1compression[i].hin[2*32+k] <== hc0.out[k];
|
||||
sha1compression[i].hin[3*32+k] <== hd0.out[k];
|
||||
sha1compression[i].hin[4*32+k] <== he0.out[k];
|
||||
}
|
||||
} else {
|
||||
for (k=0; k<32; k++) {
|
||||
sha1compression[i].hin[32*0+k] <== sha1compression[i-1].out[32*0+31-k];
|
||||
sha1compression[i].hin[32*1+k] <== sha1compression[i-1].out[32*1+31-k];
|
||||
sha1compression[i].hin[32*2+k] <== sha1compression[i-1].out[32*2+31-k];
|
||||
sha1compression[i].hin[32*3+k] <== sha1compression[i-1].out[32*3+31-k];
|
||||
sha1compression[i].hin[32*4+k] <== sha1compression[i-1].out[32*4+31-k];
|
||||
}
|
||||
}
|
||||
|
||||
for (k=0; k<512; k++) {
|
||||
sha1compression[i].inp[k] <== paddedIn[i*512+k];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (i=0; i<5; i++) {
|
||||
for (k = 0; k < 32; k++) {
|
||||
out[32*i + k] <== sha1compression[nBlocks-1].out[32*i + (31-k)];
|
||||
}
|
||||
}
|
||||
}
|
||||
127
circuits/circuits/utils/sha1/sha1compression.circom
Normal file
127
circuits/circuits/utils/sha1/sha1compression.circom
Normal file
@@ -0,0 +1,127 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "./rotate.circom";
|
||||
include "./xor4.circom";
|
||||
include "./constants.circom";
|
||||
include "./t.circom";
|
||||
|
||||
template Sha1compression() {
|
||||
|
||||
signal input hin[160];
|
||||
signal input inp[512];
|
||||
signal output out[160];
|
||||
|
||||
signal a[81][32];
|
||||
signal b[81][32];
|
||||
signal c[81][32];
|
||||
signal d[81][32];
|
||||
signal e[81][32];
|
||||
signal w[80][32];
|
||||
|
||||
var i;
|
||||
|
||||
component rotl_1[64];
|
||||
for (i=0; i<64; i++) rotl_1[i] = RotL(32, 1);
|
||||
|
||||
component xor4[64];
|
||||
for (i=0; i<64; i++) xor4[i] = Xor4(32);
|
||||
|
||||
component rotl_30[80];
|
||||
for (i=0; i<=79; i++) rotl_30[i] = RotL(32, 30);
|
||||
|
||||
component K_t[80];
|
||||
for (i=0; i<=79; i++) K_t[i] = K_sha1(i);
|
||||
|
||||
component t_tmp[80];
|
||||
for (i=0; i<=79; i++) t_tmp[i] = T(i);
|
||||
|
||||
component fsum[5];
|
||||
for (i=0; i<5; i++) fsum[i] = BinSum(32, 2);
|
||||
|
||||
var k;
|
||||
var t;
|
||||
|
||||
for (t=0; t<=15; t++) {
|
||||
for (k=0; k<32; k++) {
|
||||
w[t][k] <== inp[t*32+k];
|
||||
}
|
||||
}
|
||||
|
||||
for (t=16; t<=79; t++) {
|
||||
for (k=0; k<32; k++) {
|
||||
xor4[t-16].a[k] <== w[t-3][k];
|
||||
xor4[t-16].b[k] <== w[t-8][k];
|
||||
xor4[t-16].c[k] <== w[t-14][k];
|
||||
xor4[t-16].d[k] <== w[t-16][k];
|
||||
}
|
||||
for (k=0; k<32; k++) {
|
||||
rotl_1[t-16].in[k] <== xor4[t-16].out[k];
|
||||
}
|
||||
for (k=0; k<32; k++) {
|
||||
w[t][k] <== rotl_1[t-16].out[k];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Initialize five working variables
|
||||
for (k=0; k<32; k++) {
|
||||
a[0][k] <== hin[k];
|
||||
b[0][k] <== hin[32*1 + k];
|
||||
c[0][k] <== hin[32*2 + k];
|
||||
d[0][k] <== hin[32*3 + k];
|
||||
e[0][k] <== hin[32*4 + k];
|
||||
}
|
||||
|
||||
for (t=0; t<=79; t++) {
|
||||
|
||||
for (k=0; k<32; k++) {
|
||||
t_tmp[t].a[k] <== a[t][k];
|
||||
t_tmp[t].b[k] <== b[t][k];
|
||||
t_tmp[t].c[k] <== c[t][k];
|
||||
t_tmp[t].d[k] <== d[t][k];
|
||||
t_tmp[t].e[k] <== e[t][k];
|
||||
t_tmp[t].k_t[k] <== K_t[t].out[k];
|
||||
t_tmp[t].w[k] <== w[t][k];
|
||||
|
||||
rotl_30[t].in[k] <== b[t][k];
|
||||
}
|
||||
|
||||
for (k=0; k<32; k++) {
|
||||
e[t+1][k] <== d[t][k];
|
||||
d[t+1][k] <== c[t][k];
|
||||
c[t+1][k] <== rotl_30[t].out[k];
|
||||
b[t+1][k] <== a[t][k];
|
||||
a[t+1][k] <== t_tmp[t].out[k];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
for (k=0; k<32; k++) {
|
||||
|
||||
fsum[0].in[0][k] <== hin[31*1-k];
|
||||
fsum[0].in[1][k] <== a[80][31-k];
|
||||
|
||||
fsum[1].in[0][k] <== hin[31*2-k+1];
|
||||
fsum[1].in[1][k] <== b[80][31-k];
|
||||
|
||||
fsum[2].in[0][k] <== hin[31*3-k+2];
|
||||
fsum[2].in[1][k] <== c[80][31-k];
|
||||
|
||||
fsum[3].in[0][k] <== hin[31*4-k+3];
|
||||
fsum[3].in[1][k] <== d[80][31-k];
|
||||
|
||||
fsum[4].in[0][k] <== hin[31*5-k+4];
|
||||
fsum[4].in[1][k] <== e[80][31-k];
|
||||
|
||||
}
|
||||
|
||||
for (k=0; k<32; k++) {
|
||||
out[k] <== fsum[0].out[k];
|
||||
out[k+32*1] <== fsum[1].out[k];
|
||||
out[k+32*2] <== fsum[2].out[k];
|
||||
out[k+32*3] <== fsum[3].out[k];
|
||||
out[k+32*4] <== fsum[4].out[k];
|
||||
}
|
||||
|
||||
}
|
||||
69
circuits/circuits/utils/sha1/t.circom
Normal file
69
circuits/circuits/utils/sha1/t.circom
Normal file
@@ -0,0 +1,69 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "./rotate.circom";
|
||||
include "circomlib/circuits/binsum.circom";
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
include "./f.circom";
|
||||
include "./constants.circom";
|
||||
|
||||
template T(t) {
|
||||
signal input a[32];
|
||||
signal input b[32];
|
||||
signal input c[32];
|
||||
signal input d[32];
|
||||
signal input e[32];
|
||||
signal input k_t[32];
|
||||
signal input w[32];
|
||||
|
||||
signal output out[32];
|
||||
|
||||
component rotatel_5 = RotL(32, 5);
|
||||
component f = f_t(t);
|
||||
|
||||
var k;
|
||||
for (k=0; k<32; k++) {
|
||||
rotatel_5.in[k] <== a[k];
|
||||
f.b[k] <== b[k];
|
||||
f.c[k] <== c[k];
|
||||
f.d[k] <== d[k];
|
||||
}
|
||||
|
||||
component sum_binary = BinSum(32, 5);
|
||||
var nout = 35; // in BinSum: nbits((2**32 -1)*5);
|
||||
|
||||
for (k=0; k<32; k++) {
|
||||
sum_binary.in[0][k] <== rotatel_5.out[31-k];
|
||||
sum_binary.in[1][k] <== f.out[31-k];
|
||||
sum_binary.in[2][k] <== e[31-k];
|
||||
sum_binary.in[3][k] <== k_t[31-k];
|
||||
sum_binary.in[4][k] <== w[31-k];
|
||||
}
|
||||
|
||||
component sum = Bits2Num(nout);
|
||||
for (k=0; k<nout; k++) {
|
||||
sum.in[k] <== sum_binary.out[k];
|
||||
}
|
||||
|
||||
// perform sum modulo 32
|
||||
signal sum_modulo;
|
||||
signal quotient;
|
||||
component less_than = LessThan(33);
|
||||
|
||||
sum_modulo <-- sum.out % 2**32;
|
||||
quotient <-- sum.out \ 2**32;
|
||||
|
||||
less_than.in[0] <== sum_modulo;
|
||||
less_than.in[1] <== 2**32;
|
||||
|
||||
sum.out === quotient*2**32 + sum_modulo;
|
||||
1 === less_than.out;
|
||||
|
||||
// reconvert to bit array
|
||||
component sum_binary_modulo = Num2Bits(32);
|
||||
sum_binary_modulo.in <== sum_modulo;
|
||||
|
||||
for (k=0; k<32; k++) {
|
||||
out[k] <== sum_binary_modulo.out[31-k];
|
||||
}
|
||||
|
||||
}
|
||||
29
circuits/circuits/utils/sha1/xor4.circom
Normal file
29
circuits/circuits/utils/sha1/xor4.circom
Normal file
@@ -0,0 +1,29 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
template Xor4(n) {
|
||||
signal input a[n];
|
||||
signal input b[n];
|
||||
signal input c[n];
|
||||
signal input d[n];
|
||||
|
||||
signal mid[n];
|
||||
signal a_temp[n];
|
||||
|
||||
signal output out[n];
|
||||
|
||||
/*
|
||||
xor3:
|
||||
mid = b*c
|
||||
out_xor3 = a*( 1 - 2*b -2*c + 4*mid ) + b + c - 2 * mid
|
||||
|
||||
xor4:
|
||||
a ^ b ^ c ^ d
|
||||
out_xor4 = out_xor3 - 2*d*out_xor3 + d
|
||||
*/
|
||||
|
||||
for (var k=0; k<n; k++) {
|
||||
mid[k] <== b[k]*c[k];
|
||||
a_temp[k] <== a[k] * (1 -2*b[k] -2*c[k] +4*mid[k]) + b[k] + c[k] -2*mid[k];
|
||||
out[k] <== a_temp[k] -2*d[k]*a_temp[k] + d[k];
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
pragma circom 2.1.5;
|
||||
|
||||
include "dmpierre/sha1-circom/circuits/sha1compression.circom";
|
||||
include "../../sha1/sha1compression.circom";
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
include "circomlib/circuits/mimcsponge.circom";
|
||||
include "@zk-email/circuits/lib/fp.circom";
|
||||
include "@zk-email/circuits/utils/array.circom";
|
||||
// include "@zk-email/circuits/lib/fp.circom";
|
||||
// include "@zk-email/circuits/utils/array.circom";
|
||||
include "../../other/fp.circom";
|
||||
include "../../other/array.circom";
|
||||
|
||||
//Adapted from @zk-email/circuits/helpers/sha.circom
|
||||
template Sha1Bytes(max_num_bytes) {
|
||||
@@ -61,11 +63,11 @@ template Sha1General(maxBitsPadded) {
|
||||
bitLengthVerifier.in[1] <== maxBitsPadded;
|
||||
bitLengthVerifier.out === 1;
|
||||
|
||||
component ha0 = H(0);
|
||||
component hb0 = H(1);
|
||||
component hc0 = H(2);
|
||||
component hd0 = H(3);
|
||||
component he0 = H(4);
|
||||
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[maxBlocks];
|
||||
|
||||
164
circuits/circuits/utils/shaBytes/dynamic/sha256Bytes.circom
Normal file
164
circuits/circuits/utils/shaBytes/dynamic/sha256Bytes.circom
Normal file
@@ -0,0 +1,164 @@
|
||||
pragma circom 2.1.9;
|
||||
|
||||
include "circomlib/circuits/bitify.circom";
|
||||
include "circomlib/circuits/sha256/constants.circom";
|
||||
include "circomlib/circuits/sha256/sha256compression.circom";
|
||||
include "circomlib/circuits/comparators.circom";
|
||||
// include "./fp.circom";
|
||||
// include "../utils/array.circom";
|
||||
// include "../utils/functions.circom";
|
||||
// include "@zk-email/circuits/lib/fp.circom";
|
||||
// include "@zk-email/circuits/utils/array.circom";
|
||||
include "../../other/fp.circom";
|
||||
include "../../other/array.circom";
|
||||
|
||||
/// @title Sha256Bytes
|
||||
/// @notice Computes the SHA256 hash of input bytes
|
||||
/// @input paddedIn Message to hash, padded as per the SHA256 specification; assumes to consist of bytes
|
||||
/// @input paddedInLength Length of the padded message; assumes to be in `ceil(log2(8 * maxByteLength))` bits
|
||||
/// @output out The 256-bit hash of the input message
|
||||
template Sha256Bytes(maxByteLength) {
|
||||
signal input paddedIn[maxByteLength];
|
||||
signal input paddedInLength;
|
||||
signal output out[256];
|
||||
|
||||
var maxBits = maxByteLength * 8;
|
||||
component sha = Sha256General(maxBits);
|
||||
|
||||
component bytes[maxByteLength];
|
||||
for (var i = 0; i < maxByteLength; i++) {
|
||||
bytes[i] = Num2Bits(8);
|
||||
bytes[i].in <== paddedIn[i];
|
||||
for (var j = 0; j < 8; j++) {
|
||||
sha.paddedIn[i*8+j] <== bytes[i].out[7-j];
|
||||
}
|
||||
}
|
||||
sha.paddedInLength <== paddedInLength * 8;
|
||||
|
||||
for (var i = 0; i < 256; i++) {
|
||||
out[i] <== sha.out[i];
|
||||
}
|
||||
}
|
||||
|
||||
/// @title Sha256General
|
||||
/// @notice A modified version of the SHA256 circuit that allows specified length messages up to a
|
||||
/// max to all work via array indexing on the SHA256 compression circuit.
|
||||
/// @input paddedIn Message to hash padded as per the SHA256 specification; assumes to consist of bits
|
||||
/// @input paddedInLength Length of the padded message; assumes to be in `ceil(log2(maxBitLength))` bits
|
||||
/// @output out The 256-bit hash of the input message
|
||||
template Sha256General(maxBitLength) {
|
||||
// maxBitLength must be a multiple of 512
|
||||
// the bit circuits in this file are limited to 15 so must be raised if the message is longer.
|
||||
assert(maxBitLength % 512 == 0);
|
||||
|
||||
var maxBitsPaddedBits = log2Ceil(maxBitLength);
|
||||
|
||||
// Note that maxBitLength = maxBits + 64
|
||||
signal input paddedIn[maxBitLength];
|
||||
signal input paddedInLength;
|
||||
|
||||
signal output out[256];
|
||||
|
||||
signal inBlockIndex;
|
||||
|
||||
var i;
|
||||
var k;
|
||||
var j;
|
||||
var maxBlocks;
|
||||
var bitsLastBlock;
|
||||
maxBlocks = (maxBitLength\512);
|
||||
|
||||
inBlockIndex <-- (paddedInLength >> 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<maxBits; k++) {
|
||||
// paddedIn[k] <== in[k];
|
||||
// }
|
||||
// paddedIn[maxBits] <== 1;
|
||||
// for (k=maxBits+1; k<maxBlocks*512-64; k++) {
|
||||
// paddedIn[k] <== 0;
|
||||
// }
|
||||
// for (k = 0; k< 64; k++) {
|
||||
// paddedIn[maxBlocks*512 - k -1] <== (maxBits >> 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<maxBlocks; i++) {
|
||||
sha256compression[i] = Sha256compression() ;
|
||||
|
||||
if (i==0) {
|
||||
for (k=0; k<32; k++ ) {
|
||||
sha256compression[i].hin[0*32+k] <== ha0.out[k];
|
||||
sha256compression[i].hin[1*32+k] <== hb0.out[k];
|
||||
sha256compression[i].hin[2*32+k] <== hc0.out[k];
|
||||
sha256compression[i].hin[3*32+k] <== hd0.out[k];
|
||||
sha256compression[i].hin[4*32+k] <== he0.out[k];
|
||||
sha256compression[i].hin[5*32+k] <== hf0.out[k];
|
||||
sha256compression[i].hin[6*32+k] <== hg0.out[k];
|
||||
sha256compression[i].hin[7*32+k] <== hh0.out[k];
|
||||
}
|
||||
} else {
|
||||
for (k=0; k<32; k++ ) {
|
||||
sha256compression[i].hin[32*0+k] <== sha256compression[i-1].out[32*0+31-k];
|
||||
sha256compression[i].hin[32*1+k] <== sha256compression[i-1].out[32*1+31-k];
|
||||
sha256compression[i].hin[32*2+k] <== sha256compression[i-1].out[32*2+31-k];
|
||||
sha256compression[i].hin[32*3+k] <== sha256compression[i-1].out[32*3+31-k];
|
||||
sha256compression[i].hin[32*4+k] <== sha256compression[i-1].out[32*4+31-k];
|
||||
sha256compression[i].hin[32*5+k] <== sha256compression[i-1].out[32*5+31-k];
|
||||
sha256compression[i].hin[32*6+k] <== sha256compression[i-1].out[32*6+31-k];
|
||||
sha256compression[i].hin[32*7+k] <== sha256compression[i-1].out[32*7+31-k];
|
||||
}
|
||||
}
|
||||
|
||||
for (k=0; k<512; k++) {
|
||||
sha256compression[i].inp[k] <== paddedIn[i*512+k];
|
||||
}
|
||||
}
|
||||
|
||||
// Select the correct compression output for the given length, instead of just the last one.
|
||||
component arraySelectors[256];
|
||||
for (k=0; k<256; k++) {
|
||||
arraySelectors[k] = ItemAtIndex(maxBlocks);
|
||||
for (j=0; j<maxBlocks; j++) {
|
||||
arraySelectors[k].in[j] <== sha256compression[j].out[k];
|
||||
}
|
||||
arraySelectors[k].index <== inBlockIndex - 1; // The index is 0 indexed and the block numbers are 1 indexed.
|
||||
out[k] <== arraySelectors[k].out;
|
||||
}
|
||||
|
||||
// for (k=0; k<256; k++) {
|
||||
// out[k] <== sha256compression[maxBlocks-1].out[k];
|
||||
// }
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user