Skip to content

Commit

Permalink
Merge branch 'main' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
saleel committed Feb 6, 2024
2 parents 2777710 + 3ba8106 commit da01de3
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 14 deletions.
21 changes: 21 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -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 #<issue number>" 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
7 changes: 7 additions & 0 deletions packages/circuits/email-verifier.circom
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand All @@ -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;"
Expand Down Expand Up @@ -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);
Expand Down
14 changes: 0 additions & 14 deletions packages/circuits/helpers/extract.circom
Original file line number Diff line number Diff line change
Expand Up @@ -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<a) {
r++;
n *= 2;
}
return r;
}

// Pack size is # of chunks i.e. number of char signals that fit into a signal (default 7 but can be 30)
template PackBytes(max_in_signals, max_out_signals, pack_size) {
assert(max_out_signals == ((max_in_signals - 1) \ pack_size + 1)); // Packing constant is wrong
Expand Down
35 changes: 35 additions & 0 deletions packages/circuits/helpers/utils.circom
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ include "circomlib/circuits/comparators.circom";
include "circomlib/circuits/mimcsponge.circom";
include "./fp.circom";

// 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<a) {
r++;
n *= 2;
}
return r;
}

// returns ceil(log2(a+1))
function log2_ceil(a) {
var n = a+1;
Expand Down Expand Up @@ -123,6 +137,27 @@ template Bytes2Packed(n){
}
}

// Asserts input contains only 0s from start_index to in_array_len
template AssertZeroes(in_array_len) {
var len_bits = log2(in_array_len);
assert(in_array_len <= (1 << len_bits));

signal input in[in_array_len];
signal input start_index;

// TODO: Should we check padding_start_index < in_array_len AND/OR
// log2(padding_start_index) <= len_bits

component lessThans[in_array_len];

for (var i = 0; i < in_array_len; i++) {
lessThans[i] = LessThan(len_bits);
lessThans[i].in[0] <== start_index - 1;
lessThans[i].in[1] <== i;
lessThans[i].out * in[i] === 0;
}
}

// salt_is_message_id_from, custom_anon_from_hashed_salt = MakeAnonEmailSalt(max_email_from_len, max_message_id_len)(email_from, custom_message_id_from, shifted_message_id)
template MakeAnonEmailSalt(email_len, blinder_len) {
signal input email[email_len];
Expand Down
44 changes: 44 additions & 0 deletions packages/circuits/tests/email-verifier.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,28 @@ describe("EmailVerifier", () => {
}
});

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;
Expand All @@ -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";

Expand Down

0 comments on commit da01de3

Please sign in to comment.