Coverage Report

Created: 2025-10-28 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/password-hash-0.4.2/src/params.rs
Line
Count
Source
1
//! Algorithm parameters.
2
3
use crate::errors::InvalidValue;
4
use crate::{
5
    value::{Decimal, Value},
6
    Encoding, Error, Ident, Result,
7
};
8
use core::{
9
    fmt::{self, Debug, Write},
10
    iter::FromIterator,
11
    str::{self, FromStr},
12
};
13
14
/// Individual parameter name/value pair.
15
pub type Pair<'a> = (Ident<'a>, Value<'a>);
16
17
/// Delimiter character between name/value pairs.
18
pub(crate) const PAIR_DELIMITER: char = '=';
19
20
/// Delimiter character between parameters.
21
pub(crate) const PARAMS_DELIMITER: char = ',';
22
23
/// Maximum number of supported parameters.
24
const MAX_LENGTH: usize = 127;
25
26
/// Error message used with `expect` for when internal invariants are violated
27
/// (i.e. the contents of a [`ParamsString`] should always be valid)
28
const INVARIANT_VIOLATED_MSG: &str = "PHC params invariant violated";
29
30
/// Algorithm parameter string.
31
///
32
/// The [PHC string format specification][1] defines a set of optional
33
/// algorithm-specific name/value pairs which can be encoded into a
34
/// PHC-formatted parameter string as follows:
35
///
36
/// ```text
37
/// $<param>=<value>(,<param>=<value>)*
38
/// ```
39
///
40
/// This type represents that set of parameters.
41
///
42
/// [1]: https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md#specification
43
#[derive(Clone, Default, Eq, PartialEq)]
44
pub struct ParamsString(Buffer);
45
46
impl ParamsString {
47
    /// Create new empty [`ParamsString`].
48
0
    pub fn new() -> Self {
49
0
        Self::default()
50
0
    }
51
52
    /// Add the given byte value to the [`ParamsString`], encoding it as "B64".
53
0
    pub fn add_b64_bytes<'a>(&mut self, name: impl TryInto<Ident<'a>>, bytes: &[u8]) -> Result<()> {
54
0
        if !self.is_empty() {
55
0
            self.0
56
0
                .write_char(PARAMS_DELIMITER)
57
0
                .map_err(|_| Error::ParamsMaxExceeded)?
58
0
        }
59
60
0
        let name = name.try_into().map_err(|_| Error::ParamNameInvalid)?;
61
62
        // Add param name
63
0
        let offset = self.0.length;
64
0
        if write!(self.0, "{}=", name).is_err() {
65
0
            self.0.length = offset;
66
0
            return Err(Error::ParamsMaxExceeded);
67
0
        }
68
69
        // Encode B64 value
70
0
        let offset = self.0.length as usize;
71
0
        let written = Encoding::B64
72
0
            .encode(bytes, &mut self.0.bytes[offset..])?
73
0
            .len();
74
75
0
        self.0.length += written as u8;
76
0
        Ok(())
77
0
    }
78
79
    /// Add a key/value pair with a decimal value to the [`ParamsString`].
80
0
    pub fn add_decimal<'a>(&mut self, name: impl TryInto<Ident<'a>>, value: Decimal) -> Result<()> {
81
0
        let name = name.try_into().map_err(|_| Error::ParamNameInvalid)?;
82
0
        self.add(name, value)
83
0
    }
84
85
    /// Add a key/value pair with a string value to the [`ParamsString`].
86
0
    pub fn add_str<'a>(
87
0
        &mut self,
88
0
        name: impl TryInto<Ident<'a>>,
89
0
        value: impl TryInto<Value<'a>>,
90
0
    ) -> Result<()> {
91
0
        let name = name.try_into().map_err(|_| Error::ParamNameInvalid)?;
92
93
0
        let value = value
94
0
            .try_into()
95
0
            .map_err(|_| Error::ParamValueInvalid(InvalidValue::InvalidFormat))?;
96
97
0
        self.add(name, value)
98
0
    }
99
100
    /// Borrow the contents of this [`ParamsString`] as a byte slice.
101
0
    pub fn as_bytes(&self) -> &[u8] {
102
0
        self.as_str().as_bytes()
103
0
    }
