From ce31ad7ad34a3b088a6e088bc7e1b1ca6f0a0851 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sat, 9 Mar 2019 13:51:04 -0800 Subject: [PATCH] Initial chain registry (fixes #178) Adds a `chain::REGISTRY` (as a `lazy_static`) to tmkms, populated from the configuration file. Uses this registry when serializing keys to select the appropriate serialization format. --- Cargo.lock | 16 +- README.yubihsm.md | 5 +- src/chain/chain.rs | 1 + src/chain/guard.rs | 19 +++ src/chain/key.rs | 47 ++++++ src/chain/mod.rs | 27 ++++ src/chain/registry.rs | 42 +++++ src/commands/start.rs | 20 ++- src/commands/yubihsm/keys/generate.rs | 94 +++++++----- src/commands/yubihsm/keys/import.rs | 9 +- src/commands/yubihsm/keys/list.rs | 11 +- src/config/chain.rs | 13 ++ src/config/mod.rs | 7 +- src/config/provider/ledgertm.rs | 7 +- src/config/provider/softsign.rs | 5 +- src/config/provider/yubihsm.rs | 5 +- src/keyring/ed25519/ledgertm.rs | 28 ++-- src/keyring/ed25519/mod.rs | 2 +- src/keyring/ed25519/signer.rs | 42 +++-- src/keyring/ed25519/softsign.rs | 14 +- src/keyring/ed25519/yubihsm.rs | 18 ++- src/keyring/mod.rs | 68 +++++--- src/keyring/providers.rs | 32 ++++ src/lib.rs | 1 + src/session.rs | 13 +- tendermint-rs/Cargo.toml | 2 +- tendermint-rs/src/public_keys.rs | 145 +++++++++--------- tests/integration.rs | 12 +- .../support/gen-validator-integration-cfg.sh | 7 +- tests/support/kms_yubihsm_mock.toml | 7 +- tmkms.toml.example | 18 ++- 31 files changed, 508 insertions(+), 229 deletions(-) create mode 100644 src/chain/chain.rs create mode 100644 src/chain/guard.rs create mode 100644 src/chain/key.rs create mode 100644 src/chain/mod.rs create mode 100644 src/chain/registry.rs create mode 100644 src/config/chain.rs create mode 100644 src/keyring/providers.rs diff --git a/Cargo.lock b/Cargo.lock index ff93602..d67814d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -969,7 +969,7 @@ dependencies = [ [[package]] name = "signatory" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -988,7 +988,7 @@ dependencies = [ "digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "ed25519-dalek 1.0.0-pre.1 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "signatory 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "signatory 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -999,7 +999,7 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "ledger-tendermint 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "signatory 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "signatory 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1008,7 +1008,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "secp256k1 0.12.2 (registry+https://github.com/rust-lang/crates.io-index)", - "signatory 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "signatory 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1131,7 +1131,7 @@ dependencies = [ "serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "signatory 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "signatory 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "signatory-dalek 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "subtle-encoding 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "tai64 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1196,7 +1196,7 @@ dependencies = [ "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "signal-hook 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "signatory 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "signatory 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "signatory-dalek 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "signatory-ledger-tm 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "signatory-secp256k1 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1303,7 +1303,7 @@ dependencies = [ "serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "signatory 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)", + "signatory 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "subtle 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "untrusted 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1439,7 +1439,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" "checksum sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4d8bfd0e469f417657573d8451fb33d16cfe0989359b93baf3a1ffc639543d" "checksum signal-hook 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "97a47ae722318beceb0294e6f3d601205a1e6abaa4437d9d33e3a212233e3021" -"checksum signatory 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ac100d1d4652d2a31be39a392aef8dd8b17284df088bcfcfd795be3bacbedfed" +"checksum signatory 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ef11bf26bfda8c4d5d23add3a59deb793138e0396465c7e6fadd5839572f8e2" "checksum signatory-dalek 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b5ed7678eaeb98cb23e1efdb5e961021b02d3bd9f8bab4d4e30c53ebb3dd50" "checksum signatory-ledger-tm 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2633f1e0a241347d31f2031b6053506dd72d9c5dedf06fe231526f049e4d1ed" "checksum signatory-secp256k1 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "57765ca2c5615453b345477546ddc697921f3c8fc71fd1ffc4edc2ffa0453f1d" diff --git a/README.yubihsm.md b/README.yubihsm.md index 57d075f..456f549 100644 --- a/README.yubihsm.md +++ b/README.yubihsm.md @@ -312,7 +312,7 @@ Below is an example of the command to generate and export an encrypted backup of an Ed25519 signing key: ``` -$ tmkms yubihsm keys generate 1 -l "steakz4u-validator:2019-03-06T01:25:39Z" -b steakz4u-validator-key.enc +$ tmkms yubihsm keys generate 1 -p cosmosvalconspub -b steakz4u-validator-key.enc Generated key #1: cosmosvalconspub1zcjduepqtvzxa733n7dhrjf247n0jtdwsvvsd4jgqvzexj5tkwerpzy5sugsvmfja3 Wrote backup of key 1 (encrypted under wrap key 1) to steakz4u-validator-key.enc ``` @@ -327,6 +327,7 @@ the following sets of credentials: - `tmkms yubihsm keys generate 1` - generates asymmetric key 0x0001, which is by default an Ed25519 signing key. +- `-p` (or `--prefix`): Bech32 prefix to serialize key with (automatically sets label) - `-l` (or `--label`): an up-to-40-character label describing the key - `-b` (or `--backup`): path to a file where an *encrypted* backup of the generated key should be written @@ -342,7 +343,7 @@ The following command lists keys in the HSM: ``` $ tmkms yubihsm keys list Listing keys in YubiHSM #9876543211: -- #1: cosmosvalconspub1zcjduepqtvzxa733n7dhrjf247n0jtdwsvvsd4jgqvzexj5tkwerpzy5sugsvmfja3 +- 0x#0001: 1624DE64200FB6DB3175225219D290497E3B78190A3EEDA89AEBBC2E2294547CA98E76F9D5 ``` ## Exporting and Importing Keys diff --git a/src/chain/chain.rs b/src/chain/chain.rs new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/chain/chain.rs @@ -0,0 +1 @@ + diff --git a/src/chain/guard.rs b/src/chain/guard.rs new file mode 100644 index 0000000..be5d17b --- /dev/null +++ b/src/chain/guard.rs @@ -0,0 +1,19 @@ +use super::{Chain, Id}; +use std::{collections::BTreeMap, sync::RwLockReadGuard}; + +/// Wrapper for a `RwLockReadGuard<'static, BTreeMap>`, allowing access to +/// global information about particular Tendermint networks / "chains" +pub struct Guard<'lock>(RwLockReadGuard<'lock, BTreeMap>); + +impl<'lock> From>> for Guard<'lock> { + fn from(guard: RwLockReadGuard<'lock, BTreeMap>) -> Guard<'lock> { + Guard(guard) + } +} + +impl<'lock> Guard<'lock> { + /// Get information about a particular chain ID (if registered) + pub fn chain(&self, chain_id: Id) -> Option<&Chain> { + self.0.get(&chain_id) + } +} diff --git a/src/chain/key.rs b/src/chain/key.rs new file mode 100644 index 0000000..033aff5 --- /dev/null +++ b/src/chain/key.rs @@ -0,0 +1,47 @@ +//! Chain-specific key configuration + +use super::{Id, REGISTRY}; +use tendermint::TendermintKey; + +/// Options for how keys for this chain are represented +#[derive(Clone, Debug, Deserialize)] +#[serde(tag = "type")] +pub enum Format { + /// Use the Bech32 serialization format with the given key prefixes + #[serde(rename = "bech32")] + Bech32 { + /// Prefix to use for Account keys + account_key_prefix: String, + + /// Prefix to use for Consensus keys + consensus_key_prefix: String, + }, + + /// Hex is a baseline representation + #[serde(rename = "hex")] + Hex, +} + +impl Format { + /// Serialize a `TendermintKey` according to chain-specific rules + pub fn serialize(&self, public_key: TendermintKey) -> String { + match self { + Format::Bech32 { + account_key_prefix, + consensus_key_prefix, + } => match public_key { + TendermintKey::AccountKey(pk) => pk.to_bech32(account_key_prefix), + TendermintKey::ConsensusKey(pk) => pk.to_bech32(consensus_key_prefix), + }, + Format::Hex => public_key.to_hex(), + } + } +} + +/// Serialize a key according to chain-specific serialization rules +pub fn serialize(chain_id: Id, public_key: TendermintKey) -> Option { + let registry = REGISTRY.get(); + registry + .chain(chain_id) + .map(|chain| chain.key_format.serialize(public_key)) +} diff --git a/src/chain/mod.rs b/src/chain/mod.rs new file mode 100644 index 0000000..f767519 --- /dev/null +++ b/src/chain/mod.rs @@ -0,0 +1,27 @@ +//! Information about particular Tendermint blockchain networks + +mod guard; +pub mod key; +mod registry; + +pub use self::{guard::Guard, registry::REGISTRY}; +use crate::config::chain::ChainConfig; +pub use tendermint::chain::Id; + +/// Information about a particular Tendermint blockchain network +pub struct Chain { + /// ID of a particular chain + pub id: Id, + + /// Key format configuration + pub key_format: key::Format, +} + +impl<'a> From<&ChainConfig> for Chain { + fn from(config: &ChainConfig) -> Chain { + Self { + id: config.id, + key_format: config.key_format.clone(), + } + } +} diff --git a/src/chain/registry.rs b/src/chain/registry.rs new file mode 100644 index 0000000..ee2cc2c --- /dev/null +++ b/src/chain/registry.rs @@ -0,0 +1,42 @@ +//! Registry of information about known Tendermint blockchain networks + +use super::{Chain, Guard, Id}; +use crate::error::{KmsError, KmsErrorKind::ConfigError}; +use std::{collections::BTreeMap, sync::RwLock}; + +lazy_static! { + pub static ref REGISTRY: Registry = Registry::default(); +} + +/// Registry of blockchain networks known to the KMS +// The `RwLock` is a bit of futureproofing as this data structure is for the +// most part "immutable". New chains should be registered at boot time. +// The only case in which this structure may change is in the event of +// runtime configuration reloading, so the `RwLock` is included as +// futureproofing for such a feature. +// See: +#[derive(Default)] +pub struct Registry(RwLock>); + +impl Registry { + /// Acquire a read-only (concurrent) lock to the internal chain registry + pub fn get(&self) -> Guard { + // TODO(tarcieri): better handle `PoisonError` here? + self.0.read().unwrap().into() + } + + /// Register a chain with the registry + pub fn register(&self, chain: Chain) -> Result<(), KmsError> { + // TODO(tarcieri): better handle `PoisonError` here? + let mut chain_map = self.0.write().unwrap(); + + let chain_id = chain.id; + + if chain_map.insert(chain_id, chain).is_none() { + Ok(()) + } else { + // TODO(tarcieri): handle updating the set of registered chains + fail!(ConfigError, "chain ID already registered: {}", chain_id); + } + } +} diff --git a/src/commands/start.rs b/src/commands/start.rs index 36465a1..f02353c 100644 --- a/src/commands/start.rs +++ b/src/commands/start.rs @@ -1,14 +1,18 @@ -use abscissa::{Callable, GlobalConfig}; -use std::process; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; -use std::{thread, time}; - use crate::{ + chain, client::Client, config::{KmsConfig, ValidatorConfig}, keyring::KeyRing, }; +use abscissa::{Callable, GlobalConfig}; +use std::{ + process, + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + thread, time, +}; /// The `start` command #[derive(Debug, Options)] @@ -42,6 +46,10 @@ impl Callable for StartCommand { let config = KmsConfig::get_global(); + for chain_config in &config.chain { + chain::REGISTRY.register(chain_config.into()).unwrap(); + } + KeyRing::load_from_config(&config.providers).unwrap_or_else(|e| { status_err!("couldn't load keyring: {}", e); process::exit(1); diff --git a/src/commands/yubihsm/keys/generate.rs b/src/commands/yubihsm/keys/generate.rs index 9de4301..749e15f 100644 --- a/src/commands/yubihsm/keys/generate.rs +++ b/src/commands/yubihsm/keys/generate.rs @@ -1,6 +1,6 @@ use super::*; use abscissa::Callable; -use signatory::ed25519; +use chrono::{SecondsFormat, Utc}; use std::{ fs::OpenOptions, io::Write, @@ -9,7 +9,7 @@ use std::{ process, }; use subtle_encoding::base64; -use tendermint::public_keys::ConsensusKey; +use tendermint::PublicKey; /// The `yubihsm keys generate` subcommand #[derive(Debug, Default, Options)] @@ -22,6 +22,10 @@ pub struct GenerateCommand { #[options(short = "l", long = "label")] pub label: Option, + /// Bech32 prefix to use when displaying key + #[options(short = "p", long = "prefix")] + pub bech32_prefix: Option, + /// Type of key to generate (default 'ed25519') #[options(short = "t")] pub key_type: Option, @@ -38,7 +42,7 @@ pub struct GenerateCommand { #[options(short = "w", long = "wrapkey")] pub wrap_key_id: Option, - /// Key IDs to generate + /// Key ID to generate #[options(free)] key_ids: Vec, } @@ -46,11 +50,16 @@ pub struct GenerateCommand { impl Callable for GenerateCommand { /// Generate an Ed25519 signing key inside a YubiHSM2 device fn call(&self) { - if self.key_ids.is_empty() { - status_err!("must provide at least one key ID to generate"); + if self.key_ids.len() != 1 { + status_err!( + "expected exactly 1 key ID to generate, got {}", + self.key_ids.len() + ); process::exit(1); } + let key_id = self.key_ids[0]; + if let Some(key_type) = self.key_type.as_ref() { if key_type != DEFAULT_KEY_TYPE { status_err!( @@ -69,48 +78,53 @@ impl Callable for GenerateCommand { capabilities |= yubihsm::Capability::EXPORTABLE_UNDER_WRAP; } - for key_id in &self.key_ids { - let label = - yubihsm::object::Label::from(self.label.as_ref().map(|l| l.as_ref()).unwrap_or("")); - - if let Err(e) = hsm.generate_asymmetric_key( - *key_id, - label, - DEFAULT_DOMAINS, // TODO(tarcieri): customize domains - capabilities, - yubihsm::asymmetric::Algorithm::Ed25519, - ) { - status_err!("couldn't generate key #{}: {}", key_id, e); - process::exit(1); + let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true); + let label = yubihsm::object::Label::from( + match self.label { + Some(ref l) => l.to_owned(), + None => match self.bech32_prefix { + Some(ref prefix) => format!("{}:{}", prefix, timestamp), + None => format!("ed25519:{}", timestamp), + }, } + .as_ref(), + ); + + if let Err(e) = hsm.generate_asymmetric_key( + key_id, + label, + DEFAULT_DOMAINS, // TODO(tarcieri): customize domains + capabilities, + yubihsm::asymmetric::Algorithm::Ed25519, + ) { + status_err!("couldn't generate key #{}: {}", key_id, e); + process::exit(1); + } - let public_key = - ed25519::PublicKey::from_bytes(hsm.get_public_key(*key_id).unwrap_or_else(|e| { + let public_key = PublicKey::from_raw_ed25519( + hsm.get_public_key(key_id) + .unwrap_or_else(|e| { status_err!("couldn't get public key for key #{}: {}", key_id, e); process::exit(1); - })) - .unwrap(); + }) + .as_ref(), + ) + .unwrap(); - status_ok!( - "Generated", - "key #{}: {}", + let public_key_string = match self.bech32_prefix { + Some(ref prefix) => public_key.to_bech32(prefix), + None => public_key.to_hex(), + }; + + status_ok!("Generated", "key #{}: {}", key_id, public_key_string); + + if let Some(ref backup_file) = self.backup_file { + create_encrypted_backup( + &hsm, key_id, - ConsensusKey::from(public_key) + &backup_file, + self.wrap_key_id.unwrap_or(DEFAULT_WRAP_KEY), ); - - if let Some(ref backup_file) = self.backup_file { - assert_eq!( - self.key_ids.len(), - 1, - "can only create backups if generating one key at a time" - ); - create_encrypted_backup( - &hsm, - *key_id, - &backup_file, - self.wrap_key_id.unwrap_or(DEFAULT_WRAP_KEY), - ); - } } } } diff --git a/src/commands/yubihsm/keys/import.rs b/src/commands/yubihsm/keys/import.rs index 8c6b4a2..0de846c 100644 --- a/src/commands/yubihsm/keys/import.rs +++ b/src/commands/yubihsm/keys/import.rs @@ -3,7 +3,7 @@ use abscissa::Callable; use signatory::ed25519; use std::{fs, path::PathBuf, process}; use subtle_encoding::base64; -use tendermint::public_keys::ConsensusKey; +use tendermint::PublicKey; use yubihsm::object; /// The `yubihsm keys import` subcommand @@ -130,11 +130,12 @@ impl ImportCommand { process::exit(1); }); - // TODO: support for non-Cosmos keys, non-Ed25519 keys, non-validator (i.e. account) keys + // TODO: display non hex format when listing/displaying keys let key_info = match public_key.algorithm { yubihsm::asymmetric::Algorithm::Ed25519 => { - ConsensusKey::from(ed25519::PublicKey::from_bytes(&public_key.as_ref()).unwrap()) - .to_string() + PublicKey::from_raw_ed25519(&public_key.as_ref()) + .unwrap() + .to_hex() } alg => format!("{:?}: {:?}", alg, public_key.as_ref()), }; diff --git a/src/commands/yubihsm/keys/list.rs b/src/commands/yubihsm/keys/list.rs index 190f079..f50aab7 100644 --- a/src/commands/yubihsm/keys/list.rs +++ b/src/commands/yubihsm/keys/list.rs @@ -1,7 +1,6 @@ use abscissa::Callable; -use signatory::ed25519; use std::process; -use tendermint::public_keys::ConsensusKey; +use tendermint::PublicKey; /// The `yubihsm keys list` subcommand #[derive(Debug, Default, Options)] @@ -53,15 +52,15 @@ impl Callable for ListCommand { process::exit(1); }); - let key_id = format!("- #{}", key.object_id); + let key_id = format!("- 0x#{:04x}", key.object_id); // TODO: support for non-Ed25519 keys if public_key.algorithm == yubihsm::asymmetric::Algorithm::Ed25519 { status_attr_ok!( key_id, - ConsensusKey::from( - ed25519::PublicKey::from_bytes(&public_key.as_ref()).unwrap() - ) + PublicKey::from_raw_ed25519(&public_key.as_ref()) + .unwrap() + .to_hex() ); } else { status_attr_err!(key_id, "unsupported algorithm: {:?}", public_key.algorithm); diff --git a/src/config/chain.rs b/src/config/chain.rs new file mode 100644 index 0000000..5323db4 --- /dev/null +++ b/src/config/chain.rs @@ -0,0 +1,13 @@ +//! Chain configuration + +use crate::chain; + +/// Chain configuration +#[derive(Clone, Deserialize, Debug)] +pub struct ChainConfig { + /// Chain ID of this Tendermint network/chain + pub id: chain::Id, + + /// Key format configuration + pub key_format: chain::key::Format, +} diff --git a/src/config/mod.rs b/src/config/mod.rs index 614dad7..b662d04 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1,10 +1,11 @@ //! Configuration file structures (with serde-derived parser) +pub mod chain; pub mod provider; pub mod validator; -use self::provider::ProviderConfig; pub use self::validator::*; +use self::{chain::ChainConfig, provider::ProviderConfig}; /// Name of the KMS configuration file pub const CONFIG_FILE_NAME: &str = "tmkms.toml"; @@ -13,6 +14,10 @@ pub const CONFIG_FILE_NAME: &str = "tmkms.toml"; #[derive(Clone, Deserialize, Debug)] #[serde(deny_unknown_fields)] pub struct KmsConfig { + /// Chains the KMS is providing key management service for + #[serde(default)] + pub chain: Vec, + /// Addresses of validator nodes #[serde(default)] pub validator: Vec, diff --git a/src/config/provider/ledgertm.rs b/src/config/provider/ledgertm.rs index fbe5fab..13bc553 100644 --- a/src/config/provider/ledgertm.rs +++ b/src/config/provider/ledgertm.rs @@ -1,5 +1,10 @@ //! Configuration for Ledger Tendermint signer +use crate::chain; + /// Ledger Tendermint signer configuration #[derive(Clone, Deserialize, Debug)] -pub struct LedgerTendermintConfig {} +pub struct LedgerTendermintConfig { + /// Chains this signing key is authorized to be used from + pub chain_ids: Vec, +} diff --git a/src/config/provider/softsign.rs b/src/config/provider/softsign.rs index 60a95a0..c2286ef 100644 --- a/src/config/provider/softsign.rs +++ b/src/config/provider/softsign.rs @@ -1,12 +1,13 @@ //! Configuration for software-backed signer (using ed25519-dalek) +use crate::chain; use std::path::{Path, PathBuf}; /// Software signer configuration #[derive(Clone, Deserialize, Debug)] pub struct SoftSignConfig { - /// Identifier for this key - pub id: String, + /// Chains this signing key is authorized to be used from + pub chain_ids: Vec, /// Path to a file containing a cryptographic key // TODO: use `abscissa::Secret` to wrap this `PathBuf` diff --git a/src/config/provider/yubihsm.rs b/src/config/provider/yubihsm.rs index 88c0fce..cb26df9 100644 --- a/src/config/provider/yubihsm.rs +++ b/src/config/provider/yubihsm.rs @@ -1,5 +1,6 @@ //! Configuration for the `YubiHSM` backend +use crate::chain; use abscissa::{ secrets::{BorrowSecret, DebugSecret, Secret}, util::Zeroize, @@ -111,8 +112,8 @@ impl Zeroize for Password { #[derive(Clone, Debug, Deserialize)] pub struct SigningKeyConfig { - /// Identifier for this key - pub id: String, + /// Chains this signing key is authorized to be used from + pub chain_ids: Vec, /// Signing key ID pub key: u16, diff --git a/src/keyring/ed25519/ledgertm.rs b/src/keyring/ed25519/ledgertm.rs index 76cdf4f..22c9faa 100644 --- a/src/keyring/ed25519/ledgertm.rs +++ b/src/keyring/ed25519/ledgertm.rs @@ -1,16 +1,13 @@ //! Ledger Tendermint signer -use signatory::PublicKeyed; -use signatory_ledger_tm::{self, Ed25519LedgerTmAppSigner}; - use crate::{ config::provider::ledgertm::LedgerTendermintConfig, error::{KmsError, KmsErrorKind::*}, - keyring::{ed25519::Signer, KeyRing}, + keyring::{ed25519::Signer, KeyRing, SigningProvider}, }; - -pub const LEDGER_TM_PROVIDER_LABEL: &str = "ledgertm"; -pub const LEDGER_TM_ID: &str = "ledgertm"; +use signatory::PublicKeyed; +use signatory_ledger_tm::{self, Ed25519LedgerTmAppSigner}; +use tendermint::TendermintKey; /// Create Ledger Tendermint signer object from the given configuration pub fn init( @@ -28,10 +25,19 @@ pub fn init( ledgertm_configs.len() ); } + let provider = Box::new(Ed25519LedgerTmAppSigner::connect()?); - let pk = provider.public_key()?; - // TODO: key_id shouldn't be a constant here (see LEDGER_TM_ID): - let signer = Signer::new(LEDGER_TM_PROVIDER_LABEL, LEDGER_TM_ID.to_string(), provider); - keyring.add(pk, signer)?; + + // TODO(tarcieri): support for adding account keys into keyrings + let public_key = TendermintKey::ConsensusKey(provider.public_key()?.into()); + + let signer = Signer::new( + SigningProvider::LedgerTm, + &ledgertm_configs[0].chain_ids, + provider, + ); + + keyring.add(public_key, signer)?; + Ok(()) } diff --git a/src/keyring/ed25519/mod.rs b/src/keyring/ed25519/mod.rs index cce1eab..416dbd0 100644 --- a/src/keyring/ed25519/mod.rs +++ b/src/keyring/ed25519/mod.rs @@ -1,4 +1,4 @@ -pub use signatory::ed25519::{PublicKey, Seed, PUBLIC_KEY_SIZE}; +pub use signatory::ed25519::{PublicKey, Seed, Signature, PUBLIC_KEY_SIZE}; #[cfg(feature = "ledgertm")] pub mod ledgertm; diff --git a/src/keyring/ed25519/signer.rs b/src/keyring/ed25519/signer.rs index 3edb647..0a25f8d 100644 --- a/src/keyring/ed25519/signer.rs +++ b/src/keyring/ed25519/signer.rs @@ -1,38 +1,48 @@ -use crate::error::{KmsError, KmsErrorKind::*}; +use crate::{ + chain, + error::{KmsError, KmsErrorKind::*}, + keyring::SigningProvider, +}; use signatory::{self, ed25519::Signature, Signer as SignerTrait}; /// Wrapper for an Ed25519 signing provider (i.e. trait object) pub struct Signer { - /// Name of the signature provider for this key - pub provider_name: &'static str, + /// Provider for this signer + provider: SigningProvider, - /// ID which identifies this key (should be unique-per-provider) - pub key_id: String, + /// Chains this key is authorized to be used from + chain_ids: Vec, /// Signer trait object - provider: Box>, + signer: Box>, } impl Signer { /// Create a new signer pub fn new( - provider_name: &'static str, - key_id: String, - provider: Box>, + provider: SigningProvider, + chain_ids: &[chain::Id], + signer: Box>, ) -> Self { Self { - provider_name, - key_id, provider, + chain_ids: chain_ids.to_vec(), + signer, } } + /// Get the provider for this signer + pub fn provider(&self) -> SigningProvider { + self.provider + } + + /// Get the chains this signer is authorized to be used on + pub fn chain_ids(&self) -> &[chain::Id] { + &self.chain_ids + } + /// Sign the given message using this signer - #[inline] pub fn sign(&self, msg: &[u8]) -> Result { - Ok( - signatory::sign(self.provider.as_ref(), msg) - .map_err(|e| err!(SigningError, "{}", e))?, - ) + Ok(signatory::sign(self.signer.as_ref(), msg).map_err(|e| err!(SigningError, "{}", e))?) } } diff --git a/src/keyring/ed25519/softsign.rs b/src/keyring/ed25519/softsign.rs index 6041cf9..8bfb8e9 100644 --- a/src/keyring/ed25519/softsign.rs +++ b/src/keyring/ed25519/softsign.rs @@ -9,12 +9,9 @@ use super::Signer; use crate::{ config::provider::softsign::SoftSignConfig, error::{KmsError, KmsErrorKind::*}, - keyring::KeyRing, + keyring::{KeyRing, SigningProvider}, }; - -/// Label for ed25519-dalek provider -// TODO: use a non-string type for these, e.g. an enum -pub const DALEK_PROVIDER_LABEL: &str = "dalek"; +use tendermint::TendermintKey; /// Create software-backed Ed25519 signer objects from the given configuration pub fn init(keyring: &mut KeyRing, configs: &[SoftSignConfig]) -> Result<(), KmsError> { @@ -31,9 +28,12 @@ pub fn init(keyring: &mut KeyRing, configs: &[SoftSignConfig]) -> Result<(), Kms let provider = Box::new(Ed25519Signer::from(&seed)); + // TODO(tarcieri): support for adding account keys into keyrings + let public_key = TendermintKey::ConsensusKey(provider.public_key()?.into()); + keyring.add( - provider.public_key()?, - Signer::new(DALEK_PROVIDER_LABEL, config.id.clone(), provider), + public_key, + Signer::new(SigningProvider::SoftSign, &config.chain_ids, provider), )?; } diff --git a/src/keyring/ed25519/yubihsm.rs b/src/keyring/ed25519/yubihsm.rs index 0044c7a..057aa7d 100644 --- a/src/keyring/ed25519/yubihsm.rs +++ b/src/keyring/ed25519/yubihsm.rs @@ -3,13 +3,10 @@ use crate::{ config::provider::yubihsm::YubihsmConfig, error::{KmsError, KmsErrorKind::*}, - keyring::{ed25519::Signer, KeyRing}, + keyring::{ed25519::Signer, KeyRing, SigningProvider}, }; use signatory::PublicKeyed; - -/// Label for YubiHSM provider -// TODO: use a non-string type for these, e.g. an enum -pub const YUBIHSM_PROVIDER_LABEL: &str = "yubihsm"; +use tendermint::TendermintKey; /// Create hardware-backed YubiHSM signer objects from the given configuration pub fn init(keyring: &mut KeyRing, yubihsm_configs: &[YubihsmConfig]) -> Result<(), KmsError> { @@ -30,9 +27,16 @@ pub fn init(keyring: &mut KeyRing, yubihsm_configs: &[YubihsmConfig]) -> Result< let signer = yubihsm::ed25519::Signer::create(crate::yubihsm::client().clone(), config.key)?; + // TODO(tarcieri): support for adding account keys into keyrings + let public_key = TendermintKey::ConsensusKey(signer.public_key()?.into()); + keyring.add( - signer.public_key()?, - Signer::new(YUBIHSM_PROVIDER_LABEL, config.id.clone(), Box::new(signer)), + public_key, + Signer::new( + SigningProvider::Yubihsm, + &config.chain_ids, + Box::new(signer), + ), )?; } diff --git a/src/keyring/mod.rs b/src/keyring/mod.rs index 176b5b9..ff4cf81 100644 --- a/src/keyring/mod.rs +++ b/src/keyring/mod.rs @@ -1,16 +1,18 @@ //! Signing keyring. Presently specialized for Ed25519. mod ed25519; +mod providers; use self::ed25519::Signer; +pub use self::providers::SigningProvider; use crate::{ + chain, config::provider::ProviderConfig, error::{KmsError, KmsErrorKind::*}, }; -use signatory::ed25519::{PublicKey, Signature}; use std::{collections::BTreeMap, sync::RwLock}; use subtle_encoding; -use tendermint::public_keys::ConsensusKey; +use tendermint::TendermintKey; #[cfg(feature = "ledgertm")] use self::ed25519::ledgertm; @@ -26,7 +28,7 @@ lazy_static! { static ref GLOBAL_KEYRING: RwLock = RwLock::new(KeyRing(BTreeMap::default())); } -pub struct KeyRing(BTreeMap); +pub struct KeyRing(BTreeMap); impl KeyRing { /// Create a keyring from the given provider configuration @@ -57,28 +59,54 @@ impl KeyRing { /// Add a key to the keyring, returning an error if we already have a /// signer registered for the given public key - pub(super) fn add(&mut self, public_key: PublicKey, signer: Signer) -> Result<(), KmsError> { + pub(super) fn add( + &mut self, + public_key: TendermintKey, + signer: Signer, + ) -> Result<(), KmsError> { + let provider = signer.provider(); + + // Generate string for displaying the HRP + let chains = signer + .chain_ids() + .iter() + .map(|id| id.to_string()) + .collect::>() + .join(","); + + // Use the correct HRP for the configured chain if available + let public_key_serialized = if signer.chain_ids().len() == 1 { + let chain_id = signer.chain_ids()[0]; + match chain::key::serialize(chain_id, public_key) { + Some(bech32) => bech32, + None => fail!(InvalidKey, "unknown chain ID: {}", chain_id), + } + } else { + public_key.to_bech32("") + }; + info!( "[keyring:{}:{}] added validator key {}", - signer.provider_name, - signer.key_id, - ConsensusKey::from(public_key) + provider, chains, public_key_serialized ); if let Some(other) = self.0.insert(public_key, signer) { fail!( InvalidKey, - "duplicate key {}: already registered as {}:{}", - ConsensusKey::from(public_key), - other.provider_name, - other.key_id + "[keyring:{}:{}] duplicate key {} already registered as {}:{:?}", + provider, + chains, + public_key_serialized, + other.provider(), + other.chain_ids() ) } else { Ok(()) } } - pub fn default_pubkey() -> Result { + /// Get the default public key for this keyring + pub fn default_pubkey() -> Result { let keyring = GLOBAL_KEYRING.read().unwrap(); let mut keys = keyring.0.keys(); @@ -91,16 +119,16 @@ impl KeyRing { /// Sign a message using the secret key associated with the given public key /// (if it is in our keyring) - pub fn sign(public_key: Option<&PublicKey>, msg: &[u8]) -> Result { + pub fn sign_ed25519( + public_key: Option<&TendermintKey>, + msg: &[u8], + ) -> Result { let keyring = GLOBAL_KEYRING.read().unwrap(); let signer: &Signer = match public_key { - Some(public_key) => keyring.0.get(public_key).ok_or_else(|| { - err!( - InvalidKey, - "not in keyring: {}", - ConsensusKey::from(*public_key) - ) - })?, + Some(public_key) => keyring + .0 + .get(public_key) + .ok_or_else(|| err!(InvalidKey, "not in keyring: {}", public_key.to_bech32("")))?, None => { let mut vals = keyring.0.values(); diff --git a/src/keyring/providers.rs b/src/keyring/providers.rs new file mode 100644 index 0000000..4bf7f61 --- /dev/null +++ b/src/keyring/providers.rs @@ -0,0 +1,32 @@ +use std::fmt::{self, Display}; + +/// Enumeration of signing key providers +#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] +pub enum SigningProvider { + /// YubiHSM provider + #[cfg(feature = "yubihsm")] + Yubihsm, + + /// Ledger + Tendermint application + #[cfg(feature = "ledgertm")] + LedgerTm, + + /// Software signer (not intended for production use) + #[cfg(feature = "softsign")] + SoftSign, +} + +impl Display for SigningProvider { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + #[cfg(feature = "yubihsm")] + SigningProvider::Yubihsm => write!(f, "yubihsm"), + + #[cfg(feature = "ledgertm")] + SigningProvider::LedgerTm => write!(f, "ledgertm"), + + #[cfg(feature = "softsign")] + SigningProvider::SoftSign => write!(f, "softsign"), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 76a89b9..d4a4fb7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,7 @@ extern crate serde_derive; mod error; mod application; +mod chain; mod client; mod commands; mod config; diff --git a/src/session.rs b/src/session.rs index c791383..fa407fc 100644 --- a/src/session.rs +++ b/src/session.rs @@ -13,7 +13,7 @@ use std::{ sync::Arc, }; use tendermint::{ - amino_types::{PingRequest, PingResponse, PubKeyRequest, PubKeyResponse}, + amino_types::{PingRequest, PingResponse, PubKeyRequest}, chain, public_keys::SecretConnectionKey, SecretConnection, @@ -154,7 +154,7 @@ where // TODO(ismail): figure out which key to use here instead of taking the only key // from keyring here: - let sig = KeyRing::sign(None, &to_sign)?; + let sig = KeyRing::sign_ed25519(None, &to_sign)?; request.set_signature(&sig); debug!("successfully signed request:\n {:?}", request); @@ -169,11 +169,8 @@ where /// Get the public key for (the only) public key in the keyring fn get_public_key(&mut self, _request: &PubKeyRequest) -> Result { - let pubkey = KeyRing::default_pubkey()?; - let pubkey_bytes = pubkey.as_bytes(); - - Ok(Response::PublicKey(PubKeyResponse { - pub_key_ed25519: pubkey_bytes.to_vec(), - })) + Ok(Response::PublicKey( + KeyRing::default_pubkey()?.to_response(), + )) } } diff --git a/tendermint-rs/Cargo.toml b/tendermint-rs/Cargo.toml index 82885cc..14a2f72 100644 --- a/tendermint-rs/Cargo.toml +++ b/tendermint-rs/Cargo.toml @@ -40,7 +40,7 @@ rand_os = { version = "0.1", optional = true } ring = { version = "0.14", optional = true } serde = { version = "1", optional = true } serde_derive = { version = "1", optional = true } -signatory = { version = "0.11.1", optional = true, features = ["ed25519","ecdsa"] } +signatory = { version = "0.11.2", optional = true, features = ["ed25519", "ecdsa"] } signatory-dalek = { version = "0.11", optional = true } sha2 = { version = "0.8", optional = true, default-features = false } subtle-encoding = { version = "0.3", features = ["bech32-preview"] } diff --git a/tendermint-rs/src/public_keys.rs b/tendermint-rs/src/public_keys.rs index 11a6901..7b5a0c0 100644 --- a/tendermint-rs/src/public_keys.rs +++ b/tendermint-rs/src/public_keys.rs @@ -1,125 +1,119 @@ //! Public keys used in Tendermint networks // TODO:: account keys -use crate::error::Error; +use crate::{amino_types::PubKeyResponse, error::Error}; use sha2::{Digest, Sha256}; use signatory::{ecdsa::curve::secp256k1, ed25519}; -use std::fmt::{self, Display}; -use subtle_encoding::bech32; - -/// Validator signing keys used for authenticating consensus protocol messages -#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] -pub enum TendermintKey { - /// Ed25519 consensus keys +use std::{ + fmt::{self, Display}, + ops::Deref, +}; +use subtle_encoding::{bech32, hex}; + +/// Public keys allowed in Tendermint protocols +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] +pub enum PublicKey { + /// Ed25519 keys Ed25519(ed25519::PublicKey), - /// Secp256k1 consensus keys + + /// Secp256k1 keys Secp256k1(secp256k1::PublicKey), } -/// Validator signing keys used for authenticating consensus protocol messages -pub struct ConsensusKey(TendermintKey); -/// User signing keys used for interacting with accounts in the state machine -pub struct AccountKey(TendermintKey); - -impl TendermintKey { +impl PublicKey { /// From raw secp256k1 public key bytes - pub fn from_raw_secp256k1(bytes: &[u8]) -> Result { - Ok(TendermintKey::Secp256k1(secp256k1::PublicKey::from_bytes( + pub fn from_raw_secp256k1(bytes: &[u8]) -> Result { + Ok(PublicKey::Secp256k1(secp256k1::PublicKey::from_bytes( bytes, )?)) } /// From raw Ed25519 public key bytes - pub fn from_raw_ed25519(bytes: &[u8]) -> Result { - Ok(TendermintKey::Ed25519(ed25519::PublicKey::from_bytes( - bytes, - )?)) + pub fn from_raw_ed25519(bytes: &[u8]) -> Result { + Ok(PublicKey::Ed25519(ed25519::PublicKey::from_bytes(bytes)?)) } /// Get Ed25519 public key pub fn ed25519(self) -> Option { match self { - TendermintKey::Ed25519(pk) => Some(pk), + PublicKey::Ed25519(pk) => Some(pk), _ => None, } } -} -impl Display for ConsensusKey { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let key_bytes: Vec = match self.0 { - TendermintKey::Ed25519(ref pk) => { + /// Serialize this key as amino bytes + pub fn to_amino_bytes(self) -> Vec { + match self { + PublicKey::Ed25519(ref pk) => { //Amino prefix for Pubkey let mut key_bytes = vec![0x16, 0x24, 0xDE, 0x64, 0x20]; key_bytes.extend(pk.as_bytes()); key_bytes } - TendermintKey::Secp256k1(ref pk) => { + PublicKey::Secp256k1(ref pk) => { let mut key_bytes = vec![0xEB, 0x5A, 0xE9, 0x87, 0x21]; key_bytes.extend(pk.as_bytes()); key_bytes } - }; - bech32::encode("cosmosvalconspub", &key_bytes).fmt(f) + } } -} -impl Display for AccountKey { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let key_bytes: Vec = match self.0 { - TendermintKey::Ed25519(ref pk) => { - //Amino prefix for Pubkey - let mut key_bytes = vec![0x16, 0x24, 0xDE, 0x64, 0x20]; - key_bytes.extend(pk.as_bytes()); - key_bytes - } - TendermintKey::Secp256k1(ref pk) => { - let mut key_bytes = vec![0xEB, 0x5A, 0xE9, 0x87, 0x21]; - key_bytes.extend(pk.as_bytes()); - key_bytes - } - }; - bech32::encode("cosmospub", &key_bytes).fmt(f) + /// Serialize this key as Bech32 with the given human readable prefix + pub fn to_bech32(self, hrp: &str) -> String { + bech32::encode(hrp, self.to_amino_bytes()) } -} -impl From for TendermintKey { - fn from(pk: ed25519::PublicKey) -> TendermintKey { - TendermintKey::Ed25519(pk) + /// Serialize this key as hexadecimal + pub fn to_hex(self) -> String { + String::from_utf8(hex::encode_upper(self.to_amino_bytes())).unwrap() } -} -impl From for TendermintKey { - fn from(pk: secp256k1::PublicKey) -> TendermintKey { - TendermintKey::Secp256k1(pk) + /// Create a response which represents this public key + pub fn to_response(self) -> PubKeyResponse { + match self { + PublicKey::Ed25519(ref pk) => PubKeyResponse { + pub_key_ed25519: pk.as_bytes().to_vec(), + }, + PublicKey::Secp256k1(_) => panic!("secp256k1 PubKeyResponse unimplemented"), + } } } -impl From for ConsensusKey { - fn from(pk: ed25519::PublicKey) -> ConsensusKey { - ConsensusKey(TendermintKey::Ed25519(pk)) +impl From for PublicKey { + fn from(pk: ed25519::PublicKey) -> PublicKey { + PublicKey::Ed25519(pk) } } -impl From for ConsensusKey { - fn from(pk: secp256k1::PublicKey) -> ConsensusKey { - ConsensusKey(TendermintKey::Secp256k1(pk)) +impl From for PublicKey { + fn from(pk: secp256k1::PublicKey) -> PublicKey { + PublicKey::Secp256k1(pk) } } -impl From for AccountKey { - fn from(pk: ed25519::PublicKey) -> AccountKey { - AccountKey(TendermintKey::Ed25519(pk)) - } +/// Public key roles used in Tendermint networks +#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)] +pub enum TendermintKey { + /// User signing keys used for interacting with accounts in the state machine + AccountKey(PublicKey), + + /// Validator signing keys used for authenticating consensus protocol messages + ConsensusKey(PublicKey), } -impl From for AccountKey { - fn from(pk: secp256k1::PublicKey) -> AccountKey { - AccountKey(TendermintKey::Secp256k1(pk)) +impl Deref for TendermintKey { + type Target = PublicKey; + + fn deref(&self) -> &PublicKey { + match self { + TendermintKey::AccountKey(key) => key, + TendermintKey::ConsensusKey(key) => key, + } } } /// Secret Connection signing keys +// TODO(tarcieri): unify with `TendermintKey`? #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] pub enum SecretConnectionKey { /// Ed25519 Secret Connection keys @@ -163,7 +157,7 @@ impl From for SecretConnectionKey { #[cfg(test)] mod tests { - use super::{AccountKey, ConsensusKey, SecretConnectionKey, TendermintKey}; + use super::{PublicKey, SecretConnectionKey, TendermintKey}; use subtle_encoding::hex; const EXAMPLE_SECRET_CONN_KEY: &str = @@ -187,28 +181,29 @@ mod tests { #[test] fn test_consensus_serialization() { - let example_key = ConsensusKey( - TendermintKey::from_raw_ed25519(&hex::decode_upper(EXAMPLE_CONSENSUS_KEY).unwrap()) + let example_key = TendermintKey::ConsensusKey( + PublicKey::from_raw_ed25519(&hex::decode_upper(EXAMPLE_CONSENSUS_KEY).unwrap()) .unwrap(), ); assert_eq!( - example_key.to_string(), + example_key.to_bech32("cosmosvalconspub"), "cosmosvalconspub1zcjduepqfgjuveq2raetnjt4xwpffm63kmguxv2chdhvhf5lhslmtgeunh8qmf7exk" ); } const EXAMPLE_ACCOUNT_KEY: &str = "02A1633CAFCC01EBFB6D78E39F687A1F0995C62FC95F51EAD10A02EE0BE551B5DC"; + #[test] fn test_account_serialization() { - let example_key = AccountKey( - TendermintKey::from_raw_secp256k1(&hex::decode_upper(EXAMPLE_ACCOUNT_KEY).unwrap()) + let example_key = TendermintKey::AccountKey( + PublicKey::from_raw_secp256k1(&hex::decode_upper(EXAMPLE_ACCOUNT_KEY).unwrap()) .unwrap(), ); assert_eq!( - example_key.to_string(), + example_key.to_bech32("cosmospub"), "cosmospub1addwnpepq2skx090esq7h7md0r3e76r6ruyet330e904r6k3pgpwuzl92x6actrt4uq" ); } diff --git a/tests/integration.rs b/tests/integration.rs index 4c27069..76dc363 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -129,6 +129,10 @@ impl KmsProcess { writeln!( config_file, r#" + [[chain]] + id = "test_chain_id" + key_format = {{ type = "bech32", account_key_prefix = "cosmospub", consensus_key_prefix = "cosmosvalconspub" }} + [[validator]] addr = "tcp://127.0.0.1:{}" chain_id = "test_chain_id" @@ -136,7 +140,7 @@ impl KmsProcess { secret_key = "tests/support/secret_connection.key" [[providers.softsign]] - id = "example-key-1" + chain_ids = ["test_chain_id"] path = "{}" "#, port, SIGNING_KEY_PATH @@ -152,12 +156,16 @@ impl KmsProcess { writeln!( config_file, r#" + [[chain]] + id = "test_chain_id" + key_format = {{ type = "bech32", account_key_prefix = "cosmospub", consensus_key_prefix = "cosmosvalconspub" }} + [[validator]] addr = "unix://{}" chain_id = "test_chain_id" [[providers.softsign]] - id = "example-key-1" + chain_ids = ["test_chain_id"] path = "{}" "#, socket_path, SIGNING_KEY_PATH diff --git a/tests/support/gen-validator-integration-cfg.sh b/tests/support/gen-validator-integration-cfg.sh index 17ad38c..8d38ff4 100644 --- a/tests/support/gen-validator-integration-cfg.sh +++ b/tests/support/gen-validator-integration-cfg.sh @@ -9,6 +9,11 @@ SECRET_KEY=${SECRET_KEY:-${OUTPUT_PATH}/secret_connection.key} OUTPUT_FILE=${OUTPUT_FILE:-${OUTPUT_PATH}/tmkms.toml} VALIDATOR_ADDR=${VALIDATOR_ADDR:-"tcp://127.0.0.1:61278"} CFG_TEMPLATE=$(cat <<-EOF +# Information about Tenderment blockchain networks this KMS services +[[chain]] +id = "CHAIN_ID" +key_format = { type = "bech32", account_key_prefix = "cosmospub", consensus_key_prefix = "cosmosvalconspub" } + [[validator]] addr = "VALIDATOR_ADDR" chain_id = "CHAIN_ID" @@ -16,7 +21,7 @@ reconnect = true # true is the default secret_key = "SECRET_KEY" [[providers.softsign]] -id = "CHAIN_ID" +chain_ids = ["CHAIN_ID"] path = "SIGNING_KEY" EOF ) diff --git a/tests/support/kms_yubihsm_mock.toml b/tests/support/kms_yubihsm_mock.toml index 2187df0..1cb3a31 100644 --- a/tests/support/kms_yubihsm_mock.toml +++ b/tests/support/kms_yubihsm_mock.toml @@ -2,6 +2,11 @@ # # This file is passed to the KMS executable during integration tests +# Information about Tenderment blockchain networks this KMS services +[[chain]] +id = "cosmoshub" +key_format = { type = "bech32", account_key_prefix = "cosmospub", consensus_key_prefix = "cosmosvalconspub" } + [[validator]] addr = "tcp://127.0.0.1:23456" chain_id = "test_chain_id" @@ -11,5 +16,5 @@ secret_key = "tests/seccon.key" [[providers.yubihsm]] adapter = { type = "usb" } auth = { key = 1, password = "password" } -keys = [{ id = "test_key", key = 1 }] +keys = [{ chain_ids = ["cosmoshub"], key = 1 }] serial_number = "0123456789" diff --git a/tmkms.toml.example b/tmkms.toml.example index 3ed8dfe..27a7d5e 100644 --- a/tmkms.toml.example +++ b/tmkms.toml.example @@ -2,24 +2,28 @@ # # Copy this to 'tmkms.toml' and edit for your own purposes +# Information about Tenderment blockchain networks this KMS services +[[chain]] +id = "cosmoshub" +key_format = { type = "bech32", account_key_prefix = "cosmospub", consensus_key_prefix = "cosmosvalconspub" } + +# Validator configuration [[validator]] addr = "tcp://example1.example.com:26658" # or "unix:///path/to/socket" -chain_id = "gaia-9000" +chain_id = "cosmoshub" reconnect = true # true is the default secret_key = "path/to/secret_connection.key" +# Provider configuration [[providers.softsign]] -id = "gaia-8000" +chain_ids = ["cosmoshub"] path = "path/to/signing.key" [[providers.yubihsm]] adapter = { type = "usb" } auth = { key = 1, password = "password" } # Default YubiHSM admin credentials. Change ASAP! -keys = [{ id = "gaia-9000", key = 1 }] +keys = [{ chain_ids = ["cosmoshub"], key = 1 }] #serial_number = "0123456789" # identify serial number of a specific YubiHSM to connect to [[providers.ledgertm]] -# note: -# - this will enable a ledger signer -# - currently no other options are supported and -# - the key_id used by signatory internally will be "ledgertm" by default +chain_ids = ["cosmoshub"]