Skip to content

Commit db38557

Browse files
committed
message: support both sha1 and sha256 integrity in the same message
This is explicitly allowed by RFC 8489.
1 parent b763443 commit db38557

File tree

1 file changed

+137
-44
lines changed

1 file changed

+137
-44
lines changed

stun-types/src/message.rs

+137-44
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,6 @@ pub enum StunParseError {
130130
/// The provided data does not match the message
131131
#[error("The provided data does not match the message")]
132132
DataMismatch,
133-
/// The message has multiple integrity attributes
134-
#[error("Multiple integrity types are used in this message")]
135-
DuplicateIntegrity,
136133
/// The attribute contains invalid data
137134
#[error("The attribute contains invalid data")]
138135
InvalidAttributeData,
@@ -873,8 +870,14 @@ impl<'a> Message<'a> {
873870

874871
let mut data_offset = MessageHeader::LENGTH;
875872
let mut data = &data[MessageHeader::LENGTH..];
876-
let mut seen_message_integrity = false;
877-
let mut seen_fingerprint = false;
873+
let ending_attributes = [
874+
MessageIntegrity::TYPE,
875+
MessageIntegritySha256::TYPE,
876+
Fingerprint::TYPE,
877+
];
878+
// XXX: maybe use small/tinyvec?
879+
let mut seen_ending_attributes = [AttributeType::new(0); 3];
880+
let mut seen_ending_len = 0;
878881
while !data.is_empty() {
879882
let attr = RawAttribute::from_bytes(data).map_err(|e| {
880883
warn!(
@@ -894,24 +897,40 @@ impl<'a> Message<'a> {
894897
}
895898
})?;
896899

897-
if seen_message_integrity && attr.get_type() != Fingerprint::TYPE {
898-
// only attribute valid after MESSAGE_INTEGRITY is FINGERPRINT
899-
warn!(
900-
"unexpected attribute {} after MESSAGE_INTEGRITY",
901-
attr.get_type()
902-
);
903-
return Err(StunParseError::AttributeAfterIntegrity(attr.get_type()));
904-
}
905-
906-
if seen_fingerprint {
907-
// no valid attributes after FINGERPRINT
908-
warn!("unexpected attribute {} after FINGERPRINT", attr.get_type());
909-
return Err(StunParseError::AttributeAfterFingerprint(attr.get_type()));
900+
// if we have seen any ending attributes, then there is only a fixed set of attributes
901+
// that are allowed.
902+
if seen_ending_len > 0 && !ending_attributes.contains(&attr.get_type()) {
903+
if seen_ending_attributes.contains(&Fingerprint::TYPE) {
904+
warn!("unexpected attribute {} after FINGERPRINT", attr.get_type());
905+
return Err(StunParseError::AttributeAfterFingerprint(attr.get_type()));
906+
} else {
907+
// only attribute valid after MESSAGE_INTEGRITY is FINGERPRINT
908+
warn!(
909+
"unexpected attribute {} after MESSAGE_INTEGRITY",
910+
attr.get_type()
911+
);
912+
return Err(StunParseError::AttributeAfterIntegrity(attr.get_type()));
913+
}
910914
}
911915

912-
if [MessageIntegrity::TYPE, MessageIntegritySha256::TYPE].contains(&attr.get_type()) {
913-
seen_message_integrity = true;
914-
// need credentials to validate the integrity of the message
916+
if ending_attributes.contains(&attr.get_type()) {
917+
if seen_ending_attributes.contains(&attr.get_type()) {
918+
if seen_ending_attributes.contains(&Fingerprint::TYPE) {
919+
warn!("unexpected attribute {} after FINGERPRINT", attr.get_type());
920+
return Err(StunParseError::AttributeAfterFingerprint(attr.get_type()));
921+
} else {
922+
// only attribute valid after MESSAGE_INTEGRITY is FINGERPRINT
923+
warn!(
924+
"unexpected attribute {} after MESSAGE_INTEGRITY",
925+
attr.get_type()
926+
);
927+
return Err(StunParseError::AttributeAfterIntegrity(attr.get_type()));
928+
}
929+
} else {
930+
seen_ending_attributes[seen_ending_len] = attr.get_type();
931+
seen_ending_len += 1;
932+
// need credentials to validate the integrity of the message
933+
}
915934
}
916935
let padded_len = padded_attr_size(&attr);
917936
if padded_len > data.len() {
@@ -925,7 +944,6 @@ impl<'a> Message<'a> {
925944
});
926945
}
927946
if attr.get_type() == Fingerprint::TYPE {
928-
seen_fingerprint = true;
929947
let f = Fingerprint::from_raw(&attr)?;
930948
let msg_fingerprint = f.fingerprint();
931949
let mut fingerprint_data = orig_data[..data_offset].to_vec();
@@ -980,20 +998,19 @@ impl<'a> Message<'a> {
980998
pub fn validate_integrity(
981999
&self,
9821000
credentials: &MessageIntegrityCredentials,
983-
) -> Result<(), StunParseError> {
1001+
) -> Result<IntegrityAlgorithm, StunParseError> {
9841002
debug!("using credentials {credentials:?}");
9851003
let raw_sha1 = self.raw_attribute(MessageIntegrity::TYPE);
9861004
let raw_sha256 = self.raw_attribute(MessageIntegritySha256::TYPE);
9871005
let (algo, msg_hmac) = match (raw_sha1, raw_sha256) {
988-
(Some(_), Some(_)) => return Err(StunParseError::DuplicateIntegrity),
1006+
(_, Some(sha256)) => {
1007+
let integrity = MessageIntegritySha256::try_from(&sha256)?;
1008+
(IntegrityAlgorithm::Sha256, integrity.hmac().to_vec())
1009+
}
9891010
(Some(sha1), None) => {
9901011
let integrity = MessageIntegrity::try_from(&sha1)?;
9911012
(IntegrityAlgorithm::Sha1, integrity.hmac().to_vec())
9921013
}
993-
(None, Some(sha256)) => {
994-
let integrity = MessageIntegritySha256::try_from(&sha256)?;
995-
(IntegrityAlgorithm::Sha256, integrity.hmac().to_vec())
996-
}
9971014
(None, None) => return Err(StunParseError::MissingAttribute(MessageIntegrity::TYPE)),
9981015
};
9991016

@@ -1017,11 +1034,12 @@ impl<'a> Message<'a> {
10171034
&mut hmac_data[2..4],
10181035
data_offset as u16 + 24 - MessageHeader::LENGTH as u16,
10191036
);
1020-
return MessageIntegrity::verify(
1037+
MessageIntegrity::verify(
10211038
&hmac_data,
10221039
&key,
10231040
msg_hmac.as_slice().try_into().unwrap(),
1024-
);
1041+
)?;
1042+
return Ok(algo);
10251043
} else if algo == IntegrityAlgorithm::Sha256
10261044
&& attr.get_type() == MessageIntegritySha256::TYPE
10271045
{
@@ -1036,7 +1054,8 @@ impl<'a> Message<'a> {
10361054
&mut hmac_data[2..4],
10371055
data_offset as u16 + attr.length() + 4 - MessageHeader::LENGTH as u16,
10381056
);
1039-
return MessageIntegritySha256::verify(&hmac_data, &key, &msg_hmac);
1057+
MessageIntegritySha256::verify(&hmac_data, &key, &msg_hmac)?;
1058+
return Ok(algo);
10401059
}
10411060
let padded_len = padded_attr_size(&attr);
10421061
// checked when initially parsing.
@@ -1447,11 +1466,9 @@ impl<'a> MessageBuilder<'a> {
14471466
/// Err(StunWriteError::AttributeExists(MessageIntegrity::TYPE)),
14481467
/// ));
14491468
///
1450-
/// // only one of MessageIntegrity, and MessageIntegritySha256 is allowed
1451-
/// assert!(matches!(
1452-
/// message.add_message_integrity(&credentials, IntegrityAlgorithm::Sha256),
1453-
/// Err(StunWriteError::AttributeExists(MessageIntegrity::TYPE)),
1454-
/// ));
1469+
/// // both MessageIntegrity, and MessageIntegritySha256 are allowed, however Sha256 must be
1470+
/// // after Sha1
1471+
/// assert!(message.add_message_integrity(&credentials, IntegrityAlgorithm::Sha256).is_ok());
14551472
///
14561473
/// let data = message.build();
14571474
/// let message = Message::from_bytes(&data).unwrap();
@@ -1471,7 +1488,7 @@ impl<'a> MessageBuilder<'a> {
14711488
credentials: &MessageIntegrityCredentials,
14721489
algorithm: IntegrityAlgorithm,
14731490
) -> Result<(), StunWriteError> {
1474-
if self.has_attribute(MessageIntegrity::TYPE) {
1491+
if self.has_attribute(MessageIntegrity::TYPE) && algorithm == IntegrityAlgorithm::Sha1 {
14751492
return Err(StunWriteError::AttributeExists(MessageIntegrity::TYPE));
14761493
}
14771494
if self.has_attribute(MessageIntegritySha256::TYPE) {
@@ -1483,6 +1500,16 @@ impl<'a> MessageBuilder<'a> {
14831500
return Err(StunWriteError::FingerprintExists);
14841501
}
14851502

1503+
self.add_message_integrity_unchecked(credentials, algorithm);
1504+
1505+
Ok(())
1506+
}
1507+
1508+
fn add_message_integrity_unchecked(
1509+
&mut self,
1510+
credentials: &MessageIntegrityCredentials,
1511+
algorithm: IntegrityAlgorithm,
1512+
) {
14861513
let key = credentials.make_hmac_key();
14871514
match algorithm {
14881515
IntegrityAlgorithm::Sha1 => {
@@ -1495,12 +1522,11 @@ impl<'a> MessageBuilder<'a> {
14951522
let bytes = self.integrity_bytes_from_message(36);
14961523
let integrity = MessageIntegritySha256::compute(&bytes, &key).unwrap();
14971524
self.attributes.push(
1498-
RawAttribute::from(&MessageIntegritySha256::new(integrity.as_slice())?)
1525+
RawAttribute::from(&MessageIntegritySha256::new(integrity.as_slice()).unwrap())
14991526
.into_owned(),
15001527
);
15011528
}
15021529
}
1503-
Ok(())
15041530
}
15051531

15061532
/// Adds [`Fingerprint`] attribute to a [`Message`]
@@ -1531,6 +1557,13 @@ impl<'a> MessageBuilder<'a> {
15311557
if self.has_attribute(Fingerprint::TYPE) {
15321558
return Err(StunWriteError::AttributeExists(Fingerprint::TYPE));
15331559
}
1560+
1561+
self.add_fingerprint_unchecked();
1562+
1563+
Ok(())
1564+
}
1565+
1566+
fn add_fingerprint_unchecked(&mut self) {
15341567
// fingerprint is computed using all the data up to (exclusive of) the FINGERPRINT
15351568
// but with a length field including the FINGERPRINT attribute...
15361569
let mut bytes = self.build();
@@ -1540,7 +1573,6 @@ impl<'a> MessageBuilder<'a> {
15401573
let fingerprint = Fingerprint::compute(&bytes);
15411574
self.attributes
15421575
.push(RawAttribute::from(&Fingerprint::new(fingerprint)));
1543-
Ok(())
15441576
}
15451577

15461578
/// Add a `Attribute` to this `Message`. Only one `AttributeType` can be added for each
@@ -1744,9 +1776,13 @@ mod tests {
17441776
msg.add_message_integrity(&credentials, IntegrityAlgorithm::Sha1),
17451777
Err(StunWriteError::AttributeExists(MessageIntegrity::TYPE))
17461778
));
1779+
msg.add_message_integrity(&credentials, IntegrityAlgorithm::Sha256)
1780+
.unwrap();
17471781
assert!(matches!(
17481782
msg.add_message_integrity(&credentials, IntegrityAlgorithm::Sha256),
1749-
Err(StunWriteError::AttributeExists(MessageIntegrity::TYPE))
1783+
Err(StunWriteError::AttributeExists(
1784+
MessageIntegritySha256::TYPE
1785+
))
17501786
));
17511787
let software = Software::new("s").unwrap();
17521788
assert!(matches!(
@@ -1755,6 +1791,21 @@ mod tests {
17551791
));
17561792
}
17571793

1794+
#[test]
1795+
fn add_sha1_integrity_after_sha256() {
1796+
let _log = crate::tests::test_init_log();
1797+
let credentials = ShortTermCredentials::new("secret".to_owned()).into();
1798+
let mut msg = Message::builder_request(BINDING);
1799+
msg.add_message_integrity(&credentials, IntegrityAlgorithm::Sha256)
1800+
.unwrap();
1801+
assert!(matches!(
1802+
msg.add_message_integrity(&credentials, IntegrityAlgorithm::Sha1),
1803+
Err(StunWriteError::AttributeExists(
1804+
MessageIntegritySha256::TYPE
1805+
))
1806+
));
1807+
}
1808+
17581809
#[test]
17591810
fn add_attribute_after_integrity() {
17601811
let _log = crate::tests::test_init_log();
@@ -1844,6 +1895,29 @@ mod tests {
18441895
}
18451896
}
18461897

1898+
#[test]
1899+
fn parse_duplicate_integrity_after_integrity() {
1900+
let _log = crate::tests::test_init_log();
1901+
for algorithm in [IntegrityAlgorithm::Sha1, IntegrityAlgorithm::Sha256] {
1902+
let credentials = ShortTermCredentials::new("secret".to_owned()).into();
1903+
let mut msg = Message::builder_request(BINDING);
1904+
msg.add_message_integrity(&credentials, algorithm).unwrap();
1905+
// duplicate integrity attribute. Don't do this in real code!
1906+
msg.add_message_integrity_unchecked(&credentials, algorithm);
1907+
let bytes = msg.build();
1908+
let integrity_type = match algorithm {
1909+
IntegrityAlgorithm::Sha1 => MessageIntegrity::TYPE,
1910+
IntegrityAlgorithm::Sha256 => MessageIntegritySha256::TYPE,
1911+
};
1912+
let Err(StunParseError::AttributeAfterIntegrity(err_integrity_type)) =
1913+
Message::from_bytes(&bytes)
1914+
else {
1915+
unreachable!();
1916+
};
1917+
assert_eq!(integrity_type, err_integrity_type);
1918+
}
1919+
}
1920+
18471921
#[test]
18481922
fn parse_attribute_after_fingerprint() {
18491923
let _log = crate::tests::test_init_log();
@@ -1861,6 +1935,19 @@ mod tests {
18611935
));
18621936
}
18631937

1938+
#[test]
1939+
fn parse_duplicate_fingerprint_after_fingerprint() {
1940+
let _log = crate::tests::test_init_log();
1941+
let mut msg = Message::builder_request(BINDING);
1942+
msg.add_fingerprint().unwrap();
1943+
msg.add_fingerprint_unchecked();
1944+
let bytes = msg.build();
1945+
assert!(matches!(
1946+
Message::from_bytes(&bytes),
1947+
Err(StunParseError::AttributeAfterFingerprint(Fingerprint::TYPE))
1948+
));
1949+
}
1950+
18641951
#[test]
18651952
fn add_attribute_after_fingerprint() {
18661953
let _log = crate::tests::test_init_log();
@@ -2102,7 +2189,10 @@ mod tests {
21022189
let credentials = MessageIntegrityCredentials::ShortTerm(ShortTermCredentials {
21032190
password: "VOkJxbRl1RmTxUk/WvJxBt".to_owned(),
21042191
});
2105-
assert!(matches!(msg.validate_integrity(&credentials), Ok(())));
2192+
assert!(matches!(
2193+
msg.validate_integrity(&credentials),
2194+
Ok(IntegrityAlgorithm::Sha1)
2195+
));
21062196
builder
21072197
.add_message_integrity(&credentials, IntegrityAlgorithm::Sha1)
21082198
.unwrap();
@@ -2183,7 +2273,7 @@ mod tests {
21832273
});
21842274
let ret = msg.validate_integrity(&credentials);
21852275
debug!("{:?}", ret);
2186-
assert!(matches!(ret, Ok(())));
2276+
assert!(matches!(ret, Ok(IntegrityAlgorithm::Sha1)));
21872277
builder
21882278
.add_message_integrity(&credentials, IntegrityAlgorithm::Sha1)
21892279
.unwrap();
@@ -2263,7 +2353,10 @@ mod tests {
22632353
let credentials = MessageIntegrityCredentials::ShortTerm(ShortTermCredentials {
22642354
password: "VOkJxbRl1RmTxUk/WvJxBt".to_owned(),
22652355
});
2266-
assert!(matches!(msg.validate_integrity(&credentials), Ok(())));
2356+
assert!(matches!(
2357+
msg.validate_integrity(&credentials),
2358+
Ok(IntegrityAlgorithm::Sha1)
2359+
));
22672360
builder
22682361
.add_message_integrity(&credentials, IntegrityAlgorithm::Sha1)
22692362
.unwrap();

0 commit comments

Comments
 (0)