verifyAnswersForgedTransitionsAsDeclared
verify* called with six transitions the scheme did not produce, and held to the answer proofStrength declared for all six.
The hole this closes. Every other mention of verify* in this suite hands it material the scheme just made itself. Against an accept-only predicate that is indistinguishable from not calling it at all — and both shipped schemes are accept-only, and no production caller consults either method, so "the TCK covers verification" was a sentence with nothing behind it. Forcing a predicate always-true proves it is consulted, never that it is sufficient; here it was not even consulted with anything that could fail.
The forgeries are derived, never supplied. There is no newForgery() hook, because a fixture that hands back an answer can hand back a wrong one — the cheapest wrong one being a "forgery" that is really an honest transition, which every arm accepts and nobody notices. The suite mints two keys from the scheme under test and crosses honest material with itself: a ciphertext produced under the other key (the substituted-layer cheat), a transition where nothing happened at all (next == prev, the skipped-layer cheat — a player who pockets a card without covering it), and an honest transition re-attributed to the other player's public key (the framed-peer cheat). Three shapes, once for verifyEncrypt and once for verifyStrip.
Each of the six is checked to be a forgery before it is used, eagerly, so a scheme on which they degenerate into honest transitions reds as a fixture problem rather than passing the ProofStrength.RejectsForgeries arm by accident. Those preconditions are the rig, and on this property the rig is the whole risk: an "adversarial" input that is not adversarial makes the arm assert the opposite of what it reads as.
Not forged: a garbled proof. verifyEncrypt(m, cipher, EncryptProof(garbage), pubKey) looks like the obvious seventh case and is deliberately absent — a scheme whose transition is recomputable from prev, next and pubKey may legitimately ignore the proof bytes entirely, so rejection is not a contract obligation and asserting it would fail a correct implementation. (RecomputingXorScheme, the binding that exercises the rejecting arm, is exactly such a scheme.)
Mutation receipts, measured on this branch (JVM, --rerun-tasks, every task EXECUTED) — each applied alone, verdict read out of the results XML, reverted, revert grep-verified. Bindings: S = SraScheme, X = XorKeystreamScheme, R = RecomputingXorScheme.
| Mutation | Reds |
|---|---|
RecomputingXorScheme.verify* → return true | this property, on R only — all six forgeries in one aggregated failure. Nothing else in the suite reds, on any binding |
Fixture: R declares AcceptsEverything | this property, on R only — the declaration is pinned in both directions |
Body: impostor = honest in forgeriesOf | three preconditions, on all three bindings — and the arm never runs |
SraScheme.encrypt/strip return their argument | the preconditions again (an identity scheme has no forgeries to offer), on S |
The third row is the load-bearing one. Collapsing the impostor onto the honest key turns all six "forgeries" into honest transitions, which the ProofStrength.AcceptsEverything arm accepts happily — on X the preconditions are the only thing that fires, and without them this property would be green while asserting the opposite of what it reads as.