diff --git a/Cargo.toml b/Cargo.toml index 9cca7f2..74aa449 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ pbkdf2 = { version = "0.4.0", default-features = false } rand = "0.7.3" once_cell = { version = "1.4.0", features = [ "parking_lot" ] } unicode-normalization = "0.1.13" +zeroize = { version = "1.1.1", features = ["zeroize_derive"] } [dev-dependencies] hex = "0.4.2" diff --git a/src/language.rs b/src/language.rs index 49e5ca6..76e738a 100644 --- a/src/language.rs +++ b/src/language.rs @@ -2,6 +2,7 @@ use crate::error::ErrorKind; use crate::util::{Bits, Bits11}; use failure::Error; use rustc_hash::FxHashMap; +use zeroize::Zeroize; pub struct WordMap { inner: FxHashMap<&'static str, Bits11>, @@ -32,7 +33,7 @@ impl WordList { let count = self.inner[start..].iter() .take_while(|word| word.starts_with(prefix)) .count(); - + &self.inner[start..start + count] } } @@ -113,7 +114,8 @@ mod lazy { /// /// [Mnemonic]: ./mnemonic/struct.Mnemonic.html /// [Seed]: ./seed/struct.Seed.html -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Zeroize)] +#[zeroize(drop)] pub enum Language { English, #[cfg(feature = "chinese-simplified")] diff --git a/src/mnemonic.rs b/src/mnemonic.rs index 9b676fe..12ff4c2 100644 --- a/src/mnemonic.rs +++ b/src/mnemonic.rs @@ -1,6 +1,8 @@ use std::fmt; use failure::Error; +use std::mem; use unicode_normalization::UnicodeNormalization; +use zeroize::Zeroize; use crate::crypto::{gen_random_bytes, sha256_first_byte}; use crate::error::ErrorKind; use crate::language::Language; @@ -22,6 +24,8 @@ use crate::util::{checksum, BitWriter, IterExt}; /// but beware that the entropy value is **not the same thing** as an HD wallet seed, and should /// *never* be used that way. /// +/// [`Mnemonic`][Mnemonic] implements [`Zeroize`][Zeroize], so it's bytes will be zeroed when it's dropped. +/// /// [Mnemonic]: ./mnemonic/struct.Mnemonic.html /// [Mnemonic::new()]: ./mnemonic/struct.Mnemonic.html#method.new /// [Mnemonic::from_phrase()]: ./mnemonic/struct.Mnemonic.html#method.from_phrase @@ -30,7 +34,8 @@ use crate::util::{checksum, BitWriter, IterExt}; /// [Seed::new()]: ./seed/struct.Seed.html#method.new /// [Seed::as_bytes()]: ./seed/struct.Seed.html#method.as_bytes /// -#[derive(Clone)] +#[derive(Clone, Zeroize)] +#[zeroize(drop)] pub struct Mnemonic { phrase: String, lang: Language, @@ -220,10 +225,11 @@ impl Mnemonic { } /// Consume the `Mnemonic` and return the phrase as a `String`. - /// - /// This operation doesn't perform any allocations. - pub fn into_phrase(self) -> String { - self.phrase + pub fn into_phrase(mut self) -> String { + // Create an empty string and swap values with the mnemonic's phrase. + // This allows `Mnemonic` to implement `Drop`, while still returning the phrase. + mem::replace(&mut self.phrase, String:new()); + phrase } /// Get the original entropy value of the mnemonic phrase as a slice. diff --git a/src/seed.rs b/src/seed.rs index babba88..e2bdae7 100644 --- a/src/seed.rs +++ b/src/seed.rs @@ -1,5 +1,6 @@ use std::fmt; use unicode_normalization::UnicodeNormalization; +use zeroize::Zeroize; use crate::crypto::pbkdf2; use crate::mnemonic::Mnemonic; @@ -13,11 +14,14 @@ use crate::mnemonic::Mnemonic; /// HD wallet addresses using another crate (deriving HD wallet addresses is outside the scope of this /// crate and the BIP39 standard). /// +/// [`Seed`][Seed] implements [`Zeroize`][Zeroize], so it's bytes will be zeroed when it's dropped. +/// /// [Mnemonic]: ./mnemonic/struct.Mnemonic.html /// [Seed]: ./seed/struct.Seed.html /// [Seed::as_bytes()]: ./seed/struct.Seed.html#method.as_bytes -#[derive(Clone)] +#[derive(Clone, Zeroize)] +#[zeroize(drop)] pub struct Seed { bytes: Vec, }