Skip to content

Appendix D: Solutions for Chapter 27

This page collects solutions and editorial notes for the exercises in Chapter 27: Hybrid schemes in practice. Compute and derivation exercises have worked solutions; open-ended exercises have an editorial note describing what a strong answer addresses.

The fuller versions of these routines are in the hybrid package under solutions/ch27-hybrid. From a clone of the companion repository, pytest tests/ch27 runs its suite. Appendix C has the setup.

Editorial note. The round-trip still agrees, and the reason is duller than it looks. Both parties hold the same ordered pair (ss_a, ss_b), so both evaluate the same two HKDF calls and XOR the same two outputs. The construction’s symmetry is not what makes that work. The expression is indeed symmetric under swapping the two secrets, because XOR is commutative, but Block 2’s concatenate-then-HKDF combiner is not symmetric and its round-trip agrees just as well.

On whether the argument still applies, the answer is that it does not transfer for free, and naming the specific result is the point of the exercise. Section 3 of Bindel, Brendel, Fischlin, Goncalves and Stebila 2019 states theorems for exactly three combiners: XtM (XOR-then-MAC, Section 3.1), dualPRF (Section 3.2), and the nested variant N (Section 3.3) (Bindel et al., 2019). The construction in this exercise is none of them, so no theorem in that paper covers it as written.

It is worth knowing which trap it avoids and which it does not. Section 3.1 records that the plain XOR-combiner, which XORs the two component keys, preserves IND-CPA but not IND-CCA, and Bindel et al. add that this holds even when both component KEMs are IND-CCA: given a challenge pair of ciphertexts, an adversary decapsulates each component against a fresh partner ciphertext whose key it already knows, and recovers the challenge key from the two answers. That attack needs the two keys to be separable, and here they are not, because each HKDF call takes both secrets. So this construction is not the broken one.

What it does drop is the ciphertext binding. The analysed dualPRF combiner is PRF(dPRF(k1, k2), c1 || c2), with the component ciphertexts fed into the derivation. This construction, like Block 2’s, derives from the secrets alone. Inside TLS that gap is closed elsewhere, because the transcript hash binds the ciphertexts further down the key schedule. Standalone, it is a real difference from the object the theorem is about.

The lesson: the security argument rests on the analysed construction, not on a family resemblance to it. A different shape needs a different citation or a new proof, and “it is also a KDF over both secrets” is not one.

Worked answer. The explicit-composite serialization is plain concatenation, with no length prefixes and no framing between the components, because every component signature has a fixed size known from the algorithm identifier. So the three-component size is the sum and nothing else:

# Sizes of a three-component AND-mode composite, from the component standards.
MLDSA65_SIG = 3309 # FIPS 204 Table 2
ED25519_SIG = 64 # RFC 8032
SLHDSA128S_SIG = 7856 # FIPS 205 Table 2, SLH-DSA-SHA2-128s
two = MLDSA65_SIG + ED25519_SIG
three = two + SLHDSA128S_SIG
print(two, three, round(three / two, 2))
# ==> 3373 11229 3.33

The two-component composite is 3,373 bytes and the three-component one is 11,229 bytes, so the third component more than triples the signature while adding one security assumption. That ratio is the exercise’s real content: SLH-DSA’s conservatism is bought almost entirely in bytes.

The verify function computes all three component results and returns true only if all three are true, which is the AND-mode definition. Compute all three before combining rather than letting Python’s and short-circuit, for the reason Block 3’s comment gives. Composite signatures inherit the slowest component’s verification time and the sum of the component sizes, so adding a component is conservative on security and expensive on bandwidth and CPU.

Editorial note. The parser is six lines of Python: assert len(buf) == 1216, slice buf[:1184] for the ML-KEM-768 encapsulation key, slice buf[1184:] for the 32-byte X25519 public key. The reject path raises a structured exception (e.g. InvalidKeyShareError) rather than falling through silently. The security argument depends on length validation happening before any cryptographic operation that could leak timing information about the input. The order of the components in the wire format is fixed by RFC 10024, which published this group on the Standards Track in August 2026 (ML-KEM first, X25519 second) (Kwiatkowski et al., 2026). It is no longer a draft, and a parser written against one should be re-read against the RFC. A parser that swaps the order would produce an incorrect ML-KEM ciphertext on encapsulation.

