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 Support for CBC, CTR Cipher modes with AES 128 & 256 bit keys. #150

Merged
merged 55 commits into from
Jun 20, 2023

Conversation

skmcgrail
Copy link
Member

@skmcgrail skmcgrail commented Jun 8, 2023

Description of changes:

This pull request adds support for CBC and CTR cipher mode encryption for 128-bit and 256-bit AES keys.

  • Introduces aws_lc_rs::cipher module which supports unauthenticated ciphers for encryption/decryption.
  • PaddedBlockEncryptingKey, PaddedBlockDecryptingKey provide encryption/decryption of plaintext/ciphertext for block cipher modes using padding. PaddedBlockEncryptingKey::cbc_pkcs7(...) PaddedBlockDecryptingKey::cbc_pkcs7(...) and are the constructor methods provided for using CBC mode with PKCS#7 padding.
    • Users who need to explicitly set an initialization vector can use PaddedBlockEncryptingKey::less_safe_cbc_pkcs7.
  • EncryptingKey, DecryptingKey provide encryption/decryption of plaintext/ciphertext for unpadded block cipher modes. PaddedBlockEncryptingKey::ctr(...) PaddedBlockDecryptingKey::ctr(...) and are the constructor methods provided for using CTR mode (unpadded).
    • Users who need to explicitly set an initialization vector can use EncryptingKey::less_safe_ctr.

API Usage Examples

AES-128 CBC

use aws_lc_rs::cipher::{
    PaddedBlockDecryptingKey, PaddedBlockEncryptingKey, UnboundCipherKey, AES_128,
};

let original_message = "This is a secret message!".as_bytes();
let mut in_out_buffer = Vec::from(original_message);

let key_bytes: &[u8] = &[
    0xff, 0x0b, 0xe5, 0x84, 0x64, 0x0b, 0x00, 0xc8, 0x90, 0x7a, 0x4b, 0xbf, 0x82, 0x7c, 0xb6,
    0xd1,
];

let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
let encrypting_key = PaddedBlockEncryptingKey::cbc_pkcs7(key)?;
let context = encrypting_key.encrypt(&mut in_out_buffer)?;

let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
let decrypting_key = PaddedBlockDecryptingKey::cbc_pkcs7(key)?;
let plaintext = decrypting_key.decrypt(&mut in_out_buffer, context)?;
assert_eq!(original_message, plaintext);

AES-128 CTR

use aws_lc_rs::cipher::{DecryptingKey, EncryptingKey, UnboundCipherKey, AES_128};

let original_message = "This is a secret message!".as_bytes();
let mut in_out_buffer = Vec::from(original_message);

let key_bytes: &[u8] = &[
    0xff, 0x0b, 0xe5, 0x84, 0x64, 0x0b, 0x00, 0xc8, 0x90, 0x7a, 0x4b, 0xbf, 0x82, 0x7c, 0xb6,
    0xd1,
];

let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
let encrypting_key = EncryptingKey::ctr(key)?;
let context = encrypting_key.encrypt(&mut in_out_buffer)?;

let key = UnboundCipherKey::new(&AES_128, key_bytes)?;
let decrypting_key = DecryptingKey::ctr(key)?;
let plaintext = decrypting_key.decrypt(&mut in_out_buffer, context)?;
assert_eq!(original_message, plaintext);

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license.

Copy link
Contributor

@samuel40791765 samuel40791765 left a comment

Choose a reason for hiding this comment

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

initialization in initialization vector seems to be misspelled throughout the PR, so we might want to adjust that.

