From 1fdbbd0eafa3132b6c8791688b968de93602141e Mon Sep 17 00:00:00 2001 From: Fiono11 Date: Mon, 29 Apr 2024 12:03:10 +0100 Subject: [PATCH 1/8] Implementation of SimplPedPoP --- .DS_Store | Bin 0 -> 6148 bytes Cargo.toml | 41 +- benches/olaf_benchmarks.rs | 293 ++++++ src/aead.rs | 105 +- src/errors.rs | 46 +- src/identifier.rs | 62 ++ src/lib.rs | 26 +- src/olaf/errors.rs | 76 ++ src/olaf/identifier.rs | 176 ++++ src/olaf/keys.rs | 9 + src/olaf/mod.rs | 9 + src/olaf/polynomial.rs | 212 ++++ src/olaf/simplpedpop.rs | 965 ++++++++++++++++++ src/olaf/tests.rs | 968 ++++++++++++++++++ src/polynomial.rs | 212 ++++ src/serdey.rs | 102 +- src/sign.rs | 259 ++--- src/simplpedpop.rs | 1883 ++++++++++++++++++++++++++++++++++++ 18 files changed, 5209 insertions(+), 235 deletions(-) create mode 100644 .DS_Store create mode 100644 benches/olaf_benchmarks.rs create mode 100644 src/identifier.rs create mode 100644 src/olaf/errors.rs create mode 100644 src/olaf/identifier.rs create mode 100644 src/olaf/keys.rs create mode 100644 src/olaf/mod.rs create mode 100644 src/olaf/polynomial.rs create mode 100644 src/olaf/simplpedpop.rs create mode 100644 src/olaf/tests.rs create mode 100644 src/polynomial.rs create mode 100644 src/simplpedpop.rs diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..1f487643c31855e8a0f311ffce8c5cef9e99ed9d GIT binary patch literal 6148 zcmeHKJ8HvF5S)z-F}QJ=@?9Y}5XLz{E?_JNQiup5(yPk3aD}rERu}TBU##kOE%{`1hgF9lOFgF+Lq! zVgw+r84lwe m(T<7Hj(PBQd>2WX*L=_CUE!P Vec { + (1..=max_signers) + .map(|_| Parameters::new(max_signers, min_signers)) + .collect() + } + + fn round1( + participants: u16, + threshold: u16, + ) -> ( + Vec, + Vec, + Vec, + Vec>, + ) { + let parameters_list = generate_parameters(participants, threshold); + + let mut all_public_messages_vec = Vec::new(); + let mut participants_round1_private_data = Vec::new(); + let mut participants_round1_public_data = Vec::new(); + + for i in 0..parameters_list.len() { + let (private_data, public_message, public_data) = + round1::run(parameters_list[i as usize].clone(), OsRng) + .expect("Round 1 should complete without errors!"); + + all_public_messages_vec.push(public_message.clone()); + participants_round1_public_data.push(public_data); + participants_round1_private_data.push(private_data); + } + + let mut received_round1_public_messages: Vec> = Vec::new(); + + let mut all_public_messages = BTreeSet::new(); + + for i in 0..participants { + all_public_messages.insert(all_public_messages_vec[i as usize].clone()); + } + + // Iterate through each participant to create a set of messages excluding their own. + for i in 0..participants as usize { + let own_message = PublicMessage::new(&participants_round1_public_data[i]); + + let mut messages_for_participant = BTreeSet::new(); + + for message in &all_public_messages { + if &own_message != message { + // Exclude the participant's own message. + messages_for_participant.insert(message.clone()); + } + } + + received_round1_public_messages.push(messages_for_participant); + } + + ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + received_round1_public_messages, + ) + } + + fn round2( + parameters_list: &Vec, + participants_round1_private_data: Vec, + participants_round1_public_data: &Vec, + participants_round1_public_messages: &Vec>, + ) -> ( + Vec, + Vec, + Vec, + Vec, + ) { + let mut participants_round2_public_data = Vec::new(); + let mut participants_round2_public_messages = Vec::new(); + let mut participants_set_of_participants = Vec::new(); + let mut identifiers_vec = Vec::new(); + + for i in 0..*parameters_list[0].participants() { + let result = round2::run( + participants_round1_private_data[i as usize].clone(), + &participants_round1_public_data[i as usize].clone(), + participants_round1_public_messages[i as usize].clone(), + Transcript::new(b"simplpedpop"), + ) + .expect("Round 2 should complete without errors!"); + + participants_round2_public_data.push(result.0.clone()); + participants_round2_public_messages.push(result.1); + participants_set_of_participants.push(result.0.identifiers().clone()); + identifiers_vec.push(*result.0.identifiers().own_identifier()); + } + + ( + participants_round2_public_data, + participants_round2_public_messages, + participants_set_of_participants, + identifiers_vec, + ) + } + + fn round3( + participants_sets_of_participants: &Vec, + participants_round2_public_messages: &Vec, + participants_round2_public_data: &Vec, + participants_round1_public_data: &Vec, + participants_round1_private_data: Vec, + participants_round2_private_messages: Vec>, + identifiers_vec: &Vec, + ) -> DKGResult< + Vec<( + GroupPublicKey, + BTreeMap, + round3::PrivateData, + )>, + > { + let mut participant_data_round3 = Vec::new(); + + for i in 0..participants_sets_of_participants.len() { + let received_round2_public_messages = participants_round2_public_messages + .iter() + .enumerate() + .filter(|(index, _msg)| { + identifiers_vec[*index] + != *participants_sets_of_participants[i as usize].own_identifier() + }) + .map(|(index, msg)| (identifiers_vec[index], msg.clone())) + .collect::>(); + + let mut round2_private_messages: Vec> = + Vec::new(); + + for participants in participants_sets_of_participants.iter() { + let mut messages_for_participant = BTreeMap::new(); + + for (i, round_messages) in participants_round2_private_messages.iter().enumerate() { + if let Some(message) = round_messages.get(&participants.own_identifier()) { + messages_for_participant.insert(identifiers_vec[i], message.clone()); + } + } + + round2_private_messages.push(messages_for_participant); + } + + let result = round3::run( + &received_round2_public_messages, + &participants_round2_public_data[i as usize], + &participants_round1_public_data[i as usize], + participants_round1_private_data[i as usize].clone(), + &round2_private_messages[i as usize], + )?; + + participant_data_round3.push(result); + } + + Ok(participant_data_round3) + } + + fn benchmark_simplpedpop(c: &mut Criterion) { + let mut group = c.benchmark_group("SimplPedPoP"); + + group + .sample_size(10) + .warm_up_time(std::time::Duration::from_secs(2)) + .measurement_time(std::time::Duration::from_secs(30)); + + for &n in [3, 10, 100].iter() { + let participants = n; + let threshold = (n * 2 + 2) / 3; + let parameters_list = generate_parameters(participants, threshold); + + group.bench_function(BenchmarkId::new("round1", participants), |b| { + b.iter(|| { + round1::run(parameters_list[0].clone(), OsRng).unwrap(); + }) + }); + + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(participants, threshold); + + group.bench_function(BenchmarkId::new("round2", participants), |b| { + b.iter(|| { + round2::run( + participants_round1_private_data[0].clone(), + &participants_round1_public_data[0], + participants_round1_public_messages[0].clone(), + Transcript::new(b"simplpedpop"), + ) + .unwrap(); + }) + }); + + let ( + participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ); + + let participants_round2_public_messages: Vec = + participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(); + + let participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + let received_round2_public_messages = participants_round2_public_messages + .iter() + .enumerate() + .filter(|(index, _msg)| { + identifiers_vec[*index] + != *participants_sets_of_participants[0].own_identifier() + }) + .map(|(index, msg)| (identifiers_vec[index], msg.clone())) + .collect::>(); + + let mut round2_private_messages: Vec> = + Vec::new(); + + for participants in participants_sets_of_participants.iter() { + let mut messages_for_participant = BTreeMap::new(); + + for (i, round_messages) in participants_round2_private_messages.iter().enumerate() { + if let Some(message) = round_messages.get(&participants.own_identifier()) { + messages_for_participant.insert(identifiers_vec[i], message.clone()); + } + } + + round2_private_messages.push(messages_for_participant); + } + + group.bench_function(BenchmarkId::new("round3", participants), |b| { + b.iter(|| { + round3::run( + &received_round2_public_messages, + &participants_round2_public_data[0], + &participants_round1_public_data[0], + participants_round1_private_data[0].clone(), + &round2_private_messages[0], + ) + .unwrap(); + }) + }); + } + + group.finish(); + } + + criterion_group! { + name = olaf_benches; + config = Criterion::default(); + targets = + benchmark_simplpedpop, + } +} + +criterion_main!(olaf_benches::olaf_benches); diff --git a/src/aead.rs b/src/aead.rs index 6cb1b67..29688ca 100644 --- a/src/aead.rs +++ b/src/aead.rs @@ -25,25 +25,26 @@ regarded as a pointer, not a recommendation. // use rand_core::{RngCore,CryptoRng}; -use aead::{KeyInit, KeySizeUser, generic_array::{GenericArray}}; +use aead::{generic_array::GenericArray, KeyInit, KeySizeUser}; -use curve25519_dalek::digest::generic_array::typenum::{U32}; +use curve25519_dalek::digest::generic_array::typenum::U32; use curve25519_dalek::{ - ristretto::{CompressedRistretto}, // RistrettoPoint + ristretto::CompressedRistretto, // RistrettoPoint }; -use super::{SecretKey,PublicKey,Keypair,SignatureResult}; +use super::{Keypair, PublicKey, SecretKey, SignatureResult}; use crate::context::SigningTranscript; use crate::cert::AdaptorCertPublic; - -fn make_aead(mut t: T) -> AEAD -where T: SigningTranscript,AEAD: KeyInit +pub(crate) fn make_aead(mut t: T) -> AEAD +where + T: SigningTranscript, + AEAD: KeyInit, { let mut key: GenericArray::KeySize> = Default::default(); - t.challenge_bytes(b"",key.as_mut_slice()); + t.challenge_bytes(b"", key.as_mut_slice()); AEAD::new(&key) } @@ -56,7 +57,8 @@ impl SecretKey { /// Commit the results of a raw key exchange into a transcript pub fn commit_raw_key_exchange(&self, t: &mut T, ctx: &'static [u8], public: &PublicKey) - where T: SigningTranscript + where + T: SigningTranscript, { let p = self.raw_key_exchange(public); t.commit_point(ctx, &p); @@ -66,10 +68,11 @@ impl SecretKey { /// /// Requires the AEAD have a 32 byte public key and does not support a context. pub fn aead32_unauthenticated(&self, public: &PublicKey) -> AEAD - where AEAD: KeyInit + where + AEAD: KeyInit, { let mut key: GenericArray::KeySize> = Default::default(); - key.clone_from_slice( self.raw_key_exchange(public).as_bytes() ); + key.clone_from_slice(self.raw_key_exchange(public).as_bytes()); AEAD::new(&key) } } @@ -78,10 +81,12 @@ impl PublicKey { /// Initialize an AEAD to the public key `self` using an ephemeral key exchange. /// /// Returns the ephemeral public key and AEAD. - pub fn init_aead_unauthenticated(&self, ctx: &[u8]) -> (CompressedRistretto,AEAD) - { + pub fn init_aead_unauthenticated( + &self, + ctx: &[u8], + ) -> (CompressedRistretto, AEAD) { let ephemeral = Keypair::generate(); - let aead = ephemeral.aead_unauthenticated(ctx,self); + let aead = ephemeral.aead_unauthenticated(ctx, self); (ephemeral.public.into_compressed(), aead) } @@ -89,8 +94,9 @@ impl PublicKey { /// /// Returns the ephemeral public key and AEAD. /// Requires the AEAD have a 32 byte public key and does not support a context. - pub fn init_aead32_unauthenticated(&self) -> (CompressedRistretto,AEAD) - where AEAD: KeyInit + pub fn init_aead32_unauthenticated(&self) -> (CompressedRistretto, AEAD) + where + AEAD: KeyInit, { let secret = SecretKey::generate(); let aead = secret.aead32_unauthenticated(self); @@ -102,48 +108,55 @@ impl Keypair { /// Commit the results of a key exchange into a transcript /// including the public keys in sorted order. pub fn commit_key_exchange(&self, t: &mut T, ctx: &'static [u8], public: &PublicKey) - where T: SigningTranscript + where + T: SigningTranscript, { let mut pks = [self.public.as_compressed(), public.as_compressed()]; - pks.sort_unstable_by_key( |pk| pk.as_bytes() ); - for pk in &pks { t.commit_point(b"pk",pk); } - self.secret.commit_raw_key_exchange(t,ctx,public); + pks.sort_unstable_by_key(|pk| pk.as_bytes()); + for pk in &pks { + t.commit_point(b"pk", pk); + } + self.secret.commit_raw_key_exchange(t, ctx, public); } /// An AEAD from a key exchange with the specified public key. pub fn aead_unauthenticated(&self, ctx: &[u8], public: &PublicKey) -> AEAD { let mut t = merlin::Transcript::new(b"KEX"); - t.append_message(b"ctx",ctx); - self.commit_key_exchange(&mut t,b"kex",public); + t.append_message(b"ctx", ctx); + self.commit_key_exchange(&mut t, b"kex", public); make_aead(t) } /// Reciever's 2DH AEAD - pub fn reciever_aead( + pub fn reciever_aead( &self, mut t: T, ephemeral_pk: &PublicKey, static_pk: &PublicKey, ) -> AEAD - where T: SigningTranscript, AEAD: KeyInit + where + T: SigningTranscript, + AEAD: KeyInit, { - self.commit_key_exchange(&mut t,b"epk",ephemeral_pk); - self.commit_key_exchange(&mut t,b"epk",static_pk); + self.commit_key_exchange(&mut t, b"epk", ephemeral_pk); + self.commit_key_exchange(&mut t, b"epk", static_pk); make_aead(t) } /// Sender's 2DH AEAD - pub fn sender_aead( - &self, - mut t: T, - public: &PublicKey, - ) -> (CompressedRistretto,AEAD) - where T: SigningTranscript, AEAD: KeyInit + pub fn sender_aead(&self, mut t: T, public: &PublicKey) -> (CompressedRistretto, AEAD) + where + T: SigningTranscript, + AEAD: KeyInit, { let key = t.witness_scalar(b"make_esk", &[&self.secret.nonce]); - let ekey = SecretKey { key, nonce: self.secret.nonce }.to_keypair(); - ekey.commit_key_exchange(&mut t,b"epk",public); - self.commit_key_exchange(&mut t,b"epk",public); + let ekey = SecretKey { + key, + nonce: self.secret.nonce, + } + .to_keypair(); + ekey.commit_key_exchange(&mut t, b"epk", public); + self.commit_key_exchange(&mut t, b"epk", public); (ekey.public.into_compressed(), make_aead(t)) } @@ -152,26 +165,34 @@ impl Keypair { /// Returns the AEAD constructed from an ephemeral key exchange /// with the public key computed form the sender's public key /// and their implicit Adaptor certificate. - pub fn reciever_aead_with_adaptor_cert( + pub fn reciever_aead_with_adaptor_cert( &self, t: T, cert_public: &AdaptorCertPublic, public: &PublicKey, ) -> SignatureResult - where T: SigningTranscript, AEAD: KeyInit + where + T: SigningTranscript, + AEAD: KeyInit, { - let epk = public.open_adaptor_cert(t,cert_public) ?; - Ok(self.aead_unauthenticated(b"",&epk)) + let epk = public.open_adaptor_cert(t, cert_public)?; + Ok(self.aead_unauthenticated(b"", &epk)) } /// Sender's AEAD with Adaptor certificate. /// /// Along with the AEAD, we return the implicit Adaptor certificate /// from which the receiver recreates the ephemeral public key. - pub fn sender_aead_with_adaptor_cert(&self, t: T, public: &PublicKey) -> (AdaptorCertPublic,AEAD) - where T: SigningTranscript+Clone, AEAD: KeyInit + pub fn sender_aead_with_adaptor_cert( + &self, + t: T, + public: &PublicKey, + ) -> (AdaptorCertPublic, AEAD) + where + T: SigningTranscript + Clone, + AEAD: KeyInit, { - let (cert,secret) = self.issue_self_adaptor_cert(t); + let (cert, secret) = self.issue_self_adaptor_cert(t); let aead = secret.to_keypair().aead_unauthenticated(b"", public); (cert, aead) } diff --git a/src/errors.rs b/src/errors.rs index e8c73c0..8a5c879 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -17,7 +17,6 @@ use core::fmt; use core::fmt::Display; - /// `Result` specialized to this crate for convenience. pub type SignatureResult = Result; @@ -88,7 +87,7 @@ pub enum SignatureError { /// Describes the type returning the error description: &'static str, /// Length expected by the constructor in bytes - length: usize + length: usize, }, /// Signature not marked as schnorrkel, maybe try ed25519 instead. NotMarkedSchnorrkel, @@ -116,26 +115,32 @@ impl Display for SignatureError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use self::SignatureError::*; match *self { - EquationFalse => - write!(f, "Verification equation failed"), - PointDecompressionError => - write!(f, "Cannot decompress Ristretto point"), - ScalarFormatError => - write!(f, "Cannot use scalar with high-bit set"), - InvalidKey => - write!(f, "The provided key is not valid"), - BytesLengthError { name, length, .. } => - write!(f, "{name} must be {length} bytes in length"), - NotMarkedSchnorrkel => - write!(f, "Signature bytes not marked as a schnorrkel signature"), - MuSigAbsent { musig_stage, } => - write!(f, "Absent {musig_stage} violated multi-signature protocol"), - MuSigInconsistent { musig_stage, duplicate, } => + EquationFalse => write!(f, "Verification equation failed"), + PointDecompressionError => write!(f, "Cannot decompress Ristretto point"), + ScalarFormatError => write!(f, "Cannot use scalar with high-bit set"), + InvalidKey => write!(f, "The provided key is not valid"), + BytesLengthError { name, length, .. } => { + write!(f, "{name} must be {length} bytes in length") + } + NotMarkedSchnorrkel => { + write!(f, "Signature bytes not marked as a schnorrkel signature") + } + MuSigAbsent { musig_stage } => { + write!(f, "Absent {musig_stage} violated multi-signature protocol") + } + MuSigInconsistent { + musig_stage, + duplicate, + } => { if duplicate { write!(f, "Inconsistent duplicate {musig_stage} in multi-signature") } else { - write!(f, "Inconsistent {musig_stage} violated multi-signature protocol") - }, + write!( + f, + "Inconsistent {musig_stage} violated multi-signature protocol" + ) + } + } } } } @@ -149,7 +154,8 @@ impl failure::Fail for SignatureError {} /// `impl From for E where E: serde::de::Error`. #[cfg(feature = "serde")] pub fn serde_error_from_signature_error(err: SignatureError) -> E -where E: serde_crate::de::Error +where + E: serde::de::Error, { E::custom(err) } diff --git a/src/identifier.rs b/src/identifier.rs new file mode 100644 index 0000000..ed1ed78 --- /dev/null +++ b/src/identifier.rs @@ -0,0 +1,62 @@ +//! The identifier of a participant in a multiparty protocol. + +use core::cmp::Ordering; + +use crate::errors::DKGError; +use curve25519_dalek::Scalar; + +/// The identifier is represented by a scalar. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Identifier(pub(crate) Scalar); + +impl PartialOrd for Identifier { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Identifier { + fn cmp(&self, other: &Self) -> Ordering { + let serialized_self = self.0.as_bytes(); + let serialized_other = other.0.as_bytes(); + + // The default cmp uses lexicographic order; so we need the elements in big endian + serialized_self + .as_ref() + .iter() + .rev() + .cmp(serialized_other.as_ref().iter().rev()) + } +} + +impl TryFrom for Identifier { + type Error = DKGError; + + fn try_from(n: u16) -> Result { + if n == 0 { + Err(DKGError::InvalidIdentifier) + } else { + // Classic left-to-right double-and-add algorithm that skips the first bit 1 (since + // identifiers are never zero, there is always a bit 1), thus `sum` starts with 1 too. + let one = Scalar::ONE; + let mut sum = Scalar::ONE; + + let bits = (n.to_be_bytes().len() as u32) * 8; + for i in (0..(bits - n.leading_zeros() - 1)).rev() { + sum = sum + sum; + if n & (1 << i) != 0 { + sum += one; + } + } + Ok(Self(sum)) + } + } +} + +impl TryFrom for Identifier { + type Error = DKGError; + + fn try_from(value: Scalar) -> Result { + Ok(Self(value)) + } +} diff --git a/src/lib.rs b/src/lib.rs index 323bdaa..eb3ea91 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -231,22 +231,25 @@ extern crate std; #[cfg(feature = "alloc")] extern crate alloc; -use getrandom_or_panic::{RngCore,CryptoRng,getrandom_or_panic}; use curve25519_dalek::scalar::Scalar; +use getrandom_or_panic::{getrandom_or_panic, CryptoRng, RngCore}; #[macro_use] mod serdey; +pub mod keys; pub mod points; mod scalars; -pub mod keys; +pub mod cert; pub mod context; -pub mod sign; -pub mod vrf; pub mod derive; -pub mod cert; pub mod errors; +pub mod sign; +pub mod vrf; + +#[cfg(all(feature = "alloc", feature = "aead"))] +pub mod olaf; #[cfg(all(feature = "aead", feature = "getrandom"))] pub mod aead; @@ -256,17 +259,20 @@ mod batch; // Not safe because need randomness -#[cfg_attr(not(test), deprecated(since = "0.11.0", note = "This module will be replaced in the future"))] +#[cfg_attr( + not(test), + deprecated(since = "0.11.0", note = "This module will be replaced in the future") +)] #[cfg(feature = "std")] pub mod musig; +pub use crate::context::signing_context; // SigningContext,SigningTranscript +pub use crate::errors::{SignatureError, SignatureResult}; pub use crate::keys::*; // {MiniSecretKey,SecretKey,PublicKey,Keypair,ExpansionMode}; + *_LENGTH -pub use crate::context::{signing_context}; // SigningContext,SigningTranscript -pub use crate::sign::{Signature,SIGNATURE_LENGTH}; -pub use crate::errors::{SignatureError,SignatureResult}; +pub use crate::sign::{Signature, SIGNATURE_LENGTH}; #[cfg(feature = "alloc")] -pub use crate::batch::{verify_batch,verify_batch_rng,verify_batch_deterministic,PreparedBatch}; +pub use crate::batch::{verify_batch, verify_batch_deterministic, verify_batch_rng, PreparedBatch}; pub(crate) fn scalar_from_canonical_bytes(bytes: [u8; 32]) -> Option { let key = Scalar::from_canonical_bytes(bytes); diff --git a/src/olaf/errors.rs b/src/olaf/errors.rs new file mode 100644 index 0000000..008ce51 --- /dev/null +++ b/src/olaf/errors.rs @@ -0,0 +1,76 @@ +//! Errors of the Olaf protocol. + +use super::identifier::Identifier; +use crate::SignatureError; + +/// A result for the SimplPedPoP protocol. +pub type DKGResult = Result; + +/// An error ocurred during the execution of the SimplPedPoP protocol. +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum DKGError { + /// Invalid Proof of Possession. + InvalidProofOfPossession(SignatureError), + /// Invalid certificate. + InvalidCertificate(SignatureError), + /// Threshold cannot be greater than the number of participants. + ExcessiveThreshold, + /// Threshold must be at least 2. + InsufficientThreshold, + /// Number of participants is invalid. + InvalidNumberOfParticipants, + /// Secret share verification failed. + InvalidSecretShare(Identifier), + /// Invalid secret. + InvalidSecret, + /// Unknown identifier in round 2 public messages. + UnknownIdentifierRound2PublicMessages(Identifier), + /// Unknown identifier in round 2 private messages. + UnknownIdentifierRound2PrivateMessages(Identifier), + /// Identifier cannot be a zero scalar. + InvalidIdentifier, + /// Incorrect number of identifiers. + IncorrectNumberOfIdentifiers { + /// The expected value. + expected: usize, + /// The actual value. + actual: usize, + }, + /// Incorrect number of private messages. + IncorrectNumberOfPrivateMessages { + /// The expected value. + expected: usize, + /// The actual value. + actual: usize, + }, + /// Incorrect number of round 1 public messages. + IncorrectNumberOfRound1PublicMessages { + /// The expected value. + expected: usize, + /// The actual value. + actual: usize, + }, + /// Incorrect number of round 2 public messages. + IncorrectNumberOfRound2PublicMessages { + /// The expected value. + expected: usize, + /// The actual value. + actual: usize, + }, + /// Incorrect number of round 2 private messages. + IncorrectNumberOfRound2PrivateMessages { + /// The expected value. + expected: usize, + /// The actual value. + actual: usize, + }, + /// Decryption error when decrypting an encrypted secret share. + DecryptionError(chacha20poly1305::Error), + /// Incorrect number of coefficient commitments. + InvalidSecretPolynomialCommitment { + /// The expected value. + expected: usize, + /// The actual value. + actual: usize, + }, +} diff --git a/src/olaf/identifier.rs b/src/olaf/identifier.rs new file mode 100644 index 0000000..4f3d5bb --- /dev/null +++ b/src/olaf/identifier.rs @@ -0,0 +1,176 @@ +//! The identifier of a participant in the Olaf protocol. + +use super::errors::DKGError; +use core::cmp::Ordering; +#[cfg(feature = "serde")] +use core::fmt; +use curve25519_dalek::Scalar; +#[cfg(feature = "serde")] +use serde::de::{self, Visitor}; +#[cfg(feature = "serde")] +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +/// The identifier is represented by a Scalar. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Identifier(pub(crate) Scalar); + +#[cfg(feature = "serde")] +impl Serialize for Identifier { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let scalar_bytes = self.0.to_bytes(); + let scalar_hex = hex::encode(scalar_bytes); + serializer.serialize_str(&scalar_hex) + } +} + +#[cfg(feature = "serde")] +struct IdentifierVisitor; + +#[cfg(feature = "serde")] +impl<'de> Visitor<'de> for IdentifierVisitor { + type Value = Identifier; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a hexadecimal string representing a Scalar") + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + let bytes = match hex::decode(value) { + Ok(b) => b, + Err(_) => return Err(E::custom("Invalid hexadecimal string")), + }; + if bytes.len() != 32 { + return Err(E::custom( + "Hexadecimal string must be exactly 32 bytes long", + )); + } + let mut bytes_array = [0u8; 32]; + bytes_array.copy_from_slice(&bytes); + + let scalar = Scalar::from_canonical_bytes(bytes_array); + if scalar.is_some().unwrap_u8() == 1 { + Ok(Identifier(scalar.unwrap())) + } else { + Err(E::custom("Invalid bytes for a Scalar")) + } + } +} + +#[cfg(feature = "serde")] +impl<'de> Deserialize<'de> for Identifier { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(IdentifierVisitor) + } +} + +impl PartialOrd for Identifier { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Identifier { + fn cmp(&self, other: &Self) -> Ordering { + let serialized_self = self.0.as_bytes(); + let serialized_other = other.0.as_bytes(); + + // The default cmp uses lexicographic order; so we need the elements in big endian + serialized_self + .as_ref() + .iter() + .rev() + .cmp(serialized_other.as_ref().iter().rev()) + } +} + +impl TryFrom for Identifier { + type Error = DKGError; + + fn try_from(n: u16) -> Result { + if n == 0 { + Err(DKGError::InvalidIdentifier) + } else { + // Classic left-to-right double-and-add algorithm that skips the first bit 1 (since + // identifiers are never zero, there is always a bit 1), thus `sum` starts with 1 too. + let one = Scalar::ONE; + let mut sum = Scalar::ONE; + + let bits = (n.to_be_bytes().len() as u32) * 8; + for i in (0..(bits - n.leading_zeros() - 1)).rev() { + sum = sum + sum; + if n & (1 << i) != 0 { + sum += one; + } + } + Ok(Self(sum)) + } + } +} + +impl TryFrom for Identifier { + type Error = DKGError; + + fn try_from(value: Scalar) -> Result { + Ok(Self(value)) + } +} + +#[cfg(test)] +#[cfg(feature = "serde")] +mod tests { + use super::*; + use rand_core::OsRng; + + #[test] + fn test_serialize_deserialize_random_identifier() { + // Create a random Identifier + let random_scalar = Scalar::random(&mut OsRng); + let identifier = Identifier(random_scalar); + + // Serialize the Identifier + let serialized = serde_json::to_string(&identifier).expect("Failed to serialize"); + + // Deserialize the serialized string back into an Identifier + let deserialized: Result = serde_json::from_str(&serialized); + + // Check if the deserialized Identifier matches the original + assert!(deserialized.is_ok()); + assert_eq!(identifier, deserialized.unwrap()); + } + + #[test] + fn test_deserialize_invalid_hex_identifier() { + // Hexadecimal string with invalid characters (not valid hex) + let invalid_hex_scalar = + "\"g1c4c8a8ff4d21243af23e5ef23fea223b7cdde1baf31e56af77f872a8cc8402\""; + let result: Result = serde_json::from_str(invalid_hex_scalar); + + // Assert that the deserialization fails + assert!( + result.is_err(), + "Deserialization should fail for invalid hex characters" + ); + } + + #[test] + fn test_deserialize_invalid_length_identifier() { + // Incorrect length hexadecimal string (not 64 characters long) + let invalid_length_hex = "\"1c4c8a8ff4d21243af23e5ef23fea223b7cd\""; + let result: Result = serde_json::from_str(invalid_length_hex); + + // Assert that the deserialization fails due to length mismatch + assert!( + result.is_err(), + "Deserialization should fail for incorrect hex length" + ); + } +} diff --git a/src/olaf/keys.rs b/src/olaf/keys.rs new file mode 100644 index 0000000..f00ccd9 --- /dev/null +++ b/src/olaf/keys.rs @@ -0,0 +1,9 @@ +//! Olaf keys and key shares. + +use super::polynomial::CoefficientCommitment; +use crate::PublicKey; + +/// The group public key generated by the SimplPedPoP protocol. +pub type GroupPublicKey = PublicKey; +/// The group public key share of each participant in the SimplPedPoP protocol. +pub type GroupPublicKeyShare = CoefficientCommitment; diff --git a/src/olaf/mod.rs b/src/olaf/mod.rs new file mode 100644 index 0000000..d915964 --- /dev/null +++ b/src/olaf/mod.rs @@ -0,0 +1,9 @@ +//! Implementation of the Olaf protocol (), which is composed of the Distributed +//! Key Generation (DKG) protocol SimplPedPoP and the Threshold Signing protocol FROST. + +pub mod errors; +pub mod identifier; +pub mod keys; +mod polynomial; +pub mod simplpedpop; +mod tests; diff --git a/src/olaf/polynomial.rs b/src/olaf/polynomial.rs new file mode 100644 index 0000000..6b78fa3 --- /dev/null +++ b/src/olaf/polynomial.rs @@ -0,0 +1,212 @@ +//! Implementation of a polynomial and related operations. + +use crate::olaf::simplpedpop::GENERATOR; +use alloc::vec; +use alloc::vec::Vec; +use curve25519_dalek::{traits::Identity, RistrettoPoint, Scalar}; +use rand_core::{CryptoRng, RngCore}; +use zeroize::ZeroizeOnDrop; + +pub(crate) type Coefficient = Scalar; +pub(crate) type Value = Scalar; +pub(crate) type ValueCommitment = RistrettoPoint; +pub(crate) type CoefficientCommitment = RistrettoPoint; + +/// A polynomial. +#[derive(Debug, Clone, ZeroizeOnDrop)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Polynomial { + pub(crate) coefficients: Vec, +} + +impl Polynomial { + pub(crate) fn generate(rng: &mut R, degree: u16) -> Self { + let mut coefficients = Vec::new(); + + for _ in 0..(degree as usize + 1) { + coefficients.push(Scalar::random(rng)); + } + + Self { coefficients } + } + + pub(crate) fn evaluate(&self, x: &Value) -> Value { + let mut value = *self + .coefficients + .last() + .expect("coefficients must have at least one element"); + + // Process all coefficients except the last one, using Horner's method + for coeff in self.coefficients.iter().rev().skip(1) { + value = value * x + coeff; + } + + value + } +} + +/// A polynomial commitment. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct PolynomialCommitment { + pub(crate) coefficients_commitments: Vec, +} + +impl PolynomialCommitment { + pub(crate) fn commit(polynomial: &Polynomial) -> Self { + let coefficients_commitments = polynomial + .coefficients + .iter() + .map(|coefficient| GENERATOR * coefficient) + .collect(); + + Self { + coefficients_commitments, + } + } + + pub(crate) fn evaluate(&self, identifier: &Value) -> ValueCommitment { + let i = identifier; + + let (_, result) = self.coefficients_commitments.iter().fold( + (Scalar::ONE, RistrettoPoint::identity()), + |(i_to_the_k, sum_so_far), comm_k| (i * i_to_the_k, sum_so_far + comm_k * i_to_the_k), + ); + result + } + + pub(crate) fn sum_polynomial_commitments( + polynomials_commitments: &[&PolynomialCommitment], + ) -> PolynomialCommitment { + let max_length = polynomials_commitments + .iter() + .map(|c| c.coefficients_commitments.len()) + .max() + .unwrap_or(0); + + let mut total_commitment = vec![RistrettoPoint::identity(); max_length]; + + for polynomial_commitment in polynomials_commitments { + for (i, coeff_commitment) in polynomial_commitment + .coefficients_commitments + .iter() + .enumerate() + { + total_commitment[i] += coeff_commitment; + } + } + + PolynomialCommitment { + coefficients_commitments: total_commitment, + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + olaf::polynomial::{Coefficient, Polynomial, PolynomialCommitment}, + olaf::simplpedpop::GENERATOR, + }; + + use alloc::vec::Vec; + use curve25519_dalek::Scalar; + use rand::rngs::OsRng; + + #[test] + fn test_generate_polynomial_commitment_valid() { + let degree = 3; + + let polynomial = Polynomial::generate(&mut OsRng, degree); + + let polynomial_commitment = PolynomialCommitment::commit(&polynomial); + + assert_eq!(polynomial.coefficients.len(), degree as usize + 1); + + assert_eq!( + polynomial_commitment.coefficients_commitments.len(), + degree as usize + 1 + ); + } + + #[test] + fn test_evaluate_polynomial() { + let coefficients: Vec = + vec![Scalar::from(3u64), Scalar::from(2u64), Scalar::from(1u64)]; // Polynomial x^2 + 2x + 3 + + let polynomial = Polynomial { coefficients }; + + let value = Scalar::from(5u64); // x = 5 + + let result = polynomial.evaluate(&value); + + assert_eq!(result, Scalar::from(38u64)); // 5^2 + 2*5 + 3 + } + + #[test] + fn test_sum_secret_polynomial_commitments() { + let polynomial_commitment1 = PolynomialCommitment { + coefficients_commitments: vec![ + GENERATOR * Scalar::from(1u64), // Constant + GENERATOR * Scalar::from(2u64), // Linear + GENERATOR * Scalar::from(3u64), // Quadratic + ], + }; + + let polynomial_commitment2 = PolynomialCommitment { + coefficients_commitments: vec![ + GENERATOR * Scalar::from(4u64), // Constant + GENERATOR * Scalar::from(5u64), // Linear + GENERATOR * Scalar::from(6u64), // Quadratic + ], + }; + + let summed_polynomial_commitments = PolynomialCommitment::sum_polynomial_commitments(&[ + &polynomial_commitment1, + &polynomial_commitment2, + ]); + + let expected_coefficients_commitments = vec![ + GENERATOR * Scalar::from(5u64), // 1 + 4 = 5 + GENERATOR * Scalar::from(7u64), // 2 + 5 = 7 + GENERATOR * Scalar::from(9u64), // 3 + 6 = 9 + ]; + + assert_eq!( + summed_polynomial_commitments.coefficients_commitments, + expected_coefficients_commitments, + "Coefficient commitments do not match" + ); + } + + #[test] + fn test_evaluate_polynomial_commitment() { + // f(x) = 3 + 2x + x^2 + let constant_coefficient_commitment = Scalar::from(3u64) * GENERATOR; + let linear_commitment = Scalar::from(2u64) * GENERATOR; + let quadratic_commitment = Scalar::from(1u64) * GENERATOR; + + // Note the order and inclusion of the constant term + let coefficients_commitments = vec![ + constant_coefficient_commitment, + linear_commitment, + quadratic_commitment, + ]; + + let polynomial_commitment = PolynomialCommitment { + coefficients_commitments, + }; + + let value = Scalar::from(2u64); + + // f(2) = 11 + let expected = Scalar::from(11u64) * GENERATOR; + + let result = polynomial_commitment.evaluate(&value); + + assert_eq!( + result, expected, + "The evaluated commitment does not match the expected result" + ); + } +} diff --git a/src/olaf/simplpedpop.rs b/src/olaf/simplpedpop.rs new file mode 100644 index 0000000..6a2c6f9 --- /dev/null +++ b/src/olaf/simplpedpop.rs @@ -0,0 +1,965 @@ +//! Implementation of a modified version of SimplPedPoP (), a DKG based on PedPoP, which in turn is based +//! on Pedersen's DKG. All of them have as the fundamental building block the Shamir's Secret Sharing scheme. +//! +//! The modification consists of each participant sending the secret shares of the other participants only in round 2 +//! instead of in round 1. The reason for this is we use the secret commitments (the evaluations of the secret polynomial +//! commitments at zero) of round 1 to assign the identifiers of all the participants in round 2, which will then be +//! used to compute the corresponding secret shares. Finally, we encrypt and authenticate the secret shares with +//! Chacha20Poly1305, meaning they can be distributed to the participants by an untrusted coordinator instead of sending +//! them directly. +//! +//! The protocol is divided into three rounds. In each round some data and some messages are produced and some messages +//! are verified (if received from a previous round). Data is divided into public and private because in a given round we +//! want to pass a reference to the public data (performance reasons) and the private data itself so that it is zeroized +//! after getting out of scope (security reasons). Public messages are destined to all the other participants, while private +//! messages are destined to a single participant. + +use crate::{aead::make_aead, context::SigningTranscript, SecretKey, Signature}; +use alloc::{collections::BTreeSet, vec::Vec}; +use chacha20poly1305::{aead::Aead, ChaCha20Poly1305, Nonce}; +use curve25519_dalek::{constants::RISTRETTO_BASEPOINT_POINT, RistrettoPoint, Scalar}; +use derive_getters::Getters; +use merlin::Transcript; +use rand_core::{CryptoRng, RngCore}; +use zeroize::ZeroizeOnDrop; + +use super::{ + errors::{DKGError, DKGResult}, + identifier::Identifier, + polynomial::{Coefficient, CoefficientCommitment, Polynomial, PolynomialCommitment}, +}; + +pub(crate) const GENERATOR: RistrettoPoint = RISTRETTO_BASEPOINT_POINT; + +pub(crate) type SecretPolynomialCommitment = PolynomialCommitment; +pub(crate) type SecretPolynomial = Polynomial; +pub(crate) type TotalSecretShare = SecretShare; +pub(crate) type SecretCommitment = CoefficientCommitment; +pub(crate) type Certificate = Signature; +pub(crate) type ProofOfPossession = Signature; +pub(crate) type Secret = Coefficient; + +/// The parameters of a given execution of the SimplPedPoP protocol. +#[derive(Debug, Clone, Getters)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Parameters { + pub(crate) participants: u16, + pub(crate) threshold: u16, +} + +impl Parameters { + /// Create new parameters. + pub fn new(participants: u16, threshold: u16) -> Parameters { + Parameters { + participants, + threshold, + } + } + + pub(crate) fn validate(&self) -> Result<(), DKGError> { + if self.threshold < 2 { + return Err(DKGError::InsufficientThreshold); + } + + if self.participants < 2 { + return Err(DKGError::InvalidNumberOfParticipants); + } + + if self.threshold > self.participants { + return Err(DKGError::ExcessiveThreshold); + } + + Ok(()) + } +} + +/// The participants of a given execution of the SimplPedPoP protocol. +#[derive(Debug, Clone, Getters)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Identifiers { + pub(crate) own_identifier: Identifier, + pub(crate) others_identifiers: BTreeSet, +} + +impl Identifiers { + /// Create new participants. + pub fn new( + own_identifier: Identifier, + others_identifiers: BTreeSet, + ) -> Identifiers { + Identifiers { + own_identifier, + others_identifiers, + } + } + + pub(crate) fn validate(&self, participants: u16) -> Result<(), DKGError> { + if self.own_identifier.0 == Scalar::ZERO { + return Err(DKGError::InvalidIdentifier); + } + + for other_identifier in &self.others_identifiers { + if other_identifier.0 == Scalar::ZERO { + return Err(DKGError::InvalidIdentifier); + } + } + + if self.others_identifiers.len() != participants as usize - 1 { + return Err(DKGError::IncorrectNumberOfIdentifiers { + expected: participants as usize, + actual: self.others_identifiers.len() + 1, + }); + } + + Ok(()) + } +} + +fn derive_secret_key_from_secret(secret: &Secret, mut rng: R) -> SecretKey { + let mut bytes = [0u8; 64]; + let mut nonce: [u8; 32] = [0u8; 32]; + + rng.fill_bytes(&mut nonce); + let secret_bytes = secret.to_bytes(); + + bytes[..32].copy_from_slice(&secret_bytes[..]); + bytes[32..].copy_from_slice(&nonce[..]); + + SecretKey::from_bytes(&bytes[..]).unwrap() // This never fails because bytes has length 64 and the key is a scalar +} + +/// A secret share, which corresponds to an evaluation of a value that identifies a participant in a secret polynomial. +#[derive(Debug, Clone, ZeroizeOnDrop)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct SecretShare(pub(crate) Scalar); + +impl SecretShare { + pub(crate) fn encrypt( + &self, + decryption_key: &Scalar, + encryption_key: &RistrettoPoint, + context: &[u8], + ) -> EncryptedSecretShare { + let shared_secret = decryption_key * encryption_key; + + let mut transcript = Transcript::new(b"encryption"); + transcript.commit_point(b"shared secret", &shared_secret.compress()); + transcript.append_message(b"context", context); + + let mut bytes = [0; 12]; + transcript.challenge_bytes(b"nonce", &mut bytes); + + let cipher: ChaCha20Poly1305 = make_aead::(transcript); + let nonce = Nonce::from_slice(&bytes[..]); + + let ciphertext: Vec = cipher.encrypt(nonce, &self.0.as_bytes()[..]).unwrap(); + + EncryptedSecretShare(ciphertext) + } +} + +/// An encrypted secret share, which can be sent directly to the intended participant or through an untrusted coordinator. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct EncryptedSecretShare(pub(crate) Vec); + +impl EncryptedSecretShare { + pub(crate) fn decrypt( + &self, + decryption_key: &Scalar, + encryption_key: &RistrettoPoint, + context: &[u8], + ) -> DKGResult { + let shared_secret = decryption_key * encryption_key; + + let mut transcript = Transcript::new(b"encryption"); + transcript.commit_point(b"shared secret", &shared_secret.compress()); + transcript.append_message(b"context", context); + + let mut bytes = [0; 12]; + transcript.challenge_bytes(b"nonce", &mut bytes); + + let cipher: ChaCha20Poly1305 = make_aead::(transcript); + let nonce = Nonce::from_slice(&bytes[..]); + + let plaintext = cipher + .decrypt(nonce, &self.0[..]) + .map_err(DKGError::DecryptionError)?; + + let mut bytes = [0; 32]; + bytes.copy_from_slice(&plaintext); + + Ok(SecretShare(Scalar::from_bytes_mod_order(bytes))) + } +} + +/// SimplPedPoP round 1. +/// +/// The participant commits to a secret polynomial f(x) of degree t-1, where t is the threshold of the DKG, by commiting +/// to each one of the t coefficients of the secret polynomial. +/// +/// It derives a secret key from the secret of the polynomial f(0) and uses it to generate a Proof of Possession of that +/// secret by signing a message with the secret key. +pub mod round1 { + use super::{ + derive_secret_key_from_secret, Parameters, ProofOfPossession, SecretPolynomial, + SecretPolynomialCommitment, + }; + use crate::{ + olaf::errors::DKGResult, + olaf::polynomial::{Polynomial, PolynomialCommitment}, + PublicKey, SecretKey, + }; + use core::cmp::Ordering; + use curve25519_dalek::Scalar; + use merlin::Transcript; + use rand_core::{CryptoRng, RngCore}; + + /// The private data generated by the participant in round 1. + #[derive(Debug, Clone)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct PrivateData { + pub(crate) secret_key: SecretKey, + pub(crate) secret_polynomial: SecretPolynomial, + } + + /// The public data generated by the participant in round 1. + #[derive(Debug, Clone)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct PublicData { + pub(crate) parameters: Parameters, + pub(crate) secret_polynomial_commitment: SecretPolynomialCommitment, + pub(crate) proof_of_possession: ProofOfPossession, + } + + /// Public message to be sent by the participant to all the other participants or to the coordinator in round 1. + #[derive(Debug, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct PublicMessage { + pub(crate) secret_polynomial_commitment: SecretPolynomialCommitment, + pub(crate) proof_of_possession: ProofOfPossession, + } + + impl PublicMessage { + /// Creates a new public message. + pub fn new(public_data: &PublicData) -> PublicMessage { + PublicMessage { + secret_polynomial_commitment: public_data.secret_polynomial_commitment.clone(), + proof_of_possession: public_data.proof_of_possession, + } + } + } + + impl PartialOrd for PublicMessage { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + impl Ord for PublicMessage { + fn cmp(&self, other: &Self) -> Ordering { + self.secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap() + .compress() + .0 + .cmp( + &other + .secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap() + .compress() + .0, + ) + } + } + + /// Runs the round 1 of the SimplPedPoP protocol. + pub fn run( + parameters: Parameters, + mut rng: R, + ) -> DKGResult<(PrivateData, PublicMessage, PublicData)> { + parameters.validate()?; + + let (private_data, public_data) = generate_data(parameters, &mut rng); + + let public_message = PublicMessage::new(&public_data); + + Ok((private_data, public_message, public_data)) + } + + fn generate_data( + parameters: Parameters, + mut rng: R, + ) -> (PrivateData, PublicData) { + let secret_polynomial = loop { + let temp_polynomial = Polynomial::generate(&mut rng, *parameters.threshold() - 1); + // There must be a secret, which is the constant coefficient of the secret polynomial + if temp_polynomial.coefficients.first().unwrap() != &Scalar::ZERO { + break temp_polynomial; + } + }; + + let secret_polynomial_commitment = PolynomialCommitment::commit(&secret_polynomial); + + // This secret key will be used to sign the proof of possession and the certificate + let secret_key = + derive_secret_key_from_secret(secret_polynomial.coefficients.first().unwrap(), rng); + + let public_key = PublicKey::from_point( + *secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap(), + ); + + let proof_of_possession = + secret_key.sign(Transcript::new(b"Proof of Possession"), &public_key); + + ( + PrivateData { + secret_key, + secret_polynomial, + }, + PublicData { + parameters, + secret_polynomial_commitment, + proof_of_possession, + }, + ) + } +} + +/// SimplPedPoP round 2. +/// +/// The participant verifies the received messages of the other participants from round 1, the secret polynomial commitments +/// and the Proofs of Possession. +/// +/// It orders the secret commitments and uses that ordering to assing random identifiers to all the participants in the +/// protocol, including its own. +/// +/// It computes the secret shares of each participant based on their identifiers, encrypts and authenticates them using +/// Chacha20Poly1305 with a shared secret. +/// +/// It signs a transcript of the protocol execution (certificate) with its secret key, which contains the PoPs and the +/// polynomial commitments from all the participants (including its own). +pub mod round2 { + use super::{ + round1, Certificate, EncryptedSecretShare, Identifier, Identifiers, Parameters, + SecretCommitment, SecretPolynomial, SecretShare, + }; + use crate::{ + context::SigningTranscript, + olaf::errors::{DKGError, DKGResult}, + verify_batch, PublicKey, SecretKey, + }; + use alloc::{ + collections::{BTreeMap, BTreeSet}, + string::ToString, + vec, + vec::Vec, + }; + use curve25519_dalek::{RistrettoPoint, Scalar}; + use derive_getters::Getters; + use merlin::Transcript; + use sha2::{digest::Update, Digest, Sha512}; + + /// The public data of round 2. + #[derive(Debug, Clone, Getters)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct PublicData { + pub(crate) identifiers: Identifiers, + pub(crate) round1_public_messages: BTreeMap, + pub(crate) transcript: Scalar, + pub(crate) public_keys: Vec, + } + + /// The public message of round 2. + #[derive(Debug, Clone)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct PublicMessage { + pub(crate) certificate: Certificate, + } + + /// Private message to sent by a participant to another participant or to the coordinator in encrypted form in round 1. + #[derive(Debug, Clone)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct PrivateMessage { + pub(crate) encrypted_secret_share: EncryptedSecretShare, + } + + impl PrivateMessage { + /// Creates a new private message. + pub fn new( + secret_share: SecretShare, + deckey: Scalar, + enckey: RistrettoPoint, + context: &[u8], + ) -> PrivateMessage { + let encrypted_secret_share = secret_share.encrypt(&deckey, &enckey, context); + + PrivateMessage { + encrypted_secret_share, + } + } + } + + /// The messages to be sent by the participant in round 2. + #[derive(Debug, Clone, Getters)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct Messages { + // The identifier is the intended recipient of the private message. + private_messages: BTreeMap, + public_message: PublicMessage, + } + + fn validate_messages( + parameters: &Parameters, + round1_public_messages: &BTreeSet, + ) -> DKGResult<()> { + if round1_public_messages.len() != *parameters.participants() as usize - 1 { + return Err(DKGError::IncorrectNumberOfRound1PublicMessages { + expected: *parameters.participants() as usize - 1, + actual: round1_public_messages.len(), + }); + } + + for round1_public_message in round1_public_messages { + if round1_public_message + .secret_polynomial_commitment + .coefficients_commitments + .len() + != *parameters.threshold() as usize + { + return Err(DKGError::InvalidSecretPolynomialCommitment { + expected: *parameters.threshold() as usize, + actual: round1_public_message + .secret_polynomial_commitment + .coefficients_commitments + .len(), + }); + } + } + + Ok(()) + } + + /// Runs the round 2 of a SimplPedPoP protocol. + pub fn run( + round1_private_data: round1::PrivateData, + round1_public_data: &round1::PublicData, + round1_public_messages: BTreeSet, + transcript: T, + ) -> DKGResult<(PublicData, Messages)> { + round1_public_data.parameters.validate()?; + + validate_messages(&round1_public_data.parameters, &round1_public_messages)?; + + let public_keys = verify_round1_messages(&round1_public_messages)?; + + let public_data = generate_public_data( + round1_public_messages, + round1_public_data, + transcript, + public_keys, + ); + + let secret_commitment = round1_public_data + .secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap(); + + let messages = generate_messages( + &public_data, + &round1_private_data.secret_polynomial, + round1_private_data.secret_key, + secret_commitment, + ); + + Ok((public_data, messages)) + } + + fn generate_public_data( + round1_public_messages: BTreeSet, + round1_public_data: &round1::PublicData, + mut transcript: T, + public_keys: Vec, + ) -> PublicData { + let mut own_inserted = false; + + let own_first_coefficient_compressed = round1_public_data + .secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap() + .compress(); + + // Writes the data of all the participants in the transcript ordered by their identifiers + for message in &round1_public_messages { + let message_first_coefficient_compressed = message + .secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap() + .compress(); + + if own_first_coefficient_compressed.0 < message_first_coefficient_compressed.0 + && !own_inserted + { + // Writes own data in the transcript + transcript.commit_point(b"SecretCommitment", &own_first_coefficient_compressed); + + for coefficient_commitment in &round1_public_data + .secret_polynomial_commitment + .coefficients_commitments + { + transcript + .commit_point(b"CoefficientCommitment", &coefficient_commitment.compress()); + } + + transcript.commit_point( + b"ProofOfPossessionR", + &round1_public_data.proof_of_possession.R, + ); + + own_inserted = true; + } + // Writes the data of the other participants in the transcript + transcript.commit_point(b"SecretCommitment", &message_first_coefficient_compressed); + + for coefficient_commitment in &message + .secret_polynomial_commitment + .coefficients_commitments + { + transcript + .commit_point(b"CoefficientCommitment", &coefficient_commitment.compress()); + } + + transcript.commit_point(b"ProofOfPossessionR", &message.proof_of_possession.R); + } + + // Writes own data in the transcript if own identifier is the last one + if !own_inserted { + transcript.commit_point(b"SecretCommitment", &own_first_coefficient_compressed); + + for coefficient_commitment in &round1_public_data + .secret_polynomial_commitment + .coefficients_commitments + { + transcript + .commit_point(b"CoefficientCommitment", &coefficient_commitment.compress()); + } + + transcript.commit_point( + b"ProofOfPossessionR", + &round1_public_data.proof_of_possession.R, + ); + } + + // Scalar generated from transcript used to generate random identifiers to the participants + let scalar = transcript.challenge_scalar(b"participants"); + + let (identifiers, round1_public_messages) = + generate_identifiers(round1_public_data, round1_public_messages, &scalar); + + PublicData { + identifiers, + round1_public_messages, + transcript: scalar, + public_keys, + } + } + + fn generate_identifiers( + round1_public_data: &round1::PublicData, + round1_public_messages_set: BTreeSet, + scalar: &Scalar, + ) -> (Identifiers, BTreeMap) { + let mut others_identifiers: BTreeSet = BTreeSet::new(); + let mut round1_public_messages: BTreeMap = + BTreeMap::new(); + + let mut secret_commitments: BTreeSet<[u8; 32]> = round1_public_messages_set + .iter() + .map(|msg| { + msg.secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap() + .compress() + .0 + }) + .collect(); + + let own_secret_commitment = round1_public_data + .secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap(); + + secret_commitments.insert(own_secret_commitment.compress().0); + + let mut index = 0; + for message in &secret_commitments { + if message == &own_secret_commitment.compress().0 { + break; + } + index += 1; + } + + for i in 0..secret_commitments.len() { + let input = Sha512::new().chain(scalar.as_bytes()).chain(i.to_string()); + let random_scalar = Scalar::from_hash(input); + others_identifiers.insert(Identifier(random_scalar)); + } + + let own_identifier = *others_identifiers.iter().nth(index).unwrap(); + others_identifiers.remove(&own_identifier); + + for (id, message) in others_identifiers.iter().zip(round1_public_messages_set) { + round1_public_messages.insert(*id, message); + } + + let identifiers = Identifiers::new(own_identifier, others_identifiers); + + (identifiers, round1_public_messages) + } + + fn verify_round1_messages( + round1_public_messages: &BTreeSet, + ) -> DKGResult> { + let len = round1_public_messages.len(); + let mut public_keys = Vec::with_capacity(len); + let mut proofs_of_possession = Vec::with_capacity(len); + + // The public keys are the secret commitments of the participants + for round1_public_message in round1_public_messages { + let public_key = PublicKey::from_point( + *round1_public_message + .secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap(), + ); + public_keys.push(public_key); + proofs_of_possession.push(round1_public_message.proof_of_possession); + } + + verify_batch( + vec![Transcript::new(b"Proof of Possession"); len], + &proofs_of_possession[..], + &public_keys[..], + false, + ) + .map_err(DKGError::InvalidProofOfPossession)?; + + Ok(public_keys) + } + + fn generate_messages( + round2_public_data: &PublicData, + secret_polynomial: &SecretPolynomial, + secret_key: SecretKey, + secret_commitment: &SecretCommitment, + ) -> Messages { + let mut private_messages = BTreeMap::new(); + let enc_keys: Vec = round2_public_data + .round1_public_messages + .values() + .map(|msg| { + *msg.secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap() + }) + .collect(); + + for (i, identifier) in round2_public_data + .identifiers + .others_identifiers + .iter() + .enumerate() + { + let secret_share = secret_polynomial.evaluate(&identifier.0); + private_messages.insert( + *identifier, + PrivateMessage::new( + SecretShare(secret_share), + secret_key.key, + enc_keys[i], + identifier.0.as_bytes(), + ), + ); + } + + let public_key = PublicKey::from_point(*secret_commitment); + + let mut transcript = Transcript::new(b"certificate"); + transcript.append_message(b"scalar", round2_public_data.transcript.as_bytes()); + + let certificate = secret_key.sign(transcript, &public_key); + + let public_message = PublicMessage { certificate }; + + Messages { + private_messages, + public_message, + } + } +} + +/// SimplPedPoP round 3. +/// +/// The participant decrypts and verifies the secret shares received from the other participants from round 2, computes +/// its own secret share and its own total secret share, which corresponds to its share of the group public key. +/// +/// It verifies the certificates from all the other participants and generates the shared public +/// key and the total secret shares commitments of the other partipants. +pub mod round3 { + use super::{ + round1, round2, Certificate, Identifier, Identifiers, Parameters, SecretPolynomial, + SecretShare, TotalSecretShare, GENERATOR, + }; + use crate::{ + context::SigningTranscript, + olaf::{ + errors::{DKGError, DKGResult}, + keys::{GroupPublicKey, GroupPublicKeyShare}, + polynomial::PolynomialCommitment, + }, + verify_batch, + }; + use alloc::{collections::BTreeMap, vec, vec::Vec}; + use curve25519_dalek::Scalar; + use derive_getters::Getters; + use merlin::Transcript; + use zeroize::ZeroizeOnDrop; + + /// The private data of round 3. + #[derive(Debug, Clone, ZeroizeOnDrop, Getters)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct PrivateData { + pub(crate) total_secret_share: TotalSecretShare, + } + + fn validate_messages( + parameters: &Parameters, + round2_public_messages: &BTreeMap, + round1_public_messages: &BTreeMap, + round2_private_messages: &BTreeMap, + ) -> DKGResult<()> { + if round2_public_messages.len() != *parameters.participants() as usize - 1 { + return Err(DKGError::IncorrectNumberOfRound2PublicMessages { + expected: *parameters.participants() as usize - 1, + actual: round2_public_messages.len(), + }); + } + + if round1_public_messages.len() != *parameters.participants() as usize - 1 { + return Err(DKGError::IncorrectNumberOfRound1PublicMessages { + expected: *parameters.participants() as usize - 1, + actual: round1_public_messages.len(), + }); + } + + if round2_private_messages.len() != *parameters.participants() as usize - 1 { + return Err(DKGError::IncorrectNumberOfRound2PrivateMessages { + expected: *parameters.participants() as usize - 1, + actual: round2_private_messages.len(), + }); + } + + Ok(()) + } + + /// Runs the round 3 of the SimplPedPoP protocol. + pub fn run( + round2_public_messages: &BTreeMap, + round2_public_data: &round2::PublicData, + round1_public_data: &round1::PublicData, + round1_private_data: round1::PrivateData, + round2_private_messages: &BTreeMap, + ) -> DKGResult<( + GroupPublicKey, + BTreeMap, + PrivateData, + )> { + round1_public_data.parameters.validate()?; + + round2_public_data + .identifiers + .validate(round1_public_data.parameters.participants)?; + + validate_messages( + &round1_public_data.parameters, + round2_public_messages, + &round2_public_data.round1_public_messages, + round2_private_messages, + )?; + + let mut transcript = Transcript::new(b"certificate"); + transcript.append_message(b"scalar", round2_public_data.transcript.as_bytes()); + + verify_round2_public_messages(round2_public_messages, round2_public_data, transcript)?; + + let secret_shares = verify_round2_private_messages( + round2_public_data, + round2_private_messages, + &round1_private_data.secret_key.key, + )?; + + let private_data = generate_private_data( + &round2_public_data.identifiers, + &secret_shares, + &round1_private_data.secret_polynomial, + )?; + + let (group_public_key, group_public_key_shares) = + generate_public_data(round2_public_data, round1_public_data, &private_data)?; + + Ok((group_public_key, group_public_key_shares, private_data)) + } + + fn verify_round2_public_messages( + round2_public_messages: &BTreeMap, + round2_public_data: &round2::PublicData, + transcript: T, + ) -> DKGResult<()> { + verify_batch( + vec![transcript.clone(); round2_public_data.identifiers.others_identifiers.len()], + &round2_public_messages + .iter() + .map(|(id, msg)| { + if !round2_public_data + .identifiers + .others_identifiers() + .contains(id) + { + Err(DKGError::UnknownIdentifierRound2PublicMessages(*id)) + } else { + Ok(msg.certificate) + } + }) + .collect::, DKGError>>()?, + &round2_public_data.public_keys[..], + false, + ) + .map_err(DKGError::InvalidCertificate) + } + + fn verify_round2_private_messages( + round2_public_data: &round2::PublicData, + round2_private_messages: &BTreeMap, + secret: &Scalar, + ) -> DKGResult> { + let mut secret_shares = BTreeMap::new(); + + for (i, (identifier, private_message)) in round2_private_messages.iter().enumerate() { + if !round2_public_data + .identifiers + .others_identifiers + .contains(identifier) + { + return Err(DKGError::UnknownIdentifierRound2PrivateMessages( + *identifier, + )); + } + + let secret_share = private_message.encrypted_secret_share.decrypt( + secret, + &round2_public_data.public_keys[i].into_point(), + round2_public_data.identifiers.own_identifier.0.as_bytes(), + )?; + + let expected_evaluation = GENERATOR * secret_share.0; + + secret_shares.insert(*identifier, secret_share); + + let evaluation = round2_public_data + .round1_public_messages + .get(identifier) + .unwrap() + .secret_polynomial_commitment + .evaluate(&round2_public_data.identifiers.own_identifier.0); + + if !(evaluation == expected_evaluation) { + return Err(DKGError::InvalidSecretShare(*identifier)); + } + } + + Ok(secret_shares) + } + + fn generate_private_data( + identifiers: &Identifiers, + secret_shares: &BTreeMap, + secret_polynomial: &SecretPolynomial, + ) -> DKGResult { + let own_secret_share = secret_polynomial.evaluate(&identifiers.own_identifier.0); + + let mut total_secret_share = Scalar::ZERO; + + for id in &identifiers.others_identifiers { + // This never fails because we previously checked + total_secret_share += secret_shares.get(id).unwrap().0; + } + + total_secret_share += own_secret_share; + + let private_data = PrivateData { + total_secret_share: SecretShare(total_secret_share), + }; + + Ok(private_data) + } + + fn generate_public_data( + round2_public_data: &round2::PublicData, + round1_public_data: &round1::PublicData, + round2_private_data: &PrivateData, + ) -> DKGResult<(GroupPublicKey, BTreeMap)> { + // Sum of the secret polynomial commitments of the other participants + let others_secret_polynomial_commitment = PolynomialCommitment::sum_polynomial_commitments( + &round2_public_data + .round1_public_messages + .values() + .map(|msg| &msg.secret_polynomial_commitment) + .collect::>(), + ); + + // The total secret polynomial commitment, which includes the secret polynomial commitment of the participant + let total_secret_polynomial_commitment = + PolynomialCommitment::sum_polynomial_commitments(&[ + &others_secret_polynomial_commitment, + &round1_public_data.secret_polynomial_commitment, + ]); + + // The group public key shares of all the participants, which correspond to the total secret shares commitments + let mut group_public_key_shares = BTreeMap::new(); + + for identifier in &round2_public_data.identifiers.others_identifiers { + let group_public_key_share = total_secret_polynomial_commitment.evaluate(&identifier.0); + + group_public_key_shares.insert(*identifier, group_public_key_share); + } + + let own_group_public_key_share = round2_private_data.total_secret_share.0 * GENERATOR; + + group_public_key_shares.insert( + round2_public_data.identifiers.own_identifier, + own_group_public_key_share, + ); + + let shared_public_key = GroupPublicKey::from_point( + *total_secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap(), + ); + + Ok((shared_public_key, group_public_key_shares)) + } +} diff --git a/src/olaf/tests.rs b/src/olaf/tests.rs new file mode 100644 index 0000000..64e7ed3 --- /dev/null +++ b/src/olaf/tests.rs @@ -0,0 +1,968 @@ +#[cfg(test)] +mod tests { + use crate::olaf::{ + errors::DKGResult, + identifier::Identifier, + keys::{GroupPublicKey, GroupPublicKeyShare}, + simplpedpop::{ + self, + round1::{self, PrivateData, PublicData, PublicMessage}, + round2::{self, Messages}, + round3, Identifiers, Parameters, + }, + }; + use alloc::{ + collections::{BTreeMap, BTreeSet}, + vec::Vec, + }; + use merlin::Transcript; + use rand::{rngs::OsRng, Rng}; + + const MAXIMUM_PARTICIPANTS: u16 = 10; + const MINIMUM_PARTICIPANTS: u16 = 3; + const MININUM_THRESHOLD: u16 = 2; + const PROTOCOL_RUNS: usize = 1; + + fn generate_parameters() -> Vec { + let mut rng = rand::thread_rng(); + let max_signers = rng.gen_range(MINIMUM_PARTICIPANTS..=MAXIMUM_PARTICIPANTS); + let min_signers = rng.gen_range(MININUM_THRESHOLD..=max_signers); + + (1..=max_signers) + .map(|_| Parameters::new(max_signers, min_signers)) + .collect() + } + + fn round1() -> ( + Vec, + Vec, + Vec, + Vec>, + ) { + let parameters_list = generate_parameters(); + + let mut all_public_messages_vec = Vec::new(); + let mut participants_round1_private_data = Vec::new(); + let mut participants_round1_public_data = Vec::new(); + + for i in 0..parameters_list.len() { + let (private_data, public_message, public_data) = + round1::run(parameters_list[i as usize].clone(), OsRng) + .expect("Round 1 should complete without errors!"); + + all_public_messages_vec.push(public_message.clone()); + participants_round1_public_data.push(public_data); + participants_round1_private_data.push(private_data); + } + + let mut received_round1_public_messages: Vec> = Vec::new(); + + let mut all_public_messages = BTreeSet::new(); + + for i in 0..parameters_list[0].participants { + all_public_messages.insert(all_public_messages_vec[i as usize].clone()); + } + + // Iterate through each participant to create a set of messages excluding their own. + for i in 0..parameters_list[0].participants { + let own_message = PublicMessage::new(&participants_round1_public_data[i as usize]); + + let mut messages_for_participant = BTreeSet::new(); + + for message in &all_public_messages { + if &own_message != message { + // Exclude the participant's own message. + messages_for_participant.insert(message.clone()); + } + } + + received_round1_public_messages.push(messages_for_participant); + } + + ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + received_round1_public_messages, + ) + } + + fn round2( + parameters_list: &Vec, + participants_round1_private_data: Vec, + participants_round1_public_data: &Vec, + participants_round1_public_messages: &Vec>, + ) -> DKGResult<( + Vec, + Vec, + Vec, + Vec, + )> { + let mut participants_round2_public_data = Vec::new(); + let mut participants_round2_public_messages = Vec::new(); + let mut participants_set_of_participants = Vec::new(); + let mut identifiers_vec = Vec::new(); + + for i in 0..parameters_list[0].participants { + let result = simplpedpop::round2::run( + participants_round1_private_data[i as usize].clone(), + &participants_round1_public_data[i as usize].clone(), + participants_round1_public_messages[i as usize].clone(), + Transcript::new(b"simplpedpop"), + )?; + + participants_round2_public_data.push(result.0.clone()); + participants_round2_public_messages.push(result.1); + participants_set_of_participants.push(result.0.identifiers.clone()); + identifiers_vec.push(result.0.identifiers.own_identifier); + } + + Ok(( + participants_round2_public_data, + participants_round2_public_messages, + participants_set_of_participants, + identifiers_vec, + )) + } + + fn round3( + participants_sets_of_participants: &Vec, + participants_round2_public_messages: &Vec, + participants_round2_public_data: &Vec, + participants_round1_public_data: &Vec, + participants_round1_private_data: Vec, + participants_round2_private_messages: Vec>, + identifiers_vec: &Vec, + ) -> DKGResult< + Vec<( + GroupPublicKey, + BTreeMap, + round3::PrivateData, + )>, + > { + let mut participant_data_round3 = Vec::new(); + + for i in 0..participants_sets_of_participants.len() { + let received_round2_public_messages = participants_round2_public_messages + .iter() + .enumerate() + .filter(|(index, _msg)| { + identifiers_vec[*index] + != participants_sets_of_participants[i as usize].own_identifier + }) + .map(|(index, msg)| (identifiers_vec[index], msg.clone())) + .collect::>(); + + let mut round2_private_messages: Vec> = + Vec::new(); + + for participants in participants_sets_of_participants.iter() { + let mut messages_for_participant = BTreeMap::new(); + + for (i, round_messages) in participants_round2_private_messages.iter().enumerate() { + if let Some(message) = round_messages.get(&participants.own_identifier) { + messages_for_participant.insert(identifiers_vec[i], message.clone()); + } + } + + round2_private_messages.push(messages_for_participant); + } + + let result = round3::run( + &received_round2_public_messages, + &participants_round2_public_data[i as usize], + &participants_round1_public_data[i as usize], + participants_round1_private_data[i as usize].clone(), + &round2_private_messages[i as usize], + )?; + + participant_data_round3.push(result); + } + + Ok(participant_data_round3) + } + + mod simplpedpop_tests { + use super::*; + use crate::{ + olaf::{ + errors::DKGError, + polynomial::{Polynomial, PolynomialCommitment}, + simplpedpop::{EncryptedSecretShare, SecretShare}, + }, + PublicKey, SecretKey, SignatureError, + }; + use curve25519_dalek::{RistrettoPoint, Scalar}; + use simplpedpop_tests::simplpedpop::GENERATOR; + + #[test] + pub fn test_successful_simplpedpop() { + for _ in 0..PROTOCOL_RUNS { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let participants_data_round3 = round3( + &participants_sets_of_participants, + &participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(), + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(), + &identifiers_vec, + ) + .unwrap(); + + let shared_public_keys: Vec = participants_data_round3 + .iter() + .map(|state| state.0) + .collect(); + + assert!( + shared_public_keys.windows(2).all(|w| w[0] == w[1]), + "All participants must have the same group public key!" + ); + + for i in 0..parameters_list[0].participants { + assert_eq!( + participants_data_round3[i as usize] + .1 + .get(&participants_sets_of_participants[i as usize].own_identifier) + .unwrap() + .compress(), + (participants_data_round3[i as usize].2.total_secret_share.0 * GENERATOR) + .compress(), + "Verification of total secret shares failed!" + ); + } + } + } + + #[test] + fn test_incorrect_number_of_round1_public_messages_in_round2() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + mut participants_round1_public_messages, + ) = round1(); + + participants_round1_public_messages[0].pop_last(); + + let result = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::IncorrectNumberOfRound1PublicMessages { + expected: parameters_list[0].participants as usize - 1, + actual: parameters_list[0].participants as usize - 2, + }, + "Expected DKGError::IncorrectNumberOfRound1PublicMessages." + ), + } + } + + #[test] + fn test_invalid_secret_polynomial_commitment_in_round2() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + mut participants_round1_public_messages, + ) = round1(); + + let mut new_message = participants_round1_public_messages[0] + .first() + .unwrap() + .clone(); + + new_message + .secret_polynomial_commitment + .coefficients_commitments + .pop(); + + participants_round1_public_messages[0].pop_first(); + participants_round1_public_messages[0].insert(new_message); + + let result = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::InvalidSecretPolynomialCommitment { + expected: *parameters_list[0].threshold() as usize, + actual: *parameters_list[0].threshold() as usize - 1, + }, + "Expected DKGError::IncorrectNumberOfRound1PublicMessages." + ), + } + } + + #[test] + fn test_invalid_secret_share_error_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let mut participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + let enc_keys: Vec = participants_round1_public_messages[1] + .iter() + .map(|msg| { + *msg.secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap() + }) + .collect(); + + let secret_share = SecretShare(Scalar::random(&mut OsRng)); + + let identifiers: BTreeSet = participants_sets_of_participants[1] + .others_identifiers + .clone(); + + let index = identifiers + .iter() + .position(|x| x == &participants_sets_of_participants[0].own_identifier) + .unwrap(); + + let enc_share = secret_share.encrypt( + &participants_round1_private_data[1].secret_key.key, + &enc_keys[index], + participants_sets_of_participants[0] + .own_identifier + .0 + .as_bytes(), + ); + + let private_message = participants_round2_private_messages[1] + .get_mut(&participants_sets_of_participants[0].own_identifier) + .unwrap(); + + private_message.encrypted_secret_share = enc_share; + + let result = round3( + &participants_sets_of_participants, + &participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(), + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_private_messages, + &identifiers_vec, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::InvalidSecretShare( + participants_sets_of_participants[1].own_identifier + ), + "Expected DKGError::InvalidSecretShare." + ), + } + } + + #[test] + fn test_decryption_error_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let mut participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + let private_message = participants_round2_private_messages[1] + .get_mut(&participants_sets_of_participants[0].own_identifier) + .unwrap(); + + private_message.encrypted_secret_share = EncryptedSecretShare(vec![1]); + + let result = round3( + &participants_sets_of_participants, + &participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(), + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_private_messages, + &identifiers_vec, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::DecryptionError(chacha20poly1305::Error), + "Expected DKGError::DecryptionError." + ), + } + } + + #[test] + fn test_invalid_proof_of_possession_in_round2() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + mut participants_round1_public_messages, + ) = round1(); + + let sk = SecretKey::generate(); + let proof_of_possession = sk.sign( + Transcript::new(b"invalid proof of possession"), + &PublicKey::from(sk.clone()), + ); + let msg = PublicMessage { + secret_polynomial_commitment: PolynomialCommitment::commit(&Polynomial::generate( + &mut OsRng, + parameters_list[0].threshold - 1, + )), + proof_of_possession, + }; + participants_round1_public_messages[0].pop_last(); + participants_round1_public_messages[0].insert(msg); + + let result = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::InvalidProofOfPossession(SignatureError::EquationFalse), + "Expected DKGError::InvalidProofOfPossession." + ), + } + } + + #[test] + pub fn test_invalid_certificate_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + mut participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + participants_round2_public_data[0].transcript = Scalar::random(&mut OsRng); + + let participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + let result = round3( + &participants_sets_of_participants, + &participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(), + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_private_messages, + &identifiers_vec, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::InvalidCertificate(SignatureError::EquationFalse), + "Expected DKGError::InvalidCertificate." + ), + } + } + + #[test] + pub fn test_incorrect_number_of_round2_public_messages_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let mut participants_round2_public_messages: Vec = + participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(); + + participants_round2_public_messages.pop(); + + let participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + let result = round3( + &participants_sets_of_participants, + &participants_round2_public_messages, + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_private_messages, + &identifiers_vec, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::IncorrectNumberOfRound2PublicMessages { + expected: *parameters_list[0].participants() as usize - 1, + actual: *parameters_list[0].participants() as usize - 2 + }, + "Expected DKGError::IncorrectNumberOfRound2PublicMessages." + ), + } + } + + #[test] + pub fn test_incorrect_number_of_round1_public_messages_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + mut participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let participants_round2_public_messages: Vec = + participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(); + + participants_round2_public_data[0] + .round1_public_messages + .pop_first(); + + let participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + let result = round3( + &participants_sets_of_participants, + &participants_round2_public_messages, + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_private_messages, + &identifiers_vec, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::IncorrectNumberOfRound1PublicMessages { + expected: *parameters_list[0].participants() as usize - 1, + actual: *parameters_list[0].participants() as usize - 2 + }, + "Expected DKGError::IncorrectNumberOfRound1PublicMessages." + ), + } + } + + #[test] + pub fn test_incorrect_number_of_round2_private_messages_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let participants_round2_public_messages: Vec = + participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(); + + let mut participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + participants_round2_private_messages[1].pop_last(); + + let result = round3( + &participants_sets_of_participants, + &participants_round2_public_messages, + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_private_messages, + &identifiers_vec, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::IncorrectNumberOfRound2PrivateMessages { + expected: *parameters_list[0].participants() as usize - 1, + actual: *parameters_list[0].participants() as usize - 2 + }, + "Expected DKGError::IncorrectNumberOfRound2PrivateMessages." + ), + } + } + + #[test] + pub fn test_unknown_identifier_from_round2_public_messages_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + mut identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let participants_round2_public_messages: Vec = + participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(); + + identifiers_vec.pop(); + let unknown_identifier = Identifier(Scalar::random(&mut OsRng)); + identifiers_vec.push(unknown_identifier); + + let participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + let result = round3( + &participants_sets_of_participants, + &participants_round2_public_messages, + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_private_messages, + &identifiers_vec, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::UnknownIdentifierRound2PublicMessages(unknown_identifier), + "Expected DKGError::UnknownIdentifierRound2PublicMessages." + ), + } + } + + #[test] + fn test_unknown_identifier_from_round2_private_messages_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let participants_round2_public_messages: Vec = + participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(); + + let mut identifiers_vec2 = identifiers_vec.clone(); + + identifiers_vec2.pop(); + + let unknown_identifier = Identifier(Scalar::ONE); + identifiers_vec2.push(unknown_identifier); + + let participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + let received_round2_public_messages = participants_round2_public_messages + .iter() + .enumerate() + .filter(|(index, _msg)| { + identifiers_vec[*index] != participants_sets_of_participants[0].own_identifier + }) + .map(|(index, msg)| (identifiers_vec[index], msg.clone())) + .collect::>(); + + let mut round2_private_messages: Vec> = + Vec::new(); + + for participants in participants_sets_of_participants.iter() { + let mut messages_for_participant = BTreeMap::new(); + + for (i, round_messages) in participants_round2_private_messages.iter().enumerate() { + if let Some(message) = round_messages.get(&participants.own_identifier) { + messages_for_participant.insert(identifiers_vec2[i], message.clone()); + } + } + + round2_private_messages.push(messages_for_participant); + } + + let result = round3::run( + &received_round2_public_messages, + &participants_round2_public_data[0], + &participants_round1_public_data[0], + participants_round1_private_data[0].clone(), + &round2_private_messages[0], + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::UnknownIdentifierRound2PrivateMessages(unknown_identifier), + "Expected DKGError::UnknownIdentifierRound2PrivateMessages." + ), + } + } + + #[test] + fn test_invalid_threshold() { + let parameters = Parameters::new(3, 1); + let result = round1::run(parameters, &mut OsRng); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::InsufficientThreshold, + "Expected DKGError::InsufficientThreshold." + ), + } + } + + #[test] + fn test_invalid_participants() { + let parameters = Parameters::new(1, 2); + let result = round1::run(parameters, &mut OsRng); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::InvalidNumberOfParticipants, + "Expected DKGError::InvalidNumberOfParticipants." + ), + } + } + + #[test] + fn test_threshold_greater_than_participants() { + let parameters = Parameters::new(2, 3); + let result = round1::run(parameters, &mut OsRng); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::ExcessiveThreshold, + "Expected DKGError::ExcessiveThreshold." + ), + } + } + + #[test] + fn test_encryption_decryption() { + let mut rng = OsRng; + let deckey = Scalar::random(&mut rng); + let enckey = RistrettoPoint::random(&mut rng); + let context = b"context"; + + let original_share = SecretShare(Scalar::random(&mut rng)); + + let encrypted_share = original_share.encrypt(&deckey, &enckey, context); + let decrypted_share = encrypted_share.decrypt(&deckey, &enckey, context); + + assert_eq!( + original_share.0, + decrypted_share.unwrap().0, + "Decryption must return the original share!" + ); + } + } +} diff --git a/src/polynomial.rs b/src/polynomial.rs new file mode 100644 index 0000000..a21150e --- /dev/null +++ b/src/polynomial.rs @@ -0,0 +1,212 @@ +//! Implementation of a polynomial and related operations. + +use alloc::vec; +use alloc::vec::Vec; +use curve25519_dalek::{traits::Identity, RistrettoPoint, Scalar}; +use rand_core::{CryptoRng, RngCore}; +use zeroize::ZeroizeOnDrop; + +use crate::simplpedpop::GENERATOR; + +pub(crate) type Coefficient = Scalar; +pub(crate) type Value = Scalar; +pub(crate) type ValueCommitment = RistrettoPoint; +pub(crate) type CoefficientCommitment = RistrettoPoint; + +/// A polynomial. +#[derive(Debug, Clone, ZeroizeOnDrop)] +pub struct Polynomial { + pub(crate) coefficients: Vec, +} + +impl Polynomial { + pub(crate) fn generate(rng: &mut R, degree: u16) -> Self { + let mut coefficients = Vec::new(); + + for _ in 0..(degree as usize + 1) { + coefficients.push(Scalar::random(rng)); + } + + Self { coefficients } + } + + pub(crate) fn evaluate(&self, x: &Value) -> Value { + let mut value = *self + .coefficients + .last() + .expect("coefficients must have at least one element"); + + // Process all coefficients except the last one, using Horner's method + for coeff in self.coefficients.iter().rev().skip(1) { + value = value * x + coeff; + } + + value + } +} + +/// A polynomial commitment. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct PolynomialCommitment { + pub(crate) coefficients_commitments: Vec, +} + +impl PolynomialCommitment { + pub(crate) fn commit(polynomial: &Polynomial) -> Self { + let coefficients_commitments = polynomial + .coefficients + .iter() + .map(|coefficient| GENERATOR * coefficient) + .collect(); + + Self { + coefficients_commitments, + } + } + + pub(crate) fn evaluate(&self, identifier: &Value) -> ValueCommitment { + let i = identifier; + + let (_, result) = self.coefficients_commitments.iter().fold( + (Scalar::ONE, RistrettoPoint::identity()), + |(i_to_the_k, sum_so_far), comm_k| (i * i_to_the_k, sum_so_far + comm_k * i_to_the_k), + ); + result + } + + pub(crate) fn sum_polynomial_commitments( + polynomials_commitments: &[&PolynomialCommitment], + ) -> PolynomialCommitment { + let max_length = polynomials_commitments + .iter() + .map(|c| c.coefficients_commitments.len()) + .max() + .unwrap_or(0); + + let mut total_commitment = vec![RistrettoPoint::identity(); max_length]; + + for polynomial_commitment in polynomials_commitments { + for (i, coeff_commitment) in polynomial_commitment + .coefficients_commitments + .iter() + .enumerate() + { + total_commitment[i] += coeff_commitment; + } + } + + PolynomialCommitment { + coefficients_commitments: total_commitment, + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + polynomial::{Coefficient, Polynomial, PolynomialCommitment}, + simplpedpop::GENERATOR, + }; + + use alloc::vec::Vec; + use curve25519_dalek::Scalar; + use rand::rngs::OsRng; + + #[test] + fn test_generate_polynomial_commitment_valid() { + let degree = 3; + + let polynomial = Polynomial::generate(&mut OsRng, degree); + + let polynomial_commitment = PolynomialCommitment::commit(&polynomial); + + assert_eq!(polynomial.coefficients.len(), degree as usize + 1); + + assert_eq!( + polynomial_commitment.coefficients_commitments.len(), + degree as usize + 1 + ); + } + + #[test] + fn test_evaluate_polynomial() { + let coefficients: Vec = + vec![Scalar::from(3u64), Scalar::from(2u64), Scalar::from(1u64)]; // Polynomial x^2 + 2x + 3 + + let polynomial = Polynomial { coefficients }; + + let value = Scalar::from(5u64); // x = 5 + + let result = polynomial.evaluate(&value); + + assert_eq!(result, Scalar::from(38u64)); // 5^2 + 2*5 + 3 + } + + #[test] + fn test_sum_secret_polynomial_commitments() { + let polynomial_commitment1 = PolynomialCommitment { + coefficients_commitments: vec![ + GENERATOR * Scalar::from(1u64), // Constant + GENERATOR * Scalar::from(2u64), // Linear + GENERATOR * Scalar::from(3u64), // Quadratic + ], + }; + + let polynomial_commitment2 = PolynomialCommitment { + coefficients_commitments: vec![ + GENERATOR * Scalar::from(4u64), // Constant + GENERATOR * Scalar::from(5u64), // Linear + GENERATOR * Scalar::from(6u64), // Quadratic + ], + }; + + let summed_polynomial_commitments = PolynomialCommitment::sum_polynomial_commitments(&[ + &polynomial_commitment1, + &polynomial_commitment2, + ]); + + let expected_coefficients_commitments = vec![ + GENERATOR * Scalar::from(5u64), // 1 + 4 = 5 + GENERATOR * Scalar::from(7u64), // 2 + 5 = 7 + GENERATOR * Scalar::from(9u64), // 3 + 6 = 9 + ]; + + assert_eq!( + summed_polynomial_commitments.coefficients_commitments, + expected_coefficients_commitments, + "Coefficient commitments do not match" + ); + } + + #[test] + fn test_evaluate_polynomial_commitment() { + // f(x) = 3 + 2x + x^2 + let constant_coefficient_commitment = Scalar::from(3u64) * GENERATOR; + let linear_commitment = Scalar::from(2u64) * GENERATOR; + let quadratic_commitment = Scalar::from(1u64) * GENERATOR; + + // Note the order and inclusion of the constant term + let coefficients_commitments = vec![ + constant_coefficient_commitment, + linear_commitment, + quadratic_commitment, + ]; + + let polynomial_commitment = PolynomialCommitment { + coefficients_commitments, + }; + + let value = Scalar::from(2u64); + + // f(2) = 11 + let expected = Scalar::from(11u64) * GENERATOR; + + let result = polynomial_commitment.evaluate(&value); + + assert_eq!( + result, expected, + "The evaluated commitment does not match the expected result" + ); + } +} diff --git a/src/serdey.rs b/src/serdey.rs index 64c5e26..f152ef5 100644 --- a/src/serdey.rs +++ b/src/serdey.rs @@ -11,51 +11,60 @@ //! ### Various and tooling related to serde #[cfg(feature = "serde")] -macro_rules! serde_boilerplate { ($t:ty) => { -impl serde_crate::Serialize for $t { - fn serialize(&self, serializer: S) -> Result where S: serde_crate::Serializer { - let bytes = &self.to_bytes()[..]; - serde_bytes::Bytes::new(bytes).serialize(serializer) - } -} - -impl<'d> serde_crate::Deserialize<'d> for $t { - fn deserialize(deserializer: D) -> Result where D: serde_crate::Deserializer<'d> { - cfg_if::cfg_if!{ - if #[cfg(feature = "std")] { - let bytes = >::deserialize(deserializer)?; - } else if #[cfg(feature = "alloc")] { - let bytes = >::deserialize(deserializer)?; - } else { - let bytes = <&::serde_bytes::Bytes>::deserialize(deserializer)?; +macro_rules! serde_boilerplate { + ($t:ty) => { + impl serde::Serialize for $t { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let bytes = &self.to_bytes()[..]; + serde_bytes::Bytes::new(bytes).serialize(serializer) } } - Self::from_bytes(bytes.as_ref()) - .map_err(crate::errors::serde_error_from_signature_error) - } -} -} } // macro_rules! serde_boilerplate + impl<'d> serde::Deserialize<'d> for $t { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'d>, + { + cfg_if::cfg_if! { + if #[cfg(feature = "std")] { + let bytes = >::deserialize(deserializer)?; + } else if #[cfg(feature = "alloc")] { + let bytes = >::deserialize(deserializer)?; + } else { + let bytes = <&::serde_bytes::Bytes>::deserialize(deserializer)?; + } + } + + Self::from_bytes(bytes.as_ref()) + .map_err(crate::errors::serde_error_from_signature_error) + } + } + }; +} // macro_rules! serde_boilerplate #[cfg(not(feature = "serde"))] -macro_rules! serde_boilerplate { ($t:ty) => { } } +macro_rules! serde_boilerplate { + ($t:ty) => {}; +} #[cfg(all(test, feature = "serde"))] mod test { use std::vec::Vec; - use bincode::{serialize, serialized_size, deserialize}; - use serde_json::{to_value, from_value, to_string, from_str}; + use bincode::{deserialize, serialize, serialized_size}; + use serde_json::{from_str, from_value, to_string, to_value}; - use curve25519_dalek::ristretto::{CompressedRistretto}; + use curve25519_dalek::ristretto::CompressedRistretto; use crate::*; - static COMPRESSED_PUBLIC_KEY : CompressedRistretto = CompressedRistretto([ - 208, 120, 140, 129, 177, 179, 237, 159, - 252, 160, 028, 013, 206, 005, 211, 241, - 192, 218, 001, 097, 130, 241, 020, 169, - 119, 046, 246, 029, 079, 080, 077, 084]); + static COMPRESSED_PUBLIC_KEY: CompressedRistretto = CompressedRistretto([ + 208, 120, 140, 129, 177, 179, 237, 159, 252, 160, 028, 013, 206, 005, 211, 241, 192, 218, + 001, 097, 130, 241, 020, 169, 119, 046, 246, 029, 079, 080, 077, 084, + ]); /* static ED25519_PUBLIC_KEY: CompressedEdwardsY = CompressedEdwardsY([ @@ -66,22 +75,17 @@ mod test { */ static ED25519_SECRET_KEY: MiniSecretKey = MiniSecretKey([ - 062, 070, 027, 163, 092, 182, 011, 003, - 077, 234, 098, 004, 011, 127, 079, 228, - 243, 187, 150, 073, 201, 137, 076, 022, - 085, 251, 152, 002, 241, 042, 072, 054, ]); + 062, 070, 027, 163, 092, 182, 011, 003, 077, 234, 098, 004, 011, 127, 079, 228, 243, 187, + 150, 073, 201, 137, 076, 022, 085, 251, 152, 002, 241, 042, 072, 054, + ]); /// Ed25519 signature with the above keypair of a blank message. static SIGNATURE_BYTES: [u8; SIGNATURE_LENGTH] = [ - 010, 126, 151, 143, 157, 064, 047, 001, - 196, 140, 179, 058, 226, 152, 018, 102, - 160, 123, 080, 016, 210, 086, 196, 028, - 053, 231, 012, 157, 169, 019, 158, 063, - 045, 154, 238, 007, 053, 185, 227, 229, - 079, 108, 213, 080, 124, 252, 084, 167, - 216, 085, 134, 144, 129, 149, 041, 081, - 063, 120, 126, 100, 092, 059, 050, 138, ]; - + 010, 126, 151, 143, 157, 064, 047, 001, 196, 140, 179, 058, 226, 152, 018, 102, 160, 123, + 080, 016, 210, 086, 196, 028, 053, 231, 012, 157, 169, 019, 158, 063, 045, 154, 238, 007, + 053, 185, 227, 229, 079, 108, 213, 080, 124, 252, 084, 167, 216, 085, 134, 144, 129, 149, + 041, 081, 063, 120, 126, 100, 092, 059, 050, 138, + ]; #[test] fn serialize_deserialize_signature() { @@ -172,21 +176,21 @@ mod test { #[test] fn serialize_public_key_size() { let public_key = PublicKey::from_compressed(COMPRESSED_PUBLIC_KEY).unwrap(); - assert_eq!(serialized_size(&public_key).unwrap(), 32+8); // Size specific to bincode==1.0.1 + assert_eq!(serialized_size(&public_key).unwrap(), 32 + 8); // Size specific to bincode==1.0.1 } #[test] fn serialize_signature_size() { let signature: Signature = Signature::from_bytes(&SIGNATURE_BYTES).unwrap(); - assert_eq!(serialized_size(&signature).unwrap(), 64+8); // Size specific to bincode==1.0.1 + assert_eq!(serialized_size(&signature).unwrap(), 64 + 8); // Size specific to bincode==1.0.1 } #[test] fn serialize_secret_key_size() { - assert_eq!(serialized_size(&ED25519_SECRET_KEY).unwrap(), 32+8); + assert_eq!(serialized_size(&ED25519_SECRET_KEY).unwrap(), 32 + 8); let secret_key = ED25519_SECRET_KEY.expand(ExpansionMode::Ed25519); - assert_eq!(serialized_size(&secret_key).unwrap(), 64+8); // Sizes specific to bincode==1.0.1 + assert_eq!(serialized_size(&secret_key).unwrap(), 64 + 8); // Sizes specific to bincode==1.0.1 let secret_key = ED25519_SECRET_KEY.expand(ExpansionMode::Uniform); - assert_eq!(serialized_size(&secret_key).unwrap(), 64+8); // Sizes specific to bincode==1.0.1 + assert_eq!(serialized_size(&secret_key).unwrap(), 64 + 8); // Sizes specific to bincode==1.0.1 } } diff --git a/src/sign.rs b/src/sign.rs index d39f9d4..1845f89 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -11,16 +11,14 @@ //! ### Schnorr signature creation and verification, including batch verification. - -use core::fmt::{Debug}; +use core::fmt::Debug; use curve25519_dalek::constants; -use curve25519_dalek::ristretto::{CompressedRistretto,RistrettoPoint}; +use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use curve25519_dalek::scalar::Scalar; use super::*; -use crate::context::{SigningTranscript,SigningContext}; - +use crate::context::{SigningContext, SigningTranscript}; // === Actual signature type === // @@ -43,7 +41,7 @@ pub struct Signature { /// This digest is then interpreted as a `Scalar` and reduced into an /// element in ℤ/lℤ. The scalar is then multiplied by the distinguished /// basepoint to produce `R`, a `RistrettoPoint`. - pub (crate) R: CompressedRistretto, + pub(crate) R: CompressedRistretto, /// `s` is a `Scalar`, formed by using an hash function with 512-bits output /// to produce the digest of: @@ -54,7 +52,7 @@ pub struct Signature { /// /// This digest is then interpreted as a `Scalar` and reduced into an /// element in ℤ/lℤ. - pub (crate) s: Scalar, + pub(crate) s: Scalar, } impl Debug for Signature { @@ -74,14 +72,14 @@ pub(crate) fn check_scalar(bytes: [u8; 32]) -> SignatureResult { // This succeed-fast trick should succeed for roughly half of all scalars. if bytes[31] & 0b11110000 == 0 { #[allow(deprecated)] // Scalar's always reduced here, so this is OK. - return Ok(Scalar::from_bits(bytes)) + return Ok(Scalar::from_bits(bytes)); } crate::scalar_from_canonical_bytes(bytes).ok_or(SignatureError::ScalarFormatError) } impl Signature { - const DESCRIPTION : &'static str = "A 64 byte Ristretto Schnorr signature"; + const DESCRIPTION: &'static str = "A 64 byte Ristretto Schnorr signature"; /* const DESCRIPTION_LONG : &'static str = "A 64 byte Ristretto Schnorr signature, similar to an ed25519 \ @@ -119,7 +117,7 @@ impl Signature { return Err(SignatureError::BytesLengthError { name: "Signature", description: Signature::DESCRIPTION, - length: SIGNATURE_LENGTH + length: SIGNATURE_LENGTH, }); } @@ -132,7 +130,10 @@ impl Signature { } upper[31] &= 127; - Ok(Signature{ R: CompressedRistretto(lower), s: check_scalar(upper) ? }) + Ok(Signature { + R: CompressedRistretto(lower), + s: check_scalar(upper)?, + }) } /// Deprecated construction of a `Signature` from a slice of bytes @@ -144,7 +145,7 @@ impl Signature { return Err(SignatureError::BytesLengthError { name: "Signature", description: Signature::DESCRIPTION, - length: SIGNATURE_LENGTH + length: SIGNATURE_LENGTH, }); } let mut bytes0: [u8; SIGNATURE_LENGTH] = [0u8; SIGNATURE_LENGTH]; @@ -156,7 +157,6 @@ impl Signature { serde_boilerplate!(Signature); - // === Implement signing and verification operations on key types === // impl SecretKey { @@ -172,54 +172,57 @@ impl SecretKey { /// should be no attacks even if both the random number generator /// fails and the function gets called with the wrong public key. #[allow(non_snake_case)] - pub fn sign(&self, mut t: T, public_key: &PublicKey) -> Signature - { + pub fn sign(&self, mut t: T, public_key: &PublicKey) -> Signature { t.proto_name(b"Schnorr-sig"); - t.commit_point(b"sign:pk",public_key.as_compressed()); + t.commit_point(b"sign:pk", public_key.as_compressed()); - let mut r = t.witness_scalar(b"signing",&[&self.nonce]); // context, message, A/public_key + let mut r = t.witness_scalar(b"signing", &[&self.nonce]); // context, message, A/public_key let R = (&r * constants::RISTRETTO_BASEPOINT_TABLE).compress(); - t.commit_point(b"sign:R",&R); + t.commit_point(b"sign:R", &R); - let k: Scalar = t.challenge_scalar(b"sign:c"); // context, message, A/public_key, R=rG + let k: Scalar = t.challenge_scalar(b"sign:c"); // context, message, A/public_key, R=rG let s: Scalar = k * self.key + r; zeroize::Zeroize::zeroize(&mut r); - Signature{ R, s } + Signature { R, s } } /// Sign a message with this `SecretKey`, but doublecheck the result. pub fn sign_doublecheck(&self, t: T, public_key: &PublicKey) -> SignatureResult - where T: SigningTranscript+Clone + where + T: SigningTranscript + Clone, { - let sig = self.sign(t.clone(),public_key); - let sig = Signature::from_bytes(& sig.to_bytes()) ?; - PublicKey::from_bytes(& public_key.to_bytes()) ? - .verify(t,&sig).map(|()| sig) + let sig = self.sign(t.clone(), public_key); + let sig = Signature::from_bytes(&sig.to_bytes())?; + PublicKey::from_bytes(&public_key.to_bytes())? + .verify(t, &sig) + .map(|()| sig) } /// Sign a message with this `SecretKey`. - pub fn sign_simple(&self, ctx: &[u8], msg: &[u8], public_key: &PublicKey) -> Signature - { + pub fn sign_simple(&self, ctx: &[u8], msg: &[u8], public_key: &PublicKey) -> Signature { let t = SigningContext::new(ctx).bytes(msg); - self.sign(t,public_key) + self.sign(t, public_key) } /// Sign a message with this `SecretKey`, but doublecheck the result. - pub fn sign_simple_doublecheck(&self, ctx: &[u8], msg: &[u8], public_key: &PublicKey) - -> SignatureResult - { + pub fn sign_simple_doublecheck( + &self, + ctx: &[u8], + msg: &[u8], + public_key: &PublicKey, + ) -> SignatureResult { let t = SigningContext::new(ctx).bytes(msg); - let sig = self.sign(t,public_key); - let sig = Signature::from_bytes(& sig.to_bytes()) ?; - PublicKey::from_bytes(& public_key.to_bytes()) ? - .verify_simple(ctx,msg,&sig).map(|()| sig) + let sig = self.sign(t, public_key); + let sig = Signature::from_bytes(&sig.to_bytes())?; + PublicKey::from_bytes(&public_key.to_bytes())? + .verify_simple(ctx, msg, &sig) + .map(|()| sig) } } - impl PublicKey { /// Verify a signature by this public key on a transcript. /// @@ -227,42 +230,54 @@ impl PublicKey { /// `SigningContext` and a message, as well as the signature /// to be verified. #[allow(non_snake_case)] - pub fn verify(&self, mut t: T, signature: &Signature) - -> SignatureResult<()> - { + pub fn verify( + &self, + mut t: T, + signature: &Signature, + ) -> SignatureResult<()> { let A: &RistrettoPoint = self.as_point(); t.proto_name(b"Schnorr-sig"); - t.commit_point(b"sign:pk",self.as_compressed()); - t.commit_point(b"sign:R",&signature.R); + t.commit_point(b"sign:pk", self.as_compressed()); + t.commit_point(b"sign:R", &signature.R); - let k: Scalar = t.challenge_scalar(b"sign:c"); // context, message, A/public_key, R=rG + let k: Scalar = t.challenge_scalar(b"sign:c"); // context, message, A/public_key, R=rG let R = RistrettoPoint::vartime_double_scalar_mul_basepoint(&k, &(-A), &signature.s); - if R.compress() == signature.R { Ok(()) } else { Err(SignatureError::EquationFalse) } + if R.compress() == signature.R { + Ok(()) + } else { + Err(SignatureError::EquationFalse) + } } /// Verify a signature by this public key on a message. - pub fn verify_simple(&self, ctx: &[u8], msg: &[u8], signature: &Signature) - -> SignatureResult<()> - { + pub fn verify_simple( + &self, + ctx: &[u8], + msg: &[u8], + signature: &Signature, + ) -> SignatureResult<()> { let t = SigningContext::new(ctx).bytes(msg); - self.verify(t,signature) + self.verify(t, signature) } /// A temporary verification routine for use in transitioning substrate testnets only. #[cfg(feature = "preaudit_deprecated")] #[allow(non_snake_case)] - pub fn verify_simple_preaudit_deprecated(&self, ctx: &'static [u8], msg: &[u8], sig: &[u8]) - -> SignatureResult<()> - { + pub fn verify_simple_preaudit_deprecated( + &self, + ctx: &'static [u8], + msg: &[u8], + sig: &[u8], + ) -> SignatureResult<()> { let t = SigningContext::new(ctx).bytes(msg); if let Ok(signature) = Signature::from_bytes(sig) { - return self.verify(t,&signature); + return self.verify(t, &signature); } - let signature = Signature::from_bytes_not_distinguished_from_ed25519(sig) ?; + let signature = Signature::from_bytes_not_distinguished_from_ed25519(sig)?; let mut t = merlin::Transcript::new(ctx); t.append_message(b"sign-bytes", msg); @@ -270,18 +285,20 @@ impl PublicKey { let A: &RistrettoPoint = self.as_point(); t.proto_name(b"Schnorr-sig"); - t.commit_point(b"pk",self.as_compressed()); - t.commit_point(b"no",&signature.R); + t.commit_point(b"pk", self.as_compressed()); + t.commit_point(b"no", &signature.R); - let k: Scalar = t.challenge_scalar(b""); // context, message, A/public_key, R=rG + let k: Scalar = t.challenge_scalar(b""); // context, message, A/public_key, R=rG let R = RistrettoPoint::vartime_double_scalar_mul_basepoint(&k, &(-A), &signature.s); - if R.compress() == signature.R { Ok(()) } else { Err(SignatureError::EquationFalse) } + if R.compress() == signature.R { + Ok(()) + } else { + Err(SignatureError::EquationFalse) + } } - } - impl Keypair { /// Sign a transcript with this keypair's secret key. /// @@ -343,14 +360,12 @@ impl Keypair { /// ``` /// // lol [terrible_idea]: https://github.com/isislovecruft/scripts/blob/master/gpgkey2bc.py - pub fn sign(&self, t: T) -> Signature - { + pub fn sign(&self, t: T) -> Signature { self.secret.sign(t, &self.public) } /// Sign a message with this keypair's secret key. - pub fn sign_simple(&self, ctx: &[u8], msg: &[u8]) -> Signature - { + pub fn sign_simple(&self, ctx: &[u8], msg: &[u8]) -> Signature { self.secret.sign_simple(ctx, msg, &self.public) } @@ -381,90 +396,100 @@ impl Keypair { /// # } /// # } /// ``` - pub fn verify(&self, t: T, signature: &Signature) -> SignatureResult<()> - { + pub fn verify(&self, t: T, signature: &Signature) -> SignatureResult<()> { self.public.verify(t, signature) } /// Verify a signature by keypair's public key on a message. - pub fn verify_simple(&self, ctx: &[u8], msg: &[u8], signature: &Signature) -> SignatureResult<()> - { + pub fn verify_simple( + &self, + ctx: &[u8], + msg: &[u8], + signature: &Signature, + ) -> SignatureResult<()> { self.public.verify_simple(ctx, msg, signature) } - /// Sign a message with this `SecretKey`, but doublecheck the result. pub fn sign_doublecheck(&self, t: T) -> SignatureResult - where T: SigningTranscript+Clone + where + T: SigningTranscript + Clone, { let sig = self.sign(t.clone()); - let sig = Signature::from_bytes(& sig.to_bytes()) ?; - PublicKey::from_bytes(& self.public.to_bytes()) ? - .verify(t,&sig).map(|()| sig) + let sig = Signature::from_bytes(&sig.to_bytes())?; + PublicKey::from_bytes(&self.public.to_bytes())? + .verify(t, &sig) + .map(|()| sig) } /// Sign a message with this `SecretKey`, but doublecheck the result. - pub fn sign_simple_doublecheck(&self, ctx: &[u8], msg: &[u8]) - -> SignatureResult - { + pub fn sign_simple_doublecheck(&self, ctx: &[u8], msg: &[u8]) -> SignatureResult { let t = SigningContext::new(ctx).bytes(msg); let sig = self.sign(t); - let sig = Signature::from_bytes(& sig.to_bytes()) ?; - PublicKey::from_bytes(& self.public.to_bytes()) ? - .verify_simple(ctx,msg,&sig).map(|()| sig) + let sig = Signature::from_bytes(&sig.to_bytes())?; + PublicKey::from_bytes(&self.public.to_bytes())? + .verify_simple(ctx, msg, &sig) + .map(|()| sig) } - } - #[cfg(test)] mod test { + use curve25519_dalek::digest::Update; use sha3::Shake128; - use curve25519_dalek::digest::{Update}; use super::super::*; - #[cfg(feature = "getrandom")] #[test] fn sign_verify_bytes() { let good_sig: Signature; - let bad_sig: Signature; + let bad_sig: Signature; let ctx = signing_context(b"good"); let good: &[u8] = "test message".as_bytes(); - let bad: &[u8] = "wrong message".as_bytes(); + let bad: &[u8] = "wrong message".as_bytes(); let mut csprng = rand_core::OsRng; let keypair = Keypair::generate_with(&mut csprng); good_sig = keypair.sign(ctx.bytes(&good)); - bad_sig = keypair.sign(ctx.bytes(&bad)); + bad_sig = keypair.sign(ctx.bytes(&bad)); let good_sig = Signature::from_bytes(&good_sig.to_bytes()[..]).unwrap(); - let bad_sig = Signature::from_bytes(&bad_sig.to_bytes()[..]).unwrap(); - - assert!(keypair.verify(ctx.bytes(&good), &good_sig).is_ok(), - "Verification of a valid signature failed!"); - assert!(!keypair.verify(ctx.bytes(&good), &bad_sig).is_ok(), - "Verification of a signature on a different message passed!"); - assert!(!keypair.verify(ctx.bytes(&bad), &good_sig).is_ok(), - "Verification of a signature on a different message passed!"); - assert!(!keypair.verify(signing_context(b"bad").bytes(&good), &good_sig).is_ok(), - "Verification of a signature on a different message passed!"); + let bad_sig = Signature::from_bytes(&bad_sig.to_bytes()[..]).unwrap(); + + assert!( + keypair.verify(ctx.bytes(&good), &good_sig).is_ok(), + "Verification of a valid signature failed!" + ); + assert!( + !keypair.verify(ctx.bytes(&good), &bad_sig).is_ok(), + "Verification of a signature on a different message passed!" + ); + assert!( + !keypair.verify(ctx.bytes(&bad), &good_sig).is_ok(), + "Verification of a signature on a different message passed!" + ); + assert!( + !keypair + .verify(signing_context(b"bad").bytes(&good), &good_sig) + .is_ok(), + "Verification of a signature on a different message passed!" + ); } #[cfg(feature = "getrandom")] #[test] fn sign_verify_xof() { let good_sig: Signature; - let bad_sig: Signature; + let bad_sig: Signature; let ctx = signing_context(b"testing testing 1 2 3"); let good: &[u8] = b"test message"; - let bad: &[u8] = b"wrong message"; + let bad: &[u8] = b"wrong message"; let prehashed_good: Shake128 = Shake128::default().chain(good); let prehashed_bad: Shake128 = Shake128::default().chain(bad); @@ -474,32 +499,50 @@ mod test { let keypair = Keypair::generate_with(&mut csprng); good_sig = keypair.sign(ctx.xof(prehashed_good.clone())); - bad_sig = keypair.sign(ctx.xof(prehashed_bad.clone())); + bad_sig = keypair.sign(ctx.xof(prehashed_bad.clone())); let good_sig_d = Signature::from_bytes(&good_sig.to_bytes()[..]).unwrap(); - let bad_sig_d = Signature::from_bytes(&bad_sig.to_bytes()[..]).unwrap(); + let bad_sig_d = Signature::from_bytes(&bad_sig.to_bytes()[..]).unwrap(); assert_eq!(good_sig, good_sig_d); assert_eq!(bad_sig, bad_sig_d); - assert!(keypair.verify(ctx.xof(prehashed_good.clone()), &good_sig).is_ok(), - "Verification of a valid signature failed!"); - assert!(! keypair.verify(ctx.xof(prehashed_good.clone()), &bad_sig).is_ok(), - "Verification of a signature on a different message passed!"); - assert!(! keypair.verify(ctx.xof(prehashed_bad.clone()), &good_sig).is_ok(), - "Verification of a signature on a different message passed!"); - assert!(! keypair.verify(signing_context(b"oops").xof(prehashed_good), &good_sig).is_ok(), - "Verification of a signature on a different message passed!"); + assert!( + keypair + .verify(ctx.xof(prehashed_good.clone()), &good_sig) + .is_ok(), + "Verification of a valid signature failed!" + ); + assert!( + !keypair + .verify(ctx.xof(prehashed_good.clone()), &bad_sig) + .is_ok(), + "Verification of a signature on a different message passed!" + ); + assert!( + !keypair + .verify(ctx.xof(prehashed_bad.clone()), &good_sig) + .is_ok(), + "Verification of a signature on a different message passed!" + ); + assert!( + !keypair + .verify(signing_context(b"oops").xof(prehashed_good), &good_sig) + .is_ok(), + "Verification of a signature on a different message passed!" + ); } #[cfg(feature = "preaudit_deprecated")] #[test] fn can_verify_know_preaudit_deprecated_message() { use hex_literal::hex; - const SIGNING_CTX : &'static [u8] = b"substrate"; + const SIGNING_CTX: &'static [u8] = b"substrate"; let message = b"Verifying that I am the owner of 5G9hQLdsKQswNPgB499DeA5PkFBbgkLPJWkkS6FAM6xGQ8xD. Hash: 221455a3\n"; let public = hex!("b4bfa1f7a5166695eb75299fd1c4c03ea212871c342f2c5dfea0902b2c246918"); let public = PublicKey::from_bytes(&public[..]).unwrap(); let signature = hex!("5a9755f069939f45d96aaf125cf5ce7ba1db998686f87f2fb3cbdea922078741a73891ba265f70c31436e18a9acd14d189d73c12317ab6c313285cd938453202"); - assert!( public.verify_simple_preaudit_deprecated(SIGNING_CTX,message,&signature[..]).is_ok() ); + assert!(public + .verify_simple_preaudit_deprecated(SIGNING_CTX, message, &signature[..]) + .is_ok()); } } diff --git a/src/simplpedpop.rs b/src/simplpedpop.rs new file mode 100644 index 0000000..f48ed9a --- /dev/null +++ b/src/simplpedpop.rs @@ -0,0 +1,1883 @@ +//! Implementation of a modified version of SimplPedPoP (), a DKG based on PedPoP, which in turn is based +//! on Pedersen's DKG. All of them have as the fundamental building block the Shamir's Secret Sharing scheme. +//! +//! The modification consists of each participant sending the secret shares of the other participants only in round 2 +//! instead of in round 1. The reason for this is we use the secret commitments (the evaluations of the secret polynomial +//! commitments at zero) of round 1 to assign the identifiers of all the participants in round 2, which will then be +//! used to compute the corresponding secret shares. Finally, we encrypt and authenticate the secret shares with +//! Chacha20Poly1305, meaning they can be distributed to the participants by an untrusted coordinator instead of sending +//! them directly. +//! +//! The protocol is divided into three rounds. In each round some data and some messages are produced and some messages +//! are verified (if received from a previous round). Data is divided into public and private because in a given round we +//! want to pass a reference to the public data (performance reasons) and the private data itself so that it is zeroized +//! after getting out of scope (security reasons). Public messages are destined to all the other participants, while private +//! messages are destined to a single participant. + +use super::{ + errors::{DKGError, DKGResult}, + identifier::Identifier, + polynomial::{Coefficient, CoefficientCommitment, Polynomial, PolynomialCommitment}, +}; +use crate::{aead::make_aead, context::SigningTranscript, PublicKey, SecretKey, Signature}; +use alloc::{collections::BTreeSet, vec::Vec}; +use chacha20poly1305::{aead::Aead, ChaCha20Poly1305, Nonce}; +use curve25519_dalek::{constants::RISTRETTO_BASEPOINT_POINT, RistrettoPoint, Scalar}; +use derive_getters::Getters; +use merlin::Transcript; +use rand_core::{CryptoRng, RngCore}; +use zeroize::ZeroizeOnDrop; + +pub(crate) const GENERATOR: RistrettoPoint = RISTRETTO_BASEPOINT_POINT; + +/// The group public key generated by the SimplPedPoP protocol. +pub type GroupPublicKey = PublicKey; +/// The group public key share of each participant in the SimplPedPoP protocol. +pub type GroupPublicKeyShare = CoefficientCommitment; +pub(crate) type SecretPolynomialCommitment = PolynomialCommitment; +pub(crate) type SecretPolynomial = Polynomial; +pub(crate) type TotalSecretShare = SecretShare; +pub(crate) type SecretCommitment = CoefficientCommitment; +pub(crate) type Certificate = Signature; +pub(crate) type ProofOfPossession = Signature; +pub(crate) type Secret = Coefficient; + +/// The parameters of a given execution of the SimplPedPoP protocol. +#[derive(Debug, Clone, Getters)] +pub struct Parameters { + pub(crate) participants: u16, + pub(crate) threshold: u16, +} + +impl Parameters { + /// Create new parameters. + pub fn new(participants: u16, threshold: u16) -> Parameters { + Parameters { + participants, + threshold, + } + } + + pub(crate) fn validate(&self) -> Result<(), DKGError> { + if self.threshold < 2 { + return Err(DKGError::InsufficientThreshold); + } + + if self.participants < 2 { + return Err(DKGError::InvalidNumberOfParticipants); + } + + if self.threshold > self.participants { + return Err(DKGError::ExcessiveThreshold); + } + + Ok(()) + } +} + +/// The participants of a given execution of the SimplPedPoP protocol. +#[derive(Debug, Clone, Getters)] +pub struct Identifiers { + pub(crate) own_identifier: Identifier, + pub(crate) others_identifiers: BTreeSet, +} + +impl Identifiers { + /// Create new participants. + pub fn new( + own_identifier: Identifier, + others_identifiers: BTreeSet, + ) -> Identifiers { + Identifiers { + own_identifier, + others_identifiers, + } + } + + pub(crate) fn validate(&self, participants: u16) -> Result<(), DKGError> { + if self.own_identifier.0 == Scalar::ZERO { + return Err(DKGError::InvalidIdentifier); + } + + for other_identifier in &self.others_identifiers { + if other_identifier.0 == Scalar::ZERO { + return Err(DKGError::InvalidIdentifier); + } + } + + if self.others_identifiers.len() != participants as usize - 1 { + return Err(DKGError::IncorrectNumberOfIdentifiers { + expected: participants as usize, + actual: self.others_identifiers.len() + 1, + }); + } + + Ok(()) + } +} + +fn derive_secret_key_from_secret(secret: &Secret, mut rng: R) -> SecretKey { + let mut bytes = [0u8; 64]; + let mut nonce: [u8; 32] = [0u8; 32]; + + rng.fill_bytes(&mut nonce); + let secret_bytes = secret.to_bytes(); + + bytes[..32].copy_from_slice(&secret_bytes[..]); + bytes[32..].copy_from_slice(&nonce[..]); + + SecretKey::from_bytes(&bytes[..]).unwrap() // This never fails because bytes has length 64 and the key is a scalar +} + +/// A secret share, which corresponds to an evaluation of a value that identifies a participant in a secret polynomial. +#[derive(Debug, Clone, ZeroizeOnDrop)] +pub struct SecretShare(pub(crate) Scalar); + +impl SecretShare { + pub(crate) fn encrypt( + &self, + decryption_key: &Scalar, + encryption_key: &RistrettoPoint, + context: &[u8], + ) -> EncryptedSecretShare { + let shared_secret = decryption_key * encryption_key; + + let mut transcript = Transcript::new(b"encryption"); + transcript.commit_point(b"shared secret", &shared_secret.compress()); + transcript.append_message(b"context", context); + + let mut bytes = [0; 12]; + transcript.challenge_bytes(b"nonce", &mut bytes); + + let cipher: ChaCha20Poly1305 = make_aead::(transcript); + let nonce = Nonce::from_slice(&bytes[..]); + + let ciphertext: Vec = cipher.encrypt(nonce, &self.0.as_bytes()[..]).unwrap(); + + EncryptedSecretShare(ciphertext) + } +} + +/// An encrypted secret share, which can be sent directly to the intended participant or through an untrusted coordinator. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct EncryptedSecretShare(pub(crate) Vec); + +impl EncryptedSecretShare { + pub(crate) fn decrypt( + &self, + decryption_key: &Scalar, + encryption_key: &RistrettoPoint, + context: &[u8], + ) -> DKGResult { + let shared_secret = decryption_key * encryption_key; + + let mut transcript = Transcript::new(b"encryption"); + transcript.commit_point(b"shared secret", &shared_secret.compress()); + transcript.append_message(b"context", context); + + let mut bytes = [0; 12]; + transcript.challenge_bytes(b"nonce", &mut bytes); + + let cipher: ChaCha20Poly1305 = make_aead::(transcript); + let nonce = Nonce::from_slice(&bytes[..]); + + let plaintext = cipher + .decrypt(nonce, &self.0[..]) + .map_err(DKGError::DecryptionError)?; + + let mut bytes = [0; 32]; + bytes.copy_from_slice(&plaintext); + + Ok(SecretShare(Scalar::from_bytes_mod_order(bytes))) + } +} + +/// SimplPedPoP round 1. +/// +/// The participant commits to a secret polynomial f(x) of degree t-1, where t is the threshold of the DKG, by commiting +/// to each one of the t coefficients of the secret polynomial. +/// +/// It derives a secret key from the secret of the polynomial f(0) and uses it to generate a Proof of Possession of that +/// secret by signing a message with the secret key. +pub mod round1 { + use super::{ + derive_secret_key_from_secret, Parameters, ProofOfPossession, SecretPolynomial, + SecretPolynomialCommitment, + }; + use crate::{ + errors::DKGResult, + polynomial::{Polynomial, PolynomialCommitment}, + PublicKey, SecretKey, + }; + use core::cmp::Ordering; + use curve25519_dalek::Scalar; + use merlin::Transcript; + use rand_core::{CryptoRng, RngCore}; + + /// The private data generated by the participant in round 1. + #[derive(Debug, Clone)] + pub struct PrivateData { + pub(crate) secret_key: SecretKey, + pub(crate) secret_polynomial: SecretPolynomial, + } + + /// The public data generated by the participant in round 1. + #[derive(Debug, Clone)] + pub struct PublicData { + pub(crate) parameters: Parameters, + pub(crate) secret_polynomial_commitment: SecretPolynomialCommitment, + pub(crate) proof_of_possession: ProofOfPossession, + } + + /// Public message to be sent by the participant to all the other participants or to the coordinator in round 1. + #[derive(Debug, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct PublicMessage { + pub(crate) secret_polynomial_commitment: SecretPolynomialCommitment, + pub(crate) proof_of_possession: ProofOfPossession, + } + + impl PublicMessage { + /// Creates a new public message. + pub fn new(public_data: &PublicData) -> PublicMessage { + PublicMessage { + secret_polynomial_commitment: public_data.secret_polynomial_commitment.clone(), + proof_of_possession: public_data.proof_of_possession, + } + } + } + + impl PartialOrd for PublicMessage { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + impl Ord for PublicMessage { + fn cmp(&self, other: &Self) -> Ordering { + self.secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap() + .compress() + .0 + .cmp( + &other + .secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap() + .compress() + .0, + ) + } + } + + /// Runs the round 1 of the SimplPedPoP protocol. + pub fn run( + parameters: Parameters, + mut rng: R, + ) -> DKGResult<(PrivateData, PublicMessage, PublicData)> { + parameters.validate()?; + + let (private_data, public_data) = generate_data(parameters, &mut rng); + + let public_message = PublicMessage::new(&public_data); + + Ok((private_data, public_message, public_data)) + } + + fn generate_data( + parameters: Parameters, + mut rng: R, + ) -> (PrivateData, PublicData) { + let secret_polynomial = loop { + let temp_polynomial = Polynomial::generate(&mut rng, *parameters.threshold() - 1); + // There must be a secret, which is the constant coefficient of the secret polynomial + if temp_polynomial.coefficients.first().unwrap() != &Scalar::ZERO { + break temp_polynomial; + } + }; + + let secret_polynomial_commitment = PolynomialCommitment::commit(&secret_polynomial); + + // This secret key will be used to sign the proof of possession and the certificate + let secret_key = + derive_secret_key_from_secret(secret_polynomial.coefficients.first().unwrap(), rng); + + let public_key = PublicKey::from_point( + *secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap(), + ); + + let proof_of_possession = + secret_key.sign(Transcript::new(b"Proof of Possession"), &public_key); + + ( + PrivateData { + secret_key, + secret_polynomial, + }, + PublicData { + parameters, + secret_polynomial_commitment, + proof_of_possession, + }, + ) + } +} + +/// SimplPedPoP round 2. +/// +/// The participant verifies the received messages of the other participants from round 1, the secret polynomial commitments +/// and the Proofs of Possession. +/// +/// It orders the secret commitments and uses that ordering to assing random identifiers to all the participants in the +/// protocol, including its own. +/// +/// It computes the secret shares of each participant based on their identifiers, encrypts and authenticates them using +/// Chacha20Poly1305 with a shared secret. +/// +/// It signs a transcript of the protocol execution (certificate) with its secret key, which contains the PoPs and the +/// polynomial commitments from all the participants (including its own). +pub mod round2 { + use super::{ + round1, Certificate, EncryptedSecretShare, Identifier, Identifiers, Parameters, + SecretCommitment, SecretPolynomial, SecretShare, + }; + use crate::{ + context::SigningTranscript, + errors::{DKGError, DKGResult}, + verify_batch, PublicKey, SecretKey, + }; + use alloc::{ + collections::{BTreeMap, BTreeSet}, + string::ToString, + vec, + vec::Vec, + }; + use curve25519_dalek::{RistrettoPoint, Scalar}; + use derive_getters::Getters; + use merlin::Transcript; + use sha2::{digest::Update, Digest, Sha512}; + + /// The public data of round 2. + #[derive(Debug, Clone, Getters)] + pub struct PublicData { + pub(crate) identifiers: Identifiers, + pub(crate) round1_public_messages: BTreeMap, + pub(crate) transcript: T, + pub(crate) public_keys: Vec, + } + + /// The public message of round 2. + #[derive(Debug, Clone)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct PublicMessage { + pub(crate) certificate: Certificate, + } + + /// Private message to sent by a participant to another participant or to the coordinator in encrypted form in round 1. + #[derive(Debug, Clone)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct PrivateMessage { + pub(crate) encrypted_secret_share: EncryptedSecretShare, + } + + impl PrivateMessage { + /// Creates a new private message. + pub fn new( + secret_share: SecretShare, + deckey: Scalar, + enckey: RistrettoPoint, + context: &[u8], + ) -> PrivateMessage { + let encrypted_secret_share = secret_share.encrypt(&deckey, &enckey, context); + + PrivateMessage { + encrypted_secret_share, + } + } + } + + /// The messages to be sent by the participant in round 2. + #[derive(Debug, Clone, Getters)] + pub struct Messages { + // The identifier is the intended recipient of the private message. + private_messages: BTreeMap, + public_message: PublicMessage, + } + + fn validate_messages( + parameters: &Parameters, + round1_public_messages: &BTreeSet, + ) -> DKGResult<()> { + if round1_public_messages.len() != *parameters.participants() as usize - 1 { + return Err(DKGError::IncorrectNumberOfRound1PublicMessages { + expected: *parameters.participants() as usize - 1, + actual: round1_public_messages.len(), + }); + } + + for round1_public_message in round1_public_messages { + if round1_public_message + .secret_polynomial_commitment + .coefficients_commitments + .len() + != *parameters.threshold() as usize + { + return Err(DKGError::InvalidSecretPolynomialCommitment { + expected: *parameters.threshold() as usize, + actual: round1_public_message + .secret_polynomial_commitment + .coefficients_commitments + .len(), + }); + } + } + + Ok(()) + } + + /// Runs the round 2 of a SimplPedPoP protocol. + pub fn run( + round1_private_data: round1::PrivateData, + round1_public_data: &round1::PublicData, + round1_public_messages: BTreeSet, + transcript: T, + ) -> DKGResult<(PublicData, Messages)> { + round1_public_data.parameters.validate()?; + + validate_messages(&round1_public_data.parameters, &round1_public_messages)?; + + let public_keys = verify_round1_messages(&round1_public_messages)?; + + let public_data = generate_public_data( + round1_public_messages, + round1_public_data, + transcript, + public_keys, + ); + + let secret_commitment = round1_public_data + .secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap(); + + let messages = generate_messages( + &public_data, + &round1_private_data.secret_polynomial, + round1_private_data.secret_key, + secret_commitment, + ); + + Ok((public_data, messages)) + } + + fn generate_public_data( + round1_public_messages: BTreeSet, + round1_public_data: &round1::PublicData, + mut transcript: T, + public_keys: Vec, + ) -> PublicData { + let mut own_inserted = false; + + let own_first_coefficient_compressed = round1_public_data + .secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap() + .compress(); + + // Writes the data of all the participants in the transcript ordered by their identifiers + for message in &round1_public_messages { + let message_first_coefficient_compressed = message + .secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap() + .compress(); + + if own_first_coefficient_compressed.0 < message_first_coefficient_compressed.0 + && !own_inserted + { + // Writes own data in the transcript + transcript.commit_point(b"SecretCommitment", &own_first_coefficient_compressed); + + for coefficient_commitment in &round1_public_data + .secret_polynomial_commitment + .coefficients_commitments + { + transcript + .commit_point(b"CoefficientCommitment", &coefficient_commitment.compress()); + } + + transcript.commit_point( + b"ProofOfPossessionR", + &round1_public_data.proof_of_possession.R, + ); + + own_inserted = true; + } + // Writes the data of the other participants in the transcript + transcript.commit_point(b"SecretCommitment", &message_first_coefficient_compressed); + + for coefficient_commitment in &message + .secret_polynomial_commitment + .coefficients_commitments + { + transcript + .commit_point(b"CoefficientCommitment", &coefficient_commitment.compress()); + } + + transcript.commit_point(b"ProofOfPossessionR", &message.proof_of_possession.R); + } + + // Writes own data in the transcript if own identifier is the last one + if !own_inserted { + transcript.commit_point(b"SecretCommitment", &own_first_coefficient_compressed); + + for coefficient_commitment in &round1_public_data + .secret_polynomial_commitment + .coefficients_commitments + { + transcript + .commit_point(b"CoefficientCommitment", &coefficient_commitment.compress()); + } + + transcript.commit_point( + b"ProofOfPossessionR", + &round1_public_data.proof_of_possession.R, + ); + } + + // Scalar generated from transcript used to generate random identifiers to the participants + let scalar = transcript.challenge_scalar(b"participants"); + + let (identifiers, round1_public_messages) = + generate_identifiers(round1_public_data, round1_public_messages, &scalar); + + PublicData { + identifiers, + round1_public_messages, + transcript, + public_keys, + } + } + + fn generate_identifiers( + round1_public_data: &round1::PublicData, + round1_public_messages_set: BTreeSet, + scalar: &Scalar, + ) -> (Identifiers, BTreeMap) { + let mut others_identifiers: BTreeSet = BTreeSet::new(); + let mut round1_public_messages: BTreeMap = + BTreeMap::new(); + + let mut secret_commitments: BTreeSet<[u8; 32]> = round1_public_messages_set + .iter() + .map(|msg| { + msg.secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap() + .compress() + .0 + }) + .collect(); + + let own_secret_commitment = round1_public_data + .secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap(); + + secret_commitments.insert(own_secret_commitment.compress().0); + + let mut index = 0; + for message in &secret_commitments { + if message == &own_secret_commitment.compress().0 { + break; + } + index += 1; + } + + for i in 0..secret_commitments.len() { + let input = Sha512::new().chain(scalar.as_bytes()).chain(i.to_string()); + let random_scalar = Scalar::from_hash(input); + others_identifiers.insert(Identifier(random_scalar)); + } + + let own_identifier = *others_identifiers.iter().nth(index).unwrap(); + others_identifiers.remove(&own_identifier); + + for (id, message) in others_identifiers.iter().zip(round1_public_messages_set) { + round1_public_messages.insert(*id, message); + } + + let identifiers = Identifiers::new(own_identifier, others_identifiers); + + (identifiers, round1_public_messages) + } + + fn verify_round1_messages( + round1_public_messages: &BTreeSet, + ) -> DKGResult> { + let len = round1_public_messages.len(); + let mut public_keys = Vec::with_capacity(len); + let mut proofs_of_possession = Vec::with_capacity(len); + + // The public keys are the secret commitments of the participants + for round1_public_message in round1_public_messages { + let public_key = PublicKey::from_point( + *round1_public_message + .secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap(), + ); + public_keys.push(public_key); + proofs_of_possession.push(round1_public_message.proof_of_possession); + } + + verify_batch( + vec![Transcript::new(b"Proof of Possession"); len], + &proofs_of_possession[..], + &public_keys[..], + false, + ) + .map_err(DKGError::InvalidProofOfPossession)?; + + Ok(public_keys) + } + + fn generate_messages( + round2_public_data: &PublicData, + secret_polynomial: &SecretPolynomial, + secret_key: SecretKey, + secret_commitment: &SecretCommitment, + ) -> Messages { + let mut private_messages = BTreeMap::new(); + let enc_keys: Vec = round2_public_data + .round1_public_messages + .values() + .map(|msg| { + *msg.secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap() + }) + .collect(); + + for (i, identifier) in round2_public_data + .identifiers + .others_identifiers + .iter() + .enumerate() + { + let secret_share = secret_polynomial.evaluate(&identifier.0); + private_messages.insert( + *identifier, + PrivateMessage::new( + SecretShare(secret_share), + secret_key.key, + enc_keys[i], + identifier.0.as_bytes(), + ), + ); + } + + let public_key = PublicKey::from_point(*secret_commitment); + + let certificate = secret_key.sign(round2_public_data.transcript.clone(), &public_key); + + let public_message = PublicMessage { certificate }; + + Messages { + private_messages, + public_message, + } + } +} + +/// SimplPedPoP round 3. +/// +/// The participant decrypts and verifies the secret shares received from the other participants from round 2, computes +/// its own secret share and its own total secret share, which corresponds to its share of the group public key. +/// +/// It verifies the certificates from all the other participants and generates the shared public +/// key and the total secret shares commitments of the other partipants. +pub mod round3 { + use super::{ + round1, round2, Certificate, GroupPublicKey, GroupPublicKeyShare, Identifier, Identifiers, + Parameters, SecretPolynomial, SecretShare, TotalSecretShare, GENERATOR, + }; + use crate::{ + context::SigningTranscript, + errors::{DKGError, DKGResult}, + polynomial::PolynomialCommitment, + verify_batch, + }; + use alloc::{collections::BTreeMap, vec, vec::Vec}; + use curve25519_dalek::Scalar; + use derive_getters::Getters; + use zeroize::ZeroizeOnDrop; + + /// The private data of round 3. + #[derive(Debug, Clone, ZeroizeOnDrop, Getters)] + pub struct PrivateData { + pub(crate) total_secret_share: TotalSecretShare, + } + + fn validate_messages( + parameters: &Parameters, + round2_public_messages: &BTreeMap, + round1_public_messages: &BTreeMap, + round2_private_messages: &BTreeMap, + ) -> DKGResult<()> { + if round2_public_messages.len() != *parameters.participants() as usize - 1 { + return Err(DKGError::IncorrectNumberOfRound2PublicMessages { + expected: *parameters.participants() as usize - 1, + actual: round2_public_messages.len(), + }); + } + + if round1_public_messages.len() != *parameters.participants() as usize - 1 { + return Err(DKGError::IncorrectNumberOfRound1PublicMessages { + expected: *parameters.participants() as usize - 1, + actual: round1_public_messages.len(), + }); + } + + if round2_private_messages.len() != *parameters.participants() as usize - 1 { + return Err(DKGError::IncorrectNumberOfRound2PrivateMessages { + expected: *parameters.participants() as usize - 1, + actual: round2_private_messages.len(), + }); + } + + Ok(()) + } + + /// Runs the round 3 of the SimplPedPoP protocol. + pub fn run( + round2_public_messages: &BTreeMap, + round2_public_data: &round2::PublicData, + round1_public_data: &round1::PublicData, + round1_private_data: round1::PrivateData, + round2_private_messages: &BTreeMap, + ) -> DKGResult<( + GroupPublicKey, + BTreeMap, + PrivateData, + )> { + round1_public_data.parameters.validate()?; + + round2_public_data + .identifiers + .validate(round1_public_data.parameters.participants)?; + + validate_messages( + &round1_public_data.parameters, + round2_public_messages, + &round2_public_data.round1_public_messages, + round2_private_messages, + )?; + + verify_round2_public_messages(round2_public_messages, round2_public_data)?; + + let secret_shares = verify_round2_private_messages( + round2_public_data, + round2_private_messages, + &round1_private_data.secret_key.key, + )?; + + let private_data = generate_private_data( + &round2_public_data.identifiers, + &secret_shares, + &round1_private_data.secret_polynomial, + )?; + + let (group_public_key, group_public_key_shares) = + generate_public_data(round2_public_data, round1_public_data)?; + + Ok((group_public_key, group_public_key_shares, private_data)) + } + + fn verify_round2_public_messages( + round2_public_messages: &BTreeMap, + round2_public_data: &round2::PublicData, + ) -> DKGResult<()> { + verify_batch( + vec![ + round2_public_data.transcript.clone(); + round2_public_data.identifiers.others_identifiers.len() + ], + &round2_public_messages + .iter() + .map(|(id, msg)| { + if !round2_public_data + .identifiers + .others_identifiers() + .contains(id) + { + Err(DKGError::UnknownIdentifierRound2PublicMessages(*id)) + } else { + Ok(msg.certificate) + } + }) + .collect::, DKGError>>()?, + &round2_public_data.public_keys[..], + false, + ) + .map_err(DKGError::InvalidCertificate) + } + + fn verify_round2_private_messages( + round2_public_data: &round2::PublicData, + round2_private_messages: &BTreeMap, + secret: &Scalar, + ) -> DKGResult> { + let mut secret_shares = BTreeMap::new(); + + for (i, (identifier, private_message)) in round2_private_messages.iter().enumerate() { + if !round2_public_data + .identifiers + .others_identifiers + .contains(identifier) + { + return Err(DKGError::UnknownIdentifierRound2PrivateMessages( + *identifier, + )); + } + + let secret_share = private_message.encrypted_secret_share.decrypt( + secret, + &round2_public_data.public_keys[i].into_point(), + round2_public_data.identifiers.own_identifier.0.as_bytes(), + )?; + + let expected_evaluation = GENERATOR * secret_share.0; + + secret_shares.insert(*identifier, secret_share); + + let evaluation = round2_public_data + .round1_public_messages + .get(identifier) + .unwrap() + .secret_polynomial_commitment + .evaluate(&round2_public_data.identifiers.own_identifier.0); + + if !(evaluation == expected_evaluation) { + return Err(DKGError::InvalidSecretShare(*identifier)); + } + } + + Ok(secret_shares) + } + + fn generate_private_data( + identifiers: &Identifiers, + secret_shares: &BTreeMap, + secret_polynomial: &SecretPolynomial, + ) -> DKGResult { + let own_secret_share = secret_polynomial.evaluate(&identifiers.own_identifier.0); + + let mut total_secret_share = Scalar::ZERO; + + for id in &identifiers.others_identifiers { + // This never fails because we previously checked + total_secret_share += secret_shares.get(id).unwrap().0; + } + + total_secret_share += own_secret_share; + + let private_data = PrivateData { + total_secret_share: SecretShare(total_secret_share), + }; + + Ok(private_data) + } + + fn generate_public_data( + round2_public_data: &round2::PublicData, + round1_public_data: &round1::PublicData, + ) -> DKGResult<(GroupPublicKey, BTreeMap)> { + // Sum of the secret polynomial commitments of the other participants + let others_secret_polynomial_commitment = PolynomialCommitment::sum_polynomial_commitments( + &round2_public_data + .round1_public_messages + .values() + .map(|msg| &msg.secret_polynomial_commitment) + .collect::>(), + ); + + // The total secret polynomial commitment, which includes the secret polynomial commitment of the participant + let total_secret_polynomial_commitment = + PolynomialCommitment::sum_polynomial_commitments(&[ + &others_secret_polynomial_commitment, + &round1_public_data.secret_polynomial_commitment, + ]); + + // The group public key shares of all the participants, which correspond to the total secret shares commitments + let mut group_public_key_shares = BTreeMap::new(); + + for identifier in &round2_public_data.identifiers.others_identifiers { + let group_public_key_share = total_secret_polynomial_commitment.evaluate(&identifier.0); + + group_public_key_shares.insert(*identifier, group_public_key_share); + } + + let own_group_public_key_share = total_secret_polynomial_commitment + .evaluate(&round2_public_data.identifiers.own_identifier.0); + + group_public_key_shares.insert( + round2_public_data.identifiers.own_identifier, + own_group_public_key_share, + ); + + let shared_public_key = GroupPublicKey::from_point( + *total_secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap(), + ); + + // The shared public key corresponds to the secret commitment of the total secret polynomial commitment + if shared_public_key.as_point() + != &total_secret_polynomial_commitment.evaluate(&Scalar::ZERO) + { + return Err(DKGError::SharedPublicKeyMismatch); + } + + Ok((shared_public_key, group_public_key_shares)) + } +} + +#[cfg(test)] +mod tests { + use self::{ + round1::{PrivateData, PublicData}, + round2::Messages, + }; + use super::*; + use crate::{errors::DKGResult, SignatureError}; + #[cfg(feature = "alloc")] + use alloc::{ + collections::{BTreeMap, BTreeSet}, + vec::Vec, + }; + use merlin::Transcript; + use rand::{rngs::OsRng, Rng}; + use tests::round1::PublicMessage; + + const MAXIMUM_PARTICIPANTS: u16 = 10; + const MINIMUM_PARTICIPANTS: u16 = 3; + const MININUM_THRESHOLD: u16 = 2; + const PROTOCOL_RUNS: usize = 1; + + fn generate_parameters() -> Vec { + let mut rng = rand::thread_rng(); + let max_signers = rng.gen_range(MINIMUM_PARTICIPANTS..=MAXIMUM_PARTICIPANTS); + let min_signers = rng.gen_range(MININUM_THRESHOLD..=max_signers); + + (1..=max_signers) + .map(|_| Parameters::new(max_signers, min_signers)) + .collect() + } + + fn round1() -> ( + Vec, + Vec, + Vec, + Vec>, + ) { + let parameters_list = generate_parameters(); + + let mut all_public_messages_vec = Vec::new(); + let mut participants_round1_private_data = Vec::new(); + let mut participants_round1_public_data = Vec::new(); + + for i in 0..parameters_list.len() { + let (private_data, public_message, public_data) = + round1::run(parameters_list[i as usize].clone(), OsRng) + .expect("Round 1 should complete without errors!"); + + all_public_messages_vec.push(public_message.clone()); + participants_round1_public_data.push(public_data); + participants_round1_private_data.push(private_data); + } + + let mut received_round1_public_messages: Vec> = Vec::new(); + + let mut all_public_messages = BTreeSet::new(); + + for i in 0..parameters_list[0].participants { + all_public_messages.insert(all_public_messages_vec[i as usize].clone()); + } + + // Iterate through each participant to create a set of messages excluding their own. + for i in 0..parameters_list[0].participants { + let own_message = PublicMessage::new(&participants_round1_public_data[i as usize]); + + let mut messages_for_participant = BTreeSet::new(); + + for message in &all_public_messages { + if &own_message != message { + // Exclude the participant's own message. + messages_for_participant.insert(message.clone()); + } + } + + received_round1_public_messages.push(messages_for_participant); + } + + ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + received_round1_public_messages, + ) + } + + fn round2( + parameters_list: &Vec, + participants_round1_private_data: Vec, + participants_round1_public_data: &Vec, + participants_round1_public_messages: &Vec>, + ) -> DKGResult<( + Vec>, + Vec, + Vec, + Vec, + )> { + let mut participants_round2_public_data = Vec::new(); + let mut participants_round2_public_messages = Vec::new(); + let mut participants_set_of_participants = Vec::new(); + let mut identifiers_vec = Vec::new(); + + for i in 0..parameters_list[0].participants { + let result = round2::run( + participants_round1_private_data[i as usize].clone(), + &participants_round1_public_data[i as usize].clone(), + participants_round1_public_messages[i as usize].clone(), + Transcript::new(b"simplpedpop"), + )?; + + participants_round2_public_data.push(result.0.clone()); + participants_round2_public_messages.push(result.1); + participants_set_of_participants.push(result.0.identifiers.clone()); + identifiers_vec.push(result.0.identifiers.own_identifier); + } + + Ok(( + participants_round2_public_data, + participants_round2_public_messages, + participants_set_of_participants, + identifiers_vec, + )) + } + + fn round3( + participants_sets_of_participants: &Vec, + participants_round2_public_messages: &Vec, + participants_round2_public_data: &Vec>, + participants_round1_public_data: &Vec, + participants_round1_private_data: Vec, + participants_round2_private_messages: Vec>, + identifiers_vec: &Vec, + ) -> DKGResult< + Vec<( + GroupPublicKey, + BTreeMap, + round3::PrivateData, + )>, + > { + let mut participant_data_round3 = Vec::new(); + + for i in 0..participants_sets_of_participants.len() { + let received_round2_public_messages = participants_round2_public_messages + .iter() + .enumerate() + .filter(|(index, _msg)| { + identifiers_vec[*index] + != participants_sets_of_participants[i as usize].own_identifier + }) + .map(|(index, msg)| (identifiers_vec[index], msg.clone())) + .collect::>(); + + let mut round2_private_messages: Vec> = + Vec::new(); + + for participants in participants_sets_of_participants.iter() { + let mut messages_for_participant = BTreeMap::new(); + + for (i, round_messages) in participants_round2_private_messages.iter().enumerate() { + if let Some(message) = round_messages.get(&participants.own_identifier) { + messages_for_participant.insert(identifiers_vec[i], message.clone()); + } + } + + round2_private_messages.push(messages_for_participant); + } + + let result = round3::run( + &received_round2_public_messages, + &participants_round2_public_data[i as usize], + &participants_round1_public_data[i as usize], + participants_round1_private_data[i as usize].clone(), + &round2_private_messages[i as usize], + )?; + + participant_data_round3.push(result); + } + + Ok(participant_data_round3) + } + + #[test] + pub fn test_successful_simplpedpop() { + for _ in 0..PROTOCOL_RUNS { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let participants_data_round3 = round3( + &participants_sets_of_participants, + &participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(), + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(), + &identifiers_vec, + ) + .unwrap(); + + let shared_public_keys: Vec = participants_data_round3 + .iter() + .map(|state| state.0) + .collect(); + + assert!( + shared_public_keys.windows(2).all(|w| w[0] == w[1]), + "All participants must have the same group public key!" + ); + + for i in 0..parameters_list[0].participants { + assert_eq!( + participants_data_round3[i as usize] + .1 + .get(&participants_sets_of_participants[i as usize].own_identifier) + .unwrap() + .compress(), + (participants_data_round3[i as usize].2.total_secret_share.0 * GENERATOR) + .compress(), + "Verification of total secret shares failed!" + ); + } + } + } + + #[test] + fn test_incorrect_number_of_round1_public_messages_in_round2() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + mut participants_round1_public_messages, + ) = round1(); + + participants_round1_public_messages[0].pop_last(); + + let result = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::IncorrectNumberOfRound1PublicMessages { + expected: parameters_list[0].participants as usize - 1, + actual: parameters_list[0].participants as usize - 2, + }, + "Expected DKGError::IncorrectNumberOfRound1PublicMessages." + ), + } + } + + #[test] + fn test_invalid_secret_polynomial_commitment_in_round2() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + mut participants_round1_public_messages, + ) = round1(); + + let mut new_message = participants_round1_public_messages[0] + .first() + .unwrap() + .clone(); + + new_message + .secret_polynomial_commitment + .coefficients_commitments + .pop(); + + participants_round1_public_messages[0].pop_first(); + participants_round1_public_messages[0].insert(new_message); + + let result = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::InvalidSecretPolynomialCommitment { + expected: *parameters_list[0].threshold() as usize, + actual: *parameters_list[0].threshold() as usize - 1, + }, + "Expected DKGError::IncorrectNumberOfRound1PublicMessages." + ), + } + } + + #[test] + fn test_invalid_secret_share_error_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let mut participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + let enc_keys: Vec = participants_round1_public_messages[1] + .iter() + .map(|msg| { + *msg.secret_polynomial_commitment + .coefficients_commitments + .first() + .unwrap() + }) + .collect(); + + let secret_share = SecretShare(Scalar::random(&mut OsRng)); + + let identifiers: BTreeSet = participants_sets_of_participants[1] + .others_identifiers + .clone(); + + let index = identifiers + .iter() + .position(|x| x == &participants_sets_of_participants[0].own_identifier) + .unwrap(); + + let enc_share = secret_share.encrypt( + &participants_round1_private_data[1].secret_key.key, + &enc_keys[index], + participants_sets_of_participants[0] + .own_identifier + .0 + .as_bytes(), + ); + + let private_message = participants_round2_private_messages[1] + .get_mut(&participants_sets_of_participants[0].own_identifier) + .unwrap(); + + private_message.encrypted_secret_share = enc_share; + + let result = round3( + &participants_sets_of_participants, + &participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(), + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_private_messages, + &identifiers_vec, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::InvalidSecretShare(participants_sets_of_participants[1].own_identifier), + "Expected DKGError::InvalidSecretShare." + ), + } + } + + #[test] + fn test_decryption_error_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let mut participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + let private_message = participants_round2_private_messages[1] + .get_mut(&participants_sets_of_participants[0].own_identifier) + .unwrap(); + + private_message.encrypted_secret_share = EncryptedSecretShare(vec![1]); + + let result = round3( + &participants_sets_of_participants, + &participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(), + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_private_messages, + &identifiers_vec, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::DecryptionError(chacha20poly1305::Error), + "Expected DKGError::DecryptionError." + ), + } + } + + #[test] + fn test_invalid_proof_of_possession_in_round2() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + mut participants_round1_public_messages, + ) = round1(); + + let sk = SecretKey::generate(); + let proof_of_possession = sk.sign( + Transcript::new(b"invalid proof of possession"), + &PublicKey::from(sk.clone()), + ); + let msg = PublicMessage { + secret_polynomial_commitment: PolynomialCommitment::commit(&Polynomial::generate( + &mut OsRng, + parameters_list[0].threshold - 1, + )), + proof_of_possession, + }; + participants_round1_public_messages[0].pop_last(); + participants_round1_public_messages[0].insert(msg); + + let result = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::InvalidProofOfPossession(SignatureError::EquationFalse), + "Expected DKGError::InvalidProofOfPossession." + ), + } + } + + #[test] + pub fn test_invalid_certificate_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + mut participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + participants_round2_public_data[0].transcript = Transcript::new(b"invalid transcript"); + + let participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + let result = round3( + &participants_sets_of_participants, + &participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(), + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_private_messages, + &identifiers_vec, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::InvalidCertificate(SignatureError::EquationFalse), + "Expected DKGError::InvalidCertificate." + ), + } + } + + #[test] + pub fn test_incorrect_number_of_round2_public_messages_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let mut participants_round2_public_messages: Vec = + participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(); + + participants_round2_public_messages.pop(); + + let participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + let result = round3( + &participants_sets_of_participants, + &participants_round2_public_messages, + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_private_messages, + &identifiers_vec, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::IncorrectNumberOfRound2PublicMessages { + expected: *parameters_list[0].participants() as usize - 1, + actual: *parameters_list[0].participants() as usize - 2 + }, + "Expected DKGError::IncorrectNumberOfRound2PublicMessages." + ), + } + } + + #[test] + pub fn test_incorrect_number_of_round1_public_messages_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + mut participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let participants_round2_public_messages: Vec = + participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(); + + participants_round2_public_data[0] + .round1_public_messages + .pop_first(); + + let participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + let result = round3( + &participants_sets_of_participants, + &participants_round2_public_messages, + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_private_messages, + &identifiers_vec, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::IncorrectNumberOfRound1PublicMessages { + expected: *parameters_list[0].participants() as usize - 1, + actual: *parameters_list[0].participants() as usize - 2 + }, + "Expected DKGError::IncorrectNumberOfRound1PublicMessages." + ), + } + } + + #[test] + pub fn test_incorrect_number_of_round2_private_messages_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let participants_round2_public_messages: Vec = + participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(); + + let mut participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + participants_round2_private_messages[1].pop_last(); + + let result = round3( + &participants_sets_of_participants, + &participants_round2_public_messages, + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_private_messages, + &identifiers_vec, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::IncorrectNumberOfRound2PrivateMessages { + expected: *parameters_list[0].participants() as usize - 1, + actual: *parameters_list[0].participants() as usize - 2 + }, + "Expected DKGError::IncorrectNumberOfRound2PrivateMessages." + ), + } + } + + #[test] + pub fn test_unknown_identifier_from_round2_public_messages_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + mut identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let participants_round2_public_messages: Vec = + participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(); + + identifiers_vec.pop(); + let unknown_identifier = Identifier(Scalar::random(&mut OsRng)); + identifiers_vec.push(unknown_identifier); + + let participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + let result = round3( + &participants_sets_of_participants, + &participants_round2_public_messages, + &participants_round2_public_data, + &participants_round1_public_data, + participants_round1_private_data, + participants_round2_private_messages, + &identifiers_vec, + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::UnknownIdentifierRound2PublicMessages(unknown_identifier), + "Expected DKGError::UnknownIdentifierRound2PublicMessages." + ), + } + } + + #[test] + fn test_unknown_identifier_from_round2_private_messages_in_round3() { + let ( + parameters_list, + participants_round1_private_data, + participants_round1_public_data, + participants_round1_public_messages, + ) = round1(); + + let ( + participants_round2_public_data, + participants_round2_messages, + participants_sets_of_participants, + identifiers_vec, + ) = round2( + ¶meters_list, + participants_round1_private_data.clone(), + &participants_round1_public_data, + &participants_round1_public_messages, + ) + .unwrap(); + + let participants_round2_public_messages: Vec = + participants_round2_messages + .iter() + .map(|msg| msg.public_message().clone()) + .collect(); + + let mut identifiers_vec2 = identifiers_vec.clone(); + identifiers_vec2.pop(); + let unknown_identifier = Identifier(Scalar::random(&mut OsRng)); + identifiers_vec2.push(unknown_identifier); + + let participants_round2_private_messages: Vec< + BTreeMap, + > = participants_round2_messages + .iter() + .map(|msg| msg.private_messages().clone()) + .collect(); + + let received_round2_public_messages = participants_round2_public_messages + .iter() + .enumerate() + .filter(|(index, _msg)| { + identifiers_vec[*index] != participants_sets_of_participants[0].own_identifier + }) + .map(|(index, msg)| (identifiers_vec[index], msg.clone())) + .collect::>(); + + let mut round2_private_messages: Vec> = + Vec::new(); + + for participants in participants_sets_of_participants.iter() { + let mut messages_for_participant = BTreeMap::new(); + + for (i, round_messages) in participants_round2_private_messages.iter().enumerate() { + if let Some(message) = round_messages.get(&participants.own_identifier) { + messages_for_participant.insert(identifiers_vec2[i], message.clone()); + } + } + + round2_private_messages.push(messages_for_participant); + } + + let result = round3::run( + &received_round2_public_messages, + &participants_round2_public_data[0], + &participants_round1_public_data[0], + participants_round1_private_data[0].clone(), + &round2_private_messages[0], + ); + + match result { + Ok(_) => panic!("Expected an error, but got Ok."), + Err(e) => assert_eq!( + e, + DKGError::UnknownIdentifierRound2PrivateMessages(unknown_identifier), + "Expected DKGError::UnknownIdentifierRound2PrivateMessages." + ), + } + } + + #[test] + fn test_invalid_threshold() { + let parameters = Parameters::new(3, 1); + assert_eq!(parameters.validate(), Err(DKGError::InsufficientThreshold)); + } + + #[test] + fn test_invalid_participants() { + let parameters = Parameters::new(1, 2); + assert_eq!( + parameters.validate(), + Err(DKGError::InvalidNumberOfParticipants) + ); + } + + #[test] + fn test_threshold_greater_than_participants() { + let parameters = Parameters::new(2, 3); + assert_eq!(parameters.validate(), Err(DKGError::ExcessiveThreshold)); + } + + #[test] + fn test_encryption_decryption() { + let mut rng = OsRng; + let deckey = Scalar::random(&mut rng); + let enckey = RistrettoPoint::random(&mut rng); + let context = b"context"; + + let original_share = SecretShare(Scalar::random(&mut rng)); + + let encrypted_share = original_share.encrypt(&deckey, &enckey, context); + let decrypted_share = encrypted_share.decrypt(&deckey, &enckey, context); + + assert_eq!( + original_share.0, + decrypted_share.unwrap().0, + "Decryption must return the original share!" + ); + } +} From b152bab2b94a930b013c92d1f4c0540e0d131c2a Mon Sep 17 00:00:00 2001 From: Fiono11 Date: Mon, 29 Apr 2024 12:06:15 +0100 Subject: [PATCH 2/8] Remove unused files --- src/identifier.rs | 62 -- src/polynomial.rs | 212 ----- src/simplpedpop.rs | 1883 -------------------------------------------- 3 files changed, 2157 deletions(-) delete mode 100644 src/identifier.rs delete mode 100644 src/polynomial.rs delete mode 100644 src/simplpedpop.rs diff --git a/src/identifier.rs b/src/identifier.rs deleted file mode 100644 index ed1ed78..0000000 --- a/src/identifier.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! The identifier of a participant in a multiparty protocol. - -use core::cmp::Ordering; - -use crate::errors::DKGError; -use curve25519_dalek::Scalar; - -/// The identifier is represented by a scalar. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct Identifier(pub(crate) Scalar); - -impl PartialOrd for Identifier { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Identifier { - fn cmp(&self, other: &Self) -> Ordering { - let serialized_self = self.0.as_bytes(); - let serialized_other = other.0.as_bytes(); - - // The default cmp uses lexicographic order; so we need the elements in big endian - serialized_self - .as_ref() - .iter() - .rev() - .cmp(serialized_other.as_ref().iter().rev()) - } -} - -impl TryFrom for Identifier { - type Error = DKGError; - - fn try_from(n: u16) -> Result { - if n == 0 { - Err(DKGError::InvalidIdentifier) - } else { - // Classic left-to-right double-and-add algorithm that skips the first bit 1 (since - // identifiers are never zero, there is always a bit 1), thus `sum` starts with 1 too. - let one = Scalar::ONE; - let mut sum = Scalar::ONE; - - let bits = (n.to_be_bytes().len() as u32) * 8; - for i in (0..(bits - n.leading_zeros() - 1)).rev() { - sum = sum + sum; - if n & (1 << i) != 0 { - sum += one; - } - } - Ok(Self(sum)) - } - } -} - -impl TryFrom for Identifier { - type Error = DKGError; - - fn try_from(value: Scalar) -> Result { - Ok(Self(value)) - } -} diff --git a/src/polynomial.rs b/src/polynomial.rs deleted file mode 100644 index a21150e..0000000 --- a/src/polynomial.rs +++ /dev/null @@ -1,212 +0,0 @@ -//! Implementation of a polynomial and related operations. - -use alloc::vec; -use alloc::vec::Vec; -use curve25519_dalek::{traits::Identity, RistrettoPoint, Scalar}; -use rand_core::{CryptoRng, RngCore}; -use zeroize::ZeroizeOnDrop; - -use crate::simplpedpop::GENERATOR; - -pub(crate) type Coefficient = Scalar; -pub(crate) type Value = Scalar; -pub(crate) type ValueCommitment = RistrettoPoint; -pub(crate) type CoefficientCommitment = RistrettoPoint; - -/// A polynomial. -#[derive(Debug, Clone, ZeroizeOnDrop)] -pub struct Polynomial { - pub(crate) coefficients: Vec, -} - -impl Polynomial { - pub(crate) fn generate(rng: &mut R, degree: u16) -> Self { - let mut coefficients = Vec::new(); - - for _ in 0..(degree as usize + 1) { - coefficients.push(Scalar::random(rng)); - } - - Self { coefficients } - } - - pub(crate) fn evaluate(&self, x: &Value) -> Value { - let mut value = *self - .coefficients - .last() - .expect("coefficients must have at least one element"); - - // Process all coefficients except the last one, using Horner's method - for coeff in self.coefficients.iter().rev().skip(1) { - value = value * x + coeff; - } - - value - } -} - -/// A polynomial commitment. -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct PolynomialCommitment { - pub(crate) coefficients_commitments: Vec, -} - -impl PolynomialCommitment { - pub(crate) fn commit(polynomial: &Polynomial) -> Self { - let coefficients_commitments = polynomial - .coefficients - .iter() - .map(|coefficient| GENERATOR * coefficient) - .collect(); - - Self { - coefficients_commitments, - } - } - - pub(crate) fn evaluate(&self, identifier: &Value) -> ValueCommitment { - let i = identifier; - - let (_, result) = self.coefficients_commitments.iter().fold( - (Scalar::ONE, RistrettoPoint::identity()), - |(i_to_the_k, sum_so_far), comm_k| (i * i_to_the_k, sum_so_far + comm_k * i_to_the_k), - ); - result - } - - pub(crate) fn sum_polynomial_commitments( - polynomials_commitments: &[&PolynomialCommitment], - ) -> PolynomialCommitment { - let max_length = polynomials_commitments - .iter() - .map(|c| c.coefficients_commitments.len()) - .max() - .unwrap_or(0); - - let mut total_commitment = vec![RistrettoPoint::identity(); max_length]; - - for polynomial_commitment in polynomials_commitments { - for (i, coeff_commitment) in polynomial_commitment - .coefficients_commitments - .iter() - .enumerate() - { - total_commitment[i] += coeff_commitment; - } - } - - PolynomialCommitment { - coefficients_commitments: total_commitment, - } - } -} - -#[cfg(test)] -mod tests { - use crate::{ - polynomial::{Coefficient, Polynomial, PolynomialCommitment}, - simplpedpop::GENERATOR, - }; - - use alloc::vec::Vec; - use curve25519_dalek::Scalar; - use rand::rngs::OsRng; - - #[test] - fn test_generate_polynomial_commitment_valid() { - let degree = 3; - - let polynomial = Polynomial::generate(&mut OsRng, degree); - - let polynomial_commitment = PolynomialCommitment::commit(&polynomial); - - assert_eq!(polynomial.coefficients.len(), degree as usize + 1); - - assert_eq!( - polynomial_commitment.coefficients_commitments.len(), - degree as usize + 1 - ); - } - - #[test] - fn test_evaluate_polynomial() { - let coefficients: Vec = - vec![Scalar::from(3u64), Scalar::from(2u64), Scalar::from(1u64)]; // Polynomial x^2 + 2x + 3 - - let polynomial = Polynomial { coefficients }; - - let value = Scalar::from(5u64); // x = 5 - - let result = polynomial.evaluate(&value); - - assert_eq!(result, Scalar::from(38u64)); // 5^2 + 2*5 + 3 - } - - #[test] - fn test_sum_secret_polynomial_commitments() { - let polynomial_commitment1 = PolynomialCommitment { - coefficients_commitments: vec![ - GENERATOR * Scalar::from(1u64), // Constant - GENERATOR * Scalar::from(2u64), // Linear - GENERATOR * Scalar::from(3u64), // Quadratic - ], - }; - - let polynomial_commitment2 = PolynomialCommitment { - coefficients_commitments: vec![ - GENERATOR * Scalar::from(4u64), // Constant - GENERATOR * Scalar::from(5u64), // Linear - GENERATOR * Scalar::from(6u64), // Quadratic - ], - }; - - let summed_polynomial_commitments = PolynomialCommitment::sum_polynomial_commitments(&[ - &polynomial_commitment1, - &polynomial_commitment2, - ]); - - let expected_coefficients_commitments = vec![ - GENERATOR * Scalar::from(5u64), // 1 + 4 = 5 - GENERATOR * Scalar::from(7u64), // 2 + 5 = 7 - GENERATOR * Scalar::from(9u64), // 3 + 6 = 9 - ]; - - assert_eq!( - summed_polynomial_commitments.coefficients_commitments, - expected_coefficients_commitments, - "Coefficient commitments do not match" - ); - } - - #[test] - fn test_evaluate_polynomial_commitment() { - // f(x) = 3 + 2x + x^2 - let constant_coefficient_commitment = Scalar::from(3u64) * GENERATOR; - let linear_commitment = Scalar::from(2u64) * GENERATOR; - let quadratic_commitment = Scalar::from(1u64) * GENERATOR; - - // Note the order and inclusion of the constant term - let coefficients_commitments = vec![ - constant_coefficient_commitment, - linear_commitment, - quadratic_commitment, - ]; - - let polynomial_commitment = PolynomialCommitment { - coefficients_commitments, - }; - - let value = Scalar::from(2u64); - - // f(2) = 11 - let expected = Scalar::from(11u64) * GENERATOR; - - let result = polynomial_commitment.evaluate(&value); - - assert_eq!( - result, expected, - "The evaluated commitment does not match the expected result" - ); - } -} diff --git a/src/simplpedpop.rs b/src/simplpedpop.rs deleted file mode 100644 index f48ed9a..0000000 --- a/src/simplpedpop.rs +++ /dev/null @@ -1,1883 +0,0 @@ -//! Implementation of a modified version of SimplPedPoP (), a DKG based on PedPoP, which in turn is based -//! on Pedersen's DKG. All of them have as the fundamental building block the Shamir's Secret Sharing scheme. -//! -//! The modification consists of each participant sending the secret shares of the other participants only in round 2 -//! instead of in round 1. The reason for this is we use the secret commitments (the evaluations of the secret polynomial -//! commitments at zero) of round 1 to assign the identifiers of all the participants in round 2, which will then be -//! used to compute the corresponding secret shares. Finally, we encrypt and authenticate the secret shares with -//! Chacha20Poly1305, meaning they can be distributed to the participants by an untrusted coordinator instead of sending -//! them directly. -//! -//! The protocol is divided into three rounds. In each round some data and some messages are produced and some messages -//! are verified (if received from a previous round). Data is divided into public and private because in a given round we -//! want to pass a reference to the public data (performance reasons) and the private data itself so that it is zeroized -//! after getting out of scope (security reasons). Public messages are destined to all the other participants, while private -//! messages are destined to a single participant. - -use super::{ - errors::{DKGError, DKGResult}, - identifier::Identifier, - polynomial::{Coefficient, CoefficientCommitment, Polynomial, PolynomialCommitment}, -}; -use crate::{aead::make_aead, context::SigningTranscript, PublicKey, SecretKey, Signature}; -use alloc::{collections::BTreeSet, vec::Vec}; -use chacha20poly1305::{aead::Aead, ChaCha20Poly1305, Nonce}; -use curve25519_dalek::{constants::RISTRETTO_BASEPOINT_POINT, RistrettoPoint, Scalar}; -use derive_getters::Getters; -use merlin::Transcript; -use rand_core::{CryptoRng, RngCore}; -use zeroize::ZeroizeOnDrop; - -pub(crate) const GENERATOR: RistrettoPoint = RISTRETTO_BASEPOINT_POINT; - -/// The group public key generated by the SimplPedPoP protocol. -pub type GroupPublicKey = PublicKey; -/// The group public key share of each participant in the SimplPedPoP protocol. -pub type GroupPublicKeyShare = CoefficientCommitment; -pub(crate) type SecretPolynomialCommitment = PolynomialCommitment; -pub(crate) type SecretPolynomial = Polynomial; -pub(crate) type TotalSecretShare = SecretShare; -pub(crate) type SecretCommitment = CoefficientCommitment; -pub(crate) type Certificate = Signature; -pub(crate) type ProofOfPossession = Signature; -pub(crate) type Secret = Coefficient; - -/// The parameters of a given execution of the SimplPedPoP protocol. -#[derive(Debug, Clone, Getters)] -pub struct Parameters { - pub(crate) participants: u16, - pub(crate) threshold: u16, -} - -impl Parameters { - /// Create new parameters. - pub fn new(participants: u16, threshold: u16) -> Parameters { - Parameters { - participants, - threshold, - } - } - - pub(crate) fn validate(&self) -> Result<(), DKGError> { - if self.threshold < 2 { - return Err(DKGError::InsufficientThreshold); - } - - if self.participants < 2 { - return Err(DKGError::InvalidNumberOfParticipants); - } - - if self.threshold > self.participants { - return Err(DKGError::ExcessiveThreshold); - } - - Ok(()) - } -} - -/// The participants of a given execution of the SimplPedPoP protocol. -#[derive(Debug, Clone, Getters)] -pub struct Identifiers { - pub(crate) own_identifier: Identifier, - pub(crate) others_identifiers: BTreeSet, -} - -impl Identifiers { - /// Create new participants. - pub fn new( - own_identifier: Identifier, - others_identifiers: BTreeSet, - ) -> Identifiers { - Identifiers { - own_identifier, - others_identifiers, - } - } - - pub(crate) fn validate(&self, participants: u16) -> Result<(), DKGError> { - if self.own_identifier.0 == Scalar::ZERO { - return Err(DKGError::InvalidIdentifier); - } - - for other_identifier in &self.others_identifiers { - if other_identifier.0 == Scalar::ZERO { - return Err(DKGError::InvalidIdentifier); - } - } - - if self.others_identifiers.len() != participants as usize - 1 { - return Err(DKGError::IncorrectNumberOfIdentifiers { - expected: participants as usize, - actual: self.others_identifiers.len() + 1, - }); - } - - Ok(()) - } -} - -fn derive_secret_key_from_secret(secret: &Secret, mut rng: R) -> SecretKey { - let mut bytes = [0u8; 64]; - let mut nonce: [u8; 32] = [0u8; 32]; - - rng.fill_bytes(&mut nonce); - let secret_bytes = secret.to_bytes(); - - bytes[..32].copy_from_slice(&secret_bytes[..]); - bytes[32..].copy_from_slice(&nonce[..]); - - SecretKey::from_bytes(&bytes[..]).unwrap() // This never fails because bytes has length 64 and the key is a scalar -} - -/// A secret share, which corresponds to an evaluation of a value that identifies a participant in a secret polynomial. -#[derive(Debug, Clone, ZeroizeOnDrop)] -pub struct SecretShare(pub(crate) Scalar); - -impl SecretShare { - pub(crate) fn encrypt( - &self, - decryption_key: &Scalar, - encryption_key: &RistrettoPoint, - context: &[u8], - ) -> EncryptedSecretShare { - let shared_secret = decryption_key * encryption_key; - - let mut transcript = Transcript::new(b"encryption"); - transcript.commit_point(b"shared secret", &shared_secret.compress()); - transcript.append_message(b"context", context); - - let mut bytes = [0; 12]; - transcript.challenge_bytes(b"nonce", &mut bytes); - - let cipher: ChaCha20Poly1305 = make_aead::(transcript); - let nonce = Nonce::from_slice(&bytes[..]); - - let ciphertext: Vec = cipher.encrypt(nonce, &self.0.as_bytes()[..]).unwrap(); - - EncryptedSecretShare(ciphertext) - } -} - -/// An encrypted secret share, which can be sent directly to the intended participant or through an untrusted coordinator. -#[derive(Debug, Clone)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct EncryptedSecretShare(pub(crate) Vec); - -impl EncryptedSecretShare { - pub(crate) fn decrypt( - &self, - decryption_key: &Scalar, - encryption_key: &RistrettoPoint, - context: &[u8], - ) -> DKGResult { - let shared_secret = decryption_key * encryption_key; - - let mut transcript = Transcript::new(b"encryption"); - transcript.commit_point(b"shared secret", &shared_secret.compress()); - transcript.append_message(b"context", context); - - let mut bytes = [0; 12]; - transcript.challenge_bytes(b"nonce", &mut bytes); - - let cipher: ChaCha20Poly1305 = make_aead::(transcript); - let nonce = Nonce::from_slice(&bytes[..]); - - let plaintext = cipher - .decrypt(nonce, &self.0[..]) - .map_err(DKGError::DecryptionError)?; - - let mut bytes = [0; 32]; - bytes.copy_from_slice(&plaintext); - - Ok(SecretShare(Scalar::from_bytes_mod_order(bytes))) - } -} - -/// SimplPedPoP round 1. -/// -/// The participant commits to a secret polynomial f(x) of degree t-1, where t is the threshold of the DKG, by commiting -/// to each one of the t coefficients of the secret polynomial. -/// -/// It derives a secret key from the secret of the polynomial f(0) and uses it to generate a Proof of Possession of that -/// secret by signing a message with the secret key. -pub mod round1 { - use super::{ - derive_secret_key_from_secret, Parameters, ProofOfPossession, SecretPolynomial, - SecretPolynomialCommitment, - }; - use crate::{ - errors::DKGResult, - polynomial::{Polynomial, PolynomialCommitment}, - PublicKey, SecretKey, - }; - use core::cmp::Ordering; - use curve25519_dalek::Scalar; - use merlin::Transcript; - use rand_core::{CryptoRng, RngCore}; - - /// The private data generated by the participant in round 1. - #[derive(Debug, Clone)] - pub struct PrivateData { - pub(crate) secret_key: SecretKey, - pub(crate) secret_polynomial: SecretPolynomial, - } - - /// The public data generated by the participant in round 1. - #[derive(Debug, Clone)] - pub struct PublicData { - pub(crate) parameters: Parameters, - pub(crate) secret_polynomial_commitment: SecretPolynomialCommitment, - pub(crate) proof_of_possession: ProofOfPossession, - } - - /// Public message to be sent by the participant to all the other participants or to the coordinator in round 1. - #[derive(Debug, Clone, PartialEq, Eq)] - #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] - pub struct PublicMessage { - pub(crate) secret_polynomial_commitment: SecretPolynomialCommitment, - pub(crate) proof_of_possession: ProofOfPossession, - } - - impl PublicMessage { - /// Creates a new public message. - pub fn new(public_data: &PublicData) -> PublicMessage { - PublicMessage { - secret_polynomial_commitment: public_data.secret_polynomial_commitment.clone(), - proof_of_possession: public_data.proof_of_possession, - } - } - } - - impl PartialOrd for PublicMessage { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } - } - - impl Ord for PublicMessage { - fn cmp(&self, other: &Self) -> Ordering { - self.secret_polynomial_commitment - .coefficients_commitments - .first() - .unwrap() - .compress() - .0 - .cmp( - &other - .secret_polynomial_commitment - .coefficients_commitments - .first() - .unwrap() - .compress() - .0, - ) - } - } - - /// Runs the round 1 of the SimplPedPoP protocol. - pub fn run( - parameters: Parameters, - mut rng: R, - ) -> DKGResult<(PrivateData, PublicMessage, PublicData)> { - parameters.validate()?; - - let (private_data, public_data) = generate_data(parameters, &mut rng); - - let public_message = PublicMessage::new(&public_data); - - Ok((private_data, public_message, public_data)) - } - - fn generate_data( - parameters: Parameters, - mut rng: R, - ) -> (PrivateData, PublicData) { - let secret_polynomial = loop { - let temp_polynomial = Polynomial::generate(&mut rng, *parameters.threshold() - 1); - // There must be a secret, which is the constant coefficient of the secret polynomial - if temp_polynomial.coefficients.first().unwrap() != &Scalar::ZERO { - break temp_polynomial; - } - }; - - let secret_polynomial_commitment = PolynomialCommitment::commit(&secret_polynomial); - - // This secret key will be used to sign the proof of possession and the certificate - let secret_key = - derive_secret_key_from_secret(secret_polynomial.coefficients.first().unwrap(), rng); - - let public_key = PublicKey::from_point( - *secret_polynomial_commitment - .coefficients_commitments - .first() - .unwrap(), - ); - - let proof_of_possession = - secret_key.sign(Transcript::new(b"Proof of Possession"), &public_key); - - ( - PrivateData { - secret_key, - secret_polynomial, - }, - PublicData { - parameters, - secret_polynomial_commitment, - proof_of_possession, - }, - ) - } -} - -/// SimplPedPoP round 2. -/// -/// The participant verifies the received messages of the other participants from round 1, the secret polynomial commitments -/// and the Proofs of Possession. -/// -/// It orders the secret commitments and uses that ordering to assing random identifiers to all the participants in the -/// protocol, including its own. -/// -/// It computes the secret shares of each participant based on their identifiers, encrypts and authenticates them using -/// Chacha20Poly1305 with a shared secret. -/// -/// It signs a transcript of the protocol execution (certificate) with its secret key, which contains the PoPs and the -/// polynomial commitments from all the participants (including its own). -pub mod round2 { - use super::{ - round1, Certificate, EncryptedSecretShare, Identifier, Identifiers, Parameters, - SecretCommitment, SecretPolynomial, SecretShare, - }; - use crate::{ - context::SigningTranscript, - errors::{DKGError, DKGResult}, - verify_batch, PublicKey, SecretKey, - }; - use alloc::{ - collections::{BTreeMap, BTreeSet}, - string::ToString, - vec, - vec::Vec, - }; - use curve25519_dalek::{RistrettoPoint, Scalar}; - use derive_getters::Getters; - use merlin::Transcript; - use sha2::{digest::Update, Digest, Sha512}; - - /// The public data of round 2. - #[derive(Debug, Clone, Getters)] - pub struct PublicData { - pub(crate) identifiers: Identifiers, - pub(crate) round1_public_messages: BTreeMap, - pub(crate) transcript: T, - pub(crate) public_keys: Vec, - } - - /// The public message of round 2. - #[derive(Debug, Clone)] - #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] - pub struct PublicMessage { - pub(crate) certificate: Certificate, - } - - /// Private message to sent by a participant to another participant or to the coordinator in encrypted form in round 1. - #[derive(Debug, Clone)] - #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] - pub struct PrivateMessage { - pub(crate) encrypted_secret_share: EncryptedSecretShare, - } - - impl PrivateMessage { - /// Creates a new private message. - pub fn new( - secret_share: SecretShare, - deckey: Scalar, - enckey: RistrettoPoint, - context: &[u8], - ) -> PrivateMessage { - let encrypted_secret_share = secret_share.encrypt(&deckey, &enckey, context); - - PrivateMessage { - encrypted_secret_share, - } - } - } - - /// The messages to be sent by the participant in round 2. - #[derive(Debug, Clone, Getters)] - pub struct Messages { - // The identifier is the intended recipient of the private message. - private_messages: BTreeMap, - public_message: PublicMessage, - } - - fn validate_messages( - parameters: &Parameters, - round1_public_messages: &BTreeSet, - ) -> DKGResult<()> { - if round1_public_messages.len() != *parameters.participants() as usize - 1 { - return Err(DKGError::IncorrectNumberOfRound1PublicMessages { - expected: *parameters.participants() as usize - 1, - actual: round1_public_messages.len(), - }); - } - - for round1_public_message in round1_public_messages { - if round1_public_message - .secret_polynomial_commitment - .coefficients_commitments - .len() - != *parameters.threshold() as usize - { - return Err(DKGError::InvalidSecretPolynomialCommitment { - expected: *parameters.threshold() as usize, - actual: round1_public_message - .secret_polynomial_commitment - .coefficients_commitments - .len(), - }); - } - } - - Ok(()) - } - - /// Runs the round 2 of a SimplPedPoP protocol. - pub fn run( - round1_private_data: round1::PrivateData, - round1_public_data: &round1::PublicData, - round1_public_messages: BTreeSet, - transcript: T, - ) -> DKGResult<(PublicData, Messages)> { - round1_public_data.parameters.validate()?; - - validate_messages(&round1_public_data.parameters, &round1_public_messages)?; - - let public_keys = verify_round1_messages(&round1_public_messages)?; - - let public_data = generate_public_data( - round1_public_messages, - round1_public_data, - transcript, - public_keys, - ); - - let secret_commitment = round1_public_data - .secret_polynomial_commitment - .coefficients_commitments - .first() - .unwrap(); - - let messages = generate_messages( - &public_data, - &round1_private_data.secret_polynomial, - round1_private_data.secret_key, - secret_commitment, - ); - - Ok((public_data, messages)) - } - - fn generate_public_data( - round1_public_messages: BTreeSet, - round1_public_data: &round1::PublicData, - mut transcript: T, - public_keys: Vec, - ) -> PublicData { - let mut own_inserted = false; - - let own_first_coefficient_compressed = round1_public_data - .secret_polynomial_commitment - .coefficients_commitments - .first() - .unwrap() - .compress(); - - // Writes the data of all the participants in the transcript ordered by their identifiers - for message in &round1_public_messages { - let message_first_coefficient_compressed = message - .secret_polynomial_commitment - .coefficients_commitments - .first() - .unwrap() - .compress(); - - if own_first_coefficient_compressed.0 < message_first_coefficient_compressed.0 - && !own_inserted - { - // Writes own data in the transcript - transcript.commit_point(b"SecretCommitment", &own_first_coefficient_compressed); - - for coefficient_commitment in &round1_public_data - .secret_polynomial_commitment - .coefficients_commitments - { - transcript - .commit_point(b"CoefficientCommitment", &coefficient_commitment.compress()); - } - - transcript.commit_point( - b"ProofOfPossessionR", - &round1_public_data.proof_of_possession.R, - ); - - own_inserted = true; - } - // Writes the data of the other participants in the transcript - transcript.commit_point(b"SecretCommitment", &message_first_coefficient_compressed); - - for coefficient_commitment in &message - .secret_polynomial_commitment - .coefficients_commitments - { - transcript - .commit_point(b"CoefficientCommitment", &coefficient_commitment.compress()); - } - - transcript.commit_point(b"ProofOfPossessionR", &message.proof_of_possession.R); - } - - // Writes own data in the transcript if own identifier is the last one - if !own_inserted { - transcript.commit_point(b"SecretCommitment", &own_first_coefficient_compressed); - - for coefficient_commitment in &round1_public_data - .secret_polynomial_commitment - .coefficients_commitments - { - transcript - .commit_point(b"CoefficientCommitment", &coefficient_commitment.compress()); - } - - transcript.commit_point( - b"ProofOfPossessionR", - &round1_public_data.proof_of_possession.R, - ); - } - - // Scalar generated from transcript used to generate random identifiers to the participants - let scalar = transcript.challenge_scalar(b"participants"); - - let (identifiers, round1_public_messages) = - generate_identifiers(round1_public_data, round1_public_messages, &scalar); - - PublicData { - identifiers, - round1_public_messages, - transcript, - public_keys, - } - } - - fn generate_identifiers( - round1_public_data: &round1::PublicData, - round1_public_messages_set: BTreeSet, - scalar: &Scalar, - ) -> (Identifiers, BTreeMap) { - let mut others_identifiers: BTreeSet = BTreeSet::new(); - let mut round1_public_messages: BTreeMap = - BTreeMap::new(); - - let mut secret_commitments: BTreeSet<[u8; 32]> = round1_public_messages_set - .iter() - .map(|msg| { - msg.secret_polynomial_commitment - .coefficients_commitments - .first() - .unwrap() - .compress() - .0 - }) - .collect(); - - let own_secret_commitment = round1_public_data - .secret_polynomial_commitment - .coefficients_commitments - .first() - .unwrap(); - - secret_commitments.insert(own_secret_commitment.compress().0); - - let mut index = 0; - for message in &secret_commitments { - if message == &own_secret_commitment.compress().0 { - break; - } - index += 1; - } - - for i in 0..secret_commitments.len() { - let input = Sha512::new().chain(scalar.as_bytes()).chain(i.to_string()); - let random_scalar = Scalar::from_hash(input); - others_identifiers.insert(Identifier(random_scalar)); - } - - let own_identifier = *others_identifiers.iter().nth(index).unwrap(); - others_identifiers.remove(&own_identifier); - - for (id, message) in others_identifiers.iter().zip(round1_public_messages_set) { - round1_public_messages.insert(*id, message); - } - - let identifiers = Identifiers::new(own_identifier, others_identifiers); - - (identifiers, round1_public_messages) - } - - fn verify_round1_messages( - round1_public_messages: &BTreeSet, - ) -> DKGResult> { - let len = round1_public_messages.len(); - let mut public_keys = Vec::with_capacity(len); - let mut proofs_of_possession = Vec::with_capacity(len); - - // The public keys are the secret commitments of the participants - for round1_public_message in round1_public_messages { - let public_key = PublicKey::from_point( - *round1_public_message - .secret_polynomial_commitment - .coefficients_commitments - .first() - .unwrap(), - ); - public_keys.push(public_key); - proofs_of_possession.push(round1_public_message.proof_of_possession); - } - - verify_batch( - vec![Transcript::new(b"Proof of Possession"); len], - &proofs_of_possession[..], - &public_keys[..], - false, - ) - .map_err(DKGError::InvalidProofOfPossession)?; - - Ok(public_keys) - } - - fn generate_messages( - round2_public_data: &PublicData, - secret_polynomial: &SecretPolynomial, - secret_key: SecretKey, - secret_commitment: &SecretCommitment, - ) -> Messages { - let mut private_messages = BTreeMap::new(); - let enc_keys: Vec = round2_public_data - .round1_public_messages - .values() - .map(|msg| { - *msg.secret_polynomial_commitment - .coefficients_commitments - .first() - .unwrap() - }) - .collect(); - - for (i, identifier) in round2_public_data - .identifiers - .others_identifiers - .iter() - .enumerate() - { - let secret_share = secret_polynomial.evaluate(&identifier.0); - private_messages.insert( - *identifier, - PrivateMessage::new( - SecretShare(secret_share), - secret_key.key, - enc_keys[i], - identifier.0.as_bytes(), - ), - ); - } - - let public_key = PublicKey::from_point(*secret_commitment); - - let certificate = secret_key.sign(round2_public_data.transcript.clone(), &public_key); - - let public_message = PublicMessage { certificate }; - - Messages { - private_messages, - public_message, - } - } -} - -/// SimplPedPoP round 3. -/// -/// The participant decrypts and verifies the secret shares received from the other participants from round 2, computes -/// its own secret share and its own total secret share, which corresponds to its share of the group public key. -/// -/// It verifies the certificates from all the other participants and generates the shared public -/// key and the total secret shares commitments of the other partipants. -pub mod round3 { - use super::{ - round1, round2, Certificate, GroupPublicKey, GroupPublicKeyShare, Identifier, Identifiers, - Parameters, SecretPolynomial, SecretShare, TotalSecretShare, GENERATOR, - }; - use crate::{ - context::SigningTranscript, - errors::{DKGError, DKGResult}, - polynomial::PolynomialCommitment, - verify_batch, - }; - use alloc::{collections::BTreeMap, vec, vec::Vec}; - use curve25519_dalek::Scalar; - use derive_getters::Getters; - use zeroize::ZeroizeOnDrop; - - /// The private data of round 3. - #[derive(Debug, Clone, ZeroizeOnDrop, Getters)] - pub struct PrivateData { - pub(crate) total_secret_share: TotalSecretShare, - } - - fn validate_messages( - parameters: &Parameters, - round2_public_messages: &BTreeMap, - round1_public_messages: &BTreeMap, - round2_private_messages: &BTreeMap, - ) -> DKGResult<()> { - if round2_public_messages.len() != *parameters.participants() as usize - 1 { - return Err(DKGError::IncorrectNumberOfRound2PublicMessages { - expected: *parameters.participants() as usize - 1, - actual: round2_public_messages.len(), - }); - } - - if round1_public_messages.len() != *parameters.participants() as usize - 1 { - return Err(DKGError::IncorrectNumberOfRound1PublicMessages { - expected: *parameters.participants() as usize - 1, - actual: round1_public_messages.len(), - }); - } - - if round2_private_messages.len() != *parameters.participants() as usize - 1 { - return Err(DKGError::IncorrectNumberOfRound2PrivateMessages { - expected: *parameters.participants() as usize - 1, - actual: round2_private_messages.len(), - }); - } - - Ok(()) - } - - /// Runs the round 3 of the SimplPedPoP protocol. - pub fn run( - round2_public_messages: &BTreeMap, - round2_public_data: &round2::PublicData, - round1_public_data: &round1::PublicData, - round1_private_data: round1::PrivateData, - round2_private_messages: &BTreeMap, - ) -> DKGResult<( - GroupPublicKey, - BTreeMap, - PrivateData, - )> { - round1_public_data.parameters.validate()?; - - round2_public_data - .identifiers - .validate(round1_public_data.parameters.participants)?; - - validate_messages( - &round1_public_data.parameters, - round2_public_messages, - &round2_public_data.round1_public_messages, - round2_private_messages, - )?; - - verify_round2_public_messages(round2_public_messages, round2_public_data)?; - - let secret_shares = verify_round2_private_messages( - round2_public_data, - round2_private_messages, - &round1_private_data.secret_key.key, - )?; - - let private_data = generate_private_data( - &round2_public_data.identifiers, - &secret_shares, - &round1_private_data.secret_polynomial, - )?; - - let (group_public_key, group_public_key_shares) = - generate_public_data(round2_public_data, round1_public_data)?; - - Ok((group_public_key, group_public_key_shares, private_data)) - } - - fn verify_round2_public_messages( - round2_public_messages: &BTreeMap, - round2_public_data: &round2::PublicData, - ) -> DKGResult<()> { - verify_batch( - vec![ - round2_public_data.transcript.clone(); - round2_public_data.identifiers.others_identifiers.len() - ], - &round2_public_messages - .iter() - .map(|(id, msg)| { - if !round2_public_data - .identifiers - .others_identifiers() - .contains(id) - { - Err(DKGError::UnknownIdentifierRound2PublicMessages(*id)) - } else { - Ok(msg.certificate) - } - }) - .collect::, DKGError>>()?, - &round2_public_data.public_keys[..], - false, - ) - .map_err(DKGError::InvalidCertificate) - } - - fn verify_round2_private_messages( - round2_public_data: &round2::PublicData, - round2_private_messages: &BTreeMap, - secret: &Scalar, - ) -> DKGResult> { - let mut secret_shares = BTreeMap::new(); - - for (i, (identifier, private_message)) in round2_private_messages.iter().enumerate() { - if !round2_public_data - .identifiers - .others_identifiers - .contains(identifier) - { - return Err(DKGError::UnknownIdentifierRound2PrivateMessages( - *identifier, - )); - } - - let secret_share = private_message.encrypted_secret_share.decrypt( - secret, - &round2_public_data.public_keys[i].into_point(), - round2_public_data.identifiers.own_identifier.0.as_bytes(), - )?; - - let expected_evaluation = GENERATOR * secret_share.0; - - secret_shares.insert(*identifier, secret_share); - - let evaluation = round2_public_data - .round1_public_messages - .get(identifier) - .unwrap() - .secret_polynomial_commitment - .evaluate(&round2_public_data.identifiers.own_identifier.0); - - if !(evaluation == expected_evaluation) { - return Err(DKGError::InvalidSecretShare(*identifier)); - } - } - - Ok(secret_shares) - } - - fn generate_private_data( - identifiers: &Identifiers, - secret_shares: &BTreeMap, - secret_polynomial: &SecretPolynomial, - ) -> DKGResult { - let own_secret_share = secret_polynomial.evaluate(&identifiers.own_identifier.0); - - let mut total_secret_share = Scalar::ZERO; - - for id in &identifiers.others_identifiers { - // This never fails because we previously checked - total_secret_share += secret_shares.get(id).unwrap().0; - } - - total_secret_share += own_secret_share; - - let private_data = PrivateData { - total_secret_share: SecretShare(total_secret_share), - }; - - Ok(private_data) - } - - fn generate_public_data( - round2_public_data: &round2::PublicData, - round1_public_data: &round1::PublicData, - ) -> DKGResult<(GroupPublicKey, BTreeMap)> { - // Sum of the secret polynomial commitments of the other participants - let others_secret_polynomial_commitment = PolynomialCommitment::sum_polynomial_commitments( - &round2_public_data - .round1_public_messages - .values() - .map(|msg| &msg.secret_polynomial_commitment) - .collect::>(), - ); - - // The total secret polynomial commitment, which includes the secret polynomial commitment of the participant - let total_secret_polynomial_commitment = - PolynomialCommitment::sum_polynomial_commitments(&[ - &others_secret_polynomial_commitment, - &round1_public_data.secret_polynomial_commitment, - ]); - - // The group public key shares of all the participants, which correspond to the total secret shares commitments - let mut group_public_key_shares = BTreeMap::new(); - - for identifier in &round2_public_data.identifiers.others_identifiers { - let group_public_key_share = total_secret_polynomial_commitment.evaluate(&identifier.0); - - group_public_key_shares.insert(*identifier, group_public_key_share); - } - - let own_group_public_key_share = total_secret_polynomial_commitment - .evaluate(&round2_public_data.identifiers.own_identifier.0); - - group_public_key_shares.insert( - round2_public_data.identifiers.own_identifier, - own_group_public_key_share, - ); - - let shared_public_key = GroupPublicKey::from_point( - *total_secret_polynomial_commitment - .coefficients_commitments - .first() - .unwrap(), - ); - - // The shared public key corresponds to the secret commitment of the total secret polynomial commitment - if shared_public_key.as_point() - != &total_secret_polynomial_commitment.evaluate(&Scalar::ZERO) - { - return Err(DKGError::SharedPublicKeyMismatch); - } - - Ok((shared_public_key, group_public_key_shares)) - } -} - -#[cfg(test)] -mod tests { - use self::{ - round1::{PrivateData, PublicData}, - round2::Messages, - }; - use super::*; - use crate::{errors::DKGResult, SignatureError}; - #[cfg(feature = "alloc")] - use alloc::{ - collections::{BTreeMap, BTreeSet}, - vec::Vec, - }; - use merlin::Transcript; - use rand::{rngs::OsRng, Rng}; - use tests::round1::PublicMessage; - - const MAXIMUM_PARTICIPANTS: u16 = 10; - const MINIMUM_PARTICIPANTS: u16 = 3; - const MININUM_THRESHOLD: u16 = 2; - const PROTOCOL_RUNS: usize = 1; - - fn generate_parameters() -> Vec { - let mut rng = rand::thread_rng(); - let max_signers = rng.gen_range(MINIMUM_PARTICIPANTS..=MAXIMUM_PARTICIPANTS); - let min_signers = rng.gen_range(MININUM_THRESHOLD..=max_signers); - - (1..=max_signers) - .map(|_| Parameters::new(max_signers, min_signers)) - .collect() - } - - fn round1() -> ( - Vec, - Vec, - Vec, - Vec>, - ) { - let parameters_list = generate_parameters(); - - let mut all_public_messages_vec = Vec::new(); - let mut participants_round1_private_data = Vec::new(); - let mut participants_round1_public_data = Vec::new(); - - for i in 0..parameters_list.len() { - let (private_data, public_message, public_data) = - round1::run(parameters_list[i as usize].clone(), OsRng) - .expect("Round 1 should complete without errors!"); - - all_public_messages_vec.push(public_message.clone()); - participants_round1_public_data.push(public_data); - participants_round1_private_data.push(private_data); - } - - let mut received_round1_public_messages: Vec> = Vec::new(); - - let mut all_public_messages = BTreeSet::new(); - - for i in 0..parameters_list[0].participants { - all_public_messages.insert(all_public_messages_vec[i as usize].clone()); - } - - // Iterate through each participant to create a set of messages excluding their own. - for i in 0..parameters_list[0].participants { - let own_message = PublicMessage::new(&participants_round1_public_data[i as usize]); - - let mut messages_for_participant = BTreeSet::new(); - - for message in &all_public_messages { - if &own_message != message { - // Exclude the participant's own message. - messages_for_participant.insert(message.clone()); - } - } - - received_round1_public_messages.push(messages_for_participant); - } - - ( - parameters_list, - participants_round1_private_data, - participants_round1_public_data, - received_round1_public_messages, - ) - } - - fn round2( - parameters_list: &Vec, - participants_round1_private_data: Vec, - participants_round1_public_data: &Vec, - participants_round1_public_messages: &Vec>, - ) -> DKGResult<( - Vec>, - Vec, - Vec, - Vec, - )> { - let mut participants_round2_public_data = Vec::new(); - let mut participants_round2_public_messages = Vec::new(); - let mut participants_set_of_participants = Vec::new(); - let mut identifiers_vec = Vec::new(); - - for i in 0..parameters_list[0].participants { - let result = round2::run( - participants_round1_private_data[i as usize].clone(), - &participants_round1_public_data[i as usize].clone(), - participants_round1_public_messages[i as usize].clone(), - Transcript::new(b"simplpedpop"), - )?; - - participants_round2_public_data.push(result.0.clone()); - participants_round2_public_messages.push(result.1); - participants_set_of_participants.push(result.0.identifiers.clone()); - identifiers_vec.push(result.0.identifiers.own_identifier); - } - - Ok(( - participants_round2_public_data, - participants_round2_public_messages, - participants_set_of_participants, - identifiers_vec, - )) - } - - fn round3( - participants_sets_of_participants: &Vec, - participants_round2_public_messages: &Vec, - participants_round2_public_data: &Vec>, - participants_round1_public_data: &Vec, - participants_round1_private_data: Vec, - participants_round2_private_messages: Vec>, - identifiers_vec: &Vec, - ) -> DKGResult< - Vec<( - GroupPublicKey, - BTreeMap, - round3::PrivateData, - )>, - > { - let mut participant_data_round3 = Vec::new(); - - for i in 0..participants_sets_of_participants.len() { - let received_round2_public_messages = participants_round2_public_messages - .iter() - .enumerate() - .filter(|(index, _msg)| { - identifiers_vec[*index] - != participants_sets_of_participants[i as usize].own_identifier - }) - .map(|(index, msg)| (identifiers_vec[index], msg.clone())) - .collect::>(); - - let mut round2_private_messages: Vec> = - Vec::new(); - - for participants in participants_sets_of_participants.iter() { - let mut messages_for_participant = BTreeMap::new(); - - for (i, round_messages) in participants_round2_private_messages.iter().enumerate() { - if let Some(message) = round_messages.get(&participants.own_identifier) { - messages_for_participant.insert(identifiers_vec[i], message.clone()); - } - } - - round2_private_messages.push(messages_for_participant); - } - - let result = round3::run( - &received_round2_public_messages, - &participants_round2_public_data[i as usize], - &participants_round1_public_data[i as usize], - participants_round1_private_data[i as usize].clone(), - &round2_private_messages[i as usize], - )?; - - participant_data_round3.push(result); - } - - Ok(participant_data_round3) - } - - #[test] - pub fn test_successful_simplpedpop() { - for _ in 0..PROTOCOL_RUNS { - let ( - parameters_list, - participants_round1_private_data, - participants_round1_public_data, - participants_round1_public_messages, - ) = round1(); - - let ( - participants_round2_public_data, - participants_round2_messages, - participants_sets_of_participants, - identifiers_vec, - ) = round2( - ¶meters_list, - participants_round1_private_data.clone(), - &participants_round1_public_data, - &participants_round1_public_messages, - ) - .unwrap(); - - let participants_data_round3 = round3( - &participants_sets_of_participants, - &participants_round2_messages - .iter() - .map(|msg| msg.public_message().clone()) - .collect(), - &participants_round2_public_data, - &participants_round1_public_data, - participants_round1_private_data, - participants_round2_messages - .iter() - .map(|msg| msg.private_messages().clone()) - .collect(), - &identifiers_vec, - ) - .unwrap(); - - let shared_public_keys: Vec = participants_data_round3 - .iter() - .map(|state| state.0) - .collect(); - - assert!( - shared_public_keys.windows(2).all(|w| w[0] == w[1]), - "All participants must have the same group public key!" - ); - - for i in 0..parameters_list[0].participants { - assert_eq!( - participants_data_round3[i as usize] - .1 - .get(&participants_sets_of_participants[i as usize].own_identifier) - .unwrap() - .compress(), - (participants_data_round3[i as usize].2.total_secret_share.0 * GENERATOR) - .compress(), - "Verification of total secret shares failed!" - ); - } - } - } - - #[test] - fn test_incorrect_number_of_round1_public_messages_in_round2() { - let ( - parameters_list, - participants_round1_private_data, - participants_round1_public_data, - mut participants_round1_public_messages, - ) = round1(); - - participants_round1_public_messages[0].pop_last(); - - let result = round2( - ¶meters_list, - participants_round1_private_data.clone(), - &participants_round1_public_data, - &participants_round1_public_messages, - ); - - match result { - Ok(_) => panic!("Expected an error, but got Ok."), - Err(e) => assert_eq!( - e, - DKGError::IncorrectNumberOfRound1PublicMessages { - expected: parameters_list[0].participants as usize - 1, - actual: parameters_list[0].participants as usize - 2, - }, - "Expected DKGError::IncorrectNumberOfRound1PublicMessages." - ), - } - } - - #[test] - fn test_invalid_secret_polynomial_commitment_in_round2() { - let ( - parameters_list, - participants_round1_private_data, - participants_round1_public_data, - mut participants_round1_public_messages, - ) = round1(); - - let mut new_message = participants_round1_public_messages[0] - .first() - .unwrap() - .clone(); - - new_message - .secret_polynomial_commitment - .coefficients_commitments - .pop(); - - participants_round1_public_messages[0].pop_first(); - participants_round1_public_messages[0].insert(new_message); - - let result = round2( - ¶meters_list, - participants_round1_private_data.clone(), - &participants_round1_public_data, - &participants_round1_public_messages, - ); - - match result { - Ok(_) => panic!("Expected an error, but got Ok."), - Err(e) => assert_eq!( - e, - DKGError::InvalidSecretPolynomialCommitment { - expected: *parameters_list[0].threshold() as usize, - actual: *parameters_list[0].threshold() as usize - 1, - }, - "Expected DKGError::IncorrectNumberOfRound1PublicMessages." - ), - } - } - - #[test] - fn test_invalid_secret_share_error_in_round3() { - let ( - parameters_list, - participants_round1_private_data, - participants_round1_public_data, - participants_round1_public_messages, - ) = round1(); - - let ( - participants_round2_public_data, - participants_round2_messages, - participants_sets_of_participants, - identifiers_vec, - ) = round2( - ¶meters_list, - participants_round1_private_data.clone(), - &participants_round1_public_data, - &participants_round1_public_messages, - ) - .unwrap(); - - let mut participants_round2_private_messages: Vec< - BTreeMap, - > = participants_round2_messages - .iter() - .map(|msg| msg.private_messages().clone()) - .collect(); - - let enc_keys: Vec = participants_round1_public_messages[1] - .iter() - .map(|msg| { - *msg.secret_polynomial_commitment - .coefficients_commitments - .first() - .unwrap() - }) - .collect(); - - let secret_share = SecretShare(Scalar::random(&mut OsRng)); - - let identifiers: BTreeSet = participants_sets_of_participants[1] - .others_identifiers - .clone(); - - let index = identifiers - .iter() - .position(|x| x == &participants_sets_of_participants[0].own_identifier) - .unwrap(); - - let enc_share = secret_share.encrypt( - &participants_round1_private_data[1].secret_key.key, - &enc_keys[index], - participants_sets_of_participants[0] - .own_identifier - .0 - .as_bytes(), - ); - - let private_message = participants_round2_private_messages[1] - .get_mut(&participants_sets_of_participants[0].own_identifier) - .unwrap(); - - private_message.encrypted_secret_share = enc_share; - - let result = round3( - &participants_sets_of_participants, - &participants_round2_messages - .iter() - .map(|msg| msg.public_message().clone()) - .collect(), - &participants_round2_public_data, - &participants_round1_public_data, - participants_round1_private_data, - participants_round2_private_messages, - &identifiers_vec, - ); - - match result { - Ok(_) => panic!("Expected an error, but got Ok."), - Err(e) => assert_eq!( - e, - DKGError::InvalidSecretShare(participants_sets_of_participants[1].own_identifier), - "Expected DKGError::InvalidSecretShare." - ), - } - } - - #[test] - fn test_decryption_error_in_round3() { - let ( - parameters_list, - participants_round1_private_data, - participants_round1_public_data, - participants_round1_public_messages, - ) = round1(); - - let ( - participants_round2_public_data, - participants_round2_messages, - participants_sets_of_participants, - identifiers_vec, - ) = round2( - ¶meters_list, - participants_round1_private_data.clone(), - &participants_round1_public_data, - &participants_round1_public_messages, - ) - .unwrap(); - - let mut participants_round2_private_messages: Vec< - BTreeMap, - > = participants_round2_messages - .iter() - .map(|msg| msg.private_messages().clone()) - .collect(); - - let private_message = participants_round2_private_messages[1] - .get_mut(&participants_sets_of_participants[0].own_identifier) - .unwrap(); - - private_message.encrypted_secret_share = EncryptedSecretShare(vec![1]); - - let result = round3( - &participants_sets_of_participants, - &participants_round2_messages - .iter() - .map(|msg| msg.public_message().clone()) - .collect(), - &participants_round2_public_data, - &participants_round1_public_data, - participants_round1_private_data, - participants_round2_private_messages, - &identifiers_vec, - ); - - match result { - Ok(_) => panic!("Expected an error, but got Ok."), - Err(e) => assert_eq!( - e, - DKGError::DecryptionError(chacha20poly1305::Error), - "Expected DKGError::DecryptionError." - ), - } - } - - #[test] - fn test_invalid_proof_of_possession_in_round2() { - let ( - parameters_list, - participants_round1_private_data, - participants_round1_public_data, - mut participants_round1_public_messages, - ) = round1(); - - let sk = SecretKey::generate(); - let proof_of_possession = sk.sign( - Transcript::new(b"invalid proof of possession"), - &PublicKey::from(sk.clone()), - ); - let msg = PublicMessage { - secret_polynomial_commitment: PolynomialCommitment::commit(&Polynomial::generate( - &mut OsRng, - parameters_list[0].threshold - 1, - )), - proof_of_possession, - }; - participants_round1_public_messages[0].pop_last(); - participants_round1_public_messages[0].insert(msg); - - let result = round2( - ¶meters_list, - participants_round1_private_data.clone(), - &participants_round1_public_data, - &participants_round1_public_messages, - ); - - match result { - Ok(_) => panic!("Expected an error, but got Ok."), - Err(e) => assert_eq!( - e, - DKGError::InvalidProofOfPossession(SignatureError::EquationFalse), - "Expected DKGError::InvalidProofOfPossession." - ), - } - } - - #[test] - pub fn test_invalid_certificate_in_round3() { - let ( - parameters_list, - participants_round1_private_data, - participants_round1_public_data, - participants_round1_public_messages, - ) = round1(); - - let ( - mut participants_round2_public_data, - participants_round2_messages, - participants_sets_of_participants, - identifiers_vec, - ) = round2( - ¶meters_list, - participants_round1_private_data.clone(), - &participants_round1_public_data, - &participants_round1_public_messages, - ) - .unwrap(); - - participants_round2_public_data[0].transcript = Transcript::new(b"invalid transcript"); - - let participants_round2_private_messages: Vec< - BTreeMap, - > = participants_round2_messages - .iter() - .map(|msg| msg.private_messages().clone()) - .collect(); - - let result = round3( - &participants_sets_of_participants, - &participants_round2_messages - .iter() - .map(|msg| msg.public_message().clone()) - .collect(), - &participants_round2_public_data, - &participants_round1_public_data, - participants_round1_private_data, - participants_round2_private_messages, - &identifiers_vec, - ); - - match result { - Ok(_) => panic!("Expected an error, but got Ok."), - Err(e) => assert_eq!( - e, - DKGError::InvalidCertificate(SignatureError::EquationFalse), - "Expected DKGError::InvalidCertificate." - ), - } - } - - #[test] - pub fn test_incorrect_number_of_round2_public_messages_in_round3() { - let ( - parameters_list, - participants_round1_private_data, - participants_round1_public_data, - participants_round1_public_messages, - ) = round1(); - - let ( - participants_round2_public_data, - participants_round2_messages, - participants_sets_of_participants, - identifiers_vec, - ) = round2( - ¶meters_list, - participants_round1_private_data.clone(), - &participants_round1_public_data, - &participants_round1_public_messages, - ) - .unwrap(); - - let mut participants_round2_public_messages: Vec = - participants_round2_messages - .iter() - .map(|msg| msg.public_message().clone()) - .collect(); - - participants_round2_public_messages.pop(); - - let participants_round2_private_messages: Vec< - BTreeMap, - > = participants_round2_messages - .iter() - .map(|msg| msg.private_messages().clone()) - .collect(); - - let result = round3( - &participants_sets_of_participants, - &participants_round2_public_messages, - &participants_round2_public_data, - &participants_round1_public_data, - participants_round1_private_data, - participants_round2_private_messages, - &identifiers_vec, - ); - - match result { - Ok(_) => panic!("Expected an error, but got Ok."), - Err(e) => assert_eq!( - e, - DKGError::IncorrectNumberOfRound2PublicMessages { - expected: *parameters_list[0].participants() as usize - 1, - actual: *parameters_list[0].participants() as usize - 2 - }, - "Expected DKGError::IncorrectNumberOfRound2PublicMessages." - ), - } - } - - #[test] - pub fn test_incorrect_number_of_round1_public_messages_in_round3() { - let ( - parameters_list, - participants_round1_private_data, - participants_round1_public_data, - participants_round1_public_messages, - ) = round1(); - - let ( - mut participants_round2_public_data, - participants_round2_messages, - participants_sets_of_participants, - identifiers_vec, - ) = round2( - ¶meters_list, - participants_round1_private_data.clone(), - &participants_round1_public_data, - &participants_round1_public_messages, - ) - .unwrap(); - - let participants_round2_public_messages: Vec = - participants_round2_messages - .iter() - .map(|msg| msg.public_message().clone()) - .collect(); - - participants_round2_public_data[0] - .round1_public_messages - .pop_first(); - - let participants_round2_private_messages: Vec< - BTreeMap, - > = participants_round2_messages - .iter() - .map(|msg| msg.private_messages().clone()) - .collect(); - - let result = round3( - &participants_sets_of_participants, - &participants_round2_public_messages, - &participants_round2_public_data, - &participants_round1_public_data, - participants_round1_private_data, - participants_round2_private_messages, - &identifiers_vec, - ); - - match result { - Ok(_) => panic!("Expected an error, but got Ok."), - Err(e) => assert_eq!( - e, - DKGError::IncorrectNumberOfRound1PublicMessages { - expected: *parameters_list[0].participants() as usize - 1, - actual: *parameters_list[0].participants() as usize - 2 - }, - "Expected DKGError::IncorrectNumberOfRound1PublicMessages." - ), - } - } - - #[test] - pub fn test_incorrect_number_of_round2_private_messages_in_round3() { - let ( - parameters_list, - participants_round1_private_data, - participants_round1_public_data, - participants_round1_public_messages, - ) = round1(); - - let ( - participants_round2_public_data, - participants_round2_messages, - participants_sets_of_participants, - identifiers_vec, - ) = round2( - ¶meters_list, - participants_round1_private_data.clone(), - &participants_round1_public_data, - &participants_round1_public_messages, - ) - .unwrap(); - - let participants_round2_public_messages: Vec = - participants_round2_messages - .iter() - .map(|msg| msg.public_message().clone()) - .collect(); - - let mut participants_round2_private_messages: Vec< - BTreeMap, - > = participants_round2_messages - .iter() - .map(|msg| msg.private_messages().clone()) - .collect(); - - participants_round2_private_messages[1].pop_last(); - - let result = round3( - &participants_sets_of_participants, - &participants_round2_public_messages, - &participants_round2_public_data, - &participants_round1_public_data, - participants_round1_private_data, - participants_round2_private_messages, - &identifiers_vec, - ); - - match result { - Ok(_) => panic!("Expected an error, but got Ok."), - Err(e) => assert_eq!( - e, - DKGError::IncorrectNumberOfRound2PrivateMessages { - expected: *parameters_list[0].participants() as usize - 1, - actual: *parameters_list[0].participants() as usize - 2 - }, - "Expected DKGError::IncorrectNumberOfRound2PrivateMessages." - ), - } - } - - #[test] - pub fn test_unknown_identifier_from_round2_public_messages_in_round3() { - let ( - parameters_list, - participants_round1_private_data, - participants_round1_public_data, - participants_round1_public_messages, - ) = round1(); - - let ( - participants_round2_public_data, - participants_round2_messages, - participants_sets_of_participants, - mut identifiers_vec, - ) = round2( - ¶meters_list, - participants_round1_private_data.clone(), - &participants_round1_public_data, - &participants_round1_public_messages, - ) - .unwrap(); - - let participants_round2_public_messages: Vec = - participants_round2_messages - .iter() - .map(|msg| msg.public_message().clone()) - .collect(); - - identifiers_vec.pop(); - let unknown_identifier = Identifier(Scalar::random(&mut OsRng)); - identifiers_vec.push(unknown_identifier); - - let participants_round2_private_messages: Vec< - BTreeMap, - > = participants_round2_messages - .iter() - .map(|msg| msg.private_messages().clone()) - .collect(); - - let result = round3( - &participants_sets_of_participants, - &participants_round2_public_messages, - &participants_round2_public_data, - &participants_round1_public_data, - participants_round1_private_data, - participants_round2_private_messages, - &identifiers_vec, - ); - - match result { - Ok(_) => panic!("Expected an error, but got Ok."), - Err(e) => assert_eq!( - e, - DKGError::UnknownIdentifierRound2PublicMessages(unknown_identifier), - "Expected DKGError::UnknownIdentifierRound2PublicMessages." - ), - } - } - - #[test] - fn test_unknown_identifier_from_round2_private_messages_in_round3() { - let ( - parameters_list, - participants_round1_private_data, - participants_round1_public_data, - participants_round1_public_messages, - ) = round1(); - - let ( - participants_round2_public_data, - participants_round2_messages, - participants_sets_of_participants, - identifiers_vec, - ) = round2( - ¶meters_list, - participants_round1_private_data.clone(), - &participants_round1_public_data, - &participants_round1_public_messages, - ) - .unwrap(); - - let participants_round2_public_messages: Vec = - participants_round2_messages - .iter() - .map(|msg| msg.public_message().clone()) - .collect(); - - let mut identifiers_vec2 = identifiers_vec.clone(); - identifiers_vec2.pop(); - let unknown_identifier = Identifier(Scalar::random(&mut OsRng)); - identifiers_vec2.push(unknown_identifier); - - let participants_round2_private_messages: Vec< - BTreeMap, - > = participants_round2_messages - .iter() - .map(|msg| msg.private_messages().clone()) - .collect(); - - let received_round2_public_messages = participants_round2_public_messages - .iter() - .enumerate() - .filter(|(index, _msg)| { - identifiers_vec[*index] != participants_sets_of_participants[0].own_identifier - }) - .map(|(index, msg)| (identifiers_vec[index], msg.clone())) - .collect::>(); - - let mut round2_private_messages: Vec> = - Vec::new(); - - for participants in participants_sets_of_participants.iter() { - let mut messages_for_participant = BTreeMap::new(); - - for (i, round_messages) in participants_round2_private_messages.iter().enumerate() { - if let Some(message) = round_messages.get(&participants.own_identifier) { - messages_for_participant.insert(identifiers_vec2[i], message.clone()); - } - } - - round2_private_messages.push(messages_for_participant); - } - - let result = round3::run( - &received_round2_public_messages, - &participants_round2_public_data[0], - &participants_round1_public_data[0], - participants_round1_private_data[0].clone(), - &round2_private_messages[0], - ); - - match result { - Ok(_) => panic!("Expected an error, but got Ok."), - Err(e) => assert_eq!( - e, - DKGError::UnknownIdentifierRound2PrivateMessages(unknown_identifier), - "Expected DKGError::UnknownIdentifierRound2PrivateMessages." - ), - } - } - - #[test] - fn test_invalid_threshold() { - let parameters = Parameters::new(3, 1); - assert_eq!(parameters.validate(), Err(DKGError::InsufficientThreshold)); - } - - #[test] - fn test_invalid_participants() { - let parameters = Parameters::new(1, 2); - assert_eq!( - parameters.validate(), - Err(DKGError::InvalidNumberOfParticipants) - ); - } - - #[test] - fn test_threshold_greater_than_participants() { - let parameters = Parameters::new(2, 3); - assert_eq!(parameters.validate(), Err(DKGError::ExcessiveThreshold)); - } - - #[test] - fn test_encryption_decryption() { - let mut rng = OsRng; - let deckey = Scalar::random(&mut rng); - let enckey = RistrettoPoint::random(&mut rng); - let context = b"context"; - - let original_share = SecretShare(Scalar::random(&mut rng)); - - let encrypted_share = original_share.encrypt(&deckey, &enckey, context); - let decrypted_share = encrypted_share.decrypt(&deckey, &enckey, context); - - assert_eq!( - original_share.0, - decrypted_share.unwrap().0, - "Decryption must return the original share!" - ); - } -} From 4c033ef0c9f1d707e51f49a6ecb652499f325247 Mon Sep 17 00:00:00 2001 From: Fiono11 Date: Mon, 29 Apr 2024 17:52:59 +0100 Subject: [PATCH 3/8] Add encryption error --- src/olaf/errors.rs | 2 ++ src/olaf/simplpedpop.rs | 27 +++++++++++++++------------ src/olaf/tests.rs | 4 ++-- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/olaf/errors.rs b/src/olaf/errors.rs index 008ce51..a62ba82 100644 --- a/src/olaf/errors.rs +++ b/src/olaf/errors.rs @@ -66,6 +66,8 @@ pub enum DKGError { }, /// Decryption error when decrypting an encrypted secret share. DecryptionError(chacha20poly1305::Error), + /// Encryption error when encrypting the secret share. + EncryptionError(chacha20poly1305::Error), /// Incorrect number of coefficient commitments. InvalidSecretPolynomialCommitment { /// The expected value. diff --git a/src/olaf/simplpedpop.rs b/src/olaf/simplpedpop.rs index 6a2c6f9..2bf105a 100644 --- a/src/olaf/simplpedpop.rs +++ b/src/olaf/simplpedpop.rs @@ -139,7 +139,7 @@ impl SecretShare { decryption_key: &Scalar, encryption_key: &RistrettoPoint, context: &[u8], - ) -> EncryptedSecretShare { + ) -> DKGResult { let shared_secret = decryption_key * encryption_key; let mut transcript = Transcript::new(b"encryption"); @@ -152,9 +152,11 @@ impl SecretShare { let cipher: ChaCha20Poly1305 = make_aead::(transcript); let nonce = Nonce::from_slice(&bytes[..]); - let ciphertext: Vec = cipher.encrypt(nonce, &self.0.as_bytes()[..]).unwrap(); + let ciphertext: Vec = cipher + .encrypt(nonce, &self.0.as_bytes()[..]) + .map_err(DKGError::EncryptionError)?; - EncryptedSecretShare(ciphertext) + Ok(EncryptedSecretShare(ciphertext)) } } @@ -397,12 +399,12 @@ pub mod round2 { deckey: Scalar, enckey: RistrettoPoint, context: &[u8], - ) -> PrivateMessage { - let encrypted_secret_share = secret_share.encrypt(&deckey, &enckey, context); + ) -> DKGResult { + let encrypted_secret_share = secret_share.encrypt(&deckey, &enckey, context)?; - PrivateMessage { + Ok(PrivateMessage { encrypted_secret_share, - } + }) } } @@ -477,7 +479,7 @@ pub mod round2 { &round1_private_data.secret_polynomial, round1_private_data.secret_key, secret_commitment, - ); + )?; Ok((public_data, messages)) } @@ -664,8 +666,9 @@ pub mod round2 { secret_polynomial: &SecretPolynomial, secret_key: SecretKey, secret_commitment: &SecretCommitment, - ) -> Messages { + ) -> DKGResult { let mut private_messages = BTreeMap::new(); + let enc_keys: Vec = round2_public_data .round1_public_messages .values() @@ -691,7 +694,7 @@ pub mod round2 { secret_key.key, enc_keys[i], identifier.0.as_bytes(), - ), + )?, ); } @@ -704,10 +707,10 @@ pub mod round2 { let public_message = PublicMessage { certificate }; - Messages { + Ok(Messages { private_messages, public_message, - } + }) } } diff --git a/src/olaf/tests.rs b/src/olaf/tests.rs index 64e7ed3..494b59d 100644 --- a/src/olaf/tests.rs +++ b/src/olaf/tests.rs @@ -396,7 +396,7 @@ mod tests { .get_mut(&participants_sets_of_participants[0].own_identifier) .unwrap(); - private_message.encrypted_secret_share = enc_share; + private_message.encrypted_secret_share = enc_share.unwrap(); let result = round3( &participants_sets_of_participants, @@ -956,7 +956,7 @@ mod tests { let original_share = SecretShare(Scalar::random(&mut rng)); let encrypted_share = original_share.encrypt(&deckey, &enckey, context); - let decrypted_share = encrypted_share.decrypt(&deckey, &enckey, context); + let decrypted_share = encrypted_share.unwrap().decrypt(&deckey, &enckey, context); assert_eq!( original_share.0, From 593b622ec02ddf8537324df56ed73facb6a77506 Mon Sep 17 00:00:00 2001 From: Fiono11 Date: Mon, 29 Apr 2024 18:29:40 +0100 Subject: [PATCH 4/8] Remove unwraps --- src/olaf/errors.rs | 4 ++- src/olaf/simplpedpop.rs | 66 +++++++++++++++++++++++------------------ src/olaf/tests.rs | 28 ++++++++++------- 3 files changed, 58 insertions(+), 40 deletions(-) diff --git a/src/olaf/errors.rs b/src/olaf/errors.rs index a62ba82..301be35 100644 --- a/src/olaf/errors.rs +++ b/src/olaf/errors.rs @@ -23,10 +23,12 @@ pub enum DKGError { InvalidSecretShare(Identifier), /// Invalid secret. InvalidSecret, + /// Unknown identifier in round 1 public messages. + UnknownIdentifierRound1PublicMessages(Identifier), /// Unknown identifier in round 2 public messages. UnknownIdentifierRound2PublicMessages(Identifier), /// Unknown identifier in round 2 private messages. - UnknownIdentifierRound2PrivateMessages(Identifier), + UnknownIdentifierRound2PrivateMessages, /// Identifier cannot be a zero scalar. InvalidIdentifier, /// Incorrect number of identifiers. diff --git a/src/olaf/simplpedpop.rs b/src/olaf/simplpedpop.rs index 2bf105a..9cc4e1f 100644 --- a/src/olaf/simplpedpop.rs +++ b/src/olaf/simplpedpop.rs @@ -125,7 +125,8 @@ fn derive_secret_key_from_secret(secret: &Secret, mut rn bytes[..32].copy_from_slice(&secret_bytes[..]); bytes[32..].copy_from_slice(&nonce[..]); - SecretKey::from_bytes(&bytes[..]).unwrap() // This never fails because bytes has length 64 and the key is a scalar + SecretKey::from_bytes(&bytes[..]) + .expect("This never fails because bytes has length 64 and the key is a scalar") } /// A secret share, which corresponds to an evaluation of a value that identifies a participant in a secret polynomial. @@ -263,7 +264,7 @@ pub mod round1 { self.secret_polynomial_commitment .coefficients_commitments .first() - .unwrap() + .expect("This never fails because the minimum threshold of the protocol is 2") .compress() .0 .cmp( @@ -271,7 +272,9 @@ pub mod round1 { .secret_polynomial_commitment .coefficients_commitments .first() - .unwrap() + .expect( + "This never fails because the minimum threshold of the protocol is 2", + ) .compress() .0, ) @@ -299,7 +302,12 @@ pub mod round1 { let secret_polynomial = loop { let temp_polynomial = Polynomial::generate(&mut rng, *parameters.threshold() - 1); // There must be a secret, which is the constant coefficient of the secret polynomial - if temp_polynomial.coefficients.first().unwrap() != &Scalar::ZERO { + if temp_polynomial + .coefficients + .first() + .expect("This never fails because the minimum threshold of the protocol is 2") + != &Scalar::ZERO + { break temp_polynomial; } }; @@ -307,14 +315,19 @@ pub mod round1 { let secret_polynomial_commitment = PolynomialCommitment::commit(&secret_polynomial); // This secret key will be used to sign the proof of possession and the certificate - let secret_key = - derive_secret_key_from_secret(secret_polynomial.coefficients.first().unwrap(), rng); + let secret_key = derive_secret_key_from_secret( + secret_polynomial + .coefficients + .first() + .expect("This never fails because the minimum threshold of the protocol is 2"), + rng, + ); let public_key = PublicKey::from_point( *secret_polynomial_commitment .coefficients_commitments .first() - .unwrap(), + .expect("This never fails because the minimum threshold of the protocol is 2"), ); let proof_of_possession = @@ -472,7 +485,7 @@ pub mod round2 { .secret_polynomial_commitment .coefficients_commitments .first() - .unwrap(); + .expect("This never fails because the minimum threshold of the protocol is 2"); let messages = generate_messages( &public_data, @@ -496,7 +509,7 @@ pub mod round2 { .secret_polynomial_commitment .coefficients_commitments .first() - .unwrap() + .expect("This never fails because the minimum threshold of the protocol is 2") .compress(); // Writes the data of all the participants in the transcript ordered by their identifiers @@ -505,7 +518,7 @@ pub mod round2 { .secret_polynomial_commitment .coefficients_commitments .first() - .unwrap() + .expect("This never fails because the minimum threshold of the protocol is 2") .compress(); if own_first_coefficient_compressed.0 < message_first_coefficient_compressed.0 @@ -590,7 +603,7 @@ pub mod round2 { msg.secret_polynomial_commitment .coefficients_commitments .first() - .unwrap() + .expect("This never fails because the minimum threshold of the protocol is 2") .compress() .0 }) @@ -600,7 +613,7 @@ pub mod round2 { .secret_polynomial_commitment .coefficients_commitments .first() - .unwrap(); + .expect("This never fails because the minimum threshold of the protocol is 2"); secret_commitments.insert(own_secret_commitment.compress().0); @@ -618,7 +631,10 @@ pub mod round2 { others_identifiers.insert(Identifier(random_scalar)); } - let own_identifier = *others_identifiers.iter().nth(index).unwrap(); + let own_identifier = *others_identifiers + .iter() + .nth(index) + .expect("This never fails because the index < len"); others_identifiers.remove(&own_identifier); for (id, message) in others_identifiers.iter().zip(round1_public_messages_set) { @@ -644,7 +660,7 @@ pub mod round2 { .secret_polynomial_commitment .coefficients_commitments .first() - .unwrap(), + .expect("This never fails because the minimum threshold of the protocol is 2"), ); public_keys.push(public_key); proofs_of_possession.push(round1_public_message.proof_of_possession); @@ -676,7 +692,7 @@ pub mod round2 { *msg.secret_polynomial_commitment .coefficients_commitments .first() - .unwrap() + .expect("This never fails because the minimum threshold of the protocol is 2") }) .collect(); @@ -861,16 +877,6 @@ pub mod round3 { let mut secret_shares = BTreeMap::new(); for (i, (identifier, private_message)) in round2_private_messages.iter().enumerate() { - if !round2_public_data - .identifiers - .others_identifiers - .contains(identifier) - { - return Err(DKGError::UnknownIdentifierRound2PrivateMessages( - *identifier, - )); - } - let secret_share = private_message.encrypted_secret_share.decrypt( secret, &round2_public_data.public_keys[i].into_point(), @@ -884,7 +890,7 @@ pub mod round3 { let evaluation = round2_public_data .round1_public_messages .get(identifier) - .unwrap() + .ok_or(DKGError::UnknownIdentifierRound1PublicMessages(*identifier))? .secret_polynomial_commitment .evaluate(&round2_public_data.identifiers.own_identifier.0); @@ -906,8 +912,10 @@ pub mod round3 { let mut total_secret_share = Scalar::ZERO; for id in &identifiers.others_identifiers { - // This never fails because we previously checked - total_secret_share += secret_shares.get(id).unwrap().0; + total_secret_share += secret_shares + .get(id) + .ok_or(DKGError::UnknownIdentifierRound2PrivateMessages)? + .0; } total_secret_share += own_secret_share; @@ -960,7 +968,7 @@ pub mod round3 { *total_secret_polynomial_commitment .coefficients_commitments .first() - .unwrap(), + .expect("This never fails because the minimum threshold of the protocol is 2"), ); Ok((shared_public_key, group_public_key_shares)) diff --git a/src/olaf/tests.rs b/src/olaf/tests.rs index 494b59d..a3e5d30 100644 --- a/src/olaf/tests.rs +++ b/src/olaf/tests.rs @@ -827,7 +827,7 @@ mod tests { ) = round1(); let ( - participants_round2_public_data, + mut participants_round2_public_data, participants_round2_messages, participants_sets_of_participants, identifiers_vec, @@ -845,13 +845,6 @@ mod tests { .map(|msg| msg.public_message().clone()) .collect(); - let mut identifiers_vec2 = identifiers_vec.clone(); - - identifiers_vec2.pop(); - - let unknown_identifier = Identifier(Scalar::ONE); - identifiers_vec2.push(unknown_identifier); - let participants_round2_private_messages: Vec< BTreeMap, > = participants_round2_messages @@ -876,13 +869,28 @@ mod tests { for (i, round_messages) in participants_round2_private_messages.iter().enumerate() { if let Some(message) = round_messages.get(&participants.own_identifier) { - messages_for_participant.insert(identifiers_vec2[i], message.clone()); + messages_for_participant.insert(identifiers_vec[i], message.clone()); } } round2_private_messages.push(messages_for_participant); } + let unknown_identifier = Identifier(Scalar::ONE); + + let private_message = round2_private_messages[0].pop_first().unwrap().1; + round2_private_messages[0].insert(unknown_identifier, private_message); + + let public_message = participants_round2_public_data[0] + .round1_public_messages + .pop_first() + .unwrap() + .1; + + participants_round2_public_data[0] + .round1_public_messages + .insert(unknown_identifier, public_message); + let result = round3::run( &received_round2_public_messages, &participants_round2_public_data[0], @@ -895,7 +903,7 @@ mod tests { Ok(_) => panic!("Expected an error, but got Ok."), Err(e) => assert_eq!( e, - DKGError::UnknownIdentifierRound2PrivateMessages(unknown_identifier), + DKGError::UnknownIdentifierRound2PrivateMessages, "Expected DKGError::UnknownIdentifierRound2PrivateMessages." ), } From 009458e80699c5fc0c49970a3d685d15cad773ba Mon Sep 17 00:00:00 2001 From: Fiono11 Date: Mon, 29 Apr 2024 18:35:56 +0100 Subject: [PATCH 5/8] Cargo fmt --- benches/olaf_benchmarks.rs | 24 +- benches/schnorr_benchmarks.rs | 18 +- rustfmt.toml | 31 +- src/aead.rs | 6 +- src/batch.rs | 193 ++++++------ src/cert.rs | 67 +++-- src/context.rs | 182 +++++++---- src/derive.rs | 126 ++++---- src/errors.rs | 18 +- src/keys.rs | 153 +++++----- src/musig.rs | 550 ++++++++++++++++++++-------------- src/olaf/identifier.rs | 14 +- src/olaf/polynomial.rs | 51 ++-- src/olaf/simplpedpop.rs | 102 ++----- src/olaf/tests.rs | 68 ++--- src/points.rs | 52 ++-- src/sign.rs | 33 +- src/vrf.rs | 423 ++++++++++++++------------ 18 files changed, 1119 insertions(+), 992 deletions(-) diff --git a/benches/olaf_benchmarks.rs b/benches/olaf_benchmarks.rs index fd1032f..a8279d5 100644 --- a/benches/olaf_benchmarks.rs +++ b/benches/olaf_benchmarks.rs @@ -19,20 +19,13 @@ mod olaf_benches { use std::collections::{BTreeMap, BTreeSet}; fn generate_parameters(max_signers: u16, min_signers: u16) -> Vec { - (1..=max_signers) - .map(|_| Parameters::new(max_signers, min_signers)) - .collect() + (1..=max_signers).map(|_| Parameters::new(max_signers, min_signers)).collect() } fn round1( participants: u16, threshold: u16, - ) -> ( - Vec, - Vec, - Vec, - Vec>, - ) { + ) -> (Vec, Vec, Vec, Vec>) { let parameters_list = generate_parameters(participants, threshold); let mut all_public_messages_vec = Vec::new(); @@ -86,12 +79,7 @@ mod olaf_benches { participants_round1_private_data: Vec, participants_round1_public_data: &Vec, participants_round1_public_messages: &Vec>, - ) -> ( - Vec, - Vec, - Vec, - Vec, - ) { + ) -> (Vec, Vec, Vec, Vec) { let mut participants_round2_public_data = Vec::new(); let mut participants_round2_public_messages = Vec::new(); let mut participants_set_of_participants = Vec::new(); @@ -129,11 +117,7 @@ mod olaf_benches { participants_round2_private_messages: Vec>, identifiers_vec: &Vec, ) -> DKGResult< - Vec<( - GroupPublicKey, - BTreeMap, - round3::PrivateData, - )>, + Vec<(GroupPublicKey, BTreeMap, round3::PrivateData)>, > { let mut participant_data_round3 = Vec::new(); diff --git a/benches/schnorr_benchmarks.rs b/benches/schnorr_benchmarks.rs index ef0d829..32cd049 100644 --- a/benches/schnorr_benchmarks.rs +++ b/benches/schnorr_benchmarks.rs @@ -22,9 +22,7 @@ mod schnorr_benches { let msg: &[u8] = b""; let ctx = signing_context(b"this signature does this thing"); - c.bench_function("Schnorr signing", move |b| { - b.iter(|| keypair.sign(ctx.bytes(msg))) - }); + c.bench_function("Schnorr signing", move |b| b.iter(|| keypair.sign(ctx.bytes(msg)))); } fn verify(c: &mut Criterion) { @@ -47,10 +45,8 @@ mod schnorr_benches { let keypairs: Vec = (0..size).map(|_| Keypair::generate()).collect(); let msg: &[u8] = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; let ctx = signing_context(b"this signature does this thing"); - let signatures: Vec = keypairs - .iter() - .map(|key| key.sign(ctx.bytes(msg))) - .collect(); + let signatures: Vec = + keypairs.iter().map(|key| key.sign(ctx.bytes(msg))).collect(); let public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); b.iter(|| { let transcripts = ::std::iter::once(ctx.bytes(msg)).cycle().take(size); @@ -61,9 +57,7 @@ mod schnorr_benches { } fn key_generation(c: &mut Criterion) { - c.bench_function("Schnorr keypair generation", move |b| { - b.iter(|| Keypair::generate()) - }); + c.bench_function("Schnorr keypair generation", move |b| b.iter(|| Keypair::generate())); } criterion_group! { @@ -77,6 +71,4 @@ mod schnorr_benches { } } -criterion_main!( - schnorr_benches::schnorr_benches, -); +criterion_main!(schnorr_benches::schnorr_benches,); diff --git a/rustfmt.toml b/rustfmt.toml index 248fb36..8cf89b5 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1 +1,30 @@ -ignore = ["/"] +# Basic +edition = "2021" +max_width = 100 +use_small_heuristics = "Max" + +# Imports +imports_granularity = "Preserve" +reorder_imports = false +reorder_modules = false + +# Consistency +newline_style = "Unix" + +# Misc +chain_width = 80 +spaces_around_ranges = false +reorder_impl_items = false +match_arm_leading_pipes = "Preserve" +match_arm_blocks = false +match_block_trailing_comma = true +trailing_comma = "Vertical" +# trailing_semicolon = false +# use_field_init_shorthand = true + +# where_single_line = true # does not work on fn +# brace_style = "AlwaysNextLine" # does not work on method fn + +# Format comments +comment_width = 100 +wrap_comments = true diff --git a/src/aead.rs b/src/aead.rs index 29688ca..d16c0ed 100644 --- a/src/aead.rs +++ b/src/aead.rs @@ -150,11 +150,7 @@ impl Keypair { AEAD: KeyInit, { let key = t.witness_scalar(b"make_esk", &[&self.secret.nonce]); - let ekey = SecretKey { - key, - nonce: self.secret.nonce, - } - .to_keypair(); + let ekey = SecretKey { key, nonce: self.secret.nonce }.to_keypair(); ekey.commit_key_exchange(&mut t, b"epk", public); self.commit_key_exchange(&mut t, b"epk", public); (ekey.public.into_compressed(), make_aead(t)) diff --git a/src/batch.rs b/src/batch.rs index 4be860b..4e908a5 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -21,8 +21,8 @@ use crate::context::{SigningTranscript}; #[cfg(feature = "alloc")] use alloc::vec::Vec; -const ASSERT_MESSAGE: &str = "The number of messages/transcripts, signatures, and public keys must be equal."; - +const ASSERT_MESSAGE: &str = + "The number of messages/transcripts, signatures, and public keys must be equal."; /// Verify a batch of `signatures` on `messages` with their respective `public_keys`. /// @@ -63,7 +63,7 @@ const ASSERT_MESSAGE: &str = "The number of messages/transcripts, signatures, an /// assert!( verify_batch(transcripts, &signatures[..], &public_keys[..], false).is_ok() ); /// # } /// ``` -pub fn verify_batch( +pub fn verify_batch( transcripts: I, signatures: &[Signature], public_keys: &[PublicKey], @@ -71,19 +71,31 @@ pub fn verify_batch( ) -> SignatureResult<()> where T: SigningTranscript, - I: IntoIterator, + I: IntoIterator, { - verify_batch_rng(transcripts, signatures, public_keys, deduplicate_public_keys, getrandom_or_panic()) + verify_batch_rng( + transcripts, + signatures, + public_keys, + deduplicate_public_keys, + getrandom_or_panic(), + ) } struct NotAnRng; impl rand_core::RngCore for NotAnRng { - fn next_u32(&mut self) -> u32 { rand_core::impls::next_u32_via_fill(self) } + fn next_u32(&mut self) -> u32 { + rand_core::impls::next_u32_via_fill(self) + } - fn next_u64(&mut self) -> u64 { rand_core::impls::next_u64_via_fill(self) } + fn next_u64(&mut self) -> u64 { + rand_core::impls::next_u64_via_fill(self) + } /// A no-op function which leaves the destination bytes for randomness unchanged. - fn fill_bytes(&mut self, dest: &mut [u8]) { zeroize::Zeroize::zeroize(dest) } + fn fill_bytes(&mut self, dest: &mut [u8]) { + zeroize::Zeroize::zeroize(dest) + } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { self.fill_bytes(dest); @@ -98,13 +110,13 @@ impl rand_core::CryptoRng for NotAnRng {} /// /// We break the `R: CryptRng` requirement from `verify_batch_rng` /// here, but this appears fine using an Fiat-Shamir transform with -/// an argument similar to +/// an argument similar to /// [public key delinearization](https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html). /// /// We caution deeterministic delinearization could interact poorly /// with other functionality, *if* one delinarization scalar were /// left constant. We do not make that mistake here. -pub fn verify_batch_deterministic( +pub fn verify_batch_deterministic( transcripts: I, signatures: &[Signature], public_keys: &[PublicKey], @@ -112,7 +124,7 @@ pub fn verify_batch_deterministic( ) -> SignatureResult<()> where T: SigningTranscript, - I: IntoIterator, + I: IntoIterator, { verify_batch_rng(transcripts, signatures, public_keys, deduplicate_public_keys, NotAnRng) } @@ -120,7 +132,7 @@ where /// Verify a batch of `signatures` on `messages` with their respective `public_keys`. /// /// Inputs and return agree with `verify_batch` except the user supplies their own random number generator. -pub fn verify_batch_rng( +pub fn verify_batch_rng( transcripts: I, signatures: &[Signature], public_keys: &[PublicKey], @@ -129,77 +141,77 @@ pub fn verify_batch_rng( ) -> SignatureResult<()> where T: SigningTranscript, - I: IntoIterator, - R: RngCore+CryptoRng, + I: IntoIterator, + R: RngCore + CryptoRng, { - assert!(signatures.len() == public_keys.len(), "{}", ASSERT_MESSAGE); // Check transcripts length below + assert!(signatures.len() == public_keys.len(), "{}", ASSERT_MESSAGE); // Check transcripts length below let (zs, hrams) = prepare_batch(transcripts, signatures, public_keys, rng); // Compute the basepoint coefficient, ∑ s[i]z[i] (mod l) - let bs: Scalar = signatures.iter() - .map(|sig| sig.s) - .zip(zs.iter()) - .map(|(s, z)| z * s) - .sum(); + let bs: Scalar = signatures.iter().map(|sig| sig.s).zip(zs.iter()).map(|(s, z)| z * s).sum(); - verify_batch_equation( bs, zs, hrams, signatures, public_keys, deduplicate_public_keys ) + verify_batch_equation(bs, zs, hrams, signatures, public_keys, deduplicate_public_keys) } - trait HasR { #[allow(non_snake_case)] fn get_R(&self) -> &CompressedRistretto; } impl HasR for Signature { #[allow(non_snake_case)] - fn get_R(&self) -> &CompressedRistretto { &self.R } + fn get_R(&self) -> &CompressedRistretto { + &self.R + } } impl HasR for CompressedRistretto { #[allow(non_snake_case)] - fn get_R(&self) -> &CompressedRistretto { self } + fn get_R(&self) -> &CompressedRistretto { + self + } } /// First phase of batch verification that computes the delinierizing /// coefficents and challenge hashes #[allow(non_snake_case)] -fn prepare_batch( +fn prepare_batch( transcripts: I, signatures: &[impl HasR], public_keys: &[PublicKey], mut rng: R, -) -> (Vec,Vec) +) -> (Vec, Vec) where T: SigningTranscript, - I: IntoIterator, - R: RngCore+CryptoRng, + I: IntoIterator, + R: RngCore + CryptoRng, { - // Assumulate public keys, signatures, and transcripts for pseudo-random delinearization scalars let mut zs_t = merlin::Transcript::new(b"V-RNG"); for pk in public_keys { - zs_t.commit_point(b"",pk.as_compressed()); + zs_t.commit_point(b"", pk.as_compressed()); } for sig in signatures { - zs_t.commit_point(b"",sig.get_R()); + zs_t.commit_point(b"", sig.get_R()); } // We might collect here anyways, but right now you cannot have // IntoIterator let mut transcripts = transcripts.into_iter(); // Compute H(R || A || M) for each (signature, public_key, message) triplet - let hrams: Vec = transcripts.by_ref() + let hrams: Vec = transcripts + .by_ref() .zip(0..signatures.len()) - .map( |(mut t,i)| { + .map(|(mut t, i)| { let mut d = [0u8; 16]; - t.witness_bytes_rng(b"", &mut d, &[&[]], NotAnRng); // Could speed this up using ZeroRng - zs_t.append_message(b"",&d); + t.witness_bytes_rng(b"", &mut d, &[&[]], NotAnRng); // Could speed this up using ZeroRng + zs_t.append_message(b"", &d); t.proto_name(b"Schnorr-sig"); - t.commit_point(b"sign:pk",public_keys[i].as_compressed()); - t.commit_point(b"sign:R",signatures[i].get_R()); - t.challenge_scalar(b"sign:c") // context, message, A/public_key, R=rG - } ).collect(); + t.commit_point(b"sign:pk", public_keys[i].as_compressed()); + t.commit_point(b"sign:R", signatures[i].get_R()); + t.challenge_scalar(b"sign:c") // context, message, A/public_key, R=rG + }) + .collect(); assert!(transcripts.next().is_none(), "{}", ASSERT_MESSAGE); assert!(hrams.len() == public_keys.len(), "{}", ASSERT_MESSAGE); @@ -228,8 +240,7 @@ fn verify_batch_equation( signatures: &[impl HasR], public_keys: &[PublicKey], deduplicate_public_keys: bool, -) -> SignatureResult<()> -{ +) -> SignatureResult<()> { use curve25519_dalek::traits::IsIdentity; use curve25519_dalek::traits::VartimeMultiscalarMul; @@ -240,7 +251,7 @@ fn verify_batch_equation( let Rs = signatures.iter().map(|sig| sig.get_R().decompress()); let mut ppks = Vec::new(); - let As = if ! deduplicate_public_keys { + let As = if !deduplicate_public_keys { // Multiply each H(R || A || M) by the random value for (hram, z) in hrams.iter_mut().zip(zs.iter()) { *hram *= z; @@ -248,37 +259,43 @@ fn verify_batch_equation( public_keys } else { // TODO: Actually deduplicate all if deduplicate_public_keys is set? - ppks.reserve( public_keys.len() ); + ppks.reserve(public_keys.len()); // Multiply each H(R || A || M) by the random value for i in 0..public_keys.len() { let zhram = hrams[i] * zs[i]; let j = ppks.len().checked_sub(1); if j.is_none() || ppks[j.unwrap()] != public_keys[i] { ppks.push(public_keys[i]); - hrams[ppks.len()-1] = zhram; + hrams[ppks.len() - 1] = zhram; } else { - hrams[ppks.len()-1] = hrams[ppks.len()-1] + zhram; + hrams[ppks.len() - 1] = hrams[ppks.len() - 1] + zhram; } } hrams.truncate(ppks.len()); ppks.as_slice() - }.iter().map(|pk| Some(*pk.as_point())); + } + .iter() + .map(|pk| Some(*pk.as_point())); // Compute (-∑ z[i]s[i] (mod l)) B + ∑ z[i]R[i] + ∑ (z[i]H(R||A||M)[i] (mod l)) A[i] = 0 let b = RistrettoPoint::optional_multiscalar_mul( once(-bs).chain(zs.iter().cloned()).chain(hrams), B.chain(Rs).chain(As), - ).map(|id| id.is_identity()).unwrap_or(false); + ) + .map(|id| id.is_identity()) + .unwrap_or(false); // We need not return SignatureError::PointDecompressionError because // the decompression failures occur for R represent invalid signatures. - if b { Ok(()) } else { Err(SignatureError::EquationFalse) } + if b { + Ok(()) + } else { + Err(SignatureError::EquationFalse) + } } - - /// Half-aggregated aka prepared batch signature -/// +/// /// Implementation of "Non-interactive half-aggregation of EdDSA and /// variantsof Schnorr signatures" by Konstantinos Chalkias, /// François Garillot, Yashvanth Kondi, and Valeria Nikolaenko @@ -289,37 +306,33 @@ pub struct PreparedBatch { Rs: Vec, } -impl PreparedBatch{ - +impl PreparedBatch { /// Create a half-aggregated aka prepared batch signature from many other signatures. #[allow(non_snake_case)] - pub fn new( + pub fn new( transcripts: I, signatures: &[Signature], public_keys: &[PublicKey], ) -> PreparedBatch where T: SigningTranscript, - I: IntoIterator, + I: IntoIterator, { - assert!(signatures.len() == public_keys.len(), "{}", ASSERT_MESSAGE); // Check transcripts length below + assert!(signatures.len() == public_keys.len(), "{}", ASSERT_MESSAGE); // Check transcripts length below let (zs, _hrams) = prepare_batch(transcripts, signatures, public_keys, NotAnRng); // Compute the basepoint coefficient, ∑ s[i]z[i] (mod l) - let bs: Scalar = signatures.iter() - .map(|sig| sig.s) - .zip(zs.iter()) - .map(|(s, z)| z * s) - .sum(); + let bs: Scalar = + signatures.iter().map(|sig| sig.s).zip(zs.iter()).map(|(s, z)| z * s).sum(); let Rs = signatures.iter().map(|sig| sig.R).collect(); - PreparedBatch { bs, Rs, } + PreparedBatch { bs, Rs } } /// Verify a half-aggregated aka prepared batch signature #[allow(non_snake_case)] - pub fn verify( + pub fn verify( &self, transcripts: I, public_keys: &[PublicKey], @@ -327,17 +340,19 @@ impl PreparedBatch{ ) -> SignatureResult<()> where T: SigningTranscript, - I: IntoIterator, + I: IntoIterator, { - assert!(self.Rs.len() == public_keys.len(), "{}", ASSERT_MESSAGE); // Check transcripts length below + assert!(self.Rs.len() == public_keys.len(), "{}", ASSERT_MESSAGE); // Check transcripts length below let (zs, hrams) = prepare_batch(transcripts, self.Rs.as_slice(), public_keys, NotAnRng); verify_batch_equation( self.bs, - zs, hrams, + zs, + hrams, self.Rs.as_slice(), - public_keys, deduplicate_public_keys + public_keys, + deduplicate_public_keys, ) } @@ -345,29 +360,29 @@ impl PreparedBatch{ #[allow(non_snake_case)] pub fn read_bytes(&self, mut bytes: &[u8]) -> SignatureResult { use arrayref::array_ref; - if bytes.len() % 32 != 0 || bytes.len() < 64 { + if bytes.len() % 32 != 0 || bytes.len() < 64 { return Err(SignatureError::BytesLengthError { name: "PreparedBatch", description: "A Prepared batched signature", - length: 0 // TODO: Maybe get rid of this silly field? + length: 0, // TODO: Maybe get rid of this silly field? }); } let l = (bytes.len() % 32) - 1; let mut read = || { - let (head,tail) = bytes.split_at(32); + let (head, tail) = bytes.split_at(32); bytes = tail; - *array_ref![head,0,32] + *array_ref![head, 0, 32] }; let mut bs = read(); bs[31] &= 127; - let bs = super::sign::check_scalar(bs) ?; + let bs = super::sign::check_scalar(bs)?; let mut Rs = Vec::with_capacity(l); for _ in 0..l { - Rs.push( CompressedRistretto(read()) ); + Rs.push(CompressedRistretto(read())); } Ok(PreparedBatch { bs, Rs }) } - + /// Returns buffer size required for serialization #[allow(non_snake_case)] pub fn byte_len(&self) -> usize { @@ -377,18 +392,17 @@ impl PreparedBatch{ /// Serializes into exactly sized buffer #[allow(non_snake_case)] pub fn write_bytes(&self, mut bytes: &mut [u8]) { - assert!(bytes.len() == self.byte_len()); - let mut place = |s: &[u8]| reserve_mut(&mut bytes,32).copy_from_slice(s); + assert!(bytes.len() == self.byte_len()); + let mut place = |s: &[u8]| reserve_mut(&mut bytes, 32).copy_from_slice(s); let mut bs = self.bs.to_bytes(); bs[31] |= 128; place(&bs[..]); for R in self.Rs.iter() { place(R.as_bytes()); } - } + } } - pub fn reserve_mut<'heap, T>(heap: &mut &'heap mut [T], len: usize) -> &'heap mut [T] { let tmp: &'heap mut [T] = core::mem::take(&mut *heap); let (reserved, tmp) = tmp.split_at_mut(len); @@ -396,7 +410,6 @@ pub fn reserve_mut<'heap, T>(heap: &mut &'heap mut [T], len: usize) -> &'heap mu reserved } - #[cfg(test)] mod test { #[cfg(feature = "alloc")] @@ -425,28 +438,30 @@ mod test { for i in 0..messages.len() { let mut keypair: Keypair = Keypair::generate_with(&mut csprng); - if i == 3 || i == 4 { keypair = keypairs[0].clone(); } + if i == 3 || i == 4 { + keypair = keypairs[0].clone(); + } signatures.push(keypair.sign(ctx.bytes(messages[i]))); keypairs.push(keypair); } let mut public_keys: Vec = keypairs.iter().map(|key| key.public).collect(); - public_keys.swap(1,2); + public_keys.swap(1, 2); let transcripts = messages.iter().map(|m| ctx.bytes(m)); - assert!( verify_batch(transcripts, &signatures[..], &public_keys[..], false).is_err() ); + assert!(verify_batch(transcripts, &signatures[..], &public_keys[..], false).is_err()); let transcripts = messages.iter().map(|m| ctx.bytes(m)); - assert!( verify_batch(transcripts, &signatures[..], &public_keys[..], true).is_err() ); + assert!(verify_batch(transcripts, &signatures[..], &public_keys[..], true).is_err()); - public_keys.swap(1,2); + public_keys.swap(1, 2); let transcripts = messages.iter().map(|m| ctx.bytes(m)); - assert!( verify_batch(transcripts, &signatures[..], &public_keys[..], false).is_ok() ); + assert!(verify_batch(transcripts, &signatures[..], &public_keys[..], false).is_ok()); let transcripts = messages.iter().map(|m| ctx.bytes(m)); - assert!( verify_batch(transcripts, &signatures[..], &public_keys[..], true).is_ok() ); + assert!(verify_batch(transcripts, &signatures[..], &public_keys[..], true).is_ok()); - signatures.swap(1,2); + signatures.swap(1, 2); let transcripts = messages.iter().map(|m| ctx.bytes(m)); - assert!( verify_batch(transcripts, &signatures[..], &public_keys[..], false).is_err() ); + assert!(verify_batch(transcripts, &signatures[..], &public_keys[..], false).is_err()); let transcripts = messages.iter().map(|m| ctx.bytes(m)); - assert!( verify_batch(transcripts, &signatures[..], &public_keys[..], true).is_err() ); + assert!(verify_batch(transcripts, &signatures[..], &public_keys[..], true).is_err()); } } diff --git a/src/cert.rs b/src/cert.rs index 92bc06f..46e091f 100644 --- a/src/cert.rs +++ b/src/cert.rs @@ -7,7 +7,6 @@ // Authors: // - Jeffrey Burdges - //! ### Adaptor signature-based implicit certificate scheme for Ristretto //! //! [Implicit certificates](https://en.wikipedia.org/wiki/Implicit_certificate) @@ -57,7 +56,6 @@ use curve25519_dalek::scalar::Scalar; use super::*; use crate::context::SigningTranscript; - /// Adaptor Implicit Certificate Secret /// /// Issuing an Adaptor implicit certificate requires producing @@ -93,7 +91,7 @@ impl Keypair { /// Issue an Adaptor implicit certificate /// /// Aside from the issuing `Keypair` supplied as `self`, you provide both - /// (1) a `SigningTranscript` called `t` that incorporates both the context + /// (1) a `SigningTranscript` called `t` that incorporates both the context /// and the certificate requester's identity, and /// (2) the `seed_public_key` supplied by the certificate recipient /// in their certificate request. @@ -101,20 +99,24 @@ impl Keypair { /// certificate requester, ans from which the certificate requester /// derives their certified key pair. pub fn issue_adaptor_cert(&self, mut t: T, seed_public_key: &PublicKey) -> AdaptorCertSecret - where T: SigningTranscript + where + T: SigningTranscript, { t.proto_name(b"Adaptor"); - t.commit_point(b"issuer-pk",self.public.as_compressed()); + t.commit_point(b"issuer-pk", self.public.as_compressed()); // We cannot commit the `seed_public_key` to the transcript // because the whole point is to keep the transcript minimal. // Instead we consume it as witness datathat influences only k. - let k = t.witness_scalar(b"issuing",&[ &self.secret.nonce, seed_public_key.as_compressed().as_bytes() ]); + let k = t.witness_scalar( + b"issuing", + &[&self.secret.nonce, seed_public_key.as_compressed().as_bytes()], + ); // Compute the public key reconstruction data let gamma = seed_public_key.as_point() + &k * constants::RISTRETTO_BASEPOINT_TABLE; let gamma = gamma.compress(); - t.commit_point(b"gamma",&gamma); + t.commit_point(b"gamma", &gamma); let cert_public = AdaptorCertPublic(gamma.0); // Compute the secret key reconstruction data @@ -150,27 +152,28 @@ impl PublicKey { &self, mut t: T, seed_secret_key: &SecretKey, - cert_secret: AdaptorCertSecret + cert_secret: AdaptorCertSecret, ) -> SignatureResult<(AdaptorCertPublic, SecretKey)> - where T: SigningTranscript + where + T: SigningTranscript, { t.proto_name(b"Adaptor"); - t.commit_point(b"issuer-pk",self.as_compressed()); + t.commit_point(b"issuer-pk", self.as_compressed()); // Again we cannot commit much to the transcript, but we again // treat anything relevant as a witness when defining the let mut nonce = [0u8; 32]; - t.witness_bytes(b"accepting",&mut nonce, &[&cert_secret.0[..],&seed_secret_key.nonce]); + t.witness_bytes(b"accepting", &mut nonce, &[&cert_secret.0[..], &seed_secret_key.nonce]); let mut s = [0u8; 32]; s.copy_from_slice(&cert_secret.0[32..64]); - let s = crate::scalar_from_canonical_bytes(s).ok_or(SignatureError::ScalarFormatError) ?; - let cert_public : AdaptorCertPublic = cert_secret.into(); + let s = crate::scalar_from_canonical_bytes(s).ok_or(SignatureError::ScalarFormatError)?; + let cert_public: AdaptorCertPublic = cert_secret.into(); let gamma = CompressedRistretto(cert_public.0); - t.commit_point(b"gamma",&gamma); + t.commit_point(b"gamma", &gamma); let key = s + seed_secret_key.key; - Ok(( cert_public, SecretKey { key, nonce } )) + Ok((cert_public, SecretKey { key, nonce })) } } @@ -191,10 +194,15 @@ impl Keypair { /// only a digest `h` that incorporates any context and metadata /// pertaining to the issued key. pub fn issue_self_adaptor_cert(&self, t: T) -> (AdaptorCertPublic, SecretKey) - where T: SigningTranscript+Clone + where + T: SigningTranscript + Clone, { let mut bytes = [0u8; 96]; - t.witness_bytes(b"issue_self_adaptor_cert", &mut bytes, &[&self.secret.nonce, &self.secret.to_bytes() as &[u8]]); + t.witness_bytes( + b"issue_self_adaptor_cert", + &mut bytes, + &[&self.secret.nonce, &self.secret.to_bytes() as &[u8]], + ); let mut nonce = [0u8; 32]; nonce.copy_from_slice(&bytes[64..96]); @@ -205,25 +213,32 @@ impl Keypair { let seed = SecretKey { key, nonce }.to_keypair(); let cert_secret = self.issue_adaptor_cert(t.clone(), &seed.public); - self.public.accept_adaptor_cert(t, &seed.secret, cert_secret).expect("Cert issued above and known to produce signature errors; qed") + self.public + .accept_adaptor_cert(t, &seed.secret, cert_secret) + .expect("Cert issued above and known to produce signature errors; qed") } } impl PublicKey { /// Extract the certified pulic key from an adaptor certificate - /// + /// /// We've no confirmation that this public key was certified /// until we use it in some authenticated setting, like an AEAD /// or another signature. - pub fn open_adaptor_cert(&self, mut t: T, cert_public: &AdaptorCertPublic) -> SignatureResult - where T: SigningTranscript + pub fn open_adaptor_cert( + &self, + mut t: T, + cert_public: &AdaptorCertPublic, + ) -> SignatureResult + where + T: SigningTranscript, { t.proto_name(b"Adaptor"); - t.commit_point(b"issuer-pk",self.as_compressed()); + t.commit_point(b"issuer-pk", self.as_compressed()); let gamma = CompressedRistretto(cert_public.0); - t.commit_point(b"gamma",&gamma); - let gamma = gamma.decompress().ok_or(SignatureError::PointDecompressionError) ?; + t.commit_point(b"gamma", &gamma); + let gamma = gamma.decompress().ok_or(SignatureError::PointDecompressionError)?; let point = cert_public.derive_e(t) * self.as_point() + gamma; Ok(PublicKey::from_point(point)) @@ -242,8 +257,8 @@ mod tests { let mut csprng = rand_core::OsRng; let issuer = Keypair::generate_with(&mut csprng); - let (cert_public,secret_key) = issuer.issue_self_adaptor_cert(t.clone()); - let public_key = issuer.public.open_adaptor_cert(t,&cert_public).unwrap(); + let (cert_public, secret_key) = issuer.issue_self_adaptor_cert(t.clone()); + let public_key = issuer.public.open_adaptor_cert(t, &cert_public).unwrap(); assert_eq!(secret_key.to_public(), public_key); } } diff --git a/src/context.rs b/src/context.rs index 99fe041..80576a7 100644 --- a/src/context.rs +++ b/src/context.rs @@ -11,17 +11,16 @@ use core::cell::RefCell; -use rand_core::{RngCore,CryptoRng}; +use rand_core::{RngCore, CryptoRng}; use merlin::Transcript; -use curve25519_dalek::digest::{Update,FixedOutput,ExtendableOutput,XofReader}; -use curve25519_dalek::digest::generic_array::typenum::{U32,U64}; +use curve25519_dalek::digest::{Update, FixedOutput, ExtendableOutput, XofReader}; +use curve25519_dalek::digest::generic_array::typenum::{U32, U64}; use curve25519_dalek::ristretto::CompressedRistretto; // RistrettoPoint use curve25519_dalek::scalar::Scalar; - // === Signing context as transcript === // /// Schnorr signing transcript @@ -83,42 +82,63 @@ pub trait SigningTranscript { /// Produce secret witness bytes from the protocol transcript /// and any "nonce seeds" kept with the secret keys. - fn witness_bytes_rng(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], rng: R) - where R: RngCore+CryptoRng; + fn witness_bytes_rng( + &self, + label: &'static [u8], + dest: &mut [u8], + nonce_seeds: &[&[u8]], + rng: R, + ) where + R: RngCore + CryptoRng; } - /// We delegates any mutable reference to its base type, like `&mut Rng` /// or similar to `BorrowMut<..>` do, but doing so here simplifies /// alternative implementations. impl SigningTranscript for &mut T -where T: SigningTranscript + ?Sized, +where + T: SigningTranscript + ?Sized, { #[inline(always)] - fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8]) - { (**self).commit_bytes(label,bytes) } + fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8]) { + (**self).commit_bytes(label, bytes) + } #[inline(always)] - fn proto_name(&mut self, label: &'static [u8]) - { (**self).proto_name(label) } + fn proto_name(&mut self, label: &'static [u8]) { + (**self).proto_name(label) + } #[inline(always)] - fn commit_point(&mut self, label: &'static [u8], compressed: &CompressedRistretto) - { (**self).commit_point(label, compressed) } + fn commit_point(&mut self, label: &'static [u8], compressed: &CompressedRistretto) { + (**self).commit_point(label, compressed) + } #[inline(always)] - fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]) - { (**self).challenge_bytes(label,dest) } + fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]) { + (**self).challenge_bytes(label, dest) + } #[inline(always)] - fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar - { (**self).challenge_scalar(label) } + fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar { + (**self).challenge_scalar(label) + } #[inline(always)] - fn witness_scalar(&self, label: &'static [u8], nonce_seeds: &[&[u8]]) -> Scalar - { (**self).witness_scalar(label,nonce_seeds) } + fn witness_scalar(&self, label: &'static [u8], nonce_seeds: &[&[u8]]) -> Scalar { + (**self).witness_scalar(label, nonce_seeds) + } #[inline(always)] - fn witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]]) - { (**self).witness_bytes(label,dest,nonce_seeds) } + fn witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]]) { + (**self).witness_bytes(label, dest, nonce_seeds) + } #[inline(always)] - fn witness_bytes_rng(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], rng: R) - where R: RngCore+CryptoRng - { (**self).witness_bytes_rng(label,dest,nonce_seeds,rng) } + fn witness_bytes_rng( + &self, + label: &'static [u8], + dest: &mut [u8], + nonce_seeds: &[&[u8]], + rng: R, + ) where + R: RngCore + CryptoRng, + { + (**self).witness_bytes_rng(label, dest, nonce_seeds, rng) + } } /// We delegate `SigningTranscript` methods to the corresponding @@ -134,8 +154,14 @@ impl SigningTranscript for Transcript { Transcript::challenge_bytes(self, label, dest) } - fn witness_bytes_rng(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], mut rng: R) - where R: RngCore+CryptoRng + fn witness_bytes_rng( + &self, + label: &'static [u8], + dest: &mut [u8], + nonce_seeds: &[&[u8]], + mut rng: R, + ) where + R: RngCore + CryptoRng, { let mut br = self.build_rng(); for ns in nonce_seeds { @@ -146,7 +172,6 @@ impl SigningTranscript for Transcript { } } - /// Schnorr signing context /// /// We expect users to have separate `SigningContext`s for each role @@ -169,7 +194,7 @@ pub struct SigningContext(Transcript); /// Initialize a signing context from a static byte string that /// identifies the signature's role in the larger protocol. #[inline(always)] -pub fn signing_context(context : &[u8]) -> SigningContext { +pub fn signing_context(context: &[u8]) -> SigningContext { SigningContext::new(context) } @@ -177,9 +202,9 @@ impl SigningContext { /// Initialize a signing context from a static byte string that /// identifies the signature's role in the larger protocol. #[inline(always)] - pub fn new(context : &[u8]) -> SigningContext { + pub fn new(context: &[u8]) -> SigningContext { let mut t = Transcript::new(b"SigningContext"); - t.append_message(b"",context); + t.append_message(b"", context); SigningContext(t) } @@ -210,7 +235,7 @@ impl SigningContext { /// Initialize an owned signing transcript on a message provided as /// a hash function with 256 bit output. #[inline(always)] - pub fn hash256>(&self, h: D) -> Transcript { + pub fn hash256>(&self, h: D) -> Transcript { let mut prehash = [0u8; 32]; prehash.copy_from_slice(h.finalize_fixed().as_slice()); let mut t = self.0.clone(); @@ -221,7 +246,7 @@ impl SigningContext { /// Initialize an owned signing transcript on a message provided as /// a hash function with 512 bit output, usually a gross over kill. #[inline(always)] - pub fn hash512>(&self, h: D) -> Transcript { + pub fn hash512>(&self, h: D) -> Transcript { let mut prehash = [0u8; 64]; prehash.copy_from_slice(h.finalize_fixed().as_slice()); let mut t = self.0.clone(); @@ -230,7 +255,6 @@ impl SigningContext { } } - /// Very simple transcript construction from a modern hash function. /// /// We provide this transcript type to directly use conventional hash @@ -257,7 +281,8 @@ impl SigningContext { /// domain separation provided by our methods. We do this to make /// `&mut XoFTranscript : SigningTranscript` safe. pub struct XoFTranscript(H) -where H: Update + ExtendableOutput + Clone; +where + H: Update + ExtendableOutput + Clone; fn input_bytes(h: &mut H, bytes: &[u8]) { let l = bytes.len() as u64; @@ -266,7 +291,8 @@ fn input_bytes(h: &mut H, bytes: &[u8]) { } impl XoFTranscript -where H: Update + ExtendableOutput + Clone +where + H: Update + ExtendableOutput + Clone, { /// Create a `XoFTranscript` from a conventional hash functions with an extensible output mode. /// @@ -274,18 +300,24 @@ where H: Update + ExtendableOutput + Clone /// provided, so that our domain separation works correctly even /// when using `&mut XoFTranscript : SigningTranscript`. #[inline(always)] - pub fn new(h: H) -> XoFTranscript { XoFTranscript(h) } + pub fn new(h: H) -> XoFTranscript { + XoFTranscript(h) + } } impl From for XoFTranscript -where H: Update + ExtendableOutput + Clone +where + H: Update + ExtendableOutput + Clone, { #[inline(always)] - fn from(h: H) -> XoFTranscript { XoFTranscript(h) } + fn from(h: H) -> XoFTranscript { + XoFTranscript(h) + } } impl SigningTranscript for XoFTranscript -where H: Update + ExtendableOutput + Clone +where + H: Update + ExtendableOutput + Clone, { fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8]) { self.0.update(b"co"); @@ -301,8 +333,14 @@ where H: Update + ExtendableOutput + Clone self.0.clone().chain(b"xof").finalize_xof().read(dest); } - fn witness_bytes_rng(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], mut rng: R) - where R: RngCore+CryptoRng + fn witness_bytes_rng( + &self, + label: &'static [u8], + dest: &mut [u8], + nonce_seeds: &[&[u8]], + mut rng: R, + ) where + R: RngCore + CryptoRng, { let mut h = self.0.clone().chain(b"wb"); input_bytes(&mut h, label); @@ -319,7 +357,6 @@ where H: Update + ExtendableOutput + Clone } } - /// Schnorr signing transcript with the default `ThreadRng` replaced /// by an arbitrary `CryptoRng`. /// @@ -334,29 +371,43 @@ where H: Update + ExtendableOutput + Clone /// produces secure signatures, we recommend against doing this in /// production because we implement protocols like multi-signatures /// which likely become vulnerable when derandomized. -pub struct SigningTranscriptWithRng -where T: SigningTranscript, R: RngCore+CryptoRng +pub struct SigningTranscriptWithRng +where + T: SigningTranscript, + R: RngCore + CryptoRng, { t: T, rng: RefCell, } -impl SigningTranscript for SigningTranscriptWithRng -where T: SigningTranscript, R: RngCore+CryptoRng +impl SigningTranscript for SigningTranscriptWithRng +where + T: SigningTranscript, + R: RngCore + CryptoRng, { - fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8]) - { self.t.commit_bytes(label, bytes) } - - fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]) - { self.t.challenge_bytes(label, dest) } + fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8]) { + self.t.commit_bytes(label, bytes) + } - fn witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]]) - { self.witness_bytes_rng(label, dest, nonce_seeds, &mut *self.rng.borrow_mut()) } + fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]) { + self.t.challenge_bytes(label, dest) + } - fn witness_bytes_rng(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], rng: RR) - where RR: RngCore+CryptoRng - { self.t.witness_bytes_rng(label,dest,nonce_seeds,rng) } + fn witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]]) { + self.witness_bytes_rng(label, dest, nonce_seeds, &mut *self.rng.borrow_mut()) + } + fn witness_bytes_rng( + &self, + label: &'static [u8], + dest: &mut [u8], + nonce_seeds: &[&[u8]], + rng: RR, + ) where + RR: RngCore + CryptoRng, + { + self.t.witness_bytes_rng(label, dest, nonce_seeds, rng) + } } /// Attach a `CryptoRng` to a `SigningTranscript` to replace the default `ThreadRng`. @@ -366,12 +417,12 @@ where T: SigningTranscript, R: RngCore+CryptoRng /// however because, although such derandomization produces secure Schnorr /// signatures, we do implement protocols here like multi-signatures which /// likely become vulnerable when derandomized. -pub fn attach_rng(t: T, rng: R) -> SigningTranscriptWithRng -where T: SigningTranscript, R: RngCore+CryptoRng +pub fn attach_rng(t: T, rng: R) -> SigningTranscriptWithRng +where + T: SigningTranscript, + R: RngCore + CryptoRng, { - SigningTranscriptWithRng { - t, rng: RefCell::new(rng) - } + SigningTranscriptWithRng { t, rng: RefCell::new(rng) } } #[cfg(feature = "rand_chacha")] @@ -379,9 +430,10 @@ use rand_chacha::ChaChaRng; /// Attach a `ChaChaRng` to a `Transcript` to repalce the default `ThreadRng` #[cfg(feature = "rand_chacha")] -pub fn attach_chacharng(t: T, seed: [u8; 32]) -> SigningTranscriptWithRng -where T: SigningTranscript +pub fn attach_chacharng(t: T, seed: [u8; 32]) -> SigningTranscriptWithRng +where + T: SigningTranscript, { use rand_core::SeedableRng; - attach_rng(t,ChaChaRng::from_seed(seed)) + attach_rng(t, ChaChaRng::from_seed(seed)) } diff --git a/src/derive.rs b/src/derive.rs index f73242f..a2dc104 100644 --- a/src/derive.rs +++ b/src/derive.rs @@ -7,23 +7,23 @@ // Authors: // - Jeffrey Burdges -//! ### Implementation of "hierarchical deterministic key derivation" (HDKD) for Schnorr signatures on Ristretto -//! +//! ### Implementation of "hierarchical deterministic key derivation" (HDKD) for Schnorr signatures on Ristretto +//! //! *Warning* We warn that our VRF construction in vrf.rs supports //! malleable VRF outputs via the `Malleable` type, which becomes //! insecure when used in conjunction with our hierarchical key //! derivation methods here. -//! Attackers could translate malleable VRF outputs from one soft subkey +//! Attackers could translate malleable VRF outputs from one soft subkey //! to another soft subkey, gaining early knowledge of the VRF output. //! We think most VRF applications for which HDKH sounds suitable //! benefit from using implicit certificates instead of HDKD anyways, //! which should also be secure in combination with HDKH. //! We always use non-malleable VRF inputs in our convenience methods. -//! We suggest using implicit certificates instead of HDKD when +//! We suggest using implicit certificates instead of HDKD when //! using VRFs. //! -//! +//! // use curve25519_dalek::digest::generic_array::typenum::U64; // use curve25519_dalek::digest::Digest; @@ -45,19 +45,20 @@ pub const CHAIN_CODE_LENGTH: usize = 32; /// chain codes fill this gap by being a high entropy secret shared /// between public and private key holders. These are produced by /// key derivations and can be incorporated into subsequence key -/// derivations. +/// derivations. /// See https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#extended-keys #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct ChainCode(pub [u8; CHAIN_CODE_LENGTH]); /// Key types that support "hierarchical deterministic" key derivation -pub trait Derivation : Sized { +pub trait Derivation: Sized { /// Derive key with subkey identified by a byte array /// presented via a `SigningTranscript`, and a chain code. fn derived_key(&self, t: T, cc: ChainCode) -> (Self, ChainCode) - where T: SigningTranscript; + where + T: SigningTranscript; - /// Derive key with subkey identified by a byte array + /// Derive key with subkey identified by a byte array /// and a chain code. We do not include a context here /// because the chain code could serve this purpose. fn derived_key_simple>(&self, cc: ChainCode, i: B) -> (Self, ChainCode) { @@ -68,12 +69,14 @@ pub trait Derivation : Sized { /// Derive key with subkey identified by a byte array /// and a chain code, and with external randomnesses. - fn derived_key_simple_rng(&self, cc: ChainCode, i: B, rng: R) -> (Self, ChainCode) - where B: AsRef<[u8]>, R: RngCore+CryptoRng + fn derived_key_simple_rng(&self, cc: ChainCode, i: B, rng: R) -> (Self, ChainCode) + where + B: AsRef<[u8]>, + R: RngCore + CryptoRng, { let mut t = merlin::Transcript::new(b"SchnorrRistrettoHDKD"); t.append_message(b"sign-bytes", i.as_ref()); - self.derived_key(super::context::attach_rng(t,rng), cc) + self.derived_key(super::context::attach_rng(t, rng), cc) } } @@ -87,10 +90,11 @@ impl PublicKey { /// /// We update the signing transcript as a side effect. fn derive_scalar_and_chaincode(&self, t: &mut T, cc: ChainCode) -> (Scalar, ChainCode) - where T: SigningTranscript + where + T: SigningTranscript, { - t.commit_bytes(b"chain-code",&cc.0); - t.commit_point(b"public-key",self.as_compressed()); + t.commit_bytes(b"chain-code", &cc.0); + t.commit_point(b"public-key", self.as_compressed()); let scalar = t.challenge_scalar(b"HDKD-scalar"); @@ -115,17 +119,21 @@ impl SecretKey { /// permissible mutations of `SecretKey`. This means only that /// we hash the `SecretKey`'s scalar, but not its nonce because /// the secret key remains valid if the nonce is changed. - pub fn hard_derive_mini_secret_key>(&self, cc: Option, i: B) - -> (MiniSecretKey,ChainCode) - { + pub fn hard_derive_mini_secret_key>( + &self, + cc: Option, + i: B, + ) -> (MiniSecretKey, ChainCode) { let mut t = merlin::Transcript::new(b"SchnorrRistrettoHDKD"); t.append_message(b"sign-bytes", i.as_ref()); - if let Some(c) = cc { t.append_message(b"chain-code", &c.0); } - t.append_message(b"secret-key",& self.key.to_bytes() as &[u8]); + if let Some(c) = cc { + t.append_message(b"chain-code", &c.0); + } + t.append_message(b"secret-key", &self.key.to_bytes() as &[u8]); - let mut msk = [0u8; MINI_SECRET_KEY_LENGTH]; - t.challenge_bytes(b"HDKD-hard",&mut msk); + let mut msk = [0u8; MINI_SECRET_KEY_LENGTH]; + t.challenge_bytes(b"HDKD-hard", &mut msk); let mut chaincode = [0u8; 32]; t.challenge_bytes(b"HDKD-chaincode", &mut chaincode); @@ -148,10 +156,13 @@ impl MiniSecretKey { /// permissible mutations of `SecretKey`. This means only that /// we hash the `SecretKey`'s scalar, but not its nonce because /// the secret key remains valid if the nonce is changed. - pub fn hard_derive_mini_secret_key>(&self, cc: Option, i: B, mode: ExpansionMode) - -> (MiniSecretKey,ChainCode) - { - self.expand(mode).hard_derive_mini_secret_key(cc,i) + pub fn hard_derive_mini_secret_key>( + &self, + cc: Option, + i: B, + mode: ExpansionMode, + ) -> (MiniSecretKey, ChainCode) { + self.expand(mode).hard_derive_mini_secret_key(cc, i) } } @@ -169,9 +180,12 @@ impl Keypair { /// permissible mutations of `SecretKey`. This means only that /// we hash the `SecretKey`'s scalar, but not its nonce because /// the secret key remains valid if the nonce is changed. - pub fn hard_derive_mini_secret_key>(&self, cc: Option, i: B) - -> (MiniSecretKey,ChainCode) { - self.secret.hard_derive_mini_secret_key(cc,i) + pub fn hard_derive_mini_secret_key>( + &self, + cc: Option, + i: B, + ) -> (MiniSecretKey, ChainCode) { + self.secret.hard_derive_mini_secret_key(cc, i) } /// Derive a secret key and new chain code from a key pair and chain code. @@ -179,7 +193,8 @@ impl Keypair { /// We expect the trait methods of `Keypair as Derivation` to be /// more useful since signing anything requires the public key too. pub fn derive_secret_key(&self, mut t: T, cc: ChainCode) -> (SecretKey, ChainCode) - where T: SigningTranscript + where + T: SigningTranscript, { let (scalar, chaincode) = self.public.derive_scalar_and_chaincode(&mut t, cc); @@ -190,18 +205,20 @@ impl Keypair { // We employ the witness mechanism here so that CSPRNG associated to our // `SigningTranscript` makes our new nonce seed independent from everything. let mut nonce = [0u8; 32]; - t.witness_bytes(b"HDKD-nonce", &mut nonce, &[&self.secret.nonce, &self.secret.to_bytes() as &[u8]]); + t.witness_bytes( + b"HDKD-nonce", + &mut nonce, + &[&self.secret.nonce, &self.secret.to_bytes() as &[u8]], + ); - (SecretKey { - key: self.secret.key + scalar, - nonce, - }, chaincode) + (SecretKey { key: self.secret.key + scalar, nonce }, chaincode) } } impl Derivation for Keypair { fn derived_key(&self, t: T, cc: ChainCode) -> (Keypair, ChainCode) - where T: SigningTranscript + where + T: SigningTranscript, { let (secret, chaincode) = self.derive_secret_key(t, cc); let public = secret.to_public(); @@ -211,7 +228,8 @@ impl Derivation for Keypair { impl Derivation for SecretKey { fn derived_key(&self, t: T, cc: ChainCode) -> (SecretKey, ChainCode) - where T: SigningTranscript + where + T: SigningTranscript, { self.clone().to_keypair().derive_secret_key(t, cc) } @@ -219,7 +237,8 @@ impl Derivation for SecretKey { impl Derivation for PublicKey { fn derived_key(&self, mut t: T, cc: ChainCode) -> (PublicKey, ChainCode) - where T: SigningTranscript + where + T: SigningTranscript, { let (scalar, chaincode) = self.derive_scalar_and_chaincode(&mut t, cc); let point = self.as_point() + (&scalar * constants::RISTRETTO_BASEPOINT_TABLE); @@ -246,16 +265,16 @@ impl ExtendedKey { /// Derive key with subkey identified by a byte array /// presented as a hash, and a chain code. pub fn derived_key(&self, t: T) -> ExtendedKey - where T: SigningTranscript + where + T: SigningTranscript, { let (key, chaincode) = self.key.derived_key(t, self.chaincode); ExtendedKey { key, chaincode } } - /// Derive key with subkey identified by a byte array and + /// Derive key with subkey identified by a byte array and /// a chain code in the extended key. - pub fn derived_key_simple>(&self, i: B) -> ExtendedKey - { + pub fn derived_key_simple>(&self, i: B) -> ExtendedKey { let (key, chaincode) = self.key.derived_key_simple(self.chaincode, i); ExtendedKey { key, chaincode } } @@ -275,10 +294,12 @@ impl ExtendedKey { /// permissible mutations of `SecretKey`. This means only that /// we hash the `SecretKey`'s scalar, but not its nonce because /// the secret key remains valid if the nonce is changed. - pub fn hard_derive_mini_secret_key>(&self, i: B, mode: ExpansionMode) - -> ExtendedKey - { - let (key,chaincode) = self.key.hard_derive_mini_secret_key(Some(self.chaincode), i); + pub fn hard_derive_mini_secret_key>( + &self, + i: B, + mode: ExpansionMode, + ) -> ExtendedKey { + let (key, chaincode) = self.key.hard_derive_mini_secret_key(Some(self.chaincode), i); let key = key.expand(mode); ExtendedKey { key, chaincode } } @@ -295,17 +316,14 @@ mod tests { #[test] fn derive_key_public_vs_private_paths() { let chaincode = ChainCode([0u8; CHAIN_CODE_LENGTH]); - let msg : &'static [u8] = b"Just some test message!"; + let msg: &'static [u8] = b"Just some test message!"; let mut h = Shake128::default().chain(msg); let mut csprng = rand_core::OsRng; let key = Keypair::generate_with(&mut csprng); - let mut extended_public_key = ExtendedKey { - key: key.public.clone(), - chaincode, - }; - let mut extended_keypair = ExtendedKey { key, chaincode, }; + let mut extended_public_key = ExtendedKey { key: key.public.clone(), chaincode }; + let mut extended_keypair = ExtendedKey { key, chaincode }; let ctx = signing_context(b"testing testing 1 2 3"); @@ -335,11 +353,11 @@ mod tests { "Verification of a valid signature failed!" ); assert!( - ! extended_public_key.key.verify(ctx.xof(h.clone()), &bad_sig).is_ok(), + !extended_public_key.key.verify(ctx.xof(h.clone()), &bad_sig).is_ok(), "Verification of a signature on a different message passed!" ); assert!( - ! extended_public_key.key.verify(ctx.xof(h_bad), &good_sig).is_ok(), + !extended_public_key.key.verify(ctx.xof(h_bad), &good_sig).is_ok(), "Verification of a signature on a different message passed!" ); } diff --git a/src/errors.rs b/src/errors.rs index 8a5c879..35189b2 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -121,26 +121,20 @@ impl Display for SignatureError { InvalidKey => write!(f, "The provided key is not valid"), BytesLengthError { name, length, .. } => { write!(f, "{name} must be {length} bytes in length") - } + }, NotMarkedSchnorrkel => { write!(f, "Signature bytes not marked as a schnorrkel signature") - } + }, MuSigAbsent { musig_stage } => { write!(f, "Absent {musig_stage} violated multi-signature protocol") - } - MuSigInconsistent { - musig_stage, - duplicate, - } => { + }, + MuSigInconsistent { musig_stage, duplicate } => { if duplicate { write!(f, "Inconsistent duplicate {musig_stage} in multi-signature") } else { - write!( - f, - "Inconsistent {musig_stage} violated multi-signature protocol" - ) + write!(f, "Inconsistent {musig_stage} violated multi-signature protocol") } - } + }, } } } diff --git a/src/keys.rs b/src/keys.rs index cfba66f..52385d2 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -13,19 +13,18 @@ use core::convert::AsRef; use core::fmt::{Debug}; -use rand_core::{RngCore,CryptoRng}; +use rand_core::{RngCore, CryptoRng}; use curve25519_dalek::constants; -use curve25519_dalek::ristretto::{CompressedRistretto,RistrettoPoint}; +use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use curve25519_dalek::scalar::Scalar; -use subtle::{Choice,ConstantTimeEq}; +use subtle::{Choice, ConstantTimeEq}; use zeroize::Zeroize; use crate::scalars; use crate::points::RistrettoBoth; -use crate::errors::{SignatureError,SignatureResult}; - +use crate::errors::{SignatureError, SignatureResult}; /// The length of a Ristretto Schnorr `MiniSecretKey`, in bytes. pub const MINI_SECRET_KEY_LENGTH: usize = 32; @@ -45,7 +44,6 @@ pub const SECRET_KEY_LENGTH: usize = SECRET_KEY_KEY_LENGTH + SECRET_KEY_NONCE_LE /// The length of an Ristretto Schnorr `Keypair`, in bytes. pub const KEYPAIR_LENGTH: usize = SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH; - /// Methods for expanding a `MiniSecretKey` into a `SecretKey`. /// /// Our `SecretKey`s consist of a scalar and nonce seed, both 32 bytes, @@ -94,9 +92,9 @@ pub enum ExpansionMode { /// homomorphic properties unavailable from these seeds, so we renamed /// these and reserve `SecretKey` for what EdDSA calls an extended /// secret key. -#[derive(Clone,Zeroize)] +#[derive(Clone, Zeroize)] #[zeroize(drop)] -pub struct MiniSecretKey(pub (crate) [u8; MINI_SECRET_KEY_LENGTH]); +pub struct MiniSecretKey(pub(crate) [u8; MINI_SECRET_KEY_LENGTH]); impl Debug for MiniSecretKey { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { @@ -117,13 +115,13 @@ impl ConstantTimeEq for MiniSecretKey { } impl MiniSecretKey { - const DESCRIPTION : &'static str = "Analogous to ed25519 secret key as 32 bytes, see RFC8032."; + const DESCRIPTION: &'static str = "Analogous to ed25519 secret key as 32 bytes, see RFC8032."; /// Avoids importing `ExpansionMode` - pub const UNIFORM_MODE : ExpansionMode = ExpansionMode::Uniform; + pub const UNIFORM_MODE: ExpansionMode = ExpansionMode::Uniform; /// Avoids importing `ExpansionMode` - pub const ED25519_MODE : ExpansionMode = ExpansionMode::Ed25519; + pub const ED25519_MODE: ExpansionMode = ExpansionMode::Ed25519; /// Expand this `MiniSecretKey` into a `SecretKey` /// @@ -181,7 +179,10 @@ impl MiniSecretKey { /// # } /// ``` fn expand_ed25519(&self) -> SecretKey { - use sha2::{Sha512, digest::{Update,FixedOutput}}; + use sha2::{ + Sha512, + digest::{Update, FixedOutput}, + }; let mut h = Sha512::default(); h.update(self.as_bytes()); @@ -191,9 +192,9 @@ impl MiniSecretKey { // we do so to improve Ed25519 comparability. let mut key = [0u8; 32]; key.copy_from_slice(&r.as_slice()[0..32]); - key[0] &= 248; - key[31] &= 63; - key[31] |= 64; + key[0] &= 248; + key[31] &= 63; + key[31] |= 64; // We then divide by the cofactor to internally keep a clean // representation mod l. scalars::divide_scalar_bytes_by_cofactor(&mut key); @@ -204,7 +205,7 @@ impl MiniSecretKey { let mut nonce = [0u8; 32]; nonce.copy_from_slice(&r.as_slice()[32..64]); - SecretKey{ key, nonce } + SecretKey { key, nonce } } /// Derive the `SecretKey` corresponding to this `MiniSecretKey`. @@ -282,7 +283,7 @@ impl MiniSecretKey { return Err(SignatureError::BytesLengthError { name: "MiniSecretKey", description: MiniSecretKey::DESCRIPTION, - length: MINI_SECRET_KEY_LENGTH + length: MINI_SECRET_KEY_LENGTH, }); } let mut bits: [u8; 32] = [0u8; 32]; @@ -305,7 +306,8 @@ impl MiniSecretKey { /// /// A CSPRNG with a `fill_bytes()` method, e.g. `rand_chacha::ChaChaRng` pub fn generate_with(mut csprng: R) -> MiniSecretKey - where R: CryptoRng + RngCore, + where + R: CryptoRng + RngCore, { let mut sk: MiniSecretKey = MiniSecretKey([0u8; 32]); csprng.fill_bytes(&mut sk.0); @@ -342,7 +344,6 @@ impl MiniSecretKey { serde_boilerplate!(MiniSecretKey); - /// A secret key for use with Ristretto Schnorr signatures. /// /// Internally, these consist of a scalar mod l along with a seed for @@ -356,16 +357,16 @@ serde_boilerplate!(MiniSecretKey); /// We do not however attempt to keep the scalar's high bit set, especially /// not during hierarchical deterministic key derivations, so some Ed25519 /// libraries might compute the public key incorrectly from our secret key. -#[derive(Clone,Zeroize)] +#[derive(Clone, Zeroize)] #[zeroize(drop)] pub struct SecretKey { /// Actual public key represented as a scalar. - pub (crate) key: Scalar, + pub(crate) key: Scalar, /// Seed for deriving the nonces used in signing. /// /// We require this be random and secret or else key compromise attacks will ensue. /// Any modification here may disrupt some non-public key derivation techniques. - pub (crate) nonce: [u8; 32], + pub(crate) nonce: [u8; 32], } impl Debug for SecretKey { @@ -409,7 +410,8 @@ impl From<&MiniSecretKey> for SecretKey { */ impl SecretKey { - const DESCRIPTION : &'static str = "An ed25519-like expanded secret key as 64 bytes, as specified in RFC8032."; + const DESCRIPTION: &'static str = + "An ed25519-like expanded secret key as 64 bytes, as specified in RFC8032."; /// Convert this `SecretKey` into an array of 64 bytes with. /// @@ -461,7 +463,7 @@ impl SecretKey { #[inline] pub fn from_bytes(bytes: &[u8]) -> SignatureResult { if bytes.len() != SECRET_KEY_LENGTH { - return Err(SignatureError::BytesLengthError{ + return Err(SignatureError::BytesLengthError { name: "SecretKey", description: SecretKey::DESCRIPTION, length: SECRET_KEY_LENGTH, @@ -470,12 +472,13 @@ impl SecretKey { let mut key: [u8; 32] = [0u8; 32]; key.copy_from_slice(&bytes[00..32]); - let key = crate::scalar_from_canonical_bytes(key).ok_or(SignatureError::ScalarFormatError) ?; + let key = + crate::scalar_from_canonical_bytes(key).ok_or(SignatureError::ScalarFormatError)?; let mut nonce: [u8; 32] = [0u8; 32]; nonce.copy_from_slice(&bytes[32..64]); - Ok(SecretKey{ key, nonce }) + Ok(SecretKey { key, nonce }) } /// Convert this `SecretKey` into an array of 64 bytes, corresponding to @@ -496,7 +499,7 @@ impl SecretKey { bytes } - /* Unused tooling removed to reduce dependencies. + /* Unused tooling removed to reduce dependencies. /// Convert this `SecretKey` into an Ed25519 expanded secret key. #[cfg(feature = "ed25519_dalek")] pub fn to_ed25519_expanded_secret_key(&self) -> ed25519_dalek::ExpandedSecretKey { @@ -522,7 +525,7 @@ impl SecretKey { #[inline] pub fn from_ed25519_bytes(bytes: &[u8]) -> SignatureResult { if bytes.len() != SECRET_KEY_LENGTH { - return Err(SignatureError::BytesLengthError{ + return Err(SignatureError::BytesLengthError { name: "SecretKey", description: SecretKey::DESCRIPTION, length: SECRET_KEY_LENGTH, @@ -545,14 +548,15 @@ impl SecretKey { let mut nonce: [u8; 32] = [0u8; 32]; nonce.copy_from_slice(&bytes[32..64]); - Ok(SecretKey{ key, nonce }) + Ok(SecretKey { key, nonce }) } /// Generate an "unbiased" `SecretKey` directly from a user /// suplied `csprng` uniformly, bypassing the `MiniSecretKey` /// layer. pub fn generate_with(mut csprng: R) -> SecretKey - where R: CryptoRng + RngCore, + where + R: CryptoRng + RngCore, { let mut key: [u8; 64] = [0u8; 64]; csprng.fill_bytes(&mut key); @@ -583,7 +587,6 @@ impl SecretKey { serde_boilerplate!(SecretKey); - /// A Ristretto Schnorr public key. /// /// Internally, these are represented as a `RistrettoPoint`, meaning @@ -593,7 +596,7 @@ serde_boilerplate!(SecretKey); /// during deserialization, which improves error handling, but costs /// a compression during signing and verification. #[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PublicKey(pub (crate) RistrettoBoth); +pub struct PublicKey(pub(crate) RistrettoBoth); impl Debug for PublicKey { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { @@ -623,24 +626,33 @@ impl AsRef<[u8]> for PublicKey { } impl PublicKey { - const DESCRIPTION : &'static str = "A Ristretto Schnorr public key represented as a 32-byte Ristretto compressed point"; + const DESCRIPTION: &'static str = + "A Ristretto Schnorr public key represented as a 32-byte Ristretto compressed point"; /// Access the compressed Ristretto form - pub fn as_compressed(&self) -> &CompressedRistretto { self.0.as_compressed() } + pub fn as_compressed(&self) -> &CompressedRistretto { + self.0.as_compressed() + } /// Extract the compressed Ristretto form - pub fn into_compressed(self) -> CompressedRistretto { self.0.into_compressed() } + pub fn into_compressed(self) -> CompressedRistretto { + self.0.into_compressed() + } /// Access the point form - pub fn as_point(&self) -> &RistrettoPoint { self.0.as_point() } + pub fn as_point(&self) -> &RistrettoPoint { + self.0.as_point() + } /// Extract the point form - pub fn into_point(self) -> RistrettoPoint { self.0.into_point() } + pub fn into_point(self) -> RistrettoPoint { + self.0.into_point() + } /// Decompress into the `PublicKey` format that also retains the /// compressed form. pub fn from_compressed(compressed: CompressedRistretto) -> SignatureResult { - Ok(PublicKey(RistrettoBoth::from_compressed(compressed) ?)) + Ok(PublicKey(RistrettoBoth::from_compressed(compressed)?)) } /// Compress into the `PublicKey` format that also retains the @@ -691,7 +703,7 @@ impl PublicKey { /// is an `SignatureError` describing the error that occurred. #[inline] pub fn from_bytes(bytes: &[u8]) -> SignatureResult { - Ok(PublicKey(RistrettoBoth::from_bytes_ser("PublicKey",PublicKey::DESCRIPTION,bytes) ?)) + Ok(PublicKey(RistrettoBoth::from_bytes_ser("PublicKey", PublicKey::DESCRIPTION, bytes)?)) } } @@ -703,9 +715,8 @@ impl From for PublicKey { serde_boilerplate!(PublicKey); - /// A Ristretto Schnorr keypair. -#[derive(Clone,Debug)] +#[derive(Clone, Debug)] // #[derive(Clone,Zeroize)] // #[zeroize(drop)] pub struct Keypair { @@ -729,12 +740,12 @@ impl Drop for Keypair { impl From for Keypair { fn from(secret: SecretKey) -> Keypair { let public = secret.to_public(); - Keypair{ secret, public } + Keypair { secret, public } } } impl Keypair { - const DESCRIPTION : &'static str = "A 96 bytes Ristretto Schnorr keypair"; + const DESCRIPTION: &'static str = "A 96 bytes Ristretto Schnorr keypair"; /* const DESCRIPTION_LONG : &'static str = "An ristretto schnorr keypair, 96 bytes in total, where the \ @@ -768,8 +779,8 @@ impl Keypair { pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] { let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH]; - bytes[..SECRET_KEY_LENGTH].copy_from_slice(& self.secret.to_bytes()); - bytes[SECRET_KEY_LENGTH..].copy_from_slice(& self.public.to_bytes()); + bytes[..SECRET_KEY_LENGTH].copy_from_slice(&self.secret.to_bytes()); + bytes[SECRET_KEY_LENGTH..].copy_from_slice(&self.public.to_bytes()); bytes } @@ -802,13 +813,13 @@ impl Keypair { return Err(SignatureError::BytesLengthError { name: "Keypair", description: Keypair::DESCRIPTION, - length: KEYPAIR_LENGTH + length: KEYPAIR_LENGTH, }); } - let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH]) ?; - let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..]) ?; + let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH])?; + let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?; - Ok(Keypair{ secret, public }) + Ok(Keypair { secret, public }) } /// Serialize `Keypair` to bytes with Ed25519 secret key format. @@ -823,8 +834,8 @@ impl Keypair { pub fn to_half_ed25519_bytes(&self) -> [u8; KEYPAIR_LENGTH] { let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH]; - bytes[..SECRET_KEY_LENGTH].copy_from_slice(& self.secret.to_ed25519_bytes()); - bytes[SECRET_KEY_LENGTH..].copy_from_slice(& self.public.to_bytes()); + bytes[..SECRET_KEY_LENGTH].copy_from_slice(&self.secret.to_ed25519_bytes()); + bytes[SECRET_KEY_LENGTH..].copy_from_slice(&self.public.to_bytes()); bytes } @@ -855,13 +866,13 @@ impl Keypair { return Err(SignatureError::BytesLengthError { name: "Keypair", description: Keypair::DESCRIPTION, - length: KEYPAIR_LENGTH + length: KEYPAIR_LENGTH, }); } - let secret = SecretKey::from_ed25519_bytes(&bytes[..SECRET_KEY_LENGTH]) ?; - let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..]) ?; + let secret = SecretKey::from_ed25519_bytes(&bytes[..SECRET_KEY_LENGTH])?; + let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?; - Ok(Keypair{ secret, public }) + Ok(Keypair { secret, public }) } /// Generate a Ristretto Schnorr `Keypair` directly, @@ -891,12 +902,13 @@ impl Keypair { /// so our secret keys do not satisfy the high bit "clamping" /// imposed on Ed25519 keys. pub fn generate_with(csprng: R) -> Keypair - where R: CryptoRng + RngCore, + where + R: CryptoRng + RngCore, { let secret: SecretKey = SecretKey::generate_with(csprng); let public: PublicKey = secret.to_public(); - Keypair{ public, secret } + Keypair { public, secret } } /// Generate a Ristretto Schnorr `Keypair` directly, from a user @@ -909,7 +921,6 @@ impl Keypair { serde_boilerplate!(Keypair); - #[cfg(test)] mod test { // use std::vec::Vec; @@ -954,14 +965,8 @@ mod test { #[test] fn derives_from_core() { let pk_d = PublicKey::default(); - debug_assert_eq!( - pk_d.as_point().compress(), - CompressedRistretto::default() - ); - debug_assert_eq!( - pk_d.as_compressed().decompress().unwrap(), - RistrettoPoint::default() - ); + debug_assert_eq!(pk_d.as_point().compress(), CompressedRistretto::default()); + debug_assert_eq!(pk_d.as_compressed().decompress().unwrap(), RistrettoPoint::default()); } #[cfg(feature = "getrandom")] @@ -977,9 +982,7 @@ mod test { use core::mem; use core::slice; - unsafe { - slice::from_raw_parts(x as *const T as *const u8, mem::size_of_val(x)) - } + unsafe { slice::from_raw_parts(x as *const T as *const u8, mem::size_of_val(x)) } } assert!(!as_bytes(&keypair).iter().all(|x| *x == 0u8)); @@ -992,11 +995,13 @@ mod test { let mini_secret: MiniSecretKey = MiniSecretKey::generate_with(&mut csprng); let secret: SecretKey = mini_secret.expand(ExpansionMode::Ed25519); - let public_from_mini_secret: PublicKey = mini_secret.expand_to_public(ExpansionMode::Ed25519); + let public_from_mini_secret: PublicKey = + mini_secret.expand_to_public(ExpansionMode::Ed25519); let public_from_secret: PublicKey = secret.to_public(); assert!(public_from_mini_secret == public_from_secret); let secret: SecretKey = mini_secret.expand(ExpansionMode::Uniform); - let public_from_mini_secret: PublicKey = mini_secret.expand_to_public(ExpansionMode::Uniform); + let public_from_mini_secret: PublicKey = + mini_secret.expand_to_public(ExpansionMode::Uniform); let public_from_secret: PublicKey = secret.to_public(); assert!(public_from_mini_secret == public_from_secret); } @@ -1004,11 +1009,7 @@ mod test { #[cfg(feature = "getrandom")] #[test] fn secret_key_can_be_converted_to_ed25519_bytes_and_back() { - let count = if cfg!(debug_assertions) { - 200000 - } else { - 2000000 - }; + let count = if cfg!(debug_assertions) { 200000 } else { 2000000 }; for _ in 0..count { let key = SecretKey::generate(); diff --git a/src/musig.rs b/src/musig.rs index 8d89127..7705bc7 100644 --- a/src/musig.rs +++ b/src/musig.rs @@ -30,11 +30,12 @@ // See also https://github.com/lovesh/signature-schemes/issues/2 - -use core::borrow::{Borrow}; // BorrowMut +use core::borrow::{Borrow}; // BorrowMut #[cfg(feature = "alloc")] -use alloc::{collections::btree_map::{BTreeMap, Entry}}; +use alloc::{ + collections::btree_map::{BTreeMap, Entry}, +}; use arrayref::array_ref; use arrayvec::ArrayVec; @@ -42,14 +43,13 @@ use arrayvec::ArrayVec; use merlin::Transcript; use curve25519_dalek::constants; -use curve25519_dalek::ristretto::{CompressedRistretto,RistrettoPoint}; +use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use curve25519_dalek::scalar::Scalar; use super::*; use crate::context::SigningTranscript; use crate::errors::MultiSignatureStage; - /// Rewinding immunity count plus one /// /// At least two so that our 2-round escape hatch `add_trusted` @@ -57,7 +57,6 @@ use crate::errors::MultiSignatureStage; /// 2-round multi-signatures. const REWINDS: usize = 3; - // === Agagregate public keys for multi-signatures === // /// Compute a transcript from which we may compute public key weightings. @@ -67,12 +66,13 @@ const REWINDS: usize = 3; /// We avoided a context: &'static [u8] here and in callers because they /// seem irrelevant to the security arguments in the MuSig paper. #[inline(always)] -fn commit_public_keys<'a,I>(keys: I) -> Transcript -where I: Iterator +fn commit_public_keys<'a, I>(keys: I) -> Transcript +where + I: Iterator, { let mut t = Transcript::new(b"MuSig-aggregate-public_key"); for pk in keys { - t.commit_point(b"pk-set", pk.as_compressed() ); + t.commit_point(b"pk-set", pk.as_compressed()); } t } @@ -83,7 +83,7 @@ where I: Iterator /// We cannot verify that the public key was ever entered into the /// transcript, so user facing callers should check this. fn compute_weighting(mut t: Transcript, pk: &PublicKey) -> Scalar { - t.commit_point(b"pk-choice", pk.as_compressed() ); + t.commit_point(b"pk-choice", pk.as_compressed()); t.challenge_scalar(b"") } @@ -101,100 +101,126 @@ pub trait AggregatePublicKey { fn public_key(&self) -> PublicKey; } -impl AggregatePublicKey for BTreeMap -where K: Borrow+Ord +impl AggregatePublicKey for BTreeMap +where + K: Borrow + Ord, { fn weighting(&self, choice: &PublicKey) -> Option { if !self.contains_key(choice) { return None; } - let t0 = commit_public_keys( self.keys().map(|pk| pk.borrow()) ); + let t0 = commit_public_keys(self.keys().map(|pk| pk.borrow())); Some(compute_weighting(t0, choice)) } fn public_key(&self) -> PublicKey { - let t0 = commit_public_keys( self.keys().map(|pk| pk.borrow()) ); - let point = self.keys().map(|pk| { - let pk = pk.borrow(); - compute_weighting(t0.clone(), pk) * pk.as_point() - }).sum(); + let t0 = commit_public_keys(self.keys().map(|pk| pk.borrow())); + let point = self + .keys() + .map(|pk| { + let pk = pk.borrow(); + compute_weighting(t0.clone(), pk) * pk.as_point() + }) + .sum(); PublicKey::from_point(point) } } /// Aggregation helper for public keys kept in slices -pub struct AggregatePublicKeySlice<'a,K>(&'a [K]) -where K: Borrow; +pub struct AggregatePublicKeySlice<'a, K>(&'a [K]) +where + K: Borrow; /// Aggregate public keys stored in a mutable slice -pub fn aggregate_public_key_from_slice<'a>(public_keys: &'a mut [PublicKey]) - -> Option> -{ - if public_keys.len() == 1 { return None; } +pub fn aggregate_public_key_from_slice<'a>( + public_keys: &'a mut [PublicKey], +) -> Option> { + if public_keys.len() == 1 { + return None; + } public_keys.sort_unstable(); - if public_keys.windows(2).any(|x| x[0]==x[1]) { return None; } + if public_keys.windows(2).any(|x| x[0] == x[1]) { + return None; + } Some(AggregatePublicKeySlice(public_keys)) } /// Aggregate public keys stored in a mutable slice -pub fn aggregate_public_key_from_refs_slice<'a>(public_keys: &'a mut [&'a PublicKey]) - -> Option> -{ - if public_keys.len() == 1 { return None; } +pub fn aggregate_public_key_from_refs_slice<'a>( + public_keys: &'a mut [&'a PublicKey], +) -> Option> { + if public_keys.len() == 1 { + return None; + } public_keys.sort_unstable(); - if public_keys.windows(2).any(|x| x[0]==x[1]) { return None; } + if public_keys.windows(2).any(|x| x[0] == x[1]) { + return None; + } Some(AggregatePublicKeySlice(public_keys)) } /// Aggregate public keys stored in a sorted slice -pub fn aggregate_public_key_from_sorted_slice<'a,K>(public_keys: &'a mut [K]) - -> Option> -where K: Borrow+PartialOrd +pub fn aggregate_public_key_from_sorted_slice<'a, K>( + public_keys: &'a mut [K], +) -> Option> +where + K: Borrow + PartialOrd, { - if public_keys.len() == 1 { return None; } - if public_keys.windows(2).any(|x| x[0] >= x[1]) { return None; } + if public_keys.len() == 1 { + return None; + } + if public_keys.windows(2).any(|x| x[0] >= x[1]) { + return None; + } Some(AggregatePublicKeySlice(public_keys)) } -impl<'a,K> AggregatePublicKey for AggregatePublicKeySlice<'a,K> -where K: Borrow+PartialEq +impl<'a, K> AggregatePublicKey for AggregatePublicKeySlice<'a, K> +where + K: Borrow + PartialEq, { fn weighting(&self, choice: &PublicKey) -> Option { if self.0.iter().any(|pk| pk.borrow() == choice) { return None; } - let t0 = commit_public_keys( self.0.iter().map(|pk| pk.borrow()) ); + let t0 = commit_public_keys(self.0.iter().map(|pk| pk.borrow())); Some(compute_weighting(t0, choice)) } fn public_key(&self) -> PublicKey { - let t0 = commit_public_keys( self.0.iter().map(|pk| pk.borrow()) ); - let point = self.0.iter().map(|pk| { - let pk = pk.borrow(); - compute_weighting(t0.clone(), pk) * pk.as_point() - } ).sum(); + let t0 = commit_public_keys(self.0.iter().map(|pk| pk.borrow())); + let point = self + .0 + .iter() + .map(|pk| { + let pk = pk.borrow(); + compute_weighting(t0.clone(), pk) * pk.as_point() + }) + .sum(); PublicKey::from_point(point) } } - // === Multi-signature protocol === // -const COMMITMENT_SIZE : usize = 16; +const COMMITMENT_SIZE: usize = 16; /// Commitments to `R_i` values shared between cosigners during signing -#[derive(Debug,Clone,Copy,PartialEq,Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Commitment(pub [u8; COMMITMENT_SIZE]); impl Commitment { #[allow(non_snake_case)] fn for_R(R: I) -> Commitment - where I: IntoIterator + where + I: IntoIterator, { let mut t = Transcript::new(b"MuSig-commitment"); - for R0 in R.into_iter() { t.commit_point(b"sign:R",&R0); } + for R0 in R.into_iter() { + t.commit_point(b"sign:R", &R0); + } let mut commit = [0u8; COMMITMENT_SIZE]; - t.challenge_bytes(b"commitment",&mut commit[..]); + t.challenge_bytes(b"commitment", &mut commit[..]); Commitment(commit) } } @@ -202,9 +228,8 @@ impl Commitment { // TODO: serde_boilerplate!(Commitment); - -/// Internal representation of revealed points -#[derive(Debug,Clone,PartialEq,Eq)] +/// Internal representation of revealed points +#[derive(Debug, Clone, PartialEq, Eq)] struct RevealedPoints([RistrettoPoint; REWINDS]); impl RevealedPoints { @@ -218,9 +243,9 @@ impl RevealedPoints { fn to_reveal(&self) -> Reveal { // self.check_length() ?; - let mut reveal = [0u8; 32*REWINDS]; - for (o,i) in reveal.chunks_mut(32).zip(&self.0) { - o.copy_from_slice(i.compress().as_bytes()); + let mut reveal = [0u8; 32 * REWINDS]; + for (o, i) in reveal.chunks_mut(32).zip(&self.0) { + o.copy_from_slice(i.compress().as_bytes()); } Reveal(reveal) } @@ -228,11 +253,13 @@ impl RevealedPoints { /// Revealed `R_i` values shared between cosigners during signing // #[derive(Debug,Clone,PartialEq,Eq)] -pub struct Reveal(pub [u8; 32*REWINDS]); +pub struct Reveal(pub [u8; 32 * REWINDS]); // TODO: serde_boilerplate!(Reveal); impl Clone for Reveal { - fn clone(&self) -> Reveal { Reveal(self.0) } + fn clone(&self) -> Reveal { + Reveal(self.0) + } } impl PartialEq for Reveal { #[inline] @@ -240,40 +267,44 @@ impl PartialEq for Reveal { self.0[..] == other.0[..] } } -impl Eq for Reveal { } +impl Eq for Reveal {} impl Reveal { fn check_length(&self) -> SignatureResult<()> { - if self.0.len() % 32 == 0 { Ok(()) } else { Err(SignatureError::PointDecompressionError) } + if self.0.len() % 32 == 0 { + Ok(()) + } else { + Err(SignatureError::PointDecompressionError) + } } #[allow(non_snake_case)] - fn iter_points<'a>(&'a self) -> impl Iterator + 'a { - self.0.chunks(32).map( |R| CompressedRistretto( *array_ref![R,0,32] ) ) + fn iter_points<'a>(&'a self) -> impl Iterator + 'a { + self.0.chunks(32).map(|R| CompressedRistretto(*array_ref![R, 0, 32])) } fn to_commitment(&self) -> SignatureResult { - self.check_length() ?; - Ok(Commitment::for_R( self.iter_points() )) + self.check_length()?; + Ok(Commitment::for_R(self.iter_points())) } #[allow(clippy::wrong_self_convention)] fn into_points(&self) -> SignatureResult { - self.check_length() ?; - let a = self.iter_points().map( - |x| x.decompress().ok_or(SignatureError::PointDecompressionError) - ).collect::>>() ?; - Ok( RevealedPoints( a.into_inner().unwrap() ) ) + self.check_length()?; + let a = self + .iter_points() + .map(|x| x.decompress().ok_or(SignatureError::PointDecompressionError)) + .collect::>>()?; + Ok(RevealedPoints(a.into_inner().unwrap())) } } - #[allow(non_snake_case)] -#[derive(Debug,Clone,PartialEq,Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] enum CoR { - Commit(Commitment), // H(R_i) - Reveal(RevealedPoints), // R_i - Cosigned { s: Scalar }, // s_i extracted from Cosignature type + Commit(Commitment), // H(R_i) + Reveal(RevealedPoints), // R_i + Cosigned { s: Scalar }, // s_i extracted from Cosignature type Collect { reveal: RevealedPoints, s: Scalar }, } @@ -300,24 +331,31 @@ impl CoR { */ fn set_revealed(&mut self, reveal: Reveal) -> SignatureResult<()> { - let commitment = reveal.to_commitment() ?; - let reveal = reveal.into_points() ?; - match self.clone() { // TODO: Remove .clone() here with #![feature(nll)] + let commitment = reveal.to_commitment()?; + let reveal = reveal.into_points()?; + match self.clone() { + // TODO: Remove .clone() here with #![feature(nll)] CoR::Collect { .. } => panic!("Internal error, set_reveal during collection phase."), CoR::Cosigned { .. } => panic!("Internal error, cosigning during reveal phase."), - CoR::Commit(c_old) => - if c_old==commitment { // TODO: Restore *c_old here with #![feature(nll)] + CoR::Commit(c_old) => { + if c_old == commitment { + // TODO: Restore *c_old here with #![feature(nll)] *self = CoR::Reveal(reveal); Ok(()) } else { let musig_stage = MultiSignatureStage::Commitment; - Err(SignatureError::MuSigInconsistent { musig_stage, duplicate: false, }) - }, - CoR::Reveal(reveal_old) => - if reveal_old == reveal { Ok(()) } else { // TODO: Restore *R_old here with #![feature(nll)] + Err(SignatureError::MuSigInconsistent { musig_stage, duplicate: false }) + } + }, + CoR::Reveal(reveal_old) => { + if reveal_old == reveal { + Ok(()) + } else { + // TODO: Restore *R_old here with #![feature(nll)] let musig_stage = MultiSignatureStage::Reveal; - Err(SignatureError::MuSigInconsistent { musig_stage, duplicate: true, }) - }, // Should we have a general duplicate reveal error for this case? + Err(SignatureError::MuSigInconsistent { musig_stage, duplicate: true }) + } + }, // Should we have a general duplicate reveal error for this case? } } @@ -326,42 +364,50 @@ impl CoR { match self { CoR::Collect { .. } => panic!("Internal error, set_cosigned during collection phase."), CoR::Commit(_) => { - let musig_stage = MultiSignatureStage::Reveal; - Err(SignatureError::MuSigAbsent { musig_stage, }) - }, + let musig_stage = MultiSignatureStage::Reveal; + Err(SignatureError::MuSigAbsent { musig_stage }) + }, CoR::Reveal(_) => { - *self = CoR::Cosigned { s }; + *self = CoR::Cosigned { s }; + Ok(()) + }, + CoR::Cosigned { s: s_old } => { + if *s_old == s { Ok(()) - }, - CoR::Cosigned { s: s_old } => - if *s_old==s { Ok(()) } else { + } else { let musig_stage = MultiSignatureStage::Cosignature; - Err(SignatureError::MuSigInconsistent { musig_stage, duplicate: true, }) - }, + Err(SignatureError::MuSigInconsistent { musig_stage, duplicate: true }) + } + }, } } } - /// Schnorr multi-signature (MuSig) container generic over its session types #[allow(non_snake_case)] -pub struct MuSig { +pub struct MuSig { t: T, - Rs: BTreeMap, - stage: S + Rs: BTreeMap, + stage: S, } -impl MuSig { +impl MuSig { /// Iterates over public keys. /// /// If `require_reveal=true` then we count only public key that revealed their `R` values. - pub fn public_keys(&self, require_reveal: bool) -> impl Iterator { - self.Rs.iter().filter_map( move |(pk,cor)| match cor { - CoR::Commit(_) => if require_reveal { None } else { Some(pk) }, + pub fn public_keys(&self, require_reveal: bool) -> impl Iterator { + self.Rs.iter().filter_map(move |(pk, cor)| match cor { + CoR::Commit(_) => { + if require_reveal { + None + } else { + Some(pk) + } + }, CoR::Reveal(_) => Some(pk), CoR::Cosigned { .. } => Some(pk), CoR::Collect { .. } => Some(pk), - } ) + }) } /// Aggregate public key @@ -369,52 +415,57 @@ impl MuSig { /// If `require_reveal=true` then we count only public key that revealed their `R` values. fn compute_public_key(&self, require_reveal: bool) -> PublicKey { let t0 = commit_public_keys(self.public_keys(require_reveal)); - let point = self.public_keys(require_reveal).map( |pk| - compute_weighting(t0.clone(), pk) * pk.as_point() - ).sum(); + let point = self + .public_keys(require_reveal) + .map(|pk| compute_weighting(t0.clone(), pk) * pk.as_point()) + .sum(); PublicKey::from_point(point) } /// Aggregate public key given currently revealed `R` values - pub fn public_key(&self) -> PublicKey - { self.compute_public_key(true) } + pub fn public_key(&self) -> PublicKey { + self.compute_public_key(true) + } /// Aggregate public key expected if all currently committed nodes fully participate - pub fn expected_public_key(&self) -> PublicKey - { self.compute_public_key(false) } + pub fn expected_public_key(&self) -> PublicKey { + self.compute_public_key(false) + } /// Iterator over the Rs values we actually use. /// /// Only compatable with `compute_public_key` when calling it with `require_reveal=true` #[allow(non_snake_case)] - fn iter_Rs(&self) -> impl Iterator { - self.Rs.iter().filter_map( |(pk,cor)| match cor { + fn iter_Rs(&self) -> impl Iterator { + self.Rs.iter().filter_map(|(pk, cor)| match cor { CoR::Commit(_) => None, - CoR::Reveal(reveal) => Some((pk,reveal)), - CoR::Cosigned { .. } => panic!("Internal error, compute_R called during cosigning phase."), - CoR::Collect { reveal, .. } => Some((pk,reveal)), - } ) + CoR::Reveal(reveal) => Some((pk, reveal)), + CoR::Cosigned { .. } => { + panic!("Internal error, compute_R called during cosigning phase.") + }, + CoR::Collect { reveal, .. } => Some((pk, reveal)), + }) } /// Computes the delinearizing `R` values. /// - /// Requires `self.t` be in its final state. + /// Requires `self.t` be in its final state. /// Only compatable with `compute_public_key` when calling it with `require_reveal=true` #[allow(non_snake_case)] fn rewinder(&self) -> impl Fn(&PublicKey) -> [Scalar; REWINDS] { let mut t0 = self.t.clone(); - for (pk,R) in self.iter_Rs() { - t0.commit_point(b"pk-set", pk.as_compressed() ); + for (pk, R) in self.iter_Rs() { + t0.commit_point(b"pk-set", pk.as_compressed()); for anR in R.0.iter() { - t0.commit_point(b"R",& anR.compress()); + t0.commit_point(b"R", &anR.compress()); } } move |pk| { let mut t1 = t0.clone(); - t1.commit_point(b"pk-choice", pk.as_compressed() ); + t1.commit_point(b"pk-choice", pk.as_compressed()); let mut a = ArrayVec::::new(); while !a.is_full() { - a.push( t1.challenge_scalar(b"R") ); + a.push(t1.challenge_scalar(b"R")); } a.into_inner().unwrap() } @@ -426,29 +477,36 @@ impl MuSig { /// Only compatable with `compute_public_key` when calling it with `require_reveal=true` #[allow(non_snake_case)] fn compute_R(&self, rewinder: F) -> CompressedRistretto - where F: Fn(&PublicKey) -> [Scalar; REWINDS] + where + F: Fn(&PublicKey) -> [Scalar; REWINDS], { - self.iter_Rs().map( |(pk,R)| - R.0.iter().zip(&rewinder(pk)).map(|(y,x)| x*y).sum::() - ).sum::().compress() + self.iter_Rs() + .map(|(pk, R)| { + R.0.iter().zip(&rewinder(pk)).map(|(y, x)| x * y).sum::() + }) + .sum::() + .compress() } } - /// Initial cosigning stages during which transcript modification /// remains possible but not advisable. pub trait TranscriptStages {} impl TranscriptStages for CommitStage where K: Borrow {} impl TranscriptStages for RevealStage where K: Borrow {} -impl MuSig -where T: SigningTranscript+Clone, S: TranscriptStages +impl MuSig +where + T: SigningTranscript + Clone, + S: TranscriptStages, { /// We permit extending the transcript whenever you like, so /// that say the message may be agreed upon in parallel to the /// commitments. We advise against doing so however, as this /// requires absolute faith in your random number generator, /// usually `rand::thread_rng()`. - pub fn transcript(&mut self) -> &mut T { &mut self.t } + pub fn transcript(&mut self) -> &mut T { + &mut self.t + } } impl Keypair { @@ -458,9 +516,11 @@ impl Keypair { /// copies of the private key, but the `MuSig::new` method /// can create an owned version, or use `Rc` or `Arc`. #[allow(non_snake_case)] - pub fn musig<'k,T>(&'k self, t: T) -> MuSig> - where T: SigningTranscript+Clone { - MuSig::new(self,t) + pub fn musig<'k, T>(&'k self, t: T) -> MuSig> + where + T: SigningTranscript + Clone, + { + MuSig::new(self, t) } } @@ -472,8 +532,10 @@ pub struct CommitStage> { R_me: Reveal, } -impl MuSig> -where K: Borrow, T: SigningTranscript+Clone +impl MuSig> +where + K: Borrow, + T: SigningTranscript + Clone, { /// Initialize a multi-signature aka cosignature protocol run. /// @@ -482,27 +544,27 @@ where K: Borrow, T: SigningTranscript+Clone /// for the `K = &'k Keypair` case. You could use `Rc` or `Arc` /// with this `MuSig::new` method, or even pass in an owned copy. #[allow(non_snake_case)] - pub fn new(keypair: K, t: T) -> MuSig> { + pub fn new(keypair: K, t: T) -> MuSig> { let nonce = &keypair.borrow().secret.nonce; let mut r_me = ArrayVec::::new(); for i in 0..REWINDS { - r_me.push( t.witness_scalar(b"MuSigWitness",&[nonce,&i.to_le_bytes()]) ); + r_me.push(t.witness_scalar(b"MuSigWitness", &[nonce, &i.to_le_bytes()])); } let r_me = r_me.into_inner().unwrap(); // context, message, nonce, but not &self.public.compressed let B = constants::RISTRETTO_BASEPOINT_TABLE; - let R_me_points: ArrayVec = r_me.iter() - .map(|r_me_i| r_me_i * B).collect(); + let R_me_points: ArrayVec = + r_me.iter().map(|r_me_i| r_me_i * B).collect(); let R_me_points = RevealedPoints(R_me_points.into_inner().unwrap()); let R_me = R_me_points.to_reveal(); let mut Rs = BTreeMap::new(); - Rs.insert(keypair.borrow().public, CoR::Reveal( R_me_points )); + Rs.insert(keypair.borrow().public, CoR::Reveal(R_me_points)); let stage = CommitStage { keypair, r_me, R_me }; - MuSig { t, Rs, stage, } + MuSig { t, Rs, stage } } /// Our commitment to our `R` to send to all other cosigners @@ -511,26 +573,31 @@ where K: Borrow, T: SigningTranscript+Clone } /// Add a new cosigner's public key and associated `R` bypassing our commitment phase. - pub fn add_their_commitment(&mut self, them: PublicKey, theirs: Commitment) - -> SignatureResult<()> - { + pub fn add_their_commitment( + &mut self, + them: PublicKey, + theirs: Commitment, + ) -> SignatureResult<()> { let theirs = CoR::Commit(theirs); match self.Rs.entry(them) { - Entry::Vacant(v) => { v.insert(theirs); }, - Entry::Occupied(o) => + Entry::Vacant(v) => { + v.insert(theirs); + }, + Entry::Occupied(o) => { if o.get() != &theirs { let musig_stage = MultiSignatureStage::Commitment; - return Err(SignatureError::MuSigInconsistent { musig_stage, duplicate: true, }); - }, + return Err(SignatureError::MuSigInconsistent { musig_stage, duplicate: true }); + } + }, } Ok(()) } /// Commit to reveal phase transition. #[allow(non_snake_case)] - pub fn reveal_stage(self) -> MuSig> { - let MuSig { t, Rs, stage: CommitStage { keypair, r_me, R_me, }, } = self; - MuSig { t, Rs, stage: RevealStage { keypair, r_me, R_me, }, } + pub fn reveal_stage(self) -> MuSig> { + let MuSig { t, Rs, stage: CommitStage { keypair, r_me, R_me } } = self; + MuSig { t, Rs, stage: RevealStage { keypair, r_me, R_me } } } } @@ -542,27 +609,28 @@ pub struct RevealStage> { R_me: Reveal, } -impl MuSig> -where K: Borrow, T: SigningTranscript+Clone +impl MuSig> +where + K: Borrow, + T: SigningTranscript + Clone, { /// Reveal our `R` contribution to send to all other cosigners - pub fn our_reveal(&self) -> &Reveal { &self.stage.R_me } + pub fn our_reveal(&self) -> &Reveal { + &self.stage.R_me + } // TODO: Permit `add_their_reveal` and `add_trusted` in `CommitStage` // using const generics, const fn, and replacing the `*Stage` types // with some enum. /// Include a revealed `R` value from a previously committed cosigner - pub fn add_their_reveal(&mut self, them: PublicKey, theirs: Reveal) - -> SignatureResult<()> - { + pub fn add_their_reveal(&mut self, them: PublicKey, theirs: Reveal) -> SignatureResult<()> { match self.Rs.entry(them) { Entry::Vacant(_) => { let musig_stage = MultiSignatureStage::Commitment; - Err(SignatureError::MuSigAbsent { musig_stage, }) + Err(SignatureError::MuSigAbsent { musig_stage }) }, - Entry::Occupied(mut o) => - o.get_mut().set_revealed(theirs), + Entry::Occupied(mut o) => o.get_mut().set_revealed(theirs), } } @@ -588,47 +656,49 @@ where K: Borrow, T: SigningTranscript+Clone /// with the middle ground being only something like Parity Signer. /// Also, any public keys controlled by an organization likely /// fail (c) too, making this only useful for individuals. - pub fn add_trusted(&mut self, them: PublicKey, theirs: Reveal) - -> SignatureResult<()> - { - let reveal = theirs.into_points() ?; + pub fn add_trusted(&mut self, them: PublicKey, theirs: Reveal) -> SignatureResult<()> { + let reveal = theirs.into_points()?; let theirs = CoR::Reveal(reveal); match self.Rs.entry(them) { - Entry::Vacant(v) => { v.insert(theirs); }, - Entry::Occupied(o) => + Entry::Vacant(v) => { + v.insert(theirs); + }, + Entry::Occupied(o) => { if o.get() != &theirs { let musig_stage = MultiSignatureStage::Reveal; - return Err(SignatureError::MuSigInconsistent { musig_stage, duplicate: true, }); - }, + return Err(SignatureError::MuSigInconsistent { musig_stage, duplicate: true }); + } + }, } Ok(()) } /// Reveal to cosign phase transition. #[allow(non_snake_case)] - pub fn cosign_stage(mut self) -> MuSig { + pub fn cosign_stage(mut self) -> MuSig { self.t.proto_name(b"Schnorr-sig"); let pk = *self.public_key().as_compressed(); - self.t.commit_point(b"sign:pk",&pk); + self.t.commit_point(b"sign:pk", &pk); let rewinder = self.rewinder(); let rewinds = rewinder(&self.stage.keypair.borrow().public); let R = self.compute_R(rewinder); - self.t.commit_point(b"sign:R",&R); + self.t.commit_point(b"sign:R", &R); let t0 = commit_public_keys(self.public_keys(true)); let a_me = compute_weighting(t0, &self.stage.keypair.borrow().public); - let c = self.t.challenge_scalar(b"sign:c"); // context, message, A/public_key, R=rG + let c = self.t.challenge_scalar(b"sign:c"); // context, message, A/public_key, R=rG - let mut s_me: Scalar = self.stage.r_me.iter().zip(&rewinds).map(|(y,x)| x*y).sum(); + let mut s_me: Scalar = self.stage.r_me.iter().zip(&rewinds).map(|(y, x)| x * y).sum(); s_me += c * a_me * self.stage.keypair.borrow().secret.key; zeroize::Zeroize::zeroize(&mut self.stage.r_me); - let MuSig { t, mut Rs, stage: RevealStage { .. }, } = self; - *(Rs.get_mut(&self.stage.keypair.borrow().public).expect("Rs known to contain this public; qed")) = CoR::Cosigned { s: s_me }; - MuSig { t, Rs, stage: CosignStage { R, s_me }, } + let MuSig { t, mut Rs, stage: RevealStage { .. } } = self; + *(Rs.get_mut(&self.stage.keypair.borrow().public) + .expect("Rs known to contain this public; qed")) = CoR::Cosigned { s: s_me }; + MuSig { t, Rs, stage: CosignStage { R, s_me } } } } @@ -642,97 +712,111 @@ pub struct CosignStage { } /// Cosignatures shared between cosigners during signing -#[derive(Debug,Clone,Copy,PartialEq,Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Cosignature(pub [u8; 32]); -impl MuSig { +impl MuSig { /// Reveals our signature contribution pub fn our_cosignature(&self) -> Cosignature { Cosignature(self.stage.s_me.to_bytes()) } /// Include a cosignature from another cosigner - pub fn add_their_cosignature(&mut self, them: PublicKey, theirs: Cosignature) - -> SignatureResult<()> - { + pub fn add_their_cosignature( + &mut self, + them: PublicKey, + theirs: Cosignature, + ) -> SignatureResult<()> { let theirs = crate::scalar_from_canonical_bytes(theirs.0) - .ok_or(SignatureError::ScalarFormatError) ?; + .ok_or(SignatureError::ScalarFormatError)?; match self.Rs.entry(them) { Entry::Vacant(_) => { - let musig_stage = MultiSignatureStage::Reveal; - Err(SignatureError::MuSigAbsent { musig_stage, }) - }, - Entry::Occupied(mut o) => o.get_mut().set_cosigned(theirs) + let musig_stage = MultiSignatureStage::Reveal; + Err(SignatureError::MuSigAbsent { musig_stage }) + }, + Entry::Occupied(mut o) => o.get_mut().set_cosigned(theirs), } } /// Interate over the cosigners who successfully revaled and /// later cosigned. - pub fn cosigned(&self) -> impl Iterator { - self.Rs.iter().filter_map( |(pk,cor)| match cor { + pub fn cosigned(&self) -> impl Iterator { + self.Rs.iter().filter_map(|(pk, cor)| match cor { CoR::Commit(_) => None, CoR::Reveal(_) => None, CoR::Cosigned { .. } => Some(pk), - CoR::Collect { .. } => panic!("Collect found in Cosign phase.") - } ) + CoR::Collect { .. } => panic!("Collect found in Cosign phase."), + }) } /// Interate over the possible cosigners who successfully committed /// and revaled, but actually cosigned. - pub fn uncosigned(&self) -> impl Iterator { - self.Rs.iter().filter_map( |(pk,cor)| match cor { + pub fn uncosigned(&self) -> impl Iterator { + self.Rs.iter().filter_map(|(pk, cor)| match cor { CoR::Commit(_) => None, CoR::Reveal(_) => Some(pk), CoR::Cosigned { .. } => None, CoR::Collect { .. } => panic!("Collect found in Cosign phase."), - } ) + }) } /// Actually computes the cosignature #[allow(non_snake_case)] pub fn sign(&self) -> Option { // if self.uncosigned().all(|_| false) { return None; } // TODO: why does this fail? - if self.uncosigned().last().is_some() { return None; } - let s: Scalar = self.Rs.iter() - .filter_map( |(_pk,cor)| match cor { + if self.uncosigned().last().is_some() { + return None; + } + let s: Scalar = self + .Rs + .iter() + .filter_map(|(_pk, cor)| match cor { CoR::Commit(_) => None, - CoR::Reveal(_) => panic!("Internal error, MuSig::uncosigned broken."), + CoR::Reveal(_) => { + panic!("Internal error, MuSig::uncosigned broken.") + }, CoR::Cosigned { s, .. } => Some(s), CoR::Collect { .. } => panic!("Collect found in Cosign phase."), - } ).sum(); - Some(Signature { s, R: self.stage.R, }) + }) + .sum(); + Some(Signature { s, R: self.stage.R }) } } - /// Initialize a collector of cosignatures who does not themselves cosign. #[allow(non_snake_case)] -pub fn collect_cosignatures(mut t: T) -> MuSig { +pub fn collect_cosignatures(mut t: T) -> MuSig { t.proto_name(b"Schnorr-sig"); - MuSig { t, Rs: BTreeMap::new(), stage: CollectStage, } + MuSig { t, Rs: BTreeMap::new(), stage: CollectStage } } /// Initial stage for cosignature collectors who do not themselves cosign. pub struct CollectStage; -impl MuSig { +impl MuSig { /// Adds revealed `R` and cosignature into a cosignature collector #[allow(non_snake_case)] - pub fn add(&mut self, them: PublicKey, their_reveal: Reveal, their_cosignature: Cosignature) - -> SignatureResult<()> - { - let reveal = their_reveal.into_points() ?; + pub fn add( + &mut self, + them: PublicKey, + their_reveal: Reveal, + their_cosignature: Cosignature, + ) -> SignatureResult<()> { + let reveal = their_reveal.into_points()?; let s = crate::scalar_from_canonical_bytes(their_cosignature.0) - .ok_or(SignatureError::ScalarFormatError) ?; + .ok_or(SignatureError::ScalarFormatError)?; let cor = CoR::Collect { reveal, s }; match self.Rs.entry(them) { - Entry::Vacant(v) => { v.insert(cor); }, - Entry::Occupied(o) => + Entry::Vacant(v) => { + v.insert(cor); + }, + Entry::Occupied(o) => { if o.get() != &cor { let musig_stage = MultiSignatureStage::Reveal; - return Err(SignatureError::MuSigInconsistent { musig_stage, duplicate: true, }); - }, + return Err(SignatureError::MuSigInconsistent { musig_stage, duplicate: true }); + } + }, } Ok(()) } @@ -741,19 +825,22 @@ impl MuSig { #[allow(non_snake_case)] pub fn signature(mut self) -> Signature { let pk = *self.public_key().as_compressed(); - self.t.commit_point(b"sign:pk",&pk); + self.t.commit_point(b"sign:pk", &pk); let R = self.compute_R(self.rewinder()); - let s: Scalar = self.Rs.values().map(|cor| match cor { + let s: Scalar = self + .Rs + .values() + .map(|cor| match cor { CoR::Collect { s, .. } => s, _ => panic!("Reached CollectStage from another stage"), - }).sum(); - Signature { s, R, } + }) + .sum(); + Signature { s, R } } } - #[cfg(test)] mod tests { #[cfg(feature = "alloc")] @@ -764,7 +851,7 @@ mod tests { #[test] fn aggregation_btreeemap_vs_slice() { let mut vec: Vec = (0..16).map(|_| SecretKey::generate().to_public()).collect(); - let btm: BTreeMap = vec.iter().map( |x| (x.clone(),()) ).collect(); + let btm: BTreeMap = vec.iter().map(|x| (x.clone(), ())).collect(); debug_assert_eq!( btm.public_key(), aggregate_public_key_from_slice(vec.as_mut_slice()).unwrap().public_key() @@ -777,33 +864,41 @@ mod tests { let keypairs: Vec = (0..16).map(|_| Keypair::generate()).collect(); let t = signing_context(b"multi-sig").bytes(b"We are legion!"); - let mut commits: Vec<_> = keypairs.iter().map( |k| k.musig(t.clone()) ).collect(); + let mut commits: Vec<_> = keypairs.iter().map(|k| k.musig(t.clone())).collect(); for i in 0..commits.len() { - let r = commits[i].our_commitment(); + let r = commits[i].our_commitment(); for j in commits.iter_mut() { - assert!( j.add_their_commitment(keypairs[i].public.clone(),r) - .is_ok() != (r == j.our_commitment()) ); + assert!( + j.add_their_commitment(keypairs[i].public.clone(), r).is_ok() + != (r == j.our_commitment()) + ); } } let mut reveal_msgs: Vec = Vec::with_capacity(commits.len()); - let mut reveals: Vec<_> = commits.drain(..).map( |c| c.reveal_stage() ).collect(); + let mut reveals: Vec<_> = commits.drain(..).map(|c| c.reveal_stage()).collect(); for i in 0..reveals.len() { let r = reveals[i].our_reveal().clone(); for j in reveals.iter_mut() { - j.add_their_reveal(keypairs[i].public.clone(),r.clone()).unwrap(); + j.add_their_reveal(keypairs[i].public.clone(), r.clone()).unwrap(); } reveal_msgs.push(r); } let pk = reveals[0].public_key(); let mut cosign_msgs: Vec = Vec::with_capacity(reveals.len()); - let mut cosigns: Vec<_> = reveals.drain(..).map( |c| { assert_eq!(pk, c.public_key()); c.cosign_stage() } ).collect(); + let mut cosigns: Vec<_> = reveals + .drain(..) + .map(|c| { + assert_eq!(pk, c.public_key()); + c.cosign_stage() + }) + .collect(); for i in 0..cosigns.len() { assert_eq!(pk, cosigns[i].public_key()); let r = cosigns[i].our_cosignature(); for j in cosigns.iter_mut() { - j.add_their_cosignature(keypairs[i].public.clone(),r).unwrap(); + j.add_their_cosignature(keypairs[i].public.clone(), r).unwrap(); } cosign_msgs.push(r); assert_eq!(pk, cosigns[i].public_key()); @@ -812,11 +907,12 @@ mod tests { // let signature = cosigns[0].sign().unwrap(); let mut c = collect_cosignatures(t.clone()); for i in 0..cosigns.len() { - c.add(keypairs[i].public.clone(),reveal_msgs[i].clone(),cosign_msgs[i].clone()).unwrap(); + c.add(keypairs[i].public.clone(), reveal_msgs[i].clone(), cosign_msgs[i].clone()) + .unwrap(); } let signature = c.signature(); - assert!( pk.verify(t,&signature).is_ok() ); + assert!(pk.verify(t, &signature).is_ok()); for i in 0..cosigns.len() { assert_eq!(pk, cosigns[i].public_key()); assert_eq!(signature, cosigns[i].sign().unwrap()); diff --git a/src/olaf/identifier.rs b/src/olaf/identifier.rs index 4f3d5bb..af56466 100644 --- a/src/olaf/identifier.rs +++ b/src/olaf/identifier.rs @@ -46,9 +46,7 @@ impl<'de> Visitor<'de> for IdentifierVisitor { Err(_) => return Err(E::custom("Invalid hexadecimal string")), }; if bytes.len() != 32 { - return Err(E::custom( - "Hexadecimal string must be exactly 32 bytes long", - )); + return Err(E::custom("Hexadecimal string must be exactly 32 bytes long")); } let mut bytes_array = [0u8; 32]; bytes_array.copy_from_slice(&bytes); @@ -155,10 +153,7 @@ mod tests { let result: Result = serde_json::from_str(invalid_hex_scalar); // Assert that the deserialization fails - assert!( - result.is_err(), - "Deserialization should fail for invalid hex characters" - ); + assert!(result.is_err(), "Deserialization should fail for invalid hex characters"); } #[test] @@ -168,9 +163,6 @@ mod tests { let result: Result = serde_json::from_str(invalid_length_hex); // Assert that the deserialization fails due to length mismatch - assert!( - result.is_err(), - "Deserialization should fail for incorrect hex length" - ); + assert!(result.is_err(), "Deserialization should fail for incorrect hex length"); } } diff --git a/src/olaf/polynomial.rs b/src/olaf/polynomial.rs index 6b78fa3..c97aeec 100644 --- a/src/olaf/polynomial.rs +++ b/src/olaf/polynomial.rs @@ -31,10 +31,8 @@ impl Polynomial { } pub(crate) fn evaluate(&self, x: &Value) -> Value { - let mut value = *self - .coefficients - .last() - .expect("coefficients must have at least one element"); + let mut value = + *self.coefficients.last().expect("coefficients must have at least one element"); // Process all coefficients except the last one, using Horner's method for coeff in self.coefficients.iter().rev().skip(1) { @@ -60,18 +58,18 @@ impl PolynomialCommitment { .map(|coefficient| GENERATOR * coefficient) .collect(); - Self { - coefficients_commitments, - } + Self { coefficients_commitments } } pub(crate) fn evaluate(&self, identifier: &Value) -> ValueCommitment { let i = identifier; - let (_, result) = self.coefficients_commitments.iter().fold( - (Scalar::ONE, RistrettoPoint::identity()), - |(i_to_the_k, sum_so_far), comm_k| (i * i_to_the_k, sum_so_far + comm_k * i_to_the_k), - ); + let (_, result) = self + .coefficients_commitments + .iter() + .fold((Scalar::ONE, RistrettoPoint::identity()), |(i_to_the_k, sum_so_far), comm_k| { + (i * i_to_the_k, sum_so_far + comm_k * i_to_the_k) + }); result } @@ -87,18 +85,14 @@ impl PolynomialCommitment { let mut total_commitment = vec![RistrettoPoint::identity(); max_length]; for polynomial_commitment in polynomials_commitments { - for (i, coeff_commitment) in polynomial_commitment - .coefficients_commitments - .iter() - .enumerate() + for (i, coeff_commitment) in + polynomial_commitment.coefficients_commitments.iter().enumerate() { total_commitment[i] += coeff_commitment; } } - PolynomialCommitment { - coefficients_commitments: total_commitment, - } + PolynomialCommitment { coefficients_commitments: total_commitment } } } @@ -123,10 +117,7 @@ mod tests { assert_eq!(polynomial.coefficients.len(), degree as usize + 1); - assert_eq!( - polynomial_commitment.coefficients_commitments.len(), - degree as usize + 1 - ); + assert_eq!(polynomial_commitment.coefficients_commitments.len(), degree as usize + 1); } #[test] @@ -187,15 +178,10 @@ mod tests { let quadratic_commitment = Scalar::from(1u64) * GENERATOR; // Note the order and inclusion of the constant term - let coefficients_commitments = vec![ - constant_coefficient_commitment, - linear_commitment, - quadratic_commitment, - ]; + let coefficients_commitments = + vec![constant_coefficient_commitment, linear_commitment, quadratic_commitment]; - let polynomial_commitment = PolynomialCommitment { - coefficients_commitments, - }; + let polynomial_commitment = PolynomialCommitment { coefficients_commitments }; let value = Scalar::from(2u64); @@ -204,9 +190,6 @@ mod tests { let result = polynomial_commitment.evaluate(&value); - assert_eq!( - result, expected, - "The evaluated commitment does not match the expected result" - ); + assert_eq!(result, expected, "The evaluated commitment does not match the expected result"); } } diff --git a/src/olaf/simplpedpop.rs b/src/olaf/simplpedpop.rs index 9cc4e1f..e001c0b 100644 --- a/src/olaf/simplpedpop.rs +++ b/src/olaf/simplpedpop.rs @@ -50,10 +50,7 @@ pub struct Parameters { impl Parameters { /// Create new parameters. pub fn new(participants: u16, threshold: u16) -> Parameters { - Parameters { - participants, - threshold, - } + Parameters { participants, threshold } } pub(crate) fn validate(&self) -> Result<(), DKGError> { @@ -87,10 +84,7 @@ impl Identifiers { own_identifier: Identifier, others_identifiers: BTreeSet, ) -> Identifiers { - Identifiers { - own_identifier, - others_identifiers, - } + Identifiers { own_identifier, others_identifiers } } pub(crate) fn validate(&self, participants: u16) -> Result<(), DKGError> { @@ -185,9 +179,7 @@ impl EncryptedSecretShare { let cipher: ChaCha20Poly1305 = make_aead::(transcript); let nonce = Nonce::from_slice(&bytes[..]); - let plaintext = cipher - .decrypt(nonce, &self.0[..]) - .map_err(DKGError::DecryptionError)?; + let plaintext = cipher.decrypt(nonce, &self.0[..]).map_err(DKGError::DecryptionError)?; let mut bytes = [0; 32]; bytes.copy_from_slice(&plaintext); @@ -334,15 +326,8 @@ pub mod round1 { secret_key.sign(Transcript::new(b"Proof of Possession"), &public_key); ( - PrivateData { - secret_key, - secret_polynomial, - }, - PublicData { - parameters, - secret_polynomial_commitment, - proof_of_possession, - }, + PrivateData { secret_key, secret_polynomial }, + PublicData { parameters, secret_polynomial_commitment, proof_of_possession }, ) } } @@ -415,9 +400,7 @@ pub mod round2 { ) -> DKGResult { let encrypted_secret_share = secret_share.encrypt(&deckey, &enckey, context)?; - Ok(PrivateMessage { - encrypted_secret_share, - }) + Ok(PrivateMessage { encrypted_secret_share }) } } @@ -527,27 +510,23 @@ pub mod round2 { // Writes own data in the transcript transcript.commit_point(b"SecretCommitment", &own_first_coefficient_compressed); - for coefficient_commitment in &round1_public_data - .secret_polynomial_commitment - .coefficients_commitments + for coefficient_commitment in + &round1_public_data.secret_polynomial_commitment.coefficients_commitments { transcript .commit_point(b"CoefficientCommitment", &coefficient_commitment.compress()); } - transcript.commit_point( - b"ProofOfPossessionR", - &round1_public_data.proof_of_possession.R, - ); + transcript + .commit_point(b"ProofOfPossessionR", &round1_public_data.proof_of_possession.R); own_inserted = true; } // Writes the data of the other participants in the transcript transcript.commit_point(b"SecretCommitment", &message_first_coefficient_compressed); - for coefficient_commitment in &message - .secret_polynomial_commitment - .coefficients_commitments + for coefficient_commitment in + &message.secret_polynomial_commitment.coefficients_commitments { transcript .commit_point(b"CoefficientCommitment", &coefficient_commitment.compress()); @@ -560,18 +539,15 @@ pub mod round2 { if !own_inserted { transcript.commit_point(b"SecretCommitment", &own_first_coefficient_compressed); - for coefficient_commitment in &round1_public_data - .secret_polynomial_commitment - .coefficients_commitments + for coefficient_commitment in + &round1_public_data.secret_polynomial_commitment.coefficients_commitments { transcript .commit_point(b"CoefficientCommitment", &coefficient_commitment.compress()); } - transcript.commit_point( - b"ProofOfPossessionR", - &round1_public_data.proof_of_possession.R, - ); + transcript + .commit_point(b"ProofOfPossessionR", &round1_public_data.proof_of_possession.R); } // Scalar generated from transcript used to generate random identifiers to the participants @@ -580,12 +556,7 @@ pub mod round2 { let (identifiers, round1_public_messages) = generate_identifiers(round1_public_data, round1_public_messages, &scalar); - PublicData { - identifiers, - round1_public_messages, - transcript: scalar, - public_keys, - } + PublicData { identifiers, round1_public_messages, transcript: scalar, public_keys } } fn generate_identifiers( @@ -696,11 +667,7 @@ pub mod round2 { }) .collect(); - for (i, identifier) in round2_public_data - .identifiers - .others_identifiers - .iter() - .enumerate() + for (i, identifier) in round2_public_data.identifiers.others_identifiers.iter().enumerate() { let secret_share = secret_polynomial.evaluate(&identifier.0); private_messages.insert( @@ -723,10 +690,7 @@ pub mod round2 { let public_message = PublicMessage { certificate }; - Ok(Messages { - private_messages, - public_message, - }) + Ok(Messages { private_messages, public_message }) } } @@ -801,11 +765,7 @@ pub mod round3 { round1_public_data: &round1::PublicData, round1_private_data: round1::PrivateData, round2_private_messages: &BTreeMap, - ) -> DKGResult<( - GroupPublicKey, - BTreeMap, - PrivateData, - )> { + ) -> DKGResult<(GroupPublicKey, BTreeMap, PrivateData)> { round1_public_data.parameters.validate()?; round2_public_data @@ -852,11 +812,7 @@ pub mod round3 { &round2_public_messages .iter() .map(|(id, msg)| { - if !round2_public_data - .identifiers - .others_identifiers() - .contains(id) - { + if !round2_public_data.identifiers.others_identifiers().contains(id) { Err(DKGError::UnknownIdentifierRound2PublicMessages(*id)) } else { Ok(msg.certificate) @@ -912,17 +868,13 @@ pub mod round3 { let mut total_secret_share = Scalar::ZERO; for id in &identifiers.others_identifiers { - total_secret_share += secret_shares - .get(id) - .ok_or(DKGError::UnknownIdentifierRound2PrivateMessages)? - .0; + total_secret_share += + secret_shares.get(id).ok_or(DKGError::UnknownIdentifierRound2PrivateMessages)?.0; } total_secret_share += own_secret_share; - let private_data = PrivateData { - total_secret_share: SecretShare(total_secret_share), - }; + let private_data = PrivateData { total_secret_share: SecretShare(total_secret_share) }; Ok(private_data) } @@ -959,10 +911,8 @@ pub mod round3 { let own_group_public_key_share = round2_private_data.total_secret_share.0 * GENERATOR; - group_public_key_shares.insert( - round2_public_data.identifiers.own_identifier, - own_group_public_key_share, - ); + group_public_key_shares + .insert(round2_public_data.identifiers.own_identifier, own_group_public_key_share); let shared_public_key = GroupPublicKey::from_point( *total_secret_polynomial_commitment diff --git a/src/olaf/tests.rs b/src/olaf/tests.rs index a3e5d30..6debb7a 100644 --- a/src/olaf/tests.rs +++ b/src/olaf/tests.rs @@ -28,17 +28,11 @@ mod tests { let max_signers = rng.gen_range(MINIMUM_PARTICIPANTS..=MAXIMUM_PARTICIPANTS); let min_signers = rng.gen_range(MININUM_THRESHOLD..=max_signers); - (1..=max_signers) - .map(|_| Parameters::new(max_signers, min_signers)) - .collect() + (1..=max_signers).map(|_| Parameters::new(max_signers, min_signers)).collect() } - fn round1() -> ( - Vec, - Vec, - Vec, - Vec>, - ) { + fn round1() -> (Vec, Vec, Vec, Vec>) + { let parameters_list = generate_parameters(); let mut all_public_messages_vec = Vec::new(); @@ -92,12 +86,8 @@ mod tests { participants_round1_private_data: Vec, participants_round1_public_data: &Vec, participants_round1_public_messages: &Vec>, - ) -> DKGResult<( - Vec, - Vec, - Vec, - Vec, - )> { + ) -> DKGResult<(Vec, Vec, Vec, Vec)> + { let mut participants_round2_public_data = Vec::new(); let mut participants_round2_public_messages = Vec::new(); let mut participants_set_of_participants = Vec::new(); @@ -134,11 +124,7 @@ mod tests { participants_round2_private_messages: Vec>, identifiers_vec: &Vec, ) -> DKGResult< - Vec<( - GroupPublicKey, - BTreeMap, - round3::PrivateData, - )>, + Vec<(GroupPublicKey, BTreeMap, round3::PrivateData)>, > { let mut participant_data_round3 = Vec::new(); @@ -235,10 +221,8 @@ mod tests { ) .unwrap(); - let shared_public_keys: Vec = participants_data_round3 - .iter() - .map(|state| state.0) - .collect(); + let shared_public_keys: Vec = + participants_data_round3.iter().map(|state| state.0).collect(); assert!( shared_public_keys.windows(2).all(|w| w[0] == w[1]), @@ -300,15 +284,9 @@ mod tests { mut participants_round1_public_messages, ) = round1(); - let mut new_message = participants_round1_public_messages[0] - .first() - .unwrap() - .clone(); + let mut new_message = participants_round1_public_messages[0].first().unwrap().clone(); - new_message - .secret_polynomial_commitment - .coefficients_commitments - .pop(); + new_message.secret_polynomial_commitment.coefficients_commitments.pop(); participants_round1_public_messages[0].pop_first(); participants_round1_public_messages[0].insert(new_message); @@ -365,18 +343,14 @@ mod tests { let enc_keys: Vec = participants_round1_public_messages[1] .iter() .map(|msg| { - *msg.secret_polynomial_commitment - .coefficients_commitments - .first() - .unwrap() + *msg.secret_polynomial_commitment.coefficients_commitments.first().unwrap() }) .collect(); let secret_share = SecretShare(Scalar::random(&mut OsRng)); - let identifiers: BTreeSet = participants_sets_of_participants[1] - .others_identifiers - .clone(); + let identifiers: BTreeSet = + participants_sets_of_participants[1].others_identifiers.clone(); let index = identifiers .iter() @@ -386,10 +360,7 @@ mod tests { let enc_share = secret_share.encrypt( &participants_round1_private_data[1].secret_key.key, &enc_keys[index], - participants_sets_of_participants[0] - .own_identifier - .0 - .as_bytes(), + participants_sets_of_participants[0].own_identifier.0.as_bytes(), ); let private_message = participants_round2_private_messages[1] @@ -664,9 +635,7 @@ mod tests { .map(|msg| msg.public_message().clone()) .collect(); - participants_round2_public_data[0] - .round1_public_messages - .pop_first(); + participants_round2_public_data[0].round1_public_messages.pop_first(); let participants_round2_private_messages: Vec< BTreeMap, @@ -881,11 +850,8 @@ mod tests { let private_message = round2_private_messages[0].pop_first().unwrap().1; round2_private_messages[0].insert(unknown_identifier, private_message); - let public_message = participants_round2_public_data[0] - .round1_public_messages - .pop_first() - .unwrap() - .1; + let public_message = + participants_round2_public_data[0].round1_public_messages.pop_first().unwrap().1; participants_round2_public_data[0] .round1_public_messages diff --git a/src/points.rs b/src/points.rs index 0f60398..d9ea956 100644 --- a/src/points.rs +++ b/src/points.rs @@ -17,25 +17,22 @@ // We're discussing including some variant in curve25519-dalek directly in // https://github.com/dalek-cryptography/curve25519-dalek/pull/220 - use core::fmt::{Debug}; -use curve25519_dalek::ristretto::{CompressedRistretto,RistrettoPoint}; -use subtle::{ConstantTimeEq,Choice}; +use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; +use subtle::{ConstantTimeEq, Choice}; // use curve25519_dalek::scalar::Scalar; -use crate::errors::{SignatureError,SignatureResult}; - +use crate::errors::{SignatureError, SignatureResult}; /// Compressed Ristretto point length pub const RISTRETTO_POINT_LENGTH: usize = 32; - /// A `RistrettoBoth` contains both an uncompressed `RistrettoPoint` /// as well as the corresponding `CompressedRistretto`. It provides /// a convenient middle ground for protocols that both hash compressed /// points to derive scalars for use with uncompressed points. -#[derive(Copy, Clone, Default, Eq)] // PartialEq optimized below +#[derive(Copy, Clone, Default, Eq)] // PartialEq optimized below pub struct RistrettoBoth { compressed: CompressedRistretto, point: RistrettoPoint, @@ -49,34 +46,42 @@ impl Debug for RistrettoBoth { impl ConstantTimeEq for RistrettoBoth { fn ct_eq(&self, other: &RistrettoBoth) -> Choice { - self.compressed.ct_eq(&other.compressed) + self.compressed.ct_eq(&other.compressed) } } impl RistrettoBoth { - const DESCRIPTION : &'static str = "A ristretto point represented as a 32-byte compressed point"; + const DESCRIPTION: &'static str = "A ristretto point represented as a 32-byte compressed point"; // I dislike getter methods, and prefer direct field access, but doing // getters here permits the fields being private, and gives us faster // equality comparisons. /// Access the compressed Ristretto form - pub fn as_compressed(&self) -> &CompressedRistretto { &self.compressed } + pub fn as_compressed(&self) -> &CompressedRistretto { + &self.compressed + } /// Extract the compressed Ristretto form - pub fn into_compressed(self) -> CompressedRistretto { self.compressed } + pub fn into_compressed(self) -> CompressedRistretto { + self.compressed + } /// Access the point form - pub fn as_point(&self) -> &RistrettoPoint { &self.point } + pub fn as_point(&self) -> &RistrettoPoint { + &self.point + } /// Extract the point form - pub fn into_point(self) -> RistrettoPoint { self.point } + pub fn into_point(self) -> RistrettoPoint { + self.point + } /// Decompress into the `RistrettoBoth` format that also retains the /// compressed form. pub fn from_compressed(compressed: CompressedRistretto) -> SignatureResult { Ok(RistrettoBoth { - point: compressed.decompress().ok_or(SignatureError::PointDecompressionError) ?, + point: compressed.decompress().ok_or(SignatureError::PointDecompressionError)?, compressed, }) } @@ -84,10 +89,7 @@ impl RistrettoBoth { /// Compress into the `RistrettoBoth` format that also retains the /// uncompressed form. pub fn from_point(point: RistrettoPoint) -> RistrettoBoth { - RistrettoBoth { - compressed: point.compress(), - point, - } + RistrettoBoth { compressed: point.compress(), point } } /// Convert this public key to a byte array. @@ -126,15 +128,21 @@ impl RistrettoBoth { /// is an `SignatureError` describing the error that occurred. #[inline] pub fn from_bytes(bytes: &[u8]) -> SignatureResult { - RistrettoBoth::from_bytes_ser("RistrettoPoint",RistrettoBoth::DESCRIPTION,bytes) + RistrettoBoth::from_bytes_ser("RistrettoPoint", RistrettoBoth::DESCRIPTION, bytes) } /// Variant of `RistrettoBoth::from_bytes` that propagates more informative errors. #[inline] - pub fn from_bytes_ser(name: &'static str, description: &'static str, bytes: &[u8]) -> SignatureResult { + pub fn from_bytes_ser( + name: &'static str, + description: &'static str, + bytes: &[u8], + ) -> SignatureResult { if bytes.len() != RISTRETTO_POINT_LENGTH { - return Err(SignatureError::BytesLengthError{ - name, description, length: RISTRETTO_POINT_LENGTH, + return Err(SignatureError::BytesLengthError { + name, + description, + length: RISTRETTO_POINT_LENGTH, }); } let mut compressed = CompressedRistretto([0u8; RISTRETTO_POINT_LENGTH]); diff --git a/src/sign.rs b/src/sign.rs index 1845f89..e368b59 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -130,10 +130,7 @@ impl Signature { } upper[31] &= 127; - Ok(Signature { - R: CompressedRistretto(lower), - s: check_scalar(upper)?, - }) + Ok(Signature { R: CompressedRistretto(lower), s: check_scalar(upper)? }) } /// Deprecated construction of a `Signature` from a slice of bytes @@ -196,9 +193,7 @@ impl SecretKey { { let sig = self.sign(t.clone(), public_key); let sig = Signature::from_bytes(&sig.to_bytes())?; - PublicKey::from_bytes(&public_key.to_bytes())? - .verify(t, &sig) - .map(|()| sig) + PublicKey::from_bytes(&public_key.to_bytes())?.verify(t, &sig).map(|()| sig) } /// Sign a message with this `SecretKey`. @@ -417,9 +412,7 @@ impl Keypair { { let sig = self.sign(t.clone()); let sig = Signature::from_bytes(&sig.to_bytes())?; - PublicKey::from_bytes(&self.public.to_bytes())? - .verify(t, &sig) - .map(|()| sig) + PublicKey::from_bytes(&self.public.to_bytes())?.verify(t, &sig).map(|()| sig) } /// Sign a message with this `SecretKey`, but doublecheck the result. @@ -473,9 +466,7 @@ mod test { "Verification of a signature on a different message passed!" ); assert!( - !keypair - .verify(signing_context(b"bad").bytes(&good), &good_sig) - .is_ok(), + !keypair.verify(signing_context(b"bad").bytes(&good), &good_sig).is_ok(), "Verification of a signature on a different message passed!" ); } @@ -507,27 +498,19 @@ mod test { assert_eq!(bad_sig, bad_sig_d); assert!( - keypair - .verify(ctx.xof(prehashed_good.clone()), &good_sig) - .is_ok(), + keypair.verify(ctx.xof(prehashed_good.clone()), &good_sig).is_ok(), "Verification of a valid signature failed!" ); assert!( - !keypair - .verify(ctx.xof(prehashed_good.clone()), &bad_sig) - .is_ok(), + !keypair.verify(ctx.xof(prehashed_good.clone()), &bad_sig).is_ok(), "Verification of a signature on a different message passed!" ); assert!( - !keypair - .verify(ctx.xof(prehashed_bad.clone()), &good_sig) - .is_ok(), + !keypair.verify(ctx.xof(prehashed_bad.clone()), &good_sig).is_ok(), "Verification of a signature on a different message passed!" ); assert!( - !keypair - .verify(signing_context(b"oops").xof(prehashed_good), &good_sig) - .is_ok(), + !keypair.verify(signing_context(b"oops").xof(prehashed_good), &good_sig).is_ok(), "Verification of a signature on a different message passed!" ); } diff --git a/src/vrf.rs b/src/vrf.rs index 71bb5d4..2cc7c8c 100644 --- a/src/vrf.rs +++ b/src/vrf.rs @@ -12,7 +12,7 @@ //! *Warning* We warn that our VRF construction supports malleable //! outputs via the `*malleable*` methods. These are insecure when //! used in conjunction with our HDKD provided in dervie.rs. -//! Attackers could translate malleable VRF outputs from one soft subkey +//! Attackers could translate malleable VRF outputs from one soft subkey //! to another soft subkey, gaining early knowledge of the VRF output. //! We suggest using either non-malleable VRFs or using implicit //! certificates instead of HDKD when using VRFs. @@ -89,7 +89,7 @@ use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use curve25519_dalek::scalar::Scalar; use curve25519_dalek::traits::{IsIdentity}; // Identity #[cfg(feature = "alloc")] -use curve25519_dalek::traits::{MultiscalarMul,VartimeMultiscalarMul}; +use curve25519_dalek::traits::{MultiscalarMul, VartimeMultiscalarMul}; use merlin::Transcript; @@ -99,7 +99,7 @@ use crate::points::RistrettoBoth; // use crate::errors::SignatureError; /// Value for `kusama` paramater to `*dleq*` methods that yields the VRF for kusama. -/// +/// /// Greg Maxwell argue that nonce generation should hash all parameters /// that challenge generation does in https://moderncrypto.org/mail-archive/curves/2020/001012.html /// We support this position in principle as a defense in depth against @@ -109,23 +109,23 @@ use crate::points::RistrettoBoth; /// We cannot justify add this defense to the deployed VRF because /// several layers already address this attack, including merlin's /// witnesses and that signers normally only sign VRF outputs once. -/// +/// /// We suggest using Greg Maxwell's trick if you use a stand alone DLEQ /// proof though, meaning call `*dleq*` methods with `kusama: false`. /// /// see: https://github.com/w3f/schnorrkel/issues/53 // We currently lack tests for the case when this is false, but you can // rerun cargo test with this set to false for that. -pub const KUSAMA_VRF : bool = true; +pub const KUSAMA_VRF: bool = true; /// Length of VRF output. -pub const VRF_PREOUT_LENGTH : usize = 32; +pub const VRF_PREOUT_LENGTH: usize = 32; /// Length of the short VRF proof which lacks support for batch verification. -pub const VRF_PROOF_LENGTH : usize = 64; +pub const VRF_PROOF_LENGTH: usize = 64; /// Length of the longer VRF proof which supports batch verification. -pub const VRF_PROOF_BATCHABLE_LENGTH : usize = 96; +pub const VRF_PROOF_BATCHABLE_LENGTH: usize = 96; /// `SigningTranscript` helper trait that manages VRF output malleability. /// @@ -140,11 +140,14 @@ pub trait VRFSigningTranscript { fn transcript_with_malleability_addressed(self, publickey: &PublicKey) -> Self::T; } -impl VRFSigningTranscript for T where T: SigningTranscript { +impl VRFSigningTranscript for T +where + T: SigningTranscript, +{ type T = T; #[inline(always)] fn transcript_with_malleability_addressed(mut self, publickey: &PublicKey) -> T { - self.commit_point(b"vrf-nm-pk", publickey.as_compressed()); + self.commit_point(b"vrf-nm-pk", publickey.as_compressed()); self } } @@ -152,8 +155,8 @@ impl VRFSigningTranscript for T where T: SigningTranscript { /// VRF SigningTranscript for malleable VRF outputs. /// /// *Warning* We caution that malleable VRF outputs are insecure when -/// used in conjunction with HDKD, as provided in dervie.rs. -/// Attackers could translate malleable VRF outputs from one soft subkey +/// used in conjunction with HDKD, as provided in dervie.rs. +/// Attackers could translate malleable VRF outputs from one soft subkey /// to another soft subkey, gaining early knowledge of the VRF output. /// We think most VRF applications for which HDKH sounds suitable /// benefit from using implicit certificates instead of HDKD anyways, @@ -161,18 +164,22 @@ impl VRFSigningTranscript for T where T: SigningTranscript { /// We always use non-malleable VRF inputs in our convenience methods. #[derive(Clone)] pub struct Malleable(pub T); -impl VRFSigningTranscript for Malleable where T: SigningTranscript { +impl VRFSigningTranscript for Malleable +where + T: SigningTranscript, +{ type T = T; #[inline(always)] - fn transcript_with_malleability_addressed(self, _publickey: &PublicKey) -> T { self.0 } + fn transcript_with_malleability_addressed(self, _publickey: &PublicKey) -> T { + self.0 + } } - /// Create a malleable VRF input point by hashing a transcript to a point. /// /// *Warning* We caution that malleable VRF inputs are insecure when -/// used in conjunction with HDKD, as provided in dervie.rs. -/// Attackers could translate malleable VRF outputs from one soft subkey +/// used in conjunction with HDKD, as provided in dervie.rs. +/// Attackers could translate malleable VRF outputs from one soft subkey /// to another soft subkey, gaining early knowledge of the VRF output. /// We think most VRF applications for which HDKH sounds suitable /// benefit from using implicit certificates instead of HDKD anyways, @@ -187,22 +194,23 @@ pub fn vrf_malleable_hash(mut t: T) -> RistrettoBoth { impl PublicKey { /// Create a non-malleable VRF input point by hashing a transcript to a point. pub fn vrf_hash(&self, t: T) -> RistrettoBoth - where T: VRFSigningTranscript { + where + T: VRFSigningTranscript, + { vrf_malleable_hash(t.transcript_with_malleability_addressed(self)) } /// Pair a non-malleable VRF output with the hash of the given transcript. pub fn vrf_attach_hash(&self, output: VRFPreOut, t: T) -> SignatureResult - where T: VRFSigningTranscript { - output.attach_input_hash(self,t) + where + T: VRFSigningTranscript, + { + output.attach_input_hash(self, t) } } /// VRF pre-output, possibly unverified. -#[deprecated( - since = "0.9.2", - note = "Please use VRFPreOut instead of VRFOutput" -)] +#[deprecated(since = "0.9.2", note = "Please use VRFPreOut instead of VRFOutput")] pub type VRFOutput = VRFPreOut; /// VRF pre-output, possibly unverified. @@ -243,7 +251,7 @@ impl VRFPreOut { return Err(SignatureError::BytesLengthError { name: "VRFPreOut", description: VRFPreOut::DESCRIPTION, - length: VRF_PREOUT_LENGTH + length: VRF_PREOUT_LENGTH, }); } let mut bits: [u8; 32] = [0u8; 32]; @@ -253,10 +261,14 @@ impl VRFPreOut { /// Pair a non-malleable VRF output with the hash of the given transcript. pub fn attach_input_hash(&self, public: &PublicKey, t: T) -> SignatureResult - where T: VRFSigningTranscript { + where + T: VRFSigningTranscript, + { let input = public.vrf_hash(t); - let output = RistrettoBoth::from_bytes_ser("VRFPreOut", VRFPreOut::DESCRIPTION, &self.0) ?; - if output.as_point().is_identity() { return Err(SignatureError::PointDecompressionError); } + let output = RistrettoBoth::from_bytes_ser("VRFPreOut", VRFPreOut::DESCRIPTION, &self.0)?; + if output.as_point().is_identity() { + return Err(SignatureError::PointDecompressionError); + } Ok(VRFInOut { input, output }) } } @@ -290,7 +302,7 @@ impl SecretKey { /// and note that `vrf_create_from_point` cannot check for /// problematic inputs like `attach_input_hash` does. pub fn vrf_create_from_compressed_point(&self, input: &VRFPreOut) -> SignatureResult { - let input = RistrettoBoth::from_compressed(CompressedRistretto(input.0)) ?; + let input = RistrettoBoth::from_compressed(CompressedRistretto(input.0))?; Ok(self.vrf_create_from_point(input)) } } @@ -339,7 +351,7 @@ impl VRFInOut { /// by Bernardo David, Peter Gazi, Aggelos Kiayias, and Alexander Russell. pub fn make_bytes>(&self, context: &[u8]) -> B { let mut t = Transcript::new(b"VRFResult"); - t.append_message(b"",context); + t.append_message(b"", context); self.commit(&mut t); let mut seed = B::default(); t.challenge_bytes(b"", seed.as_mut()); @@ -384,10 +396,16 @@ impl VRFInOut { // Very insecure hack except for our commit_witness_bytes below struct ZeroFakeRng; impl rand_core::RngCore for ZeroFakeRng { - fn next_u32(&mut self) -> u32 { panic!() } - fn next_u64(&mut self) -> u64 { panic!() } + fn next_u32(&mut self) -> u32 { + panic!() + } + fn next_u64(&mut self) -> u64 { + panic!() + } fn fill_bytes(&mut self, dest: &mut [u8]) { - for i in dest.iter_mut() { *i = 0; } + for i in dest.iter_mut() { + *i = 0; + } } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { self.fill_bytes(dest); @@ -397,7 +415,7 @@ impl VRFInOut { impl rand_core::CryptoRng for ZeroFakeRng {} let mut t = Transcript::new(b"VRFResult"); - t.append_message(b"",context); + t.append_message(b"", context); self.commit(&mut t); t.build_rng().finalize(&mut ZeroFakeRng) } @@ -438,11 +456,13 @@ impl PublicKey { p.borrow().commit(&mut t); } - let zf = || ps.iter().map(|p| { - let mut t0 = t.clone(); - p.borrow().commit(&mut t0); - challenge_scalar_128(t0) - }); + let zf = || { + ps.iter().map(|p| { + let mut t0 = t.clone(); + p.borrow().commit(&mut t0); + challenge_scalar_128(t0) + }) + }; #[cfg(feature = "alloc")] let zs: Vec = zf().collect(); #[cfg(feature = "alloc")] @@ -451,11 +471,15 @@ impl PublicKey { // We need actual fns here because closures cannot easily take // closures as arguments, due to Rust lacking polymorphic // closures but giving all closures unique types. - fn get_input(p: &VRFInOut) -> &RistrettoPoint { p.input.as_point() } - fn get_output(p: &VRFInOut) -> &RistrettoPoint { p.output.as_point() } + fn get_input(p: &VRFInOut) -> &RistrettoPoint { + p.input.as_point() + } + fn get_output(p: &VRFInOut) -> &RistrettoPoint { + p.output.as_point() + } #[cfg(feature = "alloc")] let go = |io: fn(p: &VRFInOut) -> &RistrettoPoint| { - let ps = ps.iter().map( |p| io(p.borrow()) ); + let ps = ps.iter().map(|p| io(p.borrow())); RistrettoBoth::from_point(if vartime { RistrettoPoint::vartime_multiscalar_mul(zf(), ps) } else { @@ -467,14 +491,14 @@ impl PublicKey { let _ = vartime; // ignore unused variable use curve25519_dalek::traits::Identity; let mut acc = RistrettoPoint::identity(); - for (z,p) in zf().zip(ps) { + for (z, p) in zf().zip(ps) { acc += z * io(p.borrow()); } RistrettoBoth::from_point(acc) }; - let input = go( get_input ); - let output = go( get_output ); + let input = go(get_input); + let output = go(get_output); VRFInOut { input, output } } } @@ -509,7 +533,7 @@ impl VRFProof { return Err(SignatureError::BytesLengthError { name: "VRFProof", description: VRFProof::DESCRIPTION, - length: VRF_PROOF_LENGTH + length: VRF_PROOF_LENGTH, }); } let mut c: [u8; 32] = [0u8; 32]; @@ -518,8 +542,8 @@ impl VRFProof { c.copy_from_slice(&bytes[..32]); s.copy_from_slice(&bytes[32..]); - let c = crate::scalar_from_canonical_bytes(c).ok_or(SignatureError::ScalarFormatError) ?; - let s = crate::scalar_from_canonical_bytes(s).ok_or(SignatureError::ScalarFormatError) ?; + let c = crate::scalar_from_canonical_bytes(c).ok_or(SignatureError::ScalarFormatError)?; + let s = crate::scalar_from_canonical_bytes(s).ok_or(SignatureError::ScalarFormatError)?; Ok(VRFProof { c, s }) } } @@ -573,24 +597,35 @@ impl VRFProofBatchable { Hr.copy_from_slice(&bytes[32..64]); s.copy_from_slice(&bytes[64..96]); - let s = crate::scalar_from_canonical_bytes(s).ok_or(SignatureError::ScalarFormatError) ?; + let s = crate::scalar_from_canonical_bytes(s).ok_or(SignatureError::ScalarFormatError)?; Ok(VRFProofBatchable { R: CompressedRistretto(R), Hr: CompressedRistretto(Hr), s }) } /// Return the shortened `VRFProof` for retransmitting in not batched situations #[allow(non_snake_case)] - pub fn shorten_dleq(&self, mut t: T, public: &PublicKey, p: &VRFInOut, kusama: bool) -> VRFProof - where T: SigningTranscript, + pub fn shorten_dleq( + &self, + mut t: T, + public: &PublicKey, + p: &VRFInOut, + kusama: bool, + ) -> VRFProof + where + T: SigningTranscript, { t.proto_name(b"DLEQProof"); // t.commit_point(b"vrf:g",constants::RISTRETTO_BASEPOINT_TABLE.basepoint().compress()); t.commit_point(b"vrf:h", p.input.as_compressed()); - if !kusama { t.commit_point(b"vrf:pk", public.as_compressed()); } + if !kusama { + t.commit_point(b"vrf:pk", public.as_compressed()); + } t.commit_point(b"vrf:R=g^r", &self.R); t.commit_point(b"vrf:h^r", &self.Hr); - if kusama { t.commit_point(b"vrf:pk", public.as_compressed()); } + if kusama { + t.commit_point(b"vrf:pk", public.as_compressed()); + } t.commit_point(b"vrf:h^sk", p.output.as_compressed()); VRFProof { @@ -604,12 +639,17 @@ impl VRFProofBatchable { /// TODO: Avoid the error path here by avoiding decompressing, /// either locally here, or more likely by decompressing /// `VRFPreOut` in deserialization. - pub fn shorten_vrf( &self, public: &PublicKey, t: T, out: &VRFPreOut) - -> SignatureResult - where T: VRFSigningTranscript, + pub fn shorten_vrf( + &self, + public: &PublicKey, + t: T, + out: &VRFPreOut, + ) -> SignatureResult + where + T: VRFSigningTranscript, { - let p = out.attach_input_hash(public,t) ?; // Avoidable errors if decompressed earlier - let t0 = Transcript::new(b"VRF"); // We have context in t and another hear confuses batching + let p = out.attach_input_hash(public, t)?; // Avoidable errors if decompressed earlier + let t0 = Transcript::new(b"VRF"); // We have context in t and another hear confuses batching Ok(self.shorten_dleq(t0, public, &p, KUSAMA_VRF)) } } @@ -624,24 +664,33 @@ impl Keypair { /// using one of the `vrf_create_*` methods on `SecretKey`. /// If so, we produce a proof that this multiplication was done correctly. #[allow(non_snake_case)] - pub fn dleq_proove(&self, mut t: T, p: &VRFInOut, kusama: bool) -> (VRFProof, VRFProofBatchable) + pub fn dleq_proove( + &self, + mut t: T, + p: &VRFInOut, + kusama: bool, + ) -> (VRFProof, VRFProofBatchable) where T: SigningTranscript, { t.proto_name(b"DLEQProof"); // t.commit_point(b"vrf:g",constants::RISTRETTO_BASEPOINT_TABLE.basepoint().compress()); t.commit_point(b"vrf:h", p.input.as_compressed()); - if !kusama { t.commit_point(b"vrf:pk", self.public.as_compressed()); } + if !kusama { + t.commit_point(b"vrf:pk", self.public.as_compressed()); + } // We compute R after adding pk and all h. - let mut r = t.witness_scalar(b"proving\x000",&[&self.secret.nonce]); + let mut r = t.witness_scalar(b"proving\x000", &[&self.secret.nonce]); let R = (&r * constants::RISTRETTO_BASEPOINT_TABLE).compress(); t.commit_point(b"vrf:R=g^r", &R); let Hr = (r * p.input.as_point()).compress(); t.commit_point(b"vrf:h^r", &Hr); - if kusama { t.commit_point(b"vrf:pk", self.public.as_compressed()); } + if kusama { + t.commit_point(b"vrf:pk", self.public.as_compressed()); + } // We add h^sk last to save an allocation if we ever need to hash multiple h together. t.commit_point(b"vrf:h^sk", p.output.as_compressed()); @@ -660,26 +709,27 @@ impl Keypair { /// VRFs repeatedly until they win some contest. In these case, /// you should probably use vrf_sign_n_check to gain access to the /// `VRFInOut` from `vrf_create_hash` first, and then avoid computing - /// the proof whenever you do not win. + /// the proof whenever you do not win. pub fn vrf_sign(&self, t: T) -> (VRFInOut, VRFProof, VRFProofBatchable) - where T: VRFSigningTranscript, + where + T: VRFSigningTranscript, { - self.vrf_sign_extra(t,Transcript::new(b"VRF")) + self.vrf_sign_extra(t, Transcript::new(b"VRF")) // We have context in t and another hear confuses batching } - /// Run VRF on one single input transcript and an extra message transcript, + /// Run VRF on one single input transcript and an extra message transcript, /// producing the outpus and corresponding short proof. - pub fn vrf_sign_extra(&self, t: T, extra: E) -> (VRFInOut, VRFProof, VRFProofBatchable) - where T: VRFSigningTranscript, - E: SigningTranscript, + pub fn vrf_sign_extra(&self, t: T, extra: E) -> (VRFInOut, VRFProof, VRFProofBatchable) + where + T: VRFSigningTranscript, + E: SigningTranscript, { let p = self.vrf_create_hash(t); let (proof, proof_batchable) = self.dleq_proove(extra, &p, KUSAMA_VRF); (p, proof, proof_batchable) } - /// Run VRF on one single input transcript, producing the outpus /// and corresponding short proof only if the result first passes /// some check. @@ -688,27 +738,39 @@ impl Keypair { /// VRFs repeatedly until they win some contest. In these case, /// you might use this function to short circuit computing the full /// proof. - pub fn vrf_sign_after_check(&self, t: T, mut check: F) - -> Option<(VRFInOut, VRFProof, VRFProofBatchable)> - where T: VRFSigningTranscript, - F: FnMut(&VRFInOut) -> bool, + pub fn vrf_sign_after_check( + &self, + t: T, + mut check: F, + ) -> Option<(VRFInOut, VRFProof, VRFProofBatchable)> + where + T: VRFSigningTranscript, + F: FnMut(&VRFInOut) -> bool, { - self.vrf_sign_extra_after_check(t, - |io| if check(io) { Some(Transcript::new(b"VRF")) } else { None } - ) + self.vrf_sign_extra_after_check(t, |io| { + if check(io) { + Some(Transcript::new(b"VRF")) + } else { + None + } + }) } /// Run VRF on one single input transcript, producing the outpus /// and corresponding short proof only if the result first passes /// some check, which itself returns an extra message transcript. - pub fn vrf_sign_extra_after_check(&self, t: T, mut check: F) - -> Option<(VRFInOut, VRFProof, VRFProofBatchable)> - where T: VRFSigningTranscript, - E: SigningTranscript, - F: FnMut(&VRFInOut) -> Option, + pub fn vrf_sign_extra_after_check( + &self, + t: T, + mut check: F, + ) -> Option<(VRFInOut, VRFProof, VRFProofBatchable)> + where + T: VRFSigningTranscript, + E: SigningTranscript, + F: FnMut(&VRFInOut) -> Option, { let p = self.vrf_create_hash(t); - let extra = check(&p) ?; + let extra = check(&p)?; let (proof, proof_batchable) = self.dleq_proove(extra, &p, KUSAMA_VRF); Some((p, proof, proof_batchable)) } @@ -735,16 +797,18 @@ impl Keypair { /// if even the hash of the message being signed is sensitive then /// you might reimplement some constant time variant. #[cfg(feature = "alloc")] - pub fn vrfs_sign_extra(&self, ts: I, extra: E) -> (Box<[VRFInOut]>, VRFProof, VRFProofBatchable) + pub fn vrfs_sign_extra( + &self, + ts: I, + extra: E, + ) -> (Box<[VRFInOut]>, VRFProof, VRFProofBatchable) where T: VRFSigningTranscript, E: SigningTranscript, I: IntoIterator, { - let ps = ts.into_iter() - .map(|t| self.vrf_create_hash(t)) - .collect::>(); - let p = self.public.vrfs_merge(&ps,true); + let ps = ts.into_iter().map(|t| self.vrf_create_hash(t)).collect::>(); + let p = self.public.vrfs_merge(&ps, true); let (proof, proof_batchable) = self.dleq_proove(extra, &p, KUSAMA_VRF); (ps.into_boxed_slice(), proof, proof_batchable) } @@ -775,7 +839,9 @@ impl PublicKey { t.proto_name(b"DLEQProof"); // t.commit_point(b"vrf:g",constants::RISTRETTO_BASEPOINT_TABLE.basepoint().compress()); t.commit_point(b"vrf:h", p.input.as_compressed()); - if !kusama { t.commit_point(b"vrf:pk", self.as_compressed()); } + if !kusama { + t.commit_point(b"vrf:pk", self.as_compressed()); + } // We recompute R aka u from the proof // let R = (&proof.c * self.as_point()) + (&proof.s * &constants::RISTRETTO_BASEPOINT_TABLE); @@ -783,7 +849,8 @@ impl PublicKey { &proof.c, self.as_point(), &proof.s, - ).compress(); + ) + .compress(); t.commit_point(b"vrf:R=g^r", &R); // We also recompute h^r aka u using the proof @@ -800,7 +867,9 @@ impl PublicKey { let Hr = Hr.compress(); t.commit_point(b"vrf:h^r", &Hr); - if kusama { t.commit_point(b"vrf:pk", self.as_compressed()); } + if kusama { + t.commit_point(b"vrf:pk", self.as_compressed()); + } // We add h^sk last to save an allocation if we ever need to hash multiple h together. t.commit_point(b"vrf:h^sk", p.output.as_compressed()); @@ -820,28 +889,29 @@ impl PublicKey { out: &VRFPreOut, proof: &VRFProof, ) -> SignatureResult<(VRFInOut, VRFProofBatchable)> { - self.vrf_verify_extra(t,out,proof,Transcript::new(b"VRF")) + self.vrf_verify_extra(t, out, proof, Transcript::new(b"VRF")) } /// Verify VRF proof for one single input transcript and corresponding output. - pub fn vrf_verify_extra( + pub fn vrf_verify_extra( &self, t: T, out: &VRFPreOut, proof: &VRFProof, extra: E, - ) -> SignatureResult<(VRFInOut, VRFProofBatchable)> - where T: VRFSigningTranscript, - E: SigningTranscript, + ) -> SignatureResult<(VRFInOut, VRFProofBatchable)> + where + T: VRFSigningTranscript, + E: SigningTranscript, { - let p = out.attach_input_hash(self,t)?; + let p = out.attach_input_hash(self, t)?; let proof_batchable = self.dleq_verify(extra, &p, proof, KUSAMA_VRF)?; Ok((p, proof_batchable)) } /// Verify a common VRF short proof for several input transcripts and corresponding outputs. #[cfg(feature = "alloc")] - pub fn vrfs_verify( + pub fn vrfs_verify( &self, transcripts: I, outs: &[O], @@ -852,12 +922,12 @@ impl PublicKey { I: IntoIterator, O: Borrow, { - self.vrfs_verify_extra(transcripts,outs,proof,Transcript::new(b"VRF")) + self.vrfs_verify_extra(transcripts, outs, proof, Transcript::new(b"VRF")) } /// Verify a common VRF short proof for several input transcripts and corresponding outputs. #[cfg(feature = "alloc")] - pub fn vrfs_verify_extra( + pub fn vrfs_verify_extra( &self, transcripts: I, outs: &[O], @@ -871,15 +941,14 @@ impl PublicKey { O: Borrow, { let mut ts = transcripts.into_iter(); - let ps = ts.by_ref().zip(outs) - .map(|(t, out)| out.borrow().attach_input_hash(self,t)) + let ps = ts + .by_ref() + .zip(outs) + .map(|(t, out)| out.borrow().attach_input_hash(self, t)) .collect::>>()?; assert!(ts.next().is_none(), "Too few VRF outputs for VRF inputs."); - assert!( - ps.len() == outs.len(), - "Too few VRF inputs for VRF outputs." - ); - let p = self.vrfs_merge(&ps[..],true); + assert!(ps.len() == outs.len(), "Too few VRF inputs for VRF outputs."); + let p = self.vrfs_merge(&ps[..], true); let proof_batchable = self.dleq_verify(extra, &p, proof, KUSAMA_VRF)?; Ok((ps.into_boxed_slice(), proof_batchable)) } @@ -917,8 +986,8 @@ pub fn dleq_verify_batch( // inout and putput points, and the system random number generator. let mut csprng = { let mut t = Transcript::new(b"VB-RNG"); - for (pk,p) in public_keys.iter().zip(ps) { - t.commit_point(b"",pk.as_compressed()); + for (pk, p) in public_keys.iter().zip(ps) { + t.commit_point(b"", pk.as_compressed()); p.commit(&mut t); } t.build_rng().finalize(&mut getrandom_or_panic()) @@ -934,39 +1003,47 @@ pub fn dleq_verify_batch( }; let zz: Vec = proofs.iter().map(rnd_128bit_scalar).collect(); - let z_s: Vec = zz.iter().zip(proofs) - .map(|(z, proof)| z * proof.s) - .collect(); + let z_s: Vec = zz.iter().zip(proofs).map(|(z, proof)| z * proof.s).collect(); // Compute the basepoint coefficient, ∑ s[i] z[i] (mod l) let B_coefficient: Scalar = z_s.iter().sum(); let t0 = Transcript::new(b"VRF"); - let z_c: Vec = zz.iter().enumerate() - .map( |(i, z)| z * proofs[i].shorten_dleq(t0.clone(), &public_keys[i], &ps[i], kusama).c ) + let z_c: Vec = zz + .iter() + .enumerate() + .map(|(i, z)| z * proofs[i].shorten_dleq(t0.clone(), &public_keys[i], &ps[i], kusama).c) .collect(); // Compute (∑ z[i] s[i] (mod l)) B + ∑ (z[i] c[i] (mod l)) A[i] - ∑ z[i] R[i] = 0 let mut b = RistrettoPoint::optional_multiscalar_mul( - zz.iter().map(|z| -z) - .chain(z_c.iter().cloned()) - .chain(once(B_coefficient)), - proofs.iter().map(|proof| proof.R.decompress()) + zz.iter().map(|z| -z).chain(z_c.iter().cloned()).chain(once(B_coefficient)), + proofs + .iter() + .map(|proof| proof.R.decompress()) .chain(public_keys.iter().map(|pk| Some(*pk.as_point()))) .chain(once(Some(constants::RISTRETTO_BASEPOINT_POINT))), - ).map(|id| id.is_identity()).unwrap_or(false); + ) + .map(|id| id.is_identity()) + .unwrap_or(false); // Compute (∑ z[i] s[i] (mod l)) Input[i] + ∑ (z[i] c[i] (mod l)) Output[i] - ∑ z[i] Hr[i] = 0 b &= RistrettoPoint::optional_multiscalar_mul( - zz.iter().map(|z| -z) - .chain(z_c) - .chain(z_s), - proofs.iter().map(|proof| proof.Hr.decompress()) + zz.iter().map(|z| -z).chain(z_c).chain(z_s), + proofs + .iter() + .map(|proof| proof.Hr.decompress()) .chain(ps.iter().map(|p| Some(*p.output.as_point()))) .chain(ps.iter().map(|p| Some(*p.input.as_point()))), - ).map(|id| id.is_identity()).unwrap_or(false); + ) + .map(|id| id.is_identity()) + .unwrap_or(false); - if b { Ok(()) } else { Err(SignatureError::EquationFalse) } + if b { + Ok(()) + } else { + Err(SignatureError::EquationFalse) + } } /// Batch verify VRFs by different signers @@ -984,16 +1061,14 @@ where I: IntoIterator, { let mut ts = transcripts.into_iter(); - let ps = ts.by_ref() + let ps = ts + .by_ref() .zip(publickeys) .zip(outs) - .map(|((t, pk), out)| out.attach_input_hash(pk,t)) + .map(|((t, pk), out)| out.attach_input_hash(pk, t)) .collect::>>()?; assert!(ts.next().is_none(), "Too few VRF outputs for VRF inputs."); - assert!( - ps.len() == outs.len(), - "Too few VRF inputs for VRF outputs." - ); + assert!(ps.len() == outs.len(), "Too few VRF inputs for VRF outputs."); if dleq_verify_batch(&ps[..], proofs, publickeys, KUSAMA_VRF).is_ok() { Ok(ps.into_boxed_slice()) } else { @@ -1021,26 +1096,19 @@ mod tests { let out1 = &io1.to_preout(); assert_eq!( proof1, - proof1batchable - .shorten_vrf(&keypair1.public, ctx.bytes(msg), &out1) - .unwrap(), + proof1batchable.shorten_vrf(&keypair1.public, ctx.bytes(msg), &out1).unwrap(), "Oops `shorten_vrf` failed" ); - let (io1too, proof1too) = keypair1.public.vrf_verify(ctx.bytes(msg), &out1, &proof1) + let (io1too, proof1too) = keypair1 + .public + .vrf_verify(ctx.bytes(msg), &out1, &proof1) .expect("Correct VRF verification failed!"); - assert_eq!( - io1too, io1, - "Output differs between signing and verification!" - ); + assert_eq!(io1too, io1, "Output differs between signing and verification!"); assert_eq!( proof1batchable, proof1too, "VRF verification yielded incorrect batchable proof" ); - assert_eq!( - keypair1.vrf_sign(ctx.bytes(msg)).0, - io1, - "Rerunning VRF gave different output" - ); + assert_eq!(keypair1.vrf_sign(ctx.bytes(msg)).0, io1, "Rerunning VRF gave different output"); assert!( keypair1.public.vrf_verify(ctx.bytes(b"not meow"), &out1, &proof1).is_err(), @@ -1067,16 +1135,16 @@ mod tests { let out1 = &io1.to_preout(); assert_eq!( proof1, - proof1batchable.shorten_vrf(&keypair1.public, Malleable(ctx.bytes(msg)), &out1).unwrap(), + proof1batchable + .shorten_vrf(&keypair1.public, Malleable(ctx.bytes(msg)), &out1) + .unwrap(), "Oops `shorten_vrf` failed" ); let (io1too, proof1too) = keypair1 - .public.vrf_verify(Malleable(ctx.bytes(msg)), &out1, &proof1) + .public + .vrf_verify(Malleable(ctx.bytes(msg)), &out1, &proof1) .expect("Correct VRF verification failed!"); - assert_eq!( - io1too, io1, - "Output differs between signing and verification!" - ); + assert_eq!(io1too, io1, "Output differs between signing and verification!"); assert_eq!( proof1batchable, proof1too, "VRF verification yielded incorrect batchable proof" @@ -1087,7 +1155,10 @@ mod tests { "Rerunning VRF gave different output" ); assert!( - keypair1.public.vrf_verify(Malleable(ctx.bytes(b"not meow")), &out1, &proof1).is_err(), + keypair1 + .public + .vrf_verify(Malleable(ctx.bytes(b"not meow")), &out1, &proof1) + .is_err(), "VRF verification with incorrect message passed!" ); @@ -1116,23 +1187,15 @@ mod tests { proofs12.1.shorten_dleq(t0.clone(), &keypair1.public, &io12, KUSAMA_VRF), "Oops `shorten_dleq` failed" ); - assert!(keypair1 - .public - .dleq_verify(t0.clone(), &io12, &proofs12.0, KUSAMA_VRF) - .is_ok()); - assert!(keypair2 - .public - .dleq_verify(t0.clone(), &io21, &proofs21.0, KUSAMA_VRF) - .is_ok()); + assert!(keypair1.public.dleq_verify(t0.clone(), &io12, &proofs12.0, KUSAMA_VRF).is_ok()); + assert!(keypair2.public.dleq_verify(t0.clone(), &io21, &proofs21.0, KUSAMA_VRF).is_ok()); } #[cfg(feature = "alloc")] #[test] fn vrfs_merged_and_batched() { let mut csprng = rand_core::OsRng; - let keypairs: Vec = (0..4) - .map(|_| Keypair::generate_with(&mut csprng)) - .collect(); + let keypairs: Vec = (0..4).map(|_| Keypair::generate_with(&mut csprng)).collect(); let ctx = signing_context(b"yo!"); let messages: [&[u8; 4]; 2] = [b"meow", b"woof"]; @@ -1145,52 +1208,42 @@ mod tests { )>>(); for (k, (ios, proof, proof_batchable)) in keypairs.iter().zip(&ios_n_proofs) { - let outs = ios - .iter() - .map(|io| io.to_preout()) - .collect::>(); + let outs = ios.iter().map(|io| io.to_preout()).collect::>(); let (ios_too, proof_too) = k .public .vrfs_verify(ts(), &outs, &proof) .expect("Valid VRF output verification failed!"); - assert_eq!( - ios_too, *ios, - "Output differs between signing and verification!" - ); - assert_eq!( - proof_too, *proof_batchable, - "Returning batchable proof failed!" - ); + assert_eq!(ios_too, *ios, "Output differs between signing and verification!"); + assert_eq!(proof_too, *proof_batchable, "Returning batchable proof failed!"); } for (k, (ios, proof, _proof_batchable)) in keypairs.iter().zip(&ios_n_proofs) { - let outs = ios.iter() - .rev() - .map(|io| io.to_preout()) - .collect::>(); + let outs = ios.iter().rev().map(|io| io.to_preout()).collect::>(); assert!( k.public.vrfs_verify(ts(), &outs, &proof).is_err(), "Incorrect VRF output verification passed!" ); } for (k, (ios, proof, _proof_batchable)) in keypairs.iter().rev().zip(&ios_n_proofs) { - let outs = ios.iter() - .map(|io| io.to_preout()) - .collect::>(); + let outs = ios.iter().map(|io| io.to_preout()).collect::>(); assert!( k.public.vrfs_verify(ts(), &outs, &proof).is_err(), "VRF output verification by a different signer passed!" ); } - let mut ios = keypairs.iter().enumerate() - .map(|(i, keypair)| keypair.public.vrfs_merge(&ios_n_proofs[i].0,true)) + let mut ios = keypairs + .iter() + .enumerate() + .map(|(i, keypair)| keypair.public.vrfs_merge(&ios_n_proofs[i].0, true)) .collect::>(); - let mut proofs = ios_n_proofs.iter() + let mut proofs = ios_n_proofs + .iter() .map(|(_ios, _proof, proof_batchable)| proof_batchable.clone()) .collect::>(); - let mut public_keys = keypairs.iter() + let mut public_keys = keypairs + .iter() .map(|keypair| keypair.public.clone()) .collect::>(); From e1d0c1132252516702aece20d4bc6227d6d25c46 Mon Sep 17 00:00:00 2001 From: Fiono11 Date: Mon, 29 Apr 2024 18:57:48 +0100 Subject: [PATCH 6/8] Reformatting --- src/context.rs | 82 +++++++++++--------------------- src/keys.rs | 20 +++----- src/lib.rs | 8 ++-- src/points.rs | 37 +++++---------- src/sign.rs | 3 +- src/vrf.rs | 126 ++++++++++++++++++------------------------------- 6 files changed, 99 insertions(+), 177 deletions(-) diff --git a/src/context.rs b/src/context.rs index 0ef937d..bc6f06c 100644 --- a/src/context.rs +++ b/src/context.rs @@ -97,49 +97,33 @@ pub trait SigningTranscript { /// alternative implementations. #[rustfmt::skip] impl SigningTranscript for &mut T -where - T: SigningTranscript + ?Sized, +where T: SigningTranscript + ?Sized, { #[inline(always)] - fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8]) { - (**self).commit_bytes(label, bytes) - } + fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8]) + { (**self).commit_bytes(label,bytes) } #[inline(always)] - fn proto_name(&mut self, label: &'static [u8]) { - (**self).proto_name(label) - } + fn proto_name(&mut self, label: &'static [u8]) + { (**self).proto_name(label) } #[inline(always)] - fn commit_point(&mut self, label: &'static [u8], compressed: &CompressedRistretto) { - (**self).commit_point(label, compressed) - } + fn commit_point(&mut self, label: &'static [u8], compressed: &CompressedRistretto) + { (**self).commit_point(label, compressed) } #[inline(always)] - fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]) { - (**self).challenge_bytes(label, dest) - } + fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]) + { (**self).challenge_bytes(label,dest) } #[inline(always)] - fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar { - (**self).challenge_scalar(label) - } + fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar + { (**self).challenge_scalar(label) } #[inline(always)] - fn witness_scalar(&self, label: &'static [u8], nonce_seeds: &[&[u8]]) -> Scalar { - (**self).witness_scalar(label, nonce_seeds) - } + fn witness_scalar(&self, label: &'static [u8], nonce_seeds: &[&[u8]]) -> Scalar + { (**self).witness_scalar(label,nonce_seeds) } #[inline(always)] - fn witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]]) { - (**self).witness_bytes(label, dest, nonce_seeds) - } + fn witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]]) + { (**self).witness_bytes(label,dest,nonce_seeds) } #[inline(always)] - fn witness_bytes_rng( - &self, - label: &'static [u8], - dest: &mut [u8], - nonce_seeds: &[&[u8]], - rng: R, - ) where - R: RngCore + CryptoRng, - { - (**self).witness_bytes_rng(label, dest, nonce_seeds, rng) - } + fn witness_bytes_rng(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], rng: R) + where R: RngCore+CryptoRng + { (**self).witness_bytes_rng(label,dest,nonce_seeds,rng) } } /// We delegate `SigningTranscript` methods to the corresponding @@ -385,29 +369,19 @@ where impl SigningTranscript for SigningTranscriptWithRng where T: SigningTranscript, R: RngCore+CryptoRng { - fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8]) { - self.t.commit_bytes(label, bytes) - } + fn commit_bytes(&mut self, label: &'static [u8], bytes: &[u8]) + { self.t.commit_bytes(label, bytes) } - fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]) { - self.t.challenge_bytes(label, dest) - } + fn challenge_bytes(&mut self, label: &'static [u8], dest: &mut [u8]) + { self.t.challenge_bytes(label, dest) } - fn witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]]) { - self.witness_bytes_rng(label, dest, nonce_seeds, &mut *self.rng.borrow_mut()) - } + fn witness_bytes(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]]) + { self.witness_bytes_rng(label, dest, nonce_seeds, &mut *self.rng.borrow_mut()) } + + fn witness_bytes_rng(&self, label: &'static [u8], dest: &mut [u8], nonce_seeds: &[&[u8]], rng: RR) + where RR: RngCore+CryptoRng + { self.t.witness_bytes_rng(label,dest,nonce_seeds,rng) } - fn witness_bytes_rng( - &self, - label: &'static [u8], - dest: &mut [u8], - nonce_seeds: &[&[u8]], - rng: RR, - ) where - RR: RngCore + CryptoRng, - { - self.t.witness_bytes_rng(label, dest, nonce_seeds, rng) - } } /// Attach a `CryptoRng` to a `SigningTranscript` to replace the default `ThreadRng`. diff --git a/src/keys.rs b/src/keys.rs index e1d2927..8f14aa7 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -630,29 +630,21 @@ impl PublicKey { const DESCRIPTION: &'static str = "A Ristretto Schnorr public key represented as a 32-byte Ristretto compressed point"; /// Access the compressed Ristretto form - pub fn as_compressed(&self) -> &CompressedRistretto { - self.0.as_compressed() - } + pub fn as_compressed(&self) -> &CompressedRistretto { self.0.as_compressed() } /// Extract the compressed Ristretto form - pub fn into_compressed(self) -> CompressedRistretto { - self.0.into_compressed() - } + pub fn into_compressed(self) -> CompressedRistretto { self.0.into_compressed() } /// Access the point form - pub fn as_point(&self) -> &RistrettoPoint { - self.0.as_point() - } + pub fn as_point(&self) -> &RistrettoPoint { self.0.as_point() } /// Extract the point form - pub fn into_point(self) -> RistrettoPoint { - self.0.into_point() - } + pub fn into_point(self) -> RistrettoPoint { self.0.into_point() } /// Decompress into the `PublicKey` format that also retains the /// compressed form. pub fn from_compressed(compressed: CompressedRistretto) -> SignatureResult { - Ok(PublicKey(RistrettoBoth::from_compressed(compressed)?)) + Ok(PublicKey(RistrettoBoth::from_compressed(compressed) ?)) } /// Compress into the `PublicKey` format that also retains the @@ -703,7 +695,7 @@ impl PublicKey { /// is an `SignatureError` describing the error that occurred. #[inline] pub fn from_bytes(bytes: &[u8]) -> SignatureResult { - Ok(PublicKey(RistrettoBoth::from_bytes_ser("PublicKey", PublicKey::DESCRIPTION, bytes)?)) + Ok(PublicKey(RistrettoBoth::from_bytes_ser("PublicKey",PublicKey::DESCRIPTION,bytes) ?)) } } diff --git a/src/lib.rs b/src/lib.rs index e9e9b4f..5208751 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -237,16 +237,16 @@ use curve25519_dalek::scalar::Scalar; #[macro_use] mod serdey; -pub mod keys; pub mod points; mod scalars; +pub mod keys; -pub mod cert; pub mod context; -pub mod derive; -pub mod errors; pub mod sign; pub mod vrf; +pub mod derive; +pub mod cert; +pub mod errors; #[cfg(all(feature = "alloc", feature = "aead"))] pub mod olaf; diff --git a/src/points.rs b/src/points.rs index 2201d62..c2af472 100644 --- a/src/points.rs +++ b/src/points.rs @@ -59,30 +59,22 @@ impl RistrettoBoth { // equality comparisons. /// Access the compressed Ristretto form - pub fn as_compressed(&self) -> &CompressedRistretto { - &self.compressed - } + pub fn as_compressed(&self) -> &CompressedRistretto { &self.compressed } /// Extract the compressed Ristretto form - pub fn into_compressed(self) -> CompressedRistretto { - self.compressed - } + pub fn into_compressed(self) -> CompressedRistretto { self.compressed } /// Access the point form - pub fn as_point(&self) -> &RistrettoPoint { - &self.point - } + pub fn as_point(&self) -> &RistrettoPoint { &self.point } /// Extract the point form - pub fn into_point(self) -> RistrettoPoint { - self.point - } + pub fn into_point(self) -> RistrettoPoint { self.point } /// Decompress into the `RistrettoBoth` format that also retains the /// compressed form. pub fn from_compressed(compressed: CompressedRistretto) -> SignatureResult { Ok(RistrettoBoth { - point: compressed.decompress().ok_or(SignatureError::PointDecompressionError)?, + point: compressed.decompress().ok_or(SignatureError::PointDecompressionError) ?, compressed, }) } @@ -90,7 +82,10 @@ impl RistrettoBoth { /// Compress into the `RistrettoBoth` format that also retains the /// uncompressed form. pub fn from_point(point: RistrettoPoint) -> RistrettoBoth { - RistrettoBoth { compressed: point.compress(), point } + RistrettoBoth { + compressed: point.compress(), + point, + } } /// Convert this public key to a byte array. @@ -129,21 +124,15 @@ impl RistrettoBoth { /// is an `SignatureError` describing the error that occurred. #[inline] pub fn from_bytes(bytes: &[u8]) -> SignatureResult { - RistrettoBoth::from_bytes_ser("RistrettoPoint", RistrettoBoth::DESCRIPTION, bytes) + RistrettoBoth::from_bytes_ser("RistrettoPoint",RistrettoBoth::DESCRIPTION,bytes) } /// Variant of `RistrettoBoth::from_bytes` that propagates more informative errors. #[inline] - pub fn from_bytes_ser( - name: &'static str, - description: &'static str, - bytes: &[u8], - ) -> SignatureResult { + pub fn from_bytes_ser(name: &'static str, description: &'static str, bytes: &[u8]) -> SignatureResult { if bytes.len() != RISTRETTO_POINT_LENGTH { - return Err(SignatureError::BytesLengthError { - name, - description, - length: RISTRETTO_POINT_LENGTH, + return Err(SignatureError::BytesLengthError{ + name, description, length: RISTRETTO_POINT_LENGTH, }); } let mut compressed = CompressedRistretto([0u8; RISTRETTO_POINT_LENGTH]); diff --git a/src/sign.rs b/src/sign.rs index 4ab2e53..1169b19 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -18,7 +18,6 @@ use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use curve25519_dalek::scalar::Scalar; use super::*; - use crate::context::{SigningTranscript, SigningContext}; // === Actual signature type === // @@ -429,8 +428,8 @@ impl Keypair { #[cfg(test)] mod test { - use curve25519_dalek::digest::Update; use sha3::Shake128; + use curve25519_dalek::digest::{Update}; use super::super::*; diff --git a/src/vrf.rs b/src/vrf.rs index 885d0ee..eb3a9e6 100644 --- a/src/vrf.rs +++ b/src/vrf.rs @@ -398,16 +398,10 @@ impl VRFInOut { struct ZeroFakeRng; #[rustfmt::skip] impl rand_core::RngCore for ZeroFakeRng { - fn next_u32(&mut self) -> u32 { - panic!() - } - fn next_u64(&mut self) -> u64 { - panic!() - } + fn next_u32(&mut self) -> u32 { panic!() } + fn next_u64(&mut self) -> u64 { panic!() } fn fill_bytes(&mut self, dest: &mut [u8]) { - for i in dest.iter_mut() { - *i = 0; - } + for i in dest.iter_mut() { *i = 0; } } fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { self.fill_bytes(dest); @@ -459,13 +453,11 @@ impl PublicKey { p.borrow().commit(&mut t); } - let zf = || { - ps.iter().map(|p| { - let mut t0 = t.clone(); - p.borrow().commit(&mut t0); - challenge_scalar_128(t0) - }) - }; + let zf = || ps.iter().map(|p| { + let mut t0 = t.clone(); + p.borrow().commit(&mut t0); + challenge_scalar_128(t0) + }); #[cfg(feature = "alloc")] let zs: Vec = zf().collect(); #[cfg(feature = "alloc")] @@ -474,15 +466,11 @@ impl PublicKey { // We need actual fns here because closures cannot easily take // closures as arguments, due to Rust lacking polymorphic // closures but giving all closures unique types. - fn get_input(p: &VRFInOut) -> &RistrettoPoint { - p.input.as_point() - } - fn get_output(p: &VRFInOut) -> &RistrettoPoint { - p.output.as_point() - } + fn get_input(p: &VRFInOut) -> &RistrettoPoint { p.input.as_point() } + fn get_output(p: &VRFInOut) -> &RistrettoPoint { p.output.as_point() } #[cfg(feature = "alloc")] let go = |io: fn(p: &VRFInOut) -> &RistrettoPoint| { - let ps = ps.iter().map(|p| io(p.borrow())); + let ps = ps.iter().map( |p| io(p.borrow()) ); RistrettoBoth::from_point(if vartime { RistrettoPoint::vartime_multiscalar_mul(zf(), ps) } else { @@ -494,14 +482,14 @@ impl PublicKey { let _ = vartime; // ignore unused variable use curve25519_dalek::traits::Identity; let mut acc = RistrettoPoint::identity(); - for (z, p) in zf().zip(ps) { + for (z,p) in zf().zip(ps) { acc += z * io(p.borrow()); } RistrettoBoth::from_point(acc) }; - let input = go(get_input); - let output = go(get_output); + let input = go( get_input ); + let output = go( get_output ); VRFInOut { input, output } } } @@ -613,16 +601,12 @@ impl VRFProofBatchable { t.proto_name(b"DLEQProof"); // t.commit_point(b"vrf:g",constants::RISTRETTO_BASEPOINT_TABLE.basepoint().compress()); t.commit_point(b"vrf:h", p.input.as_compressed()); - if !kusama { - t.commit_point(b"vrf:pk", public.as_compressed()); - } + if !kusama { t.commit_point(b"vrf:pk", public.as_compressed()); } t.commit_point(b"vrf:R=g^r", &self.R); t.commit_point(b"vrf:h^r", &self.Hr); - if kusama { - t.commit_point(b"vrf:pk", public.as_compressed()); - } + if kusama { t.commit_point(b"vrf:pk", public.as_compressed()); } t.commit_point(b"vrf:h^sk", p.output.as_compressed()); VRFProof { @@ -669,21 +653,17 @@ impl Keypair { t.proto_name(b"DLEQProof"); // t.commit_point(b"vrf:g",constants::RISTRETTO_BASEPOINT_TABLE.basepoint().compress()); t.commit_point(b"vrf:h", p.input.as_compressed()); - if !kusama { - t.commit_point(b"vrf:pk", self.public.as_compressed()); - } + if !kusama { t.commit_point(b"vrf:pk", self.public.as_compressed()); } // We compute R after adding pk and all h. - let mut r = t.witness_scalar(b"proving\x000", &[&self.secret.nonce]); + let mut r = t.witness_scalar(b"proving\x000",&[&self.secret.nonce]); let R = (&r * constants::RISTRETTO_BASEPOINT_TABLE).compress(); t.commit_point(b"vrf:R=g^r", &R); let Hr = (r * p.input.as_point()).compress(); t.commit_point(b"vrf:h^r", &Hr); - if kusama { - t.commit_point(b"vrf:pk", self.public.as_compressed()); - } + if kusama { t.commit_point(b"vrf:pk", self.public.as_compressed()); } // We add h^sk last to save an allocation if we ever need to hash multiple h together. t.commit_point(b"vrf:h^sk", p.output.as_compressed()); @@ -833,9 +813,7 @@ impl PublicKey { t.proto_name(b"DLEQProof"); // t.commit_point(b"vrf:g",constants::RISTRETTO_BASEPOINT_TABLE.basepoint().compress()); t.commit_point(b"vrf:h", p.input.as_compressed()); - if !kusama { - t.commit_point(b"vrf:pk", self.as_compressed()); - } + if !kusama { t.commit_point(b"vrf:pk", self.as_compressed()); } // We recompute R aka u from the proof // let R = (&proof.c * self.as_point()) + (&proof.s * &constants::RISTRETTO_BASEPOINT_TABLE); @@ -843,8 +821,7 @@ impl PublicKey { &proof.c, self.as_point(), &proof.s, - ) - .compress(); + ).compress(); t.commit_point(b"vrf:R=g^r", &R); // We also recompute h^r aka u using the proof @@ -861,9 +838,7 @@ impl PublicKey { let Hr = Hr.compress(); t.commit_point(b"vrf:h^r", &Hr); - if kusama { - t.commit_point(b"vrf:pk", self.as_compressed()); - } + if kusama { t.commit_point(b"vrf:pk", self.as_compressed()); } // We add h^sk last to save an allocation if we ever need to hash multiple h together. t.commit_point(b"vrf:h^sk", p.output.as_compressed()); @@ -936,14 +911,15 @@ impl PublicKey { O: Borrow, { let mut ts = transcripts.into_iter(); - let ps = ts - .by_ref() - .zip(outs) - .map(|(t, out)| out.borrow().attach_input_hash(self, t)) + let ps = ts.by_ref().zip(outs) + .map(|(t, out)| out.borrow().attach_input_hash(self,t)) .collect::>>()?; assert!(ts.next().is_none(), "Too few VRF outputs for VRF inputs."); - assert!(ps.len() == outs.len(), "Too few VRF inputs for VRF outputs."); - let p = self.vrfs_merge(&ps[..], true); + assert!( + ps.len() == outs.len(), + "Too few VRF inputs for VRF outputs." + ); + let p = self.vrfs_merge(&ps[..],true); let proof_batchable = self.dleq_verify(extra, &p, proof, KUSAMA_VRF)?; Ok((ps.into_boxed_slice(), proof_batchable)) } @@ -982,8 +958,8 @@ pub fn dleq_verify_batch( // inout and putput points, and the system random number generator. let mut csprng = { let mut t = Transcript::new(b"VB-RNG"); - for (pk, p) in public_keys.iter().zip(ps) { - t.commit_point(b"", pk.as_compressed()); + for (pk,p) in public_keys.iter().zip(ps) { + t.commit_point(b"",pk.as_compressed()); p.commit(&mut t); } t.build_rng().finalize(&mut getrandom_or_panic()) @@ -999,47 +975,39 @@ pub fn dleq_verify_batch( }; let zz: Vec = proofs.iter().map(rnd_128bit_scalar).collect(); - let z_s: Vec = zz.iter().zip(proofs).map(|(z, proof)| z * proof.s).collect(); + let z_s: Vec = zz.iter().zip(proofs) + .map(|(z, proof)| z * proof.s) + .collect(); // Compute the basepoint coefficient, ∑ s[i] z[i] (mod l) let B_coefficient: Scalar = z_s.iter().sum(); let t0 = Transcript::new(b"VRF"); - let z_c: Vec = zz - .iter() - .enumerate() - .map(|(i, z)| z * proofs[i].shorten_dleq(t0.clone(), &public_keys[i], &ps[i], kusama).c) + let z_c: Vec = zz.iter().enumerate() + .map( |(i, z)| z * proofs[i].shorten_dleq(t0.clone(), &public_keys[i], &ps[i], kusama).c ) .collect(); // Compute (∑ z[i] s[i] (mod l)) B + ∑ (z[i] c[i] (mod l)) A[i] - ∑ z[i] R[i] = 0 let mut b = RistrettoPoint::optional_multiscalar_mul( - zz.iter().map(|z| -z).chain(z_c.iter().cloned()).chain(once(B_coefficient)), - proofs - .iter() - .map(|proof| proof.R.decompress()) + zz.iter().map(|z| -z) + .chain(z_c.iter().cloned()) + .chain(once(B_coefficient)), + proofs.iter().map(|proof| proof.R.decompress()) .chain(public_keys.iter().map(|pk| Some(*pk.as_point()))) .chain(once(Some(constants::RISTRETTO_BASEPOINT_POINT))), - ) - .map(|id| id.is_identity()) - .unwrap_or(false); + ).map(|id| id.is_identity()).unwrap_or(false); // Compute (∑ z[i] s[i] (mod l)) Input[i] + ∑ (z[i] c[i] (mod l)) Output[i] - ∑ z[i] Hr[i] = 0 b &= RistrettoPoint::optional_multiscalar_mul( - zz.iter().map(|z| -z).chain(z_c).chain(z_s), - proofs - .iter() - .map(|proof| proof.Hr.decompress()) + zz.iter().map(|z| -z) + .chain(z_c) + .chain(z_s), + proofs.iter().map(|proof| proof.Hr.decompress()) .chain(ps.iter().map(|p| Some(*p.output.as_point()))) .chain(ps.iter().map(|p| Some(*p.input.as_point()))), - ) - .map(|id| id.is_identity()) - .unwrap_or(false); + ).map(|id| id.is_identity()).unwrap_or(false); - if b { - Ok(()) - } else { - Err(SignatureError::EquationFalse) - } + if b { Ok(()) } else { Err(SignatureError::EquationFalse) } } /// Batch verify VRFs by different signers From 5207bd352e45be8a7f749b7032fbbc566bab82c7 Mon Sep 17 00:00:00 2001 From: Fiono11 Date: Wed, 1 May 2024 11:25:31 +0100 Subject: [PATCH 7/8] Remove make_aead usage --- src/aead.rs | 2 +- src/lib.rs | 4 ++-- src/olaf/simplpedpop.rs | 25 +++++++++++++++++++++---- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/aead.rs b/src/aead.rs index 6484028..d47cf6f 100644 --- a/src/aead.rs +++ b/src/aead.rs @@ -41,7 +41,7 @@ use crate::context::SigningTranscript; use crate::cert::AdaptorCertPublic; -pub(crate) fn make_aead(mut t: T) -> AEAD +fn make_aead(mut t: T) -> AEAD where T: SigningTranscript, AEAD: KeyInit, diff --git a/src/lib.rs b/src/lib.rs index 5208751..d1be587 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -248,10 +248,10 @@ pub mod derive; pub mod cert; pub mod errors; -#[cfg(all(feature = "alloc", feature = "aead"))] +//#[cfg(all(feature = "alloc", feature = "aead"))] pub mod olaf; -#[cfg(all(feature = "aead", feature = "getrandom"))] +//#[cfg(all(feature = "aead", feature = "getrandom"))] pub mod aead; #[cfg(feature = "alloc")] diff --git a/src/olaf/simplpedpop.rs b/src/olaf/simplpedpop.rs index e001c0b..61c76cb 100644 --- a/src/olaf/simplpedpop.rs +++ b/src/olaf/simplpedpop.rs @@ -14,7 +14,8 @@ //! after getting out of scope (security reasons). Public messages are destined to all the other participants, while private //! messages are destined to a single participant. -use crate::{aead::make_aead, context::SigningTranscript, SecretKey, Signature}; +use crate::{context::SigningTranscript, SecretKey, Signature}; +use aead::{generic_array::GenericArray, KeyInit, KeySizeUser}; use alloc::{collections::BTreeSet, vec::Vec}; use chacha20poly1305::{aead::Aead, ChaCha20Poly1305, Nonce}; use curve25519_dalek::{constants::RISTRETTO_BASEPOINT_POINT, RistrettoPoint, Scalar}; @@ -144,9 +145,17 @@ impl SecretShare { let mut bytes = [0; 12]; transcript.challenge_bytes(b"nonce", &mut bytes); - let cipher: ChaCha20Poly1305 = make_aead::(transcript); let nonce = Nonce::from_slice(&bytes[..]); + let mut key: GenericArray< + u8, + ::KeySize, + > = Default::default(); + + transcript.challenge_bytes(b"", key.as_mut_slice()); + + let cipher = ChaCha20Poly1305::new(&key); + let ciphertext: Vec = cipher .encrypt(nonce, &self.0.as_bytes()[..]) .map_err(DKGError::EncryptionError)?; @@ -176,8 +185,16 @@ impl EncryptedSecretShare { let mut bytes = [0; 12]; transcript.challenge_bytes(b"nonce", &mut bytes); - let cipher: ChaCha20Poly1305 = make_aead::(transcript); - let nonce = Nonce::from_slice(&bytes[..]); + let nonce = Nonce::from_slice(&bytes); + + let mut key: GenericArray< + u8, + ::KeySize, + > = Default::default(); + + transcript.challenge_bytes(b"", key.as_mut_slice()); + + let cipher = ChaCha20Poly1305::new(&key); let plaintext = cipher.decrypt(nonce, &self.0[..]).map_err(DKGError::DecryptionError)?; From 8d455b657dcee65742c0fcdeb6710472bf6742fa Mon Sep 17 00:00:00 2001 From: Fiono11 Date: Wed, 1 May 2024 11:26:01 +0100 Subject: [PATCH 8/8] Fix CI build --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d1be587..5208751 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -248,10 +248,10 @@ pub mod derive; pub mod cert; pub mod errors; -//#[cfg(all(feature = "alloc", feature = "aead"))] +#[cfg(all(feature = "alloc", feature = "aead"))] pub mod olaf; -//#[cfg(all(feature = "aead", feature = "getrandom"))] +#[cfg(all(feature = "aead", feature = "getrandom"))] pub mod aead; #[cfg(feature = "alloc")]