polyval/backend/
autodetect.rs

1//! Autodetection for CPU intrinsics, with fallback to the "soft" backend when
2//! they are unavailable.
3
4use crate::{backend::soft, Key, Tag};
5use core::mem::ManuallyDrop;
6use universal_hash::{
7    consts::U16,
8    crypto_common::{BlockSizeUser, KeySizeUser},
9    KeyInit, Reset, UniversalHash,
10};
11
12#[cfg(all(target_arch = "aarch64", polyval_armv8))]
13use super::pmull as intrinsics;
14
15#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
16use super::clmul as intrinsics;
17
18#[cfg(all(target_arch = "aarch64", polyval_armv8))]
19cpufeatures::new!(mul_intrinsics, "aes"); // `aes` implies PMULL
20
21#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
22cpufeatures::new!(mul_intrinsics, "pclmulqdq");
23
24/// **POLYVAL**: GHASH-like universal hash over GF(2^128).
25pub struct Polyval {
26    inner: Inner,
27    token: mul_intrinsics::InitToken,
28}
29
30union Inner {
31    intrinsics: ManuallyDrop<intrinsics::Polyval>,
32    soft: ManuallyDrop<soft::Polyval>,
33}
34
35impl KeySizeUser for Polyval {
36    type KeySize = U16;
37}
38
39impl Polyval {
40    /// Initialize POLYVAL with the given `H` field element and initial block
41    pub fn new_with_init_block(h: &Key, init_block: u128) -> Self {
42        let (token, has_intrinsics) = mul_intrinsics::init_get();
43
44        let inner = if has_intrinsics {
45            Inner {
46                intrinsics: ManuallyDrop::new(intrinsics::Polyval::new_with_init_block(
47                    h, init_block,
48                )),
49            }
50        } else {
51            Inner {
52                soft: ManuallyDrop::new(soft::Polyval::new_with_init_block(h, init_block)),
53            }
54        };
55
56        Self { inner, token }
57    }
58}
59
60impl KeyInit for Polyval {
61    /// Initialize POLYVAL with the given `H` field element
62    fn new(h: &Key) -> Self {
63        Self::new_with_init_block(h, 0)
64    }
65}
66
67impl BlockSizeUser for Polyval {
68    type BlockSize = U16;
69}
70
71impl UniversalHash for Polyval {
72    fn update_with_backend(
73        &mut self,
74        f: impl universal_hash::UhfClosure<BlockSize = Self::BlockSize>,
75    ) {
76        unsafe {
77            if self.token.get() {
78                f.call(&mut *self.inner.intrinsics)
79            } else {
80                f.call(&mut *self.inner.soft)
81            }
82        }
83    }
84
85    /// Get POLYVAL result (i.e. computed `S` field element)
86    fn finalize(self) -> Tag {
87        unsafe {
88            if self.token.get() {
89                ManuallyDrop::into_inner(self.inner.intrinsics).finalize()
90            } else {
91                ManuallyDrop::into_inner(self.inner.soft).finalize()
92            }
93        }
94    }
95}
96
97impl Clone for Polyval {
98    fn clone(&self) -> Self {
99        let inner = if self.token.get() {
100            Inner {
101                intrinsics: ManuallyDrop::new(unsafe { (*self.inner.intrinsics).clone() }),
102            }
103        } else {
104            Inner {
105                soft: ManuallyDrop::new(unsafe { (*self.inner.soft).clone() }),
106            }
107        };
108
109        Self {
110            inner,
111            token: self.token,
112        }
113    }
114}
115
116impl Reset for Polyval {
117    fn reset(&mut self) {
118        if self.token.get() {
119            unsafe { (*self.inner.intrinsics).reset() }
120        } else {
121            unsafe { (*self.inner.soft).reset() }
122        }
123    }
124}