Merge branch 'dev' of https://github.com/zk-passport/openpassport into feat/docs-smt

This commit is contained in:
turnoffthiscomputer
2025-01-15 19:17:30 +01:00
15 changed files with 425 additions and 270 deletions

View File

@@ -10,6 +10,50 @@ include "../utils/passport/disclose/disclose.circom";
include "../utils/passport/disclose/proveCountryIsNotInList.circom";
include "../utils/passport/ofac/ofac_name.circom";
/// @title OPENPASSPORT_PROVE
/// @notice Main circuit to verify passport data and be used to several purposes to enable passport
/// @dev Handles passport verification, OFAC checks, selective disclosure, and commitment generation
/// @param DG_HASH_ALGO Hash algorithm used for DG (Document Group) hashing
/// @param ECONTENT_HASH_ALGO Hash algorithm used for eContent
/// @param signatureAlgorithm Algorithm used for passport signature verification
/// @param n Number of bits per chunk the key is split into.
/// @param k Number of chunks the key is split into.
/// @param MAX_ECONTENT_PADDED_LEN Maximum length of padded eContent
/// @param MAX_SIGNED_ATTR_PADDED_LEN Maximum length of padded signed attributes
/// @param FORBIDDEN_COUNTRIES_LIST_LENGTH Length of the forbidden countries list
/// @input dg1 Document Group 1 data (93 bytes)
/// @input dg1_hash_offset Offset for DG1 hash
/// @input dg2_hash Document Group 2 hash (64 bytes)
/// @input eContent eContent data
/// @input eContent_padded_length Padded length of eContent
/// @input signed_attr Signed attributes data
/// @input signed_attr_padded_length Padded length of signed attributes
/// @input signed_attr_econtent_hash_offset Offset for eContent hash in signed attributes
/// @input pubKey Public key for signature verification
/// @input signature Passport signature
/// @input selector_mode Mode selectors for different operations
/// @input smt_leaf_value SMT leaf value for OFAC check
/// @input smt_root SMT root for OFAC check
/// @input smt_siblings SMT siblings for OFAC check
/// @input selector_ofac Selector for OFAC check
/// @input forbidden_countries_list List of forbidden countries
/// @input selector_dg1 Selectors for DG1 disclosure
/// @input selector_older_than Selector for age verification
/// @input current_date Current date for age verification
/// @input majority Majority age threshold
/// @input user_identifier User identifier for commitment
/// @input scope Scope for nullifier
/// @input secret Secret for commitment generation. Supposed to be saved by the user to access this commitment.
/// @input dsc_secret One time secret data to generate the blinded commitment. This blinded dsc commitment is used to find the link between a proof from this circuit and a proof from the dsc circuit.
/// @output nullifier Generated nullifier
/// @output revealedData_packed Selectively disclosed data in the passport
/// @output older_than Age verification result
/// @output pubKey_disclosed Disclosed public key
/// @output forbidden_countries_list_packed_disclosed Packed forbidden countries list
/// @output ofac_result OFAC check result
/// @output commitment Unique commitment for the passport data and their secret
/// @output blinded_dsc_commitment To find the link between a proof from this circuit and a proof from the dsc circuit.
template OPENPASSPORT_PROVE(DG_HASH_ALGO, ECONTENT_HASH_ALGO, signatureAlgorithm, n, k, MAX_ECONTENT_PADDED_LEN, MAX_SIGNED_ATTR_PADDED_LEN, FORBIDDEN_COUNTRIES_LIST_LENGTH) {
var kLengthFactor = getKLengthFactor(signatureAlgorithm);
var kScaled = k * kLengthFactor;

View File

@@ -0,0 +1,13 @@
pragma circom 2.1.9;
include "../../../utils/circomlib/signature/ecdsa/ecdsaVerifier.circom";
template VerifyBrainpoolP224r1Sha160() {
signal input signature[2 * 7];
signal input pubKey[2 * 7];
signal input hashParsed[160];
EcdsaVerifier(27, 32, 7)(signature, pubKey, hashParsed);
}
component main = VerifyBrainpoolP224r1Sha160();

View File

@@ -0,0 +1,13 @@
pragma circom 2.1.9;
include "../../../utils/circomlib/signature/ecdsa/ecdsaVerifier.circom";
template VerifyBrainpoolP256r1Sha512() {
signal input signature[2 * 4];
signal input pubKey[2 * 4];
signal input hashParsed[512];
EcdsaVerifier(25, 64, 4)(signature, pubKey, hashParsed);
}
component main = VerifyBrainpoolP256r1Sha512();

View File

@@ -0,0 +1,13 @@
pragma circom 2.1.9;
include "../../../utils/circomlib/signature/ecdsa/ecdsaVerifier.circom";
template VerifyBrainpoolP384r1Sha384() {
signal input signature[2 * 6];
signal input pubKey[2 * 6];
signal input hashParsed[384];
EcdsaVerifier(22, 64, 6)(signature, pubKey, hashParsed);
}
component main = VerifyBrainpoolP384r1Sha384();

View File

@@ -349,7 +349,7 @@ template BigMultModPDl(CHUNK_SIZE, CHUNK_NUMBER_GREATER, CHUNK_NUMBER_LESS, CHUN
mult2.in1 <== modulus;
}
component isZero = BigIntIsZero(CHUNK_SIZE, CHUNK_SIZE * 2 + log_ceil(CHUNK_NUMBER_MODULUS + CHUNK_NUMBER_DIV - 1), CHUNK_NUMBER_BASE - 1);
component isZero = BigIntIsZero(CHUNK_SIZE, CHUNK_SIZE * 2 + log_ceil_dl(CHUNK_NUMBER_MODULUS + CHUNK_NUMBER_DIV - 1), CHUNK_NUMBER_BASE - 1);
for (var i = 0; i < CHUNK_NUMBER_MODULUS; i++) {
isZero.in[i] <== mult.out[i] - mult2.out[i] - mod[i];
}

View File

@@ -6,6 +6,13 @@ include "@zk-email/circuits/lib/sha.circom";
include "./dynamic/sha384Bytes.circom";
include "./dynamic/sha512Bytes.circom";
/// @title ShaBytesDynamic
/// @notice Computes the hash of an input message using a specified hash length and padded input
/// @param hashLen Desired length of the hash in bits (e.g., 512, 384, 256, 224, 160)
/// @param max_num_bits Maximum number of bits in the padded input
/// @input in_padded Padded input message, represented as an array of bits
/// @input in_len_padded_bytes Length of the padded input in bytes
/// @output hash The computed hash of the input message, with length specified by `hashLen`
template ShaBytesDynamic(hashLen, max_num_bits) {
signal input in_padded[max_num_bits];
signal input in_len_padded_bytes;

View File

@@ -1,22 +1,21 @@
pragma circom 2.1.6;
include "../ec/curve.circom";
include "../ec/get.circom";
include "../bigInt/bigInt.circom";
include "../../ec/curve.circom";
include "../../ec/get.circom";
include "../../bigInt/bigInt.circom";
// Here is ecdsa signature verification
// For now, only 256 bit curves are allowed with chunking 64 4
//--------------------------------------------------------------------------------------------------------------------------------
// Use this one if you hash message in circuit (message is bits, not chunked int)!!!
// signature[2] = [r, s] - signature
// pubkey[2] = [x, y] - pubkey for signature
// hashed[ALGO] = h - hashed message by some algo (typically sha-2 256 for 256 bit curves)
// n is curve order
// s_inv = s ^ -1 mod n
// (x1, y1) = h * s_inv * G + r * s_inv * (x, y)
// x1 === r
/// @title verifyECDSABits
/// @notice Verifies an ECDSA signature using a specified curve and hashing algorithm
/// @param CHUNK_SIZE The size of each chunk in bits, used for representing large integers
/// @param CHUNK_NUMBER The number of chunks used to represent each large integer
/// @param A The coefficient `a` of the elliptic curve equation
/// @param B The coefficient `b` of the elliptic curve equation
/// @param P The prime number defining the finite field for the elliptic curve
/// @param ALGO The size of the hashed message in bits, corresponding to the chosen hashing algorithm
/// @input pubkey Public key used for verification, represented as a 2D array of chunks
/// @input signature Signature to verify, represented as a 2D array of chunks
/// @input hashed The hashed message being verified, represented as an array of bits with length `ALGO`
template verifyECDSABits(CHUNK_SIZE, CHUNK_NUMBER, A, B, P, ALGO){
signal input pubkey[2][CHUNK_NUMBER];
signal input signature[2][CHUNK_NUMBER];
signal input hashed[ALGO];

View File

@@ -1,8 +1,16 @@
pragma circom 2.1.9;
include "./signatureAlgorithm.circom";
include "../circomlib/signature/ecdsa.circom";
include "../../../passport/signatureAlgorithm.circom";
include "ecdsa.circom";
/// @title EcdsaVerifier
/// @notice Verifies an ECDSA signature for a given signature algorithm, public key, and message hash
/// @param signatureAlgorithm The hashing/signature algorithm as defined in `signatureAlgorithm.circom`
/// @param n The number of chunks used to represent integers (e.g., public key components and signature)
/// @param k The base chunk size, scaled based on the signature algorithm
/// @input signature The [R, S] component in an array
/// @input pubKey The public key to verify the signature
/// @input hashParsed The hash of the message to be verified
template EcdsaVerifier(signatureAlgorithm, n, k) {
var kLengthFactor = getKLengthFactor(signatureAlgorithm);
var kScaled = k * kLengthFactor;
@@ -15,11 +23,13 @@ template EcdsaVerifier(signatureAlgorithm, n, k) {
signal hash[n * k];
//if hash is greater than or equal to the field bits then truncate the rightmost part
if (HASH_LEN_BITS >= n * k) {
for (var i = 0; i < n * k; i++) {
hash[i] <== hashParsed[i];
}
}
//if hash is less than the field size then pad zeroes to the left
if (HASH_LEN_BITS < n * k) {
for (var i = n * k - 1; i >= 0; i--) {
if (i <= n * k - 1 - HASH_LEN_BITS) {
@@ -43,117 +53,58 @@ template EcdsaVerifier(signatureAlgorithm, n, k) {
}
signal pubkey_xy[2][k] <== [pubKey_x, pubKey_y];
var a[k] = get_a(signatureAlgorithm);
var b[k] = get_b(signatureAlgorithm);
var p[k] = get_p(signatureAlgorithm);
// verify eContentHash signature
if (signatureAlgorithm == 7 || signatureAlgorithm == 8) {
component ecdsa_verify = verifyECDSABits(n, k, [
component ecdsa_verify = verifyECDSABits(n, k, a, b, p, n * k);
ecdsa_verify.pubkey <== pubkey_xy;
ecdsa_verify.signature <== [signature_r, signature_s];
ecdsa_verify.hashed <== hash;
}
function get_a(signatureAlgorithm) {
if (signatureAlgorithm == 7 || signatureAlgorithm == 8) {
return [
18446744073709551612,
4294967295,
0,
18446744069414584321
],
[
4309448131093880907,
7285987128567378166,
12964664127075681980,
6540974713487397863
],
[
18446744073709551615,
4294967295,
0,
18446744069414584321
], n * k);
ecdsa_verify.pubkey <== pubkey_xy;
ecdsa_verify.signature <== [signature_r, signature_s];
ecdsa_verify.hashed <== hash;
];
}
if (signatureAlgorithm == 9 || signatureAlgorithm == 23) {
component ecdsa_verify = verifyECDSABits(n, k, [
return [
4294967292,
18446744069414584320,
18446744073709551614,
18446744073709551615,
18446744073709551615,
18446744073709551615
],
[
3064076045283764975,
14291673747578343837,
221811693264799578,
1737717031765098770,
10992729701402291481,
12912154004749740004
],
[
4294967295,
18446744069414584320,
18446744073709551614,
18446744073709551615,
18446744073709551615,
18446744073709551615
], n * k);
ecdsa_verify.pubkey <== pubkey_xy;
ecdsa_verify.signature <== [signature_r, signature_s];
ecdsa_verify.hashed <== hash;
];
}
if (signatureAlgorithm == 21 || signatureAlgorithm == 24 || signatureAlgorithm == 25) {
component ecdsa_verify = verifyECDSABits(n, k, [
if (signatureAlgorithm == 21 || signatureAlgorithm == 24 || signatureAlgorithm == 25) {
return [
16810331318623712729,
18122579188607900780,
17219079075415130087,
9032542404991529047
],
[
7767825457231955894,
10773760575486288334,
17523706096862592191,
2800214691157789508
],
[
2311270323689771895,
7943213001558335528,
4496292894210231666,
12248480212390422972
], n * k);
ecdsa_verify.pubkey <== pubkey_xy;
ecdsa_verify.signature <== [signature_r, signature_s];
ecdsa_verify.hashed <== hash;
}
];
}
if (signatureAlgorithm == 22 || signatureAlgorithm == 26) {
component ecdsa_verify = verifyECDSABits(n, k, [
return [
335737924824737830,
9990533504564909291,
1410020238645393679,
14032832221039175559,
4355552632119865248,
8918115475071440140
],
[
4230998357940653073,
8985869839777909140,
3352946025465340629,
3438355245973688998,
10032249017711215740,
335737924824737830
],
[
9747760000893709395,
12453481191562877553,
1347097566612230435,
1526563086152259252,
1107163671716839903,
10140169582434348328
], n * k);
ecdsa_verify.pubkey <== pubkey_xy;
ecdsa_verify.signature <== [signature_r, signature_s];
ecdsa_verify.hashed <== hash;
];
}
if (signatureAlgorithm == 27 || signatureAlgorithm == 28 || signatureAlgorithm == 30) {
component ecdsa_verify = verifyECDSABits(n, k, [
return [
3402800963,
2953063001,
1310206680,
@@ -161,32 +112,11 @@ template EcdsaVerifier(signatureAlgorithm, n, k) {
697828262,
2848877596,
1755702828
],
[
946618379,
1725674354,
1042363858,
2837670371,
2265387953,
3487842616,
629208636
],
[
2127085823,
2547681781,
2963212119,
1976686471,
706228261,
641951366,
3619763370
], n * k);
ecdsa_verify.pubkey <== pubkey_xy;
ecdsa_verify.signature <== [signature_r, signature_s];
ecdsa_verify.hashed <== hash;
];
}
if (signatureAlgorithm == 29) {
component ecdsa_verify = verifyECDSABits(n, k, [
return [
16699818341992010954,
9156125524185237433,
733789637240866997,
@@ -195,8 +125,64 @@ template EcdsaVerifier(signatureAlgorithm, n, k) {
10721906936585459216,
16299214545461923013,
8660601516620528521
],
[
];
}
return [0];
}
function get_b(signatureAlgorithm) {
if (signatureAlgorithm == 7 || signatureAlgorithm == 8) {
return [
4309448131093880907,
7285987128567378166,
12964664127075681980,
6540974713487397863
];
}
if (signatureAlgorithm == 9 || signatureAlgorithm == 23) {
return [
3064076045283764975,
14291673747578343837,
221811693264799578,
1737717031765098770,
10992729701402291481,
12912154004749740004
];
}
if (signatureAlgorithm == 21 || signatureAlgorithm == 24 || signatureAlgorithm == 25) {
return [
7767825457231955894,
10773760575486288334,
17523706096862592191,
2800214691157789508
];
}
if (signatureAlgorithm == 22 || signatureAlgorithm == 26) {
return [
4230998357940653073,
8985869839777909140,
3352946025465340629,
3438355245973688998,
10032249017711215740,
335737924824737830
];
}
if (signatureAlgorithm == 27 || signatureAlgorithm == 28 || signatureAlgorithm == 30) {
return [
946618379,
1725674354,
1042363858,
2837670371,
2265387953,
3487842616,
629208636
];
}
if (signatureAlgorithm == 29) {
return [
2885045271355914019,
10970857440773072349,
8645948983640342119,
@@ -205,8 +191,64 @@ template EcdsaVerifier(signatureAlgorithm, n, k) {
12116154835845181897,
16904370861210688858,
4465624766311842250
],
[
];
}
return [0];
}
function get_p(signatureAlgorithm) {
if (signatureAlgorithm == 7 || signatureAlgorithm == 8) {
return [
18446744073709551615,
4294967295,
0,
18446744069414584321
];
}
if (signatureAlgorithm == 9 || signatureAlgorithm == 23) {
return [
4294967295,
18446744069414584320,
18446744073709551614,
18446744073709551615,
18446744073709551615,
18446744073709551615
];
}
if (signatureAlgorithm == 21 || signatureAlgorithm == 24 || signatureAlgorithm == 25) {
return [
2311270323689771895,
7943213001558335528,
4496292894210231666,
12248480212390422972
];
}
if (signatureAlgorithm == 22 || signatureAlgorithm == 26) {
return [
9747760000893709395,
12453481191562877553,
1347097566612230435,
1526563086152259252,
1107163671716839903,
10140169582434348328
];
}
if (signatureAlgorithm == 27 || signatureAlgorithm == 28 || signatureAlgorithm == 30) {
return [
2127085823,
2547681781,
2963212119,
1976686471,
706228261,
641951366,
3619763370
];
}
if (signatureAlgorithm == 29) {
return [
2930260431521597683,
2918894611604883077,
12595900938455318758,
@@ -215,10 +257,8 @@ template EcdsaVerifier(signatureAlgorithm, n, k) {
14641358191536493070,
4599554755319692295,
12312170373589877899
], n * k);
ecdsa_verify.pubkey <== pubkey_xy;
ecdsa_verify.signature <== [signature_r, signature_s];
ecdsa_verify.hashed <== hash;
];
}
return [0];
}

View File

@@ -9,8 +9,17 @@ include "circomlib/circuits/bitify.circom";
// For SHA1, the OID is 0x3021300906052b0e03021a05000414
// For SHA256, the OID is 0x3031300d060960864801650304020105000420
// For SHA384, the OID is 0x3041300d060960864801650304020205000430
// For SHA512, the OID is 0x3051300d060960864801650304020305000440
// For SHA512, the OID is 0x3051300d060960864801650304020305000440
/// @title Pkcs1v1_5Padding
/// @notice Verify PKCS#1 v1.5 padding scheme for RSA signatures
/// @dev Pads the message according to PKCS#1 v1.5 and verifies the padding
/// @param CHUNK_SIZE Number of bits per chunk
/// @param CHUNK_NUMBER Number of chunks the message is split into
/// @param HASH_SIZE Size of the hash in bits (160 for SHA1, 256 for SHA256, 384 for SHA384, 512 for SHA512)
/// @input modulus The RSA modulus split into chunks
/// @input message The message hash to be padded
/// @output out The padded message split into chunks
template Pkcs1v1_5Padding(CHUNK_SIZE, CHUNK_NUMBER, HASH_SIZE) {
signal input modulus[CHUNK_NUMBER];
signal input message[CHUNK_NUMBER];
@@ -88,6 +97,10 @@ template Pkcs1v1_5Padding(CHUNK_SIZE, CHUNK_NUMBER, HASH_SIZE) {
}
}
/// @title getOID
/// @notice Returns the OID (Object Identifier) for the specified hash function
/// @param HASH_SIZE Size of the hash function in bits
/// @return The OID value as a hex number
function getOID(HASH_SIZE) {
if (HASH_SIZE == 160) {
return 0x3021300906052b0e03021a05000414;
@@ -104,6 +117,10 @@ function getOID(HASH_SIZE) {
return 0;
}
/// @title getOIDSize
/// @notice Returns the size of the OID for the specified hash function
/// @param HASH_SIZE Size of the hash function in bits
/// @return The size of the OID in bits
function getOIDSize(HASH_SIZE) {
if (HASH_SIZE == 160) {
return 120;

View File

@@ -4,12 +4,15 @@ include "@zk-email/circuits/lib/fp.circom";
include "./pkcs1v1_5Padding.circom";
include "../FpPowMod.circom";
// For 2048bits RSA, CHUNK_SIZE = 64, CHUNK_NUMBER = 32
// For 3072bits RSA, CHUNK_SIZE = 64, CHUNK_NUMBER = 48
// For 4096bits RSA, CHUNK_SIZE = 64, CHUNK_NUMBER = 64
// HASH_SIZE is the size of the hash in bits
/// @title VerifyRsa3Pkcs1v1_5
/// @notice Verifies RSA signatures with exponent 3 using PKCS#1 v1.5 padding
/// @dev Supports RSA key sizes of 2048, 3072, and 4096 bits
/// @param CHUNK_SIZE Number of bits per chunk (typically 64)
/// @param CHUNK_NUMBER Number of chunks (32 for 2048-bit RSA, 48 for 3072-bit, 64 for 4096-bit)
/// @param HASH_SIZE Size of the hash in bits (160 for SHA1, 256 for SHA256, 384 for SHA384 and 512 for SHA512)
/// @input signature The RSA signature split into chunks
/// @input modulus The RSA modulus split into chunks
/// @input message The message hash to verify
template VerifyRsa3Pkcs1v1_5(CHUNK_SIZE, CHUNK_NUMBER, HASH_SIZE) {
signal input signature[CHUNK_NUMBER];
signal input modulus[CHUNK_NUMBER];

View File

@@ -4,12 +4,15 @@ include "@zk-email/circuits/lib/fp.circom";
include "./pkcs1v1_5Padding.circom";
include "../FpPowMod.circom";
// For 2048bits RSA, CHUNK_SIZE = 64, CHUNK_NUMBER = 32
// For 3072bits RSA, CHUNK_SIZE = 64, CHUNK_NUMBER = 48
// For 4096bits RSA, CHUNK_SIZE = 64, CHUNK_NUMBER = 64
// HASH_SIZE is the size of the hash in bits
/// @title VerifyRsa65537Pkcs1v1_5
/// @notice Verifies RSA signatures with exponent 65537 using PKCS#1 v1.5 padding
/// @dev Supports RSA key sizes of 2048, 3072, and 4096 bits
/// @param CHUNK_SIZE Number of bits per chunk (typically 64)
/// @param CHUNK_NUMBER Number of chunks (32 for 2048-bit RSA, 48 for 3072-bit, 64 for 4096-bit)
/// @param HASH_SIZE Size of the hash in bits (160 for SHA1, 256 for SHA256, 384 for SHA384 and 512 for SHA512)
/// @input signature The RSA signature split into chunks
/// @input modulus The RSA modulus split into chunks
/// @input message The message hash to verify
template VerifyRsa65537Pkcs1v1_5(CHUNK_SIZE, CHUNK_NUMBER, HASH_SIZE) {
signal input signature[CHUNK_NUMBER];
signal input modulus[CHUNK_NUMBER];

View File

@@ -350,46 +350,3 @@ function getExponentBits(signatureAlgorithm) {
}
return 0;
}
function getPadding(signatureAlgorithm) {
var padding[5];
if (
signatureAlgorithm == 3 ||
signatureAlgorithm == 11
) {
padding[0] = 83887124; // 5000414
padding[1] = 650212878678426138; // 906052B0E03021A
padding[2] = 18446744069417738544; // FFFFFFFF00302130
padding[3] = 18446744073709551615; // FFFFFFFFFFFFFFFF
padding[4] = 562949953421311; // 1FFFFFFFFFFFF
}
if (
signatureAlgorithm == 1 ||
signatureAlgorithm == 4 ||
signatureAlgorithm == 10 ||
signatureAlgorithm == 13
) {
padding[0] = 217300885422736416; // 304020105000420
padding[1] = 938447882527703397; // D06096086480165
padding[2] = 18446744069417742640; // FFFFFFFF00303130
padding[3] = 18446744073709551615; // FFFFFFFFFFFFFFFF
padding[4] = 562949953421311; // 1FFFFFFFFFFFF
}
if (signatureAlgorithm == 14) {
padding[0] = 83887136; // 5000420
padding[1] = 4030602964456935904153698817; // D0609608648016503040201
padding[2] = 79228162514264337589252141360; // FFFFFFFFFFFFFFFF00303130
padding[3] = 79228162514264337593543950335; // FFFFFFFFFFFFFFFFFFFFFFFF
padding[4] = 2417851639229258349412351; // 1FFFFFFFFFFFFFFFFFFFF
}
if (signatureAlgorithm == 15) {
padding[0] = 217300894012671040; // 304020305000440
padding[1] = 938447882527703397; // D06096086480165
padding[2] = 18446744069417750832; // FFFFFFFF00305130
padding[3] = 18446744073709551615; // FFFFFFFFFFFFFFFF
padding[4] = 562949953421311; // 1FFFFFFFFFFFF
}
return padding;
}

View File

@@ -2,7 +2,7 @@ pragma circom 2.1.9;
include "../circomlib/signature/rsapss/rsapss3.circom";
include "../circomlib/signature/rsapss/rsapss65537.circom";
include "ecdsaVerifier.circom";
include "../circomlib/signature/ecdsa/ecdsaVerifier.circom";
include "../circomlib/signature/rsa/verifyRsa3Pkcs1v1_5.circom";
include "../circomlib/signature/rsa/verifyRsa65537Pkcs1v1_5.circom";
include "@zk-email/circuits/utils/bytes.circom";
@@ -22,30 +22,40 @@ template SignatureVerifier(signatureAlgorithm, n, k) {
signal hashParsed[msg_len] <== HashParser(signatureAlgorithm, n, k)(hash);
if (signatureAlgorithm == 1) {
component rsa = VerifyRsa65537Pkcs1v1_5(n, k, 256);
if (
signatureAlgorithm == 1
|| signatureAlgorithm == 3
|| signatureAlgorithm == 10
|| signatureAlgorithm == 11
|| signatureAlgorithm == 14
|| signatureAlgorithm == 15
|| signatureAlgorithm == 31
) {
component rsa65537 = VerifyRsa65537Pkcs1v1_5(n, k, HASH_LEN_BITS);
for (var i = 0; i < msg_len; i++) {
rsa.message[i] <== hashParsed[i];
rsa65537.message[i] <== hashParsed[i];
}
for (var i = msg_len; i < k; i++) {
rsa.message[i] <== 0;
rsa65537.message[i] <== 0;
}
rsa.modulus <== pubKey;
rsa.signature <== signature;
rsa65537.modulus <== pubKey;
rsa65537.signature <== signature;
}
if (signatureAlgorithm == 3) {
component rsa = VerifyRsa65537Pkcs1v1_5(n, k, 160);
if (
signatureAlgorithm == 13
|| signatureAlgorithm == 32
) {
component rsa3 = VerifyRsa3Pkcs1v1_5(n, k, HASH_LEN_BITS);
for (var i = 0; i < msg_len; i++) {
rsa.message[i] <== hashParsed[i];
rsa3.message[i] <== hashParsed[i];
}
for (var i = msg_len; i < k; i++) {
rsa.message[i] <== 0;
rsa3.message[i] <== 0;
}
rsa.modulus <== pubKey;
rsa.signature <== signature;
rsa3.modulus <== pubKey;
rsa3.signature <== signature;
}
if (
signatureAlgorithm == 4
|| signatureAlgorithm == 12
@@ -92,71 +102,6 @@ template SignatureVerifier(signatureAlgorithm, n, k) {
) {
EcdsaVerifier (signatureAlgorithm, n, k)(signature, pubKey, hash);
}
if (signatureAlgorithm == 10) {
component rsa = VerifyRsa65537Pkcs1v1_5(n, k, 256);
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 == 11) {
component rsa = VerifyRsa65537Pkcs1v1_5(n, k, 160);
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 == 12) {
}
if (
signatureAlgorithm == 13
|| signatureAlgorithm == 32
) {
component rsa = VerifyRsa3Pkcs1v1_5(n, k, 256);
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 == 14) {
component rsa = VerifyRsa65537Pkcs1v1_5(n, k, 256);
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 == 15
|| signatureAlgorithm == 31
) {
component rsa = VerifyRsa65537Pkcs1v1_5(n, k, 512);
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;
}
}

View File

@@ -9,6 +9,7 @@
"test-prove": "yarn ts-mocha --max-old-space-size=40960 'tests/prove.test.ts' --exit",
"test-rsa": "yarn ts-mocha --max-old-space-size=8192 'tests/utils/rsaPkcs1v1_5.test.ts' --exit",
"test-rsa-pss": "yarn ts-mocha --max-old-space-size=8192 'tests/utils/rsapss.test.ts' --exit",
"test-ecdsa": "yarn ts-mocha --max-old-space-size=8192 'tests/utils/ecdsa.test.ts' --exit",
"install-circuits": "cd ../common && yarn && cd ../circuits && yarn",
"format": "prettier --write .",
"lint": "prettier --check ."

View File

@@ -0,0 +1,100 @@
import { wasm as wasmTester } from 'circom_tester';
import * as crypto from 'crypto';
import { initElliptic } from '../../../common/src/utils/certificate_parsing/elliptic';
import { splitToWords } from '../../../common/src/utils/utils';
import * as path from 'path';
const elliptic = initElliptic();
const testSuite = [
{
hash: 'sha1',
curve: 'brainpoolP224r1',
n: 32,
k: 7,
reason: 'when hash is lesser than curve bits',
},
{
hash: 'sha512',
curve: 'brainpoolP256r1',
n: 64,
k: 4,
reason: 'when hash is greater than curve bits',
},
{
hash: 'sha384',
curve: 'brainpoolP384r1',
n: 64,
k: 6,
reason: 'when hash bits are the same as curve bits',
},
];
describe('ecdsa', () => {
testSuite.forEach(({ hash, curve, n, k, reason }) => {
const message = crypto.randomBytes(32);
(
[
[true, 'should verify correctly'],
[false, 'should not verify correctly'],
] as [boolean, string][]
).forEach(([shouldVerify, shouldVerifyReason]) => {
describe(shouldVerifyReason, function () {
this.timeout(0);
const inputs = sign(message, curve, hash, k, n);
if (!shouldVerify) {
inputs.hashParsed.map((x) => 0);
}
it(reason, async () => {
const circuit = await wasmTester(
path.join(__dirname, `../../circuits/tests/utils/ecdsa/test_${curve}.circom`),
{
include: ['node_modules', './node_modules/@zk-kit/binary-merkle-root.circom/src'],
}
);
try {
const witness = await circuit.calculateWitness(inputs);
await circuit.checkConstraints(witness);
if (!shouldVerify) {
throw new Error('Test failed: Invalid signature was verified.');
}
} catch (error) {
if (shouldVerify) {
throw new Error('Test failed: Valid signature was not verified.');
}
}
});
});
});
});
});
function sign(message: Uint8Array, curve: string, hash: string, n: number, k: number) {
const ec = new elliptic.ec(curve);
const key = ec.genKeyPair();
const messageHash = crypto.createHash(hash).update(message).digest();
const signature = key.sign(messageHash, 'hex');
const pubkey = key.getPublic();
const hashParsed = [];
Array.from(messageHash).forEach((x) =>
hashParsed.push(...x.toString(2).padStart(8, '0').split(''))
);
return {
signature: [
...splitToWords(BigInt(signature.r), k, n),
...splitToWords(BigInt(signature.s), k, n),
],
pubKey: [
splitToWords(BigInt(pubkey.getX().toString()), k, n),
splitToWords(BigInt(pubkey.getY().toString()), k, n),
],
hashParsed,
};
}