From fbdf5bca0658d559fb7808cb32e7dfb62db06175 Mon Sep 17 00:00:00 2001 From: Saleel Date: Sun, 8 Sep 2024 23:56:43 +0530 Subject: [PATCH] add prettier --- .prettierignore | 1 + .prettierrc | 5 + package.json | 4 + packages/circuits/tests/base64.test.ts | 87 +++-- packages/circuits/tests/byte-mask.test.ts | 69 ++-- .../tests/email-verifier-no-body.test.ts | 54 ++- .../email-verifier-with-body-mask.test.ts | 79 ++--- .../email-verifier-with-header-mask.test.ts | 79 ++--- ...ail-verifier-with-soft-line-breaks.test.ts | 59 ++-- .../circuits/tests/email-verifier.test.ts | 326 ++++++++---------- .../tests/remove-soft-line-breaks.test.ts | 127 ++----- packages/circuits/tests/rsa.test.ts | 239 ++++++------- .../tests/select-regex-reveal.test.ts | 182 +++++----- packages/circuits/tests/sha.test.ts | 55 ++- .../tests/split-bytes-to-words.test.ts | 40 +-- packages/helpers/src/binary-format.ts | 28 +- packages/helpers/src/chunked-zkey.ts | 82 ++--- packages/helpers/src/dkim/index.ts | 52 ++- packages/helpers/src/dkim/sanitizers.ts | 22 +- packages/helpers/src/hash.ts | 4 +- packages/helpers/src/index.ts | 8 +- packages/helpers/src/input-generators.ts | 47 +-- packages/helpers/src/sha-utils.ts | 29 +- .../helpers/tests/__mocks__/localforage.ts | 26 +- packages/helpers/tests/chunked-zkey.test.ts | 50 +-- packages/helpers/tests/dkim.test.ts | 68 ++-- .../helpers/tests/input-generators.test.ts | 22 +- scripts/dkim/update-dkim-registry.ts | 183 +++++----- yarn.lock | 11 + 29 files changed, 864 insertions(+), 1174 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..56756a4b4 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +packages/helpers/src/lib \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..28d58dec6 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "semi": true, + "tabWidth": 2, + "printWidth": 120 +} diff --git a/package.json b/package.json index b12be1360..3c3d6a6b7 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "license": "MIT", "private": true, "scripts": { + "lint": "yarn prettier --write packages/**/**.ts", "test": "jest" }, "workspaces": [ @@ -12,5 +13,8 @@ "packageManager": "yarn@3.2.3", "engines": { "node": ">=14.0.0" + }, + "devDependencies": { + "prettier": "^3.3.3" } } diff --git a/packages/circuits/tests/base64.test.ts b/packages/circuits/tests/base64.test.ts index 4088741e1..8be14c26d 100644 --- a/packages/circuits/tests/base64.test.ts +++ b/packages/circuits/tests/base64.test.ts @@ -2,56 +2,53 @@ import { wasm } from "circom_tester"; import path from "path"; describe("Base64 Lookup", () => { - jest.setTimeout(30 * 60 * 1000); // 30 minutes + jest.setTimeout(30 * 60 * 1000); // 30 minutes - let circuit: any; + let circuit: any; - beforeAll(async () => { - circuit = await wasm( - path.join(__dirname, "./test-circuits/base64-test.circom"), - { - recompile: true, - include: path.join(__dirname, "../../../node_modules"), - // output: path.join(__dirname, "./compiled-test-circuits"), - } - ); + beforeAll(async () => { + circuit = await wasm(path.join(__dirname, "./test-circuits/base64-test.circom"), { + recompile: true, + include: path.join(__dirname, "../../../node_modules"), + // output: path.join(__dirname, "./compiled-test-circuits"), }); + }); - it("should decode valid base64 chars", async function () { - const inputs = [ - [65, 0], // A - [90, 25], // Z - [97, 26], // a - [122, 51], // z - [48, 52], // 0 - [57, 61], // 9 - [43, 62], // + - [47, 63], // / - [61, 0], // = - ]; + it("should decode valid base64 chars", async function () { + const inputs = [ + [65, 0], // A + [90, 25], // Z + [97, 26], // a + [122, 51], // z + [48, 52], // 0 + [57, 61], // 9 + [43, 62], // + + [47, 63], // / + [61, 0], // = + ]; - for (const [input, output] of inputs) { - const witness = await circuit.calculateWitness({ - in: input, - }); - await circuit.checkConstraints(witness); - await circuit.assertOut(witness, { out: output }); - } - }); + for (const [input, output] of inputs) { + const witness = await circuit.calculateWitness({ + in: input, + }); + await circuit.checkConstraints(witness); + await circuit.assertOut(witness, { out: output }); + } + }); - it("should fail with invalid chars", async function () { - const inputs = [34, 64, 91, 44]; + it("should fail with invalid chars", async function () { + const inputs = [34, 64, 91, 44]; - expect.assertions(inputs.length); - for (const input of inputs) { - try { - const witness = await circuit.calculateWitness({ - in: input, - }); - await circuit.checkConstraints(witness); - } catch (error) { - expect((error as Error).message).toMatch("Assert Failed"); - } - } - }); + expect.assertions(inputs.length); + for (const input of inputs) { + try { + const witness = await circuit.calculateWitness({ + in: input, + }); + await circuit.checkConstraints(witness); + } catch (error) { + expect((error as Error).message).toMatch("Assert Failed"); + } + } + }); }); diff --git a/packages/circuits/tests/byte-mask.test.ts b/packages/circuits/tests/byte-mask.test.ts index dbfa8b33d..f76e2b6e2 100644 --- a/packages/circuits/tests/byte-mask.test.ts +++ b/packages/circuits/tests/byte-mask.test.ts @@ -2,48 +2,45 @@ import { wasm as wasm_tester } from "circom_tester"; import path from "path"; describe("ByteMask Circuit", () => { - let circuit: any; + let circuit: any; - beforeAll(async () => { - circuit = await wasm_tester( - path.join(__dirname, "./test-circuits/byte-mask-test.circom"), - { - recompile: true, - include: path.join(__dirname, "../../../node_modules"), - output: path.join(__dirname, "./compiled-test-circuits"), - } - ); + beforeAll(async () => { + circuit = await wasm_tester(path.join(__dirname, "./test-circuits/body-masker-test.circom"), { + recompile: true, + include: path.join(__dirname, "../../../node_modules"), + output: path.join(__dirname, "./compiled-test-circuits"), }); + }); - it("should mask the body correctly", async () => { - const input = { - in: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - mask: [1, 0, 1, 0, 1, 0, 1, 0, 1, 0], - }; + it("should mask the body correctly", async () => { + const input = { + in: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + mask: [1, 0, 1, 0, 1, 0, 1, 0, 1, 0], + }; - const witness = await circuit.calculateWitness(input); - await circuit.checkConstraints(witness); - await circuit.assertOut(witness, { - out: [1, 0, 3, 0, 5, 0, 7, 0, 9, 0], - }); + const witness = await circuit.calculateWitness(input); + await circuit.checkConstraints(witness); + await circuit.assertOut(witness, { + out: [1, 0, 3, 0, 5, 0, 7, 0, 9, 0], }); + }); - it("should fail if mask has non-bit numbers", async () => { - const input = { - body: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], - mask: [1, 2, 1, 0, 1, 0, 1, 0, 1, 0], // Mask with non-bit number (2) - }; + it("should fail if mask has non-bit numbers", async () => { + const input = { + body: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + mask: [1, 2, 1, 0, 1, 0, 1, 0, 1, 0], // Mask with non-bit number (2) + }; - try { - const witness = await circuit.calculateWitness(input); - await circuit.checkConstraints(witness); - await circuit.assertOut(witness, { - maskedBody: [1, 0, 3, 0, 5, 0, 7, 0, 9, 0], - }); - } catch (error) { - expect(error).toBeTruthy(); - } + try { + const witness = await circuit.calculateWitness(input); + await circuit.checkConstraints(witness); + await circuit.assertOut(witness, { + maskedBody: [1, 0, 3, 0, 5, 0, 7, 0, 9, 0], + }); + } catch (error) { + expect(error).toBeTruthy(); + } - expect.assertions(1); - }); + expect.assertions(1); + }); }); diff --git a/packages/circuits/tests/email-verifier-no-body.test.ts b/packages/circuits/tests/email-verifier-no-body.test.ts index 102621302..ebf80de84 100644 --- a/packages/circuits/tests/email-verifier-no-body.test.ts +++ b/packages/circuits/tests/email-verifier-no-body.test.ts @@ -6,43 +6,31 @@ import { generateEmailVerifierInputsFromDKIMResult } from "@zk-email/helpers/src import { verifyDKIMSignature } from "@zk-email/helpers/src/dkim"; describe("EmailVerifier : Without body check", () => { - jest.setTimeout(30 * 60 * 1000); // 30 minutes + jest.setTimeout(30 * 60 * 1000); // 30 minutes - let dkimResult: DKIMVerificationResult; - let circuit: any; + let dkimResult: DKIMVerificationResult; + let circuit: any; - beforeAll(async () => { - const rawEmail = fs.readFileSync( - path.join(__dirname, "./test-emails/test.eml"), - "utf8" - ); - dkimResult = await verifyDKIMSignature(rawEmail); + beforeAll(async () => { + const rawEmail = fs.readFileSync(path.join(__dirname, "./test-emails/test.eml"), "utf8"); + dkimResult = await verifyDKIMSignature(rawEmail); - circuit = await wasm_tester( - path.join( - __dirname, - "./test-circuits/email-verifier-no-body-test.circom" - ), - { - recompile: true, - include: path.join(__dirname, "../../../node_modules"), - // output: path.join(__dirname, "./compiled-test-circuits"), - } - ); + circuit = await wasm_tester(path.join(__dirname, "./test-circuits/email-verifier-no-body-test.circom"), { + recompile: true, + include: path.join(__dirname, "../../../node_modules"), + // output: path.join(__dirname, "./compiled-test-circuits"), }); + }); - it("should verify email when ignore_body_hash_check is true", async function () { - // The result wont have shaPrecomputeSelector, maxHeadersLength, maxBodyLength, ignoreBodyHashCheck - const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult( - dkimResult, - { - maxHeadersLength: 640, - maxBodyLength: 768, - ignoreBodyHashCheck: true, - } - ); - - const witness = await circuit.calculateWitness(emailVerifierInputs); - await circuit.checkConstraints(witness); + it("should verify email when ignore_body_hash_check is true", async function () { + // The result wont have shaPrecomputeSelector, maxHeadersLength, maxBodyLength, ignoreBodyHashCheck + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkimResult, { + maxHeadersLength: 640, + maxBodyLength: 768, + ignoreBodyHashCheck: true, }); + + const witness = await circuit.calculateWitness(emailVerifierInputs); + await circuit.checkConstraints(witness); + }); }); diff --git a/packages/circuits/tests/email-verifier-with-body-mask.test.ts b/packages/circuits/tests/email-verifier-with-body-mask.test.ts index f6c525c8b..68e26b987 100644 --- a/packages/circuits/tests/email-verifier-with-body-mask.test.ts +++ b/packages/circuits/tests/email-verifier-with-body-mask.test.ts @@ -6,54 +6,39 @@ import { generateEmailVerifierInputsFromDKIMResult } from "@zk-email/helpers/src import { verifyDKIMSignature } from "@zk-email/helpers/src/dkim"; describe("EmailVerifier : With body masking", () => { - jest.setTimeout(30 * 60 * 1000); // 30 minutes - - let dkimResult: DKIMVerificationResult; - let circuit: any; - - beforeAll(async () => { - const rawEmail = fs.readFileSync( - path.join(__dirname, "./test-emails/test.eml") - ); - dkimResult = await verifyDKIMSignature(rawEmail); - - circuit = await wasm_tester( - path.join( - __dirname, - "./test-circuits/email-verifier-with-body-mask-test.circom" - ), - { - recompile: true, - include: path.join(__dirname, "../../../node_modules"), - output: path.join(__dirname, "./compiled-test-circuits"), - } - ); + jest.setTimeout(30 * 60 * 1000); // 30 minutes + + let dkimResult: DKIMVerificationResult; + let circuit: any; + + beforeAll(async () => { + const rawEmail = fs.readFileSync(path.join(__dirname, "./test-emails/test.eml")); + dkimResult = await verifyDKIMSignature(rawEmail); + + circuit = await wasm_tester(path.join(__dirname, "./test-circuits/email-verifier-with-body-mask-test.circom"), { + recompile: true, + include: path.join(__dirname, "../../../node_modules"), + output: path.join(__dirname, "./compiled-test-circuits"), }); + }); + + it("should verify email with body masking", async function () { + const mask = Array.from({ length: 768 }, (_, i) => (i > 25 && i < 50 ? 1 : 0)); + + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkimResult, { + maxHeadersLength: 640, + maxBodyLength: 768, + ignoreBodyHashCheck: false, + enableBodyMasking: true, + bodyMask: mask.map((value) => (value ? 1 : 0)), + }); + + const expectedMaskedBody = emailVerifierInputs.emailBody!.map((byte, i) => (mask[i] === 1 ? byte : 0)); - it("should verify email with body masking", async function () { - const mask = Array.from({ length: 768 }, (_, i) => - i > 25 && i < 50 ? 1 : 0 - ); - - const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult( - dkimResult, - { - maxHeadersLength: 640, - maxBodyLength: 768, - ignoreBodyHashCheck: false, - enableBodyMasking: true, - bodyMask: mask.map((value) => (value ? 1 : 0)), - } - ); - - const expectedMaskedBody = emailVerifierInputs.emailBody!.map( - (byte, i) => (mask[i] === 1 ? byte : 0) - ); - - const witness = await circuit.calculateWitness(emailVerifierInputs); - await circuit.checkConstraints(witness); - await circuit.assertOut(witness, { - maskedBody: expectedMaskedBody, - }); + const witness = await circuit.calculateWitness(emailVerifierInputs); + await circuit.checkConstraints(witness); + await circuit.assertOut(witness, { + maskedBody: expectedMaskedBody, }); + }); }); diff --git a/packages/circuits/tests/email-verifier-with-header-mask.test.ts b/packages/circuits/tests/email-verifier-with-header-mask.test.ts index 04f4d5671..5b1e8715f 100644 --- a/packages/circuits/tests/email-verifier-with-header-mask.test.ts +++ b/packages/circuits/tests/email-verifier-with-header-mask.test.ts @@ -6,54 +6,39 @@ import { generateEmailVerifierInputsFromDKIMResult } from "@zk-email/helpers/src import { verifyDKIMSignature } from "@zk-email/helpers/src/dkim"; describe("EmailVerifier : With header masking", () => { - jest.setTimeout(30 * 60 * 1000); // 30 minutes - - let dkimResult: DKIMVerificationResult; - let circuit: any; - - beforeAll(async () => { - const rawEmail = fs.readFileSync( - path.join(__dirname, "./test-emails/test.eml") - ); - dkimResult = await verifyDKIMSignature(rawEmail); - - circuit = await wasm_tester( - path.join( - __dirname, - "./test-circuits/email-verifier-with-header-mask-test.circom" - ), - { - recompile: true, - include: path.join(__dirname, "../../../node_modules"), - output: path.join(__dirname, "./compiled-test-circuits"), - } - ); + jest.setTimeout(30 * 60 * 1000); // 30 minutes + + let dkimResult: DKIMVerificationResult; + let circuit: any; + + beforeAll(async () => { + const rawEmail = fs.readFileSync(path.join(__dirname, "./test-emails/test.eml")); + dkimResult = await verifyDKIMSignature(rawEmail); + + circuit = await wasm_tester(path.join(__dirname, "./test-circuits/email-verifier-with-header-mask-test.circom"), { + recompile: true, + include: path.join(__dirname, "../../../node_modules"), + output: path.join(__dirname, "./compiled-test-circuits"), }); + }); + + it("should verify email with header masking", async function () { + const mask = Array.from({ length: 640 }, (_, i) => (i > 25 && i < 50 ? 1 : 0)); + + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkimResult, { + maxHeadersLength: 640, + maxBodyLength: 768, + ignoreBodyHashCheck: false, + enableHeaderMasking: true, + headerMask: mask.map((value) => (value ? 1 : 0)), + }); + + const expectedMaskedHeader = emailVerifierInputs.emailHeader!.map((byte, i) => (mask[i] === 1 ? byte : 0)); - it("should verify email with header masking", async function () { - const mask = Array.from({ length: 640 }, (_, i) => - i > 25 && i < 50 ? 1 : 0 - ); - - const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult( - dkimResult, - { - maxHeadersLength: 640, - maxBodyLength: 768, - ignoreBodyHashCheck: false, - enableHeaderMasking: true, - headerMask: mask.map((value) => (value ? 1 : 0)), - } - ); - - const expectedMaskedHeader = emailVerifierInputs.emailHeader!.map( - (byte, i) => (mask[i] === 1 ? byte : 0) - ); - - const witness = await circuit.calculateWitness(emailVerifierInputs); - await circuit.checkConstraints(witness); - await circuit.assertOut(witness, { - maskedHeader: expectedMaskedHeader, - }); + const witness = await circuit.calculateWitness(emailVerifierInputs); + await circuit.checkConstraints(witness); + await circuit.assertOut(witness, { + maskedHeader: expectedMaskedHeader, }); + }); }); diff --git a/packages/circuits/tests/email-verifier-with-soft-line-breaks.test.ts b/packages/circuits/tests/email-verifier-with-soft-line-breaks.test.ts index 106eb9545..0fd7cdc81 100644 --- a/packages/circuits/tests/email-verifier-with-soft-line-breaks.test.ts +++ b/packages/circuits/tests/email-verifier-with-soft-line-breaks.test.ts @@ -6,43 +6,34 @@ import { generateEmailVerifierInputsFromDKIMResult } from "@zk-email/helpers/src import { verifyDKIMSignature } from "@zk-email/helpers/src/dkim"; describe("EmailVerifier : With soft line breaks", () => { - jest.setTimeout(30 * 60 * 1000); // 30 minutes + jest.setTimeout(30 * 60 * 1000); // 30 minutes - let dkimResult: DKIMVerificationResult; - let circuit: any; + let dkimResult: DKIMVerificationResult; + let circuit: any; - beforeAll(async () => { - const rawEmail = fs.readFileSync( - path.join(__dirname, "./test-emails/lorem_ipsum.eml"), - "utf8" - ); - dkimResult = await verifyDKIMSignature(rawEmail); + beforeAll(async () => { + const rawEmail = fs.readFileSync(path.join(__dirname, "./test-emails/lorem_ipsum.eml"), "utf8"); + dkimResult = await verifyDKIMSignature(rawEmail); - circuit = await wasm_tester( - path.join( - __dirname, - "./test-circuits/email-verifier-with-soft-line-breaks-test.circom" - ), - { - recompile: true, - include: path.join(__dirname, "../../../node_modules"), - output: path.join(__dirname, "./compiled-test-circuits"), - } - ); - }); - - it("should verify email when removeSoftLineBreaks is true", async function () { - const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult( - dkimResult, - { - maxHeadersLength: 640, - maxBodyLength: 1408, - ignoreBodyHashCheck: false, - removeSoftLineBreaks: true, - } - ); + circuit = await wasm_tester( + path.join(__dirname, "./test-circuits/email-verifier-with-soft-line-breaks-test.circom"), + { + recompile: true, + include: path.join(__dirname, "../../../node_modules"), + output: path.join(__dirname, "./compiled-test-circuits"), + }, + ); + }); - const witness = await circuit.calculateWitness(emailVerifierInputs); - await circuit.checkConstraints(witness); + it("should verify email when removeSoftLineBreaks is true", async function () { + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkimResult, { + maxHeadersLength: 640, + maxBodyLength: 1408, + ignoreBodyHashCheck: false, + removeSoftLineBreaks: true, }); + + const witness = await circuit.calculateWitness(emailVerifierInputs); + await circuit.checkConstraints(witness); + }); }); diff --git a/packages/circuits/tests/email-verifier.test.ts b/packages/circuits/tests/email-verifier.test.ts index 3221f9b62..adae1dc74 100644 --- a/packages/circuits/tests/email-verifier.test.ts +++ b/packages/circuits/tests/email-verifier.test.ts @@ -7,202 +7,170 @@ import { verifyDKIMSignature } from "@zk-email/helpers/src/dkim"; import { poseidonLarge } from "@zk-email/helpers/src/hash"; describe("EmailVerifier", () => { - jest.setTimeout(30 * 60 * 1000); // 30 minutes - - let dkimResult: DKIMVerificationResult; - let circuit: any; - - beforeAll(async () => { - const rawEmail = fs.readFileSync( - path.join(__dirname, "./test-emails/test.eml") - ); - dkimResult = await verifyDKIMSignature(rawEmail); - - circuit = await wasm_tester( - path.join(__dirname, "./test-circuits/email-verifier-test.circom"), - { - // @dev During development recompile can be set to false if you are only making changes in the tests. - // This will save time by not recompiling the circuit every time. - // Compile: circom "./tests/email-verifier-test.circom" --r1cs --wasm --sym --c --wat --output "./tests/compiled-test-circuits" - recompile: true, - include: path.join(__dirname, "../../../node_modules"), - output: path.join(__dirname, "./compiled-test-circuits"), - } - ); + jest.setTimeout(30 * 60 * 1000); // 30 minutes + + let dkimResult: DKIMVerificationResult; + let circuit: any; + + beforeAll(async () => { + const rawEmail = fs.readFileSync(path.join(__dirname, "./test-emails/test.eml")); + dkimResult = await verifyDKIMSignature(rawEmail); + + circuit = await wasm_tester(path.join(__dirname, "./test-circuits/email-verifier-test.circom"), { + // @dev During development recompile can be set to false if you are only making changes in the tests. + // This will save time by not recompiling the circuit every time. + // Compile: circom "./tests/email-verifier-test.circom" --r1cs --wasm --sym --c --wat --output "./tests/compiled-test-circuits" + recompile: true, + include: path.join(__dirname, "../../../node_modules"), + output: path.join(__dirname, "./compiled-test-circuits"), }); + }); - it("should verify email without any SHA precompute selector", async function () { - const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult( - dkimResult, - { - maxHeadersLength: 640, - maxBodyLength: 768, - } - ); - - const witness = await circuit.calculateWitness(emailVerifierInputs); - await circuit.checkConstraints(witness); + it("should verify email without any SHA precompute selector", async function () { + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkimResult, { + maxHeadersLength: 640, + maxBodyLength: 768, }); - it("should verify email with a SHA precompute selector", async function () { - const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult( - dkimResult, - { - shaPrecomputeSelector: "How are", - maxHeadersLength: 640, - maxBodyLength: 768, - } - ); - - const witness = await circuit.calculateWitness(emailVerifierInputs); - await circuit.checkConstraints(witness); + const witness = await circuit.calculateWitness(emailVerifierInputs); + await circuit.checkConstraints(witness); + }); + + it("should verify email with a SHA precompute selector", async function () { + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkimResult, { + shaPrecomputeSelector: "How are", + maxHeadersLength: 640, + maxBodyLength: 768, }); - it("should fail if the rsa signature is wrong", async function () { - const invalidRSASignature = dkimResult.signature + 1n; - const dkim = { ...dkimResult, signature: invalidRSASignature }; - - const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult( - dkim, - { - maxHeadersLength: 640, - maxBodyLength: 768, - } - ); - - expect.assertions(1); - try { - const witness = await circuit.calculateWitness(emailVerifierInputs); - await circuit.checkConstraints(witness); - } catch (error) { - expect((error as Error).message).toMatch("Assert Failed"); - } + const witness = await circuit.calculateWitness(emailVerifierInputs); + await circuit.checkConstraints(witness); + }); + + it("should fail if the rsa signature is wrong", async function () { + const invalidRSASignature = dkimResult.signature + 1n; + const dkim = { ...dkimResult, signature: invalidRSASignature }; + + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkim, { + maxHeadersLength: 640, + maxBodyLength: 768, }); - it("should fail if message is tampered", async function () { - const invalidHeader = Buffer.from(dkimResult.headers); - invalidHeader[0] = 1; - - const dkim = { ...dkimResult, headers: invalidHeader }; - - const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult( - dkim, - { - maxHeadersLength: 640, - maxBodyLength: 768, - } - ); - - expect.assertions(1); - try { - const witness = await circuit.calculateWitness(emailVerifierInputs); - await circuit.checkConstraints(witness); - } catch (error) { - expect((error as Error).message).toMatch("Assert Failed"); - } + expect.assertions(1); + try { + const witness = await circuit.calculateWitness(emailVerifierInputs); + await circuit.checkConstraints(witness); + } catch (error) { + expect((error as Error).message).toMatch("Assert Failed"); + } + }); + + it("should fail if message is tampered", async function () { + const invalidHeader = Buffer.from(dkimResult.headers); + invalidHeader[0] = 1; + + const dkim = { ...dkimResult, headers: invalidHeader }; + + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkim, { + maxHeadersLength: 640, + maxBodyLength: 768, }); - it("should fail if message padding is tampered", async function () { - const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult( - dkimResult, - { - maxHeadersLength: 640, - maxBodyLength: 768, - } - ); - emailVerifierInputs.emailHeader[640 - 1] = "1"; - - expect.assertions(1); - try { - const witness = await circuit.calculateWitness(emailVerifierInputs); - await circuit.checkConstraints(witness); - } catch (error) { - expect((error as Error).message).toMatch("Assert Failed"); - } + expect.assertions(1); + try { + const witness = await circuit.calculateWitness(emailVerifierInputs); + await circuit.checkConstraints(witness); + } catch (error) { + expect((error as Error).message).toMatch("Assert Failed"); + } + }); + + it("should fail if message padding is tampered", async function () { + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkimResult, { + maxHeadersLength: 640, + maxBodyLength: 768, + }); + emailVerifierInputs.emailHeader[640 - 1] = "1"; + + expect.assertions(1); + try { + const witness = await circuit.calculateWitness(emailVerifierInputs); + await circuit.checkConstraints(witness); + } catch (error) { + expect((error as Error).message).toMatch("Assert Failed"); + } + }); + + it("should fail if body is tampered", async function () { + const invalidBody = Buffer.from(dkimResult.body); + invalidBody[invalidBody.length - 1] = 1; + + const dkim = { ...dkimResult, body: invalidBody }; + + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkim, { + maxHeadersLength: 640, + maxBodyLength: 768, }); - it("should fail if body is tampered", async function () { - const invalidBody = Buffer.from(dkimResult.body); - invalidBody[invalidBody.length - 1] = 1; - - const dkim = { ...dkimResult, body: invalidBody }; - - const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult( - dkim, - { - maxHeadersLength: 640, - maxBodyLength: 768, - } - ); - - expect.assertions(1); - try { - const witness = await circuit.calculateWitness(emailVerifierInputs); - await circuit.checkConstraints(witness); - } catch (error) { - expect((error as Error).message).toMatch("Assert Failed"); - } + expect.assertions(1); + try { + const witness = await circuit.calculateWitness(emailVerifierInputs); + await circuit.checkConstraints(witness); + } catch (error) { + expect((error as Error).message).toMatch("Assert Failed"); + } + }); + + it("should fail if body padding is tampered", async function () { + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkimResult, { + maxHeadersLength: 640, + maxBodyLength: 768, }); + emailVerifierInputs.emailBody![768 - 1] = "1"; + + expect.assertions(1); + try { + const witness = await circuit.calculateWitness(emailVerifierInputs); + await circuit.checkConstraints(witness); + } catch (error) { + expect((error as Error).message).toMatch("Assert Failed"); + } + }); + + it("should fail if body hash is tampered", async function () { + const invalidBodyHash = dkimResult.bodyHash + "a"; - it("should fail if body padding is tampered", async function () { - const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult( - dkimResult, - { - maxHeadersLength: 640, - maxBodyLength: 768, - } - ); - emailVerifierInputs.emailBody![768 - 1] = "1"; - - expect.assertions(1); - try { - const witness = await circuit.calculateWitness(emailVerifierInputs); - await circuit.checkConstraints(witness); - } catch (error) { - expect((error as Error).message).toMatch("Assert Failed"); - } + const dkim = { ...dkimResult, bodyHash: invalidBodyHash }; + + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkim, { + maxHeadersLength: 640, + maxBodyLength: 768, }); - it("should fail if body hash is tampered", async function () { - const invalidBodyHash = dkimResult.bodyHash + "a"; - - const dkim = { ...dkimResult, bodyHash: invalidBodyHash }; - - const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult( - dkim, - { - maxHeadersLength: 640, - maxBodyLength: 768, - } - ); - - expect.assertions(1); - try { - const witness = await circuit.calculateWitness(emailVerifierInputs); - await circuit.checkConstraints(witness); - } catch (error) { - expect((error as Error).message).toMatch("Assert Failed"); - } + expect.assertions(1); + try { + const witness = await circuit.calculateWitness(emailVerifierInputs); + await circuit.checkConstraints(witness); + } catch (error) { + expect((error as Error).message).toMatch("Assert Failed"); + } + }); + + it("should produce dkim pubkey hash correctly", async function () { + const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult(dkimResult, { + shaPrecomputeSelector: "How are", + maxHeadersLength: 640, + maxBodyLength: 768, }); - it("should produce dkim pubkey hash correctly", async function () { - const emailVerifierInputs = generateEmailVerifierInputsFromDKIMResult( - dkimResult, - { - shaPrecomputeSelector: "How are", - maxHeadersLength: 640, - maxBodyLength: 768, - } - ); - - // Calculate the Poseidon hash with pubkey chunked to 9*242 like in circuit - const poseidonHash = await poseidonLarge(dkimResult.publicKey, 9, 242); - - // Calculate the hash using the circuit - const witness = await circuit.calculateWitness(emailVerifierInputs); - - await circuit.assertOut(witness, { - pubkeyHash: poseidonHash, - }); + // Calculate the Poseidon hash with pubkey chunked to 9*242 like in circuit + const poseidonHash = await poseidonLarge(dkimResult.publicKey, 9, 242); + + // Calculate the hash using the circuit + const witness = await circuit.calculateWitness(emailVerifierInputs); + + await circuit.assertOut(witness, { + pubkeyHash: poseidonHash, }); + }); }); diff --git a/packages/circuits/tests/remove-soft-line-breaks.test.ts b/packages/circuits/tests/remove-soft-line-breaks.test.ts index 5d14f7d3f..705830546 100644 --- a/packages/circuits/tests/remove-soft-line-breaks.test.ts +++ b/packages/circuits/tests/remove-soft-line-breaks.test.ts @@ -1,61 +1,23 @@ -import { wasm as wasm_tester } from 'circom_tester'; -import path from 'path'; +import { wasm as wasm_tester } from "circom_tester"; +import path from "path"; -describe('RemoveSoftLineBreaks', () => { +describe("RemoveSoftLineBreaks", () => { jest.setTimeout(20_1000); let circuit: any; beforeAll(async () => { - circuit = await wasm_tester( - path.join( - __dirname, - './test-circuits/remove-soft-line-breaks-test.circom' - ), - { - recompile: true, - include: path.join(__dirname, '../../../node_modules'), - output: path.join(__dirname, './compiled-test-circuits'), - } - ); + circuit = await wasm_tester(path.join(__dirname, "./test-circuits/remove-soft-line-breaks-test.circom"), { + recompile: true, + include: path.join(__dirname, "../../../node_modules"), + output: path.join(__dirname, "./compiled-test-circuits"), + }); }); - it('should correctly remove soft line breaks', async () => { + it("should correctly remove soft line breaks", async () => { const input = { - encoded: [ - 115, - 101, - 115, - 58, - 61, - 13, - 10, - 45, - 32, - 83, - 114, - 101, - 97, - 107, - 61, - 13, - 10, - ...Array(15).fill(0), - ], - decoded: [ - 115, - 101, - 115, - 58, - 45, - 32, - 83, - 114, - 101, - 97, - 107, - ...Array(21).fill(0), - ], + encoded: [115, 101, 115, 58, 61, 13, 10, 45, 32, 83, 114, 101, 97, 107, 61, 13, 10, ...Array(15).fill(0)], + decoded: [115, 101, 115, 58, 45, 32, 83, 114, 101, 97, 107, ...Array(21).fill(0)], }; const witness = await circuit.calculateWitness(input); @@ -66,28 +28,9 @@ describe('RemoveSoftLineBreaks', () => { }); }); - it('should fail when decoded input is incorrect', async () => { + it("should fail when decoded input is incorrect", async () => { const input = { - encoded: [ - 115, - 101, - 115, - 58, - 61, - 13, - 10, - 45, - 32, - 83, - 114, - 101, - 97, - 107, - 61, - 13, - 10, - ...Array(15).fill(0), - ], + encoded: [115, 101, 115, 58, 61, 13, 10, 45, 32, 83, 114, 101, 97, 107, 61, 13, 10, ...Array(15).fill(0)], decoded: [ 115, 101, @@ -112,7 +55,7 @@ describe('RemoveSoftLineBreaks', () => { }); }); - it('should handle input with no soft line breaks', async () => { + it("should handle input with no soft line breaks", async () => { const input = { encoded: [104, 101, 108, 108, 111, ...Array(27).fill(0)], decoded: [104, 101, 108, 108, 111, ...Array(27).fill(0)], @@ -126,40 +69,10 @@ describe('RemoveSoftLineBreaks', () => { }); }); - it('should handle input with multiple consecutive soft line breaks', async () => { + it("should handle input with multiple consecutive soft line breaks", async () => { const input = { - encoded: [ - 104, - 101, - 108, - 108, - 111, - 61, - 13, - 10, - 61, - 13, - 10, - 119, - 111, - 114, - 108, - 100, - ...Array(16).fill(0), - ], - decoded: [ - 104, - 101, - 108, - 108, - 111, - 119, - 111, - 114, - 108, - 100, - ...Array(22).fill(0), - ], + encoded: [104, 101, 108, 108, 111, 61, 13, 10, 61, 13, 10, 119, 111, 114, 108, 100, ...Array(16).fill(0)], + decoded: [104, 101, 108, 108, 111, 119, 111, 114, 108, 100, ...Array(22).fill(0)], }; const witness = await circuit.calculateWitness(input); @@ -170,7 +83,7 @@ describe('RemoveSoftLineBreaks', () => { }); }); - it('should handle input with soft line break at the beginning', async () => { + it("should handle input with soft line break at the beginning", async () => { const input = { encoded: [61, 13, 10, 104, 101, 108, 108, 111, ...Array(24).fill(0)], decoded: [104, 101, 108, 108, 111, ...Array(27).fill(0)], @@ -184,7 +97,7 @@ describe('RemoveSoftLineBreaks', () => { }); }); - it('should handle input with soft line break at the end', async () => { + it("should handle input with soft line break at the end", async () => { const input = { encoded: [104, 101, 108, 108, 111, 61, 13, 10, ...Array(24).fill(0)], decoded: [104, 101, 108, 108, 111, ...Array(27).fill(0)], @@ -198,7 +111,7 @@ describe('RemoveSoftLineBreaks', () => { }); }); - it('should handle input with incomplete soft line break sequence', async () => { + it("should handle input with incomplete soft line break sequence", async () => { const input = { encoded: [ 104, diff --git a/packages/circuits/tests/rsa.test.ts b/packages/circuits/tests/rsa.test.ts index 273db843e..919e04165 100644 --- a/packages/circuits/tests/rsa.test.ts +++ b/packages/circuits/tests/rsa.test.ts @@ -5,141 +5,130 @@ import { generateEmailVerifierInputs } from "@zk-email/helpers/src/input-generat import { toCircomBigIntBytes } from "@zk-email/helpers/src/binary-format"; describe("RSA", () => { - jest.setTimeout(30 * 60 * 1000); // 30 minutes + jest.setTimeout(30 * 60 * 1000); // 30 minutes - let circuit: any; - let rawEmail: Buffer; + let circuit: any; + let rawEmail: Buffer; - beforeAll(async () => { - circuit = await wasm_tester( - path.join(__dirname, "./test-circuits/rsa-test.circom"), - { - recompile: true, - include: path.join(__dirname, "../../../node_modules"), - // output: path.join(__dirname, "./compiled-test-circuits"), - } - ); - rawEmail = fs.readFileSync( - path.join(__dirname, "./test-emails/test.eml") - ); + beforeAll(async () => { + circuit = await wasm_tester(path.join(__dirname, "./test-circuits/rsa-test.circom"), { + recompile: true, + include: path.join(__dirname, "../../../node_modules"), + // output: path.join(__dirname, "./compiled-test-circuits"), }); + rawEmail = fs.readFileSync(path.join(__dirname, "./test-emails/test.eml")); + }); - it("should verify 2048 bit rsa signature correctly", async function () { - const emailVerifierInputs = await generateEmailVerifierInputs( - rawEmail, - { - maxHeadersLength: 640, - maxBodyLength: 768, - } - ); + it("should verify 2048 bit rsa signature correctly", async function () { + const emailVerifierInputs = await generateEmailVerifierInputs(rawEmail, { + maxHeadersLength: 640, + maxBodyLength: 768, + }); - const witness = await circuit.calculateWitness({ - signature: emailVerifierInputs.signature, - modulus: emailVerifierInputs.pubkey, - // TODO: generate this from the input - message: [ - "1156466847851242602709362303526378170", - "191372789510123109308037416804949834", - "7204", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - ], - }); - await circuit.checkConstraints(witness); - await circuit.assertOut(witness, {}); + const witness = await circuit.calculateWitness({ + signature: emailVerifierInputs.signature, + modulus: emailVerifierInputs.pubkey, + // TODO: generate this from the input + message: [ + "1156466847851242602709362303526378170", + "191372789510123109308037416804949834", + "7204", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + ], }); + await circuit.checkConstraints(witness); + await circuit.assertOut(witness, {}); + }); - it("should verify 1024 bit rsa signature correctly", async function () { - const signature = toCircomBigIntBytes( - BigInt( - 102386562682221859025549328916727857389789009840935140645361501981959969535413501251999442013082353139290537518086128904993091119534674934202202277050635907008004079788691412782712147797487593510040249832242022835902734939817209358184800954336078838331094308355388211284440290335887813714894626653613586546719n - ) - ); + it("should verify 1024 bit rsa signature correctly", async function () { + const signature = toCircomBigIntBytes( + BigInt( + 102386562682221859025549328916727857389789009840935140645361501981959969535413501251999442013082353139290537518086128904993091119534674934202202277050635907008004079788691412782712147797487593510040249832242022835902734939817209358184800954336078838331094308355388211284440290335887813714894626653613586546719n, + ), + ); - const pubkey = toCircomBigIntBytes( - BigInt( - 106773687078109007595028366084970322147907086635176067918161636756354740353674098686965493426431314019237945536387044259034050617425729739578628872957481830432099721612688699974185290306098360072264136606623400336518126533605711223527682187548332314997606381158951535480830524587400401856271050333371205030999n - ) - ); + const pubkey = toCircomBigIntBytes( + BigInt( + 106773687078109007595028366084970322147907086635176067918161636756354740353674098686965493426431314019237945536387044259034050617425729739578628872957481830432099721612688699974185290306098360072264136606623400336518126533605711223527682187548332314997606381158951535480830524587400401856271050333371205030999n, + ), + ); - const witness = await circuit.calculateWitness({ - signature: signature, - modulus: pubkey, - // TODO: generate this from the input - message: [ - "1156466847851242602709362303526378170", - "191372789510123109308037416804949834", - "7204", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - ], - }); - await circuit.checkConstraints(witness); - await circuit.assertOut(witness, {}); + const witness = await circuit.calculateWitness({ + signature: signature, + modulus: pubkey, + // TODO: generate this from the input + message: [ + "1156466847851242602709362303526378170", + "191372789510123109308037416804949834", + "7204", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + ], }); + await circuit.checkConstraints(witness); + await circuit.assertOut(witness, {}); + }); - it("should fail when verifying with an incorrect signature", async function () { - const emailVerifierInputs = await generateEmailVerifierInputs( - rawEmail, - { - maxHeadersLength: 640, - maxBodyLength: 768, - } - ); - - expect.assertions(1); - try { - const witness = await circuit.calculateWitness({ - signature: emailVerifierInputs.signature, - modulus: emailVerifierInputs.pubkey, - message: [ - "1156466847851242602709362303526378171", - "191372789510123109308037416804949834", - "7204", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - "0", - ], - }); - await circuit.checkConstraints(witness); - await circuit.assertOut(witness, {}); - } catch (error) { - expect((error as Error).message).toMatch("Assert Failed"); - } + it("should fail when verifying with an incorrect signature", async function () { + const emailVerifierInputs = await generateEmailVerifierInputs(rawEmail, { + maxHeadersLength: 640, + maxBodyLength: 768, }); + + expect.assertions(1); + try { + const witness = await circuit.calculateWitness({ + signature: emailVerifierInputs.signature, + modulus: emailVerifierInputs.pubkey, + message: [ + "1156466847851242602709362303526378171", + "191372789510123109308037416804949834", + "7204", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + "0", + ], + }); + await circuit.checkConstraints(witness); + await circuit.assertOut(witness, {}); + } catch (error) { + expect((error as Error).message).toMatch("Assert Failed"); + } + }); }); diff --git a/packages/circuits/tests/select-regex-reveal.test.ts b/packages/circuits/tests/select-regex-reveal.test.ts index c18cfd661..3a4dee59e 100644 --- a/packages/circuits/tests/select-regex-reveal.test.ts +++ b/packages/circuits/tests/select-regex-reveal.test.ts @@ -2,114 +2,102 @@ import { wasm } from "circom_tester"; import path from "path"; describe("Select Regex Reveal", () => { - jest.setTimeout(30 * 60 * 1000); // 30 minutes + jest.setTimeout(30 * 60 * 1000); // 30 minutes - let circuit: any; + let circuit: any; - beforeAll(async () => { - circuit = await wasm( - path.join( - __dirname, - "./test-circuits/select-regex-reveal-test.circom" - ), - { - recompile: true, - include: path.join(__dirname, "../../../node_modules"), - } - ); + beforeAll(async () => { + circuit = await wasm(path.join(__dirname, "./test-circuits/select-regex-reveal-test.circom"), { + recompile: true, + include: path.join(__dirname, "../../../node_modules"), }); + }); - it("should reveal the substring with maximum revealed length", async function () { - let input = new Array(34).fill(0); - const startIndex = Math.floor(Math.random() * 24); - const revealed = Array.from("zk email").map((char) => - char.charCodeAt(0) - ); - for (let i = 0; i < revealed.length; i++) { - input[startIndex + i] = revealed[i]; - } - const witness = await circuit.calculateWitness({ - in: input, - startIndex: startIndex, - }); - await circuit.checkConstraints(witness); - await circuit.assertOut(witness, { out: revealed }); + it("should reveal the substring with maximum revealed length", async function () { + let input = new Array(34).fill(0); + const startIndex = Math.floor(Math.random() * 24); + const revealed = Array.from("zk email").map((char) => char.charCodeAt(0)); + for (let i = 0; i < revealed.length; i++) { + input[startIndex + i] = revealed[i]; + } + const witness = await circuit.calculateWitness({ + in: input, + startIndex: startIndex, }); + await circuit.checkConstraints(witness); + await circuit.assertOut(witness, { out: revealed }); + }); - it("should reveal the substring with non-maximum revealed length", async function () { - let input = new Array(34).fill(0); - const startIndex = 30; - const revealed = Array.from("zk").map((char) => char.charCodeAt(0)); - for (let i = 0; i < revealed.length; i++) { - input[startIndex + i] = revealed[i]; - } - const witness = await circuit.calculateWitness({ - in: input, - startIndex: startIndex, - }); - await circuit.checkConstraints(witness); - await circuit.assertOut(witness, { - out: revealed.concat([0, 0, 0, 0, 0, 0]), - }); + it("should reveal the substring with non-maximum revealed length", async function () { + let input = new Array(34).fill(0); + const startIndex = 30; + const revealed = Array.from("zk").map((char) => char.charCodeAt(0)); + for (let i = 0; i < revealed.length; i++) { + input[startIndex + i] = revealed[i]; + } + const witness = await circuit.calculateWitness({ + in: input, + startIndex: startIndex, }); + await circuit.checkConstraints(witness); + await circuit.assertOut(witness, { + out: revealed.concat([0, 0, 0, 0, 0, 0]), + }); + }); - it("should fail when all zero", async function () { - let input = new Array(34).fill(0); - const startIndex = Math.floor(Math.random() * 34); - try { - const witness = await circuit.calculateWitness({ - in: input, - startIndex: startIndex, - }); - await circuit.checkConstraints(witness); - } catch (error) { - expect((error as Error).message).toMatch("Assert Failed"); - } + it("should fail when all zero", async function () { + let input = new Array(34).fill(0); + const startIndex = Math.floor(Math.random() * 34); + try { + const witness = await circuit.calculateWitness({ + in: input, + startIndex: startIndex, + }); + await circuit.checkConstraints(witness); + } catch (error) { + expect((error as Error).message).toMatch("Assert Failed"); + } - expect.assertions(1); - }); + expect.assertions(1); + }); - it("should fail when startIndex is 0", async function () { - let input = new Array(34).fill(0); - const startIndex = 1 + Math.floor(Math.random() * 24); - const revealed = Array.from("zk email").map((char) => - char.charCodeAt(0) - ); - for (let i = 0; i < revealed.length; i++) { - input[startIndex + i] = revealed[i]; - } - try { - const witness = await circuit.calculateWitness({ - in: input, - startIndex: startIndex - 1, - }); - await circuit.checkConstraints(witness); - } catch (error) { - expect((error as Error).message).toMatch("Assert Failed"); - } + it("should fail when startIndex is 0", async function () { + let input = new Array(34).fill(0); + const startIndex = 1 + Math.floor(Math.random() * 24); + const revealed = Array.from("zk email").map((char) => char.charCodeAt(0)); + for (let i = 0; i < revealed.length; i++) { + input[startIndex + i] = revealed[i]; + } + try { + const witness = await circuit.calculateWitness({ + in: input, + startIndex: startIndex - 1, + }); + await circuit.checkConstraints(witness); + } catch (error) { + expect((error as Error).message).toMatch("Assert Failed"); + } - expect.assertions(1); - }); + expect.assertions(1); + }); - it("should fail when startIndex is not before 0", async function () { - let input = new Array(34).fill(0); - const startIndex = Math.floor(Math.random() * 23); - const revealed = Array.from("zk email").map((char) => - char.charCodeAt(0) - ); - for (let i = 0; i < revealed.length; i++) { - input[startIndex + i] = revealed[i]; - } - try { - const witness = await circuit.calculateWitness({ - in: input, - startIndex: startIndex + 1, - }); - await circuit.checkConstraints(witness); - } catch (error) { - expect((error as Error).message).toMatch("Assert Failed"); - } + it("should fail when startIndex is not before 0", async function () { + let input = new Array(34).fill(0); + const startIndex = Math.floor(Math.random() * 23); + const revealed = Array.from("zk email").map((char) => char.charCodeAt(0)); + for (let i = 0; i < revealed.length; i++) { + input[startIndex + i] = revealed[i]; + } + try { + const witness = await circuit.calculateWitness({ + in: input, + startIndex: startIndex + 1, + }); + await circuit.checkConstraints(witness); + } catch (error) { + expect((error as Error).message).toMatch("Assert Failed"); + } - expect.assertions(1); - }); + expect.assertions(1); + }); }); diff --git a/packages/circuits/tests/sha.test.ts b/packages/circuits/tests/sha.test.ts index e9281924c..f43c50f4a 100644 --- a/packages/circuits/tests/sha.test.ts +++ b/packages/circuits/tests/sha.test.ts @@ -1,44 +1,35 @@ import { wasm as wasm_tester } from "circom_tester"; import path from "path"; import { sha256Pad, shaHash } from "@zk-email/helpers/src/sha-utils"; -import { - Uint8ArrayToCharArray, - uint8ToBits, -} from "@zk-email/helpers/src/binary-format"; +import { Uint8ArrayToCharArray, uint8ToBits } from "@zk-email/helpers/src/binary-format"; describe("SHA256 for email header", () => { - jest.setTimeout(30 * 60 * 1000); // 30 minutes + jest.setTimeout(30 * 60 * 1000); // 30 minutes - let circuit: any; + let circuit: any; - beforeAll(async () => { - circuit = await wasm_tester( - path.join(__dirname, "./test-circuits/sha-test.circom"), - { - recompile: true, - include: path.join(__dirname, "../../../node_modules"), - // output: path.join(__dirname, "./compiled-test-circuits"), - } - ); + beforeAll(async () => { + circuit = await wasm_tester(path.join(__dirname, "./test-circuits/sha-test.circom"), { + recompile: true, + include: path.join(__dirname, "../../../node_modules"), + // output: path.join(__dirname, "./compiled-test-circuits"), }); + }); - it("should hash correctly", async function () { - const inputs = ["0", "hello world", ""]; - for (const input of inputs) { - const [paddedMsg, messageLen] = sha256Pad( - Buffer.from(input, "ascii"), - 640 - ); + it("should hash correctly", async function () { + const inputs = ["0", "hello world", ""]; + for (const input of inputs) { + const [paddedMsg, messageLen] = sha256Pad(Buffer.from(input, "ascii"), 640); - const witness = await circuit.calculateWitness({ - paddedIn: Uint8ArrayToCharArray(paddedMsg), - paddedInLength: messageLen, - }); + const witness = await circuit.calculateWitness({ + paddedIn: Uint8ArrayToCharArray(paddedMsg), + paddedInLength: messageLen, + }); - await circuit.checkConstraints(witness); - await circuit.assertOut(witness, { - out: [...uint8ToBits(shaHash(Buffer.from(input, "ascii")))], - }); - } - }); + await circuit.checkConstraints(witness); + await circuit.assertOut(witness, { + out: [...uint8ToBits(shaHash(Buffer.from(input, "ascii")))], + }); + } + }); }); diff --git a/packages/circuits/tests/split-bytes-to-words.test.ts b/packages/circuits/tests/split-bytes-to-words.test.ts index 99fc83e41..fc677142e 100644 --- a/packages/circuits/tests/split-bytes-to-words.test.ts +++ b/packages/circuits/tests/split-bytes-to-words.test.ts @@ -2,32 +2,26 @@ import { wasm as wasm_tester } from "circom_tester"; import path from "path"; import { bigIntToChunkedBytes, Uint8ArrayToCharArray } from "@zk-email/helpers/src/binary-format"; - describe("SplitBytesToWords Helper unit test", () => { - jest.setTimeout(0.1 * 60 * 1000); - let circuit: any; - - beforeAll(async () => { - circuit = await wasm_tester( - path.join(__dirname, "./test-circuits/split-bytes-to-words-test.circom"), - { - recompile: true, - include: path.join(__dirname, "../../../node_modules"), - // output: path.join(__dirname, "./compiled-test-circuits"), - } - ); + jest.setTimeout(0.1 * 60 * 1000); + let circuit: any; + beforeAll(async () => { + circuit = await wasm_tester(path.join(__dirname, "./test-circuits/split-bytes-to-words-test.circom"), { + recompile: true, + include: path.join(__dirname, "../../../node_modules"), + // output: path.join(__dirname, "./compiled-test-circuits"), }); + }); - it("should split correctly according to bigIntToChunkedBytes function", async function () { - const bytes = new Uint8Array(256).map(() => Math.floor(Math.random() * 256)); - const bytesBigInt = bytes.reduce((acc, val) => (acc << 8n) | BigInt(val), 0n); - const ts_split_to_words = bigIntToChunkedBytes(bytesBigInt, 121, 17); - const ts_split_to_words_bigint = ts_split_to_words.map((word) => BigInt(word)); - const witness = await circuit.calculateWitness({ - in: Uint8ArrayToCharArray(bytes) - }); - await circuit.assertOut(witness, { out: ts_split_to_words_bigint }); + it("should split correctly according to bigIntToChunkedBytes function", async function () { + const bytes = new Uint8Array(256).map(() => Math.floor(Math.random() * 256)); + const bytesBigInt = bytes.reduce((acc, val) => (acc << 8n) | BigInt(val), 0n); + const ts_split_to_words = bigIntToChunkedBytes(bytesBigInt, 121, 17); + const ts_split_to_words_bigint = ts_split_to_words.map((word) => BigInt(word)); + const witness = await circuit.calculateWitness({ + in: Uint8ArrayToCharArray(bytes), }); - + await circuit.assertOut(witness, { out: ts_split_to_words_bigint }); + }); }); diff --git a/packages/helpers/src/binary-format.ts b/packages/helpers/src/binary-format.ts index a549d8654..c76e9168d 100644 --- a/packages/helpers/src/binary-format.ts +++ b/packages/helpers/src/binary-format.ts @@ -1,4 +1,4 @@ -import { CIRCOM_BIGINT_N, CIRCOM_BIGINT_K } from './constants'; +import { CIRCOM_BIGINT_N, CIRCOM_BIGINT_K } from "./constants"; export function bytesToString(bytes: Uint8Array): string { return new TextDecoder().decode(bytes); @@ -38,7 +38,7 @@ export function bufferToUint8Array(buf: Buffer): Uint8Array { } export function bufferToHex(buf: Buffer): String { - return buf.toString('hex'); + return buf.toString("hex"); } export function Uint8ArrayToCharArray(a: Uint8Array): string[] { @@ -48,11 +48,11 @@ export function Uint8ArrayToCharArray(a: Uint8Array): string[] { export async function Uint8ArrayToString(a: Uint8Array): Promise { return Array.from(a) .map((x) => x.toString()) - .join(';'); + .join(";"); } export async function Uint8ArrayToHex(a: Uint8Array): Promise { - return Buffer.from(a).toString('hex'); + return Buffer.from(a).toString("hex"); } export function bufferToString(buf: Buffer): String { @@ -70,7 +70,7 @@ export function bytesToBigInt(bytes: Uint8Array) { export function bigIntToChunkedBytes(num: BigInt | bigint, bytesPerChunk: number, numChunks: number) { const res = []; - const bigintNum: bigint = typeof num === 'bigint' ? num : num.valueOf(); + const bigintNum: bigint = typeof num === "bigint" ? num : num.valueOf(); const msk = (1n << BigInt(bytesPerChunk)) - 1n; for (let i = 0; i < numChunks; ++i) { res.push(((bigintNum >> BigInt(i * bytesPerChunk)) & msk).toString()); @@ -83,7 +83,7 @@ export function toCircomBigIntBytes(num: BigInt | bigint) { } // https://stackoverflow.com/a/69585881 -const HEX_STRINGS = '0123456789abcdef'; +const HEX_STRINGS = "0123456789abcdef"; const MAP_HEX = { 0: 0, 1: 1, @@ -113,7 +113,7 @@ const MAP_HEX = { export function toHex(bytes: Uint8Array): string { return Array.from(bytes || []) .map((b) => HEX_STRINGS[b >> 4] + HEX_STRINGS[b & 15]) - .join(''); + .join(""); } // Mimics Buffer.from(x, 'hex') logic @@ -121,10 +121,10 @@ export function toHex(bytes: Uint8Array): string { // https://github.com/nodejs/node/blob/v14.18.1/src/string_bytes.cc#L246-L261 export function fromHex(hexString: string): Uint8Array { let hexStringTrimmed: string = hexString; - if (hexString[0] === '0' && hexString[1] === 'x') { + if (hexString[0] === "0" && hexString[1] === "x") { hexStringTrimmed = hexString.slice(2); } - const bytes = new Uint8Array(Math.floor((hexStringTrimmed || '').length / 2)); + const bytes = new Uint8Array(Math.floor((hexStringTrimmed || "").length / 2)); let i; for (i = 0; i < bytes.length; i++) { const a = MAP_HEX[hexStringTrimmed[i * 2] as keyof typeof MAP_HEX]; @@ -162,7 +162,7 @@ export function bitsToUint8(bits: string[]): Uint8Array { } export function uint8ToBits(uint8: Uint8Array): string { - return uint8.reduce((acc, byte) => acc + byte.toString(2).padStart(8, '0'), ''); + return uint8.reduce((acc, byte) => acc + byte.toString(2).padStart(8, "0"), ""); } export function mergeUInt8Arrays(a1: Uint8Array, a2: Uint8Array): Uint8Array { @@ -190,14 +190,18 @@ export function packedNBytesToString(packedBytes: bigint[], n: number = 31): str } export function packBytesIntoNBytes(messagePaddedRaw: Uint8Array | string, n = 7): Array { - const messagePadded: Uint8Array = typeof messagePaddedRaw === 'string' ? stringToBytes(messagePaddedRaw) : messagePaddedRaw; + const messagePadded: Uint8Array = + typeof messagePaddedRaw === "string" ? stringToBytes(messagePaddedRaw) : messagePaddedRaw; const output: Array = []; for (let i = 0; i < messagePadded.length; i++) { if (i % n === 0) { output.push(0n); } const j = (i / n) | 0; - console.assert(j === output.length - 1, 'Not editing the index of the last element -- packing loop invariants bug!'); + console.assert( + j === output.length - 1, + "Not editing the index of the last element -- packing loop invariants bug!", + ); output[j] += BigInt(messagePadded[i]) << BigInt((i % n) * 8); } return output; diff --git a/packages/helpers/src/chunked-zkey.ts b/packages/helpers/src/chunked-zkey.ts index 6d0053a06..10b20ba0a 100644 --- a/packages/helpers/src/chunked-zkey.ts +++ b/packages/helpers/src/chunked-zkey.ts @@ -1,18 +1,16 @@ -import localforage from 'localforage'; +import localforage from "localforage"; // @ts-ignore -import pako from 'pako'; +import pako from "pako"; // @ts-ignore -import * as snarkjs from 'snarkjs'; +import * as snarkjs from "snarkjs"; -const zkeyExtension = '.gz'; -const zkeyExtensionRegEx = new RegExp(`\\b${zkeyExtension}$\\b`, 'i'); // = /.gz$/i -const zkeySuffix = ['b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k']; +const zkeyExtension = ".gz"; +const zkeyExtensionRegEx = new RegExp(`\\b${zkeyExtension}$\\b`, "i"); // = /.gz$/i +const zkeySuffix = ["b", "c", "d", "e", "f", "g", "h", "i", "j", "k"]; // uncompresses single .gz file. // returns the contents as an ArrayBuffer -export const uncompressGz = async ( - arrayBuffer: ArrayBuffer, -): Promise => { +export const uncompressGz = async (arrayBuffer: ArrayBuffer): Promise => { const output = pako.ungzip(arrayBuffer); const buff = output.buffer; return buff; @@ -26,24 +24,18 @@ async function storeArrayBuffer(keyname: string, buffer: ArrayBuffer) { async function downloadWithRetries(link: string, downloadAttempts: number) { for (let i = 1; i <= downloadAttempts; i++) { console.log(`download attempt ${i} for ${link}`); - const response = await fetch(link, { method: 'GET' }); + const response = await fetch(link, { method: "GET" }); if (response.status === 200) { return response; } } - throw new Error( - `Error downloading ${link} after ${downloadAttempts} retries`, - ); + throw new Error(`Error downloading ${link} after ${downloadAttempts} retries`); } // GET the compressed file from the remote server, then store it with localforage // Note that it must be stored as an uncompressed ArrayBuffer // and named such that filename===`${name}.zkey${a}` in order for it to be found by snarkjs. -export async function downloadFromFilename( - baseUrl: string, - filename: string, - compressed = false, -) { +export async function downloadFromFilename(baseUrl: string, filename: string, compressed = false) { const link = baseUrl + filename; const zkeyResp = await downloadWithRetries(link, 3); @@ -54,38 +46,28 @@ export async function downloadFromFilename( } else { // uncompress the data const zkeyUncompressed = await uncompressGz(zkeyBuff); - const rawFilename = filename.replace(zkeyExtensionRegEx, ''); // replace .gz with "" + const rawFilename = filename.replace(zkeyExtensionRegEx, ""); // replace .gz with "" // store the uncompressed data - console.log('storing file in localforage', rawFilename); + console.log("storing file in localforage", rawFilename); await storeArrayBuffer(rawFilename, zkeyUncompressed); - console.log('stored file in localforage', rawFilename); + console.log("stored file in localforage", rawFilename); // await localforage.setItem(filename, zkeyBuff); } console.log(`Storage of ${filename} successful!`); } -export async function downloadProofFiles( - baseUrl: string, - circuitName: string, - onFileDownloaded: () => void, -) { +export async function downloadProofFiles(baseUrl: string, circuitName: string, onFileDownloaded: () => void) { const filePromises = []; for (const c of zkeySuffix) { const targzFilename = `${circuitName}.zkey${c}${zkeyExtension}`; // const itemCompressed = await localforage.getItem(targzFilename); const item = await localforage.getItem(`${circuitName}.zkey${c}`); if (item) { - console.log( - `${circuitName}.zkey${c}${ - item ? '' : zkeyExtension - } already found in localforage!`, - ); + console.log(`${circuitName}.zkey${c}${item ? "" : zkeyExtension} already found in localforage!`); onFileDownloaded(); continue; } - filePromises.push( - downloadFromFilename(baseUrl, targzFilename, true).then(() => onFileDownloaded()), - ); + filePromises.push(downloadFromFilename(baseUrl, targzFilename, true).then(() => onFileDownloaded())); } console.log(filePromises); await Promise.all(filePromises); @@ -93,7 +75,7 @@ export async function downloadProofFiles( export async function generateProof(input: any, baseUrl: string, circuitName: string) { // TODO: figure out how to generate this s.t. it passes build - console.log('generating proof for input'); + console.log("generating proof for input"); console.log(input); const { proof, publicSignals } = await snarkjs.groth16.fullProve( input, @@ -109,19 +91,15 @@ export async function generateProof(input: any, baseUrl: string, circuitName: st } export async function verifyProof(proof: any, publicSignals: any, baseUrl: string, circuitName: string) { - console.log('PROOF', proof); - console.log('PUBLIC SIGNALS', publicSignals); + console.log("PROOF", proof); + console.log("PUBLIC SIGNALS", publicSignals); const response = await downloadWithRetries(`${baseUrl}${circuitName}.vkey.json`, 3); const vkey = await response.json(); - console.log('vkey', vkey); + console.log("vkey", vkey); - const proofVerified = await snarkjs.groth16.verify( - vkey, - publicSignals, - proof, - ); - console.log('proofV', proofVerified); + const proofVerified = await snarkjs.groth16.verify(vkey, publicSignals, proof); + console.log("proofV", proofVerified); return proofVerified; } @@ -143,24 +121,16 @@ function bigIntToArray(n: number, k: number, x: bigint) { // taken from generation code in dizkus-circuits tests function pubkeyToXYArrays(pk: string) { - const XArr = bigIntToArray(64, 4, BigInt(`0x${pk.slice(4, 4 + 64)}`)).map( - (el) => el.toString(), - ); - const YArr = bigIntToArray(64, 4, BigInt(`0x${pk.slice(68, 68 + 64)}`)).map( - (el) => el.toString(), - ); + const XArr = bigIntToArray(64, 4, BigInt(`0x${pk.slice(4, 4 + 64)}`)).map((el) => el.toString()); + const YArr = bigIntToArray(64, 4, BigInt(`0x${pk.slice(68, 68 + 64)}`)).map((el) => el.toString()); return [XArr, YArr]; } // taken from generation code in dizkus-circuits tests function sigToRSArrays(sig: string) { - const rArr = bigIntToArray(64, 4, BigInt(`0x${sig.slice(2, 2 + 64)}`)).map( - (el) => el.toString(), - ); - const sArr = bigIntToArray(64, 4, BigInt(`0x${sig.slice(66, 66 + 64)}`)).map( - (el) => el.toString(), - ); + const rArr = bigIntToArray(64, 4, BigInt(`0x${sig.slice(2, 2 + 64)}`)).map((el) => el.toString()); + const sArr = bigIntToArray(64, 4, BigInt(`0x${sig.slice(66, 66 + 64)}`)).map((el) => el.toString()); return [rArr, sArr]; } diff --git a/packages/helpers/src/dkim/index.ts b/packages/helpers/src/dkim/index.ts index 4bd714a17..d847f6579 100644 --- a/packages/helpers/src/dkim/index.ts +++ b/packages/helpers/src/dkim/index.ts @@ -1,7 +1,7 @@ -import { pki } from 'node-forge'; -import { DkimVerifier } from '../lib/mailauth/dkim-verifier'; -import { writeToStream } from '../lib/mailauth/tools'; -import sanitizers from './sanitizers'; +import { pki } from "node-forge"; +import { DkimVerifier } from "../lib/mailauth/dkim-verifier"; +import { writeToStream } from "../lib/mailauth/tools"; +import sanitizers from "./sanitizers"; // `./mailauth` is modified version of https://github.com/postalsys/mailauth // Main modification are including emailHeaders in the DKIM result, making it work in the browser, add types @@ -30,7 +30,7 @@ export interface DKIMVerificationResult { */ export async function verifyDKIMSignature( email: Buffer | string, - domain: string = '', + domain: string = "", enableSanitization: boolean = true, ): Promise { const emailStr = email.toString(); @@ -39,20 +39,20 @@ export async function verifyDKIMSignature( // If DKIM verification fails, try again after sanitizing email let appliedSanitization; - if (dkimResult.status.comment === 'bad signature' && enableSanitization) { + if (dkimResult.status.comment === "bad signature" && enableSanitization) { const results = await Promise.all( - sanitizers.map((sanitize) => tryVerifyDKIM(sanitize(emailStr), domain).then((result) => ({ - result, - sanitizer: sanitize.name, - }))), + sanitizers.map((sanitize) => + tryVerifyDKIM(sanitize(emailStr), domain).then((result) => ({ + result, + sanitizer: sanitize.name, + })), + ), ); - const passed = results.find((r) => r.result.status.result === 'pass'); + const passed = results.find((r) => r.result.status.result === "pass"); if (passed) { - console.log( - `DKIM: Verification passed after applying sanitization "${passed.sanitizer}"`, - ); + console.log(`DKIM: Verification passed after applying sanitization "${passed.sanitizer}"`); dkimResult = passed.result; appliedSanitization = passed.sanitizer; } @@ -68,16 +68,14 @@ export async function verifyDKIMSignature( bodyHash, } = dkimResult; - if (result !== 'pass') { - throw new Error( - `DKIM signature verification failed for domain ${signingDomain}. Reason: ${comment}`, - ); + if (result !== "pass") { + throw new Error(`DKIM signature verification failed for domain ${signingDomain}. Reason: ${comment}`); } const pubKeyData = pki.publicKeyFromPem(publicKey.toString()); return { - signature: BigInt(`0x${Buffer.from(signature, 'base64').toString('hex')}`), + signature: BigInt(`0x${Buffer.from(signature, "base64").toString("hex")}`), headers: status.signedHeaders, body, bodyHash, @@ -91,29 +89,23 @@ export async function verifyDKIMSignature( }; } -async function tryVerifyDKIM(email: Buffer | string, domain: string = '') { +async function tryVerifyDKIM(email: Buffer | string, domain: string = "") { const dkimVerifier = new DkimVerifier({}); await writeToStream(dkimVerifier, email as any); let domainToVerifyDKIM = domain; if (!domainToVerifyDKIM) { if (dkimVerifier.headerFrom.length > 1) { - throw new Error( - 'Multiple From header in email and domain for verification not specified', - ); + throw new Error("Multiple From header in email and domain for verification not specified"); } - domainToVerifyDKIM = dkimVerifier.headerFrom[0].split('@')[1]; + domainToVerifyDKIM = dkimVerifier.headerFrom[0].split("@")[1]; } - const dkimResult = dkimVerifier.results.find( - (d: any) => d.signingDomain === domainToVerifyDKIM, - ); + const dkimResult = dkimVerifier.results.find((d: any) => d.signingDomain === domainToVerifyDKIM); if (!dkimResult) { - throw new Error( - `DKIM signature not found for domain ${domainToVerifyDKIM}`, - ); + throw new Error(`DKIM signature not found for domain ${domainToVerifyDKIM}`); } dkimResult.headers = dkimVerifier.headers; diff --git a/packages/helpers/src/dkim/sanitizers.ts b/packages/helpers/src/dkim/sanitizers.ts index 652f2adea..b787e998a 100644 --- a/packages/helpers/src/dkim/sanitizers.ts +++ b/packages/helpers/src/dkim/sanitizers.ts @@ -1,6 +1,6 @@ function getHeaderValue(email: string, header: string) { const headerStartIndex = email.indexOf(`${header}: `) + header.length + 2; - const headerEndIndex = email.indexOf('\n', headerStartIndex); + const headerEndIndex = email.indexOf("\n", headerStartIndex); const headerValue = email.substring(headerStartIndex, headerEndIndex); return headerValue; @@ -15,17 +15,14 @@ function setHeaderValue(email: string, header: string, value: string) { // TODO: Add test for this function revertGoogleMessageId(email: string): string { // (Optional check) This only happens when google does ARC - if (!email.includes('ARC-Authentication-Results')) { + if (!email.includes("ARC-Authentication-Results")) { return email; } - const googleReplacedMessageId = getHeaderValue( - email, - 'X-Google-Original-Message-ID', - ); + const googleReplacedMessageId = getHeaderValue(email, "X-Google-Original-Message-ID"); if (googleReplacedMessageId) { - return setHeaderValue(email, 'Message-ID', googleReplacedMessageId); + return setHeaderValue(email, "Message-ID", googleReplacedMessageId); } return email; @@ -34,7 +31,7 @@ function revertGoogleMessageId(email: string): string { // Remove labels inserted to Subject - `[ListName] Newsletter 2024` to `Newsletter 2024` function removeLabels(email: string): string { // Replace Subject: [label] with Subject: - const sanitized = email.replace(/Subject: \[.*\]/, 'Subject:'); + const sanitized = email.replace(/Subject: \[.*\]/, "Subject:"); return sanitized; } @@ -62,14 +59,9 @@ function insert13Before10(email: string): string { // Replace `=09` with `\t` in email // TODO: Add test for this function sanitizeTabs(email: string): string { - return email.replace('=09', '\t'); + return email.replace("=09", "\t"); } -const sanitizers = [ - revertGoogleMessageId, - removeLabels, - insert13Before10, - sanitizeTabs, -]; +const sanitizers = [revertGoogleMessageId, removeLabels, insert13Before10, sanitizeTabs]; export default sanitizers; diff --git a/packages/helpers/src/hash.ts b/packages/helpers/src/hash.ts index c5b662f6a..186bcbb96 100644 --- a/packages/helpers/src/hash.ts +++ b/packages/helpers/src/hash.ts @@ -1,5 +1,5 @@ -import { buildPoseidon } from 'circomlibjs'; -import { bigIntToChunkedBytes } from './binary-format'; +import { buildPoseidon } from "circomlibjs"; +import { bigIntToChunkedBytes } from "./binary-format"; export async function poseidonLarge(input: bigint, numChunks: number, bitsPerChunk: number) { const poseidon = await buildPoseidon(); diff --git a/packages/helpers/src/index.ts b/packages/helpers/src/index.ts index 34c50bc51..01ea5bcbe 100644 --- a/packages/helpers/src/index.ts +++ b/packages/helpers/src/index.ts @@ -1,4 +1,4 @@ -export * from './binary-format'; -export * from './constants'; -export * from './input-generators'; -export * from './sha-utils'; +export * from "./binary-format"; +export * from "./constants"; +export * from "./input-generators"; +export * from "./sha-utils"; diff --git a/packages/helpers/src/input-generators.ts b/packages/helpers/src/input-generators.ts index dea4b5754..705926879 100644 --- a/packages/helpers/src/input-generators.ts +++ b/packages/helpers/src/input-generators.ts @@ -1,7 +1,7 @@ -import { Uint8ArrayToCharArray, toCircomBigIntBytes } from './binary-format'; -import { MAX_BODY_PADDED_BYTES, MAX_HEADER_PADDED_BYTES } from './constants'; -import { DKIMVerificationResult, verifyDKIMSignature } from './dkim'; -import { generatePartialSHA, sha256Pad } from './sha-utils'; +import { Uint8ArrayToCharArray, toCircomBigIntBytes } from "./binary-format"; +import { MAX_BODY_PADDED_BYTES, MAX_HEADER_PADDED_BYTES } from "./constants"; +import { DKIMVerificationResult, verifyDKIMSignature } from "./dkim"; +import { generatePartialSHA, sha256Pad } from "./sha-utils"; type CircuitInput = { emailHeader: string[]; @@ -34,10 +34,10 @@ function removeSoftLineBreaks(body: string[]): string[] { let i = 0; while (i < body.length) { if ( - i + 2 < body.length - && body[i] === '61' // '=' character - && body[i + 1] === '13' // '\r' character - && body[i + 2] === '10' + i + 2 < body.length && + body[i] === "61" && // '=' character + body[i + 1] === "13" && // '\r' character + body[i + 2] === "10" ) { // '\n' character // Skip the soft line break sequence @@ -49,7 +49,7 @@ function removeSoftLineBreaks(body: string[]): string[] { } // Pad the result with zeros to make it the same length as the body while (result.length < body.length) { - result.push('0'); + result.push("0"); } return result; } @@ -61,10 +61,7 @@ function removeSoftLineBreaks(body: string[]): string[] { * @param params Arguments to control the input generation * @returns Circuit inputs for the EmailVerifier circuit */ -export async function generateEmailVerifierInputs( - rawEmail: Buffer | string, - params: InputGenerationArgs = {}, -) { +export async function generateEmailVerifierInputs(rawEmail: Buffer | string, params: InputGenerationArgs = {}) { const dkimResult = await verifyDKIMSignature(rawEmail); return generateEmailVerifierInputsFromDKIMResult(dkimResult, params); @@ -81,15 +78,10 @@ export function generateEmailVerifierInputsFromDKIMResult( dkimResult: DKIMVerificationResult, params: InputGenerationArgs = {}, ): CircuitInput { - const { - headers, body, bodyHash, publicKey, signature, - } = dkimResult; + const { headers, body, bodyHash, publicKey, signature } = dkimResult; // SHA add padding - const [messagePadded, messagePaddedLen] = sha256Pad( - headers, - params.maxHeadersLength || MAX_HEADER_PADDED_BYTES, - ); + const [messagePadded, messagePaddedLen] = sha256Pad(headers, params.maxHeadersLength || MAX_HEADER_PADDED_BYTES); const circuitInputs: CircuitInput = { emailHeader: Uint8ArrayToCharArray(messagePadded), // Packed into 1 byte signals @@ -101,12 +93,10 @@ export function generateEmailVerifierInputsFromDKIMResult( if (params.enableHeaderMasking) { circuitInputs.headerMask = params.headerMask; } - + if (!params.ignoreBodyHashCheck) { if (!body || !bodyHash) { - throw new Error( - 'body and bodyHash are required when ignoreBodyHashCheck is false', - ); + throw new Error("body and bodyHash are required when ignoreBodyHashCheck is false"); } const bodyHashIndex = headers.toString().indexOf(bodyHash); @@ -115,10 +105,7 @@ export function generateEmailVerifierInputsFromDKIMResult( // 65 comes from the 64 at the end and the 1 bit in the start, then 63 comes from the formula to round it up to the nearest 64. // see sha256algorithm.com for a more full explanation of padding length const bodySHALength = Math.floor((body.length + 63 + 65) / 64) * 64; - const [bodyPadded, bodyPaddedLen] = sha256Pad( - body, - Math.max(maxBodyLength, bodySHALength), - ); + const [bodyPadded, bodyPaddedLen] = sha256Pad(body, Math.max(maxBodyLength, bodySHALength)); const { precomputedSha, bodyRemaining, bodyRemainingLength } = generatePartialSHA({ body: bodyPadded, @@ -133,9 +120,7 @@ export function generateEmailVerifierInputsFromDKIMResult( circuitInputs.emailBody = Uint8ArrayToCharArray(bodyRemaining); if (params.removeSoftLineBreaks) { - circuitInputs.decodedEmailBodyIn = removeSoftLineBreaks( - circuitInputs.emailBody, - ); + circuitInputs.decodedEmailBodyIn = removeSoftLineBreaks(circuitInputs.emailBody); } if (params.enableBodyMasking) { diff --git a/packages/helpers/src/sha-utils.ts b/packages/helpers/src/sha-utils.ts index 0db2d8b84..0617fad93 100644 --- a/packages/helpers/src/sha-utils.ts +++ b/packages/helpers/src/sha-utils.ts @@ -1,16 +1,8 @@ -import * as CryptoJS from 'crypto'; -import { - assert, - int64toBytes, - int8toBytes, - mergeUInt8Arrays, -} from './binary-format'; -import { Hash } from './lib/fast-sha256'; - -export function findIndexInUint8Array( - array: Uint8Array, - selector: Uint8Array, -): number { +import * as CryptoJS from "crypto"; +import { assert, int64toBytes, int8toBytes, mergeUInt8Arrays } from "./binary-format"; +import { Hash } from "./lib/fast-sha256"; + +export function findIndexInUint8Array(array: Uint8Array, selector: Uint8Array): number { let i = 0; let j = 0; while (i < array.length) { @@ -70,7 +62,7 @@ export function generatePartialSHA({ } if (bodyRemaining.length % 64 !== 0) { - throw new Error('Remaining body was not padded correctly with int64s'); + throw new Error("Remaining body was not padded correctly with int64s"); } bodyRemaining = padUint8ArrayWithZeros(bodyRemaining, maxRemainingBodyLength); @@ -84,7 +76,7 @@ export function generatePartialSHA({ } export function shaHash(str: Uint8Array) { - return CryptoJS.createHash('sha256').update(str).digest(); + return CryptoJS.createHash("sha256").update(str).digest(); } export function partialSha(msg: Uint8Array, msgLen: number): Uint8Array { @@ -93,10 +85,7 @@ export function partialSha(msg: Uint8Array, msgLen: number): Uint8Array { } // Puts an end selector, a bunch of 0s, then the length, then fill the rest with 0s. -export function sha256Pad( - message: Uint8Array, - maxShaBytes: number, -): [Uint8Array, number] { +export function sha256Pad(message: Uint8Array, maxShaBytes: number): [Uint8Array, number] { const msgLen = message.length * 8; // bytes to bits const msgLenBytes = int64toBytes(msgLen); @@ -107,7 +96,7 @@ export function sha256Pad( } res = mergeUInt8Arrays(res, msgLenBytes); - assert((res.length * 8) % 512 === 0, 'Padding did not complete properly!'); + assert((res.length * 8) % 512 === 0, "Padding did not complete properly!"); const messageLen = res.length; while (res.length < maxShaBytes) { res = mergeUInt8Arrays(res, int64toBytes(0)); diff --git a/packages/helpers/tests/__mocks__/localforage.ts b/packages/helpers/tests/__mocks__/localforage.ts index 0cc485fdb..88128270f 100644 --- a/packages/helpers/tests/__mocks__/localforage.ts +++ b/packages/helpers/tests/__mocks__/localforage.ts @@ -1,22 +1,22 @@ -import fs from 'fs'; -import path from 'path'; +import fs from "fs"; +import path from "path"; const getUncompressedTestFile = (): ArrayBuffer => { - const buffer = fs.readFileSync(path.join(__dirname, '../test-data/compressed-files/uncompressed-value.txt')); + const buffer = fs.readFileSync(path.join(__dirname, "../test-data/compressed-files/uncompressed-value.txt")); return buffer; }; const tempStorage: Record = { - 'email.zkeyb': getUncompressedTestFile(), - 'email.zkeyc': getUncompressedTestFile(), - 'email.zkeyd': getUncompressedTestFile(), - 'email.zkeye': getUncompressedTestFile(), - 'email.zkeyf': getUncompressedTestFile(), - 'email.zkeyg': getUncompressedTestFile(), - 'email.zkeyh': getUncompressedTestFile(), - 'email.zkeyi': getUncompressedTestFile(), - 'email.zkeyj': getUncompressedTestFile(), - 'email.zkeyk': getUncompressedTestFile(), + "email.zkeyb": getUncompressedTestFile(), + "email.zkeyc": getUncompressedTestFile(), + "email.zkeyd": getUncompressedTestFile(), + "email.zkeye": getUncompressedTestFile(), + "email.zkeyf": getUncompressedTestFile(), + "email.zkeyg": getUncompressedTestFile(), + "email.zkeyh": getUncompressedTestFile(), + "email.zkeyi": getUncompressedTestFile(), + "email.zkeyj": getUncompressedTestFile(), + "email.zkeyk": getUncompressedTestFile(), }; const getItem = jest.fn((key) => tempStorage[key]); diff --git a/packages/helpers/tests/chunked-zkey.test.ts b/packages/helpers/tests/chunked-zkey.test.ts index a4c1a6047..93e7582e4 100644 --- a/packages/helpers/tests/chunked-zkey.test.ts +++ b/packages/helpers/tests/chunked-zkey.test.ts @@ -1,13 +1,13 @@ -import fs from 'fs'; -import path from 'path'; -import { StringDecoder } from 'string_decoder'; -import _localforage from 'localforage'; -import { downloadFromFilename, downloadProofFiles, uncompressGz as uncompress } from '../src/chunked-zkey'; -import { server } from './mocks/server'; -import { MOCK_BASE_URL } from './mocks/handlers'; +import fs from "fs"; +import path from "path"; +import { StringDecoder } from "string_decoder"; +import _localforage from "localforage"; +import { downloadFromFilename, downloadProofFiles, uncompressGz as uncompress } from "../src/chunked-zkey"; +import { server } from "./mocks/server"; +import { MOCK_BASE_URL } from "./mocks/handlers"; // this is mocked in __mocks__/localforage.ts -jest.mock('localforage'); +jest.mock("localforage"); const localforage = _localforage as jest.Mocked; @@ -24,24 +24,24 @@ afterAll(() => server.close()); // localforage should be storing ArrayBuffers. // We can use this function to simplify checking the mocked value of the ArrayBuffer. const decodeArrayBufferToString = (buffer: ArrayBuffer): string => { - const decoder = new StringDecoder('utf8'); + const decoder = new StringDecoder("utf8"); const str = decoder.write(Buffer.from(buffer)); return str; }; const getCompressedTestFile = (): ArrayBuffer => { - const buffer = fs.readFileSync(path.join(__dirname, 'test-data/compressed-files/compressed.txt.gz')); + const buffer = fs.readFileSync(path.join(__dirname, "test-data/compressed-files/compressed.txt.gz")); return buffer; }; const getUncompressedTestFile = (): ArrayBuffer => { - const buffer = fs.readFileSync(path.join(__dirname, 'test-data/compressed-files/uncompressed-value.txt')); + const buffer = fs.readFileSync(path.join(__dirname, "test-data/compressed-files/uncompressed-value.txt")); return buffer; }; -describe('Uncompress GZ file', () => { - test('Uncompresss a GZ file', async () => { - const decoder = new StringDecoder('utf8'); +describe("Uncompress GZ file", () => { + test("Uncompresss a GZ file", async () => { + const decoder = new StringDecoder("utf8"); const compressedArrayBuffer: ArrayBuffer = getCompressedTestFile(); const expectedArrayBuffer: ArrayBuffer = getUncompressedTestFile(); const expectedString = decoder.write(Buffer.from(expectedArrayBuffer)); @@ -51,13 +51,13 @@ describe('Uncompress GZ file', () => { }); }); -describe('Test zkp fetch and store', () => { +describe("Test zkp fetch and store", () => { afterEach(() => { jest.resetAllMocks(); }); - test('should fetch a gz file, uncompress it, and store it in indexeddb', async () => { - const filename = 'email.zkeyb.gz'; + test("should fetch a gz file, uncompress it, and store it in indexeddb", async () => { + const filename = "email.zkeyb.gz"; // downloadFileFromFilename requests the file from the server, which we mocked with msw. // The server returns a gz file of a file containing "not compressed 👍", // which is defined in __fixtures__/compressed-files/compressed.txt.gz @@ -69,28 +69,28 @@ describe('Test zkp fetch and store', () => { // expect to be called with... const str = decodeArrayBufferToString(decompressedBuffer); - expect(filenameRaw).toBe('email.zkeyb'); + expect(filenameRaw).toBe("email.zkeyb"); // check that it decompressed the file correctly. - expect(str).toBe('not compressed 👍'); + expect(str).toBe("not compressed 👍"); }); - test('should should download all the zkeys and save them in local storage for snarkjs to access.', async () => { + test("should should download all the zkeys and save them in local storage for snarkjs to access.", async () => { // downloadProofFiles calls downloadFromFilename 10 times, one for each zkey, b-k. const onDownloaded = jest.fn(); - await downloadProofFiles(MOCK_BASE_URL, 'email', onDownloaded); + await downloadProofFiles(MOCK_BASE_URL, "email", onDownloaded); expect(localforage.setItem).toBeCalledTimes(10); // check the first one const filenameRawB = localforage.setItem.mock.calls[0][0]; const decompressedBufferB = localforage.setItem.mock.calls[0][1] as ArrayBuffer; - expect(filenameRawB).toBe('email.zkeyb'); - expect(decodeArrayBufferToString(decompressedBufferB)).toBe('not compressed 👍'); + expect(filenameRawB).toBe("email.zkeyb"); + expect(decodeArrayBufferToString(decompressedBufferB)).toBe("not compressed 👍"); // ... c d e f g h i j ... assume these are fine too. // check the last one const filenameRawK = localforage.setItem.mock.calls[9][0]; const decompressedBufferK = localforage.setItem.mock.calls[9][1] as ArrayBuffer; - expect(filenameRawK).toBe('email.zkeyk'); - expect(decodeArrayBufferToString(decompressedBufferK)).toBe('not compressed 👍'); + expect(filenameRawK).toBe("email.zkeyk"); + expect(decodeArrayBufferToString(decompressedBufferK)).toBe("not compressed 👍"); expect(onDownloaded).toBeCalledTimes(10); }); }); diff --git a/packages/helpers/tests/dkim.test.ts b/packages/helpers/tests/dkim.test.ts index 54dd8ea42..fc057ccff 100644 --- a/packages/helpers/tests/dkim.test.ts +++ b/packages/helpers/tests/dkim.test.ts @@ -1,41 +1,33 @@ -import fs from 'fs'; -import path from 'path'; -import { verifyDKIMSignature } from '../src/dkim'; +import fs from "fs"; +import path from "path"; +import { verifyDKIMSignature } from "../src/dkim"; jest.setTimeout(10000); -describe('DKIM signature verification', () => { - it('should pass for valid email', async () => { - const email = fs.readFileSync( - path.join(__dirname, 'test-data/email-good.eml'), - ); +describe("DKIM signature verification", () => { + it("should pass for valid email", async () => { + const email = fs.readFileSync(path.join(__dirname, "test-data/email-good.eml")); const result = await verifyDKIMSignature(email); - expect(result.signingDomain).toBe('icloud.com'); + expect(result.signingDomain).toBe("icloud.com"); expect(result.appliedSanitization).toBeFalsy(); }); - it('should fail for invalid selector', async () => { - const email = fs.readFileSync( - path.join(__dirname, 'test-data/email-invalid-selector.eml'), - ); + it("should fail for invalid selector", async () => { + const email = fs.readFileSync(path.join(__dirname, "test-data/email-invalid-selector.eml")); expect.assertions(1); try { await verifyDKIMSignature(email); } catch (e) { - expect(e.message).toBe( - 'DKIM signature verification failed for domain icloud.com. Reason: no key', - ); + expect(e.message).toBe("DKIM signature verification failed for domain icloud.com. Reason: no key"); } }); - it('should fail for tampered body', async () => { - const email = fs.readFileSync( - path.join(__dirname, 'test-data/email-body-tampered.eml'), - ); + it("should fail for tampered body", async () => { + const email = fs.readFileSync(path.join(__dirname, "test-data/email-body-tampered.eml")); expect.assertions(1); @@ -43,33 +35,27 @@ describe('DKIM signature verification', () => { await verifyDKIMSignature(email); } catch (e) { expect(e.message).toBe( - 'DKIM signature verification failed for domain icloud.com. Reason: body hash did not verify', + "DKIM signature verification failed for domain icloud.com. Reason: body hash did not verify", ); } }); - it('should fail for when DKIM signature is not present for domain', async () => { + it("should fail for when DKIM signature is not present for domain", async () => { // In this email From address is user@gmail.com, but the DKIM signature is only for icloud.com - const email = fs.readFileSync( - path.join(__dirname, 'test-data/email-invalid-domain.eml'), - ); + const email = fs.readFileSync(path.join(__dirname, "test-data/email-invalid-domain.eml")); expect.assertions(1); try { await verifyDKIMSignature(email); } catch (e) { - expect(e.message).toBe( - 'DKIM signature not found for domain gmail.com', - ); + expect(e.message).toBe("DKIM signature not found for domain gmail.com"); } }); - it('should be able to override domain', async () => { + it("should be able to override domain", async () => { // From address domain is icloud.com - const email = fs.readFileSync( - path.join(__dirname, 'test-data/email-different-domain.eml'), - ); + const email = fs.readFileSync(path.join(__dirname, "test-data/email-different-domain.eml")); // Should pass with default domain await verifyDKIMSignature(email); @@ -79,26 +65,22 @@ describe('DKIM signature verification', () => { // different from From domain and the below check pass. expect.assertions(1); try { - await verifyDKIMSignature(email, 'domain.com'); + await verifyDKIMSignature(email, "domain.com"); } catch (e) { - expect(e.message).toBe( - 'DKIM signature not found for domain domain.com', - ); + expect(e.message).toBe("DKIM signature not found for domain domain.com"); } }); }); -describe('DKIM with sanitization', () => { - it('should pass after removing label from Subject', async () => { - const email = fs.readFileSync( - path.join(__dirname, 'test-data/email-good.eml'), - ); +describe("DKIM with sanitization", () => { + it("should pass after removing label from Subject", async () => { + const email = fs.readFileSync(path.join(__dirname, "test-data/email-good.eml")); // Add a label to the subject - const tamperedEmail = email.toString().replace('Subject: ', 'Subject: [EmailListABC]'); + const tamperedEmail = email.toString().replace("Subject: ", "Subject: [EmailListABC]"); const result = await verifyDKIMSignature(tamperedEmail); - expect(result.appliedSanitization).toBe('removeLabels'); + expect(result.appliedSanitization).toBe("removeLabels"); }); }); diff --git a/packages/helpers/tests/input-generators.test.ts b/packages/helpers/tests/input-generators.test.ts index 04ce0931e..3d3480db7 100644 --- a/packages/helpers/tests/input-generators.test.ts +++ b/packages/helpers/tests/input-generators.test.ts @@ -7,9 +7,7 @@ jest.setTimeout(10000); describe("Input generators", () => { it("should generate input from raw email", async () => { - const email = fs.readFileSync( - path.join(__dirname, "test-data/email-good.eml") - ); + const email = fs.readFileSync(path.join(__dirname, "test-data/email-good.eml")); const inputs = await generateEmailVerifierInputs(email); @@ -23,9 +21,7 @@ describe("Input generators", () => { }); it("should generate input without body params when ignoreBodyHash is true", async () => { - const email = fs.readFileSync( - path.join(__dirname, "test-data/email-good.eml") - ); + const email = fs.readFileSync(path.join(__dirname, "test-data/email-good.eml")); const inputs = await generateEmailVerifierInputs(email, { ignoreBodyHashCheck: true, @@ -41,9 +37,7 @@ describe("Input generators", () => { }); it("should generate input with SHA precompute selector", async () => { - const email = fs.readFileSync( - path.join(__dirname, "test-data/email-good-large.eml") - ); + const email = fs.readFileSync(path.join(__dirname, "test-data/email-good-large.eml")); const inputs = await generateEmailVerifierInputs(email, { shaPrecomputeSelector: "thousands", @@ -51,9 +45,7 @@ describe("Input generators", () => { expect(inputs.emailBody).toBeDefined(); - const strBody = bytesToString( - Uint8Array.from(inputs.emailBody!.map((b) => Number(b))) - ); + const strBody = bytesToString(Uint8Array.from(inputs.emailBody!.map((b) => Number(b)))); const expected = "h hundreds of thousands of blocks."; // will round till previous 64x th byte @@ -61,14 +53,12 @@ describe("Input generators", () => { }); it("should throw if SHA precompute selector is invalid", async () => { - const email = fs.readFileSync( - path.join(__dirname, "test-data/email-good.eml") - ); + const email = fs.readFileSync(path.join(__dirname, "test-data/email-good.eml")); await expect(() => generateEmailVerifierInputs(email, { shaPrecomputeSelector: "Bla Bla", - }) + }), ).rejects.toThrow('SHA precompute selector "Bla Bla" not found in the body'); }); }); diff --git a/scripts/dkim/update-dkim-registry.ts b/scripts/dkim/update-dkim-registry.ts index 9bd1ef5b9..4a9f9b195 100644 --- a/scripts/dkim/update-dkim-registry.ts +++ b/scripts/dkim/update-dkim-registry.ts @@ -1,22 +1,22 @@ -import { ethers, JsonRpcProvider } from "ethers"; -import { buildPoseidon } from "circomlibjs"; -import dns from "dns"; -import path from "path"; -import forge from "node-forge"; -import { bigIntToChunkedBytes } from "@zk-email/helpers/src/binaryFormat"; -const fs = require("fs"); -import { abi } from "../abis/DKIMRegistry.json"; -import { poseidonLarge } from "@zk-email/helpers/src/hash"; -require("dotenv").config(); +import { ethers, JsonRpcProvider } from 'ethers'; +import { buildPoseidon } from 'circomlibjs'; +import dns from 'dns'; +import path from 'path'; +import forge from 'node-forge'; +import { bigIntToChunkedBytes } from '@zk-email/helpers/src/binaryFormat'; +const fs = require('fs'); +import { abi } from '../abis/DKIMRegistry.json'; +import { poseidonLarge } from '@zk-email/helpers/src/hash'; +require('dotenv').config(); async function updateContract(domain: string, pubkeyHashes: string[]) { if (!pubkeyHashes.length) { return; } - if (!process.env.PRIVATE_KEY) throw new Error("Env private key found"); - if (!process.env.RPC_URL) throw new Error("Env RPC URL found"); - if (!process.env.DKIM_REGISTRY) throw new Error("Env DKIM_REGISTRY found"); + if (!process.env.PRIVATE_KEY) throw new Error('Env private key found'); + if (!process.env.RPC_URL) throw new Error('Env RPC URL found'); + if (!process.env.DKIM_REGISTRY) throw new Error('Env DKIM_REGISTRY found'); const provider = new JsonRpcProvider(process.env.RPC_URL); const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider); @@ -30,11 +30,7 @@ async function updateContract(domain: string, pubkeyHashes: string[]) { console.log(`Updated hashes for domain ${domain}. Tx: ${tx.hash}`); } -async function getPublicKeyForDomainAndSelector( - domain: string, - selector: string, - print: boolean = true -) { +async function getPublicKeyForDomainAndSelector(domain: string, selector: string, print: boolean = true) { // Construct the DKIM record name let dkimRecordName = `${selector}._domainkey.${domain}`; if (print) console.log(dkimRecordName); @@ -53,7 +49,7 @@ async function getPublicKeyForDomainAndSelector( // The DKIM record is a TXT record containing a string // We need to parse this string to get the public key - let dkimRecord = records[0].join(""); + let dkimRecord = records[0].join(''); let match = dkimRecord.match(/p=([^;]+)/); if (!match) { console.error(`No public key found in DKIM record for ${domain}`); @@ -62,16 +58,16 @@ async function getPublicKeyForDomainAndSelector( // The public key is base64 encoded, we need to decode it let pubkey = match[1]; - let binaryKey = Buffer.from(pubkey, "base64").toString("base64"); + let binaryKey = Buffer.from(pubkey, 'base64').toString('base64'); // Get match let matches = binaryKey.match(/.{1,64}/g); if (!matches) { - console.error("No matches found"); + console.error('No matches found'); return; } - let formattedKey = matches.join("\n"); - if (print) console.log("Key: ", formattedKey); + let formattedKey = matches.join('\n'); + if (print) console.log('Key: ', formattedKey); // Convert to PEM format let pemKey = `-----BEGIN PUBLIC KEY-----\n${formattedKey}\n-----END PUBLIC KEY-----`; @@ -81,18 +77,14 @@ async function getPublicKeyForDomainAndSelector( // Get the modulus n only let n = publicKey.n; - if (print) console.log("Modulus n:", n.toString(16)); + if (print) console.log('Modulus n:', n.toString(16)); return BigInt(publicKey.n.toString()); } async function checkSelector(domain: string, selector: string) { try { - const publicKey = await getPublicKeyForDomainAndSelector( - domain, - selector, - false - ); + const publicKey = await getPublicKeyForDomainAndSelector(domain, selector, false); if (publicKey) { console.log(`Domain: ${domain}, Selector: ${selector} - Match found`); return { @@ -105,9 +97,7 @@ async function checkSelector(domain: string, selector: string) { // console.log(`Domain: ${domain}, Selector: ${selector} - No match found`); } } catch (error) { - console.error( - `Error processing domain: ${domain}, Selector: ${selector} - ${error}` - ); + console.error(`Error processing domain: ${domain}, Selector: ${selector} - ${error}`); } return { @@ -121,57 +111,57 @@ async function checkSelector(domain: string, selector: string) { // Filename is a file where each line is a domain // This searches for default selectors like "google" or "default" async function getDKIMPublicKeysForDomains(filename: string) { - const domains = fs.readFileSync(filename, "utf8").split("\n"); + const domains = fs.readFileSync(filename, 'utf8').split('\n'); const selectors = [ - "google", - "default", - "mail", - "smtpapi", - "dkim", - "200608", - "20230601", - "20221208", - "20210112", - "dkim-201406", - "1a1hai", - "v1", - "v2", - "v3", - "k1", - "k2", - "k3", - "hs1", - "hs2", - "s1", - "s2", - "s3", - "sig1", - "sig2", - "sig3", - "selector", - "selector1", - "selector2", - "mindbox", - "bk", - "sm1", - "sm2", - "gmail", - "10dkim1", - "11dkim1", - "12dkim1", - "memdkim", - "m1", - "mx", - "sel1", - "bk", - "scph1220", - "ml", - "pps1", - "scph0819", - "skiff1", - "s1024", - "selector1", - "dkim-202308" + 'google', + 'default', + 'mail', + 'smtpapi', + 'dkim', + '200608', + '20230601', + '20221208', + '20210112', + 'dkim-201406', + '1a1hai', + 'v1', + 'v2', + 'v3', + 'k1', + 'k2', + 'k3', + 'hs1', + 'hs2', + 's1', + 's2', + 's3', + 'sig1', + 'sig2', + 'sig3', + 'selector', + 'selector1', + 'selector2', + 'mindbox', + 'bk', + 'sm1', + 'sm2', + 'gmail', + '10dkim1', + '11dkim1', + '12dkim1', + 'memdkim', + 'm1', + 'mx', + 'sel1', + 'bk', + 'scph1220', + 'ml', + 'pps1', + 'scph0819', + 'skiff1', + 's1024', + 'selector1', + 'dkim-202308', ]; let results = []; @@ -196,9 +186,7 @@ async function getDKIMPublicKeysForDomains(filename: string) { const publicKey = result.publicKey.toString(); - if ( - !matchedSelectors[result.domain].find((d) => d.publicKey === publicKey) - ) { + if (!matchedSelectors[result.domain].find((d) => d.publicKey === publicKey)) { matchedSelectors[result.domain].push({ selector: result.selector, publicKey, @@ -210,26 +198,17 @@ async function getDKIMPublicKeysForDomains(filename: string) { return matchedSelectors; } -async function updateDKIMRegistry({ - domainListFile, - writeToFile, -}: { - domainListFile: string; - writeToFile: boolean; -}) { +async function updateDKIMRegistry({ domainListFile, writeToFile }: { domainListFile: string; writeToFile: boolean }) { function _writeToFile(filename: string, data: object) { if (!writeToFile) return; - if (!fs.existsSync(path.join(__dirname, "out"))) { - fs.mkdirSync(path.join(__dirname, "out")); + if (!fs.existsSync(path.join(__dirname, 'out'))) { + fs.mkdirSync(path.join(__dirname, 'out')); } - fs.writeFileSync( - path.join(__dirname, "out/" + filename), - JSON.stringify(data, null, 2) - ); + fs.writeFileSync(path.join(__dirname, 'out/' + filename), JSON.stringify(data, null, 2)); } const domainPubKeyMap = await getDKIMPublicKeysForDomains(domainListFile); - _writeToFile("dkim-keys.json", domainPubKeyMap); + _writeToFile('dkim-keys.json', domainPubKeyMap); // const domainPubKeyMap = JSON.parse( // fs.readFileSync(path.join(__dirname, "out/dkim-keys.json")).toString() @@ -249,7 +228,7 @@ async function updateDKIMRegistry({ chunkedDKIMPubKeyMap[domain].push(pubkeyChunked.map((s) => s.toString())); } } - _writeToFile("dkim-keys-chunked.json", chunkedDKIMPubKeyMap); + _writeToFile('dkim-keys-chunked.json', chunkedDKIMPubKeyMap); // Generate pub key hash using 242 * 9 chunks (Poseidon lib don't take more than 16 inputs) const domainHashedPubKeyMap: { [key: string]: string[] } = {}; @@ -264,7 +243,7 @@ async function updateDKIMRegistry({ domainHashedPubKeyMap[domain].push(poseidonHash.toString()); } } - _writeToFile("dkim-keys-hashed.json", domainHashedPubKeyMap); + _writeToFile('dkim-keys-hashed.json', domainHashedPubKeyMap); // Update Mailserver contract with found keys for (let domain of Object.keys(domainHashedPubKeyMap)) { @@ -273,6 +252,6 @@ async function updateDKIMRegistry({ } updateDKIMRegistry({ - domainListFile: path.join(__dirname, "domains.txt"), + domainListFile: path.join(__dirname, 'domains.txt'), writeToFile: true, }); diff --git a/yarn.lock b/yarn.lock index d144c86c0..6a20f568c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7168,6 +7168,15 @@ __metadata: languageName: node linkType: hard +"prettier@npm:^3.3.3": + version: 3.3.3 + resolution: "prettier@npm:3.3.3" + bin: + prettier: bin/prettier.cjs + checksum: bc8604354805acfdde6106852d14b045bb20827ad76a5ffc2455b71a8257f94de93f17f14e463fe844808d2ccc87248364a5691488a3304f1031326e62d9276e + languageName: node + linkType: hard + "pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0": version: 29.7.0 resolution: "pretty-format@npm:29.7.0" @@ -7455,6 +7464,8 @@ __metadata: "root-workspace-0b6124@workspace:.": version: 0.0.0-use.local resolution: "root-workspace-0b6124@workspace:." + dependencies: + prettier: ^3.3.3 languageName: unknown linkType: soft