Skip to content

Rust crate updates 2026-05-05#10402

Open
holtrop-wolfssl wants to merge 16 commits into
wolfSSL:masterfrom
holtrop-wolfssl:rust-crate-updates-2026-05-05
Open

Rust crate updates 2026-05-05#10402
holtrop-wolfssl wants to merge 16 commits into
wolfSSL:masterfrom
holtrop-wolfssl:rust-crate-updates-2026-05-05

Conversation

@holtrop-wolfssl
Copy link
Copy Markdown
Contributor

@holtrop-wolfssl holtrop-wolfssl commented May 5, 2026

Description

This PR includes several Fenrir fixes and some new functionality I found missing during my integration tests with boringtun.

See the commits for individual descriptions of each change.

Testing

Unit/CI tests.

Checklist

  • added tests
  • updated/added doxygen
  • updated appropriate READMEs
  • Updated manual and documentation

@holtrop-wolfssl holtrop-wolfssl self-assigned this May 5, 2026
Copilot AI review requested due to automatic review settings May 5, 2026 20:40
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the wolfssl-wolfcrypt Rust wrapper to (1) improve RNG lifetime safety across FFI consumers, (2) add RustCrypto trait integrations for BLAKE2 digest/MAC, and (3) extend AEAD support with AES-192 CCM/GCM wrappers, along with corresponding test updates.

Changes:

  • Refactors RNG to own a C-heap WC_RNG* and updates RNG-taking APIs to accept &RNG (plus new set_shared_rng(Arc<RNG>) for consumers that store an RNG pointer internally).
  • Adds RustCrypto digest::Digest wrappers (blake2_digest) and digest::Mac wrappers (blake2_mac) for BLAKE2b/BLAKE2s.
  • Adds AEAD wrappers and tests for AES-192-GCM and AES-192-CCM, and adds Clone support for HMAC MAC types.

Reviewed changes

