fix(verify): reject crypto_profile downgrade; show verification failing in getting-started - #237
Merged
Merged
Conversation
crypto_profile is inside the signing pre-image, but spec 3.6 excludes the
whole signature block from it, so signature.algorithm could be rewritten
without disturbing the signed bytes. A manifest declaring the post-quantum
profile therefore verified VALID on a classical-only Ed25519 signature.
Cross-check the signed profile against the declared algorithm and fail
closed on a downgrade, per spec 4.2 ("prevents downgrade attacks during
the transition period"). The check runs independently of trusted_keys:
the downgrade is a property of the manifest, not of the verifier's key
material. It is one-directional, rejecting a signature weaker than the
declared profile requires and permitting a stronger one, so an issuer
dual-signing ahead of the profile flip is not flagged.
Adds conformance vector AM-VEC-020 so other-language SDKs inherit the
check.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A VALID result only means something once you have seen the same manifest come back MISMATCH. Adds a step that demonstrates the two independent failure modes and how they differ: an edit to the signed record (the signature stops covering the bytes) and runtime drift against an intact signature (the declared-vs-actual binding fails). Names the boot-time boundary rather than leaving a reader to discover it, and points at attest_runtime_state() for freshness. Every printed value is copied from a real run; prints .value so the comments match what the snippets actually output. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
What
Two independent correctness fixes found while auditing the verifier against the spec.
1. Crypto profile downgrade (
python/src/agent_manifest/_verify.py)crypto_profileis inside the signing pre-image, but spec 3.6 excludes the entiresignatureblock from it. Sosignature.algorithmcan be rewritten without disturbing the signed bytes, and nothing anywhere inverify_manifest()readcrypto_profile. A manifest declaring the post-quantum profile verifiedVALIDon a classical-only Ed25519 signature.Spec 4.2 already requires the verifier to reject rather than silently fall back ("this prevents downgrade attacks during the transition period"); this implements it.
trusted_keys. The downgrade is a property of the manifest, not of the verifier's key material.AM-VEC-020so other-language SDKs inherit the check.Not a forgery path: an attacker still needs a key in
trusted_keys. It is a declared-guarantee gap, and it is the algorithm-agility bug class the DSSE authors cite as their reason for not profiling JWS.2. Getting started shows verification failing (
docs/getting-started.md)The walkthrough ended on
VALIDand never showed aMISMATCH, so a reader never saw the check do anything. Adds a step covering both failure modes and the difference between them:signature_verified: false.system_prompt: MISMATCH.It also names the boot-time boundary explicitly and points at
attest_runtime_state(), rather than leaving a reader to find the gap and read it as an omission.Verification
Every value printed in the new docs section is copied from a real run, not written from memory.
pytest: 612 passed, 18 skipped; coverage 84.55% (gate 80%)mypy src/agent_manifest: clean, 20 filesruff check src/ tests/ --select E,F,W --ignore E501: cleanbandit -r src/agent_manifest: 0 findingsindex.jsonand the newAM-VEC-020.jsonchangedNote, not fixed here
The CLI examples in
docs/getting-started.md,docs/index.md, andREADME.mddo not run as printed. The console script maps to thecligroup and every command hangs off a nestedmanifestgroup, so the real invocation ismanifest manifest verify ..., notmanifest verify ....tests/test_cli.pyencodes the nested path, so the tests pass while the docs are wrong. Fixing it means either changing the shipped CLI surface or rewriting every documented command, which is a separate decision. The new docs section uses the Python API only, so it does not spread the bug.