Coverage Report

Created: 2026-03-23 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/scrypt-0.11.0/src/params.rs
Line
Count
Source
1
use core::mem::size_of;
2
3
use crate::errors::InvalidParams;
4
5
#[cfg(feature = "simple")]
6
use password_hash::{errors::InvalidValue, Error, ParamsString, PasswordHash};
7
8
/// The Scrypt parameter values.
9
#[derive(Clone, Copy, Debug)]
10
pub struct Params {
11
    pub(crate) log_n: u8,
12
    pub(crate) r: u32,
13
    pub(crate) p: u32,
14
    #[allow(dead_code)] // this field is used only with the `PasswordHasher` impl
15
    pub(crate) len: usize,
16
}
17
18
impl Params {
19
    /// Recommended log₂ of the Scrypt parameter `N`: CPU/memory cost.
20
    pub const RECOMMENDED_LOG_N: u8 = 17;
21
22
    /// Recommended Scrypt parameter `r`: block size.
23
    pub const RECOMMENDED_R: u32 = 8;
24
25
    /// Recommended Scrypt parameter `p`: parallelism.
26
    pub const RECOMMENDED_P: u32 = 1;
27
28
    /// Recommended Scrypt parameter `Key length`.
29
    pub const RECOMMENDED_LEN: usize = 32;
30
31
    /// Create a new instance of [`Params`].
32
    ///
33
    /// # Arguments
34
    /// - `log_n` - The log₂ of the Scrypt parameter `N`
35
    /// - `r` - The Scrypt parameter `r`
36
    /// - `p` - The Scrypt parameter `p`
37
    /// - `len` - The Scrypt parameter `Key length`
38
    ///
39
    /// # Conditions
40
    /// - `log_n` must be less than `64`
41
    /// - `r` must be greater than `0` and less than or equal to `4294967295`
42
    /// - `p` must be greater than `0` and less than `4294967295`
43
    /// - `len` must be greater than `9` and less than or equal to `64`
44
0
    pub fn new(log_n: u8, r: u32, p: u32, len: usize) -> Result<Params, InvalidParams> {
45
0
        let cond1 = (log_n as usize) < usize::BITS as usize;
46
0
        let cond2 = size_of::<usize>() >= size_of::<u32>();
47
0
        let cond3 = r <= usize::MAX as u32 && p < usize::MAX as u32;
48
0
        let cond4 = (10..=64).contains(&len);
49
0
        if !(r > 0 && p > 0 && cond1 && (cond2 || cond3) && cond4) {
50
0
            return Err(InvalidParams);
51
0
        }
52
53
0
        let r = r as usize;
54
0
        let p = p as usize;
55
56
0
        let n: usize = 1 << log_n;
57
58
        // check that r * 128 doesn't overflow
59
0
        let r128 = r.checked_mul(128).ok_or(InvalidParams)?;
60
61
        // check that n * r * 128 doesn't overflow
62
0
        r128.checked_mul(n).ok_or(InvalidParams)?;
63
64
        // check that p * r * 128 doesn't overflow
65
0
        r128.checked_mul(p).ok_or(InvalidParams)?;
66
67
        // This check required by Scrypt:
68
        // check: n < 2^(128 * r / 8)
69
        // r * 16 won't overflow since r128 didn't
70
0
        if (log_n as usize) >= r * 16 {
71
0
            return Err(InvalidParams);
72
0
        }
73
74
        // This check required by Scrypt:
75
        // check: p <= ((2^32-1) * 32) / (128 * r)
76
        // It takes a bit of re-arranging to get the check above into this form,
77
        // but it is indeed the same.
78
0
        if r * p >= 0x4000_0000 {
79
0
            return Err(InvalidParams);
80
0
        }
81
82
0
        Ok(Params {
83
0
            log_n,
84
0
            r: r as u32,
85
0
            p: p as u32,
86
0
            len,
87
0
        })
88
0
    }
89
90
    /// Recommended values sufficient for most use-cases
91
    /// - `log_n = 15` (`n = 32768`)
92
    /// - `r = 8`
93
    /// - `p = 1`
94
0
    pub fn recommended() -> Params {
95
0
        Params {
96
0
            log_n: Self::RECOMMENDED_LOG_N,
97
0
            r: Self::RECOMMENDED_R,
98
0
            p: Self::RECOMMENDED_P,
99
0
            len: Self::RECOMMENDED_LEN,
100
0
        }
101
0
    }
102
103
    /// log₂ of the Scrypt parameter `N`, the work factor.
104
    ///
105
    /// Memory and CPU usage scale linearly with `N`.
106
0
    pub fn log_n(&self) -> u8 {
107
0
        self.log_n
108
0
    }
109
110
    /// `r` parameter: resource usage.
111
    ///
112
    /// scrypt iterates 2*r times. Memory and CPU time scale linearly
113
    /// with this parameter.
114
0
    pub fn r(&self) -> u32 {
115
0
        self.r
116
0
    }
117
118
    /// `p` parameter: parallelization.
119
0
    pub fn p(&self) -> u32 {
120
0
        self.p
121
0
    }
122
}
123
124
impl Default for Params {
125
0
    fn default() -> Params {
126
0
        Params::recommended()
127
0
    }
128
}
129
130
#[cfg(feature = "simple")]
131
#[cfg_attr(docsrs, doc(cfg(feature = "simple")))]
132
impl<'a> TryFrom<&'a PasswordHash<'a>> for Params {
133
    type Error = password_hash::Error;
134
135
0
    fn try_from(hash: &'a PasswordHash<'a>) -> Result<Self, password_hash::Error> {
136
0
        let mut log_n = Self::RECOMMENDED_LOG_N;
137
0
        let mut r = Self::RECOMMENDED_R;
138
0
        let mut p = Self::RECOMMENDED_P;
139
140
0
        if hash.version.is_some() {
141
0
            return Err(Error::Version);
142
0
        }
143
144
0
        for (ident, value) in hash.params.iter() {
145
0
            match ident.as_str() {
146
0
                "ln" => {
147
0
                    log_n = value
148
0
                        .decimal()?
149
0
                        .try_into()
150
0
                        .map_err(|_| InvalidValue::Malformed.param_error())?
151
                }
152
0
                "r" => r = value.decimal()?,
153
0
                "p" => p = value.decimal()?,
154
0
                _ => return Err(password_hash::Error::ParamNameInvalid),
155
            }
156
        }
157
158
0
        let len = hash
159
0
            .hash
160
0
            .map(|out| out.len())
161
0
            .unwrap_or(Self::RECOMMENDED_LEN);
162
0
        Params::new(log_n, r, p, len).map_err(|_| InvalidValue::Malformed.param_error())
163
0
    }
164
}
165
166
#[cfg(feature = "simple")]
167
#[cfg_attr(docsrs, doc(cfg(feature = "simple")))]
168
impl TryFrom<Params> for ParamsString {
169
    type Error = password_hash::Error;
170
171
0
    fn try_from(input: Params) -> Result<ParamsString, password_hash::Error> {
172
0
        let mut output = ParamsString::new();
173
0
        output.add_decimal("ln", input.log_n as u32)?;
174
0
        output.add_decimal("r", input.r)?;
175
0
        output.add_decimal("p", input.p)?;
176
0
        Ok(output)
177
0
    }
178
}