1use 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#[inline]
83pub fn has_aes() -> bool {
84 features() & F_AES != 0
85}
86
87#[inline]
89pub fn has_clmul() -> bool {
90 features() & F_CLMUL != 0
91}
92
93#[inline]
95pub fn has_sha256() -> bool {
96 features() & F_SHA256 != 0
97}
98
99pub 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
112pub 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 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 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#[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 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 pub fn encrypt_block(&self, block: &mut [u8; 16]) {
186 #[cfg(target_arch = "x86_64")]
187 unsafe {
189 x86::aes_block(&self.rk, self.nr, block)
190 }
191 #[cfg(target_arch = "aarch64")]
192 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#[derive(Clone)]
206pub struct GhashHw {
207 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 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 unsafe {
228 x86::ghash_init(h, &mut g.h)
229 }
230 #[cfg(target_arch = "aarch64")]
231 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 pub fn update(&self, y: &mut [u8; 16], data: &[u8]) {
243 #[cfg(target_arch = "x86_64")]
244 unsafe {
246 x86::ghash_update(&self.h, y, data)
247 }
248 #[cfg(target_arch = "aarch64")]
249 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
258pub 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 unsafe {
271 x86::gcm(&aes.rk, aes.nr, &gh.h, ctr, y, data, true)
272 }
273 #[cfg(target_arch = "aarch64")]
274 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
282pub 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 unsafe {
293 x86::gcm(&aes.rk, aes.nr, &gh.h, ctr, y, data, false)
294 }
295 #[cfg(target_arch = "aarch64")]
296 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
304pub 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 unsafe {
314 x86::sha256(state, blocks)
315 }
316 #[cfg(target_arch = "aarch64")]
317 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#[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 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 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 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 #[target_feature(enable = "aes,sse2")]
424 pub(super) unsafe fn aes_block(rk: &[[u8; 16]; 15], nr: usize, block: &mut [u8; 16]) {
425 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 #[target_feature(enable = "pclmulqdq,ssse3,sse2")]
440 pub(super) unsafe fn ghash_init(h: &[u8; 16], out: &mut [[u8; 16]; 8]) {
441 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 #[target_feature(enable = "pclmulqdq,ssse3,sse2")]
456 pub(super) unsafe fn ghash_update(hp: &[[u8; 16]; 8], y: &mut [u8; 16], data: &[u8]) {
457 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 #[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 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 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 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 #[target_feature(enable = "sha,ssse3,sse4.1,sse2")]
612 pub(super) unsafe fn sha256(state: &mut [u32; 8], blocks: &[u8]) {
613 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); s1 = _mm_shuffle_epi32(s1, 0x1b); let mut s0 = _mm_alignr_epi8(tmp, s1, 8); s1 = _mm_blend_epi16(s1, tmp, 0xf0); 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); s1 = _mm_shuffle_epi32(s1, 0xb1); s0 = _mm_blend_epi16(tmp, s1, 0xf0); s1 = _mm_alignr_epi8(s1, tmp, 8); _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 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 #[target_feature(enable = "ssse3,sse2")]
764 pub(super) unsafe fn chacha4(st: &mut [u32; 16], data: &mut [u8]) {
765 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 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 #[target_feature(enable = "avx2")]
808 pub(super) unsafe fn chacha8(st: &mut [u32; 16], data: &mut [u8]) {
809 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 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); let hi = _mm256_permute2x128_si256(u[k], u[k + 4], 0x31); 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 #[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 #[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 #[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 #[target_feature(enable = "neon,aes")]
914 pub(super) unsafe fn aes_block(rk: &[[u8; 16]; 15], nr: usize, block: &mut [u8; 16]) {
915 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 #[target_feature(enable = "neon,aes")]
930 #[allow(unused_unsafe)] pub(super) unsafe fn ghash_init(h: &[u8; 16], out: &mut [[u8; 16]; 8]) {
932 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 #[target_feature(enable = "neon,aes")]
949 #[allow(unused_unsafe)] pub(super) unsafe fn ghash_update(hp: &[[u8; 16]; 8], y: &mut [u8; 16], data: &[u8]) {
951 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 #[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 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 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 #[target_feature(enable = "neon,sha2")]
1039 pub(super) unsafe fn sha256(state: &mut [u32; 8], blocks: &[u8]) {
1040 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
1069pub 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 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 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 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 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 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#[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 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
1270static MULX: AtomicU8 = AtomicU8::new(0);
1272
1273#[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#[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 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#[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 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#[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 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#[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 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#[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 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#[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 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#[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 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#[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 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#[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 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#[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 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
2271pub 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 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 unsafe {
2468 core::arch::asm!(
2469 "2:",
2470 "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 "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 fn reference(a: &[u64; 4], b: &[u64; 4], m: &[u64; 4]) -> [u64; 4] {
2592 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 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 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 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 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 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
2894pub 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 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 #[target_feature(enable = "avx2")]
2937 #[inline]
2938 #[allow(unused_unsafe)] unsafe fn mul(a: &[__m256i; 5], r: &[__m256i; 5], s: &[__m256i; 5]) -> [__m256i; 5] {
2940 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 #[target_feature(enable = "avx2")]
2987 #[inline]
2988 #[allow(unused_unsafe)] unsafe fn carry(mut d: [__m256i; 5]) -> [__m256i; 5] {
2990 unsafe {
2992 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 #[target_feature(enable = "avx2")]
3022 #[inline]
3023 unsafe fn load(p: *const u8) -> [__m256i; 5] {
3024 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 #[target_feature(enable = "avx2")]
3049 pub(super) unsafe fn blocks(h: &mut [u64; 5], rpow: &[[u64; 5]; 4], data: &[u8]) {
3050 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 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 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
3105static VAES: AtomicU8 = AtomicU8::new(0);
3109
3110pub 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 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
3128pub 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 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 #[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 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 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 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 #[target_feature(enable = "sse2")]
3234 #[allow(unused_unsafe)]
3235 unsafe fn reduce(lo: __m128i, mid: __m128i, hi: __m128i) -> __m128i {
3236 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}