From a9d11d0a05552ee6b81d14315377d2bb5af24c84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 2 Apr 2024 13:22:26 +0200 Subject: [PATCH 01/30] Add generic Groth16 verifier --- fastcrypto-zkp/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fastcrypto-zkp/Cargo.toml b/fastcrypto-zkp/Cargo.toml index f6b3f8a9e4..3dbf473a68 100644 --- a/fastcrypto-zkp/Cargo.toml +++ b/fastcrypto-zkp/Cargo.toml @@ -21,6 +21,7 @@ name = "poseidon" harness = false [dependencies] +ark-bls12-381 = "0.4.0" ark-bn254 = "0.4.0" ark-ec = { version = "0.4.1" } ark-ff = { version = "0.4.1", features = ["asm"] } @@ -45,6 +46,7 @@ typenum = "1.13.0" lazy_static = "1.4.0" itertools = "0.12.0" regex = "1.7.1" +bincode = "1.3.3" [dev-dependencies] ark-bls12-377 = "0.4.0" From 31061d4ecf65064d9b37d1e0cc2d0065695a86fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Wed, 3 Apr 2024 14:46:07 +0200 Subject: [PATCH 02/30] Compiles but deserialization doesn't work --- fastcrypto-zkp/src/groth16/prepared_vk.rs | 140 ++++++++++++++++++++++ fastcrypto/src/groups/bls12381.rs | 2 + fastcrypto/src/groups/mod.rs | 19 ++- fastcrypto/src/groups/ristretto255.rs | 2 + fastcrypto/src/groups/secp256r1.rs | 4 +- 5 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 fastcrypto-zkp/src/groth16/prepared_vk.rs diff --git a/fastcrypto-zkp/src/groth16/prepared_vk.rs b/fastcrypto-zkp/src/groth16/prepared_vk.rs new file mode 100644 index 0000000000..a22e926b00 --- /dev/null +++ b/fastcrypto-zkp/src/groth16/prepared_vk.rs @@ -0,0 +1,140 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use itertools::Itertools; +use serde::{Deserialize, Serialize}; + +use fastcrypto::error::{FastCryptoError, FastCryptoResult}; +use fastcrypto::groups::{deserialize_vector, GroupElement, MultiScalarMul, Pairing, Scalar}; +use fastcrypto::serde_helpers::ToFromByteArray; + +use crate::groth16::{PreparedVerifyingKey, Proof}; + +impl PreparedVerifyingKey +where + G1: Pairing + MultiScalarMul + Serialize + for<'a> Deserialize<'a>, + ::Other: Serialize + for<'a> Deserialize<'a>, + ::Output: GroupElement + Serialize + for<'a> Deserialize<'a>, +{ + pub fn serialize_into_parts(&self) -> FastCryptoResult>> { + let mut result = Vec::with_capacity(4); + result.push( + self.vk_gamma_abc + .iter() + .map(|g1| bcs::to_bytes(g1).map_err(|_| FastCryptoError::InvalidInput)) + .flatten_ok() + .collect::>>()?, + ); + result.push(bcs::to_bytes(&self.alpha_beta).map_err(|_| FastCryptoError::InvalidInput)?); + result.push(bcs::to_bytes(&self.gamma_neg).map_err(|_| FastCryptoError::InvalidInput)?); + result.push(bcs::to_bytes(&self.delta_neg).map_err(|_| FastCryptoError::InvalidInput)?); + Ok(result) + } + + pub fn deserialize_from_parts(parts: &Vec<&[u8]>, g1_size_in_bytes: usize) -> FastCryptoResult { + if parts.len() != 4 { + return Err(FastCryptoError::InvalidInput); + } + + if parts[0].len() % g1_size_in_bytes != 0 { + return Err(FastCryptoError::InvalidInput); + } + + let vk_gamma_abc = deserialize_vector::(parts[0], g1_size_in_bytes)?; + let alpha_beta = bcs::from_bytes(parts[1]).map_err(|_| FastCryptoError::InvalidInput)?; + let gamma_neg = bcs::from_bytes(parts[2]).map_err(|_| FastCryptoError::InvalidInput)?; + let delta_neg = bcs::from_bytes(parts[3]).map_err(|_| FastCryptoError::InvalidInput)?; + + Ok(Self { + vk_gamma_abc, + alpha_beta, + gamma_neg, + delta_neg, + }) + } +} + +impl PreparedVerifyingKey +where + G1: Pairing + MultiScalarMul, + ::Output: GroupElement, +{ + pub fn verify( + &self, + public_inputs: &[G1::ScalarType], + proof: &Proof, + ) -> FastCryptoResult<()> { + let prepared_inputs = self.prepare_inputs(public_inputs)?; + self.verify_with_prepared_inputs(&prepared_inputs, proof) + } + + pub fn verify_with_prepared_inputs( + &self, + prepared_inputs: &G1, + proof: &Proof, + ) -> FastCryptoResult<()> { + let lhs = proof.a.pairing(&proof.b) + + prepared_inputs.pairing(&self.gamma_neg) + + proof.c.pairing(&self.delta_neg); + + if lhs == self.alpha_beta { + Ok(()) + } else { + Err(FastCryptoError::InvalidProof) + } + } + + pub fn prepare_inputs(&self, public_inputs: &[G1::ScalarType]) -> FastCryptoResult { + if (public_inputs.len() + 1) != self.vk_gamma_abc.len() { + return Err(FastCryptoError::InvalidInput); + } + let prepared_input = + self.vk_gamma_abc[0] + G1::multi_scalar_mul(public_inputs, &self.vk_gamma_abc[1..])?; + Ok(prepared_input) + } +} + +#[cfg(test)] +mod tests { + use ark_bls12_381::{Bls12_381, Fr}; + use ark_groth16::Groth16; + use ark_serialize::CanonicalSerialize; + use ark_std::rand::thread_rng; + + use fastcrypto::groups::bls12381::{G1Element, Scalar}; + + use crate::dummy_circuits::Fibonacci; + use crate::groth16::{PreparedVerifyingKey, Proof}; + + #[test] + fn test_verification() { + let mut rng = thread_rng(); + + let a = Fr::from(123); + let b = Fr::from(456); + + let params = { + let circuit = Fibonacci::::new(42, a, b); + Groth16::::generate_random_parameters_with_reduction(circuit, &mut rng) + .unwrap() + }; + + let ark_proof = { + let circuit = Fibonacci::::new(42, a, b); + Groth16::::create_random_proof_with_reduction(circuit, ¶ms, &mut rng) + .unwrap() + }; + + let mut proof_bytes = Vec::new(); + ark_proof.serialize_compressed(&mut proof_bytes).unwrap(); + let proof: Proof = bincode::deserialize(&proof_bytes).unwrap(); + + let mut vk_bytes = Vec::new(); + params.vk.serialize_compressed(&mut vk_bytes).unwrap(); + let vk = bincode::deserialize(&vk_bytes).unwrap(); + + let prepared_vk = PreparedVerifyingKey::from(&vk); + let public_inputs = vec![Scalar::from(123), Scalar::from(456)]; + prepared_vk.verify(&public_inputs, &proof).unwrap() + } +} diff --git a/fastcrypto/src/groups/bls12381.rs b/fastcrypto/src/groups/bls12381.rs index 3d07a1cb8d..3c4ce935b5 100644 --- a/fastcrypto/src/groups/bls12381.rs +++ b/fastcrypto/src/groups/bls12381.rs @@ -768,6 +768,8 @@ impl Div for Scalar { } impl ScalarType for Scalar { + const SIZE_IN_BYTES: usize = SCALAR_LENGTH; + fn rand(rng: &mut R) -> Self { let mut buffer = [0u8; 64]; rng.fill_bytes(&mut buffer); diff --git a/fastcrypto/src/groups/mod.rs b/fastcrypto/src/groups/mod.rs index e4d0f856d1..9993961d20 100644 --- a/fastcrypto/src/groups/mod.rs +++ b/fastcrypto/src/groups/mod.rs @@ -5,9 +5,10 @@ use crate::error::{FastCryptoError, FastCryptoResult}; use crate::traits::AllowedRng; use core::ops::{Add, Div, Mul, Neg, Sub}; use serde::de::DeserializeOwned; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use std::fmt::Debug; use std::ops::{AddAssign, SubAssign}; +use crate::serde_helpers::ToFromByteArray; pub mod bls12381; pub mod ristretto255; @@ -49,10 +50,26 @@ pub trait GroupElement: pub trait Scalar: GroupElement + Copy + From + Sized + Debug + Serialize + DeserializeOwned { + const SIZE_IN_BYTES: usize; + fn rand(rng: &mut R) -> Self; fn inverse(&self) -> FastCryptoResult; } + +/// Assuming that +pub fn deserialize_vector Deserialize<'a>>(bytes: &[u8], size: usize) -> FastCryptoResult> { + if bytes.len() % size != 0 { + return Err(FastCryptoError::InvalidInput); + } + bytes.chunks(size) + .map(|chunk| + bincode::deserialize(chunk) + .map_err(|_| FastCryptoError::InvalidInput) + ) + .collect::>>() +} + /// Trait for group elements that has a fast doubling operation. pub trait Doubling { /// Compute 2 * Self = Self + Self. diff --git a/fastcrypto/src/groups/ristretto255.rs b/fastcrypto/src/groups/ristretto255.rs index b84407d812..7466d6203c 100644 --- a/fastcrypto/src/groups/ristretto255.rs +++ b/fastcrypto/src/groups/ristretto255.rs @@ -194,6 +194,8 @@ impl GroupElement for RistrettoScalar { } impl Scalar for RistrettoScalar { + const SIZE_IN_BYTES: usize = RISTRETTO_SCALAR_BYTE_LENGTH; + fn rand(rng: &mut R) -> Self { Self(ExternalRistrettoScalar::random(rng)) } diff --git a/fastcrypto/src/groups/secp256r1.rs b/fastcrypto/src/groups/secp256r1.rs index b570d02862..00667549de 100644 --- a/fastcrypto/src/groups/secp256r1.rs +++ b/fastcrypto/src/groups/secp256r1.rs @@ -16,7 +16,7 @@ use ark_secp256r1::{Fr, Projective}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use derive_more::{Add, From, Neg, Sub}; use fastcrypto_derive::GroupOpsExtend; -use serde::{de, Deserialize}; +use serde::{de, Deserialize, Serialize}; use std::ops::{Div, Mul}; pub const SCALAR_SIZE_IN_BYTES: usize = 32; @@ -100,6 +100,8 @@ impl From for Scalar { } impl ScalarTrait for Scalar { + const SIZE_IN_BYTES: usize = SCALAR_SIZE_IN_BYTES; + fn rand(rng: &mut R) -> Self { Scalar(Fr::rand(rng)) } From f7033c8ae0f3ed964418b1e9a79794ea636ec5c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 4 Apr 2024 12:55:06 +0200 Subject: [PATCH 03/30] Groth16 api works --- Cargo.lock | 1 + fastcrypto-zkp/Cargo.toml | 1 + fastcrypto-zkp/src/groth16/prepared_vk.rs | 60 +++-------------------- fastcrypto/src/groups/bls12381.rs | 4 +- fastcrypto/src/groups/mod.rs | 38 +++++++++----- fastcrypto/src/groups/ristretto255.rs | 2 - fastcrypto/src/groups/secp256r1.rs | 4 +- 7 files changed, 37 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7674d93a53..000656fe5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1455,6 +1455,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "serde_with", "test-strategy", "tokio", "typenum", diff --git a/fastcrypto-zkp/Cargo.toml b/fastcrypto-zkp/Cargo.toml index 3dbf473a68..ef7b4ef556 100644 --- a/fastcrypto-zkp/Cargo.toml +++ b/fastcrypto-zkp/Cargo.toml @@ -47,6 +47,7 @@ lazy_static = "1.4.0" itertools = "0.12.0" regex = "1.7.1" bincode = "1.3.3" +serde_with = "2.3.1" [dev-dependencies] ark-bls12-377 = "0.4.0" diff --git a/fastcrypto-zkp/src/groth16/prepared_vk.rs b/fastcrypto-zkp/src/groth16/prepared_vk.rs index a22e926b00..0d55040528 100644 --- a/fastcrypto-zkp/src/groth16/prepared_vk.rs +++ b/fastcrypto-zkp/src/groth16/prepared_vk.rs @@ -1,59 +1,11 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use itertools::Itertools; -use serde::{Deserialize, Serialize}; - use fastcrypto::error::{FastCryptoError, FastCryptoResult}; -use fastcrypto::groups::{deserialize_vector, GroupElement, MultiScalarMul, Pairing, Scalar}; -use fastcrypto::serde_helpers::ToFromByteArray; +use fastcrypto::groups::{GroupElement, MultiScalarMul, Pairing}; use crate::groth16::{PreparedVerifyingKey, Proof}; -impl PreparedVerifyingKey -where - G1: Pairing + MultiScalarMul + Serialize + for<'a> Deserialize<'a>, - ::Other: Serialize + for<'a> Deserialize<'a>, - ::Output: GroupElement + Serialize + for<'a> Deserialize<'a>, -{ - pub fn serialize_into_parts(&self) -> FastCryptoResult>> { - let mut result = Vec::with_capacity(4); - result.push( - self.vk_gamma_abc - .iter() - .map(|g1| bcs::to_bytes(g1).map_err(|_| FastCryptoError::InvalidInput)) - .flatten_ok() - .collect::>>()?, - ); - result.push(bcs::to_bytes(&self.alpha_beta).map_err(|_| FastCryptoError::InvalidInput)?); - result.push(bcs::to_bytes(&self.gamma_neg).map_err(|_| FastCryptoError::InvalidInput)?); - result.push(bcs::to_bytes(&self.delta_neg).map_err(|_| FastCryptoError::InvalidInput)?); - Ok(result) - } - - pub fn deserialize_from_parts(parts: &Vec<&[u8]>, g1_size_in_bytes: usize) -> FastCryptoResult { - if parts.len() != 4 { - return Err(FastCryptoError::InvalidInput); - } - - if parts[0].len() % g1_size_in_bytes != 0 { - return Err(FastCryptoError::InvalidInput); - } - - let vk_gamma_abc = deserialize_vector::(parts[0], g1_size_in_bytes)?; - let alpha_beta = bcs::from_bytes(parts[1]).map_err(|_| FastCryptoError::InvalidInput)?; - let gamma_neg = bcs::from_bytes(parts[2]).map_err(|_| FastCryptoError::InvalidInput)?; - let delta_neg = bcs::from_bytes(parts[3]).map_err(|_| FastCryptoError::InvalidInput)?; - - Ok(Self { - vk_gamma_abc, - alpha_beta, - gamma_neg, - delta_neg, - }) - } -} - impl PreparedVerifyingKey where G1: Pairing + MultiScalarMul, @@ -99,16 +51,17 @@ mod tests { use ark_bls12_381::{Bls12_381, Fr}; use ark_groth16::Groth16; use ark_serialize::CanonicalSerialize; + use ark_std::rand::rngs::mock::StepRng; use ark_std::rand::thread_rng; use fastcrypto::groups::bls12381::{G1Element, Scalar}; use crate::dummy_circuits::Fibonacci; - use crate::groth16::{PreparedVerifyingKey, Proof}; + use crate::groth16::{PreparedVerifyingKey, Proof, VerifyingKey}; #[test] fn test_verification() { - let mut rng = thread_rng(); + let mut rng = StepRng::new(2, 1); let a = Fr::from(123); let b = Fr::from(456); @@ -129,9 +82,12 @@ mod tests { ark_proof.serialize_compressed(&mut proof_bytes).unwrap(); let proof: Proof = bincode::deserialize(&proof_bytes).unwrap(); + println!("proof: {:?}", proof); + let mut vk_bytes = Vec::new(); params.vk.serialize_compressed(&mut vk_bytes).unwrap(); - let vk = bincode::deserialize(&vk_bytes).unwrap(); + + let vk = VerifyingKey::from_arkworks_format(&vk_bytes).unwrap(); let prepared_vk = PreparedVerifyingKey::from(&vk); let public_inputs = vec![Scalar::from(123), Scalar::from(456)]; diff --git a/fastcrypto/src/groups/bls12381.rs b/fastcrypto/src/groups/bls12381.rs index 3c4ce935b5..25b77b0776 100644 --- a/fastcrypto/src/groups/bls12381.rs +++ b/fastcrypto/src/groups/bls12381.rs @@ -52,7 +52,7 @@ pub struct GTElement(blst_fp12); /// This represents a scalar modulo r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 /// which is the order of the groups G1, G2 and GT. Note that r is a 255 bit prime. #[derive(Clone, Copy, Eq, PartialEq, GroupOpsExtend)] -pub struct Scalar(blst_fr); +pub struct Scalar(pub blst_fr); pub const SCALAR_LENGTH: usize = 32; pub const G1_ELEMENT_BYTE_LENGTH: usize = 48; @@ -768,8 +768,6 @@ impl Div for Scalar { } impl ScalarType for Scalar { - const SIZE_IN_BYTES: usize = SCALAR_LENGTH; - fn rand(rng: &mut R) -> Self { let mut buffer = [0u8; 64]; rng.fill_bytes(&mut buffer); diff --git a/fastcrypto/src/groups/mod.rs b/fastcrypto/src/groups/mod.rs index 9993961d20..20400a0dc0 100644 --- a/fastcrypto/src/groups/mod.rs +++ b/fastcrypto/src/groups/mod.rs @@ -2,13 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::{FastCryptoError, FastCryptoResult}; +use crate::serde_helpers::ToFromByteArray; use crate::traits::AllowedRng; use core::ops::{Add, Div, Mul, Neg, Sub}; use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; +use serde::Serialize; use std::fmt::Debug; use std::ops::{AddAssign, SubAssign}; -use crate::serde_helpers::ToFromByteArray; pub mod bls12381; pub mod ristretto255; @@ -50,26 +50,38 @@ pub trait GroupElement: pub trait Scalar: GroupElement + Copy + From + Sized + Debug + Serialize + DeserializeOwned { - const SIZE_IN_BYTES: usize; - fn rand(rng: &mut R) -> Self; fn inverse(&self) -> FastCryptoResult; } - -/// Assuming that -pub fn deserialize_vector Deserialize<'a>>(bytes: &[u8], size: usize) -> FastCryptoResult> { - if bytes.len() % size != 0 { +/// Assuming that the serialization of +pub fn deserialize_vector>( + bytes: &[u8], +) -> FastCryptoResult> { + if bytes.len() % SIZE_IN_BYTES != 0 { return Err(FastCryptoError::InvalidInput); } - bytes.chunks(size) - .map(|chunk| - bincode::deserialize(chunk) - .map_err(|_| FastCryptoError::InvalidInput) - ) + bytes + .chunks(SIZE_IN_BYTES) + .map(|chunk| { + T::from_byte_array( + &chunk + .try_into() + .map_err(|_| FastCryptoError::InvalidInput)?, + ) + }) .collect::>>() } +pub fn serialize_vector>( + elements: &Vec, +) -> Vec { + elements + .iter() + .flat_map(|e| e.to_byte_array().to_vec()) + .collect() +} + /// Trait for group elements that has a fast doubling operation. pub trait Doubling { /// Compute 2 * Self = Self + Self. diff --git a/fastcrypto/src/groups/ristretto255.rs b/fastcrypto/src/groups/ristretto255.rs index 7466d6203c..b84407d812 100644 --- a/fastcrypto/src/groups/ristretto255.rs +++ b/fastcrypto/src/groups/ristretto255.rs @@ -194,8 +194,6 @@ impl GroupElement for RistrettoScalar { } impl Scalar for RistrettoScalar { - const SIZE_IN_BYTES: usize = RISTRETTO_SCALAR_BYTE_LENGTH; - fn rand(rng: &mut R) -> Self { Self(ExternalRistrettoScalar::random(rng)) } diff --git a/fastcrypto/src/groups/secp256r1.rs b/fastcrypto/src/groups/secp256r1.rs index 00667549de..b570d02862 100644 --- a/fastcrypto/src/groups/secp256r1.rs +++ b/fastcrypto/src/groups/secp256r1.rs @@ -16,7 +16,7 @@ use ark_secp256r1::{Fr, Projective}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use derive_more::{Add, From, Neg, Sub}; use fastcrypto_derive::GroupOpsExtend; -use serde::{de, Deserialize, Serialize}; +use serde::{de, Deserialize}; use std::ops::{Div, Mul}; pub const SCALAR_SIZE_IN_BYTES: usize = 32; @@ -100,8 +100,6 @@ impl From for Scalar { } impl ScalarTrait for Scalar { - const SIZE_IN_BYTES: usize = SCALAR_SIZE_IN_BYTES; - fn rand(rng: &mut R) -> Self { Scalar(Fr::rand(rng)) } From 16a40451ce22bec6a20a2a5bc77bc59d9e188a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 4 Apr 2024 13:07:32 +0200 Subject: [PATCH 04/30] clean up --- fastcrypto-zkp/src/groth16/{api.rs => generic_api.rs} | 0 fastcrypto-zkp/src/groth16/prepared_vk.rs | 4 ---- 2 files changed, 4 deletions(-) rename fastcrypto-zkp/src/groth16/{api.rs => generic_api.rs} (100%) diff --git a/fastcrypto-zkp/src/groth16/api.rs b/fastcrypto-zkp/src/groth16/generic_api.rs similarity index 100% rename from fastcrypto-zkp/src/groth16/api.rs rename to fastcrypto-zkp/src/groth16/generic_api.rs diff --git a/fastcrypto-zkp/src/groth16/prepared_vk.rs b/fastcrypto-zkp/src/groth16/prepared_vk.rs index 0d55040528..70e0fe93d3 100644 --- a/fastcrypto-zkp/src/groth16/prepared_vk.rs +++ b/fastcrypto-zkp/src/groth16/prepared_vk.rs @@ -52,7 +52,6 @@ mod tests { use ark_groth16::Groth16; use ark_serialize::CanonicalSerialize; use ark_std::rand::rngs::mock::StepRng; - use ark_std::rand::thread_rng; use fastcrypto::groups::bls12381::{G1Element, Scalar}; @@ -82,11 +81,8 @@ mod tests { ark_proof.serialize_compressed(&mut proof_bytes).unwrap(); let proof: Proof = bincode::deserialize(&proof_bytes).unwrap(); - println!("proof: {:?}", proof); - let mut vk_bytes = Vec::new(); params.vk.serialize_compressed(&mut vk_bytes).unwrap(); - let vk = VerifyingKey::from_arkworks_format(&vk_bytes).unwrap(); let prepared_vk = PreparedVerifyingKey::from(&vk); From 40acc323401f3159af383acc2ecafc0586d69fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 4 Apr 2024 14:31:26 +0200 Subject: [PATCH 05/30] Remove old bls impl --- fastcrypto-zkp/src/groth16/prepared_vk.rs | 92 ----------------------- fastcrypto/src/groups/mod.rs | 7 +- 2 files changed, 5 insertions(+), 94 deletions(-) delete mode 100644 fastcrypto-zkp/src/groth16/prepared_vk.rs diff --git a/fastcrypto-zkp/src/groth16/prepared_vk.rs b/fastcrypto-zkp/src/groth16/prepared_vk.rs deleted file mode 100644 index 70e0fe93d3..0000000000 --- a/fastcrypto-zkp/src/groth16/prepared_vk.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use fastcrypto::error::{FastCryptoError, FastCryptoResult}; -use fastcrypto::groups::{GroupElement, MultiScalarMul, Pairing}; - -use crate::groth16::{PreparedVerifyingKey, Proof}; - -impl PreparedVerifyingKey -where - G1: Pairing + MultiScalarMul, - ::Output: GroupElement, -{ - pub fn verify( - &self, - public_inputs: &[G1::ScalarType], - proof: &Proof, - ) -> FastCryptoResult<()> { - let prepared_inputs = self.prepare_inputs(public_inputs)?; - self.verify_with_prepared_inputs(&prepared_inputs, proof) - } - - pub fn verify_with_prepared_inputs( - &self, - prepared_inputs: &G1, - proof: &Proof, - ) -> FastCryptoResult<()> { - let lhs = proof.a.pairing(&proof.b) - + prepared_inputs.pairing(&self.gamma_neg) - + proof.c.pairing(&self.delta_neg); - - if lhs == self.alpha_beta { - Ok(()) - } else { - Err(FastCryptoError::InvalidProof) - } - } - - pub fn prepare_inputs(&self, public_inputs: &[G1::ScalarType]) -> FastCryptoResult { - if (public_inputs.len() + 1) != self.vk_gamma_abc.len() { - return Err(FastCryptoError::InvalidInput); - } - let prepared_input = - self.vk_gamma_abc[0] + G1::multi_scalar_mul(public_inputs, &self.vk_gamma_abc[1..])?; - Ok(prepared_input) - } -} - -#[cfg(test)] -mod tests { - use ark_bls12_381::{Bls12_381, Fr}; - use ark_groth16::Groth16; - use ark_serialize::CanonicalSerialize; - use ark_std::rand::rngs::mock::StepRng; - - use fastcrypto::groups::bls12381::{G1Element, Scalar}; - - use crate::dummy_circuits::Fibonacci; - use crate::groth16::{PreparedVerifyingKey, Proof, VerifyingKey}; - - #[test] - fn test_verification() { - let mut rng = StepRng::new(2, 1); - - let a = Fr::from(123); - let b = Fr::from(456); - - let params = { - let circuit = Fibonacci::::new(42, a, b); - Groth16::::generate_random_parameters_with_reduction(circuit, &mut rng) - .unwrap() - }; - - let ark_proof = { - let circuit = Fibonacci::::new(42, a, b); - Groth16::::create_random_proof_with_reduction(circuit, ¶ms, &mut rng) - .unwrap() - }; - - let mut proof_bytes = Vec::new(); - ark_proof.serialize_compressed(&mut proof_bytes).unwrap(); - let proof: Proof = bincode::deserialize(&proof_bytes).unwrap(); - - let mut vk_bytes = Vec::new(); - params.vk.serialize_compressed(&mut vk_bytes).unwrap(); - let vk = VerifyingKey::from_arkworks_format(&vk_bytes).unwrap(); - - let prepared_vk = PreparedVerifyingKey::from(&vk); - let public_inputs = vec![Scalar::from(123), Scalar::from(456)]; - prepared_vk.verify(&public_inputs, &proof).unwrap() - } -} diff --git a/fastcrypto/src/groups/mod.rs b/fastcrypto/src/groups/mod.rs index 20400a0dc0..5072c0141c 100644 --- a/fastcrypto/src/groups/mod.rs +++ b/fastcrypto/src/groups/mod.rs @@ -54,7 +54,8 @@ pub trait Scalar: fn inverse(&self) -> FastCryptoResult; } -/// Assuming that the serialization of +/// Given a byte array of length `N * SIZE_IN_BYTES`, deserialize it into a vector of `N` elements +/// of type `T`. pub fn deserialize_vector>( bytes: &[u8], ) -> FastCryptoResult> { @@ -73,8 +74,10 @@ pub fn deserialize_vector>>() } +/// Serialize a vector of elements of type T into a byte array by simply concatenating their binary +/// representations. pub fn serialize_vector>( - elements: &Vec, + elements: &[T], ) -> Vec { elements .iter() From 4e9f3af9ff934a5f396ddd56998739b4e4a82c33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 4 Apr 2024 14:32:43 +0200 Subject: [PATCH 06/30] Remove example --- Cargo.lock | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 000656fe5b..30ba961a2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,6 +94,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anes" version = "0.1.6" @@ -746,8 +761,11 @@ version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ + "android-tzdata", + "iana-time-zone", "num-traits", "serde", + "windows-targets 0.52.4", ] [[package]] @@ -872,6 +890,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + [[package]] name = "cpufeatures" version = "0.2.5" @@ -1098,6 +1122,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", + "serde", ] [[package]] @@ -1342,7 +1367,7 @@ dependencies = [ "serde-big-array", "serde-reflection", "serde_json", - "serde_with", + "serde_with 3.8.3", "sha2 0.10.6", "sha3 0.10.6", "signature 2.1.0", @@ -1435,6 +1460,7 @@ dependencies = [ "ark-snark", "ark-std", "bcs", + "bincode", "blake2", "byte-slice-cast", "criterion 0.5.1", @@ -1455,7 +1481,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "serde_with", + "serde_with 2.3.3", "test-strategy", "tokio", "typenum", @@ -1850,6 +1876,29 @@ dependencies = [ "tracing", ] +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1888,6 +1937,7 @@ checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", ] [[package]] @@ -3161,6 +3211,22 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" +dependencies = [ + "base64 0.13.1", + "chrono", + "hex", + "indexmap", + "serde", + "serde_json", + "serde_with_macros 2.3.3", + "time", +] + [[package]] name = "serde_with" version = "3.8.3" @@ -3173,10 +3239,22 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "serde_with_macros", + "serde_with_macros 3.8.3", "time", ] +[[package]] +name = "serde_with_macros" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "serde_with_macros" version = "3.8.3" @@ -3513,10 +3591,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", + "itoa", "num-conv", "powerfmt", "serde", "time-core", + "time-macros", ] [[package]] @@ -3525,6 +3605,16 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tinytemplate" version = "1.2.1" @@ -3682,7 +3772,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "rand", "static_assertions", ] @@ -3915,6 +4005,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.4", +] + [[package]] name = "windows-sys" version = "0.42.0" From 4b046bc685c033808aeb5585f9cbe612950a9ab5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 4 Apr 2024 14:44:04 +0200 Subject: [PATCH 07/30] Internal type should be private --- fastcrypto/src/groups/bls12381.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastcrypto/src/groups/bls12381.rs b/fastcrypto/src/groups/bls12381.rs index 25b77b0776..3d07a1cb8d 100644 --- a/fastcrypto/src/groups/bls12381.rs +++ b/fastcrypto/src/groups/bls12381.rs @@ -52,7 +52,7 @@ pub struct GTElement(blst_fp12); /// This represents a scalar modulo r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 /// which is the order of the groups G1, G2 and GT. Note that r is a 255 bit prime. #[derive(Clone, Copy, Eq, PartialEq, GroupOpsExtend)] -pub struct Scalar(pub blst_fr); +pub struct Scalar(blst_fr); pub const SCALAR_LENGTH: usize = 32; pub const G1_ELEMENT_BYTE_LENGTH: usize = 48; From 2370e0bd9282f17258cce3e992b5118fc73fdb8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 4 Apr 2024 14:45:20 +0200 Subject: [PATCH 08/30] Refactor --- fastcrypto/src/groups/mod.rs | 31 ------------------------------- fastcrypto/src/serde_helpers.rs | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/fastcrypto/src/groups/mod.rs b/fastcrypto/src/groups/mod.rs index 5072c0141c..02cc23da3f 100644 --- a/fastcrypto/src/groups/mod.rs +++ b/fastcrypto/src/groups/mod.rs @@ -54,37 +54,6 @@ pub trait Scalar: fn inverse(&self) -> FastCryptoResult; } -/// Given a byte array of length `N * SIZE_IN_BYTES`, deserialize it into a vector of `N` elements -/// of type `T`. -pub fn deserialize_vector>( - bytes: &[u8], -) -> FastCryptoResult> { - if bytes.len() % SIZE_IN_BYTES != 0 { - return Err(FastCryptoError::InvalidInput); - } - bytes - .chunks(SIZE_IN_BYTES) - .map(|chunk| { - T::from_byte_array( - &chunk - .try_into() - .map_err(|_| FastCryptoError::InvalidInput)?, - ) - }) - .collect::>>() -} - -/// Serialize a vector of elements of type T into a byte array by simply concatenating their binary -/// representations. -pub fn serialize_vector>( - elements: &[T], -) -> Vec { - elements - .iter() - .flat_map(|e| e.to_byte_array().to_vec()) - .collect() -} - /// Trait for group elements that has a fast doubling operation. pub trait Doubling { /// Compute 2 * Self = Self + Self. diff --git a/fastcrypto/src/serde_helpers.rs b/fastcrypto/src/serde_helpers.rs index d15b9dd81b..abfc61e34c 100644 --- a/fastcrypto/src/serde_helpers.rs +++ b/fastcrypto/src/serde_helpers.rs @@ -305,7 +305,7 @@ impl Default for crate::bls12381::min_sig::BLS12381AggregateSignatureAsBytes { #[cfg(test)] mod tests { use super::*; - use crate::groups::bls12381::{G1Element, G1ElementAsBytes, G1_ELEMENT_BYTE_LENGTH}; + use crate::groups::bls12381::{G1_ELEMENT_BYTE_LENGTH, G1Element, G1ElementAsBytes}; use crate::groups::GroupElement; use schemars::schema_for; @@ -357,3 +357,34 @@ mod tests { ); } } + +/// Given a byte array of length `N * SIZE_IN_BYTES`, deserialize it into a vector of `N` elements +/// of type `T`. +pub fn deserialize_vector>( + bytes: &[u8], +) -> FastCryptoResult> { + if bytes.len() % SIZE_IN_BYTES != 0 { + return Err(FastCryptoError::InvalidInput); + } + bytes + .chunks(SIZE_IN_BYTES) + .map(|chunk| { + T::from_byte_array( + &chunk + .try_into() + .map_err(|_| FastCryptoError::InvalidInput)?, + ) + }) + .collect::>>() +} + +/// Serialize a vector of elements of type T into a byte array by simply concatenating their binary +/// representations. +pub fn serialize_vector>( + elements: &[T], +) -> Vec { + elements + .iter() + .flat_map(|e| e.to_byte_array().to_vec()) + .collect() +} From f90813f74894eb8f4352baccdf687388266fa729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 4 Apr 2024 14:52:43 +0200 Subject: [PATCH 09/30] clippy --- fastcrypto-zkp/benches/proving.rs | 2 ++ fastcrypto/src/groups/mod.rs | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/fastcrypto-zkp/benches/proving.rs b/fastcrypto-zkp/benches/proving.rs index 874cbf0463..33831e1275 100644 --- a/fastcrypto-zkp/benches/proving.rs +++ b/fastcrypto-zkp/benches/proving.rs @@ -1,3 +1,5 @@ +use std::ops::Mul; + // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 diff --git a/fastcrypto/src/groups/mod.rs b/fastcrypto/src/groups/mod.rs index 02cc23da3f..e4d0f856d1 100644 --- a/fastcrypto/src/groups/mod.rs +++ b/fastcrypto/src/groups/mod.rs @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 use crate::error::{FastCryptoError, FastCryptoResult}; -use crate::serde_helpers::ToFromByteArray; use crate::traits::AllowedRng; use core::ops::{Add, Div, Mul, Neg, Sub}; use serde::de::DeserializeOwned; From 4adef8ae27d3ea33c138c096cde7ce1185080e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 4 Apr 2024 20:04:51 +0200 Subject: [PATCH 10/30] Clean up tests --- fastcrypto-zkp/src/bls12381/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/fastcrypto-zkp/src/bls12381/mod.rs b/fastcrypto-zkp/src/bls12381/mod.rs index d96d7e4c8d..d4b903b8fc 100644 --- a/fastcrypto-zkp/src/bls12381/mod.rs +++ b/fastcrypto-zkp/src/bls12381/mod.rs @@ -15,6 +15,14 @@ pub mod api; #[cfg(test)] mod test_helpers; +#[cfg(test)] +#[path = "unit_tests/api_tests.rs"] +mod api_tests; + +#[cfg(test)] +#[path = "unit_tests/test_helpers.rs"] +pub(crate) mod test_helpers; + /// A prepared Groth16 verifying key in the BLS12-381 construction. pub type PreparedVerifyingKey = groth16::PreparedVerifyingKey; From 669a85c81075affadb6d37718da8d0a24d819dae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 4 Apr 2024 20:15:42 +0200 Subject: [PATCH 11/30] Clean up deps --- Cargo.lock | 12 ------------ fastcrypto-zkp/Cargo.toml | 1 - 2 files changed, 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30ba961a2c..92693ee874 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,17 +121,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" -[[package]] -name = "ark-bls12-377" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb00293ba84f51ce3bd026bd0de55899c4e68f0a39a5728cebae3a73ffdc0a4f" -dependencies = [ - "ark-ec", - "ark-ff", - "ark-std", -] - [[package]] name = "ark-bls12-381" version = "0.4.0" @@ -1447,7 +1436,6 @@ dependencies = [ name = "fastcrypto-zkp" version = "0.1.3" dependencies = [ - "ark-bls12-377", "ark-bls12-381", "ark-bn254", "ark-crypto-primitives", diff --git a/fastcrypto-zkp/Cargo.toml b/fastcrypto-zkp/Cargo.toml index ef7b4ef556..ac3678480d 100644 --- a/fastcrypto-zkp/Cargo.toml +++ b/fastcrypto-zkp/Cargo.toml @@ -21,7 +21,6 @@ name = "poseidon" harness = false [dependencies] -ark-bls12-381 = "0.4.0" ark-bn254 = "0.4.0" ark-ec = { version = "0.4.1" } ark-ff = { version = "0.4.1", features = ["asm"] } From 5cbf635e962bd3aa61419ceafa20ea7722c108fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 11 Apr 2024 13:47:38 +0200 Subject: [PATCH 12/30] Clean up after rebase --- Cargo.lock | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 92693ee874..30ba961a2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,6 +121,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" +[[package]] +name = "ark-bls12-377" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb00293ba84f51ce3bd026bd0de55899c4e68f0a39a5728cebae3a73ffdc0a4f" +dependencies = [ + "ark-ec", + "ark-ff", + "ark-std", +] + [[package]] name = "ark-bls12-381" version = "0.4.0" @@ -1436,6 +1447,7 @@ dependencies = [ name = "fastcrypto-zkp" version = "0.1.3" dependencies = [ + "ark-bls12-377", "ark-bls12-381", "ark-bn254", "ark-crypto-primitives", From 0da442e89de4b1579a6016dc4408dff6c608aaee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Fri, 12 Apr 2024 12:47:54 +0200 Subject: [PATCH 13/30] Conversion in both ways --- fastcrypto-zkp/src/bls12381/api/tests.rs | 60 ++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/fastcrypto-zkp/src/bls12381/api/tests.rs b/fastcrypto-zkp/src/bls12381/api/tests.rs index 372204948a..606aa05830 100644 --- a/fastcrypto-zkp/src/bls12381/api/tests.rs +++ b/fastcrypto-zkp/src/bls12381/api/tests.rs @@ -367,3 +367,63 @@ fn api_regression_tests() { ) .unwrap()); } + +#[test] +fn test_verify() { + // Success case. + let mut vk_bytes = hex::decode("ada3c24e8c2e63579cc03fd1f112a093a17fc8ab0ff6eee7e04cab7bf8e03e7645381f309ec113309e05ac404c77ac7c8585d5e4328594f5a70a81f6bd4f29073883ee18fd90e2aa45d0fc7376e81e2fdf5351200386f5732e58eb6ff4d318dc").unwrap(); + let alpha_bytes = hex::decode("8b0f85a9e7d929244b0af9a35af10717bd667b6227aae37a6d336e815fb0d850873e0d87968345a493b2d31aa8aa400d9820af1d35fa862d1b339ea1f98ac70db7faa304bff120a151a1741d782d08b8f1c1080d4d2f3ebee63ac6cadc666605be306de0973be38fbbf0f54b476bbb002a74ff9506a2b9b9a34b99bfa7481a84a2c9face7065c19d7069cc5738c5350b886a5eeebe656499d2ffb360afc7aff20fa9ee689fb8b46863e90c85224e8f597bf323ad4efb02ee96eb40221fc89918a2c740eabd2886476c7f247a3eb34f0106b3b51cf040e2cdcafea68b0d8eecabf58b5aa2ece3d86259cf2dfa3efab1170c6eb11948826def533849b68335d76d60f3e16bb5c629b1c24df2bdd1a7f13c754d7fe38617ecd7783504e4615e5c13168185cc08de8d63a0f7032ab7e82ff78cf0bc46a84c98f2d95bb5af355cbbe525c44d5c1549c169dfe119a219dbf9038ec73729d187bd0e3ed369e4a2ec2be837f3dcfd958aea7110627d2c0192d262f17e722509c17196005b646a556cf010ef9bd2a2a9b937516a5ecdee516e77d14278e96bc891b630fc833dda714343554ae127c49460416430b7d4f048d08618058335dec0728ad37d10dd9d859c385a38673e71cc98e8439da0accc29de5c92d3c3dc98e199361e9f7558e8b0a2a315ccc5a72f54551f07fad6f6f4615af498aba98aea01a13a4eb84667fd87ee9782b1d812a03f8814f042823a7701238d0fec1e7dec2a26ffea00330b5c7930e95138381435d2a59f51313a48624e30b0a685e357874d41a0a19d83f7420c1d9c04").unwrap(); + let gamma_bytes = hex::decode("b675d1ff988116d1f2965d3c0c373569b74d0a1762ea7c4f4635faa5b5a8fa198a2a2ce6153f390a658dc9ad01a415491747e9de7d5f493f59cf05a52eb46eaac397ffc47aef1396cf0d8b75d0664077ea328ad6b63284b42972a8f11c523a60").unwrap(); + let delta_bytes = hex::decode("8229cb9443ef1fb72887f917f500e2aef998717d91857bcb92061ecd74d1d24c2b2b282736e8074e4316939b4c9853c117aa08ed49206860d648818b2cccb526585f5790161b1730d39c73603b482424a27bba891aaa6d99f3025d3df2a6bd42").unwrap(); + + let inputs_bytes = + hex::decode("440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849").unwrap(); + let proof_bytes = hex::decode("a29981304df8e0f50750b558d4de59dbc8329634b81c986e28e9fff2b0faa52333b14a1f7b275b029e13499d1f5dd8ab955cf5fa3000a097920180381a238ce12df52207597eade4a365a6872c0a19a39c08a9bfb98b69a15615f90cc32660180ca32e565c01a49b505dd277713b1eae834df49643291a3601b11f56957bde02d5446406d0e4745d1bd32c8ccb8d8e80b877712f5f373016d2ecdeebb58caebc7a425b8137ebb1bd0c5b81c1d48151b25f0f24fe9602ba4e403811fb17db6f14").unwrap(); + + // Success case + assert!(verify_groth16_in_bytes( + &vk_bytes, + &alpha_bytes, + &gamma_bytes, + &delta_bytes, + &inputs_bytes, + &proof_bytes + ) + .unwrap()); + + // Invalid public inputs bytes. + let invalid_inputs = hex::decode("cf").unwrap(); + assert!(verify_groth16_in_bytes( + &alpha_bytes, + &gamma_bytes, + &delta_bytes, + &inputs_bytes, + &invalid_inputs, + &proof_bytes + ) + .is_err()); + + // Invalid proof bytes. + let invalid_proof = hex::decode("4a").unwrap(); + assert!(verify_groth16_in_bytes( + &alpha_bytes, + &gamma_bytes, + &delta_bytes, + &inputs_bytes, + &inputs_bytes, + &invalid_proof + ) + .is_err()); + + // Invalid prepared verifying key. + vk_bytes.pop(); + assert!(verify_groth16_in_bytes( + &vk_bytes, + &alpha_bytes, + &gamma_bytes, + &delta_bytes, + &inputs_bytes, + &proof_bytes + ) + .is_err()); +} From 44ef016d2100c12681333cf5ad6bc493880d2514 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Fri, 12 Apr 2024 14:27:07 +0200 Subject: [PATCH 14/30] Use closures for generic conversion --- fastcrypto-zkp/src/bls12381/api/tests.rs | 5 +---- fastcrypto-zkp/src/groth16/mod.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api/tests.rs b/fastcrypto-zkp/src/bls12381/api/tests.rs index 606aa05830..e2f87f2273 100644 --- a/fastcrypto-zkp/src/bls12381/api/tests.rs +++ b/fastcrypto-zkp/src/bls12381/api/tests.rs @@ -16,10 +16,7 @@ use fastcrypto::groups::GroupElement; use fastcrypto::serde_helpers::ToFromByteArray; use crate::bls12381::api::{prepare_pvk_bytes, verify_groth16_in_bytes}; -use crate::bls12381::test_helpers::from_arkworks_scalar; -use crate::bls12381::{PreparedVerifyingKey, VerifyingKey}; -use crate::dummy_circuits::{DummyCircuit, Fibonacci}; -use crate::groth16::Proof; +use crate::dummy_circuits::DummyCircuit; #[test] fn test_verify() { diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index b55734c96c..8acd43feca 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -121,3 +121,18 @@ impl PreparedVerifyingKey { .map(|x| x + self.vk_gamma_abc[0]) } } + +/// Serialization of GT elements is typically not standardized across libraries, so implementations +/// must specify what implementation to use here to be compatible with the arkworks format (see +/// [`ark_ec::pairing::PairingOutput`]). +pub trait GTSerialize: Sized { + /// Serialize the element into a byte array. + fn to_arkworks_bytes(&self) -> [u8; SIZE_IN_BYTES]; + + /// Deserialize the element from a byte array. + fn from_arkworks_bytes(bytes: &[u8; SIZE_IN_BYTES]) -> FastCryptoResult; +} + +pub trait FromLittleEndianByteArray: Sized { + fn from_little_endian_byte_array(bytes: &[u8; SIZE_IN_BYTES]) -> FastCryptoResult; +} From 60a3aad704626cbaf8e72fc934fa3be7222ad3ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Fri, 12 Apr 2024 15:03:30 +0200 Subject: [PATCH 15/30] docs --- fastcrypto-zkp/src/groth16/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index 8acd43feca..8dffbd1ef1 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -133,6 +133,7 @@ pub trait GTSerialize: Sized { fn from_arkworks_bytes(bytes: &[u8; SIZE_IN_BYTES]) -> FastCryptoResult; } +/// Scalars given to the API are expected to be in little-endian format. pub trait FromLittleEndianByteArray: Sized { fn from_little_endian_byte_array(bytes: &[u8; SIZE_IN_BYTES]) -> FastCryptoResult; } From 782b6f46f7970bfd3c5412284e3e8a1a0a3d2331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Fri, 12 Apr 2024 15:04:44 +0200 Subject: [PATCH 16/30] refactor --- fastcrypto-zkp/src/groth16/mod.rs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index 8dffbd1ef1..b55734c96c 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -121,19 +121,3 @@ impl PreparedVerifyingKey { .map(|x| x + self.vk_gamma_abc[0]) } } - -/// Serialization of GT elements is typically not standardized across libraries, so implementations -/// must specify what implementation to use here to be compatible with the arkworks format (see -/// [`ark_ec::pairing::PairingOutput`]). -pub trait GTSerialize: Sized { - /// Serialize the element into a byte array. - fn to_arkworks_bytes(&self) -> [u8; SIZE_IN_BYTES]; - - /// Deserialize the element from a byte array. - fn from_arkworks_bytes(bytes: &[u8; SIZE_IN_BYTES]) -> FastCryptoResult; -} - -/// Scalars given to the API are expected to be in little-endian format. -pub trait FromLittleEndianByteArray: Sized { - fn from_little_endian_byte_array(bytes: &[u8; SIZE_IN_BYTES]) -> FastCryptoResult; -} From 28da293512cafd4763b24ffb8539b27f96748adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Mon, 15 Apr 2024 12:10:01 +0200 Subject: [PATCH 17/30] docs + arkworks compatability tests --- fastcrypto-zkp/src/groth16/{generic_api.rs => api.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename fastcrypto-zkp/src/groth16/{generic_api.rs => api.rs} (100%) diff --git a/fastcrypto-zkp/src/groth16/generic_api.rs b/fastcrypto-zkp/src/groth16/api.rs similarity index 100% rename from fastcrypto-zkp/src/groth16/generic_api.rs rename to fastcrypto-zkp/src/groth16/api.rs From ee018429bb924d47e70af3c3771b1329f96d692f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Mon, 15 Apr 2024 12:14:11 +0200 Subject: [PATCH 18/30] refactor --- fastcrypto-zkp/src/bls12381/conversions.rs | 193 +++++++++++++++++++++ fastcrypto-zkp/src/bls12381/mod.rs | 1 + 2 files changed, 194 insertions(+) create mode 100644 fastcrypto-zkp/src/bls12381/conversions.rs diff --git a/fastcrypto-zkp/src/bls12381/conversions.rs b/fastcrypto-zkp/src/bls12381/conversions.rs new file mode 100644 index 0000000000..ff37a99f1e --- /dev/null +++ b/fastcrypto-zkp/src/bls12381/conversions.rs @@ -0,0 +1,193 @@ +use fastcrypto::error::FastCryptoResult; +use fastcrypto::groups::bls12381::{FP_BYTE_LENGTH, GT_ELEMENT_BYTE_LENGTH, GTElement, Scalar, SCALAR_LENGTH}; +use fastcrypto::serde_helpers::ToFromByteArray; + +use crate::groth16::api::{FromLittleEndianByteArray, GTSerialize}; + +impl FromLittleEndianByteArray for Scalar { + fn from_little_endian_byte_array(bytes: &[u8; SCALAR_LENGTH]) -> FastCryptoResult { + let mut reversed = *bytes; + reversed.reverse(); + Scalar::from_byte_array(&reversed) + } +} + +impl GTSerialize for GTElement { + fn to_arkworks_bytes(&self) -> [u8; GT_ELEMENT_BYTE_LENGTH] { + gt_element_to_arkworks(&self.to_byte_array()) + } + + fn from_arkworks_bytes(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> FastCryptoResult { + GTElement::from_byte_array(&arkworks_to_gt_element(bytes)) + } +} + +// An element in the quadratic extension of Fp consists of two field elements. +const FP_EXTENSION_BYTE_LENGTH: usize = 2 * FP_BYTE_LENGTH; + +/// Reorder the six field extensions in a GT element according to the given permutation. +/// This functions panics if one of the elements in the permutation is larger than 5. +fn permute_elements( + bytes: &[u8; 6 * FP_EXTENSION_BYTE_LENGTH], + permutation: &[usize; 6], +) -> [u8; 6 * FP_EXTENSION_BYTE_LENGTH] { + // TODO: This could be done in-place to avoid allocating a new array. + let mut result = [0u8; 12 * FP_BYTE_LENGTH]; + for (from, to) in permutation.iter().enumerate() { + let from = from * FP_EXTENSION_BYTE_LENGTH; + let to = to * FP_EXTENSION_BYTE_LENGTH; + result[to..to + FP_EXTENSION_BYTE_LENGTH] + .copy_from_slice(&bytes[from..from + FP_EXTENSION_BYTE_LENGTH]); + } + result + .chunks_exact_mut(FP_BYTE_LENGTH) + .for_each(|chunk| chunk.reverse()); + result +} + +/// Given a serialization of a arkworks [`PairingOutput`] element, this function returns a +/// serialization of the corresponding [`GTElement`] element. It is _not_ verified whether the input +/// is a valid serialization. +fn arkworks_to_gt_element(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> [u8; GT_ELEMENT_BYTE_LENGTH] { + // This permutation flips the order of the i in 0..3 and j in 0..2 loops and may be computed as: + // for i in 0..3 { + // for j in 0..2 { + // PERMUTATION[i + j * 3] = i * 2 + j; + // } + // } + const PERMUTATION: [usize; 6] = [0, 2, 4, 1, 3, 5]; + permute_elements(bytes, &PERMUTATION) +} + +/// Given a serialization of a [`GTElement`], this function returns a serialization of the +/// corresponding element as a arkworks [`PairingOutput`] type. It is _not_ verified whether the +/// input is a valid serialization. +fn gt_element_to_arkworks(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> [u8; GT_ELEMENT_BYTE_LENGTH] { + // This is the inverse of the permutation in `arkworks_to_gt_element`. + const PERMUTATION: [usize; 6] = [0, 3, 1, 4, 2, 5]; + permute_elements(bytes, &PERMUTATION) +} + +#[cfg(test)] +mod tests { + use ark_bls12_381::{Bls12_381, G1Projective, G2Projective}; + use ark_ec::Group; + use ark_ec::pairing::PairingOutput; + use ark_ff::Zero; + use ark_serialize::CanonicalSerialize; + use fastcrypto::error::FastCryptoError; + + use fastcrypto::groups::bls12381::{G1_ELEMENT_BYTE_LENGTH, G1Element, G2_ELEMENT_BYTE_LENGTH, G2Element, GT_ELEMENT_BYTE_LENGTH, GTElement}; + use fastcrypto::groups::GroupElement; + use fastcrypto::serde_helpers::ToFromByteArray; + use crate::bls12381::conversions::{arkworks_to_gt_element, gt_element_to_arkworks}; + use crate::groth16::api::GTSerialize; + + #[test] + fn test_gt_element_conversion() { + let generator = PairingOutput::::generator(); + let mut arkworks_bytes = Vec::new(); + let mut uncompressed_bytes = Vec::new(); + + // GT elements cannot be compressed, so compressed and uncompressed serialization should be the same. + generator.serialize_compressed(&mut arkworks_bytes).unwrap(); + generator + .serialize_uncompressed(&mut uncompressed_bytes) + .unwrap(); + assert_eq!(arkworks_bytes, uncompressed_bytes); + + // The arkworks serialization does not match the GroupElement serialization. + let fc_bytes = GTElement::generator().to_byte_array(); + assert_eq!(arkworks_bytes.len(), fc_bytes.len()); + assert_ne!(arkworks_bytes, fc_bytes); + + // After conversion, the arkworks serialization should match the GroupElement serialization. + assert_eq!(arkworks_bytes, gt_element_to_arkworks(&fc_bytes)); + assert_eq!( + arkworks_to_gt_element(&arkworks_bytes.try_into().unwrap()), + fc_bytes + ); + + // Compare serializations of the identity element + let arkworks_id = PairingOutput::::zero(); + let mut arkworks_bytes = Vec::new(); + arkworks_id + .serialize_uncompressed(&mut arkworks_bytes) + .unwrap(); + let fc_bytes = GTElement::zero().to_byte_array(); + assert_ne!(&fc_bytes.to_vec(), &arkworks_bytes); + assert_eq!(>_element_to_arkworks(&fc_bytes).to_vec(), &arkworks_bytes); + assert_eq!( + arkworks_to_gt_element(&arkworks_bytes.try_into().unwrap()), + fc_bytes + ); + } + + fn test_arkworks_compatability_for_group_element< + const SIZE: usize, + G: GroupElement, + A: Group + CanonicalSerialize, + >( + g: G, + a: A, + serializer: fn(G) -> [u8; SIZE], + deserializer: fn(&[u8; SIZE]) -> Result, + ) { + let bytes = serializer(g); + let mut arkworks_bytes = Vec::new(); + a.serialize_compressed(&mut arkworks_bytes).unwrap(); + assert_eq!(bytes.to_vec(), arkworks_bytes); + assert_eq!(bytes.len(), SIZE); + + let g2 = deserializer(&bytes).unwrap(); + assert_eq!(g, g2); + + let a2 = A::deserialize_compressed(&arkworks_bytes[..]).unwrap(); + assert_eq!(a, a2); + } + + fn test_arkworks_compatability_for_group< + const SIZE: usize, + G: GroupElement + ToFromByteArray, + A: Group + CanonicalSerialize, + >( + serializer: fn(G) -> [u8; SIZE], + deserializer: fn(&[u8; SIZE]) -> Result, + ) { + test_arkworks_compatability_for_group_element::( + G::zero(), + A::zero(), + serializer, + deserializer, + ); + test_arkworks_compatability_for_group_element::( + G::generator(), + A::generator(), + serializer, + deserializer, + ); + let scalar = 12345u128; + test_arkworks_compatability_for_group_element::( + G::generator() * G::ScalarType::from(scalar), + A::generator() * A::ScalarField::from(scalar), + serializer, + deserializer, + ); + } + + #[test] + fn test_arkworks_compatability() { + test_arkworks_compatability_for_group::( + |g| g.to_byte_array(), + G1Element::from_byte_array, + ); + test_arkworks_compatability_for_group::( + |g| g.to_byte_array(), + G2Element::from_byte_array, + ); + test_arkworks_compatability_for_group::>( + |g| g.to_arkworks_bytes(), + GTElement::from_arkworks_bytes, + ); + } +} diff --git a/fastcrypto-zkp/src/bls12381/mod.rs b/fastcrypto-zkp/src/bls12381/mod.rs index d4b903b8fc..fd977c37f1 100644 --- a/fastcrypto-zkp/src/bls12381/mod.rs +++ b/fastcrypto-zkp/src/bls12381/mod.rs @@ -22,6 +22,7 @@ mod api_tests; #[cfg(test)] #[path = "unit_tests/test_helpers.rs"] pub(crate) mod test_helpers; +mod conversions; /// A prepared Groth16 verifying key in the BLS12-381 construction. pub type PreparedVerifyingKey = groth16::PreparedVerifyingKey; From 5cba8f76fb32b9182fb917d03aee4285d9925658 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Mon, 15 Apr 2024 12:23:05 +0200 Subject: [PATCH 19/30] refactor --- fastcrypto-zkp/src/bls12381/api/tests.rs | 130 +++++++------- fastcrypto-zkp/src/bls12381/conversions.rs | 193 --------------------- 2 files changed, 67 insertions(+), 256 deletions(-) delete mode 100644 fastcrypto-zkp/src/bls12381/conversions.rs diff --git a/fastcrypto-zkp/src/bls12381/api/tests.rs b/fastcrypto-zkp/src/bls12381/api/tests.rs index e2f87f2273..eebe72da2a 100644 --- a/fastcrypto-zkp/src/bls12381/api/tests.rs +++ b/fastcrypto-zkp/src/bls12381/api/tests.rs @@ -1,8 +1,6 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::ops::Mul; - use ark_bls12_381::{Bls12_381, Fq12, Fr, G1Affine}; use ark_ff::One; use ark_groth16::Groth16; @@ -16,7 +14,73 @@ use fastcrypto::groups::GroupElement; use fastcrypto::serde_helpers::ToFromByteArray; use crate::bls12381::api::{prepare_pvk_bytes, verify_groth16_in_bytes}; -use crate::dummy_circuits::DummyCircuit; +use crate::bls12381::test_helpers::from_arkworks_scalar; +use crate::bls12381::{PreparedVerifyingKey, VerifyingKey}; +use crate::dummy_circuits::{DummyCircuit, Fibonacci}; +use crate::groth16::Proof; +use fastcrypto::groups::bls12381::G1Element; + +use std::ops::Mul; + +#[test] +fn test_verify() { + // Success case. + let mut vk_bytes = hex::decode("ada3c24e8c2e63579cc03fd1f112a093a17fc8ab0ff6eee7e04cab7bf8e03e7645381f309ec113309e05ac404c77ac7c8585d5e4328594f5a70a81f6bd4f29073883ee18fd90e2aa45d0fc7376e81e2fdf5351200386f5732e58eb6ff4d318dc").unwrap(); + let alpha_bytes = hex::decode("8b0f85a9e7d929244b0af9a35af10717bd667b6227aae37a6d336e815fb0d850873e0d87968345a493b2d31aa8aa400d9820af1d35fa862d1b339ea1f98ac70db7faa304bff120a151a1741d782d08b8f1c1080d4d2f3ebee63ac6cadc666605be306de0973be38fbbf0f54b476bbb002a74ff9506a2b9b9a34b99bfa7481a84a2c9face7065c19d7069cc5738c5350b886a5eeebe656499d2ffb360afc7aff20fa9ee689fb8b46863e90c85224e8f597bf323ad4efb02ee96eb40221fc89918a2c740eabd2886476c7f247a3eb34f0106b3b51cf040e2cdcafea68b0d8eecabf58b5aa2ece3d86259cf2dfa3efab1170c6eb11948826def533849b68335d76d60f3e16bb5c629b1c24df2bdd1a7f13c754d7fe38617ecd7783504e4615e5c13168185cc08de8d63a0f7032ab7e82ff78cf0bc46a84c98f2d95bb5af355cbbe525c44d5c1549c169dfe119a219dbf9038ec73729d187bd0e3ed369e4a2ec2be837f3dcfd958aea7110627d2c0192d262f17e722509c17196005b646a556cf010ef9bd2a2a9b937516a5ecdee516e77d14278e96bc891b630fc833dda714343554ae127c49460416430b7d4f048d08618058335dec0728ad37d10dd9d859c385a38673e71cc98e8439da0accc29de5c92d3c3dc98e199361e9f7558e8b0a2a315ccc5a72f54551f07fad6f6f4615af498aba98aea01a13a4eb84667fd87ee9782b1d812a03f8814f042823a7701238d0fec1e7dec2a26ffea00330b5c7930e95138381435d2a59f51313a48624e30b0a685e357874d41a0a19d83f7420c1d9c04").unwrap(); + let gamma_bytes = hex::decode("b675d1ff988116d1f2965d3c0c373569b74d0a1762ea7c4f4635faa5b5a8fa198a2a2ce6153f390a658dc9ad01a415491747e9de7d5f493f59cf05a52eb46eaac397ffc47aef1396cf0d8b75d0664077ea328ad6b63284b42972a8f11c523a60").unwrap(); + let delta_bytes = hex::decode("8229cb9443ef1fb72887f917f500e2aef998717d91857bcb92061ecd74d1d24c2b2b282736e8074e4316939b4c9853c117aa08ed49206860d648818b2cccb526585f5790161b1730d39c73603b482424a27bba891aaa6d99f3025d3df2a6bd42").unwrap(); + + let inputs_bytes = + hex::decode("440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849").unwrap(); + let proof_bytes = hex::decode("a29981304df8e0f50750b558d4de59dbc8329634b81c986e28e9fff2b0faa52333b14a1f7b275b029e13499d1f5dd8ab955cf5fa3000a097920180381a238ce12df52207597eade4a365a6872c0a19a39c08a9bfb98b69a15615f90cc32660180ca32e565c01a49b505dd277713b1eae834df49643291a3601b11f56957bde02d5446406d0e4745d1bd32c8ccb8d8e80b877712f5f373016d2ecdeebb58caebc7a425b8137ebb1bd0c5b81c1d48151b25f0f24fe9602ba4e403811fb17db6f14").unwrap(); + + // Success case + assert!(verify_groth16_in_bytes( + &vk_bytes, + &alpha_bytes, + &gamma_bytes, + &delta_bytes, + &inputs_bytes, + &proof_bytes + ) + .unwrap()); + + // Invalid public inputs bytes. + let invalid_inputs = hex::decode("cf").unwrap(); + assert!(verify_groth16_in_bytes( + &alpha_bytes, + &gamma_bytes, + &delta_bytes, + &inputs_bytes, + &invalid_inputs, + &proof_bytes + ) + .is_err()); + + // Invalid proof bytes. + let invalid_proof = hex::decode("4a").unwrap(); + assert!(verify_groth16_in_bytes( + &alpha_bytes, + &gamma_bytes, + &delta_bytes, + &inputs_bytes, + &inputs_bytes, + &invalid_proof + ) + .is_err()); + + // Invalid prepared verifying key. + vk_bytes.pop(); + assert!(verify_groth16_in_bytes( + &vk_bytes, + &alpha_bytes, + &gamma_bytes, + &delta_bytes, + &inputs_bytes, + &proof_bytes + ) + .is_err()); +} #[test] fn test_verify() { @@ -364,63 +428,3 @@ fn api_regression_tests() { ) .unwrap()); } - -#[test] -fn test_verify() { - // Success case. - let mut vk_bytes = hex::decode("ada3c24e8c2e63579cc03fd1f112a093a17fc8ab0ff6eee7e04cab7bf8e03e7645381f309ec113309e05ac404c77ac7c8585d5e4328594f5a70a81f6bd4f29073883ee18fd90e2aa45d0fc7376e81e2fdf5351200386f5732e58eb6ff4d318dc").unwrap(); - let alpha_bytes = hex::decode("8b0f85a9e7d929244b0af9a35af10717bd667b6227aae37a6d336e815fb0d850873e0d87968345a493b2d31aa8aa400d9820af1d35fa862d1b339ea1f98ac70db7faa304bff120a151a1741d782d08b8f1c1080d4d2f3ebee63ac6cadc666605be306de0973be38fbbf0f54b476bbb002a74ff9506a2b9b9a34b99bfa7481a84a2c9face7065c19d7069cc5738c5350b886a5eeebe656499d2ffb360afc7aff20fa9ee689fb8b46863e90c85224e8f597bf323ad4efb02ee96eb40221fc89918a2c740eabd2886476c7f247a3eb34f0106b3b51cf040e2cdcafea68b0d8eecabf58b5aa2ece3d86259cf2dfa3efab1170c6eb11948826def533849b68335d76d60f3e16bb5c629b1c24df2bdd1a7f13c754d7fe38617ecd7783504e4615e5c13168185cc08de8d63a0f7032ab7e82ff78cf0bc46a84c98f2d95bb5af355cbbe525c44d5c1549c169dfe119a219dbf9038ec73729d187bd0e3ed369e4a2ec2be837f3dcfd958aea7110627d2c0192d262f17e722509c17196005b646a556cf010ef9bd2a2a9b937516a5ecdee516e77d14278e96bc891b630fc833dda714343554ae127c49460416430b7d4f048d08618058335dec0728ad37d10dd9d859c385a38673e71cc98e8439da0accc29de5c92d3c3dc98e199361e9f7558e8b0a2a315ccc5a72f54551f07fad6f6f4615af498aba98aea01a13a4eb84667fd87ee9782b1d812a03f8814f042823a7701238d0fec1e7dec2a26ffea00330b5c7930e95138381435d2a59f51313a48624e30b0a685e357874d41a0a19d83f7420c1d9c04").unwrap(); - let gamma_bytes = hex::decode("b675d1ff988116d1f2965d3c0c373569b74d0a1762ea7c4f4635faa5b5a8fa198a2a2ce6153f390a658dc9ad01a415491747e9de7d5f493f59cf05a52eb46eaac397ffc47aef1396cf0d8b75d0664077ea328ad6b63284b42972a8f11c523a60").unwrap(); - let delta_bytes = hex::decode("8229cb9443ef1fb72887f917f500e2aef998717d91857bcb92061ecd74d1d24c2b2b282736e8074e4316939b4c9853c117aa08ed49206860d648818b2cccb526585f5790161b1730d39c73603b482424a27bba891aaa6d99f3025d3df2a6bd42").unwrap(); - - let inputs_bytes = - hex::decode("440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849").unwrap(); - let proof_bytes = hex::decode("a29981304df8e0f50750b558d4de59dbc8329634b81c986e28e9fff2b0faa52333b14a1f7b275b029e13499d1f5dd8ab955cf5fa3000a097920180381a238ce12df52207597eade4a365a6872c0a19a39c08a9bfb98b69a15615f90cc32660180ca32e565c01a49b505dd277713b1eae834df49643291a3601b11f56957bde02d5446406d0e4745d1bd32c8ccb8d8e80b877712f5f373016d2ecdeebb58caebc7a425b8137ebb1bd0c5b81c1d48151b25f0f24fe9602ba4e403811fb17db6f14").unwrap(); - - // Success case - assert!(verify_groth16_in_bytes( - &vk_bytes, - &alpha_bytes, - &gamma_bytes, - &delta_bytes, - &inputs_bytes, - &proof_bytes - ) - .unwrap()); - - // Invalid public inputs bytes. - let invalid_inputs = hex::decode("cf").unwrap(); - assert!(verify_groth16_in_bytes( - &alpha_bytes, - &gamma_bytes, - &delta_bytes, - &inputs_bytes, - &invalid_inputs, - &proof_bytes - ) - .is_err()); - - // Invalid proof bytes. - let invalid_proof = hex::decode("4a").unwrap(); - assert!(verify_groth16_in_bytes( - &alpha_bytes, - &gamma_bytes, - &delta_bytes, - &inputs_bytes, - &inputs_bytes, - &invalid_proof - ) - .is_err()); - - // Invalid prepared verifying key. - vk_bytes.pop(); - assert!(verify_groth16_in_bytes( - &vk_bytes, - &alpha_bytes, - &gamma_bytes, - &delta_bytes, - &inputs_bytes, - &proof_bytes - ) - .is_err()); -} diff --git a/fastcrypto-zkp/src/bls12381/conversions.rs b/fastcrypto-zkp/src/bls12381/conversions.rs deleted file mode 100644 index ff37a99f1e..0000000000 --- a/fastcrypto-zkp/src/bls12381/conversions.rs +++ /dev/null @@ -1,193 +0,0 @@ -use fastcrypto::error::FastCryptoResult; -use fastcrypto::groups::bls12381::{FP_BYTE_LENGTH, GT_ELEMENT_BYTE_LENGTH, GTElement, Scalar, SCALAR_LENGTH}; -use fastcrypto::serde_helpers::ToFromByteArray; - -use crate::groth16::api::{FromLittleEndianByteArray, GTSerialize}; - -impl FromLittleEndianByteArray for Scalar { - fn from_little_endian_byte_array(bytes: &[u8; SCALAR_LENGTH]) -> FastCryptoResult { - let mut reversed = *bytes; - reversed.reverse(); - Scalar::from_byte_array(&reversed) - } -} - -impl GTSerialize for GTElement { - fn to_arkworks_bytes(&self) -> [u8; GT_ELEMENT_BYTE_LENGTH] { - gt_element_to_arkworks(&self.to_byte_array()) - } - - fn from_arkworks_bytes(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> FastCryptoResult { - GTElement::from_byte_array(&arkworks_to_gt_element(bytes)) - } -} - -// An element in the quadratic extension of Fp consists of two field elements. -const FP_EXTENSION_BYTE_LENGTH: usize = 2 * FP_BYTE_LENGTH; - -/// Reorder the six field extensions in a GT element according to the given permutation. -/// This functions panics if one of the elements in the permutation is larger than 5. -fn permute_elements( - bytes: &[u8; 6 * FP_EXTENSION_BYTE_LENGTH], - permutation: &[usize; 6], -) -> [u8; 6 * FP_EXTENSION_BYTE_LENGTH] { - // TODO: This could be done in-place to avoid allocating a new array. - let mut result = [0u8; 12 * FP_BYTE_LENGTH]; - for (from, to) in permutation.iter().enumerate() { - let from = from * FP_EXTENSION_BYTE_LENGTH; - let to = to * FP_EXTENSION_BYTE_LENGTH; - result[to..to + FP_EXTENSION_BYTE_LENGTH] - .copy_from_slice(&bytes[from..from + FP_EXTENSION_BYTE_LENGTH]); - } - result - .chunks_exact_mut(FP_BYTE_LENGTH) - .for_each(|chunk| chunk.reverse()); - result -} - -/// Given a serialization of a arkworks [`PairingOutput`] element, this function returns a -/// serialization of the corresponding [`GTElement`] element. It is _not_ verified whether the input -/// is a valid serialization. -fn arkworks_to_gt_element(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> [u8; GT_ELEMENT_BYTE_LENGTH] { - // This permutation flips the order of the i in 0..3 and j in 0..2 loops and may be computed as: - // for i in 0..3 { - // for j in 0..2 { - // PERMUTATION[i + j * 3] = i * 2 + j; - // } - // } - const PERMUTATION: [usize; 6] = [0, 2, 4, 1, 3, 5]; - permute_elements(bytes, &PERMUTATION) -} - -/// Given a serialization of a [`GTElement`], this function returns a serialization of the -/// corresponding element as a arkworks [`PairingOutput`] type. It is _not_ verified whether the -/// input is a valid serialization. -fn gt_element_to_arkworks(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> [u8; GT_ELEMENT_BYTE_LENGTH] { - // This is the inverse of the permutation in `arkworks_to_gt_element`. - const PERMUTATION: [usize; 6] = [0, 3, 1, 4, 2, 5]; - permute_elements(bytes, &PERMUTATION) -} - -#[cfg(test)] -mod tests { - use ark_bls12_381::{Bls12_381, G1Projective, G2Projective}; - use ark_ec::Group; - use ark_ec::pairing::PairingOutput; - use ark_ff::Zero; - use ark_serialize::CanonicalSerialize; - use fastcrypto::error::FastCryptoError; - - use fastcrypto::groups::bls12381::{G1_ELEMENT_BYTE_LENGTH, G1Element, G2_ELEMENT_BYTE_LENGTH, G2Element, GT_ELEMENT_BYTE_LENGTH, GTElement}; - use fastcrypto::groups::GroupElement; - use fastcrypto::serde_helpers::ToFromByteArray; - use crate::bls12381::conversions::{arkworks_to_gt_element, gt_element_to_arkworks}; - use crate::groth16::api::GTSerialize; - - #[test] - fn test_gt_element_conversion() { - let generator = PairingOutput::::generator(); - let mut arkworks_bytes = Vec::new(); - let mut uncompressed_bytes = Vec::new(); - - // GT elements cannot be compressed, so compressed and uncompressed serialization should be the same. - generator.serialize_compressed(&mut arkworks_bytes).unwrap(); - generator - .serialize_uncompressed(&mut uncompressed_bytes) - .unwrap(); - assert_eq!(arkworks_bytes, uncompressed_bytes); - - // The arkworks serialization does not match the GroupElement serialization. - let fc_bytes = GTElement::generator().to_byte_array(); - assert_eq!(arkworks_bytes.len(), fc_bytes.len()); - assert_ne!(arkworks_bytes, fc_bytes); - - // After conversion, the arkworks serialization should match the GroupElement serialization. - assert_eq!(arkworks_bytes, gt_element_to_arkworks(&fc_bytes)); - assert_eq!( - arkworks_to_gt_element(&arkworks_bytes.try_into().unwrap()), - fc_bytes - ); - - // Compare serializations of the identity element - let arkworks_id = PairingOutput::::zero(); - let mut arkworks_bytes = Vec::new(); - arkworks_id - .serialize_uncompressed(&mut arkworks_bytes) - .unwrap(); - let fc_bytes = GTElement::zero().to_byte_array(); - assert_ne!(&fc_bytes.to_vec(), &arkworks_bytes); - assert_eq!(>_element_to_arkworks(&fc_bytes).to_vec(), &arkworks_bytes); - assert_eq!( - arkworks_to_gt_element(&arkworks_bytes.try_into().unwrap()), - fc_bytes - ); - } - - fn test_arkworks_compatability_for_group_element< - const SIZE: usize, - G: GroupElement, - A: Group + CanonicalSerialize, - >( - g: G, - a: A, - serializer: fn(G) -> [u8; SIZE], - deserializer: fn(&[u8; SIZE]) -> Result, - ) { - let bytes = serializer(g); - let mut arkworks_bytes = Vec::new(); - a.serialize_compressed(&mut arkworks_bytes).unwrap(); - assert_eq!(bytes.to_vec(), arkworks_bytes); - assert_eq!(bytes.len(), SIZE); - - let g2 = deserializer(&bytes).unwrap(); - assert_eq!(g, g2); - - let a2 = A::deserialize_compressed(&arkworks_bytes[..]).unwrap(); - assert_eq!(a, a2); - } - - fn test_arkworks_compatability_for_group< - const SIZE: usize, - G: GroupElement + ToFromByteArray, - A: Group + CanonicalSerialize, - >( - serializer: fn(G) -> [u8; SIZE], - deserializer: fn(&[u8; SIZE]) -> Result, - ) { - test_arkworks_compatability_for_group_element::( - G::zero(), - A::zero(), - serializer, - deserializer, - ); - test_arkworks_compatability_for_group_element::( - G::generator(), - A::generator(), - serializer, - deserializer, - ); - let scalar = 12345u128; - test_arkworks_compatability_for_group_element::( - G::generator() * G::ScalarType::from(scalar), - A::generator() * A::ScalarField::from(scalar), - serializer, - deserializer, - ); - } - - #[test] - fn test_arkworks_compatability() { - test_arkworks_compatability_for_group::( - |g| g.to_byte_array(), - G1Element::from_byte_array, - ); - test_arkworks_compatability_for_group::( - |g| g.to_byte_array(), - G2Element::from_byte_array, - ); - test_arkworks_compatability_for_group::>( - |g| g.to_arkworks_bytes(), - GTElement::from_arkworks_bytes, - ); - } -} From e36e211f5b3abb9e20f9cb5bc1ea8a65aa1eaa7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Mon, 15 Apr 2024 14:31:16 +0200 Subject: [PATCH 20/30] Scalar from le-bytes test# --- fastcrypto-zkp/src/bls12381/api/conversions.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/fastcrypto-zkp/src/bls12381/api/conversions.rs b/fastcrypto-zkp/src/bls12381/api/conversions.rs index bd26d9e761..2d65d59aa8 100644 --- a/fastcrypto-zkp/src/bls12381/api/conversions.rs +++ b/fastcrypto-zkp/src/bls12381/api/conversions.rs @@ -89,6 +89,7 @@ mod tests { use ark_ff::Zero; use ark_serialize::CanonicalSerialize; use fastcrypto::error::FastCryptoError; + use num_bigint::BigUint; use crate::bls12381::api::conversions::{arkworks_to_gt_element, gt_element_to_arkworks}; use crate::groth16::api::{FromLittleEndianByteArray, GTSerialize}; From dc9d4a66d577bc71195a8c8dbf096626639d8308 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Mon, 15 Apr 2024 14:38:01 +0200 Subject: [PATCH 21/30] Clean up --- fastcrypto-zkp/src/bls12381/api/conversions.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/fastcrypto-zkp/src/bls12381/api/conversions.rs b/fastcrypto-zkp/src/bls12381/api/conversions.rs index 2d65d59aa8..bd26d9e761 100644 --- a/fastcrypto-zkp/src/bls12381/api/conversions.rs +++ b/fastcrypto-zkp/src/bls12381/api/conversions.rs @@ -89,7 +89,6 @@ mod tests { use ark_ff::Zero; use ark_serialize::CanonicalSerialize; use fastcrypto::error::FastCryptoError; - use num_bigint::BigUint; use crate::bls12381::api::conversions::{arkworks_to_gt_element, gt_element_to_arkworks}; use crate::groth16::api::{FromLittleEndianByteArray, GTSerialize}; From 62d2f7bb3285ccb3fd525378bc4e8f7ef0b92aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 16 Apr 2024 10:26:00 +0200 Subject: [PATCH 22/30] Add multi-pairing test --- fastcrypto/src/tests/bls12381_group_tests.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fastcrypto/src/tests/bls12381_group_tests.rs b/fastcrypto/src/tests/bls12381_group_tests.rs index e4f494c220..636e03658a 100644 --- a/fastcrypto/src/tests/bls12381_group_tests.rs +++ b/fastcrypto/src/tests/bls12381_group_tests.rs @@ -329,6 +329,21 @@ fn test_pairing_and_hash_to_curve() { let sig2 = e2 * sk2; assert_eq!(pk2.pairing(&e2), G1Element::generator().pairing(&sig2)); + // Test multi-pairing + assert!(G1Element::multi_pairing(&[], &[pk1]).is_err()); + assert_eq!( + G1Element::multi_pairing(&[], &[]).unwrap(), + GTElement::zero() + ); + assert_eq!( + G1Element::multi_pairing(&[e1], &[pk1]).unwrap(), + e1.pairing(&pk1) + ); + assert_eq!( + G1Element::multi_pairing(&[e1, pk2], &[pk1, e2]).unwrap(), + e1.pairing(&pk1) + pk2.pairing(&e2) + ); + assert_eq!( G1Element::zero().pairing(&G2Element::zero()), GTElement::zero() From aa46e464e7a452ae53c9c2859c8a1c02b863e14a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 16 Apr 2024 12:11:47 +0200 Subject: [PATCH 23/30] multi-pairing tests --- fastcrypto/src/tests/bls12381_group_tests.rs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/fastcrypto/src/tests/bls12381_group_tests.rs b/fastcrypto/src/tests/bls12381_group_tests.rs index 636e03658a..e4f494c220 100644 --- a/fastcrypto/src/tests/bls12381_group_tests.rs +++ b/fastcrypto/src/tests/bls12381_group_tests.rs @@ -329,21 +329,6 @@ fn test_pairing_and_hash_to_curve() { let sig2 = e2 * sk2; assert_eq!(pk2.pairing(&e2), G1Element::generator().pairing(&sig2)); - // Test multi-pairing - assert!(G1Element::multi_pairing(&[], &[pk1]).is_err()); - assert_eq!( - G1Element::multi_pairing(&[], &[]).unwrap(), - GTElement::zero() - ); - assert_eq!( - G1Element::multi_pairing(&[e1], &[pk1]).unwrap(), - e1.pairing(&pk1) - ); - assert_eq!( - G1Element::multi_pairing(&[e1, pk2], &[pk1, e2]).unwrap(), - e1.pairing(&pk1) + pk2.pairing(&e2) - ); - assert_eq!( G1Element::zero().pairing(&G2Element::zero()), GTElement::zero() From b76cef3836f954aecc9165e6e8c7d985567f5344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 9 Apr 2024 11:44:37 +0200 Subject: [PATCH 24/30] Add bn254 group --- Cargo.lock | 1 + fastcrypto/Cargo.toml | 1 + fastcrypto/src/groups/bn254.rs | 310 ++++++++++++++++++ fastcrypto/src/groups/mod.rs | 1 + fastcrypto/src/lib.rs | 4 + fastcrypto/src/tests/bn254_group_tests.rs | 366 ++++++++++++++++++++++ 6 files changed, 683 insertions(+) create mode 100644 fastcrypto/src/groups/bn254.rs create mode 100644 fastcrypto/src/tests/bn254_group_tests.rs diff --git a/Cargo.lock b/Cargo.lock index 30ba961a2c..e41e4d0938 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1317,6 +1317,7 @@ version = "0.1.8" dependencies = [ "aes", "aes-gcm", + "ark-bn254", "ark-ec", "ark-ff", "ark-secp256r1", diff --git a/fastcrypto/Cargo.toml b/fastcrypto/Cargo.toml index e3b1560570..9977c6a9e6 100644 --- a/fastcrypto/Cargo.toml +++ b/fastcrypto/Cargo.toml @@ -45,6 +45,7 @@ elliptic-curve = { version = "0.13.2", features = ["hash2curve"] } rsa = { version = "0.8.2", features = ["sha2"] } static_assertions = "1.1.0" ark-secp256r1 = "0.4.0" +ark-bn254 = "0.4.0" ark-ec = "0.4.1" ark-ff = "0.4.1" ark-serialize = "0.4.1" diff --git a/fastcrypto/src/groups/bn254.rs b/fastcrypto/src/groups/bn254.rs new file mode 100644 index 0000000000..2f8ab6c64a --- /dev/null +++ b/fastcrypto/src/groups/bn254.rs @@ -0,0 +1,310 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::fmt::Debug; +use std::ops::{Div, Mul}; + +use ark_bn254::{Bn254, Fr, G1Affine, G1Projective, G2Affine, G2Projective}; +use ark_ec::pairing::{Pairing as ArkworksPairing, PairingOutput}; +use ark_ec::{AffineRepr, Group}; +use ark_ff::{Field, One, UniformRand, Zero}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use derive_more::{Add, Neg, Sub}; +use once_cell::sync::OnceCell; +use serde::{de, Deserialize}; + +use fastcrypto_derive::GroupOpsExtend; + +use crate::error::{FastCryptoError, FastCryptoResult}; +use crate::groups::{FromTrustedByteArray, GroupElement, Pairing, Scalar as ScalarType}; +use crate::serde_helpers::ToFromByteArray; +use crate::traits::AllowedRng; +use crate::{groups, serialize_deserialize_with_to_from_byte_array}; + +/// Elements of the group G_1 in BN254. +#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)] +#[repr(transparent)] +pub struct G1Element(G1Projective); + +/// Elements of the group G_2 in BN254. +#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)] +#[repr(transparent)] +pub struct G2Element(G2Projective); + +/// Elements of the subgroup G_T of F_q^{12} in BN254. Note that it is written in additive notation here. +#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)] +pub struct GTElement(PairingOutput); + +/// This represents a scalar modulo r = 21888242871839275222246405745257275088548364400416034343698204186575808495617 +/// which is the order of the groups G1, G2 and GT. Note that r is a 254 bit prime. +#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)] +pub struct Scalar(Fr); + +pub const SCALAR_LENGTH: usize = 32; +pub const G1_ELEMENT_BYTE_LENGTH: usize = 32; +pub const G2_ELEMENT_BYTE_LENGTH: usize = 64; +pub const GT_ELEMENT_BYTE_LENGTH: usize = 384; + +impl Div for Scalar { + type Output = FastCryptoResult; + + fn div(self, rhs: Self) -> FastCryptoResult { + if rhs.0.is_zero() { + return Err(FastCryptoError::InvalidInput); + } + Ok(Self(self.0.div(rhs.0))) + } +} + +impl Mul for Scalar { + type Output = Self; + + fn mul(self, rhs: Scalar) -> Self::Output { + Self(self.0.mul(rhs.0)) + } +} + +impl GroupElement for Scalar { + type ScalarType = Scalar; + + fn zero() -> Self { + Self(Fr::zero()) + } + + fn generator() -> Self { + Self(Fr::one()) + } +} + +impl GroupElement for G1Element { + type ScalarType = Scalar; + + fn zero() -> Self { + G1Element(G1Projective::zero()) + } + + fn generator() -> Self { + G1Element(G1Projective::generator()) + } +} + +impl Div for G1Element { + type Output = FastCryptoResult; + + fn div(self, rhs: Scalar) -> Self::Output { + let inverse = rhs.inverse()?; + Ok(self.mul(inverse)) + } +} + +impl Mul for G1Element { + type Output = Self; + + fn mul(self, rhs: Scalar) -> Self::Output { + Self(self.0.mul(rhs.0)) + } +} + +impl ToFromByteArray<32> for G1Element { + fn from_byte_array(bytes: &[u8; 32]) -> Result { + let point = G1Affine::deserialize_compressed(bytes.as_slice()) + .map_err(|_| FastCryptoError::InvalidInput)?; + + // Arkworks only checks the infinty flag, but we require all-zeros to have unique serialization + if point.is_zero() && bytes[0..31].iter().any(|x| !x.is_zero()) { + return Err(FastCryptoError::InvalidInput); + } + + Ok(Self(G1Projective::from(point))) + } + + fn to_byte_array(&self) -> [u8; 32] { + let mut bytes = [0u8; 32]; + self.0 + .serialize_compressed(bytes.as_mut_slice()) + .expect("Never fails"); + bytes + } +} + +serialize_deserialize_with_to_from_byte_array!(G1Element); + +impl FromTrustedByteArray<32> for G1Element { + fn from_trusted_byte_array(bytes: &[u8; 32]) -> FastCryptoResult { + G1Projective::deserialize_compressed_unchecked(bytes.as_slice()) + .map_err(|_| FastCryptoError::InvalidInput) + .map(G1Element) + } +} + +impl GroupElement for G2Element { + type ScalarType = Scalar; + + fn zero() -> Self { + G2Element(G2Projective::zero()) + } + + fn generator() -> Self { + G2Element(G2Projective::generator()) + } +} + +impl Div for G2Element { + type Output = FastCryptoResult; + + fn div(self, rhs: Scalar) -> Self::Output { + let inverse = rhs.inverse()?; + Ok(self.mul(inverse)) + } +} + +impl Mul for G2Element { + type Output = Self; + + fn mul(self, rhs: Scalar) -> Self::Output { + Self(self.0.mul(rhs.0)) + } +} + +impl ToFromByteArray<64> for G2Element { + fn from_byte_array(bytes: &[u8; 64]) -> Result { + let point = G2Affine::deserialize_compressed(bytes.as_slice()) + .map_err(|_| FastCryptoError::InvalidInput)?; + + // Arkworks only checks the infinty flag, but we require all-zeros to have unique serialization + if point.is_zero() && bytes[0..63].iter().any(|x| !x.is_zero()) { + return Err(FastCryptoError::InvalidInput); + } + + Ok(Self(G2Projective::from(point))) + } + + fn to_byte_array(&self) -> [u8; 64] { + let mut bytes = [0u8; 64]; + self.0 + .serialize_compressed(bytes.as_mut_slice()) + .expect("Never fails"); + bytes + } +} + +serialize_deserialize_with_to_from_byte_array!(G2Element); + +impl FromTrustedByteArray<64> for G2Element { + fn from_trusted_byte_array(bytes: &[u8; 64]) -> FastCryptoResult { + G2Projective::deserialize_compressed_unchecked(bytes.as_slice()) + .map_err(|_| FastCryptoError::InvalidInput) + .map(G2Element) + } +} + +impl From for Scalar { + fn from(value: u128) -> Self { + Self(Fr::from(value)) + } +} + +impl groups::Scalar for Scalar { + fn rand(rng: &mut R) -> Self { + Self(Fr::rand(rng)) + } + + fn inverse(&self) -> FastCryptoResult { + Ok(Self(self.0.inverse().ok_or(FastCryptoError::InvalidInput)?)) + } +} + +impl ToFromByteArray<32> for Scalar { + fn from_byte_array(bytes: &[u8; Self::BYTE_LENGTH]) -> Result { + // Arkworks uses little-endian byte order for serialization, but we use big-endian. + let mut reversed = *bytes; + reversed.reverse(); + Fr::deserialize_compressed(reversed.as_slice()) + .map_err(|_| FastCryptoError::InvalidInput) + .map(Scalar) + } + + fn to_byte_array(&self) -> [u8; Self::BYTE_LENGTH] { + let mut bytes = [0u8; Self::BYTE_LENGTH]; + self.0 + .serialize_compressed(bytes.as_mut_slice()) + .expect("Never fails"); + // Arkworks uses little-endian byte order for serialization, but we use big-endian. + bytes.reverse(); + bytes + } +} + +serialize_deserialize_with_to_from_byte_array!(Scalar); + +impl Pairing for G1Element { + type Other = G2Element; + type Output = GTElement; + + fn pairing(&self, other: &Self::Other) -> ::Output { + GTElement(Bn254::pairing(self.0, other.0)) + } +} + +#[allow(clippy::suspicious_arithmetic_impl)] +impl Div for GTElement { + type Output = FastCryptoResult; + + fn div(self, rhs: Scalar) -> Self::Output { + let inverse = rhs.inverse()?; + Ok(self * inverse) + } +} + +impl Mul for GTElement { + type Output = GTElement; + + fn mul(self, rhs: Scalar) -> Self::Output { + Self(self.0.mul(rhs.0)) + } +} + +impl GroupElement for GTElement { + type ScalarType = Scalar; + + fn zero() -> Self { + GTElement(PairingOutput::zero()) + } + + fn generator() -> Self { + static G: OnceCell> = OnceCell::new(); + Self(*G.get_or_init(Self::compute_generator)) + } +} + +impl GTElement { + fn compute_generator() -> PairingOutput { + G1Element::generator().pairing(&G2Element::generator()).0 + } +} + +impl FromTrustedByteArray<384> for GTElement { + fn from_trusted_byte_array(bytes: &[u8; 384]) -> FastCryptoResult { + PairingOutput::::deserialize_compressed_unchecked(bytes.as_ref()) + .map_err(|_| FastCryptoError::InvalidInput) + .map(GTElement) + } +} + +impl ToFromByteArray<384> for GTElement { + fn from_byte_array(bytes: &[u8; 384]) -> Result { + PairingOutput::::deserialize_compressed(bytes.as_ref()) + .map_err(|_| FastCryptoError::InvalidInput) + .map(GTElement) + } + + fn to_byte_array(&self) -> [u8; 384] { + let mut bytes = [0u8; 384]; + self.0 + .serialize_compressed(bytes.as_mut_slice()) + .expect("Never fails"); + bytes + } +} + +serialize_deserialize_with_to_from_byte_array!(GTElement); diff --git a/fastcrypto/src/groups/mod.rs b/fastcrypto/src/groups/mod.rs index e4d0f856d1..8ec1327136 100644 --- a/fastcrypto/src/groups/mod.rs +++ b/fastcrypto/src/groups/mod.rs @@ -10,6 +10,7 @@ use std::fmt::Debug; use std::ops::{AddAssign, SubAssign}; pub mod bls12381; +pub mod bn254; pub mod ristretto255; pub mod secp256r1; diff --git a/fastcrypto/src/lib.rs b/fastcrypto/src/lib.rs index df3e8967cf..81d92635fe 100644 --- a/fastcrypto/src/lib.rs +++ b/fastcrypto/src/lib.rs @@ -84,6 +84,10 @@ pub mod utils_tests; #[path = "tests/secp256r1_group_tests.rs"] pub mod secp256r1_group_tests; +#[cfg(test)] +#[path = "tests/bn254_group_tests.rs"] +pub mod bn254_group_tests; + pub mod traits; #[cfg(feature = "aes")] diff --git a/fastcrypto/src/tests/bn254_group_tests.rs b/fastcrypto/src/tests/bn254_group_tests.rs new file mode 100644 index 0000000000..293899c7db --- /dev/null +++ b/fastcrypto/src/tests/bn254_group_tests.rs @@ -0,0 +1,366 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use ark_bn254::{G1Affine, G2Affine}; +use ark_ec::AffineRepr; +use ark_serialize::CanonicalSerialize; +use rand::thread_rng; + +use crate::groups::bn254::{G1Element, G2Element, GTElement, Scalar}; +use crate::groups::{FromTrustedByteArray, GroupElement, Pairing, Scalar as ScalarTrait}; +use crate::serde_helpers::ToFromByteArray; +use crate::test_helpers::verify_serialization; + +#[test] +fn test_scalar_arithmetic() { + let zero = Scalar::zero(); + let one = Scalar::generator(); + + assert_eq!(zero, zero - zero); + assert_eq!(zero, -zero); + + let four = one + zero + one + one + one; + assert_eq!(four, Scalar::from(4)); + + let three = four - one; + assert_eq!(three, one + one + one); + + let six = three * Scalar::from(2); + assert_eq!(six, Scalar::from(6)); + + let two = (six / three).unwrap(); + assert_eq!(two, Scalar::from(2)); + + assert!((six / zero).is_err()); + + let inv_two = two.inverse().unwrap(); + assert_eq!(inv_two * two, one); + + // Check that u128 is decoded correctly. + let x: u128 = 2 << 66; + let x_scalar = Scalar::from(x); + let res = x_scalar / Scalar::from(8); + assert_eq!(res.unwrap(), Scalar::from(2 << 63)); +} + +#[test] +fn test_g1_arithmetic() { + // Test that different ways of computing [5]G gives the expected result + let g = G1Element::generator(); + + let p1 = g * Scalar::from(5); + + let p2 = g + g + g + g + g; + assert_eq!(p1, p2); + + let mut p3 = G1Element::zero(); + p3 += p2; + assert_eq!(p1, p3); + + let mut p4 = g; + p4 *= Scalar::from(5); + assert_eq!(p1, p4); + + let p5 = g * (Scalar::from(7) - Scalar::from(2)); + assert_eq!(p1, p5); + + let p6 = g * Scalar::zero(); + assert_eq!(G1Element::zero(), p6); + + let sc = Scalar::rand(&mut thread_rng()); + let p7 = g * sc; + assert_eq!(p7 * Scalar::from(1), p7); + + assert_ne!(G1Element::zero(), g); + assert_eq!(G1Element::zero(), g - g); + + assert!((G1Element::generator() / Scalar::zero()).is_err()); + assert_eq!((p5 / Scalar::from(5)).unwrap(), g); + + let identity = G1Element::zero(); + assert_eq!(identity, identity - identity); + assert_eq!(identity, -identity); +} + +#[test] +fn test_g2_arithmetic() { + // Test that different ways of computing [5]G gives the expected result + let g = G2Element::generator(); + + let p1 = g * Scalar::from(5); + + let p2 = g + g + g + g + g + g - g; + assert_eq!(p1, p2); + + let mut p3 = G2Element::zero(); + p3 += p2; + assert_eq!(p1, p3); + + let mut p4 = g; + p4 *= Scalar::from(5); + assert_eq!(p1, p4); + + let p5 = g * (Scalar::from(7) - Scalar::from(2)); + assert_eq!(p1, p5); + + let p6 = g * Scalar::zero(); + assert_eq!(G2Element::zero(), p6); + + let sc = Scalar::rand(&mut thread_rng()); + let p7 = g * sc; + assert_eq!(p7 * Scalar::from(1), p7); + + assert!((G2Element::generator() / Scalar::zero()).is_err()); + assert_eq!((p5 / Scalar::from(5)).unwrap(), g); + + assert_ne!(G2Element::zero(), g); + assert_eq!(G2Element::zero(), g - g); + + let identity = G2Element::zero(); + assert_eq!(identity, identity - identity); + assert_eq!(identity, -identity); +} + +#[test] +fn test_gt_arithmetic() { + // Test that different ways of computing [5]G gives the expected result + let g = GTElement::generator(); + + let p1 = g * Scalar::from(5); + + let p2 = g + g + g + g + g + g - g; + assert_eq!(p1, p2); + + let mut p3 = GTElement::zero(); + p3 += p2; + assert_eq!(p1, p3); + + let mut p4 = g; + p4 *= Scalar::from(5); + assert_eq!(p1, p4); + + let p5 = g * (Scalar::from(7) - Scalar::from(2)); + assert_eq!(p1, p5); + + let p6 = g * Scalar::zero(); + assert_eq!(GTElement::zero(), p6); + + let sc = Scalar::rand(&mut thread_rng()); + let p7 = g * sc; + assert_eq!(p7 * Scalar::from(1), p7); + + assert_ne!(GTElement::zero(), g); + assert_eq!(GTElement::zero(), g - g); + assert_eq!(GTElement::zero(), GTElement::zero() - GTElement::zero()); + + assert!((GTElement::generator() / Scalar::zero()).is_err()); + assert_eq!((p5 / Scalar::from(5)).unwrap(), g); +} + +#[test] +fn test_pairing() { + let a = Scalar::rand(&mut thread_rng()); + + assert_eq!( + G1Element::pairing(&(G1Element::generator() * a), &G2Element::generator()), + G1Element::pairing(&G1Element::generator(), &(G2Element::generator() * a)) + ); +} + +#[test] +fn test_serde_and_regression() { + let s1 = Scalar::generator(); + let g1 = G1Element::generator(); + let g2 = G2Element::generator(); + let gt = GTElement::generator(); + let id1 = G1Element::zero(); + let id2 = G2Element::zero(); + let id3 = GTElement::zero(); + let id4 = Scalar::zero(); + + verify_serialization( + &s1, + Some( + hex::decode("0000000000000000000000000000000000000000000000000000000000000001") + .unwrap() + .as_slice(), + ), + ); + verify_serialization( + &g1, + Some( + hex::decode("0100000000000000000000000000000000000000000000000000000000000000") + .unwrap() + .as_slice(), + ), + ); + verify_serialization(&g2, Some(hex::decode("edf692d95cbdde46ddda5ef7d422436779445c5e66006a42761e1f12efde0018c212f3aeb785e49712e7a9353349aaf1255dfb31b7bf60723a480d9293938e19").unwrap().as_slice())); + verify_serialization(>, Some(hex::decode("950e879d73631f5eb5788589eb5f7ef8d63e0a28de1ba00dfe4ca9ed3f252b264a8afb8eb4349db466ed1809ea4d7c39bdab7938821f1b0a00a295c72c2de002e01dbdfd0254134efcb1ec877395d25f937719b344adb1a58d129be2d6f2a9132b16a16e8ab030b130e69c69bd20b4c45986e6744a98314b5c1a0f50faa90b04dbaf9ef8aeeee3f50be31c210b598f4752f073987f9d35be8f6770d83f2ffc0af0d18dd9d2dbcdf943825acc12a7a9ddca45e629d962c6bd64908c3930a5541cfe2924dcc5580d5cef7a4bfdec90a91b59926f850d4a7923c01a5a5dbf0f5c094a2b9fb9d415820fa6b40c59bb9eade9c953407b0fc11da350a9d872cad6d3142974ca385854afdf5f583c04231adc5957c8914b6b20dc89660ed7c3bbe7c01d972be2d53ecdb27a1bcc16ac610db95aa7d237c8ff55a898cb88645a0e32530b23d7ebf5dafdd79b0f9c2ac4ba07ce18d3d16cf36e47916c4cae5d08d3afa813972c769e8514533e380c9443b3e1ee5c96fa3a0a73f301b626454721527bf900").unwrap().as_slice())); + verify_serialization( + &id1, + Some( + hex::decode("0000000000000000000000000000000000000000000000000000000000000040") + .unwrap() + .as_slice(), + ), + ); + verify_serialization(&id2, Some(hex::decode("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040").unwrap().as_slice())); + verify_serialization(&id3, Some(hex::decode("010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap().as_slice())); + verify_serialization( + &id4, + Some( + hex::decode("0000000000000000000000000000000000000000000000000000000000000000") + .unwrap() + .as_slice(), + ), + ); +} + +#[test] +fn test_serialization_scalar() { + let bytes = [0u8; 32]; + assert_eq!(Scalar::from_byte_array(&bytes).unwrap(), Scalar::zero()); + + // Scalar::from_byte_array should not accept the order or above it. + let order = + hex::decode("30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001").unwrap(); + assert!(Scalar::from_byte_array(<&[u8; 32]>::try_from(order.as_slice()).unwrap()).is_err()); + + // Scalar::from_byte_array should accept the order - 1. + let order_minus_one = + hex::decode("30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000").unwrap(); + assert_eq!( + Scalar::from_byte_array(<&[u8; 32]>::try_from(order_minus_one.as_slice()).unwrap()) + .unwrap(), + Scalar::zero() - Scalar::generator() + ); + + for _ in 0..100 { + let s = Scalar::rand(&mut thread_rng()); + let bytes = s.to_byte_array(); + assert_eq!(s, Scalar::from_byte_array(&bytes).unwrap()); + } +} + +#[test] +fn test_serialization_g1() { + let infinity_bit = 0x40; + + // All zero serialization for G1 should fail. + let mut bytes = [0u8; 32]; + assert!(G1Element::from_byte_array(&bytes).is_err()); + + // Valid infinity + bytes[31] |= infinity_bit; + assert_eq!( + G1Element::zero(), + G1Element::from_byte_array(&bytes).unwrap() + ); + + // to and from_byte_array should be inverses. + let bytes = G1Element::generator().to_byte_array(); + assert_eq!( + G1Element::generator(), + G1Element::from_byte_array(&bytes).unwrap() + ); + + // Test correct uncompressed serialization of a point + let mut uncompressed_bytes = [0u8; 64]; + G1Affine::generator() + .serialize_uncompressed(uncompressed_bytes.as_mut_slice()) + .unwrap(); + // This works because from_byte_array the compressed format is just the first coordinate. + assert_eq!( + G1Element::generator(), + G1Element::from_byte_array(&(uncompressed_bytes[0..32].try_into().unwrap())).unwrap() + ); + + // Test FromTrustedByteArray. + let mut bytes = G1Element::generator().to_byte_array(); + let g1 = G1Element::from_trusted_byte_array(&bytes).unwrap(); + assert_eq!(g1, G1Element::generator()); + // Also when the input is not a valid point. + bytes[bytes.len() - 1] += 2; + assert!(G1Element::from_trusted_byte_array(&bytes).is_ok()); +} + +#[test] +fn test_serialization_g2() { + let infinity_bit = 0x40; + + // All zero serialization for G2 should fail. + let mut bytes = [0u8; 64]; + assert!(G2Element::from_byte_array(&bytes).is_err()); + + // Valid infinity when the right bits are set. + bytes[63] |= infinity_bit; + assert_eq!( + G2Element::zero(), + G2Element::from_byte_array(&bytes).unwrap() + ); + + // to and from_byte_array should be inverses. + let bytes = G2Element::generator().to_byte_array(); + assert_eq!( + G2Element::generator(), + G2Element::from_byte_array(&bytes).unwrap() + ); + + // Test correct uncompressed serialization of a point + let mut uncompressed_bytes = [0u8; 128]; + G2Affine::generator() + .serialize_uncompressed(uncompressed_bytes.as_mut_slice()) + .unwrap(); + + // This works because the compressed format is just the first coordinate. + assert_eq!( + G2Element::generator(), + G2Element::from_byte_array(&(uncompressed_bytes[0..64].try_into().unwrap())).unwrap() + ); + + // Test FromTrustedByteArray. + let mut bytes = G2Element::generator().to_byte_array(); + let g2 = G2Element::from_trusted_byte_array(&bytes).unwrap(); + assert_eq!(g2, G2Element::generator()); + // Also when the input is not a valid point. + bytes[bytes.len() - 1] += 2; + assert!(G2Element::from_trusted_byte_array(&bytes).is_ok()); + assert!(G2Element::from_byte_array(&bytes).is_err()); +} + +#[test] +fn test_serialization_gt() { + // All zero serialization for GT should fail. + let bytes = [0u8; 384]; + assert!(GTElement::from_byte_array(&bytes).is_err()); + + // to and from_byte_array should be inverses. + let bytes = GTElement::generator().to_byte_array(); + assert_eq!( + GTElement::generator(), + GTElement::from_byte_array(&bytes).unwrap() + ); + + // reject if one of the elements >= P + let mut bytes = GTElement::generator().to_byte_array(); + let p = hex::decode("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab").unwrap(); + let mut carry = 0; + let mut target = [0; 48]; + for i in (0..48).rev() { + let sum = (bytes[i] as u16) + (p[i] as u16) + carry; + target[i] = (sum % 256) as u8; + carry = sum / 256; + } + assert_eq!(carry, 0); + bytes[0..48].copy_from_slice(&target); + assert!(GTElement::from_byte_array(&bytes).is_err()); + + // Test FromTrustedByteArray. + let mut bytes = GTElement::generator().to_byte_array(); + let gt = GTElement::from_trusted_byte_array(&bytes).unwrap(); + assert_eq!(gt, GTElement::generator()); + // Also when the input is not a valid point. + bytes[bytes.len() - 1] += 2; + assert!(GTElement::from_trusted_byte_array(&bytes).is_ok()); + assert!(GTElement::from_byte_array(&bytes).is_err()); +} From 667be3f7f5ea72c5cd3d0ce8ce7d8add00a12c52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 9 Apr 2024 12:07:38 +0200 Subject: [PATCH 25/30] Refactor --- fastcrypto/src/groups/bn254.rs | 310 ------------------ fastcrypto/src/groups/bn254/g1.rs | 81 +++++ fastcrypto/src/groups/bn254/g2.rs | 80 +++++ fastcrypto/src/groups/bn254/gt.rs | 80 +++++ fastcrypto/src/groups/bn254/mod.rs | 56 ++++ fastcrypto/src/groups/bn254/scalar.rs | 85 +++++ .../bn254/tests.rs} | 5 +- fastcrypto/src/lib.rs | 4 - 8 files changed, 386 insertions(+), 315 deletions(-) delete mode 100644 fastcrypto/src/groups/bn254.rs create mode 100644 fastcrypto/src/groups/bn254/g1.rs create mode 100644 fastcrypto/src/groups/bn254/g2.rs create mode 100644 fastcrypto/src/groups/bn254/gt.rs create mode 100644 fastcrypto/src/groups/bn254/mod.rs create mode 100644 fastcrypto/src/groups/bn254/scalar.rs rename fastcrypto/src/{tests/bn254_group_tests.rs => groups/bn254/tests.rs} (98%) diff --git a/fastcrypto/src/groups/bn254.rs b/fastcrypto/src/groups/bn254.rs deleted file mode 100644 index 2f8ab6c64a..0000000000 --- a/fastcrypto/src/groups/bn254.rs +++ /dev/null @@ -1,310 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use std::fmt::Debug; -use std::ops::{Div, Mul}; - -use ark_bn254::{Bn254, Fr, G1Affine, G1Projective, G2Affine, G2Projective}; -use ark_ec::pairing::{Pairing as ArkworksPairing, PairingOutput}; -use ark_ec::{AffineRepr, Group}; -use ark_ff::{Field, One, UniformRand, Zero}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use derive_more::{Add, Neg, Sub}; -use once_cell::sync::OnceCell; -use serde::{de, Deserialize}; - -use fastcrypto_derive::GroupOpsExtend; - -use crate::error::{FastCryptoError, FastCryptoResult}; -use crate::groups::{FromTrustedByteArray, GroupElement, Pairing, Scalar as ScalarType}; -use crate::serde_helpers::ToFromByteArray; -use crate::traits::AllowedRng; -use crate::{groups, serialize_deserialize_with_to_from_byte_array}; - -/// Elements of the group G_1 in BN254. -#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)] -#[repr(transparent)] -pub struct G1Element(G1Projective); - -/// Elements of the group G_2 in BN254. -#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)] -#[repr(transparent)] -pub struct G2Element(G2Projective); - -/// Elements of the subgroup G_T of F_q^{12} in BN254. Note that it is written in additive notation here. -#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)] -pub struct GTElement(PairingOutput); - -/// This represents a scalar modulo r = 21888242871839275222246405745257275088548364400416034343698204186575808495617 -/// which is the order of the groups G1, G2 and GT. Note that r is a 254 bit prime. -#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)] -pub struct Scalar(Fr); - -pub const SCALAR_LENGTH: usize = 32; -pub const G1_ELEMENT_BYTE_LENGTH: usize = 32; -pub const G2_ELEMENT_BYTE_LENGTH: usize = 64; -pub const GT_ELEMENT_BYTE_LENGTH: usize = 384; - -impl Div for Scalar { - type Output = FastCryptoResult; - - fn div(self, rhs: Self) -> FastCryptoResult { - if rhs.0.is_zero() { - return Err(FastCryptoError::InvalidInput); - } - Ok(Self(self.0.div(rhs.0))) - } -} - -impl Mul for Scalar { - type Output = Self; - - fn mul(self, rhs: Scalar) -> Self::Output { - Self(self.0.mul(rhs.0)) - } -} - -impl GroupElement for Scalar { - type ScalarType = Scalar; - - fn zero() -> Self { - Self(Fr::zero()) - } - - fn generator() -> Self { - Self(Fr::one()) - } -} - -impl GroupElement for G1Element { - type ScalarType = Scalar; - - fn zero() -> Self { - G1Element(G1Projective::zero()) - } - - fn generator() -> Self { - G1Element(G1Projective::generator()) - } -} - -impl Div for G1Element { - type Output = FastCryptoResult; - - fn div(self, rhs: Scalar) -> Self::Output { - let inverse = rhs.inverse()?; - Ok(self.mul(inverse)) - } -} - -impl Mul for G1Element { - type Output = Self; - - fn mul(self, rhs: Scalar) -> Self::Output { - Self(self.0.mul(rhs.0)) - } -} - -impl ToFromByteArray<32> for G1Element { - fn from_byte_array(bytes: &[u8; 32]) -> Result { - let point = G1Affine::deserialize_compressed(bytes.as_slice()) - .map_err(|_| FastCryptoError::InvalidInput)?; - - // Arkworks only checks the infinty flag, but we require all-zeros to have unique serialization - if point.is_zero() && bytes[0..31].iter().any(|x| !x.is_zero()) { - return Err(FastCryptoError::InvalidInput); - } - - Ok(Self(G1Projective::from(point))) - } - - fn to_byte_array(&self) -> [u8; 32] { - let mut bytes = [0u8; 32]; - self.0 - .serialize_compressed(bytes.as_mut_slice()) - .expect("Never fails"); - bytes - } -} - -serialize_deserialize_with_to_from_byte_array!(G1Element); - -impl FromTrustedByteArray<32> for G1Element { - fn from_trusted_byte_array(bytes: &[u8; 32]) -> FastCryptoResult { - G1Projective::deserialize_compressed_unchecked(bytes.as_slice()) - .map_err(|_| FastCryptoError::InvalidInput) - .map(G1Element) - } -} - -impl GroupElement for G2Element { - type ScalarType = Scalar; - - fn zero() -> Self { - G2Element(G2Projective::zero()) - } - - fn generator() -> Self { - G2Element(G2Projective::generator()) - } -} - -impl Div for G2Element { - type Output = FastCryptoResult; - - fn div(self, rhs: Scalar) -> Self::Output { - let inverse = rhs.inverse()?; - Ok(self.mul(inverse)) - } -} - -impl Mul for G2Element { - type Output = Self; - - fn mul(self, rhs: Scalar) -> Self::Output { - Self(self.0.mul(rhs.0)) - } -} - -impl ToFromByteArray<64> for G2Element { - fn from_byte_array(bytes: &[u8; 64]) -> Result { - let point = G2Affine::deserialize_compressed(bytes.as_slice()) - .map_err(|_| FastCryptoError::InvalidInput)?; - - // Arkworks only checks the infinty flag, but we require all-zeros to have unique serialization - if point.is_zero() && bytes[0..63].iter().any(|x| !x.is_zero()) { - return Err(FastCryptoError::InvalidInput); - } - - Ok(Self(G2Projective::from(point))) - } - - fn to_byte_array(&self) -> [u8; 64] { - let mut bytes = [0u8; 64]; - self.0 - .serialize_compressed(bytes.as_mut_slice()) - .expect("Never fails"); - bytes - } -} - -serialize_deserialize_with_to_from_byte_array!(G2Element); - -impl FromTrustedByteArray<64> for G2Element { - fn from_trusted_byte_array(bytes: &[u8; 64]) -> FastCryptoResult { - G2Projective::deserialize_compressed_unchecked(bytes.as_slice()) - .map_err(|_| FastCryptoError::InvalidInput) - .map(G2Element) - } -} - -impl From for Scalar { - fn from(value: u128) -> Self { - Self(Fr::from(value)) - } -} - -impl groups::Scalar for Scalar { - fn rand(rng: &mut R) -> Self { - Self(Fr::rand(rng)) - } - - fn inverse(&self) -> FastCryptoResult { - Ok(Self(self.0.inverse().ok_or(FastCryptoError::InvalidInput)?)) - } -} - -impl ToFromByteArray<32> for Scalar { - fn from_byte_array(bytes: &[u8; Self::BYTE_LENGTH]) -> Result { - // Arkworks uses little-endian byte order for serialization, but we use big-endian. - let mut reversed = *bytes; - reversed.reverse(); - Fr::deserialize_compressed(reversed.as_slice()) - .map_err(|_| FastCryptoError::InvalidInput) - .map(Scalar) - } - - fn to_byte_array(&self) -> [u8; Self::BYTE_LENGTH] { - let mut bytes = [0u8; Self::BYTE_LENGTH]; - self.0 - .serialize_compressed(bytes.as_mut_slice()) - .expect("Never fails"); - // Arkworks uses little-endian byte order for serialization, but we use big-endian. - bytes.reverse(); - bytes - } -} - -serialize_deserialize_with_to_from_byte_array!(Scalar); - -impl Pairing for G1Element { - type Other = G2Element; - type Output = GTElement; - - fn pairing(&self, other: &Self::Other) -> ::Output { - GTElement(Bn254::pairing(self.0, other.0)) - } -} - -#[allow(clippy::suspicious_arithmetic_impl)] -impl Div for GTElement { - type Output = FastCryptoResult; - - fn div(self, rhs: Scalar) -> Self::Output { - let inverse = rhs.inverse()?; - Ok(self * inverse) - } -} - -impl Mul for GTElement { - type Output = GTElement; - - fn mul(self, rhs: Scalar) -> Self::Output { - Self(self.0.mul(rhs.0)) - } -} - -impl GroupElement for GTElement { - type ScalarType = Scalar; - - fn zero() -> Self { - GTElement(PairingOutput::zero()) - } - - fn generator() -> Self { - static G: OnceCell> = OnceCell::new(); - Self(*G.get_or_init(Self::compute_generator)) - } -} - -impl GTElement { - fn compute_generator() -> PairingOutput { - G1Element::generator().pairing(&G2Element::generator()).0 - } -} - -impl FromTrustedByteArray<384> for GTElement { - fn from_trusted_byte_array(bytes: &[u8; 384]) -> FastCryptoResult { - PairingOutput::::deserialize_compressed_unchecked(bytes.as_ref()) - .map_err(|_| FastCryptoError::InvalidInput) - .map(GTElement) - } -} - -impl ToFromByteArray<384> for GTElement { - fn from_byte_array(bytes: &[u8; 384]) -> Result { - PairingOutput::::deserialize_compressed(bytes.as_ref()) - .map_err(|_| FastCryptoError::InvalidInput) - .map(GTElement) - } - - fn to_byte_array(&self) -> [u8; 384] { - let mut bytes = [0u8; 384]; - self.0 - .serialize_compressed(bytes.as_mut_slice()) - .expect("Never fails"); - bytes - } -} - -serialize_deserialize_with_to_from_byte_array!(GTElement); diff --git a/fastcrypto/src/groups/bn254/g1.rs b/fastcrypto/src/groups/bn254/g1.rs new file mode 100644 index 0000000000..fcb0ab84b0 --- /dev/null +++ b/fastcrypto/src/groups/bn254/g1.rs @@ -0,0 +1,81 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::ops::{Div, Mul}; + +use crate::error::{FastCryptoError, FastCryptoResult}; +use crate::groups::bn254::Scalar; +use crate::groups::bn254::{G1Element, G1_ELEMENT_BYTE_LENGTH}; +use crate::groups::{FromTrustedByteArray, GroupElement, Scalar as ScalarType}; +use crate::serde_helpers::ToFromByteArray; +use crate::serialize_deserialize_with_to_from_byte_array; +use ark_bn254::{G1Affine, G1Projective}; +use ark_ec::{AffineRepr, Group}; +use ark_ff::Zero; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use serde::{de, Deserialize}; + +impl GroupElement for G1Element { + type ScalarType = Scalar; + + fn zero() -> Self { + G1Element(G1Projective::zero()) + } + + fn generator() -> Self { + G1Element(G1Projective::generator()) + } +} + +impl Div for G1Element { + type Output = FastCryptoResult; + + fn div(self, rhs: Scalar) -> Self::Output { + let inverse = rhs.inverse()?; + Ok(self.mul(inverse)) + } +} + +impl Mul for G1Element { + type Output = Self; + + fn mul(self, rhs: Scalar) -> Self::Output { + Self(self.0.mul(rhs.0)) + } +} + +impl ToFromByteArray for G1Element { + fn from_byte_array(bytes: &[u8; G1_ELEMENT_BYTE_LENGTH]) -> Result { + let point = G1Affine::deserialize_compressed(bytes.as_slice()) + .map_err(|_| FastCryptoError::InvalidInput)?; + + // Arkworks only checks the infinity flag, but we require all-zeros to have unique serialization + if point.is_zero() + && bytes[0..G1_ELEMENT_BYTE_LENGTH - 1] + .iter() + .any(|x| !x.is_zero()) + { + return Err(FastCryptoError::InvalidInput); + } + + Ok(Self(G1Projective::from(point))) + } + + fn to_byte_array(&self) -> [u8; G1_ELEMENT_BYTE_LENGTH] { + let mut bytes = [0u8; G1_ELEMENT_BYTE_LENGTH]; + self.0 + .serialize_compressed(bytes.as_mut_slice()) + .expect("Never fails"); + bytes + } +} + +impl FromTrustedByteArray for G1Element { + fn from_trusted_byte_array(bytes: &[u8; G1_ELEMENT_BYTE_LENGTH]) -> FastCryptoResult { + G1Projective::deserialize_compressed_unchecked(bytes.as_slice()) + .map_err(|_| FastCryptoError::InvalidInput) + .map(G1Element) + } +} + +serialize_deserialize_with_to_from_byte_array!(G1Element); diff --git a/fastcrypto/src/groups/bn254/g2.rs b/fastcrypto/src/groups/bn254/g2.rs new file mode 100644 index 0000000000..b0edbaf7e9 --- /dev/null +++ b/fastcrypto/src/groups/bn254/g2.rs @@ -0,0 +1,80 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::error::{FastCryptoError, FastCryptoResult}; +use crate::groups::bn254::G2Element; +use crate::groups::bn254::{Scalar, G2_ELEMENT_BYTE_LENGTH}; +use crate::groups::{FromTrustedByteArray, GroupElement, Scalar as ScalarType}; +use crate::serde_helpers::ToFromByteArray; +use crate::serialize_deserialize_with_to_from_byte_array; +use ark_bn254::{G2Affine, G2Projective}; +use ark_ec::{AffineRepr, Group}; +use ark_ff::Zero; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use serde::{de, Deserialize}; +use std::ops::{Div, Mul}; + +impl GroupElement for G2Element { + type ScalarType = Scalar; + + fn zero() -> Self { + G2Element(G2Projective::zero()) + } + + fn generator() -> Self { + G2Element(G2Projective::generator()) + } +} + +impl Div for G2Element { + type Output = FastCryptoResult; + + fn div(self, rhs: Scalar) -> Self::Output { + let inverse = rhs.inverse()?; + Ok(self.mul(inverse)) + } +} + +impl Mul for G2Element { + type Output = Self; + + fn mul(self, rhs: Scalar) -> Self::Output { + Self(self.0.mul(rhs.0)) + } +} + +impl ToFromByteArray for G2Element { + fn from_byte_array(bytes: &[u8; G2_ELEMENT_BYTE_LENGTH]) -> Result { + let point = G2Affine::deserialize_compressed(bytes.as_slice()) + .map_err(|_| FastCryptoError::InvalidInput)?; + + // Arkworks only checks the infinty flag, but we require all-zeros to have unique serialization + if point.is_zero() + && bytes[0..G2_ELEMENT_BYTE_LENGTH - 1] + .iter() + .any(|x| !x.is_zero()) + { + return Err(FastCryptoError::InvalidInput); + } + + Ok(Self(G2Projective::from(point))) + } + + fn to_byte_array(&self) -> [u8; G2_ELEMENT_BYTE_LENGTH] { + let mut bytes = [0u8; G2_ELEMENT_BYTE_LENGTH]; + self.0 + .serialize_compressed(bytes.as_mut_slice()) + .expect("Never fails"); + bytes + } +} + +impl FromTrustedByteArray for G2Element { + fn from_trusted_byte_array(bytes: &[u8; G2_ELEMENT_BYTE_LENGTH]) -> FastCryptoResult { + G2Projective::deserialize_compressed_unchecked(bytes.as_slice()) + .map_err(|_| FastCryptoError::InvalidInput) + .map(G2Element) + } +} + +serialize_deserialize_with_to_from_byte_array!(G2Element); diff --git a/fastcrypto/src/groups/bn254/gt.rs b/fastcrypto/src/groups/bn254/gt.rs new file mode 100644 index 0000000000..ad610a5e21 --- /dev/null +++ b/fastcrypto/src/groups/bn254/gt.rs @@ -0,0 +1,80 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::error::{FastCryptoError, FastCryptoResult}; +use crate::groups::bn254::G2Element; +use crate::groups::bn254::{G1Element, GTElement}; +use crate::groups::bn254::{Scalar, GT_ELEMENT_BYTE_LENGTH}; +use crate::groups::{FromTrustedByteArray, GroupElement, Pairing, Scalar as ScalarType}; +use crate::serde_helpers::ToFromByteArray; +use crate::serialize_deserialize_with_to_from_byte_array; +use ark_bn254::Bn254; +use ark_ec::pairing::PairingOutput; +use ark_ff::Zero; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use once_cell::sync::OnceCell; +use serde::{de, Deserialize}; +use std::ops::{Div, Mul}; + +#[allow(clippy::suspicious_arithmetic_impl)] +impl Div for GTElement { + type Output = FastCryptoResult; + + fn div(self, rhs: Scalar) -> Self::Output { + let inverse = rhs.inverse()?; + Ok(self * inverse) + } +} + +impl Mul for GTElement { + type Output = GTElement; + + fn mul(self, rhs: Scalar) -> Self::Output { + Self(self.0.mul(rhs.0)) + } +} + +impl GroupElement for GTElement { + type ScalarType = Scalar; + + fn zero() -> Self { + GTElement(PairingOutput::zero()) + } + + fn generator() -> Self { + static G: OnceCell> = OnceCell::new(); + Self(*G.get_or_init(Self::compute_generator)) + } +} + +impl GTElement { + fn compute_generator() -> PairingOutput { + G1Element::generator().pairing(&G2Element::generator()).0 + } +} + +impl FromTrustedByteArray for GTElement { + fn from_trusted_byte_array(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> FastCryptoResult { + PairingOutput::::deserialize_compressed_unchecked(bytes.as_ref()) + .map_err(|_| FastCryptoError::InvalidInput) + .map(GTElement) + } +} + +impl ToFromByteArray for GTElement { + fn from_byte_array(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> Result { + PairingOutput::::deserialize_compressed(bytes.as_ref()) + .map_err(|_| FastCryptoError::InvalidInput) + .map(GTElement) + } + + fn to_byte_array(&self) -> [u8; GT_ELEMENT_BYTE_LENGTH] { + let mut bytes = [0u8; GT_ELEMENT_BYTE_LENGTH]; + self.0 + .serialize_compressed(bytes.as_mut_slice()) + .expect("Never fails"); + bytes + } +} + +serialize_deserialize_with_to_from_byte_array!(GTElement); diff --git a/fastcrypto/src/groups/bn254/mod.rs b/fastcrypto/src/groups/bn254/mod.rs new file mode 100644 index 0000000000..3a06bb8fa7 --- /dev/null +++ b/fastcrypto/src/groups/bn254/mod.rs @@ -0,0 +1,56 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::groups::{GroupElement, Pairing}; +use ark_bn254::{Bn254, Fr, G1Projective, G2Projective}; +use ark_ec::pairing::{Pairing as ArkworksPairing, PairingOutput}; +use derive_more::{Add, Neg, Sub}; +use fastcrypto_derive::GroupOpsExtend; + +mod g1; +mod g2; +mod gt; +mod scalar; + +#[cfg(test)] +mod tests; + +/// The byte length of a compressed element of G1. +pub const G1_ELEMENT_BYTE_LENGTH: usize = 32; + +/// The byte length of a compressed element of G2. +pub const G2_ELEMENT_BYTE_LENGTH: usize = 64; + +/// The byte length of a compressed element of GT. +pub const GT_ELEMENT_BYTE_LENGTH: usize = 384; + +/// The byte length of a scalar. +pub const SCALAR_LENGTH: usize = 32; + +/// Elements of the group G1 in BN254. +#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)] +#[repr(transparent)] +pub struct G1Element(G1Projective); + +/// Elements of the group G2 in BN254. +#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)] +#[repr(transparent)] +pub struct G2Element(G2Projective); + +/// Elements of the subgroup GT of F_q^{12} in BN254. Note that it is written in additive notation here. +#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)] +pub struct GTElement(PairingOutput); + +/// This represents a scalar modulo r = 21888242871839275222246405745257275088548364400416034343698204186575808495617 +/// which is the order of the groups G1, G2 and GT. Note that r is a 254 bit prime. +#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)] +pub struct Scalar(Fr); + +impl Pairing for G1Element { + type Other = G2Element; + type Output = GTElement; + + fn pairing(&self, other: &Self::Other) -> ::Output { + GTElement(Bn254::pairing(self.0, other.0)) + } +} diff --git a/fastcrypto/src/groups/bn254/scalar.rs b/fastcrypto/src/groups/bn254/scalar.rs new file mode 100644 index 0000000000..3aea9b5a2b --- /dev/null +++ b/fastcrypto/src/groups/bn254/scalar.rs @@ -0,0 +1,85 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use std::ops::{Div, Mul}; + +use crate::error::{FastCryptoError, FastCryptoResult}; +use crate::groups::bn254::{Scalar, SCALAR_LENGTH}; +use crate::groups::GroupElement; +use crate::serde_helpers::ToFromByteArray; +use crate::traits::AllowedRng; +use crate::{groups, serialize_deserialize_with_to_from_byte_array}; +use ark_bn254::Fr; +use ark_ff::{Field, One, UniformRand, Zero}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use serde::{de, Deserialize}; + +impl Div for Scalar { + type Output = FastCryptoResult; + + fn div(self, rhs: Self) -> FastCryptoResult { + if rhs.0.is_zero() { + return Err(FastCryptoError::InvalidInput); + } + Ok(Self(self.0.div(rhs.0))) + } +} + +impl Mul for Scalar { + type Output = Self; + + fn mul(self, rhs: Scalar) -> Self::Output { + Self(self.0.mul(rhs.0)) + } +} + +impl GroupElement for Scalar { + type ScalarType = Scalar; + + fn zero() -> Self { + Self(Fr::zero()) + } + + fn generator() -> Self { + Self(Fr::one()) + } +} + +impl From for Scalar { + fn from(value: u128) -> Self { + Self(Fr::from(value)) + } +} + +impl groups::Scalar for Scalar { + fn rand(rng: &mut R) -> Self { + Self(Fr::rand(rng)) + } + + fn inverse(&self) -> FastCryptoResult { + Ok(Self(self.0.inverse().ok_or(FastCryptoError::InvalidInput)?)) + } +} + +impl ToFromByteArray for Scalar { + fn from_byte_array(bytes: &[u8; SCALAR_LENGTH]) -> Result { + // Arkworks uses little-endian byte order for serialization, but we use big-endian. + let mut reversed = *bytes; + reversed.reverse(); + Fr::deserialize_compressed(reversed.as_slice()) + .map_err(|_| FastCryptoError::InvalidInput) + .map(Scalar) + } + + fn to_byte_array(&self) -> [u8; SCALAR_LENGTH] { + let mut bytes = [0u8; SCALAR_LENGTH]; + self.0 + .serialize_compressed(bytes.as_mut_slice()) + .expect("Never fails"); + // Arkworks uses little-endian byte order for serialization, but we use big-endian. + bytes.reverse(); + bytes + } +} + +serialize_deserialize_with_to_from_byte_array!(Scalar); diff --git a/fastcrypto/src/tests/bn254_group_tests.rs b/fastcrypto/src/groups/bn254/tests.rs similarity index 98% rename from fastcrypto/src/tests/bn254_group_tests.rs rename to fastcrypto/src/groups/bn254/tests.rs index 293899c7db..b7869ebfb1 100644 --- a/fastcrypto/src/tests/bn254_group_tests.rs +++ b/fastcrypto/src/groups/bn254/tests.rs @@ -6,7 +6,10 @@ use ark_ec::AffineRepr; use ark_serialize::CanonicalSerialize; use rand::thread_rng; -use crate::groups::bn254::{G1Element, G2Element, GTElement, Scalar}; +use crate::groups::bn254::G1Element; +use crate::groups::bn254::G2Element; +use crate::groups::bn254::GTElement; +use crate::groups::bn254::Scalar; use crate::groups::{FromTrustedByteArray, GroupElement, Pairing, Scalar as ScalarTrait}; use crate::serde_helpers::ToFromByteArray; use crate::test_helpers::verify_serialization; diff --git a/fastcrypto/src/lib.rs b/fastcrypto/src/lib.rs index 81d92635fe..df3e8967cf 100644 --- a/fastcrypto/src/lib.rs +++ b/fastcrypto/src/lib.rs @@ -84,10 +84,6 @@ pub mod utils_tests; #[path = "tests/secp256r1_group_tests.rs"] pub mod secp256r1_group_tests; -#[cfg(test)] -#[path = "tests/bn254_group_tests.rs"] -pub mod bn254_group_tests; - pub mod traits; #[cfg(feature = "aes")] From b852e3ea5f40b9825414b7166fdc0b51d26e5505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 9 Apr 2024 12:11:30 +0200 Subject: [PATCH 26/30] Fix test --- fastcrypto/src/groups/bn254/tests.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/fastcrypto/src/groups/bn254/tests.rs b/fastcrypto/src/groups/bn254/tests.rs index b7869ebfb1..0a0c629d0b 100644 --- a/fastcrypto/src/groups/bn254/tests.rs +++ b/fastcrypto/src/groups/bn254/tests.rs @@ -346,16 +346,17 @@ fn test_serialization_gt() { // reject if one of the elements >= P let mut bytes = GTElement::generator().to_byte_array(); - let p = hex::decode("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab").unwrap(); + let p = + hex::decode("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47").unwrap(); let mut carry = 0; - let mut target = [0; 48]; - for i in (0..48).rev() { + let mut target = [0; 32]; + for i in (0..32).rev() { let sum = (bytes[i] as u16) + (p[i] as u16) + carry; target[i] = (sum % 256) as u8; carry = sum / 256; } assert_eq!(carry, 0); - bytes[0..48].copy_from_slice(&target); + bytes[0..32].copy_from_slice(&target); assert!(GTElement::from_byte_array(&bytes).is_err()); // Test FromTrustedByteArray. From 07ec3f9440ec68ddeddea9298675a7d05e692e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 9 Apr 2024 12:17:40 +0200 Subject: [PATCH 27/30] Use le for scalars --- fastcrypto/src/groups/bn254/scalar.rs | 9 +++------ fastcrypto/src/groups/bn254/tests.rs | 8 +++++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/fastcrypto/src/groups/bn254/scalar.rs b/fastcrypto/src/groups/bn254/scalar.rs index 3aea9b5a2b..e44f405913 100644 --- a/fastcrypto/src/groups/bn254/scalar.rs +++ b/fastcrypto/src/groups/bn254/scalar.rs @@ -63,21 +63,18 @@ impl groups::Scalar for Scalar { impl ToFromByteArray for Scalar { fn from_byte_array(bytes: &[u8; SCALAR_LENGTH]) -> Result { - // Arkworks uses little-endian byte order for serialization, but we use big-endian. - let mut reversed = *bytes; - reversed.reverse(); - Fr::deserialize_compressed(reversed.as_slice()) + // Note that arkworks uses little-endian byte order for serialization here. + Fr::deserialize_compressed(bytes.as_slice()) .map_err(|_| FastCryptoError::InvalidInput) .map(Scalar) } fn to_byte_array(&self) -> [u8; SCALAR_LENGTH] { + // Note that arkworks uses little-endian byte order for serialization here. let mut bytes = [0u8; SCALAR_LENGTH]; self.0 .serialize_compressed(bytes.as_mut_slice()) .expect("Never fails"); - // Arkworks uses little-endian byte order for serialization, but we use big-endian. - bytes.reverse(); bytes } } diff --git a/fastcrypto/src/groups/bn254/tests.rs b/fastcrypto/src/groups/bn254/tests.rs index 0a0c629d0b..549d54c9e7 100644 --- a/fastcrypto/src/groups/bn254/tests.rs +++ b/fastcrypto/src/groups/bn254/tests.rs @@ -184,7 +184,7 @@ fn test_serde_and_regression() { verify_serialization( &s1, Some( - hex::decode("0000000000000000000000000000000000000000000000000000000000000001") + hex::decode("0100000000000000000000000000000000000000000000000000000000000000") .unwrap() .as_slice(), ), @@ -225,13 +225,15 @@ fn test_serialization_scalar() { assert_eq!(Scalar::from_byte_array(&bytes).unwrap(), Scalar::zero()); // Scalar::from_byte_array should not accept the order or above it. - let order = + let mut order = hex::decode("30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001").unwrap(); + order.reverse(); // Little-endian assert!(Scalar::from_byte_array(<&[u8; 32]>::try_from(order.as_slice()).unwrap()).is_err()); // Scalar::from_byte_array should accept the order - 1. - let order_minus_one = + let mut order_minus_one = hex::decode("30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000").unwrap(); + order_minus_one.reverse(); // Little-endian assert_eq!( Scalar::from_byte_array(<&[u8; 32]>::try_from(order_minus_one.as_slice()).unwrap()) .unwrap(), From 06c30785e4e3b89124a54dad51dbff8edcf8254b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 6 Aug 2024 18:19:19 +0100 Subject: [PATCH 28/30] Clean up after merge --- fastcrypto-zkp/benches/proving.rs | 2 -- fastcrypto-zkp/src/bls12381/mod.rs | 9 --------- fastcrypto/src/serde_helpers.rs | 2 +- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/fastcrypto-zkp/benches/proving.rs b/fastcrypto-zkp/benches/proving.rs index 33831e1275..874cbf0463 100644 --- a/fastcrypto-zkp/benches/proving.rs +++ b/fastcrypto-zkp/benches/proving.rs @@ -1,5 +1,3 @@ -use std::ops::Mul; - // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 diff --git a/fastcrypto-zkp/src/bls12381/mod.rs b/fastcrypto-zkp/src/bls12381/mod.rs index fd977c37f1..d96d7e4c8d 100644 --- a/fastcrypto-zkp/src/bls12381/mod.rs +++ b/fastcrypto-zkp/src/bls12381/mod.rs @@ -15,15 +15,6 @@ pub mod api; #[cfg(test)] mod test_helpers; -#[cfg(test)] -#[path = "unit_tests/api_tests.rs"] -mod api_tests; - -#[cfg(test)] -#[path = "unit_tests/test_helpers.rs"] -pub(crate) mod test_helpers; -mod conversions; - /// A prepared Groth16 verifying key in the BLS12-381 construction. pub type PreparedVerifyingKey = groth16::PreparedVerifyingKey; diff --git a/fastcrypto/src/serde_helpers.rs b/fastcrypto/src/serde_helpers.rs index abfc61e34c..d6d400e974 100644 --- a/fastcrypto/src/serde_helpers.rs +++ b/fastcrypto/src/serde_helpers.rs @@ -305,7 +305,7 @@ impl Default for crate::bls12381::min_sig::BLS12381AggregateSignatureAsBytes { #[cfg(test)] mod tests { use super::*; - use crate::groups::bls12381::{G1_ELEMENT_BYTE_LENGTH, G1Element, G1ElementAsBytes}; + use crate::groups::bls12381::{G1Element, G1ElementAsBytes, G1_ELEMENT_BYTE_LENGTH}; use crate::groups::GroupElement; use schemars::schema_for; From 2e69d38f6e6f897c6875b6fb1c164a8c5129b2c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 6 Aug 2024 18:28:51 +0100 Subject: [PATCH 29/30] More clean up --- fastcrypto-zkp/src/bls12381/api/tests.rs | 65 +----------------------- fastcrypto/src/serde_helpers.rs | 31 ----------- 2 files changed, 2 insertions(+), 94 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api/tests.rs b/fastcrypto-zkp/src/bls12381/api/tests.rs index eebe72da2a..372204948a 100644 --- a/fastcrypto-zkp/src/bls12381/api/tests.rs +++ b/fastcrypto-zkp/src/bls12381/api/tests.rs @@ -1,6 +1,8 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use std::ops::Mul; + use ark_bls12_381::{Bls12_381, Fq12, Fr, G1Affine}; use ark_ff::One; use ark_groth16::Groth16; @@ -18,69 +20,6 @@ use crate::bls12381::test_helpers::from_arkworks_scalar; use crate::bls12381::{PreparedVerifyingKey, VerifyingKey}; use crate::dummy_circuits::{DummyCircuit, Fibonacci}; use crate::groth16::Proof; -use fastcrypto::groups::bls12381::G1Element; - -use std::ops::Mul; - -#[test] -fn test_verify() { - // Success case. - let mut vk_bytes = hex::decode("ada3c24e8c2e63579cc03fd1f112a093a17fc8ab0ff6eee7e04cab7bf8e03e7645381f309ec113309e05ac404c77ac7c8585d5e4328594f5a70a81f6bd4f29073883ee18fd90e2aa45d0fc7376e81e2fdf5351200386f5732e58eb6ff4d318dc").unwrap(); - let alpha_bytes = hex::decode("8b0f85a9e7d929244b0af9a35af10717bd667b6227aae37a6d336e815fb0d850873e0d87968345a493b2d31aa8aa400d9820af1d35fa862d1b339ea1f98ac70db7faa304bff120a151a1741d782d08b8f1c1080d4d2f3ebee63ac6cadc666605be306de0973be38fbbf0f54b476bbb002a74ff9506a2b9b9a34b99bfa7481a84a2c9face7065c19d7069cc5738c5350b886a5eeebe656499d2ffb360afc7aff20fa9ee689fb8b46863e90c85224e8f597bf323ad4efb02ee96eb40221fc89918a2c740eabd2886476c7f247a3eb34f0106b3b51cf040e2cdcafea68b0d8eecabf58b5aa2ece3d86259cf2dfa3efab1170c6eb11948826def533849b68335d76d60f3e16bb5c629b1c24df2bdd1a7f13c754d7fe38617ecd7783504e4615e5c13168185cc08de8d63a0f7032ab7e82ff78cf0bc46a84c98f2d95bb5af355cbbe525c44d5c1549c169dfe119a219dbf9038ec73729d187bd0e3ed369e4a2ec2be837f3dcfd958aea7110627d2c0192d262f17e722509c17196005b646a556cf010ef9bd2a2a9b937516a5ecdee516e77d14278e96bc891b630fc833dda714343554ae127c49460416430b7d4f048d08618058335dec0728ad37d10dd9d859c385a38673e71cc98e8439da0accc29de5c92d3c3dc98e199361e9f7558e8b0a2a315ccc5a72f54551f07fad6f6f4615af498aba98aea01a13a4eb84667fd87ee9782b1d812a03f8814f042823a7701238d0fec1e7dec2a26ffea00330b5c7930e95138381435d2a59f51313a48624e30b0a685e357874d41a0a19d83f7420c1d9c04").unwrap(); - let gamma_bytes = hex::decode("b675d1ff988116d1f2965d3c0c373569b74d0a1762ea7c4f4635faa5b5a8fa198a2a2ce6153f390a658dc9ad01a415491747e9de7d5f493f59cf05a52eb46eaac397ffc47aef1396cf0d8b75d0664077ea328ad6b63284b42972a8f11c523a60").unwrap(); - let delta_bytes = hex::decode("8229cb9443ef1fb72887f917f500e2aef998717d91857bcb92061ecd74d1d24c2b2b282736e8074e4316939b4c9853c117aa08ed49206860d648818b2cccb526585f5790161b1730d39c73603b482424a27bba891aaa6d99f3025d3df2a6bd42").unwrap(); - - let inputs_bytes = - hex::decode("440758042e68b76a376f2fecf3a5a8105edb194c3e774e5a760140305aec8849").unwrap(); - let proof_bytes = hex::decode("a29981304df8e0f50750b558d4de59dbc8329634b81c986e28e9fff2b0faa52333b14a1f7b275b029e13499d1f5dd8ab955cf5fa3000a097920180381a238ce12df52207597eade4a365a6872c0a19a39c08a9bfb98b69a15615f90cc32660180ca32e565c01a49b505dd277713b1eae834df49643291a3601b11f56957bde02d5446406d0e4745d1bd32c8ccb8d8e80b877712f5f373016d2ecdeebb58caebc7a425b8137ebb1bd0c5b81c1d48151b25f0f24fe9602ba4e403811fb17db6f14").unwrap(); - - // Success case - assert!(verify_groth16_in_bytes( - &vk_bytes, - &alpha_bytes, - &gamma_bytes, - &delta_bytes, - &inputs_bytes, - &proof_bytes - ) - .unwrap()); - - // Invalid public inputs bytes. - let invalid_inputs = hex::decode("cf").unwrap(); - assert!(verify_groth16_in_bytes( - &alpha_bytes, - &gamma_bytes, - &delta_bytes, - &inputs_bytes, - &invalid_inputs, - &proof_bytes - ) - .is_err()); - - // Invalid proof bytes. - let invalid_proof = hex::decode("4a").unwrap(); - assert!(verify_groth16_in_bytes( - &alpha_bytes, - &gamma_bytes, - &delta_bytes, - &inputs_bytes, - &inputs_bytes, - &invalid_proof - ) - .is_err()); - - // Invalid prepared verifying key. - vk_bytes.pop(); - assert!(verify_groth16_in_bytes( - &vk_bytes, - &alpha_bytes, - &gamma_bytes, - &delta_bytes, - &inputs_bytes, - &proof_bytes - ) - .is_err()); -} #[test] fn test_verify() { diff --git a/fastcrypto/src/serde_helpers.rs b/fastcrypto/src/serde_helpers.rs index d6d400e974..d15b9dd81b 100644 --- a/fastcrypto/src/serde_helpers.rs +++ b/fastcrypto/src/serde_helpers.rs @@ -357,34 +357,3 @@ mod tests { ); } } - -/// Given a byte array of length `N * SIZE_IN_BYTES`, deserialize it into a vector of `N` elements -/// of type `T`. -pub fn deserialize_vector>( - bytes: &[u8], -) -> FastCryptoResult> { - if bytes.len() % SIZE_IN_BYTES != 0 { - return Err(FastCryptoError::InvalidInput); - } - bytes - .chunks(SIZE_IN_BYTES) - .map(|chunk| { - T::from_byte_array( - &chunk - .try_into() - .map_err(|_| FastCryptoError::InvalidInput)?, - ) - }) - .collect::>>() -} - -/// Serialize a vector of elements of type T into a byte array by simply concatenating their binary -/// representations. -pub fn serialize_vector>( - elements: &[T], -) -> Vec { - elements - .iter() - .flat_map(|e| e.to_byte_array().to_vec()) - .collect() -} From 320239a8a60b99b4e4ac57788f06b9ac599c224a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 6 Aug 2024 18:35:50 +0100 Subject: [PATCH 30/30] More cleanup --- Cargo.lock | 104 +------------------------------------- fastcrypto-zkp/Cargo.toml | 2 - 2 files changed, 2 insertions(+), 104 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e41e4d0938..547ec0b666 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,21 +94,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anes" version = "0.1.6" @@ -761,11 +746,8 @@ version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ - "android-tzdata", - "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.4", ] [[package]] @@ -890,12 +872,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "core-foundation-sys" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - [[package]] name = "cpufeatures" version = "0.2.5" @@ -1122,7 +1098,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", - "serde", ] [[package]] @@ -1368,7 +1343,7 @@ dependencies = [ "serde-big-array", "serde-reflection", "serde_json", - "serde_with 3.8.3", + "serde_with", "sha2 0.10.6", "sha3 0.10.6", "signature 2.1.0", @@ -1461,7 +1436,6 @@ dependencies = [ "ark-snark", "ark-std", "bcs", - "bincode", "blake2", "byte-slice-cast", "criterion 0.5.1", @@ -1482,7 +1456,6 @@ dependencies = [ "schemars", "serde", "serde_json", - "serde_with 2.3.3", "test-strategy", "tokio", "typenum", @@ -1877,29 +1850,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "iana-time-zone" -version = "0.1.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "ident_case" version = "1.0.1" @@ -1938,7 +1888,6 @@ checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown 0.12.3", - "serde", ] [[package]] @@ -3212,22 +3161,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_with" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" -dependencies = [ - "base64 0.13.1", - "chrono", - "hex", - "indexmap", - "serde", - "serde_json", - "serde_with_macros 2.3.3", - "time", -] - [[package]] name = "serde_with" version = "3.8.3" @@ -3240,22 +3173,10 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "serde_with_macros 3.8.3", + "serde_with_macros", "time", ] -[[package]] -name = "serde_with_macros" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.38", -] - [[package]] name = "serde_with_macros" version = "3.8.3" @@ -3592,12 +3513,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde", "time-core", - "time-macros", ] [[package]] @@ -3606,16 +3525,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" -[[package]] -name = "time-macros" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" -dependencies = [ - "num-conv", - "time-core", -] - [[package]] name = "tinytemplate" version = "1.2.1" @@ -4006,15 +3915,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-core" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" -dependencies = [ - "windows-targets 0.52.4", -] - [[package]] name = "windows-sys" version = "0.42.0" diff --git a/fastcrypto-zkp/Cargo.toml b/fastcrypto-zkp/Cargo.toml index ac3678480d..f6b3f8a9e4 100644 --- a/fastcrypto-zkp/Cargo.toml +++ b/fastcrypto-zkp/Cargo.toml @@ -45,8 +45,6 @@ typenum = "1.13.0" lazy_static = "1.4.0" itertools = "0.12.0" regex = "1.7.1" -bincode = "1.3.3" -serde_with = "2.3.1" [dev-dependencies] ark-bls12-377 = "0.4.0"