Libsodiums secretstream is great right up until you need to encrypt a 1 TB file in a browser. It ratchets in order, one chunk at a time, single thread. Not performant enough. And hard to resume if job stops for some reason.
Here's an alternative, and what I'd love torn apart from a security perspective:
Instead of secretstream, each chunk gets sealed on its own:
nonce = fileNonce ‖ u64be(index)
AAD = u64be(index)
subkeys via BLAKE2b("vitalsend:{chunk,meta,header}:v3", key=rootKey)
rootKey = 32-byte link key (lives only in the URL #fragment), or BLAKE2b(linkKey, key=Argon2id(pw)) if there's a password
header is authenticated and pins chunkSize, totalChunks, fileNonce, Argon2id params
My claim: folding the index into both the nonce and the AAD buys the same guarantees secretstream gives minus the in-order bottleneck.
Three questions:
Is index-in-nonce + index-in-AAD really as good as secretstream's chaining here, or is there a case that slips through?
Any nonce footgun at TB scale / huge chunk counts?
Anything in the header or key derivation worth doing differently?
Code, wire format, and a SECURITY.md that's meant to be honest is available for the curious at https://github.com/vitalsend/crypto
P.S. I managed to review the complete code with Fable 5 which did not complain to anything in the area, but that is an LLM.