Metadata-Version: 2.4
Name: aiohttp-signer
Version: 0.1.1
Project-URL: repository, https://codeberg.org/bovine/aiohttp-signer
Requires-Python: >=3.11
Requires-Dist: aiohttp>=3.13.3
Requires-Dist: fieldz==0.1.3
Requires-Dist: mkdocs==1.6.1
Description-Content-Type: text/markdown

# aiohttp_signer

This is an [aiohttp client middleware](https://docs.aiohttp.org/en/stable/client_advanced.html#client-middleware),
see also [Client Middleware Cookbook](https://docs.aiohttp.org/en/stable/client_middleware_cookbook.html),
to add the digest and http message signatures.

__Warning__: Not all parts of [RFC 9421 HTTP Message Signatures](https://www.rfc-editor.org/rfc/rfc9421.html)
are implemented yet.

* [pypi](https://pypi.org/project/aiohttp-signer/)
* [bovine-actor](https://codeberg.org/bovine/bovine-actor), uses aiohttp-signer for Fediverse GET/POST

## History

This package was created as part of rewriting [bovine](https://codeberg.org/bovine/bovine/).
My goal with it being to separate the parts that are HTTP from the parts that are
Fediverse and the parts that are cryptography.

## Usage

The following illustrates the simplest usage of this package

```python
import aiohttp
from aiohttp_signer import DigestMiddleware, Rfc9421Signer

async def make_post_request(
    target_url: str,
    data: dict,
    key_id: str,
    signer: Callable[[bytes], bytes]
):
    async with aiohttp.ClientSession() as session:
        await session.post(
            target_url,
            json=data,
            middlewares=[
                DigestMiddleware(),
                Rfc9421Signer(key_id=key_id, signer=signer),
            ],
        )
```

This will cause the message to contain the `content-digest`,
`signature-input`, and `signature` headers. Where the used
signature parameters are `@method`, `@target-uri`, and `content-digest`.

A sample implementation of `signer` would be

```python
from cryptography.hazmat.primitives.serialization import load_pem_private_key
from cryptography.hazmat.primitives.asymmetric import ed25519

private_key_pem = b"""
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIJ+DYvh6SEqVTm50DFtMDoQikTmiCqirVv9mWG9qfSnF
-----END PRIVATE KEY-----
"""

def signer(x: bytes):
    private_key = load_pem_private_key(private_key_pem, password=None)
    assert isinstance(private_key, ed25519.Ed25519PrivateKey)
    return private_key.sign(x)
```