TL;DR: The two elliptic curves implementation can be found here.
We aim to construct an elliptic curve satisfying the following criteria:
To achieve the target security level, we adopt an approach similar to EcGFp5. Specifically, we define the extension field
We select the polynomial
The following Sage script verifies that the previous claims about
# 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())
To check whether an element
This exponentiation can be optimized by noting that:
which follows from the cyclotomic polynomial decomposition.
To compute
Exponentiation by
and using repeated squaring.
As we have shown in the previous section, we must be able compute the first and second Frobenius operator over an element in
Write
with
Proof:
On the one hand, using Fermat's little theorem we have that
On the other hand, notice
Using the previous observations, we have:
which can be computed using a few multiplications.
Similarly, notice
TODO: Develop on
Playing a little bit, we can refine exponentiation by
where
To compute the square root of an element
After a bit of algebra, we get the following identity:
so we can compute the squaring by:
Found by Thomas Pornin, the EcGFp5 curve is defined by the equation:
with constants:
and order
n = 0x7ffffffd800000077ffffff1000000167fffffe6cfb80639e8885c39d724a09ce80fd996948bffe1
Since
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)
We choose the short Weierstrass equation:
because assuming that
Setting
Since
# 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
n = 0xfffffffb0000000effffffe20000002cffffffcc2c13f5f892042da0dfcde3fc8f4b2caf22360ee3
The name for this curve was chosen to be EcMasFp5 (Elliptic Curve for Maximum Aggregation Speed over
Thanks to Robin Salen for help and discussion.
We follow the criteria in Safe Curves.
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.
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
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
The CM Discriminant
D = -0x350910d76c19ff80472df1f236bda8f251edc59a20563b4f1bcb88ad47f9892d334755425f4369e43
The number
0x128ad28934008b50864257bbff0983af9688b4f04ff3f024f86f*0xdce68ed7aef21329a0ac97d33d76f
It is the same as the one for EcFp5 with the exception that this one does not need to clean the cofactor.