Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/jose/backends/ecdsa_backend.py: 25%
80 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:16 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:16 +0000
1import hashlib
3import ecdsa
5from jose.backends.base import Key
6from jose.constants import ALGORITHMS
7from jose.exceptions import JWKError
8from jose.utils import base64_to_long, long_to_base64
11class ECDSAECKey(Key):
12 """
13 Performs signing and verification operations using
14 ECDSA and the specified hash function
16 This class requires the ecdsa package to be installed.
18 This is based off of the implementation in PyJWT 0.3.2
19 """
21 SHA256 = hashlib.sha256
22 SHA384 = hashlib.sha384
23 SHA512 = hashlib.sha512
25 CURVE_MAP = {
26 SHA256: ecdsa.curves.NIST256p,
27 SHA384: ecdsa.curves.NIST384p,
28 SHA512: ecdsa.curves.NIST521p,
29 }
30 CURVE_NAMES = (
31 (ecdsa.curves.NIST256p, "P-256"),
32 (ecdsa.curves.NIST384p, "P-384"),
33 (ecdsa.curves.NIST521p, "P-521"),
34 )
36 def __init__(self, key, algorithm):
37 if algorithm not in ALGORITHMS.EC:
38 raise JWKError("hash_alg: %s is not a valid hash algorithm" % algorithm)
40 self.hash_alg = {
41 ALGORITHMS.ES256: self.SHA256,
42 ALGORITHMS.ES384: self.SHA384,
43 ALGORITHMS.ES512: self.SHA512,
44 }.get(algorithm)
45 self._algorithm = algorithm
47 self.curve = self.CURVE_MAP.get(self.hash_alg)
49 if isinstance(key, (ecdsa.SigningKey, ecdsa.VerifyingKey)):
50 self.prepared_key = key
51 return
53 if isinstance(key, dict):
54 self.prepared_key = self._process_jwk(key)
55 return
57 if isinstance(key, str):
58 key = key.encode("utf-8")
60 if isinstance(key, bytes):
61 # Attempt to load key. We don't know if it's
62 # a Signing Key or a Verifying Key, so we try
63 # the Verifying Key first.
64 try:
65 key = ecdsa.VerifyingKey.from_pem(key)
66 except ecdsa.der.UnexpectedDER:
67 key = ecdsa.SigningKey.from_pem(key)
68 except Exception as e:
69 raise JWKError(e)
71 self.prepared_key = key
72 return
74 raise JWKError("Unable to parse an ECKey from key: %s" % key)
76 def _process_jwk(self, jwk_dict):
77 if not jwk_dict.get("kty") == "EC":
78 raise JWKError("Incorrect key type. Expected: 'EC', Received: %s" % jwk_dict.get("kty"))
80 if not all(k in jwk_dict for k in ["x", "y", "crv"]):
81 raise JWKError("Mandatory parameters are missing")
83 if "d" in jwk_dict:
84 # We are dealing with a private key; the secret exponent is enough
85 # to create an ecdsa key.
86 d = base64_to_long(jwk_dict.get("d"))
87 return ecdsa.keys.SigningKey.from_secret_exponent(d, self.curve)
88 else:
89 x = base64_to_long(jwk_dict.get("x"))
90 y = base64_to_long(jwk_dict.get("y"))
92 if not ecdsa.ecdsa.point_is_valid(self.curve.generator, x, y):
93 raise JWKError(f"Point: {x}, {y} is not a valid point")
95 point = ecdsa.ellipticcurve.Point(self.curve.curve, x, y, self.curve.order)
96 return ecdsa.keys.VerifyingKey.from_public_point(point, self.curve)
98 def sign(self, msg):
99 return self.prepared_key.sign(
100 msg, hashfunc=self.hash_alg, sigencode=ecdsa.util.sigencode_string, allow_truncate=False
101 )
103 def verify(self, msg, sig):
104 try:
105 return self.prepared_key.verify(
106 sig, msg, hashfunc=self.hash_alg, sigdecode=ecdsa.util.sigdecode_string, allow_truncate=False
107 )
108 except Exception:
109 return False
111 def is_public(self):
112 return isinstance(self.prepared_key, ecdsa.VerifyingKey)
114 def public_key(self):
115 if self.is_public():
116 return self
117 return self.__class__(self.prepared_key.get_verifying_key(), self._algorithm)
119 def to_pem(self):
120 return self.prepared_key.to_pem()
122 def to_dict(self):
123 if not self.is_public():
124 public_key = self.prepared_key.get_verifying_key()
125 else:
126 public_key = self.prepared_key
127 crv = None
128 for key, value in self.CURVE_NAMES:
129 if key == self.prepared_key.curve:
130 crv = value
131 if not crv:
132 raise KeyError(f"Can't match {self.prepared_key.curve}")
134 # Calculate the key size in bytes. Section 6.2.1.2 and 6.2.1.3 of
135 # RFC7518 prescribes that the 'x', 'y' and 'd' parameters of the curve
136 # points must be encoded as octed-strings of this length.
137 key_size = self.prepared_key.curve.baselen
139 data = {
140 "alg": self._algorithm,
141 "kty": "EC",
142 "crv": crv,
143 "x": long_to_base64(public_key.pubkey.point.x(), size=key_size).decode("ASCII"),
144 "y": long_to_base64(public_key.pubkey.point.y(), size=key_size).decode("ASCII"),
145 }
147 if not self.is_public():
148 data["d"] = long_to_base64(self.prepared_key.privkey.secret_multiplier, size=key_size).decode("ASCII")
150 return data