Skip to content
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

Merged
merged 30 commits into from Sep 16, 2022
Merged

Add AES CTR, CBC, GCM #35

merged 30 commits into from Sep 16, 2022

Conversation

jonas-lj
Copy link
Contributor

@jonas-lj jonas-lj commented Sep 9, 2022

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

};
use rand::{rngs::StdRng, SeedableRng};

#[test]
Copy link
Collaborator

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?

Copy link
Contributor Author

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.

Copy link
Collaborator

@kchalkias kchalkias left a 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.

@jonas-lj
Copy link
Contributor Author

jonas-lj commented Sep 9, 2022

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>;
Copy link
Collaborator

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"
Copy link
Collaborator

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>,
Copy link
Collaborator

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?

@jonas-lj jonas-lj marked this pull request as draft September 12, 2022 21:21
}

#[test]
fn wycheproof_test() {
Copy link
Collaborator

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>;
Copy link
Collaborator

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?

Copy link
Contributor Author

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],
Copy link
Collaborator

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 the IV? 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> {
Copy link
Collaborator

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.

@kchalkias kchalkias changed the title Add AES Add AES CTR, CBC, GCM Sep 13, 2022
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
Copy link
Collaborator

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, this is good.

Copy link
Contributor

@huitseeker huitseeker left a 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, this is good.

Copy link
Collaborator

@kchalkias kchalkias left a 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?

@jonas-lj jonas-lj marked this pull request as ready for review September 15, 2022 09:48
Copy link
Collaborator

@kchalkias kchalkias left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome! LGTM

@jonas-lj jonas-lj merged commit 41f5010 into main Sep 16, 2022
@jonas-lj jonas-lj deleted the aes branch September 16, 2022 07:19
Copy link
Collaborator

@kchalkias kchalkias left a 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.

@jonas-lj
Copy link
Contributor Author

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.

Copy link
Collaborator

@kchalkias kchalkias left a 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants