Add Rust bindings to AES_CTR through EVP_* cipher API's

Change-Id: If9b68dffc801f1d592dd0dff7d4e07fcc5eb76a7
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60445
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: Bob Beck <bbe@google.com>
diff --git a/rust/bssl-crypto/src/cipher/aes_ctr.rs b/rust/bssl-crypto/src/cipher/aes_ctr.rs
new file mode 100644
index 0000000..1375d3e
--- /dev/null
+++ b/rust/bssl-crypto/src/cipher/aes_ctr.rs
@@ -0,0 +1,208 @@
+/* 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::cipher::{Cipher, CipherError, EvpAes128Ctr, EvpAes256Ctr, StreamCipher};
+
+/// AES-CTR-128 Cipher implementation.
+pub struct Aes128Ctr(Cipher<EvpAes128Ctr>);
+
+impl StreamCipher for Aes128Ctr {
+    type Key = [u8; 16];
+    type Nonce = [u8; 16];
+
+    /// Creates a new AES-128-CTR cipher instance from key material.
+    fn new(key: &Self::Key, nonce: &Self::Nonce) -> Self {
+        Self(Cipher::new(key, nonce))
+    }
+
+    /// Applies the keystream in-place, advancing the counter state appropriately.
+    fn apply_keystream(&mut self, buffer: &mut [u8]) -> Result<(), CipherError> {
+        self.0.apply_keystream_in_place(buffer)
+    }
+}
+
+/// AES-CTR-256 Cipher implementation.
+pub struct Aes256Ctr(Cipher<EvpAes256Ctr>);
+
+impl StreamCipher for Aes256Ctr {
+    type Key = [u8; 32];
+    type Nonce = [u8; 16];
+
+    /// Creates a new AES-256-CTR cipher instance from key material.
+    fn new(key: &Self::Key, nonce: &Self::Nonce) -> Self {
+        Self(Cipher::new(key, nonce))
+    }
+
+    /// Applies the keystream in-place, advancing the counter state appropriately.
+    fn apply_keystream(&mut self, buffer: &mut [u8]) -> Result<(), CipherError> {
+        self.0.apply_keystream_in_place(buffer)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use crate::test_helpers::decode_hex;
+
+    #[test]
+    fn aes_128_ctr_test_encrypt() {
+        // https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf F.5.1
+        let iv = decode_hex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
+        let key = decode_hex("2b7e151628aed2a6abf7158809cf4f3c");
+
+        let mut cipher = Aes128Ctr::new(&key, &iv);
+        let mut block: [u8; 16];
+        block = decode_hex("6bc1bee22e409f96e93d7e117393172a");
+
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+
+        let expected_ciphertext_1 = decode_hex("874d6191b620e3261bef6864990db6ce");
+        assert_eq!(expected_ciphertext_1, block);
+
+        block = decode_hex("ae2d8a571e03ac9c9eb76fac45af8e51");
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+        let expected_ciphertext_2 = decode_hex("9806f66b7970fdff8617187bb9fffdff");
+        assert_eq!(expected_ciphertext_2, block);
+
+        block = decode_hex("30c81c46a35ce411e5fbc1191a0a52ef");
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+        let expected_ciphertext_3 = decode_hex("5ae4df3edbd5d35e5b4f09020db03eab");
+        assert_eq!(expected_ciphertext_3, block);
+
+        block = decode_hex("f69f2445df4f9b17ad2b417be66c3710");
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+        let expected_ciphertext_3 = decode_hex("1e031dda2fbe03d1792170a0f3009cee");
+        assert_eq!(expected_ciphertext_3, block);
+    }
+
+    #[test]
+    fn aes_128_ctr_test_decrypt() {
+        // https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf F.5.2
+        let key = decode_hex("2b7e151628aed2a6abf7158809cf4f3c");
+        let iv = decode_hex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
+        let mut cipher = Aes128Ctr::new(&key, &iv);
+
+        let mut block: [u8; 16];
+        block = decode_hex("874d6191b620e3261bef6864990db6ce");
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+        let expected_plaintext_1 = decode_hex("6bc1bee22e409f96e93d7e117393172a");
+        assert_eq!(expected_plaintext_1, block);
+
+        block = decode_hex("9806f66b7970fdff8617187bb9fffdff");
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+        let expected_plaintext_2 = decode_hex("ae2d8a571e03ac9c9eb76fac45af8e51");
+        assert_eq!(expected_plaintext_2, block);
+
+        block = decode_hex("5ae4df3edbd5d35e5b4f09020db03eab");
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+        let expected_plaintext_3 = decode_hex("30c81c46a35ce411e5fbc1191a0a52ef");
+        assert_eq!(expected_plaintext_3, block);
+
+        block = decode_hex("1e031dda2fbe03d1792170a0f3009cee");
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+        let expected_plaintext_3 = decode_hex("f69f2445df4f9b17ad2b417be66c3710");
+        assert_eq!(expected_plaintext_3, block);
+    }
+
+    #[test]
+    pub fn aes_256_ctr_test_encrypt() {
+        // https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf F.5.5
+        let key = decode_hex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
+        let iv = decode_hex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
+        let mut block: [u8; 16];
+        let mut cipher = Aes256Ctr::new(&key, &iv);
+
+        block = decode_hex("6bc1bee22e409f96e93d7e117393172a");
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+        let expected_ciphertext_1 = decode_hex("601ec313775789a5b7a7f504bbf3d228");
+        assert_eq!(expected_ciphertext_1, block);
+
+        block = decode_hex("ae2d8a571e03ac9c9eb76fac45af8e51");
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+        let expected_ciphertext_2 = decode_hex("f443e3ca4d62b59aca84e990cacaf5c5");
+        assert_eq!(expected_ciphertext_2, block);
+
+        block = decode_hex("30c81c46a35ce411e5fbc1191a0a52ef");
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+        let expected_ciphertext_3 = decode_hex("2b0930daa23de94ce87017ba2d84988d");
+        assert_eq!(expected_ciphertext_3, block);
+
+        block = decode_hex("f69f2445df4f9b17ad2b417be66c3710");
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+        let expected_ciphertext_3 = decode_hex("dfc9c58db67aada613c2dd08457941a6");
+        assert_eq!(expected_ciphertext_3, block);
+    }
+
+    #[test]
+    fn aes_256_ctr_test_decrypt() {
+        // https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38a.pdf F.5.6
+        let key = decode_hex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4");
+        let iv = decode_hex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff");
+        let mut cipher = Aes256Ctr::new(&key, &iv);
+
+        let mut block: [u8; 16];
+        block = decode_hex("601ec313775789a5b7a7f504bbf3d228");
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+        let expected_plaintext_1 = decode_hex("6bc1bee22e409f96e93d7e117393172a");
+        assert_eq!(expected_plaintext_1, block);
+
+        block = decode_hex("f443e3ca4d62b59aca84e990cacaf5c5");
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+        let expected_plaintext_2 = decode_hex("ae2d8a571e03ac9c9eb76fac45af8e51");
+        assert_eq!(expected_plaintext_2, block);
+
+        block = decode_hex("2b0930daa23de94ce87017ba2d84988d");
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+        let expected_plaintext_3 = decode_hex("30c81c46a35ce411e5fbc1191a0a52ef");
+        assert_eq!(expected_plaintext_3, block);
+
+        block = decode_hex("dfc9c58db67aada613c2dd08457941a6");
+        cipher
+            .apply_keystream(&mut block)
+            .expect("Failed to apply keystream");
+        let expected_plaintext_3 = decode_hex("f69f2445df4f9b17ad2b417be66c3710");
+        assert_eq!(expected_plaintext_3, block);
+    }
+}
diff --git a/rust/bssl-crypto/src/cipher/mod.rs b/rust/bssl-crypto/src/cipher/mod.rs
new file mode 100644
index 0000000..1215469
--- /dev/null
+++ b/rust/bssl-crypto/src/cipher/mod.rs
@@ -0,0 +1,146 @@
+/* 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_CIPHER;
+use std::ffi::c_int;
+use std::marker::PhantomData;
+
+/// AES-CTR stream cipher operations.
+pub mod aes_ctr;
+
+/// Error returned in the event of an unsuccessful cipher operation.
+#[derive(Debug)]
+pub struct CipherError;
+
+/// Synchronous stream cipher trait.
+pub trait StreamCipher {
+    /// The byte array key type which specifies the size of the key used to instantiate the cipher.
+    type Key: AsRef<[u8]>;
+
+    /// The byte array nonce type which specifies the size of the nonce used in the cipher
+    /// operations.
+    type Nonce: AsRef<[u8]>;
+
+    /// Instantiate a new instance of a stream cipher from a `key` and `iv`.
+    fn new(key: &Self::Key, iv: &Self::Nonce) -> Self;
+
+    /// Applies the cipher keystream to `buffer` in place, returning CipherError on an unsuccessful
+    /// operation.
+    fn apply_keystream(&mut self, buffer: &mut [u8]) -> Result<(), CipherError>;
+}
+
+trait EvpCipherType {
+    type Key: AsRef<[u8]>;
+    type Nonce: AsRef<[u8]>;
+    fn evp_cipher() -> *const EVP_CIPHER;
+}
+
+struct EvpAes128Ctr;
+impl EvpCipherType for EvpAes128Ctr {
+    type Key = [u8; 16];
+    type Nonce = [u8; 16];
+    fn evp_cipher() -> *const EVP_CIPHER {
+        // Safety:
+        // - this just returns a constant value
+        unsafe { bssl_sys::EVP_aes_128_ctr() }
+    }
+}
+
+struct EvpAes256Ctr;
+impl EvpCipherType for EvpAes256Ctr {
+    type Key = [u8; 32];
+    type Nonce = [u8; 16];
+    fn evp_cipher() -> *const EVP_CIPHER {
+        // Safety:
+        // - this just returns a constant value
+        unsafe { bssl_sys::EVP_aes_256_ctr() }
+    }
+}
+
+// Internal cipher implementation which wraps EVP_CIPHER_*, where K is the size of the Key and I is
+// the size of the IV. This must only be exposed publicly by types who ensure that K is the correct
+// size for the given CipherType. This can be checked via bssl_sys::EVP_CIPHER_key_length.
+//
+// WARNING: This is not safe to re-use for the CBC mode of operation since it is applying the
+// key stream in-place.
+struct Cipher<C: EvpCipherType> {
+    ctx: *mut bssl_sys::EVP_CIPHER_CTX,
+    _marker: PhantomData<C>,
+}
+
+impl<C: EvpCipherType> Cipher<C> {
+    fn new(key: &C::Key, iv: &C::Nonce) -> Self {
+        // Safety:
+        // - Panics on allocation failure.
+        let ctx = unsafe { bssl_sys::EVP_CIPHER_CTX_new() };
+        assert!(!ctx.is_null());
+
+        let key_cslice = CSlice::from(key.as_ref());
+        let iv_cslice = CSlice::from(iv.as_ref());
+
+        // Safety:
+        // - Key size and iv size must be properly set by the higher level wrapper types.
+        // - Panics on allocation failure.
+        let result = unsafe {
+            bssl_sys::EVP_EncryptInit_ex(
+                ctx,
+                C::evp_cipher(),
+                std::ptr::null_mut(),
+                key_cslice.as_ptr(),
+                iv_cslice.as_ptr(),
+            )
+        };
+        assert_eq!(result, 1);
+
+        Self {
+            ctx,
+            _marker: Default::default(),
+        }
+    }
+
+    fn apply_keystream_in_place(&mut self, buffer: &mut [u8]) -> Result<(), CipherError> {
+        let mut cslice_buf_mut = CSliceMut::from(buffer);
+        let mut out_len = 0;
+
+        let buff_len_int = c_int::try_from(cslice_buf_mut.len()).map_err(|_| CipherError)?;
+
+        // Safety:
+        // - The output buffer provided is always large enough for an in-place operation.
+        let result = unsafe {
+            bssl_sys::EVP_EncryptUpdate(
+                self.ctx,
+                cslice_buf_mut.as_mut_ptr(),
+                &mut out_len,
+                cslice_buf_mut.as_mut_ptr(),
+                buff_len_int,
+            )
+        };
+        if result == 1 {
+            assert_eq!(out_len as usize, cslice_buf_mut.len());
+            Ok(())
+        } else {
+            Err(CipherError)
+        }
+    }
+}
+
+impl<C: EvpCipherType> Drop for Cipher<C> {
+    fn drop(&mut self) {
+        // Safety:
+        // - `self.ctx` was allocated by `EVP_CIPHER_CTX_new` and has not yet been freed.
+        unsafe { bssl_sys::EVP_CIPHER_CTX_free(self.ctx) }
+    }
+}
diff --git a/rust/bssl-crypto/src/lib.rs b/rust/bssl-crypto/src/lib.rs
index c538467..99140b7 100644
--- a/rust/bssl-crypto/src/lib.rs
+++ b/rust/bssl-crypto/src/lib.rs
@@ -32,6 +32,9 @@
 /// AES block operations.
 pub mod aes;
 
+/// Ciphers.
+pub mod cipher;
+
 /// Hash functions.
 pub mod digest;