hmac.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. # This file is dual licensed under the terms of the Apache License, Version
  2. # 2.0, and the BSD License. See the LICENSE file in the root of this repository
  3. # for complete details.
  4. from cryptography.exceptions import (
  5. InvalidSignature,
  6. UnsupportedAlgorithm,
  7. _Reasons,
  8. )
  9. from cryptography.hazmat.primitives import constant_time, hashes
  10. class _HMACContext(hashes.HashContext):
  11. def __init__(
  12. self, backend, key: bytes, algorithm: hashes.HashAlgorithm, ctx=None
  13. ):
  14. self._algorithm = algorithm
  15. self._backend = backend
  16. if ctx is None:
  17. ctx = self._backend._lib.HMAC_CTX_new()
  18. self._backend.openssl_assert(ctx != self._backend._ffi.NULL)
  19. ctx = self._backend._ffi.gc(ctx, self._backend._lib.HMAC_CTX_free)
  20. evp_md = self._backend._evp_md_from_algorithm(algorithm)
  21. if evp_md == self._backend._ffi.NULL:
  22. raise UnsupportedAlgorithm(
  23. "{} is not a supported hash on this backend".format(
  24. algorithm.name
  25. ),
  26. _Reasons.UNSUPPORTED_HASH,
  27. )
  28. key_ptr = self._backend._ffi.from_buffer(key)
  29. res = self._backend._lib.HMAC_Init_ex(
  30. ctx, key_ptr, len(key), evp_md, self._backend._ffi.NULL
  31. )
  32. self._backend.openssl_assert(res != 0)
  33. self._ctx = ctx
  34. self._key = key
  35. @property
  36. def algorithm(self) -> hashes.HashAlgorithm:
  37. return self._algorithm
  38. def copy(self) -> "_HMACContext":
  39. copied_ctx = self._backend._lib.HMAC_CTX_new()
  40. self._backend.openssl_assert(copied_ctx != self._backend._ffi.NULL)
  41. copied_ctx = self._backend._ffi.gc(
  42. copied_ctx, self._backend._lib.HMAC_CTX_free
  43. )
  44. res = self._backend._lib.HMAC_CTX_copy(copied_ctx, self._ctx)
  45. self._backend.openssl_assert(res != 0)
  46. return _HMACContext(
  47. self._backend, self._key, self.algorithm, ctx=copied_ctx
  48. )
  49. def update(self, data: bytes) -> None:
  50. data_ptr = self._backend._ffi.from_buffer(data)
  51. res = self._backend._lib.HMAC_Update(self._ctx, data_ptr, len(data))
  52. self._backend.openssl_assert(res != 0)
  53. def finalize(self) -> bytes:
  54. buf = self._backend._ffi.new(
  55. "unsigned char[]", self._backend._lib.EVP_MAX_MD_SIZE
  56. )
  57. outlen = self._backend._ffi.new("unsigned int *")
  58. res = self._backend._lib.HMAC_Final(self._ctx, buf, outlen)
  59. self._backend.openssl_assert(res != 0)
  60. self._backend.openssl_assert(outlen[0] == self.algorithm.digest_size)
  61. return self._backend._ffi.buffer(buf)[: outlen[0]]
  62. def verify(self, signature: bytes) -> None:
  63. digest = self.finalize()
  64. if not constant_time.bytes_eq(digest, signature):
  65. raise InvalidSignature("Signature did not match digest.")