Add additive *_with_rng entropy-injection variants to the Olm API - #379
Add additive *_with_rng entropy-injection variants to the Olm API#379shakhvit wants to merge 5 commits into
*_with_rng entropy-injection variants to the Olm API#379Conversation
Every Olm key-generation path currently draws randomness from the thread-local `rand::rng()`. This adds parallel `*_with_rng` variants that accept a caller-supplied `impl CryptoRng`, threading it down to the leaf `Curve25519SecretKey`/`Ed25519Keypair` constructors: * Account::new_with_rng * Account::generate_one_time_keys_with_rng * Account::generate_fallback_key_with_rng * Account::create_outbound_session_with_rng * Session::encrypt_with_rng (consumes rng only on a DH-ratchet advance) * Curve25519SecretKey::new_with_rng / Curve25519Keypair::new_with_rng * Ed25519Keypair::new_with_rng / Ed25519SecretKey::new_with_rng The change is purely additive: every existing method keeps its exact body and behaviour (still backed by `rand::rng()`), so there is zero behaviour change for current users. The new variants enable deterministic testing, reproducible builds, and custom/hardware entropy sources. Each new public method documents the footgun: a low-entropy, predictable, or reused generator produces predictable or repeated keys and breaks the security of the protocol, so callers must pass a cryptographically secure generator with fresh entropy per genuinely-new operation. Adds tests/with_rng.rs covering determinism, distinct-seed divergence, interop with the default OsRng-backed path, and the lazy DH-ratchet advance entropy seam.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #379 +/- ##
==========================================
+ Coverage 90.93% 91.14% +0.20%
==========================================
Files 34 34
Lines 4955 5148 +193
Branches 4955 5148 +193
==========================================
+ Hits 4506 4692 +186
+ Misses 286 285 -1
- Partials 163 171 +8 ☔ View full report in Codecov by Harness. |
Reformat the new `*_with_rng` doc comments to the repo's nightly rustfmt config (comment_width=80, wrap_comments) and collapse single-line signatures. Formatting only — no API or behaviour change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011eZSbrttghGooZCgz1QKLj
cargo-mutants (incremental, --in-diff) reported 2 surviving mutants in the new `*_with_rng` code that no test pinned down; add targeted tests so both are caught: - Ed25519SecretKey::new_with_rng -> Default::default() (src/types/ed25519.rs:253): account-level tests exercise Ed25519Keypair but never the bare secret key. Add ed25519_secret_key_with_rng_is_seed_driven: same seed => identical key AND distinct seeds => distinct keys — the divergence kills a constant/Default key. - key_id += 1 -> *= 1 in FallbackKeys::generate_fallback_key_with_rng (src/olm/account/fallback_keys.rs:99): no test observed the id counter advancing. Add fallback_key_with_rng_assigns_a_fresh_key_id_each_time: regenerating the fallback key must publish under a distinct KeyId, killing a non-incrementing counter. cargo-mutants --in-diff now reports 0 missed. fmt/clippy(all+no-default)/tests green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011eZSbrttghGooZCgz1QKLj
8918b42 to
cfb46a5
Compare
…ightly) The 'Run clippy' job failed with an r, not a lint: the clippy-driver (rustc 1.99.0-nightly da80ed070, 2026-07-14) panicked in resolver_for_lowering_raw during macro expansion (exit 101). Reproduced locally on nightly-2026-07-14 and confirmed CLEAN on nightly-2026-07-15 — a transient compiler bug fixed the next day. The job pins 'toolchain: nightly' (floating), so re-running picks up the fixed toolchain. No source change: fmt/clippy/tests/mutants all pass locally on a current nightly. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011eZSbrttghGooZCgz1QKLj
poljar
left a comment
There was a problem hiding this comment.
Is this something you are personally invested in? What's your use-case here?
This PR needs some more work:
- The
*_with_rng()functions should be called by the current methods with the RNG set toUnwrapErr(SysRng). - The PR should be split out into smaller commits, ideally one added method would be one commit.
- If this is done for the
olmsubmodule we should do this for the other submodules as well, could be a separate PR.
|
@poljar thanks for the response. Yes, I wanted to pass my own entropy into your primitives to have full control over the entropy in the system and drop the OS-random dependency. That's actually what let me compile Olm for a bare-metal target (no |
|
@shakhvit do you plan to get back at it soon ? We would need pretty quickly on our side, and I am happy to take over if you don't have the bandwidth for it. |
|
@MatMaul I had no time for this recently. Yes, please, you can take it over. Thanks :) |
Summary
Adds additive
*_with_rngvariants to every key-generation entry point in theolmmodule, letting callers supply their own
impl CryptoRnginstead of the thread-localrand::rng(). Fully backwards-compatible: no existing method changes behaviour.Motivation
vodozemac currently sources all key-generation entropy from
rand::rng()(thethread-local, OS-seeded generator), with no way to inject a specific RNG. Exposing an
RNG parameter is a standard, widely-useful capability that
x25519-dalekanded25519-dalekalready provide at the primitive level(
StaticSecret::random_from_rng,SigningKey::generate). Surfacing it at theAccount/Sessionlevel enables:fixed seed, so regressions in key-derivation plumbing are caught byte-for-byte.
vetted CSPRNG and wants a single entropy source.
no_std/embedded targets wheregetrandomhas no backend and entropy must beprovided by the caller.
What changed
New
*_with_rngmethods, each takingrng: &mut impl CryptoRng:Account::new_with_rngAccount::generate_one_time_keys_with_rngAccount::generate_fallback_key_with_rngAccount::create_outbound_session_with_rngSession::encrypt_with_rng— consumes RNG bytes only when the message triggers aDiffie-Hellman ratchet advance; a pure symmetric-chain message draws nothing.
Curve25519SecretKey::new_with_rng,Curve25519Keypair::new_with_rngEd25519Keypair::new_with_rng,Ed25519SecretKey::new_with_rngThe rng is threaded through internal helpers (
Ratchet,RemoteRootKey::advance,DoubleRatchet,OneTimeKeys,FallbackKeys) via parallel_with_rngmethods.decrypt/create_inbound_sessionare unchanged — they mint no keys.Non-breaking / additive
Every existing method keeps its exact body and behaviour, still backed by
rand::rng().The diff is purely additive (the only two removed lines augment two
usestatements). Nopublic signature changes; no existing test changed. The audited
OsRngcode paths arebyte-identical.
Security note (the footgun)
An injected RNG is a sharp tool: the security of every key rests entirely on the quality
of the supplied generator. A low-entropy, predictable, or reused RNG produces
predictable or repeated secret keys, and reusing an RNG state across two genuinely-new
Diffie-Hellman ratchet steps collapses the ephemeral keys and destroys forward secrecy /
post-compromise security. Each new public method documents this explicitly and directs
callers to pass a cryptographically secure generator seeded with fresh entropy per
genuinely-new operation. The default
OsRng-backed methods remain the right choice foralmost all users.
Tests
tests/with_rng.rscovers: determinism (identical RNG ⇒ byte-identical keys/session-id/ciphertext across all seams); distinct RNG ⇒ distinct keys; interop (a
_with_rng-builtsession round-trips with a default
OsRng-built peer); and the lazy DH-ratchet advanceseam (reproducible under the same RNG, fresh ephemeral under a different one). All
existing tests pass;
cargo clippy --all-targets --all-featuresis clean.