Prompt and scope
Implement an ML-DSA signature-verification wrapper whose caller supplies the algorithm, public key, message, signature, and context. How do you validate inputs, prevent cross-protocol replay, and design errors and tests?
What the interviewer evaluates
- Reusing a validated FIPS 204 implementation instead of rewriting lattice-cryptography primitives.
- Keeping the algorithm identifier, public key, message, signature, and context as unambiguous inputs.
- Distinguishing invalid signatures, malformed input, unsupported algorithms, and internal faults without leaking sensitive detail.
- Using context binding, a narrow interface, and vector tests to prevent cross-protocol replay and implementation drift.
Clarifying questions
- Which ML-DSA parameter set is used, and has the library passed FIPS 204 vector validation?
- Which protocol defines the context, may it be empty, and what encoding and length limits apply?
- Is verification failure an ordinary business result or an alert and audit event?
- Is the message streamed, is a pre-hash interface needed, and how are keys rotated?
30-second answer framework
I would keep the wrapper on a validated ML-DSA library API. Select the parameter set from an explicit algorithm identifier, validate byte inputs and context, and pass the protocol context with the message to verification. Return only valid, invalid, unsupported, or malformed; never return an exception trace. Test official vectors, tampering, wrong parameter sets, cross-context replay, boundary lengths, concurrency, and rotation. Private keys and full messages never enter logs.
Step-by-step deep dive
1. Set the wrapper boundary
FIPS 204 defines ML-DSA signing and verification; RFC 9882 notes that CMS uses the same verification algorithm for hedged and deterministic signatures. Application code should not implement NTT, sampling, or rejection sampling. Pin the dependency, parameter set, and tested library interface.
2. Validate inputs and context
Check that the algorithm identifier is allowed, public-key and signature bytes match the selected parameter set, and the message is within policy. Encode context according to the protocol. Context is not a log label; it provides signature domain separation and must be the same value agreed by the signer. Missing or wrong context returns malformed or invalid; never guess an empty context.
3. Design safe error semantics
Return a structured result such as valid, invalid, unsupported, or malformed rather than echoing library exceptions, key material, or parser details. Metrics and audits record algorithm, version, result class, and request id only. Internal faults go through a controlled error channel and alerting; a library crash must not be disguised as an invalid signature.
4. Lock behavior with vectors and protocol tests
Run NIST ACVP or FIPS 204-compatible vectors first, then test bit flips, truncation, different contexts, wrong algorithm identifiers, and old keys. In cross-protocol cases, ensure the same message is not accepted under a different context. Use immutable inputs for concurrent calls, accept an explicit old-key window during rotation, and reject after expiry.
High-quality sample answer
I would make this a thin wrapper around a validated library, not a reimplementation of ML-DSA. The caller must provide an explicit algorithm identifier, parameter set, public key, message, signature, and protocol context. The wrapper validates formats and sizes, passes the same context to the verification API, and never retries another parameter set or an empty context after failure. It returns only valid, invalid, unsupported, or malformed; internal faults alert separately, and logs exclude keys and messages. FIPS/ACVP vectors, tampering, truncation, cross-context replay, key rotation, and concurrency tests lock behavior while the library version and parameters remain pinned.
Common mistakes
- Reimplementing ML-DSA NTT, sampling, or rejection sampling.
- Ignoring the algorithm identifier and trying one public key across parameter sets.
- Retrying an empty or alternate context after verification fails.
- Collapsing library faults, invalid signatures, and malformed input into an unjustified result.
- Writing full messages, keys, or exception traces to logs.
- Testing only the happy path without vectors, tampering, replay, and rotation cases.
Follow-up questions and answers
Do hedged and deterministic signatures need different verification code?
No. RFC 9882 states that both use the same verification algorithm, so the wrapper should not branch into two verification paths based on signing mode.
Why does context prevent cross-protocol replay?
It binds a signature to a protocol domain. The same message and key have different verification meaning under different contexts; a verifier must not guess or fall back when context is missing.
How do you handle a library upgrade?
Pin the parameter set and library version, run official vectors and historical compatibility samples, then canary the change. Record version and result class, retaining the old verifier until migration completes if behavior differs.