Network Tutorial · 09

Knocking, but encrypted

A reliable byte stream is a lovely thing to type a password into — and a catastrophe, because anyone on the wire can read it. SSH turns that raw stream into a channel that's private, tamper-evident, and sure of who it's talking to. Alquist does it from scratch, with no libraries to trust and none to attack.

[o-o] |=| /_\

Everyone reaches for a library here. We didn't. Not out of bravado, but budget: the whole system must fit on a floppy, and a dependency you didn't write is a dependency you can't fully trust, can't fully audit, and can't rebuild from knowledge. So every curve, every cipher, every hash lives in the tree, small enough to read in an afternoon.

What SSH actually is

Strip away the folklore and SSH is three layers stacked on a TCP connection:

Chapters 6 and 7 built the reliable stream. Chapter 8 built the door a program opens it through. SSH is the first serious program to walk through that door.

Four primitives, all ours

The cryptographic core (the ssh_crypto module, being ported into src/crypto/) is deliberately tiny — exactly the modern set OpenSSH uses by default, and nothing more:

No OpenSSL, no libc, no bignum library. Each primitive carries its own known-answer self-test so a miscompiled curve fails loudly at boot rather than quietly on the wire. This is the "from scraps" philosophy made concrete: given the RFCs and the papers, the whole stack is re-derivable.

The handshake, step by step

RFC 4253 is a fixed dance, and Alquist follows it exactly:

  1. Version exchange. Each side sends a banner line (SSH-2.0-...). Plaintext, and folded into the exchange hash so it can't be quietly rewritten.
  2. KEXINIT. Both list the algorithms they support; the negotiation picks curve25519-sha256 for key exchange and [email protected] for the cipher.
  3. Key exchange. The X25519 public keys cross; each side computes the shared secret K and the exchange hash H over everything said so far plus the server's host key.
  4. Host authentication. The server signs H with its Ed25519 host key. The client verifies the signature against the key whose fingerprint it either remembers or is being asked to trust.
  5. NEWKEYS. Keys are derived from K and H; every packet after this point is encrypted and authenticated. The plaintext era is over.

Proof it's real: a conversation with OpenSSH

This is not a paper design. Our from-scratch implementation completed a full, standards- compliant handshake against production OpenSSH 10.2p1 — the same daemon on hundreds of millions of servers. In the run, our code:

If any primitive were off by a byte — a wrong-endian length, a curve constant transposed, a hash misfed — the fingerprint would not match and the signature would not verify. They did. That is the strongest kind of interoperability evidence there is: the other end is the reference implementation, and it was satisfied.

From transport to a shell

With the pipe private, the last two layers are comparatively small, and they connect directly to work happening in parallel elsewhere in the system:

So the end state is the one the whole series was pointed at: ssh to the Pi and be met, over an encrypted channel, by a real Alquist prompt.

Status, honestly. The cryptography and the RFC 4253 transport are proven on the bench against real OpenSSH, as described above. The in-kernel SSH server — the same code running as an EL0 service on the socket ABI of Chapter 8, with userauth wired to the config file and the channel wired to the shell — is landing now. This chapter documents the design and the evidence; the last integration mile is in progress, not painted over.

Try it

The transport is exercised today on the host, driving the real interop handshake:

# In the SSH prototype sandbox: run our client against a live OpenSSH server.
make kex          # negotiates, verifies the host signature, opens the encrypted channel

And the destination, once the in-kernel service completes its last mile:

ssh [email protected]      # met by an encrypted Alquist shell

Gotchas worth remembering

[^-^] |=| /_\

A stranger can now knock, prove who they are, and be let in — and the wire between us reveals nothing. From ARP asking a name to a shell behind real encryption, the stack stands. What's left is to make it observable, and then to make it mean under attack.