Cryptography Fundamentals Questions
Core concepts and vocabulary of cryptography: confidentiality, integrity, authentication, and non-repudiation; the difference between symmetric and asymmetric primitives; and how standard algorithms, libraries, and protocols fit together. Covers threat models, common standards, and applying primitives and cryptographic libraries correctly to real-world security problems. The entry point for the cryptography track.
Compare AES and DES/3DES: key and block sizes, why DES is considered insecure today, and what you'd need to consider when migrating a system that still has legacy DES-encrypted data.
Sample Answer
Direct answer
AES (Advanced Encryption Standard) uses a 128-bit block with 128, 192, or 256-bit keys and is
the current standard. DES (Data Encryption Standard) uses a 64-bit block with an effective
56-bit key, small enough to brute-force with commodity hardware today, which is why it is
considered broken. 3DES applies DES three times to stretch the effective key strength but
keeps DES's small 64-bit block, which is itself now a weakness.
Structured elaboration
- Key and block sizes: AES: 128-bit block, 128/192/256-bit key. DES: 64-bit block,
56-bit effective key (the stored key is 64 bits but 8 are parity bits). 3DES: same 64-bit
block, keying options up to an effective ~112-bit security level (not the naive 168 bits
three 56-bit keys would suggest, because of a known meet-in-the-middle attack against
simple triple encryption). - Why DES is insecure: a 56-bit keyspace is small enough that a dedicated brute-force
machine (the EFF's "Deep Crack" demonstrated this publicly in 1998) can exhaust it; modern
hardware makes this dramatically cheaper and faster. Separately, 3DES's 64-bit block is
small enough that encrypting large volumes of data under one key risks a birthday-bound
collision (the "Sweet32" attack), which is a practical concern even where the key itself is
not brute-forced. - Migrating legacy DES-encrypted data: you cannot just start writing new data with AES
and leave old DES ciphertext sitting there, since that ciphertext is only as strong as
DES's already-weak key. Decrypt each record with the legacy DES key inside a controlled,
audited process, re-encrypt it with AES-256-GCM under a freshly generated key, verify the
round trip before deleting the old ciphertext, and then securely destroy (zero out) the old
DES key material so it cannot be used to decrypt any remaining copies or backups.
Worked example
A payments system storing card-adjacent data under 3DES for a legacy compliance reason
migrates by: generating one new AES-256 key per data-encryption-key tier (following the same
envelope-encryption pattern used for any modern secret), running a batch job that reads each
3DES record, decrypts it in memory, re-encrypts with AES-256-GCM and a fresh nonce, writes
the new ciphertext, and only after a verified re-read does it mark the row migrated and
schedule the old key for destruction. Doing this as an in-place batch (rather than a
big-bang cutover) lets you roll back a partial migration if something goes wrong mid-run.
Trade-offs & pitfalls
- Backups and archives are the most commonly forgotten copies of DES-encrypted data during a
migration; the old key must be destroyed everywhere the ciphertext exists, or migrating the
live database accomplishes nothing. - 3DES is noticeably slower than AES (it runs the DES algorithm three times), which is
itself a good practical reason, beyond security, to retire it.
Describe how you would perform a cryptographic library audit to find misuse (e.g., incorrect mode selection, improper padding, insecure defaults). What static and dynamic analysis tools or test vectors would you use, and how would you prioritize remediation across findings that vary in exploitability?
Sample Answer
Direct answer
Treat a cryptographic library-misuse audit as two separate lanes that feed each other: static analysis to find suspicious call sites at scale, and dynamic, test-vector-driven analysis to prove which of those sites are actually exploitable. Then triage findings by exploitability and blast radius, not by file order or how alarming a finding looks in isolation.
What "misuse" looks like
- Wrong mode selection: ECB mode used for anything beyond a single block. ECB encrypts identical plaintext blocks to identical ciphertext blocks, so repeated structure in the plaintext (a bitmap, a repeated field) leaks straight through the ciphertext.
- Improper padding: a custom padding-validation routine whose error path (message, timing, or response code) differs depending on whether the padding was well-formed, the classic padding-oracle shape that lets an attacker decrypt ciphertext byte by byte through repeated queries.
- Insecure defaults: a library or wrapper that ships a default IV of all zeros, a default weak cipher, or a key size that's fine for a demo but not for production, and a caller who never overrides the default inherits the weakness silently.
Static analysis
Pattern- and AST-based scanners (for example Semgrep with a crypto-focused ruleset, or a language's own security linter such as Bandit for Python) can flag known-bad call shapes directly: Cipher(..., modes.ECB()), a broken digest like MD5 used outside a non-security checksum context, or a byte-array key or IV that's a literal constant in source. Secret-scanning tools (gitleaks, truffleHog) catch hardcoded keys specifically. General static analysis (CodeQL and similar) adds language-wide security query packs beyond crypto-specific ones.
Dynamic and test-vector analysis
Run the code path that actually performs encryption/decryption against known-answer test vectors, Google's Project Wycheproof is built exactly for this: it contains adversarial test vectors (weak IVs, invalid curve points, edge-case moduli) specifically designed to catch the misuse classes above rather than just confirming a library works on "normal" input. Pair that with differential testing against a reference implementation on the same inputs, and fuzz the parsing and validation code paths that handle ciphertext or padding, since those are exactly where a padding-oracle-shaped bug tends to live.
Prioritizing remediation
Score every finding on two axes: exploitability (is the vulnerable path reachable by an unauthenticated network attacker, or only by someone who already has local code execution) and impact (does it break confidentiality of data at rest, or fail loudly and safely). Fix network-reachable, confidentiality-breaking findings first regardless of how small or old the code looks; track lower-exploitability findings on a remediation backlog with a deadline weighted by that same exploitability score, rather than by severity label alone.
Worked example
The ECB pattern leak, demonstrated directly:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
import os
key = os.urandom(32)
block = b"AAAAAAAAAAAAAAAA" # one AES block (16 bytes), repeated
plaintext = block * 4
encryptor = Cipher(algorithms.AES(key), modes.ECB()).encryptor()
ciphertext = encryptor.update(plaintext) + encryptor.finalize()
blocks = [ciphertext[i:i + 16] for i in range(0, len(ciphertext), 16)]
print("all four ciphertext blocks identical:", blocks[0] == blocks[1] == blocks[2] == blocks[3])
Output:
all four ciphertext blocks identical: True
Four identical plaintext blocks produce four identical ciphertext blocks under ECB, exactly the structural leak that static-analysis rules are built to flag before the code ever ships.
Trade-offs and pitfalls
Static analysis alone produces false positives (a "bad" call inside a well-reviewed wrapper that's actually fine) and false negatives (a config-gated insecure default that's only reached through a rarely-used flag), so treat its hits as leads, not verdicts. Test-vector and fuzzing analysis alone will miss anything the vectors and fuzz corpus don't exercise. And both together still miss pure logic-level misuse, the right function called with the wrong parameters, such as a correct AEAD (Authenticated Encryption with Associated Data) call with a caller-supplied fixed nonce, which usually needs a manual review pass on the highest-risk call sites even after tooling runs clean.
Discuss common side-channel attacks (timing attacks, cache attacks, power analysis) relevant to cryptographic operations in cloud environments. For each, describe detection techniques, mitigation strategies for deployed libraries (constant-time implementations, blinding, hardware isolation), and pragmatically how you'd prioritize fixes in production.
Sample Answer
Direct answer
Side-channel attacks recover secrets from how a cryptographic operation runs, not from any
weakness in the algorithm's math: timing attacks watch how long an operation takes, cache attacks
watch which memory locations get touched, and power analysis watches an operation's electrical
draw. In a cloud environment the realistic threat is timing and cache attacks from a co-located,
untrusted tenant sharing the same physical hardware; prioritize fixes on whatever service actually
handles secret-dependent operations for untrusted or externally-reachable callers first.
Structured elaboration
- Timing attacks. Any code whose execution time depends on secret data, most commonly a
byte-by-byte comparison that returns as soon as it finds a mismatch, leaks information through
response latency. Detect it with statistical tests on API latency distributions per endpoint;
mitigate with constant-time comparison functions and vetted, audited cryptographic libraries
(libsodium, BoringSSL) instead of hand-rolled comparisons. - Cache attacks (named techniques: Prime+Probe, Flush+Reload). A co-located tenant can infer
which cache lines your process accessed by measuring its own access latency to those same lines,
which leaks secret-dependent memory access patterns, such as a table lookup indexed by a key
byte. Detect it through anomalous cache-miss and cross-core cache-eviction patterns visible to
hypervisor-level monitoring; mitigate with constant-time, constant-memory-access algorithm
implementations, and, for the highest-value keys, dedicated (non-shared) cores or hardware
isolation instead of relying on software mitigation alone. - Power analysis. Genuinely relevant for physical or embedded hardware and for the internals
of an Hardware Security Module (HSM); largely out of reach for a remote attacker against a
standard cloud tenant, since it requires physical or very close electrical access. For keys that
matter enough to worry about this, use an HSM with built-in blinding countermeasures rather than
trying to defend software running on general-purpose cloud compute against it.
Worked example
The mechanism behind a timing leak is concrete and doesn't require measuring wall-clock time to
demonstrate: an early-exit comparison performs a different number of operations depending on
where the first mismatch occurs, and that operation count is exactly what an attacker's latency
measurement is a noisy proxy for. A comparison that differs at the very first byte does one
comparison and stops; one that matches through byte 16 of a 32-byte tag does seventeen comparisons
before it can stop. hmac.compare_digest-style constant-time comparisons close this specific leak
by always inspecting every byte regardless of where the first mismatch is, so the operation count,
and therefore the timing signal, no longer depends on the secret at all.
Trade-offs & pitfalls
- Prioritization for production. First: inventory which endpoints handle authentication, token
signing, or key derivation for externally-reachable or multi-tenant callers, since those are the
highest-value, highest-exposure targets. Second: patch or replace vulnerable comparison and
cryptographic code on those endpoints specifically. Third: consider dedicated hardware or
isolated tenancy only for the highest-value keys, since isolation is expensive to apply broadly.
Fourth: add ongoing telemetry (latency-distribution monitoring, cache-behavior alerts where
available) so a regression is caught automatically rather than by a future audit. - Common wrong turn: treating every theoretically-possible side channel as equally urgent
regardless of attacker proximity. A power-analysis attack requiring physical hardware access is
not the same priority as a timing leak reachable over the public network; match the mitigation
effort to what your actual threat model, and specifically your tenancy model, makes reachable. - Side-channel fixes interact with performance: constant-time code and dedicated cores both cost
something, so justify the cost against what is actually exposed to an untrusted caller, not
against a hypothetical worst case.
Explain the padding oracle attack against CBC-mode encryption. Describe how an attacker can use padding error responses to decrypt ciphertext bytes and provide practical mitigations you would apply in a web service that handles encrypted cookies.
Sample Answer
Direct answer
A padding oracle attack against Cipher Block Chaining (CBC) mode lets an attacker decrypt
ciphertext without ever learning the key, by abusing a server that reveals, one bit of
information at a time, whether decrypted padding was valid. The fix is to stop that leak entirely:
authenticate the ciphertext before you ever act on decrypted padding, ideally by switching to
authenticated encryption so the question "was the padding valid" never becomes observable.
Structured elaboration
CBC decrypts block i as:
Pi=Dk(Ci)⊕Ci−1so every byte of plaintext depends on both the ciphertext block and the previous ciphertext
block, byte for byte. If an attacker can flip bits in Ci−1 and ask an oracle "did this decrypt
to validly-padded plaintext (PKCS#7: the last byte states how many padding bytes there are, and
they must all equal that value)", the oracle's yes/no answer reveals one byte of the intermediate
decryption value Dk(Ci) at a time, working backward from the last byte of the block. Once the
attacker has that intermediate value, XOR-ing it with the real previous ciphertext byte recovers
the real plaintext byte, no key required.
Worked example
Say the attacker wants byte 15 (the last byte) of a block. Let I=Dk(Ci)[15] be the true
intermediate value (unknown to the attacker) and c=Ci−1[15]=0x3C be the real ciphertext
byte actually on the wire. The real plaintext byte is p=I⊕c.
- The attacker tries every candidate byte c′∈[0,255] in place of c and resubmits the
ciphertext, watching for the server's padding-valid response. - Padding is valid for the last byte exactly when I⊕c′=0x01 (a single valid padding
byte). Suppose that happens at c′=0x58. - The attacker now knows I=c′⊕0x01=0x58⊕0x01=0x59.
- Recover the real byte: p=I⊕c=0x59⊕0x3C=0x65, which is the ASCII letter
e.
Running that exact arithmetic (verified, not asserted) reproduces it: with I=0x59 fixed as
ground truth, the search over c′ finds the valid byte at 0x58, and 0x59⊕0x3C does
equal 0x65. Repeating this per byte, and then per block, decrypts the whole message. In the
worst case this costs up to 256 oracle queries per byte and up to 256×16=4096 queries
per 16-byte block, no cryptanalysis of the cipher itself needed, only a working oracle.
Trade-offs & pitfalls
- Use authenticated encryption with associated data (AEAD), such as AES in Galois/Counter Mode
(AES-GCM) or ChaCha20-Poly1305, which authenticates the ciphertext as one atomic operation and
never exposes a separate "was the padding valid" signal. - If you are stuck with CBC, verify a Message Authentication Code (MAC) computed over the
ciphertext (encrypt-then-MAC) before attempting to unpad, and reject on MAC failure with one
uniform, generic error, before any padding logic runs at all. - Never let padding errors, MAC errors, and format errors produce distinguishable responses,
status codes, or timings. Rate-limit and log repeated malformed-ciphertext submissions, since a
real attack needs thousands of requests per byte. - Common wrong turn: fixing only the error message while leaving a timing difference between
"padding check failed fast" and "padding check passed, MAC check failed later". Timing is itself
an oracle.
Explain the differences between collision resistance, preimage resistance, and second-preimage resistance in hash functions. Provide practical attack complexity estimates for MD5, SHA-1, and SHA-256, and state at what point you would mandate deprecation of a hashing algorithm within an organization.
Sample Answer
Direct answer
A cryptographic hash function is supposed to make three different attacks infeasible, and they are genuinely different properties, not three names for the same thing: collision resistance (hard to find any two distinct inputs that hash to the same output), preimage resistance (given a hash output, hard to find any input that produces it), and second-preimage resistance (given one specific input, hard to find a different input that hashes to the same value). MD5 and SHA-1 (Secure Hash Algorithm 1) have both had their collision resistance broken in practice; SHA-256 has not, at either a theoretical or practical level, and remains the safe current default.
Telling the three properties apart
| Property | Attacker is given | Attacker must find |
|---|---|---|
| Collision resistance | nothing specific | any two inputs x1 != x2 with hash(x1) == hash(x2) |
| Preimage resistance | a hash value h | any input x with hash(x) == h |
| Second-preimage resistance | a specific input x1 | a different input x2 with hash(x2) == hash(x1) |
Collision resistance is the weakest guarantee to break, because the attacker has complete freedom to choose both inputs, which is exactly what makes it vulnerable to the birthday paradox: you don't need to search anywhere near the full output space, only until any two outputs among many attempts happen to match.
Worked example: why collisions are so much cheaper than preimages
For a hash with an n-bit output, the generic (structure-agnostic) cost to find a collision by brute force is about 2n/2 attempts, while a generic preimage or second-preimage attack costs about 2n:
2128/2=264≈1.8×1019 (MD5’s generic collision bound) 2160/2=280≈1.2×1024 (SHA-1’s generic collision bound) 2256/2=2128≈3.4×1038 (SHA-256’s generic collision bound)Real cryptanalysis has done far better than these generic bounds for the two broken algorithms:
- MD5: differential collision attacks (Wang et al., refined repeatedly since 2004) make finding a collision practical in well under a second on an ordinary laptop, vastly cheaper than the generic 264 bound. MD5's preimage resistance is comparatively less damaged; no practical preimage attack is known, but its collision break alone rules it out for anything security-relevant.
- SHA-1: a real, published collision (the "SHAttered" attack, 2017) was demonstrated at a complexity of roughly 263.1 operations, about 100,000 times cheaper than the generic 280 birthday bound for its 160-bit output, and a cheaper chosen-prefix variant followed in 2020 at a small fraction of the original attack's cost. SHA-1's preimage resistance is not known to be practically broken, but its collision break is sufficient reason to treat it as unsafe for anything that depends on collision resistance, most notably digital signatures.
- SHA-256: no attack better than the generic birthday and brute-force bounds is known against either its collision or preimage resistance; it remains the safe, current default for new systems.
When to mandate deprecation
Don't wait for a public, practical break to start migrating, cryptographers had already been flagging MD5's collision weaknesses years before 2004's practical attacks, and SHA-1's theoretical weaknesses were known well before the 2017 demonstration. A defensible organizational policy is to mandate deprecation once a hash's best known attack (not just the generic bound) drops to a complexity a well-resourced attacker could plausibly reach within your data's required confidentiality or integrity lifetime, treating "cryptographers have found a meaningfully-better-than-generic attack at all" as the trigger to start planning migration, and "a practical, demonstrated break exists" as the trigger to have already finished it.
Trade-offs and pitfalls
A common mistake is judging a hash function purely by its output length rather than by known attacks against it: SHA-1's 160-bit output sounds larger and safer than MD5's 128-bit output, but its real collision resistance today is a demonstrated practical break, not a theoretical margin. Always check the current state of published cryptanalysis for the specific algorithm and use case (collision resistance failing matters enormously for signatures, much less for some non-adversarial checksums), rather than trusting the nominal output size alone.
That is every published Cryptography Fundamentals question for Penetration Tester so far. Browse the other topics in this category, or practice this one interactively.