From 5c59a99fd98767d14e27708a260e91334feb12e2 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/65] Add generic Groth16 verifier --- Cargo.lock | 1 + fastcrypto-zkp/Cargo.toml | 1 + fastcrypto-zkp/src/groth16/mod.rs | 145 ++++++++++++++++++++++++++++++ fastcrypto-zkp/src/lib.rs | 2 + 4 files changed, 149 insertions(+) create mode 100644 fastcrypto-zkp/src/groth16/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 564fd59cbe..148086500e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1504,6 +1504,7 @@ dependencies = [ "ark-serialize", "ark-snark", "ark-std", + "bincode", "blake2", "blst", "byte-slice-cast", diff --git a/fastcrypto-zkp/Cargo.toml b/fastcrypto-zkp/Cargo.toml index d99907eb13..cf71e31c61 100644 --- a/fastcrypto-zkp/Cargo.toml +++ b/fastcrypto-zkp/Cargo.toml @@ -45,6 +45,7 @@ ff = { version = "0.13.0", features = ["derive"] } typenum = "1.13.0" lazy_static = "1.4.0" itertools = "0.12.0" +bincode = "1.3.3" [dev-dependencies] ark-bls12-377 = "0.4.0" diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs new file mode 100644 index 0000000000..f8e60de84b --- /dev/null +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -0,0 +1,145 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use fastcrypto::error::{FastCryptoError, FastCryptoResult}; +use fastcrypto::groups::{GroupElement, MultiScalarMul, Pairing}; +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +pub struct Proof { + a: G1, + b: G1::Other, + c: G1, +} + +#[derive(Debug, Deserialize)] +pub struct VerifyingKey { + alpha: G1, + beta: G1::Other, + gamma: G1::Other, + delta: G1::Other, + gamma_abc: Vec, +} + +/// This is a helper function to store a pre-processed version of the verifying key. +/// This is roughly homologous to [`ark_groth16::data_structures::PreparedVerifyingKey`]. +/// Note that contrary to Arkworks, we don't store a "prepared" version of the `gamma_neg` and +/// `delta_neg` fields because they are very large and unpractical to use in the binary API. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PreparedVerifyingKey +where + G1: Pairing, + ::Output: GroupElement, +{ + /// The element vk.gamma_abc, + /// aka the `[gamma^{-1} * (beta * a_i + alpha * b_i + c_i) * G]`, where i spans the public inputs + vk_gamma_abc: Vec, + + /// The element `e(alpha * G, beta * H)` in `GT`. + alpha_beta: ::Output, + + /// The element `- gamma * H` in `G2`, for use in pairings. + gamma_neg: ::Other, + + /// The element `- delta * H` in `G2`, for use in pairings. + delta_neg: ::Other, +} + +impl From<&VerifyingKey> for PreparedVerifyingKey +where + G1: Pairing, + ::Output: GroupElement, +{ + fn from(vk: &VerifyingKey) -> Self { + PreparedVerifyingKey { + vk_gamma_abc: vk.gamma_abc.clone(), + alpha_beta: vk.alpha.pairing(&vk.beta), + gamma_neg: -vk.gamma, + delta_neg: -vk.delta, + } + } +} + +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, VerifyingKey as MyVerifyingKey}; + + #[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 = 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-zkp/src/lib.rs b/fastcrypto-zkp/src/lib.rs index 4631dd3b32..7807a4a785 100644 --- a/fastcrypto-zkp/src/lib.rs +++ b/fastcrypto-zkp/src/lib.rs @@ -25,3 +25,5 @@ pub mod dummy_circuits; /// Circom-compatible deserialization of points pub mod zk_login_utils; + +pub mod groth16; From 1a038d8910d6ea67d8f6044da3a26179d0b7114f 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/65] Compiles but deserialization doesn't work --- Cargo.lock | 1 + fastcrypto-zkp/Cargo.toml | 1 + fastcrypto-zkp/src/groth16/api.rs | 161 ++++++++++++++++++++++ fastcrypto-zkp/src/groth16/mod.rs | 103 ++------------ 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 +- 9 files changed, 342 insertions(+), 91 deletions(-) create mode 100644 fastcrypto-zkp/src/groth16/api.rs create mode 100644 fastcrypto-zkp/src/groth16/prepared_vk.rs diff --git a/Cargo.lock b/Cargo.lock index 148086500e..3de04b18a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1504,6 +1504,7 @@ dependencies = [ "ark-serialize", "ark-snark", "ark-std", + "bcs", "bincode", "blake2", "blst", diff --git a/fastcrypto-zkp/Cargo.toml b/fastcrypto-zkp/Cargo.toml index cf71e31c61..b0560e82fe 100644 --- a/fastcrypto-zkp/Cargo.toml +++ b/fastcrypto-zkp/Cargo.toml @@ -29,6 +29,7 @@ ark-groth16 = { version = "0.4.0", default-features = false } ark-relations = "0.4.0" ark-serialize = "0.4.1" ark-snark = "0.4.0" +bcs.workspace = true blst = "0.3.11" byte-slice-cast = "1.2.2" fastcrypto = { path = "../fastcrypto", version = "0.1.5" } diff --git a/fastcrypto-zkp/src/groth16/api.rs b/fastcrypto-zkp/src/groth16/api.rs new file mode 100644 index 0000000000..86256da498 --- /dev/null +++ b/fastcrypto-zkp/src/groth16/api.rs @@ -0,0 +1,161 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use serde::{Deserialize, Serialize}; + +use fastcrypto::error::FastCryptoError; +use fastcrypto::groups::{deserialize_vector, GroupElement, MultiScalarMul, Pairing, Scalar}; + +use crate::groth16::{PreparedVerifyingKey, Proof, VerifyingKey}; + +/// Deserialize bytes as an Arkworks representation of a verifying key, and return a vector of the +/// four components of a prepared verified key (see more at [`PreparedVerifyingKey`]). +pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoError> +where + G1: Pairing + MultiScalarMul + Serialize + for<'a> Deserialize<'a>, + ::Other: Serialize + for<'a> Deserialize<'a>, + ::Output: GroupElement + Serialize + for<'a> Deserialize<'a>, +{ + // TODO: The serialization does not match Arkworks' format. + let vk: VerifyingKey = + bcs::from_bytes(vk_bytes).map_err(|_| FastCryptoError::InvalidInput)?; + PreparedVerifyingKey::from(&vk).serialize_into_parts() +} + +/// Verify Groth16 proof using the serialized form of the four components in a prepared verifying key +/// (see more at [`PreparedVerifyingKey`]), serialized proof public input, which should +/// be concatenated serialized field elements of the scalar field of [`crate::conversions::SCALAR_SIZE`] +/// bytes each, and serialized proof points. +pub fn verify_groth16_in_bytes( + vk_gamma_abc_g1_bytes: &[u8], + alpha_g1_beta_g2_bytes: &[u8], + gamma_g2_neg_pc_bytes: &[u8], + delta_g2_neg_pc_bytes: &[u8], + proof_public_inputs_as_bytes: &[u8], + proof_points_as_bytes: &[u8], +) -> Result + where + G1: Pairing + MultiScalarMul + Serialize + for<'a> Deserialize<'a>, + ::Other: Serialize + for<'a> Deserialize<'a>, + ::Output: GroupElement + Serialize + for<'a> Deserialize<'a>, +{ + let x = deserialize_vector::(proof_public_inputs_as_bytes, G1::ScalarType::SIZE_IN_BYTES)?; + let proof = bincode::deserialize(proof_points_as_bytes).map_err(|_| FastCryptoError::InvalidInput)?; + let blst_pvk = PreparedVerifyingKey::::deserialize_from_parts(&vec![ + vk_gamma_abc_g1_bytes, + alpha_g1_beta_g2_bytes, + gamma_g2_neg_pc_bytes, + delta_g2_neg_pc_bytes, + ], G1_SIZE)?; + Ok(blst_pvk.verify(x.as_slice(), &proof).is_ok()) +} + +#[cfg(test)] +mod tests { + use crate::dummy_circuits::{DummyCircuit, Fibonacci}; + use ark_bls12_381::{Bls12_381, Fr, G1Affine}; + use ark_groth16::Groth16; + use ark_serialize::CanonicalSerialize; + use ark_snark::SNARK; + use ark_std::rand::thread_rng; + use ark_std::UniformRand; + use std::ops::Mul; + use fastcrypto::groups::bls12381::{G1_ELEMENT_BYTE_LENGTH, G1Element}; + use crate::groth16::api::{prepare_pvk_bytes, verify_groth16_in_bytes}; + + #[test] + fn test_verify_groth16_in_bytes_api() { + const PUBLIC_SIZE: usize = 128; + let rng = &mut thread_rng(); + let c = DummyCircuit:: { + a: Some(::rand(rng)), + b: Some(::rand(rng)), + num_variables: PUBLIC_SIZE, + num_constraints: 10, + }; + + let (pk, vk) = Groth16::::circuit_specific_setup(c, rng).unwrap(); + let proof = Groth16::::prove(&pk, c, rng).unwrap(); + let v = c.a.unwrap().mul(c.b.unwrap()); + let mut vk_bytes = vec![]; + vk.serialize_compressed(&mut vk_bytes).unwrap(); + + let bytes = prepare_pvk_bytes::(vk_bytes.as_slice()).unwrap(); + let vk_gamma_abc_g1_bytes = &bytes[0]; + let alpha_g1_beta_g2_bytes = &bytes[1]; + let gamma_g2_neg_pc_bytes = &bytes[2]; + let delta_g2_neg_pc_bytes = &bytes[3]; + + let mut proof_inputs_bytes = vec![]; + v.serialize_compressed(&mut proof_inputs_bytes).unwrap(); + + let mut proof_points_bytes = vec![]; + proof.serialize_compressed(&mut proof_points_bytes).unwrap(); + + // Success case. + assert!(verify_groth16_in_bytes::( + vk_gamma_abc_g1_bytes, + alpha_g1_beta_g2_bytes, + gamma_g2_neg_pc_bytes, + delta_g2_neg_pc_bytes, + &proof_inputs_bytes, + &proof_points_bytes + ) + .unwrap()); + + // Negative test: Replace the A element with a random point. + let mut modified_proof_points_bytes = proof_points_bytes.clone(); + let _ = &G1Affine::rand(rng) + .serialize_compressed(&mut modified_proof_points_bytes[0..48]) + .unwrap(); + assert!(!verify_groth16_in_bytes::( + vk_gamma_abc_g1_bytes, + alpha_g1_beta_g2_bytes, + gamma_g2_neg_pc_bytes, + delta_g2_neg_pc_bytes, + &proof_inputs_bytes, + &modified_proof_points_bytes + ) + .unwrap()); + + // Length of verifying key is incorrect. + let mut modified_bytes = bytes[0].clone(); + modified_bytes.pop(); + assert!(verify_groth16_in_bytes::( + &modified_bytes, + alpha_g1_beta_g2_bytes, + gamma_g2_neg_pc_bytes, + delta_g2_neg_pc_bytes, + &proof_inputs_bytes, + &proof_points_bytes + ) + .is_err()); + + // Length of public inputs is incorrect. + let mut modified_proof_inputs_bytes = proof_inputs_bytes.clone(); + modified_proof_inputs_bytes.pop(); + assert!(verify_groth16_in_bytes::( + vk_gamma_abc_g1_bytes, + alpha_g1_beta_g2_bytes, + gamma_g2_neg_pc_bytes, + delta_g2_neg_pc_bytes, + &modified_proof_inputs_bytes, + &proof_points_bytes + ) + .is_err()); + + // length of proof is incorrect + let mut modified_proof_points_bytes = proof_points_bytes.to_vec(); + modified_proof_points_bytes.pop(); + assert!(verify_groth16_in_bytes::( + vk_gamma_abc_g1_bytes, + alpha_g1_beta_g2_bytes, + gamma_g2_neg_pc_bytes, + delta_g2_neg_pc_bytes, + &proof_inputs_bytes, + &modified_proof_points_bytes + ) + .is_err()); + } + +} \ No newline at end of file diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index f8e60de84b..4323ad193f 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -1,19 +1,28 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use fastcrypto::error::{FastCryptoError, FastCryptoResult}; -use fastcrypto::groups::{GroupElement, MultiScalarMul, Pairing}; +use std::fmt::Debug; +use fastcrypto::groups::{GroupElement, Pairing}; use serde::Deserialize; +pub mod api; +mod prepared_vk; + #[derive(Debug, Deserialize)] -pub struct Proof { +pub struct Proof +where + G1::Other: Debug, +{ a: G1, b: G1::Other, c: G1, } #[derive(Debug, Deserialize)] -pub struct VerifyingKey { +pub struct VerifyingKey +where + G1::Other: Debug, +{ alpha: G1, beta: G1::Other, gamma: G1::Other, @@ -29,7 +38,7 @@ pub struct VerifyingKey { pub struct PreparedVerifyingKey where G1: Pairing, - ::Output: GroupElement, + ::Output: Clone + Debug + PartialEq + Eq, { /// The element vk.gamma_abc, /// aka the `[gamma^{-1} * (beta * a_i + alpha * b_i + c_i) * G]`, where i spans the public inputs @@ -59,87 +68,3 @@ where } } } - -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, VerifyingKey as MyVerifyingKey}; - - #[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 = 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-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 f6c8399062..f7a188ab88 100644 --- a/fastcrypto/src/groups/bls12381.rs +++ b/fastcrypto/src/groups/bls12381.rs @@ -725,6 +725,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 e561178e9f..e7c3cfe7f6 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 1c392113924ff329787785e1a04c35b2bf38f664 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/65] Groth16 api works --- Cargo.lock | 1 + fastcrypto-zkp/Cargo.toml | 1 + fastcrypto-zkp/src/groth16/api.rs | 312 ++++++++++++++++++---- fastcrypto-zkp/src/groth16/mod.rs | 4 +- 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 +- 9 files changed, 300 insertions(+), 126 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3de04b18a2..5ab7933bbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1526,6 +1526,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 b0560e82fe..440ab646a6 100644 --- a/fastcrypto-zkp/Cargo.toml +++ b/fastcrypto-zkp/Cargo.toml @@ -47,6 +47,7 @@ typenum = "1.13.0" lazy_static = "1.4.0" itertools = "0.12.0" bincode = "1.3.3" +serde_with = "2.3.1" [dev-dependencies] ark-bls12-377 = "0.4.0" diff --git a/fastcrypto-zkp/src/groth16/api.rs b/fastcrypto-zkp/src/groth16/api.rs index 86256da498..9e0479c970 100644 --- a/fastcrypto-zkp/src/groth16/api.rs +++ b/fastcrypto-zkp/src/groth16/api.rs @@ -1,32 +1,48 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use serde::{Deserialize, Serialize}; +use serde::Deserialize; -use fastcrypto::error::FastCryptoError; -use fastcrypto::groups::{deserialize_vector, GroupElement, MultiScalarMul, Pairing, Scalar}; +use fastcrypto::error::{FastCryptoError, FastCryptoResult}; +use fastcrypto::groups::{ + deserialize_vector, serialize_vector, GroupElement, MultiScalarMul, Pairing, +}; +use fastcrypto::serde_helpers::ToFromByteArray; -use crate::groth16::{PreparedVerifyingKey, Proof, VerifyingKey}; +use crate::groth16::{PreparedVerifyingKey, VerifyingKey}; /// Deserialize bytes as an Arkworks representation of a verifying key, and return a vector of the /// four components of a prepared verified key (see more at [`PreparedVerifyingKey`]). -pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoError> +pub fn prepare_pvk_bytes< + G1, + const G1_SIZE: usize, + const G2_SIZE: usize, + const GT_SIZE: usize, + const FR_SIZE: usize, +>( + vk_bytes: &[u8], +) -> Result>, FastCryptoError> where - G1: Pairing + MultiScalarMul + Serialize + for<'a> Deserialize<'a>, - ::Other: Serialize + for<'a> Deserialize<'a>, - ::Output: GroupElement + Serialize + for<'a> Deserialize<'a>, + G1: Pairing + MultiScalarMul + for<'a> Deserialize<'a> + ToFromByteArray, + ::Other: for<'a> Deserialize<'a> + ToFromByteArray, + ::Output: GroupElement + for<'a> Deserialize<'a> + ToFromByteArray, { // TODO: The serialization does not match Arkworks' format. - let vk: VerifyingKey = - bcs::from_bytes(vk_bytes).map_err(|_| FastCryptoError::InvalidInput)?; - PreparedVerifyingKey::from(&vk).serialize_into_parts() + let vk = VerifyingKey::::from_arkworks_format::(vk_bytes)?; + Ok(PreparedVerifyingKey::from(&vk).serialize_into_parts()) } /// Verify Groth16 proof using the serialized form of the four components in a prepared verifying key /// (see more at [`PreparedVerifyingKey`]), serialized proof public input, which should /// be concatenated serialized field elements of the scalar field of [`crate::conversions::SCALAR_SIZE`] /// bytes each, and serialized proof points. -pub fn verify_groth16_in_bytes( +pub fn verify_groth16_in_bytes< + G1, + const G1_SIZE: usize, + const G2_SIZE: usize, + const GT_SIZE: usize, + const FR_SIZE: usize, +>( vk_gamma_abc_g1_bytes: &[u8], alpha_g1_beta_g2_bytes: &[u8], gamma_g2_neg_pc_bytes: &[u8], @@ -34,34 +50,184 @@ pub fn verify_groth16_in_bytes( proof_public_inputs_as_bytes: &[u8], proof_points_as_bytes: &[u8], ) -> Result - where - G1: Pairing + MultiScalarMul + Serialize + for<'a> Deserialize<'a>, - ::Other: Serialize + for<'a> Deserialize<'a>, - ::Output: GroupElement + Serialize + for<'a> Deserialize<'a>, +where + G1: Pairing + MultiScalarMul + ToFromByteArray + for<'a> Deserialize<'a>, + ::Other: ToFromByteArray + for<'a> Deserialize<'a>, + ::Output: GroupElement + ToFromByteArray + for<'a> Deserialize<'a>, + G1::ScalarType: ToFromByteArray + for<'a> Deserialize<'a>, { - let x = deserialize_vector::(proof_public_inputs_as_bytes, G1::ScalarType::SIZE_IN_BYTES)?; - let proof = bincode::deserialize(proof_points_as_bytes).map_err(|_| FastCryptoError::InvalidInput)?; + let x = deserialize_vector::(proof_public_inputs_as_bytes)?; + let proof = + bincode::deserialize(proof_points_as_bytes).map_err(|_| FastCryptoError::InvalidInput)?; + println!("proof: {:?}", proof); + let blst_pvk = PreparedVerifyingKey::::deserialize_from_parts(&vec![ vk_gamma_abc_g1_bytes, alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - ], G1_SIZE)?; + ])?; Ok(blst_pvk.verify(x.as_slice(), &proof).is_ok()) } +impl VerifyingKey { + pub fn from_arkworks_format( + vk_bytes: &[u8], + ) -> FastCryptoResult + where + G1: ToFromByteArray, + ::Other: ToFromByteArray, + { + if (vk_bytes.len() - (G1_SIZE + 3 * G2_SIZE + 8)) % G1_SIZE != 0 { + return Err(FastCryptoError::InvalidInput); + } + + let mut i = 0; + + let alpha = G1::from_byte_array( + vk_bytes[i..G1_SIZE] + .try_into() + .map_err(|_| FastCryptoError::InvalidInput)?, + )?; + + i += G1_SIZE; + + let beta = G1::Other::from_byte_array( + vk_bytes[i..i + G2_SIZE] + .try_into() + .map_err(|_| FastCryptoError::InvalidInput)?, + )?; + + i += G2_SIZE; + + let gamma = G1::Other::from_byte_array( + vk_bytes[i..i + G2_SIZE] + .try_into() + .map_err(|_| FastCryptoError::InvalidInput)?, + )?; + + i += G2_SIZE; + + let delta = G1::Other::from_byte_array( + vk_bytes[i..i + G2_SIZE] + .try_into() + .map_err(|_| FastCryptoError::InvalidInput)?, + )?; + + i += G2_SIZE; + + let n = u64::from_le_bytes( + vk_bytes[i..i + 8] + .try_into() + .map_err(|_| FastCryptoError::InvalidInput)?, + ); + + i += 8; + + let gamma_abc = deserialize_vector::(&vk_bytes[i..]) + .map_err(|_| FastCryptoError::InvalidInput)?; + + if gamma_abc.len() != n as usize { + return Err(FastCryptoError::InvalidInput); + } + + Ok(VerifyingKey { + alpha, + beta, + gamma, + delta, + gamma_abc, + }) + } +} + +impl PreparedVerifyingKey +where + ::Output: GroupElement, +{ + pub fn serialize_into_parts( + &self, + ) -> Vec> + where + G1: ToFromByteArray, + G1::Other: ToFromByteArray, + ::Output: ToFromByteArray, + { + let mut result = Vec::with_capacity(4); + result.push(serialize_vector(&self.vk_gamma_abc)); + result.push(self.alpha_beta.to_byte_array().to_vec()); + result.push(self.gamma_neg.to_byte_array().to_vec()); + result.push(self.delta_neg.to_byte_array().to_vec()); + result + } + + pub fn deserialize_from_parts< + const G1_SIZE: usize, + const G2_SIZE: usize, + const GT_SIZE: usize, + >( + parts: &Vec<&[u8]>, + ) -> FastCryptoResult + where + G1: ToFromByteArray, + G1::Other: ToFromByteArray, + ::Output: ToFromByteArray, + { + if parts.len() != 4 { + return Err(FastCryptoError::InvalidInput); + } + + if parts[0].len() % G1_SIZE != 0 { + return Err(FastCryptoError::InvalidInput); + } + let vk_gamma_abc = deserialize_vector::(parts[0])?; + let alpha_beta = ::Output::from_byte_array( + parts[1] + .try_into() + .map_err(|_| FastCryptoError::InvalidInput)?, + )?; + let gamma_neg = ::Other::from_byte_array( + parts[2] + .try_into() + .map_err(|_| FastCryptoError::InvalidInput)?, + )?; + let delta_neg = ::Other::from_byte_array( + parts[3] + .try_into() + .map_err(|_| FastCryptoError::InvalidInput)?, + )?; + + Ok(Self { + vk_gamma_abc, + alpha_beta, + gamma_neg, + delta_neg, + }) + } +} + #[cfg(test)] mod tests { - use crate::dummy_circuits::{DummyCircuit, Fibonacci}; + use std::ops::Mul; + use ark_bls12_381::{Bls12_381, Fr, G1Affine}; use ark_groth16::Groth16; use ark_serialize::CanonicalSerialize; use ark_snark::SNARK; use ark_std::rand::thread_rng; use ark_std::UniformRand; - use std::ops::Mul; - use fastcrypto::groups::bls12381::{G1_ELEMENT_BYTE_LENGTH, G1Element}; + use blake2::digest::Mac; + + use fastcrypto::groups::bls12381::{ + G1Element, Scalar, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, GT_ELEMENT_BYTE_LENGTH, + SCALAR_LENGTH, + }; + use fastcrypto::groups::serialize_vector; + + use crate::bls12381::conversions::bls_fr_to_blst_fr; + use crate::dummy_circuits::DummyCircuit; use crate::groth16::api::{prepare_pvk_bytes, verify_groth16_in_bytes}; + use crate::groth16::{PreparedVerifyingKey, Proof, VerifyingKey}; #[test] fn test_verify_groth16_in_bytes_api() { @@ -75,87 +241,131 @@ mod tests { }; let (pk, vk) = Groth16::::circuit_specific_setup(c, rng).unwrap(); - let proof = Groth16::::prove(&pk, c, rng).unwrap(); - let v = c.a.unwrap().mul(c.b.unwrap()); + let ark_proof = Groth16::::prove(&pk, c, rng).unwrap(); + let result = c.a.unwrap().mul(c.b.unwrap()); + let public_inputs = vec![Scalar(bls_fr_to_blst_fr(&result))]; let mut vk_bytes = vec![]; vk.serialize_compressed(&mut vk_bytes).unwrap(); - let bytes = prepare_pvk_bytes::(vk_bytes.as_slice()).unwrap(); + let bytes = prepare_pvk_bytes::< + G1Element, + G1_ELEMENT_BYTE_LENGTH, + G2_ELEMENT_BYTE_LENGTH, + GT_ELEMENT_BYTE_LENGTH, + SCALAR_LENGTH, + >(vk_bytes.as_slice()) + .unwrap(); let vk_gamma_abc_g1_bytes = &bytes[0]; let alpha_g1_beta_g2_bytes = &bytes[1]; let gamma_g2_neg_pc_bytes = &bytes[2]; let delta_g2_neg_pc_bytes = &bytes[3]; - let mut proof_inputs_bytes = vec![]; - v.serialize_compressed(&mut proof_inputs_bytes).unwrap(); + let mut public_inputs_bytes = serialize_vector(&public_inputs); - let mut proof_points_bytes = vec![]; - proof.serialize_compressed(&mut proof_points_bytes).unwrap(); + println!("ark proof: {:?}", ark_proof); + + let mut proof_bytes = Vec::new(); + ark_proof.serialize_compressed(&mut proof_bytes).unwrap(); + let proof: Proof = bincode::deserialize(&proof_bytes).unwrap(); + let vk = VerifyingKey::from_arkworks_format(&vk_bytes).unwrap(); + + let prepared_vk = PreparedVerifyingKey::from(&vk); + + assert!(prepared_vk.verify(&public_inputs, &proof).is_ok()); // Success case. - assert!(verify_groth16_in_bytes::( + assert!(verify_groth16_in_bytes::< + G1Element, + { G1_ELEMENT_BYTE_LENGTH }, + { G2_ELEMENT_BYTE_LENGTH }, + { GT_ELEMENT_BYTE_LENGTH }, + { SCALAR_LENGTH }, + >( vk_gamma_abc_g1_bytes, alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - &proof_inputs_bytes, - &proof_points_bytes + &public_inputs_bytes, + &proof_bytes ) - .unwrap()); + .unwrap()); // Negative test: Replace the A element with a random point. - let mut modified_proof_points_bytes = proof_points_bytes.clone(); + let mut modified_proof_points_bytes = proof_bytes.clone(); let _ = &G1Affine::rand(rng) .serialize_compressed(&mut modified_proof_points_bytes[0..48]) .unwrap(); - assert!(!verify_groth16_in_bytes::( + assert!(!verify_groth16_in_bytes::< + G1Element, + { G1_ELEMENT_BYTE_LENGTH }, + { G2_ELEMENT_BYTE_LENGTH }, + { GT_ELEMENT_BYTE_LENGTH }, + { SCALAR_LENGTH }, + >( vk_gamma_abc_g1_bytes, alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - &proof_inputs_bytes, + &public_inputs_bytes, &modified_proof_points_bytes ) - .unwrap()); + .unwrap()); // Length of verifying key is incorrect. let mut modified_bytes = bytes[0].clone(); modified_bytes.pop(); - assert!(verify_groth16_in_bytes::( + assert!(verify_groth16_in_bytes::< + G1Element, + { G1_ELEMENT_BYTE_LENGTH }, + { G2_ELEMENT_BYTE_LENGTH }, + { GT_ELEMENT_BYTE_LENGTH }, + { SCALAR_LENGTH }, + >( &modified_bytes, alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - &proof_inputs_bytes, - &proof_points_bytes + &public_inputs_bytes, + &proof_bytes ) - .is_err()); + .is_err()); // Length of public inputs is incorrect. - let mut modified_proof_inputs_bytes = proof_inputs_bytes.clone(); + let mut modified_proof_inputs_bytes = public_inputs_bytes.clone(); modified_proof_inputs_bytes.pop(); - assert!(verify_groth16_in_bytes::( + assert!(verify_groth16_in_bytes::< + G1Element, + { G1_ELEMENT_BYTE_LENGTH }, + { G2_ELEMENT_BYTE_LENGTH }, + { GT_ELEMENT_BYTE_LENGTH }, + { SCALAR_LENGTH }, + >( vk_gamma_abc_g1_bytes, alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, &modified_proof_inputs_bytes, - &proof_points_bytes + &proof_bytes ) - .is_err()); + .is_err()); // length of proof is incorrect - let mut modified_proof_points_bytes = proof_points_bytes.to_vec(); + let mut modified_proof_points_bytes = proof_bytes.to_vec(); modified_proof_points_bytes.pop(); - assert!(verify_groth16_in_bytes::( + assert!(verify_groth16_in_bytes::< + G1Element, + { G1_ELEMENT_BYTE_LENGTH }, + { G2_ELEMENT_BYTE_LENGTH }, + { GT_ELEMENT_BYTE_LENGTH }, + { SCALAR_LENGTH }, + >( vk_gamma_abc_g1_bytes, alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - &proof_inputs_bytes, + &public_inputs_bytes, &modified_proof_points_bytes ) - .is_err()); + .is_err()); } - -} \ No newline at end of file +} diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index 4323ad193f..8c2bceccd4 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -1,9 +1,9 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use std::fmt::Debug; use fastcrypto::groups::{GroupElement, Pairing}; use serde::Deserialize; +use std::fmt::Debug; pub mod api; mod prepared_vk; @@ -18,7 +18,7 @@ where c: G1, } -#[derive(Debug, Deserialize)] +#[derive(Debug)] pub struct VerifyingKey where G1::Other: Debug, 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 f7a188ab88..8e7c710b4d 100644 --- a/fastcrypto/src/groups/bls12381.rs +++ b/fastcrypto/src/groups/bls12381.rs @@ -51,7 +51,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; @@ -725,8 +725,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 e7c3cfe7f6..ac7dd9960a 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 62bf5679c3125f94ec2ad44209dcdc28e4e54613 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/65] clean up --- .../src/groth16/{api.rs => generic_api.rs} | 52 ++++++++++++------- fastcrypto-zkp/src/groth16/mod.rs | 8 +-- fastcrypto-zkp/src/groth16/prepared_vk.rs | 4 -- 3 files changed, 38 insertions(+), 26 deletions(-) rename fastcrypto-zkp/src/groth16/{api.rs => generic_api.rs} (89%) diff --git a/fastcrypto-zkp/src/groth16/api.rs b/fastcrypto-zkp/src/groth16/generic_api.rs similarity index 89% rename from fastcrypto-zkp/src/groth16/api.rs rename to fastcrypto-zkp/src/groth16/generic_api.rs index 9e0479c970..8ce3f49acb 100644 --- a/fastcrypto-zkp/src/groth16/api.rs +++ b/fastcrypto-zkp/src/groth16/generic_api.rs @@ -25,9 +25,8 @@ pub fn prepare_pvk_bytes< where G1: Pairing + MultiScalarMul + for<'a> Deserialize<'a> + ToFromByteArray, ::Other: for<'a> Deserialize<'a> + ToFromByteArray, - ::Output: GroupElement + for<'a> Deserialize<'a> + ToFromByteArray, + ::Output: GroupElement + ToFromByteArray, { - // TODO: The serialization does not match Arkworks' format. let vk = VerifyingKey::::from_arkworks_format::(vk_bytes)?; Ok(PreparedVerifyingKey::from(&vk).serialize_into_parts()) } @@ -53,21 +52,21 @@ pub fn verify_groth16_in_bytes< where G1: Pairing + MultiScalarMul + ToFromByteArray + for<'a> Deserialize<'a>, ::Other: ToFromByteArray + for<'a> Deserialize<'a>, - ::Output: GroupElement + ToFromByteArray + for<'a> Deserialize<'a>, - G1::ScalarType: ToFromByteArray + for<'a> Deserialize<'a>, + ::Output: GroupElement + ToFromByteArray, + G1::ScalarType: ToFromByteArray, { let x = deserialize_vector::(proof_public_inputs_as_bytes)?; let proof = bincode::deserialize(proof_points_as_bytes).map_err(|_| FastCryptoError::InvalidInput)?; - println!("proof: {:?}", proof); - let blst_pvk = PreparedVerifyingKey::::deserialize_from_parts(&vec![ + let prepared_vk = PreparedVerifyingKey::::deserialize_from_parts(&vec![ vk_gamma_abc_g1_bytes, alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, ])?; - Ok(blst_pvk.verify(x.as_slice(), &proof).is_ok()) + + Ok(prepared_vk.verify(x.as_slice(), &proof).is_ok()) } impl VerifyingKey { @@ -78,6 +77,17 @@ impl VerifyingKey { G1: ToFromByteArray, ::Other: ToFromByteArray, { + // The verifying key consists of: + // - alpha: G1 + // - beta: G2 + // - gamma: G2 + // - delta: G2 + // - n: u64 lendian (size of gamma_abc) + // - gamma_abc: Vec + + // We need to implement this instead of using bincode because the length of the vector is + // not in the default bincode format where only one byte is used to represent the length. + if (vk_bytes.len() - (G1_SIZE + 3 * G2_SIZE + 8)) % G1_SIZE != 0 { return Err(FastCryptoError::InvalidInput); } @@ -89,7 +99,6 @@ impl VerifyingKey { .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, )?; - i += G1_SIZE; let beta = G1::Other::from_byte_array( @@ -97,7 +106,6 @@ impl VerifyingKey { .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, )?; - i += G2_SIZE; let gamma = G1::Other::from_byte_array( @@ -105,7 +113,6 @@ impl VerifyingKey { .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, )?; - i += G2_SIZE; let delta = G1::Other::from_byte_array( @@ -113,7 +120,6 @@ impl VerifyingKey { .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, )?; - i += G2_SIZE; let n = u64::from_le_bytes( @@ -121,7 +127,6 @@ impl VerifyingKey { .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, ); - i += 8; let gamma_abc = deserialize_vector::(&vk_bytes[i..]) @@ -216,19 +221,27 @@ mod tests { use ark_snark::SNARK; use ark_std::rand::thread_rng; use ark_std::UniformRand; - use blake2::digest::Mac; use fastcrypto::groups::bls12381::{ G1Element, Scalar, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, GT_ELEMENT_BYTE_LENGTH, SCALAR_LENGTH, }; use fastcrypto::groups::serialize_vector; + use fastcrypto::serde_helpers::ToFromByteArray; - use crate::bls12381::conversions::bls_fr_to_blst_fr; use crate::dummy_circuits::DummyCircuit; - use crate::groth16::api::{prepare_pvk_bytes, verify_groth16_in_bytes}; + use crate::groth16::generic_api::{prepare_pvk_bytes, verify_groth16_in_bytes}; use crate::groth16::{PreparedVerifyingKey, Proof, VerifyingKey}; + fn scalar_from_arkworks(scalar: &Fr) -> Scalar { + let mut scalar_bytes = [0u8; SCALAR_LENGTH]; + scalar + .serialize_compressed(scalar_bytes.as_mut_slice()) + .unwrap(); + scalar_bytes.reverse(); + Scalar::from_byte_array(&scalar_bytes).unwrap() + } + #[test] fn test_verify_groth16_in_bytes_api() { const PUBLIC_SIZE: usize = 128; @@ -243,7 +256,8 @@ mod tests { let (pk, vk) = Groth16::::circuit_specific_setup(c, rng).unwrap(); let ark_proof = Groth16::::prove(&pk, c, rng).unwrap(); let result = c.a.unwrap().mul(c.b.unwrap()); - let public_inputs = vec![Scalar(bls_fr_to_blst_fr(&result))]; + let public_inputs = vec![scalar_from_arkworks(&result)]; + let mut vk_bytes = vec![]; vk.serialize_compressed(&mut vk_bytes).unwrap(); @@ -255,14 +269,13 @@ mod tests { SCALAR_LENGTH, >(vk_bytes.as_slice()) .unwrap(); + let vk_gamma_abc_g1_bytes = &bytes[0]; let alpha_g1_beta_g2_bytes = &bytes[1]; let gamma_g2_neg_pc_bytes = &bytes[2]; let delta_g2_neg_pc_bytes = &bytes[3]; - let mut public_inputs_bytes = serialize_vector(&public_inputs); - - println!("ark proof: {:?}", ark_proof); + let public_inputs_bytes = serialize_vector(&public_inputs); let mut proof_bytes = Vec::new(); ark_proof.serialize_compressed(&mut proof_bytes).unwrap(); @@ -271,6 +284,7 @@ mod tests { let prepared_vk = PreparedVerifyingKey::from(&vk); + // Check that the proof verifies without using the API assert!(prepared_vk.verify(&public_inputs, &proof).is_ok()); // Success case. diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index 8c2bceccd4..304b781443 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -1,11 +1,13 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use fastcrypto::groups::{GroupElement, Pairing}; -use serde::Deserialize; use std::fmt::Debug; -pub mod api; +use serde::Deserialize; + +use fastcrypto::groups::{GroupElement, Pairing}; + +pub(crate) mod generic_api; mod prepared_vk; #[derive(Debug, Deserialize)] 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 ab6a97d7e4e05345ea0daa0f7b8c5b80d7a8b3d4 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/65] Remove old bls impl --- fastcrypto-zkp/benches/conversions.rs | 120 --- fastcrypto-zkp/benches/proving.rs | 20 +- fastcrypto-zkp/examples/blake2s_demo.rs | 260 +++--- fastcrypto-zkp/src/bls12381/api.rs | 51 +- fastcrypto-zkp/src/bls12381/conversions.rs | 826 ------------------ fastcrypto-zkp/src/bls12381/mod.rs | 63 +- .../src/bls12381/unit_tests/api_tests.rs | 63 +- .../src/bls12381/unit_tests/verifier_tests.rs | 251 +----- fastcrypto-zkp/src/bls12381/verifier.rs | 370 -------- fastcrypto-zkp/src/groth16/generic_api.rs | 239 +---- fastcrypto-zkp/src/groth16/mod.rs | 44 +- fastcrypto-zkp/src/groth16/prepared_vk.rs | 92 -- fastcrypto/src/groups/mod.rs | 7 +- 13 files changed, 299 insertions(+), 2107 deletions(-) delete mode 100644 fastcrypto-zkp/benches/conversions.rs delete mode 100644 fastcrypto-zkp/src/bls12381/conversions.rs delete mode 100644 fastcrypto-zkp/src/bls12381/verifier.rs delete mode 100644 fastcrypto-zkp/src/groth16/prepared_vk.rs diff --git a/fastcrypto-zkp/benches/conversions.rs b/fastcrypto-zkp/benches/conversions.rs deleted file mode 100644 index 918beb694d..0000000000 --- a/fastcrypto-zkp/benches/conversions.rs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use ark_bls12_381::{Fq, Fq2, Fr as BlsFr}; -use ark_bls12_381::{Fq12, G2Affine as BlsG2Affine}; -use ark_bls12_381::{Fq6, G1Affine as BlsG1Affine}; -use ark_ec::{AffineRepr, CurveGroup}; -use ark_ff::UniformRand; -use criterion::{criterion_group, Criterion}; -use criterion::{measurement::Measurement, BenchmarkGroup}; -use fastcrypto_zkp::bls12381::conversions::{ - bls_fq12_to_blst_fp12, bls_fq2_to_blst_fp2, bls_fq6_to_blst_fp6, bls_fq_to_blst_fp, - bls_fr_to_blst_fr, bls_g1_affine_to_blst_g1_affine, bls_g2_affine_to_blst_g2_affine, - blst_fp12_to_bls_fq12, blst_fp2_to_bls_fq2, blst_fp6_to_bls_fq6, blst_fp_to_bls_fq, - blst_fr_to_bls_fr, blst_g1_affine_to_bls_g1_affine, blst_g2_affine_to_bls_g2_affine, -}; -use std::ops::Mul; - -fn convert_from_arkworks(grp: &mut BenchmarkGroup) { - let mut rng = ark_std::test_rng(); - let element = BlsFr::rand(&mut rng); - grp.bench_with_input("BlsFr -> blst_fr", &element, |b, element| { - b.iter(|| bls_fr_to_blst_fr(element)); - }); - - let element = Fq::rand(&mut rng); - grp.bench_with_input("Fq -> blst_fp", &element, |b, element| { - b.iter(|| bls_fq_to_blst_fp(element)); - }); - - let element = Fq2::rand(&mut rng); - grp.bench_with_input("Fq2 -> blst_fp2", &element, |b, element| { - b.iter(|| bls_fq2_to_blst_fp2(element)); - }); - - let element = Fq6::rand(&mut rng); - grp.bench_with_input("Fq6 -> blst_fp6", &element, |b, element| { - b.iter(|| bls_fq6_to_blst_fp6(element)); - }); - - let element = Fq12::rand(&mut rng); - grp.bench_with_input("Fq12 -> blst_fp12", &element, |b, element| { - b.iter(|| bls_fq12_to_blst_fp12(element)); - }); - - let scalar = BlsFr::rand(&mut rng); - let element = BlsG1Affine::generator().mul(scalar).into_affine(); - grp.bench_with_input("G1Affine -> blst_p1_affine", &element, |b, element| { - b.iter(|| bls_g1_affine_to_blst_g1_affine(element)); - }); - - let scalar = BlsFr::rand(&mut rng); - let element = BlsG2Affine::generator().mul(scalar).into_affine(); - grp.bench_with_input("G2Affine -> blst_p2_affine", &element, |b, element| { - b.iter(|| bls_g2_affine_to_blst_g2_affine(element)); - }); -} - -fn convert_from_blst(grp: &mut BenchmarkGroup) { - let mut rng = ark_std::test_rng(); - let bls = BlsFr::rand(&mut rng); - let element = bls_fr_to_blst_fr(&bls); - grp.bench_with_input("blst_fr -> BlsFr", &element, |b, element| { - b.iter(|| blst_fr_to_bls_fr(element)); - }); - - let bls = Fq::rand(&mut rng); - let element = bls_fq_to_blst_fp(&bls); - grp.bench_with_input("blst_fp -> Fp", &element, |b, element| { - b.iter(|| blst_fp_to_bls_fq(element)); - }); - - let bls = Fq2::rand(&mut rng); - let element = bls_fq2_to_blst_fp2(&bls); - grp.bench_with_input("blst_fp2 -> Fq2", &element, |b, element| { - b.iter(|| blst_fp2_to_bls_fq2(element)); - }); - - let bls = Fq6::rand(&mut rng); - let element = bls_fq6_to_blst_fp6(&bls); - grp.bench_with_input("blst_fp6 -> Fq6", &element, |b, element| { - b.iter(|| blst_fp6_to_bls_fq6(element)); - }); - - let bls = Fq12::rand(&mut rng); - let element = bls_fq12_to_blst_fp12(&bls); - grp.bench_with_input("blst_fp12 -> Fq12", &element, |b, element| { - b.iter(|| blst_fp12_to_bls_fq12(element)); - }); - - let scalar = BlsFr::rand(&mut rng); - let bls = BlsG1Affine::generator().mul(scalar).into_affine(); - let element = bls_g1_affine_to_blst_g1_affine(&bls); - grp.bench_with_input("blst_p1_affine -> G1Affine", &element, |b, element| { - b.iter(|| blst_g1_affine_to_bls_g1_affine(element)); - }); - - let scalar = BlsFr::rand(&mut rng); - let bls = BlsG2Affine::generator().mul(scalar).into_affine(); - let element = bls_g2_affine_to_blst_g2_affine(&bls); - grp.bench_with_input("blst_p2_affine -> G2Affine", &element, |b, element| { - b.iter(|| blst_g2_affine_to_bls_g2_affine(element)); - }); -} - -fn to_from_arkworks(c: &mut Criterion) { - let mut group: BenchmarkGroup<_> = c.benchmark_group("Conversions Arkworks -> Blst"); - convert_from_arkworks(&mut group); - group.finish(); - let mut group: BenchmarkGroup<_> = c.benchmark_group("Conversions Blst -> Arkworks"); - convert_from_blst(&mut group); - group.finish(); -} - -criterion_group! { - name = conversion_benches; - config = Criterion::default(); - targets = - to_from_arkworks, -} diff --git a/fastcrypto-zkp/benches/proving.rs b/fastcrypto-zkp/benches/proving.rs index 375332513a..feb16f8cee 100644 --- a/fastcrypto-zkp/benches/proving.rs +++ b/fastcrypto-zkp/benches/proving.rs @@ -7,19 +7,19 @@ use ark_bn254::{Bn254, Fr as Bn254Fr}; use ark_ec::pairing::Pairing; use ark_ff::{PrimeField, UniformRand}; use ark_groth16::Groth16; +use ark_serialize::CanonicalSerialize; use ark_snark::SNARK; use ark_std::rand::thread_rng; use criterion::{ criterion_group, criterion_main, measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion, SamplingMode, }; +use fastcrypto::groups::bls12381::G1Element; use fastcrypto_zkp::dummy_circuits::DummyCircuit; +use fastcrypto_zkp::groth16::Proof; use fastcrypto_zkp::{bls12381, bn254}; use std::ops::Mul; -#[path = "./conversions.rs"] -mod conversions; - #[path = "./utils.rs"] mod utils; @@ -427,19 +427,23 @@ fn bench_our_verify(grp: &mut BenchmarkGroup) { }; let (pk, ark_vk) = Groth16::::circuit_specific_setup(c, rng).unwrap(); - let proof = bls12381::Proof::from(Groth16::::prove(&pk, c, rng).unwrap()); - let v = c.a.unwrap().mul(c.b.unwrap()); + let ark_proof = Groth16::::prove(&pk, c, 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 v = c.a.unwrap().mul(c.b.unwrap()); let vk = ark_vk.into(); grp.bench_with_input( BenchmarkId::new("BLST-based Groth16 process verifying key", *size), &vk, |b, vk| { - b.iter(|| bls12381::verifier::PreparedVerifyingKey::from(vk)); + b.iter(|| fastcrypto_zkp::bls12381::PreparedVerifyingKey::from(vk)); }, ); - let pvk = bls12381::verifier::PreparedVerifyingKey::from(&vk); + let pvk = fastcrypto_zkp::bls12381::PreparedVerifyingKey::from(&vk); grp.bench_with_input( BenchmarkId::new("BLST-based Groth16 verify with processed vk", *size), @@ -501,4 +505,4 @@ criterion_group! { prove, } -criterion_main!(conversions::conversion_benches, proving_benches,); +criterion_main!(proving_benches,); diff --git a/fastcrypto-zkp/examples/blake2s_demo.rs b/fastcrypto-zkp/examples/blake2s_demo.rs index dba3b0b1de..b6b80311d7 100644 --- a/fastcrypto-zkp/examples/blake2s_demo.rs +++ b/fastcrypto-zkp/examples/blake2s_demo.rs @@ -1,130 +1,130 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use ark_bls12_381::{Bls12_381, Fr}; -pub use ark_ff::ToConstraintField; - -use ark_crypto_primitives::prf::{PRFGadget, PRF}; -use ark_crypto_primitives::{ - prf::blake2s::constraints::Blake2sGadget, prf::blake2s::Blake2s as B2SPRF, -}; -use ark_groth16::Groth16; -use ark_relations::r1cs::{ - ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, SynthesisError, -}; -use blake2::{digest::Digest, Blake2s256}; - -use ark_r1cs_std::prelude::*; -use ark_std::rand::thread_rng; -use fastcrypto_zkp::bls12381::conversions::BlsFr; -use fastcrypto_zkp::bls12381::verifier::PreparedVerifyingKey; -use fastcrypto_zkp::bls12381::{FieldElement, Proof}; - -#[derive(Clone, Copy, Debug)] -struct Blake2sCircuit { - input: [u8; 32], - blake2_seed: [u8; 32], - pub expected_output: [u8; 32], -} - -impl Blake2sCircuit { - fn new() -> Self { - // We're going to prove knowledge of the blake2 hash of this secret - // see https://en.wikipedia.org/wiki/Cluedo - let statement = b"butler in the yard with a wrench"; - let bytes = statement.to_vec(); - - // as a sanity-check, we'll also compute the blake2 hash of the secret - // with the reference implementation - - // Blake2s takes a seed parameter. We use a default seed. - let seed: [u8; 32] = [0u8; 32]; - - // Compute the hash by traditional means. - let mut h = Blake2s256::new_with_prefix(seed); - h.update(&bytes); - let hash_result = h.finalize(); - - // We use the arkworks API for blake2s as well and check it matches - let input: [u8; 32] = bytes[..].try_into().unwrap(); - let out = B2SPRF::evaluate(&seed, &input).unwrap(); - - // the traditional means and the arkworks implementation match - assert_eq!(hash_result.as_slice(), out.as_slice()); - - // at this stage, we can publish the seed and the expected hash output, - // and we'll prove we know the input - Self { - input: statement.to_owned(), - blake2_seed: seed, - expected_output: out, - } - } -} - -impl ConstraintSynthesizer for Blake2sCircuit { - fn generate_constraints(self, cs: ConstraintSystemRef) -> Result<(), SynthesisError> { - // initialize the blake2s gadget and allocate the seed - let seed_var = UInt8::new_input_vec(cs.clone(), &self.blake2_seed).unwrap(); - - // declare the witnesses - let input_var = UInt8::new_witness_vec(cs.clone(), &self.input).unwrap(); - - // declare the public intended output - let desired_out_var = >::OutputVar::new_input(cs, || { - Ok(self.expected_output) - }) - .unwrap(); - - // link the intended output to a blake2s computation on the input, i.e. constrain Blake2s(seed, input) == output - let output_var = Blake2sGadget::evaluate(&seed_var, &input_var).unwrap(); - output_var.enforce_equal(&desired_out_var).unwrap(); - - Ok(()) - } -} - -fn main() { - let mut rng = &mut thread_rng(); - - let circuit = Blake2sCircuit::new(); - // Sanity-check - { - let cs = ConstraintSystem::::new_ref(); - - circuit.generate_constraints(cs.clone()).unwrap(); - println!("Num constraints: {}", cs.num_constraints()); - assert!(cs.is_satisfied().unwrap()); - } - - let params = - Groth16::::generate_random_parameters_with_reduction(circuit, rng).unwrap(); - - // prepare a proof (note: there is nothing trustable about the trivial setup involved here) - let proof = Proof::from( - Groth16::::create_random_proof_with_reduction(circuit, ¶ms, &mut rng) - .unwrap(), - ); - - println!( - "Generated proof of knowledge of Blake2s preimage of {}", - hex::encode(circuit.expected_output) - ); - - // prepare the verification key - let pvk = PreparedVerifyingKey::from(¶ms.vk.into()); - - // provide the public inputs (the hash target) for verification - let inputs: Vec = [&circuit.blake2_seed[..], &circuit.expected_output[..]] - .iter() - .flat_map::, _>(|x| x.to_field_elements().unwrap()) - .map(FieldElement::from) - .collect(); - - // Verify the proof - assert!(pvk.verify(&inputs, &proof).unwrap()); - println!( - "Checked proof of knowledge of Blake2s preimage of {}!", - hex::encode(circuit.expected_output) - ); -} +// // Copyright (c) 2022, Mysten Labs, Inc. +// // SPDX-License-Identifier: Apache-2.0 +// +// use ark_bls12_381::{Bls12_381, Fr}; +// pub use ark_ff::ToConstraintField; +// +// use ark_crypto_primitives::prf::{PRF, PRFGadget}; +// use ark_crypto_primitives::{ +// prf::blake2s::Blake2s as B2SPRF, prf::blake2s::constraints::Blake2sGadget, +// }; +// use ark_groth16::Groth16; +// use ark_relations::r1cs::{ +// ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, SynthesisError, +// }; +// use blake2::{Blake2s256, digest::Digest}; +// +// use ark_r1cs_std::prelude::*; +// use ark_serialize::CanonicalSerialize; +// use ark_std::rand::thread_rng; +// use fastcrypto::groups::bls12381::{Scalar, SCALAR_LENGTH}; +// use fastcrypto_zkp::bls12381::{PreparedVerifyingKey, Proof}; +// +// #[derive(Clone, Copy, Debug)] +// struct Blake2sCircuit { +// input: [u8; 32], +// blake2_seed: [u8; 32], +// pub expected_output: [u8; 32], +// } +// +// impl Blake2sCircuit { +// fn new() -> Self { +// // We're going to prove knowledge of the blake2 hash of this secret +// // see https://en.wikipedia.org/wiki/Cluedo +// let statement = b"butler in the yard with a wrench"; +// let bytes = statement.to_vec(); +// +// // as a sanity-check, we'll also compute the blake2 hash of the secret +// // with the reference implementation +// +// // Blake2s takes a seed parameter. We use a default seed. +// let seed: [u8; 32] = [0u8; 32]; +// +// // Compute the hash by traditional means. +// let mut h = Blake2s256::new_with_prefix(seed); +// h.update(&bytes); +// let hash_result = h.finalize(); +// +// // We use the arkworks API for blake2s as well and check it matches +// let input: [u8; 32] = bytes[..].try_into().unwrap(); +// let out = B2SPRF::evaluate(&seed, &input).unwrap(); +// +// // the traditional means and the arkworks implementation match +// assert_eq!(hash_result.as_slice(), out.as_slice()); +// +// // at this stage, we can publish the seed and the expected hash output, +// // and we'll prove we know the input +// Self { +// input: statement.to_owned(), +// blake2_seed: seed, +// expected_output: out, +// } +// } +// } +// +// impl ConstraintSynthesizer for Blake2sCircuit { +// fn generate_constraints(self, cs: ConstraintSystemRef) -> Result<(), SynthesisError> { +// // initialize the blake2s gadget and allocate the seed +// let seed_var = UInt8::new_input_vec(cs.clone(), &self.blake2_seed).unwrap(); +// +// // declare the witnesses +// let input_var = UInt8::new_witness_vec(cs.clone(), &self.input).unwrap(); +// +// // declare the public intended output +// let desired_out_var = >::OutputVar::new_input(cs, || { +// Ok(self.expected_output) +// }) +// .unwrap(); +// +// // link the intended output to a blake2s computation on the input, i.e. constrain Blake2s(seed, input) == output +// let output_var = Blake2sGadget::evaluate(&seed_var, &input_var).unwrap(); +// output_var.enforce_equal(&desired_out_var).unwrap(); +// +// Ok(()) +// } +// } +// +// fn main() { +// let mut rng = &mut thread_rng(); +// +// let circuit = Blake2sCircuit::new(); +// // Sanity-check +// { +// let cs = ConstraintSystem::::new_ref(); +// +// circuit.generate_constraints(cs.clone()).unwrap(); +// println!("Num constraints: {}", cs.num_constraints()); +// assert!(cs.is_satisfied().unwrap()); +// } +// +// let params = +// Groth16::::generate_random_parameters_with_reduction(circuit, rng).unwrap(); +// +// // prepare a proof (note: there is nothing trustable about the trivial setup involved here) +// let proof = Proof::from( +// Groth16::::create_random_proof_with_reduction(circuit, ¶ms, &mut rng) +// .unwrap(), +// ); +// +// println!( +// "Generated proof of knowledge of Blake2s preimage of {}", +// hex::encode(circuit.expected_output) +// ); +// +// // prepare the verification key +// let pvk = PreparedVerifyingKey::from(¶ms.vk.into()); +// +// // provide the public inputs (the hash target) for verification +// let inputs: Vec = [&scalar_from_arkworks(circuit.blake2_seed[..]), &circuit.expected_output[..]] +// .iter() +// .flat_map::, _>(|x| x.to_field_elements().unwrap()) +// .map(FieldElement::from) +// .collect(); +// +// // Verify the proof +// assert!(pvk.verify(&inputs, &proof).unwrap()); +// println!( +// "Checked proof of knowledge of Blake2s preimage of {}!", +// hex::encode(circuit.expected_output) +// ); +// } diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index 62b688ac61..5724035274 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -1,12 +1,13 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use fastcrypto::error::FastCryptoError; +use fastcrypto::error::{FastCryptoError, FastCryptoResult}; +use fastcrypto::groups::bls12381::{ + G1Element, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, GT_ELEMENT_BYTE_LENGTH, + SCALAR_LENGTH, +}; -use crate::bls12381::verifier::PreparedVerifyingKey; -use crate::bls12381::FieldElement; -use crate::bls12381::Proof; -use crate::bls12381::VerifyingKey; +use crate::groth16::generic_api; #[cfg(test)] #[path = "unit_tests/api_tests.rs"] @@ -14,8 +15,13 @@ mod api_tests; /// Deserialize bytes as an Arkwork representation of a verifying key, and return a vector of the four components of a prepared verified key (see more at [`crate::verifier::PreparedVerifyingKey`]). pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoError> { - let vk = VerifyingKey::deserialize(vk_bytes)?; - PreparedVerifyingKey::from(&vk).serialize() + generic_api::prepare_pvk_bytes::< + G1Element, + { G1_ELEMENT_BYTE_LENGTH }, + { G2_ELEMENT_BYTE_LENGTH }, + { GT_ELEMENT_BYTE_LENGTH }, + { SCALAR_LENGTH }, + >(vk_bytes) } /// Verify Groth16 proof using the serialized form of the four components in a prepared verifying key @@ -30,13 +36,34 @@ pub fn verify_groth16_in_bytes( proof_public_inputs_as_bytes: &[u8], proof_points_as_bytes: &[u8], ) -> Result { - let x = FieldElement::deserialize_vector(proof_public_inputs_as_bytes)?; - let proof = Proof::deserialize(proof_points_as_bytes)?; - let blst_pvk = PreparedVerifyingKey::deserialize(&vec![ + generic_api::verify_groth16_in_bytes::< + G1Element, + { G1_ELEMENT_BYTE_LENGTH }, + { G2_ELEMENT_BYTE_LENGTH }, + { GT_ELEMENT_BYTE_LENGTH }, + { SCALAR_LENGTH }, + >( vk_gamma_abc_g1_bytes, alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - ])?; - blst_pvk.verify(x.as_slice(), &proof) + &reverse_endianness(proof_public_inputs_as_bytes)?, + proof_points_as_bytes, + ) +} + +/// The public inputs are in big-endian byte order (like arkworks), but [fastcrypto::groups::bls12381::Scalar::from_byte_array] +/// expects them in little-endian. +fn reverse_endianness(scalars: &[u8]) -> FastCryptoResult> { + if scalars.len() % SCALAR_LENGTH != 0 { + return Err(FastCryptoError::InvalidInput); + } + let mut reversed_scalars = Vec::with_capacity(scalars.len()); + for scalar in scalars.chunks_exact(SCALAR_LENGTH) { + let mut scalar_bytes = [0u8; SCALAR_LENGTH]; + scalar_bytes.copy_from_slice(scalar); + scalar_bytes.reverse(); + reversed_scalars.extend_from_slice(&scalar_bytes); + } + Ok(reversed_scalars) } diff --git a/fastcrypto-zkp/src/bls12381/conversions.rs b/fastcrypto-zkp/src/bls12381/conversions.rs deleted file mode 100644 index c0a6858c9e..0000000000 --- a/fastcrypto-zkp/src/bls12381/conversions.rs +++ /dev/null @@ -1,826 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 -use ark_ff::{BigInteger384, Fp384, PrimeField, Zero}; -use ark_serialize::{CanonicalSerialize, CanonicalSerializeWithFlags, Compress, EmptyFlags}; -use blst::{blst_fp, blst_fp12, blst_fp6, blst_fp_from_lendian, blst_p1_affine}; -use blst::{blst_fp2, blst_p1_deserialize}; -use blst::{blst_p1_affine_serialize, blst_uint64_from_fp}; -use blst::{blst_p2_affine, blst_p2_affine_serialize, blst_p2_deserialize, BLST_ERROR}; - -pub use ark_bls12_381::Fr as BlsFr; -use ark_bls12_381::{Fq, Fq2}; -use ark_bls12_381::{Fq12, G2Affine as BlsG2Affine}; -use ark_bls12_381::{Fq6, G1Affine as BlsG1Affine}; -use ark_ec::AffineRepr; - -use ark_serialize::CanonicalDeserialize; -use blst::{blst_fr, blst_fr_from_uint64, blst_uint64_from_fr}; -use byte_slice_cast::AsByteSlice; - -/// Size of scalar elements. -pub const SCALAR_SIZE: usize = 32; -/// G1 affine point compressed size. -pub const G1_COMPRESSED_SIZE: usize = 48; -const G2_COMPRESSED_SIZE: usize = 96; - -const G1_UNCOMPRESSED_SIZE: usize = 96; -const G2_UNCOMPRESSED_SIZE: usize = 192; - -#[inline] -fn u64s_from_bytes(bytes: &[u8; 32]) -> [u64; 4] { - [ - u64::from_le_bytes(bytes[0..8].try_into().unwrap()), - u64::from_le_bytes(bytes[8..16].try_into().unwrap()), - u64::from_le_bytes(bytes[16..24].try_into().unwrap()), - u64::from_le_bytes(bytes[24..32].try_into().unwrap()), - ] -} - -// Scalar Field conversions -/// Convert an Arkworks BLS12-381 scalar field element to a blst scalar field element. -pub fn bls_fr_to_blst_fr(fe: &BlsFr) -> blst_fr { - debug_assert_eq!(fe.serialized_size(Compress::Yes), SCALAR_SIZE); - let mut bytes = [0u8; SCALAR_SIZE]; - fe.serialize_with_flags(&mut bytes[..], EmptyFlags).unwrap(); - - let mut out = blst_fr::default(); - let bytes_u64 = u64s_from_bytes(&bytes); - - unsafe { blst_fr_from_uint64(&mut out, bytes_u64.as_ptr()) }; - out -} - -/// Convert a blst scalar field element to an Arkworks BLS12-381 scalar field element. -pub fn blst_fr_to_bls_fr(fe: &blst_fr) -> BlsFr { - let mut out = [0u64; 4]; - unsafe { blst_uint64_from_fr(out.as_mut_ptr(), fe) }; - let bytes = out.as_byte_slice(); - - BlsFr::from_le_bytes_mod_order(bytes) -} - -// Base Field conversions -/// Convert an Arkworks BLS12-381 prime field element to a blst prime field element. -pub fn bls_fq_to_blst_fp(f: &Fq) -> blst_fp { - let mut fp_bytes_le = [0u8; G1_UNCOMPRESSED_SIZE / 2]; - f.serialize_uncompressed(&mut fp_bytes_le[..]) - .expect("fp size correct"); - - let mut blst_fp = blst_fp::default(); - unsafe { - blst_fp_from_lendian(&mut blst_fp, fp_bytes_le.as_ptr()); - } - blst_fp -} - -/// Convert a blst prime field element to an Arkworks BLS12-381 prime field element. -pub fn blst_fp_to_bls_fq(f: &blst_fp) -> Fq { - let mut out = [0u64; 6]; - unsafe { blst_uint64_from_fp(out.as_mut_ptr(), f) }; - let bytes = out.as_byte_slice(); - Fq::deserialize_compressed(bytes).unwrap() -} - -// QFE conversions - -/// Convert an Arkworks BLS12-381 quadratic extension field element to a blst quadratic extension -/// field element. -pub fn bls_fq2_to_blst_fp2(f: &Fq2) -> blst_fp2 { - let mut fp_bytes_le = [0u8; G2_UNCOMPRESSED_SIZE / 2]; - f.serialize_uncompressed(&mut fp_bytes_le[..]) - .expect("fp size correct"); - - blst_fp2 { - fp: fp_bytes_le - .chunks(48) - .map(|fp_bytes| { - let mut blst_fp = blst_fp::default(); - unsafe { - blst_fp_from_lendian(&mut blst_fp, fp_bytes.as_ptr()); - } - blst_fp - }) - .collect::>() - .try_into() - .unwrap(), - } -} - -/// Convert a blst quadratic extension field element to an Arkworks BLS12-381 quadratic extension -/// field element. -pub fn blst_fp2_to_bls_fq2(f: &blst_fp2) -> Fq2 { - let [fp1, fp2] = f.fp; - let bls_fp1 = blst_fp_to_bls_fq(&fp1); - let bls_fp2 = blst_fp_to_bls_fq(&fp2); - Fq2::new(bls_fp1, bls_fp2) -} - -// Target Field conversions - -/// Convert an Arkworks BLS12-381 degree-6 extension field element to a blst degree-6 extension -/// field element. -pub fn bls_fq6_to_blst_fp6(f: &Fq6) -> blst_fp6 { - let c0 = bls_fq2_to_blst_fp2(&f.c0); - let c1 = bls_fq2_to_blst_fp2(&f.c1); - let c2 = bls_fq2_to_blst_fp2(&f.c2); - blst_fp6 { fp2: [c0, c1, c2] } -} - -/// Convert a blst degree-6 extension field element to an Arkworks BLS12-381 degree-6 extension -/// field element. -pub fn blst_fp6_to_bls_fq6(f: &blst_fp6) -> Fq6 { - let c0 = blst_fp2_to_bls_fq2(&f.fp2[0]); - let c1 = blst_fp2_to_bls_fq2(&f.fp2[1]); - let c2 = blst_fp2_to_bls_fq2(&f.fp2[2]); - Fq6::new(c0, c1, c2) -} - -/// Convert an Arkworks BLS12-381 target field element to a blst target field element. -pub fn bls_fq12_to_blst_fp12(f: &Fq12) -> blst_fp12 { - let c0 = bls_fq6_to_blst_fp6(&f.c0); - let c1 = bls_fq6_to_blst_fp6(&f.c1); - blst_fp12 { fp6: [c0, c1] } -} - -/// Convert a blst target field element to an Arkworks BLS12-381 target field element. -pub fn blst_fp12_to_bls_fq12(f: &blst_fp12) -> Fq12 { - let c0 = blst_fp6_to_bls_fq6(&f.fp6[0]); - let c1 = blst_fp6_to_bls_fq6(&f.fp6[1]); - Fq12::new(c0, c1) -} - -// Affine point translations: those mostly allow us to receive the -// proof points, provided in affine form. - -fn blst_g1_affine_infinity() -> blst_p1_affine { - blst_p1_affine { - x: blst_fp::default(), - y: blst_fp::default(), - } -} - -fn bls_g1_affine_infinity() -> BlsG1Affine { - BlsG1Affine::zero() -} - -/// Convert an Arkworks BLS12-381 affine G1 point to a blst affine G1 point. -pub fn bls_g1_affine_to_blst_g1_affine(pt: &BlsG1Affine) -> blst_p1_affine { - debug_assert_eq!(pt.uncompressed_size(), G1_UNCOMPRESSED_SIZE); - // BLST disagrees with Arkworks on the uncompressed representation of the infinity point on G1 - // https://github.com/supranational/blst/blob/6382d67c72119d563975892ed49ba32e92d3d0da/src/e1.c#L153-L162 - // the infinity bit notwithstanding, - // Arkworks: x = Fq::zero(), y = Fq::one() - // BLST: x = y = Fq::zero() - // BLST follows the standard here (see https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-10#appendix-C) - if pt.infinity { - return blst_g1_affine_infinity(); - } - let tmp_p1 = blst_p1_affine { - x: bls_fq_to_blst_fp(&pt.x), - y: bls_fq_to_blst_fp(&pt.y), - }; - // See https://github.com/arkworks-rs/curves/issues/14 for why the double serialize - // we're in fact applying correct masks that arkworks does not use. This may be solved - // alternatively using https://github.com/arkworks-rs/algebra/issues/308 in a later release of - // Arkworks. - // Note: this is an uncompressed serialization - deserialization. - let mut tmp2 = [0u8; 96]; - unsafe { - blst_p1_affine_serialize(tmp2.as_mut_ptr(), &tmp_p1); - }; - - let mut g1 = blst_p1_affine::default(); - let result = unsafe { blst_p1_deserialize(&mut g1, tmp2.as_ptr()) }; - debug_assert_eq!(result, BLST_ERROR::BLST_SUCCESS); - g1 -} - -/// Convert a blst affine G1 point to an Arkworks BLS12-381 affine G1 point. -pub fn blst_g1_affine_to_bls_g1_affine(pt: &blst_p1_affine) -> BlsG1Affine { - let mut out = [0u8; G1_UNCOMPRESSED_SIZE]; - unsafe { - blst_p1_affine_serialize(out.as_mut_ptr(), pt); - } - let infinity = out[0] == 0x40; - // BLST disagrees with Arkworks on the uncompressed representation of the infinity point on G1 - // https://github.com/supranational/blst/blob/6382d67c72119d563975892ed49ba32e92d3d0da/src/e1.c#L153-L162 - // the infinity bit notwithstanding, - // Arkworks: x = Fq::zero(), y = Fq::one() - // BLST: x = y = Fq::zero() - // BLST follows the standard here (see https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-10#appendix-C) - if infinity { - return bls_g1_affine_infinity(); - } - let y = Fp384::from_be_bytes_mod_order(&out[48..]); - let x = { - // Mask away the flag bits (which Arkworks doesn't use). - out[0] &= 0b0001_1111; - Fp384::from_be_bytes_mod_order(&out[..48]) - }; - BlsG1Affine::new(x, y) -} - -fn blst_g2_affine_infinity() -> blst_p2_affine { - blst_p2_affine { - x: blst_fp2::default(), - y: blst_fp2::default(), - } -} - -fn bls_g2_affine_infinity() -> BlsG2Affine { - BlsG2Affine::zero() -} - -/// Convert an Arkworks BLS12-381 affine G2 point to a blst affine G2 point. -pub fn bls_g2_affine_to_blst_g2_affine(pt: &BlsG2Affine) -> blst_p2_affine { - debug_assert_eq!(pt.uncompressed_size(), G2_UNCOMPRESSED_SIZE); - // BLST disagrees with Arkworks on the uncompressed representation of the infinity point on G2 - // https://github.com/supranational/blst/blob/6382d67c72119d563975892ed49ba32e92d3d0da/src/e2.c#L194-L203 - // the infinity bit notwithstanding, - // Arkworks: x = Fq2::zero(), y = Fq2::one() - // BLST: x = y = Fq2::zero() - // BLST follows the standard here (see https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-10#appendix-C) - if pt.infinity { - return blst_g2_affine_infinity(); - } - let tmp_p2 = blst_p2_affine { - x: bls_fq2_to_blst_fp2(&pt.x), - y: bls_fq2_to_blst_fp2(&pt.y), - }; - // See https://github.com/arkworks-rs/curves/issues/14 for why the double serialize - // we're in fact applying correct masks that arkworks does not use. This may be solved - // alternatively using https://github.com/arkworks-rs/algebra/issues/308 in a later release of - // Arkworks. - // Note: this is an uncompressed serialization - deserialization. - let mut tmp2 = [0u8; G2_UNCOMPRESSED_SIZE]; - unsafe { - blst_p2_affine_serialize(tmp2.as_mut_ptr(), &tmp_p2); - }; - - let mut g2 = blst_p2_affine::default(); - let result = unsafe { blst_p2_deserialize(&mut g2, tmp2.as_ptr()) }; - debug_assert_eq!(result, BLST_ERROR::BLST_SUCCESS); - g2 -} - -/// Convert a blst affine G2 point to an Arkworks BLS12-381 affine G2 point. -pub fn blst_g2_affine_to_bls_g2_affine(pt: &blst_p2_affine) -> BlsG2Affine { - let ptx = blst_fp2_to_bls_fq2(&pt.x); - let pty = blst_fp2_to_bls_fq2(&pt.y); - - let infinity = ptx == Fq2::zero(); - // BLST disagrees with Arkworks on the uncompressed representation of the infinity point on G2 - // https://github.com/supranational/blst/blob/6382d67c72119d563975892ed49ba32e92d3d0da/src/e2.c#L194-L203 - // the infinity bit notwithstanding, - // Arkworks: x = Fq2::zero(), y = Fq2::one() - // BLST: x = y = Fq2::zero() - // BLST follows the standard here (see https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves-10#appendix-C) - if infinity { - bls_g2_affine_infinity() - } else { - BlsG2Affine::new(ptx, pty) - } -} - -///////////////////////////////////////////////////////////// -// Zcash point encodings to Arkworks points and back // -///////////////////////////////////////////////////////////// - -// The standard for serialization of compressed G1, G2 points of BLS12-381 -// is not followed by Arkworks 0.3.0. This is a translation layer to allow -// us to use (and receive) the standard serialization format. -// See section 5.4.9.2 of https://zips.z.cash/protocol/protocol.pdf -// and https://datatracker.ietf.org/doc/draft-irtf-cfrg-bls-signature/ Appendix A -// for its choice as a standard. - -// A note on BLST: blst uses Zcash point encoding formats across the board. - -fn bls_fq_to_zcash_bytes(field: &Fq) -> [u8; G1_COMPRESSED_SIZE] { - let mut result = [0u8; G1_COMPRESSED_SIZE]; - - let rep = field.into_bigint(); - - result[0..8].copy_from_slice(&rep.0[5].to_be_bytes()); - result[8..16].copy_from_slice(&rep.0[4].to_be_bytes()); - result[16..24].copy_from_slice(&rep.0[3].to_be_bytes()); - result[24..32].copy_from_slice(&rep.0[2].to_be_bytes()); - result[32..40].copy_from_slice(&rep.0[1].to_be_bytes()); - result[40..48].copy_from_slice(&rep.0[0].to_be_bytes()); - - result -} - -fn bls_fq_from_zcash_bytes(bytes: &[u8; G1_COMPRESSED_SIZE]) -> Option { - let mut tmp = BigInteger384::default(); - - tmp.0[5] = u64::from_be_bytes(bytes[0..8].try_into().unwrap()); - tmp.0[4] = u64::from_be_bytes(bytes[8..16].try_into().unwrap()); - tmp.0[3] = u64::from_be_bytes(bytes[16..24].try_into().unwrap()); - tmp.0[2] = u64::from_be_bytes(bytes[24..32].try_into().unwrap()); - tmp.0[1] = u64::from_be_bytes(bytes[32..40].try_into().unwrap()); - tmp.0[0] = u64::from_be_bytes(bytes[40..48].try_into().unwrap()); - - Fq::from_bigint(tmp) -} - -struct EncodingFlags { - is_compressed: bool, - is_infinity: bool, - is_lexicographically_largest: bool, -} - -impl From<&[u8]> for EncodingFlags { - fn from(bytes: &[u8]) -> Self { - Self { - is_compressed: ((bytes[0] >> 7) & 1) == 1, - is_infinity: (bytes[0] >> 6) & 1 == 1, - is_lexicographically_largest: (bytes[0] >> 5) & 1 == 1, - } - } -} - -impl EncodingFlags { - fn encode_flags(&self, bytes: &mut [u8]) { - if self.is_compressed { - bytes[0] |= 1 << 7; - } - - if self.is_infinity { - bytes[0] |= 1 << 6; - } - - if self.is_compressed && !self.is_infinity && self.is_lexicographically_largest { - bytes[0] |= 1 << 5; - } - } -} - -/// This deserializes an Arkworks G1Affine point from a Zcash point encoding. -pub fn bls_g1_affine_from_zcash_bytes(bytes: &[u8; G1_COMPRESSED_SIZE]) -> Option { - // Obtain the three flags from the start of the byte sequence. - let flags = EncodingFlags::from(&bytes[..]); - - if !flags.is_compressed { - return None; // We only support compressed points. - } - - if flags.is_infinity { - return Some(BlsG1Affine::zero()); - } - // Attempt to obtain the x-coordinate. - let x = obtain_x_coordinate(bytes.as_slice())?; - - BlsG1Affine::get_point_from_x_unchecked(x, flags.is_lexicographically_largest) -} - -/// This serializes an Arkworks G1Affine point into a Zcash point encoding. -pub fn bls_g1_affine_to_zcash_bytes(p: &BlsG1Affine) -> [u8; G1_COMPRESSED_SIZE] { - let mut result = bls_fq_to_zcash_bytes(&p.x); - let encoding = EncodingFlags { - is_compressed: true, - is_infinity: p.infinity, - is_lexicographically_largest: p.y > -p.y, - }; - encoding.encode_flags(&mut result[..]); - result -} - -/// This deserializes an Arkworks G2Affine point from a Zcash point encoding. -pub fn bls_g2_affine_from_zcash_bytes(bytes: &[u8; G2_COMPRESSED_SIZE]) -> Option { - // Obtain the three flags from the start of the byte sequence - let flags = EncodingFlags::from(&bytes[..]); - - if !flags.is_compressed { - return None; // We only support compressed points. - } - - if flags.is_infinity { - return Some(BlsG2Affine::default()); - } - - let xc1 = obtain_x_coordinate(bytes.as_slice())?; - let xc0 = { - let mut tmp = [0; G1_COMPRESSED_SIZE]; - tmp.copy_from_slice(&bytes[48..96]); - - bls_fq_from_zcash_bytes(&tmp)? - }; - - let x = Fq2::new(xc0, xc1); - - BlsG2Affine::get_point_from_x_unchecked(x, flags.is_lexicographically_largest) -} - -/// This serializes an Arkworks G2Affine point into a Zcash point encoding. -pub fn bls_g2_affine_to_zcash_bytes(p: &BlsG2Affine) -> [u8; G2_COMPRESSED_SIZE] { - let mut bytes = [0u8; G2_COMPRESSED_SIZE]; - - let c1_bytes = bls_fq_to_zcash_bytes(&p.x.c1); - let c0_bytes = bls_fq_to_zcash_bytes(&p.x.c0); - bytes[0..48].copy_from_slice(&c1_bytes[..]); - bytes[48..96].copy_from_slice(&c0_bytes[..]); - - let encoding = EncodingFlags { - is_compressed: true, - is_infinity: p.infinity, - is_lexicographically_largest: p.y > -p.y, - }; - - encoding.encode_flags(&mut bytes[..]); - bytes -} - -/// Attempt to obtain x_coordinate -fn obtain_x_coordinate(bytes: &[u8]) -> Option { - let mut tmp = [0; G1_COMPRESSED_SIZE]; - // this is safe as the private obtain_x_coordinate function is only invoked with G1 (48) and - // G2 (96) size inputs. - tmp.copy_from_slice(&bytes[0..G1_COMPRESSED_SIZE]); - - // Mask away the flag bits - tmp[0] &= 0b0001_1111; - - bls_fq_from_zcash_bytes(&tmp) -} - -#[cfg(test)] -pub(crate) mod tests { - use super::*; - use ark_bls12_381::Fr as BlsFr; - use ark_ec::AffineRepr; - use ark_ff::Field; - use blst::{ - blst_encode_to_g1, blst_encode_to_g2, blst_fp_from_uint64, blst_fr, blst_fr_from_uint64, - blst_p1, blst_p1_affine_compress, blst_p1_to_affine, blst_p1_uncompress, blst_p2, - blst_p2_affine_compress, blst_p2_to_affine, blst_p2_uncompress, - }; - use proptest::{collection, prelude::*}; - use std::ops::Mul; - - // Scalar roundtrips. - - pub(crate) fn arb_bls_fr() -> impl Strategy { - collection::vec(any::(), 32..=32) - .prop_map(|bytes| BlsFr::from_random_bytes(&bytes[..])) - .prop_filter("Valid field elements", Option::is_some) - .prop_map(|opt_fr| opt_fr.unwrap()) - .no_shrink() // this is arbitrary. - } - - fn arb_blst_fr() -> impl Strategy { - collection::vec(any::(), 4..=4) - .prop_map(|u64s| { - let mut out = blst_fr::default(); - unsafe { blst_fr_from_uint64(&mut out, u64s[..].as_ptr()) }; - out - }) - .no_shrink() // this is arbitrary. - } - - proptest! { - #[test] - fn roundtrip_bls_fr(b in arb_bls_fr()) { - let blst_variant = bls_fr_to_blst_fr(&b); - let roundtrip = blst_fr_to_bls_fr(&blst_variant); - prop_assert_eq!(b, roundtrip); - } - - #[test] - fn roundtrip_blst_fr(b in arb_blst_fr()) { - let bls_variant = blst_fr_to_bls_fr(&b); - let roundtrip = bls_fr_to_blst_fr(&bls_variant); - prop_assert_eq!(b, roundtrip); - } - } - - // Base field roundtrips. - - fn arb_bls_fq() -> impl Strategy { - collection::vec(any::(), 48..=48) - .prop_map(|bytes| Fp384::from_random_bytes(&bytes[..])) - .prop_filter("Valid field elements", Option::is_some) - .prop_map(|opt_fr| opt_fr.unwrap()) - .no_shrink() // this is arbitrary. - } - - fn arb_blst_fp() -> impl Strategy { - collection::vec(any::(), 6..=6) - .prop_map(|u64s| { - let mut out = blst_fp::default(); - unsafe { blst_fp_from_uint64(&mut out, u64s[..].as_ptr()) }; - out - }) - .no_shrink() // this is arbitrary. - } - - proptest! { - #[test] - fn roundtrip_bls_fq(b in arb_bls_fq()) { - let blst_variant = bls_fq_to_blst_fp(&b); - let roundtrip = blst_fp_to_bls_fq(&blst_variant); - prop_assert_eq!(b, roundtrip); - } - - #[test] - fn roundtrip_blst_fp(b in arb_blst_fp()) { - let bls_variant = blst_fp_to_bls_fq(&b); - let roundtrip = bls_fq_to_blst_fp(&bls_variant); - prop_assert_eq!(b, roundtrip); - } - - #[test] - fn roundtrip_bls_fq_to_zcash_bytes(b in arb_bls_fq()) { - let zcash_bytes = bls_fq_to_zcash_bytes(&b); - let roundtrip = bls_fq_from_zcash_bytes(&zcash_bytes); - prop_assert_eq!(Some(b), roundtrip); - } - } - - // QFE roundtrips. - - fn arb_bls_fq2() -> impl Strategy { - (arb_bls_fq(), arb_bls_fq()) - .prop_map(|(fp1, fp2)| Fq2::new(fp1, fp2)) - .no_shrink() - } - - fn arb_blst_fp2() -> impl Strategy { - (arb_blst_fp(), arb_blst_fp()) - .prop_map(|(fp1, fp2)| blst_fp2 { fp: [fp1, fp2] }) - .no_shrink() - } - - proptest! { - #[test] - fn roundtrip_bls_fq2(b in arb_bls_fq2()) { - let blst_variant = bls_fq2_to_blst_fp2(&b); - let roundtrip = blst_fp2_to_bls_fq2(&blst_variant); - prop_assert_eq!(b, roundtrip); - } - - #[test] - fn roundtrip_blst_fp2(b in arb_blst_fp2()) { - let bls_variant = blst_fp2_to_bls_fq2(&b); - let roundtrip = bls_fq2_to_blst_fp2(&bls_variant); - prop_assert_eq!(b, roundtrip); - } - } - - // Target field roundtrips. - - fn arb_bls_fq6() -> impl Strategy { - (arb_bls_fq2(), arb_bls_fq2(), arb_bls_fq2()) - .prop_map(|(f_c0, f_c1, f_c2)| Fq6::new(f_c0, f_c1, f_c2)) - .no_shrink() - } - - fn arb_blst_fp6() -> impl Strategy { - (arb_blst_fp2(), arb_blst_fp2(), arb_blst_fp2()) - .prop_map(|(f_c0, f_c1, f_c2)| blst_fp6 { - fp2: [f_c0, f_c1, f_c2], - }) - .no_shrink() - } - - proptest! { - #[test] - fn roundtrip_bls_fq6(b in arb_bls_fq6()){ - let blst_variant = bls_fq6_to_blst_fp6(&b); - let roundtrip = blst_fp6_to_bls_fq6(&blst_variant); - prop_assert_eq!(b, roundtrip); - } - - #[test] - fn roundtrip_blst_fp6(b in arb_blst_fp6()){ - let bls_variant = blst_fp6_to_bls_fq6(&b); - let roundtrip = bls_fq6_to_blst_fp6(&bls_variant); - prop_assert_eq!(b, roundtrip); - } - } - - fn arb_bls_fq12() -> impl Strategy { - (arb_bls_fq6(), arb_bls_fq6()) - .prop_map(|(f_c0, f_c1)| Fq12::new(f_c0, f_c1)) - .no_shrink() - } - - fn arb_blst_fp12() -> impl Strategy { - (arb_blst_fp6(), arb_blst_fp6()) - .prop_map(|(f_c0, f_c1)| blst_fp12 { fp6: [f_c0, f_c1] }) - .no_shrink() - } - - proptest! { - #[test] - fn roundtrip_bls_fq12(b in arb_bls_fq12()) { - let blst_variant = bls_fq12_to_blst_fp12(&b); - let roundtrip = blst_fp12_to_bls_fq12(&blst_variant); - prop_assert_eq!(b, roundtrip); - } - - #[test] - fn roundtrip_blst_fp12(b in arb_blst_fp12()) { - let bls_variant = blst_fp12_to_bls_fq12(&b); - let roundtrip = bls_fq12_to_blst_fp12(&bls_variant); - prop_assert_eq!(b, roundtrip); - } - } - - // Affine point roundtrips. - - fn bls_g1_affine_infinity() -> BlsG1Affine { - BlsG1Affine::zero() - } - - pub(crate) fn arb_bls_g1_affine() -> impl Strategy { - prop_oneof![ - // 1% chance of being the point at infinity. - 1 => - Just(bls_g1_affine_infinity()), - 99 => // slow, but good enough for tests. - (arb_bls_fr()).prop_map(|s| { - - BlsG1Affine::generator() - .mul(s) - .into() - - }) - ] - } - - fn blst_g1_affine_infinity() -> blst_p1_affine { - let mut res = [0u8; G1_UNCOMPRESSED_SIZE]; - res[0] = 0x40; - let mut g1_infinity = blst_p1_affine::default(); - assert_eq!( - unsafe { blst_p1_deserialize(&mut g1_infinity, res.as_ptr()) }, - BLST_ERROR::BLST_SUCCESS - ); - g1_infinity - } - - pub(crate) fn arb_blst_g1_affine() -> impl Strategy { - prop_oneof![ - // 1% chance of being the point at infinity. - 1 => Just(blst_g1_affine_infinity()), - 99 => - (collection::vec(any::(), 32..=32)).prop_map(|msg| { - - // We actually hash to a G1Projective, then convert to affine. - let mut out = blst_p1::default(); - const DST: [u8; 16] = [0; 16]; - const AUG: [u8; 16] = [0; 16]; - - unsafe { - blst_encode_to_g1( - &mut out, - msg.as_ptr(), - msg.len(), - DST.as_ptr(), - DST.len(), - AUG.as_ptr(), - AUG.len(), - ) - }; - - let mut res = blst_p1_affine::default(); - - unsafe { blst_p1_to_affine(&mut res, &out) }; - res - } - ) - ] - } - - proptest! { - #[test] - fn roundtrip_bls_g1_affine(b in arb_bls_g1_affine()) { - let blst_variant = bls_g1_affine_to_blst_g1_affine(&b); - let roundtrip = blst_g1_affine_to_bls_g1_affine(&blst_variant); - assert_eq!(b, roundtrip); - } - - #[test] - fn roundtrip_blst_g1_affine(b in arb_blst_g1_affine()) { - let bls_variant = blst_g1_affine_to_bls_g1_affine(&b); - let roundtrip = bls_g1_affine_to_blst_g1_affine(&bls_variant); - assert_eq!(b, roundtrip); - } - - #[test] - fn roundtrip_bls_g1_affine_zcash(b in arb_bls_g1_affine()) { - let zcash_bytes = bls_g1_affine_to_zcash_bytes(&b); - let roundtrip = bls_g1_affine_from_zcash_bytes(&zcash_bytes).unwrap(); - assert_eq!(b, roundtrip); - } - - #[test] - fn compatibility_bls_blst_g1_affine_serde(b in arb_bls_g1_affine(), bt in arb_blst_g1_affine()) { - let zcash_bytes = bls_g1_affine_to_zcash_bytes(&b); - // blst accepts & expects Zcash encodings. - let mut g1 = blst_p1_affine::default(); - assert_eq!(unsafe { blst_p1_uncompress(&mut g1, zcash_bytes.as_ptr()) }, BLST_ERROR::BLST_SUCCESS); - - // blst produces Zcash encodings. - let mut tmp2 = [0u8; 48]; - unsafe { - blst_p1_affine_compress(tmp2.as_mut_ptr(), &bt); - }; - assert!(bls_g1_affine_from_zcash_bytes(&tmp2).is_some()); - } - } - - fn bls_g2_affine_infinity() -> BlsG2Affine { - BlsG2Affine::zero() - } - - fn arb_bls_g2_affine() -> impl Strategy { - // slow, but good enough for tests. - (arb_bls_fr(), any::()).prop_map(|(s, maybe_infinity)| { - if maybe_infinity < 0.1 { - bls_g2_affine_infinity() - } else { - BlsG2Affine::generator().mul(s).into() - } - }) - } - - fn blst_g2_affine_infinity() -> blst_p2_affine { - let mut res = [0u8; G2_UNCOMPRESSED_SIZE]; - res[0] = 0x40; - let mut g2_infinity = blst_p2_affine::default(); - assert_eq!( - unsafe { blst_p2_deserialize(&mut g2_infinity, res.as_ptr()) }, - BLST_ERROR::BLST_SUCCESS - ); - g2_infinity - } - - pub(crate) fn arb_blst_g2_affine() -> impl Strategy { - prop_oneof![ - 1 => // 1% chance of being the point at infinity. - Just(blst_g2_affine_infinity()), - 99 => (collection::vec(any::(), 32..=32)).prop_map(|msg| { - // We actually hash to a G2Projective, then convert to affine. - let mut out = blst_p2::default(); - const DST: [u8; 16] = [0; 16]; - const AUG: [u8; 16] = [0; 16]; - - unsafe { - blst_encode_to_g2( - &mut out, - msg.as_ptr(), - msg.len(), - DST.as_ptr(), - DST.len(), - AUG.as_ptr(), - AUG.len(), - ) - }; - - let mut res = blst_p2_affine::default(); - - unsafe { blst_p2_to_affine(&mut res, &out) }; - res - }) - ] - } - - proptest! { - #[test] - fn roundtrip_bls_g2_affine(b in arb_bls_g2_affine()) { - let blst_variant = bls_g2_affine_to_blst_g2_affine(&b); - let roundtrip = blst_g2_affine_to_bls_g2_affine(&blst_variant); - assert_eq!(b, roundtrip); - } - - #[test] - fn roundtrip_blst_g2_affine(b in arb_blst_g2_affine()) { - let bls_variant = blst_g2_affine_to_bls_g2_affine(&b); - let roundtrip = bls_g2_affine_to_blst_g2_affine(&bls_variant); - assert_eq!(b, roundtrip); - } - - #[test] - fn roundtrip_bls_g2_affine_zcash(b in arb_bls_g2_affine()) { - let zcash_bytes = bls_g2_affine_to_zcash_bytes(&b); - let roundtrip = bls_g2_affine_from_zcash_bytes(&zcash_bytes).unwrap(); - assert_eq!(b, roundtrip); - } - - #[test] - fn compatibility_bls_blst_g2_affine_serde(b in arb_bls_g2_affine(), bt in arb_blst_g2_affine()) { - let zcash_bytes = bls_g2_affine_to_zcash_bytes(&b); - let mut g2 = blst_p2_affine::default(); - // blst accepts & expects Zcash encodings. - assert!(unsafe { blst_p2_uncompress(&mut g2, zcash_bytes.as_ptr()) } == BLST_ERROR::BLST_SUCCESS); - - // blst produces Zcash encodings. - let mut tmp2 = [0u8; 96]; - unsafe { - blst_p2_affine_compress(tmp2.as_mut_ptr(), &bt); - }; - assert!(bls_g2_affine_from_zcash_bytes(&tmp2).is_some()); - } - } -} diff --git a/fastcrypto-zkp/src/bls12381/mod.rs b/fastcrypto-zkp/src/bls12381/mod.rs index 4516db71e3..11be26fd98 100644 --- a/fastcrypto-zkp/src/bls12381/mod.rs +++ b/fastcrypto-zkp/src/bls12381/mod.rs @@ -5,63 +5,16 @@ //! Groth16 verifier over the BLS12-381 elliptic curve construction. -use crate::bls12381::conversions::{BlsFr, SCALAR_SIZE}; -use ark_bls12_381::Bls12_381; -use ark_serialize::CanonicalDeserialize; -use derive_more::From; -use fastcrypto::error::{FastCryptoError, FastCryptoResult}; - -/// Conversions between arkworks <-> blst -pub mod conversions; - -/// Groth16 SNARK verifier -pub mod verifier; +use crate::groth16; +use fastcrypto::groups::bls12381::G1Element; /// API that takes in serialized inputs pub mod api; -/// A field element in the BLS12-381 construction. Thin wrapper around `conversions::BlsFr`. -#[derive(Debug, From, Copy, Clone)] -pub struct FieldElement(pub(crate) BlsFr); - -/// A Groth16 proof in the BLS12-381 construction. Thin wrapper around `ark_groth16::Proof::`. -#[derive(Debug, From)] -pub struct Proof(pub(crate) ark_groth16::Proof); - -/// A Groth16 verifying key in the BLS12-381 construction. Thin wrapper around `ark_groth16::VerifyingKey::`. -#[derive(Debug, From)] -pub struct VerifyingKey(pub(crate) ark_groth16::VerifyingKey); - -impl Proof { - /// Deserialize a serialized Groth16 proof using arkworks' canonical serialisation format: https://docs.rs/ark-serialize/latest/ark_serialize/. - pub fn deserialize(proof_points_as_bytes: &[u8]) -> FastCryptoResult { - ark_groth16::Proof::::deserialize_compressed(proof_points_as_bytes) - .map_err(|_| FastCryptoError::InvalidInput) - .map(Proof) - } -} - -impl FieldElement { - /// Deserialize 32 bytes into a BLS12-381 field element using little-endian format. - pub(crate) fn deserialize(bytes: &[u8]) -> FastCryptoResult { - if bytes.len() != SCALAR_SIZE { - return Err(FastCryptoError::InputLengthWrong(bytes.len())); - } - BlsFr::deserialize_compressed(bytes) - .map(FieldElement) - .map_err(|_| FastCryptoError::InvalidInput) - } +#[cfg(test)] +#[path = "unit_tests/verifier_tests.rs"] +mod verifier_tests; - /// Deserialize a vector of bytes into a vector of BLS12-381 field elements, assuming that each element - /// is serialized as a chunk of 32 bytes. See also [`FieldElement::deserialize`]. - pub(crate) fn deserialize_vector(bytes: &[u8]) -> FastCryptoResult> { - if bytes.len() % SCALAR_SIZE != 0 { - return Err(FastCryptoError::InputLengthWrong(bytes.len())); - } - let mut field_elements = Vec::new(); - for chunk in bytes.chunks(SCALAR_SIZE) { - field_elements.push(Self::deserialize(chunk)?); - } - Ok(field_elements) - } -} +pub type PreparedVerifyingKey = groth16::PreparedVerifyingKey; +pub type VerifyingKey = groth16::VerifyingKey; +pub type Proof = groth16::Proof; diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs b/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs index 8d4885676f..90e0205797 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs +++ b/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs @@ -15,9 +15,7 @@ use fastcrypto::groups::GroupElement; use fastcrypto::serde_helpers::ToFromByteArray; use crate::bls12381::api::{prepare_pvk_bytes, verify_groth16_in_bytes}; -use crate::bls12381::verifier::PreparedVerifyingKey; -use crate::bls12381::FieldElement; -use crate::dummy_circuits::{DummyCircuit, Fibonacci}; +use crate::dummy_circuits::DummyCircuit; #[test] fn test_verify_groth16_in_bytes_api() { @@ -139,65 +137,6 @@ fn test_prepare_pvk_bytes() { assert!(prepare_pvk_bytes(&modified_bytes).is_err()); } -#[test] -fn test_verify_groth16_in_bytes_multiple_inputs() { - 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 proof = { - let circuit = Fibonacci::::new(42, a, b); - Groth16::::create_random_proof_with_reduction(circuit, ¶ms, &mut rng) - .unwrap() - }; - - // Proof::write serializes uncompressed and also adds a length to each element, so we serialize - // each individual element here to avoid that. - let mut proof_bytes = Vec::new(); - proof.a.serialize_compressed(&mut proof_bytes).unwrap(); - proof.b.serialize_compressed(&mut proof_bytes).unwrap(); - proof.c.serialize_compressed(&mut proof_bytes).unwrap(); - - let pvk = PreparedVerifyingKey::from(¶ms.vk.into()); - - let inputs: Vec<_> = [FieldElement(a), FieldElement(b)].to_vec(); - assert!(pvk.verify(&inputs, &proof.into()).unwrap()); - - let pvk = pvk.serialize().unwrap(); - - // This circuit has two public inputs: - let mut inputs_bytes = Vec::new(); - a.serialize_compressed(&mut inputs_bytes).unwrap(); - b.serialize_compressed(&mut inputs_bytes).unwrap(); - - assert!(verify_groth16_in_bytes( - &pvk[0], - &pvk[1], - &pvk[2], - &pvk[3], - &inputs_bytes, - &proof_bytes - ) - .unwrap()); - - inputs_bytes[0] += 1; - assert!(!verify_groth16_in_bytes( - &pvk[0], - &pvk[1], - &pvk[2], - &pvk[3], - &inputs_bytes, - &proof_bytes - ) - .unwrap()); -} - #[test] fn api_regression_tests() { // Prepare VK diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs b/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs index 1f8d202c3f..44237c3b75 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs +++ b/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs @@ -1,178 +1,17 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 -use crate::bls12381::verifier::{BlsFr, PreparedVerifyingKey as CustomPVK}; -use ark_bls12_381::{Bls12_381, Fq12, Fr, G1Projective}; -use ark_ec::bls12::G1Prepared; -use ark_ec::pairing::Pairing as _; -use ark_ec::CurveGroup; -use ark_ff::{One, UniformRand}; -use ark_groth16::{Groth16, PreparedVerifyingKey}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use std::ops::Mul; + +use ark_bls12_381::{Bls12_381, Fr}; +use ark_ff::UniformRand; +use ark_groth16::{Groth16, Proof as ArkworksProof}; +use ark_serialize::CanonicalSerialize; use ark_snark::SNARK; use ark_std::rand::thread_rng; -use blst::{ - blst_final_exp, blst_fp12, blst_fp12_mul, blst_fr, blst_miller_loop, blst_p1, blst_p1_affine, - blst_p1_affine_is_inf, blst_p1_to_affine, blst_p2_affine_is_inf, Pairing, -}; -use proptest::{collection, prelude::*}; -use std::{ - iter, - ops::{AddAssign, Mul, Neg}, -}; - -use crate::{ - bls12381::conversions::{ - bls_fq12_to_blst_fp12, bls_fr_to_blst_fr, bls_g1_affine_to_blst_g1_affine, - bls_g2_affine_to_blst_g2_affine, blst_fp12_to_bls_fq12, - tests::{arb_bls_fr, arb_bls_g1_affine, arb_blst_g1_affine, arb_blst_g2_affine}, - }, - bls12381::verifier::{g1_linear_combination, multipairing_with_processed_vk, BLST_FR_ONE}, - dummy_circuits::DummyCircuit, -}; - -#[test] -fn fr_one_test() { - let bls_one = Fr::one(); - let blst_one = bls_fr_to_blst_fr(&bls_one); - assert_eq!(blst_one, BLST_FR_ONE); -} - -// This emulates the process_vk function of the arkworks verifier, but using blst to compute the term -// alpha_g1_beta_g2. See [`test_prepare_vk`]. -fn ark_process_vk(vk: &ark_groth16::VerifyingKey) -> PreparedVerifyingKey { - let g1_alpha = bls_g1_affine_to_blst_g1_affine(&vk.alpha_g1); - let g2_beta = bls_g2_affine_to_blst_g2_affine(&vk.beta_g2); - let blst_alpha_g1_beta_g2 = { - let mut tmp = blst_fp12::default(); - unsafe { blst_miller_loop(&mut tmp, &g2_beta, &g1_alpha) }; - - let mut out = blst_fp12::default(); - unsafe { blst_final_exp(&mut out, &tmp) }; - out - }; - let alpha_g1_beta_g2 = blst_fp12_to_bls_fq12(&blst_alpha_g1_beta_g2); - PreparedVerifyingKey { - vk: vk.clone(), - alpha_g1_beta_g2, - gamma_g2_neg_pc: vk.gamma_g2.neg().into(), - delta_g2_neg_pc: vk.delta_g2.neg().into(), - } -} - -// This computes the result of the multi-pairing involved in the Groth16 verification, using arkworks. -// See [`test_multipairing_with_processed_vk`] -fn ark_multipairing_with_prepared_vk( - pvk: &PreparedVerifyingKey, - proof: &ark_groth16::Proof, - public_inputs: &[Fr], -) -> Fq12 { - let mut g_ic = G1Projective::from(pvk.vk.gamma_abc_g1[0]); - for (i, b) in public_inputs.iter().zip(pvk.vk.gamma_abc_g1.iter().skip(1)) { - g_ic.add_assign(&b.mul(i)); - } - - let qap = Bls12_381::multi_miller_loop( - [ - G1Prepared::from(proof.a), - G1Prepared::from(g_ic), - G1Prepared::from(proof.c), - ], - [ - proof.b.into(), - pvk.gamma_g2_neg_pc.clone(), - pvk.delta_g2_neg_pc.clone(), - ], - ); - - Bls12_381::final_exponentiation(qap).unwrap().0 -} - -const LEN: usize = 10; - -proptest! { - // This technical test is necessary because blst does not expose a generic multi-miller - // loop operation, and forces us to abuse the signature-oriented pairing engine - // that it does expose. Here we show the use of the pairing engine is equivalent to iterated - // use of one-off pairings. - // see https://github.com/supranational/blst/issues/136 - #[test] - fn test_blst_miller_loops( - a_s in collection::vec(arb_blst_g1_affine().prop_filter("values must be non-infinity", |v| unsafe{!blst_p1_affine_is_inf(v)}), LEN..=LEN), - b_s in collection::vec(arb_blst_g2_affine().prop_filter("values must be non-infinity", |v| unsafe{!blst_p2_affine_is_inf(v)}), LEN..=LEN) - ) { - let pairing_engine_result = { - let dst = [0u8; 3]; - let mut pairing_blst = Pairing::new(false, &dst); - for (b, a) in b_s.iter().zip(a_s.iter()) { - pairing_blst.raw_aggregate(b, a); - } - pairing_blst.as_fp12() // this implies pairing_blst.commit() - }; - - let mut res = blst_fp12::default(); - let mut loop0 = blst_fp12::default(); - - for i in 0..LEN { - unsafe { - blst_miller_loop(&mut loop0, b_s[i..].as_ptr(), a_s[i..].as_ptr()); - blst_fp12_mul(&mut res, &res, &loop0); - loop0 = blst_fp12::default(); - } - } - - prop_assert_eq!(res, pairing_engine_result); - } - -} - -#[test] -fn test_prepare_vk() { - const PUBLIC_SIZE: usize = 128; - let rng = &mut thread_rng(); - let c = DummyCircuit:: { - a: Some(::rand(rng)), - b: Some(::rand(rng)), - num_variables: PUBLIC_SIZE, - num_constraints: 65536, - }; - - let (_pk, vk) = Groth16::::circuit_specific_setup(c, rng).unwrap(); - - let ark_pvk = Groth16::::process_vk(&vk).unwrap(); - let blst_pvk = ark_process_vk(&vk); - assert_eq!(ark_pvk.alpha_g1_beta_g2, blst_pvk.alpha_g1_beta_g2); -} - -proptest! { - #[test] - fn test_g1_linear_combination( - frs in collection::vec(arb_bls_fr(), LEN-1..=LEN-1), - a_s in collection::vec(arb_bls_g1_affine(), LEN..=LEN), - ) { - - let pts: Vec = a_s - .iter() - .map(bls_g1_affine_to_blst_g1_affine) - .collect(); - let one = BLST_FR_ONE; - let ss: Vec = iter::once(one) - .chain(frs.iter().map(bls_fr_to_blst_fr)) - .collect(); - let mut blst_res = blst_p1::default(); - g1_linear_combination(&mut blst_res, &pts, &ss[..], ss.len()); - let mut blst_res_affine = blst_p1_affine::default(); - unsafe { blst_p1_to_affine(&mut blst_res_affine, &blst_res) }; - let mut g_ic = G1Projective::from(a_s[0]); - for (i, b) in frs.iter().zip(a_s.iter().skip(1)) { - g_ic.add_assign(&b.mul(i)); - } +use fastcrypto::groups::bls12381::{G1Element, Scalar, SCALAR_LENGTH}; +use fastcrypto::serde_helpers::ToFromByteArray; - // TODO: convert this so we can make a projective comparison - prop_assert_eq!(blst_res_affine, bls_g1_affine_to_blst_g1_affine(&g_ic.into_affine())); - - } -} +use crate::dummy_circuits::DummyCircuit; +use crate::groth16::{Proof, VerifyingKey}; #[test] fn test_verify_with_processed_vk() { @@ -186,60 +25,28 @@ fn test_verify_with_processed_vk() { }; let (pk, vk) = Groth16::::circuit_specific_setup(c, rng).unwrap(); - let proof = Groth16::::prove(&pk, c, rng).unwrap().into(); - let v = c.a.unwrap().mul(c.b.unwrap()); + let ark_proof: ArkworksProof = + Groth16::::prove(&pk, c, rng).unwrap().into(); + let public_input = c.a.unwrap().mul(c.b.unwrap()); - let blst_pvk = CustomPVK::from(&vk.into()); - assert!(blst_pvk.verify(&[v.into()], &proof).unwrap()); + let mut proof_bytes = Vec::new(); + ark_proof.serialize_compressed(&mut proof_bytes).unwrap(); + let proof: Proof = bincode::deserialize(&proof_bytes).unwrap(); - // Roundtrip serde of the proof public input bytes. - let mut public_inputs_bytes = Vec::new(); - v.serialize_compressed(&mut public_inputs_bytes).unwrap(); + let mut vk_bytes = Vec::new(); + vk.serialize_compressed(&mut vk_bytes).unwrap(); + let vk = VerifyingKey::from_arkworks_format(&vk_bytes).unwrap(); - let deserialized_public_inputs = BlsFr::deserialize_compressed(public_inputs_bytes.as_slice()) - .unwrap() - .into(); - - // Roundtrip serde of the proof points bytes. - let mut proof_points_bytes = Vec::new(); - proof - .0 - .serialize_compressed(&mut proof_points_bytes) - .unwrap(); - let deserialized_proof_points = - ark_groth16::Proof::::deserialize_compressed(proof_points_bytes.as_slice()) - .unwrap() - .into(); - - // Roundtrip serde of the prepared verifying key. - let serialized = blst_pvk.serialize().unwrap(); - let serialized_pvk = CustomPVK::deserialize(&serialized).unwrap(); - - assert!(serialized_pvk - .verify(&[deserialized_public_inputs], &deserialized_proof_points) - .unwrap()); + let prepared_vk = crate::groth16::PreparedVerifyingKey::from(&vk); + let public_inputs = vec![scalar_from_arkworks(&public_input)]; + prepared_vk.verify(&public_inputs, &proof).unwrap() } -#[test] -fn test_multipairing_with_processed_vk() { - const PUBLIC_SIZE: usize = 128; - let rng = &mut thread_rng(); - let c = DummyCircuit:: { - a: Some(::rand(rng)), - b: Some(::rand(rng)), - num_variables: PUBLIC_SIZE, - num_constraints: 65536, - }; - - let (pk, vk) = Groth16::::circuit_specific_setup(c, rng).unwrap(); - let proof = Groth16::::prove(&pk, c, rng).unwrap(); - let v = c.a.unwrap().mul(c.b.unwrap()); - - let ark_pvk = Groth16::::process_vk(&vk).unwrap(); - let blst_pvk = CustomPVK::from(&vk.into()); - - let ark_fe = ark_multipairing_with_prepared_vk(&ark_pvk, &proof, &[v]); - let blst_fe = multipairing_with_processed_vk(&blst_pvk, &[v], &proof); - - assert_eq!(bls_fq12_to_blst_fp12(&ark_fe), blst_fe); +fn scalar_from_arkworks(scalar: &Fr) -> Scalar { + let mut scalar_bytes = [0u8; SCALAR_LENGTH]; + scalar + .serialize_compressed(scalar_bytes.as_mut_slice()) + .unwrap(); + scalar_bytes.reverse(); + Scalar::from_byte_array(&scalar_bytes).unwrap() } diff --git a/fastcrypto-zkp/src/bls12381/verifier.rs b/fastcrypto-zkp/src/bls12381/verifier.rs deleted file mode 100644 index 1f0f84eea9..0000000000 --- a/fastcrypto-zkp/src/bls12381/verifier.rs +++ /dev/null @@ -1,370 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 -use std::borrow::Borrow; -use std::{iter, ops::Neg}; - -use ark_bls12_381::{Bls12_381, Fq12, Fr as BlsFr, G1Affine, G2Affine}; - -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use blst::{ - blst_final_exp, blst_fp, blst_fp12, blst_fr, blst_miller_loop, blst_p1, blst_p1_add_or_double, - blst_p1_affine, blst_p1_from_affine, blst_p1_mult, blst_p1_to_affine, blst_scalar, - blst_scalar_from_fr, Pairing, -}; -use fastcrypto::error::FastCryptoResult; -use fastcrypto::{error::FastCryptoError, utils::log2_byte}; - -use crate::bls12381::conversions::{ - bls_fq12_to_blst_fp12, bls_fr_to_blst_fr, bls_g1_affine_to_blst_g1_affine, - bls_g2_affine_to_blst_g2_affine, blst_fp12_to_bls_fq12, G1_COMPRESSED_SIZE, -}; -use crate::bls12381::{FieldElement, Proof, VerifyingKey}; - -#[cfg(test)] -#[path = "unit_tests/verifier_tests.rs"] -mod verifier_tests; - -/// This is a helper function to store a pre-processed version of the verifying key. -/// This is roughly homologous to [`ark_groth16::data_structures::PreparedVerifyingKey`]. -/// Note that contrary to Arkworks, we don't store a "prepared" version of the gamma_g2_neg_pc, -/// delta_g2_neg_pc fields, because we can't use them with blst's pairing engine and also because -/// they are very large and unpractical to use in the binary api. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PreparedVerifyingKey { - /// The element vk.gamma_abc_g1, - /// aka the `[gamma^{-1} * (beta * a_i + alpha * b_i + c_i) * G]`, where i spans the public inputs - pub vk_gamma_abc_g1: Vec, - /// The element `e(alpha * G, beta * H)` in `E::GT`. - pub alpha_g1_beta_g2: blst_fp12, - /// The element `- gamma * H` in `E::G2`, for use in pairings. - pub gamma_g2_neg_pc: G2Affine, - /// The element `- delta * H` in `E::G2`, for use in pairings. - pub delta_g2_neg_pc: G2Affine, -} - -impl PreparedVerifyingKey { - /// Returns the validity of the Groth16 proof passed as argument. The format of the inputs is assumed to be in arkworks format. - /// See [`multipairing_with_processed_vk`] for the actual pairing computation details. - /// - /// ## Example - /// ``` - /// use fastcrypto_zkp::dummy_circuits::Fibonacci; - /// use ark_bls12_381::{Bls12_381, Fr}; - /// use ark_ff::One; - /// use ark_groth16::Groth16; - /// use ark_std::rand::thread_rng; - /// use blake2::digest::Mac; - /// use fastcrypto_zkp::bls12381::FieldElement; - /// use fastcrypto_zkp::bls12381::verifier::PreparedVerifyingKey; - /// - /// let mut rng = thread_rng(); - /// - /// let params = { - /// let circuit = Fibonacci::::new(42, Fr::one(), Fr::one()); // 42 constraints, initial a = b = 1 - /// Groth16::::generate_random_parameters_with_reduction(circuit, &mut rng).unwrap() - /// }; - /// - /// let proof = { - /// let circuit = Fibonacci::::new(42, Fr::one(), Fr::one()); // 42 constraints, initial a = b = 1 - /// // Create a proof with our parameters, picking a random witness assignment - /// Groth16::::create_random_proof_with_reduction(circuit, ¶ms, &mut rng).unwrap() - /// }; - /// - /// // Prepare the verification key (for proof verification). Ideally, we would like to do this only - /// // once per circuit. - /// let pvk = PreparedVerifyingKey::from(¶ms.vk.into()); - /// - /// // We provide the public inputs which we know are used in our circuits - /// // this must be the same as the inputs used in the proof right above. - /// let inputs: Vec = [Fr::one().into(); 2].to_vec(); - /// - /// // Verify the proof - /// let r = pvk.verify(&inputs, &proof.into()).unwrap(); - /// ``` - pub fn verify(&self, x: &[FieldElement], proof: &Proof) -> Result { - // Note the "+1" : this API implies the first scalar coefficient is 1 and not sent - if (x.len() + 1) != self.vk_gamma_abc_g1.len() { - return Err(FastCryptoError::InvalidInput); - } - let x: Vec = x.iter().map(|x| x.0).collect(); - - let res = multipairing_with_processed_vk(self, &x, &proof.0); - Ok(res == self.alpha_g1_beta_g2) - } - - /// Deserialize the prepared verifying key from the serialized fields of vk_gamma_abc_g1, alpha_g1_beta_g2, gamma_g2_neg_pc, delta_g2_neg_pc - pub fn deserialize>(bytes: &Vec) -> Result { - if bytes.len() != 4 { - return Err(FastCryptoError::InputLengthWrong(bytes.len())); - } - - let vk_gamma_abc_g1_bytes = bytes[0].borrow(); - if vk_gamma_abc_g1_bytes.len() % G1_COMPRESSED_SIZE != 0 { - return Err(FastCryptoError::InvalidInput); - } - let mut vk_gamma_abc_g1: Vec = Vec::new(); - for g1_bytes in vk_gamma_abc_g1_bytes.chunks(G1_COMPRESSED_SIZE) { - let g1 = G1Affine::deserialize_compressed(g1_bytes) - .map_err(|_| FastCryptoError::InvalidInput)?; - vk_gamma_abc_g1.push(g1); - } - - let alpha_g1_beta_g2 = bls_fq12_to_blst_fp12( - &Fq12::deserialize_compressed(bytes[1].borrow()) - .map_err(|_| FastCryptoError::InvalidInput)?, - ); - - let gamma_g2_neg_pc = G2Affine::deserialize_compressed(bytes[2].borrow()) - .map_err(|_| FastCryptoError::InvalidInput)?; - - let delta_g2_neg_pc = G2Affine::deserialize_compressed(bytes[3].borrow()) - .map_err(|_| FastCryptoError::InvalidInput)?; - - Ok(PreparedVerifyingKey { - vk_gamma_abc_g1, - alpha_g1_beta_g2, - gamma_g2_neg_pc, - delta_g2_neg_pc, - }) - } - - /// Serialize the prepared verifying key to its vectors form. - pub fn serialize(&self) -> Result>, FastCryptoError> { - let mut res = Vec::new(); - - let mut vk_gamma = Vec::new(); - for g1 in &self.vk_gamma_abc_g1 { - let mut g1_bytes = Vec::new(); - g1.serialize_compressed(&mut g1_bytes) - .map_err(|_| FastCryptoError::InvalidInput)?; - vk_gamma.append(&mut g1_bytes); - } - res.push(vk_gamma); - - let mut fq12 = Vec::new(); - blst_fp12_to_bls_fq12(&self.alpha_g1_beta_g2) - .serialize_compressed(&mut fq12) - .map_err(|_| FastCryptoError::InvalidInput)?; - res.push(fq12); - - let mut gamma_bytes = Vec::new(); - self.gamma_g2_neg_pc - .serialize_compressed(&mut gamma_bytes) - .map_err(|_| FastCryptoError::InvalidInput)?; - res.push(gamma_bytes); - - let mut delta_bytes = Vec::new(); - self.delta_g2_neg_pc - .serialize_compressed(&mut delta_bytes) - .map_err(|_| FastCryptoError::InvalidInput)?; - res.push(delta_bytes); - Ok(res) - } -} - -impl From<&VerifyingKey> for PreparedVerifyingKey { - /// Takes an input [`ark_groth16::VerifyingKey`] `vk` and returns a `PreparedVerifyingKey`. This is roughly homologous to - /// [`ark_groth16::PreparedVerifyingKey::process_vk`], but uses a blst representation of the elements. - /// - /// ## Example: - /// ``` - /// use fastcrypto_zkp::{dummy_circuits::Fibonacci}; - /// use ark_bls12_381::{Bls12_381, Fr}; - /// use ark_ff::One; - /// use ark_groth16::Groth16; - /// use ark_std::rand::thread_rng; - /// use fastcrypto_zkp::bls12381::verifier::PreparedVerifyingKey; - /// - /// let mut rng = thread_rng(); - /// let params = { - /// let c = Fibonacci::::new(42, Fr::one(), Fr::one()); // 42 constraints, initial a = b = 1 (standard Fibonacci) - /// Groth16::::generate_random_parameters_with_reduction(c, &mut rng).unwrap() - /// }; - /// - /// // Prepare the verification key (for proof verification). Ideally, we would like to do this only - /// // once per circuit. - /// let pvk = PreparedVerifyingKey::from(¶ms.vk.into()); - /// ``` - fn from(vk: &VerifyingKey) -> Self { - let g1_alpha = bls_g1_affine_to_blst_g1_affine(&vk.0.alpha_g1); - let g2_beta = bls_g2_affine_to_blst_g2_affine(&vk.0.beta_g2); - let blst_alpha_g1_beta_g2 = { - let mut tmp = blst_fp12::default(); - unsafe { blst_miller_loop(&mut tmp, &g2_beta, &g1_alpha) }; - - let mut out = blst_fp12::default(); - unsafe { blst_final_exp(&mut out, &tmp) }; - out - }; - PreparedVerifyingKey { - vk_gamma_abc_g1: vk.0.gamma_abc_g1.clone(), - alpha_g1_beta_g2: blst_alpha_g1_beta_g2, - gamma_g2_neg_pc: vk.0.gamma_g2.neg(), - delta_g2_neg_pc: vk.0.delta_g2.neg(), - } - } -} - -impl VerifyingKey { - /// Deserialize a serialized Groth16 verifying key in compressed format using arkworks' canonical serialisation format: https://docs.rs/ark-serialize/latest/ark_serialize/. - pub fn deserialize(bytes: &[u8]) -> FastCryptoResult { - ark_groth16::VerifyingKey::::deserialize_compressed(bytes) - .map(VerifyingKey) - .map_err(|_| FastCryptoError::InvalidInput) - } -} - -/// This helper constant makes it easier to use compute the linear combination involved in the pairing inputs. -const G1_IDENTITY: blst_p1 = blst_p1 { - x: blst_fp { l: [0; 6] }, - y: blst_fp { l: [0; 6] }, - z: blst_fp { l: [0; 6] }, -}; - -/// Returns a single scalar multiplication of `pt` by `b`. -fn mul(pt: &blst_p1, b: &blst_fr) -> blst_p1 { - let mut scalar: blst_scalar = blst_scalar::default(); - unsafe { - blst_scalar_from_fr(&mut scalar, b); - } - - // Count the number of bytes to be multiplied. - let mut i = scalar.b.len(); - while i != 0 && scalar.b[i - 1] == 0 { - i -= 1; - } - - let mut result = blst_p1::default(); - if i == 0 { - return G1_IDENTITY; - } else if i == 1 && scalar.b[0] == 1 { - return *pt; - } else { - // Count the number of bits to be multiplied. - unsafe { - blst_p1_mult( - &mut result, - pt, - &(scalar.b[0]), - 8 * i - 7 + log2_byte(scalar.b[i - 1]), - ); - } - } - result -} - -/// Facade for the blst_p1_add_or_double function. -fn add_or_dbl(a: &blst_p1, b: &blst_p1) -> blst_p1 { - let mut ret = blst_p1::default(); - unsafe { - blst_p1_add_or_double(&mut ret, a, b); - } - ret -} - -/// Computes the [\sum p_i * b_i, p_i in p_affine, b_i in coeffs] in G1. -fn g1_linear_combination( - out: &mut blst_p1, - p_affine: &[blst_p1_affine], - coeffs: &[blst_fr], - len: usize, -) { - // Direct approach - let mut tmp; - *out = G1_IDENTITY; - for i in 0..len { - let mut p = blst_p1::default(); - unsafe { blst_p1_from_affine(&mut p, &p_affine[i]) }; - - tmp = mul(&p, &coeffs[i]); - *out = add_or_dbl(out, &tmp); - } -} - -/// This represents the multiplicative unit scalar, see fr_one_test -pub(crate) const BLST_FR_ONE: blst_fr = blst_fr { - l: [ - 8589934590, - 6378425256633387010, - 11064306276430008309, - 1739710354780652911, - ], -}; - -/// Returns the result of the multi-pairing involved in the verification equation. This will then be compared to the pre-computed term -/// pvk.alpha_g1_beta_g2 to check the validity of the proof. -/// -/// The textbook Groth16 equation is (in additive notation): -/// e(A, B) = e(g * alpha, h * beta) + e(g * f, h * gamma) + e(C, h * delta) -/// where f is the linear combination of the a_i points in the verifying key with the input scalars -/// -/// Due to the pre-processing of e(g * alpha, h * beta), and using the pairing inverse, we instead compute: -/// e(A, B) + e(g * f, h * - gamma) + e(C, h * - delta). -/// -/// Eventually, we will compare this value to e(g * alpha, h * beta) -/// -fn multipairing_with_processed_vk( - pvk: &PreparedVerifyingKey, - x: &[BlsFr], - proof: &ark_groth16::Proof, -) -> blst_fp12 { - // Linear combination: note that the arkworks interface assumes the 1st scalar is an implicit 1 - let pts: Vec = pvk - .vk_gamma_abc_g1 - .iter() - .map(bls_g1_affine_to_blst_g1_affine) - .collect(); - let one = BLST_FR_ONE; - let ss: Vec = iter::once(one) - .chain(x.iter().map(bls_fr_to_blst_fr)) - .collect(); - let mut out = blst_p1::default(); - g1_linear_combination(&mut out, &pts, &ss[..], ss.len()); - - let blst_proof_a = bls_g1_affine_to_blst_g1_affine(&proof.a); - let blst_proof_b = bls_g2_affine_to_blst_g2_affine(&proof.b); - - let mut blst_proof_1_g1 = blst_p1_affine::default(); - unsafe { blst_p1_to_affine(&mut blst_proof_1_g1, &out) }; - let blst_proof_1_g2 = bls_g2_affine_to_blst_g2_affine(&pvk.gamma_g2_neg_pc); - - let blst_proof_2_g1 = bls_g1_affine_to_blst_g1_affine(&proof.c); - let blst_proof_2_g2 = bls_g2_affine_to_blst_g2_affine(&pvk.delta_g2_neg_pc); - - let dst = [0u8; 3]; - let mut pairing_blst = Pairing::new(false, &dst); - pairing_blst.raw_aggregate(&blst_proof_b, &blst_proof_a); - pairing_blst.raw_aggregate(&blst_proof_1_g2, &blst_proof_1_g1); - pairing_blst.raw_aggregate(&blst_proof_2_g2, &blst_proof_2_g1); - pairing_blst.as_fp12().final_exp() -} - -#[cfg(test)] -mod tests { - use crate::bls12381::verifier::PreparedVerifyingKey; - use crate::bls12381::VerifyingKey; - use crate::dummy_circuits::DummyCircuit; - use ark_bls12_381::{Bls12_381, Fr}; - use ark_groth16::Groth16; - use ark_snark::SNARK; - use ark_std::rand::thread_rng; - use ark_std::UniformRand; - - #[test] - fn test_serialization() { - const PUBLIC_SIZE: usize = 128; - let rng = &mut thread_rng(); - let c = DummyCircuit:: { - a: Some(::rand(rng)), - b: Some(::rand(rng)), - num_variables: PUBLIC_SIZE, - num_constraints: 10, - }; - let (_, vk) = Groth16::::circuit_specific_setup(c, rng).unwrap(); - let pvk = PreparedVerifyingKey::from(&VerifyingKey(vk)); - - let serialized = pvk.serialize().unwrap(); - let deserialized = PreparedVerifyingKey::deserialize(&serialized).unwrap(); - assert_eq!(pvk, deserialized); - } -} diff --git a/fastcrypto-zkp/src/groth16/generic_api.rs b/fastcrypto-zkp/src/groth16/generic_api.rs index 8ce3f49acb..fb8524a097 100644 --- a/fastcrypto-zkp/src/groth16/generic_api.rs +++ b/fastcrypto-zkp/src/groth16/generic_api.rs @@ -13,7 +13,7 @@ use crate::groth16::{PreparedVerifyingKey, VerifyingKey}; /// Deserialize bytes as an Arkworks representation of a verifying key, and return a vector of the /// four components of a prepared verified key (see more at [`PreparedVerifyingKey`]). -pub fn prepare_pvk_bytes< +pub(crate) fn prepare_pvk_bytes< G1, const G1_SIZE: usize, const G2_SIZE: usize, @@ -21,7 +21,7 @@ pub fn prepare_pvk_bytes< const FR_SIZE: usize, >( vk_bytes: &[u8], -) -> Result>, FastCryptoError> +) -> FastCryptoResult>> where G1: Pairing + MultiScalarMul + for<'a> Deserialize<'a> + ToFromByteArray, ::Other: for<'a> Deserialize<'a> + ToFromByteArray, @@ -35,7 +35,7 @@ where /// (see more at [`PreparedVerifyingKey`]), serialized proof public input, which should /// be concatenated serialized field elements of the scalar field of [`crate::conversions::SCALAR_SIZE`] /// bytes each, and serialized proof points. -pub fn verify_groth16_in_bytes< +pub(crate) fn verify_groth16_in_bytes< G1, const G1_SIZE: usize, const G2_SIZE: usize, @@ -58,20 +58,18 @@ where let x = deserialize_vector::(proof_public_inputs_as_bytes)?; let proof = bincode::deserialize(proof_points_as_bytes).map_err(|_| FastCryptoError::InvalidInput)?; - - let prepared_vk = PreparedVerifyingKey::::deserialize_from_parts(&vec![ + let prepared_vk = PreparedVerifyingKey::::deserialize_from_parts( vk_gamma_abc_g1_bytes, alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - ])?; - - Ok(prepared_vk.verify(x.as_slice(), &proof).is_ok()) + )?; + Ok(prepared_vk.verify(&x, &proof).is_ok()) } impl VerifyingKey { pub fn from_arkworks_format( - vk_bytes: &[u8], + bytes: &[u8], ) -> FastCryptoResult where G1: ToFromByteArray, @@ -85,51 +83,51 @@ impl VerifyingKey { // - n: u64 lendian (size of gamma_abc) // - gamma_abc: Vec - // We need to implement this instead of using bincode because the length of the vector is - // not in the default bincode format where only one byte is used to represent the length. + // We can't use bincode because there, the length of the vector is prefixed as a single byte + // and not a lendian u64. - if (vk_bytes.len() - (G1_SIZE + 3 * G2_SIZE + 8)) % G1_SIZE != 0 { + if (bytes.len() - (G1_SIZE + 3 * G2_SIZE + 8)) % G1_SIZE != 0 { return Err(FastCryptoError::InvalidInput); } let mut i = 0; let alpha = G1::from_byte_array( - vk_bytes[i..G1_SIZE] + bytes[i..G1_SIZE] .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, )?; i += G1_SIZE; let beta = G1::Other::from_byte_array( - vk_bytes[i..i + G2_SIZE] + bytes[i..i + G2_SIZE] .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, )?; i += G2_SIZE; let gamma = G1::Other::from_byte_array( - vk_bytes[i..i + G2_SIZE] + bytes[i..i + G2_SIZE] .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, )?; i += G2_SIZE; let delta = G1::Other::from_byte_array( - vk_bytes[i..i + G2_SIZE] + bytes[i..i + G2_SIZE] .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, )?; i += G2_SIZE; let n = u64::from_le_bytes( - vk_bytes[i..i + 8] + bytes[i..i + 8] .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, ); i += 8; - let gamma_abc = deserialize_vector::(&vk_bytes[i..]) + let gamma_abc = deserialize_vector::(&bytes[i..]) .map_err(|_| FastCryptoError::InvalidInput)?; if gamma_abc.len() != n as usize { @@ -158,12 +156,12 @@ where G1::Other: ToFromByteArray, ::Output: ToFromByteArray, { - let mut result = Vec::with_capacity(4); - result.push(serialize_vector(&self.vk_gamma_abc)); - result.push(self.alpha_beta.to_byte_array().to_vec()); - result.push(self.gamma_neg.to_byte_array().to_vec()); - result.push(self.delta_neg.to_byte_array().to_vec()); - result + vec![ + serialize_vector(&self.vk_gamma_abc), + self.alpha_beta.to_byte_array().to_vec(), + self.gamma_neg.to_byte_array().to_vec(), + self.delta_neg.to_byte_array().to_vec(), + ] } pub fn deserialize_from_parts< @@ -171,33 +169,35 @@ where const G2_SIZE: usize, const GT_SIZE: usize, >( - parts: &Vec<&[u8]>, + vk_gamma_abc_bytes: &[u8], + alpha_beta_bytes: &[u8], + gamma_neg_bytes: &[u8], + delta_neg_bytes: &[u8], ) -> FastCryptoResult where G1: ToFromByteArray, G1::Other: ToFromByteArray, ::Output: ToFromByteArray, { - if parts.len() != 4 { + if vk_gamma_abc_bytes.len() % G1_SIZE != 0 { return Err(FastCryptoError::InvalidInput); } + let vk_gamma_abc = deserialize_vector::(vk_gamma_abc_bytes)?; - if parts[0].len() % G1_SIZE != 0 { - return Err(FastCryptoError::InvalidInput); - } - let vk_gamma_abc = deserialize_vector::(parts[0])?; let alpha_beta = ::Output::from_byte_array( - parts[1] + alpha_beta_bytes .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, )?; + let gamma_neg = ::Other::from_byte_array( - parts[2] + gamma_neg_bytes .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, )?; + let delta_neg = ::Other::from_byte_array( - parts[3] + delta_neg_bytes .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, )?; @@ -210,176 +210,3 @@ where }) } } - -#[cfg(test)] -mod tests { - use std::ops::Mul; - - use ark_bls12_381::{Bls12_381, Fr, G1Affine}; - use ark_groth16::Groth16; - use ark_serialize::CanonicalSerialize; - use ark_snark::SNARK; - use ark_std::rand::thread_rng; - use ark_std::UniformRand; - - use fastcrypto::groups::bls12381::{ - G1Element, Scalar, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, GT_ELEMENT_BYTE_LENGTH, - SCALAR_LENGTH, - }; - use fastcrypto::groups::serialize_vector; - use fastcrypto::serde_helpers::ToFromByteArray; - - use crate::dummy_circuits::DummyCircuit; - use crate::groth16::generic_api::{prepare_pvk_bytes, verify_groth16_in_bytes}; - use crate::groth16::{PreparedVerifyingKey, Proof, VerifyingKey}; - - fn scalar_from_arkworks(scalar: &Fr) -> Scalar { - let mut scalar_bytes = [0u8; SCALAR_LENGTH]; - scalar - .serialize_compressed(scalar_bytes.as_mut_slice()) - .unwrap(); - scalar_bytes.reverse(); - Scalar::from_byte_array(&scalar_bytes).unwrap() - } - - #[test] - fn test_verify_groth16_in_bytes_api() { - const PUBLIC_SIZE: usize = 128; - let rng = &mut thread_rng(); - let c = DummyCircuit:: { - a: Some(::rand(rng)), - b: Some(::rand(rng)), - num_variables: PUBLIC_SIZE, - num_constraints: 10, - }; - - let (pk, vk) = Groth16::::circuit_specific_setup(c, rng).unwrap(); - let ark_proof = Groth16::::prove(&pk, c, rng).unwrap(); - let result = c.a.unwrap().mul(c.b.unwrap()); - let public_inputs = vec![scalar_from_arkworks(&result)]; - - let mut vk_bytes = vec![]; - vk.serialize_compressed(&mut vk_bytes).unwrap(); - - let bytes = prepare_pvk_bytes::< - G1Element, - G1_ELEMENT_BYTE_LENGTH, - G2_ELEMENT_BYTE_LENGTH, - GT_ELEMENT_BYTE_LENGTH, - SCALAR_LENGTH, - >(vk_bytes.as_slice()) - .unwrap(); - - let vk_gamma_abc_g1_bytes = &bytes[0]; - let alpha_g1_beta_g2_bytes = &bytes[1]; - let gamma_g2_neg_pc_bytes = &bytes[2]; - let delta_g2_neg_pc_bytes = &bytes[3]; - - let public_inputs_bytes = serialize_vector(&public_inputs); - - let mut proof_bytes = Vec::new(); - ark_proof.serialize_compressed(&mut proof_bytes).unwrap(); - let proof: Proof = bincode::deserialize(&proof_bytes).unwrap(); - let vk = VerifyingKey::from_arkworks_format(&vk_bytes).unwrap(); - - let prepared_vk = PreparedVerifyingKey::from(&vk); - - // Check that the proof verifies without using the API - assert!(prepared_vk.verify(&public_inputs, &proof).is_ok()); - - // Success case. - assert!(verify_groth16_in_bytes::< - G1Element, - { G1_ELEMENT_BYTE_LENGTH }, - { G2_ELEMENT_BYTE_LENGTH }, - { GT_ELEMENT_BYTE_LENGTH }, - { SCALAR_LENGTH }, - >( - vk_gamma_abc_g1_bytes, - alpha_g1_beta_g2_bytes, - gamma_g2_neg_pc_bytes, - delta_g2_neg_pc_bytes, - &public_inputs_bytes, - &proof_bytes - ) - .unwrap()); - - // Negative test: Replace the A element with a random point. - let mut modified_proof_points_bytes = proof_bytes.clone(); - let _ = &G1Affine::rand(rng) - .serialize_compressed(&mut modified_proof_points_bytes[0..48]) - .unwrap(); - assert!(!verify_groth16_in_bytes::< - G1Element, - { G1_ELEMENT_BYTE_LENGTH }, - { G2_ELEMENT_BYTE_LENGTH }, - { GT_ELEMENT_BYTE_LENGTH }, - { SCALAR_LENGTH }, - >( - vk_gamma_abc_g1_bytes, - alpha_g1_beta_g2_bytes, - gamma_g2_neg_pc_bytes, - delta_g2_neg_pc_bytes, - &public_inputs_bytes, - &modified_proof_points_bytes - ) - .unwrap()); - - // Length of verifying key is incorrect. - let mut modified_bytes = bytes[0].clone(); - modified_bytes.pop(); - assert!(verify_groth16_in_bytes::< - G1Element, - { G1_ELEMENT_BYTE_LENGTH }, - { G2_ELEMENT_BYTE_LENGTH }, - { GT_ELEMENT_BYTE_LENGTH }, - { SCALAR_LENGTH }, - >( - &modified_bytes, - alpha_g1_beta_g2_bytes, - gamma_g2_neg_pc_bytes, - delta_g2_neg_pc_bytes, - &public_inputs_bytes, - &proof_bytes - ) - .is_err()); - - // Length of public inputs is incorrect. - let mut modified_proof_inputs_bytes = public_inputs_bytes.clone(); - modified_proof_inputs_bytes.pop(); - assert!(verify_groth16_in_bytes::< - G1Element, - { G1_ELEMENT_BYTE_LENGTH }, - { G2_ELEMENT_BYTE_LENGTH }, - { GT_ELEMENT_BYTE_LENGTH }, - { SCALAR_LENGTH }, - >( - vk_gamma_abc_g1_bytes, - alpha_g1_beta_g2_bytes, - gamma_g2_neg_pc_bytes, - delta_g2_neg_pc_bytes, - &modified_proof_inputs_bytes, - &proof_bytes - ) - .is_err()); - - // length of proof is incorrect - let mut modified_proof_points_bytes = proof_bytes.to_vec(); - modified_proof_points_bytes.pop(); - assert!(verify_groth16_in_bytes::< - G1Element, - { G1_ELEMENT_BYTE_LENGTH }, - { G2_ELEMENT_BYTE_LENGTH }, - { GT_ELEMENT_BYTE_LENGTH }, - { SCALAR_LENGTH }, - >( - vk_gamma_abc_g1_bytes, - alpha_g1_beta_g2_bytes, - gamma_g2_neg_pc_bytes, - delta_g2_neg_pc_bytes, - &public_inputs_bytes, - &modified_proof_points_bytes - ) - .is_err()); - } -} diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index 304b781443..e021043969 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -3,12 +3,12 @@ use std::fmt::Debug; +use fastcrypto::error::{FastCryptoError, FastCryptoResult}; use serde::Deserialize; -use fastcrypto::groups::{GroupElement, Pairing}; +use fastcrypto::groups::{GroupElement, MultiScalarMul, Pairing}; pub(crate) mod generic_api; -mod prepared_vk; #[derive(Debug, Deserialize)] pub struct Proof @@ -70,3 +70,43 @@ where } } } + +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) + } +} 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 ac7dd9960a..3ec9559890 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 3f0210a7e643a7174304df1a96a929572ba2768b 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/65] Remove example --- fastcrypto-zkp/examples/blake2s_demo.rs | 130 ------------------------ 1 file changed, 130 deletions(-) delete mode 100644 fastcrypto-zkp/examples/blake2s_demo.rs diff --git a/fastcrypto-zkp/examples/blake2s_demo.rs b/fastcrypto-zkp/examples/blake2s_demo.rs deleted file mode 100644 index b6b80311d7..0000000000 --- a/fastcrypto-zkp/examples/blake2s_demo.rs +++ /dev/null @@ -1,130 +0,0 @@ -// // Copyright (c) 2022, Mysten Labs, Inc. -// // SPDX-License-Identifier: Apache-2.0 -// -// use ark_bls12_381::{Bls12_381, Fr}; -// pub use ark_ff::ToConstraintField; -// -// use ark_crypto_primitives::prf::{PRF, PRFGadget}; -// use ark_crypto_primitives::{ -// prf::blake2s::Blake2s as B2SPRF, prf::blake2s::constraints::Blake2sGadget, -// }; -// use ark_groth16::Groth16; -// use ark_relations::r1cs::{ -// ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, SynthesisError, -// }; -// use blake2::{Blake2s256, digest::Digest}; -// -// use ark_r1cs_std::prelude::*; -// use ark_serialize::CanonicalSerialize; -// use ark_std::rand::thread_rng; -// use fastcrypto::groups::bls12381::{Scalar, SCALAR_LENGTH}; -// use fastcrypto_zkp::bls12381::{PreparedVerifyingKey, Proof}; -// -// #[derive(Clone, Copy, Debug)] -// struct Blake2sCircuit { -// input: [u8; 32], -// blake2_seed: [u8; 32], -// pub expected_output: [u8; 32], -// } -// -// impl Blake2sCircuit { -// fn new() -> Self { -// // We're going to prove knowledge of the blake2 hash of this secret -// // see https://en.wikipedia.org/wiki/Cluedo -// let statement = b"butler in the yard with a wrench"; -// let bytes = statement.to_vec(); -// -// // as a sanity-check, we'll also compute the blake2 hash of the secret -// // with the reference implementation -// -// // Blake2s takes a seed parameter. We use a default seed. -// let seed: [u8; 32] = [0u8; 32]; -// -// // Compute the hash by traditional means. -// let mut h = Blake2s256::new_with_prefix(seed); -// h.update(&bytes); -// let hash_result = h.finalize(); -// -// // We use the arkworks API for blake2s as well and check it matches -// let input: [u8; 32] = bytes[..].try_into().unwrap(); -// let out = B2SPRF::evaluate(&seed, &input).unwrap(); -// -// // the traditional means and the arkworks implementation match -// assert_eq!(hash_result.as_slice(), out.as_slice()); -// -// // at this stage, we can publish the seed and the expected hash output, -// // and we'll prove we know the input -// Self { -// input: statement.to_owned(), -// blake2_seed: seed, -// expected_output: out, -// } -// } -// } -// -// impl ConstraintSynthesizer for Blake2sCircuit { -// fn generate_constraints(self, cs: ConstraintSystemRef) -> Result<(), SynthesisError> { -// // initialize the blake2s gadget and allocate the seed -// let seed_var = UInt8::new_input_vec(cs.clone(), &self.blake2_seed).unwrap(); -// -// // declare the witnesses -// let input_var = UInt8::new_witness_vec(cs.clone(), &self.input).unwrap(); -// -// // declare the public intended output -// let desired_out_var = >::OutputVar::new_input(cs, || { -// Ok(self.expected_output) -// }) -// .unwrap(); -// -// // link the intended output to a blake2s computation on the input, i.e. constrain Blake2s(seed, input) == output -// let output_var = Blake2sGadget::evaluate(&seed_var, &input_var).unwrap(); -// output_var.enforce_equal(&desired_out_var).unwrap(); -// -// Ok(()) -// } -// } -// -// fn main() { -// let mut rng = &mut thread_rng(); -// -// let circuit = Blake2sCircuit::new(); -// // Sanity-check -// { -// let cs = ConstraintSystem::::new_ref(); -// -// circuit.generate_constraints(cs.clone()).unwrap(); -// println!("Num constraints: {}", cs.num_constraints()); -// assert!(cs.is_satisfied().unwrap()); -// } -// -// let params = -// Groth16::::generate_random_parameters_with_reduction(circuit, rng).unwrap(); -// -// // prepare a proof (note: there is nothing trustable about the trivial setup involved here) -// let proof = Proof::from( -// Groth16::::create_random_proof_with_reduction(circuit, ¶ms, &mut rng) -// .unwrap(), -// ); -// -// println!( -// "Generated proof of knowledge of Blake2s preimage of {}", -// hex::encode(circuit.expected_output) -// ); -// -// // prepare the verification key -// let pvk = PreparedVerifyingKey::from(¶ms.vk.into()); -// -// // provide the public inputs (the hash target) for verification -// let inputs: Vec = [&scalar_from_arkworks(circuit.blake2_seed[..]), &circuit.expected_output[..]] -// .iter() -// .flat_map::, _>(|x| x.to_field_elements().unwrap()) -// .map(FieldElement::from) -// .collect(); -// -// // Verify the proof -// assert!(pvk.verify(&inputs, &proof).unwrap()); -// println!( -// "Checked proof of knowledge of Blake2s preimage of {}!", -// hex::encode(circuit.expected_output) -// ); -// } From 98c18c1b56778b8e879eb1f04899049ff97741fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 4 Apr 2024 14:34:53 +0200 Subject: [PATCH 07/65] Clean up deps --- Cargo.lock | 2 -- fastcrypto-zkp/Cargo.toml | 2 -- fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs | 2 +- fastcrypto-zkp/src/groth16/generic_api.rs | 2 +- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5ab7933bbd..0264b2d831 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1505,7 +1505,6 @@ dependencies = [ "ark-snark", "ark-std", "bcs", - "bincode", "blake2", "blst", "byte-slice-cast", @@ -1526,7 +1525,6 @@ dependencies = [ "schemars", "serde", "serde_json", - "serde_with", "test-strategy", "tokio", "typenum", diff --git a/fastcrypto-zkp/Cargo.toml b/fastcrypto-zkp/Cargo.toml index 440ab646a6..bdb77a6bc0 100644 --- a/fastcrypto-zkp/Cargo.toml +++ b/fastcrypto-zkp/Cargo.toml @@ -46,8 +46,6 @@ ff = { version = "0.13.0", features = ["derive"] } typenum = "1.13.0" lazy_static = "1.4.0" itertools = "0.12.0" -bincode = "1.3.3" -serde_with = "2.3.1" [dev-dependencies] ark-bls12-377 = "0.4.0" diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs b/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs index 44237c3b75..c81e0c1387 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs +++ b/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs @@ -31,7 +31,7 @@ fn test_verify_with_processed_vk() { let mut proof_bytes = Vec::new(); ark_proof.serialize_compressed(&mut proof_bytes).unwrap(); - let proof: Proof = bincode::deserialize(&proof_bytes).unwrap(); + let proof: Proof = bcs::from_bytes(&proof_bytes).unwrap(); let mut vk_bytes = Vec::new(); vk.serialize_compressed(&mut vk_bytes).unwrap(); diff --git a/fastcrypto-zkp/src/groth16/generic_api.rs b/fastcrypto-zkp/src/groth16/generic_api.rs index fb8524a097..0da01b6e9f 100644 --- a/fastcrypto-zkp/src/groth16/generic_api.rs +++ b/fastcrypto-zkp/src/groth16/generic_api.rs @@ -57,7 +57,7 @@ where { let x = deserialize_vector::(proof_public_inputs_as_bytes)?; let proof = - bincode::deserialize(proof_points_as_bytes).map_err(|_| FastCryptoError::InvalidInput)?; + bcs::from_bytes(proof_points_as_bytes).map_err(|_| FastCryptoError::InvalidInput)?; let prepared_vk = PreparedVerifyingKey::::deserialize_from_parts( vk_gamma_abc_g1_bytes, alpha_g1_beta_g2_bytes, From 6142eaad56cf1a81b12c6bcd48e695c6e6a9addf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 4 Apr 2024 14:36:34 +0200 Subject: [PATCH 08/65] Add docs --- fastcrypto-zkp/src/bls12381/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/fastcrypto-zkp/src/bls12381/mod.rs b/fastcrypto-zkp/src/bls12381/mod.rs index 11be26fd98..2673900eb3 100644 --- a/fastcrypto-zkp/src/bls12381/mod.rs +++ b/fastcrypto-zkp/src/bls12381/mod.rs @@ -15,6 +15,11 @@ pub mod api; #[path = "unit_tests/verifier_tests.rs"] mod verifier_tests; +/// A prepared Groth16 verifying key in the BLS12-381 construction. pub type PreparedVerifyingKey = groth16::PreparedVerifyingKey; + +/// A Groth16 verifying key in the BLS12-381 construction. pub type VerifyingKey = groth16::VerifyingKey; + +/// A Groth16 proof in the BLS12-381 construction. pub type Proof = groth16::Proof; From 00719e973af77839c55056c91c84ff120d5b3280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 4 Apr 2024 14:42:05 +0200 Subject: [PATCH 09/65] Restore test --- .../src/bls12381/unit_tests/api_tests.rs | 70 ++++++++++++++++++- .../src/bls12381/unit_tests/verifier_tests.rs | 2 +- 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs b/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs index 90e0205797..70d5696c7a 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs +++ b/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs @@ -10,12 +10,16 @@ use ark_serialize::CanonicalSerialize; use ark_snark::SNARK; use ark_std::rand::thread_rng; use ark_std::UniformRand; +use fastcrypto::groups::bls12381::G1Element; 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::verifier_tests::scalar_from_arkworks; +use crate::bls12381::{PreparedVerifyingKey, VerifyingKey}; +use crate::dummy_circuits::{DummyCircuit, Fibonacci}; +use crate::groth16::Proof; #[test] fn test_verify_groth16_in_bytes_api() { @@ -221,3 +225,67 @@ fn api_regression_tests() { ) .unwrap()); } + +#[test] +fn test_verify_groth16_in_bytes_multiple_inputs() { + 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 proof = { + let circuit = Fibonacci::::new(42, a, b); + Groth16::::create_random_proof_with_reduction(circuit, ¶ms, &mut rng) + .unwrap() + }; + + // Proof::write serializes uncompressed and also adds a length to each element, so we serialize + // each individual element here to avoid that. + let mut proof_bytes = Vec::new(); + proof.a.serialize_compressed(&mut proof_bytes).unwrap(); + proof.b.serialize_compressed(&mut proof_bytes).unwrap(); + proof.c.serialize_compressed(&mut proof_bytes).unwrap(); + + let mut vk_bytes = vec![]; + params.vk.serialize_compressed(&mut vk_bytes).unwrap(); + let vk = VerifyingKey::from_arkworks_format(&vk_bytes).unwrap(); + let pvk = PreparedVerifyingKey::from(&vk); + + let inputs: Vec<_> = vec![scalar_from_arkworks(&a), scalar_from_arkworks(&b)]; + + let proof: Proof = bcs::from_bytes(&proof_bytes).unwrap(); + assert!(pvk.verify(&inputs, &proof).is_ok()); + + let pvk = pvk.serialize_into_parts(); + + // This circuit has two public inputs: + let mut inputs_bytes = Vec::new(); + a.serialize_compressed(&mut inputs_bytes).unwrap(); + b.serialize_compressed(&mut inputs_bytes).unwrap(); + + assert!(verify_groth16_in_bytes( + &pvk[0], + &pvk[1], + &pvk[2], + &pvk[3], + &inputs_bytes, + &proof_bytes + ) + .unwrap()); + + inputs_bytes[0] += 1; + assert!(!verify_groth16_in_bytes( + &pvk[0], + &pvk[1], + &pvk[2], + &pvk[3], + &inputs_bytes, + &proof_bytes + ) + .unwrap()); +} diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs b/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs index c81e0c1387..ccd2282fa0 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs +++ b/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs @@ -42,7 +42,7 @@ fn test_verify_with_processed_vk() { prepared_vk.verify(&public_inputs, &proof).unwrap() } -fn scalar_from_arkworks(scalar: &Fr) -> Scalar { +pub(crate) fn scalar_from_arkworks(scalar: &Fr) -> Scalar { let mut scalar_bytes = [0u8; SCALAR_LENGTH]; scalar .serialize_compressed(scalar_bytes.as_mut_slice()) From dd29c4cd9c79c98e406767a2ac8b0e2ed512996f 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 10/65] 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 8e7c710b4d..f6c8399062 100644 --- a/fastcrypto/src/groups/bls12381.rs +++ b/fastcrypto/src/groups/bls12381.rs @@ -51,7 +51,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 2240639ffb2c7e25a7c9d5f73a332a855986c21d 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 11/65] Refactor --- fastcrypto-zkp/src/groth16/generic_api.rs | 4 +-- fastcrypto/src/groups/mod.rs | 31 -------------------- fastcrypto/src/serde_helpers.rs | 35 +++++++++++++++++++++-- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/fastcrypto-zkp/src/groth16/generic_api.rs b/fastcrypto-zkp/src/groth16/generic_api.rs index 0da01b6e9f..4cfd0bb3e4 100644 --- a/fastcrypto-zkp/src/groth16/generic_api.rs +++ b/fastcrypto-zkp/src/groth16/generic_api.rs @@ -5,9 +5,9 @@ use serde::Deserialize; use fastcrypto::error::{FastCryptoError, FastCryptoResult}; use fastcrypto::groups::{ - deserialize_vector, serialize_vector, GroupElement, MultiScalarMul, Pairing, + GroupElement, MultiScalarMul, Pairing, }; -use fastcrypto::serde_helpers::ToFromByteArray; +use fastcrypto::serde_helpers::{deserialize_vector, serialize_vector, ToFromByteArray}; use crate::groth16::{PreparedVerifyingKey, VerifyingKey}; diff --git a/fastcrypto/src/groups/mod.rs b/fastcrypto/src/groups/mod.rs index 3ec9559890..3ff18ca809 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 b4da855931..0fd1e3e1b6 100644 --- a/fastcrypto/src/serde_helpers.rs +++ b/fastcrypto/src/serde_helpers.rs @@ -12,7 +12,7 @@ use serde_with::serde_as; use std::fmt; use std::fmt::{Debug, Display}; -use crate::error::FastCryptoError; +use crate::error::{FastCryptoError, FastCryptoResult}; use crate::{ encoding::{Base64, Encoding}, traits::{KeyPair, SigningKey, ToFromBytes, VerifyingKey}, @@ -277,7 +277,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; @@ -329,3 +329,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 5204eeeb870855e140bbcee23ae076472b9500f3 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 12/65] clippy --- fastcrypto-zkp/benches/proving.rs | 25 +++++--- .../src/bls12381/unit_tests/verifier_tests.rs | 3 +- fastcrypto-zkp/src/groth16/generic_api.rs | 4 +- fastcrypto/src/groups/mod.rs | 1 - fastcrypto/src/serde_helpers.rs | 64 +++++++++---------- 5 files changed, 51 insertions(+), 46 deletions(-) diff --git a/fastcrypto-zkp/benches/proving.rs b/fastcrypto-zkp/benches/proving.rs index feb16f8cee..8d5fe41633 100644 --- a/fastcrypto-zkp/benches/proving.rs +++ b/fastcrypto-zkp/benches/proving.rs @@ -1,9 +1,10 @@ +use std::ops::Mul; + // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 use ark_bls12_377::{Bls12_377, Fr as Bls377Fr}; use ark_bls12_381::{Bls12_381, Fr as BlsFr}; use ark_bn254::{Bn254, Fr as Bn254Fr}; - use ark_ec::pairing::Pairing; use ark_ff::{PrimeField, UniformRand}; use ark_groth16::Groth16; @@ -14,11 +15,12 @@ use criterion::{ criterion_group, criterion_main, measurement::Measurement, BenchmarkGroup, BenchmarkId, Criterion, SamplingMode, }; -use fastcrypto::groups::bls12381::G1Element; + +use fastcrypto::groups::bls12381::{G1Element, Scalar}; +use fastcrypto::serde_helpers::ToFromByteArray; +use fastcrypto_zkp::bn254; use fastcrypto_zkp::dummy_circuits::DummyCircuit; -use fastcrypto_zkp::groth16::Proof; -use fastcrypto_zkp::{bls12381, bn254}; -use std::ops::Mul; +use fastcrypto_zkp::groth16::{Proof, VerifyingKey}; #[path = "./utils.rs"] mod utils; @@ -431,10 +433,17 @@ fn bench_our_verify(grp: &mut BenchmarkGroup) { let ark_proof = Groth16::::prove(&pk, c, 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 proof: Proof = bcs::from_bytes(&proof_bytes).unwrap(); let v = c.a.unwrap().mul(c.b.unwrap()); - let vk = ark_vk.into(); + let mut v_bytes = [0u8; 32]; + v.serialize_compressed(v_bytes.as_mut_slice()).unwrap(); + v_bytes.reverse(); + let v = Scalar::from_byte_array(&v_bytes).unwrap(); + + let mut vk_bytes = Vec::new(); + ark_vk.serialize_compressed(&mut vk_bytes).unwrap(); + let vk = VerifyingKey::from_arkworks_format(&vk_bytes).unwrap(); grp.bench_with_input( BenchmarkId::new("BLST-based Groth16 process verifying key", *size), @@ -449,7 +458,7 @@ fn bench_our_verify(grp: &mut BenchmarkGroup) { BenchmarkId::new("BLST-based Groth16 verify with processed vk", *size), &(pvk, v), |b, (pvk, v)| { - b.iter(|| pvk.verify(&[(*v).into()], &proof).unwrap()); + b.iter(|| pvk.verify(&[*v], &proof).unwrap()); }, ); } diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs b/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs index ccd2282fa0..026dd8b8cb 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs +++ b/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs @@ -25,8 +25,7 @@ fn test_verify_with_processed_vk() { }; let (pk, vk) = Groth16::::circuit_specific_setup(c, rng).unwrap(); - let ark_proof: ArkworksProof = - Groth16::::prove(&pk, c, rng).unwrap().into(); + let ark_proof: ArkworksProof = Groth16::::prove(&pk, c, rng).unwrap(); let public_input = c.a.unwrap().mul(c.b.unwrap()); let mut proof_bytes = Vec::new(); diff --git a/fastcrypto-zkp/src/groth16/generic_api.rs b/fastcrypto-zkp/src/groth16/generic_api.rs index 4cfd0bb3e4..590b986da2 100644 --- a/fastcrypto-zkp/src/groth16/generic_api.rs +++ b/fastcrypto-zkp/src/groth16/generic_api.rs @@ -4,9 +4,7 @@ use serde::Deserialize; use fastcrypto::error::{FastCryptoError, FastCryptoResult}; -use fastcrypto::groups::{ - GroupElement, MultiScalarMul, Pairing, -}; +use fastcrypto::groups::{GroupElement, MultiScalarMul, Pairing}; use fastcrypto::serde_helpers::{deserialize_vector, serialize_vector, ToFromByteArray}; use crate::groth16::{PreparedVerifyingKey, VerifyingKey}; diff --git a/fastcrypto/src/groups/mod.rs b/fastcrypto/src/groups/mod.rs index 3ff18ca809..e561178e9f 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; diff --git a/fastcrypto/src/serde_helpers.rs b/fastcrypto/src/serde_helpers.rs index 0fd1e3e1b6..e1165db427 100644 --- a/fastcrypto/src/serde_helpers.rs +++ b/fastcrypto/src/serde_helpers.rs @@ -267,6 +267,37 @@ impl Display for BytesRepresentation { } } +/// 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() +} + // This is needed in Narwhal certificates but we don't want default implementations for all BytesRepresentations. impl Default for crate::bls12381::min_sig::BLS12381AggregateSignatureAsBytes { fn default() -> Self { @@ -277,7 +308,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; @@ -329,34 +360,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 8b8af0277d7339fa1962b7afd2742aa671cb2236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 4 Apr 2024 14:57:25 +0200 Subject: [PATCH 13/65] license --- fastcrypto-zkp/benches/proving.rs | 5 +++-- fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/fastcrypto-zkp/benches/proving.rs b/fastcrypto-zkp/benches/proving.rs index 8d5fe41633..ebf8ebf7fa 100644 --- a/fastcrypto-zkp/benches/proving.rs +++ b/fastcrypto-zkp/benches/proving.rs @@ -1,7 +1,8 @@ -use std::ops::Mul; - // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 + +use std::ops::Mul; + use ark_bls12_377::{Bls12_377, Fr as Bls377Fr}; use ark_bls12_381::{Bls12_381, Fr as BlsFr}; use ark_bn254::{Bn254, Fr as Bn254Fr}; diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs b/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs index 026dd8b8cb..efd3b5b855 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs +++ b/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs @@ -1,3 +1,6 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + use std::ops::Mul; use ark_bls12_381::{Bls12_381, Fr}; From b294f8ab747b46809fc14aee8eca60bebfc165d4 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 14/65] Clean up tests --- fastcrypto-zkp/src/bls12381/api.rs | 4 -- fastcrypto-zkp/src/bls12381/mod.rs | 8 ++++ .../src/bls12381/unit_tests/api_tests.rs | 39 ++++++++++--------- .../src/bls12381/unit_tests/test_helpers.rs | 29 ++++++++++++++ .../src/bls12381/unit_tests/verifier_tests.rs | 34 ++++------------ 5 files changed, 66 insertions(+), 48 deletions(-) create mode 100644 fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index 5724035274..89f5bf2411 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -9,10 +9,6 @@ use fastcrypto::groups::bls12381::{ use crate::groth16::generic_api; -#[cfg(test)] -#[path = "unit_tests/api_tests.rs"] -mod api_tests; - /// Deserialize bytes as an Arkwork representation of a verifying key, and return a vector of the four components of a prepared verified key (see more at [`crate::verifier::PreparedVerifyingKey`]). pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoError> { generic_api::prepare_pvk_bytes::< diff --git a/fastcrypto-zkp/src/bls12381/mod.rs b/fastcrypto-zkp/src/bls12381/mod.rs index 2673900eb3..62938bbc1c 100644 --- a/fastcrypto-zkp/src/bls12381/mod.rs +++ b/fastcrypto-zkp/src/bls12381/mod.rs @@ -15,6 +15,14 @@ pub mod api; #[path = "unit_tests/verifier_tests.rs"] mod verifier_tests; +#[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; diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs b/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs index 70d5696c7a..c19404d034 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs +++ b/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs @@ -16,7 +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::verifier_tests::scalar_from_arkworks; +use crate::bls12381::test_helpers::from_arkworks_scalar; use crate::bls12381::{PreparedVerifyingKey, VerifyingKey}; use crate::dummy_circuits::{DummyCircuit, Fibonacci}; use crate::groth16::Proof; @@ -34,21 +34,24 @@ fn test_verify_groth16_in_bytes_api() { let (pk, vk) = Groth16::::circuit_specific_setup(c, rng).unwrap(); let proof = Groth16::::prove(&pk, c, rng).unwrap(); - let v = c.a.unwrap().mul(c.b.unwrap()); + let public_input = c.a.unwrap().mul(c.b.unwrap()); + let mut vk_bytes = vec![]; vk.serialize_compressed(&mut vk_bytes).unwrap(); - let bytes = prepare_pvk_bytes(vk_bytes.as_slice()).unwrap(); + let bytes = prepare_pvk_bytes(&vk_bytes).unwrap(); let vk_gamma_abc_g1_bytes = &bytes[0]; let alpha_g1_beta_g2_bytes = &bytes[1]; let gamma_g2_neg_pc_bytes = &bytes[2]; let delta_g2_neg_pc_bytes = &bytes[3]; - let mut proof_inputs_bytes = vec![]; - v.serialize_compressed(&mut proof_inputs_bytes).unwrap(); + let mut public_inputs_bytes = vec![]; + public_input + .serialize_compressed(&mut public_inputs_bytes) + .unwrap(); - let mut proof_points_bytes = vec![]; - proof.serialize_compressed(&mut proof_points_bytes).unwrap(); + let mut proof_bytes = vec![]; + proof.serialize_compressed(&mut proof_bytes).unwrap(); // Success case. assert!(verify_groth16_in_bytes( @@ -56,13 +59,13 @@ fn test_verify_groth16_in_bytes_api() { alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - &proof_inputs_bytes, - &proof_points_bytes + &public_inputs_bytes, + &proof_bytes ) .unwrap()); // Negative test: Replace the A element with a random point. - let mut modified_proof_points_bytes = proof_points_bytes.clone(); + let mut modified_proof_points_bytes = proof_bytes.clone(); let _ = &G1Affine::rand(rng) .serialize_compressed(&mut modified_proof_points_bytes[0..48]) .unwrap(); @@ -71,7 +74,7 @@ fn test_verify_groth16_in_bytes_api() { alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - &proof_inputs_bytes, + &public_inputs_bytes, &modified_proof_points_bytes ) .unwrap()); @@ -84,13 +87,13 @@ fn test_verify_groth16_in_bytes_api() { alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - &proof_inputs_bytes, - &proof_points_bytes + &public_inputs_bytes, + &proof_bytes ) .is_err()); // Length of public inputs is incorrect. - let mut modified_proof_inputs_bytes = proof_inputs_bytes.clone(); + let mut modified_proof_inputs_bytes = public_inputs_bytes.clone(); modified_proof_inputs_bytes.pop(); assert!(verify_groth16_in_bytes( vk_gamma_abc_g1_bytes, @@ -98,19 +101,19 @@ fn test_verify_groth16_in_bytes_api() { gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, &modified_proof_inputs_bytes, - &proof_points_bytes + &proof_bytes ) .is_err()); // length of proof is incorrect - let mut modified_proof_points_bytes = proof_points_bytes.to_vec(); + let mut modified_proof_points_bytes = proof_bytes.to_vec(); modified_proof_points_bytes.pop(); assert!(verify_groth16_in_bytes( vk_gamma_abc_g1_bytes, alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - &proof_inputs_bytes, + &public_inputs_bytes, &modified_proof_points_bytes ) .is_err()); @@ -256,7 +259,7 @@ fn test_verify_groth16_in_bytes_multiple_inputs() { let vk = VerifyingKey::from_arkworks_format(&vk_bytes).unwrap(); let pvk = PreparedVerifyingKey::from(&vk); - let inputs: Vec<_> = vec![scalar_from_arkworks(&a), scalar_from_arkworks(&b)]; + let inputs: Vec<_> = vec![from_arkworks_scalar(&a), from_arkworks_scalar(&b)]; let proof: Proof = bcs::from_bytes(&proof_bytes).unwrap(); assert!(pvk.verify(&inputs, &proof).is_ok()); diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs b/fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs new file mode 100644 index 0000000000..fc1c35f8d4 --- /dev/null +++ b/fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs @@ -0,0 +1,29 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use crate::bls12381::{Proof, VerifyingKey}; +use ark_bls12_381::{Bls12_381, Fr}; +use ark_serialize::CanonicalSerialize; +use fastcrypto::groups::bls12381::{Scalar, SCALAR_LENGTH}; +use fastcrypto::serde_helpers::ToFromByteArray; + +pub(crate) fn from_arkworks_proof(ark_proof: &ark_groth16::Proof) -> Proof { + let mut proof_bytes = Vec::new(); + ark_proof.serialize_compressed(&mut proof_bytes).unwrap(); + bcs::from_bytes(&proof_bytes).unwrap() +} + +pub(crate) fn from_arkworks_vk(ark_vk: &ark_groth16::VerifyingKey) -> VerifyingKey { + let mut vk_bytes = Vec::new(); + ark_vk.serialize_compressed(&mut vk_bytes).unwrap(); + VerifyingKey::from_arkworks_format(&vk_bytes).unwrap() +} + +pub(crate) fn from_arkworks_scalar(scalar: &Fr) -> Scalar { + let mut scalar_bytes = [0u8; SCALAR_LENGTH]; + scalar + .serialize_compressed(scalar_bytes.as_mut_slice()) + .unwrap(); + scalar_bytes.reverse(); + Scalar::from_byte_array(&scalar_bytes).unwrap() +} diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs b/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs index efd3b5b855..05d231ea8d 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs +++ b/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs @@ -5,16 +5,12 @@ use std::ops::Mul; use ark_bls12_381::{Bls12_381, Fr}; use ark_ff::UniformRand; -use ark_groth16::{Groth16, Proof as ArkworksProof}; -use ark_serialize::CanonicalSerialize; +use ark_groth16::Groth16; use ark_snark::SNARK; use ark_std::rand::thread_rng; -use fastcrypto::groups::bls12381::{G1Element, Scalar, SCALAR_LENGTH}; -use fastcrypto::serde_helpers::ToFromByteArray; - +use crate::bls12381::test_helpers::{from_arkworks_proof, from_arkworks_scalar, from_arkworks_vk}; use crate::dummy_circuits::DummyCircuit; -use crate::groth16::{Proof, VerifyingKey}; #[test] fn test_verify_with_processed_vk() { @@ -24,31 +20,17 @@ fn test_verify_with_processed_vk() { a: Some(::rand(rng)), b: Some(::rand(rng)), num_variables: PUBLIC_SIZE, - num_constraints: 65536, + num_constraints: 256, }; let (pk, vk) = Groth16::::circuit_specific_setup(c, rng).unwrap(); - let ark_proof: ArkworksProof = Groth16::::prove(&pk, c, rng).unwrap(); + let ark_proof = Groth16::::prove(&pk, c, rng).unwrap(); let public_input = c.a.unwrap().mul(c.b.unwrap()); - let mut proof_bytes = Vec::new(); - ark_proof.serialize_compressed(&mut proof_bytes).unwrap(); - let proof: Proof = bcs::from_bytes(&proof_bytes).unwrap(); - - let mut vk_bytes = Vec::new(); - vk.serialize_compressed(&mut vk_bytes).unwrap(); - let vk = VerifyingKey::from_arkworks_format(&vk_bytes).unwrap(); - + let proof = from_arkworks_proof(&ark_proof); + let vk = from_arkworks_vk(&vk); let prepared_vk = crate::groth16::PreparedVerifyingKey::from(&vk); - let public_inputs = vec![scalar_from_arkworks(&public_input)]; - prepared_vk.verify(&public_inputs, &proof).unwrap() -} + let public_inputs = vec![from_arkworks_scalar(&public_input)]; -pub(crate) fn scalar_from_arkworks(scalar: &Fr) -> Scalar { - let mut scalar_bytes = [0u8; SCALAR_LENGTH]; - scalar - .serialize_compressed(scalar_bytes.as_mut_slice()) - .unwrap(); - scalar_bytes.reverse(); - Scalar::from_byte_array(&scalar_bytes).unwrap() + assert!(prepared_vk.verify(&public_inputs, &proof).is_ok()); } From b69032693d7d87529ae84c788563afc3f9278cc9 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 15/65] Clean up deps --- Cargo.lock | 13 ------------- fastcrypto-zkp/Cargo.toml | 2 -- 2 files changed, 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0264b2d831..4ed9f63bf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,17 +115,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" @@ -1492,7 +1481,6 @@ dependencies = [ name = "fastcrypto-zkp" version = "0.1.3" dependencies = [ - "ark-bls12-377", "ark-bls12-381", "ark-bn254", "ark-crypto-primitives", @@ -1506,7 +1494,6 @@ dependencies = [ "ark-std", "bcs", "blake2", - "blst", "byte-slice-cast", "criterion 0.5.1", "derive_more", diff --git a/fastcrypto-zkp/Cargo.toml b/fastcrypto-zkp/Cargo.toml index bdb77a6bc0..ceb3b88ad2 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"] } @@ -30,7 +29,6 @@ ark-relations = "0.4.0" ark-serialize = "0.4.1" ark-snark = "0.4.0" bcs.workspace = true -blst = "0.3.11" byte-slice-cast = "1.2.2" fastcrypto = { path = "../fastcrypto", version = "0.1.5" } derive_more = "0.99.16" From b4b36dd71807710ac61b1d77576d2904e056b28c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 4 Apr 2024 20:17:55 +0200 Subject: [PATCH 16/65] docs --- fastcrypto-zkp/src/groth16/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index e021043969..f74ccfe45e 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -76,6 +76,8 @@ where G1: Pairing + MultiScalarMul, ::Output: GroupElement, { + /// Verify Groth16 proof using the prepared verifying key (see more at + /// [`crate::bn254::verifier::PreparedVerifyingKey`]), a vector of public inputs and the proof. pub fn verify( &self, public_inputs: &[G1::ScalarType], @@ -85,6 +87,9 @@ where self.verify_with_prepared_inputs(&prepared_inputs, proof) } + /// Verify Groth16 proof using the prepared verifying key (see more at + /// [`crate::bn254::verifier::PreparedVerifyingKey`]), a prepared public input (see + /// [`prepare_inputs`]) and the proof. pub fn verify_with_prepared_inputs( &self, prepared_inputs: &G1, @@ -101,6 +106,7 @@ where } } + /// Prepare the public inputs for use in [`verify_with_prepared_inputs`]. pub fn prepare_inputs(&self, public_inputs: &[G1::ScalarType]) -> FastCryptoResult { if (public_inputs.len() + 1) != self.vk_gamma_abc.len() { return Err(FastCryptoError::InvalidInput); From b4a66c6766037fc926fba29cf77e3bc3b51cadf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Fri, 5 Apr 2024 12:57:43 +0200 Subject: [PATCH 17/65] Test reverse endianess --- fastcrypto-zkp/src/bls12381/api.rs | 50 +++++++++++++++---- .../src/bls12381/unit_tests/api_tests.rs | 2 +- fastcrypto-zkp/src/groth16/generic_api.rs | 1 + 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index 89f5bf2411..f3a997a300 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -1,14 +1,13 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +use crate::groth16::generic_api; use fastcrypto::error::{FastCryptoError, FastCryptoResult}; use fastcrypto::groups::bls12381::{ G1Element, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, GT_ELEMENT_BYTE_LENGTH, SCALAR_LENGTH, }; -use crate::groth16::generic_api; - /// Deserialize bytes as an Arkwork representation of a verifying key, and return a vector of the four components of a prepared verified key (see more at [`crate::verifier::PreparedVerifyingKey`]). pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoError> { generic_api::prepare_pvk_bytes::< @@ -48,18 +47,51 @@ pub fn verify_groth16_in_bytes( ) } -/// The public inputs are in big-endian byte order (like arkworks), but [fastcrypto::groups::bls12381::Scalar::from_byte_array] -/// expects them in little-endian. +/// The public inputs are in little-endian byte order, but [fastcrypto::groups::bls12381::Scalar::from_byte_array] +/// expects them in big-endian representation. fn reverse_endianness(scalars: &[u8]) -> FastCryptoResult> { if scalars.len() % SCALAR_LENGTH != 0 { return Err(FastCryptoError::InvalidInput); } let mut reversed_scalars = Vec::with_capacity(scalars.len()); - for scalar in scalars.chunks_exact(SCALAR_LENGTH) { - let mut scalar_bytes = [0u8; SCALAR_LENGTH]; - scalar_bytes.copy_from_slice(scalar); - scalar_bytes.reverse(); - reversed_scalars.extend_from_slice(&scalar_bytes); + for scalar in scalars.chunks(SCALAR_LENGTH) { + let mut scalar = scalar.to_vec(); + scalar.reverse(); + reversed_scalars.extend_from_slice(&scalar); } Ok(reversed_scalars) } + +#[cfg(test)] +mod tests { + use crate::bls12381::api::reverse_endianness; + use ark_bls12_381::Fr; + use ark_serialize::CanonicalSerialize; + use fastcrypto::groups::bls12381::{Scalar, SCALAR_LENGTH}; + use fastcrypto::serde_helpers::deserialize_vector; + + #[test] + fn test_reverse_endianness() { + let a = 123; + let b = 456; + let c = 789; + + let arkworks_scalars = vec![Fr::from(a), Fr::from(b), Fr::from(c)] + .iter() + .map(|x| { + let mut bytes = [0u8; SCALAR_LENGTH]; + x.serialize_compressed(bytes.as_mut_slice()).unwrap(); + bytes.to_vec() + }) + .flatten() + .collect::>(); + + let as_big_endian = reverse_endianness(&arkworks_scalars).unwrap(); + let blst_scalars = deserialize_vector::(&as_big_endian).unwrap(); + + assert_eq!(blst_scalars.len(), 3); + assert_eq!(blst_scalars[0], Scalar::from(a)); + assert_eq!(blst_scalars[1], Scalar::from(b)); + assert_eq!(blst_scalars[2], Scalar::from(c)); + } +} diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs b/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs index c19404d034..9798ceaf9b 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs +++ b/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs @@ -254,7 +254,7 @@ fn test_verify_groth16_in_bytes_multiple_inputs() { proof.b.serialize_compressed(&mut proof_bytes).unwrap(); proof.c.serialize_compressed(&mut proof_bytes).unwrap(); - let mut vk_bytes = vec![]; + let mut vk_bytes = Vec::new(); params.vk.serialize_compressed(&mut vk_bytes).unwrap(); let vk = VerifyingKey::from_arkworks_format(&vk_bytes).unwrap(); let pvk = PreparedVerifyingKey::from(&vk); diff --git a/fastcrypto-zkp/src/groth16/generic_api.rs b/fastcrypto-zkp/src/groth16/generic_api.rs index 590b986da2..6b143beb98 100644 --- a/fastcrypto-zkp/src/groth16/generic_api.rs +++ b/fastcrypto-zkp/src/groth16/generic_api.rs @@ -54,6 +54,7 @@ where G1::ScalarType: ToFromByteArray, { let x = deserialize_vector::(proof_public_inputs_as_bytes)?; + println!("x: {:?}", x); let proof = bcs::from_bytes(proof_points_as_bytes).map_err(|_| FastCryptoError::InvalidInput)?; let prepared_vk = PreparedVerifyingKey::::deserialize_from_parts( From dbdafb85da1a13e8540da58eea384ebae374cd2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Fri, 5 Apr 2024 13:09:23 +0200 Subject: [PATCH 18/65] clippy --- fastcrypto-zkp/benches/proving.rs | 11 ----------- fastcrypto-zkp/src/bls12381/api.rs | 5 ++--- fastcrypto-zkp/src/groth16/generic_api.rs | 1 - fastcrypto/src/serde_helpers.rs | 9 +++------ 4 files changed, 5 insertions(+), 21 deletions(-) diff --git a/fastcrypto-zkp/benches/proving.rs b/fastcrypto-zkp/benches/proving.rs index ebf8ebf7fa..874cbf0463 100644 --- a/fastcrypto-zkp/benches/proving.rs +++ b/fastcrypto-zkp/benches/proving.rs @@ -3,7 +3,6 @@ use std::ops::Mul; -use ark_bls12_377::{Bls12_377, Fr as Bls377Fr}; use ark_bls12_381::{Bls12_381, Fr as BlsFr}; use ark_bn254::{Bn254, Fr as Bn254Fr}; use ark_ec::pairing::Pairing; @@ -478,12 +477,6 @@ fn prove(c: &mut Criterion) { group.sample_size(10); bench_prove::(&mut group); group.finish(); - - let mut group: BenchmarkGroup<_> = c.benchmark_group("BLS12-377 Proving"); - group.sampling_mode(SamplingMode::Flat); // This can take a *while* - group.sample_size(10); - bench_prove::(&mut group); - group.finish(); } fn verify(c: &mut Criterion) { @@ -498,10 +491,6 @@ fn verify(c: &mut Criterion) { bench_verify::(&mut group); group.finish(); - let mut group: BenchmarkGroup<_> = c.benchmark_group("BLS12-377 Verification"); - bench_verify::(&mut group); - group.finish(); - let mut group: BenchmarkGroup<_> = c.benchmark_group("Elusiv Circuit Verification"); bench_verify_elusiv_circuit::<_>(&mut group); group.finish(); diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index f3a997a300..280d0f202e 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -76,14 +76,13 @@ mod tests { let b = 456; let c = 789; - let arkworks_scalars = vec![Fr::from(a), Fr::from(b), Fr::from(c)] + let arkworks_scalars = [Fr::from(a), Fr::from(b), Fr::from(c)] .iter() - .map(|x| { + .flat_map(|x| { let mut bytes = [0u8; SCALAR_LENGTH]; x.serialize_compressed(bytes.as_mut_slice()).unwrap(); bytes.to_vec() }) - .flatten() .collect::>(); let as_big_endian = reverse_endianness(&arkworks_scalars).unwrap(); diff --git a/fastcrypto-zkp/src/groth16/generic_api.rs b/fastcrypto-zkp/src/groth16/generic_api.rs index 6b143beb98..590b986da2 100644 --- a/fastcrypto-zkp/src/groth16/generic_api.rs +++ b/fastcrypto-zkp/src/groth16/generic_api.rs @@ -54,7 +54,6 @@ where G1::ScalarType: ToFromByteArray, { let x = deserialize_vector::(proof_public_inputs_as_bytes)?; - println!("x: {:?}", x); let proof = bcs::from_bytes(proof_points_as_bytes).map_err(|_| FastCryptoError::InvalidInput)?; let prepared_vk = PreparedVerifyingKey::::deserialize_from_parts( diff --git a/fastcrypto/src/serde_helpers.rs b/fastcrypto/src/serde_helpers.rs index e1165db427..be354930ea 100644 --- a/fastcrypto/src/serde_helpers.rs +++ b/fastcrypto/src/serde_helpers.rs @@ -276,12 +276,12 @@ pub fn deserialize_vector>>() @@ -292,10 +292,7 @@ pub fn deserialize_vector>( elements: &[T], ) -> Vec { - elements - .iter() - .flat_map(|e| e.to_byte_array().to_vec()) - .collect() + elements.iter().flat_map(|e| e.to_byte_array()).collect() } // This is needed in Narwhal certificates but we don't want default implementations for all BytesRepresentations. From 3255f1467c4718f5e3497093aa8b9804ed43a155 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Mon, 8 Apr 2024 13:18:52 +0200 Subject: [PATCH 19/65] test --- fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs b/fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs index fc1c35f8d4..e42ab0edc3 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs +++ b/fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs @@ -3,6 +3,7 @@ use crate::bls12381::{Proof, VerifyingKey}; use ark_bls12_381::{Bls12_381, Fr}; +use ark_ff::{BigInteger, PrimeField}; use ark_serialize::CanonicalSerialize; use fastcrypto::groups::bls12381::{Scalar, SCALAR_LENGTH}; use fastcrypto::serde_helpers::ToFromByteArray; @@ -20,10 +21,5 @@ pub(crate) fn from_arkworks_vk(ark_vk: &ark_groth16::VerifyingKey) -> } pub(crate) fn from_arkworks_scalar(scalar: &Fr) -> Scalar { - let mut scalar_bytes = [0u8; SCALAR_LENGTH]; - scalar - .serialize_compressed(scalar_bytes.as_mut_slice()) - .unwrap(); - scalar_bytes.reverse(); - Scalar::from_byte_array(&scalar_bytes).unwrap() + Scalar::from_byte_array(&scalar.into_bigint().to_bytes_be().try_into().unwrap()).unwrap() } From 8debbb7218f3452db9c69c079caf91336c1a2f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Mon, 8 Apr 2024 13:21:30 +0200 Subject: [PATCH 20/65] clippy --- fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs b/fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs index e42ab0edc3..4e77623d13 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs +++ b/fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs @@ -1,13 +1,15 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::bls12381::{Proof, VerifyingKey}; use ark_bls12_381::{Bls12_381, Fr}; use ark_ff::{BigInteger, PrimeField}; use ark_serialize::CanonicalSerialize; -use fastcrypto::groups::bls12381::{Scalar, SCALAR_LENGTH}; + +use fastcrypto::groups::bls12381::Scalar; use fastcrypto::serde_helpers::ToFromByteArray; +use crate::bls12381::{Proof, VerifyingKey}; + pub(crate) fn from_arkworks_proof(ark_proof: &ark_groth16::Proof) -> Proof { let mut proof_bytes = Vec::new(); ark_proof.serialize_compressed(&mut proof_bytes).unwrap(); From c8369b615fa4f550507020a8c5c4b69c5820bf53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Mon, 8 Apr 2024 14:29:35 +0200 Subject: [PATCH 21/65] msm --- fastcrypto-zkp/src/groth16/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index f74ccfe45e..7202efaa83 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -111,8 +111,9 @@ where 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) + Ok(public_inputs + .iter() + .zip(self.vk_gamma_abc.iter().skip(1)) + .fold(self.vk_gamma_abc[0], |acc, (s, g1)| acc + *g1 * s)) } } From d3c07fa52279062dcb184e1042698b35ea396ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Mon, 8 Apr 2024 17:40:59 +0200 Subject: [PATCH 22/65] traits --- fastcrypto-zkp/src/groth16/generic_api.rs | 6 +++--- fastcrypto-zkp/src/groth16/mod.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fastcrypto-zkp/src/groth16/generic_api.rs b/fastcrypto-zkp/src/groth16/generic_api.rs index 590b986da2..0dbb503503 100644 --- a/fastcrypto-zkp/src/groth16/generic_api.rs +++ b/fastcrypto-zkp/src/groth16/generic_api.rs @@ -4,7 +4,7 @@ use serde::Deserialize; use fastcrypto::error::{FastCryptoError, FastCryptoResult}; -use fastcrypto::groups::{GroupElement, MultiScalarMul, Pairing}; +use fastcrypto::groups::{GroupElement, Pairing}; use fastcrypto::serde_helpers::{deserialize_vector, serialize_vector, ToFromByteArray}; use crate::groth16::{PreparedVerifyingKey, VerifyingKey}; @@ -21,7 +21,7 @@ pub(crate) fn prepare_pvk_bytes< vk_bytes: &[u8], ) -> FastCryptoResult>> where - G1: Pairing + MultiScalarMul + for<'a> Deserialize<'a> + ToFromByteArray, + G1: Pairing + for<'a> Deserialize<'a> + ToFromByteArray, ::Other: for<'a> Deserialize<'a> + ToFromByteArray, ::Output: GroupElement + ToFromByteArray, { @@ -48,7 +48,7 @@ pub(crate) fn verify_groth16_in_bytes< proof_points_as_bytes: &[u8], ) -> Result where - G1: Pairing + MultiScalarMul + ToFromByteArray + for<'a> Deserialize<'a>, + G1: Pairing + ToFromByteArray + for<'a> Deserialize<'a>, ::Other: ToFromByteArray + for<'a> Deserialize<'a>, ::Output: GroupElement + ToFromByteArray, G1::ScalarType: ToFromByteArray, diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index 7202efaa83..65ad107bc7 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -6,7 +6,7 @@ use std::fmt::Debug; use fastcrypto::error::{FastCryptoError, FastCryptoResult}; use serde::Deserialize; -use fastcrypto::groups::{GroupElement, MultiScalarMul, Pairing}; +use fastcrypto::groups::{GroupElement, Pairing}; pub(crate) mod generic_api; @@ -73,7 +73,7 @@ where impl PreparedVerifyingKey where - G1: Pairing + MultiScalarMul, + G1: Pairing, ::Output: GroupElement, { /// Verify Groth16 proof using the prepared verifying key (see more at From 4780f1eae4e45111fb9db1ab2d01f90598c990c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 11 Apr 2024 09:51:52 +0200 Subject: [PATCH 23/65] Clean up + stricter trait bounds --- fastcrypto-zkp/src/bls12381/api.rs | 58 +++++++++++++++-------- fastcrypto-zkp/src/groth16/generic_api.rs | 23 ++++----- fastcrypto-zkp/src/groth16/mod.rs | 19 ++++---- fastcrypto/src/serde_helpers.rs | 2 +- 4 files changed, 61 insertions(+), 41 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index 280d0f202e..14f5002ca5 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -42,50 +42,70 @@ pub fn verify_groth16_in_bytes( alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - &reverse_endianness(proof_public_inputs_as_bytes)?, + &switch_scalar_endianness(proof_public_inputs_as_bytes)?, proof_points_as_bytes, ) } /// The public inputs are in little-endian byte order, but [fastcrypto::groups::bls12381::Scalar::from_byte_array] /// expects them in big-endian representation. -fn reverse_endianness(scalars: &[u8]) -> FastCryptoResult> { +fn switch_scalar_endianness(scalars: &[u8]) -> FastCryptoResult> { if scalars.len() % SCALAR_LENGTH != 0 { return Err(FastCryptoError::InvalidInput); } - let mut reversed_scalars = Vec::with_capacity(scalars.len()); - for scalar in scalars.chunks(SCALAR_LENGTH) { - let mut scalar = scalar.to_vec(); - scalar.reverse(); - reversed_scalars.extend_from_slice(&scalar); - } - Ok(reversed_scalars) + Ok(scalars + .chunks(SCALAR_LENGTH) + .flat_map(|chunk| { + let mut scalar = chunk.to_vec(); + scalar.reverse(); + scalar + }) + .collect()) } #[cfg(test)] mod tests { - use crate::bls12381::api::reverse_endianness; + use crate::bls12381::api::switch_scalar_endianness; use ark_bls12_381::Fr; + use ark_ff::Zero; use ark_serialize::CanonicalSerialize; use fastcrypto::groups::bls12381::{Scalar, SCALAR_LENGTH}; use fastcrypto::serde_helpers::deserialize_vector; - #[test] - fn test_reverse_endianness() { - let a = 123; - let b = 456; - let c = 789; - - let arkworks_scalars = [Fr::from(a), Fr::from(b), Fr::from(c)] + fn serialize_arkworks_scalars(scalars: &[Fr]) -> Vec { + scalars .iter() .flat_map(|x| { let mut bytes = [0u8; SCALAR_LENGTH]; x.serialize_compressed(bytes.as_mut_slice()).unwrap(); bytes.to_vec() }) - .collect::>(); + .collect::>() + } + + #[test] + fn test_switch_scalar_endianness() { + // For an empty input, the output should also be empty. + assert_eq!(switch_scalar_endianness(&[]).unwrap(), Vec::::new()); + + // Zero is the same in both big-endian and little-endian. + assert_eq!( + switch_scalar_endianness(&serialize_arkworks_scalars(&[Fr::zero()])).unwrap(), + vec![0u8; SCALAR_LENGTH] + ); + + // Invalid input lengths + assert!(switch_scalar_endianness(&[0; SCALAR_LENGTH - 1]).is_err()); + assert!(switch_scalar_endianness(&[0; SCALAR_LENGTH]).is_ok()); + assert!(switch_scalar_endianness(&[0; SCALAR_LENGTH + 1]).is_err()); + + // Test with a few non-trivial numbers. + let a = 123; + let b = 456; + let c = 789; - let as_big_endian = reverse_endianness(&arkworks_scalars).unwrap(); + let arkworks_scalars = serialize_arkworks_scalars(&[Fr::from(a), Fr::from(b), Fr::from(c)]); + let as_big_endian = switch_scalar_endianness(&arkworks_scalars).unwrap(); let blst_scalars = deserialize_vector::(&as_big_endian).unwrap(); assert_eq!(blst_scalars.len(), 3); diff --git a/fastcrypto-zkp/src/groth16/generic_api.rs b/fastcrypto-zkp/src/groth16/generic_api.rs index 0dbb503503..7c458fd804 100644 --- a/fastcrypto-zkp/src/groth16/generic_api.rs +++ b/fastcrypto-zkp/src/groth16/generic_api.rs @@ -1,7 +1,7 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use serde::Deserialize; +use serde::de::DeserializeOwned; use fastcrypto::error::{FastCryptoError, FastCryptoResult}; use fastcrypto::groups::{GroupElement, Pairing}; @@ -21,9 +21,9 @@ pub(crate) fn prepare_pvk_bytes< vk_bytes: &[u8], ) -> FastCryptoResult>> where - G1: Pairing + for<'a> Deserialize<'a> + ToFromByteArray, - ::Other: for<'a> Deserialize<'a> + ToFromByteArray, - ::Output: GroupElement + ToFromByteArray, + G1: Pairing + ToFromByteArray, + ::Other: ToFromByteArray, + ::Output: ToFromByteArray, { let vk = VerifyingKey::::from_arkworks_format::(vk_bytes)?; Ok(PreparedVerifyingKey::from(&vk).serialize_into_parts()) @@ -34,7 +34,7 @@ where /// be concatenated serialized field elements of the scalar field of [`crate::conversions::SCALAR_SIZE`] /// bytes each, and serialized proof points. pub(crate) fn verify_groth16_in_bytes< - G1, + G1: Pairing, const G1_SIZE: usize, const G2_SIZE: usize, const GT_SIZE: usize, @@ -48,8 +48,8 @@ pub(crate) fn verify_groth16_in_bytes< proof_points_as_bytes: &[u8], ) -> Result where - G1: Pairing + ToFromByteArray + for<'a> Deserialize<'a>, - ::Other: ToFromByteArray + for<'a> Deserialize<'a>, + G1: ToFromByteArray + DeserializeOwned, + ::Other: ToFromByteArray + DeserializeOwned, ::Output: GroupElement + ToFromByteArray, G1::ScalarType: ToFromByteArray, { @@ -123,6 +123,10 @@ impl VerifyingKey { .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, ); + // There must be at least one element in gamma_abc, which implies that there are no public inputs + if n == 0 { + return Err(FastCryptoError::InvalidInput); + } i += 8; let gamma_abc = deserialize_vector::(&bytes[i..]) @@ -142,10 +146,7 @@ impl VerifyingKey { } } -impl PreparedVerifyingKey -where - ::Output: GroupElement, -{ +impl PreparedVerifyingKey { pub fn serialize_into_parts( &self, ) -> Vec> diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index 65ad107bc7..43fe68d131 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -36,11 +36,9 @@ where /// This is roughly homologous to [`ark_groth16::data_structures::PreparedVerifyingKey`]. /// Note that contrary to Arkworks, we don't store a "prepared" version of the `gamma_neg` and /// `delta_neg` fields because they are very large and unpractical to use in the binary API. -#[derive(Clone, Debug, PartialEq, Eq)] pub struct PreparedVerifyingKey where G1: Pairing, - ::Output: Clone + Debug + PartialEq + Eq, { /// The element vk.gamma_abc, /// aka the `[gamma^{-1} * (beta * a_i + alpha * b_i + c_i) * G]`, where i spans the public inputs @@ -59,7 +57,6 @@ where impl From<&VerifyingKey> for PreparedVerifyingKey where G1: Pairing, - ::Output: GroupElement, { fn from(vk: &VerifyingKey) -> Self { PreparedVerifyingKey { @@ -71,18 +68,17 @@ where } } -impl PreparedVerifyingKey -where - G1: Pairing, - ::Output: GroupElement, -{ +impl PreparedVerifyingKey { /// Verify Groth16 proof using the prepared verifying key (see more at /// [`crate::bn254::verifier::PreparedVerifyingKey`]), a vector of public inputs and the proof. pub fn verify( &self, public_inputs: &[G1::ScalarType], proof: &Proof, - ) -> FastCryptoResult<()> { + ) -> FastCryptoResult<()> + where + ::Output: GroupElement, + { let prepared_inputs = self.prepare_inputs(public_inputs)?; self.verify_with_prepared_inputs(&prepared_inputs, proof) } @@ -94,7 +90,10 @@ where &self, prepared_inputs: &G1, proof: &Proof, - ) -> FastCryptoResult<()> { + ) -> FastCryptoResult<()> + where + ::Output: GroupElement, + { let lhs = proof.a.pairing(&proof.b) + prepared_inputs.pairing(&self.gamma_neg) + proof.c.pairing(&self.delta_neg); diff --git a/fastcrypto/src/serde_helpers.rs b/fastcrypto/src/serde_helpers.rs index be354930ea..301f78392c 100644 --- a/fastcrypto/src/serde_helpers.rs +++ b/fastcrypto/src/serde_helpers.rs @@ -281,7 +281,7 @@ pub fn deserialize_vector>>() From d3d651c1d5dfcea35526816c1d644e26e7e0bf34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 11 Apr 2024 09:55:29 +0200 Subject: [PATCH 24/65] doc --- fastcrypto-zkp/src/groth16/generic_api.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fastcrypto-zkp/src/groth16/generic_api.rs b/fastcrypto-zkp/src/groth16/generic_api.rs index 7c458fd804..0b8a6cc6a1 100644 --- a/fastcrypto-zkp/src/groth16/generic_api.rs +++ b/fastcrypto-zkp/src/groth16/generic_api.rs @@ -123,7 +123,8 @@ impl VerifyingKey { .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, ); - // There must be at least one element in gamma_abc, which implies that there are no public inputs + // There must be at least one element in gamma_abc, since this should be equal to the number + // of public inputs + 1. if n == 0 { return Err(FastCryptoError::InvalidInput); } From 38f8467e8c7ce6ac3de51e6138c0ace8815560bf 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 25/65] Clean up after rebase --- Cargo.lock | 12 ++++++++++++ fastcrypto-zkp/Cargo.toml | 1 + 2 files changed, 13 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 4ed9f63bf5..92c489836f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -115,6 +115,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" @@ -1481,6 +1492,7 @@ 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 ceb3b88ad2..77ba31fb08 100644 --- a/fastcrypto-zkp/Cargo.toml +++ b/fastcrypto-zkp/Cargo.toml @@ -47,6 +47,7 @@ itertools = "0.12.0" [dev-dependencies] ark-bls12-377 = "0.4.0" +ark-bls12-381 = "0.4.0" ark-crypto-primitives = { version = "0.4.0", features = ["r1cs", "prf"] } ark-r1cs-std = "0.4.0" ark-std = "0.4.0" From 09f17f3b541ae246658e7c1f79d8d76667f17157 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Thu, 11 Apr 2024 15:12:57 +0200 Subject: [PATCH 26/65] Regresison test --- fastcrypto-zkp/src/bls12381/api.rs | 97 ++++++++++++++----- .../src/bls12381/unit_tests/api_tests.rs | 17 ++++ 2 files changed, 89 insertions(+), 25 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index 14f5002ca5..08c067f2a6 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -1,22 +1,25 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::groth16::generic_api; use fastcrypto::error::{FastCryptoError, FastCryptoResult}; use fastcrypto::groups::bls12381::{ - G1Element, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, GT_ELEMENT_BYTE_LENGTH, - SCALAR_LENGTH, + G1Element, FP_BYTE_LENGTH, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, + GT_ELEMENT_BYTE_LENGTH, SCALAR_LENGTH, }; +use crate::groth16::generic_api; + /// Deserialize bytes as an Arkwork representation of a verifying key, and return a vector of the four components of a prepared verified key (see more at [`crate::verifier::PreparedVerifyingKey`]). pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoError> { - generic_api::prepare_pvk_bytes::< + let mut pvk_bytes = generic_api::prepare_pvk_bytes::< G1Element, { G1_ELEMENT_BYTE_LENGTH }, { G2_ELEMENT_BYTE_LENGTH }, { GT_ELEMENT_BYTE_LENGTH }, { SCALAR_LENGTH }, - >(vk_bytes) + >(vk_bytes)?; + pvk_bytes[1] = gt_element_conversion(&pvk_bytes[1])?; + Ok(pvk_bytes) } /// Verify Groth16 proof using the serialized form of the four components in a prepared verifying key @@ -42,36 +45,76 @@ pub fn verify_groth16_in_bytes( alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - &switch_scalar_endianness(proof_public_inputs_as_bytes)?, + &switch_scalar_endianness::(proof_public_inputs_as_bytes)?, proof_points_as_bytes, ) } -/// The public inputs are in little-endian byte order, but [fastcrypto::groups::bls12381::Scalar::from_byte_array] -/// expects them in big-endian representation. -fn switch_scalar_endianness(scalars: &[u8]) -> FastCryptoResult> { - if scalars.len() % SCALAR_LENGTH != 0 { +/// Given a vector of concatenated binary representations of scalars of the same length, this function +/// switches the endianess for each scalar and returns them as a vector. +fn switch_scalar_endianness( + scalars: &[u8], +) -> FastCryptoResult> { + if scalars.len() % SCALAR_SIZE_IN_BYTES != 0 { + return Err(FastCryptoError::InvalidInput); + } + let mut result = scalars.to_vec(); + switch_scalar_endianness_in_place::(&mut result)?; + Ok(result) +} + +fn switch_scalar_endianness_in_place( + scalars: &mut [u8], +) -> FastCryptoResult<()> { + if scalars.len() % SCALAR_SIZE_IN_BYTES != 0 { + return Err(FastCryptoError::InvalidInput); + } + scalars + .chunks_exact_mut(SCALAR_SIZE_IN_BYTES) + .for_each(|chunk| chunk.reverse()); + Ok(()) +} + +/// Given a arkworks represetation of a GT element, this converts the representation into a format +/// compatible with the [bls12381::GTElement] type. +fn gt_element_conversion(bytes: &[u8]) -> FastCryptoResult> { + // The conversion has two steps: + // 1) Reorder the six pairs of field elements in the following way: [0,1,2,3,4,5] <- [0,2,4,1,3,5] + // 2) Switch all 12 field elements from little-endian to big-endian + + if bytes.len() != 12 * FP_BYTE_LENGTH { return Err(FastCryptoError::InvalidInput); } - Ok(scalars - .chunks(SCALAR_LENGTH) - .flat_map(|chunk| { - let mut scalar = chunk.to_vec(); - scalar.reverse(); - scalar - }) - .collect()) + + let mut result = bytes.to_vec(); + + // Step 1 + result[2 * FP_BYTE_LENGTH..4 * FP_BYTE_LENGTH] + .copy_from_slice(&bytes[4 * FP_BYTE_LENGTH..6 * FP_BYTE_LENGTH]); + result[4 * FP_BYTE_LENGTH..6 * FP_BYTE_LENGTH] + .copy_from_slice(&bytes[8 * FP_BYTE_LENGTH..10 * FP_BYTE_LENGTH]); + result[6 * FP_BYTE_LENGTH..8 * FP_BYTE_LENGTH] + .copy_from_slice(&bytes[2 * FP_BYTE_LENGTH..4 * FP_BYTE_LENGTH]); + result[8 * FP_BYTE_LENGTH..10 * FP_BYTE_LENGTH] + .copy_from_slice(&bytes[6 * FP_BYTE_LENGTH..8 * FP_BYTE_LENGTH]); + + // Step 2 + switch_scalar_endianness_in_place::(&mut result)?; + + Ok(result) } #[cfg(test)] mod tests { - use crate::bls12381::api::switch_scalar_endianness; use ark_bls12_381::Fr; use ark_ff::Zero; use ark_serialize::CanonicalSerialize; + use fastcrypto::groups::bls12381::{Scalar, SCALAR_LENGTH}; use fastcrypto::serde_helpers::deserialize_vector; + use crate::bls12381::api::switch_scalar_endianness; + fn serialize_arkworks_scalars(scalars: &[Fr]) -> Vec { scalars .iter() @@ -86,18 +129,22 @@ mod tests { #[test] fn test_switch_scalar_endianness() { // For an empty input, the output should also be empty. - assert_eq!(switch_scalar_endianness(&[]).unwrap(), Vec::::new()); + assert_eq!( + switch_scalar_endianness::(&[]).unwrap(), + Vec::::new() + ); // Zero is the same in both big-endian and little-endian. assert_eq!( - switch_scalar_endianness(&serialize_arkworks_scalars(&[Fr::zero()])).unwrap(), + switch_scalar_endianness::(&serialize_arkworks_scalars(&[Fr::zero()])) + .unwrap(), vec![0u8; SCALAR_LENGTH] ); // Invalid input lengths - assert!(switch_scalar_endianness(&[0; SCALAR_LENGTH - 1]).is_err()); - assert!(switch_scalar_endianness(&[0; SCALAR_LENGTH]).is_ok()); - assert!(switch_scalar_endianness(&[0; SCALAR_LENGTH + 1]).is_err()); + assert!(switch_scalar_endianness::(&[0; SCALAR_LENGTH - 1]).is_err()); + assert!(switch_scalar_endianness::(&[0; SCALAR_LENGTH]).is_ok()); + assert!(switch_scalar_endianness::(&[0; SCALAR_LENGTH + 1]).is_err()); // Test with a few non-trivial numbers. let a = 123; @@ -105,7 +152,7 @@ mod tests { let c = 789; let arkworks_scalars = serialize_arkworks_scalars(&[Fr::from(a), Fr::from(b), Fr::from(c)]); - let as_big_endian = switch_scalar_endianness(&arkworks_scalars).unwrap(); + let as_big_endian = switch_scalar_endianness::(&arkworks_scalars).unwrap(); let blst_scalars = deserialize_vector::(&as_big_endian).unwrap(); assert_eq!(blst_scalars.len(), 3); diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs b/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs index 9798ceaf9b..d7e3d5267f 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs +++ b/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs @@ -292,3 +292,20 @@ fn test_verify_groth16_in_bytes_multiple_inputs() { ) .unwrap()); } + +#[test] +fn test_prepared_vk_bytes() { + // Test vector + let vk_bytes = hex::decode("a84d039ad1ae98eeeee4c8ba9af9b6c5d1cfcb98c3fc92ccfcebd77bcccffa1d170d39da29e9b4aa83b98680cb90bb25946b2b70f9e3565510c5361d5d65cb458a0b3177d612dd340b8f8f8493c2772454e3e8f577a3f77865df851d1a159b800c2ec5bae889029fc419678e83dee900465d60e7ef26f614940e719c6f7c0c7db57464fa0481a93c18d52cb2fbf8dcf0a398b153643614fc1071a54e288edb6402f1d9e00d3408c76d95c16885cc992dff5c6ebee3b739cb22359ab2d126026a1626c43ea7b898a7c1d2904c1bd4bbce5d0b1b16fab8535a52d1b08a5217df2e912ee1b0f4140892afa31d479f78dfbc82ab58a209ad00df6c86ab14841e8daa7a380a6853f28bacf38aad9903b6149fff4b119dea16de8aa3e5050b9d563a01009e061a950c233f66511c8fae2a8c58503059821df7f6defbba8f93d26e412cc07b66a9f3cdd740cce5c8488ce94fc8020000000000000081aabea18713222ac45a6ef3208a09f55ce2dde8a11cc4b12788be2ae77ae318176d631d36d80942df576af651b57a31a95f2e9bcaebbb53a588251634715599f7a7e9d51fe872fe312edf0b39d98f0d7f8b5554f96f759c041ea38b4b1e5e19").unwrap(); + let expected_vk_bytes = hex::decode("81aabea18713222ac45a6ef3208a09f55ce2dde8a11cc4b12788be2ae77ae318176d631d36d80942df576af651b57a31a95f2e9bcaebbb53a588251634715599f7a7e9d51fe872fe312edf0b39d98f0d7f8b5554f96f759c041ea38b4b1e5e19").unwrap(); + let expected_alpha_bytes = hex::decode("097ca8074c7f1d661e25d70fc2e6f14aa874dabe3d8a5d7751a012a737d30b59fc0f5f6d4ce0ea6f6c4562912dfb2a1442df06f9f0b8fc2d834ca007c8620823926b2fc09367d0dfa9b205a216921715e13deedd93580c77cae413cbb83134051cb724633c58759c77e4eda4147a54b03b1f443b68c65247166465105ab5065847ae61ba9d8bdfec536212b0dadedc042dab119d0eeea16349493a4118d481761b1e75f559fbad57c926d599e81d98dde586a2cfcc37b49972e2f9db554e5a0ba56bec2d57a8bfed629ae29c95002e3e943311b7b0d1690d2329e874b179ce5d720bd7c5fb5a2f756b37e3510582cb0c0f8fc8047305fc222c309a5a8234c5ff31a7b311aabdcebf4a43d98b69071a9e5796372146f7199ba05f9ca0a3d14b0c421e7f1bd02ac87b365fd8ce992c0f87994d0ca66f75c72fed0ce94ca174fcb9e5092f0474e07e71e9fd687b3daa441193f264ca2059760faa9c5ca5ef38f6ecefef2ac7d8c47df67b99c36efa64f625fe3f55f40ad1865abbdf2ff4c3fc3a162e28b953f6faec70a6a61c76f4dca1eecc86544b88352994495ae7fc7a77d387880e59b2357d9dd1277ae7f7ee9ba00b440e0e6923dc3971de9050a977db59d767195622f200f2bf0d00e4a986e94a6932627954dd2b7da39b4fcb32c991a0190bdc44562ad83d34e0af7656b51d6cde03530b5d523380653130b87346720ad6dd425d8133ffb02f39a95fc70e9707181ecb168bd8d2d0e9e85e262255fecab15f1ada809ecbefa42a7082fa7326a1d494261a8954fe5b215c5b761fb10b7f18").unwrap(); + let expected_gamma_bytes = hex::decode("8398b153643614fc1071a54e288edb6402f1d9e00d3408c76d95c16885cc992dff5c6ebee3b739cb22359ab2d126026a1626c43ea7b898a7c1d2904c1bd4bbce5d0b1b16fab8535a52d1b08a5217df2e912ee1b0f4140892afa31d479f78dfbc").unwrap(); + let expected_delta_bytes = hex::decode("a2ab58a209ad00df6c86ab14841e8daa7a380a6853f28bacf38aad9903b6149fff4b119dea16de8aa3e5050b9d563a01009e061a950c233f66511c8fae2a8c58503059821df7f6defbba8f93d26e412cc07b66a9f3cdd740cce5c8488ce94fc8").unwrap(); + + let prepared_vk_bytes = prepare_pvk_bytes(&vk_bytes).unwrap(); + + assert_eq!(prepared_vk_bytes[0], expected_vk_bytes); + assert_eq!(prepared_vk_bytes[1], expected_alpha_bytes); + assert_eq!(prepared_vk_bytes[2], expected_gamma_bytes); + assert_eq!(prepared_vk_bytes[3], expected_delta_bytes); +} From 094b93709776cb6f3abb281340ad5bd07ec3f6f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Fri, 12 Apr 2024 10:24:06 +0200 Subject: [PATCH 27/65] Inplace conversion of gt elements --- fastcrypto-zkp/src/bls12381/api.rs | 165 +++++++++++++++++++---------- 1 file changed, 107 insertions(+), 58 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index 08c067f2a6..0eb172cf5d 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -9,7 +9,8 @@ use fastcrypto::groups::bls12381::{ use crate::groth16::generic_api; -/// Deserialize bytes as an Arkwork representation of a verifying key, and return a vector of the four components of a prepared verified key (see more at [`crate::verifier::PreparedVerifyingKey`]). +/// Create a prepared verifying key for Groth16 over the BLS12-381 curve construction. See +/// [`generic_api::prepare_pvk_bytes`]. pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoError> { let mut pvk_bytes = generic_api::prepare_pvk_bytes::< G1Element, @@ -18,14 +19,12 @@ pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoErro { GT_ELEMENT_BYTE_LENGTH }, { SCALAR_LENGTH }, >(vk_bytes)?; - pvk_bytes[1] = gt_element_conversion(&pvk_bytes[1])?; + gt_element_conversion_in_place(&mut pvk_bytes[1])?; Ok(pvk_bytes) } -/// Verify Groth16 proof using the serialized form of the four components in a prepared verifying key -/// (see more at [`crate::verifier::PreparedVerifyingKey`]), serialized proof public input, which should -/// be concatenated serialized field elements of the scalar field of [`crate::conversions::SCALAR_SIZE`] -/// bytes each, and serialized proof points. +/// Verify Groth16 proof over the BLS12-381 curve construction. See +/// [`generic_api::verify_groth16_in_bytes`]. pub fn verify_groth16_in_bytes( vk_gamma_abc_g1_bytes: &[u8], alpha_g1_beta_g2_bytes: &[u8], @@ -34,6 +33,11 @@ pub fn verify_groth16_in_bytes( proof_public_inputs_as_bytes: &[u8], proof_points_as_bytes: &[u8], ) -> Result { + // The generic API expects scalars in big-endian format, but the input here is as little-endian + // because this is used by arkworks. + let mut proof_public_inputs_as_bytes = proof_public_inputs_as_bytes.to_vec(); + switch_scalar_endianness_in_place::(&mut proof_public_inputs_as_bytes)?; + generic_api::verify_groth16_in_bytes::< G1Element, { G1_ELEMENT_BYTE_LENGTH }, @@ -45,24 +49,13 @@ pub fn verify_groth16_in_bytes( alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - &switch_scalar_endianness::(proof_public_inputs_as_bytes)?, + &proof_public_inputs_as_bytes, proof_points_as_bytes, ) } -/// Given a vector of concatenated binary representations of scalars of the same length, this function -/// switches the endianess for each scalar and returns them as a vector. -fn switch_scalar_endianness( - scalars: &[u8], -) -> FastCryptoResult> { - if scalars.len() % SCALAR_SIZE_IN_BYTES != 0 { - return Err(FastCryptoError::InvalidInput); - } - let mut result = scalars.to_vec(); - switch_scalar_endianness_in_place::(&mut result)?; - Ok(result) -} - +/// Given a vector of concatenated binary representations of scalars of the same length, this +/// function switches the endianess from big to little or vice-versa for each scalar. fn switch_scalar_endianness_in_place( scalars: &mut [u8], ) -> FastCryptoResult<()> { @@ -75,45 +68,54 @@ fn switch_scalar_endianness_in_place( Ok(()) } -/// Given a arkworks represetation of a GT element, this converts the representation into a format -/// compatible with the [bls12381::GTElement] type. -fn gt_element_conversion(bytes: &[u8]) -> FastCryptoResult> { - // The conversion has two steps: - // 1) Reorder the six pairs of field elements in the following way: [0,1,2,3,4,5] <- [0,2,4,1,3,5] - // 2) Switch all 12 field elements from little-endian to big-endian - - if bytes.len() != 12 * FP_BYTE_LENGTH { +/// Given a serialization of a [`fastcrypto::groups::bls12381::GTElement`], this method converts it +/// into a serialization of the corresponding arkworks [`PairingOutput`] type in place. It is _not_ +/// verified whether the input is a valid serialization of a GT element. +fn gt_element_conversion_in_place(bytes: &mut [u8]) -> FastCryptoResult<()> { + // An element in the quadratic extension of Fp consistes of two field elements. + const FP_EXTENSION_BYTE_LENGTH: usize = 2 * FP_BYTE_LENGTH; + if bytes.len() != 6 * FP_EXTENSION_BYTE_LENGTH { return Err(FastCryptoError::InvalidInput); } - let mut result = bytes.to_vec(); + // The conversion has two steps: + // 1) Re-order the six pairs of field elements (field extension elements) according to the + // following mapping: (0,1,2,3,4,5) -> (0,3,1,4,2,5), + // 2) Switch all 12 field elements from big-endian to little-endian. // Step 1 - result[2 * FP_BYTE_LENGTH..4 * FP_BYTE_LENGTH] - .copy_from_slice(&bytes[4 * FP_BYTE_LENGTH..6 * FP_BYTE_LENGTH]); - result[4 * FP_BYTE_LENGTH..6 * FP_BYTE_LENGTH] - .copy_from_slice(&bytes[8 * FP_BYTE_LENGTH..10 * FP_BYTE_LENGTH]); - result[6 * FP_BYTE_LENGTH..8 * FP_BYTE_LENGTH] - .copy_from_slice(&bytes[2 * FP_BYTE_LENGTH..4 * FP_BYTE_LENGTH]); - result[8 * FP_BYTE_LENGTH..10 * FP_BYTE_LENGTH] - .copy_from_slice(&bytes[6 * FP_BYTE_LENGTH..8 * FP_BYTE_LENGTH]); + // Only the middle 4 pairs needs to be permuted: 2 -> 1, 4 -> 2, 3 -> 4, 1 -> 3 + let (zero_one_two, three_four_five) = bytes.split_at_mut(3 * FP_EXTENSION_BYTE_LENGTH); + let (zero_one, two) = zero_one_two.split_at_mut(2 * FP_EXTENSION_BYTE_LENGTH); + let (_zero, one) = zero_one.split_at_mut(FP_EXTENSION_BYTE_LENGTH); + let (three_four, _five) = three_four_five.split_at_mut(2 * FP_EXTENSION_BYTE_LENGTH); + let (three, four) = three_four.split_at_mut(FP_EXTENSION_BYTE_LENGTH); + + let tmp = one.to_vec(); + one.copy_from_slice(two); + two.copy_from_slice(four); + four.copy_from_slice(three); + three.copy_from_slice(&tmp); // Step 2 - switch_scalar_endianness_in_place::(&mut result)?; + switch_scalar_endianness_in_place::(bytes)?; - Ok(result) + Ok(()) } #[cfg(test)] mod tests { - use ark_bls12_381::Fr; + use ark_bls12_381::{Bls12_381, Fr}; + use ark_ec::pairing::PairingOutput; + use ark_ec::Group; use ark_ff::Zero; use ark_serialize::CanonicalSerialize; - use fastcrypto::groups::bls12381::{Scalar, SCALAR_LENGTH}; - use fastcrypto::serde_helpers::deserialize_vector; + use fastcrypto::groups::bls12381::{GTElement, Scalar, FP_BYTE_LENGTH, SCALAR_LENGTH}; + use fastcrypto::groups::GroupElement; + use fastcrypto::serde_helpers::{deserialize_vector, ToFromByteArray}; - use crate::bls12381::api::switch_scalar_endianness; + use crate::bls12381::api::{gt_element_conversion_in_place, switch_scalar_endianness_in_place}; fn serialize_arkworks_scalars(scalars: &[Fr]) -> Vec { scalars @@ -129,35 +131,82 @@ mod tests { #[test] fn test_switch_scalar_endianness() { // For an empty input, the output should also be empty. - assert_eq!( - switch_scalar_endianness::(&[]).unwrap(), - Vec::::new() - ); + let mut empty = Vec::new(); + switch_scalar_endianness_in_place::(&mut empty).unwrap(); + assert_eq!(empty, Vec::::new()); // Zero is the same in both big-endian and little-endian. - assert_eq!( - switch_scalar_endianness::(&serialize_arkworks_scalars(&[Fr::zero()])) - .unwrap(), - vec![0u8; SCALAR_LENGTH] - ); + let mut zero = serialize_arkworks_scalars(&[Fr::zero()]); + switch_scalar_endianness_in_place::(&mut zero).unwrap(); + assert_eq!(zero, vec![0u8; SCALAR_LENGTH]); // Invalid input lengths - assert!(switch_scalar_endianness::(&[0; SCALAR_LENGTH - 1]).is_err()); - assert!(switch_scalar_endianness::(&[0; SCALAR_LENGTH]).is_ok()); - assert!(switch_scalar_endianness::(&[0; SCALAR_LENGTH + 1]).is_err()); + assert!( + switch_scalar_endianness_in_place::(&mut [0; SCALAR_LENGTH - 1]) + .is_err() + ); + assert!( + switch_scalar_endianness_in_place::(&mut [0; SCALAR_LENGTH]).is_ok() + ); + assert!( + switch_scalar_endianness_in_place::(&mut [0; SCALAR_LENGTH + 1]) + .is_err() + ); // Test with a few non-trivial numbers. let a = 123; let b = 456; let c = 789; - let arkworks_scalars = serialize_arkworks_scalars(&[Fr::from(a), Fr::from(b), Fr::from(c)]); - let as_big_endian = switch_scalar_endianness::(&arkworks_scalars).unwrap(); - let blst_scalars = deserialize_vector::(&as_big_endian).unwrap(); + let mut scalars = serialize_arkworks_scalars(&[Fr::from(a), Fr::from(b), Fr::from(c)]); + switch_scalar_endianness_in_place::(&mut scalars).unwrap(); + let blst_scalars = deserialize_vector::(&scalars).unwrap(); assert_eq!(blst_scalars.len(), 3); assert_eq!(blst_scalars[0], Scalar::from(a)); assert_eq!(blst_scalars[1], Scalar::from(b)); assert_eq!(blst_scalars[2], Scalar::from(c)); } + + #[test] + fn test_gt_element_conversion() { + let generator = PairingOutput::::generator(); + let mut compressed_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 compressed_bytes) + .unwrap(); + generator + .serialize_uncompressed(&mut uncompressed_bytes) + .unwrap(); + assert_eq!(compressed_bytes, uncompressed_bytes); + + // The arkworks serialization does not match the GroupElement serialization. + let mut expected = GTElement::generator().to_byte_array(); + assert_eq!(compressed_bytes.len(), expected.len()); + assert_ne!(compressed_bytes, expected); + + // After conversion, the arkworks serialization should match the GroupElement serialization. + gt_element_conversion_in_place(&mut expected).unwrap(); + assert_eq!(compressed_bytes, expected); + + // The identity is the same in both representations + let arkworks_id = PairingOutput::::zero(); + let mut arkworks_bytes = Vec::new(); + arkworks_id + .serialize_uncompressed(&mut arkworks_bytes) + .unwrap(); + + let mut fc_bytes = GTElement::zero().to_byte_array(); + assert_ne!(&fc_bytes.to_vec(), &arkworks_bytes); + gt_element_conversion_in_place(&mut fc_bytes).unwrap(); + assert_eq!(&fc_bytes.to_vec(), &arkworks_bytes); + + // Invalid input lengths + assert!(gt_element_conversion_in_place(&mut [0; 0]).is_err()); + assert!(gt_element_conversion_in_place(&mut [0; 12 * FP_BYTE_LENGTH - 1]).is_err()); + assert!(gt_element_conversion_in_place(&mut [0; 12 * FP_BYTE_LENGTH + 1]).is_err()); + } } From c2745c69543064ce3b0bba11c3729d0e00b5703e 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 28/65] Conversion in both ways --- fastcrypto-zkp/src/bls12381/api.rs | 77 +++++++++++-------- .../src/bls12381/unit_tests/api_tests.rs | 60 +++++++++++++++ 2 files changed, 106 insertions(+), 31 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index 0eb172cf5d..d9db110efa 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -19,7 +19,7 @@ pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoErro { GT_ELEMENT_BYTE_LENGTH }, { SCALAR_LENGTH }, >(vk_bytes)?; - gt_element_conversion_in_place(&mut pvk_bytes[1])?; + pvk_bytes[1] = gt_element_to_arkworks(&pvk_bytes[1])?; Ok(pvk_bytes) } @@ -46,7 +46,7 @@ pub fn verify_groth16_in_bytes( { SCALAR_LENGTH }, >( vk_gamma_abc_g1_bytes, - alpha_g1_beta_g2_bytes, + &arkworks_to_gt_element(&alpha_g1_beta_g2_bytes)?, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, &proof_public_inputs_as_bytes, @@ -71,36 +71,51 @@ fn switch_scalar_endianness_in_place( /// Given a serialization of a [`fastcrypto::groups::bls12381::GTElement`], this method converts it /// into a serialization of the corresponding arkworks [`PairingOutput`] type in place. It is _not_ /// verified whether the input is a valid serialization of a GT element. -fn gt_element_conversion_in_place(bytes: &mut [u8]) -> FastCryptoResult<()> { +fn arkworks_to_gt_element(bytes: &[u8]) -> FastCryptoResult> { // An element in the quadratic extension of Fp consistes of two field elements. const FP_EXTENSION_BYTE_LENGTH: usize = 2 * FP_BYTE_LENGTH; if bytes.len() != 6 * FP_EXTENSION_BYTE_LENGTH { return Err(FastCryptoError::InvalidInput); } - // The conversion has two steps: - // 1) Re-order the six pairs of field elements (field extension elements) according to the - // following mapping: (0,1,2,3,4,5) -> (0,3,1,4,2,5), - // 2) Switch all 12 field elements from big-endian to little-endian. - - // Step 1 - // Only the middle 4 pairs needs to be permuted: 2 -> 1, 4 -> 2, 3 -> 4, 1 -> 3 - let (zero_one_two, three_four_five) = bytes.split_at_mut(3 * FP_EXTENSION_BYTE_LENGTH); - let (zero_one, two) = zero_one_two.split_at_mut(2 * FP_EXTENSION_BYTE_LENGTH); - let (_zero, one) = zero_one.split_at_mut(FP_EXTENSION_BYTE_LENGTH); - let (three_four, _five) = three_four_five.split_at_mut(2 * FP_EXTENSION_BYTE_LENGTH); - let (three, four) = three_four.split_at_mut(FP_EXTENSION_BYTE_LENGTH); - - let tmp = one.to_vec(); - one.copy_from_slice(two); - two.copy_from_slice(four); - four.copy_from_slice(three); - three.copy_from_slice(&tmp); + // Reorder elements to match arkworks serialization. + let mut result = Vec::with_capacity(12 * FP_BYTE_LENGTH); + for i in 0..3 { + for j in 0..2 { + let from = i * FP_EXTENSION_BYTE_LENGTH + 3 * j * FP_EXTENSION_BYTE_LENGTH; + result.extend_from_slice(&bytes[from..from + FP_EXTENSION_BYTE_LENGTH]); + } + } // Step 2 - switch_scalar_endianness_in_place::(bytes)?; + switch_scalar_endianness_in_place::(&mut result)?; - Ok(()) + Ok(result) +} + +/// Given a serialization of a [`fastcrypto::groups::bls12381::GTElement`], this method converts it +/// into a serialization of the corresponding arkworks [`PairingOutput`] type in place. It is _not_ +/// verified whether the input is a valid serialization of a GT element. +fn gt_element_to_arkworks(bytes: &[u8]) -> FastCryptoResult> { + // An element in the quadratic extension of Fp consistes of two field elements. + const FP_EXTENSION_BYTE_LENGTH: usize = 2 * FP_BYTE_LENGTH; + if bytes.len() != 6 * FP_EXTENSION_BYTE_LENGTH { + return Err(FastCryptoError::InvalidInput); + } + + // Reorder elements to match arkworks serialization. + let mut result = Vec::with_capacity(12 * FP_BYTE_LENGTH); + for j in 0..2 { + for i in 0..3 { + let from = 2 * i * FP_EXTENSION_BYTE_LENGTH + j * FP_EXTENSION_BYTE_LENGTH; + result.extend_from_slice(&bytes[from..from + FP_EXTENSION_BYTE_LENGTH]); + } + } + + // Step 2 + switch_scalar_endianness_in_place::(&mut result)?; + + Ok(result) } #[cfg(test)] @@ -115,7 +130,7 @@ mod tests { use fastcrypto::groups::GroupElement; use fastcrypto::serde_helpers::{deserialize_vector, ToFromByteArray}; - use crate::bls12381::api::{gt_element_conversion_in_place, switch_scalar_endianness_in_place}; + use crate::bls12381::api::{gt_element_to_arkworks, switch_scalar_endianness_in_place}; fn serialize_arkworks_scalars(scalars: &[Fr]) -> Vec { scalars @@ -184,12 +199,12 @@ mod tests { assert_eq!(compressed_bytes, uncompressed_bytes); // The arkworks serialization does not match the GroupElement serialization. - let mut expected = GTElement::generator().to_byte_array(); + let expected = GTElement::generator().to_byte_array(); assert_eq!(compressed_bytes.len(), expected.len()); assert_ne!(compressed_bytes, expected); // After conversion, the arkworks serialization should match the GroupElement serialization. - gt_element_conversion_in_place(&mut expected).unwrap(); + let expected = gt_element_to_arkworks(&expected).unwrap(); assert_eq!(compressed_bytes, expected); // The identity is the same in both representations @@ -199,14 +214,14 @@ mod tests { .serialize_uncompressed(&mut arkworks_bytes) .unwrap(); - let mut fc_bytes = GTElement::zero().to_byte_array(); + let fc_bytes = GTElement::zero().to_byte_array(); assert_ne!(&fc_bytes.to_vec(), &arkworks_bytes); - gt_element_conversion_in_place(&mut fc_bytes).unwrap(); + let fc_bytes = gt_element_to_arkworks(&fc_bytes).unwrap(); assert_eq!(&fc_bytes.to_vec(), &arkworks_bytes); // Invalid input lengths - assert!(gt_element_conversion_in_place(&mut [0; 0]).is_err()); - assert!(gt_element_conversion_in_place(&mut [0; 12 * FP_BYTE_LENGTH - 1]).is_err()); - assert!(gt_element_conversion_in_place(&mut [0; 12 * FP_BYTE_LENGTH + 1]).is_err()); + assert!(gt_element_to_arkworks(&[0; 0]).is_err()); + assert!(gt_element_to_arkworks(&[0; 12 * FP_BYTE_LENGTH - 1]).is_err()); + assert!(gt_element_to_arkworks(&[0; 12 * FP_BYTE_LENGTH + 1]).is_err()); } } diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs b/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs index d7e3d5267f..4ccff94db4 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs +++ b/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs @@ -309,3 +309,63 @@ fn test_prepared_vk_bytes() { assert_eq!(prepared_vk_bytes[2], expected_gamma_bytes); assert_eq!(prepared_vk_bytes[3], expected_delta_bytes); } + +#[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 50517f3e7ca780b8044d43571f807231ba73f712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Fri, 12 Apr 2024 12:53:03 +0200 Subject: [PATCH 29/65] clean up --- fastcrypto-zkp/src/bls12381/api.rs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index d9db110efa..b39235f099 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -46,7 +46,7 @@ pub fn verify_groth16_in_bytes( { SCALAR_LENGTH }, >( vk_gamma_abc_g1_bytes, - &arkworks_to_gt_element(&alpha_g1_beta_g2_bytes)?, + &arkworks_to_gt_element(alpha_g1_beta_g2_bytes)?, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, &proof_public_inputs_as_bytes, @@ -68,17 +68,18 @@ fn switch_scalar_endianness_in_place( Ok(()) } -/// Given a serialization of a [`fastcrypto::groups::bls12381::GTElement`], this method converts it -/// into a serialization of the corresponding arkworks [`PairingOutput`] type in place. It is _not_ -/// verified whether the input is a valid serialization of a GT element. +// An element in the quadratic extension of Fp consistes of two field elements. +const FP_EXTENSION_BYTE_LENGTH: usize = 2 * FP_BYTE_LENGTH; + +/// Given a serialization of a arkworks [`PairingOutput`] element, this returns a serialization of a +///[`fastcrypto::groups::bls12381::GTElement`] element. It is _not_ verified whether the input is a +/// valid serialization. fn arkworks_to_gt_element(bytes: &[u8]) -> FastCryptoResult> { - // An element in the quadratic extension of Fp consistes of two field elements. - const FP_EXTENSION_BYTE_LENGTH: usize = 2 * FP_BYTE_LENGTH; if bytes.len() != 6 * FP_EXTENSION_BYTE_LENGTH { return Err(FastCryptoError::InvalidInput); } - // Reorder elements to match arkworks serialization. + // Re-order elements let mut result = Vec::with_capacity(12 * FP_BYTE_LENGTH); for i in 0..3 { for j in 0..2 { @@ -87,23 +88,19 @@ fn arkworks_to_gt_element(bytes: &[u8]) -> FastCryptoResult> { } } - // Step 2 switch_scalar_endianness_in_place::(&mut result)?; - Ok(result) } /// Given a serialization of a [`fastcrypto::groups::bls12381::GTElement`], this method converts it -/// into a serialization of the corresponding arkworks [`PairingOutput`] type in place. It is _not_ -/// verified whether the input is a valid serialization of a GT element. +/// into a serialization of the corresponding arkworks [`PairingOutput`] type. It is _not_ verified +/// whether the input is a valid serialization.. fn gt_element_to_arkworks(bytes: &[u8]) -> FastCryptoResult> { - // An element in the quadratic extension of Fp consistes of two field elements. - const FP_EXTENSION_BYTE_LENGTH: usize = 2 * FP_BYTE_LENGTH; if bytes.len() != 6 * FP_EXTENSION_BYTE_LENGTH { return Err(FastCryptoError::InvalidInput); } - // Reorder elements to match arkworks serialization. + // Re-order elements let mut result = Vec::with_capacity(12 * FP_BYTE_LENGTH); for j in 0..2 { for i in 0..3 { @@ -112,9 +109,7 @@ fn gt_element_to_arkworks(bytes: &[u8]) -> FastCryptoResult> { } } - // Step 2 switch_scalar_endianness_in_place::(&mut result)?; - Ok(result) } From b7feabbf25df47424c78f64a18c3a9ce302795c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Fri, 12 Apr 2024 12:55:47 +0200 Subject: [PATCH 30/65] Clean up test --- fastcrypto-zkp/src/bls12381/api.rs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index b39235f099..6ff70fad34 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -125,7 +125,9 @@ mod tests { use fastcrypto::groups::GroupElement; use fastcrypto::serde_helpers::{deserialize_vector, ToFromByteArray}; - use crate::bls12381::api::{gt_element_to_arkworks, switch_scalar_endianness_in_place}; + use crate::bls12381::api::{ + arkworks_to_gt_element, gt_element_to_arkworks, switch_scalar_endianness_in_place, + }; fn serialize_arkworks_scalars(scalars: &[Fr]) -> Vec { scalars @@ -181,38 +183,35 @@ mod tests { #[test] fn test_gt_element_conversion() { let generator = PairingOutput::::generator(); - let mut compressed_bytes = Vec::new(); + 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 compressed_bytes) - .unwrap(); + generator.serialize_compressed(&mut arkworks_bytes).unwrap(); generator .serialize_uncompressed(&mut uncompressed_bytes) .unwrap(); - assert_eq!(compressed_bytes, uncompressed_bytes); + assert_eq!(arkworks_bytes, uncompressed_bytes); // The arkworks serialization does not match the GroupElement serialization. - let expected = GTElement::generator().to_byte_array(); - assert_eq!(compressed_bytes.len(), expected.len()); - assert_ne!(compressed_bytes, expected); + 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. - let expected = gt_element_to_arkworks(&expected).unwrap(); - assert_eq!(compressed_bytes, expected); + assert_eq!(arkworks_bytes, gt_element_to_arkworks(&fc_bytes).unwrap()); + assert_eq!(arkworks_to_gt_element(&arkworks_bytes).unwrap(), fc_bytes); - // The identity is the same in both representations + // 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); - let fc_bytes = gt_element_to_arkworks(&fc_bytes).unwrap(); - assert_eq!(&fc_bytes.to_vec(), &arkworks_bytes); + assert_eq!(>_element_to_arkworks(&fc_bytes).unwrap(), &arkworks_bytes); + assert_eq!(arkworks_to_gt_element(&arkworks_bytes).unwrap(), fc_bytes); // Invalid input lengths assert!(gt_element_to_arkworks(&[0; 0]).is_err()); From 0f29d454f49028c304f3c9a5e442464ee975b6af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Fri, 12 Apr 2024 13:00:07 +0200 Subject: [PATCH 31/65] docs --- fastcrypto-zkp/src/bls12381/api.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index 6ff70fad34..c090206b79 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -19,6 +19,7 @@ pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoErro { GT_ELEMENT_BYTE_LENGTH }, { SCALAR_LENGTH }, >(vk_bytes)?; + // GT elements are serialized differently in arkworks and fastcrypto, so we need to convert. pvk_bytes[1] = gt_element_to_arkworks(&pvk_bytes[1])?; Ok(pvk_bytes) } @@ -34,10 +35,13 @@ pub fn verify_groth16_in_bytes( proof_points_as_bytes: &[u8], ) -> Result { // The generic API expects scalars in big-endian format, but the input here is as little-endian - // because this is used by arkworks. + // because it uses the same format as arkworks. let mut proof_public_inputs_as_bytes = proof_public_inputs_as_bytes.to_vec(); switch_scalar_endianness_in_place::(&mut proof_public_inputs_as_bytes)?; + // GT elements are serialized differently in arkworks and fastcrypto, so we need to convert. + let alpha_g1_beta_g2_bytes = arkworks_to_gt_element(alpha_g1_beta_g2_bytes)?; + generic_api::verify_groth16_in_bytes::< G1Element, { G1_ELEMENT_BYTE_LENGTH }, @@ -46,7 +50,7 @@ pub fn verify_groth16_in_bytes( { SCALAR_LENGTH }, >( vk_gamma_abc_g1_bytes, - &arkworks_to_gt_element(alpha_g1_beta_g2_bytes)?, + &alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, &proof_public_inputs_as_bytes, @@ -55,7 +59,7 @@ pub fn verify_groth16_in_bytes( } /// Given a vector of concatenated binary representations of scalars of the same length, this -/// function switches the endianess from big to little or vice-versa for each scalar. +/// function switches the endianness from big to little or vice-versa for each scalar. fn switch_scalar_endianness_in_place( scalars: &mut [u8], ) -> FastCryptoResult<()> { @@ -71,9 +75,9 @@ fn switch_scalar_endianness_in_place( // An element in the quadratic extension of Fp consistes of two field elements. const FP_EXTENSION_BYTE_LENGTH: usize = 2 * FP_BYTE_LENGTH; -/// Given a serialization of a arkworks [`PairingOutput`] element, this returns a serialization of a -///[`fastcrypto::groups::bls12381::GTElement`] element. It is _not_ verified whether the input is a -/// valid serialization. +/// Given a serialization of a arkworks [`PairingOutput`] element, this function returns a +/// serialization of the corresponding [`fastcrypto::groups::bls12381::GTElement`] element. It is +/// _not_ verified whether the input is a valid serialization. fn arkworks_to_gt_element(bytes: &[u8]) -> FastCryptoResult> { if bytes.len() != 6 * FP_EXTENSION_BYTE_LENGTH { return Err(FastCryptoError::InvalidInput); @@ -92,9 +96,9 @@ fn arkworks_to_gt_element(bytes: &[u8]) -> FastCryptoResult> { Ok(result) } -/// Given a serialization of a [`fastcrypto::groups::bls12381::GTElement`], this method converts it -/// into a serialization of the corresponding arkworks [`PairingOutput`] type. It is _not_ verified -/// whether the input is a valid serialization.. +/// Given a serialization of a [`fastcrypto::groups::bls12381::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]) -> FastCryptoResult> { if bytes.len() != 6 * FP_EXTENSION_BYTE_LENGTH { return Err(FastCryptoError::InvalidInput); From fca10b1c89315a51f86dce07510256a2a5e4f224 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Fri, 12 Apr 2024 13:01:53 +0200 Subject: [PATCH 32/65] typo --- fastcrypto-zkp/src/bls12381/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index c090206b79..b04d94188d 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -72,7 +72,7 @@ fn switch_scalar_endianness_in_place( Ok(()) } -// An element in the quadratic extension of Fp consistes of two field elements. +// An element in the quadratic extension of Fp consists of two field elements. const FP_EXTENSION_BYTE_LENGTH: usize = 2 * FP_BYTE_LENGTH; /// Given a serialization of a arkworks [`PairingOutput`] element, this function returns a From 1d66b18c3e07e36c5eb2c5e050b2609ad6022f4a 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 33/65] Use closures for generic conversion --- fastcrypto-zkp/src/bls12381/api.rs | 258 ++++++++++-------- .../src/bls12381/unit_tests/api_tests.rs | 6 +- fastcrypto-zkp/src/groth16/generic_api.rs | 32 ++- fastcrypto-zkp/src/groth16/mod.rs | 15 + fastcrypto/src/serde_helpers.rs | 22 +- 5 files changed, 187 insertions(+), 146 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index b04d94188d..e642465a7d 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -3,25 +3,23 @@ use fastcrypto::error::{FastCryptoError, FastCryptoResult}; use fastcrypto::groups::bls12381::{ - G1Element, FP_BYTE_LENGTH, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, + G1Element, GTElement, Scalar, FP_BYTE_LENGTH, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, GT_ELEMENT_BYTE_LENGTH, SCALAR_LENGTH, }; +use fastcrypto::serde_helpers::ToFromByteArray; -use crate::groth16::generic_api; +use crate::groth16::{generic_api, FromLittleEndianByteArray, GTSerialize}; /// Create a prepared verifying key for Groth16 over the BLS12-381 curve construction. See /// [`generic_api::prepare_pvk_bytes`]. pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoError> { - let mut pvk_bytes = generic_api::prepare_pvk_bytes::< + generic_api::prepare_pvk_bytes::< G1Element, { G1_ELEMENT_BYTE_LENGTH }, { G2_ELEMENT_BYTE_LENGTH }, { GT_ELEMENT_BYTE_LENGTH }, { SCALAR_LENGTH }, - >(vk_bytes)?; - // GT elements are serialized differently in arkworks and fastcrypto, so we need to convert. - pvk_bytes[1] = gt_element_to_arkworks(&pvk_bytes[1])?; - Ok(pvk_bytes) + >(vk_bytes) } /// Verify Groth16 proof over the BLS12-381 curve construction. See @@ -31,17 +29,9 @@ pub fn verify_groth16_in_bytes( alpha_g1_beta_g2_bytes: &[u8], gamma_g2_neg_pc_bytes: &[u8], delta_g2_neg_pc_bytes: &[u8], - proof_public_inputs_as_bytes: &[u8], + public_inputs_as_bytes: &[u8], proof_points_as_bytes: &[u8], ) -> Result { - // The generic API expects scalars in big-endian format, but the input here is as little-endian - // because it uses the same format as arkworks. - let mut proof_public_inputs_as_bytes = proof_public_inputs_as_bytes.to_vec(); - switch_scalar_endianness_in_place::(&mut proof_public_inputs_as_bytes)?; - - // GT elements are serialized differently in arkworks and fastcrypto, so we need to convert. - let alpha_g1_beta_g2_bytes = arkworks_to_gt_element(alpha_g1_beta_g2_bytes)?; - generic_api::verify_groth16_in_bytes::< G1Element, { G1_ELEMENT_BYTE_LENGTH }, @@ -53,68 +43,73 @@ pub fn verify_groth16_in_bytes( &alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - &proof_public_inputs_as_bytes, + &public_inputs_as_bytes, proof_points_as_bytes, ) } -/// Given a vector of concatenated binary representations of scalars of the same length, this -/// function switches the endianness from big to little or vice-versa for each scalar. -fn switch_scalar_endianness_in_place( - scalars: &mut [u8], -) -> FastCryptoResult<()> { - if scalars.len() % SCALAR_SIZE_IN_BYTES != 0 { - return Err(FastCryptoError::InvalidInput); +impl FromLittleEndianByteArray for Scalar { + fn from_little_endian_byte_array(bytes: &[u8; SCALAR_LENGTH]) -> FastCryptoResult { + let mut reversed = bytes.clone(); + reversed.reverse(); + Scalar::from_byte_array(&reversed) } - scalars - .chunks_exact_mut(SCALAR_SIZE_IN_BYTES) - .for_each(|chunk| chunk.reverse()); - Ok(()) } -// An element in the quadratic extension of Fp consists of two field elements. -const FP_EXTENSION_BYTE_LENGTH: usize = 2 * FP_BYTE_LENGTH; +impl GTSerialize for GTElement { + fn to_arkworks_bytes(&self) -> [u8; GT_ELEMENT_BYTE_LENGTH] { + gt_element_to_arkworks(&self.to_byte_array()) + } -/// Given a serialization of a arkworks [`PairingOutput`] element, this function returns a -/// serialization of the corresponding [`fastcrypto::groups::bls12381::GTElement`] element. It is -/// _not_ verified whether the input is a valid serialization. -fn arkworks_to_gt_element(bytes: &[u8]) -> FastCryptoResult> { - if bytes.len() != 6 * FP_EXTENSION_BYTE_LENGTH { - return Err(FastCryptoError::InvalidInput); + fn from_arkworks_bytes(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> FastCryptoResult { + GTElement::from_byte_array(&arkworks_to_gt_element(bytes)) } +} - // Re-order elements - let mut result = Vec::with_capacity(12 * FP_BYTE_LENGTH); +// 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 function f. +fn generic_convert (usize, usize)>( + bytes: &[u8; 6 * FP_EXTENSION_BYTE_LENGTH], + f: F, +) -> [u8; 6 * FP_EXTENSION_BYTE_LENGTH] { + let mut result = [0u8; 12 * FP_BYTE_LENGTH]; for i in 0..3 { for j in 0..2 { - let from = i * FP_EXTENSION_BYTE_LENGTH + 3 * j * FP_EXTENSION_BYTE_LENGTH; - result.extend_from_slice(&bytes[from..from + FP_EXTENSION_BYTE_LENGTH]); + let (from, to) = f(i, j); + 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 +} - switch_scalar_endianness_in_place::(&mut result)?; - Ok(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] { + generic_convert(bytes, |i, j| { + ( + i * FP_EXTENSION_BYTE_LENGTH + 3 * j * FP_EXTENSION_BYTE_LENGTH, + 2 * i * FP_EXTENSION_BYTE_LENGTH + j * FP_EXTENSION_BYTE_LENGTH, + ) + }) } -/// Given a serialization of a [`fastcrypto::groups::bls12381::GTElement`], this function returns a +/// 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]) -> FastCryptoResult> { - if bytes.len() != 6 * FP_EXTENSION_BYTE_LENGTH { - return Err(FastCryptoError::InvalidInput); - } - - // Re-order elements - let mut result = Vec::with_capacity(12 * FP_BYTE_LENGTH); - for j in 0..2 { - for i in 0..3 { - let from = 2 * i * FP_EXTENSION_BYTE_LENGTH + j * FP_EXTENSION_BYTE_LENGTH; - result.extend_from_slice(&bytes[from..from + FP_EXTENSION_BYTE_LENGTH]); - } - } - - switch_scalar_endianness_in_place::(&mut result)?; - Ok(result) +fn gt_element_to_arkworks(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> [u8; GT_ELEMENT_BYTE_LENGTH] { + generic_convert(bytes, |i, j| { + ( + 2 * i * FP_EXTENSION_BYTE_LENGTH + j * FP_EXTENSION_BYTE_LENGTH, + i * FP_EXTENSION_BYTE_LENGTH + 3 * j * FP_EXTENSION_BYTE_LENGTH, + ) + }) } #[cfg(test)] @@ -123,66 +118,21 @@ mod tests { use ark_ec::pairing::PairingOutput; use ark_ec::Group; use ark_ff::Zero; + use ark_groth16::Groth16; use ark_serialize::CanonicalSerialize; + use ark_std::rand::thread_rng; - use fastcrypto::groups::bls12381::{GTElement, Scalar, FP_BYTE_LENGTH, SCALAR_LENGTH}; + use fastcrypto::groups::bls12381::{G1Element, GTElement}; use fastcrypto::groups::GroupElement; - use fastcrypto::serde_helpers::{deserialize_vector, ToFromByteArray}; + use fastcrypto::serde_helpers::ToFromByteArray; use crate::bls12381::api::{ - arkworks_to_gt_element, gt_element_to_arkworks, switch_scalar_endianness_in_place, + arkworks_to_gt_element, gt_element_to_arkworks, verify_groth16_in_bytes, }; - - fn serialize_arkworks_scalars(scalars: &[Fr]) -> Vec { - scalars - .iter() - .flat_map(|x| { - let mut bytes = [0u8; SCALAR_LENGTH]; - x.serialize_compressed(bytes.as_mut_slice()).unwrap(); - bytes.to_vec() - }) - .collect::>() - } - - #[test] - fn test_switch_scalar_endianness() { - // For an empty input, the output should also be empty. - let mut empty = Vec::new(); - switch_scalar_endianness_in_place::(&mut empty).unwrap(); - assert_eq!(empty, Vec::::new()); - - // Zero is the same in both big-endian and little-endian. - let mut zero = serialize_arkworks_scalars(&[Fr::zero()]); - switch_scalar_endianness_in_place::(&mut zero).unwrap(); - assert_eq!(zero, vec![0u8; SCALAR_LENGTH]); - - // Invalid input lengths - assert!( - switch_scalar_endianness_in_place::(&mut [0; SCALAR_LENGTH - 1]) - .is_err() - ); - assert!( - switch_scalar_endianness_in_place::(&mut [0; SCALAR_LENGTH]).is_ok() - ); - assert!( - switch_scalar_endianness_in_place::(&mut [0; SCALAR_LENGTH + 1]) - .is_err() - ); - - // Test with a few non-trivial numbers. - let a = 123; - let b = 456; - let c = 789; - - let mut scalars = serialize_arkworks_scalars(&[Fr::from(a), Fr::from(b), Fr::from(c)]); - switch_scalar_endianness_in_place::(&mut scalars).unwrap(); - let blst_scalars = deserialize_vector::(&scalars).unwrap(); - - assert_eq!(blst_scalars.len(), 3); - assert_eq!(blst_scalars[0], Scalar::from(a)); - assert_eq!(blst_scalars[1], Scalar::from(b)); - assert_eq!(blst_scalars[2], Scalar::from(c)); - } + use crate::bls12381::test_helpers::from_arkworks_scalar; + use crate::bls12381::{PreparedVerifyingKey, VerifyingKey}; + use crate::dummy_circuits::Fibonacci; + use crate::groth16::Proof; #[test] fn test_gt_element_conversion() { @@ -203,8 +153,11 @@ mod tests { 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).unwrap()); - assert_eq!(arkworks_to_gt_element(&arkworks_bytes).unwrap(), fc_bytes); + 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(); @@ -214,12 +167,75 @@ mod tests { .unwrap(); let fc_bytes = GTElement::zero().to_byte_array(); assert_ne!(&fc_bytes.to_vec(), &arkworks_bytes); - assert_eq!(>_element_to_arkworks(&fc_bytes).unwrap(), &arkworks_bytes); - assert_eq!(arkworks_to_gt_element(&arkworks_bytes).unwrap(), fc_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 + ); + } - // Invalid input lengths - assert!(gt_element_to_arkworks(&[0; 0]).is_err()); - assert!(gt_element_to_arkworks(&[0; 12 * FP_BYTE_LENGTH - 1]).is_err()); - assert!(gt_element_to_arkworks(&[0; 12 * FP_BYTE_LENGTH + 1]).is_err()); + #[test] + fn test_verify_groth16_in_bytes_multiple_inputs() { + 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 proof = { + let circuit = Fibonacci::::new(42, a, b); + Groth16::::create_random_proof_with_reduction(circuit, ¶ms, &mut rng) + .unwrap() + }; + + // Proof::write serializes uncompressed and also adds a length to each element, so we serialize + // each individual element here to avoid that. + let mut proof_bytes = Vec::new(); + proof.a.serialize_compressed(&mut proof_bytes).unwrap(); + proof.b.serialize_compressed(&mut proof_bytes).unwrap(); + proof.c.serialize_compressed(&mut 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 pvk = PreparedVerifyingKey::from(&vk); + + let inputs: Vec<_> = vec![from_arkworks_scalar(&a), from_arkworks_scalar(&b)]; + + let proof: Proof = bcs::from_bytes(&proof_bytes).unwrap(); + assert!(pvk.verify(&inputs, &proof).is_ok()); + + let pvk = pvk.serialize_into_parts(); + + // This circuit has two public inputs: + let mut inputs_bytes = Vec::new(); + a.serialize_compressed(&mut inputs_bytes).unwrap(); + b.serialize_compressed(&mut inputs_bytes).unwrap(); + + assert!(verify_groth16_in_bytes( + &pvk[0], + &pvk[1], + &pvk[2], + &pvk[3], + &inputs_bytes, + &proof_bytes + ) + .unwrap()); + + inputs_bytes[0] += 1; + assert!(!verify_groth16_in_bytes( + &pvk[0], + &pvk[1], + &pvk[2], + &pvk[3], + &inputs_bytes, + &proof_bytes + ) + .unwrap()); } } diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs b/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs index 4ccff94db4..5ba89e3d0f 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs +++ b/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs @@ -10,16 +10,12 @@ use ark_serialize::CanonicalSerialize; use ark_snark::SNARK; use ark_std::rand::thread_rng; use ark_std::UniformRand; -use fastcrypto::groups::bls12381::G1Element; 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_groth16_in_bytes_api() { diff --git a/fastcrypto-zkp/src/groth16/generic_api.rs b/fastcrypto-zkp/src/groth16/generic_api.rs index 0b8a6cc6a1..e5cdef74a3 100644 --- a/fastcrypto-zkp/src/groth16/generic_api.rs +++ b/fastcrypto-zkp/src/groth16/generic_api.rs @@ -7,7 +7,7 @@ use fastcrypto::error::{FastCryptoError, FastCryptoResult}; use fastcrypto::groups::{GroupElement, Pairing}; use fastcrypto::serde_helpers::{deserialize_vector, serialize_vector, ToFromByteArray}; -use crate::groth16::{PreparedVerifyingKey, VerifyingKey}; +use crate::groth16::{FromLittleEndianByteArray, GTSerialize, PreparedVerifyingKey, VerifyingKey}; /// Deserialize bytes as an Arkworks representation of a verifying key, and return a vector of the /// four components of a prepared verified key (see more at [`PreparedVerifyingKey`]). @@ -23,7 +23,7 @@ pub(crate) fn prepare_pvk_bytes< where G1: Pairing + ToFromByteArray, ::Other: ToFromByteArray, - ::Output: ToFromByteArray, + ::Output: GTSerialize, { let vk = VerifyingKey::::from_arkworks_format::(vk_bytes)?; Ok(PreparedVerifyingKey::from(&vk).serialize_into_parts()) @@ -44,16 +44,19 @@ pub(crate) fn verify_groth16_in_bytes< alpha_g1_beta_g2_bytes: &[u8], gamma_g2_neg_pc_bytes: &[u8], delta_g2_neg_pc_bytes: &[u8], - proof_public_inputs_as_bytes: &[u8], + public_inputs_as_bytes: &[u8], proof_points_as_bytes: &[u8], ) -> Result where G1: ToFromByteArray + DeserializeOwned, ::Other: ToFromByteArray + DeserializeOwned, - ::Output: GroupElement + ToFromByteArray, - G1::ScalarType: ToFromByteArray, + ::Output: GroupElement + GTSerialize, + G1::ScalarType: FromLittleEndianByteArray, { - let x = deserialize_vector::(proof_public_inputs_as_bytes)?; + let public_inputs = deserialize_vector::( + public_inputs_as_bytes, + G1::ScalarType::from_little_endian_byte_array, + )?; let proof = bcs::from_bytes(proof_points_as_bytes).map_err(|_| FastCryptoError::InvalidInput)?; let prepared_vk = PreparedVerifyingKey::::deserialize_from_parts( @@ -62,7 +65,7 @@ where gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, )?; - Ok(prepared_vk.verify(&x, &proof).is_ok()) + Ok(prepared_vk.verify(&public_inputs, &proof).is_ok()) } impl VerifyingKey { @@ -130,7 +133,7 @@ impl VerifyingKey { } i += 8; - let gamma_abc = deserialize_vector::(&bytes[i..]) + let gamma_abc = deserialize_vector::(&bytes[i..], G1::from_byte_array) .map_err(|_| FastCryptoError::InvalidInput)?; if gamma_abc.len() != n as usize { @@ -154,11 +157,11 @@ impl PreparedVerifyingKey { where G1: ToFromByteArray, G1::Other: ToFromByteArray, - ::Output: ToFromByteArray, + ::Output: GTSerialize, { vec![ - serialize_vector(&self.vk_gamma_abc), - self.alpha_beta.to_byte_array().to_vec(), + serialize_vector(&self.vk_gamma_abc, G1::to_byte_array), + self.alpha_beta.to_arkworks_bytes().to_vec(), self.gamma_neg.to_byte_array().to_vec(), self.delta_neg.to_byte_array().to_vec(), ] @@ -177,14 +180,15 @@ impl PreparedVerifyingKey { where G1: ToFromByteArray, G1::Other: ToFromByteArray, - ::Output: ToFromByteArray, + ::Output: GTSerialize, { if vk_gamma_abc_bytes.len() % G1_SIZE != 0 { return Err(FastCryptoError::InvalidInput); } - let vk_gamma_abc = deserialize_vector::(vk_gamma_abc_bytes)?; + let vk_gamma_abc = + deserialize_vector::(vk_gamma_abc_bytes, G1::from_byte_array)?; - let alpha_beta = ::Output::from_byte_array( + let alpha_beta = ::Output::from_arkworks_bytes( alpha_beta_bytes .try_into() .map_err(|_| FastCryptoError::InvalidInput)?, diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index 43fe68d131..6f2d7683a3 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -116,3 +116,18 @@ impl PreparedVerifyingKey { .fold(self.vk_gamma_abc[0], |acc, (s, g1)| acc + *g1 * s)) } } + +/// 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; +} diff --git a/fastcrypto/src/serde_helpers.rs b/fastcrypto/src/serde_helpers.rs index 301f78392c..c30e07cc73 100644 --- a/fastcrypto/src/serde_helpers.rs +++ b/fastcrypto/src/serde_helpers.rs @@ -268,9 +268,14 @@ impl Display for BytesRepresentation { } /// 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>( +/// of type `T` with the given deserialization function. +pub fn deserialize_vector< + const SIZE_IN_BYTES: usize, + T, + Deserializer: Fn(&[u8; SIZE_IN_BYTES]) -> FastCryptoResult, +>( bytes: &[u8], + from_byte_array: Deserializer, ) -> FastCryptoResult> { if bytes.len() % SIZE_IN_BYTES != 0 { return Err(FastCryptoError::InvalidInput); @@ -278,7 +283,7 @@ pub fn deserialize_vector>( +/// representations with the given deserialization function. +pub fn serialize_vector< + const SIZE_IN_BYTES: usize, + T, + Serializer: Fn(&T) -> [u8; SIZE_IN_BYTES], +>( elements: &[T], + to_byte_array: Serializer, ) -> Vec { - elements.iter().flat_map(|e| e.to_byte_array()).collect() + elements.iter().flat_map(to_byte_array).collect() } // This is needed in Narwhal certificates but we don't want default implementations for all BytesRepresentations. From acaeb2befbbb6568f6b67028a64a0cf83049f331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Fri, 12 Apr 2024 14:55:58 +0200 Subject: [PATCH 34/65] Simplify conversion --- fastcrypto-zkp/src/bls12381/api.rs | 40 ++++++++++++------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index e642465a7d..1a2b96cb05 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -40,17 +40,17 @@ pub fn verify_groth16_in_bytes( { SCALAR_LENGTH }, >( vk_gamma_abc_g1_bytes, - &alpha_g1_beta_g2_bytes, + alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - &public_inputs_as_bytes, + public_inputs_as_bytes, proof_points_as_bytes, ) } impl FromLittleEndianByteArray for Scalar { fn from_little_endian_byte_array(bytes: &[u8; SCALAR_LENGTH]) -> FastCryptoResult { - let mut reversed = bytes.clone(); + let mut reversed = *bytes; reversed.reverse(); Scalar::from_byte_array(&reversed) } @@ -69,18 +69,18 @@ impl GTSerialize for GTElement { // 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 function f. -fn generic_convert (usize, usize)>( +/// Reorder the six field extensions in a GT element according to the permutation. +/// This functions panics if one of the elements in the permutation is larger than 5. +fn generic_convert( bytes: &[u8; 6 * FP_EXTENSION_BYTE_LENGTH], - f: F, + permutation: &[usize; 6], ) -> [u8; 6 * FP_EXTENSION_BYTE_LENGTH] { let mut result = [0u8; 12 * FP_BYTE_LENGTH]; - for i in 0..3 { - for j in 0..2 { - let (from, to) = f(i, j); - result[to..to + FP_EXTENSION_BYTE_LENGTH] - .copy_from_slice(&bytes[from..from + FP_EXTENSION_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) @@ -92,24 +92,16 @@ fn generic_convert (usize, usize)>( /// 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] { - generic_convert(bytes, |i, j| { - ( - i * FP_EXTENSION_BYTE_LENGTH + 3 * j * FP_EXTENSION_BYTE_LENGTH, - 2 * i * FP_EXTENSION_BYTE_LENGTH + j * FP_EXTENSION_BYTE_LENGTH, - ) - }) + const PERMUTATION: [usize; 6] = [0, 2, 4, 1, 3, 5]; + generic_convert(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] { - generic_convert(bytes, |i, j| { - ( - 2 * i * FP_EXTENSION_BYTE_LENGTH + j * FP_EXTENSION_BYTE_LENGTH, - i * FP_EXTENSION_BYTE_LENGTH + 3 * j * FP_EXTENSION_BYTE_LENGTH, - ) - }) + const PERMUTATION: [usize; 6] = [0, 3, 1, 4, 2, 5]; + generic_convert(bytes, &PERMUTATION) } #[cfg(test)] From 2dbec769540e08db9e35cfc9bc15ed017af4b08e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Fri, 12 Apr 2024 15:00:11 +0200 Subject: [PATCH 35/65] docs --- fastcrypto-zkp/src/groth16/generic_api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastcrypto-zkp/src/groth16/generic_api.rs b/fastcrypto-zkp/src/groth16/generic_api.rs index e5cdef74a3..907074ad6d 100644 --- a/fastcrypto-zkp/src/groth16/generic_api.rs +++ b/fastcrypto-zkp/src/groth16/generic_api.rs @@ -32,7 +32,7 @@ where /// Verify Groth16 proof using the serialized form of the four components in a prepared verifying key /// (see more at [`PreparedVerifyingKey`]), serialized proof public input, which should /// be concatenated serialized field elements of the scalar field of [`crate::conversions::SCALAR_SIZE`] -/// bytes each, and serialized proof points. +/// bytes each in little-endian format, and serialized proof points. pub(crate) fn verify_groth16_in_bytes< G1: Pairing, const G1_SIZE: usize, From f69802289cdbda64e7e951de107997669f945732 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 36/65] 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 6f2d7683a3..4efbd74601 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -128,6 +128,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 f9359cd98b7e84620f1f3fed0d54b48b7cbbefbd 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 37/65] refactor --- fastcrypto-zkp/src/bls12381/api.rs | 3 ++- fastcrypto-zkp/src/groth16/generic_api.rs | 18 +++++++++++++++++- fastcrypto-zkp/src/groth16/mod.rs | 16 ---------------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index 1a2b96cb05..8ded3cab1c 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -8,7 +8,8 @@ use fastcrypto::groups::bls12381::{ }; use fastcrypto::serde_helpers::ToFromByteArray; -use crate::groth16::{generic_api, FromLittleEndianByteArray, GTSerialize}; +use crate::groth16::generic_api; +use crate::groth16::generic_api::{FromLittleEndianByteArray, GTSerialize}; /// Create a prepared verifying key for Groth16 over the BLS12-381 curve construction. See /// [`generic_api::prepare_pvk_bytes`]. diff --git a/fastcrypto-zkp/src/groth16/generic_api.rs b/fastcrypto-zkp/src/groth16/generic_api.rs index 907074ad6d..fc4c48acab 100644 --- a/fastcrypto-zkp/src/groth16/generic_api.rs +++ b/fastcrypto-zkp/src/groth16/generic_api.rs @@ -7,7 +7,7 @@ use fastcrypto::error::{FastCryptoError, FastCryptoResult}; use fastcrypto::groups::{GroupElement, Pairing}; use fastcrypto::serde_helpers::{deserialize_vector, serialize_vector, ToFromByteArray}; -use crate::groth16::{FromLittleEndianByteArray, GTSerialize, PreparedVerifyingKey, VerifyingKey}; +use crate::groth16::{PreparedVerifyingKey, VerifyingKey}; /// Deserialize bytes as an Arkworks representation of a verifying key, and return a vector of the /// four components of a prepared verified key (see more at [`PreparedVerifyingKey`]). @@ -214,3 +214,19 @@ impl PreparedVerifyingKey { }) } } + +/// 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; +} diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index 4efbd74601..43fe68d131 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -116,19 +116,3 @@ impl PreparedVerifyingKey { .fold(self.vk_gamma_abc[0], |acc, (s, g1)| acc + *g1 * s)) } } - -/// 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 1057386cf4993c4bb68b5e9b2f468ef74ce1faed 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 38/65] docs + arkworks compatability tests --- fastcrypto-zkp/src/bls12381/api.rs | 115 +++++++++++++++--- .../src/groth16/{generic_api.rs => api.rs} | 4 +- fastcrypto-zkp/src/groth16/mod.rs | 2 +- 3 files changed, 101 insertions(+), 20 deletions(-) rename fastcrypto-zkp/src/groth16/{generic_api.rs => api.rs} (99%) diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index 8ded3cab1c..e9f469d480 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -8,13 +8,13 @@ use fastcrypto::groups::bls12381::{ }; use fastcrypto::serde_helpers::ToFromByteArray; -use crate::groth16::generic_api; -use crate::groth16::generic_api::{FromLittleEndianByteArray, GTSerialize}; +use crate::groth16::api; +use crate::groth16::api::{FromLittleEndianByteArray, GTSerialize}; /// Create a prepared verifying key for Groth16 over the BLS12-381 curve construction. See -/// [`generic_api::prepare_pvk_bytes`]. +/// [`api::prepare_pvk_bytes`]. pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoError> { - generic_api::prepare_pvk_bytes::< + api::prepare_pvk_bytes::< G1Element, { G1_ELEMENT_BYTE_LENGTH }, { G2_ELEMENT_BYTE_LENGTH }, @@ -24,7 +24,7 @@ pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoErro } /// Verify Groth16 proof over the BLS12-381 curve construction. See -/// [`generic_api::verify_groth16_in_bytes`]. +/// [`api::verify_groth16_in_bytes`]. pub fn verify_groth16_in_bytes( vk_gamma_abc_g1_bytes: &[u8], alpha_g1_beta_g2_bytes: &[u8], @@ -33,7 +33,7 @@ pub fn verify_groth16_in_bytes( public_inputs_as_bytes: &[u8], proof_points_as_bytes: &[u8], ) -> Result { - generic_api::verify_groth16_in_bytes::< + api::verify_groth16_in_bytes::< G1Element, { G1_ELEMENT_BYTE_LENGTH }, { G2_ELEMENT_BYTE_LENGTH }, @@ -70,12 +70,13 @@ impl GTSerialize for GTElement { // 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 permutation. +/// 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 generic_convert( +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; @@ -90,24 +91,31 @@ fn generic_convert( } /// 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. +/// 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]; - generic_convert(bytes, &PERMUTATION) + 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.. +/// 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]; - generic_convert(bytes, &PERMUTATION) + permute_elements(bytes, &PERMUTATION) } #[cfg(test)] mod tests { - use ark_bls12_381::{Bls12_381, Fr}; + use ark_bls12_381::{Bls12_381, Fr, G1Projective, G2Projective}; use ark_ec::pairing::PairingOutput; use ark_ec::Group; use ark_ff::Zero; @@ -115,7 +123,11 @@ mod tests { use ark_serialize::CanonicalSerialize; use ark_std::rand::thread_rng; - use fastcrypto::groups::bls12381::{G1Element, GTElement}; + use fastcrypto::error::FastCryptoError; + use fastcrypto::groups::bls12381::{ + G1Element, G2Element, GTElement, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, + GT_ELEMENT_BYTE_LENGTH, + }; use fastcrypto::groups::GroupElement; use fastcrypto::serde_helpers::ToFromByteArray; @@ -125,6 +137,7 @@ mod tests { use crate::bls12381::test_helpers::from_arkworks_scalar; use crate::bls12381::{PreparedVerifyingKey, VerifyingKey}; use crate::dummy_circuits::Fibonacci; + use crate::groth16::api::GTSerialize; use crate::groth16::Proof; #[test] @@ -231,4 +244,72 @@ mod tests { ) .unwrap()); } + + 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/groth16/generic_api.rs b/fastcrypto-zkp/src/groth16/api.rs similarity index 99% rename from fastcrypto-zkp/src/groth16/generic_api.rs rename to fastcrypto-zkp/src/groth16/api.rs index fc4c48acab..a25f852759 100644 --- a/fastcrypto-zkp/src/groth16/generic_api.rs +++ b/fastcrypto-zkp/src/groth16/api.rs @@ -11,7 +11,7 @@ use crate::groth16::{PreparedVerifyingKey, VerifyingKey}; /// Deserialize bytes as an Arkworks representation of a verifying key, and return a vector of the /// four components of a prepared verified key (see more at [`PreparedVerifyingKey`]). -pub(crate) fn prepare_pvk_bytes< +pub fn prepare_pvk_bytes< G1, const G1_SIZE: usize, const G2_SIZE: usize, @@ -33,7 +33,7 @@ where /// (see more at [`PreparedVerifyingKey`]), serialized proof public input, which should /// be concatenated serialized field elements of the scalar field of [`crate::conversions::SCALAR_SIZE`] /// bytes each in little-endian format, and serialized proof points. -pub(crate) fn verify_groth16_in_bytes< +pub fn verify_groth16_in_bytes< G1: Pairing, const G1_SIZE: usize, const G2_SIZE: usize, diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index 43fe68d131..d9b4a89aae 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -8,7 +8,7 @@ use serde::Deserialize; use fastcrypto::groups::{GroupElement, Pairing}; -pub(crate) mod generic_api; +pub mod api; #[derive(Debug, Deserialize)] pub struct Proof From 0175b246119e3db7835cf7bfc6debb03ef94ab41 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 39/65] refactor --- fastcrypto-zkp/src/bls12381/api.rs | 197 +-------------------- fastcrypto-zkp/src/bls12381/conversions.rs | 193 ++++++++++++++++++++ fastcrypto-zkp/src/bls12381/mod.rs | 1 + 3 files changed, 199 insertions(+), 192 deletions(-) create mode 100644 fastcrypto-zkp/src/bls12381/conversions.rs diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs index e9f469d480..14f70d5cc5 100644 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ b/fastcrypto-zkp/src/bls12381/api.rs @@ -1,15 +1,13 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use fastcrypto::error::{FastCryptoError, FastCryptoResult}; +use fastcrypto::error::FastCryptoError; use fastcrypto::groups::bls12381::{ - G1Element, GTElement, Scalar, FP_BYTE_LENGTH, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, + G1_ELEMENT_BYTE_LENGTH, G1Element, G2_ELEMENT_BYTE_LENGTH, GT_ELEMENT_BYTE_LENGTH, SCALAR_LENGTH, }; -use fastcrypto::serde_helpers::ToFromByteArray; use crate::groth16::api; -use crate::groth16::api::{FromLittleEndianByteArray, GTSerialize}; /// Create a prepared verifying key for Groth16 over the BLS12-381 curve construction. See /// [`api::prepare_pvk_bytes`]. @@ -49,137 +47,20 @@ pub fn verify_groth16_in_bytes( ) } -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, Fr, G1Projective, G2Projective}; - use ark_ec::pairing::PairingOutput; - use ark_ec::Group; - use ark_ff::Zero; + use ark_bls12_381::{Bls12_381, Fr}; use ark_groth16::Groth16; use ark_serialize::CanonicalSerialize; use ark_std::rand::thread_rng; - use fastcrypto::error::FastCryptoError; - use fastcrypto::groups::bls12381::{ - G1Element, G2Element, GTElement, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, - GT_ELEMENT_BYTE_LENGTH, - }; - use fastcrypto::groups::GroupElement; - use fastcrypto::serde_helpers::ToFromByteArray; - - use crate::bls12381::api::{ - arkworks_to_gt_element, gt_element_to_arkworks, verify_groth16_in_bytes, - }; + use fastcrypto::groups::bls12381::G1Element; + use crate::bls12381::api::verify_groth16_in_bytes; use crate::bls12381::test_helpers::from_arkworks_scalar; use crate::bls12381::{PreparedVerifyingKey, VerifyingKey}; use crate::dummy_circuits::Fibonacci; - use crate::groth16::api::GTSerialize; use crate::groth16::Proof; - #[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 - ); - } - #[test] fn test_verify_groth16_in_bytes_multiple_inputs() { let mut rng = thread_rng(); @@ -244,72 +125,4 @@ mod tests { ) .unwrap()); } - - 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/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 62938bbc1c..0315a46feb 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 50d9d92b6096193076479958e412ab10f1a3b3e0 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 40/65] refactor --- fastcrypto-zkp/src/bls12381/api.rs | 128 --------------- .../src/bls12381/{ => api}/conversions.rs | 19 ++- fastcrypto-zkp/src/bls12381/api/mod.rs | 52 +++++++ .../{unit_tests/api_tests.rs => api/tests.rs} | 147 ++++++++++-------- fastcrypto-zkp/src/bls12381/mod.rs | 54 +++++-- .../bls12381/{unit_tests => }/test_helpers.rs | 6 +- .../src/bls12381/unit_tests/verifier_tests.rs | 36 ----- 7 files changed, 194 insertions(+), 248 deletions(-) delete mode 100644 fastcrypto-zkp/src/bls12381/api.rs rename fastcrypto-zkp/src/bls12381/{ => api}/conversions.rs (93%) create mode 100644 fastcrypto-zkp/src/bls12381/api/mod.rs rename fastcrypto-zkp/src/bls12381/{unit_tests/api_tests.rs => api/tests.rs} (87%) rename fastcrypto-zkp/src/bls12381/{unit_tests => }/test_helpers.rs (80%) delete mode 100644 fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs diff --git a/fastcrypto-zkp/src/bls12381/api.rs b/fastcrypto-zkp/src/bls12381/api.rs deleted file mode 100644 index 14f70d5cc5..0000000000 --- a/fastcrypto-zkp/src/bls12381/api.rs +++ /dev/null @@ -1,128 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use fastcrypto::error::FastCryptoError; -use fastcrypto::groups::bls12381::{ - G1_ELEMENT_BYTE_LENGTH, G1Element, G2_ELEMENT_BYTE_LENGTH, - GT_ELEMENT_BYTE_LENGTH, SCALAR_LENGTH, -}; - -use crate::groth16::api; - -/// Create a prepared verifying key for Groth16 over the BLS12-381 curve construction. See -/// [`api::prepare_pvk_bytes`]. -pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoError> { - api::prepare_pvk_bytes::< - G1Element, - { G1_ELEMENT_BYTE_LENGTH }, - { G2_ELEMENT_BYTE_LENGTH }, - { GT_ELEMENT_BYTE_LENGTH }, - { SCALAR_LENGTH }, - >(vk_bytes) -} - -/// Verify Groth16 proof over the BLS12-381 curve construction. See -/// [`api::verify_groth16_in_bytes`]. -pub fn verify_groth16_in_bytes( - vk_gamma_abc_g1_bytes: &[u8], - alpha_g1_beta_g2_bytes: &[u8], - gamma_g2_neg_pc_bytes: &[u8], - delta_g2_neg_pc_bytes: &[u8], - public_inputs_as_bytes: &[u8], - proof_points_as_bytes: &[u8], -) -> Result { - api::verify_groth16_in_bytes::< - G1Element, - { G1_ELEMENT_BYTE_LENGTH }, - { G2_ELEMENT_BYTE_LENGTH }, - { GT_ELEMENT_BYTE_LENGTH }, - { SCALAR_LENGTH }, - >( - vk_gamma_abc_g1_bytes, - alpha_g1_beta_g2_bytes, - gamma_g2_neg_pc_bytes, - delta_g2_neg_pc_bytes, - public_inputs_as_bytes, - proof_points_as_bytes, - ) -} - -#[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; - use crate::bls12381::api::verify_groth16_in_bytes; - use crate::bls12381::test_helpers::from_arkworks_scalar; - use crate::bls12381::{PreparedVerifyingKey, VerifyingKey}; - use crate::dummy_circuits::Fibonacci; - use crate::groth16::Proof; - - #[test] - fn test_verify_groth16_in_bytes_multiple_inputs() { - 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 proof = { - let circuit = Fibonacci::::new(42, a, b); - Groth16::::create_random_proof_with_reduction(circuit, ¶ms, &mut rng) - .unwrap() - }; - - // Proof::write serializes uncompressed and also adds a length to each element, so we serialize - // each individual element here to avoid that. - let mut proof_bytes = Vec::new(); - proof.a.serialize_compressed(&mut proof_bytes).unwrap(); - proof.b.serialize_compressed(&mut proof_bytes).unwrap(); - proof.c.serialize_compressed(&mut 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 pvk = PreparedVerifyingKey::from(&vk); - - let inputs: Vec<_> = vec![from_arkworks_scalar(&a), from_arkworks_scalar(&b)]; - - let proof: Proof = bcs::from_bytes(&proof_bytes).unwrap(); - assert!(pvk.verify(&inputs, &proof).is_ok()); - - let pvk = pvk.serialize_into_parts(); - - // This circuit has two public inputs: - let mut inputs_bytes = Vec::new(); - a.serialize_compressed(&mut inputs_bytes).unwrap(); - b.serialize_compressed(&mut inputs_bytes).unwrap(); - - assert!(verify_groth16_in_bytes( - &pvk[0], - &pvk[1], - &pvk[2], - &pvk[3], - &inputs_bytes, - &proof_bytes - ) - .unwrap()); - - inputs_bytes[0] += 1; - assert!(!verify_groth16_in_bytes( - &pvk[0], - &pvk[1], - &pvk[2], - &pvk[3], - &inputs_bytes, - &proof_bytes - ) - .unwrap()); - } -} diff --git a/fastcrypto-zkp/src/bls12381/conversions.rs b/fastcrypto-zkp/src/bls12381/api/conversions.rs similarity index 93% rename from fastcrypto-zkp/src/bls12381/conversions.rs rename to fastcrypto-zkp/src/bls12381/api/conversions.rs index ff37a99f1e..9d94627f5e 100644 --- a/fastcrypto-zkp/src/bls12381/conversions.rs +++ b/fastcrypto-zkp/src/bls12381/api/conversions.rs @@ -1,5 +1,10 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + use fastcrypto::error::FastCryptoResult; -use fastcrypto::groups::bls12381::{FP_BYTE_LENGTH, GT_ELEMENT_BYTE_LENGTH, GTElement, Scalar, SCALAR_LENGTH}; +use fastcrypto::groups::bls12381::{ + GTElement, Scalar, FP_BYTE_LENGTH, GT_ELEMENT_BYTE_LENGTH, SCALAR_LENGTH, +}; use fastcrypto::serde_helpers::ToFromByteArray; use crate::groth16::api::{FromLittleEndianByteArray, GTSerialize}; @@ -31,7 +36,6 @@ 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; @@ -71,17 +75,20 @@ fn gt_element_to_arkworks(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> [u8; GT_ELEME #[cfg(test)] mod tests { use ark_bls12_381::{Bls12_381, G1Projective, G2Projective}; - use ark_ec::Group; use ark_ec::pairing::PairingOutput; + use ark_ec::Group; 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 crate::bls12381::api::conversions::{arkworks_to_gt_element, gt_element_to_arkworks}; + use crate::groth16::api::GTSerialize; + use fastcrypto::groups::bls12381::{ + G1Element, G2Element, GTElement, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, + GT_ELEMENT_BYTE_LENGTH, + }; 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() { diff --git a/fastcrypto-zkp/src/bls12381/api/mod.rs b/fastcrypto-zkp/src/bls12381/api/mod.rs new file mode 100644 index 0000000000..8ead029502 --- /dev/null +++ b/fastcrypto-zkp/src/bls12381/api/mod.rs @@ -0,0 +1,52 @@ +// Copyright (c) 2022, Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use fastcrypto::error::FastCryptoError; +use fastcrypto::groups::bls12381::{ + G1Element, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, GT_ELEMENT_BYTE_LENGTH, + SCALAR_LENGTH, +}; + +use crate::groth16::api; + +mod conversions; +#[cfg(test)] +mod tests; + +/// Create a prepared verifying key for Groth16 over the BLS12-381 curve construction. See +/// [`api::prepare_pvk_bytes`]. +pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoError> { + api::prepare_pvk_bytes::< + G1Element, + { G1_ELEMENT_BYTE_LENGTH }, + { G2_ELEMENT_BYTE_LENGTH }, + { GT_ELEMENT_BYTE_LENGTH }, + { SCALAR_LENGTH }, + >(vk_bytes) +} + +/// Verify Groth16 proof over the BLS12-381 curve construction. See +/// [`api::verify_groth16_in_bytes`]. +pub fn verify_groth16_in_bytes( + vk_gamma_abc_g1_bytes: &[u8], + alpha_g1_beta_g2_bytes: &[u8], + gamma_g2_neg_pc_bytes: &[u8], + delta_g2_neg_pc_bytes: &[u8], + public_inputs_as_bytes: &[u8], + proof_points_as_bytes: &[u8], +) -> Result { + api::verify_groth16_in_bytes::< + G1Element, + { G1_ELEMENT_BYTE_LENGTH }, + { G2_ELEMENT_BYTE_LENGTH }, + { GT_ELEMENT_BYTE_LENGTH }, + { SCALAR_LENGTH }, + >( + vk_gamma_abc_g1_bytes, + alpha_g1_beta_g2_bytes, + gamma_g2_neg_pc_bytes, + delta_g2_neg_pc_bytes, + public_inputs_as_bytes, + proof_points_as_bytes, + ) +} diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs b/fastcrypto-zkp/src/bls12381/api/tests.rs similarity index 87% rename from fastcrypto-zkp/src/bls12381/unit_tests/api_tests.rs rename to fastcrypto-zkp/src/bls12381/api/tests.rs index 5ba89e3d0f..3407c2c612 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/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::Zero; use ark_groth16::Groth16; @@ -15,7 +13,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_groth16_in_bytes_api() { @@ -115,6 +179,23 @@ fn test_verify_groth16_in_bytes_api() { .is_err()); } +#[test] +fn test_prepare_pvk_bytes_regression() { + // Test vector + let vk_bytes = hex::decode("a84d039ad1ae98eeeee4c8ba9af9b6c5d1cfcb98c3fc92ccfcebd77bcccffa1d170d39da29e9b4aa83b98680cb90bb25946b2b70f9e3565510c5361d5d65cb458a0b3177d612dd340b8f8f8493c2772454e3e8f577a3f77865df851d1a159b800c2ec5bae889029fc419678e83dee900465d60e7ef26f614940e719c6f7c0c7db57464fa0481a93c18d52cb2fbf8dcf0a398b153643614fc1071a54e288edb6402f1d9e00d3408c76d95c16885cc992dff5c6ebee3b739cb22359ab2d126026a1626c43ea7b898a7c1d2904c1bd4bbce5d0b1b16fab8535a52d1b08a5217df2e912ee1b0f4140892afa31d479f78dfbc82ab58a209ad00df6c86ab14841e8daa7a380a6853f28bacf38aad9903b6149fff4b119dea16de8aa3e5050b9d563a01009e061a950c233f66511c8fae2a8c58503059821df7f6defbba8f93d26e412cc07b66a9f3cdd740cce5c8488ce94fc8020000000000000081aabea18713222ac45a6ef3208a09f55ce2dde8a11cc4b12788be2ae77ae318176d631d36d80942df576af651b57a31a95f2e9bcaebbb53a588251634715599f7a7e9d51fe872fe312edf0b39d98f0d7f8b5554f96f759c041ea38b4b1e5e19").unwrap(); + let expected_vk_bytes = hex::decode("81aabea18713222ac45a6ef3208a09f55ce2dde8a11cc4b12788be2ae77ae318176d631d36d80942df576af651b57a31a95f2e9bcaebbb53a588251634715599f7a7e9d51fe872fe312edf0b39d98f0d7f8b5554f96f759c041ea38b4b1e5e19").unwrap(); + let expected_alpha_bytes = hex::decode("097ca8074c7f1d661e25d70fc2e6f14aa874dabe3d8a5d7751a012a737d30b59fc0f5f6d4ce0ea6f6c4562912dfb2a1442df06f9f0b8fc2d834ca007c8620823926b2fc09367d0dfa9b205a216921715e13deedd93580c77cae413cbb83134051cb724633c58759c77e4eda4147a54b03b1f443b68c65247166465105ab5065847ae61ba9d8bdfec536212b0dadedc042dab119d0eeea16349493a4118d481761b1e75f559fbad57c926d599e81d98dde586a2cfcc37b49972e2f9db554e5a0ba56bec2d57a8bfed629ae29c95002e3e943311b7b0d1690d2329e874b179ce5d720bd7c5fb5a2f756b37e3510582cb0c0f8fc8047305fc222c309a5a8234c5ff31a7b311aabdcebf4a43d98b69071a9e5796372146f7199ba05f9ca0a3d14b0c421e7f1bd02ac87b365fd8ce992c0f87994d0ca66f75c72fed0ce94ca174fcb9e5092f0474e07e71e9fd687b3daa441193f264ca2059760faa9c5ca5ef38f6ecefef2ac7d8c47df67b99c36efa64f625fe3f55f40ad1865abbdf2ff4c3fc3a162e28b953f6faec70a6a61c76f4dca1eecc86544b88352994495ae7fc7a77d387880e59b2357d9dd1277ae7f7ee9ba00b440e0e6923dc3971de9050a977db59d767195622f200f2bf0d00e4a986e94a6932627954dd2b7da39b4fcb32c991a0190bdc44562ad83d34e0af7656b51d6cde03530b5d523380653130b87346720ad6dd425d8133ffb02f39a95fc70e9707181ecb168bd8d2d0e9e85e262255fecab15f1ada809ecbefa42a7082fa7326a1d494261a8954fe5b215c5b761fb10b7f18").unwrap(); + let expected_gamma_bytes = hex::decode("8398b153643614fc1071a54e288edb6402f1d9e00d3408c76d95c16885cc992dff5c6ebee3b739cb22359ab2d126026a1626c43ea7b898a7c1d2904c1bd4bbce5d0b1b16fab8535a52d1b08a5217df2e912ee1b0f4140892afa31d479f78dfbc").unwrap(); + let expected_delta_bytes = hex::decode("a2ab58a209ad00df6c86ab14841e8daa7a380a6853f28bacf38aad9903b6149fff4b119dea16de8aa3e5050b9d563a01009e061a950c233f66511c8fae2a8c58503059821df7f6defbba8f93d26e412cc07b66a9f3cdd740cce5c8488ce94fc8").unwrap(); + + let prepared_vk_bytes = prepare_pvk_bytes(&vk_bytes).unwrap(); + + assert_eq!(prepared_vk_bytes[0], expected_vk_bytes); + assert_eq!(prepared_vk_bytes[1], expected_alpha_bytes); + assert_eq!(prepared_vk_bytes[2], expected_gamma_bytes); + assert_eq!(prepared_vk_bytes[3], expected_delta_bytes); +} + #[test] fn test_prepare_pvk_bytes() { const PUBLIC_SIZE: usize = 128; @@ -305,63 +386,3 @@ fn test_prepared_vk_bytes() { assert_eq!(prepared_vk_bytes[2], expected_gamma_bytes); assert_eq!(prepared_vk_bytes[3], expected_delta_bytes); } - -#[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/mod.rs b/fastcrypto-zkp/src/bls12381/mod.rs index 0315a46feb..29cf044aaf 100644 --- a/fastcrypto-zkp/src/bls12381/mod.rs +++ b/fastcrypto-zkp/src/bls12381/mod.rs @@ -5,24 +5,15 @@ //! Groth16 verifier over the BLS12-381 elliptic curve construction. -use crate::groth16; use fastcrypto::groups::bls12381::G1Element; +use crate::groth16; + /// API that takes in serialized inputs pub mod api; #[cfg(test)] -#[path = "unit_tests/verifier_tests.rs"] -mod verifier_tests; - -#[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; +mod test_helpers; /// A prepared Groth16 verifying key in the BLS12-381 construction. pub type PreparedVerifyingKey = groth16::PreparedVerifyingKey; @@ -32,3 +23,42 @@ pub type VerifyingKey = groth16::VerifyingKey; /// A Groth16 proof in the BLS12-381 construction. pub type Proof = groth16::Proof; + +#[cfg(test)] +mod tests { + use std::ops::Mul; + + use ark_bls12_381::{Bls12_381, Fr}; + use ark_groth16::Groth16; + use ark_snark::SNARK; + use ark_std::rand::thread_rng; + use ark_std::UniformRand; + + use crate::bls12381::test_helpers::{ + from_arkworks_proof, from_arkworks_scalar, from_arkworks_vk, + }; + use crate::dummy_circuits::DummyCircuit; + + #[test] + fn test_verify_with_processed_vk() { + const PUBLIC_SIZE: usize = 128; + let rng = &mut thread_rng(); + let c = DummyCircuit:: { + a: Some(::rand(rng)), + b: Some(::rand(rng)), + num_variables: PUBLIC_SIZE, + num_constraints: 256, + }; + + let (pk, vk) = Groth16::::circuit_specific_setup(c, rng).unwrap(); + let ark_proof = Groth16::::prove(&pk, c, rng).unwrap(); + let public_input = c.a.unwrap().mul(c.b.unwrap()); + + let proof = from_arkworks_proof(&ark_proof); + let vk = from_arkworks_vk(&vk); + let prepared_vk = crate::groth16::PreparedVerifyingKey::from(&vk); + let public_inputs = vec![from_arkworks_scalar(&public_input)]; + + assert!(prepared_vk.verify(&public_inputs, &proof).is_ok()); + } +} diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs b/fastcrypto-zkp/src/bls12381/test_helpers.rs similarity index 80% rename from fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs rename to fastcrypto-zkp/src/bls12381/test_helpers.rs index 4e77623d13..3f889dde2b 100644 --- a/fastcrypto-zkp/src/bls12381/unit_tests/test_helpers.rs +++ b/fastcrypto-zkp/src/bls12381/test_helpers.rs @@ -10,18 +10,18 @@ use fastcrypto::serde_helpers::ToFromByteArray; use crate::bls12381::{Proof, VerifyingKey}; -pub(crate) fn from_arkworks_proof(ark_proof: &ark_groth16::Proof) -> Proof { +pub(super) fn from_arkworks_proof(ark_proof: &ark_groth16::Proof) -> Proof { let mut proof_bytes = Vec::new(); ark_proof.serialize_compressed(&mut proof_bytes).unwrap(); bcs::from_bytes(&proof_bytes).unwrap() } -pub(crate) fn from_arkworks_vk(ark_vk: &ark_groth16::VerifyingKey) -> VerifyingKey { +pub(super) fn from_arkworks_vk(ark_vk: &ark_groth16::VerifyingKey) -> VerifyingKey { let mut vk_bytes = Vec::new(); ark_vk.serialize_compressed(&mut vk_bytes).unwrap(); VerifyingKey::from_arkworks_format(&vk_bytes).unwrap() } -pub(crate) fn from_arkworks_scalar(scalar: &Fr) -> Scalar { +pub(super) fn from_arkworks_scalar(scalar: &Fr) -> Scalar { Scalar::from_byte_array(&scalar.into_bigint().to_bytes_be().try_into().unwrap()).unwrap() } diff --git a/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs b/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs deleted file mode 100644 index 05d231ea8d..0000000000 --- a/fastcrypto-zkp/src/bls12381/unit_tests/verifier_tests.rs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use std::ops::Mul; - -use ark_bls12_381::{Bls12_381, Fr}; -use ark_ff::UniformRand; -use ark_groth16::Groth16; -use ark_snark::SNARK; -use ark_std::rand::thread_rng; - -use crate::bls12381::test_helpers::{from_arkworks_proof, from_arkworks_scalar, from_arkworks_vk}; -use crate::dummy_circuits::DummyCircuit; - -#[test] -fn test_verify_with_processed_vk() { - const PUBLIC_SIZE: usize = 128; - let rng = &mut thread_rng(); - let c = DummyCircuit:: { - a: Some(::rand(rng)), - b: Some(::rand(rng)), - num_variables: PUBLIC_SIZE, - num_constraints: 256, - }; - - let (pk, vk) = Groth16::::circuit_specific_setup(c, rng).unwrap(); - let ark_proof = Groth16::::prove(&pk, c, rng).unwrap(); - let public_input = c.a.unwrap().mul(c.b.unwrap()); - - let proof = from_arkworks_proof(&ark_proof); - let vk = from_arkworks_vk(&vk); - let prepared_vk = crate::groth16::PreparedVerifyingKey::from(&vk); - let public_inputs = vec![from_arkworks_scalar(&public_input)]; - - assert!(prepared_vk.verify(&public_inputs, &proof).is_ok()); -} From 5f4e2c0191640f883d44591078bd2157ecca452e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Mon, 15 Apr 2024 12:26:12 +0200 Subject: [PATCH 41/65] Remove trait bound for function argumnet --- fastcrypto-zkp/src/groth16/api.rs | 6 +++--- fastcrypto/src/serde_helpers.rs | 16 ++++------------ 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/fastcrypto-zkp/src/groth16/api.rs b/fastcrypto-zkp/src/groth16/api.rs index a25f852759..f20b8877f8 100644 --- a/fastcrypto-zkp/src/groth16/api.rs +++ b/fastcrypto-zkp/src/groth16/api.rs @@ -53,7 +53,7 @@ where ::Output: GroupElement + GTSerialize, G1::ScalarType: FromLittleEndianByteArray, { - let public_inputs = deserialize_vector::( + let public_inputs = deserialize_vector::( public_inputs_as_bytes, G1::ScalarType::from_little_endian_byte_array, )?; @@ -133,7 +133,7 @@ impl VerifyingKey { } i += 8; - let gamma_abc = deserialize_vector::(&bytes[i..], G1::from_byte_array) + let gamma_abc = deserialize_vector::(&bytes[i..], G1::from_byte_array) .map_err(|_| FastCryptoError::InvalidInput)?; if gamma_abc.len() != n as usize { @@ -186,7 +186,7 @@ impl PreparedVerifyingKey { return Err(FastCryptoError::InvalidInput); } let vk_gamma_abc = - deserialize_vector::(vk_gamma_abc_bytes, G1::from_byte_array)?; + deserialize_vector::(vk_gamma_abc_bytes, G1::from_byte_array)?; let alpha_beta = ::Output::from_arkworks_bytes( alpha_beta_bytes diff --git a/fastcrypto/src/serde_helpers.rs b/fastcrypto/src/serde_helpers.rs index c30e07cc73..692ae85d22 100644 --- a/fastcrypto/src/serde_helpers.rs +++ b/fastcrypto/src/serde_helpers.rs @@ -269,13 +269,9 @@ impl Display for BytesRepresentation { /// Given a byte array of length `N * SIZE_IN_BYTES`, deserialize it into a vector of `N` elements /// of type `T` with the given deserialization function. -pub fn deserialize_vector< - const SIZE_IN_BYTES: usize, - T, - Deserializer: Fn(&[u8; SIZE_IN_BYTES]) -> FastCryptoResult, ->( +pub fn deserialize_vector( bytes: &[u8], - from_byte_array: Deserializer, + from_byte_array: fn(&[u8; SIZE_IN_BYTES]) -> FastCryptoResult, ) -> FastCryptoResult> { if bytes.len() % SIZE_IN_BYTES != 0 { return Err(FastCryptoError::InvalidInput); @@ -294,13 +290,9 @@ pub fn deserialize_vector< /// Serialize a vector of elements of type T into a byte array by simply concatenating their binary /// representations with the given deserialization function. -pub fn serialize_vector< - const SIZE_IN_BYTES: usize, - T, - Serializer: Fn(&T) -> [u8; SIZE_IN_BYTES], ->( +pub fn serialize_vector( elements: &[T], - to_byte_array: Serializer, + to_byte_array: fn(&T) -> [u8; SIZE_IN_BYTES], ) -> Vec { elements.iter().flat_map(to_byte_array).collect() } From cc7409b85c95f5dbf917b5f0d1d35028aa2efaa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Mon, 15 Apr 2024 14:21:47 +0200 Subject: [PATCH 42/65] gt conversion more readable --- .../src/bls12381/api/conversions.rs | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api/conversions.rs b/fastcrypto-zkp/src/bls12381/api/conversions.rs index 9d94627f5e..8d9e6d34c4 100644 --- a/fastcrypto-zkp/src/bls12381/api/conversions.rs +++ b/fastcrypto-zkp/src/bls12381/api/conversions.rs @@ -27,26 +27,33 @@ impl GTSerialize for GTElement { } } -// 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] { - let mut result = [0u8; 12 * FP_BYTE_LENGTH]; +/// Split the input into `TOTAL_SIZE / permutation.len()` chunks, and permute the chunks according +/// to the given permutation. +fn permute_elements( + bytes: &[u8; TOTAL_SIZE], + permutation: &[usize], +) -> [u8; TOTAL_SIZE] { + let elements = permutation.len(); + assert_eq!(TOTAL_SIZE % elements, 0); + let element_size = TOTAL_SIZE / elements; + let mut result = [0u8; TOTAL_SIZE]; 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]); + let from = from * element_size; + let to = to * element_size; + result[to..to + element_size].copy_from_slice(&bytes[from..from + element_size]); } result - .chunks_exact_mut(FP_BYTE_LENGTH) +} + +/// Reverse the endianness of each element in the input array, where each element is `N` bytes long. +fn reverse_endianness_for_elements( + bytes: &mut [u8; TOTAL_SIZE], + element_size: usize, +) { + assert_eq!(TOTAL_SIZE % element_size, 0); + bytes + .chunks_exact_mut(element_size) .for_each(|chunk| chunk.reverse()); - result } /// Given a serialization of a arkworks [`PairingOutput`] element, this function returns a @@ -59,8 +66,9 @@ fn arkworks_to_gt_element(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> [u8; GT_ELEME // PERMUTATION[i + j * 3] = i * 2 + j; // } // } - const PERMUTATION: [usize; 6] = [0, 2, 4, 1, 3, 5]; - permute_elements(bytes, &PERMUTATION) + let mut bytes = permute_elements(bytes, &[0, 2, 4, 1, 3, 5]); + reverse_endianness_for_elements(&mut bytes, FP_BYTE_LENGTH); + bytes } /// Given a serialization of a [`GTElement`], this function returns a serialization of the @@ -68,8 +76,9 @@ fn arkworks_to_gt_element(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> [u8; GT_ELEME /// 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) + let mut bytes = permute_elements(bytes, &[0, 3, 1, 4, 2, 5]); + reverse_endianness_for_elements(&mut bytes, FP_BYTE_LENGTH); + bytes } #[cfg(test)] From 1cd91ed89ca1173ff5ffa2ba27aeaef6eb029dce 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 43/65] Scalar from le-bytes test# --- .../src/bls12381/api/conversions.rs | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api/conversions.rs b/fastcrypto-zkp/src/bls12381/api/conversions.rs index 8d9e6d34c4..2d65d59aa8 100644 --- a/fastcrypto-zkp/src/bls12381/api/conversions.rs +++ b/fastcrypto-zkp/src/bls12381/api/conversions.rs @@ -83,18 +83,19 @@ fn gt_element_to_arkworks(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> [u8; GT_ELEME #[cfg(test)] mod tests { - use ark_bls12_381::{Bls12_381, G1Projective, G2Projective}; + use ark_bls12_381::{Bls12_381, Fr, G1Projective, G2Projective}; use ark_ec::pairing::PairingOutput; use ark_ec::Group; 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::GTSerialize; + use crate::groth16::api::{FromLittleEndianByteArray, GTSerialize}; use fastcrypto::groups::bls12381::{ - G1Element, G2Element, GTElement, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, - GT_ELEMENT_BYTE_LENGTH, + G1Element, G2Element, GTElement, Scalar, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, + GT_ELEMENT_BYTE_LENGTH, SCALAR_LENGTH, }; use fastcrypto::groups::GroupElement; use fastcrypto::serde_helpers::ToFromByteArray; @@ -206,4 +207,20 @@ mod tests { GTElement::from_arkworks_bytes, ); } + + #[test] + fn test_from_le_bytes() { + let x = 12345678u128; + let arkworks_scalar = Fr::from(x); + let mut arkworks_bytes = Vec::new(); + arkworks_scalar + .serialize_compressed(&mut arkworks_bytes) + .unwrap(); + assert_eq!(arkworks_bytes.len(), SCALAR_LENGTH); + assert_eq!(arkworks_bytes[..16], x.to_le_bytes()); + + let scalar = + Scalar::from_little_endian_byte_array(&arkworks_bytes.try_into().unwrap()).unwrap(); + assert_eq!(scalar, Scalar::from(x)); + } } From 2a28d546e02eeff81ad2e237273393fa5c943397 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 44/65] Clean up --- fastcrypto-zkp/src/bls12381/api/conversions.rs | 1 - fastcrypto-zkp/src/bls12381/mod.rs | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) 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}; diff --git a/fastcrypto-zkp/src/bls12381/mod.rs b/fastcrypto-zkp/src/bls12381/mod.rs index 29cf044aaf..d96d7e4c8d 100644 --- a/fastcrypto-zkp/src/bls12381/mod.rs +++ b/fastcrypto-zkp/src/bls12381/mod.rs @@ -28,6 +28,7 @@ pub type Proof = groth16::Proof; mod tests { use std::ops::Mul; + use crate::bls12381::PreparedVerifyingKey; use ark_bls12_381::{Bls12_381, Fr}; use ark_groth16::Groth16; use ark_snark::SNARK; @@ -56,7 +57,7 @@ mod tests { let proof = from_arkworks_proof(&ark_proof); let vk = from_arkworks_vk(&vk); - let prepared_vk = crate::groth16::PreparedVerifyingKey::from(&vk); + let prepared_vk = PreparedVerifyingKey::from(&vk); let public_inputs = vec![from_arkworks_scalar(&public_input)]; assert!(prepared_vk.verify(&public_inputs, &proof).is_ok()); From 9529c95c00754cd080f4966543ec09d58f662e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Mon, 15 Apr 2024 15:06:25 +0200 Subject: [PATCH 45/65] clean up --- fastcrypto-zkp/src/groth16/api.rs | 61 ++++++++++--------------------- 1 file changed, 20 insertions(+), 41 deletions(-) diff --git a/fastcrypto-zkp/src/groth16/api.rs b/fastcrypto-zkp/src/groth16/api.rs index f20b8877f8..6d6711bc10 100644 --- a/fastcrypto-zkp/src/groth16/api.rs +++ b/fastcrypto-zkp/src/groth16/api.rs @@ -2,6 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use serde::de::DeserializeOwned; +use std::io::Read; +use std::mem::size_of; +use tokio::io::AsyncReadExt; use fastcrypto::error::{FastCryptoError, FastCryptoResult}; use fastcrypto::groups::{GroupElement, Pairing}; @@ -53,7 +56,7 @@ where ::Output: GroupElement + GTSerialize, G1::ScalarType: FromLittleEndianByteArray, { - let public_inputs = deserialize_vector::( + let public_inputs = deserialize_vector( public_inputs_as_bytes, G1::ScalarType::from_little_endian_byte_array, )?; @@ -84,59 +87,38 @@ impl VerifyingKey { // - n: u64 lendian (size of gamma_abc) // - gamma_abc: Vec - // We can't use bincode because there, the length of the vector is prefixed as a single byte - // and not a lendian u64. + // We can't use bcs because there, the length of the vector is prefixed as a single byte and + // not a little-endian u64 as it is here. if (bytes.len() - (G1_SIZE + 3 * G2_SIZE + 8)) % G1_SIZE != 0 { return Err(FastCryptoError::InvalidInput); } - let mut i = 0; + let (alpha, bytes) = bytes.split_at(G1_SIZE); + let alpha = G1::from_byte_array(alpha.try_into().expect("The length is correct"))?; - let alpha = G1::from_byte_array( - bytes[i..G1_SIZE] - .try_into() - .map_err(|_| FastCryptoError::InvalidInput)?, - )?; - i += G1_SIZE; + let (beta, bytes) = bytes.split_at(G2_SIZE); + let beta = G1::Other::from_byte_array(beta.try_into().expect("The length is correct"))?; - let beta = G1::Other::from_byte_array( - bytes[i..i + G2_SIZE] - .try_into() - .map_err(|_| FastCryptoError::InvalidInput)?, - )?; - i += G2_SIZE; + let (gamma, bytes) = bytes.split_at(G2_SIZE); + let gamma = G1::Other::from_byte_array(gamma.try_into().expect("The length is correct"))?; - let gamma = G1::Other::from_byte_array( - bytes[i..i + G2_SIZE] - .try_into() - .map_err(|_| FastCryptoError::InvalidInput)?, - )?; - i += G2_SIZE; + let (delta, bytes) = bytes.split_at(G2_SIZE); + let delta = G1::Other::from_byte_array(delta.try_into().expect("The length is correct"))?; - let delta = G1::Other::from_byte_array( - bytes[i..i + G2_SIZE] - .try_into() - .map_err(|_| FastCryptoError::InvalidInput)?, - )?; - i += G2_SIZE; + let (gamma_abc_length, bytes) = bytes.split_at(size_of::()); + let gamma_abc_length = + u64::from_le_bytes(gamma_abc_length.try_into().expect("The length is correct")); - let n = u64::from_le_bytes( - bytes[i..i + 8] - .try_into() - .map_err(|_| FastCryptoError::InvalidInput)?, - ); // There must be at least one element in gamma_abc, since this should be equal to the number // of public inputs + 1. - if n == 0 { + if gamma_abc_length == 0 { return Err(FastCryptoError::InvalidInput); } - i += 8; - let gamma_abc = deserialize_vector::(&bytes[i..], G1::from_byte_array) - .map_err(|_| FastCryptoError::InvalidInput)?; + let gamma_abc = deserialize_vector(&bytes, G1::from_byte_array)?; - if gamma_abc.len() != n as usize { + if gamma_abc.len() != gamma_abc_length as usize { return Err(FastCryptoError::InvalidInput); } @@ -182,9 +164,6 @@ impl PreparedVerifyingKey { G1::Other: ToFromByteArray, ::Output: GTSerialize, { - if vk_gamma_abc_bytes.len() % G1_SIZE != 0 { - return Err(FastCryptoError::InvalidInput); - } let vk_gamma_abc = deserialize_vector::(vk_gamma_abc_bytes, G1::from_byte_array)?; From e9575bc7450e462d43d3f8f2d91077df004d4e43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Mon, 15 Apr 2024 15:12:15 +0200 Subject: [PATCH 46/65] expect comment --- fastcrypto-zkp/src/groth16/api.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fastcrypto-zkp/src/groth16/api.rs b/fastcrypto-zkp/src/groth16/api.rs index 6d6711bc10..882c8cd941 100644 --- a/fastcrypto-zkp/src/groth16/api.rs +++ b/fastcrypto-zkp/src/groth16/api.rs @@ -95,20 +95,20 @@ impl VerifyingKey { } let (alpha, bytes) = bytes.split_at(G1_SIZE); - let alpha = G1::from_byte_array(alpha.try_into().expect("The length is correct"))?; + let alpha = G1::from_byte_array(alpha.try_into().expect("Length already checked"))?; let (beta, bytes) = bytes.split_at(G2_SIZE); - let beta = G1::Other::from_byte_array(beta.try_into().expect("The length is correct"))?; + let beta = G1::Other::from_byte_array(beta.try_into().expect("Length already checked"))?; let (gamma, bytes) = bytes.split_at(G2_SIZE); - let gamma = G1::Other::from_byte_array(gamma.try_into().expect("The length is correct"))?; + let gamma = G1::Other::from_byte_array(gamma.try_into().expect("Length already checked"))?; let (delta, bytes) = bytes.split_at(G2_SIZE); - let delta = G1::Other::from_byte_array(delta.try_into().expect("The length is correct"))?; + let delta = G1::Other::from_byte_array(delta.try_into().expect("Length already checked"))?; let (gamma_abc_length, bytes) = bytes.split_at(size_of::()); let gamma_abc_length = - u64::from_le_bytes(gamma_abc_length.try_into().expect("The length is correct")); + u64::from_le_bytes(gamma_abc_length.try_into().expect("Length already checked")); // There must be at least one element in gamma_abc, since this should be equal to the number // of public inputs + 1. From d7bc906dd203abf7d950cb9a243c3432585eaa88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Mon, 15 Apr 2024 15:25:11 +0200 Subject: [PATCH 47/65] clippy --- fastcrypto-zkp/src/groth16/api.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/fastcrypto-zkp/src/groth16/api.rs b/fastcrypto-zkp/src/groth16/api.rs index 882c8cd941..f6e764bc1c 100644 --- a/fastcrypto-zkp/src/groth16/api.rs +++ b/fastcrypto-zkp/src/groth16/api.rs @@ -1,10 +1,9 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use serde::de::DeserializeOwned; -use std::io::Read; use std::mem::size_of; -use tokio::io::AsyncReadExt; + +use serde::de::DeserializeOwned; use fastcrypto::error::{FastCryptoError, FastCryptoResult}; use fastcrypto::groups::{GroupElement, Pairing}; @@ -116,7 +115,7 @@ impl VerifyingKey { return Err(FastCryptoError::InvalidInput); } - let gamma_abc = deserialize_vector(&bytes, G1::from_byte_array)?; + let gamma_abc = deserialize_vector(bytes, G1::from_byte_array)?; if gamma_abc.len() != gamma_abc_length as usize { return Err(FastCryptoError::InvalidInput); From 0c684d3014fc714aff2d323804e549ea2f83ff1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Mon, 15 Apr 2024 15:27:27 +0200 Subject: [PATCH 48/65] Avoid constant --- fastcrypto-zkp/src/groth16/api.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastcrypto-zkp/src/groth16/api.rs b/fastcrypto-zkp/src/groth16/api.rs index f6e764bc1c..9c5020d135 100644 --- a/fastcrypto-zkp/src/groth16/api.rs +++ b/fastcrypto-zkp/src/groth16/api.rs @@ -89,7 +89,7 @@ impl VerifyingKey { // We can't use bcs because there, the length of the vector is prefixed as a single byte and // not a little-endian u64 as it is here. - if (bytes.len() - (G1_SIZE + 3 * G2_SIZE + 8)) % G1_SIZE != 0 { + if (bytes.len() - (G1_SIZE + 3 * G2_SIZE + size_of::())) % G1_SIZE != 0 { return Err(FastCryptoError::InvalidInput); } From 78110ae802f8a22d0c3576a7d8b18a2e17706ddb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 16 Apr 2024 09:43:58 +0200 Subject: [PATCH 49/65] Use multi-pairing --- fastcrypto-zkp/src/groth16/api.rs | 4 ++-- fastcrypto-zkp/src/groth16/mod.rs | 25 +++++++++++++++---------- fastcrypto/src/groups/bls12381.rs | 22 ++++++++++++++++++++++ fastcrypto/src/groups/mod.rs | 25 +++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/fastcrypto-zkp/src/groth16/api.rs b/fastcrypto-zkp/src/groth16/api.rs index 9c5020d135..65d18ed207 100644 --- a/fastcrypto-zkp/src/groth16/api.rs +++ b/fastcrypto-zkp/src/groth16/api.rs @@ -6,7 +6,7 @@ use std::mem::size_of; use serde::de::DeserializeOwned; use fastcrypto::error::{FastCryptoError, FastCryptoResult}; -use fastcrypto::groups::{GroupElement, Pairing}; +use fastcrypto::groups::{GroupElement, MultiScalarMul, Pairing}; use fastcrypto::serde_helpers::{deserialize_vector, serialize_vector, ToFromByteArray}; use crate::groth16::{PreparedVerifyingKey, VerifyingKey}; @@ -50,7 +50,7 @@ pub fn verify_groth16_in_bytes< proof_points_as_bytes: &[u8], ) -> Result where - G1: ToFromByteArray + DeserializeOwned, + G1: ToFromByteArray + DeserializeOwned + MultiScalarMul, ::Other: ToFromByteArray + DeserializeOwned, ::Output: GroupElement + GTSerialize, G1::ScalarType: FromLittleEndianByteArray, diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index d9b4a89aae..b55734c96c 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -6,7 +6,7 @@ use std::fmt::Debug; use fastcrypto::error::{FastCryptoError, FastCryptoResult}; use serde::Deserialize; -use fastcrypto::groups::{GroupElement, Pairing}; +use fastcrypto::groups::{GroupElement, MultiScalarMul, Pairing}; pub mod api; @@ -77,6 +77,7 @@ impl PreparedVerifyingKey { proof: &Proof, ) -> FastCryptoResult<()> where + G1: MultiScalarMul, ::Output: GroupElement, { let prepared_inputs = self.prepare_inputs(public_inputs)?; @@ -94,10 +95,10 @@ impl PreparedVerifyingKey { where ::Output: GroupElement, { - let lhs = proof.a.pairing(&proof.b) - + prepared_inputs.pairing(&self.gamma_neg) - + proof.c.pairing(&self.delta_neg); - + let lhs = G1::multi_pairing( + &[proof.a, *prepared_inputs, proof.c], + &[proof.b, self.gamma_neg, self.delta_neg], + )?; if lhs == self.alpha_beta { Ok(()) } else { @@ -106,13 +107,17 @@ impl PreparedVerifyingKey { } /// Prepare the public inputs for use in [`verify_with_prepared_inputs`]. - pub fn prepare_inputs(&self, public_inputs: &[G1::ScalarType]) -> FastCryptoResult { + pub fn prepare_inputs(&self, public_inputs: &[G1::ScalarType]) -> FastCryptoResult + where + G1: MultiScalarMul, + { if (public_inputs.len() + 1) != self.vk_gamma_abc.len() { return Err(FastCryptoError::InvalidInput); } - Ok(public_inputs - .iter() - .zip(self.vk_gamma_abc.iter().skip(1)) - .fold(self.vk_gamma_abc[0], |acc, (s, g1)| acc + *g1 * s)) + if public_inputs.is_empty() { + return Ok(self.vk_gamma_abc[0]); + } + G1::multi_scalar_mul(public_inputs, &self.vk_gamma_abc[1..]) + .map(|x| x + self.vk_gamma_abc[0]) } } diff --git a/fastcrypto/src/groups/bls12381.rs b/fastcrypto/src/groups/bls12381.rs index f6c8399062..1651827cf6 100644 --- a/fastcrypto/src/groups/bls12381.rs +++ b/fastcrypto/src/groups/bls12381.rs @@ -231,6 +231,28 @@ impl Pairing for G1Element { } GTElement(res) } + + fn multi_pairing(points_g1: &[Self], points_g2: &[Self::Other]) -> FastCryptoResult<::Output> where ::Output: GroupElement { + if points_g1.len() != points_g2.len() { + return Err(FastCryptoError::InvalidInput); + } else if points_g1.is_empty() { + return Ok(::Output::zero()); + } + + let dst = [0u8; 3]; + let mut blst_pairing = blst::Pairing::new(false, &dst); + for (g1, g2) in points_g1.iter().zip(points_g2.iter()) { + let mut g1_affine = blst_p1_affine::default(); + let mut g2_affine = blst_p2_affine::default(); + unsafe { + blst_p1_to_affine(&mut g1_affine, &g1.0); + blst_p2_to_affine(&mut g2_affine, &g2.0); + } + blst_pairing.raw_aggregate(&g2_affine, &g1_affine); + } + let result = blst_pairing.as_fp12().final_exp(); + Ok(GTElement(result)) + } } impl HashToGroupElement for G1Element { diff --git a/fastcrypto/src/groups/mod.rs b/fastcrypto/src/groups/mod.rs index e561178e9f..e4d0f856d1 100644 --- a/fastcrypto/src/groups/mod.rs +++ b/fastcrypto/src/groups/mod.rs @@ -64,6 +64,31 @@ pub trait Pairing: GroupElement { type Output; fn pairing(&self, other: &Self::Other) -> ::Output; + + /// Multi-pairing operation that computes the sum of pairings of two slices of elements. + fn multi_pairing( + points_g1: &[Self], + points_g2: &[Self::Other], + ) -> FastCryptoResult<::Output> + where + ::Output: GroupElement, + { + if points_g1.len() != points_g2.len() { + return Err(FastCryptoError::InvalidInput); + } + if points_g1.is_empty() { + return Ok(::Output::zero()); + } + Ok(points_g1 + .iter() + .skip(1) + .zip(points_g2.iter().skip(1)) + .map(|(g1, g2)| g1.pairing(g2)) + .fold( + points_g1[0].pairing(&points_g2[0]), + ::Output::add, + )) + } } /// Trait for groups that have a reduction from a random buffer to a group element that is secure From 56978e6eb971e52402455e5196c30563730b31c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 16 Apr 2024 10:08:41 +0200 Subject: [PATCH 50/65] dst --- fastcrypto/src/groups/bls12381.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/fastcrypto/src/groups/bls12381.rs b/fastcrypto/src/groups/bls12381.rs index 1651827cf6..8408fa6979 100644 --- a/fastcrypto/src/groups/bls12381.rs +++ b/fastcrypto/src/groups/bls12381.rs @@ -239,8 +239,7 @@ impl Pairing for G1Element { return Ok(::Output::zero()); } - let dst = [0u8; 3]; - let mut blst_pairing = blst::Pairing::new(false, &dst); + let mut blst_pairing = blst::Pairing::new(false, &[]); for (g1, g2) in points_g1.iter().zip(points_g2.iter()) { let mut g1_affine = blst_p1_affine::default(); let mut g2_affine = blst_p2_affine::default(); From 5c071a0175787202db1a4a748d46e9927eaebe1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 16 Apr 2024 10:16:05 +0200 Subject: [PATCH 51/65] fmt --- fastcrypto/src/groups/bls12381.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fastcrypto/src/groups/bls12381.rs b/fastcrypto/src/groups/bls12381.rs index 8408fa6979..61bc9cde1f 100644 --- a/fastcrypto/src/groups/bls12381.rs +++ b/fastcrypto/src/groups/bls12381.rs @@ -232,7 +232,13 @@ impl Pairing for G1Element { GTElement(res) } - fn multi_pairing(points_g1: &[Self], points_g2: &[Self::Other]) -> FastCryptoResult<::Output> where ::Output: GroupElement { + fn multi_pairing( + points_g1: &[Self], + points_g2: &[Self::Other], + ) -> FastCryptoResult<::Output> + where + ::Output: GroupElement, + { if points_g1.len() != points_g2.len() { return Err(FastCryptoError::InvalidInput); } else if points_g1.is_empty() { From c276dd3e60194eba988145d8e10ef5da3c683c35 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 52/65] 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 edae659bd0..f80daa51f7 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 caeda1a86907e5ce83e3d7f71a7b0519a44d6cd5 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 53/65] multi-pairing tests --- fastcrypto/src/groups/bls12381.rs | 11 +++++- fastcrypto/src/tests/bls12381_group_tests.rs | 40 +++++++++++++++----- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/fastcrypto/src/groups/bls12381.rs b/fastcrypto/src/groups/bls12381.rs index 61bc9cde1f..50318b0e70 100644 --- a/fastcrypto/src/groups/bls12381.rs +++ b/fastcrypto/src/groups/bls12381.rs @@ -241,7 +241,16 @@ impl Pairing for G1Element { { if points_g1.len() != points_g2.len() { return Err(FastCryptoError::InvalidInput); - } else if points_g1.is_empty() { + } + + let (points_g1, points_g2): (Vec<_>, Vec<_>) = points_g1 + .iter() + .zip(points_g2.iter()) + .filter(|(&g1, &g2)| g1 != G1Element::zero() && g2 != G2Element::zero()) + .map(|(&g1, &g2)| (g1, g2)) + .unzip(); + + if points_g1.is_empty() { return Ok(::Output::zero()); } diff --git a/fastcrypto/src/tests/bls12381_group_tests.rs b/fastcrypto/src/tests/bls12381_group_tests.rs index f80daa51f7..e4f494c220 100644 --- a/fastcrypto/src/tests/bls12381_group_tests.rs +++ b/fastcrypto/src/tests/bls12381_group_tests.rs @@ -329,6 +329,25 @@ fn test_pairing_and_hash_to_curve() { let sig2 = e2 * sk2; assert_eq!(pk2.pairing(&e2), G1Element::generator().pairing(&sig2)); + assert_eq!( + G1Element::zero().pairing(&G2Element::zero()), + GTElement::zero() + ); + assert_eq!( + G1Element::zero().pairing(&G2Element::generator()), + GTElement::zero() + ); + assert_eq!( + G1Element::generator().pairing(&G2Element::zero()), + GTElement::zero() + ); + + // next should not fail + let _ = G1Element::hash_to_group_element(&[]); + let _ = G2Element::hash_to_group_element(&[]); + let _ = G1Element::hash_to_group_element(&[1]); + let _ = G2Element::hash_to_group_element(&[1]); + // Test multi-pairing assert!(G1Element::multi_pairing(&[], &[pk1]).is_err()); assert_eq!( @@ -343,25 +362,26 @@ fn test_pairing_and_hash_to_curve() { G1Element::multi_pairing(&[e1, pk2], &[pk1, e2]).unwrap(), e1.pairing(&pk1) + pk2.pairing(&e2) ); - assert_eq!( - G1Element::zero().pairing(&G2Element::zero()), + G1Element::multi_pairing(&[G1Element::zero()], &[G2Element::zero()]).unwrap(), GTElement::zero() ); assert_eq!( - G1Element::zero().pairing(&G2Element::generator()), + G1Element::multi_pairing( + &[G1Element::zero(), G1Element::zero()], + &[G2Element::zero(), G2Element::zero()] + ) + .unwrap(), GTElement::zero() ); assert_eq!( - G1Element::generator().pairing(&G2Element::zero()), + G1Element::multi_pairing(&[G1Element::generator()], &[G2Element::zero()]).unwrap(), + GTElement::zero() + ); + assert_eq!( + G1Element::multi_pairing(&[G1Element::zero()], &[G2Element::generator()]).unwrap(), GTElement::zero() ); - - // next should not fail - let _ = G1Element::hash_to_group_element(&[]); - let _ = G2Element::hash_to_group_element(&[]); - let _ = G1Element::hash_to_group_element(&[1]); - let _ = G2Element::hash_to_group_element(&[1]); } #[test] From 02a22444ca04efe0dbbbc03cee3592bf39578454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 30 Apr 2024 10:48:05 +0200 Subject: [PATCH 54/65] Clean up after rebase --- fastcrypto-zkp/src/bls12381/api/tests.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api/tests.rs b/fastcrypto-zkp/src/bls12381/api/tests.rs index 3407c2c612..d5849c8a20 100644 --- a/fastcrypto-zkp/src/bls12381/api/tests.rs +++ b/fastcrypto-zkp/src/bls12381/api/tests.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use ark_bls12_381::{Bls12_381, Fq12, Fr, G1Affine}; -use ark_ff::Zero; +use ark_ff::{One, Zero}; use ark_groth16::Groth16; use ark_serialize::CanonicalSerialize; use ark_snark::SNARK; @@ -17,7 +17,7 @@ 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 fastcrypto::groups::bls12381::{G1Element, G2Element}; use std::ops::Mul; @@ -275,24 +275,24 @@ fn api_regression_tests() { // Expects two public inputs, so the vk needs three elements here vk_gamma_abc_g1_bytes - .extend_from_slice(&fastcrypto::groups::bls12381::G1Element::zero().to_byte_array()); + .extend_from_slice(&G1Element::zero().to_byte_array()); vk_gamma_abc_g1_bytes - .extend_from_slice(&fastcrypto::groups::bls12381::G1Element::zero().to_byte_array()); + .extend_from_slice(&G1Element::zero().to_byte_array()); // The API expects serialization like in Arkworks for GT elements let mut alpha_g1_beta_g2_bytes = vec![]; - Fq12::zero() + Fq12::one() .serialize_compressed(&mut alpha_g1_beta_g2_bytes) .unwrap(); - let gamma_g2_neg_pc_bytes = fastcrypto::groups::bls12381::G2Element::zero().to_byte_array(); - let delta_g2_neg_pc_bytes = fastcrypto::groups::bls12381::G2Element::zero().to_byte_array(); + let gamma_g2_neg_pc_bytes = G2Element::zero().to_byte_array(); + let delta_g2_neg_pc_bytes = G2Element::zero().to_byte_array(); let public_inputs_bytes = hex::decode("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(); let mut proof_bytes = vec![]; - proof_bytes.extend_from_slice(&fastcrypto::groups::bls12381::G1Element::zero().to_byte_array()); - proof_bytes.extend_from_slice(&fastcrypto::groups::bls12381::G2Element::zero().to_byte_array()); - proof_bytes.extend_from_slice(&fastcrypto::groups::bls12381::G1Element::zero().to_byte_array()); + proof_bytes.extend_from_slice(&G1Element::zero().to_byte_array()); + proof_bytes.extend_from_slice(&G2Element::zero().to_byte_array()); + proof_bytes.extend_from_slice(&G1Element::zero().to_byte_array()); // The trivial proof should pass verification assert!(verify_groth16_in_bytes( From 1a477c2000a12b22e9ab4688b1c4df84a1e02fb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 30 Apr 2024 10:50:03 +0200 Subject: [PATCH 55/65] fmt --- fastcrypto-zkp/src/bls12381/api/tests.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api/tests.rs b/fastcrypto-zkp/src/bls12381/api/tests.rs index d5849c8a20..0b07dab651 100644 --- a/fastcrypto-zkp/src/bls12381/api/tests.rs +++ b/fastcrypto-zkp/src/bls12381/api/tests.rs @@ -274,10 +274,8 @@ fn api_regression_tests() { .to_vec(); // Expects two public inputs, so the vk needs three elements here - vk_gamma_abc_g1_bytes - .extend_from_slice(&G1Element::zero().to_byte_array()); - vk_gamma_abc_g1_bytes - .extend_from_slice(&G1Element::zero().to_byte_array()); + vk_gamma_abc_g1_bytes.extend_from_slice(&G1Element::zero().to_byte_array()); + vk_gamma_abc_g1_bytes.extend_from_slice(&G1Element::zero().to_byte_array()); // The API expects serialization like in Arkworks for GT elements let mut alpha_g1_beta_g2_bytes = vec![]; From ab2f5d333f535754af614c52fea7bcba62c4cad7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 30 Apr 2024 10:50:52 +0200 Subject: [PATCH 56/65] clippy --- fastcrypto-zkp/src/bls12381/api/tests.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api/tests.rs b/fastcrypto-zkp/src/bls12381/api/tests.rs index 0b07dab651..6a0035db6d 100644 --- a/fastcrypto-zkp/src/bls12381/api/tests.rs +++ b/fastcrypto-zkp/src/bls12381/api/tests.rs @@ -1,25 +1,25 @@ // 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, Zero}; +use ark_ff::One; use ark_groth16::Groth16; use ark_serialize::CanonicalSerialize; use ark_snark::SNARK; use ark_std::rand::thread_rng; use ark_std::UniformRand; +use fastcrypto::groups::bls12381::{G1Element, G2Element}; use fastcrypto::groups::GroupElement; use fastcrypto::serde_helpers::ToFromByteArray; +use crate::bls12381::{PreparedVerifyingKey, VerifyingKey}; 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 fastcrypto::groups::bls12381::{G1Element, G2Element}; - -use std::ops::Mul; #[test] fn test_verify() { From 0387cd00b436860ae6b08e4d13d3352f67624543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 30 Apr 2024 10:51:39 +0200 Subject: [PATCH 57/65] fmt --- fastcrypto-zkp/src/bls12381/api/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fastcrypto-zkp/src/bls12381/api/tests.rs b/fastcrypto-zkp/src/bls12381/api/tests.rs index 6a0035db6d..d2675c330f 100644 --- a/fastcrypto-zkp/src/bls12381/api/tests.rs +++ b/fastcrypto-zkp/src/bls12381/api/tests.rs @@ -15,9 +15,9 @@ use fastcrypto::groups::bls12381::{G1Element, G2Element}; use fastcrypto::groups::GroupElement; use fastcrypto::serde_helpers::ToFromByteArray; -use crate::bls12381::{PreparedVerifyingKey, VerifyingKey}; 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; From efb0b6adb4503e2626b7aad906343b96d0922dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 30 Apr 2024 10:54:34 +0200 Subject: [PATCH 58/65] redundant test --- fastcrypto-zkp/src/bls12381/api/tests.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api/tests.rs b/fastcrypto-zkp/src/bls12381/api/tests.rs index d2675c330f..b856715b28 100644 --- a/fastcrypto-zkp/src/bls12381/api/tests.rs +++ b/fastcrypto-zkp/src/bls12381/api/tests.rs @@ -367,20 +367,3 @@ fn test_verify_groth16_in_bytes_multiple_inputs() { ) .unwrap()); } - -#[test] -fn test_prepared_vk_bytes() { - // Test vector - let vk_bytes = hex::decode("a84d039ad1ae98eeeee4c8ba9af9b6c5d1cfcb98c3fc92ccfcebd77bcccffa1d170d39da29e9b4aa83b98680cb90bb25946b2b70f9e3565510c5361d5d65cb458a0b3177d612dd340b8f8f8493c2772454e3e8f577a3f77865df851d1a159b800c2ec5bae889029fc419678e83dee900465d60e7ef26f614940e719c6f7c0c7db57464fa0481a93c18d52cb2fbf8dcf0a398b153643614fc1071a54e288edb6402f1d9e00d3408c76d95c16885cc992dff5c6ebee3b739cb22359ab2d126026a1626c43ea7b898a7c1d2904c1bd4bbce5d0b1b16fab8535a52d1b08a5217df2e912ee1b0f4140892afa31d479f78dfbc82ab58a209ad00df6c86ab14841e8daa7a380a6853f28bacf38aad9903b6149fff4b119dea16de8aa3e5050b9d563a01009e061a950c233f66511c8fae2a8c58503059821df7f6defbba8f93d26e412cc07b66a9f3cdd740cce5c8488ce94fc8020000000000000081aabea18713222ac45a6ef3208a09f55ce2dde8a11cc4b12788be2ae77ae318176d631d36d80942df576af651b57a31a95f2e9bcaebbb53a588251634715599f7a7e9d51fe872fe312edf0b39d98f0d7f8b5554f96f759c041ea38b4b1e5e19").unwrap(); - let expected_vk_bytes = hex::decode("81aabea18713222ac45a6ef3208a09f55ce2dde8a11cc4b12788be2ae77ae318176d631d36d80942df576af651b57a31a95f2e9bcaebbb53a588251634715599f7a7e9d51fe872fe312edf0b39d98f0d7f8b5554f96f759c041ea38b4b1e5e19").unwrap(); - let expected_alpha_bytes = hex::decode("097ca8074c7f1d661e25d70fc2e6f14aa874dabe3d8a5d7751a012a737d30b59fc0f5f6d4ce0ea6f6c4562912dfb2a1442df06f9f0b8fc2d834ca007c8620823926b2fc09367d0dfa9b205a216921715e13deedd93580c77cae413cbb83134051cb724633c58759c77e4eda4147a54b03b1f443b68c65247166465105ab5065847ae61ba9d8bdfec536212b0dadedc042dab119d0eeea16349493a4118d481761b1e75f559fbad57c926d599e81d98dde586a2cfcc37b49972e2f9db554e5a0ba56bec2d57a8bfed629ae29c95002e3e943311b7b0d1690d2329e874b179ce5d720bd7c5fb5a2f756b37e3510582cb0c0f8fc8047305fc222c309a5a8234c5ff31a7b311aabdcebf4a43d98b69071a9e5796372146f7199ba05f9ca0a3d14b0c421e7f1bd02ac87b365fd8ce992c0f87994d0ca66f75c72fed0ce94ca174fcb9e5092f0474e07e71e9fd687b3daa441193f264ca2059760faa9c5ca5ef38f6ecefef2ac7d8c47df67b99c36efa64f625fe3f55f40ad1865abbdf2ff4c3fc3a162e28b953f6faec70a6a61c76f4dca1eecc86544b88352994495ae7fc7a77d387880e59b2357d9dd1277ae7f7ee9ba00b440e0e6923dc3971de9050a977db59d767195622f200f2bf0d00e4a986e94a6932627954dd2b7da39b4fcb32c991a0190bdc44562ad83d34e0af7656b51d6cde03530b5d523380653130b87346720ad6dd425d8133ffb02f39a95fc70e9707181ecb168bd8d2d0e9e85e262255fecab15f1ada809ecbefa42a7082fa7326a1d494261a8954fe5b215c5b761fb10b7f18").unwrap(); - let expected_gamma_bytes = hex::decode("8398b153643614fc1071a54e288edb6402f1d9e00d3408c76d95c16885cc992dff5c6ebee3b739cb22359ab2d126026a1626c43ea7b898a7c1d2904c1bd4bbce5d0b1b16fab8535a52d1b08a5217df2e912ee1b0f4140892afa31d479f78dfbc").unwrap(); - let expected_delta_bytes = hex::decode("a2ab58a209ad00df6c86ab14841e8daa7a380a6853f28bacf38aad9903b6149fff4b119dea16de8aa3e5050b9d563a01009e061a950c233f66511c8fae2a8c58503059821df7f6defbba8f93d26e412cc07b66a9f3cdd740cce5c8488ce94fc8").unwrap(); - - let prepared_vk_bytes = prepare_pvk_bytes(&vk_bytes).unwrap(); - - assert_eq!(prepared_vk_bytes[0], expected_vk_bytes); - assert_eq!(prepared_vk_bytes[1], expected_alpha_bytes); - assert_eq!(prepared_vk_bytes[2], expected_gamma_bytes); - assert_eq!(prepared_vk_bytes[3], expected_delta_bytes); -} From 56c1a8c7c042b790fa18dd72c520296aa660cb01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Tue, 30 Apr 2024 10:55:54 +0200 Subject: [PATCH 59/65] Keep test order --- fastcrypto-zkp/src/bls12381/api/tests.rs | 128 +++++++++++------------ 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/fastcrypto-zkp/src/bls12381/api/tests.rs b/fastcrypto-zkp/src/bls12381/api/tests.rs index b856715b28..372204948a 100644 --- a/fastcrypto-zkp/src/bls12381/api/tests.rs +++ b/fastcrypto-zkp/src/bls12381/api/tests.rs @@ -221,6 +221,70 @@ fn test_prepare_pvk_bytes() { assert!(prepare_pvk_bytes(&modified_bytes).is_err()); } +#[test] +fn test_verify_groth16_in_bytes_multiple_inputs() { + 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 proof = { + let circuit = Fibonacci::::new(42, a, b); + Groth16::::create_random_proof_with_reduction(circuit, ¶ms, &mut rng) + .unwrap() + }; + + // Proof::write serializes uncompressed and also adds a length to each element, so we serialize + // each individual element here to avoid that. + let mut proof_bytes = Vec::new(); + proof.a.serialize_compressed(&mut proof_bytes).unwrap(); + proof.b.serialize_compressed(&mut proof_bytes).unwrap(); + proof.c.serialize_compressed(&mut 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 pvk = PreparedVerifyingKey::from(&vk); + + let inputs: Vec<_> = vec![from_arkworks_scalar(&a), from_arkworks_scalar(&b)]; + + let proof: Proof = bcs::from_bytes(&proof_bytes).unwrap(); + assert!(pvk.verify(&inputs, &proof).is_ok()); + + let pvk = pvk.serialize_into_parts(); + + // This circuit has two public inputs: + let mut inputs_bytes = Vec::new(); + a.serialize_compressed(&mut inputs_bytes).unwrap(); + b.serialize_compressed(&mut inputs_bytes).unwrap(); + + assert!(verify_groth16_in_bytes( + &pvk[0], + &pvk[1], + &pvk[2], + &pvk[3], + &inputs_bytes, + &proof_bytes + ) + .unwrap()); + + inputs_bytes[0] += 1; + assert!(!verify_groth16_in_bytes( + &pvk[0], + &pvk[1], + &pvk[2], + &pvk[3], + &inputs_bytes, + &proof_bytes + ) + .unwrap()); +} + #[test] fn api_regression_tests() { // Prepare VK @@ -303,67 +367,3 @@ fn api_regression_tests() { ) .unwrap()); } - -#[test] -fn test_verify_groth16_in_bytes_multiple_inputs() { - 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 proof = { - let circuit = Fibonacci::::new(42, a, b); - Groth16::::create_random_proof_with_reduction(circuit, ¶ms, &mut rng) - .unwrap() - }; - - // Proof::write serializes uncompressed and also adds a length to each element, so we serialize - // each individual element here to avoid that. - let mut proof_bytes = Vec::new(); - proof.a.serialize_compressed(&mut proof_bytes).unwrap(); - proof.b.serialize_compressed(&mut proof_bytes).unwrap(); - proof.c.serialize_compressed(&mut 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 pvk = PreparedVerifyingKey::from(&vk); - - let inputs: Vec<_> = vec![from_arkworks_scalar(&a), from_arkworks_scalar(&b)]; - - let proof: Proof = bcs::from_bytes(&proof_bytes).unwrap(); - assert!(pvk.verify(&inputs, &proof).is_ok()); - - let pvk = pvk.serialize_into_parts(); - - // This circuit has two public inputs: - let mut inputs_bytes = Vec::new(); - a.serialize_compressed(&mut inputs_bytes).unwrap(); - b.serialize_compressed(&mut inputs_bytes).unwrap(); - - assert!(verify_groth16_in_bytes( - &pvk[0], - &pvk[1], - &pvk[2], - &pvk[3], - &inputs_bytes, - &proof_bytes - ) - .unwrap()); - - inputs_bytes[0] += 1; - assert!(!verify_groth16_in_bytes( - &pvk[0], - &pvk[1], - &pvk[2], - &pvk[3], - &inputs_bytes, - &proof_bytes - ) - .unwrap()); -} From d2e37c5b1416657c5a2302ded03c844c9df60596 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 60/65] 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 92c489836f..c0fdf0fda0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1365,6 +1365,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 ff452b5be3..caff4a584a 100644 --- a/fastcrypto/Cargo.toml +++ b/fastcrypto/Cargo.toml @@ -46,6 +46,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 ed151ad9674029058c0960e35ee17f67fdb39dd6 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 61/65] 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 f82b1def0ada1b8ee4945f7be6df734e4a3072f5 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 62/65] 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 710aa7b1d20075b27b79c03c47eddde73f832fce 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 63/65] 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 85426aff101fa1a8b4950a2acf56aeeaeaab84a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Wed, 1 May 2024 14:07:56 +0200 Subject: [PATCH 64/65] Use generic groth16 for bn254 --- fastcrypto-zkp/benches/proving.rs | 12 +- fastcrypto-zkp/benches/utils.rs | 17 +- fastcrypto-zkp/src/bn254/api.rs | 57 +++-- fastcrypto-zkp/src/bn254/mod.rs | 65 ++---- .../src/bn254/unit_tests/api_tests.rs | 35 +-- fastcrypto-zkp/src/bn254/unit_tests/utils.rs | 3 +- fastcrypto-zkp/src/bn254/verifier.rs | 214 ------------------ fastcrypto-zkp/src/groth16/mod.rs | 24 ++ fastcrypto/src/groups/bn254/g1.rs | 19 +- fastcrypto/src/groups/bn254/mod.rs | 10 +- 10 files changed, 136 insertions(+), 320 deletions(-) delete mode 100644 fastcrypto-zkp/src/bn254/verifier.rs diff --git a/fastcrypto-zkp/benches/proving.rs b/fastcrypto-zkp/benches/proving.rs index 874cbf0463..feb3fa8b7a 100644 --- a/fastcrypto-zkp/benches/proving.rs +++ b/fastcrypto-zkp/benches/proving.rs @@ -21,6 +21,7 @@ use fastcrypto::serde_helpers::ToFromByteArray; use fastcrypto_zkp::bn254; use fastcrypto_zkp::dummy_circuits::DummyCircuit; use fastcrypto_zkp::groth16::{Proof, VerifyingKey}; +use crate::utils::vk_from_arkworks; #[path = "./utils.rs"] mod utils; @@ -240,7 +241,7 @@ fn bench_verify_elusiv_circuit(grp: &mut BenchmarkGroup) { ), ]; - let vk: bn254::VerifyingKey = ark_groth16::VerifyingKey { + let vk: bn254::VerifyingKey = vk_from_arkworks(ark_groth16::VerifyingKey { alpha_g1: utils::G1Affine_from_str_projective(( "8057073471822347335074195152835286348058235024870127707965681971765888348219", "14493022634743109860560137600871299171677470588934003383462482807829968516757", @@ -359,8 +360,7 @@ fn bench_verify_elusiv_circuit(grp: &mut BenchmarkGroup) { .into_iter() .map(|s| utils::G1Affine_from_str_projective((s[0], s[1], s[2]))) .collect(), - } - .into(); + }); grp.bench_with_input( BenchmarkId::new( @@ -369,12 +369,12 @@ fn bench_verify_elusiv_circuit(grp: &mut BenchmarkGroup) { ), &vk, |b, vk| { - b.iter(|| bn254::verifier::PreparedVerifyingKey::from(vk)); + b.iter(|| bn254::PreparedVerifyingKey::from(vk)); }, ); - let pvk = bn254::verifier::PreparedVerifyingKey::from(&vk); - let bytes = pvk.serialize().unwrap(); + let pvk = bn254::PreparedVerifyingKey::from(&vk); + let bytes = pvk.serialize_into_parts(); let vk_gamma_abc_g1_bytes = &bytes[0]; let alpha_g1_beta_g2_bytes = &bytes[1]; let gamma_g2_neg_pc_bytes = &bytes[2]; diff --git a/fastcrypto-zkp/benches/utils.rs b/fastcrypto-zkp/benches/utils.rs index 9f8414d313..2e6847617a 100644 --- a/fastcrypto-zkp/benches/utils.rs +++ b/fastcrypto-zkp/benches/utils.rs @@ -1,11 +1,25 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use ark_bn254::{Fq, Fq2, G1Affine, G1Projective, G2Affine}; +use ark_bn254::{Bn254, Fq, Fq2, G1Affine, G1Projective, G2Affine, G2Projective}; +use fastcrypto_zkp::bn254::VerifyingKey; type StrPair = (&'static str, &'static str); type StrTriplet = (&'static str, &'static str, &'static str); +pub fn vk_from_arkworks(vk: ark_groth16::VerifyingKey) -> VerifyingKey { + VerifyingKey::new( + G1Projective::from(vk.alpha_g1).into(), + G2Projective::from(vk.beta_g2).into(), + G2Projective::from(vk.gamma_g2).into(), + G2Projective::from(vk.delta_g2).into(), + vk.gamma_abc_g1 + .iter() + .map(|x| G1Projective::from(*x).into()) + .collect(), + ) +} + #[allow(non_snake_case)] pub fn G1Affine_from_str_projective(#[allow(clippy::type_complexity)] s: StrTriplet) -> G1Affine { G1Projective::new( @@ -18,7 +32,6 @@ pub fn G1Affine_from_str_projective(#[allow(clippy::type_complexity)] s: StrTrip #[allow(non_snake_case)] pub fn G2Affine_from_str_projective(s: (StrPair, StrPair, StrPair)) -> G2Affine { - use ark_bn254::G2Projective; G2Projective::new( Fq2::new(s.0 .0.parse::().unwrap(), s.0 .1.parse::().unwrap()), Fq2::new(s.1 .0.parse::().unwrap(), s.1 .1.parse::().unwrap()), diff --git a/fastcrypto-zkp/src/bn254/api.rs b/fastcrypto-zkp/src/bn254/api.rs index 621a895396..3c0bf410b8 100644 --- a/fastcrypto-zkp/src/bn254/api.rs +++ b/fastcrypto-zkp/src/bn254/api.rs @@ -1,9 +1,12 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use crate::bn254::verifier::PreparedVerifyingKey; -use crate::bn254::{FieldElement, Proof, VerifyingKey}; +use crate::groth16::api; use fastcrypto::error::FastCryptoError; +use fastcrypto::groups::bn254::{ + G1Element, G1_ELEMENT_BYTE_LENGTH, G2_ELEMENT_BYTE_LENGTH, GT_ELEMENT_BYTE_LENGTH, + SCALAR_LENGTH, +}; #[cfg(test)] #[path = "unit_tests/api_tests.rs"] @@ -11,46 +14,40 @@ mod api_tests; /// Size of scalars in the BN254 construction. pub const SCALAR_SIZE: usize = 32; - -/// Deserialize bytes as an Arkwork representation of a verifying key, and return a vector of the -/// four components of a prepared verified key (see more at [`PreparedVerifyingKey`]). +/// Create a prepared verifying key for Groth16 over the BLS12-381 curve construction. See +/// [`api::prepare_pvk_bytes`]. pub fn prepare_pvk_bytes(vk_bytes: &[u8]) -> Result>, FastCryptoError> { - PreparedVerifyingKey::from(&VerifyingKey::deserialize(vk_bytes)?).serialize() + api::prepare_pvk_bytes::< + G1Element, + { G1_ELEMENT_BYTE_LENGTH }, + { G2_ELEMENT_BYTE_LENGTH }, + { GT_ELEMENT_BYTE_LENGTH }, + { SCALAR_LENGTH }, + >(vk_bytes) } -/// Verify Groth16 proof using the serialized form of the prepared verifying key (see more at -/// [`crate::bn254::verifier::PreparedVerifyingKey`]), serialized proof public input and serialized -/// proof points. +/// Verify Groth16 proof over the BLS12-381 curve construction. See +/// [`api::verify_groth16_in_bytes`]. pub fn verify_groth16_in_bytes( vk_gamma_abc_g1_bytes: &[u8], alpha_g1_beta_g2_bytes: &[u8], gamma_g2_neg_pc_bytes: &[u8], delta_g2_neg_pc_bytes: &[u8], - proof_public_inputs_as_bytes: &[u8], + public_inputs_as_bytes: &[u8], proof_points_as_bytes: &[u8], ) -> Result { - if proof_public_inputs_as_bytes.len() % SCALAR_SIZE != 0 { - return Err(FastCryptoError::InputLengthWrong(SCALAR_SIZE)); - } - - let pvk = PreparedVerifyingKey::deserialize(&vec![ + api::verify_groth16_in_bytes::< + G1Element, + { G1_ELEMENT_BYTE_LENGTH }, + { G2_ELEMENT_BYTE_LENGTH }, + { GT_ELEMENT_BYTE_LENGTH }, + { SCALAR_LENGTH }, + >( vk_gamma_abc_g1_bytes, alpha_g1_beta_g2_bytes, gamma_g2_neg_pc_bytes, delta_g2_neg_pc_bytes, - ])?; - - verify_groth16(&pvk, proof_public_inputs_as_bytes, proof_points_as_bytes) -} - -/// Verify proof with a given verifying key in [struct PreparedVerifyingKey], serialized public inputs -/// and serialized proof points. -pub fn verify_groth16( - pvk: &PreparedVerifyingKey, - proof_public_inputs_as_bytes: &[u8], - proof_points_as_bytes: &[u8], -) -> Result { - let proof = Proof::deserialize(proof_points_as_bytes)?; - let public_inputs = FieldElement::deserialize_vector(proof_public_inputs_as_bytes)?; - pvk.verify(&public_inputs, &proof) + public_inputs_as_bytes, + proof_points_as_bytes, + ) } diff --git a/fastcrypto-zkp/src/bn254/mod.rs b/fastcrypto-zkp/src/bn254/mod.rs index 5fddd8df1e..b09b51e71d 100644 --- a/fastcrypto-zkp/src/bn254/mod.rs +++ b/fastcrypto-zkp/src/bn254/mod.rs @@ -4,18 +4,16 @@ #![deny(unused_must_use, missing_debug_implementations)] //! Groth16 verifier over the BN254 elliptic curve construction. -use crate::bn254::api::SCALAR_SIZE; -use ark_bn254::{Bn254, Fr}; -use ark_serialize::CanonicalDeserialize; -use derive_more::From; -use fastcrypto::error::{FastCryptoError, FastCryptoResult}; +use crate::groth16; +use crate::groth16::api::{FromLittleEndianByteArray, GTSerialize}; +use fastcrypto::error::FastCryptoResult; +use fastcrypto::groups::bn254::G1Element; +use fastcrypto::groups::bn254::{GTElement, Scalar, GT_ELEMENT_BYTE_LENGTH, SCALAR_LENGTH}; +use fastcrypto::serde_helpers::ToFromByteArray; /// API that takes in serialized inputs pub mod api; -/// Groth16 SNARK verifier -pub mod verifier; - /// Poseidon hash function over BN254 pub mod poseidon; @@ -28,50 +26,27 @@ pub mod zk_login_api; /// Zk login utils pub mod utils; -/// A field element in the BN254 construction. Thin wrapper around `api::Bn254Fr`. -#[derive(Debug, From)] -pub struct FieldElement(pub(crate) ark_bn254::Fr); +/// A prepared Groth16 verifying key in the BN254 construction. +pub type PreparedVerifyingKey = groth16::PreparedVerifyingKey; -/// A Groth16 proof in the BN254 construction. Thin wrapper around `ark_groth16::Proof::`. -#[derive(Debug, From)] -pub struct Proof(pub(crate) ark_groth16::Proof); +/// A Groth16 verifying key in the BN254 construction. +pub type VerifyingKey = groth16::VerifyingKey; -/// A Groth16 verifying key in the BN254 construction. Thin wrapper around `ark_groth16::VerifyingKey::`. -#[derive(Debug, From)] -pub struct VerifyingKey(pub(crate) ark_groth16::VerifyingKey); +/// A Groth16 proof in the BN254 construction. +pub type Proof = groth16::Proof; -impl Proof { - /// Deserialize a serialized Groth16 proof using arkworks' canonical serialisation format: https://docs.rs/ark-serialize/latest/ark_serialize/. - pub fn deserialize(proof_points_as_bytes: &[u8]) -> FastCryptoResult { - ark_groth16::Proof::::deserialize_compressed(proof_points_as_bytes) - .map_err(|_| FastCryptoError::InvalidInput) - .map(Proof) +impl FromLittleEndianByteArray for Scalar { + fn from_little_endian_byte_array(bytes: &[u8; SCALAR_LENGTH]) -> FastCryptoResult { + Scalar::from_byte_array(bytes) } } -impl FieldElement { - /// Deserialize 32 bytes into a BN254 field element using little-endian format. - pub(crate) fn deserialize(bytes: &[u8]) -> FastCryptoResult { - if bytes.len() != SCALAR_SIZE { - return Err(FastCryptoError::InputLengthWrong(bytes.len())); - } - Fr::deserialize_compressed(bytes) - .map_err(|_| FastCryptoError::InvalidInput) - .map(FieldElement) +impl GTSerialize for GTElement { + fn to_arkworks_bytes(&self) -> [u8; GT_ELEMENT_BYTE_LENGTH] { + self.to_byte_array() } - /// Deserialize a vector of bytes into a vector of BN254 field elements, assuming that each element - /// is serialized as a chunk of 32 bytes. See also [`FieldElement::deserialize`]. - pub(crate) fn deserialize_vector( - field_element_bytes: &[u8], - ) -> FastCryptoResult> { - if field_element_bytes.len() % SCALAR_SIZE != 0 { - return Err(FastCryptoError::InputLengthWrong(field_element_bytes.len())); - } - let mut public_inputs = Vec::new(); - for chunk in field_element_bytes.chunks(SCALAR_SIZE) { - public_inputs.push(FieldElement::deserialize(chunk)?); - } - Ok(public_inputs) + fn from_arkworks_bytes(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> FastCryptoResult { + GTElement::from_byte_array(bytes) } } diff --git a/fastcrypto-zkp/src/bn254/unit_tests/api_tests.rs b/fastcrypto-zkp/src/bn254/unit_tests/api_tests.rs index 79b209e0bb..939aa23381 100644 --- a/fastcrypto-zkp/src/bn254/unit_tests/api_tests.rs +++ b/fastcrypto-zkp/src/bn254/unit_tests/api_tests.rs @@ -2,8 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::bn254::api::{prepare_pvk_bytes, verify_groth16_in_bytes}; -use crate::bn254::verifier::PreparedVerifyingKey; -use crate::bn254::VerifyingKey; +use crate::bn254::{PreparedVerifyingKey, VerifyingKey}; use crate::dummy_circuits::{DummyCircuit, Fibonacci}; use ark_bn254::{Bn254, Fq12, Fr, G1Projective, G2Projective}; use ark_ff::{One, Zero}; @@ -17,6 +16,19 @@ use std::ops::Mul; #[path = "./utils.rs"] mod utils; +fn vk_from_arkworks(vk: ark_groth16::VerifyingKey) -> VerifyingKey { + VerifyingKey::new( + G1Projective::from(vk.alpha_g1).into(), + G2Projective::from(vk.beta_g2).into(), + G2Projective::from(vk.gamma_g2).into(), + G2Projective::from(vk.delta_g2).into(), + vk.gamma_abc_g1 + .iter() + .map(|x| G1Projective::from(*x).into()) + .collect(), + ) +} + #[test] fn test_verify_groth16_in_bytes_api() { const PUBLIC_SIZE: usize = 128; @@ -32,9 +44,9 @@ fn test_verify_groth16_in_bytes_api() { let proof = Groth16::::prove(&pk, c, rng).unwrap(); let v = c.a.unwrap().mul(c.b.unwrap()); - let pvk = PreparedVerifyingKey::from(&vk); + let pvk = PreparedVerifyingKey::from(&vk_from_arkworks(vk)); - let bytes = pvk.serialize().unwrap(); + let bytes = pvk.serialize_into_parts(); let vk_gamma_abc_g1_bytes = &bytes[0]; let alpha_g1_beta_g2_bytes = &bytes[1]; let gamma_g2_neg_pc_bytes = &bytes[2]; @@ -113,12 +125,7 @@ fn test_verify_groth16_in_bytes_multiple_inputs() { Groth16::::create_random_proof_with_reduction(circuit, ¶ms, &mut rng).unwrap() }; - let pvk = PreparedVerifyingKey::from(¶ms.vk); - - let inputs: Vec<_> = [a, b].to_vec(); - assert!(Groth16::::verify_with_processed_vk(&(&pvk).into(), &inputs, &proof).unwrap()); - - let pvk = pvk.serialize().unwrap(); + let pvk = PreparedVerifyingKey::from(&vk_from_arkworks(params.vk)).serialize_into_parts(); // This circuit has two public inputs: let mut inputs_bytes = Vec::new(); @@ -193,7 +200,7 @@ fn test_verify_groth16_elusiv_proof_in_bytes_api() { ], ); - let vk = VerifyingKey(ark_groth16::VerifyingKey { + let vk = vk_from_arkworks(ark_groth16::VerifyingKey { alpha_g1: utils::G1Affine_from_str_projective(( "8057073471822347335074195152835286348058235024870127707965681971765888348219", "14493022634743109860560137600871299171677470588934003383462482807829968516757", @@ -316,7 +323,7 @@ fn test_verify_groth16_elusiv_proof_in_bytes_api() { let pvk = PreparedVerifyingKey::from(&vk); - let bytes = pvk.serialize().unwrap(); + let bytes = pvk.serialize_into_parts(); let vk_gamma_abc_g1_bytes = &bytes[0]; let alpha_g1_beta_g2_bytes = &bytes[1]; let gamma_g2_neg_pc_bytes = &bytes[2]; @@ -528,9 +535,9 @@ fn fail_verify_groth16_invalid_elusiv_proof_in_bytes_api() { .collect(), }; - let pvk = PreparedVerifyingKey::from(&vk); + let pvk = PreparedVerifyingKey::from(&vk_from_arkworks(vk)); - let bytes = pvk.serialize().unwrap(); + let bytes = pvk.serialize_into_parts(); let vk_gamma_abc_g1_bytes = &bytes[0]; let alpha_g1_beta_g2_bytes = &bytes[1]; let gamma_g2_neg_pc_bytes = &bytes[2]; diff --git a/fastcrypto-zkp/src/bn254/unit_tests/utils.rs b/fastcrypto-zkp/src/bn254/unit_tests/utils.rs index b8ce3799ef..5907b8e012 100644 --- a/fastcrypto-zkp/src/bn254/unit_tests/utils.rs +++ b/fastcrypto-zkp/src/bn254/unit_tests/utils.rs @@ -1,7 +1,7 @@ // Copyright (c) 2022, Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use ark_bn254::{Fq, Fq2, G1Affine, G1Projective, G2Affine}; +use ark_bn254::{Fq, Fq2, G1Affine, G1Projective, G2Affine, G2Projective}; type StrPair = (&'static str, &'static str); type StrTriplet = (&'static str, &'static str, &'static str); @@ -20,7 +20,6 @@ pub(crate) fn G1Affine_from_str_projective( #[allow(non_snake_case)] pub(crate) fn G2Affine_from_str_projective(s: (StrPair, StrPair, StrPair)) -> G2Affine { - use ark_bn254::G2Projective; G2Projective::new( Fq2::new(s.0 .0.parse::().unwrap(), s.0 .1.parse::().unwrap()), Fq2::new(s.1 .0.parse::().unwrap(), s.1 .1.parse::().unwrap()), diff --git a/fastcrypto-zkp/src/bn254/verifier.rs b/fastcrypto-zkp/src/bn254/verifier.rs deleted file mode 100644 index 18aa0a41ef..0000000000 --- a/fastcrypto-zkp/src/bn254/verifier.rs +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright (c) 2022, Mysten Labs, Inc. -// SPDX-License-Identifier: Apache-2.0 - -use std::borrow::Borrow; -use std::ops::Neg; - -use ark_bn254::{Bn254, Fq12, Fr, G1Affine, G2Affine}; -use ark_ec::bn::G2Prepared; -use ark_ec::pairing::Pairing; -use ark_groth16::{Groth16, PreparedVerifyingKey as ArkPreparedVerifyingKey}; -use ark_snark::SNARK; - -use crate::bn254::api::SCALAR_SIZE; -use crate::bn254::{FieldElement, Proof, VerifyingKey}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; -use fastcrypto::error::{FastCryptoError, FastCryptoResult}; - -#[cfg(test)] -#[path = "unit_tests/verifier_tests.rs"] -mod verifier_tests; - -/// This is a helper function to store a pre-processed version of the verifying key. -/// This is roughly homologous to [`ark_groth16::data_structures::PreparedVerifyingKey`]. -/// Note that contrary to Arkworks, we don't store a "prepared" version of the gamma_g2_neg_pc, -/// delta_g2_neg_pc fields because they are very large and unpractical to use in the binary api. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PreparedVerifyingKey { - /// The element vk.gamma_abc_g1, - /// aka the `[gamma^{-1} * (beta * a_i + alpha * b_i + c_i) * G]`, where i spans the public inputs - pub vk_gamma_abc_g1: Vec, - /// The element `e(alpha * G, beta * H)` in `E::GT`. - pub alpha_g1_beta_g2: Fq12, - /// The element `- gamma * H` in `E::G2`, for use in pairings. - pub gamma_g2_neg_pc: G2Affine, - /// The element `- delta * H` in `E::G2`, for use in pairings. - pub delta_g2_neg_pc: G2Affine, -} - -impl PreparedVerifyingKey { - /// Verify Groth16 proof using the prepared verifying key (see more at - /// [`PreparedVerifyingKey`]), a vector of public inputs and - /// the proof. - pub fn verify( - &self, - public_inputs: &[FieldElement], - proof: &Proof, - ) -> Result { - let x: Vec = public_inputs.iter().map(|x| x.0).collect(); - Groth16::::verify_with_processed_vk(&self.into(), &x, &proof.0) - .map_err(|e| FastCryptoError::GeneralError(e.to_string())) - } - - /// Serialize the prepared verifying key to its vectors form. - pub fn serialize(&self) -> Result>, FastCryptoError> { - let mut res = Vec::new(); - - let mut vk_gamma = Vec::new(); - for g1 in &self.vk_gamma_abc_g1 { - let mut g1_bytes = Vec::new(); - g1.serialize_compressed(&mut g1_bytes) - .map_err(|_| FastCryptoError::InvalidInput)?; - vk_gamma.append(&mut g1_bytes); - } - res.push(vk_gamma); - - let mut fq12 = Vec::new(); - self.alpha_g1_beta_g2 - .serialize_compressed(&mut fq12) - .map_err(|_| FastCryptoError::InvalidInput)?; - res.push(fq12); - - let mut gamma_bytes = Vec::new(); - self.gamma_g2_neg_pc - .serialize_compressed(&mut gamma_bytes) - .map_err(|_| FastCryptoError::InvalidInput)?; - res.push(gamma_bytes); - - let mut delta_bytes = Vec::new(); - self.delta_g2_neg_pc - .serialize_compressed(&mut delta_bytes) - .map_err(|_| FastCryptoError::InvalidInput)?; - res.push(delta_bytes); - Ok(res) - } - - /// Deserialize the prepared verifying key from the serialized fields of vk_gamma_abc_g1, - /// alpha_g1_beta_g2, gamma_g2_neg_pc, delta_g2_neg_pc - pub fn deserialize>(bytes: &Vec) -> Result { - if bytes.len() != 4 { - return Err(FastCryptoError::InputLengthWrong(bytes.len())); - } - - let vk_gamma_abc_g1_bytes = bytes[0].borrow(); - if vk_gamma_abc_g1_bytes.len() % SCALAR_SIZE != 0 { - return Err(FastCryptoError::InvalidInput); - } - - let mut vk_gamma_abc_g1: Vec = Vec::new(); - for g1_bytes in vk_gamma_abc_g1_bytes.chunks(SCALAR_SIZE) { - let g1 = G1Affine::deserialize_compressed(g1_bytes) - .map_err(|_| FastCryptoError::InvalidInput)?; - vk_gamma_abc_g1.push(g1); - } - - let alpha_g1_beta_g2 = Fq12::deserialize_compressed(bytes[1].borrow()) - .map_err(|_| FastCryptoError::InvalidInput)?; - - let gamma_g2_neg_pc = G2Affine::deserialize_compressed(bytes[2].borrow()) - .map_err(|_| FastCryptoError::InvalidInput)?; - - let delta_g2_neg_pc = G2Affine::deserialize_compressed(bytes[3].borrow()) - .map_err(|_| FastCryptoError::InvalidInput)?; - - Ok(PreparedVerifyingKey { - vk_gamma_abc_g1, - alpha_g1_beta_g2, - gamma_g2_neg_pc, - delta_g2_neg_pc, - }) - } -} - -impl From<&PreparedVerifyingKey> for ArkPreparedVerifyingKey { - /// Returns a [`ark_groth16::data_structures::PreparedVerifyingKey`] corresponding to this for - /// usage in the arkworks api. - fn from(pvk: &PreparedVerifyingKey) -> Self { - // Note that not all the members are set here, but we set enough to be able to run - // Groth16::::verify_with_processed_vk. - let mut ark_pvk = ArkPreparedVerifyingKey::default(); - ark_pvk.vk.gamma_abc_g1 = pvk.vk_gamma_abc_g1.clone(); - ark_pvk.alpha_g1_beta_g2 = pvk.alpha_g1_beta_g2; - ark_pvk.gamma_g2_neg_pc = G2Prepared::from(&pvk.gamma_g2_neg_pc); - ark_pvk.delta_g2_neg_pc = G2Prepared::from(&pvk.delta_g2_neg_pc); - ark_pvk - } -} - -impl From<&VerifyingKey> for PreparedVerifyingKey { - /// Takes an input [`ark_groth16::VerifyingKey`] `vk` and returns a `PreparedVerifyingKey`. This is roughly homologous to - /// [`ark_groth16::PreparedVerifyingKey::process_vk`]. - /// - /// ## Example: - /// ``` - /// use fastcrypto_zkp::{dummy_circuits::Fibonacci}; - /// use ark_bn254::{Bn254, Fr}; - /// use ark_ff::One; - /// use ark_groth16::Groth16; - /// use ark_std::rand::thread_rng; - /// use fastcrypto_zkp::bn254::verifier::PreparedVerifyingKey; - /// use fastcrypto_zkp::bn254::VerifyingKey; - /// - /// let mut rng = thread_rng(); - /// let params = { - /// let c = Fibonacci::::new(42, Fr::one(), Fr::one()); // 42 constraints, initial a = b = 1 (standard Fibonacci) - /// Groth16::::generate_random_parameters_with_reduction(c, &mut rng).unwrap() - /// }; - /// - /// // Prepare the verification key (for proof verification). Ideally, we would like to do this only - /// // once per circuit. - /// let pvk = PreparedVerifyingKey::from(&VerifyingKey::from(params.vk)); - /// ``` - fn from(vk: &VerifyingKey) -> Self { - (&vk.0).into() - } -} - -impl VerifyingKey { - /// Deserialize a serialized Groth16 verifying key in compressed format using arkworks' canonical serialisation format: https://docs.rs/ark-serialize/latest/ark_serialize/. - pub fn deserialize(bytes: &[u8]) -> FastCryptoResult { - ark_groth16::VerifyingKey::::deserialize_compressed(bytes) - .map(VerifyingKey) - .map_err(|_| FastCryptoError::InvalidInput) - } -} - -impl From<&ark_groth16::VerifyingKey> for PreparedVerifyingKey { - fn from(vk: &ark_groth16::VerifyingKey) -> Self { - PreparedVerifyingKey { - vk_gamma_abc_g1: vk.gamma_abc_g1.clone(), - alpha_g1_beta_g2: Bn254::pairing(vk.alpha_g1, vk.beta_g2).0, - gamma_g2_neg_pc: vk.gamma_g2.neg(), - delta_g2_neg_pc: vk.delta_g2.neg(), - } - } -} - -#[cfg(test)] -mod tests { - use crate::bn254::verifier::PreparedVerifyingKey; - use crate::dummy_circuits::DummyCircuit; - use ark_bn254::{Bn254, Fr}; - use ark_groth16::Groth16; - use ark_snark::SNARK; - use ark_std::rand::thread_rng; - use ark_std::UniformRand; - - #[test] - fn test_serialization() { - const PUBLIC_SIZE: usize = 128; - let rng = &mut thread_rng(); - let c = DummyCircuit:: { - a: Some(::rand(rng)), - b: Some(::rand(rng)), - num_variables: PUBLIC_SIZE, - num_constraints: 10, - }; - let (_, vk) = Groth16::::circuit_specific_setup(c, rng).unwrap(); - let pvk = PreparedVerifyingKey::from(&vk); - - let serialized = pvk.serialize().unwrap(); - let deserialized = PreparedVerifyingKey::deserialize(&serialized).unwrap(); - assert_eq!(pvk, deserialized); - } -} diff --git a/fastcrypto-zkp/src/groth16/mod.rs b/fastcrypto-zkp/src/groth16/mod.rs index b55734c96c..dcdcba3f3f 100644 --- a/fastcrypto-zkp/src/groth16/mod.rs +++ b/fastcrypto-zkp/src/groth16/mod.rs @@ -54,6 +54,30 @@ where delta_neg: ::Other, } +impl Proof { + pub fn new(a: G1, b: G1::Other, c: G1) -> Self { + Proof { a, b, c } + } +} + +impl VerifyingKey { + pub fn new( + alpha: G1, + beta: G1::Other, + gamma: G1::Other, + delta: G1::Other, + gamma_abc: Vec, + ) -> Self { + VerifyingKey { + alpha, + beta, + gamma, + delta, + gamma_abc, + } + } +} + impl From<&VerifyingKey> for PreparedVerifyingKey where G1: Pairing, diff --git a/fastcrypto/src/groups/bn254/g1.rs b/fastcrypto/src/groups/bn254/g1.rs index fcb0ab84b0..c3c1c5534d 100644 --- a/fastcrypto/src/groups/bn254/g1.rs +++ b/fastcrypto/src/groups/bn254/g1.rs @@ -6,11 +6,11 @@ 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::groups::{FromTrustedByteArray, GroupElement, MultiScalarMul, 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_ec::{AffineRepr, CurveGroup, Group, VariableBaseMSM}; use ark_ff::Zero; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use serde::{de, Deserialize}; @@ -79,3 +79,18 @@ impl FromTrustedByteArray for G1Element { } serialize_deserialize_with_to_from_byte_array!(G1Element); + +impl MultiScalarMul for G1Element { + fn multi_scalar_mul(scalars: &[Self::ScalarType], points: &[Self]) -> FastCryptoResult { + if scalars.len() != points.len() { + return Err(FastCryptoError::InvalidInput); + } + if scalars.is_empty() { + return Ok(Self::zero()); + } + Ok(Self(G1Projective::msm_unchecked( + &points.iter().map(|x| x.0.into_affine()).collect::>(), + &scalars.iter().map(|x| x.0).collect::>(), + ))) + } +} \ No newline at end of file diff --git a/fastcrypto/src/groups/bn254/mod.rs b/fastcrypto/src/groups/bn254/mod.rs index 3a06bb8fa7..ebe38fe4ef 100644 --- a/fastcrypto/src/groups/bn254/mod.rs +++ b/fastcrypto/src/groups/bn254/mod.rs @@ -4,7 +4,7 @@ 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 derive_more::{Add, From, Neg, Sub}; use fastcrypto_derive::GroupOpsExtend; mod g1; @@ -28,22 +28,22 @@ pub const GT_ELEMENT_BYTE_LENGTH: usize = 384; pub const SCALAR_LENGTH: usize = 32; /// Elements of the group G1 in BN254. -#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)] +#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend, From)] #[repr(transparent)] pub struct G1Element(G1Projective); /// Elements of the group G2 in BN254. -#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend)] +#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend, From)] #[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)] +#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend, From)] 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)] +#[derive(Clone, Copy, Eq, PartialEq, Debug, Add, Sub, Neg, GroupOpsExtend, From)] pub struct Scalar(Fr); impl Pairing for G1Element { From e0e45d29741248b064c35f3902fab300e4376084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Lindstr=C3=B8m?= Date: Wed, 1 May 2024 14:11:39 +0200 Subject: [PATCH 65/65] fmt --- fastcrypto-zkp/benches/proving.rs | 2 +- fastcrypto/src/groups/bn254/g1.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fastcrypto-zkp/benches/proving.rs b/fastcrypto-zkp/benches/proving.rs index feb3fa8b7a..08a45217d4 100644 --- a/fastcrypto-zkp/benches/proving.rs +++ b/fastcrypto-zkp/benches/proving.rs @@ -16,12 +16,12 @@ use criterion::{ Criterion, SamplingMode, }; +use crate::utils::vk_from_arkworks; use fastcrypto::groups::bls12381::{G1Element, Scalar}; use fastcrypto::serde_helpers::ToFromByteArray; use fastcrypto_zkp::bn254; use fastcrypto_zkp::dummy_circuits::DummyCircuit; use fastcrypto_zkp::groth16::{Proof, VerifyingKey}; -use crate::utils::vk_from_arkworks; #[path = "./utils.rs"] mod utils; diff --git a/fastcrypto/src/groups/bn254/g1.rs b/fastcrypto/src/groups/bn254/g1.rs index c3c1c5534d..338af32f4e 100644 --- a/fastcrypto/src/groups/bn254/g1.rs +++ b/fastcrypto/src/groups/bn254/g1.rs @@ -93,4 +93,4 @@ impl MultiScalarMul for G1Element { &scalars.iter().map(|x| x.0).collect::>(), ))) } -} \ No newline at end of file +}