cmac.py 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  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 import utils
  5. from cryptography.exceptions import (
  6. InvalidSignature,
  7. UnsupportedAlgorithm,
  8. _Reasons,
  9. )
  10. from cryptography.hazmat.primitives import constant_time
  11. from cryptography.hazmat.primitives.ciphers.modes import CBC
  12. class _CMACContext(object):
  13. def __init__(self, backend, algorithm, ctx=None):
  14. if not backend.cmac_algorithm_supported(algorithm):
  15. raise UnsupportedAlgorithm(
  16. "This backend does not support CMAC.",
  17. _Reasons.UNSUPPORTED_CIPHER,
  18. )
  19. self._backend = backend
  20. self._key = algorithm.key
  21. self._algorithm = algorithm
  22. self._output_length = algorithm.block_size // 8
  23. if ctx is None:
  24. registry = self._backend._cipher_registry
  25. adapter = registry[type(algorithm), CBC]
  26. evp_cipher = adapter(self._backend, algorithm, CBC)
  27. ctx = self._backend._lib.CMAC_CTX_new()
  28. self._backend.openssl_assert(ctx != self._backend._ffi.NULL)
  29. ctx = self._backend._ffi.gc(ctx, self._backend._lib.CMAC_CTX_free)
  30. key_ptr = self._backend._ffi.from_buffer(self._key)
  31. res = self._backend._lib.CMAC_Init(
  32. ctx,
  33. key_ptr,
  34. len(self._key),
  35. evp_cipher,
  36. self._backend._ffi.NULL,
  37. )
  38. self._backend.openssl_assert(res == 1)
  39. self._ctx = ctx
  40. algorithm = utils.read_only_property("_algorithm")
  41. def update(self, data: bytes) -> None:
  42. res = self._backend._lib.CMAC_Update(self._ctx, data, len(data))
  43. self._backend.openssl_assert(res == 1)
  44. def finalize(self) -> bytes:
  45. buf = self._backend._ffi.new("unsigned char[]", self._output_length)
  46. length = self._backend._ffi.new("size_t *", self._output_length)
  47. res = self._backend._lib.CMAC_Final(self._ctx, buf, length)
  48. self._backend.openssl_assert(res == 1)
  49. self._ctx = None
  50. return self._backend._ffi.buffer(buf)[:]
  51. def copy(self) -> "_CMACContext":
  52. copied_ctx = self._backend._lib.CMAC_CTX_new()
  53. copied_ctx = self._backend._ffi.gc(
  54. copied_ctx, self._backend._lib.CMAC_CTX_free
  55. )
  56. res = self._backend._lib.CMAC_CTX_copy(copied_ctx, self._ctx)
  57. self._backend.openssl_assert(res == 1)
  58. return _CMACContext(self._backend, self._algorithm, ctx=copied_ctx)
  59. def verify(self, signature: bytes) -> None:
  60. digest = self.finalize()
  61. if not constant_time.bytes_eq(digest, signature):
  62. raise InvalidSignature("Signature did not match digest.")