Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Added RuntimePublic for ecdsa public key. #6029

Merged
7 commits merged into from
May 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion client/keystore/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use sp_core::{
traits::{BareCryptoStore, BareCryptoStoreError as TraitError},
Encode,
};
use sp_application_crypto::{AppKey, AppPublic, AppPair, ed25519, sr25519};
use sp_application_crypto::{AppKey, AppPublic, AppPair, ed25519, sr25519, ecdsa};
use parking_lot::RwLock;

/// Keystore pointer
Expand Down Expand Up @@ -308,6 +308,7 @@ impl BareCryptoStore for Store {
.fold(Vec::new(), |mut v, k| {
v.push(CryptoTypePublicPair(sr25519::CRYPTO_ID, k.clone()));
v.push(CryptoTypePublicPair(ed25519::CRYPTO_ID, k.clone()));
v.push(CryptoTypePublicPair(ecdsa::CRYPTO_ID, k.clone()));
v
}))
}
Expand Down Expand Up @@ -343,6 +344,13 @@ impl BareCryptoStore for Store {
.key_pair_by_type::<sr25519::Pair>(&pub_key, id)
.map_err(|e| TraitError::from(e))?;
Ok(key_pair.sign(msg).encode())
},
ecdsa::CRYPTO_ID => {
let pub_key = ecdsa::Public::from_slice(key.1.as_slice());
let key_pair: ecdsa::Pair = self
.key_pair_by_type::<ecdsa::Pair>(&pub_key, id)
.map_err(|e| TraitError::from(e))?;
Ok(key_pair.sign(msg).encode())
}
_ => Err(TraitError::KeyNotSupported(id))
}
Expand Down Expand Up @@ -394,6 +402,29 @@ impl BareCryptoStore for Store {
Ok(pair.public())
}

fn ecdsa_public_keys(&self, key_type: KeyTypeId) -> Vec<ecdsa::Public> {
self.raw_public_keys(key_type)
.map(|v| {
v.into_iter()
.map(|k| ecdsa::Public::from_slice(k.as_slice()))
.collect()
})
.unwrap_or_default()
}

fn ecdsa_generate_new(
&mut self,
id: KeyTypeId,
seed: Option<&str>,
) -> std::result::Result<ecdsa::Public, TraitError> {
let pair = match seed {
Some(seed) => self.insert_ephemeral_from_seed_by_type::<ecdsa::Pair>(seed, id),
None => self.generate_by_type::<ecdsa::Pair>(id),
}.map_err(|e| -> TraitError { e.into() })?;

Ok(pair.public())
}

fn insert_unknown(&mut self, key_type: KeyTypeId, suri: &str, public: &[u8])
-> std::result::Result<(), ()>
{
Expand Down
61 changes: 61 additions & 0 deletions primitives/application-crypto/src/ecdsa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2019-2020 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/>.

//! Ecdsa crypto types.

use crate::{RuntimePublic, KeyTypeId};

use sp_std::vec::Vec;

pub use sp_core::ecdsa::*;

mod app {
use sp_core::testing::ECDSA;

crate::app_crypto!(super, ECDSA);

impl crate::traits::BoundToRuntimeAppPublic for Public {
type Public = Self;
}
}

pub use app::{Public as AppPublic, Signature as AppSignature};
#[cfg(feature = "full_crypto")]
pub use app::Pair as AppPair;

impl RuntimePublic for Public {
type Signature = Signature;

fn all(key_type: KeyTypeId) -> crate::Vec<Self> {
sp_io::crypto::ecdsa_public_keys(key_type)
}

fn generate_pair(key_type: KeyTypeId, seed: Option<Vec<u8>>) -> Self {
sp_io::crypto::ecdsa_generate(key_type, seed)
}

fn sign<M: AsRef<[u8]>>(&self, key_type: KeyTypeId, msg: &M) -> Option<Self::Signature> {
sp_io::crypto::ecdsa_sign(key_type, self, msg.as_ref())
}

fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool {
sp_io::crypto::ecdsa_verify(&signature, msg.as_ref(), self)
}

fn to_raw_vec(&self) -> Vec<u8> {
sp_core::crypto::Public::to_raw_vec(self)
}
}
1 change: 1 addition & 0 deletions primitives/application-crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub use sp_std::{ops::Deref, vec::Vec};

pub mod ed25519;
pub mod sr25519;
pub mod ecdsa;
mod traits;

pub use traits::*;
Expand Down
42 changes: 42 additions & 0 deletions primitives/application-crypto/test/src/ecdsa.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2019-2020 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/>.

//! Integration tests for ecdsa

use sp_runtime::generic::BlockId;
use sp_core::{
crypto::Pair,
testing::{KeyStore, ECDSA},
};
use substrate_test_runtime_client::{
TestClientBuilder, DefaultTestClientBuilderExt, TestClientBuilderExt,
runtime::TestAPI,
};
use sp_api::ProvideRuntimeApi;
use sp_application_crypto::ecdsa::{AppPair, AppPublic};

#[test]
fn ecdsa_works_in_runtime() {
let keystore = KeyStore::new();
let test_client = TestClientBuilder::new().set_keystore(keystore.clone()).build();
let (signature, public) = test_client.runtime_api()
.test_ecdsa_crypto(&BlockId::Number(0))
.expect("Tests `ecdsa` crypto.");

let supported_keys = keystore.read().keys(ECDSA).unwrap();
assert!(supported_keys.contains(&public.clone().into()));
assert!(AppPair::verify(&signature, "ecdsa", &AppPublic::from(public)));
}
4 changes: 3 additions & 1 deletion primitives/application-crypto/test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@
#[cfg(test)]
mod ed25519;
#[cfg(test)]
mod sr25519;
mod sr25519;
#[cfg(test)]
mod ecdsa;
37 changes: 30 additions & 7 deletions primitives/core/src/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use crate::crypto::Ss58Codec;
#[cfg(feature = "std")]
use serde::{de, Serializer, Serialize, Deserializer, Deserialize};
use crate::crypto::{Public as TraitPublic, CryptoTypePublicPair, UncheckedFrom, CryptoType, Derive, CryptoTypeId};
use sp_runtime_interface::pass_by::PassByInner;
#[cfg(feature = "full_crypto")]
use secp256k1::{PublicKey, SecretKey};

Expand All @@ -50,7 +51,7 @@ pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"ecds");
type Seed = [u8; 32];

/// The ECDSA compressed public key.
#[derive(Clone, Encode, Decode)]
#[derive(Clone, Encode, Decode, PassByInner)]
pub struct Public([u8; 33]);

impl PartialOrd for Public {
Expand Down Expand Up @@ -124,6 +125,18 @@ impl TraitPublic for Public {
}
}

impl From<Public> for CryptoTypePublicPair {
fn from(key: Public) -> Self {
(&key).into()
}
}

impl From<&Public> for CryptoTypePublicPair {
fn from(key: &Public) -> Self {
CryptoTypePublicPair(CRYPTO_ID, key.to_raw_vec())
}
}

impl Derive for Public {}

impl Default for Public {
Expand Down Expand Up @@ -177,12 +190,17 @@ impl std::fmt::Display for Public {
}
}

#[cfg(feature = "std")]
impl std::fmt::Debug for Public {
impl sp_std::fmt::Debug for Public {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let s = self.to_ss58check();
write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.as_ref()), &s[0..8])
}

#[cfg(not(feature = "std"))]
fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
Ok(())
}
}

#[cfg(feature = "std")]
Expand All @@ -208,7 +226,7 @@ impl sp_std::hash::Hash for Public {
}

/// A signature (a 512-bit value, plus 8 bits for recovery ID).
#[derive(Encode, Decode)]
#[derive(Encode, Decode, PassByInner)]
pub struct Signature([u8; 65]);

impl sp_std::convert::TryFrom<&[u8]> for Signature {
Expand Down Expand Up @@ -288,11 +306,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 sp_std::fmt::Debug for Signature {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.0))
}

#[cfg(not(feature = "std"))]
fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
Ok(())
}
}

#[cfg(feature = "full_crypto")]
Expand Down
50 changes: 49 additions & 1 deletion primitives/core/src/testing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::crypto::KeyTypeId;
#[cfg(feature = "std")]
use crate::{
crypto::{Pair, Public, CryptoTypePublicPair},
ed25519, sr25519,
ed25519, sr25519, ecdsa,
traits::BareCryptoStoreError
};
#[cfg(feature = "std")]
Expand All @@ -29,6 +29,8 @@ use std::collections::HashSet;
pub const ED25519: KeyTypeId = KeyTypeId(*b"ed25");
/// Key type for generic Sr 25519 key.
pub const SR25519: KeyTypeId = KeyTypeId(*b"sr25");
/// Key type for generic Sr 25519 key.
pub const ECDSA: KeyTypeId = KeyTypeId(*b"ecds");

