Skip to content

docs(asvs 11.2.2): the crypto agility seam contract, and a false comment corrected by execution - #328

Merged
wshallwshall merged 5 commits into
mainfrom
claude/asvs-1122-crypto-seam-contract
Aug 11, 2026
Merged

docs(asvs 11.2.2): the crypto agility seam contract, and a false comment corrected by execution#328
wshallwshall merged 5 commits into
mainfrom
claude/asvs-1122-crypto-seam-contract

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

The written seam contract that ASVS 11.2.2 needs, plus a false comment in store/crypto.py corrected
by execution. Not a behaviour change: no default moves and no algorithm changes.

The requirement has two readings that cost very different things, so this commits to one

11.2.2 asks that cryptography be "reconfigured, upgraded, or swapped at any time". That is ambiguous in
a way that matters, so the document states which reading is claimed:

  • COMMITTED -- a release can change an at-rest algorithm with no data migration and no unreadable
    ciphertext left behind.
  • NOT COMMITTED -- an operator selecting an algorithm on a running instance.

The second is a refusal with a stated price, not an unbuilt feature, and the price is what makes it a
decision rather than an omission: an algorithm identifier is read from three places -- config (the
operator chooses), the wire (the token minter chooses), and stored data (mfenc:v2's alg segment,
so whoever can write a store row chooses). A second registered AEAD lands in that third and most exposed
class, and turns a fail-closed one-way dispatch into a two-way dispatch keyed on attacker-writable
data
.

The limit is pinned, because it is the half a reader loses first

The seam covers the at-rest value core and not the audit MAC or its KDF, which carry no version
discriminator at all. Any future claim that this project "has crypto agility" must exclude the audit
chain or be false.
That sentence is pinned by a test rather than left in prose.

A false comment, settled by execution rather than by reading

messagefoundry/store/crypto.py:96 carried:

_V2_PREFIX = "mfenc:v2:"  # additive, decode-capable; not written by default

while the module docstring 60 lines above says v2 is "ON by default since ADR 0148 GIVEN 1, so the
default at-rest format is mfenc:v2"
. Two statements in one file, flatly contradicting.

Settled by running it, not by picking the more convincing sentence: StoreSettings().aad_bind is
True, so make_cipher receives write_v2=True and v2 is the shipped writer. The docstring was
right and the constant's own comment was wrong -- which is the worse way round, because the comment
sits on the thing it describes.
A reader inspecting _V2_PREFIX meets the false statement first; the
true one is 60 lines away and easy never to reach.

Verification

8 new tests pin the contract, its LIMIT and its REFUSAL -- a published contract that nothing checks
is a claim, not a control.

Watched fail, each landing on its own assertion and no other, which is what distinguishes a test that
covers a property from one that merely passes beside it:

  • reverting aad_bind to False reds only the shipped-default test
  • neutering the unknown-alg branch reds only the fails-closed test

112 passed across the crypto / PHI-doc / cipher-selection slices; ruff and mypy clean on every touched
file.

Independently re-checked before merge: the branch is 0 behind main, merges clean (merge-tree
rc=0, read from the return code rather than the output shape), and the aad_bind = True claim was
re-derived by execution against the project interpreter rather than taken from the report.

Independent of #325 -- different files entirely, no interaction with the store signature change or
the SQL Server leg.

…1.2.2)

G18 ruled Reading A on 2026-08-11: this project commits to RELEASE-swappability
and explicitly refuses RUNTIME reconfiguration. The ruling's cost was "a written
seam contract"; this is it, and the contract is what 11.2.2 actually needs.

The requirement's phrase -- "reconfigured, upgraded, or swapped at any time" --
has two readings that cost wildly different things, so the document commits to
one EXPLICITLY rather than letting a reader infer it:

  COMMITTED     a RELEASE can change an at-rest algorithm with no data
                migration and no unreadable ciphertext left behind
  NOT COMMITTED an OPERATOR cannot select an algorithm on a running instance,
                and that is a refusal with a stated price, not an unbuilt feature

Each of the three properties was verified against shipped code, not asserted:
the value is self-describing (mfenc:v2:<alg>:<key_id>:<b64>), the reader fails
closed on an unknown version OR algorithm, and rotate-key gives the swap a
driven, resumable migration path. v2 is the SHIPPED default writer, so the
contract describes what a new deployment actually writes rather than an opt-in.

WHY RUNTIME SELECTION IS REFUSED, recorded as a cost. An algorithm identifier
here is read from three places: config (the operator chooses), the wire (a
token minter chooses), and STORED DATA -- mfenc:v2's alg segment, which means
whoever can write a store row chooses. A second registered AEAD lands in that
third and most exposed class and converts a fail-closed one-way dispatch into a
two-way one keyed on attacker-writable data. The agility asked for would be
bought by creating a downgrade surface.

THE LIMIT IS STATED AND PINNED, because it is the half a reader loses first:
the seam covers the at-rest value core and NOT the audit MAC or its KDF, which
carry no version discriminator at all. Changing the audit MAC means versioning
the tamper-evidence chain, which is undesigned. Any future claim that this
project "has crypto agility" must exclude the audit chain or be false.

ALSO CORRECTS A FALSE COMMENT IN THE CRYPTO MODULE, found while verifying:
_V2_PREFIX carried "not written by default" while the module docstring 60 lines
above said v2 is ON by default since ADR 0148. Settled by execution --
StoreSettings().aad_bind is True, so make_cipher gets write_v2=True. The
docstring was right and the constant's own comment was wrong, which is the
worse way round: the comment sits on the thing it describes.

A published contract nothing checks is a claim, not a control -- so eight tests
pin it, including the limit and the refusal. WATCHED FAIL, each landing on its
own assertion and no other: reverting aad_bind to False reds ONLY the
shipped-default test; neutering the unknown-alg branch reds ONLY the
fails-closed test.
@wshallwshall
wshallwshall merged commit 4b7ed5e into main Aug 11, 2026
35 checks passed
@wshallwshall
wshallwshall deleted the claude/asvs-1122-crypto-seam-contract branch August 11, 2026 20:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant