Add rust bindings to AES-GCM-SIV through the EVP_AEAD_* API's

Implemented a generic Aead trait and struct against the EVP_AEAD
API's, which can be used to provide bindings to all of the AEAD's
provided by boringssl. Starting with AES_GCM_SIV, but will expand
to more AEAD's.

Change-Id: I7d4113f3d49ff40de3ccb76424f9a25d25797e82
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/59965
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
new file mode 100644
index 0000000..324873b
--- /dev/null
+++ b/rust/bssl-crypto/src/aead.rs
@@ -0,0 +1,288 @@
+/* Copyright (c) 2023, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+use crate::{CSlice, CSliceMut};
+use bssl_sys::{EVP_AEAD, EVP_AEAD_CTX};
+
+/// Error returned in the event of an unsuccessful AEAD operation.
+#[derive(Debug)]
+pub struct AeadError;
+
+/// Authenticated Encryption with Associated Data (AEAD) algorithm trait.
+pub trait Aead {
+    /// The size of the auth tag for the given AEAD implementation. This is the amount of bytes
+    /// appended to the data when it is encrypted.
+    const TAG_SIZE: usize;
+
+    /// The byte array nonce type which specifies the size of the nonce used in the aes operations.
+    type Nonce: AsRef<[u8]>;
+
+    /// Encrypt the given buffer containing a plaintext message. On success returns the encrypted
+    /// `msg` and appended auth tag, which will result in a Vec which is  `Self::TAG_SIZE` bytes
+    /// greater than the initial message.
+    fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError>;
+
+    /// Decrypt the message, returning the decrypted plaintext or an error in the event the
+    /// provided authentication tag does not match the given ciphertext. On success the returned
+    /// Vec will only contain the plaintext and so will be `Self::TAG_SIZE` bytes less than the
+    /// initial message.
+    fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError>;
+}
+
+/// AES-GCM-SIV implementation.
+pub struct AesGcmSiv(AeadImpl<12, 16>);
+
+/// Instantiates a new AES-128-GCM-SIV instance from key material.
+pub fn new_aes_128_gcm_siv(key: &[u8; 16]) -> AesGcmSiv {
+    AesGcmSiv(AeadImpl::new::<EvpAes128GcmSiv>(key))
+}
+
+/// Instantiates a new AES-256-GCM-SIV instance from key material.
+pub fn new_aes_256_gcm_siv(key: &[u8; 32]) -> AesGcmSiv {
+    AesGcmSiv(AeadImpl::new::<EvpAes256GcmSiv>(key))
+}
+
+impl Aead for AesGcmSiv {
+    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)
+    }
+}
+
+// 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;
+}
+
+struct EvpAes128GcmSiv;
+impl EvpAeadType for EvpAes128GcmSiv {
+    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_siv() }
+    }
+}
+
+struct EvpAes256GcmSiv;
+impl EvpAeadType for EvpAes256GcmSiv {
+    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_siv() }
+    }
+}
+
+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 {
+        let key_cslice = CSlice::from(key.as_ref());
+
+        // Safety:
+        // - This is always safe as long as the correct key size is set by the wrapper type.
+        let ctx = unsafe {
+            bssl_sys::EVP_AEAD_CTX_new(
+                A::evp_aead(),
+                key_cslice.as_ptr(),
+                key_cslice.len(),
+                bssl_sys::EVP_AEAD_DEFAULT_TAG_LENGTH as usize,
+            )
+        };
+        assert!(!ctx.is_null());
+        AeadImpl(ctx)
+    }
+
+    // Encrypts msg in-place, adding enough space to msg for the auth tag.
+    fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; N]) -> Result<Vec<u8>, AeadError> {
+        let mut out = Vec::new();
+        out.resize(msg.len() + T, 0u8);
+
+        let mut out_cslice = CSliceMut::from(out.as_mut_slice());
+        let msg_cslice = CSlice::from(msg);
+        let aad_cslice = CSlice::from(aad);
+        let nonce_cslice = CSlice::from(nonce.as_slice());
+        let mut out_len = 0usize;
+
+        // Safety:
+        // - The buffers are all valid, with corresponding ptr and length
+        let result = unsafe {
+            bssl_sys::EVP_AEAD_CTX_seal(
+                self.0,
+                out_cslice.as_mut_ptr(),
+                &mut out_len,
+                out_cslice.len(),
+                nonce_cslice.as_ptr(),
+                nonce_cslice.len(),
+                msg_cslice.as_ptr(),
+                msg_cslice.len(),
+                aad_cslice.as_ptr(),
+                aad_cslice.len(),
+            )
+        };
+
+        if result == 1 {
+            // Verify the correct number of bytes were written.
+            assert_eq!(out_len, out.len());
+            Ok(out)
+        } else {
+            Err(AeadError)
+        }
+    }
+
+    // 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> {
+        let mut out = Vec::new();
+        out.resize(msg.len() - T, 0u8);
+
+        let mut out_cslice = CSliceMut::from(out.as_mut_slice());
+        let aad_cslice = CSlice::from(aad);
+        let msg_cslice = CSlice::from(msg);
+        let mut out_len = 0usize;
+
+        // Safety:
+        // - The buffers are all valid, with corresponding ptr and length
+        let result = unsafe {
+            bssl_sys::EVP_AEAD_CTX_open(
+                self.0,
+                out_cslice.as_mut_ptr(),
+                &mut out_len,
+                out_cslice.len(),
+                nonce.as_ptr(),
+                nonce.len(),
+                msg_cslice.as_ptr(),
+                msg_cslice.len(),
+                aad_cslice.as_ptr(),
+                aad_cslice.len(),
+            )
+        };
+
+        if result == 1 {
+            // Verify the correct number of bytes were written.
+            assert_eq!(out_len, out.len());
+            Ok(out)
+        } else {
+            Err(AeadError)
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_helpers::decode_hex;
+
+    #[test]
+    fn aes_128_gcm_siv_tests() {
+        // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
+        // TC1 - Empty Message
+        let key = decode_hex("01000000000000000000000000000000");
+        let nonce = decode_hex("030000000000000000000000");
+        let tag: [u8; 16] = decode_hex("dc20e2d83f25705bb49e439eca56de25");
+        let mut buf = Vec::from(&[] as &[u8]);
+        let aes = new_aes_128_gcm_siv(&key);
+        let result = aes.encrypt(&mut buf, b"", &nonce);
+        assert!(result.is_ok());
+        assert_eq!(result.unwrap(), &tag);
+
+        // TC2
+        let msg: [u8; 8] = decode_hex("0100000000000000");
+        let ct: [u8; 8] = decode_hex("b5d839330ac7b786");
+        let tag: [u8; 16] = decode_hex("578782fff6013b815b287c22493a364c");
+        let result = aes.encrypt(&msg, b"", &nonce);
+        assert!(result.is_ok());
+        let mut result_vec = result.unwrap();
+        assert_eq!(&result_vec[..8], &ct);
+        assert_eq!(&result_vec[8..], &tag);
+        let result = aes.decrypt(result_vec.as_mut_slice(), b"", &nonce);
+        assert!(result.is_ok());
+        assert_eq!(&result.unwrap(), &msg);
+
+        // TC14 contains associated data
+        let msg: [u8; 4] = decode_hex("02000000");
+        let ct: [u8; 4] = decode_hex("a8fe3e87");
+        let aad: [u8; 12] = decode_hex("010000000000000000000000");
+        let tag: [u8; 16] = decode_hex("07eb1f84fb28f8cb73de8e99e2f48a14");
+        let result = aes.encrypt(&msg, &aad, &nonce);
+        assert!(result.is_ok());
+        let mut result_vec = result.unwrap();
+        assert_eq!(&result_vec[..4], &ct);
+        assert_eq!(&result_vec[4..], &tag);
+        let result = aes.decrypt(result_vec.as_mut_slice(), &aad, &nonce);
+        assert!(result.is_ok());
+        assert_eq!(&result.unwrap(), &msg);
+    }
+
+    #[test]
+    fn aes_256_gcm_siv_tests() {
+        // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json
+        // TC77
+        let test_key =
+            decode_hex("0100000000000000000000000000000000000000000000000000000000000000");
+        let nonce = decode_hex("030000000000000000000000");
+        let aes = new_aes_256_gcm_siv(&test_key);
+        let mut msg: [u8; 8] = decode_hex("0100000000000000");
+        let ct: [u8; 8] = decode_hex("c2ef328e5c71c83b");
+        let tag: [u8; 16] = decode_hex("843122130f7364b761e0b97427e3df28");
+        let enc_result = aes.encrypt(&mut msg, b"", &nonce);
+        assert!(enc_result.is_ok());
+        let mut enc_data = enc_result.unwrap();
+        assert_eq!(&enc_data[..8], &ct);
+        assert_eq!(&enc_data[8..], &tag);
+        let result = aes.decrypt(enc_data.as_mut_slice(), b"", &nonce);
+        assert!(result.is_ok());
+        assert_eq!(&result.unwrap(), &msg);
+
+        // TC78
+        let mut msg: [u8; 12] = decode_hex("010000000000000000000000");
+        let ct: [u8; 12] = decode_hex("9aab2aeb3faa0a34aea8e2b1");
+        let tag: [u8; 16] = decode_hex("8ca50da9ae6559e48fd10f6e5c9ca17e");
+        let enc_result = aes.encrypt(&mut msg, b"", &nonce);
+        assert!(enc_result.is_ok());
+        let mut enc_data = enc_result.unwrap();
+        assert_eq!(&enc_data[..12], &ct);
+        assert_eq!(&enc_data[12..], &tag);
+        let result = aes.decrypt(enc_data.as_mut_slice(), b"", &nonce);
+        assert!(result.is_ok());
+        assert_eq!(&result.unwrap(), &msg);
+
+        // TC89 contains associated data
+        let mut msg: [u8; 4] = decode_hex("02000000");
+        let ct: [u8; 4] = decode_hex("22b3f4cd");
+        let tag: [u8; 16] = decode_hex("1835e517741dfddccfa07fa4661b74cf");
+        let aad: [u8; 12] = decode_hex("010000000000000000000000");
+        let enc_result = aes.encrypt(&mut msg, &aad, &nonce);
+        assert!(enc_result.is_ok());
+        let mut enc_data = enc_result.unwrap();
+        assert_eq!(&enc_data[..4], &ct);
+        assert_eq!(&enc_data[4..], &tag);
+        let result = aes.decrypt(enc_data.as_mut_slice(), &aad, &nonce);
+        assert!(result.is_ok());
+        assert_eq!(&result.unwrap(), &msg);
+    }
+}
diff --git a/rust/bssl-crypto/src/lib.rs b/rust/bssl-crypto/src/lib.rs
index f4d1291..c538467 100644
--- a/rust/bssl-crypto/src/lib.rs
+++ b/rust/bssl-crypto/src/lib.rs
@@ -26,6 +26,9 @@
 
 extern crate core;
 
+/// Authenticated Encryption with Additional Data algorithms.
+pub mod aead;
+
 /// AES block operations.
 pub mod aes;
 
@@ -44,7 +47,7 @@
 /// Random number generation.
 pub mod rand;
 
-/// BoringSSL implemented memory-manipulation operations.
+/// Memory-manipulation operations.
 pub mod mem;
 
 #[cfg(test)]