Skip to content

Bases

Bases: base classes for signers.

blake2signer.bases.Base (Mixin, ABC)

Base class containing the minimum for a signer.

Source code in blake2signer/bases.py
class Base(Mixin, ABC):
    """Base class containing the minimum for a signer."""

    Hashers = HasherChoice  # Sugar to avoid having to import the enum

    MIN_SECRET_SIZE: int = 16
    """Minimum secret size allowed (during instantiation)."""

    MIN_DIGEST_SIZE: int = 16
    """Minimum digest size allowed (during instantiation)."""

    DEFAULT_DIGEST_SIZE: int = 16  # 16 bytes is good security/size tradeoff
    """Default digest size to use when no digest size is indicated."""

    def __init__(
        self,
        secret: typing.Union[str, bytes],
        *,
        personalisation: typing.Union[str, bytes] = b'',
        digest_size: typing.Optional[int] = None,
        hasher: typing.Union[HasherChoice, str] = HasherChoice.blake2b,
        deterministic: bool = False,
        separator: typing.Union[str, bytes] = b'.',
    ) -> None:
        """Sign and verify signed data using BLAKE in keyed hashing mode.

        Args:
            secret: Secret value which will be derived using BLAKE to produce the
                signing key. The minimum secret size is enforced to 16 bytes and
                there is no maximum.

        Keyword Args:
            personalisation (optional): Personalisation string to force the hash
                function to produce different digests for the same input. It is
                derived using BLAKE to ensure it fits the hasher limits, so it
                has no practical size limit. It defaults to the class name.
            digest_size (optional): Size of output signature (digest) in bytes
                (defaults to 16 bytes). The minimum size is enforced to 16 bytes.
            hasher (optional): Hash function to use: blake2b (default), blake2s
                or blake3.
            deterministic (optional): Define if signatures are deterministic or
                non-deterministic (default). Non-deterministic sigs are preferred,
                and achieved through the use of a random salt. For deterministic
                sigs, no salt is used: this means that for the same payload, the
                same sig is obtained (the advantage is that the sig is shorter).
            separator (optional): Character to separate the signature and the
                payload. It must not belong to the encoder alphabet and be ASCII
                (defaults to ".").

        Raises:
            ConversionError: A bytes parameter is not bytes and can't be converted
                to bytes.
            InvalidOptionError: A parameter is out of bounds.
        """
        hasher_choice = self._validate_hasher_choice(hasher)
        digest_size = self._validate_digest_size(digest_size)
        separator = self._validate_separator(separator)
        person = self._validate_person(personalisation)
        secret = self._validate_secret(secret)

        if deterministic:
            person += b'Deterministic'
        person += self.__class__.__name__.encode()

        self._deterministic: bool = deterministic
        self._separator: bytes = separator

        self._hasher = self._get_hasher(
            hasher_choice,
            secret=secret,
            digest_size=digest_size,
            person=person,
        )

    def _validate_secret(self, secret_: typing.Union[str, bytes]) -> bytes:
        """Validate the secret value and return it clean.

        Args:
            secret_: Secret value to validate.

        Returns:
            Cleaned secret value.

        Raises:
            ConversionError: The value is not bytes and can't be converted to bytes.
            InvalidOptionError: The value is out of bounds.
        """
        secret = self._force_bytes(secret_)

        if len(secret) < self.MIN_SECRET_SIZE:
            raise InvalidOptionError(
                f'secret should be longer than {self.MIN_SECRET_SIZE} bytes',
            )

        return secret

    def _validate_person(self, person: typing.Union[str, bytes]) -> bytes:
        """Validate the personalisation value and return it clean.

        Args:
            person: Personalisation value to validate.

        Returns:
            Cleaned personalisation value.

        Raises:
            ConversionError: The value is not bytes and can't be converted to bytes.
        """
        return self._force_bytes(person)

    def _validate_digest_size(self, digest_size: typing.Optional[int]) -> int:
        """Validate the digest_size value and return it clean.

        Args:
            digest_size: Digest size value to validate.

        Returns:
            Cleaned digest size value.

        Raises:
            InvalidOptionError: The value is out of bounds.
        """
        if digest_size is None:
            digest_size = self.DEFAULT_DIGEST_SIZE

        if digest_size < self.MIN_DIGEST_SIZE:
            raise InvalidOptionError(
                f'digest_size should be bigger than or equal to {self.MIN_DIGEST_SIZE}',
            )

        return digest_size

    @staticmethod
    def _validate_hasher_choice(
        hasher: typing.Union[HasherChoice, str],
    ) -> HasherChoice:
        """Validate the hasher choice.

        Raises:
            InvalidOptionError: Invalid hasher choice.
        """
        try:
            choice = HasherChoice(hasher)
        except ValueError:
            raise InvalidOptionError(
                f'invalid hasher choice, must be one of: '
                f'{", ".join(h for h in HasherChoice)}',
            )

        return choice

    def _validate_separator(self, separator: typing.Union[str, bytes]) -> bytes:
        """Validate the separator value and return it clean.

        Args:
            separator: Separator value to validate.

        Returns:
            Cleaned separator value.

        Raises:
            ConversionError: The value is not bytes and can't be converted to bytes.
            InvalidOptionError:  The value is out of bounds.
        """
        if not separator:
            raise InvalidOptionError('the separator character must have a value')

        if not separator.isascii():
            raise InvalidOptionError('the separator character must be ASCII')

        return self._force_bytes(separator)

    @staticmethod
    def _get_hasher(
        hasher: HasherChoice,
        *,
        secret: bytes,
        digest_size: int,
        person: bytes,
    ) -> BLAKEHasher:
        """Get the proper hasher instance regarding the choice."""
        hasher_class: typing.Type[BLAKEHasher]

        if hasher in {HasherChoice.blake2b, HasherChoice.blake2s}:
            hasher_class = BLAKE2Hasher
        else:
            hasher_class = BLAKE3Hasher

        return hasher_class(
            hasher,
            secret=secret,
            digest_size=digest_size,
            person=person,
        )

DEFAULT_DIGEST_SIZE: int

Default digest size to use when no digest size is indicated.

MIN_DIGEST_SIZE: int

Minimum digest size allowed (during instantiation).

MIN_SECRET_SIZE: int

Minimum secret size allowed (during instantiation).

Hashers (str, Enum)

Hasher selection choices.

Source code in blake2signer/bases.py
class HasherChoice(str, Enum):
    """Hasher selection choices."""

    blake2b = 'blake2b'
    blake2s = 'blake2s'
    blake3 = 'blake3'

__init__(self, secret, *, personalisation=b'', digest_size=None, hasher=<HasherChoice.blake2b: 'blake2b'>, deterministic=False, separator=b'.') special

Sign and verify signed data using BLAKE in keyed hashing mode.

Parameters:

Name Type Description Default
secret Union[str, bytes]

Secret value which will be derived using BLAKE to produce the signing key. The minimum secret size is enforced to 16 bytes and there is no maximum.

required

Keyword arguments:

Name Type Description
personalisation optional

Personalisation string to force the hash function to produce different digests for the same input. It is derived using BLAKE to ensure it fits the hasher limits, so it has no practical size limit. It defaults to the class name.

digest_size optional

Size of output signature (digest) in bytes (defaults to 16 bytes). The minimum size is enforced to 16 bytes.

hasher optional

Hash function to use: blake2b (default), blake2s or blake3.

deterministic optional

Define if signatures are deterministic or non-deterministic (default). Non-deterministic sigs are preferred, and achieved through the use of a random salt. For deterministic sigs, no salt is used: this means that for the same payload, the same sig is obtained (the advantage is that the sig is shorter).

separator optional

Character to separate the signature and the payload. It must not belong to the encoder alphabet and be ASCII (defaults to ".").

Exceptions:

Type Description
ConversionError

A bytes parameter is not bytes and can't be converted to bytes.

InvalidOptionError

A parameter is out of bounds.

