RustCrypto / RustCrypto/traits
`digest::Update`, `FixedOutput`, and `Mac` are infallible; hardware backends are forced to panic when operations fail
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 755
- Forks
- 256
- Avg merge
- 1h 27m
- Merged PRs (30d)
- 2
Description
Repo: RustCrypto/traits
Labels: api-design, digest, hardware
Background
Issue 1 covers infallible constructors. This issue covers infallible runtime
operations.
The core streaming traits define methods that return ():
digest::Update::update(&mut self, data: &[u8])digest::FixedOutput::finalize_into(self, out: &mut Output<Self>)digest::FixedOutputReset::finalize_into_reset(&mut self, out: &mut Output<Self>)universal_hash::UniversalHash::update(&mut self, blocks: &[Block<Self>])
For a pure-Rust software implementation, these are genuinely infallible.
A SHA-256 update call is a few arithmetic operations; it cannot fail.
Making the return type Result<(), E> for software implementations would be
noise — the Err branch is unreachable.
The problem
For a hardware backend, every one of these operations dispatches to a driver,
HSM, or hardware accelerator that can fail at runtime:
| Trait | Method | Return type | Hardware failure mode |
|---|---|---|---|
digest::Update |
update(&mut self, data: &[u8]) |
() |
HSM busy, DMA fault, CryptoCb error |
digest::FixedOutput |
finalize_into(self, out: &mut Output<Self>) |
() |
Hardware finalization error |
digest::FixedOutputReset |
finalize_into_reset(&mut self, out: &mut Output<Self>) |
() |
Finalization + re-init failure |
universal_hash::UniversalHash |
update(&mut self, blocks) |
() |
Hardware MAC block fault |
digest::FixedOutput |
finalize_into (used by Mac via blanket) |
() |
Hardware MAC finalization error |
cipher::StreamCipher |
apply_keystream(&mut self, buf: &mut [u8]) |
() |
Hardware cipher fault |
wolfCrypt's WOLF_CRYPTO_CB mechanism routes each algorithm operation through a
registered C callback. That callback can return WC_NO_ERR_TRACE(CRYPTOCB_UNAVAILABLE)
when the hardware device is busy, or a device-specific error code if the
hardware operation fails. The trait gives us nowhere to put that error.
In our implementation
Discovered while implementing wolfcrypt, a RustCrypto backend wrapping wolfCrypt — a FIPS 140-3 validated C cryptographic library with hardware dispatch via WOLF_CRYPTO_CB.
In every case, we are forced to assert! on the C return code, converting
hardware failures into panics:
digest::Update::updateasserts onwc_Sha256Update:
wolfcrypt/src/digest.rs:94-97digest::FixedOutput::finalize_intoasserts onwc_Sha256Final:
wolfcrypt/src/digest.rs:103-105digest::FixedOutputReset::finalize_into_resetasserts on both finalize and
re-init:wolfcrypt/src/digest.rs:119-124Mac(HMAC)Update::updateasserts onwolfcrypt_hmac_update:
wolfcrypt/src/hmac.rs:88-91Mac(HMAC)FixedOutput::finalize_intoasserts onwolfcrypt_hmac_final:
wolfcrypt/src/hmac.rs:99-102Mac(CMAC)Update::updateasserts onwolfcrypt_cmac_update:
wolfcrypt/src/cmac.rs:90-93UniversalHash::update(Poly1305) asserts onwc_Poly1305Update:
wolfcrypt/src/poly1305.rs:96-99UniversalHashfinalize (Poly1305) asserts onwc_Poly1305Final:
wolfcrypt/src/poly1305.rs:111-114
Proposed change
Add Try* variants of the affected traits with fallible signatures and blanket
impls for existing software implementations:
pub trait TryUpdate {
type Error;
fn try_update(&mut self, data: &[u8]) -> Result<(), Self::Error>;
}
// Blanket impl: all existing Update implementors get TryUpdate for free
impl<T: Update> TryUpdate for T {
type Error = core::convert::Infallible;
fn try_update(&mut self, data: &[u8]) -> Result<(), Infallible> {
self.update(data);
Ok(())
}
}
pub trait TryFixedOutput: TryUpdate {
fn try_finalize_into(self, out: &mut Output<Self>) -> Result<(), Self::Error>;
}
Hardware backends implement TryUpdate and TryFixedOutput directly.
Software implementations get them via the blanket impls at zero cost. The
existing Update and FixedOutput traits are unchanged; no existing code
breaks.
Prior art
rand_core 0.9 already solved this exact problem for the RNG case by adding
TryCryptoRng with a fallible try_fill_bytes method (see companion
Issue 10). We are asking for the same pattern to be applied to digest::Update
and digest::FixedOutput.
The embedded-hal crate uses type Error associated types on all peripheral
traits for the same reason: hardware peripherals can fail at any method call,
not just at construction time.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the digest::Update, digest::FixedOutput, digest::FixedOutputReset, universal_hash::UniversalHash, and cipher::StreamCipher definitions, then compare rand_core's TryCryptoRng pattern. Review the wolfcrypt implementation paths listed in digest.rs, hmac.rs, cmac.rs, and poly1305.rs to understand the failures being hidden. Done means the fallible API shape and compatibility strategy are agreed across the affected traits.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c, rust
- Domain
- api, cryptography
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100