Skip to main content

simoom_sys/
arch.rs

1//! Hardware crypto intrinsics behind safe wrappers, with runtime detection, and the operating
2//! system's random number source.
3//!
4//! - **AES** ([`AesHw`](crate::arch::AesHw)): AES-NI on x86-64, the ARMv8 Cryptography Extension on aarch64. Round
5//!   keys come from the caller (the portable, constant-time key schedule in `simoom-tls`), so only
6//!   the rounds run here.
7//! - **GHASH** ([`GhashHw`](crate::arch::GhashHw)): PCLMULQDQ on x86-64, PMULL on aarch64, with the powers H^1..H^8
8//!   precomputed so eight blocks share one reduction.
9//! - **AES-GCM bulk** ([`aes_gcm_seal`](crate::arch::aes_gcm_seal), [`aes_gcm_open`](crate::arch::aes_gcm_open)): CTR and GHASH fused, eight blocks per
10//!   step.
11//! - **SHA-256** ([`sha256_blocks`](crate::arch::sha256_blocks)): SHA-NI on x86-64, the ARMv8 SHA-2 instructions on aarch64.
12//! - **Randomness** ([`os_random`](crate::arch::os_random)): `getrandom(2)` on Linux and Android, `getentropy(3)` on the
13//!   BSDs and macOS, `BCryptGenRandom` on Windows.
14//!
15//! A value of [`AesHw`](crate::arch::AesHw) or [`GhashHw`](crate::arch::GhashHw) exists only if the CPU has the instructions (the
16//! constructors check), so every method can call the `#[target_feature]` code without asking
17//! again. All these instructions run in constant time: no table lookups, no data-dependent
18//! branches.
19
20use std::sync::atomic::{AtomicU8, Ordering};
21
22const F_AES: u8 = 1;
23const F_CLMUL: u8 = 2;
24const F_SHA256: u8 = 4;
25const F_SIMD4: u8 = 8;
26const F_SIMD8: u8 = 16;
27const F_DONE: u8 = 0x80;
28
29static FEATURES: AtomicU8 = AtomicU8::new(0);
30static DISABLED: AtomicU8 = AtomicU8::new(0);
31
32fn detect() -> u8 {
33    #[allow(unused_mut)]
34    let mut f = 0u8;
35    #[cfg(target_arch = "x86_64")]
36    {
37        use crate::cpu::x86;
38        let x = crate::cpu::x86_features();
39        let ssse3 = x & x86::SSSE3 != 0 && x & x86::SSE41 != 0;
40        if ssse3 && x & x86::AES != 0 {
41            f |= F_AES;
42        }
43        if ssse3 && x & x86::PCLMULQDQ != 0 {
44            f |= F_CLMUL;
45        }
46        if ssse3 && x & x86::SHA != 0 {
47            f |= F_SHA256;
48        }
49        if ssse3 {
50            f |= F_SIMD4;
51        }
52        if ssse3 && x & x86::AVX2 != 0 {
53            f |= F_SIMD8;
54        }
55    }
56    #[cfg(target_arch = "aarch64")]
57    {
58        if std::arch::is_aarch64_feature_detected!("aes") {
59            f |= F_AES;
60        }
61        if std::arch::is_aarch64_feature_detected!("pmull") {
62            f |= F_CLMUL;
63        }
64        if std::arch::is_aarch64_feature_detected!("sha2") {
65            f |= F_SHA256;
66        }
67    }
68    f
69}
70
71#[inline]
72fn features() -> u8 {
73    let mut f = FEATURES.load(Ordering::Relaxed);
74    if f == 0 {
75        f = detect() | F_DONE;
76        FEATURES.store(f, Ordering::Relaxed);
77    }
78    f & !DISABLED.load(Ordering::Relaxed)
79}
80
81/// True if the CPU has AES round instructions (AES-NI or ARMv8 AES).
82#[inline]
83pub fn has_aes() -> bool {
84    features() & F_AES != 0
85}
86
87/// True if the CPU has a 64x64-bit carry-less multiply (PCLMULQDQ or PMULL).
88#[inline]
89pub fn has_clmul() -> bool {
90    features() & F_CLMUL != 0
91}
92
93/// True if the CPU has SHA-256 instructions (SHA-NI or ARMv8 SHA-2).
94#[inline]
95pub fn has_sha256() -> bool {
96    features() & F_SHA256 != 0
97}
98
99/// Turns the hardware paths off (`true`) or back on, process-wide, so tests and benchmarks can
100/// exercise the portable fallbacks. Values built while enabled keep working.
101pub fn disable_hardware(disabled: bool) {
102    DISABLED.store(
103        if disabled {
104            F_AES | F_CLMUL | F_SHA256 | F_SIMD4 | F_SIMD8
105        } else {
106            0
107        },
108        Ordering::Relaxed,
109    );
110}
111
112/// ChaCha20 keystream XOR with SIMD (x86-64: AVX2 eight blocks per step, SSSE3 four), over
113/// the longest prefix of `data` that is a whole number of 256-byte groups. Block counters start
114/// at `counter`. Returns the number of bytes processed (0 if the CPU has no SIMD path); the
115/// caller handles the rest.
116pub fn chacha20_xor(key: &[u8; 32], counter: u32, nonce: &[u8; 12], data: &mut [u8]) -> usize {
117    let f = features();
118    let mut st = [0u32; 16];
119    st[..4].copy_from_slice(&[0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]);
120    for i in 0..8 {
121        st[4 + i] =
122            u32::from_le_bytes([key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]]);
123    }
124    st[12] = counter;
125    for i in 0..3 {
126        st[13 + i] = u32::from_le_bytes([
127            nonce[4 * i],
128            nonce[4 * i + 1],
129            nonce[4 * i + 2],
130            nonce[4 * i + 3],
131        ]);
132    }
133    #[allow(unused_mut)]
134    let mut done = 0;
135    #[cfg(target_arch = "x86_64")]
136    {
137        if f & F_SIMD8 != 0 && data.len() >= 512 {
138            let n = data.len() / 512 * 512;
139            // SAFETY: F_SIMD8 means the CPU has AVX2 (and SSSE3); `n` is a multiple of 512 and at
140            // most `data.len()`.
141            unsafe { x86::chacha8(&mut st, &mut data[..n]) };
142            done = n;
143        }
144        if f & F_SIMD4 != 0 && data.len() - done >= 256 {
145            let n = (data.len() - done) / 256 * 256;
146            // SAFETY: F_SIMD4 means the CPU has SSSE3; `n` is a multiple of 256 and the slice is
147            // within `data`.
148            unsafe { x86::chacha4(&mut st, &mut data[done..done + n]) };
149            done += n;
150        }
151    }
152    let _ = (f, &mut st, &data);
153    done
154}
155
156/// Expanded AES round keys for the hardware AES path (AES-128 or AES-256).
157#[derive(Clone)]
158pub struct AesHw {
159    rk: [[u8; 16]; 15],
160    nr: usize,
161}
162
163impl std::fmt::Debug for AesHw {
164    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165        write!(f, "AesHw(AES-{})", if self.nr == 10 { 128 } else { 256 })
166    }
167}
168
169impl AesHw {
170    /// Takes the FIPS-197 round keys (11 for AES-128, 15 for AES-256, in the standard byte
171    /// order). Returns `None` if the CPU lacks AES instructions or the count is wrong.
172    pub fn new(round_keys: &[[u8; 16]]) -> Option<AesHw> {
173        if !has_aes() || !(round_keys.len() == 11 || round_keys.len() == 15) {
174            return None;
175        }
176        let mut rk = [[0u8; 16]; 15];
177        rk[..round_keys.len()].copy_from_slice(round_keys);
178        Some(AesHw {
179            rk,
180            nr: round_keys.len() - 1,
181        })
182    }
183
184    /// Encrypts one block in place.
185    pub fn encrypt_block(&self, block: &mut [u8; 16]) {
186        #[cfg(target_arch = "x86_64")]
187        // SAFETY: an `AesHw` exists only if `has_aes()` was true (AES-NI, SSSE3, SSE4.1).
188        unsafe {
189            x86::aes_block(&self.rk, self.nr, block)
190        }
191        #[cfg(target_arch = "aarch64")]
192        // SAFETY: an `AesHw` exists only if `has_aes()` was true (ARMv8 AES).
193        unsafe {
194            arm::aes_block(&self.rk, self.nr, block)
195        }
196        #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
197        {
198            let _ = block;
199            unreachable!("AesHw is never built on this architecture")
200        }
201    }
202}
203
204/// GHASH key with the powers H^1..H^8 precomputed for the carry-less multiply path.
205#[derive(Clone)]
206pub struct GhashHw {
207    /// H^(i+1) in the internal (byte-reversed) representation.
208    h: [[u8; 16]; 8],
209}
210
211impl std::fmt::Debug for GhashHw {
212    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213        f.write_str("GhashHw")
214    }
215}
216
217impl GhashHw {
218    /// `h` is the hash subkey E(K, 0^128). Returns `None` if the CPU lacks carry-less multiply.
219    pub fn new(h: &[u8; 16]) -> Option<GhashHw> {
220        if !has_clmul() {
221            return None;
222        }
223        #[allow(unused_mut)]
224        let mut g = GhashHw { h: [[0; 16]; 8] };
225        #[cfg(target_arch = "x86_64")]
226        // SAFETY: `has_clmul()` was true (PCLMULQDQ, SSSE3, SSE4.1).
227        unsafe {
228            x86::ghash_init(h, &mut g.h)
229        }
230        #[cfg(target_arch = "aarch64")]
231        // SAFETY: `has_clmul()` was true (PMULL).
232        unsafe {
233            arm::ghash_init(h, &mut g.h)
234        }
235        #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
236        let _ = h;
237        Some(g)
238    }
239
240    /// Absorbs `data` into the GHASH state `y` (standard byte order). A final partial block is
241    /// padded with zeros, as GCM requires.
242    pub fn update(&self, y: &mut [u8; 16], data: &[u8]) {
243        #[cfg(target_arch = "x86_64")]
244        // SAFETY: a `GhashHw` exists only if `has_clmul()` was true.
245        unsafe {
246            x86::ghash_update(&self.h, y, data)
247        }
248        #[cfg(target_arch = "aarch64")]
249        // SAFETY: a `GhashHw` exists only if `has_clmul()` was true.
250        unsafe {
251            arm::ghash_update(&self.h, y, data)
252        }
253        #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
254        let _ = (y, data);
255    }
256}
257
258/// AES-GCM bulk encryption: encrypts `data` in place in CTR mode starting at counter block
259/// `ctr` (incremented as a 32-bit big-endian counter, left at the next value) and absorbs the
260/// ciphertext into the GHASH state `y`.
261pub fn aes_gcm_seal(
262    aes: &AesHw,
263    gh: &GhashHw,
264    ctr: &mut [u8; 16],
265    y: &mut [u8; 16],
266    data: &mut [u8],
267) {
268    #[cfg(target_arch = "x86_64")]
269    // SAFETY: `AesHw` and `GhashHw` values exist only if the CPU has AES-NI and PCLMULQDQ.
270    unsafe {
271        x86::gcm(&aes.rk, aes.nr, &gh.h, ctr, y, data, true)
272    }
273    #[cfg(target_arch = "aarch64")]
274    // SAFETY: `AesHw` and `GhashHw` values exist only if the CPU has ARMv8 AES and PMULL.
275    unsafe {
276        arm::gcm(&aes.rk, aes.nr, &gh.h, ctr, y, data, true)
277    }
278    #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
279    let _ = (aes, gh, ctr, y, data);
280}
281
282/// AES-GCM bulk decryption: absorbs the ciphertext `data` into `y`, then decrypts it in place.
283pub fn aes_gcm_open(
284    aes: &AesHw,
285    gh: &GhashHw,
286    ctr: &mut [u8; 16],
287    y: &mut [u8; 16],
288    data: &mut [u8],
289) {
290    #[cfg(target_arch = "x86_64")]
291    // SAFETY: `AesHw` and `GhashHw` values exist only if the CPU has AES-NI and PCLMULQDQ.
292    unsafe {
293        x86::gcm(&aes.rk, aes.nr, &gh.h, ctr, y, data, false)
294    }
295    #[cfg(target_arch = "aarch64")]
296    // SAFETY: `AesHw` and `GhashHw` values exist only if the CPU has ARMv8 AES and PMULL.
297    unsafe {
298        arm::gcm(&aes.rk, aes.nr, &gh.h, ctr, y, data, false)
299    }
300    #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
301    let _ = (aes, gh, ctr, y, data);
302}
303
304/// Runs the SHA-256 compression function over whole 64-byte blocks with the CPU's SHA
305/// instructions. Returns false (and does nothing) if the CPU lacks them or `blocks.len()` is
306/// not a multiple of 64.
307pub fn sha256_blocks(state: &mut [u32; 8], blocks: &[u8]) -> bool {
308    if !has_sha256() || blocks.len() % 64 != 0 {
309        return false;
310    }
311    #[cfg(target_arch = "x86_64")]
312    // SAFETY: `has_sha256()` was true (SHA-NI, SSSE3, SSE4.1); the length is a multiple of 64.
313    unsafe {
314        x86::sha256(state, blocks)
315    }
316    #[cfg(target_arch = "aarch64")]
317    // SAFETY: `has_sha256()` was true (ARMv8 SHA-2); the length is a multiple of 64.
318    unsafe {
319        arm::sha256(state, blocks)
320    }
321    #[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
322    let _ = state;
323    true
324}
325
326/// SHA-256 round constants, shared by both architectures.
327#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
328static K256: [u32; 64] = [
329    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
330    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
331    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
332    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
333    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
334    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
335    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
336    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
337];
338
339#[cfg(target_arch = "x86_64")]
340mod x86 {
341    use core::arch::x86_64::*;
342
343    // The GHASH representation: a block loaded with its 16 bytes reversed is the 128-bit
344    // big-endian integer of the block, whose bit i is the coefficient of x^(127-i) (GCM's
345    // reflected bit order). A carry-less product of two such values is the reflected product
346    // shifted right by one bit; `ghash_reduce!` shifts it back and reduces it modulo
347    // x^128 + x^7 + x^2 + x + 1.
348
349    macro_rules! rev {
350        () => {
351            _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
352        };
353    }
354
355    // Accumulates the Karatsuba partial products of a*b into (lo, mid, hi).
356    macro_rules! clmul_acc {
357        ($lo:ident, $mid:ident, $hi:ident, $a:expr, $b:expr) => {{
358            let a = $a;
359            let b = $b;
360            $lo = _mm_xor_si128($lo, _mm_clmulepi64_si128(a, b, 0x00));
361            $hi = _mm_xor_si128($hi, _mm_clmulepi64_si128(a, b, 0x11));
362            let a2 = _mm_xor_si128(a, _mm_shuffle_epi32(a, 0x4e));
363            let b2 = _mm_xor_si128(b, _mm_shuffle_epi32(b, 0x4e));
364            $mid = _mm_xor_si128($mid, _mm_clmulepi64_si128(a2, b2, 0x00));
365        }};
366    }
367
368    // Combines (lo, mid, hi) into the 256-bit product, shifts it left by one bit and reduces.
369    macro_rules! ghash_reduce {
370        ($lo:expr, $mid:expr, $hi:expr) => {{
371            let (mut lo, mut hi) = ($lo, $hi);
372            let mid = _mm_xor_si128($mid, _mm_xor_si128(lo, hi));
373            lo = _mm_xor_si128(lo, _mm_slli_si128(mid, 8));
374            hi = _mm_xor_si128(hi, _mm_srli_si128(mid, 8));
375            let clo = _mm_srli_epi64(lo, 63);
376            let chi = _mm_srli_epi64(hi, 63);
377            lo = _mm_or_si128(_mm_slli_epi64(lo, 1), _mm_slli_si128(clo, 8));
378            hi = _mm_or_si128(
379                _mm_or_si128(_mm_slli_epi64(hi, 1), _mm_slli_si128(chi, 8)),
380                _mm_srli_si128(clo, 8),
381            );
382            let g = _mm_xor_si128(
383                _mm_xor_si128(_mm_slli_epi64(lo, 63), _mm_slli_epi64(lo, 62)),
384                _mm_slli_epi64(lo, 57),
385            );
386            lo = _mm_xor_si128(lo, _mm_slli_si128(g, 8));
387            let f = _mm_xor_si128(
388                _mm_xor_si128(lo, _mm_srli_epi64(lo, 1)),
389                _mm_xor_si128(_mm_srli_epi64(lo, 2), _mm_srli_epi64(lo, 7)),
390            );
391            hi = _mm_xor_si128(hi, f);
392            let g = _mm_xor_si128(
393                _mm_xor_si128(_mm_slli_epi64(lo, 63), _mm_slli_epi64(lo, 62)),
394                _mm_slli_epi64(lo, 57),
395            );
396            _mm_xor_si128(hi, _mm_srli_si128(g, 8))
397        }};
398    }
399
400    macro_rules! ghash_mul {
401        ($a:expr, $b:expr) => {{
402            let z = _mm_setzero_si128();
403            let (mut lo, mut mid, mut hi) = (z, z, z);
404            clmul_acc!(lo, mid, hi, $a, $b);
405            ghash_reduce!(lo, mid, hi)
406        }};
407    }
408
409    macro_rules! ld {
410        ($p:expr) => {
411            _mm_loadu_si128(($p).as_ptr() as *const __m128i)
412        };
413    }
414
415    macro_rules! st {
416        ($p:expr, $v:expr) => {
417            _mm_storeu_si128(($p).as_mut_ptr() as *mut __m128i, $v)
418        };
419    }
420
421    /// # Safety
422    /// The CPU must have AES-NI.
423    #[target_feature(enable = "aes,sse2")]
424    pub(super) unsafe fn aes_block(rk: &[[u8; 16]; 15], nr: usize, block: &mut [u8; 16]) {
425        // SAFETY: 16-byte unaligned loads and stores of 16-byte arrays; `nr` is 10 or 14, so
426        // every index is below 15.
427        unsafe {
428            let mut x = _mm_xor_si128(ld!(block), ld!(rk[0]));
429            for k in &rk[1..nr] {
430                x = _mm_aesenc_si128(x, ld!(k));
431            }
432            x = _mm_aesenclast_si128(x, ld!(rk[nr]));
433            st!(block, x);
434        }
435    }
436
437    /// # Safety
438    /// The CPU must have PCLMULQDQ and SSSE3.
439    #[target_feature(enable = "pclmulqdq,ssse3,sse2")]
440    pub(super) unsafe fn ghash_init(h: &[u8; 16], out: &mut [[u8; 16]; 8]) {
441        // SAFETY: unaligned 16-byte loads and stores of 16-byte arrays.
442        unsafe {
443            let h1 = _mm_shuffle_epi8(ld!(h), rev!());
444            let mut p = h1;
445            st!(out[0], p);
446            for o in out.iter_mut().skip(1) {
447                p = ghash_mul!(p, h1);
448                st!(o, p);
449            }
450        }
451    }
452
453    /// # Safety
454    /// The CPU must have PCLMULQDQ and SSSE3.
455    #[target_feature(enable = "pclmulqdq,ssse3,sse2")]
456    pub(super) unsafe fn ghash_update(hp: &[[u8; 16]; 8], y: &mut [u8; 16], data: &[u8]) {
457        // SAFETY: loads read 16 bytes at offsets `i` with `i + 16 <= data.len()`; the partial
458        // tail is copied into a local 16-byte block first. Other loads and stores are 16-byte
459        // arrays.
460        unsafe {
461            let r = rev!();
462            let mut h = [_mm_setzero_si128(); 8];
463            for (k, v) in h.iter_mut().enumerate() {
464                *v = ld!(hp[k]);
465            }
466            let mut acc = _mm_shuffle_epi8(ld!(y), r);
467            let n = data.len();
468            let p = data.as_ptr();
469            let mut i = 0;
470            while i + 128 <= n {
471                let z = _mm_setzero_si128();
472                let (mut lo, mut mid, mut hi) = (z, z, z);
473                let x0 = _mm_xor_si128(
474                    acc,
475                    _mm_shuffle_epi8(_mm_loadu_si128(p.add(i) as *const __m128i), r),
476                );
477                clmul_acc!(lo, mid, hi, x0, h[7]);
478                for j in 1..8 {
479                    let x =
480                        _mm_shuffle_epi8(_mm_loadu_si128(p.add(i + 16 * j) as *const __m128i), r);
481                    clmul_acc!(lo, mid, hi, x, h[7 - j]);
482                }
483                acc = ghash_reduce!(lo, mid, hi);
484                i += 128;
485            }
486            while i + 16 <= n {
487                let x = _mm_shuffle_epi8(_mm_loadu_si128(p.add(i) as *const __m128i), r);
488                acc = ghash_mul!(_mm_xor_si128(acc, x), h[0]);
489                i += 16;
490            }
491            if i < n {
492                let mut t = [0u8; 16];
493                t[..n - i].copy_from_slice(&data[i..]);
494                acc = ghash_mul!(_mm_xor_si128(acc, _mm_shuffle_epi8(ld!(t), r)), h[0]);
495            }
496            st!(y, _mm_shuffle_epi8(acc, r));
497        }
498    }
499
500    /// # Safety
501    /// The CPU must have AES-NI, PCLMULQDQ and SSSE3; `nr` is 10 or 14.
502    #[target_feature(enable = "aes,pclmulqdq,ssse3,sse2")]
503    pub(super) unsafe fn gcm(
504        rkb: &[[u8; 16]; 15],
505        nr: usize,
506        hp: &[[u8; 16]; 8],
507        ctr: &mut [u8; 16],
508        y: &mut [u8; 16],
509        data: &mut [u8],
510        encrypt: bool,
511    ) {
512        // SAFETY: loads and stores of the data touch 16 bytes at offsets `i` with
513        // `i + 16 <= data.len()`; the partial tail goes through a local 16-byte block. The round
514        // key and power arrays are read within their bounds (`nr` <= 14).
515        unsafe {
516            let r = rev!();
517            let mut rk = [_mm_setzero_si128(); 15];
518            for k in 0..=nr {
519                rk[k] = ld!(rkb[k]);
520            }
521            let mut h = [_mm_setzero_si128(); 8];
522            for (k, v) in h.iter_mut().enumerate() {
523                *v = ld!(hp[k]);
524            }
525            let one = _mm_set_epi32(0, 0, 0, 1);
526            // The counter block byte-reversed: its low 32-bit lane is the counter as an integer,
527            // so `paddd` performs GCM's inc32.
528            let mut c = _mm_shuffle_epi8(ld!(ctr), r);
529            let mut acc = _mm_shuffle_epi8(ld!(y), r);
530            let n = data.len();
531            let p = data.as_mut_ptr();
532            let mut i = 0;
533            while i + 128 <= n {
534                let mut b = [_mm_setzero_si128(); 8];
535                for v in b.iter_mut() {
536                    *v = _mm_xor_si128(_mm_shuffle_epi8(c, r), rk[0]);
537                    c = _mm_add_epi32(c, one);
538                }
539                let mut x = [_mm_setzero_si128(); 8];
540                if !encrypt {
541                    for (j, v) in x.iter_mut().enumerate() {
542                        *v = _mm_loadu_si128(p.add(i + 16 * j) as *const __m128i);
543                    }
544                }
545                for k in &rk[1..nr] {
546                    for v in b.iter_mut() {
547                        *v = _mm_aesenc_si128(*v, *k);
548                    }
549                }
550                for v in b.iter_mut() {
551                    *v = _mm_aesenclast_si128(*v, rk[nr]);
552                }
553                if encrypt {
554                    for j in 0..8 {
555                        x[j] = _mm_xor_si128(
556                            b[j],
557                            _mm_loadu_si128(p.add(i + 16 * j) as *const __m128i),
558                        );
559                        _mm_storeu_si128(p.add(i + 16 * j) as *mut __m128i, x[j]);
560                    }
561                } else {
562                    for j in 0..8 {
563                        _mm_storeu_si128(
564                            p.add(i + 16 * j) as *mut __m128i,
565                            _mm_xor_si128(b[j], x[j]),
566                        );
567                    }
568                }
569                let z = _mm_setzero_si128();
570                let (mut lo, mut mid, mut hi) = (z, z, z);
571                clmul_acc!(
572                    lo,
573                    mid,
574                    hi,
575                    _mm_xor_si128(acc, _mm_shuffle_epi8(x[0], r)),
576                    h[7]
577                );
578                for j in 1..8 {
579                    clmul_acc!(lo, mid, hi, _mm_shuffle_epi8(x[j], r), h[7 - j]);
580                }
581                acc = ghash_reduce!(lo, mid, hi);
582                i += 128;
583            }
584            while i < n {
585                let mut ks = _mm_xor_si128(_mm_shuffle_epi8(c, r), rk[0]);
586                c = _mm_add_epi32(c, one);
587                for k in &rk[1..nr] {
588                    ks = _mm_aesenc_si128(ks, *k);
589                }
590                ks = _mm_aesenclast_si128(ks, rk[nr]);
591                let take = (n - i).min(16);
592                let mut t = [0u8; 16];
593                t[..take].copy_from_slice(&data[i..i + take]);
594                let out = _mm_xor_si128(ld!(t), ks);
595                let mut o = [0u8; 16];
596                st!(o, out);
597                // GHASH absorbs the ciphertext, zero-padded.
598                let mut ct = if encrypt { o } else { t };
599                ct[take..].fill(0);
600                acc = ghash_mul!(_mm_xor_si128(acc, _mm_shuffle_epi8(ld!(ct), r)), h[0]);
601                data[i..i + take].copy_from_slice(&o[..take]);
602                i += take;
603            }
604            st!(ctr, _mm_shuffle_epi8(c, r));
605            st!(y, _mm_shuffle_epi8(acc, r));
606        }
607    }
608
609    /// # Safety
610    /// The CPU must have SHA-NI, SSSE3 and SSE4.1; `blocks.len()` is a multiple of 64.
611    #[target_feature(enable = "sha,ssse3,sse4.1,sse2")]
612    pub(super) unsafe fn sha256(state: &mut [u32; 8], blocks: &[u8]) {
613        // SAFETY: `state` is 8 u32 (two 16-byte loads and stores); each block load reads 16
614        // bytes at offsets below `blocks.len()` (a multiple of 64); `K256` has 64 entries.
615        unsafe {
616            let swap = _mm_set_epi64x(0x0c0d_0e0f_0809_0a0b, 0x0405_0607_0001_0203);
617            let mut tmp = _mm_loadu_si128(state.as_ptr() as *const __m128i);
618            let mut s1 = _mm_loadu_si128(state.as_ptr().add(4) as *const __m128i);
619            tmp = _mm_shuffle_epi32(tmp, 0xb1); // CDAB
620            s1 = _mm_shuffle_epi32(s1, 0x1b); // EFGH
621            let mut s0 = _mm_alignr_epi8(tmp, s1, 8); // ABEF
622            s1 = _mm_blend_epi16(s1, tmp, 0xf0); // CDGH
623            for block in blocks.chunks_exact(64) {
624                let (abef, cdgh) = (s0, s1);
625                let mut w = [_mm_setzero_si128(); 16];
626                for (j, wj) in w.iter_mut().take(4).enumerate() {
627                    *wj = _mm_shuffle_epi8(
628                        _mm_loadu_si128(block.as_ptr().add(16 * j) as *const __m128i),
629                        swap,
630                    );
631                }
632                for g in 0..12 {
633                    let t = _mm_add_epi32(
634                        _mm_sha256msg1_epu32(w[g], w[g + 1]),
635                        _mm_alignr_epi8(w[g + 3], w[g + 2], 4),
636                    );
637                    w[g + 4] = _mm_sha256msg2_epu32(t, w[g + 3]);
638                }
639                for (g, wg) in w.iter().enumerate() {
640                    let wk = _mm_add_epi32(
641                        *wg,
642                        _mm_loadu_si128(super::K256.as_ptr().add(4 * g) as *const __m128i),
643                    );
644                    s1 = _mm_sha256rnds2_epu32(s1, s0, wk);
645                    s0 = _mm_sha256rnds2_epu32(s0, s1, _mm_shuffle_epi32(wk, 0x0e));
646                }
647                s0 = _mm_add_epi32(s0, abef);
648                s1 = _mm_add_epi32(s1, cdgh);
649            }
650            tmp = _mm_shuffle_epi32(s0, 0x1b); // FEBA
651            s1 = _mm_shuffle_epi32(s1, 0xb1); // DCHG
652            s0 = _mm_blend_epi16(tmp, s1, 0xf0); // DCBA
653            s1 = _mm_alignr_epi8(s1, tmp, 8); // HGFE
654            _mm_storeu_si128(state.as_mut_ptr() as *mut __m128i, s0);
655            _mm_storeu_si128(state.as_mut_ptr().add(4) as *mut __m128i, s1);
656        }
657    }
658
659    // ChaCha20 quarter round on lane-sliced state (one block per lane).
660    macro_rules! qr4 {
661        ($x:ident, $a:expr, $b:expr, $c:expr, $d:expr, $add:ident, $xor:ident, $r16:ident, $r8:ident, $rot:ident) => {{
662            $x[$a] = $add!($x[$a], $x[$b]);
663            $x[$d] = $r16!($xor!($x[$d], $x[$a]));
664            $x[$c] = $add!($x[$c], $x[$d]);
665            $x[$b] = $rot!($xor!($x[$b], $x[$c]), 12);
666            $x[$a] = $add!($x[$a], $x[$b]);
667            $x[$d] = $r8!($xor!($x[$d], $x[$a]));
668            $x[$c] = $add!($x[$c], $x[$d]);
669            $x[$b] = $rot!($xor!($x[$b], $x[$c]), 7);
670        }};
671    }
672
673    macro_rules! rounds {
674        ($x:ident, $add:ident, $xor:ident, $r16:ident, $r8:ident, $rot:ident) => {
675            for _ in 0..10 {
676                qr4!($x, 0, 4, 8, 12, $add, $xor, $r16, $r8, $rot);
677                qr4!($x, 1, 5, 9, 13, $add, $xor, $r16, $r8, $rot);
678                qr4!($x, 2, 6, 10, 14, $add, $xor, $r16, $r8, $rot);
679                qr4!($x, 3, 7, 11, 15, $add, $xor, $r16, $r8, $rot);
680                qr4!($x, 0, 5, 10, 15, $add, $xor, $r16, $r8, $rot);
681                qr4!($x, 1, 6, 11, 12, $add, $xor, $r16, $r8, $rot);
682                qr4!($x, 2, 7, 8, 13, $add, $xor, $r16, $r8, $rot);
683                qr4!($x, 3, 4, 9, 14, $add, $xor, $r16, $r8, $rot);
684            }
685        };
686    }
687
688    macro_rules! add128 {
689        ($a:expr, $b:expr) => {
690            _mm_add_epi32($a, $b)
691        };
692    }
693    macro_rules! xor128 {
694        ($a:expr, $b:expr) => {
695            _mm_xor_si128($a, $b)
696        };
697    }
698    macro_rules! rot128 {
699        ($v:expr, $n:expr) => {{
700            let v = $v;
701            _mm_or_si128(_mm_slli_epi32(v, $n), _mm_srli_epi32(v, 32 - $n))
702        }};
703    }
704    macro_rules! r16_128 {
705        ($v:expr) => {
706            _mm_shuffle_epi8(
707                $v,
708                _mm_set_epi8(13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2),
709            )
710        };
711    }
712    macro_rules! r8_128 {
713        ($v:expr) => {
714            _mm_shuffle_epi8(
715                $v,
716                _mm_set_epi8(14, 13, 12, 15, 10, 9, 8, 11, 6, 5, 4, 7, 2, 1, 0, 3),
717            )
718        };
719    }
720    macro_rules! add256 {
721        ($a:expr, $b:expr) => {
722            _mm256_add_epi32($a, $b)
723        };
724    }
725    macro_rules! xor256 {
726        ($a:expr, $b:expr) => {
727            _mm256_xor_si256($a, $b)
728        };
729    }
730    macro_rules! rot256 {
731        ($v:expr, $n:expr) => {{
732            let v = $v;
733            _mm256_or_si256(_mm256_slli_epi32(v, $n), _mm256_srli_epi32(v, 32 - $n))
734        }};
735    }
736    macro_rules! r16_256 {
737        ($v:expr) => {
738            _mm256_shuffle_epi8(
739                $v,
740                _mm256_set_epi8(
741                    13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2, 13, 12, 15, 14, 9, 8, 11,
742                    10, 5, 4, 7, 6, 1, 0, 3, 2,
743                ),
744            )
745        };
746    }
747    macro_rules! r8_256 {
748        ($v:expr) => {
749            _mm256_shuffle_epi8(
750                $v,
751                _mm256_set_epi8(
752                    14, 13, 12, 15, 10, 9, 8, 11, 6, 5, 4, 7, 2, 1, 0, 3, 14, 13, 12, 15, 10, 9, 8,
753                    11, 6, 5, 4, 7, 2, 1, 0, 3,
754                ),
755            )
756        };
757    }
758
759    /// Four ChaCha20 blocks per step.
760    ///
761    /// # Safety
762    /// The CPU must have SSSE3; `data.len()` is a multiple of 256.
763    #[target_feature(enable = "ssse3,sse2")]
764    pub(super) unsafe fn chacha4(st: &mut [u32; 16], data: &mut [u8]) {
765        // SAFETY: loads and stores touch 16 bytes at offsets below `data.len()` (a multiple of
766        // 256); the state array has 16 words.
767        unsafe {
768            let p = data.as_mut_ptr();
769            for chunk in 0..data.len() / 256 {
770                let mut x = [_mm_setzero_si128(); 16];
771                for (i, v) in x.iter_mut().enumerate() {
772                    *v = _mm_set1_epi32(st[i] as i32);
773                }
774                x[12] = _mm_add_epi32(x[12], _mm_set_epi32(3, 2, 1, 0));
775                let init = x;
776                rounds!(x, add128, xor128, r16_128, r8_128, rot128);
777                for (v, i) in x.iter_mut().zip(init.iter()) {
778                    *v = _mm_add_epi32(*v, *i);
779                }
780                // transpose each group of four words to block order and XOR into the data
781                for g in 0..4 {
782                    let (a, b, c, d) = (x[4 * g], x[4 * g + 1], x[4 * g + 2], x[4 * g + 3]);
783                    let t0 = _mm_unpacklo_epi32(a, b);
784                    let t1 = _mm_unpackhi_epi32(a, b);
785                    let t2 = _mm_unpacklo_epi32(c, d);
786                    let t3 = _mm_unpackhi_epi32(c, d);
787                    let o = [
788                        _mm_unpacklo_epi64(t0, t2),
789                        _mm_unpackhi_epi64(t0, t2),
790                        _mm_unpacklo_epi64(t1, t3),
791                        _mm_unpackhi_epi64(t1, t3),
792                    ];
793                    for (blk, ov) in o.iter().enumerate() {
794                        let q = p.add(chunk * 256 + blk * 64 + g * 16) as *mut __m128i;
795                        _mm_storeu_si128(q, _mm_xor_si128(_mm_loadu_si128(q), *ov));
796                    }
797                }
798                st[12] = st[12].wrapping_add(4);
799            }
800        }
801    }
802
803    /// Eight ChaCha20 blocks per step.
804    ///
805    /// # Safety
806    /// The CPU must have AVX2; `data.len()` is a multiple of 512.
807    #[target_feature(enable = "avx2")]
808    pub(super) unsafe fn chacha8(st: &mut [u32; 16], data: &mut [u8]) {
809        // SAFETY: loads and stores touch 32 bytes at offsets below `data.len()` (a multiple of
810        // 512); the state array has 16 words.
811        unsafe {
812            let p = data.as_mut_ptr();
813            for chunk in 0..data.len() / 512 {
814                let mut x = [_mm256_setzero_si256(); 16];
815                for (i, v) in x.iter_mut().enumerate() {
816                    *v = _mm256_set1_epi32(st[i] as i32);
817                }
818                x[12] = _mm256_add_epi32(x[12], _mm256_set_epi32(7, 6, 5, 4, 3, 2, 1, 0));
819                let init = x;
820                rounds!(x, add256, xor256, r16_256, r8_256, rot256);
821                for (v, i) in x.iter_mut().zip(init.iter()) {
822                    *v = _mm256_add_epi32(*v, *i);
823                }
824                // 8x8 transposes: words 0-7 and 8-15 of each block
825                for half in 0..2 {
826                    let a = &x[8 * half..8 * half + 8];
827                    let t: [__m256i; 8] = [
828                        _mm256_unpacklo_epi32(a[0], a[1]),
829                        _mm256_unpackhi_epi32(a[0], a[1]),
830                        _mm256_unpacklo_epi32(a[2], a[3]),
831                        _mm256_unpackhi_epi32(a[2], a[3]),
832                        _mm256_unpacklo_epi32(a[4], a[5]),
833                        _mm256_unpackhi_epi32(a[4], a[5]),
834                        _mm256_unpacklo_epi32(a[6], a[7]),
835                        _mm256_unpackhi_epi32(a[6], a[7]),
836                    ];
837                    let u: [__m256i; 8] = [
838                        _mm256_unpacklo_epi64(t[0], t[2]),
839                        _mm256_unpackhi_epi64(t[0], t[2]),
840                        _mm256_unpacklo_epi64(t[1], t[3]),
841                        _mm256_unpackhi_epi64(t[1], t[3]),
842                        _mm256_unpacklo_epi64(t[4], t[6]),
843                        _mm256_unpackhi_epi64(t[4], t[6]),
844                        _mm256_unpacklo_epi64(t[5], t[7]),
845                        _mm256_unpackhi_epi64(t[5], t[7]),
846                    ];
847                    for k in 0..4 {
848                        let lo = _mm256_permute2x128_si256(u[k], u[k + 4], 0x20); // block k
849                        let hi = _mm256_permute2x128_si256(u[k], u[k + 4], 0x31); // block k + 4
850                        let q0 = p.add(chunk * 512 + k * 64 + half * 32) as *mut __m256i;
851                        let q1 = p.add(chunk * 512 + (k + 4) * 64 + half * 32) as *mut __m256i;
852                        _mm256_storeu_si256(q0, _mm256_xor_si256(_mm256_loadu_si256(q0), lo));
853                        _mm256_storeu_si256(q1, _mm256_xor_si256(_mm256_loadu_si256(q1), hi));
854                    }
855                }
856                st[12] = st[12].wrapping_add(8);
857            }
858        }
859    }
860}
861
862#[cfg(target_arch = "aarch64")]
863mod arm {
864    use core::arch::aarch64::*;
865
866    // Same representation as on x86-64 (see there), with the 128-bit integer held as two u64
867    // halves (`hi`, `lo`) in general registers and PMULL for the 64x64 products.
868
869    #[inline(always)]
870    fn rev_load(b: &[u8; 16]) -> (u64, u64) {
871        let v = u128::from_be_bytes(*b);
872        ((v >> 64) as u64, v as u64)
873    }
874
875    #[inline(always)]
876    fn rev_store(hi: u64, lo: u64) -> [u8; 16] {
877        (((hi as u128) << 64) | lo as u128).to_be_bytes()
878    }
879
880    // Reduces the 256-bit product v3:v2:v1:v0 (already shifted left by one bit).
881    #[inline(always)]
882    fn reduce(v0: u64, mut v1: u64, mut v2: u64, mut v3: u64) -> (u64, u64) {
883        v2 ^= v0 ^ (v0 >> 1) ^ (v0 >> 2) ^ (v0 >> 7);
884        v1 ^= (v0 << 63) ^ (v0 << 62) ^ (v0 << 57);
885        v3 ^= v1 ^ (v1 >> 1) ^ (v1 >> 2) ^ (v1 >> 7);
886        v2 ^= (v1 << 63) ^ (v1 << 62) ^ (v1 << 57);
887        (v3, v2)
888    }
889
890    // (lo, mid, hi) accumulators of 128-bit partial products -> reduced (hi, lo) halves.
891    #[inline(always)]
892    fn finish(lo: u128, mid: u128, hi: u128) -> (u64, u64) {
893        let mid = mid ^ lo ^ hi;
894        let lo = lo ^ (mid << 64);
895        let hi = hi ^ (mid >> 64);
896        let hi = (hi << 1) | (lo >> 127);
897        let lo = lo << 1;
898        reduce(lo as u64, (lo >> 64) as u64, hi as u64, (hi >> 64) as u64)
899    }
900
901    macro_rules! pmull_acc {
902        ($lo:ident, $mid:ident, $hi:ident, $a:expr, $b:expr) => {{
903            let (ah, al): (u64, u64) = $a;
904            let (bh, bl): (u64, u64) = $b;
905            $lo ^= vmull_p64(al, bl);
906            $hi ^= vmull_p64(ah, bh);
907            $mid ^= vmull_p64(al ^ ah, bl ^ bh);
908        }};
909    }
910
911    /// # Safety
912    /// The CPU must have the ARMv8 AES instructions.
913    #[target_feature(enable = "neon,aes")]
914    pub(super) unsafe fn aes_block(rk: &[[u8; 16]; 15], nr: usize, block: &mut [u8; 16]) {
915        // SAFETY: 16-byte loads and stores of 16-byte arrays; `nr` <= 14.
916        unsafe {
917            let mut x = vld1q_u8(block.as_ptr());
918            for k in &rk[..nr - 1] {
919                x = vaesmcq_u8(vaeseq_u8(x, vld1q_u8(k.as_ptr())));
920            }
921            x = vaeseq_u8(x, vld1q_u8(rk[nr - 1].as_ptr()));
922            x = veorq_u8(x, vld1q_u8(rk[nr].as_ptr()));
923            vst1q_u8(block.as_mut_ptr(), x);
924        }
925    }
926
927    /// # Safety
928    /// The CPU must have PMULL.
929    #[target_feature(enable = "neon,aes")]
930    #[allow(unused_unsafe)] // `vmull_p64` became safe to call here in Rust 1.87
931    pub(super) unsafe fn ghash_init(h: &[u8; 16], out: &mut [[u8; 16]; 8]) {
932        // SAFETY: `vmull_p64` only needs the PMULL feature, which the caller checked.
933        unsafe {
934            let h1 = rev_load(h);
935            let mut p = h1;
936            out[0] = rev_store(p.0, p.1);
937            for o in out.iter_mut().skip(1) {
938                let (mut lo, mut mid, mut hi) = (0u128, 0u128, 0u128);
939                pmull_acc!(lo, mid, hi, p, h1);
940                p = finish(lo, mid, hi);
941                *o = rev_store(p.0, p.1);
942            }
943        }
944    }
945
946    /// # Safety
947    /// The CPU must have PMULL.
948    #[target_feature(enable = "neon,aes")]
949    #[allow(unused_unsafe)] // `vmull_p64` became safe to call here in Rust 1.87
950    pub(super) unsafe fn ghash_update(hp: &[[u8; 16]; 8], y: &mut [u8; 16], data: &[u8]) {
951        // SAFETY: `vmull_p64` only needs the PMULL feature, which the caller checked; all
952        // memory access is through safe slices.
953        unsafe {
954            let h: [(u64, u64); 8] = core::array::from_fn(|k| rev_load(&hp[k]));
955            let mut acc = rev_load(y);
956            let mut chunks = data.chunks_exact(128);
957            for c in &mut chunks {
958                let (mut lo, mut mid, mut hi) = (0u128, 0u128, 0u128);
959                for j in 0..8 {
960                    let mut x = rev_load(c[16 * j..16 * j + 16].try_into().unwrap());
961                    if j == 0 {
962                        x = (x.0 ^ acc.0, x.1 ^ acc.1);
963                    }
964                    pmull_acc!(lo, mid, hi, x, h[7 - j]);
965                }
966                acc = finish(lo, mid, hi);
967            }
968            for c in chunks.remainder().chunks(16) {
969                let mut t = [0u8; 16];
970                t[..c.len()].copy_from_slice(c);
971                let x = rev_load(&t);
972                let (mut lo, mut mid, mut hi) = (0u128, 0u128, 0u128);
973                pmull_acc!(lo, mid, hi, (x.0 ^ acc.0, x.1 ^ acc.1), h[0]);
974                acc = finish(lo, mid, hi);
975            }
976            *y = rev_store(acc.0, acc.1);
977        }
978    }
979
980    /// # Safety
981    /// The CPU must have the ARMv8 AES instructions and PMULL; `nr` is 10 or 14.
982    #[target_feature(enable = "neon,aes")]
983    pub(super) unsafe fn gcm(
984        rkb: &[[u8; 16]; 15],
985        nr: usize,
986        hp: &[[u8; 16]; 8],
987        ctr: &mut [u8; 16],
988        y: &mut [u8; 16],
989        data: &mut [u8],
990        encrypt: bool,
991    ) {
992        // SAFETY: NEON loads and stores read and write 16-byte local arrays; `ghash_update`
993        // needs PMULL, which the caller checked.
994        unsafe {
995            let rk: [uint8x16_t; 15] = core::array::from_fn(|k| vld1q_u8(rkb[k].as_ptr()));
996            let mut counter = u32::from_be_bytes([ctr[12], ctr[13], ctr[14], ctr[15]]);
997            let mut blk = *ctr;
998            let n = data.len();
999            let mut i = 0;
1000            while i < n {
1001                let take = (n - i).min(64);
1002                // Up to four blocks of keystream at a time.
1003                let mut ks = [0u8; 64];
1004                let nb = take.div_ceil(16);
1005                let mut b = [vdupq_n_u8(0); 4];
1006                for v in b.iter_mut().take(nb) {
1007                    blk[12..].copy_from_slice(&counter.to_be_bytes());
1008                    counter = counter.wrapping_add(1);
1009                    *v = vld1q_u8(blk.as_ptr());
1010                }
1011                for k in &rk[..nr - 1] {
1012                    for v in b.iter_mut().take(nb) {
1013                        *v = vaesmcq_u8(vaeseq_u8(*v, *k));
1014                    }
1015                }
1016                for (j, v) in b.iter().take(nb).enumerate() {
1017                    let o = veorq_u8(vaeseq_u8(*v, rk[nr - 1]), rk[nr]);
1018                    vst1q_u8(ks[16 * j..].as_mut_ptr(), o);
1019                }
1020                let chunk = &mut data[i..i + take];
1021                if !encrypt {
1022                    ghash_update(hp, y, chunk);
1023                }
1024                for (d, k) in chunk.iter_mut().zip(ks.iter()) {
1025                    *d ^= k;
1026                }
1027                if encrypt {
1028                    ghash_update(hp, y, chunk);
1029                }
1030                i += take;
1031            }
1032            ctr[12..].copy_from_slice(&counter.to_be_bytes());
1033        }
1034    }
1035
1036    /// # Safety
1037    /// The CPU must have the ARMv8 SHA-2 instructions; `blocks.len()` is a multiple of 64.
1038    #[target_feature(enable = "neon,sha2")]
1039    pub(super) unsafe fn sha256(state: &mut [u32; 8], blocks: &[u8]) {
1040        // SAFETY: loads and stores of the 8-word state and of 16-byte pieces of whole 64-byte
1041        // blocks; `K256` has 64 entries.
1042        unsafe {
1043            let mut s0 = vld1q_u32(state.as_ptr());
1044            let mut s1 = vld1q_u32(state.as_ptr().add(4));
1045            for block in blocks.chunks_exact(64) {
1046                let (abcd, efgh) = (s0, s1);
1047                let mut w = [vdupq_n_u32(0); 16];
1048                for (j, wj) in w.iter_mut().take(4).enumerate() {
1049                    *wj = vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(block.as_ptr().add(16 * j))));
1050                }
1051                for g in 0..12 {
1052                    w[g + 4] = vsha256su1q_u32(vsha256su0q_u32(w[g], w[g + 1]), w[g + 2], w[g + 3]);
1053                }
1054                for (g, wg) in w.iter().enumerate() {
1055                    let wk = vaddq_u32(*wg, vld1q_u32(super::K256.as_ptr().add(4 * g)));
1056                    let t = s0;
1057                    s0 = vsha256hq_u32(s0, s1, wk);
1058                    s1 = vsha256h2q_u32(s1, t, wk);
1059                }
1060                s0 = vaddq_u32(s0, abcd);
1061                s1 = vaddq_u32(s1, efgh);
1062            }
1063            vst1q_u32(state.as_mut_ptr(), s0);
1064            vst1q_u32(state.as_mut_ptr().add(4), s1);
1065        }
1066    }
1067}
1068
1069/// Fills `buf` from the operating system's cryptographically secure random source. Returns
1070/// false if the source failed (callers must treat that as fatal, never fall back to weaker
1071/// randomness).
1072pub fn os_random(buf: &mut [u8]) -> bool {
1073    os_rand::fill(buf)
1074}
1075
1076#[cfg(any(target_os = "linux", target_os = "android"))]
1077mod os_rand {
1078    extern "C" {
1079        fn getrandom(buf: *mut u8, buflen: usize, flags: u32) -> isize;
1080    }
1081
1082    pub(super) fn fill(buf: &mut [u8]) -> bool {
1083        let mut done = 0;
1084        while done < buf.len() {
1085            let rest = &mut buf[done..];
1086            // SAFETY: `rest` is a valid, writable region of `rest.len()` bytes; getrandom writes
1087            // at most that many and returns how many it wrote, or -1.
1088            let n = unsafe { getrandom(rest.as_mut_ptr(), rest.len(), 0) };
1089            if n < 0 {
1090                if std::io::Error::last_os_error().kind() == std::io::ErrorKind::Interrupted {
1091                    continue;
1092                }
1093                return false;
1094            }
1095            done += n as usize;
1096        }
1097        true
1098    }
1099}
1100
1101#[cfg(any(
1102    target_os = "macos",
1103    target_os = "ios",
1104    target_os = "freebsd",
1105    target_os = "openbsd",
1106    target_os = "netbsd"
1107))]
1108mod os_rand {
1109    extern "C" {
1110        fn getentropy(buf: *mut u8, buflen: usize) -> i32;
1111    }
1112
1113    pub(super) fn fill(buf: &mut [u8]) -> bool {
1114        for chunk in buf.chunks_mut(256) {
1115            // SAFETY: `chunk` is valid and writable for its length, which is at most 256, the
1116            // limit of getentropy.
1117            if unsafe { getentropy(chunk.as_mut_ptr(), chunk.len()) } != 0 {
1118                return false;
1119            }
1120        }
1121        true
1122    }
1123}
1124
1125#[cfg(windows)]
1126mod os_rand {
1127    #[link(name = "bcrypt")]
1128    extern "system" {
1129        fn BCryptGenRandom(
1130            algorithm: *mut core::ffi::c_void,
1131            buffer: *mut u8,
1132            len: u32,
1133            flags: u32,
1134        ) -> i32;
1135    }
1136
1137    const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 2;
1138
1139    pub(super) fn fill(buf: &mut [u8]) -> bool {
1140        for chunk in buf.chunks_mut(1 << 30) {
1141            // SAFETY: a null algorithm handle with BCRYPT_USE_SYSTEM_PREFERRED_RNG is the
1142            // documented way to use the system RNG; `chunk` is valid and writable for its length,
1143            // which fits in u32.
1144            let status = unsafe {
1145                BCryptGenRandom(
1146                    std::ptr::null_mut(),
1147                    chunk.as_mut_ptr(),
1148                    chunk.len() as u32,
1149                    BCRYPT_USE_SYSTEM_PREFERRED_RNG,
1150                )
1151            };
1152            if status < 0 {
1153                return false;
1154            }
1155        }
1156        true
1157    }
1158}
1159
1160#[cfg(not(any(
1161    target_os = "linux",
1162    target_os = "android",
1163    target_os = "macos",
1164    target_os = "ios",
1165    target_os = "freebsd",
1166    target_os = "openbsd",
1167    target_os = "netbsd",
1168    windows
1169)))]
1170mod os_rand {
1171    pub(super) fn fill(buf: &mut [u8]) -> bool {
1172        use std::io::Read;
1173        match std::fs::File::open("/dev/urandom") {
1174            Ok(mut f) => f.read_exact(buf).is_ok(),
1175            Err(_) => false,
1176        }
1177    }
1178}
1179
1180#[cfg(test)]
1181mod tests {
1182    use super::*;
1183
1184    #[test]
1185    fn os_random_fills() {
1186        let mut a = [0u8; 64];
1187        let mut b = [0u8; 64];
1188        assert!(os_random(&mut a));
1189        assert!(os_random(&mut b));
1190        assert_ne!(a, b);
1191        let mut empty = [0u8; 0];
1192        assert!(os_random(&mut empty));
1193    }
1194
1195    #[test]
1196    fn sha256_hardware_matches_known_digest() {
1197        // "abc", padded: the FIPS 180-4 example.
1198        let mut block = [0u8; 64];
1199        block[..3].copy_from_slice(b"abc");
1200        block[3] = 0x80;
1201        block[63] = 24;
1202        let mut st = [
1203            0x6a09e667u32,
1204            0xbb67ae85,
1205            0x3c6ef372,
1206            0xa54ff53a,
1207            0x510e527f,
1208            0x9b05688c,
1209            0x1f83d9ab,
1210            0x5be0cd19,
1211        ];
1212        if sha256_blocks(&mut st, &block) {
1213            assert_eq!(
1214                st,
1215                [
1216                    0xba7816bf, 0x8f01cfea, 0x414140de, 0x5dae2223, 0xb00361a3, 0x96177a9c,
1217                    0xb410ff61, 0xf20015ad
1218                ]
1219            );
1220        }
1221        assert!(!sha256_blocks(&mut st, &block[..63]));
1222    }
1223
1224    #[test]
1225    fn aes_constructor_checks_round_count() {
1226        // The AES and GHASH vectors live in simoom-tls, which owns the key schedule.
1227        assert!(AesHw::new(&[[0u8; 16]; 12]).is_none());
1228        if has_aes() {
1229            assert!(AesHw::new(&[[0u8; 16]; 11]).is_some());
1230        }
1231    }
1232}
1233
1234// ---- Kernels for simoom-tls's public-key arithmetic (added after the code above, kept
1235// self-contained: own feature detection, honours `disable_hardware`):
1236// - `opaque`: an optimization barrier for constant-time masks;
1237// - P-256 field and group-order Montgomery multiplication, X25519 field arithmetic and a
1238//   big-integer Montgomery multiplication (RSA) with MULX/ADCX/ADOX (BMI2 and ADX);
1239// - P-256 and X25519 additions and subtractions (ADC/SBB/CMOV, every x86-64 CPU);
1240// - Poly1305 with AVX2 (at the end of the file).
1241
1242/// Returns `x` unchanged, hidden from the optimizer, so a mask derived from secret bits cannot
1243/// be turned back into a branch (LLVM may otherwise rewrite masked selection as a conditional
1244/// jump). Unlike `core::hint::black_box` the value stays in a register.
1245#[inline(always)]
1246pub fn opaque(x: u64) -> u64 {
1247    #[cfg(any(
1248        target_arch = "x86_64",
1249        target_arch = "aarch64",
1250        target_arch = "riscv64"
1251    ))]
1252    {
1253        let mut x = x;
1254        // SAFETY: an empty asm block that only claims to read and write one register.
1255        unsafe {
1256            core::arch::asm!("/* {0} */", inout(reg) x, options(pure, nomem, nostack, preserves_flags))
1257        };
1258        x
1259    }
1260    #[cfg(not(any(
1261        target_arch = "x86_64",
1262        target_arch = "aarch64",
1263        target_arch = "riscv64"
1264    )))]
1265    {
1266        core::hint::black_box(x)
1267    }
1268}
1269
1270/// 0 = not yet detected, 1 = absent, 2 = present.
1271static MULX: AtomicU8 = AtomicU8::new(0);
1272
1273/// True if the CPU has MULX (BMI2) and ADCX/ADOX (ADX), for the Montgomery kernels below.
1274#[inline]
1275pub fn has_mulx() -> bool {
1276    let mut v = MULX.load(Ordering::Relaxed);
1277    if v == 0 {
1278        #[cfg(target_arch = "x86_64")]
1279        let yes = crate::cpu::x86_features() & (crate::cpu::x86::BMI2 | crate::cpu::x86::ADX) == crate::cpu::x86::BMI2 | crate::cpu::x86::ADX;
1280        #[cfg(not(target_arch = "x86_64"))]
1281        let yes = false;
1282        v = if yes { 2 } else { 1 };
1283        MULX.store(v, Ordering::Relaxed);
1284    }
1285    v == 2 && DISABLED.load(Ordering::Relaxed) == 0
1286}
1287
1288/// P-256 field multiplication in Montgomery form (R = 2^256): a * b * R^-1 mod p for a and b
1289/// below p = 2^256 - 2^224 + 2^192 + 2^96 - 1, in constant time; `None` without MULX/ADX. The
1290/// product uses the two ADX carry chains; the reduction uses the shape of p (the multiplier of
1291/// each step is the low limb itself, and q * p is shifts and subtractions, no multiplication).
1292#[inline]
1293pub fn p256_mul_mont(a: &[u64; 4], b: &[u64; 4]) -> Option<[u64; 4]> {
1294    if !has_mulx() {
1295        return None;
1296    }
1297    #[cfg(target_arch = "x86_64")]
1298    {
1299        let (r0, r1, r2, r3): (u64, u64, u64, u64);
1300        // SAFETY: `has_mulx()` was true (MULX, ADCX, ADOX). The block reads 4 limbs at `a` and
1301        // `b`, writes no memory, and declares every register it changes.
1302        unsafe {
1303            core::arch::asm!(
1304                "mov rdx, qword ptr [rdi]",
1305                "mulx r9, r8, qword ptr [rsi]",
1306                "mulx r10, rax, qword ptr [rsi + 8]",
1307                "add r9, rax",
1308                "mulx r11, rax, qword ptr [rsi + 16]",
1309                "adc r10, rax",
1310                "mulx r12, rax, qword ptr [rsi + 24]",
1311                "adc r11, rax",
1312                "adc r12, 0",
1313                "mov rdx, qword ptr [rdi + 8]",
1314                "xor eax, eax",
1315                "mov r13, 0",
1316                "mulx rcx, rax, qword ptr [rsi + 0]",
1317                "adox r9, rax",
1318                "adcx r10, rcx",
1319                "mulx rcx, rax, qword ptr [rsi + 8]",
1320                "adox r10, rax",
1321                "adcx r11, rcx",
1322                "mulx rcx, rax, qword ptr [rsi + 16]",
1323                "adox r11, rax",
1324                "adcx r12, rcx",
1325                "mulx rcx, rax, qword ptr [rsi + 24]",
1326                "adox r12, rax",
1327                "adcx r13, rcx",
1328                "mov eax, 0",
1329                "adox r13, rax",
1330                "mov rdx, qword ptr [rdi + 16]",
1331                "xor eax, eax",
1332                "mov r14, 0",
1333                "mulx rcx, rax, qword ptr [rsi + 0]",
1334                "adox r10, rax",
1335                "adcx r11, rcx",
1336                "mulx rcx, rax, qword ptr [rsi + 8]",
1337                "adox r11, rax",
1338                "adcx r12, rcx",
1339                "mulx rcx, rax, qword ptr [rsi + 16]",
1340                "adox r12, rax",
1341                "adcx r13, rcx",
1342                "mulx rcx, rax, qword ptr [rsi + 24]",
1343                "adox r13, rax",
1344                "adcx r14, rcx",
1345                "mov eax, 0",
1346                "adox r14, rax",
1347                "mov rdx, qword ptr [rdi + 24]",
1348                "xor eax, eax",
1349                "mov r15, 0",
1350                "mulx rcx, rax, qword ptr [rsi + 0]",
1351                "adox r11, rax",
1352                "adcx r12, rcx",
1353                "mulx rcx, rax, qword ptr [rsi + 8]",
1354                "adox r12, rax",
1355                "adcx r13, rcx",
1356                "mulx rcx, rax, qword ptr [rsi + 16]",
1357                "adox r13, rax",
1358                "adcx r14, rcx",
1359                "mulx rcx, rax, qword ptr [rsi + 24]",
1360                "adox r14, rax",
1361                "adcx r15, rcx",
1362                "mov eax, 0",
1363                "adox r15, rax",
1364                "xor edi, edi",
1365                "mov rax, r8",
1366                "shl rax, 32",
1367                "mov rcx, r8",
1368                "shr rcx, 32",
1369                "mov rsi, r8",
1370                "sub rsi, rax",
1371                "mov rdx, r8",
1372                "sbb rdx, rcx",
1373                "add r9, rax",
1374                "adc r10, rcx",
1375                "adc r11, rsi",
1376                "adc r12, rdx",
1377                "adc r13, 0",
1378                "adc r14, 0",
1379                "adc r15, 0",
1380                "adc rdi, 0",
1381                "mov rax, r9",
1382                "shl rax, 32",
1383                "mov rcx, r9",
1384                "shr rcx, 32",
1385                "mov rsi, r9",
1386                "sub rsi, rax",
1387                "mov rdx, r9",
1388                "sbb rdx, rcx",
1389                "add r10, rax",
1390                "adc r11, rcx",
1391                "adc r12, rsi",
1392                "adc r13, rdx",
1393                "adc r14, 0",
1394                "adc r15, 0",
1395                "adc rdi, 0",
1396                "mov rax, r10",
1397                "shl rax, 32",
1398                "mov rcx, r10",
1399                "shr rcx, 32",
1400                "mov rsi, r10",
1401                "sub rsi, rax",
1402                "mov rdx, r10",
1403                "sbb rdx, rcx",
1404                "add r11, rax",
1405                "adc r12, rcx",
1406                "adc r13, rsi",
1407                "adc r14, rdx",
1408                "adc r15, 0",
1409                "adc rdi, 0",
1410                "mov rax, r11",
1411                "shl rax, 32",
1412                "mov rcx, r11",
1413                "shr rcx, 32",
1414                "mov rsi, r11",
1415                "sub rsi, rax",
1416                "mov rdx, r11",
1417                "sbb rdx, rcx",
1418                "add r12, rax",
1419                "adc r13, rcx",
1420                "adc r14, rsi",
1421                "adc r15, rdx",
1422                "adc rdi, 0",
1423                "mov rax, r12",
1424                "sub rax, -1",
1425                "mov rdx, r13",
1426                "mov ecx, 0xffffffff",
1427                "sbb rdx, rcx",
1428                "mov rcx, r14",
1429                "sbb rcx, 0",
1430                "mov rsi, r15",
1431                "movabs r8, 0xffffffff00000001",
1432                "sbb rsi, r8",
1433                "sbb rdi, 0",
1434                "cmovc rax, r12",
1435                "cmovc rdx, r13",
1436                "cmovc rcx, r14",
1437                "cmovc rsi, r15",
1438                inout("rsi") a.as_ptr() => r3,
1439                inout("rdi") b.as_ptr() => _,
1440                out("rax") r0,
1441                out("rdx") r1,
1442                out("rcx") r2,
1443                out("r8") _,
1444                out("r9") _,
1445                out("r10") _,
1446                out("r11") _,
1447                out("r12") _,
1448                out("r13") _,
1449                out("r14") _,
1450                out("r15") _,
1451                options(pure, readonly, nostack),
1452            );
1453        }
1454        Some([r0, r1, r2, r3])
1455    }
1456    #[cfg(not(target_arch = "x86_64"))]
1457    {
1458        let _ = (a, b);
1459        None
1460    }
1461}
1462
1463/// P-256 field squaring in Montgomery form: a * a * R^-1 mod p for a below p, in constant
1464/// time; `None` without MULX/ADX. Six cross products doubled plus four squares.
1465#[inline]
1466pub fn p256_sqr_mont(a: &[u64; 4]) -> Option<[u64; 4]> {
1467    if !has_mulx() {
1468        return None;
1469    }
1470    #[cfg(target_arch = "x86_64")]
1471    {
1472        let (r0, r1, r2, r3): (u64, u64, u64, u64);
1473        // SAFETY: `has_mulx()` was true (MULX, ADCX, ADOX). The block reads 4 limbs at `a`,
1474        // writes no memory, and declares every register it changes.
1475        unsafe {
1476            core::arch::asm!(
1477                "mov rdx, qword ptr [rsi]",
1478                "mulx r10, r9, qword ptr [rsi + 8]",
1479                "mulx r11, rax, qword ptr [rsi + 16]",
1480                "add r10, rax",
1481                "mulx r12, rax, qword ptr [rsi + 24]",
1482                "adc r11, rax",
1483                "adc r12, 0",
1484                "mov rdx, qword ptr [rsi + 8]",
1485                "mulx rcx, rax, qword ptr [rsi + 16]",
1486                "mulx r13, rdi, qword ptr [rsi + 24]",
1487                "add r11, rax",
1488                "adc r12, rcx",
1489                "adc r13, 0",
1490                "add r12, rdi",
1491                "adc r13, 0",
1492                "mov rdx, qword ptr [rsi + 16]",
1493                "mulx r14, rax, qword ptr [rsi + 24]",
1494                "add r13, rax",
1495                "adc r14, 0",
1496                "mov r15, 0",
1497                "add r9, r9",
1498                "adc r10, r10",
1499                "adc r11, r11",
1500                "adc r12, r12",
1501                "adc r13, r13",
1502                "adc r14, r14",
1503                "adc r15, r15",
1504                "mov rdx, qword ptr [rsi]",
1505                "mulx rcx, r8, rdx",
1506                "add r9, rcx",
1507                "mov rdx, qword ptr [rsi + 8]",
1508                "mulx rcx, rax, rdx",
1509                "adc r10, rax",
1510                "adc r11, rcx",
1511                "mov rdx, qword ptr [rsi + 16]",
1512                "mulx rcx, rax, rdx",
1513                "adc r12, rax",
1514                "adc r13, rcx",
1515                "mov rdx, qword ptr [rsi + 24]",
1516                "mulx rcx, rax, rdx",
1517                "adc r14, rax",
1518                "adc r15, rcx",
1519                "xor edi, edi",
1520                "mov rax, r8",
1521                "shl rax, 32",
1522                "mov rcx, r8",
1523                "shr rcx, 32",
1524                "mov rsi, r8",
1525                "sub rsi, rax",
1526                "mov rdx, r8",
1527                "sbb rdx, rcx",
1528                "add r9, rax",
1529                "adc r10, rcx",
1530                "adc r11, rsi",
1531                "adc r12, rdx",
1532                "adc r13, 0",
1533                "adc r14, 0",
1534                "adc r15, 0",
1535                "adc rdi, 0",
1536                "mov rax, r9",
1537                "shl rax, 32",
1538                "mov rcx, r9",
1539                "shr rcx, 32",
1540                "mov rsi, r9",
1541                "sub rsi, rax",
1542                "mov rdx, r9",
1543                "sbb rdx, rcx",
1544                "add r10, rax",
1545                "adc r11, rcx",
1546                "adc r12, rsi",
1547                "adc r13, rdx",
1548                "adc r14, 0",
1549                "adc r15, 0",
1550                "adc rdi, 0",
1551                "mov rax, r10",
1552                "shl rax, 32",
1553                "mov rcx, r10",
1554                "shr rcx, 32",
1555                "mov rsi, r10",
1556                "sub rsi, rax",
1557                "mov rdx, r10",
1558                "sbb rdx, rcx",
1559                "add r11, rax",
1560                "adc r12, rcx",
1561                "adc r13, rsi",
1562                "adc r14, rdx",
1563                "adc r15, 0",
1564                "adc rdi, 0",
1565                "mov rax, r11",
1566                "shl rax, 32",
1567                "mov rcx, r11",
1568                "shr rcx, 32",
1569                "mov rsi, r11",
1570                "sub rsi, rax",
1571                "mov rdx, r11",
1572                "sbb rdx, rcx",
1573                "add r12, rax",
1574                "adc r13, rcx",
1575                "adc r14, rsi",
1576                "adc r15, rdx",
1577                "adc rdi, 0",
1578                "mov rax, r12",
1579                "sub rax, -1",
1580                "mov rdx, r13",
1581                "mov ecx, 0xffffffff",
1582                "sbb rdx, rcx",
1583                "mov rcx, r14",
1584                "sbb rcx, 0",
1585                "mov rsi, r15",
1586                "movabs r8, 0xffffffff00000001",
1587                "sbb rsi, r8",
1588                "sbb rdi, 0",
1589                "cmovc rax, r12",
1590                "cmovc rdx, r13",
1591                "cmovc rcx, r14",
1592                "cmovc rsi, r15",
1593                inout("rsi") a.as_ptr() => r3,
1594                out("rdi") _,
1595                out("rax") r0,
1596                out("rdx") r1,
1597                out("rcx") r2,
1598                out("r8") _,
1599                out("r9") _,
1600                out("r10") _,
1601                out("r11") _,
1602                out("r12") _,
1603                out("r13") _,
1604                out("r14") _,
1605                out("r15") _,
1606                options(pure, readonly, nostack),
1607            );
1608        }
1609        Some([r0, r1, r2, r3])
1610    }
1611    #[cfg(not(target_arch = "x86_64"))]
1612    {
1613        let _ = a;
1614        None
1615    }
1616}
1617
1618/// Multiplication modulo the P-256 group order n in Montgomery form: a * b * 2^-256 mod n for
1619/// a and b below n, in constant time; `None` without MULX/ADX.
1620#[inline]
1621pub fn p256_order_mul_mont(a: &[u64; 4], b: &[u64; 4]) -> Option<[u64; 4]> {
1622    if !has_mulx() {
1623        return None;
1624    }
1625    #[cfg(target_arch = "x86_64")]
1626    {
1627        let (r0, r1, r2, r3): (u64, u64, u64, u64);
1628        // SAFETY: `has_mulx()` was true (MULX, ADCX, ADOX). The block reads the 4-limb inputs,
1629        // writes no memory, and declares every register it changes.
1630        unsafe {
1631            core::arch::asm!(
1632                "mov rdx, qword ptr [rdi]",
1633                "mulx r9, r8, qword ptr [rsi]",
1634                "mulx r10, rax, qword ptr [rsi + 8]",
1635                "add r9, rax",
1636                "mulx r11, rax, qword ptr [rsi + 16]",
1637                "adc r10, rax",
1638                "mulx r12, rax, qword ptr [rsi + 24]",
1639                "adc r11, rax",
1640                "adc r12, 0",
1641                "mov rdx, qword ptr [rdi + 8]",
1642                "xor eax, eax",
1643                "mov r13, 0",
1644                "mulx rcx, rax, qword ptr [rsi + 0]",
1645                "adox r9, rax",
1646                "adcx r10, rcx",
1647                "mulx rcx, rax, qword ptr [rsi + 8]",
1648                "adox r10, rax",
1649                "adcx r11, rcx",
1650                "mulx rcx, rax, qword ptr [rsi + 16]",
1651                "adox r11, rax",
1652                "adcx r12, rcx",
1653                "mulx rcx, rax, qword ptr [rsi + 24]",
1654                "adox r12, rax",
1655                "adcx r13, rcx",
1656                "mov eax, 0",
1657                "adox r13, rax",
1658                "mov rdx, qword ptr [rdi + 16]",
1659                "xor eax, eax",
1660                "mov r14, 0",
1661                "mulx rcx, rax, qword ptr [rsi + 0]",
1662                "adox r10, rax",
1663                "adcx r11, rcx",
1664                "mulx rcx, rax, qword ptr [rsi + 8]",
1665                "adox r11, rax",
1666                "adcx r12, rcx",
1667                "mulx rcx, rax, qword ptr [rsi + 16]",
1668                "adox r12, rax",
1669                "adcx r13, rcx",
1670                "mulx rcx, rax, qword ptr [rsi + 24]",
1671                "adox r13, rax",
1672                "adcx r14, rcx",
1673                "mov eax, 0",
1674                "adox r14, rax",
1675                "mov rdx, qword ptr [rdi + 24]",
1676                "xor eax, eax",
1677                "mov r15, 0",
1678                "mulx rcx, rax, qword ptr [rsi + 0]",
1679                "adox r11, rax",
1680                "adcx r12, rcx",
1681                "mulx rcx, rax, qword ptr [rsi + 8]",
1682                "adox r12, rax",
1683                "adcx r13, rcx",
1684                "mulx rcx, rax, qword ptr [rsi + 16]",
1685                "adox r13, rax",
1686                "adcx r14, rcx",
1687                "mulx rcx, rax, qword ptr [rsi + 24]",
1688                "adox r14, rax",
1689                "adcx r15, rcx",
1690                "mov eax, 0",
1691                "adox r15, rax",
1692                "xor edi, edi",
1693                "mov rdx, r8",
1694                "movabs rax, 0xccd1c8aaee00bc4f",
1695                "imul rdx, rax",
1696                "xor eax, eax",
1697                "movabs rax, 0xf3b9cac2fc632551",
1698                "mulx rcx, rax, rax",
1699                "adox r8, rax",
1700                "adcx r9, rcx",
1701                "movabs rax, 0xbce6faada7179e84",
1702                "mulx rcx, rax, rax",
1703                "adox r9, rax",
1704                "adcx r10, rcx",
1705                "movabs rax, 0xffffffffffffffff",
1706                "mulx rcx, rax, rax",
1707                "adox r10, rax",
1708                "adcx r11, rcx",
1709                "movabs rax, 0xffffffff00000000",
1710                "mulx rcx, rax, rax",
1711                "adox r11, rax",
1712                "adcx r12, rcx",
1713                "mov eax, 0",
1714                "adox r12, rax",
1715                "adcx r13, rax",
1716                "adox r13, rax",
1717                "adcx r14, rax",
1718                "adox r14, rax",
1719                "adcx r15, rax",
1720                "adox r15, rax",
1721                "adcx rdi, rax",
1722                "adox rdi, rax",
1723                "mov rdx, r9",
1724                "movabs rax, 0xccd1c8aaee00bc4f",
1725                "imul rdx, rax",
1726                "xor eax, eax",
1727                "movabs rax, 0xf3b9cac2fc632551",
1728                "mulx rcx, rax, rax",
1729                "adox r9, rax",
1730                "adcx r10, rcx",
1731                "movabs rax, 0xbce6faada7179e84",
1732                "mulx rcx, rax, rax",
1733                "adox r10, rax",
1734                "adcx r11, rcx",
1735                "movabs rax, 0xffffffffffffffff",
1736                "mulx rcx, rax, rax",
1737                "adox r11, rax",
1738                "adcx r12, rcx",
1739                "movabs rax, 0xffffffff00000000",
1740                "mulx rcx, rax, rax",
1741                "adox r12, rax",
1742                "adcx r13, rcx",
1743                "mov eax, 0",
1744                "adox r13, rax",
1745                "adcx r14, rax",
1746                "adox r14, rax",
1747                "adcx r15, rax",
1748                "adox r15, rax",
1749                "adcx rdi, rax",
1750                "adox rdi, rax",
1751                "mov rdx, r10",
1752                "movabs rax, 0xccd1c8aaee00bc4f",
1753                "imul rdx, rax",
1754                "xor eax, eax",
1755                "movabs rax, 0xf3b9cac2fc632551",
1756                "mulx rcx, rax, rax",
1757                "adox r10, rax",
1758                "adcx r11, rcx",
1759                "movabs rax, 0xbce6faada7179e84",
1760                "mulx rcx, rax, rax",
1761                "adox r11, rax",
1762                "adcx r12, rcx",
1763                "movabs rax, 0xffffffffffffffff",
1764                "mulx rcx, rax, rax",
1765                "adox r12, rax",
1766                "adcx r13, rcx",
1767                "movabs rax, 0xffffffff00000000",
1768                "mulx rcx, rax, rax",
1769                "adox r13, rax",
1770                "adcx r14, rcx",
1771                "mov eax, 0",
1772                "adox r14, rax",
1773                "adcx r15, rax",
1774                "adox r15, rax",
1775                "adcx rdi, rax",
1776                "adox rdi, rax",
1777                "mov rdx, r11",
1778                "movabs rax, 0xccd1c8aaee00bc4f",
1779                "imul rdx, rax",
1780                "xor eax, eax",
1781                "movabs rax, 0xf3b9cac2fc632551",
1782                "mulx rcx, rax, rax",
1783                "adox r11, rax",
1784                "adcx r12, rcx",
1785                "movabs rax, 0xbce6faada7179e84",
1786                "mulx rcx, rax, rax",
1787                "adox r12, rax",
1788                "adcx r13, rcx",
1789                "movabs rax, 0xffffffffffffffff",
1790                "mulx rcx, rax, rax",
1791                "adox r13, rax",
1792                "adcx r14, rcx",
1793                "movabs rax, 0xffffffff00000000",
1794                "mulx rcx, rax, rax",
1795                "adox r14, rax",
1796                "adcx r15, rcx",
1797                "mov eax, 0",
1798                "adox r15, rax",
1799                "adcx rdi, rax",
1800                "adox rdi, rax",
1801                "mov rax, r12",
1802                "movabs r8, 0xf3b9cac2fc632551",
1803                "sub rax, r8",
1804                "mov rdx, r13",
1805                "movabs r9, 0xbce6faada7179e84",
1806                "sbb rdx, r9",
1807                "mov rcx, r14",
1808                "sbb rcx, -1",
1809                "mov rsi, r15",
1810                "movabs r10, 0xffffffff00000000",
1811                "sbb rsi, r10",
1812                "sbb rdi, 0",
1813                "cmovc rax, r12",
1814                "cmovc rdx, r13",
1815                "cmovc rcx, r14",
1816                "cmovc rsi, r15",
1817                inout("rsi") a.as_ptr() => r3,
1818                inout("rdi") b.as_ptr() => _,
1819                out("rax") r0,
1820                out("rdx") r1,
1821                out("rcx") r2,
1822                out("r8") _,
1823                out("r9") _,
1824                out("r10") _,
1825                out("r11") _,
1826                out("r12") _,
1827                out("r13") _,
1828                out("r14") _,
1829                out("r15") _,
1830                options(pure, readonly, nostack),
1831            );
1832        }
1833        Some([r0, r1, r2, r3])
1834    }
1835    #[cfg(not(target_arch = "x86_64"))]
1836    {
1837        let _ = (a, b);
1838        None
1839    }
1840}
1841
1842/// P-256 field addition a + b mod p for a and b below p, in constant time (ADC, SBB and a CMOV
1843/// select; every x86-64 CPU has them). Values stay in registers.
1844#[cfg(target_arch = "x86_64")]
1845#[inline(always)]
1846pub fn p256_add(a: &[u64; 4], b: &[u64; 4]) -> [u64; 4] {
1847    let [mut r0, mut r1, mut r2, mut r3] = *a;
1848    // SAFETY: reads the 4 limbs at `b`, writes no memory; all registers are operands.
1849    unsafe {
1850        core::arch::asm!(
1851            "add {r0}, qword ptr [{b}]",
1852            "adc {r1}, qword ptr [{b} + 8]",
1853            "adc {r2}, qword ptr [{b} + 16]",
1854            "adc {r3}, qword ptr [{b} + 24]",
1855            "sbb {c}, {c}",
1856            "mov {t0}, {r0}",
1857            "mov {t1}, {r1}",
1858            "mov {t2}, {r2}",
1859            "mov {t3}, {r3}",
1860            "sub {t0}, -1",
1861            "sbb {t1}, {p1}",
1862            "sbb {t2}, 0",
1863            "sbb {t3}, {p3}",
1864            "sbb {c}, 0",
1865            "cmovnc {r0}, {t0}",
1866            "cmovnc {r1}, {t1}",
1867            "cmovnc {r2}, {t2}",
1868            "cmovnc {r3}, {t3}",
1869            r0 = inout(reg) r0,
1870            r1 = inout(reg) r1,
1871            r2 = inout(reg) r2,
1872            r3 = inout(reg) r3,
1873            b = in(reg) b.as_ptr(),
1874            p1 = in(reg) 0xffff_ffffu64,
1875            p3 = in(reg) 0xffff_ffff_0000_0001u64,
1876            c = out(reg) _,
1877            t0 = out(reg) _,
1878            t1 = out(reg) _,
1879            t2 = out(reg) _,
1880            t3 = out(reg) _,
1881            options(pure, readonly, nostack),
1882        );
1883    }
1884    [r0, r1, r2, r3]
1885}
1886
1887/// P-256 field subtraction a - b mod p for a and b below p, in constant time (see
1888/// [`p256_add`]): the borrow becomes a mask that selects p to add back.
1889#[cfg(target_arch = "x86_64")]
1890#[inline(always)]
1891pub fn p256_sub(a: &[u64; 4], b: &[u64; 4]) -> [u64; 4] {
1892    let [mut r0, mut r1, mut r2, mut r3] = *a;
1893    // SAFETY: reads the 4 limbs at `b`, writes no memory; all registers are operands.
1894    unsafe {
1895        core::arch::asm!(
1896            "sub {r0}, qword ptr [{b}]",
1897            "sbb {r1}, qword ptr [{b} + 8]",
1898            "sbb {r2}, qword ptr [{b} + 16]",
1899            "sbb {r3}, qword ptr [{b} + 24]",
1900            "sbb {m}, {m}",
1901            "mov {t1}, {m}",
1902            "and {t1}, {p1}",
1903            "mov {t3}, {m}",
1904            "and {t3}, {p3}",
1905            "add {r0}, {m}",
1906            "adc {r1}, {t1}",
1907            "adc {r2}, 0",
1908            "adc {r3}, {t3}",
1909            r0 = inout(reg) r0,
1910            r1 = inout(reg) r1,
1911            r2 = inout(reg) r2,
1912            r3 = inout(reg) r3,
1913            b = in(reg) b.as_ptr(),
1914            p1 = in(reg) 0xffff_ffffu64,
1915            p3 = in(reg) 0xffff_ffff_0000_0001u64,
1916            m = out(reg) _,
1917            t1 = out(reg) _,
1918            t3 = out(reg) _,
1919            options(pure, readonly, nostack),
1920        );
1921    }
1922    [r0, r1, r2, r3]
1923}
1924
1925/// Multiplication modulo p = 2^255 - 19 on 4 x 64-bit limbs, for any inputs below 2^256: the
1926/// result is below 2^256 and congruent to a * b (not fully reduced). Constant time; `None`
1927/// without MULX/ADX. The high half of the product is folded in times 38 (2^256 = 38 mod p).
1928#[inline]
1929pub fn fe25519_mul(a: &[u64; 4], b: &[u64; 4]) -> Option<[u64; 4]> {
1930    if !has_mulx() {
1931        return None;
1932    }
1933    #[cfg(target_arch = "x86_64")]
1934    {
1935        let (r0, r1, r2, r3): (u64, u64, u64, u64);
1936        // SAFETY: `has_mulx()` was true (MULX, ADCX, ADOX). The block reads the 4-limb inputs,
1937        // writes no memory, and declares every register it changes.
1938        unsafe {
1939            core::arch::asm!(
1940                "mov rdx, qword ptr [rdi]",
1941                "mulx r9, r8, qword ptr [rsi]",
1942                "mulx r10, rax, qword ptr [rsi + 8]",
1943                "add r9, rax",
1944                "mulx r11, rax, qword ptr [rsi + 16]",
1945                "adc r10, rax",
1946                "mulx r12, rax, qword ptr [rsi + 24]",
1947                "adc r11, rax",
1948                "adc r12, 0",
1949                "mov rdx, qword ptr [rdi + 8]",
1950                "xor eax, eax",
1951                "mov r13, 0",
1952                "mulx rcx, rax, qword ptr [rsi + 0]",
1953                "adox r9, rax",
1954                "adcx r10, rcx",
1955                "mulx rcx, rax, qword ptr [rsi + 8]",
1956                "adox r10, rax",
1957                "adcx r11, rcx",
1958                "mulx rcx, rax, qword ptr [rsi + 16]",
1959                "adox r11, rax",
1960                "adcx r12, rcx",
1961                "mulx rcx, rax, qword ptr [rsi + 24]",
1962                "adox r12, rax",
1963                "adcx r13, rcx",
1964                "mov eax, 0",
1965                "adox r13, rax",
1966                "mov rdx, qword ptr [rdi + 16]",
1967                "xor eax, eax",
1968                "mov r14, 0",
1969                "mulx rcx, rax, qword ptr [rsi + 0]",
1970                "adox r10, rax",
1971                "adcx r11, rcx",
1972                "mulx rcx, rax, qword ptr [rsi + 8]",
1973                "adox r11, rax",
1974                "adcx r12, rcx",
1975                "mulx rcx, rax, qword ptr [rsi + 16]",
1976                "adox r12, rax",
1977                "adcx r13, rcx",
1978                "mulx rcx, rax, qword ptr [rsi + 24]",
1979                "adox r13, rax",
1980                "adcx r14, rcx",
1981                "mov eax, 0",
1982                "adox r14, rax",
1983                "mov rdx, qword ptr [rdi + 24]",
1984                "xor eax, eax",
1985                "mov r15, 0",
1986                "mulx rcx, rax, qword ptr [rsi + 0]",
1987                "adox r11, rax",
1988                "adcx r12, rcx",
1989                "mulx rcx, rax, qword ptr [rsi + 8]",
1990                "adox r12, rax",
1991                "adcx r13, rcx",
1992                "mulx rcx, rax, qword ptr [rsi + 16]",
1993                "adox r13, rax",
1994                "adcx r14, rcx",
1995                "mulx rcx, rax, qword ptr [rsi + 24]",
1996                "adox r14, rax",
1997                "adcx r15, rcx",
1998                "mov eax, 0",
1999                "adox r15, rax",
2000                "mov edx, 38",
2001                "xor eax, eax",
2002                "mulx rcx, rax, r12",
2003                "adox r8, rax",
2004                "adcx r9, rcx",
2005                "mulx rcx, rax, r13",
2006                "adox r9, rax",
2007                "adcx r10, rcx",
2008                "mulx rcx, rax, r14",
2009                "adox r10, rax",
2010                "adcx r11, rcx",
2011                "mulx rcx, rax, r15",
2012                "adox r11, rax",
2013                "mov eax, 0",
2014                "adcx rcx, rax",
2015                "adox rcx, rax",
2016                "imul rcx, rcx, 38",
2017                "add r8, rcx",
2018                "adc r9, 0",
2019                "adc r10, 0",
2020                "adc r11, 0",
2021                "sbb rax, rax",
2022                "and eax, 38",
2023                "add r8, rax",
2024                in("rsi") a.as_ptr(),
2025                in("rdi") b.as_ptr(),
2026                out("r8") r0,
2027                out("r9") r1,
2028                out("r10") r2,
2029                out("r11") r3,
2030                out("rax") _,
2031                out("rcx") _,
2032                out("rdx") _,
2033                out("r12") _,
2034                out("r13") _,
2035                out("r14") _,
2036                out("r15") _,
2037                options(pure, readonly, nostack),
2038            );
2039        }
2040        Some([r0, r1, r2, r3])
2041    }
2042    #[cfg(not(target_arch = "x86_64"))]
2043    {
2044        let _ = (a, b);
2045        None
2046    }
2047}
2048
2049/// Squaring modulo 2^255 - 19 (see [`fe25519_mul`]).
2050#[inline]
2051pub fn fe25519_sqr(a: &[u64; 4]) -> Option<[u64; 4]> {
2052    if !has_mulx() {
2053        return None;
2054    }
2055    #[cfg(target_arch = "x86_64")]
2056    {
2057        let (r0, r1, r2, r3): (u64, u64, u64, u64);
2058        // SAFETY: `has_mulx()` was true (MULX, ADCX, ADOX). The block reads the 4-limb inputs,
2059        // writes no memory, and declares every register it changes.
2060        unsafe {
2061            core::arch::asm!(
2062                "mov rdx, qword ptr [rsi]",
2063                "mulx r10, r9, qword ptr [rsi + 8]",
2064                "mulx r11, rax, qword ptr [rsi + 16]",
2065                "add r10, rax",
2066                "mulx r12, rax, qword ptr [rsi + 24]",
2067                "adc r11, rax",
2068                "adc r12, 0",
2069                "mov rdx, qword ptr [rsi + 8]",
2070                "mulx rcx, rax, qword ptr [rsi + 16]",
2071                "mulx r13, rdi, qword ptr [rsi + 24]",
2072                "add r11, rax",
2073                "adc r12, rcx",
2074                "adc r13, 0",
2075                "add r12, rdi",
2076                "adc r13, 0",
2077                "mov rdx, qword ptr [rsi + 16]",
2078                "mulx r14, rax, qword ptr [rsi + 24]",
2079                "add r13, rax",
2080                "adc r14, 0",
2081                "mov r15, 0",
2082                "add r9, r9",
2083                "adc r10, r10",
2084                "adc r11, r11",
2085                "adc r12, r12",
2086                "adc r13, r13",
2087                "adc r14, r14",
2088                "adc r15, r15",
2089                "mov rdx, qword ptr [rsi]",
2090                "mulx rcx, r8, rdx",
2091                "add r9, rcx",
2092                "mov rdx, qword ptr [rsi + 8]",
2093                "mulx rcx, rax, rdx",
2094                "adc r10, rax",
2095                "adc r11, rcx",
2096                "mov rdx, qword ptr [rsi + 16]",
2097                "mulx rcx, rax, rdx",
2098                "adc r12, rax",
2099                "adc r13, rcx",
2100                "mov rdx, qword ptr [rsi + 24]",
2101                "mulx rcx, rax, rdx",
2102                "adc r14, rax",
2103                "adc r15, rcx",
2104                "mov edx, 38",
2105                "xor eax, eax",
2106                "mulx rcx, rax, r12",
2107                "adox r8, rax",
2108                "adcx r9, rcx",
2109                "mulx rcx, rax, r13",
2110                "adox r9, rax",
2111                "adcx r10, rcx",
2112                "mulx rcx, rax, r14",
2113                "adox r10, rax",
2114                "adcx r11, rcx",
2115                "mulx rcx, rax, r15",
2116                "adox r11, rax",
2117                "mov eax, 0",
2118                "adcx rcx, rax",
2119                "adox rcx, rax",
2120                "imul rcx, rcx, 38",
2121                "add r8, rcx",
2122                "adc r9, 0",
2123                "adc r10, 0",
2124                "adc r11, 0",
2125                "sbb rax, rax",
2126                "and eax, 38",
2127                "add r8, rax",
2128                in("rsi") a.as_ptr(),
2129                out("rdi") _,
2130                out("r8") r0,
2131                out("r9") r1,
2132                out("r10") r2,
2133                out("r11") r3,
2134                out("rax") _,
2135                out("rcx") _,
2136                out("rdx") _,
2137                out("r12") _,
2138                out("r13") _,
2139                out("r14") _,
2140                out("r15") _,
2141                options(pure, readonly, nostack),
2142            );
2143        }
2144        Some([r0, r1, r2, r3])
2145    }
2146    #[cfg(not(target_arch = "x86_64"))]
2147    {
2148        let _ = a;
2149        None
2150    }
2151}
2152
2153/// a * 121666 modulo 2^255 - 19 on 4 x 64-bit limbs (see [`fe25519_mul`]); the X25519 ladder's
2154/// (A + 2) / 4 constant. `None` without MULX.
2155#[inline]
2156pub fn fe25519_mul121666(a: &[u64; 4]) -> Option<[u64; 4]> {
2157    if !has_mulx() {
2158        return None;
2159    }
2160    #[cfg(target_arch = "x86_64")]
2161    {
2162        let (r0, r1, r2, r3): (u64, u64, u64, u64);
2163        // SAFETY: `has_mulx()` was true (MULX). Reads the 4 limbs at `a`, writes no memory; all
2164        // registers are operands.
2165        unsafe {
2166            core::arch::asm!(
2167                "mulx {r1}, {r0}, qword ptr [{a}]",
2168                "mulx {r2}, {t}, qword ptr [{a} + 8]",
2169                "add {r1}, {t}",
2170                "mulx {r3}, {t}, qword ptr [{a} + 16]",
2171                "adc {r2}, {t}",
2172                "mulx {h}, {t}, qword ptr [{a} + 24]",
2173                "adc {r3}, {t}",
2174                "adc {h}, 0",
2175                "imul {h}, {h}, 38",
2176                "add {r0}, {h}",
2177                "adc {r1}, 0",
2178                "adc {r2}, 0",
2179                "adc {r3}, 0",
2180                "sbb {t}, {t}",
2181                "and {t}, 38",
2182                "add {r0}, {t}",
2183                a = in(reg) a.as_ptr(),
2184                in("rdx") 121666u64,
2185                r0 = out(reg) r0,
2186                r1 = out(reg) r1,
2187                r2 = out(reg) r2,
2188                r3 = out(reg) r3,
2189                t = out(reg) _,
2190                h = out(reg) _,
2191                options(pure, readonly, nostack),
2192            );
2193        }
2194        Some([r0, r1, r2, r3])
2195    }
2196    #[cfg(not(target_arch = "x86_64"))]
2197    {
2198        let _ = a;
2199        None
2200    }
2201}
2202
2203/// a + b modulo 2^255 - 19 on 4 x 64-bit limbs, for inputs below 2^256 (result below 2^256,
2204/// not fully reduced): a carry out of bit 256 is folded back as 38, twice. Constant time.
2205#[cfg(target_arch = "x86_64")]
2206#[inline(always)]
2207pub fn fe25519_add(a: &[u64; 4], b: &[u64; 4]) -> [u64; 4] {
2208    let [mut r0, mut r1, mut r2, mut r3] = *a;
2209    // SAFETY: reads the 4 limbs at `b`, writes no memory; all registers are operands.
2210    unsafe {
2211        core::arch::asm!(
2212            "add {r0}, qword ptr [{b}]",
2213            "adc {r1}, qword ptr [{b} + 8]",
2214            "adc {r2}, qword ptr [{b} + 16]",
2215            "adc {r3}, qword ptr [{b} + 24]",
2216            "sbb {c}, {c}",
2217            "and {c}, 38",
2218            "add {r0}, {c}",
2219            "adc {r1}, 0",
2220            "adc {r2}, 0",
2221            "adc {r3}, 0",
2222            "sbb {c}, {c}",
2223            "and {c}, 38",
2224            "add {r0}, {c}",
2225            r0 = inout(reg) r0,
2226            r1 = inout(reg) r1,
2227            r2 = inout(reg) r2,
2228            r3 = inout(reg) r3,
2229            b = in(reg) b.as_ptr(),
2230            c = out(reg) _,
2231            options(pure, readonly, nostack),
2232        );
2233    }
2234    [r0, r1, r2, r3]
2235}
2236
2237/// a - b modulo 2^255 - 19 on 4 x 64-bit limbs (see [`fe25519_add`]): a borrow is folded back
2238/// as -38, twice.
2239#[cfg(target_arch = "x86_64")]
2240#[inline(always)]
2241pub fn fe25519_sub(a: &[u64; 4], b: &[u64; 4]) -> [u64; 4] {
2242    let [mut r0, mut r1, mut r2, mut r3] = *a;
2243    // SAFETY: reads the 4 limbs at `b`, writes no memory; all registers are operands.
2244    unsafe {
2245        core::arch::asm!(
2246            "sub {r0}, qword ptr [{b}]",
2247            "sbb {r1}, qword ptr [{b} + 8]",
2248            "sbb {r2}, qword ptr [{b} + 16]",
2249            "sbb {r3}, qword ptr [{b} + 24]",
2250            "sbb {c}, {c}",
2251            "and {c}, 38",
2252            "sub {r0}, {c}",
2253            "sbb {r1}, 0",
2254            "sbb {r2}, 0",
2255            "sbb {r3}, 0",
2256            "sbb {c}, {c}",
2257            "and {c}, 38",
2258            "sub {r0}, {c}",
2259            r0 = inout(reg) r0,
2260            r1 = inout(reg) r1,
2261            r2 = inout(reg) r2,
2262            r3 = inout(reg) r3,
2263            b = in(reg) b.as_ptr(),
2264            c = out(reg) _,
2265            options(pure, readonly, nostack),
2266        );
2267    }
2268    [r0, r1, r2, r3]
2269}
2270
2271/// The core of a Montgomery multiplication for big integers with MULX/ADCX/ADOX (interleaved
2272/// CIOS, four limbs per loop step): leaves t = a * b * 2^(-64 n) mod m, plus possibly m, in
2273/// `t[..n]` and its top bit in `t[n]`, where n = m.len(); the caller makes the final
2274/// conditional subtraction. Requires n a non-zero multiple of 4, `a` and `b` of n limbs below
2275/// m, `t` at least n + 2 limbs (overwritten), and `m0inv` = -m^-1 mod 2^64. Returns false
2276/// (and does nothing) without MULX/ADX or when the sizes do not fit. Constant time.
2277pub fn bigint_mont_mul(t: &mut [u64], a: &[u64], b: &[u64], m: &[u64], m0inv: u64) -> bool {
2278    let n = m.len();
2279    if !has_mulx() || n == 0 || n % 4 != 0 || a.len() != n || b.len() != n || t.len() < n + 2 {
2280        return false;
2281    }
2282    t[..n + 2].fill(0);
2283    #[cfg(not(target_arch = "x86_64"))]
2284    let _ = m0inv;
2285    #[cfg(target_arch = "x86_64")]
2286    if n == 16 {
2287        // 1024-bit moduli (RSA-2048 CRT halves): the same steps with straight-line passes.
2288        // SAFETY: as below, with n = 16.
2289        unsafe {
2290            core::arch::asm!(
2291            "2:",
2292            "mov rdx, qword ptr [rdi]",
2293            "xor r13d, r13d",
2294            "mulx r12, rax, qword ptr [rsi + 0]",
2295            "adcx rax, qword ptr [r9 + 0]",
2296            "adox rax, r13",
2297            "mov qword ptr [r9 + 0], rax",
2298            "mulx r13, rax, qword ptr [rsi + 8]",
2299            "adcx rax, qword ptr [r9 + 8]",
2300            "adox rax, r12",
2301            "mov qword ptr [r9 + 8], rax",
2302            "mulx r12, rax, qword ptr [rsi + 16]",
2303            "adcx rax, qword ptr [r9 + 16]",
2304            "adox rax, r13",
2305            "mov qword ptr [r9 + 16], rax",
2306            "mulx r13, rax, qword ptr [rsi + 24]",
2307            "adcx rax, qword ptr [r9 + 24]",
2308            "adox rax, r12",
2309            "mov qword ptr [r9 + 24], rax",
2310            "mulx r12, rax, qword ptr [rsi + 32]",
2311            "adcx rax, qword ptr [r9 + 32]",
2312            "adox rax, r13",
2313            "mov qword ptr [r9 + 32], rax",
2314            "mulx r13, rax, qword ptr [rsi + 40]",
2315            "adcx rax, qword ptr [r9 + 40]",
2316            "adox rax, r12",
2317            "mov qword ptr [r9 + 40], rax",
2318            "mulx r12, rax, qword ptr [rsi + 48]",
2319            "adcx rax, qword ptr [r9 + 48]",
2320            "adox rax, r13",
2321            "mov qword ptr [r9 + 48], rax",
2322            "mulx r13, rax, qword ptr [rsi + 56]",
2323            "adcx rax, qword ptr [r9 + 56]",
2324            "adox rax, r12",
2325            "mov qword ptr [r9 + 56], rax",
2326            "mulx r12, rax, qword ptr [rsi + 64]",
2327            "adcx rax, qword ptr [r9 + 64]",
2328            "adox rax, r13",
2329            "mov qword ptr [r9 + 64], rax",
2330            "mulx r13, rax, qword ptr [rsi + 72]",
2331            "adcx rax, qword ptr [r9 + 72]",
2332            "adox rax, r12",
2333            "mov qword ptr [r9 + 72], rax",
2334            "mulx r12, rax, qword ptr [rsi + 80]",
2335            "adcx rax, qword ptr [r9 + 80]",
2336            "adox rax, r13",
2337            "mov qword ptr [r9 + 80], rax",
2338            "mulx r13, rax, qword ptr [rsi + 88]",
2339            "adcx rax, qword ptr [r9 + 88]",
2340            "adox rax, r12",
2341            "mov qword ptr [r9 + 88], rax",
2342            "mulx r12, rax, qword ptr [rsi + 96]",
2343            "adcx rax, qword ptr [r9 + 96]",
2344            "adox rax, r13",
2345            "mov qword ptr [r9 + 96], rax",
2346            "mulx r13, rax, qword ptr [rsi + 104]",
2347            "adcx rax, qword ptr [r9 + 104]",
2348            "adox rax, r12",
2349            "mov qword ptr [r9 + 104], rax",
2350            "mulx r12, rax, qword ptr [rsi + 112]",
2351            "adcx rax, qword ptr [r9 + 112]",
2352            "adox rax, r13",
2353            "mov qword ptr [r9 + 112], rax",
2354            "mulx r13, rax, qword ptr [rsi + 120]",
2355            "adcx rax, qword ptr [r9 + 120]",
2356            "adox rax, r12",
2357            "mov qword ptr [r9 + 120], rax",
2358            "mov rax, qword ptr [r9 + 128]",
2359            "adcx rax, r13",
2360            "mov r14d, 0",
2361            "adox rax, r14",
2362            "mov qword ptr [r9 + 128], rax",
2363            "mov rax, qword ptr [r9 + 136]",
2364            "adcx rax, r14",
2365            "adox rax, r14",
2366            "mov qword ptr [r9 + 136], rax",
2367            "mov rdx, qword ptr [r9]",
2368            "imul rdx, r11",
2369            "xor r13d, r13d",
2370            "mulx r12, rax, qword ptr [r8 + 0]",
2371            "adcx rax, qword ptr [r9 + 0]",
2372            "adox rax, r13",
2373            "mulx r13, rax, qword ptr [r8 + 8]",
2374            "adcx rax, qword ptr [r9 + 8]",
2375            "adox rax, r12",
2376            "mov qword ptr [r9 + 0], rax",
2377            "mulx r12, rax, qword ptr [r8 + 16]",
2378            "adcx rax, qword ptr [r9 + 16]",
2379            "adox rax, r13",
2380            "mov qword ptr [r9 + 8], rax",
2381            "mulx r13, rax, qword ptr [r8 + 24]",
2382            "adcx rax, qword ptr [r9 + 24]",
2383            "adox rax, r12",
2384            "mov qword ptr [r9 + 16], rax",
2385            "mulx r12, rax, qword ptr [r8 + 32]",
2386            "adcx rax, qword ptr [r9 + 32]",
2387            "adox rax, r13",
2388            "mov qword ptr [r9 + 24], rax",
2389            "mulx r13, rax, qword ptr [r8 + 40]",
2390            "adcx rax, qword ptr [r9 + 40]",
2391            "adox rax, r12",
2392            "mov qword ptr [r9 + 32], rax",
2393            "mulx r12, rax, qword ptr [r8 + 48]",
2394            "adcx rax, qword ptr [r9 + 48]",
2395            "adox rax, r13",
2396            "mov qword ptr [r9 + 40], rax",
2397            "mulx r13, rax, qword ptr [r8 + 56]",
2398            "adcx rax, qword ptr [r9 + 56]",
2399            "adox rax, r12",
2400            "mov qword ptr [r9 + 48], rax",
2401            "mulx r12, rax, qword ptr [r8 + 64]",
2402            "adcx rax, qword ptr [r9 + 64]",
2403            "adox rax, r13",
2404            "mov qword ptr [r9 + 56], rax",
2405            "mulx r13, rax, qword ptr [r8 + 72]",
2406            "adcx rax, qword ptr [r9 + 72]",
2407            "adox rax, r12",
2408            "mov qword ptr [r9 + 64], rax",
2409            "mulx r12, rax, qword ptr [r8 + 80]",
2410            "adcx rax, qword ptr [r9 + 80]",
2411            "adox rax, r13",
2412            "mov qword ptr [r9 + 72], rax",
2413            "mulx r13, rax, qword ptr [r8 + 88]",
2414            "adcx rax, qword ptr [r9 + 88]",
2415            "adox rax, r12",
2416            "mov qword ptr [r9 + 80], rax",
2417            "mulx r12, rax, qword ptr [r8 + 96]",
2418            "adcx rax, qword ptr [r9 + 96]",
2419            "adox rax, r13",
2420            "mov qword ptr [r9 + 88], rax",
2421            "mulx r13, rax, qword ptr [r8 + 104]",
2422            "adcx rax, qword ptr [r9 + 104]",
2423            "adox rax, r12",
2424            "mov qword ptr [r9 + 96], rax",
2425            "mulx r12, rax, qword ptr [r8 + 112]",
2426            "adcx rax, qword ptr [r9 + 112]",
2427            "adox rax, r13",
2428            "mov qword ptr [r9 + 104], rax",
2429            "mulx r13, rax, qword ptr [r8 + 120]",
2430            "adcx rax, qword ptr [r9 + 120]",
2431            "adox rax, r12",
2432            "mov qword ptr [r9 + 112], rax",
2433            "mov rax, qword ptr [r9 + 128]",
2434            "adcx rax, r13",
2435            "mov r14d, 0",
2436            "adox rax, r14",
2437            "mov qword ptr [r9 + 120], rax",
2438            "mov rax, qword ptr [r9 + 136]",
2439            "adcx rax, r14",
2440            "adox rax, r14",
2441            "mov qword ptr [r9 + 128], rax",
2442            "mov qword ptr [r9 + 136], r14",
2443            "lea rdi, [rdi + 8]",
2444            "dec r15",
2445            "jnz 2b",
2446                in("rsi") a.as_ptr(),
2447                inout("rdi") b.as_ptr() => _,
2448                in("r8") m.as_ptr(),
2449                in("r9") t.as_mut_ptr(),
2450                in("r11") m0inv,
2451                inout("r15") n => _,
2452                out("rax") _,
2453                out("rdx") _,
2454                out("r12") _,
2455                out("r13") _,
2456                out("r14") _,
2457                options(nostack),
2458            );
2459        }
2460        return true;
2461    }
2462    #[cfg(target_arch = "x86_64")]
2463    // SAFETY: `has_mulx()` was true (MULX, ADCX, ADOX). The block reads n limbs at `a`, `b`
2464    // and `m`, reads and writes the first n + 2 limbs of `t` (the offsets used are below
2465    // 8 (n + 2) bytes; the shifted stores write t[j - 1] for j >= 1), and declares every
2466    // register it changes. Labels are local numeric labels.
2467    unsafe {
2468        core::arch::asm!(
2469            "2:",
2470            // ---- t += a * b[i]
2471            "mov rdx, qword ptr [rdi]",
2472            "mov rcx, r10",
2473            "shr rcx, 2",
2474            "xor r14d, r14d",
2475            "xor r13d, r13d",
2476            "3:",
2477            "mulx r12, rax, qword ptr [rsi + r14]",
2478            "adcx rax, qword ptr [r9 + r14]",
2479            "adox rax, r13",
2480            "mov qword ptr [r9 + r14], rax",
2481            "mulx r13, rax, qword ptr [rsi + r14 + 8]",
2482            "adcx rax, qword ptr [r9 + r14 + 8]",
2483            "adox rax, r12",
2484            "mov qword ptr [r9 + r14 + 8], rax",
2485            "mulx r12, rax, qword ptr [rsi + r14 + 16]",
2486            "adcx rax, qword ptr [r9 + r14 + 16]",
2487            "adox rax, r13",
2488            "mov qword ptr [r9 + r14 + 16], rax",
2489            "mulx r13, rax, qword ptr [rsi + r14 + 24]",
2490            "adcx rax, qword ptr [r9 + r14 + 24]",
2491            "adox rax, r12",
2492            "mov qword ptr [r9 + r14 + 24], rax",
2493            "lea r14, [r14 + 32]",
2494            "lea rcx, [rcx - 1]",
2495            "jrcxz 4f",
2496            "jmp 3b",
2497            "4:",
2498            "mov rax, qword ptr [r9 + r14]",
2499            "adcx rax, r13",
2500            "mov r12d, 0",
2501            "adox rax, r12",
2502            "mov qword ptr [r9 + r14], rax",
2503            "mov rax, qword ptr [r9 + r14 + 8]",
2504            "adcx rax, r12",
2505            "adox rax, r12",
2506            "mov qword ptr [r9 + r14 + 8], rax",
2507            // ---- t = (t + q * m) / 2^64 with q = t[0] * m0inv
2508            "mov rdx, qword ptr [r9]",
2509            "imul rdx, r11",
2510            "mov rcx, r10",
2511            "shr rcx, 2",
2512            "xor r14d, r14d",
2513            "xor r13d, r13d",
2514            "mulx r12, rax, qword ptr [r8]",
2515            "adcx rax, qword ptr [r9]",
2516            "adox rax, r13",
2517            "mulx r13, rax, qword ptr [r8 + 8]",
2518            "adcx rax, qword ptr [r9 + 8]",
2519            "adox rax, r12",
2520            "mov qword ptr [r9], rax",
2521            "mulx r12, rax, qword ptr [r8 + 16]",
2522            "adcx rax, qword ptr [r9 + 16]",
2523            "adox rax, r13",
2524            "mov qword ptr [r9 + 8], rax",
2525            "mulx r13, rax, qword ptr [r8 + 24]",
2526            "adcx rax, qword ptr [r9 + 24]",
2527            "adox rax, r12",
2528            "mov qword ptr [r9 + 16], rax",
2529            "lea r14, [r14 + 32]",
2530            "lea rcx, [rcx - 1]",
2531            "jrcxz 6f",
2532            "5:",
2533            "mulx r12, rax, qword ptr [r8 + r14]",
2534            "adcx rax, qword ptr [r9 + r14]",
2535            "adox rax, r13",
2536            "mov qword ptr [r9 + r14 - 8], rax",
2537            "mulx r13, rax, qword ptr [r8 + r14 + 8]",
2538            "adcx rax, qword ptr [r9 + r14 + 8]",
2539            "adox rax, r12",
2540            "mov qword ptr [r9 + r14], rax",
2541            "mulx r12, rax, qword ptr [r8 + r14 + 16]",
2542            "adcx rax, qword ptr [r9 + r14 + 16]",
2543            "adox rax, r13",
2544            "mov qword ptr [r9 + r14 + 8], rax",
2545            "mulx r13, rax, qword ptr [r8 + r14 + 24]",
2546            "adcx rax, qword ptr [r9 + r14 + 24]",
2547            "adox rax, r12",
2548            "mov qword ptr [r9 + r14 + 16], rax",
2549            "lea r14, [r14 + 32]",
2550            "lea rcx, [rcx - 1]",
2551            "jrcxz 6f",
2552            "jmp 5b",
2553            "6:",
2554            "mov rax, qword ptr [r9 + r14]",
2555            "adcx rax, r13",
2556            "mov r12d, 0",
2557            "adox rax, r12",
2558            "mov qword ptr [r9 + r14 - 8], rax",
2559            "mov rax, qword ptr [r9 + r14 + 8]",
2560            "adcx rax, r12",
2561            "adox rax, r12",
2562            "mov qword ptr [r9 + r14], rax",
2563            "mov qword ptr [r9 + r14 + 8], r12",
2564            "lea rdi, [rdi + 8]",
2565            "dec r15",
2566            "jnz 2b",
2567            in("rsi") a.as_ptr(),
2568            inout("rdi") b.as_ptr() => _,
2569            in("r8") m.as_ptr(),
2570            in("r9") t.as_mut_ptr(),
2571            in("r10") n,
2572            in("r11") m0inv,
2573            inout("r15") n => _,
2574            out("rax") _,
2575            out("rcx") _,
2576            out("rdx") _,
2577            out("r12") _,
2578            out("r13") _,
2579            out("r14") _,
2580            options(nostack),
2581        );
2582    }
2583    true
2584}
2585
2586#[cfg(test)]
2587mod bignum_tests {
2588    use super::*;
2589
2590    /// Portable reference: a * b * 2^-256 mod m by bit-serial reduction.
2591    fn reference(a: &[u64; 4], b: &[u64; 4], m: &[u64; 4]) -> [u64; 4] {
2592        // t = a * b (512 bits)
2593        let mut t = [0u64; 9];
2594        for i in 0..4 {
2595            let mut c = 0u128;
2596            for j in 0..4 {
2597                let v = t[i + j] as u128 + a[i] as u128 * b[j] as u128 + c;
2598                t[i + j] = v as u64;
2599                c = v >> 64;
2600            }
2601            t[i + 4] = c as u64;
2602        }
2603        // divide by 2^256 modulo m: 256 halvings (add m when odd)
2604        for _ in 0..256 {
2605            if t[0] & 1 == 1 {
2606                let mut c = 0u128;
2607                for j in 0..9 {
2608                    let v = t[j] as u128 + if j < 4 { m[j] as u128 } else { 0 } + c;
2609                    t[j] = v as u64;
2610                    c = v >> 64;
2611                }
2612            }
2613            for j in 0..8 {
2614                t[j] = (t[j] >> 1) | (t[j + 1] << 63);
2615            }
2616            t[8] >>= 1;
2617        }
2618        // now t < 2m: subtract once if needed
2619        let ge = t[4] != 0
2620            || (0..4)
2621                .rev()
2622                .map(|j| (t[j], m[j]))
2623                .find(|(x, y)| x != y)
2624                .is_none_or(|(x, y)| x > y);
2625        if ge {
2626            let mut br = 0i128;
2627            for j in 0..4 {
2628                let v = t[j] as i128 - m[j] as i128 + br;
2629                t[j] = v as u64;
2630                br = v >> 64;
2631            }
2632        }
2633        [t[0], t[1], t[2], t[3]]
2634    }
2635
2636    #[test]
2637    fn fe25519_kernels_match_reference() {
2638        if !has_mulx() {
2639            return;
2640        }
2641        let p = [
2642            0xffff_ffff_ffff_ffed,
2643            u64::MAX,
2644            u64::MAX,
2645            0x7fff_ffff_ffff_ffff,
2646        ];
2647        // the canonical residue of x < 2^256 (< 3p)
2648        let canon = |x: [u64; 4]| -> [u64; 4] {
2649            let mut x = x;
2650            for _ in 0..2 {
2651                let ge = (0..4)
2652                    .rev()
2653                    .map(|j| (x[j], p[j]))
2654                    .find(|(a, b)| a != b)
2655                    .is_none_or(|(a, b)| a > b);
2656                if ge {
2657                    let mut br = 0i128;
2658                    for j in 0..4 {
2659                        let v = x[j] as i128 - p[j] as i128 + br;
2660                        x[j] = v as u64;
2661                        br = v >> 64;
2662                    }
2663                }
2664            }
2665            x
2666        };
2667        let mut seed = 0x0bad_5eed_1234_5678u64;
2668        let mut next = || {
2669            seed ^= seed << 13;
2670            seed ^= seed >> 7;
2671            seed ^= seed << 17;
2672            seed
2673        };
2674        let mut vals = vec![
2675            [0u64; 4],
2676            [1, 0, 0, 0],
2677            p,
2678            [u64::MAX; 4],
2679            [u64::MAX - 37, u64::MAX, u64::MAX, u64::MAX],
2680            [38, 0, 0, 0],
2681        ];
2682        for _ in 0..2000 {
2683            vals.push([next(), next(), next(), next()]);
2684        }
2685        // reference(x, y) = x y 2^-256 mod p, so reference(c, 1) == reference(a, b) iff c = a b
2686        let one = [1u64, 0, 0, 0];
2687        for (i, a) in vals.iter().enumerate() {
2688            let b = &vals[(i * 11 + 5) % vals.len()];
2689            let (ca, cb) = (canon(*a), canon(*b));
2690            let m = fe25519_mul(a, b).unwrap();
2691            assert_eq!(
2692                reference(&canon(m), &one, &p),
2693                reference(&ca, &cb, &p),
2694                "mul {a:x?} {b:x?}"
2695            );
2696            let s = fe25519_sqr(a).unwrap();
2697            assert_eq!(
2698                reference(&canon(s), &one, &p),
2699                reference(&ca, &ca, &p),
2700                "sqr {a:x?}"
2701            );
2702            let k = fe25519_mul121666(a).unwrap();
2703            assert_eq!(
2704                reference(&canon(k), &one, &p),
2705                reference(&ca, &[121666, 0, 0, 0], &p),
2706                "mul121666 {a:x?}"
2707            );
2708            #[cfg(target_arch = "x86_64")]
2709            {
2710                let sum = canon(fe25519_add(a, b));
2711                let diff = canon(fe25519_sub(a, b));
2712                assert_eq!(canon(fe25519_sub(&sum, &cb)), ca);
2713                assert_eq!(canon(fe25519_add(&diff, &cb)), ca);
2714                assert_eq!(canon(fe25519_add(&fe25519_sub(&[0; 4], b), b)), [0; 4]);
2715            }
2716        }
2717    }
2718
2719    #[test]
2720    fn p256_kernels_match_reference() {
2721        if !has_mulx() {
2722            return;
2723        }
2724        let p = [
2725            0xffff_ffff_ffff_ffff,
2726            0x0000_0000_ffff_ffff,
2727            0,
2728            0xffff_ffff_0000_0001,
2729        ];
2730        let mut seed = 0x2545_f491_4f6c_dd1du64;
2731        let mut next = || {
2732            seed ^= seed << 13;
2733            seed ^= seed >> 7;
2734            seed ^= seed << 17;
2735            seed
2736        };
2737        let mut pm1 = p;
2738        pm1[0] -= 1;
2739        let mut vals = vec![
2740            [0u64; 4],
2741            [1, 0, 0, 0],
2742            pm1,
2743            [u64::MAX, u64::MAX, u64::MAX, 0xffff_ffff_0000_0000],
2744        ];
2745        for _ in 0..2000 {
2746            let mut x = [next(), next(), next(), next()];
2747            if x[3] >= p[3] {
2748                x[3] = p[3] - 1;
2749            }
2750            vals.push(x);
2751        }
2752        for (i, a) in vals.iter().enumerate() {
2753            assert_eq!(p256_sqr_mont(a).unwrap(), reference(a, a, &p), "{a:x?}");
2754            let b = &vals[(i * 7 + 3) % vals.len()];
2755            assert_eq!(
2756                p256_mul_mont(a, b).unwrap(),
2757                reference(a, b, &p),
2758                "{a:x?} {b:x?}"
2759            );
2760            assert_eq!(p256_mul_mont(a, &pm1).unwrap(), reference(a, &pm1, &p));
2761            #[cfg(target_arch = "x86_64")]
2762            {
2763                // a + b and a - b against u128 arithmetic modulo p
2764                let (s, d) = (p256_add(a, b), p256_sub(a, b));
2765                let wide = |x: &[u64; 4]| -> [u128; 4] { x.map(|v| v as u128) };
2766                let (aw, bw) = (wide(a), wide(b));
2767                let mut sum = [0u64; 5];
2768                let mut c = 0u128;
2769                for j in 0..4 {
2770                    let v = aw[j] + bw[j] + c;
2771                    sum[j] = v as u64;
2772                    c = v >> 64;
2773                }
2774                sum[4] = c as u64;
2775                let ge = sum[4] != 0
2776                    || (0..4)
2777                        .rev()
2778                        .map(|j| (sum[j], p[j]))
2779                        .find(|(x, y)| x != y)
2780                        .is_none_or(|(x, y)| x > y);
2781                if ge {
2782                    let mut br = 0i128;
2783                    for j in 0..4 {
2784                        let v = sum[j] as i128 - p[j] as i128 + br;
2785                        sum[j] = v as u64;
2786                        br = v >> 64;
2787                    }
2788                }
2789                assert_eq!(s, [sum[0], sum[1], sum[2], sum[3]]);
2790                assert_eq!(p256_add(&d, b), *a, "a - b + b");
2791            }
2792        }
2793        let n = [
2794            0xf3b9_cac2_fc63_2551,
2795            0xbce6_faad_a717_9e84,
2796            0xffff_ffff_ffff_ffff,
2797            0xffff_ffff_0000_0000,
2798        ];
2799        let mut nm1 = n;
2800        nm1[0] -= 1;
2801        vals[2] = nm1;
2802        for v in vals.iter_mut().skip(3) {
2803            if v[3] >= n[3] {
2804                v[3] = n[3] - 1;
2805            }
2806        }
2807        for (i, a) in vals.iter().enumerate() {
2808            assert_eq!(
2809                p256_order_mul_mont(a, a).unwrap(),
2810                reference(a, a, &n),
2811                "{a:x?}"
2812            );
2813            let b = &vals[(i * 5 + 1) % vals.len()];
2814            assert_eq!(
2815                p256_order_mul_mont(a, b).unwrap(),
2816                reference(a, b, &n),
2817                "{a:x?} {b:x?}"
2818            );
2819            assert_eq!(
2820                p256_order_mul_mont(a, &nm1).unwrap(),
2821                reference(a, &nm1, &n)
2822            );
2823        }
2824    }
2825}
2826
2827#[cfg(test)]
2828mod bignum_timing {
2829    #[test]
2830    #[ignore]
2831    fn kernel_latency() {
2832        let mut a = [1u64, 2, 3, 4];
2833        let b = [5u64, 6, 7, 8];
2834        let n = 20_000_000;
2835        let t = std::time::Instant::now();
2836        for _ in 0..n {
2837            a = super::p256_mul_mont(&a, &b).unwrap();
2838        }
2839        println!(
2840            "p256_mul_mont {:.2} ns",
2841            t.elapsed().as_nanos() as f64 / n as f64
2842        );
2843        let t = std::time::Instant::now();
2844        for _ in 0..n {
2845            a = super::p256_sqr_mont(&a).unwrap();
2846        }
2847        println!(
2848            "p256_sqr_mont {:.2} ns",
2849            t.elapsed().as_nanos() as f64 / n as f64
2850        );
2851        let t = std::time::Instant::now();
2852        for _ in 0..n {
2853            a = super::p256_order_mul_mont(&a, &b).unwrap();
2854        }
2855        println!(
2856            "order_mul {:.2} ns",
2857            t.elapsed().as_nanos() as f64 / n as f64
2858        );
2859        let t = std::time::Instant::now();
2860        for _ in 0..n {
2861            a = super::fe25519_mul(&a, &b).unwrap();
2862        }
2863        println!(
2864            "fe25519_mul latency {:.2} ns",
2865            t.elapsed().as_nanos() as f64 / n as f64
2866        );
2867        let mut c = [9u64, 8, 7, 6];
2868        let mut d = [3u64, 1, 4, 1];
2869        let mut e = [2u64, 7, 1, 8];
2870        let t = std::time::Instant::now();
2871        for _ in 0..n {
2872            a = super::fe25519_mul(&a, &b).unwrap();
2873            c = super::fe25519_mul(&c, &b).unwrap();
2874            d = super::fe25519_mul(&d, &b).unwrap();
2875            e = super::fe25519_mul(&e, &b).unwrap();
2876        }
2877        println!(
2878            "fe25519_mul 4 chains {:.2} ns per mul {:?}",
2879            t.elapsed().as_nanos() as f64 / n as f64 / 4.0,
2880            (a[0] ^ c[0] ^ d[0] ^ e[0])
2881        );
2882        let t = std::time::Instant::now();
2883        let mut x = 1u64;
2884        for _ in 0..n {
2885            x = std::hint::black_box(x.wrapping_mul(3).wrapping_add(1));
2886        }
2887        println!(
2888            "calibration (black_box mul-add) {:.2} ns {x}",
2889            t.elapsed().as_nanos() as f64 / n as f64
2890        );
2891    }
2892}
2893
2894// ---- Poly1305 with AVX2 (for simoom-tls). Self-contained.
2895
2896/// Absorbs the longest prefix of `data` that is a whole number of 64-byte groups into the
2897/// Poly1305 accumulator, with AVX2: four 16-byte blocks (each with the 2^128 bit) per step in
2898/// four lanes of radix-2^26 limbs, Horner's rule with r^4 in each lane and a final
2899/// multiplication of the lanes by r^4, r^3, r^2 and r. `h` is the accumulator and `rpow` holds
2900/// r, r^2, r^3, r^4, all as five 26-bit limbs (limbs a little above 2^26 are fine; the result
2901/// has the same form). Returns the number of bytes absorbed: 0 without AVX2. Constant time.
2902pub fn poly1305_blocks(h: &mut [u64; 5], rpow: &[[u64; 5]; 4], data: &[u8]) -> usize {
2903    #[cfg(target_arch = "x86_64")]
2904    {
2905        if features() & F_SIMD8 != 0 && data.len() >= 64 {
2906            let n = data.len() / 64 * 64;
2907            // SAFETY: F_SIMD8 means the CPU has AVX2; `n` is a non-zero multiple of 64 and at
2908            // most `data.len()`.
2909            unsafe { x86_poly::blocks(h, rpow, &data[..n]) };
2910            return n;
2911        }
2912    }
2913    let _ = (h, rpow, data);
2914    0
2915}
2916
2917#[cfg(target_arch = "x86_64")]
2918mod x86_poly {
2919    use core::arch::x86_64::*;
2920
2921    macro_rules! m {
2922        ($x:expr, $y:expr) => {
2923            _mm256_mul_epu32($x, $y)
2924        };
2925    }
2926    macro_rules! add {
2927        ($x:expr, $y:expr) => {
2928            _mm256_add_epi64($x, $y)
2929        };
2930    }
2931
2932    /// d = a * r mod 2^130 - 5 per lane, unreduced (s = 5 r).
2933    ///
2934    /// # Safety
2935    /// The CPU must have AVX2.
2936    #[target_feature(enable = "avx2")]
2937    #[inline]
2938    #[allow(unused_unsafe)] // the intrinsics are safe to call here from Rust 1.87
2939    unsafe fn mul(a: &[__m256i; 5], r: &[__m256i; 5], s: &[__m256i; 5]) -> [__m256i; 5] {
2940        // SAFETY: AVX2 is available (the caller's contract); register-only intrinsics.
2941        unsafe {
2942            [
2943                add!(
2944                    add!(
2945                        add!(m!(a[0], r[0]), m!(a[1], s[4])),
2946                        add!(m!(a[2], s[3]), m!(a[3], s[2]))
2947                    ),
2948                    m!(a[4], s[1])
2949                ),
2950                add!(
2951                    add!(
2952                        add!(m!(a[0], r[1]), m!(a[1], r[0])),
2953                        add!(m!(a[2], s[4]), m!(a[3], s[3]))
2954                    ),
2955                    m!(a[4], s[2])
2956                ),
2957                add!(
2958                    add!(
2959                        add!(m!(a[0], r[2]), m!(a[1], r[1])),
2960                        add!(m!(a[2], r[0]), m!(a[3], s[4]))
2961                    ),
2962                    m!(a[4], s[3])
2963                ),
2964                add!(
2965                    add!(
2966                        add!(m!(a[0], r[3]), m!(a[1], r[2])),
2967                        add!(m!(a[2], r[1]), m!(a[3], r[0]))
2968                    ),
2969                    m!(a[4], s[4])
2970                ),
2971                add!(
2972                    add!(
2973                        add!(m!(a[0], r[4]), m!(a[1], r[3])),
2974                        add!(m!(a[2], r[2]), m!(a[3], r[1]))
2975                    ),
2976                    m!(a[4], r[0])
2977                ),
2978            ]
2979        }
2980    }
2981
2982    /// Partial carry: every limb below 2^26 except limbs 1 and 4 (a little above).
2983    ///
2984    /// # Safety
2985    /// The CPU must have AVX2.
2986    #[target_feature(enable = "avx2")]
2987    #[inline]
2988    #[allow(unused_unsafe)] // the intrinsics are safe to call here from Rust 1.87
2989    unsafe fn carry(mut d: [__m256i; 5]) -> [__m256i; 5] {
2990        // SAFETY: AVX2 is available (the caller's contract); register-only intrinsics.
2991        unsafe {
2992            // two carry chains in parallel (3 -> 4 and 0 -> 1, then 4 -> 0 and 1 -> 2, then 2 -> 3
2993            // and 0 -> 1, then 3 -> 4): four steps deep instead of seven
2994            let m26 = _mm256_set1_epi64x(0x3ff_ffff);
2995            let (c3, c0) = (_mm256_srli_epi64(d[3], 26), _mm256_srli_epi64(d[0], 26));
2996            d[3] = _mm256_and_si256(d[3], m26);
2997            d[0] = _mm256_and_si256(d[0], m26);
2998            d[4] = add!(d[4], c3);
2999            d[1] = add!(d[1], c0);
3000            let (c4, c1) = (_mm256_srli_epi64(d[4], 26), _mm256_srli_epi64(d[1], 26));
3001            d[4] = _mm256_and_si256(d[4], m26);
3002            d[1] = _mm256_and_si256(d[1], m26);
3003            d[0] = add!(d[0], add!(c4, _mm256_slli_epi64(c4, 2)));
3004            d[2] = add!(d[2], c1);
3005            let (c2, c0) = (_mm256_srli_epi64(d[2], 26), _mm256_srli_epi64(d[0], 26));
3006            d[2] = _mm256_and_si256(d[2], m26);
3007            d[0] = _mm256_and_si256(d[0], m26);
3008            d[3] = add!(d[3], c2);
3009            d[1] = add!(d[1], c0);
3010            let c = _mm256_srli_epi64(d[3], 26);
3011            d[3] = _mm256_and_si256(d[3], m26);
3012            d[4] = add!(d[4], c);
3013            d
3014        }
3015    }
3016
3017    /// Four blocks (64 bytes at `p`) as radix-2^26 limbs; lanes hold blocks 0, 2, 1, 3.
3018    ///
3019    /// # Safety
3020    /// 64 bytes at `p` must be readable.
3021    #[target_feature(enable = "avx2")]
3022    #[inline]
3023    unsafe fn load(p: *const u8) -> [__m256i; 5] {
3024        // SAFETY: the caller guarantees AVX2 and 64 readable bytes at `p`.
3025        unsafe {
3026            let m26 = _mm256_set1_epi64x(0x3ff_ffff);
3027            let (v0, v1) = (
3028                _mm256_loadu_si256(p as *const __m256i),
3029                _mm256_loadu_si256(p.add(32) as *const __m256i),
3030            );
3031            let lo = _mm256_unpacklo_epi64(v0, v1);
3032            let hi = _mm256_unpackhi_epi64(v0, v1);
3033            [
3034                _mm256_and_si256(lo, m26),
3035                _mm256_and_si256(_mm256_srli_epi64(lo, 26), m26),
3036                _mm256_and_si256(
3037                    _mm256_or_si256(_mm256_srli_epi64(lo, 52), _mm256_slli_epi64(hi, 12)),
3038                    m26,
3039                ),
3040                _mm256_and_si256(_mm256_srli_epi64(hi, 14), m26),
3041                _mm256_or_si256(_mm256_srli_epi64(hi, 40), _mm256_set1_epi64x(1 << 24)),
3042            ]
3043        }
3044    }
3045
3046    /// # Safety
3047    /// The CPU must have AVX2; `data.len()` is a non-zero multiple of 64.
3048    #[target_feature(enable = "avx2")]
3049    pub(super) unsafe fn blocks(h: &mut [u64; 5], rpow: &[[u64; 5]; 4], data: &[u8]) {
3050        // SAFETY: AVX2 is available; every `load` reads 64 bytes at an offset below
3051        // `data.len()`, a multiple of 64.
3052        unsafe {
3053            let mut r4 = [_mm256_setzero_si256(); 5];
3054            let mut s4 = r4;
3055            let mut rf = r4;
3056            let mut sf = r4;
3057            for i in 0..5 {
3058                r4[i] = _mm256_set1_epi64x(rpow[3][i] as i64);
3059                s4[i] = _mm256_set1_epi64x(5 * rpow[3][i] as i64);
3060                // lanes hold blocks 0, 2, 1, 3 of the last group: multipliers r^4, r^2, r^3, r
3061                rf[i] = _mm256_set_epi64x(
3062                    rpow[0][i] as i64,
3063                    rpow[2][i] as i64,
3064                    rpow[1][i] as i64,
3065                    rpow[3][i] as i64,
3066                );
3067                sf[i] = add!(rf[i], _mm256_slli_epi64(rf[i], 2));
3068            }
3069            let p = data.as_ptr();
3070            let mut a = load(p);
3071            for (i, x) in a.iter_mut().enumerate() {
3072                *x = add!(*x, _mm256_set_epi64x(0, 0, 0, h[i] as i64));
3073            }
3074            for g in 1..data.len() / 64 {
3075                a = carry(mul(&a, &r4, &s4));
3076                let m = load(p.add(64 * g));
3077                for i in 0..5 {
3078                    a[i] = add!(a[i], m[i]);
3079                }
3080            }
3081            let d = mul(&a, &rf, &sf);
3082            // horizontal sums (each lane is below 2^58), then a full carry chain
3083            let mut s = [0u64; 5];
3084            for i in 0..5 {
3085                let x = _mm_add_epi64(
3086                    _mm256_castsi256_si128(d[i]),
3087                    _mm256_extracti128_si256(d[i], 1),
3088                );
3089                s[i] = (_mm_cvtsi128_si64(x) as u64).wrapping_add(_mm_extract_epi64(x, 1) as u64);
3090            }
3091            const M26: u64 = 0x3ff_ffff;
3092            for i in 0..4 {
3093                s[i + 1] += s[i] >> 26;
3094                s[i] &= M26;
3095            }
3096            s[0] += (s[4] >> 26) * 5;
3097            s[4] &= M26;
3098            s[1] += s[0] >> 26;
3099            s[0] &= M26;
3100            *h = s;
3101        }
3102    }
3103}
3104
3105// ---- AES-GCM with VAES and VPCLMULQDQ on 256-bit registers (for simoom-tls). Self-contained.
3106
3107/// 0 = not yet detected, 1 = absent, 2 = present.
3108static VAES: AtomicU8 = AtomicU8::new(0);
3109
3110/// True if the CPU has VAES and VPCLMULQDQ with AVX2 (Zen 3, Ice Lake and later).
3111pub fn has_vaes() -> bool {
3112    let mut v = VAES.load(Ordering::Relaxed);
3113    if v == 0 {
3114        #[cfg(target_arch = "x86_64")]
3115        let yes = features() & F_SIMD8 != 0 && features() & F_AES != 0 && features() & F_CLMUL != 0 && {
3116            // CPUID leaf 7: ECX bit 9 is VAES, bit 10 VPCLMULQDQ
3117            let ecx = core::arch::x86_64::__cpuid_count(7, 0).ecx;
3118            ecx & (3 << 9) == 3 << 9
3119        };
3120        #[cfg(not(target_arch = "x86_64"))]
3121        let yes = false;
3122        v = if yes { 2 } else { 1 };
3123        VAES.store(v, Ordering::Relaxed);
3124    }
3125    v == 2 && DISABLED.load(Ordering::Relaxed) == 0
3126}
3127
3128/// AES-GCM bulk encryption or decryption with VAES and VPCLMULQDQ over the longest prefix of
3129/// `data` that is a whole number of 128-byte groups: two blocks per instruction, eight blocks
3130/// per GHASH reduction. Same contract as [`aes_gcm_seal`] and [`aes_gcm_open`] for that
3131/// prefix; returns the number of bytes processed (0 without the instructions: the caller then
3132/// uses those functions for everything).
3133pub fn aes_gcm_vaes(aes: &AesHw, gh: &GhashHw, ctr: &mut [u8; 16], y: &mut [u8; 16], data: &mut [u8], encrypt: bool) -> usize {
3134    let n = data.len() / 128 * 128;
3135    if n == 0 || !has_vaes() {
3136        return 0;
3137    }
3138    #[cfg(target_arch = "x86_64")]
3139    // SAFETY: `has_vaes()` was true (AVX2, VAES, VPCLMULQDQ, AES-NI, PCLMULQDQ, SSSE3); `n` is
3140    // a multiple of 128 within `data`.
3141    unsafe {
3142        x86_vaes::crypt(&aes.rk, aes.nr, &gh.h, ctr, y, &mut data[..n], encrypt)
3143    };
3144    #[cfg(not(target_arch = "x86_64"))]
3145    let _ = (aes, gh, ctr, y, encrypt);
3146    n
3147}
3148
3149#[cfg(target_arch = "x86_64")]
3150mod x86_vaes {
3151    use core::arch::x86_64::*;
3152
3153    /// # Safety
3154    /// The CPU must have AVX2, VAES, VPCLMULQDQ, AES-NI, PCLMULQDQ and SSSE3; `nr` is 10 or 14;
3155    /// `data.len()` is a multiple of 128.
3156    #[target_feature(enable = "avx2,vaes,vpclmulqdq,aes,pclmulqdq,ssse3")]
3157    #[allow(unused_unsafe)]
3158    pub(super) unsafe fn crypt(
3159        rkb: &[[u8; 16]; 15],
3160        nr: usize,
3161        hp: &[[u8; 16]; 8],
3162        ctr: &mut [u8; 16],
3163        y: &mut [u8; 16],
3164        data: &mut [u8],
3165        encrypt: bool,
3166    ) {
3167        // SAFETY: loads and stores of the data are 32 bytes at offsets below `data.len()` (a
3168        // multiple of 128); the key and power arrays are read within bounds (`nr` <= 14).
3169        unsafe {
3170            let r1 = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
3171            let r = _mm256_broadcastsi128_si256(r1);
3172            let mut rk = [_mm256_setzero_si256(); 15];
3173            for k in 0..=nr {
3174                rk[k] = _mm256_broadcastsi128_si256(_mm_loadu_si128(rkb[k].as_ptr() as *const __m128i));
3175            }
3176            // pair j holds blocks 2j (low lane) and 2j + 1, multiplied by H^(8-2j) and H^(7-2j)
3177            let ld = |k: usize| _mm_loadu_si128(hp[k].as_ptr() as *const __m128i);
3178            let mut h = [_mm256_setzero_si256(); 4];
3179            let mut hm = [_mm256_setzero_si256(); 4];
3180            for j in 0..4 {
3181                h[j] = _mm256_set_m128i(ld(6 - 2 * j), ld(7 - 2 * j));
3182                hm[j] = _mm256_xor_si256(h[j], _mm256_shuffle_epi32(h[j], 0x4e));
3183            }
3184            let c0 = _mm_shuffle_epi8(_mm_loadu_si128(ctr.as_ptr() as *const __m128i), r1);
3185            let mut c = _mm256_set_m128i(_mm_add_epi32(c0, _mm_set_epi32(0, 0, 0, 1)), c0);
3186            let two = _mm256_set_epi32(0, 0, 0, 2, 0, 0, 0, 2);
3187            let mut acc = _mm_shuffle_epi8(_mm_loadu_si128(y.as_ptr() as *const __m128i), r1);
3188            let p = data.as_mut_ptr();
3189            for g in 0..data.len() / 128 {
3190                let base = 128 * g;
3191                let mut b = [_mm256_setzero_si256(); 4];
3192                for v in b.iter_mut() {
3193                    *v = _mm256_xor_si256(_mm256_shuffle_epi8(c, r), rk[0]);
3194                    c = _mm256_add_epi32(c, two);
3195                }
3196                let mut x = [_mm256_setzero_si256(); 4];
3197                for (j, v) in x.iter_mut().enumerate() {
3198                    *v = _mm256_loadu_si256(p.add(base + 32 * j) as *const __m256i);
3199                }
3200                for k in &rk[1..nr] {
3201                    for v in b.iter_mut() {
3202                        *v = _mm256_aesenc_epi128(*v, *k);
3203                    }
3204                }
3205                for j in 0..4 {
3206                    b[j] = _mm256_xor_si256(_mm256_aesenclast_epi128(b[j], rk[nr]), x[j]);
3207                    _mm256_storeu_si256(p.add(base + 32 * j) as *mut __m256i, b[j]);
3208                }
3209                // GHASH of the ciphertext: the output when encrypting, the input when decrypting
3210                let ct = if encrypt { b } else { x };
3211                let z = _mm256_setzero_si256();
3212                let (mut lo, mut mid, mut hi) = (z, z, z);
3213                for j in 0..4 {
3214                    let mut a = _mm256_shuffle_epi8(ct[j], r);
3215                    if j == 0 {
3216                        a = _mm256_xor_si256(a, _mm256_inserti128_si256(z, acc, 0));
3217                    }
3218                    lo = _mm256_xor_si256(lo, _mm256_clmulepi64_epi128(a, h[j], 0x00));
3219                    hi = _mm256_xor_si256(hi, _mm256_clmulepi64_epi128(a, h[j], 0x11));
3220                    let a2 = _mm256_xor_si256(a, _mm256_shuffle_epi32(a, 0x4e));
3221                    mid = _mm256_xor_si256(mid, _mm256_clmulepi64_epi128(a2, hm[j], 0x00));
3222                }
3223                let fold = |v: __m256i| _mm_xor_si128(_mm256_castsi256_si128(v), _mm256_extracti128_si256(v, 1));
3224                acc = reduce(fold(lo), fold(mid), fold(hi));
3225            }
3226            let c = _mm256_castsi256_si128(c);
3227            _mm_storeu_si128(ctr.as_mut_ptr() as *mut __m128i, _mm_shuffle_epi8(c, r1));
3228            _mm_storeu_si128(y.as_mut_ptr() as *mut __m128i, _mm_shuffle_epi8(acc, r1));
3229        }
3230    }
3231
3232    /// The Karatsuba (lo, mid, hi) to the reduced GHASH value (the steps of the main kernel).
3233    #[target_feature(enable = "sse2")]
3234    #[allow(unused_unsafe)]
3235    unsafe fn reduce(lo: __m128i, mid: __m128i, hi: __m128i) -> __m128i {
3236        // SAFETY: register-only SSE2 intrinsics.
3237        unsafe {
3238            let (mut lo, mut hi) = (lo, hi);
3239            let mid = _mm_xor_si128(mid, _mm_xor_si128(lo, hi));
3240            lo = _mm_xor_si128(lo, _mm_slli_si128(mid, 8));
3241            hi = _mm_xor_si128(hi, _mm_srli_si128(mid, 8));
3242            let clo = _mm_srli_epi64(lo, 63);
3243            let chi = _mm_srli_epi64(hi, 63);
3244            lo = _mm_or_si128(_mm_slli_epi64(lo, 1), _mm_slli_si128(clo, 8));
3245            hi = _mm_or_si128(_mm_or_si128(_mm_slli_epi64(hi, 1), _mm_slli_si128(chi, 8)), _mm_srli_si128(clo, 8));
3246            let g = _mm_xor_si128(_mm_xor_si128(_mm_slli_epi64(lo, 63), _mm_slli_epi64(lo, 62)), _mm_slli_epi64(lo, 57));
3247            lo = _mm_xor_si128(lo, _mm_slli_si128(g, 8));
3248            let f = _mm_xor_si128(_mm_xor_si128(lo, _mm_srli_epi64(lo, 1)), _mm_xor_si128(_mm_srli_epi64(lo, 2), _mm_srli_epi64(lo, 7)));
3249            hi = _mm_xor_si128(hi, f);
3250            let g = _mm_xor_si128(_mm_xor_si128(_mm_slli_epi64(lo, 63), _mm_slli_epi64(lo, 62)), _mm_slli_epi64(lo, 57));
3251            _mm_xor_si128(hi, _mm_srli_si128(g, 8))
3252        }
3253    }
3254}