Refactor key management (#3296)

* Add Call type to extensible transactions.

Cleanup some naming

* Merge Resource and BlockExhausted into just Exhausted

* Fix

* Another fix

* Call

* Some fixes

* Fix srml tests.

* Fix all tests.

* Refactor crypto so each application of it has its own type.

* Introduce new AuthorityProvider API into Aura

This will eventually allow for dynamic determination of authority
keys and avoid having to set them directly on CLI.

* Introduce authority determinator for Babe.

Experiment with modular consensus API.

* Work in progress to introduce KeyTypeId and avoid polluting API
with validator IDs

* Finish up drafting imonline

* Rework offchain workers API.

* Rework API implementation.

* Make it compile for wasm, simplify app_crypto.

* Fix compilation of im-online.

* Fix compilation of im-online.

* Fix more compilation errors.

* Make it compile.

* Fixing tests.

* Rewrite `keystore`

* Fix session tests

* Bring back `TryFrom`'s'

* Fix `srml-grandpa`

* Fix `srml-aura`

* Fix consensus babe

* More fixes

* Make service generate keys from dev_seed

* Build fixes

* Remove offchain tests

* More fixes and cleanups

* Fixes finality grandpa

* Fix `consensus-aura`

* Fix cli

* Fix `node-cli`

* Fix chain_spec builder

* Fix doc tests

* Add authority getter for grandpa.

* Test fix

* Fixes

* Make keystore accessible from the runtime

* Move app crypto to its own crate

* Update `Cargo.lock`

* Make the crypto stuff usable from the runtime

* Adds some runtime crypto tests

* Use last finalized block for grandpa authority

* Fix warning

* Adds `SessionKeys` runtime api

* Remove `FinalityPair` and `ConsensusPair`

* Minor governance tweaks to get it inline with docs.

* Make the governance be up to date with the docs.

* Build fixes.

* Generate the inital session keys

* Failing keystore is a hard error

* Make babe work again

* Fix grandpa

* Fix tests

* Disable `keystore` in consensus critical stuff

* Build fix.

* ImOnline supports multiple authorities at once.

* Update core/application-crypto/src/ed25519.rs

* Merge branch 'master' into gav-in-progress

* Remove unneeded code for now.

* Some `session` testing

* Support querying the public keys

* Cleanup offchain

* Remove warnings

* More cleanup

* Apply suggestions from code review

Co-Authored-By: Benjamin Kampmann <ben.kampmann@googlemail.com>

* More cleanups

* JSONRPC API for setting keys.

Also, rename traits::KeyStore* -> traits::BareCryptoStore*

* Bad merge

* Fix integration tests

* Fix test build

* Test fix

* Fixes

* Warnings

* Another warning

* Bump version.
This commit is contained in:
Gavin Wood
2019-08-07 20:47:48 +02:00
committed by GitHub
parent a6a6779f01
commit 1a524b8207
160 changed files with 4467 additions and 2769 deletions
+202 -36
View File
@@ -18,13 +18,11 @@
//! Cryptographic utilities.
// end::description[]
#[cfg(feature = "std")]
use std::convert::{TryFrom, TryInto};
use rstd::convert::{TryFrom, TryInto};
#[cfg(feature = "std")]
use parking_lot::Mutex;
#[cfg(feature = "std")]
use rand::{RngCore, rngs::OsRng};
#[cfg(feature = "std")]
use codec::{Encode, Decode};
#[cfg(feature = "std")]
use regex::Regex;
@@ -33,6 +31,8 @@ use base58::{FromBase58, ToBase58};
#[cfg(feature = "std")]
use std::hash::Hash;
use zeroize::Zeroize;
#[doc(hidden)]
pub use rstd::ops::Deref;
/// 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";
@@ -83,6 +83,14 @@ impl<T: Zeroize> AsRef<T> for Protected<T> {
}
}
impl<T: Zeroize> rstd::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 {
@@ -279,12 +287,12 @@ pub trait Ss58Codec: Sized {
}
}
#[cfg(feature = "std")]
/// Derivable key trait.
pub trait Derive: Sized {
/// Derive a child key from a series of given junctions.
///
/// Will be `None` for public keys if there are any hard junctions in there.
#[cfg(feature = "std")]
fn derive<Iter: Iterator<Item=DeriveJunction>>(&self, _path: Iter) -> Option<Self> {
None
}
@@ -457,8 +465,8 @@ impl<T: AsMut<[u8]> + AsRef<[u8]> + Default + Derive> Ss58Codec for T {
}
/// Trait suitable for typical cryptographic PKI key public type.
pub trait Public: AsRef<[u8]> + TypedKey + PartialEq + Eq + Clone + Send + Sync {
/// A new instance from the given slice that should be 32 bytes long.
pub trait Public: AsRef<[u8]> + AsMut<[u8]> + Default + Derive + CryptoType + PartialEq + Eq + Clone + Send + Sync {
/// A new instance from the given slice.
///
/// 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!
@@ -466,17 +474,85 @@ pub trait Public: AsRef<[u8]> + TypedKey + PartialEq + Eq + Clone + Send + Sync
/// Return a `Vec<u8>` filled with raw data.
#[cfg(feature = "std")]
fn to_raw_vec(&self) -> Vec<u8>;
fn to_raw_vec(&self) -> Vec<u8> { self.as_slice().to_owned() }
/// Return a slice filled with raw data.
fn as_slice(&self) -> &[u8];
fn as_slice(&self) -> &[u8] { self.as_ref() }
}
#[cfg(feature = "std")]
pub use self::dummy::*;
#[cfg(feature = "std")]
mod dummy {
use super::*;
/// Dummy cryptography. Doesn't do anything.
#[derive(Clone, Hash, Default, Eq, PartialEq)]
pub struct Dummy;
impl AsRef<[u8]> for Dummy {
fn as_ref(&self) -> &[u8] { &b""[..] }
}
impl AsMut<[u8]> for Dummy {
fn as_mut(&mut self) -> &mut[u8] {
unsafe {
#[allow(mutable_transmutes)]
rstd::mem::transmute::<_, &'static mut [u8]>(&b""[..])
}
}
}
impl CryptoType for Dummy {
type Pair = Dummy;
}
impl Derive for Dummy {}
impl Public for Dummy {
fn from_slice(_: &[u8]) -> Self { Self }
#[cfg(feature = "std")]
fn to_raw_vec(&self) -> Vec<u8> { vec![] }
fn as_slice(&self) -> &[u8] { b"" }
}
impl Pair for Dummy {
type Public = Dummy;
type Seed = Dummy;
type Signature = Dummy;
type DeriveError = ();
fn generate_with_phrase(_: Option<&str>) -> (Self, String, Self::Seed) { Default::default() }
fn from_phrase(_: &str, _: Option<&str>)
-> Result<(Self, Self::Seed), SecretStringError>
{
Ok(Default::default())
}
fn derive<
Iter: Iterator<Item=DeriveJunction>
>(&self, _: Iter) -> Result<Self, Self::DeriveError> { Ok(Self) }
fn from_seed(_: &Self::Seed) -> Self { Self }
fn from_seed_slice(_: &[u8]) -> Result<Self, SecretStringError> { Ok(Self) }
fn from_standard_components<
I: Iterator<Item=DeriveJunction>
>(
_: &str,
_: Option<&str>,
_: I
) -> Result<Self, SecretStringError> { Ok(Self) }
fn sign(&self, _: &[u8]) -> Self::Signature { Self }
fn verify<M: AsRef<[u8]>>(_: &Self::Signature, _: M, _: &Self::Public) -> bool { true }
fn verify_weak<P: AsRef<[u8]>, M: AsRef<[u8]>>(_: &[u8], _: M, _: P) -> bool { true }
fn public(&self) -> Self::Public { Self }
fn to_raw_vec(&self) -> Vec<u8> { vec![] }
}
}
/// 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: TypedKey + Sized + Clone + Send + Sync + 'static {
pub trait Pair: CryptoType + Sized + Clone + Send + Sync + 'static {
/// The type which is used to encode a public key.
type Public: Public + Hash;
@@ -538,7 +614,7 @@ pub trait Pair: TypedKey + Sized + Clone + Send + Sync + 'static {
fn sign(&self, message: &[u8]) -> Self::Signature;
/// Verify a signature on a message. Returns true if the signature is good.
fn verify<P: AsRef<Self::Public>, M: AsRef<[u8]>>(sig: &Self::Signature, message: M, pubkey: P) -> bool;
fn verify<M: AsRef<[u8]>>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool;
/// Verify a signature on a message. Returns true if the signature is good.
fn verify_weak<P: AsRef<[u8]>, M: AsRef<[u8]>>(sig: &[u8], message: M, pubkey: P) -> bool;
@@ -603,26 +679,109 @@ pub trait Pair: TypedKey + Sized + Clone + Send + Sync + 'static {
fn to_raw_vec(&self) -> Vec<u8>;
}
/// One type is wrapped by another.
pub trait IsWrappedBy<Outer>: From<Outer> + Into<Outer> {
/// Get a reference to the inner from the outer.
fn from_ref(outer: &Outer) -> &Self;
/// Get a mutable reference to the inner from the outer.
fn from_mut(outer: &mut Outer) -> &mut Self;
}
/// Opposite of `IsWrappedBy` - denotes a type which is a simple wrapper around another type.
pub trait Wraps: Sized {
/// The inner type it is wrapping.
type Inner: IsWrappedBy<Self>;
}
impl<T, Outer> IsWrappedBy<Outer> for T where
Outer: AsRef<Self> + AsMut<Self> + From<Self>,
T: From<Outer>,
{
/// Get a reference to the inner from the outer.
fn from_ref(outer: &Outer) -> &Self { outer.as_ref() }
/// Get a mutable reference to the inner from the outer.
fn from_mut(outer: &mut Outer) -> &mut Self { outer.as_mut() }
}
impl<Inner, Outer, T> UncheckedFrom<T> for Outer where
Outer: Wraps<Inner=Inner>,
Inner: IsWrappedBy<Outer> + UncheckedFrom<T>,
{
fn unchecked_from(t: T) -> Self {
let inner: Inner = t.unchecked_into();
inner.into()
}
}
/// Type which has a particular kind of crypto associated with it.
pub trait CryptoType {
/// The pair key type of this crypto.
#[cfg(feature="std")]
type Pair: Pair;
}
/// An identifier for a type of cryptographic key.
///
/// 0-1024 are reserved.
pub type KeyTypeId = u32;
/// To avoid clashes with other modules when distributing your module publically, register your
/// `KeyTypeId` on the list here by making a PR.
///
/// Values whose first character is `_` are reserved for private use and won't conflict with any
/// public modules.
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct KeyTypeId(pub [u8; 4]);
/// Constant key types.
impl From<u32> for KeyTypeId {
fn from(x: u32) -> Self {
Self(x.to_le_bytes())
}
}
impl From<KeyTypeId> for u32 {
fn from(x: KeyTypeId) -> Self {
u32::from_le_bytes(x.0)
}
}
impl<'a> TryFrom<&'a str> for KeyTypeId {
type Error = ();
fn try_from(x: &'a str) -> Result<Self, ()> {
let b = x.as_bytes();
if b.len() != 4 {
return Err(());
}
let mut res = KeyTypeId::default();
res.0.copy_from_slice(&b[0..4]);
Ok(res)
}
}
/// Known key types; this also functions as a global registry of key types for projects wishing to
/// avoid collisions with each other.
///
/// It's not universal in the sense that *all* key types need to be mentioned here, it's just a
/// handy place to put common 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;
/// Key type for generic S/R 25519 key.
pub const SR25519: KeyTypeId = KeyTypeId(*b"sr25");
/// Key type for generic Ed25519 key.
pub const ED25519: KeyTypeId = KeyTypeId(*b"ed25");
/// Key type for Babe module, build-in.
pub const BABE: KeyTypeId = KeyTypeId(*b"babe");
/// Key type for Grandpa module, build-in.
pub const GRANDPA: KeyTypeId = KeyTypeId(*b"gran");
/// Key type for controlling an account in a Substrate runtime, built-in.
pub const ACCOUNT: KeyTypeId = KeyTypeId(*b"acco");
/// Key type for Aura module, built-in.
pub const AURA: KeyTypeId = KeyTypeId(*b"aura");
/// Key type for ImOnline module, built-in.
pub const IM_ONLINE: KeyTypeId = KeyTypeId(*b"imon");
/// A key type ID useful for tests.
#[cfg(feature = "std")]
pub const DUMMY: KeyTypeId = KeyTypeId(*b"dumy");
}
#[cfg(test)]
@@ -639,14 +798,31 @@ mod tests {
Standard{phrase: String, password: Option<String>, path: Vec<DeriveJunction>},
Seed(Vec<u8>),
}
impl Default for TestPair {
fn default() -> Self {
TestPair::Generated
}
}
impl CryptoType for TestPair {
type Pair = Self;
}
#[derive(Clone, PartialEq, Eq, Hash)]
#[derive(Clone, PartialEq, Eq, Hash, Default)]
struct TestPublic;
impl AsRef<[u8]> for TestPublic {
fn as_ref(&self) -> &[u8] {
&[]
}
}
impl AsMut<[u8]> for TestPublic {
fn as_mut(&mut self) -> &mut [u8] {
&mut []
}
}
impl CryptoType for TestPublic {
type Pair = TestPair;
}
impl Derive for TestPublic {}
impl Public for TestPublic {
fn from_slice(_bytes: &[u8]) -> Self {
Self
@@ -658,9 +834,6 @@ mod tests {
vec![]
}
}
impl TypedKey for TestPublic {
const KEY_TYPE: u32 = 4242;
}
impl Pair for TestPair {
type Public = TestPublic;
type Seed = [u8; 0];
@@ -686,11 +859,7 @@ mod tests {
}
fn from_seed(_seed: &<TestPair as Pair>::Seed) -> Self { TestPair::Seed(vec![]) }
fn sign(&self, _message: &[u8]) -> Self::Signature { [] }
fn verify<P: AsRef<Self::Public>, M: AsRef<[u8]>>(
_sig: &Self::Signature,
_message: M,
_pubkey: P
) -> bool { true }
fn verify<M: AsRef<[u8]>>(_: &Self::Signature, _: M, _: &Self::Public) -> bool { true }
fn verify_weak<P: AsRef<[u8]>, M: AsRef<[u8]>>(
_sig: &[u8],
_message: M,
@@ -717,9 +886,6 @@ mod tests {
vec![]
}
}
impl TypedKey for TestPair {
const KEY_TYPE: u32 = 4242;
}
#[test]
fn interpret_std_seed_should_work() {
+52 -49
View File
@@ -29,10 +29,10 @@ use substrate_bip39::seed_from_entropy;
#[cfg(feature = "std")]
use bip39::{Mnemonic, Language, MnemonicType};
#[cfg(feature = "std")]
use crate::crypto::{Pair as TraitPair, DeriveJunction, SecretStringError, Derive, Ss58Codec};
use crate::crypto::{Pair as TraitPair, DeriveJunction, SecretStringError, Ss58Codec};
#[cfg(feature = "std")]
use serde::{de, Serializer, Serialize, Deserializer, Deserialize};
use crate::crypto::{key_types, KeyTypeId, Public as TraitPublic, TypedKey, UncheckedFrom};
use crate::{crypto::{Public as TraitPublic, UncheckedFrom, CryptoType, Derive}};
/// 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
@@ -77,6 +77,20 @@ impl AsMut<[u8]> for Public {
}
}
impl rstd::convert::TryFrom<&[u8]> for Public {
type Error = ();
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
if data.len() == 32 {
let mut inner = [0u8; 32];
inner.copy_from_slice(data);
Ok(Public(inner))
} else {
Err(())
}
}
}
impl From<Public> for [u8; 32] {
fn from(x: Public) -> Self {
x.0
@@ -90,12 +104,6 @@ impl From<Pair> for Public {
}
}
impl AsRef<Public> for Public {
fn as_ref(&self) -> &Public {
&self
}
}
impl From<Public> for H256 {
fn from(x: Public) -> Self {
x.0.into()
@@ -115,15 +123,15 @@ impl UncheckedFrom<H256> for Public {
}
#[cfg(feature = "std")]
impl ::std::fmt::Display for Public {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
impl std::fmt::Display for Public {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.to_ss58check())
}
}
#[cfg(feature = "std")]
impl ::std::fmt::Debug for Public {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
impl std::fmt::Debug for Public {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let s = self.to_ss58check();
write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8])
}
@@ -145,8 +153,8 @@ impl<'de> Deserialize<'de> for Public {
}
#[cfg(feature = "std")]
impl ::std::hash::Hash for Public {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
impl std::hash::Hash for Public {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
@@ -155,6 +163,20 @@ impl ::std::hash::Hash for Public {
#[derive(Encode, Decode)]
pub struct Signature(pub [u8; 64]);
impl rstd::convert::TryFrom<&[u8]> for Signature {
type Error = ();
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
if data.len() == 64 {
let mut inner = [0u8; 64];
inner.copy_from_slice(data);
Ok(Signature(inner))
} else {
Err(())
}
}
}
impl Clone for Signature {
fn clone(&self) -> Self {
let mut r = [0u8; 64];
@@ -171,7 +193,7 @@ impl Default for Signature {
impl PartialEq for Signature {
fn eq(&self, b: &Self) -> bool {
&self.0[..] == &b.0[..]
self.0[..] == b.0[..]
}
}
@@ -208,16 +230,16 @@ impl AsMut<[u8]> for Signature {
}
#[cfg(feature = "std")]
impl ::std::fmt::Debug for Signature {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
impl std::fmt::Debug for Signature {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.0))
}
}
#[cfg(feature = "std")]
impl ::std::hash::Hash for Signature {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
::std::hash::Hash::hash(&self.0[..], state);
impl std::hash::Hash for Signature {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
std::hash::Hash::hash(&self.0[..], state);
}
}
@@ -306,31 +328,10 @@ impl TraitPublic for Public {
r.copy_from_slice(data);
Public(r)
}
/// Return a `Vec<u8>` filled with raw data.
#[cfg(feature = "std")]
fn to_raw_vec(&self) -> Vec<u8> {
let r: &[u8; 32] = self.as_ref();
r.to_vec()
}
/// Return a slice filled with raw data.
fn as_slice(&self) -> &[u8] {
let r: &[u8; 32] = self.as_ref();
&r[..]
}
}
#[cfg(feature = "std")]
impl Derive for Public {}
#[cfg(feature = "std")]
impl AsRef<Pair> for Pair {
fn as_ref(&self) -> &Pair {
&self
}
}
/// Derive a single hard junction.
#[cfg(feature = "std")]
fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed {
@@ -438,8 +439,8 @@ impl TraitPair for Pair {
}
/// Verify a signature on a message. Returns true if the signature is good.
fn verify<P: AsRef<Self::Public>, M: AsRef<[u8]>>(sig: &Self::Signature, message: M, pubkey: P) -> bool {
Self::verify_weak(&sig.0[..], message.as_ref(), &pubkey.as_ref().0[..])
fn verify<M: AsRef<[u8]>>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool {
Self::verify_weak(&sig.0[..], message.as_ref(), pubkey)
}
/// Verify a signature on a message. Returns true if the signature is good.
@@ -488,17 +489,19 @@ impl Pair {
}
}
impl TypedKey for Public {
const KEY_TYPE: KeyTypeId = key_types::ED25519;
impl CryptoType for Public {
#[cfg(feature="std")]
type Pair = Pair;
}
impl TypedKey for Signature {
const KEY_TYPE: KeyTypeId = key_types::ED25519;
impl CryptoType for Signature {
#[cfg(feature="std")]
type Pair = Pair;
}
#[cfg(feature = "std")]
impl TypedKey for Pair {
const KEY_TYPE: KeyTypeId = key_types::ED25519;
impl CryptoType for Pair {
type Pair = Pair;
}
#[cfg(test)]
+18 -3
View File
@@ -33,11 +33,13 @@ macro_rules! map {
use rstd::prelude::*;
use rstd::ops::Deref;
use codec::{Encode, Decode};
#[cfg(feature = "std")]
use std::borrow::Cow;
#[cfg(feature = "std")]
use serde::{Serialize, Deserialize};
#[cfg(feature = "std")]
pub use serde;// << for macro
pub use codec::{Encode, Decode};// << for macro
#[cfg(feature = "std")]
pub use impl_serde::serialize as bytes;
@@ -61,6 +63,8 @@ pub mod sandbox;
pub mod storage;
pub mod uint;
mod changes_trie;
pub mod traits;
pub mod testing;
#[cfg(test)]
mod tests;
@@ -77,7 +81,6 @@ pub use hash_db::Hasher;
pub use self::hasher::blake2::Blake2Hasher;
/// Context for executing a call into the runtime.
#[repr(u8)]
pub enum ExecutionContext {
/// Context for general importing (including own blocks).
Importing,
@@ -91,6 +94,17 @@ pub enum ExecutionContext {
Other,
}
impl ExecutionContext {
/// Returns if the keystore should be enabled for the current context.
pub fn enable_keystore(&self) -> bool {
use ExecutionContext::*;
match self {
Importing | Syncing | BlockConstruction => false,
OffchainWorker(_) | Other => true,
}
}
}
/// Hex-serialized shim for `Vec<u8>`.
#[derive(PartialEq, Eq, Clone)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Hash, PartialOrd, Ord))]
@@ -147,7 +161,7 @@ impl<R: codec::Encode> ::std::fmt::Debug for NativeOrEncoded<R> {
#[cfg(feature = "std")]
impl<R: codec::Encode> NativeOrEncoded<R> {
/// Return the value as the encoded format.
pub fn as_encoded<'a>(&'a self) -> Cow<'a, [u8]> {
pub fn as_encoded(&self) -> Cow<'_, [u8]> {
match self {
NativeOrEncoded::Encoded(e) => Cow::Borrowed(e.as_slice()),
NativeOrEncoded::Native(n) => Cow::Owned(n.encode()),
@@ -199,3 +213,4 @@ impl codec::Decode for NeverNativeValue {
Err("`NeverNativeValue` should never be decoded".into())
}
}
+2 -162
View File
@@ -16,11 +16,12 @@
//! Offchain workers types
use crate::crypto;
use codec::{Encode, Decode};
use rstd::prelude::{Vec, Box};
use rstd::convert::TryFrom;
pub use crate::crypto::KeyTypeId;
/// A type of supported crypto.
#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
@@ -58,81 +59,6 @@ impl From<StorageKind> for u32 {
}
}
/// A type of supported crypto.
#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
#[repr(C)]
pub enum CryptoKind {
/// SR25519 crypto (Schnorrkel)
Sr25519 = crypto::key_types::SR25519 as isize,
/// ED25519 crypto (Edwards)
Ed25519 = crypto::key_types::ED25519 as isize,
}
impl TryFrom<u32> for CryptoKind {
type Error = ();
fn try_from(kind: u32) -> Result<Self, Self::Error> {
match kind {
e if e == CryptoKind::Sr25519 as isize as u32 => Ok(CryptoKind::Sr25519),
e if e == CryptoKind::Ed25519 as isize as u32 => Ok(CryptoKind::Ed25519),
_ => Err(()),
}
}
}
impl From<CryptoKind> for u32 {
fn from(c: CryptoKind) -> Self {
c as isize as u32
}
}
/// Key to use in the offchain worker crypto api.
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub enum CryptoKey {
/// Use a key from the offchain workers local storage.
LocalKey {
/// The id of the key.
id: u16,
/// The kind of the key.
kind: CryptoKind,
},
/// Use the key the block authoring algorithm uses.
AuthorityKey,
/// Use the key the finality gadget uses.
FgAuthorityKey,
}
impl TryFrom<u64> for CryptoKey {
type Error = ();
fn try_from(key: u64) -> Result<Self, Self::Error> {
match key & 0xFF {
0 => {
let id = (key >> 8 & 0xFFFF) as u16;
let kind = CryptoKind::try_from((key >> 32) as u32)?;
Ok(CryptoKey::LocalKey { id, kind })
}
1 => Ok(CryptoKey::AuthorityKey),
2 => Ok(CryptoKey::FgAuthorityKey),
_ => Err(()),
}
}
}
impl From<CryptoKey> for u64 {
fn from(key: CryptoKey) -> u64 {
match key {
CryptoKey::LocalKey { id, kind } => {
((kind as u64) << 32) | ((id as u64) << 8)
}
CryptoKey::AuthorityKey => 1,
CryptoKey::FgAuthorityKey => 2,
}
}
}
/// Opaque type for offchain http requests.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "std", derive(Debug))]
@@ -314,45 +240,6 @@ pub trait Externalities {
/// Returns information about the local node's network state.
fn network_state(&self) -> Result<OpaqueNetworkState, ()>;
/// Create new key(pair) for signing/encryption/decryption.
///
/// Returns an error if given crypto kind is not supported.
fn new_crypto_key(&mut self, crypto: CryptoKind) -> Result<CryptoKey, ()>;
/// Returns the locally configured authority public key, if available.
fn pubkey(&self, key: CryptoKey) -> Result<Vec<u8>, ()>;
/// Encrypt a piece of data using given crypto key.
///
/// If `key` is `None`, it will attempt to use current authority key of `CryptoKind`.
///
/// Returns an error if `key` is not available or does not exist,
/// or the expected `CryptoKind` does not match.
fn encrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()>;
/// Decrypt a piece of data using given crypto key.
///
/// If `key` is `None`, it will attempt to use current authority key of `CryptoKind`.
///
/// Returns an error if data cannot be decrypted or the `key` is not available or does not exist,
/// or the expected `CryptoKind` does not match.
fn decrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()>;
/// Sign a piece of data using given crypto key.
///
/// If `key` is `None`, it will attempt to use current authority key of `CryptoKind`.
///
/// Returns an error if `key` is not available or does not exist,
/// or the expected `CryptoKind` does not match.
fn sign(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()>;
/// Verifies that `signature` for `msg` matches given `key`.
///
/// Returns an `Ok` with `true` in case it does, `false` in case it doesn't.
/// Returns an error in case the key is not available or does not exist or the parameters
/// lengths are incorrect or `CryptoKind` does not match.
fn verify(&mut self, key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result<bool, ()>;
/// Returns current UNIX timestamp (in millis)
fn timestamp(&mut self) -> Timestamp;
@@ -466,34 +353,10 @@ impl<T: Externalities + ?Sized> Externalities for Box<T> {
(&mut **self).submit_transaction(ex)
}
fn new_crypto_key(&mut self, crypto: CryptoKind) -> Result<CryptoKey, ()> {
(&mut **self).new_crypto_key(crypto)
}
fn encrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).encrypt(key, data)
}
fn network_state(&self) -> Result<OpaqueNetworkState, ()> {
(& **self).network_state()
}
fn pubkey(&self, key: CryptoKey) -> Result<Vec<u8>, ()> {
(&**self).pubkey(key)
}
fn decrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).decrypt(key, data)
}
fn sign(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).sign(key, data)
}
fn verify(&mut self, key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result<bool, ()> {
(&mut **self).verify(key, msg, signature)
}
fn timestamp(&mut self) -> Timestamp {
(&mut **self).timestamp()
}
@@ -571,27 +434,4 @@ mod tests {
assert_eq!(t.sub(Duration::from_millis(10)), Timestamp(0));
assert_eq!(t.diff(&Timestamp(3)), Duration(2));
}
#[test]
fn crypto_key_to_from_u64() {
let key = CryptoKey::AuthorityKey;
let uint: u64 = key.clone().into();
let key2 = CryptoKey::try_from(uint).unwrap();
assert_eq!(key, key2);
let key = CryptoKey::FgAuthorityKey;
let uint: u64 = key.clone().into();
let key2 = CryptoKey::try_from(uint).unwrap();
assert_eq!(key, key2);
let key = CryptoKey::LocalKey { id: 0, kind: CryptoKind::Ed25519 };
let uint: u64 = key.clone().into();
let key2 = CryptoKey::try_from(uint).unwrap();
assert_eq!(key, key2);
let key = CryptoKey::LocalKey { id: 10, kind: CryptoKind::Sr25519 };
let uint: u64 = key.clone().into();
let key2 = CryptoKey::try_from(uint).unwrap();
assert_eq!(key, key2);
}
}
+55 -52
View File
@@ -30,8 +30,10 @@ use substrate_bip39::mini_secret_from_entropy;
#[cfg(feature = "std")]
use bip39::{Mnemonic, Language, MnemonicType};
#[cfg(feature = "std")]
use crate::crypto::{Pair as TraitPair, DeriveJunction, Infallible, SecretStringError, Derive, Ss58Codec};
use crate::crypto::{key_types, KeyTypeId, Public as TraitPublic, TypedKey, UncheckedFrom};
use crate::crypto::{
Pair as TraitPair, DeriveJunction, Infallible, SecretStringError, Ss58Codec
};
use crate::{crypto::{Public as TraitPublic, UncheckedFrom, CryptoType, Derive}};
use crate::hash::{H256, H512};
use codec::{Encode, Decode};
@@ -56,19 +58,13 @@ pub struct Pair(Keypair);
impl Clone for Pair {
fn clone(&self) -> Self {
Pair(schnorrkel::Keypair {
public: self.0.public.clone(),
public: self.0.public,
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
}
}
impl AsRef<[u8; 32]> for Public {
fn as_ref(&self) -> &[u8; 32] {
&self.0
@@ -99,6 +95,20 @@ impl From<Public> for H256 {
}
}
impl rstd::convert::TryFrom<&[u8]> for Public {
type Error = ();
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
if data.len() == 32 {
let mut inner = [0u8; 32];
inner.copy_from_slice(data);
Ok(Public(inner))
} else {
Err(())
}
}
}
impl UncheckedFrom<[u8; 32]> for Public {
fn unchecked_from(x: [u8; 32]) -> Self {
Public::from_raw(x)
@@ -112,15 +122,15 @@ impl UncheckedFrom<H256> for Public {
}
#[cfg(feature = "std")]
impl ::std::fmt::Display for Public {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
impl std::fmt::Display for Public {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.to_ss58check())
}
}
#[cfg(feature = "std")]
impl ::std::fmt::Debug for Public {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
impl std::fmt::Debug for Public {
fn fmt(&self, f: &mut std::fmt::Formatter) -> ::std::fmt::Result {
let s = self.to_ss58check();
write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8])
}
@@ -142,8 +152,8 @@ impl<'de> Deserialize<'de> for Public {
}
#[cfg(feature = "std")]
impl ::std::hash::Hash for Public {
fn hash<H: ::std::hash::Hasher>(&self, state: &mut H) {
impl std::hash::Hash for Public {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
@@ -154,6 +164,20 @@ impl ::std::hash::Hash for Public {
#[derive(Encode, Decode)]
pub struct Signature(pub [u8; 64]);
impl rstd::convert::TryFrom<&[u8]> for Signature {
type Error = ();
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
if data.len() == 64 {
let mut inner = [0u8; 64];
inner.copy_from_slice(data);
Ok(Signature(inner))
} else {
Err(())
}
}
}
impl Clone for Signature {
fn clone(&self) -> Self {
let mut r = [0u8; 64];
@@ -170,7 +194,7 @@ impl Default for Signature {
impl PartialEq for Signature {
fn eq(&self, b: &Self) -> bool {
&self.0[..] == &b.0[..]
self.0[..] == b.0[..]
}
}
@@ -268,11 +292,11 @@ impl Signature {
}
}
#[cfg(feature = "std")]
impl Derive for Public {
/// Derive a child key from a series of given junctions.
///
/// `None` if there are any hard junctions in there.
#[cfg(feature = "std")]
fn derive<Iter: Iterator<Item=DeriveJunction>>(&self, path: Iter) -> Option<Public> {
let mut acc = PublicKey::from_bytes(self.as_ref()).ok()?;
for j in path {
@@ -318,24 +342,6 @@ impl TraitPublic for Public {
r.copy_from_slice(data);
Public(r)
}
/// Return a `Vec<u8>` filled with raw data.
#[cfg(feature = "std")]
fn to_raw_vec(&self) -> Vec<u8> {
self.0.to_vec()
}
/// Return a slice filled with raw data.
fn as_slice(&self) -> &[u8] {
&self.0
}
}
#[cfg(feature = "std")]
impl AsRef<Pair> for Pair {
fn as_ref(&self) -> &Pair {
&self
}
}
#[cfg(feature = "std")]
@@ -475,20 +481,15 @@ impl TraitPair for Pair {
}
/// Verify a signature on a message. Returns true if the signature is good.
fn verify<P: AsRef<Self::Public>, M: AsRef<[u8]>>(sig: &Self::Signature, message: M, pubkey: P) -> bool {
// Match both schnorrkel 0.1.1 and 0.8.0+ signatures, supporting both wallets
// that have not been upgraded and those that have. To swap to 0.8.0 only,
// create `schnorrkel::Signature` and pass that into `verify_simple`
match PublicKey::from_bytes(pubkey.as_ref().as_slice()) {
Ok(pk) => pk.verify_simple_preaudit_deprecated(
SIGNING_CTX, message.as_ref(), &sig.as_ref(),
).is_ok(),
Err(_) => false,
}
fn verify<M: AsRef<[u8]>>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool {
Self::verify_weak(&sig.0[..], message, pubkey)
}
/// Verify a signature on a message. Returns true if the signature is good.
fn verify_weak<P: AsRef<[u8]>, M: AsRef<[u8]>>(sig: &[u8], message: M, pubkey: P) -> bool {
// Match both schnorrkel 0.1.1 and 0.8.0+ signatures, supporting both wallets
// that have not been upgraded and those that have. To swap to 0.8.0 only,
// create `schnorrkel::Signature` and pass that into `verify_simple`
match PublicKey::from_bytes(pubkey.as_ref()) {
Ok(pk) => pk.verify_simple_preaudit_deprecated(
SIGNING_CTX, message.as_ref(), &sig,
@@ -518,17 +519,19 @@ impl Pair {
}
}
impl TypedKey for Public {
const KEY_TYPE: KeyTypeId = key_types::SR25519;
impl CryptoType for Public {
#[cfg(feature="std")]
type Pair = Pair;
}
impl TypedKey for Signature {
const KEY_TYPE: KeyTypeId = key_types::SR25519;
impl CryptoType for Signature {
#[cfg(feature="std")]
type Pair = Pair;
}
#[cfg(feature = "std")]
impl TypedKey for Pair {
const KEY_TYPE: KeyTypeId = key_types::SR25519;
impl CryptoType for Pair {
type Pair = Pair;
}
#[cfg(test)]
@@ -713,6 +716,6 @@ mod test {
let js_signature = Signature::from_raw(hex!(
"28a854d54903e056f89581c691c1f7d2ff39f8f896c9e9c22475e60902cc2b3547199e0e91fa32902028f2ca2355e8cdd16cfe19ba5e8b658c94aa80f3b81a00"
));
assert!(Pair::verify(&js_signature, b"SUBSTRATE", public));
assert!(Pair::verify(&js_signature, b"SUBSTRATE", &public));
}
}
+115
View File
@@ -0,0 +1,115 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Types that should only be used for testing!
#[cfg(feature = "std")]
use crate::{ed25519, sr25519, crypto::{Public, Pair, KeyTypeId}};
/// A keystore implementation usable in tests.
#[cfg(feature = "std")]
#[derive(Default)]
pub struct KeyStore {
/// `KeyTypeId` maps to public keys and public keys map to private keys.
keys: std::collections::HashMap<KeyTypeId, std::collections::HashMap<Vec<u8>, Vec<u8>>>,
}
#[cfg(feature = "std")]
impl KeyStore {
/// Creates a new instance of `Self`.
pub fn new() -> std::sync::Arc<parking_lot::RwLock<Self>> {
std::sync::Arc::new(parking_lot::RwLock::new(Self::default()))
}
}
#[cfg(feature = "std")]
impl crate::traits::BareCryptoStore for KeyStore {
fn sr25519_public_keys(&self, id: KeyTypeId) -> Vec<sr25519::Public> {
self.keys.get(&id)
.map(|keys|
keys.values()
.map(|s| sr25519::Pair::from_seed_slice(s).expect("`sr25519` seed slice is valid"))
.map(|p| p.public())
.collect()
)
.unwrap_or_default()
}
fn sr25519_generate_new(
&mut self,
id: KeyTypeId,
seed: Option<&str>,
) -> Result<sr25519::Public, String> {
match seed {
Some(seed) => {
let pair = sr25519::Pair::from_string(seed, None).expect("Generates an `sr25519` pair.");
self.keys.entry(id).or_default().insert(pair.public().to_raw_vec(), pair.to_raw_vec());
Ok(pair.public())
},
None => {
let (pair, _) = sr25519::Pair::generate();
self.keys.entry(id).or_default().insert(pair.public().to_raw_vec(), pair.to_raw_vec());
Ok(pair.public())
}
}
}
fn sr25519_key_pair(&self, id: KeyTypeId, pub_key: &sr25519::Public) -> Option<sr25519::Pair> {
self.keys.get(&id)
.and_then(|inner|
inner.get(pub_key.as_slice())
.map(|s| sr25519::Pair::from_seed_slice(s).expect("`sr25519` seed slice is valid"))
)
}
fn ed25519_public_keys(&self, id: KeyTypeId) -> Vec<ed25519::Public> {
self.keys.get(&id)
.map(|keys|
keys.values()
.map(|s| ed25519::Pair::from_seed_slice(s).expect("`ed25519` seed slice is valid"))
.map(|p| p.public())
.collect()
)
.unwrap_or_default()
}
fn ed25519_generate_new(
&mut self,
id: KeyTypeId,
seed: Option<&str>,
) -> Result<ed25519::Public, String> {
match seed {
Some(seed) => {
let pair = ed25519::Pair::from_string(seed, None).expect("Generates an `ed25519` pair.");
self.keys.entry(id).or_default().insert(pair.public().to_raw_vec(), pair.to_raw_vec());
Ok(pair.public())
},
None => {
let (pair, _) = ed25519::Pair::generate();
self.keys.entry(id).or_default().insert(pair.public().to_raw_vec(), pair.to_raw_vec());
Ok(pair.public())
}
}
}
fn ed25519_key_pair(&self, id: KeyTypeId, pub_key: &ed25519::Public) -> Option<ed25519::Pair> {
self.keys.get(&id)
.and_then(|inner|
inner.get(pub_key.as_slice())
.map(|s| ed25519::Pair::from_seed_slice(s).expect("`ed25519` seed slice is valid"))
)
}
}
+72
View File
@@ -0,0 +1,72 @@
// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Shareable Substrate traits.
#[cfg(feature = "std")]
use crate::{crypto::KeyTypeId, ed25519, sr25519};
/// Something that generates, stores and provides access to keys.
#[cfg(feature = "std")]
pub trait BareCryptoStore: Send + Sync {
/// Returns all sr25519 public keys for the given key type.
fn sr25519_public_keys(&self, id: KeyTypeId) -> Vec<sr25519::Public>;
/// Generate a new sr25519 key pair for the given key type and an optional seed.
///
/// If the given seed is `Some(_)`, the key pair will only be stored in memory.
///
/// Returns the public key of the generated key pair.
fn sr25519_generate_new(
&mut self,
id: KeyTypeId,
seed: Option<&str>,
) -> Result<sr25519::Public, String>;
/// Returns the sr25519 key pair for the given key type and public key combination.
fn sr25519_key_pair(&self, id: KeyTypeId, pub_key: &sr25519::Public) -> Option<sr25519::Pair>;
/// Returns all ed25519 public keys for the given key type.
fn ed25519_public_keys(&self, id: KeyTypeId) -> Vec<ed25519::Public>;
/// Generate a new ed25519 key pair for the given key type and an optional seed.
///
/// If the given seed is `Some(_)`, the key pair will only be stored in memory.
///
/// Returns the public key of the generated key pair.
fn ed25519_generate_new(
&mut self,
id: KeyTypeId,
seed: Option<&str>,
) -> Result<ed25519::Public, String>;
/// Returns the ed25519 key pair for the given key type and public key combination.
fn ed25519_key_pair(&self, id: KeyTypeId, pub_key: &ed25519::Public) -> Option<ed25519::Pair>;
/// Insert a new key. This doesn't require any known of the crypto; but a public key must be
/// manually provided.
///
/// Places it into the file system store.
///
/// `Err` if there's some sort of weird filesystem error, but should generally be `Ok`.
fn insert_unknown(&mut self, _key_type: KeyTypeId, _suri: &str, _public: &[u8]) -> Result<(), ()> {
Err(())
}
/// Get the password for this store.
fn password(&self) -> Option<&str> { None }
}
/// A pointer to the key store.
#[cfg(feature = "std")]
pub type BareCryptoStorePtr = std::sync::Arc<parking_lot::RwLock<dyn BareCryptoStore>>;