From 9aa9e4710b7e3a66291112e5ca2613d5a6e0c4f0 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Mon, 25 Mar 2024 14:50:49 +0100 Subject: [PATCH] test(olm): Add mutation tests for account Relates to: #78 Signed-off-by: Johannes Marbach --- src/olm/account/fallback_keys.rs | 13 ++++ src/olm/account/mod.rs | 108 ++++++++++++++++++++++++++++++- src/olm/account/one_time_keys.rs | 20 +++++- 3 files changed, 139 insertions(+), 2 deletions(-) diff --git a/src/olm/account/fallback_keys.rs b/src/olm/account/fallback_keys.rs index 49a9c0e3..cd537a41 100644 --- a/src/olm/account/fallback_keys.rs +++ b/src/olm/account/fallback_keys.rs @@ -133,4 +133,17 @@ mod test { assert_eq!(secret_bytes, fetched_key.to_bytes()); } + + #[test] + fn fallback_key_publishing() { + let mut fallback_keys = FallbackKeys::new(); + assert_eq!(fallback_keys.key_id, 0); + + fallback_keys.generate_fallback_key(); + assert_eq!(fallback_keys.key_id, 1); + assert!(fallback_keys.unpublished_fallback_key().is_some()); + + fallback_keys.mark_as_published(); + assert!(fallback_keys.unpublished_fallback_key().is_none()); + } } diff --git a/src/olm/account/mod.rs b/src/olm/account/mod.rs index 8ba3cdba..060466f7 100644 --- a/src/olm/account/mod.rs +++ b/src/olm/account/mod.rs @@ -676,12 +676,16 @@ mod libolm { #[cfg(test)] mod test { use anyhow::{bail, Context, Result}; + use matrix_pickle::Encode; use olm_rs::{account::OlmAccount, session::OlmMessage as LibolmOlmMessage}; - use super::{Account, InboundCreationResult, SessionConfig, SessionCreationError}; + use super::{ + libolm::Pickle, Account, InboundCreationResult, SessionConfig, SessionCreationError, + }; use crate::{ cipher::Mac, olm::{ + account::PUBLIC_MAX_ONE_TIME_KEYS, messages::{OlmMessage, PreKeyMessage}, AccountPickle, }, @@ -690,6 +694,49 @@ mod test { const PICKLE_KEY: [u8; 32] = [0u8; 32]; + #[test] + fn max_number_of_one_time_keys_matches_global_constant() { + assert_eq!(Account::new().max_number_of_one_time_keys(), PUBLIC_MAX_ONE_TIME_KEYS); + } + + #[test] + #[cfg(feature = "low-level-api")] + fn generate_and_remove_one_time_key() { + let mut alice = Account::new(); + assert_eq!(alice.stored_one_time_key_count(), 0); + + alice.generate_one_time_keys(1); + assert_eq!(alice.stored_one_time_key_count(), 1); + + let public_key = alice + .one_time_keys() + .values() + .next() + .copied() + .expect("Should have an unpublished one-time key"); + let secret_key_bytes = alice + .find_one_time_key(&public_key) + .expect("Should find secret key for public one-time key") + .to_bytes(); + let removed_key_bytes = alice + .remove_one_time_key(public_key) + .expect("Should be able to remove one-time key") + .to_bytes(); + + assert_eq!(removed_key_bytes, secret_key_bytes); + assert_eq!(alice.stored_one_time_key_count(), 0); + } + + #[test] + fn generate_and_forget_fallback_keys() { + let mut alice = Account::default(); + assert!(!alice.forget_fallback_key()); + alice.generate_fallback_key(); + assert!(!alice.forget_fallback_key()); + alice.generate_fallback_key(); + assert!(alice.forget_fallback_key()); + } + #[test] fn vodozemac_libolm_communication() -> Result<()> { // vodozemac account @@ -773,7 +820,9 @@ mod test { .1, ); + assert!(!bob.one_time_keys().is_empty()); bob.mark_keys_as_published(); + assert!(bob.one_time_keys().is_empty()); let message = "It's a secret to everybody"; let olm_message = alice_session.encrypt(message); @@ -957,6 +1006,63 @@ mod test { Ok(()) } + #[test] + fn pickle_cycle_with_one_fallback_key() { + let mut alice = Account::new(); + alice.generate_fallback_key(); + + let mut encoded = Vec::::new(); + let pickle = Pickle::from(&alice); + let size = pickle.encode(&mut encoded).expect("Should encode pickle"); + assert_eq!(size, encoded.len()); + + let account = + Account::from_decrypted_libolm_pickle(&encoded).expect("Should unpickle account"); + + let key_bytes = alice + .fallback_key() + .values() + .next() + .expect("Should have a fallback key before encoding") + .to_bytes(); + let decoded_key_bytes = account + .fallback_key() + .values() + .next() + .expect("Should have a fallback key after decoding") + .to_bytes(); + assert_eq!(key_bytes, decoded_key_bytes); + } + + #[test] + fn pickle_cycle_with_two_fallback_keys() { + let mut alice = Account::new(); + alice.generate_fallback_key(); + alice.generate_fallback_key(); + + let mut encoded = Vec::::new(); + let pickle = Pickle::from(&alice); + let size = pickle.encode(&mut encoded).expect("Should encode pickle"); + assert_eq!(size, encoded.len()); + + let account = + Account::from_decrypted_libolm_pickle(&encoded).expect("Should unpickle account"); + + let key_bytes = alice + .fallback_key() + .values() + .next() + .expect("Should have a fallback key before encoding") + .to_bytes(); + let decoded_key_bytes = account + .fallback_key() + .values() + .next() + .expect("Should have a fallback key after decoding") + .to_bytes(); + assert_eq!(key_bytes, decoded_key_bytes); + } + #[test] #[cfg(feature = "libolm-compat")] fn signing_with_expanded_key() -> Result<()> { diff --git a/src/olm/account/one_time_keys.rs b/src/olm/account/one_time_keys.rs index 1ec2a9f2..765b461e 100644 --- a/src/olm/account/one_time_keys.rs +++ b/src/olm/account/one_time_keys.rs @@ -192,15 +192,22 @@ mod test { assert!(store.private_keys.is_empty()); store.generate(OneTimeKeys::MAX_ONE_TIME_KEYS); - assert_eq!(store.private_keys.len(), OneTimeKeys::MAX_ONE_TIME_KEYS); assert_eq!(store.unpublished_public_keys.len(), OneTimeKeys::MAX_ONE_TIME_KEYS); + assert_eq!(store.private_keys.len(), OneTimeKeys::MAX_ONE_TIME_KEYS); assert_eq!(store.key_ids_by_key.len(), OneTimeKeys::MAX_ONE_TIME_KEYS); + store + .private_keys + .keys() + .for_each(|key_id| assert!(!store.is_secret_key_published(key_id))); + store.mark_as_published(); assert!(store.unpublished_public_keys.is_empty()); assert_eq!(store.private_keys.len(), OneTimeKeys::MAX_ONE_TIME_KEYS); assert_eq!(store.key_ids_by_key.len(), OneTimeKeys::MAX_ONE_TIME_KEYS); + store.private_keys.keys().for_each(|key_id| assert!(store.is_secret_key_published(key_id))); + let oldest_key_id = store.private_keys.keys().next().copied().expect("Couldn't get the first key ID"); assert_eq!(oldest_key_id, KeyId(0)); @@ -210,6 +217,17 @@ mod test { assert_eq!(store.private_keys.len(), OneTimeKeys::MAX_ONE_TIME_KEYS); assert_eq!(store.key_ids_by_key.len(), OneTimeKeys::MAX_ONE_TIME_KEYS); + store + .private_keys + .keys() + .take(OneTimeKeys::MAX_ONE_TIME_KEYS - 10) + .for_each(|key_id| assert!(store.is_secret_key_published(key_id))); + store + .private_keys + .keys() + .skip(OneTimeKeys::MAX_ONE_TIME_KEYS - 10) + .for_each(|key_id| assert!(!store.is_secret_key_published(key_id))); + let oldest_key_id = store.private_keys.keys().next().copied().expect("Couldn't get the first key ID");