CVE-2026-54496
Missing copy constraint in halo2_gadgets variable-base scalar multiplication allows under-constrained base, breaking Orchard Action circuit soundness
ZEBRA is a Zcash node written entirely in Rust. Prior to zebrad 5.0.0, halo2_gadgets 0.5.0, orchard 0.14.0, zcash_primitives 0.28.0, and zcashd 6.20.0, the variable-base scalar multiplication gadget in halo2_gadgets/src/ecc/chip/mul/incomplete.rs used assign_advice() for the base point without a copy constraint tying it to the actual base, allowing a malicious prover to produce a valid proof for an Orchard Action with an under-constrained base point and bypass the diversified-address-integrity check that binds pk_d, g_d, ivk, the nullifier (nf), and the spend validating key (ak) to the note being spent. This issue is fixed in zebrad 5.0.0, halo2_gadgets 0.5.0, orchard 0.14.0, zcash_primitives 0.28.0, and zcashd 6.20.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- None
- Integrity
- High
- Availability
- Low
Exploit likelihood
What each source reports
CVE List Score used
9.3
Missing copy constraint in halo2_gadgets variable-base scalar multiplication allows under-constrained base, breaking Orchard Action circuit soundness
ZEBRA is a Zcash node written entirely in Rust. Prior to zebrad 5.0.0, halo2_gadgets 0.5.0, orchard 0.14.0, zcash_primitives 0.28.0, and zcashd 6.20.0, the variable-base scalar multiplication gadget in halo2_gadgets/src/ecc/chip/mul/incomplete.rs used assign_advice() for the base point without a copy constraint tying it to the actual base, allowing a malicious prover to produce a valid proof for an Orchard Action with an under-constrained base point and bypass the diversified-address-integrity check that binds pk_d, g_d, ivk, the nullifier (nf), and the spend validating key (ak) to the note being spent. This issue is fixed in zebrad 5.0.0, halo2_gadgets 0.5.0, orchard 0.14.0, zcash_primitives 0.28.0, and zcashd 6.20.0.
- Record
CVE-2026-54496- CVSS 3.1
- 9.3 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:L - Weakness
- CWE-345
- Updated
- 2026-11-02
EUVDEUVD-2026-45228
9.3
ZEBRA is a Zcash node written entirely in Rust. Prior to zebrad 5.0.0, halo2_gadgets 0.5.0, orchard 0.14.0, zcash_primitives 0.28.0, and zcashd 6.20.0, the variable-base scalar multiplication gadget in halo2_gadgets/src/ecc/chip/mul/incomplete.rs used assign_advice() for the base point without a copy constraint tying it to the actual base, allowing a malicious prover to produce a valid proof for an Orchard Action with an under-constrained base point and bypass the diversified-address-integrity check that binds pk_d, g_d, ivk, the nullifier (nf), and the spend validating key (ak) to the note being spent. This issue is fixed in zebrad 5.0.0, halo2_gadgets 0.5.0, orchard 0.14.0, zcash_primitives 0.28.0, and zcashd 6.20.0.
- Record
EUVD-2026-45228- CVSS 3.1
- 9.3 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:L - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-ww9q-8r59-xv46
9.3
Zebra: Missing copy constraint in halo2_gadgets variable-base scalar multiplication allows under-constrained base, breaking Orchard Action circuit soundness
Summary
A soundness vulnerability in the variable-base scalar multiplication gadget of halo2_gadgets allowed a malicious prover to produce a valid proof for an Orchard Action with an under-constrained base point. Because this gadget enforces the diversified-address-integrity condition of the Orchard Action statement, the flaw let a prover satisfy that condition for an arbitrary (pk<sub>d</sub>, g<sub>d</sub>, ivk) triple, effectively bypassing the check that binds an Action to the correct incoming viewing key — and therefore to the correct nullifier (nf) and spend validating key (ak) — of the note being spent.
The main practical consequence is that an adversary could have performed a double-spend within the Orchard pool, resulting in a balance violation: the same note could be spent multiple times, each time revealing a distinct, valid-looking nullifier. The total ZEC supply was protected by Zcash's turnstile mechanism, which bounds value flowing out of any pool, so unbounded inflation of the overall supply was not possible; the exposure was inflation of value within the Orchard pool up to the turnstile-enforced limit.
Exploiting the vulnerability via a double-spend is undetectable on-chain. Exploitation only requires setting private circuit inputs to chosen values, and nullifiers produced by a double-spend are indistinguishable from honest nullifiers, so the zero-knowledge property hides any signature of the attack.
Alternatively, an adversary could (before the fix) have stolen funds by forging a spend authorization for an existing note. To do so they would have to know the note plaintext, which in practice they might obtain by knowing the corresponding incoming viewing key. For example, this could be used to bypass the protection provided by holding spending keys on a hardware wallet, if the linked software wallet were compromised. This form of exploitation cannot be detected via the turnstile (which might be a motivation to exploit the vulnerability in this way rather than via balance violation). It can be detected by the legitimate holder of stolen funds being unable to spend them, or by seeing the note's nullifier on-chain — but this cannot necessarily be distinguished from compromise of the spending key by other means.
The vulnerability has existed since the Orchard pool was introduced in the NU5 network upgrade (activated May 31, 2022). There is no evidence it was exploited prior to remediation.
Note that use of the vulnerability, either for balance violation or for theft of funds, would need to have occurred before it was remediated. Theft would be in principle detectable from that point (since the real nullifier would appear on-chain). Balance violation would not be detectable at the point of exploitation.
Root cause
The defect is in the double-and-add implementation of variable-base scalar multiplication in halo2_gadgets/src/ecc/chip/mul/incomplete.rs. For performance, the algorithm uses incomplete point-addition formulas in its intermediate stage. (Incomplete addition is safe here in principle: the first and last stages use complete addition, and reaching a degenerate case would require breaking discrete log.)
The base point is fed into the incomplete-addition stage via assign_advice(), which assigns a witness value (a private circuit input) without introducing a constraint that ties that value to the actual base the algorithm is supposed to operate on:
region.assign_advice(|| "x_p", self.double_and_add.x_p, row + offset, || x_p)?;
region.assign_advice(|| "y_p", self.y_p, row + offset, || y_p)?;
(halo2_gadgets/src/ecc/chip/mul/incomplete.rs, around L309–L310 at commit 32a87582dfb0ad9364ef3ffe71751ceab2a502ea.)
The existing q_mul_2 constraint forces the base values used across the incomplete-addition loop to be equal to one another, but nothing forces them to equal the actual base. A malicious prover therefore has complete freedom to…
- Record
GHSA-ww9q-8r59-xv46- CVSS 3.1
- 9.3 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:L - Weakness
- CWE-345
- Updated
- 2026-11-02
NVD
9.3
ZEBRA is a Zcash node written entirely in Rust. Prior to zebrad 5.0.0, halo2_gadgets 0.5.0, orchard 0.14.0, zcash_primitives 0.28.0, and zcashd 6.20.0, the variable-base scalar multiplication gadget in halo2_gadgets/src/ecc/chip/mul/incomplete.rs used assign_advice() for the base point without a copy constraint tying it to the actual base, allowing a malicious prover to produce a valid proof for an Orchard Action with an under-constrained base point and bypass the diversified-address-integrity check that binds pk_d, g_d, ivk, the nullifier (nf), and the spend validating key (ak) to the note being spent. This issue is fixed in zebrad 5.0.0, halo2_gadgets 0.5.0, orchard 0.14.0, zcash_primitives 0.28.0, and zcashd 6.20.0.
- Record
CVE-2026-54496- CVSS 3.1
- 9.3 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:L - Weakness
- CWE-345
- Updated
- 2026-11-02
OSV
9.3
Missing copy constraint in halo2_gadgets variable-base scalar multiplication allows under-constrained base, breaking Orchard Action circuit soundness
ZEBRA is a Zcash node written entirely in Rust. Prior to zebrad 5.0.0, halo2_gadgets 0.5.0, orchard 0.14.0, zcash_primitives 0.28.0, and zcashd 6.20.0, the variable-base scalar multiplication gadget in halo2_gadgets/src/ecc/chip/mul/incomplete.rs used assign_advice() for the base point without a copy constraint tying it to the actual base, allowing a malicious prover to produce a valid proof for an Orchard Action with an under-constrained base point and bypass the diversified-address-integrity check that binds pk_d, g_d, ivk, the nullifier (nf), and the spend validating key (ak) to the note being spent. This issue is fixed in zebrad 5.0.0, halo2_gadgets 0.5.0, orchard 0.14.0, zcash_primitives 0.28.0, and zcashd 6.20.0.
- Record
CVE-2026-54496- CVSS 3.1
- 9.3 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:L - Weakness
- CWE-345
- Updated
- 2026-11-02
OSVGHSA-ww9q-8r59-xv46
9.3
Zebra: Missing copy constraint in halo2_gadgets variable-base scalar multiplication allows under-constrained base, breaking Orchard Action circuit soundness
Summary
A soundness vulnerability in the variable-base scalar multiplication gadget of halo2_gadgets allowed a malicious prover to produce a valid proof for an Orchard Action with an under-constrained base point. Because this gadget enforces the diversified-address-integrity condition of the Orchard Action statement, the flaw let a prover satisfy that condition for an arbitrary (pk<sub>d</sub>, g<sub>d</sub>, ivk) triple, effectively bypassing the check that binds an Action to the correct incoming viewing key — and therefore to the correct nullifier (nf) and spend validating key (ak) — of the note being spent.
The main practical consequence is that an adversary could have performed a double-spend within the Orchard pool, resulting in a balance violation: the same note could be spent multiple times, each time revealing a distinct, valid-looking nullifier. The total ZEC supply was protected by Zcash's turnstile mechanism, which bounds value flowing out of any pool, so unbounded inflation of the overall supply was not possible; the exposure was inflation of value within the Orchard pool up to the turnstile-enforced limit.
Exploiting the vulnerability via a double-spend is undetectable on-chain. Exploitation only requires setting private circuit inputs to chosen values, and nullifiers produced by a double-spend are indistinguishable from honest nullifiers, so the zero-knowledge property hides any signature of the attack.
Alternatively, an adversary could (before the fix) have stolen funds by forging a spend authorization for an existing note. To do so they would have to know the note plaintext, which in practice they might obtain by knowing the corresponding incoming viewing key. For example, this could be used to bypass the protection provided by holding spending keys on a hardware wallet, if the linked software wallet were compromised. This form of exploitation cannot be detected via the turnstile (which might be a motivation to exploit the vulnerability in this way rather than via balance violation). It can be detected by the legitimate holder of stolen funds being unable to spend them, or by seeing the note's nullifier on-chain — but this cannot necessarily be distinguished from compromise of the spending key by other means.
The vulnerability has existed since the Orchard pool was introduced in the NU5 network upgrade (activated May 31, 2022). There is no evidence it was exploited prior to remediation.
Note that use of the vulnerability, either for balance violation or for theft of funds, would need to have occurred before it was remediated. Theft would be in principle detectable from that point (since the real nullifier would appear on-chain). Balance violation would not be detectable at the point of exploitation.
Root cause
The defect is in the double-and-add implementation of variable-base scalar multiplication in halo2_gadgets/src/ecc/chip/mul/incomplete.rs. For performance, the algorithm uses incomplete point-addition formulas in its intermediate stage. (Incomplete addition is safe here in principle: the first and last stages use complete addition, and reaching a degenerate case would require breaking discrete log.)
The base point is fed into the incomplete-addition stage via assign_advice(), which assigns a witness value (a private circuit input) without introducing a constraint that ties that value to the actual base the algorithm is supposed to operate on:
region.assign_advice(|| "x_p", self.double_and_add.x_p, row + offset, || x_p)?;
region.assign_advice(|| "y_p", self.y_p, row + offset, || y_p)?;
(halo2_gadgets/src/ecc/chip/mul/incomplete.rs, around L309–L310 at commit 32a87582dfb0ad9364ef3ffe71751ceab2a502ea.)
The existing q_mul_2 constraint forces the base values used across the incomplete-addition loop to be equal to one another, but nothing forces them to equal the actual base. A malicious prover therefore has complete freedom to…
- Record
GHSA-ww9q-8r59-xv46- CVSS 3.1
- 9.3 Critical
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:L - Weakness
- CWE-345
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
zebradcrates.io |
through 4.5.1 | 5.0.0 |
halo2_gadgetscrates.io |
before 0.5.0 | 0.5.0 |
orchardcrates.io |
before 0.14.0 | 0.14.0 |
zcash_primitivescrates.io |
before 0.28.0 | 0.28.0 |
zebradcrates.io |
before 5.0.0 | 5.0.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-2026-54496 is a critical vulnerability (CVSS 9.3) in Zcash Orchard, published July 6, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.32%, the 24th percentile. Fixed in zebrad 5.0.0.
References
github.com/ZcashFoundation/zebra/security/advisories/GHSA-ww9q-8r59-xv46
github.com/ZcashFoundation/zebra/releases/tag/v5.0.0
github.com/zcash/halo2/releases/tag/halo2_gadgets-0.5.0
github.com/zcash/librustzcash/releases/tag/zcash_primitives-0.28.0
github.com/zcash/orchard/releases/tag/0.14.0
github.com/zcash/zcash/releases/tag/v6.20.0
Left out here: the rest of 2 source descriptions. The JSON record has them.