Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions wrapper/rust/wolfssl-wolfcrypt/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ Breaking changes:

New features:

- Add the rand_core_0_9 feature: RNG implements the rand_core 0.9
TryRngCore and TryCryptoRng traits, alongside the rand_core 0.10 ones, for
users of the rand 0.9 ecosystem
- Add Curve25519Key::generate_shared_rng() to generate a key from an RNG shared
between keys via Rc (requires the alloc feature)
- Add HMAC::copy(), a fallible equivalent of HMAC::clone()
Expand Down
7 changes: 7 additions & 0 deletions wrapper/rust/wolfssl-wolfcrypt/Cargo.lock

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

2 changes: 2 additions & 0 deletions wrapper/rust/wolfssl-wolfcrypt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ links = "wolfssl"
[features]
alloc = []
rand_core = ["dep:rand_core"]
rand_core_0_9 = ["dep:rand_core_0_9"]
aead = ["dep:aead"]
cipher = ["dep:cipher"]
mac = ["digest/mac"]
Expand All @@ -24,6 +25,7 @@ kem = ["dep:kem", "hybrid-array/extra-sizes"]

[dependencies]
rand_core = { version = "0.10", optional = true, default-features = false }
rand_core_0_9 = { package = "rand_core", version = "0.9", optional = true, default-features = false }
aead = { version = "0.5", optional = true, default-features = false }
cipher = { version = "0.5", optional = true, default-features = false }
digest = { version = "0.11", optional = true, default-features = false, features = ["block-api"] }
Expand Down
2 changes: 1 addition & 1 deletion wrapper/rust/wolfssl-wolfcrypt/Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FEATURES := alloc,rand_core,aead,cipher,digest,mac,signature,password-hash,kem
FEATURES := alloc,rand_core,rand_core_0_9,aead,cipher,digest,mac,signature,password-hash,kem
CARGO_FEATURE_FLAGS := --features $(FEATURES)

.PHONY: all
Expand Down
7 changes: 7 additions & 0 deletions wrapper/rust/wolfssl-wolfcrypt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ default.
| `cipher` | `cipher` | AES (aes) |
| `aead` | `aead` | AES-GCM/CCM/EAX (aes), ChaCha20-Poly1305 |
| `rand_core` | `rand_core` | RNG (random) |
| `rand_core_0_9` | `rand_core` 0.9 | RNG (random) |
| `kem` | `kem` | ML-KEM (mlkem_kem) |
| `password-hash` | `password-hash` | PBKDF2 (pbkdf2_password_hash), scrypt (scrypt_password_hash) |

Expand All @@ -95,6 +96,12 @@ let mut buf = [0u8; 32];
rng.fill_bytes(&mut buf);
```

For projects still on the `rand` 0.9 ecosystem, the `rand_core_0_9` feature
implements the equivalent `rand_core` 0.9 traits, `TryRngCore` and
`TryCryptoRng`, with the same `RngError`. Wrap the RNG in the 0.9
`UnwrapErr` to get `RngCore` and `CryptoRng`. Both features can be enabled at
once.

Enable features in your `Cargo.toml`, for example:

```
Expand Down
54 changes: 49 additions & 5 deletions wrapper/rust/wolfssl-wolfcrypt/src/random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -423,29 +423,29 @@ impl RNG {
/// It carries the wolfSSL library error code of the failed
/// `wc_RNG_GenerateBlock()` call, so that entropy, reseed and hardware
/// failures are reported to the caller instead of terminating the process.
#[cfg(feature = "rand_core")]
#[cfg(any(feature = "rand_core", feature = "rand_core_0_9"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RngError(i32);

#[cfg(feature = "rand_core")]
#[cfg(any(feature = "rand_core", feature = "rand_core_0_9"))]
impl RngError {
/// Return the wolfSSL library error code that caused the failure.
pub fn code(&self) -> i32 {
self.0
}
}

#[cfg(feature = "rand_core")]
#[cfg(any(feature = "rand_core", feature = "rand_core_0_9"))]
impl core::fmt::Display for RngError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "wolfSSL RNG failure (error code {})", self.0)
}
}

#[cfg(feature = "rand_core")]
#[cfg(any(feature = "rand_core", feature = "rand_core_0_9"))]
impl core::error::Error for RngError {}

