A signature verification call returns true. Enumerate precisely what that proves and what it does not, and describe the verification mistakes that make a "valid" signature meaningless.
answer
- proves: these bytes + the key I chose
- no identity, no time, no audience, no revocation
- never read alg or key id from the message
- kid → {key, permitted alg} table, closed world
- malleable signature ≠ unique id
basics
~20 sIt proves only that these exact bytes were signed by whoever holds the private key matching the public key you chose. Not who that is, not when, not for whom, not that the key is still valid. The classic bypasses: taking the algorithm or the key identity from the message itself.
solid answer
~1 min**Proves:** these exact octets were signed by the holder of the private key corresponding to **the public key you selected**. **Does not prove:** - *Who* that holder is — the key-to-identity binding is a separate problem (a certificate chain, a pinned key, a published directory, trust on first use). - *When* — a signature carries no time, so an old valid artefact replays perfectly. Freshness needs a nonce, timestamp with a window, or a counter. - *For whom or for what* — audience and context. A key reused across environments means a staging-issued artefact verifies in production. - *That the key is still trusted* — expiry and revocation are outside the math. - *That the signer meant what your parser extracted* — coverage and re-parse gaps. **Fatal verification mistakes:** reading the algorithm from the untrusted message (including "none", and algorithm confusion where a public key is accepted as a symmetric secret so anyone can forge); selecting the verification key by an identifier or URL the message controls; treating parse success as verification; verifying one serialization and consuming another; skipping chain, expiry and revocation checks. Apply the ladder: the verifier owns algorithm and key. Where several are needed, a finite code-owned table selects them — closed-world enumeration, not header validation.
code
text · 10 linesTRUSTED = { # code-owned, reviewed, finite
"issuer-a-2024": { key: KEY_A, alg: ALG_ED, audience: "payments" },
"issuer-a-2025": { key: KEY_B, alg: ALG_ED, audience: "payments" }
}
entry = TRUSTED[msg.header.key_id] or reject # closed-world select
verify(entry.key, entry.alg, exact_bytes, sig) or reject # alg NOT from msg
check(claims.audience == entry.audience) or reject
check(now within [claims.issued_at, claims.expires]) or reject
check(replay_cache.add(claims.id)) or reject # freshnessgo deeper
State that verification proves the bytes match the key you used, and that the algorithm and key must never be taken from the message itself.
Enumerate the gaps — identity binding, freshness, audience, revocation — and describe the none-algorithm and algorithm-confusion bypasses.
Design the verifier: code-owned algorithm and a finite key-id table, audience and issuer checks, replay window plus cache, chain and revocation handling, verification returning the consumed bytes.
Set estate-wide rules for key segregation by environment and purpose, rotation with overlapping trusted key sets, revocation semantics and what evidence you keep so historical signatures remain assessable.
## The exact statement a verifier makes "These bytes were signed by an entity possessing the private key matching **the public key I chose to verify with**." Everything else that people believe a signature proves is supplied by machinery around it. Enumerating the gaps is the whole question. ## Gap 1 — key to identity The math binds a signature to a *key*, never to a person, company or service. Something must bind key to identity, and every option has failure modes: a certificate chain (trusted only as far as the issuing process and the root store), a pinned key (strong, but rotation becomes an operational problem), a published key directory (trust moves to the directory and its transport), trust on first use (protects continuity, not first contact). If you cannot say which mechanism binds the key you verify with, you have not authenticated anyone — you have merely confirmed internal consistency. ## Gap 2 — time A bare signature contains no timestamp. Consequences: - **Replay.** A genuine signed request or token remains genuine forever unless the protocol adds a nonce, a timestamp checked against a window, or a monotonic counter with server-side state. The signature cannot supply this. - **Was the key valid at signing time?** After revocation you must decide whether historical signatures still count, and a bare signature gives you no evidence. That is precisely why timestamping authorities and append-only transparency logs exist. - **Expiry.** Any expiry inside the signed content is only enforced if the verifier checks it. Signatures do not enforce their own claims. ## Gap 3 — audience and context A signature says nothing about the intended recipient or purpose unless that is inside the signed content *and checked*. Two failure shapes: - **Shared keys across environments or tenants.** One signing key across staging and production means an artefact minted in the weaker environment verifies in the stronger one. Segregate keys by environment, tenant and purpose. - **Cross-protocol reuse.** A key used to sign two different kinds of message lets a signature valid in one context be replayed into the other, unless the signed content is domain-separated with an explicit type or context string. Include the audience, the issuer and a purpose label in what is signed, and check all three. ## Gap 4 — coverage and consumption Verification covers the octets it was given. If the application later parses different bytes, or reads an unsigned field that changes the interpretation of signed content, the check is decorative. Verification should return the authenticated bytes and be the only route to the payload. ## The verification mistakes that void everything **Taking the algorithm from the message.** If the message declares its own algorithm and the verifier obeys, the attacker chooses the cryptography. Two shapes: declaring that no algorithm is used, so an empty signature verifies; and **algorithm confusion**, where the verifier is told the algorithm is symmetric and it dutifully uses the *public* key as the shared secret — the public key is public, so anyone can forge. Both are the same root cause: a security parameter taken from untrusted input. **Selecting the key by a message-controlled identifier.** A key id, a URL, or an embedded key inside the artefact. If the verifier fetches or trusts what the message names, the attacker signs with their own key and points at it. Key selection must be a code-owned decision. This is where the defence ladder applies. **Structural separation**: the verifier holds one algorithm and one key, both compiled in, nothing read from the message. Where multiple keys or algorithms genuinely exist (rotation, multiple issuers), **closed-world enumeration** takes the top rung: a finite, code-owned map from a key id to an entry that specifies *both* the key and its permitted algorithm, so a message can only select among entries you wrote. Merely **validating** the header — checking the algorithm string against a pattern, or the key id against a format — is open-world and still admits anything that matches the shape. **Detection** — alerting on unusual algorithms or verification failures — is last and prevents nothing. **Treating parse success as verification.** Decoding an artefact and reading its fields without ever calling verify, or ignoring the return value. Structure it so unverified content is unreachable rather than merely un-consulted. **Skipping the chain.** Verifying the leaf and not the path, missing name or usage constraints, ignoring expiry or revocation, or accepting a chain of arbitrary depth. A well-formed chain to an untrusted root proves nothing. **Assuming a signature is a unique identifier.** Some schemes are malleable: a valid signature can be transformed into a different byte string that also verifies for the same message. Anything using the signature as a deduplication key, idempotency token or database primary key breaks. Key idempotency on a message id inside the signed content instead. ## Where constant-time comparison does and does not matter A public-key verification is computed over public data, so a timing side channel on the *result comparison* is not the concern. Constant-time comparison matters when you compare a **secret** — MAC tag verification, token or API-key comparison — because an early-exit comparison leaks how many leading bytes were right and turns forgery into a byte-at-a-time search. Knowing which case you are in is a good senior discriminator. ## Interview delivery "True means: these bytes were signed by the holder of the key I picked. So my checklist is — which key did I pick and what binds it to an identity; where does freshness come from; is the audience and purpose inside the signed content and checked; is expiry and revocation checked; and is the algorithm and key selection owned by my code rather than read from the message."
- How do you add freshness to a signed artefact?The signature cannot supply it, so the protocol must. Put a timestamp and a unique identifier inside the signed content, reject anything outside a tight acceptance window, and keep a replay cache of seen identifiers covering at least that window. Alternatives are a server-issued nonce echoed in the signed content, or a monotonic counter with server-side state. Clock skew must be handled explicitly, because a generous window is the same as no window.
- Two services share one signing key across staging and production. What is the concrete risk?Any artefact minted in the weaker environment verifies in the stronger one, so a compromise of staging — usually less hardened, with broader developer access — becomes forgery of production credentials or records. It also makes revocation all-or-nothing and destroys attribution, since you cannot tell which environment produced a given signature. Segregate keys by environment, tenant and purpose, and bind the audience and issuer into the signed content so a mismatch fails even if a key leaks.
- Why should a signature not be used as a deduplication or idempotency key?Some schemes are malleable: a valid signature can be transformed into a different byte sequence that still verifies for the same message, so an attacker can submit the same request twice with two distinct signature values. Anything keyed on the signature bytes — a dedupe table, an idempotency key, a primary key — will see two different records. Key idempotency on a message identifier inside the signed content instead.
saying these in an interview costs you the question
- Reading the algorithm from the artefact's own header and honouring it.
- Fetching or trusting a verification key that the message names or embeds.
- "It decoded successfully, so it's valid."
- Assuming a valid signature implies freshness, so no replay defence is needed.
- Verifying only the leaf certificate and not the chain, expiry or revocation.
- Applying constant-time comparison folklore to public-key verification while comparing secret tags with an early-exit equality check.