Skip to content

Commit 53bb128

Browse files
Merge pull request #1286 from chonilius/feat/zk-rules-advanced-1204-1208-1209-1210
feat: ZK Rules Advanced Implementation Framework (Z008, Z012, Z013, Z014)
2 parents aa77f54 + a362455 commit 53bb128

5 files changed

Lines changed: 433 additions & 147 deletions

File tree

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
# ZK Rules Advanced: Implementation Plan
2+
3+
**Status**: Implementation Framework
4+
**Author**: chonilius
5+
**Date**: 2026-07-27
6+
**Issues Addressed**: #1204 (Z008), #1208 (Z012), #1209 (Z013), #1210 (Z014)
7+
8+
---
9+
10+
## Executive Summary
11+
12+
Four advanced ZK detection rules focusing on cross-file analysis, privacy leakage, rollup patterns, and merkle proofs.
13+
14+
**Priority**: Z014 (merkle) → Z013 (rollup) → Z012 (privacy) → Z008 (curve mismatch)
15+
16+
---
17+
18+
## Issue #1210: Z014 - Missing Merkle-Root Verification
19+
20+
### Goal
21+
Detect merkle-path verification where computed root never compared against stored root, or comparison happens after leaf is trusted.
22+
23+
### Vulnerable Pattern
24+
```rust
25+
// ❌ Missing root comparison
26+
pub fn claim_with_merkle(env: Env, leaf: BytesN<32>, path: Vec<BytesN<32>>) {
27+
let computed_root = compute_merkle_root(&env, leaf, path);
28+
// ❌ Never checks: computed_root == stored_root
29+
30+
process_claim(&env, &leaf); // Trust leaf without verification!
31+
}
32+
```
33+
34+
### Secure Pattern
35+
```rust
36+
// ✅ Root comparison before trust
37+
pub fn claim_with_merkle(env: Env, leaf: BytesN<32>, path: Vec<BytesN<32>>) {
38+
let computed_root = compute_merkle_root(&env, leaf, path);
39+
let stored_root = env.storage().get(&ROOT_KEY).unwrap();
40+
41+
if computed_root != stored_root {
42+
panic!("Invalid merkle proof");
43+
}
44+
45+
// Now safe to trust leaf
46+
process_claim(&env, &leaf);
47+
}
48+
```
49+
50+
### Implementation Effort
51+
**3-4 days** (after #1192, #1194)
52+
53+
---
54+
55+
## Issue #1209: Z013 - Missing Batch-Root Validation (Rollup)
56+
57+
### Goal
58+
Detect ZK-rollup state transitions accepting `(old_root, new_root, proof)` without verifying `old_root == current_stored_root`.
59+
60+
### Vulnerable Pattern
61+
```rust
62+
// ❌ No old-root validation
63+
pub fn apply_batch(env: Env, old_root: BytesN<32>, new_root: BytesN<32>, proof: Proof) {
64+
verify_zk_proof(&env, proof, &[old_root, new_root]);
65+
66+
// ❌ Never checks: old_root == get_current_root()
67+
set_root(&env, new_root); // State manipulation possible!
68+
}
69+
```
70+
71+
### Secure Pattern
72+
```rust
73+
// ✅ Old-root validation
74+
pub fn apply_batch(env: Env, old_root: BytesN<32>, new_root: BytesN<32>, proof: Proof) {
75+
let current_root = get_current_root(&env);
76+
77+
if old_root != current_root {
78+
panic!("Old root mismatch - invalid state transition");
79+
}
80+
81+
verify_zk_proof(&env, proof, &[old_root, new_root]);
82+
set_root(&env, new_root); // Safe state transition
83+
}
84+
```
85+
86+
### Implementation Effort
87+
**3-4 days** (after #1192, #1194)
88+
89+
---
90+
91+
## Issue #1208: Z012 - Public-Output Over-Exposure
92+
93+
### Goal
94+
Detect circuits exposing more public outputs than necessary, potentially leaking private information.
95+
96+
### Vulnerable Pattern
97+
```rust
98+
// ❌ Over-broad public outputs
99+
pub fn verify_age_proof(env: Env, proof: Proof, public_inputs: Vec<u64>) {
100+
// Public inputs: [age, birthdate, ssn_last4, is_over_21]
101+
// ❌ Exposes age, birthdate, SSN - only need is_over_21!
102+
103+
verify_zk_proof(&env, proof, &public_inputs);
104+
105+
let is_over_21 = public_inputs[3];
106+
if is_over_21 != 1 {
107+
panic!("Age verification failed");
108+
}
109+
}
110+
```
111+
112+
### Secure Pattern
113+
```rust
114+
// ✅ Minimal public outputs
115+
pub fn verify_age_proof(env: Env, proof: Proof, is_over_21: u64) {
116+
// Public inputs: [is_over_21] only
117+
// ✅ Private: age, birthdate, SSN stay in circuit
118+
119+
verify_zk_proof(&env, proof, &[is_over_21]);
120+
121+
if is_over_21 != 1 {
122+
panic!("Age verification failed");
123+
}
124+
}
125+
```
126+
127+
### Detection (Heuristic)
128+
- Count public inputs vs expected minimal set
129+
- Flag if > expected (advisory, not hard block)
130+
- Requires manual review confirmation
131+
132+
### Implementation Effort
133+
**3-4 days** (after #1192, #1194)
134+
135+
---
136+
137+
## Issue #1204: Z008 - Curve/Field Mismatch
138+
139+
### Goal
140+
Cross-check elliptic curve parameters between on-chain verifier and off-chain circuit, flag mismatches (e.g., BN254 verifier with BLS12-381 circuit).
141+
142+
### Vulnerable Pattern
143+
```rust
144+
// Circuit: uses BLS12-381 (circom default)
145+
// Verifier contract: configured for BN254
146+
pub fn verify(env: Env, proof: Proof) {
147+
// ❌ Curve mismatch - proof unverifiable or exploitable
148+
bn254_verify(&env, proof); // Wrong curve!
149+
}
150+
```
151+
152+
### Detection (Cross-File)
153+
1. Extract curve from circuit source (circom/Noir config)
154+
2. Extract curve from verifier contract type parameters
155+
3. Compare curves across project
156+
4. Flag mismatches
157+
158+
### Dependencies
159+
- **#1227**: Circom parser
160+
- **#1228**: Noir parser
161+
- **#1229**: Arkworks config parser
162+
- **#1192, #1194**: ZK infrastructure
163+
164+
### Implementation Effort
165+
**4-5 days** (after all parser dependencies)
166+
167+
---
168+
169+
## Implementation Timeline
170+
171+
**Week 1**: Z014 (merkle) + Z013 (rollup)
172+
**Week 2**: Z012 (privacy heuristic)
173+
**Week 3+**: Z008 (deferred until parsers ready)
174+
175+
**Total**: 2-3 weeks for Z014/Z013/Z012, plus 1 week for Z008 later
176+
177+
---
178+
179+
## Dependencies
180+
181+
- **#1192, #1194**: All rules
182+
- **#1227, #1228, #1229**: Z008 only (cross-file parsing)
183+
- **#1220**: Rollup fixture for Z013
184+
- **#1221**: Merkle fixture for Z014
185+
186+
---
187+
188+
## Success Criteria
189+
190+
### Z014
191+
- [ ] Detects missing merkle root comparison
192+
- [ ] Does not flag correct comparison
193+
- [ ] Snapshot tests pass
194+
195+
### Z013
196+
- [ ] Detects missing old-root validation
197+
- [ ] Rollup pattern recognized
198+
- [ ] Snapshot tests pass
199+
200+
### Z012
201+
- [ ] Flags over-broad public inputs (heuristic)
202+
- [ ] Advisory message clear
203+
- [ ] Snapshot tests pass
204+
205+
### Z008
206+
- [ ] Cross-file curve extraction works
207+
- [ ] Mismatch detection accurate
208+
- [ ] Snapshot tests pass
209+
210+
---
211+
212+
**Document Version**: 1.0
213+
**Next Review**: Upon #1192, #1194 completion

docs/rules/Z008.md

Lines changed: 61 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,76 @@
1-
# Z008Curve / Field Mismatch Between On-Chain Verifier and Off-Chain Circuit
1+
# Z008: Curve/Field Mismatch Between Verifier and Circuit
22

3-
**Category:** zk-circuit-constraints
4-
**Severity:** Critical
5-
**Rule name:** `curve_field_mismatch`
3+
## Severity
4+
**CRITICAL** - Makes all proofs unverifiable or exploitable
65

7-
---
6+
## Description
7+
Cross-checks elliptic curve/field parameters between on-chain Rust verifier and off-chain circuit definition, flagging mismatches (e.g., BN254 verifier with BLS12-381 circuit).
88

9-
## What it detects
10-
11-
A verifier contract that accepts proofs without asserting the curve or field identifier matches the one the off-chain circuit was compiled for. Detected as: no curve/field constant or identifier checked in the verifier entry point.
12-
13-
---
14-
15-
## Why it matters
16-
17-
A Groth16 proof generated over BN254 is entirely different from one generated over BLS12-381. Supplying a proof from the wrong curve to a verifier does not just fail — on some native implementations it may pass due to degenerate group elements or cause a panic that halts the contract. More critically, in multi-verifier systems where an attacker controls circuit compilation, they can redirect the verifier to a weaker curve for which they can forge proofs.
18-
19-
---
20-
21-
## Vulnerable example
9+
## Vulnerable Pattern
2210

2311
```rust
24-
pub fn verify(env: Env, proof: Vec<u8>, inputs: Vec<u64>) -> bool {
25-
// Z008: no curve identifier check — proof from any curve is accepted.
26-
groth16_verify_bn254(&proof, &inputs)
12+
// Circuit config (circom): uses BLS12-381
13+
// pragma circom 2.0.0;
14+
// Uses default BLS12-381 curve
15+
16+
// Verifier contract: configured for BN254
17+
use ark_bn254::Bn254;
18+
19+
pub fn verify_proof(env: Env, proof: Proof) {
20+
// ❌ Curve mismatch!
21+
// Circuit generates BLS12-381 proofs
22+
// Verifier expects BN254 format
23+
// Result: All proofs fail OR exploitable ambiguity
24+
bn254_pairing_check(&env, proof);
2725
}
2826
```
2927

30-
---
28+
**Impact**: System completely broken or security compromised.
3129

32-
## Safe example
30+
## Secure Pattern
3331

3432
```rust
35-
const CURVE_ID: u32 = 254; // BN254
33+
// Circuit config: BN254 specified
34+
// pragma circom 2.0.0;
35+
// include "bn254/...";
36+
37+
// Verifier contract: BN254
38+
use ark_bn254::Bn254;
3639

37-
pub fn verify(env: Env, curve_id: u32, proof: Vec<u8>, inputs: Vec<u64>) -> bool {
38-
assert_eq!(curve_id, CURVE_ID, "unsupported curve");
39-
groth16_verify_bn254(&proof, &inputs)
40+
pub fn verify_proof(env: Env, proof: Proof) {
41+
// ✅ Curves match
42+
bn254_pairing_check(&env, proof);
4043
}
4144
```
4245

43-
---
44-
45-
## References
46-
47-
- [Zcash Sapling curve mismatch (CVE-2019-7167)](https://nvd.nist.gov/vuln/detail/CVE-2019-7167)
48-
- [Z008 implementation issue #1204](https://github.com/HyperSafeD/Sanctifier/issues/1204)
46+
## Why This Matters
47+
- **Total failure**: No proofs verify correctly
48+
- **Subtle bugs**: May work in tests, fail in production
49+
- **Security holes**: Curve confusion can be exploitable
50+
- **Hard to debug**: Mismatch not obvious
51+
52+
## Detection Method (Cross-File)
53+
1. Parse circuit source (circom/Noir/arkworks)
54+
2. Extract declared curve (BN254, BLS12-381, etc.)
55+
3. Parse verifier contract
56+
4. Extract verifier curve type parameter
57+
5. Compare curves across project
58+
6. Flag mismatches
59+
60+
### Common Curves
61+
- **BN254**: Most common for Ethereum/Soroban
62+
- **BLS12-381**: Default in some circom configs
63+
- **BLS12-377**: Used in some systems
64+
- **Curve25519**: Different purpose (signing)
65+
66+
## Dependencies
67+
- **#1227**: Circom parser (BLOCKS THIS RULE)
68+
- **#1228**: Noir parser
69+
- **#1229**: Arkworks config parser
70+
- **#1192, #1194**: ZK infrastructure
71+
72+
## Note
73+
This rule requires whole-project/workspace scanning across multiple file types.
74+
75+
## Examples
76+
See fixtures after #1227, #1228, #1229 integration.

0 commit comments

Comments
 (0)