Skip to content

Commit

Permalink
Merge pull request #159 from zk-passport/sha1-with-ecdsa
Browse files Browse the repository at this point in the history
ECDSA With SHA1 & SHA256 Support
  • Loading branch information
remicolin authored Jul 31, 2024
2 parents 970cae3 + 5846b36 commit ce89661
Show file tree
Hide file tree
Showing 28 changed files with 115,679 additions and 1,946 deletions.
86 changes: 86 additions & 0 deletions circuits/circuits/register/register_ecdsaWithSHA1Encryption.circom
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
pragma circom 2.1.5;

include "circomlib/circuits/poseidon.circom";
include "@zk-email/circuits/utils/bytes.circom";
include "./verifier/passport_verifier_ecdsaWithSHA1Encryption.circom";
include "binary-merkle-root.circom";
include "../utils/splitSignalsToWords.circom";

template Register_ecdsaWithSHA1Encryption(n, k, max_datahashes_bytes, nLevels, signatureAlgorithm) {
signal input secret;
signal input mrz[93];
signal input dg1_hash_offset;
signal input econtent[max_datahashes_bytes];
signal input datahashes_padded_length;
signal input signed_attributes[92];

signal input signature_r[k]; // ECDSA signature component r
signal input signature_s[k]; // ECDSA signature component s
signal input dsc_modulus[2][k]; // Public Key (split into Qx and Qy)

signal input dsc_secret;
signal input attestation_id;

// Hash DSC modulus and signature components
// Poseidon(dsc_modulus[0][0], dsc_modulus[0][1], ..., dsc_modulus[0][5])
component poseidon_hasher_dsc_modules_x = Poseidon(6);
component poseidon_hasher_dsc_modules_y = Poseidon(6);

// Poseidon(signature_r[0], signature_r[1], ..., signature_r[5])
component poseidon_hasher_signature_r = Poseidon(6);
component poseidon_hasher_signature_s = Poseidon(6);

for (var i = 0; i < k; i++) {
poseidon_hasher_dsc_modules_x.inputs[i] <== dsc_modulus[0][i];
poseidon_hasher_dsc_modules_y.inputs[i] <== dsc_modulus[1][i];
poseidon_hasher_signature_r.inputs[i] <== signature_r[i];
poseidon_hasher_signature_s.inputs[i] <== signature_s[i];
}

component dsc_commitment_hasher = Poseidon(3);
component nullifier_hasher = Poseidon(3);
component leaf_hasher = Poseidon(3);

dsc_commitment_hasher.inputs[0] <== dsc_secret;
dsc_commitment_hasher.inputs[1] <== poseidon_hasher_dsc_modules_x.out;
dsc_commitment_hasher.inputs[2] <== poseidon_hasher_dsc_modules_y.out;

nullifier_hasher.inputs[0] <== secret;
nullifier_hasher.inputs[1] <== poseidon_hasher_signature_r.out;
nullifier_hasher.inputs[2] <== poseidon_hasher_signature_s.out;

leaf_hasher.inputs[0] <== signatureAlgorithm;
leaf_hasher.inputs[1] <== poseidon_hasher_dsc_modules_x.out;
leaf_hasher.inputs[2] <== poseidon_hasher_dsc_modules_y.out;

signal output blinded_dsc_commitment <== dsc_commitment_hasher.out;
signal output nullifier <== nullifier_hasher.out;

// Verify passport validity
component PV = PassportVerifier_ecdsaWithSHA1Encryption(n, k, max_datahashes_bytes);
PV.mrz <== mrz;
PV.dg1_hash_offset <== dg1_hash_offset;
PV.dataHashes <== econtent;
PV.datahashes_padded_length <== datahashes_padded_length;
PV.eContentBytes <== signed_attributes;
PV.dsc_modulus <== dsc_modulus;
PV.signature_r <== signature_r;
PV.signature_s <== signature_s;

// signature is valid
PV.result === 1;

// Generate the commitment
component poseidon_hasher = Poseidon(6);
poseidon_hasher.inputs[0] <== secret;
poseidon_hasher.inputs[1] <== attestation_id;
poseidon_hasher.inputs[2] <== leaf_hasher.out;

signal mrz_packed[3] <== PackBytes(93)(mrz);
for (var i = 0; i < 3; i++) {
poseidon_hasher.inputs[i + 3] <== mrz_packed[i];
}
signal output commitment <== poseidon_hasher.out;
}

