Elliptic Curves over Goldilocks
Building prime-order elliptic curves over a quintic extension of Goldilocks with 128-bit security and efficient hash-to-curve: EcGFp5 and EcMasFp5.
The two elliptic curves implementation can be found here.
We aim to construct an elliptic curve satisfying the following criteria:
- Prime order.
- Efficient group operations.
- Deterministc and efficient hash-to-curve encoding.
- Security level of 128 bits.
Extension Field Selection
To achieve the target security level, we adopt an approach similar to EcGFp5. Specifically, we define the extension field over
We select the polynomial because:
- It has the minimal Hamming weight among irreducible polynomials.
- It uses nonzero coefficients as close to as possible.
- A polynomial of the form minimizes the computational cost of the Frobenius operator.
The following Sage script verifies that the previous claims about are true:
# Define Fp
p = 2**64 - 2**32 + 1
F = GF(p)
# Define Fp[z]
R.<z> = PolynomialRing(F)
# Polynomials of the form z⁵±c for c ∈ [0,2] are not irreducible
for i in range(3):
assert(R(z^5+i).is_irreducible() == R(z^5-i).is_irreducible() == False)
# The polynomial z^5-3 is irreducible
assert(R(z^5-3).is_irreducible())
Quadratic Residuosity
To check whether an element is a quadratic residue, we use Euler’s criterion, raising to the power .
This exponentiation can be optimized by noting that:
which follows from the cyclotomic polynomial decomposition.
To compute to the power over , we need to efficiently compute the first and second Frobenius operator. Moreover, notice that when this exponentation is done, we get an element such that and this can only happen when ; so the second exponentiation is over instead.
Exponentiation by is done by observing that:
and using repeated squaring.
Frobenius Operator
As we have shown in the previous section, we must be able compute the first and second Frobenius operator over an element in efficiently. A naive implementation would take -operations, but we will do it much better.
Write as , with . Then, we can compute the frobenius operators as follows:
with and . Hence, assuming the precomputation of for , we can compute and by four multiplications each.
Expand to see the proof
Proof: On the one hand, using Fermat’s little theorem we have that .
On the other hand, notice and therefore , where .
Using the previous observations, we have:
which can be computed using a few multiplications.
Similarly, notice and therefore , where .
TODO: Develop on generating a subgroup of order .
Playing a little bit, we can refine exponentiation by :
where is viewed as a -element.
Square Roots
To compute the square root of an element we will use in our favour the fact that to check its quadratic residuosity we have to raise it to .
After a bit of algebra, we get the following identity:
so we can compute the squaring by:
- Compute the square of by using any squaring algorithms over the base field (recall that when an element is raised to it gets mapped to the base field).
- Compute the exponentiation of by the RHS of the identity.
- Multiply both results.
EcGFp5
Definition
Found by Thomas Pornin, the EcGFp5 curve is defined by the equation:
with constants:
and order for the -bit prime:
n = 0x7ffffffd800000077ffffff1000000167fffffe6cfb80639e8885c39d724a09ce80fd996948bffe1
Since is odd, the points belonging to the subgroup of order are precisely the -torsion of the curve.
Hashing to the Curve
We follow the RFC 9380: Hashing to Elliptic Curves to get a deterministic mapping.
hash_to_curve(msg)
Input: msg, an arbitrary-length byte string.
Output: P, a point in G.
Steps:
1. u = hash_to_field(msg, 2)
2. Q0 = map_to_curve_simple_swu(u[0])
3. Q1 = map_to_curve_simple_swu(u[1])
4. R = Q0 + Q1
5. P = R + R # clear_cofactor(R)
6. return P
––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
map_to_curve_simple_swu(u)
Input: u, an element in Fp5.
Output: P, a point in E.
Steps:
1. tv1 = inv0(Z^2 * u^4 + Z * u^2)
2. x1 = (-B / A) * (1 + tv1)
3. If tv1 == 0, set x1 = B / (Z * A)
4. gx1 = x1^3 + A * x1 + B
5. x2 = Z * u^2 * x1
6. gx2 = x2^3 + A * x2 + B
7. If is_square(gx1), set x = x1 and y = sqrt(gx1)
8. Else set x = x2 and y = sqrt(gx2)
9. If sgn0(u) != sgn0(y), set y = -y
10. return (x, y)
EcMasFp5
Definition
We choose the short Weierstrass equation:
because assuming that and that the cost of addition, subtraction, multiplication and division over the field is the same (something true in SNARK settings), point addition costs field operations and point doubling costs field operations.
Setting also restricts to be from the extension field, rather from the prime field; otherwise the curve could not be of primer order.
Since has no impact in group operations, we perform a linear search:
# Define Fp⁵
K.<z> = GF(p^5, modulus=z^5-3)
# Perform a linear search
def find_ec_over_gfp5(initial_a = 0, initial_c = 1, attempts = 50, pol_degree = K.degree(), verbose = True):
A = initial_a
c = initial_c
if verbose:
print("· Attempting with A =", A)
while True:
for i in range(1, pol_degree):
B = c * z**i
E = EllipticCurve(K, (A, B))
if verbose:
print(" -",E)
if is_prime(E.order(algorithm="pari")):
print(f"Elliptic curve found with A = {E.a4()} and B = {E.a6()}")
return E
else:
c += 1
# If no prime order was found, reset `c` and update `A`
if c == attempts:
c = initial_c
A += 1
if verbose:
print("· Attempting with A =", A)
continue
break
E = find_ec_over_gfp5()
The first elliptic curve found was:
with a -bit prime order:
n = 0xfffffffb0000000effffffe20000002cffffffcc2c13f5f892042da0dfcde3fc8f4b2caf22360ee3
The name for this curve was chosen to be EcMasFp5 (Elliptic Curve for Maximum Aggregation Speed over ).
Security of the Curve
Thanks to Robin Salen for help and discussion.
We follow the criteria in Safe Curves.
Summary
Curve prime order `n`: 0xfffffffb0000000effffffe20000002cffffffcc2c13f5f892042da0dfcde3fc8f4b2caf22360ee3 (320 bits)
Curve cofactor: 1
Curve embedding degree: n-1 (320 bits)
Curve CM discriminant `D`: -0x350910d76c19ff80472df1f236bda8f251edc59a20563b4f1bcb88ad47f9892d334755425f4369e43 (323 bits)
Curve security (Pollard-Rho): 159.83 bits
Twist prime order `nt`: 0x128ad28934008b50864257bbff0983af9688b4f04ff3f024f86f (205 bits)
Twist cofactor: 0xdce68ed7aef21329a0ac97d33d76f (116 bits)
Twist embedding degree: (nt-1)/15 (201 bits)
Twist CM discriminant: D (323 bits)
Twist security (Pollard-Rho): 101.93 bits
More details are given below.
Field
Since the extension field is the same as the one in EcGFp5, we inherit by default its security. In particular, this field selection approximately achieves a -bit security level, well beyond the target one.
Embedding Degree
We use the following code to find the embedding degree :
def embedding_degree(q,n):
F = GF(n)
return F(q).multiplicative_order()
Finding that , as large as it can possibly be, avoiding MOV-type attacks.
CM Discriminant
The CM Discriminant was found to be a -bit integer:
D = -0x350910d76c19ff80472df1f236bda8f251edc59a20563b4f1bcb88ad47f9892d334755425f4369e43
Twist
The number factorizes as:
0x128ad28934008b50864257bbff0983af9688b4f04ff3f024f86f*0xdce68ed7aef21329a0ac97d33d76f
Hashing to the Curve
It is the same as the one for EcFp5 with the exception that this one does not need to clean the cofactor.
Resources
- EcGFp5: a Specialized Elliptic Curve - Thomas Pornin (2022/274).
- SP1 Turbo Memory Argument.
- Safe Curves.
- How to Hash into Elliptic Curves - Thomas Icart (2009/226).
- RFC 9380: Hashing to Elliptic Curves.