Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(frost-core): simplify trait bounds, don't use the allocator in some places #810

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions frost-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -272,12 +272,13 @@ where
fn from_hex<T: AsRef<[u8]>>(hex: T) -> Result<Self, Self::Error> {
let v: Vec<u8> = FromHex::from_hex(hex).map_err(|_| "invalid hex")?;

match v.try_into() {
let ret = match v.as_slice().try_into() {
Ok(bytes) => <<C::Group as Group>::Field>::deserialize(&bytes)
.map(|scalar| Self(scalar))
.map_err(|_| "malformed scalar encoding"),
Err(_) => Err("malformed scalar encoding"),
}
};
ret
}
}

Expand Down
48 changes: 17 additions & 31 deletions frost-core/src/serialization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,8 @@ where

/// Deserialize a Scalar from a serialized buffer.
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error<C>> {
let serialized: <<C::Group as Group>::Field as Field>::Serialization = bytes
.to_vec()
.try_into()
.map_err(|_| FieldError::MalformedScalar)?;
let serialized: <<C::Group as Group>::Field as Field>::Serialization =
bytes.try_into().map_err(|_| FieldError::MalformedScalar)?;
let scalar = <<C::Group as Group>::Field>::deserialize(&serialized)?;
Ok(Self(scalar))
}
Expand Down Expand Up @@ -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 = <<C::Group as Group>::Field as Field>::zero();
let len = <<C::Group as Group>::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"))?;
<<C as Ciphersuite>::Group as Group>::Field::deserialize(&array)
let mut serialization = <<C::Group as Group>::Field as Field>::serialize(&zero);

serdect::array::deserialize_hex_or_bin(serialization.as_mut(), deserializer)?;

<<C as Ciphersuite>::Group as Group>::Field::deserialize(&serialization)
.map(|scalar| Self(scalar))
.map_err(serde::de::Error::custom)
}
Expand All @@ -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<Self, Error<C>> {
let serialized: <C::Group as Group>::Serialization = bytes
.to_vec()
.try_into()
.map_err(|_| FieldError::MalformedScalar)?;
let serialized: <C::Group as Group>::Serialization =
bytes.try_into().map_err(|_| FieldError::MalformedScalar)?;
let scalar = <C::Group as Group>::deserialize(&serialized)?;
Ok(Self(scalar))
}
Expand Down Expand Up @@ -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 = <C::Group>::generator();
let len = <C::Group>::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"))?;
<C::Group as Group>::deserialize(&array)
let mut serialization =
<C::Group>::serialize(&generator).expect("serializing the generator always works");

serdect::array::deserialize_hex_or_bin(serialization.as_mut(), deserializer)?;

<C::Group as Group>::deserialize(&serialization)
.map(|element| Self(element))
.map_err(serde::de::Error::custom)
}
Expand Down
37 changes: 20 additions & 17 deletions frost-core/src/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <C::Group>::generator();
let mut R_bytes = Vec::from(<C::Group>::serialize(&generator)?.as_ref());
let R_bytes_len = R_bytes.len();
let mut R_serialization = <C::Group>::serialize(&generator)?;
let R_bytes_len = R_serialization.as_ref().len();

let one = <<C::Group as Group>::Field as Field>::zero();
let mut z_bytes =
Vec::from(<<C::Group as Group>::Field as Field>::serialize(&one).as_ref());
let z_bytes_len = z_bytes.len();
let zero = <<C::Group as Group>::Field as Field>::zero();
let mut z_serialization = <<C::Group as Group>::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: <C::Group>::deserialize(R_serialization)?,
z: <<C::Group as Group>::Field>::deserialize(z_serialization)?,
R: <C::Group>::deserialize(&R_serialization)?,
z: <<C::Group as Group>::Field>::deserialize(&z_serialization)?,
})
}

