def scramble_salt(self, password, salt, server_key, client_key, rounds=None): """Scrambles a given salt using the specified server key. """ msg = salt + server_key + client_key hmac_digest = self.salt_key(password, salt, rounds) hash = Hash(self.ALGORITHM(), self.backend) hash.update(hmac_digest) hash_digest = hash.finalize() key_hash = Hash(self.ALGORITHM(), self.backend) key_hash.update(hash_digest) key_hash_digest = key_hash.finalize() sig = HMAC(key_hash_digest, self.ALGORITHM(), self.backend) sig.update(msg) sig_digest = sig.finalize() return self.xor(sig_digest, hash_digest)
def double_sha256(bytestr): hash1 = Hash(SHA256, DEFAULT_BACKEND) hash1.update(bytestr) hash1 = hash1.finalize() hash2 = Hash(SHA256, DEFAULT_BACKEND) hash2.update(hash1) hash2 = hash2.finalize() return hash2
def decrypt(secret, hash, data, file=False): """ Decrypt per telegram docs at https://core.telegram.org/passport. Args: secret (:obj:`str` or :obj:`bytes`): The encryption secret, either as bytes or as a base64 encoded string. hash (:obj:`str` or :obj:`bytes`): The hash, either as bytes or as a base64 encoded string. data (:obj:`str` or :obj:`bytes`): The data to decrypt, either as bytes or as a base64 encoded string. file (:obj:`bool`): Force data to be treated as raw data, instead of trying to b64decode it. Raises: :class:`TelegramDecryptionError`: Given hash does not match hash of decrypted data. Returns: :obj:`bytes`: The decrypted data as bytes. """ # First make sure that if secret, hash, or data was base64 encoded, to decode it into bytes try: secret = b64decode(secret) except (binascii.Error, TypeError): pass try: hash = b64decode(hash) except (binascii.Error, TypeError): pass if not file: try: data = b64decode(data) except (binascii.Error, TypeError): pass # Make a SHA512 hash of secret + update digest = Hash(SHA512(), backend=default_backend()) digest.update(secret + hash) secret_hash_hash = digest.finalize() # First 32 chars is our key, next 16 is the initialisation vector key, iv = secret_hash_hash[:32], secret_hash_hash[32:32 + 16] # Init a AES-CBC cipher and decrypt the data cipher = Cipher(AES(key), CBC(iv), backend=default_backend()) decryptor = cipher.decryptor() data = decryptor.update(data) + decryptor.finalize() # Calculate SHA256 hash of the decrypted data digest = Hash(SHA256(), backend=default_backend()) digest.update(data) data_hash = digest.finalize() # If the newly calculated hash did not match the one telegram gave us if data_hash != hash: # Raise a error that is caught inside telegram.PassportData and transformed into a warning raise TelegramDecryptionError("Hashes are not equal! {} != {}".format(data_hash, hash)) # Return data without padding return data[bord(data[0]):]
def derive(self, key_material): if not isinstance(key_material, bytes): raise TypeError("key_material must be bytes.") h = Hash(self._algorithm(), backend=self._backend) h.update(key_material) h.update(self._salt) derived_key = h.finalize() for i in xrange(self._iterations-1): h = Hash(self._algorithm(), backend=self._backend) h.update(derived_key) derived_key = h.finalize() return derived_key
def derive(self, key_material): if not isinstance(key_material, bytes): raise TypeError("key_material must be bytes.") h = Hash(self._algorithm(), backend=self._backend) h.update(key_material) h.update(self._salt) derived_key = h.finalize() for i in xrange(self._iterations - 1): h = Hash(self._algorithm(), backend=self._backend) h.update(derived_key) derived_key = h.finalize() return derived_key
def check_pcrs(self, pcrs, qinfo, halg): if not qinfo.pcrSelect.count == 1: raise Exception('too many PCR selections') parray = TPMS_PCR_SELECTION_ARRAY.frompointer( qinfo.pcrSelect.pcrSelections) pcrsel = parray[0] if not pcrsel.hash == TPM2_ALG_SHA256: # FIXME raise Exception('PCR bank does not match') h = Hash(halg(), default_backend()) sel = 0 for ps, v in pcrs.items(): # FIXME, check keys p = int(ps) sel = sel | (1 << p) pv = b16decode(v) h.update(pv) dig = h.finalize() selb = sel.to_bytes(pcrsel.sizeofSelect, 'big') qselb = bytearray() qsarray = BYTE_ARRAY.frompointer(pcrsel.pcrSelect) for i in range(0, pcrsel.sizeofSelect): qselb.append(qsarray[i]) qselb = bytes(qselb) if not qselb == selb: raise Exception('PCR selection does not match') qdig = buffer_to_bytes(qinfo.pcrDigest) if not dig == qdig: raise Exception('PCR digest does not match')
def setup_cipher_rc4(salt, password, encrypt=False): hash = Hash(SHA1(), default_backend()) hash.update(salt + bytes(password, 'ascii')) cipher = Cipher(algorithms.ARC4(hash.finalize()), None, default_backend()) cryptor = cipher.encryptor() if encrypt else cipher.decryptor() cryptor.update(bytes(RC4_SKIP)) return cryptor
def _rebuild_index(self): index = {} cipher = CipherContext(self._password) for path in self._secrets.glob('*.secret'): with open(path, 'rb') as file: contents = file.read() plaintext = decompress(cipher.decrypt(contents)) # We extract the path out of the plaintext first. path_size = unpack('>H', plaintext[:2])[0] short_path = plaintext[2:path_size + 2].decode() # This is the true plaintext. plaintext = plaintext[path_size + 2:] # We need to generate a hash for the file. hasher = Hash(BLAKE2b(64)) hasher.update(plaintext) index[short_path] = { 'path': short_path, 'uuid': path.stem, 'checksum': hasher.finalize().hex() } return index
def decrypt_encryption_key_fallback(self): """Decrypts the encryption key using the FALLBACK method. In this method, the context string, usually "CredEncryption" or "PSEEncryption", is encrypted using a derivation of a fixed key hardcoded in CommonCryptoLib, and used as key to encrypt the actual encryption key used in the file with the AES cipher. :return: Encryption key decrypted :rtype: string """ log_lps.debug("Obtaining encryption key with FALLBACK LPS mode") digest = Hash(SHA1(), backend=default_backend()) digest.update(cred_key_lps_fallback) hashed_key = digest.finalize() hmac = HMAC(hashed_key, SHA1(), backend=default_backend()) hmac.update(self.context) default_key = hmac.finalize()[:16] iv = "\x00" * 16 decryptor = Cipher(algorithms.AES(default_key), modes.CBC(iv), backend=default_backend()).decryptor() encryption_key = decryptor.update( self.encrypted_key) + decryptor.finalize() return encryption_key
def onion_name(key): pub_bytes = key.public_key().public_bytes( encoding=serialization.Encoding.DER, format=serialization.PublicFormat.PKCS1) hash = Hash(SHA1(), backend=default_backend()) hash.update(pub_bytes) return b32encode(hash.finalize()[:10]).lower().decode('ascii') + '.onion'
def hash(self, data): digest = Hash(MD5(), backend=openssl_backend) to_hash = "data={}||lpv={}||{}".format( data.decode('ascii'), '.'.join(map(str, self.version)), self.key) digest.update(to_hash.encode('utf8')) intermediate = digest.finalize().hex() return intermediate[8:24]
def _get_ciphertext_digest(ciphertext): if type(ciphertext) is str: ciphertext = bytes(ciphertext, 'utf-8') hash_provider = Hash(SHA512(), default_backend()) hash_provider.update(ciphertext) return base64.b64encode( hash_provider.finalize()).decode('utf-8').rstrip("=")
def validate_access_token(access_token, access_scope=scopes['client']): try: token = tokenserializer.loads(access_token) client_id = token.get('client', None) if not client_id: raise BadSignature('Token does not contain a client ID.') client = User.query.filter(User.id == client_id).first() if not client: raise BadSignature('No such client.') h = Hash(SHA256(), default_backend()) h.update(access_token.encode('utf-8')) if not client.token == h.finalize(): abort(401, message='Invalid access token.') if token.get('scope') > access_scope: abort(403, message='Insufficient Privileges.') except SignatureExpired: abort(401, message='Expired access token.') except BadSignature: abort(401, message='Bad access token.') except: raise
def encrypt_request(self, serialized_request): padder = self.session_key_padding.padder() serialized_request_padded = padder.update(serialized_request) + padder.finalize() encryptor = self.session_key_cipher.encryptor() serialized_request_ciphertext = encryptor.update(serialized_request_padded) + encryptor.finalize() hasher = Hash(self.digest_algorithm) hasher.update(serialized_request) serialized_request_digest = hasher.finalize() session_key_ciphertext = self.c2_public_key.encrypt( self.session_key, OAEP(MGF1(self.digest_algorithm), self.digest_algorithm, None), ) session_key_ciphertext_size = self.c2_public_key.key_size // 8 serialized_request_digest_size = self.digest_algorithm.digest_size serialized_request_ciphertext_size = len(serialized_request_ciphertext) encrypted_request = struct.pack( '<%ds%ds%ds' % ( session_key_ciphertext_size, serialized_request_digest_size, serialized_request_ciphertext_size ), session_key_ciphertext, serialized_request_digest, serialized_request_ciphertext, ) return encrypted_request
def decrypt_ec(data, ec_private_key): ephemeral_public_key = load_ec_public_key(data[:65]) shared_secret = ec_private_key.exchange(ec.ECDH(), ephemeral_public_key) digest = Hash(SHA256(), backend=_CRYPTO_BACKEND) digest.update(shared_secret) encryption_key = digest.finalize() return decrypt_aes_v2(data[65:], encryption_key)
def hash_password(password: str, salt: str) -> str: """ Generates the password hash for the given user :param password: The password to hash :param salt: The salt(base64) to use in hash :return: The password hash """ hash_ = Hash(SHA3_512()) hash_.update(password.encode('ascii') + b64decode(salt.encode('ascii'))) digest = hash_.finalize() hash_ = Hash(SHA3_512()) hash_.update(digest) digest = hash_.finalize() hash_ = Hash(SHA3_512()) hash_.update(digest + b64decode(salt.encode('ascii'))) digest = hash_.finalize() return b64encode(digest).decode('ascii')
def encrypt_ec(data, ec_public_key): e_private_key, e_public_key = generate_ec_key() shared_secret = e_private_key.exchange(ec.ECDH(), ec_public_key) digest = Hash(SHA256(), backend=_CRYPTO_BACKEND) digest.update(shared_secret) encryption_key = digest.finalize() return unload_ec_public_key(e_public_key) + encrypt_aes_v2( data, encryption_key)
def __init__(self, mail, password, scope=scopes['client']): self.email = mail self.scope = scope h = Hash(SHA256(), default_backend()) h.update(password.encode('utf-8')) self.password = h.finalize()
def decrypt(secret, hash, data): """ Decrypt per telegram docs at https://core.telegram.org/passport. Args: secret (:obj:`str` or :obj:`bytes`): The encryption secret, either as bytes or as a base64 encoded string. hash (:obj:`str` or :obj:`bytes`): The hash, either as bytes or as a base64 encoded string. data (:obj:`str` or :obj:`bytes`): The data to decrypt, either as bytes or as a base64 encoded string. file (:obj:`bool`): Force data to be treated as raw data, instead of trying to b64decode it. Raises: :class:`PassportDecryptionError`: Given hash does not match hash of decrypted data. Returns: :obj:`bytes`: The decrypted data as bytes. """ if not CRYPTO_INSTALLED: raise RuntimeError( "To use Telegram Passports, PTB must be installed via `pip install " "python-telegram-bot[passport]`." ) # Make a SHA512 hash of secret + update digest = Hash(SHA512(), backend=default_backend()) digest.update(secret + hash) secret_hash_hash = digest.finalize() # First 32 chars is our key, next 16 is the initialisation vector key, init_vector = secret_hash_hash[:32], secret_hash_hash[32 : 32 + 16] # Init a AES-CBC cipher and decrypt the data cipher = Cipher(AES(key), CBC(init_vector), backend=default_backend()) decryptor = cipher.decryptor() data = decryptor.update(data) + decryptor.finalize() # Calculate SHA256 hash of the decrypted data digest = Hash(SHA256(), backend=default_backend()) digest.update(data) data_hash = digest.finalize() # If the newly calculated hash did not match the one telegram gave us if data_hash != hash: # Raise a error that is caught inside telegram.PassportData and transformed into a warning raise PassportDecryptionError(f"Hashes are not equal! {data_hash} != {hash}") # Return data without padding return data[data[0] :]
def setup_cipher_aes(salt, password, encrypt=False): hash = Hash(SHA256(), default_backend()) hash.update(salt + bytes(password, 'ascii')) cipher = Cipher(algorithms.AES(hash.finalize()[:16]), modes.CTR(salt[:16]), default_backend()) cryptor = cipher.encryptor() if encrypt else cipher.decryptor() cryptor.update(bytes(AES_SKIP)) return cryptor
def compute_hash(cls, data: bytes) -> bytes: h = Hash(algorithm=cls.get_hash_func(), backend=default_backend()) h.update(data) digest = h.finalize() if cls.trunc_size: digest = digest[:cls.trunc_size] return digest
def __init__(self, data_secret: bytes, data_hash: bytes) -> None: digest = Hash(SHA512()) digest.update(data_secret) digest.update(data_hash) secret_hash = digest.finalize() key = secret_hash[:self._key_size] iv = secret_hash[self._key_size:self._key_size + self._iv_size] self._data_hash: Final[bytes] = data_hash self._cipher: Final[Cipher] = Cipher(AES(key), CBC(iv))
def get_public_key_digest(self): pem_public_key = self.get_pem_public_key() pem_public_key = [x for x in pem_public_key if len(x) > 0] pem_public_key.pop(0) pem_public_key.pop(-1) key_string = ''.join(pem_public_key) hash_provider = Hash(SHA512(), default_backend()) hash_provider.update(bytes(key_string, 'utf-8')) return binascii.hexlify(hash_provider.finalize())
def get_name(area): halg = tpm2_to_crypto_alg(area.nameAlg) algp = area.nameAlg.to_bytes(2, 'big') pbuf = marshal(area) h = Hash(halg(), default_backend()) h.update(pbuf) dig = h.finalize() name = algp + dig return name
def opdata1_decrypt_master_key(data, key, hmac_key, aes_size=C_AES_SIZE, ignore_hmac=False): key_size = KEY_SIZE[aes_size] bare_key = opdata1_decrypt_item(data, key, hmac_key, aes_size=aes_size, ignore_hmac=ignore_hmac) # XXX: got the following step from jeff@agilebits (as opposed to the # docs anywhere) digest = Hash(SHA512(), backend=_backend) digest.update(bare_key) hashed_key = digest.finalize() return hashed_key[:key_size], hashed_key[key_size:]
def sha256(message): """Generates a SHA256 hash of a message""" if isinstance(message, str): message = bytes(message, encoding='utf-8') elif isinstance(message, bytearray): message = bytes(message) digest = Hash(SHA256(), backend=default_backend()) digest.update(message) return digest.finalize()
def sha256(message): """Generates a SHA256 hash of a message""" if isinstance(message, str): message = bytes(message, encoding='utf-8') elif isinstance(message, bytearray): message = bytes(message) digest = Hash(SHA256(), backend=default_backend()) digest.update(message) return digest.finalize()
def sha512(self, plaintext): """ Perform SHA-512 hash """ digest = Hash(SHA512(), backend=default_backend()) digest.update(bytes(plaintext.encode('UTF-8'))) hash = digest.finalize() #return binascii.hexlify(bytearray(hash)) return base64.b16encode(hash).decode()
def _shorten_hmac_key(key: bytes) -> bytes: if len(key) > SHA1_BLOCK_SIZE: h = Hash(SHA1(), default_backend()) # nosec h.update(key) key = h.finalize() elif len(key) > HMAC_KEY_SIZE: raise NotSupportedError( "Key lengths > {} bytes not supported".format(HMAC_KEY_SIZE) ) return key
def decrypt(self, ciphertext: bytes) -> bytes: decryptor = self._cipher.decryptor() # type: ignore assert isinstance(decryptor, CipherContext) plaintext = decryptor.update(ciphertext) + decryptor.finalize() digest = Hash(SHA256()) digest.update(plaintext) computed_hash = digest.finalize() if not bytes_eq(computed_hash, self._data_hash): raise RuntimeError('Decryption error') return plaintext[plaintext[0]:]
def _build_index(self): index = {} for path in get_git_files(self._repository): if (self._repository / path).is_file(): uuid = str(uuid4()) while index.get(uuid): uuid = str(uuid4()) hasher = Hash(BLAKE2b(64)) with open(self._repository / path, 'rb') as file: hasher.update(file.read()) index[str(path)] = { 'path': str(path), 'uuid': uuid, 'checksum': hasher.finalize().hex() } for path in (self._repository / '.git').glob('**/*'): if path.is_file(): uuid = str(uuid4()) while index.get(uuid): uuid = str(uuid4()) hasher = Hash(BLAKE2b(64)) with open(path, 'rb') as file: hasher.update(file.read()) short_path = path.relative_to(self._repository) index[str(short_path)] = { 'path': str(short_path), 'uuid': uuid, 'checksum': hasher.finalize().hex() } return index
def login(email, password): user = User.query.filter(User.email == email).first() h = Hash(SHA256(), default_backend()) h.update(password.encode('utf-8')) if not user.password == h.finalize(): return None else: return user
def compute_verify_data(self, basekey, handshake_context): hash_len = self.hash.digest_size finished_key = self.expand_label(basekey, b"finished", b"", hash_len) h = Hash(self.hash, backend=default_backend()) h.update(handshake_context) hash_value = h.finalize() hm = HMAC(finished_key, self.hash, default_backend()) hm.update(hash_value) return hm.finalize()
def _generate_property_iv(entity_iv, pk, rk, property_name, isJavaV1): ''' Uses the entity_iv, partition key, and row key to generate and return the iv for the specified property. ''' digest = Hash(SHA256(), default_backend()) if not isJavaV1: digest.update(entity_iv + (rk + pk + property_name).encode('utf-8')) else: digest.update(entity_iv + (pk + rk + property_name).encode('utf-8')) propertyIV = digest.finalize() return propertyIV[:16]
def a_decrypt_item(data, key, aes_size=A_AES_SIZE): key_size = KEY_SIZE[aes_size] if data[:len(SALT_MARKER)] == SALT_MARKER: salt = data[len(SALT_MARKER):len(SALT_MARKER) + SALT_SIZE] data = data[len(SALT_MARKER) + SALT_SIZE:] pb_gen = pbkdf1.PBKDF1(key, salt) nkey = pb_gen.read(key_size) iv = pb_gen.read(key_size) else: digest = Hash(MD5(), backend=_backend) digest.update(key) nkey = digest.finalize() iv = '\x00'*key_size aes = Cipher(algorithms.AES(nkey), modes.CBC(iv), backend=_backend) decryptor = aes.decryptor() return padding.pkcs5_unpad(decryptor.update(data) + decryptor.finalize())
def decrypt_encryption_key_fallback(self): """Decrypts the encryption key using the FALLBACK method. In this method, the context string, usually "CredEncryption" or "PSEEncryption", is encrypted using a derivation of a fixed key hardcoded in CommonCryptoLib, and used as key to encrypt the actual encryption key used in the file with the AES cipher. :return: Encryption key decrypted :rtype: string """ log_lps.debug("Obtaining encryption key with FALLBACK LPS mode") digest = Hash(SHA1(), backend=default_backend()) digest.update(cred_key_lps_fallback) hashed_key = digest.finalize() hmac = HMAC(hashed_key, SHA1(), backend=default_backend()) hmac.update(self.context) default_key = hmac.finalize()[:16] iv = "\x00" * 16 decryptor = Cipher(algorithms.AES(default_key), modes.CBC(iv), backend=default_backend()).decryptor() encryption_key = decryptor.update(self.encrypted_key) + decryptor.finalize() return encryption_key
def derive_secret(self, secret, label, messages): h = Hash(self.hash, backend=default_backend()) h.update(messages) hash_messages = h.finalize() hash_len = self.hash.digest_size return self.expand_label(secret, label, hash_messages, hash_len)
backend=default_backend()).derive(Kz) Kms2 = KDF2_out[224:256] Kmp2 = KDF2_out[256:288] # Remove trailing '=' from base64 encoded values Np_b64 = Np_b64.strip('=') Ns_b64 = Ns_b64.strip('=') Np2_b64 = Np2_b64.strip('=') Ns2_b64 = Ns2_b64.strip('=') Noob_b64 = Noob_b64.strip('=') ## NoobId NoobId_input = Hash(SHA256(), backend=default_backend()) NoobId_input.update(b'NoobId') NoobId_input.update(Noob_b64.encode()) NoobId = NoobId_input.finalize()[:16] NoobId_b64 = base64url_encode(NoobId).decode().strip('=') ## Hoob Hoob_values = loads('{"Hoob":[]}', object_pairs_hook=OrderedDict) Hoob_values['Hoob'] = [Dir, Vers, Verp, PeerId, Cryptosuites, Dirs, loads(ServerInfo, object_pairs_hook=OrderedDict), Cryptosuitep, Dirp, Realm, loads(PeerInfo, object_pairs_hook=OrderedDict), 0, PKs_full, Ns_b64, PKp_full, Np_b64, Noob_b64] Hoob_input = Hash(SHA256(), backend=default_backend()) Hoob_input.update(dumps(Hoob_values['Hoob'], separators=(',', ':')).encode()) Hoob = Hoob_input.finalize()[:16] Hoob_b64 = base64url_encode(Hoob).decode().strip('=') ## OOB OOB = "P=" + PeerId + "&N=" + Noob_b64 + "&H=" + Hoob_b64
def digest(inp): h = Hash(self._algorithm(), backend=self._backend) h.update(inp) return h.finalize()
def test_validate_encryption(self): # Arrange entity = self._create_default_entity_for_encryption() key_encryption_key = KeyWrapper('key1') self.ts.key_encryption_key = key_encryption_key self.ts.insert_entity(self.table_name, entity) # Act self.ts.key_encryption_key = None entity = self.ts.get_entity(self.table_name, entity['PartitionKey'], entity['RowKey']) # Note the minor discrepancy from the normal decryption process: because the entity was retrieved # without being decrypted, the encrypted_properties list is now stored in an EntityProperty object # and is already raw bytes. encrypted_properties_list = entity['_ClientEncryptionMetadata2'].value encryption_data = entity['_ClientEncryptionMetadata1'] encryption_data = _dict_to_encryption_data(loads(encryption_data)) content_encryption_key = key_encryption_key.unwrap_key(encryption_data.wrapped_content_key.encrypted_key, encryption_data.wrapped_content_key.algorithm) digest = Hash(SHA256(), default_backend()) digest.update(encryption_data.content_encryption_IV + (entity['RowKey'] + entity['PartitionKey'] + '_ClientEncryptionMetadata2').encode('utf-8')) metadataIV = digest.finalize() metadataIV = metadataIV[:16] cipher = _generate_AES_CBC_cipher(content_encryption_key, metadataIV) # Decrypt the data. decryptor = cipher.decryptor() encrypted_properties_list = decryptor.update(encrypted_properties_list) + decryptor.finalize() # Unpad the data. unpadder = PKCS7(128).unpadder() encrypted_properties_list = unpadder.update(encrypted_properties_list) + unpadder.finalize() encrypted_properties_list = encrypted_properties_list.decode('utf-8') # Strip the square braces from the ends and split string into list. encrypted_properties_list = loads(encrypted_properties_list) entity_iv, encrypted_properties, content_encryption_key = \ (encryption_data.content_encryption_IV, encrypted_properties_list, content_encryption_key) decrypted_entity = deepcopy(entity) for property in encrypted_properties_list: value = entity[property] digest = Hash(SHA256(), default_backend()) digest.update(entity_iv + (entity['RowKey'] + entity['PartitionKey'] + property).encode('utf-8')) propertyIV = digest.finalize() propertyIV = propertyIV[:16] cipher = _generate_AES_CBC_cipher(content_encryption_key, propertyIV) # Decrypt the property. decryptor = cipher.decryptor() decrypted_data = (decryptor.update(value.value) + decryptor.finalize()) # Unpad the data. unpadder = PKCS7(128).unpadder() decrypted_data = (unpadder.update(decrypted_data) + unpadder.finalize()) decrypted_data = decrypted_data.decode('utf-8') decrypted_entity[property] = decrypted_data decrypted_entity.pop('_ClientEncryptionMetadata1') decrypted_entity.pop('_ClientEncryptionMetadata2') # Assert self.assertEqual(decrypted_entity['sex'], 'male')
def sha1(msg): sha = Hash(SHA1(), backend=bend) sha.update(msg) return sha.finalize()
def _get_digest(self, data, digest_algorithm): hasher = Hash(algorithm=digest_algorithm, backend=default_backend()) hasher.update(data) return ensure_str(b64encode(hasher.finalize()))
def sha1_hash(data): sha = Hash(SHA1(), backend) sha.update(data) return sha.finalize()