diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..24d08a6 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,66 @@ +name: Noir tests + +on: + push: + branches: + - main + pull_request: + +jobs: + test: + name: Test on Nargo ${{matrix.toolchain}} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + toolchain: [0.36.0] + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Install Nargo + uses: noir-lang/noirup@v0.1.3 + with: + toolchain: ${{ matrix.toolchain }} + + - name: Run Noir tests + working-directory: ./lib + run: nargo test + + format: + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Install Nargo + uses: noir-lang/noirup@v0.1.3 + with: + toolchain: 0.36.0 + + - name: Run formatter + working-directory: ./lib + run: nargo fmt --check + + # This is a job which depends on all test jobs and reports the overall status. + # This allows us to add/remove test jobs without having to update the required workflows. + tests-end: + name: Noir End + runs-on: ubuntu-latest + # We want this job to always run (even if the dependant jobs fail) as we want this job to fail rather than skipping. + if: ${{ always() }} + needs: + - test + - format + + steps: + - name: Report overall success + run: | + if [[ $FAIL == true ]]; then + exit 1 + else + exit 0 + fi + env: + # We treat any cancelled, skipped or failing jobs as a failure for the workflow as a whole. + FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }} diff --git a/lib/src/dkim.nr b/lib/src/dkim.nr index 9b83053..38071dc 100644 --- a/lib/src/dkim.nr +++ b/lib/src/dkim.nr @@ -1,28 +1,32 @@ -use dep::std::{collections::bounded_vec::BoundedVec, hash::{sha256_var, pedersen_hash}, panic::panic}; -use dep::bignum::{params::BigNumParams, RuntimeBigNum}; -use dep::rsa::{rsa::verify_sha256_pkcs1v15, types::{RBN1024, RBN2048}}; +use std::hash::{sha256_var, pedersen_hash}; +use bignum::{params::BigNumParams, RuntimeBigNum}; +use rsa::{rsa::verify_sha256_pkcs1v15, types::{RBN1024, RBN2048}}; use crate::{KEY_LIMBS_1024, KEY_LIMBS_2048, RSA_EXPONENT}; - pub struct RSAPubkey { modulus: [Field; KEY_LIMBS], redc: [Field; KEY_LIMBS], } impl RSAPubkey { - fn hash(self) -> Field { + + pub fn new(modulus: [Field; KEY_LIMBS], redc: [Field; KEY_LIMBS]) -> Self { + Self { modulus, redc } + } + + pub fn hash(self) -> Field { pedersen_hash(self.modulus) } } impl RSAPubkey { - fn verify_dkim_signature( + pub fn verify_dkim_signature( self, header: BoundedVec, - signature: [Field; KEY_LIMBS_1024] + signature: [Field; KEY_LIMBS_1024], ) { // hash the header - let header_hash = sha256_var(header.storage, header.len() as u64); + let header_hash = sha256_var(header.storage(), header.len() as u64); let params: BigNumParams = BigNumParams::new(false, self.modulus, self.redc); @@ -35,13 +39,13 @@ impl RSAPubkey { } impl RSAPubkey { - fn verify_dkim_signature( + pub fn verify_dkim_signature( self, header: BoundedVec, signature: [Field; KEY_LIMBS_2048], ) { // hash the header - let header_hash = sha256_var(header.storage, header.len() as u64); + let header_hash = sha256_var(header.storage(), header.len() as u64); let params: BigNumParams = BigNumParams::new(false, self.modulus, self.redc); @@ -51,4 +55,4 @@ impl RSAPubkey { // verify the DKIM signature over the header assert(verify_sha256_pkcs1v15(header_hash, signature, RSA_EXPONENT)); } -} \ No newline at end of file +} diff --git a/lib/src/headers/body_hash.nr b/lib/src/headers/body_hash.nr index 41e098d..45efe7d 100644 --- a/lib/src/headers/body_hash.nr +++ b/lib/src/headers/body_hash.nr @@ -1,6 +1,8 @@ -use dep::base64::{BASE64_DECODER}; -use dep::std::collections::bounded_vec::BoundedVec; -use crate::{Sequence, BODY_HASH_BASE64_LENGTH, MAX_DKIM_HEADER_FIELD_LENGTH, headers::constrain_header_field}; +use base64::BASE64_DECODER; +use crate::{ + Sequence, BODY_HASH_BASE64_LENGTH, MAX_DKIM_HEADER_FIELD_LENGTH, + headers::constrain_header_field, +}; /** * Constrained access to the body hash in the header @@ -13,24 +15,26 @@ use crate::{Sequence, BODY_HASH_BASE64_LENGTH, MAX_DKIM_HEADER_FIELD_LENGTH, hea pub fn get_body_hash( header: BoundedVec, dkim_header_field_sequence: Sequence, - body_hash_index: u32 + body_hash_index: u32, ) -> [u8; 32] { // constrain the access of dkim signature field - let header_field_name: [u8; 14] = comptime { - "dkim-signature".as_bytes() - }; - constrain_header_field::(header, dkim_header_field_sequence, header_field_name); + let header_field_name: [u8; 14] = comptime { "dkim-signature".as_bytes() }; + constrain_header_field::( + header, + dkim_header_field_sequence, + header_field_name, + ); // constrain access to the body hash assert( body_hash_index > dkim_header_field_sequence.index - & body_hash_index < dkim_header_field_sequence.end_index(), "Body hash index accessed outside of DKIM header field" + & body_hash_index < dkim_header_field_sequence.end_index(), + "Body hash index accessed outside of DKIM header field", ); - let bh_prefix: [u8; 3] = comptime { - "bh=".as_bytes() - }; + let bh_prefix: [u8; 3] = comptime { "bh=".as_bytes() }; for i in 0..3 { assert( - header.get_unchecked(body_hash_index - 3 + i) == bh_prefix[i], "No 'bh=' prefix found at asserted bh index" + header.get_unchecked(body_hash_index - 3 + i) == bh_prefix[i], + "No 'bh=' prefix found at asserted bh index", ); } // get the body hash @@ -46,7 +50,7 @@ pub fn get_body_hash( */ pub fn get_body_hash_unsafe( header: BoundedVec, - body_hash_index: u32 + body_hash_index: u32, ) -> [u8; 32] { // get the body hash let mut body_hash_encoded: [u8; BODY_HASH_BASE64_LENGTH] = [0; BODY_HASH_BASE64_LENGTH]; diff --git a/lib/src/headers/email_address.nr b/lib/src/headers/email_address.nr index 2c65f18..b835e14 100644 --- a/lib/src/headers/email_address.nr +++ b/lib/src/headers/email_address.nr @@ -1,27 +1,32 @@ -use dep::std::collections::bounded_vec::BoundedVec; use crate::{ Sequence, MAX_EMAIL_ADDRESS_LENGTH, EMAIL_ADDRESS_CHAR_TABLE, - headers::constrain_header_field_detect_last_angle_bracket + headers::constrain_header_field_detect_last_angle_bracket, }; pub fn get_email_address( header: BoundedVec, header_field_sequence: Sequence, email_address_sequence: Sequence, - header_field_name: [u8; HEADER_FIELD_NAME_LENGTH] + header_field_name: [u8; HEADER_FIELD_NAME_LENGTH], ) -> BoundedVec { // check field is uninterrupted and matches the expected field name - let last_angle_bracket = constrain_header_field_detect_last_angle_bracket::(header, header_field_sequence, header_field_name); + let last_angle_bracket = constrain_header_field_detect_last_angle_bracket::( + header, + header_field_sequence, + header_field_name, + ); // if angle bracket found, assert index is +1 if last_angle_bracket != 0 { assert( - email_address_sequence.index == last_angle_bracket + 1, "Email address must start immediately after '<' if bracket is present" + email_address_sequence.index == last_angle_bracket + 1, + "Email address must start immediately after '<' if bracket is present", ); } // check email sequence is within header field assert( email_address_sequence.index >= header_field_sequence.index - & email_address_sequence.end_index() <= header_field_sequence.end_index(), "Email address sequence out of bounds" + & email_address_sequence.end_index() <= header_field_sequence.end_index(), + "Email address sequence out of bounds", ); // constrained get email address @@ -30,18 +35,22 @@ pub fn get_email_address( header: BoundedVec, - email_address_sequence: Sequence + email_address_sequence: Sequence, ) -> BoundedVec { // check the sequence is proceeded by an acceptable character if email_address_sequence.index != 0 { assert( - EMAIL_ADDRESS_CHAR_TABLE[header.get_unchecked(email_address_sequence.index - 1)] == 2, "Email address must start with an acceptable character" + EMAIL_ADDRESS_CHAR_TABLE[header.get_unchecked(email_address_sequence.index - 1)] == 2, + "Email address must start with an acceptable character", ); } if email_address_sequence.end_index() < header.len() { assert( - EMAIL_ADDRESS_CHAR_TABLE[header.get_unchecked(email_address_sequence.index + email_address_sequence.length)] - == 3, "Email address must end with an acceptable character" + EMAIL_ADDRESS_CHAR_TABLE[header.get_unchecked( + email_address_sequence.index + email_address_sequence.length, + )] + == 3, + "Email address must end with an acceptable character", ); } // check the email address and assign @@ -50,15 +59,15 @@ pub fn parse_email_address( let index = email_address_sequence.index + i; if index < email_address_sequence.end_index() { let letter = header.get_unchecked(index); - email_address.storage[i] = letter; + email_address.set_unchecked(i, letter); assert( - EMAIL_ADDRESS_CHAR_TABLE[letter] == 1, "Email address must only contain acceptable characters" + EMAIL_ADDRESS_CHAR_TABLE[letter] == 1, + "Email address must only contain acceptable characters", ); } } email_address.len = email_address_sequence.length; // todo: should probably introduce a check for @ - email_address } diff --git a/lib/src/headers/mod.nr b/lib/src/headers/mod.nr index 814515c..3016088 100644 --- a/lib/src/headers/mod.nr +++ b/lib/src/headers/mod.nr @@ -1,10 +1,8 @@ -use crate::{Sequence, CR, LF}; +use crate::{CR, LF, Sequence}; pub mod body_hash; pub mod email_address; - - /** * Constrain a sequence in a header to match the specific header field * @@ -15,11 +13,7 @@ pub mod email_address; * @param header_field_sequence - The sequence of the header field * @param header_field_name - The name of the header field */ -pub fn constrain_header_field< - let MAX_HEADER_LENGTH: u32, - let MAX_HEADER_FIELD_LENGTH: u32, - let HEADER_FIELD_NAME_LENGTH: u32, ->( +pub fn constrain_header_field( header: BoundedVec, header_field_sequence: Sequence, header_field_name: [u8; HEADER_FIELD_NAME_LENGTH], @@ -27,7 +21,7 @@ pub fn constrain_header_field< // check that the sequence is within bounds assert( header_field_sequence.index + header_field_sequence.length <= header.len(), - "Header field out of bounds" + "Header field out of bounds", ); // check the range of the sequence is within the header (so we can use get_unchecked) let end_index = header_field_sequence.index + header_field_sequence.length; @@ -35,29 +29,30 @@ pub fn constrain_header_field< // if the sequence is not the start, check for a newline if header_field_sequence.index != 0 { - assert(header.get_unchecked(header_field_sequence.index - 2) == CR, "Header field must start with CRLF"); - assert(header.get_unchecked(header_field_sequence.index - 1) == LF, "Header field must start with CRLF"); - } - // if the sequence is not the end, check for a newline - if end_index != header.len() { assert( - header.get_unchecked(end_index) == CR, - "Header field must end with CRLF" + header.get_unchecked(header_field_sequence.index - 2) == CR, + "Header field must start with CRLF", ); - assert(header.get_unchecked(end_index + 1) == LF, - "Header field must end with CRLF" + assert( + header.get_unchecked(header_field_sequence.index - 1) == LF, + "Header field must start with CRLF", ); } + // if the sequence is not the end, check for a newline + if end_index != header.len() { + assert(header.get_unchecked(end_index) == CR, "Header field must end with CRLF"); + assert(header.get_unchecked(end_index + 1) == LF, "Header field must end with CRLF"); + } // check that the header field name matches the expected name for i in 0..HEADER_FIELD_NAME_LENGTH { assert( header.get_unchecked(header_field_sequence.index + i) == header_field_name[i], - "Header field name does not match" + "Header field name does not match", ); } assert( header.get_unchecked(header_field_sequence.index + HEADER_FIELD_NAME_LENGTH) == 0x3a, - "Header field name must be followed by a colon" + "Header field name must be followed by a colon", ); // check the header field is uninterrupted let start_index = header_field_sequence.index + HEADER_FIELD_NAME_LENGTH + 1; @@ -65,10 +60,7 @@ pub fn constrain_header_field< // is it safe enough to cut this constraint cost in half by not checking lf? i think so let index = start_index + i; if (index < header_field_sequence.index + header_field_sequence.length) { - assert( - header.get_unchecked(index) != CR, - "Header field must not contain newlines" - ); + assert(header.get_unchecked(index) != CR, "Header field must not contain newlines"); } } } @@ -76,11 +68,7 @@ pub fn constrain_header_field< /** * contrain_header_field with checks for the last occurence of "<" inside the loop to save constraints */ -pub fn constrain_header_field_detect_last_angle_bracket< - let MAX_HEADER_LENGTH: u32, - let MAX_HEADER_FIELD_LENGTH: u32, - let HEADER_FIELD_NAME_LENGTH: u32, ->( +pub fn constrain_header_field_detect_last_angle_bracket( header: BoundedVec, header_field_sequence: Sequence, header_field_name: [u8; HEADER_FIELD_NAME_LENGTH], @@ -88,7 +76,7 @@ pub fn constrain_header_field_detect_last_angle_bracket< // check that the sequence is within bounds assert( header_field_sequence.index + header_field_sequence.length <= header.len(), - "Header field out of bounds" + "Header field out of bounds", ); // check the range of the sequence is within the header (so we can use get_unchecked) let end_index = header_field_sequence.index + header_field_sequence.length; @@ -96,29 +84,30 @@ pub fn constrain_header_field_detect_last_angle_bracket< // if the sequence is not the start, check for a newline if header_field_sequence.index != 0 { - assert(header.get_unchecked(header_field_sequence.index - 2) == CR, "Header field must start with CRLF"); - assert(header.get_unchecked(header_field_sequence.index - 1) == LF, "Header field must start with CRLF"); - } - // if the sequence is not the end, check for a newline - if end_index != header.len() { assert( - header.get_unchecked(end_index) == CR, - "Header field must end with CRLF" + header.get_unchecked(header_field_sequence.index - 2) == CR, + "Header field must start with CRLF", ); - assert(header.get_unchecked(end_index + 1) == LF, - "Header field must end with CRLF" + assert( + header.get_unchecked(header_field_sequence.index - 1) == LF, + "Header field must start with CRLF", ); } + // if the sequence is not the end, check for a newline + if end_index != header.len() { + assert(header.get_unchecked(end_index) == CR, "Header field must end with CRLF"); + assert(header.get_unchecked(end_index + 1) == LF, "Header field must end with CRLF"); + } // check that the header field name matches the expected name for i in 0..HEADER_FIELD_NAME_LENGTH { assert( header.get_unchecked(header_field_sequence.index + i) == header_field_name[i], - "Header field name does not match" + "Header field name does not match", ); } assert( header.get_unchecked(header_field_sequence.index + HEADER_FIELD_NAME_LENGTH) == 0x3a, - "Header field name must be followed by a colon" + "Header field name must be followed by a colon", ); // check the header field is uninterrupted let mut last_angle_bracket = 0; @@ -128,10 +117,7 @@ pub fn constrain_header_field_detect_last_angle_bracket< let index = start_index + i; if (index < header_field_sequence.index + header_field_sequence.length) { let byte = header.get_unchecked(index); - assert( - byte != CR, - "Header field must not contain newlines" - ); + assert(byte != CR, "Header field must not contain newlines"); if byte == 0x3c { last_angle_bracket = index; } diff --git a/lib/src/lib.nr b/lib/src/lib.nr index cbf93f0..7f96813 100644 --- a/lib/src/lib.nr +++ b/lib/src/lib.nr @@ -1,4 +1,4 @@ -use dep::std::{hash::pedersen_hash, collections::bounded_vec::BoundedVec}; +use std::hash::pedersen_hash; use crate::dkim::RSAPubkey; pub mod dkim; @@ -10,8 +10,6 @@ pub mod remove_soft_line_breaks; mod tests; global RSA_EXPONENT: u32 = 65537; -global KEY_BYTES_1024: u32 = 128; -global KEY_BYTES_2048: u32 = 256; global KEY_LIMBS_1024: u32 = 9; global KEY_LIMBS_2048: u32 = 18; global BODY_HASH_BASE64_LENGTH: u32 = 44; @@ -22,7 +20,7 @@ global MAX_EMAIL_ADDRESS_LENGTH: u32 = 320; pub struct Sequence { index: u32, - length: u32 + length: u32, } impl Sequence { @@ -35,19 +33,10 @@ impl Sequence { // "<: " = 2 // ">\r\n" = 3 global EMAIL_ADDRESS_CHAR_TABLE: [u8; 123] = [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 3, 0, 0, 3, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, - 2, 0, 3, 0, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0, 2, 0, 3, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ]; /** @@ -62,7 +51,7 @@ global EMAIL_ADDRESS_CHAR_TABLE: [u8; 123] = [ */ pub fn standard_outputs( pubkey: [Field; KEY_BYTE_LENGTH], - signature: [Field; KEY_BYTE_LENGTH] + signature: [Field; KEY_BYTE_LENGTH], ) -> [Field; 2] { // create pedersen hash of DKIM signing key to minimize public outputs let pubkey_hash = pedersen_hash(pubkey); @@ -82,14 +71,11 @@ pub fn standard_outputs( * @param pubkey - The DKIM RSA Public Key modulus and reduction parameter * @param signature - The DKIM RSA Signature */ -pub fn verify_email< - let MAX_EMAIL_HEADER_LENGTH: u32, - let MAX_EMAIL_BODY_LENGTH: u32, // sometimes unused but needed for macro - let KEY_LIMBS: u32 ->( +pub fn verify_email( header: BoundedVec, pubkey: RSAPubkey, - signature: [Field; KEY_LIMBS_2048] + signature: [Field; KEY_LIMBS_2048], ) { // check the body and header lengths are within bounds assert(header.len() <= MAX_EMAIL_HEADER_LENGTH); diff --git a/lib/src/macro.nr b/lib/src/macro.nr index a58d2bb..097880f 100644 --- a/lib/src/macro.nr +++ b/lib/src/macro.nr @@ -1,12 +1,14 @@ use dep::std::{ - meta::unquote, - collections::umap::UHashMap, - hash::{BuildHasherDefault, poseidon2::Poseidon2Hasher}, - option::Option + meta::unquote, collections::umap::UHashMap, + hash::{BuildHasherDefault, poseidon2::Poseidon2Hasher}, option::Option, }; -use dep::std::{collections::bounded_vec::BoundedVec, hash::{sha256_var, pedersen_hash}, panic::panic}; -use dep::rsa::{bignum::{fields::{Params1024, Params2048}, runtime_bignum::BigNumInstance, BigNum}, types::RSA}; +use dep::std::{ + collections::bounded_vec::BoundedVec, hash::{sha256_var, pedersen_hash}, panic::panic, +}; +use dep::rsa::{ + bignum::{fields::{Params1024, Params2048}, runtime_bignum::BigNumInstance, BigNum}, types::RSA, +}; use crate::{KEY_LIMBS_1024, KEY_BYTES_1024, RSA_EXPONENT}; type BN1024 = BigNum; @@ -16,19 +18,20 @@ type RSA2048 = RSA, KEY_BYTES pub type DeriveFunction = fn(StructDefinition) -> Quoted; -comptime mut global HANDLERS: UHashMap> = UHashMap::default(); -comptime mut global EXCLUSIVE: UHashMap> = UHashMap::default(); -comptime mut global USED: UHashMap> = UHashMap::default(); +comptime mut global HANDLERS: UHashMap> = + UHashMap::default(); +comptime mut global EXCLUSIVE: UHashMap> = + UHashMap::default(); +comptime mut global USED: UHashMap> = + UHashMap::default(); -// comptime mut global +// comptime mut global // #[varargs] comptime fn zkemail(s: StructDefinition) -> Quoted { // setup the dkim check derive_dkim_check(s) - - - // // check the + // // check the // let should_check_body = s.has_named_attribute("check_body"); // let should_partial_hash = s.has_named_attribute("partial_hash"); // if should_check_body { @@ -39,11 +42,10 @@ comptime fn zkemail(s: StructDefinition) -> Quoted { // // check it does not also have the check_body attribute // let _ = s.add_generic("let MAX_EMAIL_BODY_LENGTH: u32"); // } - // no matter what, add verify signature // derive_dkim_check(s) // quote {} -} +} comptime fn derive_dkim_check(s: StructDefinition) -> Quoted { // figure out key byte length @@ -131,8 +133,9 @@ global HEADER_LEN: u32 = 1024; pub fn test_fn() { let header: BoundedVec = BoundedVec::new(); let signature: [Field; KEY_LIMBS_2048] = [0; KEY_LIMBS_2048]; - let pubkey = RSAPubkey:: { modulus: [0; KEY_LIMBS_2048], redc: [0; KEY_LIMBS_2048] }; - let i: input = input{ header, pubkey, signature }; + let pubkey = + RSAPubkey:: { modulus: [0; KEY_LIMBS_2048], redc: [0; KEY_LIMBS_2048] }; + let i: input = input { header, pubkey, signature }; i.verify_dkim_signature(); // i.do_nothing(); } @@ -155,7 +158,6 @@ pub fn test_fn() { // quote {} // } - // impl for input where MAX_EMAIL_HEADER_LENGTH: u32, KEY_LIMBS: u32 { // fn check_body(self) { // // check the body and header lengths are within bounds @@ -212,4 +214,4 @@ pub fn test_fn() { // fn test_fn() { // let x = MyStruct::<10> { data: BoundedVec::new() }; // x.do_something_else(); -// } \ No newline at end of file +// } diff --git a/lib/src/masking.nr b/lib/src/masking.nr index fb6ef8b..4876f2b 100644 --- a/lib/src/masking.nr +++ b/lib/src/masking.nr @@ -1,5 +1,3 @@ -use dep::std::collections::bounded_vec::BoundedVec; - /** * Byte masking for a header or body text * @dev uses fixed sized arrays for masks since saves constraints and shouldn't have security cost @@ -19,4 +17,4 @@ pub fn mask_text( masked_text[i] = (text.get_unchecked(i) * mask[i] as u8); } masked_text -} \ No newline at end of file +} diff --git a/lib/src/partial_hash.nr b/lib/src/partial_hash.nr index 317096a..3017a7d 100644 --- a/lib/src/partial_hash.nr +++ b/lib/src/partial_hash.nr @@ -9,7 +9,7 @@ pub fn msg_u8_to_u32(msg: [u8; BLOCK_SIZE]) -> [u32; 16] { for i in 0..16 { let mut msg_field: Field = 0; for j in 0..4 { - msg_field = msg_field * 256 + msg[64 - 4*(i + 1) + j] as Field; + msg_field = msg_field * 256 + msg[64 - 4 * (i + 1) + j] as Field; } msg32[15 - i] = msg_field as u32; } @@ -18,7 +18,11 @@ pub fn msg_u8_to_u32(msg: [u8; BLOCK_SIZE]) -> [u32; 16] { } // https://github.com/noir-lang/noir/blob/76eec710ff73e5e45fdddcd41ae2cd74e879cfa5/noir_stdlib/src/hash/sha256.nr#L38 -unconstrained fn build_msg_block_iter(msg: [u8; N], message_size: u32, msg_start: u32) -> ([u8; BLOCK_SIZE], u32) { +unconstrained fn build_msg_block_iter( + msg: [u8; N], + message_size: u32, + msg_start: u32, +) -> ([u8; BLOCK_SIZE], u32) { let mut msg_block: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE]; // We insert `BLOCK_SIZE` bytes (or up to the end of the message) let block_input = if msg_start + BLOCK_SIZE > message_size { @@ -44,7 +48,7 @@ fn verify_msg_block( msg: [u8; N], message_size: u32, msg_block: [u8; 64], - msg_start: u32 + msg_start: u32, ) -> u32 { let mut msg_byte_ptr: u32 = 0; // Message byte pointer let mut msg_end = msg_start + BLOCK_SIZE; @@ -63,7 +67,6 @@ fn verify_msg_block( } global BLOCK_SIZE = 64; -global ZERO = 0; // https://github.com/noir-lang/noir/blob/76eec710ff73e5e45fdddcd41ae2cd74e879cfa5/noir_stdlib/src/hash/sha256.nr#L86-L116 /** @@ -78,14 +81,14 @@ global ZERO = 0; pub fn partial_sha256_var_start(msg: [u8; N]) -> [u32; 8] { let num_blocks = N / BLOCK_SIZE; let mut msg_block: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE]; - let mut h: [u32; 8] = [1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225]; // Intermediate hash, starting with the canonical initial value + let mut h: [u32; 8] = [ + 1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, + 1541459225, + ]; // Intermediate hash, starting with the canonical initial value let mut msg_byte_ptr = 0; // Pointer into msg_block - for i in 0..num_blocks { let msg_start = BLOCK_SIZE * i; - let (new_msg_block, new_msg_byte_ptr) = unsafe { - build_msg_block_iter(msg, N, msg_start) - }; + let (new_msg_block, new_msg_byte_ptr) = unsafe { build_msg_block_iter(msg, N, msg_start) }; if msg_start < N { msg_block = new_msg_block; } @@ -120,17 +123,18 @@ pub fn partial_sha256_var_start(msg: [u8; N]) -> [u32; 8] { * @param message_size - the actual length of the preimage to hash * @return the intermediate hash state after compressing in msg to h */ -pub fn partial_sha256_var_interstitial(mut h: [u32; 8], msg: [u8; N], message_size: u32) -> [u32; 8] { +pub fn partial_sha256_var_interstitial( + mut h: [u32; 8], + msg: [u8; N], + message_size: u32, +) -> [u32; 8] { assert(message_size % BLOCK_SIZE == 0, "Message size must be a multiple of the block size"); let num_blocks = N / BLOCK_SIZE; let mut msg_block: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE]; let mut msg_byte_ptr = 0; // Pointer into msg_block - for i in 0..num_blocks { let msg_start = BLOCK_SIZE * i; - let (new_msg_block, new_msg_byte_ptr) = unsafe { - build_msg_block_iter(msg, N, msg_start) - }; + let (new_msg_block, new_msg_byte_ptr) = unsafe { build_msg_block_iter(msg, N, msg_start) }; if msg_start < N { msg_block = new_msg_block; } @@ -147,7 +151,7 @@ pub fn partial_sha256_var_interstitial(mut h: [u32; 8], msg: [u8; N] // If the block is filled, compress it. // An un-filled block is handled after this loop. - if (msg_start < N) & (msg_byte_ptr == BLOCK_SIZE) & (msg_start < message_size){ + if (msg_start < N) & (msg_byte_ptr == BLOCK_SIZE) & (msg_start < message_size) { h = sha256_compression(msg_u8_to_u32(msg_block), h); } } @@ -167,19 +171,21 @@ pub fn partial_sha256_var_interstitial(mut h: [u32; 8], msg: [u8; N] * @param real_message_size -= the total size of the preimage * @return finalized sha256 hash */ -pub fn partial_sha256_var_end(mut h: [u32; 8], msg: [u8; N], message_size: u64, real_message_size: u64) -> [u8; 32] { +pub fn partial_sha256_var_end( + mut h: [u32; 8], + msg: [u8; N], + message_size: u64, + real_message_size: u64, +) -> [u8; 32] { let message_size = message_size as u32; // noir stdlib uses u64 let real_message_size = real_message_size as u32; // noir stdlib uses u64 - let num_blocks = N / BLOCK_SIZE; let mut msg_block: [u8; BLOCK_SIZE] = [0; BLOCK_SIZE]; let mut msg_byte_ptr = 0; // Pointer into msg_block - for i in 0..num_blocks { let msg_start = BLOCK_SIZE * i; - let (new_msg_block, new_msg_byte_ptr) = unsafe { - build_msg_block_iter(msg, message_size, msg_start) - }; + let (new_msg_block, new_msg_byte_ptr) = + unsafe { build_msg_block_iter(msg, message_size, msg_start) }; if msg_start < message_size { msg_block = new_msg_block; } @@ -207,9 +213,8 @@ pub fn partial_sha256_var_end(mut h: [u32; 8], msg: [u8; N], message // or our message cannot be evenly split into blocks. if modulo != 0 { let msg_start = BLOCK_SIZE * num_blocks; - let (new_msg_block, new_msg_byte_ptr) = unsafe { - build_msg_block_iter(msg, message_size, msg_start) - }; + let (new_msg_block, new_msg_byte_ptr) = + unsafe { build_msg_block_iter(msg, message_size, msg_start) }; if msg_start < message_size { msg_block = new_msg_block; @@ -270,9 +275,7 @@ pub fn partial_sha256_var_end(mut h: [u32; 8], msg: [u8; N], message msg_byte_ptr = 0; } - msg_block = unsafe { - attach_len_to_msg_block(msg_block, msg_byte_ptr, real_message_size) - }; + msg_block = unsafe { attach_len_to_msg_block(msg_block, msg_byte_ptr, real_message_size) }; if !is_unconstrained() { for i in 0..56 { @@ -295,7 +298,7 @@ pub fn partial_sha256_var_end(mut h: [u32; 8], msg: [u8; N], message unconstrained fn pad_msg_block( mut msg_block: [u8; BLOCK_SIZE], - mut msg_byte_ptr: u32 + mut msg_byte_ptr: u32, ) -> ([u8; BLOCK_SIZE], u32) { // If i >= 57, there aren't enough bits in the current message block to accomplish this, so // the 1 and 0s fill up the current block, which we then compress accordingly. @@ -310,10 +313,13 @@ unconstrained fn pad_msg_block( } } -unconstrained fn attach_len_to_msg_block(mut msg_block: [u8; BLOCK_SIZE], mut msg_byte_ptr: u32, message_size: u32) -> [u8; BLOCK_SIZE] { +unconstrained fn attach_len_to_msg_block( + mut msg_block: [u8; BLOCK_SIZE], + mut msg_byte_ptr: u32, + message_size: u32, +) -> [u8; BLOCK_SIZE] { // We assume that `msg_byte_ptr` is less than 57 because if not then it is reset to zero before calling this function. // In any case, fill blocks up with zeros until the last 64 (i.e. until msg_byte_ptr = 56). - for i in msg_byte_ptr..56 { msg_block[i] = 0; } @@ -328,7 +334,6 @@ unconstrained fn attach_len_to_msg_block(mut msg_block: [u8; BLOCK_SIZE], mut ms fn hash_final_block(msg_block: [u8; BLOCK_SIZE], mut state: [u32; 8]) -> [u8; 32] { let mut out_h: [u8; 32] = [0; 32]; // Digest as sequence of bytes - // Hash final padded block state = sha256_compression(msg_u8_to_u32(msg_block), state); @@ -336,7 +341,7 @@ fn hash_final_block(msg_block: [u8; BLOCK_SIZE], mut state: [u32; 8]) -> [u8; 32 for j in 0..8 { let h_bytes: [u8; 4] = (state[7 - j] as Field).to_le_bytes(); for k in 0..4 { - out_h[31 - 4*j - k] = h_bytes[k]; + out_h[31 - 4 * j - k] = h_bytes[k]; } } @@ -344,16 +349,15 @@ fn hash_final_block(msg_block: [u8; BLOCK_SIZE], mut state: [u32; 8]) -> [u8; 32 } global DATA: [u8; 192] = [ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, - 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, - 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, - 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, - 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, - 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, - 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, - 185, 186, 187, 188, 189, 190, 191 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, + 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, + 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, + 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, + 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, ]; #[test] @@ -384,7 +388,12 @@ fn test_partial_hash_interstitial() { } let pre_hash = partial_sha256_var_start(data0); let interstitial_hash = partial_sha256_var_interstitial(pre_hash, data1, data1.len()); - let hash = partial_sha256_var_end(interstitial_hash, data2, data2.len() as u64, DATA.len() as u64); + let hash = partial_sha256_var_end( + interstitial_hash, + data2, + data2.len() as u64, + DATA.len() as u64, + ); let correct_hash = std::hash::sha256_var(DATA, DATA.len() as u64); assert_eq(hash, correct_hash); } @@ -401,7 +410,12 @@ fn test_partial_hash_interstitial_var() { } let pre_hash = partial_sha256_var_start(data0); let interstitial_hash = partial_sha256_var_interstitial(pre_hash, data1, 64); - let hash = partial_sha256_var_end(interstitial_hash, data2, data2.len() as u64, DATA.len() as u64); + let hash = partial_sha256_var_end( + interstitial_hash, + data2, + data2.len() as u64, + DATA.len() as u64, + ); let correct_hash = std::hash::sha256_var(DATA, DATA.len() as u64); assert_eq(hash, correct_hash); } diff --git a/lib/src/remove_soft_line_breaks.nr b/lib/src/remove_soft_line_breaks.nr index 7cb3d37..7814165 100644 --- a/lib/src/remove_soft_line_breaks.nr +++ b/lib/src/remove_soft_line_breaks.nr @@ -29,9 +29,7 @@ pub fn find_zeroes(encoded: [u8; N]) -> [bool; N] { // identify soft line breaks let mut is_break: [bool; N] = [false; N]; for i in 0..N - 2 { - is_break[i] = (encoded[i] == 0x3D) - & (encoded[i + 1] == 0x0D) - & (encoded[i + 2] == 0x0A); + is_break[i] = (encoded[i] == 0x3D) & (encoded[i + 1] == 0x0D) & (encoded[i + 2] == 0x0A); } // find indexes of chars to zero @@ -49,7 +47,7 @@ pub fn find_zeroes(encoded: [u8; N]) -> [bool; N] { pub fn powers_of_r( encoded: [u8; N], decoded: [u8; N], - should_zero: [bool; N] + should_zero: [bool; N], ) -> ([Field; N], [Field; N]) { // compute r let r = compressed_r(encoded, decoded); diff --git a/lib/src/tests/mod.nr b/lib/src/tests/mod.nr index 511ea2c..ffb34d5 100644 --- a/lib/src/tests/mod.nr +++ b/lib/src/tests/mod.nr @@ -1,10 +1,11 @@ -mod test_inputs; +pub(crate) mod test_inputs; mod test_success { use crate::{ - headers::{email_address::get_email_address, body_hash::get_body_hash}, - tests::test_inputs::EmailLarge, partial_hash::partial_sha256_var_end, MAX_EMAIL_ADDRESS_LENGTH + headers::{body_hash::get_body_hash, email_address::get_email_address}, + MAX_EMAIL_ADDRESS_LENGTH, partial_hash::partial_sha256_var_end, + tests::test_inputs::EmailLarge, }; use std::hash::sha256_var; @@ -19,13 +20,15 @@ mod test_success { let signed_body_hash = get_body_hash( EmailLarge::HEADER, EmailLarge::DKIM_HEADER_SEQUENCE, - EmailLarge::BODY_HASH_INDEX + EmailLarge::BODY_HASH_INDEX, ); // compute the body hash - let computed_body_hash: [u8; 32] = sha256_var(EmailLarge::BODY.storage, EmailLarge::BODY.len() as u64); + let computed_body_hash: [u8; 32] = + sha256_var(EmailLarge::BODY.storage(), EmailLarge::BODY.len() as u64); // compare the body hashes assert( - signed_body_hash == computed_body_hash, "SHA256 hash computed over body does not match body hash found in DKIM-signed header" + signed_body_hash == computed_body_hash, + "SHA256 hash computed over body does not match body hash found in DKIM-signed header", ); } @@ -35,45 +38,48 @@ mod test_success { let signed_body_hash = get_body_hash( EmailLarge::HEADER, EmailLarge::DKIM_HEADER_SEQUENCE, - EmailLarge::BODY_HASH_INDEX + EmailLarge::BODY_HASH_INDEX, ); // finish the partial hash let computed_body_hash = partial_sha256_var_end( EmailLarge::PARTIAL_BODY_HASH, - EmailLarge::PARTIAL_BODY.storage, + EmailLarge::PARTIAL_BODY.storage(), EmailLarge::PARTIAL_BODY.len() as u64, - EmailLarge::PARTIAL_BODY_REAL_LENGTH as u64 + EmailLarge::PARTIAL_BODY_REAL_LENGTH as u64, ); // compare the body hashes assert( - signed_body_hash == computed_body_hash, "Sha256 hash computed over body does not match DKIM-signed header" + signed_body_hash == computed_body_hash, + "Sha256 hash computed over body does not match DKIM-signed header", ); } #[test] fn test_address_extraction() { - let from = comptime { - "from".as_bytes() - }; - let to = comptime { - "to".as_bytes() - }; + let from = comptime { "from".as_bytes() }; + let to = comptime { "to".as_bytes() }; // 16k gate cost? has to be able to be brought down let from_address = get_email_address( EmailLarge::HEADER, EmailLarge::FROM_HEADER_SEQUENCE, EmailLarge::FROM_ADDRESS_SEQUENCE, - from + from, ); let to_address = get_email_address( EmailLarge::HEADER, EmailLarge::TO_HEADER_SEQUENCE, EmailLarge::TO_ADDRESS_SEQUENCE, - to + to, + ); + let expected_from_address: BoundedVec = + BoundedVec::from_array("runnier.leagues.0j@icloud.com".as_bytes()); + let expected_to_address: BoundedVec = + BoundedVec::from_array("zkewtest@gmail.com".as_bytes()); + assert_eq( + expected_from_address, + from_address, + "From address does not match expected address", ); - let expected_from_address: BoundedVec = BoundedVec::from_array("runnier.leagues.0j@icloud.com".as_bytes()); - let expected_to_address: BoundedVec = BoundedVec::from_array("zkewtest@gmail.com".as_bytes()); - assert_eq(expected_from_address, from_address, "From address does not match expected address"); assert_eq(expected_to_address, to_address, "To address does not match expected address"); } } @@ -86,7 +92,7 @@ mod test_tampered_hash { #[test(should_fail)] fn test_tampered_header() { // get tampered header - let mut tampered_header = EmailLarge::tampered_header(); + let mut tampered_header = unsafe { EmailLarge::tampered_header() }; // attempt to verify the DKIM signature EmailLarge::PUBKEY.verify_dkim_signature(tampered_header, EmailLarge::SIGNATURE); } @@ -97,14 +103,18 @@ mod test_tampered_hash { let signed_body_hash = get_body_hash( EmailLarge::HEADER, EmailLarge::DKIM_HEADER_SEQUENCE, - EmailLarge::BODY_HASH_INDEX + EmailLarge::BODY_HASH_INDEX, ); // get tampered body - let mut tampered_body = EmailLarge::tampered_body(); + let mut tampered_body = unsafe { EmailLarge::tampered_body() }; // compute the body hash - let tampered_body_hash: [u8; 32] = sha256_var(tampered_body.storage, tampered_body.len() as u64); + let tampered_body_hash: [u8; 32] = + sha256_var(tampered_body.storage(), tampered_body.len() as u64); // compare the body hashes - assert(signed_body_hash != tampered_body_hash, "SHA256 hash should not match tampered body hash"); + assert( + signed_body_hash != tampered_body_hash, + "SHA256 hash should not match tampered body hash", + ); } } @@ -118,7 +128,7 @@ mod header_field_access { let _ = get_body_hash( EmailLarge::HEADER, EmailLarge::DKIM_HEADER_SEQUENCE, - EmailLarge::BODY_HASH_INDEX - 1 + EmailLarge::BODY_HASH_INDEX - 1, ); } @@ -128,29 +138,31 @@ mod header_field_access { let _ = get_body_hash( EmailLarge::HEADER, EmailLarge::DKIM_HEADER_SEQUENCE, - EmailLarge::BODY_HASH_INDEX + 1 + EmailLarge::BODY_HASH_INDEX + 1, ); } #[test(should_fail_with = "Header field name does not match")] fn test_bad_body_hash_not_in_dkim_field() { // create header field for malicious bh - let mut dkim_field: BoundedVec = BoundedVec::new(); + let mut dkim_field: BoundedVec = + BoundedVec::new(); dkim_field.len = EmailLarge::HEADER.len(); // craft a malicious "to" field where attacker tries to put bh in display name let mut malicious_to: [u8; 78] = comptime { - "\r\nto:\"bh=2JsdK4BMzzt9w4Zlz2TdyVCFc+l7vNyT5aAgGDYf7fM=;\" \r\n".as_bytes() + "\r\nto:\"bh=2JsdK4BMzzt9w4Zlz2TdyVCFc+l7vNyT5aAgGDYf7fM=;\" \r\n" + .as_bytes() }; let mut malicious_sequence = Sequence { index: 8, // 8 to make it check for crlf on both sides (could be anything > 2) - length: malicious_to.len() - 4 // 4 is the crlf on each end + length: malicious_to.len() - 4, // 4 is the crlf on each end }; for i in 0..malicious_to.len() { let index = malicious_sequence.index + i - 2; dkim_field.storage[index] = malicious_to[i]; } let malicious_body_hash_index = 15; - // copy the body hash to the beginning of the + // copy the body hash to the beginning of the // attempt to get body hash let _ = get_body_hash(dkim_field, malicious_sequence, malicious_body_hash_index); } @@ -167,7 +179,7 @@ mod header_field_access { let _ = get_body_hash( longer_header, overflowed_sequence, - EmailLarge::BODY_HASH_INDEX + EmailLarge::BODY_HASH_INDEX, ); } @@ -180,7 +192,7 @@ mod header_field_access { let _ = get_body_hash( EmailLarge::HEADER, overflowed_sequence, - EmailLarge::BODY_HASH_INDEX + EmailLarge::BODY_HASH_INDEX, ); } @@ -193,7 +205,7 @@ mod header_field_access { let _ = get_body_hash( EmailLarge::HEADER, underflowed_sequence, - EmailLarge::BODY_HASH_INDEX + EmailLarge::BODY_HASH_INDEX, ); } @@ -206,33 +218,42 @@ mod header_field_access { let _ = get_body_hash( EmailLarge::HEADER, underflowed_sequence, - EmailLarge::BODY_HASH_INDEX + EmailLarge::BODY_HASH_INDEX, ); } #[test(should_fail_with = "Header field must not contain newlines")] fn test_header_field_multiple_fields() { // combine to and dkim-signature fields together - let mut tampered_header: BoundedVec = BoundedVec::new(); + let mut tampered_header: BoundedVec = + BoundedVec::new(); let combined_sequence = Sequence { index: 2, - length: EmailLarge::TO_HEADER_SEQUENCE.length + EmailLarge::DKIM_HEADER_SEQUENCE.length + 2 // 2 for crlf in middle + length: EmailLarge::TO_HEADER_SEQUENCE.length + + EmailLarge::DKIM_HEADER_SEQUENCE.length + + 2, // 2 for crlf in middle }; tampered_header.len = combined_sequence.length + 4; // copy dkim-signature field for i in 0..EmailLarge::DKIM_HEADER_SEQUENCE.length + 2 { - tampered_header.storage[i] = EmailLarge::HEADER.storage[EmailLarge::DKIM_HEADER_SEQUENCE.index + i - 2]; + tampered_header.set( + i, + EmailLarge::HEADER.get(EmailLarge::DKIM_HEADER_SEQUENCE.index + i - 2), + ); } - tampered_header.storage[EmailLarge::DKIM_HEADER_SEQUENCE.length + 2] = "\r".as_bytes()[0]; - tampered_header.storage[EmailLarge::DKIM_HEADER_SEQUENCE.length + 3] = "\n".as_bytes()[0]; + tampered_header.set(EmailLarge::DKIM_HEADER_SEQUENCE.length + 2, "\r".as_bytes()[0]); + tampered_header.set(EmailLarge::DKIM_HEADER_SEQUENCE.length + 3, "\n".as_bytes()[0]); // copy to field for i in 0..EmailLarge::TO_HEADER_SEQUENCE.length + 2 { let index = EmailLarge::DKIM_HEADER_SEQUENCE.length + 4; - tampered_header.storage[index + i] = EmailLarge::HEADER.storage[EmailLarge::TO_HEADER_SEQUENCE.index + i]; + tampered_header.set( + index + i, + EmailLarge::HEADER.get(EmailLarge::TO_HEADER_SEQUENCE.index + i), + ); } // set crlf at end - tampered_header.storage[combined_sequence.length + 2] = "\r".as_bytes()[0]; - tampered_header.storage[combined_sequence.length + 3] = "\n".as_bytes()[0]; + tampered_header.set(combined_sequence.length + 2, "\r".as_bytes()[0]); + tampered_header.set(combined_sequence.length + 3, "\n".as_bytes()[0]); let tampered_body_hash_index = 93; // just manually setting this // attempt to get body hash let _ = get_body_hash(tampered_header, combined_sequence, tampered_body_hash_index); @@ -242,99 +263,88 @@ mod header_field_access { fn test_header_field_outside_header() { let mut shortened_header = EmailLarge::HEADER; // shorten header to be just under the end of the dkim field - shortened_header.len = EmailLarge::DKIM_HEADER_SEQUENCE.index + EmailLarge::DKIM_HEADER_SEQUENCE.length - 1; + shortened_header.len = + EmailLarge::DKIM_HEADER_SEQUENCE.index + EmailLarge::DKIM_HEADER_SEQUENCE.length - 1; // attempt to get body hash let _ = get_body_hash( shortened_header, EmailLarge::DKIM_HEADER_SEQUENCE, - EmailLarge::BODY_HASH_INDEX + EmailLarge::BODY_HASH_INDEX, ); } } mod test_address_extraction { // header field constrains are not checked here, should work same as tests in header_field_access - use crate::{headers::email_address::get_email_address, Sequence, tests::test_inputs::EmailAddresses}; + use crate::{headers::email_address::get_email_address, tests::test_inputs::EmailAddresses}; #[test] fn test_email_only() { - let from: [u8; 4] = comptime { - "from".as_bytes() - }; + let from: [u8; 4] = comptime { "from".as_bytes() }; let header: BoundedVec = BoundedVec::from_array(EmailAddresses::ADDRESS_ONLY); let address = get_email_address( header, EmailAddresses::ADDRESS_ONLY_FIELD_SEQUENCE, EmailAddresses::ADDRESS_ONLY_ADDRESS_SEQUENCE, - from + from, ); assert_eq(address, EmailAddresses::ADDRESS); } #[test] fn test_no_brackets() { - let from: [u8; 4] = comptime { - "from".as_bytes() - }; + let from: [u8; 4] = comptime { "from".as_bytes() }; let header: BoundedVec = BoundedVec::from_array(EmailAddresses::NO_BRACKETS); let address = get_email_address( header, EmailAddresses::NO_BRACKETS_FIELD_SEQUENCE, EmailAddresses::NO_BRACKETS_ADDRESS_SEQUENCE, - from + from, ); assert_eq(address, EmailAddresses::ADDRESS); } #[test] fn test_brackets() { - let from: [u8; 4] = comptime { - "from".as_bytes() - }; + let from: [u8; 4] = comptime { "from".as_bytes() }; let header: BoundedVec = BoundedVec::from_array(EmailAddresses::BRACKETS); let address = get_email_address( header, EmailAddresses::BRACKETS_FIELD_SEQUENCE, EmailAddresses::BRACKETS_ADDRESS_SEQUENCE, - from + from, ); assert_eq(address, EmailAddresses::ADDRESS); } #[test] fn test_quotes() { - let from: [u8; 4] = comptime { - "from".as_bytes() - }; + let from: [u8; 4] = comptime { "from".as_bytes() }; let header: BoundedVec = BoundedVec::from_array(EmailAddresses::QUOTES); let address = get_email_address( header, EmailAddresses::QUOTES_FIELD_SEQUENCE, EmailAddresses::QUOTES_ADDRESS_SEQUENCE, - from + from, ); assert_eq(address, EmailAddresses::ADDRESS); } #[test(should_fail_with = "Email address must start immediately after '<' if bracket is present")] fn test_malicious_display_name() { - let from: [u8; 4] = comptime { - "from".as_bytes() - }; + let from: [u8; 4] = comptime { "from".as_bytes() }; let header: BoundedVec = BoundedVec::from_array(EmailAddresses::QUOTES); let _ = get_email_address( header, EmailAddresses::QUOTES_FIELD_SEQUENCE, EmailAddresses::MALICIOUS_QUOTES_ADDRESS_SEQUENCE, - from + from, ); } #[test(should_fail_with = "Email address must start immediately after '<' if bracket is present")] fn test_not_full_address_bracket_start() { - let from: [u8; 4] = comptime { - "from".as_bytes() - }; + let from: [u8; 4] = comptime { "from".as_bytes() }; // mutate index to remove "runnier." from address let mut malicious_sequence = EmailAddresses::QUOTES_ADDRESS_SEQUENCE; malicious_sequence.index = malicious_sequence.index + 8; @@ -344,15 +354,13 @@ mod test_address_extraction { header, EmailAddresses::QUOTES_FIELD_SEQUENCE, malicious_sequence, - from + from, ); } #[test(should_fail_with = "Email address must end with an acceptable character")] fn test_not_full_address_bracket_end() { - let from: [u8; 4] = comptime { - "from".as_bytes() - }; + let from: [u8; 4] = comptime { "from".as_bytes() }; // mutate index to remove ".com" from address let mut malicious_sequence = EmailAddresses::QUOTES_ADDRESS_SEQUENCE; malicious_sequence.index = malicious_sequence.index; @@ -362,15 +370,13 @@ mod test_address_extraction { header, EmailAddresses::QUOTES_FIELD_SEQUENCE, malicious_sequence, - from + from, ); } #[test(should_fail_with = "Email address must start with an acceptable character")] fn test_not_full_address_start() { - let from: [u8; 4] = comptime { - "from".as_bytes() - }; + let from: [u8; 4] = comptime { "from".as_bytes() }; // mutate index to remove "runnier." from address let mut malicious_sequence = EmailAddresses::ADDRESS_ONLY_ADDRESS_SEQUENCE; malicious_sequence.index = malicious_sequence.index + 8; @@ -380,15 +386,13 @@ mod test_address_extraction { header, EmailAddresses::ADDRESS_ONLY_FIELD_SEQUENCE, malicious_sequence, - from + from, ); } #[test(should_fail_with = "Email address must end with an acceptable character")] fn test_not_full_address_end() { - let from: [u8; 4] = comptime { - "from".as_bytes() - }; + let from: [u8; 4] = comptime { "from".as_bytes() }; // mutate index to remove ".com" from address let mut malicious_sequence = EmailAddresses::ADDRESS_ONLY_ADDRESS_SEQUENCE; malicious_sequence.index = malicious_sequence.index; @@ -398,7 +402,7 @@ mod test_address_extraction { header, EmailAddresses::ADDRESS_ONLY_FIELD_SEQUENCE, malicious_sequence, - from + from, ); } } diff --git a/lib/src/tests/test_inputs.nr b/lib/src/tests/test_inputs.nr index 1d5e68c..963c291 100644 --- a/lib/src/tests/test_inputs.nr +++ b/lib/src/tests/test_inputs.nr @@ -1,117 +1,206 @@ -mod EmailLarge { +pub(crate) mod EmailLarge { use crate::{Sequence, KEY_LIMBS_2048, dkim::RSAPubkey}; // regular inputs - global EMAIL_LARGE_MAX_HEADER_LENGTH: u32 = 512; - global EMAIL_LARGE_MAX_BODY_LENGTH: u32 = 1024; - global HEADER: BoundedVec = BoundedVec { - storage: [102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 116, 102, 45, 56, 13, 10, 109, 105, 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, 46, 48, 32, 40, 77, 97, 99, 32, 79, 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, 117, 98, 106, 101, 99, 116, 58, 66, 105, 116, 99, 111, 105, 110, 13, 10, 109, 101, 115, 115, 97, 103, 101, 45, 105, 100, 58, 60, 49, 50, 56, 48, 48, 65, 57, 48, 45, 52, 69, 67, 67, 45, 52, 53, 49, 51, 45, 57, 50, 57, 56, 45, 65, 51, 51, 53, 52, 54, 49, 50, 50, 57, 50, 48, 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, 100, 97, 116, 101, 58, 87, 101, 100, 44, 32, 51, 32, 65, 112, 114, 32, 50, 48, 50, 52, 32, 49, 54, 58, 50, 51, 58, 52, 56, 32, 43, 48, 53, 51, 48, 13, 10, 116, 111, 58, 122, 107, 101, 119, 116, 101, 115, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, 105, 109, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, 114, 115, 97, 45, 115, 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, 100, 47, 114, 101, 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 55, 49, 50, 49, 52, 49, 54, 52, 52, 59, 32, 98, 104, 61, 50, 74, 115, 100, 75, 52, 66, 77, 122, 122, 116, 57, 119, 52, 90, 108, 122, 50, 84, 100, 121, 86, 67, 70, 99, 43, 108, 55, 118, 78, 121, 84, 53, 97, 65, 103, 71, 68, 89, 102, 55, 102, 77, 61, 59, 32, 104, 61, 102, 114, 111, 109, 58, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, 101, 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99, 116, 58, 77, 101, 115, 115, 97, 103, 101, 45, 73, 100, 58, 68, 97, 116, 101, 58, 116, 111, 59, 32, 98, 61, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - len: 470 - }; - global BODY: BoundedVec = BoundedVec { - storage: [84, 104, 101, 32, 84, 105, 109, 101, 115, 32, 48, 51, 47, 74, 97, 110, 47, 50, 48, 48, 57, 32, 67, 104, 97, 110, 99, 101, 108, 108, 111, 114, 32, 111, 110, 32, 98, 114, 105, 110, 107, 32, 111, 102, 32, 115, 101, 99, 111, 110, 100, 32, 98, 97, 105, 108, 111, 117, 116, 32, 102, 111, 114, 32, 98, 97, 110, 107, 115, 13, 10, 13, 10, 49, 53, 32, 121, 101, 97, 114, 115, 32, 97, 103, 111, 44, 32, 83, 97, 116, 111, 115, 104, 105, 32, 109, 105, 110, 101, 100, 32, 116, 104, 101, 32, 102, 105, 114, 115, 116, 32, 98, 108, 111, 99, 107, 32, 111, 102, 32, 116, 104, 101, 32, 66, 105, 116, 99, 111, 105, 110, 32, 98, 108, 111, 99, 107, 99, 104, 97, 105, 110, 32, 61, 13, 10, 65, 102, 116, 101, 114, 32, 116, 104, 101, 32, 66, 105, 116, 99, 111, 105, 110, 32, 119, 104, 105, 116, 101, 32, 112, 97, 112, 101, 114, 32, 97, 112, 112, 101, 97, 114, 101, 100, 32, 111, 110, 32, 79, 99, 116, 111, 98, 101, 114, 32, 51, 49, 44, 32, 50, 48, 48, 56, 44, 32, 111, 110, 32, 97, 32, 61, 13, 10, 99, 114, 121, 112, 116, 111, 103, 114, 97, 112, 104, 121, 32, 109, 97, 105, 108, 105, 110, 103, 32, 108, 105, 115, 116, 44, 32, 116, 104, 101, 32, 71, 101, 110, 101, 115, 105, 115, 32, 66, 108, 111, 99, 107, 32, 61, 69, 50, 61, 56, 48, 61, 57, 52, 32, 116, 104, 101, 32, 102, 105, 114, 115, 116, 32, 98, 105, 116, 99, 111, 105, 110, 32, 61, 13, 10, 98, 108, 111, 99, 107, 32, 97, 110, 100, 32, 116, 104, 101, 32, 98, 97, 115, 105, 115, 32, 111, 102, 32, 116, 104, 101, 32, 101, 110, 116, 105, 114, 101, 32, 66, 105, 116, 99, 111, 105, 110, 32, 116, 114, 97, 100, 105, 110, 103, 32, 115, 121, 115, 116, 101, 109, 32, 105, 110, 32, 112, 108, 97, 99, 101, 32, 116, 111, 32, 61, 13, 10, 116, 104, 105, 115, 32, 100, 97, 121, 32, 61, 69, 50, 61, 56, 48, 61, 57, 52, 32, 119, 97, 115, 32, 109, 105, 110, 101, 100, 32, 111, 110, 32, 74, 97, 110, 117, 97, 114, 121, 32, 51, 44, 32, 50, 48, 48, 57, 46, 61, 50, 48, 13, 10, 13, 10, 84, 104, 101, 32, 71, 101, 110, 101, 115, 105, 115, 32, 66, 108, 111, 99, 107, 32, 105, 115, 32, 97, 108, 115, 111, 32, 107, 110, 111, 119, 110, 32, 97, 115, 32, 66, 108, 111, 99, 107, 32, 48, 32, 111, 114, 32, 66, 108, 111, 99, 107, 32, 49, 44, 32, 97, 110, 100, 32, 105, 115, 32, 115, 116, 105, 108, 108, 32, 105, 110, 32, 61, 13, 10, 116, 104, 101, 32, 66, 105, 116, 99, 111, 105, 110, 32, 110, 101, 116, 119, 111, 114, 107, 44, 32, 119, 104, 101, 114, 101, 32, 105, 116, 32, 119, 105, 108, 108, 32, 114, 101, 109, 97, 105, 110, 32, 97, 115, 32, 108, 111, 110, 103, 32, 97, 115, 32, 116, 104, 101, 114, 101, 32, 105, 115, 32, 97, 32, 99, 111, 109, 112, 117, 116, 101, 114, 32, 61, 13, 10, 114, 117, 110, 110, 105, 110, 103, 32, 116, 104, 101, 32, 66, 105, 116, 99, 111, 105, 110, 32, 115, 111, 102, 116, 119, 97, 114, 101, 46, 61, 50, 48, 13, 10, 13, 10, 65, 108, 108, 32, 110, 111, 100, 101, 115, 32, 105, 110, 32, 116, 104, 101, 32, 66, 105, 116, 99, 111, 105, 110, 32, 110, 101, 116, 119, 111, 114, 107, 32, 99, 97, 110, 32, 99, 111, 110, 115, 117, 108, 116, 32, 105, 116, 44, 32, 101, 118, 101, 110, 32, 105, 102, 32, 105, 116, 32, 105, 115, 32, 97, 116, 32, 116, 104, 101, 32, 61, 13, 10, 111, 116, 104, 101, 114, 32, 101, 110, 100, 32, 111, 102, 32, 116, 104, 101, 32, 110, 101, 116, 119, 111, 114, 107, 32, 119, 105, 116, 104, 32, 104, 117, 110, 100, 114, 101, 100, 115, 32, 111, 102, 32, 116, 104, 111, 117, 115, 97, 110, 100, 115, 32, 111, 102, 32, 98, 108, 111, 99, 107, 115, 46, 13, 10, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - len: 740 - }; - global PUBKEY: RSAPubkey = RSAPubkey { - modulus: [0xe5cf995b5ef59ce9943d1f4209b6ab, 0xe0caf03235e91a2db27e9ed214bcc6, 0xafe1309f87414bd36ed296dacfade2, 0xbeff3f19046a43adce46c932514988, 0x324041af8736e87de4358860fff057, 0xadcc6669dfa346f322717851a8c22a, 0x8b2a193089e6bf951c553b5a6f71aa, 0x0a570fe582918c4f731a0002068df2, 0x39419a433d6bfdd1978356cbca4b60, 0x550d695a514d38b45c862320a00ea5, 0x1c56ac1dfbf1beea31e8a613c2a51f, 0x6a30c9f22d2e5cb6934263d0838809, 0x0a281f268a44b21a4f77a91a52f960, 0x5134dc3966c8e91402669a47cc8597, 0x71590781df114ec072e641cdc5d224, 0xa1bc0f0937489c806c1944fd029dc9, 0x911f6e47f84db3b64c3648ebb5a127, 0xd5], - redc: [0x48a824e4ebc7e0f1059f3ecfa57c46, 0x5c1db23f3c7d47ad7e7d7cfda5189a, 0x9bb6bbbd8facf011f022fa9051aec0, 0x4faa4cef474bed639362ea71f7a217, 0x503aa50b77e24b030841a7d0615812, 0xbbf4e62805e1860a904c0f66a5fad1, 0xcbd24b72442d2ce647dd7d0a443685, 0x74a8839a4460c169dce7138efdaef5, 0xf06e09e3191b995b08e5b45182f650, 0x1fad4a89f8369fe10e5d4b6e149a10, 0xc778b15982d11ebf7fe23b4e15f105, 0x09ff3a4567077510c474e4ac0a21ad, 0x37e69e5dbb77167b73065e4c5ad6aa, 0xcf4774e22e7fe3a38642186f7ae74b, 0x6e72b5eb4c813a3b37998083aab81e, 0x48e7050aa8abedce5a45c169853761, 0xd3285e53b322b221f7bcf4f8f8ad8a, 0x132d] - }; - global SIGNATURE: [Field; KEY_LIMBS_2048] = [0xf193c3300b7c9902e32861c38d0d2d, 0x9f6927fdb3df0b84092d8459654327, 0x8a0bea5e2fa82821e49c27b68d5a7b, 0xaa8c0acc1190f9fd845ef64f8e7ae9, 0xa7aeebb37f4395965543e6df69a5a7, 0x087ecef9921569cfba83331ca11c6b, 0x4589ed316ed20757e65ad221736011, 0x0835d8748f11dcc985700c3fea27b1, 0xe870d2493fb83b4a1d72350e5de926, 0x268b28eda0aac07625cfab32b60af1, 0xb41a164eae7ba1602eaec5b5a39fe6, 0x693cc5ec578422bee48eabe390fc37, 0xa29504dd504f14423f2ce65b2ac388, 0x6c3ac6310c084a0b126fcd5225c208, 0xab0903e48563e5f4a5365ac5cbd888, 0xf05bf2e5b6266c0ac88dfc733c414f, 0xf58f9e9669e0f4f3086cce1187fd44, 0xb9]; - global BODY_HASH_INDEX: u32 = 361; - global DKIM_HEADER_SEQUENCE = Sequence { - index: 267, - length: 203 - }; - // partial hash inputs - global EMAIL_LARGE_MAX_PARTIAL_BODY_LENGTH: u32 = 192; - global PARTIAL_BODY_HASH: [u32; 8] = [616616639, 1838516336, 2875062989, 1377577374, 300896023, 2753786313, 2559427666, 979052520]; - global PARTIAL_BODY_REAL_LENGTH: u32 = BODY.len(); - global PARTIAL_BODY: BoundedVec = BoundedVec { - storage: [104, 101, 32, 66, 105, 116, 99, 111, 105, 110, 32, 115, 111, 102, 116, 119, 97, 114, 101, 46, 61, 50, 48, 13, 10, 13, 10, 65, 108, 108, 32, 110, 111, 100, 101, 115, 32, 105, 110, 32, 116, 104, 101, 32, 66, 105, 116, 99, 111, 105, 110, 32, 110, 101, 116, 119, 111, 114, 107, 32, 99, 97, 110, 32, 99, 111, 110, 115, 117, 108, 116, 32, 105, 116, 44, 32, 101, 118, 101, 110, 32, 105, 102, 32, 105, 116, 32, 105, 115, 32, 97, 116, 32, 116, 104, 101, 32, 61, 13, 10, 111, 116, 104, 101, 114, 32, 101, 110, 100, 32, 111, 102, 32, 116, 104, 101, 32, 110, 101, 116, 119, 111, 114, 107, 32, 119, 105, 116, 104, 32, 104, 117, 110, 100, 114, 101, 100, 115, 32, 111, 102, 32, 116, 104, 111, 117, 115, 97, 110, 100, 115, 32, 111, 102, 32, 98, 108, 111, 99, 107, 115, 46, 13, 10, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - len: 164 - }; + pub(crate) global EMAIL_LARGE_MAX_HEADER_LENGTH: u32 = 512; + pub(crate) global EMAIL_LARGE_MAX_BODY_LENGTH: u32 = 1024; + pub(crate) global HEADER: BoundedVec = BoundedVec::from([ + 102, 114, 111, 109, 58, 114, 117, 110, 110, 105, 101, 114, 46, 108, 101, 97, 103, 117, 101, + 115, 46, 48, 106, 64, 105, 99, 108, 111, 117, 100, 46, 99, 111, 109, 13, 10, 99, 111, 110, + 116, 101, 110, 116, 45, 116, 121, 112, 101, 58, 116, 101, 120, 116, 47, 112, 108, 97, 105, + 110, 59, 32, 99, 104, 97, 114, 115, 101, 116, 61, 117, 116, 102, 45, 56, 13, 10, 109, 105, + 109, 101, 45, 118, 101, 114, 115, 105, 111, 110, 58, 49, 46, 48, 32, 40, 77, 97, 99, 32, 79, + 83, 32, 88, 32, 77, 97, 105, 108, 32, 49, 54, 46, 48, 32, 92, 40, 51, 55, 51, 49, 46, 53, + 48, 48, 46, 50, 51, 49, 92, 41, 41, 13, 10, 115, 117, 98, 106, 101, 99, 116, 58, 66, 105, + 116, 99, 111, 105, 110, 13, 10, 109, 101, 115, 115, 97, 103, 101, 45, 105, 100, 58, 60, 49, + 50, 56, 48, 48, 65, 57, 48, 45, 52, 69, 67, 67, 45, 52, 53, 49, 51, 45, 57, 50, 57, 56, 45, + 65, 51, 51, 53, 52, 54, 49, 50, 50, 57, 50, 48, 64, 109, 101, 46, 99, 111, 109, 62, 13, 10, + 100, 97, 116, 101, 58, 87, 101, 100, 44, 32, 51, 32, 65, 112, 114, 32, 50, 48, 50, 52, 32, + 49, 54, 58, 50, 51, 58, 52, 56, 32, 43, 48, 53, 51, 48, 13, 10, 116, 111, 58, 122, 107, 101, + 119, 116, 101, 115, 116, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 13, 10, 100, 107, + 105, 109, 45, 115, 105, 103, 110, 97, 116, 117, 114, 101, 58, 118, 61, 49, 59, 32, 97, 61, + 114, 115, 97, 45, 115, 104, 97, 50, 53, 54, 59, 32, 99, 61, 114, 101, 108, 97, 120, 101, + 100, 47, 114, 101, 108, 97, 120, 101, 100, 59, 32, 100, 61, 105, 99, 108, 111, 117, 100, 46, + 99, 111, 109, 59, 32, 115, 61, 49, 97, 49, 104, 97, 105, 59, 32, 116, 61, 49, 55, 49, 50, + 49, 52, 49, 54, 52, 52, 59, 32, 98, 104, 61, 50, 74, 115, 100, 75, 52, 66, 77, 122, 122, + 116, 57, 119, 52, 90, 108, 122, 50, 84, 100, 121, 86, 67, 70, 99, 43, 108, 55, 118, 78, 121, + 84, 53, 97, 65, 103, 71, 68, 89, 102, 55, 102, 77, 61, 59, 32, 104, 61, 102, 114, 111, 109, + 58, 67, 111, 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 58, 77, 105, 109, 101, 45, 86, + 101, 114, 115, 105, 111, 110, 58, 83, 117, 98, 106, 101, 99, 116, 58, 77, 101, 115, 115, 97, + 103, 101, 45, 73, 100, 58, 68, 97, 116, 101, 58, 116, 111, 59, 32, 98, 61, + ]); + pub(crate) global BODY: BoundedVec = BoundedVec::from([ + 84, 104, 101, 32, 84, 105, 109, 101, 115, 32, 48, 51, 47, 74, 97, 110, 47, 50, 48, 48, 57, + 32, 67, 104, 97, 110, 99, 101, 108, 108, 111, 114, 32, 111, 110, 32, 98, 114, 105, 110, 107, + 32, 111, 102, 32, 115, 101, 99, 111, 110, 100, 32, 98, 97, 105, 108, 111, 117, 116, 32, 102, + 111, 114, 32, 98, 97, 110, 107, 115, 13, 10, 13, 10, 49, 53, 32, 121, 101, 97, 114, 115, 32, + 97, 103, 111, 44, 32, 83, 97, 116, 111, 115, 104, 105, 32, 109, 105, 110, 101, 100, 32, 116, + 104, 101, 32, 102, 105, 114, 115, 116, 32, 98, 108, 111, 99, 107, 32, 111, 102, 32, 116, + 104, 101, 32, 66, 105, 116, 99, 111, 105, 110, 32, 98, 108, 111, 99, 107, 99, 104, 97, 105, + 110, 32, 61, 13, 10, 65, 102, 116, 101, 114, 32, 116, 104, 101, 32, 66, 105, 116, 99, 111, + 105, 110, 32, 119, 104, 105, 116, 101, 32, 112, 97, 112, 101, 114, 32, 97, 112, 112, 101, + 97, 114, 101, 100, 32, 111, 110, 32, 79, 99, 116, 111, 98, 101, 114, 32, 51, 49, 44, 32, 50, + 48, 48, 56, 44, 32, 111, 110, 32, 97, 32, 61, 13, 10, 99, 114, 121, 112, 116, 111, 103, 114, + 97, 112, 104, 121, 32, 109, 97, 105, 108, 105, 110, 103, 32, 108, 105, 115, 116, 44, 32, + 116, 104, 101, 32, 71, 101, 110, 101, 115, 105, 115, 32, 66, 108, 111, 99, 107, 32, 61, 69, + 50, 61, 56, 48, 61, 57, 52, 32, 116, 104, 101, 32, 102, 105, 114, 115, 116, 32, 98, 105, + 116, 99, 111, 105, 110, 32, 61, 13, 10, 98, 108, 111, 99, 107, 32, 97, 110, 100, 32, 116, + 104, 101, 32, 98, 97, 115, 105, 115, 32, 111, 102, 32, 116, 104, 101, 32, 101, 110, 116, + 105, 114, 101, 32, 66, 105, 116, 99, 111, 105, 110, 32, 116, 114, 97, 100, 105, 110, 103, + 32, 115, 121, 115, 116, 101, 109, 32, 105, 110, 32, 112, 108, 97, 99, 101, 32, 116, 111, 32, + 61, 13, 10, 116, 104, 105, 115, 32, 100, 97, 121, 32, 61, 69, 50, 61, 56, 48, 61, 57, 52, + 32, 119, 97, 115, 32, 109, 105, 110, 101, 100, 32, 111, 110, 32, 74, 97, 110, 117, 97, 114, + 121, 32, 51, 44, 32, 50, 48, 48, 57, 46, 61, 50, 48, 13, 10, 13, 10, 84, 104, 101, 32, 71, + 101, 110, 101, 115, 105, 115, 32, 66, 108, 111, 99, 107, 32, 105, 115, 32, 97, 108, 115, + 111, 32, 107, 110, 111, 119, 110, 32, 97, 115, 32, 66, 108, 111, 99, 107, 32, 48, 32, 111, + 114, 32, 66, 108, 111, 99, 107, 32, 49, 44, 32, 97, 110, 100, 32, 105, 115, 32, 115, 116, + 105, 108, 108, 32, 105, 110, 32, 61, 13, 10, 116, 104, 101, 32, 66, 105, 116, 99, 111, 105, + 110, 32, 110, 101, 116, 119, 111, 114, 107, 44, 32, 119, 104, 101, 114, 101, 32, 105, 116, + 32, 119, 105, 108, 108, 32, 114, 101, 109, 97, 105, 110, 32, 97, 115, 32, 108, 111, 110, + 103, 32, 97, 115, 32, 116, 104, 101, 114, 101, 32, 105, 115, 32, 97, 32, 99, 111, 109, 112, + 117, 116, 101, 114, 32, 61, 13, 10, 114, 117, 110, 110, 105, 110, 103, 32, 116, 104, 101, + 32, 66, 105, 116, 99, 111, 105, 110, 32, 115, 111, 102, 116, 119, 97, 114, 101, 46, 61, 50, + 48, 13, 10, 13, 10, 65, 108, 108, 32, 110, 111, 100, 101, 115, 32, 105, 110, 32, 116, 104, + 101, 32, 66, 105, 116, 99, 111, 105, 110, 32, 110, 101, 116, 119, 111, 114, 107, 32, 99, 97, + 110, 32, 99, 111, 110, 115, 117, 108, 116, 32, 105, 116, 44, 32, 101, 118, 101, 110, 32, + 105, 102, 32, 105, 116, 32, 105, 115, 32, 97, 116, 32, 116, 104, 101, 32, 61, 13, 10, 111, + 116, 104, 101, 114, 32, 101, 110, 100, 32, 111, 102, 32, 116, 104, 101, 32, 110, 101, 116, + 119, 111, 114, 107, 32, 119, 105, 116, 104, 32, 104, 117, 110, 100, 114, 101, 100, 115, 32, + 111, 102, 32, 116, 104, 111, 117, 115, 97, 110, 100, 115, 32, 111, 102, 32, 98, 108, 111, + 99, 107, 115, 46, 13, 10, + ]); + pub(crate) global PUBKEY: RSAPubkey = RSAPubkey::new( + [ + 0xe5cf995b5ef59ce9943d1f4209b6ab, + 0xe0caf03235e91a2db27e9ed214bcc6, + 0xafe1309f87414bd36ed296dacfade2, + 0xbeff3f19046a43adce46c932514988, + 0x324041af8736e87de4358860fff057, + 0xadcc6669dfa346f322717851a8c22a, + 0x8b2a193089e6bf951c553b5a6f71aa, + 0x0a570fe582918c4f731a0002068df2, + 0x39419a433d6bfdd1978356cbca4b60, + 0x550d695a514d38b45c862320a00ea5, + 0x1c56ac1dfbf1beea31e8a613c2a51f, + 0x6a30c9f22d2e5cb6934263d0838809, + 0x0a281f268a44b21a4f77a91a52f960, + 0x5134dc3966c8e91402669a47cc8597, + 0x71590781df114ec072e641cdc5d224, + 0xa1bc0f0937489c806c1944fd029dc9, + 0x911f6e47f84db3b64c3648ebb5a127, + 0xd5, + ], + [ + 0x48a824e4ebc7e0f1059f3ecfa57c46, + 0x5c1db23f3c7d47ad7e7d7cfda5189a, + 0x9bb6bbbd8facf011f022fa9051aec0, + 0x4faa4cef474bed639362ea71f7a217, + 0x503aa50b77e24b030841a7d0615812, + 0xbbf4e62805e1860a904c0f66a5fad1, + 0xcbd24b72442d2ce647dd7d0a443685, + 0x74a8839a4460c169dce7138efdaef5, + 0xf06e09e3191b995b08e5b45182f650, + 0x1fad4a89f8369fe10e5d4b6e149a10, + 0xc778b15982d11ebf7fe23b4e15f105, + 0x09ff3a4567077510c474e4ac0a21ad, + 0x37e69e5dbb77167b73065e4c5ad6aa, + 0xcf4774e22e7fe3a38642186f7ae74b, + 0x6e72b5eb4c813a3b37998083aab81e, + 0x48e7050aa8abedce5a45c169853761, + 0xd3285e53b322b221f7bcf4f8f8ad8a, + 0x132d, + ], + ); + pub(crate) global SIGNATURE: [Field; KEY_LIMBS_2048] = [ + 0xf193c3300b7c9902e32861c38d0d2d, + 0x9f6927fdb3df0b84092d8459654327, + 0x8a0bea5e2fa82821e49c27b68d5a7b, + 0xaa8c0acc1190f9fd845ef64f8e7ae9, + 0xa7aeebb37f4395965543e6df69a5a7, + 0x087ecef9921569cfba83331ca11c6b, + 0x4589ed316ed20757e65ad221736011, + 0x0835d8748f11dcc985700c3fea27b1, + 0xe870d2493fb83b4a1d72350e5de926, + 0x268b28eda0aac07625cfab32b60af1, + 0xb41a164eae7ba1602eaec5b5a39fe6, + 0x693cc5ec578422bee48eabe390fc37, + 0xa29504dd504f14423f2ce65b2ac388, + 0x6c3ac6310c084a0b126fcd5225c208, + 0xab0903e48563e5f4a5365ac5cbd888, + 0xf05bf2e5b6266c0ac88dfc733c414f, + 0xf58f9e9669e0f4f3086cce1187fd44, + 0xb9, + ]; + pub(crate) global BODY_HASH_INDEX: u32 = 361; + pub(crate) global DKIM_HEADER_SEQUENCE = Sequence { index: 267, length: 203 }; + // partial hash inputs + pub(crate) global EMAIL_LARGE_MAX_PARTIAL_BODY_LENGTH: u32 = 192; + pub(crate) global PARTIAL_BODY_HASH: [u32; 8] = [ + 616616639, 1838516336, 2875062989, 1377577374, 300896023, 2753786313, 2559427666, 979052520, + ]; + pub(crate) global PARTIAL_BODY_REAL_LENGTH: u32 = BODY.len(); + pub(crate) global PARTIAL_BODY: BoundedVec = BoundedVec::from( + [ + 104, 101, 32, 66, 105, 116, 99, 111, 105, 110, 32, 115, 111, 102, 116, 119, 97, 114, + 101, 46, 61, 50, 48, 13, 10, 13, 10, 65, 108, 108, 32, 110, 111, 100, 101, 115, 32, 105, + 110, 32, 116, 104, 101, 32, 66, 105, 116, 99, 111, 105, 110, 32, 110, 101, 116, 119, + 111, 114, 107, 32, 99, 97, 110, 32, 99, 111, 110, 115, 117, 108, 116, 32, 105, 116, 44, + 32, 101, 118, 101, 110, 32, 105, 102, 32, 105, 116, 32, 105, 115, 32, 97, 116, 32, 116, + 104, 101, 32, 61, 13, 10, 111, 116, 104, 101, 114, 32, 101, 110, 100, 32, 111, 102, 32, + 116, 104, 101, 32, 110, 101, 116, 119, 111, 114, 107, 32, 119, 105, 116, 104, 32, 104, + 117, 110, 100, 114, 101, 100, 115, 32, 111, 102, 32, 116, 104, 111, 117, 115, 97, 110, + 100, 115, 32, 111, 102, 32, 98, 108, 111, 99, 107, 115, 46, 13, 10, + ], + ); // email address inputs - global FROM_HEADER_SEQUENCE = Sequence { - index: 0, - length: 34 - }; - global FROM_ADDRESS_SEQUENCE = Sequence { - index: 5, - length: 29 - }; - global TO_HEADER_SEQUENCE = Sequence { - index: 244, - length: 21 - }; - global TO_ADDRESS_SEQUENCE = Sequence { - index: 247, - length: 18 - }; + pub(crate) global FROM_HEADER_SEQUENCE = Sequence { index: 0, length: 34 }; + pub(crate) global FROM_ADDRESS_SEQUENCE = Sequence { index: 5, length: 29 }; + pub(crate) global TO_HEADER_SEQUENCE = Sequence { index: 244, length: 21 }; + pub(crate) global TO_ADDRESS_SEQUENCE = Sequence { index: 247, length: 18 }; // mutate inputs - unconstrained pub fn tampered_header() -> BoundedVec { + pub unconstrained fn tampered_header() -> BoundedVec { let mut header = HEADER; header.set(140, header.get_unchecked(140) + 1); header } - unconstrained pub fn tampered_body() -> BoundedVec { + pub unconstrained fn tampered_body() -> BoundedVec { let mut body = BODY; body.set(140, body.get_unchecked(140) + 1); body } - unconstrained pub fn body_hash_outside_sequence() -> u32 { + pub unconstrained fn body_hash_outside_sequence() -> u32 { let dkim_sequence_start = DKIM_HEADER_SEQUENCE.index; dkim_sequence_start - 40 } } -mod EmailAddresses { - +pub(crate) mod EmailAddresses { + use crate::{Sequence, MAX_EMAIL_ADDRESS_LENGTH}; - global ADDRESS: BoundedVec = BoundedVec::from_array("runnier.leagues.0j@icloud.com".as_bytes()); - global ADDRESS_ONLY: [u8; 38] = comptime { "\r\nfrom:runnier.leagues.0j@icloud.com\r\n".as_bytes() }; - global NO_BRACKETS: [u8; 51] = comptime { "\r\nfrom:ZKEmail Team runnier.leagues.0j@icloud.com\r\n".as_bytes() }; - global BRACKETS: [u8; 52] = comptime { "\r\nfrom:ZKEmail Team = + BoundedVec::from_array("runnier.leagues.0j@icloud.com".as_bytes()); + pub(crate) global ADDRESS_ONLY: [u8; 38] = + comptime { "\r\nfrom:runnier.leagues.0j@icloud.com\r\n".as_bytes() }; + pub(crate) global NO_BRACKETS: [u8; 51] = + comptime { "\r\nfrom:ZKEmail Team runnier.leagues.0j@icloud.com\r\n".as_bytes() }; + pub(crate) global BRACKETS: [u8; 52] = + comptime { "\r\nfrom:ZKEmail Team