Source code in blake2signer/bases.py
def __init__(
    self,
    secret: typing.Union[str, bytes],
    *,
    personalisation: typing.Union[str, bytes] = b'',
    digest_size: typing.Optional[int] = None,
    hasher: typing.Union[HasherChoice, str] = HasherChoice.blake2b,
    deterministic: bool = False,
    separator: typing.Union[str, bytes] = b'.',
) -> None:
    """Sign and verify signed data using BLAKE in keyed hashing mode.

    Args:
        secret: Secret value which will be derived using BLAKE to produce the
            signing key. The minimum secret size is enforced to 16 bytes and
            there is no maximum.

    Keyword Args:
        personalisation (optional): Personalisation string to force the hash
            function to produce different digests for the same input. It is
            derived using BLAKE to ensure it fits the hasher limits, so it
            has no practical size limit. It defaults to the class name.
        digest_size (optional): Size of output signature (digest) in bytes
            (defaults to 16 bytes). The minimum size is enforced to 16 bytes.
        hasher (optional): Hash function to use: blake2b (default), blake2s
            or blake3.
        deterministic (optional): Define if signatures are deterministic or
            non-deterministic (default). Non-deterministic sigs are preferred,
            and achieved through the use of a random salt. For deterministic
            sigs, no salt is used: this means that for the same payload, the
            same sig is obtained (the advantage is that the sig is shorter).
        separator (optional): Character to separate the signature and the
            payload. It must not belong to the encoder alphabet and be ASCII
            (defaults to ".").

    Raises:
        ConversionError: A bytes parameter is not bytes and can't be converted
            to bytes.
        InvalidOptionError: A parameter is out of bounds.
    """
    hasher_choice = self._validate_hasher_choice(hasher)
    digest_size = self._validate_digest_size(digest_size)
    separator = self._validate_separator(separator)
    person = self._validate_person(personalisation)
    secret = self._validate_secret(secret)

    if deterministic:
        person += b'Deterministic'
    person += self.__class__.__name__.encode()

    self._deterministic: bool = deterministic
    self._separator: bytes = separator

    self._hasher = self._get_hasher(
        hasher_choice,
        secret=secret,
        digest_size=digest_size,
        person=person,
    )

blake2signer.bases.Blake2SignerBase (EncoderMixin, Base, ABC)

Base class for a signer based on BLAKE in keyed hashing mode.