component main { public [ attestation_id ] } = Register_ecdsaWithSHA1Encryption(43, 6, 320, 16, 7);
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
pragma circom 2.1.5;

include "circomlib/circuits/poseidon.circom";
include "@zk-email/circuits/utils/bytes.circom";
include "./verifier/passport_verifier_ecdsaWithSHA256Encryption.circom";
include "binary-merkle-root.circom";
include "../utils/splitSignalsToWords.circom";

template Register_ecdsaWithSHA256Encryption(n, k, max_datahashes_bytes, nLevels, signatureAlgorithm) {
signal input secret;
signal input mrz[93];
signal input dg1_hash_offset;
signal input econtent[max_datahashes_bytes];
signal input datahashes_padded_length;
signal input signed_attributes[104];

signal input signature_r[k]; // ECDSA signature component r
signal input signature_s[k]; // ECDSA signature component s
signal input dsc_modulus[2][k]; // Public Key (split into Qx and Qy)

signal input dsc_secret;
signal input attestation_id;

// Hash DSC modulus and signature components
// Poseidon(dsc_modulus[0][0], dsc_modulus[0][1], ..., dsc_modulus[0][5])
component poseidon_hasher_dsc_modules_x = Poseidon(6);
component poseidon_hasher_dsc_modules_y = Poseidon(6);

// Poseidon(signature_r[0], signature_r[1], ..., signature_r[5])
component poseidon_hasher_signature_r = Poseidon(6);
component poseidon_hasher_signature_s = Poseidon(6);

for (var i = 0; i < k; i++) {
poseidon_hasher_dsc_modules_x.inputs[i] <== dsc_modulus[0][i];
poseidon_hasher_dsc_modules_y.inputs[i] <== dsc_modulus[1][i];
poseidon_hasher_signature_r.inputs[i] <== signature_r[i];
poseidon_hasher_signature_s.inputs[i] <== signature_s[i];
}

component dsc_commitment_hasher = Poseidon(3);
component nullifier_hasher = Poseidon(3);
component leaf_hasher = Poseidon(3);

dsc_commitment_hasher.inputs[0] <== dsc_secret;
dsc_commitment_hasher.inputs[1] <== poseidon_hasher_dsc_modules_x.out;
dsc_commitment_hasher.inputs[2] <== poseidon_hasher_dsc_modules_y.out;

nullifier_hasher.inputs[0] <== secret;
nullifier_hasher.inputs[1] <== poseidon_hasher_signature_r.out;
nullifier_hasher.inputs[2] <== poseidon_hasher_signature_s.out;

leaf_hasher.inputs[0] <== signatureAlgorithm;
leaf_hasher.inputs[1] <== poseidon_hasher_dsc_modules_x.out;
leaf_hasher.inputs[2] <== poseidon_hasher_dsc_modules_y.out;

signal output blinded_dsc_commitment <== dsc_commitment_hasher.out;
signal output nullifier <== nullifier_hasher.out;

// Verify passport validity
component PV = PassportVerifier_ecdsaWithSHA256Encryption(n, k, max_datahashes_bytes);
PV.mrz <== mrz;
PV.dg1_hash_offset <== dg1_hash_offset;
PV.dataHashes <== econtent;
PV.datahashes_padded_length <== datahashes_padded_length;
PV.eContentBytes <== signed_attributes;
PV.dsc_modulus <== dsc_modulus;
PV.signature_r <== signature_r;
PV.signature_s <== signature_s;

// signature is valid
PV.result === 1;

// Generate the commitment
component poseidon_hasher = Poseidon(6);
poseidon_hasher.inputs[0] <== secret;
poseidon_hasher.inputs[1] <== attestation_id;
poseidon_hasher.inputs[2] <== leaf_hasher.out;

signal mrz_packed[3] <== PackBytes(93)(mrz);
for (var i = 0; i < 3; i++) {
poseidon_hasher.inputs[i + 3] <== mrz_packed[i];
}
signal output commitment <== poseidon_hasher.out;
}

