Skip to main content

sha_crypt/
params.rs

1//! Algorithm parameters.
2
3use crate::{Error, Result};
4use core::{
5    default::Default,
6    fmt::{self, Display},
7    str::FromStr,
8};
9
10/// Name of the parameter when serialized in an MCF string.
11const ROUNDS_PARAM: &str = "rounds=";
12
13/// Algorithm parameters.
14#[derive(Clone, Copy, Debug, Eq, PartialEq)]
15pub struct Params {
16    /// Number of times to apply the digest function
17    pub(crate) rounds: u32,
18}
19
20impl Params {
21    /// Recommended parameters.
22    pub const RECOMMENDED: Self = Self {
23        rounds: Self::RECOMMENDED_ROUNDS,
24    };
25
26    /// Recommended number of rounds.
27    pub const RECOMMENDED_ROUNDS: u32 = 5_000;
28
29    /// Minimum number of rounds allowed.
30    pub const ROUNDS_MIN: u32 = 1_000;
31
32    /// Maximum number of rounds allowed.
33    pub const ROUNDS_MAX: u32 = 999_999_999;
34
35    /// Create new algorithm parameters.
36    ///
37    /// # Errors
38    /// Returns [`Error::RoundsInvalid`] if `rounds` is outside the range [`Params::ROUNDS_MIN`]
39    /// to [`Params::ROUNDS_MAX`] inclusive.
40    pub fn new(rounds: u32) -> Result<Params> {
41        match rounds {
42            Self::ROUNDS_MIN..=Self::ROUNDS_MAX => Ok(Params { rounds }),
43            _ => Err(Error::RoundsInvalid),
44        }
45    }
46}
47
48impl Default for Params {
49    fn default() -> Self {
50        Params::RECOMMENDED
51    }
52}
53
54impl Display for Params {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        write!(f, "rounds={}", self.rounds)
57    }
58}
59
60impl FromStr for Params {
61    type Err = Error;
62
63    fn from_str(s: &str) -> Result<Self> {
64        if s.is_empty() {
65            return Ok(Self::default());
66        }
67
68        if let Some(rounds_str) = s.strip_prefix(ROUNDS_PARAM) {
69            Self::new(rounds_str.parse().map_err(|_| Error::RoundsInvalid)?)
70        } else {
71            Err(Error::ParamsInvalid)
72        }
73    }
74}
75
76#[cfg(test)]
77mod tests {
78    use super::Params;
79
80    #[test]
81    fn test_sha256_crypt_invalid_rounds() {
82        let params = Params::new(Params::ROUNDS_MAX + 1);
83        assert!(params.is_err());
84
85        let params = Params::new(Params::ROUNDS_MIN - 1);
86        assert!(params.is_err());
87    }
88
89    #[test]
90    fn test_sha512_crypt_invalid_rounds() {
91        let params = Params::new(Params::ROUNDS_MAX + 1);
92        assert!(params.is_err());
93
94        let params = Params::new(Params::ROUNDS_MIN - 1);
95        assert!(params.is_err());
96    }
97}