A standard defines the algorithm's behavior. A deployed implementation must also preserve its secrets while executing that behavior on a real compiler, processor, operating system, and application boundary — and it can violate the standard's assumptions without ever changing its mathematical description. This chapter distinguishes timing leakage, physical leakage, fault attacks, randomness failures, memory defects, and integration errors, then assembles the specific kind of evidence appropriate to each — including exactly what real formal-verification proofs like mlkem-native's do and don't cover, and why the KyberSlash timing bug is an implementation lesson, not evidence every ML-KEM implementation is broken.
1. Objectives and evidence boundaries
Parts 7–9 explained the standardized algorithms; this chapter examines how an implementation can violate their assumptions without changing their mathematical descriptions at all. You'll learn to distinguish timing leakage, physical leakage, fault attacks, randomness failures, memory defects, and integration errors, then assemble evidence appropriate to each threat rather than one generic "we tested it."
2. Start with the attacker and the boundary
A remote client can submit chosen ciphertexts or signatures and measure response times. A colocated process may observe shared caches or other microarchitectural effects. An attacker holding a device may measure power or electromagnetic emissions, manipulate voltage or clock behavior, or access debug interfaces. These are genuinely different capabilities and require different evaluations — a review that only covers remote timing says nothing about physical access.
Name the protected assets: long-term private keys, regenerating seeds, ephemeral secrets, internal rejection decisions, and derived session material. Name the observable outputs: return codes, timing, packet sizes, logs, crashes, power traces, and fault responses. An assurance claim is meaningful only after identifying exactly what must remain independent of the secrets.
| Threat | Typical observation | Useful evidence | Important limitation |
|---|---|---|---|
| Remote timing | Request latency distributions | Constant-time review and targeted timing tests | Network noise can hide leakage |
| Cache leakage | Secret-dependent memory behavior | Access-pattern analysis, target-specific review | Timing proofs may model only selected effects |
| Power or EM | Physical traces | Masking assessment, laboratory evaluation | Branchless code alone is insufficient |
| Fault injection | Corrupted computations, skipped checks | Fault-model analysis, countermeasure testing | Functional tests don't reproduce physical faults |
| Memory defects | Crashes, unintended reads/writes | Sanitizers, fuzzing, memory-safety proofs | A safe buffer can still contain a leaked secret |
A cloud KEM service and a smart-meter firmware signer should not inherit identical review checklists without considering their actual environments. Conversely, excluding physical attacks should be an explicit, documented scope decision — not an assumption quietly inferred from passing remote tests.
3. What constant-time programming means
In the usual software leakage model, control flow, memory addresses, and variable-latency operations should not depend on secrets. "Constant time" does not mean every request takes exactly the same wall-clock duration — scheduling, caches affected by public inputs, and other environmental effects can vary while the sensitive computation itself preserves its intended independence from secret data.
A comparison that returns at the first unequal byte can reveal how long a prefix matched. A table indexed by a secret coefficient can reveal cache-line access patterns. A division instruction may have operand-dependent latency on a target processor even when the source code contains no branch at all. Review must therefore examine actual operations and generated code — not just search the source for if statements.
A safer engineering pattern is to use the library's reviewed constant-time arithmetic and comparison primitives, preserve their documented input ranges, and inspect the actual target build. Copying a bit trick from another project without its range assumptions can introduce overflow or compiler behavior that defeats the intended property entirely.
4. Bad and safer patterns at the API boundary
Unsafe design idea:
if internal_ciphertext_check_failed:
log recovered_message
return a special authentication error immediately
Safer responsibility split:
library performs the specified decapsulation and implicit rejection
application checks public API errors and expected output length
protocol performs its normal transcript/key-confirmation checks
telemetry records public failure categories without secret intermediates
The second pattern is not complete code — it assigns sensitive decisions to a reviewed primitive and keeps the application from turning internal validity into an oracle (Part 7 §7). Public malformed-length rejection remains legitimate where specified; hiding every error indiscriminately is not the goal, only hiding the internal validity signal.
Another unsafe idea is indexing a lookup table with secret data because it's "faster than arithmetic." A safer candidate may use a documented constant-time implementation whose memory pattern is independent of the secret — but whether that candidate is actually safe depends on the target architecture and compiler, so the "constant-time" label must be supported by evidence, not just intent.
Likewise, a constant-time selection primitive must receive a properly formed mask and valid buffers. Converting a secret condition into a mask through an ordinary branch can reintroduce the leak before the selection even happens. Review the entire data path — not just approve one line in isolation and assume the rest is fine.
5. Decapsulation failures and chosen ciphertexts
ML-KEM decapsulation processes attacker-controlled ciphertexts together with secret key material. Its re-encryption check and fallback-secret selection must preserve implicit rejection (Part 7 §7). A correct-length invalid ciphertext normally yields alternate secret material without exposing the internal check; later protocol authentication then fails because the peers derived different keys, not because the KEM signaled failure directly.
Length errors, malformed keys, provider failures, and failed final authentication are four distinct events. Tests should exercise all of them through supported interfaces. An application that assumes every nonempty secret output means "authenticated peer success" misunderstands the KEM contract completely. An application that logs the internal validity flag undermines the construction for the sake of diagnostics — a real and recurring integration mistake.
Rate limiting and abuse controls can reduce attack opportunities, but they do not repair an oracle. An attacker may distribute requests or exploit a low-noise local path entirely. Use operational controls as defense in depth while fixing the sensitive behavior itself — never as a substitute for it.
The Part 7 lab checked agreement, same-length corruption, and truncation. Those tests establish a few functional outcomes for one specific binary. They do not establish that the two same-length paths have indistinguishable timing, nor that faulted re-encryption behaves safely under physical attack. Each stronger claim requires additional, dedicated analysis beyond that functional test.
6. Power, electromagnetic leakage, and faults
A processor can execute the same instructions and memory addresses while consuming data-dependent power. Physical leakage therefore requires a genuinely different model from ordinary constant-time software. Masking splits sensitive values into shares so individual intermediates reveal less — but the implementation must preserve the masking assumptions through arithmetic, randomness, and compiler transformations, or the protection quietly disappears.
Fault injection can skip checks, corrupt sampled values, alter addresses, or damage a signature computation. A faulty signature can expose information in some constructions even if ordinary signatures from the same scheme remain secure. Countermeasures may include redundancy, consistency checks, protected control flow, or verifying a generated signature before release — the right choice depends on the algorithm and the specific fault model assumed.
No single countermeasure covers every fault. A fault that bypasses both a computation and its check defeats naive duplication entirely. A secure-device evaluation should state the assumed attacker capabilities and test the actual hardware/firmware combination together. A software unit test that flips one buffer byte is useful fault simulation, but it is not equivalent to a physical fault campaign on real silicon.
7. Randomness: entropy, DRBGs, and failure handling
An entropy source supplies unpredictable input; a deterministic random-bit generator (DRBG) expands suitable seed material into a pseudorandom stream. These roles are distinct (Part 2 §3). NIST's SP 800-90B addresses entropy sources, while SP 800-90A Revision 1 specifies DRBG mechanisms. A statistical-looking byte sequence alone does not prove adequate entropy underneath it.
Use the maintained library's random-generation interface and check its failure returns explicitly. Do not replace unavailable randomness with a timestamp, process identifier, fixed seed, or a retry that silently reuses previous output. Cloned virtual machines, early boot, process forks, and device manufacturing can all create repeated-state risks that ordinary desktop tests routinely miss.
ML-KEM key generation and encapsulation need fresh random inputs (Part 7 §6). ML-DSA supports hedged and deterministic signing, but both require secure key generation, and deterministic mode does not make every implementation attack disappear (Part 8 §8). SLH-DSA also derives sensitive material from seeds whose protection is fundamental (Part 9 §7). Algorithm-specific requirements should guide the randomness interface, not one generic "get random bytes" call.
Test randomness failure through controlled dependency injection in a test build. Confirm that key generation or signing fails safely, outputs are never treated as usable, and the application doesn't continue with stale buffers. Never ship a test-entropy override left enabled in production merely because it made failures reproducible during development.
8. Memory safety and secret lifecycle
Bounds checks, integer ranges, aliasing requirements, and ownership rules all matter in polynomial arithmetic and serialization. A buffer overflow can reveal keys even if the cryptographic operation is mathematically correct. An integer overflow can silently change a distribution or norm check (Part 6 §6) without an obvious crash to notice. Sanitizers and static analysis help expose these defects under tested paths.
Memory-safe languages reduce classes of accidental memory corruption, but foreign-function interfaces, unsafe blocks, assembly, and secret-dependent behavior still need review regardless of language. A safe slice API does not prove constant-time execution. Conversely, a formally reviewed arithmetic backend can be undermined by an unsafe wrapper or an incorrect output-length assumption around it.
Key custody extends beyond the live process. Private-key seeds in backups, container layers, debug attachments, or CI artifacts remain fully usable secrets (Part 12 §11). Define which process can obtain raw key material, which operations a signing service authorizes, and how key retirement reaches replicas and recovery copies. Cryptographic code cannot enforce a lifecycle that the surrounding system ignores.
9. Compiler and vector-backend changes are security changes
A compiler upgrade can transform arithmetic, introduce branches, or select instructions with different latency characteristics — even for source code that hasn't changed at all. Link-time optimization and architecture flags can change the generated path. Therefore the reviewed source file is not the entire release identity: retain compiler version, flags, target architecture, and the actual binary or reproducible build reference alongside it.
Vectorized implementations introduce lane ordering, alignment, packing, and target-dispatch concerns. AVX2, NEON, and other backends should be tested against the exact same known-answer and negative cases. A runtime CPU-feature check must not dispatch to unsupported instructions, and a fallback path should never quietly lose required security properties compared to the accelerated path.
Performance optimizations can also change stack size or register pressure, creating spills of sensitive intermediates onto the stack unexpectedly. An optimization should include functional, memory, and leakage review appropriate to its scope. A faster benchmark is insufficient justification for replacing a reviewed constant-time operation with a target-specific shortcut that hasn't been reviewed to the same standard.
10. Tests establish different kinds of evidence
Known-answer tests reproduce exact outputs for fixed test inputs, often including controlled randomness. They detect implementation disagreement with vectors but do not cover all inputs. Randomized functional tests explore additional cases and should include key agreement, sign/verify, wrong keys, altered messages, and malformed inputs together.
NIST's ACVP server repository publishes material supporting algorithm testing. Pin the vector release and algorithm revision explicitly. Passing locally downloaded vectors is useful engineering evidence — it is not the same as obtaining an official validation certificate through the relevant process (§11). See the ACVP server repository.
Fuzzing exercises parsers and APIs with generated or mutated inputs. Use sanitizers, resource limits, and minimized regression cases. Differential testing compares independent implementations on equivalent inputs — agreement increases confidence, but two implementations can share the same bug or the same interpretation of an ambiguous spec passage. Disagreement is a starting point for investigation, not automatic proof that one specific side is correct.
Statistical timing tools such as dudect compare timing distributions for selected input classes. A detected difference warrants investigation. Failure to detect a difference does not prove constant-time behavior across all secrets, devices, compilers, or attack models — test design, noise, sample count, and class selection all determine what the result can actually reveal. See dudect.
11. Formal verification: read the scope before the headline
The maintained mlkem-native project is a useful implementation-study example. Its documentation describes memory and type-safety proofs for specified C code, and functional-correctness, memory-safety, and secret-independent timing proofs for its AArch64 and x86_64 assembly. Those are scoped claims about identified components and models — not a blanket "this is secure" statement.
Fig. 1 — Good assurance communication states both columns explicitly. Expanding a scoped component claim into "the whole application is formally secure" misrepresents exactly what was proven.
The same documentation explicitly excludes other attack classes, including power/EM, some microarchitectural effects, and fault injection, from its timing-resistance target. This is good assurance communication — it lets an integrator understand precisely what evidence exists and what remains outside the model. Do not expand a component claim into "the whole application is formally secure." See the mlkem-native project.
Review the proof's trusted computing base, specification, architecture model, assumptions about callers, and correspondence to the shipped revision. Replacing a hash backend, changing a compiler path, or modifying an interface can move the deployment outside the established proof boundary without anyone noticing. The project's soundness documentation is as important as its performance chart, and reading only the latter is a common mistake.
12. Standardization, algorithm testing, and module validation
FIPS 203/204/205 define algorithms. Algorithm testing evaluates conformity to specified operations. The Cryptographic Module Validation Program (CMVP) evaluates cryptographic modules under a separate framework entirely, including an identified module boundary and operational conditions. These are related but genuinely distinct statements, each proving something different.
A product using a standardized algorithm is not automatically a validated module. A module certificate does not automatically cover every version, platform, provider configuration, or application integration built on top of it. Check the actual certificate and security policy when validation is required, and record the approved operating conditions rather than relying on a marketing phrase like "FIPS validated" without the specifics. See the Cryptographic Module Validation Program.
Likewise, enabling a provider named "FIPS" does not establish that the entire application uses only approved operations correctly. Key import, protocol composition, entropy, configuration, and boundary conditions all matter. Compliance evidence should identify the concrete module and deployment, while security review continues to examine the complete system around it.
13. Release evidence must survive upgrades
Keep the assurance record alongside the dependency update process, not in a separate document nobody checks. A library patch may fix a timing issue while simultaneously changing supported providers, encodings, or performance characteristics. The release owner should identify which tests need rerunning and which proof claims still match the shipped source and object code after the update.
An emergency rollback also needs scrutiny. Returning to a version with a known side-channel flaw can restore availability while reopening the original vulnerability. Prefer a documented mitigation or corrected build when possible, and record the residual exposure when an operational exception is genuinely unavoidable. The important artifact is a traceable decision tied to exact versions — not a permanent green security badge nobody revisits.
Does KyberSlash mean ML-KEM itself is broken?
No (§3). It documents exploitable secret-dependent division timing in specific implementations of the Kyber family — an implementation lesson with named affected binaries and fixes, not a flaw in the standardized ML-KEM algorithm itself.
Does "constant time" mean every request takes exactly the same wall-clock duration?
No (§3). It means control flow, memory addresses, and variable-latency operations don't depend on secrets. Scheduling, caches affected by public inputs, and other environmental effects can still vary the actual wall-clock time.
If mlkem-native has formal proofs, is any application using it automatically secure?
No (§11). The proofs are scoped to specific components (memory/type safety, timing for two assembly targets) and explicitly exclude power/EM, some microarchitectural effects, fault injection, and the calling application itself. Read the scope, not just the headline.
Takeaways
- Different attackers need different evidence — remote timing, cache leakage, power/EM, faults, and memory defects each require their own test methodology; passing one says nothing about the others.
- "Constant time" is about secret-independence, not equal wall-clock duration — and KyberSlash shows even a seemingly harmless division instruction can leak through operand-dependent latency.
- Never let an application see the internal KEM validity flag — implicit rejection (Part 7 §7) only works if the app treats every decapsulation output identically and lets protocol authentication fail downstream.
- Zeroization can be silently removed by dead-store elimination — use the library's documented clearing mechanism, never a naive memset loop you wrote yourself.
- A compiler upgrade or vector-backend change is a security-relevant change, even with zero source-code modifications — pin and record the full build identity, not just the source revision.
- Read a formal-verification proof's scope before its headline. mlkem-native's timing proofs cover two specific assembly targets and explicitly exclude power/EM and fault attacks — a scoped claim, not a blanket security guarantee.
References & further reading
- FIPS 203 · FIPS 204 · FIPS 205 — the algorithm specifications under review here.
- KyberSlash paper — §3, the real timing-leak case study.
- SP 800-90B · SP 800-90A Rev. 1 — §7, entropy sources and DRBGs.
- NIST ACVP server repository — §10, algorithm test vectors.
- dudect — §10, statistical timing analysis.
- mlkem-native project — §11, the formal-verification scope example.
- Cryptographic Module Validation Program — §12.