Skip to content

Commit 26ad482

Browse files
authored
Merge pull request #433 from all-opensource-projects/structural-guards-for-proof-vk-and-public-input-shapes
feat: Add structural guards for proof, VK, and public input validation (ZK-075)
2 parents d49121d + 9929399 commit 26ad482

9 files changed

Lines changed: 1559 additions & 10 deletions

File tree

STRUCTURAL_GUARDS_QUICK_REF.md

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
# Structural Guards Quick Reference (ZK-075)
2+
3+
## What Are Structural Guards?
4+
5+
Structural guards validate byte lengths and vector counts BEFORE deserializing elliptic curve points or touching cryptographic operations. They ensure malformed payloads fail fast with explicit errors.
6+
7+
## Expected Byte Lengths
8+
9+
```
10+
G1 Point: 64 bytes
11+
G2 Point: 128 bytes
12+
Field Element: 32 bytes
13+
14+
Proof:
15+
A (G1): 64 bytes
16+
B (G2): 128 bytes
17+
C (G1): 64 bytes
18+
Total: 256 bytes
19+
20+
VK:
21+
alpha_g1: 64 bytes
22+
beta_g2: 128 bytes
23+
gamma_g2: 128 bytes
24+
delta_g2: 128 bytes
25+
IC vector: 9 points × 64 bytes = 576 bytes
26+
27+
Public Inputs: 8 fields × 32 bytes = 256 bytes
28+
```
29+
30+
## Contract Usage (Rust)
31+
32+
### Automatic Validation
33+
34+
```rust
35+
use crate::crypto::verifier::verify_proof;
36+
37+
// Structural guards run automatically in verify_proof()
38+
let result = verify_proof(&env, &vk, &proof, &pub_inputs);
39+
40+
match result {
41+
Err(Error::MalformedProofA) => {
42+
// Proof A has wrong length
43+
}
44+
Err(Error::VkIcVectorWrongLength) => {
45+
// VK IC vector doesn't have 9 points
46+
}
47+
Err(Error::PublicInputWrongLength) => {
48+
// A public input field has wrong length
49+
}
50+
Ok(valid) => {
51+
// Structural validation passed, check pairing result
52+
}
53+
_ => {}
54+
}
55+
```
56+
57+
### Error Codes
58+
59+
```rust
60+
// Proof errors
61+
Error::MalformedProofA // A is not 64 bytes
62+
Error::MalformedProofB // B is not 128 bytes
63+
Error::MalformedProofC // C is not 64 bytes
64+
65+
// VK errors
66+
Error::VkAlphaG1WrongLength // alpha_g1 is not 64 bytes
67+
Error::VkBetaG2WrongLength // beta_g2 is not 128 bytes
68+
Error::VkGammaG2WrongLength // gamma_g2 is not 128 bytes
69+
Error::VkDeltaG2WrongLength // delta_g2 is not 128 bytes
70+
Error::VkIcVectorWrongLength // IC vector doesn't have 9 points
71+
Error::VkIcPointWrongLength // An IC point is not 64 bytes
72+
73+
// Public input errors
74+
Error::PublicInputWrongLength // A field is not 32 bytes
75+
```
76+
77+
## SDK Usage (TypeScript)
78+
79+
### Validate Proof
80+
81+
```typescript
82+
import { validateProofStructure } from "./structural_guards";
83+
84+
try {
85+
validateProofStructure(proofBytes);
86+
// Proof structure is valid
87+
} catch (error) {
88+
// Proof has wrong length
89+
console.error("Invalid proof structure:", error.message);
90+
}
91+
```
92+
93+
### Validate VK
94+
95+
```typescript
96+
import { validateVkStructure } from "./structural_guards";
97+
98+
const vk = {
99+
alpha_g1: new Uint8Array(64),
100+
beta_g2: new Uint8Array(128),
101+
gamma_g2: new Uint8Array(128),
102+
delta_g2: new Uint8Array(128),
103+
gamma_abc_g1: [
104+
/* 9 points of 64 bytes each */
105+
],
106+
};
107+
108+
try {
109+
validateVkStructure(vk);
110+
// VK structure is valid
111+
} catch (error) {
112+
// VK has structural issues
113+
console.error("Invalid VK structure:", error.message);
114+
}
115+
```
116+
117+
### Validate Public Inputs
118+
119+
```typescript
120+
import {
121+
validatePublicInputsStructure,
122+
validatePublicInputsHexStructure,
123+
} from "./structural_guards";
124+
125+
// Validate byte arrays
126+
const inputs = [
127+
/* 8 Uint8Array of 32 bytes each */
128+
];
129+
validatePublicInputsStructure(inputs);
130+
131+
// Validate hex strings
132+
const hexInputs = [
133+
/* 8 strings of 64 hex chars each */
134+
];
135+
validatePublicInputsHexStructure(hexInputs);
136+
```
137+
138+
### Extract Proof Components
139+
140+
```typescript
141+
import { extractProofComponents } from "./structural_guards";
142+
143+
const proof = new Uint8Array(256);
144+
const { a, b, c } = extractProofComponents(proof);
145+
146+
console.log("A:", a.length); // 64
147+
console.log("B:", b.length); // 128
148+
console.log("C:", c.length); // 64
149+
```
150+
151+
## Common Errors and Fixes
152+
153+
### Proof Too Short/Long
154+
155+
```
156+
Error: Proof must be 256 bytes (64 + 128 + 64), got 255
157+
Fix: Ensure proof contains all three components (A, B, C)
158+
```
159+
160+
### VK IC Vector Wrong Length
161+
162+
```
163+
Error: VK gamma_abc_g1 must have 9 points (IC[0] + 8 inputs), got 8
164+
Fix: IC vector needs IC[0] plus one point per public input
165+
```
166+
167+
### Public Input Wrong Length
168+
169+
```
170+
Error: Public input[3] must be 32 bytes, got 16
171+
Fix: All public inputs must be 32-byte field elements
172+
```
173+
174+
### VK Point Wrong Length
175+
176+
```
177+
Error: VK alpha_g1 must be 64 bytes, got 32
178+
Fix: G1 points are 64 bytes, G2 points are 128 bytes
179+
```
180+
181+
## Testing
182+
183+
### Contract Tests
184+
185+
```bash
186+
# Run structural guard tests
187+
cargo test structural_guards
188+
189+
# Run specific test
190+
cargo test test_proof_a_wrong_length_rejected
191+
```
192+
193+
### SDK Tests
194+
195+
```bash
196+
# Run structural guard tests
197+
npm test structural_guards
198+
199+
# Run specific test
200+
npm test -- -t "should reject proof that is too short"
201+
```
202+
203+
## Constants
204+
205+
### Contract (Rust)
206+
207+
```rust
208+
const G1_POINT_BYTE_LENGTH: u32 = 64;
209+
const G2_POINT_BYTE_LENGTH: u32 = 128;
210+
const FIELD_ELEMENT_BYTE_LENGTH: u32 = 32;
211+
const EXPECTED_PUBLIC_INPUT_COUNT: u32 = 8;
212+
const EXPECTED_IC_VECTOR_LENGTH: u32 = 9;
213+
```
214+
215+
### SDK (TypeScript)
216+
217+
```typescript
218+
export const G1_POINT_BYTE_LENGTH = 64;
219+
export const G2_POINT_BYTE_LENGTH = 128;
220+
export const FIELD_ELEMENT_BYTE_LENGTH = 32;
221+
export const EXPECTED_PUBLIC_INPUT_COUNT = 8;
222+
export const EXPECTED_IC_VECTOR_LENGTH = 9;
223+
export const GROTH16_PROOF_TOTAL_LENGTH = 256;
224+
```
225+
226+
## Validation Order
227+
228+
Structural guards run in this order:
229+
230+
1. **Proof Structure**
231+
- Check A length (64 bytes)
232+
- Check B length (128 bytes)
233+
- Check C length (64 bytes)
234+
235+
2. **VK Structure**
236+
- Check alpha_g1 length (64 bytes)
237+
- Check beta_g2 length (128 bytes)
238+
- Check gamma_g2 length (128 bytes)
239+
- Check delta_g2 length (128 bytes)
240+
- Check IC vector length (9 points)
241+
- Check each IC point length (64 bytes)
242+
243+
3. **Public Inputs Structure**
244+
- Check input count (8 fields)
245+
- Check each field length (32 bytes)
246+
247+
4. **Cryptographic Operations**
248+
- Deserialize curve points
249+
- Compute linear combination
250+
- Perform pairing check
251+
252+
## Best Practices
253+
254+
1. **Always validate before deserialization**
255+
- Structural guards should run first
256+
- Prevents expensive operations on bad data
257+
258+
2. **Use specific error codes**
259+
- Don't catch all errors generically
260+
- Handle structural errors differently from crypto errors
261+
262+
3. **Validate early in the pipeline**
263+
- SDK should validate before sending to contract
264+
- Contract validates again for defense in depth
265+
266+
4. **Test malformed payloads**
267+
- Include structural guard tests in your test suite
268+
- Test all error paths
269+
270+
5. **Log structural errors**
271+
- Structural errors indicate bugs or attacks
272+
- Log them for debugging and security monitoring
273+
274+
## See Also
275+
276+
- [ZK-075_IMPLEMENTATION_SUMMARY.md](./ZK-075_IMPLEMENTATION_SUMMARY.md) - Full implementation details
277+
- [contracts/privacy_pool/src/test/structural_guards.rs](./contracts/privacy_pool/src/test/structural_guards.rs) - Contract tests
278+
- [sdk/src/structural_guards.test.ts](./sdk/src/structural_guards.test.ts) - SDK tests

0 commit comments

Comments
 (0)