From f5e0d8de0a54f68cdf89a0489ae55c565c1182f5 Mon Sep 17 00:00:00 2001 From: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> Date: Sun, 21 Jun 2026 19:43:30 +0800 Subject: [PATCH 1/6] rename duality -> --- _drafts/crypto-series/2026-06-04-crypto-series-intro.md | 4 ++-- _drafts/crypto-series/2026-06-07-secure-channels.md | 2 +- _drafts/crypto-series/2026-06-08-authentication.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/_drafts/crypto-series/2026-06-04-crypto-series-intro.md b/_drafts/crypto-series/2026-06-04-crypto-series-intro.md index 3309fa7..1b803b5 100644 --- a/_drafts/crypto-series/2026-06-04-crypto-series-intro.md +++ b/_drafts/crypto-series/2026-06-04-crypto-series-intro.md @@ -6,14 +6,14 @@ 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. -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. 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). diff --git a/_drafts/crypto-series/2026-06-07-secure-channels.md b/_drafts/crypto-series/2026-06-07-secure-channels.md index 846cc69..aa2470d 100644 --- a/_drafts/crypto-series/2026-06-07-secure-channels.md +++ b/_drafts/crypto-series/2026-06-07-secure-channels.md @@ -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).) diff --git a/_drafts/crypto-series/2026-06-08-authentication.md b/_drafts/crypto-series/2026-06-08-authentication.md index ec33abf..48764b1 100644 --- a/_drafts/crypto-series/2026-06-08-authentication.md +++ b/_drafts/crypto-series/2026-06-08-authentication.md @@ -6,7 +6,7 @@ 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. ## The actors From a258dfe526aed44cc3c98cc1ef46fd4b1df99cbb Mon Sep 17 00:00:00 2001 From: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> Date: Sun, 21 Jun 2026 19:49:44 +0800 Subject: [PATCH 2/6] add paragraph about non-repudiation --- _drafts/crypto-series/2026-06-06-crypto-primitives.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_drafts/crypto-series/2026-06-06-crypto-primitives.md b/_drafts/crypto-series/2026-06-06-crypto-primitives.md index 6c267bf..d05c9b9 100644 --- a/_drafts/crypto-series/2026-06-06-crypto-primitives.md +++ b/_drafts/crypto-series/2026-06-06-crypto-primitives.md @@ -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 From 0843f54425c1a1a7d83764aecaca7f5398d549e1 Mon Sep 17 00:00:00 2001 From: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> Date: Sun, 21 Jun 2026 20:15:36 +0800 Subject: [PATCH 3/6] add availability to capstone --- .../crypto-series/2026-06-10-roots-of-trust-and-attestation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/_drafts/crypto-series/2026-06-10-roots-of-trust-and-attestation.md b/_drafts/crypto-series/2026-06-10-roots-of-trust-and-attestation.md index f9e3c8c..0497420 100644 --- a/_drafts/crypto-series/2026-06-10-roots-of-trust-and-attestation.md +++ b/_drafts/crypto-series/2026-06-10-roots-of-trust-and-attestation.md @@ -106,6 +106,7 @@ We've now walked the whole stack — primitives, channels, keys, authentication, - Unauthorized. Authentication answers "who"; it doesn't answer "may they". You will write the authz layer. Every time. Capabilities (macaroons, biscuits), ACLs, role tables, OPA — all of it lives above the channel. - Nameless. Public keys aren't names. You will build, or import, a naming layer (Zooko's triangle applies: secure / human-meaningful / decentralized — pick two). Petnames, ENS, DNS-over-something, .onion vanity, GPG WoT. All ad hoc, none portable. - Stale. Keys leak, certs expire, devices get lost, employees quit. You need rotation, revocation, and a story for "this was Alice and now isn't." CRL/OCSP, short-lived certs (SPIFFE), key transparency, ratchets. The state machine for "current trust" is its own distributed system. +- Killable. The channel says nothing about staying *up* — and crypto only ever *subtracts* from availability. An unauthenticated handshake packet makes you do expensive asymmetric work for free (client puzzles, SYN/DTLS cookies, WireGuard's cookie reply all exist to claw that back); a lost or revoked key is permanent denial-of-access; ransomware is just confidentiality pointed at you. Availability is the third leg of the CIA triad, and the channel is the wrong layer to ask for it — it lives in redundancy, rate limits, and capacity, not crypto. - Brittle (cryptographically). Algorithms get deprecated faster than your dependency tree updates. SHA-1, RC4, MD5, soon RSA-2048 and ECDH against a quantum adversary. Cipher agility is the "Mismatched" problem on hard mode — you can't just add a JSON field, you have to renegotiate primitives without opening a downgrade attack. (See the negotiation-as-footgun discussion in [Key Exchange & Secure Channels](/programming/secure-channels.html) — agility is exactly the attack surface TLS keeps getting burned by.) - Leaky. The channel encrypts content but not envelope: who talks to whom, when, how often, how much. Traffic analysis, timing, sizes, fingerprintable handshakes (JA3/JA4). Padding, cover traffic, mixnets — and each of those is its own pile of work. - Replayable. A single channel handles ordering inside its lifetime, but the moment your message escapes the channel (queued, logged, persisted, cross-session) freshness becomes your problem again. Nonces, timestamps, sequence numbers, idempotency keys — all rebuilt at the app layer. From bf5a35a4b134a6b745b1ece096f2e923a02e845e Mon Sep 17 00:00:00 2001 From: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> Date: Sun, 21 Jun 2026 20:29:44 +0800 Subject: [PATCH 4/6] add cia diagram to intro --- .../2026-06-04-crypto-series-intro.md | 8 ++- assets/crypto-series-intro/two-splits.svg | 56 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 assets/crypto-series-intro/two-splits.svg diff --git a/_drafts/crypto-series/2026-06-04-crypto-series-intro.md b/_drafts/crypto-series/2026-06-04-crypto-series-intro.md index 1b803b5..6fbcfb3 100644 --- a/_drafts/crypto-series/2026-06-04-crypto-series-intro.md +++ b/_drafts/crypto-series/2026-06-04-crypto-series-intro.md @@ -11,10 +11,16 @@ This is the intro to a short series on applied cryptography — not the math, bu 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 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). (There's a subtlety the series keeps returning to: key establishment and authentication are *usually* fused but are conceptually separable — Diffie–Hellman is the notable exception that establishes a shared key while authenticating nobody.) diff --git a/assets/crypto-series-intro/two-splits.svg b/assets/crypto-series-intro/two-splits.svg new file mode 100644 index 0000000..830f643 --- /dev/null +++ b/assets/crypto-series-intro/two-splits.svg @@ -0,0 +1,56 @@ + + + + +Two ways to cut the same three guarantees +The same three atoms, partitioned by property (C / I) and by engineering difficulty (what this series does). + + +By property — the C / I split · primitives view + + +Confidentiality + + +Integrity +data + origin + + + + +Confidentiality +who can read it + + + +the swing +Data integrity +unchanged in transit + + +Authentication +origin integrity — who it's from + + +By engineering difficulty — what this series does + + +Data protection +confidentiality + data integrity — fuse into an AEAD + + +Identity +authenticate the peer + + + + + + + + + +Data integrity is the swing — “integrity” by property, part of “data protection” by difficulty. +Once a key is shared, confidentiality + data integrity fuse into an AEAD; the hard, separable problem is the last column. + + From f116df3623eec92dccc6020134c137f368fffbf3 Mon Sep 17 00:00:00 2001 From: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> Date: Sun, 21 Jun 2026 20:37:26 +0800 Subject: [PATCH 5/6] add server auth --- .../2026-06-08-authentication.md | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/_drafts/crypto-series/2026-06-08-authentication.md b/_drafts/crypto-series/2026-06-08-authentication.md index 48764b1..9866456 100644 --- a/_drafts/crypto-series/2026-06-08-authentication.md +++ b/_drafts/crypto-series/2026-06-08-authentication.md @@ -8,6 +8,8 @@ date: 2026-06-08 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 Authentication is a conversation between a handful of parties — a client, the resource server it's talking to, and often an identity provider that vouches for it — each holding different resident keys and minting different per-request proofs. @@ -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) @@ -97,6 +142,12 @@ 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" @@ -104,3 +155,9 @@ For service-to-service authentication (backend APIs calling each other) — no h [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" From 00d0d76f089cbb9475a4db58ac3c0e55d5138c25 Mon Sep 17 00:00:00 2001 From: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> Date: Thu, 2 Jul 2026 13:00:30 +0800 Subject: [PATCH 6/6] refactor whole series --- .../2026-06-03-security-acronyms.md | 154 +++++++++ .../2026-06-04-crypto-series-intro.md | 119 +++++-- .../crypto-series/2026-06-05-threat-model.md | 243 ++++++++++++-- .../2026-06-06-crypto-primitives.md | 32 +- .../2026-06-07-secure-channels.md | 68 +++- .../2026-06-08-authentication.md | 86 ++++- ...authorization-policy-reference-monitors.md | 278 ++++++++++++++++ _drafts/crypto-series/2026-06-09-keys.md | 231 +++++++++++-- ...26-06-10-roots-of-trust-and-attestation.md | 136 -------- ...1-data-at-rest-storage-kms-and-recovery.md | 291 +++++++++++++++++ ...e-isolation-measurement-and-attestation.md | 300 +++++++++++++++++ ...06-13-what-crypto-still-doesnt-give-you.md | 304 ++++++++++++++++++ .../crypto-series/2026-06-14-availability.md | 73 +++++ assets/crypto-series-intro/mccumber-cube.png | Bin 0 -> 80918 bytes 14 files changed, 2072 insertions(+), 243 deletions(-) create mode 100644 _drafts/crypto-series/2026-06-03-security-acronyms.md create mode 100644 _drafts/crypto-series/2026-06-08-authorization-policy-reference-monitors.md delete mode 100644 _drafts/crypto-series/2026-06-10-roots-of-trust-and-attestation.md create mode 100644 _drafts/crypto-series/2026-06-11-data-at-rest-storage-kms-and-recovery.md create mode 100644 _drafts/crypto-series/2026-06-12-data-in-use-isolation-measurement-and-attestation.md create mode 100644 _drafts/crypto-series/2026-06-13-what-crypto-still-doesnt-give-you.md create mode 100644 _drafts/crypto-series/2026-06-14-availability.md create mode 100644 assets/crypto-series-intro/mccumber-cube.png diff --git a/_drafts/crypto-series/2026-06-03-security-acronyms.md b/_drafts/crypto-series/2026-06-03-security-acronyms.md new file mode 100644 index 0000000..61b751e --- /dev/null +++ b/_drafts/crypto-series/2026-06-03-security-acronyms.md @@ -0,0 +1,154 @@ +--- +title: "A Taxonomy of Security Taxonomies" +category: programming +date: 2026-06-03 +--- + +The sentence that organizes the zoo is: + +> For each **property/concern**, in each **data state**, against each **threat**, choose a **function/capability**, define the **policy**, enforce it with **mechanisms**, wrapped by **technical / process / human controls**. + +That sentence is intentionally not a one-dimensional stack. It has four orthogonal axes: + +1. **Concern** — what's at stake? Each concern has a *property* (its defender-side name — confidentiality, integrity, …) and a *threat* (its attacker-side name — disclosure, tampering, …): one thread seen from the two sides. +2. **Data state** — where is the information: at rest, in transit, or in use / processing? +3. **Layer** — *how abstract* is the control: capability → policy → mechanism, the realization stack. +4. **Control modality** — *what material* is the control: technology, process/practice, human/education. + +McCumber's cube is the ancestor of this picture: CIA × {storage, transmission, processing} × {technology, policy/practices, people}. The tweak here is to split two *orthogonal* things McCumber's third axis bundles together. **Technology / process / people** is the *modality* — what **material** a control is made of. **Capability → policy → mechanism** is the *layer* — how **abstract** a control is, from the function you reach for down to the engine that enforces it. These are perpendicular: every control has both an altitude and a material, so the same three layers recur in each modality (a reference monitor is `(mechanism, tech)`; a four-eyes deploy checklist is `(mechanism, process)`; a written classification standard is `(policy, process)`). The collision to watch is the word *policy*: McCumber's "policy/practices" is a modality (organizational practice), while the "policy" layer here is a decision rule such as "subject S may perform action A on object O under context C." + +Within that larger grid, the realization stack is a means-ends chain: a **mechanism** upholds a **policy model** that realizes a **capability** — and the top of that chain *delivers the property*, the concern's defender-side name. So the property is not a fourth layer; it's where the stack meets the concern axis. The other pieces are frames around it: the **threat** is the same concern's attacker-side name (the property mirrored), build principles constrain every layer, modality says what material the control is, and risk/governance decides what's worth doing at all. + +So AAA isn't a rival to CIA — it's the *realization stack below* it (the capabilities that deliver the properties). Bell–LaPadula isn't a rival to AAA — it's a level below *that* (the rule-set that realizes a capability). And the reference monitor that checks each access is a level below *that* again (the engine that enforces the rules). Sort every acronym by **which question it answers** and the zoo collapses into a grid with an attacker-side mirror and a governance wrapper. The map: + +**The concern, then the stack** — the top two rows are the concern's two faces (what's at stake); the bottom three are the realization stack (how you deliver it), descending from the function you reach for to the machine that enforces it: + +| | The question it answers | Examples | +|---|---|---| +| *concern — WHAT'S AT STAKE* | | | +| **Property** · defender's name | What must hold — what counts as a violation? | CIA · Parkerian hexad · 5 Pillars · Pentagon of Trust · McCumber | +| **Threat** · attacker's name | What does the adversary make happen instead? | STRIDE · LINDDUN | +| **↳ formalized** | …stated precisely enough to *prove*? | safety/liveness · hyperproperties · noninterference · IND-CPA / EUF-CMA | +| *layer — THE REALIZATION STACK* | | | +| **Capability / function** · *what?* | What function delivers the property? | AAA · IAAA · Lampson's Gold Standard | +| **Policy model** · *which?* | Which rules apply? (the *spec*) | Bell–LaPadula · Biba · Clark–Wilson · Chinese Wall · MAC/DAC/RBAC/ABAC | +| **Enforcement mechanism** · *how?* | How are the rules enforced at runtime? (the *implementation*) | reference monitor · PEP/PDP · MMU & page bits · TPM/enclaves · sandboxes (seccomp/eBPF) · **the crypto itself** | + +A note on what's *not* in the stack. The property and threat above aren't stack levels — they're the concern's two faces, the *what* the stack serves. State and modality are cross-cutting axes, not levels. Three more things sit *around* the grid, and this article only sketches them: **build principles** — how to build any layer *well* (Kerckhoffs, least privilege, Zero Trust); the **attacker's mirror** — the whole grid read from the other side (STRIDE is just the property axis inverted, which the concern grid below makes explicit); and the **governance / risk loop** — deciding what's even worth defending (risk scoring, NIST CSF, OODA). Privacy keeps its own property-sets (FIPPs, Contextual Integrity, Pfitzmann–Hansen) for what content-encryption can't state — but its core concern, *unlinkability*, is already a row below. + +## The concern axis + +The stack above is only the **layer** axis — *how abstract* (capability → policy → enforcement). Cutting across it is the **concern** axis: *what the thing is about*. Borrow the software word from *separation of concerns*: a concern is a subject-matter thread that runs vertically through every level, wearing a different name at each one. So every named term is really a **cell in a grid = (concern × state × layer × modality)**; the 2-D slice below holds state and modality implicit and shows just **concern × layer**. The two leftmost columns are the concern's two faces (*what's at stake* — defender's property, attacker's threat); the rest is the realization stack, which OS design reads as an interrogative ladder *what → which → how*: + +| Concern | Property · *defender* | Threat · *attacker* | Capability · *what?* | Policy · *which?* | Enforcement · *how?* | +|---|---|---|---|---|---| +| **Secrecy** — keep content unseen | confidentiality | disclosure | encryption / read-control | classification labels · BLP no-read-up | AEAD · read-ACL | +| **Unlinkability** — who talks to whom? | anonymity / unlinkability | linking / profiling | metadata minimization | data-minimization · k-anonymity | mixnets · onion routing · padding | +| **Undetectability** — is there even a message? | undetectability / unobservability | detection / traffic analysis | traffic hiding | cover-traffic rate · stego capacity | steganography · spread-spectrum · constant-rate cover traffic | +| **Correctness** — keep content unaltered | integrity | tampering | integrity protection / write-control | Biba · Clark–Wilson | MAC · signature · write-ACL | +| **Origin** — who sent it? | authenticity | spoofing | authentication | which CAs/keys to trust | signature · cert-chain check | +| **Attribution** — who did it? | accountability / non-repudiation | repudiation | auditing | what-to-log · retention | logging · **signatures** · tamper-evident trail | +| **Uptime** — is it reachable? | availability | denial of service | rate-limit / admission control | quotas · QoS thresholds | replicas · load balancers · SYN cookies | + +**Policy vs. mechanism.** [OSTEP](https://pages.cs.wisc.edu/~remzi/OSTEP/) names the bottom two stack levels for OS design — **policy = *which*** ("which process should run?") and **mechanism = *how*** ("how does a context switch work?") — and separating them is a modularity win: change the *which* without rethinking the *how*. **Capability = *what*** sits one level up (what *kind* of function you reach for: authentication? authorization? auditing?), and the **property** the stack delivers is the *why* it exists at all. The same swap-one-without-the-other modularity holds at every adjacent pair. + +One asymmetry worth flagging: *policy* is the only **multi-W** level. Capability is just *what*, mechanism just *how* — but a policy is a predicate over an access *event*, so it pins down the full situational 5-W: **who** (subject) may take **what** action on **which** object, **where/when** (context). "Which" is just the headline; **ABAC** parameterizes all of them explicitly. This is also *why* XACML grows a fourth component — the Policy *Information* Point (PIP) — whose only job is to fetch those who/which/where attributes for the decision; see the enforcement-mechanism section. + +The first three rows are one *hiding* family, split first into **data vs. metadata**: **secrecy** hides the *content* (the data), while **unlinkability** and **undetectability** hide the *metadata* (the envelope) — the *who-talks-to-whom* relationships and the *existence/volume* of traffic respectively. (That's exactly the capstone's "Leaky" split — "who talks to whom" vs. "when, how often, how much.") (Pfitzmann–Hansen's privacy vocabulary all lives in this corner: *anonymity* = unlinkability of a subject to its messages; *pseudonymity* = the mechanism that dials linkability between full anonymity and full identifiability — and, via digital pseudonyms, the bridge to **attribution**; *unobservability* = undetectability + anonymity.) Then come **correctness**; the identity concerns **origin** and **attribution**; and **uptime**, the availability leg (governance/ops, not crypto). Note what is *not* a row: **access control**. It looks like a concern — "who may touch it?" — but it has no goal of its own, which is the tell. It's the **non-crypto enforcement route** for secrecy (gate reads) and correctness (gate writes), the dual of crypto: authorization → RBAC/BLP/ABAC → reference monitor is that route's capability/policy/enforcement. The crypto-vs-access duality across the storage/transmission/processing states is *the cube*; the read/write split is what makes both confidentiality (encrypt, or gate reads) and integrity (verify, or gate writes) dual. One note on *slicing* the concern axis: Parker's *possession* and *utility* aren't new concerns — they're finer slices of **secrecy** and **uptime** (lose the sealed encrypted drive → secrecy intact, possession gone), the concern-granularity debate again. + +This is the resolution to "wait, isn't *authentication* a property?": **a property is just the concern's defender-side name** — its *what-must-hold* face. "Authenticity" and "authentication" don't overlap as a property and a capability; they're the *same concern* (origin), one its property, the other the capability that delivers it. English reuses the root because it nominalizes the verb (*authenticate* → *authenticity*); loose mnemonics then grab whichever form fits their acronym, which is why the Pentagon of Trust drops the *capability* verb *authentication* into a *property* set. (Note the asymmetry: the *access* concern has no clean property at all — authorization's goal is just the confidentiality + integrity it enforces, which is why it keeps getting treated as its own axis.) + +Two payoffs, once the grid is visible: + +- **The property-set wars are a fight about one axis only.** CIA vs. Parkerian hexad vs. Samonas's "strikes back" is entirely about *how finely to slice the concern axis* — Parker splits confidentiality into secrecy + possession, Samonas says three broad concerns suffice. None of it touches the layer axis. "How many concerns?" and "how many abstraction levels?" are independent questions, routinely conflated. +- **STRIDE is the concern axis, mirrored.** Spoofing ↔ origin, Tampering ↔ integrity, Repudiation ↔ attribution, Information-disclosure ↔ secrecy, DoS ↔ uptime, Elevation ↔ access. That's *why* it's the cleanest property-dual — it's just the concerns enumerated from the attacker's side. It's literally the **threat** column of the grid above. + +With the axes in play, the rest of this file walks the grid top-to-bottom — first the **properties** (the concern, defender-side), then each level of the **realization stack** (capability → policy → mechanism) — then the frames around it. The state and modality axes are cross-cuts rather than sections you want to expand into a full 3×N×M matrix every time. + +## The data-state axis: storage / transmission / processing + +McCumber's second axis asks *where the information is when protected*: + +- **Storage / at rest** — disks, databases, object stores, backups, cold archives. Typical threats: stolen media, leaked database snapshots, cloud-admin access, ransomware, rollback to stale state. +- **Transmission / in transit** — bytes moving between endpoints. Typical threats: eavesdropping, tampering, replay, downgrade, man-in-the-middle, misbinding a key to the wrong name. +- **Processing / in use** — plaintext while a CPU, process, enclave, database engine, or AI agent operates on it. Typical threats: malicious process, compromised OS, hostile hypervisor, side channels, prompt injection, supply-chain code executing with legitimate credentials. + +The same concern changes shape by state. Confidentiality in transit is usually a secure channel; confidentiality at rest is envelope encryption or read-control; confidentiality in use is isolation, minimization, or confidential computing. Integrity in transit is AEAD/MACs; integrity at rest is signatures, hashes, append-only logs, or write-control; integrity in use is process isolation, measured boot, sandboxing, and verified code. + +## The control-modality axis: technology / process / humans + +McCumber's third axis is best read as **control modality** — the kind of control you deploy: + +- **Technology** — ciphers, protocols, HSMs, reference monitors, MMUs, sandboxes, scanners, logs. +- **Policy / practice / process** — standards, procedures, approvals, runbooks, key-rotation schedules, incident-response playbooks, data-classification rules. +- **Humans / education / culture** — training, review habits, phishing resistance, admin discipline, incentives, separation of duties in the organizational sense. + +This is not the same as the **policy level** in the realization stack. The policy level is a decision rule ("who may do what to which object under which context?"). McCumber's policy/practices axis is broader organizational machinery. A KMS decrypt rule is policy-level; the quarterly key-rotation runbook and the training that tells engineers when to use it are process/human controls around that rule — and the rule itself can be enforced in any modality, which is exactly why *policy* lives on a different axis from *process*. + +## Properties — the concern, defender-side: *what counts as a violation?* + +The "what are we protecting" property sets — the concern's defender-side face; **STRIDE/LINDDUN mirror each into its attacker-side threat**. Every level of the realization stack below exists to deliver one of these. + +- **CIA triad** — Confidentiality, Integrity, Availability. Sometimes written **AIC** to avoid the agency collision. +- **Parkerian hexad** (Donn Parker, 1998) — CIA + Possession/Control, Authenticity, Utility. The additions exist precisely because CIA conflates things: "possession" splits the *control* of data from its *disclosure* (you can lose a sealed encrypted drive — confidentiality intact, possession lost), and "utility" covers data that's available but useless (lost the key). +- **Five Pillars of Information Assurance** (US DoD / IATF) — CIA + Authentication + Non-repudiation. Often mnemonic'd as **CIANA** or **IAS-5**. (Note the category slip baked into the mnemonic: Authentication is a *capability* — it's listed as a peer property here only because the pillar set is loose.) +- **Pentagon of Trust** — the Schneier link. Piscitello's proposal to extend the traditional four-property model (Authentication, Authorization, Availability, Authenticity) by prepending **admissibility** — asserting the trustworthiness of the endpoint device before accepting a keystroke. The comment thread correctly collapses this to **remote attestation** — i.e. the whole TDX/DCAP world. The recurring objection there (admissibility ⊆ authentication, since a system has an identity too) is the same argument you'd make for mutual attestation / MAGE. +- **McCumber Cube** (1991) — 3D model: CIA × {storage, transmission, processing} × {policy/practices, education/people, technology}. The ancestor of the model here: property × data-state × control-modality. The important cleanup is that McCumber's third axis is **modality**, not the defender-stack's layer axis — "policy/practices" there means organizational controls, not just formal access rules. + +The recurring pattern across these property sets — hexad, Pentagon of Trust, Five Pillars — is people bolting properties onto CIA because CIA feels too coarse. The Samonas counter-argument (see "Where crypto sits") is that they're all subsumable back into CIA *if you read the three words broadly enough* — which is true at the cost of precision. + +### Formalized: *how do we state the property precisely enough to prove it?* + +Same question, but rigorous. You reach here when the informal sets can't even *state* the property — exactly the BigDipper censorship-resistance situation. + +- **Safety & Liveness** (Lamport; topologically characterized by Alpern–Schneider) — "bad thing never happens" / "good thing eventually happens." +- **Hyperproperties** (Clarkson & Schneider — Fred B. Schneider, the *-der*, not Bruce Schneier) — sets of trace-sets; needed because confidentiality (noninterference, observational determinism, generalized non-interference) isn't a property of individual traces. The right home for censorship-resistance-as-hyperproperty. +- **Noninterference** (Goguen–Meseguer, 1982) — the original info-flow security definition. +- **Crypto security notions** — IND-CPA / IND-CCA, EUF-CMA, semantic security, UC (universal composability). Different genre (game/simulation-based) but the same "name the exact property" instinct. + +## Capabilities (functions): *which capabilities deliver the goals?* + +The next three sections walk the **realization stack** — capability → policy → mechanism — that delivers each property. + +Not properties — the *functions* you need to achieve them, and not yet the rules or the code that do so. This is where the "is admissibility a property or a capability?" debates actually live. (Beware the word *mechanism* here: in the classic "separation of policy and mechanism" sense it means the runtime enforcement *engine* — the enforcement-mechanism level below — not this abstract-capability level. That collision is exactly why this level is named *capability*, not *mechanism*.) + +- **Lampson's Gold Standard** — **Au**thentication, **Au**thorization, **Au**diting. The pun is that "Au" is gold's symbol. Lampson's framing is broader than the networking sense. +- **AAA** (RADIUS / TACACS+ / Diameter) — Authentication, Authorization, Accounting. Same triad, ops flavor. +- **IAAA** (CISSP canon) — Identification, Authentication, Authorization, Accountability. Splits "claim identity" from "prove it," which the Schneier thread also nitpicks. + +## Policy models: *what are the rules? (the spec)* + +The formal rule-sets that say what's allowed — the *specification*, not yet the code that enforces it. Each is pinned to a property it realizes a capability for: Bell–LaPadula → confidentiality, Biba → integrity, and so on. (BLP isn't hand-waving — it's a state machine with a proved Basic Security Theorem, a genuine formal spec.) + +- **Bell–LaPadula** (confidentiality: no-read-up, no-write-down), **Biba** (integrity: the dual), **Clark–Wilson** (integrity via well-formed transactions + separation of duty), **Brewer–Nash / Chinese Wall** (dynamic conflict-of-interest), plus **HRU**, **Take-Grant**, **Graham–Denning**, **Lipner**, and the **MAC/DAC/RBAC/ABAC** policy families. + +## Enforcement mechanisms: *how are the rules enforced at runtime? (the implementation)* + +The engine that actually upholds the policy on every access — the *implementation* the spec compiles down to. The boundary between this level and the policy level is the classic **"separation of policy and mechanism"** (Hydra, Brinch Hansen, X11's "mechanism, not policy"): keep the enforcement engine policy-agnostic so you can swap rules without rewriting the enforcer. + +- **Reference monitor** (Anderson, 1972) — the abstract "check every access" enforcer the whole field was named around; must be tamper-proof, always-invoked, and small enough to verify. +- **PEP / PDP / PAP / PIP** (XACML's "four horsemen") — the access-control split into a *policy enforcement point* (the gate that intercepts each request), a *policy decision point* (evaluates the policy rules → permit/deny), a *policy administration point* (authors and stores them), and a *policy information point* (fetches the extra attributes the PDP needs — subject, resource, environment). That last one is the runtime embodiment of "policy is multi-W" — it gathers the *who/which/where* of the access so the PDP can decide. +- **Hardware enforcers** — MMU & page-protection bits, TPMs, secure enclaves, the UEFI/secure-boot chain. +- **OS/runtime sandboxes** — seccomp, eBPF, kernel capabilities, namespaces, hypervisor isolation. +- **The crypto itself** — AEAD, signatures, MACs. This is the level cryptography lives on: it *enforces* confidentiality and integrity directly, often with no separate policy model at all (see below). + +## Where crypto sits in all this + +Crypto is an **enforcement-mechanism family** that directly realizes two broad CIA goals: confidentiality (encryption) and integrity (MAC/signature, with authenticity and non-repudiation as integrity-of-*origin* sub-flavors — the MAC-vs-signature split). It also supports privacy/unlinkability in more specialized constructions. Tellingly, it often *skips* the policy level at the data-object boundary — you don't need Bell–LaPadula to get confidentiality out of AES; the cipher *is* the enforcement, and its implicit policy is "key-holders can decrypt, non-key-holders cannot." But real systems immediately reintroduce policy one step up: who receives the key, which public keys/roots are trusted, who may call KMS `Decrypt`, when keys expire, what measurements attestable code must have. + +The useful compression is therefore **not** "all capabilities are crypto or access control." That is Samonas-style semantic inflation: true only if you stretch the words until they stop telling you what to build. The precise version is narrower and more valuable: for **confidentiality and integrity of data objects**, the two dominant enforcement routes are **self-protecting data** (crypto: encrypt/MAC/sign/commit the bytes) and **mediated access** (access control: reference monitors, ACL/RBAC/ABAC, MMUs, database guards, KMS policies). Same goal, different path down the stack: confidentiality via access control descends goal → capability (authorization) → policy (BLP/RBAC/ABAC) → enforcement (reference monitor), whereas confidentiality via crypto can shortcut straight from goal to enforcement (AEAD) and then pushes policy to key distribution and key use. + +But the full capability layer is larger: authentication, authorization, auditing/accounting, attestation, key management, monitoring/detection, rate limiting, redundancy, backup/recovery, and incident response do not collapse cleanly into two buckets. Many *use* crypto or access control as mechanisms; they are not themselves just crypto/access control. This is also where one goal fans out into *several* capabilities — the [availability appendix](/programming/availability.html) works the `uptime` cell out in full: a single goal splitting into rate-limiting, redundancy, backup/recovery, and incident response, each with its own policy and mechanism level. Crypto provides none of availability, the authorization policy itself (the policy level), the build principles, or the governance loop — this is the series' "two arms" framing, and the capstone's list of what-a-secure-channel-doesn't-give-you is just the enumeration of the layers and frames crypto leaves untouched. + +At the *concern* level, that single integrity arm fans out across the grid via the **MAC→signature escalation**: a symmetric MAC suffices for **correctness** and for **origin** between two parties, but the moment a *third party* must be convinced — **attribution / non-repudiation** — only an asymmetric **signature** works (a shared-key MAC is repudiable by construction, which is exactly why Signal authenticates with MACs *for* deniability). So crypto's enforcement column actually covers **secrecy, correctness, origin, and attribution** — the whole integrity family — not just the two data concerns. It even reaches **unlinkability**, but only partway: content-encryption hides *what* you send, never *who-talks-to-whom* (the capstone's "Leaky" gap), so anonymity needs a mixing/topology layer — though *privacy-enhancing cryptography* (mixnets, onion routing, ring/blind signatures, ZK proofs, PIR) pushes crypto deep into that concern too. The only cells crypto can't touch are **access** (policy + reference monitor) and **uptime** (it only *subtracts* from availability). + +This also dissolves the **Samonas "CIA strikes back"** question (*The CIA Strikes Back*, Samonas & Coss 2014). Their claim — that the eight historical additions (authenticity, non-repudiation, correctness, responsibility, integrity-of-people, trust, ethicality, identity management) all subsume back into CIA — is purely a **property-level** argument, and they win it only by reading C/I/A *etymologically/semantically* (confidentiality from *confidere*, "to have full trust"). The tell is their own Table 3: six of the eight route into **Integrity**, because they're using "integrity" in the organizational sense (wholeness, coherence, ethicality of people), not crypto's tamper-evidence sense. So: + +- **Narrow / crypto-integrity** = "these bytes weren't altered; this came from the key-holder." Crisp, mechanism-defining. The series' reduction lives here and is *mechanically* true. +- **Sponge / governance-integrity** = "the organization and its people are trustworthy and coherent." Absorbs almost anything — which is how Samonas reaches total subsumption, at the cost of the word telling you what to build. + +Verdict: **teeth for governance taxonomy** (CIA-broadly-read genuinely is the durable north star, and the additions were largely a narrowing-then-recovery cycle — their Table 2 runs 1970s CIA → +Au,nR → +CSpec → +RITE,Idn → 2020s back to CIA), **trying too hard for mechanism precision** (the subsumption is semantic inflation, and it never grapples with the cases that genuinely escape CIA: availability's own irreducible leg, privacy/metadata, and the hyperproperty expressiveness gap). They even concede the placements are "highly debatable" and "for illustrative purposes." + +## Throughline + +The property sets (CIA → hexad → Five Pillars → Pentagon of Trust) all answer "what counts as a violation"; STRIDE/LINDDUN invert each property into an attacker action; the formal layer (safety/liveness, hyperproperties) is what you reach for when the informal sets can't even *state* the property — exactly the BigDipper censorship-resistance situation; and the whole defender/attacker pair runs inside a risk-and-ops loop that decides what's worth defending. The Parkerian additions and the "admissibility" debate are both symptoms of the *property* being too coarse — just at the data layer vs. the endpoint layer respectively — and Samonas is the argument that you should fix that by reading the property *richer*, not by adding levels. The cleaner fix, for an engineering audience, is the opposite: keep each level's vocabulary crisp and notice that the acronyms were never competing — they were answering different questions. diff --git a/_drafts/crypto-series/2026-06-04-crypto-series-intro.md b/_drafts/crypto-series/2026-06-04-crypto-series-intro.md index 6fbcfb3..2549374 100644 --- a/_drafts/crypto-series/2026-06-04-crypto-series-intro.md +++ b/_drafts/crypto-series/2026-06-04-crypto-series-intro.md @@ -1,43 +1,118 @@ --- -title: "Applied Crypto" +title: "Applied Crypto in the Security Cube" category: programming 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. +This is a series on applied cryptography — not the math, but the engineering: which keys and algorithms exist, how they compose into channels, storage systems, authentication, authorization, and attestation, and where all the trust ultimately bottoms out. -## The split that runs through everything +The series is organized by one sentence: -Every byte that moves over a network forces two independent questions: +> For each **property/concern**, in each **data state**, against each **threat**, choose a **function/capability**, define the **policy**, enforce it with **mechanisms**, wrapped by **technical / process / human controls**. -1. **Who is this from?** — *identity / authentication.* Certs, passkeys, signatures, tokens. -2. **Who else can see or change it?** — *data protection.* Confidentiality + *data* integrity: TLS records, AEAD, envelope encryption, end-to-end encryption. +That is the working expansion of the McCumber cube for software engineers. McCumber gave us **CIA × storage/transmission/processing × technology/policy/people**. -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. +![alt text](/assets/crypto-series-intro/mccumber-cube.png) -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. +This series keeps that, but splits McCumber's third axis into the two orthogonal things it blends — *how abstract* the control is, and *what material* it is made of: -![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) +```text +concern × data-state × layer × modality +``` -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. +Where: -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). +```text +concern — what's at stake (each has a defender name = property, an attacker name = threat) +data-state — where the data is : at rest | in transit | in use +layer — how abstract : capability/function → policy → mechanism +modality — what material : technology | process/practice | human/education +``` -(There's a subtlety the series keeps returning to: key establishment and authentication are *usually* fused but are conceptually separable — Diffie–Hellman is the notable exception that establishes a shared key while authenticating nobody.) +The `layer` and `modality` axes are perpendicular: every control has both an *altitude* (capability/policy/mechanism) and a *material* (tech/process/human) — a reference monitor is a `(mechanism, tech)` control; a four-eyes deploy checklist is `(mechanism, process)`; a written classification standard is `(policy, process)`. The same three altitudes recur in every material. -## Three pillars +The `concern` axis carries two names for each thread — the **property** you want to hold and the **threat** you defend against are the same thing seen from the two sides: -**Mechanism — the atoms.** The cryptographic primitives everything else is built from. +| Concern | Property · *defender's name* | Threat · *attacker's name* | +|---|---|---| +| **secrecy** | confidentiality | information disclosure | +| **correctness** | integrity | tampering | +| **origin** | authenticity | spoofing | +| **attribution** | accountability / non-repudiation | repudiation | +| **unlinkability** | anonymity | linking / profiling | +| **uptime** | availability | denial of service | -**Establishing the shared secret — data protection.** Key exchange turns "we both hold a secret" into a live secure channel; and the secret itself, a real key, has a lifecycle of generation, custody, and protection. +The meta-post, **[A Taxonomy of Security Taxonomies](/programming/security-acronyms.html)**, is the expanded map. This series is the applied-crypto slice of that map. -**Knowing it's the right party — identity.** A shared secret is worthless if it's shared with an impostor. This arm is authentication (how a party proves itself) and the roots of trust + attestation that make any identity claim believable — where, ultimately, all trust bottoms out. +## The two enforcement families + +For confidentiality and integrity of data objects, there are two dominant enforcement styles: + +| Enforcement style | Core idea | Examples | +|---|---|---| +| **Self-protecting data** | Put the protection on the bytes themselves. If you lack the key, ciphertext is unreadable or tampering is detectable. | AEAD, MACs, signatures, commitments, encrypted objects | +| **Mediated access** | Put a trusted guard in front of operations. Every read/write/execute request is checked at access time. | reference monitors, ACL/RBAC/ABAC, IAM, KMS policy, OS kernels, database guards | + +Crypto is the first family. Access control is the second. Real systems almost always combine them: + +```text +ciphertext in object storage +wrapped DEK beside it +KEK in KMS/HSM +IAM policy decides who may unwrap +reference monitor enforces the decision +audit log records the use +``` + +The important distinction is **when policy binds**. + +**Crypto tends to bind early.** You encrypt to Bob's public key, issue a token with a scope, wrap a DEK for a service, or sign an artifact with a release key. The authorization decision is compiled into a key, token, ciphertext, or signature. That works offline and survives untrusted storage/transit, but revocation and context changes are hard. + +**Reference monitors bind late.** A subject requests an action on an object under some context, and a policy decision point says yes or no *now*. That is dynamic, revocable, context-aware, and auditable — but it requires an online trusted mediator. + +KMS is the hybrid pattern: crypto protects the stored bytes, and access control late-binds use of the decryption key. + +## Kerckhoffs's split: keys vs algorithms + +Kerckhoffs's principle says the design should remain secure even when the adversary knows the system; only the key is secret. So the first two mechanism posts split crypto into its two halves: + +```text +secret authority material: keys +public transformation code: algorithms +``` + +A cipher, MAC, signature scheme, KDF, or handshake pattern is public machinery. A key is the authority to make that machinery do something security-relevant: decrypt, authenticate, sign, unwrap, derive, attest, or delegate. Much of applied crypto is really key engineering: where the key comes from, who may use it, whether it can be extracted, how it rotates, and what root of trust says it belongs to the right party. + +## The data-state axis + +The same concern changes shape depending on where the data is: + +| Concern | At rest | In transit | In use | +|---|---|---|---| +| **Secrecy** | envelope encryption, disk encryption, read-control | TLS, WireGuard, E2EE | process isolation, TEEs, minimization | +| **Correctness** | MACs, signatures, hash trees, write-control | AEAD, sequence numbers, transcript hashes | measured boot, sandboxing, verified code | +| **Origin** | signed artifacts, package provenance, KMS caller identity | certificates, mTLS, passkeys, signed tokens | workload identity, enclave identity, device identity | +| **Attribution** | audit logs, signed releases | signed requests, non-repudiable messages | tamper-evident logs, measured execution | +| **Unlinkability** | metadata minimization, private indexes | padding, mixnets, onion routing | PIR, query privacy, local processing | +| **Uptime** | backups, recovery, key escrow tradeoffs | rate limits, cookies, retries | redundancy, failover, resource isolation | + +Availability is deliberately not a major crypto topic. Crypto can support availability indirectly — for example by authenticating admission-control cookies or protecting backups — but it mostly *subtracts* from availability: expensive handshakes enable DoS, lost keys destroy access, and ransomware is confidentiality turned against you. The [availability appendix](/programming/availability.html) gives that leg its own short treatment — the capability/policy/mechanism breakdown of rate-limiting, redundancy, backup/recovery, and incident response — for the places later articles need to point to it. ## The articles -1. **[The Changing Internet Threat Model](/programming/threat-model.html)** *(prologue)* — the motivation: forty years of the assumed adversary migrating from the wire, to identity binding, to the authenticated counterparty itself. The lens for everything that follows. -2. **[Cryptographic Primitives](/programming/crypto-primitives.html)** — PRFs vs PRPs, block vs stream ciphers, AEAD, and MACs and signatures. The recurring punchline: one primitive (a PRF) underlies encryption, authentication, key derivation, and randomness alike. -3. **[Key Exchange & Secure Channels](/programming/secure-channels.html)** — establishing the shared secret: the four tiers of channel establishment, from a shared symmetric key up to fully-negotiated TLS, with HPKE / TLS 1.3 / WireGuard / Signal as worked examples. -4. **[Authentication](/programming/authentication.html)** — knowing it's the right party: the 50-year arc from plaintext passwords to passkeys (and OAuth's parallel 1.0 → 2.0 → GNAP arc), read as one long story of moving the root of trust out of the server's hands. -5. **[Keys](/programming/keys.html)** — the life of a key: the entropy stack that seeds it, where it lives (device vs server, Secure Enclave vs HSM), and envelope encryption. -6. **[Roots of Trust & Attestation](/programming/roots-of-trust-and-attestation.html)** — where every chain of trust terminates: out-of-band anchors, then the hardware mechanism that *proves* a key lives behind one (TPMs, confidential VMs, vTPMs, cloud attestation, via Red Hat's REMITS lens). Ends with a series capstone — everything a secure channel still *doesn't* give you. +1. **[Threat Models Across Data States](/programming/threat-model.html)** — the threat layer of the cube: data in transit, at rest, and in use. The old internet story remains, but becomes one section of a larger map. +2. **[Keys: Secret Material and Authority](/programming/keys.html)** — the secret half of crypto mechanisms: entropy, custody, lifecycle, key policy, KMS/HSMs, Secure Enclaves, TPMs, DEK/KEK, rotation, revocation, recovery. +3. **[Algorithms: Public Crypto Mechanisms](/programming/crypto-primitives.html)** — the public half: PRFs/PRPs, hashes, KDFs, CSPRNGs, AEAD, MACs, signatures, freshness, misuse resistance, key commitment. +4. **[Authorization, Policy, and Reference Monitors](/programming/authorization-policy-reference-monitors.html)** — the missing non-crypto half: authentication vs authorization vs audit, policy as a predicate over an access event, ACL/RBAC/ABAC, Bell–LaPadula/Biba, PEP/PDP, IAM, KMS policy, and early vs late binding. +5. **[Authentication, Certificates, Roots, and Delegation](/programming/authentication.html)** — origin and identity: proof of possession, binding statements, trust anchors, verifier policy, Web PKI, passkeys, mTLS, OAuth/OIDC/GNAP. +6. **[Data in Transit: Key Exchange & Secure Channels](/programming/secure-channels.html)** — the in-transit composition: DH/KEM + HKDF + AEAD, TLS, HPKE, Noise, WireGuard, Signal, replay/downgrade/freshness tradeoffs. +7. **[Data at Rest: Storage, KMS, and Recovery](/programming/data-at-rest-storage-kms-and-recovery.html)** — the storage composition: disk/object/database encryption, envelope encryption, KMS/HSMs, signed artifacts, backups, rollback, ransomware, deletion, recovery. +8. **[Data in Use: Isolation, Measurement, and Attestation](/programming/data-in-use-isolation-measurement-and-attestation.html)** — the processing composition: process isolation, sandboxes, TPMs, measured boot, SGX/TDX, SEV-SNP, vTPMs, cloud attestation, and secret release. +9. **[What Crypto Still Doesn't Give You](/programming/what-crypto-still-doesnt-give-you.html)** — the negative space: roots, naming, authorization, revocation, metadata, replay, group state, side channels, availability, and governance. + +Two companion pages sit outside the numbered arc — read either when you need it, not in sequence: + +- **[A Taxonomy of Security Taxonomies](/programming/security-acronyms.html)** — the full map this series is a slice of: concern × data-state × layer × modality, and where every acronym (CIA, AAA, STRIDE, BLP, …) lands on it. +- **[Availability: Uptime, Redundancy, and Recovery](/programming/availability.html)** — the one CIA leg crypto doesn't carry, given its own capability/policy/mechanism breakdown: rate-limiting, redundancy, backup/recovery, incident response. + +The throughline is simple: crypto lets data protect itself, but it never eliminates policy. It moves policy into keys, roots, tokens, measurements, and verifier decisions — and the rest of the security cube is still there waiting for you. diff --git a/_drafts/crypto-series/2026-06-05-threat-model.md b/_drafts/crypto-series/2026-06-05-threat-model.md index ac31594..178dd84 100644 --- a/_drafts/crypto-series/2026-06-05-threat-model.md +++ b/_drafts/crypto-series/2026-06-05-threat-model.md @@ -1,71 +1,245 @@ --- -title: "The Changing Internet Threat Model" -series: "Applied Crypto, Prologue" +title: "Threat Models Across Data States" +series: "Applied Crypto, Part 1" series_url: "/programming/crypto-series-intro.html" category: programming date: 2026-06-05 --- -All the machinery in this series — keys, channels, authentication, attestation — exists to defend against an adversary. The interesting question is *where* you assume that adversary lives, and over forty years the frontier has moved. +All the machinery in this series — keys, algorithms, authorization, channels, authentication, storage encryption, and attestation — exists to defend against an adversary. The interesting question is not just *how strong* the adversary is, but **where the adversary lives**. -There are three places a threat can live: +McCumber's data-state axis gives the clean split: -1. **On the wire** — the network between two endpoints. *(Now largely solved.)* -2. **In the identity binding** — *is this key really Bob's?* *(Now infrastructure.)* -3. **Inside the authenticated session** — Bob is provably Bob, and Bob is hostile. *(The open frontier.)* +```text +data in transit → the network and protocol boundary +data at rest → disks, databases, object stores, backups +data in use → processes, CPUs, OSes, hypervisors, enclaves, agents +``` -For each, it helps to separate three things that often happened *decades apart*: the **threat**, the **theory** that modelled it, and the **implementation** that finally shipped — plus the attacks that broke those implementations in between. The recurring pattern: a threat is *modelled* long before it's *defeated*, and the deployed system gets broken many times along the way. +The old version of this post was mostly the internet threat model: the adversary moved from the wire, to identity binding, to the authenticated counterparty itself. That is still the central story for **data in transit**, but it is only one face of the cube. Stored data and running computation have their own threat models. + +## Cube coordinates + +```text +Concerns: + secrecy, correctness, origin, attribution, uptime, unlinkability + +Data states: + at rest | in transit | in use + +Reasoning layer: + threat + +Control modality: + technology threats + process/human threats +``` + +## Adversary vocabulary + +A threat model first names the adversary's powers. + +Along behavior: + +- **Passive** — observes only. +- **Active** — drops, modifies, injects, reorders, replays. +- **Crash / omission / Byzantine** — distributed-systems vocabulary for nodes that halt, omit messages, or behave arbitrarily. + +Along resources: + +- **Computationally bounded** — cannot break assumed-hard problems. +- **Quantum** — breaks RSA/ECC assumptions via Shor, but not symmetric crypto at equivalent strength or post-quantum schemes. +- **Unbounded** — information-theoretic adversary. + +Along targeting: + +- **Static** — targets chosen in advance. +- **Adaptive** — corrupts new targets as it learns. +- **Mobile** — corruptions come and go over time. + +And along location: + +- **Wire attacker** — controls the network. +- **Storage attacker** — gets copies of stored bytes or controls storage service behavior. +- **Execution attacker** — controls or observes the environment where plaintext is processed. +- **Participant attacker** — is an authenticated party behaving maliciously. + +Dolev–Yao is one coordinate in that space: an active attacker who owns the network while endpoints are honest. Modern systems often need the stronger stance: assume a strong, adaptive adversary at *every* boundary, including authenticated participants. + +![Parallel-coordinates plot of the adversary model: four orthogonal axes — behaviour (passive→crash→omission→Byzantine), compute (bounded→quantum→unbounded), targeting (static→adaptive), and mobility (non-mobile→mobile) — each applying to the wire or a corrupted node, with a passive eavesdropper, Dolev–Yao, a quantum harvest-now-decrypt-later attacker, and a mobile Byzantine node drawn as polylines](/assets/threat-model/adversary-axes.svg) + +## 1 · Data in transit: the internet threat model + +Data in transit is the classic crypto story. Over forty years, the assumed internet adversary migrated through three layers: + +1. **On the wire** — the network between two endpoints. +2. **In the identity binding** — *is this key really Bob's?* +3. **Inside the authenticated session** — Bob is provably Bob, and Bob is hostile. + +For each, separate three things that often happened decades apart: the **threat**, the **theory** that modeled it, and the **implementation** that finally shipped. ![Timeline of internet threat models across three layers — the wire, identity binding, and inside the session — from 1976 to today, showing each threat modelled years before it was defeated](/assets/threat-model/threat-model-timeline.svg) -## 1 · On the wire: the network between endpoints +### 1.1 · On the wire -**Threat.** A passive eavesdropper reads your traffic; an active attacker drops, modifies, and injects it (a man-in-the-middle). +**Threat.** A passive eavesdropper reads your traffic; an active attacker drops, modifies, injects, reorders, and replays it. -**Theory.** [Dolev and Yao][dolev-yao] (1983) formalized exactly this adversary — full control of the network — and that model is still the one we use today. Two decades later, [RFC 3552][rfc3552] (2003) made it the IETF's *default*: assume the endpoints are honest and the wire is hostile. Note the gap — the *model* of the wire attacker predates a deployed protocol that actually beats it by decades. +**Theory.** [Dolev and Yao][dolev-yao] (1983) formalized this adversary — full network control — and [RFC 3552][rfc3552] (2003) made it the IETF default: assume endpoints are honest and the wire is hostile. -**Implementation — and the setup/usage split.** SSL (1995) → TLS 1.0 (1999) → TLS 1.2 with AEAD (2008) → TLS 1.3 (2018). The model was clear the whole time; the *implementations* leaked for twenty years. But the key realization is that a channel has two phases with completely different security stories: +**Implementation.** SSL (1995) → TLS 1.0 (1999) → TLS 1.2 with AEAD (2008) → TLS 1.3 (2018). The model was clear long before the deployed protocol was clean. -- **Setup (the handshake) is the battleground.** The handshake negotiates versions and ciphersuites, and that negotiation is the soft underbelly: [FREAK and Logjam][logjam] (2015) downgrade a victim onto deliberately-weak export crypto without breaking any primitive. This is why cryptographic agility is now seen as a footgun — and why the negotiation-free designs from the [channels post](/programming/secure-channels.html) (Noise, WireGuard) sidestep the entire downgrade class by baking the ciphersuite into the protocol name rather than negotiating it. (Orthogonally, Heartbleed (2014) was a memory bug in OpenSSL — the library is its own attack surface, separate from the protocol.) -- **Usage (the record phase) is solved.** Once a key is established and you protect bytes with an AEAD, steady-state traffic is essentially unbreakable — there is nothing left for a wire attacker to do. The historical record-layer attacks (BEAST 2011, CRIME 2012, Lucky 13 2013, POODLE 2014) were all against *pre-AEAD* constructions — CBC modes and TLS-level compression — and AEAD plus TLS 1.3 retired them. The symmetric cipher underneath matured the same way: [DES][des] (standardized 1977) shipped with a suspiciously short 56-bit key and was brute-forced by 1998, and the open [AES][aes] competition (2001) replaced it — public scrutiny as the antidote to a quietly-weakened standard, the same openness-vs-subversion theme as the Dual_EC RNG in §3. (The remaining caveats are precise ones the [primitives post](/programming/crypto-primitives.html) covers: key-commitment, RUP, side channels.) +A channel has two phases with different threat surfaces: -So the wire is the *solved* part: modern AEAD usage is airtight, and TLS 1.3 hardened setup by amputating most of the legacy negotiation those attacks rode in on. Which is precisely why the threat moved on. +- **Setup is the battleground.** The handshake negotiates versions, ciphersuites, identities, keys, and transcript binding. Downgrade attacks like [FREAK and Logjam][logjam] exploit negotiation rather than breaking primitives. This is why modern fixed-suite designs like Noise and WireGuard intentionally minimize agility. +- **Usage is mostly solved.** Once a fresh session key exists and traffic is protected by an AEAD, the steady-state wire attacker has little left to do. The historical record-layer attacks — BEAST, CRIME, Lucky 13, POODLE — were attacks on pre-AEAD modes, compression, or downgradeable legacy. -## 2 · In the identity binding: *is this key really Bob's?* +So for in-transit content, the modern answer is clear: -**Threat.** A perfectly secure channel to the *wrong party*. The attacker breaks no crypto at all — he hands you his own public key, and you faithfully encrypt everything to him. +```text +key establishment + endpoint authentication + AEAD records +``` -**Theory.** [Diffie–Hellman][dh] (1976) gave us public keys, but a key is not a name. *Binding* a given key to the right identity — proving this key really is `example.com`'s — is a separate, unglamorous problem, and exactly the one PKI exists to solve. (That's distinct from *naming*: whether a human-meaningful name can simultaneously be unforgeable and decentralized is a further trilemma — Zooko's triangle — which shows up a layer up, in the [capstone](/programming/roots-of-trust-and-attestation.html).) +### 1.2 · In the identity binding -**Implementation (and its breaks).** [PKI][web-pki] — X.509, certificate authorities, the Web PKI (1990s–2000s) — turned identity binding into infrastructure, so each protocol stopped solving "who am I talking to" from scratch. Its failures are about trusting the wrong *issuer*: the Comodo and DigiNotar CA compromises (2011) minted valid certificates for domains they had no business signing, which is what drove [Certificate Transparency][ct] (2013+). Crucially, PKI solved *impersonation* — and *only* impersonation. It says nothing about whether the correctly-identified party is honest, which is exactly the next threat. +**Threat.** A secure channel to the wrong party. The attacker breaks no cryptography; he hands you his own public key and you faithfully encrypt everything to him. -## 3 · Inside the session: the authenticated counterparty itself +**Theory.** [Diffie–Hellman][dh] (1976) gave public keys, but a key is not a name. Binding a key to an identity — proving this key really is `example.com`'s — is separate from key exchange. -**Threat.** Bob is provably Bob — every signature verifies, the channel is encrypted — and Bob is hostile. The dangerous actor is a *legitimate, authenticated* participant. +**Implementation.** PKI — X.509, certificate authorities, the Web PKI — turned identity binding into infrastructure. Its failures are about trusting the wrong issuer: Comodo and DigiNotar (2011) minted valid certificates for domains they had no business signing, which drove [Certificate Transparency][ct]. -**Theory.** This was modelled startlingly early. [Lowe's attack][ns-lowe] on the Needham–Schroeder authentication protocol (1995) showed that with *perfect* crypto and *perfect* key binding, a legitimate participant — Mallory, holding his own real key pair — can still subvert a protocol by exploiting its *role structure*, relaying messages to impersonate Alice to a third party. The lesson: the channel and the keys can be flawless and the protocol still broken. The field had its hardest threat in hand in 1995 — and then shelved it for twenty years, because a secure channel plus PKI *felt* like enough. [Arkko's draft][arkko] (2019) re-opened it formally: the new baseline should be "the *implementing* end-system isn't compromised, but the other parties may be." +PKI solved impersonation, not honesty. It says this is really `example.com`; it does not say `example.com` is safe, authorized, uncompromised, or benevolent. -And it was never only theoretical. A hostile endpoint has been part of the internet since its infancy: the [Morris worm][morris] (1988) turned thousands of legitimate hosts into attackers overnight by exploiting buffer overflows and weak passwords — the first worm to hit the early internet at scale, though the self-replicating idea traces back to the benign Creeper program on the ARPANET in 1971. By the time RFC 3552 wrote down "endpoints are not compromised" in 2003, that had been a convenient fiction for fifteen years. +### 1.3 · Inside the authenticated session -Arkko's sharper point is that the cryptographic endpoints often aren't the *real* ends at all. A CDN terminates your TLS, so the "server" you share a key with isn't the origin. And a [delegated-authorization flow like OAuth](/programming/authentication.html) is a triangle, not a line — it deliberately splits a trusted server-to-server *back channel* from a browser-mediated *front channel*, because those legs face different attackers (front-channel authorization-code interception is exactly why PKCE exists). Every delegate and intermediary is one more authenticated party you're trusting, so the two-party "secure channel" is, at internet scale, a convenient fiction. +**Threat.** Bob is provably Bob — every signature verifies, the channel is encrypted — and Bob is hostile. The dangerous actor is a legitimate participant. -**Implementation (the live frontier).** This is the unsolved layer. [Confidential computing and attestation](/programming/roots-of-trust-and-attestation.html) try to defend against malicious *infrastructure* — a hostile cloud host running your workload. But the authenticated counterparty is increasingly your own software: the [xz backdoor][xz-backdoor] (2024) was a trusted maintainer who spent two years earning commit rights and then shipped a backdoor, and malware on [npm, PyPI, and the AUR][aur-malware] is now routine; even the primitive itself can be subverted ([Dual_EC_DRBG][dual-ec]). And LLMs change the economics: they cheapen the attacks whose rarity used to bound the model, and an AI agent acting with your credentials under a prompt-injected instruction is *exactly* Lowe's legitimate-participant-gone-rogue — now running inside your own tooling. +**Theory.** [Lowe's attack][ns-lowe] on Needham–Schroeder (1995) showed that perfect crypto and perfect key binding can still leave a protocol broken. A legitimate participant can exploit role structure and relay messages to cause another party to misinterpret who said what. -## Aside - Adversary Model +[Arkko's draft][arkko] (2019) reopened the point for the internet architecture: the baseline should no longer be "endpoints are honest." The implementing endpoint may be uncompromised, but the other participants, delegates, intermediaries, or authenticated counterparties may be adversarial. -The wire attacker this section opens with is just one point in a much bigger space. An [adversary][adversary-crypto] is classified along orthogonal axes: *passive* (eavesdrop only) vs *active* (deviate, drop, inject); *computationally bounded* vs unbounded; *static* vs **adaptive** (picks new targets as it learns); *non-mobile* vs *mobile* (corruptions come and go over time). Dolev–Yao is one coordinate in that space — an active, bounded attacker who owns the network — and its computational cousin is the IND-CPA / IND-CCA game in [Cryptographic Primitives](/programming/crypto-primitives.html). The [distributed-systems literature][adversary-models] populates the rest, classifying corruption of *participants* (passive → crash → omission → **Byzantine**) rather than of the wire. The engineering stance that falls out is **zero-trust**: assume a strong, adaptive adversary at *every* boundary and trust nothing implicitly — which stops being paranoia the moment you accept layer 3 below, where an authenticated participant can itself be hostile (and where Byzantine fault tolerance, threshold crypto, and blockchains live). +**Implementation frontier.** OAuth already admits the channel is a triangle: a browser-mediated front channel and a server-to-server back channel face different attackers. CDNs terminate TLS before the origin. Service meshes authenticate workloads that may still be buggy or hostile. AI agents act with valid credentials under prompt-injected instructions. The threat has moved inside the set of authenticated parties. -Those axes don't fit on a plane, so the cleanest way to read an adversary is as a *polyline* crossing one axis per feature (a [parallel-coordinates][parallel-coords] plot): the higher it rides, the stronger the attacker. Two things are worth stressing. First, the axes are orthogonal to *what* is attacked — an *adaptive*, *mobile*, *Byzantine* adversary describes a set of corrupted nodes as readily as the wire. Byzantine-on-the-wire is just active injection à la Dolev–Yao (benign drops and noise sit lower, as crash/omission), and *mobile* corruption that comes and goes is exactly what forces proactive secret-sharing/recovery. Second, *unbounded* compute has a practically important midpoint: a **quantum** adversary is unbounded only *with respect to today's elliptic-curve and RSA assumptions* (via Shor) — not against symmetric crypto or post-quantum schemes — which is why "harvest now, decrypt later" is a passive attacker on a quantum timer. +This is the bridge from secure channels to authorization, policy, and attestation: authentication proves which principal acted; it does not prove the action should be trusted. -![Parallel-coordinates plot of the adversary model: four orthogonal axes — behaviour (passive→crash→omission→Byzantine), compute (bounded→quantum→unbounded), targeting (static→adaptive), and mobility (non-mobile→mobile) — each applying to the wire or a corrupted node, with a passive eavesdropper, Dolev–Yao, a quantum harvest-now-decrypt-later attacker, and a mobile Byzantine node drawn as polylines](/assets/threat-model/adversary-axes.svg) +## 2 · Data at rest: the storage threat model + +Data at rest is not a weaker version of data in transit. The adversary gets time, copies, backups, and operational mistakes. + +### 2.1 · Stolen or copied storage + +**Threat.** An attacker obtains a disk, laptop, phone, EBS snapshot, database dump, S3 bucket, backup archive, VM image, or log bundle. + +**Property.** Confidentiality of stored content. + +**Mechanisms.** Full-disk encryption, object/field encryption, envelope encryption, KMS/HSM-held keys, client-side encryption. + +The crucial difference from transit: stored ciphertext may be attacked indefinitely. If the encryption scheme is password-derived, the adversary can run an offline guessing attack. If the key later leaks, old data may fall. If a quantum adversary is plausible, "harvest now, decrypt later" applies to any public-key wrapping vulnerable to future quantum breaks. + +### 2.2 · Malicious or curious storage operator + +**Threat.** The storage layer faithfully stores bytes but should not see plaintext — a cloud admin, database operator, backup provider, or compromised object-store credential. + +**Property.** Confidentiality from the storage operator; integrity against modification or swap. + +**Mechanisms.** Client-side encryption, AEAD associated data, signatures, Merkle trees, transparency logs, KMS separation of duties. + +This is where "encryption at rest" can be misleading. Server-side encryption by the same cloud account protects against lost disks and some infrastructure failures; it may not protect against the service that also holds the keys. Client-side encryption moves the trust boundary upward, but also moves key management burden to the client. + +### 2.3 · Rollback and stale state + +**Threat.** An attacker restores an old but valid encrypted state: last month's database, a previous authorization policy, an old package index, a stale keyset. -TODO: related with content of this thread: https://x.com/ittaia/status/2020963847134454041 +**Property.** Freshness / current integrity. + +**Mechanisms.** Version numbers, monotonic counters, signed checkpoints, append-only logs, quorum replication, transparency logs, trusted timestamps. + +AEAD proves the ciphertext was produced under the key. It does not prove this is the latest ciphertext. Storage needs its own replay defense. + +### 2.4 · Ransomware and lost keys + +**Threat.** The owner loses useful access: keys are destroyed, files are re-encrypted by malware, backups are deleted, or KMS becomes unavailable. + +**Property.** Availability and utility. + +**Mechanisms.** Backups, recovery drills, immutable retention, offline copies, key recovery/escrow policy, separation of duties. + +Crypto is double-edged here. It protects confidentiality, but a lost key is perfect denial of access. Ransomware is just unauthorized encryption plus extortion. No cipher solves the recovery policy; the answer is operational. + +## 3 · Data in use: the processing threat model + +Data in use is the hardest state because plaintext must exist somewhere. If code can compute on the data, some execution environment can see it. + +### 3.1 · Malicious process or compromised OS + +**Threat.** Another process reads memory, malware runs as the user, the OS is compromised, or a dependency executes with legitimate privileges. + +**Property.** Confidentiality and integrity of runtime state. + +**Mechanisms.** Process isolation, MMU/page tables, language sandboxes, seccomp/eBPF, containers, capability systems, least privilege, memory-safe languages, constant-time implementations. + +This is the classic access-control/reference-monitor route. The data is plaintext while used; protection comes from mediation and isolation. + +### 3.2 · Hostile hypervisor or cloud operator + +**Threat.** The infrastructure running the workload is not fully trusted: malicious host OS, hypervisor, cloud admin, or compromised management plane. + +**Property.** Confidentiality/integrity of memory and code identity despite hostile infrastructure. + +**Mechanisms.** Confidential VMs, TEEs, AMD SEV-SNP, Intel TDX/SGX, TPMs/vTPMs, measured boot, remote attestation, secret release based on measurements. + +This is where [Data in Use: Isolation, Measurement, and Attestation](/programming/data-in-use-isolation-measurement-and-attestation.html) lives. Attestation does not say "this code is good"; it says "this key is held by code with measurement M under root R." A verifier policy must still decide whether M and R are acceptable. + +### 3.3 · Side channels + +**Threat.** The protocol is logically secure, but the implementation leaks through timing, cache state, power, EM, branch prediction, speculative execution, page faults, or shared hardware. + +**Property.** Confidentiality of secrets during computation. + +**Mechanisms.** Constant-time code, masking, blinding, cache partitioning, hardware isolation, side-channel testing. + +Side channels are data-in-use attacks against the physical or microarchitectural execution channel the protocol did not model. + +### 3.4 · Supply chain and authenticated bad code + +**Threat.** The code doing the computation is itself malicious or compromised: backdoored dependency, malicious maintainer, poisoned package, compromised CI, prompt-injected agent with valid credentials. + +**Property.** Integrity of computation and intent. + +**Mechanisms.** Code signing, reproducible builds, package transparency, dependency pinning, sandboxing, least privilege, review, provenance, SLSA-style build attestations. + +This is the in-use version of the authenticated-counterparty frontier. The actor may have a valid signature, token, maintainer account, or workload identity. The protocol's role structure and policy decide whether that authenticated actor can cause harm. + +## 4 · Threats to the controls themselves + +The controls are also attack surfaces. + +| Control | Threat | +|---|---| +| RNG / entropy | weak seed, VM clone, backdoored CSPRNG like Dual_EC | +| Key storage | leaked `.env`, memory disclosure, HSM misuse, bad backup | +| Public-key binding | bad CA, stale root, key transparency failure | +| Authorization policy | overbroad IAM, confused deputy, bad scope, stale grant | +| KMS | unavailable, misconfigured, admin bypass, audit disabled | +| Attestation | compromised vendor root, stale TCB, misleading measurement, verifier accepts too much | +| Human process | rubber-stamped approval, phished admin, bad incident response | + +This is why the series separates mechanisms from policy and roots. A primitive can be sound while the key is stolen, the root is wrong, or the policy says yes to the wrong subject. ## Where the frontier is now -The story isn't that the threat *moved* on its own — it's that we kept *solving* the inner layers, so the adversary kept relocating to whatever was still open. The wire took thirty-five years to genuinely secure (Dolev–Yao's 1983 model to TLS 1.3 in 2018); identity binding became infrastructure; and what's left is the threat Lowe already saw in 1995, now at the scale of cloud providers, upstream maintainers, and AI agents — all correctly authenticated, any of them potentially hostile. The protocol's role structure, not the channel, is where the adversary lives. +The story is not that one threat replaced another. It is that each solved layer exposes the next boundary: + +```text +wire attacker → secure channels +wrong public key → PKI / authentication +stolen stored bytes → encryption / KMS +plaintext during use → isolation / attestation +valid but hostile actor → authorization / protocol design / least privilege +lost or locked data → recovery / availability / governance +``` + +The frontier today is the authenticated-but-untrustworthy participant: a cloud host, package maintainer, OAuth client, service workload, AI agent, or enclave measurement that verifies correctly but may still do the wrong thing. -That's the lens for the rest of the series: each post defends a *different* assumed adversary, and the frontier today is the authenticated-but-untrustworthy counterparty — which is exactly what [attestation](/programming/roots-of-trust-and-attestation.html) tries to pin down. +That's the lens for the rest of the series. Each article occupies a different part of the cube: keys and algorithms give the mechanism substrate; policy decides who may use them; authentication binds keys to identities; channels protect transit; storage protects rest; attestation tries to say something meaningful about use. ## References @@ -97,11 +271,10 @@ That's the lens for the rest of the series: each post defends a *different* assu [logjam]: https://weakdh.org/ "The Logjam Attack - weakdh.org" [arkko]: https://datatracker.ietf.org/doc/html/draft-arkko-arch-internet-threat-model-01 "An Internet Threat Model (draft-arkko-arch-internet-threat-model-01) - Jari Arkko" [xz-backdoor]: https://en.wikipedia.org/wiki/XZ_Utils_backdoor "XZ Utils backdoor (CVE-2024-3094) - Wikipedia" -[aur-malware]: https://ioctl.fail/preliminary-analysis-of-aur-malware/ "Preliminary analysis of AUR malware - ioctl.fail" +[aur-malware]: https://ioctl.fail/preliminary-analysis-of-aur-malware/ "Preliminary analysis of AUR Malware - ioctl.fail" [dual-ec]: https://en.wikipedia.org/wiki/Dual_EC_DRBG "Dual_EC_DRBG (the backdoored RNG) - Wikipedia" [adversary-models]: https://decentralizedthoughts.github.io/2019-06-07-modeling-the-adversary/ "Modeling the Adversary - Decentralized Thoughts" -[adversary-crypto]: https://en.wikipedia.org/wiki/Adversary_(cryptography) "Adversary (cryptography) - Wikipedia" -[parallel-coords]: https://en.wikipedia.org/wiki/Parallel_coordinates "Parallel coordinates - Wikipedia" +[adversary-crypto]: https://en.wikipedia.org/wiki/Adversary_(cryptography) "Adversary - Wikipedia" [des]: https://en.wikipedia.org/wiki/Data_Encryption_Standard "Data Encryption Standard - Wikipedia" [aes]: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard "Advanced Encryption Standard - Wikipedia" [morris]: https://en.wikipedia.org/wiki/Morris_worm "Morris worm - Wikipedia" diff --git a/_drafts/crypto-series/2026-06-06-crypto-primitives.md b/_drafts/crypto-series/2026-06-06-crypto-primitives.md index d05c9b9..7040005 100644 --- a/_drafts/crypto-series/2026-06-06-crypto-primitives.md +++ b/_drafts/crypto-series/2026-06-06-crypto-primitives.md @@ -1,12 +1,34 @@ --- -title: "Cryptographic Primitives" -series: "Applied Crypto, Part 1" +title: "Algorithms: Public Crypto Mechanisms" +series: "Applied Crypto, Part 3" series_url: "/programming/crypto-series-intro.html" category: programming -date: 2026-06-06 +date: 2026-06-07 --- -Cryptographic primitives are the atoms everything else is built from. The same machinery serves confidentiality, authentication, key derivation, and randomness alike — the recurring punchline below is that a single primitive (a PRF) is hiding behind most of them. +Kerckhoffs's principle splits crypto into **secret key material** and **public algorithms**. The previous post covered the keys; this one covers the public machinery that turns keys into confidentiality, integrity, origin, attribution, derivation, and randomness. + +```text +public algorithm + secret key + usage contract = crypto mechanism +``` + +These primitives are the atoms everything else is built from. The recurring punchline below is that a single shape — a PRF — hides behind stream encryption, MACs, key derivation, CSPRNGs, and deterministic signing nonces. + +## Cube coordinates + +```text +Concerns: + secrecy, correctness, origin, attribution, freshness, partial unlinkability + +Data states: + at rest | in transit | in use + +Reasoning layer: + mechanism + +Control modality: + technology +``` ## Primitives: PRFs vs PRPs @@ -74,7 +96,7 @@ In the formal vocabulary: bare confidentiality only buys you IND-CPA (security a **What plain AEAD still doesn't give you: robustness.** Two gaps bite real systems, and neither is implied by "authenticated encryption." First, **[misuse-resistance][ae-wiki]**: vanilla AES-GCM fails *catastrophically* if a nonce ever repeats — a single reuse can leak the authentication key — so misuse-resistant AE (SIV, AES-GCM-SIV; the [tweakable modes](#tweakable-encryption) above) was designed to degrade gracefully instead, leaking at worst that two identical messages were sent. It was a headline goal of the [CAESAR competition][caesar] (2014–2019), the open AEAD bake-off that, like the AES competition before it, pushed the field past "GCM is fine." Second, **key-commitment**: AEAD guarantees a ciphertext wasn't tampered with, but *not* that it's bound to a single key — AES-GCM and ChaCha20-Poly1305 are *non-committing*, so an attacker can craft one ciphertext that decrypts and verifies under several different keys. That's the engine behind **partitioning-oracle attacks** (which broke Shadowsocks proxies and speed up password/PAKE guessing), and it bites password-based encryption, multi-recipient and key-rotation schemes, JWE, and age; the fix is a **committing AEAD**, an active area only in the last few years. (A related sharp edge: **RUP** — release of unverified plaintext — emit decrypted bytes *before* the tag verifies and you've handed plaintext to exactly the active attacker AEAD was meant to stop.) The lesson is the same either way: "authenticated encryption" is a precise guarantee — confidentiality and integrity, full stop — not a general-purpose seal of safety. -**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. +**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 [Data in Use / Attestation](/programming/data-in-use-isolation-measurement-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**. diff --git a/_drafts/crypto-series/2026-06-07-secure-channels.md b/_drafts/crypto-series/2026-06-07-secure-channels.md index aa2470d..9248699 100644 --- a/_drafts/crypto-series/2026-06-07-secure-channels.md +++ b/_drafts/crypto-series/2026-06-07-secure-channels.md @@ -1,18 +1,56 @@ --- -title: "Key Exchange & Secure Channels" -series: "Applied Crypto, Part 2" +title: "Data in Transit: Key Exchange & Secure Channels" +series: "Applied Crypto, Part 6" series_url: "/programming/crypto-series-intro.html" category: programming -date: 2026-06-07 +date: 2026-06-10 --- -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.) +This is the **data-in-transit** chapter: how two parties establish keys over a hostile network and use those keys to protect bytes in motion. -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).) +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 hard part is not record protection; it is establishing the shared secret, binding it to the right peer, and preventing replay/downgrade/misbinding during setup. -Any real system has to answer two orthogonal questions for every byte that moves: who is this from (identity) and who else can see it (data protection). Both questions get answered at multiple layers, and the layers compose. -1. Identity layers: TLS server cert (transport), mTLS client cert or app-layer login — password/passkey/OAuth (request), session token — cookie/JWT (subsequent requests in a session), workload identity for service-to-service. -2. Data-protection layers: TLS record layer (in transit), payload encryption inside queues/caches (in motion-but-at-rest), DEK/KEK envelope encryption (storage), E2E encryption between end users (server-as-relay). +## Cube coordinates + +```text +Concerns: + secrecy, correctness, origin, freshness, partial unlinkability + +Data state: + in transit + +Reasoning layers: + threat → property → capability → policy → mechanism + +Control modality: + mostly technology, with process around roots/cert lifecycle +``` + +The in-transit ladder looks like: + +```text +Threat: + eavesdropping, tampering, replay, MITM, downgrade, wrong-party key binding + +Property: + confidentiality, integrity, peer authentication, freshness, forward secrecy + +Capability: + key establishment + secure channel + endpoint authentication + +Policy: + which roots, peer names, ciphers, protocol versions, 0-RTT rules, client-auth rules? + +Mechanism: + DH/KEM, HKDF, AEAD, certificates, TLS, HPKE, Noise, WireGuard, Signal +``` + +Any real system answers identity and data-protection questions at multiple layers: + +1. **Identity layers** — TLS server cert, mTLS client cert, app-layer login, OAuth token, session cookie/JWT, workload identity. +2. **Data-protection layers** — TLS records in transit, payload encryption inside queues/caches, envelope encryption for storage, E2E encryption between users. + +This article is about the transport/channel layer. Authentication, authorization, storage, and in-use protections get their own posts. ## The bootstrap: which channel do you start with? @@ -30,13 +68,9 @@ A typical client-server session looks like: 4. (app-layer) Session establishment: server issues a short-lived token (cookie, JWT) so subsequent requests skip step 2. 5. In-use and At-rest Data protection: now you can layer authenticated encryption for data at rest (DEK/KEK), for queue payloads, for cached blobs, or for end-to-end-encrypted content between users. -TODO: actually at both wire and app-layers, we should separate channel establishment from authentication. They are often combined, but don't have to be, and are conceptually separate: -> Generally, session key establishment protocols perform authentication. A notable exception is Diffie-Hellman, as described below, so the terms authentication protocol and session key establishment protocol are almost synonymous. -> https://book.systemsapproach.org/security/authentication.html - -Note that mTLS can be used to authenticate the user in step 1. See https://docs.secureauth.com/iam/blog/sender-constrained-access-tokens-mtls-vs-dpop +Key establishment and authentication are usually fused, but conceptually separate. Raw Diffie–Hellman establishes a shared key while authenticating nobody. TLS 1.3 establishes an ephemeral shared key and authenticates the server by signing the transcript with a certificate key. WireGuard authenticates with static-key DH instead of signatures. mTLS can authenticate the client in the transport layer; OAuth or passkeys can authenticate the user later inside the already-secure channel. -A *session* and a *channel* aren't the same thing, and the walkthrough above spans both. A **channel** is a transport-level construct that protects bytes between two endpoints — one TLS connection. A **session** is the application's notion of a *continuing relationship* ("you're still logged in"), carried by a cookie or token, outliving any single channel and surviving reconnects. The old [OSI session layer][osi-session] tried to standardize this and never cleanly fit TCP/IP, but the split is real: channels protect data *in transit*; sessions maintain *identity over time*. +A *session* and a *channel* also aren't the same thing. A **channel** is a transport-level construct that protects bytes between two endpoints — one TLS connection. A **session** is the application's notion of a continuing relationship ("you're still logged in"), carried by a cookie or token, outliving any single channel and surviving reconnects. The old [OSI session layer][osi-session] tried to standardize this and never cleanly fit TCP/IP, but the split is real: channels protect data *in transit*; sessions maintain identity over time. ## Channel Establishment @@ -51,7 +85,7 @@ Tier 3 — Interactive channel, fixed ciphersuite. Both parties online, simple h Tier 4 — Interactive channel, full negotiation. TLS. Versioned, ciphersuite-agile, extension-rich, certificate-based PKI integration. The Swiss Army knife when you need to talk to arbitrary clients on the open internet. -TODO: The arrow direction is the wrong message. "More interactive = more advanced" is the implicit story the 1D axis tells, and modern crypto thinking is largely the opposite. The history of TLS is the history of negotiation-driven attacks: FREAK, Logjam, POODLE, BEAST, CRIME, ROBOT, downgrade-dance variants — most of those exploit version or ciphersuite negotiation, not the underlying primitives. Cryptographic agility is now widely seen as a footgun. WireGuard's whole pitch is "we ripped out the negotiation." Trevor Perrin designed Noise so the ciphersuite is part of the protocol name (e.g. Noise_IK_25519_ChaChaPoly_BLAKE2s), never negotiated. DJB has written at length about why he considers algorithm agility harmful. So the honest reading of that axis is: rightward = more flexible at the cost of more attack surface and a harder security proof. Not better — more compatible with the open internet, which is a different thing. +The tiers are not a progress axis. Rightward means more interactivity and flexibility, not "more secure." TLS needs negotiation because arbitrary clients meet arbitrary servers on the open internet. Noise and WireGuard deliberately avoid that flexibility because negotiation is attack surface: FREAK, Logjam, POODLE, BEAST, CRIME, ROBOT, and downgrade-dance variants exploited version/ciphersuite flexibility more than primitive weakness. The modern bias is: bake in the suite when you can; negotiate only when interoperability forces you. ![image](/assets/secure-channels/protocol-property-matrix.png) @@ -77,9 +111,9 @@ Signal's choreography is denser because it has to be async. Alice can't ECDH aga ## AEAD -Once a handshake has produced session keys, the bytes themselves are protected with an AEAD (AES-GCM, ChaCha20-Poly1305). The mechanics — how an AEAD fuses a stream-cipher mode with a MAC so you can't forget the integrity half — live in the MACs & Signatures section of [Cryptographic Primitives](/programming/crypto-primitives.html). +Once a handshake has produced session keys, the bytes themselves are protected with an AEAD (AES-GCM, ChaCha20-Poly1305). The mechanics — how an AEAD fuses a stream-cipher mode with a MAC so you can't forget the integrity half — live in the MACs & Signatures section of [Algorithms: Public Crypto Mechanisms](/programming/crypto-primitives.html). -Establishing the key is the easy, well-understood part. Everything a secure channel *doesn't* give you — naming, authorization, revocation, replay protection across sessions, group keying — is surveyed in the series capstone at the end of [Roots of Trust & Attestation](/programming/roots-of-trust-and-attestation.html). +Establishing the key is the easy, well-understood part. Everything a secure channel *doesn't* give you — naming, authorization, revocation, replay protection across sessions, group keying, storage, in-use secrecy, metadata privacy — is surveyed in the series capstone, [What Crypto Still Doesn't Give You](/programming/what-crypto-still-doesnt-give-you.html). ## Side channels diff --git a/_drafts/crypto-series/2026-06-08-authentication.md b/_drafts/crypto-series/2026-06-08-authentication.md index 9866456..ca57a9e 100644 --- a/_drafts/crypto-series/2026-06-08-authentication.md +++ b/_drafts/crypto-series/2026-06-08-authentication.md @@ -1,14 +1,58 @@ --- -title: "Authentication" -series: "Applied Crypto, Part 3" +title: "Authentication, Certificates, Roots, and Delegation" +series: "Applied Crypto, Part 5" series_url: "/programming/crypto-series-intro.html" category: programming -date: 2026-06-08 +date: 2026-06-09 --- -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 is the **origin** concern: proving which principal, key, device, workload, service, or code environment a statement comes from. It is not the same as authorization. Authentication says **who**; policy decides **may they**. -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. +In the cube, authentication appears in every data state: + +```text +in transit: server certs, mTLS, passkeys, signed requests +at rest: signed artifacts, package provenance, KMS caller identity +in use: workload identity, device identity, enclave/TEE identity +``` + +## Cube coordinates + +```text +Concerns: + origin, attribution, access bridge + +Data states: + at rest | in transit | in use + +Reasoning layers: + capability → policy → mechanism + +Control modality: + technology + process/human roots and recovery +``` + +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" — *is this key really Bob's?* — but left the mechanism for here. + +## The authentication stack + +Every authentication system has the same shape: + +```text +proof of possession + + binding statement + + trust anchor + + verifier policy +``` + +| Piece | Example | +|---|---| +| **Proof of possession** | signature over a challenge, TLS transcript signature, WebAuthn assertion, DPoP proof | +| **Binding statement** | certificate, account registration record, OIDC token, attestation quote, signed package metadata | +| **Trust anchor** | CA root, IdP signing key, hardware vendor root, pinned SSH host key, package maintainer key | +| **Verifier policy** | which issuers, audiences, origins, roots, algorithms, expiry windows, scopes, or measurements are acceptable | + +A signature only proves possession of a private key. Authentication requires a binding from that key to a principal and a policy saying the binding is acceptable for this purpose. ## The actors @@ -22,7 +66,7 @@ Authentication is a conversation between a handful of parties — a client, the 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. +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. That baked-in root is not derived by crypto; it is verifier policy shipped as software. #### Phase 1: Self-signed & TOFU (pre-PKI) @@ -42,7 +86,7 @@ Blind global trust broke exactly as you'd expect: the Comodo and DigiNotar compr #### 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. +Revocation is PKI's chronic weak spot — the "stale" problem from the [capstone](/programming/what-crypto-still-doesnt-give-you.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? @@ -51,9 +95,9 @@ Hierarchical CAs aren't the only way to bind a key to an identity. Each alternat - **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. +- **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 — though a blockchain genesis hash is still 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. +These map cleanly onto Zooko's triangle (secure / human-meaningful / decentralized — pick two, revisited in the [capstone](/programming/what-crypto-still-doesnt-give-you.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. @@ -61,6 +105,28 @@ Notice the throughline — the same one the user-auth arc has below: every step 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. +## Roots as verifier policy + +A root of trust is just the top of an authentication chain that the verifier accepts without deriving it from another proof. + +```text +leaf proof → intermediate binding → root anchor → verifier policy says root is acceptable +``` + +Examples: + +- browser CA bundle for Web PKI +- pinned SSH host key for TOFU +- IdP signing key for OIDC tokens +- package maintainer or registry signing key +- CT log keys accepted by browser policy +- hardware vendor roots for TPM/TEE attestation +- blockchain genesis hash shipped in a client + +Crypto verifies signatures along the chain. Policy chooses which roots are in the trust store, what purposes they are valid for, when they expire, and how they are removed. That is why roots belong in authentication but are not themselves "cryptographic magic": they are out-of-band configuration made operational by signatures. + +Hardware attestation uses the same structure, but the authenticated principal is a computation environment rather than a person or domain. A quote says "this key/session/report is bound to code measurement M under hardware root R"; the verifier policy still decides whether R and M are acceptable. The mechanics are in [Data in Use: Isolation, Measurement, and Attestation](/programming/data-in-use-isolation-measurement-and-attestation.html). + ## History of User Authentication 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. @@ -130,7 +196,7 @@ For anything with real consequences (email, cloud storage, financial, health) For administrative or high-privilege access (cloud root, code signing, DB admin, infrastructure) — hardware tokens with attestation (YubiKey, Titan, smartcard) become appropriate. The key property isn't just stronger crypto; it's that a lost-or-compromised laptop doesn't compromise the token. You want the possession factor to live on a separate device you can physically inventory. -For server-held secrets (database encryption keys, service signing keys, pepper, OAuth client secrets) — KMS/HSM-backed envelope encryption becomes the appropriate architecture. The tradeoff is operational complexity: every service call that needs to read encrypted data has to make a KMS call, which costs latency and money, and you need to think about failure modes if KMS is unreachable. For data that isn't genuinely sensitive, plain AES with a key in config is still sometimes the right call. (The envelope-encryption mechanics are covered in [Keys](/programming/keys.html).) +For server-held secrets (database encryption keys, service signing keys, pepper, OAuth client secrets) — KMS/HSM-backed envelope encryption becomes the appropriate architecture. The tradeoff is operational complexity: every service call that needs to read encrypted data has to make a KMS call, which costs latency and money, and you need to think about failure modes if KMS is unreachable. For data that isn't genuinely sensitive, plain AES with a key in config is still sometimes the right call. (The key hierarchy is covered in [Keys](/programming/keys.html); the storage architecture is covered in [Data at Rest](/programming/data-at-rest-storage-kms-and-recovery.html).) For service-to-service authentication (backend APIs calling each other) — no human factors apply. You want mutual TLS with client certificates, or signed JWTs where the signing key lives in the HSM. Again, possession is the anchor — a workload identity tied to hardware or to a short-lived credential from a workload identity service. diff --git a/_drafts/crypto-series/2026-06-08-authorization-policy-reference-monitors.md b/_drafts/crypto-series/2026-06-08-authorization-policy-reference-monitors.md new file mode 100644 index 0000000..295b21b --- /dev/null +++ b/_drafts/crypto-series/2026-06-08-authorization-policy-reference-monitors.md @@ -0,0 +1,278 @@ +--- +title: "Authorization, Policy, and Reference Monitors" +series: "Applied Crypto, Part 4" +series_url: "/programming/crypto-series-intro.html" +category: programming +date: 2026-06-08 +--- + +Crypto protects bytes. **Policy decides who may do what.** + +Those are different questions. A signature can prove that Alice signed a request; it does not decide whether Alice is allowed to delete the database. TLS can prove you are talking to `api.example.com`; it does not decide whether this client may call `POST /refunds`. Envelope encryption can make an object unreadable without a key; it does not decide who may unwrap that key. + +This post is the non-crypto half of the enforcement story: **authorization, policy, and reference monitors**. It is where the security cube's reasoning ladder becomes explicit: + +```text +property → capability/function → policy → mechanism +``` + +For confidentiality and integrity, there are two dominant enforcement routes: + +| Route | Style | Core mechanism | +|---|---|---| +| **Self-protecting data** | Bind the decision early into keys, tokens, ciphertexts, or signatures. | crypto | +| **Mediated access** | Decide at access time by asking a trusted guard. | reference monitor / access control | + +The two routes are not rivals. Real systems compose them. + +## Cube coordinates + +```text +Concerns: + secrecy, correctness, origin, attribution, access + +Data states: + at rest | in transit | in use + +Reasoning layers: + capability → policy → mechanism + +Control modality: + technology + process, with human review around high-risk decisions +``` + +## Authentication, authorization, audit + +The classic split is: + +```text +authentication: who are you? +authorization: what may you do? +audit/accounting: what did you do? +``` + +Authentication produces a claim about identity or origin: this request came from Alice; this server controls `example.com`; this workload holds a SPIFFE identity; this enclave produced a quote with measurement `M`. + +Authorization consumes that claim and decides whether an action is allowed. + +Audit records what happened so future humans, machines, or courts can reconstruct it. + +AAA, IAAA, and Lampson's Gold Standard all live here. They are **capability/function** frameworks, not property frameworks. They answer *what kind of security service do I need?* — authenticate, authorize, account/audit — not *what counts as a violation?*. + +## Policy as a predicate over an event + +A policy is not merely a PDF in a compliance folder. In the technical sense, a policy is a predicate over an access event: + +```text +subject S may perform action A on object O under context C +``` + +Examples: + +```text +PayrollService may call KMS.Decrypt on payroll KEKs from prod VPCs. + +Alice may read her own medical record, but not Bob's. + +The release bot may sign artifacts only after CI passed on main. + +An enclave may receive the database key only if its quote contains measurement M, +TCB version >= V, and a fresh nonce from the verifier. +``` + +This is why policy is the only multi-W layer in the ladder. It is about **who** may do **what** to **which** object, **when/where/under what context**. + +## Early binding: crypto compiles policy into keys + +Crypto often makes authorization feel invisible because the policy is bound early. + +```text +encrypt to Bob's public key +issue a token with scope=read:payroll +wrap a DEK for PayrollService +sign an artifact with the release key +seal a secret to TPM PCR values +release a database password only to an enclave measurement +``` + +After that binding, the mechanism is local and simple: + +```text +key-holder can decrypt +private-key holder can sign +MAC-key holder can authenticate +public-key holder can verify +``` + +This is the power of crypto: the data can protect itself across untrusted storage and untrusted networks. The guard does not need to be online when the ciphertext is copied or the signature is verified. + +But the cost is that the decision is now hard to change. If Bob's key leaks, old ciphertexts are exposed. If Alice leaves the company, old files encrypted to her key remain decryptable unless you re-encrypt or wrapped the data keys through a revocable mediator. If a token is bearer and long-lived, whoever steals it gets the authority until expiry. Crypto gives you offline enforcement by freezing part of policy into key material. + +## Late binding: reference monitors decide at access time + +Access control binds later. A request arrives; a trusted mediator checks the current policy; only then does the operation happen. + +```text +subject S requests action A on object O under context C +PEP intercepts request +PDP evaluates policy +PEP allows or denies +logger records the decision +``` + +The classic abstraction is the **reference monitor**: an enforcement mechanism that is always invoked, tamper-proof, and small enough to reason about. Operating-system kernels, hypervisors, database permission engines, API gateways, IAM systems, KMSes, Kubernetes admission controllers, browser same-origin policy, and smart-contract VMs are all reference-monitor-shaped. + +Late binding buys dynamism: + +- revoke Alice now +- require MFA only for risky actions +- deny access outside business hours +- check device posture +- log every decrypt +- apply emergency break-glass rules +- roll out a new policy without re-encrypting every object + +The cost is that the mediator must be trusted and available. If the reference monitor is bypassed, the policy evaporates. If the KMS is down, your encrypted data may be unavailable. Late binding trades offline self-protection for online control. + +## The XACML split: PEP / PDP / PAP / PIP + +The access-control world names the pieces precisely: + +| Component | Role | +|---|---| +| **PEP** — Policy Enforcement Point | The gate. It intercepts the request and enforces allow/deny. | +| **PDP** — Policy Decision Point | The brain. It evaluates policy and returns a decision. | +| **PAP** — Policy Administration Point | The editor. It creates/stores the policies. | +| **PIP** — Policy Information Point | The context fetcher. It supplies attributes: user role, device posture, resource label, time, location, risk score. | + +That last piece matters because real policy is contextual. ABAC is the general form: authorization depends on attributes of the subject, object, action, and environment. + +## Policy models + +Different policy models are different languages for the same event predicate. + +| Model | Shape | Best for | +|---|---|---| +| **ACL** | Object lists allowed subjects/actions. | Files, buckets, simple resources. | +| **Capabilities** | Subject holds an unforgeable token granting authority. | Delegation, object capabilities, macaroons, bearer/proof tokens. | +| **DAC** | Owners decide permissions. | Unix-ish discretionary sharing. | +| **MAC** | System labels dominate user choice. | Classified systems, mandatory boundaries. | +| **RBAC** | Users get roles; roles get permissions. | Enterprises and admin consoles. | +| **ABAC** | Rules over attributes/context. | Cloud IAM, zero-trust, dynamic environments. | + +The classic formal models are more specialized: + +- **Bell–LaPadula** — confidentiality: no-read-up, no-write-down. +- **Biba** — integrity: no-read-down, no-write-up. +- **Clark–Wilson** — integrity through well-formed transactions and separation of duty. +- **Brewer–Nash / Chinese Wall** — dynamic conflict-of-interest. + +These are **policy models**. They are not mechanisms. A kernel, database, KMS, or API gateway may enforce them; the model itself is the rule-set. + +## Mechanisms that enforce policy + +A policy becomes real only when some mechanism mediates operations: + +| State | Mediated-access mechanisms | +|---|---| +| **At rest** | file permissions, database row-level security, S3 bucket policy, KMS/IAM, secret managers, backup ACLs | +| **In transit** | API gateways, mTLS authorization, OAuth scopes, sender-constrained tokens, service mesh policy | +| **In use** | MMU/page tables, process isolation, seccomp/eBPF, containers, hypervisors, enclave admission, Kubernetes admission controllers | + +Notice that several of these mechanisms use crypto internally. OAuth uses signed tokens; mTLS uses certificates; KMS uses HSM-held keys; enclaves use attestation signatures. But their *job* is policy enforcement. Crypto is a mechanism inside a larger reference-monitor-shaped system. + +## KMS as the canonical hybrid + +Envelope encryption is the pattern where the two enforcement families visibly meet: + +```text +object plaintext + encrypted under a DEK +ciphertext + wrapped DEK stored in object store +DEK wrapped under KEK +KEK lives in KMS/HSM +KMS policy decides who may unwrap +``` + +The object store does not need to be trusted with plaintext. The ciphertext is self-protecting. But decryption is late-bound by KMS policy: every unwrap can check IAM, network context, key state, quota, break-glass rules, and audit settings. + +This pattern is why "crypto vs access control" is the wrong fight. The useful architecture is: + +```text +crypto protects data when the mediator is absent or untrusted +access control governs key use when the mediator is present +``` + +## Policy around authentication and attestation + +Authentication also needs policy. A verifier must decide which proofs count. + +```text +Which CA roots are trusted? +Which certificate usages are acceptable? +Which OIDC issuers and audiences are allowed? +Which passkey origins match this relying party? +Which TPM/TDX/SEV measurements are admissible? +Which token scopes authorize this API call? +``` + +The mechanism can verify a signature. Policy decides whether that signer is acceptable for this purpose. + +This is the recurring pattern: + +```text +signature verifies ≠ signer is authorized +certificate chains ≠ this root should be trusted +attestation quote valid ≠ this measurement may see the secret +OAuth token valid ≠ this scope is enough for this operation +``` + +## Process and human controls + +McCumber's third axis shows up around policy more than anywhere else. Technical policy is surrounded by process and humans: + +- who can edit IAM policy? +- who approves production access? +- how often are grants reviewed? +- who can trigger break-glass? +- how are root keys rotated? +- how are emergency revocations handled? +- how do admins avoid rubber-stamping prompts? + +A perfect PDP cannot save a policy written by the wrong people, approved through the wrong process, or bypassed by social pressure. This is where separation of duties, change control, review ceremonies, incident response, and audit culture belong. + +## The punchline + +Policy is invariant across mechanism families: + +```text +who may do what to which object under which context? +``` + +Crypto and reference monitors differ mainly in **when** that policy decision is bound. + +```text +crypto: early binding into keys/tokens/ciphertexts/signatures +reference monitor: late binding at access time +KMS: crypto for the data, late binding for the key +``` + +Everything else in the series keeps using this distinction. Secure channels bind peers and keys early in a handshake; storage systems late-bind decryption through KMS; authentication proves identity but leaves authorization to policy; attestation verifies a measurement but still needs a verifier policy saying which measurements are acceptable. + +## References + +1. [Computer Security Technology Planning Study — Anderson Report][anderson] +2. [Protection — Butler Lampson][lampson] +3. [XACML Reference Architecture — OASIS][xacml] +4. [Bell–LaPadula Model - Wikipedia][blp] +5. [Biba Model - Wikipedia][biba] +6. [Role-Based Access Control - NIST][nist-rbac] +7. [Attribute Based Access Control - NIST SP 800-162][nist-abac] + +[anderson]: https://csrc.nist.gov/csrc/media/publications/conference-paper/1998/10/08/proceedings-of-the-21st-nissc-1998/documents/early-cs-papers/ande72.pdf "Computer Security Technology Planning Study — Anderson Report" +[lampson]: https://dl.acm.org/doi/10.1145/361011.361067 "Protection — Butler Lampson" +[xacml]: https://www.oasis-open.org/standard/xacmlv3-0/ "eXtensible Access Control Markup Language (XACML) Version 3.0" +[blp]: https://en.wikipedia.org/wiki/Bell%E2%80%93LaPadula_model "Bell–LaPadula model - Wikipedia" +[biba]: https://en.wikipedia.org/wiki/Biba_Model "Biba Model - Wikipedia" +[nist-rbac]: https://csrc.nist.gov/projects/role-based-access-control "Role Based Access Control - NIST" +[nist-abac]: https://csrc.nist.gov/publications/detail/sp/800-162/final "Guide to Attribute Based Access Control (ABAC) Definition and Considerations - NIST SP 800-162" diff --git a/_drafts/crypto-series/2026-06-09-keys.md b/_drafts/crypto-series/2026-06-09-keys.md index b7f4fb2..3b732ea 100644 --- a/_drafts/crypto-series/2026-06-09-keys.md +++ b/_drafts/crypto-series/2026-06-09-keys.md @@ -1,52 +1,243 @@ --- -title: "Keys" -series: "Applied Crypto, Part 4" +title: "Keys: Secret Material and Authority" +series: "Applied Crypto, Part 2" series_url: "/programming/crypto-series-intro.html" category: programming -date: 2026-06-09 +date: 2026-06-06 --- -A secure channel reduces "trust the network" to "trust a key" — but the key itself is real, physical secret material with a whole lifecycle. This post is about that material: what kinds exist, where its randomness comes from, where it physically lives, and how it's wrapped and protected. It's the data-protection foundation the [channel](/programming/secure-channels.html) post stands on; the question of *trusting* a key — anchoring it in a root — is the separate [Roots of Trust & Attestation](/programming/roots-of-trust-and-attestation.html) post. +Kerckhoffs's principle splits cryptography in two: + +```text +public algorithms + secret key material +``` + +The algorithm can be published, standardized, benchmarked, and attacked in public. The key is the secret authority that makes the public algorithm security-relevant. A key is what lets someone decrypt, MAC, sign, unwrap, derive, attest, or impersonate. Applied crypto is therefore mostly key engineering: where keys come from, where they live, who may use them, how they rotate, what wraps them, and what happens when they leak or disappear. + +Keys are not just a data-at-rest topic. They exist in every data state: + +| Key state | Example | Failure mode | +|---|---|---| +| **At rest** | private key on disk, HSM KEK, encrypted backup key | leaked `.env`, stolen laptop, bad backup, lost recovery key | +| **In transit** | DH public shares, wrapped DEK, recipient public key | MITM, wrong-recipient key, downgrade, bad certificate | +| **In use** | session key in RAM, signing key inside HSM, enclave sealing key | memory disclosure, side channel, malicious process, compromised OS | + +This post is the secret-material half of crypto mechanisms. The next post covers the public algorithm half. + +## Cube coordinates + +```text +Concerns: + secrecy, correctness, origin, attribution, possession, utility, availability + +Data states: + at rest | in transit | in use + +Reasoning layers: + mechanism substrate + policy object + +Control modality: + technology + process + human recovery ceremonies +``` ## A map of secret material -Two columns by location, three rows by role — every key, proof, verifier, and scrap of metadata that any of these protocols touches has a cell. +Two columns by location, three rows by role — every key, proof, verifier, and scrap of metadata that these protocols touch has a cell. ![image](/assets/keys/meta-map-secrets-data.png) -> The middle-right cell is the one that rewards reading carefully, because it's the universal 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, like the TLS transcript or DPoP's htm/htu/ath), what-for (scope — OAuth scopes, X.509 key-usage extensions), and bound-to-what (chaining — cnf claims that tie one proof to another's key). Any new auth scheme you encounter, you can read it by asking which of these five it covers and which it omits. Bearer access tokens cover identity + freshness + scope, but skip chaining and content — which is exactly the gap DPoP fills by adding cnf to the token and htm/htu/ath/jti to the per-request proof. mTLS-bound tokens fill the same gap differently, by using the TLS-layer client cert as the chaining anchor. Same problem, two solutions, both visible in the grid. +The middle-right cell is the one that rewards reading carefully, because it is the universal 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, like a TLS transcript or DPoP's `htm`/`htu`/`ath` +- **what-for** — scope: OAuth scopes, X.509 key-usage extensions +- **bound-to-what** — chaining: `cnf` claims, certificate chains, token binding + +Any auth scheme can be read by asking which of those five it covers and which it omits. Bearer access tokens cover identity + freshness + scope, but skip chaining and content. DPoP fills that gap by adding `cnf` to the token and `htm`/`htu`/`ath`/`jti` to the per-request proof. mTLS-bound tokens fill the same gap differently, using the TLS-layer client cert as the chaining anchor. -## The Entropy Stack +## The entropy stack -Before a key can be custodied or anchored, it has to be *generated* — and a key is only as unpredictable as the entropy that seeded it. Three sources feed three kinds of secret. +Before a key can be custodied or anchored, it has to be generated — and a key is only as unpredictable as the entropy that seeded it. Three sources feed three kinds of secret. ![image](/assets/keys/entropy-stack.png) -The middle track (blue) — the "normal" case for DH, session keys, random salts, IVs, any key you generate at runtime on a general-purpose computer. Your question "where does DH's seed entropy come from?" lands here. A Diffie–Hellman exchange starts when each party picks a random scalar — their ephemeral private key — and for Curve25519 that's just 32 random bytes. Those bytes come from the OS CSPRNG, which on Linux is the ChaCha20-based generator behind getrandom() and /dev/urandom (on macOS it's similar; Windows has CryptGenRandom / BCryptGenRandom). A CSPRNG is deterministic — given the same seed it produces the same output — so it's only as good as its seed. Which sends the question one level deeper. (That a CSPRNG is itself just a PRF run in counter mode is the [Cryptographic Primitives](/programming/crypto-primitives.html) view; here the question is where its *seed* comes from.) And the generator itself can be sabotaged: a *backdoored* CSPRNG like Dual_EC_DRBG emits output that looks uniform to you but is predictable to whoever chose its constants — the canonical algorithm-substitution attack, covered in the [threat model](/programming/threat-model.html). +### Human entropy: passwords + +Passwords come from human cognition, which is terrible at randomness. A user-chosen password may have 20–50 bits of actual entropy despite looking long, because humans cluster around dictionary words, common patterns, and familiar dates. + +No post-processing adds entropy that was not there. Argon2, scrypt, bcrypt, and PBKDF2 add **work factor**: each guess becomes expensive, so an offline attacker pays more per candidate. The salt is uniformly random, but the secret itself remains constrained by what fit in a human head. + +This is why the authentication story moves from password-held-by-human to private-key-held-by-device. + +### OS entropy: runtime keys + +Most runtime keys — DH scalars, session keys, salts, IVs, random nonces — come from the OS CSPRNG. + +A CSPRNG is deterministic once seeded. It stretches an entropy pool into many pseudorandom outputs. On modern systems, the seed comes from physical sources mixed by the kernel: CPU RNG instructions, interrupt timing, clock jitter, device events, dedicated TRNGs, and other platform-specific noise. The generator then whitens that noisy physical input into bytes that should be indistinguishable from random. + +A Diffie–Hellman exchange starts when each party picks a random private scalar. For Curve25519, that is essentially 32 random bytes from this stack. The security of the internet's ephemeral key exchanges bottoms out in thermodynamics and careful OS engineering. + +The generator itself is a target. A backdoored CSPRNG like Dual_EC_DRBG can emit output that looks uniform to users but is predictable to whoever knows the trapdoor. + +### Hardware entropy: non-extractable roots + +Secure Enclaves, TPMs, and HSMs have their own entropy sources and key-generation paths inside the secure boundary. When a Secure Enclave generates a passkey, or an HSM generates a root KEK, the private key is intended never to exist outside that boundary. + +This is a different trust statement from "the OS generated a key and stored it carefully." It says: + +```text +the key was generated inside hardware, +the key is non-extractable by API, +and operations happen behind that boundary +``` + +That statement may itself be attested later. + +## Key roles + +A key's role matters as much as its bytes. + +| Role | Purpose | Examples | +|---|---|---| +| **Content key** | Protect data directly. | DEK, session AEAD key, disk volume key | +| **Wrapping key** | Protect other keys. | KEK, KMS key, HSM master key | +| **Identity key** | Represent a principal. | TLS cert private key, passkey, SSH host key, SPIFFE key | +| **Signing key** | Produce publicly-verifiable statements. | package signing key, CA key, attestation key | +| **Derivation key** | Seed more keys. | HKDF extract secret, ratchet root key | +| **Sealing key** | Bind data to platform state. | TPM sealed key, enclave sealing key | +| **Recovery key** | Restore access after loss. | backup seed, escrow key, break-glass key | + +The same bitstring can be dangerous if reused across roles. Key separation exists so compromise or misuse in one role does not bleed into another. HKDF labels, TLS exporter contexts, and domain-separated signatures are all attempts to make "same root, different purpose" safe. + +## Key lifecycle + +A real key has a lifecycle: + +```text +generate → store → use → derive/wrap → rotate → revoke → destroy/recover +``` -The OS seed comes from physical entropy harvested by the kernel from several sources mixed together: CPU hardware RNG instructions (RDRAND/RDSEED on Intel/AMD, which internally use thermal noise in the silicon), timing jitter from interrupts and disk operations, keystrokes and mouse movements on desktop systems, clock skew between oscillators, and on some platforms, dedicated on-board TRNGs. The kernel hashes all of this together into an entropy pool and uses it to seed (and periodically reseed) the CSPRNG. The CSPRNG's job is whitening — taking whatever biased, potentially-correlated physical noise got sampled and producing uniformly distributed output indistinguishable from random. +Each stage has different threats. -So DH's seed ultimately comes from thermal motion of electrons (in CPU hardware RNGs), quantum tunneling noise (in some dedicated TRNGs), and the chaotic timing of physical events (interrupts, user input). The security of every DH handshake on the internet reduces, at bottom, to "thermodynamics makes the future unpredictable." Which is a genuinely beautiful fact if you sit with it. +### Generate -The right track (purple) — keys that live in hardware. Your second question: Secure Enclaves and HSMs have their own TRNGs, distinct from the OS's. These are dedicated silicon circuits — typically ring oscillators whose phase drift is sampled, or avalanche noise from biased diodes, or quantum effects in some high-end HSMs — running a hardware-entropy-driven CSPRNG entirely inside the secure boundary. When your Secure Enclave generates a new passkey, the private key is created using the Enclave's internal TRNG and never exists anywhere else. Same for the root KEK in an HSM: generated internally, never emitted. The OS isn't involved because the whole point is that even a compromised OS can't influence the key. This is why FIPS 140-3 certification requires HSMs to have their own certified entropy source. (Those hardware-held keys are exactly the "Where keys live" story below.) +Was the key created with enough entropy? Was it generated inside the intended boundary? Was the VM cloned before first boot? Did deterministic signing derive a per-message nonce safely? -The left track (coral) — passwords. The entropy source here is human cognition, which is terrible at randomness. A user-chosen password might have 20–50 bits of actual entropy despite having 60+ bits of notional key length, because humans cluster around dictionary words, common patterns, and familiar dates. No amount of post-processing can add entropy that wasn't there — information theory forbids it. What Argon2 and friends do is add work factor: a low-entropy secret is still low-entropy, but now each guess takes 100ms and 64 MB of RAM, which multiplies the attacker's cost by a billion or so. The salt alone is uniformly random (from the middle track), so the stored hash looks high-entropy from the outside, but the secret itself is still constrained by what fit in someone's head. (The full password-hashing arc — crypt, bcrypt, Argon2 — is the subject of [Authentication](/programming/authentication.html).) +### Store + +Is the key extractable? Is it on disk, in memory, in a TPM, in a Secure Enclave, in an HSM, in a cloud KMS, in a `.env` file, in a CI variable, in a password manager, in a backup? + +### Use + +Who can ask the key to operate? Is the use mediated by policy? Are operations logged? Is plaintext released outside the boundary? Are signing operations rate-limited? Can error messages become an oracle? + +### Derive / wrap + +Are child keys domain-separated? Is associated context bound into derivation? Are DEKs wrapped under KEKs? Can one KEK unwrap too much? Are old wrapped keys rewrapped during rotation? + +### Rotate + +Rotation is easy to demand and hard to execute. Rotating a TLS certificate is cheap; rotating a database encryption hierarchy may require rewrapping millions of DEKs; rotating an identity key may require re-establishing trust. + +### Revoke + +Revocation asks verifiers to stop accepting a key. That requires distribution: CRLs, OCSP, short-lived certs, token expiry, key transparency, KMS disabled state, attestation TCB revocation. Revocation is never just cryptography; it is a state-propagation problem. + +### Destroy / recover + +Destroying a key may destroy data. Recovering a key may create an attack path. The tradeoff is policy and process, not math. ## Where keys live -The first question for any key is *who holds it and can it be extracted*. The split is between your device (user inputs + a hardware root) and the server (public records + a KMS), and within each, between software-reachable material and keys that never leave a hardware boundary. +The first question for any key is: **who holds it, and can it be extracted?** ![image](/assets/keys/where-keys-live-device-vs-server.png) -The hardware boundary on each side — the Secure Enclave on the device, the HSM/KMS on the server — is where the non-extractable root keys live. Those keys are generated by the secure element's own TRNG and never emitted (that's the purple track of the entropy stack above). Everything else is wrapped under them. +The hardware boundary on each side — Secure Enclave / TPM on the device, HSM/KMS on the server — is where non-extractable root keys live. Those keys are generated by the secure element's own entropy source and never emitted. Everything else is wrapped under them or authorized by them. + +A rough hierarchy: + +| Location | Extractability | Example | +|---|---|---| +| Plain file / env var | extractable | `.env`, Kubernetes secret mounted as file | +| OS keychain | policy-mediated but OS-trusted | macOS Keychain, Windows DPAPI | +| TPM / Secure Enclave | non-extractable by API | passkeys, device keys, disk unlock keys | +| HSM / KMS | non-extractable or policy-mediated | KEK, CA key, signing key | +| Remote party | unknown | recipient public key's matching private key | + +The more non-extractable the key, the more you depend on APIs for use and recovery. That is the same early/late-binding tradeoff again: non-extractability improves secrecy, but forces policy and availability through the mediator. ## Envelope encryption: DEK / KEK -The standard pattern for data at rest is *envelope encryption*: a per-object **data encryption key (DEK)** encrypts the actual bytes, and a long-lived **key encryption key (KEK)** — held in an HSM or KMS and never exported — encrypts the DEK. You store the ciphertext alongside the *wrapped* DEK; to read, you make one call to the KMS to unwrap the DEK, then decrypt locally. This buys two things: the KEK can be rotated without re-encrypting terabytes of data (you only re-wrap the DEKs), and the high-value root key never leaves its hardware boundary. The tradeoff is operational — every read needs a KMS call, which costs latency and money, and you need a story for when the KMS is unreachable. +The standard pattern for data at rest is **envelope encryption**: + +```text +DEK encrypts the data +KEK encrypts the DEK +KMS/HSM holds the KEK +storage holds ciphertext + wrapped DEK +``` + +To read, the application asks KMS to unwrap the DEK, then decrypts locally. This buys cheap KEK rotation and keeps the high-value root key inside a hardware or service boundary. + +The full storage story — backups, rollback, ransomware, deletion, and recovery — is the [Data at Rest](/programming/data-at-rest-storage-kms-and-recovery.html) post. Here the point is narrower: envelope encryption turns one hard-to-protect data object into a small number of keys with explicit policy around their use. + +## Key policy + +Every key has an implied authorization policy: + +```text +Who may generate this key? +Who may see it? +Who may use it without seeing it? +Who may wrap/unwrap with it? +Which algorithms may it be used for? +Which contexts are valid? +When does it expire? +Who may rotate or revoke it? +Who may destroy it? +Who may recover it? +``` + +Crypto often hides this policy inside possession: whoever has the key can act. Systems engineering pulls the policy back out by making keys non-extractable and mediating operations through KMS, HSMs, Secure Enclaves, TPMs, agents, and reference monitors. + +## Keys and identity + +A public key is not an identity by itself. It becomes identity only when something binds it: + +```text +certificate: CA signs name → public key +passkey record: relying party stores user → public key +SSH known_hosts: first use pins host → public key +attestation quote: hardware signs measurement → public key/session data +software release: project policy accepts signing key → artifact +``` + +That binding is the [Authentication](/programming/authentication.html) story. The root that makes the binding believable is the [Data in Use / Attestation](/programming/data-in-use-isolation-measurement-and-attestation.html) story when the key lives behind hardware or measured code. + +## Keys and availability + +A key is both a security control and an availability dependency. + +- leak it and confidentiality/integrity may fail +- lose it and data may be gone forever +- disable it and production may go down +- rotate it badly and old data may become unreadable +- escrow it and attackers may target the escrow path +- refuse escrow and humans may suffer irreversible loss + +This is why key management always spills from technology into process and humans: recovery ceremonies, access reviews, break-glass procedures, rotation runbooks, and incident response. + +## The punchline + +Algorithms are public machinery. Keys are authority. -In-use and at-rest protection is a whole other world from there: it's where TEE enclaves live for in-use protection, and for data-at-rest, see https://samlaf.github.io/programming/searchable-client-side-encrypted-database.html +```text +public algorithm + secret key + policy around key use = crypto mechanism in a real system +``` -These keys are all only as trustworthy as the anchor at the top of their chain — which is the subject of [Roots of Trust & Attestation](/programming/roots-of-trust-and-attestation.html). +Everything that follows — AEAD, signatures, TLS, passkeys, KMS, attestation — is built from this split. ## References @@ -54,8 +245,12 @@ These keys are all only as trustworthy as the anchor at the top of their chain 2. [Concepts of Compliant Data Encryption - Stephen Roughley][roughley-compliant-enc] 3. [Key Management Discussion - Hacker News][hn-key-mgmt] 4. [Searchable Client-Side Encrypted Database - samlaf][samlaf-searchable-db] +5. [Best Practices for Key Derivation - Trail of Bits][tob-kdf] +6. [Kerckhoffs's Principle - Wikipedia][kerckhoffs] [fystack-dek-kek]: https://fystack.io/blog/dek-kek-the-industry-standard-to-protect-highly-sensitive-data-part-1 "DEK/KEK: The Industry Standard to Protect Sensitive Data - Fystack" [roughley-compliant-enc]: https://stephenroughley.com/2019/06/09/concepts-of-compliant-data-encryption/ "Concepts of Compliant Data Encryption - Stephen Roughley" [hn-key-mgmt]: https://news.ycombinator.com/item?id=24379120 "Key Management Discussion - Hacker News" [samlaf-searchable-db]: https://samlaf.github.io/programming/searchable-client-side-encrypted-database.html "Searchable Client-Side Encrypted Database - samlaf" +[tob-kdf]: https://blog.trailofbits.com/2025/01/28/best-practices-for-key-derivation/ "Best Practices for Key Derivation - Trail of Bits" +[kerckhoffs]: https://en.wikipedia.org/wiki/Kerckhoffs%27s_principle "Kerckhoffs's principle - Wikipedia" diff --git a/_drafts/crypto-series/2026-06-10-roots-of-trust-and-attestation.md b/_drafts/crypto-series/2026-06-10-roots-of-trust-and-attestation.md deleted file mode 100644 index 0497420..0000000 --- a/_drafts/crypto-series/2026-06-10-roots-of-trust-and-attestation.md +++ /dev/null @@ -1,136 +0,0 @@ ---- -title: "Roots of Trust & Attestation" -series: "Applied Crypto, Part 5" -series_url: "/programming/crypto-series-intro.html" -category: programming -date: 2026-06-10 ---- - -The last post, and where the *right-party* arm of the series bottoms out. Every chain of trust we've followed — a TLS cert, a passkey, a session key — terminates at some anchor you trusted *before* the connection began. This post is about those anchors (roots of trust), the mechanism that proves a key really lives behind one (attestation), and it closes with a series capstone: everything a secure channel still doesn't give you. - -## What ultimately authenticates a key? - -A channel's session keys come out of an ephemeral DH exchange — but what stops an attacker from just running that exchange with you themselves? The answer is that one long-term key in the handshake is *authenticated*, and that authentication chains upward until it hits something you trusted before the connection began. - -How is a channel's ephemeral session key ultimately authenticated? - -![image](/assets/roots-of-trust-and-attestation/session-key-authentication-chain.png) - -There are two chains to the same destination here. The left one is the familiar Web PKI: the leaf cert binds a domain to a key, signed by an intermediate, signed by a Root CA that's baked into your browser. The right one is hardware attestation: an Intel SGX root signs an Intel-issued attestation key, which signs a TDX quote whose `REPORTDATA` commits to the session key's hash. Either way, the session key is only as trustworthy as the out-of-band anchor at the top of its chain. - -## Root of Trust - -So where do those anchors come from? They're never derived — they're *configured*, out of band, by software vendors, OS installs, and DHCP. - -![image](/assets/roots-of-trust-and-attestation/roots-of-trust-out-of-band.png) - -Others that we could include: - -**CT log keys.** Modern Chrome and Safari refuse certs that don't carry Signed Certificate Timestamps from a list of trusted Certificate Transparency logs (currently ~5-6 active). The public keys of those logs are baked into browser source code, just like the CA roots. Without them, the cert chain alone isn't sufficient for trust. - -**NTP server addresses + (optionally) NTS roots.** Cert validity windows are time-bounded, so every cert check depends on a clock that's roughly right. NTP server IPs arrive via DHCP or hardcoded fallback (`pool.ntp.org`, `time.apple.com`). Plain NTP is unauthenticated; Network Time Security adds a TLS-rooted chain on top, which means it ultimately leans on Root CAs anyway. - -**OS and package signing keys.** The browser binary itself was installed by `apt`/`dnf`/`pacman`/the Mac App Store/Windows Update, each with their own keyring (Debian release key, Apple notarization key, Microsoft Authenticode roots). Whatever signed `firefox.deb` is upstream of every Root CA decision Firefox makes. - -**UEFI Secure Boot Platform Key.** The firmware-level root that authorized booting the OS that's running the browser. Burned in by the OEM at manufacture, with Microsoft's KEK enrolled as the de facto standard for x86. This is the deepest software-level trust anchor on a typical machine — everything else descends from it. - -**Other hardware vendor roots.** AMD's SEV-SNP root, Apple's Secure Enclave root, ARM's TrustZone roots, every TPM manufacturer's EK CA. Same pattern as Intel SGX Root, just different silicon. - -And tying back to where the conversation started: **blockchain genesis hashes** are the same kind of object. Ethereum's chain is defined by a specific genesis block whose hash is hardcoded in clients; "which chain is the real chain" is answered by `apt-get install geth` shipping that constant. Self-certifying keys at the application layer, sure, but the *configuration* that picks one chain over a fork lives in the same out-of-band place as the Root CA bundle. - -## Attestation: the REMITS lens - -Proving that a key really lives behind one of those hardware roots — rather than just claiming to — is **attestation**. Red Hat's REMITS model is a clean lens for reading any attestation scheme, hardware or cloud. - -Redhat's REMITS model: -![image](/assets/roots-of-trust-and-attestation/remits-model.png) - - - -## TPM - -![image](/assets/roots-of-trust-and-attestation/remits-tpm.png) - -TODO: figure out which of the 2 below diagrams is correct - -![image](/assets/roots-of-trust-and-attestation/tpm-key-hierarchy-1.png) - -![image](/assets/roots-of-trust-and-attestation/tpm-key-hierarchy-2.png) - -## CVMs - -### AMD - -![image](/assets/roots-of-trust-and-attestation/remits-amd-sev.png) - - -### Intel DCAP - -![image](/assets/roots-of-trust-and-attestation/intel-dcap-key-hierarchy.png) - - -In the REMITS model: -![image](/assets/roots-of-trust-and-attestation/remits-intel-dcap.png) - -## Cloud Attestation of CVMs: to vTPM or not - -Seems like vTPMs are more about the cloud provider decision, not the underlying hardware: Azure uses vTPM model whereas Google doesn't - -### vTPM (Azure) - - - -![image](/assets/roots-of-trust-and-attestation/azure-vtpm-two-run-chain.png) - -In the first run, we use hardware-provided evidence against the cloud provider’s own attestation architecture (such as Microsoft Azure Attestation). If successful, this first run unlocks the secrets necessary to build a vTPM (e.g. from persistent storage). How this is done precisely appears to rely on proprietary, non open-source Microsoft software. The root of trust in that first run is in hardware, namely an AMD root key (ARK) in the current SEV instances. -![image](/assets/roots-of-trust-and-attestation/azure-vtpm-run1-amd-remits.png) - -A second run will then start with the vTPM as a root of trust, and the secrets become accessible through the standard mechanisms specified for all TPMs, which we described above. Except for the root of trust being a vTPM instead of a physical TPM, the second run is otherwise equivalent. -![image](/assets/roots-of-trust-and-attestation/azure-vtpm-run2-remits.png) - - -### Real TPM (Google) - -Main difference seems to be that Google's identity is completely independent from the hardware chain of trust, and needs to be verified independently? - -![image](/assets/roots-of-trust-and-attestation/gcp-dual-pki-chains.png) - -PKI #1 — Intel DCAP (left, guest evidence). Intel SGX/TDX root CA → PCK platform CA (Intel's provisioning service) → PCK cert bound to the platform's TCB level → TD quote carrying MRTD + RTMRs, signed by the Intel-provisioned quoting-enclave attestation key. This is the exact same direct Intel-rooted quote you get on paravisor-free GCP TDX — no Google software in the signing path. The RTMR event log is the structural analog of your "TPM event logs": it's the human-readable explanation of what the RTMR values mean, and the quote signs over the RTMRs that summarize it. - -PKI #2 — Google Titan (right, platform evidence). Titan/Google root CA → per-Titan-chip identity cert (the unique keying material minted at chip manufacture) → host firmware report from first-instruction integrity (Titan held the AP in reset and verified the boot firmware before the very first instruction) → a machine-identity attestation asserting this physical box is in the fleet. The "fleet ledger" leaf is literally the append-only hardware ledger from the Apple quote you pasted; machine identity is checked against it. - -## Capstone: what a secure channel still doesn't give you - -We've now walked the whole stack — primitives, channels, keys, authentication, and the roots everything anchors in. Worth ending on the humbling part: a secure channel is the *easy* part. Analogously to https://thume.ca/2020/05/17/pipes-kill-productivity/, we can describe cryptographic pipes (secure channels) as: -- Rootless. A secure channel reduces "trust the network" to "trust a key" but never eliminates trust — somebody has to anchor the key out of band. PKI, TOFU, hardcoded fingerprints, DNSSEC, blockchain registries, key transparency logs. This is the exact same bootstrap problem Kademlia has, just shifted into crypto-space. Every system reinvents it. (This bootstrap problem is exactly the roots-of-trust section above.) -- Unauthorized. Authentication answers "who"; it doesn't answer "may they". You will write the authz layer. Every time. Capabilities (macaroons, biscuits), ACLs, role tables, OPA — all of it lives above the channel. -- Nameless. Public keys aren't names. You will build, or import, a naming layer (Zooko's triangle applies: secure / human-meaningful / decentralized — pick two). Petnames, ENS, DNS-over-something, .onion vanity, GPG WoT. All ad hoc, none portable. -- Stale. Keys leak, certs expire, devices get lost, employees quit. You need rotation, revocation, and a story for "this was Alice and now isn't." CRL/OCSP, short-lived certs (SPIFFE), key transparency, ratchets. The state machine for "current trust" is its own distributed system. -- Killable. The channel says nothing about staying *up* — and crypto only ever *subtracts* from availability. An unauthenticated handshake packet makes you do expensive asymmetric work for free (client puzzles, SYN/DTLS cookies, WireGuard's cookie reply all exist to claw that back); a lost or revoked key is permanent denial-of-access; ransomware is just confidentiality pointed at you. Availability is the third leg of the CIA triad, and the channel is the wrong layer to ask for it — it lives in redundancy, rate limits, and capacity, not crypto. -- Brittle (cryptographically). Algorithms get deprecated faster than your dependency tree updates. SHA-1, RC4, MD5, soon RSA-2048 and ECDH against a quantum adversary. Cipher agility is the "Mismatched" problem on hard mode — you can't just add a JSON field, you have to renegotiate primitives without opening a downgrade attack. (See the negotiation-as-footgun discussion in [Key Exchange & Secure Channels](/programming/secure-channels.html) — agility is exactly the attack surface TLS keeps getting burned by.) -- Leaky. The channel encrypts content but not envelope: who talks to whom, when, how often, how much. Traffic analysis, timing, sizes, fingerprintable handshakes (JA3/JA4). Padding, cover traffic, mixnets — and each of those is its own pile of work. -- Replayable. A single channel handles ordering inside its lifetime, but the moment your message escapes the channel (queued, logged, persisted, cross-session) freshness becomes your problem again. Nonces, timestamps, sequence numbers, idempotency keys — all rebuilt at the app layer. -- Pairwise. Secure channels are 2-party by construction. Groups need entirely different primitives (MLS, Signal Sender Keys, fan-out + per-member channels). Naively gluing N pairwise channels together gets you O(N) keys and zero forward secrecy guarantees as a group. -- Oracular. Composing crypto creates oracles. Padding oracles, downgrade oracles, error-message oracles, timing oracles at trust boundaries. Hume's "untrusted" said validate your inputs; crypto pipes say also don't let your error messages distinguish failure modes, which is much harder to remember. -- Non-committing. AEAD proves "this ciphertext wasn't tampered with" — not "this is the only key that decrypts it." AES-GCM and ChaCha20-Poly1305 let an attacker craft one ciphertext that verifies under multiple keys, which is the engine behind partitioning-oracle attacks (they broke Shadowsocks) and a footgun for password-based, multi-recipient, and key-rotation schemes. You need a committing AEAD; the gap is spelled out in [Cryptographic Primitives](/programming/crypto-primitives.html). -- Misusable. The APIs are foot-guns. Reuse a GCM nonce → catastrophic. Use ECB → meme. Roll your own → don't. "Misuse-resistant cryptography" exists as a research area precisely because of how much code paying the crypto tax still gets it wrong. -- Side-channeled. Even a correct implementation leaks through cache timing, branch prediction, power, EM. Constant-time code is a discipline most app developers never learn and most languages don't help with. -- Metadata-stateful. Sessions, ratchets, resumption tickets, 0-RTT early-data caches — they all carry state that has to survive crashes, sync across replicas, and not get rolled back (else replay). Your "stateless web service" has a stateful crypto layer underneath whether you want it or not. - -Secure channel ≈ "I know who you are and nobody else is listening." Everything that gives that fact operational meaning — multiplexing, negotiating, routing, budgeting, naming, authorizing, scoring, recovering — lives above it. - -> Hume's was "try really hard not to write a distributed system." Yours could be "try really hard not to build your own crypto layer — but also notice that even using a great off-the-shelf one (TLS, Noise, libp2p, Signal) only pays down maybe three of these dimensions. The rest are yours, forever, and the industry pretends they aren't." That framing — "the secure channel is the easy part; the productivity tax is everything around it" — would land hard. - -## References - -1. [Learn About Confidential Computing Attestation (REMITS) - Red Hat][redhat-cc-attestation] -2. [TPM Keys - Eric Chiang][chiang-tpm-keys] -3. [Expanding Private Cloud Compute - Apple Security][apple-pcc] -4. [OpenTitan][opentitan] -5. [Pipes Kill Productivity - Tristan Hume][thume-pipes] - -[redhat-cc-attestation]: https://www.redhat.com/en/blog/learn-about-confidential-computing-attestation "Learn About Confidential Computing Attestation (REMITS) - Red Hat" -[chiang-tpm-keys]: https://ericchiang.github.io/post/tpm-keys/ "TPM Keys - Eric Chiang" -[apple-pcc]: https://security.apple.com/blog/expanding-pcc/ "Expanding Private Cloud Compute - Apple Security" -[opentitan]: https://opentitan.org/ "OpenTitan" -[thume-pipes]: https://thume.ca/2020/05/17/pipes-kill-productivity/ "Pipes Kill Productivity - Tristan Hume" diff --git a/_drafts/crypto-series/2026-06-11-data-at-rest-storage-kms-and-recovery.md b/_drafts/crypto-series/2026-06-11-data-at-rest-storage-kms-and-recovery.md new file mode 100644 index 0000000..2b67cf0 --- /dev/null +++ b/_drafts/crypto-series/2026-06-11-data-at-rest-storage-kms-and-recovery.md @@ -0,0 +1,291 @@ +--- +title: "Data at Rest: Storage, KMS, and Recovery" +series: "Applied Crypto, Part 7" +series_url: "/programming/crypto-series-intro.html" +category: programming +date: 2026-06-11 +--- + +Data at rest is where crypto stops looking like a handshake and starts looking like operations. + +A TLS connection has a beginning and an end. A stored object may live for years, be copied into backups, move across regions, survive employee turnover, outlive algorithms, get restored after ransomware, and need to be deleted on purpose. The core mechanism is still simple — encrypt bytes with an AEAD — but the hard parts are lifecycle and policy: who may unwrap the key, how long the data exists, how rollback is detected, how recovery works, and what happens when keys are lost. + +## Cube coordinates + +```text +Concerns: + secrecy, correctness, possession, utility, attribution, uptime + +Data state: + at rest + +Reasoning layers: + threat → property → capability → policy → mechanism + +Control modality: + technology + process + human +``` + +The storage version of the series' main distinction is: + +```text +self-protecting data: encrypted / MACed / signed objects +mediated access: KMS, IAM, database guards, file permissions, backup controls +``` + +Good storage security uses both. + +## Threats to data at rest + +The obvious threat is stolen media: a laptop, phone, disk, backup tape, EBS snapshot, S3 bucket, database dump, or object-store credential leaks. But storage threats are broader than disclosure. + +| Threat | Property hit | Typical control | +|---|---|---| +| Stolen disk/laptop | confidentiality, possession | full-disk encryption, device keys | +| Leaked database snapshot | confidentiality | field/object encryption, KMS, access review | +| Malicious storage admin | confidentiality, integrity | client-side encryption, envelope encryption, audit, separation of duties | +| Backup compromise | confidentiality, integrity | encrypted backups, signed manifests, restricted restore path | +| Ransomware | availability, possession, integrity | immutable backups, offline copies, recovery drills | +| Rollback/stale restore | integrity, freshness | versioning, append-only logs, signed checkpoints | +| Silent corruption | integrity, utility | hashes, MACs, erasure coding, scrubbing | +| Lost key | availability, utility | recovery policy, escrow tradeoffs, rotation discipline | +| Over-retention | privacy, compliance | deletion policy, key destruction, retention automation | + +The important asymmetry: encryption protects confidentiality even when storage is stolen, but it can hurt availability. Lose the only key and the data is gone. Ransomware is the same mechanism pointed the wrong way: confidentiality against the owner. + +## Desired properties + +For stored data, CIA splits into several sharper concerns: + +- **Secrecy** — unauthorized parties cannot read the content. +- **Correctness/integrity** — unauthorized parties cannot undetectably modify it. +- **Freshness** — an old valid version cannot be replayed as current. +- **Possession/control** — the owner can retain or deliberately relinquish control. +- **Utility** — the bytes are usable when needed; ciphertext with a lost key is available but useless. +- **Attribution** — sensitive reads/writes/decrypts can be tied to an actor. +- **Availability** — data survives failures, extortion, deletion mistakes, and regional outages. + +Crypto directly helps with secrecy and integrity. It helps with attribution through signatures and audit-log integrity. It helps with deletion only indirectly through key destruction. Availability comes mostly from redundancy, backups, recovery drills, and operational controls. + +## Mechanism 1: full-disk encryption + +Full-disk encryption protects a device or volume while it is powered off. The disk holds ciphertext; a boot-time secret unlocks the volume key; the OS sees plaintext after unlock. + +This is excellent against theft of inert media: + +```text +stolen laptop off → attacker sees ciphertext +cloud snapshot copied without key → attacker sees ciphertext +``` + +But it does little once the machine is running: + +```text +compromised OS → plaintext available +malware running as user → reads through filesystem +cloud admin with live access path → depends on platform controls +``` + +Full-disk encryption is therefore an **at-rest** control, not an **in-use** control. It moves the problem from "protect every sector" to "protect the unlock key and the running system." + +## Mechanism 2: object / field encryption + +Application-level encryption protects individual objects, fields, or records before they hit storage. + +```text +plaintext record + → AEAD(key, nonce, associated_data, plaintext) + → ciphertext + tag + metadata +``` + +The associated data should bind the ciphertext to context that must not be swapped: + +```text +object id +schema version +tenant id +purpose +creation time +key id +``` + +That prevents an attacker from moving ciphertext from one row, tenant, or purpose to another while keeping the tag valid. AEAD authenticates both the encrypted plaintext and the unencrypted associated metadata. + +Object encryption is what you want when the storage layer itself is not trusted with plaintext: object stores, queues, caches, browser sync, multi-tenant databases, and client-side encrypted systems. + +## Mechanism 3: envelope encryption + +Envelope encryption is the standard scalable pattern: + +```text +DEK = random data-encryption key +ciphertext = AEAD(DEK, plaintext) +wrapped_DEK = KMS.Encrypt(KEK, DEK) +store(ciphertext, wrapped_DEK, key_id, metadata) +``` + +To read: + +```text +DEK = KMS.Decrypt(wrapped_DEK) +plaintext = AEAD.Open(DEK, ciphertext) +``` + +The **DEK** is per object, per file, per table, or per tenant. The **KEK** is the long-lived key-encryption key held by KMS/HSM and usually never exported. + +This buys three things: + +1. **Blast-radius control.** A leaked DEK exposes only the objects under that DEK. +2. **Cheap rotation.** Rotate the KEK by rewrapping DEKs, not re-encrypting terabytes. +3. **Late-bound policy.** Every unwrap can ask IAM/KMS policy whether this caller may decrypt now. + +Envelope encryption is the canonical hybrid of crypto and access control: ciphertext self-protects in storage; KMS mediates key use at read time. + +## KMS and HSMs + +A KMS is a reference monitor for key operations. An HSM is the hardware boundary that keeps high-value keys non-extractable. Cloud KMS products combine both: an API surface with IAM policy, backed by software or hardware key isolation. + +The useful question is not "is the data encrypted?" but: + +```text +Who can call Decrypt? +From where? +For which key? +Under which context? +Is the key enabled? +Is the request logged? +Can an admin bypass it? +Can the key be exported? +Can it be scheduled for destruction? +How is recovery handled? +``` + +KMS policy is late binding. It lets you revoke access without rewriting all ciphertext. But it also makes KMS an availability dependency and a concentrated trust point. + +## Integrity at rest + +Encryption alone does not give integrity unless it is authenticated encryption. For stored objects, use an AEAD or a separate MAC/signature. + +The choice depends on who needs to verify: + +| Need | Mechanism | +|---|---| +| Only key-holders verify and produce tags | MAC / AEAD | +| Anyone can verify but only signer can produce | signature | +| Detect accidental corruption | hash/checksum | +| Prove inclusion/history | Merkle tree, transparency log, append-only log | +| Detect rollback | signed checkpoint, monotonic counter, external timestamp, quorum state | + +The difference between MACs and signatures matters more at rest than it first appears. A MAC proves "someone with the shared key made this." A signature can prove to a third party which private key signed. That is why software releases, package registries, certificate logs, and audit records lean on signatures. + +## Rollback and freshness + +A ciphertext can be perfectly valid and still stale. If an attacker restores last month's encrypted database, every AEAD tag may verify. The cryptography proves the bytes were produced under the key; it does not prove they are the current bytes. + +Freshness needs state outside the object: + +- monotonically increasing version numbers +- append-only logs +- signed checkpoints +- transparency logs +- trusted timestamps +- quorum replication +- TPM monotonic counters, with caveats +- application-level idempotency and sequence state + +This is the at-rest version of replay. A secure channel orders bytes inside one connection; storage systems need their own freshness story across crashes, restores, replicas, and backups. + +## Backups and ransomware + +Availability lives here, and crypto is ambivalent. Encryption protects backups from disclosure; immutable/offline backups protect against ransomware; but lost keys or encrypted-by-attacker data destroy utility. + +A realistic backup policy needs: + +- encrypted backup media +- separate backup credentials +- immutable or append-only retention windows +- offline or cross-account copies +- restore drills +- signed manifests or hashes to detect tampering +- key escrow/recovery decisions made explicitly +- documented break-glass access + +The uncomfortable tradeoff: the stronger your key non-recovery story, the more permanent accidental loss becomes. The stronger your escrow/recovery story, the more attractive the escrow path becomes to attackers. There is no purely cryptographic answer; this is process and human control around cryptographic material. + +## Deletion and crypto-shredding + +Deleting encrypted data can sometimes mean deleting the key. If every object is encrypted under a unique DEK, and that DEK is irrecoverably destroyed, the ciphertext becomes useless. This is **crypto-shredding**. + +It is attractive because deleting all replicas of a blob is hard; deleting a small key may be easier. But it only works if: + +- the DEK was not copied elsewhere +- plaintext was not cached/logged/indexed +- backups do not retain the key +- derived keys cannot be regenerated +- deletion actually destroys all recovery paths + +Again: crypto gives the primitive, process decides whether the promise is true. + +## Data-at-rest policy + +Storage policy answers questions like: + +```text +Which data must be encrypted? +At what granularity: disk, table, field, object, tenant? +Who may decrypt? +Who may rotate keys? +Who may schedule key destruction? +How long is data retained? +What is the backup retention period? +Who can restore production data? +Is break-glass allowed? +Are decrypts logged and reviewed? +What associated data must be authenticated? +Which algorithms and key sizes are permitted? +``` + +Those policies are enforced by a mix of mechanisms: application code, database permissions, KMS/IAM, HSM non-extractability, object-store policy, deployment pipelines, backup systems, and audit logs. + +## Storage across the four axes + +Putting the cube together: + +| Layer | Storage example | +|---|---| +| **Threat** | leaked DB snapshot, malicious admin, ransomware, rollback | +| **Property** | confidentiality, integrity, freshness, availability | +| **Capability** | encryption, key management, access control, backup/recovery, audit | +| **Policy** | who may decrypt, rotate, restore, delete; retention and recovery rules | +| **Mechanism** | AEAD, DEK/KEK, KMS/HSM, IAM, signatures, hash trees, immutable backups | +| **Control modality** | technology plus rotation runbooks, access reviews, recovery ceremonies | + +## The punchline + +Data at rest is not "encrypt the database". It is: + +```text +protect stored bytes with crypto, +late-bind key use with policy, +record access with audit, +preserve utility with recovery, +and decide explicitly who can destroy or restore what. +``` + +The mechanism is AEAD. The system is key management. + +## References + +1. [DEK/KEK: The Industry Standard to Protect Sensitive Data - Fystack][fystack-dek-kek] +2. [Concepts of Compliant Data Encryption - Stephen Roughley][roughley-compliant-enc] +3. [Best Practices for Key Derivation - Trail of Bits][tob-kdf] +4. [Authenticated Encryption - Wikipedia][ae-wiki] +5. [Amazon S3 Server-Side Encryption - AWS][aws-s3-sse] +6. [Cloud KMS Documentation - Google Cloud][gcp-kms] + +[fystack-dek-kek]: https://fystack.io/blog/dek-kek-the-industry-standard-to-protect-highly-sensitive-data-part-1 "DEK/KEK: The Industry Standard to Protect Sensitive Data - Fystack" +[roughley-compliant-enc]: https://stephenroughley.com/2019/06/09/concepts-of-compliant-data-encryption/ "Concepts of Compliant Data Encryption - Stephen Roughley" +[tob-kdf]: https://blog.trailofbits.com/2025/01/28/best-practices-for-key-derivation/ "Best Practices for Key Derivation - Trail of Bits" +[ae-wiki]: https://en.wikipedia.org/wiki/Authenticated_encryption "Authenticated encryption - Wikipedia" +[aws-s3-sse]: https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html "Protecting data with server-side encryption - Amazon S3" +[gcp-kms]: https://cloud.google.com/kms/docs "Cloud Key Management Service Documentation - Google Cloud" diff --git a/_drafts/crypto-series/2026-06-12-data-in-use-isolation-measurement-and-attestation.md b/_drafts/crypto-series/2026-06-12-data-in-use-isolation-measurement-and-attestation.md new file mode 100644 index 0000000..30e0a7a --- /dev/null +++ b/_drafts/crypto-series/2026-06-12-data-in-use-isolation-measurement-and-attestation.md @@ -0,0 +1,300 @@ +--- +title: "Data in Use: Isolation, Measurement, and Attestation" +series: "Applied Crypto, Part 8" +series_url: "/programming/crypto-series-intro.html" +category: programming +date: 2026-06-12 +--- + +Data in use is the state crypto has the hardest time with. Stored data can be ciphertext. Data in transit can be protected inside a channel. But computation needs plaintext somewhere: in registers, memory, a process, an enclave, a VM, a database engine, or an AI agent's context window. + +So the data-in-use question is: + +> Why should I believe this key is controlled by the code and environment I intended, rather than by the OS, hypervisor, cloud provider, malware, or an impostor? + +The answer is a combination of **isolation**, **measurement**, and **attestation**. + +## Cube coordinates + +```text +Concerns: + secrecy, correctness, origin-of-code, admissibility, freshness + +Data state: + in use / processing + +Reasoning layers: + threat → property → capability → policy → mechanism + +Control modality: + technology, wrapped by verifier policy and operational process +``` + +The in-use ladder looks like: + +```text +Threat: + malicious process, compromised OS, hostile hypervisor, cloud admin, side channel, bad dependency + +Property: + memory confidentiality, runtime integrity, code identity, admissibility + +Capability: + isolation, measurement, remote attestation, secret release + +Policy: + which vendor roots, TCB versions, measurements, PCR/RTMR values, and freshness rules are acceptable? + +Mechanism: + MMU, process isolation, sandboxing, TPMs, measured boot, SGX/TDX, SEV-SNP, vTPMs, CVM attestation +``` + +## Isolation: the first in-use control + +The oldest data-in-use mechanism is not a TEE; it is the reference monitor. + +- the MMU keeps one process from reading another process's pages +- the kernel enforces file descriptors, users, capabilities, namespaces +- seccomp/eBPF restricts syscalls +- containers and hypervisors isolate workloads +- language runtimes sandbox code +- browsers isolate origins + +This is mediated access rather than self-protecting data. The data is plaintext inside the authorized process; the security claim is that other processes cannot reach it except through mediated interfaces. + +The weakness is obvious: if the mediator is compromised, the plaintext is exposed. A malicious kernel can read process memory. A hostile hypervisor can inspect a VM. A cloud operator may control the host below your workload. Data-in-use mechanisms below are attempts to shrink or re-anchor that trusted computing base. + +## Measurement: naming the computation + +Before a verifier can decide whether code is acceptable, the platform needs a stable name for what ran. That name is a **measurement**: a cryptographic digest of code, configuration, boot state, or runtime events. + +Measured boot extends registers as the system boots: + +```text +PCR := H(PCR || event) +``` + +Intel TDX uses RTMRs; TPMs use PCRs; secure boot chains measure firmware, bootloaders, kernels, initrds, policies, and sometimes application code. The measurement does not say the code is good. It says the code is *this* code. + +That distinction is load-bearing: + +```text +measurement verifies ≠ code is safe +measurement verifies = code/environment matches a known digest +``` + +Policy supplies the missing step: which measurements are acceptable for this secret or request? + +## Attestation: authentication of a computation environment + +Attestation is authentication pointed at a runtime environment. + +Normal authentication says: + +```text +this key belongs to Alice / example.com / PayrollService +``` + +Attestation says: + +```text +this key is controlled by code with measurement M +running under platform state S +under hardware/vendor root R +fresh for nonce N +``` + +The verifier checks a signed report, quote, or certificate chain, then applies policy: + +```text +Is the vendor root trusted? +Is the TCB version acceptable? +Are the measurements expected? +Is the quote fresh? +Is this workload allowed to receive this secret? +``` + +A valid quote is not authorization. It is evidence consumed by authorization policy. + +## What ultimately authenticates a key? + +A channel's session keys come out of an ephemeral DH exchange — but what stops an attacker from just running that exchange with you themselves? One long-term key in the handshake is authenticated, and that authentication chains upward until it hits something trusted before the connection began. + +![image](/assets/roots-of-trust-and-attestation/session-key-authentication-chain.png) + +There are two chains to the same destination here. The left one is the familiar Web PKI: the leaf cert binds a domain to a key, signed by an intermediate, signed by a Root CA baked into your browser. The right one is hardware attestation: an Intel SGX/TDX root signs an attestation key, which signs a quote whose report data commits to a session key hash. Either way, the session key is only as trustworthy as the out-of-band anchor at the top and the verifier policy that accepts it. + +## Roots of trust are out-of-band anchors + +Roots are never derived by crypto. They are configured. + +![image](/assets/roots-of-trust-and-attestation/roots-of-trust-out-of-band.png) + +Examples: + +**CA roots.** Browsers and OSes ship root stores. The Web PKI works because clients accept those roots as anchors for certificate chains. + +**CT log keys.** Modern browsers require Signed Certificate Timestamps from trusted Certificate Transparency logs. The log public keys are also shipped in browser policy. + +**NTP / time roots.** Certificate validation depends on time. NTP server addresses arrive via DHCP or hardcoded fallback. Network Time Security then leans on TLS roots. + +**OS and package signing keys.** The browser binary and root store were installed by an OS/package manager with its own signing roots. + +**UEFI Secure Boot Platform Key.** Firmware decides which bootloaders are accepted before the OS starts. + +**Hardware vendor roots.** Intel, AMD, Apple, ARM, TPM manufacturers, cloud providers — each attestation ecosystem starts with vendor roots accepted by verifier policy. + +**Blockchain genesis hashes.** A client ships a genesis hash or checkpoint. "Which chain is real?" begins as an out-of-band software choice. + +Attestation chains may be cryptographic, but root acceptance is policy. + +## The REMITS lens + +Red Hat's REMITS model is a useful way to read any attestation scheme: who measures, who attests, what evidence is produced, which reference values are checked, and what secret or decision follows. + +![image](/assets/roots-of-trust-and-attestation/remits-model.png) + +The recurring pattern: + +```text +attester produces evidence +verifier checks evidence against reference values +relying party releases a secret or accepts the workload +``` + +The secret-release step is where attestation meets key management. A KMS, secret manager, or verifier releases a key only if the quote is fresh and the measurement matches policy. + +## TPMs + +A TPM gives a machine hardware-backed keys, PCRs, sealed storage, and quotes. It is not magic isolation; it is a root for measurement and key operations. + +![image](/assets/roots-of-trust-and-attestation/remits-tpm.png) + +A TPM can: + +- generate or protect non-extractable keys +- extend PCRs with boot measurements +- seal secrets to PCR values +- quote PCR values with an attestation key +- prove, via an endorsement chain, that the attestation key belongs to a real TPM + +Two things are easy to confuse: + +1. **Sealing** — the TPM releases a secret locally only when PCRs match. +2. **Quoting** — the TPM signs PCRs so a remote verifier can decide. + +TODO: figure out which of the 2 below diagrams is correct. + +![image](/assets/roots-of-trust-and-attestation/tpm-key-hierarchy-1.png) + +![image](/assets/roots-of-trust-and-attestation/tpm-key-hierarchy-2.png) + +## Confidential VMs and TEEs + +Confidential computing tries to protect a guest workload from the host infrastructure. The claim is no longer merely "the cloud authenticates this VM" but "this VM's memory and initial state are protected from the host, and the guest can prove what it is." + +### AMD SEV-SNP + +AMD SEV-SNP protects VM memory and provides attestation rooted in AMD keys. + +![image](/assets/roots-of-trust-and-attestation/remits-amd-sev.png) + +The verifier checks the report, TCB, chip/platform chain, and measurement. Policy decides whether that platform/version/measurement may receive the secret. + +### Intel DCAP / TDX + +Intel DCAP provides a PKI for quote verification: Intel root → platform certs → quote-signing material → quote over measurements and report data. + +![image](/assets/roots-of-trust-and-attestation/intel-dcap-key-hierarchy.png) + +In the REMITS model: + +![image](/assets/roots-of-trust-and-attestation/remits-intel-dcap.png) + +The important detail is that the quote can commit to application data, often a hash of an ephemeral public key. That binds the attested environment to the secure channel you are about to use. + +## Cloud attestation of CVMs: vTPM or direct hardware quote + +Cloud providers differ in where they put the abstraction boundary. Some expose a vTPM-shaped interface; others expose a more direct hardware quote plus cloud platform evidence. + +### vTPM model: Azure-style two-run chain + +In the vTPM model, the first trust decision uses hardware-provided evidence against the cloud provider's attestation architecture. If successful, that first run unlocks secrets needed to construct or restore a vTPM. The second run then uses the vTPM as the guest-facing root. + +![image](/assets/roots-of-trust-and-attestation/azure-vtpm-two-run-chain.png) + +First run, hardware-rooted: + +![image](/assets/roots-of-trust-and-attestation/azure-vtpm-run1-amd-remits.png) + +Second run, vTPM-rooted: + +![image](/assets/roots-of-trust-and-attestation/azure-vtpm-run2-remits.png) + +The benefit is a standard TPM interface for guests. The cost is an extra provider-specific layer whose construction and persistence semantics matter. + +### Direct quote plus platform identity: Google-style split + +In a direct model, the guest evidence may chain to Intel/AMD while platform identity chains separately to the cloud provider's hardware root. + +![image](/assets/roots-of-trust-and-attestation/gcp-dual-pki-chains.png) + +You then verify two things: + +1. **Guest evidence** — the quote says the measured VM/workload is running under an acceptable hardware TCB. +2. **Platform evidence** — the provider says this physical machine is a legitimate member of its fleet. + +Those are different trust statements. Combining them is verifier policy. + +## Secret release + +Attestation becomes useful when it gates a secret: + +```text +workload generates ephemeral key pair +workload asks platform for quote over hash(ephemeral public key) +verifier checks quote and measurement +verifier encrypts secret to ephemeral public key +only the attested workload can decrypt +``` + +This is the in-use analog of authentication plus key exchange. The quote binds the session key to the measured environment; the verifier policy decides whether that environment is allowed to receive the secret. + +## What attestation does not solve + +Attestation is easy to oversell. + +- It proves a measurement, not source-code virtue. +- It says little about bugs in measured code. +- It depends on vendor roots and revocation feeds. +- It can be undermined by side channels. +- It can be undermined by a verifier policy that accepts too much. +- It does not solve authorization: acceptable code still needs least privilege. +- It does not solve availability: a TEE can still crash, be DoSed, or lose its sealing keys. +- It does not remove supply-chain trust: the measured binary came from somewhere. + +The strongest honest claim is: + +```text +this key is bound to this measured environment under this root, fresh for this verifier +``` + +Everything after that is policy. + +## References + +1. [Learn About Confidential Computing Attestation (REMITS) - Red Hat][redhat-cc-attestation] +2. [TPM Keys - Eric Chiang][chiang-tpm-keys] +3. [Expanding Private Cloud Compute - Apple Security][apple-pcc] +4. [OpenTitan][opentitan] +5. [Trusted Platform Module - Wikipedia][tpm] +6. [AMD SEV-SNP Firmware ABI Specification][amd-sev] +7. [Intel Trust Domain Extensions][intel-tdx] + +[redhat-cc-attestation]: https://www.redhat.com/en/blog/learn-about-confidential-computing-attestation "Learn About Confidential Computing Attestation (REMITS) - Red Hat" +[chiang-tpm-keys]: https://ericchiang.github.io/post/tpm-keys/ "TPM Keys - Eric Chiang" +[apple-pcc]: https://security.apple.com/blog/expanding-pcc/ "Expanding Private Cloud Compute - Apple Security" +[opentitan]: https://opentitan.org/ "OpenTitan" +[tpm]: https://en.wikipedia.org/wiki/Trusted_Platform_Module "Trusted Platform Module - Wikipedia" +[amd-sev]: https://www.amd.com/system/files/TechDocs/56860.pdf "AMD SEV-SNP Firmware ABI Specification" +[intel-tdx]: https://www.intel.com/content/www/us/en/developer/tools/trust-domain-extensions/overview.html "Intel Trust Domain Extensions" diff --git a/_drafts/crypto-series/2026-06-13-what-crypto-still-doesnt-give-you.md b/_drafts/crypto-series/2026-06-13-what-crypto-still-doesnt-give-you.md new file mode 100644 index 0000000..fd41ad3 --- /dev/null +++ b/_drafts/crypto-series/2026-06-13-what-crypto-still-doesnt-give-you.md @@ -0,0 +1,304 @@ +--- +title: "What Crypto Still Doesn't Give You" +series: "Applied Crypto, Capstone" +series_url: "/programming/crypto-series-intro.html" +category: programming +date: 2026-06-13 +--- + +Crypto is powerful because it lets data partially protect itself. A ciphertext can sit in a hostile object store; a signature can be verified by a stranger; a secure channel can cross an untrusted network; an attestation quote can carry evidence about code running on someone else's machine. + +But the security cube is larger than crypto. + +```text +For each property/concern, +in each data state, +against each threat, +choose a function/capability, +define the policy, +enforce it with mechanisms, +wrapped by technical/process/human controls. +``` + +Crypto is one mechanism family in that sentence. It mostly serves confidentiality, integrity, origin, attribution, and some privacy properties. It does not eliminate roots, naming, authorization, revocation, availability, governance, or humans. + +This is the negative-space post. + +## Cube coordinates + +```text +Concerns: + all + +Data states: + at rest | in transit | in use + +Reasoning layers: + the gaps around crypto mechanisms + +Control modality: + technology + process + human +``` + +## Rootless + +A secure channel reduces "trust the network" to "trust a key." It does not eliminate trust. + +Somebody has to decide which key is Bob's: + +- Web PKI root store +- TOFU cache +- pinned fingerprint +- DNSSEC root +- blockchain genesis hash +- package signing key +- hardware vendor root +- attestation root +- key-transparency log + +The root is configured out of band. Crypto authenticates chains; it does not choose the first anchor. + +## Unauthorized + +Authentication answers **who**. Authorization answers **may they**. + +TLS can prove the peer controls a certificate. A passkey can prove the user holds a device-bound private key. A signature can prove a release key signed an artifact. None of those facts decides whether this request may delete a record, unwrap a DEK, mint a token, or release a secret. + +You still need policy: + +```text +subject may perform action on object under context +``` + +And you still need an enforcement mechanism: KMS, IAM, database permissions, API gateway, OS kernel, smart contract, verifier policy, or application code. + +## Nameless + +Public keys are not names. + +A raw key can be self-authenticating, but it is not human-meaningful. A DNS name is human-meaningful, but it needs a naming authority. A decentralized name avoids a central CA, but then you inherit the governance and fork-choice problem of the naming system. + +Zooko's triangle — secure, human-meaningful, decentralized; pick two — keeps reappearing: + +- Web PKI: human-meaningful + secure-ish, not decentralized. +- Tor `.onion`: secure + decentralized, not human-meaningful. +- Web of Trust: decentralized + human-meaningful in aspiration, weak at scale. +- ENS/blockchain naming: tries to buy all three with a ledger, but the client still ships a genesis/root choice. + +Crypto binds keys to statements. Naming decides which statements humans can use. + +## Stale + +Keys leak. Certs expire. Employees leave. Devices get lost. Packages are yanked. TCB versions become vulnerable. Measurements that were acceptable yesterday may be forbidden today. + +Crypto proofs are often timeless unless you add time: + +- certificate validity windows +- OCSP / CRLs / short-lived certs +- token expiry +- key rotation +- transparency logs +- revocation lists +- ratchets +- firmware/TCB revocation +- verifier policy updates + +Revocation is a distributed-systems problem wearing a cryptographic hat. + +## Killable + +Crypto can hurt availability. + +- Expensive handshakes create DoS surface. +- Lost keys are permanent denial of access. +- Ransomware is confidentiality turned against the owner. +- KMS outages can make encrypted data unavailable. +- Certificate expiry can take down production. +- Revocation mistakes can brick fleets. + +Availability mostly lives in redundancy, rate limits, capacity, isolation, admission control, backup/restore, and operational discipline — not in ciphers. + +## Brittle + +Algorithms age. Defaults rot. Dependencies lag. + +MD5, SHA-1, RC4, DES, export RSA, static DH, CBC padding constructions — all were acceptable until they weren't. RSA-2048 and today's elliptic-curve assumptions face the quantum timer. Even when the primitive remains sound, composition mistakes create downgrade, oracle, and misuse attacks. + +Cryptographic agility is necessary over decades but dangerous in protocols. Negotiation creates attack surface; no-negotiation designs create migration pain. There is no free axis. + +## Leaky + +Encryption hides content. It does not automatically hide the envelope: + +- who talks to whom +- when +- how often +- how much +- packet sizes +- timing +- handshake fingerprints +- routing metadata +- access patterns + +Padding, cover traffic, mixnets, onion routing, PIR, ORAM, batching, and local processing all attack parts of this problem. Each has its own cost. Content confidentiality is not traffic-analysis resistance. + +## Replayable + +A secure channel orders bytes inside one connection. The moment a message escapes the channel — queued, logged, persisted, retried, cached, restored from backup — freshness becomes your problem again. + +You need some combination of: + +- nonces +- sequence numbers +- timestamps +- idempotency keys +- replay caches +- signed checkpoints +- monotonic counters +- application-level state machines + +AEAD proves a ciphertext was produced under the key. It does not prove this is the first, latest, or intended time it was used. + +## Pairwise + +Secure channels are naturally two-party. Groups are a different problem. + +Naively building a group out of pairwise channels gives you: + +- O(N) or O(N²) key state +- unclear membership semantics +- hard revocation +- weak group forward secrecy +- awkward sender authentication +- state-synchronization bugs + +MLS, Signal Sender Keys, threshold crypto, group ratchets, and broadcast encryption exist because pairwise secure channels do not scale into group security by themselves. + +## Oracular + +Crypto systems become oracles at their boundaries. + +- padding oracles +- decryption oracles +- partitioning oracles +- timing oracles +- downgrade oracles +- error-message oracles +- login/token introspection oracles +- attestation-verifier oracles + +The primitive may be correct, while the system leaks one bit per query until the secret is gone. "Do not reveal why verification failed" is easy to say and hard to maintain across logs, retries, metrics, and UX. + +## Non-committing + +AEAD proves "this ciphertext wasn't tampered with under this key." It does not necessarily prove "this ciphertext belongs to exactly one key." + +AES-GCM and ChaCha20-Poly1305 are non-committing: an attacker can sometimes craft one ciphertext that decrypts and verifies under multiple different keys. That is the engine behind partitioning-oracle attacks, and it matters for password-based encryption, multi-recipient schemes, key rotation, JWE, age-like formats, and any design where "which key worked?" leaks information. + +If your protocol needs key commitment, choose a committing AEAD or add an explicit commitment. + +## Misusable + +Crypto APIs are foot-guns. + +- reuse a GCM nonce → catastrophic +- use ECB → pattern leak +- decrypt before verifying tag → RUP bug +- compare MACs non-constant-time → timing leak +- sign the wrong transcript → identity misbinding +- forget associated data → context swap +- accept `alg=none` → self-own +- roll your own protocol → probably broken + +Misuse-resistant cryptography exists because correct primitive use is not the same as correct system design. + +## Side-channeled + +A cryptographic implementation can be logically perfect and physically leaky. + +Secrets escape through: + +- timing +- cache state +- branch prediction +- speculative execution +- power draw +- electromagnetic emanations +- acoustic signals +- memory deduplication +- page faults + +Constant-time code, masking, blinding, hardware isolation, and side-channel testing live below the API. "The math is sound" and "the deployed system does not leak the key" are different claims. + +## Metadata-stateful + +Modern crypto systems carry state even when the application wants to be stateless: + +- session tickets +- replay caches +- ratchet state +- nonce counters +- key epochs +- resumption PSKs +- transparency log checkpoints +- attestation nonces +- 0-RTT early-data state +- certificate/key rotation state + +That state has to survive crashes, replicate safely, avoid rollback, and not fork across regions. Crypto often smuggles a distributed system into your stateless service. + +## Human-process-governance gaps + +McCumber's third axis is the reminder: technology controls are wrapped by process and humans. + +Crypto does not decide: + +- who can approve root-store changes +- who reviews IAM grants +- who holds break-glass authority +- how recovery ceremonies work +- how often access is recertified +- whether engineers understand key destruction +- whether admins rubber-stamp prompts +- whether incentives reward bypassing controls + +A perfect mechanism enforcing a bad policy through a broken process is still a broken system. + +## The final picture + +A secure channel roughly buys: + +```text +I know which key I am talking to, +and outsiders cannot read or tamper with these bytes in transit. +``` + +That is valuable, but narrow. It says little about stored data, running code, authorization, naming, revocation, metadata, recovery, governance, or the authenticated counterparty's honesty. + +So the series ends where it began: + +```text +For each property/concern, +in each data state, +against each threat, +choose a function/capability, +define the policy, +enforce it with mechanisms, +wrapped by technical/process/human controls. +``` + +Crypto is one of those mechanism families. Use it. Do not ask it to be the whole cube. + +## References + +1. [Pipes Kill Productivity - Tristan Hume][thume-pipes] +2. [Partitioning Oracle Attacks - Len, Grubbs & Ristenpart][partitioning-oracle] +3. [How to Abuse and Fix Authenticated Encryption Without Key Commitment - Albertini et al.][key-commitment] +4. [Authenticated Encryption - Wikipedia][ae-wiki] +5. [Hardware-Based Side-Channel Attacks (survey) - ACM][sidechannel-survey] + +[thume-pipes]: https://thume.ca/2020/05/17/pipes-kill-productivity/ "Pipes Kill Productivity - Tristan Hume" +[partitioning-oracle]: https://www.usenix.org/conference/usenixsecurity21/presentation/len "Partitioning Oracle Attacks - Len, Grubbs & Ristenpart" +[key-commitment]: https://www.usenix.org/conference/usenixsecurity22/presentation/albertini "How to Abuse and Fix Authenticated Encryption Without Key Commitment - Albertini et al." +[ae-wiki]: https://en.wikipedia.org/wiki/Authenticated_encryption "Authenticated encryption - Wikipedia" +[sidechannel-survey]: https://dl.acm.org/doi/10.1145/3357613.3357627 "Hardware-based side-channel attacks (survey) - ACM" diff --git a/_drafts/crypto-series/2026-06-14-availability.md b/_drafts/crypto-series/2026-06-14-availability.md new file mode 100644 index 0000000..c9476d9 --- /dev/null +++ b/_drafts/crypto-series/2026-06-14-availability.md @@ -0,0 +1,73 @@ +--- +title: "Availability: Uptime, Redundancy, and Recovery" +series: "Applied Crypto, Appendix" +series_url: "/programming/crypto-series-intro.html" +category: programming +date: 2026-06-14 +--- + +The rest of this series keeps deferring availability. The intro calls it "deliberately not a major crypto topic"; the capstone files it under *Killable* and notes it "mostly lives in redundancy, rate limits, capacity, isolation, admission control, backup/restore, and operational discipline — not in ciphers." This appendix is where that leg gets its own short treatment, so the other articles can point here instead of re-litigating it. + +Availability is the **A** in CIA, and it is the one goal crypto cannot deliver — it can only *subtract* from it. But it slots into the same reasoning ladder as every other concern, and it is the cleanest illustration of a point the [taxonomy post](/programming/security-acronyms.html) makes in the abstract: **one goal fans out into several capabilities**, each with its own policy and mechanism rungs. The compressed grids elsewhere print a single "rate-limit / admission control" cell for uptime; that cell is really four. + +## Cube coordinates + +```text +Concerns: + uptime (availability) + +Data state: + at rest | in transit | in use + +Reasoning layers: + goal → capability → policy → mechanism + +Control modality: + technology + process + human (heavy on the last two) +``` + +## One goal, four capabilities + +Goal is fixed at the top — keep the service reachable (`availability` / *is it reachable?*). It fans out into four capabilities, and each drops through its own *policy* (which rules?) and *mechanism* (which runtime engine?): + +| Capability · *what?* | Policy · *which?* (rules / specs) | Mechanism · *how?* (runtime engine) | +|---|---|---| +| **Rate-limiting / admission control** — cap the load any one source can impose | per-key quotas ("100 req/s/tenant", "10 req/min anon"); token-bucket refill rate + burst size; QoS priority classes (premium served first under contention); over-limit action (429 vs. queue vs. drop) | token/leaky-bucket counters (Redis-backed); `nginx limit_req`; API-gateway limiters (Envoy/Kong); concurrency limiters / load shedding; **SYN cookies** at the TCP layer | +| **Redundancy / replication** — keep spares so one failure isn't fatal | replication factor (N=3); placement constraints (spread across 3 AZs/racks); consistency level (quorum W+R>N, sync vs. async); failover policy (auto- vs. manual-promote); how many faults to tolerate (f) | Raft/Paxos consensus; primary-replica DB replication; load balancer + health checks; k8s ReplicaSets; anycast / DNS failover; heartbeats + VIP failover (keepalived) | +| **Backup / recovery** — be able to *restore* after loss or corruption | schedule (hourly/daily) + retention (GFS rotation: 30 dailies, 12 monthlies); **RPO** (tolerable data loss) & **RTO** (max time-to-restore); 3-2-1 rule; backup immutability / air-gap (ransomware); restore-test cadence | snapshots (ZFS/EBS); incremental tools (restic, borg, `pg_basebackup`+WAL); versioned object store w/ object-lock (S3 WORM); cold storage (Glacier); point-in-time recovery | +| **Incident response** — detect, contain, recover when the automated layers fail | severity classes (SEV1/2/3) + escalation paths; on-call / paging rotation; SLOs + error budgets that *trigger* response; who may declare an incident / initiate failover; blameless post-mortem requirement; comms plan (status-page updates) | PagerDuty/Opsgenie; alert rules (Prometheus/Grafana/Datadog); Slack war-room; status-page tooling; feature-flag kill switches; automated rollback pipelines; chaos drills (the rehearsal) | + +Two things this grid makes visible, both reinforcing the taxonomy: + +- **The policy rung here is *thresholds and schedules*, not access predicates.** For the *access* concern, policy is the multi-W predicate — *who* may do *what* to *which* object *when*. For availability it collapses to numbers and timetables: quotas, replication factors, RPO/RTO, severity thresholds. Same rung, very different flavor of rule — good evidence the layer axis is real and concern-independent. +- **The control modality drifts toward process and people.** A backup *retention policy* and an IR *escalation path* are process controls; the on-call human is a people control. Where crypto's cells (AEAD, signatures) are nearly pure technology, availability's policy/mechanism cells are dominated by McCumber's process/human modality. That is exactly *why* it sits in ops, not in ciphers. + +## Why crypto only subtracts + +Crypto delivers none of the four capabilities above. Worse, it tends to *cost* availability — the same six failure modes the capstone's [*Killable*](/programming/what-crypto-still-doesnt-give-you.html) section enumerates: + +- expensive handshakes create DoS surface +- lost keys are permanent denial of access +- ransomware is confidentiality turned against the owner +- KMS outages make encrypted data unreadable +- certificate expiry takes down production +- revocation mistakes brick fleets + +The only places crypto *helps* uptime are indirect and second-order: authenticating admission-control cookies (SYN-cookie-style), and protecting the *confidentiality and integrity* of backups so recovery is trustworthy. The reachability itself comes from redundancy, limits, and recovery drills. + +## Across the data states + +Availability changes shape by where the data is — the same split the [intro's uptime row](/programming/crypto-series-intro.html) sketches: + +| State | Dominant capability | Typical mechanisms | +|---|---|---| +| **At rest** | backup / recovery | snapshots, immutable/offline backups, point-in-time restore, key-escrow tradeoffs | +| **In transit** | rate-limiting / admission control | SYN cookies, retries with backoff, anti-DoS, load balancing | +| **In use** | redundancy / failover | replicas, resource isolation, graceful degradation, circuit breakers | + +## Where this connects + +- The [taxonomy post](/programming/security-acronyms.html) places `uptime` as the availability leg of the concern grid and notes crypto "only *subtracts*" from it — this page is the worked expansion of that one cell. +- The [storage post](/programming/data-at-rest-storage-kms-and-recovery.html) covers the *backup / recovery* capability in depth (immutable backups, ransomware, rollback, lost-key recovery). +- The [keys post](/programming/keys.html) covers the flip side: a key is "both a security control and an availability dependency" — non-extractability improves secrecy but forces availability through a mediator. +- The [capstone](/programming/what-crypto-still-doesnt-give-you.html) is where availability appears as negative space; this appendix is the positive version. diff --git a/assets/crypto-series-intro/mccumber-cube.png b/assets/crypto-series-intro/mccumber-cube.png new file mode 100644 index 0000000000000000000000000000000000000000..2998ffa6cf7753640a079cc27ca4085ffed921cb GIT binary patch literal 80918 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

~fKxOL#*dOS6SEOrerSTpeu|#_{ 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