From 2aae586090479d0b01cb421c2c9a4178b1fc5a5a Mon Sep 17 00:00:00 2001 From: yash1io Date: Sun, 25 Aug 2024 16:18:21 +0530 Subject: [PATCH 01/14] add VerifyCertChain --- .gitmodules | 3 ++ circuits/add.circom | 10 ----- circuits/attestation.circom | 40 ++++++++++++++++++++ circuits/hash-circuits | 1 + package.json | 1 + src/index.ts | 18 +++++++++ tests/add.test.ts | 20 ---------- tests/attestation.test.ts | 68 ++++++++++++++++++++++++++++++++++ yarn.lock | 73 ++++++++++++++++++++++++++++++++++++- 9 files changed, 203 insertions(+), 31 deletions(-) create mode 100644 .gitmodules delete mode 100644 circuits/add.circom create mode 100644 circuits/attestation.circom create mode 160000 circuits/hash-circuits delete mode 100644 tests/add.test.ts create mode 100644 tests/attestation.test.ts diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..568d5b5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "circuits/hash-circuits"] + path = circuits/hash-circuits + url = https://github.com/bkomuves/hash-circuits/ diff --git a/circuits/add.circom b/circuits/add.circom deleted file mode 100644 index 5933ecf..0000000 --- a/circuits/add.circom +++ /dev/null @@ -1,10 +0,0 @@ -pragma circom 2.1.5; - -template Add() { - signal input a; - signal input b; - signal output out; - - out <== a + b; -} - diff --git a/circuits/attestation.circom b/circuits/attestation.circom new file mode 100644 index 0000000..ff1ff73 --- /dev/null +++ b/circuits/attestation.circom @@ -0,0 +1,40 @@ +pragma circom 2.1.5; + +import "@crema-labs/ecdsa-p384-circom/circuits/ecdsa.circom"; +import "./hash-circuit/circuits/sha2/sha384/sha384_hash_bytes.circom"; + +template VerifyCertChain(m){ + signal input r[3][8]; + signal input s[3][8]; + + signal input TBS1Size; + signal input TBS2Size; + signal input TBS3Size; + signal input TBSData[3][m]; + + signal input PubKeys[3][2][8]; + + signal output out; + + signal status[3]; + + component hash[2]; + component ecdsa[3]; + + for (var i = 0; i < 3; i++) { + has[i] = Sha384_hash_bytes_digest(m); + ecdsa[i] = ECDSAVerifyNoPubkeyCheck(48, 8); + + hash[i].inp_bytes <== TBSData[i]; + ecdsa[i].msghash <== hash[i].hash_bytes; + ecdsa[i].r <== r[i]; + ecdsa[i].s <== s[i]; + ecdsa[i].pubkey <== PubKeys[i]; + ecdsa[i].result ==> out; + } + + out <== status[0] + status[1] + status[2]; + + out === 3; +} + diff --git a/circuits/hash-circuits b/circuits/hash-circuits new file mode 160000 index 0000000..3ae1517 --- /dev/null +++ b/circuits/hash-circuits @@ -0,0 +1 @@ +Subproject commit 3ae1517526f1061a8d37a159270cc15727e6b503 diff --git a/package.json b/package.json index 546b064..320a574 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "compile:test": "npx circomkit compile add && npx circomkit prove add default && npx circomkit verify add default" }, "dependencies": { + "@crema-labs/ecdsa-p384-circom": "^0.0.1", "circomkit": "^0.0.22", "circomlib": "^2.0.5" }, diff --git a/src/index.ts b/src/index.ts index e69de29..50d3cef 100644 --- a/src/index.ts +++ b/src/index.ts @@ -0,0 +1,18 @@ +export function splitToWords(number: bigint, wordsize: bigint, numberElement: bigint): bigint[] { + let t = number; + const words: bigint[] = []; + const mask = BigInt(BigInt(1) << wordsize) - 1n; + for (let i = BigInt(0); i < numberElement; ++i) { + const word = t & mask; + words.push(word); + t >>= wordsize; + } + if (!(t == BigInt(0))) { + throw `Number ${number} does not fit in ${(wordsize * numberElement).toString()} bits`; + } + return words; +} + +export function hexToBigInt(hex: string) { + return BigInt(`0x${hex}`); + } \ No newline at end of file diff --git a/tests/add.test.ts b/tests/add.test.ts deleted file mode 100644 index 0e7cc01..0000000 --- a/tests/add.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { WitnessTester } from "circomkit"; -import { circomkit } from "./common"; - -describe("Add", () => { - let circuit: WitnessTester<["a", "b"], ["out"]>; - - describe("Add", () => { - before(async () => { - circuit = await circomkit.WitnessTester(`Add`, { - file: "add", - template: "Add", - }); - console.log("#constraints:", await circuit.getConstraintCount()); - }); - - it("should add two numbers", async () => { - await circuit.expectPass({ a: 1, b: 2 }, { out: 3 }); - }); - }); -}); diff --git a/tests/attestation.test.ts b/tests/attestation.test.ts new file mode 100644 index 0000000..25e907d --- /dev/null +++ b/tests/attestation.test.ts @@ -0,0 +1,68 @@ +import { WitnessTester } from "circomkit"; +import { circomkit } from "./common"; +import { hexToBigInt, splitToWords } from "../src"; + +describe("Attestation", () => { + // signal input r[3]; + // signal input s[3]; + // signal input TBSData[3]; + // signal input PubKeys[3][2][8]; + + // signal output out; + describe("VerifyCertChain", () => { + let circuit: WitnessTester<["r", "s", "TBSData", "PubKeys"], ["out"]>; + before(async () => { + circuit = await circomkit.WitnessTester(`Add`, { + file: "attestation", + template: "VerifyCertChain", + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("should verify correct certificate path", async () => { + const r1 = "224e2f1e5a02eb80b21bbc64ea6102db0364e2116ff82af20710938ab1b864e75182ed02aef16e8f7cd0126b4b0d47b6"; + const s1 = "028b661e6c2cb2ce177c0301f586f2e022ba66323d80262cb48acf59e4e2c3624cfd04d517d0805618995ec2a76bda25"; + + const r2 = "bbbe888d738d0502cfbcfd666d09575035bcd6872c3f8430492629edd1f914e879991c9ae8b5aef8d3a85433f7b60d06"; + const s2 = "ab38edd0cc81ed00a452c3ba44f993636553fecc297f2eb4df9f5ebe5a4acab6995c4b820df904386f7807bb589439b7"; + + const r3 = "4201469c1cafb2255ba532b04a06b490fd1ef047834b8fac4264ef6fbbe7e773b9f8545781e2e1a49d3acac0b93eb3b2"; + const s3 = "a79538c43804825945ec49f755c13789ec5966d29e627a6ab628d5a3216b696548c9dfdd81a9e6addb82d5b993046c03"; + + const x1 = "ae5b37a0774d79b2358f40e7d1f22626f1c25fef17802deab3826a59874ff8d2ad1525789aa26604191248b63cb96706"; + const y1 = "9e98d363bd5e370fbfa08e329e8073a985e7746ea359a2f66f29db32af455e211658d567af9e267eb2614dc21a66ce99"; + + const x2 = "4531e198b5b4ec04da1502045704ed4f877272d76135b26116cfc88b615d0a000719ba69858dfe77caa3b839e020ddd6"; + const y2 = "56141404702831e43f70b88fd6c394b608ea2bd6ae61e9f598c12f46af52937266e57f14eb61fec530f7144f53812e35"; + + const tbs1 = Buffer.from( + "30820339a0030201020206018ef1fd4d4a300a06082a8648ce3d040302304f3123302106035504030c1a4170706c6520417070204174746573746174696f6e204341203131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3234303431373136313435335a170d3234303432303136313435335a3081913149304706035504030c4036643261633438343566313332333332326635393233663062643964323264626535306530366237623830313231666365326232623565363665396539386436311a3018060355040b0c114141412043657274696669636174696f6e31133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613059301306072a8648ce3d020106082a8648ce3d030107034200048c2e0cab6f9223970e7f5ab6e92fd7a4d6d621a60e548644bf19764ef1ef853611f6c2b6bb53b2bba2d3468197e4beab2c36cac0e4e24f41f35132c9475e5c24a38201bc308201b8300c0603551d130101ff04023000300e0603551d0f0101ff0404030204f030818806092a864886f763640805047b3079a40302010abf893003020101bf893103020100bf893203020101bf893303020101bf8934290427303335323138373339312e636f6d2e6170706c652e6578616d706c655f6170705f617474657374a5060404736b7320bf893603020105bf893703020100bf893903020100bf893a03020100bf893b030201003081d706092a864886f7636408070481c93081c6bf8a7806040431382e30bf885007020500ffffffffbf8a7b09040732324132343462bf8a7c06040431382e30bf8a7d06040431382e30bf8a7e03020100bf8a7f03020100bf8b0003020100bf8b0103020100bf8b0203020100bf8b0303020100bf8b0403020101bf8b0503020100bf8b0a10040e32322e312e3234342e302e322c30bf8b0b10040e32322e312e3234342e302e322c30bf8b0c10040e32322e312e3234342e302e322c30bf88020a04086970686f6e656f73bf88050a0408496e7465726e616c303306092a864886f76364080204263024a1220420fb6d162a717ecab1778900506fa94d67ee0c1dc3d45b12cdde81befc56e5b7eb" + ); + const tbs2 = Buffer.from( + "308201c8a003020102021009bac5e1bc401ad9d45395bc381a0854300a06082a8648ce3d04030330523126302406035504030c1d4170706c6520417070204174746573746174696f6e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3230303331383138333935355a170d3330303331333030303030305a304f3123302106035504030c1a4170706c6520417070204174746573746174696f6e204341203131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613076301006072a8648ce3d020106052b8104002203620004ae5b37a0774d79b2358f40e7d1f22626f1c25fef17802deab3826a59874ff8d2ad1525789aa26604191248b63cb967069e98d363bd5e370fbfa08e329e8073a985e7746ea359a2f66f29db32af455e211658d567af9e267eb2614dc21a66ce99a366306430120603551d130101ff040830060101ff020100301f0603551d23041830168014ac91105333bdbe6841ffa70ca9e5faeae5e58aa1301d0603551d0e041604143ee35d1c0419a9c9b431f88474d6e1e15772e39b300e0603551d0f0101ff040403020106" + ); + const tbs3 = Buffer.from( + "308201a7a00302010202100bf3be0ef1cdd2e0fb8c6e721f621798300a06082a8648ce3d04030330523126302406035504030c1d4170706c6520417070204174746573746174696f6e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3230303331383138333235335a170d3435303331353030303030305a30523126302406035504030c1d4170706c6520417070204174746573746174696f6e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613076301006072a8648ce3d020106052b81040022036200044531e198b5b4ec04da1502045704ed4f877272d76135b26116cfc88b615d0a000719ba69858dfe77caa3b839e020ddd656141404702831e43f70b88fd6c394b608ea2bd6ae61e9f598c12f46af52937266e57f14eb61fec530f7144f53812e35a3423040300f0603551d130101ff040530030101ff301d0603551d0e04160414ac91105333bdbe6841ffa70ca9e5faeae5e58aa1300e0603551d0f0101ff040403020106" + ); + + const r = [ + splitToWords(hexToBigInt(r1), 48n, 8n), + splitToWords(hexToBigInt(r2), 48n, 8n), + splitToWords(hexToBigInt(r3), 48n, 8n), + ]; + const s = [ + splitToWords(hexToBigInt(s1), 48n, 8n), + splitToWords(hexToBigInt(s2), 48n, 8n), + splitToWords(hexToBigInt(s3), 48n, 8n), + ]; + + const pubkeys = [ + [splitToWords(hexToBigInt(x1), 48n, 8n), splitToWords(hexToBigInt(y1), 48n, 8n)], + [splitToWords(hexToBigInt(x2), 48n, 8n), splitToWords(hexToBigInt(y2), 48n, 8n)], + [splitToWords(hexToBigInt(x2), 48n, 8n), splitToWords(hexToBigInt(y2), 48n, 8n)], + ]; + + circuit.expectPass({ r, s, TBSData: [tbs1.subarray[:], [], []], PubKeys: pubkeys }, { out: 3n }); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index 93093eb..82d4ff7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,17 @@ # yarn lockfile v1 +"@crema-labs/ecdsa-p384-circom@^0.0.1": + version "0.0.1" + resolved "https://registry.yarnpkg.com/@crema-labs/ecdsa-p384-circom/-/ecdsa-p384-circom-0.0.1.tgz#98d8cc2568394e6877874744956d26e59508c7b4" + integrity sha512-7GT/e2iCoKQns0Kh7QwkJolffRm2dUcy+OxhXl6vZkWcMlHXLvBDNSeDBsBGKlCkM9ohcd8EyZmQQcdyuX6g+A== + dependencies: + bip66 "^2.0.0" + circomkit "^0.0.22" + circomlib "^2.0.5" + elliptic "^6.5.7" + node-forge "^1.3.1" + "@cspotcode/source-map-support@^0.8.0": version "0.8.1" resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" @@ -155,6 +166,11 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bip66@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/bip66/-/bip66-2.0.0.tgz#96b5cca18ad10a009f7c8ea4eb24079e37ec9c79" + integrity sha512-kBG+hSpgvZBrkIm9dt5T1Hd/7xGCPEX2npoxAWZfsK1FvjgaxySEh2WizjyIstWXriKo9K9uJ4u0OnsyLDUPXQ== + blake2b-wasm@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/blake2b-wasm/-/blake2b-wasm-2.4.0.tgz#9115649111edbbd87eb24ce7c04b427e4e2be5be" @@ -168,6 +184,11 @@ bluebird@^3.5.5: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== +bn.js@^4.11.9: + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -190,6 +211,11 @@ braces@~3.0.2: dependencies: fill-range "^7.0.1" +brorand@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== + browser-stdout@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" @@ -369,6 +395,19 @@ ejs@^3.1.6: dependencies: jake "^10.8.5" +elliptic@^6.5.7: + version "6.5.7" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.7.tgz#8ec4da2cb2939926a1b9a73619d768207e647c8b" + integrity sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q== + dependencies: + bn.js "^4.11.9" + brorand "^1.1.0" + hash.js "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -557,11 +596,28 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +hmac-drbg@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + hoopy@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" @@ -575,7 +631,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3: +inherits@2, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -709,6 +765,16 @@ make-error@^1.1.1: resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== + minimatch@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" @@ -777,6 +843,11 @@ nanoid@3.3.3: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== +node-forge@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" From 756b145142d5317293c6c60f0abbf561f24336bc Mon Sep 17 00:00:00 2001 From: yash1io Date: Sun, 25 Aug 2024 16:33:42 +0530 Subject: [PATCH 02/14] fix test --- src/index.ts | 14 ++++++++++++-- tests/attestation.test.ts | 31 +++++++++++++++++++------------ 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/index.ts b/src/index.ts index 50d3cef..1b2191b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,5 +14,15 @@ export function splitToWords(number: bigint, wordsize: bigint, numberElement: bi } export function hexToBigInt(hex: string) { - return BigInt(`0x${hex}`); - } \ No newline at end of file + return BigInt(`0x${hex}`); +} + +export function bufferToBigIntArray(buffer: Buffer): bigint[] { + const bigIntArray: bigint[] = []; + + for (const byte of buffer) { + bigIntArray.push(BigInt(byte)); + } + + return bigIntArray; +} diff --git a/tests/attestation.test.ts b/tests/attestation.test.ts index 25e907d..260d802 100644 --- a/tests/attestation.test.ts +++ b/tests/attestation.test.ts @@ -1,14 +1,8 @@ import { WitnessTester } from "circomkit"; import { circomkit } from "./common"; -import { hexToBigInt, splitToWords } from "../src"; +import { hexToBigInt, splitToWords, bufferToBigIntArray } from "../src"; describe("Attestation", () => { - // signal input r[3]; - // signal input s[3]; - // signal input TBSData[3]; - // signal input PubKeys[3][2][8]; - - // signal output out; describe("VerifyCertChain", () => { let circuit: WitnessTester<["r", "s", "TBSData", "PubKeys"], ["out"]>; before(async () => { @@ -36,13 +30,16 @@ describe("Attestation", () => { const y2 = "56141404702831e43f70b88fd6c394b608ea2bd6ae61e9f598c12f46af52937266e57f14eb61fec530f7144f53812e35"; const tbs1 = Buffer.from( - "30820339a0030201020206018ef1fd4d4a300a06082a8648ce3d040302304f3123302106035504030c1a4170706c6520417070204174746573746174696f6e204341203131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3234303431373136313435335a170d3234303432303136313435335a3081913149304706035504030c4036643261633438343566313332333332326635393233663062643964323264626535306530366237623830313231666365326232623565363665396539386436311a3018060355040b0c114141412043657274696669636174696f6e31133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613059301306072a8648ce3d020106082a8648ce3d030107034200048c2e0cab6f9223970e7f5ab6e92fd7a4d6d621a60e548644bf19764ef1ef853611f6c2b6bb53b2bba2d3468197e4beab2c36cac0e4e24f41f35132c9475e5c24a38201bc308201b8300c0603551d130101ff04023000300e0603551d0f0101ff0404030204f030818806092a864886f763640805047b3079a40302010abf893003020101bf893103020100bf893203020101bf893303020101bf8934290427303335323138373339312e636f6d2e6170706c652e6578616d706c655f6170705f617474657374a5060404736b7320bf893603020105bf893703020100bf893903020100bf893a03020100bf893b030201003081d706092a864886f7636408070481c93081c6bf8a7806040431382e30bf885007020500ffffffffbf8a7b09040732324132343462bf8a7c06040431382e30bf8a7d06040431382e30bf8a7e03020100bf8a7f03020100bf8b0003020100bf8b0103020100bf8b0203020100bf8b0303020100bf8b0403020101bf8b0503020100bf8b0a10040e32322e312e3234342e302e322c30bf8b0b10040e32322e312e3234342e302e322c30bf8b0c10040e32322e312e3234342e302e322c30bf88020a04086970686f6e656f73bf88050a0408496e7465726e616c303306092a864886f76364080204263024a1220420fb6d162a717ecab1778900506fa94d67ee0c1dc3d45b12cdde81befc56e5b7eb" + "30820339a0030201020206018ef1fd4d4a300a06082a8648ce3d040302304f3123302106035504030c1a4170706c6520417070204174746573746174696f6e204341203131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3234303431373136313435335a170d3234303432303136313435335a3081913149304706035504030c4036643261633438343566313332333332326635393233663062643964323264626535306530366237623830313231666365326232623565363665396539386436311a3018060355040b0c114141412043657274696669636174696f6e31133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613059301306072a8648ce3d020106082a8648ce3d030107034200048c2e0cab6f9223970e7f5ab6e92fd7a4d6d621a60e548644bf19764ef1ef853611f6c2b6bb53b2bba2d3468197e4beab2c36cac0e4e24f41f35132c9475e5c24a38201bc308201b8300c0603551d130101ff04023000300e0603551d0f0101ff0404030204f030818806092a864886f763640805047b3079a40302010abf893003020101bf893103020100bf893203020101bf893303020101bf8934290427303335323138373339312e636f6d2e6170706c652e6578616d706c655f6170705f617474657374a5060404736b7320bf893603020105bf893703020100bf893903020100bf893a03020100bf893b030201003081d706092a864886f7636408070481c93081c6bf8a7806040431382e30bf885007020500ffffffffbf8a7b09040732324132343462bf8a7c06040431382e30bf8a7d06040431382e30bf8a7e03020100bf8a7f03020100bf8b0003020100bf8b0103020100bf8b0203020100bf8b0303020100bf8b0403020101bf8b0503020100bf8b0a10040e32322e312e3234342e302e322c30bf8b0b10040e32322e312e3234342e302e322c30bf8b0c10040e32322e312e3234342e302e322c30bf88020a04086970686f6e656f73bf88050a0408496e7465726e616c303306092a864886f76364080204263024a1220420fb6d162a717ecab1778900506fa94d67ee0c1dc3d45b12cdde81befc56e5b7eb", + "hex" ); const tbs2 = Buffer.from( - "308201c8a003020102021009bac5e1bc401ad9d45395bc381a0854300a06082a8648ce3d04030330523126302406035504030c1d4170706c6520417070204174746573746174696f6e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3230303331383138333935355a170d3330303331333030303030305a304f3123302106035504030c1a4170706c6520417070204174746573746174696f6e204341203131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613076301006072a8648ce3d020106052b8104002203620004ae5b37a0774d79b2358f40e7d1f22626f1c25fef17802deab3826a59874ff8d2ad1525789aa26604191248b63cb967069e98d363bd5e370fbfa08e329e8073a985e7746ea359a2f66f29db32af455e211658d567af9e267eb2614dc21a66ce99a366306430120603551d130101ff040830060101ff020100301f0603551d23041830168014ac91105333bdbe6841ffa70ca9e5faeae5e58aa1301d0603551d0e041604143ee35d1c0419a9c9b431f88474d6e1e15772e39b300e0603551d0f0101ff040403020106" + "308201c8a003020102021009bac5e1bc401ad9d45395bc381a0854300a06082a8648ce3d04030330523126302406035504030c1d4170706c6520417070204174746573746174696f6e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3230303331383138333935355a170d3330303331333030303030305a304f3123302106035504030c1a4170706c6520417070204174746573746174696f6e204341203131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613076301006072a8648ce3d020106052b8104002203620004ae5b37a0774d79b2358f40e7d1f22626f1c25fef17802deab3826a59874ff8d2ad1525789aa26604191248b63cb967069e98d363bd5e370fbfa08e329e8073a985e7746ea359a2f66f29db32af455e211658d567af9e267eb2614dc21a66ce99a366306430120603551d130101ff040830060101ff020100301f0603551d23041830168014ac91105333bdbe6841ffa70ca9e5faeae5e58aa1301d0603551d0e041604143ee35d1c0419a9c9b431f88474d6e1e15772e39b300e0603551d0f0101ff040403020106", + "hex" ); const tbs3 = Buffer.from( - "308201a7a00302010202100bf3be0ef1cdd2e0fb8c6e721f621798300a06082a8648ce3d04030330523126302406035504030c1d4170706c6520417070204174746573746174696f6e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3230303331383138333235335a170d3435303331353030303030305a30523126302406035504030c1d4170706c6520417070204174746573746174696f6e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613076301006072a8648ce3d020106052b81040022036200044531e198b5b4ec04da1502045704ed4f877272d76135b26116cfc88b615d0a000719ba69858dfe77caa3b839e020ddd656141404702831e43f70b88fd6c394b608ea2bd6ae61e9f598c12f46af52937266e57f14eb61fec530f7144f53812e35a3423040300f0603551d130101ff040530030101ff301d0603551d0e04160414ac91105333bdbe6841ffa70ca9e5faeae5e58aa1300e0603551d0f0101ff040403020106" + "308201a7a00302010202100bf3be0ef1cdd2e0fb8c6e721f621798300a06082a8648ce3d04030330523126302406035504030c1d4170706c6520417070204174746573746174696f6e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3230303331383138333235335a170d3435303331353030303030305a30523126302406035504030c1d4170706c6520417070204174746573746174696f6e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613076301006072a8648ce3d020106052b81040022036200044531e198b5b4ec04da1502045704ed4f877272d76135b26116cfc88b615d0a000719ba69858dfe77caa3b839e020ddd656141404702831e43f70b88fd6c394b608ea2bd6ae61e9f598c12f46af52937266e57f14eb61fec530f7144f53812e35a3423040300f0603551d130101ff040530030101ff301d0603551d0e04160414ac91105333bdbe6841ffa70ca9e5faeae5e58aa1300e0603551d0f0101ff040403020106", + "hex" ); const r = [ @@ -56,13 +53,23 @@ describe("Attestation", () => { splitToWords(hexToBigInt(s3), 48n, 8n), ]; - const pubkeys = [ + const PubKeys = [ [splitToWords(hexToBigInt(x1), 48n, 8n), splitToWords(hexToBigInt(y1), 48n, 8n)], [splitToWords(hexToBigInt(x2), 48n, 8n), splitToWords(hexToBigInt(y2), 48n, 8n)], [splitToWords(hexToBigInt(x2), 48n, 8n), splitToWords(hexToBigInt(y2), 48n, 8n)], ]; - circuit.expectPass({ r, s, TBSData: [tbs1.subarray[:], [], []], PubKeys: pubkeys }, { out: 3n }); + const TBSData = [bufferToBigIntArray(tbs1), bufferToBigIntArray(tbs2), bufferToBigIntArray(tbs3)]; + + circuit.expectPass( + { + r, + s, + TBSData, + PubKeys, + }, + { out: 3n } + ); }); }); }); From ecd9cb902e9397c52ce0b424a581c9971d13c83e Mon Sep 17 00:00:00 2001 From: 0xvikasrushi <0xvikas@gmail.com> Date: Sun, 25 Aug 2024 17:07:05 +0530 Subject: [PATCH 03/14] bug: test cases p384 --- circuits/attestation.circom | 35 ++++++++++++++++++----------------- package.json | 4 +++- tests/attestation.test.ts | 19 +++++++++++++++++++ yarn.lock | 26 ++++++++++++++++++++++++++ 4 files changed, 66 insertions(+), 18 deletions(-) diff --git a/circuits/attestation.circom b/circuits/attestation.circom index ff1ff73..44de9e0 100644 --- a/circuits/attestation.circom +++ b/circuits/attestation.circom @@ -1,7 +1,8 @@ -pragma circom 2.1.5; +pragma circom 2.1.9; + +include "@crema-labs/ecdsa-p384-circom/circuits/ecdsa.circom"; +include "hash-circuits/circuits/sha2/sha384/sha384_hash_bits.circom"; -import "@crema-labs/ecdsa-p384-circom/circuits/ecdsa.circom"; -import "./hash-circuit/circuits/sha2/sha384/sha384_hash_bytes.circom"; template VerifyCertChain(m){ signal input r[3][8]; @@ -21,20 +22,20 @@ template VerifyCertChain(m){ component hash[2]; component ecdsa[3]; - for (var i = 0; i < 3; i++) { - has[i] = Sha384_hash_bytes_digest(m); - ecdsa[i] = ECDSAVerifyNoPubkeyCheck(48, 8); - - hash[i].inp_bytes <== TBSData[i]; - ecdsa[i].msghash <== hash[i].hash_bytes; - ecdsa[i].r <== r[i]; - ecdsa[i].s <== s[i]; - ecdsa[i].pubkey <== PubKeys[i]; - ecdsa[i].result ==> out; - } + // for (var i = 0; i < 3; i++) { + // hash[i] = Sha384_hash_bytes_digest(m); + // ecdsa[i] = ECDSAVerifyNoPubkeyCheck(48, 8); - out <== status[0] + status[1] + status[2]; + // hash[i].inp_bytes <== TBSData[i]; + // ecdsa[i].msghash <== hash[i].hash_bytes; + // ecdsa[i].r <== r[i]; + // ecdsa[i].s <== s[i]; + // ecdsa[i].pubkey <== PubKeys[i]; + // ecdsa[i].result ==> out; + // } - out === 3; -} + // out <== status[0] + status[1] + status[2]; + // out === 3; + out <== 3; // REMOVE THIS FOR NOW! +} \ No newline at end of file diff --git a/package.json b/package.json index 320a574..d044a67 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,10 @@ }, "dependencies": { "@crema-labs/ecdsa-p384-circom": "^0.0.1", + "@types/elliptic": "^6.4.18", "circomkit": "^0.0.22", - "circomlib": "^2.0.5" + "circomlib": "^2.0.5", + "elliptic": "^6.5.7" }, "devDependencies": { "@types/mocha": "^10.0.1", diff --git a/tests/attestation.test.ts b/tests/attestation.test.ts index 260d802..8875a9d 100644 --- a/tests/attestation.test.ts +++ b/tests/attestation.test.ts @@ -1,14 +1,20 @@ import { WitnessTester } from "circomkit"; import { circomkit } from "./common"; import { hexToBigInt, splitToWords, bufferToBigIntArray } from "../src"; +import elliptic, { SignatureInput } from "elliptic"; +import crypto from "crypto"; describe("Attestation", () => { describe("VerifyCertChain", () => { let circuit: WitnessTester<["r", "s", "TBSData", "PubKeys"], ["out"]>; + + const MAX_CERT_CHAIN_LEN = 100; + before(async () => { circuit = await circomkit.WitnessTester(`Add`, { file: "attestation", template: "VerifyCertChain", + params: [MAX_CERT_CHAIN_LEN], }); console.log("#constraints:", await circuit.getConstraintCount()); }); @@ -61,6 +67,19 @@ describe("Attestation", () => { const TBSData = [bufferToBigIntArray(tbs1), bufferToBigIntArray(tbs2), bufferToBigIntArray(tbs3)]; + const ec = new elliptic.ec("p384"); + const key = ec.keyFromPublic({ x: x1, y: y1 }, "hex"); + const hashMessage = crypto.createHash("sha384").update(tbs1).digest(); + + const signature: SignatureInput = { + r: r1, + s: s1, + }; + + const isValid = key.verify(hashMessage, signature); + + console.log("isValid:", isValid); + circuit.expectPass( { r, diff --git a/yarn.lock b/yarn.lock index 82d4ff7..b2016cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -71,11 +71,32 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== +"@types/bn.js@*": + version "5.1.5" + resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.5.tgz#2e0dacdcce2c0f16b905d20ff87aedbc6f7b4bf0" + integrity sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A== + dependencies: + "@types/node" "*" + +"@types/elliptic@^6.4.18": + version "6.4.18" + resolved "https://registry.yarnpkg.com/@types/elliptic/-/elliptic-6.4.18.tgz#bc96e26e1ccccbabe8b6f0e409c85898635482e1" + integrity sha512-UseG6H5vjRiNpQvrhy4VF/JXdA3V/Fp5amvveaL+fs28BZ6xIKJBPnUPRlEaZpysD9MbpfaLi8lbl7PGUAkpWw== + dependencies: + "@types/bn.js" "*" + "@types/mocha@^10.0.1": version "10.0.1" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.1.tgz#2f4f65bb08bc368ac39c96da7b2f09140b26851b" integrity sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q== +"@types/node@*": + version "22.5.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.5.0.tgz#10f01fe9465166b4cab72e75f60d8b99d019f958" + integrity sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg== + dependencies: + undici-types "~6.19.2" + "@types/node@^20.3.0": version "20.3.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.0.tgz#719498898d5defab83c3560f45d8498f58d11938" @@ -1074,6 +1095,11 @@ typescript@^5.1.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826" integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw== +undici-types@~6.19.2: + version "6.19.8" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" + integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== + util@^0.12.4: version "0.12.5" resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" From bf3b35cce926867c4bd235d19f8a0798e212a917 Mon Sep 17 00:00:00 2001 From: yash1io Date: Sun, 25 Aug 2024 17:39:22 +0530 Subject: [PATCH 04/14] fix tests --- specs/appattest.md | 49 +++++++++++++++++++++++++++++++++++++++ tests/attestation.test.ts | 38 ++++++++++++++++++++---------- 2 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 specs/appattest.md diff --git a/specs/appattest.md b/specs/appattest.md new file mode 100644 index 0000000..936dd28 --- /dev/null +++ b/specs/appattest.md @@ -0,0 +1,49 @@ +# Apple app attest Architecture +This document describes the architecture of the Apple app attest system . +### Actors + +- **Apple App Attest Server**: The server that issues the attestation keys to the app. +- **An iOS App**: The app that wants to use the attestation keys. +- **App Backend Server**: The server that the app communicates with. + +### Flow + +Phase 1: Attestation +1. The iOS app creates a public-private key pair in the Secure Enclave. +2. The iOS app requests the server for some random data. +3. The iOS app send the data and the public key to the Apple App Attest Server. +4. The Apple App Attest Server signs and creates an attestation object and sends it back to the iOS app. +5. The iOS app sends the attestation object to the App Backend Server. +6. The App Backend Server verifies the attestation object with the Apple Certificate Authority which is publicly available and other verification steps as mentioned in the [Apple documentation for attestation validity](https://developer.apple.com/documentation/devicecheck/validating-apps-that-connect-to-your-server#Verify-the-attestation). +7. The App Backed also store the public key hash of the leaf X.509 certificate in the attestation object for future verification. +8. Optionally, the App Backend Server will also verify receipt of the attestation object with the Apple App Attest Server and stores it. + +Phase 2: Assertion + +The iOS app after attestation can request the server for some data with an assertion. The backend server will verify the assertion with the public key hash stored from attestation object.The backend can decide to when when assertion is required based on the business logic. + +1. The iOS app requests the server for some random data . +2. The iOS app generates an assertion using the private key in the Secure Enclave and `clientDataHash`. +3. The iOS app sends the assertion to the App Backend Server which verifies the assertion and checks if the hash of the public key has been attested before. Refer to the [Apple documentation for assertion validity](https://developer.apple.com/documentation/devicecheck/validating-apps-that-connect-to-your-server#Verify-the-assertion). + +### Notes +1. Attestation object contains three X.509 certificates: + - **Leaf X.509 certificate**: The certificate that contains the public key of the app. + - **Intermediate X.509 certificate**: The certificate that signs the leaf certificate. + - **Root X.509 certificate**: The certificate that signs the intermediate certificate and is the root certificate of the Apple App Attest Server available at [Apple CA](https://www.apple.com/certificateauthority/private/). + - All the certificate for attestations are generated using the Elliptic Curve Digital Signature Algorithm (ECDSA) with the P-384 curve. + - The attestation object is in CBOR encoding. +```cbor +{ + fmt: 'apple-appattest', + attStmt: { + x5c: [ + , + + ], + receipt: + }, + authData: +} +``` +2. Assertions are signed using the Elliptic Curve Digital Signature Algorithm (ECDSA) with the P-256 curve. \ No newline at end of file diff --git a/tests/attestation.test.ts b/tests/attestation.test.ts index 8875a9d..603d7b8 100644 --- a/tests/attestation.test.ts +++ b/tests/attestation.test.ts @@ -5,6 +5,29 @@ import elliptic, { SignatureInput } from "elliptic"; import crypto from "crypto"; describe("Attestation", () => { + const verifySig = ( + curve: string, + hash: string, + r: string, + s: string, + p_x: string, + p_y: string, + tbs: Buffer, + id: string + ) => { + const ec = new elliptic.ec(curve); + const key = ec.keyFromPublic({ x: p_x, y: p_y }, "hex"); + const hashMessage = crypto.createHash(hash).update(tbs).digest(); + + const signature: SignatureInput = { + r: r, + s: s, + }; + + const isValid = key.verify(hashMessage, signature); + + console.log(id, "isValid:", isValid); + }; describe("VerifyCertChain", () => { let circuit: WitnessTester<["r", "s", "TBSData", "PubKeys"], ["out"]>; @@ -67,18 +90,9 @@ describe("Attestation", () => { const TBSData = [bufferToBigIntArray(tbs1), bufferToBigIntArray(tbs2), bufferToBigIntArray(tbs3)]; - const ec = new elliptic.ec("p384"); - const key = ec.keyFromPublic({ x: x1, y: y1 }, "hex"); - const hashMessage = crypto.createHash("sha384").update(tbs1).digest(); - - const signature: SignatureInput = { - r: r1, - s: s1, - }; - - const isValid = key.verify(hashMessage, signature); - - console.log("isValid:", isValid); + verifySig("p384", "sha256", r1, s1, x1, y1, tbs1, "cert 1"); + verifySig("p384", "sha384", r2, s2, x2, y2, tbs2, "cert 2"); + verifySig("p384", "sha384", r3, s3, x2, y2, tbs3, "cert 3"); circuit.expectPass( { From 07a46d5226ddda6a73aa81adf85e19890c0658ca Mon Sep 17 00:00:00 2001 From: yash1io Date: Sun, 25 Aug 2024 17:43:12 +0530 Subject: [PATCH 05/14] added source --- tests/attestation.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/attestation.test.ts b/tests/attestation.test.ts index 603d7b8..11bc8e2 100644 --- a/tests/attestation.test.ts +++ b/tests/attestation.test.ts @@ -43,6 +43,7 @@ describe("Attestation", () => { }); it("should verify correct certificate path", async () => { + // generated from example from here https://developer.apple.com/documentation/devicecheck/attestation-object-validation-guide const r1 = "224e2f1e5a02eb80b21bbc64ea6102db0364e2116ff82af20710938ab1b864e75182ed02aef16e8f7cd0126b4b0d47b6"; const s1 = "028b661e6c2cb2ce177c0301f586f2e022ba66323d80262cb48acf59e4e2c3624cfd04d517d0805618995ec2a76bda25"; From 721b2a8c253e92a749cd187e5684c011fe298f86 Mon Sep 17 00:00:00 2001 From: yash1io Date: Sun, 25 Aug 2024 19:25:05 +0530 Subject: [PATCH 06/14] add POC plan --- specs/appattest.md | 20 ++++++++++++++++++-- tests/attestation.test.ts | 2 +- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/specs/appattest.md b/specs/appattest.md index 936dd28..c3a7cf9 100644 --- a/specs/appattest.md +++ b/specs/appattest.md @@ -1,5 +1,5 @@ # Apple app attest Architecture -This document describes the architecture of the Apple app attest system . +This document describes the architecture of the Apple app attest system. ### Actors - **Apple App Attest Server**: The server that issues the attestation keys to the app. @@ -32,6 +32,10 @@ The iOS app after attestation can request the server for some data with an asser - **Intermediate X.509 certificate**: The certificate that signs the leaf certificate. - **Root X.509 certificate**: The certificate that signs the intermediate certificate and is the root certificate of the Apple App Attest Server available at [Apple CA](https://www.apple.com/certificateauthority/private/). - All the certificate for attestations are generated using the Elliptic Curve Digital Signature Algorithm (ECDSA) with the P-384 curve. + - Assuming there will only be 3 certificates in the attestation object. + - The leaf certificate is the first certificate in the `x5c` array needs P-384 curve verification with SHA-256 of RawTBSData from leaf certificate and public key of intermediate certificate. + - The intermediate certificate is the second certificate in the `x5c` array needs P-384 curve verification with SHA-384 of RawTBSData from intermediate certificate and public key of root certificate. + - THe root certificate is the third certificate in the `x5c` array needs P-384 curve verification with SHA-384 of RawTBSData from root certificate and public key of root certificate itself. - The attestation object is in CBOR encoding. ```cbor { @@ -46,4 +50,16 @@ The iOS app after attestation can request the server for some data with an asser authData: } ``` -2. Assertions are signed using the Elliptic Curve Digital Signature Algorithm (ECDSA) with the P-256 curve. \ No newline at end of file +2. Assertions are signed using the Elliptic Curve Digital Signature Algorithm (ECDSA) with the P-256 curve. + + +### POC and Demo Plan +We use the flow to prove a user in in Asia without revealing which country you are in. +1. Create a simple iOS app that address to the above flow to generate attestation object and verify on chain. +2. Verifier contract that verifies the attestation object and stores the public key hash. +3. iOS application will generate a assertion with proof that the device is in a particular location. +4. Verifier contract will verify the assertion and location proof. + +### Blockers + +1. Implementation of variable length sha-384. \ No newline at end of file diff --git a/tests/attestation.test.ts b/tests/attestation.test.ts index 11bc8e2..674b7d1 100644 --- a/tests/attestation.test.ts +++ b/tests/attestation.test.ts @@ -34,7 +34,7 @@ describe("Attestation", () => { const MAX_CERT_CHAIN_LEN = 100; before(async () => { - circuit = await circomkit.WitnessTester(`Add`, { + circuit = await circomkit.WitnessTester(`VerifyCertChain`, { file: "attestation", template: "VerifyCertChain", params: [MAX_CERT_CHAIN_LEN], From 1d51066a7405222912b983e11030442a94635b87 Mon Sep 17 00:00:00 2001 From: yash1io Date: Thu, 5 Sep 2024 02:12:49 +0530 Subject: [PATCH 07/14] add required utils --- .gitmodules | 3 + circuits/attestation.circom | 119 ++++++++++++++++++++++++++++++------ circuits/sha256-var | 1 + src/index.ts | 22 ++++++- tests/attestation.test.ts | 6 +- 5 files changed, 129 insertions(+), 22 deletions(-) create mode 160000 circuits/sha256-var diff --git a/.gitmodules b/.gitmodules index 568d5b5..0aacb64 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "circuits/hash-circuits"] path = circuits/hash-circuits url = https://github.com/bkomuves/hash-circuits/ +[submodule "circuits/sha256-var"] + path = circuits/sha256-var + url = https://github.com/noway/sha256-var-circom diff --git a/circuits/attestation.circom b/circuits/attestation.circom index 44de9e0..05d11eb 100644 --- a/circuits/attestation.circom +++ b/circuits/attestation.circom @@ -2,16 +2,62 @@ pragma circom 2.1.9; include "@crema-labs/ecdsa-p384-circom/circuits/ecdsa.circom"; include "hash-circuits/circuits/sha2/sha384/sha384_hash_bits.circom"; +include "sha256-var/circuits/sha256Var.circom"; +include "circomlib/circuits/bitify.circom"; +template SplitToWords(nBits, wordsize, numberElement) { + signal input in[nBits]; + signal output out[numberElement]; -template VerifyCertChain(m){ + assert(numberElement * wordsize == nBits); + + component bitsToNum[numberElement]; + + for (var i = 0; i < numberElement; i++) { + bitsToNum[i] = Bits2Num(wordsize); + for (var j = 0; j < wordsize; j++) { + bitsToNum[i].in[wordsize-1-j] <== in[i*wordsize + j]; + } + bitsToNum[i].out ==> out[numberElement-1-i]; + } +} + +template BytesToBits(nBytes) { + signal input in[nBytes]; + signal output out[nBytes*8]; + + component NumToBits[nBytes]; + + for (var i=0; i < nBytes; i++) { + NumToBits[i] = Num2Bits(8); + NumToBits[i].in <== in[i]; + for (var j=0; j < 8; j++) { + NumToBits[i].out[j] ==> out[i*8 + j]; + } + } +} + +template PadBits(nBits,target){ + signal input in[nBits]; + signal output out[target]; + + for (var i=0; i < target-nBits; i++) { + out[i] <== 0; + } + for (var i=nBitarget-nBits; i < target; i++) { + out[i] <== 0; + } +} + + +template VerifyCertChain(TBS2Size,TBS3Size){ signal input r[3][8]; signal input s[3][8]; signal input TBS1Size; - signal input TBS2Size; - signal input TBS3Size; - signal input TBSData[3][m]; + signal input TBS1Data[8192]; // max size of TBS1Data in bits as per sha256var spec + signal input TBS2Data[TBS2Size]; + signal input TBS3Data[TBS3Size]; signal input PubKeys[3][2][8]; @@ -19,23 +65,58 @@ template VerifyCertChain(m){ signal status[3]; - component hash[2]; - component ecdsa[3]; + component sha256Hash = Sha256Var(4); // 4 bits for 16 blocks each of 512 bits + component sha384Hash[2]; + component p384Ecdsa[3]; + component bitsToWords[3]; + + + // variable length hash of leaf certificate raw tbs data + sha256Hash.in <== TBS1Data; + sha256Hash.len <== TBS1Size; + + bitsToWords[0] = BitStreamToWords(256); + bitsToWords[0].in <== sha256Hash.out; + + // signature verification with public key of next certificate in chain + p384Ecdsa[0] = ECDSAVerifyNoPubkeyCheck(48, 8); + p384Ecdsa[0].msghash <== bitsToWords[0].out; + + p384Ecdsa[0].r <== r[0]; + p384Ecdsa[0].s <== s[0]; + p384Ecdsa[0].pubkey <== PubKeys[0]; + p384Ecdsa[0].result ==> status[0]; + + // const length hash of intermediate certificate raw tbs data + sha384Hash[0] = Sha384_hash_bytes_digest(TBS2Size); + sha384Hash[0].inp_bytes <== TBS2Data; + + bitsToWords[1] = BitStreamToWords(384); + bitsToWords[1].in <== sha384Hash[0].hash_bytes; + + p384Ecdsa[1] = ECDSAVerifyNoPubkeyCheck(48, 8); + p384Ecdsa[1].msghash <== bitsToWords[1].out; + p384Ecdsa[1].r <== r[1]; + p384Ecdsa[1].s <== s[1]; + p384Ecdsa[1].pubkey <== PubKeys[1]; + p384Ecdsa[1].result ==> status[1]; + + + // variable length hash of root certificate raw tbs data + sha384Hash[1] = Sha384_hash_bytes_digest(TBS3Size); + sha384Hash[1].inp_bytes <== TBS3Data; - // for (var i = 0; i < 3; i++) { - // hash[i] = Sha384_hash_bytes_digest(m); - // ecdsa[i] = ECDSAVerifyNoPubkeyCheck(48, 8); + bitsToWords[2] = BitStreamToWords(384); + bitsToWords[2].in <== sha384Hash[1].hash_bytes; - // hash[i].inp_bytes <== TBSData[i]; - // ecdsa[i].msghash <== hash[i].hash_bytes; - // ecdsa[i].r <== r[i]; - // ecdsa[i].s <== s[i]; - // ecdsa[i].pubkey <== PubKeys[i]; - // ecdsa[i].result ==> out; - // } + p384Ecdsa[2] = ECDSAVerifyNoPubkeyCheck(48, 8); + p384Ecdsa[2].msghash <== bitsToWords[2].out; + p384Ecdsa[2].r <== r[2]; + p384Ecdsa[2].s <== s[2]; + p384Ecdsa[2].pubkey <== PubKeys[2]; + p384Ecdsa[2].result ==> status[2]; - // out <== status[0] + status[1] + status[2]; + out <== status[0] + status[1] + status[2]; - // out === 3; - out <== 3; // REMOVE THIS FOR NOW! + out === 3; } \ No newline at end of file diff --git a/circuits/sha256-var b/circuits/sha256-var new file mode 160000 index 0000000..ba32596 --- /dev/null +++ b/circuits/sha256-var @@ -0,0 +1 @@ +Subproject commit ba325965578e83520073ec946d16889c1528f85b diff --git a/src/index.ts b/src/index.ts index 1b2191b..d20f737 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,7 +17,7 @@ export function hexToBigInt(hex: string) { return BigInt(`0x${hex}`); } -export function bufferToBigIntArray(buffer: Buffer): bigint[] { +export function bufferToBigIntByteArray(buffer: Buffer): bigint[] { const bigIntArray: bigint[] = []; for (const byte of buffer) { @@ -26,3 +26,23 @@ export function bufferToBigIntArray(buffer: Buffer): bigint[] { return bigIntArray; } + +export function bufferToBigIntBitArray(buffer: Buffer): bigint[] { + const bigIntArray: bigint[] = []; + + for (const byte of buffer) { + for (let i = 7; i >= 0; i--) { + bigIntArray.push(BigInt((byte >> i) & 1)); + } + } + + return bigIntArray; +} + +export function numToBits(num: number): bigint[] { + const bits: bigint[] = []; + for (let i = 7; i >= 0; i--) { + bits.push(BigInt((num >> i) & 1)); + } + return bits; +} diff --git a/tests/attestation.test.ts b/tests/attestation.test.ts index 674b7d1..1181e75 100644 --- a/tests/attestation.test.ts +++ b/tests/attestation.test.ts @@ -1,6 +1,6 @@ import { WitnessTester } from "circomkit"; import { circomkit } from "./common"; -import { hexToBigInt, splitToWords, bufferToBigIntArray } from "../src"; +import { hexToBigInt, splitToWords, bufferToBigIntBitArray } from "../src"; import elliptic, { SignatureInput } from "elliptic"; import crypto from "crypto"; @@ -89,7 +89,7 @@ describe("Attestation", () => { [splitToWords(hexToBigInt(x2), 48n, 8n), splitToWords(hexToBigInt(y2), 48n, 8n)], ]; - const TBSData = [bufferToBigIntArray(tbs1), bufferToBigIntArray(tbs2), bufferToBigIntArray(tbs3)]; + const TBSData = [bufferToBigIntBitArray(tbs1), bufferToBigIntBitArray(tbs2), bufferToBigIntBitArray(tbs3)]; verifySig("p384", "sha256", r1, s1, x1, y1, tbs1, "cert 1"); verifySig("p384", "sha384", r2, s2, x2, y2, tbs2, "cert 2"); @@ -107,3 +107,5 @@ describe("Attestation", () => { }); }); }); + + From 5eb10ecc5c6d40519cab0e2fc7ff1cfa57c60523 Mon Sep 17 00:00:00 2001 From: yash1io Date: Thu, 5 Sep 2024 02:15:12 +0530 Subject: [PATCH 08/14] add utils test --- tests/utils.test.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/utils.test.ts diff --git a/tests/utils.test.ts b/tests/utils.test.ts new file mode 100644 index 0000000..1164eae --- /dev/null +++ b/tests/utils.test.ts @@ -0,0 +1,30 @@ +import { WitnessTester } from "circomkit"; +import crypto from "crypto"; +import { circomkit } from "./common"; +import { hexToBigInt, splitToWords, bufferToBigIntBitArray } from "../src"; +import { log } from "console"; +describe("SplitToWords", () => { + let circuit: WitnessTester<["in"], ["out"]>; + + before(async () => { + circuit = await circomkit.WitnessTester(`SplitToWords`, { + file: "attestation", + template: "SplitToWords", + params: [384, 48, 8], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("should parse Bit Stream to words", async () => { + const msg = crypto.createHash("sha384").update("foo world").digest("hex"); + const words = splitToWords(hexToBigInt(msg), 48n, 8n); + await circuit.expectPass( + { + in: bufferToBigIntBitArray(Buffer.from(msg, "hex")), + }, + { + out: words, + } + ); + }); +}); From b8a0497281c1ebcbaef1b250091614cbc310d48e Mon Sep 17 00:00:00 2001 From: 0xvikasrushi <0xvikas@gmail.com> Date: Sun, 8 Sep 2024 14:09:23 +0530 Subject: [PATCH 09/14] feat: test for utils.circom --- circuits/attestation.circom | 45 --------------------------------- circuits/utils.circom | 47 ++++++++++++++++++++++++++++++++++ tests/utils.test.ts | 50 +++++++++++++++++++++++++++++++++++-- 3 files changed, 95 insertions(+), 47 deletions(-) create mode 100644 circuits/utils.circom diff --git a/circuits/attestation.circom b/circuits/attestation.circom index 05d11eb..6546e06 100644 --- a/circuits/attestation.circom +++ b/circuits/attestation.circom @@ -3,51 +3,6 @@ pragma circom 2.1.9; include "@crema-labs/ecdsa-p384-circom/circuits/ecdsa.circom"; include "hash-circuits/circuits/sha2/sha384/sha384_hash_bits.circom"; include "sha256-var/circuits/sha256Var.circom"; -include "circomlib/circuits/bitify.circom"; - -template SplitToWords(nBits, wordsize, numberElement) { - signal input in[nBits]; - signal output out[numberElement]; - - assert(numberElement * wordsize == nBits); - - component bitsToNum[numberElement]; - - for (var i = 0; i < numberElement; i++) { - bitsToNum[i] = Bits2Num(wordsize); - for (var j = 0; j < wordsize; j++) { - bitsToNum[i].in[wordsize-1-j] <== in[i*wordsize + j]; - } - bitsToNum[i].out ==> out[numberElement-1-i]; - } -} - -template BytesToBits(nBytes) { - signal input in[nBytes]; - signal output out[nBytes*8]; - - component NumToBits[nBytes]; - - for (var i=0; i < nBytes; i++) { - NumToBits[i] = Num2Bits(8); - NumToBits[i].in <== in[i]; - for (var j=0; j < 8; j++) { - NumToBits[i].out[j] ==> out[i*8 + j]; - } - } -} - -template PadBits(nBits,target){ - signal input in[nBits]; - signal output out[target]; - - for (var i=0; i < target-nBits; i++) { - out[i] <== 0; - } - for (var i=nBitarget-nBits; i < target; i++) { - out[i] <== 0; - } -} template VerifyCertChain(TBS2Size,TBS3Size){ diff --git a/circuits/utils.circom b/circuits/utils.circom new file mode 100644 index 0000000..1dd8fcb --- /dev/null +++ b/circuits/utils.circom @@ -0,0 +1,47 @@ +pragma circom 2.1.9; + +include "circomlib/circuits/bitify.circom"; + +template SplitToWords(nBits, wordsize, numberElement) { + signal input in[nBits]; + signal output out[numberElement]; + + assert(numberElement * wordsize == nBits); + + component bitsToNum[numberElement]; + + for (var i = 0; i < numberElement; i++) { + bitsToNum[i] = Bits2Num(wordsize); + for (var j = 0; j < wordsize; j++) { + bitsToNum[i].in[wordsize-1-j] <== in[i*wordsize + j]; + } + bitsToNum[i].out ==> out[numberElement-1-i]; + } +} + +template BytesToBits(nBytes) { + signal input in[nBytes]; + signal output out[nBytes*8]; + + component NumToBits[nBytes]; + + for (var i=0; i < nBytes; i++) { + NumToBits[i] = Num2Bits(8); + NumToBits[i].in <== in[i]; + for (var j=0; j < 8; j++) { + out[i*8 + j] <== NumToBits[i].out[j]; + } + } +} + +template PadBits(nBits,target){ + signal input in[nBits]; + signal output out[target]; + + for (var i=0; i < target-nBits; i++) { + out[i] <== in[i]; + } + for (var i= target-nBits; i < target; i++) { + out[i] <== 0; + } +} diff --git a/tests/utils.test.ts b/tests/utils.test.ts index 1164eae..90d9969 100644 --- a/tests/utils.test.ts +++ b/tests/utils.test.ts @@ -2,13 +2,13 @@ import { WitnessTester } from "circomkit"; import crypto from "crypto"; import { circomkit } from "./common"; import { hexToBigInt, splitToWords, bufferToBigIntBitArray } from "../src"; -import { log } from "console"; + describe("SplitToWords", () => { let circuit: WitnessTester<["in"], ["out"]>; before(async () => { circuit = await circomkit.WitnessTester(`SplitToWords`, { - file: "attestation", + file: "utils", template: "SplitToWords", params: [384, 48, 8], }); @@ -28,3 +28,49 @@ describe("SplitToWords", () => { ); }); }); + +describe("PadBits", () => { + let circuit: WitnessTester<["in"], ["out"]>; + + before(async () => { + circuit = await circomkit.WitnessTester(`PadBits`, { + file: "utils", + template: "PadBits", + params: [4, 8], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("should pad bits correctly", async () => { + const inputBits = [1, 0, 1, 1]; + const expectedOutput = [1, 0, 1, 1, 0, 0, 0, 0]; + await circuit.expectPass({ in: inputBits }, { out: expectedOutput }); + }); +}); + +function byteToBits(byte: number): number[] { + let bits = []; + for (let i = 7; i >= 0; i--) { + bits.push((byte >> i) & 1); + } + return bits.reverse(); +} + +describe("BytesToBits", () => { + let circuit: WitnessTester<["in"], ["out"]>; + const testBytes = [0xa5, 0x53, 0x21, 0x12]; + + before(async () => { + circuit = await circomkit.WitnessTester(`BytesToBits`, { + file: "utils", + template: "BytesToBits", + params: [testBytes.length], + }); + console.log("#constraints:", await circuit.getConstraintCount()); + }); + + it("should convert bytes to bits", async () => { + const expectedBits = testBytes.map((byte) => byteToBits(byte)).flat(); + await circuit.expectPass({ in: testBytes }, { out: expectedBits }); + }); +}); From 852bb84e39a822906f3707b5696e4ffb93849664 Mon Sep 17 00:00:00 2001 From: yash1io Date: Sun, 8 Sep 2024 20:41:04 +0530 Subject: [PATCH 10/14] fix attestation tests --- .gitmodules | 6 ++-- .mocharc.json | 2 +- circuits/attestation.circom | 72 ++++++++++++++++++------------------- circuits/sha256-var | 1 - circuits/sha256-var-module | 1 + circuits/utils.circom | 6 ++-- tests/attestation.test.ts | 36 ++++++++++++------- tests/utils.test.ts | 10 +++--- 8 files changed, 71 insertions(+), 63 deletions(-) delete mode 160000 circuits/sha256-var create mode 160000 circuits/sha256-var-module diff --git a/.gitmodules b/.gitmodules index 0aacb64..3287b28 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "circuits/hash-circuits"] path = circuits/hash-circuits url = https://github.com/bkomuves/hash-circuits/ -[submodule "circuits/sha256-var"] - path = circuits/sha256-var - url = https://github.com/noway/sha256-var-circom +[submodule "circuits/sha256-var-module"] + path = circuits/sha256-var-module + url = https://github.com/crema-labs/sha256-var-circom diff --git a/.mocharc.json b/.mocharc.json index 6382cd6..005d17b 100644 --- a/.mocharc.json +++ b/.mocharc.json @@ -2,6 +2,6 @@ "extension": ["ts"], "require": "ts-node/register", "spec": "tests/**/*.test.ts", - "timeout": 100000, + "timeout": 100000000000, "exit": true } diff --git a/circuits/attestation.circom b/circuits/attestation.circom index 6546e06..f594929 100644 --- a/circuits/attestation.circom +++ b/circuits/attestation.circom @@ -1,77 +1,73 @@ pragma circom 2.1.9; include "@crema-labs/ecdsa-p384-circom/circuits/ecdsa.circom"; -include "hash-circuits/circuits/sha2/sha384/sha384_hash_bits.circom"; -include "sha256-var/circuits/sha256Var.circom"; +include "./hash-circuits/circuits/sha2/sha384/sha384_hash_bits.circom"; +include "./sha256-var-module/circuits/sha256Var.circom"; +include "./utils.circom"; -template VerifyCertChain(TBS2Size,TBS3Size){ +template VerifyCertChain(TBS2Size,TBS3Size,BlockSpace){ signal input r[3][8]; signal input s[3][8]; - signal input TBS1Size; - signal input TBS1Data[8192]; // max size of TBS1Data in bits as per sha256var spec + + var BLOCK_LEN = 512; + var MaxBlockCount = pow(2, BlockSpace); + var MaxLen = BLOCK_LEN * MaxBlockCount; // max size of TBS1Data in bits as per sha256var spec + signal input TBS1Data[MaxLen]; + signal input TBS2Data[TBS2Size]; signal input TBS3Data[TBS3Size]; - signal input PubKeys[3][2][8]; signal output out; - - signal status[3]; - - component sha256Hash = Sha256Var(4); // 4 bits for 16 blocks each of 512 bits + signal status[3]; component sha384Hash[2]; component p384Ecdsa[3]; component bitsToWords[3]; - - + component padding; + component bytesToBits[2]; + component sha256Hash = Sha256Var(4); // 4 bits for 16 blocks each of 512 bits // variable length hash of leaf certificate raw tbs data sha256Hash.in <== TBS1Data; - sha256Hash.len <== TBS1Size; - - bitsToWords[0] = BitStreamToWords(256); - bitsToWords[0].in <== sha256Hash.out; - + sha256Hash.len <== TBS1Size; + padding = PadBits(256, 384); + padding.in <== sha256Hash.out; + bitsToWords[0] = SplitToWords(384, 48, 8); + bitsToWords[0].in <== padding.out; // signature verification with public key of next certificate in chain p384Ecdsa[0] = ECDSAVerifyNoPubkeyCheck(48, 8); - p384Ecdsa[0].msghash <== bitsToWords[0].out; - + p384Ecdsa[0].msghash <== bitsToWords[0].out; p384Ecdsa[0].r <== r[0]; p384Ecdsa[0].s <== s[0]; p384Ecdsa[0].pubkey <== PubKeys[0]; - p384Ecdsa[0].result ==> status[0]; - + p384Ecdsa[0].result ==> status[0]; // const length hash of intermediate certificate raw tbs data - sha384Hash[0] = Sha384_hash_bytes_digest(TBS2Size); - sha384Hash[0].inp_bytes <== TBS2Data; - - bitsToWords[1] = BitStreamToWords(384); - bitsToWords[1].in <== sha384Hash[0].hash_bytes; - + sha384Hash[0] = Sha384_hash_bits_digest(TBS2Size); + sha384Hash[0].inp_bits <== TBS2Data; + bytesToBits[0] = BytesToBits(48); + bytesToBits[0].in <== sha384Hash[0].hash_bytes; + bitsToWords[1] = SplitToWords(384, 48, 8); + bitsToWords[1].in <== bytesToBits[0].out; p384Ecdsa[1] = ECDSAVerifyNoPubkeyCheck(48, 8); p384Ecdsa[1].msghash <== bitsToWords[1].out; p384Ecdsa[1].r <== r[1]; p384Ecdsa[1].s <== s[1]; p384Ecdsa[1].pubkey <== PubKeys[1]; p384Ecdsa[1].result ==> status[1]; - - - // variable length hash of root certificate raw tbs data - sha384Hash[1] = Sha384_hash_bytes_digest(TBS3Size); - sha384Hash[1].inp_bytes <== TBS3Data; - - bitsToWords[2] = BitStreamToWords(384); - bitsToWords[2].in <== sha384Hash[1].hash_bytes; - + // const length hash of root certificate raw tbs data + sha384Hash[1] = Sha384_hash_bits_digest(TBS3Size); + sha384Hash[1].inp_bits <== TBS3Data; + bytesToBits[1] = BytesToBits(48); + bytesToBits[1].in <== sha384Hash[1].hash_bytes; + bitsToWords[2] = SplitToWords(384, 48, 8); + bitsToWords[2].in <== bytesToBits[1].out; p384Ecdsa[2] = ECDSAVerifyNoPubkeyCheck(48, 8); p384Ecdsa[2].msghash <== bitsToWords[2].out; p384Ecdsa[2].r <== r[2]; p384Ecdsa[2].s <== s[2]; p384Ecdsa[2].pubkey <== PubKeys[2]; p384Ecdsa[2].result ==> status[2]; - out <== status[0] + status[1] + status[2]; - out === 3; } \ No newline at end of file diff --git a/circuits/sha256-var b/circuits/sha256-var deleted file mode 160000 index ba32596..0000000 --- a/circuits/sha256-var +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ba325965578e83520073ec946d16889c1528f85b diff --git a/circuits/sha256-var-module b/circuits/sha256-var-module new file mode 160000 index 0000000..cee3d34 --- /dev/null +++ b/circuits/sha256-var-module @@ -0,0 +1 @@ +Subproject commit cee3d342d07f43f50d83e1db8df3d6515feb938e diff --git a/circuits/utils.circom b/circuits/utils.circom index 1dd8fcb..fef876a 100644 --- a/circuits/utils.circom +++ b/circuits/utils.circom @@ -35,13 +35,15 @@ template BytesToBits(nBytes) { } template PadBits(nBits,target){ + assert(nBits <= target); + signal input in[nBits]; signal output out[target]; for (var i=0; i < target-nBits; i++) { - out[i] <== in[i]; + out[i] <== 0; } for (var i= target-nBits; i < target; i++) { - out[i] <== 0; + out[i] <== in[i-(target-nBits)]; } } diff --git a/tests/attestation.test.ts b/tests/attestation.test.ts index 1181e75..4553346 100644 --- a/tests/attestation.test.ts +++ b/tests/attestation.test.ts @@ -29,15 +29,22 @@ describe("Attestation", () => { console.log(id, "isValid:", isValid); }; describe("VerifyCertChain", () => { - let circuit: WitnessTester<["r", "s", "TBSData", "PubKeys"], ["out"]>; + let circuit: WitnessTester<["r", "s", "TBS1Size", "TBS1Data", "TBS2Data", "TBS3Data", "PubKeys"], ["out"]>; - const MAX_CERT_CHAIN_LEN = 100; + const tbs2 = Buffer.from( + "308201c8a003020102021009bac5e1bc401ad9d45395bc381a0854300a06082a8648ce3d04030330523126302406035504030c1d4170706c6520417070204174746573746174696f6e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3230303331383138333935355a170d3330303331333030303030305a304f3123302106035504030c1a4170706c6520417070204174746573746174696f6e204341203131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613076301006072a8648ce3d020106052b8104002203620004ae5b37a0774d79b2358f40e7d1f22626f1c25fef17802deab3826a59874ff8d2ad1525789aa26604191248b63cb967069e98d363bd5e370fbfa08e329e8073a985e7746ea359a2f66f29db32af455e211658d567af9e267eb2614dc21a66ce99a366306430120603551d130101ff040830060101ff020100301f0603551d23041830168014ac91105333bdbe6841ffa70ca9e5faeae5e58aa1301d0603551d0e041604143ee35d1c0419a9c9b431f88474d6e1e15772e39b300e0603551d0f0101ff040403020106", + "hex" + ); + const tbs3 = Buffer.from( + "308201a7a00302010202100bf3be0ef1cdd2e0fb8c6e721f621798300a06082a8648ce3d04030330523126302406035504030c1d4170706c6520417070204174746573746174696f6e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3230303331383138333235335a170d3435303331353030303030305a30523126302406035504030c1d4170706c6520417070204174746573746174696f6e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613076301006072a8648ce3d020106052b81040022036200044531e198b5b4ec04da1502045704ed4f877272d76135b26116cfc88b615d0a000719ba69858dfe77caa3b839e020ddd656141404702831e43f70b88fd6c394b608ea2bd6ae61e9f598c12f46af52937266e57f14eb61fec530f7144f53812e35a3423040300f0603551d130101ff040530030101ff301d0603551d0e04160414ac91105333bdbe6841ffa70ca9e5faeae5e58aa1300e0603551d0f0101ff040403020106", + "hex" + ); before(async () => { circuit = await circomkit.WitnessTester(`VerifyCertChain`, { file: "attestation", template: "VerifyCertChain", - params: [MAX_CERT_CHAIN_LEN], + params: [tbs2.length * 8, tbs3.length * 8, 4], }); console.log("#constraints:", await circuit.getConstraintCount()); }); @@ -63,14 +70,6 @@ describe("Attestation", () => { "30820339a0030201020206018ef1fd4d4a300a06082a8648ce3d040302304f3123302106035504030c1a4170706c6520417070204174746573746174696f6e204341203131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3234303431373136313435335a170d3234303432303136313435335a3081913149304706035504030c4036643261633438343566313332333332326635393233663062643964323264626535306530366237623830313231666365326232623565363665396539386436311a3018060355040b0c114141412043657274696669636174696f6e31133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613059301306072a8648ce3d020106082a8648ce3d030107034200048c2e0cab6f9223970e7f5ab6e92fd7a4d6d621a60e548644bf19764ef1ef853611f6c2b6bb53b2bba2d3468197e4beab2c36cac0e4e24f41f35132c9475e5c24a38201bc308201b8300c0603551d130101ff04023000300e0603551d0f0101ff0404030204f030818806092a864886f763640805047b3079a40302010abf893003020101bf893103020100bf893203020101bf893303020101bf8934290427303335323138373339312e636f6d2e6170706c652e6578616d706c655f6170705f617474657374a5060404736b7320bf893603020105bf893703020100bf893903020100bf893a03020100bf893b030201003081d706092a864886f7636408070481c93081c6bf8a7806040431382e30bf885007020500ffffffffbf8a7b09040732324132343462bf8a7c06040431382e30bf8a7d06040431382e30bf8a7e03020100bf8a7f03020100bf8b0003020100bf8b0103020100bf8b0203020100bf8b0303020100bf8b0403020101bf8b0503020100bf8b0a10040e32322e312e3234342e302e322c30bf8b0b10040e32322e312e3234342e302e322c30bf8b0c10040e32322e312e3234342e302e322c30bf88020a04086970686f6e656f73bf88050a0408496e7465726e616c303306092a864886f76364080204263024a1220420fb6d162a717ecab1778900506fa94d67ee0c1dc3d45b12cdde81befc56e5b7eb", "hex" ); - const tbs2 = Buffer.from( - "308201c8a003020102021009bac5e1bc401ad9d45395bc381a0854300a06082a8648ce3d04030330523126302406035504030c1d4170706c6520417070204174746573746174696f6e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3230303331383138333935355a170d3330303331333030303030305a304f3123302106035504030c1a4170706c6520417070204174746573746174696f6e204341203131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613076301006072a8648ce3d020106052b8104002203620004ae5b37a0774d79b2358f40e7d1f22626f1c25fef17802deab3826a59874ff8d2ad1525789aa26604191248b63cb967069e98d363bd5e370fbfa08e329e8073a985e7746ea359a2f66f29db32af455e211658d567af9e267eb2614dc21a66ce99a366306430120603551d130101ff040830060101ff020100301f0603551d23041830168014ac91105333bdbe6841ffa70ca9e5faeae5e58aa1301d0603551d0e041604143ee35d1c0419a9c9b431f88474d6e1e15772e39b300e0603551d0f0101ff040403020106", - "hex" - ); - const tbs3 = Buffer.from( - "308201a7a00302010202100bf3be0ef1cdd2e0fb8c6e721f621798300a06082a8648ce3d04030330523126302406035504030c1d4170706c6520417070204174746573746174696f6e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e6961301e170d3230303331383138333235335a170d3435303331353030303030305a30523126302406035504030c1d4170706c6520417070204174746573746174696f6e20526f6f7420434131133011060355040a0c0a4170706c6520496e632e3113301106035504080c0a43616c69666f726e69613076301006072a8648ce3d020106052b81040022036200044531e198b5b4ec04da1502045704ed4f877272d76135b26116cfc88b615d0a000719ba69858dfe77caa3b839e020ddd656141404702831e43f70b88fd6c394b608ea2bd6ae61e9f598c12f46af52937266e57f14eb61fec530f7144f53812e35a3423040300f0603551d130101ff040530030101ff301d0603551d0e04160414ac91105333bdbe6841ffa70ca9e5faeae5e58aa1300e0603551d0f0101ff040403020106", - "hex" - ); const r = [ splitToWords(hexToBigInt(r1), 48n, 8n), @@ -99,7 +98,10 @@ describe("Attestation", () => { { r, s, - TBSData, + TBS1Size: tbs1.length * 8, + TBS1Data: bufferToBitArray(tbs1), + TBS2Data: bufferToBitArray(tbs2), + TBS3Data: bufferToBitArray(tbs3), PubKeys, }, { out: 3n } @@ -108,4 +110,12 @@ describe("Attestation", () => { }); }); - +function bufferToBitArray(b: Buffer): number[] { + let res: number[] = []; + for (let i = 0; i < b.length; i++) { + for (let j = 0; j < 8; j++) { + res.push((b[i] >> (7 - j)) & 1); + } + } + return res; +} diff --git a/tests/utils.test.ts b/tests/utils.test.ts index 90d9969..340414d 100644 --- a/tests/utils.test.ts +++ b/tests/utils.test.ts @@ -36,24 +36,24 @@ describe("PadBits", () => { circuit = await circomkit.WitnessTester(`PadBits`, { file: "utils", template: "PadBits", - params: [4, 8], + params: [6, 8], }); console.log("#constraints:", await circuit.getConstraintCount()); }); it("should pad bits correctly", async () => { - const inputBits = [1, 0, 1, 1]; - const expectedOutput = [1, 0, 1, 1, 0, 0, 0, 0]; + const inputBits = [1, 0, 1, 1, 0, 1]; + const expectedOutput = [0, 0, 1, 0, 1, 1, 0, 1]; await circuit.expectPass({ in: inputBits }, { out: expectedOutput }); }); }); function byteToBits(byte: number): number[] { let bits = []; - for (let i = 7; i >= 0; i--) { + for (let i = 0; i >= 7; i++) { bits.push((byte >> i) & 1); } - return bits.reverse(); + return bits; } describe("BytesToBits", () => { From c94420dd8e6ba8bd3bdad7b133a08cc34225f6cf Mon Sep 17 00:00:00 2001 From: yash1io Date: Sun, 8 Sep 2024 20:42:31 +0530 Subject: [PATCH 11/14] update circom version in workflows --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9e32850..ff65482 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,9 +22,9 @@ jobs: nasm \ nlohmann-json3-dev - - name: Download Circom Binary v2.1.5 + - name: Download Circom Binary v2.1.9 run: | - wget -qO /home/runner/work/circom https://github.com/iden3/circom/releases/download/v2.1.5/circom-linux-amd64 + wget -qO /home/runner/work/circom https://github.com/iden3/circom/releases/download/v2.1.9/circom-linux-amd64 chmod +x /home/runner/work/circom sudo mv /home/runner/work/circom /bin/circom From a9943b0b4ae855a0cc44afa94e665c43a6ffe866 Mon Sep 17 00:00:00 2001 From: yash1io Date: Sun, 8 Sep 2024 21:48:01 +0530 Subject: [PATCH 12/14] fix tbs1data size --- tests/attestation.test.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/attestation.test.ts b/tests/attestation.test.ts index 4553346..b4364ff 100644 --- a/tests/attestation.test.ts +++ b/tests/attestation.test.ts @@ -40,11 +40,13 @@ describe("Attestation", () => { "hex" ); + const blockspace = 4; + before(async () => { circuit = await circomkit.WitnessTester(`VerifyCertChain`, { file: "attestation", template: "VerifyCertChain", - params: [tbs2.length * 8, tbs3.length * 8, 4], + params: [tbs2.length * 8, tbs3.length * 8, blockspace], }); console.log("#constraints:", await circuit.getConstraintCount()); }); @@ -99,7 +101,7 @@ describe("Attestation", () => { r, s, TBS1Size: tbs1.length * 8, - TBS1Data: bufferToBitArray(tbs1), + TBS1Data: padSuffix(bufferToBitArray(tbs1), (512 * 1) << blockspace), TBS2Data: bufferToBitArray(tbs2), TBS3Data: bufferToBitArray(tbs3), PubKeys, @@ -119,3 +121,7 @@ function bufferToBitArray(b: Buffer): number[] { } return res; } + +function padSuffix(bits: number[], length: number): number[] { + return bits.concat(Array(length - bits.length).fill(0)); +} From 855ec38db1a85c1edbf8b3f7db31170af4ce7a3f Mon Sep 17 00:00:00 2001 From: Vikas Rushi <0xvikas@gmail.com> Date: Wed, 11 Sep 2024 09:49:41 +0000 Subject: [PATCH 13/14] fix: attestation circuit --- circuits/attestation.circom | 85 ++++++++++++++----------------- circuits/ecdsa-with-sha384.circom | 31 +++++++++++ circuits/utils.circom | 16 +++--- tests/utils.test.ts | 15 ++++-- 4 files changed, 89 insertions(+), 58 deletions(-) create mode 100644 circuits/ecdsa-with-sha384.circom diff --git a/circuits/attestation.circom b/circuits/attestation.circom index f594929..761bc93 100644 --- a/circuits/attestation.circom +++ b/circuits/attestation.circom @@ -1,10 +1,10 @@ pragma circom 2.1.9; include "@crema-labs/ecdsa-p384-circom/circuits/ecdsa.circom"; -include "./hash-circuits/circuits/sha2/sha384/sha384_hash_bits.circom"; -include "./sha256-var-module/circuits/sha256Var.circom"; -include "./utils.circom"; - +include "hash-circuits/circuits/sha2/sha384/sha384_hash_bits.circom"; +include "sha256-var-module/circuits/sha256Var.circom"; +include "utils.circom"; +include "ecdsa-with-sha384.circom"; template VerifyCertChain(TBS2Size,TBS3Size,BlockSpace){ signal input r[3][8]; @@ -22,52 +22,45 @@ template VerifyCertChain(TBS2Size,TBS3Size,BlockSpace){ signal output out; signal status[3]; - component sha384Hash[2]; - component p384Ecdsa[3]; - component bitsToWords[3]; - component padding; - component bytesToBits[2]; - component sha256Hash = Sha256Var(4); // 4 bits for 16 blocks each of 512 bits + + component ecdsaWith384[2]; + component sha256Hash = Sha256Var(4); + + // 4 bits for 16 blocks each of 512 bits // variable length hash of leaf certificate raw tbs data sha256Hash.in <== TBS1Data; sha256Hash.len <== TBS1Size; - padding = PadBits(256, 384); + component padding = PadBits(256, 384); padding.in <== sha256Hash.out; - bitsToWords[0] = SplitToWords(384, 48, 8); - bitsToWords[0].in <== padding.out; + component bitsToWords = SplitToWords(384, 48, 8); + bitsToWords.in <== padding.out; + // signature verification with public key of next certificate in chain - p384Ecdsa[0] = ECDSAVerifyNoPubkeyCheck(48, 8); - p384Ecdsa[0].msghash <== bitsToWords[0].out; - p384Ecdsa[0].r <== r[0]; - p384Ecdsa[0].s <== s[0]; - p384Ecdsa[0].pubkey <== PubKeys[0]; - p384Ecdsa[0].result ==> status[0]; - // const length hash of intermediate certificate raw tbs data - sha384Hash[0] = Sha384_hash_bits_digest(TBS2Size); - sha384Hash[0].inp_bits <== TBS2Data; - bytesToBits[0] = BytesToBits(48); - bytesToBits[0].in <== sha384Hash[0].hash_bytes; - bitsToWords[1] = SplitToWords(384, 48, 8); - bitsToWords[1].in <== bytesToBits[0].out; - p384Ecdsa[1] = ECDSAVerifyNoPubkeyCheck(48, 8); - p384Ecdsa[1].msghash <== bitsToWords[1].out; - p384Ecdsa[1].r <== r[1]; - p384Ecdsa[1].s <== s[1]; - p384Ecdsa[1].pubkey <== PubKeys[1]; - p384Ecdsa[1].result ==> status[1]; - // const length hash of root certificate raw tbs data - sha384Hash[1] = Sha384_hash_bits_digest(TBS3Size); - sha384Hash[1].inp_bits <== TBS3Data; - bytesToBits[1] = BytesToBits(48); - bytesToBits[1].in <== sha384Hash[1].hash_bytes; - bitsToWords[2] = SplitToWords(384, 48, 8); - bitsToWords[2].in <== bytesToBits[1].out; - p384Ecdsa[2] = ECDSAVerifyNoPubkeyCheck(48, 8); - p384Ecdsa[2].msghash <== bitsToWords[2].out; - p384Ecdsa[2].r <== r[2]; - p384Ecdsa[2].s <== s[2]; - p384Ecdsa[2].pubkey <== PubKeys[2]; - p384Ecdsa[2].result ==> status[2]; + component p384Ecdsa = ECDSAVerifyNoPubkeyCheck(48, 8); + p384Ecdsa.msghash <== bitsToWords.out; + p384Ecdsa.r <== r[0]; + p384Ecdsa.s <== s[0]; + p384Ecdsa.pubkey <== PubKeys[0]; + p384Ecdsa.result ==> status[0]; + p384Ecdsa.result === 1; + + + ecdsaWith384[0] = Sha384ECDSAVerify(TBS2Size); + ecdsaWith384[0].TBSData <== TBS2Data; + ecdsaWith384[0].r <== r[1]; + ecdsaWith384[0].s <== s[1]; + ecdsaWith384[0].PubKey <== PubKeys[1]; + ecdsaWith384[0].status ==> status[1]; + ecdsaWith384[0].status === 1; + + + ecdsaWith384[1] = Sha384ECDSAVerify(TBS3Size); + ecdsaWith384[1].TBSData <== TBS3Data; + ecdsaWith384[1].r <== r[2]; + ecdsaWith384[1].s <== s[2]; + ecdsaWith384[1].PubKey <== PubKeys[2]; + ecdsaWith384[1].status ==> status[2]; + ecdsaWith384[1].status === 1; + out <== status[0] + status[1] + status[2]; - out === 3; } \ No newline at end of file diff --git a/circuits/ecdsa-with-sha384.circom b/circuits/ecdsa-with-sha384.circom new file mode 100644 index 0000000..d5df455 --- /dev/null +++ b/circuits/ecdsa-with-sha384.circom @@ -0,0 +1,31 @@ +pragma circom 2.1.9; + +include "@crema-labs/ecdsa-p384-circom/circuits/ecdsa.circom"; +include "hash-circuits/circuits/sha2/sha384/sha384_hash_bits.circom"; +include "utils.circom"; + +template Sha384ECDSAVerify(TBSSize){ + signal input TBSData[TBSSize]; + + signal input r[8]; + signal input s[8]; + + signal input PubKey[2][8]; + signal output status; + + component sha384Hash = Sha384_hash_bits_digest(TBSSize); + sha384Hash.inp_bits <== TBSData; + + component bytesToBits = BytesToBits(48); + bytesToBits.in <== sha384Hash.hash_bytes; + + component bitsToWords = SplitToWords(384, 48, 8); + bitsToWords.in <== bytesToBits.out; + + component p384Ecdsa = ECDSAVerifyNoPubkeyCheck(48, 8); + p384Ecdsa.msghash <== bitsToWords.out; + p384Ecdsa.r <== r; + p384Ecdsa.s <== s; + p384Ecdsa.pubkey <== PubKey; + p384Ecdsa.result ==> status; +} \ No newline at end of file diff --git a/circuits/utils.circom b/circuits/utils.circom index fef876a..51016e7 100644 --- a/circuits/utils.circom +++ b/circuits/utils.circom @@ -22,14 +22,16 @@ template SplitToWords(nBits, wordsize, numberElement) { template BytesToBits(nBytes) { signal input in[nBytes]; signal output out[nBytes*8]; - - component NumToBits[nBytes]; - + + component num2Bits[nBytes]; + for (var i=0; i < nBytes; i++) { - NumToBits[i] = Num2Bits(8); - NumToBits[i].in <== in[i]; - for (var j=0; j < 8; j++) { - out[i*8 + j] <== NumToBits[i].out[j]; + num2Bits[i] = Num2Bits(8); + num2Bits[i].in <== in[i]; + for (var j=0; j < 8; j++) { + out[i*8 + (7-j)] <== num2Bits[i].out[j]; + // For big-endian order ==> out[i*8 + (7-j)] + // For little-endian order ==> use: out[i*8 + j] } } } diff --git a/tests/utils.test.ts b/tests/utils.test.ts index 340414d..8415984 100644 --- a/tests/utils.test.ts +++ b/tests/utils.test.ts @@ -48,9 +48,10 @@ describe("PadBits", () => { }); }); + function byteToBits(byte: number): number[] { let bits = []; - for (let i = 0; i >= 7; i++) { + for (let i = 7; i >= 0; i--) { bits.push((byte >> i) & 1); } return bits; @@ -58,19 +59,23 @@ function byteToBits(byte: number): number[] { describe("BytesToBits", () => { let circuit: WitnessTester<["in"], ["out"]>; - const testBytes = [0xa5, 0x53, 0x21, 0x12]; + + const sha384BytesDigest = [ + 237, 100, 209, 7, 109, 211, 190, 176, 239, 60, 81, 47, 203, 179, 183, 52, 170, 110, 108, 58, 200, 31, 50, 225, 2, + 235, 178, 131, 102, 79, 129, 147, 34, 125, 222, 195, 183, 42, 75, 123, 153, 33, 60, 250, 207, 8, 238, 37, + ]; before(async () => { circuit = await circomkit.WitnessTester(`BytesToBits`, { file: "utils", template: "BytesToBits", - params: [testBytes.length], + params: [sha384BytesDigest.length], }); console.log("#constraints:", await circuit.getConstraintCount()); }); it("should convert bytes to bits", async () => { - const expectedBits = testBytes.map((byte) => byteToBits(byte)).flat(); - await circuit.expectPass({ in: testBytes }, { out: expectedBits }); + const expectedBits = sha384BytesDigest.map((byte) => byteToBits(byte)).flat(); + await circuit.expectPass({ in: sha384BytesDigest }, { out: expectedBits }); }); }); From ad5f58a18f585139e57c0753c539329c0ec39fb2 Mon Sep 17 00:00:00 2001 From: Vikas Rushi <0xvikas@gmail.com> Date: Wed, 11 Sep 2024 15:29:16 +0530 Subject: [PATCH 14/14] Update tests.yml --- .github/workflows/tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ff65482..dbdedf9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,6 +30,9 @@ jobs: - name: Print Circom version run: circom --version + + - name: Download Hash Circuits repo + run: git submodule update --init --recursive - name: Install dependencies run: yarn