Skip to main content

vodozemac/olm/messages/
pre_key.rs

1// Copyright 2021 Damir Jelić
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 prost::Message as ProstMessage;
18use serde::{Deserialize, Serialize};
19
20use super::Message;
21use crate::{
22    Curve25519PublicKey, DecodeError,
23    olm::SessionKeys,
24    utilities::{base64_decode, base64_encode, encode_protobuf_message},
25};
26
27/// An encrypted Olm pre-key message.
28///
29/// Contains metadata that is required to establish a [`Session`] and a normal
30/// Olm [`Message`].
31///
32/// [`Session`]: crate::olm::Session
33#[derive(Clone, Debug, PartialEq, Eq)]
34pub struct PreKeyMessage {
35    pub(crate) session_keys: SessionKeys,
36    pub(crate) message: Message,
37}
38
39impl PreKeyMessage {
40    const VERSION: u8 = 3;
41
42    /// The single-use key that was uploaded to a public key directory by the
43    /// receiver of the message. Should be used to establish a [`Session`].
44    ///
45    /// [`Session`]: crate::olm::Session
46    pub const fn one_time_key(&self) -> Curve25519PublicKey {
47        self.session_keys.one_time_key
48    }
49
50    /// The base key, a single use key that was created just in time by the
51    /// sender of the message. Should be used to establish a [`Session`].
52    ///
53    /// [`Session`]: crate::olm::Session
54    pub const fn base_key(&self) -> Curve25519PublicKey {
55        self.session_keys.base_key
56    }
57
58    /// The long term identity key of the sender of the message. Should be used
59    /// to establish a [`Session`]
60    ///
61    /// [`Session`]: crate::olm::Session
62    pub const fn identity_key(&self) -> Curve25519PublicKey {
63        self.session_keys.identity_key
64    }
65
66    /// The collection of all keys required for establishing an Olm [`Session`]
67    /// from this pre-key message.
68    ///
69    /// Other methods on this struct (like [`PreKeyMessage::identity_key()`])
70    /// can be used to retrieve individual keys from this collection.
71    ///
72    /// [`Session`]: crate::olm::Session
73    pub const fn session_keys(&self) -> SessionKeys {
74        self.session_keys
75    }
76
77    /// Returns the globally unique session ID, in base64-encoded form.
78    ///
79    /// This is a shorthand helper of the [`SessionKeys::session_id()`] method.
80    pub fn session_id(&self) -> String {
81        self.session_keys.session_id()
82    }
83
84    /// The actual message that contains the ciphertext.
85    pub const fn message(&self) -> &Message {
86        &self.message
87    }
88
89    /// Try to decode the given byte slice as a Olm [`Message`].
90    ///
91    /// The expected format of the byte array is described in the
92    /// [`PreKeyMessage::to_bytes()`] method.
93    pub fn from_bytes(message: &[u8]) -> Result<Self, DecodeError> {
94        Self::try_from(message)
95    }
96
97    /// Encode the `PreKeyMessage` as an array of bytes.
98    ///
99    /// Olm `PreKeyMessage`s consist of a one-byte version, followed by a
100    /// variable length payload.
101    ///
102    /// ```text
103    /// +--------------+------------------------------------+
104    /// | Version Byte | Payload Bytes                      |
105    /// +--------------+------------------------------------+
106    /// ```
107    ///
108    /// The payload uses a format based on the Protocol Buffers encoding. It
109    /// consists of the following key-value pairs:
110    ///
111    ///   **Name**  |**Tag**|**Type**|              **Meaning**
112    /// :----------:|:-----:|:------:|:----------------------------------------:
113    /// One-Time-Key| 0x0A  | String |The public part of Bob's single-use key
114    ///   Base-Key  | 0x12  | String |The public part of Alice's single-use key
115    /// Identity-Key| 0x1A  | String |The public part of Alice's identity key
116    ///   Message   | 0x22  | String |An embedded Olm message
117    ///
118    /// The last key/value pair in a [`PreKeyMessage`] is a normal Olm
119    /// [`Message`].
120    pub fn to_bytes(&self) -> Vec<u8> {
121        let message = ProtoBufPreKeyMessage {
122            one_time_key: self.session_keys.one_time_key.as_bytes().to_vec(),
123            base_key: self.session_keys.base_key.as_bytes().to_vec(),
124            identity_key: self.session_keys.identity_key.as_bytes().to_vec(),
125            message: self.message.to_bytes(),
126        };
127
128        encode_protobuf_message(message, Self::VERSION)
129    }
130
131    /// Try to decode the given string as a Olm [`PreKeyMessage`].
132    ///
133    /// The string needs to be a base64 encoded byte array that follows the
134    /// format described in the [`PreKeyMessage::to_bytes()`] method.
135    pub fn from_base64(message: &str) -> Result<Self, DecodeError> {
136        Self::try_from(message)
137    }
138
139    /// Encode the [`PreKeyMessage`] as a string.
140    ///
141    /// This method first calls [`PreKeyMessage::to_bytes()`] and then encodes
142    /// the resulting byte array as a string using base64 encoding.
143    pub fn to_base64(&self) -> String {
144        base64_encode(self.to_bytes())
145    }
146
147    /// Create a new pre-key message from the session keys and standard message.
148    #[cfg(feature = "low-level-api")]
149    pub const fn wrap(session_keys: SessionKeys, message: Message) -> Self {
150        PreKeyMessage::new(session_keys, message)
151    }
152
153    pub(crate) const fn new(session_keys: SessionKeys, message: Message) -> Self {
154        Self { session_keys, message }
155    }
156}
157
158impl Serialize for PreKeyMessage {
159    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
160    where
161        S: serde::Serializer,
162    {
163        let message = self.to_base64();
164        serializer.serialize_str(&message)
165    }
166}
167
168impl<'de> Deserialize<'de> for PreKeyMessage {
169    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
170        let ciphertext = String::deserialize(d)?;
171        PreKeyMessage::from_base64(&ciphertext).map_err(serde::de::Error::custom)
172    }
173}
174
175impl TryFrom<&str> for PreKeyMessage {
176    type Error = DecodeError;
177
178    fn try_from(value: &str) -> Result<Self, Self::Error> {
179        let decoded = base64_decode(value)?;
180
181        Self::try_from(decoded)
182    }
183}
184
185impl TryFrom<Vec<u8>> for PreKeyMessage {
186    type Error = DecodeError;
187
188    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
189        Self::try_from(value.as_slice())
190    }
191}
192
193impl TryFrom<&[u8]> for PreKeyMessage {
194    type Error = DecodeError;
195
196    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
197        let version = *value.first().ok_or(DecodeError::MissingVersion)?;
198
199        if version != Self::VERSION {
200            Err(DecodeError::InvalidVersion(Self::VERSION, version))
201        } else {
202            let decoded = ProtoBufPreKeyMessage::decode(&value[1..value.len()])?;
203            let one_time_key = Curve25519PublicKey::from_slice(&decoded.one_time_key)?;
204            let base_key = Curve25519PublicKey::from_slice(&decoded.base_key)?;
205            let identity_key = Curve25519PublicKey::from_slice(&decoded.identity_key)?;
206
207            let message = decoded.message.try_into()?;
208
209            let session_keys = SessionKeys { one_time_key, identity_key, base_key };
210
211            Ok(Self { session_keys, message })
212        }
213    }
214}
215
216#[derive(Clone, ProstMessage)]
217struct ProtoBufPreKeyMessage {
218    #[prost(bytes, tag = "1")]
219    one_time_key: Vec<u8>,
220    #[prost(bytes, tag = "2")]
221    base_key: Vec<u8>,
222    #[prost(bytes, tag = "3")]
223    identity_key: Vec<u8>,
224    #[prost(bytes, tag = "4")]
225    message: Vec<u8>,
226}