Skip to content

colibri 0.6.0: AES-256-GCM first on the AES instructions, and a client's own suite order

Choose a tag to compare

@c4milo c4milo released this 29 Sep 20:45
· 655 commits to main since this release

colibri 0.6.0 runs AES-256-GCM first where the CPU has the AES instructions, as a TLS client and as a server, and lets a client choose the suites it offers. It pins chapulin at 10a5bc8, whose default order this is, and stdx at c134f2c. qlog writes each record's tokens many at a call, and the client and the server send a failed record's alert before they close.

Breaking

  • qlog: TextWriter is now qlog's batch writer, which gathers a record's tokens and hands them to stdx's JSON writer many at a call (81707f2). It keeps json.TextWriter's calls, but written() returns an error union, because it writes what is still gathered. The records are the same octets.

To move from 0.5.0, handle the error qlog.TextWriter.written() may return.

Changed

  • tls: on a target with the AES instructions, both roles prefer TLS_AES_256_GCM_SHA384, then TLS_AES_128_GCM_SHA256, then TLS_CHACHA20_POLY1305_SHA256, where 0.5.0 put ChaCha20 first (b3880da, chapulin's decision 80). AES-256-GCM comes first for CNSA 2.0. Against Go's server a client still runs AES-128-GCM, because Go chooses among the AES suites by its own order once the client lists one first. A target without the AES instructions holds ChaCha20 alone, as before. A program that wants another order sets tls.values.Server.cipher_suites, or the new client field below.

Added

  • tls: values.Client.cipher_suites, the suites a client offers, most preferred first, as RFC 9846 Appendix B.4 codepoints. The ClientHello offers exactly this list, and an empty one keeps chapulin's order. An object without AES-GCM refuses a list with SuitesUnavailable, and chapulin refuses a suite named twice when the session starts (b3880da).
  • tls.quic: version, the QUIC version every session and Retry protects packets for: version 1 (RFC 9000 §15).

Dependencies

  • chapulin is pinned at 10a5bc8, where 0.5.0 pinned 0adcf33. It brings the default order above, a client's own order, and a version argument on each QUIC packet call, which colibri fills in.
  • stdx is pinned at c134f2c, where 0.5.0 pinned 8c2a3b8 (a5bfb50). Its DEFLATE encoder, which the server's content codings run, writes smaller output, and runs level 1 at 0.91 and 0.93 of its old speed (stdx's decision 36). Its brotli command loop runs without Zig's runtime checks, and colibri never runs brotli.

Fixed

  • client and server: after a record failed, should_close said to close before send wrote the bad_record_mac alert the connection owes (RFC 9846 §5.2), so a caller that asked before its next send closed without it. Each now waits for the alert (4ed2108). The interop scripts check it with a record no key sealed (628449b, #74).

Checked against

CI ran tools/ci.sh on 830af4c, on Ubuntu 24.04 with an AMD EPYC 7763, and every section passed:

  • TLS against Go 1.27.1: colibri's server selected TLS_AES_256_GCM_SHA384 (suite=0x1302), and its client ran TLS_AES_128_GCM_SHA256 (suite=0x1301), the suite Go's server chose. On 0.5.0 both ran ChaCha20 (0x1303).
  • h2spec 2.6.0: 144 passed in cleartext and over TLS, and 2 skipped by name, which test an RFC 7540 rule RFC 9113 dropped. h3spec 0.1.13: 49 examples, 0 failures.
  • The client and the server over h11 and h2 against Go 1.27.1, nghttpd 1.52.0, h2o 2.2.5, curl 7.88.1 and nghttp, in cleartext and over TLS, with gzip content codings in both directions and the scripts' forged-record checks.
  • The client's Channel fetched over h3 from aioquic 1.3.0 and quic-go 9d085cc, and over h2 from Go's server with no UDP.
  • aioquic in both directions over hq-interop and h3, QPACK against ls-qpack, the TLA+ models with the h2, h3 and client traces, the Lean proofs, and 2,337 tests in Debug and in ReleaseSafe. The tests also passed on Linux and macOS arm64.

The HTTP Garden and the QUIC Interop Runner ran on b5a7c79, which adds documentation alone to 830af4c. The Garden fed its 72 streams to colibri's server and to 35 other origins, and compared every one. In the runner, colibri passed every case against quic-go, ngtcp2, neqo, quinn and itself, as the server and as the client, but two:

  • chacha20, wherever colibri was the client. The case requires the client to offer ChaCha20 alone, and the test endpoint still offered its object's order, now AES-256-GCM first. 0af377e on main gives the endpoint a chacha20 option.
  • handshakeloss with quinn's client, which also failed at 9796e95 and passed at bb9263e.

The runner ran again at 0af377e, main with that fix and the QUIC version 2 work that follows this release, and colibri passed every case, chacha20 and quinn's handshakeloss included.

quic-go runs no ecn case. Every qlog file colibri wrote, in 168 test cases, passed tools/qlog_check.py.

docs/design.md §8 records each check: what ran, on what machine, and what it printed.

Install

zig fetch --save git+https://github.com/c4milo/colibri#v0.6.0