Skip to content

Commit 00400b9

Browse files
committed
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<u8>. This impedance mismatch between the two APIs makes for unnecessary allocation but saves us some code.
1 parent 2e2673b commit 00400b9

File tree

4 files changed

+40
-20
lines changed

4 files changed

+40
-20
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ num-bigint = "0.4.6"
2121
p256 = "0.12"
2222
pem-rfc7468 = { version = "0.7.0", features = ["alloc", "std"] }
2323
rand = "0.8.5"
24-
rand_chacha = "0.3.1"
24+
rand_core = { version = "0.6.4", features = ["std"] }
2525
rpassword = "7.3.1"
2626
serde = "1.0.210"
2727
serde_json = "1.0.128"

src/hsm.rs

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use log::{debug, error, info};
77
use p256::elliptic_curve::PrimeField;
88
use p256::{NonZeroScalar, ProjectivePoint, Scalar, SecretKey};
99
use pem_rfc7468::LineEnding;
10-
use rand_chacha::{rand_core::SeedableRng, ChaCha20Rng};
10+
use rand_core::{impls, CryptoRng, Error as RngError, RngCore};
1111
use static_assertions as sa;
1212
use std::collections::HashSet;
1313
use std::{
@@ -37,7 +37,6 @@ const CAPS: Capability = Capability::all();
3737
const DELEGATED_CAPS: Capability = Capability::all();
3838
const DOMAIN: Domain = Domain::all();
3939
const ID: Id = 0x1;
40-
const SEED_LEN: usize = 32;
4140
const KEY_LEN: usize = 32;
4241
const SHARE_LEN: usize = KEY_LEN + 1;
4342
const LABEL: &str = "backup";
@@ -208,24 +207,18 @@ impl Hsm {
208207
/// then put the key into the YubiHSM. The shares and the verifier are then
209208
/// returned to the caller. Generally they will then be distributed
210209
/// 'off-platform' somehow.
211-
pub fn new_split_wrap(&self) -> Result<(Zeroizing<SharesMax>, Verifier)> {
210+
pub fn new_split_wrap(
211+
&mut self,
212+
) -> Result<(Zeroizing<SharesMax>, Verifier)> {
212213
info!(
213214
"Generating wrap / backup key from HSM PRNG with label: \"{}\"",
214215
LABEL.to_string()
215216
);
216217
// get 32 bytes from YubiHSM PRNG
217218
// TODO: zeroize
218-
let wrap_key = self.client.get_pseudo_random(KEY_LEN)?;
219-
let rng_seed = self.client.get_pseudo_random(SEED_LEN)?;
220-
let rng_seed: [u8; SEED_LEN] =
221-
rng_seed.try_into().map_err(|v: Vec<u8>| {
222-
anyhow::anyhow!(
223-
"Expected vec with {} elements, got {}",
224-
SEED_LEN,
225-
v.len()
226-
)
227-
})?;
228-
let mut rng = ChaCha20Rng::from_seed(rng_seed);
219+
let mut wrap_key = [0u8; KEY_LEN];
220+
self.try_fill_bytes(&mut wrap_key)?;
221+
let wrap_key = wrap_key;
229222

230223
info!("Splitting wrap key into {} shares.", LIMIT);
231224
let wrap_key = SecretKey::from_be_bytes(&wrap_key)?;
@@ -237,9 +230,9 @@ impl Hsm {
237230
let (shares, verifier) = Feldman::<THRESHOLD, LIMIT>::split_secret::<
238231
Scalar,
239232
ProjectivePoint,
240-
ChaCha20Rng,
233+
Self,
241234
SHARE_LEN,
242-
>(*nzs.as_ref(), None, &mut rng)
235+
>(*nzs.as_ref(), None, &mut *self)
243236
.map_err(|e| HsmError::SplitKeyFailed { e })?;
244237

245238
// put 32 random bytes into the YubiHSM as an Aes256Ccm wrap key
@@ -466,6 +459,33 @@ impl Hsm {
466459
}
467460
}
468461

462+
impl RngCore for Hsm {
463+
fn next_u32(&mut self) -> u32 {
464+
impls::next_u32_via_fill(self)
465+
}
466+
fn next_u64(&mut self) -> u64 {
467+
impls::next_u64_via_fill(self)
468+
}
469+
fn fill_bytes(&mut self, dest: &mut [u8]) {
470+
self.try_fill_bytes(dest)
471+
.expect("RNG failed to fill the provided buffer.")
472+
}
473+
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), RngError> {
474+
// The yubihsm.rs client allocates memory for the bytes that we
475+
// request here. Then we copy them to the slice provided by the
476+
// caller. API impedence mismatch.
477+
let bytes = match self.client.get_pseudo_random(dest.len()) {
478+
Ok(b) => Ok(b),
479+
Err(e) => Err(RngError::new(e)),
480+
}?;
481+
dest.copy_from_slice(&bytes);
482+
Ok(())
483+
}
484+
}
485+
486+
// This is required for Feldman::split_secret to use `Hms` as an RNG.
487+
impl CryptoRng for Hsm {}
488+
469489
/// Provided a key ID and a object type this function will find the object
470490
/// in the HSM and generate the appropriate KeySpec for it.
471491
pub fn backup_object<P: AsRef<Path>>(

src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ fn do_ceremony<P: AsRef<Path>>(
304304
let passwd_new = {
305305
// assume YubiHSM is in default state: use default auth credentials
306306
let passwd = "password".to_string();
307-
let hsm = Hsm::new(
307+
let mut hsm = Hsm::new(
308308
1,
309309
&passwd,
310310
&args.output,
@@ -689,7 +689,7 @@ fn main() -> Result<()> {
689689
} => {
690690
let passwd = get_passwd(auth_id, &command)?;
691691
let auth_id = get_auth_id(auth_id, &command);
692-
let hsm = Hsm::new(
692+
let mut hsm = Hsm::new(
693693
auth_id,
694694
&passwd,
695695
&args.output,

0 commit comments

Comments
 (0)