Generic keystore (#3008)

* Add KeyTypeId.

* Implement clone for sr25519::Pair.

* Extend Pair with to_raw_vec.

* Implement TypedKey for Signature and Pair.

* Add trait Public.

* Make keystore generic.

* Fixup clone.

* Fix tests.

* Update service.

* Fix imports.

* Fix build.

* Fix babe build.

* Fix subkey build.

* Make authority setup generic.

* Update node-template.

* Fix build.

* Remove unsafe code.

* Fix tests.
This commit is contained in:
David Craven
2019-07-04 13:14:55 +02:00
committed by GitHub
parent 336053f7ae
commit 51e345c901
15 changed files with 279 additions and 122 deletions
+64 -4
View File
@@ -26,6 +26,8 @@ use parity_codec::{Encode, Decode};
use regex::Regex;
#[cfg(feature = "std")]
use base58::{FromBase58, ToBase58};
#[cfg(feature = "std")]
use std::hash::Hash;
/// 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";
@@ -286,13 +288,30 @@ impl<T: AsMut<[u8]> + AsRef<[u8]> + Default + Derive> Ss58Codec for T {
}
}
/// Trait suitable for typical cryptographic PKI key public type.
pub trait Public: PartialEq + Eq {
/// A new instance from the given slice that should be 32 bytes long.
///
/// NOTE: No checking goes on to ensure this is a real public key. Only use it if
/// you are certain that the array actually is a pubkey. GIGO!
fn from_slice(data: &[u8]) -> Self;
/// Return a `Vec<u8>` filled with raw data.
#[cfg(feature = "std")]
fn to_raw_vec(&self) -> Vec<u8>;
/// Return a slice filled with raw data.
fn as_slice(&self) -> &[u8];
}
/// Trait suitable for typical cryptographic PKI key pair type.
///
/// For now it just specifies how to create a key from a phrase and derivation path.
#[cfg(feature = "std")]
pub trait Pair: Sized + 'static {
pub trait Pair: Sized + 'static
{
/// TThe type which is used to encode a public key.
type Public;
type Public: Public + Hash;
/// The type used to (minimally) encode the data required to securely create
/// a new key pair.
@@ -412,6 +431,31 @@ pub trait Pair: Sized + 'static {
path,
)
}
/// Return a vec filled with raw data.
fn to_raw_vec(&self) -> Vec<u8>;
}
/// An identifier for a type of cryptographic key.
///
/// 0-1024 are reserved.
pub type KeyTypeId = u32;
/// Constant key types.
pub mod key_types {
use super::KeyTypeId;
/// ED25519 public key.
pub const ED25519: KeyTypeId = 10;
/// SR25519 public key.
pub const SR25519: KeyTypeId = 20;
}
/// A trait for something that has a key type ID.
pub trait TypedKey {
/// The type ID of this key.
const KEY_TYPE: KeyTypeId;
}
#[cfg(test)]
@@ -429,8 +473,21 @@ mod tests {
Seed(Vec<u8>),
}
#[derive(PartialEq, Eq, Hash)]
struct TestPublic;
impl Public for TestPublic {
fn from_slice(bytes: &[u8]) -> Self {
Self
}
fn as_slice(&self) -> &[u8] {
&[]
}
fn to_raw_vec(&self) -> Vec<u8> {
vec![]
}
}
impl Pair for TestPair {
type Public = ();
type Public = TestPublic;
type Seed = [u8; 0];
type Signature = ();
type DeriveError = ();
@@ -464,7 +521,7 @@ mod tests {
_message: M,
_pubkey: P
) -> bool { true }
fn public(&self) -> Self::Public { () }
fn public(&self) -> Self::Public { TestPublic }
fn from_standard_components<I: Iterator<Item=DeriveJunction>>(
phrase: &str,
password: Option<&str>,
@@ -481,6 +538,9 @@ mod tests {
{
Ok(TestPair::Seed(seed.to_owned()))
}
fn to_raw_vec(&self) -> Vec<u8> {
vec![]
}
}
#[test]
+38 -18
View File
@@ -32,7 +32,7 @@ use bip39::{Mnemonic, Language, MnemonicType};
use crate::crypto::{Pair as TraitPair, DeriveJunction, SecretStringError, Derive, Ss58Codec};
#[cfg(feature = "std")]
use serde::{de, Serializer, Serialize, Deserializer, Deserialize};
use crate::crypto::UncheckedFrom;
use crate::crypto::{key_types, KeyTypeId, Public as TraitPublic, TypedKey, UncheckedFrom};
/// A secret seed. It's not called a "secret key" because ring doesn't expose the secret keys
/// of the key pair (yeah, dumb); as such we're forced to remember the seed manually if we
@@ -282,16 +282,6 @@ impl Public {
Public(data)
}
/// A new instance from the given slice that should be 32 bytes long.
///
/// NOTE: No checking goes on to ensure this is a real public key. Only use it if
/// you are certain that the array actually is a pubkey. GIGO!
pub fn from_slice(data: &[u8]) -> Self {
let mut r = [0u8; 32];
r.copy_from_slice(data);
Public(r)
}
/// A new instance from an H256.
///
/// NOTE: No checking goes on to ensure this is a real public key. Only use it if
@@ -300,23 +290,35 @@ impl Public {
Public(x.into())
}
/// Return a slice filled with raw data.
pub fn as_array_ref(&self) -> &[u8; 32] {
self.as_ref()
}
}
impl TraitPublic for Public {
/// A new instance from the given slice that should be 32 bytes long.
///
/// NOTE: No checking goes on to ensure this is a real public key. Only use it if
/// you are certain that the array actually is a pubkey. GIGO!
fn from_slice(data: &[u8]) -> Self {
let mut r = [0u8; 32];
r.copy_from_slice(data);
Public(r)
}
/// Return a `Vec<u8>` filled with raw data.
#[cfg(feature = "std")]
pub fn to_raw_vec(self) -> Vec<u8> {
fn to_raw_vec(&self) -> Vec<u8> {
let r: &[u8; 32] = self.as_ref();
r.to_vec()
}
/// Return a slice filled with raw data.
pub fn as_slice(&self) -> &[u8] {
fn as_slice(&self) -> &[u8] {
let r: &[u8; 32] = self.as_ref();
&r[..]
}
/// Return a slice filled with raw data.
pub fn as_array_ref(&self) -> &[u8; 32] {
self.as_ref()
}
}
#[cfg(feature = "std")]
@@ -460,6 +462,11 @@ impl TraitPair for Pair {
_ => false,
}
}
/// Return a vec filled with raw data.
fn to_raw_vec(&self) -> Vec<u8> {
self.seed().to_vec()
}
}
#[cfg(feature = "std")]
@@ -481,6 +488,19 @@ impl Pair {
}
}
impl TypedKey for Public {
const KEY_TYPE: KeyTypeId = key_types::ED25519;
}
impl TypedKey for Signature {
const KEY_TYPE: KeyTypeId = key_types::ED25519;
}
#[cfg(feature = "std")]
impl TypedKey for Pair {
const KEY_TYPE: KeyTypeId = key_types::ED25519;
}
#[cfg(test)]
mod test {
use super::*;
+1 -1
View File
@@ -69,7 +69,7 @@ pub use self::hash::{H160, H256, H512, convert_hash};
pub use self::uint::U256;
pub use changes_trie::ChangesTrieConfiguration;
#[cfg(feature = "std")]
pub use crypto::{DeriveJunction, Pair};
pub use crypto::{DeriveJunction, Pair, Public};
pub use hash_db::Hasher;
// Switch back to Blake after PoC-3 is out
+67 -27
View File
@@ -31,13 +31,14 @@ use substrate_bip39::mini_secret_from_entropy;
use bip39::{Mnemonic, Language, MnemonicType};
#[cfg(feature = "std")]
use crate::crypto::{Pair as TraitPair, DeriveJunction, Infallible, SecretStringError, Derive, Ss58Codec};
use crate::{hash::{H256, H512}, crypto::UncheckedFrom};
use crate::crypto::{key_types, KeyTypeId, Public as TraitPublic, TypedKey, UncheckedFrom};
use crate::hash::{H256, H512};
use parity_codec::{Encode, Decode};
#[cfg(feature = "std")]
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "std")]
use schnorrkel::keys::MINI_SECRET_KEY_LENGTH;
use schnorrkel::keys::{MINI_SECRET_KEY_LENGTH, SECRET_KEY_LENGTH};
// signing context
#[cfg(feature = "std")]
@@ -51,6 +52,17 @@ pub struct Public(pub [u8; 32]);
#[cfg(feature = "std")]
pub struct Pair(Keypair);
#[cfg(feature = "std")]
impl Clone for Pair {
fn clone(&self) -> Self {
Pair(schnorrkel::Keypair {
public: self.0.public.clone(),
secret: schnorrkel::SecretKey::from_bytes(&self.0.secret.to_bytes()[..])
.expect("key is always the correct size; qed")
})
}
}
impl AsRef<Public> for Public {
fn as_ref(&self) -> &Public {
&self
@@ -282,16 +294,6 @@ impl Public {
Public(data)
}
/// A new instance from the given slice that should be 32 bytes long.
///
/// NOTE: No checking goes on to ensure this is a real public key. Only use it if
/// you are certain that the array actually is a pubkey. GIGO!
pub fn from_slice(data: &[u8]) -> Self {
let mut r = [0u8; 32];
r.copy_from_slice(data);
Public(r)
}
/// A new instance from an H256.
///
/// NOTE: No checking goes on to ensure this is a real public key. Only use it if
@@ -300,21 +302,33 @@ impl Public {
Public(x.into())
}
/// Return a slice filled with raw data.
pub fn as_array_ref(&self) -> &[u8; 32] {
self.as_ref()
}
}
impl TraitPublic for Public {
/// A new instance from the given slice that should be 32 bytes long.
///
/// NOTE: No checking goes on to ensure this is a real public key. Only use it if
/// you are certain that the array actually is a pubkey. GIGO!
fn from_slice(data: &[u8]) -> Self {
let mut r = [0u8; 32];
r.copy_from_slice(data);
Public(r)
}
/// Return a `Vec<u8>` filled with raw data.
#[cfg(feature = "std")]
pub fn into_raw_vec(self) -> Vec<u8> {
fn to_raw_vec(&self) -> Vec<u8> {
self.0.to_vec()
}
/// Return a slice filled with raw data.
pub fn as_slice(&self) -> &[u8] {
fn as_slice(&self) -> &[u8] {
&self.0
}
/// Return a slice filled with raw data.
pub fn as_array_ref(&self) -> &[u8; 32] {
self.as_ref()
}
}
#[cfg(feature = "std")]
@@ -397,14 +411,22 @@ impl TraitPair for Pair {
///
/// You should never need to use this; generate(), generate_with_phrase(), from_phrase()
fn from_seed_slice(seed: &[u8]) -> Result<Pair, SecretStringError> {
if seed.len() != MINI_SECRET_KEY_LENGTH {
Err(SecretStringError::InvalidSeedLength)
} else {
Ok(Pair(
MiniSecretKey::from_bytes(seed)
.map_err(|_| SecretStringError::InvalidSeed)?
.expand_to_keypair()
))
match seed.len() {
MINI_SECRET_KEY_LENGTH => {
Ok(Pair(
MiniSecretKey::from_bytes(seed)
.map_err(|_| SecretStringError::InvalidSeed)?
.expand_to_keypair()
))
}
SECRET_KEY_LENGTH => {
Ok(Pair(
SecretKey::from_bytes(seed)
.map_err(|_| SecretStringError::InvalidSeed)?
.to_keypair()
))
}
_ => Err(SecretStringError::InvalidSeedLength)
}
}
@@ -478,6 +500,11 @@ impl TraitPair for Pair {
Err(_) => false,
}
}
/// Return a vec filled with raw data.
fn to_raw_vec(&self) -> Vec<u8> {
self.0.secret.to_bytes().to_vec()
}
}
#[cfg(feature = "std")]
@@ -495,6 +522,19 @@ impl Pair {
}
}
impl TypedKey for Public {
const KEY_TYPE: KeyTypeId = key_types::SR25519;
}
impl TypedKey for Signature {
const KEY_TYPE: KeyTypeId = key_types::SR25519;
}
#[cfg(feature = "std")]
impl TypedKey for Pair {
const KEY_TYPE: KeyTypeId = key_types::SR25519;
}
#[cfg(test)]
mod test {
use super::*;