/// A keystore implementation usable in tests.
#[cfg(feature = "std")]
Expand Down Expand Up @@ -61,6 +63,14 @@ impl KeyStore {
)
}

fn ecdsa_key_pair(&self, id: KeyTypeId, pub_key: &ecdsa::Public) -> Option<ecdsa::Pair> {
self.keys.get(&id)
.and_then(|inner|
inner.get(pub_key.as_slice())
.map(|s| ecdsa::Pair::from_string(s, None).expect("`ecdsa` seed slice is valid"))
)
}

}

#[cfg(feature = "std")]
Expand All @@ -73,6 +83,7 @@ impl crate::traits::BareCryptoStore for KeyStore {
.fold(Vec::new(), |mut v, k| {
v.push(CryptoTypePublicPair(sr25519::CRYPTO_ID, k.clone()));
v.push(CryptoTypePublicPair(ed25519::CRYPTO_ID, k.clone()));
v.push(CryptoTypePublicPair(ecdsa::CRYPTO_ID, k.clone()));
v
}))
})
Expand Down Expand Up @@ -141,6 +152,37 @@ impl crate::traits::BareCryptoStore for KeyStore {
}
}

fn ecdsa_public_keys(&self, id: KeyTypeId) -> Vec<ecdsa::Public> {
self.keys.get(&id)
.map(|keys|
keys.values()
.map(|s| ecdsa::Pair::from_string(s, None).expect("`ecdsa` seed slice is valid"))
.map(|p| p.public())
.collect()
)
.unwrap_or_default()
}

fn ecdsa_generate_new(
&mut self,
id: KeyTypeId,
seed: Option<&str>,
) -> Result<ecdsa::Public, BareCryptoStoreError> {
match seed {
Some(seed) => {
let pair = ecdsa::Pair::from_string(seed, None)
.map_err(|_| BareCryptoStoreError::ValidationError("Generates an `ecdsa` pair.".to_owned()))?;
self.keys.entry(id).or_default().insert(pair.public().to_raw_vec(), seed.into());
Ok(pair.public())
},
None => {
let (pair, phrase, _) = ecdsa::Pair::generate_with_phrase(None);
self.keys.entry(id).or_default().insert(pair.public().to_raw_vec(), phrase);
Ok(pair.public())
}
}
}

fn insert_unknown(&mut self, id: KeyTypeId, suri: &str, public: &[u8]) -> Result<(), ()> {
self.keys.entry(id).or_default().insert(public.to_owned(), suri.to_string());
Ok(())
Expand Down Expand Up @@ -186,6 +228,12 @@ impl crate::traits::BareCryptoStore for KeyStore {
.ok_or(BareCryptoStoreError::PairNotFound("sr25519".to_owned()))?;
return Ok(key_pair.sign(msg).encode());
}
ecdsa::CRYPTO_ID => {
let key_pair: ecdsa::Pair = self
.ecdsa_key_pair(id, &ecdsa::Public::from_slice(key.1.as_slice()))
.ok_or(BareCryptoStoreError::PairNotFound("ecdsa".to_owned()))?;
return Ok(key_pair.sign(msg).encode());
}
_ => Err(BareCryptoStoreError::KeyNotSupported(id))
}
}
Expand Down
14 changes: 13 additions & 1 deletion primitives/core/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

use crate::{
crypto::{KeyTypeId, CryptoTypePublicPair},
ed25519, sr25519,
ed25519, sr25519, ecdsa,
};

use std::{
Expand Down Expand Up @@ -71,6 +71,18 @@ pub trait BareCryptoStore: Send + Sync {
id: KeyTypeId,
seed: Option<&str>,
) -> Result<ed25519::Public, BareCryptoStoreError>;
/// Returns all ecdsa public keys for the given key type.
fn ecdsa_public_keys(&self, id: KeyTypeId) -> Vec<ecdsa::Public>;
/// Generate a new ecdsa 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 ecdsa_generate_new(
&mut self,
id: KeyTypeId,
seed: Option<&str>,
) -> Result<ecdsa::Public, BareCryptoStoreError>;

/// Insert a new key. This doesn't require any known of the crypto; but a public key must be
/// manually provided.
Expand Down
Loading