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