1#![no_std]
2#![doc = include_str!("../README.md")]
3#![cfg_attr(docsrs, feature(doc_cfg))]
4#![doc(
5 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
6 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
7)]
8#![deny(unsafe_code)]
9#![warn(missing_docs, rust_2018_idioms)]
10
11#![cfg_attr(feature = "getrandom", doc = "```")]
14#![cfg_attr(not(feature = "getrandom"), doc = "```ignore")]
15mod errors;
32mod params;
33
34#[cfg(feature = "password-hash")]
35mod algorithm;
36#[cfg(feature = "password-hash")]
37mod mcf;
38
39pub use crate::{
40 errors::{Error, Result},
41 params::Params,
42};
43
44#[cfg(feature = "password-hash")]
45pub use {
46 crate::{
47 algorithm::Algorithm,
48 mcf::{PasswordHashRef, ShaCrypt},
49 },
50 password_hash::{self, CustomizedPasswordHasher, PasswordHasher, PasswordVerifier},
51};
52
53#[cfg(all(feature = "password-hash", feature = "alloc"))]
54pub use ::mcf::PasswordHash;
55
56use sha2::{Digest, Sha256, Sha512};
57
58pub const BLOCK_SIZE_SHA256: usize = 32;
60
61pub const BLOCK_SIZE_SHA512: usize = 64;
63
64#[must_use]
73pub fn sha256_crypt(password: &[u8], salt: &[u8], params: Params) -> [u8; BLOCK_SIZE_SHA256] {
74 let pw_len = password.len();
75
76 let salt_len = salt.len();
77 let salt = match salt_len {
78 0..=15 => &salt[0..salt_len],
79 _ => &salt[0..16],
80 };
81 let salt_len = salt.len();
82
83 let digest_a = sha256_crypt_intermediate(password, salt);
84
85 let mut hasher_alt = Sha256::default();
87
88 for _ in 0..pw_len {
90 hasher_alt.update(password);
91 }
92
93 let dp = hasher_alt.finalize();
95
96 hasher_alt = Sha256::default();
102
103 for _ in 0..(16 + digest_a[0] as usize) {
106 hasher_alt.update(salt);
107 }
108
109 let ds = hasher_alt.finalize();
112
113 let mut digest_c = digest_a;
118 for i in 0..params.rounds {
121 let mut hasher = Sha256::default();
123
124 if (i & 1) != 0 {
126 hash_byte_seq(pw_len, &dp, &mut hasher);
127 } else {
128 hasher.update(digest_c);
129 }
130
131 if i % 3 != 0 {
133 hash_byte_seq(salt_len, &ds, &mut hasher);
134 }
135
136 if i % 7 != 0 {
138 hash_byte_seq(pw_len, &dp, &mut hasher);
139 }
140
141 if (i & 1) != 0 {
143 hasher.update(digest_c);
144 } else {
145 hash_byte_seq(pw_len, &dp, &mut hasher);
146 }
147
148 digest_c.clone_from_slice(&hasher.finalize());
149 }
150
151 digest_c
152}
153
154#[must_use]
163pub fn sha512_crypt(password: &[u8], salt: &[u8], params: Params) -> [u8; BLOCK_SIZE_SHA512] {
164 let pw_len = password.len();
165
166 let salt_len = salt.len();
167 let salt = match salt_len {
168 0..=15 => &salt[0..salt_len],
169 _ => &salt[0..16],
170 };
171 let salt_len = salt.len();
172
173 let digest_a = sha512_crypt_intermediate(password, salt);
174
175 let mut hasher_alt = Sha512::default();
177
178 for _ in 0..pw_len {
180 hasher_alt.update(password);
181 }
182
183 let dp = hasher_alt.finalize();
185
186 hasher_alt = Sha512::default();
192
193 for _ in 0..(16 + digest_a[0] as usize) {
196 hasher_alt.update(salt);
197 }
198
199 let ds = hasher_alt.finalize();
202
203 let mut digest_c = digest_a;
208 for i in 0..params.rounds {
211 let mut hasher = Sha512::default();
213
214 if (i & 1) != 0 {
216 hash_byte_seq(pw_len, &dp, &mut hasher);
217 } else {
218 hasher.update(digest_c);
219 }
220
221 if i % 3 != 0 {
223 hash_byte_seq(salt_len, &ds, &mut hasher);
224 }
225
226 if i % 7 != 0 {
228 hash_byte_seq(pw_len, &dp, &mut hasher);
229 }
230
231 if (i & 1) != 0 {
233 hasher.update(digest_c);
234 } else {
235 hash_byte_seq(pw_len, &dp, &mut hasher);
236 }
237
238 digest_c.clone_from_slice(&hasher.finalize());
239 }
240
241 digest_c
242}
243
244fn sha256_crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA256] {
245 let pw_len = password.len();
246
247 let mut hasher = Sha256::default();
248 hasher.update(password);
249 hasher.update(salt);
250
251 let mut hasher_alt = Sha256::default();
253 hasher_alt.update(password);
255 hasher_alt.update(salt);
257 hasher_alt.update(password);
259 let digest_b = hasher_alt.finalize();
261
262 for _ in 0..(pw_len / BLOCK_SIZE_SHA256) {
264 hasher.update(digest_b);
265 }
266 hasher.update(&digest_b[..(pw_len % BLOCK_SIZE_SHA256)]);
268
269 let mut n = pw_len;
271 for _ in 0..pw_len {
272 if n == 0 {
273 break;
274 }
275 if (n & 1) != 0 {
276 hasher.update(digest_b);
277 } else {
278 hasher.update(password);
279 }
280 n >>= 1;
281 }
282
283 hasher.finalize().into()
285}
286
287fn sha512_crypt_intermediate(password: &[u8], salt: &[u8]) -> [u8; BLOCK_SIZE_SHA512] {
288 let pw_len = password.len();
289
290 let mut hasher = Sha512::default();
291 hasher.update(password);
292 hasher.update(salt);
293
294 let mut hasher_alt = Sha512::default();
296 hasher_alt.update(password);
298 hasher_alt.update(salt);
300 hasher_alt.update(password);
302 let digest_b = hasher_alt.finalize();
304
305 for _ in 0..(pw_len / BLOCK_SIZE_SHA512) {
307 hasher.update(digest_b);
308 }
309 hasher.update(&digest_b[..(pw_len % BLOCK_SIZE_SHA512)]);
311
312 let mut n = pw_len;
314 for _ in 0..pw_len {
315 if n == 0 {
316 break;
317 }
318 if (n & 1) != 0 {
319 hasher.update(digest_b);
320 } else {
321 hasher.update(password);
322 }
323 n >>= 1;
324 }
325
326 hasher.finalize().into()
328}
329
330fn hash_byte_seq<D: Digest>(len: usize, fill_from: &[u8], digest: &mut D) {
331 let bs = fill_from.len();
332 for _ in 0..(len / bs) {
333 digest.update(fill_from);
334 }
335 digest.update(&fill_from[..(len % bs)]);
336}