Crypto fixes/improvements (#2008)

* Crypto fixes:

- Use schnorrkel's HDKD derive
- Assume all key URIs beginning with `/` are prefixed with public
  root phrase.

* Remove commented code.

* Update README

* Update core/primitives/src/ed25519.rs

Co-Authored-By: gavofyork <github@gavwood.com>
This commit is contained in:
Gav Wood
2019-03-15 13:53:09 +01:00
committed by GitHub
parent ae1351cb79
commit 03d52fdbeb
10 changed files with 70 additions and 38 deletions
+15 -4
View File
@@ -25,6 +25,12 @@ use regex::Regex;
#[cfg(feature = "std")]
use base58::{FromBase58, ToBase58};
/// The root phrase for our publically known keys.
pub const DEV_PHRASE: &str = "bottom drive obey lake curtain smoke basket hold race lonely fit walk";
/// The address of the associated root phrase for our publically known keys.
pub const DEV_ADDRESS: &str = "5DfhGyQdFobKM8NsWvEeAKk5EQQgYe9AydgJ7rMB6E1EqAS7";
/// The infallible type.
#[derive(Debug)]
pub enum Infallible {}
@@ -243,14 +249,16 @@ impl<T: AsMut<[u8]> + AsRef<[u8]> + Default + Derive> Ss58Codec for T {
}
fn from_string(s: &str) -> Result<Self, PublicError> {
let re = Regex::new(r"^(?P<ss58>[\w\d]+)(?P<path>(//?[^/]+)*)$")
let re = Regex::new(r"^(?P<ss58>[\w\d]+)?(?P<path>(//?[^/]+)*)$")
.expect("constructed from known-good static value; qed");
let cap = re.captures(s).ok_or(PublicError::InvalidFormat)?;
let re_junction = Regex::new(r"/(/?[^/]+)")
.expect("constructed from known-good static value; qed");
let path = re_junction.captures_iter(&cap["path"])
.map(|f| DeriveJunction::from(&f[1]));
Self::from_ss58check(&cap["ss58"])?.derive(path).ok_or(PublicError::InvalidPath)
Self::from_ss58check(cap.name("ss58").map(|r| r.as_str()).unwrap_or(DEV_ADDRESS))?
.derive(path)
.ok_or(PublicError::InvalidPath)
}
}
@@ -335,6 +343,9 @@ pub trait Pair: Sized {
/// - the phrase may be followed by one or more items delimited by `/` characters.
/// - the path may be followed by `///`, in which case everything after the `///` is treated
/// as a password.
/// - If `s` begins with a `/` character it is prefixed with the Substrate public `DEV_PHRASE` and
/// interpreted as above.
///
/// In this case they are interpreted as HDKD junctions; purely numeric items are interpreted as
/// integers, non-numeric items as strings. Junctions prefixed with `/` are interpreted as soft
/// junctions, and with `//` as hard junctions.
@@ -359,7 +370,7 @@ pub trait Pair: Sized {
}
}
let re = Regex::new(r"^(?P<phrase>\w+( \w+)*)(?P<path>(//?[^/]+)*)(///(?P<password>.*))?$")
let re = Regex::new(r"^(?P<phrase>\w+( \w+)*)?(?P<path>(//?[^/]+)*)(///(?P<password>.*))?$")
.expect("constructed from known-good static value; qed");
let cap = re.captures(s).ok_or(SecretStringError::InvalidFormat)?;
let re_junction = Regex::new(r"/(/?[^/]+)")
@@ -367,7 +378,7 @@ pub trait Pair: Sized {
let path = re_junction.captures_iter(&cap["path"])
.map(|f| DeriveJunction::from(&f[1]));
Self::from_standard_components(
&cap["phrase"],
cap.name("phrase").map(|r| r.as_str()).unwrap_or(DEV_PHRASE),
password_override.or_else(|| cap.name("password").map(|m| m.as_str())),
path,
)
+9 -1
View File
@@ -523,7 +523,15 @@ impl Pair {
mod test {
use super::*;
use hex_literal::{hex, hex_impl};
use crate::Pair as _Pair;
use crate::{Pair as _Pair, crypto::DEV_PHRASE};
#[test]
fn default_phrase_should_be_used() {
assert_eq!(
Pair::from_string("//Alice///password", None).unwrap().public(),
Pair::from_string(&format!("{}//Alice", DEV_PHRASE), Some("password")).unwrap().public(),
);
}
#[test]
fn test_vector_should_work() {
+35 -7
View File
@@ -366,11 +366,7 @@ impl AsRef<schnorrkel::Keypair> for Pair {
/// Derive a single hard junction.
#[cfg(feature = "std")]
fn derive_hard_junction(secret: &SecretKey, cc: &[u8; CHAIN_CODE_LENGTH]) -> SecretKey {
("SchnorrRistrettoHDKD", &secret.to_bytes()[..], cc).using_encoded(|data|
MiniSecretKey::from_bytes(blake2_rfc::blake2b::blake2b(32, &[], data).as_bytes())
.expect("all 32-byte crypto-hash results are valid MiniSecretKeys; qed")
.expand()
)
secret.hard_derive_mini_secret_key(signing_context(b"SchnorrRistrettoHDKD").bytes(&cc[..])).expand()
}
#[cfg(feature = "std")]
@@ -507,9 +503,41 @@ impl Pair {
#[cfg(test)]
mod test {
use super::*;
use crate::Pair as _Pair;
use crate::{Pair as _Pair, crypto::{Ss58Codec, DEV_PHRASE, DEV_ADDRESS}};
use hex_literal::{hex, hex_impl};
#[test]
fn default_phrase_should_be_used() {
assert_eq!(
Pair::from_string("//Alice///password", None).unwrap().public(),
Pair::from_string(&format!("{}//Alice", DEV_PHRASE), Some("password")).unwrap().public(),
);
assert_eq!(
Pair::from_string(&format!("{}/Alice", DEV_PHRASE), None).as_ref().map(Pair::public),
Pair::from_string("/Alice", None).as_ref().map(Pair::public)
);
}
#[test]
fn default_address_should_be_used() {
assert_eq!(
Public::from_string(&format!("{}/Alice", DEV_ADDRESS)),
Public::from_string("/Alice")
);
}
#[test]
fn default_phrase_should_correspond_to_default_address() {
assert_eq!(
Pair::from_string(&format!("{}/Alice", DEV_PHRASE), None).unwrap().public(),
Public::from_string(&format!("{}/Alice", DEV_ADDRESS)).unwrap(),
);
assert_eq!(
Pair::from_string("/Alice", None).unwrap().public(),
Public::from_string("/Alice").unwrap()
);
}
#[test]
fn derive_soft_should_work() {
let pair: Pair = Pair::from_seed(hex!(