Merge #852: Add sage script for generating scalar_split_lambda constants
329a2e0a3f2d9e936179cbf079773538f95bee33 sage: Add script for generating scalar_split_lambda constants (Tim Ruffing) f554dfc7088c6ca8d4aff927a51bd889b29dc186 sage: Reorganize files (Tim Ruffing) Pull request description: ACKs for top commit: sipa: ACK 329a2e0a3f2d9e936179cbf079773538f95bee33 Tree-SHA512: d41fe5eba332f48af0b800778aa076925c4e8e95ec21c4371a500ddd6088b6d52961bdb93f7ce2b127e18095667dbb966a0d14191177f0d0e78dfaf55271d5e2
This commit is contained in:
commit
2d9e7175c6
@ -1,9 +1,4 @@
|
|||||||
# Define field size and field
|
load("secp256k1_params.sage")
|
||||||
P = 2^256 - 2^32 - 977
|
|
||||||
F = GF(P)
|
|
||||||
BETA = F(0x7ae96a2b657c07106e64479eac3434e99cf0497512f58995c1396c28719501ee)
|
|
||||||
|
|
||||||
assert(BETA != F(1) and BETA^3 == F(1))
|
|
||||||
|
|
||||||
orders_done = set()
|
orders_done = set()
|
||||||
results = {}
|
results = {}
|
||||||
|
114
sage/gen_split_lambda_constants.sage
Normal file
114
sage/gen_split_lambda_constants.sage
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
""" Generates the constants used in secp256k1_scalar_split_lambda.
|
||||||
|
|
||||||
|
See the comments for secp256k1_scalar_split_lambda in src/scalar_impl.h for detailed explanations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
load("secp256k1_params.sage")
|
||||||
|
|
||||||
|
def inf_norm(v):
|
||||||
|
"""Returns the infinity norm of a vector."""
|
||||||
|
return max(map(abs, v))
|
||||||
|
|
||||||
|
def gauss_reduction(i1, i2):
|
||||||
|
v1, v2 = i1.copy(), i2.copy()
|
||||||
|
while True:
|
||||||
|
if inf_norm(v2) < inf_norm(v1):
|
||||||
|
v1, v2 = v2, v1
|
||||||
|
# This is essentially
|
||||||
|
# m = round((v1[0]*v2[0] + v1[1]*v2[1]) / (inf_norm(v1)**2))
|
||||||
|
# (rounding to the nearest integer) without relying on floating point arithmetic.
|
||||||
|
m = ((v1[0]*v2[0] + v1[1]*v2[1]) + (inf_norm(v1)**2) // 2) // (inf_norm(v1)**2)
|
||||||
|
if m == 0:
|
||||||
|
return v1, v2
|
||||||
|
v2[0] -= m*v1[0]
|
||||||
|
v2[1] -= m*v1[1]
|
||||||
|
|
||||||
|
def find_split_constants_gauss():
|
||||||
|
"""Find constants for secp256k1_scalar_split_lamdba using gauss reduction."""
|
||||||
|
(v11, v12), (v21, v22) = gauss_reduction([0, N], [1, int(LAMBDA)])
|
||||||
|
|
||||||
|
# We use related vectors in secp256k1_scalar_split_lambda.
|
||||||
|
A1, B1 = -v21, -v11
|
||||||
|
A2, B2 = v22, -v21
|
||||||
|
|
||||||
|
return A1, B1, A2, B2
|
||||||
|
|
||||||
|
def find_split_constants_explicit_tof():
|
||||||
|
"""Find constants for secp256k1_scalar_split_lamdba using the trace of Frobenius.
|
||||||
|
|
||||||
|
See Benjamin Smith: "Easy scalar decompositions for efficient scalar multiplication on
|
||||||
|
elliptic curves and genus 2 Jacobians" (https://eprint.iacr.org/2013/672), Example 2
|
||||||
|
"""
|
||||||
|
assert P % 3 == 1 # The paper says P % 3 == 2 but that appears to be a mistake, see [10].
|
||||||
|
assert C.j_invariant() == 0
|
||||||
|
|
||||||
|
t = C.trace_of_frobenius()
|
||||||
|
|
||||||
|
c = Integer(sqrt((4*P - t**2)/3))
|
||||||
|
A1 = Integer((t - c)/2 - 1)
|
||||||
|
B1 = c
|
||||||
|
|
||||||
|
A2 = Integer((t + c)/2 - 1)
|
||||||
|
B2 = Integer(1 - (t - c)/2)
|
||||||
|
|
||||||
|
# We use a negated b values in secp256k1_scalar_split_lambda.
|
||||||
|
B1, B2 = -B1, -B2
|
||||||
|
|
||||||
|
return A1, B1, A2, B2
|
||||||
|
|
||||||
|
A1, B1, A2, B2 = find_split_constants_explicit_tof()
|
||||||
|
|
||||||
|
# For extra fun, use an independent method to recompute the constants.
|
||||||
|
assert (A1, B1, A2, B2) == find_split_constants_gauss()
|
||||||
|
|
||||||
|
# PHI : Z[l] -> Z_n where phi(a + b*l) == a + b*lambda mod n.
|
||||||
|
def PHI(a,b):
|
||||||
|
return Z(a + LAMBDA*b)
|
||||||
|
|
||||||
|
# Check that (A1, B1) and (A2, B2) are in the kernel of PHI.
|
||||||
|
assert PHI(A1, B1) == Z(0)
|
||||||
|
assert PHI(A2, B2) == Z(0)
|
||||||
|
|
||||||
|
# Check that the parallelogram generated by (A1, A2) and (B1, B2)
|
||||||
|
# is a fundamental domain by containing exactly N points.
|
||||||
|
# Since the LHS is the determinant and N != 0, this also checks that
|
||||||
|
# (A1, A2) and (B1, B2) are linearly independent. By the previous
|
||||||
|
# assertions, (A1, A2) and (B1, B2) are a basis of the kernel.
|
||||||
|
assert A1*B2 - B1*A2 == N
|
||||||
|
|
||||||
|
# Check that their components are short enough.
|
||||||
|
assert (A1 + A2)/2 < sqrt(N)
|
||||||
|
assert B1 < sqrt(N)
|
||||||
|
assert B2 < sqrt(N)
|
||||||
|
|
||||||
|
G1 = round((2**384)*B2/N)
|
||||||
|
G2 = round((2**384)*(-B1)/N)
|
||||||
|
|
||||||
|
def rnddiv2(v):
|
||||||
|
if v & 1:
|
||||||
|
v += 1
|
||||||
|
return v >> 1
|
||||||
|
|
||||||
|
def scalar_lambda_split(k):
|
||||||
|
"""Equivalent to secp256k1_scalar_lambda_split()."""
|
||||||
|
c1 = rnddiv2((k * G1) >> 383)
|
||||||
|
c2 = rnddiv2((k * G2) >> 383)
|
||||||
|
c1 = (c1 * -B1) % N
|
||||||
|
c2 = (c2 * -B2) % N
|
||||||
|
r2 = (c1 + c2) % N
|
||||||
|
r1 = (k + r2 * -LAMBDA) % N
|
||||||
|
return (r1, r2)
|
||||||
|
|
||||||
|
# The result of scalar_lambda_split can depend on the representation of k (mod n).
|
||||||
|
SPECIAL = (2**383) // G2 + 1
|
||||||
|
assert scalar_lambda_split(SPECIAL) != scalar_lambda_split(SPECIAL + N)
|
||||||
|
|
||||||
|
print(' A1 =', hex(A1))
|
||||||
|
print(' -B1 =', hex(-B1))
|
||||||
|
print(' A2 =', hex(A2))
|
||||||
|
print(' -B2 =', hex(-B2))
|
||||||
|
print(' =', hex(Z(-B2)))
|
||||||
|
print(' -LAMBDA =', hex(-LAMBDA))
|
||||||
|
|
||||||
|
print(' G1 =', hex(G1))
|
||||||
|
print(' G2 =', hex(G2))
|
36
sage/secp256k1_params.sage
Normal file
36
sage/secp256k1_params.sage
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
"""Prime order of finite field underlying secp256k1 (2^256 - 2^32 - 977)"""
|
||||||
|
P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
|
||||||
|
|
||||||
|
"""Finite field underlying secp256k1"""
|
||||||
|
F = FiniteField(P)
|
||||||
|
|
||||||
|
"""Elliptic curve secp256k1: y^2 = x^3 + 7"""
|
||||||
|
C = EllipticCurve([F(0), F(7)])
|
||||||
|
|
||||||
|
"""Base point of secp256k1"""
|
||||||
|
G = C.lift_x(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798)
|
||||||
|
|
||||||
|
"""Prime order of secp256k1"""
|
||||||
|
N = C.order()
|
||||||
|
|
||||||
|
"""Finite field of scalars of secp256k1"""
|
||||||
|
Z = FiniteField(N)
|
||||||
|
|
||||||
|
""" Beta value of secp256k1 non-trivial endomorphism: lambda * (x, y) = (beta * x, y)"""
|
||||||
|
BETA = F(2)^((P-1)/3)
|
||||||
|
|
||||||
|
""" Lambda value of secp256k1 non-trivial endomorphism: lambda * (x, y) = (beta * x, y)"""
|
||||||
|
LAMBDA = Z(3)^((N-1)/3)
|
||||||
|
|
||||||
|
assert is_prime(P)
|
||||||
|
assert is_prime(N)
|
||||||
|
|
||||||
|
assert BETA != F(1)
|
||||||
|
assert BETA^3 == F(1)
|
||||||
|
assert BETA^2 + BETA + 1 == 0
|
||||||
|
|
||||||
|
assert LAMBDA != Z(1)
|
||||||
|
assert LAMBDA^3 == Z(1)
|
||||||
|
assert LAMBDA^2 + LAMBDA + 1 == 0
|
||||||
|
|
||||||
|
assert Integer(LAMBDA)*G == C(BETA*G[0], G[1])
|
Loading…
x
Reference in New Issue
Block a user