Copilot reviewed 28 out of 28 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
wrapper/rust/wolfssl-wolfcrypt/tests/test_rsa.rs Updates tests to new RNG borrowing/sharing patterns (&RNG, set_shared_rng).
wrapper/rust/wolfssl-wolfcrypt/tests/test_random.rs Updates RNG tests to match RNG methods taking &self (no mut).
wrapper/rust/wolfssl-wolfcrypt/tests/test_hmac_mac.rs Adds a clone/forking test to validate cloned HMAC MAC state equivalence.
wrapper/rust/wolfssl-wolfcrypt/tests/test_ecc.rs Updates ECC tests for RNG ownership and shared RNG binding.
wrapper/rust/wolfssl-wolfcrypt/tests/test_curve25519.rs Updates Curve25519 tests for conditional RNG sharing when blinding is enabled.
wrapper/rust/wolfssl-wolfcrypt/tests/test_blake2_mac.rs Adds MAC trait tests for BLAKE2b/BLAKE2s keyed constructions.
wrapper/rust/wolfssl-wolfcrypt/tests/test_blake2_digest.rs Adds Digest trait tests for typed BLAKE2b/BLAKE2s hashers.
wrapper/rust/wolfssl-wolfcrypt/tests/test_aes.rs Adds AES-192-GCM/CCM AEAD roundtrip tests.
wrapper/rust/wolfssl-wolfcrypt/src/rsa.rs Refactors RSA RNG usage (&RNG params, owned/shared RNG binding stored to ensure lifetime).
wrapper/rust/wolfssl-wolfcrypt/src/rsa_pkcs1v15.rs Updates RSA PKCS#1v1.5 signing wrapper to new RNG pointer model.
wrapper/rust/wolfssl-wolfcrypt/src/random.rs Refactors RNG to own WC_RNG* allocated via wc_rng_new_ex, updates methods to take &self.
wrapper/rust/wolfssl-wolfcrypt/src/mlkem.rs Updates ML-KEM APIs to accept &RNG and pass WC_RNG* through FFI.
wrapper/rust/wolfssl-wolfcrypt/src/lms.rs Updates LMS keygen to accept &RNG.
wrapper/rust/wolfssl-wolfcrypt/src/lib.rs Adds alloc support and conditionally exports new BLAKE2 digest/MAC modules.
wrapper/rust/wolfssl-wolfcrypt/src/hmac.rs Implements deep Clone for HMAC via wc_HmacCopy.
wrapper/rust/wolfssl-wolfcrypt/src/hmac_mac.rs Derives Clone for HMAC MAC wrapper types.
wrapper/rust/wolfssl-wolfcrypt/src/ed448.rs Updates Ed448 key generation to accept &RNG.
wrapper/rust/wolfssl-wolfcrypt/src/ed25519.rs Updates Ed25519 key generation to accept &RNG.
wrapper/rust/wolfssl-wolfcrypt/src/ecdsa.rs Adapts ECDSA wrapper FFI calls to ECC key pointer storage changes.
wrapper/rust/wolfssl-wolfcrypt/src/ecc.rs Refactors ECC to store a C-heap ecc_key* and adds owned/shared RNG binding.
wrapper/rust/wolfssl-wolfcrypt/src/dilithium.rs Updates Dilithium APIs to accept &RNG.
wrapper/rust/wolfssl-wolfcrypt/src/dh.rs Updates DH APIs to accept &RNG.
wrapper/rust/wolfssl-wolfcrypt/src/curve25519.rs Updates Curve25519 APIs to accept &RNG, adds RNG ownership/sharing for blinding.
wrapper/rust/wolfssl-wolfcrypt/src/blake2_mac.rs Adds RustCrypto Mac trait wrappers for keyed BLAKE2b/BLAKE2s.
wrapper/rust/wolfssl-wolfcrypt/src/blake2_digest.rs Adds RustCrypto Digest trait wrappers for typed BLAKE2b/BLAKE2s hashers.
wrapper/rust/wolfssl-wolfcrypt/src/aes.rs Adds AES-192 CCM/GCM AEAD wrappers.
wrapper/rust/wolfssl-wolfcrypt/Makefile Enables the new alloc feature in the Makefile feature set.
wrapper/rust/wolfssl-wolfcrypt/Cargo.toml Replaces std feature with alloc and keeps feature list in sync with new APIs/modules.
Comments suppressed due to low confidence (1)

wrapper/rust/wolfssl-wolfcrypt/Cargo.toml:22

  • This change removes the previously exported std feature and adds a new alloc feature. Together with the public API signature changes in this PR (e.g., set_rng/generate now taking RNG or &RNG instead of &mut RNG), this is a semver-breaking change for a 1.x crate. Consider either (a) bumping the crate major version, or (b) keeping std as a backwards-compatible feature alias to alloc and providing compatibility shims where practical.
[features]
alloc = []
rand_core = ["dep:rand_core"]
aead = ["dep:aead"]
cipher = ["dep:cipher"]
mac = ["digest/mac"]
digest = ["dep:digest"]
signature = ["dep:signature"]
password-hash = ["dep:password-hash", "password-hash/phc"]
kem = ["dep:kem", "hybrid-array/extra-sizes"]

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread wrapper/rust/wolfssl-wolfcrypt/src/ecc.rs Outdated
Comment thread wrapper/rust/wolfssl-wolfcrypt/src/ecc.rs Outdated
Comment thread wrapper/rust/wolfssl-wolfcrypt/src/rsa.rs Outdated
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 6, 2026

MemBrowse Memory Report

gcc-arm-cortex-m4

  • FLASH: .rodata +8 B, .text +512 B (+0.3%, 198,017 B / 262,144 B, total: 76% used)

gcc-arm-cortex-m4-baremetal

  • FLASH: .rodata +8 B, .text +384 B (+0.6%, 64,891 B / 262,144 B, total: 25% used)

