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.