cAEAD - Committing AEAD
Committing AEAD with ChaCha20 and BLAKE3.
Warning: This is an experiment created by Soatok for fun. Don't use it.
Make sure you read the blog post that accompanies this repository.
What does this do?
This implements an RKR-secure alternative to XChaCha20-Poly1305, for use in protocols that require RKR security (i.e. OPAQUE). The primitives used (ChaCha20, BLAKE3) are secure and constant-time in software.
Although large nonces (32 bytes) are employed by this construction, it is not strictly speaking nonce misuse resistant.
If you reuse a (nonce, key) tuple with two different messages, attackers will learn the XOR of the two plaintexts.
(We're using the IETF variant of ChaCha20 with 96-bit nonces and 32-bit counters.)
How to Test this Code
git clone https://github.com/soatok/experimental-caead
cd js
npm install
npm test
Algorithm Definition
Notation
| Symbol | Meaning |
|---|---|
:= |
Assignment (store right-side value in left-side variable) |
|| |
Concatenation |
var[x:y] |
Slice var from index x to y |
Constants
Algorithm prefix: CRYPTO_CAEAD_CHACHA20BLAKE3_
DOMAIN_ENCRYPT := "Soatok01"
DOMAIN_AUTH := "Soatok}~"
NONCE_BYTES := 32
KEY_BYTES := 32
TAG_BYTES := 32
Encryption Algorithm
-
Split the key into an encryption key and an authentication key.
encKey := BLAKE3.keyedHash(key, DOMAIN_ENCRYPT || nonce[0:19]) authKey := BLAKE3.keyedHash(key, DOMAIN_AUTH || nonce[0:19]) -
Encrypt the message:
C := ChaCha20.encrypt(plaintext, nonce[20:31], encKey, block_counter = 0) -
Calculate the authentication tag:
T := BLAKE3.keyedHash(authKey, aad || C || STORE64LE(aad.length) || STORE64LE(C.length)) -
Return
T || C
Decryption Algorithm
-
Split the key into an encryption key and an authentication key.
encKey := BLAKE3.keyedHash(key, DOMAIN_ENCRYPT || nonce[0:19]) authKey := BLAKE3.keyedHash(key, DOMAIN_AUTH || nonce[0:19]) -
Realculate the authentication tag:
T' := BLAKE3.keyedHash(authKey, aad || C || STORE64LE(aad.length) || STORE64LE(C.length)) -
Compare T with T' in constant-time. If it fails, abort.
-
Decrypt the message:
P := ChaCha20.decrypt(C, nonce[20:31], encKey, block_counter = 0) -
Return the decrypted plaintext.
