From a323979e593738392a9ca96819ee74409b8521d9 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Tue, 31 Dec 2019 15:10:05 -0500 Subject: [PATCH] [WIP] ECDSA (secp256k1) key support Support for ECDSA (secp256k1) account keys, with the goal of using them to support KMS-backed Cosmos transaction signing (#386) --- src/chain/registry.rs | 2 +- src/keyring.rs | 95 ++++++++++++++++++++++++++++------- src/keyring/ecdsa.rs | 7 +++ src/keyring/ecdsa/signer.rs | 57 +++++++++++++++++++++ src/keyring/ed25519/signer.rs | 2 +- src/session.rs | 2 +- 6 files changed, 144 insertions(+), 21 deletions(-) create mode 100644 src/keyring/ecdsa.rs create mode 100644 src/keyring/ecdsa/signer.rs diff --git a/src/chain/registry.rs b/src/chain/registry.rs index 9456aaf..a10f837 100644 --- a/src/chain/registry.rs +++ b/src/chain/registry.rs @@ -35,7 +35,7 @@ impl Registry { ) })?; - chain.keyring.add(signer) + chain.keyring.add_ed25519(signer) } /// Register a `Chain` with the registry diff --git a/src/keyring.rs b/src/keyring.rs index ef776a0..0258c3a 100644 --- a/src/keyring.rs +++ b/src/keyring.rs @@ -1,10 +1,10 @@ //! Signing keyring. Presently specialized for Ed25519. +pub mod ecdsa; pub mod ed25519; pub mod format; pub mod providers; -use self::ed25519::Signer; pub use self::{format::Format, providers::SigningProvider}; use crate::{ chain, @@ -12,7 +12,7 @@ use crate::{ error::{Error, ErrorKind::*}, prelude::*, }; -use std::collections::BTreeMap; +use std::collections::BTreeMap as Map; use subtle_encoding; use tendermint::TendermintKey; @@ -21,8 +21,11 @@ pub type SecretKeyEncoding = subtle_encoding::Base64; /// Signing keyring pub struct KeyRing { - /// Keys in the keyring - keys: BTreeMap, + /// ECDSA (secp256k1) keys in the keyring + ecdsa_keys: Map, + + /// Ed25519 keys in the keyring + ed25519_keys: Map, /// Formatting configuration when displaying keys (e.g. bech32) format: Format, @@ -32,14 +35,44 @@ impl KeyRing { /// Create a new keyring pub fn new(format: Format) -> Self { Self { - keys: BTreeMap::new(), + ecdsa_keys: Map::new(), + ed25519_keys: Map::new(), format, } } - /// Add a key to the keyring, returning an error if we already have a - /// signer registered for the given public key - pub fn add(&mut self, signer: Signer) -> Result<(), Error> { + /// Add an ECDSA key to the keyring, returning an error if we already + /// have a signer registered for the given public key. + pub fn add_ecdsa(&mut self, signer: ecdsa::Signer) -> Result<(), Error> { + let provider = signer.provider(); + let public_key = signer.public_key(); + let public_key_serialized = self.format.serialize(public_key); + let key_type = match public_key { + TendermintKey::AccountKey(_) => "account", + TendermintKey::ConsensusKey(_) => "consensus", + }; + + info!( + "[keyring:{}] added ECDSA {} key {}", + provider, key_type, public_key_serialized + ); + + if let Some(other) = self.ecdsa_keys.insert(public_key, signer) { + fail!( + InvalidKey, + "[keyring:{}] duplicate ECDSA key {} already registered as {}", + provider, + public_key_serialized, + other.provider(), + ) + } else { + Ok(()) + } + } + + /// Add an Ed25519 key to the keyring, returning an error if we already + /// have a signer registered for the given public key. + pub fn add_ed25519(&mut self, signer: ed25519::Signer) -> Result<(), Error> { let provider = signer.provider(); let public_key = signer.public_key(); let public_key_serialized = self.format.serialize(public_key); @@ -49,14 +82,14 @@ impl KeyRing { }; info!( - "[keyring:{}] added {} key {}", + "[keyring:{}] added Ed25519 {} key {}", provider, key_type, public_key_serialized ); - if let Some(other) = self.keys.insert(public_key, signer) { + if let Some(other) = self.ed25519_keys.insert(public_key, signer) { fail!( InvalidKey, - "[keyring:{}] duplicate key {} already registered as {}", + "[keyring:{}] duplicate Ed25519 key {} already registered as {}", provider, public_key_serialized, other.provider(), @@ -66,9 +99,9 @@ impl KeyRing { } } - /// Get the default public key for this keyring - pub fn default_pubkey(&self) -> Result { - let mut keys = self.keys.keys(); + /// Get the default Ed25519 public key for this keyring + pub fn default_ed25519_pubkey(&self) -> Result { + let mut keys = self.ed25519_keys.keys(); if keys.len() == 1 { Ok(*keys.next().unwrap()) @@ -77,19 +110,45 @@ impl KeyRing { } } - /// Sign a message using the secret key associated with the given public key - /// (if it is in our keyring) + /// Sign a message using the ECDSA secret key associated with the given + /// public key (if it is in our keyring) + pub fn sign_ecdsa( + &self, + public_key: Option<&TendermintKey>, + msg: &[u8], + ) -> Result { + let signer = match public_key { + Some(public_key) => self.ecdsa_keys.get(public_key).ok_or_else(|| { + format_err!(InvalidKey, "not in keyring: {}", public_key.to_bech32("")) + })?, + None => { + let mut vals = self.ecdsa_keys.values(); + + if vals.len() > 1 { + fail!(SigningError, "expected only one key in keyring"); + } else { + vals.next() + .ok_or_else(|| format_err!(InvalidKey, "keyring is empty"))? + } + } + }; + + signer.sign(msg) + } + + /// Sign a message using the Ed25519 secret key associated with the given + /// public key (if it is in our keyring) pub fn sign_ed25519( &self, public_key: Option<&TendermintKey>, msg: &[u8], ) -> Result { let signer = match public_key { - Some(public_key) => self.keys.get(public_key).ok_or_else(|| { + Some(public_key) => self.ed25519_keys.get(public_key).ok_or_else(|| { format_err!(InvalidKey, "not in keyring: {}", public_key.to_bech32("")) })?, None => { - let mut vals = self.keys.values(); + let mut vals = self.ed25519_keys.values(); if vals.len() > 1 { fail!(SigningError, "expected only one key in keyring"); diff --git a/src/keyring/ecdsa.rs b/src/keyring/ecdsa.rs new file mode 100644 index 0000000..79b8e6e --- /dev/null +++ b/src/keyring/ecdsa.rs @@ -0,0 +1,7 @@ +//! ECDSA (secp256k1) signing keys + +pub use signatory::ecdsa::curve::secp256k1::{FixedSignature as Signature, PublicKey}; + +pub mod signer; + +pub use self::signer::Signer; diff --git a/src/keyring/ecdsa/signer.rs b/src/keyring/ecdsa/signer.rs new file mode 100644 index 0000000..4c8e064 --- /dev/null +++ b/src/keyring/ecdsa/signer.rs @@ -0,0 +1,57 @@ +//! ECDSA (secp256k1) signer + +use super::Signature; +use crate::{ + error::{Error, ErrorKind::*}, + keyring::SigningProvider, + prelude::*, +}; +use signatory::signature; +use std::sync::Arc; +use tendermint::TendermintKey; + +/// Trait object wrapper for an Ed25519 signers +#[derive(Clone)] +pub struct Signer { + /// Provider for this signer + provider: SigningProvider, + + /// Tendermint public key + public_key: TendermintKey, + + /// Signer trait object + signer: Arc + Send + Sync>>, +} + +impl Signer { + /// Create a new signer + pub fn new( + provider: SigningProvider, + public_key: TendermintKey, + signer: Box + Send + Sync>, + ) -> Self { + Self { + provider, + public_key, + signer: Arc::new(signer), + } + } + + /// Get the Tendermint public key for this signer + pub fn public_key(&self) -> TendermintKey { + self.public_key + } + + /// Get the provider for this signer + pub fn provider(&self) -> SigningProvider { + self.provider + } + + /// Sign the given message using this signer + pub fn sign(&self, msg: &[u8]) -> Result { + Ok(self + .signer + .try_sign(msg) + .map_err(|e| format_err!(SigningError, "{}", e))?) + } +} diff --git a/src/keyring/ed25519/signer.rs b/src/keyring/ed25519/signer.rs index 639be2c..c104148 100644 --- a/src/keyring/ed25519/signer.rs +++ b/src/keyring/ed25519/signer.rs @@ -1,4 +1,4 @@ -//! Wrapper for Ed25519 signers +//! Ed25519 signer use crate::{ error::{Error, ErrorKind::*}, diff --git a/src/session.rs b/src/session.rs index c41a807..c8f159a 100644 --- a/src/session.rs +++ b/src/session.rs @@ -194,7 +194,7 @@ impl Session { let chain = registry.get_chain(&self.config.chain_id).unwrap(); Ok(Response::PublicKey(PubKeyResponse::from( - *chain.keyring.default_pubkey()?, + *chain.keyring.default_ed25519_pubkey()?, ))) }