Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions _drafts/crypto-series/2026-06-04-crypto-series-intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@ date: 2026-06-04

This is the intro to a short series on applied cryptography — not the math, but the engineering: which primitives exist, how they compose into channels and authentication, and where all the trust ultimately bottoms out.

## The duality that runs through everything
## The split that runs through everything

Every byte that moves over a network forces two independent questions:

1. **Who is this from?** — *identity / authentication.* Certs, passkeys, signatures, tokens.
2. **Who else can see or change it?** — *data protection.* Confidentiality + integrity: TLS records, AEAD, envelope encryption, end-to-end encryption.
2. **Who else can see or change it?** — *data protection.* Confidentiality + *data* integrity: TLS records, AEAD, envelope encryption, end-to-end encryption.

They're **orthogonal**. You can have one without the other: raw Diffie–Hellman gives you a confidential channel with no idea who's on the other end; a bare signature proves origin while hiding nothing. Real systems answer both, at several layers that stack on top of each other — and almost every topic in this series is one half of that duality, pointed at one layer.
They're **orthogonal**. You can have one without the other: raw Diffie–Hellman gives you a confidential channel with no idea who's on the other end; a bare signature proves origin while hiding nothing. Real systems answer both, at several layers that stack on top of each other — and almost every topic in this series is one half of that split, pointed at one layer.

In CIA-triad terms, the data-protection arm is *confidentiality + integrity*, and the identity arm is integrity aimed at origin — *authenticity*. The third leg, **availability**, is deliberately not in this series: it isn't a property cryptography provides. If anything, crypto *subtracts* from it — ransomware is confidentiality turned against you, an expensive handshake is a denial-of-service vector, a lost key is permanent unavailability. And the properties people bolt onto the triad — non-repudiation, deniability, authenticity — aren't a fourth pillar either; they're refinements of integrity (precisely the [MAC-vs-signature](/programming/crypto-primitives.html) split), so everything here really does reduce to those two arms.