component main { public [ attestation_id ] } = Register_ecdsaWithSHA256Encryption(43, 6, 320, 16, 8);
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
pragma circom 2.1.5;

include "@zk-email/circuits/utils/bytes.circom";
include "../../utils/Sha1BytesStatic.circom";
include "../../utils/Sha1Bytes.circom";
include "../../utils/rsaPkcs1.circom";
include "dmpierre/sha1-circom/circuits/sha1.circom";
include "../../utils/circom-ecdsa/ecdsa.circom";

template PassportVerifier_ecdsaWithSHA1Encryption(n, k, max_datahashes_bytes) {
var hashLen = 20;
var eContentBytesLength = 72 + hashLen; // 92

signal input mrz[93]; // formatted mrz (5 + 88) chars
signal input dg1_hash_offset;
signal input dataHashes[max_datahashes_bytes];

signal input datahashes_padded_length;
signal input eContentBytes[eContentBytesLength];

signal input dsc_modulus[2][k]; // Public Key (split into Qx and Qy)

signal input signature_r[k]; // ECDSA signature component r
signal input signature_s[k]; // ECDSA signature component s

// compute sha1 of formatted mrz
signal mrzSha[160] <== Sha1BytesStatic(93)(mrz);

// mrzSha_bytes: list of 32 Bits2Num
component mrzSha_bytes[hashLen];

// cast the 160 bits from mrzSha into a list of 20 bytes
for (var i = 0; i < hashLen; i++) {
mrzSha_bytes[i] = Bits2Num(8);

for (var j = 0; j < 8; j++) {
mrzSha_bytes[i].in[7 - j] <== mrzSha[i * 8 + j];
}
}

// assert mrz_hash equals the one extracted from dataHashes input (bytes dg1_hash_offset to dg1_hash_offset + hashLen)
signal dg1Hash[hashLen] <== SelectSubArray(max_datahashes_bytes, hashLen)(dataHashes, dg1_hash_offset, hashLen);
for(var i = 0; i < hashLen; i++) {
dg1Hash[i] === mrzSha_bytes[i].out;
}

// hash dataHashes dynamically
signal dataHashesSha[160] <== Sha1Bytes(max_datahashes_bytes)(dataHashes, datahashes_padded_length);

// get output of dataHashes into bytes to check against eContent
component dataHashesSha_bytes[hashLen];
for (var i = 0; i < hashLen; i++) {
dataHashesSha_bytes[i] = Bits2Num(8);
for (var j = 0; j < 8; j++) {
dataHashesSha_bytes[i].in[7 - j] <== dataHashesSha[i * 8 + j];
}
}

// assert dataHashesSha is in eContentBytes in range bytes 72 to 92
for(var i = 0; i < hashLen; i++) {
eContentBytes[eContentBytesLength - hashLen + i] === dataHashesSha_bytes[i].out;
}

// hash eContentBytes
signal eContentSha[160] <== Sha1BytesStatic(eContentBytesLength)(eContentBytes);

// get output of eContentBytes sha1 into k chunks of n bits each
var msg_len = (160 + n) \ n;

//eContentHash: list of length 160/n +1 of components of n bits
component eContentHash[msg_len];
for (var i = 0; i < msg_len; i++) {
eContentHash[i] = Bits2Num(n);
}

for (var i = 0; i < 160; i++) {
eContentHash[i \ n].in[i % n] <== eContentSha[159 - i];
}

for (var i = 160; i < n * msg_len; i++) {
eContentHash[i \ n].in[i % n] <== 0;
}

// 43 * 6 = 258;
signal msgHash[6];
for(var i = 0; i < 4; i++) {
msgHash[i] <== eContentHash[i].out;
}
msgHash[4] <== 0;
msgHash[5] <== 0;

// 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 <== dsc_modulus;

signal output result <== ecdsa_verify.result;
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
pragma circom 2.1.5;

include "@zk-email/circuits/utils/bytes.circom";
include "../../utils/circom-ecdsa/ecdsa.circom";
include "../../utils/Sha256BytesStatic.circom";
include "@zk-email/circuits/lib/sha.circom";

template PassportVerifier_ecdsaWithSHA256Encryption(n, k, max_datahashes_bytes) {
var hashLen = 32;
var eContentBytesLength = 72 + hashLen; // 104

signal input mrz[93]; // formatted mrz (5 + 88) chars
signal input dg1_hash_offset;
signal input dataHashes[max_datahashes_bytes];

signal input datahashes_padded_length;
signal input eContentBytes[eContentBytesLength];

signal input dsc_modulus[2][k]; // Public Key (split into Qx and Qy)

signal input signature_r[k]; // ECDSA signature component r
signal input signature_s[k]; // ECDSA signature component s

// compute sha256 of formatted mrz
signal mrzSha[256] <== Sha256BytesStatic(93)(mrz);

// mrzSha_bytes: list of 32 Bits2Num
component mrzSha_bytes[hashLen];

// cast the 256 bits from mrzSha into a list of 32 bytes
for (var i = 0; i < hashLen; i++) {
mrzSha_bytes[i] = Bits2Num(8);

for (var j = 0; j < 8; j++) {
mrzSha_bytes[i].in[7 - j] <== mrzSha[i * 8 + j];
}
}

// assert mrz_hash equals the one extracted from dataHashes input (bytes dg1_hash_offset to dg1_hash_offset + hashLen)
signal dg1Hash[hashLen] <== SelectSubArray(max_datahashes_bytes, hashLen)(dataHashes, dg1_hash_offset, hashLen);
for(var i = 0; i < hashLen; i++) {
dg1Hash[i] === mrzSha_bytes[i].out;
}

// hash dataHashes dynamically
signal dataHashesSha[256] <== Sha256Bytes(max_datahashes_bytes)(dataHashes, datahashes_padded_length);

// get output of dataHashes into bytes to check against eContent
component dataHashesSha_bytes[hashLen];
for (var i = 0; i < hashLen; i++) {
dataHashesSha_bytes[i] = Bits2Num(8);
for (var j = 0; j < 8; j++) {
dataHashesSha_bytes[i].in[7 - j] <== dataHashesSha[i * 8 + j];
}
}

// assert dataHashesSha is in eContentBytes in range bytes 72 to 92
for(var i = 0; i < hashLen; i++) {
eContentBytes[eContentBytesLength - hashLen + i] === dataHashesSha_bytes[i].out;
}

// hash eContentBytes
signal eContentSha[256] <== Sha256BytesStatic(104)(eContentBytes);

// get output of eContentBytes sha256 into k chunks of n bits each
var msg_len = (256 + n) \ n;

//eContentHash: list of length 256/n +1 of components of n bits
component eContentHash[msg_len];
for (var i = 0; i < msg_len; i++) {
eContentHash[i] = Bits2Num(n);
}

for (var i = 0; i < 256; i++) {
eContentHash[i \ n].in[i % n] <== eContentSha[255 - i];
}

for (var i = 256; i < n * msg_len; i++) {
eContentHash[i \ n].in[i % n] <== 0;
}


// 43 * 6 = 258;
signal msgHash[6];
for(var i = 0; i < msg_len; i++) {
msgHash[i] <== eContentHash[i].out;
}

// 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 <== dsc_modulus;

signal output result <== ecdsa_verify.result;
}

Loading

0 comments on commit ce89661

Please sign in to comment.