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")]
15#[macro_use]
33extern crate alloc;
34
35mod errors;
36mod params;
37
38#[cfg(feature = "password-hash")]
39mod algorithm;
40#[cfg(feature = "password-hash")]
41mod mcf;
42
43pub use crate::{
44 errors::{Error, Result},
45 params::Params,
46};
47
48#[cfg(feature = "password-hash")]
49pub use {
50 crate::{
51 algorithm::Algorithm,
52 mcf::{PasswordHash, PasswordHashRef, ShaCrypt},
53 },
54 password_hash::{self, CustomizedPasswordHasher, PasswordHasher, PasswordVerifier},
55};
56
57use alloc::vec::Vec;
58use sha2::{Digest, Sha256, Sha512};
59
60pub const BLOCK_SIZE_SHA256: usize = 32;
62
63pub const BLOCK_SIZE_SHA512: usize = 64;
65
66pub fn sha256_crypt(password: &[u8], salt: &[u8], params: Params) -> [u8; BLOCK_SIZE_SHA256] {
75 let pw_len = password.len();
76
77 let salt_len = salt.len();
78 let salt = match salt_len {
79 0..=15 => &salt[0..salt_len],
80 _ => &salt[0..16],
81 };
82 let salt_len = salt.len();
83
84 let digest_a = sha256_crypt_intermediate(password, salt);
85
86 let mut hasher_alt = Sha256::default();
88
89 for _ in 0..pw_len {
91 hasher_alt.update(password);
92 }
93
94 let dp = hasher_alt.finalize();
96
97 let p_vec = produce_byte_seq(pw_len, &dp);
100
101 hasher_alt = Sha256::default();
103
104 for _ in 0..(16 + digest_a[0] as usize) {
107 hasher_alt.update(salt);
108 }
109
110 let ds = hasher_alt.finalize();
113
114 let s_vec = produce_byte_seq(salt_len, &ds);
117
118 let mut digest_c = digest_a;
119 for i in 0..params.rounds {
122 let mut hasher = Sha256::default();
124
125 if (i & 1) != 0 {
127 hasher.update(&p_vec);
128 } else {
129 hasher.update(digest_c);
130 }
131
132 if i % 3 != 0 {
134 hasher.update(&s_vec);
135 }
136
137 if i % 7 != 0 {
139 hasher.update(&p_vec);
140 }
141
142 if (i & 1) != 0 {
144 hasher.update(digest_c);
145 } else {
146 hasher.update(&p_vec);
147 }
148
149 digest_c.clone_from_slice(&hasher.finalize());
150 }
151
152 digest_c
153}
154
155pub 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 let p_vec = produce_byte_seq(pw_len, &dp);
189
190 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 s_vec = produce_byte_seq(salt_len, &ds);
206
207 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 hasher.update(&p_vec);
217 } else {
218 hasher.update(digest_c);
219 }
220
221 if i % 3 != 0 {
223 hasher.update(&s_vec);
224 }
225
226 if i % 7 != 0 {
228 hasher.update(&p_vec);
229 }
230
231 if (i & 1) != 0 {
233 hasher.update(digest_c);
234 } else {
235 hasher.update(&p_vec);
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().as_slice().try_into().unwrap()
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().as_slice().try_into().unwrap()
328}
329
330fn produce_byte_seq(len: usize, fill_from: &[u8]) -> Vec<u8> {
331 let bs = fill_from.len();
332 let mut seq: Vec<u8> = vec![0; len];
333 let mut offset: usize = 0;
334 for _ in 0..(len / bs) {
335 seq[offset..offset + bs].clone_from_slice(fill_from);
336 offset += bs;
337 }
338 let from_slice = &fill_from[..(len % bs)];
339 seq[offset..offset + (len % bs)].clone_from_slice(from_slice);
340 seq
341}