Repository navigation
feat(port): HTTPS over the lwIP adapter with mbedTLS 3.6 (#447 Phase 4) - #457
Merged
Merged
Conversation
lwIP's altcp_tls_mbedtls only supports mbedTLS 2.x (in 2.2.1 and on
master), so the adapter drives mbedTLS 3.6 itself.
- cwist_lwip_tls_create()/destroy() build a server context (certificate
chain, key, CTR_DRBG, ssl config); cwist_lwip_config.tls turns HTTPS on
for a listener. TLS code compiles with -DCWIST_LWIP_TLS; without it,
create returns NULL and a listener asked for TLS fails instead of
serving plaintext.
- The DRBG is seeded only from cwist_port_entropy(): a failing hook makes
create fail, and a failing reseed fails the handshake.
- Per connection: an mbedtls_ssl_context with BIOs over the pcb. Received
ciphertext queues (bounded, refused back to lwIP when full) and is
decrypted into the same rx/framing path as plaintext; responses go
through mbedtls_ssl_write and are copied into lwIP, so tx is freed as
soon as mbedTLS has taken it. The next pipelined request is picked up
from tcp_sent, as on the plaintext path. 100 Continue goes through the
record layer. Connection: close sends close_notify. A failed handshake
aborts the connection.
- tests/lwip/cwist_test_mbedtls_config.h: TLS 1.2 server profile
(ECDHE-ECDSA P-256, AES-128-GCM, SHA-256; 16 KB in / 4 KB out records).
tests/lwip/tls_client.{c,h}: mbedTLS client over a loopback pcb.
tests/lwip/test_tls_cert.h: test-only self-signed P-256 credentials.
- tests/test_lwip_tls.c (make test): verified handshake, keep-alive and
pipelining in one session, 100-continue with a split body, 40 KB
response, 413, close_notify, plaintext client on the TLS port, and
context creation failures. Under CWIST_TLS=mbedtls it links the
vendored mbedTLS libcwist already uses.
- tests/baremetal/hello_tls.c joins baremetal-smoke and the size gate:
Cortex-M7 flash 137 KB, heap high-water 106.6 KB with both sessions,
~50 KB held by server + client sessions after the handshake.
Member
Author
|
Full native |
gg582
added a commit
that referenced
this pull request
Oct 27, 2026
…458) * ci(baremetal): Cortex-M7 smoke and size budget gate (#447 Phase 5) (#455) - .github/workflows/baremetal.yml (ubuntu-24.04: gcc-arm-none-eabi 13.2, newlib 4.4, qemu-user 8.2) runs `make baremetal-size-gate` on every PR and push: the three smoke firmware (in-memory dispatch with and without an RTC, and the lwIP adapter over loopback) under qemu-arm, then the budget check. ELFs, maps and logs are uploaded as an artifact. - baremetal-smoke keeps each run's output in .baremetal-build/<fw>.log and still fails on the first failing firmware. - scripts/ci/baremetal_size_gate.py measures flash (code, rodata, exidx, init arrays, .data load image), static RAM (.data + .bss, not the linker heap region) and the heap/stack high-water marks the firmware report, against scripts/ci/baremetal-budget.txt. A budgeted value the run did not report fails rather than passing. - Budgets sit about 10% above values measured with both gcc 13.2 (the CI toolchain) and 14.2, which agree within 0.6%. (cherry picked from commit 35ed9a0) * feat(port): HTTPS over the lwIP adapter with mbedTLS 3.6 (#447 Phase 4) (#457) lwIP's altcp_tls_mbedtls only supports mbedTLS 2.x (in 2.2.1 and on master), so the adapter drives mbedTLS 3.6 itself. - cwist_lwip_tls_create()/destroy() build a server context (certificate chain, key, CTR_DRBG, ssl config); cwist_lwip_config.tls turns HTTPS on for a listener. TLS code compiles with -DCWIST_LWIP_TLS; without it, create returns NULL and a listener asked for TLS fails instead of serving plaintext. - The DRBG is seeded only from cwist_port_entropy(): a failing hook makes create fail, and a failing reseed fails the handshake. - Per connection: an mbedtls_ssl_context with BIOs over the pcb. Received ciphertext queues (bounded, refused back to lwIP when full) and is decrypted into the same rx/framing path as plaintext; responses go through mbedtls_ssl_write and are copied into lwIP, so tx is freed as soon as mbedTLS has taken it. The next pipelined request is picked up from tcp_sent, as on the plaintext path. 100 Continue goes through the record layer. Connection: close sends close_notify. A failed handshake aborts the connection. - tests/lwip/cwist_test_mbedtls_config.h: TLS 1.2 server profile (ECDHE-ECDSA P-256, AES-128-GCM, SHA-256; 16 KB in / 4 KB out records). tests/lwip/tls_client.{c,h}: mbedTLS client over a loopback pcb. tests/lwip/test_tls_cert.h: test-only self-signed P-256 credentials. - tests/test_lwip_tls.c (make test): verified handshake, keep-alive and pipelining in one session, 100-continue with a split body, 40 KB response, 413, close_notify, plaintext client on the TLS port, and context creation failures. Under CWIST_TLS=mbedtls it links the vendored mbedTLS libcwist already uses. - tests/baremetal/hello_tls.c joins baremetal-smoke and the size gate: Cortex-M7 flash 137 KB, heap high-water 106.6 KB with both sessions, ~50 KB held by server + client sessions after the handshake. (cherry picked from commit eb537f9)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 4 of the bare-metal STM32 epic (#447): HTTPS on the lwIP adapter.
Why not lwIP's
altcp_tlslwIP's
altcp_tls_mbedtlsincludes mbedTLS 2.x-only headers (mbedtls/certs.h,mbedtls/ssl_internal.h). That holds in 2.2.1 and on current master, so it cannot use the vendored mbedTLS 3.6 LTS (lib/mbedtls). The adapter therefore drives mbedTLS 3.6 itself over the pcb, usingmbedtls_ssl_set_bio.API (
include/cwist/sys/port/lwip.h)-DCWIST_LWIP_TLSand mbedTLS 3.6 linked: a TLS 1.2 server, X.509, PK parse, and CTR_DRBG.CWIST_LWIP_TLS:cwist_lwip_tls_create()returns NULL, andcwist_lwip_listen()withcfg.tlsset fails instead of serving plaintext.cwist_port_entropy(). A failing hook makescreatefail. A failing reseed later fails the handshake, so nothing runs on a guessable seed.create: the certificate and key are parsed and checked to be a pair withmbedtls_pk_check_pair.How it works
tcp_recvgoes into a bounded per-connection queue (one full record plus slack). When the queue is full it is refused back to lwIP, so the TCP window throttles the peer. A pump runs the handshake, then decrypts into the samerxbuffer and framing path the plaintext server uses. All request limits, 400/413/431/501 handling, keep-alive and pipelining behave the same.mbedtls_ssl_write. Its records are copied into lwIP (TCP_WRITE_FLAG_COPY), so the serialized response is freed as soon as mbedTLS has taken all of it.WANT_WRITEis retried fromtcp_sentandtcp_poll.tcp_sent, as on the plaintext path, never recursively. That keeps stack use flat however many requests are queued.Connection: closesendsclose_notifybefore the TCP close. A failed handshake (not TLS, no shared suite, alert) aborts the connection.#ifdef CWIST_LWIP_TLS.Tests
tests/lwip/cwist_test_mbedtls_config.his a small TLS 1.2 server profile:tests/lwip/tls_client.{c,h}is an mbedTLS client over a loopback pcb. It verifies the server against the test CA forlocalhost.tests/lwip/test_tls_cert.hholds test-only self-signed P-256 credentials.test_lwip_tls(inmake test, host):Expect: 100-continuewith the body split across recordsConnection: close→close_notify→ FINI confirmed the assertions are live by changing one expectation and watching it fail. The test is also clean under ASan/UBSan/LSan.
Under
CWIST_TLS=mbedtls,libcwistalready links the vendored mbedTLS, so the test reuses that copy (default config) instead of linking a second one.hello-tlsjoinsmake baremetal-smokeand the size gate: the same stack on Cortex-M7 under qemu-arm. It passes with gcc 13.2 / newlib 4.4 / qemu 8.2 (the CI toolchain) and with gcc 14.2 / qemu 10.lwipopts.hpools and test buffers)After the handshake, server and client sessions together hold 49,980 bytes of heap. Both use the same record buffers (16 KB + 4 KB each), which puts a server-side TLS connection at about 25 KB.
MBEDTLS_SSL_IN_CONTENT_LENstays at 16 KB because clients may send full-size records: browsers do not negotiatemax_fragment_length.Local verification
test_lwip_tls,test_lwip_adapterandtest_baremetal_portpass.make baremetal-smokeandmake baremetal-size-gatepass, with both toolchains.CWIST_LWIP_TLS, under-Wall -Wextra: no warnings.check_api_baseline: 2 functions and 1 type addedcheck_api_test_coverage,check_test_wiring,check_env_knobs,check_inline_exports,malloc_lint.sh: OKmake -k test: still running locally at the time of opening; result will follow in a comment.Not in this PR
MBEDTLS_*_ALTchoice. To be documented in Phase 6.Refs #447