1// Copyright (c) 2022 Yureka Lilian <yuka@yuka.dev>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7use xso::{text::Base64, AsXml, FromXml};
8
9use crate::message::MessagePayload;
10use crate::ns;
11use crate::pubsub::PubSubPayload;
12
13/// Element of the device list
14#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
15#[xml(namespace = ns::LEGACY_OMEMO, name = "device")]
16pub struct Device {
17 /// Device id
18 #[xml(attribute)]
19 pub id: u32,
20}
21
22/// A user's device list contains the OMEMO device ids of all the user's
23/// devicse. These can be used to look up bundles and build a session.
24#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
25#[xml(namespace = ns::LEGACY_OMEMO, name = "list")]
26pub struct DeviceList {
27 /// List of devices
28 #[xml(child(n = ..))]
29 pub devices: Vec<Device>,
30}
31
32impl PubSubPayload for DeviceList {}
33
34/// SignedPreKey public key
35/// Part of a device's bundle
36#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
37#[xml(namespace = ns::LEGACY_OMEMO, name = "signedPreKeyPublic")]
38pub struct SignedPreKeyPublic {
39 /// SignedPreKey id
40 #[xml(attribute(default, name = "signedPreKeyId"))]
41 pub signed_pre_key_id: Option<u32>,
42
43 /// Serialized PublicKey
44 #[xml(text = Base64)]
45 pub data: Vec<u8>,
46}
47
48/// SignedPreKey signature
49/// Part of a device's bundle
50#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
51#[xml(namespace = ns::LEGACY_OMEMO, name = "signedPreKeySignature")]
52pub struct SignedPreKeySignature {
53 /// Signature bytes
54 #[xml(text = Base64)]
55 pub data: Vec<u8>,
56}
57
58/// Part of a device's bundle
59#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
60#[xml(namespace = ns::LEGACY_OMEMO, name = "identityKey")]
61pub struct IdentityKey {
62 /// Serialized PublicKey
63 #[xml(text = Base64)]
64 pub data: Vec<u8>,
65}
66
67/// List of (single use) PreKeys
68/// Part of a device's bundle
69#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
70#[xml(namespace = ns::LEGACY_OMEMO, name = "prekeys")]
71pub struct Prekeys {
72 /// List of (single use) PreKeys
73 #[xml(child(n = ..))]
74 pub keys: Vec<PreKeyPublic>,
75}
76
77/// PreKey public key
78/// Part of a device's bundle
79#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
80#[xml(namespace = ns::LEGACY_OMEMO, name = "preKeyPublic")]
81pub struct PreKeyPublic {
82 /// PreKey id
83 #[xml(attribute = "preKeyId")]
84 pub pre_key_id: u32,
85
86 /// Serialized PublicKey
87 #[xml(text = Base64)]
88 pub data: Vec<u8>,
89}
90
91/// A collection of publicly accessible data that can be used to build a
92/// session with a device, namely its public IdentityKey, a signed PreKey with
93/// corresponding signature, and a list of (single use) PreKeys.
94#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
95#[xml(namespace = ns::LEGACY_OMEMO, name = "bundle")]
96pub struct Bundle {
97 /// SignedPreKey public key
98 #[xml(child(default))]
99 pub signed_pre_key_public: Option<SignedPreKeyPublic>,
100
101 /// SignedPreKey signature
102 #[xml(child(default))]
103 pub signed_pre_key_signature: Option<SignedPreKeySignature>,
104
105 /// IdentityKey public key
106 #[xml(child(default))]
107 pub identity_key: Option<IdentityKey>,
108
109 /// List of (single use) PreKeys
110 #[xml(child(default))]
111 pub prekeys: Option<Prekeys>,
112}
113
114impl PubSubPayload for Bundle {}
115
116/// The header contains encrypted keys for a message
117#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
118#[xml(namespace = ns::LEGACY_OMEMO, name = "header")]
119pub struct Header {
120 /// The device id of the sender
121 #[xml(attribute)]
122 pub sid: u32,
123
124 /// The key that the payload message is encrypted with, separately
125 /// encrypted for each recipient device.
126 #[xml(child(n = ..))]
127 pub keys: Vec<Key>,
128
129 /// IV used for payload encryption
130 #[xml(child)]
131 pub iv: IV,
132}
133
134/// IV used for payload encryption
135#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
136#[xml(namespace = ns::LEGACY_OMEMO, name = "iv")]
137pub struct IV {
138 /// IV bytes
139 #[xml(text = Base64)]
140 pub data: Vec<u8>,
141}
142
143/// Part of the OMEMO element header
144#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
145#[xml(namespace = ns::LEGACY_OMEMO, name = "key")]
146pub struct Key {
147 /// The device id this key is encrypted for.
148 #[xml(attribute)]
149 pub rid: u32,
150
151 /// The key element MUST be tagged with a prekey attribute set to true
152 /// if a PreKeySignalMessage is being used.
153 #[xml(attribute(default))]
154 pub prekey: bool,
155
156 /// The 16 bytes key and the GCM authentication tag concatenated together
157 /// and encrypted using the corresponding long-standing SignalProtocol
158 /// session
159 #[xml(text = Base64)]
160 pub data: Vec<u8>,
161}
162
163/// The encrypted message body
164#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
165#[xml(namespace = ns::LEGACY_OMEMO, name = "payload")]
166pub struct Payload {
167 /// Encrypted with AES-128 in Galois/Counter Mode (GCM)
168 #[xml(text = Base64)]
169 pub data: Vec<u8>,
170}
171
172/// An OMEMO element, which can be either a MessageElement (with payload),
173/// or a KeyTransportElement (without payload).
174#[derive(FromXml, AsXml, Debug, PartialEq, Clone)]
175#[xml(namespace = ns::LEGACY_OMEMO, name = "encrypted")]
176pub struct Encrypted {
177 /// The header contains encrypted keys for a message
178 #[xml(child)]
179 pub header: Header,
180
181 /// Payload for MessageElement
182 #[xml(child(default))]
183 pub payload: Option<Payload>,
184}
185
186impl MessagePayload for Encrypted {}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191 use minidom::Element;
192
193 #[test]
194 fn parse_bundle() {
195 let elem: Element = r#"<bundle xmlns="eu.siacs.conversations.axolotl">
196 <signedPreKeyPublic signedPreKeyId="1">BYAbACA15bPn95p7RGC2XbgQyly8aRKS4BaJ+hD8Ybhe</signedPreKeyPublic>
197 <signedPreKeySignature>sIJVNDZi/NgFsry4OBdM+adyGttLEXbUh/h/5dVOZveMgyVoIdgwBUzq8Wgd2xYTQMioNzwYebTX+9p0h9eujA==</signedPreKeySignature>
198 <identityKey>BQFd2p/Oq97vAAdLKA09DlcSg0x1xWn260p1jaeyIhAZ</identityKey>
199 <prekeys>
200 <preKeyPublic preKeyId="1">BbjHsF5ndtNV8ToRcJTYSNGePgAWsFGkSL6OG7B7LXRe</preKeyPublic>
201 <preKeyPublic preKeyId="2">BeWHsbBNx1uer1ia/nW/6tn/OlqHll9itjjUTIvV39x7</preKeyPublic>
202 <preKeyPublic preKeyId="3">BeVr5xPmNErkwK3ocPmv0Nohy3C4PKQBnxMuOqiXotJY</preKeyPublic>
203 </prekeys>
204</bundle>
205 "#.parse().unwrap();
206 let bundle: Bundle = elem.try_into().unwrap();
207 let bundle2 = Bundle {
208 signed_pre_key_public: Some(SignedPreKeyPublic {
209 signed_pre_key_id: Some(1),
210 data: vec![
211 5, 128, 27, 0, 32, 53, 229, 179, 231, 247, 154, 123, 68, 96, 182, 93, 184, 16,
212 202, 92, 188, 105, 18, 146, 224, 22, 137, 250, 16, 252, 97, 184, 94,
213 ],
214 }),
215 signed_pre_key_signature: Some(SignedPreKeySignature {
216 data: vec![
217 176, 130, 85, 52, 54, 98, 252, 216, 5, 178, 188, 184, 56, 23, 76, 249, 167,
218 114, 26, 219, 75, 17, 118, 212, 135, 248, 127, 229, 213, 78, 102, 247, 140,
219 131, 37, 104, 33, 216, 48, 5, 76, 234, 241, 104, 29, 219, 22, 19, 64, 200, 168,
220 55, 60, 24, 121, 180, 215, 251, 218, 116, 135, 215, 174, 140,
221 ],
222 }),
223 identity_key: Some(IdentityKey {
224 data: vec![
225 5, 1, 93, 218, 159, 206, 171, 222, 239, 0, 7, 75, 40, 13, 61, 14, 87, 18, 131,
226 76, 117, 197, 105, 246, 235, 74, 117, 141, 167, 178, 34, 16, 25,
227 ],
228 }),
229 prekeys: Some(Prekeys {
230 keys: vec![
231 PreKeyPublic {
232 pre_key_id: 1,
233 data: vec![
234 5, 184, 199, 176, 94, 103, 118, 211, 85, 241, 58, 17, 112, 148, 216,
235 72, 209, 158, 62, 0, 22, 176, 81, 164, 72, 190, 142, 27, 176, 123, 45,
236 116, 94,
237 ],
238 },
239 PreKeyPublic {
240 pre_key_id: 2,
241 data: vec![
242 5, 229, 135, 177, 176, 77, 199, 91, 158, 175, 88, 154, 254, 117, 191,
243 234, 217, 255, 58, 90, 135, 150, 95, 98, 182, 56, 212, 76, 139, 213,
244 223, 220, 123,
245 ],
246 },
247 PreKeyPublic {
248 pre_key_id: 3,
249 data: vec![
250 5, 229, 107, 231, 19, 230, 52, 74, 228, 192, 173, 232, 112, 249, 175,
251 208, 218, 33, 203, 112, 184, 60, 164, 1, 159, 19, 46, 58, 168, 151,
252 162, 210, 88,
253 ],
254 },
255 ],
256 }),
257 };
258 assert_eq!(bundle, bundle2);
259 }
260 #[test]
261 fn parse_device_list() {
262 let elem: Element = r#"<list xmlns="eu.siacs.conversations.axolotl">
263 <device id="1164059891" />
264 <device id="26052318" />
265 <device id="564866972" />
266</list>
267 "#
268 .parse()
269 .unwrap();
270 let list: DeviceList = elem.try_into().unwrap();
271 let list2 = DeviceList {
272 devices: vec![
273 Device { id: 1164059891 },
274 Device { id: 26052318 },
275 Device { id: 564866972 },
276 ],
277 };
278 assert_eq!(list, list2);
279 }
280 #[test]
281 fn parse_encrypted() {
282 let elem: Element = r#"<encrypted xmlns="eu.siacs.conversations.axolotl">
283 <header sid="564866972">
284 <key prekey="true" rid="1236">Mwjp9AESIQVylscLPpj/HlowaTiIsaBj73HCVEllXpVTtMG9EYwRexohBQFd2p/Oq97vAAdLKA09DlcSg0x1xWn260p1jaeyIhAZImIzCiEFhaQ4I+DuQgo6vCLCjHu4uewDZmWHuBl8uJw1IkyZxhUQABgAIjCoEVgVThWlaIlnN3V5Bg1hQX7OD1cvstLD5lH3zZMadL3KeONELESlBbeKmNgcYC/e3HZnbgWzBiic36yNAjAW</key>
285 <key rid="26052318">MwohBTV6dpumL1OxA9MdIFmu2E19+cIWDHWYfhdubvo0hmh6EAAYHCIwNc9/59eeYi8pVZQhMJJMVkKUkFP/yrTfG3o1lfpHGseCqb/JTgtDytQPiYrTpHl2V/mdsM6IPig=</key>
286 <key rid="1164059891">MwohBVnhz9pvEj1s1waEHuk5qpQqhUrpavycFz0hq/KYwI8oEAAYCSIwedEGN6MidxyvaPI8zorLcpG0Y7e7ecGkkd5vdDrL7Qt1tXaHb0iDyE/rZZHpFiNN38Izfp5vHv4=</key>
287 <iv>SY/SCGPt0CnA2odB</iv>
288 </header>
289 <payload>Vas=</payload>
290</encrypted>
291 "#.parse().unwrap();
292 let encrypted: Encrypted = elem.try_into().unwrap();
293 let encrypted2 = Encrypted {
294 header: Header {
295 sid: 564866972,
296 keys: vec![
297 Key {
298 rid: 1236,
299 prekey: true,
300 data: vec![
301 51, 8, 233, 244, 1, 18, 33, 5, 114, 150, 199, 11, 62, 152, 255, 30, 90,
302 48, 105, 56, 136, 177, 160, 99, 239, 113, 194, 84, 73, 101, 94, 149,
303 83, 180, 193, 189, 17, 140, 17, 123, 26, 33, 5, 1, 93, 218, 159, 206,
304 171, 222, 239, 0, 7, 75, 40, 13, 61, 14, 87, 18, 131, 76, 117, 197,
305 105, 246, 235, 74, 117, 141, 167, 178, 34, 16, 25, 34, 98, 51, 10, 33,
306 5, 133, 164, 56, 35, 224, 238, 66, 10, 58, 188, 34, 194, 140, 123, 184,
307 185, 236, 3, 102, 101, 135, 184, 25, 124, 184, 156, 53, 34, 76, 153,
308 198, 21, 16, 0, 24, 0, 34, 48, 168, 17, 88, 21, 78, 21, 165, 104, 137,
309 103, 55, 117, 121, 6, 13, 97, 65, 126, 206, 15, 87, 47, 178, 210, 195,
310 230, 81, 247, 205, 147, 26, 116, 189, 202, 120, 227, 68, 44, 68, 165,
311 5, 183, 138, 152, 216, 28, 96, 47, 222, 220, 118, 103, 110, 5, 179, 6,
312 40, 156, 223, 172, 141, 2, 48, 22,
313 ],
314 },
315 Key {
316 rid: 26052318,
317 prekey: false,
318 data: vec![
319 51, 10, 33, 5, 53, 122, 118, 155, 166, 47, 83, 177, 3, 211, 29, 32, 89,
320 174, 216, 77, 125, 249, 194, 22, 12, 117, 152, 126, 23, 110, 110, 250,
321 52, 134, 104, 122, 16, 0, 24, 28, 34, 48, 53, 207, 127, 231, 215, 158,
322 98, 47, 41, 85, 148, 33, 48, 146, 76, 86, 66, 148, 144, 83, 255, 202,
323 180, 223, 27, 122, 53, 149, 250, 71, 26, 199, 130, 169, 191, 201, 78,
324 11, 67, 202, 212, 15, 137, 138, 211, 164, 121, 118, 87, 249, 157, 176,
325 206, 136, 62, 40,
326 ],
327 },
328 Key {
329 rid: 1164059891,
330 prekey: false,
331 data: vec![
332 51, 10, 33, 5, 89, 225, 207, 218, 111, 18, 61, 108, 215, 6, 132, 30,
333 233, 57, 170, 148, 42, 133, 74, 233, 106, 252, 156, 23, 61, 33, 171,
334 242, 152, 192, 143, 40, 16, 0, 24, 9, 34, 48, 121, 209, 6, 55, 163, 34,
335 119, 28, 175, 104, 242, 60, 206, 138, 203, 114, 145, 180, 99, 183, 187,
336 121, 193, 164, 145, 222, 111, 116, 58, 203, 237, 11, 117, 181, 118,
337 135, 111, 72, 131, 200, 79, 235, 101, 145, 233, 22, 35, 77, 223, 194,
338 51, 126, 158, 111, 30, 254,
339 ],
340 },
341 ],
342 iv: IV {
343 data: vec![73, 143, 210, 8, 99, 237, 208, 41, 192, 218, 135, 65],
344 },
345 },
346 payload: Some(Payload {
347 data: vec![85, 171],
348 }),
349 };
350 assert_eq!(encrypted, encrypted2);
351 }
352}