The post BN254 For The Rest Of Us constitutes the base point for this article, so I highly recommend reading it to get a solid background of the BN254 curve. This post will reuse some parts with an emphasy on the computational aspect of the optimal Ate pairing over the BN254 curve.
For those with a poor background on Pairing-Based Cryptography (PBC) but with a decent background on Elliptic-Curve Cryptoraphy (ECC) I recommend reading the following resources in order:
Only after (and not before) you feel sufficiently comfortable with both PBC and ECC come back to this post and start to make sense out of it.
The implementation in zkASM can be found here.
The BN254 is the elliptic curve
defined over a finite field
p = 21888242871839275222246405745257275088696311157297823662689037894645226208583
The number of points
r = 21888242871839275222246405745257275088548364400416034343698204186575808495617
where t = 147946756881789318990833708069417712967
is known as the trace of Frobenius.
The point
The embedding degree
For more information, refer to BN254 For The Rest Of Us or the EIPs 196 and 197.
To represent
fields:
where
In the particular case of the BN254, we have
from which we can extract the identities
Using the latter identity, we can also write:
These two representations of
BN curves admits a sextic twist
or more specifically:
(y')² = (x')³ + (19485874751759354771024239261021720505790618469301721065564631296452457478373 + 266929791119991161246907387137283842545076965332900288569378510910307636690·u)
This means that pairing computations can be restricted to points
The number of points of
which concrete to:
#E' = 479095176016622842441988045216678740799252316531100822436447802254070093686356349204969212544220033486413271283566945264650845755880805213916963058350733
c = 21888242871839275222246405745257275088844257914179612981679871602714643921549
The mapping
defines a group homomorphism from
The optimal Ate pairing
where:
x = 4965661367192848881
and therefore 6x+2 = 29793968203157093288
, which is a number of
2^3 + 2^5 - 2^7 + 2^10 - 2^11 + 2^14 + 2^17 + 2^18 - 2^20 + 2^23 - 2^25 + 2^30 + 2^31 + 2^32 - 2^35 + 2^38 - 2^44 + 2^47 + 2^48 - 2^51 + 2^55 + 2^56 - 2^58 + 2^61 + 2^63 + 2^64
[0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, 1, 1]
0001010-1001-100100110-10010-1000011100-100100000-1001100-1000110-1001011
which has a Hamming weight of 26.
We prefer base 6x+2
expressed in the latter is:
2^3 + 2^5 + 2^7 + 2^8 + 2^9 + 2^11 + 2^12 + 2^13 + 2^17 + 2^18 + 2^20 + 2^21 + 2^22 + 2^25 + 2^26 + 2^27 + 2^28 + 2^29 + 2^31 + 2^32 + 2^35 + 2^36 + 2^37 + 2^44 + 2^45 + 2^46 + 2^48 + 2^51 + 2^52 + 2^53 + 2^54 + 2^56 + 2^58 + 2^59 + 2^60 + 2^63 + 2^64
[0,0,0,1,0,1,0,1,1,1,0,1,1,1,0,0,0,1,1,0,1,1,1,0,0,1,1,1,1,1,0,1,1,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,1,0,0,1,1,1,1,0,1,0,1,1,1,0,0,1,1]
00010101110111000110111001111101100111000000111010011110101110011
which has a Hamming weigth of 37.
I moved from base
starting with
Therefore, what we want to compute is the following:
bound = [0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0, 0, 1, 1, 0, -1,
0, 0, 1, 0, -1, 0, 0, 0, 0, 1, 1, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0,
0, 0, -1, 0, 0, 1, 1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0,
1, 1]
1101111010011011000001001001000111011101011100001011001111110111111100000111110100101101000001011101000011111001111110101000110
# This is 6x+2 in base {-1,0,1}, which has a Hamming weight of 26
def e(P,Q):
assert(subgroup_check_G1(P))
assert(subgroup_check_G2(Q))
if ((P == 0) or (Q == 0)) return 1 # Here, 0 is the identity element of E
R = Q
f = 1
for i in range(len(bound)-2,-1,-1): # len(bound)=65
f = f * f * line(twist(R),twist(R),P)
R = 2 * R
if bound[i] == 1:
f = f * line(untwist(R),untwist(Q),P)
R = R + Q
elif bound[i] == -1:
f = f * line(untwist(R),untwist(-Q),P)
R = R - Q
Qp = Frobenius1(Q); # Q'
f = f * line(twist(R), twist(Qp), P);
R = R + Qp;
Qpp = -Frobenius1(Qp); # Q''
f = f * line(twist(R), twist(Qpp), P);
return final_exponentiation(f)
In what follows we explain how to compute each of the components of the previous pseudocode snippet.
To summary BN254 For The Rest Of Us, we have:
Costs:
A point
where
This was proven in Proposition 3 of 2022/352 and it is clearly much faster than the naive check
TODO:
A more efficient approach appears in 2022/348, which ensures that
Costs:
Given two points
Costs:
Given a point
Costs:
Now we explain some tricks for the computation of the lines:
For the first line notice that if
where
The conjugate of an element in
Moreover, due to
Costs:
I don't include the cost of computing
For the second line we similarly have:
Again, due to the homomorphic property of
Costs:
Hence, in both cases, the Frobenius operator comes for free.
In the final exponentiation, we raise an element
First, divide the exponent as follows:
Let's start with the easy part of the easy part, when we first attempt to compute
The conjugate of an element in
To finish the easy part, we first compute
Importantly, after the computation of the easy part, the resulting field element becomes a member of the cyclotomic subgroup
Costs:
Following Section 5 of this 2008/490, we can write:
where:
So we just need to compute:
and then multiply them all.
In what follows, remember that x = 4965661367192848881
, which is a number of
1 - 2^4 + 2^9 + 2^11 + 2^16 + 2^19 + 2^21 + 2^22 + 2^25 + 2^27 + 2^30 + 2^34 + 2^36 + 2^37 + 2^39 + 2^41 + 2^44 + 2^47 + 2^48 + 2^51 - 2^53 + 2^56 + 2^58 + 2^62
[1,0,0,0,-1,0,0,0,0,1,0,1,0,0,0,0,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,-1,0,0,1,0,1,0,0,0,1]
1000-1000010100001001011001010010001011010100100110010-1001010001
which has a Hamming weight of 24.
We prefer base x
expressed in the latter is:
1 + 2^4 + 2^5 + 2^6 + 2^7 + 2^8 + 2^11 + 2^16 + 2^19 + 2^21 + 2^22 + 2^25 + 2^27 + 2^30 + 2^34 + 2^36 + 2^37 + 2^39 + 2^41 + 2^44 + 2^47 + 2^48 + 2^51 + 2^53 + 2^54 + 2^55 + 2^58 + 2^62
[1,0,0,0,1,1,1,1,1,0,0,1,0,0,0,0,1,0,0,1,0,1,1,0,0,1,0,1,0,0,1,0,0,0,1,0,1,1,0,1,0,1,0,0,1,0,0,1,1,0,0,1,0,1,1,1,0,0,1,0,0,0,1]
100011111001000010010110010100100010110101001001100101110010001
which has a Hamming weigth of 28.
We proceed as follows:
We start by computing
Then, compute
Next, we group the previous elements as follows:
(recall that at this point computing the conjugate is the same as computing the inverse)
Finally, compute the product:
To compute this product as efficient as possible, we use the vectorial addition chain technique as explained in the last part of Section 5 of 2008/490:
which requires only
Costs:
As we have shown, in the final exponentation we must compute the first, second and third Frobenius operator
Recall that we can write any element
where
We will now show that raising an
Using all the previous observations, we have:
which can be computed using a few multiplications and a few conjugations, assuming each
Similarly, notice
Lastly, we can use the identity
Then, we have:
with
Costs:
I follow 2010/542 closely for this section.
Recall that:
and also:
For this optimitzation, we will further need the following
so that for
This optimization is due to "compression" and "decompression" technique, which exploits the algebraic relationships arising from elements belonging to the cyclotomic subgroup
If
and the decompression function
where:
Now, on input
Given
Represent
Therefore, we can use the square-and-multiply algorithm to compute such product. Since we are going to be squaring in compressed form, we start at
def exp_cyclo(f,e):
Cf = comp(f);
result = 1;
for i in range(len(e)):
if (e[i] == 1) {
result = decomp(Cf) * result;
}
Cf = square_comp(Cf);
}
return result;
Recall that:
We use two additional representations of
The first one is used to efficiently compute the Frobenius operator and is as follows:
The second one is used to speed up the squares and exponentations in the hard part of the final exponentiation:
We start with
This relationship induces the following field isomorphism
Exercise: Check that
We continue with
This relationship induces the following field isomorphism
Exercise: Check that
Addition
# INPUT: (a = a1 + a2·u), (b = b1 + b2·u) ∈ Fp2, where ai,bi ∈ Fp
# OUTPUT: (c1 + c2·u) = a + b ∈ Fp
c1 = a1 + b1
c2 = a2 + b2
Subtraction
# INPUT: (a = a1 + a2·u), (b = b1 + b2·u) ∈ Fp2, where ai,bi ∈ Fp
# OUTPUT: (c1 + c2·u) = a - b ∈ Fp
c1 = a1 - b1
c2 = a2 - b2
Multiplication
# INPUT: (a = a1 + a2·u), (b = b1 + b2·u) ∈ Fp2, where ai,bi ∈ Fp
# OUTPUT: (c1 + c2·u) = a·b ∈ Fp
c1 = a1·b1 - a2·b2
c2 = a1·b2 + a2·b1
Scalar Multiplication
# INPUT: b ∈ Fp, (a = a1 + a2·u) ∈ Fp2, where ai ∈ Fp
# OUTPUT: (c1 + c2·u) = a·b ∈ Fp
c1 = b·a1
c2 = b·a2
Square
# INPUT: (a = a1 + a2·u) ∈ Fp2, where ai ∈ Fp
# OUTPUT: (c1 + c2·u) = a² ∈ Fp
c1 = (a1 - a2)·(a1 + a2)
c2 = 2·a1·a2
Inverse
# INPUT: (a = a1 + a2·u) ∈ Fp2, where ai ∈ Fp
# OUTPUT: (c1 + c2·u) = a⁻¹ ∈ Fp
c1 = a1·(a1² + a2²)⁻¹
c2 = -a2·(a1² + a2²)⁻¹
Square
# INPUT: (a = a1 + a2·V) ∈ Fp4, where ai ∈ Fp2
# OUTPUT: (c1 + c2·V) = a² ∈ Fp4
c1 = a2²·(9 + u) + a1²
c2 = (a1 + a2)² - a1² - a2²
Addition
# INPUT: (a = a1 + a2·v + a3·v²),(b = b1 + b2·v + b3·v²) ∈ Fp6, where ai,bi ∈ Fp2
# OUTPUT: (c1 + c2·v + c3·v²) = a + b ∈ Fp6
c1 = a1 + b1
c2 = a2 + b2
c3 = a3 + b3
Subtraction
# INPUT: (a = a1 + a2·v + a3·v²),(b = b1 + b2·v + b3·v²) ∈ Fp6, where ai,bi ∈ Fp2
# OUTPUT: (c1 + c2·v + c3·v²) = a - b ∈ Fp6
c1 = a1 - b1
c2 = a2 - b2
c3 = a3 - b3
Multiplication
# INPUT: (a = a1 + a2·v + a3·v²),(b = b1 + b2·v + b3·v²) ∈ Fp6, where ai,bi ∈ Fp2
# OUTPUT: (c1 + c2·v + c3·v²) = a·b ∈ Fp6
c1 = [(a2+a3)·(b2+b3) - a2·b2 - a3·b3]·(9+u) + a1·b1
c2 = (a1+a2)·(b1+b2) - a1·b1 - a2·b2 + (9+u)·(a3·b3)
c3 = (a1+a3)·(b1+b3) - a1·b1 + a2·b2 - a3·b3
Scalar Multiplication
# INPUT: b ∈ Fp, (a = a1 + a2·v + a3·v²) ∈ Fp6, where ai ∈ Fp2
# OUTPUT: (c1 + c2·v + c3·v²) = a·b ∈ Fp6
c1 = b·a1
c1 = b·a2
c1 = b·a3
The following three algorithms help to compute the line equations. See the sparse multiplications algorithms of next section for a full understanding.
Sparse Multiplication A
# INPUT: (a = a1 + a2·v + a3·v²),b = b2·v ∈ Fp6, where ai,b2 ∈ Fp2
# OUTPUT: (c1 + c2·v + c3·v²) = a·b ∈ Fp6
c1 = b2·a3·(9+u)
c2 = b2·a1
c3 = b2·a2
Sparse Multiplication B
# INPUT: (a = a1 + a2·v + a3·v²),(b = b2·v + b3·v²) ∈ Fp6, where ai,bi ∈ Fp2
# OUTPUT: (c1 + c2·v + c3·v²) = a·b ∈ Fp6
c1 = [(a2 + a3)·(b2 + b3) - a2·b2 - a3·b3]·(9+u)
c2 = a1·b2 + a3·b3·(9+u)
c3 = (a1 + a3)·b3 - a3·b3 + a2·b2
Sparse Multiplication C
# INPUT: (a = a1 + a2·v + a3·v²),(b = b1 + b3·v²) ∈ Fp6, where ai,bi ∈ Fp2
# OUTPUT: (c1 + c2·v + c3·v²) = a·b ∈ Fp6
c1 = a1·b1 + a2·b3·(9+u)
c2 = a2·b1 + a3·b3·(9+u)
c3 = (a1 + a3)·(b1 + b3) - a1·b1 - a3·b3
Square
# INPUT: (a = a1 + a2·v + a3·v²) ∈ Fp6, where ai ∈ Fp2
# OUTPUT: (c1 + c2·v + c3·v²) = a² ∈ Fp6
c1 = 2·a2·a3·(9 + u) + a1²
c2 = a3²·(9 + u) + 2·a1·a2
c3 = 2·a1·a2 - a3² + (a1 - a2 + a3)² + 2·a2·a3 - a1²
Inverse
# INPUT: (a = a1 + a2·v + a3·v²) ∈ Fp6, where ai ∈ Fp2
# OUTPUT: (c1 + c2·v + c3·v²) = a⁻¹ ∈ Fp6
c1mid = a1² - (9 + u)·(a2·a3)
c2mid = (9 + u)·a3² - (a1·a2)
c3mid = a2² - (a1·a3)
c1 = (a1² - (9 + u)·(a2·a3))·(a1·c1mid + xi·(a3·c2mid + a2·c3mid))⁻¹
c2 = ((9 + u)·a3² - (a1·a2))·(a1·c1mid + xi·(a3·c2mid + a2·c3mid))⁻¹
c3 = (a2²-a1·a3)·(a1·c1mid + xi·(a3·c2mid + a2·c3mid))⁻¹
Addition
# INPUT: (a = a1 + a2·w),(b = b1 + b2·w) ∈ Fp12, where ai,bi ∈ Fp6
# OUTPUT: (c1 + c2·w) = a + b ∈ Fp12
c1 = a1 + b1
c2 = a2 + b2
Subtraction
# INPUT: (a = a1 + a2·w),(b = b1 + b2·w) ∈ Fp12, where ai,bi ∈ Fp6
# OUTPUT: (c1 + c2·w) = a - b ∈ Fp12
c1 = a1 - b1
c2 = a2 - b2
Multiplication
# INPUT: (a = a1 + a2·w),(b = b1 + b2·w) ∈ Fp12, where ai,bi ∈ Fp6
# OUTPUT: (c1 + c2·w) = a·b ∈ Fp12
c1 = a1·b1 + a2·b2·v
c2 = (a1+a2)·(b1+b2) - a1·b1 - a2·b2
The following two algorithms are used to compute the line equations avoiding all multiplications by zero that arise from the sparisity of such equations.
Sparse Multiplication A
# INPUT: (a = a1 + a2·w),(b = b1 + b2·w) ∈ Fp12, where ai ∈ Fp6, b1 = b12·v and b2 = b22·v + b23·v², with b12,b22,b23 ∈ Fp2
# OUTPUT: (c1 + c2·w) = a·b ∈ Fp12
c1 = a1·b1 + a2·b2·v
c2 = (a1+a2)·[(b12+b22)·v + b23·v²] - a1·b1 - a2·b2
Sparse Multiplication B
# INPUT: (a = a1 + a2·w),(b = b1 + b2·w) ∈ Fp12, where ai ∈ Fp6, b1 = b11 + b13·v² and b2 = b22·v, with b11,b13,b22 ∈ Fp2
# OUTPUT: (c1 + c2·w) = a·b ∈ Fp12
c1 = a1·b1 + a2·b2·v
c2 = (a1+a2)·(b11 + b22·v + b13·v²) - a1·b1 - a2·b2
Square
# INPUT: (a = a1 + a2·w) ∈ Fp12, where ai ∈ Fp6
# OUTPUT: (c1 + c2·w) = a² ∈ Fp12
c1 = (a1-a2)·(a1-a2·v) + a1·a2 + a1·a2·v
c2 = 2·a1·a2
Inverse
# INPUT: (a = a1 + a2·w) ∈ Fp12, where ai ∈ Fp6
# OUTPUT: (c1 + c2·w) = a⁻¹ ∈ Fp12
c1 = a1·(a1² - a2²·v)⁻¹
c2 = -a2·(a1² - a2²·v)⁻¹
First Frobenius Operator
# INPUT: (a = a1 + a2·w) = ((a11 + a12v + a13v²) + (a21 + a22v + a23v²)) ∈ Fp12, where ai ∈ Fp6 and aij ∈ Fp2
# OUTPUT: (c1 + c2·w) = a^p ∈ Fp12
c1 = a̅11 + a̅12·γ12·v + a̅13·γ14·v²
c2 = a̅21·γ11 + a̅22·γ13·v + a̅23·γ15·v²
Second Frobenius Operator
# INPUT: (a = a1 + a2·w) = ((a11 + a12v + a13v²) + (a21 + a22v + a23v²)) ∈ Fp12, where ai ∈ Fp6 and aij ∈ Fp2
# OUTPUT: (c1 + c2·w) = a^p² ∈ Fp12
c1 = a11 + a12·γ22·v + a13·γ24·v²
c2 = a21·γ21 + a22·γ23·v + a23·γ25·v²
Third Frobenius Operator
# INPUT: (a = a1 + a2·w) = ((a11 + a12v + a13v²) + (a21 + a22v + a23v²)) ∈ Fp12, where ai ∈ Fp6 and aij ∈ Fp2
# OUTPUT: (c1 + c2·w) = a^p³ ∈ Fp12
c1 = a̅11 + a̅12·γ32·v + a̅13·γ34·v²
c2 = a̅21·γ31 + a̅22·γ33·v + a̅23·γ35·v²
The following two algorithms are used for computing squarings and exponentiations over the cyclotomic subgroup
Fast Squaring
# INPUT: (a = a1 + a2·w) ∈ GΦ6(p²), where ai ∈ Fp6
# OUTPUT: ((c11 + c12·v + c13·v²) + (c21 + c22·v + c23·v²)·w) = a² ∈ GΦ6(p²)
[t11,t22] = (a11 + a22·V)²
[t23,t12] = (a21 + a13·V)²
[t13,aux] = (a12 + a23·V)²
t21 = aux·(9+u)
c11 = -2·a11 + 3·t11
c12 = -2·a12 + 3·t23
c13 = -2·a13 + 3·t13
c21 = 2·a21 + 3·t21
c22 = 2·a22 + 3·t22
c23 = 2·a23 + 3·t12
Fast Exponentatiation
# INPUT: e, (a = a1 + a2·w) ∈ GΦ6(p²), where e ∈ [0,p¹²-2] ai ∈ Fp6
# OUTPUT: (c = c1 + c2·w) = a^e ∈ GΦ6(p²)
c = a
for i in range(L-2,-1,-1): # L is log2(e)
c = c² # Use previous algorithm
if e[i] == 1:
c = c·a
return c
if you don't know why, make sure to review Galois theory. Start by noticing that Galois group
I recently discoverd that this is just a particular case of the more generic non-adjacent form (NAF) of a number. ↩︎