From 4074fee5211784ea078cd38b5b1eddce4b8cb89f Mon Sep 17 00:00:00 2001 From: StackOverflowExcept1on <109800286+StackOverflowExcept1on@users.noreply.github.com> Date: Thu, 12 Dec 2024 16:45:26 +0300 Subject: [PATCH 1/2] feat(frost-core): simplify trait bounds, don't use the allocator in some places --- frost-core/src/lib.rs | 5 +- frost-core/src/serialization.rs | 48 +++++++------------ frost-core/src/signature.rs | 37 +++++++------- .../src/tests/coefficient_commitment.rs | 2 +- frost-core/src/tests/repairable.rs | 10 ++-- frost-core/src/tests/vectors.rs | 3 +- frost-core/src/tests/vectors_dkg.rs | 10 ++-- frost-core/src/tests/vss_commitment.rs | 2 +- frost-core/src/traits.rs | 16 ++++--- 9 files changed, 65 insertions(+), 68 deletions(-) diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index 25c8ff99..5e667123 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -272,12 +272,13 @@ where fn from_hex>(hex: T) -> Result { let v: Vec = FromHex::from_hex(hex).map_err(|_| "invalid hex")?; - match v.try_into() { + let ret = match v.as_slice().try_into() { Ok(bytes) => <::Field>::deserialize(&bytes) .map(|scalar| Self(scalar)) .map_err(|_| "malformed scalar encoding"), Err(_) => Err("malformed scalar encoding"), - } + }; + ret } } diff --git a/frost-core/src/serialization.rs b/frost-core/src/serialization.rs index fe1df7b8..73195c16 100644 --- a/frost-core/src/serialization.rs +++ b/frost-core/src/serialization.rs @@ -27,10 +27,8 @@ where /// Deserialize a Scalar from a serialized buffer. pub fn deserialize(bytes: &[u8]) -> Result> { - let serialized: <::Field as Field>::Serialization = bytes - .to_vec() - .try_into() - .map_err(|_| FieldError::MalformedScalar)?; + let serialized: <::Field as Field>::Serialization = + bytes.try_into().map_err(|_| FieldError::MalformedScalar)?; let scalar = <::Field>::deserialize(&serialized)?; Ok(Self(scalar)) } @@ -59,18 +57,13 @@ where where D: serde::Deserializer<'de>, { - // Get size from the size of the zero scalar + // Get serialization buffer from the zero scalar let zero = <::Field as Field>::zero(); - let len = <::Field as Field>::serialize(&zero) - .as_ref() - .len(); - - let mut bytes = vec![0u8; len]; - serdect::array::deserialize_hex_or_bin(&mut bytes[..], deserializer)?; - let array = bytes - .try_into() - .map_err(|_| serde::de::Error::custom("invalid byte length"))?; - <::Group as Group>::Field::deserialize(&array) + let mut serialization = <::Field as Field>::serialize(&zero); + + serdect::array::deserialize_hex_or_bin(serialization.as_mut(), deserializer)?; + + <::Group as Group>::Field::deserialize(&serialization) .map(|scalar| Self(scalar)) .map_err(serde::de::Error::custom) } @@ -91,10 +84,8 @@ where /// Deserialize an Element. Returns an error if it's malformed or is the /// identity. pub fn deserialize(bytes: &[u8]) -> Result> { - let serialized: ::Serialization = bytes - .to_vec() - .try_into() - .map_err(|_| FieldError::MalformedScalar)?; + let serialized: ::Serialization = + bytes.try_into().map_err(|_| FieldError::MalformedScalar)?; let scalar = ::deserialize(&serialized)?; Ok(Self(scalar)) } @@ -124,19 +115,14 @@ where where D: serde::Deserializer<'de>, { - // Get size from the size of the generator + // Get serialization buffer from the generator let generator = ::generator(); - let len = ::serialize(&generator) - .expect("serializing the generator always works") - .as_ref() - .len(); - - let mut bytes = vec![0u8; len]; - serdect::array::deserialize_hex_or_bin(&mut bytes[..], deserializer)?; - let array = bytes - .try_into() - .map_err(|_| serde::de::Error::custom("invalid byte length"))?; - ::deserialize(&array) + let mut serialization = + ::serialize(&generator).expect("serializing the generator always works"); + + serdect::array::deserialize_hex_or_bin(serialization.as_mut(), deserializer)?; + + ::deserialize(&serialization) .map(|element| Self(element)) .map_err(serde::de::Error::custom) } diff --git a/frost-core/src/signature.rs b/frost-core/src/signature.rs index 91d3ade9..506cb6e7 100644 --- a/frost-core/src/signature.rs +++ b/frost-core/src/signature.rs @@ -38,34 +38,31 @@ where // and get its length. Note that we can't use the identity because it can be encoded // shorter in some cases (e.g. P-256, which uses SEC1 encoding). let generator = ::generator(); - let mut R_bytes = Vec::from(::serialize(&generator)?.as_ref()); - let R_bytes_len = R_bytes.len(); + let mut R_serialization = ::serialize(&generator)?; + let R_bytes_len = R_serialization.as_ref().len(); - let one = <::Field as Field>::zero(); - let mut z_bytes = - Vec::from(<::Field as Field>::serialize(&one).as_ref()); - let z_bytes_len = z_bytes.len(); + let zero = <::Field as Field>::zero(); + let mut z_serialization = <::Field as Field>::serialize(&zero); + let z_bytes_len = z_serialization.as_ref().len(); if bytes.len() != R_bytes_len + z_bytes_len { return Err(Error::MalformedSignature); } - R_bytes[..].copy_from_slice(bytes.get(0..R_bytes_len).ok_or(Error::MalformedSignature)?); - - let R_serialization = &R_bytes.try_into().map_err(|_| Error::MalformedSignature)?; + R_serialization + .as_mut() + .copy_from_slice(bytes.get(0..R_bytes_len).ok_or(Error::MalformedSignature)?); // We extract the exact length of bytes we expect, not just the remaining bytes with `bytes[R_bytes_len..]` - z_bytes[..].copy_from_slice( + z_serialization.as_mut().copy_from_slice( bytes .get(R_bytes_len..R_bytes_len + z_bytes_len) .ok_or(Error::MalformedSignature)?, ); - let z_serialization = &z_bytes.try_into().map_err(|_| Error::MalformedSignature)?; - Ok(Self { - R: ::deserialize(R_serialization)?, - z: <::Field>::deserialize(z_serialization)?, + R: ::deserialize(&R_serialization)?, + z: <::Field>::deserialize(&z_serialization)?, }) } @@ -77,10 +74,16 @@ where /// Converts this signature to its default byte serialization. #[cfg(feature = "internals")] pub fn default_serialize(&self) -> Result, Error> { - let mut bytes = Vec::::new(); + let R_serialization = ::serialize(&self.R)?; + let z_serialization = <::Field>::serialize(&self.z); + + let R_bytes = R_serialization.as_ref(); + let z_bytes = z_serialization.as_ref(); + + let mut bytes = Vec::with_capacity(R_bytes.len() + z_bytes.len()); - bytes.extend(::serialize(&self.R)?.as_ref()); - bytes.extend(<::Field>::serialize(&self.z).as_ref()); + bytes.extend(R_bytes); + bytes.extend(z_bytes); Ok(bytes) } diff --git a/frost-core/src/tests/coefficient_commitment.rs b/frost-core/src/tests/coefficient_commitment.rs index b3392ca4..533bf543 100644 --- a/frost-core/src/tests/coefficient_commitment.rs +++ b/frost-core/src/tests/coefficient_commitment.rs @@ -44,7 +44,7 @@ pub fn check_create_coefficient_commitment_error( let values = &commitment_helpers["elements"]; let serialized: ::Serialization = ::Serialization::try_from( - hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(), + &hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(), ) .debugless_unwrap(); diff --git a/frost-core/src/tests/repairable.rs b/frost-core/src/tests/repairable.rs index 532e1e06..80e4e0b3 100644 --- a/frost-core/src/tests/repairable.rs +++ b/frost-core/src/tests/repairable.rs @@ -92,9 +92,11 @@ pub fn check_rts(mut rng: R) { fn generate_scalar_from_byte_string( bs: &str, -) -> <<::Group as Group>::Field as Field>::Scalar { +) -> <::Field as Field>::Scalar { let decoded = hex::decode(bs).unwrap(); - let out = <::Field>::deserialize(&decoded.try_into().debugless_unwrap()); + let out = <::Field>::deserialize( + &decoded.as_slice().try_into().debugless_unwrap(), + ); out.unwrap() } @@ -160,7 +162,7 @@ pub fn check_repair_share_step_2(repair_share_helpers: &Value) { let expected: Scalar = repair_share_step_2::(&[value_1, value_2, value_3]); - let actual: <<::Group as Group>::Field as Field>::Scalar = + let actual: <::Field as Field>::Scalar = generate_scalar_from_byte_string::(values["random_scalar_sum"].as_str().unwrap()); assert!(actual == expected); @@ -198,7 +200,7 @@ pub fn check_repair_share_step_3( &commitment, ); - let actual_sigma: <<::Group as Group>::Field as Field>::Scalar = + let actual_sigma: <::Field as Field>::Scalar = generate_scalar_from_byte_string::(sigmas["sigma_sum"].as_str().unwrap()); let actual: SecretShare = SecretShare::new( Identifier::try_from(2).unwrap(), diff --git a/frost-core/src/tests/vectors.rs b/frost-core/src/tests/vectors.rs index c11b1441..950fe9a9 100644 --- a/frost-core/src/tests/vectors.rs +++ b/frost-core/src/tests/vectors.rs @@ -45,7 +45,8 @@ pub fn parse_test_vectors(json_vectors: &Value) -> TestVectors::Field>::deserialize(&vec.try_into().debugless_unwrap()).unwrap() + <::Field>::deserialize(&vec.as_slice().try_into().debugless_unwrap()) + .unwrap() }) .collect(); diff --git a/frost-core/src/tests/vectors_dkg.rs b/frost-core/src/tests/vectors_dkg.rs index fe775bda..fab3dbba 100644 --- a/frost-core/src/tests/vectors_dkg.rs +++ b/frost-core/src/tests/vectors_dkg.rs @@ -31,14 +31,14 @@ pub struct DKGTestVectors { fn json_to_scalar( vector: &Value, ) -> <::Field as Field>::Serialization { - (hex::decode(vector.as_str().unwrap()).unwrap()) - .try_into() - .debugless_unwrap() + <::Field as Field>::Serialization::try_from( + &hex::decode(vector.as_str().unwrap()).unwrap(), + ) + .debugless_unwrap() } fn json_to_element(vector: &Value) -> ::Serialization { - (hex::decode(vector.as_str().unwrap()).unwrap()) - .try_into() + ::Serialization::try_from(&hex::decode(vector.as_str().unwrap()).unwrap()) .debugless_unwrap() } diff --git a/frost-core/src/tests/vss_commitment.rs b/frost-core/src/tests/vss_commitment.rs index 10465227..4e76873c 100644 --- a/frost-core/src/tests/vss_commitment.rs +++ b/frost-core/src/tests/vss_commitment.rs @@ -92,7 +92,7 @@ pub fn check_deserialize_vss_commitment_error::Serialization = ::Serialization::try_from( - hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(), + &hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(), ) .debugless_unwrap(); // --- diff --git a/frost-core/src/traits.rs b/frost-core/src/traits.rs index 4e3f959d..37399e1a 100644 --- a/frost-core/src/traits.rs +++ b/frost-core/src/traits.rs @@ -25,7 +25,7 @@ use crate::{ /// pass-through, implemented for a type just for the ciphersuite, and calls through to another /// implementation underneath, so that this trait does not have to be implemented for types you /// don't own. -pub trait Field: Copy + Clone { +pub trait Field: Copy { /// An element of the scalar field GF(p). /// The Eq/PartialEq implementation MUST be constant-time. type Scalar: Add @@ -37,7 +37,7 @@ pub trait Field: Copy + Clone { + Sub; /// A unique byte array buf of fixed length N. - type Serialization: AsRef<[u8]> + Debug + TryFrom>; + type Serialization: Copy + AsRef<[u8]> + AsMut<[u8]> + for<'a> TryFrom<&'a [u8]> + Debug; /// Returns the zero element of the field, the additive identity. fn zero() -> Self::Scalar; @@ -85,7 +85,7 @@ pub type Scalar = <<::Group as Group>::Field as Field>::Sca /// pass-through, implemented for a type just for the ciphersuite, and calls through to another /// implementation underneath, so that this trait does not have to be implemented for types you /// don't own. -pub trait Group: Copy + Clone + PartialEq { +pub trait Group: Copy + PartialEq { /// A prime order finite field GF(q) over which all scalar values for our prime order group can /// be multiplied are defined. type Field: Field; @@ -102,7 +102,7 @@ pub trait Group: Copy + Clone + PartialEq { /// A unique byte array buf of fixed length N. /// /// Little-endian! - type Serialization: AsRef<[u8]> + Debug + TryFrom>; + type Serialization: Copy + AsRef<[u8]> + AsMut<[u8]> + for<'a> TryFrom<&'a [u8]> + Debug; /// The order of the the quotient group when the prime order subgroup divides the order of the /// full curve group. @@ -147,7 +147,7 @@ pub type Element = <::Group as Group>::Element; /// /// [FROST ciphersuite]: https://datatracker.ietf.org/doc/html/rfc9591#name-ciphersuites // See https://github.com/ZcashFoundation/frost/issues/693 for reasoning about the 'static bound. -pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static { +pub trait Ciphersuite: Copy + PartialEq + Debug + 'static { /// The ciphersuite ID string. It should be equal to the contextString in /// the spec. For new ciphersuites, this should be a string that identifies /// the ciphersuite; it's recommended to use a similar format to the @@ -162,7 +162,11 @@ pub trait Ciphersuite: Copy + Clone + PartialEq + Debug + 'static { /// A unique byte array of fixed length that is the `Group::ElementSerialization` + /// `Group::ScalarSerialization` - type SignatureSerialization: AsRef<[u8]> + TryFrom>; + type SignatureSerialization: Copy + + AsRef<[u8]> + + AsMut<[u8]> + + for<'a> TryFrom<&'a [u8]> + + Debug; /// [H1] for a FROST ciphersuite. /// From 5a65678cec1f737e34eab7c0a1386ee7d4801d3a Mon Sep 17 00:00:00 2001 From: StackOverflowExcept1on <109800286+StackOverflowExcept1on@users.noreply.github.com> Date: Thu, 12 Dec 2024 17:03:09 +0300 Subject: [PATCH 2/2] use as_slice() --- frost-core/src/tests/vectors_dkg.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/frost-core/src/tests/vectors_dkg.rs b/frost-core/src/tests/vectors_dkg.rs index fab3dbba..a0b8aba4 100644 --- a/frost-core/src/tests/vectors_dkg.rs +++ b/frost-core/src/tests/vectors_dkg.rs @@ -31,14 +31,16 @@ pub struct DKGTestVectors { fn json_to_scalar( vector: &Value, ) -> <::Field as Field>::Serialization { - <::Field as Field>::Serialization::try_from( - &hex::decode(vector.as_str().unwrap()).unwrap(), - ) - .debugless_unwrap() + (hex::decode(vector.as_str().unwrap()).unwrap()) + .as_slice() + .try_into() + .debugless_unwrap() } fn json_to_element(vector: &Value) -> ::Serialization { - ::Serialization::try_from(&hex::decode(vector.as_str().unwrap()).unwrap()) + (hex::decode(vector.as_str().unwrap()).unwrap()) + .as_slice() + .try_into() .debugless_unwrap() }