Source code in blake2signer/bases.py
class Blake2SignerBase(EncoderMixin, Base, ABC):
    """Base class for a signer based on BLAKE in keyed hashing mode."""

    def __init__(
        self,
        secret: typing.Union[str, bytes],
        *,
        personalisation: typing.Union[str, bytes] = b'',
        digest_size: typing.Optional[int] = None,
        hasher: typing.Union[HasherChoice, str] = HasherChoice.blake2b,
        deterministic: bool = False,
        separator: typing.Union[str, bytes] = b'.',
        encoder: typing.Type[EncoderInterface] = B64URLEncoder,
    ) -> None:
        """Sign and verify signed data using BLAKE in keyed hashing mode.

        Args:
            secret: Secret value which will be derived using BLAKE to produce the
                signing key. The minimum secret size is enforced to 16 bytes and
                there is no maximum.

        Keyword Args:
            personalisation (optional): Personalisation string to force the hash
                function to produce different digests for the same input. It is
                derived using BLAKE to ensure it fits the hasher limits, so it
                has no practical size limit. It defaults to the class name.
            digest_size (optional): Size of output signature (digest) in bytes
                (defaults to 16 bytes). The minimum size is enforced to 16 bytes.
            hasher (optional): Hash function to use: blake2b (default), blake2s
                or blake3.
            deterministic (optional): Define if signatures are deterministic or
                non-deterministic (default). Non-deterministic sigs are preferred,
                and achieved through the use of a random salt. For deterministic
                sigs, no salt is used: this means that for the same payload, the
                same sig is obtained (the advantage is that the sig is shorter).
            separator (optional): Character to separate the signature and the
                payload. It must not belong to the encoder alphabet and be ASCII
                (defaults to ".").
            encoder (optional): Encoder class to use for the signature, nothing
                else is encoded (defaults to a Base64 URL safe encoder).

        Raises:
            ConversionError: A bytes parameter is not bytes and can't be converted
                to bytes.
            InvalidOptionError: A parameter is out of bounds.
        """
        super().__init__(
            secret,
            personalisation=personalisation,
            digest_size=digest_size,
            hasher=hasher,
            separator=separator,
            deterministic=deterministic,
            encoder=encoder,
        )

    def _validate_separator(self, separator: typing.Union[str, bytes]) -> bytes:
        """Validate the separator value and return it clean.

        Args:
            separator: Separator value to validate.

        Returns:
            Cleaned separator value.

        Raises:
            ConversionError: The value is not bytes and can't be converted to bytes.
            InvalidOptionError: The value is out of bounds.
        """
        sep = super()._validate_separator(separator)

        if sep in self._encoder.alphabet:
            raise InvalidOptionError(
                'the separator character must not belong to the encoder alphabet',
            )

        return sep

    def _get_salt(self) -> bytes:
        """Get a salt for the signature considering its type.

        For non-deterministic signatures, a pseudo random salt is generated.
        """
        if self._deterministic:
            return b''

        salt_size = self._hasher.salt_size
        salt = os.urandom(salt_size)
        # Produce an encoded salt to use it as is, so we don't have to deal with
        # decoding it when unsigning. The only downside is that we loose a few
        # bits, but it's tolerable since we are using the maximum allowed size.
        return self._encode(salt)[:salt_size]

    def _force_bytes_parts(
        self,
        signature: typing.Union[Blake2Signature, Blake2SignatureDump],
    ) -> Blake2Signature:
        """Force given value into bytes, meaning a Blake2Signature container."""
        return Blake2Signature(
            data=self._force_bytes(signature.data),
            signature=self._force_bytes(signature.signature),
        )

    def _compose(self, data: bytes, *, signature: bytes) -> bytes:
        """Compose data and signature into a single stream."""
        return signature + self._separator + data

    def _decompose(self, signed_data: bytes) -> SignedDataParts:
        """Decompose a signed data stream into its parts.

        Raises:
            SignatureError: Invalid signed data.
        """
        if self._separator not in signed_data:
            raise SignatureError('separator not found in signed data')

        composite_signature, data = signed_data.split(self._separator, 1)

        if not composite_signature:
            raise SignatureError('signature information is missing')

        if self._deterministic:
            salt = b''
            signature = composite_signature
        else:
            salt_size = self._hasher.salt_size
            salt = composite_signature[:salt_size]
            signature = composite_signature[salt_size:]

        return SignedDataParts(data=data, salt=salt, signature=signature)

    def _signify(self, *, salt: bytes, data: bytes) -> bytes:
        """Return signature for given data using salt and all the hasher options.

        The signature is encoded using the chosen encoder.
        """
        signature = self._hasher.digest(data, salt=salt)

        return self._encode(signature)

    def _sign(self, data: bytes) -> bytes:
        """Sign given data and produce a signature stream composed of salt and signature.

        The signature stream (salt and signature) is encoded using the chosen encoder.
        """
        salt = self._get_salt()
        signature = self._signify(salt=salt, data=data)

        return salt + signature

    def _unsign(self, parts: SignedDataParts) -> bytes:
        """Verify signed data parts and recover original data.

        Args:
            parts: Signed data parts to unsign.

        Returns:
            Original data.

        Raises:
            SignatureError: Signed data structure is not valid.
            InvalidSignatureError: Signed data signature is invalid.
        """
        good_signature = self._signify(salt=parts.salt, data=parts.data)

        if compare_digest(good_signature, parts.signature):
            return parts.data

        raise InvalidSignatureError('signature is not valid')

__init__(self, secret, *, personalisation=b'', digest_size=None, hasher=<HasherChoice.blake2b: 'blake2b'>, deterministic=False, separator=b'.', encoder=<class 'blake2signer.encoders.B64URLEncoder'>) special

Sign and verify signed data using BLAKE in keyed hashing mode.

Parameters:

Name Type Description Default
secret Union[str, bytes]

Secret value which will be derived using BLAKE to produce the signing key. The minimum secret size is enforced to 16 bytes and there is no maximum.

required

Keyword arguments:

Name Type Description
personalisation optional

Personalisation string to force the hash function to produce different digests for the same input. It is derived using BLAKE to ensure it fits the hasher limits, so it has no practical size limit. It defaults to the class name.

digest_size optional

Size of output signature (digest) in bytes (defaults to 16 bytes). The minimum size is enforced to 16 bytes.

hasher optional

Hash function to use: blake2b (default), blake2s or blake3.

deterministic optional

Define if signatures are deterministic or non-deterministic (default). Non-deterministic sigs are preferred, and achieved through the use of a random salt. For deterministic sigs, no salt is used: this means that for the same payload, the same sig is obtained (the advantage is that the sig is shorter).

separator optional

Character to separate the signature and the payload. It must not belong to the encoder alphabet and be ASCII (defaults to ".").

encoder optional

Encoder class to use for the signature, nothing else is encoded (defaults to a Base64 URL safe encoder).

Exceptions:

Type Description
ConversionError

A bytes parameter is not bytes and can't be converted to bytes.

InvalidOptionError

A parameter is out of bounds.

