ps-cypher 0.1.0-29

Convergent encryption with ChaCha20-Poly1305, zstd, and Reed-Solomon ECC
Documentation
# ps-cypher

Convergent encryption with ChaCha20-Poly1305, zstd, and Reed-Solomon ECC.

## Scheme

- The encryption key is the hash of the plaintext (`ps-hash`): the `ChaCha20`
  key is the hash's 32-byte digest, and the nonce is derived from the hash's
  parity bytes.
- The plaintext is compressed deterministically (zstd level 7 via
  `ps-compress`) before encryption.
- The ciphertext is Reed-Solomon encoded (`ps-ecc`), so up to 12 corrupted
  bytes per codeword are repaired transparently during decryption.
- Identical inputs therefore always produce identical ciphertexts, keys, and
  hashes, which enables content-addressed deduplication of encrypted data.
- `decrypt` re-hashes the decrypted data and fails with `KeyMismatch` unless
  it hashes to the decryption key, so a successful decryption returns
  exactly the data that produced the key.

## Example

```rust
use ps_cypher::{decrypt, encrypt};

let encrypted = encrypt(b"example payload")?;
let decrypted = decrypt(&encrypted.bytes, &encrypted.key)?;

assert_eq!(&*decrypted, b"example payload");
# Ok::<(), Box<dyn std::error::Error>>(())
```

## Security caveats

Convergent encryption is deterministic by design, which leaks information
that a randomized cipher would not:

- **Plaintext equality**: identical plaintexts encrypt to identical
  ciphertexts, so an observer learns when two stored objects are equal.
- **Confirmation and dictionary attacks**: anyone who can guess a plaintext
  can encrypt the guess and compare ciphertexts, confirming whether it is
  stored. Low-entropy data is therefore not confidential against an
  adversary who can enumerate candidates.
- **Length leakage**: the ciphertext length reveals the compressed size of
  the plaintext, i.e., its length and compressibility.
- **`validate_ecc` is not authentication**: it is an unkeyed checksum that
  detects accidental corruption only, and it returns `false` for
  corrupted-but-correctable data that `decrypt` still accepts. Tampering is
  detected by the Poly1305 tag during `decrypt`, and the decrypted data is
  additionally verified to hash to the decryption key.