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 core::convert::TryFrom;
10
11use crate::message::StunParseError;
12
13use super::{
14    Attribute, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
15    AttributeWriteExt, RawAttribute,
16};
17
18/// The Fingerprint [`Attribute`]
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct Fingerprint {
21    fingerprint: [u8; 4],
22}
23
24impl AttributeStaticType for Fingerprint {
25    const TYPE: AttributeType = AttributeType(0x8028);
26}
27
28impl Attribute for Fingerprint {
29    fn get_type(&self) -> AttributeType {
30        Self::TYPE
31    }
32
33    fn length(&self) -> u16 {
34        4
35    }
36}
37
38impl AttributeWrite for Fingerprint {
39    fn to_raw(&self) -> RawAttribute<'_> {
40        let buf = bytewise_xor!(4, self.fingerprint, Fingerprint::XOR_CONSTANT, 0);
41        RawAttribute::new(Fingerprint::TYPE, &buf).into_owned()
42    }
43
44    fn write_into_unchecked(&self, dest: &mut [u8]) {
45        let offset = self.write_header_unchecked(dest);
46        let buf = bytewise_xor!(4, self.fingerprint, Fingerprint::XOR_CONSTANT, 0);
47        dest[offset..offset + 4].copy_from_slice(&buf);
48    }
49}
50
51impl AttributeFromRaw<'_> for Fingerprint {
52    fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
53    where
54        Self: Sized,
55    {
56        Self::try_from(raw)
57    }
58}
59
60impl TryFrom<&RawAttribute<'_>> for Fingerprint {
61    type Error = StunParseError;
62
63    fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
64        raw.check_type_and_len(Self::TYPE, 4..=4)?;
65        // sized checked earlier
66        let boxed: [u8; 4] = (&*raw.value).try_into().unwrap();
67        let fingerprint = bytewise_xor!(4, boxed, Fingerprint::XOR_CONSTANT, 0);
68        Ok(Self { fingerprint })
69    }
70}
71
72impl Fingerprint {
73    const XOR_CONSTANT: [u8; 4] = [0x53, 0x54, 0x55, 0x4E];
74
75    /// Create a new Fingerprint [`Attribute`]
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// # use stun_types::attribute::*;
81    /// let value = [0;4];
82    /// let fingerprint = Fingerprint::new(value);
83    /// assert_eq!(fingerprint.fingerprint(), &value);
84    /// ```
85    pub fn new(fingerprint: [u8; 4]) -> Self {
86        Self { fingerprint }
87    }
88
89    /// Retrieve the fingerprint value
90    ///
91    /// # Examples
92    ///
93    /// ```
94    /// # use stun_types::attribute::*;
95    /// let value = [0;4];
96    /// let fingerprint = Fingerprint::new(value);
97    /// assert_eq!(fingerprint.fingerprint(), &value);
98    /// ```
99    pub fn fingerprint(&self) -> &[u8; 4] {
100        &self.fingerprint
101    }
102
103    /// Compute the fingerprint of a specified block of data as required by STUN
104    ///
105    /// # Examples
106    /// ```
107    /// # use stun_types::attribute::*;
108    /// let value = [99; 4];
109    /// assert_eq!(Fingerprint::compute(&[&value]), [216, 45, 250, 14]);
110    /// ```
111    pub fn compute(data: &[&[u8]]) -> [u8; 4] {
112        use crc::{Crc, CRC_32_ISO_HDLC};
113        const CRC_ALGO: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
114        let mut digest = CRC_ALGO.digest();
115        for data in data {
116            digest.update(data);
117        }
118        digest.finalize().to_be_bytes()
119    }
120}
121
122impl core::fmt::Display for Fingerprint {
123    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
124        write!(f, "{}: 0x", Self::TYPE)?;
125        for val in self.fingerprint.iter() {
126            write!(f, "{val:02x}")?;
127        }
128        Ok(())
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use crate::attribute::AttributeExt;
135
136    use super::*;
137    use alloc::vec;
138    use alloc::vec::Vec;
139    use byteorder::{BigEndian, ByteOrder};
140    use tracing::trace;
141
142    #[test]
143    fn fingerprint() {
144        let _log = crate::tests::test_init_log();
145        let val = [1; 4];
146        let attr = Fingerprint::new(val);
147        trace!("{attr}");
148        assert_eq!(attr.fingerprint(), &val);
149        assert_eq!(attr.length(), 4);
150    }
151
152    #[test]
153    fn fingerprint_raw() {
154        let _log = crate::tests::test_init_log();
155        let val = [1; 4];
156        let attr = Fingerprint::new(val);
157
158        let raw = RawAttribute::from(&attr);
159        trace!("{raw}");
160        assert_eq!(raw.get_type(), Fingerprint::TYPE);
161        let mapped2 = Fingerprint::try_from(&raw).unwrap();
162        assert_eq!(mapped2.fingerprint(), &val);
163    }
164
165    #[test]
166    fn fingerprint_raw_short() {
167        let _log = crate::tests::test_init_log();
168        let val = [1; 4];
169        let attr = Fingerprint::new(val);
170        let raw = RawAttribute::from(&attr);
171
172        // truncate by one byte
173        let mut data: Vec<_> = raw.clone().into();
174        let len = data.len();
175        BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
176        assert!(matches!(
177            Fingerprint::try_from(&RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()),
178            Err(StunParseError::Truncated {
179                expected: 4,
180                actual: 3
181            })
182        ));
183    }
184
185    #[test]
186    fn fingerprint_raw_wrong_type() {
187        let _log = crate::tests::test_init_log();
188        let val = [1; 4];
189        let attr = Fingerprint::new(val);
190
191        let raw = RawAttribute::from(&attr);
192
193        // provide incorrectly typed data
194        let mut data: Vec<_> = raw.clone().into();
195        BigEndian::write_u16(&mut data[0..2], 0);
196        assert!(matches!(
197            Fingerprint::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
198            Err(StunParseError::WrongAttributeImplementation)
199        ));
200    }
201
202    #[test]
203    fn fingerprint_write_into() {
204        let _log = crate::tests::test_init_log();
205        let val = [1; 4];
206        let attr = Fingerprint::new(val);
207
208        let raw = RawAttribute::from(&attr);
209        let mut dest = vec![0; raw.padded_len()];
210        attr.write_into(&mut dest).unwrap();
211        let raw = RawAttribute::from_bytes(&dest).unwrap();
212        let attr2 = Fingerprint::try_from(&raw).unwrap();
213        assert_eq!(attr2.fingerprint(), &val);
214    }
215
216    #[test]
217    #[should_panic(expected = "out of range")]
218    fn fingerprint_write_into_unchecked() {
219        let _log = crate::tests::test_init_log();
220        let val = [1; 4];
221        let attr = Fingerprint::new(val);
222
223        let raw = RawAttribute::from(&attr);
224        let mut dest = vec![0; raw.padded_len() - 1];
225        attr.write_into_unchecked(&mut dest);
226    }
227}