diff --git a/frost-core/Cargo.toml b/frost-core/Cargo.toml index e0df64b..7d268fd 100644 --- a/frost-core/Cargo.toml +++ b/frost-core/Cargo.toml @@ -25,6 +25,7 @@ hex = { version = "0.4.3", features = ["serde"] } rand_core = "0.6" serde = { version = "1", optional = true, features = ["derive"] } thiserror = "1.0" +visibility = "0.0.1" zeroize = { version = "1.5.4", default-features = false, features = ["derive"] } # Test dependencies used with the test-impl feature @@ -46,3 +47,4 @@ nightly = [] default = ["serde"] # Exposes ciphersuite-generic tests for other crates to use test-impl = ["proptest", "proptest-derive", "serde_json"] +internals = [] diff --git a/frost-core/src/frost.rs b/frost-core/src/frost.rs index 113a25c..865ed36 100644 --- a/frost-core/src/frost.rs +++ b/frost-core/src/frost.rs @@ -12,7 +12,6 @@ use std::{ collections::{BTreeMap, HashMap}, - convert::TryFrom, fmt::{self, Debug}, ops::Index, }; @@ -41,6 +40,18 @@ impl BindingFactor where C: Ciphersuite, { + /// Create a new [`Rho`] from the given scalar. + #[cfg(feature = "internals")] + pub fn new(scalar: <::Field as Field>::Scalar) -> Self { + Self(scalar) + } + + /// Return the underlying scalar. + #[cfg(feature = "internals")] + pub fn to_scalar(self) -> <<::Group as Group>::Field as Field>::Scalar { + self.0 + } + /// Deserializes [`BindingFactor`] from bytes. pub fn from_bytes( bytes: <::Field as Field>::Serialization, @@ -73,6 +84,12 @@ impl BindingFactorList where C: Ciphersuite, { + /// Create a new [`BindingFactorList`] from a vector of binding factors. + #[cfg(feature = "internals")] + pub fn new(binding_factors: BTreeMap, BindingFactor>) -> Self { + Self(binding_factors) + } + /// Return iterator through all factors. pub fn iter(&self) -> impl Iterator, &BindingFactor)> { self.0.iter() @@ -136,6 +153,7 @@ where // TODO: pub struct Lagrange(Scalar); /// Generates the lagrange coefficient for the i'th participant. +#[cfg_attr(feature = "internals", visibility::make(pub))] fn derive_lagrange_coeff( signer_id: &Identifier, signing_package: &SigningPackage, @@ -185,7 +203,7 @@ impl SigningPackage where C: Ciphersuite, { - /// Create a new `SigingPackage` + /// Create a new `SigningPackage` /// /// The `signing_commitments` are sorted by participant `identifier`. pub fn new( @@ -244,9 +262,25 @@ where /// The product of all signers' individual commitments, published as part of the /// final signature. -#[derive(PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq)] pub struct GroupCommitment(pub(super) Element); +impl GroupCommitment +where + C: Ciphersuite, +{ + /// Create a new `GroupCommitment` + pub fn new(element: ::Element) -> Self { + Self(element) + } + + /// Return the underlying element. + #[cfg(feature = "internals")] + pub fn to_element(self) -> ::Element { + self.0 + } +} + // impl Debug for GroupCommitment where C: Ciphersuite { // fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // f.debug_tuple("GroupCommitment") @@ -255,43 +289,41 @@ pub struct GroupCommitment(pub(super) Element); // } // } -impl TryFrom<&SigningPackage> for GroupCommitment +/// Generates the group commitment which is published as part of the joint +/// Schnorr signature. +/// +/// Implements [`compute_group_commitment`] from the spec. +/// +/// [`compute_group_commitment`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-4.5 +#[cfg_attr(feature = "internals", visibility::make(pub))] +fn compute_group_commitment( + signing_package: &SigningPackage, + binding_factor_list: &BindingFactorList, +) -> Result, Error> where C: Ciphersuite, { - type Error = Error; + let identity = ::identity(); - /// Generates the group commitment which is published as part of the joint - /// Schnorr signature. - /// - /// Implements [`compute_group_commitment`] from the spec. - /// - /// [`compute_group_commitment`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#section-4.5 - fn try_from(signing_package: &SigningPackage) -> Result, Error> { - let binding_factor_list: BindingFactorList = signing_package.into(); + let mut group_commitment = ::identity(); - let identity = ::identity(); - - let mut group_commitment = ::identity(); - - // Ala the sorting of B, just always sort by identifier in ascending order - // - // https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding - for commitment in signing_package.signing_commitments() { - // The following check prevents a party from accidentally revealing their share. - // Note that the '&&' operator would be sufficient. - if identity == commitment.binding.0 || identity == commitment.hiding.0 { - return Err(Error::IdentityCommitment); - } - - let binding_factor = binding_factor_list[commitment.identifier].clone(); - - group_commitment = group_commitment - + (commitment.hiding.0 + (commitment.binding.0 * binding_factor.0)); + // Ala the sorting of B, just always sort by identifier in ascending order + // + // https://github.com/cfrg/draft-irtf-cfrg-frost/blob/master/draft-irtf-cfrg-frost.md#encoding-operations-dep-encoding + for commitment in signing_package.signing_commitments() { + // The following check prevents a party from accidentally revealing their share. + // Note that the '&&' operator would be sufficient. + if identity == commitment.binding.0 || identity == commitment.hiding.0 { + return Err(Error::IdentityCommitment); } - Ok(GroupCommitment(group_commitment)) + let binding_factor = binding_factor_list[commitment.identifier].clone(); + + group_commitment = + group_commitment + (commitment.hiding.0 + (commitment.binding.0 * binding_factor.0)); } + + Ok(GroupCommitment::new(group_commitment)) } //////////////////////////////////////////////////////////////////////////////// @@ -326,7 +358,7 @@ where let binding_factor_list: BindingFactorList = signing_package.into(); // Compute the group commitment from signing commitments produced in round one. - let group_commitment = GroupCommitment::::try_from(signing_package)?; + let group_commitment = compute_group_commitment(signing_package, &binding_factor_list)?; // Compute the per-message challenge. let challenge = crate::challenge::( diff --git a/frost-core/src/frost/identifier.rs b/frost-core/src/frost/identifier.rs index c0cff73..d02ecef 100644 --- a/frost-core/src/frost/identifier.rs +++ b/frost-core/src/frost/identifier.rs @@ -19,7 +19,8 @@ impl Identifier where C: Ciphersuite, { - // Serialize the underlying scalar. + /// Serialize the underlying scalar. + #[cfg_attr(feature = "internals", visibility::make(pub))] pub(crate) fn serialize(&self) -> <::Field as Field>::Serialization { <::Field>::serialize(&self.0) } diff --git a/frost-core/src/frost/keys.rs b/frost-core/src/frost/keys.rs index f70ea0a..e52693e 100644 --- a/frost-core/src/frost/keys.rs +++ b/frost-core/src/frost/keys.rs @@ -121,6 +121,12 @@ impl SigningShare where C: Ciphersuite, { + /// Return the underlying scalar. + #[cfg(feature = "internals")] + pub fn to_scalar(self) -> <<::Group as Group>::Field as Field>::Scalar { + self.0 + } + /// Deserialize from bytes pub fn from_bytes( bytes: <::Field as Field>::Serialization, diff --git a/frost-core/src/frost/round1.rs b/frost-core/src/frost/round1.rs index 168582d..1b17130 100644 --- a/frost-core/src/frost/round1.rs +++ b/frost-core/src/frost/round1.rs @@ -54,6 +54,12 @@ where Self(C::H3(input.as_slice())) } + /// Return the underlying scalar. + #[cfg(feature = "internals")] + pub fn to_scalar(self) -> <<::Group as Group>::Field as Field>::Scalar { + self.0 + } + /// Deserialize [`Nonce`] from bytes pub fn from_bytes( bytes: <::Field as Field>::Serialization, @@ -100,6 +106,12 @@ impl NonceCommitment where C: Ciphersuite, { + /// Return the underlying element. + #[cfg(feature = "internals")] + pub fn to_element(self) -> ::Element { + self.0 + } + /// Deserialize [`NonceCommitment`] from bytes pub fn from_bytes(bytes: ::Serialization) -> Result { ::deserialize(&bytes).map(|element| Self(element)) @@ -221,6 +233,7 @@ where /// Computes the [signature commitment share] from these round one signing commitments. /// /// [signature commitment share]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#name-signature-share-verificatio + #[cfg_attr(feature = "internals", visibility::make(pub))] pub(super) fn to_group_commitment_share( self, binding_factor: &frost::BindingFactor, @@ -271,6 +284,7 @@ pub struct GroupCommitmentShare(pub(super) Element); /// - A byte string containing the serialized representation of B. /// /// [`encode_group_commitment_list()`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-11.html#name-list-operations +#[cfg_attr(feature = "internals", visibility::make(pub))] pub(super) fn encode_group_commitments( signing_commitments: Vec>, ) -> Vec { diff --git a/frost-core/src/frost/round2.rs b/frost-core/src/frost/round2.rs index 55a57bf..82e1306 100644 --- a/frost-core/src/frost/round2.rs +++ b/frost-core/src/frost/round2.rs @@ -139,7 +139,7 @@ pub fn sign( binding_factor_list[key_package.identifier].clone(); // Compute the group commitment from signing commitments produced in round one. - let group_commitment = GroupCommitment::::try_from(signing_package)?; + let group_commitment = compute_group_commitment(signing_package, &binding_factor_list)?; // Compute Lagrange coefficient. let lambda_i = frost::derive_lagrange_coeff(key_package.identifier(), signing_package)?; diff --git a/frost-core/src/lib.rs b/frost-core/src/lib.rs index 001d3e7..e22e74d 100644 --- a/frost-core/src/lib.rs +++ b/frost-core/src/lib.rs @@ -241,6 +241,17 @@ pub trait Ciphersuite: Copy + Clone + PartialEq { #[derive(Clone)] pub struct Challenge(pub(crate) <::Field as Field>::Scalar); +impl Challenge +where + C: Ciphersuite, +{ + /// Return the underlying scalar. + #[cfg(feature = "internals")] + pub fn to_scalar(self) -> <<::Group as Group>::Field as Field>::Scalar { + self.0 + } +} + impl Debug for Challenge where C: Ciphersuite, @@ -263,6 +274,7 @@ where /// /// [FROST]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#name-signature-challenge-computa /// [RFC]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-10.html#section-3.2 +#[cfg_attr(feature = "internals", visibility::make(pub))] fn challenge(R: &Element, verifying_key: &Element, msg: &[u8]) -> Challenge where C: Ciphersuite, diff --git a/frost-core/src/signature.rs b/frost-core/src/signature.rs index 5499e56..6a9aece 100644 --- a/frost-core/src/signature.rs +++ b/frost-core/src/signature.rs @@ -20,6 +20,15 @@ where C::Group: Group, ::Field: Field, { + /// Create a new Signature. + #[cfg(feature = "internals")] + pub fn new( + R: ::Element, + z: <::Field as Field>::Scalar, + ) -> Self { + Self { R, z } + } + /// Converts bytes as [`Ciphersuite::SignatureSerialization`] into a `Signature`. pub fn from_bytes(bytes: C::SignatureSerialization) -> Result { // To compute the expected length of the encoded point, encode the generator diff --git a/frost-core/src/verifying_key.rs b/frost-core/src/verifying_key.rs index 1338412..e828f07 100644 --- a/frost-core/src/verifying_key.rs +++ b/frost-core/src/verifying_key.rs @@ -23,6 +23,18 @@ where // VerifyingKey { element } // } + /// Create a new VerifyingKey from the given element. + #[cfg(feature = "internals")] + pub fn new(element: ::Element) -> Self { + Self { element } + } + + /// Return the underlying element. + #[cfg(feature = "internals")] + pub fn to_element(self) -> ::Element { + self.element + } + /// Deserialize from bytes pub fn from_bytes(bytes: ::Serialization) -> Result, Error> { ::deserialize(&bytes).map(|element| VerifyingKey { element })