Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hash RSA public key in circuit #100

Merged
merged 23 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
edfdc1e
feat: add pubkey hash output, rename modulus to pubkey
saleel Aug 22, 2023
04f9103
chore: add test for dkim compression
saleel Aug 23, 2023
d41bb95
chore: add popular domain hash to DKIMRegistry contract
saleel Aug 23, 2023
56a4aee
chore: update twitter circuits to use pubkey hash
saleel Aug 24, 2023
6e8ddb4
feat: add DKIMRegistry contract
saleel Aug 24, 2023
9f9d57a
feat: update twitter verifier circuit to user pubkey hash (test failing)
saleel Aug 24, 2023
b558310
feat: change pubkey hash to MIMC
saleel Aug 26, 2023
66464a7
chore: update DKIMRegistry to use MIMC
saleel Aug 26, 2023
866398d
chore: fix in circuit scripts
saleel Aug 28, 2023
1197d62
chore: update contracts to use new vkey
saleel Aug 29, 2023
9802dda
chore: update app to use new circuit
saleel Aug 29, 2023
809ccec
chore: update package versions
saleel Aug 29, 2023
e388b82
chore: fix vkey in app
saleel Aug 29, 2023
b4dc6e9
chore: update contract config in app
saleel Aug 29, 2023
4254cf1
feat: add dirname to aws upload script
saleel Aug 29, 2023
6a43bc1
chore: update proof in test
saleel Aug 29, 2023
640db2c
fix: fix array index error in contract
saleel Aug 30, 2023
de946e6
chore: use new version of viem and wagmi in app
saleel Aug 30, 2023
890960b
chore: fix failing e2e tests
saleel Aug 30, 2023
55666ce
chore: update wallet conect key
saleel Aug 30, 2023
12a4230
chore: remove tsc from app build as it case build fail (temp)
saleel Aug 31, 2023
245013e
chore: fix build issue in twitter app
saleel Aug 31, 2023
09bb172
chore: use contract address from .env
saleel Sep 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,6 @@
"jest": "^29.5.0",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1",
"typescript": "^5.1.6"
"typescript": "^5.2.2"
}
}
14 changes: 12 additions & 2 deletions packages/circuits/email-verifier.circom
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pragma circom 2.1.5;

