cryptography_backend.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605
  1. import math
  2. import warnings
  3. from cryptography.exceptions import InvalidSignature, InvalidTag
  4. from cryptography.hazmat.backends import default_backend
  5. from cryptography.hazmat.bindings.openssl.binding import Binding
  6. from cryptography.hazmat.primitives import hashes, hmac, serialization
  7. from cryptography.hazmat.primitives.asymmetric import ec, padding, rsa
  8. from cryptography.hazmat.primitives.asymmetric.utils import decode_dss_signature, encode_dss_signature
  9. from cryptography.hazmat.primitives.ciphers import Cipher, aead, algorithms, modes
  10. from cryptography.hazmat.primitives.keywrap import InvalidUnwrap, aes_key_unwrap, aes_key_wrap
  11. from cryptography.hazmat.primitives.padding import PKCS7
  12. from cryptography.hazmat.primitives.serialization import load_pem_private_key, load_pem_public_key
  13. from cryptography.utils import int_to_bytes
  14. from cryptography.x509 import load_pem_x509_certificate
  15. from ..constants import ALGORITHMS
  16. from ..exceptions import JWEError, JWKError
  17. from ..utils import base64_to_long, base64url_decode, base64url_encode, ensure_binary, long_to_base64
  18. from .base import Key
  19. _binding = None
  20. def get_random_bytes(num_bytes):
  21. """
  22. Get random bytes
  23. Currently, Cryptography returns OS random bytes. If you want OpenSSL
  24. generated random bytes, you'll have to switch the RAND engine after
  25. initializing the OpenSSL backend
  26. Args:
  27. num_bytes (int): Number of random bytes to generate and return
  28. Returns:
  29. bytes: Random bytes
  30. """
  31. global _binding
  32. if _binding is None:
  33. _binding = Binding()
  34. buf = _binding.ffi.new("char[]", num_bytes)
  35. _binding.lib.RAND_bytes(buf, num_bytes)
  36. rand_bytes = _binding.ffi.buffer(buf, num_bytes)[:]
  37. return rand_bytes
  38. class CryptographyECKey(Key):
  39. SHA256 = hashes.SHA256
  40. SHA384 = hashes.SHA384
  41. SHA512 = hashes.SHA512
  42. def __init__(self, key, algorithm, cryptography_backend=default_backend):
  43. if algorithm not in ALGORITHMS.EC:
  44. raise JWKError("hash_alg: %s is not a valid hash algorithm" % algorithm)
  45. self.hash_alg = {
  46. ALGORITHMS.ES256: self.SHA256,
  47. ALGORITHMS.ES384: self.SHA384,
  48. ALGORITHMS.ES512: self.SHA512,
  49. }.get(algorithm)
  50. self._algorithm = algorithm
  51. self.cryptography_backend = cryptography_backend
  52. if hasattr(key, "public_bytes") or hasattr(key, "private_bytes"):
  53. self.prepared_key = key
  54. return
  55. if hasattr(key, "to_pem"):
  56. # convert to PEM and let cryptography below load it as PEM
  57. key = key.to_pem().decode("utf-8")
  58. if isinstance(key, dict):
  59. self.prepared_key = self._process_jwk(key)
  60. return
  61. if isinstance(key, str):
  62. key = key.encode("utf-8")
  63. if isinstance(key, bytes):
  64. # Attempt to load key. We don't know if it's
  65. # a Public Key or a Private Key, so we try
  66. # the Public Key first.
  67. try:
  68. try:
  69. key = load_pem_public_key(key, self.cryptography_backend())
  70. except ValueError:
  71. key = load_pem_private_key(key, password=None, backend=self.cryptography_backend())
  72. except Exception as e:
  73. raise JWKError(e)
  74. self.prepared_key = key
  75. return
  76. raise JWKError("Unable to parse an ECKey from key: %s" % key)
  77. def _process_jwk(self, jwk_dict):
  78. if not jwk_dict.get("kty") == "EC":
  79. 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")
  82. x = base64_to_long(jwk_dict.get("x"))
  83. y = base64_to_long(jwk_dict.get("y"))
  84. curve = {
  85. "P-256": ec.SECP256R1,
  86. "P-384": ec.SECP384R1,
  87. "P-521": ec.SECP521R1,
  88. }[jwk_dict["crv"]]
  89. public = ec.EllipticCurvePublicNumbers(x, y, curve())
  90. if "d" in jwk_dict:
  91. d = base64_to_long(jwk_dict.get("d"))
  92. private = ec.EllipticCurvePrivateNumbers(d, public)
  93. return private.private_key(self.cryptography_backend())
  94. else:
  95. return public.public_key(self.cryptography_backend())
  96. def _sig_component_length(self):
  97. """Determine the correct serialization length for an encoded signature component.
  98. This is the number of bytes required to encode the maximum key value.
  99. """
  100. return int(math.ceil(self.prepared_key.key_size / 8.0))
  101. def _der_to_raw(self, der_signature):
  102. """Convert signature from DER encoding to RAW encoding."""
  103. r, s = decode_dss_signature(der_signature)
  104. component_length = self._sig_component_length()
  105. return int_to_bytes(r, component_length) + int_to_bytes(s, component_length)
  106. def _raw_to_der(self, raw_signature):
  107. """Convert signature from RAW encoding to DER encoding."""
  108. component_length = self._sig_component_length()
  109. if len(raw_signature) != int(2 * component_length):
  110. raise ValueError("Invalid signature")
  111. r_bytes = raw_signature[:component_length]
  112. s_bytes = raw_signature[component_length:]
  113. r = int.from_bytes(r_bytes, "big")
  114. s = int.from_bytes(s_bytes, "big")
  115. return encode_dss_signature(r, s)
  116. def sign(self, msg):
  117. if self.hash_alg.digest_size * 8 > self.prepared_key.curve.key_size:
  118. raise TypeError(
  119. "this curve (%s) is too short "
  120. "for your digest (%d)" % (self.prepared_key.curve.name, 8 * self.hash_alg.digest_size)
  121. )
  122. signature = self.prepared_key.sign(msg, ec.ECDSA(self.hash_alg()))
  123. return self._der_to_raw(signature)
  124. def verify(self, msg, sig):
  125. try:
  126. signature = self._raw_to_der(sig)
  127. self.prepared_key.verify(signature, msg, ec.ECDSA(self.hash_alg()))
  128. return True
  129. except Exception:
  130. return False
  131. def is_public(self):
  132. return hasattr(self.prepared_key, "public_bytes")
  133. def public_key(self):
  134. if self.is_public():
  135. return self
  136. return self.__class__(self.prepared_key.public_key(), self._algorithm)
  137. def to_pem(self):
  138. if self.is_public():
  139. pem = self.prepared_key.public_bytes(
  140. encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo
  141. )
  142. return pem
  143. pem = self.prepared_key.private_bytes(
  144. encoding=serialization.Encoding.PEM,
  145. format=serialization.PrivateFormat.TraditionalOpenSSL,
  146. encryption_algorithm=serialization.NoEncryption(),
  147. )
  148. return pem
  149. def to_dict(self):
  150. if not self.is_public():
  151. public_key = self.prepared_key.public_key()
  152. else:
  153. public_key = self.prepared_key
  154. crv = {
  155. "secp256r1": "P-256",
  156. "secp384r1": "P-384",
  157. "secp521r1": "P-521",
  158. }[self.prepared_key.curve.name]
  159. # Calculate the key size in bytes. Section 6.2.1.2 and 6.2.1.3 of
  160. # RFC7518 prescribes that the 'x', 'y' and 'd' parameters of the curve
  161. # points must be encoded as octed-strings of this length.
  162. key_size = (self.prepared_key.curve.key_size + 7) // 8
  163. data = {
  164. "alg": self._algorithm,
  165. "kty": "EC",
  166. "crv": crv,
  167. "x": long_to_base64(public_key.public_numbers().x, size=key_size).decode("ASCII"),
  168. "y": long_to_base64(public_key.public_numbers().y, size=key_size).decode("ASCII"),
  169. }
  170. if not self.is_public():
  171. private_value = self.prepared_key.private_numbers().private_value
  172. data["d"] = long_to_base64(private_value, size=key_size).decode("ASCII")
  173. return data
  174. class CryptographyRSAKey(Key):
  175. SHA256 = hashes.SHA256
  176. SHA384 = hashes.SHA384
  177. SHA512 = hashes.SHA512
  178. RSA1_5 = padding.PKCS1v15()
  179. RSA_OAEP = padding.OAEP(padding.MGF1(hashes.SHA1()), hashes.SHA1(), None)
  180. RSA_OAEP_256 = padding.OAEP(padding.MGF1(hashes.SHA256()), hashes.SHA256(), None)
  181. def __init__(self, key, algorithm, cryptography_backend=default_backend):
  182. if algorithm not in ALGORITHMS.RSA:
  183. raise JWKError("hash_alg: %s is not a valid hash algorithm" % algorithm)
  184. self.hash_alg = {
  185. ALGORITHMS.RS256: self.SHA256,
  186. ALGORITHMS.RS384: self.SHA384,
  187. ALGORITHMS.RS512: self.SHA512,
  188. }.get(algorithm)
  189. self._algorithm = algorithm
  190. self.padding = {
  191. ALGORITHMS.RSA1_5: self.RSA1_5,
  192. ALGORITHMS.RSA_OAEP: self.RSA_OAEP,
  193. ALGORITHMS.RSA_OAEP_256: self.RSA_OAEP_256,
  194. }.get(algorithm)
  195. self.cryptography_backend = cryptography_backend
  196. # if it conforms to RSAPublicKey interface
  197. if hasattr(key, "public_bytes") and hasattr(key, "public_numbers"):
  198. self.prepared_key = key
  199. return
  200. if isinstance(key, dict):
  201. self.prepared_key = self._process_jwk(key)
  202. return
  203. if isinstance(key, str):
  204. key = key.encode("utf-8")
  205. if isinstance(key, bytes):
  206. try:
  207. if key.startswith(b"-----BEGIN CERTIFICATE-----"):
  208. self._process_cert(key)
  209. return
  210. try:
  211. self.prepared_key = load_pem_public_key(key, self.cryptography_backend())
  212. except ValueError:
  213. self.prepared_key = load_pem_private_key(key, password=None, backend=self.cryptography_backend())
  214. except Exception as e:
  215. raise JWKError(e)
  216. return
  217. raise JWKError("Unable to parse an RSA_JWK from key: %s" % key)
  218. def _process_jwk(self, jwk_dict):
  219. if not jwk_dict.get("kty") == "RSA":
  220. raise JWKError("Incorrect key type. Expected: 'RSA', Received: %s" % jwk_dict.get("kty"))
  221. e = base64_to_long(jwk_dict.get("e", 256))
  222. n = base64_to_long(jwk_dict.get("n"))
  223. public = rsa.RSAPublicNumbers(e, n)
  224. if "d" not in jwk_dict:
  225. return public.public_key(self.cryptography_backend())
  226. else:
  227. # This is a private key.
  228. d = base64_to_long(jwk_dict.get("d"))
  229. extra_params = ["p", "q", "dp", "dq", "qi"]
  230. if any(k in jwk_dict for k in extra_params):
  231. # Precomputed private key parameters are available.
  232. if not all(k in jwk_dict for k in extra_params):
  233. # These values must be present when 'p' is according to
  234. # Section 6.3.2 of RFC7518, so if they are not we raise
  235. # an error.
  236. raise JWKError("Precomputed private key parameters are incomplete.")
  237. p = base64_to_long(jwk_dict["p"])
  238. q = base64_to_long(jwk_dict["q"])
  239. dp = base64_to_long(jwk_dict["dp"])
  240. dq = base64_to_long(jwk_dict["dq"])
  241. qi = base64_to_long(jwk_dict["qi"])
  242. else:
  243. # The precomputed private key parameters are not available,
  244. # so we use cryptography's API to fill them in.
  245. p, q = rsa.rsa_recover_prime_factors(n, e, d)
  246. dp = rsa.rsa_crt_dmp1(d, p)
  247. dq = rsa.rsa_crt_dmq1(d, q)
  248. qi = rsa.rsa_crt_iqmp(p, q)
  249. private = rsa.RSAPrivateNumbers(p, q, d, dp, dq, qi, public)
  250. return private.private_key(self.cryptography_backend())
  251. def _process_cert(self, key):
  252. key = load_pem_x509_certificate(key, self.cryptography_backend())
  253. self.prepared_key = key.public_key()
  254. def sign(self, msg):
  255. try:
  256. signature = self.prepared_key.sign(msg, padding.PKCS1v15(), self.hash_alg())
  257. except Exception as e:
  258. raise JWKError(e)
  259. return signature
  260. def verify(self, msg, sig):
  261. if not self.is_public():
  262. warnings.warn("Attempting to verify a message with a private key. " "This is not recommended.")
  263. try:
  264. self.public_key().prepared_key.verify(sig, msg, padding.PKCS1v15(), self.hash_alg())
  265. return True
  266. except InvalidSignature:
  267. return False
  268. def is_public(self):
  269. return hasattr(self.prepared_key, "public_bytes")
  270. def public_key(self):
  271. if self.is_public():
  272. return self
  273. return self.__class__(self.prepared_key.public_key(), self._algorithm)
  274. def to_pem(self, pem_format="PKCS8"):
  275. if self.is_public():
  276. if pem_format == "PKCS8":
  277. fmt = serialization.PublicFormat.SubjectPublicKeyInfo
  278. elif pem_format == "PKCS1":
  279. fmt = serialization.PublicFormat.PKCS1
  280. else:
  281. raise ValueError("Invalid format specified: %r" % pem_format)
  282. pem = self.prepared_key.public_bytes(encoding=serialization.Encoding.PEM, format=fmt)
  283. return pem
  284. if pem_format == "PKCS8":
  285. fmt = serialization.PrivateFormat.PKCS8
  286. elif pem_format == "PKCS1":
  287. fmt = serialization.PrivateFormat.TraditionalOpenSSL
  288. else:
  289. raise ValueError("Invalid format specified: %r" % pem_format)
  290. return self.prepared_key.private_bytes(
  291. encoding=serialization.Encoding.PEM, format=fmt, encryption_algorithm=serialization.NoEncryption()
  292. )
  293. def to_dict(self):
  294. if not self.is_public():
  295. public_key = self.prepared_key.public_key()
  296. else:
  297. public_key = self.prepared_key
  298. data = {
  299. "alg": self._algorithm,
  300. "kty": "RSA",
  301. "n": long_to_base64(public_key.public_numbers().n).decode("ASCII"),
  302. "e": long_to_base64(public_key.public_numbers().e).decode("ASCII"),
  303. }
  304. if not self.is_public():
  305. data.update(
  306. {
  307. "d": long_to_base64(self.prepared_key.private_numbers().d).decode("ASCII"),
  308. "p": long_to_base64(self.prepared_key.private_numbers().p).decode("ASCII"),
  309. "q": long_to_base64(self.prepared_key.private_numbers().q).decode("ASCII"),
  310. "dp": long_to_base64(self.prepared_key.private_numbers().dmp1).decode("ASCII"),
  311. "dq": long_to_base64(self.prepared_key.private_numbers().dmq1).decode("ASCII"),
  312. "qi": long_to_base64(self.prepared_key.private_numbers().iqmp).decode("ASCII"),
  313. }
  314. )
  315. return data
  316. def wrap_key(self, key_data):
  317. try:
  318. wrapped_key = self.prepared_key.encrypt(key_data, self.padding)
  319. except Exception as e:
  320. raise JWEError(e)
  321. return wrapped_key
  322. def unwrap_key(self, wrapped_key):
  323. try:
  324. unwrapped_key = self.prepared_key.decrypt(wrapped_key, self.padding)
  325. return unwrapped_key
  326. except Exception as e:
  327. raise JWEError(e)
  328. class CryptographyAESKey(Key):
  329. KEY_128 = (ALGORITHMS.A128GCM, ALGORITHMS.A128GCMKW, ALGORITHMS.A128KW, ALGORITHMS.A128CBC)
  330. KEY_192 = (ALGORITHMS.A192GCM, ALGORITHMS.A192GCMKW, ALGORITHMS.A192KW, ALGORITHMS.A192CBC)
  331. KEY_256 = (
  332. ALGORITHMS.A256GCM,
  333. ALGORITHMS.A256GCMKW,
  334. ALGORITHMS.A256KW,
  335. ALGORITHMS.A128CBC_HS256,
  336. ALGORITHMS.A256CBC,
  337. )
  338. KEY_384 = (ALGORITHMS.A192CBC_HS384,)
  339. KEY_512 = (ALGORITHMS.A256CBC_HS512,)
  340. AES_KW_ALGS = (ALGORITHMS.A128KW, ALGORITHMS.A192KW, ALGORITHMS.A256KW)
  341. MODES = {
  342. ALGORITHMS.A128GCM: modes.GCM,
  343. ALGORITHMS.A192GCM: modes.GCM,
  344. ALGORITHMS.A256GCM: modes.GCM,
  345. ALGORITHMS.A128CBC_HS256: modes.CBC,
  346. ALGORITHMS.A192CBC_HS384: modes.CBC,
  347. ALGORITHMS.A256CBC_HS512: modes.CBC,
  348. ALGORITHMS.A128CBC: modes.CBC,
  349. ALGORITHMS.A192CBC: modes.CBC,
  350. ALGORITHMS.A256CBC: modes.CBC,
  351. ALGORITHMS.A128GCMKW: modes.GCM,
  352. ALGORITHMS.A192GCMKW: modes.GCM,
  353. ALGORITHMS.A256GCMKW: modes.GCM,
  354. ALGORITHMS.A128KW: None,
  355. ALGORITHMS.A192KW: None,
  356. ALGORITHMS.A256KW: None,
  357. }
  358. def __init__(self, key, algorithm):
  359. if algorithm not in ALGORITHMS.AES:
  360. raise JWKError("%s is not a valid AES algorithm" % algorithm)
  361. if algorithm not in ALGORITHMS.SUPPORTED.union(ALGORITHMS.AES_PSEUDO):
  362. raise JWKError("%s is not a supported algorithm" % algorithm)
  363. self._algorithm = algorithm
  364. self._mode = self.MODES.get(self._algorithm)
  365. if algorithm in self.KEY_128 and len(key) != 16:
  366. raise JWKError(f"Key must be 128 bit for alg {algorithm}")
  367. elif algorithm in self.KEY_192 and len(key) != 24:
  368. raise JWKError(f"Key must be 192 bit for alg {algorithm}")
  369. elif algorithm in self.KEY_256 and len(key) != 32:
  370. raise JWKError(f"Key must be 256 bit for alg {algorithm}")
  371. elif algorithm in self.KEY_384 and len(key) != 48:
  372. raise JWKError(f"Key must be 384 bit for alg {algorithm}")
  373. elif algorithm in self.KEY_512 and len(key) != 64:
  374. raise JWKError(f"Key must be 512 bit for alg {algorithm}")
  375. self._key = key
  376. def to_dict(self):
  377. data = {"alg": self._algorithm, "kty": "oct", "k": base64url_encode(self._key)}
  378. return data
  379. def encrypt(self, plain_text, aad=None):
  380. plain_text = ensure_binary(plain_text)
  381. try:
  382. iv = get_random_bytes(algorithms.AES.block_size // 8)
  383. mode = self._mode(iv)
  384. if mode.name == "GCM":
  385. cipher = aead.AESGCM(self._key)
  386. cipher_text_and_tag = cipher.encrypt(iv, plain_text, aad)
  387. cipher_text = cipher_text_and_tag[: len(cipher_text_and_tag) - 16]
  388. auth_tag = cipher_text_and_tag[-16:]
  389. else:
  390. cipher = Cipher(algorithms.AES(self._key), mode, backend=default_backend())
  391. encryptor = cipher.encryptor()
  392. padder = PKCS7(algorithms.AES.block_size).padder()
  393. padded_data = padder.update(plain_text)
  394. padded_data += padder.finalize()
  395. cipher_text = encryptor.update(padded_data) + encryptor.finalize()
  396. auth_tag = None
  397. return iv, cipher_text, auth_tag
  398. except Exception as e:
  399. raise JWEError(e)
  400. def decrypt(self, cipher_text, iv=None, aad=None, tag=None):
  401. cipher_text = ensure_binary(cipher_text)
  402. try:
  403. iv = ensure_binary(iv)
  404. mode = self._mode(iv)
  405. if mode.name == "GCM":
  406. if tag is None:
  407. raise ValueError("tag cannot be None")
  408. cipher = aead.AESGCM(self._key)
  409. cipher_text_and_tag = cipher_text + tag
  410. try:
  411. plain_text = cipher.decrypt(iv, cipher_text_and_tag, aad)
  412. except InvalidTag:
  413. raise JWEError("Invalid JWE Auth Tag")
  414. else:
  415. cipher = Cipher(algorithms.AES(self._key), mode, backend=default_backend())
  416. decryptor = cipher.decryptor()
  417. padded_plain_text = decryptor.update(cipher_text)
  418. padded_plain_text += decryptor.finalize()
  419. unpadder = PKCS7(algorithms.AES.block_size).unpadder()
  420. plain_text = unpadder.update(padded_plain_text)
  421. plain_text += unpadder.finalize()
  422. return plain_text
  423. except Exception as e:
  424. raise JWEError(e)
  425. def wrap_key(self, key_data):
  426. key_data = ensure_binary(key_data)
  427. cipher_text = aes_key_wrap(self._key, key_data, default_backend())
  428. return cipher_text # IV, cipher text, auth tag
  429. def unwrap_key(self, wrapped_key):
  430. wrapped_key = ensure_binary(wrapped_key)
  431. try:
  432. plain_text = aes_key_unwrap(self._key, wrapped_key, default_backend())
  433. except InvalidUnwrap as cause:
  434. raise JWEError(cause)
  435. return plain_text
  436. class CryptographyHMACKey(Key):
  437. """
  438. Performs signing and verification operations using HMAC
  439. and the specified hash function.
  440. """
  441. ALG_MAP = {ALGORITHMS.HS256: hashes.SHA256(), ALGORITHMS.HS384: hashes.SHA384(), ALGORITHMS.HS512: hashes.SHA512()}
  442. def __init__(self, key, algorithm):
  443. if algorithm not in ALGORITHMS.HMAC:
  444. raise JWKError("hash_alg: %s is not a valid hash algorithm" % algorithm)
  445. self._algorithm = algorithm
  446. self._hash_alg = self.ALG_MAP.get(algorithm)
  447. if isinstance(key, dict):
  448. self.prepared_key = self._process_jwk(key)
  449. return
  450. if not isinstance(key, str) and not isinstance(key, bytes):
  451. raise JWKError("Expecting a string- or bytes-formatted key.")
  452. if isinstance(key, str):
  453. key = key.encode("utf-8")
  454. invalid_strings = [
  455. b"-----BEGIN PUBLIC KEY-----",
  456. b"-----BEGIN RSA PUBLIC KEY-----",
  457. b"-----BEGIN CERTIFICATE-----",
  458. b"ssh-rsa",
  459. ]
  460. if any(string_value in key for string_value in invalid_strings):
  461. raise JWKError(
  462. "The specified key is an asymmetric key or x509 certificate and"
  463. " should not be used as an HMAC secret."
  464. )
  465. self.prepared_key = key
  466. def _process_jwk(self, jwk_dict):
  467. if not jwk_dict.get("kty") == "oct":
  468. raise JWKError("Incorrect key type. Expected: 'oct', Received: %s" % jwk_dict.get("kty"))
  469. k = jwk_dict.get("k")
  470. k = k.encode("utf-8")
  471. k = bytes(k)
  472. k = base64url_decode(k)
  473. return k
  474. def to_dict(self):
  475. return {
  476. "alg": self._algorithm,
  477. "kty": "oct",
  478. "k": base64url_encode(self.prepared_key).decode("ASCII"),
  479. }
  480. def sign(self, msg):
  481. msg = ensure_binary(msg)
  482. h = hmac.HMAC(self.prepared_key, self._hash_alg, backend=default_backend())
  483. h.update(msg)
  484. signature = h.finalize()
  485. return signature
  486. def verify(self, msg, sig):
  487. msg = ensure_binary(msg)
  488. sig = ensure_binary(sig)
  489. h = hmac.HMAC(self.prepared_key, self._hash_alg, backend=default_backend())
  490. h.update(msg)
  491. try:
  492. h.verify(sig)
  493. verified = True
  494. except InvalidSignature:
  495. verified = False
  496. return verified