From 9179252ec5d1cf6d564dbb5738ffbeae08c1bfde Mon Sep 17 00:00:00 2001 From: Joy Wang <108701016+joyqvq@users.noreply.github.com> Date: Tue, 26 Mar 2024 12:12:48 -0400 Subject: [PATCH] fix: Use strong type during deser --- Cargo.lock | 5 +- fastcrypto-zkp/Cargo.toml | 1 + fastcrypto-zkp/benches/zklogin.rs | 2 +- .../bn254/unit_tests/zk_login_e2e_tests.rs | 2 +- .../src/bn254/unit_tests/zk_login_tests.rs | 33 ++- fastcrypto-zkp/src/bn254/utils.rs | 31 +- fastcrypto-zkp/src/bn254/zk_login.rs | 27 +- fastcrypto-zkp/src/bn254/zk_login_api.rs | 264 ++++++++++++------ fastcrypto-zkp/src/circom.rs | 196 +++++++++++-- 9 files changed, 413 insertions(+), 148 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8bdd75e9be..fa476b08c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1541,6 +1541,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "thiserror", "tokio", "typenum", ] @@ -2381,9 +2382,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", "libm", diff --git a/fastcrypto-zkp/Cargo.toml b/fastcrypto-zkp/Cargo.toml index a526fab609..6feaeac719 100644 --- a/fastcrypto-zkp/Cargo.toml +++ b/fastcrypto-zkp/Cargo.toml @@ -45,6 +45,7 @@ ff = { version = "0.13.0", features = ["derive"] } typenum = "1.13.0" lazy_static = "1.4.0" itertools = "0.12.0" +thiserror = "1.0.40" [dev-dependencies] ark-bls12-377 = "0.4.0" diff --git a/fastcrypto-zkp/benches/zklogin.rs b/fastcrypto-zkp/benches/zklogin.rs index 68c8cc892d..33f36fbf94 100644 --- a/fastcrypto-zkp/benches/zklogin.rs +++ b/fastcrypto-zkp/benches/zklogin.rs @@ -40,7 +40,7 @@ mod zklogin_benches { "25769832374-famecqrhe2gkebt5fvqms2263046lj96.apps.googleusercontent.com", ) .unwrap(); - let input = ZkLoginInputs::from_json("{\"proofPoints\":{\"a\":[\"8247215875293406890829839156897863742504615191361518281091302475904551111016\",\"6872980335748205979379321982220498484242209225765686471076081944034292159666\",\"1\"],\"b\":[[\"21419680064642047510915171723230639588631899775315750803416713283740137406807\",\"21566716915562037737681888858382287035712341650647439119820808127161946325890\"],[\"17867714710686394159919998503724240212517838710399045289784307078087926404555\",\"21812769875502013113255155836896615164559280911997219958031852239645061854221\"],[\"1\",\"0\"]],\"c\":[\"7530826803702928198368421787278524256623871560746240215547076095911132653214\",\"16244547936249959771862454850485726883972969173921727256151991751860694123976\",\"1\"]},\"issBase64Details\":{\"value\":\"yJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLC\",\"indexMod4\":1},\"headerBase64\":\"eyJhbGciOiJSUzI1NiIsImtpZCI6IjZmNzI1NDEwMWY1NmU0MWNmMzVjOTkyNmRlODRhMmQ1NTJiNGM2ZjEiLCJ0eXAiOiJKV1QifQ\"}", &address_seed).unwrap(); + let input = ZkLoginInputs::from_json("{\"proofPoints\":{\"a\":[\"8247215875293406890829839156897863742504615191361518281091302475904551111016\",\"6872980335748205979379321982220498484242209225765686471076081944034292159666\",\"1\"],\"b\":[[\"21419680064642047510915171723230639588631899775315750803416713283740137406807\",\"21566716915562037737681888858382287035712341650647439119820808127161946325890\"],[\"17867714710686394159919998503724240212517838710399045289784307078087926404555\",\"21812769875502013113255155836896615164559280911997219958031852239645061854221\"],[\"1\",\"0\"]],\"c\":[\"7530826803702928198368421787278524256623871560746240215547076095911132653214\",\"16244547936249959771862454850485726883972969173921727256151991751860694123976\",\"1\"]},\"issBase64Details\":{\"value\":\"yJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLC\",\"indexMod4\":1},\"headerBase64\":\"eyJhbGciOiJSUzI1NiIsImtpZCI6IjZmNzI1NDEwMWY1NmU0MWNmMzVjOTkyNmRlODRhMmQ1NTJiNGM2ZjEiLCJ0eXAiOiJKV1QifQ\"}", &address_seed.to_string()).unwrap(); let kp = Ed25519KeyPair::generate(&mut StdRng::from_seed([0; 32])); let mut eph_pubkey = vec![0x00]; eph_pubkey.extend(kp.public().as_ref()); diff --git a/fastcrypto-zkp/src/bn254/unit_tests/zk_login_e2e_tests.rs b/fastcrypto-zkp/src/bn254/unit_tests/zk_login_e2e_tests.rs index 280372cb0d..3ac3e9e712 100644 --- a/fastcrypto-zkp/src/bn254/unit_tests/zk_login_e2e_tests.rs +++ b/fastcrypto-zkp/src/bn254/unit_tests/zk_login_e2e_tests.rs @@ -212,6 +212,6 @@ async fn get_test_inputs(parsed_token: &str) -> (u64, Vec, ZkLoginInputs) { let (sub, aud) = parse_and_validate_jwt(parsed_token).unwrap(); // Get the address seed. let address_seed = gen_address_seed(user_salt, "sub", &sub, &aud).unwrap(); - let zk_login_inputs = ZkLoginInputs::from_reader(reader, &address_seed).unwrap(); + let zk_login_inputs = ZkLoginInputs::from_reader(reader, &address_seed.to_string()).unwrap(); (max_epoch, eph_pubkey, zk_login_inputs) } diff --git a/fastcrypto-zkp/src/bn254/unit_tests/zk_login_tests.rs b/fastcrypto-zkp/src/bn254/unit_tests/zk_login_tests.rs index e4ea895961..710e2da548 100644 --- a/fastcrypto-zkp/src/bn254/unit_tests/zk_login_tests.rs +++ b/fastcrypto-zkp/src/bn254/unit_tests/zk_login_tests.rs @@ -5,14 +5,13 @@ use std::str::FromStr; use crate::bn254::poseidon::poseidon_zk_login; use crate::bn254::utils::{ - big_int_str_to_bytes, gen_address_seed, gen_address_seed_with_salt_hash, get_nonce, - get_zk_login_address, + gen_address_seed, gen_address_seed_with_salt_hash, get_nonce, get_zk_login_address, }; use crate::bn254::zk_login::big_int_array_to_bits; use crate::bn254::zk_login::bitarray_to_bytearray; use crate::bn254::zk_login::{ base64_to_bitarray, convert_base, decode_base64_url, hash_ascii_str_to_field, hash_to_field, - parse_jwks, to_field, trim, verify_extended_claim, Claim, JWTDetails, JwkId, + parse_jwks, parse_fr_field_element, trim, verify_extended_claim, Claim, JWTDetails, JwkId, }; use crate::bn254::zk_login::{fetch_jwks, OIDCProvider}; use crate::bn254::zk_login_api::ZkLoginEnv; @@ -21,6 +20,7 @@ use crate::bn254::{ zk_login::{ZkLoginInputs, JWK}, zk_login_api::verify_zk_login, }; +use crate::circom::Bn254FrElement; use ark_bn254::Fr; use ark_std::rand::rngs::StdRng; use ark_std::rand::SeedableRng; @@ -109,7 +109,7 @@ async fn test_verify_zk_login_google() { .unwrap(); // Get a proof from endpoint and serialize it. - let zk_login_inputs = ZkLoginInputs::from_json("{\"proofPoints\":{\"a\":[\"8247215875293406890829839156897863742504615191361518281091302475904551111016\",\"6872980335748205979379321982220498484242209225765686471076081944034292159666\",\"1\"],\"b\":[[\"21419680064642047510915171723230639588631899775315750803416713283740137406807\",\"21566716915562037737681888858382287035712341650647439119820808127161946325890\"],[\"17867714710686394159919998503724240212517838710399045289784307078087926404555\",\"21812769875502013113255155836896615164559280911997219958031852239645061854221\"],[\"1\",\"0\"]],\"c\":[\"7530826803702928198368421787278524256623871560746240215547076095911132653214\",\"16244547936249959771862454850485726883972969173921727256151991751860694123976\",\"1\"]},\"issBase64Details\":{\"value\":\"yJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLC\",\"indexMod4\":1},\"headerBase64\":\"eyJhbGciOiJSUzI1NiIsImtpZCI6IjZmNzI1NDEwMWY1NmU0MWNmMzVjOTkyNmRlODRhMmQ1NTJiNGM2ZjEiLCJ0eXAiOiJKV1QifQ\"}", &address_seed).unwrap(); + let zk_login_inputs = ZkLoginInputs::from_json("{\"proofPoints\":{\"a\":[\"8247215875293406890829839156897863742504615191361518281091302475904551111016\",\"6872980335748205979379321982220498484242209225765686471076081944034292159666\",\"1\"],\"b\":[[\"21419680064642047510915171723230639588631899775315750803416713283740137406807\",\"21566716915562037737681888858382287035712341650647439119820808127161946325890\"],[\"17867714710686394159919998503724240212517838710399045289784307078087926404555\",\"21812769875502013113255155836896615164559280911997219958031852239645061854221\"],[\"1\",\"0\"]],\"c\":[\"7530826803702928198368421787278524256623871560746240215547076095911132653214\",\"16244547936249959771862454850485726883972969173921727256151991751860694123976\",\"1\"]},\"issBase64Details\":{\"value\":\"yJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLC\",\"indexMod4\":1},\"headerBase64\":\"eyJhbGciOiJSUzI1NiIsImtpZCI6IjZmNzI1NDEwMWY1NmU0MWNmMzVjOTkyNmRlODRhMmQ1NTJiNGM2ZjEiLCJ0eXAiOiJKV1QifQ\"}", &address_seed.to_string()).unwrap(); assert_eq!( zk_login_inputs.get_kid(), "6f7254101f56e41cf35c9926de84a2d552b4c6f1".to_string() @@ -118,7 +118,10 @@ async fn test_verify_zk_login_google() { zk_login_inputs.get_iss(), OIDCProvider::Google.get_config().iss ); - assert_eq!(zk_login_inputs.get_address_seed(), address_seed); + assert_eq!( + zk_login_inputs.get_address_seed().to_string(), + address_seed.to_string() + ); assert_eq!( get_zk_login_address( zk_login_inputs.get_address_seed(), @@ -190,6 +193,11 @@ fn test_parse_jwt_details() { #[test] fn test_decode_base64() { + assert_eq!( + decode_base64_url("aa", &1).unwrap_err(), + FastCryptoError::GeneralError("Invalid UTF8 string".to_string()) + ); + assert_eq!( decode_base64_url("", &0).unwrap_err(), FastCryptoError::GeneralError("Base64 string smaller than 2".to_string()) @@ -472,7 +480,7 @@ fn test_gen_seed() { ) .unwrap(); assert_eq!( - address_seed, + address_seed.to_string(), "16657007263003735230240998439420301694514420923267872433517882233836276100450".to_string() ); } @@ -487,9 +495,11 @@ fn test_verify_zk_login() { let aud = "575519204237-msop9ep45u2uo98hapqmngv8d84qdc8k.apps.googleusercontent.com"; let salt = "6588741469050502421550140105345050859"; let iss = "https://accounts.google.com"; - let salt_hash = poseidon_zk_login(vec![to_field(salt).unwrap()]) - .unwrap() - .to_string(); + let salt_hash = poseidon_zk_login(vec![ + parse_fr_field_element(&Bn254FrElement::from_str(salt).unwrap()).unwrap() + ]) + .unwrap() + .to_string(); assert!(verify_zk_login_id(&address, name, value, aud, iss, &salt_hash).is_ok()); let address_seed = gen_address_seed_with_salt_hash(&salt_hash, name, value, aud).unwrap(); @@ -583,10 +593,11 @@ fn test_alternative_iss_for_google() { let mut eph_pubkey_bytes = vec![0]; eph_pubkey_bytes.extend( - big_int_str_to_bytes( + BigUint::from_str( "3598866369818193253063936208363210863933653800990958031560302098730308306242903464", ) - .unwrap(), + .unwrap() + .to_bytes_be(), ); let mut all_jwk = ImHashMap::new(); all_jwk.insert( diff --git a/fastcrypto-zkp/src/bn254/utils.rs b/fastcrypto-zkp/src/bn254/utils.rs index 0ea526cbd1..1501dc3db3 100644 --- a/fastcrypto-zkp/src/bn254/utils.rs +++ b/fastcrypto-zkp/src/bn254/utils.rs @@ -4,6 +4,7 @@ use crate::bn254::poseidon::poseidon_zk_login; use crate::bn254::zk_login::{OIDCProvider, ZkLoginInputsReader}; use crate::bn254::zk_login_api::Bn254Fr; +use crate::circom::{parse_fr_field_element, Bn254FrElement}; use fastcrypto::error::FastCryptoError; use fastcrypto::hash::{Blake2b256, HashFunction}; use fastcrypto::rsa::Base64UrlUnpadded; @@ -14,7 +15,7 @@ use serde::Deserialize; use serde_json::json; use std::str::FromStr; -use super::zk_login::{hash_ascii_str_to_field, to_field}; +use super::zk_login::hash_ascii_str_to_field; const ZK_LOGIN_AUTHENTICATOR_FLAG: u8 = 0x05; const MAX_KEY_CLAIM_NAME_LENGTH: u8 = 32; @@ -22,13 +23,16 @@ const MAX_KEY_CLAIM_VALUE_LENGTH: u8 = 115; const MAX_AUD_VALUE_LENGTH: u8 = 145; /// Calculate the Sui address based on address seed and address params. -pub fn get_zk_login_address(address_seed: &str, iss: &str) -> Result<[u8; 32], FastCryptoError> { +pub fn get_zk_login_address( + address_seed: &Bn254FrElement, + iss: &str, +) -> Result<[u8; 32], FastCryptoError> { let mut hasher = Blake2b256::default(); hasher.update([ZK_LOGIN_AUTHENTICATOR_FLAG]); let bytes = iss.as_bytes(); hasher.update([bytes.len() as u8]); hasher.update(bytes); - hasher.update(big_int_str_to_bytes(address_seed)?); + hasher.update(address_seed.padded()); Ok(hasher.finalize().digest) } @@ -39,7 +43,7 @@ pub fn gen_address_seed( value: &str, // i.e. the sub value aud: &str, // i.e. the client ID ) -> Result { - let salt_hash = poseidon_zk_login(vec![to_field(salt)?])?; + let salt_hash = poseidon_zk_login(vec![parse_fr_field_element(&Bn254FrElement::from_str(salt)?)?])?; gen_address_seed_with_salt_hash(&salt_hash.to_string(), name, value, aud) } @@ -51,12 +55,12 @@ pub(crate) fn gen_address_seed_with_salt_hash( aud: &str, // i.e. the client ID ) -> Result { Ok(poseidon_zk_login(vec![ - hash_ascii_str_to_field(name, MAX_KEY_CLAIM_NAME_LENGTH)?, - hash_ascii_str_to_field(value, MAX_KEY_CLAIM_VALUE_LENGTH)?, - hash_ascii_str_to_field(aud, MAX_AUD_VALUE_LENGTH)?, - to_field(salt_hash)?, - ])? - .to_string()) + hash_ascii_str_to_field(name, MAX_KEY_CLAIM_NAME_LENGTH)?, + hash_ascii_str_to_field(value, MAX_KEY_CLAIM_VALUE_LENGTH)?, + hash_ascii_str_to_field(aud, MAX_AUD_VALUE_LENGTH)?, + parse_fr_field_element(&Bn254FrElement::from_str(salt_hash)?)?, + ])? + .to_string()) } /// Return the OIDC URL for the given parameters. Crucially the nonce is computed. @@ -196,10 +200,3 @@ pub fn split_to_two_frs(eph_pk_bytes: &[u8]) -> Result<(Bn254Fr, Bn254Fr), FastC let eph_public_key_1 = Bn254Fr::from(second_bigint); Ok((eph_public_key_0, eph_public_key_1)) } - -/// Convert a big int string to a big endian bytearray. -pub fn big_int_str_to_bytes(value: &str) -> Result, FastCryptoError> { - Ok(BigUint::from_str(value) - .map_err(|_| FastCryptoError::InvalidInput)? - .to_bytes_be()) -} diff --git a/fastcrypto-zkp/src/bn254/zk_login.rs b/fastcrypto-zkp/src/bn254/zk_login.rs index 6df9147978..6b1150b5fd 100644 --- a/fastcrypto-zkp/src/bn254/zk_login.rs +++ b/fastcrypto-zkp/src/bn254/zk_login.rs @@ -8,7 +8,8 @@ use serde_json::Value; use super::utils::split_to_two_frs; use crate::bn254::poseidon::poseidon_zk_login; use crate::circom::{ - g1_affine_from_str_projective, g2_affine_from_str_projective, CircomG1, CircomG2, + g1_affine_from_str_projective, g2_affine_from_str_projective, parse_fr_field_element, Bn254FrElement, + CircomG1, CircomG2, }; pub use ark_bn254::{Bn254, Fr as Bn254Fr}; pub use ark_ff::ToConstraintField; @@ -301,7 +302,7 @@ pub struct ZkLoginInputs { proof_points: ZkLoginProof, iss_base64_details: Claim, header_base64: String, - address_seed: String, + address_seed: Bn254FrElement, #[serde(skip)] jwt_details: JWTDetails, } @@ -334,7 +335,8 @@ impl ZkLoginInputs { proof_points: reader.proof_points, iss_base64_details: reader.iss_base64_details, header_base64: reader.header_base64, - address_seed: address_seed.to_owned(), + address_seed: Bn254FrElement::from_str(address_seed) + .map_err(|_| FastCryptoError::InvalidInput)?, jwt_details: reader.jwt_details, } .init() @@ -342,9 +344,6 @@ impl ZkLoginInputs { /// Initialize JWTDetails by parsing header_base64 and iss_base64_details. pub fn init(&mut self) -> Result { - if BigUint::from_str(&self.address_seed).is_err() { - return Err(FastCryptoError::InvalidInput); - } self.jwt_details = JWTDetails::new(&self.header_base64, &self.iss_base64_details)?; Ok(self.to_owned()) } @@ -365,7 +364,7 @@ impl ZkLoginInputs { } /// Get the address seed string. - pub fn get_address_seed(&self) -> &str { + pub fn get_address_seed(&self) -> &Bn254FrElement { &self.address_seed } @@ -380,11 +379,13 @@ impl ZkLoginInputs { return Err(FastCryptoError::GeneralError("Header too long".to_string())); } - let addr_seed = to_field(&self.address_seed)?; + let addr_seed = parse_fr_field_element(&self.address_seed)?; let (first, second) = split_to_two_frs(eph_pk_bytes)?; - let max_epoch_f = to_field(&max_epoch.to_string())?; - let index_mod_4_f = to_field(&self.iss_base64_details.index_mod_4.to_string())?; + let max_epoch_f = parse_fr_field_element(&Bn254FrElement::from_str(&max_epoch.to_string())?)?; + let index_mod_4_f = parse_fr_field_element(&Bn254FrElement::from_str( + &self.iss_base64_details.index_mod_4.to_string(), + )?)?; let iss_base64_f = hash_ascii_str_to_field(&self.iss_base64_details.value, MAX_ISS_LEN_B64)?; @@ -536,12 +537,6 @@ fn bitarray_to_bytearray(bits: &[u8]) -> FastCryptoResult> { .collect()) } -/// Convert a bigint string to a field element. -pub fn to_field(val: &str) -> Result { - Bn254Fr::from_str(val) - .map_err(|_| FastCryptoError::GeneralError("Convert to field error".to_string())) -} - /// Pads a stream of bytes and maps it to a field element pub fn hash_ascii_str_to_field(str: &str, max_size: u8) -> Result { let str_padded = str_to_padded_char_codes(str, max_size)?; diff --git a/fastcrypto-zkp/src/bn254/zk_login_api.rs b/fastcrypto-zkp/src/bn254/zk_login_api.rs index 6410b74e4a..3d853c91be 100644 --- a/fastcrypto-zkp/src/bn254/zk_login_api.rs +++ b/fastcrypto-zkp/src/bn254/zk_login_api.rs @@ -1,12 +1,16 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use std::str::FromStr; + use ark_snark::SNARK; use fastcrypto::rsa::{Base64UrlUnpadded, Encoding}; use super::zk_login::{JwkId, ZkLoginInputs, JWK}; use crate::bn254::utils::{gen_address_seed_with_salt_hash, get_zk_login_address}; -use crate::circom::{g1_affine_from_str_projective, g2_affine_from_str_projective}; +use crate::circom::{ + g1_affine_from_str_projective, g2_affine_from_str_projective, Bn254FqElement, Bn254FrElement, +}; pub use ark_bn254::{Bn254, Fr as Bn254Fr}; pub use ark_ff::ToConstraintField; use ark_groth16::{Groth16, PreparedVerifyingKey, Proof, VerifyingKey}; @@ -41,57 +45,96 @@ static INSECURE_VERIFYING_KEY: Lazy> = Lazy::new(ins fn insecure_pvk() -> PreparedVerifyingKey { // Convert the Circom G1/G2/GT to arkworks G1/G2/GT let vk_alpha_1 = g1_affine_from_str_projective(&vec![ - "20491192805390485299153009773594534940189261866228447918068658471970481763042".to_string(), - "9383485363053290200918347156157836566562967994039712273449902621266178545958".to_string(), - "1".to_string(), + Bn254FqElement::from_str( + "20491192805390485299153009773594534940189261866228447918068658471970481763042", + ) + .unwrap(), + Bn254FqElement::from_str( + "9383485363053290200918347156157836566562967994039712273449902621266178545958", + ) + .unwrap(), + Bn254FqElement::from_str("1").unwrap(), ]) .unwrap(); let vk_beta_2 = g2_affine_from_str_projective(&vec![ vec![ - "6375614351688725206403948262868962793625744043794305715222011528459656738731" - .to_string(), - "4252822878758300859123897981450591353533073413197771768651442665752259397132" - .to_string(), + Bn254FqElement::from_str( + "6375614351688725206403948262868962793625744043794305715222011528459656738731", + ) + .unwrap(), + Bn254FqElement::from_str( + "4252822878758300859123897981450591353533073413197771768651442665752259397132", + ) + .unwrap(), + ], + vec![ + Bn254FqElement::from_str( + "10505242626370262277552901082094356697409835680220590971873171140371331206856", + ) + .unwrap(), + Bn254FqElement::from_str( + "21847035105528745403288232691147584728191162732299865338377159692350059136679", + ) + .unwrap(), ], vec![ - "10505242626370262277552901082094356697409835680220590971873171140371331206856" - .to_string(), - "21847035105528745403288232691147584728191162732299865338377159692350059136679" - .to_string(), + Bn254FqElement::from_str("1").unwrap(), + Bn254FqElement::from_str("0").unwrap(), ], - vec!["1".to_string(), "0".to_string()], ]) .unwrap(); let vk_gamma_2 = g2_affine_from_str_projective(&vec![ vec![ - "10857046999023057135944570762232829481370756359578518086990519993285655852781" - .to_string(), - "11559732032986387107991004021392285783925812861821192530917403151452391805634" - .to_string(), + Bn254FqElement::from_str( + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + ) + .unwrap(), + Bn254FqElement::from_str( + "11559732032986387107991004021392285783925812861821192530917403151452391805634", + ) + .unwrap(), ], vec![ - "8495653923123431417604973247489272438418190587263600148770280649306958101930" - .to_string(), - "4082367875863433681332203403145435568316851327593401208105741076214120093531" - .to_string(), + Bn254FqElement::from_str( + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + ) + .unwrap(), + Bn254FqElement::from_str( + "4082367875863433681332203403145435568316851327593401208105741076214120093531", + ) + .unwrap(), + ], + vec![ + Bn254FqElement::from_str("1").unwrap(), + Bn254FqElement::from_str("0").unwrap(), ], - vec!["1".to_string(), "0".to_string()], ]) .unwrap(); let vk_delta_2 = g2_affine_from_str_projective(&vec![ vec![ - "10857046999023057135944570762232829481370756359578518086990519993285655852781" - .to_string(), - "11559732032986387107991004021392285783925812861821192530917403151452391805634" - .to_string(), + Bn254FqElement::from_str( + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + ) + .unwrap(), + Bn254FqElement::from_str( + "11559732032986387107991004021392285783925812861821192530917403151452391805634", + ) + .unwrap(), + ], + vec![ + Bn254FqElement::from_str( + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + ) + .unwrap(), + Bn254FqElement::from_str( + "4082367875863433681332203403145435568316851327593401208105741076214120093531", + ) + .unwrap(), ], vec![ - "8495653923123431417604973247489272438418190587263600148770280649306958101930" - .to_string(), - "4082367875863433681332203403145435568316851327593401208105741076214120093531" - .to_string(), + Bn254FqElement::from_str("1").unwrap(), + Bn254FqElement::from_str("0").unwrap(), ], - vec!["1".to_string(), "0".to_string()], ]) .unwrap(); @@ -99,18 +142,26 @@ fn insecure_pvk() -> PreparedVerifyingKey { let mut vk_gamma_abc_g1 = Vec::new(); for e in [ vec![ - "20701306374481714853949730154526815782802808896228594855451770849676897643964" - .to_string(), - "2766989084754673216772682210231588284954002353414778477810174100808747060165" - .to_string(), - "1".to_string(), + Bn254FqElement::from_str( + "20701306374481714853949730154526815782802808896228594855451770849676897643964", + ) + .unwrap(), + Bn254FqElement::from_str( + "2766989084754673216772682210231588284954002353414778477810174100808747060165", + ) + .unwrap(), + Bn254FqElement::from_str("1").unwrap(), ], vec![ - "501195541410525737371980194958674422793469475773065719916327137354779402600" - .to_string(), - "13527631693157515024233848630878973193664410306029731429350155106228769355415" - .to_string(), - "1".to_string(), + Bn254FqElement::from_str( + "501195541410525737371980194958674422793469475773065719916327137354779402600", + ) + .unwrap(), + Bn254FqElement::from_str( + "13527631693157515024233848630878973193664410306029731429350155106228769355415", + ) + .unwrap(), + Bn254FqElement::from_str("1").unwrap(), ], ] { let g1 = g1_affine_from_str_projective(&e).unwrap(); @@ -133,57 +184,96 @@ fn insecure_pvk() -> PreparedVerifyingKey { fn global_pvk() -> PreparedVerifyingKey { // Convert the Circom G1/G2/GT to arkworks G1/G2/GT let vk_alpha_1 = g1_affine_from_str_projective(&vec![ - "21529901943976716921335152104180790524318946701278905588288070441048877064089".to_string(), - "7775817982019986089115946956794180159548389285968353014325286374017358010641".to_string(), - "1".to_string(), + Bn254FqElement::from_str( + "21529901943976716921335152104180790524318946701278905588288070441048877064089", + ) + .unwrap(), + Bn254FqElement::from_str( + "7775817982019986089115946956794180159548389285968353014325286374017358010641", + ) + .unwrap(), + Bn254FqElement::from_str("1").unwrap(), ]) .unwrap(); let vk_beta_2 = g2_affine_from_str_projective(&vec![ vec![ - "6600437987682835329040464538375790690815756241121776438004683031791078085074" - .to_string(), - "16207344858883952201936462217289725998755030546200154201671892670464461194903" - .to_string(), + Bn254FqElement::from_str( + "6600437987682835329040464538375790690815756241121776438004683031791078085074", + ) + .unwrap(), + Bn254FqElement::from_str( + "16207344858883952201936462217289725998755030546200154201671892670464461194903", + ) + .unwrap(), ], vec![ - "17943105074568074607580970189766801116106680981075272363121544016828311544390" - .to_string(), - "18339640667362802607939727433487930605412455701857832124655129852540230493587" - .to_string(), + Bn254FqElement::from_str( + "17943105074568074607580970189766801116106680981075272363121544016828311544390", + ) + .unwrap(), + Bn254FqElement::from_str( + "18339640667362802607939727433487930605412455701857832124655129852540230493587", + ) + .unwrap(), + ], + vec![ + Bn254FqElement::from_str("1").unwrap(), + Bn254FqElement::from_str("0").unwrap(), ], - vec!["1".to_string(), "0".to_string()], ]) .unwrap(); let vk_gamma_2 = g2_affine_from_str_projective(&vec![ vec![ - "10857046999023057135944570762232829481370756359578518086990519993285655852781" - .to_string(), - "11559732032986387107991004021392285783925812861821192530917403151452391805634" - .to_string(), + Bn254FqElement::from_str( + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + ) + .unwrap(), + Bn254FqElement::from_str( + "11559732032986387107991004021392285783925812861821192530917403151452391805634", + ) + .unwrap(), + ], + vec![ + Bn254FqElement::from_str( + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + ) + .unwrap(), + Bn254FqElement::from_str( + "4082367875863433681332203403145435568316851327593401208105741076214120093531", + ) + .unwrap(), ], vec![ - "8495653923123431417604973247489272438418190587263600148770280649306958101930" - .to_string(), - "4082367875863433681332203403145435568316851327593401208105741076214120093531" - .to_string(), + Bn254FqElement::from_str("1").unwrap(), + Bn254FqElement::from_str("0").unwrap(), ], - vec!["1".to_string(), "0".to_string()], ]) .unwrap(); let vk_delta_2 = g2_affine_from_str_projective(&vec![ vec![ - "19260309516619721648285279557078789954438346514188902804737557357941293711874" - .to_string(), - "2480422554560175324649200374556411861037961022026590718777465211464278308900" - .to_string(), + Bn254FqElement::from_str( + "19260309516619721648285279557078789954438346514188902804737557357941293711874", + ) + .unwrap(), + Bn254FqElement::from_str( + "2480422554560175324649200374556411861037961022026590718777465211464278308900", + ) + .unwrap(), + ], + vec![ + Bn254FqElement::from_str( + "14489104692423540990601374549557603533921811847080812036788172274404299703364", + ) + .unwrap(), + Bn254FqElement::from_str( + "12564378633583954025611992187142343628816140907276948128970903673042690269191", + ) + .unwrap(), ], vec![ - "14489104692423540990601374549557603533921811847080812036788172274404299703364" - .to_string(), - "12564378633583954025611992187142343628816140907276948128970903673042690269191" - .to_string(), + Bn254FqElement::from_str("1").unwrap(), + Bn254FqElement::from_str("0").unwrap(), ], - vec!["1".to_string(), "0".to_string()], ]) .unwrap(); @@ -191,18 +281,26 @@ fn global_pvk() -> PreparedVerifyingKey { let mut vk_gamma_abc_g1 = Vec::new(); for e in [ vec![ - "1607694606386445293170795095076356565829000940041894770459712091642365695804" - .to_string(), - "18066827569413962196795937356879694709963206118612267170825707780758040578649" - .to_string(), - "1".to_string(), + Bn254FqElement::from_str( + "1607694606386445293170795095076356565829000940041894770459712091642365695804", + ) + .unwrap(), + Bn254FqElement::from_str( + "18066827569413962196795937356879694709963206118612267170825707780758040578649", + ) + .unwrap(), + Bn254FqElement::from_str("1").unwrap(), ], vec![ - "20653794344898475822834426774542692225449366952113790098812854265588083247207" - .to_string(), - "3296759704176575765409730962060698204792513807296274014163938591826372646699" - .to_string(), - "1".to_string(), + Bn254FqElement::from_str( + "20653794344898475822834426774542692225449366952113790098812854265588083247207", + ) + .unwrap(), + Bn254FqElement::from_str( + "3296759704176575765409730962060698204792513807296274014163938591826372646699", + ) + .unwrap(), + Bn254FqElement::from_str("1").unwrap(), ], ] { let g1 = g1_affine_from_str_projective(&e).unwrap(); @@ -284,8 +382,8 @@ pub fn verify_zk_login_id( } /// Verify that the given parameters (address_seed and iss) were used to generate the given address. -pub fn verify_zk_login_iss(address: &[u8], address_seed: &str, iss: &str) -> FastCryptoResult<()> { - let reconstructed_address = get_zk_login_address(address_seed, iss)?; +pub fn verify_zk_login_iss(address: &[u8],address_seed: &str,iss: &str) -> FastCryptoResult<()> { + let reconstructed_address = get_zk_login_address(&Bn254FrElement::from_str(address_seed)?, iss)?; match reconstructed_address == address { true => Ok(()), false => Err(FastCryptoError::InvalidProof), diff --git a/fastcrypto-zkp/src/circom.rs b/fastcrypto-zkp/src/circom.rs index 2c4f3ac72d..66bac4f428 100644 --- a/fastcrypto-zkp/src/circom.rs +++ b/fastcrypto-zkp/src/circom.rs @@ -1,22 +1,142 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use ark_bn254::{Fq, Fq2, G1Affine, G1Projective, G2Affine, G2Projective}; +use ark_bn254::{Fq, Fq2, Fr, G1Affine, G1Projective, G2Affine, G2Projective}; +use ark_ff::BigInteger; +use ark_ff::PrimeField; use fastcrypto::error::FastCryptoError; - +use num_bigint::BigUint; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; /// A G1 point in BN254 serialized as a vector of three strings which is the canonical decimal /// representation of the projective coordinates in Fq. -pub type CircomG1 = Vec; +pub type CircomG1 = Vec; /// A G2 point in BN254 serialized as a vector of three vectors each being a vector of two strings /// which are the canonical decimal representation of the coefficients of the projective coordinates /// in Fq2. -pub type CircomG2 = Vec>; +pub type CircomG2 = Vec>; + +/// A struct that stores a Bn254 Fq field element as 32 bytes. +#[derive(Debug, Clone, JsonSchema, Eq, PartialEq)] +pub struct Bn254FqElement([u8; 32]); + +impl std::str::FromStr for Bn254FqElement { + type Err = FastCryptoError; + + fn from_str(s: &str) -> Result { + let big_int = Fq::from_str(s).map_err(|_| FastCryptoError::InvalidInput)?; + let be_bytes = big_int.into_bigint().to_bytes_be(); + be_bytes + .try_into() + .map_err(|_| FastCryptoError::InvalidInput) + .map(Bn254FqElement) + } +} + +impl std::fmt::Display for Bn254FqElement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let big_int = BigUint::from_bytes_be(&self.0); + let radix10 = big_int.to_string(); + f.write_str(&radix10) + } +} + +// Bn254FqElement's serialized format is as a radix10 encoded string +impl Serialize for Bn254FqElement { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.to_string().serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for Bn254FqElement { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = std::borrow::Cow::<'de, str>::deserialize(deserializer)?; + std::str::FromStr::from_str(&s).map_err(serde::de::Error::custom) + } +} + +/// A struct that stores a Bn254 Fr field element as 32 bytes. +#[derive(Debug, Clone, JsonSchema, Eq, PartialEq)] +pub struct Bn254FrElement([u8; 32]); + +impl Bn254FrElement { + /// Returns the unpadded version of the field element. This returns with leading zeros removed. + pub fn unpadded(&self) -> &[u8] { + let mut buf = self.0.as_slice(); + + while !buf.is_empty() && buf[0] == 0 { + buf = &buf[1..]; + } + + // If the value is '0' then just return a slice of length 1 of the final byte + if buf.is_empty() { + &self.0[31..] + } else { + buf + } + } + + /// Returns the padded version of the field element. This returns with leading zeros preserved to 32 bytes. + pub fn padded(&self) -> &[u8] { + &self.0 + } +} +impl std::str::FromStr for Bn254FrElement { + type Err = FastCryptoError; -/// Parse a string as a field element in BN254. Return an `FastCryptoError::InvalidInput` error if -/// the parsing fails. -fn parse_field_element(s: &str) -> Result { - s.parse::().map_err(|_| FastCryptoError::InvalidInput) + fn from_str(s: &str) -> Result { + let big_int = Fr::from_str(s).map_err(|_| FastCryptoError::InvalidInput)?; + let be_bytes = big_int.into_bigint().to_bytes_be(); + be_bytes + .try_into() + .map_err(|_| FastCryptoError::InvalidInput) + .map(Bn254FrElement) + } +} + +impl std::fmt::Display for Bn254FrElement { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let big_int = BigUint::from_bytes_be(&self.0); + let radix10 = big_int.to_string(); + f.write_str(&radix10) + } +} + +// Bn254FrElement's serialized format is as a radix10 encoded string +impl Serialize for Bn254FrElement { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + self.to_string().serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for Bn254FrElement { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let s = std::borrow::Cow::<'de, str>::deserialize(deserializer)?; + std::str::FromStr::from_str(&s).map_err(serde::de::Error::custom) + } +} + +/// Convert Bn254FqElement type to arkworks' Fq. +fn parse_fq_field_element(s: &Bn254FqElement) -> Result { + Ok(Fq::from_be_bytes_mod_order(&s.0)) +} + +/// Convert Bn254FrElement type to arkworks' Fr. +pub fn parse_fr_field_element(s: &Bn254FrElement) -> Result { + Ok(Fr::from_be_bytes_mod_order(&s.0)) } /// Deserialize a G1 projective point in BN254 serialized as a vector of three strings into an affine @@ -28,9 +148,9 @@ pub(crate) fn g1_affine_from_str_projective(s: &CircomG1) -> Result Result Result(), 33..1024) + ) { + let big_int = BigUint::from_bytes_be(&bytes); + let radix10 = big_int.to_str_radix(10); + + // doesn't crash + let _ = Bn254FrElement::from_str(&radix10); + let _ = Bn254FqElement::from_str(&radix10); + } + } +}