Add Rust bindings to AES-GCM through the EVP_AEAD_* APIs Change-Id: I295b0142b4448a5ee10ca9b092a2c3eaa1fffc86 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60405 Reviewed-by: David Benjamin <davidben@google.com> Reviewed-by: Bob Beck <bbe@google.com> Commit-Queue: Bob Beck <bbe@google.com>
diff --git a/rust/bssl-crypto/src/aead.rs b/rust/bssl-crypto/src/aead.rs index 324873b..4cab452 100644 --- a/rust/bssl-crypto/src/aead.rs +++ b/rust/bssl-crypto/src/aead.rs
@@ -66,11 +66,6 @@ } } -// Private implementation of an AEAD which is generic over Nonce size and Tag size. This should -// only be exposed publicly by wrapper types which provide the correctly sized const generics for -// the given aead algorithm. -struct AeadImpl<const N: usize, const T: usize>(*mut EVP_AEAD_CTX); - trait EvpAeadType { type Key: AsRef<[u8]>; fn evp_aead() -> *const EVP_AEAD; @@ -98,6 +93,59 @@ } } +/// AES-GCM implementation. +pub struct AesGcm(AeadImpl<12, 16>); + +/// Instantiates a new AES-128-GCM instance from key material. +pub fn new_aes_128_gcm(key: &[u8; 16]) -> AesGcm { + AesGcm(AeadImpl::new::<EvpAes128Gcm>(key)) +} + +/// Instantiates a new AES-256-GCM instance from key material. +pub fn new_aes_256_gcm(key: &[u8; 32]) -> AesGcm { + AesGcm(AeadImpl::new::<EvpAes256Gcm>(key)) +} + +impl Aead for AesGcm { + const TAG_SIZE: usize = 16; + type Nonce = [u8; 12]; + + fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; 12]) -> Result<Vec<u8>, AeadError> { + self.0.encrypt(msg, aad, nonce) + } + + fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; 12]) -> Result<Vec<u8>, AeadError> { + self.0.decrypt(msg, aad, nonce) + } +} + +struct EvpAes128Gcm; +impl EvpAeadType for EvpAes128Gcm { + type Key = [u8; 16]; + + fn evp_aead() -> *const EVP_AEAD { + // Safety: + // - this just returns a constant value + unsafe { bssl_sys::EVP_aead_aes_128_gcm() } + } +} + +struct EvpAes256Gcm; +impl EvpAeadType for EvpAes256Gcm { + type Key = [u8; 32]; + + fn evp_aead() -> *const EVP_AEAD { + // Safety: + // - this just returns a constant value + unsafe { bssl_sys::EVP_aead_aes_256_gcm() } + } +} + +// Private implementation of an AEAD which is generic over Nonce size and Tag size. This should +// only be exposed publicly by wrapper types which provide the correctly sized const generics for +// the given aead algorithm. +struct AeadImpl<const N: usize, const T: usize>(*mut EVP_AEAD_CTX); + impl<const N: usize, const T: usize> AeadImpl<N, T> { // Create a new AeadImpl instance from key material and for a supported AeadType. fn new<A: EvpAeadType>(key: &A::Key) -> Self { @@ -157,6 +205,9 @@ // Decrypts msg in-place, on success msg will contain the plain text alone, without the auth // tag. fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; N]) -> Result<Vec<u8>, AeadError> { + if msg.len() < T { + return Err(AeadError); + } let mut out = Vec::new(); out.resize(msg.len() - T, 0u8); @@ -192,6 +243,14 @@ } } +impl<const N: usize, const T: usize> Drop for AeadImpl<N, T> { + fn drop(&mut self) { + // Safety: + // - `self.0` was allocated by `EVP_AEAD_CTX_new` and has not yet been freed. + unsafe { bssl_sys::EVP_AEAD_CTX_free(self.0) } + } +} + #[cfg(test)] mod test { use super::*; @@ -285,4 +344,78 @@ assert!(result.is_ok()); assert_eq!(&result.unwrap(), &msg); } + + #[test] + fn aes_128_gcm_tests() { + // TC 1 from crypto/cipher_extra/test/aes_128_gcm_tests.txt + let key = decode_hex("d480429666d48b400633921c5407d1d1"); + let nonce = decode_hex("3388c676dc754acfa66e172a"); + let tag: [u8; 16] = decode_hex("7d7daf44850921a34e636b01adeb104f"); + let mut buf = Vec::from(&[] as &[u8]); + let aes = new_aes_128_gcm(&key); + let result = aes.encrypt(&mut buf, b"", &nonce); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), &tag); + + // TC2 + let key = decode_hex("3881e7be1bb3bbcaff20bdb78e5d1b67"); + let nonce = decode_hex("dcf5b7ae2d7552e2297fcfa9"); + let msg: [u8; 5] = decode_hex("0a2714aa7d"); + let ad: [u8; 5] = decode_hex("c60c64bbf7"); + let ct: [u8; 5] = decode_hex("5626f96ecb"); + let tag: [u8; 16] = decode_hex("ff4c4f1d92b0abb1d0820833d9eb83c7"); + + let mut buf = Vec::from(msg.as_slice()); + let aes = new_aes_128_gcm(&key); + let result = aes.encrypt(&mut buf, &ad, &nonce); + assert!(result.is_ok()); + let mut data = result.unwrap(); + assert_eq!(&data[..5], &ct); + assert_eq!(&data[5..], &tag); + let result = aes.decrypt(data.as_mut_slice(), &ad, &nonce); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), &msg); + } + + #[test] + fn aes_256_gcm_tests() { + // TC 1 from crypto/cipher_extra/test/aes_256_gcm_tests.txt + let key = decode_hex("e5ac4a32c67e425ac4b143c83c6f161312a97d88d634afdf9f4da5bd35223f01"); + let nonce = decode_hex("5bf11a0951f0bfc7ea5c9e58"); + let tag: [u8; 16] = decode_hex("d7cba289d6d19a5af45dc13857016bac"); + let mut buf = Vec::from(&[] as &[u8]); + let aes = new_aes_256_gcm(&key); + let result = aes.encrypt(&mut buf, b"", &nonce); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), &tag); + + // TC2 + let key = decode_hex("73ad7bbbbc640c845a150f67d058b279849370cd2c1f3c67c4dd6c869213e13a"); + let nonce = decode_hex("a330a184fc245812f4820caa"); + let msg: [u8; 5] = decode_hex("f0535fe211"); + let ad: [u8; 5] = decode_hex("e91428be04"); + let ct: [u8; 5] = decode_hex("e9b8a896da"); + let tag: [u8; 16] = decode_hex("9115ed79f26a030c14947b3e454db9e7"); + + let mut buf = Vec::from(msg.as_slice()); + let aes = new_aes_256_gcm(&key); + let result = aes.encrypt(&mut buf, &ad, &nonce); + assert!(result.is_ok()); + let mut data = result.unwrap(); + assert_eq!(&data[..5], &ct); + assert_eq!(&data[5..], &tag); + let result = aes.decrypt(data.as_mut_slice(), &ad, &nonce); + assert!(result.is_ok()); + assert_eq!(result.unwrap(), &msg); + } + + #[test] + fn test_invalid_data_length_decrypt() { + let key = decode_hex("00000000000000000000000000000000"); + let nonce = decode_hex("000000000000000000000000"); + let buf = Vec::from(&[] as &[u8]); + let aes = new_aes_128_gcm_siv(&key); + let result = aes.decrypt(&buf, b"", &nonce); + assert!(result.is_err()); + } }