Skip to content

Commit

Permalink
Place common (de)serialization methods in a dedicated module
Browse files Browse the repository at this point in the history
  • Loading branch information
andiflabs committed Mar 22, 2024
1 parent cbc8224 commit 809c4fa
Show file tree
Hide file tree
Showing 3 changed files with 218 additions and 37 deletions.
54 changes: 17 additions & 37 deletions src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -56,52 +61,27 @@ impl PublicKeyPackage {
}

pub fn serialize_into<W: io::Write>(&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<R: io::Read>(mut reader: R) -> io::Result<Self> {
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,
Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
198 changes: 198 additions & 0 deletions src/serde.rs
Original file line number Diff line number Diff line change
@@ -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<W: io::Write>(mut writer: W, value: u16) -> io::Result<()> {
writer.write_all(&value.to_le_bytes())
}

#[inline]
pub(crate) fn write_u32<W: io::Write>(mut writer: W, value: u32) -> io::Result<()> {
writer.write_all(&value.to_le_bytes())
}

#[inline]
pub(crate) fn write_usize<W: io::Write>(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<W, I, F>(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<W: io::Write>(
mut writer: W,
bytes: &[u8],
) -> io::Result<()> {
write_usize(&mut writer, bytes.len())?;
writer.write_all(bytes)
}

#[inline]
pub(crate) fn read_u16<R: io::Read>(mut reader: R) -> io::Result<u16> {
let mut value = [0u8; 2];
reader.read_exact(&mut value)?;
Ok(u16::from_le_bytes(value))
}

#[inline]
pub(crate) fn read_u32<R: io::Read>(mut reader: R) -> io::Result<u32> {
let mut value = [0u8; 4];
reader.read_exact(&mut value)?;
Ok(u32::from_le_bytes(value))
}

#[inline]
pub(crate) fn read_usize<R: io::Read>(reader: R) -> io::Result<usize> {
read_u32(reader).map(|value| value as usize)
}

#[inline]
pub(crate) fn read_variable_length<R, F, T>(mut reader: R, f: F) -> io::Result<Vec<T>>
where
R: io::Read,
F: Fn(&mut R) -> io::Result<T>,
{
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<R: io::Read>(mut reader: R) -> io::Result<Vec<u8>> {
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::<usize>() > mem::size_of::<u32>() {
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::<u16>::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
);
}
}

0 comments on commit 809c4fa

Please sign in to comment.