@@ -873,8 +873,14 @@ impl<'a> Message<'a> {
873
873
874
874
let mut data_offset = MessageHeader :: LENGTH ;
875
875
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 ;
878
884
while !data. is_empty ( ) {
879
885
let attr = RawAttribute :: from_bytes ( data) . map_err ( |e| {
880
886
warn ! (
@@ -894,24 +900,40 @@ impl<'a> Message<'a> {
894
900
}
895
901
} ) ?;
896
902
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
+ }
910
917
}
911
918
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
+ }
915
937
}
916
938
let padded_len = padded_attr_size ( & attr) ;
917
939
if padded_len > data. len ( ) {
@@ -925,7 +947,6 @@ impl<'a> Message<'a> {
925
947
} ) ;
926
948
}
927
949
if attr. get_type ( ) == Fingerprint :: TYPE {
928
- seen_fingerprint = true ;
929
950
let f = Fingerprint :: from_raw ( & attr) ?;
930
951
let msg_fingerprint = f. fingerprint ( ) ;
931
952
let mut fingerprint_data = orig_data[ ..data_offset] . to_vec ( ) ;
@@ -980,20 +1001,19 @@ impl<'a> Message<'a> {
980
1001
pub fn validate_integrity (
981
1002
& self ,
982
1003
credentials : & MessageIntegrityCredentials ,
983
- ) -> Result < ( ) , StunParseError > {
1004
+ ) -> Result < IntegrityAlgorithm , StunParseError > {
984
1005
debug ! ( "using credentials {credentials:?}" ) ;
985
1006
let raw_sha1 = self . raw_attribute ( MessageIntegrity :: TYPE ) ;
986
1007
let raw_sha256 = self . raw_attribute ( MessageIntegritySha256 :: TYPE ) ;
987
1008
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
+ }
989
1013
( Some ( sha1) , None ) => {
990
1014
let integrity = MessageIntegrity :: try_from ( & sha1) ?;
991
1015
( IntegrityAlgorithm :: Sha1 , integrity. hmac ( ) . to_vec ( ) )
992
1016
}
993
- ( None , Some ( sha256) ) => {
994
- let integrity = MessageIntegritySha256 :: try_from ( & sha256) ?;
995
- ( IntegrityAlgorithm :: Sha256 , integrity. hmac ( ) . to_vec ( ) )
996
- }
997
1017
( None , None ) => return Err ( StunParseError :: MissingAttribute ( MessageIntegrity :: TYPE ) ) ,
998
1018
} ;
999
1019
@@ -1017,11 +1037,12 @@ impl<'a> Message<'a> {
1017
1037
& mut hmac_data[ 2 ..4 ] ,
1018
1038
data_offset as u16 + 24 - MessageHeader :: LENGTH as u16 ,
1019
1039
) ;
1020
- return MessageIntegrity :: verify (
1040
+ MessageIntegrity :: verify (
1021
1041
& hmac_data,
1022
1042
& key,
1023
1043
msg_hmac. as_slice ( ) . try_into ( ) . unwrap ( ) ,
1024
- ) ;
1044
+ ) ?;
1045
+ return Ok ( algo) ;
1025
1046
} else if algo == IntegrityAlgorithm :: Sha256
1026
1047
&& attr. get_type ( ) == MessageIntegritySha256 :: TYPE
1027
1048
{
@@ -1036,7 +1057,8 @@ impl<'a> Message<'a> {
1036
1057
& mut hmac_data[ 2 ..4 ] ,
1037
1058
data_offset as u16 + attr. length ( ) + 4 - MessageHeader :: LENGTH as u16 ,
1038
1059
) ;
1039
- return MessageIntegritySha256 :: verify ( & hmac_data, & key, & msg_hmac) ;
1060
+ MessageIntegritySha256 :: verify ( & hmac_data, & key, & msg_hmac) ?;
1061
+ return Ok ( algo) ;
1040
1062
}
1041
1063
let padded_len = padded_attr_size ( & attr) ;
1042
1064
// checked when initially parsing.
@@ -1447,11 +1469,9 @@ impl<'a> MessageBuilder<'a> {
1447
1469
/// Err(StunWriteError::AttributeExists(MessageIntegrity::TYPE)),
1448
1470
/// ));
1449
1471
///
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());
1455
1475
///
1456
1476
/// let data = message.build();
1457
1477
/// let message = Message::from_bytes(&data).unwrap();
@@ -1471,7 +1491,7 @@ impl<'a> MessageBuilder<'a> {
1471
1491
credentials : & MessageIntegrityCredentials ,
1472
1492
algorithm : IntegrityAlgorithm ,
1473
1493
) -> Result < ( ) , StunWriteError > {
1474
- if self . has_attribute ( MessageIntegrity :: TYPE ) {
1494
+ if self . has_attribute ( MessageIntegrity :: TYPE ) && algorithm == IntegrityAlgorithm :: Sha1 {
1475
1495
return Err ( StunWriteError :: AttributeExists ( MessageIntegrity :: TYPE ) ) ;
1476
1496
}
1477
1497
if self . has_attribute ( MessageIntegritySha256 :: TYPE ) {
@@ -1483,6 +1503,16 @@ impl<'a> MessageBuilder<'a> {
1483
1503
return Err ( StunWriteError :: FingerprintExists ) ;
1484
1504
}
1485
1505
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
+ ) {
1486
1516
let key = credentials. make_hmac_key ( ) ;
1487
1517
match algorithm {
1488
1518
IntegrityAlgorithm :: Sha1 => {
@@ -1495,12 +1525,11 @@ impl<'a> MessageBuilder<'a> {
1495
1525
let bytes = self . integrity_bytes_from_message ( 36 ) ;
1496
1526
let integrity = MessageIntegritySha256 :: compute ( & bytes, & key) . unwrap ( ) ;
1497
1527
self . attributes . push (
1498
- RawAttribute :: from ( & MessageIntegritySha256 :: new ( integrity. as_slice ( ) ) ? )
1528
+ RawAttribute :: from ( & MessageIntegritySha256 :: new ( integrity. as_slice ( ) ) . unwrap ( ) )
1499
1529
. into_owned ( ) ,
1500
1530
) ;
1501
1531
}
1502
1532
}
1503
- Ok ( ( ) )
1504
1533
}
1505
1534
1506
1535
/// Adds [`Fingerprint`] attribute to a [`Message`]
@@ -1531,6 +1560,13 @@ impl<'a> MessageBuilder<'a> {
1531
1560
if self . has_attribute ( Fingerprint :: TYPE ) {
1532
1561
return Err ( StunWriteError :: AttributeExists ( Fingerprint :: TYPE ) ) ;
1533
1562
}
1563
+
1564
+ self . add_fingerprint_unchecked ( ) ;
1565
+
1566
+ Ok ( ( ) )
1567
+ }
1568
+
1569
+ fn add_fingerprint_unchecked ( & mut self ) {
1534
1570
// fingerprint is computed using all the data up to (exclusive of) the FINGERPRINT
1535
1571
// but with a length field including the FINGERPRINT attribute...
1536
1572
let mut bytes = self . build ( ) ;
@@ -1540,7 +1576,6 @@ impl<'a> MessageBuilder<'a> {
1540
1576
let fingerprint = Fingerprint :: compute ( & bytes) ;
1541
1577
self . attributes
1542
1578
. push ( RawAttribute :: from ( & Fingerprint :: new ( fingerprint) ) ) ;
1543
- Ok ( ( ) )
1544
1579
}
1545
1580
1546
1581
/// Add a `Attribute` to this `Message`. Only one `AttributeType` can be added for each
@@ -1744,9 +1779,13 @@ mod tests {
1744
1779
msg. add_message_integrity( & credentials, IntegrityAlgorithm :: Sha1 ) ,
1745
1780
Err ( StunWriteError :: AttributeExists ( MessageIntegrity :: TYPE ) )
1746
1781
) ) ;
1782
+ msg. add_message_integrity ( & credentials, IntegrityAlgorithm :: Sha256 )
1783
+ . unwrap ( ) ;
1747
1784
assert ! ( matches!(
1748
1785
msg. add_message_integrity( & credentials, IntegrityAlgorithm :: Sha256 ) ,
1749
- Err ( StunWriteError :: AttributeExists ( MessageIntegrity :: TYPE ) )
1786
+ Err ( StunWriteError :: AttributeExists (
1787
+ MessageIntegritySha256 :: TYPE
1788
+ ) )
1750
1789
) ) ;
1751
1790
let software = Software :: new ( "s" ) . unwrap ( ) ;
1752
1791
assert ! ( matches!(
@@ -1755,6 +1794,21 @@ mod tests {
1755
1794
) ) ;
1756
1795
}
1757
1796
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
+
1758
1812
#[ test]
1759
1813
fn add_attribute_after_integrity ( ) {
1760
1814
let _log = crate :: tests:: test_init_log ( ) ;
@@ -1844,6 +1898,29 @@ mod tests {
1844
1898
}
1845
1899
}
1846
1900
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
+
1847
1924
#[ test]
1848
1925
fn parse_attribute_after_fingerprint ( ) {
1849
1926
let _log = crate :: tests:: test_init_log ( ) ;
@@ -1861,6 +1938,19 @@ mod tests {
1861
1938
) ) ;
1862
1939
}
1863
1940
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
+
1864
1954
#[ test]
1865
1955
fn add_attribute_after_fingerprint ( ) {
1866
1956
let _log = crate :: tests:: test_init_log ( ) ;
@@ -2102,7 +2192,10 @@ mod tests {
2102
2192
let credentials = MessageIntegrityCredentials :: ShortTerm ( ShortTermCredentials {
2103
2193
password : "VOkJxbRl1RmTxUk/WvJxBt" . to_owned ( ) ,
2104
2194
} ) ;
2105
- assert ! ( matches!( msg. validate_integrity( & credentials) , Ok ( ( ) ) ) ) ;
2195
+ assert ! ( matches!(
2196
+ msg. validate_integrity( & credentials) ,
2197
+ Ok ( IntegrityAlgorithm :: Sha1 )
2198
+ ) ) ;
2106
2199
builder
2107
2200
. add_message_integrity ( & credentials, IntegrityAlgorithm :: Sha1 )
2108
2201
. unwrap ( ) ;
@@ -2183,7 +2276,7 @@ mod tests {
2183
2276
} ) ;
2184
2277
let ret = msg. validate_integrity ( & credentials) ;
2185
2278
debug ! ( "{:?}" , ret) ;
2186
- assert ! ( matches!( ret, Ok ( ( ) ) ) ) ;
2279
+ assert ! ( matches!( ret, Ok ( IntegrityAlgorithm :: Sha1 ) ) ) ;
2187
2280
builder
2188
2281
. add_message_integrity ( & credentials, IntegrityAlgorithm :: Sha1 )
2189
2282
. unwrap ( ) ;
@@ -2263,7 +2356,10 @@ mod tests {
2263
2356
let credentials = MessageIntegrityCredentials :: ShortTerm ( ShortTermCredentials {
2264
2357
password : "VOkJxbRl1RmTxUk/WvJxBt" . to_owned ( ) ,
2265
2358
} ) ;
2266
- assert ! ( matches!( msg. validate_integrity( & credentials) , Ok ( ( ) ) ) ) ;
2359
+ assert ! ( matches!(
2360
+ msg. validate_integrity( & credentials) ,
2361
+ Ok ( IntegrityAlgorithm :: Sha1 )
2362
+ ) ) ;
2267
2363
builder
2268
2364
. add_message_integrity ( & credentials, IntegrityAlgorithm :: Sha1 )
2269
2365
. unwrap ( ) ;
0 commit comments