stun_types/attribute/
user.rs1use alloc::borrow::ToOwned;
10use alloc::string::{String, ToString};
11use core::convert::TryFrom;
12
13use crate::message::{StunParseError, StunWriteError};
14
15use super::{
16 Attribute, AttributeExt, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
17 AttributeWriteExt, RawAttribute,
18};
19
20#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct Username {
23 user: String,
24}
25
26impl AttributeStaticType for Username {
27 const TYPE: AttributeType = AttributeType(0x0006);
28}
29
30impl Attribute for Username {
31 fn get_type(&self) -> AttributeType {
32 Self::TYPE
33 }
34
35 fn length(&self) -> u16 {
36 self.user.len() as u16
37 }
38}
39
40impl AttributeWrite for Username {
41 fn to_raw(&self) -> RawAttribute<'_> {
42 RawAttribute::new(Username::TYPE, self.user.as_bytes())
43 }
44 fn write_into_unchecked(&self, dest: &mut [u8]) {
45 let len = self.padded_len();
46 self.write_header_unchecked(dest);
47 let offset = 4 + self.user.len();
48 dest[4..offset].copy_from_slice(self.user.as_bytes());
49 if len > offset {
50 dest[offset..len].fill(0);
51 }
52 }
53}
54
55impl AttributeFromRaw<'_> for Username {
56 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
57 where
58 Self: Sized,
59 {
60 Self::try_from(raw)
61 }
62}
63
64impl TryFrom<&RawAttribute<'_>> for Username {
65 type Error = StunParseError;
66
67 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
68 raw.check_type_and_len(Self::TYPE, ..=513)?;
69 Ok(Self {
70 user: core::str::from_utf8(&raw.value)
71 .map_err(|_| StunParseError::InvalidAttributeData)?
72 .to_owned(),
73 })
74 }
75}
76
77impl Username {
78 pub fn new(user: &str) -> Result<Self, StunWriteError> {
94 if user.len() > 513 {
95 return Err(StunWriteError::TooLarge {
96 expected: 513,
97 actual: user.len(),
98 });
99 }
100 Ok(Self {
102 user: user.to_owned(),
103 })
104 }
105
106 pub fn username(&self) -> &str {
116 &self.user
117 }
118}
119
120impl core::fmt::Display for Username {
121 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
122 write!(f, "{}: '{}'", Self::TYPE, self.user)
123 }
124}
125
126#[derive(Debug, Clone, PartialEq, Eq)]
128pub struct Userhash {
129 hash: [u8; 32],
130}
131
132impl AttributeStaticType for Userhash {
133 const TYPE: AttributeType = AttributeType(0x001E);
134}
135
136impl Attribute for Userhash {
137 fn get_type(&self) -> AttributeType {
138 Self::TYPE
139 }
140
141 fn length(&self) -> u16 {
142 32
143 }
144}
145
146impl AttributeWrite for Userhash {
147 fn to_raw(&self) -> RawAttribute<'_> {
148 RawAttribute::new(Userhash::TYPE, &self.hash)
149 }
150
151 fn write_into_unchecked(&self, dest: &mut [u8]) {
152 self.write_header_unchecked(dest);
153 dest[4..4 + self.hash.len()].copy_from_slice(&self.hash);
154 }
155}
156
157impl AttributeFromRaw<'_> for Userhash {
158 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
159 where
160 Self: Sized,
161 {
162 Self::try_from(raw)
163 }
164}
165
166impl TryFrom<&RawAttribute<'_>> for Userhash {
167 type Error = StunParseError;
168
169 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
170 raw.check_type_and_len(Self::TYPE, 32..=32)?;
171 let hash: [u8; 32] = raw.value[..32].try_into().unwrap();
173 Ok(Self { hash })
174 }
175}
176
177impl Userhash {
178 pub fn new(hash: [u8; 32]) -> Self {
189 Self { hash }
190 }
191
192 pub fn hash(&self) -> &[u8; 32] {
203 &self.hash
204 }
205
206 pub fn compute(user: &str, realm: &str) -> [u8; 32] {
214 let data = user.to_string() + ":" + realm;
215 use sha2::{Digest, Sha256};
216 let ret = Sha256::digest(data);
217 ret.as_slice().try_into().unwrap()
218 }
219}
220
221impl core::fmt::Display for Userhash {
222 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
223 write!(f, "{}: 0x", Self::TYPE)?;
224 for val in self.hash.iter() {
225 write!(f, "{val:02x}")?;
226 }
227 Ok(())
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234 use alloc::vec::Vec;
235 use byteorder::{BigEndian, ByteOrder};
236 use tracing::trace;
237
238 #[test]
239 fn username() {
240 let _log = crate::tests::test_init_log();
241 let s = "woohoo!";
242 let user = Username::new(s).unwrap();
243 trace!("{user}");
244 assert_eq!(user.username(), s);
245 assert_eq!(user.length() as usize, s.len());
246 let raw = RawAttribute::from(&user);
247 trace!("{raw}");
248 assert_eq!(raw.get_type(), Username::TYPE);
249 let user2 = Username::try_from(&raw).unwrap();
250 assert_eq!(user2.username(), s);
251 let mut data: Vec<_> = raw.into();
253 BigEndian::write_u16(&mut data[0..2], 0);
254 assert!(matches!(
255 Username::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
256 Err(StunParseError::WrongAttributeImplementation)
257 ));
258 }
259
260 #[test]
261 fn username_not_utf8() {
262 let _log = crate::tests::test_init_log();
263 let attr = Username::new("user").unwrap();
264 let raw = RawAttribute::from(&attr);
265 let mut data = raw.to_bytes();
266 data[6] = 0x88;
267 assert!(matches!(
268 Username::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
269 Err(StunParseError::InvalidAttributeData)
270 ));
271 }
272
273 #[test]
274 fn username_new_too_large() {
275 let _log = crate::tests::test_init_log();
276 let mut large = String::new();
277 for _i in 0..64 {
278 large.push_str("abcdefgh");
279 }
280 large.push_str("ab");
281 assert!(matches!(
282 Username::new(&large),
283 Err(StunWriteError::TooLarge {
284 expected: 513,
285 actual: 514
286 })
287 ));
288 }
289
290 #[test]
291 fn userhash() {
292 let _log = crate::tests::test_init_log();
293 let hash = Userhash::compute("username", "realm1");
294 let attr = Userhash::new(hash);
295 trace!("{attr}");
296 assert_eq!(attr.hash(), &hash);
297 assert_eq!(attr.length(), 32);
298 let raw = RawAttribute::from(&attr);
299 trace!("{raw}");
300 assert_eq!(raw.get_type(), Userhash::TYPE);
301 let mapped2 = Userhash::try_from(&raw).unwrap();
302 assert_eq!(mapped2.hash(), &hash);
303 let mut data: Vec<_> = raw.clone().into();
305 let len = data.len();
306 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
307 assert!(matches!(
308 Userhash::try_from(&RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()),
309 Err(StunParseError::Truncated {
310 expected: 32,
311 actual: 31
312 })
313 ));
314 let mut data: Vec<_> = raw.into();
316 BigEndian::write_u16(&mut data[0..2], 0);
317 assert!(matches!(
318 Userhash::from_raw_ref(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
319 Err(StunParseError::WrongAttributeImplementation)
320 ));
321 }
322}