gcc-arm-cortex-m4-min-ecc

  • FLASH: .rodata +8 B, .text +256 B (+0.4%, 60,413 B / 262,144 B, total: 23% used)

gcc-arm-cortex-m4-tls12

  • FLASH: .rodata +8 B, .text +512 B (+0.4%, 121,437 B / 262,144 B, total: 46% used)

Comment thread wrapper/rust/wolfssl-wolfcrypt/src/rsa.rs
Comment thread wrapper/rust/wolfssl-wolfcrypt/tests/test_ecc.rs
Comment thread wrapper/rust/wolfssl-wolfcrypt/src/ecc.rs
Comment thread wrapper/rust/wolfssl-wolfcrypt/src/ecc.rs Outdated
Comment thread wrapper/rust/wolfssl-wolfcrypt/src/ecc.rs
@gasbytes gasbytes removed their assignment May 8, 2026
gasbytes
gasbytes previously approved these changes May 8, 2026
@holtrop-wolfssl
Copy link
Copy Markdown
Contributor Author

retest this please (build removed)

@holtrop-wolfssl
Copy link
Copy Markdown
Contributor Author

retest this please (Finished: FAILURE)

@holtrop-wolfssl holtrop-wolfssl removed their assignment May 8, 2026
@holtrop-wolfssl
Copy link
Copy Markdown
Contributor Author

retest this please (fips repo failures)

1 similar comment
@holtrop-wolfssl
Copy link
Copy Markdown
Contributor Author

retest this please (fips repo failures)

@holtrop-wolfssl
Copy link
Copy Markdown
Contributor Author

retest this please (Build 'wolfSSL/PRB-fips-repo-and-harness-test-v3-part2' failed with result: FAILURE)

Copy link
Copy Markdown
Contributor

@MarkAtwood MarkAtwood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the seven logical chunks (RNG memory-safety refactor, ECC heap allocation + double-init fix + empty-slice tests, AES-192 additions, BLAKE2 modules, HMAC Clone, doc/version changes) with FFI checks against the wolfCrypt C sources.

Headline: no P0 (UB / wrong-crypto) regressions introduced. The three architectural changes — RngHandle::Owned(RNG) | Shared(Rc<RNG>), C-heap allocation of ecc_key, manual wc_HmacCopy-based Clone — are sound and address real previous issues. Drop ordering, double-set_rng ordering, and MaybeUninit/assume_init flow all check out.

That said, there is one concrete correctness blocker, one doc-test breakage, and one version-bump item I'd want addressed before merge.


Blocker

ECC::import_unsigned has no length validation — empty slice triggers OOB read in C

wrapper/rust/wolfssl-wolfcrypt/src/ecc.rs:987-1004

