Skip to content

feat: Add structural guards for proof, VK, and public input validation (ZK-075)#433

Merged
ANAVHEOBA merged 2 commits intoANAVHEOBA:mainfrom
all-opensource-projects:structural-guards-for-proof-vk-and-public-input-shapes
May 1, 2026
Merged

feat: Add structural guards for proof, VK, and public input validation (ZK-075)#433
ANAVHEOBA merged 2 commits intoANAVHEOBA:mainfrom
all-opensource-projects:structural-guards-for-proof-vk-and-public-input-shapes

Conversation

@meshackyaro
Copy link
Copy Markdown
Contributor

Summary

This PR implements ZK-075: Structural Guards for Proof, VK, and Public Input Shapes. The verifier path now rejects malformed byte lengths, wrong IC counts, and impossible payload shapes BEFORE deserializing elliptic-curve points or touching pairing logic.

Closes #351

Changes Made

Contract Changes (Rust)

Added 8 new granular error codes for structural validation:

  • VkAlphaG1WrongLength (52) - VK alpha_g1 has wrong byte length (expected 64)
  • VkBetaG2WrongLength (53) - VK beta_g2 has wrong byte length (expected 128)
  • VkGammaG2WrongLength (54) - VK gamma_g2 has wrong byte length (expected 128)
  • VkDeltaG2WrongLength (55) - VK delta_g2 has wrong byte length (expected 128)
  • VkIcVectorWrongLength (56) - VK gamma_abc_g1 vector has wrong length (expected 9)
  • VkIcPointWrongLength (57) - VK gamma_abc_g1 contains a point with wrong byte length
  • PublicInputWrongLength (63) - Public input field has wrong byte length (expected 32)

Added three structural validation functions in verifier.rs:

  1. validate_proof_structure()

    • Validates G1 point A is 64 bytes
    • Validates G2 point B is 128 bytes
    • Validates G1 point C is 64 bytes
    • Returns specific error for each malformed component
  2. validate_vk_structure()

    • Validates alpha_g1 is 64 bytes
    • Validates beta_g2, gamma_g2, delta_g2 are 128 bytes each
    • Validates gamma_abc_g1 has exactly 9 elements (IC[0] + 8 public inputs)
    • Validates each IC point is 64 bytes
  3. validate_public_inputs_structure()

    • Validates all 8 public input fields are exactly 32 bytes

Updated verify_proof() to call all three validation functions BEFORE any cryptographic operations.

Added comprehensive test suite (structural_guards.rs) with 20+ tests covering:

  • Proof structure validation (wrong A, B, C lengths)
  • VK structure validation (wrong alpha, beta, gamma, delta lengths)
  • IC vector validation (too short, too long, empty, wrong point lengths)
  • Public inputs validation (wrong field lengths)
  • Multiple structural errors (first error reported)
  • Valid structures passing guards

SDK Changes (TypeScript)

Created structural_guards.ts module with:

Constants:

  • G1_POINT_BYTE_LENGTH = 64
  • G2_POINT_BYTE_LENGTH = 128
  • FIELD_ELEMENT_BYTE_LENGTH = 32
  • EXPECTED_PUBLIC_INPUT_COUNT = 8
  • EXPECTED_IC_VECTOR_LENGTH = 9
  • GROTH16_PROOF_TOTAL_LENGTH = 256

Functions:

  1. validateProofStructure(proof: Uint8Array)

    • Validates proof is exactly 256 bytes (64 + 128 + 64)
    • Throws WitnessValidationError on malformed proof
  2. validateVkStructure(vk: VerifyingKeyStructure)

    • Validates all VK curve points have correct byte lengths
    • Validates IC vector has exactly 9 points
    • Validates each IC point is 64 bytes
  3. validatePublicInputsStructure(publicInputs: Uint8Array[])

    • Validates exactly 8 public inputs
    • Validates each input is 32 bytes
  4. validatePublicInputsHexStructure(publicInputs: string[])

    • Validates hex string format (64 hex chars = 32 bytes)
    • Validates hex characters are valid
  5. extractProofComponents(proof: Uint8Array)

    • Extracts A, B, C components from raw proof bytes
    • Validates structure before extraction

Created comprehensive test suite (structural_guards.test.ts) with 30+ tests covering all validation functions.

Updated witness.ts to use validateProofStructure() from structural guards module.

Documentation

  • ZK-075_IMPLEMENTATION_SUMMARY.md - Complete implementation details, error mapping, testing guide
  • STRUCTURAL_GUARDS_QUICK_REF.md - Quick reference for developers with examples and common errors

Validation Flow

