Applied Cryptography and Key Management Questions
Selecting and applying cryptographic primitives correctly: symmetric and asymmetric encryption, hashing, digital signatures, key derivation, secure random number generation, and public key infrastructure. Covers key lifecycle management, key exchange and distribution, choosing appropriate algorithms for a given constraint set including resource-constrained environments, and the forward-looking side of algorithm lifecycle: cryptographic agility and algorithm-migration strategy, forward secrecy, and the post-quantum cryptography transition and planning upgrades without breaking existing data or interoperability. The applied-crypto engineering layer, distinct from compliance-driven crypto standards.
Architect a feature-flag-driven rollout system that lets you switch which symmetric encryption algorithm is used in live traffic without downtime. Include client/server negotiation, tagging ciphertexts with algorithm and key-version, dual-write modes, a bulk re-encryption strategy for already-stored blobs, the metrics you'd watch, and automated rollback triggers.
Sample Answer
Direct answer: Make the algorithm choice versioned metadata carried with the ciphertext itself, not a global environment switch. Gate the write path's default algorithm behind a feature flag so you can move the percentage of new writes onto the new algorithm gradually, and let the read path branch on each ciphertext's own tag so old data stays readable forever without a synchronized cutover.
Ciphertext tagging. Store a small, versioned header alongside every ciphertext: an algorithm identifier, a key version, and the nonce (you already need to store the nonce for any AEAD scheme, so this is a small addition, not a new mechanism). This makes every ciphertext self-describing: a reader never needs external context to know how to decrypt it, which is exactly what makes instant rollback possible later.
Client/server negotiation. For a live protocol with two active parties, both sides advertise supported algorithm identifiers and agree on the highest mutually supported one, the same idea as TLS cipher-suite negotiation. For data at rest with no live negotiation partner, "negotiation" collapses to: the read path supports every algorithm ID that has ever been in production use, and the write path uses whatever the feature flag says today.
Rollout mechanics
- Dark launch (0%): verify decrypt-compatibility of the new algorithm end to end without it ever being the default for real writes.
- Canary (roughly 1%): a small slice of new writes use the new algorithm; watch a defined bake period.
- Ramp (roughly 10%, then 50%, then 100%): each stage gated by an automated check on decrypt-failure rate before advancing.
- Dual-write mode for the riskiest early stages: write both the old and new ciphertext temporarily, at real storage and CPU cost, purely so rollback is an instant flag flip with zero data loss risk; treat this as a bounded, temporary state with an explicit exit criterion, not a permanent posture.
Bulk re-encryption for already-stored blobs. This is a separate concern from the live write-path flag: a background batch job, throttled and idempotent, that walks existing blobs, re-encrypts them under the new algorithm, verifies the new ciphertext decrypts correctly, and only then updates the tag, keeping the old ciphertext until a verification-plus-grace-period window passes.
Metrics to watch
- Per-algorithm-tag write count and write-error rate.
- Per-algorithm-tag read/decrypt success rate and latency; a spike in decrypt failures for the new tag is the strongest early signal something is wrong.
- CPU/latency delta between old and new algorithm; if you switched specifically for CPU reasons (for example, moving to ChaCha20-Poly1305 on hardware without AES-NI), a latency regression on AES-NI-equipped hosts is a real, unexpected finding worth alerting on.
- The population mix by tag over time, to track rollout progress and know when full migration is actually complete.
Automated rollback triggers. Define a hard threshold (decrypt-failure rate crossing a fixed ceiling, or a spike in write-path errors) that automatically flips the feature flag back to the old algorithm for new writes. Rollback of already-written new-algorithm ciphertexts is unnecessary, since the read path already branches on the tag; this is precisely why tagging is what makes rollback safe and instantaneous, versus a scheme with no version tag, where rollback would mean reprocessing everything already written.
Worked example. A simple, fully derived rollout table at a sustained 10,000 writes/sec:
Stage 2 (10% of new writes on the new algorithm):
10,000 writes/sec x 0.10 = 1,000 writes/sec exercising the new path
That 1,000 writes/sec is your real canary blast radius at that stage, directly computable from the stated rate and the stage's rollout percentage, which is exactly the number you would watch against your decrypt-failure-rate threshold before advancing.
Trade-offs and pitfalls. Dual-write doubles storage and CPU cost during the riskiest stages; budget for it explicitly and do not leave it on indefinitely. A common wrong turn is putting the version tag in an external index or database column instead of binding it to the ciphertext bytes themselves; if the index and the blob are ever separated (a mismatched backup restore, replication lag), you lose the ability to decrypt correctly even though nothing about the ciphertext itself changed. Self-describing ciphertext avoids this entire class of bug.
Explain the 'quantum threat timeline' and its practical implications for long-term confidentiality. Describe the 'harvest-now, decrypt-later' threat model, give a reasonable range for when a large-scale quantum computer could threaten RSA/ECC, and explain how that timeline should influence which assets get prioritized for migration and what cryptoperiods you'd set.
Sample Answer
Direct answer
"Harvest now, decrypt later" means an adversary records today's encrypted traffic or backups now, while they cannot yet break the encryption, and simply waits until a sufficiently powerful quantum computer exists to decrypt it retroactively. This matters today, not only in the future, for any data whose confidentiality needs to outlive the time until that computer plausibly arrives.
Why quantum computing threatens RSA/ECC specifically
Shor's algorithm gives a quantum computer an efficient, polynomial-time way to solve the integer-factorization and discrete-logarithm problems that RSA and elliptic-curve cryptography (ECC) rely on, which no known classical algorithm can do efficiently at today's key sizes. Symmetric algorithms like AES are affected far less severely: Grover's algorithm gives only a quadratic speedup against brute-force key search, modeled as roughly halving effective security in bits:
beff=2b
So AES-256 degrades to roughly 128 bits of post-quantum security, still considered strong, while AES-128 would degrade to roughly 64 bits, no longer adequate. That is the real reason guidance recommends AES-256 rather than AES-128 going forward, independent of any other consideration.
A reasonable range for the threat
There is genuine, wide expert disagreement here, and any answer claiming a precise year should be treated skeptically. Most expert assessments put a non-trivial probability of a cryptographically relevant quantum computer, one actually capable of breaking RSA-2048 or equivalent ECC in practice, emerging sometime in the 2030s, with a long uncertainty tail extending further out and a much smaller chance of an earlier surprise. That wide, genuinely uncertain range is itself why standards bodies are not waiting for certainty: the U.S. National Institute of Standards and Technology (NIST) finalized its first post-quantum algorithm standards in August 2024 (FIPS 203, 204, and 205), and the National Security Agency's CNSA 2.0 timeline requires U.S. National Security Systems to support quantum-resistant algorithms starting in 2025, move most software/firmware signing and networking equipment to exclusive use by 2030, and complete the transition across systems by 2033 to 2035.
How the timeline drives prioritization and cryptoperiods
The decision rule is not "when will the quantum computer arrive" alone, it is "does this asset's required confidentiality lifetime extend past that point." Define a cryptoperiod, the length of time a key or the data it protects needs to remain confidential or trusted, for each asset class, and compare it against the harvest-now-decrypt-later risk window:
- Data needing confidentiality for only a few years is lower priority: by the time a quantum computer capable of breaking today's asymmetric keys plausibly exists, this data would have aged out of sensitivity anyway.
- Data needing confidentiality for decades (health records, long-lived intellectual property, anything with a multi-decade retention requirement) is high priority right now, precisely because an adversary recording it today and waiting is a rational, low-cost attack against exactly this category.
- Long-lived signing keys and trust anchors (root certificate authorities, code-signing keys with long validity, firmware-update signing) are also high priority even though harvest-now-decrypt-later does not directly apply to signatures, because a future quantum computer could let an attacker forge new signatures under an old, still-trusted public key, a forward-looking integrity risk rather than a retrospective confidentiality one.
Trade-offs and pitfalls
Do not treat post-quantum migration as a single monolithic deadline; cryptoperiod-driven prioritization means some systems migrate this year and some genuinely can wait, and treating everything as equally urgent burns credibility and budget on the wrong things. Symmetric-only systems using AES-256 are not the urgent part of this migration; asymmetric key exchange and long-lived signatures are.
Design a benchmark suite to measure symmetric and asymmetric cryptographic operation performance for a latency-sensitive service: throughput, latency percentiles (p50/p95/p99), CPU cycles per operation, and memory/cache behavior. What warmup considerations, power-saving-feature controls, and JIT-runtime effects do you need to account for to get statistically meaningful numbers?
Sample Answer
Direct answer: Design the harness to isolate the cryptographic primitive from everything else that adds noise: warm it up before measuring, pin the CPU's clock behavior so it does not ramp mid-run, and report full latency distributions (p50/p95/p99) rather than a single average, since cryptographic operation cost has a long tail driven by cache misses, garbage collection, and thermal effects.
What to collect
- Throughput: operations per second under sustained load, not a single best-case call.
- Latency distribution: per-operation wall-clock time, reported at p50, p95, and p99, since the tail is where real production pain shows up (a slow p99 handshake looks fine on average and terrible to the unlucky 1% of users).
- CPU cycles per operation: measured with hardware counters (
perf staton Linux,RDTSC-based instrumentation, or a benchmarking library that exposes cycle counts) rather than wall-clock time alone, because cycle counts are far more reproducible across runs than millisecond timings, which drift with system load. - Memory and cache behavior: allocations per operation and cache-miss rate (L1/L2/L3), since a cryptographic library that looks fast in isolation can still thrash cache under concurrent load.
Warmup. Run a discarded warmup phase before the measured window so the CPU reaches steady clock speed (turbo boost needs time to ramp), a managed runtime's JIT compiler (JVM, V8, .NET) has time to promote the hot path to its optimized tier, and the working set is resident in cache. Skipping warmup skews your p99/p999 upward in a way that has nothing to do with the algorithm itself.
Power-saving-feature controls. Disable CPU frequency scaling (pin the Linux performance governor, disable Intel SpeedStep or AMD Cool'n'Quiet) so throughput reflects a steady clock rate instead of one still ramping partway through the benchmark, and watch for thermal throttling on longer sustained runs, which will quietly degrade your later samples relative to your earlier ones.
JIT-runtime effects. Managed runtimes recompile hot methods after a call-count threshold, so a naive loop measures a mix of interpreted and optimized code. Use a benchmarking framework built for this (JMH on the JVM, BenchmarkDotNet on .NET) that separates warmup from measurement and forks a fresh process per configuration, so one benchmarked variant cannot pollute the JIT state of the next.
Worked example. The methodology that matters most is computing percentiles correctly and understanding what a long tail looks like; here is a small, fully reproducible illustration using a synthetic (not real crypto) per-operation cost distribution, seeded so it reproduces exactly:
import random
import statistics
def percentiles(samples, ps=(50, 95, 99)):
ordered = sorted(samples)
n = len(ordered)
out = {}
for p in ps:
idx = max(0, min(n - 1, -(-p * n // 100) - 1)) # ceil(p/100 * n) - 1
out[f"p{p}"] = ordered[idx]
return out
random.seed(42)
base = [random.gauss(1000, 50) for _ in range(950)] # the tight cluster
tail = [random.gauss(4000, 300) for _ in range(50)] # the long tail (GC/cache/throttle)
samples = base + tail
result = percentiles(samples)
print("n =", len(samples))
print("mean =", round(statistics.mean(samples), 1))
print("p50/p95/p99 (synthetic ns) =", {k: round(v, 1) for k, v in result.items()})
Output:
n = 1000
mean = 1149.9
p50/p95/p99 (synthetic ns) = {'p50': 1001.7, 'p95': 1187.6, 'p99': 4313.0}
Notice p50 and p95 sit close together while p99 jumps roughly 4x higher: that gap is exactly the tail a real benchmark harness must capture, and it is invisible if you only report a mean or a p50. In production you would feed this same percentiles function real per-operation timings collected with time.perf_counter_ns() inside the measured window, after warmup.
Trade-offs and pitfalls. Cross-language or cross-library comparisons are often apples to oranges: different underlying crypto libraries (OpenSSL versus BoringSSL) and different AES-NI availability across VM or container flavors can dominate the result more than the algorithm choice you meant to test. In cloud or containerized environments, check for CPU steal time (a "noisy neighbor" stealing cycles from your VM); a benchmark that ignores it will blame the algorithm for a scheduling problem. Finally, watch for dead-code elimination: constant-time cryptographic code deliberately avoids data-dependent branching, and a naive microbenchmark can let the compiler or JIT optimize away a result it decides is never used, silently measuring nothing. Consume the result (print it, sum it into an accumulator) so it cannot be eliminated.
Design a scalable PKI and key-management solution for 10 million IoT devices, many with intermittent connectivity and limited TPM/HSM capability. Address secure provisioning, key storage choices, rotation, revocation strategies where CRL/OCSP don't fit well, OTA updates, and how you'd bootstrap a root of trust.
Sample Answer
Direct answer
At 10 million devices with intermittent connectivity, favor short-lived certificates that are
renewed opportunistically on next contact over classic revocation checking (Certificate Revocation
Lists, CRLs, or the Online Certificate Status Protocol, OCSP), because a device that cannot reliably
reach a live revocation server cannot be trusted to check one anyway. Bootstrap trust from a small,
offline root Certificate Authority (CA) that only ever signs intermediate CAs, and generate each
device's identity keypair inside its Trusted Platform Module (TPM) or secure element when one is
available, falling back to a documented, weaker posture when it is not.
Structured elaboration
- Secure provisioning. At manufacturing time, generate the device's identity keypair inside its
secure element or TPM if it has one, so the private key never exists outside hardware. For cheaper
devices with no secure hardware, inject a per-device secret at a controlled factory line using a
Hardware Security Module (HSM), and treat that factory HSM itself as a critical root-of-trust asset. - Key storage choices. Prefer Elliptic Curve Cryptography (ECC, for example the P-256 curve) over
RSA for constrained devices: smaller keys and faster operations mean less flash storage and battery
drain. Where there is no secure element, the private key is encrypted at rest using a device-unique
wrapping key derived from the factory-provisioned secret, an explicit compromise, not equivalent to
hardware-backed storage, and worth stating plainly to reviewers rather than glossing over. - Bootstrapping the root of trust. Keep the root CA offline (air-gapped, used only in occasional,
audited signing ceremonies) and have it sign intermediate CAs, which are what actually issue device
certificates day to day. A compromised intermediate can then be revoked and replaced without
invalidating every device's trust in the root itself. - Revocation where CRL/OCSP do not fit. A CRL that lists revoked certificates for 10 million
devices becomes too large for constrained devices to fetch or parse, and OCSP needs an always
reachable responder many IoT deployments cannot guarantee. Short-lived certificates sidestep this:
if a device is compromised, you simply refuse to renew its certificate on its next check-in, and it
naturally stops being trusted once its current certificate expires. For urgent revocation before
natural expiry, push a small, compact deny-list (a Bloom filter or delta CRL) opportunistically to
gateways and provisioning servers via over-the-air (OTA) updates, rather than requiring every device
to check it directly. - OTA firmware updates. Sign firmware images with a separate code-signing key, distinct from each
device's identity key, and verify the signature plus a rollback-protection counter before flashing.
Rotate the signing key on a much slower cadence than device identity certificates, using a small
hierarchy of trusted signing keys so devices are not locked to a single one forever. - Handling long offline periods. Allow a short grace window where a recently expired certificate
is still accepted for the sole purpose of renewal, so a device left unplugged in a warehouse for a
few weeks does not get permanently bricked the moment it reconnects.
Worked example
Suppose devices check in roughly every 7 days on average, and you issue 30-day certificates with
automatic renewal starting at day 20. A device that misses up to two consecutive weekly check-ins (14
days late) is still comfortably inside its 30-day validity window and renews cleanly the next time it
connects. A device offline for 45 days has exceeded that window and needs the grace-window or
re-bootstrap path instead. This is a direct consequence of the numbers you choose (30-day validity,
20-day renewal start, roughly 7-day check-in cadence), and it is exactly the kind of arithmetic worth
running explicitly before committing to certificate lifetimes at this scale.
Trade-offs & pitfalls
- Reaching for a classic CRL at this device count is a common wrong first instinct; the list itself
becomes the bottleneck long before any cryptographic weakness would. - OCSP assumes reliable outbound connectivity that many locked-down IoT network policies deliberately
restrict, so building a revocation architecture around it for this fleet often fails in the field
even if it works in a lab. - Keeping the root CA offline and having it sign only intermediates is what lets you recover from a
compromised intermediate without a full 10-million-device re-provisioning event; skipping that
layer is a design mistake that only becomes visible during an actual incident.
Design a scheme using HKDF to derive multiple independent keys (an encryption key, a MAC key, an IV/nonce seed, and a key-encryption key) from a single per-tenant master secret in a multi-tenant SaaS environment. Specify how you use extract and expand, what goes into your salt and info/context strings for domain separation, and how you handle per-tenant rotation and forward/backward compatibility. Then analyze what an attacker who compromises one derived key can and cannot recover about the master secret or the other derived keys.
Sample Answer
Direct answer
Run HKDF-Extract once per tenant on the master secret with a per-tenant random salt to compress it
into a uniform pseudorandom key (PRK), then call HKDF-Expand several times against that PRK, once
per key you need, each with a distinct "info" context string that encodes the purpose. Because
HKDF-Expand's outputs are only as related to each other as the underlying pseudorandom function
(PRF) allows, an attacker who recovers one derived key learns nothing about the PRK, the master
secret, or any sibling key derived under a different info string.
Structured elaboration
- HKDF-Extract. Computes
PRK = HMAC-Hash(salt, IKM), where IKM (input keying material) is the
per-tenant master secret. The salt does not need to be secret, its job is to make the PRK for two
tenants provably distinct even in the pathological case where two tenants somehow ended up with
the same master secret, and to strengthen extraction if the master secret is not perfectly uniform. - HKDF-Expand. Produces output keying material (OKM) as a chain:
with T(0) empty, and OKM equal to T(1) || T(2) || ... truncated to the number of bytes you
need. In plain terms: each output block depends on the PRK, the previous block, a fixed "info"
label, and a counter byte, so changing the info label produces a completely different, unrelated
chain of outputs from the same PRK.
- Domain separation via info strings. For four purposes you would call HKDF-Expand four times
with four distinct labels, for example"tenant:{id}|purpose:enc-key|v1",
"tenant:{id}|purpose:mac-key|v1","tenant:{id}|purpose:iv-seed|v1", and
"tenant:{id}|purpose:kek|v1". Reusing the same info string for two different purposes silently
produces identical subkeys, which quietly defeats the whole point of deriving separate keys. - Rotation. You can rotate at two different levels: rotating the master secret itself forces
every derived key to change (a full re-derive, with a real migration cost since old data encrypted
under the old keys must still be readable), or rotating only the version tag inside the info string
(bumpv1tov2) while keeping the master secret, which is cheaper and protects against a
compromise of a specific derived key, but does nothing if the master secret itself is what leaked. - Forward and backward compatibility. Tag ciphertext with the version used to derive its key
(storev1/v2alongside the data), so a reader derives the matching version's key directly
instead of guessing, and old data stays decryptable as long as the old master secret (or its
derivation chain) is retained until that data is re-encrypted or expires.
Worked example: security analysis of a compromised derived key
Say an attacker fully compromises only the MAC key (one of the four derived keys). Can they recover
the PRK or the master secret? No: doing so would require inverting HMAC, which under the standard
assumption that HMAC behaves as a secure PRF is computationally infeasible, an HMAC output does not
algebraically reveal its key the way, for example, reusing a one-time-pad key would. Can they derive
a sibling key, such as the encryption key, from the leaked MAC key? Also no, because the encryption
key's chain depends on the PRK directly plus its own distinct info label, not on the MAC key's
output; there is no shared exponent or intermediate value linking the two chains the way there would
be if you had, say, XORed a single value into two different keys. The one scenario where the
analysis changes is if the compromise is broad enough to expose the PRK itself, for instance a
process-memory dump captured right after extraction, in which case every derived key becomes
recoverable, because the PRK, not any individual derived key, is the actual secret binding the whole
tree together. That is the real security boundary to protect operationally: PRK exposure, not any
single leaf key's exposure.
Trade-offs & pitfalls
- HKDF is not a password-hashing function. If the input secret is a low-entropy human password
rather than an already-random, KMS-generated master secret, run it through a memory-hard password
key derivation function such as Argon2id first, then use HKDF only to fan the resulting high-entropy
secret out into purpose-specific subkeys. - Treating the salt as something that must be kept secret leads to awkward, unnecessary key-management
overhead; it only needs to be unique, not confidential. - If a system uses a naive construction like plain concatenation-then-hash instead of HKDF's
HMAC-based chain, it can be vulnerable to length-extension-style issues that HKDF's HMAC
construction is specifically designed to avoid, so do not "roll your own" HKDF-like scheme even
though the construction looks simple.
Unlock Full Question Bank
Get access to all Applied Cryptography and Key Management interview questions and detailed answers.
Sign in to ContinueJoin thousands of developers preparing for their dream job.