104
105
    /// Borrow the contents of this [`ParamsString`] as a `str`.
106
0
    pub fn as_str(&self) -> &str {
107
0
        self.0.as_ref()
108
0
    }
109
110
    /// Get the count of the number ASCII characters in this [`ParamsString`].
111
0
    pub fn len(&self) -> usize {
112
0
        self.as_str().len()
113
0
    }
114
115
    /// Is this set of parameters empty?
116
0
    pub fn is_empty(&self) -> bool {
117
0
        self.len() == 0
118
0
    }
119
120
    /// Iterate over the parameters.
121
0
    pub fn iter(&self) -> Iter<'_> {
122
0
        Iter::new(self.as_str())
123
0
    }
124
125
    /// Get a parameter [`Value`] by name.
126
0
    pub fn get<'a>(&self, name: impl TryInto<Ident<'a>>) -> Option<Value<'_>> {
127
0
        let name = name.try_into().ok()?;
128
129
0
        for (n, v) in self.iter() {
130
0
            if name == n {
131
0
                return Some(v);
132
0
            }
133
        }
134
135
0
        None
136
0
    }
137
138
    /// Get a parameter as a `str`.
139
0
    pub fn get_str<'a>(&self, name: impl TryInto<Ident<'a>>) -> Option<&str> {
140
0
        self.get(name).map(|value| value.as_str())
141
0
    }
142
143
    /// Get a parameter as a [`Decimal`].
144
    ///
145
    /// See [`Value::decimal`] for format information.
146
0
    pub fn get_decimal<'a>(&self, name: impl TryInto<Ident<'a>>) -> Option<Decimal> {
147
0
        self.get(name).and_then(|value| value.decimal().ok())
148
0
    }
149
150
    /// Add a value to this [`ParamsString`] using the provided callback.
151
0
    fn add(&mut self, name: Ident<'_>, value: impl fmt::Display) -> Result<()> {
152
0
        if self.get(name).is_some() {
153
0
            return Err(Error::ParamNameDuplicated);
154
0
        }
155
156
0
        let orig_len = self.0.length;
157
158
0
        if !self.is_empty() {
159
0
            self.0
160
0
                .write_char(PARAMS_DELIMITER)
161
0
                .map_err(|_| Error::ParamsMaxExceeded)?
162
0
        }
163
164
0
        if write!(self.0, "{}={}", name, value).is_err() {
165
0
            self.0.length = orig_len;
166
0
            return Err(Error::ParamsMaxExceeded);
167
0
        }
168
169
0
        Ok(())
170
0
    }
171
}
172
173
impl FromStr for ParamsString {
174
    type Err = Error;
175
176
0
    fn from_str(s: &str) -> Result<Self> {
177
0
        if s.as_bytes().len() > MAX_LENGTH {
178
0
            return Err(Error::ParamsMaxExceeded);
179
0
        }
180
181
0
        if s.is_empty() {
182
0
            return Ok(ParamsString::new());
183
0
        }
184
185
        // Validate the string is well-formed
186
0
        for mut param in s.split(PARAMS_DELIMITER).map(|p| p.split(PAIR_DELIMITER)) {
187
            // Validate name
188
0
            param
189
0
                .next()
190
0
                .ok_or(Error::ParamNameInvalid)
191
0
                .and_then(Ident::try_from)?;
192
193
            // Validate value
194
0
            param
195
0
                .next()
196
0
                .ok_or(Error::ParamValueInvalid(InvalidValue::Malformed))
197
0
                .and_then(Value::try_from)?;
198
199
0
            if param.next().is_some() {
200
0
                return Err(Error::ParamValueInvalid(InvalidValue::Malformed));
201
0
            }
202
        }
203
204
0
        let mut bytes = [0u8; MAX_LENGTH];
205
0
        bytes[..s.as_bytes().len()].copy_from_slice(s.as_bytes());
206
207
0
        Ok(Self(Buffer {
208
0
            bytes,
209
0
            length: s.as_bytes().len() as u8,
210
0
        }))
211
0
    }
212
}
213
214
impl<'a> FromIterator<Pair<'a>> for ParamsString {
215
0
    fn from_iter<I>(iter: I) -> Self
216
0
    where
217
0
        I: IntoIterator<Item = Pair<'a>>,
218
    {
219
0
        let mut params = ParamsString::new();
220
221
0
        for pair in iter {
222
0
            params.add_str(pair.0, pair.1).expect("PHC params error");
223
0
        }
224
225
0
        params
226
0
    }
227
}
228
229
impl fmt::Display for ParamsString {
230
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
231
0
        f.write_str(self.as_str())
232
0
    }
