From 359b644eeff125fbf71a8be544d9a5106a248e0b Mon Sep 17 00:00:00 2001 From: Dimitri Date: Mon, 16 Sep 2024 15:35:12 +0700 Subject: [PATCH 1/6] Move util files from ether-email-auth --- Cargo.lock | 141 +++++++++++++++++++++++ Cargo.toml | 1 + src/command_templates.rs | 241 +++++++++++++++++++++++++++++++++++++++ src/lib.rs | 4 + src/proof.rs | 93 +++++++++++++++ 5 files changed, 480 insertions(+) create mode 100644 src/command_templates.rs create mode 100644 src/proof.rs diff --git a/Cargo.lock b/Cargo.lock index b56cbff..f9cd40f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1273,6 +1273,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1690,6 +1705,19 @@ dependencies = [ "tokio-rustls", ] +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.60" @@ -2132,6 +2160,23 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "neon" version = "0.10.1" @@ -2324,6 +2369,50 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "option-ext" version = "0.2.0" @@ -2855,6 +2944,7 @@ dependencies = [ "poseidon-rs", "rand_core", "regex", + "reqwest", "rsa", "serde", "serde_json", @@ -2882,10 +2972,12 @@ dependencies = [ "http-body", "hyper", "hyper-rustls", + "hyper-tls", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", @@ -2897,6 +2989,7 @@ dependencies = [ "sync_wrapper", "system-configuration", "tokio", + "tokio-native-tls", "tokio-rustls", "tower-service", "url", @@ -3129,6 +3222,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "schannel" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -3177,6 +3279,29 @@ dependencies = [ "zeroize", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75da29fe9b9b08fe9d6b22b5b4bcbc75d8db3aa31e639aa56bb62e9d46bfceaf" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "0.9.0" @@ -3733,6 +3858,16 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.24.1" @@ -4023,6 +4158,12 @@ dependencies = [ "serde", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" diff --git a/Cargo.toml b/Cargo.toml index ee64936..09a3771 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ num-traits = "0.2.15" cfdkim = { version = "0.3.3", git = "https://github.com/zkemail/dkim.git" } hmac-sha256 = { git = "https://github.com/zkemail/rust-hmac-sha256.git" } ethers = "2.0.14" +reqwest = "0.11.22" slog = { version = "2.7.0", features = [ "max_level_trace", "release_max_level_warn", diff --git a/src/command_templates.rs b/src/command_templates.rs new file mode 100644 index 0000000..af7cb23 --- /dev/null +++ b/src/command_templates.rs @@ -0,0 +1,241 @@ +#![allow(clippy::upper_case_acronyms)] + +use anyhow::{anyhow, Result}; +use ethers::abi::{self, Token}; +use ethers::types::{Address, Bytes, I256, U256}; +use regex::Regex; +use serde::{Deserialize, Serialize}; + +const STRING_REGEX: &str = r"\S+"; +const UINT_REGEX: &str = r"\d+"; +const INT_REGEX: &str = r"-?\d+"; +const ETH_ADDR_REGEX: &str = r"0x[a-fA-F0-9]{40}"; +const DECIMALS_REGEX: &str = r"\d+\.\d+"; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum TemplateValue { + String(String), + Uint(U256), + Int(I256), + Decimals(String), + EthAddr(Address), + Fixed(String), +} + +impl TemplateValue { + pub fn abi_encode(&self, decimal_size: Option) -> Result { + match self { + Self::String(string) => Ok(Bytes::from(abi::encode(&[Token::String(string.clone())]))), + Self::Uint(uint) => Ok(Bytes::from(abi::encode(&[Token::Uint(*uint)]))), + Self::Int(int) => Ok(Bytes::from(abi::encode(&[Token::Int(int.into_raw())]))), + Self::Decimals(string) => Ok(Bytes::from(abi::encode(&[Token::Uint( + Self::decimals_str_to_uint(string, decimal_size.unwrap_or(18)), + )]))), + Self::EthAddr(address) => Ok(Bytes::from(abi::encode(&[Token::Address(*address)]))), + Self::Fixed(_) => Err(anyhow!("Fixed value must not be passed to abi_encode")), + } + } + + pub fn decimals_str_to_uint(str: &str, decimal_size: u8) -> U256 { + let decimal_size = decimal_size as usize; + let dot = Regex::new("\\.").unwrap().find(str); + let (before_dot_str, mut after_dot_str) = match dot { + Some(dot_match) => ( + str[0..dot_match.start()].to_string(), + str[dot_match.end()..].to_string(), + ), + None => (str.to_string(), "".to_string()), + }; + assert!(after_dot_str.len() <= decimal_size); + let num_leading_zeros = decimal_size - after_dot_str.len(); + after_dot_str.push_str(&"0".repeat(num_leading_zeros)); + U256::from_dec_str(&(before_dot_str + &after_dot_str)) + .expect("composed amount string is not valid decimal") + } +} + +pub fn extract_template_vals_from_command( + input: &str, + templates: Vec, +) -> Result, anyhow::Error> { + // Convert the template to a regex pattern, escaping necessary characters and replacing placeholders + let pattern = templates + .iter() + .map(|template| match template.as_str() { + "{string}" => STRING_REGEX.to_string(), + "{uint}" => UINT_REGEX.to_string(), + "{int}" => INT_REGEX.to_string(), + "{decimals}" => DECIMALS_REGEX.to_string(), + "{ethAddr}" => ETH_ADDR_REGEX.to_string(), + _ => regex::escape(template), + }) + .collect::>() + .join("\\s+"); + + let regex = Regex::new(&pattern).map_err(|e| anyhow!("Regex compilation failed: {}", e))?; + + // Attempt to find the pattern in the input + if let Some(matched) = regex.find(input) { + // Calculate the number of bytes to skip before the match + let skipped_bytes = matched.start(); + + // Extract the values based on the matched pattern + let current_input = &input[skipped_bytes..]; + extract_template_vals(current_input, templates) + } else { + // If there's no match, return an error indicating no match was found + Err(anyhow!("Unable to match templates with input")) + } +} + +pub fn extract_template_vals(input: &str, templates: Vec) -> Result> { + let input_decomposed: Vec<&str> = input.split_whitespace().collect(); + let mut template_vals = Vec::new(); + + for (input_idx, template) in templates.iter().enumerate() { + match template.as_str() { + "{string}" => { + let string_match = Regex::new(STRING_REGEX) + .unwrap() + .find(input_decomposed[input_idx]) + .ok_or(anyhow!("No string found"))?; + if string_match.start() != 0 { + return Err(anyhow!("String must be the whole word")); + } + let mut string = string_match.as_str().to_string(); + if string.contains("") { + string = string.split("").collect::>()[0].to_string(); + } + template_vals.push(TemplateValue::String(string)); + } + "{uint}" => { + let uint_match = Regex::new(UINT_REGEX) + .unwrap() + .find(input_decomposed[input_idx]) + .ok_or(anyhow!("No uint found"))?; + if uint_match.start() != 0 || uint_match.end() != input_decomposed[input_idx].len() + { + return Err(anyhow!("Uint must be the whole word")); + } + let mut uint_match = uint_match.as_str(); + if uint_match.contains("") { + uint_match = uint_match.split("").collect::>()[0]; + } + let uint = U256::from_dec_str(uint_match).unwrap(); + template_vals.push(TemplateValue::Uint(uint)); + } + "{int}" => { + let int_match = Regex::new(INT_REGEX) + .unwrap() + .find(input_decomposed[input_idx]) + .ok_or(anyhow!("No int found"))?; + if int_match.start() != 0 || int_match.end() != input_decomposed[input_idx].len() { + return Err(anyhow!("Int must be the whole word")); + } + let mut int_match = int_match.as_str(); + if int_match.contains("") { + int_match = int_match.split("").collect::>()[0]; + } + let int = I256::from_dec_str(int_match).unwrap(); + template_vals.push(TemplateValue::Int(int)); + } + "{decimals}" => { + let decimals_match = Regex::new(DECIMALS_REGEX) + .unwrap() + .find(input_decomposed[input_idx]) + .ok_or(anyhow!("No decimals found"))?; + if decimals_match.start() != 0 + || decimals_match.end() != input_decomposed[input_idx].len() + { + return Err(anyhow!("Decimals must be the whole word")); + } + let mut decimals = decimals_match.as_str().to_string(); + if decimals.contains("") { + decimals = decimals.split("").collect::>()[0].to_string(); + } + template_vals.push(TemplateValue::Decimals(decimals)); + } + "{ethAddr}" => { + let address_match = Regex::new(ETH_ADDR_REGEX) + .unwrap() + .find(input_decomposed[input_idx]) + .ok_or(anyhow!("No address found"))?; + if address_match.start() != 0 { + return Err(anyhow!("Address must be the whole word")); + } + let address = address_match.as_str().parse::
().unwrap(); + template_vals.push(TemplateValue::EthAddr(address)); + } + _ => {} // Skip unknown placeholders + } + } + + Ok(template_vals) +} + +// Generated by Github Copilot! +pub fn uint_to_decimal_string(uint: u128, decimal: usize) -> String { + // Convert amount to string in wei format (no decimals) + let uint_str = uint.to_string(); + let uint_length = uint_str.len(); + + // Create result vector with max length + // If less than 18 decimals, then 2 extra for "0.", otherwise one extra for "." + let mut result = vec![ + '0'; + if uint_length > decimal { + uint_length + 1 + } else { + decimal + 2 + } + ]; + let result_length = result.len(); + + // Difference between result and amount array index when copying + // If more than 18, then 1 index diff for ".", otherwise actual diff in length + let mut delta = if uint_length > decimal { + 1 + } else { + result_length - uint_length + }; + + // Boolean to indicate if we found a non-zero digit when scanning from last to first index + let mut found_non_zero_decimal = false; + + let mut actual_result_len = 0; + + // In each iteration we fill one index of result array (starting from end) + for i in (0..result_length).rev() { + // Check if we have reached the index where we need to add decimal point + if i == result_length - decimal - 1 { + // No need to add "." if there was no value in decimal places + if found_non_zero_decimal { + result[i] = '.'; + actual_result_len += 1; + } + // Set delta to 0, as we have already added decimal point (only for amount_length > 18) + delta = 0; + } + // If amountLength < 18 and we have copied everything, fill zeros + else if uint_length <= decimal && i < result_length - uint_length { + result[i] = '0'; + actual_result_len += 1; + } + // If non-zero decimal is found, or decimal point inserted (delta == 0), copy from amount array + else if found_non_zero_decimal || delta == 0 { + result[i] = uint_str.chars().nth(i - delta).unwrap(); + actual_result_len += 1; + } + // If we find non-zero decimal for the first time (trailing zeros are skipped) + else if uint_str.chars().nth(i - delta).unwrap() != '0' { + result[i] = uint_str.chars().nth(i - delta).unwrap(); + actual_result_len += 1; + found_non_zero_decimal = true; + } + } + + // Create final result string with correct length + let compact_result: String = result.into_iter().take(actual_result_len).collect(); + + compact_result +} diff --git a/src/lib.rs b/src/lib.rs index 4b6b7af..387b761 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,18 +1,22 @@ pub mod circuit; +pub mod command_templates; pub mod constants; pub mod converters; pub mod cryptos; pub mod logger; pub mod node; pub mod parse_email; +pub mod proof; pub use circuit::*; +pub use command_templates::*; pub(crate) use constants::*; pub use converters::*; pub use cryptos::*; pub use logger::*; pub(crate) use node::*; pub use parse_email::*; +pub use proof::*; pub use neon::{context::ModuleContext, result::NeonResult}; pub use zk_regex_apis::extract_substrs::*; diff --git a/src/proof.rs b/src/proof.rs new file mode 100644 index 0000000..477df6b --- /dev/null +++ b/src/proof.rs @@ -0,0 +1,93 @@ +#![allow(clippy::upper_case_acronyms)] +#![allow(clippy::identity_op)] + +use anyhow::Result; +use ethers::abi::{self, Token}; +use ethers::types::{Bytes, U256}; + +use ::serde::Deserialize; + +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; + +use crate::{field_to_hex, hex_to_field, AccountCode, AccountSalt, PaddedEmailAddr}; + +#[derive(Debug, Clone, Deserialize)] +pub struct ProverRes { + proof: ProofJson, + pub_signals: Vec, +} + +#[derive(Debug, Clone, Deserialize)] +pub struct ProofJson { + pi_a: Vec, + pi_b: Vec>, + pi_c: Vec, +} + +impl ProofJson { + pub fn to_eth_bytes(&self) -> Result { + let pi_a = Token::FixedArray(vec![ + Token::Uint(U256::from_dec_str(self.pi_a[0].as_str())?), + Token::Uint(U256::from_dec_str(self.pi_a[1].as_str())?), + ]); + let pi_b = Token::FixedArray(vec![ + Token::FixedArray(vec![ + Token::Uint(U256::from_dec_str(self.pi_b[0][1].as_str())?), + Token::Uint(U256::from_dec_str(self.pi_b[0][0].as_str())?), + ]), + Token::FixedArray(vec![ + Token::Uint(U256::from_dec_str(self.pi_b[1][1].as_str())?), + Token::Uint(U256::from_dec_str(self.pi_b[1][0].as_str())?), + ]), + ]); + let pi_c = Token::FixedArray(vec![ + Token::Uint(U256::from_dec_str(self.pi_c[0].as_str())?), + Token::Uint(U256::from_dec_str(self.pi_c[1].as_str())?), + ]); + Ok(Bytes::from(abi::encode(&[pi_a, pi_b, pi_c]))) + } +} + +pub async fn generate_proof( + input: &str, + request: &str, + address: &str, +) -> Result<(Bytes, Vec)> { + let client = reqwest::Client::new(); + let res = client + .post(format!("{}/prove/{}", address, request)) + .json(&serde_json::json!({ "input": input })) + .send() + .await? + .error_for_status()?; + let res_json = res.json::().await?; + let proof = res_json.proof.to_eth_bytes()?; + let pub_signals = res_json + .pub_signals + .into_iter() + .map(|str| U256::from_dec_str(&str).expect("pub signal should be u256")) + .collect(); + Ok((proof, pub_signals)) +} + +pub fn calculate_default_hash(input: &str) -> String { + let mut hasher = DefaultHasher::new(); + input.hash(&mut hasher); + let hash_code = hasher.finish(); + + hash_code.to_string() +} + +pub fn calculate_account_salt(email_addr: &str, account_code: &str) -> String { + let padded_email_addr = PaddedEmailAddr::from_email_addr(email_addr); + let account_code = if account_code.starts_with("0x") { + hex_to_field(account_code).unwrap() + } else { + hex_to_field(&format!("0x{}", account_code)).unwrap() + }; + let account_code = AccountCode::from(account_code); + let account_salt = AccountSalt::new(&padded_email_addr, account_code).unwrap(); + + field_to_hex(&account_salt.0) +} From 8f38e8dced2644988bae85ec7a21379a6d527e59 Mon Sep 17 00:00:00 2001 From: Dimitri Date: Mon, 26 Aug 2024 12:40:04 +0100 Subject: [PATCH 2/6] Add email headers to ParsedEmail struct --- Cargo.lock | 1 + Cargo.toml | 1 + src/parse_email.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index f9cd40f..5620e00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2937,6 +2937,7 @@ dependencies = [ "hmac-sha256", "itertools 0.10.5", "lazy_static", + "mailparse", "neon", "num-bigint", "num-traits", diff --git a/Cargo.toml b/Cargo.toml index 09a3771..d045d3f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ slog-json = "2.6.1" lazy_static = "1.4" file-rotate = "0.7.5" regex = "1.10.6" +mailparse = "0.15.0" [dependencies.neon] version = "0.10" diff --git a/src/parse_email.rs b/src/parse_email.rs index 3436492..31a80d4 100644 --- a/src/parse_email.rs +++ b/src/parse_email.rs @@ -4,6 +4,8 @@ use anyhow::Result; use cfdkim::{canonicalize_signed_email, resolve_public_key}; use hex; use itertools::Itertools; +use mailparse::{parse_mail, ParsedMail}; +use neon::prelude::*; use rsa::traits::PublicKeyParts; use serde::{Deserialize, Serialize}; use zk_regex_apis::extract_substrs::{ @@ -20,6 +22,11 @@ pub struct ParsedEmail { pub signature: Vec, // The email signature bytes. pub public_key: Vec, // The public key bytes associated with the email. pub cleaned_body: String, // The cleaned email body. + pub canonicalized_header: String, + pub canonicalized_body: String, + pub signature: Vec, + pub public_key: Vec, + pub headers: EmailHeaders, } impl ParsedEmail { @@ -59,6 +66,16 @@ impl ParsedEmail { cleaned_body: String::from_utf8(remove_quoted_printable_soft_breaks( canonicalized_body, ))?, // Remove quoted-printable soft breaks from the canonicalized body. + canonicalize_signed_email(raw_email.as_bytes()).unwrap(); + // Extract all headers + let parsed_mail = parse_mail(raw_email.as_bytes())?; + let headers: EmailHeaders = EmailHeaders::new_from_mail(&parsed_mail); + let parsed_email = ParsedEmail { + canonicalized_header: String::from_utf8(canonicalized_header)?, + canonicalized_body: String::from_utf8(canonicalized_body)?, + signature: signature_bytes.into_iter().collect_vec(), + public_key: public_key.n().to_bytes_be(), + headers, }; Ok(parsed_email) @@ -215,6 +232,54 @@ impl ParsedEmail { Ok(idxes) => { let str = self.canonicalized_body[idxes[0].0..idxes[0].1].to_string(); Ok(str.replace("=\r\n", "")) +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct EmailHeaders(Arc>>); + +impl EmailHeaders { + pub fn new_from_mail(parsed_mail: &ParsedMail) -> Self { + let mut headers = HashMap::new(); + for header in &parsed_mail.headers { + let key = header.get_key().to_string(); + let value = header.get_value(); + headers.entry(key).or_insert_with(Vec::new).push(value); + } + Self(Arc::new(headers)) // Wrap the HashMap in Arc and then in EmailHeaders + } + + pub fn get_header(&self, name: &str) -> Option> { + self.0.get(name).cloned() + } +} + +pub fn parse_email_node(mut cx: FunctionContext) -> JsResult { + let raw_email = cx.argument::(0)?.value(&mut cx); + let channel = cx.channel(); + let (deferred, promise) = cx.promise(); + let rt = runtime(&mut cx)?; + + rt.spawn(async move { + let parsed_email = ParsedEmail::new_from_raw_email(&raw_email).await; + deferred.settle_with(&channel, move |mut cx| { + match parsed_email { + // Resolve the promise with the release date + Ok(parsed_email) => { + let signature_str = parsed_email.signature_string(); + let public_key_str = parsed_email.public_key_string(); + let obj = cx.empty_object(); + let canonicalized_header = cx.string(parsed_email.canonicalized_header); + obj.set(&mut cx, "canonicalizedHeader", canonicalized_header)?; + // let signed_header = cx.string( + // "0x".to_string() + hex::encode(parsed_email.signed_header).as_str(), + // ); + // obj.set(&mut cx, "signedHeader", signed_header)?; + let signature = cx.string(&signature_str); + obj.set(&mut cx, "signature", signature)?; + + let public_key = cx.string(&public_key_str); + obj.set(&mut cx, "publicKey", public_key)?; + // let dkim_domain = cx.string(&parsed_email.dkim_domain); + // obj.set(&mut cx, "dkimDomain", dkim_domain)?; + Ok(obj) } Err(_) => match extract_substr_idxes(&self.cleaned_body, ®ex_config) { Ok(idxes) => { From 0c196312b6ce7ce2c282d11e6c736e958b8df77a Mon Sep 17 00:00:00 2001 From: Dimitri Date: Thu, 19 Sep 2024 12:15:42 +0700 Subject: [PATCH 3/6] Add email headers to ParsedEmail --- src/parse_email.rs | 86 +++++++++++++--------------------------------- 1 file changed, 24 insertions(+), 62 deletions(-) diff --git a/src/parse_email.rs b/src/parse_email.rs index 31a80d4..f85b424 100644 --- a/src/parse_email.rs +++ b/src/parse_email.rs @@ -1,11 +1,12 @@ //! This module contains the `ParsedEmail` struct and its implementation. +use std::collections::HashMap; + use anyhow::Result; use cfdkim::{canonicalize_signed_email, resolve_public_key}; use hex; use itertools::Itertools; use mailparse::{parse_mail, ParsedMail}; -use neon::prelude::*; use rsa::traits::PublicKeyParts; use serde::{Deserialize, Serialize}; use zk_regex_apis::extract_substrs::{ @@ -22,10 +23,6 @@ pub struct ParsedEmail { pub signature: Vec, // The email signature bytes. pub public_key: Vec, // The public key bytes associated with the email. pub cleaned_body: String, // The cleaned email body. - pub canonicalized_header: String, - pub canonicalized_body: String, - pub signature: Vec, - pub public_key: Vec, pub headers: EmailHeaders, } @@ -57,6 +54,9 @@ impl ParsedEmail { let (canonicalized_header, canonicalized_body, signature_bytes) = canonicalize_signed_email(raw_email.as_bytes())?; + // Extract all headers + let parsed_mail = parse_mail(raw_email.as_bytes())?; + let headers: EmailHeaders = EmailHeaders::new_from_mail(&parsed_mail); // Construct the `ParsedEmail` instance. let parsed_email = ParsedEmail { canonicalized_header: String::from_utf8(canonicalized_header)?, // Convert bytes to string, may return an error if not valid UTF-8. @@ -66,15 +66,6 @@ impl ParsedEmail { cleaned_body: String::from_utf8(remove_quoted_printable_soft_breaks( canonicalized_body, ))?, // Remove quoted-printable soft breaks from the canonicalized body. - canonicalize_signed_email(raw_email.as_bytes()).unwrap(); - // Extract all headers - let parsed_mail = parse_mail(raw_email.as_bytes())?; - let headers: EmailHeaders = EmailHeaders::new_from_mail(&parsed_mail); - let parsed_email = ParsedEmail { - canonicalized_header: String::from_utf8(canonicalized_header)?, - canonicalized_body: String::from_utf8(canonicalized_body)?, - signature: signature_bytes.into_iter().collect_vec(), - public_key: public_key.n().to_bytes_be(), headers, }; @@ -232,54 +223,6 @@ impl ParsedEmail { Ok(idxes) => { let str = self.canonicalized_body[idxes[0].0..idxes[0].1].to_string(); Ok(str.replace("=\r\n", "")) -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct EmailHeaders(Arc>>); - -impl EmailHeaders { - pub fn new_from_mail(parsed_mail: &ParsedMail) -> Self { - let mut headers = HashMap::new(); - for header in &parsed_mail.headers { - let key = header.get_key().to_string(); - let value = header.get_value(); - headers.entry(key).or_insert_with(Vec::new).push(value); - } - Self(Arc::new(headers)) // Wrap the HashMap in Arc and then in EmailHeaders - } - - pub fn get_header(&self, name: &str) -> Option> { - self.0.get(name).cloned() - } -} - -pub fn parse_email_node(mut cx: FunctionContext) -> JsResult { - let raw_email = cx.argument::(0)?.value(&mut cx); - let channel = cx.channel(); - let (deferred, promise) = cx.promise(); - let rt = runtime(&mut cx)?; - - rt.spawn(async move { - let parsed_email = ParsedEmail::new_from_raw_email(&raw_email).await; - deferred.settle_with(&channel, move |mut cx| { - match parsed_email { - // Resolve the promise with the release date - Ok(parsed_email) => { - let signature_str = parsed_email.signature_string(); - let public_key_str = parsed_email.public_key_string(); - let obj = cx.empty_object(); - let canonicalized_header = cx.string(parsed_email.canonicalized_header); - obj.set(&mut cx, "canonicalizedHeader", canonicalized_header)?; - // let signed_header = cx.string( - // "0x".to_string() + hex::encode(parsed_email.signed_header).as_str(), - // ); - // obj.set(&mut cx, "signedHeader", signed_header)?; - let signature = cx.string(&signature_str); - obj.set(&mut cx, "signature", signature)?; - - let public_key = cx.string(&public_key_str); - obj.set(&mut cx, "publicKey", public_key)?; - // let dkim_domain = cx.string(&parsed_email.dkim_domain); - // obj.set(&mut cx, "dkimDomain", dkim_domain)?; - Ok(obj) } Err(_) => match extract_substr_idxes(&self.cleaned_body, ®ex_config) { Ok(idxes) => { @@ -362,3 +305,22 @@ pub(crate) fn find_index_in_body(body: Option<&Vec>, pattern: &str) -> usize }) .unwrap_or(0) // Default to 0 if not found or pattern is empty } + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct EmailHeaders(HashMap>); + +impl EmailHeaders { + pub fn new_from_mail(parsed_mail: &ParsedMail) -> Self { + let mut headers = HashMap::new(); + for header in &parsed_mail.headers { + let key = header.get_key().to_string(); + let value = header.get_value(); + headers.entry(key).or_insert_with(Vec::new).push(value); + } + Self(headers) + } + + pub fn get_header(&self, name: &str) -> Option> { + self.0.get(name).cloned() + } +} From 45947363f3778e83dea6ad3050edbe3a1de54982 Mon Sep 17 00:00:00 2001 From: Dimitri Date: Thu, 19 Sep 2024 13:33:36 +0700 Subject: [PATCH 4/6] Add AI generated comments --- src/command_templates.rs | 66 ++++++++++++++++++++++++++++++++++++++-- src/parse_email.rs | 43 +++++++++++++++++++++----- src/proof.rs | 64 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 10 deletions(-) diff --git a/src/command_templates.rs b/src/command_templates.rs index af7cb23..637d1b3 100644 --- a/src/command_templates.rs +++ b/src/command_templates.rs @@ -12,17 +12,33 @@ const INT_REGEX: &str = r"-?\d+"; const ETH_ADDR_REGEX: &str = r"0x[a-fA-F0-9]{40}"; const DECIMALS_REGEX: &str = r"\d+\.\d+"; +/// Represents different types of template values. #[derive(Debug, Clone, Serialize, Deserialize)] pub enum TemplateValue { + /// A string value. String(String), + /// An unsigned integer value. Uint(U256), + /// A signed integer value. Int(I256), + /// A decimal value represented as a string. Decimals(String), + /// An Ethereum address. EthAddr(Address), + /// A fixed value represented as a string. Fixed(String), } impl TemplateValue { + /// Encodes the template value into ABI format. + /// + /// # Arguments + /// + /// * `decimal_size` - An optional value specifying the number of decimal places for Decimals type. + /// + /// # Returns + /// + /// A `Result` containing the encoded bytes or an error. pub fn abi_encode(&self, decimal_size: Option) -> Result { match self { Self::String(string) => Ok(Bytes::from(abi::encode(&[Token::String(string.clone())]))), @@ -36,7 +52,17 @@ impl TemplateValue { } } - pub fn decimals_str_to_uint(str: &str, decimal_size: u8) -> U256 { + /// Converts a decimal string to a U256 integer. + /// + /// # Arguments + /// + /// * `str` - The decimal string to convert. + /// * `decimal_size` - The number of decimal places. + /// + /// # Returns + /// + /// A `U256` representing the decimal value. + fn decimals_str_to_uint(str: &str, decimal_size: u8) -> U256 { let decimal_size = decimal_size as usize; let dot = Regex::new("\\.").unwrap().find(str); let (before_dot_str, mut after_dot_str) = match dot { @@ -54,6 +80,16 @@ impl TemplateValue { } } +/// Extracts template values from a command input string. +/// +/// # Arguments +/// +/// * `input` - The input string to extract values from. +/// * `templates` - A vector of template strings. +/// +/// # Returns +/// +/// A `Result` containing a vector of `TemplateValue`s or an error. pub fn extract_template_vals_from_command( input: &str, templates: Vec, @@ -88,13 +124,24 @@ pub fn extract_template_vals_from_command( } } -pub fn extract_template_vals(input: &str, templates: Vec) -> Result> { +/// Extracts template values from an input string. +/// +/// # Arguments +/// +/// * `input` - The input string to extract values from. +/// * `templates` - A vector of template strings. +/// +/// # Returns +/// +/// A `Result` containing a vector of `TemplateValue`s or an error. +fn extract_template_vals(input: &str, templates: Vec) -> Result> { let input_decomposed: Vec<&str> = input.split_whitespace().collect(); let mut template_vals = Vec::new(); for (input_idx, template) in templates.iter().enumerate() { match template.as_str() { "{string}" => { + // Extract and validate string value let string_match = Regex::new(STRING_REGEX) .unwrap() .find(input_decomposed[input_idx]) @@ -109,6 +156,7 @@ pub fn extract_template_vals(input: &str, templates: Vec) -> Result { + // Extract and validate unsigned integer value let uint_match = Regex::new(UINT_REGEX) .unwrap() .find(input_decomposed[input_idx]) @@ -125,6 +173,7 @@ pub fn extract_template_vals(input: &str, templates: Vec) -> Result { + // Extract and validate signed integer value let int_match = Regex::new(INT_REGEX) .unwrap() .find(input_decomposed[input_idx]) @@ -140,6 +189,7 @@ pub fn extract_template_vals(input: &str, templates: Vec) -> Result { + // Extract and validate decimal value let decimals_match = Regex::new(DECIMALS_REGEX) .unwrap() .find(input_decomposed[input_idx]) @@ -156,6 +206,7 @@ pub fn extract_template_vals(input: &str, templates: Vec) -> Result { + // Extract and validate Ethereum address let address_match = Regex::new(ETH_ADDR_REGEX) .unwrap() .find(input_decomposed[input_idx]) @@ -173,7 +224,16 @@ pub fn extract_template_vals(input: &str, templates: Vec) -> Result String { // Convert amount to string in wei format (no decimals) let uint_str = uint.to_string(); diff --git a/src/parse_email.rs b/src/parse_email.rs index f85b424..66f8be6 100644 --- a/src/parse_email.rs +++ b/src/parse_email.rs @@ -15,14 +15,20 @@ use zk_regex_apis::extract_substrs::{ extract_substr_idxes, extract_timestamp_idxes, extract_to_addr_idxes, }; -#[derive(Debug, Clone, Serialize, Deserialize)] /// `ParsedEmail` holds the canonicalized parts of an email along with its signature and public key. +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ParsedEmail { - pub canonicalized_header: String, // The canonicalized email header. - pub canonicalized_body: String, // The canonicalized email body. - pub signature: Vec, // The email signature bytes. - pub public_key: Vec, // The public key bytes associated with the email. - pub cleaned_body: String, // The cleaned email body. + /// The canonicalized email header. + pub canonicalized_header: String, + /// The canonicalized email body. + pub canonicalized_body: String, + /// The email signature bytes. + pub signature: Vec, + /// The public key bytes associated with the email. + pub public_key: Vec, + /// The cleaned email body. + pub cleaned_body: String, + /// The email headers. pub headers: EmailHeaders, } @@ -57,6 +63,7 @@ impl ParsedEmail { // Extract all headers let parsed_mail = parse_mail(raw_email.as_bytes())?; let headers: EmailHeaders = EmailHeaders::new_from_mail(&parsed_mail); + // Construct the `ParsedEmail` instance. let parsed_email = ParsedEmail { canonicalized_header: String::from_utf8(canonicalized_header)?, // Convert bytes to string, may return an error if not valid UTF-8. @@ -270,12 +277,14 @@ pub(crate) fn remove_quoted_printable_soft_breaks(body: Vec) -> Vec { while let Some((i, &byte)) = iter.next() { if byte == b'=' && body.get(i + 1..i + 3) == Some(&[b'\r', b'\n']) { - iter.nth(1); // Skip the next two bytes + // Skip the next two bytes (soft line break) + iter.nth(1); } else { result.push(byte); } } + // Resize the result to match the original body length result.resize(body.len(), 0); result } @@ -296,6 +305,7 @@ pub(crate) fn remove_quoted_printable_soft_breaks(body: Vec) -> Vec { pub(crate) fn find_index_in_body(body: Option<&Vec>, pattern: &str) -> usize { body.and_then(|body_bytes| { if !pattern.is_empty() { + // Search for the pattern in the body body_bytes .windows(pattern.len()) .position(|w| w == pattern.as_bytes()) @@ -306,10 +316,20 @@ pub(crate) fn find_index_in_body(body: Option<&Vec>, pattern: &str) -> usize .unwrap_or(0) // Default to 0 if not found or pattern is empty } +/// Represents the email headers as a collection of key-value pairs. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EmailHeaders(HashMap>); impl EmailHeaders { + /// Creates a new `EmailHeaders` instance from a parsed email. + /// + /// # Arguments + /// + /// * `parsed_mail` - A reference to a `ParsedMail` instance. + /// + /// # Returns + /// + /// A new `EmailHeaders` instance containing the headers from the parsed email. pub fn new_from_mail(parsed_mail: &ParsedMail) -> Self { let mut headers = HashMap::new(); for header in &parsed_mail.headers { @@ -320,6 +340,15 @@ impl EmailHeaders { Self(headers) } + /// Retrieves the value(s) of a specific header. + /// + /// # Arguments + /// + /// * `name` - The name of the header to retrieve. + /// + /// # Returns + /// + /// An `Option` containing a `Vec` of header values if the header exists, or `None` if it doesn't. pub fn get_header(&self, name: &str) -> Option> { self.0.get(name).cloned() } diff --git a/src/proof.rs b/src/proof.rs index 477df6b..74e31bf 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -12,25 +12,40 @@ use std::hash::{Hash, Hasher}; use crate::{field_to_hex, hex_to_field, AccountCode, AccountSalt, PaddedEmailAddr}; +/// Represents the response from the prover. #[derive(Debug, Clone, Deserialize)] pub struct ProverRes { + /// The proof in JSON format. proof: ProofJson, + /// The public signals associated with the proof. pub_signals: Vec, } +/// Represents the proof in JSON format. #[derive(Debug, Clone, Deserialize)] pub struct ProofJson { + /// The pi_a component of the proof. pi_a: Vec, + /// The pi_b component of the proof. pi_b: Vec>, + /// The pi_c component of the proof. pi_c: Vec, } impl ProofJson { + /// Converts the proof to Ethereum-compatible bytes. + /// + /// # Returns + /// + /// A `Result` containing the Ethereum-compatible bytes or an error. pub fn to_eth_bytes(&self) -> Result { + // Convert pi_a to Token::FixedArray let pi_a = Token::FixedArray(vec![ Token::Uint(U256::from_dec_str(self.pi_a[0].as_str())?), Token::Uint(U256::from_dec_str(self.pi_a[1].as_str())?), ]); + + // Convert pi_b to nested Token::FixedArray let pi_b = Token::FixedArray(vec![ Token::FixedArray(vec![ Token::Uint(U256::from_dec_str(self.pi_b[0][1].as_str())?), @@ -41,36 +56,69 @@ impl ProofJson { Token::Uint(U256::from_dec_str(self.pi_b[1][0].as_str())?), ]), ]); + + // Convert pi_c to Token::FixedArray let pi_c = Token::FixedArray(vec![ Token::Uint(U256::from_dec_str(self.pi_c[0].as_str())?), Token::Uint(U256::from_dec_str(self.pi_c[1].as_str())?), ]); + + // Encode the tokens and return as Bytes Ok(Bytes::from(abi::encode(&[pi_a, pi_b, pi_c]))) } } +/// Generates a proof for the given input. +/// +/// # Arguments +/// +/// * `input` - The input string for proof generation. +/// * `request` - The request string. +/// * `address` - The address string. +/// +/// # Returns +/// +/// A `Result` containing a tuple of `Bytes` (the proof) and `Vec` (public signals) or an error. pub async fn generate_proof( input: &str, request: &str, address: &str, ) -> Result<(Bytes, Vec)> { let client = reqwest::Client::new(); + + // Send POST request to the prover let res = client .post(format!("{}/prove/{}", address, request)) .json(&serde_json::json!({ "input": input })) .send() .await? .error_for_status()?; + + // Parse the response JSON let res_json = res.json::().await?; + + // Convert the proof to Ethereum-compatible bytes let proof = res_json.proof.to_eth_bytes()?; + + // Convert public signals to U256 let pub_signals = res_json .pub_signals .into_iter() .map(|str| U256::from_dec_str(&str).expect("pub signal should be u256")) .collect(); + Ok((proof, pub_signals)) } +/// Calculates a default hash for the given input string. +/// +/// # Arguments +/// +/// * `input` - The input string to hash. +/// +/// # Returns +/// +/// A string representation of the calculated hash. pub fn calculate_default_hash(input: &str) -> String { let mut hasher = DefaultHasher::new(); input.hash(&mut hasher); @@ -79,15 +127,31 @@ pub fn calculate_default_hash(input: &str) -> String { hash_code.to_string() } +/// Calculates the account salt based on the email address and account code. +/// +/// # Arguments +/// +/// * `email_addr` - The email address string. +/// * `account_code` - The account code string. +/// +/// # Returns +/// +/// A string representation of the calculated account salt. pub fn calculate_account_salt(email_addr: &str, account_code: &str) -> String { + // Pad the email address let padded_email_addr = PaddedEmailAddr::from_email_addr(email_addr); + + // Convert account code to field element let account_code = if account_code.starts_with("0x") { hex_to_field(account_code).unwrap() } else { hex_to_field(&format!("0x{}", account_code)).unwrap() }; let account_code = AccountCode::from(account_code); + + // Generate account salt let account_salt = AccountSalt::new(&padded_email_addr, account_code).unwrap(); + // Convert account salt to hexadecimal representation field_to_hex(&account_salt.0) } From 6fb85e6b867165a5deb2fe877bc81827b6bf001e Mon Sep 17 00:00:00 2001 From: Dimitri Date: Thu, 19 Sep 2024 14:10:27 +0700 Subject: [PATCH 5/6] Move functions from command_template --- src/command_templates.rs | 76 ---------------------------------------- src/converters.rs | 76 ++++++++++++++++++++++++++++++++++++++++ src/cryptos.rs | 52 +++++++++++++++++++++++++-- src/proof.rs | 51 --------------------------- 4 files changed, 126 insertions(+), 129 deletions(-) diff --git a/src/command_templates.rs b/src/command_templates.rs index 637d1b3..7fb3ff4 100644 --- a/src/command_templates.rs +++ b/src/command_templates.rs @@ -223,79 +223,3 @@ fn extract_template_vals(input: &str, templates: Vec) -> Result String { - // Convert amount to string in wei format (no decimals) - let uint_str = uint.to_string(); - let uint_length = uint_str.len(); - - // Create result vector with max length - // If less than 18 decimals, then 2 extra for "0.", otherwise one extra for "." - let mut result = vec![ - '0'; - if uint_length > decimal { - uint_length + 1 - } else { - decimal + 2 - } - ]; - let result_length = result.len(); - - // Difference between result and amount array index when copying - // If more than 18, then 1 index diff for ".", otherwise actual diff in length - let mut delta = if uint_length > decimal { - 1 - } else { - result_length - uint_length - }; - - // Boolean to indicate if we found a non-zero digit when scanning from last to first index - let mut found_non_zero_decimal = false; - - let mut actual_result_len = 0; - - // In each iteration we fill one index of result array (starting from end) - for i in (0..result_length).rev() { - // Check if we have reached the index where we need to add decimal point - if i == result_length - decimal - 1 { - // No need to add "." if there was no value in decimal places - if found_non_zero_decimal { - result[i] = '.'; - actual_result_len += 1; - } - // Set delta to 0, as we have already added decimal point (only for amount_length > 18) - delta = 0; - } - // If amountLength < 18 and we have copied everything, fill zeros - else if uint_length <= decimal && i < result_length - uint_length { - result[i] = '0'; - actual_result_len += 1; - } - // If non-zero decimal is found, or decimal point inserted (delta == 0), copy from amount array - else if found_non_zero_decimal || delta == 0 { - result[i] = uint_str.chars().nth(i - delta).unwrap(); - actual_result_len += 1; - } - // If we find non-zero decimal for the first time (trailing zeros are skipped) - else if uint_str.chars().nth(i - delta).unwrap() != '0' { - result[i] = uint_str.chars().nth(i - delta).unwrap(); - actual_result_len += 1; - found_non_zero_decimal = true; - } - } - - // Create final result string with correct length - let compact_result: String = result.into_iter().take(actual_result_len).collect(); - - compact_result -} diff --git a/src/converters.rs b/src/converters.rs index 3f588f5..44dfa98 100644 --- a/src/converters.rs +++ b/src/converters.rs @@ -351,3 +351,79 @@ pub fn u256_to_bytes32_little(x: &U256) -> [u8; 32] { x.to_little_endian(&mut bytes); bytes } + +/// Converts an unsigned integer to a decimal string representation. +/// +/// # Arguments +/// +/// * `uint` - The unsigned integer to convert. +/// * `decimal` - The number of decimal places to use. +/// +/// # Returns +/// +/// A string representation of the decimal value. +pub fn uint_to_decimal_string(uint: u128, decimal: usize) -> String { + // Convert amount to string in wei format (no decimals) + let uint_str = uint.to_string(); + let uint_length = uint_str.len(); + + // Create result vector with max length + // If less than 18 decimals, then 2 extra for "0.", otherwise one extra for "." + let mut result = vec![ + '0'; + if uint_length > decimal { + uint_length + 1 + } else { + decimal + 2 + } + ]; + let result_length = result.len(); + + // Difference between result and amount array index when copying + // If more than 18, then 1 index diff for ".", otherwise actual diff in length + let mut delta = if uint_length > decimal { + 1 + } else { + result_length - uint_length + }; + + // Boolean to indicate if we found a non-zero digit when scanning from last to first index + let mut found_non_zero_decimal = false; + + let mut actual_result_len = 0; + + // In each iteration we fill one index of result array (starting from end) + for i in (0..result_length).rev() { + // Check if we have reached the index where we need to add decimal point + if i == result_length - decimal - 1 { + // No need to add "." if there was no value in decimal places + if found_non_zero_decimal { + result[i] = '.'; + actual_result_len += 1; + } + // Set delta to 0, as we have already added decimal point (only for amount_length > 18) + delta = 0; + } + // If amountLength < 18 and we have copied everything, fill zeros + else if uint_length <= decimal && i < result_length - uint_length { + result[i] = '0'; + actual_result_len += 1; + } + // If non-zero decimal is found, or decimal point inserted (delta == 0), copy from amount array + else if found_non_zero_decimal || delta == 0 { + result[i] = uint_str.chars().nth(i - delta).unwrap(); + actual_result_len += 1; + } + // If we find non-zero decimal for the first time (trailing zeros are skipped) + else if uint_str.chars().nth(i - delta).unwrap() != '0' { + result[i] = uint_str.chars().nth(i - delta).unwrap(); + actual_result_len += 1; + found_non_zero_decimal = true; + } + } + + // Create final result string with correct length + let compact_result: String = result.into_iter().take(actual_result_len).collect(); + + compact_result +} diff --git a/src/cryptos.rs b/src/cryptos.rs index 78d6fea..346255a 100644 --- a/src/cryptos.rs +++ b/src/cryptos.rs @@ -1,11 +1,13 @@ //! Cryptographic functions. +use crate::{field_to_hex, hex_to_field}; use ethers::types::Bytes; use halo2curves::ff::Field; -use hmac_sha256::Hash; use poseidon_rs::{poseidon_bytes, poseidon_fields, Fr, PoseidonError}; use rand_core::RngCore; +use std::collections::hash_map::DefaultHasher; use std::error::Error; +use std::hash::{Hash, Hasher}; use zk_regex_apis::padding::pad_string; use crate::{ @@ -312,7 +314,7 @@ pub fn sha256_pad(mut data: Vec, max_sha_bytes: usize) -> (Vec, usize) { /// /// A vector containing the SHA-256 hash of the message. pub fn partial_sha(msg: &[u8], msg_len: usize) -> Vec { - let mut hasher = Hash::new(); + let mut hasher = hmac_sha256::Hash::new(); hasher.update(&msg[..msg_len]); let result = hasher.cache_state(); result.to_vec() @@ -436,3 +438,49 @@ mod tests { assert_eq!(field_to_hex(&hash_field), expected_hash); } } + +/// Calculates a default hash for the given input string. +/// +/// # Arguments +/// +/// * `input` - The input string to hash. +/// +/// # Returns +/// +/// A string representation of the calculated hash. +pub fn calculate_default_hash(input: &str) -> String { + let mut hasher = DefaultHasher::new(); + input.hash(&mut hasher); + let hash_code = hasher.finish(); + + hash_code.to_string() +} + +/// Calculates the account salt based on the email address and account code. +/// +/// # Arguments +/// +/// * `email_addr` - The email address string. +/// * `account_code` - The account code string. +/// +/// # Returns +/// +/// A string representation of the calculated account salt. +pub fn calculate_account_salt(email_addr: &str, account_code: &str) -> String { + // Pad the email address + let padded_email_addr = PaddedEmailAddr::from_email_addr(email_addr); + + // Convert account code to field element + let account_code = if account_code.starts_with("0x") { + hex_to_field(account_code).unwrap() + } else { + hex_to_field(&format!("0x{}", account_code)).unwrap() + }; + let account_code = AccountCode::from(account_code); + + // Generate account salt + let account_salt = AccountSalt::new(&padded_email_addr, account_code).unwrap(); + + // Convert account salt to hexadecimal representation + field_to_hex(&account_salt.0) +} diff --git a/src/proof.rs b/src/proof.rs index 74e31bf..0efe046 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -7,11 +7,6 @@ use ethers::types::{Bytes, U256}; use ::serde::Deserialize; -use std::collections::hash_map::DefaultHasher; -use std::hash::{Hash, Hasher}; - -use crate::{field_to_hex, hex_to_field, AccountCode, AccountSalt, PaddedEmailAddr}; - /// Represents the response from the prover. #[derive(Debug, Clone, Deserialize)] pub struct ProverRes { @@ -109,49 +104,3 @@ pub async fn generate_proof( Ok((proof, pub_signals)) } - -/// Calculates a default hash for the given input string. -/// -/// # Arguments -/// -/// * `input` - The input string to hash. -/// -/// # Returns -/// -/// A string representation of the calculated hash. -pub fn calculate_default_hash(input: &str) -> String { - let mut hasher = DefaultHasher::new(); - input.hash(&mut hasher); - let hash_code = hasher.finish(); - - hash_code.to_string() -} - -/// Calculates the account salt based on the email address and account code. -/// -/// # Arguments -/// -/// * `email_addr` - The email address string. -/// * `account_code` - The account code string. -/// -/// # Returns -/// -/// A string representation of the calculated account salt. -pub fn calculate_account_salt(email_addr: &str, account_code: &str) -> String { - // Pad the email address - let padded_email_addr = PaddedEmailAddr::from_email_addr(email_addr); - - // Convert account code to field element - let account_code = if account_code.starts_with("0x") { - hex_to_field(account_code).unwrap() - } else { - hex_to_field(&format!("0x{}", account_code)).unwrap() - }; - let account_code = AccountCode::from(account_code); - - // Generate account salt - let account_salt = AccountSalt::new(&padded_email_addr, account_code).unwrap(); - - // Convert account salt to hexadecimal representation - field_to_hex(&account_salt.0) -} From 8750d1810707bb6509c7dd0a95084ecc589d8898 Mon Sep 17 00:00:00 2001 From: Aditya Bisht Date: Wed, 9 Oct 2024 12:52:39 +0530 Subject: [PATCH 6/6] fix: build --- src/cryptos.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cryptos.rs b/src/cryptos.rs index 999736b..883ab30 100644 --- a/src/cryptos.rs +++ b/src/cryptos.rs @@ -5,23 +5,23 @@ use ethers::types::Bytes; use halo2curves::ff::Field; use poseidon_rs::{poseidon_bytes, poseidon_fields, Fr, PoseidonError}; use rand_core::RngCore; +use serde::{ + de::{self, Visitor}, + Deserialize, Deserializer, +}; use std::{ collections::hash_map::DefaultHasher, error::Error, fmt, hash::{Hash, Hasher}, }; -use serde::{ - de::{self, Visitor}, - Deserialize, Deserializer, -}; use zk_regex_apis::padding::pad_string; use crate::{ converters::{ bytes_chunk_fields, bytes_to_fields, int64_to_bytes, int8_to_bytes, merge_u8_arrays, }, - hex_to_field, MAX_EMAIL_ADDR_BYTES, + MAX_EMAIL_ADDR_BYTES, }; type ShaResult = Vec; // The result of a SHA-256 hash operation.