1use core::marker::PhantomData;
2
3#[cfg(feature = "alloc")]
4use generic_array::typenum::{B1, IsGreaterOrEqual};
5use generic_array::{
6 ArrayLength, GenericArray,
7 typenum::{NonZero, U1, U2, U3, U4, U8, U10, U14, U16, U20, U53, U64, Unsigned},
8};
9
10use crate::{
11 Align64, ValidCostFactor,
12 fixed_r::{Block, BufferSet},
13 pbkdf2_1::Pbkdf2HmacSha256State,
14 pipeline::PipelineContext,
15};
16
17pub struct CaseN16R1P1;
19
20impl CaseP1 for CaseN16R1P1 {
21 type OutputLen = U64;
22 type CF = U4;
23 type R = U1;
24 const PASSWORD: &'static [u8] = b"";
25 const SALT: &'static [u8] = b"";
26 const KNOWN_ANSWER: GenericArray<u8, Self::OutputLen> = GenericArray::from_array([
27 0x77, 0xd6, 0x57, 0x62, 0x38, 0x65, 0x7b, 0x20, 0x3b, 0x19, 0xca, 0x42, 0xc1, 0x8a, 0x04,
28 0x97, 0xf1, 0x6b, 0x48, 0x44, 0xe3, 0x07, 0x4a, 0xe8, 0xdf, 0xdf, 0xfa, 0x3f, 0xed, 0xe2,
29 0x14, 0x42, 0xfc, 0xd0, 0x06, 0x9d, 0xed, 0x09, 0x48, 0xf8, 0x32, 0x6a, 0x75, 0x3a, 0x0f,
30 0xc8, 0x1f, 0x17, 0xe8, 0xd3, 0xe0, 0xfb, 0x2e, 0x0d, 0x36, 0x28, 0xcf, 0x35, 0xe2, 0x0c,
31 0x38, 0xd1, 0x89, 0x06,
32 ]);
33}
34
35pub struct CaseN16384R8P1;
37
38impl CaseP1 for CaseN16384R8P1 {
39 type OutputLen = U64;
40 type CF = U14;
41 type R = U8;
42 const PASSWORD: &'static [u8] = b"pleaseletmein";
43 const SALT: &'static [u8] = b"SodiumChloride";
44 const KNOWN_ANSWER: GenericArray<u8, Self::OutputLen> = GenericArray::from_array([
45 0x70, 0x23, 0xbd, 0xcb, 0x3a, 0xfd, 0x73, 0x48, 0x46, 0x1c, 0x06, 0xcd, 0x81, 0xfd, 0x38,
46 0xeb, 0xfd, 0xa8, 0xfb, 0xba, 0x90, 0x4f, 0x8e, 0x3e, 0xa9, 0xb5, 0x43, 0xf6, 0x54, 0x5d,
47 0xa1, 0xf2, 0xd5, 0x43, 0x29, 0x55, 0x61, 0x3f, 0x0f, 0xcf, 0x62, 0xd4, 0x97, 0x05, 0x24,
48 0x2a, 0x9a, 0xf9, 0xe6, 0x1e, 0x85, 0xdc, 0x0d, 0x65, 0x1e, 0x40, 0xdf, 0xcf, 0x01, 0x7b,
49 0x45, 0x57, 0x58, 0x87,
50 ]);
51}
52
53pub struct CaseN1048576R8P1;
55
56impl CaseP1 for CaseN1048576R8P1 {
57 type OutputLen = U64;
58 type CF = U20;
59 type R = U8;
60 const PASSWORD: &'static [u8] = b"pleaseletmein";
61 const SALT: &'static [u8] = b"SodiumChloride";
62 const KNOWN_ANSWER: GenericArray<u8, Self::OutputLen> = GenericArray::from_array([
63 0x21, 0x01, 0xcb, 0x9b, 0x6a, 0x51, 0x1a, 0xae, 0xad, 0xdb, 0xbe, 0x09, 0xcf, 0x70, 0xf8,
64 0x81, 0xec, 0x56, 0x8d, 0x57, 0x4a, 0x2f, 0xfd, 0x4d, 0xab, 0xe5, 0xee, 0x98, 0x20, 0xad,
65 0xaa, 0x47, 0x8e, 0x56, 0xfd, 0x8f, 0x4b, 0xa5, 0xd0, 0x9f, 0xfa, 0x1c, 0x6d, 0x92, 0x7c,
66 0x40, 0xf4, 0xc3, 0x37, 0x30, 0x40, 0x49, 0xe8, 0xa9, 0x52, 0xfb, 0xcb, 0xf4, 0x5c, 0x6f,
67 0xa7, 0x7a, 0x41, 0xa4,
68 ]);
69}
70
71pub struct CaseN1024R1P2;
73
74impl Case for CaseN1024R1P2 {
75 type OutputLen = U64;
76 type CF = U10;
77 type R = U1;
78 type P = U2;
79 const PASSWORD: &'static [u8] = b"password";
80 const SALT: &'static [u8] = b"NaCl";
81 const KNOWN_ANSWER: GenericArray<u8, Self::OutputLen> = GenericArray::from_array([
82 0x09, 0xc4, 0x23, 0x86, 0xb2, 0x46, 0x97, 0x53, 0xeb, 0x76, 0x27, 0x75, 0x15, 0xbe, 0xff,
83 0x09, 0x80, 0x9d, 0x18, 0xd9, 0x3f, 0xb4, 0xd3, 0x16, 0xea, 0xe1, 0xa8, 0x63, 0x43, 0x9a,
84 0x48, 0x98, 0x17, 0xcf, 0x56, 0xa5, 0x87, 0x69, 0xcc, 0x13, 0xbd, 0xb3, 0x33, 0x14, 0x11,
85 0xcc, 0xd7, 0xd5, 0x7f, 0x8e, 0x43, 0x9b, 0xa1, 0xa4, 0x84, 0x58, 0x0f, 0x41, 0x9f, 0x7c,
86 0x8e, 0x34, 0x99, 0x41,
87 ]);
88}
89
90#[cfg(feature = "alloc")]
91pub struct CaseN1024R8P16;
93
94#[cfg(feature = "alloc")]
95impl Case for CaseN1024R8P16 {
96 type P = U16;
97 type OutputLen = U64;
98 type CF = U10;
99 type R = U8;
100 const PASSWORD: &'static [u8] = b"password";
101 const SALT: &'static [u8] = b"NaCl";
102 const KNOWN_ANSWER: GenericArray<u8, Self::OutputLen> = GenericArray::from_array([
103 0xfd, 0xba, 0xbe, 0x1c, 0x9d, 0x34, 0x72, 0x00, 0x78, 0x56, 0xe7, 0x19, 0x0d, 0x01, 0xe9,
104 0xfe, 0x7c, 0x6a, 0xd7, 0xcb, 0xc8, 0x23, 0x78, 0x30, 0xe7, 0x73, 0x76, 0x63, 0x4b, 0x37,
105 0x31, 0x62, 0x2e, 0xaf, 0x30, 0xd9, 0x2e, 0x22, 0xa3, 0x88, 0x6f, 0xf1, 0x09, 0x27, 0x9d,
106 0x98, 0x30, 0xda, 0xc7, 0x27, 0xaf, 0xb9, 0x4a, 0x83, 0xee, 0x6d, 0x83, 0x60, 0xcb, 0xdf,
107 0xa2, 0xcc, 0x06, 0x40,
108 ]);
109}
110
111pub struct CaseN1024R53P1;
113
114impl CaseP1 for CaseN1024R53P1 {
115 type OutputLen = U64;
116 type CF = U10;
117 type R = U53;
118 const PASSWORD: &'static [u8] = b"password";
119 const SALT: &'static [u8] = b"NaCl";
120 const KNOWN_ANSWER: GenericArray<u8, Self::OutputLen> = GenericArray::from_array([
121 0x7c, 0x34, 0x45, 0xf8, 0x73, 0xde, 0x41, 0x9a, 0x96, 0xd7, 0xa3, 0x20, 0x51, 0xd1, 0xb4,
122 0x8f, 0xb3, 0xde, 0x94, 0x8d, 0xac, 0x06, 0x78, 0x57, 0xf3, 0x7a, 0x58, 0xdf, 0x2d, 0x71,
123 0xeb, 0x4d, 0x4b, 0xeb, 0x87, 0xd6, 0xe8, 0x7d, 0x70, 0xd4, 0xb7, 0xa5, 0x86, 0x66, 0x99,
124 0xe4, 0x7a, 0xcd, 0x09, 0x4e, 0x93, 0x4e, 0xc4, 0xa1, 0xd7, 0xb8, 0x7d, 0x53, 0x93, 0x10,
125 0x7e, 0x75, 0xdc, 0x70,
126 ]);
127}
128
129pub struct CaseN1024R53P3;
131
132impl Case for CaseN1024R53P3 {
133 type P = U3;
134 type OutputLen = U64;
135 type CF = U10;
136 type R = U53;
137 const PASSWORD: &'static [u8] = b"password";
138 const SALT: &'static [u8] = b"NaCl";
139 const KNOWN_ANSWER: GenericArray<u8, Self::OutputLen> = GenericArray::from_array([
140 0x98, 0x13, 0x74, 0xb7, 0x0a, 0x02, 0x9a, 0x5b, 0x8a, 0xe0, 0x36, 0x28, 0x62, 0x84, 0x9f,
141 0xed, 0x87, 0xd7, 0x3e, 0x29, 0xf6, 0x08, 0x99, 0xda, 0x4f, 0xc5, 0x37, 0xa3, 0xc0, 0x6b,
142 0x36, 0x81, 0x04, 0x4a, 0xbf, 0x68, 0x0c, 0x6f, 0x40, 0x68, 0xf0, 0x4f, 0x5f, 0xa2, 0x8c,
143 0x8a, 0x16, 0x32, 0x26, 0x5b, 0xdc, 0x82, 0xa6, 0xa8, 0x06, 0xce, 0x08, 0x9a, 0x62, 0x18,
144 0xca, 0x02, 0x93, 0xb2,
145 ]);
146}
147
148pub trait CaseP1 {
150 type OutputLen: ArrayLength + NonZero;
152 type CF: ValidCostFactor;
154 type R: ArrayLength + NonZero;
156 const PASSWORD: &'static [u8];
158 const SALT: &'static [u8];
160 const KNOWN_ANSWER: GenericArray<u8, Self::OutputLen>;
162
163 fn algorithm_self_test() {
165 #[cfg(not(feature = "alloc"))]
166 let mut buffer0: GenericArray<
167 Align64<Block<Self::R>>,
168 <Self::CF as ValidCostFactor>::MinimumBlocks,
169 > = GenericArray::default();
170
171 #[cfg(feature = "alloc")]
172 let mut buffer0: alloc::boxed::Box<
173 GenericArray<Align64<Block<Self::R>>, <Self::CF as ValidCostFactor>::MinimumBlocks>,
174 > = unsafe { alloc::boxed::Box::new_uninit().assume_init() };
175
176 #[cfg(not(feature = "alloc"))]
177 let mut buffer1: GenericArray<
178 Align64<Block<Self::R>>,
179 <Self::CF as ValidCostFactor>::MinimumBlocks,
180 > = GenericArray::default();
181
182 #[cfg(feature = "alloc")]
183 let mut buffer1: alloc::boxed::Box<
184 GenericArray<Align64<Block<Self::R>>, <Self::CF as ValidCostFactor>::MinimumBlocks>,
185 > = unsafe { alloc::boxed::Box::new_uninit().assume_init() };
186
187 let mut buffer_set0 = BufferSet::<_, Self::R>::new(buffer0.as_mut_slice());
188 let mut buffer_set1 = BufferSet::<_, Self::R>::new(buffer1.as_mut_slice());
189
190 assert_eq!(buffer_set0.n(), 1 << Self::CF::U8);
191 assert_eq!(buffer_set1.n(), 1 << Self::CF::U8);
192
193 let mut output0 = GenericArray::default();
194 let mut output1 = GenericArray::default();
195 let mut output_dummy = GenericArray::default();
196
197 #[cfg(feature = "std")]
199 {
200 crate::compat::scrypt(
201 Self::PASSWORD,
202 Self::SALT,
203 Self::CF::U8.try_into().unwrap(),
204 Self::R::U32.try_into().unwrap(),
205 1.try_into().unwrap(),
206 output0.as_mut_slice(),
207 );
208 assert_eq!(output0, Self::KNOWN_ANSWER);
209 output0.fill(0);
210 }
211
212 let hmac_state = Pbkdf2HmacSha256State::new(Self::PASSWORD);
213 let hmac_state_dummy = Pbkdf2HmacSha256State::new(b"not a password");
214
215 buffer_set0.set_input(&hmac_state, Self::SALT);
217
218 buffer_set0.scrypt_ro_mix();
219
220 buffer_set0.extract_output(&hmac_state, &mut output0);
221
222 assert_eq!(output0, Self::KNOWN_ANSWER);
223
224 buffer_set0.set_input(&hmac_state_dummy, Self::SALT);
226 buffer_set0.scrypt_ro_mix();
227 buffer_set0.extract_output(&hmac_state_dummy, &mut output_dummy);
228 assert_ne!(output_dummy, Self::KNOWN_ANSWER, "stuck output");
229
230 buffer_set0.set_input(&hmac_state, Self::SALT);
232 buffer_set1.set_input(&hmac_state_dummy, Self::SALT);
233
234 buffer_set0.ro_mix_front();
235 buffer_set0.ro_mix_interleaved(&mut buffer_set1);
236 buffer_set1.ro_mix_back();
237
238 buffer_set0.extract_output(&hmac_state, &mut output0);
239 buffer_set1.extract_output(&hmac_state_dummy, &mut output1);
240
241 assert_eq!(output0, Self::KNOWN_ANSWER);
242 assert_eq!(output1, output_dummy);
243
244 buffer_set0.as_mut().iter_mut().for_each(|b| {
245 b.fill(0);
246 });
247
248 buffer_set1.as_mut().iter_mut().for_each(|b| {
249 b.fill(0);
250 });
251 }
252
253 fn pipeline_api_test() {
255 #[cfg(not(feature = "alloc"))]
256 let mut buffer0: GenericArray<
257 Align64<Block<Self::R>>,
258 <Self::CF as ValidCostFactor>::MinimumBlocks,
259 > = GenericArray::default();
260
261 #[cfg(feature = "alloc")]
262 let mut buffer0: alloc::boxed::Box<
263 GenericArray<Align64<Block<Self::R>>, <Self::CF as ValidCostFactor>::MinimumBlocks>,
264 > = unsafe { alloc::boxed::Box::new_uninit().assume_init() };
265
266 #[cfg(not(feature = "alloc"))]
267 let mut buffer1: GenericArray<
268 Align64<Block<Self::R>>,
269 <Self::CF as ValidCostFactor>::MinimumBlocks,
270 > = GenericArray::default();
271
272 #[cfg(feature = "alloc")]
273 let mut buffer1: alloc::boxed::Box<
274 GenericArray<Align64<Block<Self::R>>, <Self::CF as ValidCostFactor>::MinimumBlocks>,
275 > = unsafe { alloc::boxed::Box::new_uninit().assume_init() };
276
277 let hmac_state = Pbkdf2HmacSha256State::new(Self::PASSWORD);
278 let hmac_state_dummy = Pbkdf2HmacSha256State::new(b"not a password");
279
280 let mut buffer_set0 = BufferSet::<_, Self::R>::new(buffer0.as_mut_slice());
281 let mut buffer_set1 = BufferSet::<_, Self::R>::new(buffer1.as_mut_slice());
282
283 struct Context<R: ArrayLength + NonZero, OutputLen: ArrayLength + NonZero> {
285 i: usize,
286 salt: &'static [u8],
287 known_answer: GenericArray<u8, OutputLen>,
288
289 hmac_state: Pbkdf2HmacSha256State,
290 hmac_state_dummy: Pbkdf2HmacSha256State,
291 _marker: PhantomData<R>,
292 }
293
294 impl<R: ArrayLength + NonZero, OutputLen: ArrayLength + NonZero>
295 PipelineContext<usize, &mut [Align64<Block<R>>], R, ()> for Context<R, OutputLen>
296 {
297 fn begin(
298 &mut self,
299 _state: &mut usize,
300 buffer_set: &mut BufferSet<&mut [Align64<Block<R>>], R>,
301 ) {
302 match self.i % 3 {
303 0 | 1 => {
304 buffer_set.set_input(&self.hmac_state, self.salt);
305 }
306 2 => {
307 buffer_set.set_input(&self.hmac_state_dummy, self.salt);
308 }
309 _ => unreachable!(),
310 }
311 }
312 fn drain(
313 self,
314 state: &mut usize,
315 buffer_set: &mut BufferSet<&mut [Align64<Block<R>>], R>,
316 ) -> Option<()> {
317 match self.i % 3 {
318 0 | 1 => {
319 let mut output = GenericArray::<_, OutputLen>::default();
320 buffer_set.extract_output(&self.hmac_state, &mut output);
321 assert_eq!(output, self.known_answer);
322 }
323 2 => {
324 let mut output_dummy = GenericArray::<_, OutputLen>::default();
325 buffer_set.extract_output(&self.hmac_state_dummy, &mut output_dummy);
326 assert_ne!(output_dummy, self.known_answer, "stuck output");
327 }
328 _ => unreachable!(),
329 }
330 *state += self.i;
331
332 None
333 }
334 }
335
336 for pipeline_length in 0..11 {
337 let mut counter = 0;
338 buffer_set0.pipeline(
339 &mut buffer_set1,
340 (0..pipeline_length).map(|i| Context {
341 i,
342 hmac_state,
343 hmac_state_dummy,
344 salt: Self::SALT,
345 known_answer: Self::KNOWN_ANSWER,
346 _marker: PhantomData,
347 }),
348 &mut counter,
349 );
350
351 let expected_sum = pipeline_length * (pipeline_length.saturating_sub(1)) / 2;
352 assert_eq!(counter, expected_sum);
353 }
354 }
355}
356
357#[cfg(feature = "alloc")]
359pub trait Case {
360 type P: ArrayLength + NonZero + IsGreaterOrEqual<U2, Output = B1>;
362 type OutputLen: ArrayLength + NonZero;
364 type CF: ValidCostFactor;
366 type R: ArrayLength + NonZero;
368 const PASSWORD: &'static [u8];
370 const SALT: &'static [u8];
372 const KNOWN_ANSWER: GenericArray<u8, Self::OutputLen>;
374
375 fn algorithm_self_test() {
377 let hmac_state = Pbkdf2HmacSha256State::new(Self::PASSWORD);
378 let mut input_buffers: GenericArray<Align64<Block<Self::R>>, Self::P> =
379 GenericArray::default();
380 let mut output_buffers: GenericArray<Align64<Block<Self::R>>, Self::P> =
381 GenericArray::default();
382
383 let mut buffer0 = BufferSet::<_, Self::R>::new_boxed(Self::CF::U8.try_into().unwrap());
384 let mut buffer1 = BufferSet::<_, Self::R>::new_boxed(Self::CF::U8.try_into().unwrap());
385
386 hmac_state.emit_scatter(Self::SALT, input_buffers.iter_mut());
387
388 buffer0.pipeline(
389 &mut buffer1,
390 input_buffers.iter().zip(output_buffers.iter_mut()),
391 &mut (),
392 );
393
394 let mut output = GenericArray::default();
395
396 #[cfg(feature = "std")]
398 {
399 crate::compat::scrypt(
400 Self::PASSWORD,
401 Self::SALT,
402 Self::CF::U8.try_into().unwrap(),
403 Self::R::U32.try_into().unwrap(),
404 Self::P::U32.try_into().unwrap(),
405 output.as_mut_slice(),
406 );
407 assert_eq!(output, Self::KNOWN_ANSWER);
408 output.fill(0);
409 }
410
411 hmac_state.emit_gather(output_buffers.iter(), &mut output);
412
413 assert_eq!(output, Self::KNOWN_ANSWER);
414 }
415}
416
417#[cfg(test)]
418mod tests {
419 use super::*;
420
421 #[test]
422 fn test_case_16_1_1_algorithm_self_test() {
423 CaseN16R1P1::algorithm_self_test();
424 }
425
426 #[test]
427 fn test_case_16_1_1_pipeline_api_test() {
428 CaseN16R1P1::pipeline_api_test();
429 }
430
431 #[cfg(feature = "alloc")]
432 #[test]
433 fn test_case_16384_8_1_algorithm_self_test() {
434 CaseN16384R8P1::algorithm_self_test();
435 }
436
437 #[cfg(feature = "alloc")]
438 #[test]
439 fn test_case_1024_8_16_algorithm_self_test() {
440 CaseN1024R8P16::algorithm_self_test();
441 }
442
443 #[cfg(feature = "alloc")]
444 #[test]
445 fn test_case_1024_1_2_algorithm_self_test() {
446 CaseN1024R1P2::algorithm_self_test();
447 }
448
449 #[cfg(feature = "alloc")]
450 #[test]
451 fn test_case_1024_53_1_algorithm_self_test() {
452 CaseN1024R53P1::algorithm_self_test();
453 }
454
455 #[cfg(feature = "alloc")]
456 #[test]
457 fn test_case_1024_53_1_pipeline_api_test() {
458 CaseN1024R53P1::pipeline_api_test();
459 }
460
461 #[cfg(feature = "alloc")]
462 #[test]
463 fn test_case_1024_53_3_algorithm_self_test() {
464 CaseN1024R53P3::algorithm_self_test();
465 }
466}