pub fn import_unsigned(qx: &[u8], qy: &[u8], d: &[u8], curve_id: i32, ...) -> Result<Self, i32> {
    ...
    let rc = unsafe {
        sys::wc_ecc_import_unsigned(ecc.wc_ecc_key, qx.as_ptr(),
            qy.as_ptr(), d.as_ptr(), curve_id)
    };

The C path wc_ecc_import_unsigned → _ecc_import_raw_private (wolfcrypt/src/ecc.c:11898-11904) calls:

err = mp_read_unsigned_bin(key->pubkey.x, (const byte*)qx, (word32)key->dp->size);

It reads key->dp->size bytes (32 for P-256, 48 for P-384, 66 for P-521) regardless of the Rust slice length. The sp_read_unsigned_bin guard at wolfcrypt/src/sp_int.c:18177 only catches (in == NULL) && (inSz > 0). An empty Rust slice's as_ptr() returns a non-null dangling pointer, so the guard is bypassed and the C side reads invalid memory → UB at the FFI boundary.

This is pre-existing, but commit c6d14218d ("test more empty slices in test_ecc") explicitly added empty-slice rejection tests for import_raw and import_raw_ex — which are validated in Rust via the trailing-NUL + is_empty check at ecc.rs:866-867,923-924 — while conspicuously skipping import_unsigned. The natural reviewer expectation given the commit's title is that import_unsigned got the same treatment.

Same audit applies to priv_buf in import_private_key / import_private_key_ex (ecc.rs:746-766, 813-833).

Suggested fix: validate each slice length against wc_ecc_get_curve_size_from_id(curve_id) in the Rust wrapper and return BAD_FUNC_ARG on short input. Add a regression test mirroring the c6d14218d pattern.


Should fix in this PR

Five RSA doc tests miss feature = "alloc" in their cfg gates

wrapper/rust/wolfssl-wolfcrypt/src/rsa.rs

These doc-test blocks use std::rc::Rc::new(...) + rsa.set_shared_rng(...):

Lines Gate
30-57 (module-level example) #[cfg(random)]
871-901 (pss_sign) #[cfg(all(random, rsa_pss))]
934-963 (pss_check_padding) #[cfg(all(random, rsa_pss, rsa_const_api))]
1000-1029 (pss_verify) same
1071-1100 (pss_verify_check) same

But set_shared_rng is #[cfg(all(random, feature = "alloc"))]. cargo test --doc --features random (without --features alloc) will fail to compile these.

The correctly-gated pattern is visible at rsa.rs:148-175 and rsa.rs:1284-1310, both of which include feature = "alloc". Either add feature = "alloc" to the five gates above, or rewrite to use owned RNG via set_rng (the approach 4ace46f2e used for the set_rng doc test at :1197-1223).

Bump crate version and document the API breaks

wrapper/rust/wolfssl-wolfcrypt/Cargo.toml, README.md / PR description

Per the Cargo SemVer reference, both of these are MAJOR-bump changes:

  1. Public function signature change: set_rng(&mut self, &mut RNG)set_rng(&mut self, RNG) on ECC, RSA, Curve25519Key. Callers passing &mut rng get a hard compile error.
  2. Cargo feature rename: std = []alloc = []. Anyone with features = ["std"] gets a hard build failure. (Listed verbatim under MAJOR in the Cargo reference.)

Given the crate is narrow-audience right now, 1.3.0 with an explicit CHANGELOG / PR-description note is pragmatic and honest. Bumping to 2.0.0 would be the strict-SemVer answer. Silently going to 1.3.0 without flagging the breaks would be the worst option.

Suggested CHANGELOG bullets:

## 1.3.0 — 2026-05-NN

### Breaking changes
- `ECC::set_rng`, `RSA::set_rng`, `Curve25519Key::set_rng` now take owned
  `RNG` instead of `&mut RNG`. The previous borrow-based API was unsound:
  the C library cached the raw `*WC_RNG` past the borrow's lifetime. New
  `set_shared_rng(Rc<RNG>)` is also available behind `feature = "alloc"`.
- Cargo feature `std` has been renamed to `alloc`. Update any
  `features = ["std"]` in your Cargo.toml.
- `&mut RNG` parameters on `generate*` / `make_pub*` / `sign_*` are now `&RNG`.
  (Source-compatible via reborrow coercion.)

### Added
- `AesGcm` / `AesCcm` variants for 192-bit keys.
- `blake2_digest` module (typed BLAKE2b-{256,384,512} and BLAKE2s-{128,192,256}).
- `blake2_mac` module (typed BLAKE2b/2s MACs).
- `Clone` impl for HMAC types (deep-copy via `wc_HmacCopy`).
- `set_shared_rng(Rc<RNG>)` for sharing an RNG across multiple consumers.

### Fixed
- Avoid double `wc_ecc_init_ex` call (was leaking async device context under
  WOLFSSL_ASYNC_CRYPT && WC_ASYNC_ENABLE_ECC).
- ECC key is now C-heap-allocated to remain valid across Rust moves under
  non-default math configurations.

Also: minimum wolfSSL version is correctly documented as 5.9.0 in the README. wc_HmacCopy is the binding constraint (introduced in v5.9.0); wc_rng_new_ex is older (v5.7.0).


Advisory (track for follow-up, not blocking)

Inline vs. heap inconsistency between ECC and RSA/DH/Curve25519

Commit 113b6b7ec's rationale ("internal pointers breaking if Rust moves the ECC struct with some build configurations") implies certain wolfCrypt configs put self-pointers in C structs. ECC was migrated to a C-heap pointer; RSA.wc_rsakey, DH.wc_dhkey, and Curve25519Key.wc_key remain inline. In the default bindgen config these are move-safe, but if the same configs that affected ECC (non-SP integer math, WOLFSSL_ASYNC_CRYPT, FIPS, hardware-CB) also affect the other key types, they carry the same latent UB this PR just fixed for ECC. Worth a follow-up audit and/or a // Safety: comment in each consumer struct recording the move-safety assumption.


Nits (not blocking)

  • src/ecc.rs:405 — the comment "wc_ecc_key_new() always initializes the key with INVALID_DEVID" is wrong on CAAM builds (ecc.c:6174-6180 initializes with WOLFSSL_CAAM_DEVID). The subsequent (*key).devId = dev_id; under #[cfg(wolf_crypto_cb)] will then clobber the CAAM default if the caller passed dev_id: None.
  • src/random.rs:67-70 — the unsafe impl Send for RNG safety comment claims the pointer is non-null but it isn't asserted. A debug_assert!(!wc_rng.is_null()) after the success branch at :137 and :197 would document and enforce the invariant.
  • src/random.rs:60-62 — DRBG state is mutated through &self. Sound because the state lives behind an opaque *mut WC_RNG, but UnsafeCell<*mut WC_RNG> would make the interior-mutability intent explicit and defeat any future compiler improvement that might re-derive Sync or hoist reads.
  • src/ecc.rs:866-867, 923-924 — the null-terminator check accepts buffers with embedded NULs (only the last byte is checked). XSTRLEN / mp_read_radix on the C side stop at the first NUL, so embedded NULs cause silent truncation. Accepting &CStr would be cleaner.
  • src/rsa.rs:1208-1212 — the set_rng doc test (fixed in 4ace46f2e) does call set_rng, but the immediately-following public_encrypt uses a separate enc_rng argument, so the bound RNG isn't exercised here. Only the second set_rng at :1217 + private_decrypt at :1219 actually depends on the bound RNG (under WC_RSA_BLINDING). The first set_rng call is dead in this example.
  • src/ecdsa.rs:177-185 and many tests/test_*.rs sites — stale let mut rng = ... + &mut rng reborrows left from before the &RNG migration. Compiles via reborrow coercion, just noisy. Same cleanup as commit 1a441c910, just not applied everywhere.
  • tests/test_hmac_mac.rs:43-62 — the Clone test would be stronger with divergent post-clone updates (both branches feed suffix, so the test verifies "clone preserves state" + "clone doesn't poison original" but not "diverged updates produce different MACs"). The existing test catches the most likely shallow-copy bug, so this is a nice-to-have.

What the PR does right (worth calling out)

  • RNG ownership refactor: the old &'a mut WC_RNG borrow was unsound (C cached the raw pointer past the borrow lifetime). The new design heap-allocates WC_RNG via wc_rng_new_ex (pointer stable across moves), correctly picks Rc over Arc (WC_RNG is not internally synchronized), splits set_rng(RNG) from set_shared_rng(Rc<RNG>), and gets Drop ordering right — consumer free runs before RNG free, so wc_FreeRsaKey/wc_ecc_key_free can still safely read key->rng. Double-set_rng ordering is also correct (C pointer replaced before old RngHandle drops).
  • ECC heap allocation: switching to *mut sys::ecc_key via wc_ecc_key_new is the right fix for move-unsafety under configs where mp_int.dp points back into the same struct. Pin isn't needed because the Rust handle is a pointer to a never-moving C-heap object.
  • Double-init fix: real bug — wc_ecc_key_new already calls wc_ecc_init_ex internally; the explicit second call XMEMSETs the struct, blowing away heap context and leaking async device contexts in WOLFSSL_ASYNC_CRYPT && WC_ASYNC_ENABLE_ECC builds.
  • HMAC Clone: uses a manual impl delegating to wc_HmacCopy instead of bitwise #[derive(Clone)] — the latter would be unsound for hash configs with internal pointers. MaybeUninit::assume_init is correctly placed after the error check.

- store pointer to WC_RNG instead of full struct
- enforce RNG is not dropped before consumer structs

The C library stores a pointer via the set_rng() methods on a few
structs (e.g. RSA). This change holds a reference (or instance) of RNG
within the consumer structs to ensure it is kept alive if set_rng (or
now set_shared_rng) is used.
This fixes internal pointers breaking if Rust moves the ECC struct (with
some build configurations).
@holtrop-wolfssl
Copy link
Copy Markdown
Contributor Author

Reviewed the seven logical chunks (RNG memory-safety refactor, ECC heap allocation + double-init fix + empty-slice tests, AES-192 additions, BLAKE2 modules, HMAC Clone, doc/version changes) with FFI checks against the wolfCrypt C sources.

Headline: no P0 (UB / wrong-crypto) regressions introduced. The three architectural changes — RngHandle::Owned(RNG) | Shared(Rc<RNG>), C-heap allocation of ecc_key, manual wc_HmacCopy-based Clone — are sound and address real previous issues. Drop ordering, double-set_rng ordering, and MaybeUninit/assume_init flow all check out.

That said, there is one concrete correctness blocker, one doc-test breakage, and one version-bump item I'd want addressed before merge.

Blocker

ECC::import_unsigned has no length validation — empty slice triggers OOB read in C

wrapper/rust/wolfssl-wolfcrypt/src/ecc.rs:987-1004

pub fn import_unsigned(qx: &[u8], qy: &[u8], d: &[u8], curve_id: i32, ...) -> Result<Self, i32> {
    ...
    let rc = unsafe {
        sys::wc_ecc_import_unsigned(ecc.wc_ecc_key, qx.as_ptr(),
            qy.as_ptr(), d.as_ptr(), curve_id)
    };

The C path wc_ecc_import_unsigned → _ecc_import_raw_private (wolfcrypt/src/ecc.c:11898-11904) calls:

err = mp_read_unsigned_bin(key->pubkey.x, (const byte*)qx, (word32)key->dp->size);

It reads key->dp->size bytes (32 for P-256, 48 for P-384, 66 for P-521) regardless of the Rust slice length. The sp_read_unsigned_bin guard at wolfcrypt/src/sp_int.c:18177 only catches (in == NULL) && (inSz > 0). An empty Rust slice's as_ptr() returns a non-null dangling pointer, so the guard is bypassed and the C side reads invalid memory → UB at the FFI boundary.

This is pre-existing, but commit c6d14218d ("test more empty slices in test_ecc") explicitly added empty-slice rejection tests for import_raw and import_raw_ex — which are validated in Rust via the trailing-NUL + is_empty check at ecc.rs:866-867,923-924 — while conspicuously skipping import_unsigned. The natural reviewer expectation given the commit's title is that import_unsigned got the same treatment.

Same audit applies to priv_buf in import_private_key / import_private_key_ex (ecc.rs:746-766, 813-833).

Suggested fix: validate each slice length against wc_ecc_get_curve_size_from_id(curve_id) in the Rust wrapper and return BAD_FUNC_ARG on short input. Add a regression test mirroring the c6d14218d pattern.

Should fix in this PR

Five RSA doc tests miss feature = "alloc" in their cfg gates

wrapper/rust/wolfssl-wolfcrypt/src/rsa.rs

These doc-test blocks use std::rc::Rc::new(...) + rsa.set_shared_rng(...):

Lines Gate
30-57 (module-level example) #[cfg(random)]
871-901 (pss_sign) #[cfg(all(random, rsa_pss))]
934-963 (pss_check_padding) #[cfg(all(random, rsa_pss, rsa_const_api))]
1000-1029 (pss_verify) same
1071-1100 (pss_verify_check) same
But set_shared_rng is #[cfg(all(random, feature = "alloc"))]. cargo test --doc --features random (without --features alloc) will fail to compile these.

The correctly-gated pattern is visible at rsa.rs:148-175 and rsa.rs:1284-1310, both of which include feature = "alloc". Either add feature = "alloc" to the five gates above, or rewrite to use owned RNG via set_rng (the approach 4ace46f2e used for the set_rng doc test at :1197-1223).

Bump crate version and document the API breaks

wrapper/rust/wolfssl-wolfcrypt/Cargo.toml, README.md / PR description

Per the Cargo SemVer reference, both of these are MAJOR-bump changes:

  1. Public function signature change: set_rng(&mut self, &mut RNG)set_rng(&mut self, RNG) on ECC, RSA, Curve25519Key. Callers passing &mut rng get a hard compile error.
  2. Cargo feature rename: std = []alloc = []. Anyone with features = ["std"] gets a hard build failure. (Listed verbatim under MAJOR in the Cargo reference.)

Given the crate is narrow-audience right now, 1.3.0 with an explicit CHANGELOG / PR-description note is pragmatic and honest. Bumping to 2.0.0 would be the strict-SemVer answer. Silently going to 1.3.0 without flagging the breaks would be the worst option.

Suggested CHANGELOG bullets:

## 1.3.0 — 2026-05-NN

### Breaking changes
- `ECC::set_rng`, `RSA::set_rng`, `Curve25519Key::set_rng` now take owned
  `RNG` instead of `&mut RNG`. The previous borrow-based API was unsound:
  the C library cached the raw `*WC_RNG` past the borrow's lifetime. New
  `set_shared_rng(Rc<RNG>)` is also available behind `feature = "alloc"`.
- Cargo feature `std` has been renamed to `alloc`. Update any
  `features = ["std"]` in your Cargo.toml.
- `&mut RNG` parameters on `generate*` / `make_pub*` / `sign_*` are now `&RNG`.
  (Source-compatible via reborrow coercion.)

### Added
- `AesGcm` / `AesCcm` variants for 192-bit keys.
- `blake2_digest` module (typed BLAKE2b-{256,384,512} and BLAKE2s-{128,192,256}).
- `blake2_mac` module (typed BLAKE2b/2s MACs).
- `Clone` impl for HMAC types (deep-copy via `wc_HmacCopy`).
- `set_shared_rng(Rc<RNG>)` for sharing an RNG across multiple consumers.

### Fixed
- Avoid double `wc_ecc_init_ex` call (was leaking async device context under
  WOLFSSL_ASYNC_CRYPT && WC_ASYNC_ENABLE_ECC).
- ECC key is now C-heap-allocated to remain valid across Rust moves under
  non-default math configurations.

Also: minimum wolfSSL version is correctly documented as 5.9.0 in the README. wc_HmacCopy is the binding constraint (introduced in v5.9.0); wc_rng_new_ex is older (v5.7.0).

Advisory (track for follow-up, not blocking)

Inline vs. heap inconsistency between ECC and RSA/DH/Curve25519

Commit 113b6b7ec's rationale ("internal pointers breaking if Rust moves the ECC struct with some build configurations") implies certain wolfCrypt configs put self-pointers in C structs. ECC was migrated to a C-heap pointer; RSA.wc_rsakey, DH.wc_dhkey, and Curve25519Key.wc_key remain inline. In the default bindgen config these are move-safe, but if the same configs that affected ECC (non-SP integer math, WOLFSSL_ASYNC_CRYPT, FIPS, hardware-CB) also affect the other key types, they carry the same latent UB this PR just fixed for ECC. Worth a follow-up audit and/or a // Safety: comment in each consumer struct recording the move-safety assumption.

Nits (not blocking)

  • src/ecc.rs:405 — the comment "wc_ecc_key_new() always initializes the key with INVALID_DEVID" is wrong on CAAM builds (ecc.c:6174-6180 initializes with WOLFSSL_CAAM_DEVID). The subsequent (*key).devId = dev_id; under #[cfg(wolf_crypto_cb)] will then clobber the CAAM default if the caller passed dev_id: None.
  • src/random.rs:67-70 — the unsafe impl Send for RNG safety comment claims the pointer is non-null but it isn't asserted. A debug_assert!(!wc_rng.is_null()) after the success branch at :137 and :197 would document and enforce the invariant.
  • src/random.rs:60-62 — DRBG state is mutated through &self. Sound because the state lives behind an opaque *mut WC_RNG, but UnsafeCell<*mut WC_RNG> would make the interior-mutability intent explicit and defeat any future compiler improvement that might re-derive Sync or hoist reads.
  • src/ecc.rs:866-867, 923-924 — the null-terminator check accepts buffers with embedded NULs (only the last byte is checked). XSTRLEN / mp_read_radix on the C side stop at the first NUL, so embedded NULs cause silent truncation. Accepting &CStr would be cleaner.
  • src/rsa.rs:1208-1212 — the set_rng doc test (fixed in 4ace46f2e) does call set_rng, but the immediately-following public_encrypt uses a separate enc_rng argument, so the bound RNG isn't exercised here. Only the second set_rng at :1217 + private_decrypt at :1219 actually depends on the bound RNG (under WC_RSA_BLINDING). The first set_rng call is dead in this example.
  • src/ecdsa.rs:177-185 and many tests/test_*.rs sites — stale let mut rng = ... + &mut rng reborrows left from before the &RNG migration. Compiles via reborrow coercion, just noisy. Same cleanup as commit 1a441c910, just not applied everywhere.
  • tests/test_hmac_mac.rs:43-62 — the Clone test would be stronger with divergent post-clone updates (both branches feed suffix, so the test verifies "clone preserves state" + "clone doesn't poison original" but not "diverged updates produce different MACs"). The existing test catches the most likely shallow-copy bug, so this is a nice-to-have.

What the PR does right (worth calling out)

  • RNG ownership refactor: the old &'a mut WC_RNG borrow was unsound (C cached the raw pointer past the borrow lifetime). The new design heap-allocates WC_RNG via wc_rng_new_ex (pointer stable across moves), correctly picks Rc over Arc (WC_RNG is not internally synchronized), splits set_rng(RNG) from set_shared_rng(Rc<RNG>), and gets Drop ordering right — consumer free runs before RNG free, so wc_FreeRsaKey/wc_ecc_key_free can still safely read key->rng. Double-set_rng ordering is also correct (C pointer replaced before old RngHandle drops).
  • ECC heap allocation: switching to *mut sys::ecc_key via wc_ecc_key_new is the right fix for move-unsafety under configs where mp_int.dp points back into the same struct. Pin isn't needed because the Rust handle is a pointer to a never-moving C-heap object.
  • Double-init fix: real bug — wc_ecc_key_new already calls wc_ecc_init_ex internally; the explicit second call XMEMSETs the struct, blowing away heap context and leaking async device contexts in WOLFSSL_ASYNC_CRYPT && WC_ASYNC_ENABLE_ECC builds.
  • HMAC Clone: uses a manual impl delegating to wc_HmacCopy instead of bitwise #[derive(Clone)] — the latter would be unsound for hash configs with internal pointers. MaybeUninit::assume_init is correctly placed after the error check.

I added the slice length checks to ECC::import_unsigned. I added the gating on the alloc feature for the RSA doc tests that called set_shared_rng().

The version will be bumped later outside of this PR when preparing for the crate release.

@holtrop-wolfssl holtrop-wolfssl removed their assignment May 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants