diff --git a/src/checksum.rs b/src/checksum.rs new file mode 100644 index 0000000..6d150a6 --- /dev/null +++ b/src/checksum.rs @@ -0,0 +1,48 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::participant::Identity; +use siphasher::sip::SipHasher24; +use std::borrow::Borrow; +use std::error; +use std::fmt; +use std::hash::Hasher; + +pub const CHECKSUM_LEN: usize = 8; + +pub type Checksum = u64; + +#[derive(Clone, Debug)] +pub struct ChecksumError; + +impl fmt::Display for ChecksumError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt("checksum doesn't match", f) + } +} + +impl error::Error for ChecksumError {} + +#[must_use] +pub fn input_checksum(input_data: &[u8], signing_participants: &[I]) -> Checksum +where + I: Borrow, +{ + let mut signing_participants = signing_participants + .iter() + .map(Borrow::borrow) + .collect::>(); + signing_participants.sort_unstable(); + signing_participants.dedup(); + + let mut hasher = SipHasher24::new(); + hasher.write(input_data); + + for id in signing_participants { + hasher.write(&id.serialize()); + } + + hasher.finish() +} diff --git a/src/dkg.rs b/src/dkg.rs new file mode 100644 index 0000000..f2cd7aa --- /dev/null +++ b/src/dkg.rs @@ -0,0 +1,166 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::borrow::Borrow; + +use crate::checksum::input_checksum; +use crate::checksum::Checksum; +use crate::checksum::ChecksumError; +use crate::frost::keys::dkg::round1; +use crate::participant::Identity; + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Package { + identity: Identity, + frost_package: round1::Package, + checksum: Checksum, +} + +impl Package { + pub fn new( + identity: Identity, + frost_package: round1::Package, + min_signers: u16, + signing_participants: &[Identity], + ) -> Self { + let checksum = input_checksum(&min_signers.to_le_bytes(), signing_participants); + + Package { + identity, + frost_package, + checksum, + } + } + + pub fn verify_checksum( + &self, + min_signers: u16, + signing_participants: &[I], + ) -> Result<(), ChecksumError> + where + I: Borrow, + { + let computed_checksum = input_checksum(&min_signers.to_le_bytes(), signing_participants); + if self.checksum == computed_checksum { + Ok(()) + } else { + Err(ChecksumError) + } + } + + pub fn checksum(&self) -> Checksum { + self.checksum + } +} + +#[cfg(test)] +mod tests { + use rand::thread_rng; + use reddsa::frost::redjubjub::keys::dkg::part1; + + use crate::participant::Secret; + + use super::Package; + + #[test] + fn test_checksum_variation_with_min_signers() { + let mut rng = thread_rng(); + + let signing_participants = [ + Secret::random(&mut rng).to_identity(), + Secret::random(&mut rng).to_identity(), + Secret::random(&mut rng).to_identity(), + ]; + + let max_signers: u16 = 3; + let min_signers1: u16 = 2; + let min_signers2: u16 = 3; + + let identity = &signing_participants[0]; + + let (_, frost_package_1) = part1( + identity.to_frost_identifier(), + max_signers, + min_signers1, + thread_rng(), + ) + .expect("creating frost round1 package should not fail"); + let (_, frost_package_2) = part1( + identity.to_frost_identifier(), + max_signers, + min_signers2, + thread_rng(), + ) + .expect("creating frost round 1 package should not fail"); + + let package_1 = Package::new( + identity.clone(), + frost_package_1, + min_signers1, + &signing_participants, + ); + + let package_2 = Package::new( + identity.clone(), + frost_package_2, + min_signers2, + &signing_participants, + ); + + assert_ne!(package_1.checksum(), package_2.checksum()); + } + + #[test] + fn test_checksum_variation_with_signing_participants() { + let mut rng = thread_rng(); + + let identity = Secret::random(&mut rng).to_identity(); + + let signing_participants1 = [ + identity.clone(), + Secret::random(&mut rng).to_identity(), + Secret::random(&mut rng).to_identity(), + ]; + + let signing_participants2 = [ + identity.clone(), + Secret::random(&mut rng).to_identity(), + Secret::random(&mut rng).to_identity(), + ]; + + let min_signers: u16 = 2; + let max_signers: u16 = 3; + + let (_, frost_package_1) = part1( + identity.to_frost_identifier(), + max_signers, + min_signers, + thread_rng(), + ) + .expect("creating frost round1 package should not fail"); + let (_, frost_package_2) = part1( + identity.to_frost_identifier(), + max_signers, + min_signers, + thread_rng(), + ) + .expect("creating frost round 1 package should not fail"); + + let package_1 = Package::new( + identity.clone(), + frost_package_1, + min_signers, + &signing_participants1, + ); + + let package_2 = Package::new( + identity.clone(), + frost_package_2, + min_signers, + &signing_participants2, + ); + + assert_ne!(package_1.checksum(), package_2.checksum()); + } +} diff --git a/src/lib.rs b/src/lib.rs index 3220f76..c797339 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,8 @@ #![warn(unused_crate_dependencies)] #![warn(unused_qualifications)] +pub mod checksum; +pub mod dkg; pub mod keys; pub mod multienc; pub mod nonces; diff --git a/src/signing_commitment.rs b/src/signing_commitment.rs index 7417775..73e9d6b 100644 --- a/src/signing_commitment.rs +++ b/src/signing_commitment.rs @@ -2,7 +2,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use crate::frost::keys::SigningShare; +use reddsa::frost::redjubjub::keys::SigningShare; + +use crate::checksum::input_checksum; +use crate::checksum::Checksum; +use crate::checksum::ChecksumError; +use crate::checksum::CHECKSUM_LEN; use crate::frost::round1::NonceCommitment; use crate::frost::round1::SigningCommitments; use crate::nonces::deterministic_signing_nonces; @@ -11,54 +16,13 @@ use crate::participant::Secret; use crate::participant::Signature; use crate::participant::SignatureError; use crate::participant::IDENTITY_LEN; -use siphasher::sip::SipHasher24; use std::borrow::Borrow; -use std::error; -use std::fmt; -use std::hash::Hasher; use std::io; const NONCE_COMMITMENT_LEN: usize = 32; -const CHECKSUM_LEN: usize = 8; pub const AUTHENTICATED_DATA_LEN: usize = IDENTITY_LEN + NONCE_COMMITMENT_LEN * 2 + CHECKSUM_LEN; pub const SIGNING_COMMITMENT_LEN: usize = AUTHENTICATED_DATA_LEN + Signature::BYTE_SIZE; -type Checksum = u64; - -#[derive(Clone, Debug)] -pub struct ChecksumError; - -impl fmt::Display for ChecksumError { - #[inline] - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt("checksum doesn't match", f) - } -} - -impl error::Error for ChecksumError {} - -#[must_use] -fn input_checksum(transaction_hash: &[u8], signing_participants: &[I]) -> Checksum -where - I: Borrow, -{ - let mut signing_participants = signing_participants - .iter() - .map(Borrow::borrow) - .collect::>(); - signing_participants.sort_unstable(); - signing_participants.dedup(); - - let mut hasher = SipHasher24::new(); - hasher.write(transaction_hash); - - for id in signing_participants { - hasher.write(&id.serialize()); - } - - hasher.finish() -} - #[must_use] fn authenticated_data( identity: &Identity,