include "circomlib/circuits/bitify.circom";
include "circomlib/circuits/mimcsponge.circom";
include "./helpers/sha.circom";
include "./helpers/rsa.circom";
include "./helpers/base64.circom";
Expand All @@ -20,7 +21,7 @@ template EmailVerifier(max_header_bytes, max_body_bytes, n, k, ignore_body_hash_
assert(n < (255 \ 2)); // we want a multiplication to fit into a circom signal

signal input in_padded[max_header_bytes]; // prehashed email data, includes up to 512 + 64? bytes of padding pre SHA256, and padded with lots of 0s at end after the length
signal input modulus[k]; // rsa pubkey, verified with smart contract + DNSSEC proof. split up into k parts of n bits each.
signal input pubkey[k]; // rsa pubkey, verified with smart contract + DNSSEC proof. split up into k parts of n bits each.
signal input signature[k]; // rsa signature. split up into k parts of n bits each.
signal input in_len_padded_bytes; // length of in email data including the padding, which will inform the sha256 block length

Expand All @@ -34,6 +35,8 @@ template EmailVerifier(max_header_bytes, max_body_bytes, n, k, ignore_body_hash_
// section of the "DKIM-Signature:"" line, along with the body hash.
// Note that nothing above the "DKIM-Signature:" line is signed.
signal output sha[256] <== Sha256Bytes(max_header_bytes)(in_padded, in_len_padded_bytes);
signal output pubkey_hash;

var msg_len = (256 + n) \ n;

component base_msg[msg_len];
Expand All @@ -56,7 +59,7 @@ template EmailVerifier(max_header_bytes, max_body_bytes, n, k, ignore_body_hash_
for (var i = msg_len; i < k; i++) {
rsa.base_message[i] <== 0;
}
rsa.modulus <== modulus;
rsa.modulus <== pubkey;
rsa.signature <== signature;


Expand Down Expand Up @@ -101,4 +104,11 @@ template EmailVerifier(max_header_bytes, max_body_bytes, n, k, ignore_body_hash_
sha_body_bytes[i].out === sha_b64_out[i];
}
}

// Calculate the hash (MIMC) of public key and produce as an output
// This can be used to verify the public key is correct in contract without requiring the actual key
component hasher = MiMCSponge(k, 220, 1);
hasher.ins <== pubkey;
hasher.k <== 123;
pubkey_hash <== hasher.outs[0];
}
4 changes: 2 additions & 2 deletions packages/circuits/package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"name": "@zk-email/circuits",
"version": "1.1.2",
"version": "2.0.0",
"scripts": {
"publish": "yarn npm publish --access=public",
"test": "NODE_OPTIONS=--max_old_space_size=4096 jest tests/*.ts"
"test": "NODE_OPTIONS=--max_old_space_size=8192 jest tests/*.ts"
},
"devDependencies": {
"circom_tester": "^0.0.19",
Expand Down
2 changes: 1 addition & 1 deletion packages/circuits/tests/email-verifier-test.circom
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ pragma circom 2.1.5;

include "../email-verifier.circom";

component main { public [ modulus ] } = EmailVerifier(640, 768, 121, 17, 0);
component main { public [ pubkey ] } = EmailVerifier(640, 768, 121, 17, 0);
64 changes: 42 additions & 22 deletions packages/circuits/tests/email-verifier.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import fs from "fs";
import { buildMimcSponge } from "circomlibjs";
import { wasm as wasm_tester } from "circom_tester";
import { Scalar } from "ffjavascript";
import path from "path";
import { DKIMVerificationResult } from "@zk-email/helpers/src/dkim";
import { generateCircuitInputs } from "@zk-email/helpers/src/input-helpers";

const { verifyDKIMSignature } = require("@zk-email/helpers/src/dkim");
const fs = require("fs");
const path = require("path");
const wasm_tester = require("circom_tester").wasm;
const F1Field = require("ffjavascript").F1Field;
const Scalar = require("ffjavascript").Scalar;
import { verifyDKIMSignature } from "@zk-email/helpers/src/dkim";

exports.p = Scalar.fromString(
"21888242871839275222246405745257275088548364400416034343698204186575808495617"
Expand All @@ -19,18 +18,15 @@ describe("EmailVerifier", () => {
let circuit: any;

beforeAll(async () => {
const rawEmail = fs.readFileSync(
path.join(__dirname, "./test.eml"),
"utf8"
);
const rawEmail = fs.readFileSync(path.join(__dirname, "./test.eml"));
dkimResult = await verifyDKIMSignature(rawEmail);

circuit = await wasm_tester(
path.join(__dirname, "./email-verifier-test.circom"),
{
// NOTE: We are running tests against pre-compiled circuit in the below path
// You need to manually compile when changes are made to circuit if `recompile` is set to `false`.
// circom "./tests/email-verifier-test.circom" --r1cs --wasm --sym --c --wat --output "./tests/compiled-test-circuit"
// @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-circuit"
recompile: true,
output: path.join(__dirname, "./compiled-test-circuit"),
include: path.join(__dirname, "../../../node_modules"),
Expand All @@ -41,7 +37,7 @@ describe("EmailVerifier", () => {
it("should verify email without any SHA precompute selector", async function () {
const emailVerifierInputs = generateCircuitInputs({
rsaSignature: dkimResult.signature,
rsaModulus: dkimResult.modulus,
rsaPublicKey: dkimResult.publicKey,
body: dkimResult.body,
bodyHash: dkimResult.bodyHash,
message: dkimResult.message,
Expand All @@ -56,7 +52,7 @@ describe("EmailVerifier", () => {
it("should verify email with a SHA precompute selector", async function () {
const emailVerifierInputs = generateCircuitInputs({
rsaSignature: dkimResult.signature,
rsaModulus: dkimResult.modulus,
rsaPublicKey: dkimResult.publicKey,
body: dkimResult.body,
bodyHash: dkimResult.bodyHash,
message: dkimResult.message,
Expand All @@ -74,7 +70,7 @@ describe("EmailVerifier", () => {

const emailVerifierInputs = generateCircuitInputs({
rsaSignature: invalidRSASignature,
rsaModulus: dkimResult.modulus,
rsaPublicKey: dkimResult.publicKey,
body: dkimResult.body,
bodyHash: dkimResult.bodyHash,
message: dkimResult.message,
Expand All @@ -95,7 +91,7 @@ describe("EmailVerifier", () => {
it("should fail if precompute string is not found in body", async function () {
const emailVerifierInputs = generateCircuitInputs({
rsaSignature: dkimResult.signature,
rsaModulus: dkimResult.modulus,
rsaPublicKey: dkimResult.publicKey,
body: dkimResult.body,
bodyHash: dkimResult.bodyHash,
message: dkimResult.message,
Expand All @@ -119,7 +115,7 @@ describe("EmailVerifier", () => {

const emailVerifierInputs = generateCircuitInputs({
rsaSignature: dkimResult.signature,
rsaModulus: dkimResult.modulus,
rsaPublicKey: dkimResult.publicKey,
body: dkimResult.body,
bodyHash: dkimResult.bodyHash,
message: invalidMessage,
Expand All @@ -143,7 +139,7 @@ describe("EmailVerifier", () => {

const emailVerifierInputs = generateCircuitInputs({
rsaSignature: dkimResult.signature,
rsaModulus: dkimResult.modulus,
rsaPublicKey: dkimResult.publicKey,
body: invalidBody,
bodyHash: dkimResult.bodyHash,
message: dkimResult.message,
Expand All @@ -162,11 +158,11 @@ describe("EmailVerifier", () => {
});

it("should fail if body hash is tampered", async function () {
const invalidBodyHash = dkimResult.bodyHash + 'a';
const invalidBodyHash = dkimResult.bodyHash + "a";

const emailVerifierInputs = generateCircuitInputs({
rsaSignature: dkimResult.signature,
rsaModulus: dkimResult.modulus,
rsaPublicKey: dkimResult.publicKey,
body: dkimResult.body,
bodyHash: invalidBodyHash,
message: dkimResult.message,
Expand All @@ -183,4 +179,28 @@ describe("EmailVerifier", () => {
expect((error as Error).message).toMatch("Assert Failed");
}
});

it("should produce dkim pubkey hash correctly", 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,
});

// Calculate the MIMC hash
const mimc = await buildMimcSponge();
const hash = mimc.multiHash(emailVerifierInputs.pubkey, 123, 1);

// Calculate the hash using the circuit
const witness = await circuit.calculateWitness(emailVerifierInputs);

await circuit.assertOut(witness, {
pubkey_hash: mimc.F.toObject(hash),
});
});
});
2 changes: 1 addition & 1 deletion packages/circuits/tests/no-body-hash.test.circom
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ pragma circom 2.1.5;

include "../email-verifier.circom";

component main { public [ modulus ] } = EmailVerifier(640, 768, 121, 17, 1);
component main { public [ pubkey ] } = EmailVerifier(640, 768, 121, 17, 1);
5 changes: 1 addition & 4 deletions packages/circuits/tests/no-body-hash.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,6 @@ describe("EmailVerifier : Without body check", () => {
circuit = await wasm_tester(
path.join(__dirname, "./no-body-hash.test.circom"),
{
// NOTE: We are running tests against pre-compiled circuit in the below path
// You need to manually compile when changes are made to circuit if `recompile` is set to `false`.
// circom "./tests/email-verifier-test.circom" --r1cs --wasm --sym --c --wat --output "./tests/compiled-test-circuit"
recompile: true,
output: path.join(__dirname, "./compiled-test-circuit"),
include: path.join(__dirname, "../../../node_modules"),
Expand All @@ -42,7 +39,7 @@ describe("EmailVerifier : Without body check", () => {
// The result wont have shaPrecomputeSelector, maxMessageLength, maxBodyLength, ignoreBodyHashCheck
const emailVerifierInputs = generateCircuitInputs({
rsaSignature: dkimResult.signature,
rsaModulus: dkimResult.modulus,
rsaPublicKey: dkimResult.publicKey,
body: dkimResult.body,
bodyHash: dkimResult.bodyHash,
message: dkimResult.message,
Expand Down
80 changes: 14 additions & 66 deletions packages/circuits/tests/test.eml
Original file line number Diff line number Diff line change
@@ -1,70 +1,18 @@
Return-path: <[email protected]>
Original-recipient: rfc822;[email protected]
Received: from ci74p00im-qukt09071102.me.com by p59-mailgateway-smtp-6776dc6585-266mm (mailgateway 2318B155)
with SMTP id 4bbe59e4-9423-43ff-9c6e-d47053303967
for <[email protected]>; Thu, 22 Jun 2023 20:02:52 GMT
X-Apple-MoveToFolder: INBOX
X-Apple-Action: MOVE_TO_FOLDER/INBOX
X-Apple-UUID: 4bbe59e4-9423-43ff-9c6e-d47053303967
Received: from pv50p00im-ztdg10012001.me.com (pv50p00im-ztdg10012001.me.com [17.58.6.51])
by ci74p00im-qukt09071102.me.com (Postfix) with ESMTPS id 491294B00172
for <[email protected]>; Thu, 22 Jun 2023 20:02:47 +0000 (UTC)
X-ICL-SCORE: 3.233003230041
X-ICL-INFO: GAtbVUseBFFGSVZESgMGUkFIRFcUWUIPAApbVRYSFhEAREQZF15TQFUcAkpaQ1cOEBwKNxVVGAEa
FERXHlQLQBgcSBQXXRRCBhAWSloBAUxAQUhBVgUHQFURAxsXDRQSA0xWB0gAXw9YAxITHwEGUkRL
VkdJHlsHWxoJGloQRhYHREQHDgUGEkVJDxpVSkIGEkhWR0kCBlJEVwsSVlNZD1dZAhNFElsHWxoJ
GloQWwsRRERLPnEOUENMVUBVAAYgM1RSQ0x1GyBNSyJaTQZzIEBPIEE+AwFTNRQDWRtfW1xXWRQU
RRJFAxkcAxs4Q1cOEBwKWQBJTEA=
Authentication-Results: bimi.icloud.com; bimi=declined
X-ARC-Info: policy=fail; arc=none
Authentication-Results: arc.icloud.com; arc=none
Authentication-Results: dmarc.icloud.com; dmarc=pass header.from=me.com
X-DMARC-Info: pass=pass; dmarc-policy=quarantine; s=r0; d=r1; pdomain=me.com
X-DMARC-Policy: v=DMARC1; p=quarantine; rua=mailto:[email protected]; ruf=mailto:[email protected];
Authentication-Results: dkim-verifier.icloud.com;
dkim=pass (2048-bit key) header.d=me.com [email protected] header.b=FpmCwgC9
Authentication-Results: spf.icloud.com; spf=none (spf.icloud.com: [email protected] does not designate permitted sender hosts) [email protected]
Received-SPF: none (spf.icloud.com: [email protected] does not designate permitted sender hosts) receiver=spf.icloud.com; client-ip=17.58.6.51; helo=pv50p00im-ztdg10012001.me.com; [email protected]
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=me.com; s=1a1hai;
t=1687464166; bh=FxfpVjf51msd35Z/BVrd7Uv2GcGRjxaW9dGr90Y2M88=;
h=From:Content-Type:Mime-Version:Subject:Message-Id:Date:To;
b=FpmCwgC9dKuHA2BXHuO6Ujfls+b5psK4yurk9TkR9Q2rpH8ahp6XDuDRQW/8mucud
kIoudejHdNfXqUpWzmtcVCff+cWyzBJarYt8YKgPKQZ3f+UZqXMPJ7t1sZbIJkSU7n
zTiXh+F7KX/kIsJ1vUHnf40EsULPMU2CUSdBxvHpH4C2+MiVcx/mazLdh9BlpKavwY
QMl3uEDcH/blpvK7YRFn3eYpFL8TkS819/aBUcQg6aGZMJrGsr+cQFCsPmXTPHbxBD
LUnk7BUdzdvIwPDnOOrFJIf6JCbJd1rWIiGy3ZOP67+h79eHkQPcZxl/fq0BFQypPq
TOvKhhMsQAmHw==
Received: from smtpclient.apple (pv50p00im-dlb-asmtp-mailmevip.me.com [17.56.9.10])
by pv50p00im-ztdg10012001.me.com (Postfix) with ESMTPSA id A9258A01A4
for <[email protected]>; Thu, 22 Jun 2023 20:02:44 +0000 (UTC)
From: Saleel <[email protected]>
Content-Type: text/plain;
charset=us-ascii
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=icloud.com; s=1a1hai; t=1693038337; bh=7xQMDuoVVU4m0W0WRVSrVXMeGSIASsnucK9dJsrc+vU=; h=from:Content-Type:Mime-Version:Subject:Message-Id:Date:to; b=EhLyVPpKD7d2/+h1nrnu+iEEBDfh6UWiAf9Y5UK+aPNLt3fAyEKw6Ic46v32NOcZD
M/zhXWucN0FXNiS0pz/QVIEy8Bcdy7eBZA0QA1fp8x5x5SugDELSRobQNbkOjBg7Mx
VXy7h4pKZMm/hKyhvMZXK4AX9fSoXZt4VGlAFymFNavfdAeKgg/SHXLds4lOPJV1wR
2E21g853iz5m/INq3uK6SQKzTnz/wDkdyiq90gC0tHQe8HpDRhPIqgL5KSEpuvUYmJ
wjEOwwHqP6L3JfEeROOt6wyuB1ah7wgRvoABOJ81+qLYRn3bxF+y1BC+PwFd5yFWH5
Ry43lwp1/3+sA==
from: [email protected]
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3731.500.231\))
Subject: Test email subject
Message-Id: <[email protected]>
Date: Fri, 23 Jun 2023 01:32:20 +0530
To: Saleel <[email protected]>
X-Mailer: Apple Mail (2.3731.500.231)
X-Proofpoint-GUID: MU47Y4dCOAohatJZW3Su6CexM6uUt94W
X-Proofpoint-ORIG-GUID: MU47Y4dCOAohatJZW3Su6CexM6uUt94W
X-MANTSH: 1TEIXSUMdHVoaGkNHB1tfQV4aEhoTGxsaGBEKTEMXGxoEGxwSBBscGgQfGhAbHho
fGhEKTFkXGx8ZEQpZRBdsUm5CHktdTXIFbxEKWU0XZEVETxEKWUkXGRxxGwYdG3cGEh0GGgYaB
hoGGRpxGxAadwYaBhoGGgYaBhoGGnEaEBp3BhoRClleF2xseREKQ04XZ38eHXMeTmlla0VCS15
gcH0ZeV8caU9SZxxff14THn0RClhcFxkEGgQfGgUbGhoEEhgEHhgEGBIQGx4aHxoRCl5ZF0hTQ
RhsEQpNXBcaEQpMWhdoaU1raxEKTEYXTWsRCkNaFxsdBB8SBBwEHxsRCkJeFxsRCkJcFxsRCl5
OFxsRCkJLF2xwYHlAHWJSaRpiEQpCSRdscGB5QB1iUmkaYhEKQkUXbllZcnNAGl1aGF4RCkJOF
2xwYHlAHWJSaRpiEQpCTBdgTVt4G1BffxhwZxEKQm4XbWVwG0JrYh5eZlwRCkJsF2h7YBNtbBh
JfU4dEQpCQBdufWFrHURjemEFHxEKQlgXemEZYklYHBwYWk8RCk1eFxsRClpYFxgRCnBoF2RMR
20YaAEeBXJpEBkaEQpwbBdpRhJHa2Bzb1NHZhAZGhEKbX4XGxEKWE0XSxE=
X-CLX-Shades: None
X-Proofpoint-Virus-Version: =?UTF-8?Q?vendor=3Dfsecure_engine=3D1.1.170-22c6f66c430a71ce266a39bfe25bc?=
=?UTF-8?Q?2903e8d5c8f:6.0.425,18.0.790,17.0.605.474.0000000_definitions?=
=?UTF-8?Q?=3D2022-01-13=5F02:2022-01-11=5F01,2022-01-13=5F02,2020-01-23?=
=?UTF-8?Q?=5F02_signatures=3D0?=
X-Proofpoint-Spam-Reason: safe
Subject: Hello
Message-Id: <[email protected]>
Date: Sat, 26 Aug 2023 12:25:22 +0400
to: [email protected]

Hello
Hello,

How are you doing?
How are you?
39 changes: 39 additions & 0 deletions packages/contracts/DKIMRegistry.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;

import "@openzeppelin/contracts/access/Ownable.sol";

/**
A Registry that store the hash(dkim_public_key) for each domain
The hash is calculated by taking Poseidon of DKIM key split into 9 chunks of 242 bits each
*/
contract DKIMRegistry is Ownable {
// Mapping from domain name to DKIM public key hash
mapping(string => uint256) public dkimPublicKeyHashes;

constructor() {
// Set values for popular domains
dkimPublicKeyHashes["gmail.com"] = uint256(20579775636546222313859320423592165398188168817714003219389601176739340973605);
dkimPublicKeyHashes["hotmail.com"] = uint256(2750248559912404074361997670683337416910370052869160728223409986079552486582);
dkimPublicKeyHashes["twitter.com"] = uint256(12431732230788297063498039481224031586256793440953465069048041914965586355958);
dkimPublicKeyHashes["ethereum.org"] = uint256(13749471426528386843484698195116860745506750565298853141220185289842769029726);
dkimPublicKeyHashes["skiff.com"] = uint256(11874169184886542147081299005924838984240934585001783050565158265014763417816);
}

function _stringEq(string memory a, string memory b) internal pure returns (bool) {
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
}

function getDKIMPublicKeyHash(
string memory domainName
) public view returns (uint256) {
return dkimPublicKeyHashes[domainName];
}

function setDKIMPublicKeyHash(
string memory domainName,
uint256 publicKeyHash
) public onlyOwner {
dkimPublicKeyHashes[domainName] = publicKeyHash;
}
}
Loading