diff --git a/Cargo.lock b/Cargo.lock index a7ce7af0ea..f2ff0812ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1362,13 +1362,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.3" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ - "errno-dragonfly", "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -2089,7 +2088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi 0.3.1", - "rustix 0.38.11", + "rustix 0.38.31", "windows-sys 0.48.0", ] @@ -2186,9 +2185,9 @@ checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" [[package]] name = "linux-raw-sys" -version = "0.4.5" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "log" @@ -3010,15 +3009,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.11" +version = "0.38.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0c3dde1fc030af041adc40e79c0e7fbcf431dd24870053d187d7c66e4b87453" +checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" dependencies = [ "bitflags 2.4.0", - "errno 0.3.3", + "errno 0.3.8", "libc", - "linux-raw-sys 0.4.5", - "windows-sys 0.48.0", + "linux-raw-sys 0.4.13", + "windows-sys 0.52.0", ] [[package]] @@ -3991,6 +3990,15 @@ dependencies = [ "windows-targets 0.48.2", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.4", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -4021,6 +4029,21 @@ dependencies = [ "windows_x86_64_msvc 0.48.2", ] +[[package]] +name = "windows-targets" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +dependencies = [ + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -4033,6 +4056,12 @@ version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b10d0c968ba7f6166195e13d593af609ec2e3d24f916f081690695cf5eaffb2f" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -4045,6 +4074,12 @@ version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "571d8d4e62f26d4932099a9efe89660e8bd5087775a2ab5cdd8b747b811f1058" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -4057,6 +4092,12 @@ version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2229ad223e178db5fbbc8bd8d3835e51e566b8474bfca58d2e6150c48bb723cd" +[[package]] +name = "windows_i686_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -4069,6 +4110,12 @@ version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "600956e2d840c194eedfc5d18f8242bc2e17c7775b6684488af3a9fff6fe3287" +[[package]] +name = "windows_i686_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -4081,6 +4128,12 @@ version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea99ff3f8b49fb7a8e0d305e5aec485bd068c2ba691b6e277d29eaeac945868a" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -4093,6 +4146,12 @@ version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f1a05a1ece9a7a0d5a7ccf30ba2c33e3a61a30e042ffd247567d1de1d94120d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -4105,6 +4164,12 @@ version = "0.48.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d419259aba16b663966e29e6d7c6ecfa0bb8425818bb96f6f1f3c3eb71a6e7b9" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" + [[package]] name = "winreg" version = "0.50.0" diff --git a/fastcrypto/benches/groups.rs b/fastcrypto/benches/groups.rs index 80d92a6311..16626686bb 100644 --- a/fastcrypto/benches/groups.rs +++ b/fastcrypto/benches/groups.rs @@ -6,14 +6,19 @@ extern crate criterion; mod group_benches { use criterion::measurement::Measurement; use criterion::{measurement, BenchmarkGroup, BenchmarkId, Criterion}; - use fastcrypto::groups::bls12381::{G1Element, G2Element, GTElement}; + use fastcrypto::groups::bls12381::{ + G1Element, G2Element, GTElement, Scalar as BlsScalar, G1_ELEMENT_BYTE_LENGTH, + G2_ELEMENT_BYTE_LENGTH, GT_ELEMENT_BYTE_LENGTH, SCALAR_LENGTH, + }; use fastcrypto::groups::multiplier::windowed::WindowedScalarMultiplier; use fastcrypto::groups::multiplier::ScalarMultiplier; use fastcrypto::groups::ristretto255::RistrettoPoint; use fastcrypto::groups::secp256r1::ProjectivePoint; use fastcrypto::groups::{ - secp256r1, GroupElement, HashToGroupElement, MultiScalarMul, Pairing, Scalar, + secp256r1, FromTrustedByteArray, GroupElement, HashToGroupElement, MultiScalarMul, Pairing, + Scalar, }; + use fastcrypto::serde_helpers::ToFromByteArray; use rand::thread_rng; fn add_single( @@ -229,10 +234,60 @@ mod group_benches { } } + fn deser_single< + G: GroupElement + ToFromByteArray + FromTrustedByteArray, + M: Measurement, + const LENGTH: usize, + >( + name: &str, + trusted: bool, + c: &mut BenchmarkGroup, + ) { + let as_bytes = G::generator().to_byte_array(); + c.bench_function(&(name.to_string()), move |b| { + b.iter(|| { + if trusted { + G::from_trusted_byte_array(&as_bytes).unwrap() + } else { + G::from_byte_array(&as_bytes).unwrap() + } + }) + }); + } + + fn deserialize(c: &mut Criterion) { + let mut group: BenchmarkGroup<_> = c.benchmark_group("Deserialize"); + deser_single::( + "BLS12381-Scalar-trusted", + true, + &mut group, + ); + deser_single::("BLS12381-Scalar", false, &mut group); + deser_single::( + "BLS12381-G1-trusted", + true, + &mut group, + ); + deser_single::("BLS12381-G1", false, &mut group); + deser_single::( + "BLS12381-G2-trusted", + true, + &mut group, + ); + deser_single::("BLS12381-G2", false, &mut group); + deser_single::( + "BLS12381-GT-trusted", + true, + &mut group, + ); + deser_single::("BLS12381-GT", false, &mut group); + } + criterion_group! { name = group_benches; config = Criterion::default().sample_size(100); targets = + deserialize, add, scale, hash_to_group, diff --git a/fastcrypto/src/groups/bls12381.rs b/fastcrypto/src/groups/bls12381.rs index e7c3b79dba..94554590b9 100644 --- a/fastcrypto/src/groups/bls12381.rs +++ b/fastcrypto/src/groups/bls12381.rs @@ -6,8 +6,8 @@ use crate::bls12381::min_sig::DST_G1; use crate::encoding::{Encoding, Hex}; use crate::error::{FastCryptoError, FastCryptoResult}; use crate::groups::{ - FiatShamirChallenge, GroupElement, HashToGroupElement, MultiScalarMul, Pairing, - Scalar as ScalarType, + FiatShamirChallenge, FromTrustedByteArray, GroupElement, HashToGroupElement, MultiScalarMul, + Pairing, Scalar as ScalarType, }; use crate::serde_helpers::BytesRepresentation; use crate::serde_helpers::ToFromByteArray; @@ -113,7 +113,7 @@ fn size_in_bits(scalar: &blst_scalar, size_in_bytes: usize) -> usize { #[allow(clippy::suspicious_arithmetic_impl)] impl Div for G1Element { - type Output = Result; + type Output = FastCryptoResult; fn div(self, rhs: Scalar) -> Self::Output { let inv = rhs.inverse()?; @@ -237,8 +237,8 @@ impl HashToGroupElement for G1Element { } } -impl ToFromByteArray for G1Element { - fn from_byte_array(bytes: &[u8; G1_ELEMENT_BYTE_LENGTH]) -> Result { +impl FromTrustedByteArray for G1Element { + fn from_trusted_byte_array(bytes: &[u8; G1_ELEMENT_BYTE_LENGTH]) -> FastCryptoResult { let mut ret = blst_p1::default(); unsafe { let mut affine = blst_p1_affine::default(); @@ -246,12 +246,21 @@ impl ToFromByteArray for G1Element { return Err(FastCryptoError::InvalidInput); } blst_p1_from_affine(&mut ret, &affine); + } + Ok(G1Element(ret)) + } +} + +impl ToFromByteArray for G1Element { + fn from_byte_array(bytes: &[u8; G1_ELEMENT_BYTE_LENGTH]) -> FastCryptoResult { + let ret = Self::from_trusted_byte_array(bytes)?; + unsafe { // Verify that the deserialized element is in G1 - if !blst_p1_in_g1(&ret) { + if !blst_p1_in_g1(&ret.0) { return Err(FastCryptoError::InvalidInput); } } - Ok(G1Element(ret)) + Ok(ret) } fn to_byte_array(&self) -> [u8; G1_ELEMENT_BYTE_LENGTH] { @@ -307,7 +316,7 @@ impl Neg for G2Element { #[allow(clippy::suspicious_arithmetic_impl)] impl Div for G2Element { - type Output = Result; + type Output = FastCryptoResult; fn div(self, rhs: Scalar) -> Self::Output { let inv = rhs.inverse()?; @@ -406,8 +415,8 @@ impl HashToGroupElement for G2Element { } } -impl ToFromByteArray for G2Element { - fn from_byte_array(bytes: &[u8; G2_ELEMENT_BYTE_LENGTH]) -> Result { +impl FromTrustedByteArray for G2Element { + fn from_trusted_byte_array(bytes: &[u8; G2_ELEMENT_BYTE_LENGTH]) -> FastCryptoResult { let mut ret = blst_p2::default(); unsafe { let mut affine = blst_p2_affine::default(); @@ -415,12 +424,21 @@ impl ToFromByteArray for G2Element { return Err(FastCryptoError::InvalidInput); } blst_p2_from_affine(&mut ret, &affine); + } + Ok(G2Element(ret)) + } +} + +impl ToFromByteArray for G2Element { + fn from_byte_array(bytes: &[u8; G2_ELEMENT_BYTE_LENGTH]) -> FastCryptoResult { + let ret = Self::from_trusted_byte_array(bytes)?; + unsafe { // Verify that the deserialized element is in G2 - if !blst_p2_in_g2(&ret) { + if !blst_p2_in_g2(&ret.0) { return Err(FastCryptoError::InvalidInput); } } - Ok(G2Element(ret)) + Ok(ret) } fn to_byte_array(&self) -> [u8; G2_ELEMENT_BYTE_LENGTH] { @@ -476,7 +494,7 @@ impl Neg for GTElement { #[allow(clippy::suspicious_arithmetic_impl)] impl Div for GTElement { - type Output = Result; + type Output = FastCryptoResult; fn div(self, rhs: Scalar) -> Self::Output { let inv = rhs.inverse()?; @@ -545,8 +563,8 @@ impl GTElement { const P_AS_BYTES: [u8; FP_BYTE_LENGTH] = hex!("1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab"); // Note that the serialization below is uncompressed, i.e. it uses 576 bytes. -impl ToFromByteArray for GTElement { - fn from_byte_array(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> FastCryptoResult { +impl FromTrustedByteArray for GTElement { + fn from_trusted_byte_array(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> FastCryptoResult { // The following is based on the order from // https://github.com/supranational/blst/blob/b4ebf88014251f1cfefeb6cf1cd4df7c40dc568f/src/fp12_tower.c#L773-L786C2 let mut gt: blst_fp12 = Default::default(); @@ -569,9 +587,16 @@ impl ToFromByteArray for GTElement { } } } + Ok(Self(gt)) + } +} - match gt.in_group() { - true => Ok(Self(gt)), +// Note that the serialization below is uncompressed, i.e. it uses 576 bytes. +impl ToFromByteArray for GTElement { + fn from_byte_array(bytes: &[u8; GT_ELEMENT_BYTE_LENGTH]) -> FastCryptoResult { + let gt = Self::from_trusted_byte_array(bytes)?; + match gt.0.in_group() { + true => Ok(gt), false => Err(FastCryptoError::InvalidInput), } } @@ -663,7 +688,7 @@ impl From for Scalar { #[allow(clippy::suspicious_arithmetic_impl)] impl Div for Scalar { - type Output = Result; + type Output = FastCryptoResult; fn div(self, rhs: Self) -> Self::Output { let inv = rhs.inverse()?; @@ -713,8 +738,20 @@ impl FiatShamirChallenge for Scalar { } } +impl FromTrustedByteArray for Scalar { + fn from_trusted_byte_array(bytes: &[u8; SCALAR_LENGTH]) -> FastCryptoResult { + let mut ret = blst_fr::default(); + unsafe { + let mut scalar = blst_scalar::default(); + blst_scalar_from_bendian(&mut scalar, bytes.as_ptr()); + blst_fr_from_scalar(&mut ret, &scalar); + } + Ok(Scalar(ret)) + } +} + impl ToFromByteArray for Scalar { - fn from_byte_array(bytes: &[u8; SCALAR_LENGTH]) -> Result { + fn from_byte_array(bytes: &[u8; SCALAR_LENGTH]) -> FastCryptoResult { let mut ret = blst_fr::default(); unsafe { let mut scalar = blst_scalar::default(); diff --git a/fastcrypto/src/groups/mod.rs b/fastcrypto/src/groups/mod.rs index 7c0a08c8eb..e561178e9f 100644 --- a/fastcrypto/src/groups/mod.rs +++ b/fastcrypto/src/groups/mod.rs @@ -84,3 +84,8 @@ pub trait HashToGroupElement { pub trait MultiScalarMul: GroupElement { fn multi_scalar_mul(scalars: &[Self::ScalarType], points: &[Self]) -> FastCryptoResult; } + +/// Faster deserialization in case the input is trusted (otherwise it can be insecure). +pub trait FromTrustedByteArray: Sized { + fn from_trusted_byte_array(bytes: &[u8; LENGTH]) -> FastCryptoResult; +} diff --git a/fastcrypto/src/tests/bls12381_group_tests.rs b/fastcrypto/src/tests/bls12381_group_tests.rs index 361796e92a..94ed25414a 100644 --- a/fastcrypto/src/tests/bls12381_group_tests.rs +++ b/fastcrypto/src/tests/bls12381_group_tests.rs @@ -4,7 +4,8 @@ use crate::bls12381::min_pk::{BLS12381KeyPair, BLS12381Signature}; use crate::groups::bls12381::{reduce_mod_uniform_buffer, G1Element, G2Element, GTElement, Scalar}; use crate::groups::{ - GroupElement, HashToGroupElement, MultiScalarMul, Pairing, Scalar as ScalarTrait, + FromTrustedByteArray, GroupElement, HashToGroupElement, MultiScalarMul, Pairing, + Scalar as ScalarTrait, }; use crate::serde_helpers::ToFromByteArray; use crate::test_helpers::verify_serialization; @@ -12,8 +13,9 @@ use crate::traits::Signer; use crate::traits::VerifyingKey; use crate::traits::{KeyPair, ToFromBytes}; use blst::{ - blst_p1_affine_generator, blst_p1_affine_serialize, blst_p2_affine_generator, - blst_p2_affine_serialize, + blst_p1_affine, blst_p1_affine_generator, blst_p1_affine_on_curve, blst_p1_affine_serialize, + blst_p1_deserialize, blst_p2_affine, blst_p2_affine_generator, blst_p2_affine_on_curve, + blst_p2_affine_serialize, blst_p2_deserialize, BLST_ERROR, }; use rand::{rngs::StdRng, thread_rng, SeedableRng as _}; @@ -378,6 +380,21 @@ fn test_serialization_g1() { G1Element::generator(), G1Element::from_byte_array(&(uncompressed_bytes[0..48].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()); + // Verify that this is a valid point on the curve. + unsafe { + let mut p: blst_p1_affine = blst_p1_affine::default(); + assert!(blst_p1_deserialize(&mut p, bytes.as_ptr()) == BLST_ERROR::BLST_SUCCESS); + assert!(blst_p1_affine_on_curve(&p)); + }; + assert!(G1Element::from_byte_array(&bytes).is_err()); } #[test] @@ -426,6 +443,21 @@ fn test_serialization_g2() { G2Element::generator(), G2Element::from_byte_array(&(uncompressed_bytes[0..96].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()); + // Verify that this is a valid point on the curve. + unsafe { + let mut p: blst_p2_affine = blst_p2_affine::default(); + assert!(blst_p2_deserialize(&mut p, bytes.as_ptr()) == BLST_ERROR::BLST_SUCCESS); + assert!(blst_p2_affine_on_curve(&p)); + }; + assert!(G2Element::from_byte_array(&bytes).is_err()); } #[test] @@ -470,4 +502,13 @@ fn test_serialization_gt() { 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()); }