Skip to main content

xmpp_parsers/
ecaps2.rs

1// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
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::{AsXml, FromXml};
8
9use crate::data_forms::DataForm;
10use crate::disco::{DiscoInfoQuery, DiscoInfoResult, Identity};
11use crate::hashes::{Algo, Hash};
12use crate::ns;
13use crate::presence::PresencePayload;
14use base64::{engine::general_purpose::STANDARD as Base64, Engine};
15use blake2::{
16    digest::{Update, VariableOutput},
17    Blake2bVar,
18};
19use digest::Digest;
20use sha2::{Sha256, Sha512};
21use sha3::{Sha3_256, Sha3_512};
22use xso::error::Error;
23
24/// Represents a set of capability hashes, all of them must correspond to
25/// the same input [disco#info](../disco/struct.DiscoInfoResult.html),
26/// using different [algorithms](../hashes/enum.Algo.html).
27#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
28#[xml(namespace = ns::ECAPS2, name = "c")]
29pub struct ECaps2 {
30    /// Hashes of the [disco#info](../disco/struct.DiscoInfoResult.html).
31    #[xml(child(n = ..))]
32    pub hashes: Vec<Hash>,
33}
34
35impl PresencePayload for ECaps2 {}
36
37impl ECaps2 {
38    /// Create an ECaps2 element from a list of hashes.
39    pub fn new(hashes: Vec<Hash>) -> ECaps2 {
40        ECaps2 { hashes }
41    }
42}
43
44fn compute_item(field: &str) -> Vec<u8> {
45    let mut bytes = field.as_bytes().to_vec();
46    bytes.push(0x1f);
47    bytes
48}
49
50fn compute_items<'x, T: 'x, I: IntoIterator<Item = &'x T>, F: Fn(&'x T) -> Vec<u8>>(
51    things: I,
52    separator: u8,
53    encode: F,
54) -> Vec<u8> {
55    let mut string: Vec<u8> = vec![];
56    let mut accumulator: Vec<Vec<u8>> = vec![];
57    for thing in things.into_iter() {
58        let bytes = encode(thing);
59        accumulator.push(bytes);
60    }
61    // This works using the expected i;octet collation.
62    accumulator.sort();
63    for mut bytes in accumulator {
64        string.append(&mut bytes);
65    }
66    string.push(separator);
67    string
68}
69
70fn compute_features(features: &[String]) -> Vec<u8> {
71    compute_items(features, 0x1c, |feature| compute_item(&feature))
72}
73
74fn compute_identities(identities: &[Identity]) -> Vec<u8> {
75    compute_items(identities, 0x1c, |identity| {
76        let mut bytes = compute_item(&identity.category);
77        bytes.append(&mut compute_item(&identity.type_));
78        bytes.append(&mut compute_item(
79            identity.lang.as_deref().unwrap_or_default(),
80        ));
81        bytes.append(&mut compute_item(
82            identity.name.as_deref().unwrap_or_default(),
83        ));
84        bytes.push(0x1e);
85        bytes
86    })
87}
88
89fn compute_extensions(extensions: &[DataForm]) -> Result<Vec<u8>, Error> {
90    for extension in extensions {
91        if extension.form_type().is_none() {
92            return Err(Error::Other("Missing FORM_TYPE in extension."));
93        }
94    }
95    Ok(compute_items(extensions, 0x1c, |extension| {
96        let mut bytes = compute_item("FORM_TYPE");
97        bytes.append(&mut compute_item(
98            if let Some(form_type) = extension.form_type() {
99                form_type
100            } else {
101                unreachable!()
102            },
103        ));
104        bytes.push(0x1e);
105        bytes.append(&mut compute_items(
106            extension
107                .fields
108                .iter()
109                .filter(|field| field.var.as_deref().unwrap_or("") != "FORM_TYPE"),
110            0x1d,
111            |field| {
112                let mut bytes = vec![];
113                if let Some(var) = &field.var {
114                    bytes.append(&mut compute_item(var));
115                }
116                bytes.append(&mut compute_items(&field.values, 0x1e, |value| {
117                    compute_item(value)
118                }));
119                bytes
120            },
121        ));
122        bytes
123    }))
124}
125
126/// Applies the [algorithm from
127/// XEP-0390](https://xmpp.org/extensions/xep-0390.html#algorithm-input) on a
128/// [disco#info query element](../disco/struct.DiscoInfoResult.html).
129pub fn compute_disco(disco: &DiscoInfoResult) -> Result<Vec<u8>, Error> {
130    // TODO: Figure out a way to remove the clones here.
131    let features: Vec<_> = disco.features.iter().cloned().collect();
132
133    let features_string = compute_features(&features);
134    let identities_string = compute_identities(&disco.identities);
135    let extensions_string = compute_extensions(&disco.extensions)?;
136
137    let mut final_string = vec![];
138    final_string.extend(features_string);
139    final_string.extend(identities_string);
140    final_string.extend(extensions_string);
141    Ok(final_string)
142}
143
144/// Hashes the result of [compute_disco()] with one of the supported [hash
145/// algorithms](../hashes/enum.Algo.html).
146pub fn hash_ecaps2(data: &[u8], algo: Algo) -> Result<Hash, Error> {
147    Ok(Hash {
148        hash: match algo {
149            Algo::Sha_256 => {
150                let hash = Sha256::digest(data);
151                hash.to_vec()
152            }
153            Algo::Sha_512 => {
154                let hash = Sha512::digest(data);
155                hash.to_vec()
156            }
157            Algo::Sha3_256 => {
158                let hash = Sha3_256::digest(data);
159                hash.to_vec()
160            }
161            Algo::Sha3_512 => {
162                let hash = Sha3_512::digest(data);
163                hash.to_vec()
164            }
165            Algo::Blake2b_256 => {
166                let mut hasher = Blake2bVar::new(32).unwrap();
167                hasher.update(data);
168                let mut vec = vec![0u8; 32];
169                hasher.finalize_variable(&mut vec).unwrap();
170                vec
171            }
172            Algo::Blake2b_512 => {
173                let mut hasher = Blake2bVar::new(64).unwrap();
174                hasher.update(data);
175                let mut vec = vec![0u8; 64];
176                hasher.finalize_variable(&mut vec).unwrap();
177                vec
178            }
179            Algo::Sha_1 => return Err(Error::Other("Disabled algorithm sha-1: unsafe.")),
180            Algo::Unknown(_algo) => return Err(Error::Other("Unknown algorithm in ecaps2.")),
181        },
182        algo,
183    })
184}
185
186/// Helper function to create the query for the disco#info corresponding to an
187/// ecaps2 hash.
188pub fn query_ecaps2(hash: Hash) -> DiscoInfoQuery {
189    DiscoInfoQuery {
190        node: Some(format!(
191            "{}#{}.{}",
192            ns::ECAPS2,
193            String::from(hash.algo),
194            Base64.encode(&hash.hash)
195        )),
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use super::*;
202    use minidom::Element;
203    use xso::error::FromElementError;
204
205    #[cfg(target_pointer_width = "32")]
206    #[test]
207    fn test_size() {
208        assert_size!(ECaps2, 12);
209    }
210
211    #[cfg(target_pointer_width = "64")]
212    #[test]
213    fn test_size() {
214        assert_size!(ECaps2, 24);
215    }
216
217    #[test]
218    fn test_parse() {
219        let elem: Element = "<c xmlns='urn:xmpp:caps'><hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=</hash><hash xmlns='urn:xmpp:hashes:2' algo='sha3-256'>+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=</hash></c>".parse().unwrap();
220        let ecaps2 = ECaps2::try_from(elem).unwrap();
221        assert_eq!(ecaps2.hashes.len(), 2);
222        assert_eq!(ecaps2.hashes[0].algo, Algo::Sha_256);
223        assert_eq!(
224            ecaps2.hashes[0].hash,
225            Base64
226                .decode("K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=")
227                .unwrap()
228        );
229        assert_eq!(ecaps2.hashes[1].algo, Algo::Sha3_256);
230        assert_eq!(
231            ecaps2.hashes[1].hash,
232            Base64
233                .decode("+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=")
234                .unwrap()
235        );
236    }
237
238    #[test]
239    #[cfg_attr(not(feature = "pedantic"), should_panic = "Result::unwrap_err")]
240    fn test_invalid_child() {
241        let elem: Element = "<c xmlns='urn:xmpp:caps'><hash xmlns='urn:xmpp:hashes:2' algo='sha-256'>K1Njy3HZBThlo4moOD5gBGhn0U0oK7/CbfLlIUDi6o4=</hash><hash xmlns='urn:xmpp:hashes:1' algo='sha3-256'>+sDTQqBmX6iG/X3zjt06fjZMBBqL/723knFIyRf0sg8=</hash></c>".parse().unwrap();
242        let error = ECaps2::try_from(elem).unwrap_err();
243        let message = match error {
244            FromElementError::Invalid(Error::Other(string)) => string,
245            _ => panic!(),
246        };
247        assert_eq!(message, "Unknown child in ECaps2 element.");
248    }
249
250    #[test]
251    fn test_simple() {
252        let elem: Element = "<query xmlns='http://jabber.org/protocol/disco#info'><identity category='client' type='pc'/><feature var='http://jabber.org/protocol/disco#info'/></query>".parse().unwrap();
253        let disco = DiscoInfoResult::try_from(elem).unwrap();
254        let ecaps2 = compute_disco(&disco).unwrap();
255        assert_eq!(ecaps2.len(), 54);
256    }
257
258    #[test]
259    fn test_xep_ex1() {
260        let elem: Element = r#"<query xmlns="http://jabber.org/protocol/disco#info">
261  <identity category="client" name="BombusMod" type="mobile"/>
262  <feature var="http://jabber.org/protocol/si"/>
263  <feature var="http://jabber.org/protocol/bytestreams"/>
264  <feature var="http://jabber.org/protocol/chatstates"/>
265  <feature var="http://jabber.org/protocol/disco#info"/>
266  <feature var="http://jabber.org/protocol/disco#items"/>
267  <feature var="urn:xmpp:ping"/>
268  <feature var="jabber:iq:time"/>
269  <feature var="jabber:iq:privacy"/>
270  <feature var="jabber:iq:version"/>
271  <feature var="http://jabber.org/protocol/rosterx"/>
272  <feature var="urn:xmpp:time"/>
273  <feature var="jabber:x:oob"/>
274  <feature var="http://jabber.org/protocol/ibb"/>
275  <feature var="http://jabber.org/protocol/si/profile/file-transfer"/>
276  <feature var="urn:xmpp:receipts"/>
277  <feature var="jabber:iq:roster"/>
278  <feature var="jabber:iq:last"/>
279</query>
280"#
281        .parse()
282        .unwrap();
283        let expected = vec![
284            104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
285            114, 111, 116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109,
286            115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103,
287            47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116,
288            101, 115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
289            103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105,
290            110, 102, 111, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111,
291            114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35,
292            105, 116, 101, 109, 115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114,
293            46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, 98, 31, 104,
294            116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114,
295            111, 116, 111, 99, 111, 108, 47, 114, 111, 115, 116, 101, 114, 120, 31, 104, 116, 116,
296            112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,
297            111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
298            101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105,
299            47, 112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108, 101, 45, 116, 114, 97, 110,
300            115, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116,
301            31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 112, 114, 105, 118, 97, 99, 121, 31,
302            106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97,
303            98, 98, 101, 114, 58, 105, 113, 58, 116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114,
304            58, 105, 113, 58, 118, 101, 114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101, 114, 58,
305            120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110,
306            103, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116,
307            115, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109, 101, 31, 28, 99,
308            108, 105, 101, 110, 116, 31, 109, 111, 98, 105, 108, 101, 31, 31, 66, 111, 109, 98,
309            117, 115, 77, 111, 100, 31, 30, 28, 28,
310        ];
311        let disco = DiscoInfoResult::try_from(elem).unwrap();
312        let ecaps2 = compute_disco(&disco).unwrap();
313        assert_eq!(ecaps2.len(), 0x1d9);
314        assert_eq!(ecaps2, expected);
315
316        let sha_256 = hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap();
317        assert_eq!(
318            sha_256.hash,
319            Base64
320                .decode("kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8=")
321                .unwrap()
322        );
323        let sha3_256 = hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap();
324        assert_eq!(
325            sha3_256.hash,
326            Base64
327                .decode("79mdYAfU9rEdTOcWDO7UEAt6E56SUzk/g6TnqUeuD9Q=")
328                .unwrap()
329        );
330    }
331
332    #[test]
333    fn test_xep_ex2() {
334        let elem: Element = r#"<query xmlns="http://jabber.org/protocol/disco#info">
335  <identity category="client" name="Tkabber" type="pc" xml:lang="en"/>
336  <identity category="client" name="Ткаббер" type="pc" xml:lang="ru"/>
337  <feature var="games:board"/>
338  <feature var="http://jabber.org/protocol/activity"/>
339  <feature var="http://jabber.org/protocol/activity+notify"/>
340  <feature var="http://jabber.org/protocol/bytestreams"/>
341  <feature var="http://jabber.org/protocol/chatstates"/>
342  <feature var="http://jabber.org/protocol/commands"/>
343  <feature var="http://jabber.org/protocol/disco#info"/>
344  <feature var="http://jabber.org/protocol/disco#items"/>
345  <feature var="http://jabber.org/protocol/evil"/>
346  <feature var="http://jabber.org/protocol/feature-neg"/>
347  <feature var="http://jabber.org/protocol/geoloc"/>
348  <feature var="http://jabber.org/protocol/geoloc+notify"/>
349  <feature var="http://jabber.org/protocol/ibb"/>
350  <feature var="http://jabber.org/protocol/iqibb"/>
351  <feature var="http://jabber.org/protocol/mood"/>
352  <feature var="http://jabber.org/protocol/mood+notify"/>
353  <feature var="http://jabber.org/protocol/rosterx"/>
354  <feature var="http://jabber.org/protocol/si"/>
355  <feature var="http://jabber.org/protocol/si/profile/file-transfer"/>
356  <feature var="http://jabber.org/protocol/tune"/>
357  <feature var="http://www.facebook.com/xmpp/messages"/>
358  <feature var="http://www.xmpp.org/extensions/xep-0084.html#ns-metadata+notify"/>
359  <feature var="jabber:iq:avatar"/>
360  <feature var="jabber:iq:browse"/>
361  <feature var="jabber:iq:dtcp"/>
362  <feature var="jabber:iq:filexfer"/>
363  <feature var="jabber:iq:ibb"/>
364  <feature var="jabber:iq:inband"/>
365  <feature var="jabber:iq:jidlink"/>
366  <feature var="jabber:iq:last"/>
367  <feature var="jabber:iq:oob"/>
368  <feature var="jabber:iq:privacy"/>
369  <feature var="jabber:iq:roster"/>
370  <feature var="jabber:iq:time"/>
371  <feature var="jabber:iq:version"/>
372  <feature var="jabber:x:data"/>
373  <feature var="jabber:x:event"/>
374  <feature var="jabber:x:oob"/>
375  <feature var="urn:xmpp:avatar:metadata+notify"/>
376  <feature var="urn:xmpp:ping"/>
377  <feature var="urn:xmpp:receipts"/>
378  <feature var="urn:xmpp:time"/>
379  <x xmlns="jabber:x:data" type="result">
380    <field type="hidden" var="FORM_TYPE">
381      <value>urn:xmpp:dataforms:softwareinfo</value>
382    </field>
383    <field var="software">
384      <value>Tkabber</value>
385    </field>
386    <field var="software_version">
387      <value>0.11.1-svn-20111216-mod (Tcl/Tk 8.6b2)</value>
388    </field>
389    <field var="os">
390      <value>Windows</value>
391    </field>
392    <field var="os_version">
393      <value>XP</value>
394    </field>
395  </x>
396</query>
397"#
398        .parse()
399        .unwrap();
400        let expected = vec![
401            103, 97, 109, 101, 115, 58, 98, 111, 97, 114, 100, 31, 104, 116, 116, 112, 58, 47, 47,
402            106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
403            108, 47, 97, 99, 116, 105, 118, 105, 116, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106,
404            97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47,
405            97, 99, 116, 105, 118, 105, 116, 121, 43, 110, 111, 116, 105, 102, 121, 31, 104, 116,
406            116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111,
407            116, 111, 99, 111, 108, 47, 98, 121, 116, 101, 115, 116, 114, 101, 97, 109, 115, 31,
408            104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
409            114, 111, 116, 111, 99, 111, 108, 47, 99, 104, 97, 116, 115, 116, 97, 116, 101, 115,
410            31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
411            112, 114, 111, 116, 111, 99, 111, 108, 47, 99, 111, 109, 109, 97, 110, 100, 115, 31,
412            104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
413            114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 110, 102, 111,
414            31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
415            112, 114, 111, 116, 111, 99, 111, 108, 47, 100, 105, 115, 99, 111, 35, 105, 116, 101,
416            109, 115, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
417            103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 101, 118, 105, 108, 31, 104, 116,
418            116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111,
419            116, 111, 99, 111, 108, 47, 102, 101, 97, 116, 117, 114, 101, 45, 110, 101, 103, 31,
420            104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112,
421            114, 111, 116, 111, 99, 111, 108, 47, 103, 101, 111, 108, 111, 99, 31, 104, 116, 116,
422            112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116,
423            111, 99, 111, 108, 47, 103, 101, 111, 108, 111, 99, 43, 110, 111, 116, 105, 102, 121,
424            31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47,
425            112, 114, 111, 116, 111, 99, 111, 108, 47, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47,
426            47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
427            108, 47, 105, 113, 105, 98, 98, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98,
428            101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 109, 111,
429            111, 100, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
430            103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 109, 111, 111, 100, 43, 110, 111,
431            116, 105, 102, 121, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46,
432            111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 114, 111, 115, 116, 101,
433            114, 120, 31, 104, 116, 116, 112, 58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114,
434            103, 47, 112, 114, 111, 116, 111, 99, 111, 108, 47, 115, 105, 31, 104, 116, 116, 112,
435            58, 47, 47, 106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111,
436            99, 111, 108, 47, 115, 105, 47, 112, 114, 111, 102, 105, 108, 101, 47, 102, 105, 108,
437            101, 45, 116, 114, 97, 110, 115, 102, 101, 114, 31, 104, 116, 116, 112, 58, 47, 47,
438            106, 97, 98, 98, 101, 114, 46, 111, 114, 103, 47, 112, 114, 111, 116, 111, 99, 111,
439            108, 47, 116, 117, 110, 101, 31, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46,
440            102, 97, 99, 101, 98, 111, 111, 107, 46, 99, 111, 109, 47, 120, 109, 112, 112, 47, 109,
441            101, 115, 115, 97, 103, 101, 115, 31, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119,
442            46, 120, 109, 112, 112, 46, 111, 114, 103, 47, 101, 120, 116, 101, 110, 115, 105, 111,
443            110, 115, 47, 120, 101, 112, 45, 48, 48, 56, 52, 46, 104, 116, 109, 108, 35, 110, 115,
444            45, 109, 101, 116, 97, 100, 97, 116, 97, 43, 110, 111, 116, 105, 102, 121, 31, 106, 97,
445            98, 98, 101, 114, 58, 105, 113, 58, 97, 118, 97, 116, 97, 114, 31, 106, 97, 98, 98,
446            101, 114, 58, 105, 113, 58, 98, 114, 111, 119, 115, 101, 31, 106, 97, 98, 98, 101, 114,
447            58, 105, 113, 58, 100, 116, 99, 112, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58,
448            102, 105, 108, 101, 120, 102, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113,
449            58, 105, 98, 98, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 105, 110, 98, 97,
450            110, 100, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 106, 105, 100, 108, 105,
451            110, 107, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 108, 97, 115, 116, 31, 106,
452            97, 98, 98, 101, 114, 58, 105, 113, 58, 111, 111, 98, 31, 106, 97, 98, 98, 101, 114,
453            58, 105, 113, 58, 112, 114, 105, 118, 97, 99, 121, 31, 106, 97, 98, 98, 101, 114, 58,
454            105, 113, 58, 114, 111, 115, 116, 101, 114, 31, 106, 97, 98, 98, 101, 114, 58, 105,
455            113, 58, 116, 105, 109, 101, 31, 106, 97, 98, 98, 101, 114, 58, 105, 113, 58, 118, 101,
456            114, 115, 105, 111, 110, 31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 100, 97, 116, 97,
457            31, 106, 97, 98, 98, 101, 114, 58, 120, 58, 101, 118, 101, 110, 116, 31, 106, 97, 98,
458            98, 101, 114, 58, 120, 58, 111, 111, 98, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58,
459            97, 118, 97, 116, 97, 114, 58, 109, 101, 116, 97, 100, 97, 116, 97, 43, 110, 111, 116,
460            105, 102, 121, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 112, 105, 110, 103, 31,
461            117, 114, 110, 58, 120, 109, 112, 112, 58, 114, 101, 99, 101, 105, 112, 116, 115, 31,
462            117, 114, 110, 58, 120, 109, 112, 112, 58, 116, 105, 109, 101, 31, 28, 99, 108, 105,
463            101, 110, 116, 31, 112, 99, 31, 101, 110, 31, 84, 107, 97, 98, 98, 101, 114, 31, 30,
464            99, 108, 105, 101, 110, 116, 31, 112, 99, 31, 114, 117, 31, 208, 162, 208, 186, 208,
465            176, 208, 177, 208, 177, 208, 181, 209, 128, 31, 30, 28, 70, 79, 82, 77, 95, 84, 89,
466            80, 69, 31, 117, 114, 110, 58, 120, 109, 112, 112, 58, 100, 97, 116, 97, 102, 111, 114,
467            109, 115, 58, 115, 111, 102, 116, 119, 97, 114, 101, 105, 110, 102, 111, 31, 30, 111,
468            115, 31, 87, 105, 110, 100, 111, 119, 115, 31, 30, 111, 115, 95, 118, 101, 114, 115,
469            105, 111, 110, 31, 88, 80, 31, 30, 115, 111, 102, 116, 119, 97, 114, 101, 31, 84, 107,
470            97, 98, 98, 101, 114, 31, 30, 115, 111, 102, 116, 119, 97, 114, 101, 95, 118, 101, 114,
471            115, 105, 111, 110, 31, 48, 46, 49, 49, 46, 49, 45, 115, 118, 110, 45, 50, 48, 49, 49,
472            49, 50, 49, 54, 45, 109, 111, 100, 32, 40, 84, 99, 108, 47, 84, 107, 32, 56, 46, 54,
473            98, 50, 41, 31, 30, 29, 28,
474        ];
475        let disco = DiscoInfoResult::try_from(elem).unwrap();
476        let ecaps2 = compute_disco(&disco).unwrap();
477        assert_eq!(ecaps2.len(), 0x543);
478        assert_eq!(ecaps2, expected);
479
480        let sha_256 = hash_ecaps2(&ecaps2, Algo::Sha_256).unwrap();
481        assert_eq!(
482            sha_256.hash,
483            Base64
484                .decode("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=")
485                .unwrap()
486        );
487        let sha3_256 = hash_ecaps2(&ecaps2, Algo::Sha3_256).unwrap();
488        assert_eq!(
489            sha3_256.hash,
490            Base64
491                .decode("XpUJzLAc93258sMECZ3FJpebkzuyNXDzRNwQog8eycg=")
492                .unwrap()
493        );
494    }
495
496    #[test]
497    fn test_blake2b_512() {
498        let hash = hash_ecaps2("abc".as_bytes(), Algo::Blake2b_512).unwrap();
499        let known_hash: Vec<u8> = vec![
500            0xBA, 0x80, 0xA5, 0x3F, 0x98, 0x1C, 0x4D, 0x0D, 0x6A, 0x27, 0x97, 0xB6, 0x9F, 0x12,
501            0xF6, 0xE9, 0x4C, 0x21, 0x2F, 0x14, 0x68, 0x5A, 0xC4, 0xB7, 0x4B, 0x12, 0xBB, 0x6F,
502            0xDB, 0xFF, 0xA2, 0xD1, 0x7D, 0x87, 0xC5, 0x39, 0x2A, 0xAB, 0x79, 0x2D, 0xC2, 0x52,
503            0xD5, 0xDE, 0x45, 0x33, 0xCC, 0x95, 0x18, 0xD3, 0x8A, 0xA8, 0xDB, 0xF1, 0x92, 0x5A,
504            0xB9, 0x23, 0x86, 0xED, 0xD4, 0x00, 0x99, 0x23,
505        ];
506        assert_eq!(hash.hash, known_hash);
507    }
508}