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 crate::{
332 message::{MessageIntegrityCredentials, ShortTermCredentials},
333 prelude::AttributeExt,
334 };
335 use alloc::string::ToString;
336 use alloc::vec;
337
338 use super::*;
339 use byteorder::{BigEndian, ByteOrder};
340 use tracing::trace;
341
342 #[test]
343 fn message_integrity() {
344 let _log = crate::tests::test_init_log();
345 let val = [1; 20];
346 let attr = MessageIntegrity::new(val);
347 trace!("{attr}");
348 assert_eq!(attr.hmac(), &val);
349 assert_eq!(attr.length(), 20);
350 }
351
352 #[test]
353 fn message_integrity_raw() {
354 let _log = crate::tests::test_init_log();
355 let val = [1; 20];
356 let attr = MessageIntegrity::new(val);
357 let raw = RawAttribute::from(&attr);
358 trace!("{raw}");
359 assert_eq!(raw.get_type(), MessageIntegrity::TYPE);
360 let mapped2 = MessageIntegrity::try_from(&raw).unwrap();
361 assert_eq!(mapped2.hmac(), &val);
362 }
363
364 #[test]
365 fn message_integrity_raw_short() {
366 let _log = crate::tests::test_init_log();
367 let val = [1; 20];
368 let attr = MessageIntegrity::new(val);
369 let raw = RawAttribute::from(&attr);
370 let mut data: Vec<_> = raw.clone().into();
372 let len = data.len();
373 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
374 assert!(matches!(
375 MessageIntegrity::try_from(
376 &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
377 ),
378 Err(StunParseError::Truncated {
379 expected: 20,
380 actual: 19
381 })
382 ));
383 }
384
385 #[test]
386 fn message_integrity_wrong_type() {
387 let _log = crate::tests::test_init_log();
388 let val = [1; 20];
389 let attr = MessageIntegrity::new(val);
390 let raw = RawAttribute::from(&attr);
391 let mut data: Vec<_> = raw.into();
393 BigEndian::write_u16(&mut data[0..2], 0);
394 assert!(matches!(
395 MessageIntegrity::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
396 Err(StunParseError::WrongAttributeImplementation)
397 ));
398 }
399
400 #[test]
401 fn message_integrity_write_into() {
402 let _log = crate::tests::test_init_log();
403 let val = [1; 20];
404 let attr = MessageIntegrity::new(val);
405 let raw = RawAttribute::from(&attr);
406
407 let mut out = vec![0; raw.padded_len()];
408 attr.write_into(&mut out).unwrap();
409 assert_eq!(out, raw.to_bytes());
410 }
411
412 #[test]
413 fn message_integrity_verify_fixed_value() {
414 let credentials = ShortTermCredentials::new("pass".to_string());
415 let key = MessageIntegrityCredentials::from(credentials).make_key();
416 let data = [10; 30];
417 let mut expected = [
418 92, 91, 148, 243, 28, 168, 16, 154, 137, 179, 250, 169, 153, 222, 37, 127, 210, 148,
419 222, 119,
420 ];
421 assert!(matches!(
422 MessageIntegrity::verify(&[&data], &key, &expected),
423 Ok(())
424 ));
425 expected[0] = 0;
426 assert!(matches!(
427 MessageIntegrity::verify(&[&data], &key, &expected),
428 Err(StunParseError::IntegrityCheckFailed)
429 ));
430 }
431
432 #[test]
433 fn message_integrity_sha256() {
434 let _log = crate::tests::test_init_log();
435 let val = [1; 32];
436 let attr = MessageIntegritySha256::new(&val).unwrap();
437 trace!("{attr}");
438 assert_eq!(attr.hmac(), &val);
439 assert_eq!(attr.length(), 32);
440 }
441
442 #[test]
443 fn message_integrity_sha256_raw() {
444 let _log = crate::tests::test_init_log();
445 let val = [1; 32];
446 let attr = MessageIntegritySha256::new(&val).unwrap();
447 let raw = RawAttribute::from(&attr);
448 trace!("{raw}");
449 assert_eq!(raw.get_type(), MessageIntegritySha256::TYPE);
450 let mapped2 = MessageIntegritySha256::try_from(&raw).unwrap();
451 assert_eq!(mapped2.hmac(), &val);
452 }
453
454 #[test]
455 fn message_integrity_sha256_raw_short() {
456 let _log = crate::tests::test_init_log();
457 let val = [1; 32];
458 let attr = MessageIntegritySha256::new(&val).unwrap();
459 let raw = RawAttribute::from(&attr);
460 let mut data: Vec<_> = raw.clone().into();
462 let len = data.len();
463 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
464 assert!(matches!(
465 MessageIntegritySha256::try_from(
466 &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
467 ),
468 Err(StunParseError::InvalidAttributeData)
469 ));
470 }
471
472 #[test]
473 fn message_integrity_sha256_raw_wrong_type() {
474 let _log = crate::tests::test_init_log();
475 let val = [1; 32];
476 let attr = MessageIntegritySha256::new(&val).unwrap();
477 let raw = RawAttribute::from(&attr);
478 let mut data: Vec<_> = raw.into();
480 BigEndian::write_u16(&mut data[0..2], 0);
481 assert!(matches!(
482 MessageIntegritySha256::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
483 Err(StunParseError::WrongAttributeImplementation)
484 ));
485 }
486
487 #[test]
488 fn message_integrity_sha256_write_into() {
489 let _log = crate::tests::test_init_log();
490 let val = [1; 32];
491 let attr = MessageIntegritySha256::new(&val).unwrap();
492 let raw = RawAttribute::from(&attr);
493
494 let mut out = vec![0; raw.padded_len()];
495 attr.write_into(&mut out).unwrap();
496 assert_eq!(out, raw.to_bytes());
497 }
498
499 #[test]
500 fn message_integrity_sha256_verify_fixed_value() {
501 let credentials = ShortTermCredentials::new("pass".to_string());
502 let key = MessageIntegrityCredentials::from(credentials).make_key();
503 let data = [10; 30];
504 let mut expected = [
505 16, 175, 53, 195, 18, 50, 153, 148, 7, 247, 27, 185, 195, 171, 22, 197, 22, 180, 244,
506 67, 190, 185, 71, 34, 150, 194, 108, 18, 75, 94, 221, 185,
507 ];
508 assert!(matches!(
509 MessageIntegritySha256::verify(&[&data], &key, &expected),
510 Ok(())
511 ));
512 expected[0] = 0;
513 assert!(matches!(
514 MessageIntegritySha256::verify(&[&data], &key, &expected),
515 Err(StunParseError::IntegrityCheckFailed)
516 ));
517 }
518
519 #[test]
520 fn message_integrity_sha256_new_too_large() {
521 let _log = crate::tests::test_init_log();
522 let val = [1; 33];
523 assert!(matches!(
524 MessageIntegritySha256::new(&val),
525 Err(StunWriteError::TooLarge {
526 expected: 32,
527 actual: 33
528 })
529 ));
530 }
531
532 #[test]
533 fn message_integrity_sha256_new_too_small() {
534 let _log = crate::tests::test_init_log();
535 let val = [1; 15];
536 assert!(matches!(
537 MessageIntegritySha256::new(&val),
538 Err(StunWriteError::TooSmall {
539 expected: 16,
540 actual: 15
541 })
542 ));
543 }
544
545 #[test]
546 fn message_integrity_sha256_new_not_multiple_of_4() {
547 let _log = crate::tests::test_init_log();
548 let val = [1; 19];
549 assert!(matches!(
550 MessageIntegritySha256::new(&val),
551 Err(StunWriteError::IntegrityFailed)
552 ));
553 }
554}