ethereum.crypto.elliptic_curve

Elliptic Curves ^^^^^^^^^^^^^^^

  1"""
  2Elliptic Curves
  3^^^^^^^^^^^^^^^
  4"""
  5
  6from typing import Generic, Type, TypeVar
  7
  8import coincurve
  9
 10from ..base_types import U256, Bytes
 11from .finite_field import Field
 12from .hash import Hash32
 13
 14SECP256K1N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
 15
 16F = TypeVar("F", bound=Field)
 17T = TypeVar("T", bound="EllipticCurve")
 18
 19
 20def secp256k1_recover(r: U256, s: U256, v: U256, msg_hash: Hash32) -> Bytes:
 21    """
 22    Recovers the public key from a given signature.
 23
 24    Parameters
 25    ----------
 26    r :
 27        TODO
 28    s :
 29        TODO
 30    v :
 31        TODO
 32    msg_hash :
 33        Hash of the message being recovered.
 34
 35    Returns
 36    -------
 37    public_key : `ethereum.base_types.Bytes`
 38        Recovered public key.
 39    """
 40    r_bytes = r.to_be_bytes32()
 41    s_bytes = s.to_be_bytes32()
 42
 43    signature = bytearray([0] * 65)
 44    signature[32 - len(r_bytes) : 32] = r_bytes
 45    signature[64 - len(s_bytes) : 64] = s_bytes
 46    signature[64] = v
 47    public_key = coincurve.PublicKey.from_signature_and_message(
 48        bytes(signature), msg_hash, hasher=None
 49    )
 50    public_key = public_key.format(compressed=False)[1:]
 51    return public_key
 52
 53
 54class EllipticCurve(Generic[F]):
 55    """
 56    Superclass for integers modulo a prime. Not intended to be used
 57    directly, but rather to be subclassed.
 58    """
 59
 60    __slots__ = ("x", "y")
 61
 62    FIELD: Type[F]
 63    A: F
 64    B: F
 65
 66    x: F
 67    y: F
 68
 69    def __new__(cls: Type[T], x: F, y: F) -> T:
 70        """
 71        Make new point on the curve. The point is not checked to see if it is
 72        on the curve.
 73        """
 74        res = object.__new__(cls)
 75        res.x = x
 76        res.y = y
 77        return res
 78
 79    def __init__(self, x: F, y: F) -> None:
 80        """
 81        Checks if the point is on the curve. To skip this check call
 82        `__new__()` directly.
 83        """
 84        if (
 85            x != self.FIELD.zero() or y != self.FIELD.zero()
 86        ) and y ** 2 - x**3 - self.A * x - self.B != self.FIELD.zero():
 87            raise ValueError("Point not on curve")
 88
 89    def __eq__(self, other: object) -> bool:
 90        """
 91        Test two points for equality.
 92        """
 93        if not isinstance(other, type(self)):
 94            return False
 95        return self.x == other.x and self.y == other.y
 96
 97    def __str__(self) -> str:
 98        """
 99        Stringify a point as its coordinates.
100        """
101        return str((self.x, self.y))
102
103    @classmethod
104    def point_at_infinity(cls: Type[T]) -> T:
105        """
106        Return the point at infinity. This is the identity element of the group
107        operation.
108
109        The point at infinity doesn't actually have coordinates so we use
110        `(0, 0)` (which isn't on the curve) to represent it.
111        """
112        return cls.__new__(cls, cls.FIELD.zero(), cls.FIELD.zero())
113
114    def double(self: T) -> T:
115        """
116        Add a point to itself.
117        """
118        x, y, F = self.x, self.y, self.FIELD
119        if x == 0 and y == 0:
120            return self
121        lam = (F.from_int(3) * x**2 + self.A) / (F.from_int(2) * y)
122        new_x = lam**2 - x - x
123        new_y = lam * (x - new_x) - y
124        return self.__new__(type(self), new_x, new_y)
125
126    def __add__(self: T, other: T) -> T:
127        """
128        Add two points together.
129        """
130        ZERO = self.FIELD.zero()
131        self_x, self_y, other_x, other_y = self.x, self.y, other.x, other.y
132        if self_x == ZERO and self_y == ZERO:
133            return other
134        if other_x == ZERO and other_y == ZERO:
135            return self
136        if self_x == other_x:
137            if self_y == other_y:
138                return self.double()
139            else:
140                return self.point_at_infinity()
141        lam = (other_y - self_y) / (other_x - self_x)
142        x = lam**2 - self_x - other_x
143        y = lam * (self_x - x) - self_y
144        return self.__new__(type(self), x, y)
145
146    def mul_by(self: T, n: int) -> T:
147        """
148        Multiply `self` by `n` using the double and add algorithm.
149        """
150        res = self.__new__(type(self), self.FIELD.zero(), self.FIELD.zero())
151        s = self
152        while n != 0:
153            if n % 2 == 1:
154                res = res + s
155            s = s + s
156            n //= 2
157        return res
def secp256k1_recover( r: ethereum.base_types.U256, s: ethereum.base_types.U256, v: ethereum.base_types.U256, msg_hash: ethereum.base_types.Bytes32) -> bytes:
21def secp256k1_recover(r: U256, s: U256, v: U256, msg_hash: Hash32) -> Bytes:
22    """
23    Recovers the public key from a given signature.
24
25    Parameters
26    ----------
27    r :
28        TODO
29    s :
30        TODO
31    v :
32        TODO
33    msg_hash :
34        Hash of the message being recovered.
35
36    Returns
37    -------
38    public_key : `ethereum.base_types.Bytes`
39        Recovered public key.
40    """
41    r_bytes = r.to_be_bytes32()
42    s_bytes = s.to_be_bytes32()
43
44    signature = bytearray([0] * 65)
45    signature[32 - len(r_bytes) : 32] = r_bytes
46    signature[64 - len(s_bytes) : 64] = s_bytes
47    signature[64] = v
48    public_key = coincurve.PublicKey.from_signature_and_message(
49        bytes(signature), msg_hash, hasher=None
50    )
51    public_key = public_key.format(compressed=False)[1:]
52    return public_key

Recovers the public key from a given signature.

Parameters

r : TODO s : TODO v : TODO msg_hash : Hash of the message being recovered.

Returns

public_key : ethereum.base_types.Bytes Recovered public key.

class EllipticCurve(typing.Generic[~F]):
 55class EllipticCurve(Generic[F]):
 56    """
 57    Superclass for integers modulo a prime. Not intended to be used
 58    directly, but rather to be subclassed.
 59    """
 60
 61    __slots__ = ("x", "y")
 62
 63    FIELD: Type[F]
 64    A: F
 65    B: F
 66
 67    x: F
 68    y: F
 69
 70    def __new__(cls: Type[T], x: F, y: F) -> T:
 71        """
 72        Make new point on the curve. The point is not checked to see if it is
 73        on the curve.
 74        """
 75        res = object.__new__(cls)
 76        res.x = x
 77        res.y = y
 78        return res
 79
 80    def __init__(self, x: F, y: F) -> None:
 81        """
 82        Checks if the point is on the curve. To skip this check call
 83        `__new__()` directly.
 84        """
 85        if (
 86            x != self.FIELD.zero() or y != self.FIELD.zero()
 87        ) and y ** 2 - x**3 - self.A * x - self.B != self.FIELD.zero():
 88            raise ValueError("Point not on curve")
 89
 90    def __eq__(self, other: object) -> bool:
 91        """
 92        Test two points for equality.
 93        """
 94        if not isinstance(other, type(self)):
 95            return False
 96        return self.x == other.x and self.y == other.y
 97
 98    def __str__(self) -> str:
 99        """
100        Stringify a point as its coordinates.
101        """
102        return str((self.x, self.y))
103
104    @classmethod
105    def point_at_infinity(cls: Type[T]) -> T:
106        """
107        Return the point at infinity. This is the identity element of the group
108        operation.
109
110        The point at infinity doesn't actually have coordinates so we use
111        `(0, 0)` (which isn't on the curve) to represent it.
112        """
113        return cls.__new__(cls, cls.FIELD.zero(), cls.FIELD.zero())
114
115    def double(self: T) -> T:
116        """
117        Add a point to itself.
118        """
119        x, y, F = self.x, self.y, self.FIELD
120        if x == 0 and y == 0:
121            return self
122        lam = (F.from_int(3) * x**2 + self.A) / (F.from_int(2) * y)
123        new_x = lam**2 - x - x
124        new_y = lam * (x - new_x) - y
125        return self.__new__(type(self), new_x, new_y)
126
127    def __add__(self: T, other: T) -> T:
128        """
129        Add two points together.
130        """
131        ZERO = self.FIELD.zero()
132        self_x, self_y, other_x, other_y = self.x, self.y, other.x, other.y
133        if self_x == ZERO and self_y == ZERO:
134            return other
135        if other_x == ZERO and other_y == ZERO:
136            return self
137        if self_x == other_x:
138            if self_y == other_y:
139                return self.double()
140            else:
141                return self.point_at_infinity()
142        lam = (other_y - self_y) / (other_x - self_x)
143        x = lam**2 - self_x - other_x
144        y = lam * (self_x - x) - self_y
145        return self.__new__(type(self), x, y)
146
147    def mul_by(self: T, n: int) -> T:
148        """
149        Multiply `self` by `n` using the double and add algorithm.
150        """
151        res = self.__new__(type(self), self.FIELD.zero(), self.FIELD.zero())
152        s = self
153        while n != 0:
154            if n % 2 == 1:
155                res = res + s
156            s = s + s
157            n //= 2
158        return res

