Skip to content

catch-up-to-draft - #36

Merged
OR13 merged 5 commits into
mainfrom
catch-up-to-draft
Oct 20, 2026
Merged

OR13 merged 5 commits into
mainfrom
catch-up-to-draft

Conversation

@OR13

@OR13 OR13 commented Oct 20, 2026

Copy link
Copy Markdown
Owner

Brings the implementation up to the current draft (`65893f6`, 2026-10-17) and the newest katie that builds (`a2959a5`).

What changed

  • `AuditorUpdate.leaves` (§15.2): new wire field, auditor steps 2 and 4, and `PrefixTree::mutate` for the log's side. Vectors cover removals that move one or two leaves, a chain collapse, and a log that omits `leaves` (both implementations reach the same wrong root).
  • Wire counts: `PrefixProof` and `AuditorUpdate` counts are now four bytes (`<0..2^32-1>`).
  • Binary ladders cover the whole `uint32` range; the ladder for `2^32-1` has no non-inclusion rung. `binary-ladder.json` no longer stops at `2^31-2`.
  • §4.2 restart: our first reading resent the user's own retained entry; katie caught it. The pre-amendment reading is deleted.
  • Interop: generators ported to katie's merged `db` package and new auditor API. `from-kt.json`: 235 artifacts, 0 disagreements.
  • Docs: pins, findings register (KT-01/02/03/05, DRAFT-01/04/06 resolved; KT-08, NOTE-05 added), survey of the IETF 125 slides' implementations.

Things to know

  • katie is pinned at `a2959a5`, not its tip: `main` at `f93331d` does not compile (KT-08). Log entry 0 updates, the owner left-direct-path change and the distinguished operation are therefore not verified against the peer.
  • katie needs Go 1.27; CI's `go-version: stable` should cover it but this has not run in CI yet.
  • Includes `8f098e1` (P-256 suite), which is not on `main`.
  • Checked locally: fmt, clippy, 335 tests, line coverage 97.7% (gate 97%), katie's own tests, regeneration diff (only the four known non-reproducible files differ).

🤖 Generated with Claude Code

OR13 and others added 4 commits July 30, 2026 11:19
… and it is the only one that exercises VRF.Np != 80

Implements `KT_128_SHA256_P256`: ECVRF-P256-SHA256-TAI (§11.7) and ECDSA/P-256 over SHA-256
(§11.3, §11.4). Both agree with katie on the first run. The coverage table loses its only
out-of-scope row: 35 verified against the peer, 2 implemented without an oracle, 3 not implemented.

The VRF is the substantial half, and it has two oracles that are independent of each other. RFC
9381 Appendix B.1's three vectors are in the unit tests and settle every choice the module makes —
the suite string, the three domain separators, the big-endian integers, the SEC1 0x02 tag in
encode_to_curve, the absence of truncation in proof_to_hash. Getting any one wrong fails all three.
katie's own proofs are the second, through vrf-p256.json, and they pin what RFC 9381 says nothing
about: that alpha_string is the encoded VrfInput.

Verification only, and the module says so rather than stubbing a prover. A VRF proof is produced by
a log and consumed by everyone else, and RFC 9381 §5.4.2.1 derives P-256's nonce with RFC 6979,
which is a signing concern a verifier has no use for. What a client must be able to do — take an
81-byte proof from a BinaryLadderStep and recover the search key it commits to — is there in full,
and the report row says "proving is a log's operation and is not" rather than implying otherwise.

`vrf.rs` becomes `vrf/{mod,edwards25519,p256}.rs`, mirroring both RFC 9381's structure and the
peer's `crypto/vrf/{edwards25519,p256}`. Error and Output are shared — a search key is 32 bytes
either way, since §17.1 truncates edwards25519's 64-byte beta_string and takes P-256's whole 32 —
and everything else differs: curve, hash, integer byte order, encoded sizes. The keys and proofs are
deliberately not unified behind an enum: a Configuration fixes the suite for the whole log, so a
caller knows which it holds, and a sum type would only move that knowledge to run time and invite
reading an 81-byte proof as an 80-byte one.

Signatures were the easy half but have their own trap, now documented: the two suites differ in how
the *key* is encoded and §11.2 does not say. The peer emits an Ed25519 key as 32 raw bytes and a
P-256 signature key uncompressed at 65, while the same Configuration carries a P-256 VRF key
compressed at 33. Rather than fix a length, the P-256 path accepts whatever SEC1 admits, and
tree-head-p256.json pins what the peer sends.

Two new vector files, generated by parameterising the existing generators rather than copying them,
so a difference between the suites' files is attributable to the suite and nothing else.
tree-head-p256.json cannot be reproducible, because Go's crypto/ecdsa draws a nonce per signature —
and here that is a feature: CI regenerates and re-checks, so every run verifies signatures nobody
has seen, and a verifier that happened to work for one nonce would fail. Added to the exclusion list
with that reasoning attached.

What P-256 does *not* buy, recorded so the table is not read as more than it is: both suites use
SHA-256 and identical commitment fixed bytes, so commitment.json, log-tree.json, prefix-tree.json,
log-append.json, prefix-mutation.json and the pure-math families would be byte-identical under it.
The new coverage is the VRF, the signatures, the suite code in every Configuration, and VRF.Np = 81
— which the response decoders take as context and have only ever run at 80. Threading 81 end to end
through a live SearchResponse is the remaining piece, tracked in #27.

7071 checks over 817 cases in 21 suites, 0 disagreements. 225 reverse-direction artifacts, 0
disagreements. Coverage floor holds at 97.67% lines.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…re changes (AuditorUpdate.leaves, 2^32 counts, ladder overflow)

12121fd -> 65893f6 (2026-10-17). Pin bump only; the code that follows from it is in the next commits.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…o the pin is the last commit that does

00da525 -> a2959a5 (2026-10-15), not main at f93331d: tree/transparency fails to compile there and two algorithms tests fail. Needs Go 1.27. Recorded as KT-08.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…counts to 2^32 and skips ladder rungs above 2^32-1; katie agrees, and the comparison caught a misreading of §4.2 here

- kt-wire: AuditorUpdate.leaves; PrefixProof and AuditorUpdate counts are four bytes
- kt-tree: PrefixTree::mutate (the log's side, returns moved leaves); evaluate_before_after and the auditor take leaves, with new step 2 and step 4 checks
- kt-tree: ladders are infallible and cover 2^32-1; update_view's restart no longer resends the user's own entry (katie's reading, which is the draft's)
- interop: generators ported to katie's merged db package and auditor API; prefix-mutation.json now carries leaves, including a log that omits them; binary-ladder.json runs to 2^32-1; from-kt.json grows to 235 artifacts with 0 disagreements
- docs: pins, register (KT-01/02/03/05 and DRAFT-01/04/06 resolved; KT-08, NOTE-05 added), survey of the slides' implementations

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…d, and KT-08 was unfiled

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@OR13
OR13 marked this pull request as ready for review October 20, 2026 04:10
@OR13
OR13 merged commit 7ba65f1 into main Oct 20, 2026
3 checks passed
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