Curve-Point Validation Failure / Malformed Proof-Element Acceptance
- Name: curve-point validation failure / malformed proof-element acceptance
- Category: exploit pattern / proof verifier implementation failure / elliptic-curve input validation failure / malformed-proof acceptance
- Summary: A proof verifier can be mathematically sound on paper and still fail catastrophically if its implementation accepts malformed elliptic-curve points, malformed field elements, or identity-like encodings where the protocol expected well-formed proof objects. The recurring mistake is not
bad proving system designbutbad admission control on the proof elements the verifier is willing to process. Once off-curve points, the point at infinity, zero encodings, or unchecked scalars cross that boundary, pairing logic, point arithmetic, batch normalization, or downstream application code can start proving things the protocol never meant to authenticate. - What it is:
- A zk or commitment verifier receives proof-supplied elliptic-curve points, commitments, openings, or scalar values.
- The protocol assumes those objects are well formed: on the right curve, in the right subgroup, not the identity unless explicitly allowed, and encoded inside the expected field ranges.
- The implementation fails to enforce one or more of those assumptions before continuing with verification.
- Malformed inputs then change the meaning of pairing checks, point arithmetic, or note-validation logic.
- The verifier may directly accept an invalid proof, or it may feed false success into a downstream spend / withdrawal / mint path that turns verifier confusion into fund loss.
- Typical preconditions:
- Proof verification code deserializes attacker-controlled points or integers from calldata, witness blobs, or proof transcripts.
- The implementation depends on helper routines for
isOnCurve, subgroup checks, non-zero / non-infinity checks, scalar bounds, or commitment-shape validation. - Some of those checks are absent, incomplete, bypassable, or accidentally not called.
- Downstream code treats verifier success as authority to move funds, accept spends, consume notes, or mutate state with economic meaning.
- The verifier or surrounding application does not fail closed on weird edge cases like
(0,0), zero inversions, invalid affine/projective conversions, or default-map values.
- Common vulnerable surfaces:
- verifier entrypoints that deserialize G1 / G2 points from user-controlled proof bytes
- note or commitment validators that should reject malformed curve points before proof arithmetic
- pairing wrappers that behave unexpectedly on identity or off-curve encodings
- batch-normalization code paths that let one malformed point corrupt other points in the same batch
- modular-inversion helpers that silently treat
0as invertible or otherwise continue after undefined arithmetic - codebases with validation helpers present but not actually called in the deployed path
- downstream note-ownership or spend paths that interpret failed cryptographic recovery as
address(0)and accidentally align that with default storage values
- Exploit sequence:
- The attacker identifies a verifier or spend path that accepts attacker-controlled proof elements or note commitments.
- They craft malformed points, zero encodings, identity-like objects, or out-of-range scalars that the implementation should have rejected.
- Because well-formedness checks are missing, bypassed, or not called, those objects enter pairing or point-arithmetic logic anyway.
- The malformed objects collapse part of the verification equation, poison normalization state, or otherwise make invalid proof material look acceptable.
- The verifier returns success or passes a downstream gate that should have remained closed.
- A consumer path such as spend validation, note redemption, or withdrawal logic monetizes that false proof success.
- Variants:
- Skipped validation subroutine — the code has the right
validateCommitments/isOnCurvehelper, but the deployed path never calls it, as in the AZTEC v1 demo exploit. - Identity-point acceptance — the verifier treats
(0,0)or another identity-like encoding as an acceptable proof element and later pairing logic interprets it as a harmless neutral element. - Point-at-infinity misclassification — the implementation performs the wrong infinity check or uses an encoding rule the rest of the verifier does not share.
- Batch-normalization corruption — one malformed point with
z = 0or equivalent state poisons affine normalization for neighboring points. - Scalar / field-range admission failure — the verifier accepts proof integers outside the intended field or subgroup assumptions and later arithmetic reduces them into a meaning the caller never explicitly authenticated.
- Composite verifier-plus-application exploit — malformed-proof acceptance only becomes theft because a second downstream authorization surface also fails closed badly, such as zero-address ownership checks.
- Skipped validation subroutine — the code has the right
Three proof-element admission promotions healthy systems should not collapse
-
A proof-shaped point, commitment, scalar, or note object exists at all
- Some attacker-supplied object was successfully parsed, deserialized, or otherwise made available to the verifier path.
- This is only the raw-object-existence question:
bytes decoded into something the runtime can carry around.
-
That object counts as an admissible proof element for this exact verifier and object type
- This is the step vulnerable systems skip over. They quietly promote
some curve-looking or proof-looking object existsintothis is a well-formed point / scalar / commitment that deserves to enter verification arithmetic. - The Aztec Plonk
W_z/W_zwzero-point case is the clean preserved exemplar: the dangerous jump was not merely that a zero value existed, but that it survived long enough to inherit point-at-infinity semantics inside the live verifier path.
- This is the step vulnerable systems skip over. They quietly promote
-
That admitted proof element still deserves downstream authority here
- Even once an object looks admissible to the verifier, the protocol still has to decide what downstream ownership, note, withdrawal, or release authority may inherit from that success.
- The AZTEC v1 demo case preserves this extra step: malformed commitments first crossed a skipped admission gate, then a separate zero-address / missing-owner alias let that parser-side failure become real token-withdrawal authority.
- Detection heuristics:
- Trace every proof-supplied point from calldata to its first arithmetic use and ask exactly where
on curve,in subgroup,not infinity, and range assumptions become machine-checked. - Fuzz verifiers with
(0,0), infinity encodings, off-curve points, low-order points, and out-of-range scalars. - Confirm validation helpers are not only present in source but actually executed in the deployed path.
- Review batch affine-normalization and inversion helpers with the same suspicion as the pairing equation itself.
- Look for application code that maps failed cryptographic recovery, invalid note lookup, or absent owner state to a default value that can accidentally match another default.
- Treat
proof parserandcurve-point admission logicas part of the verifier, not as harmless serialization glue.
- Trace every proof-supplied point from calldata to its first arithmetic use and ask exactly where
- Defenses:
- Centralize proof-element admission: reject malformed points and scalars before any arithmetic or pairing call.
- Enforce explicit checks for curve membership, subgroup membership, and non-identity / non-infinity status wherever the protocol requires them.
- Make impossible states impossible at the application layer too: failed signature recovery, nonexistent note ownership, or invalid proof elements should never alias to a usable default like
address(0). - Add regression tests for malformed proof objects, especially zero encodings and identity-like points.
- Keep deserialization, validation, and arithmetic boundaries simple enough that reviewers can point to one line proving malformed objects are dead on arrival.
What sound design looks like
- Healthy systems keep three facts separate on purpose:
a proof-shaped object exists,that object is an admissible proof element for this verifier, andthat admitted object still deserves downstream authority here. - A healthy verifier has one explicit admission boundary for proof elements. Before any pairing or commitment logic runs, the implementation should already have proven
these points exist on the intended curve, in the intended subgroup, with the intended identity semantics. - Proof-element admission boundary discipline is the positive-design companion for this family: malformed proof objects should die at one parser / validator boundary, and downstream consumers should never be able to reinterpret failure-shaped values like
address(0)into spend authority. - Application logic should not be able to rescue or reinterpret malformed proof objects into something spendable. If proof validation fails, every downstream ownership, note, or withdrawal path should still stay shut.
- Reviewers should be able to answer two crisp questions, not one fuzzy one:
where do malformed curve points die before arithmetic begins?andwhat downstream default value, if any, could still inherit authority even after that boundary? - The clean positive property is not merely
pairing equation looks rightbutonly well-formed proof objects are ever allowed to participate in that equation, and only non-failure-shaped results can later reach spend authority. - High-signal incidents:
- [[../../../incidents/proofs-verifiers/aztec-plonk-cpp-verifier-wz-wzw-zero-point-at-infinity-proof-forgery-near-miss-2021-12-14|Aztec Plonk C++ verifier
W_z/W_zwzero / point-at-infinity proof forgery near miss (2021-12-14)]] — a pure-verifier exemplar where malformed proof elements survived admission long enough to acquire point-at-infinity semantics inside pairing logic. - [[../../../incidents/proofs-verifiers/aztec-v1-poc-zkdai-commented-validatecommitments-ecrecover-zero-address-drain-2018-12-02|AZTEC v1 PoC / zkDAI demo commented
validateCommitments()+ zero-addressecrecoverdrain (2018-12-02)]] — malformed note / commitment material crossed a skipped validator, then a zero-address ownership check turned verifier admission failure into a real token withdrawal.
- [[../../../incidents/proofs-verifiers/aztec-plonk-cpp-verifier-wz-wzw-zero-point-at-infinity-proof-forgery-near-miss-2021-12-14|Aztec Plonk C++ verifier
Failure-prevention checklist
- Centralize proof-object deserialization and validation so every caller path hits the same
on-curve, subgroup, infinity, identity, and scalar-range checks before any arithmetic runs. - Keep the three promotions explicit in code and review:
object exists,object is admissible proof-element truth, andthat admitted object may still unlock downstream authority. Do not let any one of those stand in for the next by implication. - Reject malformed points before batch normalization, inversion, pairing precompiles, note parsing, or ownership recovery. Do not rely on downstream math to fail safely on impossible inputs.
- Make identity semantics explicit per object type. If a proof element, commitment, or note is never supposed to be the point at infinity or a zero-like encoding, encode that rule as a hard check rather than a comment.
- Treat zero-inversion,
(0,0), low-order points, and malformed compressed encodings as permanent regression tests, not just fuzzer curiosities. - Ensure validation helpers are actually wired into deployed execution paths. A perfect
validateCommitments()routine is useless if optimized, refactored, or commented out of the live flow. - Gate release on that malformed-input suite actually running. Proof-verifier deployments should fail closed if adversarial parser / point-validation tests are skipped, partially run, or lose coverage unexpectedly.
- Keep application-layer default values from becoming fallback authority. Failed recovery, missing note ownership, absent commitments, or malformed proof objects must never alias to spendable defaults like
address(0). - Review batch operations as one security surface. One malformed point should not be able to poison affine conversion, transcript state, or pairing inputs for neighboring objects in the same batch.
- Use libraries whose subgroup and curve-membership assumptions are explicit and tested against the exact encoding format you accept onchain.
- Add negative tests around parser boundaries, calldata length edges, and alternate encodings so malformed objects cannot slip past ABI or serialization assumptions before validation starts.
- Add downstream fail-closed tests too: even if a malformed object somehow survives farther than intended, ownership lookup, note spend logic, withdrawal paths, and release paths should still refuse to treat failure-shaped values as authority.
- Keep money-moving consumers behind independent blast-radius controls where practical. Withdrawal caps, pauses, or staged rollout help when proof-object admission bugs survive code review.
Audit prompts
- Where is the first line of code that proves each proof-supplied point is on the right curve, in the right subgroup, and not an unacceptable identity encoding?
- Can any arithmetic, normalization, inversion, hashing, or ownership recovery happen before malformed proof objects are rejected?
- Are validation helpers merely present in source, or can reviewers trace the exact deployed path that calls them for every entrypoint?
- Could release tooling still ship this verifier without running the malformed-input suite or after coverage on the admission path regressed? What exact deploy gate says no?
- What does the code do with
(0,0), the point at infinity, low-order points, off-curve encodings, zero scalars, and malformed compressed inputs? - Do batch-verification or batch-normalization paths let one malformed point corrupt or short-circuit checks for other otherwise unrelated proof elements?
- If cryptographic recovery or note lookup fails, what default value flows downstream, and could that value accidentally satisfy an authorization or withdrawal condition?
- Are subgroup checks real subgroup checks, or only curve-equation checks that still admit low-order points or equivalent malformed structure?
- Does the verifier trust a serialization library, precompile wrapper, or external helper whose identity semantics differ from the application’s own assumptions?
- What regression tests prove malformed proof objects remain dead on arrival across compiler upgrades, gas optimizations, refactors, and library swaps?
- If a malformed proof did get past admission, what secondary control would limit the payout or state corruption before a full drain?
Lessons that should already be standard
- The proof parser is part of the verifier. Treating deserialization and point-validation code as harmless glue is how mathematically sound systems end up authenticating malformed objects.
A proof-shaped object existsis weaker thanthat object is admissible verifier truth, which is weaker thanthat admitted object deserves downstream authority. Healthy systems keep those promotions separate on purpose.On curveis not enough. Subgroup membership, identity handling, and encoding rules are part of the security boundary too.- Default values are never neutral around cryptography.
address(0), zero commitments, or zero inversions become dangerous the moment malformed proof objects can steer execution into them. - A skipped validation helper is functionally equivalent to not having the check at all. Security-critical helpers need path-level tests, not just source-level existence.
- A local failing malformed-input test is not a control if release tooling can bypass it. Proof systems need deploy gates that fail closed when admission-boundary coverage is missing or not executed.
- Batch code deserves paranoia. The fastest path through point arithmetic is often where impossible inputs get the most surprising influence over neighboring state.
- Application consumers must still fail closed. Even if malformed proof objects should have died earlier, withdrawal and ownership code should not turn cryptographic weirdness into spend authority.
- A small malformed-input corpus can pay for itself many times over.
(0,0), infinity, low-order points, and bad scalars should live in every verifier team’s permanent regression suite.
Where this pattern stops
- Use verification-key-independence-collapse when the trusted setup or verification key itself makes forged proofs globally possible. That family is about collapsing soundness for all inputs; this family is about the verifier implementation misclassifying specific malformed proof elements.
- Use witness-public-input-binding-failure when the proof statement is underconstrained even for well-formed proof objects. Here the statement can be fine; the verifier just accepted malformed objects into it.
- Use proof-to-effects-binding-failure when the proof may be valid and well formed, but the downstream settlement consumer executes a different effect set than the proof authenticated.
- Use interface-conformance-semantic-compatibility-failure when the important lesson is a broader
object looked interface-compatible but meant something elsestory outside a proof verifier. - In short: this pattern begins when malformed proof elements are admitted into verification arithmetic, and it stops once the sharper story is instead toxic setup, an underbound proof statement, or a proof-valid-but-misconsumed settlement path.
Internal linkages
- Best current incident exemplars: aztec-plonk-cpp-verifier-wz-wzw-zero-point-at-infinity-proof-forgery-near-miss-2021-12-14, aztec-v1-poc-zkdai-commented-validatecommitments-ecrecover-zero-address-drain-2018-12-02.
- Positive-design companion: proof-element-admission-boundary-discipline.
- Adjacent setup-collapse family: verification-key-independence-collapse.
- Adjacent split-statement family: witness-public-input-binding-failure.
- Adjacent proof-consumer family: proof-to-effects-binding-failure.
Control surface
-
The leverage sits in proof deserialization, curve-point validation, subgroup / infinity checks, batch normalization, inversion helpers, and the first downstream code path that treats verifier success as spend authority.
-
The recurring mistake is to collapse
proof-looking object exists,object is admissible verifier truth, andthat admitted object still deserves spend authorityinto one fuzzy success condition. -
The healthy review stance is harsher: parser truth, admissible proof-element truth, and downstream authority truth each need their own fail-closed boundary.
-
Sources:
-
Last reviewed: 2026-07-30 UTC