Skip to content

Let HTTPS servers on the board accept normal clients - #11677

Merged
tannewt merged 2 commits into
adafruit:mainfrom
mikeysklar:ssl-server-no-client-cert
Oct 28, 2026
Merged

tannewt merged 2 commits into
adafruit:mainfrom
mikeysklar:ssl-server-no-client-cert

Conversation

@mikeysklar

@mikeysklar mikeysklar commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

What

Board HTTPS servers stop asking visitors for a certificate.

Why

Every normal visit failed, so basic board HTTPS servers did not work.

This series is @tyeth's work, integrated with his permission, to bring Bluetooth to the Pico W and Pico 2 W on zephyr-cp.

# Series PR Order
1 HTTPS servers accept normal clients this PR
2 Zephyr drivers: CYW43 Bluetooth, access point, USB (adafruit/zephyr) next
3 WiFi sockets, HTTPS and access point after 2
4 Bluetooth for Pico W and Pico 2 W after 3
5 Report USB drive errors any time
6 Version fallback when CP_VERSION is empty any time
7 Build tooling: version refresh, RTT console any time
Before After
Server with only its own certificate asks client for a certificate, handshake fails asks for none, like CPython's CERT_NONE server default
Server that called load_verify_locations() verifies the client unchanged
Client sockets verify the server unchanged
Tested on Pico 2 W and Metro ESP32-S3: before fails, after serves 3/3
Ubuntu host, curl -k, no client cert curl Board
Pico 2 W, raspberrypi port, main timeout OSError -29824 (MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE)
Pico 2 W, raspberrypi port, tyeth's commit pico! SERVED
Pico 2 W, zephyr-cp, both commits pico! 3/3 SERVED 3/3
Metro ESP32-S3, main timeout OSError -29824
Metro ESP32-S3, both commits pico! 3/3 SERVED 3/3
import wifi, socketpool, ssl
pool = socketpool.SocketPool(wifi.radio)
ctx = ssl.create_default_context()
ctx.load_cert_chain("/cert.pem", "/key.pem")
s = pool.socket(pool.AF_INET, pool.SOCK_STREAM)
s.setsockopt(pool.SOL_SOCKET, pool.SO_REUSEADDR, 1)
s.bind(("0.0.0.0", 4443))
s.listen(1)
ss = ctx.wrap_socket(s, server_side=True)
c, a = ss.accept()
c.settimeout(None)
c.recv_into(bytearray(256))
c.send(b"HTTP/1.0 200 OK\r\nContent-Length: 6\r\n\r\npico!\n")
c.close()
print("SERVED", a)

Claude Code helped review, test and write this.

I reviewed the change and the results myself.

🤖 Generated with Claude Code

tyeth and others added 2 commits October 27, 2026 12:10
wrap_socket(server_side=True) configured mbedTLS with VERIFY_REQUIRED
whenever the context had the default root bundle attached, which every
ssl.SSLContext() does. On a server that means "require a client
certificate", so a plain HTTPS server -- load_cert_chain() and nothing else
-- failed every handshake with MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE
(-0x7480) and the client saw a timeout.

Default server-side contexts to VERIFY_NONE, as CPython's do (CERT_NONE for
servers); a context that loaded its own CA with load_verify_locations() still
verifies clients. Client-side behaviour is unchanged.

Seen on a Pico 2 W serving a captive portal over its softAP; the same code
runs on every port using the shared mbedTLS module.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A context holds either the root bundle or its own CA, never both:
set_default_verify_paths() and load_verify_locations() clear each
other. Skipping the bundle on the server side therefore gives the same
result as the separate server branch: no client verification unless a
CA was loaded.

Fold the check into the bundle branch and shorten the comment. The
reasoning stays in the previous commit message.

No behavior change.

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

@tannewt tannewt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@tannewt
tannewt merged commit c71f4dd into adafruit:main Oct 28, 2026
316 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.

3 participants