from abc import ABC, abstractmethod
import enum
import sys
if sys.version_info >= (3, 11):
from typing import assert_never
else:
from typing_extensions import assert_never
__all__ = [
"CryptoProvider",
"HashFunction"
]
[docs]
@enum.unique
class HashFunction(enum.Enum):
"""
Enumeration of the three hash functions that can be used with
:class:`doubleratchet.recommended.aead_aes_hmac.AEAD`, :class:`doubleratchet.recommended.kdf_hkdf.KDF` and
:class:`doubleratchet.recommended.kdf_separate_hmacs.KDF`. The three hash functions are SHA-256, SHA-512,
and truncated SHA-512 to 256 bits.
"""
SHA_256 = "SHA_256"
SHA_512 = "SHA_512"
SHA_512_256 = "SHA_512_256"
@property
def hash_size(self) -> int:
"""
Returns:
The byte size of the hashes produced by this hash function.
"""
if self is HashFunction.SHA_256:
return 32
if self is HashFunction.SHA_512:
return 64
if self is HashFunction.SHA_512_256:
return 32
return assert_never(self)
[docs]
class CryptoProvider(ABC):
"""
Abstraction of the cryptographic operations needed by this package to allow for different backend
implementations.
"""
[docs]
@staticmethod
@abstractmethod
async def hkdf_derive(
hash_function: HashFunction,
length: int,
salt: bytes,
info: bytes,
key_material: bytes
) -> bytes:
"""
Args:
hash_function: The hash function to parameterize the HKDF with.
length: The number of bytes to derive.
salt: The salt input for the HKDF.
info: The info input for the HKDF.
key_material: The input key material to derive from.
Returns:
The derived key material.
"""
[docs]
@staticmethod
@abstractmethod
async def hmac_calculate(key: bytes, hash_function: HashFunction, data: bytes) -> bytes:
"""
Args:
key: The authentication key.
hash_function: The hash function to parameterize the HMAC with.
data: The data to authenticate.
Returns:
The authentication tag.
"""
[docs]
@staticmethod
@abstractmethod
async def aes_cbc_encrypt(key: bytes, initialization_vector: bytes, plaintext: bytes) -> bytes:
"""
Encrypt plaintext with AES-CBC. The plaintext is padded with PKCS#7 before encryption.
Args:
key: The AES key. Either 128, 192 or 256 bits.
initialization_vector: The initialization vector as needed by AES-CBC.
plaintext: The plaintext.
Returns:
The ciphertext obtained by padding the plaintext with PKCS#7 and then encrypting it with AES-CBC.
"""
[docs]
@staticmethod
@abstractmethod
async def aes_cbc_decrypt(key: bytes, initialization_vector: bytes, ciphertext: bytes) -> bytes:
"""
Decrypt plaintext with AES-CBC. The plaintext is unpadded with PKCS#7 after decryption.
Args:
key: The AES key. Either 128, 192 or 256 bits.
initialization_vector: The initialization vector as needed by AES-CBC.
ciphertext: The ciphertext.
Returns:
The plaintext obtained by decrypting it with AES-CBC and unpadding the result with PKCS#7.
Raises:
DecryptionFailedException: on decryption or unpadding failure.
"""