diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 000000000..bbe917d87 Binary files /dev/null and b/.DS_Store differ diff --git a/ed25519-dalek/src/olaf/frost/errors.rs b/ed25519-dalek/src/olaf/frost/errors.rs index a21709035..5e3662f03 100644 --- a/ed25519-dalek/src/olaf/frost/errors.rs +++ b/ed25519-dalek/src/olaf/frost/errors.rs @@ -1,6 +1,13 @@ //! Errors of the FROST protocol. -use crate::SignatureError; +use core::array::TryFromSliceError; + +use alloc::vec::Vec; + +use crate::{ + olaf::{simplpedpop::errors::SPPError, VerifyingShare}, + SignatureError, +}; /// A result for the SimplPedPoP protocol. pub type FROSTResult = Result; @@ -8,24 +15,39 @@ pub type FROSTResult = Result; /// An error ocurred during the execution of the SimplPedPoP protocol. #[derive(Debug)] pub enum FROSTError { - /// Invalid Proof of Possession. - InvalidProofOfPossession(SignatureError), /// The number of signing commitments must be at least equal to the threshold. InvalidNumberOfSigningCommitments, - /// The number of signing commitments must be equal to the number of signature shares. - IncorrectNumberOfSigningCommitments, /// The participant's signing commitment is missing. MissingOwnSigningCommitment, /// Commitment equals the identity IdentitySigningCommitment, - /// The number of veriyfing shares must be equal to the number of participants. + /// The number of veriyfing shares must be equal to the number of signers. IncorrectNumberOfVerifyingShares, - /// The identifiers of the SimplPedPoP protocol must be the same of the FROST protocol. - InvalidIdentifier, + /// Error deserializing the signature share. + SignatureShareDeserializationError, + /// The signature share is invalid. + InvalidSignatureShare { + /// The verifying share(s) of the culprit(s). + culprit: Vec, + }, /// The output of the SimplPedPoP protocol must contain the participant's verifying share. InvalidOwnVerifyingShare, /// Invalid signature. InvalidSignature(SignatureError), + /// Deserialization error. + DeserializationError(TryFromSliceError), + /// Invalid nonce commitment. + InvalidNonceCommitment, + /// Error deserializing the output of the SimplPedPoP protocol. + SPPOutputDeserializationError(SPPError), + /// The number of signing packages must be at least equal to the threshold. + InvalidNumberOfSigningPackages, + /// The common data of all the signing packages must be the same. + MismatchedCommonData, + /// The number of signature shares and the number of signing commitments must be the same. + MismatchedSignatureSharesAndSigningCommitments, + /// The signing packages are empty. + EmptySigningPackages, } #[cfg(test)] @@ -75,29 +97,29 @@ mod tests { all_messages.push(message); } - let mut dkg_outputs = Vec::new(); + let mut spp_outputs = Vec::new(); for kp in &mut keypairs { - let dkg_output = kp.simplpedpop_recipient_all(&all_messages).unwrap(); - dkg_outputs.push(dkg_output); + let spp_output = kp.simplpedpop_recipient_all(&all_messages).unwrap(); + spp_outputs.push(spp_output); } let mut all_signing_commitments = Vec::new(); let mut all_signing_nonces = Vec::new(); - for dkg_output in &dkg_outputs { - let (signing_nonces, signing_commitments) = dkg_output.1.commit(&mut OsRng); + for spp_output in &spp_outputs { + let (signing_nonces, signing_commitments) = spp_output.1.commit(&mut OsRng); all_signing_nonces.push(signing_nonces); all_signing_commitments.push(signing_commitments); } let message = b"message"; - dkg_outputs[0].1 = SigningKeypair(SigningKey::generate(&mut rng)); + spp_outputs[0].1 = SigningKeypair(SigningKey::generate(&mut rng)); - let result = dkg_outputs[0].1.sign( + let result = spp_outputs[0].1.sign( message, - &dkg_outputs[0].0.dkg_output, + &spp_outputs[0].0.spp_output, &all_signing_commitments, &all_signing_nonces[0], ); @@ -136,29 +158,29 @@ mod tests { all_messages.push(message); } - let mut dkg_outputs = Vec::new(); + let mut spp_outputs = Vec::new(); for kp in &mut keypairs { - let dkg_output = kp.simplpedpop_recipient_all(&all_messages).unwrap(); - dkg_outputs.push(dkg_output); + let spp_output = kp.simplpedpop_recipient_all(&all_messages).unwrap(); + spp_outputs.push(spp_output); } let mut all_signing_commitments = Vec::new(); let mut all_signing_nonces = Vec::new(); - for dkg_output in &dkg_outputs { - let (signing_nonces, signing_commitments) = dkg_output.1.commit(&mut OsRng); + for spp_output in &spp_outputs { + let (signing_nonces, signing_commitments) = spp_output.1.commit(&mut OsRng); all_signing_nonces.push(signing_nonces); all_signing_commitments.push(signing_commitments); } let message = b"message"; - dkg_outputs[0].0.dkg_output.verifying_keys.pop(); + spp_outputs[0].0.spp_output.verifying_keys.pop(); - let result = dkg_outputs[0].1.sign( + let result = spp_outputs[0].1.sign( message, - &dkg_outputs[0].0.dkg_output, + &spp_outputs[0].0.spp_output, &all_signing_commitments, &all_signing_nonces[0], ); @@ -197,18 +219,18 @@ mod tests { all_messages.push(message); } - let mut dkg_outputs = Vec::new(); + let mut spp_outputs = Vec::new(); for kp in &mut keypairs { - let dkg_output = kp.simplpedpop_recipient_all(&all_messages).unwrap(); - dkg_outputs.push(dkg_output); + let spp_output = kp.simplpedpop_recipient_all(&all_messages).unwrap(); + spp_outputs.push(spp_output); } let mut all_signing_commitments = Vec::new(); let mut all_signing_nonces = Vec::new(); - for dkg_output in &dkg_outputs { - let (signing_nonces, signing_commitments) = dkg_output.1.commit(&mut OsRng); + for spp_output in &spp_outputs { + let (signing_nonces, signing_commitments) = spp_output.1.commit(&mut OsRng); all_signing_nonces.push(signing_nonces); all_signing_commitments.push(signing_commitments); } @@ -220,9 +242,9 @@ mod tests { binding: NonceCommitment(Scalar::random(&mut OsRng) * GENERATOR), }; - let result = dkg_outputs[0].1.sign( + let result = spp_outputs[0].1.sign( message, - &dkg_outputs[0].0.dkg_output, + &spp_outputs[0].0.spp_output, &all_signing_commitments, &all_signing_nonces[0], ); @@ -261,18 +283,18 @@ mod tests { all_messages.push(message); } - let mut dkg_outputs = Vec::new(); + let mut spp_outputs = Vec::new(); for kp in &mut keypairs { - let dkg_output = kp.simplpedpop_recipient_all(&all_messages).unwrap(); - dkg_outputs.push(dkg_output); + let spp_output = kp.simplpedpop_recipient_all(&all_messages).unwrap(); + spp_outputs.push(spp_output); } let mut all_signing_commitments = Vec::new(); let mut all_signing_nonces = Vec::new(); - for dkg_output in &dkg_outputs { - let (signing_nonces, signing_commitments) = dkg_output.1.commit(&mut OsRng); + for spp_output in &spp_outputs { + let (signing_nonces, signing_commitments) = spp_output.1.commit(&mut OsRng); all_signing_nonces.push(signing_nonces); all_signing_commitments.push(signing_commitments); } @@ -280,9 +302,9 @@ mod tests { let message = b"message"; all_signing_commitments[1].hiding = NonceCommitment(EdwardsPoint::identity()); - let result = dkg_outputs[0].1.sign( + let result = spp_outputs[0].1.sign( message, - &dkg_outputs[0].0.dkg_output, + &spp_outputs[0].0.spp_output, &all_signing_commitments, &all_signing_nonces[0], ); @@ -321,27 +343,27 @@ mod tests { all_messages.push(message); } - let mut dkg_outputs = Vec::new(); + let mut spp_outputs = Vec::new(); for kp in &mut keypairs { - let dkg_output = kp.simplpedpop_recipient_all(&all_messages).unwrap(); - dkg_outputs.push(dkg_output); + let spp_output = kp.simplpedpop_recipient_all(&all_messages).unwrap(); + spp_outputs.push(spp_output); } let mut all_signing_commitments = Vec::new(); let mut all_signing_nonces = Vec::new(); - for dkg_output in &dkg_outputs { - let (signing_nonces, signing_commitments) = dkg_output.1.commit(&mut OsRng); + for spp_output in &spp_outputs { + let (signing_nonces, signing_commitments) = spp_output.1.commit(&mut OsRng); all_signing_nonces.push(signing_nonces); all_signing_commitments.push(signing_commitments); } let message = b"message"; - let result = dkg_outputs[0].1.sign( + let result = spp_outputs[0].1.sign( message, - &dkg_outputs[0].0.dkg_output, + &spp_outputs[0].0.spp_output, &all_signing_commitments[..1], &all_signing_nonces[0], ); diff --git a/ed25519-dalek/src/olaf/frost/mod.rs b/ed25519-dalek/src/olaf/frost/mod.rs index 58cbeda1f..92769b323 100644 --- a/ed25519-dalek/src/olaf/frost/mod.rs +++ b/ed25519-dalek/src/olaf/frost/mod.rs @@ -13,12 +13,12 @@ use self::{ }, }; use super::{ - simplpedpop::{DKGOutput, SecretPolynomial}, - SigningKeypair, ThresholdPublicKey, VerifyingShare, + simplpedpop::SPPOutput, Identifier, SigningKeypair, ThresholdPublicKey, VerifyingShare, }; use alloc::vec::Vec; use curve25519_dalek::Scalar; use ed25519::{signature::Verifier, Signature}; +use merlin::Transcript; use rand_core::{CryptoRng, RngCore}; use sha2::{Digest, Sha512}; @@ -73,6 +73,18 @@ impl SigningKeypair { ) } + /// Performed once by each participant selected for the signing operation. + /// + /// Implements [`sign`] from the spec. + /// + /// Receives the message to be signed and a set of signing commitments and a set + /// of randomizing commitments to be used in that signing operation, including + /// that for this participant. + /// + /// Assumes the participant has already determined which nonce corresponds with + /// the commitment that was assigned by the coordinator in the SigningPackage. + /// + /// [`sign`]: https://www.ietf.org/archive/id/draft-irtf-cfrg-frost-14.html#name-round-two-signature-share-g /// Performed once by each participant selected for the signing operation. /// /// Implements [`sign`] from the spec. @@ -88,7 +100,7 @@ impl SigningKeypair { pub fn sign( &self, message: &[u8], - dkg_output: &DKGOutput, + dkg_output: &SPPOutput, all_signing_commitments: &[SigningCommitments], signer_nonces: &SigningNonces, ) -> FROSTResult { @@ -126,7 +138,7 @@ impl SigningKeypair { let binding_factor_list: BindingFactorList = BindingFactorList::compute( all_signing_commitments, - &dkg_output.group_public_key, + &dkg_output.threshold_public_key, message, &[], ); @@ -136,16 +148,12 @@ impl SigningKeypair { let identifiers_vec: Vec<_> = dkg_output.verifying_keys.iter().map(|x| x.0).collect(); - let lambda_i = SecretPolynomial::compute_lagrange_coefficient( - &identifiers_vec, - None, - *identifiers[index], - ); + let lambda_i = compute_lagrange_coefficient(&identifiers_vec, None, *identifiers[index]); let mut preimage = vec![]; preimage.extend_from_slice(group_commitment.0.compress().as_bytes()); - preimage.extend_from_slice(dkg_output.group_public_key.0.as_bytes()); + preimage.extend_from_slice(dkg_output.threshold_public_key.0.as_bytes()); preimage.extend_from_slice(&message); let challenge = hash_to_scalar(&[&preimage[..]]); @@ -200,7 +208,7 @@ pub fn aggregate( group_public_key: ThresholdPublicKey, ) -> Result { if signing_commitments.len() != signature_shares.len() { - return Err(FROSTError::IncorrectNumberOfSigningCommitments); + //return Err(FROSTError::IncorrectNumberOfSigningCommitments); } let binding_factor_list: BindingFactorList = @@ -243,6 +251,43 @@ pub(super) fn hash_to_scalar(inputs: &[&[u8]]) -> Scalar { Scalar::from_bytes_mod_order_wide(&output) } +/// Generates a lagrange coefficient. +/// +/// The Lagrange polynomial for a set of points (x_j, y_j) for 0 <= j <= k +/// is ∑_{i=0}^k y_i.ℓ_i(x), where ℓ_i(x) is the Lagrange basis polynomial: +/// +/// ℓ_i(x) = ∏_{0≤j≤k; j≠i} (x - x_j) / (x_i - x_j). +/// +/// This computes ℓ_j(x) for the set of points `xs` and for the j corresponding +/// to the given xj. +/// +/// If `x` is None, it uses 0 for it (since Identifiers can't be 0). +pub(super) fn compute_lagrange_coefficient( + x_set: &[Identifier], + x: Option, + x_i: Identifier, +) -> Scalar { + let mut num = Scalar::ONE; + let mut den = Scalar::ONE; + + for x_j in x_set.iter() { + if x_i == *x_j { + continue; + } + + if let Some(x) = x { + num *= x.0 - x_j.0; + den *= x_i.0 - x_j.0; + } else { + // Both signs inverted just to avoid requiring Neg (-*xj) + num *= x_j.0; + den *= x_j.0 - x_i.0; + } + } + + num * den.invert() +} + #[cfg(test)] mod tests { use super::{ @@ -318,7 +363,7 @@ mod tests { .1 .sign( message, - &dkg_output.0.dkg_output, + &dkg_output.0.spp_output, &all_signing_commitments, &all_signing_nonces[i], ) @@ -331,7 +376,7 @@ mod tests { message, &all_signing_commitments, &signature_shares, - dkg_outputs[0].0.dkg_output.group_public_key, + dkg_outputs[0].0.spp_output.threshold_public_key, ) .unwrap(); } @@ -381,7 +426,7 @@ mod tests { .1 .sign( message, - &dkg_output.0.dkg_output, + &dkg_output.0.spp_output, &all_signing_commitments, &all_signing_nonces[i], ) @@ -394,7 +439,7 @@ mod tests { message, &all_signing_commitments, &signature_shares, - dkg_outputs[0].0.dkg_output.group_public_key, + dkg_outputs[0].0.spp_output.threshold_public_key, ) .unwrap(); } @@ -427,7 +472,7 @@ mod tests { dkg_outputs.push(dkg_output); } - let group_public_key = dkg_outputs[0].0.dkg_output.group_public_key; + let group_public_key = dkg_outputs[0].0.spp_output.threshold_public_key; let mut all_nonces_map: Vec> = Vec::new(); let mut all_commitments_map: Vec> = Vec::new(); @@ -474,7 +519,7 @@ mod tests { .1 .sign( &message, - &dkg_output.0.dkg_output, + &dkg_output.0.spp_output, &commitments, nonces_to_use, ) diff --git a/ed25519-dalek/src/olaf/frost/types.rs b/ed25519-dalek/src/olaf/frost/types.rs index 927821d0e..04d405660 100644 --- a/ed25519-dalek/src/olaf/frost/types.rs +++ b/ed25519-dalek/src/olaf/frost/types.rs @@ -1,12 +1,19 @@ //! Internal types of the FROST protocol. -use super::{errors::FROSTError, hash_to_array, hash_to_scalar}; +use super::{ + errors::{FROSTError, FROSTResult}, + hash_to_array, hash_to_scalar, +}; use crate::{ - olaf::{ThresholdPublicKey, GENERATOR}, + olaf::{ + scalar_from_canonical_bytes, simplpedpop::SPPOutput, ThresholdPublicKey, VerifyingShare, + COMPRESSED_EDWARDS_LENGTH, GENERATOR, SCALAR_LENGTH, + }, SecretKey, }; use alloc::vec::Vec; use curve25519_dalek::{ + edwards::CompressedEdwardsY, traits::{Identity, VartimeMultiscalarMul}, EdwardsPoint, Scalar, }; @@ -16,11 +23,40 @@ use zeroize::ZeroizeOnDrop; /// A participant's signature share, which the coordinator will aggregate with all other signer's /// shares into the joint signature. -pub struct SignatureShare { +#[derive(Clone, PartialEq, Eq)] +pub(super) struct SignatureShare { /// This participant's signature over the message. pub(super) share: Scalar, } +impl SignatureShare { + fn to_bytes(&self) -> [u8; SCALAR_LENGTH] { + self.share.to_bytes() + } + + fn from_bytes(bytes: &[u8]) -> FROSTResult { + let mut share_bytes = [0; SCALAR_LENGTH]; + share_bytes.copy_from_slice(&bytes[..SCALAR_LENGTH]); + let share = scalar_from_canonical_bytes(share_bytes) + .ok_or(FROSTError::SignatureShareDeserializationError)?; + + Ok(SignatureShare { share }) + } + + pub(super) fn verify( + &self, + group_commitment_share: &GroupCommitmentShare, + verifying_share: &VerifyingShare, + lambda_i: Scalar, + challenge: &Scalar, + ) -> bool { + (GENERATOR * self.share) + == (group_commitment_share.0 + (verifying_share.0.point * challenge * lambda_i)) + } +} + +pub(super) struct GroupCommitmentShare(pub(super) EdwardsPoint); + /// The binding factor, also known as _rho_ (ρ), ensures each signature share is strongly bound to a signing set, specific set /// of commitments, and a specific message. pub(super) struct BindingFactor(pub(super) Scalar); @@ -109,7 +145,7 @@ impl BindingFactorList { } /// A scalar that is a signing nonce. -#[derive(ZeroizeOnDrop)] +#[derive(Debug, Clone, ZeroizeOnDrop, PartialEq, Eq)] pub(super) struct Nonce(pub(super) Scalar); impl Nonce { @@ -148,15 +184,35 @@ impl Nonce { Self(nonce) } + + fn to_bytes(&self) -> [u8; SCALAR_LENGTH] { + self.0.to_bytes() + } + + fn from_bytes(bytes: [u8; SCALAR_LENGTH]) -> Self { + Nonce(Scalar::from_bytes_mod_order(bytes)) + } } /// A group element that is a commitment to a signing nonce share. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(super) struct NonceCommitment(pub(super) EdwardsPoint); -impl From for NonceCommitment { - fn from(nonce: Nonce) -> Self { - From::from(&nonce) +impl NonceCommitment { + fn to_bytes(self) -> [u8; COMPRESSED_EDWARDS_LENGTH] { + self.0.compress().to_bytes() + } + + /// Deserializes the `NonceCommitment` from bytes. + fn from_bytes(bytes: &[u8]) -> FROSTResult { + let compressed = CompressedEdwardsY::from_slice(&bytes[..COMPRESSED_EDWARDS_LENGTH]) + .map_err(FROSTError::DeserializationError)?; + + let point = compressed + .decompress() + .ok_or(FROSTError::InvalidNonceCommitment)?; + + Ok(NonceCommitment(point)) } } @@ -171,7 +227,7 @@ impl From<&Nonce> for NonceCommitment { /// Note that [`SigningNonces`] must be used *only once* for a signing /// operation; re-using nonces will result in leakage of a signer's long-lived /// signing key. -#[derive(ZeroizeOnDrop)] +#[derive(Debug, Clone, ZeroizeOnDrop, PartialEq, Eq)] pub struct SigningNonces { pub(super) hiding: Nonce, pub(super) binding: Nonce, @@ -198,6 +254,42 @@ impl SigningNonces { Self::from_nonces(hiding, binding) } + /// Serializes SigningNonces into bytes. + pub fn to_bytes(self) -> Vec { + let mut bytes = Vec::new(); + + bytes.extend(self.hiding.to_bytes()); + bytes.extend(self.binding.to_bytes()); + bytes.extend(self.commitments.to_bytes()); + + bytes + } + + /// Deserializes SigningNonces from bytes. + pub fn from_bytes(bytes: &[u8]) -> FROSTResult { + let mut cursor = 0; + + let mut hiding_bytes = [0; 32]; + hiding_bytes.copy_from_slice(&bytes[cursor..cursor + SCALAR_LENGTH]); + + let hiding = Nonce::from_bytes(hiding_bytes); + cursor += SCALAR_LENGTH; + + let mut binding_bytes = [0; 32]; + binding_bytes.copy_from_slice(&bytes[cursor..cursor + SCALAR_LENGTH]); + + let binding = Nonce::from_bytes(binding_bytes); + cursor += SCALAR_LENGTH; + + let commitments = SigningCommitments::from_bytes(&bytes[cursor..])?; + + Ok(Self { + hiding, + binding, + commitments, + }) + } + /// Generates a new [`SigningNonces`] from a pair of [`Nonce`]. /// /// # Security @@ -205,7 +297,7 @@ impl SigningNonces { /// SigningNonces MUST NOT be repeated in different FROST signings. /// Thus, if you're using this method (because e.g. you're writing it /// to disk between rounds), be careful so that does not happen. - pub(super) fn from_nonces(hiding: Nonce, binding: Nonce) -> Self { + fn from_nonces(hiding: Nonce, binding: Nonce) -> Self { let hiding_commitment = (&hiding).into(); let binding_commitment = (&binding).into(); let commitments = SigningCommitments::new(hiding_commitment, binding_commitment); @@ -233,6 +325,27 @@ impl SigningCommitments { pub(super) fn new(hiding: NonceCommitment, binding: NonceCommitment) -> Self { Self { hiding, binding } } + + /// Serializes SigningCommitments into bytes. + pub fn to_bytes(self) -> [u8; COMPRESSED_EDWARDS_LENGTH * 2] { + let mut bytes = [0u8; COMPRESSED_EDWARDS_LENGTH * 2]; + + let hiding_bytes = self.hiding.to_bytes(); + let binding_bytes = self.binding.to_bytes(); + + bytes[..COMPRESSED_EDWARDS_LENGTH].copy_from_slice(&hiding_bytes); + bytes[COMPRESSED_EDWARDS_LENGTH..].copy_from_slice(&binding_bytes); + + bytes + } + + /// Deserializes SigningCommitments from bytes. + pub fn from_bytes(bytes: &[u8]) -> FROSTResult { + let hiding = NonceCommitment::from_bytes(&bytes[..COMPRESSED_EDWARDS_LENGTH])?; + let binding = NonceCommitment::from_bytes(&bytes[COMPRESSED_EDWARDS_LENGTH..])?; + + Ok(SigningCommitments { hiding, binding }) + } } impl From<&SigningNonces> for SigningCommitments { @@ -286,3 +399,219 @@ impl GroupCommitment { Ok(GroupCommitment(group_commitment)) } } + +#[derive(Clone, PartialEq, Eq)] +pub(super) struct CommonData { + pub(super) message: Vec, + pub(super) signing_commitments: Vec, + pub(super) spp_output: SPPOutput, +} + +impl CommonData { + /// Serializes CommonData into bytes. + fn to_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + + bytes.extend((self.message.len() as u32).to_le_bytes()); + bytes.extend(&self.message); + + bytes.extend((self.signing_commitments.len() as u32).to_le_bytes()); + for commitment in &self.signing_commitments { + bytes.extend(commitment.to_bytes()); + } + + bytes.extend(self.spp_output.to_bytes()); + + bytes + } + + /// Deserializes CommonData from bytes. + fn from_bytes(bytes: &[u8]) -> FROSTResult { + let mut cursor = 0; + + let message_len = + u32::from_le_bytes(bytes[cursor..cursor + 4].try_into().unwrap()) as usize; + cursor += 4; + let message = bytes[cursor..cursor + message_len].to_vec(); + cursor += message_len; + + let signing_commitments_len = + u32::from_le_bytes(bytes[cursor..cursor + 4].try_into().unwrap()) as usize; + cursor += 4; + let mut signing_commitments = Vec::with_capacity(signing_commitments_len); + for _ in 0..signing_commitments_len { + let commitment_bytes = &bytes[cursor..cursor + 64]; // Assuming each SigningCommitment is 64 bytes + cursor += 64; + signing_commitments.push(SigningCommitments::from_bytes(commitment_bytes)?); + } + + let spp_output = SPPOutput::from_bytes(&bytes[cursor..]) + .map_err(FROSTError::SPPOutputDeserializationError)?; + + Ok(CommonData { + message, + signing_commitments, + spp_output, + }) + } +} + +#[derive(Clone, PartialEq, Eq)] +pub(super) struct SignerData { + pub(super) signature_share: SignatureShare, +} + +impl SignerData { + /// Serializes SignerData into bytes. + fn to_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + + bytes.extend(self.signature_share.to_bytes()); + + bytes + } + + /// Deserializes SignerData from bytes. + pub fn from_bytes(bytes: &[u8]) -> FROSTResult { + let share_bytes = &bytes[..SCALAR_LENGTH]; + let signature_share = SignatureShare::from_bytes(share_bytes)?; + + Ok(SignerData { signature_share }) + } +} + +/// The signing package that each signer produces in the signing round of the FROST protocol and sends to the +/// coordinator, which aggregates them into the final threshold signature. +#[derive(PartialEq, Eq)] +pub struct SigningPackage { + pub(super) signer_data: SignerData, + pub(super) common_data: CommonData, +} + +impl SigningPackage { + /// Serializes SigningPackage into bytes. + pub fn to_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + + bytes.extend(self.signer_data.to_bytes()); + bytes.extend(self.common_data.to_bytes()); + + bytes + } + + /// Deserializes SigningPackage from bytes. + pub fn from_bytes(bytes: &[u8]) -> FROSTResult { + let signer_data = SignerData::from_bytes(&bytes[..SCALAR_LENGTH])?; + + let common_data = CommonData::from_bytes(&bytes[SCALAR_LENGTH..])?; + + Ok(SigningPackage { + common_data, + signer_data, + }) + } +} + +#[cfg(test)] +mod tests { + use super::{SigningCommitments, SigningNonces, SigningPackage}; + use crate::{ + olaf::{simplpedpop::AllMessage, test_utils::generate_parameters}, + SigningKey, VerifyingKey, + }; + use alloc::vec::Vec; + use rand_core::OsRng; + + #[test] + fn test_round1_serialization() { + let parameters = generate_parameters(); + let participants = parameters.participants as usize; + let threshold = parameters.threshold as usize; + + let mut rng = OsRng; + let mut keypairs: Vec = (0..participants) + .map(|_| SigningKey::generate(&mut rng)) + .collect(); + let public_keys: Vec = + keypairs.iter_mut().map(|kp| kp.verifying_key).collect(); + + let mut all_messages = Vec::new(); + for i in 0..participants { + let message: AllMessage = keypairs[i] + .simplpedpop_contribute_all(threshold as u16, public_keys.clone()) + .unwrap(); + all_messages.push(message); + } + + let spp_output = keypairs[0] + .simplpedpop_recipient_all(&all_messages) + .unwrap(); + + let (signing_nonces, signing_commitments) = spp_output.1.commit(&mut rng); + + let nonces_bytes = signing_nonces.clone().to_bytes(); + let commitments_bytes = signing_commitments.clone().to_bytes(); + + let deserialized_nonces = SigningNonces::from_bytes(&nonces_bytes).unwrap(); + let deserialized_commitments = SigningCommitments::from_bytes(&commitments_bytes).unwrap(); + + assert_eq!(signing_nonces, deserialized_nonces); + assert_eq!(signing_commitments, deserialized_commitments); + } + + #[test] + fn test_round2_serialization() { + let parameters = generate_parameters(); + let participants = parameters.participants as usize; + let threshold = parameters.threshold as usize; + + let mut rng = OsRng; + let mut keypairs: Vec = (0..participants) + .map(|_| SigningKey::generate(&mut rng)) + .collect(); + let public_keys: Vec = + keypairs.iter_mut().map(|kp| kp.verifying_key).collect(); + + let mut all_messages = Vec::new(); + for i in 0..participants { + let message: AllMessage = keypairs[i] + .simplpedpop_contribute_all(threshold as u16, public_keys.clone()) + .unwrap(); + all_messages.push(message); + } + + let mut spp_outputs = Vec::new(); + + for kp in keypairs.iter_mut() { + let spp_output = kp.simplpedpop_recipient_all(&all_messages).unwrap(); + spp_outputs.push(spp_output); + } + + let mut all_signing_commitments = Vec::new(); + let mut all_signing_nonces = Vec::new(); + + for spp_output in &spp_outputs { + let (signing_nonces, signing_commitments) = spp_output.1.commit(&mut rng); + all_signing_nonces.push(signing_nonces); + all_signing_commitments.push(signing_commitments); + } + + let message = b"message"; + + let signing_package = spp_outputs[0] + .1 + .sign( + message, + &spp_outputs[0].0.spp_output, + &all_signing_commitments, + &all_signing_nonces[0], + ) + .unwrap(); + + let signing_package_bytes = signing_package.to_bytes(); + //let deserialized_signing_package = + //SigningPackage::from_bytes(&signing_package_bytes).unwrap(); + + //assert!(deserialized_signing_package == signing_package); + } +} diff --git a/ed25519-dalek/src/olaf/mod.rs b/ed25519-dalek/src/olaf/mod.rs index 760a54108..3df282e80 100644 --- a/ed25519-dalek/src/olaf/mod.rs +++ b/ed25519-dalek/src/olaf/mod.rs @@ -6,12 +6,14 @@ pub mod frost; /// Implementation of the SimplPedPoP protocol. pub mod simplpedpop; -use crate::{SigningKey, VerifyingKey}; +use crate::{SignatureError, SigningKey, VerifyingKey, KEYPAIR_LENGTH}; use curve25519_dalek::{constants::ED25519_BASEPOINT_POINT, EdwardsPoint, Scalar}; use merlin::Transcript; pub(super) const MINIMUM_THRESHOLD: u16 = 2; pub(super) const GENERATOR: EdwardsPoint = ED25519_BASEPOINT_POINT; +pub(crate) const SCALAR_LENGTH: usize = 32; +pub(super) const COMPRESSED_EDWARDS_LENGTH: usize = 32; /// The threshold public key generated in the SimplPedPoP protocol, used to validate the threshold signatures of the FROST protocol. #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -25,6 +27,22 @@ pub struct VerifyingShare(pub(crate) VerifyingKey); #[derive(Clone, Debug)] pub struct SigningKeypair(pub(crate) SigningKey); +impl SigningKeypair { + /// Serializes `SigningKeypair` to bytes. + pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] { + self.0.to_keypair_bytes() + } + + /// Deserializes a `SigningKeypair` from bytes. + pub fn from_bytes(bytes: &[u8]) -> Result { + let mut keypair_bytes = [0; 64]; + keypair_bytes.copy_from_slice(bytes); + + let keypair = SigningKey::from_keypair_bytes(&keypair_bytes)?; + Ok(SigningKeypair(keypair)) + } +} + /// The identifier of a participant, which is the same in the SimplPedPoP protocol and in the FROST protocol. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Identifier(pub(crate) Scalar); @@ -52,3 +70,25 @@ pub(crate) fn scalar_from_canonical_bytes(bytes: [u8; 32]) -> Option { Some(key.unwrap()) } + +#[cfg(test)] +pub(crate) mod test_utils { + use crate::olaf::simplpedpop::Parameters; + use rand::{thread_rng, Rng}; + + const MAXIMUM_PARTICIPANTS: u16 = 10; + const MINIMUM_PARTICIPANTS: u16 = 2; + + pub(crate) fn generate_parameters() -> Parameters { + use super::MINIMUM_THRESHOLD; + + let mut rng = thread_rng(); + let participants = rng.gen_range(MINIMUM_PARTICIPANTS..=MAXIMUM_PARTICIPANTS); + let threshold = rng.gen_range(MINIMUM_THRESHOLD..=participants); + + Parameters { + participants, + threshold, + } + } +} diff --git a/ed25519-dalek/src/olaf/simplpedpop/mod.rs b/ed25519-dalek/src/olaf/simplpedpop/mod.rs index 9a2ebad1e..50b9c52e1 100644 --- a/ed25519-dalek/src/olaf/simplpedpop/mod.rs +++ b/ed25519-dalek/src/olaf/simplpedpop/mod.rs @@ -1,16 +1,16 @@ -mod errors; +pub mod errors; mod types; pub(crate) use self::types::SecretPolynomial; -pub use self::types::{AllMessage, DKGOutput, Parameters}; +pub use self::types::{AllMessage, Parameters, SPPOutput}; use self::{ errors::{SPPError, SPPResult}, types::{ - DKGOutputMessage, MessageContent, PolynomialCommitment, SecretShare, + MessageContent, PolynomialCommitment, SPPOutputMessage, SecretShare, CHACHA20POLY1305_KEY_LENGTH, ENCRYPTION_NONCE_LENGTH, RECIPIENTS_HASH_LENGTH, }, }; -use crate::{olaf::GENERATOR, SecretKey, SigningKey, VerifyingKey}; +use crate::{olaf::GENERATOR, SigningKey, VerifyingKey}; use alloc::vec::Vec; use curve25519_dalek::{traits::Identity, EdwardsPoint, Scalar}; use ed25519::signature::{SignerMut, Verifier}; @@ -117,7 +117,7 @@ impl SigningKey { pub fn simplpedpop_recipient_all( &mut self, messages: &[AllMessage], - ) -> SPPResult<(DKGOutputMessage, SigningKeypair)> { + ) -> SPPResult<(SPPOutputMessage, SigningKeypair)> { let first_message = &messages[0]; let parameters = &first_message.content.parameters; let threshold = parameters.threshold as usize; @@ -234,14 +234,14 @@ impl SigningKey { verifying_keys.push((*id, VerifyingShare(VerifyingKey::from(evaluation)))); } - let dkg_output = DKGOutput::new( + let spp_output = SPPOutput::new( parameters, ThresholdPublicKey(VerifyingKey::from(group_point)), verifying_keys, ); - let signature = self.sign(&dkg_output.to_bytes()); - let dkg_output = DKGOutputMessage::new(self.verifying_key, dkg_output, signature); + let signature = self.sign(&spp_output.to_bytes()); + let spp_output = SPPOutputMessage::new(self.verifying_key, spp_output, signature); let mut nonce: [u8; 32] = [0u8; 32]; OsRng.fill_bytes(&mut nonce); @@ -251,7 +251,7 @@ impl SigningKey { verifying_key: VerifyingKey::from(total_secret_share * GENERATOR), }; - Ok((dkg_output, SigningKeypair(signing_key))) + Ok((spp_output, SigningKeypair(signing_key))) } } @@ -302,37 +302,37 @@ mod tests { all_messages.push(message); } - let mut dkg_outputs = Vec::new(); + let mut spp_outputs = Vec::new(); for kp in keypairs.iter_mut() { - let dkg_output = kp.simplpedpop_recipient_all(&all_messages).unwrap(); - dkg_outputs.push(dkg_output); + let spp_output = kp.simplpedpop_recipient_all(&all_messages).unwrap(); + spp_outputs.push(spp_output); } - // Verify that all DKG outputs are equal for group_public_key and verifying_keys + // Verify that all SPP outputs are equal for group_public_key and verifying_keys assert!( - dkg_outputs + spp_outputs .windows(2) - .all(|w| w[0].0.dkg_output.group_public_key.0 - == w[1].0.dkg_output.group_public_key.0 - && w[0].0.dkg_output.verifying_keys.len() - == w[1].0.dkg_output.verifying_keys.len() + .all(|w| w[0].0.spp_output.threshold_public_key.0 + == w[1].0.spp_output.threshold_public_key.0 + && w[0].0.spp_output.verifying_keys.len() + == w[1].0.spp_output.verifying_keys.len() && w[0] .0 - .dkg_output + .spp_output .verifying_keys .iter() - .zip(w[1].0.dkg_output.verifying_keys.iter()) + .zip(w[1].0.spp_output.verifying_keys.iter()) .all(|((a, b), (c, d))| a.0 == c.0 && b.0 == d.0)), - "All DKG outputs should have identical group public keys and verifying keys." + "All SPP outputs should have identical group public keys and verifying keys." ); // Verify that all verifying_shares are valid for i in 0..participants { for j in 0..participants { assert_eq!( - dkg_outputs[i].0.dkg_output.verifying_keys[j].1 .0.point, - (Scalar::from_canonical_bytes(dkg_outputs[j].1 .0.secret_key).unwrap() + spp_outputs[i].0.spp_output.verifying_keys[j].1 .0.point, + (Scalar::from_canonical_bytes(spp_outputs[j].1 .0.secret_key).unwrap() * GENERATOR), "Verification of total secret shares failed!" ); diff --git a/ed25519-dalek/src/olaf/simplpedpop/types.rs b/ed25519-dalek/src/olaf/simplpedpop/types.rs index a6f60cd2e..72853fdfa 100644 --- a/ed25519-dalek/src/olaf/simplpedpop/types.rs +++ b/ed25519-dalek/src/olaf/simplpedpop/types.rs @@ -21,13 +21,13 @@ pub(super) const COMPRESSED_EDWARDS_LENGTH: usize = 32; pub(super) const VEC_LENGTH: usize = 2; pub(super) const ENCRYPTION_NONCE_LENGTH: usize = 12; pub(super) const RECIPIENTS_HASH_LENGTH: usize = 16; -pub(super) const CHACHA20POLY1305_LENGTH: usize = 64; +pub(super) const CHACHA20POLY1305_LENGTH: usize = 48; pub(super) const CHACHA20POLY1305_KEY_LENGTH: usize = 32; pub(super) const SCALAR_LENGTH: usize = 32; pub(super) const U16_LENGTH: usize = 2; /// The parameters of a given execution of the SimplPedPoP protocol. -#[derive(PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Parameters { pub(crate) participants: u16, pub(crate) threshold: u16, @@ -108,7 +108,7 @@ impl SecretShare { } } -#[derive(Clone)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct EncryptedSecretShare(pub(super) Vec); impl EncryptedSecretShare { @@ -166,46 +166,10 @@ impl SecretPolynomial { value } - - /// Generates a lagrange coefficient. - /// - /// The Lagrange polynomial for a set of points (x_j, y_j) for 0 <= j <= k - /// is ∑_{i=0}^k y_i.ℓ_i(x), where ℓ_i(x) is the Lagrange basis polynomial: - /// - /// ℓ_i(x) = ∏_{0≤j≤k; j≠i} (x - x_j) / (x_i - x_j). - /// - /// This computes ℓ_j(x) for the set of points `xs` and for the j corresponding - /// to the given xj. - /// - /// If `x` is None, it uses 0 for it (since Identifiers can't be 0). - pub(crate) fn compute_lagrange_coefficient( - x_set: &[Identifier], - x: Option, - x_i: Identifier, - ) -> Scalar { - let mut num = Scalar::ONE; - let mut den = Scalar::ONE; - - for x_j in x_set.iter() { - if x_i == *x_j { - continue; - } - - if let Some(x) = x { - num *= x.0 - x_j.0; - den *= x_i.0 - x_j.0; - } else { - // Both signs inverted just to avoid requiring Neg (-*xj) - num *= x_j.0; - den *= x_j.0 - x_i.0; - } - } - - num * den.invert() - } } /// The polynomial commitment of a participant, used to verify the secret shares without revealing the polynomial. +#[derive(Debug, PartialEq, Eq)] pub struct PolynomialCommitment { pub(super) coefficients_commitments: Vec, } @@ -266,6 +230,7 @@ impl PolynomialCommitment { /// We'd save bandwidth by having separate messages for each /// participant, but typical thresholds lie between 1/2 and 2/3, /// so this doubles or tripples bandwidth usage. +#[derive(Debug, PartialEq, Eq)] pub struct AllMessage { pub(super) content: MessageContent, pub(super) signature: Signature, @@ -303,6 +268,7 @@ impl AllMessage { } /// The contents of the message destined to all participants. +#[derive(Debug, PartialEq, Eq)] pub struct MessageContent { pub(super) sender: VerifyingKey, pub(super) encryption_nonce: [u8; ENCRYPTION_NONCE_LENGTH], @@ -337,8 +303,7 @@ impl MessageContent { bytes.extend(self.sender.to_bytes()); bytes.extend(&self.encryption_nonce); - bytes.extend(self.parameters.participants.to_le_bytes()); - bytes.extend(self.parameters.threshold.to_le_bytes()); + bytes.extend(self.parameters.to_bytes()); bytes.extend(&self.recipients_hash); for point in &self.polynomial_commitment.coefficients_commitments { @@ -369,18 +334,10 @@ impl MessageContent { .map_err(SPPError::DeserializationError)?; cursor += ENCRYPTION_NONCE_LENGTH; - let participants = u16::from_le_bytes( - bytes[cursor..cursor + VEC_LENGTH] - .try_into() - .map_err(SPPError::DeserializationError)?, - ); - cursor += VEC_LENGTH; - let threshold = u16::from_le_bytes( - bytes[cursor..cursor + VEC_LENGTH] - .try_into() - .map_err(SPPError::DeserializationError)?, - ); - cursor += VEC_LENGTH; + let parameters = Parameters::from_bytes(&bytes[cursor..cursor + U16_LENGTH * 2])?; + cursor += U16_LENGTH * 2; + + let participants = parameters.participants; let recipients_hash: [u8; RECIPIENTS_HASH_LENGTH] = bytes [cursor..cursor + RECIPIENTS_HASH_LENGTH] @@ -390,7 +347,7 @@ impl MessageContent { let mut coefficients_commitments = Vec::with_capacity(participants as usize); - for _ in 0..participants { + for _ in 0..parameters.threshold { let point = CompressedEdwardsY::from_slice(&bytes[cursor..cursor + COMPRESSED_EDWARDS_LENGTH]) .map_err(SPPError::DeserializationError)?; @@ -419,10 +376,7 @@ impl MessageContent { Ok(MessageContent { sender, encryption_nonce, - parameters: Parameters { - participants, - threshold, - }, + parameters, recipients_hash, polynomial_commitment, encrypted_secret_shares, @@ -431,30 +385,31 @@ impl MessageContent { } /// The signed output of the SimplPedPoP protocol. -pub struct DKGOutputMessage { +#[derive(Debug, PartialEq, Eq)] +pub struct SPPOutputMessage { pub(super) sender: VerifyingKey, - pub dkg_output: DKGOutput, + pub spp_output: SPPOutput, pub(super) signature: Signature, } -impl DKGOutputMessage { +impl SPPOutputMessage { /// Creates a signed SimplPedPoP output. - pub fn new(sender: VerifyingKey, content: DKGOutput, signature: Signature) -> Self { + pub fn new(sender: VerifyingKey, content: SPPOutput, signature: Signature) -> Self { Self { sender, - dkg_output: content, + spp_output: content, signature, } } - /// Serializes the DKGOutput into bytes. + /// Serializes the SPPOutput into bytes. pub fn to_bytes(&self) -> Vec { let mut bytes = Vec::new(); let pk_bytes = self.sender.to_bytes(); bytes.extend(pk_bytes); - let content_bytes = self.dkg_output.to_bytes(); + let content_bytes = self.spp_output.to_bytes(); bytes.extend(content_bytes); let signature_bytes = self.signature.to_bytes(); @@ -463,7 +418,7 @@ impl DKGOutputMessage { bytes } - /// Deserializes the DKGOutput from bytes. + /// Deserializes the SPPOutput from bytes. pub fn from_bytes(bytes: &[u8]) -> Result { let mut cursor = 0; @@ -474,7 +429,7 @@ impl DKGOutputMessage { cursor += PUBLIC_KEY_LENGTH; let content_bytes = &bytes[cursor..bytes.len() - SIGNATURE_LENGTH]; - let dkg_output = DKGOutput::from_bytes(content_bytes)?; + let spp_output = SPPOutput::from_bytes(content_bytes)?; cursor = bytes.len() - SIGNATURE_LENGTH; @@ -483,22 +438,23 @@ impl DKGOutputMessage { let signature = Signature::from_bytes(&sig_bytes); - Ok(DKGOutputMessage { + Ok(SPPOutputMessage { sender, - dkg_output, + spp_output, signature, }) } } /// The content of the signed output of the SimplPedPoP protocol. -pub struct DKGOutput { +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SPPOutput { pub(crate) parameters: Parameters, - pub group_public_key: ThresholdPublicKey, + pub threshold_public_key: ThresholdPublicKey, pub verifying_keys: Vec<(Identifier, VerifyingShare)>, } -impl DKGOutput { +impl SPPOutput { /// Creates the content of the SimplPedPoP output. pub fn new( parameters: &Parameters, @@ -509,17 +465,17 @@ impl DKGOutput { Self { parameters, - group_public_key, + threshold_public_key: group_public_key, verifying_keys, } } - /// Serializes the DKGOutputContent into bytes. + /// Serializes the SPPOutputContent into bytes. pub fn to_bytes(&self) -> Vec { let mut bytes = Vec::new(); bytes.extend(self.parameters.to_bytes()); - let compressed_public_key = self.group_public_key.0.compressed; + let compressed_public_key = self.threshold_public_key.0.compressed; bytes.extend(compressed_public_key.to_bytes().iter()); let key_count = self.verifying_keys.len() as u16; @@ -533,7 +489,7 @@ impl DKGOutput { bytes } - /// Deserializes the DKGOutputContent from bytes. + /// Deserializes the SPPOutputContent from bytes. pub fn from_bytes(bytes: &[u8]) -> Result { let mut cursor = 0; @@ -568,9 +524,9 @@ impl DKGOutput { verifying_keys.push((Identifier(identifier), VerifyingShare(key))); } - Ok(DKGOutput { + Ok(SPPOutput { parameters, - group_public_key, + threshold_public_key: group_public_key, verifying_keys, }) } @@ -578,169 +534,64 @@ impl DKGOutput { #[cfg(test)] mod tests { - use crate::SigningKey; - use super::*; - use ed25519::signature::Signer; + use crate::{olaf::test_utils::generate_parameters, SigningKey}; use merlin::Transcript; use rand_core::OsRng; #[test] fn test_serialize_deserialize_all_message() { - let sender = SigningKey::generate(&mut OsRng); - let encryption_nonce = [1u8; ENCRYPTION_NONCE_LENGTH]; - let parameters = Parameters { - participants: 2, - threshold: 1, - }; - let recipients_hash = [2u8; RECIPIENTS_HASH_LENGTH]; - let coefficients_commitments = vec![ - Scalar::random(&mut OsRng) * GENERATOR, - Scalar::random(&mut OsRng) * GENERATOR, - ]; - let polynomial_commitment = PolynomialCommitment { - coefficients_commitments, - }; - let encrypted_secret_shares = vec![ - EncryptedSecretShare(vec![1; CHACHA20POLY1305_LENGTH]), - EncryptedSecretShare(vec![1; CHACHA20POLY1305_LENGTH]), - ]; - let signature = sender.sign(b"sig"); + let parameters = generate_parameters(); + let participants = parameters.participants as usize; + let threshold = parameters.threshold as usize; - let message_content = MessageContent::new( - sender.verifying_key, - encryption_nonce, - parameters, - recipients_hash, - polynomial_commitment, - encrypted_secret_shares, - ); + let mut keypairs: Vec = (0..participants) + .map(|_| SigningKey::generate(&mut OsRng)) + .collect(); - let message = AllMessage::new(message_content, signature); + let public_keys: Vec = keypairs.iter().map(|kp| kp.verifying_key).collect(); + + let message: AllMessage = keypairs[0] + .simplpedpop_contribute_all(threshold as u16, public_keys.clone()) + .unwrap(); let bytes = message.to_bytes(); let deserialized_message = AllMessage::from_bytes(&bytes).expect("Failed to deserialize"); - assert_eq!(message.content.sender, deserialized_message.content.sender); - - assert_eq!( - message.content.encryption_nonce, - deserialized_message.content.encryption_nonce - ); - - assert_eq!( - message.content.parameters.participants, - deserialized_message.content.parameters.participants - ); - - assert_eq!( - message.content.parameters.threshold, - deserialized_message.content.parameters.threshold - ); - - assert_eq!( - message.content.recipients_hash, - deserialized_message.content.recipients_hash - ); - - assert!(message - .content - .polynomial_commitment - .coefficients_commitments - .iter() - .zip( - deserialized_message - .content - .polynomial_commitment - .coefficients_commitments - .iter() - ) - .all(|(a, b)| a.compress() == b.compress())); - - assert!(message - .content - .encrypted_secret_shares - .iter() - .zip(deserialized_message.content.encrypted_secret_shares.iter()) - .all(|(a, b)| a.0 == b.0)); - - assert_eq!(message.signature, deserialized_message.signature); + assert_eq!(message, deserialized_message); } #[test] - fn test_dkg_output_serialization() { - let mut rng = OsRng; - - let parameters = Parameters::generate(2, 2); - - let verifying_keys = vec![ - ( - Identifier(Scalar::random(&mut rng)), - VerifyingShare(VerifyingKey::from(Scalar::random(&mut rng) * GENERATOR)), - ), - ( - Identifier(Scalar::random(&mut rng)), - VerifyingShare(VerifyingKey::from(Scalar::random(&mut rng) * GENERATOR)), - ), - ( - Identifier(Scalar::random(&mut rng)), - VerifyingShare(VerifyingKey::from(Scalar::random(&mut rng) * GENERATOR)), - ), - ]; + fn test_spp_output_serialization() { + let parameters = generate_parameters(); + let participants = parameters.participants as usize; + let threshold = parameters.threshold as usize; - let dkg_output = DKGOutput { - parameters, - group_public_key: ThresholdPublicKey(VerifyingKey::from( - Scalar::random(&mut rng) * GENERATOR, - )), - verifying_keys, - }; - - let keypair = SigningKey::generate(&mut OsRng); - let signature = keypair.sign(b"test"); - - let dkg_output = DKGOutputMessage { - sender: keypair.verifying_key, - dkg_output, - signature, - }; + let mut keypairs: Vec = (0..participants) + .map(|_| SigningKey::generate(&mut OsRng)) + .collect(); - let bytes = dkg_output.to_bytes(); + let public_keys: Vec = keypairs.iter().map(|kp| kp.verifying_key).collect(); - let deserialized_dkg_output = - DKGOutputMessage::from_bytes(&bytes).expect("Deserialization failed"); + let mut all_messages = Vec::new(); + for i in 0..participants { + let message: AllMessage = keypairs[i] + .simplpedpop_contribute_all(threshold as u16, public_keys.clone()) + .unwrap(); + all_messages.push(message); + } - assert_eq!( - deserialized_dkg_output - .dkg_output - .group_public_key - .0 - .to_bytes(), - dkg_output.dkg_output.group_public_key.0.to_bytes(), - "Group public keys do not match" - ); + let spp_output = keypairs[0] + .simplpedpop_recipient_all(&all_messages) + .unwrap(); - assert_eq!( - deserialized_dkg_output.dkg_output.verifying_keys.len(), - dkg_output.dkg_output.verifying_keys.len(), - "Verifying keys counts do not match" - ); + let bytes = spp_output.0.to_bytes(); - assert!( - deserialized_dkg_output - .dkg_output - .verifying_keys - .iter() - .zip(dkg_output.dkg_output.verifying_keys.iter()) - .all(|((a, b), (c, d))| a.0 == c.0 && b.0 == d.0), - "Verifying keys do not match" - ); + let deserialized_spp_output_message = + SPPOutputMessage::from_bytes(&bytes).expect("Deserialization failed"); - assert_eq!( - deserialized_dkg_output.signature, dkg_output.signature, - "Signatures do not match" - ); + assert_eq!(deserialized_spp_output_message, spp_output.0); } #[test]