Source code for pyflocker.ciphers.base

"""Base classes for pyflocker."""

from __future__ import annotations

from abc import ABCMeta, abstractmethod


[docs] class BaseSymmetricCipher(metaclass=ABCMeta): __slots__ = ()
[docs] @abstractmethod def is_encrypting(self) -> bool: """Whether the cipher is encrypting or not. Returns: ``True`` if encrypting, else ``False``. """
[docs] @abstractmethod def update(self, data: bytes) -> bytes: """Takes bytes-like object and returns encrypted/decrypted bytes object. Args: data: The bytes-like object to pass to the cipher. Returns: Encrypted/decrypted data as bytes. """
[docs] @abstractmethod def update_into( self, data: bytes, out: bytearray | memoryview, ) -> None: """ Encrypt or decrypt the ``data`` and store it in a preallocated buffer ``out``. Args: data: The bytes-like object to pass to the cipher. out: The buffer interface where the encrypted/decrypted data must be written into. """
[docs] class BaseNonAEADCipher(BaseSymmetricCipher): """ Abstract Base Class for ciphers that have no authentication support. These ciphers can be wrapped by classes that implement ``BaseAEADCipher`` and the wrappers must provide authentication support. """ __slots__ = ()
[docs] @abstractmethod def finalize(self) -> None: """Finalizes and closes the cipher. Raises: AlreadyFinalized: If the cipher was already finalized. """
[docs] class BaseAEADCipher(BaseSymmetricCipher): """Abstract base class for AEAD ciphers. Custom cipher wrappers that provide AEAD functionality to NonAEAD ciphers must derive from this. """ __slots__ = ()
[docs] @abstractmethod def authenticate(self, data: bytes) -> None: """Authenticates part of the message that get delivered as is, without any encryption. Args: data: The bytes-like object that must be authenticated. Raises: TypeError: if this method is called after calling :py:meth:`~BaseSymmetricCipher.update`. """
[docs] @abstractmethod def finalize(self, tag: bytes | None = None) -> None: """Finalizes and ends the cipher state. Args: tag: The associated tag that authenticates the decryption. Tag is required for decryption only. Raises: ValueError: If cipher is decrypting and tag is not supplied. DecryptionError: If the decryption was incorrect. """
[docs] @abstractmethod def calculate_tag(self) -> bytes | None: """Calculates and returns the associated tag. Returns: Returns ``None`` if decrypting, otherwise the associated authentication tag. """
[docs] class BaseAEADOneShotCipher(BaseAEADCipher):
[docs] @abstractmethod def update(self, data: bytes, tag: bytes | None = None) -> bytes: """Encrypt or decrypt ``data``. Tag is required only for decryption. The cipher is finalized after calling this method. Args: data: A bytes-like object to pass to the cipher. tag: The associated tag that authenticates the decryption. Tag is required for decryption only. Returns: Encrypted/decrypted data as bytes object. """
[docs] @abstractmethod def update_into( self, data: bytes, out: bytearray | memoryview, tag: bytes | None = None, ) -> None: """Encrypt or decrypt ``data`` and write it to ``out``. If decrypting, the MAC tag must be provided. The cipher is finalized after calling this method. Args: data: The bytes-like oject to pass to the cipher. out: The buffer interface where the encrypted/decrypted data must be written into. tag: The associated tag that authenticates the decryption. Tag is required for decryption only. """
[docs] class BaseHash(metaclass=ABCMeta): """Abstract base class for hash functions. Follows PEP-0452. Custom MACs must use this interface. """ __slots__ = () @property @abstractmethod def digest_size(self) -> int: """ The size of the digest produced by the hashing object, measured in bytes. If the hash has a variable output size, this output size must be chosen when the hashing object is created, and this attribute must contain the selected size. Therefore, None is not a legal value for this attribute. Returns: Digest size as integer. """ @property @abstractmethod def block_size(self) -> int: """ An integer value or :any:`NotImplemented`; the internal block size of the hash algorithm in bytes. The block size is used by the HMAC module to pad the secret key to digest_size or to hash the secret key if it is longer than digest_size. If no HMAC algorithm is standardized for the hash algorithm, returns :any:`NotImplemented` instead. Returns: An integer if block size is available, otherwise :any:`NotImplemented`. See Also: PEP 452 -- API for Cryptographic Hash Functions v2.0, https://www.python.org/dev/peps/pep-0452 """
[docs] @abstractmethod def update(self, data: bytes) -> None: """ Hash string into the current state of the hashing object. ``update()`` can be called any number of times during a hashing object's lifetime. Args: data: The chunk of message being hashed. Raises: AlreadyFinalized: Raised if :py:meth:`~digest` or :py:meth:`~hexdigest` has been called. """
[docs] @abstractmethod def digest(self) -> bytes: """ Return the hash value of this hashing object as a string containing 8-bit data. The object is not altered in any way by this function; you can continue updating the object after calling this function. Returns: Digest as binary form. """
[docs] def hexdigest(self) -> str: """ Return the hash value of this hashing object as a string containing hexadecimal digits. Returns: Digest, as a hexadecimal form. """ return self.digest().hex()
[docs] @abstractmethod def copy(self) -> BaseHash: """ Return a separate copy of this hashing object. An update to this copy won't affect the original object. Returns: A copy of hash function. Raises: AlreadyFinalized: This is raised if the method is called after calling :py:meth:`~digest` method. """
@property @abstractmethod def name(self) -> str: """Name of the hash function."""
[docs] @abstractmethod def new(self, data: bytes | None = None) -> BaseHash: """Create a fresh hash object."""
def __repr__(self) -> str: # pragma: no cover return f"<{type(self).__name__} {self.name!r} at {hex(id(self))}>"
[docs] class BaseRSAPrivateKey(metaclass=ABCMeta): @property @abstractmethod def n(self) -> int: """RSA public modulus. The number `n` is such that `n == p * q`. """ @property @abstractmethod def e(self) -> int: """RSA public exponent.""" @property @abstractmethod def p(self) -> int: """First factor of RSA modulus.""" @property @abstractmethod def q(self) -> int: """Second factor of RSA modulus.""" @property @abstractmethod def d(self) -> int: """RSA private exponent.""" @property @abstractmethod def key_size(self) -> int: """Size of the key, in bits."""
[docs] @abstractmethod def decryptor( self, padding: BaseAsymmetricPadding | None = None, ) -> BaseDecryptorContext: """Creates a decryption context. Args: padding: The padding to use. Default is OAEP. Returns: object for decrypting ciphertexts. """
[docs] @abstractmethod def signer( self, padding: BaseAsymmetricPadding | None = None, ) -> BaseSignerContext: """Create a signer context. Args: padding: The padding to use. Default is PSS. Returns: Signer object for signing messages. Note: If the padding is PSS and ``salt_length`` is None, the salt length will be maximized, as in OpenSSL. """
[docs] @abstractmethod def public_key(self) -> BaseRSAPublicKey: """Creates a public key from the private key. Returns: The RSA public key. """
[docs] @abstractmethod def serialize( self, encoding: str, format: str, passphrase: bytes | None = None, ) -> bytes: """Serialize the private key. Args: encoding: The encoding to use. format: The format to use. passphrase: A bytes object to use for encrypting the private key. If ``passphrase`` is None, the private key will be exported in the clear! Returns: Serialized key as a bytes object. Raises: ValueError: If the encoding or format is incorrect. Important: The ``encoding`` and ``format`` supported by one backend may not be supported by the other. You should check the documentation of the implementation of the method for supported options. """
[docs] @classmethod @abstractmethod def load( cls, data: bytes, passphrase: bytes | None = None, ) -> BaseRSAPrivateKey: """Loads the private key as bytes object and returns the Key interface. Args: data: The key as bytes object. passphrase: The passphrase that deserializes the private key. It must be a bytes-like object if the key was encrypted while serialization, otherwise ``None``. Returns: RSA private key. Raises: ValueError: if the key could not be deserialized. """
[docs] class BaseRSAPublicKey(metaclass=ABCMeta): @property @abstractmethod def n(self) -> int: """RSA public modulus. The number `n` is such that `n = p * q`. """ @property @abstractmethod def e(self) -> int: """RSA public exponent.""" @property @abstractmethod def key_size(self) -> int: """Size of the key, in bits."""
[docs] @abstractmethod def encryptor( self, padding: BaseAsymmetricPadding | None = None, ) -> BaseEncryptorContext: """Creates a encryption context. Args: padding: The padding to use. Defaults to OAEP. Returns: object for encrypting plaintexts. """
[docs] @abstractmethod def verifier( self, padding: BaseAsymmetricPadding | None = None, ) -> BaseVerifierContext: """Creates a verifier context. Args: padding: The padding to use. Defaults to PSS. Returns: verifier object for verification. """
[docs] @abstractmethod def serialize( self, encoding: str, format: str, ) -> bytes: """Serialize the public key. Args: encoding: The encoding to use. format: The format to use. Returns: Serialized public key as bytes object. Raises: ValueError: if the encoding or format is incorrect or unsupported. Important: The ``encoding`` and ``format`` supported by one backend may not be supported by the other. You should check the documentation of the implementation of this method for supported options. """
[docs] @classmethod @abstractmethod def load(cls, data: bytes) -> BaseRSAPublicKey: """Loads the public key as ``bytes`` object and returns the Key interface. Args: data: The key as bytes object. Returns: The RSA public key. Raises: ValueError: if the key could not be deserialized. """
[docs] class BaseAsymmetricPadding(metaclass=ABCMeta): """ Base class for padding schemes used by asymmetric algorithms. """ @property @abstractmethod def name(self) -> str: """Name of the padding scheme."""
[docs] class BaseMGF(metaclass=ABCMeta): """ Base class for mask generation functions used by padding algorithms. """
[docs] class BaseEllepticCurveExchangeAlgorithm(metaclass=ABCMeta): """ Base class for exchange algorithm for elleptic keys. """
[docs] class BaseEllepticCurveSignatureAlgorithm(metaclass=ABCMeta): """ Base class for signing algorithm for elleptic keys. """
[docs] class BaseECCPrivateKey(metaclass=ABCMeta): @property @abstractmethod def key_size(self) -> int: """Size of ECC key, in bits.""" @property @abstractmethod def curve(self) -> str: """Name of the curve that this key is on."""
[docs] @abstractmethod def public_key(self) -> BaseECCPublicKey: """Creates a public key from the private key. Returns: The public key. """
[docs] @abstractmethod def serialize( self, encoding: str, format: str, passphrase: bytes | None = None, ) -> bytes: """Serialize the private key. Args: encoding: The encoding to use. format: The format to use. passphrase: A bytes object to use for encrypting the private key. If ``passphrase`` is None, the private key will be exported in the clear! Returns: Serialized key as a bytes object. Raises: ValueError: If the encoding or format is incorrect. Important: The ``encoding`` and ``format`` supported by one backend may not be supported by the other. You should check the documentation of the implementation of the method for supported options. """
[docs] @classmethod @abstractmethod def load( cls, data: bytes, *, curve: str | None = None, ) -> BaseECCPrivateKey: """Loads the private key as bytes object and returns the Key interface. Args: data: The key as bytes object. passphrase: The passphrase that deserializes the private key. It must be a bytes-like object if the key was encrypted while serialization, otherwise ``None``. Keyword Arguments: curve: The name of the curve as string. It is required only for ``Raw`` keys. Returns: RSA private key. Raises: ValueError: if the key could not be deserialized. """
[docs] @abstractmethod def exchange( self, peer_public_key: bytes | BaseECCPublicKey, algorithm: BaseEllepticCurveExchangeAlgorithm | None = None, ) -> bytes: """Perform a key exchange. Args: peer_public_key: The peer public key can be a bytes or an ECC public key object. algorithm: The algorithm to use for performing key exchange. Default is ECDH. Returns: A shared key. Raises: TypeError: if ``peer_public_key`` is not a bytes-like or ECC Public Key object. """
[docs] @abstractmethod def signer( self, algorithm: BaseEllepticCurveSignatureAlgorithm, ) -> BaseSignerContext | BaseEdDSASignerContext: """Creates a signer context. Args: algorithm: The signing algorithm to use. Default is ECDSA for NIST curves and EdDSA for Edwards curves. Returns: signer object for signing. """
[docs] class BaseECCPublicKey(metaclass=ABCMeta): @property @abstractmethod def key_size(self) -> int: """Size of ECC key, in bits.""" @property @abstractmethod def curve(self) -> str: """Name of the curve that this key is on."""
[docs] @abstractmethod def serialize(self, encoding: str, format: str) -> bytes: """Serialize the public key. Args: encoding: The encoding to use. format: The format to use. Returns: Serialized public key as bytes object. Raises: ValueError: if the encoding or format is incorrect or unsupported. Important: The ``encoding`` and ``format`` supported by one backend may not be supported by the other. You should check the documentation of the implementation of this method for supported options. """
[docs] @classmethod @abstractmethod def load( cls, data: bytes, *, curve: str | None = None, ) -> BaseECCPublicKey: """Loads the public key as ``bytes`` object and returns the Key interface. Args: data: The key as bytes object. Keyword Arguments: curve: The name of the curve as a string. Required only for ``SEC1`` and ``Raw`` keys. Returns: The ECC public key. Raises: ValueError: if the key could not be deserialized. """
[docs] @abstractmethod def verifier( self, algorithm: BaseEllepticCurveSignatureAlgorithm | None = None, ) -> BaseVerifierContext | BaseEdDSAVerifierContext: """Creates a verifier context. Args: algorithm: The signing algorithm to use. Default is ECDSA for NIST curves and EdDSA for Edwards curves. Returns: verifier object for verification. """
[docs] class BaseSignerContext(metaclass=ABCMeta):
[docs] @abstractmethod def sign(self, msghash: BaseHash) -> bytes: """Return the signature of the message hash. Args: msghash: It must be a :any:`BaseHash` object, used to digest the message to sign. Returns: signature of the message as bytes object. """
[docs] class BaseVerifierContext(metaclass=ABCMeta):
[docs] @abstractmethod def verify(self, msghash: BaseHash, signature: bytes) -> None: """Verifies the signature of the message hash. Args: msghash: It must be a :any:`BaseHash` object, used to digest the message to sign. signature: The signature of the message. Raises: SignatureError: if the signature was incorrect. """
[docs] class BaseEdDSASignerContext(metaclass=ABCMeta):
[docs] @abstractmethod def sign(self, msghash: bytes) -> bytes: """Return the signature of the message. Args: msghash: A bytes object. Returns: signature of the message as bytes object. """
[docs] class BaseEdDSAVerifierContext(metaclass=ABCMeta):
[docs] @abstractmethod def verify(self, msghash: bytes, signature: bytes) -> None: """Verifies the signature of the message. Args: msghash: A bytes object. signature: The signature of the message. Raises: SignatureError: if the signature was incorrect. """
[docs] class BaseEncryptorContext(metaclass=ABCMeta):
[docs] @abstractmethod def encrypt(self, plaintext: bytes) -> bytes: """Encrypts the plaintext and returns the ciphertext. Args: plaintext: The data to encrypt. Returns: encrypted bytes object. """
[docs] class BaseDecryptorContext(metaclass=ABCMeta):
[docs] @abstractmethod def decrypt(self, ciphertext: bytes) -> bytes: """Decrypts the ciphertext and returns the plaintext. Args: ciphertext: The ciphertext to decrypt. Returns: The plaintext. Raises: DecryptionError: if the decryption was not successful. """
[docs] class BaseDHParameters(metaclass=ABCMeta): @property @abstractmethod def g(self) -> int: """The generator value.""" @property @abstractmethod def p(self) -> int: """The prime modulus value.""" @property @abstractmethod def q(self) -> int | None: """The p subgroup order value."""
[docs] @abstractmethod def private_key(self) -> BaseDHPrivateKey: """Create a DH private key from the parameters. Returns: A private key object. """
[docs] @abstractmethod def serialize(self, encoding: str, format: str) -> bytes: """Serialize the DH parameters. Args: encoding: The encoding to use. format: The format to use. Returns: The parameters encoded as bytes object. Raises: ValueError: if the encoding of format is invalid. Important: The ``encoding`` and ``format`` supported by one backend may not be supported by the other. You should check the documentation of the implementation of this method for supported options. """
[docs] @classmethod @abstractmethod def load(cls, data: bytes) -> BaseDHParameters: """Deserialize the encoded DH parameters. Args: data: The parameters as an encoded bytes object. Returns: DH parameter object. """
[docs] @classmethod @abstractmethod def load_from_parameters( cls, p: int, g: int = 2, q: int | None = None, ) -> BaseDHParameters: """Generates a DH parameter group from the parameters. Args: p: The prime modulus value. g: The generator value. Must be 2 or 5. Default is 2. q: p subgroup order value. Defaults to ``None``. Returns: DH Parameter object. """
[docs] class BaseDHPrivateKey(metaclass=ABCMeta):
[docs] @abstractmethod def parameters(self) -> BaseDHParameters: """Creates a new DH Parameters object from the key. Returns: The DH parameter object. """
@property @abstractmethod def key_size(self) -> int: """Size of the key, in bytes."""
[docs] @abstractmethod def public_key(self) -> BaseDHPublicKey: """Create a public key from the private key. Returns: A public key object. """
[docs] @abstractmethod def exchange( self, peer_public_key: bytes | BaseDHPublicKey, ) -> bytes: """Perform a key exchange. Args: peer_public_key: The peer public key can be a bytes or a :any:`BaseDHPublicKey` object. Returns: A shared key. Raises: TypeError: if ``peer_public_key`` is not a bytes-like or DH Public Key object. """
[docs] @abstractmethod def serialize( self, encoding: str, format: str, passphrase: bytes | None, ) -> bytes: """Serialize the private key. Args: encoding: The encoding to use. format: The format to use. passphrase: The passphrase to use to protect the private key. ``None`` if the private key is not encrypted. Returns: The private key as bytes object. Raises: ValueError: if the encoding or format is invalid. TypeError: if the passphrase is not a bytes-like object. Important: The ``encoding`` and ``format`` supported by one backend may not be supported by the other. You should check the documentation of the implementation of this method for supported options. """
@property @abstractmethod def x(self) -> int: """The private value."""
[docs] @classmethod @abstractmethod def load( cls, data: bytes, passphrase: bytes | None = None, ) -> BaseDHPrivateKey: """Deserialize and load the the private key. Args: data: The serialized private key as bytes-like object. passphrase: The passphrase that was used to protect the private key. If key is not protected, passphrase is ``None``. Returns: A private key. Raises: ValueError: If the key could not be deserialized. TypeError: If passphrase is not a bytes-like object. """
[docs] class BaseDHPublicKey(metaclass=ABCMeta):
[docs] @abstractmethod def parameters(self) -> BaseDHParameters: """Creates a new DH parameters object from the key. Returns: The DH parameter object. """
@property @abstractmethod def key_size(self) -> int: """Size of the key, in bytes."""
[docs] @abstractmethod def serialize( self, encoding: str, format: str, ) -> bytes: """Serialize the public key. Args: encoding: The encoding to use. format: The format to use. Returns: The public key as bytes object. Raises: ValueError: if the encoding or format is invalid. """
@property @abstractmethod def y(self) -> int: """The public value."""
[docs] @classmethod @abstractmethod def load(cls, data: bytes) -> BaseDHPublicKey: """Deserialize and load the public key. Args: data: The serialized public key as bytes-like object. Returns: A public key object. Raises: ValueError: If the key could not be deserialized. """