Source code in blake2signer/bases.py
def __init__(
    self,
    secret: typing.Union[str, bytes],
    *,
    personalisation: typing.Union[str, bytes] = b'',
    digest_size: typing.Optional[int] = None,
    hasher: typing.Union[HasherChoice, str] = HasherChoice.blake2b,
    deterministic: bool = False,
    separator: typing.Union[str, bytes] = b'.',
    encoder: typing.Type[EncoderInterface] = B64URLEncoder,
) -> None:
    """Sign and verify signed data using BLAKE in keyed hashing mode.

    Args:
        secret: Secret value which will be derived using BLAKE to produce the
            signing key. The minimum secret size is enforced to 16 bytes and
            there is no maximum.

    Keyword Args:
        personalisation (optional): Personalisation string to force the hash
            function to produce different digests for the same input. It is
            derived using BLAKE to ensure it fits the hasher limits, so it
            has no practical size limit. It defaults to the class name.
        digest_size (optional): Size of output signature (digest) in bytes
            (defaults to 16 bytes). The minimum size is enforced to 16 bytes.
        hasher (optional): Hash function to use: blake2b (default), blake2s
            or blake3.
        deterministic (optional): Define if signatures are deterministic or
            non-deterministic (default). Non-deterministic sigs are preferred,
            and achieved through the use of a random salt. For deterministic
            sigs, no salt is used: this means that for the same payload, the
            same sig is obtained (the advantage is that the sig is shorter).
        separator (optional): Character to separate the signature and the
            payload. It must not belong to the encoder alphabet and be ASCII
            (defaults to ".").
        encoder (optional): Encoder class to use for the signature, nothing
            else is encoded (defaults to a Base64 URL safe encoder).

    Raises:
        ConversionError: A bytes parameter is not bytes and can't be converted
            to bytes.
        InvalidOptionError: A parameter is out of bounds.
    """
    super().__init__(
        secret,
        personalisation=personalisation,
        digest_size=digest_size,
        hasher=hasher,
        separator=separator,
        deterministic=deterministic,
        encoder=encoder,
    )

blake2signer.bases.Blake2TimestampSignerBase (Blake2SignerBase, ABC)

Base class for a timestamp signer based on BLAKE in keyed hashing mode.

Source code in blake2signer/bases.py
class Blake2TimestampSignerBase(Blake2SignerBase, ABC):
    """Base class for a timestamp signer based on BLAKE in keyed hashing mode."""

    def _get_timestamp(self) -> bytes:
        """Get the encoded timestamp value."""
        timestamp = int(time())  # It's easier to encode and decode an integer
        try:
            timestamp_b = timestamp.to_bytes(4, 'big', signed=False)
        except OverflowError:  # This will happen in ~2106-02-07
            raise RuntimeError(
                'can not represent this timestamp in bytes: this library is '
                'too old and needs to be updated!',
            )

        return self._encode(timestamp_b)

    def _decode_timestamp(self, encoded_timestamp: bytes) -> int:
        """Decode an encoded timestamp whose signature should have been validated.

        Raises:
            DecodeError: Timestamp can't be decoded.
        """
        return int.from_bytes(self._decode(encoded_timestamp), 'big', signed=False)

    def _compose_timestamp(self, data: bytes, *, timestamp: bytes) -> bytes:
        """Compose timestamp value with data."""
        return timestamp + self._separator + data

    def _decompose_timestamp(self, timestamped_data: bytes) -> TimestampedDataParts:
        """Decompose data + timestamp value.

        Raises:
            SignatureError: Invalid timestamped data.
            DecodeError: Timestamp can't be decoded.
        """
        if self._separator not in timestamped_data:
            raise SignatureError('separator not found in timestamped data')

        encoded_timestamp, data = timestamped_data.split(self._separator, 1)

        if not encoded_timestamp:
            raise SignatureError('timestamp information is missing')

        timestamp = self._decode_timestamp(encoded_timestamp)

        return TimestampedDataParts(data=data, timestamp=timestamp)

    @staticmethod
    def _get_ttl_from_max_age(max_age: typing.Union[int, float, timedelta]) -> float:
        """Get the time-to-live value in seconds."""
        if isinstance(max_age, timedelta):
            return max_age.total_seconds()

        return float(max_age)

    def _sign_with_timestamp(self, data: bytes) -> bytes:
        """Sign given data and produce a timestamped signature stream.

        The timestamped signature stream (timestamp, signature and salt) is
        encoded using the chosen encoder.

        Returns:
            A signature stream composed of salt, signature and timestamp.
        """
        timestamp = self._get_timestamp()
        timestamped_data = self._compose_timestamp(data, timestamp=timestamp)

        return self._compose(timestamp, signature=self._sign(timestamped_data))

    def _unsign_with_timestamp(
        self,
        parts: SignedDataParts,
        *,
        max_age: typing.Union[int, float, timedelta],
    ) -> bytes:
        """Verify signed data parts with timestamp and recover original data.

        Args:
            parts: Signed data parts to unsign.

        Keyword Args:
            max_age: Ensure the signature is not older than this time in seconds.

        Returns:
            Original data.

        Raises:
            SignatureError: Signed data structure is not valid.
            InvalidSignatureError: Signed data signature is invalid.
            ExpiredSignatureError: Signed data signature has expired.
            DecodeError: Timestamp can't be decoded.
        """
        timestamped_data = self._unsign(parts)

        timestamped_parts = self._decompose_timestamp(timestamped_data)

        now = time()
        age = now - timestamped_parts.timestamp
        ttl = self._get_ttl_from_max_age(max_age)

        if age > ttl:
            raise ExpiredSignatureError(
                f'signature has expired, age {age} > {ttl} seconds',
                timestamp=timestamp_to_aware_datetime(timestamped_parts.timestamp),
            )

        if age < 0:  # Signed in the future
            raise ExpiredSignatureError(
                f'signature has expired, age {age} < 0 seconds',
                timestamp=timestamp_to_aware_datetime(timestamped_parts.timestamp),
            )

        return timestamped_parts.data

