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