1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use std::io::Cursor;
use crate::definitions::{STUN_FINGERPRINT_ATTR_SIZE, STUN_INTEGRITY_ATTR_SIZE};
use crate::StunHeader;
use crate::{AttributeDecodeError, StunAttribute};
pub use super::errors::{IntegrityKeyGenerationError, MessageDecodeError, MessageEncodeError};
use super::message::StunMessage;
impl StunMessage {
/// Decodes and returns the STUN message
///
/// Arguments:
///
/// * `bytes`: binary encoded message to decode from
/// * `integrity_password`: Optionally set key that will be used for message integrity verification
pub fn decode(
bytes: &[u8],
integrity_password: Option<&str>,
) -> Result<Self, MessageDecodeError> {
let mut cursor = Cursor::new(bytes);
// Decode header
let header = StunHeader::decode(&mut cursor)?;
// Decode attributes
let mut attributes = Vec::new();
let mut integrity_attr_passed = false;
// Track for username/realm occurrences
let mut username = None;
let mut realm = None;
// Message length does not include the 20-byte header.
while cursor.position() < 20 + header.message_len as u64 {
let decoded = StunAttribute::decode(&mut cursor, header.transaction_id);
match decoded {
Ok(decoded) => {
// Ignore all attributes after the MESSAGE-INTEGRITY attribute.
// As per [RFC5389 Section 15.4](https://tools.ietf.org/html/rfc5389#section-15.4)
if !integrity_attr_passed {
attributes.push(decoded.clone());
}
// Handle Fingerprint and MessageIntegrity attributes
match decoded {
StunAttribute::Username { value } => {
username = Some(value);
}
StunAttribute::Realm { value } => {
realm = Some(value);
}
StunAttribute::Fingerprint { value } => {
let attr_pos = cursor.position() as usize - STUN_FINGERPRINT_ATTR_SIZE;
// Make sure the Fingerprint attribute is the last one
if cursor.position() != bytes.len() as u64 {
return Err(
MessageDecodeError::IncorrectFingerprintAttributePosition {
msg_len: bytes.len(),
attr_pos: attr_pos as usize,
},
);
}
// Compute fingerprint for verification
let computed_fingerprint =
Self::calculate_fingerprint(&cursor.get_ref()[0..attr_pos]);
// Make sure the fingerprint matches
if computed_fingerprint != value {
return Err(MessageDecodeError::FingerprintMismatch {
attr_value: value,
computed_value: computed_fingerprint,
});
}
if integrity_attr_passed {
// Push the attribute to the list explicitly since it's after the MessageIntegrity attribute
attributes.push(decoded);
}
}
StunAttribute::MessageIntegrity { key } => {
// Mark MessageIntegrity attribute as passed so we can ignore attributes that happen after it
// With the exception of the Fingerprint attribute
integrity_attr_passed = true;
// If an `integrity_password` has been supplied, recalculate and verify the HMAC value
if let Some(integrity_password) = integrity_password {
let integrity_key = Self::calculate_integrity_key(
integrity_password,
realm.clone(),
username.clone(),
)?;
let hmac = Self::calculate_integrity_hash(
&integrity_key,
&cursor.get_ref()[0..(cursor.position() as usize
- STUN_INTEGRITY_ATTR_SIZE)
as usize],
);
// Verify message integrity
if hmac != key {
return Err(MessageDecodeError::MessageIntegrityFail {
attr_value: key,
computed_value: hmac,
});
}
}
}
_ => {}
};
}
Err(err) => {
match err {
AttributeDecodeError::UnrecognizedAttributeType { attr_type } => {
// Attributes with type values between 0x8000 and 0xFFFF are
// comprehension-optional attributes, which means that those attributes
// can be ignored by the STUN agent if it does not understand them.
// Only return an error when the attribute is comprehension-required
if attr_type <= 0x8000 {
return Err(MessageDecodeError::AttributeDecodeFailure{source: err, transaction_id: header.transaction_id});
}
}
// Return an error on any other attribute decoding error
_ => return Err(MessageDecodeError::AttributeDecodeFailure{source: err, transaction_id: header.transaction_id}),
}
}
}
}
Ok(Self { header, attributes })
}
}