Skip to content

Commit 2eb8fed

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 2eb8fed

File tree

1 file changed

+137
-41
lines changed

1 file changed

+137
-41
lines changed

stun-types/src/message.rs

+137-41
Original file line numberDiff line numberDiff line change
@@ -873,8 +873,14 @@ impl<'a> Message<'a> {
873873

874874
let mut data_offset = MessageHeader::LENGTH;
875875
let mut data = &data[MessageHeader::LENGTH..];
876-
let mut seen_message_integrity = false;
877-
let mut seen_fingerprint = false;
876+
let ending_attributes = [
877+
MessageIntegrity::TYPE,
878+
MessageIntegritySha256::TYPE,
879+
Fingerprint::TYPE,
880+
];
881+
// XXX: maybe use small/tinyvec?
882+
let mut seen_ending_attributes = [AttributeType::new(0); 3];
883+
let mut seen_ending_len = 0;
878884
while !data.is_empty() {
879885
let attr = RawAttribute::from_bytes(data).map_err(|e| {
880886
warn!(
@@ -894,24 +900,40 @@ impl<'a> Message<'a> {
894900
}
895901
})?;
896902

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()));
903+
// if we have seen any ending attributes, then there is only a fixed set of attributes
904+
// that are allowed.
905+
if seen_ending_len > 0 && !ending_attributes.contains(&attr.get_type()) {
906+
if seen_ending_attributes.contains(&Fingerprint::TYPE) {
907+
warn!("unexpected attribute {} after FINGERPRINT", attr.get_type());
908+
return Err(StunParseError::AttributeAfterFingerprint(attr.get_type()));
909+
} else {
910+
// only attribute valid after MESSAGE_INTEGRITY is FINGERPRINT
911+
warn!(
912+
"unexpected attribute {} after MESSAGE_INTEGRITY",
913+
attr.get_type()
914+
);
915+
return Err(StunParseError::AttributeAfterIntegrity(attr.get_type()));
916+
}
910917
}
911918

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
919+
if ending_attributes.contains(&attr.get_type()) {
920+
if seen_ending_attributes.contains(&attr.get_type()) {
921+
if seen_ending_attributes.contains(&Fingerprint::TYPE) {
922+
warn!("unexpected attribute {} after FINGERPRINT", attr.get_type());
923+
return Err(StunParseError::AttributeAfterFingerprint(attr.get_type()));
924+
} else {
925+
// only attribute valid after MESSAGE_INTEGRITY is FINGERPRINT
926+
warn!(
927+
"unexpected attribute {} after MESSAGE_INTEGRITY",
928+
attr.get_type()
929+
);
930+
return Err(StunParseError::AttributeAfterIntegrity(attr.get_type()));
931+
}
932+
} else {
933+
seen_ending_attributes[seen_ending_len] = attr.get_type();
934+
seen_ending_len += 1;
935+
// need credentials to validate the integrity of the message
936+
}
915937
}
916938
let padded_len = padded_attr_size(&attr);
917939
if padded_len > data.len() {
@@ -925,7 +947,6 @@ impl<'a> Message<'a> {
925947
});
926948
}
927949
if attr.get_type() == Fingerprint::TYPE {
928-
seen_fingerprint = true;
929950
let f = Fingerprint::from_raw(&attr)?;
930951
let msg_fingerprint = f.fingerprint();
931952
let mut fingerprint_data = orig_data[..data_offset].to_vec();
@@ -980,20 +1001,19 @@ impl<'a> Message<'a> {
9801001
pub fn validate_integrity(
9811002
&self,
9821003
credentials: &MessageIntegrityCredentials,
983-
) -> Result<(), StunParseError> {
1004+
) -> Result<IntegrityAlgorithm, StunParseError> {
9841005
debug!("using credentials {credentials:?}");
9851006
let raw_sha1 = self.raw_attribute(MessageIntegrity::TYPE);
9861007
let raw_sha256 = self.raw_attribute(MessageIntegritySha256::TYPE);
9871008
let (algo, msg_hmac) = match (raw_sha1, raw_sha256) {
988-
(Some(_), Some(_)) => return Err(StunParseError::DuplicateIntegrity),
1009+
(_, Some(sha256)) => {
1010+
let integrity = MessageIntegritySha256::try_from(&sha256)?;
1011+
(IntegrityAlgorithm::Sha256, integrity.hmac().to_vec())
1012+
}
9891013
(Some(sha1), None) => {
9901014
let integrity = MessageIntegrity::try_from(&sha1)?;
9911015
(IntegrityAlgorithm::Sha1, integrity.hmac().to_vec())
9921016
}
993-
(None, Some(sha256)) => {
994-
let integrity = MessageIntegritySha256::try_from(&sha256)?;
995-
(IntegrityAlgorithm::Sha256, integrity.hmac().to_vec())
996-
}
9971017
(None, None) => return Err(StunParseError::MissingAttribute(MessageIntegrity::TYPE)),
9981018
};
9991019

@@ -1017,11 +1037,12 @@ impl<'a> Message<'a> {
10171037
&mut hmac_data[2..4],
10181038
data_offset as u16 + 24 - MessageHeader::LENGTH as u16,
10191039
);
1020-
return MessageIntegrity::verify(
1040+
MessageIntegrity::verify(
10211041
&hmac_data,
10221042
&key,
10231043
msg_hmac.as_slice().try_into().unwrap(),
1024-
);
1044+
)?;
1045+
return Ok(algo);
10251046
} else if algo == IntegrityAlgorithm::Sha256
10261047
&& attr.get_type() == MessageIntegritySha256::TYPE
10271048
{
@@ -1036,7 +1057,8 @@ impl<'a> Message<'a> {
10361057
&mut hmac_data[2..4],
10371058
data_offset as u16 + attr.length() + 4 - MessageHeader::LENGTH as u16,
10381059
);
1039-
return MessageIntegritySha256::verify(&hmac_data, &key, &msg_hmac);
1060+
MessageIntegritySha256::verify(&hmac_data, &key, &msg_hmac)?;
1061+
return Ok(algo);
10401062
}
10411063
let padded_len = padded_attr_size(&attr);
10421064
// checked when initially parsing.
@@ -1447,11 +1469,9 @@ impl<'a> MessageBuilder<'a> {
14471469
/// Err(StunWriteError::AttributeExists(MessageIntegrity::TYPE)),
14481470
/// ));
14491471
///
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-
/// ));
1472+
/// // both MessageIntegrity, and MessageIntegritySha256 are allowed, however Sha256 must be
1473+
/// // after Sha1
1474+
/// assert!(message.add_message_integrity(&credentials, IntegrityAlgorithm::Sha256).is_ok());
14551475
///
14561476
/// let data = message.build();
14571477
/// let message = Message::from_bytes(&data).unwrap();
@@ -1471,7 +1491,7 @@ impl<'a> MessageBuilder<'a> {
14711491
credentials: &MessageIntegrityCredentials,
14721492
algorithm: IntegrityAlgorithm,
14731493
) -> Result<(), StunWriteError> {
1474-
if self.has_attribute(MessageIntegrity::TYPE) {
1494+
if self.has_attribute(MessageIntegrity::TYPE) && algorithm == IntegrityAlgorithm::Sha1 {
14751495
return Err(StunWriteError::AttributeExists(MessageIntegrity::TYPE));
14761496
}
14771497
if self.has_attribute(MessageIntegritySha256::TYPE) {
@@ -1483,6 +1503,16 @@ impl<'a> MessageBuilder<'a> {
14831503
return Err(StunWriteError::FingerprintExists);
14841504
}
14851505

1506+
self.add_message_integrity_unchecked(credentials, algorithm);
1507+
1508+
Ok(())
1509+
}
1510+
1511+
fn add_message_integrity_unchecked(
1512+
&mut self,
1513+
credentials: &MessageIntegrityCredentials,
1514+
algorithm: IntegrityAlgorithm,
1515+
) {
14861516
let key = credentials.make_hmac_key();
14871517
match algorithm {
14881518
IntegrityAlgorithm::Sha1 => {
@@ -1495,12 +1525,11 @@ impl<'a> MessageBuilder<'a> {
14951525
let bytes = self.integrity_bytes_from_message(36);
14961526
let integrity = MessageIntegritySha256::compute(&bytes, &key).unwrap();
14971527
self.attributes.push(
1498-
RawAttribute::from(&MessageIntegritySha256::new(integrity.as_slice())?)
1528+
RawAttribute::from(&MessageIntegritySha256::new(integrity.as_slice()).unwrap())
14991529
.into_owned(),
15001530
);
15011531
}
15021532
}
1503-
Ok(())
15041533
}
15051534

15061535
/// Adds [`Fingerprint`] attribute to a [`Message`]
@@ -1531,6 +1560,13 @@ impl<'a> MessageBuilder<'a> {
15311560
if self.has_attribute(Fingerprint::TYPE) {
15321561
return Err(StunWriteError::AttributeExists(Fingerprint::TYPE));
15331562
}
1563+
1564+
self.add_fingerprint_unchecked();
1565+
1566+
Ok(())
1567+
}
1568+
1569+
fn add_fingerprint_unchecked(&mut self) {
15341570
// fingerprint is computed using all the data up to (exclusive of) the FINGERPRINT
15351571
// but with a length field including the FINGERPRINT attribute...
15361572
let mut bytes = self.build();
@@ -1540,7 +1576,6 @@ impl<'a> MessageBuilder<'a> {
15401576
let fingerprint = Fingerprint::compute(&bytes);
15411577
self.attributes
15421578
.push(RawAttribute::from(&Fingerprint::new(fingerprint)));
1543-
Ok(())
15441579
}
15451580

15461581
/// Add a `Attribute` to this `Message`. Only one `AttributeType` can be added for each
@@ -1744,9 +1779,13 @@ mod tests {
17441779
msg.add_message_integrity(&credentials, IntegrityAlgorithm::Sha1),
17451780
Err(StunWriteError::AttributeExists(MessageIntegrity::TYPE))
17461781
));
1782+
msg.add_message_integrity(&credentials, IntegrityAlgorithm::Sha256)
1783+
.unwrap();
17471784
assert!(matches!(
17481785
msg.add_message_integrity(&credentials, IntegrityAlgorithm::Sha256),
1749-
Err(StunWriteError::AttributeExists(MessageIntegrity::TYPE))
1786+
Err(StunWriteError::AttributeExists(
1787+
MessageIntegritySha256::TYPE
1788+
))
17501789
));
17511790
let software = Software::new("s").unwrap();
17521791
assert!(matches!(
@@ -1755,6 +1794,21 @@ mod tests {
17551794
));
17561795
}
17571796

1797+
#[test]
1798+
fn add_sha1_integrity_after_sha256() {
1799+
let _log = crate::tests::test_init_log();
1800+
let credentials = ShortTermCredentials::new("secret".to_owned()).into();
1801+
let mut msg = Message::builder_request(BINDING);
1802+
msg.add_message_integrity(&credentials, IntegrityAlgorithm::Sha256)
1803+
.unwrap();
1804+
assert!(matches!(
1805+
msg.add_message_integrity(&credentials, IntegrityAlgorithm::Sha1),
1806+
Err(StunWriteError::AttributeExists(
1807+
MessageIntegritySha256::TYPE
1808+
))
1809+
));
1810+
}
1811+
17581812
#[test]
17591813
fn add_attribute_after_integrity() {
17601814
let _log = crate::tests::test_init_log();
@@ -1844,6 +1898,29 @@ mod tests {
18441898
}
18451899
}
18461900

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

1941+
#[test]
1942+
fn parse_duplicate_fingerprint_after_fingerprint() {
1943+
let _log = crate::tests::test_init_log();
1944+
let mut msg = Message::builder_request(BINDING);
1945+
msg.add_fingerprint().unwrap();
1946+
msg.add_fingerprint_unchecked();
1947+
let bytes = msg.build();
1948+
assert!(matches!(
1949+
Message::from_bytes(&bytes),
1950+
Err(StunParseError::AttributeAfterFingerprint(Fingerprint::TYPE))
1951+
));
1952+
}
1953+
18641954
#[test]
18651955
fn add_attribute_after_fingerprint() {
18661956
let _log = crate::tests::test_init_log();
@@ -2102,7 +2192,10 @@ mod tests {
21022192
let credentials = MessageIntegrityCredentials::ShortTerm(ShortTermCredentials {
21032193
password: "VOkJxbRl1RmTxUk/WvJxBt".to_owned(),
21042194
});
2105-
assert!(matches!(msg.validate_integrity(&credentials), Ok(())));
2195+
assert!(matches!(
2196+
msg.validate_integrity(&credentials),
2197+
Ok(IntegrityAlgorithm::Sha1)
2198+
));
21062199
builder
21072200
.add_message_integrity(&credentials, IntegrityAlgorithm::Sha1)
21082201
.unwrap();
@@ -2183,7 +2276,7 @@ mod tests {
21832276
});
21842277
let ret = msg.validate_integrity(&credentials);
21852278
debug!("{:?}", ret);
2186-
assert!(matches!(ret, Ok(())));
2279+
assert!(matches!(ret, Ok(IntegrityAlgorithm::Sha1)));
21872280
builder
21882281
.add_message_integrity(&credentials, IntegrityAlgorithm::Sha1)
21892282
.unwrap();
@@ -2263,7 +2356,10 @@ mod tests {
22632356
let credentials = MessageIntegrityCredentials::ShortTerm(ShortTermCredentials {
22642357
password: "VOkJxbRl1RmTxUk/WvJxBt".to_owned(),
22652358
});
2266-
assert!(matches!(msg.validate_integrity(&credentials), Ok(())));
2359+
assert!(matches!(
2360+
msg.validate_integrity(&credentials),
2361+
Ok(IntegrityAlgorithm::Sha1)
2362+
));
22672363
builder
22682364
.add_message_integrity(&credentials, IntegrityAlgorithm::Sha1)
22692365
.unwrap();

0 commit comments

Comments
 (0)