Skip to content

Commit 0854198

Browse files
sage: Add script for generating scalar_split_lambda constants
1 parent f554dfc commit 0854198

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed

sage/gen_split_lambda_constants.sage

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
load("secp256k1_params.sage")
2+
3+
def inf_norm(v):
4+
"""Returns the infinity norm of a vector."""
5+
return max(map(abs, v))
6+
7+
def gauss_reduction(i1, i2):
8+
v1, v2 = i1.copy(), i2.copy()
9+
while True:
10+
if inf_norm(v2) < inf_norm(v1):
11+
v1, v2 = v2, v1
12+
m = round( (v1[0]*v2[0] + v1[1]*v2[1]) / inf_norm(v1)**2 )
13+
if m == 0:
14+
return (v1, v2)
15+
v2[0] = v2[0] - m*v1[0]
16+
v2[1] = v2[1] - m*v1[1]
17+
18+
def find_split_constants_gauss():
19+
"""Find constants for secp256k1_scalar_split_lamdba using gauss reduction."""
20+
(v11, v12), (v21, v22) = gauss_reduction([0, N], [1, int(LAMBDA)])
21+
22+
# We use related vectors in secp256k1_scalar_split_lambda
23+
A1, B1 = -v21, -v11
24+
A2, B2 = v22, -v21
25+
26+
return (A1, B1, A2, B2)
27+
28+
def find_split_constants_explicit_tof():
29+
"""Find constants for secp256k1_scalar_split_lamdba using the trace of Frobenius.
30+
31+
See Benjamin Smith: "Easy scalar decompositions for efficient scalar multiplication on
32+
elliptic curves and genus 2 Jacobians" (https://eprint.iacr.org/2013/672), Example 2
33+
"""
34+
assert P % 3 == 1 # The paper says P % 3 == 2 but that appears to be a mistake, see [10].
35+
assert C.j_invariant() == 0
36+
37+
t = C.trace_of_frobenius()
38+
39+
c = Integer(sqrt((4*P - t**2)/3))
40+
A1 = Integer((t - c)/2 - 1)
41+
B1 = c
42+
43+
A2 = Integer((t + c)/2 - 1)
44+
B2 = Integer(1 - (t - c)/2)
45+
46+
# We use a negated b values in secp256k1_scalar_split_lambda
47+
B1, B2 = -B1, -B2
48+
49+
return A1, B1, A2, B2
50+
51+
A1, B1, A2, B2 = find_split_constants_explicit_tof()
52+
53+
# For extra fun, use an independent method to recompute the constants
54+
assert (A1, B1, A2, B2) == find_split_constants_gauss()
55+
56+
assert A1*B2 - B1*A2 == N
57+
58+
# Check that (A1, B1) and (A2, B2) are in the kernel
59+
assert Z(A1 + LAMBDA*B1) == Z(0)
60+
assert Z(A2 + LAMBDA*B2) == Z(0)
61+
62+
# Check that they're linearly independent
63+
assert not (ZZ^2).are_linearly_dependent([[A1, B1], [A2, B2]])
64+
65+
# Check that their components are short enough
66+
assert (A1 + A2)/2 < sqrt(N)
67+
assert B1 < sqrt(N)
68+
assert B2 < sqrt(N)
69+
70+
G1 = round((2**384)*B2/N)
71+
G2 = round((2**384)*(-B1)/N)
72+
73+
def rnddiv2(v):
74+
if v & 1:
75+
v += 1
76+
return v >> 1
77+
78+
def scalar_lambda_split(k):
79+
"""Equivalent to secp256k1_scalar_lambda_split()."""
80+
k = int(k)
81+
c1 = rnddiv2((k * G1) >> 383)
82+
c2 = rnddiv2((k * G2) >> 383)
83+
c1 = (c1 * -B1) % N
84+
c2 = (c2 * -B2) % N
85+
r2 = (c1 + c2) % N
86+
r1 = (k + r2 * -LAMBDA) % N
87+
return (r1, r2)
88+
89+
print(' A1 =', hex(A1))
90+
print(' -B1 =', hex(-B1))
91+
print(' A2 =', hex(A2))
92+
print(' -B2 =', hex(-B2))
93+
print(' =', hex(Z(-B2)))
94+
print(' -LAMBDA =', hex(-LAMBDA))
95+
96+
print(' G1 =', hex(G1))
97+
print(' G2 =', hex(G2))

sage/secp256k1_params.sage

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ assert is_prime(N)
2727

2828
assert BETA != F(1)
2929
assert BETA^3 == F(1)
30+
assert BETA^2 + BETA + 1 == 0
3031

3132
assert LAMBDA != Z(1)
3233
assert LAMBDA^3 == Z(1)
34+
assert LAMBDA^2 + LAMBDA + 1 == 0

0 commit comments

Comments
 (0)