Skip to content

rig_https_local.py has failed on every UCI image since #135: it sends the listener's IP as the hostname, which the SAN check rejects #141

Closed

Description

@JC-000

Symptom

tools/uci/rig_https_local.py (U64E, uci-onchip, 48 MHz) fails with the client aborting at the Certificate stage and the listener seeing SSLEOFError: UNEXPECTED_EOF_WHILE_READING. Reproduced 3/3 on 2026-08-24 — twice on a #70 branch and once as a control on master ab5de2b (PRG 1af096e1…), identical state each time:

tls_state              = $FF
tls_last_state         = $04   (CERTIFICATE)
tls_recv_sub_progress  = $31   (tls_deframe.s:271 — in-place dispatch into the cert handler)
tls_read_seq           = 2
net_last_error         = $00
listener: client_addr seen, request <none>, TLS handshake failed: UNEXPECTED_EOF

boot_check.py and phase2_check.py pass on the same device in the same session, and rig_https_live.py (github.com) is the control for the TLS path itself.

Cause

rig_https_local.py:1408:

host_str = host_ip_bytes + b"\x00"

The rig DMA-feeds the listener's dotted-quad IP as the hostname (UCI firmware resolves it, so it connects), and that same string is what tls_hostname carries into the v0.4.2 server-name validation (#135, 0673acb). The bundled certificate is:

tools/https_e2e/certs/server.pem   SAN: DNS:foo.bar, DNS:www.foo.bar   CN=www.foo.bar

An IP literal never matches a dNSName, and no-SAN-match is a reject — which is the correct behaviour of the check, and exactly why the rig now fails. The #135 hardware verification was done with rig_https_wiki.py (a real host, matching SAN), so this rig was never re-run after the check went live.

Only the UCI images are affected (X509_VERIFY_NAME is compiled out on ip65), which is the whole set this rig runs on.

Fix options (not done here — filed from the #70 alignment, which it blocks from using this rig as evidence)

  1. Rig-side, preferred: send a hostname the cert names and let the U64E resolve it — e.g. build the test PRG with HTTPS_SNI=www.foo.bar (the knob added for exactly the connect-host ≠ name-check-host case) while the DMA'd host_str stays the IP; or point the U64E's resolver at www.foo.bar via the dnsmasq the VICE rig already uses.
  2. Cert-side: add IP:<listener ip> to the SAN — but the parser is dNSName-only by design (RFC 6125), so that would mean extending src/x509_name.s, and the negative vectors in tools/test_x509_name.py would need an IP case.
  3. Same audit for rig_https_print_body.py / rig_https_local_p384.py / rig_https_bad_finished.py, which share the host-string convention.

Until one of these lands, a rig_https_local FAIL on a UCI image is not evidence about the change under test.

Activity

  1. JC-000 commented on Aug 30, 2026

    @JC-000
    OwnerAuthor

    Hardware confirmation of the diagnosis, and fix option 1 verified working — U64E 601A96, quiet device, fw 3.15 preview, uci onchip @ 48 MHz, 2026-08-30 (taken while verifying #149).

    Default mode reproduces this issue exactly as filed:

    tls_state             = $FF
    tls_last_state        = $04
    tls_recv_sub_progress = $31
    net_last_error        = $00
    listener: request <none>, TLS handshake failed: UNEXPECTED_EOF
    

    Two controls isolate the name check as the sole cause:

    build result
    X509_VERIFY_NAME compiled out PASS — HELLO FROM TLS SERVER, carry=0
    name check on + make clean && make BACKEND=uci USE_NISTCURVES_ONCHIP=1 HTTPS_SNI=www.foo.bar PASS — same

    So the cause is exactly as diagnosed, and fix option 1 works: the DMA'd host_str stays the listener IP (firmware still resolves and connects), while http_get's HTTPS_SNI_OVERRIDE prologue puts www.foo.bar into tls_hostname, which matches the bundled cert's DNS:www.foo.bar. The cert is valid to 2036, so no cert work is needed.

    One trap for whoever implements this, which cost a false negative here first:

    HTTPS_SNI= requires make clean. It is not in #128's no-clean-needed set. The generated build/https_host.inc content-compare invalidates boot.o + the PRG, but HTTPS_SNI= also adds -D HTTPS_SNI_OVERRIDE=1, which changes http.s. That is a flag change, so http.o goes stale. Without make clean you get a mixed link that embeds the SNI string via boot.o but never runs the override in http.o — the PRG hash changes, the string greps out of the image, and it still fails identically to no-SNI. The clean and dirty PRGs differ (a65fff60… vs d1b63508…); that hash difference is the only thing that gives it away.

    Worth folding into the fix: if the rig grows a HTTPS_SNI build step it should force the clean, and the same applies to rig_https_print_body.py / rig_https_local_p384.py / rig_https_bad_finished.py per option 3.

  2. added a commit that references this issue on Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions