264 lines
7.3 KiB
Python
264 lines
7.3 KiB
Python
|
# Constantine
|
|||
|
# Copyright (c) 2018-2019 Status Research & Development GmbH
|
|||
|
# Copyright (c) 2020-Present Mamy André-Ratsimbazafy
|
|||
|
# Licensed and distributed under either of
|
|||
|
# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
|
|||
|
# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
|
|||
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
|||
|
|
|||
|
# ############################################################
|
|||
|
#
|
|||
|
# BLS12-377
|
|||
|
# Constant-time Square Root
|
|||
|
#
|
|||
|
# ############################################################
|
|||
|
|
|||
|
# Parameters
|
|||
|
x = 3 * 2^46 * (7 * 13 * 499) + 1
|
|||
|
p = (x - 1)^2 * (x^4 - x^2 + 1)//3 + x
|
|||
|
r = x^4 - x^2 + 1
|
|||
|
t = x + 1
|
|||
|
print('x : ' + x.hex())
|
|||
|
print('p : ' + p.hex())
|
|||
|
print('r : ' + r.hex())
|
|||
|
print('t : ' + t.hex())
|
|||
|
|
|||
|
def modCheck(p, pow):
|
|||
|
## Find q mod 2^s != 1
|
|||
|
q = p^pow
|
|||
|
s = 4
|
|||
|
while q % s == 1:
|
|||
|
s *= 2
|
|||
|
if s > q:
|
|||
|
raise ValueError('Uh Oh')
|
|||
|
if pow == 1:
|
|||
|
print(f'Found: p mod {s} = {q % s}')
|
|||
|
else:
|
|||
|
print(f'Found: p^{pow} mod {s} = {q % s}')
|
|||
|
|
|||
|
modCheck(p, 1) # Found: p mod 140737488355328 = 70368744177665
|
|||
|
modCheck(p, 2) # Found: p^2 mod 281474976710656 = 140737488355329
|
|||
|
|
|||
|
# On Fp
|
|||
|
# a^((p-70368744177665+140737488355328)/140737488355328)
|
|||
|
# would lead to a square root but there would be
|
|||
|
# log2(140737488355328)-1 candidates
|
|||
|
# which must be checked constant time
|
|||
|
|
|||
|
def precomp_tonelli_shanks(p):
|
|||
|
## Precompute constants for
|
|||
|
## constant-time Tonelli Shanks algorithm
|
|||
|
## with q = p^pow returns:
|
|||
|
## 1. c1, the largest integer such that 2^c1 divides q - 1.
|
|||
|
## 2. c2 = (q - 1) / (2^c1) in ℕ
|
|||
|
## 3. c3 = (c2 - 1) / 2 in ℕ
|
|||
|
## 4. c4, a non-square value in Fq
|
|||
|
## 5. c5 = c4^c2 in Fq
|
|||
|
q = p
|
|||
|
c1 = 0
|
|||
|
c2 = q-1
|
|||
|
while c2 & 1 == 0:
|
|||
|
c2 >>= 1
|
|||
|
c1 += 1
|
|||
|
c3 = (c2 - 1) // 2
|
|||
|
c4 = 1
|
|||
|
while kronecker(c4, q) == 1:
|
|||
|
c4 += 1
|
|||
|
c5 = GF(p)(c4)^c2
|
|||
|
return (c1,c2,c3,c4,c5)
|
|||
|
|
|||
|
def ccopy(a, b, ctl):
|
|||
|
## `b` if `ctl` is true, `a` if false
|
|||
|
return int(not(bool(ctl)))*a + int(bool(ctl))*b
|
|||
|
|
|||
|
def sqrt_tonelli_shanks(x, p):
|
|||
|
## Returns z = x² (p^pow)
|
|||
|
(c1, c2, c3, c4, c5) = precomp_tonelli_shanks(p)
|
|||
|
|
|||
|
x = GF(p)(x)
|
|||
|
|
|||
|
z = x^c3
|
|||
|
t = z*z*x
|
|||
|
z *= x
|
|||
|
b = t
|
|||
|
c = c5
|
|||
|
for i in range(c1, 1, -1): # c1 ... 2
|
|||
|
for j in range(1, i-1): # 1 ... i-2
|
|||
|
b *= b
|
|||
|
z = ccopy(z, z*c, b != 1)
|
|||
|
c *= c
|
|||
|
t = ccopy(t, t*c, b != 1)
|
|||
|
b = t
|
|||
|
return z
|
|||
|
|
|||
|
# for a in range(2, 30):
|
|||
|
# if kronecker(a, p) != 1:
|
|||
|
# continue
|
|||
|
# # print(f'{a}^(p-1)/2 = ' + str(GF(p)(a)^((p-1)/2)))
|
|||
|
# print(f'{a} is a quadratic residue mod p')
|
|||
|
# b = sqrt_tonelli_shanks(a, p)
|
|||
|
# # print(f'{b}² = {a} mod p')
|
|||
|
# # print('b*b = ' + str(b*b))
|
|||
|
# assert b*b == a
|
|||
|
|
|||
|
# Optimized Tonelli Shanks
|
|||
|
# --------------------------------------------------------
|
|||
|
|
|||
|
# Finite fields
|
|||
|
Fp = GF(p)
|
|||
|
K2.<u> = PolynomialRing(Fp)
|
|||
|
Fp2.<beta> = Fp.extension(u^2+5)
|
|||
|
|
|||
|
def precomp_ts(Fq):
|
|||
|
## From q = p^m with p the prime characteristic of the field Fp^m
|
|||
|
##
|
|||
|
## Returns (s, e) such as
|
|||
|
## q == s * 2^e + 1
|
|||
|
s = Fq.order() - 1
|
|||
|
e = 0
|
|||
|
while s & 1 == 0:
|
|||
|
s >>= 1
|
|||
|
e += 1
|
|||
|
return s, e
|
|||
|
|
|||
|
def find_any_qnr(Fq):
|
|||
|
## Find a quadratic Non-Residue
|
|||
|
## in GF(p^m)
|
|||
|
qnr = Fq(Fq.gen())
|
|||
|
r = Fq.order()
|
|||
|
while qnr.is_square():
|
|||
|
qnr += 1
|
|||
|
return qnr
|
|||
|
|
|||
|
def sqrt_exponent_precomp(Fq, e):
|
|||
|
## Returns precomputation a^((q-1-2^e)/(2*2^e))
|
|||
|
##
|
|||
|
## With 2^e the largest power of 2 that divides q-1
|
|||
|
##
|
|||
|
## For all sqrt related functions
|
|||
|
## - legendre symbol
|
|||
|
## - SQRT
|
|||
|
## - inverse SQRT
|
|||
|
r = Fq.order()
|
|||
|
precomp = (r - 1) >> e # (q-1) / 2^e
|
|||
|
precomp = (precomp - 1) >> 1 # ((q-1) / 2^e) - 1) / 2 = (q-1-2^e)/2^e / 2
|
|||
|
return precomp
|
|||
|
|
|||
|
s, e = precomp_ts(Fp)
|
|||
|
qnr = find_any_qnr(Fp)
|
|||
|
root_unity = qnr^s
|
|||
|
exponent = sqrt_exponent_precomp(Fp, e)
|
|||
|
|
|||
|
# print('tonelli s: 0x' + Integer(s).hex())
|
|||
|
print('tonelli e (2-adicity): ' + str(e))
|
|||
|
print('tonelli root: 0x' + Integer(root_unity).hex())
|
|||
|
print('tonelli exponent: 0x' + Integer(exponent).hex())
|
|||
|
|
|||
|
def legendre_symbol_impl(a, e, a_pre_exp):
|
|||
|
## Legendre symbol χ(a) = a^(q-1)/2
|
|||
|
## -1 if a is non-square
|
|||
|
## 0 if a is 0
|
|||
|
## 1 if a is square
|
|||
|
##
|
|||
|
## a_pre_exp = a^((q-1-2^e)/(2*2^e))
|
|||
|
## with
|
|||
|
## s and e, precomputed values
|
|||
|
## such as q == s * 2^e + 1
|
|||
|
##
|
|||
|
## a_pre_exp is used in square root
|
|||
|
## and or inverse square root computation
|
|||
|
##
|
|||
|
## for fused operations
|
|||
|
r = a_pre_exp * a_pre_exp # a^((q-1-2^e)/2^e) = a^((q-1)/2^e - 1)
|
|||
|
r *= a # a^((q-1)/2^e)
|
|||
|
for i in range(0, e-1):
|
|||
|
r *= r # a^((q-1)/2)
|
|||
|
|
|||
|
return r
|
|||
|
|
|||
|
def legendre_symbol(a):
|
|||
|
a_pre_exp = a^exponent
|
|||
|
return legendre_symbol_impl(a, e, a_pre_exp)
|
|||
|
|
|||
|
for a in range(20):
|
|||
|
assert kronecker(a, p) == legendre_symbol(GF(p)(a))
|
|||
|
|
|||
|
def sqrt_tonelli_shanks_impl(a, a_pre_exp, s, e, root_of_unity):
|
|||
|
## Square root for any `a` in a field of prime characteristic p
|
|||
|
##
|
|||
|
## a_pre_exp = a^((q-1-2^e)/(2*2^e))
|
|||
|
## with
|
|||
|
## s and e, precomputed values
|
|||
|
## such as q == s * 2^e + 1
|
|||
|
z = a_pre_exp
|
|||
|
t = z*z*a
|
|||
|
r = z * a
|
|||
|
b = t
|
|||
|
root = root_of_unity
|
|||
|
for i in range(e, 1, -1): # e .. 2
|
|||
|
for j in range(1, i-1): # 1 .. i-2
|
|||
|
b *= b
|
|||
|
doCopy = b != 1
|
|||
|
r = ccopy(r, r * root, doCopy)
|
|||
|
root *= root
|
|||
|
t = ccopy(t, t * root, doCopy)
|
|||
|
b = t
|
|||
|
return r
|
|||
|
|
|||
|
def sqrt_tonelli_shanks_opt(a):
|
|||
|
a_pre_exp = a^exponent
|
|||
|
return sqrt_tonelli_shanks_impl(a, a_pre_exp, s, e, root_unity)
|
|||
|
|
|||
|
# for a in range(2, 30):
|
|||
|
# if kronecker(a, p) != 1:
|
|||
|
# continue
|
|||
|
# # print(f'{a}^(p-1)/2 = ' + str(GF(p)(a)^((p-1)/2)))
|
|||
|
# print(f'{a} is a quadratic residue mod p')
|
|||
|
# b = sqrt_tonelli_shanks_opt(GF(p)(a))
|
|||
|
# # print(f'{b}² = {a} mod p')
|
|||
|
# # print('b*b = ' + str(b*b))
|
|||
|
# assert b*b == a
|
|||
|
|
|||
|
def sqrt_inv_sqrt_tonelli_shanks_impl(a, a_pre_exp, s, e, root_of_unity):
|
|||
|
## Square root and inverse square root for any `a` in a field of prime characteristic p
|
|||
|
##
|
|||
|
## a_pre_exp = a^((q-1-2^e)/(2*2^e))
|
|||
|
## with
|
|||
|
## s and e, precomputed values
|
|||
|
## such as q == s * 2^e + 1
|
|||
|
|
|||
|
# Implementation
|
|||
|
# 1/√a * a = √a
|
|||
|
# Notice that in Tonelli Shanks, the result `r` is bootstrapped by "z*a"
|
|||
|
# We bootstrap it instead by just z to get invsqrt for free
|
|||
|
|
|||
|
z = a_pre_exp
|
|||
|
t = z*z*a
|
|||
|
r = z
|
|||
|
b = t
|
|||
|
root = root_of_unity
|
|||
|
for i in range(e, 1, -1): # e .. 2
|
|||
|
for j in range(1, i-1): # 1 .. i-2
|
|||
|
b *= b
|
|||
|
doCopy = b != 1
|
|||
|
r = ccopy(r, r * root, doCopy)
|
|||
|
root *= root
|
|||
|
t = ccopy(t, t * root, doCopy)
|
|||
|
b = t
|
|||
|
return r*a, r
|
|||
|
|
|||
|
def sqrt_invsqrt_tonelli_shanks_opt(a):
|
|||
|
a_pre_exp = a^exponent
|
|||
|
return sqrt_inv_sqrt_tonelli_shanks_impl(a, a_pre_exp, s, e, root_unity)
|
|||
|
|
|||
|
for a in range(2, 30):
|
|||
|
if kronecker(a, p) != 1:
|
|||
|
continue
|
|||
|
# print(f'{a}^(p-1)/2 = ' + str(GF(p)(a)^((p-1)/2)))
|
|||
|
print(f'{a} is a quadratic residue mod p')
|
|||
|
b, invb = sqrt_invsqrt_tonelli_shanks_opt(GF(p)(a))
|
|||
|
# print(f'{b}² = {a} mod p')
|
|||
|
# print('b*b = ' + str(b*b))
|
|||
|
assert b*b == a
|
|||
|
assert invb*a == b
|