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/gcm.rs b/rust-symcrypt/src/gcm.rs index 5b2432ee..b12309b1 100644 --- a/rust-symcrypt/src/gcm.rs +++ b/rust-symcrypt/src/gcm.rs @@ -85,6 +85,7 @@ pub struct GcmExpandedKey { } /// [`GcmInnerKey`] is a struct that holds the underlying SymCrypt state for GCM. +#[derive(Clone, Copy, Default)] struct GcmInnerKey { // inner represents the actual state of the hash from SymCrypt inner: symcrypt_sys::SYMCRYPT_GCM_EXPANDED_KEY, @@ -98,10 +99,7 @@ struct GcmInnerKey { 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, - }) + Box::pin(GcmInnerKey::default()) } /// Provides a mutable pointer to the inner SymCrypt state. @@ -120,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, ); } } @@ -157,109 +155,942 @@ impl GcmExpandedKey { Ok(gcm_expanded_key) } + /// + /// Creates a borrowed reference to the underlying SYMCRYPT_GCM_EXPANDED_KEY. + /// + #[inline(always)] + pub fn as_ref(&self) -> GcmExpandedKeyRef { + self.into() + } + + /// `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], + ) { + 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. + /// + /// `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> { + 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`. + 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 {} + +// 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 an uninitialized SYMCRYPT_GCM_EXPANDED_KEY. +/// +#[derive(Clone, Copy, Default)] +pub struct GcmUninitializedKey(GcmInnerKey); + +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 { + gcm_expand_key( + key_data, + 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. + Ok(GcmExpandedKeyHandle::new(Pin::new_unchecked(&mut self.0))) + } + } +} + +/// +/// 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. +/// 3. Zero the underlying storage when dropped. +/// +pub struct GcmExpandedKeyHandle<'a>(Pin<&'a mut GcmInnerKey>); + +impl<'a> GcmExpandedKeyHandle<'a> { + // + // `new` creates a new handle to an initialized GcmInnerKey. + // + // # Safety + // + // 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 SYMCRYPT_GCM_EXPANDED_KEY 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 Drop for GcmExpandedKeyHandle<'_> { + fn drop(&mut self) { + // + // SAFETY: Is is safe to uninitialize the underlying storage as this + // is the only reference to it and we are being dropped. + // + + unsafe { + symcrypt_sys::SymCryptWipe( + self.0.as_mut().get_inner_mut() as *mut c_void, + mem::size_of::() as symcrypt_sys::SIZE_T, + ); + } + } +} + +/// +/// 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. +/// +/// This type does not zero the underlying storage when dropped. +/// +pub struct GcmExpandedKeyRef<'a>(Pin<&'a GcmInnerKey>); + +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. + /// + /// `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_inner(), + 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_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, + ); + + 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_inner(), + 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. + /// `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_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, + ); + } + } +} + +impl<'a, 'b> From<&'a GcmExpandedKeyHandle<'b>> for GcmExpandedKeyRef<'a> { + fn from(value: &'a GcmExpandedKeyHandle<'b>) -> Self { + GcmExpandedKeyRef(value.0.as_ref()) + } +} + +impl<'a> From<&'a GcmExpandedKey> for GcmExpandedKeyRef<'a> { + fn from(value: &'a GcmExpandedKey) -> Self { + GcmExpandedKeyRef(value.expanded_key.as_ref()) + } +} + +/// +/// This type represents an uninitialized SYMCRYPT_GCM_STATE. +/// +#[derive(Clone, Copy, Default)] +pub struct GcmStream(internal::GcmInnerStream); + +impl GcmStream { + // + // `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( + 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(Pin::new_unchecked(&mut self.0)) + } + } + + /// + /// 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); + } + + /// + /// `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>(internal::GcmInitializedStreamRefMut<'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. + // + + unsafe { + symcrypt_sys::SymCryptGcmAuthPart( + self.0.as_mut().get_inner_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>(internal::GcmInitializedStream<'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()) + } + + /// + /// `complete` finishes this decryption stream and validates that the provided tag matches + /// the generated tag. /// - /// `auth_data` is an optional `&[u8]` that can be provided, if you do not wish to provide any auth data, input an empty array. + /// `tag` is a `&[u8]` that contains the authentication tag generated during encryption. + /// This is used to verify the integrity of the cipher text. /// - /// `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 complete(mut self, tag: &[u8]) -> Result<(), SymCryptError> { + // + // SAFETY: The internal stream is guaranteed to still be initialized while + // self is alive. + // + + let result = unsafe { + symcrypt_sys::SymCryptGcmDecryptFinal( + self.0.as_mut().get_inner_mut(), + tag.as_ptr(), + 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()), + } + } + /// - /// `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(); + /// `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>(internal::GcmInitializedStreamRefMut<'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 + // are the same length. + // + 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, + symcrypt_sys::SymCryptGcmDecryptPart( + self.0.as_mut().get_inner_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 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. + /// `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. /// - /// `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. /// - /// `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. + /// `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. + 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.as_mut().get_inner_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 GcmEncryptionStream<'_> { /// - /// 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(); + /// `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 { - // 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(), + symcrypt_sys::SymCryptGcmEncryptFinal( + self.0.as_mut().get_inner_mut(), + tag.as_mut_ptr(), tag.len() as symcrypt_sys::SIZE_T, - ) { - symcrypt_sys::SYMCRYPT_ERROR_SYMCRYPT_NO_ERROR => Ok(()), - err => Err(err.into()), - } + ); } + + self.0.drop_without_zero(); } - /// `key_len` returns a the length of the [`GcmExpandedKey`] as a `usize`. - pub fn key_len(&self) -> usize { - self.key_length + /// + /// `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); } } -// 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 borrowed mutable handle to an initialized GcmStream that can be used to +/// encrypt data. +/// +pub struct GcmEncryptionStreamRefMut<'a>(internal::GcmInitializedStreamRefMut<'a>); -// 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()), +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 + // are the same length. + // + + unsafe { + symcrypt_sys::SymCryptGcmEncryptPart( + self.0.as_mut().get_inner_mut(), + source.as_ptr(), + destination.as_mut_ptr(), + 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. + // + + unsafe { + symcrypt_sys::SymCryptGcmEncryptPart( + self.0.as_mut().get_inner_mut(), + data.as_ptr(), + data.as_mut_ptr(), + data.len() as symcrypt_sys::SIZE_T, + ); } } } @@ -299,6 +1130,146 @@ pub fn validate_gcm_parameters( } } +mod internal { + + use std::{ + ffi::c_void, + marker::PhantomPinned, + mem, + ops::{Deref, DerefMut}, + pin::Pin, + }; + + #[derive(Clone, Copy, Default)] + pub struct GcmInnerStream { + // inner represents the actual state of the hash from SymCrypt + 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 + // 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 _ } + } + } + + /// + /// 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>(Pin<&'a mut GcmInnerStream>); + + impl<'a> GcmInitializedStream<'a> { + // + // `new` creates a new handle to an initialized GcmStream. + // + // # Safety + // + // The caller must ensure that this is the only pointer to the inner stream. + // + pub unsafe fn new(inner: Pin<&'a mut GcmInnerStream>) -> 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 Drop for GcmInitializedStream<'_> { + fn drop(&mut self) { + // + // SAFETY: Is is safe to uninitialize the underlying storage as this + // is the only reference to it and we are being dropped. + // + + unsafe { + symcrypt_sys::SymCryptWipe( + self.0.as_mut().get_inner_mut() as *mut c_void, + mem::size_of::() + as symcrypt_sys::SIZE_T, + ); + } + } + } + + impl<'a> Deref for GcmInitializedStream<'a> { + type Target = Pin<&'a mut GcmInnerStream>; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl DerefMut for GcmInitializedStream<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut 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>(Pin<&'a mut GcmInnerStream>); + + 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.as_mut()) + } + } + + impl<'a> Deref for GcmInitializedStreamRefMut<'a> { + type Target = Pin<&'a mut GcmInnerStream>; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + + impl DerefMut for GcmInitializedStreamRefMut<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } + } +} + #[cfg(test)] mod test { use super::*; @@ -425,4 +1396,126 @@ 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(()) + } }