Skip to content

Commit

Permalink
Bump deps (signatory/stdtx/k256/tendermint/yubihsm)
Browse files Browse the repository at this point in the history
Upgrades the following dependencies to the latest versions:

- `signatory` v0.22.0
- `stdtx` v0.3.0
- `k256` v0.5.9
- `tendermint` v0.16.0
- `yubihsm` v0.35.0-rc

This commit also moves away from Signatory-based abstractions in several
places, leaning more directly on `ed25519-dalek` and the `k256` crate,
the latter of which now contains full ECDSA/secp256k1 support.
Additionally, it completely removes use of `signatory-dalek` and
`rust-secp256k1`/`signatory-secp256k1`.

The rationale is that ed25519-dalek has natively adopted the `Signer`
and `Verifier` traits which Signatory originally provided, which
eliminates the need to use it through an abstraction layer/wrapper.
The `signatory-dalek` crate has also since been retired.
  • Loading branch information
tony-iqlusion committed Oct 13, 2020
1 parent 14edb4e commit 1cfc62e
Show file tree
Hide file tree
Showing 19 changed files with 498 additions and 580 deletions.
538 changes: 255 additions & 283 deletions Cargo.lock

Large diffs are not rendered by default.

28 changes: 13 additions & 15 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,45 +16,43 @@ abscissa_tokio = { version = "0.5", optional = true }
bytes = "0.5"
chacha20poly1305 = "0.5"
chrono = "0.4"
ed25519-dalek = "1"
getrandom = "0.1"
gumdrop = "0.7"
hkd32 = { version = "0.4", default-features = false, features = ["mnemonic"] }
hkdf = "0.9"
hyper = { version = "0.13", optional = true }
k256 = "0.3"
k256 = { version = "0.5", features = ["ecdsa", "sha256"] }
merlin = "2"
once_cell = "1.4"
prost-amino = "0.6"
prost-amino-derive = "0.6"
rand = "0.7"
rand_core = { version = "0.5", features = ["std"] }
rpassword = { version = "5", optional = true }
serde = { version = "1", features = ["serde_derive"] }
serde_json = "1"
sha2 = "0.9"
secp256k1 = { version = "0.17", optional = true }
signatory = { version = "0.20", features = ["ecdsa", "ed25519", "encoding"] }
signatory-dalek = "0.20"
signatory-secp256k1 = { version = "0.20", optional = true }
signatory-ledger-tm = { version = "0.20", optional = true }
stdtx = { version = "0.2.4", optional = true }
signatory = { version = "0.22", features = ["ecdsa", "ed25519", "encoding"] }
signatory-ledger-tm = { version = "0.22", optional = true }
stdtx = { version = "0.3", optional = true }
subtle = "2"
subtle-encoding = { version = "0.5", features = ["bech32-preview"] }
tempfile = "3"
tendermint = { version = "0.15.0", features = ["secp256k1"] }
tendermint-rpc = { version = "0.15.0", optional = true, features = ["client"] }
tendermint = { version = "0.16.0", features = ["secp256k1"] }
tendermint-rpc = { version = "0.16.0", optional = true, features = ["client"] }
thiserror = "1"
wait-timeout = "0.2"
x25519-dalek = "1.1"
yubihsm = { version = "0.34", features = ["secp256k1", "setup", "usb"], optional = true }
yubihsm = { version = "=0.35.0-rc", features = ["secp256k1", "setup", "usb"], optional = true }
zeroize = "1"

[dev-dependencies.abscissa_core]
version = "0.5"
features = ["testing"]
[dev-dependencies]
abscissa_core = { version = "0.5", features = ["testing"] }
rand = "0.7"

[features]
ledgertm = ["signatory-ledger-tm"]
softsign = ["secp256k1", "signatory-secp256k1"]
softsign = []
tx-signer = ["abscissa_tokio", "hyper", "stdtx", "tendermint-rpc"]
yubihsm-mock = ["yubihsm/mockhsm"]
yubihsm-server = ["yubihsm/http-server", "rpassword"]
Expand Down
48 changes: 25 additions & 23 deletions src/commands/softsign/import.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
//! `tmkms softsign import` command
use crate::{config::provider::softsign::KeyFormat, keyring::SecretKeyEncoding, prelude::*};
use crate::{config::provider::softsign::KeyFormat, prelude::*};
use abscissa_core::{Command, Options, Runnable};
use signatory::{ed25519, encoding::Encode};
use std::{path::PathBuf, process};
use std::{fs::OpenOptions, io::Write, os::unix::fs::OpenOptionsExt, path::PathBuf, process};
use subtle_encoding::base64;
use tendermint::{config::PrivValidatorKey, PrivateKey};
use zeroize::Zeroizing;

