/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 | | } |