From 88245091908ddb048c67ac8833d8a8e7eed60807 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Tue, 20 Nov 2018 22:19:59 +0100 Subject: [PATCH 1/4] add `tmkms yubihsm keys import` command --- Cargo.lock | 10 +++ Cargo.toml | 1 + src/commands/yubihsm/keys/generate.rs | 10 +-- src/commands/yubihsm/keys/import.rs | 125 ++++++++++++++++++++++++++ src/commands/yubihsm/keys/mod.rs | 19 +++- tests/cli/yubihsm/keys/import.rs | 2 + 6 files changed, 157 insertions(+), 10 deletions(-) create mode 100644 src/commands/yubihsm/keys/import.rs create mode 100644 tests/cli/yubihsm/keys/import.rs diff --git a/Cargo.lock b/Cargo.lock index aa51b4f..33cf755 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,6 +93,14 @@ dependencies = [ "safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "base64" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bit-set" version = "0.2.0" @@ -840,6 +848,7 @@ version = "0.1.0" dependencies = [ "abscissa 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "abscissa_derive 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -971,6 +980,7 @@ dependencies = [ "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" "checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" +"checksum base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "621fc7ecb8008f86d7fb9b95356cd692ce9514b80a86d85b397f32a22da7b9e2" "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" "checksum bit-set 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6e1e6fb1c9e3d6fcdec57216a74eaa03e41f52a22f13a16438251d8e88b89da" "checksum bit-vec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4440d5cb623bb7390ae27fec0bb6c61111969860f8e3ae198bfa0663645e67cf" diff --git a/Cargo.toml b/Cargo.toml index 3286edb..e6d5ec2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ circle-ci = { repository = "tendermint/kms" } abscissa = "0.0.6" abscissa_derive = "0.0.2" byteorder = "1.2" +base64 = "0.10.0" bytes = "0.4" chrono = "0.4" failure = "0.1" diff --git a/src/commands/yubihsm/keys/generate.rs b/src/commands/yubihsm/keys/generate.rs index 1cd8348..d0b8404 100644 --- a/src/commands/yubihsm/keys/generate.rs +++ b/src/commands/yubihsm/keys/generate.rs @@ -1,19 +1,11 @@ use abscissa::Callable; use std::process; +use super::*; use signatory::ed25519; use tendermint::public_keys::ConsensusKey; use yubihsm; -/// Default key type to generate -pub const DEFAULT_KEY_TYPE: &str = "ed25519"; - -/// Default YubiHSM2 domain (internal partitioning) -pub const DEFAULT_DOMAINS: yubihsm::Domain = yubihsm::Domain::DOM1; - -/// Default YubiHSM2 permissions for generated keys -pub const DEFAULT_CAPABILITIES: yubihsm::Capability = yubihsm::Capability::ASYMMETRIC_SIGN_EDDSA; - /// The `yubihsm keys generate` subcommand #[derive(Debug, Default, Options)] pub struct GenerateCommand { diff --git a/src/commands/yubihsm/keys/import.rs b/src/commands/yubihsm/keys/import.rs new file mode 100644 index 0000000..8205f07 --- /dev/null +++ b/src/commands/yubihsm/keys/import.rs @@ -0,0 +1,125 @@ +extern crate base64; +extern crate serde_json; + +use super::*; + +use std::fs::File; +use std::io::prelude::*; +use std::process; +use std::str; + +use abscissa::Callable; +use signatory::ed25519; +use signatory::ed25519::Seed; +use tendermint::public_keys::ConsensusKey; +use yubihsm; + +use serde_json::Value; + +/// The `yubihsm keys import` subcommand +#[derive(Debug, Default, Options)] +pub struct ImportCommand { + /// Path to the validator configuration file + #[options(short = "p", long = "path")] + pub path: Option, + + /// Type of key to generate (default 'ed25519') + #[options(short = "t")] + pub key_type: Option, + + /// Label for imported key + #[options(short = "l", long = "label")] + pub label: Option, + + /// Key ID for imported key + #[options(free)] + key_id: Option, +} + +impl Callable for ImportCommand { + fn call(&self) { + if self.path.is_none() { + status_err!("must provide a valid path to priv_validator.json"); + process::exit(1); + } + + if let None = &self.key_id { + status_err!("must provide a unique key_id"); + process::exit(1); + } + + match &self.key_type { + Some(ref key_type) => if key_type != DEFAULT_KEY_TYPE { + status_err!( + "only supported key type is: ed25519 (given: \"{}\")", + key_type + ); + process::exit(1); + }, + None => (), + } + + if let Some(path) = &self.path { + let mut f = File::open(path).unwrap_or_else(|e| { + status_err!("couldn't open validator config file {}: {}", path, e); + process::exit(1); + }); + + let mut contents = Vec::new(); + f.read_to_end(&mut contents).unwrap_or_else(|e| { + status_err!("couldn't read validator config file {}: {}", path, e); + process::exit(1); + }); + let v: Value = serde_json::from_slice(&contents).unwrap(); + let s = v["priv_key"]["value"].as_str().unwrap_or_else(|| { + status_err!("couldn't read validator private key from config: {}", path); + process::exit(1); + }); + + let key = base64::decode(s).unwrap_or_else(|e| { + status_err!("couldn't decode validator private key from config: {}", e); + process::exit(1); + }); + Seed::from_keypair(&key).unwrap_or_else(|e| { + status_err!("invalid key in validator config: {}", e); + process::exit(1); + }); + let label = + yubihsm::ObjectLabel::from(self.label.as_ref().map(|l| l.as_ref()).unwrap_or("")); + + let mut hsm = yubihsm::get_hsm_client(); + + if let Err(e) = hsm.put_asymmetric_key( + self.key_id.unwrap(), + label, + DEFAULT_DOMAINS, + DEFAULT_CAPABILITIES, + yubihsm::AsymmetricAlg::Ed25519, + key, + ) { + status_err!("couldn't generate key #{}: {}", self.key_id.unwrap(), e); + process::exit(1); + } + + let public_key = ed25519::PublicKey::from_bytes( + hsm.get_pubkey(self.key_id.unwrap()).unwrap_or_else(|e| { + status_err!( + "couldn't get public key for key #{}: {}", + self.key_id.unwrap(), + e + ); + process::exit(1); + }), + ).unwrap(); + + status_ok!( + "Imported", + "key #{}: {}", + self.key_id.unwrap(), + ConsensusKey::from(public_key) + ); + } + } +} + +impl_command!(ImportCommand); diff --git a/src/commands/yubihsm/keys/mod.rs b/src/commands/yubihsm/keys/mod.rs index fd28173..b71ce2a 100644 --- a/src/commands/yubihsm/keys/mod.rs +++ b/src/commands/yubihsm/keys/mod.rs @@ -1,10 +1,23 @@ mod generate; mod help; +mod import; mod list; use abscissa::Callable; +use yubihsm; -use self::{generate::GenerateCommand, help::HelpCommand, list::ListCommand}; +use self::{ + generate::GenerateCommand, help::HelpCommand, import::ImportCommand, list::ListCommand, +}; + +/// Default key type to generate +pub const DEFAULT_KEY_TYPE: &str = "ed25519"; + +/// Default YubiHSM2 domain (internal partitioning) +pub const DEFAULT_DOMAINS: yubihsm::Domain = yubihsm::Domain::DOM1; + +/// Default YubiHSM2 permissions for generated keys +pub const DEFAULT_CAPABILITIES: yubihsm::Capability = yubihsm::Capability::ASYMMETRIC_SIGN_EDDSA; /// The `yubihsm keys` subcommand #[derive(Debug, Options)] @@ -15,6 +28,9 @@ pub enum KeysCommand { #[options(help = "show help for the 'yubihsm keys' subcommand")] Help(HelpCommand), + #[options(help = "import validator signing key for the 'yubihsm keys' subcommand")] + Import(ImportCommand), + #[options(help = "list all suitable Ed25519 keys in the HSM")] List(ListCommand), } @@ -37,6 +53,7 @@ impl Callable for KeysCommand { match self { KeysCommand::Generate(generate) => generate.call(), KeysCommand::Help(help) => help.call(), + KeysCommand::Import(import) => import.call(), KeysCommand::List(list) => list.call(), } } diff --git a/tests/cli/yubihsm/keys/import.rs b/tests/cli/yubihsm/keys/import.rs new file mode 100644 index 0000000..f228f2d --- /dev/null +++ b/tests/cli/yubihsm/keys/import.rs @@ -0,0 +1,2 @@ +//! Integration tests for the `yubihsm keys import` subcommand +// TODO: `yubihsm keys import` tests From 7aae5e04384c353933f4c7b15fe5a2cc61ddf413 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Tue, 20 Nov 2018 22:22:38 +0100 Subject: [PATCH 2/4] thanks clippy --- src/commands/yubihsm/keys/import.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/yubihsm/keys/import.rs b/src/commands/yubihsm/keys/import.rs index 8205f07..62bf449 100644 --- a/src/commands/yubihsm/keys/import.rs +++ b/src/commands/yubihsm/keys/import.rs @@ -43,7 +43,7 @@ impl Callable for ImportCommand { process::exit(1); } - if let None = &self.key_id { + if self.key_id.is_none() { status_err!("must provide a unique key_id"); process::exit(1); } From 55130fd87a9a8e72c11f72caf8bcc6b6861018e2 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Tue, 20 Nov 2018 22:35:27 +0100 Subject: [PATCH 3/4] remove superfluous base64 dependency --- Cargo.lock | 10 ---------- Cargo.toml | 1 - src/commands/yubihsm/keys/import.rs | 2 +- 3 files changed, 1 insertion(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 33cf755..aa51b4f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,14 +93,6 @@ dependencies = [ "safemem 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "base64" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "bit-set" version = "0.2.0" @@ -848,7 +840,6 @@ version = "0.1.0" dependencies = [ "abscissa 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "abscissa_derive 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -980,7 +971,6 @@ dependencies = [ "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" "checksum backtrace-sys 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" -"checksum base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "621fc7ecb8008f86d7fb9b95356cd692ce9514b80a86d85b397f32a22da7b9e2" "checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" "checksum bit-set 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e6e1e6fb1c9e3d6fcdec57216a74eaa03e41f52a22f13a16438251d8e88b89da" "checksum bit-vec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4440d5cb623bb7390ae27fec0bb6c61111969860f8e3ae198bfa0663645e67cf" diff --git a/Cargo.toml b/Cargo.toml index e6d5ec2..3286edb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,6 @@ circle-ci = { repository = "tendermint/kms" } abscissa = "0.0.6" abscissa_derive = "0.0.2" byteorder = "1.2" -base64 = "0.10.0" bytes = "0.4" chrono = "0.4" failure = "0.1" diff --git a/src/commands/yubihsm/keys/import.rs b/src/commands/yubihsm/keys/import.rs index 62bf449..bc59ab1 100644 --- a/src/commands/yubihsm/keys/import.rs +++ b/src/commands/yubihsm/keys/import.rs @@ -1,4 +1,3 @@ -extern crate base64; extern crate serde_json; use super::*; @@ -11,6 +10,7 @@ use std::str; use abscissa::Callable; use signatory::ed25519; use signatory::ed25519::Seed; +use subtle_encoding::base64; use tendermint::public_keys::ConsensusKey; use yubihsm; From 649cdf2b1eb60a762491d9ef44add34b787fc559 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Tue, 20 Nov 2018 22:48:59 +0100 Subject: [PATCH 4/4] s/generate/import --- src/commands/yubihsm/keys/import.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/yubihsm/keys/import.rs b/src/commands/yubihsm/keys/import.rs index bc59ab1..7591f32 100644 --- a/src/commands/yubihsm/keys/import.rs +++ b/src/commands/yubihsm/keys/import.rs @@ -23,7 +23,7 @@ pub struct ImportCommand { #[options(short = "p", long = "path")] pub path: Option, - /// Type of key to generate (default 'ed25519') + /// Type of key to import (default 'ed25519') #[options(short = "t")] pub key_type: Option, @@ -97,7 +97,7 @@ impl Callable for ImportCommand { yubihsm::AsymmetricAlg::Ed25519, key, ) { - status_err!("couldn't generate key #{}: {}", self.key_id.unwrap(), e); + status_err!("couldn't import key #{}: {}", self.key_id.unwrap(), e); process::exit(1); }