Keystore overhaul (final) (#13683)

* Introduce keystore specialized sign methods

* Get rid of 'AppKey::UntypedGeneric' associated type.

Untyped generics are accessible using associated types 'Generic' associated type.
I.e. <T as AppKey>::Public::Generic

* Get rid of 'CryptoTypePublicPair'

* Trivial fix

* Small refactory of local keystore implementations

* Remove 'crypto_id' method from 'Public'

* Trivial rename of 'AppKey' to 'AppCrypto'

* Remove unused import

* Improve docs

* Better signature related errors for authority-discovery

* Apply review suggestion

* Apply review suggestions

Co-authored-by: Koute <koute@users.noreply.github.com>

* Authority discoverty signing error revisited

* Signing error revisited for babe and aura as well

* Further cleanup

---------

Co-authored-by: Koute <koute@users.noreply.github.com>
This commit is contained in:
Davide Galassi
2023-03-24 14:46:02 +01:00
committed by GitHub
parent 370e71cb20
commit 40e1704e1c
27 changed files with 552 additions and 616 deletions
+106 -108
View File
@@ -18,13 +18,10 @@
//! Local keystore implementation
use parking_lot::RwLock;
use sp_application_crypto::{ecdsa, ed25519, sr25519, AppKey, AppPair, IsWrappedBy};
use sp_application_crypto::{AppCrypto, AppPair, IsWrappedBy};
use sp_core::{
crypto::{
ByteArray, CryptoTypePublicPair, ExposeSecret, KeyTypeId, Pair as PairT, SecretString,
},
sr25519::{Pair as Sr25519Pair, Public as Sr25519Public},
Encode,
crypto::{ByteArray, ExposeSecret, KeyTypeId, Pair as CorePair, SecretString},
ecdsa, ed25519, sr25519,
};
use sp_keystore::{
vrf::{make_transcript, VRFSignature, VRFTranscriptData},
@@ -62,67 +59,13 @@ impl LocalKeystore {
/// `Err(_)` when something failed.
pub fn key_pair<Pair: AppPair>(
&self,
public: &<Pair as AppKey>::Public,
public: &<Pair as AppCrypto>::Public,
) -> Result<Option<Pair>> {
self.0.read().key_pair::<Pair>(public)
}
}
impl Keystore for LocalKeystore {
fn keys(&self, id: KeyTypeId) -> std::result::Result<Vec<CryptoTypePublicPair>, TraitError> {
let raw_keys = self.0.read().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.push(CryptoTypePublicPair(ecdsa::CRYPTO_ID, k));
v
}))
}
fn sign_with(
&self,
id: KeyTypeId,
key: &CryptoTypePublicPair,
msg: &[u8],
) -> std::result::Result<Option<Vec<u8>>, TraitError> {
match key.0 {
ed25519::CRYPTO_ID => {
let pub_key = ed25519::Public::from_slice(key.1.as_slice()).map_err(|()| {
TraitError::Other("Corrupted public key - Invalid size".into())
})?;
let key_pair = self
.0
.read()
.key_pair_by_type::<ed25519::Pair>(&pub_key, id)
.map_err(TraitError::from)?;
key_pair.map(|k| k.sign(msg).encode()).map(Ok).transpose()
},
sr25519::CRYPTO_ID => {
let pub_key = sr25519::Public::from_slice(key.1.as_slice()).map_err(|()| {
TraitError::Other("Corrupted public key - Invalid size".into())
})?;
let key_pair = self
.0
.read()
.key_pair_by_type::<sr25519::Pair>(&pub_key, id)
.map_err(TraitError::from)?;
key_pair.map(|k| k.sign(msg).encode()).map(Ok).transpose()
},
ecdsa::CRYPTO_ID => {
let pub_key = ecdsa::Public::from_slice(key.1.as_slice()).map_err(|()| {
TraitError::Other("Corrupted public key - Invalid size".into())
})?;
let key_pair = self
.0
.read()
.key_pair_by_type::<ecdsa::Pair>(&pub_key, id)
.map_err(TraitError::from)?;
key_pair.map(|k| k.sign(msg).encode()).map(Ok).transpose()
},
_ => Err(TraitError::KeyNotSupported(id)),
}
}
fn sr25519_public_keys(&self, key_type: KeyTypeId) -> Vec<sr25519::Public> {
self.0
.read()
@@ -140,19 +83,49 @@ impl Keystore for LocalKeystore {
/// If the `[seed]` is `Some` then the key will be ephemeral and stored in memory.
fn sr25519_generate_new(
&self,
id: KeyTypeId,
key_type: KeyTypeId,
seed: Option<&str>,
) -> std::result::Result<sr25519::Public, TraitError> {
let pair = match seed {
Some(seed) =>
self.0.write().insert_ephemeral_from_seed_by_type::<sr25519::Pair>(seed, id),
None => self.0.write().generate_by_type::<sr25519::Pair>(id),
Some(seed) => self
.0
.write()
.insert_ephemeral_from_seed_by_type::<sr25519::Pair>(seed, key_type),
None => self.0.write().generate_by_type::<sr25519::Pair>(key_type),
}
.map_err(|e| -> TraitError { e.into() })?;
Ok(pair.public())
}
fn sr25519_sign(
&self,
key_type: KeyTypeId,
public: &sr25519::Public,
msg: &[u8],
) -> std::result::Result<Option<sr25519::Signature>, TraitError> {
let res = self
.0
.read()
.key_pair_by_type::<sr25519::Pair>(public, key_type)?
.map(|pair| pair.sign(msg));
Ok(res)
}
fn sr25519_vrf_sign(
&self,
key_type: KeyTypeId,
public: &sr25519::Public,
transcript_data: VRFTranscriptData,
) -> std::result::Result<Option<VRFSignature>, TraitError> {
let res = self.0.read().key_pair_by_type::<sr25519::Pair>(public, key_type)?.map(|pair| {
let transcript = make_transcript(transcript_data);
let (inout, proof, _) = pair.as_ref().vrf_sign(transcript);
VRFSignature { output: inout.to_output(), proof }
});
Ok(res)
}
fn ed25519_public_keys(&self, key_type: KeyTypeId) -> Vec<ed25519::Public> {
self.0
.read()
@@ -170,19 +143,35 @@ impl Keystore for LocalKeystore {
/// If the `[seed]` is `Some` then the key will be ephemeral and stored in memory.
fn ed25519_generate_new(
&self,
id: KeyTypeId,
key_type: KeyTypeId,
seed: Option<&str>,
) -> std::result::Result<ed25519::Public, TraitError> {
let pair = match seed {
Some(seed) =>
self.0.write().insert_ephemeral_from_seed_by_type::<ed25519::Pair>(seed, id),
None => self.0.write().generate_by_type::<ed25519::Pair>(id),
Some(seed) => self
.0
.write()
.insert_ephemeral_from_seed_by_type::<ed25519::Pair>(seed, key_type),
None => self.0.write().generate_by_type::<ed25519::Pair>(key_type),
}
.map_err(|e| -> TraitError { e.into() })?;
Ok(pair.public())
}
fn ed25519_sign(
&self,
key_type: KeyTypeId,
public: &ed25519::Public,
msg: &[u8],
) -> std::result::Result<Option<ed25519::Signature>, TraitError> {
let res = self
.0
.read()
.key_pair_by_type::<ed25519::Pair>(public, key_type)?
.map(|pair| pair.sign(msg));
Ok(res)
}
fn ecdsa_public_keys(&self, key_type: KeyTypeId) -> Vec<ecdsa::Public> {
self.0
.read()
@@ -200,19 +189,47 @@ impl Keystore for LocalKeystore {
/// If the `[seed]` is `Some` then the key will be ephemeral and stored in memory.
fn ecdsa_generate_new(
&self,
id: KeyTypeId,
key_type: KeyTypeId,
seed: Option<&str>,
) -> std::result::Result<ecdsa::Public, TraitError> {
let pair = match seed {
Some(seed) =>
self.0.write().insert_ephemeral_from_seed_by_type::<ecdsa::Pair>(seed, id),
None => self.0.write().generate_by_type::<ecdsa::Pair>(id),
self.0.write().insert_ephemeral_from_seed_by_type::<ecdsa::Pair>(seed, key_type),
None => self.0.write().generate_by_type::<ecdsa::Pair>(key_type),
}
.map_err(|e| -> TraitError { e.into() })?;
Ok(pair.public())
}
fn ecdsa_sign(
&self,
key_type: KeyTypeId,
public: &ecdsa::Public,
msg: &[u8],
) -> std::result::Result<Option<ecdsa::Signature>, TraitError> {
let res = self
.0
.read()
.key_pair_by_type::<ecdsa::Pair>(public, key_type)?
.map(|pair| pair.sign(msg));
Ok(res)
}
fn ecdsa_sign_prehashed(
&self,
key_type: KeyTypeId,
public: &ecdsa::Public,
msg: &[u8; 32],
) -> std::result::Result<Option<ecdsa::Signature>, TraitError> {
let res = self
.0
.read()
.key_pair_by_type::<ecdsa::Pair>(public, key_type)?
.map(|pair| pair.sign_prehashed(msg));
Ok(res)
}
fn insert(
&self,
key_type: KeyTypeId,
@@ -222,39 +239,15 @@ impl Keystore for LocalKeystore {
self.0.write().insert(key_type, suri, public).map_err(|_| ())
}
fn keys(&self, key_type: KeyTypeId) -> std::result::Result<Vec<Vec<u8>>, TraitError> {
self.0.read().raw_public_keys(key_type).map_err(|e| e.into())
}
fn has_keys(&self, public_keys: &[(Vec<u8>, KeyTypeId)]) -> bool {
public_keys
.iter()
.all(|(p, t)| self.0.read().key_phrase_by_type(p, *t).ok().flatten().is_some())
}
fn sr25519_vrf_sign(
&self,
key_type: KeyTypeId,
public: &Sr25519Public,
transcript_data: VRFTranscriptData,
) -> std::result::Result<Option<VRFSignature>, TraitError> {
let transcript = make_transcript(transcript_data);
let pair = self.0.read().key_pair_by_type::<Sr25519Pair>(public, key_type)?;
if let Some(pair) = pair {
let (inout, proof, _) = pair.as_ref().vrf_sign(transcript);
Ok(Some(VRFSignature { output: inout.to_output(), proof }))
} else {
Ok(None)
}
}
fn ecdsa_sign_prehashed(
&self,
id: KeyTypeId,
public: &ecdsa::Public,
msg: &[u8; 32],
) -> std::result::Result<Option<ecdsa::Signature>, TraitError> {
let pair = self.0.read().key_pair_by_type::<ecdsa::Pair>(public, id)?;
pair.map(|k| k.sign_prehashed(msg)).map(Ok).transpose()
}
}
impl Into<KeystorePtr> for LocalKeystore {
@@ -305,7 +298,12 @@ impl KeystoreInner {
/// Insert the given public/private key pair with the given key type.
///
/// Does not place it into the file system store.
fn insert_ephemeral_pair<Pair: PairT>(&mut self, pair: &Pair, seed: &str, key_type: KeyTypeId) {
fn insert_ephemeral_pair<Pair: CorePair>(
&mut self,
pair: &Pair,
seed: &str,
key_type: KeyTypeId,
) {
let key = (key_type, pair.public().to_raw_vec());
self.additional.insert(key, seed.into());
}
@@ -325,7 +323,7 @@ impl KeystoreInner {
///
/// Places it into the file system store, if a path is configured. Otherwise insert
/// it into the memory cache only.
fn generate_by_type<Pair: PairT>(&mut self, key_type: KeyTypeId) -> Result<Pair> {
fn generate_by_type<Pair: CorePair>(&mut self, key_type: KeyTypeId) -> Result<Pair> {
let (pair, phrase, _) = Pair::generate_with_phrase(self.password());
if let Some(path) = self.key_file_path(pair.public().as_slice(), key_type) {
Self::write_to_file(path, &phrase)?;
@@ -354,7 +352,7 @@ impl KeystoreInner {
/// Create a new key from seed.
///
/// Does not place it into the file system store.
fn insert_ephemeral_from_seed_by_type<Pair: PairT>(
fn insert_ephemeral_from_seed_by_type<Pair: CorePair>(
&mut self,
seed: &str,
key_type: KeyTypeId,
@@ -386,7 +384,7 @@ impl KeystoreInner {
}
/// Get a key pair for the given public key and key type.
fn key_pair_by_type<Pair: PairT>(
fn key_pair_by_type<Pair: CorePair>(
&self,
public: &Pair::Public,
key_type: KeyTypeId,
@@ -418,12 +416,12 @@ impl KeystoreInner {
}
/// Returns a list of raw public keys filtered by `KeyTypeId`
fn raw_public_keys(&self, id: KeyTypeId) -> Result<Vec<Vec<u8>>> {
fn raw_public_keys(&self, key_type: 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 })
.filter_map(|k| if k.0 == key_type { Some(k.1.clone()) } else { None })
.collect();
if let Some(path) = &self.path {
@@ -435,7 +433,7 @@ impl KeystoreInner {
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
match array_bytes::hex2bytes(name) {
Ok(ref hex) if hex.len() > 4 => {
if hex[0..4] != id.0 {
if hex[0..4] != key_type.0 {
continue
}
let public = hex[4..].to_vec();
@@ -456,7 +454,7 @@ impl KeystoreInner {
/// when something failed.
pub fn key_pair<Pair: AppPair>(
&self,
public: &<Pair as AppKey>::Public,
public: &<Pair as AppCrypto>::Public,
) -> Result<Option<Pair>> {
self.key_pair_by_type::<Pair::Generic>(IsWrappedBy::from_ref(public), Pair::ID)
.map(|v| v.map(Into::into))