Source code for swimprotocol.sign


from __future__ import annotations

import hashlib
import hmac
import secrets
import uuid
from typing import Final, NamedTuple, Union

from .__about__ import __version__

__all__ = ['Signature', 'Signatures']


[docs]class Signature(NamedTuple): """A salted digest, including the salt value. Args: salt: Salt used to sign *digest*. digest: The salted digest. """ salt: bytes digest: bytes
[docs]class Signatures: """Provides a hash signature for inclusion alongside :class:`~swimprotocol.packet.Packet` objects when implementing a :class:`~swimprotocol.transport.Transport` protocol. Note: Passing ``secret=None`` uses :func:`uuid.getnode` as the secret, which effectively allows you to experiment locally but will not suffice for connections over a network. Args: secret: A shared secret among all cluster members. hash_name: The :mod:`hashlib` hash name to use. salt_len: The length of the salt to use when hashing. check_version: True if the :attr:`swimprotocol.__version__` should be included in the signature and verification. """ def __init__(self, secret: Union[None, str, bytes], *, hash_name: str = 'sha256', salt_len: int = 16, check_version: bool = True) -> None: super().__init__() if secret is None: secret = b'%x' % uuid.getnode() elif isinstance(secret, str): secret = secret.encode('utf-8') self.secret: Final = secret self.hash_name: Final = hash_name self.salt_len: Final = salt_len self.digest_size: Final = hashlib.new(hash_name).digest_size self.version = __version__.encode('ascii') if check_version else b''
[docs] def sign(self, data: bytes) -> Signature: """Sign the data using a new random salt, returning the salt and the resulting digest as a tuple. Args: data: The bytes to be signed. """ salt = secrets.token_bytes(16) salted_data = b'%s%s%s' % (self.version, salt, data) digest = hmac.digest(self.secret, salted_data, self.hash_name) return Signature(salt, digest)
[docs] def verify(self, data: bytes, sig: tuple[bytes, bytes]) -> bool: """Verify that a signature is valid for the given salt and data. Args: data: The bytes to be verified. sig: A tuple of the salt used during signing and the resulting digest. """ salt, sig_digest = sig salted_data = b'%s%s%s' % (self.version, salt, data) digest = hmac.digest(self.secret, salted_data, self.hash_name) return hmac.compare_digest(sig_digest, digest)