diff --git a/crates/bitwarden/src/auth/register.rs b/crates/bitwarden/src/auth/register.rs index 1815b5457..52d3782c0 100644 --- a/crates/bitwarden/src/auth/register.rs +++ b/crates/bitwarden/src/auth/register.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use crate::{ client::auth_settings::Kdf, - crypto::{HashPurpose, MasterKey, RsaKeyPair}, + crypto::{keys::{HashPurpose, FromMasterPassword}, RsaKeyPair, SymmetricCryptoKey}, error::Result, util::default_pbkdf2_iterations, Client, @@ -64,7 +64,7 @@ pub(super) fn make_register_keys( password: String, kdf: Kdf, ) -> Result { - let master_key = MasterKey::derive(password.as_bytes(), email.as_bytes(), &kdf)?; + let master_key = SymmetricCryptoKey::::derive(password.as_bytes(), email.as_bytes(), &kdf)?; let master_password_hash = master_key.derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization)?; let (user_key, encrypted_user_key) = master_key.make_user_key()?; diff --git a/crates/bitwarden/src/client/access_token.rs b/crates/bitwarden/src/client/access_token.rs index b68d78572..dd4ae8333 100644 --- a/crates/bitwarden/src/client/access_token.rs +++ b/crates/bitwarden/src/client/access_token.rs @@ -9,10 +9,16 @@ use crate::{ util::BASE64_ENGINE, }; +/// Marker struct to indicate a key associated with an access token +pub struct AccessTokenEncryption {} +impl crate::crypto::keys::KeyPurpose for AccessTokenEncryption {} +// TODO: It seems odd that this needs to be a sharable key -- perhaps that concept is misnamed, or this key should be build differently? +impl crate::crypto::keys::ShareableKey for AccessTokenEncryption {} + pub struct AccessToken { pub service_account_id: Uuid, pub client_secret: String, - pub encryption_key: SymmetricCryptoKey, + pub(crate) encryption_key: SymmetricCryptoKey, } impl FromStr for AccessToken { diff --git a/crates/bitwarden/src/client/auth_settings.rs b/crates/bitwarden/src/client/auth_settings.rs index 8d9aac3ca..5a1ac5f0b 100644 --- a/crates/bitwarden/src/client/auth_settings.rs +++ b/crates/bitwarden/src/client/auth_settings.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "internal")] use crate::{ - crypto::{HashPurpose, MasterKey}, + crypto::keys::HashPurpose, error::Result, }; @@ -69,7 +69,9 @@ impl AuthSettings { #[cfg(feature = "internal")] pub fn derive_user_password_hash(&self, password: &str) -> Result { - let master_key = MasterKey::derive(password.as_bytes(), self.email.as_bytes(), &self.kdf)?; + use crate::crypto::{SymmetricCryptoKey, keys::FromMasterPassword}; + + let master_key = SymmetricCryptoKey::::derive(password.as_bytes(), self.email.as_bytes(), &self.kdf)?; master_key.derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization) } } diff --git a/crates/bitwarden/src/client/client.rs b/crates/bitwarden/src/client/client.rs index 357b13aa1..291e28a46 100644 --- a/crates/bitwarden/src/client/client.rs +++ b/crates/bitwarden/src/client/client.rs @@ -1,4 +1,4 @@ -use std::time::{Duration, Instant}; +use std::{time::{Duration, Instant}, str::FromStr}; use reqwest::header::{self}; use uuid::Uuid; @@ -28,10 +28,12 @@ use crate::{ client_settings::{ClientSettings, DeviceType}, encryption_settings::EncryptionSettings, }, - crypto::SymmetricCryptoKey, + crypto::{SymmetricCryptoKey, keys::UserEncryption}, error::{Error, Result}, }; +use super::access_token::AccessTokenEncryption; + #[derive(Debug)] pub(crate) struct ApiConfigurations { pub identity: bitwarden_api_identity::apis::configuration::Configuration, @@ -235,9 +237,12 @@ impl Client { pub(crate) fn initialize_crypto_single_key( &mut self, - key: SymmetricCryptoKey, + key: SymmetricCryptoKey, ) -> &EncryptionSettings { - self.encryption_settings = Some(EncryptionSettings::new_single_key(key)); + // TODO: this is a hack to convert access token keys to user keys. We need to rework how encryption settings work to fix. + let user_key = SymmetricCryptoKey::::from_str(&key.to_base64()).unwrap(); + + self.encryption_settings = Some(EncryptionSettings::new_single_key(user_key)); self.encryption_settings.as_ref().unwrap() } diff --git a/crates/bitwarden/src/client/encryption_settings.rs b/crates/bitwarden/src/client/encryption_settings.rs index 56f4b2628..2667b720f 100644 --- a/crates/bitwarden/src/client/encryption_settings.rs +++ b/crates/bitwarden/src/client/encryption_settings.rs @@ -9,14 +9,14 @@ use { }; use crate::{ - crypto::{encrypt_aes256_hmac, EncString, SymmetricCryptoKey}, + crypto::{encrypt_aes256_hmac, EncString, SymmetricCryptoKey, keys::{UserEncryption, OrganizationEncryption, KeyPurpose}}, error::{CryptoError, Result}, }; pub struct EncryptionSettings { - user_key: SymmetricCryptoKey, + user_key: SymmetricCryptoKey, private_key: Option, - org_keys: HashMap, + org_keys: HashMap>, } impl std::fmt::Debug for EncryptionSettings { @@ -25,6 +25,12 @@ impl std::fmt::Debug for EncryptionSettings { } } +// TODO: This is a hack to allow Encryptable and Decryptable to automatically determine which key to use. +// Removing this would require a refactor of the traits +// to require keys to use. However, it would allow for greater assurances of key usage from the compiler. struct UniversalKeyPurpose {} +struct UniversalKeyPurpose {} +impl KeyPurpose for UniversalKeyPurpose {} + impl EncryptionSettings { #[cfg(feature = "internal")] pub(crate) fn new( @@ -33,7 +39,7 @@ impl EncryptionSettings { user_key: EncString, private_key: EncString, ) -> Result { - use crate::crypto::MasterKey; + use crate::crypto::keys::MasterKey; // Derive master key from password let master_key = MasterKey::derive(password.as_bytes(), auth.email.as_bytes(), &auth.kdf)?; @@ -54,7 +60,7 @@ impl EncryptionSettings { }) } - pub(crate) fn new_single_key(key: SymmetricCryptoKey) -> Self { + pub(crate) fn new_single_key(key: SymmetricCryptoKey) -> Self { EncryptionSettings { user_key: key, private_key: None, @@ -90,7 +96,8 @@ impl EncryptionSettings { Ok(self) } - fn get_key(&self, org_id: &Option) -> Option<&SymmetricCryptoKey> { + // TODO: Remove UniversalKeyPurpose + fn get_key(&self, org_id: &Option) -> Option<&SymmetricCryptoKey> { // If we don't have a private key set (to decode multiple org keys), we just use the main user key if self.private_key.is_none() { return Some(&self.user_key); diff --git a/crates/bitwarden/src/crypto/enc_string.rs b/crates/bitwarden/src/crypto/enc_string.rs index b701aaf8f..1487a5514 100644 --- a/crates/bitwarden/src/crypto/enc_string.rs +++ b/crates/bitwarden/src/crypto/enc_string.rs @@ -11,6 +11,8 @@ use crate::{ util::BASE64_ENGINE, }; +use super::keys::KeyPurpose; + #[derive(Clone)] #[allow(unused, non_camel_case_types)] pub enum EncString { @@ -305,7 +307,7 @@ impl EncString { } } - pub fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result> { + pub(crate) fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result> { match self { EncString::AesCbc256_HmacSha256_B64 { iv, mac, data } => { let mac_key = key.mac_key.ok_or(CryptoError::InvalidMac)?; diff --git a/crates/bitwarden/src/crypto/master_key.rs b/crates/bitwarden/src/crypto/keys/master_key.rs similarity index 89% rename from crates/bitwarden/src/crypto/master_key.rs rename to crates/bitwarden/src/crypto/keys/master_key.rs index dd4dd5019..3db49ef72 100644 --- a/crates/bitwarden/src/crypto/master_key.rs +++ b/crates/bitwarden/src/crypto/keys/master_key.rs @@ -4,9 +4,9 @@ use rand::Rng; use crate::util::BASE64_ENGINE; -use super::{ - encrypt_aes256, hkdf_expand, EncString, PbkdfSha256Hmac, SymmetricCryptoKey, UserKey, - PBKDF_SHA256_HMAC_OUT_SIZE, +use crate::crypto::{ + encrypt_aes256, hkdf_expand, EncString, PbkdfSha256Hmac, SymmetricCryptoKey, + PBKDF_SHA256_HMAC_OUT_SIZE, keys::UserEncryption, }; use { crate::{client::auth_settings::Kdf, error::Result}, @@ -20,11 +20,10 @@ pub(crate) enum HashPurpose { // LocalAuthorization = 2, } -/// A Master Key. -pub(crate) struct MasterKey(SymmetricCryptoKey); +pub struct FromMasterPassword {} +impl super::KeyPurpose for FromMasterPassword {} -impl MasterKey { - /// Derives a users master key from their password, email and KDF. +impl SymmetricCryptoKey { pub fn derive(password: &[u8], email: &[u8], kdf: &Kdf) -> Result { derive_key(password, email, kdf).map(Self) } @@ -45,17 +44,17 @@ impl MasterKey { Ok(BASE64_ENGINE.encode(hash)) } - pub(crate) fn make_user_key(&self) -> Result<(UserKey, EncString)> { + pub(crate) fn make_user_key(&self) -> Result<(SymmetricCryptoKey, EncString)> { let mut user_key = [0u8; 64]; rand::thread_rng().fill(&mut user_key); let protected = encrypt_aes256(&user_key, self.0.key)?; let u: &[u8] = &user_key; - Ok((UserKey::new(SymmetricCryptoKey::try_from(u)?), protected)) + Ok((SymmetricCryptoKey::try_from(u)?, protected)) } - pub(crate) fn decrypt_user_key(&self, user_key: EncString) -> Result { + pub(crate) fn decrypt_user_key(&self, user_key: EncString) -> Result> { let stretched_key = stretch_master_key(self)?; let dec = user_key.decrypt_with_key(&stretched_key)?; @@ -64,7 +63,7 @@ impl MasterKey { } /// Derive a generic key from a secret and salt using the provided KDF. -fn derive_key(secret: &[u8], salt: &[u8], kdf: &Kdf) -> Result { +fn derive_key(secret: &[u8], salt: &[u8], kdf: &Kdf) -> Result { let hash = match kdf { Kdf::PBKDF2 { iterations } => pbkdf2::pbkdf2_array::< PbkdfSha256Hmac, @@ -103,19 +102,16 @@ fn derive_key(secret: &[u8], salt: &[u8], kdf: &Kdf) -> Result Result { +fn stretch_master_key(master_key: &MasterKey) -> Result> { let key: GenericArray = hkdf_expand(&master_key.0.key, Some("enc"))?; let mac_key: GenericArray = hkdf_expand(&master_key.0.key, Some("mac"))?; - Ok(SymmetricCryptoKey { - key, - mac_key: Some(mac_key), - }) + Ok(SymmetricCryptoKey::new(key, Some(mac_key))) } #[cfg(test)] mod tests { - use crate::crypto::SymmetricCryptoKey; + use crate::crypto::{SymmetricCryptoKey, keys::UserEncryption}; use super::{stretch_master_key, HashPurpose, MasterKey}; use {crate::client::auth_settings::Kdf, std::num::NonZeroU32}; @@ -166,14 +162,13 @@ mod tests { #[test] fn test_stretch_master_key() { - let master_key = MasterKey(SymmetricCryptoKey { - key: [ + let master_key = MasterKey(SymmetricCryptoKey::::new([ 31, 79, 104, 226, 150, 71, 177, 90, 194, 80, 172, 209, 17, 129, 132, 81, 138, 167, 69, 167, 254, 149, 2, 27, 39, 197, 64, 42, 22, 195, 86, 75, ] .into(), - mac_key: None, - }); + None, + )); let stretched = stretch_master_key(&master_key).unwrap(); diff --git a/crates/bitwarden/src/crypto/keys/mod.rs b/crates/bitwarden/src/crypto/keys/mod.rs new file mode 100644 index 000000000..5d1baddcb --- /dev/null +++ b/crates/bitwarden/src/crypto/keys/mod.rs @@ -0,0 +1,15 @@ +mod master_key; +pub use master_key::*; +mod shareable_key; +pub use shareable_key::*; +mod symmetric_crypto_key; +pub use symmetric_crypto_key::*; + +#[cfg(feature = "internal")] +mod user_key; +#[cfg(feature = "internal")] +pub use user_key::*; +#[cfg(feature = "internal")] +mod organization_key; +#[cfg(feature = "internal")] +pub use organization_key::*; diff --git a/crates/bitwarden/src/crypto/keys/organization_key.rs b/crates/bitwarden/src/crypto/keys/organization_key.rs new file mode 100644 index 000000000..c8b65000c --- /dev/null +++ b/crates/bitwarden/src/crypto/keys/organization_key.rs @@ -0,0 +1,9 @@ +use crate::crypto::SymmetricCryptoKey; + +use super::{AsymmetricKeyPairGeneration, KeyPurpose}; + +pub struct OrganizationEncryption {} +impl KeyPurpose for OrganizationEncryption {} +impl AsymmetricKeyPairGeneration for OrganizationEncryption {} + +pub type OrganizationKey = SymmetricCryptoKey; diff --git a/crates/bitwarden/src/crypto/shareable_key.rs b/crates/bitwarden/src/crypto/keys/shareable_key.rs similarity index 73% rename from crates/bitwarden/src/crypto/shareable_key.rs rename to crates/bitwarden/src/crypto/keys/shareable_key.rs index 4127b0712..ceaa5f429 100644 --- a/crates/bitwarden/src/crypto/shareable_key.rs +++ b/crates/bitwarden/src/crypto/keys/shareable_key.rs @@ -3,15 +3,28 @@ use hmac::{Hmac, Mac}; use crate::crypto::{hkdf_expand, SymmetricCryptoKey}; +use super::KeyPurpose; + +/// Marker trait to annotate that the key is intended shareable beyond the current account +pub trait ShareableKey : KeyPurpose {} + +impl SymmetricCryptoKey { + pub fn generate(name: &str) -> Self { + use rand::Rng; + let secret: [u8; 16] = rand::thread_rng().gen(); + derive_shareable_key::(secret, name, None) + } +} + /// Derive a shareable key using hkdf from secret and name. /// /// A specialized variant of this function was called `CryptoService.makeSendKey` in the Bitwarden /// `clients` repository. -pub(crate) fn derive_shareable_key( +pub(crate) fn derive_shareable_key( secret: [u8; 16], name: &str, info: Option<&str>, -) -> SymmetricCryptoKey { +) -> SymmetricCryptoKey { // Because all inputs are fixed size, we can unwrap all errors here without issue // TODO: Are these the final `key` and `info` parameters or should we change them? I followed the pattern used for sends diff --git a/crates/bitwarden/src/crypto/symmetric_crypto_key.rs b/crates/bitwarden/src/crypto/keys/symmetric_crypto_key.rs similarity index 55% rename from crates/bitwarden/src/crypto/symmetric_crypto_key.rs rename to crates/bitwarden/src/crypto/keys/symmetric_crypto_key.rs index 7b2086f75..187dc2680 100644 --- a/crates/bitwarden/src/crypto/symmetric_crypto_key.rs +++ b/crates/bitwarden/src/crypto/keys/symmetric_crypto_key.rs @@ -1,28 +1,36 @@ -use std::str::FromStr; +use std::{str::FromStr, marker::PhantomData}; use aes::cipher::{generic_array::GenericArray, typenum::U32}; use base64::Engine; use crate::{ - crypto::derive_shareable_key, - error::{CryptoError, Error}, - util::BASE64_ENGINE, + error::{CryptoError, Error, Result}, + util::BASE64_ENGINE, crypto::RsaKeyPair, }; +/// Marker trait to annotate the purpose of a key +pub trait KeyPurpose {} + +/// Marker trait to annotate that the key is capable of generating an asymmetric key pair +pub trait AsymmetricKeyPairGeneration : KeyPurpose {} + /// A symmetric encryption key. Used to encrypt and decrypt [`EncString`](crate::crypto::EncString) -pub struct SymmetricCryptoKey { +pub(crate) struct SymmetricCryptoKey { pub key: GenericArray, pub mac_key: Option>, + _type: PhantomData, } -impl SymmetricCryptoKey { +impl SymmetricCryptoKey { const KEY_LEN: usize = 32; const MAC_LEN: usize = 32; - pub fn generate(name: &str) -> Self { - use rand::Rng; - let secret: [u8; 16] = rand::thread_rng().gen(); - derive_shareable_key(secret, name, None) + pub fn make_key(key: GenericArray, mac_key: Option>) -> Self { + SymmetricCryptoKey { + key, + mac_key, + _type: PhantomData, + } } pub fn to_base64(&self) -> String { @@ -37,7 +45,13 @@ impl SymmetricCryptoKey { } } -impl FromStr for SymmetricCryptoKey { +impl SymmetricCryptoKey { + pub fn make_key_pair(&self) -> Result { + crate::crypto::rsa::make_key_pair(&self) + } +} + +impl FromStr for SymmetricCryptoKey where TKeyPurpose : KeyPurpose { type Err = Error; fn from_str(s: &str) -> Result { @@ -48,7 +62,7 @@ impl FromStr for SymmetricCryptoKey { } } -impl TryFrom<&[u8]> for SymmetricCryptoKey { +impl TryFrom<&[u8]> for SymmetricCryptoKey where TKeyPurpose : KeyPurpose { type Error = Error; fn try_from(value: &[u8]) -> Result { @@ -56,11 +70,13 @@ impl TryFrom<&[u8]> for SymmetricCryptoKey { Ok(SymmetricCryptoKey { key: GenericArray::clone_from_slice(&value[..Self::KEY_LEN]), mac_key: Some(GenericArray::clone_from_slice(&value[Self::KEY_LEN..])), + _type: PhantomData, }) } else if value.len() == Self::KEY_LEN { Ok(SymmetricCryptoKey { key: GenericArray::clone_from_slice(value), mac_key: None, + _type: PhantomData, }) } else { Err(CryptoError::InvalidKeyLen.into()) @@ -69,7 +85,7 @@ impl TryFrom<&[u8]> for SymmetricCryptoKey { } // We manually implement these to make sure we don't print any sensitive data -impl std::fmt::Debug for SymmetricCryptoKey { +impl std::fmt::Debug for SymmetricCryptoKey { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Key").finish() } @@ -79,17 +95,20 @@ impl std::fmt::Debug for SymmetricCryptoKey { mod tests { use std::str::FromStr; - use super::SymmetricCryptoKey; + use super::*; + + struct SymmetricCryptoKeyTests {} + impl KeyPurpose for SymmetricCryptoKeyTests {} #[test] fn test_symmetric_crypto_key() { - let key = SymmetricCryptoKey::generate("test"); - let key2 = SymmetricCryptoKey::from_str(&key.to_base64()).unwrap(); + let key = SymmetricCryptoKey::::generate("test"); + let key2 = SymmetricCryptoKey::::from_str(&key.to_base64()).unwrap(); assert_eq!(key.key, key2.key); assert_eq!(key.mac_key, key2.mac_key); let key = "UY4B5N4DA4UisCNClgZtRr6VLy9ZF5BXXC7cDZRqourKi4ghEMgISbCsubvgCkHf5DZctQjVot11/vVvN9NNHQ=="; - let key2 = SymmetricCryptoKey::from_str(key).unwrap(); + let key2 = SymmetricCryptoKey::::from_str(key).unwrap(); assert_eq!(key, key2.to_base64()); } } diff --git a/crates/bitwarden/src/crypto/keys/user_key.rs b/crates/bitwarden/src/crypto/keys/user_key.rs new file mode 100644 index 000000000..289816a1c --- /dev/null +++ b/crates/bitwarden/src/crypto/keys/user_key.rs @@ -0,0 +1,9 @@ +use crate::crypto::SymmetricCryptoKey; + +use super::{AsymmetricKeyPairGeneration, KeyPurpose}; + +pub struct UserEncryption {} +impl KeyPurpose for UserEncryption {} +impl AsymmetricKeyPairGeneration for UserEncryption {} + +pub type UserKey = SymmetricCryptoKey; diff --git a/crates/bitwarden/src/crypto/mod.rs b/crates/bitwarden/src/crypto/mod.rs index b35157981..73f304406 100644 --- a/crates/bitwarden/src/crypto/mod.rs +++ b/crates/bitwarden/src/crypto/mod.rs @@ -32,19 +32,9 @@ mod encryptable; pub use encryptable::{Decryptable, Encryptable}; mod aes_ops; pub use aes_ops::{decrypt_aes256, decrypt_aes256_hmac, encrypt_aes256, encrypt_aes256_hmac}; -mod symmetric_crypto_key; -pub use symmetric_crypto_key::SymmetricCryptoKey; -mod shareable_key; -pub(crate) use shareable_key::derive_shareable_key; +pub mod keys; +pub(crate) use keys::{derive_shareable_key, SymmetricCryptoKey}; -#[cfg(feature = "internal")] -mod master_key; -#[cfg(feature = "internal")] -pub(crate) use master_key::{HashPurpose, MasterKey}; -#[cfg(feature = "internal")] -mod user_key; -#[cfg(feature = "internal")] -pub(crate) use user_key::UserKey; #[cfg(feature = "internal")] mod rsa; #[cfg(feature = "internal")] diff --git a/crates/bitwarden/src/crypto/rsa.rs b/crates/bitwarden/src/crypto/rsa.rs index 0d2d135b9..4abfd03ac 100644 --- a/crates/bitwarden/src/crypto/rsa.rs +++ b/crates/bitwarden/src/crypto/rsa.rs @@ -10,6 +10,8 @@ use crate::{ util::BASE64_ENGINE, }; +use super::keys::AsymmetricKeyPairGeneration; + #[cfg_attr(feature = "mobile", derive(uniffi::Record))] pub struct RsaKeyPair { /// Base64 encoded DER representation of the public key @@ -18,7 +20,7 @@ pub struct RsaKeyPair { pub private: EncString, } -pub(super) fn make_key_pair(key: &SymmetricCryptoKey) -> Result { +pub(super) fn make_key_pair(key: &SymmetricCryptoKey) -> Result { let mut rng = rand::thread_rng(); let bits = 2048; let priv_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key"); diff --git a/crates/bitwarden/src/crypto/user_key.rs b/crates/bitwarden/src/crypto/user_key.rs deleted file mode 100644 index 0fe560665..000000000 --- a/crates/bitwarden/src/crypto/user_key.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::{ - crypto::{ - rsa::{make_key_pair, RsaKeyPair}, - SymmetricCryptoKey, - }, - error::Result, -}; - -pub(crate) struct UserKey(SymmetricCryptoKey); - -impl UserKey { - pub(crate) fn new(key: SymmetricCryptoKey) -> Self { - Self(key) - } - - pub(crate) fn make_key_pair(&self) -> Result { - make_key_pair(&self.0) - } -} diff --git a/crates/bitwarden/src/vault/send.rs b/crates/bitwarden/src/vault/send.rs index 7c9360e0f..8a233c62d 100644 --- a/crates/bitwarden/src/vault/send.rs +++ b/crates/bitwarden/src/vault/send.rs @@ -6,7 +6,7 @@ use uuid::Uuid; use crate::{ client::encryption_settings::EncryptionSettings, - crypto::{derive_shareable_key, Decryptable, EncString, Encryptable, SymmetricCryptoKey}, + crypto::{derive_shareable_key, Decryptable, EncString, Encryptable, SymmetricCryptoKey, keys::KeyPurpose, keys::ShareableKey}, error::Result, }; @@ -125,12 +125,17 @@ pub struct SendListView { pub expiration_date: Option>, } +/// Indicate the purpose of a SymmetricCryptoKey is for Sends. +pub struct SendEncryption {} +impl KeyPurpose for SendEncryption {} +impl ShareableKey for SendEncryption {} + impl Send { fn get_key( key: &EncString, enc: &EncryptionSettings, org_id: &Option, - ) -> Result { + ) -> Result> { let key: Vec = enc.decrypt_bytes(key, org_id)?; let key = derive_shareable_key(key.try_into().unwrap(), "send", Some("send")); Ok(key)