Skip to content

Judge enclave raw messages and FROST shares only on what the enclave can read - #1124

Merged
kwsantiago merged 1 commit into
mainfrom
enclave-unread-requests
Oct 28, 2026
Merged

kwsantiago merged 1 commit into
mainfrom
enclave-unread-requests

Conversation

@kwsantiago

@kwsantiago kwsantiago commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The enclave signs a raw Sign message as given, so it cannot read what the message spends or which event it is. Until now it judged the amount, address and event kind rules on fields the host sent alongside the request, and FROST shares were judged with none of them. This makes the policy engine judge only what the enclave itself read.

  • SigningRequest drops amount_sats, destination and event_kind on both the host and the enclave. The wire digest is updated on both sides.
  • SigningContext now holds Contents::Spend { amount_sats, fee_sats, destination } for an analyzed PSBT or Contents::Unread for a raw message or FROST share.
  • An unread request fails MaxAmountSats, AddressAllowlist and AddressBlocklist; the policy's action then decides, as for any failed rule.
  • A MaxFeeSats anywhere in the loaded policy, at any depth and under any action, refuses an unread request, since it may pay any fee.
  • AllowedEventKinds fails every request, a PSBT included, so an Or holding it cannot let a spend past the caps beside it. The fee bound analysis counts it as unable to pass.
  • PSBT outcomes are otherwise unchanged.
  • docs/ENCLAVE.md says which rules can judge which requests, and the keep enclave sign-psbt example, a command that does not exist, is removed.

An enclave whose policy has amount, fee or address rules now signs spends only through SignPsbt. Raw signing still works under rate, hour and weekend rules.

Testing

  • cargo +1.89.0 test --manifest-path keep-enclave/enclave/Cargo.toml (default and allow-plaintext-import) and cargo +1.89.0 test -p keep-enclave-host
  • New engine tests for each rule against an unread request under every action, a fee cap wherever it sits, the event kind rule failing a spend, and the fee bound beside it; new server tests for raw messages and both FROST rounds
  • Rate limit tests that relied on host-claimed amounts now sign PSBTs
  • Mutation checks on every new branch
  • Workspace clippy and fmt

Summary by CodeRabbit

  • Policy Updates
    • Amount, fee, and address rules now use data read from PSBTs. Raw signing and FROST requests cannot satisfy amount or address rules; results follow each rule’s action.
    • Any MaxFeeSats rule rejects raw and FROST requests, regardless of its action or position in the policy. AllowedEventKinds rules fail all requests.
    • Rate limits, allowed hours, and weekend restrictions continue to apply to raw and FROST requests.
  • Documentation
    • Clarified which policy rules apply to PSBTs and raw or FROST signing requests.

…can read

The enclave signs a raw Sign message as given, so it cannot tell what that message spends or which event it is. It judged the amount, address and event kind rules on fields the host sent with the request, and passed them when the host sent none; FROST shares carried none at all.

SigningRequest no longer carries amount, destination or event kind, on either side of the wire. The policy engine now sees what the enclave itself read: an analyzed PSBT, or an unread request. An unread request fails MaxAmountSats and the address rules, with the policy's action deciding the outcome as for any failed rule. A MaxFeeSats anywhere in the loaded policy refuses it outright, since it may pay any fee. AllowedEventKinds fails every request, a PSBT included, so an Or holding it can no longer let a spend past the caps beside it. The rate, hour and weekend rules apply as before.

The wire digest is updated on both sides, docs/ENCLAVE.md says which rules can judge which requests, and the stale enclave sign-psbt CLI example is removed.
@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 4fc0ca7e-6181-4a53-8e3b-d5bfe1c96274
Commits

Reviewing files that changed from the base of the PR and between 09ba1cd and d50d95a.

Files selected for processing: 8
  • docs/ENCLAVE.md
  • keep-cli/src/commands/enclave.rs
  • keep-enclave/enclave/src/policy.rs
  • keep-enclave/enclave/src/vsock_server.rs
  • keep-enclave/host/src/backend.rs
  • keep-enclave/host/src/mock.rs
  • keep-enclave/host/src/protocol.rs
  • keep-enclave/host/tests/integration_tests.rs
Files with no reviewable changes: 4
  • keep-enclave/host/src/mock.rs
  • keep-enclave/host/src/backend.rs
  • keep-cli/src/commands/enclave.rs
  • keep-enclave/host/tests/integration_tests.rs

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

The signing request no longer carries event kind, amount, or destination fields. The enclave evaluates raw and FROST requests as unread content and evaluates PSBT requests with enclave-derived spend data. Policy rules and tests now reflect these input semantics.

Changes

Enclave policy evaluation

Layer / File(s) Summary
Policy inputs and request contract
keep-enclave/enclave/src/policy.rs, keep-enclave/enclave/src/vsock_server.rs, keep-enclave/host/src/protocol.rs, keep-cli/src/commands/enclave.rs, keep-enclave/host/src/backend.rs, keep-enclave/host/src/mock.rs, keep-enclave/host/tests/integration_tests.rs
SigningContext distinguishes unread requests from analyzed spends. SigningRequest removes the event-kind, amount, and destination fields. Request examples, wire-format digests, and initializers are updated.
Policy evaluation and unit tests
keep-enclave/enclave/src/policy.rs
Amount, fee, address, and event-kind rules use the new context semantics. Fee-cap detection checks nested rules. Tests cover unread requests, spend data, and fee-bound analysis.
Signing paths and policy validation
keep-enclave/enclave/src/vsock_server.rs, docs/ENCLAVE.md
Raw and FROST policy checks use unread content. PSBT checks use spend amount, fee, and destination. Signing tests and documentation describe the updated policy outcomes.

Priority: Normal

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

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant Host
  participant VsockServer
  participant PolicyEngine
  Host->>VsockServer: Send raw, FROST, or PSBT signing request
  alt Raw or FROST request
    VsockServer->>PolicyEngine: Evaluate Contents::Unread
  else PSBT request
    VsockServer->>PolicyEngine: Evaluate Contents::Spend with amount, fee, and destination
  end
  PolicyEngine-->>VsockServer: Return policy decision
  VsockServer-->>Host: Return signing response
Loading

Possibly related PRs

  • privkeyio/keep#1122: Changes SigningContext and rate-rule evaluation, which share the policy evaluation flow updated here.
  • privkeyio/keep#1123: Adds FROST policy checks that this change updates to use unread content.
  • privkeyio/keep#1109: Adds enclave PSBT analysis whose spend data this change uses in policy evaluation.

Suggested reviewers: wksantiago

Merge Risk: Minimal · up to d50d9

Raw and FROST signing follow the documented policy restrictions. No material merge-blocking risk was identified.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 29.41% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main policy change for raw messages and FROST shares.
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.
Full details: Docstring Coverage

Explanation

Docstring coverage is 29.41% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 17 functions across 3 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.

❤️ Share

A rabbit checks the signing flow,
Unread requests have rules to know.
PSBT spends bring amounts clear,
Fee and address checks appear.
The bunny hops; the tests all glow.

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

@kwsantiago
kwsantiago merged commit b173156 into main Oct 28, 2026
13 checks passed
@kwsantiago
kwsantiago deleted the enclave-unread-requests branch October 28, 2026 18:25
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