% crypto-cryptokit.tex — applied cryptography for an app developer: AEAD
% (AES-GCM, ChaCha20-Poly1305) and nonces, ECDH + HKDF, signatures, KDFs,
% envelope encryption, and the CryptoKit API that does each; the mistakes.
% Source: docs/memos/security-crypto-fundamentals.md (checked against
% docs/school/notes/knowledge-gaps-2026-09-23.md). Source error found: Q15
% describes HMAC as "H(key ‖ ... ‖ key)-style" — HMAC is the nested
% H((K⊕opad) ‖ H((K⊕ipad) ‖ m)); the sheet states the real construction.
% Hashes / passwords / HMAC internals: cs/hashing-deep.tex.
% Keychain + Secure Enclave storage: security-build/keychain-secure-enclave.tex.
% Build ONLY with: tools/print/print-sheet.py <this>.tex --dry-run
% @source: hiot monorepo, docs/school/sheets/cs/crypto-cryptokit.tex — the SOURCE OF TRUTH; a copy anywhere else (e.g. artur.gurgul.pro) is regenerated from it, never edited
% @labels: area=cs kind=concept level=senior platform=apple new=no round=missing-2026-09-25 topic=security
% @tags: cryptokit, aead, aes-gcm, chacha20-poly1305, nonce-reuse, ecdh, hkdf, ecdsa, ed25519, envelope-encryption, secure-enclave, forward-secrecy
\documentclass[8pt]{extarticle}
\usepackage{printup-sheet}
\usepackage{array}

