Metadata-Version: 2.5
Name: chab3
Version: 0.1.0
Summary: ChaCha20-Blake3 AEAD boxes, counter-nonce channels, and a fast CSPRNG — everything supports in-place operation into caller buffers
License: MIT
Requires-Python: >=3.12
Requires-Dist: blake3>=1.0.10
Requires-Dist: cryptography>=50.0.2
Description-Content-Type: text/markdown

# ChaCha20-Blake3

Standard IETF ChaCha20-Poly1305 style construct with a cryptographically strong BLAKE3 hash instead, preventing key holders from crafting different messages that correspond to same tag or ciphertext.

The implementation follows standard practices, not the vastly modified KERKOUR ChaCha20-BLAKE3 implementation. Therefore we keep 12 byte nonce and 16 byte default tag length, so this remains a full drop-in replacement for ChaCha20-Poly1305, whereas they use 32 bytes for each, staged key/nonce derivation and a completely different authentication format.

**Draft version 0.1.0:** Implementation is still likely to change. Do not use in production.

## Features

- **Boxes**: `encrypt`/`decrypt` and `encrypt_into`/`decrypt_into` — one-shot AEAD with appended random nonce, associated data, and MAC.
- **Detached nonces**: the standard AEAD construct using a provided nonce and does not attach it to the ciphertext; as opposed to random nonce attached in boxes
- **Detached tags**: `tag` — compute the BLAKE3 authentication tag separately, for use with external signature schemes over the tag.
- **Channels**: `Sender`/`Receiver` — ordered message encryption/decryption over a shared key using an incrementing counter nonce.
- **CSPRNG**: `random(n)` / `random(buf)` — fast ChaCha20-based random bytes, thread-safe, seeded from OS randomness.

Wire format: `ciphertext mac [nonce]`. The MAC is a keyed BLAKE3 over the associated data and ciphertext; the key stream is IETF ChaCha20.

## Usage

```python
import chab3

key = chab3.random(32)

ct = chab3.encrypt(b"hello", key, ad=b"metadata")
msg = chab3.decrypt(ct, key, ad=b"metadata")

s, r = chab3.Sender(key), chab3.Receiver(key)
assert r.decrypt(s.encrypt(b"in order")) == b"in order"
```

Using existing output buffer, in-place operation:

```python
buf = bytearray(100)
out = chab3.encrypt_into(buf, b"attack at dawn", key)  # out is a sliced memoryview into buf
msg = chab3.decrypt_into(buf, out, key)  # memoryview into buf now showing b"hello"
```

Tampered ciphertext raises `chab3.WireError`; caller misuse (wrong buffer or key sizes) raises `TypeError`.
