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.EVP_MD_CTX_new() 22 ctx = self._backend._ffi.gc( 23 ctx, self._backend._lib.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 "{} is not a supported hash on this backend.".format( 29 algorithm.name 30 ), 31 _Reasons.UNSUPPORTED_HASH, 32 ) 33 res = self._backend._lib.EVP_DigestInit_ex( 34 ctx, evp_md, self._backend._ffi.NULL 35 ) 36 self._backend.openssl_assert(res != 0) 37 38 self._ctx = ctx 39 40 algorithm = utils.read_only_property("_algorithm") 41 42 def copy(self): 43 copied_ctx = self._backend._lib.EVP_MD_CTX_new() 44 copied_ctx = self._backend._ffi.gc( 45 copied_ctx, self._backend._lib.EVP_MD_CTX_free 46 ) 47 res = self._backend._lib.EVP_MD_CTX_copy_ex(copied_ctx, self._ctx) 48 self._backend.openssl_assert(res != 0) 49 return _HashContext(self._backend, self.algorithm, ctx=copied_ctx) 50 51 def update(self, data): 52 data_ptr = self._backend._ffi.from_buffer(data) 53 res = self._backend._lib.EVP_DigestUpdate( 54 self._ctx, data_ptr, len(data) 55 ) 56 self._backend.openssl_assert(res != 0) 57 58 def finalize(self): 59 if isinstance(self.algorithm, hashes.ExtendableOutputFunction): 60 # extendable output functions use a different finalize 61 return self._finalize_xof() 62 else: 63 buf = self._backend._ffi.new( 64 "unsigned char[]", self._backend._lib.EVP_MAX_MD_SIZE 65 ) 66 outlen = self._backend._ffi.new("unsigned int *") 67 res = self._backend._lib.EVP_DigestFinal_ex(self._ctx, buf, outlen) 68 self._backend.openssl_assert(res != 0) 69 self._backend.openssl_assert( 70 outlen[0] == self.algorithm.digest_size 71 ) 72 return self._backend._ffi.buffer(buf)[: outlen[0]] 73 74 def _finalize_xof(self): 75 buf = self._backend._ffi.new( 76 "unsigned char[]", self.algorithm.digest_size 77 ) 78 res = self._backend._lib.EVP_DigestFinalXOF( 79 self._ctx, buf, self.algorithm.digest_size 80 ) 81 self._backend.openssl_assert(res != 0) 82 return self._backend._ffi.buffer(buf)[: self.algorithm.digest_size] 83