-
Notifications
You must be signed in to change notification settings - Fork 216
aead: stream
module
#436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
aead: stream
module
#436
Conversation
Note: I left allocating APIs out of this PR as well to make it easier to review, but they're easily added in a follow-up. |
874b73e
to
1c1bfb9
Compare
aead/src/stream.rs
Outdated
if self.position == S::COUNTER_MAX { | ||
// Counter overflow | ||
return Err(Error); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this implementation precludes calling encrypt_next_in_place
/decrypt_next_in_place
with the maximum counter value.
That's deliberate: it ensures any segment encrypted under the maximum counter value MUST have the "last block" flag set.
#[doc = $obj_desc] | ||
#[doc = "object in order to prevent further use."] | ||
pub fn $last_method( | ||
self, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consuming self
here is annoying in async encryptors, where we may need to leave poll_close
after encrypting the last chunk but before we've finished writing the encrypted chunk. It can be managed by storing an Option<aead::stream::Encryptor>
, but it's a bit of a hassle.
More problematic is that consuming self
here is incompatible with seeking decryptors, as we may need to decrypt the last chunk, read part of it, then seek back earlier than the last chunk. Decryptor
doesn't implement Clone
, so we can't clone before consuming. Instead, someone trying to implement Seek
would need to save the key and nonce themselves, and then reconstruct the Decryptor
every time a seek is requested.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Encryptor
and Decryptor
objects are high-level misuse resistant APIs designed to ensure the STREAM is encoded/decoded correctly.
The lower-level StreamPrimitive
trait is intended for the use cases you're describing.
stream: S, | ||
|
||
/// Current position in the STREAM. | ||
position: S::Counter, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is currently no way to set position
manually to a specific chunk, which completely prevents Decryptor
from being used in a seeking context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried migrating the age
crate to this (which uses ChaCha20Poly1305 with a nonce structured as an 11-byte big-endian counter and 1-byte last block flag), and encountered a few issues.
@str4d I think for something like They could potentially include seeking behavior similar to SyncStreamCipherSeek, but if you are intending to do anything in parallel the fact they keep state at all seems problematic and I think you should just use |
As I noted in Discord, If we could move (what is currently) |
@str4d it might make sense to spike things out in another crate initially, then circle back on upstreaming the parts that make sense into |
Implementation of the STREAM construction as described in the paper "Online Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance": https://eprint.iacr.org/2015/189.pdf The implementation is generic over AEAD ciphers and is factored into a low-level `StreamPrimitive` trait (permitting different "flavors" of STREAM) as well as higher-level stateful `Encryptor` and `Decryptor` objects which are generic over `StreamPrimitive` types. Includes two concrete implementations of `StreamPrimitive`: - `StreamBE32`: the original version of stream described in the paper, with a nonce in the form: `prefix || counter || last_block`, where `counter` is a 32-bit big endian-encoded integer, and `last_block` is a 1-byte flag. - `StreamLE31`: uses a 31-bit counter and 1-bit last block flag, packed into the last 4 bytes of the nonce as a little endian integer. Using little endian provides better performance on commonly used CPU architectures, and using a 1-bit last block flag ensures the user-facing STREAM nonce is even numbered in terms of bytes (e.g. for a 96-bit nonce it'd be 64-bits or 8-bytes instead of a 7-byte nonce using the construction described in the paper) and also avoids wasting bits.
Added a I'm wondering if we might reduce That would reduce duplication of code between |
I'm going to go ahead and land this as I think a I plan on submitting a follow-up PR to simplify the API as described above. I'm also not in a rush to cut another release of |
Implementation of the STREAM construction as described in the paper "Online Authenticated-Encryption and its Nonce-Reuse Misuse-Resistance":
https://eprint.iacr.org/2015/189.pdf
The implementation is generic over AEAD ciphers and is factored into a low-level
StreamPrimitive
trait (permitting different "flavors" of STREAM) as well as higher-level statefulEncryptor
andDecryptor
objects which are generic overStreamPrimitive
types.Includes one concrete implementation of STREAM:
StreamLE31
, which uses a 31-bit counter and 1-bit last block flag. Note that this implementation differs slightly from the one described in the paper, which uses a 1-byte last block flag.Using little endian provides better performance on commonly used CPU architectures, and using a 1-bit last block flag ensures the user-facing STREAM nonce is even numbered in terms of bytes (e.g. for a 96-bit nonce it'd be 64-bits or 8-bytes instead of a 7-byte nonce using the construction described in the paper) and also avoids wasting bits.
It would probably make sense to provide a concrete implementation of STREAM as described in the paper as well, especially for compatibility with existing deployments of this construction, however I wanted to both make sure we provide a useful deployed STREAM variant as such, and also wanted to keep the PR smaller for initial review.