1use alloc::vec::Vec;
10use core::convert::TryFrom;
11
12use crate::message::{StunParseError, StunWriteError};
13
14use super::{
15 Attribute, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
16 AttributeWriteExt, RawAttribute,
17};
18
19use tracing::{debug, error};
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(
115 name = "MessageIntegrity::compute",
116 level = "trace",
117 err,
118 skip(data, key)
119 )]
120 pub fn compute(data: &[u8], key: &[u8]) -> Result<[u8; 20], StunWriteError> {
121 use hmac::{Hmac, Mac};
122 let mut hmac =
123 Hmac::<sha1::Sha1>::new_from_slice(key).map_err(|_| StunWriteError::IntegrityFailed)?;
124 hmac.update(data);
125 Ok(hmac.finalize().into_bytes().into())
126 }
127
128 #[tracing::instrument(
139 name = "MessageIntegrity::verify",
140 level = "debug",
141 skip(data, key, expected)
142 )]
143 pub fn verify(data: &[u8], key: &[u8], expected: &[u8; 20]) -> Result<(), StunParseError> {
144 use hmac::{Hmac, Mac};
145 let mut hmac = Hmac::<sha1::Sha1>::new_from_slice(key).map_err(|_| {
146 error!("failed to create hmac from key data");
147 StunParseError::InvalidAttributeData
148 })?;
149 hmac.update(data);
150 hmac.verify_slice(expected).map_err(|_| {
151 debug!("integrity check failed");
152 StunParseError::IntegrityCheckFailed
153 })
154 }
155}
156
157impl core::fmt::Display for MessageIntegrity {
158 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
159 write!(f, "{}: 0x", Self::TYPE)?;
160 for val in self.hmac.iter() {
161 write!(f, "{val:02x}")?;
162 }
163 Ok(())
164 }
165}
166
167#[derive(Debug, Clone, PartialEq, Eq)]
169pub struct MessageIntegritySha256 {
170 hmac: Vec<u8>,
171}
172
173impl AttributeStaticType for MessageIntegritySha256 {
174 const TYPE: AttributeType = AttributeType(0x001C);
175}
176
177impl Attribute for MessageIntegritySha256 {
178 fn get_type(&self) -> AttributeType {
179 Self::TYPE
180 }
181
182 fn length(&self) -> u16 {
183 self.hmac.len() as u16
184 }
185}
186
187impl AttributeWrite for MessageIntegritySha256 {
188 fn to_raw(&self) -> RawAttribute<'_> {
189 RawAttribute::new(MessageIntegritySha256::TYPE, &self.hmac)
190 }
191 fn write_into_unchecked(&self, dest: &mut [u8]) {
192 self.write_header_unchecked(dest);
193 dest[4..4 + self.hmac.len()].copy_from_slice(&self.hmac);
194 }
195}
196
197impl AttributeFromRaw<'_> for MessageIntegritySha256 {
198 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
199 where
200 Self: Sized,
201 {
202 Self::try_from(raw)
203 }
204}
205
206impl TryFrom<&RawAttribute<'_>> for MessageIntegritySha256 {
207 type Error = StunParseError;
208
209 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
210 raw.check_type_and_len(Self::TYPE, 16..=32)?;
211 if raw.value.len() % 4 != 0 {
212 return Err(StunParseError::InvalidAttributeData);
213 }
214 Ok(Self {
215 hmac: raw.value.to_vec(),
216 })
217 }
218}
219
220impl MessageIntegritySha256 {
221 pub fn new(hmac: &[u8]) -> Result<Self, StunWriteError> {
232 if hmac.len() < 16 {
233 return Err(StunWriteError::TooSmall {
234 expected: 16,
235 actual: hmac.len(),
236 });
237 }
238 if hmac.len() > 32 {
239 return Err(StunWriteError::TooLarge {
240 expected: 32,
241 actual: hmac.len(),
242 });
243 }
244 if hmac.len() % 4 != 0 {
245 return Err(StunWriteError::IntegrityFailed);
246 }
247 Ok(Self {
248 hmac: hmac.to_vec(),
249 })
250 }
251
252 pub fn hmac(&self) -> &[u8] {
263 &self.hmac
264 }
265
266 #[tracing::instrument(
281 name = "MessageIntegritySha256::compute",
282 level = "trace",
283 err,
284 skip(data, key)
285 )]
286 pub fn compute(data: &[u8], key: &[u8]) -> Result<[u8; 32], StunWriteError> {
287 use hmac::{Hmac, Mac};
288 let mut hmac = Hmac::<sha2::Sha256>::new_from_slice(key)
289 .map_err(|_| StunWriteError::IntegrityFailed)?;
290 hmac.update(data);
291 let ret = hmac.finalize().into_bytes();
292 Ok(ret.into())
293 }
294
295 #[tracing::instrument(
306 name = "MessageIntegrity::verify",
307 level = "debug",
308 skip(data, key, expected)
309 )]
310 pub fn verify(data: &[u8], key: &[u8], expected: &[u8]) -> Result<(), StunParseError> {
311 use hmac::{Hmac, Mac};
312 let mut hmac = Hmac::<sha2::Sha256>::new_from_slice(key).map_err(|_| {
313 error!("failed to create hmac from key data");
314 StunParseError::InvalidAttributeData
315 })?;
316 hmac.update(data);
317 hmac.verify_truncated_left(expected).map_err(|_| {
318 debug!("integrity check failed");
319 StunParseError::IntegrityCheckFailed
320 })
321 }
322}
323
324impl core::fmt::Display for MessageIntegritySha256 {
325 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
326 write!(f, "{}: 0x", Self::TYPE)?;
327 for val in self.hmac.iter() {
328 write!(f, "{val:02x}")?;
329 }
330 Ok(())
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337 use byteorder::{BigEndian, ByteOrder};
338 use tracing::trace;
339
340 #[test]
341 fn message_integrity() {
342 let _log = crate::tests::test_init_log();
343 let val = [1; 20];
344 let attr = MessageIntegrity::new(val);
345 trace!("{attr}");
346 assert_eq!(attr.hmac(), &val);
347 assert_eq!(attr.length(), 20);
348 let raw = RawAttribute::from(&attr);
349 trace!("{raw}");
350 assert_eq!(raw.get_type(), MessageIntegrity::TYPE);
351 let mapped2 = MessageIntegrity::try_from(&raw).unwrap();
352 assert_eq!(mapped2.hmac(), &val);
353 let mut data: Vec<_> = raw.clone().into();
355 let len = data.len();
356 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
357 assert!(matches!(
358 MessageIntegrity::try_from(
359 &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
360 ),
361 Err(StunParseError::Truncated {
362 expected: 20,
363 actual: 19
364 })
365 ));
366 let mut data: Vec<_> = raw.into();
368 BigEndian::write_u16(&mut data[0..2], 0);
369 assert!(matches!(
370 MessageIntegrity::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
371 Err(StunParseError::WrongAttributeImplementation)
372 ));
373 }
374
375 #[test]
376 fn message_integrity_sha256() {
377 let _log = crate::tests::test_init_log();
378 let val = [1; 32];
379 let attr = MessageIntegritySha256::new(&val).unwrap();
380 trace!("{attr}");
381 assert_eq!(attr.hmac(), &val);
382 assert_eq!(attr.length(), 32);
383 let raw = RawAttribute::from(&attr);
384 trace!("{raw}");
385 assert_eq!(raw.get_type(), MessageIntegritySha256::TYPE);
386 let mapped2 = MessageIntegritySha256::try_from(&raw).unwrap();
387 assert_eq!(mapped2.hmac(), &val);
388 let mut data: Vec<_> = raw.clone().into();
390 let len = data.len();
391 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
392 assert!(matches!(
393 MessageIntegritySha256::try_from(
394 &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
395 ),
396 Err(StunParseError::InvalidAttributeData)
397 ));
398 let mut data: Vec<_> = raw.into();
400 BigEndian::write_u16(&mut data[0..2], 0);
401 assert!(matches!(
402 MessageIntegritySha256::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
403 Err(StunParseError::WrongAttributeImplementation)
404 ));
405 }
406
407 #[test]
408 fn message_integrity_sha256_new_too_large() {
409 let _log = crate::tests::test_init_log();
410 let val = [1; 33];
411 assert!(matches!(
412 MessageIntegritySha256::new(&val),
413 Err(StunWriteError::TooLarge {
414 expected: 32,
415 actual: 33
416 })
417 ));
418 }
419
420 #[test]
421 fn message_integrity_sha256_new_too_small() {
422 let _log = crate::tests::test_init_log();
423 let val = [1; 15];
424 assert!(matches!(
425 MessageIntegritySha256::new(&val),
426 Err(StunWriteError::TooSmall {
427 expected: 16,
428 actual: 15
429 })
430 ));
431 }
432
433 #[test]
434 fn message_integrity_sha256_new_not_multiple_of_4() {
435 let _log = crate::tests::test_init_log();
436 let val = [1; 19];
437 assert!(matches!(
438 MessageIntegritySha256::new(&val),
439 Err(StunWriteError::IntegrityFailed)
440 ));
441 }
442}