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 5from __future__ import absolute_import, division, print_function 6 7 8from cryptography import utils 9from cryptography.exceptions import UnsupportedAlgorithm, _Reasons 10from cryptography.hazmat.primitives import hashes 11 12 13@utils.register_interface(hashes.HashContext) 14class _HashContext(object): 15 def __init__(self, backend, algorithm, ctx=None): 16 self._algorithm = algorithm 17 18 self._backend = backend 19 20 if ctx is None: 21 ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() 22 ctx = self._backend._ffi.gc( 23 ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free 24 ) 25 evp_md = self._backend._evp_md_from_algorithm(algorithm) 26 if evp_md == self._backend._ffi.NULL: 27 raise UnsupportedAlgorithm( 28 "{0} is not a supported hash on this backend.".format( 29 algorithm.name), 30 _Reasons.UNSUPPORTED_HASH 31 ) 32 res = self._backend._lib.EVP_DigestInit_ex(ctx, evp_md, 33 self._backend._ffi.NULL) 34 self._backend.openssl_assert(res != 0) 35 36 self._ctx = ctx 37 38 algorithm = utils.read_only_property("_algorithm") 39 40 def copy(self): 41 copied_ctx = self._backend._lib.Cryptography_EVP_MD_CTX_new() 42 copied_ctx = self._backend._ffi.gc( 43 copied_ctx, self._backend._lib.Cryptography_EVP_MD_CTX_free 44 ) 45 res = self._backend._lib.EVP_MD_CTX_copy_ex(copied_ctx, self._ctx) 46 self._backend.openssl_assert(res != 0) 47 return _HashContext(self._backend, self.algorithm, ctx=copied_ctx) 48 49 def update(self, data): 50 data_ptr = self._backend._ffi.from_buffer(data) 51 res = self._backend._lib.EVP_DigestUpdate( 52 self._ctx, data_ptr, len(data) 53 ) 54 self._backend.openssl_assert(res != 0) 55 56 def finalize(self): 57 if isinstance(self.algorithm, hashes.ExtendableOutputFunction): 58 # extendable output functions use a different finalize 59 return self._finalize_xof() 60 else: 61 buf = self._backend._ffi.new("unsigned char[]", 62 self._backend._lib.EVP_MAX_MD_SIZE) 63 outlen = self._backend._ffi.new("unsigned int *") 64 res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf, outlen) 65 self._backend.openssl_assert(res != 0) 66 self._backend.openssl_assert( 67 outlen[0] == self.algorithm.digest_size 68 ) 69 return self._backend._ffi.buffer(buf)[:outlen[0]] 70 71 def _finalize_xof(self): 72 buf = self._backend._ffi.new("unsigned char[]", 73 self.algorithm.digest_size) 74 res = self._backend._lib.EVP_DigestFinalXOF( 75 self._ctx, buf, self.algorithm.digest_size 76 ) 77 self._backend.openssl_assert(res != 0) 78 return self._backend._ffi.buffer(buf)[:self.algorithm.digest_size] 79