From 64d92c21323359c266d677e28ed514cf4ed3566b Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Tue, 24 Jan 2023 12:49:54 +0000 Subject: [PATCH 01/20] `async` stream encryption and decryption --- crates/crypto/src/crypto/stream.rs | 47 +++++++++++++++++++----------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/crates/crypto/src/crypto/stream.rs b/crates/crypto/src/crypto/stream.rs index 83e8cef15..7e363bd3d 100644 --- a/crates/crypto/src/crypto/stream.rs +++ b/crates/crypto/src/crypto/stream.rs @@ -1,7 +1,7 @@ //! This module contains the crate's STREAM implementation, and wrappers that allow us to support multiple AEADs. #![allow(clippy::use_self)] // I think: https://github.com/rust-lang/rust-clippy/issues/3909 -use std::io::{Cursor, Read, Write}; +use std::io::Cursor; use crate::{ primitives::{AEAD_TAG_SIZE, BLOCK_SIZE, KEY_LEN}, @@ -13,6 +13,7 @@ use aead::{ }; use aes_gcm::Aes256Gcm; use chacha20poly1305::XChaCha20Poly1305; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; /// These are all possible algorithms that can be used for encryption and decryption #[derive(Clone, Copy, Eq, PartialEq, Hash)] @@ -105,14 +106,19 @@ impl StreamEncryption { /// It requires a reader, a writer, and any AAD to go with it. /// /// The AAD will be authenticated with each block of data. - pub fn encrypt_streams(mut self, mut reader: R, mut writer: W, aad: &[u8]) -> Result<()> + pub async fn encrypt_streams( + mut self, + mut reader: R, + mut writer: W, + aad: &[u8], + ) -> Result<()> where - R: Read, - W: Write, + R: AsyncReadExt + Unpin + Send, + W: AsyncWriteExt + Unpin + Send, { let mut read_buffer = vec![0u8; BLOCK_SIZE].into_boxed_slice(); loop { - let read_count = reader.read(&mut read_buffer)?; + let read_count = reader.read(&mut read_buffer).await?; if read_count == BLOCK_SIZE { let payload = Payload { aad, @@ -121,7 +127,7 @@ impl StreamEncryption { let encrypted_data = self.encrypt_next(payload).map_err(|_| Error::Encrypt)?; - writer.write_all(&encrypted_data)?; + writer.write_all(&encrypted_data).await?; } else { // we use `..read_count` in order to only use the read data, and not zeroes also let payload = Payload { @@ -130,13 +136,13 @@ impl StreamEncryption { }; let encrypted_data = self.encrypt_last(payload).map_err(|_| Error::Encrypt)?; - writer.write_all(&encrypted_data)?; + writer.write_all(&encrypted_data).await?; break; } } - writer.flush()?; + writer.flush().await?; Ok(()) } @@ -145,7 +151,7 @@ impl StreamEncryption { /// /// It is just a thin wrapper around `encrypt_streams()`, but reduces the amount of code needed elsewhere. #[allow(unused_mut)] - pub fn encrypt_bytes( + pub async fn encrypt_bytes( key: Protected<[u8; KEY_LEN]>, nonce: &[u8], algorithm: Algorithm, @@ -157,6 +163,7 @@ impl StreamEncryption { encryptor .encrypt_streams(bytes, &mut writer, aad) + .await .map_or_else(Err, |_| Ok(writer.into_inner())) } } @@ -218,15 +225,20 @@ impl StreamDecryption { /// It requires a reader, a writer, and any AAD that was used. /// /// The AAD will be authenticated with each block of data - if the AAD doesn't match what was used during encryption, an error will be returned. - pub fn decrypt_streams(mut self, mut reader: R, mut writer: W, aad: &[u8]) -> Result<()> + pub async fn decrypt_streams( + mut self, + mut reader: R, + mut writer: W, + aad: &[u8], + ) -> Result<()> where - R: Read, - W: Write, + R: AsyncReadExt + Unpin + Send, + W: AsyncWriteExt + Unpin + Send, { let mut read_buffer = vec![0u8; BLOCK_SIZE + AEAD_TAG_SIZE].into_boxed_slice(); loop { - let read_count = reader.read(&mut read_buffer)?; + let read_count = reader.read(&mut read_buffer).await?; if read_count == (BLOCK_SIZE + AEAD_TAG_SIZE) { let payload = Payload { aad, @@ -235,7 +247,7 @@ impl StreamDecryption { let decrypted_data = self.decrypt_next(payload).map_err(|_| Error::Decrypt)?; - writer.write_all(&decrypted_data)?; + writer.write_all(&decrypted_data).await?; } else { let payload = Payload { aad, @@ -243,13 +255,13 @@ impl StreamDecryption { }; let decrypted_data = self.decrypt_last(payload).map_err(|_| Error::Decrypt)?; - writer.write_all(&decrypted_data)?; + writer.write_all(&decrypted_data).await?; break; } } - writer.flush()?; + writer.flush().await?; Ok(()) } @@ -258,7 +270,7 @@ impl StreamDecryption { /// /// It is just a thin wrapper around `decrypt_streams()`, but reduces the amount of code needed elsewhere. #[allow(unused_mut)] - pub fn decrypt_bytes( + pub async fn decrypt_bytes( key: Protected<[u8; KEY_LEN]>, nonce: &[u8], algorithm: Algorithm, @@ -270,6 +282,7 @@ impl StreamDecryption { decryptor .decrypt_streams(bytes, &mut writer, aad) + .await .map_or_else(Err, |_| Ok(Protected::new(writer.into_inner()))) } } From b077e16f00396b7ed3e9b46815bc7b4bca8f8554 Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Tue, 24 Jan 2023 13:16:47 +0000 Subject: [PATCH 02/20] mostly async keymanager --- crates/crypto/src/keys/keymanager.rs | 107 ++++++++++++++++----------- 1 file changed, 65 insertions(+), 42 deletions(-) diff --git a/crates/crypto/src/keys/keymanager.rs b/crates/crypto/src/keys/keymanager.rs index 567412b1a..e8e7531fb 100644 --- a/crates/crypto/src/keys/keymanager.rs +++ b/crates/crypto/src/keys/keymanager.rs @@ -131,7 +131,7 @@ impl KeyManager { /// /// It will also generate a verification key, which should be written to the database. #[allow(clippy::needless_pass_by_value)] - pub fn onboarding(config: OnboardingConfig) -> Result { + pub async fn onboarding(config: OnboardingConfig) -> Result { let content_salt = generate_salt(); let secret_key = config.secret_key.map(Self::convert_secret_key_string); @@ -156,13 +156,16 @@ impl KeyManager { let root_key_nonce = generate_nonce(algorithm); // Encrypt the master key with the hashed master password - let encrypted_master_key = to_array::(StreamEncryption::encrypt_bytes( - derive_key(hashed_password, salt, MASTER_PASSWORD_CONTEXT), - &master_key_nonce, - algorithm, - master_key.expose(), - &[], - )?)?; + let encrypted_master_key = to_array::( + StreamEncryption::encrypt_bytes( + derive_key(hashed_password, salt, MASTER_PASSWORD_CONTEXT), + &master_key_nonce, + algorithm, + master_key.expose(), + &[], + ) + .await?, + )?; let encrypted_root_key = StreamEncryption::encrypt_bytes( master_key, @@ -170,7 +173,8 @@ impl KeyManager { algorithm, root_key.expose(), &[], - )?; + ) + .await?; let verification_key = StoredKey { uuid, @@ -235,7 +239,7 @@ impl KeyManager { } #[allow(clippy::needless_pass_by_value)] - pub fn change_master_password( + pub async fn change_master_password( &self, master_password: Protected, algorithm: Algorithm, @@ -263,13 +267,16 @@ impl KeyManager { let salt = generate_salt(); // Encrypt the master key with the hashed master password - let encrypted_master_key = to_array::(StreamEncryption::encrypt_bytes( - derive_key(hashed_password, salt, MASTER_PASSWORD_CONTEXT), - &master_key_nonce, - algorithm, - master_key.expose(), - &[], - )?)?; + let encrypted_master_key = to_array::( + StreamEncryption::encrypt_bytes( + derive_key(hashed_password, salt, MASTER_PASSWORD_CONTEXT), + &master_key_nonce, + algorithm, + master_key.expose(), + &[], + ) + .await?, + )?; let encrypted_root_key = StreamEncryption::encrypt_bytes( master_key, @@ -277,7 +284,8 @@ impl KeyManager { algorithm, root_key.expose(), &[], - )?; + ) + .await?; let verification_key = StoredKey { uuid, @@ -303,7 +311,7 @@ impl KeyManager { /// /// It returns a `Vec` so they can be written to Prisma #[allow(clippy::needless_pass_by_value)] - pub fn import_keystore_backup( + pub async fn import_keystore_backup( &self, master_password: Protected, // at the time of the backup secret_key: Option>, // at the time of the backup @@ -347,7 +355,8 @@ impl KeyManager { old_verification_key.algorithm, &old_verification_key.master_key, &[], - )?; + ) + .await?; // get the root key from the backup let old_root_key = StreamDecryption::decrypt_bytes( @@ -356,7 +365,8 @@ impl KeyManager { old_verification_key.algorithm, &old_verification_key.key, &[], - )?; + ) + .await?; Protected::new(to_array(old_root_key.into_inner())?) } @@ -379,6 +389,7 @@ impl KeyManager { &key.master_key, &[], ) + .await .map_or(Err(Error::IncorrectPassword), |v| { Ok(Protected::new(to_array::(v.into_inner())?)) })?; @@ -389,13 +400,16 @@ impl KeyManager { let salt = generate_salt(); // encrypt the master key with the current root key - let encrypted_master_key = to_array(StreamEncryption::encrypt_bytes( - derive_key(self.get_root_key()?, salt, ROOT_KEY_CONTEXT), - &master_key_nonce, - key.algorithm, - master_key.expose(), - &[], - )?)?; + let encrypted_master_key = to_array( + StreamEncryption::encrypt_bytes( + derive_key(self.get_root_key()?, salt, ROOT_KEY_CONTEXT), + &master_key_nonce, + key.algorithm, + master_key.expose(), + &[], + ) + .await?, + )?; let mut updated_key = key.clone(); updated_key.master_key_nonce = master_key_nonce; @@ -422,7 +436,7 @@ impl KeyManager { /// /// Note: The invalidation function is ran after updating the queue both times, so it isn't required externally. #[allow(clippy::needless_pass_by_value)] - pub fn set_master_password( + pub async fn set_master_password( &self, master_password: Protected, secret_key: Option>, @@ -473,6 +487,7 @@ impl KeyManager { &verification_key.master_key, &[], ) + .await .map_err(|_| { self.remove_from_queue(uuid).ok(); Error::IncorrectKeymanagerDetails @@ -486,7 +501,8 @@ impl KeyManager { verification_key.algorithm, &verification_key.key, &[], - )? + ) + .await? .expose() .clone(), ) @@ -512,7 +528,7 @@ impl KeyManager { /// This is to ensure that only functions which require access to the mounted key receive it. /// /// We could add a log to this, so that the user can view mounts - pub fn mount(&self, uuid: Uuid) -> Result<()> { + pub async fn mount(&self, uuid: Uuid) -> Result<()> { if self.keymount.get(&uuid).is_some() { return Err(Error::KeyAlreadyMounted); } else if self.is_queued(uuid) { @@ -533,6 +549,7 @@ impl KeyManager { &stored_key.master_key, &[], ) + .await .map_or_else( |_| { self.remove_from_queue(uuid).ok(); @@ -548,6 +565,7 @@ impl KeyManager { &stored_key.key, &[], ) + .await .map_err(|e| { self.remove_from_queue(uuid).ok(); e @@ -581,7 +599,7 @@ impl KeyManager { /// This function is used for getting the key value itself, from a given UUID. /// /// The master password/salt needs to be present, so we are able to decrypt the key itself from the stored key. - pub fn get_key(&self, uuid: Uuid) -> Result>> { + pub async fn get_key(&self, uuid: Uuid) -> Result>> { self.keystore .get(&uuid) .map_or(Err(Error::KeyNotFound), |stored_key| { @@ -592,6 +610,7 @@ impl KeyManager { &stored_key.master_key, &[], ) + .await .map_or(Err(Error::IncorrectPassword), |k| { Ok(Protected::new(to_array(k.into_inner())?)) })?; @@ -603,7 +622,8 @@ impl KeyManager { stored_key.algorithm, &stored_key.key, &[], - )?; + ) + .await?; Ok(key) }) @@ -621,7 +641,7 @@ impl KeyManager { /// /// You may optionally provide a content salt, if not one will be generated (used primarily for password-based decryption) #[allow(clippy::needless_pass_by_value)] - pub fn add_to_keystore( + pub async fn add_to_keystore( &self, key: Protected>, algorithm: Algorithm, @@ -643,17 +663,20 @@ impl KeyManager { let salt = generate_salt(); // Encrypt the master key with a derived key (derived from the root key) - let encrypted_master_key = to_array::(StreamEncryption::encrypt_bytes( - derive_key(self.get_root_key()?, salt, ROOT_KEY_CONTEXT), - &master_key_nonce, - algorithm, - master_key.expose(), - &[], - )?)?; + let encrypted_master_key = to_array::( + StreamEncryption::encrypt_bytes( + derive_key(self.get_root_key()?, salt, ROOT_KEY_CONTEXT), + &master_key_nonce, + algorithm, + master_key.expose(), + &[], + ) + .await?, + )?; // Encrypt the actual key (e.g. user-added/autogenerated, text-encodable) let encrypted_key = - StreamEncryption::encrypt_bytes(master_key, &key_nonce, algorithm, &key, &[])?; + StreamEncryption::encrypt_bytes(master_key, &key_nonce, algorithm, &key, &[]).await?; // Insert it into the Keystore self.keystore.insert( From b929d36c89c98104404a29bb5408191ce7a453c7 Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Tue, 24 Jan 2023 13:50:46 +0000 Subject: [PATCH 03/20] more async crypto --- crates/crypto/src/header/keyslot.rs | 28 +++++++++++------- crates/crypto/src/header/metadata.rs | 35 ++++++++++++----------- crates/crypto/src/header/preview_media.rs | 35 ++++++++++++----------- crates/crypto/src/keys/keymanager.rs | 18 ++++++------ 4 files changed, 66 insertions(+), 50 deletions(-) diff --git a/crates/crypto/src/header/keyslot.rs b/crates/crypto/src/header/keyslot.rs index 9f8ffa830..71166a5b5 100644 --- a/crates/crypto/src/header/keyslot.rs +++ b/crates/crypto/src/header/keyslot.rs @@ -64,7 +64,7 @@ impl Keyslot { /// /// You will need to provide the password, and a generated master key (this can't generate it, otherwise it can't be used elsewhere) #[allow(clippy::needless_pass_by_value)] - pub fn new( + pub async fn new( version: KeyslotVersion, algorithm: Algorithm, hashing_algorithm: HashingAlgorithm, @@ -76,13 +76,16 @@ impl Keyslot { let salt = generate_salt(); - let encrypted_master_key = to_array::(StreamEncryption::encrypt_bytes( - derive_key(hashed_key, salt, FILE_KEY_CONTEXT), - &nonce, - algorithm, - master_key.expose(), - &[], - )?)?; + let encrypted_master_key = to_array::( + StreamEncryption::encrypt_bytes( + derive_key(hashed_key, salt, FILE_KEY_CONTEXT), + &nonce, + algorithm, + master_key.expose(), + &[], + ) + .await?, + )?; Ok(Self { version, @@ -101,7 +104,10 @@ impl Keyslot { /// /// An error will be returned on failure. #[allow(clippy::needless_pass_by_value)] - pub fn decrypt_master_key(&self, password: Protected>) -> Result>> { + pub async fn decrypt_master_key( + &self, + password: Protected>, + ) -> Result>> { let key = self .hashing_algorithm .hash(password, self.content_salt, None) @@ -114,6 +120,7 @@ impl Keyslot { &self.master_key, &[], ) + .await } /// This function should not be used directly, use `header.decrypt_master_key()` instead @@ -123,7 +130,7 @@ impl Keyslot { /// No hashing is done internally. /// /// An error will be returned on failure. - pub fn decrypt_master_key_from_prehashed( + pub async fn decrypt_master_key_from_prehashed( &self, key: Protected<[u8; KEY_LEN]>, ) -> Result>> { @@ -134,6 +141,7 @@ impl Keyslot { &self.master_key, &[], ) + .await } /// This function is used to serialize a keyslot into bytes diff --git a/crates/crypto/src/header/metadata.rs b/crates/crypto/src/header/metadata.rs index 829349a1d..2e9c108ff 100644 --- a/crates/crypto/src/header/metadata.rs +++ b/crates/crypto/src/header/metadata.rs @@ -68,7 +68,7 @@ impl FileHeader { /// Metadata needs to be accessed switfly, so a key management system should handle the salt generation. #[cfg(feature = "serde")] #[allow(clippy::needless_pass_by_value)] - pub fn add_metadata( + pub async fn add_metadata( &mut self, version: MetadataVersion, algorithm: Algorithm, @@ -86,7 +86,8 @@ impl FileHeader { algorithm, &serde_json::to_vec(metadata).map_err(|_| Error::Serialization)?, &[], - )?; + ) + .await?; let metadata = Metadata { version, @@ -106,7 +107,7 @@ impl FileHeader { /// /// A deserialized data type will be returned from this function #[cfg(feature = "serde")] - pub fn decrypt_metadata_from_prehashed( + pub async fn decrypt_metadata_from_prehashed( &self, hashed_keys: Vec>, ) -> Result @@ -115,20 +116,21 @@ impl FileHeader { { let master_key = self.decrypt_master_key_from_prehashed(hashed_keys)?; - self.metadata.as_ref().map_or_else( - || Err(Error::NoMetadata), - |metadata| { + match self.metadata.as_ref() { + Some(metadata) => { let metadata = StreamDecryption::decrypt_bytes( master_key, &metadata.metadata_nonce, metadata.algorithm, &metadata.metadata, &[], - )?; + ) + .await?; serde_json::from_slice::(&metadata).map_err(|_| Error::Serialization) - }, - ) + } + None => Err(Error::NoMetadata), + } } /// This function should be used to retrieve the metadata for a file @@ -137,26 +139,27 @@ impl FileHeader { /// /// A deserialized data type will be returned from this function #[cfg(feature = "serde")] - pub fn decrypt_metadata(&self, password: Protected>) -> Result + pub async fn decrypt_metadata(&self, password: Protected>) -> Result where T: serde::de::DeserializeOwned, { let master_key = self.decrypt_master_key(password)?; - self.metadata.as_ref().map_or_else( - || Err(Error::NoMetadata), - |metadata| { + match self.metadata.as_ref() { + Some(metadata) => { let metadata = StreamDecryption::decrypt_bytes( master_key, &metadata.metadata_nonce, metadata.algorithm, &metadata.metadata, &[], - )?; + ) + .await?; serde_json::from_slice::(&metadata).map_err(|_| Error::Serialization) - }, - ) + } + None => Err(Error::NoMetadata), + } } } diff --git a/crates/crypto/src/header/preview_media.rs b/crates/crypto/src/header/preview_media.rs index aa835da02..f6c81f6e2 100644 --- a/crates/crypto/src/header/preview_media.rs +++ b/crates/crypto/src/header/preview_media.rs @@ -57,7 +57,7 @@ impl FileHeader { /// /// Preview media needs to be accessed switfly, so a key management system should handle the salt generation. #[allow(clippy::needless_pass_by_value)] - pub fn add_preview_media( + pub async fn add_preview_media( &mut self, version: PreviewMediaVersion, algorithm: Algorithm, @@ -67,7 +67,8 @@ impl FileHeader { let media_nonce = generate_nonce(algorithm); let encrypted_media = - StreamEncryption::encrypt_bytes(master_key, &media_nonce, algorithm, media, &[])?; + StreamEncryption::encrypt_bytes(master_key, &media_nonce, algorithm, media, &[]) + .await?; let pvm = PreviewMedia { version, @@ -86,26 +87,27 @@ impl FileHeader { /// All it requires is pre-hashed keys returned from the key manager /// /// Once provided, a `Vec` is returned that contains the preview media - pub fn decrypt_preview_media_from_prehashed( + pub async fn decrypt_preview_media_from_prehashed( &self, hashed_keys: Vec>, ) -> Result>> { let master_key = self.decrypt_master_key_from_prehashed(hashed_keys)?; - self.preview_media.as_ref().map_or_else( - || Err(Error::NoPreviewMedia), - |pvm| { + match self.preview_media.as_ref() { + Some(pvm) => { let pvm = StreamDecryption::decrypt_bytes( master_key, &pvm.media_nonce, pvm.algorithm, &pvm.media, &[], - )?; + ) + .await?; Ok(pvm) - }, - ) + } + None => Err(Error::NoPreviewMedia), + } } /// This function is what you'll want to use to get the preview media for a file @@ -113,26 +115,27 @@ impl FileHeader { /// All it requires is the user's password. Hashing is handled for you. /// /// Once provided, a `Vec` is returned that contains the preview media - pub fn decrypt_preview_media( + pub async fn decrypt_preview_media( &self, password: Protected>, ) -> Result>> { let master_key = self.decrypt_master_key(password)?; - self.preview_media.as_ref().map_or_else( - || Err(Error::NoPreviewMedia), - |pvm| { + match self.preview_media.as_ref() { + Some(pvm) => { let pvm = StreamDecryption::decrypt_bytes( master_key, &pvm.media_nonce, pvm.algorithm, &pvm.media, &[], - )?; + ) + .await?; Ok(pvm) - }, - ) + } + None => Err(Error::NoPreviewMedia), + } } } diff --git a/crates/crypto/src/keys/keymanager.rs b/crates/crypto/src/keys/keymanager.rs index e8e7531fb..67cc7964e 100644 --- a/crates/crypto/src/keys/keymanager.rs +++ b/crates/crypto/src/keys/keymanager.rs @@ -535,9 +535,8 @@ impl KeyManager { return Err(Error::KeyAlreadyQueued); } - self.keystore - .get(&uuid) - .map_or(Err(Error::KeyNotFound), |stored_key| { + match self.keystore.get(&uuid) { + Some(stored_key) => { match stored_key.version { StoredKeyVersion::V1 => { self.mounting_queue.insert(uuid); @@ -593,16 +592,17 @@ impl KeyManager { } Ok(()) - }) + } + None => Err(Error::KeyNotFound), + } } /// This function is used for getting the key value itself, from a given UUID. /// /// The master password/salt needs to be present, so we are able to decrypt the key itself from the stored key. pub async fn get_key(&self, uuid: Uuid) -> Result>> { - self.keystore - .get(&uuid) - .map_or(Err(Error::KeyNotFound), |stored_key| { + match self.keystore.get(&uuid) { + Some(stored_key) => { let master_key = StreamDecryption::decrypt_bytes( derive_key(self.get_root_key()?, stored_key.salt, ROOT_KEY_CONTEXT), &stored_key.master_key_nonce, @@ -626,7 +626,9 @@ impl KeyManager { .await?; Ok(key) - }) + } + None => Err(Error::KeyNotFound), + } } /// This function is used to add a new key/password to the keystore. From 87b75cfe62998ea7b2a8ed8b1f99119869e9a249 Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Tue, 24 Jan 2023 14:04:18 +0000 Subject: [PATCH 04/20] return to match/for statements for async iterators (until rust gets better support) --- crates/crypto/src/header/file.rs | 75 +++++++++++++---------- crates/crypto/src/header/metadata.rs | 4 +- crates/crypto/src/header/preview_media.rs | 4 +- 3 files changed, 48 insertions(+), 35 deletions(-) diff --git a/crates/crypto/src/header/file.rs b/crates/crypto/src/header/file.rs index 79b49c58a..9619547b2 100644 --- a/crates/crypto/src/header/file.rs +++ b/crates/crypto/src/header/file.rs @@ -98,7 +98,7 @@ impl FileHeader { /// /// You receive an error if the password doesn't match or if there are no keyslots. #[allow(clippy::needless_pass_by_value)] - pub fn decrypt_master_key( + pub async fn decrypt_master_key( &self, password: Protected>, ) -> Result> { @@ -106,27 +106,18 @@ impl FileHeader { return Err(Error::NoKeyslots); } - self.keyslots - .iter() - .find_map(|v| v.decrypt_master_key(password.clone()).ok()) - .map(|v| Protected::new(to_array::(v.into_inner()).unwrap())) - .ok_or(Error::IncorrectPassword) - } - - /// This is a helper function to find which keyslot a key belongs to. - /// - /// You receive an error if the password doesn't match or if there are no keyslots. - #[allow(clippy::needless_pass_by_value)] - pub fn find_key_index(&self, password: Protected>) -> Result { - if self.keyslots.is_empty() { - return Err(Error::NoKeyslots); + for v in self.keyslots.iter() { + if let Some(key) = v + .decrypt_master_key(password.clone()) + .await + .ok() + .map(|v| Protected::new(to_array::(v.into_inner()).unwrap())) + { + return Ok(key); + } } - self.keyslots - .iter() - .enumerate() - .find_map(|(i, v)| v.decrypt_master_key(password.clone()).ok().map(|_| i)) - .ok_or(Error::IncorrectPassword) + Err(Error::IncorrectPassword) } /// This is a helper function to serialize and write a header to a file. @@ -144,7 +135,7 @@ impl FileHeader { /// /// You receive an error if the password doesn't match or if there are no keyslots. #[allow(clippy::needless_pass_by_value)] - pub fn decrypt_master_key_from_prehashed( + pub async fn decrypt_master_key_from_prehashed( &self, hashed_keys: Vec>, ) -> Result> { @@ -152,16 +143,38 @@ impl FileHeader { return Err(Error::NoKeyslots); } - hashed_keys - .iter() - .find_map(|v| { - self.keyslots.iter().find_map(|z| { - z.decrypt_master_key_from_prehashed(v.clone()) - .ok() - .map(|x| Protected::new(to_array::(x.into_inner()).unwrap())) - }) - }) - .ok_or(Error::IncorrectPassword) + for hashed_key in hashed_keys { + for v in self.keyslots.iter() { + if let Some(key) = v + .decrypt_master_key_from_prehashed(hashed_key.clone()) + .await + .ok() + .map(|v| Protected::new(to_array::(v.into_inner()).unwrap())) + { + return Ok(key); + } + } + } + + Err(Error::IncorrectPassword) + } + + /// This is a helper function to find which keyslot a key belongs to. + /// + /// You receive an error if the password doesn't match or if there are no keyslots. + #[allow(clippy::needless_pass_by_value)] + pub async fn find_key_index(&self, password: Protected>) -> Result { + if self.keyslots.is_empty() { + return Err(Error::NoKeyslots); + } + + for (i, v) in self.keyslots.iter().enumerate() { + if let Some(i) = v.decrypt_master_key(password.clone()).await.ok().map(|_| i) { + return Ok(i); + } + } + + Err(Error::IncorrectPassword) } /// This function should be used for generating AAD before encryption diff --git a/crates/crypto/src/header/metadata.rs b/crates/crypto/src/header/metadata.rs index 2e9c108ff..9a2a0317f 100644 --- a/crates/crypto/src/header/metadata.rs +++ b/crates/crypto/src/header/metadata.rs @@ -114,7 +114,7 @@ impl FileHeader { where T: serde::de::DeserializeOwned, { - let master_key = self.decrypt_master_key_from_prehashed(hashed_keys)?; + let master_key = self.decrypt_master_key_from_prehashed(hashed_keys).await?; match self.metadata.as_ref() { Some(metadata) => { @@ -143,7 +143,7 @@ impl FileHeader { where T: serde::de::DeserializeOwned, { - let master_key = self.decrypt_master_key(password)?; + let master_key = self.decrypt_master_key(password).await?; match self.metadata.as_ref() { Some(metadata) => { diff --git a/crates/crypto/src/header/preview_media.rs b/crates/crypto/src/header/preview_media.rs index f6c81f6e2..2e3ccda08 100644 --- a/crates/crypto/src/header/preview_media.rs +++ b/crates/crypto/src/header/preview_media.rs @@ -91,7 +91,7 @@ impl FileHeader { &self, hashed_keys: Vec>, ) -> Result>> { - let master_key = self.decrypt_master_key_from_prehashed(hashed_keys)?; + let master_key = self.decrypt_master_key_from_prehashed(hashed_keys).await?; match self.preview_media.as_ref() { Some(pvm) => { @@ -119,7 +119,7 @@ impl FileHeader { &self, password: Protected>, ) -> Result>> { - let master_key = self.decrypt_master_key(password)?; + let master_key = self.decrypt_master_key(password).await?; match self.preview_media.as_ref() { Some(pvm) => { From 371ac93bcf6bb5d0d5cb9826abe59d23d508a437 Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Tue, 24 Jan 2023 14:19:27 +0000 Subject: [PATCH 05/20] async crypto! --- core/src/api/keys.rs | 71 ++++++++++++---------- core/src/library/library_manager.rs | 2 +- core/src/object/fs/decrypt.rs | 46 +++++++------- core/src/object/fs/encrypt.rs | 74 ++++++++++++----------- crates/crypto/src/header/file.rs | 65 +++++++++++--------- crates/crypto/src/header/metadata.rs | 20 +++--- crates/crypto/src/header/preview_media.rs | 20 +++--- crates/crypto/src/keys/keymanager.rs | 4 +- 8 files changed, 161 insertions(+), 141 deletions(-) diff --git a/core/src/api/keys.rs b/core/src/api/keys.rs index b0fc99872..65738baa7 100644 --- a/core/src/api/keys.rs +++ b/core/src/api/keys.rs @@ -23,7 +23,7 @@ pub struct KeyAddArgs { } #[derive(Type, Deserialize)] -pub struct SetMasterPasswordArgs { +pub struct UnlockKeyManagerArgs { password: String, secret_key: Option, } @@ -64,14 +64,14 @@ pub(crate) fn mount() -> RouterBuilder { }) .library_query("getKey", |t| { t(|_, key_uuid: Uuid, library| async move { - let key = library.key_manager.get_key(key_uuid)?; + let key = library.key_manager.get_key(key_uuid).await?; Ok(String::from_utf8(key.into_inner()).map_err(Error::StringParse)?) }) }) .library_mutation("mount", |t| { t(|_, key_uuid: Uuid, library| async move { - library.key_manager.mount(key_uuid)?; + library.key_manager.mount(key_uuid).await?; // we also need to dispatch jobs that automatically decrypt preview media and metadata here invalidate_query!(library, "keys.listMounted"); Ok(()) @@ -148,14 +148,17 @@ pub(crate) fn mount() -> RouterBuilder { Ok(()) }) }) - .library_mutation("setMasterPassword", |t| { - t(|_, args: SetMasterPasswordArgs, library| async move { + .library_mutation("unlockKeyManager", |t| { + t(|_, args: UnlockKeyManagerArgs, library| async move { // if this returns an error, the user MUST re-enter the correct password - library.key_manager.set_master_password( - Protected::new(args.password), - args.secret_key.map(Protected::new), - || invalidate_query!(library, "keys.isKeyManagerUnlocking"), - )?; + library + .key_manager + .unlock( + Protected::new(args.password), + args.secret_key.map(Protected::new), + || invalidate_query!(library, "keys.isKeyManagerUnlocking"), + ) + .await?; invalidate_query!(library, "keys.hasMasterPassword"); @@ -169,7 +172,8 @@ pub(crate) fn mount() -> RouterBuilder { for key in automount { library .key_manager - .mount(Uuid::from_str(&key.uuid).map_err(|_| Error::Serialization)?)?; + .mount(Uuid::from_str(&key.uuid).map_err(|_| Error::Serialization)?) + .await?; invalidate_query!(library, "keys.listMounted"); } @@ -222,14 +226,17 @@ pub(crate) fn mount() -> RouterBuilder { .library_mutation("add", |t| { t(|_, args: KeyAddArgs, library| async move { // register the key with the keymanager - let uuid = library.key_manager.add_to_keystore( - Protected::new(args.key.as_bytes().to_vec()), - args.algorithm, - args.hashing_algorithm, - !args.library_sync, - args.automount, - None, - )?; + let uuid = library + .key_manager + .add_to_keystore( + Protected::new(args.key.as_bytes().to_vec()), + args.algorithm, + args.hashing_algorithm, + !args.library_sync, + args.automount, + None, + ) + .await?; if args.library_sync { write_storedkey_to_db(&library.db, &library.key_manager.access_keystore(uuid)?) @@ -248,7 +255,7 @@ pub(crate) fn mount() -> RouterBuilder { } } - library.key_manager.mount(uuid)?; + library.key_manager.mount(uuid).await?; invalidate_query!(library, "keys.list"); invalidate_query!(library, "keys.listMounted"); @@ -290,11 +297,10 @@ pub(crate) fn mount() -> RouterBuilder { let secret_key = args.secret_key.map(Protected::new); - let updated_keys = library.key_manager.import_keystore_backup( - Protected::new(args.password), - secret_key, - &stored_keys, - )?; + let updated_keys = library + .key_manager + .import_keystore_backup(Protected::new(args.password), secret_key, &stored_keys) + .await?; for key in &updated_keys { write_storedkey_to_db(&library.db, key).await?; @@ -310,12 +316,15 @@ pub(crate) fn mount() -> RouterBuilder { t(|_, args: MasterPasswordChangeArgs, library| async move { let secret_key = args.secret_key.map(Protected::new); - let verification_key = library.key_manager.change_master_password( - Protected::new(args.password), - args.algorithm, - args.hashing_algorithm, - secret_key, - )?; + let verification_key = library + .key_manager + .change_master_password( + Protected::new(args.password), + args.algorithm, + args.hashing_algorithm, + secret_key, + ) + .await?; // remove old nil-id keys if they were set library diff --git a/core/src/library/library_manager.rs b/core/src/library/library_manager.rs index 1750a4bbc..b5ac6d5e0 100644 --- a/core/src/library/library_manager.rs +++ b/core/src/library/library_manager.rs @@ -197,7 +197,7 @@ impl LibraryManager { indexer_rules_seeder(&library.db).await?; // setup master password - let verification_key = KeyManager::onboarding(km_config)?; + let verification_key = KeyManager::onboarding(km_config).await?; write_storedkey_to_db(&library.db, &verification_key).await?; diff --git a/core/src/object/fs/decrypt.rs b/core/src/object/fs/decrypt.rs index abe1fb81f..719828c97 100644 --- a/core/src/object/fs/decrypt.rs +++ b/core/src/object/fs/decrypt.rs @@ -1,9 +1,8 @@ use sd_crypto::{crypto::stream::StreamDecryption, header::file::FileHeader, Protected}; use serde::{Deserialize, Serialize}; use specta::Type; -use std::{collections::VecDeque, fs::File, path::PathBuf}; - -use tokio::task; +use std::{collections::VecDeque, path::PathBuf}; +use tokio::fs::File; use crate::job::{JobError, JobReportUpdate, JobResult, JobState, StatefulJob, WorkerContext}; @@ -82,10 +81,10 @@ impl StatefulJob for FileDecryptorJob { |p| p, ); - let mut reader = File::open(info.obj_path.clone())?; - let mut writer = File::create(output_path)?; + let mut reader = File::open(info.obj_path.clone()).await?; + let mut writer = File::create(output_path).await?; - let (header, aad) = FileHeader::from_reader(&mut reader)?; + let (header, aad) = FileHeader::from_reader(&mut reader).await?; let master_key = if let Some(password) = state.init.password.clone() { if let Some(save_to_library) = state.init.save_to_library { @@ -93,20 +92,23 @@ impl StatefulJob for FileDecryptorJob { // we can do this first, as `find_key_index` requires a successful decryption (just like `decrypt_master_key`) if save_to_library { - let index = header.find_key_index(password.clone())?; + let index = header.find_key_index(password.clone()).await?; // inherit the encryption algorithm from the keyslot - ctx.library_ctx.key_manager.add_to_keystore( - password.clone(), - header.algorithm, - header.keyslots[index].hashing_algorithm, - false, - false, - Some(header.keyslots[index].salt), - )?; + ctx.library_ctx + .key_manager + .add_to_keystore( + password.clone(), + header.algorithm, + header.keyslots[index].hashing_algorithm, + false, + false, + Some(header.keyslots[index].salt), + ) + .await?; } - header.decrypt_master_key(password)? + header.decrypt_master_key(password).await? } else { return Err(JobError::JobDataNotFound(String::from( "Password decryption selected, but save to library boolean was not included", @@ -115,16 +117,14 @@ impl StatefulJob for FileDecryptorJob { } else { let keys = ctx.library_ctx.key_manager.enumerate_hashed_keys(); - header.decrypt_master_key_from_prehashed(keys)? + header.decrypt_master_key_from_prehashed(keys).await? }; - task::block_in_place(|| { - let decryptor = StreamDecryption::new(master_key, &header.nonce, header.algorithm)?; + let decryptor = StreamDecryption::new(master_key, &header.nonce, header.algorithm)?; - decryptor.decrypt_streams(&mut reader, &mut writer, &aad)?; - - Ok::<(), JobError>(()) - })?; + decryptor + .decrypt_streams(&mut reader, &mut writer, &aad) + .await?; // need to decrypt preview media/metadata, and maybe add an option in the UI so the user can chosoe to restore these values // for now this can't easily be implemented, as we don't know what the new object id for the file will be (we know the old one, but it may differ) diff --git a/core/src/object/fs/encrypt.rs b/core/src/object/fs/encrypt.rs index 6e98a4261..db3c4cb25 100644 --- a/core/src/object/fs/encrypt.rs +++ b/core/src/object/fs/encrypt.rs @@ -1,6 +1,6 @@ -use std::{collections::VecDeque, fs::File, io::Read, path::PathBuf}; +use std::{collections::VecDeque, path::PathBuf}; -use tokio::task; +use tokio::{fs::File, io::AsyncReadExt}; use chrono::FixedOffset; use sd_crypto::{ @@ -141,22 +141,25 @@ impl StatefulJob for FileEncryptorJob { ) .await?; - let mut reader = task::block_in_place(|| File::open(&info.obj_path))?; - let mut writer = task::block_in_place(|| File::create(output_path))?; + let mut reader = File::open(&info.obj_path).await?; + let mut writer = File::create(output_path).await?; let master_key = generate_master_key(); let mut header = FileHeader::new( LATEST_FILE_HEADER, state.init.algorithm, - vec![Keyslot::new( - LATEST_KEYSLOT, - state.init.algorithm, - user_key_details.hashing_algorithm, - user_key_details.content_salt, - user_key, - master_key.clone(), - )?], + vec![ + Keyslot::new( + LATEST_KEYSLOT, + state.init.algorithm, + user_key_details.hashing_algorithm, + user_key_details.content_salt, + user_key, + master_key.clone(), + ) + .await?, + ], ); if state.init.metadata || state.init.preview_media { @@ -187,12 +190,14 @@ impl StatefulJob for FileEncryptorJob { date_modified: object.date_modified, }; - header.add_metadata( - LATEST_METADATA, - state.init.algorithm, - master_key.clone(), - &metadata, - )?; + header + .add_metadata( + LATEST_METADATA, + state.init.algorithm, + master_key.clone(), + &metadata, + ) + .await?; } // if state.init.preview_media @@ -209,18 +214,17 @@ impl StatefulJob for FileEncryptorJob { if tokio::fs::metadata(&pvm_path).await.is_ok() { let mut pvm_bytes = Vec::new(); - task::block_in_place(|| { - let mut pvm_file = File::open(pvm_path)?; - pvm_file.read_to_end(&mut pvm_bytes)?; - Ok::<_, JobError>(()) - })?; + let mut pvm_file = File::open(pvm_path).await?; + pvm_file.read_to_end(&mut pvm_bytes).await?; - header.add_preview_media( - LATEST_PREVIEW_MEDIA, - state.init.algorithm, - master_key.clone(), - &pvm_bytes, - )?; + header + .add_preview_media( + LATEST_PREVIEW_MEDIA, + state.init.algorithm, + master_key.clone(), + &pvm_bytes, + ) + .await?; } } else { // should use container encryption if it's a directory @@ -230,15 +234,13 @@ impl StatefulJob for FileEncryptorJob { } } - task::block_in_place(|| { - header.write(&mut writer)?; + header.write(&mut writer).await?; - let encryptor = - StreamEncryption::new(master_key, &header.nonce, header.algorithm)?; + let encryptor = StreamEncryption::new(master_key, &header.nonce, header.algorithm)?; - encryptor.encrypt_streams(&mut reader, &mut writer, &header.generate_aad())?; - Ok::<_, JobError>(()) - })?; + encryptor + .encrypt_streams(&mut reader, &mut writer, &header.generate_aad()) + .await?; } _ => warn!( "encryption is skipping {} as it isn't a file", diff --git a/crates/crypto/src/header/file.rs b/crates/crypto/src/header/file.rs index 9619547b2..ea03d3d97 100644 --- a/crates/crypto/src/header/file.rs +++ b/crates/crypto/src/header/file.rs @@ -29,7 +29,9 @@ //! // Write the header to the file //! header.write(&mut writer).unwrap(); //! ``` -use std::io::{Read, Seek, SeekFrom, Write}; +use std::io::SeekFrom; + +use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}; use crate::{ crypto::stream::Algorithm, @@ -121,11 +123,11 @@ impl FileHeader { } /// This is a helper function to serialize and write a header to a file. - pub fn write(&self, writer: &mut W) -> Result<()> + pub async fn write(&self, writer: &mut W) -> Result<()> where - W: Write, + W: AsyncWriteExt + Unpin + Send, { - writer.write_all(&self.to_bytes()?)?; + writer.write_all(&self.to_bytes()?).await?; Ok(()) } @@ -254,12 +256,12 @@ impl FileHeader { /// On error, the cursor will not be rewound. /// /// It returns both the header, and the AAD that should be used for decryption. - pub fn from_reader(reader: &mut R) -> Result<(Self, Vec)> + pub async fn from_reader(reader: &mut R) -> Result<(Self, Vec)> where - R: Read + Seek, + R: AsyncReadExt + AsyncSeekExt + Unpin + Send, { let mut magic_bytes = [0u8; MAGIC_BYTES.len()]; - reader.read_exact(&mut magic_bytes)?; + reader.read_exact(&mut magic_bytes).await?; if magic_bytes != MAGIC_BYTES { return Err(Error::FileHeader); @@ -267,36 +269,38 @@ impl FileHeader { let mut version = [0u8; 2]; - reader.read_exact(&mut version)?; + reader.read_exact(&mut version).await?; let version = FileHeaderVersion::from_bytes(version)?; // Rewind so we can get the AAD - reader.rewind()?; + reader.rewind().await?; // read the aad according to the size let mut aad = vec![0u8; Self::size(version)]; - reader.read_exact(&mut aad)?; + reader.read_exact(&mut aad).await?; // seek back to the start (plus magic bytes and the two version bytes) - reader.seek(SeekFrom::Start(MAGIC_BYTES.len() as u64 + 2))?; + reader + .seek(SeekFrom::Start(MAGIC_BYTES.len() as u64 + 2)) + .await?; // read the header let header = match version { FileHeaderVersion::V1 => { let mut algorithm = [0u8; 2]; - reader.read_exact(&mut algorithm)?; + reader.read_exact(&mut algorithm).await?; let algorithm = Algorithm::from_bytes(algorithm)?; let mut nonce = vec![0u8; algorithm.nonce_len()]; - reader.read_exact(&mut nonce)?; + reader.read_exact(&mut nonce).await?; // read and discard the padding - reader.read_exact(&mut vec![0u8; 25 - nonce.len()])?; + reader.read_exact(&mut vec![0u8; 25 - nonce.len()]).await?; let mut keyslot_bytes = [0u8; (KEYSLOT_SIZE * 2)]; // length of 2x keyslots let mut keyslots: Vec = Vec::new(); - reader.read_exact(&mut keyslot_bytes)?; + reader.read_exact(&mut keyslot_bytes).await?; for _ in 0..2 { Keyslot::from_reader(&mut keyslot_bytes.as_ref()) @@ -304,18 +308,21 @@ impl FileHeader { .ok(); } - let metadata = Metadata::from_reader(reader).map_or_else( - |_| { - reader.seek(SeekFrom::Start( + let metadata = if let Ok(metadata) = Metadata::from_reader(reader).await { + reader + .seek(SeekFrom::Start( Self::size(version) as u64 + (KEYSLOT_SIZE * 2) as u64, - ))?; - Ok::, Error>(None) - }, - |metadata| Ok(Some(metadata)), - )?; + )) + .await?; + Ok::, Error>(Some(metadata)) + } else { + Ok(None) + }?; - let preview_media = PreviewMedia::from_reader(reader).map_or_else( - |_| { + let preview_media = + if let Ok(preview_media) = PreviewMedia::from_reader(reader).await { + Ok::, Error>(Some(preview_media)) + } else { let seek_len = metadata.as_ref().map_or_else( || Self::size(version) as u64 + (KEYSLOT_SIZE * 2) as u64, |metadata| { @@ -324,12 +331,10 @@ impl FileHeader { }, ); - reader.seek(SeekFrom::Start(seek_len))?; + reader.seek(SeekFrom::Start(seek_len)).await?; - Ok::, Error>(None) - }, - |preview_media| Ok(Some(preview_media)), - )?; + Ok(None) + }?; Self { version, diff --git a/crates/crypto/src/header/metadata.rs b/crates/crypto/src/header/metadata.rs index 9a2a0317f..94aa12327 100644 --- a/crates/crypto/src/header/metadata.rs +++ b/crates/crypto/src/header/metadata.rs @@ -27,7 +27,7 @@ //! ) //! .unwrap(); //! ``` -use std::io::Read; +use tokio::io::AsyncReadExt; #[cfg(feature = "serde")] use crate::{ @@ -195,33 +195,35 @@ impl Metadata { /// The cursor will be left at the end of the metadata item on success /// /// The cursor will not be rewound on error. - pub fn from_reader(reader: &mut R) -> Result + pub async fn from_reader(reader: &mut R) -> Result where - R: Read, + R: AsyncReadExt + Unpin + Send, { let mut version = [0u8; 2]; - reader.read_exact(&mut version)?; + reader.read_exact(&mut version).await?; let version = MetadataVersion::from_bytes(version).map_err(|_| Error::NoMetadata)?; match version { MetadataVersion::V1 => { let mut algorithm = [0u8; 2]; - reader.read_exact(&mut algorithm)?; + reader.read_exact(&mut algorithm).await?; let algorithm = Algorithm::from_bytes(algorithm)?; let mut metadata_nonce = vec![0u8; algorithm.nonce_len()]; - reader.read_exact(&mut metadata_nonce)?; + reader.read_exact(&mut metadata_nonce).await?; - reader.read_exact(&mut vec![0u8; 24 - metadata_nonce.len()])?; + reader + .read_exact(&mut vec![0u8; 24 - metadata_nonce.len()]) + .await?; let mut metadata_length = [0u8; 8]; - reader.read_exact(&mut metadata_length)?; + reader.read_exact(&mut metadata_length).await?; let metadata_length = u64::from_le_bytes(metadata_length); #[allow(clippy::cast_possible_truncation)] let mut metadata = vec![0u8; metadata_length as usize]; - reader.read_exact(&mut metadata)?; + reader.read_exact(&mut metadata).await?; let metadata = Self { version, diff --git a/crates/crypto/src/header/preview_media.rs b/crates/crypto/src/header/preview_media.rs index 2e3ccda08..8beefb61b 100644 --- a/crates/crypto/src/header/preview_media.rs +++ b/crates/crypto/src/header/preview_media.rs @@ -20,7 +20,7 @@ //! ) //! .unwrap(); //! ``` -use std::io::Read; +use tokio::io::AsyncReadExt; use crate::{ crypto::stream::{Algorithm, StreamDecryption, StreamEncryption}, @@ -171,34 +171,36 @@ impl PreviewMedia { /// The cursor will be left at the end of the preview media item on success /// /// The cursor will not be rewound on error. - pub fn from_reader(reader: &mut R) -> Result + pub async fn from_reader(reader: &mut R) -> Result where - R: Read, + R: AsyncReadExt + Unpin + Send, { let mut version = [0u8; 2]; - reader.read_exact(&mut version)?; + reader.read_exact(&mut version).await?; let version = PreviewMediaVersion::from_bytes(version).map_err(|_| Error::NoPreviewMedia)?; match version { PreviewMediaVersion::V1 => { let mut algorithm = [0u8; 2]; - reader.read_exact(&mut algorithm)?; + reader.read_exact(&mut algorithm).await?; let algorithm = Algorithm::from_bytes(algorithm)?; let mut media_nonce = vec![0u8; algorithm.nonce_len()]; - reader.read_exact(&mut media_nonce)?; + reader.read_exact(&mut media_nonce).await?; - reader.read_exact(&mut vec![0u8; 24 - media_nonce.len()])?; + reader + .read_exact(&mut vec![0u8; 24 - media_nonce.len()]) + .await?; let mut media_length = [0u8; 8]; - reader.read_exact(&mut media_length)?; + reader.read_exact(&mut media_length).await?; let media_length = u64::from_le_bytes(media_length); #[allow(clippy::cast_possible_truncation)] let mut media = vec![0u8; media_length as usize]; - reader.read_exact(&mut media)?; + reader.read_exact(&mut media).await?; let preview_media = Self { version, diff --git a/crates/crypto/src/keys/keymanager.rs b/crates/crypto/src/keys/keymanager.rs index 67cc7964e..8ce46bdbb 100644 --- a/crates/crypto/src/keys/keymanager.rs +++ b/crates/crypto/src/keys/keymanager.rs @@ -425,7 +425,7 @@ impl KeyManager { Ok(reencrypted_keys) } - /// This requires both the master password and the secret key + /// This is used for unlocking the key manager, and requires both the master password and the secret key. /// /// The master password and secret key are hashed together. /// This minimises the risk of an attacker obtaining the master password, as both of these are required to unlock the vault (and both should be stored separately). @@ -436,7 +436,7 @@ impl KeyManager { /// /// Note: The invalidation function is ran after updating the queue both times, so it isn't required externally. #[allow(clippy::needless_pass_by_value)] - pub async fn set_master_password( + pub async fn unlock( &self, master_password: Protected, secret_key: Option>, From f13c247aecc461e520c7f3796e5567098d716fad Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Tue, 24 Jan 2023 14:25:19 +0000 Subject: [PATCH 06/20] update examples --- crates/crypto/examples/single_file.rs | 30 +++++++++++-------- .../examples/single_file_with_metadata.rs | 28 +++++++++-------- .../single_file_with_preview_media.rs | 28 +++++++++-------- 3 files changed, 49 insertions(+), 37 deletions(-) diff --git a/crates/crypto/examples/single_file.rs b/crates/crypto/examples/single_file.rs index d8a5f1a10..142d6a0e6 100644 --- a/crates/crypto/examples/single_file.rs +++ b/crates/crypto/examples/single_file.rs @@ -1,4 +1,4 @@ -use std::fs::File; +use tokio::fs::File; use sd_crypto::{ crypto::stream::{Algorithm, StreamDecryption, StreamEncryption}, @@ -11,12 +11,12 @@ use sd_crypto::{ const ALGORITHM: Algorithm = Algorithm::XChaCha20Poly1305; const HASHING_ALGORITHM: HashingAlgorithm = HashingAlgorithm::Argon2id(Params::Standard); -pub fn encrypt() { +async fn encrypt() { let password = Protected::new(b"password".to_vec()); // Open both the source and the output file - let mut reader = File::open("test").unwrap(); - let mut writer = File::create("test.encrypted").unwrap(); + let mut reader = File::open("test").await.unwrap(); + let mut writer = File::create("test.encrypted").await.unwrap(); // This needs to be generated here, otherwise we won't have access to it for encryption let master_key = generate_master_key(); @@ -36,13 +36,14 @@ pub fn encrypt() { hashed_password, master_key.clone(), ) + .await .unwrap()]; // Create the header for the encrypted file let header = FileHeader::new(LATEST_FILE_HEADER, ALGORITHM, keyslots); // Write the header to the file - header.write(&mut writer).unwrap(); + header.write(&mut writer).await.unwrap(); // Use the nonce created by the header to initialize a stream encryption object let encryptor = StreamEncryption::new(master_key, &header.nonce, header.algorithm).unwrap(); @@ -51,21 +52,22 @@ pub fn encrypt() { // Use AAD so the header can be authenticated against every block of data encryptor .encrypt_streams(&mut reader, &mut writer, &header.generate_aad()) + .await .unwrap(); } -pub fn decrypt() { +async fn decrypt() { let password = Protected::new(b"password".to_vec()); // Open both the encrypted file and the output file - let mut reader = File::open("test.encrypted").unwrap(); - let mut writer = File::create("test.original").unwrap(); + let mut reader = File::open("test.encrypted").await.unwrap(); + let mut writer = File::create("test.original").await.unwrap(); // Deserialize the header, keyslots, etc from the encrypted file - let (header, aad) = FileHeader::from_reader(&mut reader).unwrap(); + let (header, aad) = FileHeader::from_reader(&mut reader).await.unwrap(); // Decrypt the master key with the user's password - let master_key = header.decrypt_master_key(password).unwrap(); + let master_key = header.decrypt_master_key(password).await.unwrap(); // Initialize a stream decryption object using data provided by the header let decryptor = StreamDecryption::new(master_key, &header.nonce, header.algorithm).unwrap(); @@ -73,11 +75,13 @@ pub fn decrypt() { // Decrypt data the from the writer, and write it to the writer decryptor .decrypt_streams(&mut reader, &mut writer, &aad) + .await .unwrap(); } -fn main() { - encrypt(); +#[tokio::main] +async fn main() { + encrypt().await; - decrypt(); + decrypt().await; } diff --git a/crates/crypto/examples/single_file_with_metadata.rs b/crates/crypto/examples/single_file_with_metadata.rs index c61596dea..ee48c1e00 100644 --- a/crates/crypto/examples/single_file_with_metadata.rs +++ b/crates/crypto/examples/single_file_with_metadata.rs @@ -7,7 +7,7 @@ use sd_crypto::{ primitives::{generate_master_key, generate_salt, LATEST_FILE_HEADER, LATEST_KEYSLOT}, Protected, }; -use std::fs::File; +use tokio::fs::File; const ALGORITHM: Algorithm = Algorithm::XChaCha20Poly1305; const HASHING_ALGORITHM: HashingAlgorithm = HashingAlgorithm::Argon2id(Params::Standard); @@ -16,7 +16,7 @@ pub struct FileInformation { pub file_name: String, } -fn encrypt() { +async fn encrypt() { let password = Protected::new(b"password".to_vec()); let embedded_metadata = FileInformation { @@ -24,8 +24,8 @@ fn encrypt() { }; // Open both the source and the output file - let mut reader = File::open("test").unwrap(); - let mut writer = File::create("test.encrypted").unwrap(); + let mut reader = File::open("test").await.unwrap(); + let mut writer = File::create("test.encrypted").await.unwrap(); // This needs to be generated here, otherwise we won't have access to it for encryption let master_key = generate_master_key(); @@ -45,6 +45,7 @@ fn encrypt() { hashed_password, master_key.clone(), ) + .await .unwrap()]; // Create the header for the encrypted file (and include our metadata) @@ -57,10 +58,11 @@ fn encrypt() { master_key.clone(), &embedded_metadata, ) + .await .unwrap(); // Write the header to the file - header.write(&mut writer).unwrap(); + header.write(&mut writer).await.unwrap(); // Use the nonce created by the header to initialise a stream encryption object let encryptor = StreamEncryption::new(master_key, &header.nonce, header.algorithm).unwrap(); @@ -69,26 +71,28 @@ fn encrypt() { // Use AAD so the header can be authenticated against every block of data encryptor .encrypt_streams(&mut reader, &mut writer, &header.generate_aad()) + .await .unwrap(); } -pub fn decrypt_metadata() { +async fn decrypt_metadata() { let password = Protected::new(b"password".to_vec()); // Open the encrypted file - let mut reader = File::open("test.encrypted").unwrap(); + let mut reader = File::open("test.encrypted").await.unwrap(); // Deserialize the header, keyslots, etc from the encrypted file - let (header, _) = FileHeader::from_reader(&mut reader).unwrap(); + let (header, _) = FileHeader::from_reader(&mut reader).await.unwrap(); // Decrypt the metadata - let file_info: FileInformation = header.decrypt_metadata(password).unwrap(); + let file_info: FileInformation = header.decrypt_metadata(password).await.unwrap(); println!("file name: {}", file_info.file_name); } -fn main() { - encrypt(); +#[tokio::main] +async fn main() { + encrypt().await; - decrypt_metadata(); + decrypt_metadata().await; } diff --git a/crates/crypto/examples/single_file_with_preview_media.rs b/crates/crypto/examples/single_file_with_preview_media.rs index b6d822e99..032f87447 100644 --- a/crates/crypto/examples/single_file_with_preview_media.rs +++ b/crates/crypto/examples/single_file_with_preview_media.rs @@ -1,4 +1,4 @@ -use std::fs::File; +use tokio::fs::File; use sd_crypto::{ crypto::stream::{Algorithm, StreamEncryption}, @@ -11,12 +11,12 @@ use sd_crypto::{ const ALGORITHM: Algorithm = Algorithm::XChaCha20Poly1305; const HASHING_ALGORITHM: HashingAlgorithm = HashingAlgorithm::Argon2id(Params::Standard); -fn encrypt() { +async fn encrypt() { let password = Protected::new(b"password".to_vec()); // Open both the source and the output file - let mut reader = File::open("test").unwrap(); - let mut writer = File::create("test.encrypted").unwrap(); + let mut reader = File::open("test").await.unwrap(); + let mut writer = File::create("test.encrypted").await.unwrap(); // This needs to be generated here, otherwise we won't have access to it for encryption let master_key = generate_master_key(); @@ -36,6 +36,7 @@ fn encrypt() { hashed_password, master_key.clone(), ) + .await .unwrap()]; let pvm_media = b"a nice mountain".to_vec(); @@ -50,10 +51,11 @@ fn encrypt() { master_key.clone(), &pvm_media, ) + .await .unwrap(); // Write the header to the file - header.write(&mut writer).unwrap(); + header.write(&mut writer).await.unwrap(); // Use the nonce created by the header to initialise a stream encryption object let encryptor = StreamEncryption::new(master_key, &header.nonce, header.algorithm).unwrap(); @@ -62,26 +64,28 @@ fn encrypt() { // Use AAD so the header can be authenticated against every block of data encryptor .encrypt_streams(&mut reader, &mut writer, &header.generate_aad()) + .await .unwrap(); } -pub fn decrypt_preview_media() { +async fn decrypt_preview_media() { let password = Protected::new(b"password".to_vec()); // Open the encrypted file - let mut reader = File::open("test.encrypted").unwrap(); + let mut reader = File::open("test.encrypted").await.unwrap(); // Deserialize the header, keyslots, etc from the encrypted file - let (header, _) = FileHeader::from_reader(&mut reader).unwrap(); + let (header, _) = FileHeader::from_reader(&mut reader).await.unwrap(); // Decrypt the preview media - let media = header.decrypt_preview_media(password).unwrap(); + let media = header.decrypt_preview_media(password).await.unwrap(); println!("{:?}", media.expose()); } -fn main() { - encrypt(); +#[tokio::main] +async fn main() { + encrypt().await; - decrypt_preview_media(); + decrypt_preview_media().await; } From 3fd547ab2dc887232d5c1266669de3cccfba3d13 Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Tue, 24 Jan 2023 14:25:42 +0000 Subject: [PATCH 07/20] disable benchmarks as they need async support --- Cargo.lock | 159 +------------------- crates/crypto/Cargo.toml | 28 ++-- crates/crypto/benches/aes-256-gcm.rs | 111 +++++++------- crates/crypto/benches/argon2id.rs | 58 +++---- crates/crypto/benches/xchacha20-poly1305.rs | 104 ++++++------- 5 files changed, 157 insertions(+), 303 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af40c8c2f..fcc46428f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,12 +114,6 @@ dependencies = [ "libc", ] -[[package]] -name = "anes" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" - [[package]] name = "anyhow" version = "1.0.68" @@ -681,12 +675,6 @@ dependencies = [ "toml", ] -[[package]] -name = "cast" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" - [[package]] name = "cc" version = "1.0.73" @@ -785,33 +773,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "ciborium" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" -dependencies = [ - "ciborium-io", - "ciborium-ll", - "serde", -] - -[[package]] -name = "ciborium-io" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" - -[[package]] -name = "ciborium-ll" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" -dependencies = [ - "ciborium-io", - "half 1.8.2", -] - [[package]] name = "cipher" version = "0.4.3" @@ -834,18 +795,6 @@ dependencies = [ "libloading", ] -[[package]] -name = "clap" -version = "3.2.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" -dependencies = [ - "bitflags", - "clap_lex 0.2.4", - "indexmap", - "textwrap", -] - [[package]] name = "clap" version = "4.0.32" @@ -854,7 +803,7 @@ checksum = "a7db700bc935f9e43e88d00b0850dae18a63773cfbec6d8e070fccf7fef89a39" dependencies = [ "bitflags", "clap_derive", - "clap_lex 0.3.0", + "clap_lex", "is-terminal", "once_cell", "strsim", @@ -874,15 +823,6 @@ dependencies = [ "syn", ] -[[package]] -name = "clap_lex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] - [[package]] name = "clap_lex" version = "0.3.0" @@ -897,7 +837,7 @@ name = "cli" version = "0.1.0" dependencies = [ "anyhow", - "clap 4.0.32", + "clap", "hex", "indoc", "sd-crypto", @@ -1109,42 +1049,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "criterion" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" -dependencies = [ - "anes", - "atty", - "cast", - "ciborium", - "clap 3.2.23", - "criterion-plot", - "itertools", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" -dependencies = [ - "cast", - "itertools", -] - [[package]] name = "crossbeam-channel" version = "0.5.6" @@ -1655,7 +1559,7 @@ checksum = "8eb5f255b5980bb0c8cf676b675d1a99be40f316881444f44e0462eaf5df5ded" dependencies = [ "bit_field", "flume", - "half 2.1.0", + "half", "lebe", "miniz_oxide 0.6.2", "smallvec 1.10.0", @@ -2274,12 +2178,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "half" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" - [[package]] name = "half" version = "2.1.0" @@ -3768,12 +3666,6 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - [[package]] name = "opaque-debug" version = "0.3.0" @@ -4251,34 +4143,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "plotters" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" - -[[package]] -name = "plotters-svg" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" -dependencies = [ - "plotters-backend", -] - [[package]] name = "png" version = "0.11.0" @@ -5521,7 +5385,6 @@ dependencies = [ "balloon-hash", "blake3", "chacha20poly1305", - "criterion", "dashmap", "hex", "rand 0.8.5", @@ -6683,12 +6546,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "textwrap" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" - [[package]] name = "thin-slice" version = "0.1.1" @@ -6773,16 +6630,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" -[[package]] -name = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - [[package]] name = "tinyvec" version = "1.6.0" diff --git a/crates/crypto/Cargo.toml b/crates/crypto/Cargo.toml index 0456ff925..1fcc0c984 100644 --- a/crates/crypto/Cargo.toml +++ b/crates/crypto/Cargo.toml @@ -44,24 +44,24 @@ hex = "0.4.3" tokio = { version = "1.21.2", features = ["io-util", "rt-multi-thread"] } -[dev-dependencies] -criterion = "0.4.0" +# [dev-dependencies] +# criterion = "0.4.0" [features] rspc = ["dep:rspc", "dep:specta"] serde = ["dep:serde", "dep:serde_json", "dep:serde-big-array", "uuid/serde"] -[[bench]] -name = "aes-256-gcm" -path = "benches/aes-256-gcm.rs" -harness = false +# [[bench]] +# name = "aes-256-gcm" +# path = "benches/aes-256-gcm.rs" +# harness = false -[[bench]] -name = "xchacha20-poly1305" -path = "benches/xchacha20-poly1305.rs" -harness = false +# [[bench]] +# name = "xchacha20-poly1305" +# path = "benches/xchacha20-poly1305.rs" +# harness = false -[[bench]] -name = "argon2id" -path = "benches/argon2id.rs" -harness = false +# [[bench]] +# name = "argon2id" +# path = "benches/argon2id.rs" +# harness = false diff --git a/crates/crypto/benches/aes-256-gcm.rs b/crates/crypto/benches/aes-256-gcm.rs index de57033d7..a58cc790d 100644 --- a/crates/crypto/benches/aes-256-gcm.rs +++ b/crates/crypto/benches/aes-256-gcm.rs @@ -1,65 +1,72 @@ -use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; -use sd_crypto::{ - crypto::stream::{Algorithm, StreamDecryption, StreamEncryption}, - primitives::{generate_master_key, generate_nonce}, -}; +// use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; +// use sd_crypto::{ +// crypto::stream::{Algorithm, StreamDecryption, StreamEncryption}, +// primitives::{generate_master_key, generate_nonce}, +// }; -const ALGORITHM: Algorithm = Algorithm::Aes256Gcm; +// const ALGORITHM: Algorithm = Algorithm::Aes256Gcm; -const KB: usize = 1024; +// const KB: usize = 1024; -const SIZES: [usize; 8] = [ - KB * 16, - KB * 32, - KB * 64, - KB * 128, - KB * 512, - KB * 1024, - KB * 2048, - KB * 4096, -]; +// const SIZES: [usize; 8] = [ +// KB * 16, +// KB * 32, +// KB * 64, +// KB * 128, +// KB * 512, +// KB * 1024, +// KB * 2048, +// KB * 4096, +// ]; -fn bench(c: &mut Criterion) { - let mut group = c.benchmark_group("aes-256-gcm"); +// async fn bench(c: &mut Criterion) { +// let mut group = c.benchmark_group("aes-256-gcm"); - for size in SIZES { - let buf = vec![0u8; size]; +// for size in SIZES { +// let buf = vec![0u8; size]; - let key = generate_master_key(); - let nonce = generate_nonce(ALGORITHM); +// let key = generate_master_key(); +// let nonce = generate_nonce(ALGORITHM); - let encrypted_bytes = - StreamEncryption::encrypt_bytes(key.clone(), &nonce, ALGORITHM, &buf, &[]).unwrap(); // bytes to decrypt +// let encrypted_bytes = +// StreamEncryption::encrypt_bytes(key.clone(), &nonce, ALGORITHM, &buf, &[]) +// .await +// .unwrap(); // bytes to decrypt - group.throughput(criterion::Throughput::Bytes(size as u64)); +// group.throughput(criterion::Throughput::Bytes(size as u64)); - group.bench_function(BenchmarkId::new("encrypt", size), |b| { - b.iter_batched( - || key.clone(), - |key| StreamEncryption::encrypt_bytes(key, &nonce, ALGORITHM, &buf, &[]).unwrap(), - BatchSize::SmallInput, - ) - }); +// group.bench_function(BenchmarkId::new("encrypt", size), |b| { +// b.iter_batched( +// || key.clone(), +// |key| async { +// StreamEncryption::encrypt_bytes(key, &nonce, ALGORITHM, &buf, &[]) +// .await +// .unwrap() +// }, +// BatchSize::SmallInput, +// ) +// }); - group.bench_function(BenchmarkId::new("decrypt", size), |b| { - b.iter_batched( - || key.clone(), - |key| { - StreamDecryption::decrypt_bytes(key, &nonce, ALGORITHM, &encrypted_bytes, &[]) - .unwrap() - }, - BatchSize::SmallInput, - ) - }); - } +// group.bench_function(BenchmarkId::new("decrypt", size), |b| { +// b.iter_batched( +// || key.clone(), +// |key| async { +// StreamDecryption::decrypt_bytes(key, &nonce, ALGORITHM, &encrypted_bytes, &[]) +// .await +// .unwrap() +// }, +// BatchSize::SmallInput, +// ) +// }); +// } - group.finish(); -} +// group.finish(); +// } -criterion_group!( - name = benches; - config = Criterion::default(); - targets = bench -); +// criterion_group!( +// name = benches; +// config = Criterion::default(); +// targets = bench; +// ); -criterion_main!(benches); +// criterion_main!(benches); diff --git a/crates/crypto/benches/argon2id.rs b/crates/crypto/benches/argon2id.rs index 488bce1c1..88b2adc08 100644 --- a/crates/crypto/benches/argon2id.rs +++ b/crates/crypto/benches/argon2id.rs @@ -1,36 +1,36 @@ -use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; -use sd_crypto::{ - keys::hashing::{HashingAlgorithm, Params}, - primitives::{generate_master_key, generate_salt}, - Protected, -}; +// use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; +// use sd_crypto::{ +// keys::hashing::{HashingAlgorithm, Params}, +// primitives::{generate_master_key, generate_salt}, +// Protected, +// }; -const PARAMS: [Params; 3] = [Params::Standard, Params::Hardened, Params::Paranoid]; +// const PARAMS: [Params; 3] = [Params::Standard, Params::Hardened, Params::Paranoid]; -fn bench(c: &mut Criterion) { - let mut group = c.benchmark_group("argon2id"); +// fn bench(c: &mut Criterion) { +// let mut group = c.benchmark_group("argon2id"); - for param in PARAMS { - let key = Protected::new(generate_master_key().expose().to_vec()); - let salt = generate_salt(); - let hashing_algorithm = HashingAlgorithm::Argon2id(param); +// for param in PARAMS { +// let key = Protected::new(generate_master_key().expose().to_vec()); +// let salt = generate_salt(); +// let hashing_algorithm = HashingAlgorithm::Argon2id(param); - group.bench_function(BenchmarkId::new("hash", param.argon2id().m_cost()), |b| { - b.iter_batched( - || (key.clone(), salt), - |(key, salt)| hashing_algorithm.hash(key, salt, None), - BatchSize::SmallInput, - ) - }); - } +// group.bench_function(BenchmarkId::new("hash", param.argon2id().m_cost()), |b| { +// b.iter_batched( +// || (key.clone(), salt), +// |(key, salt)| hashing_algorithm.hash(key, salt, None), +// BatchSize::SmallInput, +// ) +// }); +// } - group.finish(); -} +// group.finish(); +// } -criterion_group!( - name = benches; - config = Criterion::default(); - targets = bench -); +// criterion_group!( +// name = benches; +// config = Criterion::default(); +// targets = bench +// ); -criterion_main!(benches); +// criterion_main!(benches); diff --git a/crates/crypto/benches/xchacha20-poly1305.rs b/crates/crypto/benches/xchacha20-poly1305.rs index 682395390..75fce0ea0 100644 --- a/crates/crypto/benches/xchacha20-poly1305.rs +++ b/crates/crypto/benches/xchacha20-poly1305.rs @@ -1,65 +1,65 @@ -use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; -use sd_crypto::{ - crypto::stream::{Algorithm, StreamDecryption, StreamEncryption}, - primitives::{generate_master_key, generate_nonce}, -}; +// use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion}; +// use sd_crypto::{ +// crypto::stream::{Algorithm, StreamDecryption, StreamEncryption}, +// primitives::{generate_master_key, generate_nonce}, +// }; -const ALGORITHM: Algorithm = Algorithm::XChaCha20Poly1305; +// const ALGORITHM: Algorithm = Algorithm::XChaCha20Poly1305; -const KB: usize = 1024; +// const KB: usize = 1024; -const SIZES: [usize; 8] = [ - KB * 16, - KB * 32, - KB * 64, - KB * 128, - KB * 512, - KB * 1024, - KB * 2048, - KB * 4096, -]; +// const SIZES: [usize; 8] = [ +// KB * 16, +// KB * 32, +// KB * 64, +// KB * 128, +// KB * 512, +// KB * 1024, +// KB * 2048, +// KB * 4096, +// ]; -fn bench(c: &mut Criterion) { - let mut group = c.benchmark_group("xchacha20-poly1305"); +// fn bench(c: &mut Criterion) { +// let mut group = c.benchmark_group("xchacha20-poly1305"); - for size in SIZES { - let buf = vec![0u8; size]; +// for size in SIZES { +// let buf = vec![0u8; size]; - let key = generate_master_key(); - let nonce = generate_nonce(ALGORITHM); +// let key = generate_master_key(); +// let nonce = generate_nonce(ALGORITHM); - let encrypted_bytes = - StreamEncryption::encrypt_bytes(key.clone(), &nonce, ALGORITHM, &buf, &[]).unwrap(); // bytes to decrypt +// let encrypted_bytes = +// StreamEncryption::encrypt_bytes(key.clone(), &nonce, ALGORITHM, &buf, &[]).unwrap(); // bytes to decrypt - group.throughput(criterion::Throughput::Bytes(size as u64)); +// group.throughput(criterion::Throughput::Bytes(size as u64)); - group.bench_function(BenchmarkId::new("encrypt", size), |b| { - b.iter_batched( - || key.clone(), - |key| StreamEncryption::encrypt_bytes(key, &nonce, ALGORITHM, &buf, &[]).unwrap(), - BatchSize::SmallInput, - ) - }); +// group.bench_function(BenchmarkId::new("encrypt", size), |b| { +// b.iter_batched( +// || key.clone(), +// |key| StreamEncryption::encrypt_bytes(key, &nonce, ALGORITHM, &buf, &[]).unwrap(), +// BatchSize::SmallInput, +// ) +// }); - group.bench_function(BenchmarkId::new("decrypt", size), |b| { - b.iter_batched( - || key.clone(), - |key| { - StreamDecryption::decrypt_bytes(key, &nonce, ALGORITHM, &encrypted_bytes, &[]) - .unwrap() - }, - BatchSize::SmallInput, - ) - }); - } +// group.bench_function(BenchmarkId::new("decrypt", size), |b| { +// b.iter_batched( +// || key.clone(), +// |key| { +// StreamDecryption::decrypt_bytes(key, &nonce, ALGORITHM, &encrypted_bytes, &[]) +// .unwrap() +// }, +// BatchSize::SmallInput, +// ) +// }); +// } - group.finish(); -} +// group.finish(); +// } -criterion_group!( - name = benches; - config = Criterion::default(); - targets = bench -); +// criterion_group!( +// name = benches; +// config = Criterion::default(); +// targets = bench +// ); -criterion_main!(benches); +// criterion_main!(benches); From ecc6ac9a53815a026a4ad04d09ba891c5354172c Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Tue, 24 Jan 2023 14:57:44 +0000 Subject: [PATCH 08/20] update bindings and TS mutations --- packages/client/src/core.ts | 6 +++--- packages/interface/src/components/key/KeyManager.tsx | 2 +- .../interface/src/screens/settings/library/KeysSetting.tsx | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/client/src/core.ts b/packages/client/src/core.ts index 7fbe9283c..56ca4cbd8 100644 --- a/packages/client/src/core.ts +++ b/packages/client/src/core.ts @@ -55,8 +55,8 @@ export type Procedures = { { key: "keys.mount", input: LibraryArgs, result: null } | { key: "keys.restoreKeystore", input: LibraryArgs, result: number } | { key: "keys.setDefault", input: LibraryArgs, result: null } | - { key: "keys.setMasterPassword", input: LibraryArgs, result: null } | { key: "keys.syncKeyToLibrary", input: LibraryArgs, result: null } | + { key: "keys.unlockKeyManager", input: LibraryArgs, result: null } | { key: "keys.unmount", input: LibraryArgs, result: null } | { key: "keys.unmountAll", input: LibraryArgs, result: null } | { key: "keys.updateAutomountStatus", input: LibraryArgs, result: null } | @@ -177,8 +177,6 @@ export type RuleKind = "AcceptFilesByGlob" | "RejectFilesByGlob" | "AcceptIfChil export interface SetFavoriteArgs { id: number, favorite: boolean } -export interface SetMasterPasswordArgs { password: string, secret_key: string | null } - export interface SetNoteArgs { id: number, note: string | null } export interface Statistics { id: number, date_captured: string, total_object_count: number, library_db_size: string, total_bytes_used: string, total_bytes_capacity: string, total_unique_bytes: string, total_bytes_free: string, preview_media_bytes: string } @@ -195,6 +193,8 @@ export interface TagCreateArgs { name: string, color: string } export interface TagUpdateArgs { id: number, name: string | null, color: string | null } +export interface UnlockKeyManagerArgs { password: string, secret_key: string | null } + export interface Volume { name: string, mount_point: string, total_capacity: bigint, available_capacity: bigint, is_removable: boolean, disk_type: string | null, file_system: string | null, is_root_filesystem: boolean } export interface FilePathWithObject { id: number, is_dir: boolean, location_id: number, materialized_path: string, name: string, extension: string | null, object_id: number | null, parent_id: number | null, key_id: number | null, date_created: string, date_modified: string, date_indexed: string, object: Object | null } diff --git a/packages/interface/src/components/key/KeyManager.tsx b/packages/interface/src/components/key/KeyManager.tsx index e9d594b8b..13b4b52e5 100644 --- a/packages/interface/src/components/key/KeyManager.tsx +++ b/packages/interface/src/components/key/KeyManager.tsx @@ -11,7 +11,7 @@ export type KeyManagerProps = DefaultProps; export function KeyManager(props: KeyManagerProps) { const hasMasterPw = useLibraryQuery(['keys.hasMasterPassword']); const isKeyManagerUnlocking = useLibraryQuery(['keys.isKeyManagerUnlocking']); - const setMasterPasswordMutation = useLibraryMutation('keys.setMasterPassword', { + const setMasterPasswordMutation = useLibraryMutation('keys.unlockKeyManager', { onError: () => { alert('Incorrect information provided.'); } diff --git a/packages/interface/src/screens/settings/library/KeysSetting.tsx b/packages/interface/src/screens/settings/library/KeysSetting.tsx index c4c828c49..d0ef16870 100644 --- a/packages/interface/src/screens/settings/library/KeysSetting.tsx +++ b/packages/interface/src/screens/settings/library/KeysSetting.tsx @@ -76,7 +76,7 @@ export const KeyMounterDropdown = ({ export default function KeysSettings() { const platform = usePlatform(); const hasMasterPw = useLibraryQuery(['keys.hasMasterPassword']); - const setMasterPasswordMutation = useLibraryMutation('keys.setMasterPassword', { + const setMasterPasswordMutation = useLibraryMutation('keys.unlockKeyManager', { onError: () => { showAlertDialog({ title: 'Unlock Error', From d8b6789cb7dd9b200b295275b1a7b57fe7a83282 Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Tue, 24 Jan 2023 16:19:30 +0000 Subject: [PATCH 09/20] update `cli` to use `async` crypto --- Cargo.lock | 1 + apps/cli/Cargo.toml | 1 + apps/cli/src/main.rs | 14 ++++++++------ 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fcc46428f..68caa37c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -841,6 +841,7 @@ dependencies = [ "hex", "indoc", "sd-crypto", + "tokio", ] [[package]] diff --git a/apps/cli/Cargo.toml b/apps/cli/Cargo.toml index 5707542af..36c311322 100644 --- a/apps/cli/Cargo.toml +++ b/apps/cli/Cargo.toml @@ -11,3 +11,4 @@ clap = { version = "4.0.32", features = ["derive"] } anyhow = "1.0.68" hex = "0.4.3" sd-crypto = { path = "../../crates/crypto" } +tokio = { version = "1.21.2", features = ["io-util", "rt-multi-thread"] } diff --git a/apps/cli/src/main.rs b/apps/cli/src/main.rs index d35d67a6b..0d5dba93a 100644 --- a/apps/cli/src/main.rs +++ b/apps/cli/src/main.rs @@ -2,7 +2,8 @@ use anyhow::{Context, Result}; use clap::Parser; use indoc::printdoc; use sd_crypto::header::file::FileHeader; -use std::{fs::File, path::PathBuf}; +use std::path::PathBuf; +use tokio::fs::File; #[derive(Parser)] struct Args { @@ -10,17 +11,18 @@ struct Args { path: PathBuf, } -fn main() -> Result<()> { +#[tokio::main] +async fn main() -> Result<()> { let args = Args::parse(); - let mut reader = File::open(args.path).context("unable to open file")?; - let (header, aad) = FileHeader::from_reader(&mut reader)?; - print_details(&header, &aad); + let mut reader = File::open(args.path).await.context("unable to open file")?; + let (header, aad) = FileHeader::from_reader(&mut reader).await?; + print_crypto_details(&header, &aad); Ok(()) } -fn print_details(header: &FileHeader, aad: &[u8]) { +fn print_crypto_details(header: &FileHeader, aad: &[u8]) { printdoc! {" Header version: {version} Encryption algorithm: {algorithm} From 0ba0945662747b1ae49990be3560696e7c7b4d23 Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Tue, 24 Jan 2023 16:22:48 +0000 Subject: [PATCH 10/20] use `alertDialog` within key mnanager panel --- packages/interface/src/components/key/KeyManager.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/interface/src/components/key/KeyManager.tsx b/packages/interface/src/components/key/KeyManager.tsx index 13b4b52e5..fbc348129 100644 --- a/packages/interface/src/components/key/KeyManager.tsx +++ b/packages/interface/src/components/key/KeyManager.tsx @@ -2,6 +2,7 @@ import { Eye, EyeSlash, Gear, Lock } from 'phosphor-react'; import { useState } from 'react'; import { useLibraryMutation, useLibraryQuery } from '@sd/client'; import { Button, ButtonLink, Input, Tabs } from '@sd/ui'; +import { showAlertDialog } from '~/util/dialog'; import { DefaultProps } from '../primitive/types'; import { KeyList } from './KeyList'; import { KeyMounter } from './KeyMounter'; @@ -13,7 +14,10 @@ export function KeyManager(props: KeyManagerProps) { const isKeyManagerUnlocking = useLibraryQuery(['keys.isKeyManagerUnlocking']); const setMasterPasswordMutation = useLibraryMutation('keys.unlockKeyManager', { onError: () => { - alert('Incorrect information provided.'); + showAlertDialog({ + title: 'Unlock Error', + value: 'The information provided to the key manager was incorrect' + }); } }); const unmountAll = useLibraryMutation('keys.unmountAll'); From 5053b6ab53c032228f99e07198ae0a199c41514f Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Tue, 24 Jan 2023 16:23:03 +0000 Subject: [PATCH 11/20] fix broken forms --- .../components/dialog/DecryptFileDialog.tsx | 28 +++++++++---------- .../components/dialog/EncryptFileDialog.tsx | 2 ++ .../dialog/MasterPasswordChangeDialog.tsx | 8 +++--- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/interface/src/components/dialog/DecryptFileDialog.tsx b/packages/interface/src/components/dialog/DecryptFileDialog.tsx index 040dea257..c58ff09af 100644 --- a/packages/interface/src/components/dialog/DecryptFileDialog.tsx +++ b/packages/interface/src/components/dialog/DecryptFileDialog.tsx @@ -41,7 +41,7 @@ export const DecryptFileDialog = (props: DecryptDialogProps) => { const decryptFile = useLibraryMutation('files.decryptFiles', { onSuccess: () => { showAlertDialog({ - title: 'Info', + title: 'Success', value: 'The decryption job has started successfully. You may track the progress in the job overview panel.' }); @@ -59,26 +59,24 @@ export const DecryptFileDialog = (props: DecryptDialogProps) => { const PasswordCurrentEyeIcon = show.password ? EyeSlash : Eye; const form = useZodForm({ - schema, defaultValues: { type: hasMountedKeys ? 'key' : 'password', - saveToKeyManager: true - } + saveToKeyManager: true, + outputPath: '', + password: '' + }, + schema }); - const onSubmit = form.handleSubmit((data) => { - const output = data.outputPath !== '' ? data.outputPath : null; - const pw = data.type === 'password' ? data.password : null; - const save = data.type === 'password' ? data.saveToKeyManager : null; - - return decryptFile.mutateAsync({ + const onSubmit = form.handleSubmit((data) => + decryptFile.mutateAsync({ location_id: props.location_id, path_id: props.path_id, - output_path: output, - password: pw, - save_to_library: save - }); - }); + output_path: data.outputPath !== '' ? data.outputPath : null, + password: data.type === 'password' ? data.password : null, + save_to_library: data.type === 'password' ? data.saveToKeyManager : null + }) + ); return ( { }); const form = useZodForm({ + defaultValues: { encryptionAlgo: 'XChaCha20Poly1305', outputPath: '' }, schema }); @@ -147,6 +148,7 @@ export const EncryptFileDialog = ({ ...props }: EncryptDialogProps) => { className="mt-2 text-gray-400/80" onChange={() => {}} value={form.watch('hashingAlgo')} + disabled > Argon2id (standard) Argon2id (hardened) diff --git a/packages/interface/src/components/dialog/MasterPasswordChangeDialog.tsx b/packages/interface/src/components/dialog/MasterPasswordChangeDialog.tsx index 9c1e7c483..d6c16c778 100644 --- a/packages/interface/src/components/dialog/MasterPasswordChangeDialog.tsx +++ b/packages/interface/src/components/dialog/MasterPasswordChangeDialog.tsx @@ -52,7 +52,9 @@ export const MasterPasswordChangeDialog = (props: MasterPasswordChangeDialogProp schema, defaultValues: { encryptionAlgo: 'XChaCha20Poly1305', - hashingAlgo: 'Argon2id-s' + hashingAlgo: 'Argon2id-s', + masterPassword: '', + masterPassword2: '' } }); @@ -64,13 +66,11 @@ export const MasterPasswordChangeDialog = (props: MasterPasswordChangeDialogProp }); } else { const hashing_algorithm = getHashingAlgorithmSettings(data.hashingAlgo); - const sk = data.secretKey || null; - return changeMasterPassword.mutateAsync({ algorithm: data.encryptionAlgo as Algorithm, hashing_algorithm, password: data.masterPassword, - secret_key: sk + secret_key: data.secretKey || null }); } }); From 3ed106e27b2c5f3994cf4ee5924e61cd3fedc57b Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Tue, 24 Jan 2023 16:35:31 +0000 Subject: [PATCH 12/20] clippy --- crates/crypto/src/header/file.rs | 4 ++-- crates/crypto/src/header/metadata.rs | 8 +++----- crates/crypto/src/header/preview_media.rs | 6 ++---- crates/crypto/src/keys/keymanager.rs | 2 +- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/crates/crypto/src/header/file.rs b/crates/crypto/src/header/file.rs index ea03d3d97..03610ab4c 100644 --- a/crates/crypto/src/header/file.rs +++ b/crates/crypto/src/header/file.rs @@ -108,7 +108,7 @@ impl FileHeader { return Err(Error::NoKeyslots); } - for v in self.keyslots.iter() { + for v in &self.keyslots { if let Some(key) = v .decrypt_master_key(password.clone()) .await @@ -146,7 +146,7 @@ impl FileHeader { } for hashed_key in hashed_keys { - for v in self.keyslots.iter() { + for v in &self.keyslots { if let Some(key) = v .decrypt_master_key_from_prehashed(hashed_key.clone()) .await diff --git a/crates/crypto/src/header/metadata.rs b/crates/crypto/src/header/metadata.rs index 94aa12327..82c1b5bca 100644 --- a/crates/crypto/src/header/metadata.rs +++ b/crates/crypto/src/header/metadata.rs @@ -76,7 +76,7 @@ impl FileHeader { metadata: &T, ) -> Result<()> where - T: ?Sized + serde::Serialize, + T: ?Sized + serde::Serialize + Sync + Send, { let metadata_nonce = generate_nonce(algorithm); @@ -89,14 +89,12 @@ impl FileHeader { ) .await?; - let metadata = Metadata { + self.metadata = Some(Metadata { version, algorithm, metadata_nonce, metadata: encrypted_metadata, - }; - - self.metadata = Some(metadata); + }); Ok(()) } diff --git a/crates/crypto/src/header/preview_media.rs b/crates/crypto/src/header/preview_media.rs index 8beefb61b..d9061b12a 100644 --- a/crates/crypto/src/header/preview_media.rs +++ b/crates/crypto/src/header/preview_media.rs @@ -70,14 +70,12 @@ impl FileHeader { StreamEncryption::encrypt_bytes(master_key, &media_nonce, algorithm, media, &[]) .await?; - let pvm = PreviewMedia { + self.preview_media = Some(PreviewMedia { version, algorithm, media_nonce, media: encrypted_media, - }; - - self.preview_media = Some(pvm); + }); Ok(()) } diff --git a/crates/crypto/src/keys/keymanager.rs b/crates/crypto/src/keys/keymanager.rs index 8ce46bdbb..dbc3b3c98 100644 --- a/crates/crypto/src/keys/keymanager.rs +++ b/crates/crypto/src/keys/keymanager.rs @@ -443,7 +443,7 @@ impl KeyManager { invalidate: F, ) -> Result<()> where - F: Fn(), + F: Fn() + Send, { let uuid = Uuid::nil(); From 546bf643b28be2c571e71a66e12d7c80a1d5b4d7 Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Tue, 24 Jan 2023 16:47:41 +0000 Subject: [PATCH 13/20] add `ProtectedVec` type alias --- crates/crypto/src/crypto/stream.rs | 3 ++- crates/crypto/src/header/file.rs | 5 +++-- crates/crypto/src/header/keyslot.rs | 8 +++----- crates/crypto/src/header/metadata.rs | 7 +++++-- crates/crypto/src/header/preview_media.rs | 7 ++++--- crates/crypto/src/keys/hashing.rs | 13 +++++++------ crates/crypto/src/keys/keymanager.rs | 7 ++++--- crates/crypto/src/protected.rs | 2 ++ 8 files changed, 30 insertions(+), 22 deletions(-) diff --git a/crates/crypto/src/crypto/stream.rs b/crates/crypto/src/crypto/stream.rs index 7e363bd3d..109ecf2a1 100644 --- a/crates/crypto/src/crypto/stream.rs +++ b/crates/crypto/src/crypto/stream.rs @@ -5,6 +5,7 @@ use std::io::Cursor; use crate::{ primitives::{AEAD_TAG_SIZE, BLOCK_SIZE, KEY_LEN}, + protected::ProtectedVec, Error, Protected, Result, }; use aead::{ @@ -276,7 +277,7 @@ impl StreamDecryption { algorithm: Algorithm, bytes: &[u8], aad: &[u8], - ) -> Result>> { + ) -> Result> { let mut writer = Cursor::new(Vec::::new()); let decryptor = Self::new(key, nonce, algorithm)?; diff --git a/crates/crypto/src/header/file.rs b/crates/crypto/src/header/file.rs index 03610ab4c..acfb2aa59 100644 --- a/crates/crypto/src/header/file.rs +++ b/crates/crypto/src/header/file.rs @@ -36,6 +36,7 @@ use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}; use crate::{ crypto::stream::Algorithm, primitives::{generate_nonce, to_array, KEY_LEN}, + protected::ProtectedVec, Error, Protected, Result, }; @@ -102,7 +103,7 @@ impl FileHeader { #[allow(clippy::needless_pass_by_value)] pub async fn decrypt_master_key( &self, - password: Protected>, + password: ProtectedVec, ) -> Result> { if self.keyslots.is_empty() { return Err(Error::NoKeyslots); @@ -165,7 +166,7 @@ impl FileHeader { /// /// You receive an error if the password doesn't match or if there are no keyslots. #[allow(clippy::needless_pass_by_value)] - pub async fn find_key_index(&self, password: Protected>) -> Result { + pub async fn find_key_index(&self, password: ProtectedVec) -> Result { if self.keyslots.is_empty() { return Err(Error::NoKeyslots); } diff --git a/crates/crypto/src/header/keyslot.rs b/crates/crypto/src/header/keyslot.rs index 71166a5b5..8d8cbd8b4 100644 --- a/crates/crypto/src/header/keyslot.rs +++ b/crates/crypto/src/header/keyslot.rs @@ -30,6 +30,7 @@ use crate::{ derive_key, generate_nonce, generate_salt, to_array, ENCRYPTED_KEY_LEN, FILE_KEY_CONTEXT, KEY_LEN, SALT_LEN, }, + protected::ProtectedVec, Error, Protected, Result, }; @@ -104,10 +105,7 @@ impl Keyslot { /// /// An error will be returned on failure. #[allow(clippy::needless_pass_by_value)] - pub async fn decrypt_master_key( - &self, - password: Protected>, - ) -> Result>> { + pub async fn decrypt_master_key(&self, password: ProtectedVec) -> Result> { let key = self .hashing_algorithm .hash(password, self.content_salt, None) @@ -133,7 +131,7 @@ impl Keyslot { pub async fn decrypt_master_key_from_prehashed( &self, key: Protected<[u8; KEY_LEN]>, - ) -> Result>> { + ) -> Result> { StreamDecryption::decrypt_bytes( derive_key(key, self.salt, FILE_KEY_CONTEXT), &self.nonce, diff --git a/crates/crypto/src/header/metadata.rs b/crates/crypto/src/header/metadata.rs index 82c1b5bca..1c9dae758 100644 --- a/crates/crypto/src/header/metadata.rs +++ b/crates/crypto/src/header/metadata.rs @@ -27,7 +27,8 @@ //! ) //! .unwrap(); //! ``` -use tokio::io::AsyncReadExt; +#[cfg(feature = "serde")] +use crate::protected::ProtectedVec; #[cfg(feature = "serde")] use crate::{ @@ -36,6 +37,8 @@ use crate::{ Protected, }; +use tokio::io::AsyncReadExt; + use crate::{crypto::stream::Algorithm, Error, Result}; use super::file::FileHeader; @@ -137,7 +140,7 @@ impl FileHeader { /// /// A deserialized data type will be returned from this function #[cfg(feature = "serde")] - pub async fn decrypt_metadata(&self, password: Protected>) -> Result + pub async fn decrypt_metadata(&self, password: ProtectedVec) -> Result where T: serde::de::DeserializeOwned, { diff --git a/crates/crypto/src/header/preview_media.rs b/crates/crypto/src/header/preview_media.rs index d9061b12a..79e0849e5 100644 --- a/crates/crypto/src/header/preview_media.rs +++ b/crates/crypto/src/header/preview_media.rs @@ -25,6 +25,7 @@ use tokio::io::AsyncReadExt; use crate::{ crypto::stream::{Algorithm, StreamDecryption, StreamEncryption}, primitives::{generate_nonce, KEY_LEN}, + protected::ProtectedVec, Error, Protected, Result, }; @@ -88,7 +89,7 @@ impl FileHeader { pub async fn decrypt_preview_media_from_prehashed( &self, hashed_keys: Vec>, - ) -> Result>> { + ) -> Result> { let master_key = self.decrypt_master_key_from_prehashed(hashed_keys).await?; match self.preview_media.as_ref() { @@ -115,8 +116,8 @@ impl FileHeader { /// Once provided, a `Vec` is returned that contains the preview media pub async fn decrypt_preview_media( &self, - password: Protected>, - ) -> Result>> { + password: ProtectedVec, + ) -> Result> { let master_key = self.decrypt_master_key(password).await?; match self.preview_media.as_ref() { diff --git a/crates/crypto/src/keys/hashing.rs b/crates/crypto/src/keys/hashing.rs index 11a945043..ab1d791d2 100644 --- a/crates/crypto/src/keys/hashing.rs +++ b/crates/crypto/src/keys/hashing.rs @@ -12,6 +12,7 @@ //! ``` use crate::primitives::KEY_LEN; +use crate::protected::ProtectedVec; use crate::Protected; use crate::{primitives::SALT_LEN, Error, Result}; use argon2::Argon2; @@ -52,9 +53,9 @@ impl HashingAlgorithm { #[allow(clippy::needless_pass_by_value)] pub fn hash( &self, - password: Protected>, + password: ProtectedVec, salt: [u8; SALT_LEN], - secret: Option>>, + secret: Option>, ) -> Result> { match self { Self::Argon2id(params) => PasswordHasher::argon2id(password, salt, secret, *params), @@ -106,9 +107,9 @@ struct PasswordHasher; impl PasswordHasher { #[allow(clippy::needless_pass_by_value)] fn argon2id( - password: Protected>, + password: ProtectedVec, salt: [u8; SALT_LEN], - secret: Option>>, + secret: Option>, params: Params, ) -> Result> { let secret = secret.map_or(Protected::new(vec![]), |k| k); @@ -129,9 +130,9 @@ impl PasswordHasher { #[allow(clippy::needless_pass_by_value)] fn balloon_blake3( - password: Protected>, + password: ProtectedVec, salt: [u8; SALT_LEN], - secret: Option>>, + secret: Option>, params: Params, ) -> Result> { let secret = secret.map_or(Protected::new(vec![]), |k| k); diff --git a/crates/crypto/src/keys/keymanager.rs b/crates/crypto/src/keys/keymanager.rs index dbc3b3c98..b0be50ebe 100644 --- a/crates/crypto/src/keys/keymanager.rs +++ b/crates/crypto/src/keys/keymanager.rs @@ -42,6 +42,7 @@ use crate::primitives::{ derive_key, generate_master_key, generate_nonce, generate_salt, to_array, OnboardingConfig, KEY_LEN, LATEST_STORED_KEY, MASTER_PASSWORD_CONTEXT, ROOT_KEY_CONTEXT, }; +use crate::protected::ProtectedVec; use crate::{ crypto::stream::Algorithm, primitives::{ENCRYPTED_KEY_LEN, SALT_LEN}, @@ -600,7 +601,7 @@ impl KeyManager { /// This function is used for getting the key value itself, from a given UUID. /// /// The master password/salt needs to be present, so we are able to decrypt the key itself from the stored key. - pub async fn get_key(&self, uuid: Uuid) -> Result>> { + pub async fn get_key(&self, uuid: Uuid) -> Result> { match self.keystore.get(&uuid) { Some(stored_key) => { let master_key = StreamDecryption::decrypt_bytes( @@ -645,7 +646,7 @@ impl KeyManager { #[allow(clippy::needless_pass_by_value)] pub async fn add_to_keystore( &self, - key: Protected>, + key: ProtectedVec, algorithm: Algorithm, hashing_algorithm: HashingAlgorithm, memory_only: bool, @@ -704,7 +705,7 @@ impl KeyManager { } #[allow(clippy::needless_pass_by_value)] - fn convert_secret_key_string(secret_key: Protected) -> Protected> { + fn convert_secret_key_string(secret_key: Protected) -> ProtectedVec { Protected::new(secret_key.expose().as_bytes().to_vec()) } diff --git a/crates/crypto/src/protected.rs b/crates/crypto/src/protected.rs index 4755f1cb3..27a3629e0 100644 --- a/crates/crypto/src/protected.rs +++ b/crates/crypto/src/protected.rs @@ -31,6 +31,8 @@ use std::{fmt::Debug, mem::swap}; use zeroize::Zeroize; +pub type ProtectedVec = Protected>; + #[derive(Clone)] pub struct Protected where From c75e983fd43c1fa73556756a4e516d388af2f1ae Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Tue, 24 Jan 2023 17:01:50 +0000 Subject: [PATCH 14/20] bring in `Salt`, `Key` and `EncryptedKey` type aliases --- crates/crypto/src/crypto/stream.rs | 10 ++++---- crates/crypto/src/header/file.rs | 11 +++------ crates/crypto/src/header/keyslot.rs | 18 +++++++------- crates/crypto/src/header/metadata.rs | 9 ++++--- crates/crypto/src/header/preview_media.rs | 6 ++--- crates/crypto/src/keys/hashing.rs | 16 ++++++------ crates/crypto/src/keys/keymanager.rs | 30 ++++++++++------------- crates/crypto/src/primitives.rs | 16 ++++++------ crates/crypto/src/protected.rs | 1 - 9 files changed, 56 insertions(+), 61 deletions(-) diff --git a/crates/crypto/src/crypto/stream.rs b/crates/crypto/src/crypto/stream.rs index 109ecf2a1..8106f183f 100644 --- a/crates/crypto/src/crypto/stream.rs +++ b/crates/crypto/src/crypto/stream.rs @@ -4,7 +4,7 @@ use std::io::Cursor; use crate::{ - primitives::{AEAD_TAG_SIZE, BLOCK_SIZE, KEY_LEN}, + primitives::{Key, AEAD_TAG_SIZE, BLOCK_SIZE}, protected::ProtectedVec, Error, Protected, Result, }; @@ -55,7 +55,7 @@ impl StreamEncryption { /// /// The master key, a suitable nonce, and a specific algorithm should be provided. #[allow(clippy::needless_pass_by_value)] - pub fn new(key: Protected<[u8; KEY_LEN]>, nonce: &[u8], algorithm: Algorithm) -> Result { + pub fn new(key: Protected, nonce: &[u8], algorithm: Algorithm) -> Result { if nonce.len() != algorithm.nonce_len() { return Err(Error::NonceLengthMismatch); } @@ -153,7 +153,7 @@ impl StreamEncryption { /// It is just a thin wrapper around `encrypt_streams()`, but reduces the amount of code needed elsewhere. #[allow(unused_mut)] pub async fn encrypt_bytes( - key: Protected<[u8; KEY_LEN]>, + key: Protected, nonce: &[u8], algorithm: Algorithm, bytes: &[u8], @@ -174,7 +174,7 @@ impl StreamDecryption { /// /// The master key, nonce and algorithm that were used for encryption should be provided. #[allow(clippy::needless_pass_by_value)] - pub fn new(key: Protected<[u8; KEY_LEN]>, nonce: &[u8], algorithm: Algorithm) -> Result { + pub fn new(key: Protected, nonce: &[u8], algorithm: Algorithm) -> Result { if nonce.len() != algorithm.nonce_len() { return Err(Error::NonceLengthMismatch); } @@ -272,7 +272,7 @@ impl StreamDecryption { /// It is just a thin wrapper around `decrypt_streams()`, but reduces the amount of code needed elsewhere. #[allow(unused_mut)] pub async fn decrypt_bytes( - key: Protected<[u8; KEY_LEN]>, + key: Protected, nonce: &[u8], algorithm: Algorithm, bytes: &[u8], diff --git a/crates/crypto/src/header/file.rs b/crates/crypto/src/header/file.rs index acfb2aa59..60b3659ef 100644 --- a/crates/crypto/src/header/file.rs +++ b/crates/crypto/src/header/file.rs @@ -35,7 +35,7 @@ use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}; use crate::{ crypto::stream::Algorithm, - primitives::{generate_nonce, to_array, KEY_LEN}, + primitives::{generate_nonce, to_array, Key, KEY_LEN}, protected::ProtectedVec, Error, Protected, Result, }; @@ -101,10 +101,7 @@ impl FileHeader { /// /// You receive an error if the password doesn't match or if there are no keyslots. #[allow(clippy::needless_pass_by_value)] - pub async fn decrypt_master_key( - &self, - password: ProtectedVec, - ) -> Result> { + pub async fn decrypt_master_key(&self, password: ProtectedVec) -> Result> { if self.keyslots.is_empty() { return Err(Error::NoKeyslots); } @@ -140,8 +137,8 @@ impl FileHeader { #[allow(clippy::needless_pass_by_value)] pub async fn decrypt_master_key_from_prehashed( &self, - hashed_keys: Vec>, - ) -> Result> { + hashed_keys: Vec>, + ) -> Result> { if self.keyslots.is_empty() { return Err(Error::NoKeyslots); } diff --git a/crates/crypto/src/header/keyslot.rs b/crates/crypto/src/header/keyslot.rs index 8d8cbd8b4..e15b1b7c4 100644 --- a/crates/crypto/src/header/keyslot.rs +++ b/crates/crypto/src/header/keyslot.rs @@ -27,8 +27,8 @@ use crate::{ crypto::stream::{Algorithm, StreamDecryption, StreamEncryption}, keys::hashing::HashingAlgorithm, primitives::{ - derive_key, generate_nonce, generate_salt, to_array, ENCRYPTED_KEY_LEN, FILE_KEY_CONTEXT, - KEY_LEN, SALT_LEN, + derive_key, generate_nonce, generate_salt, to_array, EncryptedKey, Key, Salt, + ENCRYPTED_KEY_LEN, FILE_KEY_CONTEXT, SALT_LEN, }, protected::ProtectedVec, Error, Protected, Result, @@ -42,9 +42,9 @@ pub struct Keyslot { pub version: KeyslotVersion, pub algorithm: Algorithm, // encryption algorithm pub hashing_algorithm: HashingAlgorithm, // password hashing algorithm - pub salt: [u8; SALT_LEN], // the salt used for deriving a KEK from a (key/content salt) hash - pub content_salt: [u8; SALT_LEN], - pub master_key: [u8; ENCRYPTED_KEY_LEN], // this is encrypted so we can store it + pub salt: Salt, // the salt used for deriving a KEK from a (key/content salt) hash + pub content_salt: Salt, + pub master_key: EncryptedKey, // this is encrypted so we can store it pub nonce: Vec, } @@ -69,9 +69,9 @@ impl Keyslot { version: KeyslotVersion, algorithm: Algorithm, hashing_algorithm: HashingAlgorithm, - content_salt: [u8; SALT_LEN], - hashed_key: Protected<[u8; KEY_LEN]>, - master_key: Protected<[u8; KEY_LEN]>, + content_salt: Salt, + hashed_key: Protected, + master_key: Protected, ) -> Result { let nonce = generate_nonce(algorithm); @@ -130,7 +130,7 @@ impl Keyslot { /// An error will be returned on failure. pub async fn decrypt_master_key_from_prehashed( &self, - key: Protected<[u8; KEY_LEN]>, + key: Protected, ) -> Result> { StreamDecryption::decrypt_bytes( derive_key(key, self.salt, FILE_KEY_CONTEXT), diff --git a/crates/crypto/src/header/metadata.rs b/crates/crypto/src/header/metadata.rs index 1c9dae758..f0138aba8 100644 --- a/crates/crypto/src/header/metadata.rs +++ b/crates/crypto/src/header/metadata.rs @@ -30,10 +30,13 @@ #[cfg(feature = "serde")] use crate::protected::ProtectedVec; +#[cfg(feature = "serde")] +use crate::primitives::Key; + #[cfg(feature = "serde")] use crate::{ crypto::stream::{StreamDecryption, StreamEncryption}, - primitives::{generate_nonce, KEY_LEN}, + primitives::generate_nonce, Protected, }; @@ -75,7 +78,7 @@ impl FileHeader { &mut self, version: MetadataVersion, algorithm: Algorithm, - master_key: Protected<[u8; KEY_LEN]>, + master_key: Protected, metadata: &T, ) -> Result<()> where @@ -110,7 +113,7 @@ impl FileHeader { #[cfg(feature = "serde")] pub async fn decrypt_metadata_from_prehashed( &self, - hashed_keys: Vec>, + hashed_keys: Vec>, ) -> Result where T: serde::de::DeserializeOwned, diff --git a/crates/crypto/src/header/preview_media.rs b/crates/crypto/src/header/preview_media.rs index 79e0849e5..101d02669 100644 --- a/crates/crypto/src/header/preview_media.rs +++ b/crates/crypto/src/header/preview_media.rs @@ -24,7 +24,7 @@ use tokio::io::AsyncReadExt; use crate::{ crypto::stream::{Algorithm, StreamDecryption, StreamEncryption}, - primitives::{generate_nonce, KEY_LEN}, + primitives::{generate_nonce, Key}, protected::ProtectedVec, Error, Protected, Result, }; @@ -62,7 +62,7 @@ impl FileHeader { &mut self, version: PreviewMediaVersion, algorithm: Algorithm, - master_key: Protected<[u8; KEY_LEN]>, + master_key: Protected, media: &[u8], ) -> Result<()> { let media_nonce = generate_nonce(algorithm); @@ -88,7 +88,7 @@ impl FileHeader { /// Once provided, a `Vec` is returned that contains the preview media pub async fn decrypt_preview_media_from_prehashed( &self, - hashed_keys: Vec>, + hashed_keys: Vec>, ) -> Result> { let master_key = self.decrypt_master_key_from_prehashed(hashed_keys).await?; diff --git a/crates/crypto/src/keys/hashing.rs b/crates/crypto/src/keys/hashing.rs index ab1d791d2..73756afbf 100644 --- a/crates/crypto/src/keys/hashing.rs +++ b/crates/crypto/src/keys/hashing.rs @@ -11,10 +11,10 @@ //! let hashed_password = hashing_algorithm.hash(password, salt).unwrap(); //! ``` -use crate::primitives::KEY_LEN; +use crate::primitives::{Key, Salt, KEY_LEN}; use crate::protected::ProtectedVec; use crate::Protected; -use crate::{primitives::SALT_LEN, Error, Result}; +use crate::{Error, Result}; use argon2::Argon2; use balloon_hash::Balloon; @@ -54,9 +54,9 @@ impl HashingAlgorithm { pub fn hash( &self, password: ProtectedVec, - salt: [u8; SALT_LEN], + salt: Salt, secret: Option>, - ) -> Result> { + ) -> Result> { match self { Self::Argon2id(params) => PasswordHasher::argon2id(password, salt, secret, *params), Self::BalloonBlake3(params) => { @@ -108,10 +108,10 @@ impl PasswordHasher { #[allow(clippy::needless_pass_by_value)] fn argon2id( password: ProtectedVec, - salt: [u8; SALT_LEN], + salt: Salt, secret: Option>, params: Params, - ) -> Result> { + ) -> Result> { let secret = secret.map_or(Protected::new(vec![]), |k| k); let mut key = [0u8; KEY_LEN]; @@ -131,10 +131,10 @@ impl PasswordHasher { #[allow(clippy::needless_pass_by_value)] fn balloon_blake3( password: ProtectedVec, - salt: [u8; SALT_LEN], + salt: Salt, secret: Option>, params: Params, - ) -> Result> { + ) -> Result> { let secret = secret.map_or(Protected::new(vec![]), |k| k); let mut key = [0u8; KEY_LEN]; diff --git a/crates/crypto/src/keys/keymanager.rs b/crates/crypto/src/keys/keymanager.rs index b0be50ebe..2396e3cd2 100644 --- a/crates/crypto/src/keys/keymanager.rs +++ b/crates/crypto/src/keys/keymanager.rs @@ -39,15 +39,11 @@ use std::sync::Mutex; use crate::crypto::stream::{StreamDecryption, StreamEncryption}; use crate::primitives::{ - derive_key, generate_master_key, generate_nonce, generate_salt, to_array, OnboardingConfig, - KEY_LEN, LATEST_STORED_KEY, MASTER_PASSWORD_CONTEXT, ROOT_KEY_CONTEXT, + derive_key, generate_master_key, generate_nonce, generate_salt, to_array, EncryptedKey, Key, + OnboardingConfig, Salt, KEY_LEN, LATEST_STORED_KEY, MASTER_PASSWORD_CONTEXT, ROOT_KEY_CONTEXT, }; use crate::protected::ProtectedVec; -use crate::{ - crypto::stream::Algorithm, - primitives::{ENCRYPTED_KEY_LEN, SALT_LEN}, - Protected, -}; +use crate::{crypto::stream::Algorithm, primitives::ENCRYPTED_KEY_LEN, Protected}; use crate::{Error, Result}; use dashmap::{DashMap, DashSet}; @@ -67,13 +63,13 @@ pub struct StoredKey { pub version: StoredKeyVersion, pub algorithm: Algorithm, // encryption algorithm for encrypting the master key. can be changed (requires a re-encryption though) pub hashing_algorithm: HashingAlgorithm, // hashing algorithm used for hashing the key with the content salt - pub content_salt: [u8; SALT_LEN], + pub content_salt: Salt, #[cfg_attr(feature = "serde", serde(with = "BigArray"))] // salt used for file data - pub master_key: [u8; ENCRYPTED_KEY_LEN], // this is for encrypting the `key` + pub master_key: EncryptedKey, // this is for encrypting the `key` pub master_key_nonce: Vec, // nonce for encrypting the master key pub key_nonce: Vec, // nonce used for encrypting the main key pub key: Vec, // encrypted. the key stored in spacedrive (e.g. generated 64 char key) - pub salt: [u8; SALT_LEN], + pub salt: Salt, pub memory_only: bool, pub automount: bool, } @@ -90,8 +86,8 @@ pub enum StoredKeyVersion { /// This contains the plaintext key, and the same key hashed with the content salt. #[derive(Clone)] pub struct MountedKey { - pub uuid: Uuid, // used for identification. shared with stored keys - pub hashed_key: Protected<[u8; KEY_LEN]>, // this is hashed with the content salt, for instant access + pub uuid: Uuid, // used for identification. shared with stored keys + pub hashed_key: Protected, // this is hashed with the content salt, for instant access } /// This is the key manager itself. @@ -100,7 +96,7 @@ pub struct MountedKey { /// /// Use the associated functions to interact with it. pub struct KeyManager { - root_key: Mutex>>, // the root key for the vault + root_key: Mutex>>, // the root key for the vault verification_key: Mutex>, keystore: DashMap, keymount: DashMap, @@ -651,7 +647,7 @@ impl KeyManager { hashing_algorithm: HashingAlgorithm, memory_only: bool, automount: bool, - content_salt: Option<[u8; SALT_LEN]>, + content_salt: Option, ) -> Result { let uuid = uuid::Uuid::new_v4(); @@ -741,7 +737,7 @@ impl KeyManager { } /// This should ONLY be used internally. - fn get_root_key(&self) -> Result> { + fn get_root_key(&self) -> Result> { self.root_key.lock()?.clone().ok_or(Error::NoMasterPassword) } @@ -779,11 +775,11 @@ impl KeyManager { /// /// This means we don't need to keep super specific track of which key goes to which file, and we can just throw all of them at it. #[must_use] - pub fn enumerate_hashed_keys(&self) -> Vec> { + pub fn enumerate_hashed_keys(&self) -> Vec> { self.keymount .iter() .map(|mounted_key| mounted_key.hashed_key.clone()) - .collect::>>() + .collect::>>() } /// This function is for converting a memory-only key to a saved key which syncs to the library. diff --git a/crates/crypto/src/primitives.rs b/crates/crypto/src/primitives.rs index 982e7deb0..e15d2cf8f 100644 --- a/crates/crypto/src/primitives.rs +++ b/crates/crypto/src/primitives.rs @@ -44,6 +44,10 @@ pub const MASTER_PASSWORD_CONTEXT: &str = "spacedrive 2022-12-14 15:35:41 master password hash derivation"; // used for deriving keys from the master password hash pub const FILE_KEY_CONTEXT: &str = "spacedrive 2022-12-14 12:54:12 file key derivation"; // used for deriving keys from user key/content salt hashes (for file encryption) +pub type Key = [u8; KEY_LEN]; +pub type EncryptedKey = [u8; ENCRYPTED_KEY_LEN]; +pub type Salt = [u8; SALT_LEN]; + #[derive(Clone)] #[cfg_attr(feature = "serde", derive(serde::Deserialize))] #[cfg_attr(feature = "rspc", derive(specta::Type))] @@ -70,7 +74,7 @@ pub fn generate_nonce(algorithm: Algorithm) -> Vec { /// /// This function uses `ChaCha20Rng` for generating cryptographically-secure random data #[must_use] -pub fn generate_salt() -> [u8; SALT_LEN] { +pub fn generate_salt() -> Salt { let mut salt = [0u8; SALT_LEN]; rand_chacha::ChaCha20Rng::from_entropy().fill_bytes(&mut salt); salt @@ -82,7 +86,7 @@ pub fn generate_salt() -> [u8; SALT_LEN] { /// /// This function uses `ChaCha20Rng` for generating cryptographically-secure random data #[must_use] -pub fn generate_master_key() -> Protected<[u8; KEY_LEN]> { +pub fn generate_master_key() -> Protected { let mut master_key = [0u8; KEY_LEN]; rand_chacha::ChaCha20Rng::from_entropy().fill_bytes(&mut master_key); Protected::new(master_key) @@ -90,11 +94,7 @@ pub fn generate_master_key() -> Protected<[u8; KEY_LEN]> { #[must_use] #[allow(clippy::needless_pass_by_value)] -pub fn derive_key( - key: Protected<[u8; KEY_LEN]>, - salt: [u8; SALT_LEN], - context: &str, -) -> Protected<[u8; KEY_LEN]> { +pub fn derive_key(key: Protected, salt: Salt, context: &str) -> Protected { let mut input = key.expose().to_vec(); input.extend_from_slice(&salt); @@ -107,7 +107,7 @@ pub fn derive_key( /// This is used for converting a `Vec` to an array of bytes /// -/// It's main usage is for converting an encrypted master key from a `Vec` to `[u8; ENCRYPTED_KEY_LEN]` +/// It's main usage is for converting an encrypted master key from a `Vec` to `EncryptedKey` /// /// As the master key is encrypted at this point, it does not need to be `Protected<>` /// diff --git a/crates/crypto/src/protected.rs b/crates/crypto/src/protected.rs index 27a3629e0..747f1a1bc 100644 --- a/crates/crypto/src/protected.rs +++ b/crates/crypto/src/protected.rs @@ -32,7 +32,6 @@ use std::{fmt::Debug, mem::swap}; use zeroize::Zeroize; pub type ProtectedVec = Protected>; - #[derive(Clone)] pub struct Protected where From 238179879028dc6b21d1103fa688b6c704a826b9 Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Tue, 24 Jan 2023 23:28:16 +0000 Subject: [PATCH 15/20] fix stream reading due to `read()` Co-authored-by: Ericson Fogo Soares --- crates/crypto/src/crypto/stream.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/crates/crypto/src/crypto/stream.rs b/crates/crypto/src/crypto/stream.rs index 8106f183f..e6e431e90 100644 --- a/crates/crypto/src/crypto/stream.rs +++ b/crates/crypto/src/crypto/stream.rs @@ -118,8 +118,18 @@ impl StreamEncryption { W: AsyncWriteExt + Unpin + Send, { let mut read_buffer = vec![0u8; BLOCK_SIZE].into_boxed_slice(); + loop { - let read_count = reader.read(&mut read_buffer).await?; + let mut read_count = 0; + loop { + let i = reader.read(&mut read_buffer[read_count..]).await?; + read_count += i; + if i == 0 || read_count == BLOCK_SIZE { + // if we're EOF or the buffer is filled + break; + } + } + if read_count == BLOCK_SIZE { let payload = Payload { aad, @@ -239,7 +249,16 @@ impl StreamDecryption { let mut read_buffer = vec![0u8; BLOCK_SIZE + AEAD_TAG_SIZE].into_boxed_slice(); loop { - let read_count = reader.read(&mut read_buffer).await?; + let mut read_count = 0; + loop { + let i = reader.read(&mut read_buffer[read_count..]).await?; + read_count += i; + if i == 0 || read_count == (BLOCK_SIZE + AEAD_TAG_SIZE) { + // if we're EOF or the buffer is filled + break; + } + } + if read_count == (BLOCK_SIZE + AEAD_TAG_SIZE) { let payload = Payload { aad, From fa147f3e87e79b11dcd88a46b7cc1d4910d3c590 Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Wed, 25 Jan 2023 12:47:26 +0000 Subject: [PATCH 16/20] add correct features to `tokio` dep --- crates/crypto/Cargo.toml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/crypto/Cargo.toml b/crates/crypto/Cargo.toml index 1fcc0c984..d1f199d0d 100644 --- a/crates/crypto/Cargo.toml +++ b/crates/crypto/Cargo.toml @@ -42,7 +42,12 @@ specta = { workspace = true, optional = true } hex = "0.4.3" -tokio = { version = "1.21.2", features = ["io-util", "rt-multi-thread"] } +tokio = { version = "1.21.2", features = [ + "io-util", + "rt-multi-thread", + "fs", + "macros", +] } # [dev-dependencies] # criterion = "0.4.0" From 7623bf9a6c67bf72cdf78c50ff79f707f759d6ff Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Wed, 25 Jan 2023 14:03:00 +0000 Subject: [PATCH 17/20] change `fs` and `macros` `tokio` features to dev-deps --- crates/crypto/Cargo.toml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/crates/crypto/Cargo.toml b/crates/crypto/Cargo.toml index d1f199d0d..e3b790191 100644 --- a/crates/crypto/Cargo.toml +++ b/crates/crypto/Cargo.toml @@ -42,15 +42,13 @@ specta = { workspace = true, optional = true } hex = "0.4.3" +tokio = { version = "1.21.2", features = ["io-util", "rt-multi-thread"] } + +[dev-dependencies] tokio = { version = "1.21.2", features = [ - "io-util", - "rt-multi-thread", "fs", "macros", -] } - -# [dev-dependencies] -# criterion = "0.4.0" +] } # features needed for examples [features] rspc = ["dep:rspc", "dep:specta"] From a11d81549f3c48d999ee8b8a692494976fa34014 Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Wed, 25 Jan 2023 18:20:29 +0000 Subject: [PATCH 18/20] use a `tokio::sync::Mutex` just to be safe --- core/src/api/keys.rs | 14 +++--- core/src/library/library_manager.rs | 4 +- crates/crypto/src/keys/keymanager.rs | 69 +++++++++++++++++----------- 3 files changed, 51 insertions(+), 36 deletions(-) diff --git a/core/src/api/keys.rs b/core/src/api/keys.rs index 65738baa7..b8dacec05 100644 --- a/core/src/api/keys.rs +++ b/core/src/api/keys.rs @@ -56,7 +56,9 @@ pub(crate) fn mount() -> RouterBuilder { }) // do not unlock the key manager until this route returns true .library_query("hasMasterPassword", |t| { - t(|_, _: (), library| async move { Ok(library.key_manager.has_master_password()?) }) + t( + |_, _: (), library| async move { Ok(library.key_manager.has_master_password().await?) }, + ) }) // this is so we can show the key as mounted in the UI .library_query("listMounted", |t| { @@ -88,7 +90,7 @@ pub(crate) fn mount() -> RouterBuilder { .library_mutation("clearMasterPassword", |t| { t(|_, _: (), library| async move { // This technically clears the root key, but it means the same thing to the frontend - library.key_manager.clear_root_key()?; + library.key_manager.clear_root_key().await?; invalidate_query!(library, "keys.hasMasterPassword"); Ok(()) @@ -139,7 +141,7 @@ pub(crate) fn mount() -> RouterBuilder { .await?; } - library.key_manager.remove_key(key_uuid)?; + library.key_manager.remove_key(key_uuid).await?; // we also need to delete all in-memory decrypted data associated with this key invalidate_query!(library, "keys.list"); @@ -183,7 +185,7 @@ pub(crate) fn mount() -> RouterBuilder { }) .library_mutation("setDefault", |t| { t(|_, key_uuid: Uuid, library| async move { - library.key_manager.set_default(key_uuid)?; + library.key_manager.set_default(key_uuid).await?; library .db @@ -210,7 +212,7 @@ pub(crate) fn mount() -> RouterBuilder { }) }) .library_query("getDefault", |t| { - t(|_, _: (), library| async move { library.key_manager.get_default().ok() }) + t(|_, _: (), library| async move { library.key_manager.get_default().await.ok() }) }) .library_query("isKeyManagerUnlocking", |t| { t(|_, _: (), library| async move { library.key_manager.is_queued(Uuid::nil()) }) @@ -268,7 +270,7 @@ pub(crate) fn mount() -> RouterBuilder { let mut stored_keys = library.key_manager.dump_keystore(); // include the verification key at the time of backup - stored_keys.push(library.key_manager.get_verification_key()?); + stored_keys.push(library.key_manager.get_verification_key().await?); // exclude all memory-only keys stored_keys.retain(|k| !k.memory_only); diff --git a/core/src/library/library_manager.rs b/core/src/library/library_manager.rs index b5ac6d5e0..24d476b14 100644 --- a/core/src/library/library_manager.rs +++ b/core/src/library/library_manager.rs @@ -111,7 +111,7 @@ pub async fn seed_keymanager( .unwrap(); // insert all keys from the DB into the keymanager's keystore - km.populate_keystore(stored_keys)?; + km.populate_keystore(stored_keys).await?; // if any key had an associated default tag default.map(|k| km.set_default(k)); @@ -329,7 +329,7 @@ impl LibraryManager { .exec() .await?; - let key_manager = Arc::new(KeyManager::new(vec![])?); + let key_manager = Arc::new(KeyManager::new(vec![]).await?); seed_keymanager(&db, &key_manager).await?; let (sync_manager, _) = SyncManager::new(db.clone(), id); diff --git a/crates/crypto/src/keys/keymanager.rs b/crates/crypto/src/keys/keymanager.rs index 2396e3cd2..7a8ba123b 100644 --- a/crates/crypto/src/keys/keymanager.rs +++ b/crates/crypto/src/keys/keymanager.rs @@ -35,7 +35,7 @@ //! let keys = key_manager.enumerate_hashed_keys(); //! ``` -use std::sync::Mutex; +use tokio::sync::Mutex; use crate::crypto::stream::{StreamDecryption, StreamEncryption}; use crate::primitives::{ @@ -107,7 +107,7 @@ pub struct KeyManager { /// The `KeyManager` functions should be used for all key-related management. impl KeyManager { /// Initialize the Key Manager with `StoredKeys` retrieved from Prisma - pub fn new(stored_keys: Vec) -> Result { + pub async fn new(stored_keys: Vec) -> Result { let keymanager = Self { root_key: Mutex::new(None), verification_key: Mutex::new(None), @@ -117,7 +117,7 @@ impl KeyManager { mounting_queue: DashSet::new(), }; - keymanager.populate_keystore(stored_keys)?; + keymanager.populate_keystore(stored_keys).await?; Ok(keymanager) } @@ -196,14 +196,14 @@ impl KeyManager { /// It's suitable for when you created the key manager without populating it. /// /// This also detects the nil-UUID master passphrase verification key - pub fn populate_keystore(&self, stored_keys: Vec) -> Result<()> { + pub async fn populate_keystore(&self, stored_keys: Vec) -> Result<()> { for key in stored_keys { if self.keystore.contains_key(&key.uuid) { continue; } if key.uuid.is_nil() { - *self.verification_key.lock()? = Some(key); + *self.verification_key.lock().await = Some(key); } else { self.keystore.insert(key.uuid, key); } @@ -213,11 +213,11 @@ impl KeyManager { } /// This function removes a key from the keystore, the keymount and it's unset as the default. - pub fn remove_key(&self, uuid: Uuid) -> Result<()> { + pub async fn remove_key(&self, uuid: Uuid) -> Result<()> { if self.keystore.contains_key(&uuid) { // if key is default, clear it // do this manually to prevent deadlocks - let mut default = self.default.lock()?; + let mut default = self.default.lock().await; if *default == Some(uuid) { *default = None; } @@ -258,7 +258,7 @@ impl KeyManager { let master_key = generate_master_key(); let master_key_nonce = generate_nonce(algorithm); - let root_key = self.get_root_key()?; + let root_key = self.get_root_key().await?; let root_key_nonce = generate_nonce(algorithm); let salt = generate_salt(); @@ -299,7 +299,7 @@ impl KeyManager { automount: false, }; - *self.verification_key.lock()? = Some(verification_key.clone()); + *self.verification_key.lock().await = Some(verification_key.clone()); Ok(verification_key) } @@ -399,7 +399,7 @@ impl KeyManager { // encrypt the master key with the current root key let encrypted_master_key = to_array( StreamEncryption::encrypt_bytes( - derive_key(self.get_root_key()?, salt, ROOT_KEY_CONTEXT), + derive_key(self.get_root_key().await?, salt, ROOT_KEY_CONTEXT), &master_key_nonce, key.algorithm, master_key.expose(), @@ -444,13 +444,13 @@ impl KeyManager { { let uuid = Uuid::nil(); - if self.has_master_password()? { + if self.has_master_password().await? { return Err(Error::KeyAlreadyMounted); } else if self.is_queued(uuid) { return Err(Error::KeyAlreadyQueued); } - let verification_key = (*self.verification_key.lock()?) + let verification_key = (*self.verification_key.lock().await) .as_ref() .map_or(Err(Error::NoVerificationKey), |k| Ok(k.clone()))?; @@ -490,7 +490,7 @@ impl KeyManager { Error::IncorrectKeymanagerDetails })?; - *self.root_key.lock()? = Some(Protected::new( + *self.root_key.lock().await = Some(Protected::new( to_array( StreamDecryption::decrypt_bytes( Protected::new(to_array(master_key.into_inner())?), @@ -539,7 +539,11 @@ impl KeyManager { self.mounting_queue.insert(uuid); let master_key = StreamDecryption::decrypt_bytes( - derive_key(self.get_root_key()?, stored_key.salt, ROOT_KEY_CONTEXT), + derive_key( + self.get_root_key().await?, + stored_key.salt, + ROOT_KEY_CONTEXT, + ), &stored_key.master_key_nonce, stored_key.algorithm, &stored_key.master_key, @@ -601,7 +605,11 @@ impl KeyManager { match self.keystore.get(&uuid) { Some(stored_key) => { let master_key = StreamDecryption::decrypt_bytes( - derive_key(self.get_root_key()?, stored_key.salt, ROOT_KEY_CONTEXT), + derive_key( + self.get_root_key().await?, + stored_key.salt, + ROOT_KEY_CONTEXT, + ), &stored_key.master_key_nonce, stored_key.algorithm, &stored_key.master_key, @@ -664,7 +672,7 @@ impl KeyManager { // Encrypt the master key with a derived key (derived from the root key) let encrypted_master_key = to_array::( StreamEncryption::encrypt_bytes( - derive_key(self.get_root_key()?, salt, ROOT_KEY_CONTEXT), + derive_key(self.get_root_key().await?, salt, ROOT_KEY_CONTEXT), &master_key_nonce, algorithm, master_key.expose(), @@ -722,9 +730,9 @@ impl KeyManager { } /// This allows you to set the default key - pub fn set_default(&self, uuid: Uuid) -> Result<()> { + pub async fn set_default(&self, uuid: Uuid) -> Result<()> { if self.keystore.contains_key(&uuid) { - *self.default.lock()? = Some(uuid); + *self.default.lock().await = Some(uuid); Ok(()) } else { Err(Error::KeyNotFound) @@ -732,18 +740,23 @@ impl KeyManager { } /// This allows you to get the default key's ID - pub fn get_default(&self) -> Result { - self.default.lock()?.ok_or(Error::NoDefaultKeySet) + pub async fn get_default(&self) -> Result { + self.default.lock().await.ok_or(Error::NoDefaultKeySet) } /// This should ONLY be used internally. - fn get_root_key(&self) -> Result> { - self.root_key.lock()?.clone().ok_or(Error::NoMasterPassword) + async fn get_root_key(&self) -> Result> { + self.root_key + .lock() + .await + .clone() + .ok_or(Error::NoMasterPassword) } - pub fn get_verification_key(&self) -> Result { + pub async fn get_verification_key(&self) -> Result { self.verification_key - .lock()? + .lock() + .await .clone() .ok_or(Error::NoVerificationKey) } @@ -806,8 +819,8 @@ impl KeyManager { } /// This function is for removing a previously-added master password - pub fn clear_root_key(&self) -> Result<()> { - *self.root_key.lock()? = None; + pub async fn clear_root_key(&self) -> Result<()> { + *self.root_key.lock().await = None; Ok(()) } @@ -815,8 +828,8 @@ impl KeyManager { /// This function is used for seeing if the key manager has a master password. /// /// Technically this checks for the root key, but it makes no difference to the front end. - pub fn has_master_password(&self) -> Result { - Ok(self.root_key.lock()?.is_some()) + pub async fn has_master_password(&self) -> Result { + Ok(self.root_key.lock().await.is_some()) } /// This function is used for unmounting all keys at once. From 2abf75defde10053b2ea34e4c66cfc12bb866d09 Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Wed, 25 Jan 2023 22:03:10 +0000 Subject: [PATCH 19/20] update things to use workspace tokio --- apps/cli/Cargo.toml | 2 +- crates/crypto/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/cli/Cargo.toml b/apps/cli/Cargo.toml index 36c311322..43d8895f0 100644 --- a/apps/cli/Cargo.toml +++ b/apps/cli/Cargo.toml @@ -11,4 +11,4 @@ clap = { version = "4.0.32", features = ["derive"] } anyhow = "1.0.68" hex = "0.4.3" sd-crypto = { path = "../../crates/crypto" } -tokio = { version = "1.21.2", features = ["io-util", "rt-multi-thread"] } +tokio = { workspace = true, features = ["io-util", "rt-multi-thread"] } diff --git a/crates/crypto/Cargo.toml b/crates/crypto/Cargo.toml index 109d5690a..dea91ab93 100644 --- a/crates/crypto/Cargo.toml +++ b/crates/crypto/Cargo.toml @@ -45,7 +45,7 @@ hex = "0.4.3" tokio = { workspace = true, features = ["io-util", "rt-multi-thread"] } [dev-dependencies] -tokio = { version = "1.21.2", features = [ +tokio = { workspace = true, features = [ "fs", "macros", ] } # features needed for examples From 6874c23c017478419adc5cb5a2d5045ae38efae9 Mon Sep 17 00:00:00 2001 From: brxken128 <77554505+brxken128@users.noreply.github.com> Date: Thu, 26 Jan 2023 10:39:18 +0000 Subject: [PATCH 20/20] make requested changes --- crates/crypto/src/header/metadata.rs | 59 +++---- crates/crypto/src/header/preview_media.rs | 53 +++---- crates/crypto/src/keys/hashing.rs | 8 +- crates/crypto/src/keys/keymanager.rs | 183 +++++++++++----------- crates/crypto/src/lib.rs | 1 + 5 files changed, 149 insertions(+), 155 deletions(-) diff --git a/crates/crypto/src/header/metadata.rs b/crates/crypto/src/header/metadata.rs index f0138aba8..e6965c9ea 100644 --- a/crates/crypto/src/header/metadata.rs +++ b/crates/crypto/src/header/metadata.rs @@ -27,17 +27,12 @@ //! ) //! .unwrap(); //! ``` -#[cfg(feature = "serde")] -use crate::protected::ProtectedVec; - -#[cfg(feature = "serde")] -use crate::primitives::Key; #[cfg(feature = "serde")] use crate::{ crypto::stream::{StreamDecryption, StreamEncryption}, - primitives::generate_nonce, - Protected, + primitives::{generate_nonce, Key}, + Protected, ProtectedVec, }; use tokio::io::AsyncReadExt; @@ -120,20 +115,19 @@ impl FileHeader { { let master_key = self.decrypt_master_key_from_prehashed(hashed_keys).await?; - match self.metadata.as_ref() { - Some(metadata) => { - let metadata = StreamDecryption::decrypt_bytes( - master_key, - &metadata.metadata_nonce, - metadata.algorithm, - &metadata.metadata, - &[], - ) - .await?; + if let Some(metadata) = self.metadata.as_ref() { + let metadata = StreamDecryption::decrypt_bytes( + master_key, + &metadata.metadata_nonce, + metadata.algorithm, + &metadata.metadata, + &[], + ) + .await?; - serde_json::from_slice::(&metadata).map_err(|_| Error::Serialization) - } - None => Err(Error::NoMetadata), + serde_json::from_slice::(&metadata).map_err(|_| Error::Serialization) + } else { + Err(Error::NoMetadata) } } @@ -149,20 +143,19 @@ impl FileHeader { { let master_key = self.decrypt_master_key(password).await?; - match self.metadata.as_ref() { - Some(metadata) => { - let metadata = StreamDecryption::decrypt_bytes( - master_key, - &metadata.metadata_nonce, - metadata.algorithm, - &metadata.metadata, - &[], - ) - .await?; + if let Some(metadata) = self.metadata.as_ref() { + let metadata = StreamDecryption::decrypt_bytes( + master_key, + &metadata.metadata_nonce, + metadata.algorithm, + &metadata.metadata, + &[], + ) + .await?; - serde_json::from_slice::(&metadata).map_err(|_| Error::Serialization) - } - None => Err(Error::NoMetadata), + serde_json::from_slice::(&metadata).map_err(|_| Error::Serialization) + } else { + Err(Error::NoMetadata) } } } diff --git a/crates/crypto/src/header/preview_media.rs b/crates/crypto/src/header/preview_media.rs index 101d02669..4481a5009 100644 --- a/crates/crypto/src/header/preview_media.rs +++ b/crates/crypto/src/header/preview_media.rs @@ -25,8 +25,7 @@ use tokio::io::AsyncReadExt; use crate::{ crypto::stream::{Algorithm, StreamDecryption, StreamEncryption}, primitives::{generate_nonce, Key}, - protected::ProtectedVec, - Error, Protected, Result, + Error, Protected, ProtectedVec, Result, }; use super::file::FileHeader; @@ -92,20 +91,19 @@ impl FileHeader { ) -> Result> { let master_key = self.decrypt_master_key_from_prehashed(hashed_keys).await?; - match self.preview_media.as_ref() { - Some(pvm) => { - let pvm = StreamDecryption::decrypt_bytes( - master_key, - &pvm.media_nonce, - pvm.algorithm, - &pvm.media, - &[], - ) - .await?; + if let Some(pvm) = self.preview_media.as_ref() { + let pvm = StreamDecryption::decrypt_bytes( + master_key, + &pvm.media_nonce, + pvm.algorithm, + &pvm.media, + &[], + ) + .await?; - Ok(pvm) - } - None => Err(Error::NoPreviewMedia), + Ok(pvm) + } else { + Err(Error::NoPreviewMedia) } } @@ -120,20 +118,19 @@ impl FileHeader { ) -> Result> { let master_key = self.decrypt_master_key(password).await?; - match self.preview_media.as_ref() { - Some(pvm) => { - let pvm = StreamDecryption::decrypt_bytes( - master_key, - &pvm.media_nonce, - pvm.algorithm, - &pvm.media, - &[], - ) - .await?; + if let Some(pvm) = self.preview_media.as_ref() { + let pvm = StreamDecryption::decrypt_bytes( + master_key, + &pvm.media_nonce, + pvm.algorithm, + &pvm.media, + &[], + ) + .await?; - Ok(pvm) - } - None => Err(Error::NoPreviewMedia), + Ok(pvm) + } else { + Err(Error::NoPreviewMedia) } } } diff --git a/crates/crypto/src/keys/hashing.rs b/crates/crypto/src/keys/hashing.rs index 73756afbf..ccc810156 100644 --- a/crates/crypto/src/keys/hashing.rs +++ b/crates/crypto/src/keys/hashing.rs @@ -11,10 +11,10 @@ //! let hashed_password = hashing_algorithm.hash(password, salt).unwrap(); //! ``` -use crate::primitives::{Key, Salt, KEY_LEN}; -use crate::protected::ProtectedVec; -use crate::Protected; -use crate::{Error, Result}; +use crate::{ + primitives::{Key, Salt, KEY_LEN}, + Error, Protected, ProtectedVec, Result, +}; use argon2::Argon2; use balloon_hash::Balloon; diff --git a/crates/crypto/src/keys/keymanager.rs b/crates/crypto/src/keys/keymanager.rs index 7a8ba123b..c289e6fd1 100644 --- a/crates/crypto/src/keys/keymanager.rs +++ b/crates/crypto/src/keys/keymanager.rs @@ -37,14 +37,19 @@ use tokio::sync::Mutex; -use crate::crypto::stream::{StreamDecryption, StreamEncryption}; -use crate::primitives::{ - derive_key, generate_master_key, generate_nonce, generate_salt, to_array, EncryptedKey, Key, - OnboardingConfig, Salt, KEY_LEN, LATEST_STORED_KEY, MASTER_PASSWORD_CONTEXT, ROOT_KEY_CONTEXT, +// use crate::primitives::{ +// derive_key, generate_master_key, generate_nonce, generate_salt, to_array, EncryptedKey, Key, +// OnboardingConfig, Salt, KEY_LEN, LATEST_STORED_KEY, MASTER_PASSWORD_CONTEXT, ROOT_KEY_CONTEXT, +// }; +use crate::{ + crypto::stream::{Algorithm, StreamDecryption, StreamEncryption}, + primitives::{ + derive_key, generate_master_key, generate_nonce, generate_salt, to_array, EncryptedKey, + Key, OnboardingConfig, Salt, ENCRYPTED_KEY_LEN, KEY_LEN, LATEST_STORED_KEY, + MASTER_PASSWORD_CONTEXT, ROOT_KEY_CONTEXT, + }, + Error, Protected, ProtectedVec, Result, }; -use crate::protected::ProtectedVec; -use crate::{crypto::stream::Algorithm, primitives::ENCRYPTED_KEY_LEN, Protected}; -use crate::{Error, Result}; use dashmap::{DashMap, DashSet}; use uuid::Uuid; @@ -532,69 +537,68 @@ impl KeyManager { return Err(Error::KeyAlreadyQueued); } - match self.keystore.get(&uuid) { - Some(stored_key) => { - match stored_key.version { - StoredKeyVersion::V1 => { - self.mounting_queue.insert(uuid); + if let Some(stored_key) = self.keystore.get(&uuid) { + match stored_key.version { + StoredKeyVersion::V1 => { + self.mounting_queue.insert(uuid); - let master_key = StreamDecryption::decrypt_bytes( - derive_key( - self.get_root_key().await?, - stored_key.salt, - ROOT_KEY_CONTEXT, - ), - &stored_key.master_key_nonce, - stored_key.algorithm, - &stored_key.master_key, - &[], - ) - .await - .map_or_else( - |_| { - self.remove_from_queue(uuid).ok(); - Err(Error::IncorrectPassword) - }, - |v| Ok(Protected::new(to_array(v.into_inner())?)), - )?; - // Decrypt the StoredKey using the decrypted master key - let key = StreamDecryption::decrypt_bytes( - master_key, - &stored_key.key_nonce, - stored_key.algorithm, - &stored_key.key, - &[], - ) - .await + let master_key = StreamDecryption::decrypt_bytes( + derive_key( + self.get_root_key().await?, + stored_key.salt, + ROOT_KEY_CONTEXT, + ), + &stored_key.master_key_nonce, + stored_key.algorithm, + &stored_key.master_key, + &[], + ) + .await + .map_or_else( + |_| { + self.remove_from_queue(uuid).ok(); + Err(Error::IncorrectPassword) + }, + |v| Ok(Protected::new(to_array(v.into_inner())?)), + )?; + // Decrypt the StoredKey using the decrypted master key + let key = StreamDecryption::decrypt_bytes( + master_key, + &stored_key.key_nonce, + stored_key.algorithm, + &stored_key.key, + &[], + ) + .await + .map_err(|e| { + self.remove_from_queue(uuid).ok(); + e + })?; + + // Hash the key once with the parameters/algorithm the user selected during first mount + let hashed_key = stored_key + .hashing_algorithm + .hash(key, stored_key.content_salt, None) .map_err(|e| { self.remove_from_queue(uuid).ok(); e })?; - // Hash the key once with the parameters/algorithm the user selected during first mount - let hashed_key = stored_key - .hashing_algorithm - .hash(key, stored_key.content_salt, None) - .map_err(|e| { - self.remove_from_queue(uuid).ok(); - e - })?; + self.keymount.insert( + uuid, + MountedKey { + uuid: stored_key.uuid, + hashed_key, + }, + ); - self.keymount.insert( - uuid, - MountedKey { - uuid: stored_key.uuid, - hashed_key, - }, - ); - - self.remove_from_queue(uuid)?; - } + self.remove_from_queue(uuid)?; } - - Ok(()) } - None => Err(Error::KeyNotFound), + + Ok(()) + } else { + Err(Error::KeyNotFound) } } @@ -602,37 +606,36 @@ impl KeyManager { /// /// The master password/salt needs to be present, so we are able to decrypt the key itself from the stored key. pub async fn get_key(&self, uuid: Uuid) -> Result> { - match self.keystore.get(&uuid) { - Some(stored_key) => { - let master_key = StreamDecryption::decrypt_bytes( - derive_key( - self.get_root_key().await?, - stored_key.salt, - ROOT_KEY_CONTEXT, - ), - &stored_key.master_key_nonce, - stored_key.algorithm, - &stored_key.master_key, - &[], - ) - .await - .map_or(Err(Error::IncorrectPassword), |k| { - Ok(Protected::new(to_array(k.into_inner())?)) - })?; + if let Some(stored_key) = self.keystore.get(&uuid) { + let master_key = StreamDecryption::decrypt_bytes( + derive_key( + self.get_root_key().await?, + stored_key.salt, + ROOT_KEY_CONTEXT, + ), + &stored_key.master_key_nonce, + stored_key.algorithm, + &stored_key.master_key, + &[], + ) + .await + .map_or(Err(Error::IncorrectPassword), |k| { + Ok(Protected::new(to_array(k.into_inner())?)) + })?; - // Decrypt the StoredKey using the decrypted master key - let key = StreamDecryption::decrypt_bytes( - master_key, - &stored_key.key_nonce, - stored_key.algorithm, - &stored_key.key, - &[], - ) - .await?; + // Decrypt the StoredKey using the decrypted master key + let key = StreamDecryption::decrypt_bytes( + master_key, + &stored_key.key_nonce, + stored_key.algorithm, + &stored_key.key, + &[], + ) + .await?; - Ok(key) - } - None => Err(Error::KeyNotFound), + Ok(key) + } else { + Err(Error::KeyNotFound) } } diff --git a/crates/crypto/src/lib.rs b/crates/crypto/src/lib.rs index 87be04556..03b048a79 100644 --- a/crates/crypto/src/lib.rs +++ b/crates/crypto/src/lib.rs @@ -24,6 +24,7 @@ pub use aead::Payload; // Make this easier to use (e.g. `sd_crypto::Protected`) pub use protected::Protected; +pub use protected::ProtectedVec; // Re-export zeroize so it can be used elsewhere pub use zeroize::Zeroize;