Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serde optional and Clear_on_drop replaced by Zeroize #330

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
26 changes: 18 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 <cathieyun@gmail.com>",
"Henry de Valence <hdevalence@hdevalence.ca>",
"Oleg Andreev <oleganza@gmail.com>"]
Expand All @@ -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"
Expand All @@ -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"
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

extern crate alloc;

#[cfg(feature = "serde")]
#[macro_use]
extern crate serde_derive;

Expand Down
42 changes: 14 additions & 28 deletions src/r1cs/prover.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
#![allow(non_snake_case)]

use clear_on_drop::clear::Clear;
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,
Expand Down Expand Up @@ -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();
}
}

Expand Down Expand Up @@ -666,16 +656,12 @@ impl<'t, 'g> Prover<'t, 'g> {
r_vec,
);

// We do not yet have a ClearOnDrop wrapper for Vec<Scalar>.
// 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 {
Expand Down
15 changes: 10 additions & 5 deletions src/range_proof/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,41 @@ 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,
pub(super) S_j: RistrettoPoint,
}

/// 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,
Expand Down
7 changes: 5 additions & 2 deletions src/range_proof/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, de::Visitor, Deserialize, Deserializer, Serialize, Serializer};

// Modules for MPC protocol

Expand Down Expand Up @@ -538,6 +539,7 @@ impl RangeProof {
}
}

#[cfg(feature = "serde")]
impl Serialize for RangeProof {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
Expand All @@ -547,6 +549,7 @@ impl Serialize for RangeProof {
}
}

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for RangeProof {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
Expand Down
43 changes: 19 additions & 24 deletions src/range_proof/party.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
extern crate alloc;

use alloc::vec::Vec;
use clear_on_drop::clear::Clear;
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};
Expand Down Expand Up @@ -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();
}
}

Expand Down Expand Up @@ -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();
}
}

Expand Down Expand Up @@ -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.
Expand Down
38 changes: 12 additions & 26 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ extern crate alloc;

use alloc::vec;
use alloc::vec::Vec;
use clear_on_drop::clear::Clear;
use curve25519_dalek::scalar::Scalar;
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;

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<Scalar>, pub Vec<Scalar>);

/// Represents a degree-3 vector polynomial
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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<T>(x: &[T]) -> &[u8] {
Expand All @@ -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<T>(x: &T) -> &[u8] {
use core::mem;
Expand Down