Skip to content

Appendix D: Solutions for Chapter 26

This page collects solutions and editorial notes for the exercises in Chapter 26: Crypto agility. 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 agility package under solutions/ch26-agility. From a clone of the companion repository, pytest tests/ch26 runs its suite. Appendix C has the setup.

Editorial note. The exercise has no canonical answer because the system is the reader’s. A strong response cites concrete identifiers (e.g. TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 rather than “ECDHE”, OID 1.2.840.113549.1.1.11 rather than “RSA with SHA-256”), names the brittle point precisely (e.g. “JWT validator hard-codes RS256 in the alg allow-list”), and identifies an agile element that already exists (e.g. “the rotation cron flips kid weekly”). The trap is declaring an area “agile” because the codebase has flexibility nobody has exercised. Agility means an algorithm change has been rehearsed in production, not that the code allows for one in principle.

Editorial note. The registry extension is mechanical: add a family field with values "hmac" or "asymmetric", dispatch the verifier from the family at lookup time, confirm the round trip for both algorithms. The value of the exercise is in the dispatch surface: HMAC verifies with the same secret the signer used; Ed25519 verifies with a public key the signer publishes separately. The registry has to carry both a key-material reference and a verifier function, with the key-material lookup distinguishing symmetric secrets from public-key entries. That distinction is the structural reason a single alg field is not enough for a multi-family registry.

The labeling is a two-axis lookup on (has algorithm identifier, has rotation policy). Run against Ch 25’s inventory unchanged, all four application-layer touchpoints come out partial, and that result is the exercise.

# Block 4: agility-status labels over the four Ch 25 touchpoints.
def agility_status(touchpoint):
has_identifier = bool(touchpoint.get("algorithm"))
has_rotation = bool(touchpoint.get("rotation_policy"))
if has_identifier and has_rotation:
return "agile"
if has_identifier or has_rotation:
return "partial"
return "brittle"
# The fields Ch 25's TOUCHPOINTS records carry. Every entry names an
# algorithm; no entry has any rotation-policy field to read.
CH25 = [
{"name": "tls_endpoint_api", "algorithm": "ECDHE-ECDSA-AES256-GCM-SHA384"},
{"name": "jwt_signing", "algorithm": "RS256"},
{"name": "password_hashing", "algorithm": "PBKDF2-HMAC-SHA256"},
{"name": "webhook_hmac", "algorithm": "HMAC-SHA256"},
]
for t in CH25:
print(t["name"], agility_status(t))
print("tls_endpoint_api + rotation:",
agility_status(dict(CH25[0], rotation_policy="90d")))
# ==> tls_endpoint_api partial
# ==> jwt_signing partial
# ==> password_hashing partial
# ==> webhook_hmac partial
# ==> tls_endpoint_api + rotation: agile

Editorial note. No touchpoint reaches agile and none falls to brittle, because Ch 25’s touchpoint schema has an algorithm field and no rotation field at all. The nine keys it fixes are name, location, algorithm, primitive, parameters, families, exposure, deployed, and owner. A uniform column is a finding about the inventory rather than about the four services. The CBOM Ch 25 built cannot express the second axis at all, so extending the generator means extending the source schema first. That is the honest answer to the exercise. The last line shows the label moving once a rotation policy exists. The deeper point: agility status lives next to quantum status in the CBOM but tracks operational maturity rather than cryptographic vulnerability, and the two move independently during a real migration.

Editorial note. A useful plan covers detection with a concrete log query (e.g. “non-zero count of signatures issued under kid rs256-2023 per hour”). It names a rotation trigger that is event-driven rather than calendar-driven (e.g. “cumulative count drops below 100 per day for one full reporting quarter”). It specifies a rollback path with a measurable abort condition (“if verification failure rate on the new kid exceeds 1 percent for any 5-minute window, redeploy the previous artifact”). Each step ties to a Ch 26 architectural area: detection to observability, rotation trigger to lifecycle management, rollback to operational tooling. The CBOM diff ties to observability, where the chapter’s Tradeoffs table puts the per-release diff. The nightly job that runs it sits under operational tooling. The trap is naming a calendar deadline as the rotation trigger. Calendar triggers retire the key whether or not the deprecation has actually completed, which produces incidents instead of cutovers.