"""Implementation of Edwards Digital Signature Algorithm.""" import hashlib from ._sha3 import shake_256 from . import ellipticcurve from ._compat import ( remove_whitespace, bit_length, bytes_to_int, int_to_bytes, compat26_str, ) # edwards25519, defined in RFC7748 _p = 2**255 - 19 _a = -1 _d = int( remove_whitespace( "370957059346694393431380835087545651895421138798432190163887855330" "85940283555" ) ) _h = 8 _Gx = int( remove_whitespace( "151122213495354007725011514095885315114540126930418572060461132" "83949847762202" ) ) _Gy = int( remove_whitespace( "463168356949264781694283940034751631413079938662562256157830336" "03165251855960" ) ) _r = 2**252 + 0x14DEF9DEA2F79CD65812631A5CF5D3ED def _sha512(data): return hashlib.new("sha512", compat26_str(data)).digest() curve_ed25519 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _sha512) generator_ed25519 = ellipticcurve.PointEdwards( curve_ed25519, _Gx, _Gy, 1, _Gx * _Gy % _p, _r, generator=True ) # edwards448, defined in RFC7748 _p = 2**448 - 2**224 - 1 _a = 1 _d = -39081 % _p _h = 4 _Gx = int( remove_whitespace( "224580040295924300187604334099896036246789641632564134246125461" "686950415467406032909029192869357953282578032075146446173674602635" "247710" ) ) _Gy = int( remove_whitespace( "298819210078481492676017930443930673437544040154080242095928241" "372331506189835876003536878655418784733982303233503462500531545062" "832660" ) ) _r = 2**446 - 0x8335DC163BB124B65129C96FDE933D8D723A70AADC873D6D54A7BB0D def _shake256(data): return shake_256(data, 114) curve_ed448 = ellipticcurve.CurveEdTw(_p, _a, _d, _h, _shake256) generator_ed448 = ellipticcurve.PointEdwards( curve_ed448, _Gx, _Gy, 1, _Gx * _Gy % _p, _r, generator=True ) class PublicKey(object): """Public key for the Edwards Digital Signature Algorithm.""" def __init__(self, generator, public_key, public_point=None): self.generator = generator self.curve = generator.curve() self.__encoded = public_key # plus one for the sign bit and round up self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8 if len(public_key) != self.baselen: raise ValueError( "Incorrect size of the public key, expected: {0} bytes".format( self.baselen ) ) if public_point: self.__point = public_point else: self.__point = ellipticcurve.PointEdwards.from_bytes( self.curve, public_key ) def __eq__(self, other): if isinstance(other, PublicKey): return ( self.curve == other.curve and self.__encoded == other.__encoded ) return NotImplemented def __ne__(self, other): return not self == other @property def point(self): return self.__point @point.setter def point(self, other): if self.__point != other: raise ValueError("Can't change the coordinates of the point") self.__point = other def public_point(self): return self.__point def public_key(self): return self.__encoded def verify(self, data, signature): """Verify a Pure EdDSA signature over data.""" data = compat26_str(data) if len(signature) != 2 * self.baselen: raise ValueError( "Invalid signature length, expected: {0} bytes".format( 2 * self.baselen ) ) R = ellipticcurve.PointEdwards.from_bytes( self.curve, signature[: self.baselen] ) S = bytes_to_int(signature[self.baselen :], "little") if S >= self.generator.order(): raise ValueError("Invalid signature") dom = bytearray() if self.curve == curve_ed448: dom = bytearray(b"SigEd448" + b"\x00\x00") k = bytes_to_int( self.curve.hash_func(dom + R.to_bytes() + self.__encoded + data), "little", ) if self.generator * S != self.__point * k + R: raise ValueError("Invalid signature") return True class PrivateKey(object): """Private key for the Edwards Digital Signature Algorithm.""" def __init__(self, generator, private_key): self.generator = generator self.curve = generator.curve() # plus one for the sign bit and round up self.baselen = (bit_length(self.curve.p()) + 1 + 7) // 8 if len(private_key) != self.baselen: raise ValueError( "Incorrect size of private key, expected: {0} bytes".format( self.baselen ) ) self.__private_key = bytes(private_key) self.__h = bytearray(self.curve.hash_func(private_key)) self.__public_key = None a = self.__h[: self.baselen] a = self._key_prune(a) scalar = bytes_to_int(a, "little") self.__s = scalar @property def private_key(self): return self.__private_key def __eq__(self, other): if isinstance(other, PrivateKey): return ( self.curve == other.curve and self.__private_key == other.__private_key ) return NotImplemented def __ne__(self, other): return not self == other def _key_prune(self, key): # make sure the key is not in a small subgroup h = self.curve.cofactor() if h == 4: h_log = 2 elif h == 8: h_log = 3 else: raise ValueError("Only cofactor 4 and 8 curves supported") key[0] &= ~((1 << h_log) - 1) # ensure the highest bit is set but no higher l = bit_length(self.curve.p()) if l % 8 == 0: key[-1] = 0 key[-2] |= 0x80 else: key[-1] = key[-1] & (1 << (l % 8)) - 1 | 1 << (l % 8) - 1 return key def public_key(self): """Generate the public key based on the included private key""" if self.__public_key: return self.__public_key public_point = self.generator * self.__s self.__public_key = PublicKey( self.generator, public_point.to_bytes(), public_point ) return self.__public_key def sign(self, data): """Perform a Pure EdDSA signature over data.""" data = compat26_str(data) A = self.public_key().public_key() prefix = self.__h[self.baselen :] dom = bytearray() if self.curve == curve_ed448: dom = bytearray(b"SigEd448" + b"\x00\x00") r = bytes_to_int(self.curve.hash_func(dom + prefix + data), "little") R = (self.generator * r).to_bytes() k = bytes_to_int(self.curve.hash_func(dom + R + A + data), "little") k %= self.generator.order() S = (r + k * self.__s) % self.generator.order() return R + int_to_bytes(S, self.baselen, "little")