Era

ZKsync
FoundryLayer 2
500,000 USDC
View results
Submission Details
Severity: low
Valid

`EcPairing.yul` - Passed wrong parameter to `g2JacobianDouble` in `g2ScalarMul` implementation, should pass `zp1` instead of `yp1`

Summary

In EcPairing.yul, the final parameter to g2JacobianDouble is wrong in g2ScalarMul implementation when scalar is 0x02. It should pass zp1 instead of yp1.

Vulnerability Details

EcPairing.yul is part of the precompiled special-purpose circuits, which consist the main component of ZKsyncVM and it is specifically designed for elliptic curve pairing operations.

It contains many functions to perform computationally intensive operations including g2IsInSubGroup, g2IsInSubGroupNaive which are used to check if a G2 point in jacobian coordinates is in the subgroup of the twisted curve.
Both functions use g2ScalarMul to multiply a G2 point represented in jacobian coordinates by a scalar, which internally uses g2JacobianDouble function to calculate a jacobian double. There's an issue with the final parameter to g2JacobianDouble when scalar is 0x02. Below is the definition of g2ScalarMul function:

function g2ScalarMul(xp0, xp1, yp0, yp1, zp0, zp1, scalar) -> xr0, xr1, yr0, yr1, zr0, zr1 {
let scalarBitIndex := bitLen(scalar)
switch scalar
case 0x02 {
>> xr0, xr1, yr0, yr1, zr0, zr1 := g2JacobianDouble(xp0, xp1, yp0, yp1, zp0, yp1)
}
default {
xr0 := 0
xr1 := 0
yr0 := MONTGOMERY_ONE()
yr1 := 0
zr0 := 0
zr1 := 0
for {} scalarBitIndex {} {
scalarBitIndex := sub(scalarBitIndex, 1)
xr0, xr1, yr0, yr1, zr0, zr1 := g2JacobianDouble(xr0, xr1, yr0, yr1, zr0, zr1)
let bitindex := checkBit(scalarBitIndex, scalar)
if bitindex {
xr0, xr1, yr0, yr1, zr0, zr1 := g2JacobianAdd(xp0, xp1, yp0, yp1, zp0, zp1, xr0, xr1, yr0, yr1, zr0, zr1)
}
}
}
}

It passed yp1 as the final parameter which should be zp1 in regards to the definition of g2JacobianDouble function:

function g2JacobianDouble(xp0, xp1, yp0, yp1, zp0, zp1) -> xr0, xr1, yr0, yr1, zr0, zr1 {
...
}

This highlights the incorrect usage of a parameter within the g2ScalarMul function, leading to inaccurate Jacobian multiplication calculations, which would subsequently affect the outcomes of higher-level functions.

Impact

The incorrect parameter usage within the g2ScalarMul function in EcPairing.yul leads to inaccurate Jacobian multiplication calculations. This directly impacts the correctness of subgroup validation functions such as g2IsInSubGroup and g2IsInSubGroupNaive, which rely on g2ScalarMul to verify if a G2 point belongs to the subgroup of the twisted curve.

As a result, higher-level functions that depend on accurate elliptic curve pairing operations may produce invalid outcomes, potentially compromising the integrity and security of the ZKsyncVM’s cryptographic operations.

Tools Used

Manual Review

Recommendations

Update g2ScalarMul function to pass the correct parameter.

function g2ScalarMul(xp0, xp1, yp0, yp1, zp0, zp1, scalar) -> xr0, xr1, yr0, yr1, zr0, zr1 {
let scalarBitIndex := bitLen(scalar)
switch scalar
case 0x02 {
- xr0, xr1, yr0, yr1, zr0, zr1 := g2JacobianDouble(xp0, xp1, yp0, yp1, zp0, yp1)
+ xr0, xr1, yr0, yr1, zr0, zr1 := g2JacobianDouble(xp0, xp1, yp0, yp1, zp0, zp1)
}
default {
xr0 := 0
xr1 := 0
yr0 := MONTGOMERY_ONE()
yr1 := 0
zr0 := 0
zr1 := 0
for {} scalarBitIndex {} {
scalarBitIndex := sub(scalarBitIndex, 1)
xr0, xr1, yr0, yr1, zr0, zr1 := g2JacobianDouble(xr0, xr1, yr0, yr1, zr0, zr1)
let bitindex := checkBit(scalarBitIndex, scalar)
if bitindex {
xr0, xr1, yr0, yr1, zr0, zr1 := g2JacobianAdd(xp0, xp1, yp0, yp1, zp0, zp1, xr0, xr1, yr0, yr1, zr0, zr1)
}
}
}
}
Updates

Lead Judging Commences

inallhonesty Lead Judge 5 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Valid proof will be rejected once `case 0x02` code is triggered

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.