1use alloc::vec::Vec;
10use core::convert::TryFrom;
11
12use crate::message::{IntegrityKey, StunParseError, StunWriteError};
13
14use super::{
15 Attribute, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
16 AttributeWriteExt, RawAttribute,
17};
18
19#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct MessageIntegrity {
22 hmac: [u8; 20],
23}
24
25impl AttributeStaticType for MessageIntegrity {
26 const TYPE: AttributeType = AttributeType(0x0008);
27}
28
29impl Attribute for MessageIntegrity {
30 fn get_type(&self) -> AttributeType {
31 Self::TYPE
32 }
33
34 fn length(&self) -> u16 {
35 20
36 }
37}
38
39impl AttributeWrite for MessageIntegrity {
40 fn to_raw(&self) -> RawAttribute<'_> {
41 RawAttribute::new(MessageIntegrity::TYPE, &self.hmac)
42 }
43 fn write_into_unchecked(&self, dest: &mut [u8]) {
44 self.write_header_unchecked(dest);
45 dest[4..4 + self.hmac.len()].copy_from_slice(&self.hmac);
46 }
47}
48
49impl AttributeFromRaw<'_> for MessageIntegrity {
50 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
51 where
52 Self: Sized,
53 {
54 Self::try_from(raw)
55 }
56}
57
58impl TryFrom<&RawAttribute<'_>> for MessageIntegrity {
59 type Error = StunParseError;
60
61 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
62 raw.check_type_and_len(Self::TYPE, 20..=20)?;
63 let hmac: [u8; 20] = (&*raw.value).try_into().unwrap();
65 Ok(Self { hmac })
66 }
67}
68
69impl MessageIntegrity {
70 pub fn new(hmac: [u8; 20]) -> Self {
81 Self { hmac }
82 }
83
84 pub fn hmac(&self) -> &[u8; 20] {
95 &self.hmac
96 }
97
98 #[tracing::instrument(
115 name = "MessageIntegrity::compute",
116 level = "trace",
117 err,
118 skip(data, key)
119 )]
120 pub fn compute(data: &[&[u8]], key: &IntegrityKey) -> Result<[u8; 20], StunWriteError> {
121 Ok(key.compute_sha1(data).into_bytes().into())
122 }
123
124 #[tracing::instrument(
137 name = "MessageIntegrity::verify",
138 level = "debug",
139 skip(data, key, expected)
140 )]
141 pub fn verify(
142 data: &[&[u8]],
143 key: &IntegrityKey,
144 expected: &[u8; 20],
145 ) -> Result<(), StunParseError> {
146 if key.verify_sha1(data, expected) {
147 Ok(())
148 } else {
149 Err(StunParseError::IntegrityCheckFailed)
150 }
151 }
152}
153
154impl core::fmt::Display for MessageIntegrity {
155 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
156 write!(f, "{}: 0x", Self::TYPE)?;
157 for val in self.hmac.iter() {
158 write!(f, "{val:02x}")?;
159 }
160 Ok(())
161 }
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
166pub struct MessageIntegritySha256 {
167 hmac: Vec<u8>,
168}
169
170impl AttributeStaticType for MessageIntegritySha256 {
171 const TYPE: AttributeType = AttributeType(0x001C);
172}
173
174impl Attribute for MessageIntegritySha256 {
175 fn get_type(&self) -> AttributeType {
176 Self::TYPE
177 }
178
179 fn length(&self) -> u16 {
180 self.hmac.len() as u16
181 }
182}
183
184impl AttributeWrite for MessageIntegritySha256 {
185 fn to_raw(&self) -> RawAttribute<'_> {
186 RawAttribute::new(MessageIntegritySha256::TYPE, &self.hmac)
187 }
188 fn write_into_unchecked(&self, dest: &mut [u8]) {
189 self.write_header_unchecked(dest);
190 dest[4..4 + self.hmac.len()].copy_from_slice(&self.hmac);
191 }
192}
193
194impl AttributeFromRaw<'_> for MessageIntegritySha256 {
195 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
196 where
197 Self: Sized,
198 {
199 Self::try_from(raw)
200 }
201}
202
203impl TryFrom<&RawAttribute<'_>> for MessageIntegritySha256 {
204 type Error = StunParseError;
205
206 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
207 raw.check_type_and_len(Self::TYPE, 16..=32)?;
208 if raw.value.len() % 4 != 0 {
209 return Err(StunParseError::InvalidAttributeData);
210 }
211 Ok(Self {
212 hmac: raw.value.to_vec(),
213 })
214 }
215}
216
217impl MessageIntegritySha256 {
218 pub fn new(hmac: &[u8]) -> Result<Self, StunWriteError> {
229 if hmac.len() < 16 {
230 return Err(StunWriteError::TooSmall {
231 expected: 16,
232 actual: hmac.len(),
233 });
234 }
235 if hmac.len() > 32 {
236 return Err(StunWriteError::TooLarge {
237 expected: 32,
238 actual: hmac.len(),
239 });
240 }
241 if hmac.len() % 4 != 0 {
242 return Err(StunWriteError::IntegrityFailed);
243 }
244 Ok(Self {
245 hmac: hmac.to_vec(),
246 })
247 }
248
249 pub fn hmac(&self) -> &[u8] {
260 &self.hmac
261 }
262
263 #[tracing::instrument(
280 name = "MessageIntegritySha256::compute",
281 level = "trace",
282 err,
283 skip(data, key)
284 )]
285 pub fn compute(data: &[&[u8]], key: &IntegrityKey) -> Result<[u8; 32], StunWriteError> {
286 Ok(key.compute_sha256(data))
287 }
288
289 #[tracing::instrument(
302 name = "MessageIntegritySha256::verify",
303 level = "debug",
304 skip(data, key, expected)
305 )]
306 pub fn verify(
307 data: &[&[u8]],
308 key: &IntegrityKey,
309 expected: &[u8],
310 ) -> Result<(), StunParseError> {
311 if key.verify_sha256(data, expected) {
312 Ok(())
313 } else {
314 Err(StunParseError::IntegrityCheckFailed)
315 }
316 }
317}
318
319impl core::fmt::Display for MessageIntegritySha256 {
320 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
321 write!(f, "{}: 0x", Self::TYPE)?;
322 for val in self.hmac.iter() {
323 write!(f, "{val:02x}")?;
324 }
325 Ok(())
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332 use byteorder::{BigEndian, ByteOrder};
333 use tracing::trace;
334
335 #[test]
336 fn message_integrity() {
337 let _log = crate::tests::test_init_log();
338 let val = [1; 20];
339 let attr = MessageIntegrity::new(val);
340 trace!("{attr}");
341 assert_eq!(attr.hmac(), &val);
342 assert_eq!(attr.length(), 20);
343 let raw = RawAttribute::from(&attr);
344 trace!("{raw}");
345 assert_eq!(raw.get_type(), MessageIntegrity::TYPE);
346 let mapped2 = MessageIntegrity::try_from(&raw).unwrap();
347 assert_eq!(mapped2.hmac(), &val);
348 let mut data: Vec<_> = raw.clone().into();
350 let len = data.len();
351 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
352 assert!(matches!(
353 MessageIntegrity::try_from(
354 &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
355 ),
356 Err(StunParseError::Truncated {
357 expected: 20,
358 actual: 19
359 })
360 ));
361 let mut data: Vec<_> = raw.into();
363 BigEndian::write_u16(&mut data[0..2], 0);
364 assert!(matches!(
365 MessageIntegrity::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
366 Err(StunParseError::WrongAttributeImplementation)
367 ));
368 }
369
370 #[test]
371 fn message_integrity_sha256() {
372 let _log = crate::tests::test_init_log();
373 let val = [1; 32];
374 let attr = MessageIntegritySha256::new(&val).unwrap();
375 trace!("{attr}");
376 assert_eq!(attr.hmac(), &val);
377 assert_eq!(attr.length(), 32);
378 let raw = RawAttribute::from(&attr);
379 trace!("{raw}");
380 assert_eq!(raw.get_type(), MessageIntegritySha256::TYPE);
381 let mapped2 = MessageIntegritySha256::try_from(&raw).unwrap();
382 assert_eq!(mapped2.hmac(), &val);
383 let mut data: Vec<_> = raw.clone().into();
385 let len = data.len();
386 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
387 assert!(matches!(
388 MessageIntegritySha256::try_from(
389 &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
390 ),
391 Err(StunParseError::InvalidAttributeData)
392 ));
393 let mut data: Vec<_> = raw.into();
395 BigEndian::write_u16(&mut data[0..2], 0);
396 assert!(matches!(
397 MessageIntegritySha256::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
398 Err(StunParseError::WrongAttributeImplementation)
399 ));
400 }
401
402 #[test]
403 fn message_integrity_sha256_new_too_large() {
404 let _log = crate::tests::test_init_log();
405 let val = [1; 33];
406 assert!(matches!(
407 MessageIntegritySha256::new(&val),
408 Err(StunWriteError::TooLarge {
409 expected: 32,
410 actual: 33
411 })
412 ));
413 }
414
415 #[test]
416 fn message_integrity_sha256_new_too_small() {
417 let _log = crate::tests::test_init_log();
418 let val = [1; 15];
419 assert!(matches!(
420 MessageIntegritySha256::new(&val),
421 Err(StunWriteError::TooSmall {
422 expected: 16,
423 actual: 15
424 })
425 ));
426 }
427
428 #[test]
429 fn message_integrity_sha256_new_not_multiple_of_4() {
430 let _log = crate::tests::test_init_log();
431 let val = [1; 19];
432 assert!(matches!(
433 MessageIntegritySha256::new(&val),
434 Err(StunWriteError::IntegrityFailed)
435 ));
436 }
437}