![Two ways to partition the same three guarantees — confidentiality, data integrity, and authentication (origin integrity). The C/I "property" split groups data and origin integrity together as integrity, cutting after confidentiality. This series' "difficulty" split instead groups confidentiality with data integrity as data protection, cutting after data integrity and leaving authentication as identity. Data integrity is the swing cell — it lands on a different side depending on which split you use.](/assets/crypto-series-intro/two-splits.svg)

Same three atoms, cut two ways: the C / I split divides by *property*, this series by *engineering difficulty* — with data integrity the swing that lands on either side depending on which cut you take.

Here's the way to *feel* the split: **once two parties share a secret, an AEAD turns it into a secure channel almost for free.** So nearly all the real difficulty reduces to the two sub-problems of getting that shared secret in the first place — **(a) establishing it safely**, and **(b) knowing it's shared with the *right* party.** (a) is the data-protection arm (key exchange, plus the key material itself); (b) is the identity arm (authentication, plus the roots of trust that make any identity believable).

Expand Down
2 changes: 2 additions & 0 deletions _drafts/crypto-series/2026-06-06-crypto-primitives.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ In the formal vocabulary: bare confidentiality only buys you IND-CPA (security a

**Signatures (asymmetric authentication).** A MAC's limitation is that verifying requires the same secret used to sign — so the verifier can also forge. Signatures break that symmetry: a private key signs, and a *public* key verifies, so anyone can check authenticity without being able to forge. That asymmetry is what makes signatures the backbone of identity at a distance — certificates, software signing, the passkey login in the [Authentication](/programming/authentication.html) post, the quote-signing keys in the [Attestation](/programming/roots-of-trust-and-attestation.html) post. RSA, ECDSA (NIST curves), and EdDSA (Ed25519) are the workhorses; the deterministic-nonce trick in EdDSA / RFC 6979 is, again, just a PRF over `(private_key, message)` — the same "PRF in counter mode" idea from the diagram above, reused to avoid catastrophic nonce reuse.

That same asymmetry decides a property protocols care about on its own: **non-repudiation**. Only the private-key holder could have produced a signature, so a *third party* can be convinced who signed — the signer can't later deny it. A MAC gives the mirror image: since either party could have forged the tag, neither can prove to anyone else who made it. Usually that's a limitation — but sometimes it's the point, and Signal and OTR authenticate with symmetric MACs precisely so messages stay **deniable**.

The throughline: a MAC is the integrity twin of symmetric encryption, and a signature is the integrity twin of asymmetric encryption. Confidentiality hides content; authentication binds it to a holder of a key. Real protocols always want both — which is why AEAD fuses them, and why the channel and authentication posts lean on these primitives constantly.

## References <!-- omit in toc -->
Expand Down
2 changes: 1 addition & 1 deletion _drafts/crypto-series/2026-06-07-secure-channels.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ category: programming
date: 2026-06-07
---

This is the data-protection arm of the [identity / data-protection duality](/programming/crypto-series-intro.html): how two parties agree on a shared key and protect bytes in transit. (The identity arm — proving *who* the other party is — gets its own [Authentication](/programming/authentication.html) post; the two are usually fused in practice but conceptually separate.)
This is the data-protection arm of the [identity / data-protection split](/programming/crypto-series-intro.html): how two parties agree on a shared key and protect bytes in transit. (The identity arm — proving *who* the other party is — gets its own [Authentication](/programming/authentication.html) post; the two are usually fused in practice but conceptually separate.)

The whole post rests on one observation: **once two parties share a secret, an AEAD turns it into a secure channel almost trivially.** So the entire difficulty is *safely establishing that shared secret* — which is what everything below is about. (The other half of the problem, knowing the secret is shared with the *right* party, is [Authentication](/programming/authentication.html) and [Roots of Trust & Attestation](/programming/roots-of-trust-and-attestation.html).)

Expand Down
61 changes: 59 additions & 2 deletions _drafts/crypto-series/2026-06-08-authentication.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ category: programming
date: 2026-06-08
---

This is the identity arm of the [duality](/programming/crypto-series-intro.html): proving *who* a party is, as opposed to protecting *what* they send (that's [Key Exchange & Secure Channels](/programming/secure-channels.html)). The two are usually fused in a handshake, but they're separable — and authentication has its own 50-year story worth telling on its own.
This is the identity arm of the [split](/programming/crypto-series-intro.html): proving *who* a party is, as opposed to protecting *what* they send (that's [Key Exchange & Secure Channels](/programming/secure-channels.html)). The two are usually fused in a handshake, but they're separable — and authentication has its own 50-year story worth telling on its own.

Authentication runs in two directions, and they arrived in the opposite order you'd guess. **Server authentication** — your browser proving it's really talking to `google.com` and not an impostor — is the older problem and the one that runs silently on *every* HTTPS connection, login or not; it's the job of the Web PKI. **User (client) authentication** — a human or device proving identity *to* the server — is the visible login step, and its 50-year arc fills most of this post. **Mutual authentication** (mTLS) is just both at once. The [threat-model post](/programming/threat-model.html) named server authentication as "the identity binding" — layer 2, *is this key really Bob's?* — but left the mechanism for here.

## The actors

Expand All @@ -16,9 +18,52 @@ Authentication is a conversation between a handful of parties — a client, the

> The grammar of what any proof can claim — who (identity: aud, iss, sub, rpId), when (freshness: iat, exp, jti, nonces), what (content: the bytes the proof commits to), what-for (scope), and bound-to-what (chaining: cnf claims) — is laid out in the secret-material map in [Keys](/programming/keys.html). Read any auth scheme below by asking which of those five it covers and which it omits.

## Server Authentication & the Web PKI

The user-auth story below is about moving the root of trust *out of the server's hands*. Server authentication has the mirror-image arc: moving it *out of any single CA's hands*. A public key is not a name — nothing in the bytes of `google.com`'s public key says "google.com" — so something has to vouch for the binding, and the whole history is about who that voucher is and how much you're forced to trust them.

You can read an X.509 certificate through the same five-part grammar as any other proof: **who** (the `subject` / Subject Alternative Names — the domains), **when** (the `notBefore`/`notAfter` validity window), **what-for** (key-usage and extended-key-usage extensions — "this key may sign TLS handshakes, not mint other certs"), and **bound-to-what** (the issuer's signature, chaining the leaf upward). The leaf binds a domain to a key, an intermediate signs the leaf, a root CA signs the intermediate — and the root's public key was baked into your browser or OS out of band, which is the [Roots of Trust](/programming/roots-of-trust-and-attestation.html) story, the anchor every chain terminates at.

#### Phase 1: Self-signed & TOFU (pre-PKI)

With no third party to vouch, you either accept a self-signed cert blindly or trust-on-first-use — pin the key the first time you see it and scream if it ever changes. SSH host keys still work exactly this way (`The authenticity of host ... can't be established`). TOFU is genuinely fine when you talk to the same server repeatedly, but it can't bootstrap trust with a server you've never met — which is the entire web.

#### Phase 2: Hierarchical CAs — X.509 & the Web PKI (1978 →)

Loren Kohnfelder's 1978 MIT thesis coined the word *certificate*: a signed statement binding a name to a key, so you no longer need an online directory lookup for every party. ITU standardized the format as X.509 (1988; the v3 extensions everyone actually uses arrived in 1996, profiled for the internet as RFC 5280). Netscape wired it into SSL in the mid-90s, and a commercial CA industry grew up to sign certs for a fee. The model: trust a fixed set of root CAs preinstalled in your browser, and transitively trust anything they sign. This is *still* the Web PKI — but notice the trust is blind and global: **any** trusted CA can vouch for **any** domain, so the system is only as strong as its single weakest CA.

#### Phase 3: Automating issuance — ACME & Let's Encrypt (2015)

For two decades certs cost money and were issued by hand, so most of the web stayed on plaintext HTTP. Let's Encrypt (ISRG, 2015) made domain-validated certs free and fully automated via the ACME protocol (RFC 8555): prove you control a domain by serving a token at a well-known URL or publishing a DNS record, and a cert drops out — scriptable end to end. HTTPS went from minority to near-universal in a few years. This is the **DV** (domain-validated) tier — "controls the domain," nothing more; the **OV** and **EV** tiers additionally vet the legal *organization*, but browsers quietly stopped giving EV any special UI treatment around 2019, conceding that users never read it.

#### Phase 4: Auditing the CAs — Certificate Transparency (2013 →)

Blind global trust broke exactly as you'd expect: the Comodo and DigiNotar compromises (2011) minted valid certs for domains like `google.com`, and nobody could *see* it had happened (the [threat-model post](/programming/threat-model.html) tells that story). Certificate Transparency (RFC 6962) is the fix: every cert must be logged in append-only, publicly-auditable logs, and Chrome and Safari reject certs that don't carry proof of logging. A bogus cert can still be *minted*, but no longer *invisibly* — domain owners monitor the logs for certs they never requested. CAA DNS records (which CAs are even permitted to issue for a domain) and multi-perspective domain validation (check domain control from several network vantage points, so a local BGP hijack can't fool it) are the same move: stop trusting a single CA blindly, constrain and audit it instead.

#### Phase 5: Shrinking the trust window — short-lived certs

Revocation is PKI's chronic weak spot — the "stale" problem from the [capstone](/programming/roots-of-trust-and-attestation.html). CRLs (download every revoked serial) grew too big; OCSP (ask the CA live, per connection) leaked your browsing history to the CA and added latency; OCSP stapling (the *server* fetches and attaches a fresh signed status) fixed privacy and latency but was never reliably deployed. The blunt answer that won was to make certs so short-lived that revocation barely matters: maximum lifetimes have ratcheted from 825 days to 398 (Sept 2020), and the CA/Browser Forum has voted to reach 47 days by 2029. A cert that expires in weeks is its own revocation.

#### The alternatives: who else can vouch?

Hierarchical CAs aren't the only way to bind a key to an identity. Each alternative is really a different answer to "who vouches, and for what?":

- **Web of Trust (PGP, 1991).** No CAs — users sign each other's keys, and trust propagates through whoever you've personally chosen as "introducers" (Zimmermann's "decentralized fault-tolerant web of confidence"). Decentralized and human-meaningful, but it never scaled: key-signing parties are friction, and trust paths between strangers are rarely short or legible.
- **SPKI/SDSI (RFC 2693, 1999).** Grew out of frustration with X.509's complexity, and made a radical move: *the key is the principal* — don't bind keys to global real-world identities at all, bind them to **authorizations** and **local names**, with the verifier often also the issuer (an "authorization loop"). Closer in spirit to today's capability tokens (macaroons, biscuits) than to the Web PKI.
- **DANE (RFC 6698, 2012).** Anchor certs in DNSSEC instead of CAs — publish the cert's fingerprint in a DNS record signed up the DNS hierarchy. Trades the CA cartel for the DNS root; browsers never adopted it.
- **Decentralized PKI / key transparency.** Blockchain naming (Namecoin, ENS) and W3C DIDs let each entity act as its own root authority, anchored in a distributed ledger rather than a CA — and the [roots-of-trust](/programming/roots-of-trust-and-attestation.html) post notes a blockchain genesis hash is just one more out-of-band anchor. Meanwhile key transparency (CONIKS, then Google / WhatsApp / iMessage key transparency) applies CT's "log everything" idea to *end-user* keys, so an E2EE provider can't swap in a wiretap key without leaving a public, auditable trace.

These map cleanly onto Zooko's triangle (secure / human-meaningful / decentralized — pick two, from the [capstone](/programming/roots-of-trust-and-attestation.html)): the Web PKI takes human-meaningful + secure and sacrifices decentralization (you trust the CA cartel); Web of Trust reaches for human-meaningful + decentralized and gives up reliable security at scale; self-certifying names (a Tor `.onion` address, a raw public-key fingerprint) are secure + decentralized but not human-meaningful; and blockchain naming is the bet that a global ledger can finally square all three.

Notice the throughline — the same one the user-auth arc has below: every step after Phase 2 (CT, CAA, multi-perspective validation, short lifetimes, and every decentralized alternative) pushes the root of trust *out of any single CA's hands*. Server auth and user auth are one de-trusting trajectory pointed in opposite directions.

#### Mutual TLS

Everything above authenticates the *server* to the client. mTLS makes it symmetric: the client also presents a cert, and the server validates it against its own trust anchor. On the open web this is rare (users don't carry certs), but it's the backbone of *service-to-service* authentication inside infrastructure — a workload's identity *is* an X.509 cert (SPIFFE/SPIRE issue exactly these), and possession of the matching private key is the proof. Same possession anchor as a passkey, just with no human in the loop — which reconnects to the service-to-service tier of the [security hierarchy](#picking-an-auth-method-security-hierarchy) below.

## History of User Authentication

The whole history of server-side authentication is the story of moving the root of trust out of the server's hands. The arc is secret-held-by-server → secret-held-by-server-but-harder-to-crack → secret-held-by-client.
The whole history of user authentication is the story of moving the root of trust out of the server's hands. The arc is secret-held-by-server → secret-held-by-server-but-harder-to-crack → secret-held-by-client.

#### Phase 1: Plaintext passwords (1960s–70s)

Expand Down Expand Up @@ -97,10 +142,22 @@ For service-to-service authentication (backend APIs calling each other) — no h
4. [Best Current Practice for OAuth 2.0 Security - RFC 9700][rfc9700]
5. [Grant Negotiation and Authorization Protocol (GNAP) - RFC 9635][rfc9635]
6. [OAuth 2.0 and the Road to Hell - Eran Hammer][hammer-road-to-hell]
7. [Public Key Infrastructure - Wikipedia][pki-wiki]
8. [Internet X.509 PKI Certificate and CRL Profile - RFC 5280][rfc5280]
9. [Automatic Certificate Management Environment (ACME) - RFC 8555][rfc8555]
10. [Certificate Transparency - RFC 6962][rfc6962]
11. [SPKI Certificate Theory - RFC 2693][rfc2693]
12. [DNS-Based Authentication of Named Entities (DANE) - RFC 6698][rfc6698]

[morris-thompson]: https://rist.tech.cornell.edu/6431papers/MorrisThompson1979.pdf "Password Security: A Case History - Morris & Thompson (1979)"
[sysapproach-auth]: https://book.systemsapproach.org/security/authentication.html "Authentication - Computer Networks: A Systems Approach"
[rfc6749]: https://datatracker.ietf.org/doc/html/rfc6749 "The OAuth 2.0 Authorization Framework - RFC 6749"
[rfc9700]: https://datatracker.ietf.org/doc/html/rfc9700 "Best Current Practice for OAuth 2.0 Security - RFC 9700"
[rfc9635]: https://datatracker.ietf.org/doc/html/rfc9635 "Grant Negotiation and Authorization Protocol (GNAP) - RFC 9635"
[hammer-road-to-hell]: https://hueniverse.com/oauth-2-0-and-the-road-to-hell-8eec45921529 "OAuth 2.0 and the Road to Hell - Eran Hammer"
[pki-wiki]: https://en.wikipedia.org/wiki/Public_key_infrastructure "Public Key Infrastructure - Wikipedia"
[rfc5280]: https://datatracker.ietf.org/doc/html/rfc5280 "Internet X.509 Public Key Infrastructure Certificate and CRL Profile - RFC 5280"
[rfc8555]: https://datatracker.ietf.org/doc/html/rfc8555 "Automatic Certificate Management Environment (ACME) - RFC 8555"
[rfc6962]: https://datatracker.ietf.org/doc/html/rfc6962 "Certificate Transparency - RFC 6962"
[rfc2693]: https://datatracker.ietf.org/doc/html/rfc2693 "SPKI Certificate Theory - RFC 2693"
[rfc6698]: https://datatracker.ietf.org/doc/html/rfc6698 "DNS-Based Authentication of Named Entities (DANE: TLSA) - RFC 6698"
Loading