Skip to content

RngCore::fill_bytes is infallible; hardware RNG failure cannot be surfaced — TryCryptoRng is the fix and needs to be the primary recommended path #2364

@MarkAtwood

Description

@MarkAtwood

Repo: RustCrypto/traits (affects rand_core)
Labels: api-design, rand, hardware, fips

Background

rand_core::RngCore::fill_bytes(&mut self, dest: &mut [u8]) returns ().
For a software PRNG (ChaCha20, DRBG backed by a software seed), this is
fine — filling bytes from a seeded generator cannot fail.

For a hardware entropy source, failure is a normal operational condition,
not a programmer error:

  • The hardware may not be ready (power-on self-test still running).
  • The entropy pool may be temporarily exhausted (valid in some designs).
  • The hardware may report a fault that must be handled — this is precisely what
    FIPS 140-3's Continuous Random Bit Generator (CRBG) test is designed to
    detect.

rand_core 0.9 added TryCryptoRng with a fallible try_fill_bytes to
address exactly this. We have adopted it, and it solves the problem for our
crate.

What remains

The issue is adoption and discoverability:

1. TryCryptoRng is not the documented primary interface for hardware RNG
backends.
The rand_core documentation and ecosystem treat it as an
advanced feature. A developer writing a new hardware entropy driver will reach
for RngCore first and implement fill_bytes with a panic or a silent
discard of errors, because that is what the documentation implies.

2. Key generation APIs still take impl CryptoRng, not impl TryCryptoRng.
For example:

// In signature crates:
pub fn generate(rng: &mut impl CryptoRng) -> Self;

// In KeyInit:
pub fn generate(rng: &mut impl CryptoRng) -> Self;

A hardware RNG backend correctly implements TryCryptoRng but has no
CryptoRng blanket impl (or the blanket impl panics on failure, defeating the
purpose). Key generation from a hardware source that can fail cannot propagate
errors through these APIs.

In our codebase (discovered while implementing wolfcrypt, a RustCrypto backend wrapping wolfCrypt — a FIPS 140-3 validated C cryptographic library with hardware dispatch via WOLF_CRYPTO_CB):

Our WolfRng type implements TryCryptoRng. The fill_bytes implementation
is forced to assert! because RngCore::fill_bytes returns (), and the code
comment explicitly documents why:

"RNG failure is unrecoverable" as a direct consequence of the trait signature

wolfcrypt/src/rand.rs:80-90

What we are asking for

  1. Document TryCryptoRng as the required interface for hardware entropy
    sources
    , not just an optional extra. A hardware RNG backend that
    implements only RngCore::fill_bytes with a panic is not a correct
    implementation; this should be stated clearly.

  2. Audit key generation entry pointsKeyInit::generate,
    SigningKey::generate, and similar — and add TryCryptoRng-accepting
    variants so that hardware entropy sources can propagate failures through key
    generation without panicking.

  3. Apply the same Try* pattern to digest::Update and Mac (see
    companion Issue 9) to complete the hardware-dispatch story: if entropy,
    hashing, and MACing can all fail and propagate errors, a hardware-backed
    crypto pipeline becomes first-class.

Prior art

  • rand_core 0.9: TryCryptoRng / try_fill_bytes — already shipped; this
    is exactly the right design.
  • embedded-hal 1.0: fallible type Error on all peripheral traits, including
    RNG.
  • embedded-hal-async: async + fallible, showing the pattern composes with
    async hardware dispatch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions