From e17a23e9071e128707268c48c46a5231c4ca2b80 Mon Sep 17 00:00:00 2001
From: Rakan Alhneiti <rakan.alhneiti@gmail.com>
Date: Mon, 30 Mar 2020 13:18:59 +0200
Subject: [PATCH] Introduce `sign_with` method in keystore (#4925)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 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>
---
 .../client/authority-discovery/src/error.rs   |   6 +
 .../client/authority-discovery/src/lib.rs     |  42 +++--
 substrate/client/keystore/src/lib.rs          | 172 +++++++++++++-----
 substrate/client/rpc/src/author/tests.rs      |  27 +--
 .../application-crypto/src/ed25519.rs         |  15 ++
 .../primitives/application-crypto/src/lib.rs  |   4 +-
 .../application-crypto/src/sr25519.rs         |  15 ++
 .../application-crypto/test/src/ed25519.rs    |  12 +-
 .../application-crypto/test/src/sr25519.rs    |  12 +-
 .../primitives/authority-discovery/src/lib.rs |  19 +-
 substrate/primitives/core/src/crypto.rs       |  29 ++-
 substrate/primitives/core/src/ecdsa.rs        |   5 +-
 substrate/primitives/core/src/ed25519.rs      |  17 +-
 substrate/primitives/core/src/sr25519.rs      |  19 +-
 substrate/primitives/core/src/testing.rs      | 116 ++++++++----
 substrate/primitives/core/src/traits.rs       | 102 ++++++++++-
 substrate/primitives/io/src/lib.rs            |  10 +-
 17 files changed, 472 insertions(+), 150 deletions(-)

diff --git a/substrate/client/authority-discovery/src/error.rs b/substrate/client/authority-discovery/src/error.rs
index 74b7043c29b..216f8c26bf0 100644
--- a/substrate/client/authority-discovery/src/error.rs
+++ b/substrate/client/authority-discovery/src/error.rs
@@ -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),
 }
diff --git a/substrate/client/authority-discovery/src/lib.rs b/substrate/client/authority-discovery/src/lib.rs
index 171a401bbbf..ec103ab8a0e 100644
--- a/substrate/client/authority-discovery/src/lib.rs
+++ b/substrate/client/authority-discovery/src/lib.rs
@@ -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
diff --git a/substrate/client/keystore/src/lib.rs b/substrate/client/keystore/src/lib.rs
index 087ddf326de..f8bc9309711 100644
--- a/substrate/client/keystore/src/lib.rs
+++ b/substrate/client/keystore/src/lib.rs
@@ -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())
+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<_>>())
 	}
 
-	/// 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)
+	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))
+		}
 	}
-}
 
-impl BareCryptoStore for Store {
 	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(),
 		);
 	}
 }
diff --git a/substrate/client/rpc/src/author/tests.rs b/substrate/client/rpc/src/author/tests.rs
index 3093cd9d3b7..8b956c23a5e 100644
--- a/substrate/client/rpc/src/author/tests.rs
+++ b/substrate/client/rpc/src/author/tests.rs
@@ -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 sr25519_key_pair = setup.keystore.read().sr25519_key_pair(
-		SR25519,
-		&session_keys.sr25519.clone().into(),
-	).expect("sr25519 key exists in store");
+	let ed25519_public_keys = setup.keystore.read().keys(ED25519).unwrap();
+	let sr25519_public_keys = setup.keystore.read().keys(SR25519).unwrap();
 
