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