From 33ba19d3819107c40cff5be21425b3b9ccfe7d3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Miguel=20Garc=C3=ADa?= Date: Wed, 15 Jul 2020 12:35:00 +0200 Subject: [PATCH 1/2] Serde optional and `Clear_on_drop` replacement. (#1) * Serde is optional * Replace clear_on_drop by zeroize, to support wasm and remove inline asm. * Support different backends: `u32_backend`, `u64_backend`, and `avx2_backend`(default in `std`) --- CHANGELOG.md | 7 ++++++ Cargo.toml | 26 +++++++++++++++------- src/lib.rs | 1 + src/r1cs/prover.rs | 42 ++++++++++++------------------------ src/range_proof/messages.rs | 15 ++++++++----- src/range_proof/mod.rs | 7 ++++-- src/range_proof/party.rs | 43 ++++++++++++++++--------------------- src/util.rs | 38 +++++++++++--------------------- 8 files changed, 86 insertions(+), 93 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7edbe28c..76b5ede6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ Entries are listed in reverse chronological order. +## 2.0.1 + +* `Serde` is optional. +* Adds new backends for `curve25519-dalek`: `u32_backend`, `u64_backend`, + and `avx2_backend`(default on `std`). +* Replace `clear_on_drop` by `zeroize`. + ## 2.0.0 * Switch from `failure` to `std`-compatible errors via `thiserror`. diff --git a/Cargo.toml b/Cargo.toml index 2cc94403..924908da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ name = "bulletproofs" # - update html_root_url # - ensure yoloproofs was disabled in an atomic (revertable) commit # - update CHANGELOG -version = "2.0.0" +version = "2.0.1" authors = ["Cathie Yun ", "Henry de Valence ", "Oleg Andreev "] @@ -18,18 +18,18 @@ description = "A pure-Rust implementation of Bulletproofs using Ristretto" edition = "2018" [dependencies] -curve25519-dalek = { version = "2", default-features = false, features = ["u64_backend", "nightly", "serde", "alloc"] } +curve25519-dalek = { version = "2", default-features = false, features = [ "alloc", "nightly"] } subtle = { version = "2", default-features = false } sha3 = { version = "0.8", default-features = false } digest = { version = "0.8", default-features = false } rand_core = { version = "0.5", default-features = false, features = ["alloc"] } -rand = { version = "0.7", default-features = false, optional = true } +rand = { version = "0.7", default-features = false } byteorder = { version = "1", default-features = false } -serde = { version = "1", default-features = false, features = ["alloc"] } -serde_derive = { version = "1", default-features = false } +serde = { version = "1", default-features = false, features = ["alloc"], optional = true } +serde_derive = { version = "1", default-features = false, optional = true } thiserror = { version = "1", optional = true } merlin = { version = "2", default-features = false } -clear_on_drop = { version = "0.2", default-features = false, features = ["nightly"] } +zeroize = { version = "1.1.0", default-features = false, features = ["alloc"], optional = true } [dev-dependencies] hex = "0.3" @@ -38,10 +38,20 @@ bincode = "1" rand_chacha = "0.2" [features] -default = ["std", "avx2_backend"] +default = ["std", "avx2_backend", "zeroize"] + +u64_backend = ["curve25519-dalek/u64_backend", ] avx2_backend = ["curve25519-dalek/avx2_backend"] +u32_backend = ["curve25519-dalek/u32_backend"] # yoloproofs = [] -std = ["rand", "rand/std", "thiserror"] + +std = [ + "serde", + "serde_derive", + "curve25519-dalek/serde", + "rand/std", + "thiserror", +] [[test]] name = "range_proof" diff --git a/src/lib.rs b/src/lib.rs index 41b1edd0..a3e9fd83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ extern crate alloc; +#[cfg(feature = "serde")] #[macro_use] extern crate serde_derive; diff --git a/src/r1cs/prover.rs b/src/r1cs/prover.rs index bee4a945..9851dedb 100644 --- a/src/r1cs/prover.rs +++ b/src/r1cs/prover.rs @@ -1,6 +1,7 @@ #![allow(non_snake_case)] -use clear_on_drop::clear::Clear; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; use core::mem; use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use curve25519_dalek::scalar::Scalar; @@ -62,26 +63,15 @@ pub struct RandomizingProver<'t, 'g> { } /// Overwrite secrets with null bytes when they go out of scope. +#[cfg(feature = "zeroize")] impl<'t, 'g> Drop for Prover<'t, 'g> { fn drop(&mut self) { - self.v.clear(); - self.v_blinding.clear(); - - // Important: due to how ClearOnDrop auto-implements InitializableFromZeroed - // for T: Default, calling .clear() on Vec compiles, but does not - // clear the content. Instead, it only clears the Vec's header. - // Clearing the underlying buffer item-by-item will do the job, but will - // keep the header as-is, which is fine since the header does not contain secrets. - for e in self.a_L.iter_mut() { - e.clear(); - } - for e in self.a_R.iter_mut() { - e.clear(); - } - for e in self.a_O.iter_mut() { - e.clear(); - } - // XXX use ClearOnDrop instead of doing the above + self.v.zeroize(); + self.v_blinding.zeroize(); + + self.a_L.zeroize(); + self.a_R.zeroize(); + self.a_O.zeroize(); } } @@ -666,16 +656,12 @@ impl<'t, 'g> Prover<'t, 'g> { r_vec, ); - // We do not yet have a ClearOnDrop wrapper for Vec. - // When PR 202 [1] is merged, we can simply wrap s_L and s_R at the point of creation. - // [1] https://github.com/dalek-cryptography/curve25519-dalek/pull/202 - for scalar in s_L1 - .iter_mut() - .chain(s_L2.iter_mut()) - .chain(s_R1.iter_mut()) - .chain(s_R2.iter_mut()) + #[cfg(feature = "zeroize")] { - scalar.clear(); + s_L1.zeroize(); + s_L2.zeroize(); + s_R1.zeroize(); + s_R2.zeroize(); } Ok(R1CSProof { diff --git a/src/range_proof/messages.rs b/src/range_proof/messages.rs index 8a563fb0..a791591d 100644 --- a/src/range_proof/messages.rs +++ b/src/range_proof/messages.rs @@ -14,7 +14,8 @@ use curve25519_dalek::scalar::Scalar; use crate::generators::{BulletproofGens, PedersenGens}; /// A commitment to the bits of a party's value. -#[derive(Serialize, Deserialize, Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct BitCommitment { pub(super) V_j: CompressedRistretto, pub(super) A_j: RistrettoPoint, @@ -22,28 +23,32 @@ pub struct BitCommitment { } /// Challenge values derived from all parties' [`BitCommitment`]s. -#[derive(Serialize, Deserialize, Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct BitChallenge { pub(super) y: Scalar, pub(super) z: Scalar, } /// A commitment to a party's polynomial coefficents. -#[derive(Serialize, Deserialize, Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct PolyCommitment { pub(super) T_1_j: RistrettoPoint, pub(super) T_2_j: RistrettoPoint, } /// Challenge values derived from all parties' [`PolyCommitment`]s. -#[derive(Serialize, Deserialize, Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct PolyChallenge { pub(super) x: Scalar, } /// A party's proof share, ready for aggregation into the final /// [`RangeProof`](::RangeProof). -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct ProofShare { pub(super) t_x: Scalar, pub(super) t_x_blinding: Scalar, diff --git a/src/range_proof/mod.rs b/src/range_proof/mod.rs index 6eee8d31..294abe3b 100644 --- a/src/range_proof/mod.rs +++ b/src/range_proof/mod.rs @@ -23,8 +23,9 @@ use crate::transcript::TranscriptProtocol; use crate::util; use rand_core::{CryptoRng, RngCore}; -use serde::de::Visitor; -use serde::{self, Deserialize, Deserializer, Serialize, Serializer}; + +#[cfg(feature = "serde")] +use serde::{self, Deserialize, Deserializer, Serialize, Serializer, de::Visitor}; // Modules for MPC protocol @@ -538,6 +539,7 @@ impl RangeProof { } } +#[cfg(feature = "serde")] impl Serialize for RangeProof { fn serialize(&self, serializer: S) -> Result where @@ -547,6 +549,7 @@ impl Serialize for RangeProof { } } +#[cfg(feature = "serde")] impl<'de> Deserialize<'de> for RangeProof { fn deserialize(deserializer: D) -> Result where diff --git a/src/range_proof/party.rs b/src/range_proof/party.rs index ebb232cc..6365f850 100644 --- a/src/range_proof/party.rs +++ b/src/range_proof/party.rs @@ -13,7 +13,8 @@ extern crate alloc; use alloc::vec::Vec; -use clear_on_drop::clear::Clear; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; use core::iter; use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use curve25519_dalek::scalar::Scalar; @@ -145,10 +146,11 @@ impl<'a> PartyAwaitingPosition<'a> { } /// Overwrite secrets with null bytes when they go out of scope. +#[cfg(feature = "zeroize")] impl<'a> Drop for PartyAwaitingPosition<'a> { fn drop(&mut self) { - self.v.clear(); - self.v_blinding.clear(); + self.v.zeroize(); + self.v_blinding.zeroize(); } } @@ -238,24 +240,16 @@ impl<'a> PartyAwaitingBitChallenge<'a> { } /// Overwrite secrets with null bytes when they go out of scope. +#[cfg(feature = "zeroize")] impl<'a> Drop for PartyAwaitingBitChallenge<'a> { fn drop(&mut self) { - self.v.clear(); - self.v_blinding.clear(); - self.a_blinding.clear(); - self.s_blinding.clear(); - - // Important: due to how ClearOnDrop auto-implements InitializableFromZeroed - // for T: Default, calling .clear() on Vec compiles, but does not - // clear the content. Instead, it only clears the Vec's header. - // Clearing the underlying buffer item-by-item will do the job, but will - // keep the header as-is, which is fine since the header does not contain secrets. - for e in self.s_L.iter_mut() { - e.clear(); - } - for e in self.s_R.iter_mut() { - e.clear(); - } + self.v.zeroize(); + self.v_blinding.zeroize(); + self.a_blinding.zeroize(); + self.s_blinding.zeroize(); + + self.s_L.zeroize(); + self.s_R.zeroize(); } } @@ -306,13 +300,14 @@ impl PartyAwaitingPolyChallenge { } /// Overwrite secrets with null bytes when they go out of scope. +#[cfg(feature = "zeroize")] impl Drop for PartyAwaitingPolyChallenge { fn drop(&mut self) { - self.v_blinding.clear(); - self.a_blinding.clear(); - self.s_blinding.clear(); - self.t_1_blinding.clear(); - self.t_2_blinding.clear(); + self.v_blinding.zeroize(); + self.a_blinding.zeroize(); + self.s_blinding.zeroize(); + self.t_1_blinding.zeroize(); + self.t_2_blinding.zeroize(); // Note: polynomials r_poly, l_poly and t_poly // are cleared within their own Drop impls. diff --git a/src/util.rs b/src/util.rs index dd7ce2fe..34261c7b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -5,12 +5,14 @@ extern crate alloc; use alloc::vec; use alloc::vec::Vec; -use clear_on_drop::clear::Clear; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; use curve25519_dalek::scalar::Scalar; use crate::inner_product_proof::inner_product; /// Represents a degree-1 vector polynomial \\(\mathbf{a} + \mathbf{b} \cdot x\\). +#[cfg_attr(feature = "zeroize", derive(Zeroize), zeroize(drop))] pub struct VecPoly1(pub Vec, pub Vec); /// Represents a degree-3 vector polynomial @@ -24,6 +26,7 @@ pub struct VecPoly3( ); /// Represents a degree-2 scalar polynomial \\(a + b \cdot x + c \cdot x^2\\) +#[cfg_attr(feature = "zeroize", derive(Zeroize), zeroize(drop))] pub struct Poly2(pub Scalar, pub Scalar, pub Scalar); /// Represents a degree-6 scalar polynomial, without the zeroth degree @@ -167,25 +170,6 @@ impl Poly6 { } } -impl Drop for VecPoly1 { - fn drop(&mut self) { - for e in self.0.iter_mut() { - e.clear(); - } - for e in self.1.iter_mut() { - e.clear(); - } - } -} - -impl Drop for Poly2 { - fn drop(&mut self) { - self.0.clear(); - self.1.clear(); - self.2.clear(); - } -} - #[cfg(feature = "yoloproofs")] impl Drop for VecPoly3 { fn drop(&mut self) { @@ -350,12 +334,13 @@ mod tests { assert_eq!(sum_of_powers_slow(&x, 6), Scalar::from(111111u64)); } + #[cfg(feature = "zeroize")] #[test] - fn vec_of_scalars_clear_on_drop() { + fn vec_of_scalars_zeroize() { let mut v = vec![Scalar::from(24u64), Scalar::from(42u64)]; for e in v.iter_mut() { - e.clear(); + e.zeroize(); } fn flat_slice(x: &[T]) -> &[u8] { @@ -370,17 +355,18 @@ mod tests { assert_eq!(v[1], Scalar::zero()); } + #[cfg(feature = "zeroize")] #[test] - fn tuple_of_scalars_clear_on_drop() { + fn tuple_of_scalars_zeroize() { let mut v = Poly2( Scalar::from(24u64), Scalar::from(42u64), Scalar::from(255u64), ); - v.0.clear(); - v.1.clear(); - v.2.clear(); + v.0.zeroize(); + v.1.zeroize(); + v.2.zeroize(); fn as_bytes(x: &T) -> &[u8] { use core::mem; From be11c804e6d47e48600b091d74c937270f4b34e0 Mon Sep 17 00:00:00 2001 From: miguel Date: Wed, 15 Jul 2020 14:07:26 +0200 Subject: [PATCH 2/2] Cargo fmt --- src/r1cs/prover.rs | 4 ++-- src/range_proof/mod.rs | 2 +- src/range_proof/party.rs | 4 ++-- src/util.rs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/r1cs/prover.rs b/src/r1cs/prover.rs index 9851dedb..b4172991 100644 --- a/src/r1cs/prover.rs +++ b/src/r1cs/prover.rs @@ -1,12 +1,12 @@ #![allow(non_snake_case)] -#[cfg(feature = "zeroize")] -use zeroize::Zeroize; use core::mem; use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use curve25519_dalek::scalar::Scalar; use curve25519_dalek::traits::{Identity, MultiscalarMul}; use merlin::Transcript; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; use super::{ ConstraintSystem, LinearCombination, R1CSProof, RandomizableConstraintSystem, diff --git a/src/range_proof/mod.rs b/src/range_proof/mod.rs index 294abe3b..576757f7 100644 --- a/src/range_proof/mod.rs +++ b/src/range_proof/mod.rs @@ -25,7 +25,7 @@ use crate::util; use rand_core::{CryptoRng, RngCore}; #[cfg(feature = "serde")] -use serde::{self, Deserialize, Deserializer, Serialize, Serializer, de::Visitor}; +use serde::{self, de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; // Modules for MPC protocol diff --git a/src/range_proof/party.rs b/src/range_proof/party.rs index 6365f850..fcfc80bd 100644 --- a/src/range_proof/party.rs +++ b/src/range_proof/party.rs @@ -13,13 +13,13 @@ extern crate alloc; use alloc::vec::Vec; -#[cfg(feature = "zeroize")] -use zeroize::Zeroize; use core::iter; use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint}; use curve25519_dalek::scalar::Scalar; use curve25519_dalek::traits::MultiscalarMul; use rand_core::{CryptoRng, RngCore}; +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; use crate::errors::MPCError; use crate::generators::{BulletproofGens, PedersenGens}; diff --git a/src/util.rs b/src/util.rs index 34261c7b..42f63bcd 100644 --- a/src/util.rs +++ b/src/util.rs @@ -5,9 +5,9 @@ extern crate alloc; use alloc::vec; use alloc::vec::Vec; +use curve25519_dalek::scalar::Scalar; #[cfg(feature = "zeroize")] use zeroize::Zeroize; -use curve25519_dalek::scalar::Scalar; use crate::inner_product_proof::inner_product;