Lesson map
Lesson 11 published. Map, example, EX031.
flowchart TD
N0["Learning outcomes"]
N1["11.1 Verify negotiation, not configuration"]
N2["11.2 Practical activity — Lab V"]
N3["11.3 Negative controls"]
N4["11.4 Closing with evidence"]
N5["11.6 Hands-on activity"]
N6["11.7 Verifying after other changes"]
N7["11.9 What verification does not show"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7
N7 --> EX["Worked example"]
EX --> Q["Exercise EX031"]
Logical drawing
flowchart LR
N0["Learning outcomes"]
N1["11.1 Verify negotiation, not configuration"]
N2["11.2 Practical activity — Lab V"]
N3["11.3 Negative controls"]
N4["11.4 Closing with evidence"]
N5["11.6 Hands-on activity"]
N6["11.7 Verifying after other changes"]
N7["11.9 What verification does not show"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> N7 --> Q["EX031"]
[Learning outcomes]
|
v
[11.1 Verify negotiation, not configuration]
|
v
[11.2 Practical activity — Lab V]
|
v
[11.3 Negative controls]
|
v
[11.4 Closing with evidence]
|
v
[11.6 Hands-on activity]
|
v
[11.7 Verifying after other changes]
|
v
[11.9 What verification does not show]
|
v
[Worked example] --> [Exercise EX031]
Learning outcomes
You will (1) verify that changes achieved their aim; (2) run known-answer and negative tests in the lab; (3) use negative controls; and (4) close work with evidence.
11.1 Verify negotiation, not configuration
A configuration can say hybrid while connections negotiate classical. Verify from the client side (tool output) and the server side (logs). For SSH, check the KEX algorithm in ssh -v; for VPNs, check tunnel status.
Use both sides: client tool output shows what one client got, and server logs show what all clients got. At HHG the guest portal's configuration listed hybrid, but logs showed only 3 % hybrid. Verify on more than one client type, including an old client, to confirm that fallback still works as intended.
11.2 Practical activity — Lab V
# Lab V (ch 11): known-answer and negative tests - the habit behind validating PQC libraries.
import hashlib
KATS = [ # (function, input, expected hex prefix) - published test values for SHA-3 / SHAKE on empty input
("sha3_256", b"", "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a"),
("shake_128/32", b"", "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26"),
]
def run(name, data):
if name == "sha3_256": return hashlib.sha3_256(data).hexdigest()
return hashlib.shake_128(data).hexdigest(32)
for name, data, want in KATS:
print(f"KAT {name:13s}", "PASS" if run(name, data) == want else "FAIL")
SIZES = {"ML-KEM-512": 800, "ML-KEM-768": 1184, "ML-KEM-1024": 1568} # encapsulation-key bytes, FIPS 203
def validate_ek(param, ek):
if len(ek) != SIZES[param]: raise ValueError(f"{param}: expected {SIZES[param]} bytes, got {len(ek)}")
return True
for param, n in [("ML-KEM-768", 1184), ("ML-KEM-768", 1183), ("ML-KEM-512", 1184)]:
try: validate_ek(param, bytes(n)); print(f"negative/positive test {param} len {n}: accepted")
except ValueError as e: print(f"negative/positive test {param} len {n}: rejected ({e})")
Expected output:
KAT sha3_256 PASS
KAT shake_128/32 PASS
negative/positive test ML-KEM-768 len 1184: accepted
negative/positive test ML-KEM-768 len 1183: rejected (ML-KEM-768: expected 1184 bytes, got 1183)
negative/positive test ML-KEM-512 len 1184: rejected (ML-KEM-512: expected 800 bytes, got 1184)
Known-answer tests check that functions produce published outputs; negative tests check that wrong-length inputs are rejected.
The first part checks two hash functions against published outputs for empty input; the second checks that encapsulation keys of the wrong length for each ML-KEM parameter set are rejected. A PASS on the known answers and the right rejections show the checks work. These are the same habits used when validating real post-quantum libraries: confirm correct outputs and confirm that bad input is refused. Change a byte of an expected value and confirm that the test now reports FAIL.
11.3 Negative controls
To trust a check, show it can fail: run it against a server with hybrid removed and confirm it reports classical. A check that always says "pass" is useless.
At HHG, after OPS-430, the staging negative control showed 0 % hybrid with hybrid removed, proving the dashboard would show a failure if one occurred. Run the negative control in staging or the lab, never by removing protection in production. Record the negative control's result in the ticket with the time and configuration used.
11.4 Closing with evidence
Attach outputs, dashboard screenshots with times, and the configuration version to the ticket. Note anything unexpected, even if the change worked.
At HHG, the OPS-430 ticket was closed with client results, the load-balancer log figure of 85 % and the staging negative control. Someone reading the ticket months later can see what was checked and when. Missing evidence means the change is not verified, however well it seems to work. Keep evidence in the agreed folder and link it from the ticket rather than pasting large outputs.
11.5 Worked example
After OPS-430, the operator verifies: three client types against the production portal (two hybrid, one older client classical as expected); load-balancer logs show 85 % hybrid; negative control in staging (hybrid removed) correctly shows 0 %. Evidence attached; ticket closed.
The verification covers both sides, includes old clients as expected fallback, and adds a negative control that proves the check can fail. Each result is attached to the ticket. An auditor could check the work from the ticket alone, without asking the operator. Use it as a model.
11.6 Hands-on activity
Verify a lab change from both client and server sides and include a negative control.
Use a change from Chapter 10, such as the group reorder. Record client results with versions, server log lines and the configuration version. For the negative control, remove hybrid in the lab, confirm the check reports classical, then restore it. Attach all outputs to a practice ticket, as if closing a production change, and note anything unexpected. Ask a peer to review it. Keep the ticket with your lab notes.
11.7 Verifying after other changes
Verification is not only for your own changes. Re-check hybrid negotiation after any update that touches the TLS or SSH stack — operating system patches, load-balancer firmware, container image rebuilds — because such updates can reset configuration. A short scripted check run after every patch window catches regressions within hours.
At HHG, a load-balancer firmware update reset the booking API's TLS profile; a scripted check after the patch window would have found it the same day. Add the check to the patch procedure.
11.8 Common errors and limits
- Verifying configuration only.
- No negative control.
- Closing tickets without evidence.
Each error leaves a change unproven. Configuration alone can say hybrid while clients negotiate classical, as on HHG's guest portal before OPS-430. A check with no negative control may report success whatever happens. A ticket closed without evidence cannot be audited and gives the next operator nothing to compare against after an update. Check each closed ticket for all three. Correct any you find before the ticket is reviewed. Note what you changed.
11.9 What verification does not show
A successful hybrid negotiation shows that the session key was agreed with ML-KEM and X25519 together. It does not show that the server certificate is post-quantum (it is probably still ECDSA or RSA), that every other hop on the path is protected, or that the library is free of implementation flaws. Write these limits in the ticket so that nobody reads "verified" as "the whole service is quantum-safe". Implementation quality comes from using vetted, updated libraries; past flaws such as the KyberSlash timing issue (Bernstein et al., 2024) were fixed by library updates, not by configuration.
Note these limits in every verification ticket.
Exercises
- EX031 — Two sides. Name the client-side and server-side evidence for TLS hybrid negotiation. Rubric: 3 = tool output and server logs.
- EX032 — Negative control. Design a negative control for SSH hybrid verification. Rubric: 3 = remove hybrid, expect classical.
- EX033 — Post-patch check. Write three steps of a post-patch verification. Rubric: 3 = run clients, check logs/dashboard, compare to baseline.
Synthesis
Verification checks real negotiation from both sides, includes negative controls, is repeated after other updates and closes work with evidence.
Continue studying — chapter 11 of 18
← Chapter 10 (draft) · Course chapter index · Chapter 12 (draft) →