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}