Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimization of G2 subgroup check using NAF representation #266

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 58 additions & 7 deletions precompiles/EcPairing.yul
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ object "EcPairing" {
ret := 4965661367192848881
}

/// @notice Constant function for the alt_bn128 curve seed (parameter `x`).
/// @dev See https://eips.ethereum.org/EIPS/eip-196 for further details.
/// @return ret The alt_bn128 curve seed.
function X_NAF() -> ret {
// NAF in binary form
// 010000000100010000100001000100100000010001001000100010000100000001000001000100010010000100000100000000010001000000001000000001
ret := 21356084665891114007971320526050427393
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is correct.

}

/// @notice Constant function for decimal representation of the NAF for the Millers Loop.
/// @dev Millers loop uses to iterate the NAF representation of the value t = 6x^2. Where x = 4965661367192848881 is a parameter of the BN 256 curve.
Expand Down Expand Up @@ -451,6 +459,21 @@ object "EcPairing" {
ny0, ny1 := fp2Neg(y0, y1)
}

/// @notice Negates a G2 point in Jacobian coordinates.
/// @dev The coordinates are encoded in Montgomery form.
/// @dev The negation of a point (x, y, z) is (x, -y, z).
/// @param x0, x1 The x coordinate of the point.
/// @param y0, y1 The y coordinate of the point.
/// @param z0, z1 The z coordinate of the point.
/// @return nx0, nx1, ny0, ny1, nz0, nz1 The coordinates of the negated point.
function g2JacobianNeg(x0, x1, y0, y1, z0, z1) -> nx0, nx1, ny0, ny1, nz0, nz1 {
nx0 := x0
nx1 := x1
ny0, ny1 := fp2Neg(y0, y1)
nz0 := z0
nz1 := z1
}

/// @notice Constant function for the alt_bn128 returning `(xi)^ ((N - 1) // 2)`. Where `xi` is D-type twist param.
/// @dev See https://eprint.iacr.org/2022/352.pdf (2 Preliminaries) for further details.
/// @return ret Twisted curve `xi2 = (xi)^ ((N - 1) // 2)` value in Montgomery form.
Expand All @@ -467,6 +490,33 @@ object "EcPairing" {
xi1 := intoMontgomeryForm(10307601595873709700152284273816112264069230130616436755625194854815875713954)
}

/// @notice Multiplies a given G2 point by X in NAF form.
/// @dev The given G2 point is in Jacobian Coordinates coordinates and Montgomery Form.
/// @return ret G2 Point multiplied by X in Jacobian Coordinates and Montgomery Form.
function g2TimesXNAF(p00, p01, p10, p11, p20, p21) -> q00, q01, q10, q11, q20, q21 {
q00, q01, q10, q11, q20, q21 := G2_INFINITY()

let naf := X_NAF()
let n_iter := 63

for {let i := 0} lt(i, n_iter) { i := add(i, 1) } {
// naf digit = 1
if and(naf, 1) {
q00, q01, q10, q11, q20, q21 := g2JacobianAdd(q00, q01, q10, q11, q20, q21, p00, p01, p10, p11, p20, p21)
}

// naf digit = -1
if and(naf, 2) {
let pn00, pn01, pn10, pn11, pn20, pn21 := g2JacobianNeg(p00, p01, p10, p11, p20, p21)
q00, q01, q10, q11, q20, q21 := g2JacobianAdd(q00, q01, q10, q11, q20, q21, pn00, pn01, pn10, pn11, pn20, pn21)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may have better performance if iterating X_NAF from MSB to LSB. Then you add either constant P or constant -P and the later can be computed once.

Here is snippet from wikipedia:

   let bits = bit_representation(s) # the vector of bits (from LSB to MSB) representing s
   let i = length(bits) - 2
   let res = P
   while (i >= 0): # traversing from second MSB to LSB
       res = res + res # double
       if bits[i] == 1:            
           res = res + P # add
       i = i - 1
   return res

}

p00, p01, p10, p11, p20, p21 := g2JacobianDouble(p00, p01, p10, p11, p20, p21)

naf := shr(2, naf)
}
}

