1// Copyright 2021 The Matrix.org Foundation C.I.C.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
1415use std::sync::Arc;
1617use matrix_sdk_common::locks::Mutex;
18use ruma::{
19 api::client::backup::{EncryptedSessionDataInit, KeyBackupData, KeyBackupDataInit},
20 serde::Base64,
21};
22use vodozemac::{pk_encryption::PkEncryption, Curve25519PublicKey};
23use zeroize::Zeroizing;
2425use super::decryption::DecodeError;
26use crate::{olm::InboundGroupSession, types::Signatures};
2728#[derive(Debug)]
29struct InnerBackupKey {
30 key: Curve25519PublicKey,
31 signatures: Signatures,
32 version: Mutex<Option<String>>,
33}
3435/// The public part of a backup key.
36#[derive(Clone)]
37pub struct MegolmV1BackupKey {
38 inner: Arc<InnerBackupKey>,
39}
4041#[cfg(not(tarpaulin_include))]
42impl std::fmt::Debug for MegolmV1BackupKey {
43fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 formatter
45 .debug_struct("MegolmV1BackupKey")
46 .field("key", &self.to_base64())
47 .field("version", &self.backup_version())
48 .finish()
49 }
50}
5152impl MegolmV1BackupKey {
53pub(super) fn new(key: Curve25519PublicKey, version: Option<String>) -> Self {
54Self {
55 inner: InnerBackupKey {
56 key,
57 signatures: Default::default(),
58 version: Mutex::new(version),
59 }
60 .into(),
61 }
62 }
6364/// Get the full name of the backup algorithm this backup key supports.
65pub fn backup_algorithm(&self) -> &str {
66"m.megolm_backup.v1.curve25519-aes-sha2"
67}
6869/// Get all the signatures of this `MegolmV1BackupKey`.
70pub fn signatures(&self) -> Signatures {
71self.inner.signatures.to_owned()
72 }
7374/// Try to create a new `MegolmV1BackupKey` from a base 64 encoded string.
75pub fn from_base64(public_key: &str) -> Result<Self, DecodeError> {
76let key = Curve25519PublicKey::from_base64(public_key)?;
7778let inner =
79 InnerBackupKey { key, signatures: Default::default(), version: Mutex::new(None) };
8081Ok(MegolmV1BackupKey { inner: inner.into() })
82 }
8384/// Convert the [`MegolmV1BackupKey`] to a base 64 encoded string.
85pub fn to_base64(&self) -> String {
86self.inner.key.to_base64()
87 }
8889/// Get the backup version that this key is used with, if any.
90pub fn backup_version(&self) -> Option<String> {
91self.inner.version.lock().clone()
92 }
9394/// Set the backup version that this `MegolmV1BackupKey` will be used with.
95 ///
96 /// The key won't be able to encrypt room keys unless a version has been
97 /// set.
98pub fn set_version(&self, version: String) {
99*self.inner.version.lock() = Some(version);
100 }
101102/// Export the given inbound group session, and encrypt the data, ready for
103 /// writing to the backup.
104pub async fn encrypt(&self, session: InboundGroupSession) -> KeyBackupData {
105let pk = PkEncryption::from_key(self.inner.key);
106107// The forwarding chains don't mean much, we only care whether we received the
108 // session directly from the creator of the session or not.
109let forwarded_count = (session.has_been_imported() as u8).into();
110let first_message_index = session.first_known_index().into();
111112// Convert our key to the backup representation.
113let key = session.to_backup().await;
114115// The key gets zeroized in `BackedUpRoomKey` but we're creating a copy
116 // here that won't, so let's wrap it up in a `Zeroizing` struct.
117let key =
118 Zeroizing::new(serde_json::to_vec(&key).expect("Can't serialize exported room key"));
119120let message = pk.encrypt(&key);
121122let session_data = EncryptedSessionDataInit {
123 ephemeral: Base64::new(message.ephemeral_key.to_vec()),
124 ciphertext: Base64::new(message.ciphertext),
125 mac: Base64::new(message.mac),
126 }
127 .into();
128129 KeyBackupDataInit {
130 first_message_index,
131 forwarded_count,
132// TODO: is this actually used anywhere? seems to be completely
133 // useless and requires us to get the Device out of the store?
134 // Also should this be checked at the time of the backup or at the
135 // time of the room key receival?
136is_verified: false,
137 session_data,
138 }
139 .into()
140 }
141}