Skip to content

Commit

Permalink
authenticate TutaCrypt emails in crypto_entity_client to avoid parsin…
Browse files Browse the repository at this point in the history
…g in crypto_facade when resolving the session_key.

#tutadb1965
  • Loading branch information
vaf-hub committed Feb 20, 2025
1 parent 9a57082 commit 5682af2
Show file tree
Hide file tree
Showing 12 changed files with 203 additions and 33 deletions.
10 changes: 5 additions & 5 deletions src/common/api/worker/crypto/CryptoFacade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ export class CryptoFacade {
public async resolveWithBucketKey(bucketKey: BucketKey, instance: Record<string, any>, typeModel: TypeModel): Promise<ResolvedSessionKeys> {
const instanceElementId = this.getElementIdFromInstance(instance)
let decryptedBucketKey: AesKey
let unencryptedSenderAuthStatus: EncryptionAuthStatus | null = null
let encryptionAuthStatus: EncryptionAuthStatus | null = null
let pqMessageSenderKey: EccPublicKey | null = null
if (bucketKey.keyGroup && bucketKey.pubEncBucketKey) {
// bucket key is encrypted with public key for internal recipient
Expand All @@ -284,7 +284,7 @@ export class CryptoFacade {
}

decryptedBucketKey = await this.resolveWithGroupReference(keyGroup, groupKeyVersion, bucketKey.groupEncBucketKey)
unencryptedSenderAuthStatus = EncryptionAuthStatus.AES_NO_AUTHENTICATION
encryptionAuthStatus = EncryptionAuthStatus.AES_NO_AUTHENTICATION
} else {
throw new SessionKeyNotFoundError(`encrypted bucket key not set on instance ${typeModel.name}`)
}
Expand All @@ -294,7 +294,7 @@ export class CryptoFacade {
instanceElementId,
instance,
typeModel,
unencryptedSenderAuthStatus,
encryptionAuthStatus,
pqMessageSenderKey,
)

Expand Down Expand Up @@ -508,9 +508,9 @@ export class CryptoFacade {
pqMessageSenderKey: Uint8Array | null,
pqMessageSenderKeyVersion: KeyVersion | null,
instance: Record<string, any>,
resolvedSessionKeyForInstance: number[],
resolvedSessionKeyForInstance: AesKey,
instanceSessionKeyWithOwnerEncSessionKey: InstanceSessionKey,
decryptedSessionKey: number[],
decryptedSessionKey: AesKey,
keyGroup: Id | null,
) {
// we only authenticate mail instances
Expand Down
1 change: 1 addition & 0 deletions tuta-sdk/rust/sdk/src/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub use aes::PlaintextAndIv;
#[allow(unused_imports)]
pub use aes::{Aes256Key, AES_256_KEY_SIZE, IV_BYTE_SIZE};
pub use argon2_id::generate_key_from_passphrase;
pub use ecc::EccPublicKey;
pub use hkdf::hkdf;
pub use sha::sha256;
#[allow(unused_imports)]
Expand Down
51 changes: 29 additions & 22 deletions tuta-sdk/rust/sdk/src/crypto/asymmetric_crypto_facade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,18 @@ impl AsymmetricCryptoFacade {
/// @param identifier the identifier to load the public key to verify that it matches the one used in the protocol run.
/// @param sender_identity_pub_key the sender_identity_pub_key that was used to encrypt/authenticate the data.
/// @param sender_key_version the version of the sender_identity_pub_key.
pub async fn authenticate_sender(
pub async fn authenticate_sender<'a>(
&self,
identifier: PublicKeyIdentifier,
sender_identity_pub_key: &[u8],
sender_key_version: u64,
sender_identity_pub_key: Versioned<&'a EccPublicKey>,
) -> Result<EncryptionAuthStatus, AsymmetricCryptoError> {
let pub_keys = self
.public_key_provider
.load_versioned_pub_key(&identifier, sender_key_version)
.load_versioned_pub_key(&identifier, sender_identity_pub_key.version)
.await?;

if Option::is_some(&pub_keys.pub_ecc_key)
&& pub_keys.pub_ecc_key.unwrap() == sender_identity_pub_key
&& pub_keys.pub_ecc_key.unwrap().as_slice() == sender_identity_pub_key.object.as_bytes()
{
Ok(EncryptionAuthStatus::TutacryptAuthenticationSucceeded)
} else {
Expand Down Expand Up @@ -132,13 +131,14 @@ impl AsymmetricCryptoFacade {
let sender_identity_pub_key_from_pq_message = decapsulated_aes_key
.sender_identity_pub_key
.as_ref()
.unwrap()
.as_bytes();
.unwrap();
let encryption_auth_status = self
.authenticate_sender(
sender_identifier,
sender_identity_pub_key_from_pq_message,
convert_version_to_u64(pub_enc_key_data.senderKeyVersion.unwrap()),
Versioned {
object: sender_identity_pub_key_from_pq_message,
version: convert_version_to_u64(pub_enc_key_data.senderKeyVersion.unwrap()),
},
)
.await?;
if encryption_auth_status != EncryptionAuthStatus::TutacryptAuthenticationSucceeded {
Expand Down Expand Up @@ -445,8 +445,10 @@ mod tests {
make_asymmetric_crypto_facade, setup_authentication_test,
};
use crate::crypto::public_key_provider::PublicKeys;
use crate::crypto::EccPublicKey;
use crate::services::generated::sys::PublicKeyService;
use crate::tutanota_constants::EncryptionAuthStatus;
use crate::util::Versioned;
use mockall::predicate::eq;

#[tokio::test]
Expand All @@ -457,9 +459,8 @@ mod tests {
mut service_executor,
mut public_key_provider,
) = setup_authentication_test();
let sender_identity_pub_key = vec![9, 8, 7];

let pub_key = sender_identity_pub_key.clone();
let pub_key = vec![2; 32];
let sender_identity_pub_key = EccPublicKey::from_bytes(pub_key.as_slice()).unwrap();

service_executor.expect_get::<PublicKeyService>().never();
service_executor.expect_put::<PublicKeyService>().never();
Expand All @@ -480,8 +481,10 @@ mod tests {
let result = asymmetric_crypto_facade
.authenticate_sender(
public_key_identifier,
&sender_identity_pub_key,
sender_key_version,
Versioned {
object: &sender_identity_pub_key,
version: sender_key_version,
},
)
.await
.unwrap();
Expand All @@ -500,9 +503,8 @@ mod tests {
mut service_executor,
mut public_key_provider,
) = setup_authentication_test();
let sender_identity_pub_key = vec![9, 8, 7];

let pub_key = sender_identity_pub_key.clone();
let pub_key = vec![2; 32];
let sender_identity_pub_key = EccPublicKey::from_bytes(pub_key.as_slice()).unwrap();

service_executor.expect_get::<PublicKeyService>().never();
service_executor.expect_put::<PublicKeyService>().never();
Expand All @@ -522,8 +524,10 @@ mod tests {
let result = asymmetric_crypto_facade
.authenticate_sender(
public_key_identifier,
&sender_identity_pub_key,
sender_key_version,
Versioned {
object: &sender_identity_pub_key,
version: sender_key_version,
},
)
.await
.unwrap();
Expand All @@ -539,7 +543,8 @@ mod tests {
mut service_executor,
mut public_key_provider,
) = setup_authentication_test();
let sender_identity_pub_key = vec![9, 8, 7];
let pub_key = vec![5; 32];
let sender_identity_pub_key = EccPublicKey::from_bytes(pub_key.as_slice()).unwrap();

service_executor.expect_get::<PublicKeyService>().never();
service_executor.expect_put::<PublicKeyService>().never();
Expand All @@ -560,8 +565,10 @@ mod tests {
let result = asymmetric_crypto_facade
.authenticate_sender(
public_key_identifier,
&sender_identity_pub_key,
sender_key_version,
Versioned {
object: &sender_identity_pub_key,
version: sender_key_version,
},
)
.await
.unwrap();
Expand Down
6 changes: 5 additions & 1 deletion tuta-sdk/rust/sdk/src/crypto/crypto_facade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::crypto::aes::Iv;
#[cfg_attr(test, mockall_double::double)]
use crate::crypto::asymmetric_crypto_facade::AsymmetricCryptoFacade;
use crate::crypto::asymmetric_crypto_facade::{AsymmetricCryptoError, DecapsulatedAesKey};
use crate::crypto::ecc::EccPublicKey;
use crate::crypto::key::{GenericAesKey, KeyLoadError};
use crate::crypto::randomizer_facade::RandomizerFacade;
use crate::crypto::rsa::RSAEncryptionError;
Expand Down Expand Up @@ -40,6 +41,7 @@ pub struct ResolvedSessionKey {
pub session_key: GenericAesKey,
pub owner_enc_session_key: Vec<u8>,
pub owner_key_version: u64,
pub sender_identity_pub_key: Option<EccPublicKey>, // the sender's ecc key that was used to decrypt the PQ message, in case TutaCrypt was used.
}

#[cfg_attr(test, mockall::automock)]
Expand Down Expand Up @@ -113,6 +115,7 @@ impl CryptoFacade {
session_key,
owner_enc_session_key: owner_enc_session_key.clone(),
owner_key_version,
sender_identity_pub_key: None,
}))
}

Expand Down Expand Up @@ -148,7 +151,7 @@ impl CryptoFacade {
let mut _auth_status: Option<EncryptionAuthStatus> = None; // TODO: implement
let DecapsulatedAesKey {
decrypted_aes_key: decrypted_bucket_key,
sender_identity_pub_key: _sender_identity_key,
sender_identity_pub_key,
} = if let (Some(key_group), Some(pub_enc_bucket_key)) =
(&bucket_key.keyGroup, &bucket_key.pubEncBucketKey)
{
Expand Down Expand Up @@ -206,6 +209,7 @@ impl CryptoFacade {
session_key,
owner_enc_session_key,
owner_key_version: versioned_owner_group_key.version,
sender_identity_pub_key,
})
}
}
Expand Down
1 change: 1 addition & 0 deletions tuta-sdk/rust/sdk/src/crypto/ecc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ impl EccKeyPair {

impl EccPublicKey {
/// Get a reference to the underlying bytes.
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
self.0.as_slice()
}
Expand Down
2 changes: 1 addition & 1 deletion tuta-sdk/rust/sdk/src/crypto/kyber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ fn bind_shared_secret_to_ciphertext(
fn shake256(input: &[&[u8]]) -> [u8; SHAKE_BYTE_LENGTH] {
let mut hasher = Shake256::default();
for data in input {
hasher.update(*data);
hasher.update(data);
}
let mut reader = hasher.finalize_xof();
let mut output = [0; SHAKE_BYTE_LENGTH];
Expand Down
Loading

0 comments on commit 5682af2

Please sign in to comment.