F = GF(2^255-19) dM = F(-121665) d = F(-121665/121666) ii = sqrt(F(-1)) def lobit(x): return int(x) & 1 def hibit(x): return lobit(2*x) magic = sqrt(F(-121666)) if lobit(magic): magic = -magic def eddsa_to_decaf(x,y): """ Converts an EdDSA point to a Decaf representation, in a manner compatible with libdecaf. The input point must be even. Note well! Decaf does not represent the cofactor information of a point. So e2d(d2e(s)) = s, but d2e(e2d(x,y)) might not be (x,y). """ if x*y == 0: return 0 # This will happen anyway with straightforward square root trick if not is_square((1-y)/(1+y)): raise Exception("Unimplemented: odd point in eddsa_to_decaf") if hibit(magic/(x*y)): (x,y) = (ii*y,ii*x) if hibit(2*magic/x): y = -y s = sqrt((1-y)/(1+y)) if hibit(s): s = -s return s def isqrt_trick(to_isr,to_inv): to_sqrt = to_isr*to_inv^2 if to_sqrt == 0: return 0,0,0 # This happens automatically in C; just to avoid problems in SAGE if not is_square(to_sqrt): raise Exception("Not square in isqrt_trick!") tmp = 1/sqrt(to_sqrt) isr = tmp * to_inv inv = tmp * isr * to_isr assert isr^2 == 1/to_isr assert inv == 1/to_inv return isr, inv, tmp def eddsa_to_decaf_opt(x,y,z=None): """ Optimized version of eddsa_to_decaf. Uses only one isqrt. There's probably some way to further optimize if you have a T-coord, but whatever. """ if z is None: # Pretend that we're in projective z = F.random_element() x *= z y *= z isr,inv,tmp = isqrt_trick(z^2-y^2,x*y) minv = inv*magic*z rotate = hibit(minv*z) if rotate: isr = tmp*(z^2-y^2)*magic y = ii*x if hibit(2*minv*y) != rotate: y = -y s = (z-y) * isr if hibit(s): s = -s return s print [eddsa_to_decaf_opt(x,y) == eddsa_to_decaf(x,y) for _,_,_,_,y1,y2 in points for x,y in [decode(y1,y2)]] def decaf_to_eddsa(s): """ Convert a Decaf representation to an EdDSA point, in a manner compatible with libdecaf. Note well! The Decaf representation of a point is canonical, but the EdDSA one is not, in that """ if s == 0: return (0,1) if hibit(s): raise Exception("invalid: s has high bit") if not is_square(s^4 + (2-4*dM)*s^2 + 1): raise Exception("invalid: not on curve") t = sqrt(s^4 + (2-4*dM)*s^2 + 1)/s if hibit(t): t = -t y = (1-s^2)/(1+s^2) x = 2*magic/t if y == 0 or lobit(t/y): raise Exception("invalid: t/y has high bit") assert y^2 - x^2 == 1+d*x^2*y^2 return (x,y) def decaf_to_eddsa_opt(s): """ Convert a Decaf representation to an EdDSA point, in a manner compatible with libdecaf. """ if s == 0: return (0,1) if hibit(s): raise Exception("invalid: s has high bit") if not is_square(s^4 + (2-4*dM)*s^2 + 1): raise Exception("invalid: not on curve") t = sqrt(s^4 + (2-4*dM)*s^2 + 1)/s if hibit(t): t = -t y = (1-s^2)/(1+s^2) x = 2*magic/t if y == 0 or lobit(t/y): raise Exception("invalid: t/y has high bit") assert y^2 - x^2 == 1+d*x^2*y^2 return (x,y) print [s == eddsa_to_decaf(*decaf_to_eddsa(s)) for _,_,s,_,_,_ in points] print [s == eddsa_to_decaf_opt(*decaf_to_eddsa_opt(s)) for _,_,s,_,_,_ in points]