1 engine, 3 apps

All three open their own TLS 1.2 connection, with an X25519 key exchange and ChaCha20-Poly1305. The cryptography runs on the 68030. A PiSCSI or BlueSCSI plays the SCSI network card and only moves packets. I haven't tested it with a real network card yet; if you have one, see Measure your Mac below.

A Covid project that stalled

In 2019 a friend gave me her father’s old Macintosh Classic II. Around then I saw the super cool MacPlayer project on Hackaday: a Spotify remote Anthony Super ran on his SE/30, with two little speakers beside it. A real living-room showpiece (I later bought an SE/30 myself). But my Classic II had no network card, so it sat in a corner gathering dust.

That changed in 2022, when I found RaSCSI (now PiSCSI): a Raspberry Pi that pretends to be a SCSI hard disk and can also emulate a network card.

Then I watched Notion’s interview with Macintosh developer Andy Hertzfeld and got an idea: wouldn’t it be fun to bring Notion back to the old Mac? Its UI suited the 1-bit screen perfectly.

With help from Macintosh C Programming by Example, but mostly the MacPlayer source code, I loaded a Notion page in the Basilisk II emulator. To make the api connection I've simply turned on HTTPS in the MacHTTP source code. 

mbedtls_ssl_conf_authmode(&_conf, MBEDTLS_SSL_VERIFY_NONE); 
// BAD BAD BAD! No remote certificate verification (requires root cert) 

It was working in the Basilisk emulator! But then I tried it on the real Classic II and got mbedtls_ssl_handshake returned -76.  The modern Mac had been doing the heavy lifting... My Dunning-Kruger low point

To get HTTPS working it needed a modern machine to make the conversion, so I wrote a simple Python proxy that passed the HTTPS to plain HTTP. which could also be running on the PiSCSI.

 - YESSSS, it worked! Showing the Notion page on my Compact mac

However, because a 512 × 342 screen can't hold much text, it had to scroll in order to be usable.  Scrolling inside a textfield was easy but a Notion page needs multiple textfields..  Unfortunately, I was unable to get the page to scroll. And after many attempts the project was put on hold. - May 2023


Vibe coding

2026 - For work I was using Claude Code, and was impressed. Could it help fixing my old scroll problem? In no time. So started to wonder.. could I drop the proxy too? Claude advised keep the proxy, It would take months of writing custom code to get HTTPS to work.


How hard can it be?

After all, there are ESP32s and other low-powered IoT devices make secure API calls, without root certificates just like and like System 7. 

With stock mbedTLS, a first connection checks two P-256 signatures and a P-384 one. All of this has to happen within 15 seconds... 

On my first attempt with the added root certificates, it took my classic Mac 120.4 seconds, but server had hung up long ago!


The math was too heavy

I searched the web for faster algorithms, all the way down to papers like Rivain 2011, and had Claude analyze them. I even tried to offload some of the work to the 68882 FPU coprocessor, which unfortunately turned out to be slower. There was progress, but the first handshake stayed above 15 seconds.

So the plan became to sacrifice a connection: the first does the slow certificate check, caches the answer and dies; the second finds the answer waiting and gets in.

Testing was low-tech: first test in Basilisk II, then copy it over using a PowerBook G4 as an intermediary, run it, wait, photograph the CRT, and type the error message and the number of ticks back to Claude.

After 91 photos and 23 videos of error codes, on March 23, the page suddenly actually loaded!! It sacrificed the first connection which died after 15.1s, and the second got through in about 6.5s

Even Claude was impressed:

"Notion on an SE/30 over HTTPS. That's genuinely impressive - a 16MHz 68030 doing P-256 ECDH and TLS 1.3 against Cloudflare."

Deep dive on the 12 improvements

Faster math

I was wondering if it could be faster? Pretty all the attempts went into finding faster methods multiplying 256-bit numbers, for a chip that work 16-32 bits at a time.

  1. Swapping the curve code. Three #defines send mbedTLS's curve math to micro-ecc and an X25519 based on curve25519-donna: small, fixed-size code I could tune.
  2. -O3 for the crypto only. That one flag took a signature check on the Classic II from 6.4 s to 4.7 s.
  3. Two multiplications for the price of one. A signature check needs two. Shamir's trick runs them side by side so they share their doublings, and splitting each number in half cuts those from about 257 to 129.
  4. Writing the one loop the compiler couldn't. In 68020 assembly, with the chip's 32×32→64-bit MULU.L, the whole multiplication stays in registers and fits in the 68030's 256-byte instruction cache. It beat an unrolled version that didn't fit, and gives exactly the same bits as the C code.
  5. Using the shape of the prime. P-256's prime is built from powers of two, so trimming each product back to 256 bits is a few sums of 32-bit words (Solinas reduction).
  6. X25519 on the same multiply. The key exchange now takes 1.1 s on the SE/30 and 1.8 s on the Classic II, down from 1.7 s and 3.0 s.
  7. ChaCha20, not AES. ChaCha20 is just additions, rotations and XORs. AES in software is slow on a 68k, and Cloudflare picks it whenever the Mac offers it, even as second choice.

Less work

  1. Skipping P-384. The root certificate's P-384 signature alone would take 15–25 s. So the Mac trusts the intermediate certificate WE1 directly, until it expires on February 20, 2029.
  2. Session tickets. After a full handshake the server hands over a ticket, and the next connection skips the key exchange and every signature check: 1.1 s instead of 13 on the Classic II. Two bugs hid this for weeks.
  3. Never doing the same work twice. The ticket, the DNS answer and the signature results survive a restart, and a connection that's still open is reused without a handshake.

Less waiting

  1. Calculating while waiting. The next key pair is made while the Mac would otherwise sit idle, waiting for the network.
  2. A 2-byte patch to Apple's network code. Open Transport 1.3 reads Cloudflare's 128 KB TCP window, sent as 16 × 8192, as 16 bytes, and holds back what the Mac sends for 3–4 s. Turning one conditional jump into an unconditional one fixes it: on the SE/30 a returning page went from about 4.7 s to under a second.

Memory. During the math, a fixed 64 KB pool takes mbedTLS's thousands of small allocations, so they never fragment the Mac's heap. It went wrong in three different ways first.

All findings, measurements and code can be fount in the deep dive on GitHub.

How long until the answer is on screen?

The handshake only opens the secure connection. Before it, the Mac prepares its certificates and keys. After it, the request goes out, the answer comes back, and the Mac reads it and draws it. For a short API call on the SE/30, it adds up to:

Keep in mind

Credits and use of AI

The 2022 version was written by hand. Everything since was made in 2026 with Claude Code. Using AI on these machines feels a bit controversial, but I couldn't have done this without it, and I still think I've learned a lot. A year ago I couldn't have told you what a TLS handshake does.

Huge thanks to Anthony Super (antscode). His MacPlayer made me want to try, and his MacHTTP, mbedtls-Mac-68k and MacTCPHelper are the foundation every handshake still runs on.

Thanks also to mbedTLS, Retro68 (Wolfgang Thaller), micro-ecc (Kenneth MacKay), curve25519-donna (Adam Langley), http-parser, yuarel, gason, PiSCSI, BlueSCSI and Infinite Mac (Mihai Parparita), which the browser demo is built on.

"Pretty neat you got an https implementation working." — Eric Helgeson, BlueSCSI