-	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]
diff --git a/substrate/primitives/application-crypto/src/ed25519.rs b/substrate/primitives/application-crypto/src/ed25519.rs
index 414715a1068..5be79ff4f79 100644
--- a/substrate/primitives/application-crypto/src/ed25519.rs
+++ b/substrate/primitives/application-crypto/src/ed25519.rs
@@ -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};
diff --git a/substrate/primitives/application-crypto/src/lib.rs b/substrate/primitives/application-crypto/src/lib.rs
index b7c9ccaa982..07e2b45106e 100644
--- a/substrate/primitives/application-crypto/src/lib.rs
+++ b/substrate/primitives/application-crypto/src/lib.rs
@@ -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;
diff --git a/substrate/primitives/application-crypto/src/sr25519.rs b/substrate/primitives/application-crypto/src/sr25519.rs
index 59c6f19b6f2..a0f2cef1c4e 100644
--- a/substrate/primitives/application-crypto/src/sr25519.rs
+++ b/substrate/primitives/application-crypto/src/sr25519.rs
@@ -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};
diff --git a/substrate/primitives/application-crypto/test/src/ed25519.rs b/substrate/primitives/application-crypto/test/src/ed25519.rs
index 400edfd6ae4..1d72962829a 100644
--- a/substrate/primitives/application-crypto/test/src/ed25519.rs
+++ b/substrate/primitives/application-crypto/test/src/ed25519.rs
@@ -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)));
 }
diff --git a/substrate/primitives/application-crypto/test/src/sr25519.rs b/substrate/primitives/application-crypto/test/src/sr25519.rs
index 49bb3c2a836..f2c7c48b2bc 100644
--- a/substrate/primitives/application-crypto/test/src/sr25519.rs
+++ b/substrate/primitives/application-crypto/test/src/sr25519.rs
@@ -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)));
 }
diff --git a/substrate/primitives/authority-discovery/src/lib.rs b/substrate/primitives/authority-discovery/src/lib.rs
index 89268255259..68680ad7594 100644
--- a/substrate/primitives/authority-discovery/src/lib.rs
+++ b/substrate/primitives/authority-discovery/src/lib.rs
@@ -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! {
diff --git a/substrate/primitives/core/src/crypto.rs b/substrate/primitives/core/src/crypto.rs
index 6301600921a..f16c1609603 100644
--- a/substrate/primitives/core/src/crypto.rs
+++ b/substrate/primitives/core/src/crypto.rs
@@ -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.
 ///
diff --git a/substrate/primitives/core/src/ecdsa.rs b/substrate/primitives/core/src/ecdsa.rs
index 548ae49b554..8a45157844f 100644
--- a/substrate/primitives/core/src/ecdsa.rs
+++ b/substrate/primitives/core/src/ecdsa.rs
@@ -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]>.
diff --git a/substrate/primitives/core/src/ed25519.rs b/substrate/primitives/core/src/ed25519.rs
index 5e04dcceffb..abeac05388d 100644
--- a/substrate/primitives/core/src/ed25519.rs
+++ b/substrate/primitives/core/src/ed25519.rs
@@ -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 {
diff --git a/substrate/primitives/core/src/sr25519.rs b/substrate/primitives/core/src/sr25519.rs
index de643dbb0a8..717952eb01c 100644
--- a/substrate/primitives/core/src/sr25519.rs
+++ b/substrate/primitives/core/src/sr25519.rs
@@ -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
diff --git a/substrate/primitives/core/src/testing.rs b/substrate/primitives/core/src/testing.rs
index 247323f9703..b5e6f4c7aff 100644
--- a/substrate/primitives/core/src/testing.rs
+++ b/substrate/primitives/core/src/testing.rs
@@ -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()));
 	}
 }
diff --git a/substrate/primitives/core/src/traits.rs b/substrate/primitives/core/src/traits.rs
index 21a0b77ca7a..14839fb5856 100644
--- a/substrate/primitives/core/src/traits.rs
+++ b/substrate/primitives/core/src/traits.rs
@@ -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.
diff --git a/substrate/primitives/io/src/lib.rs b/substrate/primitives/io/src/lib.rs
index 78006589747..bc49df159eb 100644
--- a/substrate/primitives/io/src/lib.rs
+++ b/substrate/primitives/io/src/lib.rs
@@ -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.
-- 
GitLab