233
}
234
235
impl fmt::Debug for ParamsString {
236
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237
0
        f.debug_map().entries(self.iter()).finish()
238
0
    }
239
}
240
241
/// Iterator over algorithm parameters stored in a [`ParamsString`] struct.
242
pub struct Iter<'a> {
243
    inner: Option<str::Split<'a, char>>,
244
}
245
246
impl<'a> Iter<'a> {
247
    /// Create a new [`Iter`].
248
0
    fn new(s: &'a str) -> Self {
249
0
        if s.is_empty() {
250
0
            Self { inner: None }
251
        } else {
252
0
            Self {
253
0
                inner: Some(s.split(PARAMS_DELIMITER)),
254
0
            }
255
        }
256
0
    }
257
}
258
259
impl<'a> Iterator for Iter<'a> {
260
    type Item = Pair<'a>;
261
262
0
    fn next(&mut self) -> Option<Pair<'a>> {
263
0
        let mut param = self.inner.as_mut()?.next()?.split(PAIR_DELIMITER);
264
265
0
        let name = param
266
0
            .next()
267
0
            .and_then(|id| Ident::try_from(id).ok())
268
0
            .expect(INVARIANT_VIOLATED_MSG);
269
270
0
        let value = param
271
0
            .next()
272
0
            .and_then(|value| Value::try_from(value).ok())
273
0
            .expect(INVARIANT_VIOLATED_MSG);
274
275
0
        debug_assert_eq!(param.next(), None);
276
0
        Some((name, value))
277
0
    }
