The cryptographic world relies on the soundness of its basic operations—especially when it comes to zero-knowledge proofs (ZKPs). gnark is a popular Go library for zk-SNARK circuits, widely used for designing secure, privacy-preserving crypto protocols. But sometimes, a tiny bug at the core abstractions can expose serious vulnerabilities. That’s exactly what happened in CVE-2023-44378, which impacts versions of gnark prior to .9..

In this post, we’ll dive into what this bug was, how it could be exploited, and what you need to know to protect your ZKP circuits. We will keep things simple—no cryptic math or heavy jargon!

In brief

- CVE-2023-44378 is a vulnerability in the gnark library before version .9..
- It involves incorrect handling of bit decomposition for in-circuit values when they overflow the field modulus.
- Attackers could construct two valid decompositions for some values, which could undermine the soundness of ZKP circuits designed with affected versions.

The Problem: Double Decomposition

Most ZKP protocols convert numbers into their *bitwise* representation as part of constraints. For any number a, there should only be *one* way to "decompose" it into bits, like turning 13 into [1, , 1, 1].

But in gnark ≤ .8.x, you could sometimes provide two different bit decompositions for the same value (in-circuit), if you exploited the field modulus.

Why Does This Happen?

The Gnark circuits work in a finite field of prime order (think of this as numbers wrapping around after hitting a certain value, called modulus r). It turned out that for *small values of a*, you could create a different bit decomposition using a+r (wrapping around the modulus).

Both 4 and 21 (a+r) are equal modulo field, but their bit representations differ!

This means the circuit might "pass" two different bit constraints for the same field value.

Code Snippet Demonstrating the Flaw

Here’s a simple (pseudo-code) example illustrating the bug. For simplicity, let's assume field modulus 17 (in real gnark, the field is much larger):

package main

import (
    "github.com/consensys/gnark/frontend"
)

func Circuit(api frontend.API, a frontend.Variable) {
    bits, _ := api.ToBinary(a, 5) // decompose into 5 bits

    // Add your usual constraints, e.g.,
    sum := api.Add(api.Mul(bits[], 1), api.Mul(bits[1], 2), api.Mul(bits[2], 4), api.Mul(bits[3], 8), api.Mul(bits[4], 16))
    api.AssertIsEqual(sum, a)
}

Sample Exploit: Hands-on Illustration

Say your circuit expects a value less than 16, so you want bits to always be [,,,,] to [,1,1,1,1]. But an attacker submits a=21.

Legit bit decomposition for 4:
  4 => [, , 1, , ]

'Alternative' decomposition for 21:
  21 => [1, , 1, , 1]

Both satisfy: sum = bits[] × 1 + bits[1] × 2 + ... = 21
But, 21 mod 17 = 4, so api.AssertIsEqual(sum, a) passes!

--> The constraint is tricked.

This could let a malicious prover claim a value’s bits are different from what's intended, depending on the circuit design.

Security Impact

If your circuit used these bits for further processing—say, if bits represented switches, or were inputs to hashes—a malicious proof could sneak in incorrect values without failing constraints!

All gnark users with custom circuits using bitwise decompositions prior to v.9. should immediately review their code and upgrade.

The Fix: Upgrade to gnark v.9.

Developers fixed this in gnark v.9..

- The internal bit-decomposition routines now check that decompositions are canonical, i.e., no ambiguous representations.

References

- GitHub Security Advisory
- gnark Source Code
- What is a modulus? (Wikipedia)

Update to gnark v.9. or higher. This is the only way to fully remove the risk.

- Review circuit logic using bit decompositions, especially if they encode security or business logic.

Regenerate proving and verifying keys after upgrading.

- Stay tuned to gnark's GitHub repo for alerts or further security notes.

Conclusion

Bit decomposition bugs can be sneaky but dangerous. CVE-2023-44378 shows how even established zk-SNARK libraries can trip up over field arithmetic. The fix is simple—upgrade now, and keep your zero-knowledge circuits secure!


*Feel free to share this post! Questions? Drop them below or find us on GitHub.*

Timeline

Published on: 10/09/2023 14:15:10 UTC
Last modified on: 10/13/2023 18:43:14 UTC