Skip to content
This repository has been archived by the owner on Jun 3, 2020. It is now read-only.

Commit

Permalink
[WIP] ECDSA (secp256k1) key support
Browse files Browse the repository at this point in the history
Support for ECDSA (secp256k1) account keys, with the goal of using them
to support KMS-backed Cosmos transaction signing (#386)
  • Loading branch information
tony-iqlusion committed Dec 31, 2019
1 parent 30a7550 commit a323979
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 21 deletions.
2 changes: 1 addition & 1 deletion src/chain/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl Registry {
)
})?;

chain.keyring.add(signer)
chain.keyring.add_ed25519(signer)
}

/// Register a `Chain` with the registry
Expand Down
95 changes: 77 additions & 18 deletions src/keyring.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
//! 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,
config::provider::ProviderConfig,
error::{Error, ErrorKind::*},
prelude::*,
};
use std::collections::BTreeMap;
use std::collections::BTreeMap as Map;
use subtle_encoding;
use tendermint::TendermintKey;

Expand All @@ -21,8 +21,11 @@ pub type SecretKeyEncoding = subtle_encoding::Base64;

/// Signing keyring
pub struct KeyRing {
/// Keys in the keyring
keys: BTreeMap<TendermintKey, Signer>,
/// ECDSA (secp256k1) keys in the keyring
ecdsa_keys: Map<TendermintKey, ecdsa::Signer>,

/// Ed25519 keys in the keyring
ed25519_keys: Map<TendermintKey, ed25519::Signer>,

/// Formatting configuration when displaying keys (e.g. bech32)
format: Format,
Expand All @@ -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);
Expand All @@ -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(),
Expand All @@ -66,9 +99,9 @@ impl KeyRing {
}
}

/// Get the default public key for this keyring
pub fn default_pubkey(&self) -> Result<TendermintKey, Error> {
let mut keys = self.keys.keys();
/// Get the default Ed25519 public key for this keyring
pub fn default_ed25519_pubkey(&self) -> Result<TendermintKey, Error> {
let mut keys = self.ed25519_keys.keys();

if keys.len() == 1 {
Ok(*keys.next().unwrap())
Expand All @@ -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<ecdsa::Signature, Error> {
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<ed25519::Signature, Error> {
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");
Expand Down
7 changes: 7 additions & 0 deletions src/keyring/ecdsa.rs
Original file line number Diff line number Diff line change
@@ -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;
57 changes: 57 additions & 0 deletions src/keyring/ecdsa/signer.rs
Original file line number Diff line number Diff line change
@@ -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<Box<dyn signature::Signer<Signature> + Send + Sync>>,
}

impl Signer {
/// Create a new signer
pub fn new(
provider: SigningProvider,
public_key: TendermintKey,
signer: Box<dyn signature::Signer<Signature> + 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<Signature, Error> {
Ok(self
.signer
.try_sign(msg)
.map_err(|e| format_err!(SigningError, "{}", e))?)
}
}
2 changes: 1 addition & 1 deletion src/keyring/ed25519/signer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Wrapper for Ed25519 signers
//! Ed25519 signer
use crate::{
error::{Error, ErrorKind::*},
Expand Down
2 changes: 1 addition & 1 deletion src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()?,
)))
}

Expand Down

0 comments on commit a323979

Please sign in to comment.