Repository navigation
XMSS^MT: tie the carried-over traversal state to the previous subtree in the index check - #2560
AlexLaroche wants to merge 1 commit into
Conversation
… the last leaf of a subtree only when it belongs to the previous subtree, so a key moved back to the first leaf of the subtree it was saved at the end of is refused rather than reusing a one-time key.
|
Please include a statement saying you reviewed the code. See CONTRIBUTING.md for details. |
Contribution statementUse of generative AI. I used Claude Code (Anthropic's coding agent, model Claude Opus) to help with this pull request. It helped find and reproduce the issue, write the change and its regression test, run the existing tests, and write this description. I chose what to submit and I am responsible for it. Review. I have reviewed every line of this change, including the tests and the release note, and I understand what it does and why. I checked that the new test fails on Representations under CONTRIBUTING.md. By submitting this pull request I make the representations in the project's contributing guidelines, including the following:
|
Summary
The XMSS^MT index check added for github #2414 (
BDSStateMap.validateIndex) makes one allowance. Where a layer's expected leaf is 0, it also accepts a traversal state on the layer's last leaf, because the state carried over from the previous subtree legitimately stays there until the signer rebuilds it.The allowance only checks that the state is on a last leaf. It doesn't check that this is the previous subtree's last leaf. As a result, a key saved on the last leaf of a subtree, whose index was then moved back to the first leaf of that same subtree, still passes. That key signs a second message under a one-time key it has already used, and both signatures verify.
Reproduced with bcprov-jdk18on 1.86 (through
PrivateKeyFactory, in both the legacy and the RFC 9802 PKCS#8 forms) and withmain:Change
BDS:isLastLeafOfTree(publicSeed, secretKeySeed, layer, tree)decides whether a state on the last leaf belongs to a given subtree.BDSStateMap.validate(XMSSMTParameters, long, byte[], byte[])andvalidateIndex(XMSSMTParameters, long, byte[], byte[])take the key's seeds. With the seeds, they accept the carried-over state only when it belongs to subtreetree - 1, and refuse it at tree 0.@deprecated.XMSSMTPrivateKeyParametersconstructor (both the decode and the builder paths);toByteArray()androllKey();XMSSEnginebefore signing;XmssKeyUtilbefore writing PKCS#8 (its copy of the secret seed is cleared afterwards).docs/claude/conventions.md.Tests
XMSSMTPrivateKeyTests.testIndexRollbackToStartOfSubtreeRejected.mainwithout this change ("no exception on index 3 moved back to 0").org.bouncycastle.crypto.signers.xmss.AllTestspass. They includetestEveryIndexRoundTripsand the index enumeration tests, which decode at every index of a key and so exercise the allowance at each legitimate boundary.