mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 13:31:10 +00:00
Introduce sign_with method in keystore (#4925)
* Add KEY_KIND_ID to the public trait This change is being introduced for the purpose of identifying a public key with it's identifier and algorithm "kind". * Use `sign_with` as implemented in BareCryptoStore * Implement `sign_with` in sc_keystore * Fix inconsistencies, use *_KIND_ID in sp_core testing * Rename KeyKindId to CryptoTypeId * Remove pair-returning functions from BareCryptoStore trait * Define CryptoTypeId in app-crypto macros * Add functions to get keys supported by keystore * Fix sign_with signature to include CryptoTypePublicPair * Add `sign_with_any` and `sign_with_all` * Use keystore.sign_with in auth_discovery * Rename get_supported_keys -> supported_keys * Added headers to function docstrings * Use chain instead of extending a temp vector * Fixed some code formatting * Restrict size of CryptoTypeId This is to be able to use Encode/Decode derives and the overcome having the size being unknown at compile-time. * Implement sign_with in the trait itself * Remove whitespace * Use key_type also as a CryptoTypeId in app_crypto macros * Rename `get_keys` to `keys` in BareCryptoStore * Remove usage of key_pair funcs in tests * Adjust docstring for *_CYPTO_ID constants * Fix failures * Simplify mapping on keys * Remove one let * Fixed typo * PR feedback * remove whitespace * Zip keys and signatures * Use into_iter & remove cloned * Pass index to MissingSignature * Use typed errors instead of strings for BareCryptoStore * Implement Debug for trait error * Use hashsets for better performance for supported_keys * Make sure keys are inserted into the keystore * Make sign_with_all return type consistent with `sign_with` * Rename Error to BareCryptoStoreError * Rename CRYPT_TYPE_ID -> CRYPTO_ID * Remove unnecessary CRYPTO_ID declaration in Public trait * Convert pub key to CryptoTypePublicPair * Fix use * Fix code style * Implement From on CryptoTypePublicPair in app_crypto macros * Change CryptoTypePublicPair to a struct * Implement Display on CryptoTypePublicPair * Pass CryptoTypePublicPair to MissingSignature error * Adjust docs according to function signature * Unify keys implementation * Fix RPC author tests * Fix stackoverflow * Tabify spaces * Pass KeyTypeId to error for easier debugging * Fix asserts * Use ToHex to format public key * Use constants from sp_core * Rename testing KeyTypeId constants * Please compiler * Restore KeyTypeId names apparently, they're not only used in tests * Use BareCryptoStoreError instead of String * Document return value * Fix borrow check * Convert to hashset internally * WIP - iter_keys * Return raw_public_keys * Address PR feedback * Address PR Feedback * Fix hexdisplay import error * Update primitives/core/src/traits.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
|
||||
//! Authority discovery errors.
|
||||
|
||||
use sp_core::crypto::CryptoTypePublicPair;
|
||||
|
||||
/// AuthorityDiscovery Result.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
@@ -46,6 +48,10 @@ pub enum Error {
|
||||
EncodingDecodingScale(codec::Error),
|
||||
/// Failed to parse a libp2p multi address.
|
||||
ParsingMultiaddress(libp2p::core::multiaddr::Error),
|
||||
/// Failed to sign using a specific public key
|
||||
MissingSignature(CryptoTypePublicPair),
|
||||
/// Failed to sign using all public keys
|
||||
Signing,
|
||||
/// Failed to register Prometheus metric.
|
||||
Prometheus(prometheus_endpoint::PrometheusError),
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ use prost::Message;
|
||||
use sc_client_api::blockchain::HeaderBackend;
|
||||
use sc_network::{DhtEvent, ExHashT, NetworkStateInfo};
|
||||
use sp_authority_discovery::{AuthorityDiscoveryApi, AuthorityId, AuthoritySignature, AuthorityPair};
|
||||
use sp_core::crypto::{key_types, Pair};
|
||||
use sp_core::crypto::{key_types, CryptoTypePublicPair, Pair};
|
||||
use sp_core::traits::BareCryptoStorePtr;
|
||||
use sp_runtime::{traits::Block as BlockT, generic::BlockId};
|
||||
use sp_api::ProvideRuntimeApi;
|
||||
@@ -283,19 +283,36 @@ where
|
||||
.encode(&mut serialized_addresses)
|
||||
.map_err(Error::EncodingProto)?;
|
||||
|
||||
for key in self.get_priv_keys_within_authority_set()?.into_iter() {
|
||||
let signature = key.sign(&serialized_addresses);
|
||||
let keys: Vec<CryptoTypePublicPair> = self.get_own_public_keys_within_authority_set()?
|
||||
.into_iter()
|
||||
.map(Into::into)
|
||||
.collect();
|
||||
|
||||
let signatures = self.key_store
|
||||
.read()
|
||||
.sign_with_all(
|
||||
key_types::AUTHORITY_DISCOVERY,
|
||||
keys.clone(),
|
||||
serialized_addresses.as_slice(),
|
||||
)
|
||||
.map_err(|_| Error::Signing)?;
|
||||
|
||||
for (sign_result, key) in signatures.iter().zip(keys) {
|
||||
let mut signed_addresses = vec![];
|
||||
|
||||
// sign_with_all returns Result<Signature, Error> signature
|
||||
// is generated for a public key that is supported.
|
||||
// Verify that all signatures exist for all provided keys.
|
||||
let signature = sign_result.as_ref().map_err(|_| Error::MissingSignature(key.clone()))?;
|
||||
schema::SignedAuthorityAddresses {
|
||||
addresses: serialized_addresses.clone(),
|
||||
signature: signature.encode(),
|
||||
signature: Encode::encode(&signature),
|
||||
}
|
||||
.encode(&mut signed_addresses)
|
||||
.map_err(Error::EncodingProto)?;
|
||||
|
||||
self.network.put_value(
|
||||
hash_authority_id(key.public().as_ref())?,
|
||||
hash_authority_id(key.1.as_ref())?,
|
||||
signed_addresses,
|
||||
);
|
||||
}
|
||||
@@ -446,21 +463,6 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Retrieve all local authority discovery private keys that are within the current authority
|
||||
/// set.
|
||||
fn get_priv_keys_within_authority_set(&mut self) -> Result<Vec<AuthorityPair>> {
|
||||
let keys = self.get_own_public_keys_within_authority_set()?
|
||||
.into_iter()
|
||||
.map(std::convert::Into::into)
|
||||
.filter_map(|pub_key| {
|
||||
self.key_store.read().sr25519_key_pair(key_types::AUTHORITY_DISCOVERY, &pub_key)
|
||||
})
|
||||
.map(std::convert::Into::into)
|
||||
.collect();
|
||||
|
||||
Ok(keys)
|
||||
}
|
||||
|
||||
/// Retrieve our public keys within the current authority set.
|
||||
//
|
||||
// A node might have multiple authority discovery keys within its keystore, e.g. an old one and
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
//! Keystore (and session key management) for ed25519 based chains like Polkadot.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use std::{collections::HashMap, path::PathBuf, fs::{self, File}, io::{self, Write}, sync::Arc};
|
||||
use std::{collections::{HashMap, HashSet}, path::PathBuf, fs::{self, File}, io::{self, Write}, sync::Arc};
|
||||
use sp_core::{
|
||||
crypto::{KeyTypeId, Pair as PairT, Public, IsWrappedBy, Protected}, traits::BareCryptoStore,
|
||||
crypto::{IsWrappedBy, CryptoTypePublicPair, KeyTypeId, Pair as PairT, Protected, Public},
|
||||
traits::{BareCryptoStore, BareCryptoStoreError as TraitError},
|
||||
Encode,
|
||||
};
|
||||
use sp_application_crypto::{AppKey, AppPublic, AppPair, ed25519, sr25519};
|
||||
use parking_lot::RwLock;
|
||||
@@ -44,6 +45,12 @@ pub enum Error {
|
||||
/// Invalid seed
|
||||
#[display(fmt="Invalid seed")]
|
||||
InvalidSeed,
|
||||
/// Public key type is not supported
|
||||
#[display(fmt="Key crypto type is not supported")]
|
||||
KeyNotSupported(KeyTypeId),
|
||||
/// Pair not found for public key and KeyTypeId
|
||||
#[display(fmt="Pair not found for {} public key", "_0")]
|
||||
PairNotFound(String),
|
||||
/// Keystore unavailable
|
||||
#[display(fmt="Keystore unavailable")]
|
||||
Unavailable,
|
||||
@@ -52,6 +59,21 @@ pub enum Error {
|
||||
/// Keystore Result
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl From<Error> for TraitError {
|
||||
fn from(error: Error) -> Self {
|
||||
match error {
|
||||
Error::KeyNotSupported(id) => TraitError::KeyNotSupported(id),
|
||||
Error::PairNotFound(e) => TraitError::PairNotFound(e),
|
||||
Error::InvalidSeed | Error::InvalidPhrase | Error::InvalidPassword => {
|
||||
TraitError::ValidationError(error.to_string())
|
||||
},
|
||||
Error::Unavailable => TraitError::Unavailable,
|
||||
Error::Io(e) => TraitError::Other(e.to_string()),
|
||||
Error::Json(e) => TraitError::Other(e.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
@@ -220,16 +242,35 @@ impl Store {
|
||||
self.key_pair_by_type::<Pair::Generic>(IsWrappedBy::from_ref(public), Pair::ID).map(Into::into)
|
||||
}
|
||||
|
||||
/// Get public keys of all stored keys that match the given key type.
|
||||
pub fn public_keys_by_type<TPublic: Public>(&self, key_type: KeyTypeId) -> Result<Vec<TPublic>> {
|
||||
let mut public_keys: Vec<TPublic> = self.additional.keys()
|
||||
.filter_map(|(ty, public)| {
|
||||
if *ty == key_type {
|
||||
Some(TPublic::from_slice(public))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
/// Get public keys of all stored keys that match the key type.
|
||||
///
|
||||
/// This will just use the type of the public key (a list of which to be returned) in order
|
||||
/// to determine the key type. Unless you use a specialized application-type public key, then
|
||||
/// this only give you keys registered under generic cryptography, and will not return keys
|
||||
/// registered under the application type.
|
||||
pub fn public_keys<Public: AppPublic>(&self) -> Result<Vec<Public>> {
|
||||
self.raw_public_keys(Public::ID)
|
||||
.map(|v| {
|
||||
v.into_iter()
|
||||
.map(|k| Public::from_slice(k.as_slice()))
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the file path for the given public key and key type.
|
||||
fn key_file_path(&self, public: &[u8], key_type: KeyTypeId) -> Option<PathBuf> {
|
||||
let mut buf = self.path.as_ref()?.clone();
|
||||
let key_type = hex::encode(key_type.0);
|
||||
let key = hex::encode(public);
|
||||
buf.push(key_type + key.as_str());
|
||||
Some(buf)
|
||||
}
|
||||
|
||||
/// Returns a list of raw public keys filtered by `KeyTypeId`
|
||||
fn raw_public_keys(&self, id: KeyTypeId) -> Result<Vec<Vec<u8>>> {
|
||||
let mut public_keys: Vec<Vec<u8>> = self.additional.keys()
|
||||
.into_iter()
|
||||
.filter_map(|k| if k.0 == id { Some(k.1.clone()) } else { None })
|
||||
.collect();
|
||||
|
||||
if let Some(path) = &self.path {
|
||||
@@ -241,8 +282,10 @@ impl Store {
|
||||
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
|
||||
match hex::decode(name) {
|
||||
Ok(ref hex) if hex.len() > 4 => {
|
||||
if &hex[0..4] != &key_type.0 { continue }
|
||||
let public = TPublic::from_slice(&hex[4..]);
|
||||
if &hex[0..4] != &id.0 {
|
||||
continue;
|
||||
}
|
||||
let public = hex[4..].to_vec();
|
||||
public_keys.push(public);
|
||||
}
|
||||
_ => continue,
|
||||
@@ -253,71 +296,104 @@ impl Store {
|
||||
|
||||
Ok(public_keys)
|
||||
}
|
||||
|
||||
/// Get public keys of all stored keys that match the key type.
|
||||
///
|
||||
/// This will just use the type of the public key (a list of which to be returned) in order
|
||||
/// to determine the key type. Unless you use a specialized application-type public key, then
|
||||
/// this only give you keys registered under generic cryptography, and will not return keys
|
||||
/// registered under the application type.
|
||||
pub fn public_keys<Public: AppPublic>(&self) -> Result<Vec<Public>> {
|
||||
self.public_keys_by_type::<Public::Generic>(Public::ID)
|
||||
.map(|v| v.into_iter().map(Into::into).collect())
|
||||
}
|
||||
|
||||
/// Returns the file path for the given public key and key type.
|
||||
fn key_file_path(&self, public: &[u8], key_type: KeyTypeId) -> Option<PathBuf> {
|
||||
let mut buf = self.path.as_ref()?.clone();
|
||||
let key_type = hex::encode(key_type.0);
|
||||
let key = hex::encode(public);
|
||||
buf.push(key_type + key.as_str());
|
||||
Some(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl BareCryptoStore for Store {
|
||||
fn keys(
|
||||
&self,
|
||||
id: KeyTypeId
|
||||
) -> std::result::Result<Vec<CryptoTypePublicPair>, TraitError> {
|
||||
let raw_keys = self.raw_public_keys(id)?;
|
||||
Ok(raw_keys.into_iter()
|
||||
.fold(Vec::new(), |mut v, k| {
|
||||
v.push(CryptoTypePublicPair(sr25519::CRYPTO_ID, k.clone()));
|
||||
v.push(CryptoTypePublicPair(ed25519::CRYPTO_ID, k.clone()));
|
||||
v
|
||||
}))
|
||||
}
|
||||
|
||||
fn supported_keys(
|
||||
&self,
|
||||
id: KeyTypeId,
|
||||
keys: Vec<CryptoTypePublicPair>
|
||||
) -> std::result::Result<Vec<CryptoTypePublicPair>, TraitError> {
|
||||
let all_keys = self.keys(id)?.into_iter().collect::<HashSet<_>>();
|
||||
Ok(keys.into_iter()
|
||||
.filter(|key| all_keys.contains(key))
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
fn sign_with(
|
||||
&self,
|
||||
id: KeyTypeId,
|
||||
key: &CryptoTypePublicPair,
|
||||
msg: &[u8],
|
||||
) -> std::result::Result<Vec<u8>, TraitError> {
|
||||
match key.0 {
|
||||
ed25519::CRYPTO_ID => {
|
||||
let pub_key = ed25519::Public::from_slice(key.1.as_slice());
|
||||
let key_pair: ed25519::Pair = self
|
||||
.key_pair_by_type::<ed25519::Pair>(&pub_key, id)
|
||||
.map_err(|e| TraitError::from(e))?;
|
||||
Ok(key_pair.sign(msg).encode())
|
||||
}
|
||||
sr25519::CRYPTO_ID => {
|
||||
let pub_key = sr25519::Public::from_slice(key.1.as_slice());
|
||||
let key_pair: sr25519::Pair = self
|
||||
.key_pair_by_type::<sr25519::Pair>(&pub_key, id)
|
||||
.map_err(|e| TraitError::from(e))?;
|
||||
Ok(key_pair.sign(msg).encode())
|
||||
}
|
||||
_ => Err(TraitError::KeyNotSupported(id))
|
||||
}
|
||||
}
|
||||
|
||||
fn sr25519_public_keys(&self, key_type: KeyTypeId) -> Vec<sr25519::Public> {
|
||||
self.public_keys_by_type::<sr25519::Public>(key_type).unwrap_or_default()
|
||||
self.raw_public_keys(key_type)
|
||||
.map(|v| {
|
||||
v.into_iter()
|
||||
.map(|k| sr25519::Public::from_slice(k.as_slice()))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn sr25519_generate_new(
|
||||
&mut self,
|
||||
id: KeyTypeId,
|
||||
seed: Option<&str>,
|
||||
) -> std::result::Result<sr25519::Public, String> {
|
||||
) -> std::result::Result<sr25519::Public, TraitError> {
|
||||
let pair = match seed {
|
||||
Some(seed) => self.insert_ephemeral_from_seed_by_type::<sr25519::Pair>(seed, id),
|
||||
None => self.generate_by_type::<sr25519::Pair>(id),
|
||||
}.map_err(|e| e.to_string())?;
|
||||
}.map_err(|e| -> TraitError { e.into() })?;
|
||||
|
||||
Ok(pair.public())
|
||||
}
|
||||
|
||||
fn sr25519_key_pair(&self, id: KeyTypeId, pub_key: &sr25519::Public) -> Option<sr25519::Pair> {
|
||||
self.key_pair_by_type::<sr25519::Pair>(pub_key, id).ok()
|
||||
}
|
||||
|
||||
fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec<ed25519::Public> {
|
||||
self.public_keys_by_type::<ed25519::Public>(key_type).unwrap_or_default()
|
||||
self.raw_public_keys(key_type)
|
||||
.map(|v| {
|
||||
v.into_iter()
|
||||
.map(|k| ed25519::Public::from_slice(k.as_slice()))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn ed25519_generate_new(
|
||||
&mut self,
|
||||
id: KeyTypeId,
|
||||
seed: Option<&str>,
|
||||
) -> std::result::Result<ed25519::Public, String> {
|
||||
) -> std::result::Result<ed25519::Public, TraitError> {
|
||||
let pair = match seed {
|
||||
Some(seed) => self.insert_ephemeral_from_seed_by_type::<ed25519::Pair>(seed, id),
|
||||
None => self.generate_by_type::<ed25519::Pair>(id),
|
||||
}.map_err(|e| e.to_string())?;
|
||||
}.map_err(|e| -> TraitError { e.into() })?;
|
||||
|
||||
Ok(pair.public())
|
||||
}
|
||||
|
||||
fn ed25519_key_pair(&self, id: KeyTypeId, pub_key: &ed25519::Public) -> Option<ed25519::Pair> {
|
||||
self.key_pair_by_type::<ed25519::Pair>(pub_key, id).ok()
|
||||
}
|
||||
|
||||
fn insert_unknown(&mut self, key_type: KeyTypeId, suri: &str, public: &[u8])
|
||||
-> std::result::Result<(), ()>
|
||||
{
|
||||
@@ -337,7 +413,7 @@ impl BareCryptoStore for Store {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tempfile::TempDir;
|
||||
use sp_core::{testing::{SR25519}, crypto::{Ss58Codec}};
|
||||
use sp_core::{testing::SR25519, crypto::Ss58Codec};
|
||||
|
||||
#[test]
|
||||
fn basic_store() {
|
||||
@@ -451,7 +527,7 @@ mod tests {
|
||||
fs::write(file_name, "test").expect("Invalid file is written");
|
||||
|
||||
assert!(
|
||||
store.read().public_keys_by_type::<sr25519::AppPublic>(SR25519).unwrap().is_empty(),
|
||||
store.read().sr25519_public_keys(SR25519).is_empty(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ use assert_matches::assert_matches;
|
||||
use codec::Encode;
|
||||
use sp_core::{
|
||||
H256, blake2_256, hexdisplay::HexDisplay, testing::{ED25519, SR25519, KeyStore},
|
||||
traits::BareCryptoStorePtr, ed25519, crypto::{Pair, Public},
|
||||
traits::BareCryptoStorePtr, ed25519, sr25519,
|
||||
crypto::{CryptoTypePublicPair, Pair, Public},
|
||||
};
|
||||
use rpc::futures::Stream as _;
|
||||
use substrate_test_runtime_client::{
|
||||
@@ -173,7 +174,7 @@ fn should_return_pending_extrinsics() {
|
||||
|
||||
let ex = uxt(AccountKeyring::Alice, 0);
|
||||
AuthorApi::submit_extrinsic(&p, ex.encode().into()).wait().unwrap();
|
||||
assert_matches!(
|
||||
assert_matches!(
|
||||
p.pending_extrinsics(),
|
||||
Ok(ref expected) if *expected == vec![Bytes(ex.encode())]
|
||||
);
|
||||
@@ -199,7 +200,7 @@ fn should_remove_extrinsics() {
|
||||
hash::ExtrinsicOrHash::Extrinsic(ex1.encode().into()),
|
||||
]).unwrap();
|
||||
|
||||
assert_eq!(removed.len(), 3);
|
||||
assert_eq!(removed.len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -215,10 +216,9 @@ fn should_insert_key() {
|
||||
key_pair.public().0.to_vec().into(),
|
||||
).expect("Insert key");
|
||||
|
||||
let store_key_pair = setup.keystore.read()
|
||||
.ed25519_key_pair(ED25519, &key_pair.public()).expect("Key exists in store");
|
||||
let public_keys = setup.keystore.read().keys(ED25519).unwrap();
|
||||
|
||||
assert_eq!(key_pair.public(), store_key_pair.public());
|
||||
assert!(public_keys.contains(&CryptoTypePublicPair(ed25519::CRYPTO_ID, key_pair.public().to_raw_vec())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -231,18 +231,11 @@ fn should_rotate_keys() {
|
||||
let session_keys = SessionKeys::decode(&mut &new_public_keys[..])
|
||||
.expect("SessionKeys decode successfully");
|
||||
|
||||
let ed25519_key_pair = setup.keystore.read().ed25519_key_pair(
|
||||
ED25519,
|
||||
&session_keys.ed25519.clone().into(),
|
||||
).expect("ed25519 key exists in store");
|
||||
let ed25519_public_keys = setup.keystore.read().keys(ED25519).unwrap();
|
||||
let sr25519_public_keys = setup.keystore.read().keys(SR25519).unwrap();
|
||||
|
||||
let sr25519_key_pair = setup.keystore.read().sr25519_key_pair(
|
||||
SR25519,
|
||||
&session_keys.sr25519.clone().into(),
|
||||
).expect("sr25519 key exists in store");
|
||||
|
||||
assert_eq!(session_keys.ed25519, ed25519_key_pair.public().into());
|
||||
assert_eq!(session_keys.sr25519, sr25519_key_pair.public().into());
|
||||
assert!(ed25519_public_keys.contains(&CryptoTypePublicPair(ed25519::CRYPTO_ID, session_keys.ed25519.to_raw_vec())));
|
||||
assert!(sr25519_public_keys.contains(&CryptoTypePublicPair(sr25519::CRYPTO_ID, session_keys.sr25519.to_raw_vec())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -23,12 +23,27 @@ use sp_std::vec::Vec;
|
||||
pub use sp_core::ed25519::*;
|
||||
|
||||
mod app {
|
||||
use sp_core::crypto::{CryptoTypePublicPair, Public as TraitPublic};
|
||||
use sp_core::testing::ED25519;
|
||||
use sp_core::ed25519::CRYPTO_ID;
|
||||
|
||||
crate::app_crypto!(super, ED25519);
|
||||
|
||||
impl crate::traits::BoundToRuntimeAppPublic for Public {
|
||||
type Public = Self;
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use app::{Public as AppPublic, Signature as AppSignature};
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use sp_core::{self, crypto::{CryptoType, Public, Derive, IsWrappedBy, Wraps}, RuntimeDebug};
|
||||
pub use sp_core::{self, crypto::{CryptoType, CryptoTypePublicPair, Public, Derive, IsWrappedBy, Wraps}, RuntimeDebug};
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "full_crypto")]
|
||||
pub use sp_core::crypto::{SecretStringError, DeriveJunction, Ss58Codec, Pair};
|
||||
pub use sp_core::{crypto::{KeyTypeId, key_types}};
|
||||
pub use sp_core::crypto::{CryptoTypeId, KeyTypeId, key_types};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use codec;
|
||||
|
||||
@@ -23,12 +23,27 @@ use sp_std::vec::Vec;
|
||||
pub use sp_core::sr25519::*;
|
||||
|
||||
mod app {
|
||||
use sp_core::crypto::{CryptoTypePublicPair, Public as TraitPublic};
|
||||
use sp_core::testing::SR25519;
|
||||
use sp_core::sr25519::CRYPTO_ID;
|
||||
|
||||
crate::app_crypto!(super, SR25519);
|
||||
|
||||
impl crate::traits::BoundToRuntimeAppPublic for Public {
|
||||
type Public = Self;
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use app::{Public as AppPublic, Signature as AppSignature};
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
//! Integration tests for ed25519
|
||||
|
||||
use sp_runtime::generic::BlockId;
|
||||
use sp_core::{testing::{KeyStore, ED25519}, crypto::Pair};
|
||||
use sp_core::{
|
||||
crypto::Pair,
|
||||
testing::{KeyStore, ED25519},
|
||||
};
|
||||
use substrate_test_runtime_client::{
|
||||
TestClientBuilder, DefaultTestClientBuilderExt, TestClientBuilderExt,
|
||||
runtime::TestAPI,
|
||||
@@ -33,8 +36,7 @@ fn ed25519_works_in_runtime() {
|
||||
.test_ed25519_crypto(&BlockId::Number(0))
|
||||
.expect("Tests `ed25519` crypto.");
|
||||
|
||||
let key_pair = keystore.read().ed25519_key_pair(ED25519, &public.as_ref())
|
||||
.expect("There should be at a `ed25519` key in the keystore for the given public key.");
|
||||
|
||||
assert!(AppPair::verify(&signature, "ed25519", &AppPublic::from(key_pair.public())));
|
||||
let supported_keys = keystore.read().keys(ED25519).unwrap();
|
||||
assert!(supported_keys.contains(&public.clone().into()));
|
||||
assert!(AppPair::verify(&signature, "ed25519", &AppPublic::from(public)));
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@
|
||||
|
||||
|
||||
use sp_runtime::generic::BlockId;
|
||||
use sp_core::{testing::{KeyStore, SR25519}, crypto::Pair};
|
||||
use sp_core::{
|
||||
crypto::Pair,
|
||||
testing::{KeyStore, SR25519},
|
||||
};
|
||||
use substrate_test_runtime_client::{
|
||||
TestClientBuilder, DefaultTestClientBuilderExt, TestClientBuilderExt,
|
||||
runtime::TestAPI,
|
||||
@@ -34,8 +37,7 @@ fn sr25519_works_in_runtime() {
|
||||
.test_sr25519_crypto(&BlockId::Number(0))
|
||||
.expect("Tests `sr25519` crypto.");
|
||||
|
||||
let key_pair = keystore.read().sr25519_key_pair(SR25519, public.as_ref())
|
||||
.expect("There should be at a `sr25519` key in the keystore for the given public key.");
|
||||
|
||||
assert!(AppPair::verify(&signature, "sr25519", &AppPublic::from(key_pair.public())));
|
||||
let supported_keys = keystore.read().keys(SR25519).unwrap();
|
||||
assert!(supported_keys.contains(&public.clone().into()));
|
||||
assert!(AppPair::verify(&signature, "sr25519", &AppPublic::from(public)));
|
||||
}
|
||||
|
||||
@@ -21,8 +21,25 @@
|
||||
use sp_std::vec::Vec;
|
||||
|
||||
mod app {
|
||||
use sp_application_crypto::{app_crypto, key_types::AUTHORITY_DISCOVERY, sr25519};
|
||||
use sp_application_crypto::{
|
||||
CryptoTypePublicPair,
|
||||
key_types::AUTHORITY_DISCOVERY,
|
||||
Public as _,
|
||||
app_crypto,
|
||||
sr25519};
|
||||
app_crypto!(sr25519, AUTHORITY_DISCOVERY);
|
||||
|
||||
impl From<Public> for CryptoTypePublicPair {
|
||||
fn from(key: Public) -> Self {
|
||||
(&key).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Public> for CryptoTypePublicPair {
|
||||
fn from(key: &Public) -> Self {
|
||||
CryptoTypePublicPair(sr25519::CRYPTO_ID, key.to_raw_vec())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sp_application_crypto::with_pair! {
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
use crate::{sr25519, ed25519};
|
||||
use sp_std::hash::Hash;
|
||||
use sp_std::vec::Vec;
|
||||
use sp_std::str;
|
||||
#[cfg(feature = "std")]
|
||||
use sp_std::convert::TryInto;
|
||||
use sp_std::convert::TryFrom;
|
||||
@@ -33,7 +34,8 @@ use codec::{Encode, Decode};
|
||||
use regex::Regex;
|
||||
#[cfg(feature = "std")]
|
||||
use base58::{FromBase58, ToBase58};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use crate::hexdisplay::HexDisplay;
|
||||
use zeroize::Zeroize;
|
||||
#[doc(hidden)]
|
||||
pub use sp_std::ops::Deref;
|
||||
@@ -539,7 +541,9 @@ impl<T: Sized + AsMut<[u8]> + AsRef<[u8]> + Default + Derive> Ss58Codec for T {
|
||||
}
|
||||
|
||||
/// Trait suitable for typical cryptographic PKI key public type.
|
||||
pub trait Public: AsRef<[u8]> + AsMut<[u8]> + Default + Derive + CryptoType + PartialEq + Eq + Clone + Send + Sync {
|
||||
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
|
||||
@@ -955,6 +959,27 @@ impl<'a> TryFrom<&'a str> for KeyTypeId {
|
||||
}
|
||||
}
|
||||
|
||||
/// An identifier for a specific cryptographic algorithm used by a key pair
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)]
|
||||
pub struct CryptoTypeId(pub [u8; 4]);
|
||||
|
||||
/// A type alias of CryptoTypeId & a public key
|
||||
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode)]
|
||||
pub struct CryptoTypePublicPair(pub CryptoTypeId, pub Vec<u8>);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl sp_std::fmt::Display for CryptoTypePublicPair {
|
||||
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
||||
let id = match str::from_utf8(&(self.0).0[..]) {
|
||||
Ok(id) => id.to_string(),
|
||||
Err(_) => {
|
||||
format!("{:#?}", self.0)
|
||||
}
|
||||
};
|
||||
write!(f, "{}-{}", id, HexDisplay::from(&self.1))
|
||||
}
|
||||
}
|
||||
|
||||
/// Known key types; this also functions as a global registry of key types for projects wishing to
|
||||
/// avoid collisions with each other.
|
||||
///
|
||||
|
||||
@@ -36,10 +36,13 @@ use crate::{hashing::blake2_256, crypto::{Pair as TraitPair, DeriveJunction, Sec
|
||||
use crate::crypto::Ss58Codec;
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{de, Serializer, Serialize, Deserializer, Deserialize};
|
||||
use crate::crypto::{Public as TraitPublic, UncheckedFrom, CryptoType, Derive};
|
||||
use crate::crypto::{Public as TraitPublic, UncheckedFrom, CryptoType, Derive, CryptoTypeId};
|
||||
#[cfg(feature = "full_crypto")]
|
||||
use secp256k1::{PublicKey, SecretKey};
|
||||
|
||||
/// An identifier used to match public keys against ecdsa keys
|
||||
pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"ecds");
|
||||
|
||||
/// A secret seed (which is bytewise essentially equivalent to a SecretKey).
|
||||
///
|
||||
/// We need it as a different type because `Seed` is expected to be AsRef<[u8]>.
|
||||
|
||||
@@ -38,10 +38,13 @@ use crate::crypto::{Pair as TraitPair, DeriveJunction, SecretStringError};
|
||||
use crate::crypto::Ss58Codec;
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{de, Serializer, Serialize, Deserializer, Deserialize};
|
||||
use crate::{crypto::{Public as TraitPublic, UncheckedFrom, CryptoType, Derive}};
|
||||
use crate::crypto::{Public as TraitPublic, CryptoTypePublicPair, UncheckedFrom, CryptoType, Derive, CryptoTypeId};
|
||||
use sp_runtime_interface::pass_by::PassByInner;
|
||||
use sp_std::ops::Deref;
|
||||
|
||||
/// An identifier used to match public keys against ed25519 keys
|
||||
pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"ed25");
|
||||
|
||||
/// 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
|
||||
/// will need it later (such as for HDKD).
|
||||
@@ -378,6 +381,18 @@ impl TraitPublic for Public {
|
||||
|
||||
impl Derive 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())
|
||||
}
|
||||
}
|
||||
|
||||
/// Derive a single hard junction.
|
||||
#[cfg(feature = "full_crypto")]
|
||||
fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed {
|
||||
|
||||
@@ -39,7 +39,7 @@ use crate::crypto::{
|
||||
#[cfg(feature = "std")]
|
||||
use crate::crypto::Ss58Codec;
|
||||
|
||||
use crate::{crypto::{Public as TraitPublic, UncheckedFrom, CryptoType, Derive}};
|
||||
use crate::crypto::{Public as TraitPublic, CryptoTypePublicPair, UncheckedFrom, CryptoType, Derive, CryptoTypeId};
|
||||
use crate::hash::{H256, H512};
|
||||
use codec::{Encode, Decode};
|
||||
use sp_std::ops::Deref;
|
||||
@@ -54,6 +54,9 @@ use sp_runtime_interface::pass_by::PassByInner;
|
||||
#[cfg(feature = "full_crypto")]
|
||||
const SIGNING_CTX: &[u8] = b"substrate";
|
||||
|
||||
/// An identifier used to match public keys against sr25519 keys
|
||||
pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"sr25");
|
||||
|
||||
/// An Schnorrkel/Ristretto x25519 ("sr25519") public key.
|
||||
#[cfg_attr(feature = "full_crypto", derive(Hash))]
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Encode, Decode, Default, PassByInner)]
|
||||
@@ -390,6 +393,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())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl From<MiniSecretKey> for Pair {
|
||||
fn from(sec: MiniSecretKey) -> Pair {
|
||||
@@ -599,7 +614,7 @@ impl CryptoType for Pair {
|
||||
#[cfg(test)]
|
||||
mod compatibility_test {
|
||||
use super::*;
|
||||
use crate::crypto::{DEV_PHRASE};
|
||||
use crate::crypto::DEV_PHRASE;
|
||||
use hex_literal::hex;
|
||||
|
||||
// NOTE: tests to ensure addresses that are created with the `0.1.x` version (pre-audit) are
|
||||
|
||||
@@ -16,10 +16,16 @@
|
||||
|
||||
//! Types that should only be used for testing!
|
||||
|
||||
use crate::crypto::{KeyTypeId, CryptoTypePublicPair};
|
||||
#[cfg(feature = "std")]
|
||||
use crate::{ed25519, sr25519, crypto::{Public, Pair}};
|
||||
use crate::crypto::KeyTypeId;
|
||||
|
||||
use crate::{
|
||||
crypto::{Pair, Public},
|
||||
ed25519, sr25519,
|
||||
traits::BareCryptoStoreError
|
||||
};
|
||||
#[cfg(feature = "std")]
|
||||
use std::collections::HashSet;
|
||||
use codec::Encode;
|
||||
/// Key type for generic Ed25519 key.
|
||||
pub const ED25519: KeyTypeId = KeyTypeId(*b"ed25");
|
||||
/// Key type for generic Sr 25519 key.
|
||||
@@ -39,10 +45,41 @@ impl KeyStore {
|
||||
pub fn new() -> crate::traits::BareCryptoStorePtr {
|
||||
std::sync::Arc::new(parking_lot::RwLock::new(Self::default()))
|
||||
}
|
||||
|
||||
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_string(s, None).expect("`sr25519` seed slice is valid"))
|
||||
)
|
||||
}
|
||||
|
||||
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_string(s, None).expect("`ed25519` seed slice is valid"))
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl crate::traits::BareCryptoStore for KeyStore {
|
||||
fn keys(&self, id: KeyTypeId) -> Result<Vec<CryptoTypePublicPair>, BareCryptoStoreError> {
|
||||
self.keys
|
||||
.get(&id)
|
||||
.map(|map| {
|
||||
Ok(map.keys()
|
||||
.fold(Vec::new(), |mut v, k| {
|
||||
v.push(CryptoTypePublicPair(sr25519::CRYPTO_ID, k.clone()));
|
||||
v.push(CryptoTypePublicPair(ed25519::CRYPTO_ID, k.clone()));
|
||||
v
|
||||
}))
|
||||
})
|
||||
.unwrap_or(Ok(vec![]))
|
||||
}
|
||||
|
||||
fn sr25519_public_keys(&self, id: KeyTypeId) -> Vec<sr25519::Public> {
|
||||
self.keys.get(&id)
|
||||
.map(|keys|
|
||||
@@ -58,10 +95,11 @@ impl crate::traits::BareCryptoStore for KeyStore {
|
||||
&mut self,
|
||||
id: KeyTypeId,
|
||||
seed: Option<&str>,
|
||||
) -> Result<sr25519::Public, String> {
|
||||
) -> Result<sr25519::Public, BareCryptoStoreError> {
|
||||
match seed {
|
||||
Some(seed) => {
|
||||
let pair = sr25519::Pair::from_string(seed, None).expect("Generates an `sr25519` pair.");
|
||||
let pair = sr25519::Pair::from_string(seed, None)
|
||||
.map_err(|_| BareCryptoStoreError::ValidationError("Generates an `sr25519` pair.".to_owned()))?;
|
||||
self.keys.entry(id).or_default().insert(pair.public().to_raw_vec(), seed.into());
|
||||
Ok(pair.public())
|
||||
},
|
||||
@@ -73,14 +111,6 @@ impl crate::traits::BareCryptoStore for KeyStore {
|
||||
}
|
||||
}
|
||||
|
||||
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_string(s, None).expect("`sr25519` seed slice is valid"))
|
||||
)
|
||||
}
|
||||
|
||||
fn ed25519_public_keys(&self, id: KeyTypeId) -> Vec<ed25519::Public> {
|
||||
self.keys.get(&id)
|
||||
.map(|keys|
|
||||
@@ -96,10 +126,11 @@ impl crate::traits::BareCryptoStore for KeyStore {
|
||||
&mut self,
|
||||
id: KeyTypeId,
|
||||
seed: Option<&str>,
|
||||
) -> Result<ed25519::Public, String> {
|
||||
) -> Result<ed25519::Public, BareCryptoStoreError> {
|
||||
match seed {
|
||||
Some(seed) => {
|
||||
let pair = ed25519::Pair::from_string(seed, None).expect("Generates an `ed25519` pair.");
|
||||
let pair = ed25519::Pair::from_string(seed, None)
|
||||
.map_err(|_| BareCryptoStoreError::ValidationError("Generates an `ed25519` pair.".to_owned()))?;
|
||||
self.keys.entry(id).or_default().insert(pair.public().to_raw_vec(), seed.into());
|
||||
Ok(pair.public())
|
||||
},
|
||||
@@ -111,14 +142,6 @@ impl crate::traits::BareCryptoStore for KeyStore {
|
||||
}
|
||||
}
|
||||
|
||||
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_string(s, None).expect("`ed25519` seed slice is valid"))
|
||||
)
|
||||
}
|
||||
|
||||
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(())
|
||||
@@ -131,6 +154,40 @@ impl crate::traits::BareCryptoStore for KeyStore {
|
||||
fn has_keys(&self, public_keys: &[(Vec<u8>, KeyTypeId)]) -> bool {
|
||||
public_keys.iter().all(|(k, t)| self.keys.get(&t).and_then(|s| s.get(k)).is_some())
|
||||
}
|
||||
|
||||
fn supported_keys(
|
||||
&self,
|
||||
id: KeyTypeId,
|
||||
keys: Vec<CryptoTypePublicPair>,
|
||||
) -> std::result::Result<Vec<CryptoTypePublicPair>, BareCryptoStoreError> {
|
||||
let provided_keys = keys.into_iter().collect::<HashSet<_>>();
|
||||
let all_keys = self.keys(id)?.into_iter().collect::<HashSet<_>>();
|
||||
|
||||
Ok(provided_keys.intersection(&all_keys).cloned().collect())
|
||||
}
|
||||
|
||||
fn sign_with(
|
||||
&self,
|
||||
id: KeyTypeId,
|
||||
key: &CryptoTypePublicPair,
|
||||
msg: &[u8],
|
||||
) -> Result<Vec<u8>, BareCryptoStoreError> {
|
||||
match key.0 {
|
||||
ed25519::CRYPTO_ID => {
|
||||
let key_pair: ed25519::Pair = self
|
||||
.ed25519_key_pair(id, &ed25519::Public::from_slice(key.1.as_slice()))
|
||||
.ok_or(BareCryptoStoreError::PairNotFound("ed25519".to_owned()))?;
|
||||
return Ok(key_pair.sign(msg).encode());
|
||||
}
|
||||
sr25519::CRYPTO_ID => {
|
||||
let key_pair: sr25519::Pair = self
|
||||
.sr25519_key_pair(id, &sr25519::Public::from_slice(key.1.as_slice()))
|
||||
.ok_or(BareCryptoStoreError::PairNotFound("sr25519".to_owned()))?;
|
||||
return Ok(key_pair.sign(msg).encode());
|
||||
}
|
||||
_ => Err(BareCryptoStoreError::KeyNotSupported(id))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Macro for exporting functions from wasm in with the expected signature for using it with the
|
||||
@@ -247,11 +304,9 @@ mod tests {
|
||||
.ed25519_generate_new(ED25519, None)
|
||||
.expect("Generates key");
|
||||
|
||||
let store_key_pair = store.read()
|
||||
.ed25519_key_pair(ED25519, &public)
|
||||
.expect("Key should exists in store");
|
||||
let public_keys = store.read().keys(ED25519).unwrap();
|
||||
|
||||
assert_eq!(public, store_key_pair.public());
|
||||
assert!(public_keys.contains(&public.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -267,11 +322,8 @@ mod tests {
|
||||
key_pair.public().as_ref(),
|
||||
).expect("Inserts unknown key");
|
||||
|
||||
let store_key_pair = store.read().sr25519_key_pair(
|
||||
SR25519,
|
||||
&key_pair.public(),
|
||||
).expect("Gets key pair from keystore");
|
||||
let public_keys = store.read().keys(SR25519).unwrap();
|
||||
|
||||
assert_eq!(key_pair.public(), store_key_pair.public());
|
||||
assert!(public_keys.contains(&key_pair.public().into()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,35 @@
|
||||
|
||||
//! Shareable Substrate traits.
|
||||
|
||||
use crate::{crypto::KeyTypeId, ed25519, sr25519};
|
||||
use crate::{
|
||||
crypto::{KeyTypeId, CryptoTypePublicPair},
|
||||
ed25519, sr25519,
|
||||
};
|
||||
|
||||
use std::{
|
||||
fmt::{Debug, Display}, panic::UnwindSafe, sync::Arc, borrow::Cow,
|
||||
borrow::Cow,
|
||||
fmt::{Debug, Display},
|
||||
panic::UnwindSafe,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
pub use sp_externalities::{Externalities, ExternalitiesExt};
|
||||
|
||||
/// BareCryptoStore error
|
||||
#[derive(Debug)]
|
||||
pub enum BareCryptoStoreError {
|
||||
/// Public key type is not supported
|
||||
KeyNotSupported(KeyTypeId),
|
||||
/// Pair not found for public key and KeyTypeId
|
||||
PairNotFound(String),
|
||||
/// Validation error
|
||||
ValidationError(String),
|
||||
/// Keystore unavailable
|
||||
Unavailable,
|
||||
/// Programming errors
|
||||
Other(String)
|
||||
}
|
||||
|
||||
/// Something that generates, stores and provides access to keys.
|
||||
pub trait BareCryptoStore: Send + Sync {
|
||||
/// Returns all sr25519 public keys for the given key type.
|
||||
@@ -37,10 +58,7 @@ pub trait BareCryptoStore: Send + Sync {
|
||||
&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>;
|
||||
|
||||
) -> Result<sr25519::Public, BareCryptoStoreError>;
|
||||
/// 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.
|
||||
@@ -52,10 +70,7 @@ pub trait BareCryptoStore: Send + Sync {
|
||||
&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>;
|
||||
) -> Result<ed25519::Public, BareCryptoStoreError>;
|
||||
|
||||
/// Insert a new key. This doesn't require any known of the crypto; but a public key must be
|
||||
/// manually provided.
|
||||
@@ -67,11 +82,78 @@ pub trait BareCryptoStore: Send + Sync {
|
||||
|
||||
/// Get the password for this store.
|
||||
fn password(&self) -> Option<&str>;
|
||||
/// Find intersection between provided keys and supported keys
|
||||
///
|
||||
/// Provided a list of (CryptoTypeId,[u8]) pairs, this would return
|
||||
/// a filtered set of public keys which are supported by the keystore.
|
||||
fn supported_keys(
|
||||
&self,
|
||||
id: KeyTypeId,
|
||||
keys: Vec<CryptoTypePublicPair>
|
||||
) -> Result<Vec<CryptoTypePublicPair>, BareCryptoStoreError>;
|
||||
/// List all supported keys
|
||||
///
|
||||
/// Returns a set of public keys the signer supports.
|
||||
fn keys(&self, id: KeyTypeId) -> Result<Vec<CryptoTypePublicPair>, BareCryptoStoreError>;
|
||||
|
||||
/// Checks if the private keys for the given public key and key type combinations exist.
|
||||
///
|
||||
/// Returns `true` iff all private keys could be found.
|
||||
fn has_keys(&self, public_keys: &[(Vec<u8>, KeyTypeId)]) -> bool;
|
||||
|
||||
/// Sign with key
|
||||
///
|
||||
/// Signs a message with the private key that matches
|
||||
/// the public key passed.
|
||||
///
|
||||
/// Returns the SCALE encoded signature if key is found & supported,
|
||||
/// an error otherwise.
|
||||
fn sign_with(
|
||||
&self,
|
||||
id: KeyTypeId,
|
||||
key: &CryptoTypePublicPair,
|
||||
msg: &[u8],
|
||||
) -> Result<Vec<u8>, BareCryptoStoreError>;
|
||||
|
||||
/// Sign with any key
|
||||
///
|
||||
/// Given a list of public keys, find the first supported key and
|
||||
/// sign the provided message with that key.
|
||||
///
|
||||
/// Returns a tuple of the used key and the signature
|
||||
fn sign_with_any(
|
||||
&self,
|
||||
id: KeyTypeId,
|
||||
keys: Vec<CryptoTypePublicPair>,
|
||||
msg: &[u8]
|
||||
) -> Result<(CryptoTypePublicPair, Vec<u8>), BareCryptoStoreError> {
|
||||
if keys.len() == 1 {
|
||||
return self.sign_with(id, &keys[0], msg).map(|s| (keys[0].clone(), s));
|
||||
} else {
|
||||
for k in self.supported_keys(id, keys)? {
|
||||
if let Ok(sign) = self.sign_with(id, &k, msg) {
|
||||
return Ok((k, sign));
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(BareCryptoStoreError::KeyNotSupported(id))
|
||||
}
|
||||
|
||||
/// Sign with all keys
|
||||
///
|
||||
/// Provided a list of public keys, sign a message with
|
||||
/// each key given that the key is supported.
|
||||
///
|
||||
/// Returns a list of `Result`s each representing the signature of each key or
|
||||
/// a BareCryptoStoreError for non-supported keys.
|
||||
fn sign_with_all(
|
||||
&self,
|
||||
id: KeyTypeId,
|
||||
keys: Vec<CryptoTypePublicPair>,
|
||||
msg: &[u8],
|
||||
) -> Result<Vec<Result<Vec<u8>, BareCryptoStoreError>>, ()>{
|
||||
Ok(keys.iter().map(|k| self.sign_with(id, k, msg)).collect())
|
||||
}
|
||||
}
|
||||
|
||||
/// A pointer to the key store.
|
||||
|
||||
@@ -411,8 +411,9 @@ pub trait Crypto {
|
||||
self.extension::<KeystoreExt>()
|
||||
.expect("No `keystore` associated for the current context!")
|
||||
.read()
|
||||
.ed25519_key_pair(id, &pub_key)
|
||||
.map(|k| k.sign(msg))
|
||||
.sign_with(id, &pub_key.into(), msg)
|
||||
.map(|sig| ed25519::Signature::from_slice(sig.as_slice()))
|
||||
.ok()
|
||||
}
|
||||
|
||||
/// Verify an `ed25519` signature.
|
||||
@@ -463,8 +464,9 @@ pub trait Crypto {
|
||||
self.extension::<KeystoreExt>()
|
||||
.expect("No `keystore` associated for the current context!")
|
||||
.read()
|
||||
.sr25519_key_pair(id, &pub_key)
|
||||
.map(|k| k.sign(msg))
|
||||
.sign_with(id, &pub_key.into(), msg)
|
||||
.map(|sig| sr25519::Signature::from_slice(sig.as_slice()))
|
||||
.ok()
|
||||
}
|
||||
|
||||
/// Verify an `sr25519` signature.
|
||||
|
||||
Reference in New Issue
Block a user