Appendix D: Solutions for Chapter 5
This page collects solutions and editorial notes for the exercises in Chapter 5: KEMs vs key agreement vs public-key encryption. Compute and derivation exercises have worked solutions; open-ended exercises have an editorial note describing what a strong answer addresses.
Every block below is self-contained and runs on the standard library alone. The fuller versions of these routines, with the mauling attack and the multiplicative-order computation written out, are in the kem_primitives package under solutions/ch05-kem-primitives. From a clone of the companion repository, pytest tests/ch05 runs its suite. Appendix C has the setup.
Exercise 1
Section titled “Exercise 1”Compute and guess “real” exactly when it equals . The encapsulated key always matches, and a fresh random key matches with probability about , so the guess is almost always right without a single decapsulation query. Correctness constrains what the honest parties compute and says nothing about what an attacker can learn: the toy decapsulates every ciphertext correctly and still loses the KEM game outright. Real RSA-KEM outputs rather than (Housley & Turner, 2025), so testing a candidate key needs , which needs inverting RSA.
A weak answer reaches for the mauling attack, which uses a query, or blames the 64-bit modulus.
Exercise 2
Section titled “Exercise 2”(a) Any receiver that processes ciphertexts an attacker supplies and reveals something computed from the result. A padding or format error is one, as in Bleichenbacher’s attack. A reply encrypted under the key the receiver derived is another. (b) Answering would let the attacker beat every KEM, by comparing the answer with . (c) A real receiver cannot tell which ciphertexts an attacker derived from which, so the definition must not either, and that is exactly the freedom the ElGamal attack uses.
A weak answer treats the oracle as the attacker holding the private key, or says is excluded “because it is secret”.
Exercise 3
Section titled “Exercise 3”(a) Encrypt under the public key and compare the result with . Textbook RSA is deterministic, so the two are equal exactly when . Against ElGamal the comparison fails even when , because each encryption draws a fresh . (b) Submit , which is a different ciphertext from , and divide the answer by 4 to recover , which names . (c) IND-CPA gives the attacker less, no decryption access, so more schemes pass it. The ElGamal attack uses algebra rather than computation, so it works at every key size, and a longer key cannot stop it.
A weak answer says weaker means fewer bits of security.
Exercise 4
Section titled “Exercise 4”With the changed fallback the three lines are True, then False, then False True True. The honest round trip and the re-encryption comparison never reach the fallback, so the first two lines are unchanged. The third changes: two decapsulations of the same rejected ciphertext now return different values, which a successful decapsulation never does.
(a) With fresh randomness Bob cannot recompute the ciphertext and has nothing to compare against. With the ciphertext is a function of and the public key, so Bob can recompute it from the he decrypts and compare. A ciphertext passes exactly when it is the encryption of its own decryption. That tests consistency, not whether the sender knew the plaintext, so a modified ciphertext can still pass by luck.
(b) The third line is half the answer. Fresh random bytes let the attacker recognize a rejection by querying twice and comparing, since a success always returns the same key. A fixed dummy key is predictable, so the attacker could recognize a rejection, and two mauled ciphertexts would share it. is unpredictable without and stable for each ciphertext, so it looks like a key.
A weak answer says the fallback is random and therefore safe, or that implicit rejection is what makes the KEM CCA-secure.
Exercise 5
Section titled “Exercise 5”The ciphertext length is bytes.
(a) The 1,087-byte input fails the ciphertext check, so ML-KEM.Decaps shall not run. The caller sees whatever error the implementation uses for a failed input check, since FIPS 203 does not prescribe one. The random 1,088-byte string passes the ciphertext check, so with a checked decapsulation key, decapsulation runs and returns 32 bytes with no error. Almost certainly they are the fallback . A ciphertext passes re-encryption only if it is the encryption of its own decryption, and at most of the strings of that length are, one for each message. If the string did pass, the caller would receive the success-path key.
(b) Anyone with the encapsulation key can make a ciphertext that decapsulates, so success authenticates nobody, and authentication needs a signature or an authenticated protocol. CCA security comes from re-encryption with derived randomness plus hashing to the key. Explicit-rejection variants also have proofs, and implicit rejection removes the rejection signal from the interface.
(c) , and are SHA3-512, SHA3-256 and SHAKE256 (National Institute of Standards and Technology, 2024, sec. 4.1), fixed public functions rather than random oracles, and a quantum attacker can query them in superposition, which is why post-quantum arguments use the QROM.
A weak answer says the random string “returns an error” or “returns ”.
Exercise 6
Section titled “Exercise 6”The exchange round-trips because regardless of how and are sampled. The auxiliary computation returns 2062, which is . The order of 5 in must divide , so it is 1, 2, 1031, or 2062. The first three are ruled out: , , and . So the order is 2062 and is a primitive root, generating the full multiplicative group rather than a strict subgroup. Real DH groups still prefer prime-order subgroups of carefully chosen safe primes for unrelated reasons (subgroup-confinement attacks).
Classical hardness of the discrete log on is set against the general number field sieve, which runs in sub-exponential time. In 2015 the Logjam analysis estimated the number field sieve’s precomputation against a single 1024-bit prime at about 45 million core-years, “not necessarily out of reach for a nation state” (Adrian et al., 2015, sec. 4.1). It recommended that servers move to groups of 2048 bits or more (Adrian et al., 2015, sec. 5). NIST’s key-management guidance rates finite-field Diffie-Hellman with a 2048-bit prime and a 224-bit private key at 112 bits of security, and with a 1024-bit prime and a 160-bit private key at 80 bits or less (Barker, 2020, sec. 5.6.1.1). It no longer approves anything below 112 bits for protecting federal government information (Barker, 2020, sec. 5.6.1.1). This answer takes 2048 bits as its working minimum.
p, g = 2063, 5print(pow(5, 1031, p))# ==> 2062Exercise 7
Section titled “Exercise 7”The mauling works because RSA encryption is multiplicative: , so the receiver decrypts to instead of . With the attacker recovers exactly. Since was chosen invertible, multiplying the oracle’s answer by returns itself, which is the recovery step the exercise asks for and the one test_mauling_recovers_the_encapsulated_key pins. One decapsulation query on a ciphertext other than the challenge therefore hands over the challenge key, which is exactly the IND-CCA capability the FO transform must defend against.
The transform recomputes the ciphertext during decap by re-encrypting the recovered plaintext under encryption coins derived from a random oracle of that plaintext, then compares the re-encryption byte-for-byte to the received ciphertext. Over a randomized public-key encryption scheme each message has exactly one ciphertext under its derived coins. So a obtained by applying an algebraic relation to the challenge is essentially never the re-encryption of whatever it decrypts to, and the check fails. Decap then returns the implicit-rejection fallback , which depends on the private seed and on the ciphertext the attacker submitted, not on the challenge key.
Why “any invertible ” is not good enough. The CCA game excludes the challenge ciphertext from the oracle, so the attack needs as well as an invertible , and invertibility does not imply it. Take , so , and choose by the Chinese remainder theorem with and . That is coprime to (it is mod and mod , so neither prime divides it) and is not . Yet : modulo both sides are , and modulo multiplication by changes nothing. For the chapter’s modulus that is , and the query it produces is forbidden.
escapes this for every , and not merely on this modulus: it holds for every valid two-prime RSA key. The collision means . First note , since would force and , hence , which excludes. So some prime does not divide , and the collision would require .
That is impossible, which is the property of the exercise asks for. Because exists, is invertible modulo , so . If then divides , and it divides by Fermat, so it divides . Then , forcing , which no prime does. So is collision-free under any valid two-prime RSA key, and no per-modulus check is needed.
In the PKE game rather than the KEM game one further restriction is needed, because there the adversary chooses the challenge messages. If it chose then and for every , so the attack must pick two distinct non-zero challenge representatives, preferably coprime to , before taking . Encapsulation cannot hand the KEM adversary that problem, since it samples from .
The transform is not claiming that every ciphertext other than the challenge fails the check, and a strong answer avoids saying so. An attacker can always run encapsulation honestly and submit a well-formed , which passes by construction and yields its own key . What the transform buys is that an accepted ciphertext produces a key derived from its own plaintext, so nothing the oracle returns is algebraically tied to the challenge key.
The toy RSA-KEM is a weaker starting point than that, and the difference is worth stating. Textbook RSA has no encryption coins to derandomize, so the mauled is a legitimate encryption of and would survive a re-encryption check. What defeats the attack in an FO-style KEM is that the shared secret is a hash of the recovered message rather than the message itself: the attacker obtains a hash of , which reveals nothing about the hash of .
Exercise 8
Section titled “Exercise 8”Correctness, for every and not only the coprime ones. samples uniformly from with no coprimality test, and outputs . computes . The RSA setup chooses with , so for some non-negative integer .
Work modulo each prime separately. If , Fermat’s little theorem gives , so
If , then both and are , so the congruence holds trivially. Either way , and the identical argument gives . Since and are distinct primes, the Chinese remainder theorem lifts the two congruences to . Both and the value returns lie in , so they are equal rather than merely congruent, and for every that can produce.
Euler’s theorem from Chapter 4 proves only the case , because needs invertible. The per-prime argument above is what closes the remaining residues, and it is why the chapter can call the toy KEM’s correctness exact rather than probabilistic.
The non-coprime fraction. fails to be coprime to exactly when or . Let and . Then (the multiples of in are ), and similarly . The intersection is the set of multiples of in , which is empty. By inclusion-exclusion,
The exact probability is therefore , and it is strictly below . Cross-multiplying reduces that inequality to , which holds for any primes since .
For the chapter snippet’s two 32-bit primes the exact fraction is about , below , which is the bound the chapter quotes.
What this fraction does not govern. Not correctness, which the first half showed is exact on the whole range. And not the mauling attack of Exercise 7 either, which is the tempting misreading. That attack needs its blinding factor to be invertible, not the encapsulated , and is chosen by the attacker rather than sampled: for an RSA modulus, which is odd, is invertible with no test needed. Had been drawn uniformly the same count would give its rejection rate, which is the only sense in which the two quantities are related.
Real RSA-KEM samples a random integer much as this toy does. The difference is what it does with it, and it is not OAEP. RSA-KEM encrypts a random with raw RSA and derives the shared secret as rather than using as the key, so no padding is involved and every is admissible (Housley & Turner, 2025). RSAES-OAEP is a separate padded construction for encrypting a chosen message (Moriarty et al., 2016). The two are often conflated and should not be.
def coprime_failure_bound(p, q): n = p * q return (p + q - 2) / (n - 1)
for p, q in [(53, 61), (1009, 1013), (3184935163, 3199286161)]: print(f"p={p:>14} q={q:>14} fraction={coprime_failure_bound(p, q):.3e}")# ==> p= 53 q= 61 fraction=3.465e-02# ==> p= 1009 q= 1013 fraction=1.976e-03# ==> p= 3184935163 q= 3199286161 fraction=6.265e-10The third row is the chapter snippet’s own modulus. The function returns the exact fraction, not the upper bound.
Exercise 9
Section titled “Exercise 9”A reference statement to compare against your own version:
- Inputs. An IND-CPA-secure public-key encryption scheme that is -correct, a key-derivation random oracle , and a coin-derivation random oracle .
- Output. A KEM that is IND-CCA2-secure in the random oracle model. runs and also samples a uniform private seed , from the message space itself, which the decapsulation key carries. Naming the domain matters: the bound’s guessing term is written over , so an answer that samples from an independent -bit space owes a separate term for seed guessing. The query count is part of it, since the adversary gets one attempt per hash query. samples a plaintext , computes the ciphertext with coins derived from , and sets the shared key to . runs to get , re-encrypts under , and returns on a byte-for-byte match. It takes the fallback branch on a mismatch and when itself returns . A statement that covers only the mismatch is incomplete.
- Role of the private seed . On a failed re-encryption check, returns rather than a distinguished symbol. This is what makes the rejection implicit: the caller cannot tell a rejection from a success by inspecting the output. Because is secret and per-key, the adversary cannot compute the fallback itself; because the fallback depends on , two different mauled ciphertexts get independently distributed fallback values, colliding only with negligible probability. Both properties are load-bearing, and neither holds for a fixed dummy key.
- Security game. IND-CCA2 for KEMs: the challenger returns where is the real encapsulated key and is uniform, and an adversary with a decapsulation oracle on every ciphertext except cannot guess with non-negligible advantage.
- Assumption. IND-CPA security of , -correctness in the worst case over messages, a message space large enough that is negligible, and the random oracle model for and . The bound is with (Hofheinz et al., 2017).
- Tightness. This IND-CPA-based reduction is tight: the advantage term carries only a constant factor of 3, with no factor of multiplying it. Calling the FO transform non-tight refers to the route from a one-way assumption instead, and to the QROM reductions. An answer that labels this reduction non-tight has the tightness picture backwards.
A complete answer also distinguishes the two rejection styles by name, versus . It should state the QROM caveat too: the HHK theorem above is in the classical ROM, and QROM tightness for ML-KEM’s variant is the subject of subsequent work (Chapter 11 returns to this).
One further point separates the teaching theorem from the deployed scheme. FIPS 203’s ML-KEM does not derive its success-path key as : it computes , omitting the ciphertext, and uses for the implicit-rejection fallback. An answer that states the abstract theorem correctly and flags that ML-KEM deviates from it is stronger than one that conflates the two.