Skip to content

Conversation

@wksantiago
Copy link
Contributor

@wksantiago wksantiago commented Jan 15, 2026

Summary

  • Add entropy mixing from multiple RNG sources (OS, RDRAND, timing jitter, process context)
  • Mix all sources through BLAKE2b-512
  • Add health check for RNG validation
  • Update all security-critical randomness to use mixed entropy

Test plan

  • All 81 tests pass (74 unit + 7 integration)
  • Zero clippy warnings
  • Verified on x86_64 Linux

Summary by CodeRabbit

  • New Features

    • Introduced a multi-source entropy system that combines OS randomness, optional CPU hardware support, timing jitter, and process context to produce stronger random output.
    • Added entropy health checks to verify quality and uniqueness of generated randomness.
  • Refactor

    • Switched the app's randomness generation to use the new entropy system for cryptographic operations.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 15, 2026

Walkthrough

Adds a new public entropy module that mixes OS randomness, optional x86_64 RDRAND, timing jitter, and process context via Blake2b to produce variable-length random bytes. The crypto module now sources randomness from this entropy API instead of direct RNG usage.

Changes

Cohort / File(s) Summary
New Entropy Module
keep-core/src/entropy.rs
Adds multi-source entropy mixer: random_bytes_mixed() -> [u8;32], random_bytes<const N: usize>() -> [u8;N], check_entropy_health(), has_rdrand(), and EntropyHealthError. Implements OS RNG, optional RDRAND, timing jitter, process context, Blake2b hashing, and tests.
Module Exposure
keep-core/src/lib.rs
Exposes the new entropy module (pub mod entropy;).
Crypto Integration
keep-core/src/crypto.rs
Replaces direct rand::RngCore usage with calls to entropy::random_bytes() for SecretKey generation and encryption nonce generation.

Sequence Diagram

sequenceDiagram
    actor Caller
    participant Crypto as Crypto Module
    participant Entropy as Entropy Module
    participant Sources as Entropy Sources

    Caller->>Crypto: SecretKey::generate() / encrypt nonce request
    Crypto->>Entropy: random_bytes<32>() / random_bytes<N>()
    Entropy->>Sources: gather OS randomness
    Entropy->>Sources: probe RDRAND (if available)
    Entropy->>Sources: capture timing jitter
    Entropy->>Sources: collect process context
    Sources-->>Entropy: raw entropy pieces
    Entropy->>Entropy: Blake2b-512 each source, truncate/combine
    Entropy-->>Crypto: composed random bytes
    Crypto-->>Caller: generated SecretKey / nonce
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Suggested reviewers

  • kwsantiago

Poem

🐰 I sniff the bytes from OS and core,
I hop through clocks and RDRAND's door,
I hash and mix with Blake2b light,
Then gift you randomness for the night,
Happy keys and nonces—hop! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and accurately summarizes the main change: adding multi-source entropy mixing for improved randomness defense.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
keep-core/src/entropy.rs (1)

57-74: Timing jitter quality may be limited in virtualized environments.

The spin_loop() hint provides minimal CPU-level jitter, and on VMs or containers with stable scheduling, this source may contribute little entropy. However, since this is defense-in-depth and OS entropy is the primary source, this is acceptable.

Consider adding a brief doc comment noting that timing jitter quality varies by platform and is supplementary to OS entropy.


📜 Recent review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c517061 and ce5f514.

📒 Files selected for processing (1)
  • keep-core/src/entropy.rs
🔇 Additional comments (15)
keep-core/src/entropy.rs (15)

1-6: LGTM!

Module documentation and imports are appropriate. The 128-byte pool size provides sufficient OS entropy for mixing.


8-12: LGTM!

Truncating Blake2b-512 output to 32 bytes is cryptographically sound and standard practice.


14-16: LGTM!

Uses rand::rng() correctly for OS-seeded cryptographic randomness per rand 0.9.0 API.


18-25: LGTM!

