RustCrypto / traits

Collection of cryptography-related traits
555 stars 178 forks source link

aead: generalization of AEADs based on stream ciphers #45

Open tarcieri opened 5 years ago

tarcieri commented 5 years ago

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 for StatelessAead which support generic composition based on the stream-cipher traits.

I'm not sure what crate these belong in. Should the aead crate have a stream-cipher feature? Or should the stream-cipher crate have an aead feature?

newpavlov commented 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.

tarcieri commented 5 years ago

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?

cynecx commented 4 years ago

Ping? I guess this will provide an api which allows encrypting/decrypting "chunk-wise" data? (The current aead api doesn't support this.)

tarcieri commented 4 years ago

@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.

cynecx commented 4 years ago

@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
tarcieri commented 4 years ago

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.

cynecx commented 4 years ago

@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.

tarcieri commented 4 years ago

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.

burdges commented 4 years ago

We've three-ish choices for Poly1305:

Associated constants break trait objects completely now, but maybe they'll get fixed eventually, and generic array sucks for trait objects anyways.

cynecx commented 4 years ago

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?

tarcieri commented 4 years ago

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.

cynecx commented 4 years ago

@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.

tarcieri commented 4 years ago

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.

burdges commented 4 years ago

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.