Skip to main content

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