1use std::convert::TryFrom;
10
11use crate::message::{StunParseError, StunWriteError};
12
13use super::{
14 Attribute, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
15 AttributeWriteExt, RawAttribute,
16};
17
18use tracing::error;
19
20#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct MessageIntegrity {
23 hmac: [u8; 20],
24}
25
26impl AttributeStaticType for MessageIntegrity {
27 const TYPE: AttributeType = AttributeType(0x0008);
28}
29
30impl Attribute for MessageIntegrity {
31 fn get_type(&self) -> AttributeType {
32 Self::TYPE
33 }
34
35 fn length(&self) -> u16 {
36 20
37 }
38}
39
40impl AttributeWrite for MessageIntegrity {
41 fn to_raw(&self) -> RawAttribute {
42 RawAttribute::new(MessageIntegrity::TYPE, &self.hmac)
43 }
44 fn write_into_unchecked(&self, dest: &mut [u8]) {
45 self.write_header_unchecked(dest);
46 dest[4..4 + self.hmac.len()].copy_from_slice(&self.hmac);
47 }
48}
49
50impl AttributeFromRaw<'_> for MessageIntegrity {
51 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
52 where
53 Self: Sized,
54 {
55 Self::try_from(raw)
56 }
57}
58
59impl TryFrom<&RawAttribute<'_>> for MessageIntegrity {
60 type Error = StunParseError;
61
62 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
63 raw.check_type_and_len(Self::TYPE, 20..=20)?;
64 let hmac: [u8; 20] = (&*raw.value).try_into().unwrap();
66 Ok(Self { hmac })
67 }
68}
69
70impl MessageIntegrity {
71 pub fn new(hmac: [u8; 20]) -> Self {
82 Self { hmac }
83 }
84
85 pub fn hmac(&self) -> &[u8; 20] {
96 &self.hmac
97 }
98
99 #[tracing::instrument(
114 name = "MessageIntegrity::compute",
115 level = "trace",
116 err,
117 ret,
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 error!("integrity check failed");
152 StunParseError::IntegrityCheckFailed
153 })
154 }
155}
156
157impl std::fmt::Display for MessageIntegrity {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 write!(f, "{}: 0x", Self::TYPE)?;
160 for val in self.hmac.iter() {
161 write!(f, "{:02x}", val)?;
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 ret,
285 skip(data, key)
286 )]
287 pub fn compute(data: &[u8], key: &[u8]) -> Result<[u8; 32], StunWriteError> {
288 use hmac::{Hmac, Mac};
289 let mut hmac = Hmac::<sha2::Sha256>::new_from_slice(key)
290 .map_err(|_| StunWriteError::IntegrityFailed)?;
291 hmac.update(data);
292 let ret = hmac.finalize().into_bytes();
293 Ok(ret.into())
294 }
295
296 #[tracing::instrument(
307 name = "MessageIntegrity::verify",
308 level = "debug",
309 skip(data, key, expected)
310 )]
311 pub fn verify(data: &[u8], key: &[u8], expected: &[u8]) -> Result<(), StunParseError> {
312 use hmac::{Hmac, Mac};
313 let mut hmac = Hmac::<sha2::Sha256>::new_from_slice(key).map_err(|_| {
314 error!("failed to create hmac from key data");
315 StunParseError::InvalidAttributeData
316 })?;
317 hmac.update(data);
318 hmac.verify_truncated_left(expected).map_err(|_| {
319 error!("integrity check failed");
320 StunParseError::IntegrityCheckFailed
321 })
322 }
323}
324
325impl std::fmt::Display for MessageIntegritySha256 {
326 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
327 write!(f, "{}: 0x", Self::TYPE)?;
328 for val in self.hmac.iter() {
329 write!(f, "{:02x}", val)?;
330 }
331 Ok(())
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338 use byteorder::{BigEndian, ByteOrder};
339 use tracing::trace;
340
341 #[test]
342 fn message_integrity() {
343 let _log = crate::tests::test_init_log();
344 let val = [1; 20];
345 let attr = MessageIntegrity::new(val);
346 trace!("{attr}");
347 assert_eq!(attr.hmac(), &val);
348 assert_eq!(attr.length(), 20);
349 let raw = RawAttribute::from(&attr);
350 trace!("{raw}");
351 assert_eq!(raw.get_type(), MessageIntegrity::TYPE);
352 let mapped2 = MessageIntegrity::try_from(&raw).unwrap();
353 assert_eq!(mapped2.hmac(), &val);
354 let mut data: Vec<_> = raw.clone().into();
356 let len = data.len();
357 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
358 assert!(matches!(
359 MessageIntegrity::try_from(
360 &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
361 ),
362 Err(StunParseError::Truncated {
363 expected: 20,
364 actual: 19
365 })
366 ));
367 let mut data: Vec<_> = raw.into();
369 BigEndian::write_u16(&mut data[0..2], 0);
370 assert!(matches!(
371 MessageIntegrity::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
372 Err(StunParseError::WrongAttributeImplementation)
373 ));
374 }
375
376 #[test]
377 fn message_integrity_sha256() {
378 let _log = crate::tests::test_init_log();
379 let val = [1; 32];
380 let attr = MessageIntegritySha256::new(&val).unwrap();
381 trace!("{attr}");
382 assert_eq!(attr.hmac(), &val);
383 assert_eq!(attr.length(), 32);
384 let raw = RawAttribute::from(&attr);
385 trace!("{raw}");
386 assert_eq!(raw.get_type(), MessageIntegritySha256::TYPE);
387 let mapped2 = MessageIntegritySha256::try_from(&raw).unwrap();
388 assert_eq!(mapped2.hmac(), &val);
389 let mut data: Vec<_> = raw.clone().into();
391 let len = data.len();
392 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
393 assert!(matches!(
394 MessageIntegritySha256::try_from(
395 &RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()
396 ),
397 Err(StunParseError::InvalidAttributeData)
398 ));
399 let mut data: Vec<_> = raw.into();
401 BigEndian::write_u16(&mut data[0..2], 0);
402 assert!(matches!(
403 MessageIntegritySha256::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
404 Err(StunParseError::WrongAttributeImplementation)
405 ));
406 }
407
408 #[test]
409 fn message_integrity_sha256_new_too_large() {
410 let _log = crate::tests::test_init_log();
411 let val = [1; 33];
412 assert!(matches!(
413 MessageIntegritySha256::new(&val),
414 Err(StunWriteError::TooLarge {
415 expected: 32,
416 actual: 33
417 })
418 ));
419 }
420
421 #[test]
422 fn message_integrity_sha256_new_too_small() {
423 let _log = crate::tests::test_init_log();
424 let val = [1; 15];
425 assert!(matches!(
426 MessageIntegritySha256::new(&val),
427 Err(StunWriteError::TooSmall {
428 expected: 16,
429 actual: 15
430 })
431 ));
432 }
433
434 #[test]
435 fn message_integrity_sha256_new_not_multiple_of_4() {
436 let _log = crate::tests::test_init_log();
437 let val = [1; 19];
438 assert!(matches!(
439 MessageIntegritySha256::new(&val),
440 Err(StunWriteError::IntegrityFailed)
441 ));
442 }
443}