mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 00:01:09 +00:00
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:
@@ -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[..]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user