From e1d79367c7ded0564c7aaef5f6eabff361b76040 Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Tue, 7 Nov 2023 15:27:53 -0700 Subject: [PATCH 01/13] Add marker to indicate that types are available. --- tinyec/py.typed | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tinyec/py.typed diff --git a/tinyec/py.typed b/tinyec/py.typed new file mode 100644 index 0000000..e69de29 From 5adf2ca45967bbb13f2de6a06586aab831c0856f Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Tue, 7 Nov 2023 15:29:57 -0700 Subject: [PATCH 02/13] Remove Python 2 compatibility shim. Python 2 is deprecated. --- tinyec/ec.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/tinyec/ec.py b/tinyec/ec.py index 6a72969..46b3b4e 100644 --- a/tinyec/ec.py +++ b/tinyec/ec.py @@ -3,11 +3,7 @@ import warnings -# Python3 compatibility -try: - LONG_TYPE = long -except NameError: - LONG_TYPE = int +LONG_TYPE = int def egcd(a, b): if a == 0: From ad0bbb47c1f2e438f3b56ed09344f238934acb81 Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Wed, 8 Nov 2023 13:06:50 -0700 Subject: [PATCH 03/13] Remove the encoding markers from the source files. This isn't required in Python 3. --- tinyec/ec.py | 1 - tinyec/registry.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/tinyec/ec.py b/tinyec/ec.py index 46b3b4e..f520b5b 100644 --- a/tinyec/ec.py +++ b/tinyec/ec.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import random import warnings diff --git a/tinyec/registry.py b/tinyec/registry.py index f3a912d..cd95714 100644 --- a/tinyec/registry.py +++ b/tinyec/registry.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - import tinyec.ec as ec From 3b2250657bc1975b52813373d72c1ff94f98cd96 Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Wed, 8 Nov 2023 13:12:51 -0700 Subject: [PATCH 04/13] Remove LONG_TYPE reference. We no longer support Python 2, it's over. --- tinyec/ec.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tinyec/ec.py b/tinyec/ec.py index f520b5b..a2bbdc9 100644 --- a/tinyec/ec.py +++ b/tinyec/ec.py @@ -2,8 +2,6 @@ import warnings -LONG_TYPE = int - def egcd(a, b): if a == 0: return b, 0, 1 @@ -162,7 +160,7 @@ def __sub__(self, other): def __mul__(self, other): if isinstance(other, Inf): return Inf(self.curve) - if isinstance(other, int) or isinstance(other, LONG_TYPE): + if isinstance(other, int): if other % self.curve.field.n == 0: return Inf(self.curve) if other < 0: From 8c8115821519287859628b79aaff6dee48a0a9fa Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Wed, 8 Nov 2023 13:17:29 -0700 Subject: [PATCH 05/13] Reorder SubGroup and make_keypair. It makes it easier to declare the type signatures without having to resort to using strings since the class is defined later. --- tinyec/ec.py | 58 ++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/tinyec/ec.py b/tinyec/ec.py index a2bbdc9..10c0045 100644 --- a/tinyec/ec.py +++ b/tinyec/ec.py @@ -20,6 +20,29 @@ def mod_inv(a, p): return x % p +class SubGroup(object): + def __init__(self, p, g, n, h): + self.p = p + self.g = g + self.n = n + self.h = h + + def __eq__(self, other): + if not isinstance(other, SubGroup): + return False + return self.p == other.p and self.g == other.g and self.n == other.n and self.h == other.h + + def __ne__(self, other): + return not self.__eq__(other) + + def __str__(self): + return "Subgroup => generator %s, order: %d, cofactor: %d on Field => prime %d" % (self.g, self.n, + self.h, self.p) + + def __repr__(self): + return self.__str__() + + class Curve(object): def __init__(self, a, b, field, name="undefined"): self.name = name @@ -46,29 +69,6 @@ def __str__(self): return "\"%s\" => y^2 = x^3 + %dx + %d (mod %d)" % (self.name, self.a, self.b, self.field.p) -class SubGroup(object): - def __init__(self, p, g, n, h): - self.p = p - self.g = g - self.n = n - self.h = h - - def __eq__(self, other): - if not isinstance(other, SubGroup): - return False - return self.p == other.p and self.g == other.g and self.n == other.n and self.h == other.h - - def __ne__(self, other): - return not self.__eq__(other) - - def __str__(self): - return "Subgroup => generator %s, order: %d, cofactor: %d on Field => prime %d" % (self.g, self.n, - self.h, self.p) - - def __repr__(self): - return self.__str__() - - class Inf(object): def __init__(self, curve, x=None, y=None): self.x = x @@ -188,12 +188,6 @@ def __repr__(self): return self.__str__() -def make_keypair(curve): - priv = random.randint(1, curve.field.n) - pub = priv * curve.g - return Keypair(curve, priv, pub) - - class Keypair(object): def __init__(self, curve, priv=None, pub=None): if priv is None and pub is None: @@ -222,3 +216,9 @@ def get_secret(self, keypair): else: raise ValueError("Missing crypto material to generate DH secret") return secret + + +def make_keypair(curve): + priv = random.randint(1, curve.field.n) + pub = priv * curve.g + return Keypair(curve, priv, pub) From 48a3826f1faf68b89e5dd69857b4f27e3b8746f3 Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Wed, 8 Nov 2023 13:21:45 -0700 Subject: [PATCH 06/13] Remove Python 2-style class declarations. Python 2 is well and truly gone. --- tinyec/ec.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tinyec/ec.py b/tinyec/ec.py index 10c0045..1063a6e 100644 --- a/tinyec/ec.py +++ b/tinyec/ec.py @@ -20,7 +20,7 @@ def mod_inv(a, p): return x % p -class SubGroup(object): +class SubGroup: def __init__(self, p, g, n, h): self.p = p self.g = g @@ -43,7 +43,7 @@ def __repr__(self): return self.__str__() -class Curve(object): +class Curve: def __init__(self, a, b, field, name="undefined"): self.name = name self.a = a @@ -69,7 +69,7 @@ def __str__(self): return "\"%s\" => y^2 = x^3 + %dx + %d (mod %d)" % (self.name, self.a, self.b, self.field.p) -class Inf(object): +class Inf: def __init__(self, curve, x=None, y=None): self.x = x self.y = y @@ -106,7 +106,7 @@ def __repr__(self): return self.__str__() -class Point(object): +class Point: def __init__(self, curve, x, y): self.curve = curve self.x = x @@ -188,7 +188,7 @@ def __repr__(self): return self.__str__() -class Keypair(object): +class Keypair: def __init__(self, curve, priv=None, pub=None): if priv is None and pub is None: raise ValueError("Private and/or public key must be provided") @@ -203,7 +203,7 @@ def __init__(self, curve, priv=None, pub=None): self.pub = self.priv * self.curve.g -class ECDH(object): +class ECDH: def __init__(self, keypair): self.keypair = keypair From c15a57139cf3db99f8bc105daee8450c6b91d357 Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Wed, 8 Nov 2023 14:36:02 -0700 Subject: [PATCH 07/13] Add initial types to function signatures. We still have a variety of typing errors, but this at least gives us something to target based on the behavior of the unit tests. --- tinyec/ec.py | 69 ++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/tinyec/ec.py b/tinyec/ec.py index 1063a6e..eb71cb9 100644 --- a/tinyec/ec.py +++ b/tinyec/ec.py @@ -1,8 +1,9 @@ import random +from typing import Any, Optional, Tuple, Union import warnings -def egcd(a, b): +def egcd(a: int, b: int) -> Tuple[int, int, int]: if a == 0: return b, 0, 1 else: @@ -10,7 +11,7 @@ def egcd(a, b): return g, x - (b // a) * y, y -def mod_inv(a, p): +def mod_inv(a: int, p: int) -> int: if a < 0: return p - mod_inv(-a, p) g, x, y = egcd(a, p) @@ -21,69 +22,69 @@ def mod_inv(a, p): class SubGroup: - def __init__(self, p, g, n, h): + def __init__(self, p: int, g: Tuple[int, int], n: int, h:int): self.p = p self.g = g self.n = n self.h = h - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: if not isinstance(other, SubGroup): return False return self.p == other.p and self.g == other.g and self.n == other.n and self.h == other.h - def __ne__(self, other): + def __ne__(self, other: Any) -> bool: return not self.__eq__(other) - def __str__(self): + def __str__(self) -> str: return "Subgroup => generator %s, order: %d, cofactor: %d on Field => prime %d" % (self.g, self.n, self.h, self.p) - def __repr__(self): + def __repr__(self) -> str: return self.__str__() class Curve: - def __init__(self, a, b, field, name="undefined"): + def __init__(self, a: int, b: int, field: SubGroup, name:str="undefined"): self.name = name self.a = a self.b = b self.field = field self.g = Point(self, self.field.g[0], self.field.g[1]) - def is_singular(self): + def is_singular(self) -> bool: return (4 * self.a**3 + 27 * self.b**2) % self.field.p == 0 - def on_curve(self, x, y): + def on_curve(self, x: int, y: int) -> bool: return (y**2 - x**3 - self.a * x - self.b) % self.field.p == 0 - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: if not isinstance(other, Curve): return False return self.a == other.a and self.b == other.b and self.field == other.field - def __ne__(self, other): + def __ne__(self, other: Any) -> bool: return not self.__eq__(other) - def __str__(self): + def __str__(self) -> str: return "\"%s\" => y^2 = x^3 + %dx + %d (mod %d)" % (self.name, self.a, self.b, self.field.p) class Inf: - def __init__(self, curve, x=None, y=None): + def __init__(self, curve: Curve, x: Optional[int]=None, y: Optional[int]=None): self.x = x self.y = y self.curve = curve - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: if not isinstance(other, Inf): return False return self.curve == other.curve - def __ne__(self, other): + def __ne__(self, other: Any) -> bool: return not self.__eq__(other) - def __add__(self, other): + def __add__(self, other: Any) -> Union["Inf", "Point"]: if isinstance(other, Inf): return Inf() if isinstance(other, Point): @@ -91,7 +92,7 @@ def __add__(self, other): raise TypeError("Unsupported operand type(s) for +: '%s' and '%s'" % (other.__class__.__name__, self.__class__.__name__)) - def __sub__(self, other): + def __sub__(self, other: Any) -> Union["Inf", "Point"]: if isinstance(other, Inf): return Inf() if isinstance(other, Point): @@ -99,15 +100,15 @@ def __sub__(self, other): raise TypeError("Unsupported operand type(s) for +: '%s' and '%s'" % (other.__class__.__name__, self.__class__.__name__)) - def __str__(self): + def __str__(self) -> str: return "%s on %s" % (self.__class__.__name__, self.curve) - def __repr__(self): + def __repr__(self) -> str: return self.__str__() class Point: - def __init__(self, curve, x, y): + def __init__(self, curve: Curve, x: int, y: int): self.curve = curve self.x = x self.y = y @@ -117,21 +118,21 @@ def __init__(self, curve, x, y): warnings.warn("Point (%d, %d) is not on curve \"%s\"" % (self.x, self.y, self.curve)) self.on_curve = False - def __m(self, p, q): + def __m(self, p: "Point", q: "Point") -> int: if p.x == q.x: return (3 * p.x**2 + self.curve.a) * mod_inv(2 * p.y, self.p) else: return (p.y - q.y) * mod_inv(p.x - q.x, self.p) - def __eq__(self, other): + def __eq__(self, other: Any) -> bool: if not isinstance(other, Point): return False return self.x == other.x and self.y == other.y and self.curve == other.curve - def __ne__(self, other): + def __ne__(self, other: Any) -> bool: return not self.__eq__(other) - def __add__(self, other): + def __add__(self, other: Any) -> Union[Inf, "Point"]: if isinstance(other, Inf): return self if isinstance(other, Point): @@ -148,7 +149,7 @@ def __add__(self, other): raise TypeError("Unsupported operand type(s) for +: '%s' and '%s'" % (other.__class__.__name__, self.__class__.__name__)) - def __sub__(self, other): + def __sub__(self, other: Any) -> Union[Inf, "Point"]: if isinstance(other, Inf): return self.__add__(other) if isinstance(other, Point): @@ -157,7 +158,7 @@ def __sub__(self, other): raise TypeError("Unsupported operand type(s) for -: '%s' and '%s'" % (other.__class__.__name__, self.__class__.__name__)) - def __mul__(self, other): + def __mul__(self, other: Any) -> Union[Inf, "Point"]: if isinstance(other, Inf): return Inf(self.curve) if isinstance(other, int): @@ -178,18 +179,18 @@ def __mul__(self, other): raise TypeError("Unsupported operand type(s) for *: '%s' and '%s'" % (other.__class__.__name__, self.__class__.__name__)) - def __rmul__(self, other): + def __rmul__(self, other: Any) -> Union[Inf, "Point"]: return self.__mul__(other) - def __str__(self): + def __str__(self) -> str: return "(%d, %d) %s %s" % (self.x, self.y, "on" if self.on_curve else "off", self.curve) - def __repr__(self): + def __repr__(self) -> str: return self.__str__() class Keypair: - def __init__(self, curve, priv=None, pub=None): + def __init__(self, curve: Curve, priv: Optional[int]=None, pub: Optional[Point]=None): if priv is None and pub is None: raise ValueError("Private and/or public key must be provided") self.curve = curve @@ -204,10 +205,10 @@ def __init__(self, curve, priv=None, pub=None): class ECDH: - def __init__(self, keypair): + def __init__(self, keypair: Keypair): self.keypair = keypair - def get_secret(self, keypair): + def get_secret(self, keypair: Keypair) -> Point: # Don;t check if both keypairs are on the same curve. Should raise a warning only if self.keypair.can_sign and keypair.can_encrypt: secret = self.keypair.priv * keypair.pub @@ -218,7 +219,7 @@ def get_secret(self, keypair): return secret -def make_keypair(curve): +def make_keypair(curve: Curve) -> Keypair: priv = random.randint(1, curve.field.n) pub = priv * curve.g return Keypair(curve, priv, pub) From 77b16f4d724d25e2290d3a1c085bb6bb2a2b50b2 Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Wed, 8 Nov 2023 14:37:22 -0700 Subject: [PATCH 08/13] Always provide a curve to the Inf constructor. I don't know if this is mathematically correct, but I'm not sure why the code is returning a new Inf rather than self. I'll leave it for now. --- tinyec/ec.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tinyec/ec.py b/tinyec/ec.py index eb71cb9..cdb49d8 100644 --- a/tinyec/ec.py +++ b/tinyec/ec.py @@ -86,7 +86,7 @@ def __ne__(self, other: Any) -> bool: def __add__(self, other: Any) -> Union["Inf", "Point"]: if isinstance(other, Inf): - return Inf() + return Inf(self.curve) if isinstance(other, Point): return other raise TypeError("Unsupported operand type(s) for +: '%s' and '%s'" % (other.__class__.__name__, @@ -94,7 +94,7 @@ def __add__(self, other: Any) -> Union["Inf", "Point"]: def __sub__(self, other: Any) -> Union["Inf", "Point"]: if isinstance(other, Inf): - return Inf() + return Inf(self.curve) if isinstance(other, Point): return other raise TypeError("Unsupported operand type(s) for +: '%s' and '%s'" % (other.__class__.__name__, From f36a6d9de70f238b32e0b7622a2ea6a2be7061cd Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Wed, 8 Nov 2023 14:42:22 -0700 Subject: [PATCH 09/13] Fix type inferrence in Point.__mul__ We manually specify the expected types for addend and result since they may take either Point or Inf. Also de-indent by raising an exception earlien for clarity. --- tinyec/ec.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/tinyec/ec.py b/tinyec/ec.py index cdb49d8..1f4332f 100644 --- a/tinyec/ec.py +++ b/tinyec/ec.py @@ -161,23 +161,19 @@ def __sub__(self, other: Any) -> Union[Inf, "Point"]: def __mul__(self, other: Any) -> Union[Inf, "Point"]: if isinstance(other, Inf): return Inf(self.curve) - if isinstance(other, int): - if other % self.curve.field.n == 0: - return Inf(self.curve) - if other < 0: - addend = Point(self.curve, self.x, -self.y % self.p) - else: - addend = self - result = Inf(self.curve) - # Iterate over all bits starting by the LSB - for bit in reversed([int(i) for i in bin(abs(other))[2:]]): - if bit == 1: - result += addend - addend += addend - return result - else: + if not isinstance(other, int): raise TypeError("Unsupported operand type(s) for *: '%s' and '%s'" % (other.__class__.__name__, self.__class__.__name__)) + if other % self.curve.field.n == 0: + return Inf(self.curve) + addend: Union[Inf, Point] = Point(self.curve, self.x, -self.y % self.p) if other < 0 else self + result: Union[Inf, Point] = Inf(self.curve) + # Iterate over all bits starting by the LSB + for bit in reversed([int(i) for i in bin(abs(other))[2:]]): + if bit == 1: + result += addend + addend += addend + return result def __rmul__(self, other: Any) -> Union[Inf, "Point"]: return self.__mul__(other) From 5e5c64af1df1a844b0b26e17e6d1845bc7f120e7 Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Wed, 8 Nov 2023 14:46:32 -0700 Subject: [PATCH 10/13] Fix type inferrence within Keypair constructor. This one is a bit tricky. We need to provide clear paths where the mypy engine can eliminate the potential to have None types. This means creating a somewhat clunky conditional for setting up the member variables and raising exceptions in a few different places. --- tinyec/ec.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/tinyec/ec.py b/tinyec/ec.py index 1f4332f..ec10165 100644 --- a/tinyec/ec.py +++ b/tinyec/ec.py @@ -187,17 +187,27 @@ def __repr__(self) -> str: class Keypair: def __init__(self, curve: Curve, priv: Optional[int]=None, pub: Optional[Point]=None): - if priv is None and pub is None: - raise ValueError("Private and/or public key must be provided") self.curve = curve self.can_sign = True self.can_encrypt = True + self.priv: Optional[int] = priv if priv is None: self.can_sign = False - self.priv = priv - self.pub = pub - if pub is None: - self.pub = self.priv * self.curve.g + if pub is None: + raise ValueError("At least one of private or public key must be provided.") + self.pub = pub + else: + self.can_sign = True + if pub is None: + self.pub = self._generate_pub(priv) + else: + self.pub = pub + + def _generate_pub(self, priv: int) -> Point: + result = self.priv * self.curve.g + if isinstance(result, Inf): + raise ValueError("Generated public value at infinity point.") + return result class ECDH: From b1059ee6beb7f30cf609a181dbbaad991d4ad4e6 Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Wed, 8 Nov 2023 14:48:52 -0700 Subject: [PATCH 11/13] Fix type inferrence for ECDH.get_secret. This requires explicitly checking for None so that mypy can detect that the Optional has been resolved and we no longer have uncertain types. This does somewhat damage the readability of X.can_sign, so the double check is left in at a slight performance penalty. I also renamed the "keypair" parameter as I find it more clearly readable instead of having to realize that self.keypair and keypair are different. Also avoid the Union type in return by catching and raising on the Inf case. This may not be strictly correct. --- tinyec/ec.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tinyec/ec.py b/tinyec/ec.py index ec10165..1eca10d 100644 --- a/tinyec/ec.py +++ b/tinyec/ec.py @@ -214,14 +214,16 @@ class ECDH: def __init__(self, keypair: Keypair): self.keypair = keypair - def get_secret(self, keypair: Keypair) -> Point: - # Don;t check if both keypairs are on the same curve. Should raise a warning only - if self.keypair.can_sign and keypair.can_encrypt: - secret = self.keypair.priv * keypair.pub - elif self.keypair.can_encrypt and keypair.can_sign: - secret = self.keypair.pub * keypair.priv + def get_secret(self, other: Keypair) -> Point: + # Don't check if both keypairs are on the same curve. Should raise a warning only + if self.keypair.priv is not None and self.keypair.can_sign and other.can_encrypt: + secret = self.keypair.priv * other.pub + elif self.keypair.can_encrypt and other.can_sign and other.priv is not None: + secret = self.keypair.pub * other.priv else: raise ValueError("Missing crypto material to generate DH secret") + if isinstance(secret, Inf): + raise ValueError("Got a secret that is at the infinite point") return secret From 1dfd21992416d1c0576c59b8900e3e1abfcb5a0b Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Wed, 8 Nov 2023 14:52:10 -0700 Subject: [PATCH 12/13] Fix Union return in make_keypair Without this we may have an Inf type, which we really shouldn't ever get. --- tinyec/ec.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tinyec/ec.py b/tinyec/ec.py index 1eca10d..fddc319 100644 --- a/tinyec/ec.py +++ b/tinyec/ec.py @@ -230,4 +230,6 @@ def get_secret(self, other: Keypair) -> Point: def make_keypair(curve: Curve) -> Keypair: priv = random.randint(1, curve.field.n) pub = priv * curve.g + if isinstance(pub, Inf): + raise RuntimeError("Created infinite public key") return Keypair(curve, priv, pub) From c5aa70f8b89d2af98a9a15f080f04a2b7e6e8a30 Mon Sep 17 00:00:00 2001 From: Eli Ribble Date: Wed, 8 Nov 2023 14:54:34 -0700 Subject: [PATCH 13/13] Add typing information to the curve registry. The big change here is that we now make the known curves into typed data structures instead of just nested mappings. This represents a change in the public API. --- tinyec/registry.py | 207 ++++++++++++++++++++++++--------------------- 1 file changed, 111 insertions(+), 96 deletions(-) diff --git a/tinyec/registry.py b/tinyec/registry.py index cd95714..52d213f 100644 --- a/tinyec/registry.py +++ b/tinyec/registry.py @@ -1,109 +1,124 @@ +from dataclasses import dataclass +from typing import Optional, Mapping, Tuple, Union + import tinyec.ec as ec +@dataclass +class KnownCurve: + name: str + p: int + a: int + b: int + g: Tuple[int, int] + n: int + h: int -EC_CURVE_REGISTRY = {"brainpoolP160r1": {"p": 0xE95E4A5F737059DC60DFC7AD95B3D8139515620F, - "a": 0x340E7BE2A280EB74E2BE61BADA745D97E8F7C300, - "b": 0x1E589A8595423412134FAA2DBDEC95C8D8675E58, - "g": (0xBED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3, - 0x1667CB477A1A8EC338F94741669C976316DA6321), - "n": 0xE95E4A5F737059DC60DF5991D45029409E60FC09, - "h": 0x1}, - "brainpoolP192r1": {"p": 0xC302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297, - "a": 0x6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF, - "b": 0x469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9, - "g": (0xC0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6, - 0x14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F), - "n": 0xC302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1, - "h": 0x1}, - "brainpoolP224r1": {"p": 0xD7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF, - "a": 0x68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43, - "b": 0x2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B, - "g": (0x0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D, - 0x58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD), - "n": 0xD7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F, - "h": 0x1}, - "brainpoolP256r1": {"p": 0xA9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377, - "a": 0x7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9, - "b": 0x26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6, - "g": (0x8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262, - 0x547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997), - "n": 0xA9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7, - "h": 0x1}, - "brainpoolP320r1": {"p": 0xD35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27, - "a": 0x3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB4, - "b": 0x520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6, - "g": (0x43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C710AF8D0D39E20611, - 0x14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7D35245D1692E8EE1), - "n": 0xD35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658E98691555B44C59311, - "h": 0x1}, - "brainpoolP384r1": {"p": 0x8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53, - "a": 0x7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826, - "b": 0x04A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11, - "g": (0x1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E, - 0x8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315), - "n": 0x8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565, - "h": 0x1}, - "brainpoolP512r1": {"p": 0xAADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3, - "a": 0x7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA, - "b": 0x3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723, - "g": (0x81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822, - 0x7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892), - "n": 0xAADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069, - "h": 0x1}, - "secp192r1": {"p": 0xfffffffffffffffffffffffffffffffeffffffffffffffff, - "a": 0xfffffffffffffffffffffffffffffffefffffffffffffffc, - "b": 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1, - "g": (0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012, - 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811), - "n": 0xffffffffffffffffffffffff99def836146bc9b1b4d22831, - "h": 0x1}, - "secp224r1": {"p": 0xffffffffffffffffffffffffffffffff000000000000000000000001, - "a": 0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe, - "b": 0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4, - "g": (0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21, - 0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34), - "n": 0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d, - "h": 0x1}, - "secp256r1": {"p": 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff, - "a": 0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc, - "b": 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b, - "g": (0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, - 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5), - "n": 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551, - "h": 0x1}, - "secp256k1": {"p": 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f, - "a": 0x0, - "b": 0x7, - "g": (0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798, - 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8), - "n": 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141, - "h": 0x1}, - "secp384r1": {"p": 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff, - "a": 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc, - "b": 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef, - "g": (0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7, - 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f), - "n": 0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973, - "h": 0x1}, - "secp521r1": {"p": 0x000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, - "a": 0x000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc, - "b": 0x00000051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00, - "g": (0x000000c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66, - 0x0000011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650), - "n": 0x000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409, - "h": 0x1}} +KNOWN_CURVES = [ + KnownCurve("brainpoolP160r1", + p=0xE95E4A5F737059DC60DFC7AD95B3D8139515620F, + a=0x340E7BE2A280EB74E2BE61BADA745D97E8F7C300, + b=0x1E589A8595423412134FAA2DBDEC95C8D8675E58, + g=(0xBED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3, 0x1667CB477A1A8EC338F94741669C976316DA6321), + n=0xE95E4A5F737059DC60DF5991D45029409E60FC09, + h=0x1), + KnownCurve("brainpoolP192r1", + p=0xC302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297, + a=0x6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF, + b=0x469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9, + g=(0xC0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6, 0x14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F), + n=0xC302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1, + h=0x1), + KnownCurve("brainpoolP224r1", + p=0xD7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF, + a=0x68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43, + b=0x2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B, + g=(0x0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D, 0x58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD), + n=0xD7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F, + h=0x1), + KnownCurve("brainpoolP256r1", + p=0xA9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377, + a=0x7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9, + b=0x26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6, + g=(0x8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262, 0x547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997), + n=0xA9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7, + h=0x1), + KnownCurve("brainpoolP320r1", + p=0xD35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27, + a=0x3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB4, + b=0x520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6, + g=(0x43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C710AF8D0D39E20611, 0x14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7D35245D1692E8EE1), + n=0xD35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658E98691555B44C59311, + h=0x1), + KnownCurve("brainpoolP384r1", + p=0x8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53, + a=0x7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826, + b=0x04A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11, + g=(0x1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E, 0x8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315), + n=0x8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565, + h=0x1), + KnownCurve("brainpoolP512r1", + p=0xAADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3, + a=0x7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA, + b=0x3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723, + g=(0x81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822, 0x7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892), + n=0xAADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069, + h=0x1), + KnownCurve("secp192r1", + p=0xfffffffffffffffffffffffffffffffeffffffffffffffff, + a=0xfffffffffffffffffffffffffffffffefffffffffffffffc, + b=0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1, + g=(0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012, 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811), + n=0xffffffffffffffffffffffff99def836146bc9b1b4d22831, + h=0x1), + KnownCurve("secp224r1", + p=0xffffffffffffffffffffffffffffffff000000000000000000000001, + a=0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe, + b=0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4, + g=(0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21, 0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34), + n=0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d, + h=0x1), + KnownCurve("secp256r1", + p=0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff, + a=0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc, + b=0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b, + g=(0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5), + n=0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551, + h=0x1), + KnownCurve("secp256k1", + p=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f, + a=0x0, + b=0x7, + g=(0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798, 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8), + n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141, + h=0x1), + KnownCurve("secp384r1", + p=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff, + a=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc, + b=0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef, + g=(0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7, 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f), + n=0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973, + h=0x1), + KnownCurve("secp521r1", + p=0x000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, + a=0x000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc, + b=0x00000051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00, + g=(0x000000c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66, 0x0000011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650), + n=0x000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409, + h=0x1), +] +EC_CURVE_REGISTRY: Mapping[str, KnownCurve] = {curve.name: curve for curve in KNOWN_CURVES} -def get_curve(name): - curve_params = {} +def get_curve(name: str) -> ec.Curve: + curve_params: Optional[KnownCurve] = None for k, v in EC_CURVE_REGISTRY.items(): if name.lower() == k.lower(): curve_params = v - if curve_params == {}: + if curve_params is None: raise ValueError("Unknown elliptic curve name") try: - sub_group = ec.SubGroup(curve_params["p"], curve_params["g"], curve_params["n"], curve_params["h"]) - curve = ec.Curve(curve_params["a"], curve_params["b"], sub_group, name) + sub_group = ec.SubGroup(curve_params.p, curve_params.g, curve_params.n, curve_params.h) + curve = ec.Curve(curve_params.a, curve_params.b, sub_group, name) except KeyError: raise RuntimeError("Missing parameters for curve %s" % name) return curve