Skip to content

Phase 28 ssl plan

Simon Dick edited this page Jun 20, 2026 · 5 revisions

Phase 28 — TLS/SSL via AmiSSL v5

Part of the Amiga port design log.

Landed in six commits, end-to-end verified against www.python.org on 2026-05-31 with CERT_REQUIRED chain validation (HTTP/1.1 200 OK, 53 912 B HTML). This page holds both the design and rationale — what and why — and the step-by-step ship plan — how it shipped: the per-step deliverables, measured heap costs, the AmigaOS capath trailing-slash gotcha, and the modern-CDN TLS 1.3 limitation tracked as a follow-up.

Upstream MicroPython ships axTLS (small, dated, no SNI) and mbedTLS (~250 KB add). Neither fits the AmigaOS ethos when the platform already has a perfectly serviceable TLS implementation — AmiSSL — as a shared library. TLS code stays on the user's machine; we ship only the thin wrapper.

AmiSSL v5 is OpenSSL 3.x: TLS 1.3, ChaCha20-Poly1305, simpler TLS_client_method(). v4 (OpenSSL 1.1.x) is EOL upstream and not worth a deprecated-API codepath.

Usage looks like the upstream ssl module:

ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.load_verify_locations(capath="AmiSSL:certs/")
ws = ctx.wrap_socket(s, server_hostname="www.example.com")

Phase 28 lands behind a build flag (MICROPY_PY_AMIGA_SSL, defaulting to ON for the variants that already include MICROPY_PY_AMIGA_SOCKET), so partial progress doesn't disturb the variants that are currently green.


Design and rationale

How AmiSSL is opened

Unlike bsdsocket.library, AmiSSL has a master-indirection step. v5 has a unified OpenAmiSSLTags() replacing the legacy v3/v4 triple:

AmiSSLMasterBase = OpenLibrary("amisslmaster.library",
                               AMISSLMASTER_MIN_VERSION);
OpenAmiSSLTags(AMISSL_CURRENT_VERSION,                  // positional
               AmiSSL_UsesOpenSSLStructs, FALSE,        // opaque-only
               AmiSSL_GetAmiSSLBase,      &AmiSSLBase,
               AmiSSL_GetAmiSSLExtBase,   &AmiSSLExtBase,  // OS3 split
               AmiSSL_SocketBase,         (ULONG)SocketBase,
               AmiSSL_ErrNoPtr,           (ULONG)&errno,
               TAG_DONE);
  • AMISSL_CURRENT_VERSION is positional, not a tag. Compiling against the v5 SDK produces a binary that requires AmiSSL v5 at runtime — that's the point of the master indirection.
  • AmiSSL_UsesOpenSSLStructs = FALSE — MicroPython treats SSL_CTX * / SSL * / X509 * as opaque pointers.
  • AmiSSLExtBase is OS3-only: v5's API surface is large enough they split it across two bases on m68k.
  • SocketBase is supplied in the same call — amiga_ssl_open() must run after amiga_socket_open().

Link against -lamisslstubs (function stubs needed for callbacks like SSL_CTX_set_verify(..., X509_free)); don't use -lamisslauto (its constructor-magic open fires before SocketBase is valid).

Headers: straight #include <openssl/...>, same as desktop.

Callback ABI

OS3 callbacks under AmiSSL take args on the stack (not registers) and need STDARGS SAVEDS annotations:

STDARGS SAVEDS static int amiga_ssl_verify_cb(int preverify_ok,
                                              X509_STORE_CTX *ctx) { ... }

bebbo gcc has __attribute__((stkparm)) and __saveds. Worth a AMISSL_CB macro in amiga_ssl.h.

Module shape

ports/amiga/modssl.c follows the upstream ssl API:

ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.load_verify_locations(capath="AmiSSL:certs/")
ws = ctx.wrap_socket(s, server_hostname="www.example.com")

