diff --git a/Cargo.toml b/Cargo.toml index 89261339..9944e8bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,11 @@ version = "1.0" default-features = false features = ["std"] +[dependencies.zeroize] +optional = true +version = "0.6" +default-features = false + [dev-dependencies.serde_test] version = "1.0" diff --git a/README.md b/README.md index f7eefed8..54bc6032 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,10 @@ Implementations for `i128` and `u128` are only available with Rust 1.26 and later. The build script automatically detects this, but you can make it mandatory by enabling the `i128` crate feature. +The [`zeroize`](https://crates.io/crates/zeroize) feature causes +`BigInt`/`BigUint` to be wiped from memory on `Drop`, which may improve security +in some applications. `zeroize` requires Rust 1.31. + ## Releases Release notes are available in [RELEASES.md](RELEASES.md). diff --git a/ci/test_full.sh b/ci/test_full.sh index 3021b040..ce3918a0 100755 --- a/ci/test_full.sh +++ b/ci/test_full.sh @@ -11,6 +11,9 @@ fi if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable|1.26.0)$ ]]; then FEATURES="$FEATURES i128" fi +if [[ "$TRAVIS_RUST_VERSION" =~ ^(nightly|beta|stable|1.31.0)$ ]]; then + FEATURES="$FEATURES zeroize" +fi # num-bigint should build and test everywhere. cargo build --verbose diff --git a/src/algorithms.rs b/src/algorithms.rs index 90ef702b..4e2e51ec 100644 --- a/src/algorithms.rs +++ b/src/algorithms.rs @@ -610,6 +610,7 @@ pub fn ilog2(v: T) -> usize { pub fn biguint_shl(n: Cow, bits: usize) -> BigUint { let n_unit = bits / big_digit::BITS; let mut data = match n_unit { + #[cfg(not(feature = "zeroize"))] 0 => n.into_owned().data, _ => { let len = n_unit + n.data.len() + 1; @@ -644,6 +645,9 @@ pub fn biguint_shr(n: Cow, bits: usize) -> BigUint { } let mut data = match n { Cow::Borrowed(n) => n.data[n_unit..].to_vec(), + #[cfg(feature = "zeroize")] + Cow::Owned(mut n) => n.data[n_unit..].to_vec(), + #[cfg(not(feature = "zeroize"))] Cow::Owned(mut n) => { n.data.drain(..n_unit); n.data diff --git a/src/biguint.rs b/src/biguint.rs index da953dac..b228fb5b 100644 --- a/src/biguint.rs +++ b/src/biguint.rs @@ -79,6 +79,14 @@ impl Default for BigUint { } } +#[cfg(feature = "zeroize")] +impl Drop for BigUint { + fn drop(&mut self) { + use zeroize::Zeroize; + self.data.zeroize(); + } +} + impl fmt::Display for BigUint { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.pad_integral(true, "", &self.to_str_radix(10)) diff --git a/src/lib.rs b/src/lib.rs index 5503bfcc..1674e690 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,6 +84,8 @@ extern crate rand; #[cfg(feature = "serde")] extern crate serde; +#[cfg(feature = "zeroize")] +extern crate zeroize; extern crate num_integer as integer; extern crate num_traits as traits; diff --git a/src/monty.rs b/src/monty.rs index bc3546ec..ac736a8e 100644 --- a/src/monty.rs +++ b/src/monty.rs @@ -57,13 +57,12 @@ impl<'a> MontyReducer<'a> { // // Reference: // Brent & Zimmermann, Modern Computer Arithmetic, v0.5.9, Algorithm 2.6 -fn monty_redc(a: BigUint, mr: &MontyReducer) -> BigUint { - let mut c = a.data; +fn monty_redc(mut a: BigUint, mr: &MontyReducer) -> BigUint { let n = &mr.n.data; let n_size = n.len(); // Allocate sufficient work space - c.resize(2 * n_size + 2, 0); + a.data.resize(2 * n_size + 2, 0); // β is the size of a word, in this case 32 bits. So "a mod β" is // equivalent to masking a to 32 bits. @@ -73,15 +72,15 @@ fn monty_redc(a: BigUint, mr: &MontyReducer) -> BigUint { // 1: for i = 0 to (n-1) for i in 0..n_size { // 2: q_i <- mu*c_i mod β - let q_i = c[i].wrapping_mul(mu); + let q_i = a.data[i].wrapping_mul(mu); // 3: C <- C + q_i * N * β^i - super::algorithms::mac_digit(&mut c[i..], n, q_i); + super::algorithms::mac_digit(&mut a.data[i..], n, q_i); } // 4: R <- C * β^(-n) // This is an n-word bitshift, equivalent to skipping n words. - let ret = BigUint::new(c[n_size..].to_vec()); + let ret = BigUint::new(a.data[n_size..].to_vec()); // 5: if R >= β^n then return R-N else return R. if &ret < mr.n {