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

Introduce sign_with method in keystore #4925

Merged
merged 73 commits into from
Mar 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
ad44d72
Add KEY_KIND_ID to the public trait
rakanalh Feb 17, 2020
3199d1e
Use `sign_with` as implemented in BareCryptoStore
rakanalh Feb 17, 2020
25f4a5c
Implement `sign_with` in sc_keystore
rakanalh Feb 17, 2020
8cc6dfd
Fix inconsistencies, use *_KIND_ID in sp_core testing
rakanalh Feb 17, 2020
4895e80
Rename KeyKindId to CryptoTypeId
rakanalh Feb 18, 2020
42f426d
Remove pair-returning functions from BareCryptoStore trait
rakanalh Feb 19, 2020
35555ff
Define CryptoTypeId in app-crypto macros
rakanalh Feb 19, 2020
d7623c7
Add functions to get keys supported by keystore
rakanalh Feb 19, 2020
01af73e
Fix sign_with signature to include CryptoTypePublicPair
rakanalh Feb 19, 2020
97775db
Add `sign_with_any` and `sign_with_all`
rakanalh Feb 19, 2020
1ee13ac
Use keystore.sign_with in auth_discovery
rakanalh Feb 19, 2020
ccec7f5
Rename get_supported_keys -> supported_keys
rakanalh Feb 19, 2020
5f59041
Added headers to function docstrings
rakanalh Feb 19, 2020
b588f7f
Use chain instead of extending a temp vector
rakanalh Feb 19, 2020
94e45a9
Fixed some code formatting
rakanalh Feb 19, 2020
e19ecf6
Restrict size of CryptoTypeId
rakanalh Feb 19, 2020
12836f4
Implement sign_with in the trait itself
rakanalh Feb 19, 2020
d6082cc
Remove whitespace
rakanalh Feb 19, 2020
ef029eb
Use key_type also as a CryptoTypeId in app_crypto macros
rakanalh Feb 19, 2020
c9680e3
Rename `get_keys` to `keys` in BareCryptoStore
rakanalh Feb 20, 2020
9dd7929
Remove usage of key_pair funcs in tests
rakanalh Feb 20, 2020
4d0b81b
Adjust docstring for *_CYPTO_ID constants
rakanalh Feb 20, 2020
0956ef7
Fix failures
rakanalh Feb 20, 2020
02f7099
Simplify mapping on keys
rakanalh Feb 20, 2020
79290de
Remove one let
rakanalh Feb 20, 2020
1ff324b
Merge remote-tracking branch 'upstream/master' into sign_with
rakanalh Feb 20, 2020
6a06530
Fixed typo
rakanalh Feb 20, 2020
6c37161
PR feedback
rakanalh Feb 20, 2020
ea81fba
remove whitespace
rakanalh Feb 20, 2020
118a53c
Zip keys and signatures
rakanalh Feb 20, 2020
eaa1c80
Use into_iter & remove cloned
rakanalh Feb 20, 2020
bf42dec
Pass index to MissingSignature
rakanalh Feb 20, 2020
17dd9a2
Use typed errors instead of strings for BareCryptoStore
rakanalh Feb 20, 2020
25c9bdd
Implement Debug for trait error
rakanalh Feb 20, 2020
b6de5e6
Use hashsets for better performance for supported_keys
rakanalh Feb 21, 2020
6cad7b9
Make sure keys are inserted into the keystore
rakanalh Feb 21, 2020
87e13fa
Make sign_with_all return type consistent with `sign_with`
rakanalh Feb 21, 2020
a7bfe13
Rename Error to BareCryptoStoreError
rakanalh Feb 21, 2020
7d38383
Rename CRYPT_TYPE_ID -> CRYPTO_ID
rakanalh Feb 21, 2020
321f16c
Remove unnecessary CRYPTO_ID declaration in Public trait
rakanalh Feb 21, 2020
69f474e
Convert pub key to CryptoTypePublicPair
rakanalh Feb 21, 2020
164d16b
Fix use
rakanalh Feb 21, 2020
c120f12
Fix code style
rakanalh Feb 21, 2020
fe8cf28
Implement From on CryptoTypePublicPair in app_crypto macros
rakanalh Feb 21, 2020
19c7499
Change CryptoTypePublicPair to a struct
rakanalh Feb 26, 2020
499c1bb
Implement Display on CryptoTypePublicPair
rakanalh Feb 26, 2020
b42af41
Pass CryptoTypePublicPair to MissingSignature error
rakanalh Feb 26, 2020
fc40b1e
Adjust docs according to function signature
rakanalh Feb 26, 2020
bca2c90
Unify keys implementation
rakanalh Feb 26, 2020
79b7e23
Fix RPC author tests
rakanalh Feb 26, 2020
31ae7ce
Fix stackoverflow
rakanalh Feb 26, 2020
6696aa0
Merge remote-tracking branch 'upstream/master' into sign_with
rakanalh Feb 26, 2020
5de1d04
Tabify spaces
rakanalh Mar 13, 2020
47f6c08
Pass KeyTypeId to error for easier debugging
rakanalh Mar 13, 2020
1674645
Fix asserts
rakanalh Mar 13, 2020
94651fa
Use ToHex to format public key
rakanalh Mar 13, 2020
c71646a
Use constants from sp_core
rakanalh Mar 13, 2020
46d6f23
Rename testing KeyTypeId constants
rakanalh Mar 13, 2020
f4d231e
Merge remote-tracking branch 'upstream/master' into sign_with
rakanalh Mar 13, 2020
bc0880b
Please compiler
rakanalh Mar 13, 2020
c8b721c
Restore KeyTypeId names
rakanalh Mar 13, 2020
fbfd23b
Use BareCryptoStoreError instead of String
rakanalh Mar 13, 2020
cac19ad
Document return value
rakanalh Mar 13, 2020
d5f4c4b
Fix borrow check
rakanalh Mar 13, 2020
c007d13
Convert to hashset internally
rakanalh Mar 20, 2020
9874c96
WIP - iter_keys
rakanalh Mar 20, 2020
f6be826
Return raw_public_keys
rakanalh Mar 20, 2020
0c012e7
Address PR feedback
rakanalh Mar 23, 2020
145d876
Merge remote-tracking branch 'upstream/master' into sign_with
rakanalh Mar 23, 2020
9e80c07
Address PR Feedback
rakanalh Mar 23, 2020
7b92399
Fix hexdisplay import error
rakanalh Mar 23, 2020
7908f71
Update primitives/core/src/traits.rs
bkchr Mar 23, 2020
5f9ad09
Merge remote-tracking branch 'upstream/master' into sign_with2
rakanalh Mar 30, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions client/authority-discovery/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

//! Authority discovery errors.

use sp_core::crypto::CryptoTypePublicPair;

/// AuthorityDiscovery Result.
pub type Result<T> = std::result::Result<T, Error>;

Expand Down Expand Up @@ -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),
}
42 changes: 22 additions & 20 deletions client/authority-discovery/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
rakanalh marked this conversation as resolved.
Show resolved Hide resolved
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,
);
}
Expand Down Expand Up @@ -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
Expand Down
172 changes: 124 additions & 48 deletions client/keystore/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand All @@ -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 {
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand All @@ -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<(), ()>
{
Expand All @@ -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() {
Expand Down Expand Up @@ -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(),
);
}
}
Loading