aws-lc-rs/tests/cipher_test.rs Outdated Show resolved Hide resolved
aws-lc-rs/src/cipher.rs Outdated Show resolved Hide resolved
aws-lc-rs/src/cipher.rs Outdated Show resolved Hide resolved
aws-lc-rs/src/cipher.rs Outdated Show resolved Hide resolved
aws-lc-rs/src/cipher/key.rs Show resolved Hide resolved
aws-lc-rs/examples/cipher.rs Outdated Show resolved Hide resolved
aws-lc-rs/examples/cipher.rs Outdated Show resolved Hide resolved
aws-lc-rs/src/cipher/key.rs Outdated Show resolved Hide resolved
aws-lc-rs/src/cipher.rs Show resolved Hide resolved
aws-lc-rs/src/cipher/key.rs Show resolved Hide resolved
aws-lc-rs/src/cipher.rs Show resolved Hide resolved
aws-lc-rs/src/cipher.rs Outdated Show resolved Hide resolved
aws-lc-rs/src/cipher.rs Show resolved Hide resolved
aws-lc-rs/src/cipher.rs Show resolved Hide resolved
aws-lc-rs/src/cipher.rs Show resolved Hide resolved
aws-lc-rs/src/cipher.rs Outdated Show resolved Hide resolved
aws-lc-rs/src/cipher.rs Outdated Show resolved Hide resolved
@skmcgrail
Copy link
Member Author

#155 has some API improvements which will allow reuse of the Encrypt/Decrypt keys so that they aren't consumed by value after calling encrypt/decrypt. Will allow us a path for future optimizations if/when we decide to migrate to the EVP_CIPHER based APIs.

Copy link
Collaborator

@camshaft camshaft left a comment

Choose a reason for hiding this comment

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

I got through everything outside of cipher.rs and cipher/key.rs. I'll need to do the rest tomorrow.

aws-lc-rs/benches/cipher_benchmark.rs Show resolved Hide resolved
aws-lc-rs/benches/cipher_benchmark.rs Outdated Show resolved Hide resolved
aws-lc-rs/src/cipher/key.rs Outdated Show resolved Hide resolved
aws-lc-rs/src/cipher/key.rs Show resolved Hide resolved
aws-lc-rs/src/hkdf.rs Outdated Show resolved Hide resolved
aws-lc-rs/src/iv.rs Show resolved Hide resolved
Comment on lines 15 to 41
let key = from_hex($key).unwrap();
let input = from_hex($plaintext).unwrap();
let expected_ciphertext = from_hex($ciphertext).unwrap();

let iv = from_hex($iv).unwrap();
let fixed_iv = FixedLength::try_from(iv.as_slice()).unwrap();
let context = CipherContext::Iv128(fixed_iv);

let unbound_key = UnboundCipherKey::new($alg, &key).unwrap();

let encrypting_key =
PaddedBlockEncryptingKey::[<less_safe_ $constructor>](unbound_key, context).unwrap();
assert_eq!($mode, encrypting_key.mode());
assert_eq!($padding, encrypting_key.padding());
assert_eq!($alg, encrypting_key.algorithm());
let mut in_out = input.clone();
let context = encrypting_key.encrypt(&mut in_out).unwrap();
assert_eq!(expected_ciphertext.as_slice(), in_out.as_slice());

let unbound_key2 = UnboundCipherKey::new($alg, &key).unwrap();
let decrypting_key =
PaddedBlockDecryptingKey::$constructor(unbound_key2, context).unwrap();
assert_eq!($mode, decrypting_key.mode());
assert_eq!($padding, decrypting_key.padding());
assert_eq!($alg, decrypting_key.algorithm());
let plaintext = decrypting_key.decrypt(&mut in_out).unwrap();
assert_eq!(input.as_slice(), plaintext);
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: it might be worth extracting some of this out into an actual function rather than a macro. It's especially annoying to work with paste! and other tooling like rustfmt or rust-analyzer.

justsmth and others added 4 commits June 15, 2023 11:16
Co-authored-by: Cameron Bytheway <bytheway.cameron@gmail.com>
* CBC/CTR API Improvements
* Cleanup unnecessary paste usage
torben-hansen
torben-hansen previously approved these changes Jun 19, 2023
Copy link
Contributor

@torben-hansen torben-hansen left a comment

Choose a reason for hiding this comment

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

CI looks unhappy. But otherwise, OK from me.

@justsmth
Copy link
Contributor

CI looks unhappy. But otherwise, OK from me.

CI is happy now.

@skmcgrail skmcgrail merged commit 784c0fe into main Jun 20, 2023
@skmcgrail skmcgrail deleted the aes-cbc-ctr branch June 20, 2023 17:21
@skmcgrail skmcgrail restored the aes-cbc-ctr branch June 20, 2023 17:29
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.

5 participants