Skip to content

docs(tbtc/signer): Phase 7 interactive-session spec freeze#4049

Merged
mswilkison merged 3 commits into
extraction/frost-signer-mirror-2026-05-26from
docs/phase-7-interactive-spec-freeze-2026-06-12
Jun 12, 2026
Merged

docs(tbtc/signer): Phase 7 interactive-session spec freeze#4049
mswilkison merged 3 commits into
extraction/frost-signer-mirror-2026-05-26from
docs/phase-7-interactive-spec-freeze-2026-06-12

Conversation

@mswilkison

Copy link
Copy Markdown
Contributor

What

Phase 7.0: the spec-freeze candidate for the hardened interactive two-round FROST signing session — the production signing path — assembling the already-settled decisions (t-of-included-native per decisions 5/6, sidecar-shaped API per decision 2, OS-randomness-only production signing) into a precise contract, plus the deletion plan for the frozen transitional deterministic-nonce path.

The load-bearing design change: nonce custody moves inside the engine

Verified current state: the stateless primitives (frost_ops.rs) return serialized SigningNonces to the Go host and accept them back at sign_share — secret nonces cross the FFI twice, live in host memory between rounds, and single-use is enforced by caller discipline only. Calling sign_share twice with one nonce pair is the canonical FROST key-extraction failure and nothing prevents it today.

The session layer (spec §4): nonces are generated and held in session-scoped engine memory behind an opaque handle, consumed atomically (durable consumption marker before the share leaves the engine), zeroized on use, and never persisted and never exported. Restart loses in-flight nonces by construction (attempt fails safe); the cloned-state nonce-reuse class becomes structurally impossible; and after Phase 7 no secret signing material transits the FFI in either direction — which is also the audit story for that boundary.

Also specified

  • Session API (§5): InteractiveSessionOpen/Round1/Round2/Aggregate/Abort, idempotent-or-fail-closed, strict-mode attempt contexts only, consumed-registry semantics carried from the coarse path, transport-agnostic for the dlopen→sidecar swap.
  • t-of-included semantics (§6): engine-side subset verification at Round2 (own membership, subset-of-included, size t) so safety never depends on coordinator honesty; signing packages ride feat(frost/roast): protobuf signed-body envelopes for transition evidence #4040-style signed-body envelopes extending the feat(frost/roast): retain signed equivocation evidence at detection points #4044 equivocation-evidence retention; silent members cost zero attempts.
  • Deletion trigger made precise (§7): three conditions defining "interactive production path validated e2e" (Phase-5-equivalent suites incl. consumed-nonce-marker persist-fault cases; a real testnet t-of-included finalize through the full retry machinery; pinned cross-language vectors). The nonce.rs freeze marker now points at this section — the only code change in this PR, comment-only on the frozen file.
  • Reserved, not built (§8): bounded n−t+1 concurrency hooks (attempt-scoped keys/handles).
  • Phasing (§9): PR-sized 7.0–7.6 with repo-side mapping; [DRAFT - decision-gated] feat(tbtc/signer): add TEE hardening checker stack #4007 sidecar scoping folds into 7.0 as an addendum.
  • Open questions with proposed defaults (§10): package-distribution channel, round-1 transport shape, responsive-subset policy, markers-only durability — to be decided at freeze sign-off and recorded in the Decision Log.

Freeze process

Status is Proposed; it freezes on signer + keep-core owner sign-off per §11, with the §10 defaults ratified or overridden in the gates-doc Decision Log. The audit scope statement should reference this document and name the §5 API as in-scope (decision 1 interaction).

🤖 Generated with Claude Code

Specifies the hardened interactive two-round FROST signing session -
the production signing path - per the 2026-06-12 Decision Log:
t-of-included-native finalize (decision 5), no transitional retrofit
(decision 6), sidecar-shaped API contract (decision 2).

The load-bearing design change: secret nonce custody moves inside the
engine. Today's stateless primitives return serialized SigningNonces
to the Go host and accept them back at sign_share, so nonces cross
the FFI twice, live in host memory between rounds, and single-use is
caller discipline only. The session layer keeps nonces in
session-scoped engine memory behind an opaque handle, consumes
atomically (durable marker before share release), never persists
them - making restart/clone nonce reuse structurally impossible and
removing all secret signing material from the FFI boundary.

Also specified: the session API and registry semantics carried over
from the coarse path's hardening inventory; t-of-included subset
verification at Round2 (membership, subset-of-included, size t) so
safety never depends on coordinator honesty; signed-body package
envelopes extending the #4044 equivocation-evidence retention;
the precise deletion trigger for the frozen transitional
deterministic-nonce path (nonce.rs freeze marker now points here);
reserved room for bounded n-t+1 concurrency; PR-sized phasing
7.0-7.6; and four open questions with proposed defaults for the
freeze sign-off.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5f3c6a2e-efad-438f-8d4b-f3e6936d1527

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/phase-7-interactive-spec-freeze-2026-06-12

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.

mswilkison and others added 2 commits June 12, 2026 18:36
Four findings from the adversarial self-review pass:

