<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: arcker</title>
    <description>The latest articles on DEV Community by arcker (@arcker).</description>
    <link>https://dev.to/arcker</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3979063%2Fb47f840e-534a-44db-83bd-f4f50d654c30.png</url>
      <title>DEV Community: arcker</title>
      <link>https://dev.to/arcker</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/arcker"/>
    <language>en</language>
    <item>
      <title>TLS 1.3 without a library — a real browser does the handshake against Verbose machine code</title>
      <dc:creator>arcker</dc:creator>
      <pubDate>Thu, 11 Jun 2026 13:44:22 +0000</pubDate>
      <link>https://dev.to/arcker/tls-13-without-a-library-a-real-browser-does-the-handshake-against-verbose-machine-code-5c1f</link>
      <guid>https://dev.to/arcker/tls-13-without-a-library-a-real-browser-does-the-handshake-against-verbose-machine-code-5c1f</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is an English version of an article first published, in French, on my blog: &lt;a href="https://arcker.org/blog/2026-06-04-tls-sans-bibliotheque/" rel="noopener noreferrer"&gt;arcker.org&lt;/a&gt;. It's part of an educational series about &lt;strong&gt;Verbose&lt;/strong&gt; — an experimental language whose compiler verifies proofs declared in source and emits small, readable x86-64 machine code.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When your browser shows a little padlock next to a URL, it has just held a cryptographic conversation with the server — the TLS &lt;em&gt;handshake&lt;/em&gt;. That conversation is usually handled by enormous C libraries: OpenSSL, BoringSSL, hundreds of thousands of lines nobody ever reads in full.&lt;/p&gt;

&lt;p&gt;This article is the &lt;strong&gt;capstone of the crypto arc&lt;/strong&gt; of the series. It shows the payoff: a real browser opens an HTTPS page served by a binary whose &lt;em&gt;every&lt;/em&gt; cryptographic transform — the key exchange, the identity signature, the bulk encryption, the hash — is &lt;strong&gt;machine code emitted by Verbose&lt;/strong&gt;. Not one line of OpenSSL. And the browser, the most demanding TLS client there is, can't tell the difference: it completes the handshake and renders the page.&lt;/p&gt;

&lt;p&gt;We build on &lt;a href="https://arcker.org/blog/2026-06-01-sha256-from-nothing/" rel="noopener noreferrer"&gt;SHA-256&lt;/a&gt; (chapter 1 of the arc) — it shows up everywhere in TLS. The details of each brick (AES, Ed25519) get their own chapters; here we pull up and watch the whole thing work.&lt;/p&gt;




&lt;h2&gt;
  
  
  A handshake, in three ideas
&lt;/h2&gt;

&lt;p&gt;Before any encrypted exchange, the browser and the server have to settle three things. That's all of TLS, one sentence each:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Agree on a secret&lt;/strong&gt; that no eavesdropper can guess, even after hearing the entire conversation (the key exchange).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prove identity&lt;/strong&gt; — the server shows a certificate &lt;em&gt;and&lt;/em&gt; signs, so you know you're talking to the right party (the signature).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Talk encrypted&lt;/strong&gt; — once the shared secret exists, everything else is encrypted with a fast symmetric algorithm (the bulk encryption).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the wire, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  BROWSER                                 SERVER (Verbose binary)
      │                                          │
      │ ─── ClientHello ───────────────────────► │  "here are my algos + my key"
      │ ◄── ServerHello ───────────────────────- │  "here's mine"
      │            (encrypted from here on)      │
      │ ◄── Certificate + signature ──────────── │  "here's who I am, signed"
      │ ─── Finished ──────────────────────────► │
      │ ◄══ HTML page (AES-GCM encrypted) ══════ │  "Hello from Verbose TLS"
      │                                          │
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each of the three ideas needs a cryptographic ingredient. And that's where Verbose comes in.&lt;/p&gt;




&lt;h2&gt;
  
  
  The ingredients — and where the boundary runs
&lt;/h2&gt;

