diff --git a/circuits/README.md b/circuits/README.md index 103cff607..0599ddd81 100644 --- a/circuits/README.md +++ b/circuits/README.md @@ -41,7 +41,7 @@ Proof of Passport currently supports the following sig/hash algorithms: - [x] sha256WithRSAEncryption - [x] sha1WithRSAEncryption -- [ ] rsassaPss (under development) +- [x] sha256WithRSASSAPSS - [ ] ecdsa-with-SHA384 - [ ] ecdsa-with-SHA1 - [ ] ecdsa-with-SHA256 diff --git a/circuits/circuits/passport_verifier_sha256WithRSASSAPSS_65537.circom b/circuits/circuits/passport_verifier_sha256WithRSASSAPSS_65537.circom new file mode 100644 index 000000000..18cc7e8c4 --- /dev/null +++ b/circuits/circuits/passport_verifier_sha256WithRSASSAPSS_65537.circom @@ -0,0 +1,75 @@ +pragma circom 2.1.5; + +// include "@zk-email/circuits/lib/rsa.circom"; +include "@zk-email/circuits/utils/bytes.circom"; +include "@zk-email/circuits/lib/sha.circom"; +include "@zk-email/circuits/utils/array.circom"; +include "./utils/Sha256BytesStatic.circom"; +include "./utils/RSASSAPSS.circom"; + + +template PassportVerifier_sha256WithRSASSAPSS_65537(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]; + + // pubkey that signed the passport + signal input pubkey[k]; + + // signature of the passport + signal input signature[k]; + + // 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 sha256 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 104 + for(var i = 0; i < hashLen; i++) { + eContentBytes[eContentBytesLength - hashLen + i] === dataHashesSha_bytes[i].out; + } + + // decode signature to get encoded message + component rsaDecode = RSASSAPSS_Decode(n, k); + rsaDecode.signature <== signature; + rsaDecode.modulus <== pubkey; + signal encodedMessage[(n*k) \ 8] <== rsaDecode.eM; + + // verify eContent signature + component rsaVerify = RSASSAPSSVerify_SHA256(n*k, eContentBytesLength); + rsaVerify.eM <== encodedMessage; + rsaVerify.message <== eContentBytes; +} \ No newline at end of file diff --git a/circuits/circuits/register_sha256WithRSASSAPSS_65537.circom b/circuits/circuits/register_sha256WithRSASSAPSS_65537.circom new file mode 100644 index 000000000..c8c4c9e40 --- /dev/null +++ b/circuits/circuits/register_sha256WithRSASSAPSS_65537.circom @@ -0,0 +1,61 @@ +pragma circom 2.1.5; + +include "circomlib/circuits/poseidon.circom"; +include "@zk-email/circuits/utils/bytes.circom"; +include "./passport_verifier_sha256WithRSASSAPSS_65537.circom"; +include "./utils/chunk_data.circom"; +include "./utils/compute_pubkey_leaf.circom"; +include "binary-merkle-root.circom"; + +template register_sha256WithRSASSAPSS_65537(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[k]; + + signal input pubkey[k]; + signal input merkle_root; + signal input path[nLevels]; + signal input siblings[nLevels]; + + signal input attestation_id; + + // Verify inclusion of the pubkey in the pubkey tree + signal leaf <== ComputePubkeyLeaf(n, k, signatureAlgorithm)(pubkey); + signal computed_merkle_root <== BinaryMerkleRoot(nLevels)(leaf, nLevels, path, siblings); + merkle_root === computed_merkle_root; + + // Verify passport validity + component PV = PassportVerifier_sha256WithRSASSAPSS_65537(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.pubkey <== pubkey; + PV.signature <== signature; + + // Generate the commitment + 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]; + } + signal output commitment <== poseidon_hasher.out; + + // Generate the nullifier + var chunk_size = 11; // Since ceil(32 / 3) in integer division is 11 + signal chunked_signature[chunk_size] <== ChunkData(n, k, chunk_size)(signature); + signal output nullifier <== Poseidon(chunk_size)(chunked_signature); +} + +// We hardcode 1 here for sha256WithRSAEncryption_65537 +component main { public [ merkle_root, attestation_id ] } = register_sha256WithRSASSAPSS_65537(64, 32, 320, 16, 4); diff --git a/circuits/circuits/utils/Mgf1Sha256.circom b/circuits/circuits/utils/Mgf1Sha256.circom index ff1509aaa..19a71d6aa 100644 --- a/circuits/circuits/utils/Mgf1Sha256.circom +++ b/circuits/circuits/utils/Mgf1Sha256.circom @@ -34,7 +34,7 @@ template Mgf1Sha256(seedLen, maskLen) { //in bytes for (var j = 0; j < 32; j++) { //concat seed and counter - concated[seedLenBits + j] = num2Bits[i].out[j]; + concated[seedLenBits + j] = num2Bits[i].out[31-j]; } sha256[i].in <== concated; diff --git a/circuits/circuits/utils/RSASSAPSS.circom b/circuits/circuits/utils/RSASSAPSS.circom new file mode 100644 index 000000000..a83920c4e --- /dev/null +++ b/circuits/circuits/utils/RSASSAPSS.circom @@ -0,0 +1,192 @@ +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]; + signal output eM[(n*k)\8]; //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(832); + 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 { + circuit = await wasm_tester( + path.join(__dirname, "../circuits/register_sha256WithRSASSAPSS_65537.circom"), + { + include: [ + "node_modules", + "node_modules/@zk-email/circuits/helpers/sha.circom", + "./node_modules/@zk-kit/binary-merkle-root.circom/src", + "./node_modules/circomlib/circuits" + ] + }, + ); + + const secret = BigInt(Math.floor(Math.random() * Math.pow(2, 254))).toString(); + console.log("secret", secret); + + const attestation_name = "E-PASSPORT"; + attestation_id = poseidon1([ + BigInt(Buffer.from(attestation_name).readUIntBE(0, 6)) + ]).toString(); + + inputs = generateCircuitInputsRegister( + secret, + attestation_id, + passportData, + [passportData], + ); + }); + + it("should compile and load the circuit", async function () { + expect(circuit).to.not.be.undefined; + }); + + it("should calculate the witness with correct inputs", async function () { + console.time('calculateWitness') + const w = await circuit.calculateWitness(inputs); + console.timeEnd('calculateWitness') + await circuit.checkConstraints(w); + + console.log("nullifier", (await circuit.getOutput(w, ["nullifier"])).nullifier); + + const commitment_circom = (await circuit.getOutput(w, ["commitment"])).commitment; + + const mrz_bytes = packBytes(inputs.mrz); + const commitment_bytes = poseidon6([ + inputs.secret[0], + attestation_id, + getLeaf({ + signatureAlgorithm: passportData.signatureAlgorithm, + modulus: passportData.pubKey.modulus, + exponent: passportData.pubKey.exponent + }), + mrz_bytes[0], + mrz_bytes[1], + mrz_bytes[2] + ]); + const commitment_js = commitment_bytes.toString(); + console.log('commitment_js', commitment_js) + console.log('commitment_circom', commitment_circom) + expect(commitment_circom).to.be.equal(commitment_js); + }); + + it("should fail to calculate witness with invalid mrz", async function () { + try { + const invalidInputs = { + ...inputs, + mrz: Array(93).fill(0).map(byte => BigInt(byte).toString()) + } + await circuit.calculateWitness(invalidInputs); + expect.fail("Expected an error but none was thrown."); + } catch (error) { + expect(error.message).to.include("Assert Failed"); + } + }); + + it("should fail to calculate witness with invalid econtent", async function () { + try { + const invalidInputs = { + ...inputs, + econtent: inputs.econtent.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)), + } + await circuit.calculateWitness(invalidInputs); + expect.fail("Expected an error but none was thrown."); + } catch (error) { + expect(error.message).to.include("Assert Failed"); + } + }); + + it("should fail to calculate witness with invalid signature", async function () { + try { + const invalidInputs = { + ...inputs, + signature: inputs.signature.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)), + } + await circuit.calculateWitness(invalidInputs); + expect.fail("Expected an error but none was thrown."); + } catch (error) { + expect(error.message).to.include("Assert Failed"); + } + }); + + it("should fail to calculate witness with invalid merkle root", async function () { + try { + const invalidInputs = { + ...inputs, + merkle_root: inputs.merkle_root.map((byte: string) => String((parseInt(byte, 10) + 1) % 256)), + } + await circuit.calculateWitness(invalidInputs); + expect.fail("Expected an error but none was thrown."); + } catch (error) { + expect(error.message).to.include("Assert Failed"); + } + }); + +}); \ No newline at end of file diff --git a/common/src/constants/constants.ts b/common/src/constants/constants.ts index 509719936..bbbfee759 100644 --- a/common/src/constants/constants.ts +++ b/common/src/constants/constants.ts @@ -17,8 +17,8 @@ export enum SignatureAlgorithm { sha256WithRSAEncryption_65537 = 1, sha256WithRSAEncryption_3 = 2, sha1WithRSAEncryption_65537 = 3, - rsassaPss_65537 = 4, - rsassaPss_3 = 5, + sha256WithRSASSAPSS_65537 = 4, + sha256WithRSASSAPSS_3 = 5, ecdsa_with_SHA384 = 6, ecdsa_with_SHA1 = 7, ecdsa_with_SHA256 = 8, diff --git a/common/src/utils/formatNames.ts b/common/src/utils/formatNames.ts index 10fd0b75f..9ea2baf26 100644 --- a/common/src/utils/formatNames.ts +++ b/common/src/utils/formatNames.ts @@ -56,9 +56,14 @@ export function toStandardName(jmrtdName: string): string { case "SSAwithRSA/PSS": case "RSASSA-PSS": case "rsassa-pss": + case "rsassaPss": + case "rsassapss": + case "rsapss": case "SHA256withRSAandMGF1": case "id-mgf1": - return "rsassaPss"; + return "sha256WithRSASSAPSS"; + // we call it sha256WithRSASSAPSS even if specs says rsassaPss because there are multiple hash functions used + // e.g. Italy uses sha512 with it // added this one for iOS diff --git a/common/src/utils/generateInputs.ts b/common/src/utils/generateInputs.ts index f3b63d047..9c01a4361 100644 --- a/common/src/utils/generateInputs.ts +++ b/common/src/utils/generateInputs.ts @@ -34,7 +34,11 @@ export function generateCircuitInputsRegister( } } - if (!["sha256WithRSAEncryption"].includes(signatureAlgorithm)) { + if (![ + "sha256WithRSAEncryption", + // "sha1WithRSAEncryption", + // "sha256WithRSASSAPSS" + ].includes(signatureAlgorithm)) { console.error(`${signatureAlgorithm} has not been implemented.`); throw new Error(`${signatureAlgorithm} has not been implemented.`); } @@ -86,7 +90,7 @@ export function generateCircuitInputsRegister( return { secret: [secret], mrz: formattedMrz.map(byte => String(byte)), - // dg1_hash_offset: [dg1HashOffset.toString()], uncomment when adding new circuits + // dg1_hash_offset: [dg1HashOffset.toString()], // uncomment when adding new circuits econtent: Array.from(messagePadded).map((x) => x.toString()), datahashes_padded_length: [messagePaddedLen.toString()], signed_attributes: eContent.map(toUnsignedByte).map(byte => String(byte)), diff --git a/common/src/utils/mockPassportData.ts b/common/src/utils/mockPassportData.ts index f51c03288..de7da57f8 100644 --- a/common/src/utils/mockPassportData.ts +++ b/common/src/utils/mockPassportData.ts @@ -102,9 +102,9 @@ export const mockPassportData_sha1WithRSAEncryption_65537 = { "photoBase64": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABjElEQVR42mL8//8/AyUYiBQYmIw3..." } -export const mockPassportData_rsassaPss_65537 = { +export const mockPassportData_sha256WithRSASSAPSS_65537 = { "mrz": "P. +*/ + +pragma solidity >=0.7.0 <0.9.0; + +contract Verifier_register_sha256WithRSASSAPSS_65537 { + // Scalar field size + uint256 constant r = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + // Base field size + uint256 constant q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + // Verification Key data + uint256 constant alphax = 20491192805390485299153009773594534940189261866228447918068658471970481763042; + uint256 constant alphay = 9383485363053290200918347156157836566562967994039712273449902621266178545958; + uint256 constant betax1 = 4252822878758300859123897981450591353533073413197771768651442665752259397132; + uint256 constant betax2 = 6375614351688725206403948262868962793625744043794305715222011528459656738731; + uint256 constant betay1 = 21847035105528745403288232691147584728191162732299865338377159692350059136679; + uint256 constant betay2 = 10505242626370262277552901082094356697409835680220590971873171140371331206856; + uint256 constant gammax1 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; + uint256 constant gammax2 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; + uint256 constant gammay1 = 4082367875863433681332203403145435568316851327593401208105741076214120093531; + uint256 constant gammay2 = 8495653923123431417604973247489272438418190587263600148770280649306958101930; + uint256 constant deltax1 = 2513356704415346673552161314305955270236630903884681993780363069379262270944; + uint256 constant deltax2 = 2561660084047439280986367104146460235004641848496139823103770214612527114094; + uint256 constant deltay1 = 16723398497241255269184728114814174364828886103396228033236460836502696283958; + uint256 constant deltay2 = 18406674030956056018589715181737882583854065581403281200957779094299343047709; + + + uint256 constant IC0x = 6516154797273814180336004176967344307104266312158582012392061120100851162901; + uint256 constant IC0y = 7011411371762049720135394743588784168330811849030613049161469013902729963131; + + uint256 constant IC1x = 7490839539779982688993491760112859705035583352389262038261439790637635773288; + uint256 constant IC1y = 3238512700884050445458358383672608752951570151934557834980826762817490123688; + + uint256 constant IC2x = 15752392097507176707308873155820389988651860920408165055927698306096626444697; + uint256 constant IC2y = 5976112676946398219828298974913973178576215643860049436785492553204522425577; + + uint256 constant IC3x = 16910614110048622389326331015743537879782997691696669081560314808181162496550; + uint256 constant IC3y = 115464461926218779265699564364532387878381884190664829188695820794383378419; + + uint256 constant IC4x = 18688184865694347247300054969641975141367589743774271614757416410176028471659; + uint256 constant IC4y = 19475466214309651477314430752520843379694799003033527395901992555787956235432; + + + // Memory data + uint16 constant pVk = 0; + uint16 constant pPairing = 128; + + uint16 constant pLastMem = 896; + + function verifyProof(uint[2] calldata _pA, uint[2][2] calldata _pB, uint[2] calldata _pC, uint[4] calldata _pubSignals) public view returns (bool) { + assembly { + function checkField(v) { + if iszero(lt(v, q)) { + mstore(0, 0) + return(0, 0x20) + } + } + + // G1 function to multiply a G1 value(x,y) to value in an address + function g1_mulAccC(pR, x, y, s) { + let success + let mIn := mload(0x40) + mstore(mIn, x) + mstore(add(mIn, 32), y) + mstore(add(mIn, 64), s) + + success := staticcall(sub(gas(), 2000), 7, mIn, 96, mIn, 64) + + if iszero(success) { + mstore(0, 0) + return(0, 0x20) + } + + mstore(add(mIn, 64), mload(pR)) + mstore(add(mIn, 96), mload(add(pR, 32))) + + success := staticcall(sub(gas(), 2000), 6, mIn, 128, pR, 64) + + if iszero(success) { + mstore(0, 0) + return(0, 0x20) + } + } + + function checkPairing(pA, pB, pC, pubSignals, pMem) -> isOk { + let _pPairing := add(pMem, pPairing) + let _pVk := add(pMem, pVk) + + mstore(_pVk, IC0x) + mstore(add(_pVk, 32), IC0y) + + // Compute the linear combination vk_x + + g1_mulAccC(_pVk, IC1x, IC1y, calldataload(add(pubSignals, 0))) + + g1_mulAccC(_pVk, IC2x, IC2y, calldataload(add(pubSignals, 32))) + + g1_mulAccC(_pVk, IC3x, IC3y, calldataload(add(pubSignals, 64))) + + g1_mulAccC(_pVk, IC4x, IC4y, calldataload(add(pubSignals, 96))) + + + // -A + mstore(_pPairing, calldataload(pA)) + mstore(add(_pPairing, 32), mod(sub(q, calldataload(add(pA, 32))), q)) + + // B + mstore(add(_pPairing, 64), calldataload(pB)) + mstore(add(_pPairing, 96), calldataload(add(pB, 32))) + mstore(add(_pPairing, 128), calldataload(add(pB, 64))) + mstore(add(_pPairing, 160), calldataload(add(pB, 96))) + + // alpha1 + mstore(add(_pPairing, 192), alphax) + mstore(add(_pPairing, 224), alphay) + + // beta2 + mstore(add(_pPairing, 256), betax1) + mstore(add(_pPairing, 288), betax2) + mstore(add(_pPairing, 320), betay1) + mstore(add(_pPairing, 352), betay2) + + // vk_x + mstore(add(_pPairing, 384), mload(add(pMem, pVk))) + mstore(add(_pPairing, 416), mload(add(pMem, add(pVk, 32)))) + + + // gamma2 + mstore(add(_pPairing, 448), gammax1) + mstore(add(_pPairing, 480), gammax2) + mstore(add(_pPairing, 512), gammay1) + mstore(add(_pPairing, 544), gammay2) + + // C + mstore(add(_pPairing, 576), calldataload(pC)) + mstore(add(_pPairing, 608), calldataload(add(pC, 32))) + + // delta2 + mstore(add(_pPairing, 640), deltax1) + mstore(add(_pPairing, 672), deltax2) + mstore(add(_pPairing, 704), deltay1) + mstore(add(_pPairing, 736), deltay2) + + + let success := staticcall(sub(gas(), 2000), 8, _pPairing, 768, _pPairing, 0x20) + + isOk := and(success, mload(_pPairing)) + } + + let pMem := mload(0x40) + mstore(0x40, add(pMem, pLastMem)) + + // Validate that all evaluations ∈ F + + checkField(calldataload(add(_pubSignals, 0))) + + checkField(calldataload(add(_pubSignals, 32))) + + checkField(calldataload(add(_pubSignals, 64))) + + checkField(calldataload(add(_pubSignals, 96))) + + checkField(calldataload(add(_pubSignals, 128))) + + + // Validate all evaluations + let isValid := checkPairing(_pA, _pB, _pC, _pubSignals, pMem) + + mstore(0, isValid) + return(0, 0x20) + } + } + } diff --git a/contracts/test/RegisterAndDisclose.ts b/contracts/test/RegisterAndDisclose.ts index 6a38513f7..b59261dba 100644 --- a/contracts/test/RegisterAndDisclose.ts +++ b/contracts/test/RegisterAndDisclose.ts @@ -1,7 +1,7 @@ import { expect, assert } from "chai"; import { ethers } from "hardhat"; // import { describe } from "mocha"; -import { mockPassportData_sha1WithRSAEncryption_65537, mockPassportData_sha256WithRSAEncryption_65537 } from "../../common/src/utils/mockPassportData"; +import { mockPassportData_sha256WithRSASSAPSS_65537, mockPassportData_sha1WithRSAEncryption_65537, mockPassportData_sha256WithRSAEncryption_65537 } from "../../common/src/utils/mockPassportData"; import { countryCodes, PASSPORT_ATTESTATION_ID, SignatureAlgorithm } from "../../common/src/constants/constants"; import { formatRoot } from "../../common/src/utils/utils"; import { groth16 } from 'snarkjs' @@ -29,7 +29,6 @@ describe("Proof of Passport - Contracts - Register & Disclose flow", function () this.timeout(0); let proof, publicSignals - // Paths const register_circuits: RegisterCircuitArtifacts = { sha256WithRSAEncryption_65537: { @@ -41,6 +40,11 @@ describe("Proof of Passport - Contracts - Register & Disclose flow", function () wasm: "../circuits/build/register_sha1WithRSAEncryption_65537_js/register_sha1WithRSAEncryption_65537.wasm", zkey: "../circuits/build/register_sha1WithRSAEncryption_65537_final.zkey", vkey: "../circuits/build/register_sha1WithRSAEncryption_65537_vkey.json" + }, + sha256WithRSASSAPSS_65537: { + wasm: "../circuits/build/register_sha256WithRSASSAPSS_65537_js/register_sha256WithRSASSAPSS_65537.wasm", + zkey: "../circuits/build/register_sha256WithRSASSAPSS_65537_final.zkey", + vkey: "../circuits/build/register_sha256WithRSASSAPSS_65537_vkey.json" } } @@ -53,7 +57,7 @@ describe("Proof of Passport - Contracts - Register & Disclose flow", function () let owner, otherAccount, thirdAccount: Signer; let imt: LeanIMT; - let bitmap, scope, user_address, majority, user_identifier, current_date, input_disclose: any; + let bitmap, scope, user_address, majority, input_disclose: any; let proof_disclose, publicSignals_disclose, proof_result_disclose, vkey_disclose, verified_disclose: any, rawCallData_disclose, parsedCallData_disclose: any[], formattedCallData_disclose: any; let secret: string = BigInt(0).toString(); let attestation_id: string = PASSPORT_ATTESTATION_ID; @@ -78,6 +82,12 @@ describe("Proof of Passport - Contracts - Register & Disclose flow", function () mockPassportData_sha1WithRSAEncryption_65537, ); + register_circuits.sha256WithRSASSAPSS_65537.inputs = generateCircuitInputsRegister( + secret, + attestation_id, + mockPassportData_sha256WithRSASSAPSS_65537, + ); + /*** Deploy contracts ***/ await deployContracts(); @@ -110,6 +120,12 @@ describe("Proof of Passport - Contracts - Register & Disclose flow", function () register_circuits.sha1WithRSAEncryption_65537.verifier = verifier_register_sha1WithRSAEncryption_65537; console.log('\x1b[34m%s\x1b[0m', `Verifier_register_sha1WithRSAEncryption_65537 deployed to ${verifier_register_sha1WithRSAEncryption_65537.target}`); + const Verifier_register_sha256WithRSASSAPSS_65537 = await ethers.getContractFactory("Verifier_register_sha256WithRSASSAPSS_65537"); + const verifier_register_sha256WithRSASSAPSS_65537 = await Verifier_register_sha256WithRSASSAPSS_65537.deploy(deployOptions); + await verifier_register_sha256WithRSASSAPSS_65537.waitForDeployment(); + register_circuits.sha256WithRSASSAPSS_65537.verifier = verifier_register_sha256WithRSASSAPSS_65537; + console.log('\x1b[34m%s\x1b[0m', `Verifier_register_sha256WithRSASSAPSS_65537 deployed to ${verifier_register_sha256WithRSASSAPSS_65537.target}`); + const Formatter = await ethers.getContractFactory("Formatter"); formatter = await Formatter.deploy(deployOptions); await formatter.waitForDeployment(); @@ -141,9 +157,9 @@ describe("Proof of Passport - Contracts - Register & Disclose flow", function () await verifier_disclose.waitForDeployment(); console.log('\x1b[34m%s\x1b[0m', `Verifier_disclose deployed to ${verifier_disclose.target}`); - // for indexes, see SignatureAlgorithm object in common/src/constants/constants.ts - await register.addSignatureAlgorithm(1, verifier_register_sha256WithRSAEncryption_65537.target); - await register.addSignatureAlgorithm(3, verifier_register_sha1WithRSAEncryption_65537.target); + await register.addSignatureAlgorithm(SignatureAlgorithm["sha256WithRSAEncryption_65537"], verifier_register_sha256WithRSAEncryption_65537.target); + await register.addSignatureAlgorithm(SignatureAlgorithm["sha1WithRSAEncryption_65537"], verifier_register_sha1WithRSAEncryption_65537.target); + await register.addSignatureAlgorithm(SignatureAlgorithm["sha256WithRSASSAPSS_65537"], verifier_register_sha256WithRSASSAPSS_65537.target); const SBT = await ethers.getContractFactory("SBT"); sbt = await SBT.deploy( @@ -172,7 +188,7 @@ describe("Proof of Passport - Contracts - Register & Disclose flow", function () /*** Register flow ***/ describe("Proof of Passport - Register flow", function () { - const sigAlgNames = ['sha256WithRSAEncryption_65537', 'sha1WithRSAEncryption_65537'] + const sigAlgNames = ['sha256WithRSAEncryption_65537', 'sha1WithRSAEncryption_65537', 'sha256WithRSASSAPSS_65537'] before(async function () { await Promise.all(sigAlgNames.map(async (sigAlgName) => { @@ -216,7 +232,12 @@ describe("Proof of Passport - Contracts - Register & Disclose flow", function () it(`Verifier contract verifies a correct proof - Register - ${sigAlgName}`, async function () { expect( - await sigAlgArtifacts.verifier.verifyProof(sigAlgArtifacts.parsedCallData[0], sigAlgArtifacts.parsedCallData[1], sigAlgArtifacts.parsedCallData[2], sigAlgArtifacts.parsedCallData[3]) + await sigAlgArtifacts.verifier.verifyProof( + sigAlgArtifacts.parsedCallData[0], + sigAlgArtifacts.parsedCallData[1], + sigAlgArtifacts.parsedCallData[2], + sigAlgArtifacts.parsedCallData[3] + ) ).to.be.true; });