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());
+ }
}