blake2signer.bases.Blake2DualSignerBase (Blake2TimestampSignerBase, ABC)

Base class for a dual signer: with and without timestamp.

Source code in blake2signer/bases.py
class Blake2DualSignerBase(Blake2TimestampSignerBase, ABC):
    """Base class for a dual signer: with and without timestamp."""

    def __init__(
        self,
        secret: typing.Union[str, bytes],
        *,
        max_age: typing.Union[None, int, float, timedelta] = None,
        personalisation: typing.Union[str, bytes] = b'',
        digest_size: typing.Optional[int] = None,
        hasher: typing.Union[HasherChoice, str] = HasherChoice.blake2b,
        deterministic: bool = False,
        separator: typing.Union[str, bytes] = b'.',
        encoder: typing.Type[EncoderInterface] = B64URLEncoder,
    ) -> None:
        """Sign and verify signed and optionally timestamped data using BLAKE.

        It uses BLAKE in keyed hashing mode.

        Setting `max_age` will produce a timestamped signed stream.

        Args:
            secret: Secret value which will be derived using BLAKE to produce the
                signing key. The minimum secret size is enforced to 16 bytes and
                there is no maximum.

        Keyword Args:
            max_age (optional): Use a timestamp signer instead of a regular one
                to ensure that the signature is not older than this time in seconds.
            personalisation (optional): Personalisation string to force the hash
                function to produce different digests for the same input. It is
                derived using BLAKE to ensure it fits the hasher limits, so it
                has no practical size limit. It defaults to the class name.
            digest_size (optional): Size of output signature (digest) in bytes
                (defaults to 16 bytes). The minimum size is enforced to 16 bytes.
            hasher (optional): Hash function to use: blake2b (default), blake2s
                or blake3.
            deterministic (optional): Define if signatures are deterministic or
                non-deterministic (default). Non-deterministic sigs are preferred,
                and achieved through the use of a random salt. For deterministic
                sigs, no salt is used: this means that for the same payload, the
                same sig is obtained (the advantage is that the sig is shorter).
            separator (optional): Character to separate the signature and the
                payload. It must not belong to the encoder alphabet and be ASCII
                (defaults to ".").
            encoder (optional): Encoder class to use (defaults to a Base64 URL
                safe encoder).

        Raises:
            ConversionError: A bytes parameter is not bytes and can't be converted
                to bytes.
            InvalidOptionError: A parameter is out of bounds.
        """
        if max_age is not None:
            personalisation = self._force_bytes(personalisation) + b'Timestamp'

        self._max_age: typing.Union[None, int, float, timedelta] = max_age

        super().__init__(
            secret,
            personalisation=personalisation,
            digest_size=digest_size,
            hasher=hasher,
            deterministic=deterministic,
            separator=separator,
            encoder=encoder,
        )

    def _proper_sign(self, data: bytes) -> bytes:
        """Sign given data with a (timestamp) signer producing a signature stream.

        The signature stream (salt, signature and/or timestamp) are encoded using
        the chosen encoder.
        """
        if self._max_age is None:
            return self._sign(data)

        return self._sign_with_timestamp(data)

    def _proper_unsign(self, parts: SignedDataParts) -> bytes:
        """Unsign signed data properly with the corresponding signer.

        Raises:
            SignatureError: Signed data structure is not valid.
            InvalidSignatureError: Signed data signature is invalid.
            ExpiredSignatureError: Signed data signature has expired.
            DecodeError: Timestamp can't be decoded.
        """
        if self._max_age is None:
            return self._unsign(parts)

        return self._unsign_with_timestamp(parts, max_age=self._max_age)

