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 #[allow(deprecated)]
218 {
219 ret.as_slice().try_into().unwrap()
220 }
221 }
222}
223
224impl core::fmt::Display for Userhash {
225 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
226 write!(f, "{}: 0x", Self::TYPE)?;
227 for val in self.hash.iter() {
228 write!(f, "{val:02x}")?;
229 }
230 Ok(())
231 }
232}
233
234#[cfg(test)]
235mod tests {
236 use super::*;
237 use alloc::vec;
238 use alloc::vec::Vec;
239 use byteorder::{BigEndian, ByteOrder};
240 use tracing::trace;
241
242 #[test]
243 fn username() {
244 let _log = crate::tests::test_init_log();
245 let s = "woohoo!";
246 let user = Username::new(s).unwrap();
247 trace!("{user}");
248 assert_eq!(user.username(), s);
249 assert_eq!(user.length() as usize, s.len());
250 }
251
252 #[test]
253 fn username_raw() {
254 let _log = crate::tests::test_init_log();
255 let s = "woohoo!";
256 let user = Username::new(s).unwrap();
257 let raw = RawAttribute::from(&user);
258 trace!("{raw}");
259 assert_eq!(raw.get_type(), Username::TYPE);
260 let user2 = Username::try_from(&raw).unwrap();
261 assert_eq!(user2.username(), s);
262 }
263
264 #[test]
265 fn username_raw_wrong_type() {
266 let _log = crate::tests::test_init_log();
267 let s = "woohoo!";
268 let user = Username::new(s).unwrap();
269 let raw = RawAttribute::from(&user);
270 let mut data: Vec<_> = raw.into();
272 BigEndian::write_u16(&mut data[0..2], 0);
273 assert!(matches!(
274 Username::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
275 Err(StunParseError::WrongAttributeImplementation)
276 ));
277 }
278
279 #[test]
280 fn username_write_into() {
281 let _log = crate::tests::test_init_log();
282 let s = "woohoo!";
283 let user = Username::new(s).unwrap();
284 let raw = RawAttribute::from(&user);
285
286 let mut dest = vec![0; raw.padded_len()];
287 user.write_into(&mut dest).unwrap();
288 let raw = RawAttribute::from_bytes(&dest).unwrap();
289 let user2 = Username::try_from(&raw).unwrap();
290 assert_eq!(user2.username(), s);
291 }
292
293 #[test]
294 #[should_panic(expected = "out of range")]
295 fn username_write_into_unchecked() {
296 let _log = crate::tests::test_init_log();
297 let s = "woohoo!";
298 let user = Username::new(s).unwrap();
299 let raw = RawAttribute::from(&user);
300
301 let mut dest = vec![0; raw.padded_len() - 1];
302 user.write_into_unchecked(&mut dest);
303 }
304
305 #[test]
306 fn username_not_utf8() {
307 let _log = crate::tests::test_init_log();
308 let attr = Username::new("user").unwrap();
309 let raw = RawAttribute::from(&attr);
310 let mut data = raw.to_bytes();
311 data[6] = 0x88;
312 assert!(matches!(
313 Username::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
314 Err(StunParseError::InvalidAttributeData)
315 ));
316 }
317
318 #[test]
319 fn username_new_too_large() {
320 let _log = crate::tests::test_init_log();
321 let mut large = String::new();
322 for _i in 0..64 {
323 large.push_str("abcdefgh");
324 }
325 large.push_str("ab");
326 assert!(matches!(
327 Username::new(&large),
328 Err(StunWriteError::TooLarge {
329 expected: 513,
330 actual: 514
331 })
332 ));
333 }
334
335 #[test]
336 fn userhash() {
337 let _log = crate::tests::test_init_log();
338 let hash = Userhash::compute("username", "realm1");
339 let attr = Userhash::new(hash);
340 trace!("{attr}");
341 assert_eq!(attr.hash(), &hash);
342 assert_eq!(attr.length(), 32);
343 }
344
345 #[test]
346 fn userhash_raw() {
347 let _log = crate::tests::test_init_log();
348 let hash = Userhash::compute("username", "realm1");
349 let attr = Userhash::new(hash);
350 let raw = RawAttribute::from(&attr);
351 trace!("{raw}");
352 assert_eq!(raw.get_type(), Userhash::TYPE);
353 let mapped2 = Userhash::try_from(&raw).unwrap();
354 assert_eq!(mapped2.hash(), &hash);
355 }
356
357 #[test]
358 fn userhash_raw_short() {
359 let _log = crate::tests::test_init_log();
360 let hash = Userhash::compute("username", "realm1");
361 let attr = Userhash::new(hash);
362 let raw = RawAttribute::from(&attr);
363 let mut data: Vec<_> = raw.clone().into();
365 let len = data.len();
366 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
367 assert!(matches!(
368 Userhash::try_from(&RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()),
369 Err(StunParseError::Truncated {
370 expected: 32,
371 actual: 31
372 })
373 ));
374 }
375
376 #[test]
377 fn userhash_raw_wrong_type() {
378 let _log = crate::tests::test_init_log();
379 let hash = Userhash::compute("username", "realm1");
380 let attr = Userhash::new(hash);
381 let raw = RawAttribute::from(&attr);
382 let mut data: Vec<_> = raw.into();
384 BigEndian::write_u16(&mut data[0..2], 0);
385 assert!(matches!(
386 Userhash::from_raw_ref(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
387 Err(StunParseError::WrongAttributeImplementation)
388 ));
389 }
390
391 #[test]
392 fn userhash_write_into() {
393 let _log = crate::tests::test_init_log();
394 let hash = Userhash::compute("username", "realm1");
395 let attr = Userhash::new(hash);
396 let raw = RawAttribute::from(&attr);
397
398 let mut dest = vec![0; raw.padded_len()];
399 attr.write_into(&mut dest).unwrap();
400 let raw = RawAttribute::from_bytes(&dest).unwrap();
401 let hash2 = Userhash::try_from(&raw).unwrap();
402 assert_eq!(hash2.hash(), &hash);
403 }
404
405 #[test]
406 #[should_panic(expected = "out of range")]
407 fn userhash_write_into_unchecked() {
408 let _log = crate::tests::test_init_log();
409 let hash = Userhash::compute("username", "realm1");
410 let attr = Userhash::new(hash);
411 let raw = RawAttribute::from(&attr);
412
413 let mut dest = vec![0; raw.padded_len() - 1];
414 attr.write_into_unchecked(&mut dest);
415 }
416}