From 00400b98e54260d15969a6659fb163bfce82a244 Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Wed, 20 Nov 2024 11:44:10 -0800 Subject: [PATCH] Implement RngCore and CryptoRng for Hsm. 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. This impedance mismatch between the two APIs makes for unnecessary allocation but saves us some code. --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/hsm.rs | 52 ++++++++++++++++++++++++++++++++++++---------------- src/main.rs | 4 ++-- 4 files changed, 40 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f3556d2..ae9633c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1046,7 +1046,7 @@ dependencies = [ "p256 0.12.0", "pem-rfc7468", "rand", - "rand_chacha 0.3.1", + "rand_core 0.6.4", "rpassword", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 3197bcc..f0fd513 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/hsm.rs b/src/hsm.rs index 1e6c6aa..708dd8a 100644 --- a/src/hsm.rs +++ b/src/hsm.rs @@ -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::{ @@ -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"; @@ -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, Verifier)> { + pub fn new_split_wrap( + &mut self, + ) -> Result<(Zeroizing, 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| { - 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)?; @@ -237,9 +230,9 @@ impl Hsm { let (shares, verifier) = Feldman::::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 @@ -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>( diff --git a/src/main.rs b/src/main.rs index 22ec302..3166f33 100644 --- a/src/main.rs +++ b/src/main.rs @@ -304,7 +304,7 @@ fn do_ceremony>( 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, @@ -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,