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
This commit is contained in:
Weiliang Li 2019-11-08 18:53:40 +09:00 committed by Andreas Fackler
parent 48c7b7bd40
commit 036b720b7f
8 changed files with 198 additions and 154 deletions

View File

@ -1,6 +1,6 @@
language: rust
rust:
- 1.35.0
- 1.39.0
cache:
cargo: true
timeout: 1200

View File

@ -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"]

View File

@ -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.

View File

@ -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);
}

27
src/codec_impl.rs Normal file
View File

@ -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<u8> {
let encoded = bincode::serialize(&self).unwrap();
codec::Encode::encode(&encoded)
}
}
impl codec::Decode for $type {
fn decode<I: codec::Input>(value: &mut I) -> std::result::Result<Self, codec::Error> {
let decoded: Vec<u8> = 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);

View File

@ -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<Fr>);
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<u8> = $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!(<G1Affine as CurveAffine>::Compressed::size(), PK_SIZE);
assert_eq!(<G2Affine as CurveAffine>::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);
}
}

View File

@ -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<Fr>,
}
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<Poly>> ops::Mul<B> 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<B: Borrow<Self>> ops::MulAssign<B> for Poly {
impl ops::MulAssign<Fr> 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<u64> 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<Vec<Fr>> for Poly {
@ -272,14 +281,6 @@ impl From<Vec<Fr>> 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<Fr>,
}
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();

View File

@ -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::<Fr>();
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<T>` is meant to be used a wrapper around `T`, where `T` is either an `&mut U` or
/// `Box<U>`.
pub(crate) struct Safe<T: DerefMut>(T);
impl<T> Deref for Safe<T>
where
T: DerefMut,
{
type Target = T::Target;
fn deref(&self) -> &Self::Target {
&*(self.0)
}
}
impl<T> DerefMut for Safe<T>
where
T: DerefMut,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut *(self.0)
}
}
impl<T> Drop for Safe<T>
where
T: DerefMut,
{
fn drop(&mut self) {
self.zero_secret();
}
}
impl<T> ContainsSecret for Safe<T>
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<T> Safe<T>
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());
}
}