Open tarcieri opened 5 years ago
I think that AEAD is a higher level concept than steam ciphers, so it would make sense to include this functionality to the aead
crate.
Another issue is that I have deliberately not impl'd the Mac
trait for Poly1305
, and feel the same way about POLYVAL/GHASH, as these are, at best, "one time authenticators". But we still need something generic to bound on for a generic stream cipher AEAD.
@newpavlov what do you think about adding a trait like OneTimeMac
which a generic stream cipher AEAD can bound on, with a blanket impl for all Mac
types, which can be used to express the idea that things like Poly1305/POLYVAL cannot be reused with the same key?
Ping? I guess this will provide an api which allows encrypting/decrypting "chunk-wise" data? (The current aead api doesn't support this.)
@cynecx AEADs are inherently all-or-nothing by design.
You're probably interested in something like Rogaway's STREAM construction, which we've also discussed adding, and perhaps I can work on soon.
This issue pertains to constructing AEADs from unauthenticated stream ciphers in a generic way, whereas the current implementations all contain some repetitive logic.
Having a single generic core is also a single place to focus on improvements to e.g. buffering strategies.
@tarcieri Ehrm, perhaps my comment is more or less ambiguous. I simply seek a way to do aead with known-sized data, however the environment is resource-constrained (limited ram), so I can't fully load the buffer into memory, so I would like to read the data chunks-wise and encrypt/decrypt accordingly.
pseudocode:
let aead = ChaCha20Poly1305::encrypt(nonce, key);
aead.update(data1); // This encrypts data1 in-place
aead.update(data2);
aead.update(data3); // there are only 3 chuncks, but only one chunk can be loaded into memory at a time.
let tag = aead.finish(); // the aead auth tag
I'd still suggest using STREAM for that. AEADs are only safe if they do not disclosed unauthenticated data. STREAM allows for chunk-wise processing while still ensuring all chunks are authenticated.
@tarcieri Sorry I don’t quite understand. How does encrypting in chunks affect safety? Let’s say I want to encrypt 100 bytes. I could simply use the current api as it is. However because the environment has limited memory I can only load 50 bytes at a time, so I basically have to encrypt/hash the first and second part. I don’t see why I shouldn’t use aead here and how would STREAM help here? because I see it mostly as an api limitation.
Aah, sorry, for some AEAD modes streaming encryption is possible and safe.
The security issue is around streaming decryption and exposing unauthenticated plaintexts before the MAC tag has been checked.
We've three-ish choices for Poly1305
:
Poly1305 : Mac + !MacPlus
,Poly1305 : MacOnce + !Mac
,impl Poly1305 for Mac { const Plus : bool = false; ... }
Associated constants break trait objects completely now, but maybe they'll get fixed eventually, and generic array sucks for trait objects anyways.
The security issue is around streaming decryption and exposing unauthenticated plaintexts before the MAC tag has been checked.
An authentication-only api could help here, so the user can authenticate and then do the decryption/encryption accordingly, however this raises ergonomics issues, like you are required to provide each buffer twice. In terms of efficiency, it would be the same, as the current implementation does two passes anyway.
@tarcieri Wouldn't this also prevent https://github.com/RustCrypto/AEADs/issues/74 from happening?
Doing two passes like that where it can't take ownership of one contiguous buffer carries a risk that an attacker could potentially provide a different ciphertext for the second pass.
Generally I'd consider anything besides an all-or-nothing API for decryption pretty risky.
@tarcieri Yeah, I agree. However, how about we could drop in-place decryption then? I've settled for sodiumoxide's secretstream
for now which supports specifying an output buffer and also supports encrypting and decrypting in chunks (pull_to_vec). The api feels really nice to use and I would hope something similar could land here too.
I discussed secretstream in the post about Rogaway's STREAM I linked earlier. They accomplish the same goals, but secretstream imposes its own bespoke message framing, and unlike Rogaway's STREAM doesn't have security proofs.
I agree with Tony that AEADs need all-of-nothing APIs. We're talking about 16 byte MACs in the case of Poly1305, and never more than 32 bytes, so you can afford one tag per chunk even under rather tight memory constraints. Also, your computation time depends primarily upon the total message length, not the number of tags.
There are scenarios in which MACs to not work, like fitting data into the expected disk sector size, or 512 byte Tor frames, but normally you want wide block ciphers for this, quite a different construction.
Continuing the discussion from https://github.com/RustCrypto/AEADs/pull/3#issuecomment-523557454
It'd be good to have traits for implementing AEADs based on stream ciphers. The
aead
crate presents an interface which is generic enough to incorporate the one AEAD mode based on a block cipher, but most useful AEADs are based on stream ciphers, so it'd be nice to have trait(s) with blanket impls forStatelessAead
which support generic composition based on thestream-cipher
traits.I'm not sure what crate these belong in. Should the
aead
crate have astream-cipher
feature? Or should thestream-cipher
crate have anaead
feature?