2020-12-29 04:53:13 -08:00
#!/usr/bin/env sage
2020-12-30 19:35:50 -08:00
# Simplified SWU for a = 0 as described in [WB2019] <https://eprint.iacr.org/2019/403> and
2020-12-29 04:53:13 -08:00
# <https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.html#name-simplified-swu-for-ab-0-2>.
import sys
from math import ceil , log
from struct import pack
2021-02-21 13:11:19 -08:00
from pyblake2 import blake2b
from hashlib import sha256
2020-12-29 04:53:13 -08:00
if sys . version_info [ 0 ] == 2 :
range = xrange
as_byte = ord
else :
as_byte = lambda x : x
2021-02-21 13:11:19 -08:00
def as_bytes ( x ) :
# <https://stackoverflow.com/a/29182294/393146>
return bytes ( bytearray ( x ) )
2020-12-30 19:35:50 -08:00
load ( ' squareroottab.sage ' )
2020-12-29 04:53:13 -08:00
2021-01-01 18:23:55 -08:00
DEBUG = True
2021-01-12 17:11:20 -08:00
VERBOSE = False
OP_COUNT = False
2020-12-29 04:53:13 -08:00
# E: a short Weierstrass elliptic curve
def find_z_sswu ( E ) :
( 0 , 0 , 0 , A , B ) = E . a_invariants ( )
F = E . base_field ( )
R . < x > = F [ ] # Polynomial ring over F
g = x ^ 3 + F ( A ) * x + F ( B ) # y^2 = g(x) = x^3 + A * x + B
2021-04-21 04:35:27 -07:00
# <https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.html#name-finding-z-for-the-shallue-va> is
# ambiguous about whether to start with ctr = F.gen() or ctr = 1. In fact they are specified to be the same,
# since F is a prime field.
# <https://doc.sagemath.org/html/en/reference/finite_rings/sage/rings/finite_rings/finite_field_prime_modn.html#sage.rings.finite_rings.finite_field_prime_modn.FiniteField_prime_modn.gen>,
# The note in the I-D ("NOTE: if init_ctr=1 fails to find Z, try setting it to F.gen()") could only make a
# difference for extension fields that are constructed with an explicit modulus.
ctr = 1
2020-12-29 04:53:13 -08:00
while True :
for Z_cand in ( F ( ctr ) , F ( - ctr ) ) :
2020-12-30 19:35:50 -08:00
if is_good_Z ( F , g , A , B , Z_cand ) :
2020-12-29 04:53:13 -08:00
return Z_cand
ctr + = 1
2020-12-30 19:35:50 -08:00
def is_good_Z ( F , g , A , B , Z ) :
# Criterion 1: Z is non-square in F.
if Z . is_square ( ) :
return False
2020-12-29 04:53:13 -08:00
2020-12-30 19:35:50 -08:00
# Criterion 2: Z != -1 in F.
if Z == F ( - 1 ) :
return False
# Criterion 3: g(x) - Z is irreducible over F.
if not ( g - Z ) . is_irreducible ( ) :
return False
# Criterion 4: g(B / (Z * A)) is square in F.
if not g ( F ( B ) / ( Z * F ( A ) ) ) . is_square ( ) :
return False
return True
2021-01-02 13:01:33 -08:00
# Point in Chudnovsky coordinates (Jacobian with Z^2 and Z^3 cached).
2021-01-01 18:22:01 -08:00
class ChudnovskyPoint :
2021-02-21 13:11:19 -08:00
def __init__ ( self , E , x , y , z , z2 = None , z3 = None ) :
if z2 is None :
z2 = z ^ 2
if z3 is None :
z3 = z ^ 3
2021-01-12 17:11:20 -08:00
if DEBUG :
( 0 , 0 , 0 , A , B ) = E . a_invariants ( )
assert z2 == z ^ 2
assert z3 == z ^ 3
assert y ^ 2 == x ^ 3 + A * x * z ^ 4 + B * z ^ 6
2021-01-01 18:22:01 -08:00
( self . x , self . y , self . z , self . z2 , self . z3 ) = ( x , y , z , z2 , z3 )
def add ( self , other , E , c ) :
2021-01-02 13:01:33 -08:00
( 0 , 0 , 0 , A , B ) = E . a_invariants ( )
# Unified addition on y^2 = x^3 + Ax + B with Chudnovsky input and output.
2021-01-01 18:22:01 -08:00
( X1 , Y1 , Z1 , Z1_2 , Z1_3 ) = ( self . x , self . y , self . z , self . z2 , self . z3 )
( X2 , Y2 , Z2 , Z2_2 , Z2_3 ) = ( other . x , other . y , other . z , other . z2 , other . z3 )
2021-01-02 13:01:33 -08:00
assert Z1 != 0 and Z2 != 0
# <https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-add-2007-bl>
2021-01-01 18:22:01 -08:00
U1 = c . mul ( X1 , Z2_2 )
U2 = c . mul ( X2 , Z1_2 )
S1 = c . mul ( Y1 , Z2_3 )
S2 = c . mul ( Y2 , Z1_3 )
2021-01-02 13:01:33 -08:00
H = U2 - U1
# now unify doubling <https://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl>:
# XX = c.sqr(X)
# YY = c.sqr(Y)
# YYYY = c.sqr(YY)
# S = 2*(c.sqr(X1 + YY) - XX - YYYY)
# = 4*X1*YY
# M = 3*XX + c.mul(A, Z1_2)
# X3 = c.sqr(M) - 2*S
# Y3 = M*(S - X3) - 8*YYYY
# Z3 = c.sqr(Y1 + Z1) - YY - ZZ
#
# with the rest of addition:
# I = c.sqr(2*H)
# J = c.mul(H, I)
# r = 2*(S2-S1)
# V = c.mul(U1, I)
# X3 = c.sqr(r) - J - 2*V
# Y3 = r*(V - X3) - 2*c.mul(S1, J)
# Z3 = c.mul((c.sqr(Z1 + Z2) - Z1_2 - Z2_2), H)
r = 2 * ( S2 - S1 )
X_or_r = select_z_nz ( H , X1 , r )
Y_or_H = select_z_nz ( H , Y1 , H )
XX_or_rr = c . sqr ( X_or_r )
if DEBUG : assert XX_or_rr == X1 ^ 2 if H == 0 else r ^ 2
YY_or_HH = c . sqr ( Y_or_H )
if DEBUG : assert YY_or_HH == Y1 ^ 2 if H == 0 else H ^ 2
YYY_or_HHH = c . mul ( Y_or_H , YY_or_HH )
if DEBUG : assert YYY_or_HHH == Y1 ^ 3 if H == 0 else H ^ 3
YYYY_or_S1HHH = c . mul ( select_z_nz ( H , Y_or_H , S1 ) , YYY_or_HHH )
if DEBUG : assert YYYY_or_S1HHH == Y1 ^ 4 if H == 0 else S1 * H ^ 3
S_or_V = 4 * c . mul ( select_z_nz ( H , X1 , U1 ) , YY_or_HH )
if DEBUG : assert S_or_V == 4 * X1 * Y1 ^ 2 if H == 0 else U1 * ( 4 * H ^ 3 )
J = select_z_nz ( H , 0 , 4 * YYY_or_HHH )
# W = { (Z + Y)^2 - Z^2 = 2*Y*Z + Y^2 for doubling
# { (Z1 + Z2)^2 - Z1^2 = 2*Z1*Z2 + Z2^2 for addition
W = c . sqr ( Z1 + select_z_nz ( H , Y1 , Z2 ) ) - Z1_2
if DEBUG : assert W == 2 * Y1 * Z1 + Y1 ^ 2 if H == 0 else 2 * Z1 * Z2 + Z2 ^ 2
# Another option would be to multiply Z1_2 by A if that's faster than squaring.
Z1_4 = c . sqr ( Z1_2 )
AZ1_4_or_Z3 = c . mul ( select_z_nz ( H , A , H ) , select_z_nz ( H , Z1_4 , W - Z2_2 ) )
if DEBUG : assert AZ1_4_or_Z3 == A * Z1 ^ 4 if H == 0 else 2 * Z1 * Z2 * H
M_or_r = select_z_nz ( H , 3 * XX_or_rr + AZ1_4_or_Z3 , r )
if DEBUG : assert M_or_r == 3 * X1 ^ 2 + A * Z1 ^ 4 if H == 0 else r
X3 = c . sqr ( M_or_r ) - J - 2 * S_or_V
Y3 = c . mul ( M_or_r , S_or_V - X3 ) - 8 * YYYY_or_S1HHH
# If U1 + U2 = 0 then the result is the point at infinity.
Z3 = select_z_nz ( U1 + U2 , 0 , select_z_nz ( H , W - YY_or_HH , AZ1_4_or_Z3 ) )
2021-01-01 18:22:01 -08:00
Z3_2 = c . sqr ( Z3 )
Z3_3 = c . mul ( Z3_2 , Z3 )
2021-01-12 17:11:20 -08:00
R = ChudnovskyPoint ( E , X3 , Y3 , Z3 , Z3_2 , Z3_3 )
2021-01-01 18:22:01 -08:00
if DEBUG : assert R . to_sage ( E ) == self . to_sage ( E ) + other . to_sage ( E )
return R
def to_sage ( self , E ) :
return E ( ( self . x / self . z2 , self . y / self . z3 ) )
2021-04-27 06:24:13 -07:00
def to_affine ( self , E ) :
return self . to_sage ( E ) . xy ( )
2021-01-01 18:22:01 -08:00
def to_jacobian ( self ) :
return ( self . x , self . y , self . z )
def __repr__ ( self ) :
2021-02-21 13:11:19 -08:00
return " ChudnovskyPoint { \n 0x %064x \n : 0x %064x \n : 0x %064x \n : 0x %064x \n : 0x %064x \n } " % ( int ( self . x ) , int ( self . y ) , int ( self . z ) , int ( self . z2 ) , int ( self . z3 ) )
2021-01-01 18:22:01 -08:00
2020-12-30 19:35:50 -08:00
assert p == 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001
assert q == 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001
Fp = GF ( p )
Fq = GF ( q )
2021-02-21 13:11:19 -08:00
IsoEp_A = 10949663248450308183708987909873589833737836120165333298109615750520499732811
IsoEq_A = 17413348858408915339762682399132325137863850198379221683097628341577494210225
IsoEp_B = 1265
IsoEq_B = 1265
IsoEp = EllipticCurve ( Fp , [ IsoEp_A , IsoEp_B ] )
IsoEq = EllipticCurve ( Fq , [ IsoEq_A , IsoEq_B ] )
Ep = EllipticCurve ( Fp , [ 0 , 5 ] )
Eq = EllipticCurve ( Fq , [ 0 , 5 ] )
2020-12-29 04:53:13 -08:00
2021-02-21 13:11:19 -08:00
k = 256
2020-12-30 19:35:50 -08:00
Lp = ( len ( format ( p , ' b ' ) ) + k + 7 ) / / 8
Lq = ( len ( format ( q , ' b ' ) ) + k + 7 ) / / 8
2021-02-21 13:11:19 -08:00
assert Lp == 64 and Lq == 64
CHUNKLEN = Lp
2020-12-30 19:35:50 -08:00
2021-02-21 13:11:19 -08:00
IsoEpZ = find_z_sswu ( IsoEp )
IsoEqZ = find_z_sswu ( IsoEq )
assert IsoEpZ == Mod ( - 13 , p )
assert IsoEqZ == Mod ( - 13 , q )
2020-12-30 19:35:50 -08:00
2020-12-29 04:53:13 -08:00
def select_z_nz ( s , ifz , ifnz ) :
# This should be constant-time in a real implementation.
return ifz if ( s == 0 ) else ifnz
2021-01-01 16:50:42 -08:00
def map_to_curve_simple_swu ( F , E , Z , u , c ) :
2021-02-21 13:11:19 -08:00
if VERBOSE : print ( " map_to_curve(0x %064x ) " % ( u , ) )
2020-12-29 04:53:13 -08:00
# would be precomputed
2020-12-31 19:36:05 -08:00
h = F . g
2020-12-29 04:53:13 -08:00
( 0 , 0 , 0 , A , B ) = E . a_invariants ( )
mBdivA = - B / A
BdivZA = B / ( Z * A )
2020-12-29 09:52:35 -08:00
Z2 = Z ^ 2
2020-12-31 05:45:35 -08:00
assert ( Z / h ) . is_square ( )
theta = sqrt ( Z / h )
2020-12-29 04:53:13 -08:00
2021-01-01 16:50:42 -08:00
# 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
#
# We use the "Avoiding inversions" optimization in [WB2019, section 4.2]
# (not to be confused with section 4.3):
#
# here [WB2019]
# ------- ---------------------------------
# Z \xi
# u t
# Z * u^2 \xi * t^2 (called u, confusingly)
# x1 X_0(t)
# x2 X_1(t)
# gx1 g(X_0(t))
# gx2 g(X_1(t))
#
# Using the "here" names:
2021-01-01 18:22:01 -08:00
# x1 = N_x1/D = [B*(Z^2 * u^4 + Z * u^2 + 1)] / [-A*(Z^2 * u^4 + Z * u^2]
# gx1 = U/V = [N_x1^3 + A * N_x1 * D^2 + B * D^3] / D^3
2021-01-01 16:50:42 -08:00
# Z and B are small so we don't count multiplication by them as a mul; A is large.
Zu2 = Z * c . sqr ( u )
ta = c . sqr ( Zu2 ) + Zu2
2021-01-01 18:22:01 -08:00
N_x1 = B * ( ta + 1 )
2021-02-21 13:10:23 -08:00
D = c . mul ( A , select_z_nz ( ta , Z , - ta ) )
2021-01-01 18:22:01 -08:00
N2_x1 = c . sqr ( N_x1 )
2021-01-01 16:50:42 -08:00
D2 = c . sqr ( D )
D3 = c . mul ( D2 , D )
2021-02-21 13:10:23 -08:00
U = c . mul ( N2_x1 + c . mul ( A , D2 ) , N_x1 ) + B * D3
2021-01-01 16:50:42 -08:00
if DEBUG :
2021-01-01 18:22:01 -08:00
x1 = N_x1 / D
2021-02-21 13:10:23 -08:00
gx1 = U / D3
2021-01-01 16:50:42 -08:00
tv1 = ( 0 if ta == 0 else 1 / ta )
assert x1 == ( BdivZA if tv1 == 0 else mBdivA * ( 1 + tv1 ) )
assert gx1 == x1 ^ 3 + A * x1 + B
# 5. x2 = Z * u^2 * x1
2021-01-01 18:22:01 -08:00
N_x2 = c . mul ( Zu2 , N_x1 ) # same D
2021-01-01 16:50:42 -08:00
# 6. gx2 = x2^3 + A * x2 + B [optimized out; see below]
# 7. If is_square(gx1), set x = x1 and y = sqrt(gx1)
# 8. Else set x = x2 and y = sqrt(gx2)
2021-02-21 13:10:23 -08:00
( y1 , zero_if_gx1_square ) = F . sarkar_divsqrt ( U , D3 , c )
2021-02-21 13:11:19 -08:00
if VERBOSE : print ( " zero_if_gx1_square = %064x " % ( zero_if_gx1_square , ) )
2021-01-01 16:50:42 -08:00
# This magic also comes from a generalization of [WB2019, section 4.2].
#
# The Sarkar square root algorithm with input s gives us a square root of
# h * s for free when s is not square, where h is a fixed nonsquare.
# We know that Z/h is a square since both Z and h are nonsquares.
# Precompute \theta as a square root of Z/h, or choose Z = h so that \theta = 1.
#
# We have gx2 = g(Z * u^2 * x1) = Z^3 * u^6 * gx1
# = (Z * u^3)^2 * (Z/h * h * gx1)
# = (Z * \theta * u^3)^2 * (h * gx1)
#
# When gx1 is not square, y1 is a square root of h * gx1, and so Z * \theta * u^3 * y1
# is a square root of gx2. Note that we don't actually need to compute gx2.
y2 = c . mul ( theta , c . mul ( Zu2 , c . mul ( u , y1 ) ) )
if DEBUG and zero_if_gx1_square != 0 :
2021-01-01 18:23:55 -08:00
x2 = N_x2 / D
2021-01-01 16:50:42 -08:00
assert y1 ^ 2 == h * gx1 , ( y1_2 , Z , gx1 )
assert y2 ^ 2 == x2 ^ 3 + A * x2 + B , ( y2 , x2 , A , B )
2021-01-01 18:22:01 -08:00
N_x = select_z_nz ( zero_if_gx1_square , N_x1 , N_x2 )
2021-01-01 16:50:42 -08:00
y = select_z_nz ( zero_if_gx1_square , y1 , y2 )
# 9. If sgn0(u) != sgn0(y), set y = -y
y = select_z_nz ( ( int ( u ) % 2 ) - ( int ( y ) % 2 ) , y , - y )
2021-01-12 17:11:20 -08:00
if VERBOSE :
print ( " num_x = 0x %064x \n div = 0x %064x \n y = 0x %064x \n div3 = 0x %064x " % ( int ( N_x ) , int ( D ) , int ( y ) , int ( D3 ) ) )
return ChudnovskyPoint ( E , c . mul ( N_x , D ) , c . mul ( y , D3 ) , D , D2 , D3 )
2020-12-29 04:53:13 -08:00
# iso_Ep = Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + 10949663248450308183708987909873589833737836120165333298109615750520499732811*x + 1265 over Fp
2020-12-29 16:09:26 -08:00
def isop_map_affine ( x , y , c ) :
2020-12-29 04:53:13 -08:00
c . muls + = 2 + 1 + 1 + 2 + 1 + 1 + 2
# batch inversion
c . muls + = 3
c . invs + = 1
2020-12-29 16:09:26 -08:00
Nx = ( ( ( 6432893846517566412420610278260439325191790329320346825767705947633326140075 * x +
23989696149150192365340222745168215001509815558210986772351135915822265203574 ) * x +
10492611921771203378452795982353351666191589197598957448093274638589204800759 ) * x +
12865787693035132824841220556520878650383580658640693651535411895266652280192 )
Dx = ( ( x +
13271109177048389296812780941310096270046944650307955939477485891950613419807 ) * x +
22768321103861051515190775253992702316905399997697804654926324362758820947460 )
Ny = ( ( ( 11793638718615538422771118843477472096184948937087302513907460903994431256804 * x +
11994848074575096182670111372584107500754907779105493386175567957911132601787 ) * x +
28823569610051396102362669851238297121581474897215657071023781420043761726004 ) * x +
1072148974419594402070101713043406554198631721553391137627950991272221023311 ) * y
Dy = ( ( ( x +
5432652610908059517272798285879155923388888734491153551238890455750936314542 ) * x +
10408918692925056833786833257634153023990087029210292532869619559576527581706 ) * x +
28948022309329048855892746252171976963363056481941560715954676764349967629797 )
return ( Nx / Dx , Ny / Dy )
2021-01-12 17:11:20 -08:00
# The same isogeny iso_Ep but with input in Chudnovsky coordinates (Jacobian with z^2 and z^3)
2021-01-01 18:22:01 -08:00
# and output in Jacobian <https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html>,
2020-12-30 19:35:50 -08:00
# according to "Avoiding inversions" in [WB2019, section 4.3].
2021-01-01 18:22:01 -08:00
def isop_map_jacobian ( P , c ) :
( x , y , z , z2 , z3 ) = ( P . x , P . y , P . z , P . z2 , P . z3 )
2020-12-29 16:09:26 -08:00
z4 = c . sqr ( z2 )
z6 = c . sqr ( z3 )
2021-02-21 13:11:19 -08:00
if VERBOSE : print ( " IsoEp { x: 0x %064x , y: 0x %064x , z: 0x %064x } " % ( x , y , z ) )
2020-12-29 16:09:26 -08:00
Nx = ( ( ( 6432893846517566412420610278260439325191790329320346825767705947633326140075 * x +
23989696149150192365340222745168215001509815558210986772351135915822265203574 * z2 ) * x +
10492611921771203378452795982353351666191589197598957448093274638589204800759 * z4 ) * x +
12865787693035132824841220556520878650383580658640693651535411895266652280192 * z6 )
c . muls + = 6
Dx = ( ( z2 * x +
13271109177048389296812780941310096270046944650307955939477485891950613419807 * z4 ) * x +
22768321103861051515190775253992702316905399997697804654926324362758820947460 * z6 )
c . muls + = 4
Ny = ( ( ( 11793638718615538422771118843477472096184948937087302513907460903994431256804 * x +
11994848074575096182670111372584107500754907779105493386175567957911132601787 * z2 ) * x +
28823569610051396102362669851238297121581474897215657071023781420043761726004 * z4 ) * x +
1072148974419594402070101713043406554198631721553391137627950991272221023311 * z6 ) * y
c . muls + = 7
Dy = ( ( ( x +
5432652610908059517272798285879155923388888734491153551238890455750936314542 * z2 ) * x +
10408918692925056833786833257634153023990087029210292532869619559576527581706 * z4 ) * x +
28948022309329048855892746252171976963363056481941560715954676764349967629797 * z6 ) * z3
c . muls + = 6
2021-02-21 13:11:19 -08:00
if VERBOSE : print ( " num_x = 0x %064x \n div_x = 0x %064x \n num_y = 0x %064x \n div_y = 0x %064x " % ( Nx , Dx , Ny , Dy ) )
2020-12-29 16:09:26 -08:00
zo = c . mul ( Dx , Dy )
xo = c . mul ( c . mul ( Nx , Dy ) , zo )
yo = c . mul ( c . mul ( Ny , Dx ) , c . sqr ( zo ) )
assert isop_map_affine ( x / z2 , y / z3 , Cost ( ) ) == ( xo / zo ^ 2 , yo / zo ^ 3 )
return ( xo , yo , zo )
2021-01-12 17:11:20 -08:00
# iso_Eq = Isogeny of degree 3 from Elliptic Curve defined by y^2 = x^3 + 17413348858408915339762682399132325137863850198379221683097628341577494210225*x + 1265 over Fp
def isoq_map_affine ( x , y , c ) :
c . muls + = 2 + 1 + 1 + 2 + 1 + 1 + 2
# batch inversion
c . muls + = 3
c . invs + = 1
Nx = ( ( ( 25731575386070265649682441113041757300767161317281464337493104665238544842753 * x +
13377367003779316331268047403600734872799183885837485433911493934102207511749 ) * x +
11064082577423419940183149293632076317553812518550871517841037420579891210813 ) * x +
22515128462811482443472135973911537638171266152621281295306466582083726737451 )
Dx = ( ( x +
4604213796697651557841441623718706001740429044770779386484474413346415813353 ) * x +
9250006497141849826017568406346290940322373181457057184910582871723433210981 )
Ny = ( ( ( 8577191795356755216560813704347252433589053772427154779164368221746181614251 * x +
21162694656554182593580396827886355918081120183889566406795618341247785229923 ) * x +
11620280474556824258112134491145636201000922752744881519070727793732904824884 ) * x +
13937936667454727226911322269564285204582212380194126516142098360337545123123 ) * y
Dy = ( ( ( x +
21380331849711001764708535561664047484292171808126992769566582994216305194078 ) * x +
27750019491425549478052705219038872820967119544371171554731748615170299632943 ) * x +
28948022309329048855892746252171976963363056481941647379679742748393362947557 )
return ( Nx / Dx , Ny / Dy )
# The same isogeny iso_Eq but with input in Chudnovsky coordinates (Jacobian with z^2 and z^3)
# and output in Jacobian <https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html>,
# according to "Avoiding inversions" in [WB2019, section 4.3].
def isoq_map_jacobian ( P , c ) :
( x , y , z , z2 , z3 ) = ( P . x , P . y , P . z , P . z2 , P . z3 )
z4 = c . sqr ( z2 )
z6 = c . sqr ( z3 )
Nx = ( ( ( 25731575386070265649682441113041757300767161317281464337493104665238544842753 * x +
13377367003779316331268047403600734872799183885837485433911493934102207511749 * z2 ) * x +
11064082577423419940183149293632076317553812518550871517841037420579891210813 * z4 ) * x +
22515128462811482443472135973911537638171266152621281295306466582083726737451 * z6 )
c . muls + = 6
Dx = ( ( z2 * x +
4604213796697651557841441623718706001740429044770779386484474413346415813353 * z4 ) * x +
9250006497141849826017568406346290940322373181457057184910582871723433210981 * z6 )
c . muls + = 4
Ny = ( ( ( 8577191795356755216560813704347252433589053772427154779164368221746181614251 * x +
21162694656554182593580396827886355918081120183889566406795618341247785229923 * z2 ) * x +
11620280474556824258112134491145636201000922752744881519070727793732904824884 * z4 ) * x +
13937936667454727226911322269564285204582212380194126516142098360337545123123 * z6 ) * y
c . muls + = 7
Dy = ( ( ( x +
21380331849711001764708535561664047484292171808126992769566582994216305194078 * z2 ) * x +
27750019491425549478052705219038872820967119544371171554731748615170299632943 * z4 ) * x +
28948022309329048855892746252171976963363056481941647379679742748393362947557 * z6 ) * z3
c . muls + = 6
zo = c . mul ( Dx , Dy )
xo = c . mul ( c . mul ( Nx , Dy ) , zo )
yo = c . mul ( c . mul ( Ny , Dx ) , c . sqr ( zo ) )
assert isoq_map_affine ( x / z2 , y / z3 , Cost ( ) ) == ( xo / zo ^ 2 , yo / zo ^ 3 )
return ( xo , yo , zo )
2021-02-21 13:11:19 -08:00
def hex_bytes ( bs ) :
return " [ %s ] " % ( " , " . join ( [ " %02x " % ( as_byte ( b ) , ) for b in bs ] ) , )
2021-01-12 17:11:20 -08:00
2021-02-21 13:11:19 -08:00
def hash ( hasher , msg ) :
if VERBOSE : print ( hex_bytes ( msg ) )
h = hasher ( )
h . update ( msg )
return h . digest ( )
2020-12-29 04:53:13 -08:00
2021-02-21 13:11:19 -08:00
SHA256 = ( sha256 , 32 , 64 )
2021-03-22 15:34:29 -07:00
BLAKE2b = ( blake2b , 64 , 128 )
2020-12-29 04:53:13 -08:00
2021-02-21 13:11:19 -08:00
def hash_to_field ( modulus , message , DST , count ) :
outlen = int ( count * CHUNKLEN )
uniform_bytes = expand_message_xmd ( BLAKE2b , message , DST , outlen )
if VERBOSE :
print ( " uniform_bytes: " )
print ( hex_bytes ( uniform_bytes [ : 64 ] ) )
print ( hex_bytes ( uniform_bytes [ 64 : ] ) )
return [ Mod ( OS2IP ( uniform_bytes [ CHUNKLEN * i : CHUNKLEN * ( i + 1 ) ] ) , modulus ) for i in range ( count ) ]
2020-12-29 04:53:13 -08:00
def OS2IP ( bs ) :
acc = 0
for b in bs :
acc = ( acc << 8 ) + as_byte ( b )
return acc
2021-02-21 13:11:19 -08:00
def expand_message_xmd ( H , msg , DST , len_in_bytes ) :
2021-04-21 04:32:27 -07:00
assert isinstance ( DST , bytes )
assert isinstance ( msg , bytes )
2021-02-21 13:11:19 -08:00
( hasher , b_in_bytes , r_in_bytes ) = H
assert len ( DST ) < = 255
ell = ( len_in_bytes + b_in_bytes - 1 ) / / b_in_bytes
assert ell < = 255
DST_prime = DST + as_bytes ( [ len ( DST ) ] )
2021-04-21 04:32:27 -07:00
msg_prime = b " \x00 " * r_in_bytes + msg + as_bytes ( [ len_in_bytes >> 8 , len_in_bytes & 0xFF , 0 ] ) + DST_prime
2021-02-21 13:11:19 -08:00
if VERBOSE : print ( " b_0: " )
b_0 = hash ( hasher , msg_prime )
if VERBOSE : print ( " b_1: " )
b = hash ( hasher , b_0 + b " \x01 " + DST_prime )
for i in range ( 2 , ell + 1 ) :
if VERBOSE : print ( " b_ %d : " % ( i , ) )
b + = hash ( hasher , as_bytes ( as_byte ( x ) ^ ^ as_byte ( y ) for x , y in zip ( b_0 , b [ - 64 : ] ) ) + as_bytes ( [ i ] ) + DST_prime )
return b [ : len_in_bytes ]
2020-12-29 16:09:26 -08:00
2021-01-12 17:11:20 -08:00
def hash_to_pallas_jacobian ( msg , DST ) :
2020-12-29 16:09:26 -08:00
c = Cost ( )
2021-01-12 17:11:20 -08:00
us = hash_to_field ( p , msg , DST , 2 )
2021-02-21 13:11:19 -08:00
if VERBOSE : print ( " us = [0x %064x , 0x064 %x ] " % ( us [ 0 ] , us [ 1 ] ) )
Q0 = map_to_curve_simple_swu ( F_p , IsoEp , IsoEpZ , us [ 0 ] , c )
Q1 = map_to_curve_simple_swu ( F_p , IsoEp , IsoEpZ , us [ 1 ] , c )
2021-01-01 16:50:42 -08:00
2021-02-21 13:11:19 -08:00
R = Q0 . add ( Q1 , IsoEp , c )
# Q0.add(Q0, IsoEp, Cost()) # check that unified addition works
2020-12-29 16:09:26 -08:00
2021-01-12 17:11:20 -08:00
# no cofactor clearing needed since Pallas is prime-order
2021-01-01 18:22:01 -08:00
( Px , Py , Pz ) = isop_map_jacobian ( R , c )
2021-02-21 13:11:19 -08:00
P = Ep ( ( Px / Pz ^ 2 , Py / Pz ^ 3 ) )
2021-03-06 14:04:22 -08:00
return ( P , ( Px , Py , Pz ) , c )
2020-12-29 04:53:13 -08:00
2021-01-12 17:11:20 -08:00
def hash_to_vesta_jacobian ( msg , DST ) :
c = Cost ( )
us = hash_to_field ( q , msg , DST , 2 )
2021-02-21 13:11:19 -08:00
if VERBOSE : print ( " us = [0x %064x , 0x064 %x ] " % ( us [ 0 ] , us [ 1 ] ) )
Q0 = map_to_curve_simple_swu ( F_q , IsoEq , IsoEqZ , us [ 0 ] , c )
Q1 = map_to_curve_simple_swu ( F_q , IsoEq , IsoEqZ , us [ 1 ] , c )
2021-01-12 17:11:20 -08:00
2021-02-21 13:11:19 -08:00
R = Q0 . add ( Q1 , IsoEq , c )
# Q0.add(Q0, IsoEq, Cost()) # check that unified addition works
2021-01-12 17:11:20 -08:00
# no cofactor clearing needed since Vesta is prime-order
( Px , Py , Pz ) = isoq_map_jacobian ( R , c )
2021-02-21 13:11:19 -08:00
P = Eq ( ( Px / Pz ^ 2 , Py / Pz ^ 3 ) )
2021-03-06 14:04:22 -08:00
return ( P , ( Px , Py , Pz ) , c )
2020-12-29 04:53:13 -08:00
2021-01-12 17:11:20 -08:00
print ( " " )
2021-04-27 06:24:13 -07:00
P0 = map_to_curve_simple_swu ( F_p , IsoEp , IsoEpZ , Mod ( 0 , p ) , Cost ( ) )
print ( " Fp: map_to_curve_simple_swu(0) = %r " % ( P0 . to_affine ( IsoEp ) , ) )
P1 = map_to_curve_simple_swu ( F_p , IsoEp , IsoEpZ , Mod ( 1 , p ) , Cost ( ) )
print ( " Fp: map_to_curve_simple_swu(1) = %r " % ( P1 . to_affine ( IsoEp ) , ) )
Pa = map_to_curve_simple_swu ( F_p , IsoEp , IsoEpZ , Mod ( 0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef0123 , p ) , Cost ( ) )
print ( " Fp: map_to_curve_simple_swu(0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef0123) = %r " % ( Pa . to_affine ( IsoEp ) , ) )
2021-01-12 17:11:20 -08:00
print ( " " )
2021-04-27 06:24:13 -07:00
Q0 = map_to_curve_simple_swu ( F_q , IsoEq , IsoEqZ , Mod ( 0 , q ) , Cost ( ) )
print ( " Fq: map_to_curve_simple_swu(0) = %r " % ( Q0 . to_affine ( IsoEq ) , ) )
Q1 = map_to_curve_simple_swu ( F_q , IsoEq , IsoEqZ , Mod ( 1 , q ) , Cost ( ) )
print ( " Fq: map_to_curve_simple_swu(1) = %r " % ( Q1 . to_affine ( IsoEq ) , ) )
Qa = map_to_curve_simple_swu ( F_q , IsoEq , IsoEqZ , Mod ( 0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef0123 , q ) , Cost ( ) )
print ( " Fq: map_to_curve_simple_swu(0x123456789abcdef123456789abcdef123456789abcdef123456789abcdef0123) = %r " % ( Qa . to_affine ( IsoEq ) , ) )
2021-02-21 13:11:19 -08:00
print ( " " )
2021-03-06 14:04:22 -08:00
xyz = isop_map_jacobian (
2021-02-21 13:11:19 -08:00
ChudnovskyPoint ( IsoEp ,
Mod ( 0x0a881e4d556945aa9c6cfc47bce1aba6593c053e5e2337adc37f111df5c4419e , p ) ,
Mod ( 0x035e5c8a06d5cfb4a62eec46f662cb4e6979f7f2b0acf188f234e04434502b47 , p ) ,
Mod ( 0x3af37975b09331256ac4e343558dcbf3575baa717958ef1f11ab791d4fb6f6b4 , p ) ) ,
Cost ( ) )
2021-03-06 14:04:22 -08:00
print ( " Ep { x: 0x %064x , y: 0x %064x , z: 0x %064x } " % xyz )
2021-02-21 13:11:19 -08:00
print ( " " )
# This test vector is chosen so that the first map_to_curve_simple_swu takes the gx1 square
# "branch" and the second takes the gx1 non-square "branch" (opposite to the Vesta test vector).
2021-04-21 04:32:27 -07:00
( P , xyz , c ) = hash_to_pallas_jacobian ( b " Trans rights now! " , b " z.cash:test-pallas_XMD:BLAKE2b_SSWU_RO_ " )
2021-03-06 14:04:22 -08:00
print ( " Ep { x: 0x %064x , y: 0x %064x , z: 0x %064x } " % xyz )
2021-02-21 13:11:19 -08:00
print ( " " )
# This test vector is chosen so that the first map_to_curve_simple_swu takes the gx1 non-square
# "branch" and the second takes the gx1 square "branch" (opposite to the Pallas test vector).
2021-04-21 04:32:27 -07:00
( P , xyz , c ) = hash_to_vesta_jacobian ( b " hello " , b " z.cash:test-vesta_XMD:BLAKE2b_SSWU_RO_ " )
2021-03-06 14:04:22 -08:00
print ( " Eq { x: 0x %064x , y: 0x %064x , z: 0x %064x } " % xyz )
2020-12-31 05:45:35 -08:00
print ( " " )
2020-12-29 04:53:13 -08:00
2021-01-12 17:11:20 -08:00
if OP_COUNT :
iters = 100
for i in range ( iters ) :
2021-04-21 04:32:27 -07:00
( P , xyz , cost ) = hash_to_pallas_jacobian ( pack ( " >I " , i ) , b " z.cash:test-pallas_XMD:BLAKE2b_SSWU_RO_ " )
2021-03-06 14:04:22 -08:00
print ( xyz , cost )