diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..2aefe7916 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,21 @@ +## Description + +Please include a summary of the changes and the related issue. Please also include relevant motivation and context. +Mention the issue number that your changes are addressing. Use the format "Fixes #" to automatically link your pull request to the relevant issue. + +## Type of Change + +Please delete options that are not relevant. + +- [ ] Bug fix (non-breaking change which fixes an issue) +- [ ] New feature (non-breaking change which adds functionality) +- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) +- [ ] This change requires a documentation update + +## Checklist: + +- [ ] I have discussed with the team prior to submitting this PR +- [ ] I have performed a self-review of my code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] My changes generate no new warnings +- [ ] New and existing unit tests pass locally with my changes diff --git a/packages/circuits/email-verifier.circom b/packages/circuits/email-verifier.circom index bd4be9090..ba4319226 100644 --- a/packages/circuits/email-verifier.circom +++ b/packages/circuits/email-verifier.circom @@ -6,6 +6,7 @@ include "./helpers/sha.circom"; include "./helpers/rsa.circom"; include "./helpers/base64.circom"; include "./helpers/extract.circom"; +include "./helpers/utils.circom"; // include "./regexes/body_hash_regex.circom"; include "@zk-email/zk-regex-circom/circuits/common/body_hash_regex.circom"; @@ -30,6 +31,9 @@ template EmailVerifier(max_header_bytes, max_body_bytes, n, k, ignore_body_hash_ // Base 64 body hash variables var LEN_SHA_B64 = 44; // ceil(32 / 3) * 4, due to base64 encoding. + // Assert padding is all zeroes + AssertZeroes(max_header_bytes)(in_padded, in_len_padded_bytes + 1); + // SHA HEADER: 506,670 constraints // This calculates the SHA256 hash of the header, which is the "base_msg" that is RSA signed. // The header signs the fields in the "h=Date:From:To:Subject:MIME-Version:Content-Type:Message-ID;" @@ -89,6 +93,9 @@ template EmailVerifier(max_header_bytes, max_body_bytes, n, k, ignore_body_hash_ signal input in_body_padded[max_body_bytes]; signal input in_body_len_padded_bytes; + // Assert padding is all zeroes + AssertZeroes(max_body_bytes)(in_body_padded, in_body_len_padded_bytes + 1); + // This verifies that the hash of the body, when calculated from the precomputed part forwards, // actually matches the hash in the header signal sha_body_out[256] <== Sha256BytesPartial(max_body_bytes)(in_body_padded, in_body_len_padded_bytes, precomputed_sha); diff --git a/packages/circuits/helpers/extract.circom b/packages/circuits/helpers/extract.circom index 6cc9199db..c02055c0d 100644 --- a/packages/circuits/helpers/extract.circom +++ b/packages/circuits/helpers/extract.circom @@ -4,20 +4,6 @@ include "./utils.circom"; // A set of utils for shifting and packing signal arrays // Performs extraction of reveal signals and packed signals -// From https://github.com/iden3/circomlib/blob/master/circuits/multiplexer.circom -function log2(a) { - if (a == 0) { - return 0; - } - var n = 1; - var r = 1; - while (n { } }); + it("should fail if message padding is tampered", async function () { + const emailVerifierInputs = generateCircuitInputs({ + rsaSignature: dkimResult.signature, + rsaPublicKey: dkimResult.publicKey, + body: dkimResult.body, + bodyHash: dkimResult.bodyHash, + message: dkimResult.message, + shaPrecomputeSelector: "How are", + maxMessageLength: 640, + maxBodyLength: 768, + }); + emailVerifierInputs.in_padded[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; @@ -158,6 +180,28 @@ describe("EmailVerifier", () => { } }); + it("should fail if body padding is tampered", async function () { + const emailVerifierInputs = generateCircuitInputs({ + rsaSignature: dkimResult.signature, + rsaPublicKey: dkimResult.publicKey, + body: dkimResult.body, + bodyHash: dkimResult.bodyHash, + message: dkimResult.message, + shaPrecomputeSelector: "How are", + maxMessageLength: 640, + maxBodyLength: 768, + }); + emailVerifierInputs.in_body_padded[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";