Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

yubihsm: add account key support to keys generate #101

Merged
merged 1 commit into from
Jul 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 18 additions & 12 deletions README.txsigner.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,20 +177,11 @@ the following arguments:

```
$ tmkms yubihsm keys generate -t account -l "columbus-3 oracle signer" -b columbus-oracle-key.enc 0x123
Generated account (secp256k1) key 0x0123
```

If the operation succeeded, you should now see the key listed when you run
`tmkms yubihsm keys list`, flagged as being an `[acct]` key:

```
$ tmkms yubihsm keys list
Listing keys in YubiHSM #0001234567:
- 0x0001: [cons] cosmosvalconspub1zcjduepqpxg30wtw7tlt750lhl3fdjfex6eq7tj3gfer3ugrzahd27srflhqv6ep6j
- 0x0123: [acct] terra13tdvxsauagu33glu74u93mdka7ahvm5a6yfr76
```

Finally, add the generated key to your [`tmkms.toml`] config file's
`[[providers.yubihsm]]` section (under `keys`):
If that succeeded, you can now add the generated key to your [`tmkms.toml`]
config file's `[[providers.yubihsm]]` section (under `keys`):

```toml
[[providers.yubihsm]]
Expand All @@ -205,6 +196,21 @@ keys = [
This will register the newly generated key as an account key on the provided
chain IDs (i.e. `columbus-3` in this case)

Finally, confirm you see the key listed when you run
`tmkms yubihsm keys list`, flagged as being an `[acct]` key:

```
$ tmkms yubihsm keys list
Listing keys in YubiHSM #0001234567:
- 0x0001: [cons] cosmosvalconspub1zcjduepqpxg30wtw7tlt750lhl3fdjfex6eq7tj3gfer3ugrzahd27srflhqv6ep6j
- 0x0123: [acct] terra13tdvxsauagu33glu74u93mdka7ahvm5a6yfr76
```

If the newly generated account key is properly configured for the desired chain
the `list` command should display its Bech32-formatted account address. Make a
note of this as you'll need to configure it as `[[tx_signer.account_address]]`
(see below).

### `softsign`: creating account keys

To create a new "soft" account key (randomly generated using the host OS's
Expand Down
3 changes: 0 additions & 3 deletions src/commands/yubihsm/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ use self::{
use abscissa_core::{Command, Help, Options, Runnable};
use std::path::PathBuf;

/// 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;

Expand Down
99 changes: 69 additions & 30 deletions src/commands/yubihsm/keys/generate.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Generate a new key within the YubiHSM2

use super::*;
use crate::prelude::*;
use crate::{config::provider::KeyType, prelude::*};
use abscissa_core::{Command, Options, Runnable};
use chrono::{SecondsFormat, Utc};
use std::{
Expand Down Expand Up @@ -59,12 +59,12 @@ pub struct GenerateCommand {

/// Key ID to generate
#[options(free, help = "key ID to generate")]
key_ids: Vec<u16>,
key_ids: Vec<String>,
}

impl Runnable for GenerateCommand {
/// Generate an Ed25519 signing key inside a YubiHSM2 device
fn run(&self) {
impl GenerateCommand {
/// Parse the key ID provided in the arguments
pub fn parse_key_id(&self) -> u16 {
if self.key_ids.len() != 1 {
status_err!(
"expected exactly 1 key ID to generate, got {}",
Expand All @@ -73,17 +73,37 @@ impl Runnable for GenerateCommand {
process::exit(1);
}

let key_id = self.key_ids[0];
let key_id_str = &self.key_ids[0];

if key_id_str.starts_with("0x") {
u16::from_str_radix(&key_id_str[2..], 16).ok()
} else {
key_id_str.parse().ok()
}
.unwrap_or_else(|| {
status_err!("couldn't parse key ID: {}", key_id_str);
process::exit(1);
})
}

if let Some(key_type) = self.key_type.as_ref() {
if key_type != DEFAULT_KEY_TYPE {
status_err!(
"only supported key type is: ed25519 (given: \"{}\")",
key_type
);
/// Parse the key type provided in the arguments
pub fn parse_key_type(&self) -> KeyType {
match self.key_type.as_ref().map(AsRef::as_ref) {
Some("account") => KeyType::Account,
Some("consensus") | None => KeyType::Consensus, // default
Some(other) => {
status_err!("invalid key type: {}", other);
process::exit(1);
}
}
}
}

impl Runnable for GenerateCommand {
/// Generate an Ed25519 signing key inside a YubiHSM2 device
fn run(&self) {
let key_id = self.parse_key_id();
let key_type = self.parse_key_type();

let hsm = crate::yubihsm::client();
let mut capabilities = DEFAULT_CAPABILITIES;
Expand All @@ -99,39 +119,58 @@ impl Runnable for GenerateCommand {
Some(ref l) => l.to_owned(),
None => match self.bech32_prefix {
Some(ref prefix) => format!("{}:{}", prefix, timestamp),
None => format!("ed25519:{}", timestamp),
None => format!("{}:{}", key_type, timestamp),
},
}
.as_ref(),
);

let algorithm = match key_type {
KeyType::Account => yubihsm::asymmetric::Algorithm::EcK256,
KeyType::Consensus => yubihsm::asymmetric::Algorithm::Ed25519,
};

if let Err(e) = hsm.generate_asymmetric_key(
key_id,
label,
DEFAULT_DOMAINS, // TODO(tarcieri): customize domains
capabilities,
yubihsm::asymmetric::Algorithm::Ed25519,
algorithm,
) {
status_err!("couldn't generate key #{}: {}", key_id, e);
process::exit(1);
}

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);
})
.as_ref(),
)
.unwrap();

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 0x{:04x}: {}", key_id, public_key_string);
match key_type {
KeyType::Account => {
// TODO(tarcieri): generate and show account ID (fingerprint)
status_ok!("Generated", "account (secp256k1) key 0x{:04x}", key_id)
}
KeyType::Consensus => {
// TODO(tarcieri): use KeyFormat (when available) to format Bech32
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);
})
.as_ref(),
)
.unwrap();

let public_key_string = match self.bech32_prefix {
Some(ref prefix) => public_key.to_bech32(prefix),
None => public_key.to_hex(),
};

status_ok!(
"Generated",
"consensus (ed25519) key 0x{:04x}: {}",
key_id,
public_key_string
)
}
}

if let Some(ref backup_file) = self.backup_file {
create_encrypted_backup(
Expand Down
11 changes: 11 additions & 0 deletions src/config/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ use self::ledgertm::LedgerTendermintConfig;
use self::softsign::SoftsignConfig;
#[cfg(feature = "yubihsm")]
use self::yubihsm::YubihsmConfig;

use serde::Deserialize;
use std::fmt;

/// Provider configuration
#[derive(Default, Deserialize, Debug)]
Expand Down Expand Up @@ -54,3 +56,12 @@ impl Default for KeyType {
KeyType::Consensus
}
}

impl fmt::Display for KeyType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
KeyType::Account => f.write_str("account"),
KeyType::Consensus => f.write_str("consensus"),
}
}
}