diff --git a/Cargo.toml b/Cargo.toml index 7a2021ace..f18341aa8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ secp256k1-sys = { version = "0.4.0", default-features = false, path = "./secp256 bitcoin_hashes = { version = "0.9", optional = true } rand = { version = "0.6", default-features = false, optional = true } serde = { version = "1.0", default-features = false, optional = true } - +zeroize = { version = "1.2", default-features = false, optional = true } [dev-dependencies] rand = "0.6" diff --git a/README.md b/README.md index 4e03acbf8..a2e14b30b 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,20 @@ before_script: fi ``` +## Zeroize + +Zeroizing secrets is supported using the `zeroize` feature. Enabling this +feature changes the MSRV to 1.44 (see https://docs.rs/zeroize/1.2.0/zeroize/). + +The aim of the `zeroize` feature is to allow users of this library to reduce the +number of copies of secrets in memory (by wrapping our types, implementing +`Zeroize` and implementing `Drop` (i.e. derive `Zeroize`). Our aim is not to +guarantee that there are no copies left un-zeroed in memory. Internally we +absolutely do leave secrets on the stack. + +> I think even zeroing some out the copies is better than none, because every +> copy makes a Heartbleed-like attack a little easier. + ## Fuzzing If you want to fuzz this library, or any library which depends on it, you will diff --git a/secp256k1-sys/src/macros.rs b/secp256k1-sys/src/macros.rs index 5ca019840..c4dbcdbdf 100644 --- a/secp256k1-sys/src/macros.rs +++ b/secp256k1-sys/src/macros.rs @@ -83,6 +83,14 @@ macro_rules! impl_array_newtype { } } + #[cfg(feature = "zeroize")] + impl zeroize::Zeroize for $thing { + fn zeroize(&mut self) { + let &mut $thing(ref mut dat) = self; + dat.zeroize() + } + } + impl ::core::ops::Index for $thing { type Output = $ty; diff --git a/src/key.rs b/src/key.rs index 2492f0436..9341cf85f 100644 --- a/src/key.rs +++ b/src/key.rs @@ -850,4 +850,20 @@ mod test { assert_tokens(&pk.compact(), &[Token::BorrowedBytes(&PK_BYTES[..])]); assert_tokens(&pk.readable(), &[Token::BorrowedStr(PK_STR)]); } + + #[cfg(feature = "zeroize")] + #[test] + fn zeroize_secret_key() { + use zeroize::Zeroize; + + let mut sk = SecretKey::new(&mut thread_rng()); + sk.zeroize(); + + let ptr = &sk.0[0]; + + for _ in 0..32 { + assert_eq!(*ptr, 0x00); + } + } } + diff --git a/src/lib.rs b/src/lib.rs index 10ebd8fb1..c3a81f768 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -135,6 +135,7 @@ pub use secp256k1_sys as ffi; #[cfg(any(test, feature = "rand"))] use rand::Rng; #[cfg(any(test, feature = "std"))] extern crate core; #[cfg(all(test, target_arch = "wasm32"))] extern crate wasm_bindgen_test; +#[cfg(feature = "zeroize")] extern crate zeroize; use core::{fmt, ptr, str};