Skip to main content

stun_types/attribute/
integrity.rs

1// Copyright (C) 2020 Matthew Waters <matthew@centricular.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8//
9// SPDX-License-Identifier: MIT OR Apache-2.0
10
11use 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/// The MessageIntegrity [`Attribute`]
22#[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        // sized checked earlier
66        let hmac: [u8; 20] = (&*raw.value).try_into().unwrap();
67        Ok(Self { hmac })
68    }
69}
70
71impl MessageIntegrity {
72    /// Create a new MessageIntegrity [`Attribute`]
73    ///
74    /// # Examples
75    ///
76    /// ```
77    /// # use stun_types::attribute::*;
78    /// let hmac = [0;20];
79    /// let integrity = MessageIntegrity::new(hmac);
80    /// assert_eq!(integrity.hmac(), &hmac);
81    /// ```
82    pub fn new(hmac: [u8; 20]) -> Self {
83        Self { hmac }
84    }
85
86    /// Retrieve the value of the hmac
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// # use stun_types::attribute::*;
92    /// let hmac = [0; 20];
93    /// let integrity = MessageIntegrity::new(hmac);
94    /// assert_eq!(integrity.hmac(), &hmac);
95    /// ```
96    pub fn hmac(&self) -> &[u8; 20] {
97        &self.hmac
98    }
99
100    /// Compute the Message Integrity value of a chunk of data using a key
101    ///
102    /// Note: use `MessageIntegrity::verify` for the actual verification to ensure constant time
103    /// checks of the values to defeat certain types of timing attacks.
104    ///
105    /// # Examples
106    /// ```
107    /// # use stun_types::attribute::*;
108    /// # use stun_types::message::*;
109    /// let credentials = ShortTermCredentials::new("pass".to_owned());
110    /// let key = MessageIntegrityCredentials::from(credentials).make_key(IntegrityAlgorithm::Sha1);
111    /// let data = [10; 30];
112    /// let expected = [92, 91, 148, 243, 28, 168, 16, 154, 137, 179, 250, 169, 153, 222, 37, 127, 210, 148, 222, 119];
113    /// let integrity = MessageIntegrity::compute(&[&data], &key).unwrap();
114    /// assert_eq!(integrity, expected);
115    /// ```
116    #[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    /// Compute the Message Integrity value of a chunk of data using a key
127    ///
128    /// # Examples
129    /// ```
130    /// # use stun_types::attribute::*;
131    /// # use stun_types::message::*;
132    /// let credentials = ShortTermCredentials::new("pass".to_owned());
133    /// let key = MessageIntegrityCredentials::from(credentials).make_key(IntegrityAlgorithm::Sha1);
134    /// let data = [10; 30];
135    /// let expected = [92, 91, 148, 243, 28, 168, 16, 154, 137, 179, 250, 169, 153, 222, 37, 127, 210, 148, 222, 119];
136    /// assert!(MessageIntegrity::verify(&[&data], &key, &expected));
137    /// ```
138    #[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/// The MessageIntegritySha256 [`Attribute`]
159#[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    /// Create a new MessageIntegritySha256 [`Attribute`]
213    ///
214    /// # Examples
215    ///
216    /// ```
217    /// # use stun_types::attribute::*;
218    /// let hmac = [0;20];
219    /// let integrity = MessageIntegritySha256::new(&hmac).unwrap();
220    /// assert_eq!(integrity.hmac(), &hmac);
221    /// ```
222    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    /// Retrieve the value of the hmac
244    ///
245    /// # Examples
246    ///
247    /// ```
248    /// # use stun_types::attribute::*;
249    /// let hmac = [0; 20];
250    /// let integrity = MessageIntegritySha256::new(&hmac).unwrap();
251    /// assert_eq!(integrity.hmac(), &hmac);
252    /// ```
253    pub fn hmac(&self) -> &[u8] {
254        &self.hmac
255    }
256
257    /// Compute the Message Integrity value of a chunk of data using a key
258    ///
259    /// Note: use `MessageIntegritySha256::verify` for the actual verification to ensure constant time
260    /// checks of the values to defeat certain types of timing attacks.
261    ///
262    /// # Examples
263    /// ```
264    /// # use stun_types::attribute::*;
265    /// # use stun_types::message::*;
266    /// let credentials = ShortTermCredentials::new("pass".to_owned());
267    /// let key = MessageIntegrityCredentials::from(credentials).make_key(IntegrityAlgorithm::Sha256);
268    /// let data = [10; 30];
269    /// let expected = [16, 175, 53, 195, 18, 50, 153, 148, 7, 247, 27, 185, 195, 171, 22, 197, 22, 180, 244, 67, 190, 185, 71, 34, 150, 194, 108, 18, 75, 94, 221, 185];
270    /// let integrity = MessageIntegritySha256::compute(&[&data], &key).unwrap();
271    /// assert_eq!(integrity, expected);
272    /// ```
273    #[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    /// Compute the Message Integrity value of a chunk of data using a key
284    ///
285    /// # Examples
286    /// ```
287    /// # use stun_types::attribute::*;
288    /// # use stun_types::message::*;
289    /// let credentials = ShortTermCredentials::new("pass".to_owned());
290    /// let key = MessageIntegrityCredentials::from(credentials).make_key(IntegrityAlgorithm::Sha256);
291    /// let data = [10; 30];
292    /// let expected = [16, 175, 53, 195, 18, 50, 153, 148, 7, 247, 27, 185, 195, 171, 22, 197, 22, 180, 244, 67, 190, 185, 71, 34, 150, 194, 108, 18, 75, 94, 221, 185];
293    /// assert!(MessageIntegritySha256::verify(&[&data], &key, &expected));
294    /// ```
295    #[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        // truncate by one byte
357        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        // provide incorrectly typed data
378        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            //161, 160, 22, 105, 211, 138, 184, 39, 172, 103, 186, 106,
427        ];
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        // truncate by one byte
462        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        // provide incorrectly typed data
480        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}