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
Add AES CTR, CBC, GCM #35
Conversation
}; | ||
use rand::{rngs::StdRng, SeedableRng}; | ||
|
||
#[test] |
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.
can we also add wycheproof
tests for completeness?
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 doesn't seem to be wycheproof tests for any of the modes I've added so far. There's only tests for authenticated encryption, but I think it would be a good idea to add those along with some wycheproof tests.
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.
Another general comment is if we did some digging and comparison of the various available AES implementations, as one of the things we want to advertise is faster crypto. The goal of this repo is to pick the fastest possible (but safe) Rust impl and wrap or augment them.
I did some research on it, and there doesn't seem to be a lot of options. The RustCrypto modes seem to be by far the most used and best supported. However, I did look at https://github.com/keepsimple1/libaes which has a benchmark showing that it's about 3-4 times faster than RustCrypto for CBC. I tried it with the most recent version of RustCrypto, and the benchmark shows the same, so we could use this lib at least for the modes it supports. |
src/traits.rs
Outdated
/// | ||
pub trait Cipher { | ||
/// Encrypt `plaintext` and write result to `buffer` | ||
fn encrypt_b2b(&self, plaintext: &[u8], buffer: &mut [u8]) -> Result<(), 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.
Wondering if we should rename these to just encrypt
and decrypt
Cargo.toml
Outdated
aes = "0.8.1" | ||
ctr = "0.9.1" | ||
cbc = "0.1.2" | ||
cfb-mode = "0.8.1" |
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 think we could retire cfb for now, and replace it with GCM which is the most popular mode for authenticated encryption (and for which there is a wycheproof test).
All in all, we can start with CTR, CBC, GCM for which we know of Sui applications already
src/aes.rs
Outdated
/// Aes128 in GCM mode (Authenticated) | ||
/// | ||
pub struct Aes128Gcm<'a, NonceSize: ArrayLength<u8>> { | ||
pub iv: &'a GenericArray<u8, NonceSize>, |
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.
qq: how about having a similar iv
field type (bytearray), to have consistency with the other schemes?
} | ||
|
||
#[test] | ||
fn wycheproof_test() { |
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.
<3
src/traits.rs
Outdated
/// | ||
pub trait Cipher { | ||
/// Encrypt `plaintext` and write result to `buffer` using the given IV | ||
fn encrypt(&self, iv: &[u8], plaintext: &[u8], buffer: &mut [u8]) -> Result<(), 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.
Wondering if we should hide this from the end-user buffer: &mut [u8])
, by experiencer it's very common to make mistakes here or be inconvenient, ie by providing wrong size input buffers, even the fact to be prepared to have an existing buffer already. Wdyt?
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.
That makes sense. I just copied the interface from rustcrypto, assuming that was the common way to do it in rust. I think we should return a new array with the result then.
src/aes.rs
Outdated
{ | ||
fn encrypt( | ||
&self, | ||
iv: &[u8], |
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.
A few questions:
- do we already check iv size mismatch?
- we handle the AES
Key
as a special struct, what about theIV
? maybe for consistency we should as well. Not strongly opinionated on that. - For all of the encrypt/decrypt and their authenticated variants, let's have some tests on input [u8] sizes mismatch.
src/aes.rs
Outdated
/// | ||
#[serde_as] | ||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct Key<const N: usize> { |
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.
Similarly to what we do for other schemes (i.e., for ed25519), let's rename this to AesKey
to avoid any future readability confusion against future apis ie HMAC keys etc.
src/aes.rs
Outdated
#[serde_as] | ||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct Key<const N: usize> { | ||
// This is needed to derive Serialize and Deserialize since serde does not support derive for arrays |
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.
cc: @huitseeker to sign off the idiomatic way
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.
Yep, this is good.
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.
Just food for thought: there may be a way to express a size floor on Key<N>
by requiring a MinSlice<_; 16>
or some such appropriate projection to a slice of a minimum length (ref)
In the old days, before const generics, we used to do it with something like https://crates.io/crates/generic-bytes
src/aes.rs
Outdated
#[serde_as] | ||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct Key<const N: usize> { | ||
// This is needed to derive Serialize and Deserialize since serde does not support derive for arrays |
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.
Yep, this is good.
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 guess the only remaining feature is IV to be in its own struct, right?
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.
Awesome! LGTM
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.
Thanks for merging! We generally prefer to squash and merge to avoid inflating our commit log and have all main
commits in a stable state just in case we ever need to cherry pick, go back to specific commits etc.
That makes sense. I guess it doesn't make sense to revert the merge, because the commits will still be in the log, but I'll make sure to do that next time. |
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.
Yes, we're good with this PR merge, no need to revert.
Added support for AES with 128 and 256 bit keys using either CTR, CBC, GCM with PKCS#7 padding. All modes are backed by crates from RustCrypto (https://github.com/RustCrypto/block-modes).