SSLContext wraps SSL_CTX *; SSLSocket wraps SSL * + fd and implements the stream protocol so it slots into readline()/with. Per socket: SSL_newSSL_set_fdSSL_set_tlsext_host_name (SNI) → SSL_connectSSL_read/writeSSL_shutdown + SSL_free. Transient SSL_ERROR_WANT_READ/WRITEMP_EAGAIN; terminal errors raise OSError with ERR_error_string text.

Cert trust

Two paths:

  1. SSL_CTX_set_default_verify_paths() picks up the AmiSSL:certs/ c_rehash dir (the hashed CAs the AmiSSL installer drops there).
  2. ctx.load_verify_locations(cafile=...) / cadata=... for custom roots.

Don't ship a CA bundle in the binary — ~200 KB, gets stale fast.

Variant gating + memory

#define MICROPY_PY_AMIGA_SSL (MICROPY_PY_AMIGA_SOCKET)

All three shipped variants link AmiSSL. If amisslmaster.library isn't installed at runtime, import ssl raises ImportError.

OpenSSL 3.x is memory-hungry: SSL_CTX ~16 KB, each SSL ~48 KB, CA store ~200 KB. Two HTTPS connections push the 256 KB standard heap to the edge — but AmiSSL allocates from system memory (MEMF_ANY), not MicroPython's GC heap, so -X heap= doesn't help; the lever is total system Fast RAM. (The measured numbers from the implemented port are in Step 5 below.)

Deliberately not doing

  • Server-side TLS — straightforward, but the Amiga isn't a webserver host.
  • OpenSSL config-file integration — AmiSSL ships sane defaults.
  • Asyncio-friendly handshake — MICROPY_PY_ASYNCIO (0); revisit when asyncio lights up.
  • Touching upstream extmod/modssl_*.c — AmiSSL-only, port-side module.

SDK provisioning (CI + container build)

