Browse Source

isogenous encode/decode working in spec, looks doable for opt (at least with cofactor 4)

master
Michael Hamburg 7 years ago
parent
commit
5f12ca4582
1 changed files with 105 additions and 46 deletions
  1. +105
    -46
      aux/ristretto.sage

+ 105
- 46
aux/ristretto.sage View File

@@ -10,6 +10,7 @@ def dec_le(x): return sum(b<<(8*i) for i,b in enumerate(x))
def randombytes(n): return bytearray([randint(0,255) for _ in range(n)])

def optimized_version_of(spec):
"""Decorator: This function is an optimized version of some specification"""
def decorator(f):
def wrapper(self,*args,**kwargs):
try: spec_ans = getattr(self,spec,spec)(*args,**kwargs),None
@@ -25,7 +26,7 @@ def optimized_version_of(spec):
if spec_ans[0] != opt_ans[0]:
raise SpecException("Mismatch in %s: %s != %s"
% (f.__name__,str(spec_ans[0]),str(opt_ans[0])))
if opt_ans[1] is not None: raise opt_ans[1]
if opt_ans[1] is not None: raise
else: return opt_ans[0]
wrapper.__name__ = f.__name__
return wrapper
@@ -52,8 +53,8 @@ def isqrt_i(x):
if is_square(x): return True,1/sqrt(x)
else: return False,1/sqrt(x*gen)

class EdwardsPoint(object):
"""Abstract class for point an an Edwards curve; needs F,a,d to work"""
class QuotientEdwardsPoint(object):
"""Abstract class for point an a quotiented Edwards curve; needs F,a,d,cofactor to work"""
def __init__(self,x=0,y=1):
x = self.x = self.F(x)
y = self.y = self.F(y)
@@ -79,11 +80,16 @@ class EdwardsPoint(object):
def __neg__(self): return self.__class__(-self.x,self.y)
def __sub__(self,other): return self + (-other)
def __rmul__(self,other): return self*other
def __eq__(self,other): return tuple(self) == tuple(other)
def __eq__(self,other):
"""NB: this is the only method that is different from the usual one"""
x,y = self
X,Y = other
return x*Y == X*y or (self.cofactor==8 and -self.a*x*X == y*Y)
def __ne__(self,other): return not (self==other)
def __mul__(self,exp):
exp = int(exp)
if exp < 0: exp,self = -exp,-self
total = self.__class__()
work = self
while exp != 0:
@@ -103,14 +109,9 @@ class EdwardsPoint(object):
return self.__class__(self.y*self.i, self.x*self.i)
else:
return self.__class__(-self.x, -self.y)

class RistrettoPoint(EdwardsPoint):
"""Like current decaf but tweaked for simplicity"""
def __eq__(self,other):
x,y = self
X,Y = other
return x*Y == X*y or x*X == y*Y

# Utility functions
@classmethod
def bytesToGf(cls,bytes,mustBeProper=True,mustBePositive=False):
"""Convert little-endian bytes to field element, sanity check length"""
@@ -123,6 +124,14 @@ class RistrettoPoint(EdwardsPoint):
raise InvalidEncodingException("%d is negative!" % s)
return cls.F(s)
@classmethod
def gfToBytes(cls,x,mustBePositive=False):
"""Convert little-endian bytes to field element, sanity check length"""
if lobit(x) and mustBePositive: x = -x
return enc_le(x,cls.encLen)

class RistrettoPoint(QuotientEdwardsPoint):
"""The new Ristretto group"""
def encodeSpec(self):
"""Unoptimized specification for encoding"""
x,y = self
@@ -133,8 +142,7 @@ class RistrettoPoint(EdwardsPoint):
if lobit(x): x,y = -x,-y
s = xsqrt(self.a*(y-1)/(y+1),exn=Exception("Unimplemented: point is odd: " + str(self)))
return enc_le(s,self.encLen)
return self.gfToBytes(s)
@classmethod
def decodeSpec(cls,s):
@@ -162,9 +170,8 @@ class RistrettoPoint(EdwardsPoint):
i1 = isr*u1
i2 = isr*u2
z_inv = i1*i2*t

rotate = self.cofactor==8 and lobit(t*z_inv)
if rotate:
if self.cofactor==8 and lobit(t*z_inv):
x,y = y*self.i,x*self.i
den_inv = self.magic * i1
else:
@@ -172,9 +179,7 @@ class RistrettoPoint(EdwardsPoint):

if lobit(x*z_inv): y = -y
s = (z-y) * den_inv
if lobit(s): s=-s
return enc_le(s,self.encLen)
return self.gfToBytes(s,mustBePositive=True)
@classmethod
@optimized_version_of("decodeSpec")
@@ -224,8 +229,7 @@ class RistrettoPoint(EdwardsPoint):
else:
sgn,s,t = -1,xsqrt(n2), r*(r-1)*(a+d)^2 / den - 1
ret = cls.fromJacobiQuartic(s,t,sgn)
return ret
return cls.fromJacobiQuartic(s,t,sgn)
@classmethod
@optimized_version_of("elligatorSpec")
@@ -243,7 +247,51 @@ class RistrettoPoint(EdwardsPoint):
s = isri*num
t = isri*s*(r-1)*(d+a)^2 + sgn
return cls.fromJacobiQuartic(s,t,sgn)


