Repository navigation
Keep a replayed RNG sequence from repeating a FROST nonce - #1117
Conversation
Every fill a checked draw hands out is now hashed, after the health criteria have judged the raw bytes, with a process-wide fill counter, the process id and the clock. A source that replays the same sequence from one call to the next passes every comparison inside each call, and before this it committed to the same nonce pair in every session. Nothing drawn is kept between calls. The guarantee holds within one process; across process starts only the pid and the clock separate fills, which is best effort. An RNG health failure is now its own KeepError variant instead of a stringly-typed encryption error, so callers can tell a degraded source from a bad ciphertext.
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Separate checked fills keep-core/Cargo.toml, keep-core/src/frost/checked_rng.rs |
CheckedRng judges source bytes before separating fills with per-fill process data and hashing. Tests cover replayed sources, several fill widths, and multiple fill methods. The sha2 dependency enables zeroize. |
Entropy error preservation
| Layer / File(s) | Summary |
|---|---|
Preserve entropy health errors keep-core/src/error.rs, keep-core/src/crypto.rs |
KeepError adds a transparent Entropy variant and removes the conversion to Encryption. Tests check that the error kind and display text are preserved. |
Map entropy errors in consumers keep-mobile/src/lib.rs, keep-nip46/src/error.rs, keep-frost-net/src/node/signing.rs |
The mobile adapter maps entropy errors to EncryptionError with the error text. NIP-46 maps them to "Cryptographic operation failed". The signing comment identifies the entropy refusal path. |
Priority:
Estimated code review effort: 3 (Moderate) | ~20 minutes
Change: Bug fix
Sequence Diagram(s)
sequenceDiagram
participant EntropySource
participant CheckedRng
participant RngConsumer
RngConsumer->>CheckedRng: request fill
CheckedRng->>EntropySource: draw bytes
CheckedRng->>CheckedRng: judge original bytes
CheckedRng->>CheckedRng: separate destination bytes
CheckedRng-->>RngConsumer: return separated bytes
Suggested reviewers: wksantiago
Merge Risk: 
Minimal · up to 81e2d
-
The change keeps a replayed RNG sequence from producing repeated nonces within a process and keeps RNG health failures distinguishable in keep-core. No actionable merge-blocking issue remains.
Pre-merge checks: 
4 · 
1
-
Failed checks: 1 warning Passed checks: 4 passedFull details: Docstring Coverage
-
Explanation
Docstring coverage is 56.25% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 16 functions across 6 files. (1 skipped: 1 unsupported.)
Resolution
Write docstrings for the functions missing them to satisfy the coverage threshold.
-
Finishing Touches: 💡 1
-
📝 Generate docstrings: 💡
-
- Commit to this branch
- Create a new PR
🧪 Generate unit tests (beta)
-
- Commit to this branch
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
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.
A rabbit checks each byte with care,
Then hashes fills that travel there.
Old streams return, but outputs part,
While errors keep their proper heart.
I nibble greens and hop away.
Comment @coderabbitai help to get the list of available commands.
What
A checked draw now hashes every fill it hands out, after the health criteria have judged the raw bytes, together with a process-wide fill counter, the process id and the clock. An RNG health failure also becomes its own
KeepError::Entropyvariant instead of being flattened intoKeepError::Encryption(String).Why
CheckedRngbuilds its judge per call, so a source that replays the same sequence from one call to the next (A, B in one commitment, A, B again in the next) passes every comparison inside each call. frost-core derives a nonce asH3(random_bytes || signing_share), so that source committed to the same nonce pair in every session, and two signatures over different messages give up the share. Comparing across calls would mean keeping drawn bytes for the life of the process, which was ruled out earlier. Hashing in a counter that never repeats within a process closes the case without keeping anything drawn.The judge still sees the source's own bytes. Hashing first would hide a stuck source, since its hash looks healthy to every criterion. A mutation check confirms the existing stuck, cycling and partial-fill tests fail if the order is reversed.
Scope and limits
Degradedwithout matching a message.Tests
next_u32,next_u64,try_fill_bytes); identical fills are separated at several widths.cargo test -p keep-core --features ed25519, the enclave suite, keep-nip46 and keep-frost-net, and workspace clippy with the CI features all pass.Summary by CodeRabbit