|
- """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")
|