__init__(self, secret, *, max_age=None, personalisation=b'', digest_size=None, hasher=<HasherChoice.blake2b: 'blake2b'>, deterministic=False, separator=b'.', encoder=<class 'blake2signer.encoders.B64URLEncoder'>) special

Sign and verify signed and optionally timestamped data using BLAKE.

It uses BLAKE in keyed hashing mode.

Setting max_age will produce a timestamped signed stream.

Parameters:

Name Type Description Default
secret Union[str, bytes]

Secret value which will be derived using BLAKE to produce the signing key. The minimum secret size is enforced to 16 bytes and there is no maximum.

required

Keyword arguments:

Name Type Description
max_age optional

Use a timestamp signer instead of a regular one to ensure that the signature is not older than this time in seconds.

personalisation optional

Personalisation string to force the hash function to produce different digests for the same input. It is derived using BLAKE to ensure it fits the hasher limits, so it has no practical size limit. It defaults to the class name.

digest_size optional

Size of output signature (digest) in bytes (defaults to 16 bytes). The minimum size is enforced to 16 bytes.

hasher optional

Hash function to use: blake2b (default), blake2s or blake3.

deterministic optional

Define if signatures are deterministic or non-deterministic (default). Non-deterministic sigs are preferred, and achieved through the use of a random salt. For deterministic sigs, no salt is used: this means that for the same payload, the same sig is obtained (the advantage is that the sig is shorter).

separator optional

Character to separate the signature and the payload. It must not belong to the encoder alphabet and be ASCII (defaults to ".").

encoder optional

Encoder class to use (defaults to a Base64 URL safe encoder).

Exceptions:

Type Description
ConversionError

A bytes parameter is not bytes and can't be converted to bytes.

InvalidOptionError

A parameter is out of bounds.

Source code in blake2signer/bases.py
def __init__(
    self,
    secret: typing.Union[str, bytes],
    *,
    max_age: typing.Union[None, int, float, timedelta] = None,
    personalisation: typing.Union[str, bytes] = b'',
    digest_size: typing.Optional[int] = None,
    hasher: typing.Union[HasherChoice, str] = HasherChoice.blake2b,
    deterministic: bool = False,
    separator: typing.Union[str, bytes] = b'.',
    encoder: typing.Type[EncoderInterface] = B64URLEncoder,
) -> None:
    """Sign and verify signed and optionally timestamped data using BLAKE.

    It uses BLAKE in keyed hashing mode.

    Setting `max_age` will produce a timestamped signed stream.

    Args:
        secret: Secret value which will be derived using BLAKE to produce the
            signing key. The minimum secret size is enforced to 16 bytes and
            there is no maximum.

    Keyword Args:
        max_age (optional): Use a timestamp signer instead of a regular one
            to ensure that the signature is not older than this time in seconds.
        personalisation (optional): Personalisation string to force the hash
            function to produce different digests for the same input. It is
            derived using BLAKE to ensure it fits the hasher limits, so it
            has no practical size limit. It defaults to the class name.
        digest_size (optional): Size of output signature (digest) in bytes
            (defaults to 16 bytes). The minimum size is enforced to 16 bytes.
        hasher (optional): Hash function to use: blake2b (default), blake2s
            or blake3.
        deterministic (optional): Define if signatures are deterministic or
            non-deterministic (default). Non-deterministic sigs are preferred,
            and achieved through the use of a random salt. For deterministic
            sigs, no salt is used: this means that for the same payload, the
            same sig is obtained (the advantage is that the sig is shorter).
        separator (optional): Character to separate the signature and the
            payload. It must not belong to the encoder alphabet and be ASCII
            (defaults to ".").
        encoder (optional): Encoder class to use (defaults to a Base64 URL
            safe encoder).

    Raises:
        ConversionError: A bytes parameter is not bytes and can't be converted
            to bytes.
        InvalidOptionError: A parameter is out of bounds.
    """
    if max_age is not None:
        personalisation = self._force_bytes(personalisation) + b'Timestamp'

    self._max_age: typing.Union[None, int, float, timedelta] = max_age

    super().__init__(
        secret,
        personalisation=personalisation,
        digest_size=digest_size,
        hasher=hasher,
        deterministic=deterministic,
        separator=separator,
        encoder=encoder,
    )

