keyring: thread a FIPS-204 signing context through pair.sign() #5
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Depends on #2. The caller side is quantus/extension#4.
Why a context at all
ML-DSA hashes a domain-separation context into the signature, so a signature made under one context does not verify under another — same key, same message, different answer. Quantus uses that deliberately: extrinsics are signed under
"QUANTUS_EXTRINSIC", while litep2p node-identity signatures stay on the empty context so mixed-version Noise handshakes keep working.chain:primitives/dilithium-crypto/src/signing_context.rsspells out the reasoning.There is a version boundary. Spec ≥ 148 verifies under
"QUANTUS_EXTRINSIC"; earlier specs use the empty context. The SDK encodes this as:The API problem
KeyringPair.sign(message, options)currently takes{ withType?: boolean }. The context is a third thing the caller must supply, it is not derivable from anything the keyring knows, and getting it wrong produces a signature that is cryptographically valid and rejected by the chain — the worst failure shape available, because nothing local can detect it.So: extend the options type with a context, and do not give it a default that silently works. A Quantus pair asked to sign with no context stated should either refuse, or use the empty context and be documented as doing so — but it must not quietly assume
"QUANTUS_EXTRINSIC", because then the spec-147-and-earlier case becomes undiagnosable.Note that only the caller knows the spec version: it arrives in the
SignerPayloadJSONthe dapp sends. The keyring's job is to carry the context faithfully, not to guess it.Acceptance
"QUANTUS_EXTRINSIC"verifies with the runtime'sverify_ml_dsa_65and fails under the empty contextVerified against
main(7f23d0a60), closing.SignOptions.contextexists, and an ML-DSA pair refuses to sign without one (pair/index.ts, with a spec).contextForSpec(specVersion), and quantus/extension#4 showed chain acceptance under spec 148 and rejection under the empty context.@quantus/crypto.Small gaps, not worth holding this open for:
The curve pairs go away in quantus/common#6 anyway.