diff --git a/Cargo.lock b/Cargo.lock index 7674d93a53..547ec0b666 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1292,6 +1292,7 @@ version = "0.1.8" dependencies = [ "aes", "aes-gcm", + "ark-bn254", "ark-ec", "ark-ff", "ark-secp256r1", @@ -3681,7 +3682,7 @@ version = "1.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "rand", "static_assertions", ] diff --git a/fastcrypto/Cargo.toml b/fastcrypto/Cargo.toml index e3b1560570..9977c6a9e6 100644 --- a/fastcrypto/Cargo.toml +++ b/fastcrypto/Cargo.toml @@ -45,6 +45,7 @@ elliptic-curve = { version = "0.13.2", features = ["hash2curve"] } rsa = { version = "0.8.2", features = ["sha2"] } static_assertions = "1.1.0" ark-secp256r1 = "0.4.0" +ark-bn254 = "0.4.0" ark-ec = "0.4.1" ark-ff = "0.4.1" ark-serialize = "0.4.1" diff --git a/fastcrypto/src/groups/bn254/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..e44f405913 --- /dev/null +++ b/fastcrypto/src/groups/bn254/scalar.rs @@ -0,0 +1,82 @@ +// 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 { + // 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"); + bytes + } +} + +serialize_deserialize_with_to_from_byte_array!(Scalar); diff --git a/fastcrypto/src/groups/bn254/tests.rs b/fastcrypto/src/groups/bn254/tests.rs new file mode 100644 index 0000000000..549d54c9e7 --- /dev/null +++ b/fastcrypto/src/groups/bn254/tests.rs @@ -0,0 +1,372 @@ +// 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; +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; + +#[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("0100000000000000000000000000000000000000000000000000000000000000") + .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 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 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(), + 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("30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47").unwrap(); + let mut carry = 0; + 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..32].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()); +} 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;