278
}
279
280
/// Parameter buffer.
281
#[derive(Clone, Debug, Eq)]
282
struct Buffer {
283
    /// Byte array containing an ASCII-encoded string.
284
    bytes: [u8; MAX_LENGTH],
285
286
    /// Length of the string in ASCII characters (i.e. bytes).
287
    length: u8,
288
}
289
290
impl AsRef<str> for Buffer {
291
0
    fn as_ref(&self) -> &str {
292
0
        str::from_utf8(&self.bytes[..(self.length as usize)]).expect(INVARIANT_VIOLATED_MSG)
293
0
    }
294
}
295
296
impl Default for Buffer {
297
0
    fn default() -> Buffer {
298
0
        Buffer {
299
0
            bytes: [0u8; MAX_LENGTH],
300
0
            length: 0,
301
0
        }
302
0
    }
303
}
304
305
impl PartialEq for Buffer {
306
0
    fn eq(&self, other: &Self) -> bool {
307
        // Ensure comparisons always honor the initialized portion of the buffer
308
0
        self.as_ref().eq(other.as_ref())
309
0
    }
310
}
311
312
impl Write for Buffer {
313
0
    fn write_str(&mut self, input: &str) -> fmt::Result {
314
0
        let bytes = input.as_bytes();
315
0
        let length = self.length as usize;
316
317
0
        if length + bytes.len() > MAX_LENGTH {
318
0
            return Err(fmt::Error);
319
0
        }
320
321
0
        self.bytes[length..(length + bytes.len())].copy_from_slice(bytes);
322
0
        self.length += bytes.len() as u8;
323
324
0
        Ok(())
325
0
    }
326
}
327
328
#[cfg(test)]
329
mod tests {
330
    use super::{Error, FromIterator, Ident, ParamsString, Value};
331
332
    #[cfg(feature = "alloc")]
333
    use alloc::string::ToString;
334
    use core::str::FromStr;
335
336
    #[test]
337
    fn add() {
338
        let mut params = ParamsString::new();
339
        params.add_str("a", "1").unwrap();
340
        params.add_decimal("b", 2).unwrap();
341
        params.add_str("c", "3").unwrap();
342
343
        assert_eq!(params.iter().count(), 3);
344
        assert_eq!(params.get_decimal("a").unwrap(), 1);
345
        assert_eq!(params.get_decimal("b").unwrap(), 2);
346
        assert_eq!(params.get_decimal("c").unwrap(), 3);
347
    }
348
349
    #[test]
350
    #[cfg(feature = "alloc")]
351
    fn add_b64_bytes() {
352
        let mut params = ParamsString::new();
353
        params.add_b64_bytes("a", &[1]).unwrap();
354
        params.add_b64_bytes("b", &[2, 3]).unwrap();
355
        params.add_b64_bytes("c", &[4, 5, 6]).unwrap();
356
        assert_eq!(params.to_string(), "a=AQ,b=AgM,c=BAUG");
357
    }
358
359
    #[test]
360
    fn duplicate_names() {
361
        let name = Ident::new("a").unwrap();
362
        let mut params = ParamsString::new();
363
        params.add_decimal(name, 1).unwrap();
364
365
        let err = params.add_decimal(name, 2u32.into()).err().unwrap();
366
        assert_eq!(err, Error::ParamNameDuplicated);
367
    }
368
369
    #[test]
370
    fn from_iter() {
371
        let params = ParamsString::from_iter(
372
            [
373
                (Ident::new("a").unwrap(), Value::try_from("1").unwrap()),
374
                (Ident::new("b").unwrap(), Value::try_from("2").unwrap()),
375
                (Ident::new("c").unwrap(), Value::try_from("3").unwrap()),
376
            ]
377
            .iter()
378
            .cloned(),
379
        );
380
381
        assert_eq!(params.iter().count(), 3);
382
        assert_eq!(params.get_decimal("a").unwrap(), 1);
383
        assert_eq!(params.get_decimal("b").unwrap(), 2);
384
        assert_eq!(params.get_decimal("c").unwrap(), 3);
385
    }
386
387
    #[test]
388
    fn iter() {
389
        let mut params = ParamsString::new();
390
        params.add_str("a", "1").unwrap();
391
        params.add_str("b", "2").unwrap();
392
        params.add_str("c", "3").unwrap();
393
394
        let mut i = params.iter();
395
396
        for (name, value) in &[("a", "1"), ("b", "2"), ("c", "3")] {
397
            let name = Ident::new(name).unwrap();
398
            let value = Value::try_from(*value).unwrap();
399
            assert_eq!(i.next(), Some((name, value)));
400
        }
401
402
        assert_eq!(i.next(), None);
403
    }
404
405
    //
406
    // `FromStr` tests
407
    //
408
409
    #[test]
410
    fn parse_empty() {
411
        let params = ParamsString::from_str("").unwrap();
412
        assert!(params.is_empty());
413
    }
414
415
    #[test]
416
    fn parse_one() {
417
        let params = ParamsString::from_str("a=1").unwrap();
418
        assert_eq!(params.iter().count(), 1);
419
        assert_eq!(params.get("a").unwrap().decimal().unwrap(), 1);
420
    }
421
422
    #[test]
423
    fn parse_many() {
424
        let params = ParamsString::from_str("a=1,b=2,c=3").unwrap();
425
        assert_eq!(params.iter().count(), 3);
426
        assert_eq!(params.get_decimal("a").unwrap(), 1);
427
        assert_eq!(params.get_decimal("b").unwrap(), 2);
428
        assert_eq!(params.get_decimal("c").unwrap(), 3);
429
    }
430
431
    //
432
    // `Display` tests
433
    //
434
435
    #[test]
436
    #[cfg(feature = "alloc")]
437
    fn display_empty() {
438
        let params = ParamsString::new();
439
        assert_eq!(params.to_string(), "");
440
    }
441
442
    #[test]
443
    #[cfg(feature = "alloc")]
444
    fn display_one() {
445
        let params = ParamsString::from_str("a=1").unwrap();
446
        assert_eq!(params.to_string(), "a=1");
447
    }
448
449
    #[test]
450
    #[cfg(feature = "alloc")]
451
    fn display_many() {
452
        let params = ParamsString::from_str("a=1,b=2,c=3").unwrap();
453
        assert_eq!(params.to_string(), "a=1,b=2,c=3");
454
    }
455
}