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