@@ -130,9 +130,6 @@ pub enum StunParseError {
130
130
/// The provided data does not match the message
131
131
#[ error( "The provided data does not match the message" ) ]
132
132
DataMismatch ,
133
- /// The message has multiple integrity attributes
134
- #[ error( "Multiple integrity types are used in this message" ) ]
135
- DuplicateIntegrity ,
136
133
/// The attribute contains invalid data
137
134
#[ error( "The attribute contains invalid data" ) ]
138
135
InvalidAttributeData ,
@@ -873,8 +870,14 @@ impl<'a> Message<'a> {
873
870
874
871
let mut data_offset = MessageHeader :: LENGTH ;
875
872
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 ;
878
881
while !data. is_empty ( ) {
879
882
let attr = RawAttribute :: from_bytes ( data) . map_err ( |e| {
880
883
warn ! (
@@ -894,24 +897,40 @@ impl<'a> Message<'a> {
894
897
}
895
898
} ) ?;
896
899
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
+ }
910
914
}
911
915
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
+ }
915
934
}
916
935
let padded_len = padded_attr_size ( & attr) ;
917
936
if padded_len > data. len ( ) {
@@ -925,7 +944,6 @@ impl<'a> Message<'a> {
925
944
} ) ;
926
945
}
927
946
if attr. get_type ( ) == Fingerprint :: TYPE {
928
- seen_fingerprint = true ;
929
947
let f = Fingerprint :: from_raw ( & attr) ?;
930
948
let msg_fingerprint = f. fingerprint ( ) ;
931
949
let mut fingerprint_data = orig_data[ ..data_offset] . to_vec ( ) ;
@@ -980,20 +998,19 @@ impl<'a> Message<'a> {
980
998
pub fn validate_integrity (
981
999
& self ,
982
1000
credentials : & MessageIntegrityCredentials ,
983
- ) -> Result < ( ) , StunParseError > {
1001
+ ) -> Result < IntegrityAlgorithm , StunParseError > {
984
1002
debug ! ( "using credentials {credentials:?}" ) ;
985
1003
let raw_sha1 = self . raw_attribute ( MessageIntegrity :: TYPE ) ;
986
1004
let raw_sha256 = self . raw_attribute ( MessageIntegritySha256 :: TYPE ) ;
987
1005
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
+ }
989
1010
( Some ( sha1) , None ) => {
990
1011
let integrity = MessageIntegrity :: try_from ( & sha1) ?;
991
1012
( IntegrityAlgorithm :: Sha1 , integrity. hmac ( ) . to_vec ( ) )
992
1013
}
993
- ( None , Some ( sha256) ) => {
994
- let integrity = MessageIntegritySha256 :: try_from ( & sha256) ?;
995
- ( IntegrityAlgorithm :: Sha256 , integrity. hmac ( ) . to_vec ( ) )
996
- }
997
1014
( None , None ) => return Err ( StunParseError :: MissingAttribute ( MessageIntegrity :: TYPE ) ) ,
998
1015
} ;
999
1016
@@ -1017,11 +1034,12 @@ impl<'a> Message<'a> {
1017
1034
& mut hmac_data[ 2 ..4 ] ,
1018
1035
data_offset as u16 + 24 - MessageHeader :: LENGTH as u16 ,
1019
1036
) ;
1020
- return MessageIntegrity :: verify (
1037
+ MessageIntegrity :: verify (
1021
1038
& hmac_data,
1022
1039
& key,
1023
1040
msg_hmac. as_slice ( ) . try_into ( ) . unwrap ( ) ,
1024
- ) ;
1041
+ ) ?;
1042
+ return Ok ( algo) ;
1025
1043
} else if algo == IntegrityAlgorithm :: Sha256
1026
1044
&& attr. get_type ( ) == MessageIntegritySha256 :: TYPE
1027
1045
{
@@ -1036,7 +1054,8 @@ impl<'a> Message<'a> {
1036
1054
& mut hmac_data[ 2 ..4 ] ,
1037
1055
data_offset as u16 + attr. length ( ) + 4 - MessageHeader :: LENGTH as u16 ,
1038
1056
) ;
1039
- return MessageIntegritySha256 :: verify ( & hmac_data, & key, & msg_hmac) ;
1057
+ MessageIntegritySha256 :: verify ( & hmac_data, & key, & msg_hmac) ?;
1058
+ return Ok ( algo) ;
1040
1059
}
1041
1060
let padded_len = padded_attr_size ( & attr) ;
1042
1061
// checked when initially parsing.
@@ -1447,11 +1466,9 @@ impl<'a> MessageBuilder<'a> {
1447
1466
/// Err(StunWriteError::AttributeExists(MessageIntegrity::TYPE)),
1448
1467
/// ));
1449
1468
///
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());
1455
1472
///
1456
1473
/// let data = message.build();
1457
1474
/// let message = Message::from_bytes(&data).unwrap();
@@ -1471,7 +1488,7 @@ impl<'a> MessageBuilder<'a> {
1471
1488
credentials : & MessageIntegrityCredentials ,
1472
1489
algorithm : IntegrityAlgorithm ,
1473
1490
) -> Result < ( ) , StunWriteError > {
1474
- if self . has_attribute ( MessageIntegrity :: TYPE ) {
1491
+ if self . has_attribute ( MessageIntegrity :: TYPE ) && algorithm == IntegrityAlgorithm :: Sha1 {
1475
1492
return Err ( StunWriteError :: AttributeExists ( MessageIntegrity :: TYPE ) ) ;
1476
1493
}
1477
1494
if self . has_attribute ( MessageIntegritySha256 :: TYPE ) {
@@ -1483,6 +1500,16 @@ impl<'a> MessageBuilder<'a> {
1483
1500
return Err ( StunWriteError :: FingerprintExists ) ;
1484
1501
}
1485
1502
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
+ ) {
1486
1513
let key = credentials. make_hmac_key ( ) ;
1487
1514
match algorithm {
1488
1515
IntegrityAlgorithm :: Sha1 => {
@@ -1495,12 +1522,11 @@ impl<'a> MessageBuilder<'a> {
1495
1522
let bytes = self . integrity_bytes_from_message ( 36 ) ;
1496
1523
let integrity = MessageIntegritySha256 :: compute ( & bytes, & key) . unwrap ( ) ;
1497
1524
self . attributes . push (
1498
- RawAttribute :: from ( & MessageIntegritySha256 :: new ( integrity. as_slice ( ) ) ? )
1525
+ RawAttribute :: from ( & MessageIntegritySha256 :: new ( integrity. as_slice ( ) ) . unwrap ( ) )
1499
1526
. into_owned ( ) ,
1500
1527
) ;
1501
1528
}
1502
1529
}
1503
- Ok ( ( ) )
1504
1530
}
1505
1531
1506
1532
/// Adds [`Fingerprint`] attribute to a [`Message`]
@@ -1531,6 +1557,13 @@ impl<'a> MessageBuilder<'a> {
1531
1557
if self . has_attribute ( Fingerprint :: TYPE ) {
1532
1558
return Err ( StunWriteError :: AttributeExists ( Fingerprint :: TYPE ) ) ;
1533
1559
}
1560
+
1561
+ self . add_fingerprint_unchecked ( ) ;
1562
+
1563
+ Ok ( ( ) )
1564
+ }
1565
+
1566
+ fn add_fingerprint_unchecked ( & mut self ) {
1534
1567
// fingerprint is computed using all the data up to (exclusive of) the FINGERPRINT
1535
1568
// but with a length field including the FINGERPRINT attribute...
1536
1569
let mut bytes = self . build ( ) ;
@@ -1540,7 +1573,6 @@ impl<'a> MessageBuilder<'a> {
1540
1573
let fingerprint = Fingerprint :: compute ( & bytes) ;
1541
1574
self . attributes
1542
1575
. push ( RawAttribute :: from ( & Fingerprint :: new ( fingerprint) ) ) ;
1543
- Ok ( ( ) )
1544
1576
}
1545
1577
1546
1578
/// Add a `Attribute` to this `Message`. Only one `AttributeType` can be added for each
@@ -1744,9 +1776,13 @@ mod tests {
1744
1776
msg. add_message_integrity( & credentials, IntegrityAlgorithm :: Sha1 ) ,
1745
1777
Err ( StunWriteError :: AttributeExists ( MessageIntegrity :: TYPE ) )
1746
1778
) ) ;
1779
+ msg. add_message_integrity ( & credentials, IntegrityAlgorithm :: Sha256 )
1780
+ . unwrap ( ) ;
1747
1781
assert ! ( matches!(
1748
1782
msg. add_message_integrity( & credentials, IntegrityAlgorithm :: Sha256 ) ,
1749
- Err ( StunWriteError :: AttributeExists ( MessageIntegrity :: TYPE ) )
1783
+ Err ( StunWriteError :: AttributeExists (
1784
+ MessageIntegritySha256 :: TYPE
1785
+ ) )
1750
1786
) ) ;
1751
1787
let software = Software :: new ( "s" ) . unwrap ( ) ;
1752
1788
assert ! ( matches!(
@@ -1755,6 +1791,21 @@ mod tests {
1755
1791
) ) ;
1756
1792
}
1757
1793
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
+
1758
1809
#[ test]
1759
1810
fn add_attribute_after_integrity ( ) {
1760
1811
let _log = crate :: tests:: test_init_log ( ) ;
@@ -1844,6 +1895,29 @@ mod tests {
1844
1895
}
1845
1896
}
1846
1897
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
+
1847
1921
#[ test]
1848
1922
fn parse_attribute_after_fingerprint ( ) {
1849
1923
let _log = crate :: tests:: test_init_log ( ) ;
@@ -1861,6 +1935,19 @@ mod tests {
1861
1935
) ) ;
1862
1936
}
1863
1937
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
+
1864
1951
#[ test]
1865
1952
fn add_attribute_after_fingerprint ( ) {
1866
1953
let _log = crate :: tests:: test_init_log ( ) ;
@@ -2102,7 +2189,10 @@ mod tests {
2102
2189
let credentials = MessageIntegrityCredentials :: ShortTerm ( ShortTermCredentials {
2103
2190
password : "VOkJxbRl1RmTxUk/WvJxBt" . to_owned ( ) ,
2104
2191
} ) ;
2105
- assert ! ( matches!( msg. validate_integrity( & credentials) , Ok ( ( ) ) ) ) ;
2192
+ assert ! ( matches!(
2193
+ msg. validate_integrity( & credentials) ,
2194
+ Ok ( IntegrityAlgorithm :: Sha1 )
2195
+ ) ) ;
2106
2196
builder
2107
2197
. add_message_integrity ( & credentials, IntegrityAlgorithm :: Sha1 )
2108
2198
. unwrap ( ) ;
@@ -2183,7 +2273,7 @@ mod tests {
2183
2273
} ) ;
2184
2274
let ret = msg. validate_integrity ( & credentials) ;
2185
2275
debug ! ( "{:?}" , ret) ;
2186
- assert ! ( matches!( ret, Ok ( ( ) ) ) ) ;
2276
+ assert ! ( matches!( ret, Ok ( IntegrityAlgorithm :: Sha1 ) ) ) ;
2187
2277
builder
2188
2278
. add_message_integrity ( & credentials, IntegrityAlgorithm :: Sha1 )
2189
2279
. unwrap ( ) ;
@@ -2263,7 +2353,10 @@ mod tests {
2263
2353
let credentials = MessageIntegrityCredentials :: ShortTerm ( ShortTermCredentials {
2264
2354
password : "VOkJxbRl1RmTxUk/WvJxBt" . to_owned ( ) ,
2265
2355
} ) ;
2266
- assert ! ( matches!( msg. validate_integrity( & credentials) , Ok ( ( ) ) ) ) ;
2356
+ assert ! ( matches!(
2357
+ msg. validate_integrity( & credentials) ,
2358
+ Ok ( IntegrityAlgorithm :: Sha1 )
2359
+ ) ) ;
2267
2360
builder
2268
2361
. add_message_integrity ( & credentials, IntegrityAlgorithm :: Sha1 )
2269
2362
. unwrap ( ) ;
0 commit comments