Skip to content

TLS 1.3, libcrux crypto, CertificateVerify, P-384 and from_unix_seconds (3.5) - #34

Closed
atiernan wants to merge 198 commits into
mainfrom
phase3/tls-handover
Closed

atiernan wants to merge 198 commits into
mainfrom
phase3/tls-handover

Conversation

@atiernan

@atiernan atiernan commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Brings the TLS work of Phase 3 onto main. Supersedes #1. Draft: not to be merged until CI's full verification is green and Alex approves.

What the branch brings

All four crates are at tier 0, as declared in their manifests.

  • clazel-tls (tier 0, 0 axioms): TLS 1.3 record and handshake formats, non-PSK key schedule, ClientHello/retry exchange, server selection, initial authentication, bounded connection dispatcher over provider parameters, and the CCS-in-fragment refusal.
  • clazel-crypto (tier 0, 12 axioms): libcrux providers for hashing, key schedule, records and X25519, and verified CertificateVerify signatures (ECDSA P-256, RSA-PSS, Ed25519).
  • clazel-p384 (tier 0, 6 axioms, over fiat-crypto): verification-only ECDSA P-384 with Wycheproof, NIST CAVP and differential conformance suites.
  • clazel-x509: from_unix_seconds (clazel-x509: from_unix_seconds for path validation (3.5b P1), tier 0 #28) with its spec, contract, executable, lemmas, tests and 7 certified mutations (40 mutations in total, all killed).
  • Phase3/tls close and cert ext #29 (close and certificate extensions).
  • Shared: libcrux/fiat-crypto TCB text in docs/VERIFICATION.md, clazel-p384 optimised in the dev profile, xtask testdata generators for P-384 Wycheproof and CAVP.

clazel-sys is unchanged, the clazel-p384 axioms hash is e9ee88f1...a4e3 (unchanged), and clazel-crypto/src/axioms.rs is identical to the branch before the merge. No tier was changed or raised.

How the merge conflicts were resolved

  • clazel-der, clazel-pkcs1: Cargo.toml and README.md from main (tier 2). Both directories are now identical to origin/main. This branch's one commit touching them (4f77bd5, splitting the pkcs1 equivalence lemma) is already reflected in main's independent.rs, so nothing is lost and the tier-2 evidence is untouched.
  • clazel-x509: this branch's src/time/{spec,contract,exec,lemmas}.rs, tests/validity_time.rs and mutations.json, because each is main's file plus pure additions (from_unix_seconds; no line of main's was removed). src/lib.rs is main's (keeps pub mod roots). The result differs from main only in those six files.
  • Cargo.lock: this branch's, then cargo update --workspace --offline, which only added clazel-html and html5gum. No package changed version relative to either parent.
  • docs/DEPENDENCIES.md: all rows from both sides; where both had a row, this branch's (the same version, class shipped where libcrux reaches it, and a longer "why").
  • docs/ERRATA.md: every entry from both sides.
  • docs/phases/phase-3.md: main's rows for 3.5a and 3.5c (tier 2), 3.6 and 3.7; this branch's for 3.5d, 3.5e and 3.5f; 3.5b merges both (x509 is on main at tier 0, this branch adds from_unix_seconds).
  • xtask/src/testdata.rs: every generator from both sides (hash, html5lib, Wycheproof ECDSA and RSA, x509-limbo, CAVP).
  • README.md status table, CLAUDE.md, CI: main's, untouched.

Local checks (worktree at the merge commit)

  • cargo xtask check: STATIC CHECKS PASSED (clazel-sys audit, escape hatches, 18 documented axioms, tier declarations, dependency allowlist and generated test data all PASS).
  • cargo build --workspace, cargo clippy --workspace --all-targets, cargo fmt --check: clean.
  • cargo test -p clazel-x509 -p clazel-tls -p clazel-crypto -p clazel-p384 -p clazel-der -p clazel-pkcs1 -p xtask: exit 0, 271 passed, 0 failed.
  • Verus per crate from a clean cache: clazel-x509 268 verified, clazel-tls 726, clazel-crypto 44, clazel-p384 133; 0 errors each.
  • cargo xtask mutate clazel-x509: 40 mutations, 40 killed by a theorem, 0 survived.

CI has not run yet. Because shared files change (Cargo.lock, workspace manifest, xtask), it will verify every crate.

🤖 Generated with Claude Code

atiernan and others added 30 commits October 24, 2026 18:53
lemma_independent_equals_spec exceeded Verus's default resource limit on
Linux CI (it passed on macOS). The case where both texts reach their final
comparison is now its own lemma, as clazel-base64's encoder was split.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ger fails

CI will keep the table up to date on main from a fully verified commit's
crate parts, so verify and assemble now report a table that differs from
what they measured instead of failing on it. verify --part assemble
--metrics <dir> --write writes it, refusing if any check failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
atiernan and others added 13 commits October 27, 2026 18:18
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Skips whole 400-year cycles by division, walks at most 400 years and 12
months. Unit tests with the known answers, cross-checked with Python.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Leap-year century and 400 rules, February and 30-day month lengths,
seconds per day, epoch year and the 9999 bound.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
clazel-p384: Wycheproof, NIST CAVP and ring differential conformance (3.5h, PR5), tier 0
clazel-x509: from_unix_seconds for path validation (3.5b P1), tier 0
RFC 8446 §4.6.3 owes the response "prior to sending its next Application
Data record"; after our close_notify (§6.1) there is none. A received
update_requested then rotates only the receive epoch. Ready's invariant
now forbids an owed or queued KeyUpdate while the write side is closing
or closed, and receive_update_ensures says such a receipt sends nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RFC 8446 §4.4.2: extensions in the server's Certificate "MUST correspond
to ones from the ClientHello message"; §4.2 aborts an unsolicited one
with unsupported_extension. on_certificate now takes whether every
entry's extensions were offered and fails before chain validation if not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…te extensions

receive_key_update owes a response only while the write side is open, so
an update_requested after close_notify is sealed or sent no longer queues
a protected KeyUpdate behind it. queue_key_update now requires an open
write side; the existing guards in request_key_update already ensured it.

Authentication checks every CertificateEntry's extensions against the
ClientHello's offers and fails with unsupported_extension before chain
validation (RFC 8446 §4.4.2, §4.2). All 724 clazel-tls obligations verify.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ensions

A peer update_requested after our close_notify (sealed or sent) rotates
the receive epoch and sends nothing. Certificate entries carrying
status_request, SCT or an unknown extension, on the leaf or a later entry,
fail with unsupported_extension before the chain validator is called.
Both tests fail on the previous code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n rules

Three mutations, each killed by a lemma evaluated at a concrete input:
dropping the offered check in on_certificate, dispatching a constant in
place of certificate_extensions_offered, and owing a KeyUpdate response
after our close_notify. All 726 clazel-tls obligations verify.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved:
- clazel-der, clazel-pkcs1: main's tier-2 Cargo.toml and README.md (the
  directories are identical to main's).
- clazel-x509: main's lib.rs (keeps `pub mod roots`); time/{spec,contract,
  exec,lemmas}.rs, mutations.json and tests/validity_time.rs from this
  branch, which are main's plus the from_unix_seconds additions and its 7
  mutations.
- Cargo.lock: this branch's, plus clazel-html and html5gum added by
  `cargo update --workspace --offline`; no package changed version.
- docs/DEPENDENCIES.md, docs/ERRATA.md, docs/phases/phase-3.md,
  xtask/src/testdata.rs: union of both sides.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Comment thread crates/clazel-tls/tests/conformance_tls_formats.rs Fixed
Comment thread crates/clazel-tls/tests/connection_dispatch.rs Fixed
Comment thread crates/clazel-tls/tests/hello_auth_join.rs Fixed
Comment thread crates/clazel-tls/tests/hello_auth_join.rs Fixed
Comment thread crates/clazel-tls/tests/inner_plaintext_limits.rs Fixed
Comment thread crates/clazel-tls/tests/traffic_epoch.rs Fixed
@atiernan atiernan changed the title Phase3/tls handover TLS 1.3, libcrux crypto, CertificateVerify, P-384 and from_unix_seconds (3.5) Oct 29, 2026
@atiernan
atiernan marked this pull request as draft October 29, 2026 00:18
atiernan and others added 4 commits October 29, 2026 00:23
Spec-only commit (path-validation P2). Contracts are unchanged: they say
"output == classify(..)" / "== parse_subject_public_key_info(..)", so they
pick up the new spec. The exec code does not match yet, so this commit does
not build on its own; the exec change follows.

- signature_algorithm/spec.rs: AlgorithmClass::Ecdsa(EcdsaHash::{Sha256,
  Sha384}) for ecdsa-with-SHA256 (1.2.840.10045.4.3.2) and ecdsa-with-SHA384
  (1.2.840.10045.4.3.3). RFC 5758 §3.2: parameters MUST be omitted, so a
  known ECDSA OID with any parameters is malformed (None). RSA classes
  unchanged. ecdsa-with-SHA1 and -SHA512 stay Unsupported.
- spki/spec.rs: PublicKey::EcP256 (X||Y) and EcP384 (the 97-octet ECPoint)
  for id-ecPublicKey with namedCurve secp256r1 / secp384r1 and an
  uncompressed point of exactly 65 / 97 octets (RFC 5480 §2.1.1, §2.2).
  Unclassified EC keys stay Unsupported, so the inputs that parse are
  unchanged. RSA unchanged.
- crypto signature/spec.rs (a spec file): p256_point now reads the EcP256
  class. The previous definition is kept as the lemma-only
  p256_point_direct; the equality is proven in the exec commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Spec-only; the two guards mean the same as before. The textual difference keeps
the existing sig-match-supported-profile-hash and sig-supported-unused-bits-zero
mutations' find strings unique.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Implements the previous spec commits. Verus: clazel-x509 297 verified, 0 errors;
clazel-crypto 43 verified, 0 errors (both from a clean cache).

- signature_algorithm/exec.rs: EcdsaHash, AlgorithmClass::Ecdsa, classify and
  prepare_signature_input for ecdsa-with-SHA256/384 (parameters absent).
- spki/exec.rs: PublicKey::EcP256 / EcP384 via classify_ec_public_key.
- spki/lemmas.rs: lemma_parse_classifies_key and lemma_parse_ec_lengths.
- crypto: p256_point reads the EcP256 class; lemma_p256_point_unchanged proves
  it equal to the earlier direct definition p256_point_direct on every parsed
  key. ecdsa_p256_sha256 now requires ec_lengths_ok (from the parse) to meet the
  libcrux axiom's 64-octet precondition; no axiom changed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… for EC classes

- x509 certificates: compute_only known answers for each new OID, both curve
  parameters, point forms and lengths, unused bits, and whole-DER P-256/P-384
  keys generated with openssl.
- x509 mutations.json: 55 new certified killed mutations (every arc of both
  ecdsa OIDs and of id-ecPublicKey, every octet of both curve parameters,
  04 prefix, 65/97 lengths, point offsets, parameters-absent, hash
  selection, pairing). cargo xtask mutate clazel-x509: 95 killed by a theorem.
- crypto mutations.json: 2 new killed mutations for p256_point; the three
  p256-compressed/-length/-subrange witnesses now evaluate p256_point_direct
  (find/replace/expect unchanged). cargo xtask mutate clazel-crypto: 27 killed,
  6 killed incidentally, 0 survive.
- tests: ECDSA classes, EC keys, and the trust store: 3 P-256, 35 P-384, 79 RSA
  and 1 unclassified (e-Szigno TLS Root CA 2023 on secp521r1) roots, checked
  against a raw curve-OID scan.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@atiernan
atiernan added this pull request to stack #39 October 29, 2026 05:28
@atiernan
atiernan marked this pull request as ready for review October 29, 2026 11:04
Brings in #35 (DOM tree builder), #44 (Windows support) and #49 (CI),
so #34 merges cleanly.

Conflicts:
- Cargo.lock: this branch's, then cargo update --workspace --offline,
  which only added clazel-dom and clazel-treebuilder. Every package
  version is one of the two parents'.
- README.md: main's unsafe counts (38 blocks, 136 lines, from the
  Windows sandbox) with this branch's shipped-dependencies sentence.
- docs/ERRATA.md: every entry from both sides (15).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@atiernan
atiernan removed this pull request from stack #39 October 30, 2026 15:53
clazel-x509, clazel-crypto: ECDSA signature and EC key classification (3.5b P2), tier 0

This branch is waiting to be deployed

1 waiting deployment
full-verification — 6c588901 Waiting Oct 30, 2026 by atiernan via Approve the proofs #170
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.

2 participants