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

zkEmail circuit #86

Draft
wants to merge 26 commits into
base: feature/zkemail-prep-2
Choose a base branch
from

Conversation

mitschabaude
Copy link
Member

@mitschabaude mitschabaude commented Dec 4, 2024

on top of #87, this adds a first version of the o1js zkEmail implementation.

Motivation

We want to use emails as private credentials, stored in our wallets, and make selective disclosures about them.

The "attributes" of an email credential are the plain text body and header (or specific extracted header fields, like from and subject), and the "issuer" is the DKIM public key.

API

let email: string;

// outside circuit: preparation
let provableEmail: ProvableEmail = await prepareProvableEmail(email);

// inside circuit: verification
await verifyEmail(provableEmail);

ProvableEmail type:

type ProvableEmail = {
  /**
   * The email header in canonicalized form, i.e. the form that was signed.
   */
  header: string | DynamicString;

  /**
   * The email body in canonicalized form, i.e. the form that was signed.
   */
  body: string | DynamicString;

  /**
   * RSA public key that signed the email.
   */
  publicKey: Bigint2048;

  /**
   * The RSA signature of the email.
   */
  signature: Bigint2048;
};

Details

the full zkemail implementation is self-contained in a single file, zkemail.ts.

included in this PR:

  • verifying RSA signature on header hash
  • hashing both the body and header
  • a variant which only verifies the header and ignores the body (much cheaper, and enough if an application only needs the header)

not included in this PR:

  • properly checking that the body hash is contained in the header
  • extracting other useful info, like the from address or subject, from the header

to overcome circuit size limits, we use recursive zkprograms. header is limited to a configurable length (currently 10 sha2 blocks = 640 bytes), while the body has arbitrary length (by optionally using more proofs).
everything is structured into a linear chain of proofs: the inner ones just deal with hashing, and the outer one converts the sha2 states of body and header hashes to bytes, and verifies the RSA signature

note: this depends on o1-labs/o1js#1931 and its follow-up PRs, and currently uses o1js locally at this branch: https://github.com/o1-labs/o1js/tree/tmp/zkprogram-merged

Results

Test using

node --enable-source-maps --experimental-strip-types --no-warnings src/email/zkemail-recursive.eg.ts

For email bodies up to 704 bytes, verifyEmail() fits in 3 proofs and takes 1:40 min on my machine. (Longer bodies just work as well, but will take more proofs.)

However, the final proof already uses about 32k constraints and doesn't include any header extraction (e.g., of the body hash) yet. It seems likely that with the header extraction added, hashing has to be pushed deeper down into the inner proof and everything will need 4 proofs minimum by default.

@mitschabaude mitschabaude changed the title zkEmail preparations 2 zkEmail circuit Dec 5, 2024
@mitschabaude mitschabaude changed the base branch from feature/zk-email to feature/zkemail-prep-2 December 5, 2024 12:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant