wolfcrypt 0.1.0

RustCrypto trait implementations backed by wolfCrypt
docs.rs failed to build wolfcrypt-0.1.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

RustCrypto trait implementations backed by wolfCrypt.

This crate is #![no_std] (with alloc). It wraps wolfCrypt's C implementations behind standard RustCrypto trait interfaces so they can be used as drop-in backends in generic Rust crypto code.

What implements RustCrypto traits

Module Trait(s) implemented Types
[digest] Digest, Update, FixedOutput, Reset Sha256, Sha384, Sha512, …
[hmac] Mac (via KeyInit + Update + FixedOutput + MacMarker) WolfHmacSha256, …
[cmac] Mac WolfCmacAes128, WolfCmacAes256
[aead] AeadInPlace, KeyInit Aes128Gcm, Aes256Gcm, ChaCha20Poly1305
[cipher] BlockCipher, BlockEncrypt/Decrypt, StreamCipher, KeyInit/KeyIvInit AES-ECB/CTR/CBC/CFB, WolfChaCha20
[des3] BlockEncryptMut, BlockDecryptMut, KeyIvInit DesEde3CbcEnc, DesEde3CbcDec
[poly1305] Mac WolfPoly1305
[ed25519] Signer, Verifier (using ed25519::Signature) Ed25519SigningKey, Ed25519VerifyingKey
[ed448] Signer, Verifier Ed448SigningKey, Ed448VerifyingKey
[ecdsa] Signer, Verifier EcdsaSigningKey<P256>, EcdsaVerifyingKey<P256>, …
[rsa] Signer, Verifier RsaPrivateKey, RsaPublicKey
[mldsa] Signer, Verifier MlDsa44SigningKey, MlDsa65SigningKey, …; MlDsaSignature<L>
[rand] TryRng, TryCryptoRng (Rng + CryptoRng via blanket impl) WolfRng

What uses a bespoke (non-trait) API

Module Why no trait API shape
[hkdf] The hkdf crate provides a concrete struct, not a trait new() / extract() / expand() — same method names as hkdf::Hkdf
[pbkdf2] pbkdf2::pbkdf2_hmac requires CoreProxy; our EVP digests can't satisfy Sync pbkdf2_hmac_sha256(password, salt, rounds, &mut out)
[keywrap] No RustCrypto trait exists for AES Key Wrap aes_wrap_key() / aes_unwrap_key()
[dh] No RustCrypto trait for classic DH DhSecret::generate() / compute_shared_secret()
[ecdh] No RustCrypto trait for raw ECDH X25519StaticSecret, NistEcdhSecret<C>, etc.
[mlkem] No RustCrypto trait for ML-KEM MlKemDecapsulationKey / MlKemEncapsulationKey

For HKDF and PBKDF2, our [digest] types are compatible with the standard crates via hkdf::SimpleHkdf<Sha256> and hmac::SimpleHmac<Sha256> respectively — see each module's docs for examples.

Panics vs Result in constructors

Several RustCrypto traits define infallible constructors that return Self rather than Result<Self, _>:

Because these signatures leave no room for a Result, our implementations assert! on the wolfCrypt return code and panic on failure. In practice this means a panic only on OOM or a fundamental library-init failure — never on user-supplied input of the correct length. Fallible alternatives (new_from_slice, generate, from_seed, etc.) return Result wherever the trait or our own API allows it.

Buffer size limit

wolfCrypt's C API uses u32 (or c_int) for buffer lengths. This crate casts Rust usize lengths to u32 at each FFI boundary. On 64-bit targets, buffers larger than 4 GB (u32::MAX) will panic rather than silently truncate.

In practice, single-call buffers of 4 GB+ are extremely rare in cryptographic operations. If you need to process data of that size, feed it incrementally through the streaming / update APIs.

RustCrypto trait ecosystem gaps

Implementing the RustCrypto traits on top of a C FFI backend exposed several design limitations that pure-Rust implementations never hit. This section documents each gap and the workaround used in this crate, both as guidance for contributors and as a reference for anyone proposing upstream improvements.

1. Infallible constructors for fallible operations

KeyInit::new, Digest::new (via Default) return Self, not Result. Any FFI backend can fail for reasons beyond the caller's control — OOM in the C allocator, hardware device unavailable, library not initialized. The trait gives no way to report this.

Workaround: We assert! on the wolfCrypt return code. In practice this panics only on OOM or device-init failure, never on valid input. Fallible alternatives (new_from_slice, generate, from_seed) return Result wherever the trait allows.

