Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Restrict Protected to some heap types. #6471

Merged
10 commits merged into from
Jul 1, 2020
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 20 additions & 7 deletions client/cli/src/params/keystore_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,24 +56,37 @@ pub struct KeystoreParams {
pub password_filename: Option<PathBuf>,
}

impl Drop for KeystoreParams {
fn drop(&mut self) {
use sp_core::crypto::Zeroize;
self.password.zeroize();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not replace password: Option<StringSecret> to remove this custom implementation of Drop here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It implements FromStr, but structopt complain about to_string not being implemented. Could use a type wrapper, but I am not sure it is worth it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, it is actually the from str error that needed to be display, so no pb.

}
}

impl KeystoreParams {
/// Get the keystore configuration for the parameters
pub fn keystore_config(&self, base_path: &PathBuf) -> Result<KeystoreConfig> {
let password = if self.password_interactive {
#[cfg(not(target_os = "unknown"))]
{
Some(input_keystore_password()?.into())
let mut password = input_keystore_password()?;
let secret = std::str::FromStr::from_str(password.as_str())?;
use sp_core::crypto::Zeroize;
password.zeroize();
Some(secret)
}
#[cfg(target_os = "unknown")]
None
} else if let Some(ref file) = self.password_filename {
Some(
fs::read_to_string(file)
.map_err(|e| format!("{}", e))?
.into(),
)
let mut password = fs::read_to_string(file)
.map_err(|e| format!("{}", e))?;
let secret = std::str::FromStr::from_str(password.as_str())?;
use sp_core::crypto::Zeroize;
password.zeroize();
Some(secret)
} else if let Some(ref password) = self.password {
Some(password.clone().into())
let secret = std::str::FromStr::from_str(password.as_str())?;
Some(secret)
} else {
None
};
Expand Down
27 changes: 18 additions & 9 deletions client/keystore/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#![warn(missing_docs)]
use std::{collections::{HashMap, HashSet}, path::PathBuf, fs::{self, File}, io::{self, Write}, sync::Arc};
use sp_core::{
crypto::{IsWrappedBy, CryptoTypePublicPair, KeyTypeId, Pair as PairT, Protected, Public},
crypto::{IsWrappedBy, CryptoTypePublicPair, KeyTypeId, Pair as PairT, ExposeSecret, SecretString, Public},
traits::{BareCryptoStore, Error as TraitError},
sr25519::{Public as Sr25519Public, Pair as Sr25519Pair},
vrf::{VRFTranscriptData, VRFSignature, make_transcript},
Expand Down Expand Up @@ -95,14 +95,14 @@ pub struct Store {
path: Option<PathBuf>,
/// Map over `(KeyTypeId, Raw public key)` -> `Key phrase/seed`
additional: HashMap<(KeyTypeId, Vec<u8>), String>,
password: Option<Protected<String>>,
password: Option<SecretString>,
}

impl Store {
/// Open the store at the given path.
///
/// Optionally takes a password that will be used to encrypt/decrypt the keys.
pub fn open<T: Into<PathBuf>>(path: T, password: Option<Protected<String>>) -> Result<KeyStorePtr> {
pub fn open<T: Into<PathBuf>>(path: T, password: Option<SecretString>) -> Result<KeyStorePtr> {
let path = path.into();
fs::create_dir_all(&path)?;

Expand Down Expand Up @@ -155,7 +155,7 @@ impl Store {
pub fn insert_by_type<Pair: PairT>(&self, key_type: KeyTypeId, suri: &str) -> Result<Pair> {
let pair = Pair::from_string(
suri,
self.password.as_ref().map(|p| &***p)
self.password()
).map_err(|_| Error::InvalidSeed)?;
self.insert_unknown(key_type, suri, pair.public().as_slice())
.map_err(|_| Error::Unavailable)?;
Expand All @@ -173,7 +173,7 @@ impl Store {
///
/// Places it into the file system store.
pub fn generate_by_type<Pair: PairT>(&self, key_type: KeyTypeId) -> Result<Pair> {
let (pair, phrase, _) = Pair::generate_with_phrase(self.password.as_ref().map(|p| &***p));
let (pair, phrase, _) = Pair::generate_with_phrase(self.password());
if let Some(path) = self.key_file_path(pair.public().as_slice(), key_type) {
let mut file = File::create(path)?;
serde_json::to_writer(&file, &phrase)?;
Expand Down Expand Up @@ -229,7 +229,7 @@ impl Store {
let phrase = self.key_phrase_by_type(public.as_slice(), key_type)?;
let pair = Pair::from_string(
&phrase,
self.password.as_ref().map(|p| &***p),
self.password(),
).map_err(|_| Error::InvalidPhrase)?;

if &pair.public() == public {
Expand Down Expand Up @@ -434,7 +434,9 @@ impl BareCryptoStore for Store {
}

fn password(&self) -> Option<&str> {
self.password.as_ref().map(|x| x.as_str())
self.password.as_ref()
.map(|p| p.expose_secret())
.map(|p| p.as_str())
}

fn has_keys(&self, public_keys: &[(Vec<u8>, KeyTypeId)]) -> bool {
Expand Down Expand Up @@ -464,6 +466,7 @@ mod tests {
use super::*;
use tempfile::TempDir;
use sp_core::{testing::SR25519, crypto::Ss58Codec};
use std::str::FromStr;

#[test]
fn basic_store() {
Expand Down Expand Up @@ -504,7 +507,10 @@ mod tests {
fn password_being_used() {
let password = String::from("password");
let temp_dir = TempDir::new().unwrap();
let store = Store::open(temp_dir.path(), Some(password.clone().into())).unwrap();
let store = Store::open(
temp_dir.path(),
Some(FromStr::from_str(password.as_str()).unwrap()),
).unwrap();

let pair: ed25519::AppPair = store.write().generate().unwrap();
assert_eq!(
Expand All @@ -516,7 +522,10 @@ mod tests {
let store = Store::open(temp_dir.path(), None).unwrap();
assert!(store.read().key_pair::<ed25519::AppPair>(&pair.public()).is_err());

let store = Store::open(temp_dir.path(), Some(password.into())).unwrap();
let store = Store::open(
temp_dir.path(),
Some(FromStr::from_str(password.as_str()).unwrap()),
).unwrap();
assert_eq!(
pair.public(),
store.read().key_pair::<ed25519::AppPair>(&pair.public()).unwrap().public(),
Expand Down
4 changes: 2 additions & 2 deletions client/service/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use sc_client_api::execution_extensions::ExecutionStrategies;
use std::{io, future::Future, path::{PathBuf, Path}, pin::Pin, net::SocketAddr, sync::Arc};
pub use sc_transaction_pool::txpool::Options as TransactionPoolOptions;
use sc_chain_spec::ChainSpec;
use sp_core::crypto::Protected;
use sp_core::crypto::SecretString;
pub use sc_telemetry::TelemetryEndpoints;
use prometheus_endpoint::Registry;
#[cfg(not(target_os = "unknown"))]
Expand Down Expand Up @@ -127,7 +127,7 @@ pub enum KeystoreConfig {
/// The path of the keystore.
path: PathBuf,
/// Node keystore's password.
password: Option<Protected<String>>
password: Option<SecretString>
},
/// In-memory keystore. Recommended for in-browser nodes.
InMemory,
Expand Down
2 changes: 2 additions & 0 deletions primitives/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ tiny-bip39 = { version = "0.7", optional = true }
regex = { version = "1.3.1", optional = true }
num-traits = { version = "0.2.8", default-features = false }
zeroize = { version = "1.0.0", default-features = false }
secrecy = { version = "0.6.0", default-features = false }
lazy_static = { version = "1.4.0", default-features = false, optional = true }
parking_lot = { version = "0.10.0", optional = true }
sp-debug-derive = { version = "2.0.0-rc3", path = "../debug-derive" }
Expand Down Expand Up @@ -106,6 +107,7 @@ std = [
"sp-storage/std",
"sp-runtime-interface/std",
"zeroize/alloc",
"secrecy/alloc",
"futures",
"futures/thread-pool",
"libsecp256k1/std",
Expand Down
53 changes: 7 additions & 46 deletions primitives/core/src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,16 @@ use regex::Regex;
use base58::{FromBase58, ToBase58};
#[cfg(feature = "std")]
use crate::hexdisplay::HexDisplay;
use zeroize::Zeroize;
#[doc(hidden)]
pub use sp_std::ops::Deref;
use sp_runtime_interface::pass_by::PassByInner;
#[doc(hidden)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are both of these exported hidden?

pub use zeroize::Zeroize;
#[doc(hidden)]
pub use secrecy::ExposeSecret;
/// A store for sensitive data.
#[cfg(feature = "std")]
pub use secrecy::SecretString;

/// The root phrase for our publicly known keys.
pub const DEV_PHRASE: &str = "bottom drive obey lake curtain smoke basket hold race lonely fit walk";
Expand Down Expand Up @@ -79,51 +85,6 @@ impl<S, T: UncheckedFrom<S>> UncheckedInto<T> for S {
}
}

/// A store for sensitive data.
///
/// Calls `Zeroize::zeroize` upon `Drop`.
#[derive(Clone)]
pub struct Protected<T: Zeroize>(T);

impl<T: Zeroize> AsRef<T> for Protected<T> {
fn as_ref(&self) -> &T {
&self.0
}
}

impl<T: Zeroize> sp_std::ops::Deref for Protected<T> {
type Target = T;

fn deref(&self) -> &T {
&self.0
}
}

#[cfg(feature = "std")]
impl<T: Zeroize> std::fmt::Debug for Protected<T> {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "<protected>")
}
}

impl<T: Zeroize> From<T> for Protected<T> {
fn from(t: T) -> Self {
Protected(t)
}
}

impl<T: Zeroize> Zeroize for Protected<T> {
fn zeroize(&mut self) {
self.0.zeroize()
}
}

impl<T: Zeroize> Drop for Protected<T> {
fn drop(&mut self) {
self.zeroize()
}
}

/// An error with the interpretation of a secret.
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg(feature = "full_crypto")]
Expand Down