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”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. Modern parameter sets use bits. The Logjam analysis put the 1024-bit primes that protected legacy TLS at the edge of nation-state-feasible per-prime precomputation (Adrian et al., 2015), so 2048 bits is the conservative floor used today.
p, g = 2063, 5print(pow(5, 1031, p))# ==> 2062Exercise 2
Section titled “Exercise 2”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 oracle refuses the challenge ciphertext, 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 3
Section titled “Exercise 3”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 2 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 4
Section titled “Exercise 4”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 original FO theorem 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.