Skip to content

Seal enclave FROST shares to their policy and hold FROST rounds to it - #1123

Merged
kwsantiago merged 1 commit into
mainfrom
enclave-sealed-frost
Oct 28, 2026
Merged

kwsantiago merged 1 commit into
mainfrom
enclave-sealed-frost

Conversation

@kwsantiago

@kwsantiago kwsantiago commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

FROST key packages reached the Nitro enclave in the clear through ImportFrostKey, and the FROST rounds only checked that some policy was loaded. A share therefore signed under whatever policy the host chose, with no rate or time rule applied.

  • ImportEncryptedFrostKey imports a key package sealed with its policy in the existing EncryptedWallet format (the host's EnvelopeEncryption::encrypt_for_enclave already seals arbitrary key bytes). Opening and policy adoption are shared with ImportEncryptedKey: the first sealed key imported, wallet or FROST, sets the policy, and a key sealed to any other policy is refused. A wallet blob never parses as a key package and a key package is never 32 bytes, so neither kind imports as the other.
  • Plaintext ImportFrostKey is refused unless the enclave is built with the test-only allow-plaintext-import feature, like ImportKey.
  • Round 1 judges the policy for the share's group key. Round 2 judges it again before making the share, so sessions opened together cannot all be signed past a rate limit, and closes a session the policy refuses. Each share counts once against the group key's rate limits.
  • A session remembers the group key it was opened with, and round 2 refuses if a different share has since been imported under the same name.
  • The share's bytes are locked before parsing, so bytes that fail to parse are wiped too.

Host and enclave wire format

The host's EnclaveRequest listed ImportFrostKey after GetPublicKey, while the enclave lists it after ImportEncryptedKey. postcard numbers enum variants by position, so every request from Sign on decoded as a different request in the enclave. The host now matches. Both crates encode one of every request, response and policy rule, with extreme integer values, and pin the same SHA-256 digest, so a variant, field or width changed on one side alone fails a test. The new variant is appended last on both sides, and the host client gains import_encrypted_frost_key.

Docs

docs/ENCLAVE.md describes sealed FROST import, how the rounds are held to the policy, and that a FROST message carries no amount, destination or event kind, so only the rate, AllowedHours and BlockWeekends rules can stop a FROST share.

Testing

  • cargo +1.89.0 test --manifest-path keep-enclave/enclave/Cargo.toml: 104 passed (103 with --features allow-plaintext-import).
  • cargo +1.89.0 test -p keep-enclave-host: passed, including the wire digest.
  • New tests cover plaintext refusal, sealed import adopting and locking the policy, a share sealed to another policy, wallet and FROST blobs not importing as each other, the time rules, a share counting against the rate limit, sessions opened together, and a share imported over an open session.
  • Mutation checks: the host enum in its old order, no round 1 policy, no round 2 policy, keeping a refused session, not recording the share, not adopting the sealed policy, skipping the sealed policy hash check and skipping the share-changed check each fail at least one test.

Summary by CodeRabbit

  • New Features
    • Added encrypted FROST key import, with imported keys sealed under the enclave’s policy.
    • FROST signing now enforces policy at both signing rounds, including rate limits. Plaintext FROST key imports are refused unless the test-only option is enabled.
  • Documentation
    • Clarified which policy rules apply to FROST signing and how signing requests and FROST shares count toward rate limits.
    • Documented that imported keys sealed under a different policy are refused.

FROST key packages reached the enclave in the clear through ImportFrostKey, and the FROST rounds checked only that some policy was loaded, so a share signed under whatever policy the host chose, with no rate or time rule applied.

ImportEncryptedFrostKey imports a key package sealed with its policy, sharing the opening and policy adoption of ImportEncryptedKey: the first sealed key imported sets the policy and a key sealed to any other is refused. Plaintext ImportFrostKey is refused unless the enclave is built with allow-plaintext-import, like ImportKey. Round 1 judges the policy for the share's group key, round 2 judges it again before making the share and closes a session it refuses, and each share counts against the group key's rate limits.

The host's EnclaveRequest listed ImportFrostKey after GetPublicKey while the enclave lists it after ImportEncryptedKey. postcard numbers variants by position, so every request from Sign on decoded as a different one in the enclave. The host now matches, and both crates pin the encoding of one of every request, response and policy rule to the same digest.
@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: ed4e01cf-394c-4d9a-8165-94ea8227f958
Commits

Reviewing files that changed from the base of the PR and between c9b979e and 26cb1b4.

Files selected for processing: 7
  • docs/ENCLAVE.md
  • keep-enclave/enclave/src/policy.rs
  • keep-enclave/enclave/src/signer.rs
  • keep-enclave/enclave/src/vsock_server.rs
  • keep-enclave/host/src/client.rs
  • keep-enclave/host/src/mock.rs
  • keep-enclave/host/src/protocol.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 enclave protocol now supports encrypted FROST key imports tied to sealed policy data. FROST signing checks policy at both rounds, records successful shares, and closes refused sessions. The signer retains group public keys across sessions and rejects a changed share.

Changes

Encrypted FROST Import and Policy Enforcement

Layer / File(s) Summary
FROST import protocol and host support
keep-enclave/host/src/protocol.rs, keep-enclave/enclave/src/vsock_server.rs, keep-enclave/host/src/client.rs, keep-enclave/host/src/mock.rs
The protocol adds ImportEncryptedFrostKey. The host client sends the encrypted key and public-key package. Wire-format tests cover representative requests, responses, policies, and networks.
Sealed key import and policy adoption
keep-enclave/enclave/src/signer.rs, keep-enclave/enclave/src/vsock_server.rs, docs/ENCLAVE.md
The enclave validates sealed data for wallet and FROST imports, adopts the policy after a successful import, and restricts plaintext FROST import to the test-only feature. The signer stores the imported group public key.
FROST signing policy and session checks
keep-enclave/enclave/src/signer.rs, keep-enclave/enclave/src/vsock_server.rs, keep-enclave/enclave/src/policy.rs, docs/ENCLAVE.md
The enclave checks policy at both signing rounds and records successful shares. The signer associates sessions with group public keys and rejects a changed share. Tests and documentation cover policy enforcement and rate limits.

Priority: Normal

Estimated code review effort: 4 (Complex) | ~45 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant EnclaveClient
  participant handle_request
  participant EnclaveSigner
  participant PolicyEngine
  EnclaveClient->>handle_request: Send round-one request
  handle_request->>EnclaveSigner: Get group public key
  handle_request->>PolicyEngine: Check FROST policy
  handle_request->>EnclaveSigner: Open signing session
  EnclaveClient->>handle_request: Send round-two request
  handle_request->>EnclaveSigner: Get session group public key
  handle_request->>PolicyEngine: Recheck FROST policy
  alt Policy allows share
    handle_request->>EnclaveSigner: Produce share
    handle_request->>PolicyEngine: Record successful share
    handle_request-->>EnclaveClient: Return share
  else Policy refuses share
    handle_request->>EnclaveSigner: Drop session
    handle_request-->>EnclaveClient: Return refusal
  end
Loading
Merge Risk: Minimal · up to 26cb1

This change adds encrypted FROST key import and applies the sealed policy to both signing rounds. No actionable merge-blocking risk was found in the supplied context.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 44.44% 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 summarizes the main change: FROST shares are sealed to their enclave policy, and FROST signing rounds enforce that policy.
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 44.44% 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 18 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.

❤️ Share

A rabbit checks the sealed key’s name,
Then guards each signing round the same.
The group key stays beside the share,
A changed one meets a guarded glare.
Success is counted; closed sessions rest,
And carrot crumbs mark policy’s test.

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

@kwsantiago
kwsantiago merged commit 09ba1cd into main Oct 28, 2026
13 checks passed
@kwsantiago
kwsantiago deleted the enclave-sealed-frost branch October 28, 2026 14:57
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