stun_types/attribute/
fingerprint.rs

1// Copyright (C) 2020 Matthew Waters <matthew@centricular.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::convert::TryFrom;
10
11use crate::message::StunParseError;
12
13use super::{
14    Attribute, AttributeStaticType, AttributeType, AttributeWrite, AttributeWriteExt, RawAttribute,
15};
16
17/// The Fingerprint [`Attribute`]
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Fingerprint {
20    fingerprint: [u8; 4],
21}
22
23impl AttributeStaticType for Fingerprint {
24    const TYPE: AttributeType = AttributeType(0x8028);
25}
26
27impl Attribute for Fingerprint {
28    fn get_type(&self) -> AttributeType {
29        Self::TYPE
30    }
31
32    fn length(&self) -> u16 {
33        4
34    }
35}
36
37impl AttributeWrite for Fingerprint {
38    fn to_raw(&self) -> RawAttribute {
39        let buf = bytewise_xor!(4, self.fingerprint, Fingerprint::XOR_CONSTANT, 0);
40        RawAttribute::new(Fingerprint::TYPE, &buf).into_owned()
41    }
42
43    fn write_into_unchecked(&self, dest: &mut [u8]) {
44        let offset = self.write_header_unchecked(dest);
45        let buf = bytewise_xor!(4, self.fingerprint, Fingerprint::XOR_CONSTANT, 0);
46        dest[offset..offset + 4].copy_from_slice(&buf);
47    }
48}
49impl<'a> TryFrom<&RawAttribute<'a>> for Fingerprint {
50    type Error = StunParseError;
51
52    fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
53        raw.check_type_and_len(Self::TYPE, 4..=4)?;
54        // sized checked earlier
55        let boxed: [u8; 4] = (&*raw.value).try_into().unwrap();
56        let fingerprint = bytewise_xor!(4, boxed, Fingerprint::XOR_CONSTANT, 0);
57        Ok(Self { fingerprint })
58    }
59}
60
61impl Fingerprint {
62    const XOR_CONSTANT: [u8; 4] = [0x53, 0x54, 0x55, 0x4E];
63
64    /// Create a new Fingerprint [`Attribute`]
65    ///
66    /// # Examples
67    ///
68    /// ```
69    /// # use stun_types::attribute::*;
70    /// let value = [0;4];
71    /// let fingerprint = Fingerprint::new(value);
72    /// assert_eq!(fingerprint.fingerprint(), &value);
73    /// ```
74    pub fn new(fingerprint: [u8; 4]) -> Self {
75        Self { fingerprint }
76    }
77
78    /// Retrieve the fingerprint value
79    ///
80    /// # Examples
81    ///
82    /// ```
83    /// # use stun_types::attribute::*;
84    /// let value = [0;4];
85    /// let fingerprint = Fingerprint::new(value);
86    /// assert_eq!(fingerprint.fingerprint(), &value);
87    /// ```
88    pub fn fingerprint(&self) -> &[u8; 4] {
89        &self.fingerprint
90    }
91
92    /// Compute the fingerprint of a specified block of data as required by STUN
93    ///
94    /// # Examples
95    /// ```
96    /// # use stun_types::attribute::*;
97    /// let value = [99;4];
98    /// assert_eq!(Fingerprint::compute(&value), [216, 45, 250, 14]);
99    /// ```
100    pub fn compute(data: &[u8]) -> [u8; 4] {
101        use crc::{Crc, CRC_32_ISO_HDLC};
102        const CRC_ALGO: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
103        CRC_ALGO.checksum(data).to_be_bytes()
104    }
105}
106
107impl std::fmt::Display for Fingerprint {
108    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        write!(f, "{}: 0x", Self::TYPE)?;
110        for val in self.fingerprint.iter() {
111            write!(f, "{:02x}", val)?;
112        }
113        Ok(())
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use crate::prelude::AttributeExt;
120
121    use super::*;
122    use byteorder::{BigEndian, ByteOrder};
123    use tracing::trace;
124
125    #[test]
126    fn fingerprint() {
127        let _log = crate::tests::test_init_log();
128        let val = [1; 4];
129        let attr = Fingerprint::new(val);
130        trace!("{attr}");
131        assert_eq!(attr.fingerprint(), &val);
132        assert_eq!(attr.length(), 4);
133        let raw = RawAttribute::from(&attr);
134        trace!("{raw}");
135        assert_eq!(raw.get_type(), Fingerprint::TYPE);
136        let mapped2 = Fingerprint::try_from(&raw).unwrap();
137        assert_eq!(mapped2.fingerprint(), &val);
138        // truncate by one byte
139        let mut data: Vec<_> = raw.clone().into();
140        let len = data.len();
141        BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
142        assert!(matches!(
143            Fingerprint::try_from(&RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()),
144            Err(StunParseError::Truncated {
145                expected: 4,
146                actual: 3
147            })
148        ));
149        // provide incorrectly typed data
150        let mut data: Vec<_> = raw.clone().into();
151        BigEndian::write_u16(&mut data[0..2], 0);
152        assert!(matches!(
153            Fingerprint::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
154            Err(StunParseError::WrongAttributeImplementation)
155        ));
156
157        let mut dest = vec![0; raw.padded_len()];
158        attr.write_into(&mut dest).unwrap();
159        let raw = RawAttribute::from_bytes(&dest).unwrap();
160        let attr2 = Fingerprint::try_from(&raw).unwrap();
161        assert_eq!(attr2.fingerprint(), &val);
162    }
163}