blake2signer.bases.Blake2SerializerSignerBase (Blake2DualSignerBase, ABC)

Base class for a serializer signer that implements dumps and loads.

Source code in blake2signer/bases.py
class Blake2SerializerSignerBase(Blake2DualSignerBase, ABC):
    """Base class for a serializer signer that implements `dumps` and `loads`."""

    @abstractmethod
    def _dumps(self, data: typing.Any, **kwargs: typing.Any) -> bytes:
        """Dump data serializing it.

        Implement this method with all the tasks necessary to serialize data, such
        as encoding, compression, etc.

        Args:
            data: Data to serialize.

        Keyword Args:
            **kwargs: Additional keyword only arguments for the method.

        Returns:
            Serialized data.
        """

    @abstractmethod
    def _loads(self, dumped_data: bytes, **kwargs: typing.Any) -> typing.Any:
        """Load serialized data to recover it.

        Implement this method with all the tasks necessary to unserialize data,
        such as decoding, decompression, etc.

        Args:
            dumped_data: Data to unserialize.

        Keyword Args
            **kwargs: Additional keyword only arguments for the method.

        Returns:
            Original data.
        """

    @staticmethod
    def _read(file: typing.IO) -> typing.AnyStr:
        """Read data from a file.

        Raises:
            FileError: File can't be read.
        """
        try:
            return file.read()
        except OSError as exc:
            raise FileError('file can not be read') from exc

    def _write(self, file: typing.IO, data: str) -> None:
        """Write data to file.

        Notes:
            The file can be either in text or binary mode, therefore given data
            is properly converted before writing.

        Raises:
            FileError: File can't be written.
            ConversionError: Data can't be converted to bytes (can happen when
                file is in binary mode).
        """
        data_ = data if file_mode_is_text(file) else self._force_bytes(data)

        try:
            file.write(data_)
        except OSError as exc:
            raise FileError('file can not be written') from exc

Signatures

blake2signer.bases.Blake2Signature (tuple)

Signature container.

Source code in blake2signer/bases.py
class Blake2Signature(typing.NamedTuple):
    """Signature container."""

    signature: bytes  # Composite signature
    data: bytes

__getnewargs__(self) special

Return self as a plain tuple. Used by copy and pickle.

Source code in blake2signer/bases.py
def __getnewargs__(self):
    'Return self as a plain tuple.  Used by copy and pickle.'
    return _tuple(self)

__new__(_cls, signature, data) special staticmethod

Create new instance of Blake2Signature(signature, data)

__repr__(self) special

Return a nicely formatted representation string

Source code in blake2signer/bases.py
def __repr__(self):
    'Return a nicely formatted representation string'
    return self.__class__.__name__ + repr_fmt % self

blake2signer.bases.Blake2SignatureDump (tuple)

Signature container.

Source code in blake2signer/bases.py
class Blake2SignatureDump(typing.NamedTuple):
    """Signature container."""

    signature: str  # Composite signature
    data: str

__getnewargs__(self) special

Return self as a plain tuple. Used by copy and pickle.

Source code in blake2signer/bases.py
def __getnewargs__(self):
    'Return self as a plain tuple.  Used by copy and pickle.'
    return _tuple(self)

__new__(_cls, signature, data) special staticmethod

Create new instance of Blake2SignatureDump(signature, data)

__repr__(self) special

Return a nicely formatted representation string

Source code in blake2signer/bases.py
def __repr__(self):
    'Return a nicely formatted representation string'
    return self.__class__.__name__ + repr_fmt % self
Back to top