mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-11 08:47:33 +00:00
revert(cashu): re-enable NUT-12 DLEQ on own-mint outputs
Restores the per-signature `Bdhke.verifyDleq` check in `CashuMintOperations.unblindOne` that was removed earlier on this branch under the (wrong) belief that `verifyDleq`'s allocation density was triggering the ART JIT crash. The real bug was `uLtInline` inline-expansion in U256/ScalarN/FieldP (see preceding commit); with that fixed, `verifyDleq` runs at 2048 iterations/process in the regression suite (`r_verifyDleq_2048`) with no JIT trouble. The skip-DLEQ commit argued "a malicious mint can just refuse the request" so the check buys "fail fast vs fail-at-next-spend, not actual security." That undersells NUT-12: a key-substituting mint produces *unspendable* proofs, and pre-emptive DLEQ catches that at mint-receive time, before the user considers the operation done. Without the check, the failure surfaces at the next swap — by which point a sender has already considered the payment complete and any sent token is dead. Third-party proof verification (incoming cashu tokens, nutzap redeems) continues to go through `verifyDleqCarol` / `verifyTokenDleq` — unchanged, still the harder untrust boundary. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
1f3630c4ae
commit
7d5573532f
+22
-9
@@ -675,15 +675,28 @@ class CashuMintOperations(
|
||||
val mintPubKey = mintPubKeyHex.hexToByteArray()
|
||||
val cTickBytes = signature.cTick.hexToByteArray()
|
||||
|
||||
// NUT-12 DLEQ on the mint's response is intentionally NOT
|
||||
// verified here. unblindOne only runs for outputs we asked the
|
||||
// mint to sign (swap, mint-from-LN, swap-to-locked) — a
|
||||
// malicious mint could just refuse the request, so the DLEQ
|
||||
// check on our own outputs offers little marginal security
|
||||
// beyond "fail fast vs. fail-at-next-spend". Third-party proofs
|
||||
// (incoming cashu tokens, nutzap redeems) continue to be
|
||||
// Carol-verified via [verifyTokenDleq] — that's where the
|
||||
// untrust boundary actually lives.
|
||||
// NUT-12 DLEQ verification. When the mint emits a DLEQ proof
|
||||
// alongside the blind signature, we MUST verify it before we
|
||||
// treat the resulting proof as valid — without this check, a
|
||||
// malicious or buggy mint can hand us a junk C' that fails only
|
||||
// at spend time, by which point a sender already considers the
|
||||
// payment complete. Older mints omit the dleq field entirely;
|
||||
// we accept those silently for backwards compatibility.
|
||||
signature.dleq?.let { dleq ->
|
||||
val ok =
|
||||
Bdhke.verifyDleq(
|
||||
e = dleq.e.hexToByteArray(),
|
||||
s = dleq.s.hexToByteArray(),
|
||||
blindedMessage = output.bTick,
|
||||
blindSignature = cTickBytes,
|
||||
mintPubKey = mintPubKey,
|
||||
)
|
||||
if (!ok) {
|
||||
throw MintProtocolException(
|
||||
"NUT-12 DLEQ verification failed for amount ${output.amount} — mint signature does not match its published keyset key",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val c =
|
||||
Bdhke.unblind(
|
||||
|
||||
Reference in New Issue
Block a user