&lt;p&gt;Here's the part that matters for Verbose's thesis. &lt;strong&gt;All&lt;/strong&gt; the cryptography is Verbose machine code. The host (in Python) only does plumbing: open the socket, frame the TLS messages, and draw the one random secret (&lt;code&gt;os.urandom&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  ┌────────────────────────────────────────────────────────────┐
  │  HOST (Python)  : socket, TLS record framing,              │
  │                   os.urandom (the only secret input)       │
  ├────────────────────────────────────────────────────────────┤
  │  VERBOSE (x86-64 machine code) :                           │
  │     X25519          the key exchange                       │
  │     Ed25519 / P-256 the identity signature                 │
  │     AES-128-GCM     the bulk encryption                    │
  │     SHA-256 + HKDF  the hash and key derivation            │
  └────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every line of the Verbose block was validated &lt;strong&gt;byte-for-byte&lt;/strong&gt; against a reference before being assembled:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AES-128-GCM&lt;/strong&gt; against the NIST GCM Test Case 2 vector;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;X25519&lt;/strong&gt; against RFC 7748;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ed25519&lt;/strong&gt; against the three RFC 8032 §7.1 vectors;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HKDF&lt;/strong&gt; against RFC 5869;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SHA-256&lt;/strong&gt;, as we saw in chapter 1, against &lt;code&gt;sha256sum&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing is &lt;em&gt;taken on faith&lt;/em&gt; because "it worked once." Each brick is checked against the official spec, in isolation, before it's stacked into the next.&lt;/p&gt;




&lt;h2&gt;
  
  
  The only loop in all of TLS: the X25519 ladder
&lt;/h2&gt;

&lt;p&gt;Almost all the cryptography here is &lt;em&gt;unrolled&lt;/em&gt;: each operation finishes in a fixed number of steps the verifier can follow statically. There's exactly &lt;strong&gt;one&lt;/strong&gt; real loop — the X25519 key exchange, which climbs a "ladder" (the &lt;em&gt;Montgomery ladder&lt;/em&gt;) over 255 rungs.&lt;/p&gt;

&lt;p&gt;In Verbose, a loop is written as recursion, and — like in &lt;a href="https://arcker.org/blog/2026-05-26-proving-termination/" rel="noopener noreferrer"&gt;chapter 3&lt;/a&gt; — it has to &lt;strong&gt;prove&lt;/strong&gt; it terminates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rule ladder
  logic:
    ...
    out = if s.i == 0 then &amp;lt;the ladder result&amp;gt;
          else ladder(LadderState { ..., i: s.i - 1, ... })
  proofs:
    termination:
      decreasing : i
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(The real state carries ~50 fields — the field elements over 10 limbs — elided here.) The key point: &lt;code&gt;decreasing : i&lt;/code&gt; is a &lt;em&gt;compile-time-verified promise&lt;/em&gt;. The rung &lt;code&gt;i&lt;/code&gt; strictly decreases on every call, so the compiler proves the ladder stops. The 255 iterations run at runtime; the guarantee that they finish is established before the binary even exists. No general loop construct was added to do TLS — the proof tools from chapter 3 are enough.&lt;/p&gt;




&lt;h2&gt;
  
  
  The catch: a real browser doesn't compromise
&lt;/h2&gt;

&lt;p&gt;The first server signed its identity with &lt;strong&gt;Ed25519&lt;/strong&gt;. Against &lt;code&gt;openssl s_client&lt;/code&gt; — the usual test client — everything passed: &lt;code&gt;Verify return code: 0 (ok)&lt;/code&gt;, signature &lt;code&gt;ed25519&lt;/code&gt;. We could have declared victory there.&lt;/p&gt;

&lt;p&gt;Except a real browser refused. Flat: &lt;code&gt;illegal_parameter&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Why? In its &lt;code&gt;ClientHello&lt;/code&gt;, the browser announces the signature schemes it accepts (the &lt;code&gt;signature_algorithms&lt;/code&gt; extension). And browsers offer &lt;code&gt;ecdsa_secp256r1_sha256&lt;/code&gt; (P-256), &lt;strong&gt;not&lt;/strong&gt; Ed25519. RFC 8446 requires the server to sign with an offered scheme. Ed25519 wasn't offered → immediate rejection.&lt;/p&gt;

&lt;p&gt;That's &lt;em&gt;exactly&lt;/em&gt; the value of a real browser as a target: it imposes constraints a test client lets slide. So we had to build the whole &lt;strong&gt;ECDSA P-256&lt;/strong&gt; stack — GF(p256) field arithmetic, point add/double, scalar multiplication, the modular inverse, and ECDSA-P256-SHA256 signing (RFC 6979 deterministic nonce, DER encoding, &lt;em&gt;low-s&lt;/em&gt;) — validated against the RFC 6979 §A.2.5 vectors and &lt;code&gt;openssl dgst -verify&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With a P-256 certificate, the browser stops saying &lt;code&gt;illegal_parameter&lt;/code&gt;. It completes the handshake. It renders the page: &lt;strong&gt;"Hello from Verbose TLS"&lt;/strong&gt;. The only remaining warning is the expected self-signed-certificate one. Not a crypto failure. The page renders.&lt;/p&gt;




&lt;h2&gt;
  
  
  The detail that's fun: smaller, for free
&lt;/h2&gt;

&lt;p&gt;Two recursive rewrites, the same week, at zero compute cost:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;p256_ninv&lt;/code&gt;: &lt;strong&gt;11.2 MB → 84 KB&lt;/strong&gt; of native code (131× smaller), 8/8 byte-for-byte vs the unrolled version.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;x25519_finish&lt;/code&gt;: &lt;strong&gt;1.3 MB → 42 KB&lt;/strong&gt; (31× smaller), 266 field multiplications identical to the unrolled version. openssl handshake re-validated after the cure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same algorithm, same exact output, zero CPU overhead. The recursive path introduced for self-hosting (Phase A) turned out to be the right tool for collapsing huge unrolled cryptographic chains into compact machine code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why it matters
&lt;/h2&gt;

&lt;p&gt;TLS 1.3 is not a toy: it's a stack of cryptographic primitives, inside a state machine, inside a wire format, with a public client — your browser — that won't compromise on any of it. Making it work end-to-end is empirical proof that the language and its compiler can &lt;strong&gt;express, verify, and run&lt;/strong&gt; a real protocol, not a demo.&lt;/p&gt;

&lt;p&gt;And the method is the message. The cryptography here isn't credible because a tool produced it — it's credible because it's confronted, brick by brick, with the RFC vectors, then with OpenSSL, then with a browser that forgives nothing. You don't &lt;em&gt;trust&lt;/em&gt;, you &lt;em&gt;verify&lt;/em&gt;. That's the whole difference, and that's all of Verbose: a binary small enough to read, proofs declared in source, and output checked against reality at every step.&lt;/p&gt;

&lt;p&gt;The arc began with a 12 KB hash in chapter 1. It ends with a browser rendering a page encrypted by a binary you can read line by line. In between, each brick still needs telling in detail — AES, Ed25519, the Montgomery ladder. Those are the next chapters.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://arcker.org/blog/2026-06-04-tls-sans-bibliotheque/" rel="noopener noreferrer"&gt;arcker.org&lt;/a&gt;, where the full series lives.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>compilers</category>
      <category>cryptography</category>
      <category>programming</category>
      <category>rust</category>
    </item>
  </channel>
</rss>
