Repository navigation
Conversation
…t checker; Details: TC-2223
…tor documents; Details: TC-2223
…rtificate checks; Details: TC-2223
…ficate SPEC rulings to the checker; Details: TC-2223
…ertificates in the checker; Details: TC-2223
…2223-s05-transcript-checker; Details: TC-2223
…/tc-2223-s05-transcript-checker; Details: TC-2223
…er; Details: TC-2223 Genesis ceremonies and genesis/re-key certificates accept n >= 2t-1 or exactly 3-of-4, matching the crate; every other shape stays UnsupportedFaultBudget.
…DKG vector in the checker; Details: TC-2223
…les; Details: TC-2223
…er checker; Details: TC-2223 The certificate-layer decoder and its genesis and re-key certificate and ceremony checks now share isGenesisShape with the transcript layer (n >= 2t-1 or exactly 3-of-4). Cross-family tests accept the same 3-of-4 genesis and re-key certificates and genesis ceremony in both layers and refuse 3-of-3 and 4-of-5 in both.
…certificate-layer closed rounds; Details: TC-2223 Reshare/refresh closed rounds in certificates.ts now resolve and verify an envelope in its signed cohort, then drop it from the round's role messages when it is outside the round's role (R1/R3 old dealers, R2 new recipients), as SPEC S0.4, the crate and reshare.ts do. Unresolvable or badly signed envelopes are still refused; genesis/re-key still accept recipients only. Cross-checker tests use one reshare transcript with an extra recipient-cohort R1 envelope (accepted by both), an index outside the directory and a wrong-key signature (refused by both).
samgbafa
changed the base branch from
skgbafa/tc-2221-threshold-reshare
to
main
November 8, 2026 00:28
samgbafa
marked this pull request as ready for review
November 8, 2026 00:29
This branch has not been deployed
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.
Independent checker for threshold DKG and resharing transcripts (S0.5). Stacked on #493 (TC-2221), which sits on #492 (TC-2220); retarget to
mainafter those merge.tinycloud-threshold/vectors/checknow recomputes a whole genesis DKG and a whole reshare/refresh outcome from the raw signed records, using only the published SPEC. It covers the H0 start link and transcript chain, every closure and member signature and binding, the complaint and answer rules, QUAL/D selection, mpk and public shares, identity rejections, the 4 MiB bound and the malformed-message rules. It reuses lane A's primitives (TH, BLS12-381 decoders,@noble/curves2.4.0) and Ed25519 from the same package withzip215: false(strict RFC 8032). No new dependency;bun.lockis unchanged.SPEC changes (owner rulings, 2026-10-30)
The independent review approved this PR at
ee0d4d3d. A follow-up commit applies the owner's SPEC rulings toSPEC.mdand enforces them in the checker:MalformedDeal. This supersedes proposal 7.genesis_t = tandn ≥ 2t−1. Every successor requiresepoch ≥ 1andrekey ≤ epoch. A re-key must change mpk (previous mpk ≠ mpk), haverekey ≥ 1, and, like genesis, satisfygenesis_t = tandn ≥ 2t−1(UnsupportedFaultBudget). The re-key clauses were added in a second rulings commit after the delta review; they matchverify_successor's "genesis t = new t" and S0.2's "re-key uses the genesis budget".InvalidCeremony(authorized against the pinned certificate). This settles proposal 9. Rust's resharing reports a different code for this input, but it also rejects it, and codes are informative, so no Rust change is needed (Naomi, 2026-10-30).Clean-room statement
Files read for this checker (verbatim list). No Rust source under
tinycloud-threshold/src/, no Rust git history and no Rust diffs were opened.tinycloud-threshold/SPEC.md(whole file, including the S0.2, S0.3 DKG and S0.4 redistribution appendices)tinycloud-threshold/vectors/v1/dkg-genesis.json,reshare.json,cert-core.json(layouts, and the published Ed25519 fixture seeds for tests),primitives.jsontinycloud-threshold/vectors/check/*(lane A's checker:README.md,package.json,bun.lock,bunfig.toml,tsconfig.json,.gitignore,isolate.ts,cli.ts,schema.ts,vectors.ts,protocol.tsand the four existing*.test.ts)/var/tmp/tc2092-plan.J13a/plan-r2.md(sha256f701d2be…4d14verified): §0, §§3.0–3.11, §5 (allowed by Naomi for error names; it names none), §6@noble/curves2.4.0README.mdand theed25519.d.ts/abstract/edwards.d.tsdeclarations (API only).github/workflows/rust.yml(only to confirm the CI lane)/var/tmp/naomi-brief-lines.txt,host-hygiene.mdVector coverage
Every committed case agrees byte for byte (
bun run vectors: primitives 18/18, DKG 6/6, reshare 5/5).genesis_2of3genesis_2of3_selective_extractiongenesis_3of5_two_faultygenesis_3of5_answers_and_disqualificationEquivocated{round:1}(dealer 4);MissingAnswer(dealer 5)insufficient_reconstruction_valuesInsufficientRecoverywith R6 as last recordinsufficient_qualified_dealersInsufficientQualifiedwith R3 as last recordreshare_2of3_to_3of4refresh_2of3_answered_complaintreshare_wrong_constantMalformedDeal; D = [2,3]reshare_t_prime_complaintsComplaints; D in R1 order skips dealer 3reshare_insufficient_dealersInsufficientQualifiedSynthetic transcripts (
testkit.ts, signed with the published fixture seeds) cover each rule the vectors do not. In DKG: every disqualification reason, the order of rules 5–8 pairwise, self-complaints, R2/R3 index bounds, unrequired valid and invalid answers, missing/conflicting/malformed/wrong-count R4, R5 entries that are upheld, self-directed against a Feldman-invalid dealer, Pedersen-invalid, or against a Feldman-valid dealer, R6 shortfall and conflicts, and 3-of-5 with two silent members. Genesis ceremony rules: anchor, kind, current digest, counters, budget, directory contiguity and duplicate DIDs, option marker, signature cap. In reshare: every exclusion reason, the order of rules 4–7, R1 owner-order selection, other-cohort envelopes ignored, no self-exclusion, and entry checks. In both: identity mpk, top coefficient and public share; byte-identical and alternate-signature retransmissions; envelope, closure and certificate signature caps; record count; closure binding; list reordering. Strict RFC 8032: a ZIP-215-only signature (non-canonical R), S + L and a small-order public key are rejected.s02.test.tsruns everycert-core.jsoncertificate (genesis, reshare successor, re-key), ceremony, decode refusal and closed-round record through the same S0.2 layer; all agree. It also covers the §3.9 decoded-certificate rules: constant = mpk, top coefficient, evaluated shares, predecessor combinations, genesis t, option markers, anchor and signer.Checker-order reason codes (corruption probe)
Each probe flips one byte of one committed signed record, in every field class, for all 11 cases (first and last message of each round). 1309/1309 are rejected, each with the reason code this checker's order produces. Rejection is normative; the reason codes are informative (SPEC "Verifier conformance").
Re-signed one-byte payload flips reach the rule-level reason. Dealer commitment byte →
MalformedDeal(QUAL [2,3]). R2 version byte → malformed R2 contributes nothing (mpk unchanged). R4 byte → extraction fault, so a 5-record transcript isInvalidRound. Corrupted upheld R5 pair → nothing to reconstruct, so R6 is an extra record (InvalidRound). Reshare B_0 byte →MalformedDeal, D [2,3]. R3 value byte →InvalidAnswer. Complaint version byte →MissingAnswer. One byte over 4 MiB →LimitExceededbefore any signature.Error names chosen as defaults (not from SPEC, plan §5 or vector reason fields)
Plan §5 names the error types but no variants. The defaults below follow Naomi's 2026-10-30 rulings and sit in one table (
DEFAULT_ERROR,ceremony.ts):InvalidSignature: owner or member signature fails strict RFC 8032, or the DID has no key.InvalidRound: closure non-canonical, ordered-list mismatch, envelope names another ceremony/round. (SPEC namesInvalidRoundfor the closure's ceremony/round/previous-hash binding and for record counts; the rest is a default.)InvalidIndex: envelope cohort/index does not resolve in its signed cohort (including any cohort-2 envelope in genesis, and unknown cohort bytes).InvalidCeremony: ceremony owner/network differs from the anchor; genesis with a current digest or non-zero counters; successor epoch ≠ checked current + 1 or re-key counter changed. (Stale or absent current digest is now SPEC-namedInvalidCeremony.)InvalidCertificate: current certificate owner/network differs from the anchor.ContextMismatch: reshare B′_0 ≠ current mpk.InvalidEncoding: envelope over 64 KiB, any signature over 512 bytes; certificate commitment[0] ≠ mpk, genesis t outside 2..=16, and the §3.9 field-combination rules (including the new genesis_t = t, successor epoch/rekey and re-key mpk/rekey rules). A genesis certificate over budget reports the SPEC-namedUnsupportedFaultBudget.SPEC clarifications proposed
Fix the check order inside a closed round.Settled: codes and their precedence are informative (SPEC "Verifier conformance"). This checker's order, for reference: closure ceremony/round/previous → owner closure signature → each envelope (decode, ceremony/round, signer resolution, signature) → ordered-list commitment.InsufficientQualified,InsufficientRecovery) must be the last record; extra records areInvalidRound(Naomi ruling).zip215: false, which rejects non-canonical A/R encodings, S ≥ L and (observed) small-order public keys.Sealed dealing decoded by layout only.Superseded: SPEC now requires a ciphertext of at least 16 bytes.A stale current digest has two names.Settled:InvalidCeremony(SPEC S0.4 now says so; the checker follows).dkg_verify_transcripthere returnsInvalidCeremonyfor it.Verification
bun install --frozen-lockfile && bun run check: all tests,tsc --noEmit, and the real CLI over the three committed documents pass.≥ t→> t; R3 index bound removed; R6 record-count rule removed; R5i ≠ jremoved; DKG rules 5–8 and reshare rules 4–7 reordered pairwise; reshare B_0 check removed; R1-order selection removed. Checks:>→≥on the 4 MiB bound; ZIP-215 enabled; certificate constant/top/public-share/combination/genesis-t/option-marker checks removed; envelope, closure and certificate signature caps removed; directory contiguity removed; genesis re-key-counter check removed. The "lowest t R6 senders" mutation survives because it is unobservable (see 8).genesis_t = tremoved; genesis budget removed; successorepoch ≥ 1removed;rekey ≤ epochremoved; re-keympkchange removed; re-keyrekey ≥ 1removed; re-keygenesis_t = tremoved; re-key budget removed; stale/absent digest back toContextMismatch. New tests: 0- and 15-byte ciphertexts exclude the dealer and change QUAL (DKG) or D (reshare); a 16-byte tag-only ciphertext is accepted; a re-key with t=2, n=3, genesis_t=3 is rejected; a 3-of-4 re-key isUnsupportedFaultBudget.ContextMismatchtoInvalidCeremonyper ruling 4.Internal review (builder's subagent)
This is an internal check, not the independent review; Naomi has seated a separate independent Opus reviewer for this PR. A fresh-context Opus subagent read SPEC and the checker line by line. It found no P0. Its four P1 findings were all test gaps: rule precedence, the R5
i ≠ jrule, untested §3.9 certificate rules and caps, and genesis counter rules. All four are fixed in the follow-up commit, with each reported surviving mutant now killed. Its P2 items are applied: every default is inDEFAULT_ERROR, the README wording is corrected, and clarifications 4 and 9–10 are added.Subagent clean-room disclosure (accepted by Naomi, 2026-10-30): one subagent grep over
tinycloud-thresholdfor error names also printed about 40 lines of Rust error-variant snippets fromsrc/*.rs. The subagent opened no Rust file, history or diff, made no finding that relies on those lines, and declined to rule on directory-rule error names because of them. The builder (author of this PR) did not see those lines; no code here depends on them.The independent reviewer (
abf01a5d) approvedee0d4d3d, requested one P2 change at3e61e74c(re-key certificate rules), and approved6d1883aewith no remaining P1/P2/P3 findings. The head is frozen at6d1883ae; retarget tomainonly after #492 and #493 merge.Provenance log
dkg-genesis.json,reshare.json,cert-core.jsonseeds)zip215: falseAPIDetails: TC-2223
Update: 3-of-4 genesis at 6aedd42
df81eb18merges #493 at57ef5e24(3-of-4 genesis), then three commits:2859fa20feat:checkGenesisShapeinceremony.tsacceptsn ≥ 2t−1or exactly 3-of-4, and is used for genesis ceremonies (dkg.ts) and for genesis and re-key certificates; every other shape staysUnsupportedFaultBudget. README check order updated.0e7ae846test: a synthetic honest 3-of-4 DKG matches the scalar-domain expectation; 4-of-5 and 3-of-3 genesis ceremonies are refused while 3-of-4 is refused only for its missing rounds, like 2-of-3; 3-of-4 genesis and re-key certificates are accepted, 3-of-3 and 4-of-5 refused; the committedgenesis_3of4_one_faultyDKG vector is recomputed (the CLI reportsDKG transcripts: 7/7).6aedd42fdocs: the SPEC certificate rules name the 3-of-4 genesis shape.Checks at
6aedd42f:bun run check(tests, typecheck, vectors): 219 pass, 0 fail; Primitive 18/18, Certificate 18/18, DKG transcripts 7/7, Reshare 5/5. With the shape rule reverted ton ≥ 2t−1, five tests fail, including the committed-vector and CLI checks.Details: TC-2223
Update: certificate-layer checker at aa8ebc8
One commit on
6aedd42f. The certificate-layer checker (certificates.ts, which the CLI uses forcert-core.json) still appliedn ≥ 2t−1in its decoder and in its genesis/re-key certificate and ceremony checks. All four checks now useisGenesisShapefromceremony.ts(n ≥ 2t−1or exactly 3-of-4), the same predicate behind the transcript layer'scheckGenesisShape. No other rule remains that checks the genesis shape.genesis-shape.test.tspasses the same signed inputs to both layers: 3-of-4 genesis and re-key certificates and a 3-of-4 genesis ceremony are accepted by both; 3-of-3 and 4-of-5 are refused by both; the certificate-layer re-key ceremony accepts 3-of-4 and refuses 3-of-3. Withcertificates.tsat6aedd42f, three of its four tests fail.Checks at
aa8ebc8a:bun run check223 pass, 0 fail; Primitive 18/18, Certificate 18/18, DKG transcripts 7/7, Reshare 5/5.Details: TC-2223
Update: certificate-layer closed rounds at 8a21c36
One commit on
aa8ebc8a. Closed reshare and refresh rounds incertificates.tsused to reject an authenticated envelope signed under the cohort outside the round's role. SPEC S0.4, the crate andreshare.tsauthenticate such an envelope and then ignore it.certificates.tsnow does the same: it resolves the envelope's index in its signed cohort and verifies the signature, then leaves the envelope out of the round's role messages. The ordered-list hash still covers every envelope. An index outside the cohort's directory, or a bad signature, is still refused. Genesis and re-key rounds still accept only the recipient cohort.closed-round-cohorts.test.tsruns one 2-of-3 → 3-of-4 reshare transcript through both layers, adding one extra R1 envelope in each case:InvalidIndex, certificate layerInvalidCeremony);InvalidSignature.With
certificates.tsataa8ebc8a, two of its three tests fail.Checks at
8a21c36b:bun run check226 pass, 0 fail; Primitive 18/18, Certificate 18/18, DKG transcripts 7/7, Reshare 5/5.Details: TC-2223