From 5efc7dc00f399d241ff719c1deb0f0f4ca056e63 Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Mon, 10 Feb 2025 15:26:53 -0800 Subject: [PATCH 1/6] Add initial implementation of wrappers for GCM incremental APIs --- Cargo.lock | 118 ++++ rust-symcrypt/Cargo.toml | 1 + rust-symcrypt/src/gcm2.rs | 1193 +++++++++++++++++++++++++++++++++++++ rust-symcrypt/src/lib.rs | 1 + 4 files changed, 1313 insertions(+) create mode 100644 rust-symcrypt/src/gcm2.rs diff --git a/Cargo.lock b/Cargo.lock index afc44e85..7aa98eb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,6 +49,12 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cexpr" version = "0.6.0" @@ -131,6 +137,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi", + "windows-targets", +] + [[package]] name = "glob" version = "0.3.2" @@ -241,6 +259,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy 0.7.35", +] + [[package]] name = "prettyplease" version = "0.2.29" @@ -291,6 +318,37 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha", + "rand_core", + "zerocopy 0.8.17", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" +dependencies = [ + "getrandom", + "zerocopy 0.8.17", +] + [[package]] name = "regex" version = "1.11.1" @@ -377,6 +435,7 @@ dependencies = [ "hex", "lazy_static", "libc", + "rand", "symcrypt-sys", ] @@ -424,6 +483,15 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "winapi" version = "0.3.9" @@ -536,3 +604,53 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.8.0", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa91407dacce3a68c56de03abe2760159582b846c6a4acd2f456618087f12713" +dependencies = [ + "zerocopy-derive 0.8.17", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06718a168365cad3d5ff0bb133aad346959a2074bd4a85c121255a11304a8626" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/rust-symcrypt/Cargo.toml b/rust-symcrypt/Cargo.toml index 6cb1fd6d..82be9dc1 100644 --- a/rust-symcrypt/Cargo.toml +++ b/rust-symcrypt/Cargo.toml @@ -27,3 +27,4 @@ pkcs1-encrypt-decrypt = [] [dev-dependencies] hex = "0.4.3" +rand = "0.9.0" \ No newline at end of file diff --git a/rust-symcrypt/src/gcm2.rs b/rust-symcrypt/src/gcm2.rs new file mode 100644 index 00000000..4f7f3d1e --- /dev/null +++ b/rust-symcrypt/src/gcm2.rs @@ -0,0 +1,1193 @@ +use std::{mem, ptr::{addr_of, addr_of_mut}}; + +use internal::{GcmInitializedStream, GcmInitializedStreamRefMut}; +use symcrypt_sys::{SymCryptGcmAuthPart, SymCryptGcmDecrypt, SymCryptGcmDecryptFinal, SymCryptGcmDecryptPart, SymCryptGcmEncrypt, SymCryptGcmEncryptFinal, SymCryptGcmEncryptPart, SymCryptGcmExpandKey, SymCryptGcmInit, SymCryptWipe, SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR, SYMCRYPT_GCM_EXPANDED_KEY, SYMCRYPT_GCM_STATE}; + +use crate::{cipher::{convert_cipher, BlockCipherType}, errors::SymCryptError}; + + +/// +/// This type represents an uninitialized GcmExpandedKey storage. It can be freely +/// copied, cloned, or moved as it does not contain any information. +/// +#[derive(Clone, Copy, Default)] +pub struct GcmUnexpandedKey(SYMCRYPT_GCM_EXPANDED_KEY); + +impl GcmUnexpandedKey { + + // + // `get_key_ptr` gets a constant pointer to the underlying SYMCRYPT_GCM_EXPANDED_KEY. + // + #[inline(always)] + fn get_key_ptr(&self) -> *const SYMCRYPT_GCM_EXPANDED_KEY { + addr_of!(self.0) + } + + // + // `get_key_ptr_mut` gets a mutable pointer to the underlying SYMCRYPT_GCM_EXPANDED_KEY. + // + #[inline(always)] + fn get_key_ptr_mut(&mut self) -> *mut SYMCRYPT_GCM_EXPANDED_KEY { + addr_of_mut!(self.0) + } + + /// + /// `expand_key` will initialize this SYMCRYPT_GCM_EXPANDED_KEY to using the probided + /// cipher type and key. + /// + /// `cipher_type` is a `BlockCipherType` that determines the cipher to use for this key. + /// The only supported cipher type is [`BlockCipherType::AesBlock`] + /// + /// `key_data` is a `&[u8]` that contains the key to initialize with. + /// + pub fn expand_key(&mut self, cipher_type: BlockCipherType, key_data: &[u8]) -> Result { + let cipher = convert_cipher(cipher_type); + + // + // SAFETY: FFI call to initialize repr(C) struct. + // + + unsafe { + let result = + SymCryptGcmExpandKey( + self.get_key_ptr_mut(), + cipher, + key_data.as_ptr(), + key_data.len() as symcrypt_sys::SIZE_T); + + if result != SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR { + return Err(result.into()); + } + + Ok(GcmExpandedKey::new(self)) + } + } + +} + +/// +/// This type represents a handle to an initialized GcmExpandedKey that +/// is used to: +/// 1. Provide a guarantee that the underlying storage is initialized. +/// 2. Prevent the underlying storage from being moved or copied. +/// 3. Zero the underlying storage when dropped. +/// +pub struct GcmExpandedKey<'a>(&'a mut GcmUnexpandedKey); + +impl<'a> GcmExpandedKey<'a> { + + // + // # Safety: + // + // The caller must enture that the underlying storage has been correctly + // initialized. + // + #[inline(always)] + unsafe fn new(inner: &'a mut GcmUnexpandedKey) -> Self { + Self(inner) + } + + /// + /// Creates a borrowed reference to the underlying GcmExpandedKey storage. + /// + #[inline(always)] + pub fn as_ref(&self) -> GcmExpandedKeyRef { + GcmExpandedKeyRef::new(self) + } + + /// + /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. + /// This call can fail and the caller must check the result. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. + /// + /// `source` is a `&[u8]` that contains the cipher text to be decrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. + /// `destination` must be of the same length as `source`. + /// + /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. + /// + /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. + /// + #[inline(always)] + pub fn decrypt( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + source: &[u8], + destination: &mut [u8], + tag: &[u8] + ) -> Result<(), SymCryptError> + + { + + self.as_ref().decrypt(nonce, auth_data, source, destination, tag) + } + + /// + /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. This call can fail and the caller must check the result. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. + /// + /// `buffer` is a `&mut [u8]` that contains the cipher text data to be decrypted. After the decryption has been completed, + /// `buffer` will be over-written to contain the plain text data. + /// + /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. + /// + /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. + /// + #[inline(always)] + pub fn decrypt_in_place( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + buffer: &mut [u8], + tag: &[u8] + ) -> Result<(), SymCryptError> + + { + + self.as_ref().decrypt_in_place(nonce, auth_data, buffer, tag) + } + + /// + /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. + /// This call cannot fail. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. + /// + /// `source` is a `&[u8]` that contains the plain text to be encrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. + /// `destination` must be of the same length as `source`. + /// + /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. + /// Tag sizes of 4 and 8 are not supported. + /// + #[inline(always)] + pub fn encrypt( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + source: &[u8], + destination: &mut [u8], + tag: &mut [u8] + ) + + { + + self.as_ref().encrypt(nonce, auth_data, source, destination, tag); + } + + /// + /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. This call cannot fail. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. + /// + /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, + /// `buffer` will be over-written to contain the cipher text data. + /// + /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. + /// Tag sizes of 4 and 8 are not supported. + /// + #[inline(always)] + pub fn encrypt_in_place( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + buffer: &mut [u8], + tag: &mut [u8] + ) + + { + + self.as_ref().encrypt_in_place(nonce, auth_data, buffer, tag); + } + +} + +impl<'a> Drop for GcmExpandedKey<'a> { + + fn drop(&mut self) { + + // + // SAFETY: FFI calls to securly zero repr(C) structs + // + + unsafe { + SymCryptWipe( + self.0.get_key_ptr_mut() as *mut _, + mem::size_of::() as symcrypt_sys::SIZE_T); + } + } + +} + +/// +/// This type represents a borrowed handle to an initialized GcmExpandedKey that +/// is used to: +/// 1. Provide a guarantee that the underlying storage is initialized. +/// 2. Prevent the underlying storage from being moved or copied. +/// +/// This type does not zero the underlying storage when dropped. +/// +pub struct GcmExpandedKeyRef<'a>(&'a GcmUnexpandedKey); + +impl<'a> GcmExpandedKeyRef<'a> { + + /// + /// `new` creates a new borrowed handle to an initialized GcmExpandedKey. + /// + /// `expanded_key` is a `&GcmExpandedKey` that is the owning handle for the GcmExpandedKey to create a reference to. + /// + #[inline(always)] + pub fn new(expanded_key: &'a GcmExpandedKey) -> Self { + Self(expanded_key.0) + } + + /// + /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. + /// This call can fail and the caller must check the result. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. + /// + /// `source` is a `&[u8]` that contains the cipher text to be decrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. + /// `destination` must be of the same length as `source`. + /// + /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. + /// + /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. + /// + pub fn decrypt( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + source: &[u8], + destination: &mut [u8], + tag: &[u8] + ) -> Result<(), SymCryptError> + + { + + assert_eq!(source.len(), destination.len()); + + // + // SAFETY: The underlying SYMCRYPT_GCM_EXPANDED_KEY is guaranteed to be initialized + // by the caller of `GcmExpandedKey::new` and we have asserted that both `source` and + // `destination` are of the same length. + // + + unsafe { + let result = + SymCryptGcmDecrypt( + self.0.get_key_ptr(), + nonce.as_ptr(), + nonce.len() as symcrypt_sys::SIZE_T, + auth_data.as_ptr(), + auth_data.len() as symcrypt_sys::SIZE_T, + source.as_ptr(), + destination.as_mut_ptr(), + destination.len() as symcrypt_sys::SIZE_T, + tag.as_ptr(), + tag.len() as symcrypt_sys::SIZE_T); + + match result { + SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), + error => Err(error.into()) + } + } + } + + /// + /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. This call can fail and the caller must check the result. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. + /// + /// `buffer` is a `&mut [u8]` that contains the cipher text data to be decrypted. After the decryption has been completed, + /// `buffer` will be over-written to contain the plain text data. + /// + /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. + /// + /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. + /// + pub fn decrypt_in_place( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + buffer: &mut [u8], + tag: &[u8] + ) -> Result<(), SymCryptError> + + { + + // + // SAFETY: The underlying SYMCRYPT_GCM_EXPANDED_KEY is guaranteed to be initialized + // by the caller of `GcmExpandedKey::new`. + // + + unsafe { + let result = + SymCryptGcmDecrypt( + self.0.get_key_ptr(), + nonce.as_ptr(), + nonce.len() as symcrypt_sys::SIZE_T, + auth_data.as_ptr(), + auth_data.len() as symcrypt_sys::SIZE_T, + buffer.as_ptr(), + buffer.as_mut_ptr(), + buffer.len() as symcrypt_sys::SIZE_T, + tag.as_ptr(), + tag.len() as symcrypt_sys::SIZE_T); + + match result { + SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), + error => Err(error.into()) + } + } + } + + /// + /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. + /// This call cannot fail. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. + /// + /// `source` is a `&[u8]` that contains the plain text to be encrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. + /// `destination` must be of the same length as `source`. + /// + /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. + /// Tag sizes of 4 and 8 are not supported. + /// + pub fn encrypt( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + source: &[u8], + destination: &mut [u8], + tag: &mut [u8] + ) + + { + + assert_eq!(source.len(), destination.len()); + + // + // SAFETY: The underlying SYMCRYPT_GCM_EXPANDED_KEY is guaranteed to be initialized + // by the caller of `GcmExpandedKey::new` and we have asserted that both `source` and + // `destination` are of the same length. + // + + unsafe { + SymCryptGcmEncrypt( + self.0.get_key_ptr(), + nonce.as_ptr(), + nonce.len() as symcrypt_sys::SIZE_T, + auth_data.as_ptr(), + auth_data.len() as symcrypt_sys::SIZE_T, + source.as_ptr(), + destination.as_mut_ptr(), + destination.len() as symcrypt_sys::SIZE_T, + tag.as_mut_ptr(), + tag.len() as symcrypt_sys::SIZE_T); + } + } + + /// + /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. This call cannot fail. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. + /// + /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, + /// `buffer` will be over-written to contain the cipher text data. + /// + /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. + /// Tag sizes of 4 and 8 are not supported. + /// + pub fn encrypt_in_place( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + buffer: &mut [u8], + tag: &mut [u8] + ) + + { + + // + // SAFETY: The underlying SYMCRYPT_GCM_EXPANDED_KEY is guaranteed to be initialized + // by the caller of `GcmExpandedKey::new`. + // + + unsafe { + SymCryptGcmEncrypt( + self.0.get_key_ptr(), + nonce.as_ptr(), + nonce.len() as symcrypt_sys::SIZE_T, + auth_data.as_ptr(), + auth_data.len() as symcrypt_sys::SIZE_T, + buffer.as_ptr(), + buffer.as_mut_ptr(), + buffer.len() as symcrypt_sys::SIZE_T, + tag.as_mut_ptr(), + tag.len() as symcrypt_sys::SIZE_T); + } + } + +} + +/// +/// This type represents an uninitialized SYMCRYPT_GCM_STATE. It can be freely +/// copied, clones, or moved as it does not contain any information. +/// +#[derive(Copy, Clone, Default)] +pub struct GcmStream(SYMCRYPT_GCM_STATE); + +impl GcmStream { + + // + // `get_key_ptr_mut` gets a mutable pointer to the underlying SYMCRYPT_GCM_STATE. + // + #[inline(always)] + fn get_state_ptr_mut(&mut self) -> *mut SYMCRYPT_GCM_STATE { + addr_of_mut!(self.0) + } + + // + // `initialize` initializes the underlying `SYMCRYPT_GCM_STATE`` with the provided key and nonce. + // + // `expanded_key` provides a borrowed reference to an initialized `SYMCRYPT_GCM_EXPANDED_KEY` + // + // `nonce` is a `&[u8; 12]` that is used as the nonce. + // + fn initialize<'a>(&'a mut self, expanded_key: GcmExpandedKeyRef<'a>, nonce: &[u8; 12]) -> GcmInitializedStream<'a> { + + // + // SAFETY: FFI call to initialize repr(C) struct. + // + + unsafe { + SymCryptGcmInit( + self.get_state_ptr_mut(), + expanded_key.0.get_key_ptr(), + nonce.as_ptr(), + nonce.len() as symcrypt_sys::SIZE_T); + + GcmInitializedStream::new(self) + } + } + + /// + /// Initializes this GcmStream as a GcmAuthStream using the provided key, and nonce. + /// + /// `expanded_key` is a `GcmExpandedKeyRef` that provides a handle to the key to use + /// for operations. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce. + /// + #[inline(always)] + pub fn as_auth_stream<'a>( + &'a mut self, + expanded_key: GcmExpandedKeyRef<'a>, + nonce: &[u8; 12]) -> GcmAuthStream<'a> { + + GcmAuthStream(self.initialize(expanded_key, nonce)) + } + + /// + /// Initializes this GcmStream as a GcmDecryptionStream using the provided key, and nonce. + /// + /// `expanded_key` is a `GcmExpandedKeyRef` that provides a handle to the key to use + /// for operations. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce. + /// + #[inline(always)] + pub fn as_decryption_stream<'a>( + &'a mut self, + expanded_key: GcmExpandedKeyRef<'a>, + nonce: &[u8; 12]) -> GcmDecryptionStream<'a> { + + GcmDecryptionStream(self.initialize(expanded_key, nonce)) + } + + /// + /// Initializes this GcmStream as a GcmEncryptionStream using the provided key, and nonce. + /// + /// `expanded_key` is a `GcmExpandedKeyRef` that provides a handle to the key to use + /// for operations. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce. + /// + #[inline(always)] + pub fn as_encryption_stream<'a>( + &'a mut self, + expanded_key: GcmExpandedKeyRef<'a>, + nonce: &[u8; 12]) -> GcmEncryptionStream<'a> { + + GcmEncryptionStream(self.initialize(expanded_key, nonce)) + } + +} + +/// +/// This type represents a handle to an initialized GcmStream that can be used to autheticate, +/// but not encrypt or decrypt, data. It can later be converted to a GcmDecryptionStream or +/// GcmEncryptionStream. +/// +pub struct GcmAuthStream<'a>(GcmInitializedStream<'a>); + +impl<'a> GcmAuthStream<'a> { + + /// + /// `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. + /// + #[inline(always)] + pub fn as_ref_mut(&mut self) -> GcmAuthStreamRefMut { + GcmAuthStreamRefMut(self.0.as_ref_mut()) + } + + /// + /// `authenticate` authenticates, but does not otherwise encrypt or decrypt, the provided data. + /// + /// `data` is a `&[u8]` that contains the data to authenticate. + /// + #[inline(always)] + pub fn authenticate(&mut self, data: &[u8]) { + self.as_ref_mut().authenticate(data); + } + + /// + /// `to_decryption_stream` converts this GcmAuthStream into a GcmDecryptionStream + /// + #[inline(always)] + pub fn to_decryption_stream(self) -> GcmDecryptionStream<'a> { + GcmDecryptionStream(self.0) + } + + /// + /// `to_encryption_stream` converts this GcmAuthStream into a GcmEncryptionStream + /// + #[inline(always)] + pub fn to_encryption_stream(self) -> GcmEncryptionStream<'a> { + GcmEncryptionStream(self.0) + } + +} + +/// +/// This type represents a borrowed mutable handle to an initialized GcmStream that can be used to +/// autheticate, but not encrypt or decrypt, data. +/// +pub struct GcmAuthStreamRefMut<'a>(GcmInitializedStreamRefMut<'a>); + +impl<'a> GcmAuthStreamRefMut<'a> { + + /// + /// `authenticate` authenticates, but does not otherwise encrypt or decrypt, the provided data. + /// + /// `data` is a `&[u8]` that contains the data to authenticate. + /// + pub fn authenticate(&mut self, data: &[u8]) { + + // + // SAFETY: The internal stream is guaranteed to still be initialized while + // self is alive. + // + + unsafe { + SymCryptGcmAuthPart( + self.0.get_state_ptr_mut(), + data.as_ptr(), + data.len() as symcrypt_sys::SIZE_T); + } + } + +} + +/// +/// This type represents a handle to an initialized GcmStream that can be used to decrypt data. +/// +pub struct GcmDecryptionStream<'a>(GcmInitializedStream<'a>); + +impl<'a> GcmDecryptionStream<'a> { + + /// + /// `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. + /// + #[inline(always)] + pub fn as_ref_mut(&mut self) -> GcmDecryptionStreamRefMut { + GcmDecryptionStreamRefMut(self.0.as_ref_mut()) + } + + /// + /// `complete` finishes this decryption stream and validates that the provided tag matches + /// the generated tag. + /// + /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. + /// This is used to verify the integrity of the cipher text. + /// + pub fn complete(mut self, tag: &[u8]) -> Result<(), SymCryptError> { + + // + // SAFETY: The internal stream is guaranteed to still be initialized while + // self is alive. + // + + let result = unsafe { + SymCryptGcmDecryptFinal( + self.0.get_state_ptr_mut(), + tag.as_ptr(), + tag.len() as u64) + }; + + self.0.drop_without_zero(); + match result { + SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), + error => Err(error.into()) + } + } + + /// + /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. + /// This is a partial decryption of the cipher text and the results of the plain text are not validated + /// until `complete` is called. + /// + /// `source` is a `&[u8]` that contains the cipher text to be decrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. + /// `destination` must be of the same length as `source`. + /// + #[inline(always)] + pub fn decrypt(&mut self, source: &[u8], destination: &mut [u8]) { + self.as_ref_mut().decrypt(source, destination); + } + + /// + /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. + /// This is a partial decryption of the cipher text and the results of the plain text are not validated + /// until `complete` is called. + /// + /// `source` is a `&[u8]` that contains the cipher text to be decrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. + /// `destination` must be of the same length as `source`. + /// + #[inline(always)] + pub fn decrypt_in_place(&mut self, data: &mut [u8]) { + self.as_ref_mut().decrypt_in_place(data); + } + +} + +/// +/// This type represents a borrowed mutable handle to an initialized GcmStream that can be used to +/// decrypt data. +/// +pub struct GcmDecryptionStreamRefMut<'a>(GcmInitializedStreamRefMut<'a>); + +impl<'a> GcmDecryptionStreamRefMut<'a> { + + /// + /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. + /// This is a partial decryption of the cipher text and the results of the plain text are not validated + /// until `complete` is called. + /// + /// `source` is a `&[u8]` that contains the cipher text to be decrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. + /// `destination` must be of the same length as `source`. + /// + pub fn decrypt(&mut self, source: &[u8], destination: &mut [u8]) { + assert_eq!(source.len(), destination.len()); + + // + // SAFETY: The internal stream is guaranteed to still be initialized while + // self is alive and we've asserted that the source and destination buffers + // are the same length. + // + + unsafe { + SymCryptGcmDecryptPart( + self.0.get_state_ptr_mut(), + source.as_ptr(), + destination.as_mut_ptr(), + destination.len() as symcrypt_sys::SIZE_T); + } + } + + /// + /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. + /// This is a partial decryption of the cipher text and the results of the plain text are not validated + /// until `complete` is called. + /// + /// `source` is a `&[u8]` that contains the cipher text to be decrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. + /// `destination` must be of the same length as `source`. + /// + pub fn decrypt_in_place(&mut self, data: &mut [u8]) { + + // + // SAFETY: The internal stream is guaranteed to still be initialized while + // self is alive. + // + + unsafe { + SymCryptGcmDecryptPart( + self.0.get_state_ptr_mut(), + data.as_ptr(), + data.as_mut_ptr(), + data.len() as symcrypt_sys::SIZE_T); + } + } + +} + +/// +/// This type represents a handle to an initialized GcmStream that can be used to +/// encrypt data. +/// +pub struct GcmEncryptionStream<'a>(GcmInitializedStream<'a>); + +impl<'a> GcmEncryptionStream<'a> { + + /// + /// `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. + /// + #[inline(always)] + pub fn as_ref_mut(&mut self) -> GcmEncryptionStreamRefMut { + GcmEncryptionStreamRefMut(self.0.as_ref_mut()) + } + + /// + /// `complete` finishes this encryption stream and returns the generated tag for validating + /// decryption. + /// + /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. + /// Tag size must be 12, 13, 14, 15, 16 per SP800-38D. + /// Tag sizes of 4 and 8 are not supported. + /// + pub fn complete(mut self, tag: &mut [u8]) { + + // + // SAFETY: The internal stream is guaranteed to still be initialized while + // self is alive. + // + + unsafe { + SymCryptGcmEncryptFinal( + self.0.get_state_ptr_mut(), + tag.as_mut_ptr(), + tag.len() as u64); + } + + self.0.drop_without_zero(); + } + + /// + /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. + /// + /// + /// `source` is a `&[u8]` that contains the plain text to be encrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. + /// `destination` must be of the same length as `source`. + /// + #[inline(always)] + pub fn encrypt(&mut self, source: &[u8], destination: &mut [u8]) { + self.as_ref_mut().encrypt(source, destination); + } + + /// + /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. + /// + /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, + /// `buffer` will be over-written to contain the cipher text data. + /// + #[inline(always)] + pub fn encrypt_in_place(&mut self, data: &mut [u8]) { + self.as_ref_mut().encrypt_in_place(data); + } + +} + +/// +/// This type represents a borrowed mutable handle to an initialized GcmStream that can be used to +/// encrypt data. +/// +pub struct GcmEncryptionStreamRefMut<'a>(GcmInitializedStreamRefMut<'a>); + +impl<'a> GcmEncryptionStreamRefMut<'a> { + + /// + /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. + /// + /// + /// `source` is a `&[u8]` that contains the plain text to be encrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. + /// `destination` must be of the same length as `source`. + /// + pub fn encrypt(&mut self, source: &[u8], destination: &mut [u8]) { + assert_eq!(source.len(), destination.len()); + + // + // SAFETY: The internal stream is guaranteed to still be initialized while + // self is alive and we've asserted that the source and destination buffers + // are the same length. + // + + unsafe { + SymCryptGcmEncryptPart( + self.0.get_state_ptr_mut(), + source.as_ptr(), + destination.as_mut_ptr(), + destination.len() as u64); + } + } + + /// + /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. + /// + /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, + /// `buffer` will be over-written to contain the cipher text data. + /// + pub fn encrypt_in_place(&mut self, data: &mut [u8]) { + + // + // SAFETY: The internal stream is guaranteed to still be initialized while + // self is alive. + // + + unsafe { + SymCryptGcmEncryptPart( + self.0.get_state_ptr_mut(), + data.as_ptr(), + data.as_mut_ptr(), + data.len() as u64); + } + } + +} + +mod internal { + + use std::{mem, ops::{Deref, DerefMut}}; + + use symcrypt_sys::{SymCryptWipe, SYMCRYPT_GCM_STATE}; + + use super::GcmStream; + + /// + /// This type represents a handle to an initialized GcmStream that + /// is used to: + /// 1. Provide a guarantee that the underlying storage is initialized. + /// 2. Prevent the underlying storage from being moved or copied. + /// 3. Zero the underlying storage when dropped. + /// + pub struct GcmInitializedStream<'a>(&'a mut GcmStream); + + impl<'a> GcmInitializedStream<'a> { + + // + // `new` creates a new handle to an initialized GcmStream. + // + // # Safety: + // + // The caller must ensure that the underlying storage has been correctly + // initialized. + // + pub unsafe fn new(inner: &'a mut GcmStream) -> Self { + Self(inner) + } + + // + // `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. + // + #[inline(always)] + pub fn as_ref_mut(&mut self) -> GcmInitializedStreamRefMut { + GcmInitializedStreamRefMut::new(self) + } + + // + // `drop_without_zero` will drop this handle to an GcmStream without + // zeroing out the underlying storage. The caller should ensure that + // the storage was (or will be) zeroed. + // + #[inline(always)] + pub fn drop_without_zero(self) { + mem::forget(self); + } + + } + + impl<'a> Drop for GcmInitializedStream<'a> { + + fn drop(&mut self) { + + // + // SAFETY: FFI calls to securly zero repr(C) structs + // + + unsafe { + SymCryptWipe( + self.0.get_state_ptr_mut() as *mut _, + mem::size_of::() as symcrypt_sys::SIZE_T); + } + } + + } + + impl<'a> Deref for GcmInitializedStream<'a> { + + type Target = GcmStream; + + fn deref(&self) -> &Self::Target { + self.0 + } + + } + + impl<'a> DerefMut for GcmInitializedStream<'a> { + + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } + + } + + + /// + /// This type represents a borrowed handle to an initialized GcmStream that + /// is used to: + /// 1. Provide a guarantee that the underlying storage is initialized. + /// 2. Prevent the underlying storage from being moved or copied. + /// + /// This type does not zero the underlying storage when dropped. + /// + pub struct GcmInitializedStreamRefMut<'a>(&'a mut GcmStream); + + impl<'a> GcmInitializedStreamRefMut<'a> { + + // + // `new` creates a new borrowed mutable reference to an initialized GcmStream + // from an existing owned reference. + // + #[inline(always)] + pub fn new(inner: &'a mut GcmInitializedStream) -> Self { + Self(inner.0) + } + + } + + impl<'a> Deref for GcmInitializedStreamRefMut<'a> { + + type Target = GcmStream; + + fn deref(&self) -> &Self::Target { + self.0 + } + + } + + impl<'a> DerefMut for GcmInitializedStreamRefMut<'a> { + + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } + + } + +} + +#[cfg(test)] +mod test { + + use crate::{cipher::BlockCipherType, errors::SymCryptError}; + + use super::{GcmStream, GcmUnexpandedKey}; + + #[test] + fn test_invalid_aes_key() { + let key_data = &[]; + + let mut key_storage = GcmUnexpandedKey::default(); + + match key_storage.expand_key(BlockCipherType::AesBlock, key_data) { + Err(SymCryptError::WrongKeySize) => {}, + Ok(_) => panic!("Incorrectly returned success when generating auth stream"), + Err(error) => panic!("Invalid result when generating auth stream: {:?}", error) + }; + } + + #[test] + fn test_encrypt_decrypt_part() -> Result<(), SymCryptError> { + let mut key = GcmUnexpandedKey::default(); + let key = + key.expand_key( + BlockCipherType::AesBlock, + &hex::decode("feffe9928665731c6d6a8f9467308308").unwrap())?; + + let mut nonce = [0; 12]; + rand::fill(&mut nonce); + + let mut orig_data = [0; 1024]; + rand::fill(&mut orig_data); + + let (expected_encrypted, expected_tag) = { + let mut encrypted_data = orig_data; + let mut tag = [0; 16]; + + key.encrypt_in_place(&nonce, &[], &mut encrypted_data, &mut tag); + (encrypted_data, tag) + }; + + let mut gcm_stream = GcmStream::default(); + for chunk_size in 1..orig_data.len() { + let mut encryption_stream = + gcm_stream.as_encryption_stream( + key.as_ref(), + &nonce); + + let mut encrypted_data = [0; 1024]; + let mut tag = [0; 16]; + for (source, destination) in + orig_data.chunks(chunk_size) + .zip(encrypted_data.chunks_mut(chunk_size)) { + + encryption_stream.encrypt(source, destination); + } + + encryption_stream.complete(&mut tag); + assert_eq!(expected_encrypted, encrypted_data); + assert_eq!(expected_tag, tag); + } + + for chunk_size in 1..orig_data.len() { + let mut decryption_stream = + gcm_stream.as_decryption_stream( + key.as_ref(), + &nonce); + + let mut decrypted_data = [0; 1024]; + for (source, destination) in + expected_encrypted.chunks(chunk_size) + .zip(decrypted_data.chunks_mut(chunk_size)) { + + decryption_stream.decrypt(source, destination); + } + + decryption_stream.complete(&expected_tag)?; + assert_eq!(orig_data, decrypted_data); + } + + Ok(()) + } + + #[test] + fn test_encrypt_decrypt_part_inplace() -> Result<(), SymCryptError> { + let mut key = GcmUnexpandedKey::default(); + let key = + key.expand_key( + BlockCipherType::AesBlock, + &hex::decode("feffe9928665731c6d6a8f9467308308").unwrap())?; + + let mut nonce = [0; 12]; + rand::fill(&mut nonce); + + let mut orig_data = [0; 1024]; + rand::fill(&mut orig_data); + + let (expected_encrypted, expected_tag) = { + let mut encrypted_data = orig_data; + let mut tag = [0; 16]; + key.encrypt_in_place(&nonce, &[], &mut encrypted_data, &mut tag); + (encrypted_data, tag) + }; + + let mut gcm_stream = GcmStream::default(); + for chunk_size in 1..orig_data.len() { + let mut encryption_stream = + gcm_stream.as_encryption_stream( + key.as_ref(), + &nonce); + + let mut encrypted_data = orig_data; + let mut tag = [0; 16]; + for window in encrypted_data.chunks_mut(chunk_size) { + encryption_stream.encrypt_in_place(window); + } + + encryption_stream.complete(&mut tag); + assert_eq!(expected_encrypted, encrypted_data); + assert_eq!(expected_tag, tag); + } + + for chunk_size in 1..orig_data.len() { + let mut decryption_stream = + gcm_stream.as_decryption_stream( + key.as_ref(), + &nonce); + + let mut decrypted_data = expected_encrypted; + for window in decrypted_data.chunks_mut(chunk_size) { + decryption_stream.decrypt_in_place(window); + } + + decryption_stream.complete(&expected_tag)?; + assert_eq!(orig_data, decrypted_data); + } + + Ok(()) + } + + #[test] + fn test_gcm_decrypt_will_fail_wrong_tag() { + let p_key = hex::decode("feffe9928665731c6d6a8f9467308308").unwrap(); + let mut nonce_array = [0u8; 12]; + hex::decode_to_slice("cafebabefacedbaddecaf888", &mut nonce_array).unwrap(); + let auth_data = hex::decode("feedfacedeadbeeffeedfacedeadbeefabaddad2").unwrap(); + + let mut tag = [0u8; 16]; + hex::decode_to_slice("5bc94fbc3221a5db94fae95ae7121aaa", &mut tag).unwrap(); + + let mut buffer = [0u8; 60]; + hex::decode_to_slice("42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091", &mut buffer).unwrap(); + let cipher = BlockCipherType::AesBlock; + + let mut key = GcmUnexpandedKey::default(); + let gcm_state = key.expand_key(cipher, &p_key).unwrap(); + let result = gcm_state.decrypt_in_place(&nonce_array, &auth_data, &mut buffer, &tag); + + match result { + Ok(_) => { + panic!("Test passed when it should fail"); + } + Err(err) => { + assert_eq!(err, SymCryptError::AuthenticationFailure); + } + } + } + +} diff --git a/rust-symcrypt/src/lib.rs b/rust-symcrypt/src/lib.rs index 5ea0b00e..2445384d 100644 --- a/rust-symcrypt/src/lib.rs +++ b/rust-symcrypt/src/lib.rs @@ -6,6 +6,7 @@ pub mod cipher; pub mod ecc; pub mod errors; pub mod gcm; +pub mod gcm2; pub mod hash; pub mod hkdf; pub mod hmac; From dbe29f942d643a4463fb91d48fed2ffc4d72a452 Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Mon, 10 Feb 2025 15:29:25 -0800 Subject: [PATCH 2/6] Add missing symcrypt_init call --- rust-symcrypt/src/gcm2.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rust-symcrypt/src/gcm2.rs b/rust-symcrypt/src/gcm2.rs index 4f7f3d1e..c2381c34 100644 --- a/rust-symcrypt/src/gcm2.rs +++ b/rust-symcrypt/src/gcm2.rs @@ -3,7 +3,7 @@ use std::{mem, ptr::{addr_of, addr_of_mut}}; use internal::{GcmInitializedStream, GcmInitializedStreamRefMut}; use symcrypt_sys::{SymCryptGcmAuthPart, SymCryptGcmDecrypt, SymCryptGcmDecryptFinal, SymCryptGcmDecryptPart, SymCryptGcmEncrypt, SymCryptGcmEncryptFinal, SymCryptGcmEncryptPart, SymCryptGcmExpandKey, SymCryptGcmInit, SymCryptWipe, SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR, SYMCRYPT_GCM_EXPANDED_KEY, SYMCRYPT_GCM_STATE}; -use crate::{cipher::{convert_cipher, BlockCipherType}, errors::SymCryptError}; +use crate::{cipher::{convert_cipher, BlockCipherType}, errors::SymCryptError, symcrypt_init}; /// @@ -41,6 +41,7 @@ impl GcmUnexpandedKey { /// `key_data` is a `&[u8]` that contains the key to initialize with. /// pub fn expand_key(&mut self, cipher_type: BlockCipherType, key_data: &[u8]) -> Result { + symcrypt_init(); let cipher = convert_cipher(cipher_type); // From 963820bb0c4fa51b62907f274600f7970ef2c5fa Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Tue, 11 Feb 2025 10:18:32 -0800 Subject: [PATCH 3/6] Unify gcm and gcm2 interfaces --- rust-symcrypt/src/gcm.rs | 1459 +++++++++++++++++++++++++++++++++---- rust-symcrypt/src/gcm2.rs | 1194 ------------------------------ rust-symcrypt/src/lib.rs | 1 - 3 files changed, 1321 insertions(+), 1333 deletions(-) delete mode 100644 rust-symcrypt/src/gcm2.rs diff --git a/rust-symcrypt/src/gcm.rs b/rust-symcrypt/src/gcm.rs index 5b2432ee..242f7ae7 100644 --- a/rust-symcrypt/src/gcm.rs +++ b/rust-symcrypt/src/gcm.rs @@ -64,13 +64,86 @@ use crate::cipher::{convert_cipher, BlockCipherType}; use crate::errors::SymCryptError; use crate::symcrypt_init; -use core::ffi::c_void; -use std::marker::PhantomPinned; use std::mem; -use std::pin::Pin; use std::ptr; use symcrypt_sys; +/// +/// This type represents a common storage for SYMCRYPT_GCM_EXPANDED_KEY. +/// +#[derive(Clone, Copy, Default)] +struct GcmExpandedKeyStorage(symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY); + +impl GcmExpandedKeyStorage { + + // + // `get_key_ptr` gets a constant pointer to the underlying SYMCRYPT_GCM_EXPANDED_KEY. + // + #[inline(always)] + fn get_key_ptr(&self) -> *const symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY { + ptr::addr_of!(self.0) + } + + // + // `get_key_ptr_mut` gets a mutable pointer to the underlying SYMCRYPT_GCM_EXPANDED_KEY. + // + #[inline(always)] + fn get_key_ptr_mut(&mut self) -> *mut symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY { + ptr::addr_of_mut!(self.0) + } + + // + // `zero_storage` zeroes the underlying SYMCRYPT_GCM_EXPANDED_KEY memory. + // + // This is unsafe as it uninitializes the storage that other unsafe code + // may rely on being initialized. + // + #[inline(always)] + unsafe fn zero_storage(&mut self) { + symcrypt_sys::SymCryptWipe( + self.get_key_ptr_mut() as *mut _, + mem::size_of::() as symcrypt_sys::SIZE_T); + } + +} + +/// +/// This type represents an uninitialized GcmExpandedKeyStorage. It can be freely +/// copied, cloned, or moved as it does not contain any information. +/// +#[derive(Clone, Copy, Default)] +pub struct GcmUninitializedKey(GcmExpandedKeyStorage); + +impl GcmUninitializedKey { + + /// + /// `expand_key` will initialize this SYMCRYPT_GCM_EXPANDED_KEY to using the probided + /// cipher type and key. + /// + /// `cipher_type` is a `BlockCipherType` that determines the cipher to use for this key. + /// The only supported cipher type is [`BlockCipherType::AesBlock`] + /// + /// `key_data` is a `&[u8]` that contains the key to initialize with. + /// + pub fn expand_key(&mut self, cipher_type: BlockCipherType, key_data: &[u8]) -> Result { + symcrypt_init(); + + internal::gcm_expand_key( + key_data, + self.0.get_key_ptr_mut(), + convert_cipher(cipher_type))?; + + // + // SAFETY: gcm_expand_key guarantees that the SYMCRYPT_GCM_EXPANDED_KEY storage is initialized on success. + // + + unsafe { + Ok(GcmExpandedKeyHandle::new(self)) + } + } + +} + /// [`GcmExpandedKey`] is a struct that holds the Gcm expanded key from SymCrypt. pub struct GcmExpandedKey { // expanded_key holds the key from SymCrypt which is Pin>'d since the memory address for Self is moved around when @@ -80,188 +153,1012 @@ pub struct GcmExpandedKey { // SymCrypt expects the address for its structs to stay static through the structs lifetime to guarantee that structs are not memcpy'd as // doing so would lead to use-after-free and inconsistent states. - expanded_key: Pin>, + expanded_key: Box, key_length: usize, } -/// [`GcmInnerKey`] is a struct that holds the underlying SymCrypt state for GCM. -struct GcmInnerKey { - // inner represents the actual state of the hash from SymCrypt - inner: symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY, +impl Drop for GcmExpandedKey { + + fn drop(&mut self) { + + // + // SAFETY: Is is safe to uninitialize the underlying storage as this + // if the only reference to it and we are being dropped. + // + + unsafe { + self.expanded_key.zero_storage(); + } + } + +} + +/// `encrypt_in_place` and `decrypt_in_place` take in an allocated `buffer` as an in/out parameter for performance reasons. +/// This is for scenarios such as encrypting over a stream of data; allocating and copying data from a return will be costly performance wise. +impl GcmExpandedKey { + /// `new` takes in a reference to a key and a [`BlockCipherType`] and returns an expanded key that is Pin>'d. + /// + /// This function can fail and will propagate the error back to the caller. This call will fail if the wrong key size is provided. + /// + /// The only accepted Cipher for GCM is [`BlockCipherType::AesBlock`] + pub fn new(key: &[u8], cipher: BlockCipherType) -> Result { + symcrypt_init(); + let mut expanded_key = Box::new(GcmExpandedKeyStorage::default()); // Get expanded_key that is already Pin>'d + + // Use as_mut() to get a Pin<&mut GcmInnerKey> and then call get_inner_mut to get *mut + internal::gcm_expand_key( + key, + expanded_key.get_key_ptr_mut(), + convert_cipher(cipher), + )?; + let gcm_expanded_key = GcmExpandedKey { + expanded_key, + key_length: key.len(), + }; + Ok(gcm_expanded_key) + } + + /// + /// Creates a borrowed reference to the underlying GcmExpandedKeyStorage. + /// + #[inline(always)] + pub fn as_ref(&self) -> GcmExpandedKeyRef { + self.into() + } + + /// + /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. + /// This call cannot fail. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. + /// + /// `source` is a `&[u8]` that contains the plain text to be encrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. + /// `destination` must be of the same length as `source`. + /// + /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. + /// Tag sizes of 4 and 8 are not supported. + /// + #[inline(always)] + pub fn encrypt( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + source: &[u8], + destination: &mut [u8], + tag: &mut [u8] + ) + + { + + self.as_ref().encrypt(nonce, auth_data, source, destination, tag); + } + + /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. This call cannot fail. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. + /// + /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, + /// `buffer` will be over-written to contain the cipher text data. + /// + /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. + /// Tag sizes of 4 and 8 are not supported. + #[inline(always)] + pub fn encrypt_in_place( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + buffer: &mut [u8], + tag: &mut [u8], + ) { + + self.as_ref().encrypt_in_place(nonce, auth_data, buffer, tag); + } + + /// + /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. + /// This call can fail and the caller must check the result. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. + /// + /// `source` is a `&[u8]` that contains the cipher text to be decrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. + /// `destination` must be of the same length as `source`. + /// + /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. + /// + /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. + /// + #[inline(always)] + pub fn decrypt( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + source: &[u8], + destination: &mut [u8], + tag: &[u8] + ) -> Result<(), SymCryptError> + + { + + self.as_ref().decrypt(nonce, auth_data, source, destination, tag) + } + + /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. This call can fail and the caller must check the result. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. + /// + /// `buffer` is a `&mut [u8]` that contains the cipher text data to be decrypted. After the decryption has been completed, + /// `buffer` will be over-written to contain the plain text data. + /// + /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. + /// + /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. + #[inline(always)] + pub fn decrypt_in_place( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + buffer: &mut [u8], + tag: &[u8], + ) -> Result<(), SymCryptError> { + + self.as_ref().decrypt_in_place(nonce, auth_data, buffer, tag) + } + + /// `key_len` returns a the length of the [`GcmExpandedKey`] as a `usize`. + pub fn key_len(&self) -> usize { + self.key_length + } +} + +// No custom Send / Sync impl. needed for GcmExpandedKey since the +// underlying data is a pointer to a SymCrypt struct that is not modified after it is created. +unsafe impl Send for GcmExpandedKey {} +unsafe impl Sync for GcmExpandedKey {} + +/// +/// This type represents a handle to an initialized GcmUnexpandedKey that +/// is used to: +/// 1. Provide a guarantee that the underlying storage is initialized. +/// 2. Prevent the underlying storage from being moved or copied. +/// 3. Zero the underlying storage when dropped. +/// +pub struct GcmExpandedKeyHandle<'a>(&'a mut GcmExpandedKeyStorage); + +impl<'a> GcmExpandedKeyHandle<'a> { + + // + // # Safety: + // + // The caller must enture that the underlying storage has been correctly + // initialized. + // + #[inline(always)] + unsafe fn new(inner: &'a mut GcmUninitializedKey) -> Self { + Self(&mut inner.0) + } + + /// + /// Creates a borrowed reference to the underlying GcmUnexpandedKey storage. + /// + #[inline(always)] + pub fn as_ref(&self) -> GcmExpandedKeyRef { + self.into() + } + + /// + /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. + /// This call can fail and the caller must check the result. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. + /// + /// `source` is a `&[u8]` that contains the cipher text to be decrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. + /// `destination` must be of the same length as `source`. + /// + /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. + /// + /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. + /// + #[inline(always)] + pub fn decrypt( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + source: &[u8], + destination: &mut [u8], + tag: &[u8] + ) -> Result<(), SymCryptError> + + { + + self.as_ref().decrypt(nonce, auth_data, source, destination, tag) + } + + /// + /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. This call can fail and the caller must check the result. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. + /// + /// `buffer` is a `&mut [u8]` that contains the cipher text data to be decrypted. After the decryption has been completed, + /// `buffer` will be over-written to contain the plain text data. + /// + /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. + /// + /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. + /// + #[inline(always)] + pub fn decrypt_in_place( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + buffer: &mut [u8], + tag: &[u8] + ) -> Result<(), SymCryptError> + + { + + self.as_ref().decrypt_in_place(nonce, auth_data, buffer, tag) + } + + /// + /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. + /// This call cannot fail. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. + /// + /// `source` is a `&[u8]` that contains the plain text to be encrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. + /// `destination` must be of the same length as `source`. + /// + /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. + /// Tag sizes of 4 and 8 are not supported. + /// + #[inline(always)] + pub fn encrypt( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + source: &[u8], + destination: &mut [u8], + tag: &mut [u8] + ) + + { + + self.as_ref().encrypt(nonce, auth_data, source, destination, tag); + } + + /// + /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. This call cannot fail. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. + /// + /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, + /// `buffer` will be over-written to contain the cipher text data. + /// + /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. + /// Tag sizes of 4 and 8 are not supported. + /// + #[inline(always)] + pub fn encrypt_in_place( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + buffer: &mut [u8], + tag: &mut [u8] + ) + + { + + self.as_ref().encrypt_in_place(nonce, auth_data, buffer, tag); + } + +} + +impl<'a> Drop for GcmExpandedKeyHandle<'a> { + + fn drop(&mut self) { + + // + // SAFETY: Is is safe to uninitialize the underlying storage as this + // if the only reference to it and we are being dropped. + // + + unsafe { + self.0.zero_storage(); + } + } - // _pinned is a marker to ensure that instances of the inner state cannot be moved once pinned. - // This prevents the struct from implementing the Unpin trait, enforcing that any - // references to this structure remain valid throughout its lifetime. - _pinned: PhantomPinned, } -impl GcmInnerKey { - /// Creates a new GcmInnerKey and returns a pinned Box - fn new() -> Pin> { - Box::pin(GcmInnerKey { - inner: symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY::default(), - _pinned: PhantomPinned, - }) +/// +/// This type represents a borrowed handle to an initialized SYMCRYPT_GCM_EXPANDED_KEY +/// that is used to: +/// 1. Provide a guarantee that the underlying storage is initialized. +/// 2. Prevent the underlying storage from being moved or copied. +/// +/// This type does not zero the underlying storage when dropped. +/// +pub struct GcmExpandedKeyRef<'a>(&'a GcmExpandedKeyStorage); + +impl<'a> GcmExpandedKeyRef<'a> { + + /// + /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. + /// This call can fail and the caller must check the result. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. + /// + /// `source` is a `&[u8]` that contains the cipher text to be decrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. + /// `destination` must be of the same length as `source`. + /// + /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. + /// + /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. + /// + pub fn decrypt( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + source: &[u8], + destination: &mut [u8], + tag: &[u8] + ) -> Result<(), SymCryptError> + + { + + assert_eq!(source.len(), destination.len()); + + // + // SAFETY: The underlying SYMCRYPT_GCM_EXPANDED_KEY is guaranteed to be initialized + // by the caller of `GcmExpandedKeyHandle::new` and we have asserted that both `source` + // and `destination` are of the same length. + // + + unsafe { + let result = + symcrypt_sys::SymCryptGcmDecrypt( + self.0.get_key_ptr(), + nonce.as_ptr(), + nonce.len() as symcrypt_sys::SIZE_T, + auth_data.as_ptr(), + auth_data.len() as symcrypt_sys::SIZE_T, + source.as_ptr(), + destination.as_mut_ptr(), + destination.len() as symcrypt_sys::SIZE_T, + tag.as_ptr(), + tag.len() as symcrypt_sys::SIZE_T); + + match result { + symcrypt_sys::SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), + error => Err(error.into()) + } + } + } + + /// + /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. This call can fail and the caller must check the result. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. + /// + /// `buffer` is a `&mut [u8]` that contains the cipher text data to be decrypted. After the decryption has been completed, + /// `buffer` will be over-written to contain the plain text data. + /// + /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. + /// + /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. + /// + pub fn decrypt_in_place( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + buffer: &mut [u8], + tag: &[u8] + ) -> Result<(), SymCryptError> + + { + + // + // SAFETY: The underlying SYMCRYPT_GCM_EXPANDED_KEY is guaranteed to be initialized + // by the caller of `GcmExpandedKeyHandle::new`. + // + + unsafe { + let result = + symcrypt_sys::SymCryptGcmDecrypt( + self.0.get_key_ptr(), + nonce.as_ptr(), + nonce.len() as symcrypt_sys::SIZE_T, + auth_data.as_ptr(), + auth_data.len() as symcrypt_sys::SIZE_T, + buffer.as_ptr(), + buffer.as_mut_ptr(), + buffer.len() as symcrypt_sys::SIZE_T, + tag.as_ptr(), + tag.len() as symcrypt_sys::SIZE_T); + + match result { + symcrypt_sys::SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), + error => Err(error.into()) + } + } + } + + /// + /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. + /// This call cannot fail. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. + /// + /// `source` is a `&[u8]` that contains the plain text to be encrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. + /// `destination` must be of the same length as `source`. + /// + /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. + /// Tag sizes of 4 and 8 are not supported. + /// + pub fn encrypt( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + source: &[u8], + destination: &mut [u8], + tag: &mut [u8] + ) + + { + + assert_eq!(source.len(), destination.len()); + + // + // SAFETY: The underlying SYMCRYPT_GCM_EXPANDED_KEY is guaranteed to be initialized + // by the caller of `GcmExpandedKeyHandle::new` and we have asserted that both `source` + // and `destination` are of the same length. + // + + unsafe { + symcrypt_sys::SymCryptGcmEncrypt( + self.0.get_key_ptr(), + nonce.as_ptr(), + nonce.len() as symcrypt_sys::SIZE_T, + auth_data.as_ptr(), + auth_data.len() as symcrypt_sys::SIZE_T, + source.as_ptr(), + destination.as_mut_ptr(), + destination.len() as symcrypt_sys::SIZE_T, + tag.as_mut_ptr(), + tag.len() as symcrypt_sys::SIZE_T); + } + } + + /// + /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. This call cannot fail. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. + /// + /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. + /// + /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, + /// `buffer` will be over-written to contain the cipher text data. + /// + /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. + /// Tag sizes of 4 and 8 are not supported. + /// + pub fn encrypt_in_place( + &self, + nonce: &[u8; 12], + auth_data: &[u8], + buffer: &mut [u8], + tag: &mut [u8] + ) + + { + + // + // SAFETY: The underlying SYMCRYPT_GCM_EXPANDED_KEY is guaranteed to be initialized + // by the caller of `GcmExpandedKeyHandle::new`. + // + + unsafe { + symcrypt_sys::SymCryptGcmEncrypt( + self.0.get_key_ptr(), + nonce.as_ptr(), + nonce.len() as symcrypt_sys::SIZE_T, + auth_data.as_ptr(), + auth_data.len() as symcrypt_sys::SIZE_T, + buffer.as_ptr(), + buffer.as_mut_ptr(), + buffer.len() as symcrypt_sys::SIZE_T, + tag.as_mut_ptr(), + tag.len() as symcrypt_sys::SIZE_T); + } + } + +} + +impl<'a, 'b> From<&'a GcmExpandedKeyHandle<'b>> for GcmExpandedKeyRef<'a> { + + fn from(value: &'a GcmExpandedKeyHandle<'b>) -> Self { + GcmExpandedKeyRef(value.0) + } + +} + +impl<'a> From<&'a GcmExpandedKey> for GcmExpandedKeyRef<'a> { + + fn from(value: &'a GcmExpandedKey) -> Self { + GcmExpandedKeyRef(&value.expanded_key) + } + +} + +/// +/// This type represents an uninitialized SYMCRYPT_GCM_STATE. It can be freely +/// copied, clones, or moved as it does not contain any information. +/// +#[derive(Copy, Clone, Default)] +pub struct GcmStream(symcrypt_sys::SYMCRYPT_GCM_STATE); + +impl GcmStream { + + // + // `get_key_ptr_mut` gets a mutable pointer to the underlying SYMCRYPT_GCM_STATE. + // + #[inline(always)] + fn get_state_ptr_mut(&mut self) -> *mut symcrypt_sys::SYMCRYPT_GCM_STATE { + ptr::addr_of_mut!(self.0) + } + + // + // `initialize` initializes the underlying `SYMCRYPT_GCM_STATE`` with the provided key and nonce. + // + // `expanded_key` provides a borrowed reference to an initialized `SYMCRYPT_GCM_EXPANDED_KEY` + // + // `nonce` is a `&[u8; 12]` that is used as the nonce. + // + fn initialize<'a>( + &'a mut self, + expanded_key: GcmExpandedKeyRef<'a>, + nonce: &[u8; 12]) -> internal::GcmInitializedStream<'a> { + + // + // SAFETY: FFI call to initialize repr(C) struct. + // + + unsafe { + symcrypt_sys::SymCryptGcmInit( + self.get_state_ptr_mut(), + expanded_key.0.get_key_ptr(), + nonce.as_ptr(), + nonce.len() as symcrypt_sys::SIZE_T); + + internal::GcmInitializedStream::new(self) + } + } + + /// + /// Initializes this GcmStream as a GcmAuthStream using the provided key, and nonce. + /// + /// `expanded_key` is a `GcmExpandedKeyRef` that provides a handle to the key to use + /// for operations. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce. + /// + #[inline(always)] + pub fn as_auth_stream<'a>( + &'a mut self, + expanded_key: GcmExpandedKeyRef<'a>, + nonce: &[u8; 12]) -> GcmAuthStream<'a> { + + GcmAuthStream(self.initialize(expanded_key, nonce)) + } + + /// + /// Initializes this GcmStream as a GcmDecryptionStream using the provided key, and nonce. + /// + /// `expanded_key` is a `GcmExpandedKeyRef` that provides a handle to the key to use + /// for operations. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce. + /// + #[inline(always)] + pub fn as_decryption_stream<'a>( + &'a mut self, + expanded_key: GcmExpandedKeyRef<'a>, + nonce: &[u8; 12]) -> GcmDecryptionStream<'a> { + + GcmDecryptionStream(self.initialize(expanded_key, nonce)) + } + + /// + /// Initializes this GcmStream as a GcmEncryptionStream using the provided key, and nonce. + /// + /// `expanded_key` is a `GcmExpandedKeyRef` that provides a handle to the key to use + /// for operations. + /// + /// `nonce` is a `&[u8; 12]` that is used as the nonce. + /// + #[inline(always)] + pub fn as_encryption_stream<'a>( + &'a mut self, + expanded_key: GcmExpandedKeyRef<'a>, + nonce: &[u8; 12]) -> GcmEncryptionStream<'a> { + + GcmEncryptionStream(self.initialize(expanded_key, nonce)) + } + +} + +/// +/// This type represents a handle to an initialized GcmStream that can be used to autheticate, +/// but not encrypt or decrypt, data. It can later be converted to a GcmDecryptionStream or +/// GcmEncryptionStream. +/// +pub struct GcmAuthStream<'a>(internal::GcmInitializedStream<'a>); + +impl<'a> GcmAuthStream<'a> { + + /// + /// `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. + /// + #[inline(always)] + pub fn as_ref_mut(&mut self) -> GcmAuthStreamRefMut { + GcmAuthStreamRefMut(self.0.as_ref_mut()) + } + + /// + /// `authenticate` authenticates, but does not otherwise encrypt or decrypt, the provided data. + /// + /// `data` is a `&[u8]` that contains the data to authenticate. + /// + #[inline(always)] + pub fn authenticate(&mut self, data: &[u8]) { + self.as_ref_mut().authenticate(data); } - /// Provides a mutable pointer to the inner SymCrypt state. /// - /// This is primarily meant to be used while making calls to the underlying SymCrypt APIs. - /// The pointer returned is pinned and cannot be moved - /// This function returns pointer to pinned data, which means callers must not use the pointer to move the data out of its location. - fn get_inner_mut(self: Pin<&mut Self>) -> *mut symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY { - // SAFETY: Accessing the inner state of the pinned data - unsafe { &mut self.get_unchecked_mut().inner as *mut _ } + /// `to_decryption_stream` converts this GcmAuthStream into a GcmDecryptionStream + /// + #[inline(always)] + pub fn to_decryption_stream(self) -> GcmDecryptionStream<'a> { + GcmDecryptionStream(self.0) } - // Safe method to access the inner state immutably - pub(crate) fn get_inner(&self) -> *const symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY { - &self.inner as *const _ + /// + /// `to_encryption_stream` converts this GcmAuthStream into a GcmEncryptionStream + /// + #[inline(always)] + pub fn to_encryption_stream(self) -> GcmEncryptionStream<'a> { + GcmEncryptionStream(self.0) } + } -impl Drop for GcmInnerKey { - fn drop(&mut self) { +/// +/// This type represents a borrowed mutable handle to an initialized GcmStream that can be used to +/// autheticate, but not encrypt or decrypt, data. +/// +pub struct GcmAuthStreamRefMut<'a>(internal::GcmInitializedStreamRefMut<'a>); + +impl<'a> GcmAuthStreamRefMut<'a> { + + /// + /// `authenticate` authenticates, but does not otherwise encrypt or decrypt, the provided data. + /// + /// `data` is a `&[u8]` that contains the data to authenticate. + /// + pub fn authenticate(&mut self, data: &[u8]) { + + // + // SAFETY: The internal stream is guaranteed to still be initialized while + // self is alive. + // + unsafe { - // SAFETY: FFI calls - symcrypt_sys::SymCryptWipe( - ptr::addr_of_mut!(self.inner) as *mut c_void, // Using addr_of_mut! so we don't access in the inner field - mem::size_of_val(&self.inner) as symcrypt_sys::SIZE_T, // Using size_of_val! so we don't access in the inner field - ); + symcrypt_sys::SymCryptGcmAuthPart( + self.0.get_state_ptr_mut(), + data.as_ptr(), + data.len() as symcrypt_sys::SIZE_T); } } + } -/// `encrypt_in_place` and `decrypt_in_place` take in an allocated `buffer` as an in/out parameter for performance reasons. -/// This is for scenarios such as encrypting over a stream of data; allocating and copying data from a return will be costly performance wise. -impl GcmExpandedKey { - /// `new` takes in a reference to a key and a [`BlockCipherType`] and returns an expanded key that is Pin>'d. +/// +/// This type represents a handle to an initialized GcmStream that can be used to decrypt data. +/// +pub struct GcmDecryptionStream<'a>(internal::GcmInitializedStream<'a>); + +impl<'a> GcmDecryptionStream<'a> { + /// - /// This function can fail and will propagate the error back to the caller. This call will fail if the wrong key size is provided. + /// `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. + /// + #[inline(always)] + pub fn as_ref_mut(&mut self) -> GcmDecryptionStreamRefMut { + GcmDecryptionStreamRefMut(self.0.as_ref_mut()) + } + /// - /// The only accepted Cipher for GCM is [`BlockCipherType::AesBlock`] - pub fn new(key: &[u8], cipher: BlockCipherType) -> Result { - symcrypt_init(); - let mut expanded_key = GcmInnerKey::new(); // Get expanded_key that is already Pin>'d + /// `complete` finishes this decryption stream and validates that the provided tag matches + /// the generated tag. + /// + /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. + /// This is used to verify the integrity of the cipher text. + /// + pub fn complete(mut self, tag: &[u8]) -> Result<(), SymCryptError> { + + // + // SAFETY: The internal stream is guaranteed to still be initialized while + // self is alive. + // - // Use as_mut() to get a Pin<&mut GcmInnerKey> and then call get_inner_mut to get *mut - gcm_expand_key( - key, - expanded_key.as_mut().get_inner_mut(), - convert_cipher(cipher), - )?; - let gcm_expanded_key = GcmExpandedKey { - expanded_key, - key_length: key.len(), + let result = unsafe { + symcrypt_sys::SymCryptGcmDecryptFinal( + self.0.get_state_ptr_mut(), + tag.as_ptr(), + tag.len() as u64) }; - Ok(gcm_expanded_key) + + self.0.drop_without_zero(); + match result { + symcrypt_sys::SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), + error => Err(error.into()) + } } - /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. This call cannot fail. + /// + /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. + /// This is a partial decryption of the cipher text and the results of the plain text are not validated + /// until `complete` is called. + /// + /// `source` is a `&[u8]` that contains the cipher text to be decrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. + /// `destination` must be of the same length as `source`. /// - /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. + #[inline(always)] + pub fn decrypt(&mut self, source: &[u8], destination: &mut [u8]) { + self.as_ref_mut().decrypt(source, destination); + } + + /// + /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. + /// This is a partial decryption of the cipher text and the results of the plain text are not validated + /// until `complete` is called. + /// + /// `source` is a `&[u8]` that contains the cipher text to be decrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. + /// `destination` must be of the same length as `source`. + /// + #[inline(always)] + pub fn decrypt_in_place(&mut self, data: &mut [u8]) { + self.as_ref_mut().decrypt_in_place(data); + } + +} + +/// +/// This type represents a borrowed mutable handle to an initialized GcmStream that can be used to +/// decrypt data. +/// +pub struct GcmDecryptionStreamRefMut<'a>(internal::GcmInitializedStreamRefMut<'a>); + +impl<'a> GcmDecryptionStreamRefMut<'a> { + + /// + /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. + /// This is a partial decryption of the cipher text and the results of the plain text are not validated + /// until `complete` is called. + /// + /// `source` is a `&[u8]` that contains the cipher text to be decrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. + /// `destination` must be of the same length as `source`. /// - /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. + pub fn decrypt(&mut self, source: &[u8], destination: &mut [u8]) { + assert_eq!(source.len(), destination.len()); + + // + // SAFETY: The internal stream is guaranteed to still be initialized while + // self is alive and we've asserted that the source and destination buffers + // are the same length. + // + + unsafe { + symcrypt_sys::SymCryptGcmDecryptPart( + self.0.get_state_ptr_mut(), + source.as_ptr(), + destination.as_mut_ptr(), + destination.len() as symcrypt_sys::SIZE_T); + } + } + + /// + /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. + /// This is a partial decryption of the cipher text and the results of the plain text are not validated + /// until `complete` is called. + /// + /// `source` is a `&[u8]` that contains the cipher text to be decrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. + /// `destination` must be of the same length as `source`. + /// + pub fn decrypt_in_place(&mut self, data: &mut [u8]) { + + // + // SAFETY: The internal stream is guaranteed to still be initialized while + // self is alive. + // + + unsafe { + symcrypt_sys::SymCryptGcmDecryptPart( + self.0.get_state_ptr_mut(), + data.as_ptr(), + data.as_mut_ptr(), + data.len() as symcrypt_sys::SIZE_T); + } + } + +} + +/// +/// This type represents a handle to an initialized GcmStream that can be used to +/// encrypt data. +/// +pub struct GcmEncryptionStream<'a>(internal::GcmInitializedStream<'a>); + +impl<'a> GcmEncryptionStream<'a> { + /// - /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, - /// `buffer` will be over-written to contain the cipher text data. + /// `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. + /// + #[inline(always)] + pub fn as_ref_mut(&mut self) -> GcmEncryptionStreamRefMut { + GcmEncryptionStreamRefMut(self.0.as_ref_mut()) + } + /// - /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. + /// `complete` finishes this encryption stream and returns the generated tag for validating + /// decryption. + /// + /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. + /// Tag size must be 12, 13, 14, 15, 16 per SP800-38D. /// Tag sizes of 4 and 8 are not supported. - pub fn encrypt_in_place( - &self, - nonce: &[u8; 12], - auth_data: &[u8], - buffer: &mut [u8], - tag: &mut [u8], - ) { - symcrypt_init(); + /// + pub fn complete(mut self, tag: &mut [u8]) { + + // + // SAFETY: The internal stream is guaranteed to still be initialized while + // self is alive. + // + unsafe { - // SAFETY: FFI calls - symcrypt_sys::SymCryptGcmEncrypt( - self.expanded_key.get_inner(), - nonce.as_ptr(), - nonce.len() as symcrypt_sys::SIZE_T, - auth_data.as_ptr(), - auth_data.len() as symcrypt_sys::SIZE_T, - buffer.as_ptr(), - buffer.as_mut_ptr(), - buffer.len() as symcrypt_sys::SIZE_T, + symcrypt_sys::SymCryptGcmEncryptFinal( + self.0.get_state_ptr_mut(), tag.as_mut_ptr(), - tag.len() as symcrypt_sys::SIZE_T, - ); + tag.len() as u64); } + + self.0.drop_without_zero(); } - /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. This call can fail and the caller must check the result. + /// + /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. /// - /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. /// - /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. + /// `source` is a `&[u8]` that contains the plain text to be encrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. + /// `destination` must be of the same length as `source`. + /// + #[inline(always)] + pub fn encrypt(&mut self, source: &[u8], destination: &mut [u8]) { + self.as_ref_mut().encrypt(source, destination); + } + + /// + /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. /// - /// `buffer` is a `&mut [u8]` that contains the cipher text data to be decrypted. After the decryption has been completed, - /// `buffer` will be over-written to contain the plain text data. + /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, + /// `buffer` will be over-written to contain the cipher text data. + /// + #[inline(always)] + pub fn encrypt_in_place(&mut self, data: &mut [u8]) { + self.as_ref_mut().encrypt_in_place(data); + } + +} + +/// +/// This type represents a borrowed mutable handle to an initialized GcmStream that can be used to +/// encrypt data. +/// +pub struct GcmEncryptionStreamRefMut<'a>(internal::GcmInitializedStreamRefMut<'a>); + +impl<'a> GcmEncryptionStreamRefMut<'a> { + + /// + /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. /// - /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. /// - /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. - pub fn decrypt_in_place( - &self, - nonce: &[u8; 12], - auth_data: &[u8], - buffer: &mut [u8], - tag: &[u8], - ) -> Result<(), SymCryptError> { - symcrypt_init(); + /// `source` is a `&[u8]` that contains the plain text to be encrypted. + /// + /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. + /// `destination` must be of the same length as `source`. + /// + pub fn encrypt(&mut self, source: &[u8], destination: &mut [u8]) { + assert_eq!(source.len(), destination.len()); + + // + // SAFETY: The internal stream is guaranteed to still be initialized while + // self is alive and we've asserted that the source and destination buffers + // are the same length. + // + unsafe { - // SAFETY: FFI calls - match symcrypt_sys::SymCryptGcmDecrypt( - self.expanded_key.get_inner(), - nonce.as_ptr(), - nonce.len() as symcrypt_sys::SIZE_T, - auth_data.as_ptr(), - auth_data.len() as symcrypt_sys::SIZE_T, - buffer.as_ptr(), - buffer.as_mut_ptr(), - buffer.len() as symcrypt_sys::SIZE_T, - tag.as_ptr(), - tag.len() as symcrypt_sys::SIZE_T, - ) { - symcrypt_sys::SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), - err => Err(err.into()), - } + symcrypt_sys::SymCryptGcmEncryptPart( + self.0.get_state_ptr_mut(), + source.as_ptr(), + destination.as_mut_ptr(), + destination.len() as u64); } } - /// `key_len` returns a the length of the [`GcmExpandedKey`] as a `usize`. - pub fn key_len(&self) -> usize { - self.key_length - } -} - -// No custom Send / Sync impl. needed for GcmExpandedKey since the -// underlying data is a pointer to a SymCrypt struct that is not modified after it is created. -unsafe impl Send for GcmExpandedKey {} -unsafe impl Sync for GcmExpandedKey {} + /// + /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. + /// + /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, + /// `buffer` will be over-written to contain the cipher text data. + /// + pub fn encrypt_in_place(&mut self, data: &mut [u8]) { + + // + // SAFETY: The internal stream is guaranteed to still be initialized while + // self is alive. + // -// Internal function to expand the SymCrypt Gcm Key. -fn gcm_expand_key( - key: &[u8], - expanded_key: *mut symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY, - cipher: *const symcrypt_sys::SYMCRYPT_BLOCKCIPHER, -) -> Result<(), SymCryptError> { - unsafe { - // SAFETY: FFI calls - match symcrypt_sys::SymCryptGcmExpandKey( - expanded_key, - cipher, - key.as_ptr(), - key.len() as symcrypt_sys::SIZE_T, - ) { - symcrypt_sys::SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), - err => Err(err.into()), + unsafe { + symcrypt_sys::SymCryptGcmEncryptPart( + self.0.get_state_ptr_mut(), + data.as_ptr(), + data.as_mut_ptr(), + data.len() as u64); } } + } /// [`validate_gcm_parameters`] is a utility function that validates the input parameters for a GCM call. @@ -299,6 +1196,158 @@ pub fn validate_gcm_parameters( } } +mod internal { + + use std::{mem, ops::{Deref, DerefMut}}; + + use symcrypt_sys::{SymCryptWipe, SYMCRYPT_GCM_STATE}; + + use crate::errors::SymCryptError; + + use super::GcmStream; + + // Internal function to expand the SymCrypt Gcm Key. + pub fn gcm_expand_key( + key: &[u8], + expanded_key: *mut symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY, + cipher: *const symcrypt_sys::SYMCRYPT_BLOCKCIPHER, + ) -> Result<(), SymCryptError> { + unsafe { + // SAFETY: FFI calls + match symcrypt_sys::SymCryptGcmExpandKey( + expanded_key, + cipher, + key.as_ptr(), + key.len() as symcrypt_sys::SIZE_T, + ) { + symcrypt_sys::SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), + err => Err(err.into()), + } + } + } + + /// + /// This type represents a handle to an initialized GcmStream that + /// is used to: + /// 1. Provide a guarantee that the underlying storage is initialized. + /// 2. Prevent the underlying storage from being moved or copied. + /// 3. Zero the underlying storage when dropped. + /// + pub struct GcmInitializedStream<'a>(&'a mut GcmStream); + + impl<'a> GcmInitializedStream<'a> { + + // + // `new` creates a new handle to an initialized GcmStream. + // + // # Safety: + // + // The caller must ensure that the underlying storage has been correctly + // initialized. + // + pub unsafe fn new(inner: &'a mut GcmStream) -> Self { + Self(inner) + } + + // + // `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. + // + #[inline(always)] + pub fn as_ref_mut(&mut self) -> GcmInitializedStreamRefMut { + GcmInitializedStreamRefMut::new(self) + } + + // + // `drop_without_zero` will drop this handle to an GcmStream without + // zeroing out the underlying storage. The caller should ensure that + // the storage was (or will be) zeroed. + // + #[inline(always)] + pub fn drop_without_zero(self) { + mem::forget(self); + } + + } + + impl<'a> Drop for GcmInitializedStream<'a> { + + fn drop(&mut self) { + + // + // SAFETY: FFI calls to securly zero repr(C) structs + // + + unsafe { + SymCryptWipe( + self.0.get_state_ptr_mut() as *mut _, + mem::size_of::() as symcrypt_sys::SIZE_T); + } + } + + } + + impl<'a> Deref for GcmInitializedStream<'a> { + + type Target = GcmStream; + + fn deref(&self) -> &Self::Target { + self.0 + } + + } + + impl<'a> DerefMut for GcmInitializedStream<'a> { + + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } + + } + + + /// + /// This type represents a borrowed handle to an initialized GcmStream that + /// is used to: + /// 1. Provide a guarantee that the underlying storage is initialized. + /// 2. Prevent the underlying storage from being moved or copied. + /// + /// This type does not zero the underlying storage when dropped. + /// + pub struct GcmInitializedStreamRefMut<'a>(&'a mut GcmStream); + + impl<'a> GcmInitializedStreamRefMut<'a> { + + // + // `new` creates a new borrowed mutable reference to an initialized GcmStream + // from an existing owned reference. + // + #[inline(always)] + pub fn new(inner: &'a mut GcmInitializedStream) -> Self { + Self(inner.0) + } + + } + + impl<'a> Deref for GcmInitializedStreamRefMut<'a> { + + type Target = GcmStream; + + fn deref(&self) -> &Self::Target { + self.0 + } + + } + + impl<'a> DerefMut for GcmInitializedStreamRefMut<'a> { + + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } + + } + +} + #[cfg(test)] mod test { use super::*; @@ -425,4 +1474,138 @@ mod test { let gcm_state = GcmExpandedKey::new(&p_key, cipher).unwrap(); assert_eq!(gcm_state.key_len(), 16); } + + #[test] + fn test_invalid_aes_key() { + let key_data = &[]; + + let mut key_storage = GcmUninitializedKey::default(); + + match key_storage.expand_key(BlockCipherType::AesBlock, key_data) { + Err(SymCryptError::WrongKeySize) => {}, + Ok(_) => panic!("Incorrectly returned success when generating auth stream"), + Err(error) => panic!("Invalid result when generating auth stream: {:?}", error) + }; + } + + #[test] + fn test_encrypt_decrypt_part() -> Result<(), SymCryptError> { + let mut key = GcmUninitializedKey::default(); + let key = + key.expand_key( + BlockCipherType::AesBlock, + &hex::decode("feffe9928665731c6d6a8f9467308308").unwrap())?; + + let mut nonce = [0; 12]; + rand::fill(&mut nonce); + + let mut orig_data = [0; 1024]; + rand::fill(&mut orig_data); + + let (expected_encrypted, expected_tag) = { + let mut encrypted_data = orig_data; + let mut tag = [0; 16]; + + key.encrypt_in_place(&nonce, &[], &mut encrypted_data, &mut tag); + (encrypted_data, tag) + }; + + let mut gcm_stream = GcmStream::default(); + for chunk_size in 1..orig_data.len() { + let mut encryption_stream = + gcm_stream.as_encryption_stream( + key.as_ref(), + &nonce); + + let mut encrypted_data = [0; 1024]; + let mut tag = [0; 16]; + for (source, destination) in + orig_data.chunks(chunk_size) + .zip(encrypted_data.chunks_mut(chunk_size)) { + + encryption_stream.encrypt(source, destination); + } + + encryption_stream.complete(&mut tag); + assert_eq!(expected_encrypted, encrypted_data); + assert_eq!(expected_tag, tag); + } + + for chunk_size in 1..orig_data.len() { + let mut decryption_stream = + gcm_stream.as_decryption_stream( + key.as_ref(), + &nonce); + + let mut decrypted_data = [0; 1024]; + for (source, destination) in + expected_encrypted.chunks(chunk_size) + .zip(decrypted_data.chunks_mut(chunk_size)) { + + decryption_stream.decrypt(source, destination); + } + + decryption_stream.complete(&expected_tag)?; + assert_eq!(orig_data, decrypted_data); + } + + Ok(()) + } + + #[test] + fn test_encrypt_decrypt_part_inplace() -> Result<(), SymCryptError> { + let mut key = GcmUninitializedKey::default(); + let key = + key.expand_key( + BlockCipherType::AesBlock, + &hex::decode("feffe9928665731c6d6a8f9467308308").unwrap())?; + + let mut nonce = [0; 12]; + rand::fill(&mut nonce); + + let mut orig_data = [0; 1024]; + rand::fill(&mut orig_data); + + let (expected_encrypted, expected_tag) = { + let mut encrypted_data = orig_data; + let mut tag = [0; 16]; + key.encrypt_in_place(&nonce, &[], &mut encrypted_data, &mut tag); + (encrypted_data, tag) + }; + + let mut gcm_stream = GcmStream::default(); + for chunk_size in 1..orig_data.len() { + let mut encryption_stream = + gcm_stream.as_encryption_stream( + key.as_ref(), + &nonce); + + let mut encrypted_data = orig_data; + let mut tag = [0; 16]; + for window in encrypted_data.chunks_mut(chunk_size) { + encryption_stream.encrypt_in_place(window); + } + + encryption_stream.complete(&mut tag); + assert_eq!(expected_encrypted, encrypted_data); + assert_eq!(expected_tag, tag); + } + + for chunk_size in 1..orig_data.len() { + let mut decryption_stream = + gcm_stream.as_decryption_stream( + key.as_ref(), + &nonce); + + let mut decrypted_data = expected_encrypted; + for window in decrypted_data.chunks_mut(chunk_size) { + decryption_stream.decrypt_in_place(window); + } + + decryption_stream.complete(&expected_tag)?; + assert_eq!(orig_data, decrypted_data); + } + + Ok(()) + } } diff --git a/rust-symcrypt/src/gcm2.rs b/rust-symcrypt/src/gcm2.rs deleted file mode 100644 index c2381c34..00000000 --- a/rust-symcrypt/src/gcm2.rs +++ /dev/null @@ -1,1194 +0,0 @@ -use std::{mem, ptr::{addr_of, addr_of_mut}}; - -use internal::{GcmInitializedStream, GcmInitializedStreamRefMut}; -use symcrypt_sys::{SymCryptGcmAuthPart, SymCryptGcmDecrypt, SymCryptGcmDecryptFinal, SymCryptGcmDecryptPart, SymCryptGcmEncrypt, SymCryptGcmEncryptFinal, SymCryptGcmEncryptPart, SymCryptGcmExpandKey, SymCryptGcmInit, SymCryptWipe, SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR, SYMCRYPT_GCM_EXPANDED_KEY, SYMCRYPT_GCM_STATE}; - -use crate::{cipher::{convert_cipher, BlockCipherType}, errors::SymCryptError, symcrypt_init}; - - -/// -/// This type represents an uninitialized GcmExpandedKey storage. It can be freely -/// copied, cloned, or moved as it does not contain any information. -/// -#[derive(Clone, Copy, Default)] -pub struct GcmUnexpandedKey(SYMCRYPT_GCM_EXPANDED_KEY); - -impl GcmUnexpandedKey { - - // - // `get_key_ptr` gets a constant pointer to the underlying SYMCRYPT_GCM_EXPANDED_KEY. - // - #[inline(always)] - fn get_key_ptr(&self) -> *const SYMCRYPT_GCM_EXPANDED_KEY { - addr_of!(self.0) - } - - // - // `get_key_ptr_mut` gets a mutable pointer to the underlying SYMCRYPT_GCM_EXPANDED_KEY. - // - #[inline(always)] - fn get_key_ptr_mut(&mut self) -> *mut SYMCRYPT_GCM_EXPANDED_KEY { - addr_of_mut!(self.0) - } - - /// - /// `expand_key` will initialize this SYMCRYPT_GCM_EXPANDED_KEY to using the probided - /// cipher type and key. - /// - /// `cipher_type` is a `BlockCipherType` that determines the cipher to use for this key. - /// The only supported cipher type is [`BlockCipherType::AesBlock`] - /// - /// `key_data` is a `&[u8]` that contains the key to initialize with. - /// - pub fn expand_key(&mut self, cipher_type: BlockCipherType, key_data: &[u8]) -> Result { - symcrypt_init(); - let cipher = convert_cipher(cipher_type); - - // - // SAFETY: FFI call to initialize repr(C) struct. - // - - unsafe { - let result = - SymCryptGcmExpandKey( - self.get_key_ptr_mut(), - cipher, - key_data.as_ptr(), - key_data.len() as symcrypt_sys::SIZE_T); - - if result != SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR { - return Err(result.into()); - } - - Ok(GcmExpandedKey::new(self)) - } - } - -} - -/// -/// This type represents a handle to an initialized GcmExpandedKey that -/// is used to: -/// 1. Provide a guarantee that the underlying storage is initialized. -/// 2. Prevent the underlying storage from being moved or copied. -/// 3. Zero the underlying storage when dropped. -/// -pub struct GcmExpandedKey<'a>(&'a mut GcmUnexpandedKey); - -impl<'a> GcmExpandedKey<'a> { - - // - // # Safety: - // - // The caller must enture that the underlying storage has been correctly - // initialized. - // - #[inline(always)] - unsafe fn new(inner: &'a mut GcmUnexpandedKey) -> Self { - Self(inner) - } - - /// - /// Creates a borrowed reference to the underlying GcmExpandedKey storage. - /// - #[inline(always)] - pub fn as_ref(&self) -> GcmExpandedKeyRef { - GcmExpandedKeyRef::new(self) - } - - /// - /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. - /// This call can fail and the caller must check the result. - /// - /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. - /// - /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. - /// - /// `source` is a `&[u8]` that contains the cipher text to be decrypted. - /// - /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. - /// `destination` must be of the same length as `source`. - /// - /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. - /// - /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. - /// - #[inline(always)] - pub fn decrypt( - &self, - nonce: &[u8; 12], - auth_data: &[u8], - source: &[u8], - destination: &mut [u8], - tag: &[u8] - ) -> Result<(), SymCryptError> - - { - - self.as_ref().decrypt(nonce, auth_data, source, destination, tag) - } - - /// - /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. This call can fail and the caller must check the result. - /// - /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. - /// - /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. - /// - /// `buffer` is a `&mut [u8]` that contains the cipher text data to be decrypted. After the decryption has been completed, - /// `buffer` will be over-written to contain the plain text data. - /// - /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. - /// - /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. - /// - #[inline(always)] - pub fn decrypt_in_place( - &self, - nonce: &[u8; 12], - auth_data: &[u8], - buffer: &mut [u8], - tag: &[u8] - ) -> Result<(), SymCryptError> - - { - - self.as_ref().decrypt_in_place(nonce, auth_data, buffer, tag) - } - - /// - /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. - /// This call cannot fail. - /// - /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. - /// - /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. - /// - /// `source` is a `&[u8]` that contains the plain text to be encrypted. - /// - /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. - /// `destination` must be of the same length as `source`. - /// - /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. - /// Tag sizes of 4 and 8 are not supported. - /// - #[inline(always)] - pub fn encrypt( - &self, - nonce: &[u8; 12], - auth_data: &[u8], - source: &[u8], - destination: &mut [u8], - tag: &mut [u8] - ) - - { - - self.as_ref().encrypt(nonce, auth_data, source, destination, tag); - } - - /// - /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. This call cannot fail. - /// - /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. - /// - /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. - /// - /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, - /// `buffer` will be over-written to contain the cipher text data. - /// - /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. - /// Tag sizes of 4 and 8 are not supported. - /// - #[inline(always)] - pub fn encrypt_in_place( - &self, - nonce: &[u8; 12], - auth_data: &[u8], - buffer: &mut [u8], - tag: &mut [u8] - ) - - { - - self.as_ref().encrypt_in_place(nonce, auth_data, buffer, tag); - } - -} - -impl<'a> Drop for GcmExpandedKey<'a> { - - fn drop(&mut self) { - - // - // SAFETY: FFI calls to securly zero repr(C) structs - // - - unsafe { - SymCryptWipe( - self.0.get_key_ptr_mut() as *mut _, - mem::size_of::() as symcrypt_sys::SIZE_T); - } - } - -} - -/// -/// This type represents a borrowed handle to an initialized GcmExpandedKey that -/// is used to: -/// 1. Provide a guarantee that the underlying storage is initialized. -/// 2. Prevent the underlying storage from being moved or copied. -/// -/// This type does not zero the underlying storage when dropped. -/// -pub struct GcmExpandedKeyRef<'a>(&'a GcmUnexpandedKey); - -impl<'a> GcmExpandedKeyRef<'a> { - - /// - /// `new` creates a new borrowed handle to an initialized GcmExpandedKey. - /// - /// `expanded_key` is a `&GcmExpandedKey` that is the owning handle for the GcmExpandedKey to create a reference to. - /// - #[inline(always)] - pub fn new(expanded_key: &'a GcmExpandedKey) -> Self { - Self(expanded_key.0) - } - - /// - /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. - /// This call can fail and the caller must check the result. - /// - /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. - /// - /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. - /// - /// `source` is a `&[u8]` that contains the cipher text to be decrypted. - /// - /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. - /// `destination` must be of the same length as `source`. - /// - /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. - /// - /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. - /// - pub fn decrypt( - &self, - nonce: &[u8; 12], - auth_data: &[u8], - source: &[u8], - destination: &mut [u8], - tag: &[u8] - ) -> Result<(), SymCryptError> - - { - - assert_eq!(source.len(), destination.len()); - - // - // SAFETY: The underlying SYMCRYPT_GCM_EXPANDED_KEY is guaranteed to be initialized - // by the caller of `GcmExpandedKey::new` and we have asserted that both `source` and - // `destination` are of the same length. - // - - unsafe { - let result = - SymCryptGcmDecrypt( - self.0.get_key_ptr(), - nonce.as_ptr(), - nonce.len() as symcrypt_sys::SIZE_T, - auth_data.as_ptr(), - auth_data.len() as symcrypt_sys::SIZE_T, - source.as_ptr(), - destination.as_mut_ptr(), - destination.len() as symcrypt_sys::SIZE_T, - tag.as_ptr(), - tag.len() as symcrypt_sys::SIZE_T); - - match result { - SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), - error => Err(error.into()) - } - } - } - - /// - /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. This call can fail and the caller must check the result. - /// - /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. - /// - /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. - /// - /// `buffer` is a `&mut [u8]` that contains the cipher text data to be decrypted. After the decryption has been completed, - /// `buffer` will be over-written to contain the plain text data. - /// - /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. - /// - /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. - /// - pub fn decrypt_in_place( - &self, - nonce: &[u8; 12], - auth_data: &[u8], - buffer: &mut [u8], - tag: &[u8] - ) -> Result<(), SymCryptError> - - { - - // - // SAFETY: The underlying SYMCRYPT_GCM_EXPANDED_KEY is guaranteed to be initialized - // by the caller of `GcmExpandedKey::new`. - // - - unsafe { - let result = - SymCryptGcmDecrypt( - self.0.get_key_ptr(), - nonce.as_ptr(), - nonce.len() as symcrypt_sys::SIZE_T, - auth_data.as_ptr(), - auth_data.len() as symcrypt_sys::SIZE_T, - buffer.as_ptr(), - buffer.as_mut_ptr(), - buffer.len() as symcrypt_sys::SIZE_T, - tag.as_ptr(), - tag.len() as symcrypt_sys::SIZE_T); - - match result { - SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), - error => Err(error.into()) - } - } - } - - /// - /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. - /// This call cannot fail. - /// - /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. - /// - /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. - /// - /// `source` is a `&[u8]` that contains the plain text to be encrypted. - /// - /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. - /// `destination` must be of the same length as `source`. - /// - /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. - /// Tag sizes of 4 and 8 are not supported. - /// - pub fn encrypt( - &self, - nonce: &[u8; 12], - auth_data: &[u8], - source: &[u8], - destination: &mut [u8], - tag: &mut [u8] - ) - - { - - assert_eq!(source.len(), destination.len()); - - // - // SAFETY: The underlying SYMCRYPT_GCM_EXPANDED_KEY is guaranteed to be initialized - // by the caller of `GcmExpandedKey::new` and we have asserted that both `source` and - // `destination` are of the same length. - // - - unsafe { - SymCryptGcmEncrypt( - self.0.get_key_ptr(), - nonce.as_ptr(), - nonce.len() as symcrypt_sys::SIZE_T, - auth_data.as_ptr(), - auth_data.len() as symcrypt_sys::SIZE_T, - source.as_ptr(), - destination.as_mut_ptr(), - destination.len() as symcrypt_sys::SIZE_T, - tag.as_mut_ptr(), - tag.len() as symcrypt_sys::SIZE_T); - } - } - - /// - /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. This call cannot fail. - /// - /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. - /// - /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. - /// - /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, - /// `buffer` will be over-written to contain the cipher text data. - /// - /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. - /// Tag sizes of 4 and 8 are not supported. - /// - pub fn encrypt_in_place( - &self, - nonce: &[u8; 12], - auth_data: &[u8], - buffer: &mut [u8], - tag: &mut [u8] - ) - - { - - // - // SAFETY: The underlying SYMCRYPT_GCM_EXPANDED_KEY is guaranteed to be initialized - // by the caller of `GcmExpandedKey::new`. - // - - unsafe { - SymCryptGcmEncrypt( - self.0.get_key_ptr(), - nonce.as_ptr(), - nonce.len() as symcrypt_sys::SIZE_T, - auth_data.as_ptr(), - auth_data.len() as symcrypt_sys::SIZE_T, - buffer.as_ptr(), - buffer.as_mut_ptr(), - buffer.len() as symcrypt_sys::SIZE_T, - tag.as_mut_ptr(), - tag.len() as symcrypt_sys::SIZE_T); - } - } - -} - -/// -/// This type represents an uninitialized SYMCRYPT_GCM_STATE. It can be freely -/// copied, clones, or moved as it does not contain any information. -/// -#[derive(Copy, Clone, Default)] -pub struct GcmStream(SYMCRYPT_GCM_STATE); - -impl GcmStream { - - // - // `get_key_ptr_mut` gets a mutable pointer to the underlying SYMCRYPT_GCM_STATE. - // - #[inline(always)] - fn get_state_ptr_mut(&mut self) -> *mut SYMCRYPT_GCM_STATE { - addr_of_mut!(self.0) - } - - // - // `initialize` initializes the underlying `SYMCRYPT_GCM_STATE`` with the provided key and nonce. - // - // `expanded_key` provides a borrowed reference to an initialized `SYMCRYPT_GCM_EXPANDED_KEY` - // - // `nonce` is a `&[u8; 12]` that is used as the nonce. - // - fn initialize<'a>(&'a mut self, expanded_key: GcmExpandedKeyRef<'a>, nonce: &[u8; 12]) -> GcmInitializedStream<'a> { - - // - // SAFETY: FFI call to initialize repr(C) struct. - // - - unsafe { - SymCryptGcmInit( - self.get_state_ptr_mut(), - expanded_key.0.get_key_ptr(), - nonce.as_ptr(), - nonce.len() as symcrypt_sys::SIZE_T); - - GcmInitializedStream::new(self) - } - } - - /// - /// Initializes this GcmStream as a GcmAuthStream using the provided key, and nonce. - /// - /// `expanded_key` is a `GcmExpandedKeyRef` that provides a handle to the key to use - /// for operations. - /// - /// `nonce` is a `&[u8; 12]` that is used as the nonce. - /// - #[inline(always)] - pub fn as_auth_stream<'a>( - &'a mut self, - expanded_key: GcmExpandedKeyRef<'a>, - nonce: &[u8; 12]) -> GcmAuthStream<'a> { - - GcmAuthStream(self.initialize(expanded_key, nonce)) - } - - /// - /// Initializes this GcmStream as a GcmDecryptionStream using the provided key, and nonce. - /// - /// `expanded_key` is a `GcmExpandedKeyRef` that provides a handle to the key to use - /// for operations. - /// - /// `nonce` is a `&[u8; 12]` that is used as the nonce. - /// - #[inline(always)] - pub fn as_decryption_stream<'a>( - &'a mut self, - expanded_key: GcmExpandedKeyRef<'a>, - nonce: &[u8; 12]) -> GcmDecryptionStream<'a> { - - GcmDecryptionStream(self.initialize(expanded_key, nonce)) - } - - /// - /// Initializes this GcmStream as a GcmEncryptionStream using the provided key, and nonce. - /// - /// `expanded_key` is a `GcmExpandedKeyRef` that provides a handle to the key to use - /// for operations. - /// - /// `nonce` is a `&[u8; 12]` that is used as the nonce. - /// - #[inline(always)] - pub fn as_encryption_stream<'a>( - &'a mut self, - expanded_key: GcmExpandedKeyRef<'a>, - nonce: &[u8; 12]) -> GcmEncryptionStream<'a> { - - GcmEncryptionStream(self.initialize(expanded_key, nonce)) - } - -} - -/// -/// This type represents a handle to an initialized GcmStream that can be used to autheticate, -/// but not encrypt or decrypt, data. It can later be converted to a GcmDecryptionStream or -/// GcmEncryptionStream. -/// -pub struct GcmAuthStream<'a>(GcmInitializedStream<'a>); - -impl<'a> GcmAuthStream<'a> { - - /// - /// `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. - /// - #[inline(always)] - pub fn as_ref_mut(&mut self) -> GcmAuthStreamRefMut { - GcmAuthStreamRefMut(self.0.as_ref_mut()) - } - - /// - /// `authenticate` authenticates, but does not otherwise encrypt or decrypt, the provided data. - /// - /// `data` is a `&[u8]` that contains the data to authenticate. - /// - #[inline(always)] - pub fn authenticate(&mut self, data: &[u8]) { - self.as_ref_mut().authenticate(data); - } - - /// - /// `to_decryption_stream` converts this GcmAuthStream into a GcmDecryptionStream - /// - #[inline(always)] - pub fn to_decryption_stream(self) -> GcmDecryptionStream<'a> { - GcmDecryptionStream(self.0) - } - - /// - /// `to_encryption_stream` converts this GcmAuthStream into a GcmEncryptionStream - /// - #[inline(always)] - pub fn to_encryption_stream(self) -> GcmEncryptionStream<'a> { - GcmEncryptionStream(self.0) - } - -} - -/// -/// This type represents a borrowed mutable handle to an initialized GcmStream that can be used to -/// autheticate, but not encrypt or decrypt, data. -/// -pub struct GcmAuthStreamRefMut<'a>(GcmInitializedStreamRefMut<'a>); - -impl<'a> GcmAuthStreamRefMut<'a> { - - /// - /// `authenticate` authenticates, but does not otherwise encrypt or decrypt, the provided data. - /// - /// `data` is a `&[u8]` that contains the data to authenticate. - /// - pub fn authenticate(&mut self, data: &[u8]) { - - // - // SAFETY: The internal stream is guaranteed to still be initialized while - // self is alive. - // - - unsafe { - SymCryptGcmAuthPart( - self.0.get_state_ptr_mut(), - data.as_ptr(), - data.len() as symcrypt_sys::SIZE_T); - } - } - -} - -/// -/// This type represents a handle to an initialized GcmStream that can be used to decrypt data. -/// -pub struct GcmDecryptionStream<'a>(GcmInitializedStream<'a>); - -impl<'a> GcmDecryptionStream<'a> { - - /// - /// `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. - /// - #[inline(always)] - pub fn as_ref_mut(&mut self) -> GcmDecryptionStreamRefMut { - GcmDecryptionStreamRefMut(self.0.as_ref_mut()) - } - - /// - /// `complete` finishes this decryption stream and validates that the provided tag matches - /// the generated tag. - /// - /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. - /// This is used to verify the integrity of the cipher text. - /// - pub fn complete(mut self, tag: &[u8]) -> Result<(), SymCryptError> { - - // - // SAFETY: The internal stream is guaranteed to still be initialized while - // self is alive. - // - - let result = unsafe { - SymCryptGcmDecryptFinal( - self.0.get_state_ptr_mut(), - tag.as_ptr(), - tag.len() as u64) - }; - - self.0.drop_without_zero(); - match result { - SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), - error => Err(error.into()) - } - } - - /// - /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. - /// This is a partial decryption of the cipher text and the results of the plain text are not validated - /// until `complete` is called. - /// - /// `source` is a `&[u8]` that contains the cipher text to be decrypted. - /// - /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. - /// `destination` must be of the same length as `source`. - /// - #[inline(always)] - pub fn decrypt(&mut self, source: &[u8], destination: &mut [u8]) { - self.as_ref_mut().decrypt(source, destination); - } - - /// - /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. - /// This is a partial decryption of the cipher text and the results of the plain text are not validated - /// until `complete` is called. - /// - /// `source` is a `&[u8]` that contains the cipher text to be decrypted. - /// - /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. - /// `destination` must be of the same length as `source`. - /// - #[inline(always)] - pub fn decrypt_in_place(&mut self, data: &mut [u8]) { - self.as_ref_mut().decrypt_in_place(data); - } - -} - -/// -/// This type represents a borrowed mutable handle to an initialized GcmStream that can be used to -/// decrypt data. -/// -pub struct GcmDecryptionStreamRefMut<'a>(GcmInitializedStreamRefMut<'a>); - -impl<'a> GcmDecryptionStreamRefMut<'a> { - - /// - /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. - /// This is a partial decryption of the cipher text and the results of the plain text are not validated - /// until `complete` is called. - /// - /// `source` is a `&[u8]` that contains the cipher text to be decrypted. - /// - /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. - /// `destination` must be of the same length as `source`. - /// - pub fn decrypt(&mut self, source: &[u8], destination: &mut [u8]) { - assert_eq!(source.len(), destination.len()); - - // - // SAFETY: The internal stream is guaranteed to still be initialized while - // self is alive and we've asserted that the source and destination buffers - // are the same length. - // - - unsafe { - SymCryptGcmDecryptPart( - self.0.get_state_ptr_mut(), - source.as_ptr(), - destination.as_mut_ptr(), - destination.len() as symcrypt_sys::SIZE_T); - } - } - - /// - /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. - /// This is a partial decryption of the cipher text and the results of the plain text are not validated - /// until `complete` is called. - /// - /// `source` is a `&[u8]` that contains the cipher text to be decrypted. - /// - /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. - /// `destination` must be of the same length as `source`. - /// - pub fn decrypt_in_place(&mut self, data: &mut [u8]) { - - // - // SAFETY: The internal stream is guaranteed to still be initialized while - // self is alive. - // - - unsafe { - SymCryptGcmDecryptPart( - self.0.get_state_ptr_mut(), - data.as_ptr(), - data.as_mut_ptr(), - data.len() as symcrypt_sys::SIZE_T); - } - } - -} - -/// -/// This type represents a handle to an initialized GcmStream that can be used to -/// encrypt data. -/// -pub struct GcmEncryptionStream<'a>(GcmInitializedStream<'a>); - -impl<'a> GcmEncryptionStream<'a> { - - /// - /// `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. - /// - #[inline(always)] - pub fn as_ref_mut(&mut self) -> GcmEncryptionStreamRefMut { - GcmEncryptionStreamRefMut(self.0.as_ref_mut()) - } - - /// - /// `complete` finishes this encryption stream and returns the generated tag for validating - /// decryption. - /// - /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. - /// Tag size must be 12, 13, 14, 15, 16 per SP800-38D. - /// Tag sizes of 4 and 8 are not supported. - /// - pub fn complete(mut self, tag: &mut [u8]) { - - // - // SAFETY: The internal stream is guaranteed to still be initialized while - // self is alive. - // - - unsafe { - SymCryptGcmEncryptFinal( - self.0.get_state_ptr_mut(), - tag.as_mut_ptr(), - tag.len() as u64); - } - - self.0.drop_without_zero(); - } - - /// - /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. - /// - /// - /// `source` is a `&[u8]` that contains the plain text to be encrypted. - /// - /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. - /// `destination` must be of the same length as `source`. - /// - #[inline(always)] - pub fn encrypt(&mut self, source: &[u8], destination: &mut [u8]) { - self.as_ref_mut().encrypt(source, destination); - } - - /// - /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. - /// - /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, - /// `buffer` will be over-written to contain the cipher text data. - /// - #[inline(always)] - pub fn encrypt_in_place(&mut self, data: &mut [u8]) { - self.as_ref_mut().encrypt_in_place(data); - } - -} - -/// -/// This type represents a borrowed mutable handle to an initialized GcmStream that can be used to -/// encrypt data. -/// -pub struct GcmEncryptionStreamRefMut<'a>(GcmInitializedStreamRefMut<'a>); - -impl<'a> GcmEncryptionStreamRefMut<'a> { - - /// - /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. - /// - /// - /// `source` is a `&[u8]` that contains the plain text to be encrypted. - /// - /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. - /// `destination` must be of the same length as `source`. - /// - pub fn encrypt(&mut self, source: &[u8], destination: &mut [u8]) { - assert_eq!(source.len(), destination.len()); - - // - // SAFETY: The internal stream is guaranteed to still be initialized while - // self is alive and we've asserted that the source and destination buffers - // are the same length. - // - - unsafe { - SymCryptGcmEncryptPart( - self.0.get_state_ptr_mut(), - source.as_ptr(), - destination.as_mut_ptr(), - destination.len() as u64); - } - } - - /// - /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. - /// - /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, - /// `buffer` will be over-written to contain the cipher text data. - /// - pub fn encrypt_in_place(&mut self, data: &mut [u8]) { - - // - // SAFETY: The internal stream is guaranteed to still be initialized while - // self is alive. - // - - unsafe { - SymCryptGcmEncryptPart( - self.0.get_state_ptr_mut(), - data.as_ptr(), - data.as_mut_ptr(), - data.len() as u64); - } - } - -} - -mod internal { - - use std::{mem, ops::{Deref, DerefMut}}; - - use symcrypt_sys::{SymCryptWipe, SYMCRYPT_GCM_STATE}; - - use super::GcmStream; - - /// - /// This type represents a handle to an initialized GcmStream that - /// is used to: - /// 1. Provide a guarantee that the underlying storage is initialized. - /// 2. Prevent the underlying storage from being moved or copied. - /// 3. Zero the underlying storage when dropped. - /// - pub struct GcmInitializedStream<'a>(&'a mut GcmStream); - - impl<'a> GcmInitializedStream<'a> { - - // - // `new` creates a new handle to an initialized GcmStream. - // - // # Safety: - // - // The caller must ensure that the underlying storage has been correctly - // initialized. - // - pub unsafe fn new(inner: &'a mut GcmStream) -> Self { - Self(inner) - } - - // - // `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. - // - #[inline(always)] - pub fn as_ref_mut(&mut self) -> GcmInitializedStreamRefMut { - GcmInitializedStreamRefMut::new(self) - } - - // - // `drop_without_zero` will drop this handle to an GcmStream without - // zeroing out the underlying storage. The caller should ensure that - // the storage was (or will be) zeroed. - // - #[inline(always)] - pub fn drop_without_zero(self) { - mem::forget(self); - } - - } - - impl<'a> Drop for GcmInitializedStream<'a> { - - fn drop(&mut self) { - - // - // SAFETY: FFI calls to securly zero repr(C) structs - // - - unsafe { - SymCryptWipe( - self.0.get_state_ptr_mut() as *mut _, - mem::size_of::() as symcrypt_sys::SIZE_T); - } - } - - } - - impl<'a> Deref for GcmInitializedStream<'a> { - - type Target = GcmStream; - - fn deref(&self) -> &Self::Target { - self.0 - } - - } - - impl<'a> DerefMut for GcmInitializedStream<'a> { - - fn deref_mut(&mut self) -> &mut Self::Target { - self.0 - } - - } - - - /// - /// This type represents a borrowed handle to an initialized GcmStream that - /// is used to: - /// 1. Provide a guarantee that the underlying storage is initialized. - /// 2. Prevent the underlying storage from being moved or copied. - /// - /// This type does not zero the underlying storage when dropped. - /// - pub struct GcmInitializedStreamRefMut<'a>(&'a mut GcmStream); - - impl<'a> GcmInitializedStreamRefMut<'a> { - - // - // `new` creates a new borrowed mutable reference to an initialized GcmStream - // from an existing owned reference. - // - #[inline(always)] - pub fn new(inner: &'a mut GcmInitializedStream) -> Self { - Self(inner.0) - } - - } - - impl<'a> Deref for GcmInitializedStreamRefMut<'a> { - - type Target = GcmStream; - - fn deref(&self) -> &Self::Target { - self.0 - } - - } - - impl<'a> DerefMut for GcmInitializedStreamRefMut<'a> { - - fn deref_mut(&mut self) -> &mut Self::Target { - self.0 - } - - } - -} - -#[cfg(test)] -mod test { - - use crate::{cipher::BlockCipherType, errors::SymCryptError}; - - use super::{GcmStream, GcmUnexpandedKey}; - - #[test] - fn test_invalid_aes_key() { - let key_data = &[]; - - let mut key_storage = GcmUnexpandedKey::default(); - - match key_storage.expand_key(BlockCipherType::AesBlock, key_data) { - Err(SymCryptError::WrongKeySize) => {}, - Ok(_) => panic!("Incorrectly returned success when generating auth stream"), - Err(error) => panic!("Invalid result when generating auth stream: {:?}", error) - }; - } - - #[test] - fn test_encrypt_decrypt_part() -> Result<(), SymCryptError> { - let mut key = GcmUnexpandedKey::default(); - let key = - key.expand_key( - BlockCipherType::AesBlock, - &hex::decode("feffe9928665731c6d6a8f9467308308").unwrap())?; - - let mut nonce = [0; 12]; - rand::fill(&mut nonce); - - let mut orig_data = [0; 1024]; - rand::fill(&mut orig_data); - - let (expected_encrypted, expected_tag) = { - let mut encrypted_data = orig_data; - let mut tag = [0; 16]; - - key.encrypt_in_place(&nonce, &[], &mut encrypted_data, &mut tag); - (encrypted_data, tag) - }; - - let mut gcm_stream = GcmStream::default(); - for chunk_size in 1..orig_data.len() { - let mut encryption_stream = - gcm_stream.as_encryption_stream( - key.as_ref(), - &nonce); - - let mut encrypted_data = [0; 1024]; - let mut tag = [0; 16]; - for (source, destination) in - orig_data.chunks(chunk_size) - .zip(encrypted_data.chunks_mut(chunk_size)) { - - encryption_stream.encrypt(source, destination); - } - - encryption_stream.complete(&mut tag); - assert_eq!(expected_encrypted, encrypted_data); - assert_eq!(expected_tag, tag); - } - - for chunk_size in 1..orig_data.len() { - let mut decryption_stream = - gcm_stream.as_decryption_stream( - key.as_ref(), - &nonce); - - let mut decrypted_data = [0; 1024]; - for (source, destination) in - expected_encrypted.chunks(chunk_size) - .zip(decrypted_data.chunks_mut(chunk_size)) { - - decryption_stream.decrypt(source, destination); - } - - decryption_stream.complete(&expected_tag)?; - assert_eq!(orig_data, decrypted_data); - } - - Ok(()) - } - - #[test] - fn test_encrypt_decrypt_part_inplace() -> Result<(), SymCryptError> { - let mut key = GcmUnexpandedKey::default(); - let key = - key.expand_key( - BlockCipherType::AesBlock, - &hex::decode("feffe9928665731c6d6a8f9467308308").unwrap())?; - - let mut nonce = [0; 12]; - rand::fill(&mut nonce); - - let mut orig_data = [0; 1024]; - rand::fill(&mut orig_data); - - let (expected_encrypted, expected_tag) = { - let mut encrypted_data = orig_data; - let mut tag = [0; 16]; - key.encrypt_in_place(&nonce, &[], &mut encrypted_data, &mut tag); - (encrypted_data, tag) - }; - - let mut gcm_stream = GcmStream::default(); - for chunk_size in 1..orig_data.len() { - let mut encryption_stream = - gcm_stream.as_encryption_stream( - key.as_ref(), - &nonce); - - let mut encrypted_data = orig_data; - let mut tag = [0; 16]; - for window in encrypted_data.chunks_mut(chunk_size) { - encryption_stream.encrypt_in_place(window); - } - - encryption_stream.complete(&mut tag); - assert_eq!(expected_encrypted, encrypted_data); - assert_eq!(expected_tag, tag); - } - - for chunk_size in 1..orig_data.len() { - let mut decryption_stream = - gcm_stream.as_decryption_stream( - key.as_ref(), - &nonce); - - let mut decrypted_data = expected_encrypted; - for window in decrypted_data.chunks_mut(chunk_size) { - decryption_stream.decrypt_in_place(window); - } - - decryption_stream.complete(&expected_tag)?; - assert_eq!(orig_data, decrypted_data); - } - - Ok(()) - } - - #[test] - fn test_gcm_decrypt_will_fail_wrong_tag() { - let p_key = hex::decode("feffe9928665731c6d6a8f9467308308").unwrap(); - let mut nonce_array = [0u8; 12]; - hex::decode_to_slice("cafebabefacedbaddecaf888", &mut nonce_array).unwrap(); - let auth_data = hex::decode("feedfacedeadbeeffeedfacedeadbeefabaddad2").unwrap(); - - let mut tag = [0u8; 16]; - hex::decode_to_slice("5bc94fbc3221a5db94fae95ae7121aaa", &mut tag).unwrap(); - - let mut buffer = [0u8; 60]; - hex::decode_to_slice("42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091", &mut buffer).unwrap(); - let cipher = BlockCipherType::AesBlock; - - let mut key = GcmUnexpandedKey::default(); - let gcm_state = key.expand_key(cipher, &p_key).unwrap(); - let result = gcm_state.decrypt_in_place(&nonce_array, &auth_data, &mut buffer, &tag); - - match result { - Ok(_) => { - panic!("Test passed when it should fail"); - } - Err(err) => { - assert_eq!(err, SymCryptError::AuthenticationFailure); - } - } - } - -} diff --git a/rust-symcrypt/src/lib.rs b/rust-symcrypt/src/lib.rs index 2445384d..5ea0b00e 100644 --- a/rust-symcrypt/src/lib.rs +++ b/rust-symcrypt/src/lib.rs @@ -6,7 +6,6 @@ pub mod cipher; pub mod ecc; pub mod errors; pub mod gcm; -pub mod gcm2; pub mod hash; pub mod hkdf; pub mod hmac; From f1208f49b2ebf15829d6d1768df34bbe4a69bfd0 Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Tue, 11 Feb 2025 10:59:25 -0800 Subject: [PATCH 4/6] Apply clippy/fmt + linux build fixes --- rust-symcrypt/src/gcm.rs | 558 +++++++++++++++++---------------------- 1 file changed, 241 insertions(+), 317 deletions(-) diff --git a/rust-symcrypt/src/gcm.rs b/rust-symcrypt/src/gcm.rs index 242f7ae7..83170e78 100644 --- a/rust-symcrypt/src/gcm.rs +++ b/rust-symcrypt/src/gcm.rs @@ -70,12 +70,11 @@ use symcrypt_sys; /// /// This type represents a common storage for SYMCRYPT_GCM_EXPANDED_KEY. -/// +/// #[derive(Clone, Copy, Default)] struct GcmExpandedKeyStorage(symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY); impl GcmExpandedKeyStorage { - // // `get_key_ptr` gets a constant pointer to the underlying SYMCRYPT_GCM_EXPANDED_KEY. // @@ -102,46 +101,47 @@ impl GcmExpandedKeyStorage { unsafe fn zero_storage(&mut self) { symcrypt_sys::SymCryptWipe( self.get_key_ptr_mut() as *mut _, - mem::size_of::() as symcrypt_sys::SIZE_T); + mem::size_of::() as symcrypt_sys::SIZE_T, + ); } - } /// /// This type represents an uninitialized GcmExpandedKeyStorage. It can be freely /// copied, cloned, or moved as it does not contain any information. -/// +/// #[derive(Clone, Copy, Default)] pub struct GcmUninitializedKey(GcmExpandedKeyStorage); impl GcmUninitializedKey { - /// /// `expand_key` will initialize this SYMCRYPT_GCM_EXPANDED_KEY to using the probided /// cipher type and key. - /// + /// /// `cipher_type` is a `BlockCipherType` that determines the cipher to use for this key. /// The only supported cipher type is [`BlockCipherType::AesBlock`] - /// + /// /// `key_data` is a `&[u8]` that contains the key to initialize with. - /// - pub fn expand_key(&mut self, cipher_type: BlockCipherType, key_data: &[u8]) -> Result { + /// + pub fn expand_key( + &mut self, + cipher_type: BlockCipherType, + key_data: &[u8], + ) -> Result { symcrypt_init(); - + internal::gcm_expand_key( key_data, self.0.get_key_ptr_mut(), - convert_cipher(cipher_type))?; + convert_cipher(cipher_type), + )?; // // SAFETY: gcm_expand_key guarantees that the SYMCRYPT_GCM_EXPANDED_KEY storage is initialized on success. // - unsafe { - Ok(GcmExpandedKeyHandle::new(self)) - } + unsafe { Ok(GcmExpandedKeyHandle::new(self)) } } - } /// [`GcmExpandedKey`] is a struct that holds the Gcm expanded key from SymCrypt. @@ -158,9 +158,7 @@ pub struct GcmExpandedKey { } impl Drop for GcmExpandedKey { - fn drop(&mut self) { - // // SAFETY: Is is safe to uninitialize the underlying storage as this // if the only reference to it and we are being dropped. @@ -170,7 +168,6 @@ impl Drop for GcmExpandedKey { self.expanded_key.zero_storage(); } } - } /// `encrypt_in_place` and `decrypt_in_place` take in an allocated `buffer` as an in/out parameter for performance reasons. @@ -186,11 +183,7 @@ impl GcmExpandedKey { let mut expanded_key = Box::new(GcmExpandedKeyStorage::default()); // Get expanded_key that is already Pin>'d // Use as_mut() to get a Pin<&mut GcmInnerKey> and then call get_inner_mut to get *mut - internal::gcm_expand_key( - key, - expanded_key.get_key_ptr_mut(), - convert_cipher(cipher), - )?; + internal::gcm_expand_key(key, expanded_key.get_key_ptr_mut(), convert_cipher(cipher))?; let gcm_expanded_key = GcmExpandedKey { expanded_key, key_length: key.len(), @@ -200,13 +193,13 @@ impl GcmExpandedKey { /// /// Creates a borrowed reference to the underlying GcmExpandedKeyStorage. - /// + /// #[inline(always)] pub fn as_ref(&self) -> GcmExpandedKeyRef { self.into() } - /// + /// /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. /// This call cannot fail. /// @@ -215,13 +208,13 @@ impl GcmExpandedKey { /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. /// /// `source` is a `&[u8]` that contains the plain text to be encrypted. - /// + /// /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. /// `destination` must be of the same length as `source`. /// /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. /// Tag sizes of 4 and 8 are not supported. - /// + /// #[inline(always)] pub fn encrypt( &self, @@ -229,12 +222,10 @@ impl GcmExpandedKey { auth_data: &[u8], source: &[u8], destination: &mut [u8], - tag: &mut [u8] - ) - - { - - self.as_ref().encrypt(nonce, auth_data, source, destination, tag); + tag: &mut [u8], + ) { + self.as_ref() + .encrypt(nonce, auth_data, source, destination, tag); } /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. This call cannot fail. @@ -256,11 +247,11 @@ impl GcmExpandedKey { buffer: &mut [u8], tag: &mut [u8], ) { - - self.as_ref().encrypt_in_place(nonce, auth_data, buffer, tag); + self.as_ref() + .encrypt_in_place(nonce, auth_data, buffer, tag); } - /// + /// /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. /// This call can fail and the caller must check the result. /// @@ -269,14 +260,14 @@ impl GcmExpandedKey { /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. /// /// `source` is a `&[u8]` that contains the cipher text to be decrypted. - /// + /// /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. /// `destination` must be of the same length as `source`. /// /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. /// /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. - /// + /// #[inline(always)] pub fn decrypt( &self, @@ -284,12 +275,10 @@ impl GcmExpandedKey { auth_data: &[u8], source: &[u8], destination: &mut [u8], - tag: &[u8] - ) -> Result<(), SymCryptError> - - { - - self.as_ref().decrypt(nonce, auth_data, source, destination, tag) + tag: &[u8], + ) -> Result<(), SymCryptError> { + self.as_ref() + .decrypt(nonce, auth_data, source, destination, tag) } /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. This call can fail and the caller must check the result. @@ -312,8 +301,8 @@ impl GcmExpandedKey { buffer: &mut [u8], tag: &[u8], ) -> Result<(), SymCryptError> { - - self.as_ref().decrypt_in_place(nonce, auth_data, buffer, tag) + self.as_ref() + .decrypt_in_place(nonce, auth_data, buffer, tag) } /// `key_len` returns a the length of the [`GcmExpandedKey`] as a `usize`. @@ -333,11 +322,10 @@ unsafe impl Sync for GcmExpandedKey {} /// 1. Provide a guarantee that the underlying storage is initialized. /// 2. Prevent the underlying storage from being moved or copied. /// 3. Zero the underlying storage when dropped. -/// +/// pub struct GcmExpandedKeyHandle<'a>(&'a mut GcmExpandedKeyStorage); impl<'a> GcmExpandedKeyHandle<'a> { - // // # Safety: // @@ -351,13 +339,13 @@ impl<'a> GcmExpandedKeyHandle<'a> { /// /// Creates a borrowed reference to the underlying GcmUnexpandedKey storage. - /// + /// #[inline(always)] pub fn as_ref(&self) -> GcmExpandedKeyRef { self.into() } - /// + /// /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. /// This call can fail and the caller must check the result. /// @@ -366,14 +354,14 @@ impl<'a> GcmExpandedKeyHandle<'a> { /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. /// /// `source` is a `&[u8]` that contains the cipher text to be decrypted. - /// + /// /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. /// `destination` must be of the same length as `source`. /// /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. /// /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. - /// + /// #[inline(always)] pub fn decrypt( &self, @@ -381,15 +369,13 @@ impl<'a> GcmExpandedKeyHandle<'a> { auth_data: &[u8], source: &[u8], destination: &mut [u8], - tag: &[u8] - ) -> Result<(), SymCryptError> - - { - - self.as_ref().decrypt(nonce, auth_data, source, destination, tag) + tag: &[u8], + ) -> Result<(), SymCryptError> { + self.as_ref() + .decrypt(nonce, auth_data, source, destination, tag) } - /// + /// /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. This call can fail and the caller must check the result. /// /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. @@ -402,22 +388,20 @@ impl<'a> GcmExpandedKeyHandle<'a> { /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. /// /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. - /// + /// #[inline(always)] pub fn decrypt_in_place( &self, nonce: &[u8; 12], auth_data: &[u8], buffer: &mut [u8], - tag: &[u8] - ) -> Result<(), SymCryptError> - - { - - self.as_ref().decrypt_in_place(nonce, auth_data, buffer, tag) + tag: &[u8], + ) -> Result<(), SymCryptError> { + self.as_ref() + .decrypt_in_place(nonce, auth_data, buffer, tag) } - /// + /// /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. /// This call cannot fail. /// @@ -426,13 +410,13 @@ impl<'a> GcmExpandedKeyHandle<'a> { /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. /// /// `source` is a `&[u8]` that contains the plain text to be encrypted. - /// + /// /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. /// `destination` must be of the same length as `source`. /// /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. /// Tag sizes of 4 and 8 are not supported. - /// + /// #[inline(always)] pub fn encrypt( &self, @@ -440,15 +424,13 @@ impl<'a> GcmExpandedKeyHandle<'a> { auth_data: &[u8], source: &[u8], destination: &mut [u8], - tag: &mut [u8] - ) - - { - - self.as_ref().encrypt(nonce, auth_data, source, destination, tag); + tag: &mut [u8], + ) { + self.as_ref() + .encrypt(nonce, auth_data, source, destination, tag); } - /// + /// /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. This call cannot fail. /// /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. @@ -460,37 +442,31 @@ impl<'a> GcmExpandedKeyHandle<'a> { /// /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. /// Tag sizes of 4 and 8 are not supported. - /// + /// #[inline(always)] pub fn encrypt_in_place( &self, nonce: &[u8; 12], auth_data: &[u8], buffer: &mut [u8], - tag: &mut [u8] - ) - - { - - self.as_ref().encrypt_in_place(nonce, auth_data, buffer, tag); + tag: &mut [u8], + ) { + self.as_ref() + .encrypt_in_place(nonce, auth_data, buffer, tag); } - } -impl<'a> Drop for GcmExpandedKeyHandle<'a> { - +impl Drop for GcmExpandedKeyHandle<'_> { fn drop(&mut self) { - // // SAFETY: Is is safe to uninitialize the underlying storage as this // if the only reference to it and we are being dropped. // - + unsafe { self.0.zero_storage(); } } - } /// @@ -498,14 +474,13 @@ impl<'a> Drop for GcmExpandedKeyHandle<'a> { /// that is used to: /// 1. Provide a guarantee that the underlying storage is initialized. /// 2. Prevent the underlying storage from being moved or copied. -/// +/// /// This type does not zero the underlying storage when dropped. -/// +/// pub struct GcmExpandedKeyRef<'a>(&'a GcmExpandedKeyStorage); -impl<'a> GcmExpandedKeyRef<'a> { - - /// +impl GcmExpandedKeyRef<'_> { + /// /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. /// This call can fail and the caller must check the result. /// @@ -514,25 +489,22 @@ impl<'a> GcmExpandedKeyRef<'a> { /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. /// /// `source` is a `&[u8]` that contains the cipher text to be decrypted. - /// + /// /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. /// `destination` must be of the same length as `source`. /// /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. /// /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. - /// + /// pub fn decrypt( &self, nonce: &[u8; 12], auth_data: &[u8], source: &[u8], destination: &mut [u8], - tag: &[u8] - ) -> Result<(), SymCryptError> - - { - + tag: &[u8], + ) -> Result<(), SymCryptError> { assert_eq!(source.len(), destination.len()); // @@ -542,27 +514,27 @@ impl<'a> GcmExpandedKeyRef<'a> { // unsafe { - let result = - symcrypt_sys::SymCryptGcmDecrypt( - self.0.get_key_ptr(), - nonce.as_ptr(), - nonce.len() as symcrypt_sys::SIZE_T, - auth_data.as_ptr(), - auth_data.len() as symcrypt_sys::SIZE_T, - source.as_ptr(), - destination.as_mut_ptr(), - destination.len() as symcrypt_sys::SIZE_T, - tag.as_ptr(), - tag.len() as symcrypt_sys::SIZE_T); + let result = symcrypt_sys::SymCryptGcmDecrypt( + self.0.get_key_ptr(), + nonce.as_ptr(), + nonce.len() as symcrypt_sys::SIZE_T, + auth_data.as_ptr(), + auth_data.len() as symcrypt_sys::SIZE_T, + source.as_ptr(), + destination.as_mut_ptr(), + destination.len() as symcrypt_sys::SIZE_T, + tag.as_ptr(), + tag.len() as symcrypt_sys::SIZE_T, + ); match result { symcrypt_sys::SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), - error => Err(error.into()) + error => Err(error.into()), } } } - /// + /// /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. This call can fail and the caller must check the result. /// /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. @@ -575,44 +547,41 @@ impl<'a> GcmExpandedKeyRef<'a> { /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. /// /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. - /// + /// pub fn decrypt_in_place( &self, nonce: &[u8; 12], auth_data: &[u8], buffer: &mut [u8], - tag: &[u8] - ) -> Result<(), SymCryptError> - - { - + tag: &[u8], + ) -> Result<(), SymCryptError> { // // SAFETY: The underlying SYMCRYPT_GCM_EXPANDED_KEY is guaranteed to be initialized // by the caller of `GcmExpandedKeyHandle::new`. // unsafe { - let result = - symcrypt_sys::SymCryptGcmDecrypt( - self.0.get_key_ptr(), - nonce.as_ptr(), - nonce.len() as symcrypt_sys::SIZE_T, - auth_data.as_ptr(), - auth_data.len() as symcrypt_sys::SIZE_T, - buffer.as_ptr(), - buffer.as_mut_ptr(), - buffer.len() as symcrypt_sys::SIZE_T, - tag.as_ptr(), - tag.len() as symcrypt_sys::SIZE_T); + let result = symcrypt_sys::SymCryptGcmDecrypt( + self.0.get_key_ptr(), + nonce.as_ptr(), + nonce.len() as symcrypt_sys::SIZE_T, + auth_data.as_ptr(), + auth_data.len() as symcrypt_sys::SIZE_T, + buffer.as_ptr(), + buffer.as_mut_ptr(), + buffer.len() as symcrypt_sys::SIZE_T, + tag.as_ptr(), + tag.len() as symcrypt_sys::SIZE_T, + ); match result { symcrypt_sys::SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), - error => Err(error.into()) + error => Err(error.into()), } } } - /// + /// /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. /// This call cannot fail. /// @@ -621,24 +590,21 @@ impl<'a> GcmExpandedKeyRef<'a> { /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. /// /// `source` is a `&[u8]` that contains the plain text to be encrypted. - /// + /// /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. /// `destination` must be of the same length as `source`. /// /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. /// Tag sizes of 4 and 8 are not supported. - /// + /// pub fn encrypt( &self, nonce: &[u8; 12], auth_data: &[u8], source: &[u8], destination: &mut [u8], - tag: &mut [u8] - ) - - { - + tag: &mut [u8], + ) { assert_eq!(source.len(), destination.len()); // @@ -658,11 +624,12 @@ impl<'a> GcmExpandedKeyRef<'a> { destination.as_mut_ptr(), destination.len() as symcrypt_sys::SIZE_T, tag.as_mut_ptr(), - tag.len() as symcrypt_sys::SIZE_T); + tag.len() as symcrypt_sys::SIZE_T, + ); } } - /// + /// /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. This call cannot fail. /// /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. @@ -674,17 +641,14 @@ impl<'a> GcmExpandedKeyRef<'a> { /// /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. /// Tag sizes of 4 and 8 are not supported. - /// + /// pub fn encrypt_in_place( &self, nonce: &[u8; 12], auth_data: &[u8], buffer: &mut [u8], - tag: &mut [u8] - ) - - { - + tag: &mut [u8], + ) { // // SAFETY: The underlying SYMCRYPT_GCM_EXPANDED_KEY is guaranteed to be initialized // by the caller of `GcmExpandedKeyHandle::new`. @@ -701,37 +665,32 @@ impl<'a> GcmExpandedKeyRef<'a> { buffer.as_mut_ptr(), buffer.len() as symcrypt_sys::SIZE_T, tag.as_mut_ptr(), - tag.len() as symcrypt_sys::SIZE_T); + tag.len() as symcrypt_sys::SIZE_T, + ); } } - } impl<'a, 'b> From<&'a GcmExpandedKeyHandle<'b>> for GcmExpandedKeyRef<'a> { - fn from(value: &'a GcmExpandedKeyHandle<'b>) -> Self { GcmExpandedKeyRef(value.0) } - } impl<'a> From<&'a GcmExpandedKey> for GcmExpandedKeyRef<'a> { - fn from(value: &'a GcmExpandedKey) -> Self { GcmExpandedKeyRef(&value.expanded_key) } - } /// /// This type represents an uninitialized SYMCRYPT_GCM_STATE. It can be freely /// copied, clones, or moved as it does not contain any information. -/// +/// #[derive(Copy, Clone, Default)] pub struct GcmStream(symcrypt_sys::SYMCRYPT_GCM_STATE); impl GcmStream { - // // `get_key_ptr_mut` gets a mutable pointer to the underlying SYMCRYPT_GCM_STATE. // @@ -750,18 +709,19 @@ impl GcmStream { fn initialize<'a>( &'a mut self, expanded_key: GcmExpandedKeyRef<'a>, - nonce: &[u8; 12]) -> internal::GcmInitializedStream<'a> { - + nonce: &[u8; 12], + ) -> internal::GcmInitializedStream<'a> { // // SAFETY: FFI call to initialize repr(C) struct. // - + unsafe { symcrypt_sys::SymCryptGcmInit( self.get_state_ptr_mut(), expanded_key.0.get_key_ptr(), nonce.as_ptr(), - nonce.len() as symcrypt_sys::SIZE_T); + nonce.len() as symcrypt_sys::SIZE_T, + ); internal::GcmInitializedStream::new(self) } @@ -769,69 +729,67 @@ impl GcmStream { /// /// Initializes this GcmStream as a GcmAuthStream using the provided key, and nonce. - /// + /// /// `expanded_key` is a `GcmExpandedKeyRef` that provides a handle to the key to use /// for operations. - /// + /// /// `nonce` is a `&[u8; 12]` that is used as the nonce. /// #[inline(always)] pub fn as_auth_stream<'a>( &'a mut self, expanded_key: GcmExpandedKeyRef<'a>, - nonce: &[u8; 12]) -> GcmAuthStream<'a> { - + nonce: &[u8; 12], + ) -> GcmAuthStream<'a> { GcmAuthStream(self.initialize(expanded_key, nonce)) } /// /// Initializes this GcmStream as a GcmDecryptionStream using the provided key, and nonce. - /// + /// /// `expanded_key` is a `GcmExpandedKeyRef` that provides a handle to the key to use /// for operations. - /// + /// /// `nonce` is a `&[u8; 12]` that is used as the nonce. /// #[inline(always)] pub fn as_decryption_stream<'a>( &'a mut self, expanded_key: GcmExpandedKeyRef<'a>, - nonce: &[u8; 12]) -> GcmDecryptionStream<'a> { - + nonce: &[u8; 12], + ) -> GcmDecryptionStream<'a> { GcmDecryptionStream(self.initialize(expanded_key, nonce)) } /// /// Initializes this GcmStream as a GcmEncryptionStream using the provided key, and nonce. - /// + /// /// `expanded_key` is a `GcmExpandedKeyRef` that provides a handle to the key to use /// for operations. - /// + /// /// `nonce` is a `&[u8; 12]` that is used as the nonce. /// #[inline(always)] pub fn as_encryption_stream<'a>( &'a mut self, expanded_key: GcmExpandedKeyRef<'a>, - nonce: &[u8; 12]) -> GcmEncryptionStream<'a> { - + nonce: &[u8; 12], + ) -> GcmEncryptionStream<'a> { GcmEncryptionStream(self.initialize(expanded_key, nonce)) } - } /// /// This type represents a handle to an initialized GcmStream that can be used to autheticate, /// but not encrypt or decrypt, data. It can later be converted to a GcmDecryptionStream or /// GcmEncryptionStream. -/// +/// pub struct GcmAuthStream<'a>(internal::GcmInitializedStream<'a>); impl<'a> GcmAuthStream<'a> { - /// /// `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. - /// + /// #[inline(always)] pub fn as_ref_mut(&mut self) -> GcmAuthStreamRefMut { GcmAuthStreamRefMut(self.0.as_ref_mut()) @@ -839,9 +797,9 @@ impl<'a> GcmAuthStream<'a> { /// /// `authenticate` authenticates, but does not otherwise encrypt or decrypt, the provided data. - /// + /// /// `data` is a `&[u8]` that contains the data to authenticate. - /// + /// #[inline(always)] pub fn authenticate(&mut self, data: &[u8]) { self.as_ref_mut().authenticate(data); @@ -849,7 +807,7 @@ impl<'a> GcmAuthStream<'a> { /// /// `to_decryption_stream` converts this GcmAuthStream into a GcmDecryptionStream - /// + /// #[inline(always)] pub fn to_decryption_stream(self) -> GcmDecryptionStream<'a> { GcmDecryptionStream(self.0) @@ -857,29 +815,26 @@ impl<'a> GcmAuthStream<'a> { /// /// `to_encryption_stream` converts this GcmAuthStream into a GcmEncryptionStream - /// + /// #[inline(always)] pub fn to_encryption_stream(self) -> GcmEncryptionStream<'a> { GcmEncryptionStream(self.0) } - } /// /// This type represents a borrowed mutable handle to an initialized GcmStream that can be used to /// autheticate, but not encrypt or decrypt, data. -/// +/// pub struct GcmAuthStreamRefMut<'a>(internal::GcmInitializedStreamRefMut<'a>); -impl<'a> GcmAuthStreamRefMut<'a> { - +impl GcmAuthStreamRefMut<'_> { /// /// `authenticate` authenticates, but does not otherwise encrypt or decrypt, the provided data. - /// + /// /// `data` is a `&[u8]` that contains the data to authenticate. - /// + /// pub fn authenticate(&mut self, data: &[u8]) { - // // SAFETY: The internal stream is guaranteed to still be initialized while // self is alive. @@ -889,22 +844,21 @@ impl<'a> GcmAuthStreamRefMut<'a> { symcrypt_sys::SymCryptGcmAuthPart( self.0.get_state_ptr_mut(), data.as_ptr(), - data.len() as symcrypt_sys::SIZE_T); + data.len() as symcrypt_sys::SIZE_T, + ); } } - } /// /// This type represents a handle to an initialized GcmStream that can be used to decrypt data. -/// +/// pub struct GcmDecryptionStream<'a>(internal::GcmInitializedStream<'a>); -impl<'a> GcmDecryptionStream<'a> { - +impl GcmDecryptionStream<'_> { /// /// `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. - /// + /// #[inline(always)] pub fn as_ref_mut(&mut self) -> GcmDecryptionStreamRefMut { GcmDecryptionStreamRefMut(self.0.as_ref_mut()) @@ -913,12 +867,11 @@ impl<'a> GcmDecryptionStream<'a> { /// /// `complete` finishes this decryption stream and validates that the provided tag matches /// the generated tag. - /// + /// /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. /// This is used to verify the integrity of the cipher text. - /// + /// pub fn complete(mut self, tag: &[u8]) -> Result<(), SymCryptError> { - // // SAFETY: The internal stream is guaranteed to still be initialized while // self is alive. @@ -928,23 +881,24 @@ impl<'a> GcmDecryptionStream<'a> { symcrypt_sys::SymCryptGcmDecryptFinal( self.0.get_state_ptr_mut(), tag.as_ptr(), - tag.len() as u64) + tag.len() as symcrypt_sys::SIZE_T, + ) }; self.0.drop_without_zero(); match result { symcrypt_sys::SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), - error => Err(error.into()) + error => Err(error.into()), } } - /// + /// /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. /// This is a partial decryption of the cipher text and the results of the plain text are not validated /// until `complete` is called. - /// + /// /// `source` is a `&[u8]` that contains the cipher text to be decrypted. - /// + /// /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. /// `destination` must be of the same length as `source`. /// @@ -953,44 +907,42 @@ impl<'a> GcmDecryptionStream<'a> { self.as_ref_mut().decrypt(source, destination); } - /// + /// /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. /// This is a partial decryption of the cipher text and the results of the plain text are not validated /// until `complete` is called. - /// + /// /// `source` is a `&[u8]` that contains the cipher text to be decrypted. - /// + /// /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. /// `destination` must be of the same length as `source`. - /// + /// #[inline(always)] pub fn decrypt_in_place(&mut self, data: &mut [u8]) { self.as_ref_mut().decrypt_in_place(data); } - } /// /// This type represents a borrowed mutable handle to an initialized GcmStream that can be used to /// decrypt data. -/// +/// pub struct GcmDecryptionStreamRefMut<'a>(internal::GcmInitializedStreamRefMut<'a>); -impl<'a> GcmDecryptionStreamRefMut<'a> { - - /// +impl GcmDecryptionStreamRefMut<'_> { + /// /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. /// This is a partial decryption of the cipher text and the results of the plain text are not validated /// until `complete` is called. - /// + /// /// `source` is a `&[u8]` that contains the cipher text to be decrypted. - /// + /// /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. /// `destination` must be of the same length as `source`. /// pub fn decrypt(&mut self, source: &[u8], destination: &mut [u8]) { assert_eq!(source.len(), destination.len()); - + // // SAFETY: The internal stream is guaranteed to still be initialized while // self is alive and we've asserted that the source and destination buffers @@ -1002,22 +954,22 @@ impl<'a> GcmDecryptionStreamRefMut<'a> { self.0.get_state_ptr_mut(), source.as_ptr(), destination.as_mut_ptr(), - destination.len() as symcrypt_sys::SIZE_T); + destination.len() as symcrypt_sys::SIZE_T, + ); } } - /// + /// /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. /// This is a partial decryption of the cipher text and the results of the plain text are not validated /// until `complete` is called. - /// + /// /// `source` is a `&[u8]` that contains the cipher text to be decrypted. - /// + /// /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. /// `destination` must be of the same length as `source`. - /// + /// pub fn decrypt_in_place(&mut self, data: &mut [u8]) { - // // SAFETY: The internal stream is guaranteed to still be initialized while // self is alive. @@ -1028,23 +980,22 @@ impl<'a> GcmDecryptionStreamRefMut<'a> { self.0.get_state_ptr_mut(), data.as_ptr(), data.as_mut_ptr(), - data.len() as symcrypt_sys::SIZE_T); + data.len() as symcrypt_sys::SIZE_T, + ); } } - } /// /// This type represents a handle to an initialized GcmStream that can be used to /// encrypt data. -/// +/// pub struct GcmEncryptionStream<'a>(internal::GcmInitializedStream<'a>); -impl<'a> GcmEncryptionStream<'a> { - +impl GcmEncryptionStream<'_> { /// /// `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. - /// + /// #[inline(always)] pub fn as_ref_mut(&mut self) -> GcmEncryptionStreamRefMut { GcmEncryptionStreamRefMut(self.0.as_ref_mut()) @@ -1053,13 +1004,12 @@ impl<'a> GcmEncryptionStream<'a> { /// /// `complete` finishes this encryption stream and returns the generated tag for validating /// decryption. - /// + /// /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. /// Tag size must be 12, 13, 14, 15, 16 per SP800-38D. /// Tag sizes of 4 and 8 are not supported. - /// + /// pub fn complete(mut self, tag: &mut [u8]) { - // // SAFETY: The internal stream is guaranteed to still be initialized while // self is alive. @@ -1069,59 +1019,58 @@ impl<'a> GcmEncryptionStream<'a> { symcrypt_sys::SymCryptGcmEncryptFinal( self.0.get_state_ptr_mut(), tag.as_mut_ptr(), - tag.len() as u64); + tag.len() as symcrypt_sys::SIZE_T, + ); } self.0.drop_without_zero(); } - /// + /// /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. /// /// /// `source` is a `&[u8]` that contains the plain text to be encrypted. - /// + /// /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. /// `destination` must be of the same length as `source`. - /// + /// #[inline(always)] pub fn encrypt(&mut self, source: &[u8], destination: &mut [u8]) { self.as_ref_mut().encrypt(source, destination); } - /// + /// /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. /// /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, /// `buffer` will be over-written to contain the cipher text data. - /// + /// #[inline(always)] pub fn encrypt_in_place(&mut self, data: &mut [u8]) { self.as_ref_mut().encrypt_in_place(data); } - } /// /// This type represents a borrowed mutable handle to an initialized GcmStream that can be used to /// encrypt data. -/// +/// pub struct GcmEncryptionStreamRefMut<'a>(internal::GcmInitializedStreamRefMut<'a>); -impl<'a> GcmEncryptionStreamRefMut<'a> { - - /// +impl GcmEncryptionStreamRefMut<'_> { + /// /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. /// /// /// `source` is a `&[u8]` that contains the plain text to be encrypted. - /// + /// /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. /// `destination` must be of the same length as `source`. - /// + /// pub fn encrypt(&mut self, source: &[u8], destination: &mut [u8]) { assert_eq!(source.len(), destination.len()); - + // // SAFETY: The internal stream is guaranteed to still be initialized while // self is alive and we've asserted that the source and destination buffers @@ -1133,18 +1082,18 @@ impl<'a> GcmEncryptionStreamRefMut<'a> { self.0.get_state_ptr_mut(), source.as_ptr(), destination.as_mut_ptr(), - destination.len() as u64); + destination.len() as symcrypt_sys::SIZE_T, + ); } } - /// + /// /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. /// /// `buffer` is a `&mut [u8]` that contains the plain text data to be encrypted. After the encryption has been completed, /// `buffer` will be over-written to contain the cipher text data. - /// + /// pub fn encrypt_in_place(&mut self, data: &mut [u8]) { - // // SAFETY: The internal stream is guaranteed to still be initialized while // self is alive. @@ -1155,10 +1104,10 @@ impl<'a> GcmEncryptionStreamRefMut<'a> { self.0.get_state_ptr_mut(), data.as_ptr(), data.as_mut_ptr(), - data.len() as u64); + data.len() as symcrypt_sys::SIZE_T, + ); } } - } /// [`validate_gcm_parameters`] is a utility function that validates the input parameters for a GCM call. @@ -1198,7 +1147,10 @@ pub fn validate_gcm_parameters( mod internal { - use std::{mem, ops::{Deref, DerefMut}}; + use std::{ + mem, + ops::{Deref, DerefMut}, + }; use symcrypt_sys::{SymCryptWipe, SYMCRYPT_GCM_STATE}; @@ -1232,16 +1184,15 @@ mod internal { /// 1. Provide a guarantee that the underlying storage is initialized. /// 2. Prevent the underlying storage from being moved or copied. /// 3. Zero the underlying storage when dropped. - /// + /// pub struct GcmInitializedStream<'a>(&'a mut GcmStream); impl<'a> GcmInitializedStream<'a> { - // // `new` creates a new handle to an initialized GcmStream. // // # Safety: - // + // // The caller must ensure that the underlying storage has been correctly // initialized. // @@ -1251,7 +1202,7 @@ mod internal { // // `as_ref_mut` creates a new borrowed handle to the underlying GcmStream. - // + // #[inline(always)] pub fn as_ref_mut(&mut self) -> GcmInitializedStreamRefMut { GcmInitializedStreamRefMut::new(self) @@ -1266,13 +1217,10 @@ mod internal { pub fn drop_without_zero(self) { mem::forget(self); } - } - impl<'a> Drop for GcmInitializedStream<'a> { - + impl Drop for GcmInitializedStream<'_> { fn drop(&mut self) { - // // SAFETY: FFI calls to securly zero repr(C) structs // @@ -1280,43 +1228,37 @@ mod internal { unsafe { SymCryptWipe( self.0.get_state_ptr_mut() as *mut _, - mem::size_of::() as symcrypt_sys::SIZE_T); + mem::size_of::() as symcrypt_sys::SIZE_T, + ); } } - } - impl<'a> Deref for GcmInitializedStream<'a> { - + impl Deref for GcmInitializedStream<'_> { type Target = GcmStream; - + fn deref(&self) -> &Self::Target { self.0 } - } - impl<'a> DerefMut for GcmInitializedStream<'a> { - + impl DerefMut for GcmInitializedStream<'_> { fn deref_mut(&mut self) -> &mut Self::Target { self.0 } - } - /// /// This type represents a borrowed handle to an initialized GcmStream that /// is used to: /// 1. Provide a guarantee that the underlying storage is initialized. /// 2. Prevent the underlying storage from being moved or copied. - /// + /// /// This type does not zero the underlying storage when dropped. - /// + /// pub struct GcmInitializedStreamRefMut<'a>(&'a mut GcmStream); impl<'a> GcmInitializedStreamRefMut<'a> { - // // `new` creates a new borrowed mutable reference to an initialized GcmStream // from an existing owned reference. @@ -1325,27 +1267,21 @@ mod internal { pub fn new(inner: &'a mut GcmInitializedStream) -> Self { Self(inner.0) } - } - impl<'a> Deref for GcmInitializedStreamRefMut<'a> { - + impl Deref for GcmInitializedStreamRefMut<'_> { type Target = GcmStream; - + fn deref(&self) -> &Self::Target { self.0 } - } - impl<'a> DerefMut for GcmInitializedStreamRefMut<'a> { - + impl DerefMut for GcmInitializedStreamRefMut<'_> { fn deref_mut(&mut self) -> &mut Self::Target { self.0 } - } - } #[cfg(test)] @@ -1474,7 +1410,7 @@ mod test { let gcm_state = GcmExpandedKey::new(&p_key, cipher).unwrap(); assert_eq!(gcm_state.key_len(), 16); } - + #[test] fn test_invalid_aes_key() { let key_data = &[]; @@ -1482,19 +1418,19 @@ mod test { let mut key_storage = GcmUninitializedKey::default(); match key_storage.expand_key(BlockCipherType::AesBlock, key_data) { - Err(SymCryptError::WrongKeySize) => {}, + Err(SymCryptError::WrongKeySize) => {} Ok(_) => panic!("Incorrectly returned success when generating auth stream"), - Err(error) => panic!("Invalid result when generating auth stream: {:?}", error) + Err(error) => panic!("Invalid result when generating auth stream: {:?}", error), }; } #[test] fn test_encrypt_decrypt_part() -> Result<(), SymCryptError> { let mut key = GcmUninitializedKey::default(); - let key = - key.expand_key( - BlockCipherType::AesBlock, - &hex::decode("feffe9928665731c6d6a8f9467308308").unwrap())?; + let key = key.expand_key( + BlockCipherType::AesBlock, + &hex::decode("feffe9928665731c6d6a8f9467308308").unwrap(), + )?; let mut nonce = [0; 12]; rand::fill(&mut nonce); @@ -1512,17 +1448,14 @@ mod test { let mut gcm_stream = GcmStream::default(); for chunk_size in 1..orig_data.len() { - let mut encryption_stream = - gcm_stream.as_encryption_stream( - key.as_ref(), - &nonce); + let mut encryption_stream = gcm_stream.as_encryption_stream(key.as_ref(), &nonce); let mut encrypted_data = [0; 1024]; let mut tag = [0; 16]; - for (source, destination) in - orig_data.chunks(chunk_size) - .zip(encrypted_data.chunks_mut(chunk_size)) { - + for (source, destination) in orig_data + .chunks(chunk_size) + .zip(encrypted_data.chunks_mut(chunk_size)) + { encryption_stream.encrypt(source, destination); } @@ -1532,16 +1465,13 @@ mod test { } for chunk_size in 1..orig_data.len() { - let mut decryption_stream = - gcm_stream.as_decryption_stream( - key.as_ref(), - &nonce); + let mut decryption_stream = gcm_stream.as_decryption_stream(key.as_ref(), &nonce); let mut decrypted_data = [0; 1024]; - for (source, destination) in - expected_encrypted.chunks(chunk_size) - .zip(decrypted_data.chunks_mut(chunk_size)) { - + for (source, destination) in expected_encrypted + .chunks(chunk_size) + .zip(decrypted_data.chunks_mut(chunk_size)) + { decryption_stream.decrypt(source, destination); } @@ -1555,10 +1485,10 @@ mod test { #[test] fn test_encrypt_decrypt_part_inplace() -> Result<(), SymCryptError> { let mut key = GcmUninitializedKey::default(); - let key = - key.expand_key( - BlockCipherType::AesBlock, - &hex::decode("feffe9928665731c6d6a8f9467308308").unwrap())?; + let key = key.expand_key( + BlockCipherType::AesBlock, + &hex::decode("feffe9928665731c6d6a8f9467308308").unwrap(), + )?; let mut nonce = [0; 12]; rand::fill(&mut nonce); @@ -1575,10 +1505,7 @@ mod test { let mut gcm_stream = GcmStream::default(); for chunk_size in 1..orig_data.len() { - let mut encryption_stream = - gcm_stream.as_encryption_stream( - key.as_ref(), - &nonce); + let mut encryption_stream = gcm_stream.as_encryption_stream(key.as_ref(), &nonce); let mut encrypted_data = orig_data; let mut tag = [0; 16]; @@ -1592,10 +1519,7 @@ mod test { } for chunk_size in 1..orig_data.len() { - let mut decryption_stream = - gcm_stream.as_decryption_stream( - key.as_ref(), - &nonce); + let mut decryption_stream = gcm_stream.as_decryption_stream(key.as_ref(), &nonce); let mut decrypted_data = expected_encrypted; for window in decrypted_data.chunks_mut(chunk_size) { From 69306a5ac29300707a4530be85c0592935c783a9 Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Wed, 12 Feb 2025 14:50:54 -0800 Subject: [PATCH 5/6] Remove most changes to existing GcmExpandedKey implementation --- rust-symcrypt/src/gcm.rs | 481 ++++++++++++++++++++------------------- 1 file changed, 244 insertions(+), 237 deletions(-) diff --git a/rust-symcrypt/src/gcm.rs b/rust-symcrypt/src/gcm.rs index 83170e78..49315a11 100644 --- a/rust-symcrypt/src/gcm.rs +++ b/rust-symcrypt/src/gcm.rs @@ -64,86 +64,13 @@ use crate::cipher::{convert_cipher, BlockCipherType}; use crate::errors::SymCryptError; use crate::symcrypt_init; +use core::ffi::c_void; +use std::marker::PhantomPinned; use std::mem; +use std::pin::Pin; use std::ptr; use symcrypt_sys; -/// -/// This type represents a common storage for SYMCRYPT_GCM_EXPANDED_KEY. -/// -#[derive(Clone, Copy, Default)] -struct GcmExpandedKeyStorage(symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY); - -impl GcmExpandedKeyStorage { - // - // `get_key_ptr` gets a constant pointer to the underlying SYMCRYPT_GCM_EXPANDED_KEY. - // - #[inline(always)] - fn get_key_ptr(&self) -> *const symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY { - ptr::addr_of!(self.0) - } - - // - // `get_key_ptr_mut` gets a mutable pointer to the underlying SYMCRYPT_GCM_EXPANDED_KEY. - // - #[inline(always)] - fn get_key_ptr_mut(&mut self) -> *mut symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY { - ptr::addr_of_mut!(self.0) - } - - // - // `zero_storage` zeroes the underlying SYMCRYPT_GCM_EXPANDED_KEY memory. - // - // This is unsafe as it uninitializes the storage that other unsafe code - // may rely on being initialized. - // - #[inline(always)] - unsafe fn zero_storage(&mut self) { - symcrypt_sys::SymCryptWipe( - self.get_key_ptr_mut() as *mut _, - mem::size_of::() as symcrypt_sys::SIZE_T, - ); - } -} - -/// -/// This type represents an uninitialized GcmExpandedKeyStorage. It can be freely -/// copied, cloned, or moved as it does not contain any information. -/// -#[derive(Clone, Copy, Default)] -pub struct GcmUninitializedKey(GcmExpandedKeyStorage); - -impl GcmUninitializedKey { - /// - /// `expand_key` will initialize this SYMCRYPT_GCM_EXPANDED_KEY to using the probided - /// cipher type and key. - /// - /// `cipher_type` is a `BlockCipherType` that determines the cipher to use for this key. - /// The only supported cipher type is [`BlockCipherType::AesBlock`] - /// - /// `key_data` is a `&[u8]` that contains the key to initialize with. - /// - pub fn expand_key( - &mut self, - cipher_type: BlockCipherType, - key_data: &[u8], - ) -> Result { - symcrypt_init(); - - internal::gcm_expand_key( - key_data, - self.0.get_key_ptr_mut(), - convert_cipher(cipher_type), - )?; - - // - // SAFETY: gcm_expand_key guarantees that the SYMCRYPT_GCM_EXPANDED_KEY storage is initialized on success. - // - - unsafe { Ok(GcmExpandedKeyHandle::new(self)) } - } -} - /// [`GcmExpandedKey`] is a struct that holds the Gcm expanded key from SymCrypt. pub struct GcmExpandedKey { // expanded_key holds the key from SymCrypt which is Pin>'d since the memory address for Self is moved around when @@ -153,19 +80,52 @@ pub struct GcmExpandedKey { // SymCrypt expects the address for its structs to stay static through the structs lifetime to guarantee that structs are not memcpy'd as // doing so would lead to use-after-free and inconsistent states. - expanded_key: Box, + expanded_key: Pin>, key_length: usize, } -impl Drop for GcmExpandedKey { - fn drop(&mut self) { - // - // SAFETY: Is is safe to uninitialize the underlying storage as this - // if the only reference to it and we are being dropped. - // +/// [`GcmInnerKey`] is a struct that holds the underlying SymCrypt state for GCM. +#[derive(Default)] +struct GcmInnerKey { + // inner represents the actual state of the hash from SymCrypt + inner: symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY, + + // _pinned is a marker to ensure that instances of the inner state cannot be moved once pinned. + // This prevents the struct from implementing the Unpin trait, enforcing that any + // references to this structure remain valid throughout its lifetime. + _pinned: PhantomPinned, +} + +impl GcmInnerKey { + /// Creates a new GcmInnerKey and returns a pinned Box + fn new() -> Pin> { + Box::pin(GcmInnerKey::default()) + } + + /// Provides a mutable pointer to the inner SymCrypt state. + /// + /// This is primarily meant to be used while making calls to the underlying SymCrypt APIs. + /// The pointer returned is pinned and cannot be moved + /// This function returns pointer to pinned data, which means callers must not use the pointer to move the data out of its location. + fn get_inner_mut(self: Pin<&mut Self>) -> *mut symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY { + // SAFETY: Accessing the inner state of the pinned data + unsafe { &mut self.get_unchecked_mut().inner as *mut _ } + } + + // Safe method to access the inner state immutably + pub(crate) fn get_inner(&self) -> *const symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY { + &self.inner as *const _ + } +} +impl Drop for GcmInnerKey { + fn drop(&mut self) { unsafe { - self.expanded_key.zero_storage(); + // SAFETY: FFI calls + symcrypt_sys::SymCryptWipe( + ptr::addr_of_mut!(self.inner) as *mut c_void, // Using addr_of_mut! so we don't access in the inner field + mem::size_of_val(&self.inner) as symcrypt_sys::SIZE_T, // Using size_of_val! so we don't access in the inner field + ); } } } @@ -180,10 +140,14 @@ impl GcmExpandedKey { /// The only accepted Cipher for GCM is [`BlockCipherType::AesBlock`] pub fn new(key: &[u8], cipher: BlockCipherType) -> Result { symcrypt_init(); - let mut expanded_key = Box::new(GcmExpandedKeyStorage::default()); // Get expanded_key that is already Pin>'d + let mut expanded_key = GcmInnerKey::new(); // Get expanded_key that is already Pin>'d // Use as_mut() to get a Pin<&mut GcmInnerKey> and then call get_inner_mut to get *mut - internal::gcm_expand_key(key, expanded_key.get_key_ptr_mut(), convert_cipher(cipher))?; + gcm_expand_key( + key, + expanded_key.as_mut().get_inner_mut(), + convert_cipher(cipher), + )?; let gcm_expanded_key = GcmExpandedKey { expanded_key, key_length: key.len(), @@ -192,42 +156,13 @@ impl GcmExpandedKey { } /// - /// Creates a borrowed reference to the underlying GcmExpandedKeyStorage. + /// Creates a borrowed reference to the underlying SYMCRYPT_GCM_EXPANDED_KEY. /// #[inline(always)] pub fn as_ref(&self) -> GcmExpandedKeyRef { self.into() } - /// - /// `encrypt` performs an encryption of the data in `source` and writes the encrypted data to `destination`. - /// This call cannot fail. - /// - /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. - /// - /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. - /// - /// `source` is a `&[u8]` that contains the plain text to be encrypted. - /// - /// `destination` is a `&mut [u8]` that after decryption will contain the encrypted cipher text. - /// `destination` must be of the same length as `source`. - /// - /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. - /// Tag sizes of 4 and 8 are not supported. - /// - #[inline(always)] - pub fn encrypt( - &self, - nonce: &[u8; 12], - auth_data: &[u8], - source: &[u8], - destination: &mut [u8], - tag: &mut [u8], - ) { - self.as_ref() - .encrypt(nonce, auth_data, source, destination, tag); - } - /// `encrypt_in_place` performs an in-place encryption on the `&mut buffer` that is passed. This call cannot fail. /// /// `nonce` is a `&[u8; 12]` that is used as the nonce for the encryption. @@ -239,7 +174,6 @@ impl GcmExpandedKey { /// /// `tag` is a `&mut [u8]` which is the buffer where the resulting tag will be written to. Tag size must be 12, 13, 14, 15, 16 per SP800-38D. /// Tag sizes of 4 and 8 are not supported. - #[inline(always)] pub fn encrypt_in_place( &self, nonce: &[u8; 12], @@ -247,38 +181,22 @@ impl GcmExpandedKey { buffer: &mut [u8], tag: &mut [u8], ) { - self.as_ref() - .encrypt_in_place(nonce, auth_data, buffer, tag); - } - - /// - /// `decrypt` performs a decryption of the data in `source` and writes the decrypted data to `destination`. - /// This call can fail and the caller must check the result. - /// - /// `nonce` is a `&[u8; 12]` that is used as the nonce for the decryption. It must match the nonce used during encryption. - /// - /// `auth_data` is an optional `&[u8]` that can be provided. If you do not wish to provide any auth data, input an empty array. - /// - /// `source` is a `&[u8]` that contains the cipher text to be decrypted. - /// - /// `destination` is a `&mut [u8]` that after decryption will contain the decrypted plain text. - /// `destination` must be of the same length as `source`. - /// - /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. - /// - /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. - /// - #[inline(always)] - pub fn decrypt( - &self, - nonce: &[u8; 12], - auth_data: &[u8], - source: &[u8], - destination: &mut [u8], - tag: &[u8], - ) -> Result<(), SymCryptError> { - self.as_ref() - .decrypt(nonce, auth_data, source, destination, tag) + symcrypt_init(); + unsafe { + // SAFETY: FFI calls + symcrypt_sys::SymCryptGcmEncrypt( + self.expanded_key.get_inner(), + nonce.as_ptr(), + nonce.len() as symcrypt_sys::SIZE_T, + auth_data.as_ptr(), + auth_data.len() as symcrypt_sys::SIZE_T, + buffer.as_ptr(), + buffer.as_mut_ptr(), + buffer.len() as symcrypt_sys::SIZE_T, + tag.as_mut_ptr(), + tag.len() as symcrypt_sys::SIZE_T, + ); + } } /// `decrypt_in_place` performs an in-place decryption on the `&mut buffer` that is passed. This call can fail and the caller must check the result. @@ -293,7 +211,6 @@ impl GcmExpandedKey { /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. This is used to verify the integrity of the cipher text. /// /// If decryption succeeds, the function will return `Ok(())`, and `buffer` will contain the plain text. If it fails, an error of type `SymCryptError` will be returned. - #[inline(always)] pub fn decrypt_in_place( &self, nonce: &[u8; 12], @@ -301,8 +218,25 @@ impl GcmExpandedKey { buffer: &mut [u8], tag: &[u8], ) -> Result<(), SymCryptError> { - self.as_ref() - .decrypt_in_place(nonce, auth_data, buffer, tag) + symcrypt_init(); + unsafe { + // SAFETY: FFI calls + match symcrypt_sys::SymCryptGcmDecrypt( + self.expanded_key.get_inner(), + nonce.as_ptr(), + nonce.len() as symcrypt_sys::SIZE_T, + auth_data.as_ptr(), + auth_data.len() as symcrypt_sys::SIZE_T, + buffer.as_ptr(), + buffer.as_mut_ptr(), + buffer.len() as symcrypt_sys::SIZE_T, + tag.as_ptr(), + tag.len() as symcrypt_sys::SIZE_T, + ) { + symcrypt_sys::SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), + err => Err(err.into()), + } + } } /// `key_len` returns a the length of the [`GcmExpandedKey`] as a `usize`. @@ -316,29 +250,94 @@ impl GcmExpandedKey { unsafe impl Send for GcmExpandedKey {} unsafe impl Sync for GcmExpandedKey {} +// Internal function to expand the SymCrypt Gcm Key. +fn gcm_expand_key( + key: &[u8], + expanded_key: *mut symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY, + cipher: *const symcrypt_sys::SYMCRYPT_BLOCKCIPHER, +) -> Result<(), SymCryptError> { + unsafe { + // SAFETY: FFI calls + match symcrypt_sys::SymCryptGcmExpandKey( + expanded_key, + cipher, + key.as_ptr(), + key.len() as symcrypt_sys::SIZE_T, + ) { + symcrypt_sys::SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), + err => Err(err.into()), + } + } +} + /// -/// This type represents a handle to an initialized GcmUnexpandedKey that -/// is used to: +/// This type represents an uninitialized SYMCRYPT_GCM_EXPANDED_KEY. +/// +pub struct GcmUninitializedKey(mem::MaybeUninit); + +impl GcmUninitializedKey { + /// + /// `expand_key` will initialize this SYMCRYPT_GCM_EXPANDED_KEY to using the probided + /// cipher type and key. + /// + /// `cipher_type` is a `BlockCipherType` that determines the cipher to use for this key. + /// The only supported cipher type is [`BlockCipherType::AesBlock`] + /// + /// `key_data` is a `&[u8]` that contains the key to initialize with. + /// + pub fn expand_key( + &mut self, + cipher_type: BlockCipherType, + key_data: &[u8], + ) -> Result { + symcrypt_init(); + + unsafe { + let default_key = self.0.write(GcmInnerKey::default()); + + gcm_expand_key( + key_data, + &mut default_key.inner as *mut _, + convert_cipher(cipher_type), + )?; + + // SAFETY: GcmExpandedKeyHandle holds the only reference to the initialized + // key and will uninitialize it when dropped. + let pinned_key = Pin::new_unchecked(default_key); + Ok(GcmExpandedKeyHandle::new(pinned_key)) + } + } +} + +impl Default for GcmUninitializedKey { + fn default() -> Self { + Self(mem::MaybeUninit::zeroed()) + } +} + +/// +/// This type represents an owned pointer to an initialized SYMCRYPT_GCM_EXPANDED_KEY +/// that is used to: /// 1. Provide a guarantee that the underlying storage is initialized. -/// 2. Prevent the underlying storage from being moved or copied. +/// 2. Prevent the underlying storage from being moved. /// 3. Zero the underlying storage when dropped. /// -pub struct GcmExpandedKeyHandle<'a>(&'a mut GcmExpandedKeyStorage); +pub struct GcmExpandedKeyHandle<'a>(Pin<&'a mut GcmInnerKey>); impl<'a> GcmExpandedKeyHandle<'a> { // - // # Safety: + // `new` creates a new handle to an initialized GcmInnerKey. // - // The caller must enture that the underlying storage has been correctly - // initialized. + // # Safety // - #[inline(always)] - unsafe fn new(inner: &'a mut GcmUninitializedKey) -> Self { - Self(&mut inner.0) + // The caller must ensure that this is the only pointer to the inner key. + // + unsafe fn new(pinned_key: Pin<&'a mut GcmInnerKey>) -> Self { + Self(pinned_key) } /// - /// Creates a borrowed reference to the underlying GcmUnexpandedKey storage. + /// Creates a borrowed reference to the underlying SYMCRYPT_GCM_EXPANDED_KEY storage. /// #[inline(always)] pub fn as_ref(&self) -> GcmExpandedKeyRef { @@ -460,11 +459,11 @@ impl Drop for GcmExpandedKeyHandle<'_> { fn drop(&mut self) { // // SAFETY: Is is safe to uninitialize the underlying storage as this - // if the only reference to it and we are being dropped. + // is the only reference to it and we are being dropped. // unsafe { - self.0.zero_storage(); + std::ptr::drop_in_place(self.0.as_mut().get_unchecked_mut() as *mut _); } } } @@ -473,11 +472,11 @@ impl Drop for GcmExpandedKeyHandle<'_> { /// This type represents a borrowed handle to an initialized SYMCRYPT_GCM_EXPANDED_KEY /// that is used to: /// 1. Provide a guarantee that the underlying storage is initialized. -/// 2. Prevent the underlying storage from being moved or copied. +/// 2. Prevent the underlying storage from being moved. /// /// This type does not zero the underlying storage when dropped. /// -pub struct GcmExpandedKeyRef<'a>(&'a GcmExpandedKeyStorage); +pub struct GcmExpandedKeyRef<'a>(Pin<&'a GcmInnerKey>); impl GcmExpandedKeyRef<'_> { /// @@ -515,7 +514,7 @@ impl GcmExpandedKeyRef<'_> { unsafe { let result = symcrypt_sys::SymCryptGcmDecrypt( - self.0.get_key_ptr(), + self.0.get_inner(), nonce.as_ptr(), nonce.len() as symcrypt_sys::SIZE_T, auth_data.as_ptr(), @@ -562,7 +561,7 @@ impl GcmExpandedKeyRef<'_> { unsafe { let result = symcrypt_sys::SymCryptGcmDecrypt( - self.0.get_key_ptr(), + self.0.get_inner(), nonce.as_ptr(), nonce.len() as symcrypt_sys::SIZE_T, auth_data.as_ptr(), @@ -615,7 +614,7 @@ impl GcmExpandedKeyRef<'_> { unsafe { symcrypt_sys::SymCryptGcmEncrypt( - self.0.get_key_ptr(), + self.0.get_inner(), nonce.as_ptr(), nonce.len() as symcrypt_sys::SIZE_T, auth_data.as_ptr(), @@ -656,7 +655,7 @@ impl GcmExpandedKeyRef<'_> { unsafe { symcrypt_sys::SymCryptGcmEncrypt( - self.0.get_key_ptr(), + self.0.get_inner(), nonce.as_ptr(), nonce.len() as symcrypt_sys::SIZE_T, auth_data.as_ptr(), @@ -673,34 +672,24 @@ impl GcmExpandedKeyRef<'_> { impl<'a, 'b> From<&'a GcmExpandedKeyHandle<'b>> for GcmExpandedKeyRef<'a> { fn from(value: &'a GcmExpandedKeyHandle<'b>) -> Self { - GcmExpandedKeyRef(value.0) + GcmExpandedKeyRef(value.0.as_ref()) } } impl<'a> From<&'a GcmExpandedKey> for GcmExpandedKeyRef<'a> { fn from(value: &'a GcmExpandedKey) -> Self { - GcmExpandedKeyRef(&value.expanded_key) + GcmExpandedKeyRef(value.expanded_key.as_ref()) } } /// -/// This type represents an uninitialized SYMCRYPT_GCM_STATE. It can be freely -/// copied, clones, or moved as it does not contain any information. +/// This type represents an uninitialized SYMCRYPT_GCM_STATE. /// -#[derive(Copy, Clone, Default)] -pub struct GcmStream(symcrypt_sys::SYMCRYPT_GCM_STATE); +pub struct GcmStream(mem::MaybeUninit); impl GcmStream { // - // `get_key_ptr_mut` gets a mutable pointer to the underlying SYMCRYPT_GCM_STATE. - // - #[inline(always)] - fn get_state_ptr_mut(&mut self) -> *mut symcrypt_sys::SYMCRYPT_GCM_STATE { - ptr::addr_of_mut!(self.0) - } - - // - // `initialize` initializes the underlying `SYMCRYPT_GCM_STATE`` with the provided key and nonce. + // `initialize` initializes the underlying `SYMCRYPT_GCM_STATE` with the provided key and nonce. // // `expanded_key` provides a borrowed reference to an initialized `SYMCRYPT_GCM_EXPANDED_KEY` // @@ -716,14 +705,16 @@ impl GcmStream { // unsafe { + let mut pinned_state = + Pin::new_unchecked(self.0.write(internal::GcmInnerStream::default())); symcrypt_sys::SymCryptGcmInit( - self.get_state_ptr_mut(), - expanded_key.0.get_key_ptr(), + pinned_state.as_mut().get_inner_mut(), + expanded_key.0.get_inner(), nonce.as_ptr(), nonce.len() as symcrypt_sys::SIZE_T, ); - internal::GcmInitializedStream::new(self) + internal::GcmInitializedStream::new(pinned_state) } } @@ -779,6 +770,12 @@ impl GcmStream { } } +impl Default for GcmStream { + fn default() -> Self { + Self(mem::MaybeUninit::zeroed()) + } +} + /// /// This type represents a handle to an initialized GcmStream that can be used to autheticate, /// but not encrypt or decrypt, data. It can later be converted to a GcmDecryptionStream or @@ -842,7 +839,7 @@ impl GcmAuthStreamRefMut<'_> { unsafe { symcrypt_sys::SymCryptGcmAuthPart( - self.0.get_state_ptr_mut(), + self.0.as_mut().get_inner_mut(), data.as_ptr(), data.len() as symcrypt_sys::SIZE_T, ); @@ -879,7 +876,7 @@ impl GcmDecryptionStream<'_> { let result = unsafe { symcrypt_sys::SymCryptGcmDecryptFinal( - self.0.get_state_ptr_mut(), + self.0.as_mut().get_inner_mut(), tag.as_ptr(), tag.len() as symcrypt_sys::SIZE_T, ) @@ -951,7 +948,7 @@ impl GcmDecryptionStreamRefMut<'_> { unsafe { symcrypt_sys::SymCryptGcmDecryptPart( - self.0.get_state_ptr_mut(), + self.0.as_mut().get_inner_mut(), source.as_ptr(), destination.as_mut_ptr(), destination.len() as symcrypt_sys::SIZE_T, @@ -977,7 +974,7 @@ impl GcmDecryptionStreamRefMut<'_> { unsafe { symcrypt_sys::SymCryptGcmDecryptPart( - self.0.get_state_ptr_mut(), + self.0.as_mut().get_inner_mut(), data.as_ptr(), data.as_mut_ptr(), data.len() as symcrypt_sys::SIZE_T, @@ -1017,7 +1014,7 @@ impl GcmEncryptionStream<'_> { unsafe { symcrypt_sys::SymCryptGcmEncryptFinal( - self.0.get_state_ptr_mut(), + self.0.as_mut().get_inner_mut(), tag.as_mut_ptr(), tag.len() as symcrypt_sys::SIZE_T, ); @@ -1079,7 +1076,7 @@ impl GcmEncryptionStreamRefMut<'_> { unsafe { symcrypt_sys::SymCryptGcmEncryptPart( - self.0.get_state_ptr_mut(), + self.0.as_mut().get_inner_mut(), source.as_ptr(), destination.as_mut_ptr(), destination.len() as symcrypt_sys::SIZE_T, @@ -1101,7 +1098,7 @@ impl GcmEncryptionStreamRefMut<'_> { unsafe { symcrypt_sys::SymCryptGcmEncryptPart( - self.0.get_state_ptr_mut(), + self.0.as_mut().get_inner_mut(), data.as_ptr(), data.as_mut_ptr(), data.len() as symcrypt_sys::SIZE_T, @@ -1148,32 +1145,45 @@ pub fn validate_gcm_parameters( mod internal { use std::{ + ffi::c_void, + marker::PhantomPinned, mem, ops::{Deref, DerefMut}, + pin::Pin, + ptr, }; - use symcrypt_sys::{SymCryptWipe, SYMCRYPT_GCM_STATE}; - - use crate::errors::SymCryptError; - - use super::GcmStream; + #[derive(Default)] + pub struct GcmInnerStream { + // inner represents the actual state of the hash from SymCrypt + inner: symcrypt_sys::SYMCRYPT_GCM_STATE, + + // _pinned is a marker to ensure that instances of the inner state cannot be moved once pinned. + // This prevents the struct from implementing the Unpin trait, enforcing that any + // references to this structure remain valid throughout its lifetime. + _pinned: PhantomPinned, + } + + impl GcmInnerStream { + /// Provides a mutable pointer to the inner SymCrypt state. + /// + /// This is primarily meant to be used while making calls to the underlying SymCrypt APIs. + /// The pointer returned is pinned and cannot be moved + /// This function returns pointer to pinned data, which means callers must not use the pointer to move the data out of its location. + pub fn get_inner_mut(self: Pin<&mut Self>) -> *mut symcrypt_sys::SYMCRYPT_GCM_STATE { + // SAFETY: Accessing the inner state of the pinned data + unsafe { &mut self.get_unchecked_mut().inner as *mut _ } + } + } - // Internal function to expand the SymCrypt Gcm Key. - pub fn gcm_expand_key( - key: &[u8], - expanded_key: *mut symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY, - cipher: *const symcrypt_sys::SYMCRYPT_BLOCKCIPHER, - ) -> Result<(), SymCryptError> { - unsafe { - // SAFETY: FFI calls - match symcrypt_sys::SymCryptGcmExpandKey( - expanded_key, - cipher, - key.as_ptr(), - key.len() as symcrypt_sys::SIZE_T, - ) { - symcrypt_sys::SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), - err => Err(err.into()), + impl Drop for GcmInnerStream { + fn drop(&mut self) { + unsafe { + // SAFETY: FFI calls + symcrypt_sys::SymCryptWipe( + ptr::addr_of_mut!(self.inner) as *mut c_void, // Using addr_of_mut! so we don't access in the inner field + mem::size_of_val(&self.inner) as symcrypt_sys::SIZE_T, // Using size_of_val! so we don't access in the inner field + ); } } } @@ -1185,18 +1195,17 @@ mod internal { /// 2. Prevent the underlying storage from being moved or copied. /// 3. Zero the underlying storage when dropped. /// - pub struct GcmInitializedStream<'a>(&'a mut GcmStream); + pub struct GcmInitializedStream<'a>(Pin<&'a mut GcmInnerStream>); impl<'a> GcmInitializedStream<'a> { // // `new` creates a new handle to an initialized GcmStream. // - // # Safety: + // # Safety // - // The caller must ensure that the underlying storage has been correctly - // initialized. + // The caller must ensure that this is the only pointer to the inner stream. // - pub unsafe fn new(inner: &'a mut GcmStream) -> Self { + pub unsafe fn new(inner: Pin<&'a mut GcmInnerStream>) -> Self { Self(inner) } @@ -1222,29 +1231,27 @@ mod internal { impl Drop for GcmInitializedStream<'_> { fn drop(&mut self) { // - // SAFETY: FFI calls to securly zero repr(C) structs + // SAFETY: Is is safe to uninitialize the underlying storage as this + // is the only reference to it and we are being dropped. // unsafe { - SymCryptWipe( - self.0.get_state_ptr_mut() as *mut _, - mem::size_of::() as symcrypt_sys::SIZE_T, - ); + std::ptr::drop_in_place(self.0.as_mut().get_unchecked_mut() as *mut _); } } } - impl Deref for GcmInitializedStream<'_> { - type Target = GcmStream; + impl<'a> Deref for GcmInitializedStream<'a> { + type Target = Pin<&'a mut GcmInnerStream>; fn deref(&self) -> &Self::Target { - self.0 + &self.0 } } impl DerefMut for GcmInitializedStream<'_> { fn deref_mut(&mut self) -> &mut Self::Target { - self.0 + &mut self.0 } } @@ -1256,7 +1263,7 @@ mod internal { /// /// This type does not zero the underlying storage when dropped. /// - pub struct GcmInitializedStreamRefMut<'a>(&'a mut GcmStream); + pub struct GcmInitializedStreamRefMut<'a>(Pin<&'a mut GcmInnerStream>); impl<'a> GcmInitializedStreamRefMut<'a> { // @@ -1265,21 +1272,21 @@ mod internal { // #[inline(always)] pub fn new(inner: &'a mut GcmInitializedStream) -> Self { - Self(inner.0) + Self(inner.0.as_mut()) } } - impl Deref for GcmInitializedStreamRefMut<'_> { - type Target = GcmStream; + impl<'a> Deref for GcmInitializedStreamRefMut<'a> { + type Target = Pin<&'a mut GcmInnerStream>; fn deref(&self) -> &Self::Target { - self.0 + &self.0 } } impl DerefMut for GcmInitializedStreamRefMut<'_> { fn deref_mut(&mut self) -> &mut Self::Target { - self.0 + &mut self.0 } } } From eb59de11904997f7723d258d8dce2472960d8a58 Mon Sep 17 00:00:00 2001 From: Jacob Smith Date: Wed, 12 Feb 2025 18:40:56 -0800 Subject: [PATCH 6/6] Remove MaybeUninit --- rust-symcrypt/src/gcm.rs | 67 ++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 44 deletions(-) diff --git a/rust-symcrypt/src/gcm.rs b/rust-symcrypt/src/gcm.rs index 49315a11..b12309b1 100644 --- a/rust-symcrypt/src/gcm.rs +++ b/rust-symcrypt/src/gcm.rs @@ -85,7 +85,7 @@ pub struct GcmExpandedKey { } /// [`GcmInnerKey`] is a struct that holds the underlying SymCrypt state for GCM. -#[derive(Default)] +#[derive(Clone, Copy, Default)] struct GcmInnerKey { // inner represents the actual state of the hash from SymCrypt inner: symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY, @@ -118,13 +118,13 @@ impl GcmInnerKey { } } -impl Drop for GcmInnerKey { +impl Drop for GcmExpandedKey { fn drop(&mut self) { unsafe { // SAFETY: FFI calls symcrypt_sys::SymCryptWipe( - ptr::addr_of_mut!(self.inner) as *mut c_void, // Using addr_of_mut! so we don't access in the inner field - mem::size_of_val(&self.inner) as symcrypt_sys::SIZE_T, // Using size_of_val! so we don't access in the inner field + self.expanded_key.as_mut().get_inner_mut() as *mut c_void, + mem::size_of::() as symcrypt_sys::SIZE_T, ); } } @@ -273,7 +273,8 @@ fn gcm_expand_key( /// /// This type represents an uninitialized SYMCRYPT_GCM_EXPANDED_KEY. /// -pub struct GcmUninitializedKey(mem::MaybeUninit); +#[derive(Clone, Copy, Default)] +pub struct GcmUninitializedKey(GcmInnerKey); impl GcmUninitializedKey { /// @@ -293,28 +294,19 @@ impl GcmUninitializedKey { symcrypt_init(); unsafe { - let default_key = self.0.write(GcmInnerKey::default()); - gcm_expand_key( key_data, - &mut default_key.inner as *mut _, + ptr::addr_of_mut!(self.0.inner), convert_cipher(cipher_type), )?; // SAFETY: GcmExpandedKeyHandle holds the only reference to the initialized // key and will uninitialize it when dropped. - let pinned_key = Pin::new_unchecked(default_key); - Ok(GcmExpandedKeyHandle::new(pinned_key)) + Ok(GcmExpandedKeyHandle::new(Pin::new_unchecked(&mut self.0))) } } } -impl Default for GcmUninitializedKey { - fn default() -> Self { - Self(mem::MaybeUninit::zeroed()) - } -} - /// /// This type represents an owned pointer to an initialized SYMCRYPT_GCM_EXPANDED_KEY /// that is used to: @@ -463,7 +455,10 @@ impl Drop for GcmExpandedKeyHandle<'_> { // unsafe { - std::ptr::drop_in_place(self.0.as_mut().get_unchecked_mut() as *mut _); + symcrypt_sys::SymCryptWipe( + self.0.as_mut().get_inner_mut() as *mut c_void, + mem::size_of::() as symcrypt_sys::SIZE_T, + ); } } } @@ -685,7 +680,8 @@ impl<'a> From<&'a GcmExpandedKey> for GcmExpandedKeyRef<'a> { /// /// This type represents an uninitialized SYMCRYPT_GCM_STATE. /// -pub struct GcmStream(mem::MaybeUninit); +#[derive(Clone, Copy, Default)] +pub struct GcmStream(internal::GcmInnerStream); impl GcmStream { // @@ -705,16 +701,14 @@ impl GcmStream { // unsafe { - let mut pinned_state = - Pin::new_unchecked(self.0.write(internal::GcmInnerStream::default())); symcrypt_sys::SymCryptGcmInit( - pinned_state.as_mut().get_inner_mut(), + ptr::addr_of_mut!(self.0.inner), expanded_key.0.get_inner(), nonce.as_ptr(), nonce.len() as symcrypt_sys::SIZE_T, ); - internal::GcmInitializedStream::new(pinned_state) + internal::GcmInitializedStream::new(Pin::new_unchecked(&mut self.0)) } } @@ -770,12 +764,6 @@ impl GcmStream { } } -impl Default for GcmStream { - fn default() -> Self { - Self(mem::MaybeUninit::zeroed()) - } -} - /// /// This type represents a handle to an initialized GcmStream that can be used to autheticate, /// but not encrypt or decrypt, data. It can later be converted to a GcmDecryptionStream or @@ -1150,13 +1138,12 @@ mod internal { mem, ops::{Deref, DerefMut}, pin::Pin, - ptr, }; - #[derive(Default)] + #[derive(Clone, Copy, Default)] pub struct GcmInnerStream { // inner represents the actual state of the hash from SymCrypt - inner: symcrypt_sys::SYMCRYPT_GCM_STATE, + pub inner: symcrypt_sys::SYMCRYPT_GCM_STATE, // _pinned is a marker to ensure that instances of the inner state cannot be moved once pinned. // This prevents the struct from implementing the Unpin trait, enforcing that any @@ -1176,18 +1163,6 @@ mod internal { } } - impl Drop for GcmInnerStream { - fn drop(&mut self) { - unsafe { - // SAFETY: FFI calls - symcrypt_sys::SymCryptWipe( - ptr::addr_of_mut!(self.inner) as *mut c_void, // Using addr_of_mut! so we don't access in the inner field - mem::size_of_val(&self.inner) as symcrypt_sys::SIZE_T, // Using size_of_val! so we don't access in the inner field - ); - } - } - } - /// /// This type represents a handle to an initialized GcmStream that /// is used to: @@ -1236,7 +1211,11 @@ mod internal { // unsafe { - std::ptr::drop_in_place(self.0.as_mut().get_unchecked_mut() as *mut _); + symcrypt_sys::SymCryptWipe( + self.0.as_mut().get_inner_mut() as *mut c_void, + mem::size_of::() + as symcrypt_sys::SIZE_T, + ); } } }