-
Notifications
You must be signed in to change notification settings - Fork 13
/
sqrt_alg.sage
126 lines (98 loc) · 3.7 KB
/
sqrt_alg.sage
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
p = 8444461749428370424248824938781546531375899335154063827935233455917409239041
F = GF(p)
z = F(2841681278031794617739547238867782961338435681360110683443920362658525667816)
# First, solve p - 1 = 2**n * m where n >= 1 and m odd
n = 47
m = (p - 1) / 2**n
# Next, compute g = z^m where z is a quadratic non-residue in Fp
g = z ** m
# Finally, define l_0... l_{k-1} > 0 such that l_0 + .. + l_{k-1} = n-1
k = 6
l = [7, 7, 8, 8, 8, 8]
w = 8
# g powers lookup table: Indexed by power of two, then nu
powers_needed_for_t_i = [0, 8, 16, 24, 32]
powers_needed_for_t_k_over_2 = [40]
# This is Table 1 and 2 from Sarkar 2020 (combined)
gtab = {}
for power_of_two in powers_needed_for_t_i + powers_needed_for_t_k_over_2:
gtab_i = []
for nu in range(0, 2**w):
exponent = nu * F(2) ** power_of_two
gtab_i.append(g**exponent)
gtab[power_of_two] = gtab_i
# s lookup table: Indexed by g^{\nu * 2^{n-l}}
# This is Table 3 from Sarkar 2020
s_lookup_table = {}
for nu in range(0, 2**w):
s_lookup_table[g**(-1 * nu * 2**(n-w))] = nu
def sqrt_ratio(num, den):
s = den**(2**n - 1)
t = s**2 * den
w = (num * t)**((m-1)/2) * s
v = w * den;
uv = w * num;
assert v == (num / den)**((m - 1) / 2)
assert uv == (num / den)**((m + 1) / 2)
return table_based_findSqRoot_sarkar(uv, v)
def table_based_findSqRoot_sarkar(uv, v):
"""Implements decaf377 Sarkar 2020 based method"""
x = uv * v # Such that x = u**m
x5 = x
x4 = x5 ** (2**l[5])
x3 = x4 ** (2**l[4])
x2 = x3 ** (2**l[3])
x1 = x2 ** (2**l[2])
x0 = x1 ** (2**l[1])
# i = 0
# Here we want to look up s_0 = q_0 * g**{n-7}, but our table has
# powers of g**{n-8}, so we're actually looking up q_prime = 2*q_0.
q_0_prime = s_lookup_table[x0] # Since x0 = alpha0
t = q_0_prime
# i = 1
alpha_1 = x1 * gtab[32][t]
#breakpoint()
q_1_prime = s_lookup_table[alpha_1]
# The left shift values come from the final expression for t:
# t_6 = q_0_prime + q_1_prime * 2^7 + ... + q_5 * 2^39
t += q_1_prime << 7
# i = 2
alpha_2 = x2 * gtab[24][t & 0xFF] * gtab[32][t >> 8]
assert (t >> 8) & 0xFF == q_1_prime // 2
q_2 = s_lookup_table[alpha_2]
t += q_2 << 15
# i = 3
alpha_3 = x3 * gtab[16][t & 0xFF] * gtab[24][(t >> 8) & 0xFF] * gtab[32][(t >> 16) & 0xFF]
assert (t >> 16) & 0xFF == q_2 // 2
q_3 = s_lookup_table[alpha_3]
t += q_3 << 23
# i = 4
alpha_4 = x4 * gtab[8][t & 0xFF] * gtab[16][(t >> 8) & 0xFF] * gtab[24][(t >> 16) & 0xFF] * gtab[32][(t >> 24) & 0xFF]
assert (t >> 24) & 0xFF == q_3 // 2
q_4 = s_lookup_table[alpha_4]
t += q_4 << 31
# i = 5
alpha_5 = x5 * gtab[0][t & 0xFF] * gtab[8][(t >> 8) & 0xFF] * gtab[16][(t >> 16) & 0xFF] * gtab[24][(t >> 24) & 0xFF] * gtab[32][(t >> 32) & 0xFF]
assert (t >> 32) & 0xFF == q_4 // 2
q_5 = s_lookup_table[alpha_5]
t += q_5 << 39
assert t == q_0_prime + q_1_prime * 2**7 + q_2 * 2**15 + q_3 * 2**23 + q_4 * 2**31 + q_5 * 2**39 # Lemma 4 assertion
t = (t + 1) >> 1;
# Take 8 bits at a time, e.g. (t & 0xFF) is taking the last 8 bits of t to yield a value from 0-255, which are the allowed values in each g lookup table
gamma = gtab[0][t & 0xFF] * gtab[8][(t >> 8) & 0xFF] * gtab[16][(t >> 16) & 0xFF] * gtab[24][(t >> 24) & 0xFF] * gtab[32][(t >> 32) & 0xFF] * gtab[40][(t >> 40)]
y = uv * gamma
if q_0_prime % 2 != 0:
y *= z**((1-m) / 2)
return y
def test_sqrt(n):
for _ in range(n):
num = F.random_element()
den = F(1)
u = num / den
res = sqrt_ratio(num, den)
if u.is_square():
assert res**2 == u
else:
assert res**2 == u * z
#test_sqrt(10000)
test_sqrt(100)