aboutsummaryrefslogtreecommitdiff
path: root/rust
diff options
context:
space:
mode:
authorNabil Wadih <nwadih@google.com>2023-06-05 17:55:37 -0700
committerBoringssl LUCI CQ <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com>2023-08-24 22:46:45 +0000
commitc6c9c381ed469a17e6d93ccf83ab87d3184b75a6 (patch)
tree8f010c1b5c53398c23cb54a1886d3044167ed07f /rust
parent4325d8c801b043f23d23e99aa3c5f663f0d88168 (diff)
downloadboringssl-c6c9c381ed469a17e6d93ccf83ab87d3184b75a6.zip
boringssl-c6c9c381ed469a17e6d93ccf83ab87d3184b75a6.tar.gz
boringssl-c6c9c381ed469a17e6d93ccf83ab87d3184b75a6.tar.bz2
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>
Diffstat (limited to 'rust')
-rw-r--r--rust/bssl-crypto/src/cipher/aes_ctr.rs208
-rw-r--r--rust/bssl-crypto/src/cipher/mod.rs146
-rw-r--r--rust/bssl-crypto/src/lib.rs3
3 files changed, 357 insertions, 0 deletions
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 @@ pub mod aead;
/// AES block operations.
pub mod aes;
+/// Ciphers.
+pub mod cipher;
+
/// Hash functions.
pub mod digest;