Skip to content

Record which peer asked for a signature in the audit log - #949

Merged
kwsantiago merged 2 commits into
mainfrom
audit-requester-attribution
Aug 7, 2026
Merged

Record which peer asked for a signature in the audit log#949
kwsantiago merged 2 commits into
mainfrom
audit-requester-attribution

Conversation

@kwsantiago

@kwsantiago kwsantiago commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Summary

Entries recorded what this node signed but not who asked for it. For a single holder that is enough, since there is only one party who could have asked. It stops being enough the moment several principals share a signer, which is the shape an agent deployment takes: the first question about any entry is which agent produced it, and no field answered it.

Both entries written while handling an inbound request now carry the requester's share index: the policy refusal and the structured-payload mismatch. The second is the one most worth attributing, since it fires when a requester's body does not produce the digest it asked us to sign, which is an attempted relabel rather than a configuration saying no.

The field is covered by the entry HMAC, because a field the HMAC does not cover is one anyone with write access can change, and this is exactly the claim an audit log exists to make unforgeable. That is safe to add here only because nothing persists these entries: the log is an in-memory queue rebuilt on every start, read by one status endpoint. There are no stored entries whose verification could break. The vault's hash-chained audit log is a different type in a different crate and is untouched.

Adding a variable-width field at the end exposed an existing weakness in the preimage. The participant list is written without a length prefix; every field after it used to be fixed width, so the total length still determined where the list ended. A trailing optional removed that, and two different records began hashing identically: two participants plus an index and a one-byte operation are also the bytes of three participants plus a larger index and a different operation. The list is now length-prefixed.

Two claims from the first version of this change were wrong and are corrected rather than quietly dropped. The requester is not attribution for the whole round: the session id is derived from the message, threshold and sorted participants, none of which mention the requester, and participant selection does not depend on who asked, so two peers requesting the same digest derive the same session id. A duplicate request is also answered from the cached commitment before the requester is resolved, so a second peer can drive a round whose remaining entries would join back to the first. The field now documents that it attributes the entry it sits on and nothing more. And the index is only as trustworthy as peer admission, which is weaker than it sounds, because member transport keys are derived from the group public key; it identifies which member slot a request arrived under rather than proving who sat in it.

The value is now also emitted on the tracing line, alongside every sibling field.

Test plan

Five tests. Reattributing an entry breaks its HMAC. An unattributed entry does not verify as one attributed to share index zero. Two different records do not share a tag, using the exact values that collided. And both inbound-request call sites assert the recorded peer, which nothing did before: the existing refusal test exercised the attributed path with peer 2 and asserted nothing about it, so either call site could have passed nothing and the suite would have stayed green.

Falsified rather than assumed, three ways. Unbinding the requester from the HMAC fails the first two. Removing the length prefix reproduces the collision exactly. Passing nothing at either call site fails both wiring tests.

One earlier claim was withdrawn because falsification refuted it: the tag byte distinguishing present from absent is not what prevents a collision with share index zero. Removing it leaves those tests passing. It stays to keep the field self-delimiting, and the comment no longer claims more than that.

455 library tests pass, workspace builds, formatter and clippy clean. Follow-ups filed rather than fixed here: carrying the requester on the session so downstream entries are attributed, and the transport-key derivation that bounds what any of this can prove.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

The signing audit model now records optional requester share indexes. HMAC generation and verification authenticate requester presence and value. Signing paths attribute resolved requesters for refusals and commitments while leaving other operations unattributed.

Changes

Requester-aware signing audit logging

Layer / File(s) Summary
Audit entry and HMAC contract
keep-frost-net/src/audit.rs
SigningAuditEntry stores an optional requester. HMAC encoding distinguishes no requester from requester share index zero. SignRequestRefused is added to signing operations.
Audit logging API and integrity tests
keep-frost-net/src/audit.rs
log_signing_operation accepts and stores requester data. Tests update existing calls and verify requester tampering, optional-field distinction, and retention behavior.
Signing-path requester attribution
keep-frost-net/src/node/signing.rs
Refusal and commitment audit records pass resolved requester indexes when available. Other signing operations record no requester.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: wksantiago

Poem

A rabbit logs each signing call,
With requester marks for one and all.
HMAC guards each written trace,
Zero stays distinct from empty space.
Refused requests now leave a sign—
Audit carrots, crisp and fine.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding peer requester attribution to signing audit-log entries.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch audit-requester-attribution

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@keep-frost-net/src/node/signing.rs`:
- Line 530: Update the refusal handling around record_refusal to pass the
already resolved requester as Some(requester) instead of None, matching the
pre-sign refusal path and attributing the inbound refusal to the verified peer
identity.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fb5e0406-ead6-4000-bfa3-2c4db37b1af0

📥 Commits

Reviewing files that changed from the base of the PR and between aa89cfa and 615d91b.

📒 Files selected for processing (2)
  • keep-frost-net/src/audit.rs
  • keep-frost-net/src/node/signing.rs

Comment thread keep-frost-net/src/node/signing.rs Outdated
@kwsantiago
kwsantiago merged commit 8377b18 into main Aug 7, 2026
12 checks passed
@kwsantiago
kwsantiago deleted the audit-requester-attribution branch August 7, 2026 18:41
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.

1 participant