use std::sync::Arc;
use matrix_sdk_common::locks::Mutex;
use ruma::{
api::client::backup::{EncryptedSessionDataInit, KeyBackupData, KeyBackupDataInit},
serde::Base64,
};
use vodozemac::{pk_encryption::PkEncryption, Curve25519PublicKey};
use zeroize::Zeroizing;
use super::decryption::DecodeError;
use crate::{olm::InboundGroupSession, types::Signatures};
#[derive(Debug)]
struct InnerBackupKey {
key: Curve25519PublicKey,
signatures: Signatures,
version: Mutex<Option<String>>,
}
#[derive(Clone)]
pub struct MegolmV1BackupKey {
inner: Arc<InnerBackupKey>,
}
#[cfg(not(tarpaulin_include))]
impl std::fmt::Debug for MegolmV1BackupKey {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("MegolmV1BackupKey")
.field("key", &self.to_base64())
.field("version", &self.backup_version())
.finish()
}
}
impl MegolmV1BackupKey {
pub(super) fn new(key: Curve25519PublicKey, version: Option<String>) -> Self {
Self {
inner: InnerBackupKey {
key,
signatures: Default::default(),
version: Mutex::new(version),
}
.into(),
}
}
pub fn backup_algorithm(&self) -> &str {
"m.megolm_backup.v1.curve25519-aes-sha2"
}
pub fn signatures(&self) -> Signatures {
self.inner.signatures.to_owned()
}
pub fn from_base64(public_key: &str) -> Result<Self, DecodeError> {
let key = Curve25519PublicKey::from_base64(public_key)?;
let inner =
InnerBackupKey { key, signatures: Default::default(), version: Mutex::new(None) };
Ok(MegolmV1BackupKey { inner: inner.into() })
}
pub fn to_base64(&self) -> String {
self.inner.key.to_base64()
}
pub fn backup_version(&self) -> Option<String> {
self.inner.version.lock().clone()
}
pub fn set_version(&self, version: String) {
*self.inner.version.lock() = Some(version);
}
pub async fn encrypt(&self, session: InboundGroupSession) -> KeyBackupData {
let pk = PkEncryption::from_key(self.inner.key);
let forwarded_count = (session.has_been_imported() as u8).into();
let first_message_index = session.first_known_index().into();
let key = session.to_backup().await;
let key =
Zeroizing::new(serde_json::to_vec(&key).expect("Can't serialize exported room key"));
let message = pk.encrypt(&key);
let session_data = EncryptedSessionDataInit {
ephemeral: Base64::new(message.ephemeral_key.to_vec()),
ciphertext: Base64::new(message.ciphertext),
mac: Base64::new(message.mac),
}
.into();
KeyBackupDataInit {
first_message_index,
forwarded_count,
is_verified: false,
session_data,
}
.into()
}
}