#[cfg(feature = "rand_core")]
#[cfg(any(feature = "rand_core", feature = "rand_core_0_9"))]
impl From<RngError> for i32 {
fn from(err: RngError) -> i32 {
err.0
Expand Down Expand Up @@ -496,6 +496,50 @@ impl rand_core::TryRng for RNG {
#[cfg(feature = "rand_core")]
impl rand_core::TryCryptoRng for RNG {}

/// Implement `rand_core` 0.9's `TryRngCore` for `RNG`, for callers still on
/// the `rand` 0.9 ecosystem.
///
/// This mirrors the `rand_core` 0.10 `TryRng` implementation, failures are
/// returned as an [`RngError`]. Wrap the RNG in the 0.9 `UnwrapErr` adapter
/// to get the infallible `RngCore` and `CryptoRng` traits, which panic on RNG
/// failure.
///
/// ```rust
/// #![cfg(feature = "rand_core_0_9")]
/// use rand_core_0_9::{RngCore, UnwrapErr};
/// use wolfssl_wolfcrypt::random::RNG;
///
/// let mut rng = UnwrapErr(RNG::new().expect("Failed to create RNG"));
/// let mut buf = [0u8; 32];
/// // Panics on RNG failure.
/// rng.fill_bytes(&mut buf);
/// ```
#[cfg(feature = "rand_core_0_9")]
impl rand_core_0_9::TryRngCore for RNG {
type Error = RngError;

fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
let mut buf = [0u8; 4];
self.generate_block(&mut buf).map_err(RngError)?;
Ok(u32::from_le_bytes(buf))
}

fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
let mut buf = [0u8; 8];
self.generate_block(&mut buf).map_err(RngError)?;
Ok(u64::from_le_bytes(buf))
}

fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
self.generate_block(dest).map_err(RngError)
}
}

/// Mark `RNG` as a cryptographically secure random number generator for
/// `rand_core` 0.9.
#[cfg(feature = "rand_core_0_9")]
impl rand_core_0_9::TryCryptoRng for RNG {}

/// `wc_rng_new_ex()`, or on libraries without it (FIPS v5) the older
/// `wc_rng_new()`, which takes no device ID, so any device ID is refused there.
///
Expand Down
63 changes: 63 additions & 0 deletions wrapper/rust/wolfssl-wolfcrypt/tests/test_random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,66 @@ fn test_rng_try_rng_error_type() {
let rng = RNG::new().expect("Failed to create RNG");
requires_rng_error(&rng);
}

#[test]
#[cfg(feature = "rand_core_0_9")]
fn test_rng_rand_core_0_9_fill_bytes() {
use rand_core_0_9::{RngCore, UnwrapErr};
let mut rng = UnwrapErr(RNG::new().expect("Failed to create RNG"));
let mut buf = [0u8; 32];
rng.fill_bytes(&mut buf);
assert_ne!(buf, [0u8; 32]);
}

#[test]
#[cfg(feature = "rand_core_0_9")]
fn test_rng_rand_core_0_9_try_fill_bytes() {
use rand_core_0_9::TryRngCore;
let mut rng = RNG::new().expect("Failed to create RNG");
let mut buf = [0u8; 32];
rng.try_fill_bytes(&mut buf).expect("Failed to try_fill_bytes");
assert_ne!(buf, [0u8; 32]);
}

#[test]
#[cfg(feature = "rand_core_0_9")]
fn test_rng_rand_core_0_9_next_u32() {
use rand_core_0_9::{RngCore, UnwrapErr};
let mut rng = UnwrapErr(RNG::new().expect("Failed to create RNG"));
// Generate several values and verify they aren't all zero.
let v: u64 = (0..4).map(|_| rng.next_u32() as u64).sum();
assert_ne!(v, 0);
}

#[test]
#[cfg(feature = "rand_core_0_9")]
fn test_rng_rand_core_0_9_next_u64() {
use rand_core_0_9::{RngCore, UnwrapErr};
let mut rng = UnwrapErr(RNG::new().expect("Failed to create RNG"));
// Generate two values and verify they aren't all ones.
let v1 = rng.next_u64();
let v2 = rng.next_u64();
assert_ne!(v1 & v2, u64::MAX);
}

/// Wrapped in the 0.9 `UnwrapErr`, the RNG satisfies the 0.9 `CryptoRng`
/// contract, which is what `rand` 0.9 APIs require.
#[test]
#[cfg(feature = "rand_core_0_9")]
fn test_rng_rand_core_0_9_unwrap_err_is_crypto_rng() {
use rand_core_0_9::{CryptoRng, UnwrapErr};
fn requires_crypto_rng<R: CryptoRng>(_: &R) {}
let rng = UnwrapErr(RNG::new().expect("Failed to create RNG"));
requires_crypto_rng(&rng);
}

/// The 0.9 `TryRngCore` must stay fallible, like the 0.10 `TryRng`.
#[test]
#[cfg(feature = "rand_core_0_9")]
fn test_rng_rand_core_0_9_error_type() {
use rand_core_0_9::{TryCryptoRng, TryRngCore};
use wolfssl_wolfcrypt::random::RngError;
fn requires_rng_error<R: TryCryptoRng + TryRngCore<Error = RngError>>(_: &R) {}
let rng = RNG::new().expect("Failed to create RNG");
requires_rng_error(&rng);
}
Loading