diff --git a/substrate/core/consensus/babe/src/lib.rs b/substrate/core/consensus/babe/src/lib.rs index d1585947ce..3e98817e1d 100644 --- a/substrate/core/consensus/babe/src/lib.rs +++ b/substrate/core/consensus/babe/src/lib.rs @@ -976,7 +976,7 @@ mod tests { #[test] fn wrong_consensus_engine_id_rejected() { drop(env_logger::try_init()); - let sig = sr25519::Pair::generate().sign(b""); + let sig = sr25519::Pair::generate().0.sign(b""); let bad_seal: Item = DigestItem::Seal([0; 4], sig); assert!(bad_seal.as_babe_pre_digest().is_none()); assert!(bad_seal.as_babe_seal().is_none()) @@ -992,7 +992,7 @@ mod tests { #[test] fn sig_is_not_pre_digest() { drop(env_logger::try_init()); - let sig = sr25519::Pair::generate().sign(b""); + let sig = sr25519::Pair::generate().0.sign(b""); let bad_seal: Item = DigestItem::Seal(BABE_ENGINE_ID, sig); assert!(bad_seal.as_babe_pre_digest().is_none()); assert!(bad_seal.as_babe_seal().is_some()) @@ -1002,7 +1002,7 @@ mod tests { fn can_author_block() { drop(env_logger::try_init()); let randomness = &[]; - let pair = sr25519::Pair::generate(); + let (pair, _) = sr25519::Pair::generate(); let mut i = 0; loop { match claim_slot(randomness, i, &[], 0, &[pair.public()], &pair, u64::MAX / 10) { diff --git a/substrate/core/consensus/slots/src/aux_schema.rs b/substrate/core/consensus/slots/src/aux_schema.rs index ed96bf2e22..1af8b2da53 100644 --- a/substrate/core/consensus/slots/src/aux_schema.rs +++ b/substrate/core/consensus/slots/src/aux_schema.rs @@ -176,7 +176,7 @@ mod test { #[test] fn check_equivocation_works() { let client = test_client::new(); - let pair = sr25519::Pair::generate(); + let (pair, _seed) = sr25519::Pair::generate(); let public = pair.public(); let header1 = create_header(1); // @ slot 2 diff --git a/substrate/core/executor/src/wasm_executor.rs b/substrate/core/executor/src/wasm_executor.rs index 920639c0a2..08bda16f4f 100644 --- a/substrate/core/executor/src/wasm_executor.rs +++ b/substrate/core/executor/src/wasm_executor.rs @@ -1379,7 +1379,7 @@ mod tests { fn ed25519_verify_should_work() { let mut ext = TestExternalities::::default(); let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); - let key = ed25519::Pair::from_seed(blake2_256(b"test")); + let key = ed25519::Pair::from_seed(&blake2_256(b"test")); let sig = key.sign(b"all ok!"); let mut calldata = vec![]; calldata.extend_from_slice(key.public().as_ref()); @@ -1405,7 +1405,7 @@ mod tests { fn sr25519_verify_should_work() { let mut ext = TestExternalities::::default(); let test_code = include_bytes!("../wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm"); - let key = sr25519::Pair::from_seed(blake2_256(b"test")); + let key = sr25519::Pair::from_seed(&blake2_256(b"test")); let sig = key.sign(b"all ok!"); let mut calldata = vec![]; calldata.extend_from_slice(key.public().as_ref()); diff --git a/substrate/core/keystore/src/lib.rs b/substrate/core/keystore/src/lib.rs index 67cf2c8d32..c36c6504c0 100644 --- a/substrate/core/keystore/src/lib.rs +++ b/substrate/core/keystore/src/lib.rs @@ -71,7 +71,7 @@ impl Store { /// Generate a new key, placing it into the store. pub fn generate(&self, password: &str) -> Result { - let (pair, phrase) = Pair::generate_with_phrase(Some(password)); + let (pair, phrase, _) = Pair::generate_with_phrase(Some(password)); let mut file = File::create(self.key_file_path(&pair.public()))?; ::serde_json::to_writer(&file, &phrase)?; file.flush()?; @@ -95,7 +95,7 @@ impl Store { let file = File::open(path)?; let phrase: String = ::serde_json::from_reader(&file)?; - let pair = Pair::from_phrase(&phrase, Some(password)) + let (pair, _) = Pair::from_phrase(&phrase, Some(password)) .ok().ok_or(Error::InvalidPhrase)?; if &pair.public() != public { return Err(Error::InvalidPassword); diff --git a/substrate/core/primitives/src/crypto.rs b/substrate/core/primitives/src/crypto.rs index 9cd71bb9b2..9ddc9a93f7 100644 --- a/substrate/core/primitives/src/crypto.rs +++ b/substrate/core/primitives/src/crypto.rs @@ -18,6 +18,8 @@ //! Cryptographic utilities. // end::description[] +#[cfg(feature = "std")] +use rand::{RngCore, rngs::OsRng}; #[cfg(feature = "std")] use parity_codec::{Encode, Decode}; #[cfg(feature = "std")] @@ -294,7 +296,7 @@ pub trait Pair: Sized + 'static { /// The type used to (minimally) encode the data required to securely create /// a new key pair. - type Seed; + type Seed: Default + AsRef<[u8]> + AsMut<[u8]> + Clone; /// The type used to represent a signature. Can be created from a key pair and a message /// and verified with the message and a public key. @@ -307,7 +309,12 @@ pub trait Pair: Sized + 'static { /// /// This is only for ephemeral keys really, since you won't have access to the secret key /// for storage. If you want a persistent key pair, use `generate_with_phrase` instead. - fn generate() -> Self; + fn generate() -> (Self, Self::Seed) { + let mut csprng: OsRng = OsRng::new().expect("OS random generator works; qed"); + let mut seed = Self::Seed::default(); + csprng.fill_bytes(seed.as_mut()); + (Self::from_seed(&seed), seed) + } /// Generate new secure (random) key pair and provide the recovery phrase. /// @@ -315,10 +322,10 @@ pub trait Pair: Sized + 'static { /// /// This is generally slower than `generate()`, so prefer that unless you need to persist /// the key from the current session. - fn generate_with_phrase(password: Option<&str>) -> (Self, String); + fn generate_with_phrase(password: Option<&str>) -> (Self, String, Self::Seed); /// Returns the KeyPair from the English BIP39 seed `phrase`, or `None` if it's invalid. - fn from_phrase(phrase: &str, password: Option<&str>) -> Result; + fn from_phrase(phrase: &str, password: Option<&str>) -> Result<(Self, Self::Seed), SecretStringError>; /// Derive a child key from a series of given junctions. fn derive>(&self, path: Iter) -> Result; @@ -327,7 +334,7 @@ pub trait Pair: Sized + 'static { /// /// @WARNING: THIS WILL ONLY BE SECURE IF THE `seed` IS SECURE. If it can be guessed /// by an attacker then they can also derive your key. - fn from_seed(seed: Self::Seed) -> Self; + fn from_seed(seed: &Self::Seed) -> Self; /// Make a new key pair from secret seed material. The slice must be the correct size or /// it will return `None`. @@ -424,27 +431,54 @@ mod tests { impl Pair for TestPair { type Public = (); - type Seed = (); + type Seed = [u8; 0]; type Signature = (); type DeriveError = (); - fn generate() -> Self { TestPair::Generated } - fn generate_with_phrase(_password: Option<&str>) -> (Self, String) { (TestPair::GeneratedWithPhrase, "".into()) } - fn from_phrase(phrase: &str, password: Option<&str>) -> Result { - Ok(TestPair::GeneratedFromPhrase{ phrase: phrase.to_owned(), password: password.map(Into::into) }) + fn generate() -> (Self, ::Seed) { (TestPair::Generated, []) } + fn generate_with_phrase(_password: Option<&str>) -> (Self, String, ::Seed) { + (TestPair::GeneratedWithPhrase, "".into(), []) } - fn derive>(&self, _path: Iter) -> Result { + fn from_phrase(phrase: &str, password: Option<&str>) + -> Result<(Self, ::Seed), SecretStringError> + { + Ok((TestPair::GeneratedFromPhrase { + phrase: phrase.to_owned(), + password: password.map(Into::into) + }, [])) + } + fn derive>(&self, _path: Iter) + -> Result + { Err(()) } - fn from_seed(_seed: ::Seed) -> Self { TestPair::Seed(vec![]) } + fn from_seed(_seed: &::Seed) -> Self { TestPair::Seed(vec![]) } fn sign(&self, _message: &[u8]) -> Self::Signature { () } - fn verify, M: AsRef<[u8]>>(_sig: &Self::Signature, _message: M, _pubkey: P) -> bool { true } - fn verify_weak, M: AsRef<[u8]>>(_sig: &[u8], _message: M, _pubkey: P) -> bool { true } + fn verify, M: AsRef<[u8]>>( + _sig: &Self::Signature, + _message: M, + _pubkey: P + ) -> bool { true } + fn verify_weak, M: AsRef<[u8]>>( + _sig: &[u8], + _message: M, + _pubkey: P + ) -> bool { true } fn public(&self) -> Self::Public { () } - fn from_standard_components>(phrase: &str, password: Option<&str>, path: I) -> Result { - Ok(TestPair::Standard { phrase: phrase.to_owned(), password: password.map(ToOwned::to_owned), path: path.collect() }) + fn from_standard_components>( + phrase: &str, + password: Option<&str>, + path: I + ) -> Result { + Ok(TestPair::Standard { + phrase: phrase.to_owned(), + password: password.map(ToOwned::to_owned), + path: path.collect() + }) } - fn from_seed_slice(seed: &[u8]) -> Result { + fn from_seed_slice(seed: &[u8]) + -> Result + { Ok(TestPair::Seed(seed.to_owned())) } } diff --git a/substrate/core/primitives/src/ed25519.rs b/substrate/core/primitives/src/ed25519.rs index e593e997a4..26086816a3 100644 --- a/substrate/core/primitives/src/ed25519.rs +++ b/substrate/core/primitives/src/ed25519.rs @@ -29,8 +29,6 @@ use substrate_bip39::seed_from_entropy; #[cfg(feature = "std")] use bip39::{Mnemonic, Language, MnemonicType}; #[cfg(feature = "std")] -use rand::Rng; -#[cfg(feature = "std")] use crate::crypto::{Pair as TraitPair, DeriveJunction, SecretStringError, Derive, Ss58Codec}; #[cfg(feature = "std")] use serde::{de, Serializer, Serialize, Deserializer, Deserialize}; @@ -355,46 +353,38 @@ impl TraitPair for Pair { type Signature = Signature; type DeriveError = DeriveError; - /// Generate new secure (random) key pair. - /// - /// This is only for ephemeral keys really, since you won't have access to the secret key - /// for storage. If you want a persistent key pair, use `generate_with_phrase` instead. - fn generate() -> Pair { - let mut seed: Seed = Default::default(); - rand::rngs::EntropyRng::new().fill(seed.as_mut()); - Self::from_seed(seed) - } - /// Generate new secure (random) key pair and provide the recovery phrase. /// /// You can recover the same key later with `from_phrase`. - fn generate_with_phrase(password: Option<&str>) -> (Pair, String) { + fn generate_with_phrase(password: Option<&str>) -> (Pair, String, Seed) { let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); let phrase = mnemonic.phrase(); + let (pair, seed) = Self::from_phrase(phrase, password) + .expect("All phrases generated by Mnemonic are valid; qed"); ( - Self::from_phrase(phrase, password).expect("All phrases generated by Mnemonic are valid; qed"), + pair, phrase.to_owned(), + seed, ) } /// Generate key pair from given recovery phrase and password. - fn from_phrase(phrase: &str, password: Option<&str>) -> Result { + fn from_phrase(phrase: &str, password: Option<&str>) -> Result<(Pair, Seed), SecretStringError> { let big_seed = seed_from_entropy( Mnemonic::from_phrase(phrase, Language::English) .map_err(|_| SecretStringError::InvalidPhrase)?.entropy(), password.unwrap_or(""), ).map_err(|_| SecretStringError::InvalidSeed)?; - Self::from_seed_slice(&big_seed[0..32]) + let mut seed = Seed::default(); + seed.copy_from_slice(&big_seed[0..32]); + Self::from_seed_slice(&big_seed[0..32]).map(|x| (x, seed)) } /// Make a new key pair from secret seed material. /// /// You should never need to use this; generate(), generate_with_phrasee - fn from_seed(seed: Seed) -> Pair { - let secret = ed25519_dalek::SecretKey::from_bytes(&seed[..]) - .expect("seed has valid length; qed"); - let public = ed25519_dalek::PublicKey::from(&secret); - Pair(ed25519_dalek::Keypair { secret, public }) + fn from_seed(seed: &Seed) -> Pair { + Self::from_seed_slice(&seed[..]).expect("seed has valid length; qed") } /// Make a new key pair from secret seed material. The slice must be 32 bytes long or it @@ -402,13 +392,10 @@ impl TraitPair for Pair { /// /// You should never need to use this; generate(), generate_with_phrase fn from_seed_slice(seed_slice: &[u8]) -> Result { - if seed_slice.len() != 32 { - Err(SecretStringError::InvalidSeedLength) - } else { - let mut seed = [0u8; 32]; - seed.copy_from_slice(&seed_slice); - Ok(Self::from_seed(seed)) - } + let secret = ed25519_dalek::SecretKey::from_bytes(seed_slice) + .map_err(|_| SecretStringError::InvalidSeedLength)?; + let public = ed25519_dalek::PublicKey::from(&secret); + Ok(Pair(ed25519_dalek::Keypair { secret, public })) } /// Derive a child key from a series of given junctions. @@ -420,12 +407,18 @@ impl TraitPair for Pair { DeriveJunction::Hard(cc) => acc = derive_hard_junction(&acc, &cc), } } - Ok(Self::from_seed(acc)) + Ok(Self::from_seed(&acc)) } /// Generate a key from the phrase, password and derivation path. - fn from_standard_components>(phrase: &str, password: Option<&str>, path: I) -> Result { - Self::from_phrase(phrase, password)?.derive(path).map_err(|_| SecretStringError::InvalidPath) + fn from_standard_components>( + phrase: &str, + password: Option<&str>, + path: I + ) -> Result { + Self::from_phrase(phrase, password)?.0 + .derive(path) + .map_err(|_| SecretStringError::InvalidPath) } /// Get the public key. @@ -483,7 +476,7 @@ impl Pair { let mut padded_seed: Seed = [' ' as u8; 32]; let len = s.len().min(32); padded_seed[..len].copy_from_slice(&s.as_bytes()[..len]); - Self::from_seed(padded_seed) + Self::from_seed(&padded_seed) }) } } @@ -505,38 +498,52 @@ mod test { #[test] fn seed_and_derive_should_work() { let seed = hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60"); - let pair = Pair::from_seed(seed); + let pair = Pair::from_seed(&seed); assert_eq!(pair.seed(), &seed); let path = vec![DeriveJunction::Hard([0u8; 32])]; let derived = pair.derive(path.into_iter()).ok().unwrap(); - assert_eq!(derived.seed(), &hex!("ede3354e133f9c8e337ddd6ee5415ed4b4ffe5fc7d21e933f4930a3730e5b21c")); + assert_eq!( + derived.seed(), + &hex!("ede3354e133f9c8e337ddd6ee5415ed4b4ffe5fc7d21e933f4930a3730e5b21c") + ); } #[test] fn test_vector_should_work() { - let pair: Pair = Pair::from_seed(hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")); + let pair = Pair::from_seed( + &hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60") + ); let public = pair.public(); - assert_eq!(public, Public::from_raw(hex!("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"))); + assert_eq!(public, Public::from_raw( + hex!("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a") + )); let message = b""; - let signature = Signature::from_raw(hex!("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b")); + let signature = hex!("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"); + let signature = Signature::from_raw(signature); assert!(&pair.sign(&message[..]) == &signature); assert!(Pair::verify(&signature, &message[..], &public)); } #[test] fn test_vector_by_string_should_work() { - let pair: Pair = Pair::from_string("0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", None).unwrap(); + let pair = Pair::from_string( + "0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", + None + ).unwrap(); let public = pair.public(); - assert_eq!(public, Public::from_raw(hex!("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"))); + assert_eq!(public, Public::from_raw( + hex!("d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a") + )); let message = b""; - let signature = Signature::from_raw(hex!("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b")); + let signature = hex!("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b"); + let signature = Signature::from_raw(signature); assert!(&pair.sign(&message[..]) == &signature); assert!(Pair::verify(&signature, &message[..], &public)); } #[test] fn generated_pair_should_work() { - let pair = Pair::generate(); + let (pair, _) = Pair::generate(); let public = pair.public(); let message = b"Something important"; let signature = pair.sign(&message[..]); @@ -546,9 +553,11 @@ mod test { #[test] fn seeded_pair_should_work() { - let pair = Pair::from_seed(*b"12345678901234567890123456789012"); + let pair = Pair::from_seed(b"12345678901234567890123456789012"); let public = pair.public(); - assert_eq!(public, Public::from_raw(hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee"))); + assert_eq!(public, Public::from_raw( + hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee") + )); let message = hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000200d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000"); let signature = pair.sign(&message[..]); println!("Correct signature: {:?}", signature); @@ -558,31 +567,31 @@ mod test { #[test] fn generate_with_phrase_recovery_possible() { - let (pair1, phrase) = Pair::generate_with_phrase(None); - let pair2 = Pair::from_phrase(&phrase, None).unwrap(); + let (pair1, phrase, _) = Pair::generate_with_phrase(None); + let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap(); assert_eq!(pair1.public(), pair2.public()); } #[test] fn generate_with_password_phrase_recovery_possible() { - let (pair1, phrase) = Pair::generate_with_phrase(Some("password")); - let pair2 = Pair::from_phrase(&phrase, Some("password")).unwrap(); + let (pair1, phrase, _) = Pair::generate_with_phrase(Some("password")); + let (pair2, _) = Pair::from_phrase(&phrase, Some("password")).unwrap(); assert_eq!(pair1.public(), pair2.public()); } #[test] fn password_does_something() { - let (pair1, phrase) = Pair::generate_with_phrase(Some("password")); - let pair2 = Pair::from_phrase(&phrase, None).unwrap(); + let (pair1, phrase, _) = Pair::generate_with_phrase(Some("password")); + let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap(); assert_ne!(pair1.public(), pair2.public()); } #[test] fn ss58check_roundtrip_works() { - let pair = Pair::from_seed(*b"12345678901234567890123456789012"); + let pair = Pair::from_seed(b"12345678901234567890123456789012"); let public = pair.public(); let s = public.to_ss58check(); println!("Correct: {}", s); diff --git a/substrate/core/primitives/src/sr25519.rs b/substrate/core/primitives/src/sr25519.rs index 876dcb7078..aa2db2dc12 100644 --- a/substrate/core/primitives/src/sr25519.rs +++ b/substrate/core/primitives/src/sr25519.rs @@ -21,8 +21,6 @@ //! for this to work. // end::description[] -#[cfg(feature = "std")] -use rand::rngs::OsRng; #[cfg(feature = "std")] use schnorrkel::{signing_context, Keypair, SecretKey, MiniSecretKey, PublicKey, derive::{Derivation, ChainCode, CHAIN_CODE_LENGTH} @@ -377,23 +375,14 @@ impl TraitPair for Pair { type Signature = Signature; type DeriveError = Infallible; - /// Generate new secure (random) key pair. - fn generate() -> Pair { - let mut csprng: OsRng = OsRng::new().expect("os random generator works; qed"); - let key_pair: Keypair = Keypair::generate(&mut csprng); - Pair(key_pair) - } - /// Make a new key pair from raw secret seed material. /// /// This is generated using schnorrkel's Mini-Secret-Keys. /// /// A MiniSecretKey is literally what Ed25519 calls a SecretKey, which is just 32 random bytes. - fn from_seed(seed: Seed) -> Pair { - let mini_key: MiniSecretKey = MiniSecretKey::from_bytes(&seed[..]) - .expect("32 bytes can always build a key; qed"); - let kp = mini_key.expand_to_keypair(); - Pair(kp) + fn from_seed(seed: &Seed) -> Pair { + Self::from_seed_slice(&seed[..]) + .expect("32 bytes can always build a key; qed") } /// Get the public key. @@ -420,22 +409,29 @@ impl TraitPair for Pair { } /// Generate a key from the phrase, password and derivation path. - fn from_standard_components>(phrase: &str, password: Option<&str>, path: I) -> Result { - Self::from_phrase(phrase, password)? + fn from_standard_components>( + phrase: &str, + password: Option<&str>, + path: I + ) -> Result { + Self::from_phrase(phrase, password)?.0 .derive(path) .map_err(|_| SecretStringError::InvalidPath) } - fn generate_with_phrase(password: Option<&str>) -> (Pair, String) { + fn generate_with_phrase(password: Option<&str>) -> (Pair, String, Seed) { let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); let phrase = mnemonic.phrase(); + let (pair, seed) = Self::from_phrase(phrase, password) + .expect("All phrases generated by Mnemonic are valid; qed"); ( - Self::from_phrase(phrase, password).expect("All phrases generated by Mnemonic are valid; qed"), + pair, phrase.to_owned(), + seed, ) } - fn from_phrase(phrase: &str, password: Option<&str>) -> Result { + fn from_phrase(phrase: &str, password: Option<&str>) -> Result<(Pair, Seed), SecretStringError> { Mnemonic::from_phrase(phrase, Language::English) .map_err(|_| SecretStringError::InvalidPhrase) .map(|m| Self::from_entropy(m.entropy(), password)) @@ -490,11 +486,12 @@ impl Pair { /// /// This uses a key derivation function to convert the entropy into a seed, then returns /// the pair generated from it. - pub fn from_entropy(entropy: &[u8], password: Option<&str>) -> Pair { + pub fn from_entropy(entropy: &[u8], password: Option<&str>) -> (Pair, Seed) { let mini_key: MiniSecretKey = mini_secret_from_entropy(entropy, password.unwrap_or("")) .expect("32 bytes can always build a key; qed"); + let kp = mini_key.expand_to_keypair(); - Pair(kp) + (Pair(kp), mini_key.to_bytes()) } } @@ -538,7 +535,7 @@ mod test { #[test] fn derive_soft_should_work() { - let pair: Pair = Pair::from_seed(hex!( + let pair = Pair::from_seed(&hex!( "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60" )); let derive_1 = pair.derive(Some(DeriveJunction::soft(1)).into_iter()).unwrap(); @@ -550,7 +547,7 @@ mod test { #[test] fn derive_hard_should_work() { - let pair: Pair = Pair::from_seed(hex!( + let pair = Pair::from_seed(&hex!( "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60" )); let derive_1 = pair.derive(Some(DeriveJunction::hard(1)).into_iter()).unwrap(); @@ -562,7 +559,7 @@ mod test { #[test] fn derive_soft_public_should_work() { - let pair: Pair = Pair::from_seed(hex!( + let pair = Pair::from_seed(&hex!( "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60" )); let path = Some(DeriveJunction::soft(1)); @@ -573,7 +570,7 @@ mod test { #[test] fn derive_hard_public_should_fail() { - let pair: Pair = Pair::from_seed(hex!( + let pair = Pair::from_seed(&hex!( "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60" )); let path = Some(DeriveJunction::hard(1)); @@ -582,7 +579,7 @@ mod test { #[test] fn sr_test_vector_should_work() { - let pair: Pair = Pair::from_seed(hex!( + let pair = Pair::from_seed(&hex!( "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60" )); let public = pair.public(); @@ -599,7 +596,7 @@ mod test { #[test] fn generated_pair_should_work() { - let pair = Pair::generate(); + let (pair, _) = Pair::generate(); let public = pair.public(); let message = b"Something important"; let signature = pair.sign(&message[..]); @@ -609,7 +606,7 @@ mod test { #[test] fn seeded_pair_should_work() { - let pair = Pair::from_seed(*b"12345678901234567890123456789012"); + let pair = Pair::from_seed(b"12345678901234567890123456789012"); let public = pair.public(); assert_eq!( public, @@ -624,7 +621,7 @@ mod test { #[test] fn ss58check_roundtrip_works() { - let pair = Pair::generate(); + let (pair, _) = Pair::generate(); let public = pair.public(); let s = public.to_ss58check(); println!("Correct: {}", s); @@ -637,9 +634,13 @@ mod test { // The values in this test case are compared to the output of `node-test.js` in schnorrkel-js. // // This is to make sure that the wasm library is compatible. - let pk = Pair::from_seed(hex!("0000000000000000000000000000000000000000000000000000000000000000")); + let pk = Pair::from_seed( + &hex!("0000000000000000000000000000000000000000000000000000000000000000") + ); let public = pk.public(); - let js_signature = Signature::from_raw(hex!("28a854d54903e056f89581c691c1f7d2ff39f8f896c9e9c22475e60902cc2b3547199e0e91fa32902028f2ca2355e8cdd16cfe19ba5e8b658c94aa80f3b81a00")); + let js_signature = Signature::from_raw( + hex!("28a854d54903e056f89581c691c1f7d2ff39f8f896c9e9c22475e60902cc2b3547199e0e91fa32902028f2ca2355e8cdd16cfe19ba5e8b658c94aa80f3b81a00") + ); assert!(Pair::verify(&js_signature, b"SUBSTRATE", public)); } } diff --git a/substrate/node/cli/src/factory_impl.rs b/substrate/node/cli/src/factory_impl.rs index a2ac6f5b29..0d94610362 100644 --- a/substrate/node/cli/src/factory_impl.rs +++ b/substrate/node/cli/src/factory_impl.rs @@ -170,13 +170,13 @@ impl RuntimeAdapter for FactoryState { /// Generates a random `AccountId` from `seed`. fn gen_random_account_id(seed: &Self::Number) -> Self::AccountId { - let pair: sr25519::Pair = sr25519::Pair::from_seed(gen_seed_bytes(*seed)); + let pair: sr25519::Pair = sr25519::Pair::from_seed(&gen_seed_bytes(*seed)); pair.public().into() } /// Generates a random `Secret` from `seed`. fn gen_random_account_secret(seed: &Self::Number) -> Self::Secret { - let pair: sr25519::Pair = sr25519::Pair::from_seed(gen_seed_bytes(*seed)); + let pair: sr25519::Pair = sr25519::Pair::from_seed(&gen_seed_bytes(*seed)); pair } diff --git a/substrate/subkey/src/cli.yml b/substrate/subkey/src/cli.yml index eedd475737..89190df362 100644 --- a/substrate/subkey/src/cli.yml +++ b/substrate/subkey/src/cli.yml @@ -21,6 +21,12 @@ args: subcommands: - generate: about: Generate a random account + args: + - words: + short: w + long: words + help: The number of words in the phrase to generate. One of 12 (default), 15, 18, 21 and 24. + takes_value: true - inspect: about: Gets a public key and a SS58 address from the provided Secret URI args: diff --git a/substrate/subkey/src/main.rs b/substrate/subkey/src/main.rs index 1b9afb8cd7..5bd698575d 100644 --- a/substrate/subkey/src/main.rs +++ b/substrate/subkey/src/main.rs @@ -18,75 +18,48 @@ #[cfg(feature = "bench")] extern crate test; -use std::io::{stdin, Read}; +use std::{str::FromStr, io::{stdin, Read}}; use hex_literal::hex; use clap::load_yaml; -use rand::{RngCore, rngs::OsRng}; -use substrate_bip39::mini_secret_from_entropy; use bip39::{Mnemonic, Language, MnemonicType}; use substrate_primitives::{ed25519, sr25519, hexdisplay::HexDisplay, Pair, crypto::Ss58Codec, blake2_256}; use parity_codec::{Encode, Decode, Compact}; use sr_primitives::generic::Era; -use schnorrkel::keys::MiniSecretKey; use node_primitives::{Balance, Index, Hash}; use node_runtime::{Call, UncheckedExtrinsic, BalancesCall}; mod vanity; trait Crypto { - type Seed: AsRef<[u8]> + AsMut<[u8]> + Sized + Default; - type Pair: Pair; - fn generate_phrase() -> String { - Mnemonic::new(MnemonicType::Words12, Language::English).phrase().to_owned() - } - fn generate_seed() -> Self::Seed { - let mut seed: Self::Seed = Default::default(); - OsRng::new().unwrap().fill_bytes(seed.as_mut()); - seed - } - fn seed_from_phrase(phrase: &str, password: Option<&str>) -> Self::Seed; - fn pair_from_seed(seed: &Self::Seed) -> Self::Pair; - fn pair_from_suri(phrase: &str, password: Option<&str>) -> Self::Pair { - Self::pair_from_seed(&Self::seed_from_phrase(phrase, password)) - } - fn ss58_from_pair(pair: &Self::Pair) -> String; - fn public_from_pair(pair: &Self::Pair) -> Vec; - fn seed_from_pair(_pair: &Self::Pair) -> Option<&Self::Seed> { None } - fn print_from_seed(seed: &Self::Seed) { - let pair = Self::pair_from_seed(seed); - println!("Seed 0x{} is account:\n Public key (hex): 0x{}\n Address (SS58): {}", - HexDisplay::from(&seed.as_ref()), - HexDisplay::from(&Self::public_from_pair(&pair)), - Self::ss58_from_pair(&pair) - ); - } - fn print_from_phrase(phrase: &str, password: Option<&str>) { - let seed = Self::seed_from_phrase(phrase, password); - let pair = Self::pair_from_seed(&seed); - println!("Phrase `{}` is account:\n Seed: 0x{}\n Public key (hex): 0x{}\n Address (SS58): {}", - phrase, - HexDisplay::from(&seed.as_ref()), - HexDisplay::from(&Self::public_from_pair(&pair)), - Self::ss58_from_pair(&pair) - ); + type Pair: Pair; + type Public: Ss58Codec + AsRef<[u8]>; + fn pair_from_suri(suri: &str, password: Option<&str>) -> Self::Pair { + Self::Pair::from_string(suri, password).expect("Invalid phrase") } + fn ss58_from_pair(pair: &Self::Pair) -> String { pair.public().to_ss58check() } + fn public_from_pair(pair: &Self::Pair) -> Vec { pair.public().as_ref().to_owned() } fn print_from_uri(uri: &str, password: Option<&str>) where ::Public: Sized + Ss58Codec + AsRef<[u8]> { - if let Ok(pair) = Self::Pair::from_string(uri, password) { - let seed_text = Self::seed_from_pair(&pair) - .map_or_else(Default::default, |s| format!("\n Seed: 0x{}", HexDisplay::from(&s.as_ref()))); - println!("Secret Key URI `{}` is account:{}\n Public key (hex): 0x{}\n Address (SS58): {}", + if let Ok((pair, seed)) = Self::Pair::from_phrase(uri, password) { + println!("Secret phrase `{}` is account:\n Secret seed: 0x{}\n Public key (hex): 0x{}\n Address (SS58): {}", uri, - seed_text, + HexDisplay::from(&seed.as_ref()), HexDisplay::from(&Self::public_from_pair(&pair)), Self::ss58_from_pair(&pair) ); - } - if let Ok(public) = ::Public::from_string(uri) { + } else if let Ok(pair) = Self::Pair::from_string(uri, password) { + println!("Secret Key URI `{}` is account:\n Public key (hex): 0x{}\n Address (SS58): {}", + uri, + HexDisplay::from(&Self::public_from_pair(&pair)), + Self::ss58_from_pair(&pair) + ); + } else if let Ok(public) = ::Public::from_string(uri) { println!("Public Key URI `{}` is account:\n Public key (hex): 0x{}\n Address (SS58): {}", uri, HexDisplay::from(&public.as_ref()), public.to_ss58check() ); + } else { + println!("Invalid phrase/URI given"); } } } @@ -94,75 +67,47 @@ trait Crypto { struct Ed25519; impl Crypto for Ed25519 { - type Seed = [u8; 32]; type Pair = ed25519::Pair; + type Public = ed25519::Public; - fn seed_from_phrase(phrase: &str, password: Option<&str>) -> Self::Seed { - Sr25519::seed_from_phrase(phrase, password) - } fn pair_from_suri(suri: &str, password_override: Option<&str>) -> Self::Pair { ed25519::Pair::from_legacy_string(suri, password_override) } - fn pair_from_seed(seed: &Self::Seed) -> Self::Pair { ed25519::Pair::from_seed(seed.clone()) } - fn ss58_from_pair(pair: &Self::Pair) -> String { pair.public().to_ss58check() } - fn public_from_pair(pair: &Self::Pair) -> Vec { (&pair.public().0[..]).to_owned() } - fn seed_from_pair(pair: &Self::Pair) -> Option<&Self::Seed> { Some(pair.seed()) } } struct Sr25519; impl Crypto for Sr25519 { - type Seed = [u8; 32]; type Pair = sr25519::Pair; - - fn seed_from_phrase(phrase: &str, password: Option<&str>) -> Self::Seed { - mini_secret_from_entropy( - Mnemonic::from_phrase(phrase, Language::English) - .unwrap_or_else(|_| - panic!("Phrase is not a valid BIP-39 phrase: \n {}", phrase) - ) - .entropy(), - password.unwrap_or("") - ) - .expect("32 bytes can always build a key; qed") - .to_bytes() - } - - fn pair_from_suri(suri: &str, password: Option<&str>) -> Self::Pair { - sr25519::Pair::from_string(suri, password).expect("Invalid phrase") - } - - fn pair_from_seed(seed: &Self::Seed) -> Self::Pair { - MiniSecretKey::from_bytes(seed) - .expect("32 bytes can always build a key; qed") - .into() - } - fn ss58_from_pair(pair: &Self::Pair) -> String { pair.public().to_ss58check() } - fn public_from_pair(pair: &Self::Pair) -> Vec { (&pair.public().0[..]).to_owned() } + type Public = sr25519::Public; } -fn execute>(matches: clap::ArgMatches) where +fn execute(matches: clap::ArgMatches) where <::Pair as Pair>::Signature: AsRef<[u8]> + AsMut<[u8]> + Default, <::Pair as Pair>::Public: Sized + AsRef<[u8]> + Ss58Codec + AsRef<<::Pair as Pair>::Public>, { let password = matches.value_of("password"); match matches.subcommand() { - ("generate", Some(_matches)) => { + ("generate", Some(matches)) => { // create a new randomly generated mnemonic phrase - let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English); - C::print_from_phrase(mnemonic.phrase(), password); - }, - ("vanity", Some(matches)) => { - let desired: String = matches.value_of("pattern").map(str::to_string).unwrap_or_default(); - let key = vanity::generate_key::(&desired).expect("Key generation failed"); - C::print_from_seed(&key.seed); + let words = matches.value_of("words") + .map(|x| usize::from_str(x).expect("Invalid number given for --words")) + .map(|x| MnemonicType::for_word_count(x) + .expect("Invalid number of words given for phrase: must be 12/15/18/21/24") + ).unwrap_or(MnemonicType::Words12); + let mnemonic = Mnemonic::new(words, Language::English); + C::print_from_uri(mnemonic.phrase(), password); } ("inspect", Some(matches)) => { - // TODO: Accept public key with derivation path. let uri = matches.value_of("uri") .expect("URI parameter is required; thus it can't be None; qed"); C::print_from_uri(uri, password); - }, + } + ("vanity", Some(matches)) => { + let desired: String = matches.value_of("pattern").map(str::to_string).unwrap_or_default(); + let result = vanity::generate_key::(&desired).expect("Key generation failed"); + C::print_from_uri(&format!("0x{}", HexDisplay::from(&result.seed.as_ref())), None); + } ("sign", Some(matches)) => { let suri = matches.value_of("suri") .expect("secret URI parameter is required; thus it can't be None; qed"); diff --git a/substrate/subkey/src/vanity.rs b/substrate/subkey/src/vanity.rs index 2399197e99..ea1a609218 100644 --- a/substrate/subkey/src/vanity.rs +++ b/substrate/subkey/src/vanity.rs @@ -16,6 +16,7 @@ use rand::{rngs::OsRng, RngCore}; use super::Crypto; +use substrate_primitives::Pair; fn good_waypoint(done: u64) -> u64 { match done { @@ -26,21 +27,20 @@ fn good_waypoint(done: u64) -> u64 { } } -fn next_seed(mut seed: [u8; 32]) -> [u8; 32] { - for i in 0..32 { +fn next_seed(seed: &mut [u8]) { + for i in 0..seed.len() { match seed[i] { 255 => { seed[i] = 0; } _ => { seed[i] += 1; break; } } } - return seed; } /// A structure used to carry both Pair and seed. /// This should usually NOT been used. If unsure, use Pair. pub(super) struct KeyPair { pub pair: C::Pair, - pub seed: C::Seed, + pub seed: ::Seed, pub score: usize, } @@ -57,7 +57,7 @@ fn calculate_score(_desired: &str, key: &str) -> usize { 0 } -pub(super) fn generate_key>(desired: &str) -> Result, &str> { +pub(super) fn generate_key(desired: &str) -> Result, &str> { if desired.is_empty() { return Err("Pattern must not be empty"); } @@ -66,18 +66,17 @@ pub(super) fn generate_key>(desired: &str) -> Result::Seed::default(); let mut done = 0; - OsRng::new().unwrap().fill_bytes(&mut seed[..]); - loop { - // reset to a new random seed at beginning and regularly thereafter if done % 100000 == 0 { - OsRng::new().unwrap().fill_bytes(&mut seed[..]); + OsRng::new().unwrap().fill_bytes(seed.as_mut()); + } else { + next_seed(seed.as_mut()); } - let p = C::pair_from_seed(&seed); + let p = C::Pair::from_seed(&seed); let ss58 = C::ss58_from_pair(&p); let score = calculate_score(&desired, &ss58); if score > best || desired.len() < 2 { @@ -92,7 +91,6 @@ pub(super) fn generate_key>(desired: &str) -> Result