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
9use 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/// The MessageIntegrity [`Attribute`]
20#[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        // sized checked earlier
64        let hmac: [u8; 20] = (&*raw.value).try_into().unwrap();
65        Ok(Self { hmac })
66    }
67}
68
69impl MessageIntegrity {
70    /// Create a new MessageIntegrity [`Attribute`]
71    ///
72    /// # Examples
73    ///
74    /// ```
75    /// # use stun_types::attribute::*;
76    /// let hmac = [0;20];
77    /// let integrity = MessageIntegrity::new(hmac);
78    /// assert_eq!(integrity.hmac(), &hmac);
79    /// ```
80    pub fn new(hmac: [u8; 20]) -> Self {
81        Self { hmac }
82    }
83
84    /// Retrieve the value of the hmac
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// # use stun_types::attribute::*;
90    /// let hmac = [0; 20];
91    /// let integrity = MessageIntegrity::new(hmac);
92    /// assert_eq!(integrity.hmac(), &hmac);
93    /// ```
94    pub fn hmac(&self) -> &[u8; 20] {
95        &self.hmac
96    }
97
98    /// Compute the Message Integrity value of a chunk of data using a key
99    ///
100    /// Note: use `MessageIntegrity::verify` for the actual verification to ensure constant time
101    /// checks of the values to defeat certain types of timing attacks.
102    ///
103    /// # Examples
104    /// ```
105    /// # use stun_types::attribute::*;
106    /// # use stun_types::message::*;
107    /// let credentials = ShortTermCredentials::new("pass".to_owned());
108    /// let key = MessageIntegrityCredentials::from(credentials).make_key();
109    /// let data = [10; 30];
110    /// let expected = [92, 91, 148, 243, 28, 168, 16, 154, 137, 179, 250, 169, 153, 222, 37, 127, 210, 148, 222, 119];
111    /// let integrity = MessageIntegrity::compute(&[&data], &key).unwrap();
112    /// assert_eq!(integrity, expected);
113    /// ```
114    #[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    /// Compute the Message Integrity value of a chunk of data using a key
125    ///
126    /// # Examples
127    /// ```
128    /// # use stun_types::attribute::*;
129    /// # use stun_types::message::*;
130    /// let credentials = ShortTermCredentials::new("pass".to_owned());
131    /// let key = MessageIntegrityCredentials::from(credentials).make_key();
132    /// let data = [10; 30];
133    /// let expected = [92, 91, 148, 243, 28, 168, 16, 154, 137, 179, 250, 169, 153, 222, 37, 127, 210, 148, 222, 119];
134    /// assert_eq!(MessageIntegrity::verify(&[&data], &key, &expected).unwrap(), ());
135    /// ```
136    #[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/// The MessageIntegritySha256 [`Attribute`]
165#[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    /// Create a new MessageIntegritySha256 [`Attribute`]
219    ///
220    /// # Examples
221    ///
222    /// ```
223    /// # use stun_types::attribute::*;
224    /// let hmac = [0;20];
225    /// let integrity = MessageIntegritySha256::new(&hmac).unwrap();
226    /// assert_eq!(integrity.hmac(), &hmac);
227    /// ```
228    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    /// Retrieve the value of the hmac
250    ///
251    /// # Examples
252    ///
253    /// ```
254    /// # use stun_types::attribute::*;
255    /// let hmac = [0; 20];
256    /// let integrity = MessageIntegritySha256::new(&hmac).unwrap();
257    /// assert_eq!(integrity.hmac(), &hmac);
258    /// ```
259    pub fn hmac(&self) -> &[u8] {
260        &self.hmac
261    }
262
263    /// Compute the Message Integrity value of a chunk of data using a key
264    ///
265    /// Note: use `MessageIntegritySha256::verify` for the actual verification to ensure constant time
266    /// checks of the values to defeat certain types of timing attacks.
267    ///
268    /// # Examples
269    /// ```
270    /// # use stun_types::attribute::*;
271    /// # use stun_types::message::*;
272    /// let credentials = ShortTermCredentials::new("pass".to_owned());
273    /// let key = MessageIntegrityCredentials::from(credentials).make_key();
274    /// let data = [10; 30];
275    /// 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];
276    /// let integrity = MessageIntegritySha256::compute(&[&data], &key).unwrap();
277    /// assert_eq!(integrity, expected);
278    /// ```
279    #[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    /// Compute the Message Integrity value of a chunk of data using a key
290    ///
291    /// # Examples
292    /// ```
293    /// # use stun_types::attribute::*;
294    /// # use stun_types::message::*;
295    /// let credentials = ShortTermCredentials::new("pass".to_owned());
296    /// let key = MessageIntegrityCredentials::from(credentials).make_key();
297    /// let data = [10; 30];
298    /// 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];
299    /// assert_eq!(MessageIntegritySha256::verify(&[&data], &key, &expected).unwrap(), ());
300    /// ```
301    #[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        // truncate by one byte
371        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        // provide incorrectly typed data
392        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        // truncate by one byte
461        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        // provide incorrectly typed data
479        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}