/// `import` command: import a `priv_validator.json` formatted key and convert
/// it into the raw format used by the softsign backend (by default)
Expand Down Expand Up @@ -43,31 +44,32 @@ impl Runnable for ImportCommand {
})
.unwrap_or(KeyFormat::Json);

let seed = match format {
KeyFormat::Json => {
let private_key = PrivValidatorKey::load_json_file(input_path)
.unwrap_or_else(|e| {
status_err!("couldn't load {}: {}", input_path.display(), e);
process::exit(1);
})
.priv_key;
if format != KeyFormat::Json {
status_err!("invalid format: {:?} (must be 'json')", format);
process::exit(1);
}

match private_key {
PrivateKey::Ed25519(pk) => {
// TODO(tarcieri): upgrade Signatory version
ed25519::Seed::from_bytes(pk.to_seed().as_secret_slice()).unwrap()
}
}
}
KeyFormat::Base64 => {
status_err!("invalid format: baes64 (must be 'json' or 'raw')");
let private_key = PrivValidatorKey::load_json_file(input_path)
.unwrap_or_else(|e| {
status_err!("couldn't load {}: {}", input_path.display(), e);
process::exit(1);
}
})
.priv_key;

let private_key_encoded = match private_key {
PrivateKey::Ed25519(pk) => Zeroizing::new(base64::encode(pk.secret.as_bytes())),
_ => unreachable!("unsupported priv_validator.json algorithm"),
};

seed.encode_to_file(output_path, &SecretKeyEncoding::default())
OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.mode(0o600)
.open(output_path)
.and_then(|mut file| file.write_all(&*private_key_encoded))
.unwrap_or_else(|e| {
status_err!("couldn't write to {}: {}", output_path.display(), e);
status_err!("couldn't write `{}`: {}", output_path.display(), e);
process::exit(1);
});

Expand Down
22 changes: 6 additions & 16 deletions src/commands/softsign/keygen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
use crate::{keyring::SecretKeyEncoding, prelude::*};
use abscissa_core::{Command, Options, Runnable};
use rand::{rngs::OsRng, RngCore};
use k256::ecdsa;
use rand_core::OsRng;
use signatory::{ed25519, encoding::Encode};
use std::{fs::OpenOptions, io::Write, os::unix::fs::OpenOptionsExt};
use std::{path::PathBuf, process};
use std::{fs::OpenOptions, io::Write, os::unix::fs::OpenOptionsExt, path::PathBuf, process};
use subtle_encoding::base64;
use zeroize::Zeroize;

/// Default type of key to generate
Expand Down Expand Up @@ -56,17 +57,7 @@ impl Runnable for KeygenCommand {

/// Randomly generate a Base64-encoded secp256k1 key and store it at the given path
fn generate_secp256k1_key(output_path: &PathBuf) {
// This method may look gross but it is in fact the same method
// used by the upstream `secp256k1` crate's `rand` feature.
// We don't use that because it's using the outdated `rand` v0.6
let mut bytes = [0u8; 32];

loop {
OsRng.fill_bytes(&mut bytes);
if secp256k1::key::SecretKey::from_slice(&bytes).is_ok() {
break;
}
}
let signing_key = ecdsa::SigningKey::random(&mut OsRng);

let mut file = OpenOptions::new()
.create(true)
Expand All @@ -79,8 +70,7 @@ fn generate_secp256k1_key(output_path: &PathBuf) {
process::exit(1);
});

let mut encoded = subtle_encoding::base64::encode(&bytes);
bytes.zeroize();
let mut encoded = base64::encode(&signing_key.to_bytes());

file.write_all(&encoded).unwrap_or_else(|e| {
status_err!("couldn't write to `{}`: {}", output_path.display(), e);
Expand Down
5 changes: 3 additions & 2 deletions src/commands/yubihsm/keys/import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ impl ImportCommand {
.priv_key;

let seed = match private_key {
PrivateKey::Ed25519(pk) => pk.to_seed(),
PrivateKey::Ed25519(pk) => pk.secret,
_ => unreachable!(),
};

let label =
Expand All @@ -183,7 +184,7 @@ impl ImportCommand {
DEFAULT_DOMAINS,
DEFAULT_CAPABILITIES | yubihsm::Capability::EXPORTABLE_UNDER_WRAP,
yubihsm::asymmetric::Algorithm::Ed25519,
seed.as_secret_slice(),
seed.as_bytes().as_ref(),
) {
status_err!("couldn't import key #{}: {}", self.key_id.unwrap(), e);
process::exit(1);
Expand Down
18 changes: 8 additions & 10 deletions src/commands/yubihsm/keys/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,14 @@ fn display_key_info(
yubihsm::asymmetric::Algorithm::EcK256 => {
// The YubiHSM2 returns the uncompressed public key, so for
// compatibility with Tendermint, we have to compress it first
let uncompressed_pubkey =
k256::PublicKey::from_untagged_point(GenericArray::from_slice(public_key.as_ref()));

let compressed_point = k256::arithmetic::AffinePoint::from_pubkey(&uncompressed_pubkey)
.unwrap()
.to_compressed_pubkey();

let compressed_pubkey =
PublicKey::from_raw_secp256k1(compressed_point.as_bytes()).unwrap();
TendermintKey::AccountKey(compressed_pubkey)
let compressed_pubkey = k256::EncodedPoint::from_untagged_bytes(
GenericArray::from_slice(public_key.as_ref()),
)
.compress();

TendermintKey::AccountKey(
PublicKey::from_raw_secp256k1(compressed_pubkey.as_ref()).unwrap(),
)
}
yubihsm::asymmetric::Algorithm::Ed25519 => {
let pk = PublicKey::from_raw_ed25519(public_key.as_ref()).unwrap();
Expand Down
36 changes: 0 additions & 36 deletions src/config/validator.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
//! Validator configuration
use crate::{
connection::secret_connection,
error::{Error, ErrorKind::*},
keyring::SecretKeyEncoding,
prelude::*,
};
use serde::{Deserialize, Serialize};
use signatory::{ed25519, encoding::Decode};
use std::path::PathBuf;
use tendermint::{chain, net};

Expand Down Expand Up @@ -51,35 +44,6 @@ pub enum TendermintVersion {
V0_33,
}

impl ValidatorConfig {
/// Load the configured secret key from disk
pub fn load_secret_key(&self) -> Result<ed25519::Seed, Error> {
let secret_key_path = self.secret_key.as_ref().ok_or_else(|| {
format_err!(
VerificationError,
"config error: no `secret_key` for validator {}",
&self.addr
)
})?;

if !secret_key_path.exists() {
secret_connection::generate_key(&secret_key_path)?;
}

ed25519::Seed::decode_from_file(secret_key_path, &SecretKeyEncoding::default()).map_err(
|e| {
format_err!(
ConfigError,
"error loading Secret Connection key from {}: {}",
secret_key_path.display(),
e
)
.into()
},
)
}
}

/// Default value for the `ValidatorConfig` reconnect field
fn reconnect_default() -> bool {
true
Expand Down
57 changes: 33 additions & 24 deletions src/connection/secret_connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,30 @@ mod public_key;
pub use self::{amino_types::AuthSigMessage, kdf::Kdf, nonce::Nonce, public_key::PublicKey};
use crate::{
error::{Error, ErrorKind},
keyring::SecretKeyEncoding,
prelude::*,
};
use bytes::BufMut;
use chacha20poly1305::{
aead::{generic_array::GenericArray, AeadInPlace, NewAead},
ChaCha20Poly1305,
};
use ed25519_dalek::{self as ed25519, Signer, Verifier, SECRET_KEY_LENGTH};
use merlin::Transcript;
use prost_amino::{encoding::encode_varint, Message};
use signatory::{
ed25519,
encoding::Encode,
signature::{Signature, Signer, Verifier},
};
use signatory_dalek::Ed25519Verifier;
use rand_core::{OsRng, RngCore};
use std::{
cmp,
convert::TryInto,
convert::{TryFrom, TryInto},
fs::OpenOptions,
io::{self, Read, Write},
marker::{Send, Sync},
os::unix::fs::OpenOptionsExt,
path::Path,
};
use subtle::ConstantTimeEq;
use subtle_encoding::base64;
use x25519_dalek::{EphemeralSecret, PublicKey as EphemeralPublic};
use zeroize::Zeroizing;

/// Size of the MAC tag
pub const TAG_SIZE: usize = 16;
Expand All @@ -44,17 +43,27 @@ const TOTAL_FRAME_SIZE: usize = DATA_MAX_SIZE + DATA_LEN_SIZE;

/// Generate a Secret Connection key at the given path
pub fn generate_key(path: impl AsRef<Path>) -> Result<(), Error> {
ed25519::Seed::generate()
.encode_to_file(path.as_ref(), &SecretKeyEncoding::default())
.map_err(|_| {
let mut secret_key = Zeroizing::new([0u8; SECRET_KEY_LENGTH]);
OsRng.fill_bytes(&mut *secret_key);

let secret_key_encoded = Zeroizing::new(base64::encode(&*secret_key));

OpenOptions::new()
.create(true)
.write(true)
.truncate(true)
.mode(0o600)
.open(path.as_ref())
.and_then(|mut file| file.write_all(&secret_key_encoded))
.map_err(|e| {
format_err!(
ErrorKind::IoError,
"couldn't write: {}",
path.as_ref().display()
"couldn't write `{}`: {}",
path.as_ref().display(),
e
)
})?;

Ok(())
.into()
})
}

/// Encrypted connection between peers in a Tendermint network
Expand All @@ -77,10 +86,11 @@ impl<IoHandler: Read + Write + Send + Sync> SecretConnection<IoHandler> {
/// Performs handshake and returns a new authenticated SecretConnection.
pub fn new(
mut handler: IoHandler,
local_pubkey: &PublicKey,
local_privkey: &dyn Signer<ed25519::Signature>,
local_privkey: &ed25519::Keypair,
v0_33_handshake: bool,
) -> Result<SecretConnection<IoHandler>, Error> {
let local_pubkey = PublicKey::from(local_privkey);

// Generate ephemeral keys for perfect forward secrecy.
let (local_eph_pubkey, local_eph_privkey) = gen_eph_keys();

Expand Down Expand Up @@ -150,17 +160,17 @@ impl<IoHandler: Read + Write + Send + Sync> SecretConnection<IoHandler> {
};

let remote_pubkey = ed25519::PublicKey::from_bytes(&auth_sig_msg.key)
.ok_or_else(|| ErrorKind::CryptoError)?;
.map_err(|_| ErrorKind::CryptoError)?;

let remote_sig = ed25519::Signature::from_bytes(auth_sig_msg.sig.as_slice())
let remote_sig = ed25519::Signature::try_from(auth_sig_msg.sig.as_slice())
.map_err(|_| ErrorKind::CryptoError)?;

if v0_33_handshake {
Ed25519Verifier::from(&remote_pubkey)
remote_pubkey
.verify(&sc_mac, &remote_sig)
.map_err(|_| ErrorKind::CryptoError)?;
} else {
Ed25519Verifier::from(&remote_pubkey)
remote_pubkey
.verify(&kdf.challenge, &remote_sig)
.map_err(|_| ErrorKind::CryptoError)?;
}
Expand Down Expand Up @@ -324,8 +334,7 @@ where

/// Returns pubkey, private key
fn gen_eph_keys() -> (EphemeralPublic, EphemeralSecret) {
let mut local_csprng = rand::thread_rng();
let local_privkey = EphemeralSecret::new(&mut local_csprng);
let local_privkey = EphemeralSecret::new(&mut OsRng);
let local_pubkey = EphemeralPublic::from(&local_privkey);
(local_pubkey, local_privkey)
}
Expand Down
Loading

0 comments on commit 1cfc62e

Please sign in to comment.