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