Skip to content

clazel-crypto: libcrux providers for TLS hashing, key schedule, records and X25519 (3.5f, part 1) - #19

Merged
atiernan merged 10 commits into
phase3/tls-bootstrap-ccsfrom
phase3/crypto-provider
Oct 27, 2026
Merged

atiernan merged 10 commits into
phase3/tls-bootstrap-ccsfrom
phase3/crypto-provider

Conversation

@atiernan

@atiernan atiernan commented Oct 26, 2026 •

Copy link
Copy Markdown
Member

Stacked on #13. This is the first shipped cryptography: a new crate, clazel-crypto, that instantiates clazel-tls's provider traits with libcrux. clazel-tls itself stays parameterised and free of axioms.

Commits

  • 329172b contracts: src/spec.rs holds uninterpreted spec functions for SHA-256, HMAC-SHA256, HKDF-SHA256, ChaCha20-Poly1305 and X25519. src/axioms.rs is the trust boundary: one external_body wrapper per libcrux call, each with an // AXIOM: that states only libcrux's documented input and output types, plus one proof axiom for the 16-octet tag length. That makes 9 axioms. Inputs over u32::MAX, which libcrux refuses or panics on, abort the process instead of returning a value no axiom covers. The commit also adds the docs/DEPENDENCIES.md rows.
  • ba1a820 adapters and tests: src/provider.rs implements schedule::crypto::Crypto, traffic::provider::{Factory, KeyedAead} with record::Aead, and hello::key_exchange::KeyExchange, all checked by Verus against the traits' contracts. The 32-octet AEAD key is a type invariant. tests/known_answers.rs runs the standards' own vectors through the traits:
    • FIPS 180-2 B.1 (SHA-256);
    • RFC 4231 §4.2–4.3 (HMAC-SHA256);
    • RFC 5869 A.1 and A.3 (HKDF-SHA256);
    • RFC 8439 §2.8.2 (ChaCha20-Poly1305), with tampered ciphertext, tag and additional data;
    • RFC 7748 §6.1 (X25519), and refusal of an all-zero shared secret.

Dependencies (decided 2026-10-26)

  • Compiled into the browser:
    • libcrux verified-hacl crates with no unsafe: libcrux-sha2, -hmac, -hkdf, -chacha20poly1305, -poly1305 and -curve25519, all at exact 0.0.x versions;
    • their pre-verification support crates: libcrux-hacl-rs, -traits, -secrets and -macros, the last compile-time only;
    • rand and rand_core, which libcrux-traits requires but we never call.
  • Compiled but never called: libcrux-hmac 0.0.9 has a mandatory dependency on libcrux-sha3 (pre-verification) for its HMAC-SHA3 types. That brings in libcrux-intrinsics (193 unsafe mentions) and libcrux-platform. Our HMAC-SHA256 path calls only libcrux-sha2. Alex chose libcrux's verified HMAC and HKDF over writing our own, since HACL* proves the timing independence that our own code would lack. Their rows say so.
  • 50 rows locked but never compiled: packages reached only under cfg(hax) or cfg(valgrind_ct_test), which our builds never set.
  • 19 existing rows raised to shipped, because libcrux now reaches them through normal dependency edges; each row says whether that use is compiled. The libc row also notes libcrux-platform.

Local results

  • Verus on clazel-crypto: 13 verified, 0 errors, fresh run.
  • cargo test -p clazel-crypto: 8 passed, 0 failed.
  • Strict clippy and cargo fmt --check are clean. cargo xtask check reported STATIC CHECKS PASSED with 9 documented axioms.
  • No full verify has run. The crate is at tier = 0 and target-tier = 0, since it parses nothing.

Needs, before it lands

  • A review of src/axioms.rs by a different session Done, twice. Neither reviewer wrote the code, and both read it against the pinned libcrux sources.
    • The first review, of f6f7249e…d508, found that hkdf_expand could panic on an info within 33 octets of u32::MAX, and that the input cap sat past HACL*'s preconditions. Both are fixed in a335ece.
    • The second review, of c05d4642…42e3 (the current file), found all 9 axioms sound and every finding fixed. Its wording notes are fixed in 51e67fd, which leaves axioms.rs unchanged.
    • Caveat: the HACL* F* preconditions behind MAX_INPUT were recalled, not read, because the pinned crates ship only the extracted Rust.
  • Not in this PR:
    • signature verification (ECDSA P-256, RSA-PSS and Ed25519 from libcrux, and RSA PKCS#1 v1.5 from clazel-pkcs1), which comes with path validation;
    • the README's safe-Rust line, which phase-3.md 3.0 says 3.5f updates (VERIFICATION.md's TCB line is now updated, in a335ece and 51e67fd);
    • the timing-safe finished_equal.

🤖 Generated with Claude Code

