diff --git a/src/keys.rs b/src/keys.rs index 2321e18..3541a48 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -2,10 +2,15 @@ * 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::VerifyingKey; - use crate::frost::keys::PublicKeyPackage as FrostPublicKeyPackage; use crate::participant::Identity; +use crate::serde::read_u16; +use crate::serde::read_variable_length; +use crate::serde::read_variable_length_bytes; +use crate::serde::write_u16; +use crate::serde::write_variable_length; +use crate::serde::write_variable_length_bytes; +use reddsa::frost::redjubjub::VerifyingKey; use std::io; #[derive(Clone, Eq, PartialEq, Debug)] @@ -56,52 +61,27 @@ impl PublicKeyPackage { } pub fn serialize_into(&self, mut writer: W) -> io::Result<()> { - let public_key_package = self + let frost_public_key_package = self .frost_public_key_package .serialize() .map_err(io::Error::other)?; - let public_key_package_len = u32::try_from(public_key_package.len()) - .map_err(io::Error::other)? - .to_le_bytes(); - writer.write_all(&public_key_package_len)?; - writer.write_all(&public_key_package)?; - - let identities_len = u32::try_from(self.identities.len()) - .map_err(io::Error::other)? - .to_le_bytes(); - writer.write_all(&identities_len)?; - for identity in &self.identities { - let identity_bytes = identity.serialize(); - writer.write_all(&identity_bytes)? - } - writer.write_all(&self.min_signers.to_le_bytes())?; + write_variable_length_bytes(&mut writer, &frost_public_key_package)?; + write_variable_length(&mut writer, &self.identities, |writer, identity| { + identity.serialize_into(writer) + })?; + write_u16(&mut writer, self.min_signers)?; Ok(()) } pub fn deserialize_from(mut reader: R) -> io::Result { - let mut public_key_package_len = [0u8; 4]; - reader.read_exact(&mut public_key_package_len)?; - let public_key_package_len = u32::from_le_bytes(public_key_package_len) as usize; - - let mut frost_public_key_package = vec![0u8; public_key_package_len]; - reader.read_exact(&mut frost_public_key_package)?; + 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)?; - - let mut identities_len = [0u8; 4]; - reader.read_exact(&mut identities_len)?; - let identities_len = u32::from_le_bytes(identities_len) as usize; - - let mut identities = Vec::with_capacity(identities_len); - for _ in 0..identities_len { - identities.push(Identity::deserialize_from(&mut reader)?); - } - - let mut min_signers = [0u8; 2]; - reader.read_exact(&mut min_signers)?; - let min_signers = u16::from_le_bytes(min_signers); + let identities = + read_variable_length(&mut reader, |reader| Identity::deserialize_from(reader))?; + let min_signers = read_u16(&mut reader)?; Ok(PublicKeyPackage { frost_public_key_package, diff --git a/src/lib.rs b/src/lib.rs index 3220f76..31d53ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,10 +8,13 @@ #![warn(unused_crate_dependencies)] #![warn(unused_qualifications)] +mod serde; + pub mod keys; pub mod multienc; pub mod nonces; pub mod participant; pub mod signature_share; pub mod signing_commitment; + pub use reddsa::frost::redjubjub as frost; diff --git a/src/serde.rs b/src/serde.rs new file mode 100644 index 0000000..c8067c5 --- /dev/null +++ b/src/serde.rs @@ -0,0 +1,198 @@ +/* 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/. */ + +//! Internal module to help with serialization and deserialization. + +use std::io; + +#[inline] +pub(crate) fn write_u16(mut writer: W, value: u16) -> io::Result<()> { + writer.write_all(&value.to_le_bytes()) +} + +#[inline] +pub(crate) fn write_u32(mut writer: W, value: u32) -> io::Result<()> { + writer.write_all(&value.to_le_bytes()) +} + +#[inline] +pub(crate) fn write_usize(writer: W, value: usize) -> io::Result<()> { + let value: u32 = value + .try_into() + .map_err(|_| io::Error::other("size too large to fit into 32 bits"))?; + write_u32(writer, value) +} + +#[inline] +pub(crate) fn write_variable_length(mut writer: W, iter: I, f: F) -> io::Result<()> +where + W: io::Write, + I: IntoIterator, + I::IntoIter: ExactSizeIterator, + F: Fn(&mut W, I::Item) -> io::Result<()>, +{ + let iter = iter.into_iter(); + write_usize(&mut writer, iter.len())?; + for item in iter { + f(&mut writer, item)?; + } + Ok(()) +} + +#[inline] +pub(crate) fn write_variable_length_bytes( + mut writer: W, + bytes: &[u8], +) -> io::Result<()> { + write_usize(&mut writer, bytes.len())?; + writer.write_all(bytes) +} + +#[inline] +pub(crate) fn read_u16(mut reader: R) -> io::Result { + let mut value = [0u8; 2]; + reader.read_exact(&mut value)?; + Ok(u16::from_le_bytes(value)) +} + +#[inline] +pub(crate) fn read_u32(mut reader: R) -> io::Result { + let mut value = [0u8; 4]; + reader.read_exact(&mut value)?; + Ok(u32::from_le_bytes(value)) +} + +#[inline] +pub(crate) fn read_usize(reader: R) -> io::Result { + read_u32(reader).map(|value| value as usize) +} + +#[inline] +pub(crate) fn read_variable_length(mut reader: R, f: F) -> io::Result> +where + R: io::Read, + F: Fn(&mut R) -> io::Result, +{ + let len = read_usize(&mut reader)?; + let mut items = Vec::with_capacity(len); + for _ in 0..len { + items.push(f(&mut reader)?); + } + Ok(items) +} + +#[inline] +pub(crate) fn read_variable_length_bytes(mut reader: R) -> io::Result> { + let len = read_usize(&mut reader)?; + let mut bytes = vec![0u8; len]; + reader.read_exact(&mut bytes)?; + Ok(bytes) +} + +#[cfg(test)] +mod test { + use super::*; + use rand::thread_rng; + use rand::Rng; + use std::mem; + + macro_rules! test_serde { + ( $value:expr, $write:expr, $read:expr, size = $size:expr ) => { + let value = $value; + let mut serialized = [0u8; $size]; + + let mut writer = &mut serialized[..]; + #[allow(clippy::redundant_closure_call)] + $write(&mut writer, value.clone()).expect("serialization failed"); + assert_eq!(writer.len(), 0, "serialization did not fill output buffer"); + + let mut reader = &serialized[..]; + #[allow(clippy::redundant_closure_call)] + let deserialized = $read(&mut reader).expect("deserialization failed"); + assert_eq!( + reader.len(), + 0, + "deserialization did not consume output buffer" + ); + + assert_eq!( + value, deserialized, + "deserialization did not return the original value" + ); + }; + } + + macro_rules! test_int { + ( $type:ty, $write:expr, $read:expr, size = $size:expr ) => { + test_serde!(<$type>::MIN, $write, $read, size = $size); + test_serde!(<$type>::MAX, $write, $read, size = $size); + + let mut rng = thread_rng(); + for _ in 0..1000 { + let value: $type = rng.gen(); + test_serde!(value, $write, $read, size = $size); + } + }; + } + + #[test] + fn write_read_u16() { + test_int!(u16, write_u16, read_u16, size = 2); + } + + #[test] + fn write_read_u32() { + test_int!(u32, write_u32, read_u32, size = 4); + } + + #[test] + fn write_read_usize() { + test_serde!(usize::MIN, write_usize, read_usize, size = 4); + test_serde!(u32::MAX as usize, write_usize, read_usize, size = 4); + + if mem::size_of::() > mem::size_of::() { + write_usize(&mut [0u8; 4][..], (u32::MAX as usize) + 1) + .expect_err("serialization should have failed due to overflow"); + write_usize(&mut [0u8; 4][..], usize::MAX) + .expect_err("serialization should have failed due to overflow"); + } + } + + #[test] + fn write_read_variable_length() { + test_serde!( + Vec::::new(), + |writer, iter| write_variable_length(writer, iter, |writer, item| write_u16( + writer, item + )), + |reader| read_variable_length(reader, |reader| read_u16(reader)), + size = 4 + ); + + test_serde!( + vec![1, 2, 3, 4, 5, 6], + |writer, iter| write_variable_length(writer, iter, |writer, item| write_u16( + writer, item + )), + |reader| read_variable_length(reader, |reader| read_u16(reader)), + size = 4 + 6 * 2 + ); + } + + #[test] + fn write_read_variable_length_bytes() { + test_serde!( + &b""[..], + write_variable_length_bytes, + read_variable_length_bytes, + size = 4 + ); + test_serde!( + &b"abcdef"[..], + write_variable_length_bytes, + read_variable_length_bytes, + size = 4 + 6 + ); + } +}