diff --git a/src/olaf/frost/errors.rs b/src/olaf/frost/errors.rs index ff47c7f..fb97914 100644 --- a/src/olaf/frost/errors.rs +++ b/src/olaf/frost/errors.rs @@ -10,11 +10,11 @@ use crate::{ }; /// A result for the SimplPedPoP protocol. -pub type FROSTResult = Result; +pub type MultiSigResult = Result; /// An error ocurred during the execution of the SimplPedPoP protocol. #[derive(Debug)] -pub enum FROSTError { +pub enum MultiSigError { /// The number of signing commitments must be at least equal to the threshold. InvalidNumberOfSigningCommitments, /// The participant's signing commitment is missing. @@ -68,7 +68,7 @@ mod tests { }, Keypair, PublicKey, }; - use super::FROSTError; + use super::MultiSigError; #[test] fn test_empty_signing_packages() { @@ -79,9 +79,9 @@ mod tests { match result { Ok(_) => panic!("Expected an error, but got Ok."), Err(e) => match e { - FROSTError::EmptySigningPackages => assert!(true), + MultiSigError::EmptySigningPackages => assert!(true), _ => { - panic!("Expected FROSTError::EmptySigningPackages, but got {:?}", e) + panic!("Expected MultiSigError::EmptySigningPackages, but got {:?}", e) }, }, } @@ -148,7 +148,7 @@ mod tests { match result { Ok(_) => panic!("Expected an error, but got Ok."), Err(e) => match e { - FROSTError::InvalidSignatureShare { culprit } => { + MultiSigError::InvalidSignatureShare { culprit } => { assert_eq!( culprit, vec![ @@ -157,7 +157,7 @@ mod tests { ] ); }, - _ => panic!("Expected FROSTError::InvalidSignatureShare, but got {:?}", e), + _ => panic!("Expected MultiSigError::InvalidSignatureShare, but got {:?}", e), }, } } @@ -222,9 +222,9 @@ mod tests { match result { Ok(_) => panic!("Expected an error, but got Ok."), Err(e) => match e { - FROSTError::MismatchedSignatureSharesAndSigningCommitments => assert!(true), + MultiSigError::MismatchedSignatureSharesAndSigningCommitments => assert!(true), _ => { - panic!("Expected FROSTError::MismatchedSignatureSharesAndSigningCommitments, but got {:?}", e) + panic!("Expected MultiSigError::MismatchedSignatureSharesAndSigningCommitments, but got {:?}", e) }, }, } @@ -290,9 +290,9 @@ mod tests { match result { Ok(_) => panic!("Expected an error, but got Ok."), Err(e) => match e { - FROSTError::MismatchedCommonData => assert!(true), + MultiSigError::MismatchedCommonData => assert!(true), _ => { - panic!("Expected FROSTError::MismatchedCommonData, but got {:?}", e) + panic!("Expected MultiSigError::MismatchedCommonData, but got {:?}", e) }, }, } @@ -354,9 +354,9 @@ mod tests { match result { Ok(_) => panic!("Expected an error, but got Ok."), Err(e) => match e { - FROSTError::InvalidNumberOfSigningPackages => assert!(true), + MultiSigError::InvalidNumberOfSigningPackages => assert!(true), _ => { - panic!("Expected FROSTError::InvalidNumberOfSigningPackages, but got {:?}", e) + panic!("Expected MultiSigError::InvalidNumberOfSigningPackages, but got {:?}", e) }, }, } @@ -411,9 +411,9 @@ mod tests { match result { Ok(_) => panic!("Expected an error, but got Ok."), Err(e) => match e { - FROSTError::InvalidOwnVerifyingShare => assert!(true), + MultiSigError::InvalidOwnVerifyingShare => assert!(true), _ => { - panic!("Expected FROSTError::InvalidOwnVerifyingShare, but got {:?}", e) + panic!("Expected MultiSigError::InvalidOwnVerifyingShare, but got {:?}", e) }, }, } @@ -468,9 +468,9 @@ mod tests { match result { Ok(_) => panic!("Expected an error, but got Ok."), Err(e) => match e { - FROSTError::IncorrectNumberOfVerifyingShares => assert!(true), + MultiSigError::IncorrectNumberOfVerifyingShares => assert!(true), _ => { - panic!("Expected FROSTError::IncorrectNumberOfVerifyingShares, but got {:?}", e) + panic!("Expected MultiSigError::IncorrectNumberOfVerifyingShares, but got {:?}", e) }, }, } @@ -528,9 +528,9 @@ mod tests { match result { Ok(_) => panic!("Expected an error, but got Ok."), Err(e) => match e { - FROSTError::MissingOwnSigningCommitment => assert!(true), + MultiSigError::MissingOwnSigningCommitment => assert!(true), _ => { - panic!("Expected FROSTError::MissingOwnSigningCommitment, but got {:?}", e) + panic!("Expected MultiSigError::MissingOwnSigningCommitment, but got {:?}", e) }, }, } @@ -585,9 +585,9 @@ mod tests { match result { Ok(_) => panic!("Expected an error, but got Ok."), Err(e) => match e { - FROSTError::IdentitySigningCommitment => assert!(true), + MultiSigError::IdentitySigningCommitment => assert!(true), _ => { - panic!("Expected FROSTError::IdentitySigningCommitment, but got {:?}", e) + panic!("Expected MultiSigError::IdentitySigningCommitment, but got {:?}", e) }, }, } @@ -644,10 +644,10 @@ mod tests { match result { Ok(_) => panic!("Expected an error, but got Ok."), Err(e) => match e { - FROSTError::InvalidNumberOfSigningCommitments => assert!(true), + MultiSigError::InvalidNumberOfSigningCommitments => assert!(true), _ => { panic!( - "Expected FROSTError::InvalidNumberOfSigningCommitments, but got {:?}", + "Expected MultiSigError::InvalidNumberOfSigningCommitments, but got {:?}", e ) }, diff --git a/src/olaf/frost/mod.rs b/src/olaf/frost/mod.rs index 1c32e51..0e5fdce 100644 --- a/src/olaf/frost/mod.rs +++ b/src/olaf/frost/mod.rs @@ -1,4 +1,10 @@ -//! Implementation of the FROST protocol (). +//! Two-nonce non-deterministic MultiSig +//! +//! Intentionally prohibits the dangerous precomputed nonces +//! for which FROST was designed, but otherwise similar to +//! 2-round non-deterministic MultiSig protocols like +//! FROST, MuSig2, DWMS, etc. +//! See https://eprint.iacr.org/2020/852 or others #![allow(non_snake_case)] #![allow(clippy::result_large_err)] @@ -16,7 +22,7 @@ use crate::{ Signature, }; use self::{ - errors::{FROSTError, FROSTResult}, + errors::{MultiSigError, MultiSigResult}, types::{BindingFactor, BindingFactorList, GroupCommitment}, }; use super::{simplpedpop::SPPOutput, Identifier, SigningKeypair, ThresholdPublicKey, VerifyingShare}; @@ -83,20 +89,20 @@ impl SigningKeypair { spp_output: SPPOutput, all_signing_commitments: Vec, signer_nonces: &SigningNonces, - ) -> FROSTResult { + ) -> MultiSigResult { let threshold_public_key = &spp_output.threshold_public_key; let len = all_signing_commitments.len(); if len < spp_output.parameters.threshold as usize { - return Err(FROSTError::InvalidNumberOfSigningCommitments); + return Err(MultiSigError::InvalidNumberOfSigningCommitments); } if spp_output.verifying_keys.len() != len { - return Err(FROSTError::IncorrectNumberOfVerifyingShares); + return Err(MultiSigError::IncorrectNumberOfVerifyingShares); } if !all_signing_commitments.contains(&signer_nonces.commitments) { - return Err(FROSTError::MissingOwnSigningCommitment); + return Err(MultiSigError::MissingOwnSigningCommitment); } let mut identifiers = Vec::new(); @@ -116,11 +122,11 @@ impl SigningKeypair { } if !shares.contains(&&own_verifying_share) { - return Err(FROSTError::InvalidOwnVerifyingShare); + return Err(MultiSigError::InvalidOwnVerifyingShare); } if all_signing_commitments.len() < spp_output.parameters.threshold as usize { - return Err(FROSTError::InvalidNumberOfSigningCommitments); + return Err(MultiSigError::InvalidNumberOfSigningCommitments); } let binding_factor_list: BindingFactorList = BindingFactorList::compute( @@ -229,15 +235,15 @@ pub(super) fn compute_lagrange_coefficient( /// signature, if the coordinator themselves is a signer and misbehaves, they /// can avoid that step. However, at worst, this results in a denial of /// service attack due to publishing an invalid signature. -pub fn aggregate(signing_packages: &[SigningPackage]) -> Result { +pub fn aggregate(signing_packages: &[SigningPackage]) -> Result { if signing_packages.is_empty() { - return Err(FROSTError::EmptySigningPackages); + return Err(MultiSigError::EmptySigningPackages); } let parameters = &signing_packages[0].common_data.spp_output.parameters; if signing_packages.len() < parameters.threshold as usize { - return Err(FROSTError::InvalidNumberOfSigningPackages); + return Err(MultiSigError::InvalidNumberOfSigningPackages); } let common_data = &signing_packages[0].common_data; @@ -250,14 +256,14 @@ pub fn aggregate(signing_packages: &[SigningPackage]) -> Result Result = spp_output.verifying_keys.iter().map(|x| x.0).collect(); @@ -317,7 +323,7 @@ pub fn aggregate(signing_packages: &[SigningPackage]) -> Result FROSTResult { + fn from_bytes(bytes: &[u8]) -> MultiSigResult { 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_or(MultiSigError::SignatureShareDeserializationError)?; Ok(SignatureShare { share }) } @@ -186,11 +186,11 @@ impl NonceCommitment { } /// Deserializes the `NonceCommitment` from bytes. - fn from_bytes(bytes: &[u8]) -> FROSTResult { + fn from_bytes(bytes: &[u8]) -> MultiSigResult { let compressed = CompressedRistretto::from_slice(&bytes[..COMPRESSED_RISTRETTO_LENGTH]) - .map_err(FROSTError::DeserializationError)?; + .map_err(MultiSigError::DeserializationError)?; - let point = compressed.decompress().ok_or(FROSTError::InvalidNonceCommitment)?; + let point = compressed.decompress().ok_or(MultiSigError::InvalidNonceCommitment)?; Ok(NonceCommitment(point)) } @@ -246,7 +246,7 @@ impl SigningNonces { } /// Deserializes SigningNonces from bytes. - pub fn from_bytes(bytes: &[u8]) -> FROSTResult { + pub fn from_bytes(bytes: &[u8]) -> MultiSigResult { let mut cursor = 0; let mut hiding_bytes = [0; 32]; @@ -311,7 +311,7 @@ impl SigningCommitments { } /// Deserializes SigningCommitments from bytes. - pub fn from_bytes(bytes: &[u8]) -> FROSTResult { + pub fn from_bytes(bytes: &[u8]) -> MultiSigResult { let hiding = NonceCommitment::from_bytes(&bytes[..COMPRESSED_RISTRETTO_LENGTH])?; let binding = NonceCommitment::from_bytes(&bytes[COMPRESSED_RISTRETTO_LENGTH..])?; @@ -362,7 +362,7 @@ impl CommonData { } /// Deserializes CommonData from bytes. - fn from_bytes(bytes: &[u8]) -> FROSTResult { + fn from_bytes(bytes: &[u8]) -> MultiSigResult { let mut cursor = 0; let message_len = @@ -388,7 +388,7 @@ impl CommonData { } let spp_output = SPPOutput::from_bytes(&bytes[cursor..]) - .map_err(FROSTError::SPPOutputDeserializationError)?; + .map_err(MultiSigError::SPPOutputDeserializationError)?; Ok(CommonData { message, context, signing_commitments, spp_output }) } @@ -410,7 +410,7 @@ impl SignerData { } /// Deserializes SignerData from bytes. - pub fn from_bytes(bytes: &[u8]) -> FROSTResult { + pub fn from_bytes(bytes: &[u8]) -> MultiSigResult { let share_bytes = &bytes[..SCALAR_LENGTH]; let signature_share = SignatureShare::from_bytes(share_bytes)?; @@ -418,7 +418,7 @@ impl SignerData { } } -/// The signing package that each signer produces in the signing round of the FROST protocol and sends to the +/// The signing package that each signer produces in the signing round of the multi-signature protocol and sends to the /// coordinator, which aggregates them into the final threshold signature. #[derive(PartialEq, Eq)] pub struct SigningPackage { @@ -438,7 +438,7 @@ impl SigningPackage { } /// Deserializes SigningPackage from bytes. - pub fn from_bytes(bytes: &[u8]) -> FROSTResult { + pub fn from_bytes(bytes: &[u8]) -> MultiSigResult { let signer_data = SignerData::from_bytes(&bytes[..SCALAR_LENGTH])?; let common_data = CommonData::from_bytes(&bytes[SCALAR_LENGTH..])?; @@ -455,7 +455,7 @@ impl GroupCommitment { pub(super) fn compute( signing_commitments: &[SigningCommitments], binding_factor_list: &BindingFactorList, - ) -> Result { + ) -> Result { let identity = RistrettoPoint::identity(); let mut group_commitment = RistrettoPoint::identity(); @@ -471,7 +471,7 @@ impl GroupCommitment { // 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(FROSTError::IdentitySigningCommitment); + return Err(MultiSigError::IdentitySigningCommitment); } let binding_factor = &binding_factor_list.0[i];