Home

Awesome

ZKEmail.nr

ZKEmail written in NoirLang

Using the Noir Library

In your Nargo.toml file, add the version of this library you would like to install under dependency:

[dependencies]
zkemail = { tag = "v0.3.2", git = "https://github.com/zkemail/zkemail.nr", directory = "lib" }

The library exports the following functions:

Additionally, the @zk-email/zkemail-nr JS library exports an ergonomic API for easily deriving circuit inputs needed to utilize the Noir library.

For demonstrations of all functionality, see the examples.

Basic Email Verification

A basic email verifier will often look like this:

use dep::zkemail::{
    KEY_LIMBS_1024, dkim::RSAPubkey, get_body_hash_by_index,     
    base64::body_hash_base64_decode, standard_outputs
};
use dep::std::hash::sha256_var;

// Somewhere in your function
...
  // verify the dkim signature over the asserted header
  pubkey.verify_dkim_signature(header, signature);
  // extract the body hash from the header
  let signed_body_hash = get_body_hash(header, dkim_header_sequence, body_hash_index);
  // compute the sha256 hash of the asserted body
  let computed_body_hash: [u8; 32] = sha256_var(body.storage, body.len() as u64);
  // constain the computed body hash to match the one found in the header
  assert(
    signed_body_hash == computed_body_hash,
    "SHA256 hash computed over body does not match body hash found in DKIM-signed header"
  );
...

From here, you can operate on the header or body with guarantees that the accessed text was signed by the DKIM key.

You may also have an email where you need access to the header, but not the body. You can simply omit everything after verify_dkim_signature and proceed!

Usage with partial SHA

You can use partial hashing technique for email with large body when the part you want to constrain in the body is towards the end.

Since SHA works in chunks of 64 bytes, we can hash the body up to the chunk from where we want to extract outside of the circuit and do the remaining hash in the circuit. This will save a lot of constraints as SHA is very expensive in circuit (~100 constraints/ byte).

use dep::zkemail::{
    KEY_LIMBS_2048, dkim::RSAPubkey, headers::body_hash::get_body_hash,
    partial_hash::partial_sha256_var_end
};

...
  // verify the dkim signature over the asserted header
  pubkey.verify_dkim_signature(header, signature);
  // extract the body hash from the header
  let signed_body_hash = get_body_hash(header, dkim_header_sequence, body_hash_index);
  // finish the partial hash
  let computed_body_hash = partial_sha256_var_end(partial_body_hash, body.storage(), body.len() as u64, partial_body_real_length);   
  // constain the computed body hash to match the one found in the header
  assert(
    signed_body_hash == computed_body_hash,
    "SHA256 hash computed over body does not match body hash found in DKIM-signed header"
  );
...

Extracting Email Addresses

To and from email addresses can be extracted from the header with get_email_address

use dep::zkemail::get_email_address;
...
  // define the header field to access (set "to" or "from")
  let to = comptime { "to".as_bytes() };
  // constrained retrieval of the email header
  let to_address = get_email_address(header, to_header_sequence, to_address_sequence, to);
...

to_address is a "BoundedVec", meaning the output of a parsed email address "zkemail@prove.email" would export

{
  "storage": [122, 107, 101, 109,  97, 105, 108,  64, 112, 114, 111, 118, 101,  46, 101, 109,  97, 105, 108, 0, ..., 0],
  "len": 19
}

which is easily interpreted with Buffer.from(output.storage.slice(0, output.storage.len)).toString(). You can additionally perform your own transformations or commitments in-circuit.

Using the Input Generation JS Library

Install the library:

yarn add @zk-email/zkemail-nr

Usage

See the witness simulation and proving tests for an in-depth demonstration of each use case.

// example of generating inputs for a partial hash
import { generateEmailVerifierInputs } from "@zk-email/zkemail-nr";

const zkEmailInputs = await generateEmailVerifierInputs(emailContent, {
  maxBodyLength: 1280,
  maxHeadersLength: 1408,
  shaPrecomputeSelector: "some string in body up to which you want to hash outside circuit",
});

Using ZKEmail.nr in EVM Smart Contracts

TODO

Using ZKEmail.nr in Aztec Smart Contracts

TODO

Todo

By Mach-34