atiernan and others added 10 commits October 26, 2026 17:53
New crate clazel-crypto (Phase 3 item 3.5f), contracts only:
- src/spec.rs: SHA-256, HMAC-SHA256, HKDF-SHA256, ChaCha20-Poly1305
  and X25519 as uninterpreted spec functions.
- src/axioms.rs: one external_body wrapper per libcrux call, each an
  `// AXIOM:` stating only libcrux's documented input/output types, plus
  the 16-octet tag length. Inputs over u32::MAX abort instead of
  returning unaxiomatised values.
- docs/DEPENDENCIES.md: shipped rows for the 15 compiled libcrux
  packages (verified-hacl crates, their pre-verification support crates,
  rand) and the 50 locked under cfg(hax)/cfg(valgrind_ct_test) only;
  19 existing rows raised to shipped since libcrux now reaches them.

libcrux-hmac 0.0.9 requires libcrux-sha3 (pre-verification) and so
libcrux-intrinsics and -platform: compiled, never called. Chosen on
2026-10-26 over writing HMAC/HKDF ourselves.

The adapters implementing clazel-tls's provider traits follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/provider.rs implements clazel-tls's provider traits with the
axiom wrappers, checked by Verus against each trait's contract
(13 verified, 0 errors):
- Sha256Suite: schedule::crypto::Crypto (SHA-256, HKDF, HMAC);
- ChaCha20Poly1305Factory / ChaCha20Poly1305: traffic::provider::Factory,
  KeyedAead and record::Aead, the 32-octet key a type invariant;
- X25519: hello::key_exchange::KeyExchange.

tests/known_answers.rs: the standards' own vectors through the traits
(FIPS 180-2 B.1, RFC 4231 §4.2-4.3, RFC 5869 A.1/A.3, RFC 8439 §2.8.2
with tampering, RFC 7748 §6.1 and the all-zero shared secret).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
From the independent review of src/axioms.rs (sha256 f6f7249e…d508):
- hkdf_expand could unwind: libcrux-hkdf's expand sizes a buffer with a
  wrapping u32 sum, so an info within 33 octets of u32::MAX slices out
  of range. The wrapper now aborts above MAX_INPUT first.
- MAX_INPUT is 2^32 - 256, below every HACL* precondition (the tightest
  is HKDF-Expand's inner HMAC), and every wrapper checks each
  variable-length input against it explicitly, so the module doc's
  "every wrapper checks first" is now true. AXIOM lines updated, and
  open's notes the < 16-octet ciphertext case.
- DEPENDENCIES: libcrux-hacl-rs has no compiled unsafe; its memzero is
  a no-op.
- VERIFICATION.md TCB: names the translation toolchain, libcrux's
  unverified glue and its pre-verification support crates.

axioms.rs changed, so it needs a fresh review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Wording findings from the re-review of src/axioms.rs (sha256
c05d4642…42e3, all 9 axioms sound): spec.rs and the README now give
MAX_INPUT (2^32 - 256) as the abort bound instead of u32::MAX, and the
VERIFICATION.md TCB entry says the axioms state output lengths and
failure cases as well as types. axioms.rs is unchanged, so the review
still applies to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Module doc (modelling choices: Verus proves the result, not the timing),
signature, requires and ensures. The body is a stub returning false, so the
postcondition deliberately does not verify in this commit (the contract is
the part to review); the implementation and proof follow in the next commit.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
One pass over every index, ORing the XOR of each byte pair, returning
acc == 0. Invariant: acc == 0 iff the first i bytes are equal; the bitwise
facts (x ^ y == 0 iff x == y, p | q == 0 iff both zero) are by (bit_vector).
No axioms. Tests: equal values, every single-byte difference, the empty
pair, and a last-byte-only difference.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…tem 3.0)

The xtask-checked sentence and unsafe count are unchanged; a sentence on the
shipped dependencies (libc, libcrux) is appended.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
"100% safe Rust except clazel-sys" followed by "safe Rust throughout"
contradicted itself; keep the checked sentence and state only the
shipped dependencies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
atiernan added a commit that referenced this pull request Oct 27, 2026
CI's mutation step fails a Verus crate with no mutations.json, and
clazel-crypto had none (since #19). signature/examples.rs proves known
answers about the spec's parsing and profile functions by evaluation: the
key OIDs, the secp256r1 parameter decoded by clazel-der, pad32, the
uncompressed point, Ecdsa-Sig-Value, the RSA moduli, the hash lengths and
the small-order table against encodings computed from the curve.

31 mutations: 25 killed with certificates, 6 killed incidentally. Those
six remove guards around the uninterpreted verifications, which no
concrete example can evaluate; the exec proof and tests/signature.rs catch
them, and they are not counted as coverage.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@atiernan
atiernan added this pull request to stack #17 October 27, 2026 06:01
@atiernan
atiernan marked this pull request as ready for review October 27, 2026 07:12
@atiernan
atiernan merged commit 96baecf into phase3/tls-handover Oct 27, 2026
27 of 28 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