Skip to content

Commit

Permalink
Implement RngCore and CryptoRng for Hsm.
Browse files Browse the repository at this point in the history
This allows us to replace use of the ChaCha20 Rng w/ the YubiHSM RNG /
Hsm type. The RngCore API fills in a buffer provided by the caller while
the YubiHSM `get_pseudo_random` allocates & returns a Vec<u8>. This
impedance mismatch between the two APIs makes for unnecessary allocation
but saves us some code.
  • Loading branch information
flihp committed Nov 20, 2024
1 parent 2e2673b commit 00400b9
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 20 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ num-bigint = "0.4.6"
p256 = "0.12"
pem-rfc7468 = { version = "0.7.0", features = ["alloc", "std"] }
rand = "0.8.5"
rand_chacha = "0.3.1"
rand_core = { version = "0.6.4", features = ["std"] }
rpassword = "7.3.1"
serde = "1.0.210"
serde_json = "1.0.128"
Expand Down
52 changes: 36 additions & 16 deletions src/hsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use log::{debug, error, info};
use p256::elliptic_curve::PrimeField;
use p256::{NonZeroScalar, ProjectivePoint, Scalar, SecretKey};
use pem_rfc7468::LineEnding;
use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng};
use rand_core::{impls, CryptoRng, Error as RngError, RngCore};
use static_assertions as sa;
use std::collections::HashSet;
use std::{
Expand Down Expand Up @@ -37,7 +37,6 @@ const CAPS: Capability = Capability::all();
const DELEGATED_CAPS: Capability = Capability::all();
const DOMAIN: Domain = Domain::all();
const ID: Id = 0x1;
const SEED_LEN: usize = 32;
const KEY_LEN: usize = 32;
const SHARE_LEN: usize = KEY_LEN + 1;
const LABEL: &str = "backup";
Expand Down Expand Up @@ -208,24 +207,18 @@ impl Hsm {
/// then put the key into the YubiHSM. The shares and the verifier are then
/// returned to the caller. Generally they will then be distributed
/// 'off-platform' somehow.
pub fn new_split_wrap(&self) -> Result<(Zeroizing<SharesMax>, Verifier)> {
pub fn new_split_wrap(
&mut self,
) -> Result<(Zeroizing<SharesMax>, Verifier)> {
info!(
"Generating wrap / backup key from HSM PRNG with label: \"{}\"",
LABEL.to_string()
);
// get 32 bytes from YubiHSM PRNG
// TODO: zeroize
let wrap_key = self.client.get_pseudo_random(KEY_LEN)?;
let rng_seed = self.client.get_pseudo_random(SEED_LEN)?;
let rng_seed: [u8; SEED_LEN] =
rng_seed.try_into().map_err(|v: Vec<u8>| {
anyhow::anyhow!(
"Expected vec with {} elements, got {}",
SEED_LEN,
v.len()
)
})?;
let mut rng = ChaCha20Rng::from_seed(rng_seed);
let mut wrap_key = [0u8; KEY_LEN];
self.try_fill_bytes(&mut wrap_key)?;
let wrap_key = wrap_key;

info!("Splitting wrap key into {} shares.", LIMIT);
let wrap_key = SecretKey::from_be_bytes(&wrap_key)?;
Expand All @@ -237,9 +230,9 @@ impl Hsm {
let (shares, verifier) = Feldman::<THRESHOLD, LIMIT>::split_secret::<
Scalar,
ProjectivePoint,
ChaCha20Rng,
Self,
SHARE_LEN,
>(*nzs.as_ref(), None, &mut rng)
>(*nzs.as_ref(), None, &mut *self)
.map_err(|e| HsmError::SplitKeyFailed { e })?;

// put 32 random bytes into the YubiHSM as an Aes256Ccm wrap key
Expand Down Expand Up @@ -466,6 +459,33 @@ impl Hsm {
}
}

impl RngCore for Hsm {
fn next_u32(&mut self) -> u32 {
impls::next_u32_via_fill(self)
}
fn next_u64(&mut self) -> u64 {
impls::next_u64_via_fill(self)
}
fn fill_bytes(&mut self, dest: &mut [u8]) {
self.try_fill_bytes(dest)
.expect("RNG failed to fill the provided buffer.")
}
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), RngError> {
// The yubihsm.rs client allocates memory for the bytes that we
// request here. Then we copy them to the slice provided by the
// caller. API impedence mismatch.
let bytes = match self.client.get_pseudo_random(dest.len()) {
Ok(b) => Ok(b),
Err(e) => Err(RngError::new(e)),
}?;
dest.copy_from_slice(&bytes);
Ok(())
}
}

// This is required for Feldman::split_secret to use `Hms` as an RNG.
impl CryptoRng for Hsm {}

/// Provided a key ID and a object type this function will find the object
/// in the HSM and generate the appropriate KeySpec for it.
pub fn backup_object<P: AsRef<Path>>(
Expand Down
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ fn do_ceremony<P: AsRef<Path>>(
let passwd_new = {
// assume YubiHSM is in default state: use default auth credentials
let passwd = "password".to_string();
let hsm = Hsm::new(
let mut hsm = Hsm::new(
1,
&passwd,
&args.output,
Expand Down Expand Up @@ -689,7 +689,7 @@ fn main() -> Result<()> {
} => {
let passwd = get_passwd(auth_id, &command)?;
let auth_id = get_auth_id(auth_id, &command);
let hsm = Hsm::new(
let mut hsm = Hsm::new(
auth_id,
&passwd,
&args.output,
Expand Down

0 comments on commit 00400b9

Please sign in to comment.