Source code for audible.crypto_provider.protocols

"""Protocol definitions for crypto providers.

This module defines the contracts that all crypto provider implementations
must follow. Using Python's Protocol allows for type-safe duck typing and
enables static type checking with mypy.
"""

from collections.abc import Callable
from typing import Any, Protocol, runtime_checkable


[docs] @runtime_checkable class HashAlgorithm(Protocol): """Protocol for hash algorithm objects. This protocol defines the interface that hash objects must provide. Compatible with hashlib hash objects (e.g., hashlib.sha256()). Attributes: name: The name of the hash algorithm (e.g., 'sha256', 'sha1'). """ name: str
[docs] @runtime_checkable class AESProvider(Protocol): """Protocol for AES-CBC encryption/decryption operations. Implementations must provide AES encryption in CBC mode with configurable padding. Both "default" (PKCS7) and "none" (no padding) modes must be supported. """
[docs] def encrypt( self, key: bytes, iv: bytes, data: str, padding: str = "default" ) -> bytes: """Encrypt data using AES-CBC. Args: key: The AES encryption key (16, 24, or 32 bytes). iv: The initialization vector (16 bytes). data: The plaintext data to encrypt. padding: Padding mode - "default" for PKCS7, "none" for no padding. Returns: The encrypted data as bytes. """ ...
[docs] def decrypt( self, key: bytes, iv: bytes, encrypted_data: bytes, padding: str = "default" ) -> str: """Decrypt data using AES-CBC. Args: key: The AES decryption key (16, 24, or 32 bytes). iv: The initialization vector (16 bytes). encrypted_data: The ciphertext to decrypt. padding: Padding mode - "default" for PKCS7, "none" for no padding. Returns: The decrypted plaintext as a string. """ ...
[docs] @runtime_checkable class PBKDF2Provider(Protocol): """Protocol for PBKDF2 key derivation operations. Implementations must provide PBKDF2 (Password-Based Key Derivation Function 2) with configurable hash functions and iteration counts. """
[docs] def derive_key( self, password: str, salt: bytes, iterations: int, key_size: int, hashmod: Callable[..., "HashAlgorithm"], ) -> bytes: """Derive a cryptographic key from a password using PBKDF2. Args: password: The password to derive the key from. salt: Random salt for key derivation. iterations: Number of iterations (1-65535). key_size: Desired key size in bytes. hashmod: Hash function factory (e.g., hashlib.sha256). Returns: The derived key as bytes. """ ...
[docs] @runtime_checkable class RSAProvider(Protocol): """Protocol for RSA signing operations. Implementations must provide RSA private key loading and PKCS#1 v1.5 signing with SHA-256. Implementations should cache parsed keys for performance. """
[docs] def load_private_key(self, pem_data: str) -> Any: """Load and cache an RSA private key from PEM format. This method should implement caching to avoid re-parsing the same key multiple times, as key loading can be expensive. Args: pem_data: RSA private key in PEM format. Returns: The loaded RSA private key object. """ ...
[docs] def sign(self, key: Any, data: bytes, algorithm: str = "SHA-256") -> bytes: """Sign data with an RSA private key using PKCS#1 v1.5. Args: key: A parsed RSA private key (from load_private_key). data: The data to sign. algorithm: The hash algorithm to use. Currently only "SHA-256" is supported to match Audible API requirements (SHA256withRSA). Returns: The RSA signature as bytes. """ ...
[docs] @runtime_checkable class HashProvider(Protocol): """Protocol for cryptographic hash operations. Implementations must provide SHA-256 and SHA-1 hash functions. """
[docs] def sha256(self, data: bytes) -> bytes: """Compute SHA-256 digest. Args: data: The data to hash. Returns: The SHA-256 hash digest as bytes. """ ...
[docs] def sha1(self, data: bytes) -> bytes: """Compute SHA-1 digest. Args: data: The data to hash. Returns: The SHA-1 hash digest as bytes. Note: SHA-1 is considered cryptographically broken and should only be used for non-security-critical purposes (e.g., legacy compatibility). """ ...
[docs] @runtime_checkable class CryptoProvider(Protocol): """Protocol for complete crypto provider implementations. This protocol defines the contract that all crypto providers must follow. Providers must implement all four sub-providers (AES, PBKDF2, RSA, Hash) to ensure complete cryptographic functionality. Custom providers can be created by implementing this protocol and passing the provider class to get_crypto_providers(). Example: >>> class MyProvider: # doctest: +SKIP ... @property ... def aes(self) -> AESProvider: ... return MyAESProvider() ... @property ... def pbkdf2(self) -> PBKDF2Provider: ... return MyPBKDF2Provider() ... @property ... def rsa(self) -> RSAProvider: ... return MyRSAProvider() ... @property ... def hash(self) -> HashProvider: ... return MyHashProvider() ... @property ... def provider_name(self) -> str: ... return "my-custom-provider" >>> from audible.crypto_provider import get_crypto_providers # doctest: +SKIP >>> providers = get_crypto_providers(MyProvider) # doctest: +SKIP >>> providers.provider_name # doctest: +SKIP 'my-custom-provider' """ @property def aes(self) -> AESProvider: """Return the AES encryption/decryption provider. Returns: The AES encryption/decryption provider instance. """ ... @property def pbkdf2(self) -> PBKDF2Provider: """Return the PBKDF2 key derivation provider. Returns: The PBKDF2 key derivation provider instance. """ ... @property def rsa(self) -> RSAProvider: """Return the RSA signing provider. Returns: The RSA signing provider instance. """ ... @property def hash(self) -> HashProvider: """Return the cryptographic hash provider. Returns: The cryptographic hash provider instance. """ ... @property def provider_name(self) -> str: """Return the human-readable provider name. Returns: The human-readable provider name (e.g., 'cryptography', 'pycryptodome', 'legacy'). """ ...
__all__ = [ "AESProvider", "CryptoProvider", "HashAlgorithm", "HashProvider", "PBKDF2Provider", "RSAProvider", ]