stun_types/attribute/
fingerprint.rs1use core::convert::TryFrom;
10
11use crate::message::StunParseError;
12
13use super::{
14 Attribute, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
15 AttributeWriteExt, RawAttribute,
16};
17
18#[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 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 pub fn new(fingerprint: [u8; 4]) -> Self {
86 Self { fingerprint }
87 }
88
89 pub fn fingerprint(&self) -> &[u8; 4] {
100 &self.fingerprint
101 }
102
103 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 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 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}