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, Block, Key};
5use core::mem::ManuallyDrop;
6use universal_hash::{consts::U16, NewUniversalHash, Output, UniversalHash};
7
8#[cfg(all(target_arch = "aarch64", feature = "armv8"))]
9use super::pmull as intrinsics;
10
11#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
12use super::clmul as intrinsics;
13
14#[cfg(all(target_arch = "aarch64", feature = "armv8"))]
15cpufeatures::new!(mul_intrinsics, "aes"); // `aes` implies PMULL
16
17#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
18cpufeatures::new!(mul_intrinsics, "pclmulqdq", "sse4.1");
19
20/// **POLYVAL**: GHASH-like universal hash over GF(2^128).
21pub struct Polyval {
22    inner: Inner,
23    token: mul_intrinsics::InitToken,
24}
25
26union Inner {
27    intrinsics: ManuallyDrop<intrinsics::Polyval>,
28    soft: ManuallyDrop<soft::Polyval>,
29}
30
31impl NewUniversalHash for Polyval {
32    type KeySize = U16;
33
34    /// Initialize POLYVAL with the given `H` field element
35    fn new(h: &Key) -> Self {
36        let (token, has_intrinsics) = mul_intrinsics::init_get();
37
38        let inner = if has_intrinsics {
39            Inner {
40                intrinsics: ManuallyDrop::new(intrinsics::Polyval::new(h)),
41            }
42        } else {
43            Inner {
44                soft: ManuallyDrop::new(soft::Polyval::new(h)),
45            }
46        };
47
48        Self { inner, token }
49    }
50}
51
52impl UniversalHash for Polyval {
53    type BlockSize = U16;
54
55    /// Input a field element `X` to be authenticated
56    #[inline]
57    fn update(&mut self, x: &Block) {
58        if self.token.get() {
59            unsafe { (*self.inner.intrinsics).update(x) }
60        } else {
61            unsafe { (*self.inner.soft).update(x) }
62        }
63    }
64
65    /// Reset internal state
66    fn reset(&mut self) {
67        if self.token.get() {
68            unsafe { (*self.inner.intrinsics).reset() }
69        } else {
70            unsafe { (*self.inner.soft).reset() }
71        }
72    }
73
74    /// Get POLYVAL result (i.e. computed `S` field element)
75    fn finalize(self) -> Output<Self> {
76        let output_bytes = if self.token.get() {
77            unsafe {
78                ManuallyDrop::into_inner(self.inner.intrinsics)
79                    .finalize()
80                    .into_bytes()
81            }
82        } else {
83            unsafe {
84                ManuallyDrop::into_inner(self.inner.soft)
85                    .finalize()
86                    .into_bytes()
87            }
88        };
89
90        Output::new(output_bytes)
91    }
92}
93
94impl Clone for Polyval {
95    fn clone(&self) -> Self {
96        let inner = if self.token.get() {
97            Inner {
98                intrinsics: ManuallyDrop::new(unsafe { (*self.inner.intrinsics).clone() }),
99            }
100        } else {
101            Inner {
102                soft: ManuallyDrop::new(unsafe { (*self.inner.soft).clone() }),
103            }
104        };
105
106        Self {
107            inner,
108            token: self.token,
109        }
110    }
111}