From 798a3e46311353c48f20af1957520db1a94fc205 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 9 Dec 2019 11:07:24 -0800 Subject: [PATCH 1/4] Fix trait bounds on SigType. When Rust derives Copy, Clone, Eq, PartialEq, etc. on a type with `PhantomData`, it adds a `T: Clone` etc. bound, regardless of whether `T` is only ever used inside of the `PhantomData`. A better fix would be to fix the derived bounds themselves, but in the meantime this works, even if it's slightly ugly. --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e4cb492..d477a30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,18 +40,18 @@ pub use signature::Signature; pub trait SigType: private::Sealed {} /// A type variable corresponding to Zcash's `BindingSig`. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum Binding {} impl SigType for Binding {} /// A type variable corresponding to Zcash's `SpendAuthSig`. -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum SpendAuth {} impl SigType for SpendAuth {} pub(crate) mod private { use super::*; - pub trait Sealed: Copy + Clone + std::fmt::Debug { + pub trait Sealed: Copy + Clone + Eq + PartialEq + std::fmt::Debug { fn basepoint() -> jubjub::ExtendedPoint; } impl Sealed for Binding { From e58376fc47658ce1fd767b3cfcdaa409d2f94590 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 9 Dec 2019 11:07:05 -0800 Subject: [PATCH 2/4] Add Serialize, Deserialize for byte wrapper types. --- Cargo.toml | 3 +++ src/public_key.rs | 1 + src/signature.rs | 1 + tests/bincode.rs | 56 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+) create mode 100644 tests/bincode.rs diff --git a/Cargo.toml b/Cargo.toml index 508e11b..41f99ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,12 +9,15 @@ rand_core = "0.5" thiserror = "1.0" blake2b_simd = "0.5" jubjub = "0.3" +serde = { version = "1", optional = true, features = ["derive"] } [dev-dependencies] rand = "0.7" rand_chacha = "0.2" proptest = "0.9" lazy_static = "1.4" +bincode = "1" [features] nightly = [] +default = ["serde"] diff --git a/src/public_key.rs b/src/public_key.rs index b5a8696..a303b71 100644 --- a/src/public_key.rs +++ b/src/public_key.rs @@ -9,6 +9,7 @@ use crate::{Error, Randomizer, Scalar, SigType, Signature, SpendAuth}; /// [`PublicKey`] type in this library holds other decompressed state /// used in signature verification. #[derive(Copy, Clone, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct PublicKeyBytes { pub(crate) bytes: [u8; 32], pub(crate) _marker: PhantomData, diff --git a/src/signature.rs b/src/signature.rs index 74b39b4..acb9bd1 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -4,6 +4,7 @@ use crate::SigType; /// A RedJubJub signature. #[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Signature { pub(crate) r_bytes: [u8; 32], pub(crate) s_bytes: [u8; 32], diff --git a/tests/bincode.rs b/tests/bincode.rs new file mode 100644 index 0000000..e50f3b7 --- /dev/null +++ b/tests/bincode.rs @@ -0,0 +1,56 @@ +use std::convert::TryFrom; + +use proptest::prelude::*; + +use redjubjub_zebra::*; + +proptest! { + #[test] + fn publickeybytes_serialization( + bytes in prop::array::uniform32(any::()), + ) { + let pk_bytes_from = PublicKeyBytes::::from(bytes); + let pk_bytes_bincode: PublicKeyBytes:: + = bincode::deserialize(&bytes[..]).unwrap(); + + // Check 1: both decoding methods should have the same result. + assert_eq!(pk_bytes_from, pk_bytes_bincode); + + // Check 2: bincode encoding should match original bytes. + let bytes_bincode = bincode::serialize(&pk_bytes_from).unwrap(); + assert_eq!(&bytes[..], &bytes_bincode[..]); + + // Check 3: From encoding should match original bytes. + let bytes_from: [u8; 32] = pk_bytes_bincode.into(); + assert_eq!(&bytes[..], &bytes_from[..]); + } + + #[test] + fn signature_serialization( + lo in prop::array::uniform32(any::()), + hi in prop::array::uniform32(any::()), + ) { + // array length hack + let bytes = { + let mut bytes = [0; 64]; + bytes[0..32].copy_from_slice(&lo[..]); + bytes[32..64].copy_from_slice(&hi[..]); + bytes + }; + + let sig_bytes_from = Signature::::from(bytes); + let sig_bytes_bincode: Signature:: + = bincode::deserialize(&bytes[..]).unwrap(); + + // Check 1: both decoding methods should have the same result. + assert_eq!(sig_bytes_from, sig_bytes_bincode); + + // Check 2: bincode encoding should match original bytes. + let bytes_bincode = bincode::serialize(&sig_bytes_from).unwrap(); + assert_eq!(&bytes[..], &bytes_bincode[..]); + + // Check 3: From encoding should match original bytes. + let bytes_from: [u8; 64] = sig_bytes_bincode.into(); + assert_eq!(&bytes[..], &bytes_from[..]); + } +} From 2ca445ad2391688ef727424ea6ee33a744296bba Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 9 Dec 2019 11:22:39 -0800 Subject: [PATCH 3/4] Add Serialize, Deserialize to PublicKey. --- src/public_key.rs | 4 ++++ tests/bincode.rs | 25 +++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/public_key.rs b/src/public_key.rs index a303b71..db8490d 100644 --- a/src/public_key.rs +++ b/src/public_key.rs @@ -36,6 +36,10 @@ impl From> for [u8; 32] { /// public key may not be used immediately, it is probably better to use /// [`PublicKeyBytes`], which is a refinement type for `[u8; 32]`. #[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(try_from = "PublicKeyBytes"))] +#[cfg_attr(feature = "serde", serde(into = "PublicKeyBytes"))] +#[cfg_attr(feature = "serde", serde(bound = "T: SigType"))] pub struct PublicKey { // XXX-jubjub: this should just be Point pub(crate) point: jubjub::ExtendedPoint, diff --git a/tests/bincode.rs b/tests/bincode.rs index e50f3b7..827107e 100644 --- a/tests/bincode.rs +++ b/tests/bincode.rs @@ -25,6 +25,31 @@ proptest! { assert_eq!(&bytes[..], &bytes_from[..]); } + #[test] + fn publickey_serialization( + bytes in prop::array::uniform32(any::()), + ) { + let pk_result_try_from = PublicKey::::try_from(bytes); + let pk_result_bincode: Result, _> + = bincode::deserialize(&bytes[..]); + + // Check 1: both decoding methods should have the same result + match (pk_result_try_from, pk_result_bincode) { + // Both agree on success + (Ok(pk_try_from), Ok(pk_bincode)) => { + // Check 2: bincode encoding should match original bytes + let bytes_bincode = bincode::serialize(&pk_try_from).unwrap(); + assert_eq!(&bytes[..], &bytes_bincode[..]); + // Check 3: From encoding should match original bytes + let bytes_from: [u8; 32] = pk_bincode.into(); + assert_eq!(&bytes[..], &bytes_from[..]); + }, + // Both agree on failure + (Err(_), Err(_)) => {}, + _ => panic!("bincode and try_from do not agree"), + } + } + #[test] fn signature_serialization( lo in prop::array::uniform32(any::()), From 87f09b87b52562763630be58e246238a22858952 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 9 Dec 2019 11:54:31 -0800 Subject: [PATCH 4/4] Add Serialize, Deserialize for SecretKey. --- src/secret_key.rs | 19 +++++++++++++++++++ tests/bincode.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/secret_key.rs b/src/secret_key.rs index 7e14204..b60bc1f 100644 --- a/src/secret_key.rs +++ b/src/secret_key.rs @@ -6,6 +6,10 @@ use rand_core::{CryptoRng, RngCore}; /// A RedJubJub secret key. #[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "serde", serde(from = "SerdeHelper"))] +#[cfg_attr(feature = "serde", serde(into = "SerdeHelper"))] +#[cfg_attr(feature = "serde", serde(bound = "T: SigType"))] pub struct SecretKey { sk: Scalar, pk: PublicKey, @@ -37,6 +41,21 @@ impl From<[u8; 32]> for SecretKey { } } +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +struct SerdeHelper([u8; 32]); + +impl From for SecretKey { + fn from(helper: SerdeHelper) -> Self { + helper.0.into() + } +} + +impl From> for SerdeHelper { + fn from(sk: SecretKey) -> Self { + Self(sk.into()) + } +} + impl SecretKey { /// Randomize this public key with the given `randomizer`. pub fn randomize(&self, randomizer: &Randomizer) -> SecretKey { diff --git a/tests/bincode.rs b/tests/bincode.rs index 827107e..dedac21 100644 --- a/tests/bincode.rs +++ b/tests/bincode.rs @@ -5,6 +5,32 @@ use proptest::prelude::*; use redjubjub_zebra::*; proptest! { + #[test] + fn secretkey_serialization( + bytes in prop::array::uniform32(any::()), + ) { + let sk_from = SecretKey::::from(bytes); + let sk_bincode: SecretKey:: + = bincode::deserialize(&bytes[..]).unwrap(); + + // Check 1: both decoding methods should have the same public key + let pk_bytes_from = PublicKeyBytes::from(PublicKey::from(&sk_from)); + let pk_bytes_bincode = PublicKeyBytes::from(PublicKey::from(&sk_bincode)); + assert_eq!(pk_bytes_from, pk_bytes_bincode); + + // The below tests fail because we do not require canonically-encoded secret keys. + /* + + // Check 2: bincode encoding should match original bytes. + let bytes_bincode = bincode::serialize(&sk_from).unwrap(); + assert_eq!(&bytes[..], &bytes_bincode[..]); + + // Check 3: From encoding should match original bytes. + let bytes_from: [u8; 32] = sk_bincode.into(); + assert_eq!(&bytes[..], &bytes_from[..]); + */ + } + #[test] fn publickeybytes_serialization( bytes in prop::array::uniform32(any::()),