\lstdefinelanguage{SwiftSheet}{
  morekeywords={protocol,class,final,struct,enum,func,var,let,weak,init,import,
    if,else,return,guard,self,nil,try,await,async,throws,private,some,static,
    true,false,AnyObject,Void,String,Bool,Int,Data},
  sensitive=true, morecomment=[l]{//}, morecomment=[s]{/*}{*/}, morestring=[b]"}
\lstset{basicstyle=\ttfamily\scriptsize, aboveskip=2pt, belowskip=2pt}

\newcommand\ct[1]{\texttt{#1}}
\tikzset{
  s/.style={box, font=\tiny, minimum height=5mm, inner sep=1.5pt},
  g/.style={s, draw=sheetGreen, fill=sheetGreen!10},
  r/.style={s, draw=sheetRed, fill=sheetRed!7},
  o/.style={s, draw=sheetOrange, fill=sheetOrange!10},
  hd/.style={font=\bfseries\small, text=sheetBlue, anchor=west},
  l/.style={font=\tiny, text=black!75, align=center, inner sep=1pt},
  seg/.style={draw=sheetGrey, minimum height=4.5mm, font=\ttfamily\tiny, inner sep=1pt},
}

\begin{document}

\sheettitle{Cryptography with CryptoKit — AEAD, ECDH, signatures, KDFs}{cs · memo}

\oneliner{\textbf{Symmetric} AEAD (AES-GCM, ChaCha20-Poly1305) encrypts
\emph{and} authenticates bulk data with one shared key and a \textbf{unique
nonce}; \textbf{asymmetric} keys (P-256, Curve25519) \emph{agree} on that key
(ECDH → HKDF) and \emph{sign} (ECDSA, Ed25519). CryptoKit gives vetted
primitives with safe defaults — your job is choosing them, managing keys and
nonces, and \textbf{never inventing} a primitive, mode or protocol.}

\vspace{2pt}
\noindent\begin{tikzpicture}[sheet]
  % ===== ECDH -> HKDF -> AES-GCM
  \node[hd] at (-0.1,2.3) {Agree → derive → seal (the shape of TLS 1.3)};
  \node[s, minimum width=19mm] (al) at (0.9,1.45) {\textbf{app}\\priv $a$ · pub $A$\\(ephemeral)};
  \node[s, minimum width=19mm] (bo) at (6.6,1.45) {\textbf{server}\\priv $b$ · pub $B$\\(ephemeral)};
  \draw[hot] ([yshift=4pt]al.east) -- node[l, above]{send $A$ (public, may be seen)} ([yshift=4pt]bo.west);
  \draw[hot] ([yshift=-4pt]bo.west) -- node[l, below]{send $B$ — \textbf{verify it} (sig / pin) or MITM} ([yshift=-4pt]al.east);
  \node[o, minimum width=30mm] (ss) at (3.75,0.35) {shared secret = ECDH($a$, $B$) = ECDH($b$, $A$)\\\emph{not} a key yet — uneven bits};
  \draw[flow] (al.south) |- (ss.west); \draw[flow] (bo.south) |- (ss.east);
  \node[g, minimum width=30mm] (kd) at (3.75,-0.5) {HKDF-SHA256(secret, salt, info = ``chat v1'')\\→ 256-bit \ct{SymmetricKey}};
  \draw[flow] (ss) -- (kd);
  \node[l, anchor=west, align=left, text=sheetGreen!45!black] at (5.75,-0.05) {discard $a$, $b$ after:\\\textbf{forward secrecy}};
  % sealed box layout
  \node[seg, fill=sheetBlue!10, minimum width=12mm] (n) at (0.6,-1.35) {nonce 12 B};
  \node[seg, fill=sheetOrange!12, minimum width=27mm, anchor=west] (ct) at (n.east) {ciphertext (= plaintext length)};
  \node[seg, fill=sheetGreen!15, minimum width=12mm, anchor=west] (tg) at (ct.east) {tag 16 B};
  \node[l, anchor=west] at (5.45,-1.35) {= \ct{box.combined}};
  \draw[flow] (kd.south) -- node[l, right]{\ct{AES.GCM.seal}} (3.75,-1.1);
  \node[l, anchor=west, align=left] at (-0.05,-1.8) {nonce: public, \textbf{unique per key} · AAD (headers, ids): authenticated, not encrypted, not in the box};
  \draw[sheetGrey!50] (8.25,2.45) -- (8.25,-2.0);
  % ===== envelope encryption
  \node[hd] at (8.3,2.3) {Envelope encryption};
  \node[s, minimum width=13mm] (pt) at (9.0,1.45) {data\\(any size)};
  \node[o, minimum width=12mm] (dek) at (9.0,0.35) {\textbf{DEK}\\random 256-bit\\per object};
  \node[s, minimum width=15mm, fill=black!5, draw=sheetGrey] (ctx) at (11.6,1.45) {ciphertext};
  \draw[hot] (pt) -- node[l, above]{AES-GCM} (ctx);
  \draw[hot] (dek) -- node[l, left]{key} (10.35,1.45);
  \node[r, minimum width=17mm] (kek) at (14.2,-1.05) {\textbf{KEK}: KMS / HSM /\\Secure Enclave —\\never leaves it};
  \node[s, minimum width=15mm, fill=black!5, draw=sheetGrey] (wd) at (11.6,0.35) {wrapped DEK};
  \draw[hot] (dek) -- node[l, above]{wrap} (wd);
  \draw[hot, sheetRed] (kek.west) -| node[l, left, pos=0.7]{wraps} (wd.south);
  \draw[decorate, decoration={brace, amplitude=3pt}, draw=sheetBrown] (12.5,1.65) -- (12.5,0.15) node[midway, right=3pt, l, text=sheetBrown, align=left]{stored\\together};
  \node[l, anchor=west, align=left] at (13.3,1.35) {\textbf{why}: bulk data under a\\fast local key; the master\\key only ever touches\\32-byte DEKs};
  \node[l, anchor=west, align=left] at (13.3,0.2) {\textbf{rotate} the KEK =\\re-wrap DEKs, not\\re-encrypt the data};
  \node[l, anchor=west, align=left] at (8.45,-1.5) {\textbf{revoke} = destroy one DEK (crypto-shredding)\\iOS: SE holds only P-256 → derive a KEK via\\SE key agreement (ECIES), or \ct{AES.KeyWrap}};
\end{tikzpicture}

\begin{multicols}{2}
\footnotesize\setstretch{1.0}

\section{How it works}
\begin{itemize}
  \item \textbf{AEAD} = confidentiality + integrity + authenticity in one call;
        \ct{open} throws on any flipped bit — never ``decrypt then check''.
        \textbf{AES-GCM}: fastest with AES hardware (every Apple chip).
        \textbf{ChaCha20-Poly1305}: fast in pure software, constant-time without
        AES instructions. Both: 256-bit key, 96-bit nonce, 128-bit tag.
  \item \textbf{Nonce reuse} under one key = catastrophe: same keystream, so
        $C_1 \oplus C_2 = P_1 \oplus P_2$, and in GCM it leaks the GHASH key →
        \textbf{forged tags}. Random 96-bit nonces (CryptoKit's default) are safe
        up to $\sim$2\textsuperscript{32} messages per key; then rotate.
  \item \textbf{ECDH}: combine your private with their public → the same
        secret on both sides. Run it through \textbf{HKDF} (extract-then-expand;
        salt + \ct{info} bind it to a purpose) — one secret, many keys.
        \textbf{Ephemeral} keys give forward secrecy.
  \item \textbf{Signatures}: sign with \emph{your private}, anyone verifies with
        your public → authenticity, integrity, non-repudiation, \emph{no}
        confidentiality. \textbf{ECDSA} (P-256) needs a unique per-signature
        nonce — reuse leaks the private key (PS3, 2010); \textbf{Ed25519} is
        deterministic. Encryption runs the other way: \emph{their public} key.
  \item \textbf{Key sizes}: P-256 / Curve25519 $\approx$ 128-bit security
        $\approx$ RSA-3072. CryptoKit has no RSA → \ct{SecKey}.
  \item \textbf{KDFs}: \textbf{HKDF} for high-entropy input (ECDH output, a
        master key) — fast, not for passwords. \textbf{PBKDF2} / Argon2id stretch a
        \emph{password} slowly. CryptoKit has HKDF only; PBKDF2 =
        \ct{CCKeyDerivationPBKDF} (CommonCrypto).
\end{itemize}

\section{Example — the CryptoKit calls}
\begin{lstlisting}[language=SwiftSheet]
import CryptoKit
let mine = Curve25519.KeyAgreement.PrivateKey()          // ephemeral
let secret = try mine.sharedSecretFromKeyAgreement(with: theirPublic)
let key = secret.hkdfDerivedSymmetricKey(using: SHA256.self,
    salt: salt, sharedInfo: Data("chat v1".utf8), outputByteCount: 32)
// AEAD: CryptoKit makes a random 12-byte nonce for you
let box = try AES.GCM.seal(msg, using: key, authenticating: header)
let wire = box.combined!                 // nonce | ciphertext | tag
let plain = try AES.GCM.open(AES.GCM.SealedBox(combined: wire),
                             using: key, authenticating: header)
// signatures; SecureEnclave.P256.Signing.PrivateKey() = same API
let signer = P256.Signing.PrivateKey()
let sig = try signer.signature(for: msg)
let ok = signer.publicKey.isValidSignature(sig, for: msg)
// MAC with its OWN key; the check is constant-time
let tag = HMAC<SHA256>.authenticationCode(for: msg, using: macKey)
let valid = HMAC<SHA256>.isValidAuthenticationCode(tag,
                authenticating: msg, using: macKey)
\end{lstlisting}

\columnbreak

\section{What CryptoKit gives you (iOS 13+)}
{\scriptsize
\begin{tabular}{@{}>{\raggedright\arraybackslash}p{19mm}>{\raggedright\arraybackslash}p{52mm}@{}}
\toprule
\textbf{need} & \textbf{API}\\
\midrule
key & \ct{SymmetricKey(size: .bits256)} — CSPRNG\\
AEAD & \ct{AES.GCM.seal/open}, \ct{ChaChaPoly.seal/open}\\
hash & \ct{SHA256/384/512.hash(data:)}; \ct{Insecure.MD5}, \ct{Insecure.SHA1} — the namespace is the warning\\
MAC & \ct{HMAC<SHA256>}\\
KDF & \ct{HKDF<SHA256>.deriveKey} (iOS 14), \ct{SharedSecret.hkdfDerivedSymmetricKey}\\
agreement & \ct{Curve25519.KeyAgreement}, \ct{P256.KeyAgreement}\\
signing & \ct{Curve25519.Signing} (Ed25519), \ct{P256.Signing} (ECDSA)\\
hardware & \ct{SecureEnclave.P256.Signing / .KeyAgreement}; check \ct{SecureEnclave.isAvailable}\\
\bottomrule
\end{tabular}}\par
\textbf{Secure Enclave} keys are P-256 only; the private key never leaves the
chip — \ct{dataRepresentation} is an \emph{encrypted blob} only that device's
SE can use (store it in the Keychain). Operations can require biometry via
\ct{SecAccessControl}.

\section{Symmetric vs asymmetric}
{\scriptsize
\begin{tabular}{@{}>{\raggedright\arraybackslash}p{14mm}>{\raggedright\arraybackslash}p{27mm}>{\raggedright\arraybackslash}p{29mm}@{}}
\toprule
& \textbf{symmetric} & \textbf{asymmetric}\\
\midrule
keys & one shared secret & public + private pair\\
speed & GB/s (hardware AES) & $\sim$1000$\times$ slower\\
for & bulk data, storage, sessions & key agreement, signatures, identity\\
problem & how to share the key & how to trust the public key (PKI, pinning)\\
\bottomrule
\end{tabular}}\par
Real systems are \textbf{hybrid}: asymmetric to agree or wrap a key,
symmetric for the data (TLS, envelope encryption, ECIES).

\section{Interview traps — the common mistakes}
\begin{itemize}
  \trap{\textbf{ECB} — equal blocks → equal ciphertext (the penguin). CBC
        without a MAC → malleable, padding oracles. Use AEAD.}
  \trap{\textbf{Fixed or reset nonce} (a counter restarting after reinstall,
        two devices sharing a key + counter).}
  \trap{\textbf{Hard-coded keys} — \ct{strings} on the binary finds them. Keys
        are generated on device, kept in Keychain / Secure Enclave.}
  \trap{A \textbf{password as a key} (\ct{SymmetricKey(data: pw.utf8)}) — run a
        slow KDF first.}
  \trap{\textbf{MAC compared with \ct{==}} — timing leaks the first bad byte.}
  \trap{\textbf{One key, two jobs} (encrypt + MAC, two protocols) — derive
        separate keys with HKDF \ct{info}.}
  \trap{Unauthenticated ECDH = key agreement with the attacker (MITM).}
  \trap{Key or nonce bytes from a seeded / custom PRNG (GameplayKit, a test
        generator) — use \ct{SymmetricKey(size:)} / \ct{SecRandomCopyBytes}.}
\end{itemize}

\section{Remember}
\textbf{Agree (ECDH) → derive (HKDF) → seal (AEAD, fresh nonce) → sign what
must be proven.} Don't roll your own — not the primitive, the mode, nor the protocol.

\section{Likely questions}
\begin{enumerate}
  \item GCM nonce reused? — keystream reuse + tag forgery; rotate, random nonces.
  \item Why HKDF after ECDH? — raw secret is not uniform; bind keys to a purpose.
  \item Where does a Secure Enclave key live? — in the SE; the app holds a blob.
  \item Encrypt vs sign — which key? — their public / your private.
  \item Why envelope encryption? — cheap rotation, small master-key exposure.
\end{enumerate}

\end{multicols}

\noindent{\footnotesize\color{sheetGrey}\textit{Related:} hashing-deep (HMAC,
passwords) · keychain-secure-enclave · app-hardening-privacy (ATS, pinning) ·
iot-device-security-provisioning · ntag424-dna-sun (AES-CMAC)}

\end{document}