/// @notice Frobenius endomophism used to G2 sub group check for twisted curve.
/// @dev For more datail see https://eprint.iacr.org/2022/348.pdf
/// @param xp0, xp1 The x coordinate of the point on twisted curve.
Expand All @@ -489,16 +539,17 @@ object "EcPairing" {
/// @param xp0, xp1 The x coordinate of the point.
/// @param zp0, zp1 The z coordinate of the point.
/// @return ret True if the point is in the subgroup, false otherwise.
function g2IsInSubGroup(xp0, xp1, yp0, yp1, zp0, zp1) -> ret {
// P * X
let px_xp0, px_xp1, px_yp0, px_yp1, px_zp0, px_zp1 := g2ScalarMul(xp0, xp1, yp0, yp1, zp0, zp1, X())
function g2IsInSubGroup(xp0, xp1, yp0, yp1) -> ret {
let xp0_a, xp1_a, yp0_a, yp1_a, zp0_a, zp1_a := g2ProjectiveFromAffine(xp0, xp1, yp0, yp1)
let t00, t01, t10, t11, t20, t21 := g2TimesXNAF(xp0_a, xp1_a, yp0_a, yp1_a, zp0_a, zp1_a)

// P * (X + 1)
let px1_xp0, px1_xp1, px1_yp0, px1_yp1, px1_zp0, px1_zp1 := g2JacobianAdd(px_xp0, px_xp1, px_yp0, px_yp1, px_zp0, px_zp1, xp0, xp1, yp0, yp1, zp0, zp1)
let px1_xp0, px1_xp1, px1_yp0, px1_yp1, px1_zp0, px1_zp1 := g2JacobianAdd(t00, t01, t10, t11, t20, t21, xp0_a, xp1_a, yp0_a, yp1_a, zp0_a, zp1_a)
// P * 2X
let p2x_xp0, p2x_xp1, p2x_yp0, p2x_yp1, p2x_zp0, p2x_zp1 := g2JacobianDouble(px_xp0, px_xp1, px_yp0, px_yp1, px_zp0, px_zp1)
let p2x_xp0, p2x_xp1, p2x_yp0, p2x_yp1, p2x_zp0, p2x_zp1 := g2JacobianDouble(t00, t01, t10, t11, t20, t21)

// phi(P * X)
let e_px_xp0, e_px_xp1, e_px_yp0, e_px_yp1, e_px_zp0, e_px_zp1 := endomorphism(px_xp0, px_xp1, px_yp0, px_yp1, px_zp0, px_zp1)
let e_px_xp0, e_px_xp1, e_px_yp0, e_px_yp1, e_px_zp0, e_px_zp1 := endomorphism(t00, t01, t10, t11, t20, t21)
// phi(phi(P * X))
let e2_px_xp0, e2_px_xp1, e2_px_yp0, e2_px_yp1, e2_px_zp0, e2_px_zp1 := endomorphism(e_px_xp0, e_px_xp1, e_px_yp0, e_px_yp1, e_px_zp0, e_px_zp1)

Expand Down Expand Up @@ -1700,7 +1751,7 @@ object "EcPairing" {
g2_y0 := intoMontgomeryForm(g2_y0)
g2_y1 := intoMontgomeryForm(g2_y1)

if iszero(g2IsInSubGroup(g2_x0,g2_x1, g2_y0, g2_y1, MONTGOMERY_ONE(), 0)) {
if iszero(g2IsInSubGroup(g2_x0,g2_x1, g2_y0, g2_y1)) {
burnGas()
}

Expand Down
45 changes: 45 additions & 0 deletions scripts/pairing_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,48 @@ def is_in_twisted_curve(x0, x1, y0, y1):
b = fp2.add(*b, *TWISTED_CURVE_COEFFS)
c = fp2.exp(y0, y1, 2)
return b == c

def naf(E):
Z = []
i = 0
while E > 0:
if E % 2 == 1:
zi = 2 - (E % 4)
E -= zi
else:
zi = 0
E //= 2
i += 1
Z.append(zi)
Z.reverse()
return Z

def naf_aux(naf):
a = []
for n in naf:
if n == 0:
a.append("00")
elif n == 1:
a.append("01")
elif n == -1:
a.append("10")
b = "".join(a)
return b

def naf_aux_to_naf(naf_aux):
naf = []
for i in range(0, len(naf_aux), 2):
if naf_aux[i] == "0" and naf_aux[i+1] == "0":
naf.append(0)
elif naf_aux[i] == "0" and naf_aux[i+1] == "1":
naf.append(1)
elif naf_aux[i] == "1" and naf_aux[i+1] == "0":
naf.append(-1)
return naf

x_naf = naf(4965661367192848881)
print(x_naf)
naf_yul_rep = naf_aux(x_naf)
print(naf_yul_rep)
original_naf = naf_aux_to_naf(naf_yul_rep)
print(original_naf)
Loading