stefanreinauer/amiga-gcc:latest ships only vbcc-side AmiSSL stubs (proto/amisslmaster.h, proto/amissl.h, inline/amissl_protos.h under …/vbcc/…). The GCC tree has no <openssl/*> headers, no <libraries/amissl[master].h>, and no libamisslstubs.a/ libamisslauto.a. Phase 28 needs the AmiSSL v5 SDK layered in. Three options:

  1. Extend the container image. Custom Dockerfile FROM stefanreinauer/amiga-gcc:latest that fetches the SDK from https://github.com/jens-maus/amissl/releases, unpacks headers into /opt/amiga/m68k-amigaos/include/ and libs into /opt/amiga/m68k-amigaos/lib/. Push to GHCR; CI workflow and tools/amiga-build.sh both point at it. Cleanest, one-time setup.
  2. Workflow step. Download + unpack the SDK on every CI run. Simpler setup, but adds 10–30 s per run and a network dependency on GitHub releases.
  3. Vendored in-tree. Drop the headers + stubs under ports/amiga/amissl-sdk/. No external deps; downside is shipping a few MB of third-party code in the repo.

Option 2 (workflow step + local script) was the route taken — vendor nothing; rely on the upstream release artefact. The mechanics are in Step 1 below.

Open items

  • AmiSSL 4 fallback. Land v5-only initially.
  • Cert pinning helpers (set_servername_callback, raw-public-key APIs) — punt to a follow-up.
  • Async-friendly handshake — tied to asyncio gating.
  • urequests / mip — pure-Python, freeze into variant manifests once ssl lands. (Phase 29 picks up urequests.)

Implementation / step plan

This is the step-by-step ship plan — how the work was chunked into landable, individually-testable PRs.

Phasing overview

Step 1: SDK in build env  →  Step 2: library lifecycle  →  Step 3: SSLContext
                                       │                          │
                                       ↓                          ↓
                                Step 5: variants/heap    Step 4: SSLSocket
                                       └─────────┬────────────────┘
                                                 ↓
                                       Step 6: docs + tests
# Step Output On-target smoke test
1 ✅ SDK fetched on every build Headers <openssl/*> + libamisslstubs.a reachable from m68k-amigaos-gcc inside the bebbo container 1-line C link test inside the container
2 ✅ ports/amiga/amiga_ssl.{c,h} — library lifecycle OpenLibrary("amisslmaster.library") + OpenAmiSSLTags(AMISSL_CURRENT_VERSION, ...) filling AmiSSLBase / AmiSSLExtBase. Wired into main.c after amiga_socket_open(). Silent fallback if amisslmaster.library missing Build + boot under Amiberry, observe no crash on startup/shutdown
3 ✅ ports/amiga/modssl.cssl module + SSLContext PROTOCOL_TLS_CLIENT, CERT_REQUIRED, CERT_NONE; SSLContext.__init__/__del__ (SSL_CTX_new(TLS_client_method()) + SSL_CTX_free); verify_mode property; load_verify_locations(cafile=…) import ssl; ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
4 ✅ SSLSocket + wrap_socket New type wrapping SSL * + fd. SSL_newSSL_set_fdSSL_set_tlsext_host_name (SNI) → SSL_connect. Stream protocol: SSL_read/SSL_write with SSL_ERROR_WANT_READ/WRITEMP_EAGAIN. Close via SSL_shutdown + SSL_free End-to-end HTTPS GET against an external host
5 ✅ Variant + heap reality check Confirm minimal excludes SSL (no socket → no SSL). Decide heap policy for standard given SSL_CTX ~16 KB + SSL ~48 KB allocations from system memory Two-context allocation under default heap; document -X heap= if needed
6 ✅ Docs + test wiring Flip Phase 28 to ✅ in docs/amiga.md. Add SSL section to docs/amiga-testing.md. Run upstream tests/extmod/ssl* against our binary on-device tests/extmod/ssl_basic.py via tools/amiga-runtests.py

All six steps landed. The implementation log under each step section below records what shipped, the on-target verification output, and any gotchas surfaced during the work.


Step 1 — SDK fetched on every build

Per the "SDK provisioning" notes above, option 2 (workflow step + local script) is the chosen route. Vendor nothing; rely on the upstream release artefact.

Deliverables

  • tools/amiga-fetch-amissl-sdk.sh — downloads the AmiSSL SDK archive from https://github.com/jens-maus/amissl/releases (pinned version, checksum-verified), unpacks the headers + stubs into a host-side cache directory (e.g. ~/.cache/amissl-sdk/<version>/), and publishes the path to a stable symlink.
  • tools/amiga-build.sh — call the fetcher before launching the container, bind-mount the cache as a second volume at /sdk, pass EXTRA_CFLAGS=-I/sdk/include EXTRA_LDFLAGS="-L/sdk/lib" into the make invocation.
  • ports/amiga/Makefile — pick up EXTRA_CFLAGS/EXTRA_LDFLAGS (already standard MicroPython make variables, no change likely needed beyond confirming they're honoured).
  • .github/workflows/ports_amiga.yml — add a step before the build that runs tools/amiga-fetch-amissl-sdk.sh and mounts the cache into the build container the same way amiga-build.sh does.
  • Pinned version chosen — propose AmiSSL 5.20, the most recent stable at the time of writing; bump deliberately.

Verification

Inside the container, after the fetch step:

m68k-amigaos-gcc -I/sdk/include -L/sdk/lib \
    -xc - -lamisslstubs <<'EOF'
#include <openssl/ssl.h>
int main(void) { return SSL_library_init(); }
EOF

Should produce an AmigaOS HUNK executable (we don't run it; linking is the test).

Notes

  • ~10–30 s added to each clean CI run; cached across runs via actions/cache keyed on SDK version.
  • tools/amiga-build.sh clean should not purge the SDK cache — it's not a build artefact, it's a toolchain artefact.

Step 2 — Library lifecycle

Deliverables

  • ports/amiga/amiga_ssl.h — externs for AmiSSLBase, AmiSSLExtBase, AmiSSLMasterBase; prototypes for amiga_ssl_open() / amiga_ssl_close(); AMISSL_CB macro for callback annotations (STDARGS SAVEDS-equivalent for bebbo).
  • ports/amiga/amiga_ssl.c:
    • amiga_ssl_open()OpenLibrary("amisslmaster.library", AMISSLMASTER_MIN_VERSION), then OpenAmiSSLTags(AMISSL_CURRENT_VERSION, AmiSSL_UsesOpenSSLStructs, FALSE, AmiSSL_GetAmiSSLBase, &AmiSSLBase, AmiSSL_GetAmiSSLExtBase, &AmiSSLExtBase, AmiSSL_SocketBase, (ULONG)SocketBase, AmiSSL_ErrNoPtr, (ULONG)&errno, TAG_DONE). Silent (no error) if amisslmaster.library not present.
    • amiga_ssl_close()CloseAmiSSL() if opened, then CloseLibrary.
  • ports/amiga/main.c — call amiga_ssl_open() after amiga_socket_open(), amiga_ssl_close() before amiga_socket_close() (reverse order: AmiSSL → socket → dos).
  • ports/amiga/mpconfigport.hMICROPY_PY_AMIGA_SSL (MICROPY_PY_AMIGA_SOCKET) so it autodisables on the minimal variant.
  • ports/amiga/Makefile — add amiga_ssl.c to SRC_C (or SRC_QSTR if any qstrs land in Step 3), link -lamisslstubs when MICROPY_PY_AMIGA_SSL=1.

Verification

  • Build standard variant. Verify text segment grows by the expected ~5–10 KB (mostly stub trampolines).
  • Boot under Amiberry (AmiSSL already installed) — no startup error, no shutdown error. Add a print(amiga.ssl_base()) debug helper temporarily if needed and confirm it returns non-zero.
  • Boot under vamos — silent fallback path (vamos has no amisslmaster.library), confirm no crash and no warning.

Step 3 — SSLContext

Deliverables

  • ports/amiga/modssl.c — new file, registers ssl module.
  • Constants: PROTOCOL_TLS_CLIENT, CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED.
  • SSLContext type:
    • __init__(protocol)SSL_CTX_new(TLS_client_method()); raise OSError with ERR_error_string text on failure.
    • verify_mode property (getter + setter calling SSL_CTX_set_verify).
    • load_verify_locations(cafile=None, cadata=None)SSL_CTX_load_verify_locations or SSL_CTX_load_verify_dir as appropriate.
    • close() + __del__ finaliser — SSL_CTX_free, then mark base as closed. Tolerate double-close.
  • If amiga_ssl_open() left the base NULL → import ssl raises ImportError("amisslmaster.library not available").

Verification

Under Amiberry:

import ssl
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.load_verify_locations(capath="AmiSSL:certs/")
del ctx     # GC finalises, SSL_CTX_free runs

No crash, no leak (run amiga.heap_info() before/after a loop of constructing + finalising 10 contexts to confirm release).


Step 4 — SSLSocket + wrap_socket

Deliverables

  • SSLSocket type in modssl.c, wrapping SSL * + the underlying socket fd:
    • SSLContext.wrap_socket(sock, server_hostname=...)SSL_new(ctx)SSL_set_fd(ssl, fd)SSL_set_tlsext_host_name(ssl, server_hostname) (SNI) → SSL_connect(ssl) looping on WANT_READ/WANT_WRITE.
    • Stream protocol (mp_stream_p_t):
      • readSSL_read; SSL_ERROR_WANT_READ/WANT_WRITE → set errcode = MP_EAGAIN, return MP_STREAM_ERROR.
      • writeSSL_write; same EAGAIN handling.
      • ioctl(close)SSL_shutdown (one round-trip), then SSL_free.
  • Error mapping: terminal SSL_get_error values → OSError with ERR_error_string text.

Verification

Under Amiberry with network:

import socket, ssl

s = socket.socket()
s.connect(socket.getaddrinfo("www.python.org", 443)[0][-1])
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.verify_mode = ssl.CERT_REQUIRED
ctx.set_default_verify_paths()
ws = ctx.wrap_socket(s, server_hostname="www.python.org")
ws.write(b"GET / HTTP/1.0\r\nHost: www.python.org\r\n\r\n")
print(ws.read(2048)[:80])
ws.close()

Should print the start of an HTTP/1.0 response — verified on 2026-05-31 with HTTP/1.1 200 OK followed by ~54 KB of HTML.

Status — verified

End-to-end HTTPS GET against www.python.org succeeds under Amiberry with full CERT_REQUIRED chain validation: TCP connect, TLS handshake + verify, write 104-byte request, read 53 912 bytes of HTML, clean shutdown. The pure-protocol pipeline works.

Known limitation — TLS 1.3 against modern CDNs

Cloudflare-fronted (www.example.com) and GitHub (api.github.com) endpoints complete the TLS 1.3 handshake (CERT_REQUIRED validation passes — the chain is verified, with all four certs verify return:1) but the connection then dies before any application data can be written: OpenSSL reports "CONNECTION CLOSED BY SERVER" followed by tls_retry_write_records:Broken pipe (ssl/record/methods/tls_common.c:1949). The underlying socket recv returns errno 54 (ECONNRESET).

Confirmed reproducible with AmiSSL's own openssl s_client — this is not in our MicroPython wrapper. From an AmigaShell:

1> AmiSSL:OpenSSL s_client -connect www.example.com:443 \
       -servername www.example.com -CApath AmiSSL:certs -brief
... TLS 1.3 handshake completes, Verification: OK ...
CONNECTION CLOSED BY SERVER
tls_retry_write_records:Broken pipe ... tls_common.c:1949

The same trace appears against api.github.com. Workarounds attempted via s_client:

Attempt Result
-groups X25519 (classic, no MLKEM) same close-after-handshake
-no_ticket (disable session tickets) same close-after-handshake
-tls1_2 (force TLS 1.2) server rejects ClientHello: unexpected eof while reading

www.python.org (a direct origin that still negotiates TLS 1.2 or TLS 1.3 without strict-fronting heuristics) handshakes, writes, and reads cleanly from both our wrapper and s_client, which is what the Step 4 end-to-end verification used.

The pattern points to AmiSSL's TLS 1.3 ClientHello / handshake producing a fingerprint that modern fronts choose not to talk to post-handshake. It is below the MicroPython layer; we have no visible knob that helps. Reported upstream; not a Phase 28 blocker. See AmiSSL issue notes for the full investigation and upstream report.

Cert path gotcha

SSL_CTX_load_verify_locations(capath=...) is sensitive to a trailing slash on AmigaOS: "AmiSSL:certs/" fails (path gets concatenated to "AmiSSL:certs//<hash>.0" which the AmigaDOS handler interprets as parent-dir-reference and ends up nowhere), "AmiSSL:certs" works. set_default_verify_paths() sidesteps the issue entirely and is the recommended call.

Also: AmiSSL's c_rehash output uses the old (pre-1.0.0) subject hash algorithm. New-hash filenames silently miss in lookups even though byte-identical files exist under their old-hash names in the same directory. Use set_default_verify_paths() and let AmiSSL handle this internally.


Step 5 — Variants + heap reality check

Deliverables

  • Confirm MICROPY_PY_AMIGA_SSL correctly autodisables on minimal (no socket → no SSL).
  • Measure actual heap pressure of:
    • 1 × SSLContext + 1 × SSLSocket connected idle.
    • 2 × concurrent SSLSockets mid-handshake.
  • Decision matrix:
    • If a single connection comfortably fits in the existing 256 KB standard heap, leave heap defaults alone, document the cliff.
    • If not, bump standard default heap to 384 or 512 KB.
  • Update docs/amiga.md Phase 27 build-variants table with any text-segment growth.

Verification

  • amiga.heap_info() snapshots around handshake.
  • AvailMem(MEMF_ANY|MEMF_LARGEST) snapshot to size system-memory vs GC-heap consumption.

Status — verified

Measured under Amiberry on 2026-05-31, standard variant, end-to-end HTTPS GET against www.python.org (53 912-byte response). Each row is a snapshot from amiga.heap_info() + AvailMem(MEMF_ANY|*):

Stage GC used GC free sys total sys largest
startup 26 816 229 312 14 243 080 12 166 992
after SSLContext() + set_default_verify_paths() 27 008 229 120 13 919 952 11 835 784
after del ctx + gc.collect() 26 992 229 136 13 919 952 11 835 784
ctx ready (second one) 27 008 229 120 13 911 720 11 827 552
after wrap_socket (TLS handshake done) 27 136 228 992 13 823 208 11 663 352
after full read (53 912 B response) 28 400 227 728 13 789 880 11 663 352
after ws.close() + s.close() 28 352 227 776 13 829 376 11 663 352
after del + gc.collect() 28 304 227 824 13 837 608 11 687 976

GC heap impact: negligible. Total growth across the entire session is well under 2 KB. The 256 KB default heap is plenty for SSL workloads; no heap bump needed.

System memory is where AmiSSL lives. It allocates from MEMF_ANY via exec.library, completely outside the GC heap, so -X heap= doesn't help. The observed costs:

  • SSL_CTX_new(TLS_client_method()) + set_default_verify_paths()~316 KB of system memory, persistent for the lifetime of the process. SSL_CTX_free doesn't return it (likely caches the parsed default trust paths globally inside AmiSSL).
  • One HTTPS session (handshake + 54 KB read) → ~125 KB additional during the session.
  • ws.close() + del recovers ~48 KB.
  • Net per-session leak after close: ~77 KB, plausibly the AmiSSL session-resumption cache for that endpoint.

For a stock 2 MB A1200 (chip RAM only), ~441 KB for one HTTPS session is significant but workable if Fast RAM is added. The minimal variant is the right target for tight-memory setups — SSL autodisables there.

Minimal variant gate — verified

MICROPY_PY_AMIGA_SSL = 0 in variants/minimal/mpconfigvariant.mk correctly drops amiga_ssl.c and modssl.c from SRC_C, omits -lamisslstubs from the link, and excludes AmiSSL-related strings from the binary (3 occurrences in standard, 0 in minimal).

Variant sizes (post-Step-4 baseline)

Variant Text bytes Total binary
standard 491 216 562 840
minimal 459 808 527 220
68020fpu (built) 530 388
68040 (built) 540 716

Standard grew ~33 KB over the pre-Phase-28 baseline (488 596 → 491 216) for the SSL wiring + AmiSSL stub trampolines. Minimal is unchanged from pre-Phase 28.


Step 6 — Docs + test wiring

Deliverables

  • docs/amiga.md Phase 28 status → ✅; add link to this page.
  • docs/amiga-testing.md — new "Step 4 — SSL tests" subsection under the Amiberry runner: requires AmiSSL v5 installed (amisslmaster.library reachable via LIBS:, e.g. via Assign LIBS: AmiSSL:Libs ADD; CA dir at AmiSSL:certs/), not exercisable under vamos.
  • tools/amiga-runtests.py — confirm tests/extmod/ssl*.py aren't in any _SKIP_* list and run cleanly.
  • Possibly: a small host-side TLS echo server (Python ssl.wrap_socket) so the on-device runner has a deterministic endpoint that doesn't depend on the external internet.

Status — done

  • docs/amiga.md Phase 28 row flipped to ✅; section header updated with the verified-status sentence and a forward pointer here.
  • docs/amiga-testing.md gained an SSL Tests subsection under the Amiberry runner — covers prereqs (AmiSSL v5 install + assigns in scope), the recommended set_default_verify_paths() entry point, the two AmigaOS cert-load gotchas (trailing-slash capath, old-hash c_rehash filenames), and the TLS 1.3 modern-CDN limitation.
  • tools/amiga-runtests.py confirmed not to gate out tests/extmod/ssl*.py / tls*.py — they walk naturally when the runner is pointed at extmod/. Tests that need surface we don't expose (ssl.wrap_socket module-level legacy form, cadata=, server-mode handshake with canned certs) will fail; the SSLContext-shape tests (ssl_sslcontext.py, ssl_sslcontext_verify_mode.py, etc.) exercise the API we land here.
  • Deferred: the host-side TLS echo server. Not built — external hosts that negotiate TLS 1.2 (www.python.org etc.) are sufficient for the verification we needed.

Cross-cutting concerns

  • Cleanup ordering. AmiSSL holds SocketBase; closing bsdsocket.library before AmiSSL would leak or double-fault. main.c must shut down in reverse open order: AmiSSL → socket → dos.
  • STDARGS SAVEDS callbacks. Phase 28 punts custom callback thunks. Step 4 stays on SSL_VERIFY_PEER with OpenSSL's default verify callback, which doesn't need user code. If a future step exposes set_verify_callback we'll need the bebbo equivalent (__attribute__((stkparm)) __saveds) — verify at that point that bebbo's calling convention attributes still work as documented above.
  • CA bundle distribution. Don't ship one in the binary. Rely on AmiSSL:certs/ (the c_rehash-style dir of hashed CAs the AmiSSL installer drops there) for the default trust store.
  • AmiSSL v4 fallback. Out of scope. v5-only initially.
  • Async-friendly handshake. Tied to asyncio gating (MICROPY_PY_ASYNCIO (0) today). Out of scope.
  • urequests / mip. Once Step 4 lands, these become trivial to freeze into the standard variant manifest. Separate PR.

Out-of-scope items reaffirmed

The "Deliberately not doing" list from the design section stands:

  • Server-side TLS.
  • OpenSSL config-file integration.
  • Asyncio-friendly handshake.
  • Touching upstream extmod/modssl_*.c.

If any of those become relevant later, they get their own follow-up issue.


Files

ports/amiga/modssl.c       — module def, SSLContext, SSLSocket
ports/amiga/amiga_ssl.c    — AmiSSL master open / init / cleanup
ports/amiga/amiga_ssl.h    — shared prototypes + base pointers

Build / test tooling touched:

tools/amiga-fetch-amissl-sdk.sh   — fetch + cache the AmiSSL v5 SDK
tools/amiga-build.sh              — mount the SDK cache into the container
ports/amiga/Makefile              — link -lamisslstubs when SSL is enabled
ports/amiga/mpconfigport.h        — MICROPY_PY_AMIGA_SSL gate
ports/amiga/main.c                — amiga_ssl_open/close lifecycle
.github/workflows/ports_amiga.yml — SDK fetch step in CI
docs/amiga.md, docs/amiga-testing.md — Phase 28 status + SSL test notes

SDK ref: jens-maus/amissl:dist/README-SDK. Examples/httpget.c and Examples/https.c are minimal working clients worth mirroring.

Status

✅ Done. All six steps landed; end-to-end HTTPS GET against www.python.org verified under Amiberry on 2026-05-31 with full CERT_REQUIRED chain validation. The one open follow-up is the TLS 1.3 modern-CDN close-after-handshake behaviour (Cloudflare / GitHub), reproduced with AmiSSL's own s_client, below the MicroPython layer, and tracked in AmiSSL issue notes — not a Phase 28 blocker.

Clone this wiki locally