diff --git a/cryptoki-rustcrypto/src/lib.rs b/cryptoki-rustcrypto/src/lib.rs index 68b1ccc..1500192 100644 --- a/cryptoki-rustcrypto/src/lib.rs +++ b/cryptoki-rustcrypto/src/lib.rs @@ -9,6 +9,7 @@ use cryptoki::{ }; pub mod ecdsa; +pub mod rng; pub mod rsa; pub mod x509; @@ -21,6 +22,7 @@ pub trait SessionLike { attributes: &[AttributeType], ) -> Result>; fn sign(&self, mechanism: &Mechanism, key: ObjectHandle, data: &[u8]) -> Result>; + fn generate_random_slice(&self, random_data: &mut [u8]) -> Result<()>; } impl SessionLike for Session { @@ -40,6 +42,9 @@ impl SessionLike for Session { fn sign(&self, mechanism: &Mechanism, key: ObjectHandle, data: &[u8]) -> Result> { Session::sign(self, mechanism, key, data) } + fn generate_random_slice(&self, random_data: &mut [u8]) -> Result<()> { + Session::generate_random_slice(self, random_data) + } } impl<'s> SessionLike for &'s Session { @@ -59,4 +64,7 @@ impl<'s> SessionLike for &'s Session { fn sign(&self, mechanism: &Mechanism, key: ObjectHandle, data: &[u8]) -> Result> { Session::sign(self, mechanism, key, data) } + fn generate_random_slice(&self, random_data: &mut [u8]) -> Result<()> { + Session::generate_random_slice(self, random_data) + } } diff --git a/cryptoki-rustcrypto/src/rng.rs b/cryptoki-rustcrypto/src/rng.rs new file mode 100644 index 0000000..7109a52 --- /dev/null +++ b/cryptoki-rustcrypto/src/rng.rs @@ -0,0 +1,50 @@ +// Copyright 2023 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +use signature::rand_core::{CryptoRng, Error as RndError, RngCore}; +use thiserror::Error; + +use crate::SessionLike; + +#[derive(Debug, Error)] +pub enum Error {} + +/// [`Rng`] is a PKCS#11-backed CSPRNG. +pub struct Rng(S); + +// TODO(baloo): check for CKF_RNG bit flag (CK_TOKEN_INFO struct -> flags) +impl Rng { + pub fn new(session: S) -> Result { + Ok(Self(session)) + } +} + +macro_rules! impl_next_uint { + ($self:ident, $u:ty) => {{ + let mut buf = <$u>::MIN.to_be_bytes(); + $self.fill_bytes(&mut buf[..]); + + <$u>::from_be_bytes(buf) + }}; +} + +impl RngCore for Rng { + fn next_u32(&mut self) -> u32 { + impl_next_uint!(self, u32) + } + + fn next_u64(&mut self) -> u64 { + impl_next_uint!(self, u64) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.try_fill_bytes(dest) + .expect("Cryptoki provider failed to generate random"); + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), RndError> { + self.0.generate_random_slice(dest).map_err(RndError::new) + } +} + +impl CryptoRng for Rng {} diff --git a/cryptoki-rustcrypto/tests/rng.rs b/cryptoki-rustcrypto/tests/rng.rs new file mode 100644 index 0000000..38184d6 --- /dev/null +++ b/cryptoki-rustcrypto/tests/rng.rs @@ -0,0 +1,57 @@ +// Copyright 2023 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +mod common; + +use crate::common::USER_PIN; +use common::init_pins; +use cryptoki::{session::UserType, types::AuthPin}; +use cryptoki_rustcrypto::rng::Rng; +use serial_test::serial; +use signature::rand_core::{CryptoRngCore, RngCore}; +use testresult::TestResult; + +// This test is meant to ensure we provide [`rand_core::CryptoRngCore`]. +// This is the trait consumers will use throughout the RustCrypto ecosystem +// to express interest in a CSPRNG. +#[test] +#[serial] +fn ensure_crypto_rng_core() -> TestResult { + fn just_making_sure(_r: &mut R) { + // Hi! just making sure you provide a CSPRNG. + } + let (pkcs11, slot) = init_pins(); + + // open a session + let session = pkcs11.open_rw_session(slot)?; + + // log in the session + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + let mut rng = Rng::new(session).unwrap(); + just_making_sure(&mut rng); + + Ok(()) +} + +#[test] +#[serial] +fn generate_random() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // open a session + let session = pkcs11.open_rw_session(slot)?; + + // log in the session + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + let mut rng = Rng::new(session).unwrap(); + rng.next_u32(); + rng.next_u64(); + + let mut buf = vec![0; 123]; + rng.fill_bytes(&mut buf); + rng.try_fill_bytes(&mut buf).unwrap(); + + Ok(()) +}