1#![doc = include_str!("../README.md")]
2#![cfg_attr(
3 all(not(test), not(feature = "std"), not(target_arch = "wasm32")),
4 no_std
5)]
6#![cfg_attr(feature = "portable-simd", feature(portable_simd))]
7#![warn(missing_docs)]
8
9#[cfg(feature = "alloc")]
10extern crate alloc;
11
12#[rustfmt::skip]
13macro_rules! repeat2 {
14 ($i:ident, $b:block) => {
15 { let $i = 0; $b; }
16 { let $i = 1; $b; }
17 };
18}
19
20#[rustfmt::skip]
21macro_rules! repeat4 {
22 ($i:ident, $b:block) => {
23 repeat2!(di, { let $i = di; $b });
24 repeat2!(di, { let $i = di + 2; $b });
25 };
26}
27
28#[rustfmt::skip]
29macro_rules! repeat8 {
30 ($i:ident, $b:block) => {{
31 repeat4!(di, { let $i = di; $b });
32 repeat4!(di, { let $i = di + 4; $b });
33 }};
34}
35
36pub use sha2;
38
39pub use generic_array;
41
42pub mod self_test;
44
45pub mod memory;
47
48pub mod salsa20;
50
51pub(crate) mod simd;
53
54pub mod pbkdf2_1;
56
57pub mod pipeline;
59
60#[cfg(target_arch = "x86_64")]
62pub(crate) mod sha2_mb;
63
64pub mod features;
66
67#[cfg(any(feature = "std", target_arch = "wasm32"))]
69pub mod compat;
70
71pub mod fixed_r;
73
74use core::num::{NonZeroU8, NonZeroU32};
75
76use generic_array::typenum::{
77 B1, IsLess, IsLessOrEqual, PowerOfTwo, U1, U2, U3, U4, U5, U6, U7, U8, U9, U10, U11, U12, U13,
78 U14, U15, U16, U17, U18, U19, U20, U21, U22, U23, U24, U25, U26, U27, U28, U29, U30, U31,
79 U4294967296, Unsigned,
80};
81
82use generic_array::{ArrayLength, GenericArray, typenum::NonZero};
83
84#[allow(unused_imports)]
85use crate::features::Feature as _;
86
87use crate::memory::Align64;
88use crate::salsa20::{BlockType, Salsa20};
89
90include!("block_mix.rs");
91
92#[cfg(target_arch = "x86_64")]
98cfg_if::cfg_if! {
99 if #[cfg(target_feature = "avx512f")] {
100 pub type DefaultEngine1 = salsa20::x86_64::BlockAvx512F;
102 pub type DefaultEngine2 = salsa20::x86_64::BlockAvx512FMb2;
104 } else if #[cfg(target_feature = "avx2")] {
105 pub type DefaultEngine1 = salsa20::x86_64::BlockSse2<U1>;
107 pub type DefaultEngine2 = salsa20::x86_64::BlockAvx2Mb2;
109 } else if #[cfg(feature = "portable-simd")] {
110 pub type DefaultEngine1 = salsa20::BlockPortableSimd;
112 pub type DefaultEngine2 = salsa20::BlockPortableSimd2;
114 } else {
115 pub type DefaultEngine1 = salsa20::x86_64::BlockSse2<U1>;
117 pub type DefaultEngine2 = salsa20::x86_64::BlockSse2<U2>;
119 }
120}
121
122#[cfg(not(target_arch = "x86_64"))]
123cfg_if::cfg_if! {
124 if #[cfg(feature = "portable-simd")] {
125 pub type DefaultEngine1 = salsa20::BlockPortableSimd;
127 pub type DefaultEngine2 = salsa20::BlockPortableSimd2;
129 } else {
130 pub type DefaultEngine1 = salsa20::BlockScalar<U1>;
132 pub type DefaultEngine2 = salsa20::BlockScalar<U2>;
134 }
135}
136
137mod sealing {
138 pub trait Sealed {}
139}
140
141pub trait ValidCostFactor: Unsigned + NonZero + sealing::Sealed {
143 type Output: ArrayLength + PowerOfTwo + NonZero + IsLess<U4294967296, Output = B1>;
145
146 type MinimumBlocks: ArrayLength + NonZero + IsLessOrEqual<U4294967296, Output = B1>;
148}
149
150const MAX_CF: u8 = 31;
151const MAX_N: u32 = 1 << MAX_CF;
152
153macro_rules! impl_valid_cost_factor {
154 ($($base:ty),*) => {
155 $(
156 impl sealing::Sealed for $base {}
157 impl ValidCostFactor for $base {
158 type Output = <U1 as core::ops::Shl<$base>>::Output;
159 type MinimumBlocks = <<U1 as core::ops::Shl<$base>>::Output as core::ops::Add<U2>>::Output;
160 }
161 )*
162 };
163}
164
165impl_valid_cost_factor!(
166 U1, U2, U3, U4, U5, U6, U7, U8, U9, U10, U11, U12, U13, U14, U15, U16, U17, U18, U19, U20, U21,
167 U22, U23, U24, U25, U26, U27, U28, U29, U30, U31
168);
169
170pub trait RoMix {
172 fn ro_mix_front_ex<S: Salsa20<Lanes = U1>>(&mut self, r: NonZeroU32, cf: NonZeroU8);
176 fn ro_mix_back_ex<S: Salsa20<Lanes = U1>>(&mut self, r: NonZeroU32, cf: NonZeroU8) -> &[u8];
182 fn ro_mix_interleaved_ex<'a, S: Salsa20<Lanes = U2>>(
188 &'a mut self,
189 front: &mut Self,
190 r: NonZeroU32,
191 cf: NonZeroU8,
192 ) -> &'a [u8];
193
194 fn ro_mix_input_buffer(&mut self, r: NonZeroU32) -> &mut [u8];
198
199 fn ro_mix_front(&mut self, r: NonZeroU32, cf: NonZeroU8) {
203 self.ro_mix_front_ex::<DefaultEngine1>(r, cf);
204 }
205 fn ro_mix_back(&mut self, r: NonZeroU32, cf: NonZeroU8) -> &[u8] {
211 self.ro_mix_back_ex::<DefaultEngine1>(r, cf)
212 }
213 fn ro_mix_interleaved(&mut self, front: &mut Self, r: NonZeroU32, cf: NonZeroU8) -> &[u8] {
219 self.ro_mix_interleaved_ex::<DefaultEngine2>(front, r, cf)
220 }
221}
222
223#[cfg_attr(
224 all(target_arch = "x86_64", not(target_feature = "avx2")),
225 scrypt_opt_derive::generate_target_variant("avx2")
226)]
227#[cfg_attr(
228 not(all(target_arch = "x86_64", not(target_feature = "avx2"))),
229 inline(always)
230)]
231fn ro_mix_front_ex_dyn<S: Salsa20<Lanes = U1>>(
232 v: &mut [Align64<fixed_r::Block<U1>>],
233 r: NonZeroU32,
234 cf: NonZeroU8,
235) {
236 let r = r.get() as usize;
237 let n = 1 << cf.get();
238 assert!(
239 v.len() >= r * (n + 1),
240 "ro_mix_front_ex: v.len() < r * (n + 1)"
241 );
242
243 unsafe {
245 v.get_unchecked_mut(..r).iter_mut().for_each(|chunk| {
246 S::shuffle_in(
247 chunk
248 .as_mut_ptr()
249 .cast::<Align64<[u32; 16]>>()
250 .as_mut()
251 .unwrap(),
252 );
253 S::shuffle_in(
254 chunk
255 .as_mut_ptr()
256 .cast::<Align64<[u32; 16]>>()
257 .add(1)
258 .as_mut()
259 .unwrap(),
260 );
261 });
262 }
263
264 for i in 0..n {
265 let [src, dst] = unsafe {
266 v.get_disjoint_unchecked_mut([(i * r)..((i + 1) * r), ((i + 1) * r)..((i + 2) * r)])
267 };
268 block_mix!(r; [<S> &*src => &mut *dst]);
269 }
270}
271
272#[cfg_attr(
273 all(target_arch = "x86_64", not(target_feature = "avx2")),
274 scrypt_opt_derive::generate_target_variant("avx2")
275)]
276#[cfg_attr(
277 not(all(target_arch = "x86_64", not(target_feature = "avx2"))),
278 inline(always)
279)]
280fn ro_mix_back_ex_dyn<S: Salsa20<Lanes = U1>>(
281 v: &mut [Align64<fixed_r::Block<U1>>],
282 r: NonZeroU32,
283 cf: NonZeroU8,
284) -> &[u8] {
285 let r = r.get() as usize;
286 let n = 1 << cf.get();
287 assert!(
288 v.len() >= r * (n + 2),
289 "pipeline_end_ex: v.len() < r * (n + 2)"
290 );
291
292 for _ in (0..n).step_by(2) {
293 let idx = unsafe {
294 v.as_ptr()
295 .add((n * r) as usize)
296 .cast::<u32>()
297 .add(r * 32 - 16)
298 .read()
299 } as usize;
300
301 let j = idx & (n - 1);
302
303 let [in0, in1, out] = unsafe {
305 v.get_disjoint_unchecked_mut([
306 (n * r)..((n + 1) * r),
307 (j * r)..((j + 1) * r),
308 ((n + 1) * r)..((n + 2) * r),
309 ])
310 };
311 block_mix!(r; [<S> &(&*in0, &*in1) => &mut *out]);
312 let idx2 = unsafe {
313 v.as_ptr()
314 .add(((n + 1) * r) as usize)
315 .cast::<u32>()
316 .add(r * 32 - 16)
317 .read()
318 } as usize;
319
320 let j2 = idx2 & (n - 1);
321
322 let [b, v, t] = unsafe {
324 v.get_disjoint_unchecked_mut([
325 (n * r)..((n + 1) * r),
326 (j2 * r)..((j2 + 1) * r),
327 ((n + 1) * r)..((n + 2) * r),
328 ])
329 };
330 block_mix!(r; [<S> &(&*v, &*t) => &mut *b]);
331 }
332
333 unsafe {
335 v.get_unchecked_mut(r * n..r * (n + 1))
336 .iter_mut()
337 .for_each(|chunk| {
338 S::shuffle_out(
339 chunk
340 .as_mut_ptr()
341 .cast::<Align64<[u32; 16]>>()
342 .as_mut()
343 .unwrap(),
344 );
345 S::shuffle_out(
346 chunk
347 .as_mut_ptr()
348 .cast::<Align64<[u32; 16]>>()
349 .add(1)
350 .as_mut()
351 .unwrap(),
352 );
353 });
354
355 core::slice::from_raw_parts(v.as_ptr().add(r * n).cast::<u8>(), 128 * r)
356 }
357}
358
359#[cfg_attr(
360 all(target_arch = "x86_64", not(target_feature = "avx2")),
361 scrypt_opt_derive::generate_target_variant("avx2")
362)]
363#[cfg_attr(
364 not(all(target_arch = "x86_64", not(target_feature = "avx2"))),
365 inline(always)
366)]
367fn ro_mix_interleaved_ex_dyn<'a, S: Salsa20<Lanes = U2>>(
368 self_v: &mut [Align64<fixed_r::Block<U1>>],
369 other_v: &mut [Align64<fixed_r::Block<U1>>],
370 r: NonZeroU32,
371 cf: NonZeroU8,
372) -> &'a [u8] {
373 let r = r.get() as usize;
374 let n = 1 << cf.get();
375
376 assert!(
377 other_v.len() >= r * (n + 2),
378 "ro_mix_interleaved_ex: other_v.len() < r * (n + 2)"
379 );
380 assert!(
381 self_v.len() >= r * (n + 2),
382 "ro_mix_interleaved_ex: self_v.len() < r * (n + 2)"
383 );
384
385 unsafe {
388 other_v.get_unchecked_mut(..r).iter_mut().for_each(|chunk| {
389 S::shuffle_in(
390 chunk
391 .as_mut_ptr()
392 .cast::<Align64<[u32; 16]>>()
393 .as_mut()
394 .unwrap(),
395 );
396 S::shuffle_in(
397 chunk
398 .as_mut_ptr()
399 .cast::<Align64<[u32; 16]>>()
400 .add(1)
401 .as_mut()
402 .unwrap(),
403 );
404 });
405 }
406
407 for i in (0..n).step_by(2) {
408 let [src, middle, dst] = unsafe {
410 other_v.get_disjoint_unchecked_mut([
411 (i * r)..((i + 1) * r),
412 ((i + 1) * r)..((i + 2) * r),
413 ((i + 2) * r)..((i + 3) * r),
414 ])
415 };
416
417 {
418 let idx = unsafe {
421 self_v
422 .as_ptr()
423 .add((n * r) as usize)
424 .cast::<u32>()
425 .add(r * 32 - 16)
426 .read()
427 } as usize;
428
429 let j = idx & (n - 1);
430
431 let [in0, in1, out] = unsafe {
432 self_v.get_disjoint_unchecked_mut([
433 (j * r)..((j + 1) * r),
434 (n * r)..((n + 1) * r),
435 ((n + 1) * r)..((n + 2) * r),
436 ])
437 };
438
439 block_mix!(r; [<S> &&*src => &mut *middle, <S> &(&*in0, &*in1) => &mut *out]);
440 }
441
442 {
443 let idx2 = unsafe {
446 self_v
447 .as_ptr()
448 .add(((n + 1) * r) as usize)
449 .cast::<u32>()
450 .add(r * 32 - 16)
451 .read()
452 } as usize;
453
454 let j2 = idx2 & (n - 1);
455 let [self_b, self_v, self_t] = unsafe {
456 self_v.get_disjoint_unchecked_mut([
457 (n * r)..((n + 1) * r),
458 (j2 * r)..((j2 + 1) * r),
459 ((n + 1) * r)..((n + 2) * r),
460 ])
461 };
462
463 block_mix!(r; [<S> &*middle => &mut *dst, <S> &(&*self_v, &*self_t) => &mut *self_b]);
464 }
465 }
466 unsafe {
468 self_v
469 .get_unchecked_mut(r * n..r * (n + 1))
470 .iter_mut()
471 .for_each(|chunk| {
472 S::shuffle_out(
473 chunk
474 .as_mut_ptr()
475 .cast::<Align64<[u32; 16]>>()
476 .as_mut()
477 .unwrap(),
478 );
479 S::shuffle_out(
480 chunk
481 .as_mut_ptr()
482 .cast::<Align64<[u32; 16]>>()
483 .add(1)
484 .as_mut()
485 .unwrap(),
486 );
487 });
488
489 core::slice::from_raw_parts(self_v.as_ptr().add(r * n).cast::<u8>(), 128 * r)
490 }
491}
492
493impl<Q: AsRef<[Align64<fixed_r::Block<U1>>]> + AsMut<[Align64<fixed_r::Block<U1>>]>> RoMix for Q {
494 fn ro_mix_input_buffer(&mut self, r: NonZeroU32) -> &mut [u8] {
495 let r = r.get() as usize;
496 let v = self.as_mut();
497 assert!(v.len() >= r, "ro_mix_input_buffer: v.len() < r");
498 unsafe { core::slice::from_raw_parts_mut(v.as_mut_ptr().cast::<u8>(), 128 * r) }
499 }
500
501 fn ro_mix_front_ex<S: Salsa20<Lanes = U1>>(&mut self, r: NonZeroU32, cf: NonZeroU8) {
502 let v = self.as_mut();
503
504 #[cfg(all(target_arch = "x86_64", not(target_feature = "avx2")))]
505 {
506 if features::Avx2.check() {
507 unsafe { ro_mix_front_ex_dyn_avx2::<salsa20::x86_64::BlockSse2<U1>>(v, r, cf) }
508 return;
509 }
510 }
511
512 ro_mix_front_ex_dyn::<S>(v, r, cf)
513 }
514
515 fn ro_mix_back_ex<S: Salsa20<Lanes = U1>>(&mut self, r: NonZeroU32, cf: NonZeroU8) -> &[u8] {
516 let v = self.as_mut();
517
518 #[cfg(all(target_arch = "x86_64", not(target_feature = "avx2")))]
519 {
520 if features::Avx2.check() {
521 return unsafe {
522 ro_mix_back_ex_dyn_avx2::<salsa20::x86_64::BlockSse2<U1>>(v, r, cf)
523 };
524 }
525 }
526
527 ro_mix_back_ex_dyn::<S>(v, r, cf)
528 }
529
530 fn ro_mix_interleaved_ex<'a, S: Salsa20<Lanes = U2>>(
531 &'a mut self,
532 front: &mut Self,
533 r: NonZeroU32,
534 cf: NonZeroU8,
535 ) -> &'a [u8] {
536 let self_v = self.as_mut();
537 let other_v = front.as_mut();
538
539 #[cfg(all(target_arch = "x86_64", not(target_feature = "avx2")))]
540 {
541 if features::Avx2.check() {
542 return unsafe {
543 ro_mix_interleaved_ex_dyn_avx2::<salsa20::x86_64::BlockAvx2Mb2>(
544 self_v, other_v, r, cf,
545 )
546 };
547 }
548 }
549
550 ro_mix_interleaved_ex_dyn::<S>(self_v, other_v, r, cf)
551 }
552}
553
554pub trait ScryptBlockMixInput<'a, B: BlockType> {
556 unsafe fn load(&self, word_idx: usize) -> B;
558}
559
560impl<'a, B: BlockType> ScryptBlockMixInput<'a, B> for &'a [Align64<fixed_r::Block<U1>>] {
561 #[inline(always)]
562 unsafe fn load(&self, word_idx: usize) -> B {
563 unsafe { B::read_from_ptr(self.as_ptr().cast::<[u8; 64]>().add(word_idx).cast()) }
564 }
565}
566
567impl<'a, B: BlockType, Lhs: ScryptBlockMixInput<'a, B>, Rhs: ScryptBlockMixInput<'a, B>>
568 ScryptBlockMixInput<'a, B> for (Lhs, Rhs)
569{
570 #[inline(always)]
571 unsafe fn load(&self, word_idx: usize) -> B {
572 let mut x0 = unsafe { self.0.load(word_idx) };
573 let x1 = unsafe { self.1.load(word_idx) };
574 x0.xor_with(x1);
575 x0
576 }
577}
578
579pub trait ScryptBlockMixOutput<'a, R: ArrayLength, B: BlockType> {
581 fn store_even(&mut self, word_idx: usize, value: B);
583 fn store_odd(&mut self, word_idx: usize, value: B);
585}
586
587impl<
588 'a,
589 R: ArrayLength,
590 B: BlockType,
591 U: ScryptBlockMixOutput<'a, R, B>,
592 V: ScryptBlockMixOutput<'a, R, B>,
593> ScryptBlockMixOutput<'a, R, B> for (U, V)
594{
595 #[inline(always)]
596 fn store_even(&mut self, word_idx: usize, value: B) {
597 self.0.store_even(word_idx, value);
598 self.1.store_even(word_idx, value);
599 }
600 #[inline(always)]
601 fn store_odd(&mut self, word_idx: usize, value: B) {
602 self.0.store_odd(word_idx, value);
603 self.1.store_odd(word_idx, value);
604 }
605}
606
607#[cfg(test)]
608mod tests {
609 use generic_array::typenum::{U1, U2, U4, U8, U16};
610
611 use super::*;
612 use crate::{
613 fixed_r::{Block, BufferSet},
614 pbkdf2_1::Pbkdf2HmacSha256State,
615 pipeline::PipelineContext,
616 };
617
618 macro_rules! write_test {
619 ($name:ident, $test:ident, $($generic:ty),* $(,)?) => {
620 #[test]
621 fn $name() {
622 $test::<$($generic),*>();
623 }
624 };
625 }
626
627 #[cfg(all(target_arch = "x86_64", target_feature = "avx512f"))]
628 fn test_ro_mix_cas_zmm<R: ArrayLength + NonZero>() {
629 const CF: u8 = 8;
630
631 let password = b"password";
632 let hmac = Pbkdf2HmacSha256State::new(password);
633 let salt = b"salt";
634 let mut expected = [0u8; 64];
635
636 let params = scrypt::Params::new(CF, R::U32, 1, 64).unwrap();
637
638 scrypt::scrypt(password, salt, ¶ms, &mut expected).expect("scrypt failed");
639
640 let mut buffers = BufferSet::<_, R>::new_boxed(CF.try_into().unwrap());
641 buffers.set_input(&hmac, salt);
642
643 buffers.scrypt_ro_mix_ex_zmm::<salsa20::x86_64::BlockAvx512F>();
644
645 let mut output = [0u8; 64];
646
647 buffers.extract_output(&hmac, &mut output);
648
649 assert_eq!(output, expected);
650 }
651
652 #[test]
653 fn test_pipeline() {
654 for cf in 1..=8 {
655 let mut buffers0 = BufferSet::<_, U1>::new_boxed(cf.try_into().unwrap());
656 let mut buffers1 = BufferSet::<_, U1>::new_boxed(cf.try_into().unwrap());
657
658 let input_passwords = [
659 b"password0".as_slice(),
660 b"password1".as_slice(),
661 b"password2".as_slice(),
662 b"password3".as_slice(),
663 b"password4".as_slice(),
664 b"password5".as_slice(),
665 b"password6".as_slice(),
666 b"password7".as_slice(),
667 b"password8".as_slice(),
668 b"password9".as_slice(),
669 b"password10".as_slice(),
670 ];
671
672 let input_salts = [
673 b"salt0".as_slice(),
674 b"salt1".as_slice(),
675 b"salt2".as_slice(),
676 b"salt3".as_slice(),
677 b"salt4".as_slice(),
678 b"salt5".as_slice(),
679 b"salt6".as_slice(),
680 b"salt7".as_slice(),
681 b"salt8".as_slice(),
682 b"salt9".as_slice(),
683 b"salt10".as_slice(),
684 ];
685
686 struct Context<'a> {
687 params: scrypt::Params,
688 i: usize,
689 total: usize,
690 password: &'a [u8],
691 salt: &'a [u8],
692 }
693
694 impl<'a, R: ArrayLength + NonZero> PipelineContext<usize, Vec<Align64<Block<R>>>, R, ()>
695 for Context<'a>
696 {
697 fn begin(
698 &mut self,
699 _ratchet: &mut usize,
700 buffer_set: &mut BufferSet<Vec<Align64<Block<R>>>, R>,
701 ) {
702 buffer_set.set_input(&Pbkdf2HmacSha256State::new(self.password), self.salt);
703 }
704
705 fn drain(
706 self,
707 ratchet: &mut usize,
708 buffer_set: &mut BufferSet<Vec<Align64<Block<R>>>, R>,
709 ) -> Option<()> {
710 assert_eq!(*ratchet, self.i, "output should be in order");
711 assert!(*ratchet < self.total, "should have processed all passwords");
712 *ratchet += 1;
713 let mut output = [0u8; 64];
714 buffer_set
715 .extract_output(&Pbkdf2HmacSha256State::new(self.password), &mut output);
716 let mut expected = [0u8; 64];
717
718 scrypt::scrypt(self.password, self.salt, &self.params, &mut expected)
719 .expect("scrypt failed");
720
721 assert_eq!(output, expected, "unexpected output at round {}", self.i);
722
723 if *ratchet == self.total {
724 Some(())
725 } else {
726 None
727 }
728 }
729 }
730
731 let params = scrypt::Params::new(cf, U1::U32, 1, 64).unwrap();
732
733 for test_len in 0..input_passwords.len() {
735 let mut ratchet = 0;
736 let ret = buffers0.pipeline(
737 &mut buffers1,
738 input_passwords
739 .iter()
740 .zip(input_salts.iter())
741 .enumerate()
742 .map(|(i, (p, s))| Context {
743 params,
744 i,
745 total: test_len,
746 password: p,
747 salt: s,
748 })
749 .take(test_len),
750 &mut ratchet,
751 );
752
753 assert_eq!(
754 ret.is_some(),
755 test_len > 0,
756 "should have processed all passwords"
757 );
758 }
759 }
760 }
761
762 fn test_ro_mix_cas<R: ArrayLength + NonZero>() {
763 const CF: u8 = 8;
764
765 let password = b"password";
766 let salt = b"salt";
767 let mut expected = [0u8; 64];
768
769 let params = scrypt::Params::new(CF, R::U32, 1, 64).unwrap();
770
771 scrypt::scrypt(password, salt, ¶ms, &mut expected).expect("scrypt failed");
772
773 let mut buffers = BufferSet::<_, R>::new_boxed(CF.try_into().unwrap());
774
775 assert_eq!(buffers.n(), 1 << CF);
776
777 buffers.set_input(&Pbkdf2HmacSha256State::new(password), salt);
778
779 buffers.scrypt_ro_mix();
780
781 let mut output = [0u8; 64];
782
783 buffers.extract_output(&Pbkdf2HmacSha256State::new(password), &mut output);
784
785 assert_eq!(output, expected);
786 }
787
788 fn test_ro_mix_cas_ex<R: ArrayLength + NonZero, S: Salsa20<Lanes = U1>>() {
789 const CF: u8 = 8;
790
791 let password = b"password";
792 let salt = b"salt";
793 let mut expected = [0u8; 64];
794
795 let params = scrypt::Params::new(CF, R::U32, 1, 64).unwrap();
796
797 scrypt::scrypt(password, salt, ¶ms, &mut expected).expect("scrypt failed");
798
799 let mut buffers = BufferSet::<_, R>::new_boxed(CF.try_into().unwrap());
800
801 let mut buffer_dyn = vec![Default::default(); R::USIZE * ((1 << CF) + 2)];
802
803 assert_eq!(buffers.n(), 1 << CF);
804
805 buffers.set_input(&Pbkdf2HmacSha256State::new(password), salt);
806 buffer_dyn
807 .ro_mix_input_buffer(R::U32.try_into().unwrap())
808 .copy_from_slice(buffers.input_buffer().as_slice());
809
810 buffer_dyn.ro_mix_front_ex::<S>(R::U32.try_into().unwrap(), CF.try_into().unwrap());
811 buffers.ro_mix_front_ex::<S>();
812 let dyn_output =
813 buffer_dyn.ro_mix_back_ex::<S>(R::U32.try_into().unwrap(), CF.try_into().unwrap());
814 buffers.ro_mix_back_ex::<S>();
815
816 assert_eq!(dyn_output, buffers.raw_salt_output().as_slice());
817
818 let mut output = [0u8; 64];
819
820 buffers.extract_output(&Pbkdf2HmacSha256State::new(password), &mut output);
821
822 assert_eq!(output, expected);
823 }
824
825 fn test_ro_mix_cas_interleaved<R: ArrayLength + NonZero>() {
826 const CF: u8 = 8;
827
828 let passwords = [
829 b"password0".as_slice(),
830 b"password1".as_slice(),
831 b"password2".as_slice(),
832 b"password3".as_slice(),
833 b"password4".as_slice(),
834 b"password5".as_slice(),
835 b"password6".as_slice(),
836 b"password7".as_slice(),
837 b"password8".as_slice(),
838 b"password9".as_slice(),
839 b"password10".as_slice(),
840 b"password11".as_slice(),
841 b"password12".as_slice(),
842 b"password13".as_slice(),
843 b"password14".as_slice(),
844 b"password15".as_slice(),
845 ];
846
847 let mut expected = [[0u8; 64]; 16];
848
849 for (i, password) in passwords.iter().enumerate() {
850 let params = scrypt::Params::new(CF, R::U32, 1, 64).unwrap();
851 scrypt::scrypt(password, b"salt", ¶ms, &mut expected[i]).expect("scrypt failed");
852 }
853
854 let mut buffers0 = BufferSet::<_, R>::new_boxed(CF.try_into().unwrap());
855 let mut buffers1 = BufferSet::<_, R>::new_boxed(CF.try_into().unwrap());
856
857 let mut output = [0u8; 64];
858 buffers0.set_input(&Pbkdf2HmacSha256State::new(passwords[0]), b"salt");
859 buffers1.set_input(&Pbkdf2HmacSha256State::new(passwords[1]), b"salt");
860 buffers0.ro_mix_front();
861 for i in 2..16 {
862 buffers0.ro_mix_interleaved(&mut buffers1);
863 buffers0.extract_output(&Pbkdf2HmacSha256State::new(passwords[i - 2]), &mut output);
864 assert_eq!(output, expected[i - 2], "error at round {}", i);
865 core::hint::black_box(&mut buffers0);
866 (buffers0, buffers1) = (buffers1, buffers0);
867 buffers1.set_input(&Pbkdf2HmacSha256State::new(passwords[i]), b"salt");
868 }
869 buffers0.ro_mix_back();
870 buffers1.scrypt_ro_mix();
871 buffers0.extract_output(&Pbkdf2HmacSha256State::new(passwords[14]), &mut output);
872 assert_eq!(output, expected[14]);
873 buffers1.extract_output(&Pbkdf2HmacSha256State::new(passwords[15]), &mut output);
874 assert_eq!(output, expected[15]);
875 }
876
877 fn test_ro_mix_cas_interleaved_ex<
878 R: ArrayLength + NonZero,
879 S1: Salsa20<Lanes = U1>,
880 S2: Salsa20<Lanes = U2>,
881 >() {
882 const CF: u8 = 8;
883
884 let passwords = [
885 b"password0".as_slice(),
886 b"password1".as_slice(),
887 b"password2".as_slice(),
888 b"password3".as_slice(),
889 b"password4".as_slice(),
890 b"password5".as_slice(),
891 b"password6".as_slice(),
892 b"password7".as_slice(),
893 b"password8".as_slice(),
894 b"password9".as_slice(),
895 b"password10".as_slice(),
896 b"password11".as_slice(),
897 b"password12".as_slice(),
898 b"password13".as_slice(),
899 b"password14".as_slice(),
900 b"password15".as_slice(),
901 ];
902
903 let mut expected = [[0u8; 64]; 16];
904
905 for (i, password) in passwords.iter().enumerate() {
906 let params = scrypt::Params::new(CF, R::U32, 1, 64).unwrap();
907 scrypt::scrypt(password, b"salt", ¶ms, &mut expected[i]).expect("scrypt failed");
908 }
909
910 let mut buffers0 = BufferSet::<_, R>::new_boxed(CF.try_into().unwrap());
911 let mut buffers1 = BufferSet::<_, R>::new_boxed(CF.try_into().unwrap());
912 let mut buffers0_dyn = vec![Default::default(); R::USIZE * ((1 << CF) + 2)];
913 let mut buffers1_dyn = vec![Default::default(); R::USIZE * ((1 << CF) + 2)];
914
915 let mut output = [0u8; 64];
916 buffers0.set_input(&Pbkdf2HmacSha256State::new(passwords[0]), b"salt");
917 buffers1.set_input(&Pbkdf2HmacSha256State::new(passwords[1]), b"salt");
918 buffers0_dyn
919 .ro_mix_input_buffer(R::U32.try_into().unwrap())
920 .copy_from_slice(buffers0.input_buffer().as_slice());
921 buffers1_dyn
922 .ro_mix_input_buffer(R::U32.try_into().unwrap())
923 .copy_from_slice(buffers1.input_buffer().as_slice());
924
925 buffers0.ro_mix_front_ex::<S1>();
926 buffers0_dyn.ro_mix_front_ex::<S1>(R::U32.try_into().unwrap(), CF.try_into().unwrap());
927 for i in 2..16 {
928 buffers0.ro_mix_interleaved_ex::<S2>(&mut buffers1);
929 let dyn_salt_output = buffers0_dyn.ro_mix_interleaved_ex::<S2>(
930 &mut buffers1_dyn,
931 R::U32.try_into().unwrap(),
932 CF.try_into().unwrap(),
933 );
934 buffers0.extract_output(&Pbkdf2HmacSha256State::new(passwords[i - 2]), &mut output);
935 assert_eq!(dyn_salt_output, buffers0.raw_salt_output().as_slice());
936
937 assert_eq!(output, expected[i - 2], "error at round {}", i);
938 core::hint::black_box(&mut buffers0);
939 (buffers0, buffers1) = (buffers1, buffers0);
940 (buffers0_dyn, buffers1_dyn) = (buffers1_dyn, buffers0_dyn);
941 buffers1.set_input(&Pbkdf2HmacSha256State::new(passwords[i]), b"salt");
942 buffers1_dyn
943 .ro_mix_input_buffer(R::U32.try_into().unwrap())
944 .copy_from_slice(buffers1.input_buffer().as_slice());
945 }
946 buffers0.ro_mix_back_ex::<S1>();
947 let dyn_salt_output =
948 buffers0_dyn.ro_mix_back_ex::<S1>(R::U32.try_into().unwrap(), CF.try_into().unwrap());
949 assert_eq!(dyn_salt_output, buffers0.raw_salt_output().as_slice());
950
951 buffers1.scrypt_ro_mix();
952 buffers1_dyn.ro_mix_front_ex::<S1>(R::U32.try_into().unwrap(), CF.try_into().unwrap());
953 let dyn_salt_output =
954 buffers1_dyn.ro_mix_back_ex::<S1>(R::U32.try_into().unwrap(), CF.try_into().unwrap());
955 assert_eq!(dyn_salt_output, buffers1.raw_salt_output().as_slice());
956
957 buffers0.extract_output(&Pbkdf2HmacSha256State::new(passwords[14]), &mut output);
958 assert_eq!(output, expected[14]);
959 buffers1.extract_output(&Pbkdf2HmacSha256State::new(passwords[15]), &mut output);
960 assert_eq!(output, expected[15]);
961 }
962
963 #[cfg(all(target_arch = "x86_64", target_feature = "avx512f"))]
964 mod avx512 {
965 use super::*;
966
967 fn test_ro_mix_cas_interleaved_zmm<R: ArrayLength + NonZero>() {
968 const CF: u8 = 8;
969
970 let passwords = [
971 b"password0".as_slice(),
972 b"password1".as_slice(),
973 b"password2".as_slice(),
974 b"password3".as_slice(),
975 b"password4".as_slice(),
976 b"password5".as_slice(),
977 b"password6".as_slice(),
978 b"password7".as_slice(),
979 b"password8".as_slice(),
980 b"password9".as_slice(),
981 b"password10".as_slice(),
982 b"password11".as_slice(),
983 b"password12".as_slice(),
984 b"password13".as_slice(),
985 b"password14".as_slice(),
986 b"password15".as_slice(),
987 ];
988
989 let hmacs: [Pbkdf2HmacSha256State; 16] =
990 core::array::from_fn(|i| Pbkdf2HmacSha256State::new(passwords[i]));
991
992 let mut expected = [[0u8; 64]; 16];
993
994 for (i, password) in passwords.iter().enumerate() {
995 let params = scrypt::Params::new(CF, R::U32, 1, 64).unwrap();
996 scrypt::scrypt(password, b"salt", ¶ms, &mut expected[i])
997 .expect("scrypt failed");
998 }
999
1000 let mut buffers0 = BufferSet::<_, R>::new_boxed(CF.try_into().unwrap());
1001 let mut buffers1 = BufferSet::<_, R>::new_boxed(CF.try_into().unwrap());
1002
1003 let mut output = [0u8; 64];
1004 buffers0.set_input(&hmacs[0], b"salt");
1005 buffers1.set_input(&hmacs[1], b"salt");
1006 buffers0.ro_mix_front();
1007 for i in 2..16 {
1008 buffers0
1009 .ro_mix_interleaved_ex_zmm::<salsa20::x86_64::BlockAvx512FMb2>(&mut buffers1);
1010 buffers0.extract_output(&hmacs[i - 2], &mut output);
1011 assert_eq!(output, expected[i - 2], "error at round {}", i);
1012 core::hint::black_box(&mut buffers0);
1013 (buffers0, buffers1) = (buffers1, buffers0);
1014 buffers1.set_input(&hmacs[i], b"salt");
1015 }
1016 buffers0.ro_mix_back();
1017 buffers1.scrypt_ro_mix();
1018 buffers0.extract_output(&hmacs[14], &mut output);
1019 assert_eq!(output, expected[14]);
1020 buffers1.extract_output(&hmacs[15], &mut output);
1021 assert_eq!(output, expected[15]);
1022 }
1023
1024 write_test!(
1025 test_ro_mix_cas_avx512f_1,
1026 test_ro_mix_cas_ex,
1027 U1,
1028 salsa20::x86_64::BlockAvx512F
1029 );
1030 write_test!(
1031 test_ro_mix_cas_avx512f_2,
1032 test_ro_mix_cas_ex,
1033 U2,
1034 salsa20::x86_64::BlockAvx512F
1035 );
1036 write_test!(
1037 test_ro_mix_cas_avx512f_4,
1038 test_ro_mix_cas_ex,
1039 U4,
1040 salsa20::x86_64::BlockAvx512F
1041 );
1042 write_test!(
1043 test_ro_mix_cas_avx512f_8,
1044 test_ro_mix_cas_ex,
1045 U8,
1046 salsa20::x86_64::BlockAvx512F
1047 );
1048
1049 write_test!(
1050 test_ro_mix_cas_avx512f_16,
1051 test_ro_mix_cas_ex,
1052 U16,
1053 salsa20::x86_64::BlockAvx512F
1054 );
1055 write_test!(
1056 test_ro_mix_cas_interleaved_avx512f_1,
1057 test_ro_mix_cas_interleaved_ex,
1058 U1,
1059 salsa20::x86_64::BlockAvx512F,
1060 salsa20::x86_64::BlockAvx512FMb2
1061 );
1062 write_test!(
1063 test_ro_mix_cas_interleaved_avx512f_2,
1064 test_ro_mix_cas_interleaved_ex,
1065 U2,
1066 salsa20::x86_64::BlockAvx512F,
1067 salsa20::x86_64::BlockAvx512FMb2
1068 );
1069 #[cfg(all(target_arch = "x86_64", target_feature = "avx512f"))]
1070 write_test!(
1071 test_ro_mix_cas_interleaved_avx512f_4,
1072 test_ro_mix_cas_interleaved_ex,
1073 U4,
1074 salsa20::x86_64::BlockAvx512F,
1075 salsa20::x86_64::BlockAvx512FMb2
1076 );
1077 write_test!(
1078 test_ro_mix_cas_interleaved_avx512f_8,
1079 test_ro_mix_cas_interleaved_ex,
1080 U8,
1081 salsa20::x86_64::BlockAvx512F,
1082 salsa20::x86_64::BlockAvx512FMb2
1083 );
1084
1085 write_test!(test_ro_mix_cas_zmm_1, test_ro_mix_cas_zmm, U1);
1088 write_test!(test_ro_mix_cas_zmm_2, test_ro_mix_cas_zmm, U2);
1089 write_test!(test_ro_mix_cas_zmm_4, test_ro_mix_cas_zmm, U4);
1090 write_test!(test_ro_mix_cas_zmm_8, test_ro_mix_cas_zmm, U8);
1091 write_test!(
1092 test_ro_mix_cas_interleaved_zmm_1,
1093 test_ro_mix_cas_interleaved_zmm,
1094 U1
1095 );
1096 write_test!(
1097 test_ro_mix_cas_interleaved_zmm_2,
1098 test_ro_mix_cas_interleaved_zmm,
1099 U2
1100 );
1101 write_test!(
1102 test_ro_mix_cas_interleaved_zmm_4,
1103 test_ro_mix_cas_interleaved_zmm,
1104 U4
1105 );
1106 write_test!(
1107 test_ro_mix_cas_interleaved_zmm_8,
1108 test_ro_mix_cas_interleaved_zmm,
1109 U8
1110 );
1111 }
1112
1113 write_test!(test_ro_mix_cas_1, test_ro_mix_cas, U1);
1115 write_test!(test_ro_mix_cas_2, test_ro_mix_cas, U2);
1116 write_test!(test_ro_mix_cas_4, test_ro_mix_cas, U4);
1117 write_test!(test_ro_mix_cas_8, test_ro_mix_cas, U8);
1118 write_test!(test_ro_mix_cas_16, test_ro_mix_cas, U16);
1119
1120 write_test!(
1121 test_ro_mix_cas_interleaved_1,
1122 test_ro_mix_cas_interleaved,
1123 U1
1124 );
1125
1126 write_test!(
1127 test_ro_mix_cas_interleaved_2,
1128 test_ro_mix_cas_interleaved,
1129 U2
1130 );
1131
1132 write_test!(
1133 test_ro_mix_cas_interleaved_4,
1134 test_ro_mix_cas_interleaved,
1135 U4
1136 );
1137
1138 write_test!(
1139 test_ro_mix_cas_interleaved_8,
1140 test_ro_mix_cas_interleaved,
1141 U8
1142 );
1143
1144 write_test!(
1145 test_ro_mix_cas_interleaved_16,
1146 test_ro_mix_cas_interleaved,
1147 U16
1148 );
1149
1150 #[cfg(all(target_arch = "x86_64", target_feature = "avx2"))]
1152 mod avx2 {
1153 use super::*;
1154
1155 write_test!(
1156 test_ro_mix_cas_interleaved_1_avx2,
1157 test_ro_mix_cas_interleaved_ex,
1158 U1,
1159 salsa20::x86_64::BlockSse2<U1>,
1160 salsa20::x86_64::BlockAvx2Mb2
1161 );
1162
1163 write_test!(
1164 test_ro_mix_cas_interleaved_2_avx2,
1165 test_ro_mix_cas_interleaved_ex,
1166 U2,
1167 salsa20::x86_64::BlockSse2<U1>,
1168 salsa20::x86_64::BlockAvx2Mb2
1169 );
1170
1171 write_test!(
1172 test_ro_mix_cas_interleaved_4_avx2,
1173 test_ro_mix_cas_interleaved_ex,
1174 U4,
1175 salsa20::x86_64::BlockSse2<U1>,
1176 salsa20::x86_64::BlockAvx2Mb2
1177 );
1178
1179 write_test!(
1180 test_ro_mix_cas_interleaved_8_avx2,
1181 test_ro_mix_cas_interleaved_ex,
1182 U8,
1183 salsa20::x86_64::BlockSse2<U1>,
1184 salsa20::x86_64::BlockAvx2Mb2
1185 );
1186
1187 write_test!(
1188 test_ro_mix_cas_interleaved_16_avx2,
1189 test_ro_mix_cas_interleaved_ex,
1190 U16,
1191 salsa20::x86_64::BlockSse2<U1>,
1192 salsa20::x86_64::BlockAvx2Mb2
1193 );
1194 }
1195
1196 #[cfg(target_arch = "x86_64")]
1197 write_test!(
1198 test_ro_mix_cas_1_sse2,
1199 test_ro_mix_cas_ex,
1200 U1,
1201 salsa20::x86_64::BlockSse2<U1>,
1202 );
1203 #[cfg(target_arch = "x86_64")]
1204 write_test!(
1205 test_ro_mix_cas_2_sse2,
1206 test_ro_mix_cas_ex,
1207 U2,
1208 salsa20::x86_64::BlockSse2<U1>,
1209 );
1210 #[cfg(target_arch = "x86_64")]
1211 write_test!(
1212 test_ro_mix_cas_4_sse2,
1213 test_ro_mix_cas_ex,
1214 U4,
1215 salsa20::x86_64::BlockSse2<U1>,
1216 );
1217 #[cfg(target_arch = "x86_64")]
1218 write_test!(
1219 test_ro_mix_cas_8_sse2,
1220 test_ro_mix_cas_ex,
1221 U8,
1222 salsa20::x86_64::BlockSse2<U1>,
1223 );
1224 #[cfg(target_arch = "x86_64")]
1225 write_test!(
1226 test_ro_mix_cas_16_sse2,
1227 test_ro_mix_cas_ex,
1228 U16,
1229 salsa20::x86_64::BlockSse2<U1>,
1230 );
1231
1232 write_test!(
1235 test_ro_mix_cas_scalar_1,
1236 test_ro_mix_cas_ex,
1237 U1,
1238 salsa20::BlockScalar<U1>
1239 );
1240
1241 write_test!(
1242 test_ro_mix_cas_scalar_2,
1243 test_ro_mix_cas_ex,
1244 U2,
1245 salsa20::BlockScalar<U1>
1246 );
1247
1248 write_test!(
1249 test_ro_mix_cas_scalar_4,
1250 test_ro_mix_cas_ex,
1251 U4,
1252 salsa20::BlockScalar<U1>
1253 );
1254
1255 write_test!(
1256 test_ro_mix_cas_scalar_8,
1257 test_ro_mix_cas_ex,
1258 U8,
1259 salsa20::BlockScalar<U1>
1260 );
1261
1262 write_test!(
1263 test_ro_mix_cas_scalar_16,
1264 test_ro_mix_cas_ex,
1265 U16,
1266 salsa20::BlockScalar<U1>
1267 );
1268
1269 write_test!(
1270 test_ro_mix_cas_scalar_interleaved_1,
1271 test_ro_mix_cas_interleaved_ex,
1272 U1,
1273 salsa20::BlockScalar<U1>,
1274 salsa20::BlockScalar<U2>
1275 );
1276
1277 write_test!(
1278 test_ro_mix_cas_scalar_interleaved_2,
1279 test_ro_mix_cas_interleaved_ex,
1280 U2,
1281 salsa20::BlockScalar<U1>,
1282 salsa20::BlockScalar<U2>
1283 );
1284
1285 write_test!(
1286 test_ro_mix_cas_scalar_interleaved_4,
1287 test_ro_mix_cas_interleaved_ex,
1288 U4,
1289 salsa20::BlockScalar<U1>,
1290 salsa20::BlockScalar<U2>
1291 );
1292
1293 write_test!(
1294 test_ro_mix_cas_scalar_interleaved_8,
1295 test_ro_mix_cas_interleaved_ex,
1296 U8,
1297 salsa20::BlockScalar<U1>,
1298 salsa20::BlockScalar<U2>
1299 );
1300
1301 write_test!(
1302 test_ro_mix_cas_scalar_interleaved_16,
1303 test_ro_mix_cas_interleaved_ex,
1304 U16,
1305 salsa20::BlockScalar<U1>,
1306 salsa20::BlockScalar<U2>
1307 );
1308
1309 #[cfg(feature = "portable-simd")]
1312 write_test!(
1313 test_ro_mix_cas_portable_simd_1,
1314 test_ro_mix_cas_ex,
1315 U1,
1316 salsa20::BlockPortableSimd
1317 );
1318
1319 #[cfg(feature = "portable-simd")]
1320 write_test!(
1321 test_ro_mix_cas_portable_simd_2,
1322 test_ro_mix_cas_ex,
1323 U2,
1324 salsa20::BlockPortableSimd
1325 );
1326
1327 #[cfg(feature = "portable-simd")]
1328 write_test!(
1329 test_ro_mix_cas_portable_simd_4,
1330 test_ro_mix_cas_ex,
1331 U4,
1332 salsa20::BlockPortableSimd
1333 );
1334
1335 #[cfg(feature = "portable-simd")]
1336 write_test!(
1337 test_ro_mix_cas_portable_simd_8,
1338 test_ro_mix_cas_ex,
1339 U8,
1340 salsa20::BlockPortableSimd
1341 );
1342
1343 #[cfg(feature = "portable-simd")]
1344 write_test!(
1345 test_ro_mix_cas_portable_simd_16,
1346 test_ro_mix_cas_ex,
1347 U16,
1348 salsa20::BlockPortableSimd
1349 );
1350
1351 #[cfg(feature = "portable-simd")]
1354 write_test!(
1355 test_ro_mix_cas_portable_simd_interleaved_1,
1356 test_ro_mix_cas_interleaved_ex,
1357 U1,
1358 salsa20::BlockPortableSimd,
1359 salsa20::BlockPortableSimd2
1360 );
1361
1362 #[cfg(feature = "portable-simd")]
1363 write_test!(
1364 test_ro_mix_cas_portable_simd_interleaved_2,
1365 test_ro_mix_cas_interleaved_ex,
1366 U2,
1367 salsa20::BlockPortableSimd,
1368 salsa20::BlockPortableSimd2
1369 );
1370
1371 #[cfg(feature = "portable-simd")]
1372 write_test!(
1373 test_ro_mix_cas_portable_simd_interleaved_4,
1374 test_ro_mix_cas_interleaved_ex,
1375 U4,
1376 salsa20::BlockPortableSimd,
1377 salsa20::BlockPortableSimd2
1378 );
1379
1380 #[cfg(feature = "portable-simd")]
1381 write_test!(
1382 test_ro_mix_cas_portable_simd_interleaved_8,
1383 test_ro_mix_cas_interleaved_ex,
1384 U8,
1385 salsa20::BlockPortableSimd,
1386 salsa20::BlockPortableSimd2
1387 );
1388
1389 #[cfg(feature = "portable-simd")]
1390 write_test!(
1391 test_ro_mix_cas_portable_simd_interleaved_16,
1392 test_ro_mix_cas_interleaved_ex,
1393 U16,
1394 salsa20::BlockPortableSimd,
1395 salsa20::BlockPortableSimd2
1396 );
1397}