1use alloc::vec::Vec;
12use core::convert::TryFrom;
13
14use crate::message::{IntegrityKey, StunParseError, StunWriteError};
15
16use super::{
17 Attribute, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
18 AttributeWriteExt, RawAttribute,
19};
20
21#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct MessageIntegrity {
24 hmac: [u8; 20],
25}
26
27impl AttributeStaticType for MessageIntegrity {
28 const TYPE: AttributeType = AttributeType(0x0008);
29}
30
31impl Attribute for MessageIntegrity {
32 fn get_type(&self) -> AttributeType {
33 Self::TYPE
34 }
35
36 fn length(&self) -> u16 {
37 20
38 }
39}
40
41impl AttributeWrite for MessageIntegrity {
42 fn to_raw(&self) -> RawAttribute<'_> {
43 RawAttribute::new(MessageIntegrity::TYPE, &self.hmac)
44 }
45 fn write_into_unchecked(&self, dest: &mut [u8]) {
46 self.write_header_unchecked(dest);
47 dest[4..4 + self.hmac.len()].copy_from_slice(&self.hmac);
48 }
49}
50
51impl AttributeFromRaw<'_> for MessageIntegrity {
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 MessageIntegrity {
61 type Error = StunParseError;
62
63 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
64 raw.check_type_and_len(Self::TYPE, 20..=20)?;
65 let hmac: [u8; 20] = (&*raw.value).try_into().unwrap();
67 Ok(Self { hmac })
68 }
69}
70
71impl MessageIntegrity {
72 pub fn new(hmac: [u8; 20]) -> Self {
83 Self { hmac }
84 }
85
86 pub fn hmac(&self) -> &[u8; 20] {
97 &self.hmac
98 }
99
100 #[tracing::instrument(
117 name = "MessageIntegrity::compute",
118 level = "trace",
119 err,
120 skip(data, key)
121 )]
122 pub fn compute(data: &[&[u8]], key: &IntegrityKey) -> Result<[u8; 20], StunWriteError> {
123 Ok(key.compute_sha1(data).into_bytes().into())
124 }
125
126 #[tracing::instrument(
139 name = "MessageIntegrity::verify",
140 level = "debug",
141 skip(data, key, expected)
142 )]
143 pub fn verify(data: &[&[u8]], key: &IntegrityKey, expected: &[u8; 20]) -> bool {
144 key.verify_sha1(data, expected)
145 }
146}
147
148impl core::fmt::Display for MessageIntegrity {
149 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
150 write!(f, "{}: 0x", Self::TYPE)?;
151 for val in self.hmac.iter() {
152 write!(f, "{val:02x}")?;
153 }
154 Ok(())
155 }
156}
157
158#[derive(Debug, Clone, PartialEq, Eq)]
160pub struct MessageIntegritySha256 {
161 hmac: Vec<u8>,
162}
163
164impl AttributeStaticType for MessageIntegritySha256 {
165 const TYPE: AttributeType = AttributeType(0x001C);
166}
167
168impl Attribute for MessageIntegritySha256 {
169 fn get_type(&self) -> AttributeType {
170 Self::TYPE
171 }
172
173 fn length(&self) -> u16 {
174 self.hmac.len() as u16
175 }
176}
177
178impl AttributeWrite for MessageIntegritySha256 {
179 fn to_raw(&self) -> RawAttribute<'_> {
180 RawAttribute::new(MessageIntegritySha256::TYPE, &self.hmac)
181 }
182 fn write_into_unchecked(&self, dest: &mut [u8]) {
183 self.write_header_unchecked(dest);
184 dest[4..4 + self.hmac.len()].copy_from_slice(&self.hmac);
185 }
186}
187
188impl AttributeFromRaw<'_> for MessageIntegritySha256 {
189 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
190 where
191 Self: Sized,
192 {
193 Self::try_from(raw)
194 }
195}
196
197impl TryFrom<&RawAttribute<'_>> for MessageIntegritySha256 {
198 type Error = StunParseError;
199
200 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
201 raw.check_type_and_len(Self::TYPE, 16..=32)?;
202 if raw.value.len() % 4 != 0 {
203 return Err(StunParseError::InvalidAttributeData);
204 }
205 Ok(Self {
206 hmac: raw.value.to_vec(),
207 })
208 }
209}
210
211impl MessageIntegritySha256 {
212 pub fn new(hmac: &[u8]) -> Result<Self, StunWriteError> {
223 if hmac.len() < 16 {
224 return Err(StunWriteError::TooSmall {
225 expected: 16,
226 actual: hmac.len(),
227 });
228 }
229 if hmac.len() > 32 {
230 return Err(StunWriteError::TooLarge {
231 expected: 32,
232 actual: hmac.len(),
233 });
234 }
235 if hmac.len() % 4 != 0 {
236 return Err(StunWriteError::IntegrityFailed);
237 }
238 Ok(Self {
239 hmac: hmac.to_vec(),
240 })
241 }
242
243 pub fn hmac(&self) -> &[u8] {
254 &self.hmac
255 }
256
257 #[tracing::instrument(
274 name = "MessageIntegritySha256::compute",
275 level = "trace",
276 err,
277 skip(data, key)
278 )]
279 pub fn compute(data: &[&[u8]], key: &IntegrityKey) -> Result<[u8; 32], StunWriteError> {
280 Ok(key.compute_sha256(data))
281 }
282
283 #[tracing::instrument(
296 name = "MessageIntegritySha256::verify",
297 level = "debug",
298 skip(data, key, expected)
299 )]
300 pub fn verify(data: &[&[u8]], key: &IntegrityKey, expected: &[u8]) -> bool {
301 key.verify_sha256(data, expected)
302 }
303}
304
305impl core::fmt::Display for MessageIntegritySha256 {
306 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
307 write!(f, "{}: 0x", Self::TYPE)?;
308 for val in self.hmac.iter() {
309 write!(f, "{val:02x}")?;
310 }
311 Ok(())
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use crate::{
318 message::{LongTermKeyCredentials, MessageIntegrityCredentials, ShortTermCredentials},
319 prelude::AttributeExt,
320 };
321 use alloc::string::ToString;
322 use alloc::vec;
323
324 use super::*;
325 use byteorder::{BigEndian, ByteOrder};
326 use tracing::trace;
327
328 #[test]
329 fn message_integrity() {
330 let _log = crate::tests::test_init_log();
331 let val = [1; 20];
332 let attr = MessageIntegrity::new(val);
333 trace!("{attr}");
334 assert_eq!(attr.hmac(), &val);
335 assert_eq!(attr.length(), 20);
336 }
337
338 #[test]
339 fn message_integrity_raw() {
340 let _log = crate::tests::test_init_log();
341 let val = [1; 20];
342 let attr = MessageIntegrity::new(val);
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 }
349
350 #[test]
351 fn message_integrity_raw_short() {
352 let _log = crate::tests::test_init_log();
353 let val = [1; 20];
354 let attr = MessageIntegrity::new(val);
355 let raw = RawAttribute::from(&attr);
356 let mut data: Vec<_> = raw.clone().into();
358 let len = data.len();
359 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
360 assert!(matches!(
361 MessageIntegrity::try_from(
362 &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
363 ),
364 Err(StunParseError::Truncated {
365 expected: 20,
366 actual: 19
367 })
368 ));
369 }
370
371 #[test]
372 fn message_integrity_wrong_type() {
373 let _log = crate::tests::test_init_log();
374 let val = [1; 20];
375 let attr = MessageIntegrity::new(val);
376 let raw = RawAttribute::from(&attr);
377 let mut data: Vec<_> = raw.into();
379 BigEndian::write_u16(&mut data[0..2], 0);
380 assert!(matches!(
381 MessageIntegrity::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
382 Err(StunParseError::WrongAttributeImplementation)
383 ));
384 }
385
386 #[test]
387 fn message_integrity_write_into() {
388 let _log = crate::tests::test_init_log();
389 let val = [1; 20];
390 let attr = MessageIntegrity::new(val);
391 let raw = RawAttribute::from(&attr);
392
393 let mut out = vec![0; raw.padded_len()];
394 attr.write_into(&mut out).unwrap();
395 assert_eq!(out, raw.to_bytes());
396 }
397
398 #[test]
399 fn message_integrity_verify_fixed_value() {
400 let credentials = ShortTermCredentials::new("pass".to_string());
401 let key = MessageIntegrityCredentials::from(credentials)
402 .make_key(crate::message::IntegrityAlgorithm::Sha1);
403 let data = [10; 30];
404 let mut expected = [
405 92, 91, 148, 243, 28, 168, 16, 154, 137, 179, 250, 169, 153, 222, 37, 127, 210, 148,
406 222, 119,
407 ];
408 assert!(MessageIntegrity::verify(&[&data], &key, &expected),);
409 expected[0] = 0;
410 assert!(!MessageIntegrity::verify(&[&data], &key, &expected),);
411 }
412
413 #[test]
414 fn message_integrity_verify_key_long_wrong_type() {
415 let credentials = LongTermKeyCredentials::new(
416 "user".to_string(),
417 "pass".to_string(),
418 "realm".to_string(),
419 );
420 let key = MessageIntegrityCredentials::from(credentials)
421 .make_key(crate::message::IntegrityAlgorithm::Sha256);
422 let data = [10; 30];
423 let mut expected = [
424 6, 162, 255, 56, 215, 134, 145, 90, 154, 49, 51, 6, 22, 49, 202, 8, 176, 159, 24,
425 93,
426 ];
428 assert!(!MessageIntegrity::verify(&[&data], &key, &expected),);
429 expected[0] = 0;
430 assert!(!MessageIntegrity::verify(&[&data], &key, &expected),);
431 }
432
433 #[test]
434 fn message_integrity_sha256() {
435 let _log = crate::tests::test_init_log();
436 let val = [1; 32];
437 let attr = MessageIntegritySha256::new(&val).unwrap();
438 trace!("{attr}");
439 assert_eq!(attr.hmac(), &val);
440 assert_eq!(attr.length(), 32);
441 }
442
443 #[test]
444 fn message_integrity_sha256_raw() {
445 let _log = crate::tests::test_init_log();
446 let val = [1; 32];
447 let attr = MessageIntegritySha256::new(&val).unwrap();
448 let raw = RawAttribute::from(&attr);
449 trace!("{raw}");
450 assert_eq!(raw.get_type(), MessageIntegritySha256::TYPE);
451 let mapped2 = MessageIntegritySha256::try_from(&raw).unwrap();
452 assert_eq!(mapped2.hmac(), &val);
453 }
454
455 #[test]
456 fn message_integrity_sha256_raw_short() {
457 let _log = crate::tests::test_init_log();
458 let val = [1; 32];
459 let attr = MessageIntegritySha256::new(&val).unwrap();
460 let raw = RawAttribute::from(&attr);
461 let mut data: Vec<_> = raw.clone().into();
463 let len = data.len();
464 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
465 assert!(matches!(
466 MessageIntegritySha256::try_from(
467 &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
468 ),
469 Err(StunParseError::InvalidAttributeData)
470 ));
471 }
472
473 #[test]
474 fn message_integrity_sha256_raw_wrong_type() {
475 let _log = crate::tests::test_init_log();
476 let val = [1; 32];
477 let attr = MessageIntegritySha256::new(&val).unwrap();
478 let raw = RawAttribute::from(&attr);
479 let mut data: Vec<_> = raw.into();
481 BigEndian::write_u16(&mut data[0..2], 0);
482 assert!(matches!(
483 MessageIntegritySha256::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
484 Err(StunParseError::WrongAttributeImplementation)
485 ));
486 }
487
488 #[test]
489 fn message_integrity_sha256_write_into() {
490 let _log = crate::tests::test_init_log();
491 let val = [1; 32];
492 let attr = MessageIntegritySha256::new(&val).unwrap();
493 let raw = RawAttribute::from(&attr);
494
495 let mut out = vec![0; raw.padded_len()];
496 attr.write_into(&mut out).unwrap();
497 assert_eq!(out, raw.to_bytes());
498 }
499
500 #[test]
501 fn message_integrity_sha256_verify_fixed_value() {
502 let credentials = ShortTermCredentials::new("pass".to_string());
503 let key = MessageIntegrityCredentials::from(credentials)
504 .make_key(crate::message::IntegrityAlgorithm::Sha256);
505 let data = [10; 30];
506 let mut expected = [
507 16, 175, 53, 195, 18, 50, 153, 148, 7, 247, 27, 185, 195, 171, 22, 197, 22, 180, 244,
508 67, 190, 185, 71, 34, 150, 194, 108, 18, 75, 94, 221, 185,
509 ];
510 assert!(MessageIntegritySha256::verify(&[&data], &key, &expected),);
511 expected[0] = 0;
512 assert!(!MessageIntegritySha256::verify(&[&data], &key, &expected),);
513 }
514
515 #[test]
516 fn message_integrity_sha256_verify_key_long() {
517 let credentials = LongTermKeyCredentials::new(
518 "user".to_string(),
519 "pass".to_string(),
520 "realm".to_string(),
521 );
522 let key = MessageIntegrityCredentials::from(credentials)
523 .make_key(crate::message::IntegrityAlgorithm::Sha256);
524 let data = [10; 30];
525 let mut expected = [
526 6, 162, 255, 56, 215, 134, 145, 90, 154, 49, 51, 6, 22, 49, 202, 8, 176, 159, 24, 93,
527 161, 160, 22, 105, 211, 138, 184, 39, 172, 103, 186, 106,
528 ];
529 assert!(MessageIntegritySha256::verify(&[&data], &key, &expected),);
530 expected[0] = 0;
531 assert!(!MessageIntegritySha256::verify(&[&data], &key, &expected),);
532 }
533
534 #[test]
535 fn message_integrity_sha256_verify_key_long_wrong_type() {
536 let credentials = LongTermKeyCredentials::new(
537 "user".to_string(),
538 "pass".to_string(),
539 "realm".to_string(),
540 );
541 let key = MessageIntegrityCredentials::from(credentials)
542 .make_key(crate::message::IntegrityAlgorithm::Sha1);
543 let data = [10; 30];
544 let mut expected = [
545 6, 162, 255, 56, 215, 134, 145, 90, 154, 49, 51, 6, 22, 49, 202, 8, 176, 159, 24, 93,
546 161, 160, 22, 105, 211, 138, 184, 39, 172, 103, 186, 106,
547 ];
548 assert!(!MessageIntegritySha256::verify(&[&data], &key, &expected),);
549 expected[0] = 0;
550 assert!(!MessageIntegritySha256::verify(&[&data], &key, &expected),);
551 }
552
553 #[test]
554 fn message_integrity_sha256_new_too_large() {
555 let _log = crate::tests::test_init_log();
556 let val = [1; 33];
557 assert!(matches!(
558 MessageIntegritySha256::new(&val),
559 Err(StunWriteError::TooLarge {
560 expected: 32,
561 actual: 33
562 })
563 ));
564 }
565
566 #[test]
567 fn message_integrity_sha256_new_too_small() {
568 let _log = crate::tests::test_init_log();
569 let val = [1; 15];
570 assert!(matches!(
571 MessageIntegritySha256::new(&val),
572 Err(StunWriteError::TooSmall {
573 expected: 16,
574 actual: 15
575 })
576 ));
577 }
578
579 #[test]
580 fn message_integrity_sha256_new_not_multiple_of_4() {
581 let _log = crate::tests::test_init_log();
582 let val = [1; 19];
583 assert!(matches!(
584 MessageIntegritySha256::new(&val),
585 Err(StunWriteError::IntegrityFailed)
586 ));
587 }
588}