class Decaf1Point(QuotientEdwardsPoint):
"""Like current decaf but tweaked for simplicity"""
def encodeSpec(self):
"""Unoptimized specification for encoding"""
a,d = self.a,self.d
x,y = self
if x==0: return(self.gfToBytes(0))
isr2 = isqrt(a*(y^2-1)) / self.magic
altx = 1/isr2*self.isoMagic
if lobit(altx): s = (1+x*y*isr2)/(a*x)
else: s = (1-x*y*isr2)/(a*x)
# TODO: cofactor 8
return self.gfToBytes(s,mustBePositive=True)
@classmethod
def decodeSpec(cls,s):
"""Unoptimized specification for decoding"""
a,d = cls.a,cls.d
s = cls.bytesToGf(s,mustBePositive=True)
if s==0: return cls()
isr = isqrt(s^4 + 2*(a-2*d)*s^2 + 1)
altx = 2*s*isr*cls.isoMagic
if lobit(altx): isr = -isr
x = 2*s / (1+a*s^2)
y = (1-a*s^2) * isr
# TODO: cofactor 8
return cls(x,y)

@optimized_version_of("encodeSpec")
def encode(self):
"""Encode, optimized version"""
return self.encodeSpec() # TODO
@classmethod
@optimized_version_of("decodeSpec")
def decode(cls,s):
"""Decode, optimized version"""
return cls.decodeSpec(s) # TODO

class Ed25519Point(RistrettoPoint):
F = GF(2^255-19)
d = F(-121665/121666)
@@ -261,51 +309,50 @@ class Ed25519Point(RistrettoPoint):
if lobit(x): x = -x
return cls(x,y)

class TwistedEd448GoldilocksPoint(RistrettoPoint):
class IsoEd448Point(RistrettoPoint):
F = GF(2^448-2^224-1)
d = F(-39082)
a = F(-1)
d = F(39082/39081)
a = F(1)
qnr = -1
magic = isqrt(a*d-1)
cofactor = 4
encLen = 56
@classmethod
def base(cls):
y = cls.F(6) # TODO: no it isn't
x = sqrt((y^2-1)/(cls.d*y^2+1))
if lobit(x): x = -x
return cls(x,y)

class Ed448GoldilocksPoint(RistrettoPoint):
# TODO: decaf vs ristretto
# = ..., -3/2
return cls.decodeSpec(bytearray(binascii.unhexlify(
"00000000000000000000000000000000000000000000000000000000"+
"fdffffffffffffffffffffffffffffffffffffffffffffffffffffff")))
class TwistedEd448GoldilocksPoint(Decaf1Point):
F = GF(2^448-2^224-1)
d = F(-39081)
a = F(1)
d = F(-39082)
a = F(-1)
qnr = -1
magic = isqrt(a*d-1)
cofactor = 4
encLen = 56
isoMagic = IsoEd448Point.magic

@classmethod
def base(cls):
return cls(
0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa955555555555555555555555555555555555555555555555555555555,
0xae05e9634ad7048db359d6205086c2b0036ed7a035884dd7b7e36d728ad8c4b80d6565833a2a3098bbbcb2bed1cda06bdaeafbcdea9386ed
)
return cls.decodeSpec(bytearray(binascii.unhexlify(
"00000000000000000000000000000000000000000000000000000000"+
"fdffffffffffffffffffffffffffffffffffffffffffffffffffffff")))

class IsoEd448Point(RistrettoPoint):
class Ed448GoldilocksPoint(Decaf1Point):
F = GF(2^448-2^224-1)
d = F(1/39081+1)
d = F(-39081)
a = F(1)
qnr = -1
magic = isqrt(a*d-1)
cofactor = 4
encLen = 56
isoMagic = IsoEd448Point.magic
@classmethod
def base(cls):
# = ..., -3/2
return cls.decodeSpec(bytearray(binascii.unhexlify(
"00000000000000000000000000000000000000000000000000000000"+
"fdffffffffffffffffffffffffffffffffffffffffffffffffffffff")))
@@ -338,15 +385,27 @@ def test(cls,n):
if Q1 + Q0 != Q2: raise TestFailedException("Scalarmul doesn't work")
Q = Q1
test(Ed25519Point,100)
test(IsoEd448Point,100)
test(TwistedEd448GoldilocksPoint,100)
test(Ed448GoldilocksPoint,100)
test(IsoEd448Point,100)

def gangtest(classes,n):
for i in xrange(n):
rets = [bytes((cls.base()*i).encode()) for cls in classes]
if len(set(rets)) != 1:
print "Divergence at %d" % i
for c,ret in zip(classes,rets):
print c,binascii.hexlify(ret)
print
gangtest([IsoEd448Point,TwistedEd448GoldilocksPoint,Ed448GoldilocksPoint],100)

def testElligator(cls,n):
for i in xrange(n):
cls.elligator(randombytes(cls.encLen))
testElligator(Ed25519Point,100)
testElligator(Ed448GoldilocksPoint,100)
testElligator(TwistedEd448GoldilocksPoint,100)
testElligator(IsoEd448Point,100)
# testElligator(Ed448GoldilocksPoint,100)
# testElligator(TwistedEd448GoldilocksPoint,100)

Loading…
Cancel
Save