stun_types/attribute/
user.rs1use alloc::borrow::ToOwned;
12use alloc::string::{String, ToString};
13use core::convert::TryFrom;
14
15use crate::message::{StunParseError, StunWriteError};
16
17use super::{
18 Attribute, AttributeExt, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
19 AttributeWriteExt, RawAttribute,
20};
21
22#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct Username {
25 user: String,
26}
27
28impl AttributeStaticType for Username {
29 const TYPE: AttributeType = AttributeType(0x0006);
30}
31
32impl Attribute for Username {
33 fn get_type(&self) -> AttributeType {
34 Self::TYPE
35 }
36
37 fn length(&self) -> u16 {
38 self.user.len() as u16
39 }
40}
41
42impl AttributeWrite for Username {
43 fn to_raw(&self) -> RawAttribute<'_> {
44 RawAttribute::new(Username::TYPE, self.user.as_bytes())
45 }
46 fn write_into_unchecked(&self, dest: &mut [u8]) {
47 let len = self.padded_len();
48 self.write_header_unchecked(dest);
49 let offset = 4 + self.user.len();
50 dest[4..offset].copy_from_slice(self.user.as_bytes());
51 if len > offset {
52 dest[offset..len].fill(0);
53 }
54 }
55}
56
57impl AttributeFromRaw<'_> for Username {
58 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
59 where
60 Self: Sized,
61 {
62 Self::try_from(raw)
63 }
64}
65
66impl TryFrom<&RawAttribute<'_>> for Username {
67 type Error = StunParseError;
68
69 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
70 raw.check_type_and_len(Self::TYPE, ..=513)?;
71 Ok(Self {
72 user: core::str::from_utf8(&raw.value)
73 .map_err(|_| StunParseError::InvalidAttributeData)?
74 .to_owned(),
75 })
76 }
77}
78
79impl Username {
80 pub fn new(user: &str) -> Result<Self, StunWriteError> {
96 if user.len() > 513 {
97 return Err(StunWriteError::TooLarge {
98 expected: 513,
99 actual: user.len(),
100 });
101 }
102 Ok(Self {
104 user: user.to_owned(),
105 })
106 }
107
108 pub fn username(&self) -> &str {
118 &self.user
119 }
120}
121
122impl core::fmt::Display for Username {
123 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
124 write!(f, "{}: '{}'", Self::TYPE, self.user)
125 }
126}
127
128#[derive(Debug, Clone, PartialEq, Eq)]
130pub struct Userhash {
131 hash: [u8; 32],
132}
133
134impl AttributeStaticType for Userhash {
135 const TYPE: AttributeType = AttributeType(0x001E);
136}
137
138impl Attribute for Userhash {
139 fn get_type(&self) -> AttributeType {
140 Self::TYPE
141 }
142
143 fn length(&self) -> u16 {
144 32
145 }
146}
147
148impl AttributeWrite for Userhash {
149 fn to_raw(&self) -> RawAttribute<'_> {
150 RawAttribute::new(Userhash::TYPE, &self.hash)
151 }
152
153 fn write_into_unchecked(&self, dest: &mut [u8]) {
154 self.write_header_unchecked(dest);
155 dest[4..4 + self.hash.len()].copy_from_slice(&self.hash);
156 }
157}
158
159impl AttributeFromRaw<'_> for Userhash {
160 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
161 where
162 Self: Sized,
163 {
164 Self::try_from(raw)
165 }
166}
167
168impl TryFrom<&RawAttribute<'_>> for Userhash {
169 type Error = StunParseError;
170
171 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
172 raw.check_type_and_len(Self::TYPE, 32..=32)?;
173 let hash: [u8; 32] = raw.value[..32].try_into().unwrap();
175 Ok(Self { hash })
176 }
177}
178
179impl Userhash {
180 pub fn new(hash: [u8; 32]) -> Self {
191 Self { hash }
192 }
193
194 pub fn hash(&self) -> &[u8; 32] {
205 &self.hash
206 }
207
208 pub fn compute(user: &str, realm: &str) -> [u8; 32] {
216 let data = user.to_string() + ":" + realm;
217 use sha2::{Digest, Sha256};
218 let ret = Sha256::digest(data);
219 #[allow(deprecated)]
220 {
221 ret.as_slice().try_into().unwrap()
222 }
223 }
224}
225
226impl core::fmt::Display for Userhash {
227 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
228 write!(f, "{}: 0x", Self::TYPE)?;
229 for val in self.hash.iter() {
230 write!(f, "{val:02x}")?;
231 }
232 Ok(())
233 }
234}
235
236#[cfg(test)]
237mod tests {
238 use super::*;
239 use alloc::vec;
240 use alloc::vec::Vec;
241 use byteorder::{BigEndian, ByteOrder};
242 use tracing::trace;
243
244 #[test]
245 fn username() {
246 let _log = crate::tests::test_init_log();
247 let s = "woohoo!";
248 let user = Username::new(s).unwrap();
249 trace!("{user}");
250 assert_eq!(user.username(), s);
251 assert_eq!(user.length() as usize, s.len());
252 }
253
254 #[test]
255 fn username_raw() {
256 let _log = crate::tests::test_init_log();
257 let s = "woohoo!";
258 let user = Username::new(s).unwrap();
259 let raw = RawAttribute::from(&user);
260 trace!("{raw}");
261 assert_eq!(raw.get_type(), Username::TYPE);
262 let user2 = Username::try_from(&raw).unwrap();
263 assert_eq!(user2.username(), s);
264 }
265
266 #[test]
267 fn username_raw_wrong_type() {
268 let _log = crate::tests::test_init_log();
269 let s = "woohoo!";
270 let user = Username::new(s).unwrap();
271 let raw = RawAttribute::from(&user);
272 let mut data: Vec<_> = raw.into();
274 BigEndian::write_u16(&mut data[0..2], 0);
275 assert!(matches!(
276 Username::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
277 Err(StunParseError::WrongAttributeImplementation)
278 ));
279 }
280
281 #[test]
282 fn username_write_into() {
283 let _log = crate::tests::test_init_log();
284 let s = "woohoo!";
285 let user = Username::new(s).unwrap();
286 let raw = RawAttribute::from(&user);
287
288 let mut dest = vec![0; raw.padded_len()];
289 user.write_into(&mut dest).unwrap();
290 let raw = RawAttribute::from_bytes(&dest).unwrap();
291 let user2 = Username::try_from(&raw).unwrap();
292 assert_eq!(user2.username(), s);
293 }
294
295 #[test]
296 #[should_panic(expected = "out of range")]
297 fn username_write_into_unchecked() {
298 let _log = crate::tests::test_init_log();
299 let s = "woohoo!";
300 let user = Username::new(s).unwrap();
301 let raw = RawAttribute::from(&user);
302
303 let mut dest = vec![0; raw.padded_len() - 1];
304 user.write_into_unchecked(&mut dest);
305 }
306
307 #[test]
308 fn username_not_utf8() {
309 let _log = crate::tests::test_init_log();
310 let attr = Username::new("user").unwrap();
311 let raw = RawAttribute::from(&attr);
312 let mut data = raw.to_bytes();
313 data[6] = 0x88;
314 assert!(matches!(
315 Username::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
316 Err(StunParseError::InvalidAttributeData)
317 ));
318 }
319
320 #[test]
321 fn username_new_too_large() {
322 let _log = crate::tests::test_init_log();
323 let mut large = String::new();
324 for _i in 0..64 {
325 large.push_str("abcdefgh");
326 }
327 large.push_str("ab");
328 assert!(matches!(
329 Username::new(&large),
330 Err(StunWriteError::TooLarge {
331 expected: 513,
332 actual: 514
333 })
334 ));
335 }
336
337 #[test]
338 fn userhash() {
339 let _log = crate::tests::test_init_log();
340 let hash = Userhash::compute("username", "realm1");
341 let attr = Userhash::new(hash);
342 trace!("{attr}");
343 assert_eq!(attr.hash(), &hash);
344 assert_eq!(attr.length(), 32);
345 }
346
347 #[test]
348 fn userhash_raw() {
349 let _log = crate::tests::test_init_log();
350 let hash = Userhash::compute("username", "realm1");
351 let attr = Userhash::new(hash);
352 let raw = RawAttribute::from(&attr);
353 trace!("{raw}");
354 assert_eq!(raw.get_type(), Userhash::TYPE);
355 let mapped2 = Userhash::try_from(&raw).unwrap();
356 assert_eq!(mapped2.hash(), &hash);
357 }
358
359 #[test]
360 fn userhash_raw_short() {
361 let _log = crate::tests::test_init_log();
362 let hash = Userhash::compute("username", "realm1");
363 let attr = Userhash::new(hash);
364 let raw = RawAttribute::from(&attr);
365 let mut data: Vec<_> = raw.clone().into();
367 let len = data.len();
368 BigEndian::write_u16(&mut data[2..4], len as u16 - 4 - 1);
369 assert!(matches!(
370 Userhash::try_from(&RawAttribute::from_bytes(data[..len - 1].as_ref()).unwrap()),
371 Err(StunParseError::Truncated {
372 expected: 32,
373 actual: 31
374 })
375 ));
376 }
377
378 #[test]
379 fn userhash_raw_wrong_type() {
380 let _log = crate::tests::test_init_log();
381 let hash = Userhash::compute("username", "realm1");
382 let attr = Userhash::new(hash);
383 let raw = RawAttribute::from(&attr);
384 let mut data: Vec<_> = raw.into();
386 BigEndian::write_u16(&mut data[0..2], 0);
387 assert!(matches!(
388 Userhash::from_raw_ref(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
389 Err(StunParseError::WrongAttributeImplementation)
390 ));
391 }
392
393 #[test]
394 fn userhash_write_into() {
395 let _log = crate::tests::test_init_log();
396 let hash = Userhash::compute("username", "realm1");
397 let attr = Userhash::new(hash);
398 let raw = RawAttribute::from(&attr);
399
400 let mut dest = vec![0; raw.padded_len()];
401 attr.write_into(&mut dest).unwrap();
402 let raw = RawAttribute::from_bytes(&dest).unwrap();
403 let hash2 = Userhash::try_from(&raw).unwrap();
404 assert_eq!(hash2.hash(), &hash);
405 }
406
407 #[test]
408 #[should_panic(expected = "out of range")]
409 fn userhash_write_into_unchecked() {
410 let _log = crate::tests::test_init_log();
411 let hash = Userhash::compute("username", "realm1");
412 let attr = Userhash::new(hash);
413 let raw = RawAttribute::from(&attr);
414
415 let mut dest = vec![0; raw.padded_len() - 1];
416 attr.write_into_unchecked(&mut dest);
417 }
418}