Skip to main content

stun_types/attribute/
user.rs

1// Copyright (C) 2020 Matthew Waters <matthew@centricular.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8//
9// SPDX-License-Identifier: MIT OR Apache-2.0
10
11use 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/// The username [`Attribute`]
23#[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    /// Create a new [`Username`] [`Attribute`]
81    ///
82    /// # Errors
83    ///
84    /// - When the length of the username is longer than allowed in a STUN
85    ///   [`Message`](crate::message::Message)
86    /// - TODO: If converting through SASLPrep fails
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// # use stun_types::attribute::*;
92    /// let username = Username::new ("user").unwrap();
93    /// assert_eq!(username.username(), "user");
94    /// ```
95    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        // TODO: SASLPrep RFC4013 requirements
103        Ok(Self {
104            user: user.to_owned(),
105        })
106    }
107
108    /// The username stored in a [`Username`] [`Attribute`]
109    ///
110    /// # Examples
111    ///
112    /// ```
113    /// # use stun_types::attribute::*;
114    /// let username = Username::new ("user").unwrap();
115    /// assert_eq!(username.username(), "user");
116    /// ```
117    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/// The Userhash [`Attribute`]
129#[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        // sized checked earlier
174        let hash: [u8; 32] = raw.value[..32].try_into().unwrap();
175        Ok(Self { hash })
176    }
177}
178
179impl Userhash {
180    /// Create a new Userhash [`Attribute`]
181    ///
182    /// # Examples
183    ///
184    /// ```
185    /// # use stun_types::attribute::*;
186    /// let value = [0;32];
187    /// let user = Userhash::new(value);
188    /// assert_eq!(user.hash(), &value);
189    /// ```
190    pub fn new(hash: [u8; 32]) -> Self {
191        Self { hash }
192    }
193
194    /// Retrieve the hash value
195    ///
196    /// # Examples
197    ///
198    /// ```
199    /// # use stun_types::attribute::*;
200    /// let value = [0;32];
201    /// let user = Userhash::new(value);
202    /// assert_eq!(user.hash(), &value);
203    /// ```
204    pub fn hash(&self) -> &[u8; 32] {
205        &self.hash
206    }
207
208    /// Compute the hash of a specified block of data as required by STUN
209    ///
210    /// # Examples
211    /// ```
212    /// # use stun_types::attribute::*;
213    /// assert_eq!(Userhash::compute("user", "realm"), [106, 48, 41, 17, 107, 71, 170, 152, 188, 170, 50, 83, 153, 115, 61, 193, 162, 60, 213, 126, 38, 184, 27, 239, 63, 246, 83, 28, 230, 36, 226, 218]);
214    /// ```
215    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        // provide incorrectly typed data
273        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        // truncate by one byte
366        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        // provide incorrectly typed data
385        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}