Skip to content

bovine.crypto.http_signature

bovine.crypto.http_signature

HttpSignature

Helper class to build http signatures

Usage: Add fields used for signature with with_fields. Then use build_signature or verify depending on use case.

Source code in bovine/bovine/crypto/http_signature.py
class HttpSignature:
    """Helper class to build http signatures

    Usage: Add fields used for signature with `with_fields`. Then
    use `build_signature` or `verify` depending on use case.
    """

    def __init__(self):
        self.fields = []

    def build_signature(self, key_id: str, private_key: str):
        """Returns the signature string when signed with private_key"""

        warnings.warn(
            "Deprecated use sign_for_http_draft instead, will be remove in bovine 0.6.0",
            DeprecationWarning,
        )

        message = self.build_message()

        signature_string = sign_message(private_key, message)
        headers = " ".join(name for name, _ in self.fields)

        signature_parts = [
            f'keyId="{key_id}"',
            'algorithm="rsa-sha256"',
            f'headers="{headers}"',
            f'signature="{signature_string}"',
        ]

        return ",".join(signature_parts)

    def sign_for_http_draft(self, secret: CryptographicSecret):
        message = self.build_message()

        signature_string = secret.sign(message)
        headers = " ".join(name for name, _ in self.fields)

        signature_parts = [
            f'keyId="{secret.key_id}"',
            'algorithm="rsa-sha256"',
            f'headers="{headers}"',
            f'signature="{signature_string}"',
        ]

        return ",".join(signature_parts)

    def ed25519_sign(self, private_encoded):
        private_key = multibase_to_private_key(private_encoded)

        message = self.build_message()

        return multibase_58btc_encode(private_key.sign(message.encode("utf-8")))

    def ed25519_verify(self, did_key, signature):
        public_key = did_key_to_public_key(did_key)

        if signature[0] != "z":
            raise ValueError(f"Expected signature to start with a z, got: {signature}")

        signature = multibase_decode(signature)

        message = self.build_message().encode("utf-8")

        try:
            public_key.verify(signature, message)
        except InvalidSignature:
            return False

        return True

    def verify(self, public_key: str, signature: str):
        """Verifies signature

        to be remove with bovine 0.6.0"""
        warnings.warn(
            "Deprecated use verify_with_identity instead, will be remove in bovine 0.6.0",
            DeprecationWarning,
        )

        message = self.build_message()
        return verify_signature(public_key, message, signature)

    def verify_with_identity(self, identifier: CryptographicIdentifier, signature: str):
        """Verifies the signature with the given Cryptographic identifier
        used for legacy http signatures

        :param identifier:
        :param signature:
        """
        return identifier.verify(self.build_message(), signature)

    def build_message(self):
        """Builds the message as used in legacy http signatures

        ```pycon
        >>> signature = HttpSignature().with_field("field", "value")
        >>> signature.build_message()
        'field: value'

        ```
        """
        return "\n".join(f"{name}: {value}" for name, value in self.fields)

    def verify_with_identity_quoted(
        self, identifier: CryptographicIdentifier, signature: str
    ):
        """Verifies the signature with the given Cryptographic identifier
        used for [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421.html) http signatures

        :param identifier:
        :param signature:
        """
        message = self.build_message_quoted()
        return identifier.verify(message, signature)

    def build_message_quoted(self):
        """Builds the message in [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421.html) format

        ```pycon
        >>> signature = HttpSignature().with_field("field", "value")
        >>> signature.build_message_quoted()
        '"field": value'

        ```
        """
        return "\n".join(f""""{name}": {value}""" for name, value in self.fields)

    def with_field(self, field_name, field_value):
        """Adds a field to be used when building a http signature"""
        self.fields.append((field_name, field_value))
        return self

    @property
    def headers(self):
        """Headers as specified when building http signature"""
        return {name: value for name, value in self.fields if name[0] != "("}

headers property

headers

Headers as specified when building http signature

build_message

build_message()

Builds the message as used in legacy http signatures

>>> signature = HttpSignature().with_field("field", "value")
>>> signature.build_message()
'field: value'
Source code in bovine/bovine/crypto/http_signature.py
def build_message(self):
    """Builds the message as used in legacy http signatures

    ```pycon
    >>> signature = HttpSignature().with_field("field", "value")
    >>> signature.build_message()
    'field: value'

    ```
    """
    return "\n".join(f"{name}: {value}" for name, value in self.fields)

build_message_quoted

build_message_quoted()

Builds the message in RFC 9421 format

>>> signature = HttpSignature().with_field("field", "value")
>>> signature.build_message_quoted()
'"field": value'
Source code in bovine/bovine/crypto/http_signature.py
def build_message_quoted(self):
    """Builds the message in [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421.html) format

    ```pycon
    >>> signature = HttpSignature().with_field("field", "value")
    >>> signature.build_message_quoted()
    '"field": value'

    ```
    """
    return "\n".join(f""""{name}": {value}""" for name, value in self.fields)

build_signature

build_signature(key_id: str, private_key: str)

Returns the signature string when signed with private_key

Source code in bovine/bovine/crypto/http_signature.py
def build_signature(self, key_id: str, private_key: str):
    """Returns the signature string when signed with private_key"""

    warnings.warn(
        "Deprecated use sign_for_http_draft instead, will be remove in bovine 0.6.0",
        DeprecationWarning,
    )

    message = self.build_message()

    signature_string = sign_message(private_key, message)
    headers = " ".join(name for name, _ in self.fields)

    signature_parts = [
        f'keyId="{key_id}"',
        'algorithm="rsa-sha256"',
        f'headers="{headers}"',
        f'signature="{signature_string}"',
    ]

    return ",".join(signature_parts)

verify

verify(public_key: str, signature: str)

Verifies signature

to be remove with bovine 0.6.0

Source code in bovine/bovine/crypto/http_signature.py
def verify(self, public_key: str, signature: str):
    """Verifies signature

    to be remove with bovine 0.6.0"""
    warnings.warn(
        "Deprecated use verify_with_identity instead, will be remove in bovine 0.6.0",
        DeprecationWarning,
    )

    message = self.build_message()
    return verify_signature(public_key, message, signature)

verify_with_identity

verify_with_identity(
    identifier: CryptographicIdentifier, signature: str
)

Verifies the signature with the given Cryptographic identifier used for legacy http signatures

Parameters:

Name Type Description Default
identifier CryptographicIdentifier
required
signature str
required
Source code in bovine/bovine/crypto/http_signature.py
def verify_with_identity(self, identifier: CryptographicIdentifier, signature: str):
    """Verifies the signature with the given Cryptographic identifier
    used for legacy http signatures

    :param identifier:
    :param signature:
    """
    return identifier.verify(self.build_message(), signature)

verify_with_identity_quoted

verify_with_identity_quoted(
    identifier: CryptographicIdentifier, signature: str
)

Verifies the signature with the given Cryptographic identifier used for RFC 9421 http signatures

Parameters:

Name Type Description Default
identifier CryptographicIdentifier
required
signature str
required
Source code in bovine/bovine/crypto/http_signature.py
def verify_with_identity_quoted(
    self, identifier: CryptographicIdentifier, signature: str
):
    """Verifies the signature with the given Cryptographic identifier
    used for [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421.html) http signatures

    :param identifier:
    :param signature:
    """
    message = self.build_message_quoted()
    return identifier.verify(message, signature)

with_field

with_field(field_name, field_value)

Adds a field to be used when building a http signature

Source code in bovine/bovine/crypto/http_signature.py
def with_field(self, field_name, field_value):
    """Adds a field to be used when building a http signature"""
    self.fields.append((field_name, field_value))
    return self