[DRAFT - decision-gated] feat(tbtc/signer): add TEE hardening checker stack#4007
Draft
mswilkison wants to merge 5 commits into
Draft
Conversation
…k from tBTC monorepo⚠️ DRAFT — DECISION-GATED. This PR mirrors the source PR tlabs-xyz/tbtc#88 which is itself a decision-gated draft. It must NOT merge until the Threshold policy decision to require TEE operation for signers is explicit. Stacked on top of [PR #4005](https://github.com/threshold-network/ keep-core/pull/4005) which lands the base FROST/ROAST Rust signer at pkg/tbtc/signer/. This PR adds the optional TEE signer-hardening checker stack on top. Phases A-D (per source PR #88) - Phase A: governance registry + audit-event validation (tee_registry_checker) - Phase B: verifier/keyset/token/revocation validation (tee_token_checker) - Phase C: runtime token + denylist + vendor-diversity enforcement (tee_runtime_checker) - Phase D: enforcement modes + break-glass controls (tee_enforcement_checker) Files (19 total — all mirror status) - 4 Rust binaries at pkg/tbtc/signer/src/bin/ - 12 sample config JSON files at pkg/tbtc/signer/scripts/ - 1 README.md update at pkg/tbtc/signer/ - 2 docs at pkg/tbtc/signer/docs/ (tee-whitelisted-signer-activation- gate-record.md + tee-whitelisted-signer-enforcement-plan.md) Provenance - Source repository: tlabs-xyz/tbtc - Source PR: #88 (decision-gated, draft) - Source commit (PR #88 HEAD): 31f0aa2b8efbf8ac42909f8e8d4ce67248cb478e - Source branch: feat/tee-phase-a-governance-registry (stacked on feat/frost-schnorr-migration) - Base for THIS PR: extraction/frost-signer-mirror-2026-05-26 (PR #4005) - Source PR includes stacked phases B, C, D from PRs #89, #90, #91 merged into PR #88's branch Decision gate The source PR #88 description is explicit: this stays in draft until the runtime TEE policy decision is made. The same constraint applies to this canonical mirror — DO NOT MERGE before: 1. PR #4005 (base FROST signer) lands 2. Threshold policy decision on TEE enforcement for signers is explicit and documented (per the source PR's "Decision-Gated" framing) Verification (per plan v38 §7.2 mirror status) For each file: - sha256(git show 31f0aa2b8e:<sourcePath>) == sha256(file at this PR head at <targetPath>) The 19 files port as byte-for-byte mirrors with path normalization: - tools/tbtc-signer/* → pkg/tbtc/signer/* - docs/frost-migration/tee-* → pkg/tbtc/signer/docs/tee-* Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
mswilkison
added a commit
that referenced
this pull request
May 26, 2026
Resolves CI failures on PR #4005 (signer mirror): 1. TLA model checks: run_tla_models.sh lacked executable bit at canonical HEAD. CI ran the script directly (no `bash` prefix), which fails with `Permission denied`. Fixed via `git update-index --chmod=+x`. 2. Signer formal invariants: engine.rs's formal_verification_roast_attempt_context_shared_vectors_match_ expected_values test referenced vectors at a path stale from the umbrella's docs/frost-migration/test-vectors/ layout. The manifest places the vector at the canonical-signer test/vectors/ subdir (pkg/tbtc/signer/test/vectors/roast-attempt-context-v1.json per the source-to-target map). Updated the `PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(...)` argument from `../../docs/frost-migration/test-vectors/roast-attempt-context-v1.json` (umbrella-relative) to `test/vectors/roast-attempt-context-v1.json` (signer-CARGO_MANIFEST_DIR-relative, where the vector actually lives at canonical HEAD). Verified locally: - ls -l shows executable bit set on run_tla_models.sh - engine.rs path now resolves to the correct mirror location - Vector exists at pkg/tbtc/signer/test/vectors/roast-attempt-context-v1.json Same fix needs to be applied to PR #4007 (stacked on #4005) in a follow-up commit on its branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mswilkison
added a commit
that referenced
this pull request
Jun 12, 2026
## 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 #4040-style signed-body envelopes extending the #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; #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](https://claude.com/claude-code)
mswilkison
added a commit
that referenced
this pull request
Jun 13, 2026
Section 1 said the host "holds no signing secrets at any time," but section 3 maps the transitional DKG calls unchanged and the frozen Phase 7 spec still has the DKG APIs returning/accepting secret_package_hex through the host until the DKG-custody follow-up. So in deployments that run DKG through this transport the host still sees DKG secret material (review finding). Section 1 now scopes the property to the signing path and states explicitly that #4007 must treat the host<->sidecar signing interface as a secret boundary but NOT the DKG interface until DKG custody moves inside the sidecar - a precondition for the sidecar being a complete secret boundary. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds the optional TEE signer-hardening readiness stack on top of
PR #4005, which
lands the base FROST/ROAST Rust signer at
pkg/tbtc/signer/.It must NOT be undrafted or merged until:
mainafter feat(tbtc/signer): mirror FROST/ROAST Rust signer from tBTC monorepo #4005 merges.Canonical repo note
This PR was initially sourced from
tlabs-xyz/tbtc#88, but
threshold-network/keep-coreis now the canonical repo for the signer. Themonorepo/source-PR reference is provenance only; follow-up fixes in this PR are
ordinary keep-core changes and are not expected to remain byte-for-byte mirrors
of the old source branch.
Scope
Four-phase TEE checker stack:
tee_registry_checker)tee_token_checker)tee_runtime_checker)tee_enforcement_checker)Files
pkg/tbtc/signer/src/bin/tee_*.rspkg/tbtc/signer/scripts/tee-*.sample.jsonpkg/tbtc/signer/README.mdpkg/tbtc/signer/docs/tee-whitelisted-signer-activation-gate-record.md+tee-whitelisted-signer-enforcement-plan.mdFollow-up fixes included
signer Rust checks workflow is present.
documentation, and updates the new TEE sections to use
pkg/tbtc/signer/.different
key_ids, closing the threshold-quorum bypass.time and policy TTL, so an old ticket cannot refresh its waiver window.
across the registry/token/runtime checkers.
Unix epoch.
Initial provenance
31f0aa2b8efbf8ac42909f8e8d4ce67248cb478efeat/tee-phase-a-governance-registry(stacked onfeat/frost-schnorr-migration)extraction/frost-signer-mirror-2026-05-26(PR #4005)Verification
Local verification on the latest pushed head:
cargo fmt --manifest-path pkg/tbtc/signer/Cargo.toml -- --check cargo clippy --manifest-path pkg/tbtc/signer/Cargo.toml --all-targets -- -D warnings TBTC_SIGNER_STATE_PATH=/tmp/tbtc-signer-ci-state-pr4007-local.json \ cargo test --manifest-path pkg/tbtc/signer/Cargo.tomlPlan context
This is an optional, decision-gated addition to the FROST extraction. Unlike the
mandatory extraction/mirror PRs, this PR's merge depends on the Threshold TEE
policy decision, not just operational readiness.
🤖 Generated with Claude Code