Cryptographic Protocol Design and Analysis Questions
Designing and reasoning about cryptographic protocols and secure channels: how message flows, key-exchange handshakes, and end-to-end encryption systems are constructed so that composing individual primitives yields a provably or informally verified secure whole. Covers authentication and key-exchange protocol design (mutual authentication, forward secrecy, key confirmation, key-compromise-impersonation resistance), message-flow and state-machine security, formal and informal protocol verification (BAN logic, symbolic tools such as ProVerif and Tamarin, game-based reduction proofs), protocol-level vulnerability analysis (downgrade, replay, padding-oracle, algorithm-confusion attacks), TLS handshake and key-schedule internals, and end-to-end encryption system design (ratcheting, group key agreement, key transparency, post-compromise security). This is the design and analysis layer: why a protocol construction is secure, not which library call or key-management process to run in production. Distinct from selecting and operating cryptographic primitives day to day (certificate lifecycle management, TLS deployment monitoring and incident response, key rotation operations, algorithm and parameter selection for a given constraint set), which belongs to applied cryptography and key management; from core cryptographic vocabulary and primitive fundamentals; and from implementation-level bugs (side-channel leakage, memory-safety flaws, timing attacks in code), which belong to cryptographic implementation security.
In authentication logics, injective agreement (each run on one side corresponds to a distinct run on the other) is a strictly stronger guarantee than non-injective agreement. Give an example protocol that satisfies non-injective authentication but fails injective authentication, and explain what that gap means in practice for replay defense and session uniqueness.
Sample Answer
Direct answer
Non-injective agreement only guarantees that whenever the initiator, call it A, completes a run believing it has authenticated the responder B, SOME earlier run of B produced the data A is checking; it says nothing about whether that earlier B run is the ONLY B run A's completion could be pointing at. Injective agreement adds exactly that: a one-to-one correspondence, so every completed A-side run maps to a DISTINCT B-side run. A protocol can satisfy the weaker, non-injective property while completely failing the stronger one whenever the responder's proof of participation does not bind a value unique to each specific run the INITIATOR opens; a captured, genuinely valid response from one real run of B can then be replayed to make several different A-side sessions all complete, even though B only ever ran the protocol once. That gap matters in practice anywhere a system treats "session completed" as authorization to bill, log, or grant a one-time entitlement exactly once per real event.
Structured elaboration
The definitions, precisely. Following the standard formal hierarchy for authentication properties, attributed to Gavin Lowe's treatment of authentication specifications, which orders properties from weakest to strongest as aliveness, weak agreement, non-injective agreement, and injective agreement, for a protocol where A is meant to authenticate B on a set of data items:
- Non-injective agreement: whenever A completes a run of the protocol, apparently with B, then B has previously been engaged in SOME run of the protocol, apparently with A, agreeing on the relevant data. Existence, not uniqueness.
- Injective agreement: the same guarantee, PLUS each such completed run of A corresponds to a UNIQUE run of B, i.e., the mapping from A's completions to B's runs is one-to-one, not many-to-one.
A concrete protocol that satisfies one and not the other. Let A and B share a long-term key K, and define MACK(m) as a standard keyed MAC (message authentication code) function. The protocol, "SimpleAuth," is meant to let A confirm B is live and participating in THIS specific session:
A -> B: "AUTH_REQUEST", A(A asks B to prove it is live; A does not include any per-session challenge of its own in what B will sign)B -> A: N_B, MAC_K(N_B)(B generates a fresh nonce NB for this run and MACs it under the shared key)- A checks that MACK(NB) is valid for the NB it just received. If valid, A completes, believing B was live and participated in this run.
Why non-injective agreement holds. Only A and B know K (assume B never authenticates to anyone else and A is honest), so any (NB,MACK(NB)) pair that verifies really was produced by B, at some point, running the protocol. Whenever A completes, there genuinely IS a prior run of B that generated exactly the data A is checking. That is precisely the non-injective guarantee, and it holds even under replay, because replay does not forge a NEW valid MAC, it only reuses a real one B already produced.
Worked example
Why injective agreement fails, concretely. Nothing A sends in message 1 is bound into what B signs in message 2, so B's proof of liveness for one run is INDISTINGUISHABLE from B's proof of liveness for any other run, from A's point of view, as long as the nonce value matches what A was just handed. An on-path or replay-capable attacker, call her Eve, who needs no knowledge of K, only the ability to observe and later re-inject messages, does the following, using concrete toy values so the construction is checkable by hand:
- Run 0 (the one real execution of B).
A0 -> B: "AUTH_REQUEST", A.B -> A0: N_B = 7331, MAC_K(7331) = t(some real MAC valuet, computed once, by B, under its actual key).A0checksMAC_K(7331) == t: valid.A0completes, believing session 0 authenticated B. Eve records the pair(7331, t). - Run 1 (a session B never sees).
A1 -> B: "AUTH_REQUEST", A, intercepted by Eve and never forwarded. Eve replies toA1directly:N_B = 7331, MAC_K(7331) = t, the exact recorded pair from Run 0.A1checksMAC_K(7331) == t: valid, because it IS the same value B genuinely produced once.A1completes, believing session 1 authenticated B. - Run 2 (another session B never sees). Same replay, again intercepted and answered from Eve's recorded pair.
A2completes, believing session 2 authenticated B.
A now has at least three completed runs, A0, A1, A2, each believing it freshly authenticated a live B "in this session." But B executed the protocol exactly ONCE, in Run 0, and never received or responded to A1's or A2's requests at all. Every one of A1's and A2's completions is non-injectively justified, Run 0 of B really did produce that exact (N_B, MAC) pair, satisfying the "some prior run exists" requirement, but there is no INJECTIVE mapping from {A0, A1, A2} to B's runs, because all three map to the SAME single run of B. That is the injective-agreement violation, made concrete: 2 or more initiator-side completions, one real responder-side run.
The root cause, stated precisely. The break is that B's proof binds a value B chose (NB), but nothing A chose for THIS specific session. If step 1 had A send its own fresh nonce NA, and step 2 had B compute MACK(NA,NB) covering BOTH nonces, the exact same replay would fail: Eve's captured pair only verifies against the specific NA Run 0's A instance sent, and A1's and A2's freshly generated NA values would not match, so MAC_K(N_A_1, N_B) computed on the captured NB would not equal the captured t, and verification would fail. Binding a run-unique value contributed by the party doing the CHECKING is exactly what turns "some matching run exists somewhere" into "this specific run corresponds to a unique matching run."
Trade-offs and pitfalls
- It is tempting to think "the MAC is valid, so it's authenticated," and stop there; validity of the authentication data is exactly the non-injective guarantee, and by itself says nothing about session uniqueness. A code review that only checks "is the cryptographic primitive used correctly", yes,
MAC_Kis a real, unforgeable MAC here, will miss this class of bug entirely, because the primitive is not misused, the PROTOCOL is under-specified. - The practical stakes are concrete, not academic: a payment or entitlement system that grants a one-time benefit "once per authenticated session," issue a coupon, decrement inventory, log a billable event, can be tricked into granting that benefit multiple times for what the responder considers a SINGLE real event, exactly the shape of the replay above. Session or transaction uniqueness needs injective agreement; mere non-injective agreement is not enough to prevent this, no matter how strong the underlying MAC or signature scheme is.
- The fix generalizes beyond this toy example: any responder proof that omits binding something the VERIFIER contributed to THIS run, a fresh nonce, a session identifier, a sequence number tied to a specific transaction, is a candidate for exactly this gap, whether the underlying primitive is a MAC, a signature, or an authenticated key exchange.
Explain what 'key confirmation' means in a key-exchange protocol. Provide an example attack that becomes possible if parties do not confirm derived keys, and describe at least two protocol-level mechanisms that provide explicit confirmation.
Sample Answer
Direct answer
Key confirmation is the step, after a key exchange, where both parties prove to each other that they actually derived the same session key, rather than just assuming the exchange worked. Without it, a mismatch, whether caused by an active tamperer, a subtle implementation bug, or a protocol confusion, goes completely undetected: both sides silently start "encrypting" application data under keys that do not agree, and neither one learns anything is wrong until the data is unreadable, or worse, readable by the wrong party.
Structured elaboration
A key exchange (such as Diffie-Hellman) produces a shared value at each side independently; nothing about the exchange itself guarantees the two computed values match, only that they would match if every message arrived unmodified and both implementations agree on every input to the computation. Key confirmation closes that gap by having each side compute a value derived from its own key, that only someone holding the same key could reproduce (typically a MAC, message authentication code: a keyed cryptographic checksum), and send it to the other side to be checked against an independently recomputed value. A mismatch causes an immediate, explicit abort instead of a silent, undetectable divergence.
Worked example
A small, checkable Diffie-Hellman exchange, then a scenario where a single bit is corrupted in transit with and without confirmation:
"""
key confirmation. Two parties run an (unauthenticated, for clarity)
Diffie-Hellman exchange and derive a session key each. Without a confirmation
step, a silent key mismatch (here: a single flipped bit in a transmitted
public value, standing in for either an active tamperer or a plain
implementation bug) goes completely undetected -- both sides proceed to
"encrypt" under keys that don't match. With an HMAC-based confirmation
message (exactly what a "Finished" message does in TLS, Transport Layer
Security, and SSH), the mismatch is caught immediately. Small pinned DH
parameters so every number is checkable by hand; stdlib only.
"""
import hashlib
import hmac
# Small (INSECURE, for a checkable worked example only) DH group.
P = 2_147_483_647 # a 31-bit Mersenne prime, large enough that the demo
G = 5 # numbers aren't trivially guessable, small enough to read
def dh_keypair(private: int):
return private, pow(G, private, P)
def confirmation_tag(shared_secret: int, label: bytes) -> bytes:
key = hashlib.sha256(str(shared_secret).encode()).digest()
return hmac.new(key, label, hashlib.sha256).digest()
if __name__ == "__main__":
a_priv, A_pub = dh_keypair(123456789)
b_priv, B_pub = dh_keypair(987654321)
print(f"Alice's public value A = g^a mod p = {A_pub}")
print(f"Bob's public value B = g^b mod p = {B_pub}")
alice_shared = pow(B_pub, a_priv, P)
bob_shared = pow(A_pub, b_priv, P)
print(f"\nAlice computes B^a mod p = {alice_shared}")
print(f"Bob computes A^b mod p = {bob_shared}")
print("agree (as expected for honest DH):", alice_shared == bob_shared)
print("\n=== Scenario: NO key confirmation, one bit of B corrupted in transit ===")
B_pub_corrupted = B_pub ^ 1 # a single flipped bit: tamper, or a real bug
alice_shared_wrong = pow(B_pub_corrupted, a_priv, P)
print(f"Alice receives a corrupted B' = {B_pub_corrupted} (off by one bit)")
print(f"Alice computes B'^a mod p = {alice_shared_wrong}")
print("Alice's key equals Bob's key:", alice_shared_wrong == bob_shared)
print("Without confirmation, Alice has no way to learn this and starts")
print("sending application data under a key Bob never derived.")
print("\n=== Same scenario, WITH an HMAC-based confirmation message ===")
alice_tag = confirmation_tag(alice_shared_wrong, b"session-confirm")
bob_tag = confirmation_tag(bob_shared, b"session-confirm")
print("Alice's confirmation tag:", alice_tag.hex())
print("Bob's confirmation tag: ", bob_tag.hex())
print("Bob verifies Alice's tag against his own key:",
hmac.compare_digest(alice_tag, bob_tag))
print("-> Bob detects the mismatch immediately and aborts instead of")
print(" silently exchanging data under keys that don't agree.")
print("\n=== Control: honest exchange, confirmation succeeds ===")
alice_tag_ok = confirmation_tag(alice_shared, b"session-confirm")
bob_tag_ok = confirmation_tag(bob_shared, b"session-confirm")
print("tags match:", hmac.compare_digest(alice_tag_ok, bob_tag_ok))
Output:
Alice's public value A = g^a mod p = 1891294900
Bob's public value B = g^b mod p = 1686535327
Alice computes B^a mod p = 753432501
Bob computes A^b mod p = 753432501
agree (as expected for honest DH): True
=== Scenario: NO key confirmation, one bit of B corrupted in transit ===
Alice receives a corrupted B' = 1686535326 (off by one bit)
Alice computes B'^a mod p = 2096047033
Alice's key equals Bob's key: False
Without confirmation, Alice has no way to learn this and starts
sending application data under a key Bob never derived.
=== Same scenario, WITH an HMAC-based confirmation message ===
Alice's confirmation tag: aad1c945ff17c123a695d1bf8b98d9329a1f28ff6e4a714a87d19136fbc18958
Bob's confirmation tag: 21a9de9b5c07e34a9da5a2e82085d638fe9277fd68fa2fc76b54221b6ebd35bb
Bob verifies Alice's tag against his own key: False
-> Bob detects the mismatch immediately and aborts instead of
silently exchanging data under keys that don't agree.
=== Control: honest exchange, confirmation succeeds ===
tags match: True
Without confirmation, Alice silently starts using a key Bob never derived. With an HMAC-based confirmation tag exchanged after key derivation, Bob's comparison fails immediately and the session aborts instead of proceeding on mismatched keys.
Two protocol-level mechanisms that provide explicit confirmation
- MAC-based confirmation messages. Each side computes and sends an HMAC over an agreed label (or the handshake transcript) keyed by its derived key; the other side recomputes and compares. This is exactly what TLS and SSH "Finished" messages do, sent by both sides and verified before either one trusts the connection.
- Encrypt/decrypt a known or previously agreed value. One side encrypts a value the other side can predict (or a value it committed to earlier) under the derived key; if the receiving side's independently-derived key does not match, decryption produces garbage or fails an integrity check, again giving an explicit, checkable signal rather than silence.
Trade-offs and pitfalls
- Key confirmation catches any source of mismatch, not just an active attacker: implementation bugs, version confusion, and bit-flip corruption all get caught by the same mechanism, which is a large part of its value, it does not require attributing the failure to a cause before reacting to it.
- Confirmation on its own does not stop a MITM (an active network attacker between the two parties) from establishing two separate keys, one with each victim, and relaying between them; each side's confirmation with the attacker still succeeds locally, since the mismatch confirmation catches is "my key versus my intended peer's key," not "is my peer who I think it is." Preventing that requires authenticating the exchange itself (signed or certificate-bound key material), a separate property from confirmation.
Analyze the practical impact of the Logjam attack (weak DH parameters) combined with an infrastructure that still accepts export-grade or small-group DH. For a large organization, propose detection rules, short-term mitigations, and a prioritized remediation roadmap to eliminate weak DH usage across services and clients.
Sample Answer
Direct answer
Logjam (2015) combined a downgrade attack with a precomputation attack: it forced TLS (Transport Layer Security) connections down to export-grade Diffie-Hellman (DH), a deliberately weakened 512-bit key-exchange group left over from 1990s cryptography export restrictions, and exploited the fact that many servers reused the same small set of well-known DH primes, letting an attacker do the expensive part of breaking discrete logarithms for a given prime once, then break any individual connection using that prime cheaply and repeatedly afterward. The organizational response is a two-track roadmap: eliminate export-grade and small-group DH everywhere immediately, and separately track down and replace any remaining shared, non-unique DH primes, since either weakness alone is enough to make this attack practical.
Structured elaboration
How the attack works. TLS historically supported *_DHE_EXPORT cipher suites, a relic of US export-control rules that once restricted the strength of cryptography allowed in software shipped outside the country, forcing a small (512-bit) DH group. Even servers that no longer intended to actually use these suites often still had them enabled. An active attacker performing a man-in-the-middle (MITM) could rewrite the cipher suite list in a client's ClientHello to make it look like export-grade DHE was the only option, causing the server to negotiate the weak group even though both real endpoints supported something much stronger, the downgrade half of the attack. The precomputation half exploits a separate, compounding weakness: generating a fresh, safe DH prime is computationally expensive, so many implementations, and in particular many servers, shipped with the same small set of hard-coded, widely reused primes. The Number Field Sieve (NFS), the best known classical algorithm for computing discrete logarithms in this kind of group, has an expensive one-time precomputation phase that depends only on the prime itself, not on any particular connection; once that precomputation is done for a specific, widely-shared prime, computing any individual connection's discrete logarithm (and therefore its DH shared secret) using that same prime becomes comparatively cheap. Reusing the same prime across a huge number of deployments is exactly what turns a merely-expensive attack into one worth mounting at all, the cost is paid once and amortized across every server sharing that prime.
Detection. Fleet-wide TLS scanning for any negotiable *_DHE_EXPORT cipher suite, since offering it at all is the precondition for the downgrade half of the attack regardless of whether a legitimate client would ever choose it. Separately, scanning the actual DH group size (p) offered in non-export DHE negotiations and flagging anything below 2048 bits, and comparing offered primes against a known list of common, non-unique DH primes (the ones shipped as defaults in widely used server software), since a server using one of those, even at a larger bit size, inherits the same amortized-precomputation risk as export-grade DH does at a smaller one.
Short-term mitigations. Disable *_EXPORT cipher suites entirely in the server's TLS configuration, this removes the downgrade target outright and requires no code change. Require any remaining DHE groups to be at least 2048 bits, and prefer switching to elliptic-curve Diffie-Hellman (ECDHE) with a modern curve, or to the standardized finite-field groups defined in RFC 7919, both of which are large, well-vetted, and shared broadly enough by design that there is no incentive or ability for an attacker to precompute against one organization's bespoke small group.
Prioritized remediation roadmap:
- Immediate (config-only, no code change): disable export cipher suites fleet-wide via a policy push, closing the acute exposure the same day it is found.
- Short-term: inventory every remaining service still offering DH groups below 2048 bits, and migrate them to either ECDHE or an RFC 7919 standardized group; treat any custom, locally-generated DH parameters as suspect until confirmed unique and adequately sized.
- Medium-term: move affected services to TLS 1.3, which structurally removes export ciphers from the protocol and mandates modern (EC)DHE groups only, so the whole attack class becomes unavailable by protocol design rather than by configuration discipline.
- Ongoing: fold the weak-DH and export-cipher checks into recurring, automated external TLS scanning, so a regression, for example a newly deployed load balancer shipped with legacy defaults re-enabled, is caught automatically rather than by the next external audit.
Worked example
A large organization runs external TLS scanning across its public-facing fleet and finds that 40 of 300 scanned endpoints still offer at least one *_DHE_EXPORT suite, most of them older internal tools that were never explicitly configured, they simply inherited an old default TLS configuration template. Cross-referencing the non-export DHE groups offered by the remaining 260 endpoints against a list of known common DH primes shows 15 endpoints using one specific 1024-bit prime that ships as the default in a popular, older web-server package. The prioritized roadmap above would push the fix for the 40 export-suite endpoints to the top (config-only, same-day), and separately schedule the 15 shared-prime endpoints for a parameter regeneration or ECDHE migration in the short-term track, since neither weakness depends on the other to be exploitable.
Trade-offs and pitfalls
It is tempting to treat "increase the DH group size" as the whole fix, but a large, UNIQUE, freshly-generated prime is not the same mitigation as a large, WIDELY-SHARED one, size alone does not remove the amortized-precomputation incentive if thousands of other deployments use the identical prime; the standardized RFC 7919 groups solve this by being large enough that even shared, well-known use is not practically exploitable, rather than by being secret or unique. A second pitfall is treating this purely as a detection problem: a scanner that only checks for export suites and misses the separate small-or-shared-prime issue will report a fleet as clean while it is still carrying the compounding weakness that made Logjam practical at scale in the first place.
Describe mutual TLS (mTLS): what changes in the TLS handshake, how the server validates the client certificate, and where you'd actually use it in production (service-to-service auth, zero-trust internal traffic, partner APIs).
Sample Answer
Direct answer
Mutual TLS (mTLS) is ordinary TLS (Transport Layer Security) with authentication running in both directions instead of one: the server proves its identity as usual, and additionally requests and verifies a certificate from the client, so both ends of the connection cryptographically prove who they are before any application data flows.
Structured elaboration
What changes in the handshake. In a normal (one-way) TLS handshake, only the server presents a certificate. In mTLS, after the server sends its own Certificate, CertificateVerify, and reaches the point of finishing its side, it also sends a CertificateRequest message, naming which certificate authorities (CAs) it will accept a client certificate from. The client then must respond with its own Certificate message, followed by its own CertificateVerify message, in which the client signs the running handshake transcript using its certificate's private key, proving it actually holds that private key rather than just having copied a public certificate file.
How the server validates the client certificate. The server performs the same kind of chain validation it would expect a browser to perform on a server certificate: it verifies the certificate chains up to a CA it trusts (typically a private, internally-operated CA for service-to-service mTLS, rather than a public one), checks the certificate's validity window, optionally checks revocation status, and verifies the CertificateVerify signature against the client certificate's public key over the negotiated transcript, which is what proves possession of the private key rather than mere presentation of a public certificate someone could have copied. Beyond raw validity, the server typically also performs an authorization step: checking the certificate's subject or SAN (Subject Alternative Name) against an expected identity, for example "this connection claims to be payments-service, and the certificate's SAN actually says payments-service," since a validly-issued certificate for the wrong service should still be rejected for this particular endpoint.
Where it is actually used in production. mTLS shows up wherever both ends need to be sure who is talking to whom, not just that the channel is encrypted: service-to-service authentication inside a zero-trust internal network or service mesh (so that any two services can prove their identities to each other without relying on network location alone), and partner-facing or B2B APIs, where a business partner is issued a certificate rather than relying only on an API key.
Worked example
Trace the added messages for a client (a payments service) connecting to a server (a ledger service) inside an internal mesh: ClientHello and ServerHello negotiate parameters as usual; the server sends Certificate (its own), then CertificateRequest naming the internal CA it trusts; the client responds with Certificate (proving it was issued by that same internal CA) and CertificateVerify (a signature over the transcript so far, made with the payments service's private key); the server verifies that signature against the public key in the client's certificate, checks the certificate's SAN says payments-service, and only then proceeds to derive traffic keys and accept application data. If an attacker without a valid client certificate attempts the same connection, it has no private key to produce a valid CertificateVerify, so the handshake fails before any application data is exchanged, regardless of whether the attacker can reach the network the ledger service listens on.
Trade-offs and pitfalls
The mechanics above are the part worth being precise about in an interview; the operational side, issuing and rotating short-lived client certificates at fleet scale, is a real cost but belongs more to how a service mesh or an internal CA is operated day to day than to the handshake mechanics themselves. The one mechanics-adjacent pitfall worth naming: verifying the certificate chain without also checking the SAN against the expected caller identity is a common, exploitable gap, since a validly-issued certificate for a different, unrelated service would still pass pure chain validation, and only the identity check catches that it should not be trusted for this specific connection.
You use a third-party CDN that terminates TLS for edge performance. Describe options to ensure end-to-end confidentiality without uploading origin private keys to the CDN: keyless TLS, origin-pull TLS, mutual TLS to origin, and encrypted re-encryption. For each option discuss latency, key exposure, complexity and failure modes.
Sample Answer
Direct answer
All four options keep the origin's private key off the CDN (content delivery network), but they differ in where trust and failure risk concentrate. Keyless TLS keeps the private key strictly at the origin (or a signing service you control) and has the CDN ask for a signature per handshake; it gives the strongest key-exposure guarantee but makes your signing service a hard dependency for every new connection. Mutual TLS (mTLS, where both sides present and verify certificates) between CDN and origin and application-layer re-encryption both keep the origin private key off the CDN too, but they still require provisioning some key material to the CDN (a client certificate, or nothing at all if re-encryption is fully application-layer) and each shifts complexity to a different place: mTLS to certificate lifecycle management, re-encryption to application code.
Structured elaboration
- Keyless TLS (the CDN performs the TLS handshake's public operations but calls out to your infrastructure for the private-key operation itself): latency adds one extra network round trip to the signer on every new handshake (not on session resumption, which reuses the earlier result); key exposure is minimal, the origin key never leaves your control; complexity is real but bounded to running a highly available signing endpoint with strong CDN-to-signer authentication; the failure mode is that a signer outage blocks new TLS sessions at the edge until caches expire, so the signer becomes an availability-critical dependency, not just a security one.
- Origin-pull TLS (edge terminates the client's TLS session; the CDN opens a separate TLS connection back to the origin using its own certificate/key for that leg): latency is low once the CDN-to-origin connection is pooled and reused across requests, though the first request to a given origin pays a full extra TLS handshake; simplest to set up if the CDN supports it, but if you provision that CDN-to-origin key yourself, you have handed the CDN a key, just not the client-facing one; failure mode is origin unreachability or a stale certificate on that leg blocking content fetches.
- Mutual TLS to origin (the origin authenticates the CDN via a client certificate, and vice versa): latency is close to origin-pull TLS, with a small added cost for client-certificate verification on top of the same pooled connection; adds identity verification on top of origin-pull, at the cost of provisioning and rotating a CDN-facing client certificate; failure mode is a misissued or expired client certificate silently blocking every CDN fetch, often surfacing as clock-skew-shaped errors that are easy to misdiagnose.
- Encrypted re-encryption (the payload is encrypted at the application layer; the CDN only ever handles ciphertext, optionally re-wrapping a symmetric key rather than re-encrypting the whole payload): latency at the TLS layer is unchanged (the edge still terminates client TLS normally), but application-level encrypt/decrypt work adds CPU-bound processing time on every request that none of the other three options need, which is the real latency cost here; it is the strongest confidentiality guarantee of the four, since the CDN cannot read content even if it fully controls the TLS layer, but it requires application-level changes, its own key-management story, and careful handling of caching (a CDN caching opaque ciphertext is only useful if identical plaintext reliably produces cacheable, matchable ciphertext, which reintroduces the same tension between deterministic (cache-friendly, but equality-leaking) and randomized (private, but not cacheable) encryption that shows up anywhere a middlebox needs to act on ciphertext it cannot decrypt).
Worked example
Concretely, sizing the operational surface of each option for a service fronting one origin behind a CDN: keyless TLS adds exactly one new network dependency (the signer) that must be reachable on every new handshake but not on session resumption; origin-pull TLS and mTLS both add one certificate lifecycle to manage on the CDN-to-origin leg (versus zero extra certificates for a plain client-facing-only TLS setup); re-encryption adds no new TLS-layer certificate at all, but adds an application-level encrypt/decrypt step on every request that did not exist in any of the other three options. That progression, from "one new network dependency" to "one new certificate" to "one new code path," is a reasonable way to reason about where the engineering cost actually lands for each choice, independent of which one is ultimately selected.
Trade-offs & pitfalls
The most common mistake is treating "the CDN doesn't have the origin's TLS private key" as equivalent to "the CDN cannot read my traffic," when in options 2 and 3 the CDN still terminates the client-facing TLS session and sees plaintext at the edge, just not under the origin's specific key; only option 4 (or true keyless TLS combined with never letting the CDN decrypt at all, which is not how CDNs are normally used) actually keeps content confidential from the CDN operator. A second pitfall specific to keyless TLS: if the signing service and the CDN's connection to it are not independently monitored, a signer degradation looks identical to a generic TLS handshake failure, which delays root-causing an outage that is actually about the signing dependency, not the CDN or the origin.
Unlock Full Question Bank
Get access to all Cryptographic Protocol Design and Analysis interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.