ps-cypher
Convergent encryption with ChaCha20-Poly1305, zstd, and Reed-Solomon ECC.
Scheme
- The encryption key is the hash of the plaintext (
ps-hash): theChaCha20key 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. - A four-byte XXH64 tag of the ECC codeword is appended, so
validatecan recognise a pristine ps-cypher codeword without the key and tell it apart from other ps-ecc codewords. - Identical inputs therefore always produce identical ciphertexts, keys, and hashes, which enables content-addressed deduplication of encrypted data.
decryptre-hashes the decrypted data and fails withKeyMismatchunless it hashes to the decryption key, so a successful decryption returns exactly the data that produced the key.
Example
use ;
let encrypted = encrypt?;
let decrypted = decrypt?;
assert_eq!;
# Ok::
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.
validateis not authentication: the tag is an unkeyed checksum, soValidity::Pristinemeans "unmodified sinceencrypt", not "produced by a trusted party".decryptignores the tag entirely. Tampering is detected by the Poly1305 tag duringdecrypt, and the decrypted data is additionally verified to hash to the decryption key.