Round2 gains check (f): the member's own commitment entry in the
signing package must be byte-identical to its Round1 output. Without
it a malicious coordinator substitutes an honest member's commitment,
the member's correctly-computed share fails verification at
aggregation, and the blame machinery manufactures re-checkable
evidence against an honest party - the same missing-field class as
the F1 nonce-binding finding. Section 6 now states that
share-verification blame is sound only because of (f), and that blame
re-checking must run against the retained package envelope, never a
reconstruction. Verification-before-consumption is now explicit: an
invalid package leaves the nonce handle live.

Live-state bounds added to section 5: open sessions and unconsumed
handles are secret-bearing engine memory and get the registry
discipline - hard cap, fail closed at capacity, TTL sweep that aborts
and zeroizes abandoned sessions.

The section 4 FFI claim is scoped to the signing path: dkg_part1/2
still hand secret round packages to the host (verified in
frost_ops.rs), DKG custody is a named follow-up, and the audit scope
must describe the DKG boundary as-is.

Phasing 7.5 notes the manifest's V1-migration verification is an
independent flip condition.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner sign-off 2026-06-12 with review converged (adversarial-pass
findings applied in 73dc594; Codex and Gemini clean). The four
forced questions are decided and recorded as Decision Log entry 8:
dedicated operator-key-signed topic for signing packages,
members-to-coordinator round-1 transport, strict first-t responsive
subset, markers-only durability. Spec status flips Proposed -> FROZEN;
implementation starts at Phase 7.1 against this contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mswilkison mswilkison merged commit 5d65712 into extraction/frost-signer-mirror-2026-05-26 Jun 12, 2026
@mswilkison mswilkison deleted the docs/phase-7-interactive-spec-freeze-2026-06-12 branch June 12, 2026 22:41
mswilkison added a commit that referenced this pull request Jun 13, 2026
)

## What

Phase 7.1 of the frozen spec
([#4049](#4049),
`docs/phase-7-interactive-session-spec-freeze.md` §§4–5): the engine
session layer for the interactive two-round signing path, with
**engine-held nonce custody** as the defining property.

## The custody contract (spec §4)

- Round-1 nonces: OS randomness, in-memory only, bound to `(session_id,
attempt_id)`, zeroized on consumption/abort/expiry/replacement,
**never** in any request, response, or persisted state.
- **Consumption-before-release**: the durable per-attempt marker
persists BEFORE the share leaves the engine. Persist failure → marker
rolled back, nonces stay live, no share escaped (pinned by a
persist-fault-injection test). Marker-without-share → attempt dead, fail
closed.
- Restart can never yield a second share under one nonce pair; the
cloned-state nonce-reuse class is structurally eliminated. Pinned by a
restart test: the marker survives reload and rejects the consumed
attempt at every entry point while a fresh attempt proceeds.

## The API (spec §5, strict-mode only)

| Call | Contract highlights |
|---|---|
| `InteractiveSessionOpen` | Key package once per session (validated
against member); idempotent by fingerprint; conflicting reopen fails
closed; **newer attempt implicitly aborts the prior live one**; consumed
attempts rejected |
| `InteractiveRound1` | Fresh nonces + commitments; idempotent until
consumed |
| `InteractiveRound2` | **All verification precedes consumption**:
message binding, subset ⊆ included set, exactly-`t` size, own
membership, and check (f) — own-commitment byte-identity, defeating
coordinator framing of honest members |
| `InteractiveSessionAbort` | Idempotent; destroys nonces without a
marker (never-consumed attempts may reopen with fresh nonces) |

Live-state bounds per the spec: fail-closed capacity cap
(`TBTC_SIGNER_MAX_LIVE_INTERACTIVE_SESSIONS`, default 64) + lazy TTL
sweep with abort semantics
(`TBTC_SIGNER_INTERACTIVE_SESSION_TTL_SECONDS`, default 3600); both
knobs ride the init-config surface. New structured error
`consumed_nonce_replay`. Telemetry: call/success counters ×4, latency
for the two cryptographic rounds.

The four `frost_tbtc_interactive_*` FFI exports ship additively per the
established pattern (Go adoption is Phase 7.3; nothing breaks until the
host calls them).

## Verification

- **10 engine tests**: e2e round trip with one member through the
session API and one through the stateless primitive, aggregating to a
**verified BIP-340 signature** (custody changed, cryptography didn't);
framing-attack rejection then honest-package acceptance
(verify-before-consume); package-shape rejections (outside-set, message
mismatch, oversized, self-missing); round-1 idempotency → consumed
replay; restart-marker durability; persist-fault rollback; open
lifecycle (idempotent/conflict/replacement); abort; TTL expiry; capacity
fail-closed.
- **1 FFI dispatch smoke test** across all four exports.
- Full suite **255 passed / 1 ignored** (baseline 244+1 plus the 11
new), `clippy -D warnings` clean on all targets incl. the bench shape,
**Phase 5 chaos suite green**, persisted-state schema additive (existing
state files load unchanged).

## Scope boundary

`InteractiveAggregate` + package-envelope evidence + cross-language
vectors are Phase 7.2 by the frozen phasing; Go orchestration is 7.3.
Out of scope per the freeze: DKG secret-package custody (named
follow-up).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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