Editorial note. The TLS 1.3 transcript hash prevents it, and the reason is that the client checks the transcript against the ClientHello it sent, not against the one the server received.

RFC 9846 §4.5.2 says the CertificateVerify message “also provides integrity for the handshake up to this point”, and the signature covers Transcript-Hash(Handshake Context, Certificate), where the Handshake Context begins at ClientHello (Rescorla, 2026). The Finished MAC of §4.5.3 covers the same transcript plus CertificateVerify. An attacker who strips X25519MLKEM768 from the ClientHello leaves the server signing a transcript over the modified message while the client verifies against the original, so the two hashes differ and the client rejects the signature. Producing a CertificateVerify the client would accept requires the server’s private key, which is the whole point of authenticating the transcript rather than the message. This is the mechanism the chapter’s cryptanalysis section credits when it says transcript hashing satisfies the RFC 7696 §2.4 integrity requirement for NamedGroups.

RFC 7696 §2.4 states the requirement (“SHOULD be integrity protected”) rather than supplying a mechanism (Housley, 2015), so it is the standard being satisfied here, not the thing doing the work.

Two distinctions a strong answer draws. First, the transcript hash defeats tampering with the group list; it does nothing about a server that genuinely does not implement X25519MLKEM768 and honestly selects X25519. That second case is not an attack, and only client policy addresses it: refuse to complete a handshake without a post-quantum group, or log and alert on classical-only negotiations. Second, the protection is a detection-and-abort, not a repair. The client learns the handshake was tampered with and fails; it does not recover the hybrid.

Editorial note. Direct mitigation: Ch 26’s “Protocol-level adaptability”. Hybrid KEM is exactly a protocol-level mechanism for negotiating two key-establishment primitives at once, and the X25519MLKEM768 NamedGroup makes the dual-primitive choice a single machine-readable codepoint at the TLS layer, which is what that area asks for. Ch 26 makes the connection itself in its closing line, citing CSWP 39 §3.2.4 on hybrids as a transition-period mechanism.

Not a direct mitigation: Ch 26’s “Lifecycle management”. A hybrid changes nothing about rotation cadence, deprecation triggers, or end-of-life policy for the underlying keys. An organization running X25519MLKEM768 still has to decide when X25519 is retired from the pair, and that decision looks exactly like every other lifecycle decision in that area. If anything the hybrid makes it harder, because there are now two primitives on two retirement clocks behind one codepoint.

Either of the other four is defensible if argued from Ch 26’s own text. “Cryptographic observability” is the strongest alternative for the first half, since a single NamedGroup is what makes the choice visible in a connection log at all. Note that Ch 26’s areas are named, not numbered: a solution citing “Ch 26 §3.4” is citing a section that does not exist.

Bindel, N., Brendel, J., Fischlin, M., Goncalves, B., & Stebila, D. (2019). Hybrid Key Encapsulation Mechanisms and Authenticated Key Exchange. Post-Quantum Cryptography (PQCrypto 2019), 11505, 206–226. https://doi.org/10.1007/978-3-030-25510-7_12
Housley, R. (2015). Guidelines for Cryptographic Algorithm Agility and Selecting Mandatory-to-Implement Algorithms. IETF RFC 7696 (BCP 201). https://doi.org/10.17487/RFC7696
Kwiatkowski, K., Kampanakis, P., Westerbaan, B., & Stebila, D. (2026). Post-Quantum Traditional (PQ/T) Hybrid Key Agreement Mechanisms for TLS 1.3. RFC 10024. https://doi.org/10.17487/RFC10024
Rescorla, E. (2026). The Transport Layer Security (TLS) Protocol Version 1.3. RFC 9846. https://doi.org/10.17487/RFC9846