Superclass for integers modulo a prime. Not intended to be used directly, but rather to be subclassed.

EllipticCurve(x: ~F, y: ~F)
80    def __init__(self, x: F, y: F) -> None:
81        """
82        Checks if the point is on the curve. To skip this check call
83        `__new__()` directly.
84        """
85        if (
86            x != self.FIELD.zero() or y != self.FIELD.zero()
87        ) and y ** 2 - x**3 - self.A * x - self.B != self.FIELD.zero():
88            raise ValueError("Point not on curve")

Checks if the point is on the curve. To skip this check call __new__() directly.

@classmethod
def point_at_infinity(cls: Type[~T]) -> ~T:
104    @classmethod
105    def point_at_infinity(cls: Type[T]) -> T:
106        """
107        Return the point at infinity. This is the identity element of the group
108        operation.
109
110        The point at infinity doesn't actually have coordinates so we use
111        `(0, 0)` (which isn't on the curve) to represent it.
112        """
113        return cls.__new__(cls, cls.FIELD.zero(), cls.FIELD.zero())

Return the point at infinity. This is the identity element of the group operation.

The point at infinity doesn't actually have coordinates so we use (0, 0) (which isn't on the curve) to represent it.

def double(self: ~T) -> ~T:
115    def double(self: T) -> T:
116        """
117        Add a point to itself.
118        """
119        x, y, F = self.x, self.y, self.FIELD
120        if x == 0 and y == 0:
121            return self
122        lam = (F.from_int(3) * x**2 + self.A) / (F.from_int(2) * y)
123        new_x = lam**2 - x - x
124        new_y = lam * (x - new_x) - y
125        return self.__new__(type(self), new_x, new_y)

Add a point to itself.

def mul_by(self: ~T, n: int) -> ~T:
147    def mul_by(self: T, n: int) -> T:
148        """
149        Multiply `self` by `n` using the double and add algorithm.
150        """
151        res = self.__new__(type(self), self.FIELD.zero(), self.FIELD.zero())
152        s = self
153        while n != 0:
154            if n % 2 == 1:
155                res = res + s
156            s = s + s
157            n //= 2
158        return res

Multiply self by n using the double and add algorithm.