diff --git a/src/lib.rs b/src/lib.rs index 2f806a0..9f146e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -254,6 +254,11 @@ impl SignatureShare { /// A secret key; wraps a single prime field element. The field element is /// heap allocated to avoid any stack copying that result when passing /// `SecretKey`s between stack frames. +/// +/// # Serde integration +/// `SecretKey` implements `Deserialize` but not `Serialize` to avoid accidental +/// serialization in insecure contexts. To enable both use the `::serde_impl::SerdeSecret` +/// wrapper which implements both `Deserialize` and `Serialize`. #[derive(PartialEq, Eq)] pub struct SecretKey(Box); @@ -364,6 +369,11 @@ impl SecretKey { } /// A secret key share. +/// +/// # Serde integration +/// `SecretKeyShare` implements `Deserialize` but not `Serialize` to avoid accidental +/// serialization in insecure contexts. To enable both use the `::serde_impl::SerdeSecret` +/// wrapper which implements both `Deserialize` and `Serialize`. #[derive(Clone, PartialEq, Eq, Default)] pub struct SecretKeyShare(SecretKey); diff --git a/src/serde_impl.rs b/src/serde_impl.rs index 34fcc55..d440218 100644 --- a/src/serde_impl.rs +++ b/src/serde_impl.rs @@ -3,6 +3,7 @@ pub use self::field_vec::FieldWrap; use std::borrow::Cow; +use std::ops::Deref; use crate::G1; use serde::de::Error as DeserializeError; @@ -10,9 +11,122 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_derive::{Deserialize, Serialize}; use crate::poly::{coeff_pos, BivarCommitment}; +use crate::serde_impl::serialize_secret_internal::SerializeSecret; const ERR_DEG: &str = "commitment degree does not match coefficients"; +mod serialize_secret_internal { + use serde::Serializer; + + /// To avoid deriving [`Serialize`] automatically for structs containing secret keys this trait + /// should be implemented instead. It only enables explicit serialization through + /// [`::serde_impls::SerdeSecret`]. + pub trait SerializeSecret { + fn serialize_secret(&self, serializer: S) -> Result; + } + + impl SerializeSecret for &T { + fn serialize_secret( + &self, + serializer: S, + ) -> Result<::Ok, ::Error> { + (*self).serialize_secret(serializer) + } + } +} + +/// `SerdeSecret` is a wrapper struct for serializing and deserializing secret keys. Due to security +/// concerns serialize shouldn't be implemented for secret keys to avoid accidental leakage. +/// +/// Whenever this struct is used the integrity of security boundaries should be checked carefully. +pub struct SerdeSecret(T); + +impl Deref for SerdeSecret { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.inner() + } +} + +impl SerdeSecret { + /// Returns the actual secret from the wrapper + pub fn into_inner(self) -> T { + self.0 + } + + /// Returns a reference to the actual secret contained in the wrapper + pub fn inner(&self) -> &T { + &self.0 + } +} + +impl<'de, T: Deserialize<'de>> Deserialize<'de> for SerdeSecret { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(SerdeSecret(Deserialize::deserialize(deserializer)?)) + } +} + +impl Serialize for SerdeSecret { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.0.serialize_secret(serializer) + } +} + +impl<'de> Deserialize<'de> for crate::SecretKey { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use crate::{Fr, FrRepr}; + use pairing::PrimeField; + use serde::de; + + let mut fr = match Fr::from_repr(FrRepr(Deserialize::deserialize(deserializer)?)) { + Ok(x) => x, + Err(pairing::PrimeFieldDecodingError::NotInField(_)) => { + return Err(de::Error::invalid_value( + de::Unexpected::Other(&"Number outside of prime field."), + &"Valid prime field element.", + )) + } + }; + + Ok(crate::SecretKey::from_mut(&mut fr)) + } +} + +impl SerializeSecret for crate::SecretKey { + fn serialize_secret(&self, serializer: S) -> Result { + use pairing::PrimeField; + + Serialize::serialize(&self.0.into_repr().0, serializer) + } +} + +impl<'de> Deserialize<'de> for crate::SecretKeyShare { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + Ok(crate::SecretKeyShare(Deserialize::deserialize( + deserializer, + )?)) + } +} + +impl SerializeSecret for crate::SecretKeyShare { + fn serialize_secret(&self, serializer: S) -> Result { + self.0.serialize_secret(serializer) + } +} + /// A type with the same content as `BivarCommitment`, but that has not been validated yet. #[derive(Serialize, Deserialize)] struct WireBivarCommitment<'a> { @@ -250,4 +364,54 @@ mod tests { assert_eq!(comm, de_comm); } } + + #[test] + #[cfg(not(feature = "use-insecure-test-only-mock-crypto"))] + fn serde_secret_key() { + use crate::serde_impl::SerdeSecret; + use crate::SecretKey; + use rand::{thread_rng, Rng}; + + let mut rng = thread_rng(); + for _ in 0..2048 { + let sk: SecretKey = rng.gen(); + let ser_ref = bincode::serialize(&SerdeSecret(&sk)).expect("serialize secret key"); + + let de = bincode::deserialize(&ser_ref).expect("deserialize secret key"); + assert_eq!(sk, de); + + let de_serde_secret: SerdeSecret = + bincode::deserialize(&ser_ref).expect("deserialize secret key"); + assert_eq!(sk, de_serde_secret.into_inner()); + + let ser_val = bincode::serialize(&SerdeSecret(sk)).expect("serialize secret key"); + assert_eq!(ser_ref, ser_val); + } + } + + #[test] + fn serde_secret_key_share() { + use crate::serde_impl::SerdeSecret; + use crate::SecretKeyShare; + use rand::{thread_rng, Rng}; + + let mut rng = thread_rng(); + for _ in 0..2048 { + let sk: SecretKeyShare = rng.gen(); + let ser_ref = bincode::serialize(&SerdeSecret(&sk)).expect("serialize secret key"); + + let de = bincode::deserialize(&ser_ref).expect("deserialize secret key"); + assert_eq!(sk, de); + + let de_serde_secret: SerdeSecret = + bincode::deserialize(&ser_ref).expect("deserialize secret key"); + assert_eq!(sk, de_serde_secret.into_inner()); + + let ser_val = bincode::serialize(&SerdeSecret(sk)).expect("serialize secret key"); + assert_eq!(ser_ref, ser_val); + + #[cfg(not(feature = "use-insecure-test-only-mock-crypto"))] + assert_eq!(ser_val.len(), 32); + } + } }