fix(verify): return UNVERIFIABLE when an algorithm is unavailable; amend ADR-0005 - #245
Merged
Merged
Conversation
pyoqs is an optional extra, so on a default install any manifest declaring ML-DSA-65 or hybrid reached _require_oqs() and crashed verify_manifest() with an uncaught RuntimeError. A manifest is untrusted input, so a verification endpoint answered 500 to anything an attacker sent rather than returning a verdict. _require_oqs() now raises AlgorithmUnavailableError, a RuntimeError subclass so existing callers are unaffected. The engine catches it, records the reason as a warning, and leaves signature_verified false so the fail-closed chain yields UNVERIFIABLE. UNVERIFIABLE rather than MISMATCH is the point: the verifier has established nothing about a manifest that may be entirely valid, so reporting a defect it never observed would be wrong. An algorithm outside the registry stays a MISMATCH, rejected by the schema enum before verification runs. Spec 4.2 says this normatively, including that INCOMPATIBLE_VERSION is reserved for unsupported specification versions and must not be used here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This was referenced Jul 27, 2026
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Started as the ADR-0005 reconciliation flagged on #243. Verifying the three divergences turned up a live bug behind the third one.
The bug
pyoqsis an optional extra (pip install "agent-manifest[pq]"), so on a default install the post-quantum verifiers are unavailable. Any manifest declaringML-DSA-65orhybrid-Ed25519-ML-DSA-65reached_require_oqs(), which raisedRuntimeError, and the engine caught onlyInvalidSignatureandValueError. Reproduced before fixing:A manifest is untrusted input, so
verify_manifest()owes the caller a verdict. Instead the verification endpoint would answer 500 to anything an attacker sent, in the default install, with no key material or valid signature required to trigger it.The fix, and why UNVERIFIABLE
_require_oqs()now raisesAlgorithmUnavailableError, aRuntimeErrorsubclass so anything already catchingRuntimeErroris unaffected. The engine catches it, records the reason as a warning, and leavessignature_verifiedfalse so the existing fail-closed chain yieldsUNVERIFIABLE.Not
MISMATCH. The verifier could not appraise the signature at all, so it has established nothing about a manifest that may be entirely valid;MISMATCHwould assert a defect it never observed. This is the same distinction §5.2 already draws for a verifier lacking key material. After the fix:An algorithm identifier outside the registry stays a
MISMATCH. Worth noting I had assumed that came from the verifier's unknown-algorithm branch; it does not. The schema enum rejects it first asschema:signature.algorithm, which is stricter than I expected, and the test asserts the real mechanism.ADR-0005 amendment
Three statements in ADR-0005 did not match what shipped. Original text preserved per the repo's ADR immutability rule, corrections appended as an amendment following the ADR-0006 precedent:
standard,post_quantum,hybrid. Spec §3.1/§4.2 andCryptoProfiledefinestandardandpost-quantum, hyphenated. Hybrid is a signature algorithm, not a profile, which is the better factoring: the profile states the posture claimed, the algorithm states how it was met, so one profile can admit both a pure and a hybrid signature mid-transition.UNVERIFIABLE, notINCOMPATIBLE_VERSION. The instinct was right and the mechanism wrong twice over:INCOMPATIBLE_VERSIONis reserved for unsupported specification versions (§2.4), so reusing it leaves a relying party unable to tell "I don't understand this format" from "I understand it but can't run this primitive"; and "raise" is not something a verifier should do to untrusted input. The one thing it got exactly right, that silent pass is not permitted, is preserved.§4.2 now states the requirement normatively, including that
INCOMPATIBLE_VERSIONmust not be used for an unsupported algorithm.Verification
No conformance vector for this one: the expected result depends on which extras the verifier has installed, so it is not portable across implementations the way the
AM-VEC-*contract requires.