Before ZK-075

verify_proof() → deserialize points → pairing check
                 ↑ Malformed data discovered here

After ZK-075

verify_proof() → structural guards → deserialize points → pairing check
                 ↑ Malformed data discovered here (FAST)

Benefits

Early Failure: Malformed payloads fail before expensive cryptographic operations
Explicit Errors: Specific error codes identify exactly which component is malformed
Consistent Validation: Same invariants enforced in both contract and SDK
Performance: Structural checks are O(1) vs. O(n) for curve operations
Security: Prevents malformed data from reaching cryptographic code
Debuggability: Clear error messages help developers identify issues quickly

Expected Byte Lengths

Component Type Bytes
G1 Point Curve point 64
G2 Point Curve point 128
Field Element Scalar 32
Proof A G1 Point 64
Proof B G2 Point 128
Proof C G1 Point 64
Total Proof A + B + C 256
VK IC vector 9 × G1 576
Public Inputs 8 × Field 256

Acceptance Criteria

✅ Malformed proof or VK structures fail with explicit pre-verification errors
✅ Contract and SDK tests cover short, long, and count-mismatch payloads
✅ Verifier code is no longer the first place malformed data is discovered
✅ Structural guards run before any elliptic curve deserialization
✅ Error messages clearly identify which component is malformed

Testing

Contract Tests

cd contracts/privacy_pool
cargo test structural_guards  # Run structural guard tests
cargo test                     # Run all tests

SDK Tests

cd sdk
npm test structural_guards     # Run structural guard tests
npm test                       # Run all tests

Files Changed

Contract Files (4)

  • contracts/privacy_pool/src/types/errors.rs - Added 8 new error codes
  • contracts/privacy_pool/src/crypto/verifier.rs - Added 3 validation functions
  • contracts/privacy_pool/src/test/structural_guards.rs - NEW: 20+ tests
  • contracts/privacy_pool/src/test/mod.rs - Added structural_guards module

SDK Files (3)

  • sdk/src/structural_guards.ts - NEW: Validation functions and constants
  • sdk/src/structural_guards.test.ts - NEW: 30+ tests
  • sdk/src/witness.ts - Updated to use structural guards

Documentation Files (2)

  • ZK-075_IMPLEMENTATION_SUMMARY.md - NEW: Complete implementation details
  • STRUCTURAL_GUARDS_QUICK_REF.md - NEW: Quick reference guide

Performance Impact

Structural guards add minimal overhead:

  • Contract: ~3 length checks + 1 vector iteration = O(n) where n=9
  • SDK: ~3 length checks + 1 array iteration = O(n) where n=9
  • Benefit: Avoids expensive elliptic curve deserialization on malformed data

Estimated savings on malformed payload:

  • Without guards: Full deserialization attempt + potential panic/error
  • With guards: Simple length check (< 1% of deserialization cost)

Migration Notes

No breaking changes - this is purely additive validation. Existing valid payloads continue to work unchanged.

Wave Issue Key: ZK-075

…n (ZK-075)

- Added pre-deserialization validation for all proof, VK, and public input components
- Validates byte lengths and vector counts before touching cryptographic operations
- Added 8 new granular error codes for structural validation failures
- Implemented structural guards in both contract (Rust) and SDK (TypeScript)
- Added comprehensive test suites with 20+ contract tests and 30+ SDK tests
- Malformed payloads now fail fast with explicit errors before expensive operations

Contract changes:
- validate_proof_structure(): Checks A (64B), B (128B), C (64B)
- validate_vk_structure(): Checks all VK points and IC vector (9 points)
- validate_public_inputs_structure(): Checks all 8 fields (32B each)

SDK changes:
- validateProofStructure(): Validates 256-byte proof format
- validateVkStructure(): Validates all VK components and IC vector
- validatePublicInputsStructure(): Validates byte arrays and hex strings
- extractProofComponents(): Safely extracts A, B, C from proof bytes

Wave Issue Key: ZK-075
… structural guards

Resolved conflicts in:
- contracts/privacy_pool/src/crypto/verifier.rs: Combined ZK-074 and ZK-075 validations
- contracts/privacy_pool/src/types/errors.rs: Merged error codes, renumbered to avoid conflicts

Both validation layers now work together:
- ZK-075 structural guards validate byte lengths and vector counts
- ZK-074 metadata validation checks circuit IDs and public input counts
@ANAVHEOBA ANAVHEOBA merged commit 26ad482 into ANAVHEOBA:main May 1, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ZK-075: Add structural guards for proof, VK, and public-input shapes before BN254 parsing

2 participants