stun_rs/attributes/stun/
nonce_cookie.rs1use crate::{attributes::stun::Nonce, StunError, StunErrorType};
4use base64::prelude::BASE64_STANDARD;
5use base64::Engine;
6use byteorder::{BigEndian, ByteOrder};
7use enumflags2::{bitflags, BitFlags};
8
9const NONCE_COOKIE_HEADER: &str = "obMatJos2";
10
11#[bitflags]
13#[repr(u32)]
14#[derive(Copy, Clone, Debug, Eq, PartialEq)]
15pub enum StunSecurityFeatures {
16 PasswordAlgorithms = 1 << 31,
18 UserNameAnonymity = 1 << 30,
20}
21
22impl Nonce {
23 pub fn new_nonce_cookie<S>(
26 value: S,
27 flags: Option<BitFlags<StunSecurityFeatures>>,
28 ) -> Result<Self, StunError>
29 where
30 S: AsRef<str>,
31 {
32 let features: u32 = match flags {
33 Some(flags) => flags.bits(),
34 None => 0,
35 };
36
37 let base64 = BASE64_STANDARD.encode(&features.to_be_bytes()[..3]);
38 let value = format!("{}{}{}", NONCE_COOKIE_HEADER, base64, value.as_ref());
39 Nonce::new(value)
40 }
41
42 pub fn is_nonce_cookie(&self) -> bool {
44 self.as_str().starts_with(NONCE_COOKIE_HEADER)
45 && self.as_str().len() >= NONCE_COOKIE_HEADER.len() + 4
46 }
47
48 pub fn security_features(&self) -> Result<BitFlags<StunSecurityFeatures>, StunError> {
50 self.is_nonce_cookie()
51 .then_some(())
52 .ok_or_else(|| StunError::new(StunErrorType::InvalidParam, "Not nonce cookie"))?;
53
54 let flags = &self.as_str()[NONCE_COOKIE_HEADER.len()..NONCE_COOKIE_HEADER.len() + 4];
55 let mut bytes = [0x00; 4];
56 let size = BASE64_STANDARD
57 .decode_slice(flags, &mut bytes)
58 .map_err(|_e| {
59 StunError::new(
60 StunErrorType::InvalidParam,
61 "Error decoding base64 security features",
62 )
63 })?;
64
65 (size == 3).then_some(()).ok_or_else(|| {
67 StunError::new(
68 StunErrorType::InvalidParam,
69 "Unexpected security features lenght",
70 )
71 })?;
72
73 let val = BigEndian::read_u32(&bytes);
74
75 let flags =
76 BitFlags::<StunSecurityFeatures>::from_bits_truncate_c(val, BitFlags::CONST_TOKEN);
77 Ok(flags)
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use enumflags2::make_bitflags;
84
85 use super::*;
86
87 #[test]
88 fn nonce_cookie() {
89 let value = "f//499k954d6OL34oL9FSTvy64sA";
90 let nonce = Nonce::new_nonce_cookie(value, None).expect("Can not create nonce cookie");
91 assert!(nonce.is_nonce_cookie());
92 let flags = nonce
93 .security_features()
94 .expect("Can not get feature flags");
95 assert!(!flags.contains(StunSecurityFeatures::PasswordAlgorithms));
96 assert!(!flags.contains(StunSecurityFeatures::UserNameAnonymity));
97
98 let flags: BitFlags<StunSecurityFeatures> =
99 make_bitflags!(StunSecurityFeatures::{PasswordAlgorithms});
100 let nonce =
101 Nonce::new_nonce_cookie(value, Some(flags)).expect("Can not create nonce cookie");
102 assert!(nonce.is_nonce_cookie());
103 let flags = nonce
104 .security_features()
105 .expect("Can not get feature flags");
106 assert!(flags.contains(StunSecurityFeatures::PasswordAlgorithms));
107 assert!(!flags.contains(StunSecurityFeatures::UserNameAnonymity));
108
109 let flags: BitFlags<StunSecurityFeatures> =
110 make_bitflags!(StunSecurityFeatures::{UserNameAnonymity});
111 let nonce =
112 Nonce::new_nonce_cookie(value, Some(flags)).expect("Can not create nonce cookie");
113 assert!(nonce.is_nonce_cookie());
114 let flags = nonce
115 .security_features()
116 .expect("Can not get feature flags");
117 assert!(!flags.contains(StunSecurityFeatures::PasswordAlgorithms));
118 assert!(flags.contains(StunSecurityFeatures::UserNameAnonymity));
119
120 let flags: BitFlags<StunSecurityFeatures> =
121 make_bitflags!(StunSecurityFeatures::{PasswordAlgorithms | UserNameAnonymity});
122 let nonce =
123 Nonce::new_nonce_cookie(value, Some(flags)).expect("Can not create nonce cookie");
124 assert!(nonce.is_nonce_cookie());
125 let flags = nonce
126 .security_features()
127 .expect("Can not get feature flags");
128 assert!(flags.contains(StunSecurityFeatures::PasswordAlgorithms));
129 assert!(flags.contains(StunSecurityFeatures::UserNameAnonymity));
130
131 let flags: BitFlags<StunSecurityFeatures> =
133 make_bitflags!(StunSecurityFeatures::{PasswordAlgorithms | UserNameAnonymity});
134 let nonce = Nonce::new_nonce_cookie("", Some(flags)).expect("Can not create nonce cookie");
135 assert!(nonce.is_nonce_cookie());
136 let flags = nonce
137 .security_features()
138 .expect("Can not get feature flags");
139 assert!(flags.contains(StunSecurityFeatures::PasswordAlgorithms));
140 assert!(flags.contains(StunSecurityFeatures::UserNameAnonymity));
141 }
142
143 #[test]
144 fn nonce_cookie_error() {
145 let value = String::from("f//499k954d6OL34oL9FSTvy64sA");
146 let nonce = Nonce::new(value).expect("Can not create a Nonce attribute");
147 assert!(!nonce.is_nonce_cookie());
148 let result = nonce.security_features();
149 assert_eq!(
150 result.expect_err("Error expected"),
151 StunErrorType::InvalidParam
152 );
153
154 let value = String::from("obMatJos2f//==8");
156 let nonce = Nonce::new(value).expect("Can not create a Nonce attribute");
157 assert!(nonce.is_nonce_cookie());
158 let result = nonce.security_features();
159 assert_eq!(
160 result.expect_err("Error expected"),
161 StunErrorType::InvalidParam
162 );
163 }
164}