diff --git a/Cargo.toml b/Cargo.toml index 0cd99eb..349e6ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ chacha20poly1305 = "0.10.1" ed25519-dalek = { version = "2.1.0", features = ["rand_core"] } rand_chacha = { version = "0.3.1", optional = true } rand_core = "0.6.4" -reddsa = { git = "https://github.com/ZcashFoundation/reddsa.git", rev = "311baf8865f6e21527d1f20750d8f2cf5c9e531a", features = ["frost", "frost-rerandomized"] } +reddsa = { git = "https://github.com/ZcashFoundation/reddsa.git", rev = "b9c3107e6ec5333a89a7fa064f2d10f749a90cce", features = ["frost", "frost-rerandomized"] } siphasher = { version = "1.0.0", optional = true } x25519-dalek = { version = "2.0.0", features = ["reusable_secrets", "static_secrets"] } diff --git a/src/dkg/error.rs b/src/dkg/error.rs deleted file mode 100644 index f3597e6..0000000 --- a/src/dkg/error.rs +++ /dev/null @@ -1,46 +0,0 @@ -/* 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::checksum::ChecksumError; -use crate::frost; -use std::fmt; -use std::io; - -#[derive(Debug)] -pub enum Error { - InvalidInput(String), - FrostError(frost::Error), - EncryptionError(io::Error), - DecryptionError(io::Error), - ChecksumError(ChecksumError), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - match self { - Self::InvalidInput(e) => { - write!(f, "invalid input: ")?; - e.fmt(f) - } - Self::FrostError(e) => { - write!(f, "frost error: ")?; - e.fmt(f) - } - Self::EncryptionError(e) => { - write!(f, "encryption error: ")?; - e.fmt(f) - } - Self::DecryptionError(e) => { - write!(f, "decryption error: ")?; - e.fmt(f) - } - Self::ChecksumError(e) => { - write!(f, "checksum error: ")?; - e.fmt(f) - } - } - } -} - -impl std::error::Error for Error {} diff --git a/src/dkg/group_key.rs b/src/dkg/group_key.rs index 17168fc..60bbbd0 100644 --- a/src/dkg/group_key.rs +++ b/src/dkg/group_key.rs @@ -79,7 +79,7 @@ impl GroupSecretKeyShard { } pub fn import(secret: &Secret, exported: &[u8]) -> io::Result { - let bytes = multienc::decrypt(secret, &exported).map_err(io::Error::other)?; + let bytes = multienc::decrypt(secret, exported).map_err(io::Error::other)?; if bytes.len() != GROUP_SECRET_KEY_LEN { return Err(io::Error::other( diff --git a/src/dkg/mod.rs b/src/dkg/mod.rs index 70a737f..8605e2d 100644 --- a/src/dkg/mod.rs +++ b/src/dkg/mod.rs @@ -2,7 +2,6 @@ * 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/. */ -pub mod error; pub mod group_key; pub mod round1; pub mod round2; diff --git a/src/dkg/round1.rs b/src/dkg/round1.rs index 71ada66..e7f9482 100644 --- a/src/dkg/round1.rs +++ b/src/dkg/round1.rs @@ -5,8 +5,8 @@ use crate::checksum::Checksum; use crate::checksum::ChecksumHasher; use crate::checksum::CHECKSUM_LEN; -use crate::dkg::error::Error; use crate::dkg::group_key::GroupSecretKeyShard; +use crate::error::IronfishFrostError; use crate::frost; use crate::frost::keys::dkg::round1::Package; use crate::frost::keys::dkg::round1::SecretPackage; @@ -15,7 +15,6 @@ use crate::frost::Field; use crate::frost::Identifier; use crate::frost::JubjubScalarField; use crate::multienc; -use crate::multienc::read_encrypted_blob; use crate::participant; use crate::participant::Identity; use crate::serde::read_u16; @@ -80,12 +79,13 @@ impl<'a> From<&'a SerializableSecretPackage> for &'a SecretPackage { } impl SerializableSecretPackage { - fn serialize_into(&self, mut writer: W) -> io::Result<()> { + fn serialize_into(&self, mut writer: W) -> Result<(), IronfishFrostError> { writer.write_all(&self.identifier.serialize())?; write_variable_length(&mut writer, &self.coefficients, |writer, scalar| { writer.write_all(&scalar.to_bytes()) })?; - write_variable_length(&mut writer, self.commitment.serialize(), |writer, array| { + let serialized = self.commitment.serialize()?; + write_variable_length(&mut writer, serialized, |writer, array| { writer.write_all(&array) })?; write_u16(&mut writer, self.min_signers)?; @@ -93,10 +93,10 @@ impl SerializableSecretPackage { Ok(()) } - fn deserialize_from(mut reader: R) -> io::Result { + fn deserialize_from(mut reader: R) -> Result { let mut identifier = [0u8; 32]; reader.read_exact(&mut identifier)?; - let identifier = Identifier::deserialize(&identifier).map_err(io::Error::other)?; + let identifier = Identifier::deserialize(&identifier)?; let coefficients = read_variable_length(&mut reader, |reader| { let mut scalar = [0u8; 32]; @@ -112,8 +112,7 @@ impl SerializableSecretPackage { reader.read_exact(&mut array)?; Ok(array) }, - )?) - .map_err(io::Error::other)?; + )?)?; let min_signers = read_u16(&mut reader)?; let max_signers = read_u16(&mut reader)?; @@ -153,8 +152,8 @@ pub fn export_secret_package( pub fn import_secret_package( exported: &[u8], secret: &participant::Secret, -) -> io::Result { - let serialized = multienc::decrypt(secret, &exported).map_err(io::Error::other)?; +) -> Result { + let serialized = multienc::decrypt(secret, exported).map_err(io::Error::other)?; SerializableSecretPackage::deserialize_from(&serialized[..]).map(|pkg| pkg.into()) } @@ -247,22 +246,22 @@ impl PublicPackage { buf } - pub fn serialize_into(&self, mut writer: W) -> io::Result<()> { + pub fn serialize_into(&self, mut writer: W) -> Result<(), IronfishFrostError> { self.identity.serialize_into(&mut writer)?; - let frost_package = self.frost_package.serialize().map_err(io::Error::other)?; + let frost_package = self.frost_package.serialize()?; write_variable_length_bytes(&mut writer, &frost_package)?; - writer.write_all(&self.group_secret_key_shard_encrypted[..])?; + write_variable_length_bytes(&mut writer, &self.group_secret_key_shard_encrypted)?; writer.write_all(&self.checksum.to_le_bytes())?; Ok(()) } - pub fn deserialize_from(mut reader: R) -> io::Result { + pub fn deserialize_from(mut reader: R) -> Result { let identity = Identity::deserialize_from(&mut reader).expect("reading identity failed"); let frost_package = read_variable_length_bytes(&mut reader)?; - let frost_package = Package::deserialize(&frost_package).map_err(io::Error::other)?; + let frost_package = Package::deserialize(&frost_package)?; - let group_secret_key_shard_encrypted = read_encrypted_blob(&mut reader)?; + let group_secret_key_shard_encrypted = read_variable_length_bytes(&mut reader)?; let mut checksum = [0u8; CHECKSUM_LEN]; reader.read_exact(&mut checksum)?; @@ -282,7 +281,7 @@ pub fn round1<'a, I, R>( min_signers: u16, participants: I, mut csrng: R, -) -> Result<(Vec, PublicPackage), Error> +) -> Result<(Vec, PublicPackage), IronfishFrostError> where I: IntoIterator, R: RngCore + CryptoRng, @@ -294,25 +293,21 @@ where let participants = participants; if !participants.contains(&self_identity) { - return Err(Error::InvalidInput( - "participants must include self_identity".to_string(), - )); + return Err(IronfishFrostError::InvalidInput); } - let max_signers = u16::try_from(participants.len()) - .map_err(|_| Error::InvalidInput("too many participants".to_string()))?; + let max_signers = + u16::try_from(participants.len()).map_err(|_| IronfishFrostError::InvalidInput)?; let (secret_package, public_package) = frost::keys::dkg::part1( self_identity.to_frost_identifier(), max_signers, min_signers, &mut csrng, - ) - .map_err(Error::FrostError)?; + )?; let encrypted_secret_package = - export_secret_package(&secret_package, self_identity, &mut csrng) - .map_err(Error::EncryptionError)?; + export_secret_package(&secret_package, self_identity, &mut csrng)?; let group_secret_key_shard = GroupSecretKeyShard::random(&mut csrng); @@ -492,6 +487,13 @@ mod tests { let deserialized = PublicPackage::deserialize_from(&serialized[..]) .expect("package deserialization failed"); + assert_eq!(public_package.identity, deserialized.identity); + assert_eq!(public_package.checksum, deserialized.checksum); + assert_eq!(public_package.frost_package, deserialized.frost_package); + assert_eq!( + public_package.group_secret_key_shard_encrypted, + deserialized.group_secret_key_shard_encrypted + ); assert_eq!(public_package, deserialized); } diff --git a/src/dkg/round2.rs b/src/dkg/round2.rs index 516f5cd..2085cef 100644 --- a/src/dkg/round2.rs +++ b/src/dkg/round2.rs @@ -6,8 +6,8 @@ use crate::checksum::Checksum; use crate::checksum::ChecksumError; use crate::checksum::ChecksumHasher; use crate::checksum::CHECKSUM_LEN; -use crate::dkg::error::Error; use crate::dkg::round1; +use crate::error::IronfishFrostError; use crate::frost; use crate::frost::keys::dkg::round1::Package as Round1Package; use crate::frost::keys::dkg::round2::Package; @@ -82,9 +82,10 @@ impl<'a> From<&'a SerializableSecretPackage> for &'a SecretPackage { } impl SerializableSecretPackage { - fn serialize_into(&self, mut writer: W) -> io::Result<()> { + fn serialize_into(&self, mut writer: W) -> Result<(), IronfishFrostError> { writer.write_all(&self.identifier.serialize())?; - write_variable_length(&mut writer, self.commitment.serialize(), |writer, array| { + let serialized = self.commitment.serialize()?; + write_variable_length(&mut writer, serialized, |writer, array| { writer.write_all(&array) })?; writer.write_all(&self.secret_share.to_bytes())?; @@ -93,10 +94,10 @@ impl SerializableSecretPackage { Ok(()) } - fn deserialize_from(mut reader: R) -> io::Result { + fn deserialize_from(mut reader: R) -> Result { let mut identifier = [0u8; 32]; reader.read_exact(&mut identifier)?; - let identifier = Identifier::deserialize(&identifier).map_err(io::Error::other)?; + let identifier = Identifier::deserialize(&identifier)?; let commitment = VerifiableSecretSharingCommitment::deserialize(read_variable_length( &mut reader, @@ -105,8 +106,7 @@ impl SerializableSecretPackage { reader.read_exact(&mut array)?; Ok(array) }, - )?) - .map_err(io::Error::other)?; + )?)?; let mut scalar = [0u8; 32]; reader.read_exact(&mut scalar)?; @@ -152,8 +152,8 @@ pub fn export_secret_package( pub fn import_secret_package( exported: &[u8], secret: &participant::Secret, -) -> io::Result { - let serialized = multienc::decrypt(secret, &exported).map_err(io::Error::other)?; +) -> Result { + let serialized = multienc::decrypt(secret, exported).map_err(io::Error::other)?; SerializableSecretPackage::deserialize_from(&serialized[..]).map(|pkg| pkg.into()) } @@ -226,19 +226,22 @@ impl PublicPackage { buf } - pub fn serialize_into(&self, mut writer: W) -> io::Result<()> { + pub fn serialize_into(&self, mut writer: W) -> Result<(), IronfishFrostError> { self.sender_identity.serialize_into(&mut writer)?; self.serialize_without_sender_into(writer) } - fn serialize_without_sender_into(&self, mut writer: W) -> io::Result<()> { + fn serialize_without_sender_into( + &self, + mut writer: W, + ) -> Result<(), IronfishFrostError> { self.recipient_identity.serialize_into(&mut writer)?; - let frost_package = self.frost_package.serialize().map_err(io::Error::other)?; + let frost_package = self.frost_package.serialize()?; write_variable_length_bytes(&mut writer, &frost_package)?; - writer.write_all(&self.checksum.to_le_bytes()) + Ok(writer.write_all(&self.checksum.to_le_bytes())?) } - pub fn deserialize_from(mut reader: R) -> io::Result { + pub fn deserialize_from(mut reader: R) -> Result { let sender_identity = Identity::deserialize_from(&mut reader)?; Self::deserialize_without_sender_from(reader, sender_identity) } @@ -246,11 +249,11 @@ impl PublicPackage { fn deserialize_without_sender_from( mut reader: R, sender_identity: Identity, - ) -> io::Result { + ) -> Result { let recipient_identity = Identity::deserialize_from(&mut reader)?; let frost_package = read_variable_length_bytes(&mut reader)?; - let frost_package = Package::deserialize(&frost_package).map_err(io::Error::other)?; + let frost_package = Package::deserialize(&frost_package)?; let mut checksum = [0u8; CHECKSUM_LEN]; reader.read_exact(&mut checksum)?; @@ -310,19 +313,25 @@ impl CombinedPublicPackage { buf } - pub fn serialize_into(&self, mut writer: W) -> io::Result<()> { + pub fn serialize_into(&self, mut writer: W) -> Result<(), IronfishFrostError> { let sender_identity = &self.packages[0].sender_identity; sender_identity.serialize_into(&mut writer)?; - write_variable_length(writer, &self.packages, |writer, pkg| { - pkg.serialize_without_sender_into(writer) - }) + Ok(write_variable_length( + writer, + &self.packages, + |writer, pkg| { + pkg.serialize_without_sender_into(writer) + .map_err(|_| io::Error::other("serialize_into failed")) + }, + )?) } - pub fn deserialize_from(mut reader: R) -> io::Result { + pub fn deserialize_from(mut reader: R) -> Result { let sender_identity = Identity::deserialize_from(&mut reader)?; let packages = read_variable_length(reader, move |reader| { PublicPackage::deserialize_without_sender_from(reader, sender_identity.clone()) + .map_err(|_| io::Error::other("deserialization failed")) })?; Ok(Self { packages }) @@ -343,14 +352,13 @@ pub fn round2<'a, P, R>( round1_secret_package: &[u8], round1_public_packages: P, mut csrng: R, -) -> Result<(Vec, CombinedPublicPackage), Error> +) -> Result<(Vec, CombinedPublicPackage), IronfishFrostError> where P: IntoIterator, R: RngCore + CryptoRng, { let self_identity = secret.to_identity(); - let round1_secret_package = round1::import_secret_package(round1_secret_package, secret) - .map_err(Error::DecryptionError)?; + let round1_secret_package = round1::import_secret_package(round1_secret_package, secret)?; // Extract the min/max signers from the secret package let (min_signers, max_signers) = round1::get_secret_package_signers(&round1_secret_package); @@ -359,11 +367,7 @@ where // Ensure that the number of public packages provided matches max_signers if round1_public_packages.len() != max_signers as usize { - return Err(Error::InvalidInput(format!( - "expected {} public packages, got {}", - max_signers, - round1_public_packages.len() - ))); + return Err(IronfishFrostError::InvalidInput); } let expected_round1_checksum = round1::input_checksum( @@ -375,7 +379,9 @@ where let mut round1_frost_packages: BTreeMap = BTreeMap::new(); for public_package in round1_public_packages.clone() { if public_package.checksum() != expected_round1_checksum { - return Err(Error::ChecksumError(ChecksumError::DkgPublicPackageError)); + return Err(IronfishFrostError::ChecksumError( + ChecksumError::DkgPublicPackageError, + )); } let identity = public_package.identity(); @@ -386,10 +392,7 @@ where .insert(frost_identifier, frost_package) .is_some() { - return Err(Error::InvalidInput(format!( - "multiple public packages provided for identity {}", - public_package.identity() - ))); + return Err(IronfishFrostError::InvalidInput); } identities.insert(frost_identifier, identity); @@ -411,13 +414,11 @@ where // Run the FROST DKG round 2 let (round2_secret_package, round2_packages) = - frost::keys::dkg::part2(round1_secret_package.clone(), &round1_frost_packages) - .map_err(Error::FrostError)?; + frost::keys::dkg::part2(round1_secret_package.clone(), &round1_frost_packages)?; // Encrypt the secret package let encrypted_secret_package = - export_secret_package(&round2_secret_package, &self_identity, &mut csrng) - .map_err(Error::EncryptionError)?; + export_secret_package(&round2_secret_package, &self_identity, &mut csrng)?; // Convert the Identifier->Package map to an Identity->PublicPackage map let mut round2_public_packages = Vec::new(); @@ -679,7 +680,7 @@ mod tests { ); match result { - Err(Error::InvalidInput(_)) => (), + Err(IronfishFrostError::InvalidInput) => (), _ => panic!("dkg round2 should have failed with InvalidInput"), } } @@ -707,7 +708,7 @@ mod tests { // We can use `assert_matches` once it's stabilized match result { - Err(Error::InvalidInput(_)) => (), + Err(IronfishFrostError::InvalidInput) => (), _ => panic!("dkg round2 should have failed with InvalidInput"), } } diff --git a/src/dkg/round3.rs b/src/dkg/round3.rs index e93a04d..1a01bd8 100644 --- a/src/dkg/round3.rs +++ b/src/dkg/round3.rs @@ -3,12 +3,12 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::checksum::ChecksumError; -use crate::dkg::error::Error; use crate::dkg::group_key::GroupSecretKey; use crate::dkg::group_key::GroupSecretKeyShard; use crate::dkg::round1; use crate::dkg::round2; use crate::dkg::round2::import_secret_package; +use crate::error::IronfishFrostError; use crate::frost::keys::dkg::part3; use crate::frost::keys::KeyPackage; use crate::frost::keys::PublicKeyPackage as FrostPublicKeyPackage; @@ -73,11 +73,8 @@ impl PublicKeyPackage { } #[cfg(feature = "std")] - pub fn serialize_into(&self, mut writer: W) -> io::Result<()> { - let frost_public_key_package = self - .frost_public_key_package - .serialize() - .map_err(io::Error::other)?; + pub fn serialize_into(&self, mut writer: W) -> Result<(), IronfishFrostError> { + let frost_public_key_package = self.frost_public_key_package.serialize()?; write_variable_length_bytes(&mut writer, &frost_public_key_package)?; write_variable_length(&mut writer, &self.identities, |writer, identity| { identity.serialize_into(writer) @@ -88,11 +85,10 @@ impl PublicKeyPackage { } #[cfg(feature = "std")] - pub fn deserialize_from(mut reader: R) -> io::Result { + pub fn deserialize_from(mut reader: R) -> Result { let frost_public_key_package = read_variable_length_bytes(&mut reader)?; let frost_public_key_package = - FrostPublicKeyPackage::deserialize(&frost_public_key_package) - .map_err(io::Error::other)?; + FrostPublicKeyPackage::deserialize(&frost_public_key_package)?; let identities = read_variable_length(&mut reader, |reader| Identity::deserialize_from(reader))?; let min_signers = read_u16(&mut reader)?; @@ -110,14 +106,13 @@ pub fn round3<'a, P, Q>( round2_secret_package: &[u8], round1_public_packages: P, round2_public_packages: Q, -) -> Result<(KeyPackage, PublicKeyPackage, GroupSecretKey), Error> +) -> Result<(KeyPackage, PublicKeyPackage, GroupSecretKey), IronfishFrostError> where P: IntoIterator, Q: IntoIterator, { let identity = secret.to_identity(); - let round2_secret_package = - import_secret_package(round2_secret_package, secret).map_err(Error::DecryptionError)?; + let round2_secret_package = import_secret_package(round2_secret_package, secret)?; let round1_public_packages = round1_public_packages.into_iter().collect::>(); let round2_public_packages = round2_public_packages .into_iter() @@ -129,20 +124,12 @@ where // Ensure that the number of public packages provided matches max_signers let expected_round1_packages = max_signers as usize; if round1_public_packages.len() != expected_round1_packages { - return Err(Error::InvalidInput(format!( - "expected {} round 1 public packages, got {}", - expected_round1_packages, - round1_public_packages.len() - ))); + return Err(IronfishFrostError::InvalidInput); } let expected_round2_packages = expected_round1_packages.saturating_sub(1); if round2_public_packages.len() != expected_round2_packages { - return Err(Error::InvalidInput(format!( - "expected {} round 2 public packages, got {}", - expected_round2_packages, - round2_public_packages.len() - ))); + return Err(IronfishFrostError::InvalidInput); } let expected_round1_checksum = round1::input_checksum( @@ -156,7 +143,9 @@ where for public_package in round1_public_packages.iter() { if public_package.checksum() != expected_round1_checksum { - return Err(Error::ChecksumError(ChecksumError::DkgPublicPackageError)); + return Err(IronfishFrostError::ChecksumError( + ChecksumError::DkgPublicPackageError, + )); } let identity = public_package.identity(); @@ -167,15 +156,10 @@ where .insert(frost_identifier, frost_package) .is_some() { - return Err(Error::InvalidInput(format!( - "multiple round 1 public packages provided for identity {}", - public_package.identity() - ))); + return Err(IronfishFrostError::InvalidInput); } - let gsk_shard = public_package - .group_secret_key_shard(secret) - .map_err(Error::DecryptionError)?; + let gsk_shard = public_package.group_secret_key_shard(secret)?; gsk_shards.push(gsk_shard); identities.push(identity.clone()); } @@ -187,9 +171,7 @@ where // inputs round1_frost_packages .remove(&identity.to_frost_identifier()) - .ok_or_else(|| { - Error::InvalidInput("missing round 1 public package for own identity".to_string()) - })?; + .ok_or_else(|| IronfishFrostError::InvalidInput)?; let expected_round2_checksum = round2::input_checksum(round1_public_packages.iter().map(Borrow::borrow)); @@ -197,14 +179,13 @@ where let mut round2_frost_packages = BTreeMap::new(); for public_package in round2_public_packages.iter() { if public_package.checksum() != expected_round2_checksum { - return Err(Error::ChecksumError(ChecksumError::DkgPublicPackageError)); + return Err(IronfishFrostError::ChecksumError( + ChecksumError::DkgPublicPackageError, + )); } if !identity.eq(public_package.recipient_identity()) { - return Err(Error::InvalidInput(format!( - "round 2 public package does not have the correct recipient identity {:?}", - public_package.recipient_identity().serialize() - ))); + return Err(IronfishFrostError::InvalidInput); } let frost_identifier = public_package.sender_identity().to_frost_identifier(); @@ -214,10 +195,7 @@ where .insert(frost_identifier, frost_package) .is_some() { - return Err(Error::InvalidInput(format!( - "multiple round 2 public packages provided for identity {}", - public_package.sender_identity() - ))); + return Err(IronfishFrostError::InvalidInput); } } @@ -227,8 +205,7 @@ where &round2_secret_package, &round1_frost_packages, &round2_frost_packages, - ) - .map_err(Error::FrostError)?; + )?; let public_key_package = PublicKeyPackage::from_frost(public_key_package, identities, min_signers); @@ -244,9 +221,9 @@ where mod tests { use super::round3; use super::PublicKeyPackage; - use crate::dkg::error::Error; use crate::dkg::round1; use crate::dkg::round2; + use crate::error::IronfishFrostError; use crate::participant::Secret; use hex_literal::hex; use rand::thread_rng; @@ -347,7 +324,7 @@ mod tests { ); match result { - Err(Error::InvalidInput(_)) => (), + Err(IronfishFrostError::InvalidInput) => (), _ => panic!("dkg round3 should have failed with InvalidInput"), } } @@ -391,7 +368,7 @@ mod tests { ); match result { - Err(Error::ChecksumError(_)) => (), + Err(IronfishFrostError::ChecksumError(_)) => (), _ => panic!("dkg round3 should have failed with ChecksumError"), } } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..440a11f --- /dev/null +++ b/src/error.rs @@ -0,0 +1,40 @@ +/* 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 reddsa::frost::redjubjub::frost::Error as FrostError; +use reddsa::frost::redjubjub::JubjubBlake2b512; + +use crate::io; + +#[cfg(feature = "signing")] +use crate::checksum::ChecksumError; + +#[derive(Debug)] +pub enum IronfishFrostError { + InvalidInput, + StdError, + IoError(io::Error), + FrostError(FrostError), + SignatureError(ed25519_dalek::SignatureError), + #[cfg(feature = "signing")] + ChecksumError(ChecksumError), +} + +impl From> for IronfishFrostError { + fn from(error: FrostError) -> Self { + IronfishFrostError::FrostError(error) + } +} + +impl From for IronfishFrostError { + fn from(error: io::Error) -> Self { + IronfishFrostError::IoError(error) + } +} + +impl From for IronfishFrostError { + fn from(error: ed25519_dalek::SignatureError) -> Self { + IronfishFrostError::SignatureError(error) + } +} diff --git a/src/lib.rs b/src/lib.rs index 8ae6651..994fbad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ mod serde; #[cfg(feature = "signing")] mod checksum; +pub mod error; pub mod multienc; pub mod participant; diff --git a/src/multienc.rs b/src/multienc.rs index 8e8d03b..d522e96 100644 --- a/src/multienc.rs +++ b/src/multienc.rs @@ -63,8 +63,8 @@ where let recipients = recipients.into_iter(); let metadata_len = metadata_size(recipients.len()); let mut result = vec![0u8; metadata_len + data.len()]; - let (metadata, ciphertext) = result.split_at_mut(metadata_len); + let (metadata, ciphertext) = result.split_at_mut(metadata_len); ciphertext.copy_from_slice(data); encrypt_in_place(ciphertext, metadata, recipients, csrng).expect("failed to encrypt data"); diff --git a/src/nonces.rs b/src/nonces.rs index 2c195f2..36d36f5 100644 --- a/src/nonces.rs +++ b/src/nonces.rs @@ -105,7 +105,7 @@ mod tests { #[test] fn same_input() { - let secret = SigningShare::deserialize(*b"some signing share.............\0").unwrap(); + let secret = SigningShare::deserialize(b"some signing share.............\0").unwrap(); let transaction_hash = b"some hash"; let p1 = Secret::random(thread_rng()).to_identity(); let p2 = Secret::random(thread_rng()).to_identity(); @@ -122,7 +122,7 @@ mod tests { #[test] fn different_participants_order() { - let secret = SigningShare::deserialize(*b"some signing share.............\0").unwrap(); + let secret = SigningShare::deserialize(b"some signing share.............\0").unwrap(); let transaction_hash = b"some hash"; let p1 = Secret::random(thread_rng()).to_identity(); let p2 = Secret::random(thread_rng()).to_identity(); @@ -140,7 +140,7 @@ mod tests { #[test] fn repeated_participants() { - let secret = SigningShare::deserialize(*b"some signing share.............\0").unwrap(); + let secret = SigningShare::deserialize(b"some signing share.............\0").unwrap(); let transaction_hash = b"some hash"; let p1 = Secret::random(thread_rng()).to_identity(); let p2 = Secret::random(thread_rng()).to_identity(); @@ -158,8 +158,8 @@ mod tests { #[test] fn different_shares() { - let secret1 = SigningShare::deserialize(*b"some signing share.............\0").unwrap(); - let secret2 = SigningShare::deserialize(*b"some other signing share.......\0").unwrap(); + let secret1 = SigningShare::deserialize(b"some signing share.............\0").unwrap(); + let secret2 = SigningShare::deserialize(b"some other signing share.......\0").unwrap(); let transaction_hash = b"some hash"; let p1 = Secret::random(thread_rng()).to_identity(); let p2 = Secret::random(thread_rng()).to_identity(); @@ -176,7 +176,7 @@ mod tests { #[test] fn different_transactions() { - let secret = SigningShare::deserialize(*b"some signing share.............\0").unwrap(); + let secret = SigningShare::deserialize(b"some signing share.............\0").unwrap(); let transaction_hash1 = b"some hash"; let transaction_hash2 = b"some other hash"; let p1 = Secret::random(thread_rng()).to_identity(); @@ -194,7 +194,7 @@ mod tests { #[test] fn different_participants() { - let secret = SigningShare::deserialize(*b"some signing share.............\0").unwrap(); + let secret = SigningShare::deserialize(b"some signing share.............\0").unwrap(); let transaction_hash = b"some hash"; let p1 = Secret::random(thread_rng()).to_identity(); let p2 = Secret::random(thread_rng()).to_identity(); diff --git a/src/signature_share.rs b/src/signature_share.rs index a0f019d..d55a26b 100644 --- a/src/signature_share.rs +++ b/src/signature_share.rs @@ -6,7 +6,10 @@ use std::io; use reddsa::frost::redjubjub::round2::SignatureShare as FrostSignatureShare; -use crate::participant::{Identity, IDENTITY_LEN}; +use crate::{ + error::IronfishFrostError, + participant::{Identity, IDENTITY_LEN}, +}; const FROST_SIGNATURE_SHARE_LEN: usize = 32; pub const SIGNATURE_SHARE_SERIALIZATION_LEN: usize = IDENTITY_LEN + FROST_SIGNATURE_SHARE_LEN; @@ -50,13 +53,12 @@ impl SignatureShare { writer.write_all(&signature_share_bytes) } - pub fn deserialize_from(mut reader: R) -> io::Result { + pub fn deserialize_from(mut reader: R) -> Result { let identity = Identity::deserialize_from(&mut reader)?; let mut signature_share_bytes = [0u8; FROST_SIGNATURE_SHARE_LEN]; reader.read_exact(&mut signature_share_bytes)?; - let frost_signature_share = - FrostSignatureShare::deserialize(signature_share_bytes).map_err(io::Error::other)?; + let frost_signature_share = FrostSignatureShare::deserialize(&signature_share_bytes[..])?; Ok(SignatureShare { identity, diff --git a/src/signing_commitment.rs b/src/signing_commitment.rs index 46fafd9..8709803 100644 --- a/src/signing_commitment.rs +++ b/src/signing_commitment.rs @@ -6,6 +6,7 @@ use crate::checksum::Checksum; use crate::checksum::ChecksumError; use crate::checksum::ChecksumHasher; use crate::checksum::CHECKSUM_LEN; +use crate::error::IronfishFrostError; use crate::frost::keys::SigningShare; use crate::frost::round1::NonceCommitment; use crate::frost::round1::SigningCommitments; @@ -13,7 +14,6 @@ use crate::nonces::deterministic_signing_nonces; use crate::participant::Identity; use crate::participant::Secret; use crate::participant::Signature; -use crate::participant::SignatureError; use crate::participant::IDENTITY_LEN; use std::borrow::Borrow; use std::hash::Hasher; @@ -45,17 +45,16 @@ where hasher.finish() } -#[must_use] fn authenticated_data( identity: &Identity, raw_commitments: &SigningCommitments, checksum: Checksum, -) -> [u8; AUTHENTICATED_DATA_LEN] { +) -> Result<[u8; AUTHENTICATED_DATA_LEN], IronfishFrostError> { let mut data = [0u8; AUTHENTICATED_DATA_LEN]; let parts = [ &identity.serialize()[..], - &raw_commitments.hiding().serialize(), - &raw_commitments.binding().serialize(), + &raw_commitments.hiding().serialize()?, + &raw_commitments.binding().serialize()?, &checksum.to_le_bytes(), ]; let mut slice = &mut data[..]; @@ -64,7 +63,7 @@ fn authenticated_data( slice = &mut slice[part.len()..]; } debug_assert_eq!(slice.len(), 0); - data + Ok(data) } #[derive(Clone, PartialEq, Eq, Debug)] @@ -85,7 +84,7 @@ impl SigningCommitment { raw_commitments: SigningCommitments, checksum: Checksum, signature: Signature, - ) -> Result { + ) -> Result { let signing_commitment = Self { identity, raw_commitments, @@ -97,13 +96,12 @@ impl SigningCommitment { .map(|_| signing_commitment) } - #[must_use] pub fn from_secrets( participant_secret: &Secret, secret_share: &SigningShare, transaction_hash: &[u8], signing_participants: &[I], - ) -> SigningCommitment + ) -> Result where I: Borrow, { @@ -112,21 +110,22 @@ impl SigningCommitment { deterministic_signing_nonces(secret_share, transaction_hash, signing_participants); let raw_commitments = *nonces.commitments(); let checksum = input_checksum(transaction_hash, signing_participants); - let authenticated_data = authenticated_data(&identity, &raw_commitments, checksum); + let authenticated_data = authenticated_data(&identity, &raw_commitments, checksum)?; let signature = participant_secret.sign(&authenticated_data); - SigningCommitment { + Ok(SigningCommitment { identity, raw_commitments, checksum, signature, - } + }) } - pub fn verify_authenticity(&self) -> Result<(), SignatureError> { + pub fn verify_authenticity(&self) -> Result<(), IronfishFrostError> { let authenticated_data = - authenticated_data(&self.identity, &self.raw_commitments, self.checksum); - self.identity - .verify_data(&authenticated_data, &self.signature) + authenticated_data(&self.identity, &self.raw_commitments, self.checksum)?; + Ok(self + .identity + .verify_data(&authenticated_data, &self.signature)?) } pub fn verify_checksum( @@ -172,16 +171,16 @@ impl SigningCommitment { bytes } - pub fn serialize_into(&self, mut writer: W) -> io::Result<()> { + pub fn serialize_into(&self, mut writer: W) -> Result<(), IronfishFrostError> { writer.write_all(&self.signature.to_bytes())?; writer.write_all(&self.identity.serialize())?; - writer.write_all(&self.hiding().serialize())?; - writer.write_all(&self.binding().serialize())?; + writer.write_all(&self.hiding().serialize()?)?; + writer.write_all(&self.binding().serialize()?)?; writer.write_all(&self.checksum.to_le_bytes())?; Ok(()) } - pub fn deserialize_from(mut reader: R) -> io::Result { + pub fn deserialize_from(mut reader: R) -> Result { let mut signature_bytes = [0u8; Signature::BYTE_SIZE]; reader.read_exact(&mut signature_bytes)?; let signature = Signature::from_bytes(&signature_bytes); @@ -190,11 +189,11 @@ impl SigningCommitment { let mut hiding = [0u8; 32]; reader.read_exact(&mut hiding)?; - let hiding = NonceCommitment::deserialize(hiding).map_err(io::Error::other)?; + let hiding = NonceCommitment::deserialize(&hiding[..])?; let mut binding = [0u8; 32]; reader.read_exact(&mut binding)?; - let binding = NonceCommitment::deserialize(binding).map_err(io::Error::other)?; + let binding = NonceCommitment::deserialize(&binding[..])?; let raw_commitments = SigningCommitments::new(hiding, binding); @@ -203,7 +202,6 @@ impl SigningCommitment { let checksum = Checksum::from_le_bytes(checksum); Self::from_raw_parts(identity, raw_commitments, checksum, signature) - .map_err(io::Error::other) } } @@ -233,7 +231,8 @@ mod tests { &signing_share, b"transaction hash", &signing_participants, - ); + ) + .expect("commitment creation failed"); let serialized = commitment.serialize(); @@ -279,7 +278,8 @@ mod tests { &signing_share, b"transaction hash", &signing_participants, - ); + ) + .expect("commitment creation failed"); let serialized = commitment.serialize(); @@ -307,7 +307,8 @@ mod tests { &signing_share, b"transaction hash", &signing_participants, - ); + ) + .expect("commitment creation failed"); assert!(commitment.verify_authenticity().is_ok()); } @@ -329,14 +330,18 @@ mod tests { &signing_share, b"transaction hash", &signing_participants, - ); + ) + .expect("commitment creation failed"); let unrelated_secret = Secret::random(&mut rng); - let invalid_signature = unrelated_secret.sign(&authenticated_data( - commitment.identity(), - commitment.raw_commitments(), - commitment.checksum(), - )); + let invalid_signature = unrelated_secret.sign( + &authenticated_data( + commitment.identity(), + commitment.raw_commitments(), + commitment.checksum(), + ) + .expect("authenticated data failed to return"), + ); let invalid_commitment = SigningCommitment { identity: commitment.identity().clone(), @@ -368,14 +373,16 @@ mod tests { &signing_share1, transaction_hash, &signing_participants, - ); + ) + .expect("commitment creation failed"); let commitment2 = SigningCommitment::from_secrets( &secret2, &signing_share2, transaction_hash, &signing_participants, - ); + ) + .expect("commitment creation failed"); assert_ne!(commitment1, commitment2); assert_eq!(commitment1.checksum(), commitment2.checksum()); @@ -398,14 +405,16 @@ mod tests { &signing_share, b"something", &signing_participants, - ); + ) + .expect("commitment creation failed"); let commitment2 = SigningCommitment::from_secrets( &secret, &signing_share, b"something else", &signing_participants, - ); + ) + .expect("commitment creation failed"); assert_ne!(commitment1.checksum(), commitment2.checksum()); } @@ -433,14 +442,16 @@ mod tests { &signing_share, transaction_hash, &signing_participants1, - ); + ) + .expect("commitment creation failed"); let commitment2 = SigningCommitment::from_secrets( &secret, &signing_share, transaction_hash, &signing_participants2, - ); + ) + .expect("commitment creation failed"); assert_ne!(commitment1.checksum(), commitment2.checksum()); }