def verify_data(data, signature, sig_key, hashalg): """ Check whether ``data`` is authentic for ``signature`` with the key ``sig_key``. .. warning:: ``sig_key`` must be a byte string of a sufficient length (recommended is ``32`` bytes). """ reference = authenticate_data(data, sig_key, hashalg) if not compare_constant_time(reference, signature): raise CryptoError("Invalid Signature") else: return True
def decrypt_authenticated(ciphertext, tag, enc_key, hmac_key, hashalg): """ This decrypts an authenticated ciphertext. It raises an exception if either the decryption fails or the ciphertext could not be authenticated. It is the inverse of :func:`encrypt_then_authenticate` Args: ``ciphertext``: A byte string ciphertext ``tag``: A byte string hmac, the output of :func:`hmac.HMAC.digest`. ``enc_key``: A 32 byte encryption key ``hmac_key``: A byte string hmac_key for signature. ``hashalg``: A hash algorithm to use, e.g. :class:`hashlib.sha256` Returns: The plain text Raises: :exc:`ValueError` when authentication fails or no plaintext could be decrypted. """ from Crypto.Cipher import AES from Crypto.Util import Counter if not isinstance(ciphertext, str): raise TypeError("Ciphertext is not a byte string.") reference_tag = hmac.new(hmac_key, ciphertext, hashalg).hexdigest() if not compare_constant_time(reference_tag, tag): raise CryptoError("Signature does not match, invalid ciphertext") cipher = AES.new(enc_key, AES.MODE_CTR, counter=Counter.new(128)) plaintext = cipher.decrypt(ciphertext) try: return plaintext.decode("utf-8") except UnicodeDecodeError: raise CryptoError("Could not retrieve plaintext back properly " "(wrong key or ciphertext?)")
def test_compare_constant_time_unequal_strings(): assert not compare_constant_time("A" * 30, "A" * 29 + "B")
def test_compare_constant_time_unequal_length(): assert not compare_constant_time("A" * 30, "A" * 29)
def test_compare_constant_time_works(): assert compare_constant_time("A" * 30, "A" * 30)