stun_rs/attributes/stun/
password_algorithm.rs

1use crate::attributes::{stunt_attribute, DecodeAttributeValue, EncodeAttributeValue};
2use crate::common::check_buffer_boundaries;
3use crate::context::{AttributeDecoderContext, AttributeEncoderContext};
4use crate::StunError;
5use crate::{Algorithm, AlgorithmId};
6use byteorder::{BigEndian, ByteOrder};
7use std::convert::TryInto;
8
9// 0                   1                   2                   3
10// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
11// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
12// |          Algorithm           |  Algorithm Parameters Length   |
13// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
14// |                    Algorithm Parameters (variable)
15// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16
17const PASSWORD_ALGORITHM: u16 = 0x001D;
18
19/// The PASSWORD-ALGORITHM attribute is present only in requests. It
20/// contains the algorithm that the server must use to derive a key from
21/// the long-term password.
22///
23/// # Examples
24///```rust
25/// # use stun_rs::attributes::stun::PasswordAlgorithm;
26/// # use stun_rs::{Algorithm, AlgorithmId};
27/// // Creates a MD5 password algorithm without parameters
28/// let attr = PasswordAlgorithm::new(Algorithm::from(AlgorithmId::MD5));
29/// assert_eq!(attr.algorithm(), AlgorithmId::MD5);
30/// assert_eq!(attr.parameters(), None);
31///
32/// // Creates a custom password algorithm with parameters
33/// let params = [0x01, 0x02, 0x03, 0x04, 0x05];
34/// let algorithm = Algorithm::new(AlgorithmId::Unassigned(255), params.as_ref());
35/// let attr = PasswordAlgorithm::new(algorithm);
36/// assert_eq!(attr.algorithm(), AlgorithmId::Unassigned(255));
37/// assert_eq!(attr.parameters(), Some(params.as_ref()));
38///```
39#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct PasswordAlgorithm(Algorithm);
41
42impl PasswordAlgorithm {
43    /// Creates a new [`PasswordAlgorithm`] attribute.
44    /// # Attributes:
45    /// * `algorithm` - The [Algorithm].
46    /// * `value` - Specific parameters for the algorithm, if any.
47    pub fn new(algorithm: Algorithm) -> Self {
48        Self(algorithm)
49    }
50
51    /// Returns the algorithm
52    pub fn algorithm(&self) -> AlgorithmId {
53        self.0.algorithm()
54    }
55
56    /// Returns the parameters required by the algorithm.
57    pub fn parameters(&self) -> Option<&[u8]> {
58        self.0.parameters()
59    }
60}
61
62impl AsRef<Algorithm> for PasswordAlgorithm {
63    fn as_ref(&self) -> &Algorithm {
64        &self.0
65    }
66}
67
68impl DecodeAttributeValue for PasswordAlgorithm {
69    fn decode(ctx: AttributeDecoderContext) -> Result<(Self, usize), StunError> {
70        // Check if we can pick algorithm (2 bytes) and parameters length (2 bytes)
71        let mut size: usize = 4;
72        let raw_value = ctx.raw_value();
73
74        check_buffer_boundaries(raw_value, size)?;
75
76        let algorithm = BigEndian::read_u16(&raw_value[..2]);
77        let param_length = BigEndian::read_u16(&raw_value[2..4]);
78
79        size += param_length as usize;
80        check_buffer_boundaries(raw_value, size)?;
81
82        let params = &raw_value[4..(param_length + 4).into()];
83
84        let algorithm_param = Algorithm::new(
85            AlgorithmId::from(algorithm),
86            (param_length > 0).then_some(params),
87        );
88        Ok((Self(algorithm_param), size))
89    }
90}
91
92impl EncodeAttributeValue for PasswordAlgorithm {
93    fn encode(&self, mut ctx: AttributeEncoderContext) -> Result<usize, StunError> {
94        let params_len = match self.0.parameters().as_ref() {
95            Some(buf) => buf.len(),
96            _ => 0,
97        };
98
99        // 2 bytes algorithm + 2 bytes parameter length + parameter value length
100        let len = 4 + params_len;
101        let raw_value = ctx.raw_value_mut();
102
103        check_buffer_boundaries(raw_value, len)?;
104
105        BigEndian::write_u16(&mut raw_value[..2], self.0.algorithm().into());
106        BigEndian::write_u16(&mut raw_value[2..4], params_len.try_into()?);
107
108        if let Some(buf) = self.0.parameters() {
109            raw_value[4..params_len + 4].clone_from_slice(buf);
110        };
111
112        Ok(len)
113    }
114}
115
116impl crate::attributes::AsVerifiable for PasswordAlgorithm {}
117
118stunt_attribute!(PasswordAlgorithm, PASSWORD_ALGORITHM);
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use crate::error::StunErrorType;
124    use crate::StunAttribute;
125
126    #[test]
127    fn password_algorithm() {
128        let algorithm = Algorithm::from(AlgorithmId::MD5);
129        let attr = PasswordAlgorithm::new(algorithm);
130
131        let algorithm = Algorithm::from(AlgorithmId::MD5);
132        assert_eq!(AlgorithmId::MD5, attr.algorithm());
133        assert_eq!(&algorithm, attr.as_ref());
134        assert!(attr.parameters().is_none());
135    }
136
137    #[test]
138    fn decode_password_algorithm() {
139        let dummy_msg: [u8; 0] = [0x0; 0];
140        let buffer = [0x00, 0x00, 0x00, 0x00];
141        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
142        let (attr, size) =
143            PasswordAlgorithm::decode(ctx).expect("Could not decode PasswordAlgorithm");
144        assert_eq!(size, 4);
145        assert_eq!(attr.algorithm(), AlgorithmId::Reserved);
146        assert_eq!(attr.parameters(), None);
147
148        let buffer = [0x00, 0x01, 0x00, 0x00];
149        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
150        let (attr, size) =
151            PasswordAlgorithm::decode(ctx).expect("Could not decode PasswordAlgorithm");
152        assert_eq!(size, 4);
153        assert_eq!(attr.algorithm(), AlgorithmId::MD5);
154        assert_eq!(attr.parameters(), None);
155
156        let buffer = [0x00, 0x02, 0x00, 0x00];
157        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
158        let (attr, size) =
159            PasswordAlgorithm::decode(ctx).expect("Could not decode PasswordAlgorithm");
160        assert_eq!(size, 4);
161        assert_eq!(attr.algorithm(), AlgorithmId::SHA256);
162        assert_eq!(attr.parameters(), None);
163
164        let buffer = [0x00, 0x03, 0x00, 0x02, 0x45, 0x23];
165        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
166        let (attr, size) =
167            PasswordAlgorithm::decode(ctx).expect("Could not decode PasswordAlgorithm");
168        assert_eq!(size, 6);
169        assert_eq!(attr.algorithm(), AlgorithmId::Unassigned(3));
170        assert_eq!(attr.parameters(), Some([0x45, 0x23].as_slice()));
171    }
172
173    #[test]
174    fn decode_password_algorithm_error() {
175        let dummy_msg: [u8; 0] = [0x0; 0];
176        let buffer = [];
177        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
178        let result = PasswordAlgorithm::decode(ctx);
179        assert_eq!(
180            result.expect_err("Error expected"),
181            StunErrorType::SmallBuffer
182        );
183
184        let buffer = [0x00, 0x01, 0x00];
185        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
186        let result = PasswordAlgorithm::decode(ctx);
187        assert_eq!(
188            result.expect_err("Error expected"),
189            StunErrorType::SmallBuffer
190        );
191
192        let buffer = [0x00, 0x03, 0x00, 0x02, 0x45];
193        let ctx = AttributeDecoderContext::new(None, &dummy_msg, &buffer);
194        let result = PasswordAlgorithm::decode(ctx);
195        assert_eq!(
196            result.expect_err("Error expected"),
197            StunErrorType::SmallBuffer
198        );
199    }
200
201    #[test]
202    fn encode_password_algorithm() {
203        let dummy_msg: [u8; 0] = [0x0; 0];
204        let algorithm = Algorithm::from(AlgorithmId::MD5);
205        let attr = PasswordAlgorithm::new(algorithm);
206        let mut buffer: [u8; 4] = [0x0; 4];
207        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
208        let result = attr.encode(ctx);
209        assert_eq!(result, Ok(4));
210        let expected_buffer = [0x00, 0x01, 0x00, 0x00];
211        assert_eq!(&buffer[..], &expected_buffer[..]);
212
213        let params = [1, 2, 3, 4, 5];
214        let algorithm = Algorithm::new(AlgorithmId::Unassigned(255), params.as_ref());
215        let attr = PasswordAlgorithm::new(algorithm);
216        let mut buffer: [u8; 9] = [0x0; 9];
217        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
218        let result = attr.encode(ctx);
219        assert_eq!(result, Ok(9));
220        let expected_buffer = [0x00, 0xFF, 0x00, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05];
221        assert_eq!(&buffer[..], &expected_buffer[..]);
222    }
223
224    #[test]
225    fn encode_password_algorithm_error() {
226        let dummy_msg: [u8; 0] = [0x0; 0];
227        let algorithm = Algorithm::from(AlgorithmId::MD5);
228        let attr = PasswordAlgorithm::new(algorithm);
229
230        let mut buffer = [];
231        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
232        let result = attr.encode(ctx);
233        assert_eq!(
234            result.expect_err("Error expected"),
235            StunErrorType::SmallBuffer
236        );
237
238        let mut buffer: [u8; 3] = [0x0; 3];
239        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
240        let result = attr.encode(ctx);
241        assert_eq!(
242            result.expect_err("Error expected"),
243            StunErrorType::SmallBuffer
244        );
245
246        let params = [1, 2, 3];
247        let algorithm = Algorithm::new(AlgorithmId::Unassigned(255), params.as_ref());
248        let attr = PasswordAlgorithm::new(algorithm);
249        let mut buffer: [u8; 6] = [0x0; 6];
250        let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut buffer);
251        let result = attr.encode(ctx);
252        assert_eq!(
253            result.expect_err("Error expected"),
254            StunErrorType::SmallBuffer
255        );
256    }
257
258    #[test]
259    fn password_algorithm_stunt_attribute() {
260        let algorithm = Algorithm::from(AlgorithmId::MD5);
261        let attr = StunAttribute::PasswordAlgorithm(PasswordAlgorithm::new(algorithm));
262        assert!(attr.is_password_algorithm());
263        assert!(attr.as_password_algorithm().is_ok());
264        assert!(attr.as_unknown().is_err());
265
266        assert!(attr.attribute_type().is_comprehension_required());
267        assert!(!attr.attribute_type().is_comprehension_optional());
268
269        let dbg_fmt = format!("{:?}", attr);
270        assert_eq!(
271            "PasswordAlgorithm(PasswordAlgorithm(Algorithm { algorithm: MD5, params: None }))",
272            dbg_fmt
273        );
274    }
275}