You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
The PQ verify precompile exposes no signature context, so signatures are not bound to a purpose #494
The SLH DSA verify precompile takes a verifying key, a message, and a signature, and checks a pure SLH DSA signature with an empty context. The interface exposes no context parameter, and the roadmap does not define a purpose binding convention for post quantum signatures. The precompile and the planned post quantum ecrecover are still pre production and not protecting live assets, so this is the right moment to raise it as a design point rather than a live concern.
Without a context, a single SLH DSA key reused across Arc's mechanisms, the raw precompile, the planned ecrecover override, account abstraction, and dApps, produces signatures that are not bound to a purpose. A signature produced for one mechanism verifies in another whenever the signed bytes coincide, which is the cross context reuse that domain separation exists to prevent. The EIP 712 digest binds the ecrecover path, but the raw precompile and cross dApp use have no enforced binding, and leaving it to each caller is where it tends to be missed.
FIPS 205 includes a context string for exactly this. Exposing it in the precompile interface and defining one distinct context per mechanism binds every signature to its purpose by construction, at negligible cost.
We raise this from direct experience. We standardised purpose binding for post quantum signatures on another chain, where every signature is bound to its context so it cannot be replayed across uses, and we are glad to share the construction and help shape the convention here.
We would be happy to open a pull request for the interface change and the test vectors if the direction is welcome.
This behavior is intentional, and it matches ecrecover. ecrecover checks a signature over whatever 32 bytes it is given and does not bind a purpose. Domain separation on EVM comes from the application layer through EIP-191 and EIP-712, not from the precompile. The SLH-DSA precompile follows the same model: it verifies a signature over the bytes the caller passes and leaves the meaning of those bytes to the application.
The precompile is already live on mainnet, and available in beta mode for app developers at this stage, and its interface might evolve in the future.
Keeping the precompile neutral like ecrecover is a coherent choice. The point still worth acting on is the docs, since the EVM world hit replay bugs again and again where domain separation was left implicit, and a line telling app teams to bind context the way EIP-712 does would close that for SLH-DSA from the start.
Agreed—a neutral precompile keeps application policy out of consensus. One documentation nuance from the current interface and implementation: verifySlhDsaSha2128s accepts arbitrary bytes message and verifies those bytes directly; it has no FIPS 205 ctx input (interface, verification call). The docs should therefore say explicitly that chain ID, verifying contract, and purpose are not injected by the precompile: callers must include a domain in the signed message themselves. A canonical EIP-712 digest or equivalent unambiguous typed encoding can do this; the app should pass those exact bytes as message.
A domain separator prevents cross-domain reuse, but not replay within the same domain. For state-changing authorizations, the guidance should also require an app-level nonce, deadline, or equivalent consumed-message check. A short example and tests showing verification fails for a different purpose/chain/verifier—and that the caller rejects a second use of the same authorization—would make the neutral interface safer to use without changing it.
@osr21 has it right and the two parts split cleanly. A domain in the signed bytes stops cross domain reuse. An app level nonce or deadline stops replay inside one domain. The precompile stays neutral either way.
I can put up a small docs and example change for verifySlhDsaSha2128s. It would pass an EIP 712 style digest as the message and add a test that verification fails when the purpose, chain id or verifying contract changes. It would also add a consumed authorization that rejects a second use. I keep a public SLH DSA reject corpus for the SHA2 128s set, with the 32 byte key and 7856 byte signature shape this precompile uses, that you could fold into the tests for the malformed signature side.
That sounds useful. To stay aligned with the maintainer’s intentional neutral interface, I’d keep this PR to documentation and an application example rather than changing precompile semantics. One useful test split would be: (1) precompile conformance vectors—valid signature/message, modified message, and malformed key/signature inputs, with the corpus source/version and license recorded; and (2) example-contract tests—build the exact 32-byte typed-data digest including chain ID, verifying contract, and purpose, then show altered domain fields fail and a consumed nonce/deadline rejects a second authorization. The latter should make clear that the precompile itself will verify the same signature repeatedly; one-time use is enforced by application state. A small manifest mapping each corpus vector to expected valid/invalid behavior would make the proposed corpus straightforward to review and adopt.
I have opened #505 with exactly this shape. The precompile stays neutral and I add the safe usage layer around it.
One, docs that the precompile injects no chain id, no verifying contract and no purpose, so the caller puts the domain in the signed message as an EIP 712 digest, a domain stopping cross domain reuse and a nonce and a deadline stopping replay inside a domain. Two, an example contract that builds the 32 byte digest over chain id, verifying contract and a purpose and enforces a nonce and a deadline. Three, the six conformance vectors mapped to their expected results with the corpus source and license. Four, example tests where a changed chain, contract or purpose fails and a reused nonce is rejected while the precompile verifies the same signature twice.
Please assign this to me and I will raise the PR under your process.
The branch is ready on my fork at quantova-inc:pq-usage-guidance, one signed commit with the four files osr21 scoped, the docs, the example, the conformance manifest and eight tests passing on solc 0.8.29. I will open it with Closes: #494 once I am assigned.
Activity
nenadmilosevic95 commentedon Oct 26, 2026
Thanks for raising this! We'll review it with the team and follow up here.
msheth-circle commentedon Oct 26, 2026
Thanks for the detailed write-up.
This behavior is intentional, and it matches
ecrecover.ecrecoverchecks a signature over whatever 32 bytes it is given and does not bind a purpose. Domain separation on EVM comes from the application layer through EIP-191 and EIP-712, not from the precompile. The SLH-DSA precompile follows the same model: it verifies a signature over the bytes the caller passes and leaves the meaning of those bytes to the application.The precompile is already live on mainnet, and available in beta mode for app developers at this stage, and its interface might evolve in the future.
quantova-inc commentedon Nov 1, 2026
Keeping the precompile neutral like ecrecover is a coherent choice. The point still worth acting on is the docs, since the EVM world hit replay bugs again and again where domain separation was left implicit, and a line telling app teams to bind context the way EIP-712 does would close that for SLH-DSA from the start.
osr21 commentedon Nov 1, 2026
Agreed—a neutral precompile keeps application policy out of consensus. One documentation nuance from the current interface and implementation:
verifySlhDsaSha2128saccepts arbitrarybytes messageand verifies those bytes directly; it has no FIPS 205ctxinput (interface, verification call). The docs should therefore say explicitly that chain ID, verifying contract, and purpose are not injected by the precompile: callers must include a domain in the signed message themselves. A canonical EIP-712 digest or equivalent unambiguous typed encoding can do this; the app should pass those exact bytes asmessage.A domain separator prevents cross-domain reuse, but not replay within the same domain. For state-changing authorizations, the guidance should also require an app-level nonce, deadline, or equivalent consumed-message check. A short example and tests showing verification fails for a different purpose/chain/verifier—and that the caller rejects a second use of the same authorization—would make the neutral interface safer to use without changing it.
quantova-inc commentedon Nov 2, 2026
@osr21 has it right and the two parts split cleanly. A domain in the signed bytes stops cross domain reuse. An app level nonce or deadline stops replay inside one domain. The precompile stays neutral either way.
I can put up a small docs and example change for verifySlhDsaSha2128s. It would pass an EIP 712 style digest as the message and add a test that verification fails when the purpose, chain id or verifying contract changes. It would also add a consumed authorization that rejects a second use. I keep a public SLH DSA reject corpus for the SHA2 128s set, with the 32 byte key and 7856 byte signature shape this precompile uses, that you could fold into the tests for the malformed signature side.
osr21 commentedon Nov 3, 2026
That sounds useful. To stay aligned with the maintainer’s intentional neutral interface, I’d keep this PR to documentation and an application example rather than changing precompile semantics. One useful test split would be: (1) precompile conformance vectors—valid signature/message, modified message, and malformed key/signature inputs, with the corpus source/version and license recorded; and (2) example-contract tests—build the exact 32-byte typed-data digest including chain ID, verifying contract, and purpose, then show altered domain fields fail and a consumed nonce/deadline rejects a second authorization. The latter should make clear that the precompile itself will verify the same signature repeatedly; one-time use is enforced by application state. A small manifest mapping each corpus vector to expected valid/invalid behavior would make the proposed corpus straightforward to review and adopt.
quantova-inc commentedon Nov 3, 2026
I have opened #505 with exactly this shape. The precompile stays neutral and I add the safe usage layer around it.
One, docs that the precompile injects no chain id, no verifying contract and no purpose, so the caller puts the domain in the signed message as an EIP 712 digest, a domain stopping cross domain reuse and a nonce and a deadline stopping replay inside a domain. Two, an example contract that builds the 32 byte digest over chain id, verifying contract and a purpose and enforces a nonce and a deadline. Three, the six conformance vectors mapped to their expected results with the corpus source and license. Four, example tests where a changed chain, contract or purpose fails and a reused nonce is rejected while the precompile verifies the same signature twice.
Eight tests pass on solc 0.8.29.
quantova-inc commentedon Nov 3, 2026
Please assign this to me and I will raise the PR under your process.
The branch is ready on my fork at
quantova-inc:pq-usage-guidance, one signed commit with the four files osr21 scoped, the docs, the example, the conformance manifest and eight tests passing on solc 0.8.29. I will open it withCloses: #494once I am assigned.