Add SECP256k1/ECDSA support for transaction signing (#3861)

* Add SECP256k1/ECDSA support for transaction signing.

* Refactoring and fixes

* Fix for contracts

* Avoid breaking runtime host function

* Build fixes, make subkey work more generaically.

* Fix tests

* Dedpulicate a bit of code, remove unneeded code, docs

* Bump runtime version

* Fix a test and clean up some code.

* Derivation can derive seed.

* Whitespace

* Bump runtime again.

* Update core/primitives/src/crypto.rs

Co-Authored-By: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update core/primitives/src/ecdsa.rs

Co-Authored-By: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Fix AppVerify
This commit is contained in:
Gavin Wood
2019-10-24 10:59:09 +02:00
committed by GitHub
parent 62a238a81b
commit d97775542a
30 changed files with 1286 additions and 419 deletions
@@ -21,7 +21,7 @@ use rstd::fmt;
use runtime_io::blake2_256;
use codec::{Decode, Encode, EncodeLike, Input, Error};
use crate::{
traits::{self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic},
traits::{self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic, IdentifyAccount},
generic::CheckedExtrinsic, transaction_validity::{TransactionValidityError, InvalidTransaction},
};
@@ -98,7 +98,8 @@ for
where
Address: Member + MaybeDisplay,
Call: Encode + Member,
Signature: Member + traits::Verify<Signer=AccountId>,
Signature: Member + traits::Verify,
<Signature as traits::Verify>::Signer: IdentifyAccount<AccountId=AccountId>,
Extra: SignedExtension<AccountId=AccountId>,
AccountId: Member + MaybeDisplay,
Lookup: traits::Lookup<Source=Address, Target=AccountId>,
@@ -284,17 +285,26 @@ mod tests {
use super::*;
use runtime_io::blake2_256;
use crate::codec::{Encode, Decode};
use crate::traits::{SignedExtension, IdentityLookup};
use crate::traits::{SignedExtension, IdentifyAccount, IdentityLookup};
use serde::{Serialize, Deserialize};
type TestContext = IdentityLookup<u64>;
#[derive(Eq, PartialEq, Clone, Copy, Debug, Serialize, Deserialize, Encode, Decode)]
pub struct TestSigner(pub u64);
impl From<u64> for TestSigner { fn from(x: u64) -> Self { Self(x) } }
impl From<TestSigner> for u64 { fn from(x: TestSigner) -> Self { x.0 } }
impl IdentifyAccount for TestSigner {
type AccountId = u64;
fn into_account(self) -> u64 { self.into() }
}
#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Encode, Decode)]
struct TestSig(u64, Vec<u8>);
impl traits::Verify for TestSig {
type Signer = u64;
fn verify<L: traits::Lazy<[u8]>>(&self, mut msg: L, signer: &Self::Signer) -> bool {
*signer == self.0 && msg.get() == &self.1[..]
type Signer = TestSigner;
fn verify<L: traits::Lazy<[u8]>>(&self, mut msg: L, signer: &u64) -> bool {
signer == &self.0 && msg.get() == &self.1[..]
}
}
+49 -11
View File
@@ -42,7 +42,7 @@ pub use runtime_io::{StorageOverlay, ChildrenStorageOverlay};
use rstd::prelude::*;
use rstd::convert::TryFrom;
use primitives::{crypto, ed25519, sr25519, hash::{H256, H512}};
use primitives::{crypto, ed25519, sr25519, ecdsa, hash::{H256, H512}};
use codec::{Encode, Decode};
#[cfg(feature = "std")]
@@ -59,7 +59,7 @@ pub mod weights;
pub use generic::{DigestItem, Digest};
/// Re-export this since it's part of the API of this crate.
pub use primitives::{TypeId, crypto::{key_types, KeyTypeId, CryptoType}};
pub use primitives::{TypeId, crypto::{key_types, KeyTypeId, CryptoType, AccountId32}};
pub use app_crypto::RuntimeAppPublic;
/// Re-export `RuntimeDebug`, to avoid dependency clutter.
@@ -117,6 +117,7 @@ macro_rules! create_runtime_str {
#[cfg(feature = "std")]
pub use serde::{Serialize, Deserialize, de::DeserializeOwned};
use crate::traits::IdentifyAccount;
/// Complex storage builder stuff.
#[cfg(feature = "std")]
@@ -175,6 +176,8 @@ pub enum MultiSignature {
Ed25519(ed25519::Signature),
/// An Sr25519 signature.
Sr25519(sr25519::Signature),
/// An ECDSA/SECP256k1 signature.
Ecdsa(ecdsa::Signature),
}
impl From<ed25519::Signature> for MultiSignature {
@@ -189,6 +192,12 @@ impl From<sr25519::Signature> for MultiSignature {
}
}
impl From<ecdsa::Signature> for MultiSignature {
fn from(x: ecdsa::Signature) -> Self {
MultiSignature::Ecdsa(x)
}
}
impl Default for MultiSignature {
fn default() -> Self {
MultiSignature::Ed25519(Default::default())
@@ -203,6 +212,8 @@ pub enum MultiSigner {
Ed25519(ed25519::Public),
/// An Sr25519 identity.
Sr25519(sr25519::Public),
/// An SECP256k1/ECDSA identity (actually, the Blake2 hash of the pub key).
Ecdsa(ecdsa::Public),
}
impl Default for MultiSigner {
@@ -224,6 +235,18 @@ impl AsRef<[u8]> for MultiSigner {
match *self {
MultiSigner::Ed25519(ref who) => who.as_ref(),
MultiSigner::Sr25519(ref who) => who.as_ref(),
MultiSigner::Ecdsa(ref who) => who.as_ref(),
}
}
}
impl traits::IdentifyAccount for MultiSigner {
type AccountId = AccountId32;
fn into_account(self) -> AccountId32 {
match self {
MultiSigner::Ed25519(who) => <[u8; 32]>::from(who).into(),
MultiSigner::Sr25519(who) => <[u8; 32]>::from(who).into(),
MultiSigner::Ecdsa(who) => runtime_io::blake2_256(who.as_ref()).into(),
}
}
}
@@ -240,23 +263,37 @@ impl From<sr25519::Public> for MultiSigner {
}
}
#[cfg(feature = "std")]
impl From<ecdsa::Public> for MultiSigner {
fn from(x: ecdsa::Public) -> Self {
MultiSigner::Ecdsa(x)
}
}
#[cfg(feature = "std")]
impl std::fmt::Display for MultiSigner {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
MultiSigner::Ed25519(ref who) => write!(fmt, "ed25519: {}", who),
MultiSigner::Sr25519(ref who) => write!(fmt, "sr25519: {}", who),
MultiSigner::Ecdsa(ref who) => write!(fmt, "ecdsa: {}", who),
}
}
}
impl Verify for MultiSignature {
type Signer = MultiSigner;
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &Self::Signer) -> bool {
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &AccountId32) -> bool {
use primitives::crypto::Public;
match (self, signer) {
(MultiSignature::Ed25519(ref sig), &MultiSigner::Ed25519(ref who)) => sig.verify(msg, who),
(MultiSignature::Sr25519(ref sig), &MultiSigner::Sr25519(ref who)) => sig.verify(msg, who),
_ => false,
(MultiSignature::Ed25519(ref sig), who) => sig.verify(msg, &ed25519::Public::from_slice(who.as_ref())),
(MultiSignature::Sr25519(ref sig), who) => sig.verify(msg, &sr25519::Public::from_slice(who.as_ref())),
(MultiSignature::Ecdsa(ref sig), who) => {
let m = runtime_io::blake2_256(msg.get());
match runtime_io::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) {
Ok(pubkey) => &runtime_io::blake2_256(pubkey.as_ref()) == <dyn AsRef<[u8; 32]>>::as_ref(who),
_ => false,
}
}
}
}
}
@@ -269,12 +306,13 @@ pub struct AnySignature(H512);
impl Verify for AnySignature {
type Signer = sr25519::Public;
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &sr25519::Public) -> bool {
use primitives::crypto::Public;
let msg = msg.get();
sr25519::Signature::try_from(self.0.as_fixed_bytes().as_ref())
.map(|s| runtime_io::sr25519_verify(&s, msg.get(), &signer))
.map(|s| s.verify(msg, signer))
.unwrap_or(false)
|| ed25519::Signature::try_from(self.0.as_fixed_bytes().as_ref())
.and_then(|s| ed25519::Public::try_from(signer.0.as_ref()).map(|p| (s, p)))
.map(|(s, p)| runtime_io::ed25519_verify(&s, msg.get(), &p))
.map(|s| s.verify(msg, &ed25519::Public::from_slice(signer.as_ref())))
.unwrap_or(false)
}
}
@@ -398,7 +436,7 @@ impl From<&'static str> for DispatchError {
/// Verify a signature on an encoded value in a lazy manner. This can be
/// an optimization if the signature scheme has an "unsigned" escape hash.
pub fn verify_encoded_lazy<V: Verify, T: codec::Encode>(sig: &V, item: &T, signer: &V::Signer) -> bool {
pub fn verify_encoded_lazy<V: Verify, T: codec::Encode>(sig: &V, item: &T, signer: &<V::Signer as IdentifyAccount>::AccountId) -> bool {
// The `Lazy<T>` trait expresses something like `X: FnMut<Output = for<'a> &'a T>`.
// unfortunately this is a lifetime relationship that can't
// be expressed without generic associated types, better unification of HRTBs in type position,
+63 -10
View File
@@ -50,46 +50,84 @@ impl<'a> Lazy<[u8]> for &'a [u8] {
fn get(&mut self) -> &[u8] { &**self }
}
/// Some type that is able to be collapsed into an account ID. It is not possible to recreate the original value from
/// the account ID.
pub trait IdentifyAccount {
/// The account ID that this can be transformed into.
type AccountId;
/// Transform into an account.
fn into_account(self) -> Self::AccountId;
}
impl IdentifyAccount for primitives::ed25519::Public {
type AccountId = Self;
fn into_account(self) -> Self { self }
}
impl IdentifyAccount for primitives::sr25519::Public {
type AccountId = Self;
fn into_account(self) -> Self { self }
}
impl IdentifyAccount for primitives::ecdsa::Public {
type AccountId = Self;
fn into_account(self) -> Self { self }
}
/// Means of signature verification.
pub trait Verify {
/// Type of the signer.
type Signer;
type Signer: IdentifyAccount;
/// Verify a signature. Return `true` if signature is valid for the value.
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &Self::Signer) -> bool;
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &<Self::Signer as IdentifyAccount>::AccountId) -> bool;
}
impl Verify for primitives::ed25519::Signature {
type Signer = primitives::ed25519::Public;
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &Self::Signer) -> bool {
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &primitives::ed25519::Public) -> bool {
runtime_io::ed25519_verify(self, msg.get(), signer)
}
}
impl Verify for primitives::sr25519::Signature {
type Signer = primitives::sr25519::Public;
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &Self::Signer) -> bool {
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &primitives::sr25519::Public) -> bool {
runtime_io::sr25519_verify(self, msg.get(), signer)
}
}
impl Verify for primitives::ecdsa::Signature {
type Signer = primitives::ecdsa::Public;
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &primitives::ecdsa::Public) -> bool {
match runtime_io::secp256k1_ecdsa_recover_compressed(self.as_ref(), &runtime_io::blake2_256(msg.get())) {
Ok(pubkey) => <dyn AsRef<[u8]>>::as_ref(signer) == &pubkey[..],
_ => false,
}
}
}
/// Means of signature verification of an application key.
pub trait AppVerify {
/// Type of the signer.
type Signer;
type AccountId;
/// Verify a signature. Return `true` if signature is valid for the value.
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &Self::Signer) -> bool;
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &Self::AccountId) -> bool;
}
impl<
S: Verify<Signer=<<T as AppKey>::Public as app_crypto::AppPublic>::Generic> + From<T>,
T: app_crypto::Wraps<Inner=S> + app_crypto::AppKey + app_crypto::AppSignature +
AsRef<S> + AsMut<S> + From<S>,
> AppVerify for T {
type Signer = <T as AppKey>::Public;
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &Self::Signer) -> bool {
> AppVerify for T where
<S as Verify>::Signer: IdentifyAccount<AccountId = <S as Verify>::Signer>,
<<T as AppKey>::Public as app_crypto::AppPublic>::Generic:
IdentifyAccount<AccountId = <<T as AppKey>::Public as app_crypto::AppPublic>::Generic>,
{
type AccountId = <T as AppKey>::Public;
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &<T as AppKey>::Public) -> bool {
use app_crypto::IsWrappedBy;
let inner: &S = self.as_ref();
let inner_pubkey = <Self::Signer as app_crypto::AppPublic>::Generic::from_ref(&signer);
let inner_pubkey = <<T as AppKey>::Public as app_crypto::AppPublic>::Generic::from_ref(&signer);
Verify::verify(inner, msg, inner_pubkey)
}
}
@@ -1179,6 +1217,21 @@ mod tests {
use super::AccountIdConversion;
use crate::codec::{Encode, Decode, Input};
mod t {
use primitives::crypto::KeyTypeId;
use app_crypto::{app_crypto, sr25519};
app_crypto!(sr25519, KeyTypeId(*b"test"));
}
#[test]
fn app_verify_works() {
use t::*;
use super::AppVerify;
let s = Signature::default();
let _ = s.verify(&[0u8; 100][..], &Public::default());
}
#[derive(Encode, Decode, Default, PartialEq, Debug)]
struct U32Value(u32);
impl super::TypeId for U32Value {