[offchain] Support for sign & verify for crypto keys (#3023)

* Implement sign & verify.

* Use phrases and password.

* Sign & verify with authority keys.

* Fix tests.

* WiP

* WiP

* Allow the caller to decide on 'CryptoKind'.

* Remove TODO.

* Make seed private back.

* Fix non-std build and bump version.

* Use Into<u32> instead of asses.

* Add missing typedef.
This commit is contained in:
Tomasz Drwięga
2019-07-09 17:09:14 +02:00
committed by Gavin Wood
parent ed630e5eda
commit e729dbabbe
22 changed files with 647 additions and 178 deletions
+51 -8
View File
@@ -28,6 +28,7 @@ use regex::Regex;
use base58::{FromBase58, ToBase58};
#[cfg(feature = "std")]
use std::hash::Hash;
use zeroize::Zeroize;
/// 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";
@@ -66,6 +67,43 @@ 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
}
}
#[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 = "std")]
@@ -289,7 +327,7 @@ 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 {
pub trait Public: TypedKey + 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
@@ -308,9 +346,8 @@ pub trait Public: PartialEq + Eq {
///
/// For now it just specifies how to create a key from a phrase and derivation path.
#[cfg(feature = "std")]
pub trait Pair: Sized + 'static
{
/// TThe type which is used to encode a public key.
pub trait Pair: TypedKey + Sized + 'static {
/// The type which is used to encode a public key.
type Public: Public + Hash;
/// The type used to (minimally) encode the data required to securely create
@@ -319,7 +356,7 @@ pub trait Pair: Sized + 'static
/// 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.
type Signature;
type Signature: AsRef<[u8]>;
/// Error returned from the `derive` function.
type DeriveError;
@@ -476,7 +513,7 @@ mod tests {
#[derive(PartialEq, Eq, Hash)]
struct TestPublic;
impl Public for TestPublic {
fn from_slice(bytes: &[u8]) -> Self {
fn from_slice(_bytes: &[u8]) -> Self {
Self
}
fn as_slice(&self) -> &[u8] {
@@ -486,10 +523,13 @@ mod tests {
vec![]
}
}
impl TypedKey for TestPublic {
const KEY_TYPE: u32 = 4242;
}
impl Pair for TestPair {
type Public = TestPublic;
type Seed = [u8; 0];
type Signature = ();
type Signature = [u8; 0];
type DeriveError = ();
fn generate() -> (Self, <Self as Pair>::Seed) { (TestPair::Generated, []) }
@@ -510,7 +550,7 @@ mod tests {
Err(())
}
fn from_seed(_seed: &<TestPair as Pair>::Seed) -> Self { TestPair::Seed(vec![]) }
fn sign(&self, _message: &[u8]) -> Self::Signature { () }
fn sign(&self, _message: &[u8]) -> Self::Signature { [] }
fn verify<P: AsRef<Self::Public>, M: AsRef<[u8]>>(
_sig: &Self::Signature,
_message: M,
@@ -542,6 +582,9 @@ mod tests {
vec![]
}
}
impl TypedKey for TestPair {
const KEY_TYPE: u32 = 4242;
}
#[test]
fn interpret_std_seed_should_work() {
+60 -25
View File
@@ -16,11 +16,13 @@
//! Offchain workers types
use crate::crypto;
use parity_codec::{Encode, Decode};
use rstd::prelude::{Vec, Box};
use rstd::convert::TryFrom;
/// A type of supported crypto.
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
#[repr(C)]
pub enum StorageKind {
@@ -50,15 +52,21 @@ impl TryFrom<u32> for StorageKind {
}
}
impl From<StorageKind> for u32 {
fn from(c: StorageKind) -> Self {
c as u8 as u32
}
}
/// A type of supported crypto.
#[derive(Clone, Copy, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
#[repr(C)]
pub enum CryptoKind {
/// SR25519 crypto (Schnorrkel)
Sr25519 = 1,
Sr25519 = crypto::key_types::SR25519 as isize,
/// ED25519 crypto (Edwards)
Ed25519 = 2,
Ed25519 = crypto::key_types::ED25519 as isize,
}
impl TryFrom<u32> for CryptoKind {
@@ -66,23 +74,41 @@ impl TryFrom<u32> for CryptoKind {
fn try_from(kind: u32) -> Result<Self, Self::Error> {
match kind {
e if e == u32::from(CryptoKind::Sr25519 as u8) => Ok(CryptoKind::Sr25519),
e if e == u32::from(CryptoKind::Ed25519 as u8) => Ok(CryptoKind::Ed25519),
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
}
}
/// Opaque type for created crypto keys.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct CryptoKeyId(pub u16);
impl From<CryptoKeyId> for u32 {
fn from(c: CryptoKeyId) -> Self {
c.0 as u32
}
}
/// Opaque type for offchain http requests.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct HttpRequestId(pub u16);
impl From<HttpRequestId> for u32 {
fn from(c: HttpRequestId) -> Self {
c.0 as u32
}
}
/// An error enum returned by some http methods.
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
@@ -106,6 +132,12 @@ impl TryFrom<u32> for HttpError {
}
}
impl From<HttpError> for u32 {
fn from(c: HttpError) -> Self {
c as u8 as u32
}
}
/// Status of the HTTP request
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
@@ -216,31 +248,34 @@ pub trait Externalities {
/// Encrypt a piece of data using given crypto key.
///
/// If `key` is `None`, it will attempt to use current authority 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.
fn encrypt(&mut self, key: Option<CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()>;
/// Returns an error if `key` is not available or does not exist,
/// or the expected `CryptoKind` does not match.
fn encrypt(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, 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.
/// 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.
fn decrypt(&mut self, key: Option<CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()>;
/// 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: Option<CryptoKeyId>, kind: CryptoKind, 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.
/// 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.
fn sign(&mut self, key: Option<CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()>;
/// Returns an error if `key` is not available or does not exist,
/// or the expected `CryptoKind` does not match.
fn sign(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, 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.
fn verify(&mut self, key: Option<CryptoKeyId>, msg: &[u8], signature: &[u8]) -> Result<bool, ()>;
/// lengths are incorrect or `CryptoKind` does not match.
fn verify(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, msg: &[u8], signature: &[u8]) -> Result<bool, ()>;
/// Returns current UNIX timestamp (in millis)
fn timestamp(&mut self) -> Timestamp;
@@ -359,20 +394,20 @@ impl<T: Externalities + ?Sized> Externalities for Box<T> {
(&mut **self).new_crypto_key(crypto)
}
fn encrypt(&mut self, key: Option<CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).encrypt(key, data)
fn encrypt(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).encrypt(key, kind, data)
}
fn decrypt(&mut self, key: Option<CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).decrypt(key, data)
fn decrypt(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).decrypt(key, kind, data)
}
fn sign(&mut self, key: Option<CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).sign(key, data)
fn sign(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).sign(key, kind, data)
}
fn verify(&mut self, key: Option<CryptoKeyId>, msg: &[u8], signature: &[u8]) -> Result<bool, ()> {
(&mut **self).verify(key, msg, signature)
fn verify(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, msg: &[u8], signature: &[u8]) -> Result<bool, ()> {
(&mut **self).verify(key, kind, msg, signature)
}
fn timestamp(&mut self) -> Timestamp {
+1
View File
@@ -379,6 +379,7 @@ fn derive_hard_junction(secret: &SecretKey, cc: &[u8; CHAIN_CODE_LENGTH]) -> Sec
secret.hard_derive_mini_secret_key(Some(ChainCode(cc.clone())), b"").0.expand()
}
/// The raw secret seed, which can be used to recreate the `Pair`.
#[cfg(feature = "std")]
type Seed = [u8; MINI_SECRET_KEY_LENGTH];