vodozemac/olm/session/
message_key.rs

1// Copyright 2021 Damir Jelić, Denis Kasak
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.
14
15use std::fmt::Debug;
16
17use serde::{Deserialize, Serialize};
18use zeroize::Zeroize;
19
20use super::{ratchet::RatchetPublicKey, DecryptionError};
21use crate::{
22    cipher::{Cipher, Mac},
23    olm::messages::Message,
24};
25
26/// A single-use encryption key for per-message encryption.
27///
28/// This key is used to encrypt a single message in an Olm session.
29pub struct MessageKey {
30    key: Box<[u8; 32]>,
31    ratchet_key: RatchetPublicKey,
32    index: u64,
33}
34
35impl Drop for MessageKey {
36    fn drop(&mut self) {
37        self.key.zeroize()
38    }
39}
40
41#[derive(Serialize, Deserialize, Clone)]
42pub(super) struct RemoteMessageKey {
43    pub key: Box<[u8; 32]>,
44    pub index: u64,
45}
46
47impl Debug for RemoteMessageKey {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        let Self { key: _, index } = self;
50
51        f.debug_struct("RemoteMessageKey").field("index", index).finish()
52    }
53}
54
55impl Drop for RemoteMessageKey {
56    fn drop(&mut self) {
57        self.key.zeroize()
58    }
59}
60
61impl MessageKey {
62    /// Creates a new [`MessageKey`] from the provided raw 32 bytes, along with
63    /// a public ratchet key and index.
64    ///
65    /// The ratchet key and index will be included in the created [`Message`],
66    /// allowing the message recipient to derive the same [`MessageKey`].
67    pub const fn new(key: Box<[u8; 32]>, ratchet_key: RatchetPublicKey, index: u64) -> Self {
68        Self { key, ratchet_key, index }
69    }
70
71    /// Encrypt the given plaintext using this [`MessageKey`].
72    ///
73    /// This method will authenticate the ciphertext using a 32-byte message
74    /// authentication code (MAC). If you need the message authentication code
75    /// to be truncated, please take a look at the
76    /// [`MessageKey::encrypt_truncated_mac()`] method instead.
77    pub fn encrypt(self, plaintext: &[u8]) -> Message {
78        let cipher = Cipher::new(&self.key);
79
80        let ciphertext = cipher.encrypt(plaintext);
81
82        let mut message = Message::new(*self.ratchet_key.as_ref(), self.index, ciphertext);
83
84        let mac = cipher.mac(&message.to_mac_bytes());
85        message.set_mac(mac);
86
87        message
88    }
89
90    /// Encrypts the provided plaintext using this [`MessageKey`].
91    ///
92    /// This method authenticates the ciphertext with an 8-byte message
93    /// authentication code (MAC). If you require the full, non-truncated
94    /// MAC, refer to the [`MessageKey::encrypt()`] method.
95    pub fn encrypt_truncated_mac(self, plaintext: &[u8]) -> Message {
96        let cipher = Cipher::new(&self.key);
97
98        let ciphertext = cipher.encrypt(plaintext);
99
100        let mut message =
101            Message::new_truncated_mac(*self.ratchet_key.as_ref(), self.index, ciphertext);
102
103        let mac = cipher.mac(&message.to_mac_bytes());
104        message.set_mac(mac);
105
106        message
107    }
108
109    /// Get a reference to the message key's raw 32-bytes.
110    #[cfg(feature = "low-level-api")]
111    pub fn key(&self) -> &[u8; 32] {
112        self.key.as_ref()
113    }
114
115    /// Get the message key's ratchet key.
116    #[cfg(feature = "low-level-api")]
117    pub const fn ratchet_key(&self) -> RatchetPublicKey {
118        self.ratchet_key
119    }
120
121    /// Get the message key's index.
122    #[cfg(feature = "low-level-api")]
123    pub const fn index(&self) -> u64 {
124        self.index
125    }
126}
127
128impl RemoteMessageKey {
129    pub const fn new(key: Box<[u8; 32]>, index: u64) -> Self {
130        Self { key, index }
131    }
132
133    pub const fn chain_index(&self) -> u64 {
134        self.index
135    }
136
137    pub fn decrypt_truncated_mac(&self, message: &Message) -> Result<Vec<u8>, DecryptionError> {
138        let cipher = Cipher::new(&self.key);
139
140        if let crate::cipher::MessageMac::Truncated(m) = &message.mac {
141            cipher.verify_truncated_mac(&message.to_mac_bytes(), m)?;
142            Ok(cipher.decrypt(&message.ciphertext)?)
143        } else {
144            Err(DecryptionError::InvalidMACLength(Mac::TRUNCATED_LEN, Mac::LENGTH))
145        }
146    }
147
148    pub fn decrypt(&self, message: &Message) -> Result<Vec<u8>, DecryptionError> {
149        let cipher = Cipher::new(&self.key);
150
151        if let crate::cipher::MessageMac::Full(m) = &message.mac {
152            cipher.verify_mac(&message.to_mac_bytes(), m)?;
153            Ok(cipher.decrypt(&message.ciphertext)?)
154        } else {
155            Err(DecryptionError::InvalidMACLength(Mac::LENGTH, Mac::TRUNCATED_LEN))
156        }
157    }
158}
159
160#[cfg(test)]
161#[cfg(feature = "low-level-api")]
162mod test {
163    use super::MessageKey;
164    use crate::olm::RatchetPublicKey;
165
166    #[test]
167    fn low_level_getters() {
168        let key = b"11111111111111111111111111111111";
169        let ratchet_key = RatchetPublicKey::from(*b"22222222222222222222222222222222");
170        let index: u64 = 3;
171        let message_key = MessageKey::new(Box::new(*key), ratchet_key, index);
172        assert_eq!(message_key.key(), key);
173        assert_eq!(message_key.ratchet_key(), ratchet_key);
174        assert_eq!(message_key.index(), index);
175    }
176}