Runtime CPUID check correctly detects RDRAND support (ECX bit 30) with proper caching via OnceLock. This addresses the previous concern about compile-time feature assumptions.


27-45: LGTM!

Retry logic with 10 attempts per chunk correctly handles transient RDRAND failures per Intel recommendations. This addresses the previous review concern.


47-55: LGTM!

Clean platform-specific separation with correct fallback for non-x86_64 architectures.


76-88: LGTM!

Collects process context including ASLR-randomized addresses as supplementary entropy. The heap allocation is properly scoped and dropped.


90-101: LGTM!

Proper domain separation with "keep-entropy-v1" prefix and structured encoding (index + length + data) prevents extension attacks and ensures distinct outputs for different input configurations.


103-127: LGTM!

Well-documented function that properly combines multiple entropy sources with graceful degradation when RDRAND is unavailable. The mixing order is consistent and deterministic.


129-158: LGTM!

The implementation correctly handles both small (≤32 bytes) and large requests. For large requests, the per-block counter augmentation provides additional domain separation even though each block already uses fresh entropy from random_bytes_mixed().


160-172: LGTM!

Clean error type with descriptive message. The unit struct design is appropriate since the health check has a single failure mode.


174-195: LGTM!

Health check validates basic RNG functionality with non-zero and uniqueness tests. This catches catastrophic failures without the overhead of statistical tests.


197-207: LGTM!

Clean public API exposing RDRAND capability with appropriate platform-specific behavior.


256-261: LGTM!

The test now correctly compares 32-byte slices to verify block independence. This addresses the previous review concern about the assertion.


209-255: LGTM!

