1use std::fmt::Debug;
16
17use prost::Message as ProstMessage;
18use serde::{Deserialize, Serialize};
19
20use crate::{
21 Curve25519PublicKey, DecodeError,
22 cipher::{Mac, MessageMac},
23 utilities::{base64_decode, base64_encode, encode_protobuf_message, extract_mac},
24};
25
26pub(crate) const MAC_TRUNCATED_VERSION: u8 = 3;
27pub(crate) const VERSION: u8 = 4;
28
29#[derive(Clone, PartialEq, Eq)]
36pub struct Message {
37 pub(crate) version: u8,
38 pub(crate) ratchet_key: Curve25519PublicKey,
39 pub(crate) chain_index: u64,
40 pub(crate) ciphertext: Vec<u8>,
41 pub(crate) mac: MessageMac,
42}
43
44impl Message {
45 pub const fn ratchet_key(&self) -> Curve25519PublicKey {
48 self.ratchet_key
49 }
50
51 pub const fn chain_index(&self) -> u64 {
53 self.chain_index
54 }
55
56 pub fn ciphertext(&self) -> &[u8] {
58 &self.ciphertext
59 }
60
61 pub const fn version(&self) -> u8 {
63 self.version
64 }
65
66 pub fn from_bytes(bytes: &[u8]) -> Result<Self, DecodeError> {
71 Self::try_from(bytes)
72 }
73
74 pub fn to_bytes(&self) -> Vec<u8> {
94 let mut message = self.encode();
95 message.extend(self.mac.as_bytes());
96
97 message
98 }
99
100 pub fn from_base64(message: &str) -> Result<Self, DecodeError> {
105 Self::try_from(message)
106 }
107
108 pub fn to_base64(&self) -> String {
113 base64_encode(self.to_bytes())
114 }
115
116 #[cfg(feature = "experimental-session-config")]
117 pub(crate) fn new(
118 ratchet_key: Curve25519PublicKey,
119 chain_index: u64,
120 ciphertext: Vec<u8>,
121 ) -> Self {
122 Self {
123 version: VERSION,
124 ratchet_key,
125 chain_index,
126 ciphertext,
127 mac: Mac([0u8; Mac::LENGTH]).into(),
128 }
129 }
130
131 pub(crate) fn new_truncated_mac(
132 ratchet_key: Curve25519PublicKey,
133 chain_index: u64,
134 ciphertext: Vec<u8>,
135 ) -> Self {
136 Self {
137 version: MAC_TRUNCATED_VERSION,
138 ratchet_key,
139 chain_index,
140 ciphertext,
141 mac: [0u8; Mac::TRUNCATED_LEN].into(),
142 }
143 }
144
145 fn encode(&self) -> Vec<u8> {
146 let message = ProtoBufMessage {
147 ratchet_key: self.ratchet_key.to_bytes().to_vec(),
148 chain_index: self.chain_index,
149 ciphertext: self.ciphertext.clone(),
150 };
151
152 encode_protobuf_message(message, self.version())
153 }
154
155 pub(crate) fn to_mac_bytes(&self) -> Vec<u8> {
156 self.encode()
157 }
158
159 pub(crate) fn set_mac(&mut self, mac: Mac) {
160 match self.mac {
161 MessageMac::Truncated(_) => self.mac = mac.truncate().into(),
162 MessageMac::Full(_) => self.mac = mac.into(),
163 }
164 }
165}
166
167impl Serialize for Message {
168 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
169 where
170 S: serde::Serializer,
171 {
172 let message = self.to_base64();
173 serializer.serialize_str(&message)
174 }
175}
176
177impl<'de> Deserialize<'de> for Message {
178 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
179 let ciphertext = String::deserialize(d)?;
180 Message::from_base64(&ciphertext).map_err(serde::de::Error::custom)
181 }
182}
183
184impl TryFrom<&str> for Message {
185 type Error = DecodeError;
186
187 fn try_from(value: &str) -> Result<Self, Self::Error> {
188 let decoded = base64_decode(value)?;
189
190 Self::try_from(decoded)
191 }
192}
193
194impl TryFrom<Vec<u8>> for Message {
195 type Error = DecodeError;
196
197 fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
198 Self::try_from(value.as_slice())
199 }
200}
201
202impl TryFrom<&[u8]> for Message {
203 type Error = DecodeError;
204
205 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
206 let version = *value.first().ok_or(DecodeError::MissingVersion)?;
207
208 let mac_length = match version {
209 VERSION => Mac::LENGTH,
210 MAC_TRUNCATED_VERSION => Mac::TRUNCATED_LEN,
211 _ => return Err(DecodeError::InvalidVersion(VERSION, version)),
212 };
213
214 if value.len() < mac_length + 2 {
215 Err(DecodeError::MessageTooShort(value.len()))
216 } else {
217 let inner = ProtoBufMessage::decode(
218 value
219 .get(1..value.len() - mac_length)
220 .ok_or(DecodeError::MessageTooShort(value.len()))?,
221 )?;
222
223 let mac_slice = &value[value.len() - mac_length..];
224
225 if mac_slice.len() != mac_length {
226 Err(DecodeError::InvalidMacLength(mac_length, mac_slice.len()))
227 } else {
228 let mac = extract_mac(mac_slice, version == MAC_TRUNCATED_VERSION);
229
230 let chain_index = inner.chain_index;
231 let ciphertext = inner.ciphertext;
232 let ratchet_key = Curve25519PublicKey::from_slice(&inner.ratchet_key)?;
233
234 let message = Message { version, ratchet_key, chain_index, ciphertext, mac };
235
236 Ok(message)
237 }
238 }
239 }
240}
241
242impl Debug for Message {
243 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
244 let Self { version, ratchet_key, chain_index, ciphertext: _, mac: _ } = self;
245
246 f.debug_struct("Message")
247 .field("version", version)
248 .field("ratchet_key", ratchet_key)
249 .field("chain_index", chain_index)
250 .finish_non_exhaustive()
251 }
252}
253
254#[derive(ProstMessage, PartialEq, Eq)]
255struct ProtoBufMessage {
256 #[prost(bytes, tag = "1")]
257 ratchet_key: Vec<u8>,
258 #[prost(uint64, required, tag = "2")]
259 chain_index: u64,
260 #[prost(bytes, tag = "4")]
261 ciphertext: Vec<u8>,
262}
263
264#[cfg(test)]
265mod test {
266 use std::assert_matches;
267
268 use strass::assert_let;
269
270 use super::Message;
271 use crate::{
272 Curve25519PublicKey, DecodeError,
273 olm::messages::message::{MAC_TRUNCATED_VERSION, VERSION},
274 };
275
276 #[test]
277 fn encode() {
278 let message = b"\x03\n\x20ratchetkeyhereprettyplease123456\x10\x02\"\nciphertext";
279 let message_mac =
280 b"\x03\n\x20ratchetkeyhereprettyplease123456\x10\x02\"\nciphertextMACHEREE";
281
282 let ratchet_key = Curve25519PublicKey::from(*b"ratchetkeyhereprettyplease123456");
283 let ciphertext = b"ciphertext";
284 let chain_index = 2;
285
286 let mut encoded = Message::new_truncated_mac(ratchet_key, chain_index, ciphertext.to_vec());
287 encoded.mac = (*b"MACHEREE").into();
288
289 assert_eq!(encoded.to_mac_bytes(), message.as_slice());
290 assert_eq!(encoded.to_bytes(), message_mac.as_slice());
291 assert_eq!(encoded.ciphertext(), ciphertext.to_vec());
292 assert_eq!(encoded.chain_index(), chain_index);
293 assert_eq!(encoded.version(), MAC_TRUNCATED_VERSION);
294 }
295
296 #[test]
297 fn from_bytes_normal_message() {
298 let bytes = b"\x04\n\x20ratchetkeyhereprettyplease123456\x10\x02\"\nciphertextMAC_01234567890_01234567890_HERE";
299 let result = Message::try_from(bytes.as_slice());
300 assert_let!(Ok(message) = result);
301
302 let Message { version, ratchet_key, chain_index, ciphertext, mac } = message;
303
304 assert_eq!(version, VERSION);
305 assert_eq!(ratchet_key.as_bytes(), b"ratchetkeyhereprettyplease123456");
306 assert_eq!(chain_index, 2);
307 assert_eq!(ciphertext, b"ciphertext");
308 assert_eq!(mac.as_bytes(), b"MAC_01234567890_01234567890_HERE");
309 }
310
311 #[test]
312 fn from_bytes_too_short() {
313 let bytes = vec![MAC_TRUNCATED_VERSION, 0, 0, 0, 0, 0, 0, 0, 0];
314 let result = Message::try_from(bytes);
315 assert_matches!(result, Err(DecodeError::MessageTooShort(9)));
316 }
317
318 #[test]
319 fn from_bytes_decoding_error() {
320 let bytes = vec![MAC_TRUNCATED_VERSION, 0, 0, 0, 0, 0, 0, 0, 0, 0];
321 let result = Message::try_from(bytes);
322 assert_matches!(result, Err(DecodeError::ProtoBufError(_)));
323 }
324
325 #[test]
326 fn from_bytes_invalid_tag() {
327 let bytes = [
328 VERSION, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
329 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
330 ];
331 let result = Message::try_from(bytes.to_vec());
332 assert_matches!(result, Err(DecodeError::ProtoBufError(_)));
333 }
334}