mirror of
https://github.com/selfxyz/self.git
synced 2026-04-05 03:00:53 -04:00
Merge branch 'dev' of https://github.com/zk-passport/openpassport into feat/docs-smt
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 ."
|
||||
|
||||
100
circuits/tests/utils/ecdsa.test.ts
Normal file
100
circuits/tests/utils/ecdsa.test.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user