# This is as sketch of how to decaffeinate Curve25519 F = GF(2^255-19) doPinkBikeShed = True if doPinkBikeShed: d = -89747 # PinkBikeShed else: d = -121665 # Curve25519 M = EllipticCurve(F,[0,2-4*d,0,1,0]) sqrtN1 = sqrt(F(-1)) def maybe(): return randint(0,1) def qpositive(x): return int(x) <= (2^255-19-1)/2 def M_to_E(P): # P must be even (x,y) = P.xy() assert x.is_square() s = sqrt(x) if s == 0: t = 1 else: t = y/s X,Y = 2*s / (1+s^2), (1-s^2) / t if maybe(): X,Y = -X,-Y if maybe(): X,Y = Y,-X # OK, have point in ed return X,Y def decaf_encode_from_E(X,Y): assert X^2 + Y^2 == 1 + d*X^2*Y^2 if not (X==0 or Y==0 or qpositive(1/(X*Y)) or doPinkBikeShed): X,Y = Y,-X assert X==0 or Y==0 or qpositive(1/(X*Y)) assert (1-X^2).is_square() sx = sqrt(1-X^2) tos = -2*sx/X/Y if not qpositive(tos): sx = -sx s = (1 + sx) / X if not qpositive(s): s = -s return s def isqrt(x): ops = [(1,2),(1,2),(3,1),(6,0),(1,2),(12,1),(25,1),(25,1),(50,0),(125,0),(2,2),(1,2)] st = [x,x,x] for i,(sh,add) in enumerate(ops): od = i&1 st[od] = st[od^^1]^(2^sh)*st[add] # assert st[2] == x^(2^252-3) assert st[1] == 1 or st[1] == -1 if st[1] == 1: return st[0] else: return st[0] * sqrtN1 def decaf_encode_from_E_c(X,Y): Z = F.random_element() T = X*Y*Z X = X*Z Y = Y*Z assert X^2 + Y^2 == Z^2 + d*T^2 # Precompute sd = sqrt(F(1-d)) # comments in case of identity point # (1,0) or (0,1) zx = Z^2-X^2 # 0^2 or 1 * Z^2 TZ = T*Z # 0 * Z^2 assert zx.is_square ooAll = isqrt(zx*TZ^2) # inf^2 or inf * 1/Z^3 osx = ooAll * TZ # inf or 1 * 1/Z ooTZ = ooAll * zx * osx # inf * 1/Z^2 floop = qpositive(Z^2 * ooTZ) or doPinkBikeShed # 0 if floop: frob = zx * ooTZ # 0 or inf # Y = 0 or 1 else: frob = sd # sd Y = -X # -1 or 0 * Z osx *= frob # 1 or inf * 1/Z || inf sd or sd * 1/Z # y * osx = 0 or inf or -inf sd or 0 sd # y * ootz * z = 1 or inf or inf sd or 0^2 sd # s = 0 or 1 or inf sd or if qpositive(-2*osx*Z) != floop: osx = -osx s = Y*(ooTZ*Z + osx) # if not qpositive(s): s = -s return s def is_rotation((X,Y),(x,y)): return x*Y == X*y or x*X == -y*Y def decaf_decode_to_E(s): assert qpositive(s) t = sqrt(s^4 + (2-4*d)*s^2 + 1) if not qpositive(t/s): t = -t X,Y = 2*s / (1+s^2), (1-s^2) / t assert X==0 or Y==0 or qpositive(1/(X*Y)) or doPinkBikeShed return X,Y def decaf_decode_to_E_c(s): assert qpositive(s) s2 = s^2 s21 = 1+s2 t2 = s21^2 - 4*d*s2 alt = s21*s the = isqrt(t2*alt^2) oot = the * alt the *= t2 tos = the * s21 X = 2 * (tos-the) * oot Y = (1-s2) * oot if not qpositive(tos): Y = -Y assert qpositive(X*Y) or doPinkBikeShed return X,Y def test(): P = 2*M.random_point() X,Y = M_to_E(P) s = decaf_encode_from_E(X,Y) assert s == decaf_encode_from_E_c(X,Y) XX,YY = decaf_decode_to_E(s) XX2,YY2 = decaf_decode_to_E_c(s) assert is_rotation((X,Y),(XX,YY)) assert is_rotation((X,Y),(XX2,YY2))