2. No ZeroizeOnDrop bound on key types

KeyInit creates types holding secret key material, but the trait doesn't require ZeroizeOnDrop or any cleanup guarantee. Each implementor must remember to add it manually — an easy thing to miss.

Workaround: We manually implement Drop with zeroize calls on every type that holds key material (AES key schedules, ChaCha20Poly1305 keys, HKDF PRKs, keywrap temporaries). The zeroize_aes_key helper centralizes the pattern for opaque C structs.

3. AeadInPlace is the only AEAD trait

There is no Aead variant with separate input and output buffers. wolfCrypt's one-shot ChaCha20-Poly1305 API (wc_ChaCha20Poly1305_Encrypt) required separate buffers, which would have forced a heap allocation per encrypt/decrypt call.

Workaround: We switched to wolfCrypt's streaming ChaCha20-Poly1305 API (wc_ChaCha20Poly1305_Init / UpdateData / Final), which supports input == output pointers. This eliminated the allocation entirely. A backend with no in-place path (e.g., a DMA-based hardware accelerator) would have no such escape hatch.

4. No HKDF or PBKDF2 traits

The hkdf and pbkdf2 crates provide concrete implementations, not traits. There is no way to supply an alternative backend through trait implementation.

Workaround: Our HKDF module exposes a bespoke API with method names matching hkdf::Hkdf (new, extract, expand). Our PBKDF2 module exposes standalone functions matching pbkdf2::pbkdf2_hmac's signature. For callers that need the actual hkdf or pbkdf2 crate types, our digest types compose with hkdf::SimpleHkdf<Sha256> and hmac::SimpleHmac<Sha256> — see each module's docs for examples.

5. The CoreProxy cliff

The digest crate has two tiers: high-level (Digest, Update, FixedOutput) and low-level (CoreProxy, UpdateCore, FixedOutputCore). Our EVP-based digests implement the high-level tier, but pbkdf2::pbkdf2_hmac requires CoreProxy (low-level), which opaque FFI wrappers cannot satisfy.

Workaround: Callers needing pbkdf2_hmac should use hmac::SimpleHmac<Sha256> (which bridges from high-level digest to low-level HMAC) or use our native pbkdf2_hmac_sha256 function, which calls wolfCrypt's wc_PBKDF2 in a single FFI call.

6. pbkdf2::pbkdf2 requires Sync unconditionally

The function signature has PRF: Sync even when the parallel feature is disabled. Our EVP-based digest types are !Sync (correctly — EVP_MD_CTX has interior mutable state), so pbkdf2::<SimpleHmac<Sha256>>(...) does not compile.

Workaround: Use our native pbkdf2_hmac_sha256 function instead. This is a candidate for an upstream fix (the Sync bound should be conditional on the parallel feature).

7. SignatureEncoding::Repr assumes fixed-size signatures

The trait works naturally for Ed25519 (Repr = [u8; 64]) but post-quantum signatures are variable-length — ML-DSA-87 is 4627 bytes. We use Repr = Box<[u8]>, which works but means the signature cannot be stack-allocated and loses compile-time size guarantees.

Workaround: Accepted as-is. As PQC becomes standard across the RustCrypto ecosystem, this will likely be revisited upstream.

8. Interior mutability for FFI verification calls

wolfCrypt's C API requires *mut pointers even for logically read-only operations like signature verification and public-key export. The RustCrypto Verifier::verify trait takes &self, so we cannot pass &mut self to the FFI.

Workaround: Signing and verifying key types wrap their C key handle in UnsafeCell (see ed25519.rs, ed448.rs, ecdsa.rs, rsa.rs, mldsa.rs, lms.rs, aead.rs, cipher/ccm.rs). This lets us obtain *mut from &self for FFI calls. UnsafeCell also makes the type !Sync, which is correct — wolfCrypt key handles are not thread-safe. Each UnsafeCell usage has a SAFETY comment at the call site explaining why single-threaded access is guaranteed.

Testing

This crate's in-tree tests (tests/) cover only functionality that has no pure-Rust counterpart for cross-validation: classic DH, NIST-curve ECDH, and RSA encryption.

The bulk of correctness testing lives in the wolfcrypt-conformance crate (a sibling workspace member). That suite cross-validates every algorithm against pure-Rust RustCrypto implementations, NIST CAVP/SHAVS vectors, Wycheproof edge-case vectors, and RFC known-answer tests. Always run the conformance suite after modifying this crate:

cargo test -p wolfcrypt-conformance