diff --git a/Cargo.lock b/Cargo.lock index a5834ffc9984ee27ff69fcddc4201fa4a6f7d5e5..8e35fc861776771821ebacd5f337cf572e7f35e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -586,6 +586,7 @@ dependencies = [ name = "durs-core" version = "0.3.0-dev" dependencies = [ + "clear_on_drop 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "dubp-currency-params 0.2.0", "dup-crypto 0.7.0", diff --git a/lib/core/conf/src/keys.rs b/lib/core/conf/src/keys.rs index 6d58de07e01422e847d99712a2e0215198682b0c..8f12e74d354804ffab9ef461cc558372c750a90e 100644 --- a/lib/core/conf/src/keys.rs +++ b/lib/core/conf/src/keys.rs @@ -42,25 +42,25 @@ impl From<std::io::Error> for WizardError { /// Modify network keys command pub fn modify_network_keys( - salt: &str, - password: &str, + salt: String, + password: String, mut key_pairs: DuniterKeyPairs, ) -> DuniterKeyPairs { let generator = ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters(); key_pairs.network_keypair = - KeyPairEnum::Ed25519(generator.generate(salt.as_bytes(), password.as_bytes())); + KeyPairEnum::Ed25519(generator.generate(ed25519::SaltedPassword::new(salt, password))); key_pairs } /// Modify member keys command pub fn modify_member_keys( - salt: &str, - password: &str, + salt: String, + password: String, mut key_pairs: DuniterKeyPairs, ) -> DuniterKeyPairs { let generator = ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters(); key_pairs.member_keypair = Some(KeyPairEnum::Ed25519( - generator.generate(salt.as_bytes(), password.as_bytes()), + generator.generate(ed25519::SaltedPassword::new(salt, password)), )); key_pairs } @@ -129,10 +129,9 @@ fn salt_password_prompt() -> Result<KeyPairEnum, WizardError> { let password = rpassword::prompt_password_stdout("Password: ")?; if !password.is_empty() { let generator = ed25519::KeyPairFromSaltedPasswordGenerator::with_default_parameters(); - let key_pairs = KeyPairEnum::Ed25519(generator.generate( - salt.into_bytes().as_slice(), - password.into_bytes().as_slice(), - )); + let key_pairs = KeyPairEnum::Ed25519( + generator.generate(ed25519::SaltedPassword::new(salt, password)), + ); Ok(key_pairs) } else { Err(WizardError::BadInput) @@ -191,7 +190,8 @@ mod tests { }), member_keypair: None, }; - let result_key_pairs = modify_member_keys(SALT_TEST, PASSWORD_TEST, key_pairs); + let result_key_pairs = + modify_member_keys(SALT_TEST.to_owned(), PASSWORD_TEST.to_owned(), key_pairs); // We expect network key not to change assert_eq!( result_key_pairs.network_keypair.public_key(), @@ -239,7 +239,8 @@ mod tests { }), member_keypair: None, }; - let result_key_pairs = modify_network_keys(SALT_TEST, PASSWORD_TEST, key_pairs); + let result_key_pairs = + modify_network_keys(SALT_TEST.to_owned(), PASSWORD_TEST.to_owned(), key_pairs); // We expect network key to update assert_eq!( result_key_pairs.network_keypair.public_key(), diff --git a/lib/core/core/Cargo.toml b/lib/core/core/Cargo.toml index 4f947dc4a0328b3dd3d377eaa8c44f54ca6a8c4c..86f6c5440444365360dc5eab1f16746cf6c25d84 100644 --- a/lib/core/core/Cargo.toml +++ b/lib/core/core/Cargo.toml @@ -10,6 +10,7 @@ edition = "2018" path = "src/lib.rs" [dependencies] +clear_on_drop = "0.2.3" dirs = "1.0.2" durs-blockchain = { path = "../../modules/blockchain/blockchain" } durs-common-tools = { path = "../../tools/common-tools" } diff --git a/lib/core/core/src/commands/keys.rs b/lib/core/core/src/commands/keys.rs index b950aeb60910c529124f191dcf9c6da96836ce7d..7190c1c9b422ccc920aba712adebfd2e602df348 100644 --- a/lib/core/core/src/commands/keys.rs +++ b/lib/core/core/src/commands/keys.rs @@ -18,6 +18,7 @@ use crate::commands::DursExecutableCoreCommand; use crate::errors::DursCoreError; use crate::DursCore; +use clear_on_drop::clear::Clear; use durs_conf::keys::*; use durs_conf::DuRsConf; @@ -124,6 +125,14 @@ pub struct SaltPasswordOpt { pub password: String, } +impl Drop for SaltPasswordOpt { + #[inline] + fn drop(&mut self) { + <String as Clear>::clear(&mut self.salt); + <String as Clear>::clear(&mut self.password); + } +} + #[derive(StructOpt, Debug, Copy, Clone)] /// WizardOpt pub struct WizardOpt {} @@ -146,14 +155,20 @@ impl DursExecutableCoreCommand for KeysOpt { } KeysSubCommand::Modify(modify_opt) => match modify_opt.subcommand { ModifySubCommand::NetworkSaltPassword(network_opt) => { - let new_keypairs = - modify_network_keys(&network_opt.salt, &network_opt.password, keypairs); + let new_keypairs = modify_network_keys( + network_opt.salt.clone(), + network_opt.password.clone(), + keypairs, + ); save_keypairs(profile_path, &keypairs_file, new_keypairs) .map_err(DursCoreError::FailWriteKeypairsFile) } ModifySubCommand::MemberSaltPassword(member_opt) => { - let new_keypairs = - modify_member_keys(&member_opt.salt, &member_opt.password, keypairs); + let new_keypairs = modify_member_keys( + member_opt.salt.clone(), + member_opt.password.clone(), + keypairs, + ); save_keypairs(profile_path, &keypairs_file, new_keypairs) .map_err(DursCoreError::FailWriteKeypairsFile) } diff --git a/lib/crypto/src/encryption.rs b/lib/crypto/src/encryption.rs index 881d1e5842c8e717d468e74ed1b0092ec1cfb72b..33a0eb74fe4387a3f49146a8e271ab3f588d591a 100644 --- a/lib/crypto/src/encryption.rs +++ b/lib/crypto/src/encryption.rs @@ -17,11 +17,12 @@ use crate::errors::CryptoError; use crate::seeds::Seed48; +use clear_on_drop::clear::Clear; use std::io::Read; const CHACHA20_TAG_SIZE: usize = 16; -#[derive(Clone, Copy, Default)] +#[derive(Clone, Default)] /// Secret key used for encryption algo pub struct SecretKey { key: [u8; 32], @@ -29,6 +30,15 @@ pub struct SecretKey { aad: [u8; 4], } +impl Drop for SecretKey { + #[inline] + fn drop(&mut self) { + self.key.clear(); + self.nonce.clear(); + self.aad.clear(); + } +} + impl SecretKey { /// Create new secret key pub fn new(seed: &Seed48) -> SecretKey { diff --git a/lib/crypto/src/keys/ed25519.rs b/lib/crypto/src/keys/ed25519.rs index ec63172a04b9e28939f29f8445e3c13f523021b3..c6bc8ef72d0695e1fa5a7587a4990bdcdae68484 100644 --- a/lib/crypto/src/keys/ed25519.rs +++ b/lib/crypto/src/keys/ed25519.rs @@ -221,13 +221,6 @@ pub struct Ed25519KeyPair { pub seed: Seed32, } -impl Drop for Ed25519KeyPair { - #[inline] - fn drop(&mut self) { - self.seed.clear(); - } -} - impl Display for Ed25519KeyPair { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { write!(f, "({}, hidden)", self.pubkey.to_base58()) @@ -293,6 +286,27 @@ impl KeyPairFromSeed32Generator { } } +/// Salted password +pub struct SaltedPassword { + salt: String, + password: String, +} + +impl SaltedPassword { + /// Create new salted password + pub fn new(salt: String, password: String) -> SaltedPassword { + SaltedPassword { salt, password } + } +} + +impl Drop for SaltedPassword { + #[inline] + fn drop(&mut self) { + <String as Clear>::clear(&mut self.salt); + <String as Clear>::clear(&mut self.password); + } +} + /// Keypair generator with given parameters for `scrypt` keypair function. #[derive(Copy, Clone)] pub struct KeyPairFromSaltedPasswordGenerator { @@ -329,7 +343,7 @@ impl KeyPairFromSaltedPasswordGenerator { pub fn generate_seed(&self, password: &[u8], salt: &[u8]) -> Seed32 { let mut seed = [0u8; 32]; - scrypt::scrypt(salt, password, &self.scrypt_params, &mut seed) + scrypt::scrypt(password, salt, &self.scrypt_params, &mut seed) .expect("dev error: invalid seed len"); Seed32::new(seed) @@ -339,9 +353,12 @@ impl KeyPairFromSaltedPasswordGenerator { /// /// The [`PublicKey`](struct.PublicKey.html) will be able to verify messaged signed with /// the [`PrivateKey`](struct.PrivateKey.html). - pub fn generate(&self, password: &[u8], salt: &[u8]) -> Ed25519KeyPair { + pub fn generate(&self, salted_password: SaltedPassword) -> Ed25519KeyPair { // Generate seed from tuple (password + salt) - let seed = self.generate_seed(password, salt); + let seed = self.generate_seed( + salted_password.password.as_bytes(), + salted_password.salt.as_bytes(), + ); // Generate keypair from seed KeyPairFromSeed32Generator::generate(seed) } @@ -599,8 +616,10 @@ Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 #[test] fn keypair_generate() { let key_pair1 = KeyPairFromSaltedPasswordGenerator::with_default_parameters().generate( - "JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV".as_bytes(), - "JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV_".as_bytes(), + SaltedPassword::new( + "JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV".to_owned(), + "JhxtHB7UcsDbA9wMSyMKXUzBZUQvqVyB32KwzS9SWoLkjrUhHV_".to_owned(), + ), ); assert_eq!( @@ -610,7 +629,7 @@ Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 let key_pair2 = KeyPairFromSaltedPasswordGenerator::with_parameters(12u8, 16, 1) .expect("fail to create KeyPairFromSaltedPasswordGenerator: invalid scrypt params.") - .generate(b"toto", b"toto"); + .generate(SaltedPassword::new("toto".to_owned(), "toto".to_owned())); // Test signature display and debug assert_eq!( @@ -630,8 +649,9 @@ Timestamp: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 #[test] fn keypair_generate_sign_and_verify() { - let keypair = KeyPairFromSaltedPasswordGenerator::with_default_parameters() - .generate("password".as_bytes(), "salt".as_bytes()); + let keypair = KeyPairFromSaltedPasswordGenerator::with_default_parameters().generate( + SaltedPassword::new("password".to_owned(), "salt".to_owned()), + ); let message = "Version: 10 Type: Identity diff --git a/lib/crypto/src/keys/mod.rs b/lib/crypto/src/keys/mod.rs index 05016379c7d2d245b99813d1ec68aac7ce745cef..34096c08ad3be06d9995edc648c10dee0b13dbcc 100644 --- a/lib/crypto/src/keys/mod.rs +++ b/lib/crypto/src/keys/mod.rs @@ -22,14 +22,14 @@ //! //! ``` //! use dup_crypto::keys::{KeyPair, PublicKey, Signator, Signature}; -//! use dup_crypto::keys::ed25519::KeyPairFromSaltedPasswordGenerator; +//! use dup_crypto::keys::ed25519::{KeyPairFromSaltedPasswordGenerator, SaltedPassword}; //! //! let generator = KeyPairFromSaltedPasswordGenerator::with_default_parameters(); //! -//! let keypair = generator.generate( -//! b"password", -//! b"salt" -//! ); +//! let keypair = generator.generate(SaltedPassword::new( +//! "salt".to_owned(), +//! "password".to_owned(), +//! )); //! //! let signator = keypair.generate_signator().expect("keypair corrupted"); //! diff --git a/lib/crypto/src/seeds.rs b/lib/crypto/src/seeds.rs index 2d26bb200e99f1cb65e0c3b391db9c55d83bff79..0dcca5eeaebb95f513c821981317d1fcd61af849 100644 --- a/lib/crypto/src/seeds.rs +++ b/lib/crypto/src/seeds.rs @@ -17,24 +17,34 @@ use crate::bases::*; use base58::ToBase58; +use clear_on_drop::clear::Clear; use durs_common_tools::fatal_error; use log::error; use ring::rand; use std::fmt::{self, Debug, Display, Formatter}; /// Store a 48 bytes seed used to generate keys. -#[derive(Clone, Copy)] -pub struct Seed48([u8; 48]); +#[derive(Default)] +pub struct Seed48(InnerSeed48); + +struct InnerSeed48([u8; 48]); + +impl Default for InnerSeed48 { + fn default() -> Self { + InnerSeed48([0u8; 48]) + } +} impl AsRef<[u8]> for Seed48 { fn as_ref(&self) -> &[u8] { - &self.0 + &(self.0).0 } } -impl Default for Seed48 { - fn default() -> Self { - Seed48([0u8; 48]) +impl Drop for Seed48 { + #[inline] + fn drop(&mut self) { + <InnerSeed48 as Clear>::clear(&mut self.0); } } @@ -42,7 +52,7 @@ impl Seed48 { #[inline] /// Create new seed pub fn new(seed_bytes: [u8; 48]) -> Seed48 { - Seed48(seed_bytes) + Seed48(InnerSeed48(seed_bytes)) } #[inline] /// Generate random seed @@ -56,7 +66,7 @@ impl Seed48 { } /// Store a 32 bytes seed used to generate keys. -#[derive(Clone, Copy, Default, Deserialize, PartialEq, Eq, Hash, Serialize)] +#[derive(Clone, Default, Deserialize, PartialEq, Eq, Hash, Serialize)] pub struct Seed32([u8; 32]); impl AsRef<[u8]> for Seed32 { @@ -71,16 +81,22 @@ impl ToBase58 for Seed32 { } } +impl Debug for Seed32 { + fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { + write!(f, "Seed32 {{ {} }}", self) + } +} + impl Display for Seed32 { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { write!(f, "{}", self.to_base58()) } } -impl Debug for Seed32 { - // Seed32 { DNann1L... } - fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { - write!(f, "Seed32 {{ {} }}", self) +impl Drop for Seed32 { + #[inline] + fn drop(&mut self) { + <[u8; 32] as Clear>::clear(&mut self.0); } }