Skip to content

Release v0.6.0: post-quantum hybrid key exchange on the UCI products - #316

Merged
JC-000 merged 7 commits into
masterfrom
release/v0.6.0-pq
Oct 18, 2026
Merged

JC-000 merged 7 commits into
masterfrom
release/v0.6.0-pq

Conversation

@JC-000

@JC-000 JC-000 commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Stacked on #315 (base = feat/pqc-p5-hybrid-keyschedule; retarget to master after #315 merges). Not tagged, no GitHub release created. Release notes draft: docs/release-notes-v0.6.0.md (the evidence below is in it too).

What changes

  • tools/package/_common.sh: PQ_HYBRID=1 in uci-onchip and uci-comb; guidance text; build_prgs.sh, write_manifest.sh (incl. the demo-only/not-secure note next to the hybrid, and the corrected block arithmetic: 186+186+202 = 574 fits one disk, the split is deliberate), README (version, product table, make args, architecture/key-exchange/memory-map/checklist/related-projects now describe 11EC then 001D, c64-mlkem, the PQ memory layout; a plain "what the hybrid protects" paragraph: key exchange only, authentication still classical P-256 + TOFU, weak-DRBG caveat, no "quantum-safe" wording).
  • tools/package/write_release_sums.sh: generates the top-level SHA256SUMS from dist/ + the M3 assets (refuses a missing file, verifies its own output).
  • tools/uci/rig_https_banner.py + tools/http_body_checks.py: REQUIRE_KEX is now part of decide_exit (new check_kex_group verdict, parse_require_kex validated before the device is touched, the report line and the final verdict agree, an unreadable group is a FAIL not a skip, a failed tls_kex_group read is non-fatal until then); hardware-free red cases in test_http_body_checks_unit.py (21/21); tools/mutate_http_body_checks.py 95/95 caught (5 new mutants, 2 anchors updated). The boot-wait message says "comb boot precompute" only on comb images. rig_https_local.py: PQ_DUMP tolerates classic images; diag prints.
  • CLAUDE.md: End-to-end status no longer says "Do not treat any large-body fetch as complete until http_get reports success (carry=0, HTTP 200) on a truncated body — silent data loss #211 closes" (http_get reports success (carry=0, HTTP 200) on a truncated body — silent data loss #211 is closed, PR fix(uci): wait for the reply to be valid, not for CMD_BUSY to clear (#230 b, #211) #238, 2026-09-24); packaging list shows PQ_HYBRID=1 and that no classic UCI image ships.
  • ip65 stays classic.

Release PRGs (make package rc=0; make package-verify 14/14 RELEASE ARTIFACTS VERIFIED; test_package_verify 41/41, test_trust_release_guard 24/24, bare pytest 511 passed)

image make args (+ TRUST_RELEASE=1) bytes sha256
ip65-onchip BACKEND=ip65 USE_NISTCURVES_ONCHIP=1 47,105 e69bfce7b552e765df02cb253b4ff12cecaca3e978669376b7af5ffad9343dce
uci-onchip BACKEND=uci USE_NISTCURVES_ONCHIP=1 TRUST_STORE=1 PQ_HYBRID=1 47,105 3b4dd718cbdc296f2083c90ce4d89a25e1fbd032e849c66f25227ef4d0ac4d5a
uci-comb BACKEND=uci USE_NISTCURVES_ONCHIP_COMB=1 TRUST_STORE=1 PQ_HYBRID=1 51,201 41202de9fa6b9da769fce87a3223e7d1c74630f873b4b64a940afacddb7cbeb6
disk sha256
ip65-onchip.d64 30c5ba3974eed49a248e0041c1bf1e3e2e9584d772c710b086f50dc18a840e38
uci-onchip.d64 70356cd1a7334c009ac37641d6cc7f4954550fa9d530e0ab913dda7a2b5d2302
uci-comb.d64 293883c77a30364674425a17b3121a1adab08d21d4e2f7f8c72362e494916faa
m3-demo.d64 (carried from v0.5.1) ae2cc5b7d40190ddfc911cb40e7a7d62e406c134f304b3fe9e2f04ea540ff498

write_release_sums.sh over dist/ + the v0.5.1 M3 assets: 13 assets verified. Margins at the release builds: uci-onchip LOADER 157 / NET_CODE 105 / CRYPTO_OVERLAY 372 / CRYPTO_HOT 196; uci-comb LOADER 342 / NET_CODE 134 / CRYPTO_OVERLAY 55 / CRYPTO_HOT 379.

ip65-onchip is NOT the v0.5.1 binary (disclosed in the notes)

v0.5.1 810f1158… -> 1c377d34… at #311 (link order) -> e69bfce7… from #312 (rebuilt at ded2247, f08ddd9, 7b1ed07, 10ccf25, 4e45a9b, same release args). #312 added the HRR check to the ip65 ServerHello parser and changed the shared ClientHello builder. VICE e2e PASS (2026-10-18): tests/rig_vice_https_macos.py, E2E_PROFILE=onchip E2E_NO_WARP=1, patched pcap VICE 3.10 x64sc, feth/pcap rig, honest 1 MHz: handshake + GET in 2,328 s wall, http_status=200, body TLS13 OK FROM C64 TEST (22 B). Image: make BACKEND=ip65 USE_NISTCURVES_ONCHIP=1 TRUST_RELEASE=1 HTTPS_PORT=24530 = 9b1ef4b1582242ef0820a33499c60f1f6a69db803f45de6431451fe34a4c7463, which differs from the release PRG e69bfce7… in exactly 4 bytes (the two HTTPS_PORT words, 443 vs 24530, each stored twice). Phases after G: TCP connected +3 s, CH +475 s, SH +931 s, PROC +950 s, last flight record +2,266 s, Finished +2,269 s, request +2,287 s, closed +2,293 s. Not covered: real RR-Net silicon, name validation (none on ip65), speeds above 1 MHz. The 3 PRG + 3 d64 release hashes are unchanged (e69bfce7…, 3b4dd718…, 41202de9…; 30c5ba39…, 70356cd1…, 293883c7…).

Cost of PQ, measured properly (U64E 48 MHz, local OpenSSL listener, release args + HTTPS_SNI, SYS trigger to client Finished, 0.5 s poll, n=3)

The earlier "+1.7 s" compared hybrid vs X25519 on the SAME PQ image (both pay KeyGen); withdrawn everywhere.

image classic (PQ_HYBRID off) hybrid diff
uci-onchip 52.4, 52.1, 52.6 s 55.2, 54.8, 54.6 s +2.5 s (+4.8%)
uci-comb 41.0, 41.3, 40.6 s 43.9, 43.1, 43.4 s +2.5 s (+6.1%)
(The classic runs ended in a rig traceback after the fetch because PQ_DUMP read PQ-only labels, since fixed; CFIN and the sentinel were reached.)

Wikipedia on hardware, exact release PRGs (rig_https_banner.py, REQUIRE_KEX=EC, real /USB1/TRUST.A|B, no key-change prompt)

# image kex HTTP body elapsed
1-3 uci-comb 41202de9 $EC 200 760,609 B == CL PASS (x3) 220 / 222 / 222 s
4-5 uci-onchip 3b4dd718, device REU Enabled $EC 200 PASS (x2) 191 / 192 s
6 uci-onchip 3b4dd718, device REU Disabled (set_reu(False), restored to Enabled/16 MB afterwards) $EC 200 PASS 190 s
control BACKEND=uci USE_NISTCURVES_ONCHIP_COMB=1 TRUST_STORE=1 TRUST_RELEASE=1 on this tree, PQ_HYBRID off (fee4d5b1) n/a 200 PASS 218 s
fallback uci-comb, github.com /robots.txt $1D 200 14,489 B PASS 101 s
8/8 completed. #211 was closed 2026-09-24 (PR #238): these are evidence for that fix on the PQ images, not a statement that it is intermittent now.

Other validation

  • boot_check.py PASS on both release PRGs. rig_https_local.py vs OpenSSL 3.6.5 (release args + HTTPS_SNI=www.foo.invalid, since the rig dials an IP): onchip and comb, -groups X25519MLKEM768 PASS ($EC, oracle MATCH), -groups X25519 PASS ($1D).
  • Trust store on the PQ images (ring now also hosts the ML-KEM dk scratch), release args + TRUST_STORE_DIR=/USB1/c64https-test HTTPS_PORT=24520 (onchip a0d26e3d…, comb e8adb55d…): rig_trust_store.py exit 0 on both (24 / 25 checks, 0 failures); rig_trust_policy.py (first-use record, matching key, changed-key prompt, accept-on-retry, UNPINNED override) exit 0 on both, 30 passed, 0 failed each. Real /USB1/TRUST.A|B (80 B / 144 B) unchanged afterwards. The policy rig's Python listener can only pick X25519 (0x001D); live mode needs the REU demo (does not link with PQ), not run.
  • BACKEND=uci run_all_tests.py 455/455 with 2 accounted skips — that aggregate is the non-PQ REU profile; no VICE suite runs the release images (covered by package-verify's boot, the hardware runs, and the PQ suites).
  • REU viewer demo on PQ comb: does not link (CRYPTO_HOT overflows by 507 B); documented.
  • M3 demo: carried forward from v0.5.1 unchanged (rebuild reproduces 916d4162… byte for byte; build_m3_demo.sh not used, no provenance re-written).

Review round 2 nits (docs/tooling only; the 3 PRG and 3 .d64 hashes above rebuilt identical)

CLAUDE.md live-server wording (onchip has Wikipedia runs); README diagram border (box restored, a sentence under it names ML-KEM-768); MANIFEST has one demo-only/not-secure disclaimer at the top; the notes' cost table states the differing server -groups; write_release_sums.sh header matches the code, also verifies the M3 PRG/.d64/README against the M3 checksum file (a corrupted M3 PRG makes it exit 1), and is mode 100755; notes intro lists #303 and #310 once; pq_keylog_check.py prints n/a instead of $: on classic images (new test, 12/12); the banner rig comment says REQUIRE_KEX is a decide_exit verdict. The notes also disclose that first-use / changed-key trust paths ran on the PQ images with X25519 fetches only (KeyGen and the dk in the ring still ran); with 11EC only the known-key path has run. Bare pytest 512 passed, test_package_verify 41/41.

Review round 1 items addressed

SF1 README PQ sections; SF2 cost re-measured; SF3 "nothing differs" removed, classic UCI build instructions added; SF4 what PQ protects; SF5 ip65 disclosure; SF6 trust store rigs; SF7 d64 hashes, full asset list, rename-README instruction, SHA256SUMS script; SF8 REQUIRE_KEX in decide_exit; SF9 #211 closed; SF10 C64U findings carried forward. NITs: block-count text, demo-only note, control relabelled, #311-#315 listed, single-record SH symptom (tls_sh_err=$0E, not on screen) and Cloudflare/BoringSSL out of scope, CLAUDE.md packaging list, REU-disabled onchip run, non-fatal kex read, boot-message fix.

🤖 Generated with Claude Code

JC-000 and others added 6 commits October 17, 2026 21:54
…est text, README)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…e notes draft

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…UIRE_KEX in decide_exit, SHA256SUMS script

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…PQ_DUMP tolerates classic images

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…, sums script checks M3 files and is executable, kex message, notes)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@JC-000
JC-000 marked this pull request as ready for review October 18, 2026 05:06
Base automatically changed from feat/pqc-p5-hybrid-keyschedule to master October 18, 2026 12:22
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@JC-000
JC-000 merged commit 569274c into master Oct 18, 2026
@JC-000
JC-000 deleted the release/v0.6.0-pq branch October 18, 2026 13:21
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