diff --git a/tuta-sdk/rust/sdk/src/key_loader_facade.rs b/tuta-sdk/rust/sdk/src/key_loader_facade.rs index cc8a4602fa36..c6cd557828ef 100644 --- a/tuta-sdk/rust/sdk/src/key_loader_facade.rs +++ b/tuta-sdk/rust/sdk/src/key_loader_facade.rs @@ -1,13 +1,14 @@ use crate::crypto::key::{AsymmetricKeyPair, GenericAesKey, KeyLoadError}; use crate::crypto::key_encryption::decrypt_key_pair; -use crate::entities::sys::{Group, GroupKey}; +use crate::custom_id::CustomId; +use crate::entities::sys::{Group, GroupKey, KeyPair}; use crate::generated_id::GeneratedId; #[mockall_double::double] use crate::typed_entity_client::TypedEntityClient; #[mockall_double::double] use crate::user_facade::UserFacade; use crate::util::Versioned; -use crate::ListLoadDirection; +use crate::{IdTuple, ListLoadDirection}; use base64::Engine; use futures::future::BoxFuture; use std::cmp::Ordering; @@ -209,12 +210,6 @@ impl KeyLoaderFacade { ) -> Result { let group: Group = self.entity_client.load(key_pair_group_id).await?; let current_group_key = self.get_current_sym_group_key(&group._id).await?; - - // if (requested_version > current_group_key.version) { - // group = (await (await this.cacheManagementFacade()).refreshKeyCache(keyPairGroupId)).group - // currentGroupKey = await this.getCurrentSymGroupKey(keyPairGroupId) - // } - if current_group_key.version == requested_version { return self.get_and_decrypt_key_pair(&group, ¤t_group_key.object); } @@ -234,6 +229,84 @@ impl KeyLoaderFacade { } } + // async loadKeypair(key_pair_group_id: Id, requestedVersion: number): Promise { + // let group = await this.entityClient.load(GroupTypeRef, key_pair_group_id) + // let current_group_key = await this.getCurrentSymGroupKey(key_pair_group_id) + // + // if (requestedVersion > current_group_key.version) { + // group = (await (await this.cacheManagementFacade()).refreshKeyCache(key_pair_group_id)).group + // current_group_key = await this.getCurrentSymGroupKey(key_pair_group_id) + // } + // return await this.loadKeyPairImpl(group, requestedVersion, current_group_key) + // } + // + // async loadCurrentKeyPair(groupId: Id): Promise> { + // let group = await this.entityClient.load(GroupTypeRef, groupId) + // + // let current_group_key = await this.getCurrentSymGroupKey(groupId) + // if (Number(group.groupKeyVersion) !== current_group_key.version) { + // // There is a race condition after rotating the group key were the group entity in the cache is not in sync with current key version in the key cache. + // // group.groupKeyVersion might be newer than current_group_key.version. + // // We reload group and user and refresh entity and key cache to synchronize both caches. + // group = (await (await this.cacheManagementFacade()).refreshKeyCache(groupId)).group + // current_group_key = await this.getCurrentSymGroupKey(groupId) + // if (Number(group.groupKeyVersion) !== current_group_key.version) { + // // we still do not have the proper state to get the current key pair + // throw new Error(`inconsistent key version state in cache and key cache for group ${groupId}`) + // } + // } + // return { object: this.validateAndDecryptKeyPair(group.currentKeys, groupId, current_group_key.object), version: Number(group.groupKeyVersion) } + // } + + async fn load_key_pair_impl( + &self, + group: Group, + requested_version: i64, + current_group_key: VersionedAesKey, + ) -> Result { + let key_pair_group_id = group._id; + let key_pair: Option; + let sym_group_key: GenericAesKey; + if requested_version > current_group_key.version { + return Err(KeyLoadError{reason: format!("Not possible to get newer key version than is cached for group {key_pair_group_id}")}); + } else if requested_version == current_group_key.version { + sym_group_key = current_group_key.object; + if group.groupKeyVersion == current_group_key.version { + key_pair = group.currentKeys + } else { + let former_keys_list = group.formerGroupKeys.unwrap().list; + // we load by the version and thus can be sure that we are able to decrypt this key + let former_group_key: GroupKey = self + .entity_client + .load(IdTuple::new( + former_keys_list, + CustomId::from_custom_string(¤t_group_key.version.to_string()), + )) + .await?; + key_pair = former_group_key.keyPair + } + // }else { + // // load a former key pair: groupKeyVersion < groupKey.version + // let { symmetricGroupKey, groupKeyInstance } = self.find_former_group_key(group, current_group_key, requestedVersion).await? + // key_pair = groupKeyInstance.keyPair + // sym_group_key = symmetricGroupKey + } + Ok(self.validate_and_decrypt_key_pair(key_pair, key_pair_group_id, sym_group_key)?) + } + + fn validate_and_decrypt_key_pair( + key_pair: Option, + group_id: &GeneratedId, + group_key: GenericAesKey, + ) -> Result { + match key_pair { + None => Err(KeyLoadError { + reason: format!("no key pair on group {group_id}"), + }), + Some(kp) => Ok(decrypt_key_pair(&group_key, &kp)?), + } + } + fn get_and_decrypt_key_pair( &self, group: &Group, diff --git a/tuta-sdk/rust/sdk/src/lib.rs b/tuta-sdk/rust/sdk/src/lib.rs index e8cc08d291a7..a42aa0372dcf 100644 --- a/tuta-sdk/rust/sdk/src/lib.rs +++ b/tuta-sdk/rust/sdk/src/lib.rs @@ -15,6 +15,7 @@ use crate::crypto::crypto_facade::CryptoFacade; use crate::crypto::randomizer_facade::RandomizerFacade; #[mockall_double::double] use crate::crypto_entity_client::CryptoEntityClient; +use crate::custom_id::CustomId; use crate::element_value::ElementValue; use crate::entities::entity_facade::EntityFacade; use crate::entities::tutanota::Mail; @@ -224,16 +225,23 @@ pub enum ListLoadDirection { DESC, } +#[derive(Clone, Debug, PartialEq, PartialOrd, Serialize, Deserialize)] +#[repr(transparent)] +pub enum ElementId { + Generated(GeneratedId), + Custom(CustomId), +} + /// A set of keys used to identify an element within a List Element Type #[derive(uniffi::Record, Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct IdTuple { pub list_id: GeneratedId, - pub element_id: GeneratedId, + pub element_id: ElementId, } impl IdTuple { #[must_use] - pub fn new(list_id: GeneratedId, element_id: GeneratedId) -> Self { + pub fn new(list_id: GeneratedId, element_id: ElementId) -> Self { Self { list_id, element_id,