Expand All @@ -77,10 +74,16 @@ where
/// Converts this signature to its default byte serialization.
#[cfg(feature = "internals")]
pub fn default_serialize(&self) -> Result<Vec<u8>, Error<C>> {
let mut bytes = Vec::<u8>::new();
let R_serialization = <C::Group>::serialize(&self.R)?;
let z_serialization = <<C::Group as Group>::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(<C::Group>::serialize(&self.R)?.as_ref());
bytes.extend(<<C::Group as Group>::Field>::serialize(&self.z).as_ref());
bytes.extend(R_bytes);
bytes.extend(z_bytes);

Ok(bytes)
}
Expand Down
2 changes: 1 addition & 1 deletion frost-core/src/tests/coefficient_commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ pub fn check_create_coefficient_commitment_error<C: Ciphersuite + PartialEq>(
let values = &commitment_helpers["elements"];
let serialized: <C::Group as Group>::Serialization =
<C::Group as Group>::Serialization::try_from(
hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(),
&hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(),
)
.debugless_unwrap();

Expand Down
10 changes: 6 additions & 4 deletions frost-core/src/tests/repairable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,11 @@ pub fn check_rts<C: Ciphersuite, R: RngCore + CryptoRng>(mut rng: R) {

fn generate_scalar_from_byte_string<C: Ciphersuite>(
bs: &str,
) -> <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar {
) -> <<C::Group as Group>::Field as Field>::Scalar {
let decoded = hex::decode(bs).unwrap();
let out = <<C::Group as Group>::Field>::deserialize(&decoded.try_into().debugless_unwrap());
let out = <<C::Group as Group>::Field>::deserialize(
&decoded.as_slice().try_into().debugless_unwrap(),
);
out.unwrap()
}

Expand Down Expand Up @@ -160,7 +162,7 @@ pub fn check_repair_share_step_2<C: Ciphersuite>(repair_share_helpers: &Value) {

let expected: Scalar<C> = repair_share_step_2::<C>(&[value_1, value_2, value_3]);

let actual: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar =
let actual: <<C::Group as Group>::Field as Field>::Scalar =
generate_scalar_from_byte_string::<C>(values["random_scalar_sum"].as_str().unwrap());

assert!(actual == expected);
Expand Down Expand Up @@ -198,7 +200,7 @@ pub fn check_repair_share_step_3<C: Ciphersuite, R: RngCore + CryptoRng>(
&commitment,
);

let actual_sigma: <<<C as Ciphersuite>::Group as Group>::Field as Field>::Scalar =
let actual_sigma: <<C::Group as Group>::Field as Field>::Scalar =
generate_scalar_from_byte_string::<C>(sigmas["sigma_sum"].as_str().unwrap());
let actual: SecretShare<C> = SecretShare::new(
Identifier::try_from(2).unwrap(),
Expand Down
3 changes: 2 additions & 1 deletion frost-core/src/tests/vectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ pub fn parse_test_vectors<C: Ciphersuite>(json_vectors: &Value) -> TestVectors<C
.iter()
.map(|v| {
let vec = hex::decode(v.as_str().unwrap()).unwrap();
<<C::Group as Group>::Field>::deserialize(&vec.try_into().debugless_unwrap()).unwrap()
<<C::Group as Group>::Field>::deserialize(&vec.as_slice().try_into().debugless_unwrap())
.unwrap()
})
.collect();

Expand Down
2 changes: 2 additions & 0 deletions frost-core/src/tests/vectors_dkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,14 @@ fn json_to_scalar<C: Ciphersuite>(
vector: &Value,
) -> <<C::Group as Group>::Field as Field>::Serialization {
(hex::decode(vector.as_str().unwrap()).unwrap())
.as_slice()
.try_into()
.debugless_unwrap()
}

fn json_to_element<C: Ciphersuite>(vector: &Value) -> <C::Group as Group>::Serialization {
(hex::decode(vector.as_str().unwrap()).unwrap())
.as_slice()
.try_into()
.debugless_unwrap()
}
Expand Down
2 changes: 1 addition & 1 deletion frost-core/src/tests/vss_commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ pub fn check_deserialize_vss_commitment_error<C: Ciphersuite, R: RngCore + Crypt

let serialized: <C::Group as Group>::Serialization =
<C::Group as Group>::Serialization::try_from(
hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(),
&hex::decode(values["invalid_element"].as_str().unwrap()).unwrap(),
)
.debugless_unwrap();
// ---
Expand Down
16 changes: 10 additions & 6 deletions frost-core/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Output = Self::Scalar>
Expand All @@ -37,7 +37,7 @@ pub trait Field: Copy + Clone {
+ Sub<Output = Self::Scalar>;

/// A unique byte array buf of fixed length N.
type Serialization: AsRef<[u8]> + Debug + TryFrom<Vec<u8>>;
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;
Expand Down Expand Up @@ -85,7 +85,7 @@ pub type Scalar<C> = <<<C as Ciphersuite>::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;
Expand All @@ -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<Vec<u8>>;
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.
Expand Down Expand Up @@ -147,7 +147,7 @@ pub type Element<C> = <<C as Ciphersuite>::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
Expand All @@ -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<Vec<u8>>;
type SignatureSerialization: Copy
+ AsRef<[u8]>
+ AsMut<[u8]>
+ for<'a> TryFrom<&'a [u8]>
+ Debug;

/// [H1] for a FROST ciphersuite.
///
Expand Down