Good test coverage for basic entropy functionality including non-zero output, uniqueness, health checks, and domain separation.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@keep-core/src/entropy.rs`:
- Around line 34-44: The fill_rdrand function uses a single call to the
_rdrand64_step intrinsic and returns false on the first transient failure;
change fill_rdrand to retry the _rdrand64_step call (e.g., up to 10 attempts)
for each 64-bit chunk before giving up so transient failures don't abort filling
the pool; keep the existing behavior of writing val.to_le_bytes() into each
chunk when successful and only return false if all retry attempts for a chunk
fail.
- Around line 247-252: The test test_random_bytes_large_buffer incorrectly
compares the first 32 bytes to a single byte (bytes[32]) instead of comparing
blocks for independence; update the assertion to compare slices of equal length
(e.g., compare &bytes[..32] with &bytes[32..64] or compare corresponding bytes
across blocks) so the test verifies that the first 32-byte block is not
identical to the next 32-byte block returned by random_bytes().
- Around line 18-32: The is_rdrand_available function should not unconditionally
return true when compiled with target_feature="rdrand"; always perform the
runtime CPUID check (using core::arch::x86_64::__cpuid) and cache the result
(e.g., with the existing static AVAILABLE OnceLock) so that fill_rdrand() can
safely rely on is_rdrand_available() at runtime even if the compile-time flag
and actual CPU capabilities differ; remove the #[cfg(target_feature = "rdrand")]
early-true branch and ensure the CPUID bit test ((result.ecx & (1 << 30)) != 0)
always runs.
🧹 Nitpick comments (4)
keep-core/src/entropy.rs (4)

56-73: Timing jitter source provides minimal entropy on virtualized or high-performance systems.

The spin_loop() hint is a no-op on many architectures and may produce near-zero or constant jitter values, especially in VMs, containers, or systems with consistent clock resolution. While this is mixed with other sources (defense-in-depth), the loop only runs 8 iterations with a trivial spin, which may not accumulate meaningful entropy variance.

Consider adding more iterations or actual work within the loop to increase timing variance. However, since this is one of multiple sources and OS entropy is the primary source, this is acceptable for defense-in-depth.


75-87: Process context entropy is largely static within a single process lifetime.

PID, thread ID, and memory addresses are deterministic values that remain constant or predictable within a process. An attacker with access to the process can trivially determine these values. While hashed together with other sources, this provides minimal additional entropy beyond the initial call.

This is acceptable for defense-in-depth since OS entropy is the primary source, but the comment should clarify these are anti-prediction measures rather than true entropy sources.


138-153: Unnecessary re-gathering of entropy for each 32-byte block when N > 32.

For requests larger than 32 bytes, random_bytes_mixed() is called for every 32-byte block, which re-gathers entropy from all sources (OS, RDRAND, timing, context) each iteration. This is computationally expensive and unnecessary—the standard approach is to gather entropy once and use counter-mode expansion via the hash.

♻️ Suggested improvement for large buffer generation
     } else {
+        let base_block = random_bytes_mixed();
         let mut offset = 0;
         let mut counter: u64 = 0;
         while offset < N {
-            let block = random_bytes_mixed();
             let mut hasher = Blake2b512::new();
-            hasher.update(block);
+            hasher.update(base_block);
             hasher.update(counter.to_le_bytes());
             let result = hasher.finalize();

             let remaining = N - offset;
             let copy_len = remaining.min(32);
             output[offset..offset + copy_len].copy_from_slice(&result[..copy_len]);
             offset += copy_len;
             counter += 1;
         }
     }

175-186: Health check is probabilistically weak with only 3 samples.

The uniqueness check samples[0] != samples[1] && samples[1] != samples[2] && samples[0] != samples[2] only verifies 3 samples are distinct. A broken RNG that cycles through a small set of values could pass this check. Consider increasing the sample count or adding additional statistical checks (e.g., hamming distance, bit distribution).

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e9b5abd and c517061.

📒 Files selected for processing (3)
  • keep-core/src/crypto.rs
  • keep-core/src/entropy.rs
  • keep-core/src/lib.rs
🧰 Additional context used
🧬 Code graph analysis (2)
keep-core/src/entropy.rs (1)
keep-core/src/crypto.rs (1)
  • random_bytes (363-365)
keep-core/src/crypto.rs (1)
keep-core/src/entropy.rs (1)
  • random_bytes (132-157)
🔇 Additional comments (7)
keep-core/src/entropy.rs (2)

89-100: Good domain separation in entropy mixing.

The mix_entropy function properly uses a domain separator ("keep-entropy-v1"), source index, and length prefixing before each source. This prevents length extension attacks and ensures different source combinations produce distinct outputs.


111-126: Solid multi-source entropy mixing implementation.

The random_bytes_mixed() function correctly gathers from multiple sources and conditionally includes RDRAND when available. The design ensures that even if one source is compromised, the output remains secure as long as at least one source provides good entropy.

keep-core/src/lib.rs (1)

10-10: Verify intentional public exposure of entropy module.

Making the entropy module pub exposes random_bytes_mixed(), random_bytes(), check_entropy_health(), and has_rdrand() as part of the crate's public API. If this is intended only for internal use within the crate, consider pub(crate) mod entropy; instead.

keep-core/src/crypto.rs (4)

12-12: Clean import of the new entropy module.

The import correctly references the crate-local entropy module.


227-230: Key generation correctly uses mixed entropy.

SecretKey::generate() now uses the multi-source entropy for cryptographic key generation, improving defense-in-depth for this security-critical operation.


331-332: Nonce generation correctly uses mixed entropy.

The 24-byte XChaCha20-Poly1305 nonce is now generated from mixed entropy sources, which is appropriate for this security-critical value.


363-365: Public random_bytes function is now a thin wrapper.

This maintains backward compatibility while delegating to the new entropy module. The wrapper in crypto.rs and the implementation in entropy.rs (shown in relevant snippets) are consistent.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@wksantiago wksantiago requested a review from kwsantiago January 15, 2026 15:29
@wksantiago wksantiago self-assigned this Jan 15, 2026
@wksantiago wksantiago linked an issue Jan 15, 2026 that may be closed by this pull request
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.

Add entropy mixing from multiple RNG sources

2 participants