Merge pull request #203 from zk-passport/refactor-circuits

Refactor circuits
This commit is contained in:
turnoffthiscomputer
2024-09-20 15:49:08 +02:00
committed by GitHub
143 changed files with 5420 additions and 4425 deletions

View File

@@ -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

View File

@@ -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.

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View 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]);
}

View File

@@ -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];
}
}

View File

@@ -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

View File

@@ -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]);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View 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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -1,6 +1,6 @@
pragma circom 2.1.6;
include "../../utils/isOlderThan.circom";
include "../../utils/passport/date/isOlderThan.circom";
template isOlderThan_tester() {

View File

@@ -1,6 +1,6 @@
pragma circom 2.1.6;
include "../../utils/isValid.circom";
include "../../utils/passport/date/isValid.circom";
template IsValid_tester() {

View File

@@ -1,5 +0,0 @@
pragma circom 2.1.6;
include "../../utils/leafHasherLight.circom";
component main = LeafHasherLightWithSigAlg(32);

View File

@@ -0,0 +1,6 @@
pragma circom 2.1.6;
include "../../utils/passport/customHashers.circom";
// component main = LeafHasher(12);
component main = CustomHasher(32);

View File

@@ -1,3 +0,0 @@
include "@zk-email/circuits/lib/rsa.circom";
component main = RSAVerifier65537(121, 17);

View File

@@ -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);

View File

@@ -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];
}
}

View File

@@ -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]);
}
}

View File

@@ -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

View File

@@ -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) {

View File

@@ -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];

View File

@@ -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++) {

View File

@@ -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);

View File

@@ -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]);

View File

@@ -1,4 +1,4 @@
pragma circom 2.1.5;
pragma circom 2.1.9;
include "curve.circom";
include "p256_func.circom";

View File

@@ -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));

View File

@@ -1,6 +1,6 @@
/// UNUSED FILE
pragma circom 2.1.5;
pragma circom 2.1.9;
include "bigInt_func.circom";
include "p256_func.circom";

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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;
}

View 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;
}
}

View 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];
}
}
}

View 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;
}

View 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
}
}

View 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;
}
}

View File

@@ -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.

View 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;
}

View 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;
}

View 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;
}

View File

@@ -1,4 +1,4 @@
pragma circom 2.1.5;
pragma circom 2.1.9;
include "circomlib/circuits/comparators.circom";
include "circomlib/circuits/bitify.circom";

View File

@@ -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";

View 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;
}

View File

@@ -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;
}

View File

@@ -1,4 +1,4 @@
pragma circom 2.1.6;
pragma circom 2.1.9;
include "../node_modules/circomlib/circuits/comparators.circom";

View File

@@ -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";

View File

@@ -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";

View File

@@ -0,0 +1,6 @@
pragma circom 2.1.9;
template FormatECDSAInputs(signatureAlgorithm, k) {
}

View 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);
}

View 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;
}

View 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;
}

View 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;
}
}

View 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];
}
}

View 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;
}
}

View File

@@ -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];

View 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
}
}

View File

@@ -0,0 +1,5 @@
pragma circom 2.1.9;
include "./rsa.circom";
component main = RsaVerifyPkcs1v15(64, 32, 17, 256);

View File

@@ -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";

View File

@@ -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;
}

View File

@@ -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){

View File

@@ -1,4 +1,4 @@
pragma circom 2.1.6;
pragma circom 2.1.9;
template Xor2(n) {
signal input a[n];

View 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];
}
}

View 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];
}
}
}
}

View 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];
}
}

View 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 ];
}
}

View 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)];
}
}
}

View 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];
}
}

View 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];
}
}

View 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];
}
}

View File

@@ -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];

View 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