Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.

33 changes: 23 additions & 10 deletions client/cli/src/params/keystore_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use sc_service::config::KeystoreConfig;
use std::fs;
use std::path::PathBuf;
use structopt::StructOpt;
use sp_core::crypto::SecretString;

/// default sub directory for the key store
const DEFAULT_KEYSTORE_CONFIG_PATH: &'static str = "keystore";
Expand All @@ -42,9 +43,10 @@ pub struct KeystoreParams {
/// Password used by the keystore.
#[structopt(
long = "password",
parse(try_from_str = secret_string_from_str),
conflicts_with_all = &[ "password-interactive", "password-filename" ]
)]
pub password: Option<String>,
pub password: Option<SecretString>,

/// File that contains the password used by the keystore.
#[structopt(
Expand All @@ -56,26 +58,37 @@ pub struct KeystoreParams {
pub password_filename: Option<PathBuf>,
}

/// Parse a sercret string, returning a displayable error.
pub fn secret_string_from_str(s: &str) -> std::result::Result<SecretString, String> {
Ok(std::str::FromStr::from_str(s)
.map_err(|_e| "Could not get SecretString".to_string())?)
}

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())
.map_err(|()| "Error reading password")?;
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(),
)
} else if let Some(ref password) = self.password {
Some(password.clone().into())
let mut password = fs::read_to_string(file)
.map_err(|e| format!("{}", e))?;
let secret = std::str::FromStr::from_str(password.as_str())
.map_err(|()| "Error reading password")?;
use sp_core::crypto::Zeroize;
password.zeroize();
Some(secret)
} else {
None
self.password.clone()
};

let path = self
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 @@ -130,7 +130,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-rc4", 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;
/// Trait to zeroize a memory buffer.
pub use zeroize::Zeroize;
/// Trait for accessing reference to `SecretString`.
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