From 036b720b7f7d030331007b5c4cd6cef74c621c64 Mon Sep 17 00:00:00 2001 From: Weiliang Li Date: Fri, 8 Nov 2019 18:53:40 +0900 Subject: [PATCH] Clean up and add parity codec support (#91) * clean Cargo.toml and add parity codec support * upgrade parity codec * fix travis * make clippy happy * use zeroize instead of memsec * fix zeroize and add test * Update .travis.yml * improve codec support * fix * add TODO for removing clear_fr --- .travis.yml | 2 +- Cargo.toml | 20 ++++---- examples/threshold_enc.rs | 2 +- examples/threshold_sig.rs | 2 +- src/codec_impl.rs | 27 ++++++++++ src/lib.rs | 99 +++++++++++++++++++++++++++--------- src/poly.rs | 102 +++++++++++++++++++++++--------------- src/secret.rs | 98 ++++++++---------------------------- 8 files changed, 198 insertions(+), 154 deletions(-) create mode 100644 src/codec_impl.rs diff --git a/.travis.yml b/.travis.yml index fe4aa2c..069c0fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: rust rust: - - 1.35.0 + - 1.39.0 cache: cargo: true timeout: 1200 diff --git a/Cargo.toml b/Cargo.toml index 79ec117..a3281fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,22 +19,23 @@ description = "Pairing threshold cryptography" edition = "2018" [dependencies] -byteorder = "1.2.7" -errno = "0.2.4" -failure = "0.1.5" +byteorder = "1.3.2" +failure = "0.1.6" hex_fmt = "0.3.0" -log = "0.4.6" -memsec = "0.5.4" +log = "0.4.8" pairing = { version = "0.14.2", features = ["u128-support"] } rand = "0.6.5" rand04_compat = "0.1.1" rand_chacha = "0.1.1" -serde = { version = "1.0.89", features = ["derive"] } -tiny-keccak = "1.4.2" +serde = { version = "1.0.102", features = ["derive"] } +tiny-keccak = "1.5.0" +codec = { package = "parity-scale-codec", version = "1.0.6", default-features = false, features = ["derive"], optional = true } +bincode = { version = "1.2", optional = true } +zeroize = "1.0" [dev-dependencies] -bincode = "1.0.1" -criterion = "0.2.7" +bincode = "1.2" +criterion = "0.3.0" rand_xorshift = "0.1.1" [[bench]] @@ -43,3 +44,4 @@ harness = false [features] use-insecure-test-only-mock-crypto = [] +codec-support = ["codec", "bincode"] diff --git a/examples/threshold_enc.rs b/examples/threshold_enc.rs index 1e59821..952240c 100644 --- a/examples/threshold_enc.rs +++ b/examples/threshold_enc.rs @@ -147,7 +147,7 @@ fn main() { let ciphertext = pk.encrypt(msg); send_msg(society.get_actor(alice), ciphertext.clone()); send_msg(society.get_actor(bob), ciphertext.clone()); - send_msg(society.get_actor(clara), ciphertext.clone()); + send_msg(society.get_actor(clara), ciphertext); // We start a meeting of the secret society. At the meeting, each actor contributes their // share of the decryption process to decrypt the ciphertext that they each received. diff --git a/examples/threshold_sig.rs b/examples/threshold_sig.rs index 8d369e9..fc3f59c 100644 --- a/examples/threshold_sig.rs +++ b/examples/threshold_sig.rs @@ -216,7 +216,7 @@ fn main() { // message now has two signatures (which is `threshold + 1` signatures). The network runs a // round of consensus, which successfully creates a combined-signature for Alice's message. // Alice's message is appended to the chat log. - alice.send(network.get_mut_node(node2), alice_greeting.clone()); + alice.send(network.get_mut_node(node2), alice_greeting); network.step(); assert_eq!(network.chat_log.len(), 1); } diff --git a/src/codec_impl.rs b/src/codec_impl.rs new file mode 100644 index 0000000..5dbc745 --- /dev/null +++ b/src/codec_impl.rs @@ -0,0 +1,27 @@ +#[macro_export] +/// implement parity codec for type +macro_rules! impl_codec_for { + ($type:ty) => { + impl codec::Encode for $type { + fn encode(&self) -> Vec { + let encoded = bincode::serialize(&self).unwrap(); + codec::Encode::encode(&encoded) + } + } + + impl codec::Decode for $type { + fn decode(value: &mut I) -> std::result::Result { + let decoded: Vec = codec::Decode::decode(value)?; + bincode::deserialize(decoded.as_slice()).map_err(|_| codec::Error) + } + } + }; +} + +use crate::{Ciphertext, DecryptionShare, PublicKey, PublicKeySet, Signature}; + +impl_codec_for!(PublicKey); +impl_codec_for!(Signature); +impl_codec_for!(DecryptionShare); +impl_codec_for!(PublicKeySet); +impl_codec_for!(Ciphertext); diff --git a/src/lib.rs b/src/lib.rs index 47aa9fe..6612fa3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,10 @@ mod cmp_pairing; mod into_fr; mod secret; +#[cfg(feature = "codec-support")] +#[macro_use] +mod codec_impl; + pub mod error; pub mod poly; pub mod serde_impl; @@ -35,17 +39,15 @@ use rand04_compat::RngExt; use rand_chacha::ChaChaRng; use serde::{Deserialize, Serialize}; use tiny_keccak::sha3_256; +use zeroize::Zeroize; use crate::cmp_pairing::cmp_projective; use crate::error::{Error, FromBytesError, FromBytesResult, Result}; use crate::poly::{Commitment, Poly}; -use crate::secret::{clear_fr, ContainsSecret, MemRange, FR_SIZE}; +use crate::secret::clear_fr; pub use crate::into_fr::IntoFr; -#[cfg(not(feature = "use-insecure-test-only-mock-crypto"))] -pub use pairing::bls12_381::{Bls12 as PEngine, Fr, FrRepr, G1Affine, G2Affine, G1, G2}; - #[cfg(feature = "use-insecure-test-only-mock-crypto")] mod mock; @@ -55,6 +57,9 @@ pub use crate::mock::{ Ms8Affine as G2Affine, Ms8Projective as G1, Ms8Projective as G2, PK_SIZE, SIG_SIZE, }; +#[cfg(not(feature = "use-insecure-test-only-mock-crypto"))] +pub use pairing::bls12_381::{Bls12 as PEngine, Fr, FrRepr, G1Affine, G2Affine, G1, G2}; + /// The size of a key's representation in bytes. #[cfg(not(feature = "use-insecure-test-only-mock-crypto"))] pub const PK_SIZE: usize = 48; @@ -145,6 +150,7 @@ impl PublicKey { } /// A public key share. +#[cfg_attr(feature = "codec-support", derive(codec::Encode, codec::Decode))] #[derive(Deserialize, Serialize, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct PublicKeyShare(PublicKey); @@ -251,6 +257,7 @@ impl Signature { /// A signature share. // Note: Random signature shares can be generated for testing. +#[cfg_attr(feature = "codec-support", derive(codec::Encode, codec::Decode))] #[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct SignatureShare(pub Signature); @@ -290,6 +297,18 @@ impl SignatureShare { #[derive(PartialEq, Eq)] pub struct SecretKey(Box); +impl Zeroize for SecretKey { + fn zeroize(&mut self) { + clear_fr(&mut *self.0) + } +} + +impl Drop for SecretKey { + fn drop(&mut self) { + self.zeroize(); + } +} + /// Creates a `SecretKey` containing the zero prime field element. impl Default for SecretKey { fn default() -> Self { @@ -316,13 +335,6 @@ impl Clone for SecretKey { } } -/// Zeroes out the memory allocated from the `SecretKey`'s field element. -impl Drop for SecretKey { - fn drop(&mut self) { - self.zero_secret(); - } -} - /// A debug statement where the secret prime field element is redacted. impl fmt::Debug for SecretKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -330,15 +342,6 @@ impl fmt::Debug for SecretKey { } } -impl ContainsSecret for SecretKey { - fn secret_memory(&self) -> MemRange { - MemRange { - ptr: &*self.0 as *const Fr as *mut u8, - n_bytes: FR_SIZE, - } - } -} - impl SecretKey { /// Creates a new `SecretKey` from a mutable reference to a field element. This constructor /// takes a reference to avoid any unnecessary stack copying/moving of secrets (i.e. the field @@ -353,7 +356,7 @@ impl SecretKey { unsafe { copy_nonoverlapping(fr_ptr, &mut *boxed_fr as *mut Fr, 1); } - clear_fr(fr_ptr); + clear_fr(fr); SecretKey(boxed_fr) } @@ -1002,9 +1005,7 @@ mod tests { #[test] fn test_serde() { - use bincode; - - let sk: SecretKey = random(); + let sk = SecretKey::random(); let sig = sk.sign("Please sign here: ______"); let pk = sk.public_key(); let ser_pk = bincode::serialize(&pk).expect("serialize public key"); @@ -1017,9 +1018,59 @@ mod tests { assert_eq!(sig, deser_sig); } + #[cfg(feature = "codec-support")] + #[test] + fn test_codec() { + use codec::{Decode, Encode}; + use rand::distributions::{Distribution, Standard}; + use rand::thread_rng; + + macro_rules! assert_codec { + ($obj:expr, $type:ty) => { + let encoded: Vec = $obj.encode(); + let decoded: $type = <$type>::decode(&mut &encoded[..]).unwrap(); + assert_eq!(decoded, $obj.clone()); + }; + } + + let sk = SecretKey::random(); + let pk = sk.public_key(); + assert_codec!(pk, PublicKey); + + let pk_share = PublicKeyShare(pk); + assert_codec!(pk_share, PublicKeyShare); + + let sig = sk.sign(b"this is a test"); + assert_codec!(sig, Signature); + + let sig_share = SignatureShare(sig); + assert_codec!(sig_share, SignatureShare); + + let cipher_text = pk.encrypt(b"cipher text"); + assert_codec!(cipher_text, Ciphertext); + + let dec_share: DecryptionShare = Standard.sample(&mut thread_rng()); + assert_codec!(dec_share, DecryptionShare); + + let sk_set = SecretKeySet::random(3, &mut thread_rng()); + let pk_set = sk_set.public_keys(); + assert_codec!(pk_set, PublicKeySet); + } + #[test] fn test_size() { assert_eq!(::Compressed::size(), PK_SIZE); assert_eq!(::Compressed::size(), SIG_SIZE); } + + #[test] + fn test_zeroize() { + let zero_sk = SecretKey::from_mut(&mut Fr::zero()); + + let mut sk = SecretKey::random(); + assert_ne!(zero_sk, sk); + + sk.zeroize(); + assert_eq!(zero_sk, sk); + } } diff --git a/src/poly.rs b/src/poly.rs index faab349..7af6d28 100644 --- a/src/poly.rs +++ b/src/poly.rs @@ -20,18 +20,18 @@ use std::borrow::Borrow; use std::cmp::Ordering; use std::fmt::{self, Debug, Formatter}; use std::hash::{Hash, Hasher}; -use std::mem::size_of_val; use std::{cmp, iter, ops}; use pairing::{CurveAffine, CurveProjective, Field}; use rand::Rng; use rand04_compat::RngExt; use serde::{Deserialize, Serialize}; +use zeroize::Zeroize; use crate::cmp_pairing::cmp_projective; use crate::error::{Error, Result}; use crate::into_fr::IntoFr; -use crate::secret::{clear_fr, ContainsSecret, MemRange, Safe}; +use crate::secret::clear_fr; use crate::{Fr, G1Affine, G1}; /// A univariate polynomial in the prime field. @@ -42,6 +42,20 @@ pub struct Poly { pub(super) coeff: Vec, } +impl Zeroize for Poly { + fn zeroize(&mut self) { + for fr in self.coeff.iter_mut() { + clear_fr(fr) + } + } +} + +impl Drop for Poly { + fn drop(&mut self) { + self.zeroize(); + } +} + /// Creates a new `Poly` with the same coefficients as another polynomial. impl Clone for Poly { fn clone(&self) -> Self { @@ -172,14 +186,15 @@ impl<'a, B: Borrow> ops::Mul for &'a Poly { } let n_coeffs = self.coeff.len() + rhs.coeff.len() - 1; let mut coeffs = vec![Fr::zero(); n_coeffs]; - let mut tmp = Safe::new(Box::new(Fr::zero())); + let mut tmp = Fr::zero(); for (i, ca) in self.coeff.iter().enumerate() { for (j, cb) in rhs.coeff.iter().enumerate() { - *tmp = *ca; + tmp = *ca; tmp.mul_assign(cb); - coeffs[i + j].add_assign(&*tmp); + coeffs[i + j].add_assign(&tmp); } } + clear_fr(&mut tmp); Poly::from(coeffs) } } @@ -201,7 +216,7 @@ impl> ops::MulAssign for Poly { impl ops::MulAssign for Poly { fn mul_assign(&mut self, rhs: Fr) { if rhs.is_zero() { - self.zero_secret(); + self.zeroize(); self.coeff.clear(); } else { for c in &mut self.coeff { @@ -216,7 +231,7 @@ impl<'a> ops::Mul<&'a Fr> for Poly { fn mul(mut self, rhs: &Fr) -> Self::Output { if rhs.is_zero() { - self.zero_secret(); + self.zeroize(); self.coeff.clear(); } else { self.coeff.iter_mut().for_each(|c| c.mul_assign(rhs)); @@ -258,12 +273,6 @@ impl ops::Mul for Poly { } } -impl Drop for Poly { - fn drop(&mut self) { - self.zero_secret(); - } -} - /// Creates a new `Poly` instance from a vector of prime field elements representing the /// coefficients of the polynomial. impl From> for Poly { @@ -272,14 +281,6 @@ impl From> for Poly { } } -impl ContainsSecret for Poly { - fn secret_memory(&self) -> MemRange { - let ptr = self.coeff.as_ptr() as *mut u8; - let n_bytes = size_of_val(self.coeff.as_slice()); - MemRange { ptr, n_bytes } - } -} - impl Poly { /// Creates a random polynomial. /// @@ -318,13 +319,12 @@ impl Poly { } /// Returns the polynomial with constant value `c`. - pub fn constant(c: Fr) -> Self { + pub fn constant(mut c: Fr) -> Self { // We create a raw pointer to the field element within this method's stack frame so we can // overwrite that portion of memory with zeros once we have copied the element onto the // heap as part of the vector of polynomial coefficients. - let fr_ptr = &c as *const Fr; let poly = Poly::from(vec![c]); - clear_fr(fr_ptr); + clear_fr(&mut c); poly } @@ -533,6 +533,21 @@ pub struct BivarPoly { coeff: Vec, } +impl Zeroize for BivarPoly { + fn zeroize(&mut self) { + for fr in self.coeff.iter_mut() { + clear_fr(fr) + } + self.degree.zeroize(); + } +} + +impl Drop for BivarPoly { + fn drop(&mut self) { + self.zeroize(); + } +} + impl Clone for BivarPoly { fn clone(&self) -> Self { BivarPoly { @@ -542,12 +557,6 @@ impl Clone for BivarPoly { } } -impl Drop for BivarPoly { - fn drop(&mut self) { - self.zero_secret(); - } -} - /// A debug statement where the `coeff` vector has been redacted. impl Debug for BivarPoly { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { @@ -558,13 +567,6 @@ impl Debug for BivarPoly { } } -impl ContainsSecret for BivarPoly { - fn secret_memory(&self) -> MemRange { - let ptr = self.coeff.as_ptr() as *const Fr as *mut u8; - let n_bytes = size_of_val(self.coeff.as_slice()); - MemRange { ptr, n_bytes } - } -} impl BivarPoly { /// Creates a random polynomial. /// @@ -769,10 +771,11 @@ mod tests { use std::collections::BTreeMap; use super::{coeff_pos, BivarPoly, IntoFr, Poly}; - - use super::{Fr, G1Affine}; - use pairing::{CurveAffine, Field}; + use super::{Fr, G1Affine, G1}; + use pairing::{CurveAffine, CurveProjective, Field}; use rand; + use rand04_compat::RngExt; + use zeroize::Zeroize; #[test] fn test_coeff_pos() { @@ -808,6 +811,25 @@ mod tests { assert_eq!(interp, poly); } + #[test] + fn test_zeroize() { + let mut poly = Poly::monomial(3) + Poly::monomial(2) - 1; + poly.zeroize(); + assert!(poly.is_zero()); + + let mut bi_poly = BivarPoly::random(3, &mut rand::thread_rng()); + let random_commitment = bi_poly.commitment(); + + bi_poly.zeroize(); + + let zero_commitment = bi_poly.commitment(); + assert_ne!(random_commitment, zero_commitment); + + let mut rng = rand::thread_rng(); + let (x, y): (Fr, Fr) = (rng.gen04(), rng.gen04()); + assert_eq!(zero_commitment.evaluate(x, y), G1::zero()); + } + #[test] fn distributed_key_generation() { let mut rng = rand::thread_rng(); diff --git a/src/secret.rs b/src/secret.rs index 4cec30b..4902f37 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -1,90 +1,32 @@ //! Utilities for working with secret values. This module includes functionality for overwriting //! memory with zeros. -use std::mem::{size_of, size_of_val}; -use std::ops::{Deref, DerefMut}; +use zeroize::Zeroize; -use memsec::memzero; - -use crate::Fr; - -pub(crate) const FR_SIZE: usize = size_of::(); +use crate::{Fr, FrRepr}; /// Overwrites a single field element with zeros. -pub(crate) fn clear_fr(fr_ptr: *const Fr) { - unsafe { memzero(fr_ptr as *mut u8, FR_SIZE) }; +pub(crate) fn clear_fr(fr: &mut Fr) { + // TODO: Remove this after pairing support `Zeroize` + let fr_repr = unsafe { &mut *(fr as *mut Fr as *mut FrRepr) }; + fr_repr.0.zeroize(); } -pub(crate) struct MemRange { - pub ptr: *mut u8, - pub n_bytes: usize, -} +#[cfg(test)] +mod tests { + use super::*; + use pairing::Field; + use rand::thread_rng; + use rand04_compat::RngExt; -/// Marks a type as containing some secret value. -pub(crate) trait ContainsSecret { - /// Returns the range of memory marked as secret. - fn secret_memory(&self) -> MemRange; + #[test] + fn test_clear() { + let mut rng = thread_rng(); - /// Overwrites the secret region of memory with zeros. - /// - /// This method should be called upon destruction of every type that implements `ContainsSecret`. - fn zero_secret(&self) { - let MemRange { ptr, n_bytes } = self.secret_memory(); - unsafe { memzero(ptr, n_bytes) }; - } -} - -/// A wrapper around temporary values to ensure that they are cleared on drop. -/// -/// `Safe` is meant to be used a wrapper around `T`, where `T` is either an `&mut U` or -/// `Box`. -pub(crate) struct Safe(T); - -impl Deref for Safe -where - T: DerefMut, -{ - type Target = T::Target; - - fn deref(&self) -> &Self::Target { - &*(self.0) - } -} - -impl DerefMut for Safe -where - T: DerefMut, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - &mut *(self.0) - } -} - -impl Drop for Safe -where - T: DerefMut, -{ - fn drop(&mut self) { - self.zero_secret(); - } -} - -impl ContainsSecret for Safe -where - T: DerefMut, -{ - fn secret_memory(&self) -> MemRange { - let ptr = &*self.0 as *const T::Target as *mut u8; - let n_bytes = size_of_val(&*self.0); - MemRange { ptr, n_bytes } - } -} - -impl Safe -where - T: DerefMut, -{ - pub(crate) fn new(x: T) -> Self { - Safe(x) + let mut fr: Fr = rng.gen04(); + assert_ne!(fr, Fr::zero()); + + clear_fr(&mut fr); + assert_eq!(fr, Fr::zero()); } }