poly1305/backend/
autodetect.rs

1//! Autodetection support for AVX2 CPU intrinsics on x86 CPUs, with fallback
2//! to the "soft" backend when it's unavailable.
3
4use universal_hash::{consts::U16, crypto_common::BlockSizeUser, UniversalHash};
5
6use crate::{backend, Block, Key, Tag};
7use core::mem::ManuallyDrop;
8
9cpufeatures::new!(avx2_cpuid, "avx2");
10
11pub struct State {
12    inner: Inner,
13    token: avx2_cpuid::InitToken,
14}
15
16union Inner {
17    avx2: ManuallyDrop<backend::avx2::State>,
18    soft: ManuallyDrop<backend::soft::State>,
19}
20
21impl BlockSizeUser for State {
22    type BlockSize = U16;
23}
24
25impl State {
26    /// Initialize Poly1305 [`State`] with the given key
27    #[inline]
28    pub(crate) fn new(key: &Key) -> State {
29        let (token, avx2_present) = avx2_cpuid::init_get();
30
31        let inner = if avx2_present {
32            Inner {
33                avx2: ManuallyDrop::new(backend::avx2::State::new(key)),
34            }
35        } else {
36            Inner {
37                soft: ManuallyDrop::new(backend::soft::State::new(key)),
38            }
39        };
40
41        Self { inner, token }
42    }
43
44    /// Compute a Poly1305 block
45    #[inline]
46    pub(crate) fn compute_block(&mut self, block: &Block, partial: bool) {
47        if self.token.get() {
48            unsafe { (*self.inner.avx2).compute_block(block, partial) }
49        } else {
50            unsafe { (*self.inner.soft).compute_block(block, partial) }
51        }
52    }
53}
54
55impl UniversalHash for State {
56    fn update_with_backend(
57        &mut self,
58        f: impl universal_hash::UhfClosure<BlockSize = Self::BlockSize>,
59    ) {
60        if self.token.get() {
61            unsafe { f.call(&mut *self.inner.avx2) }
62        } else {
63            unsafe { f.call(&mut *self.inner.soft) }
64        }
65    }
66
67    /// Finalize output producing a [`Tag`]
68    #[inline]
69    fn finalize(mut self) -> Tag {
70        if self.token.get() {
71            unsafe { (*self.inner.avx2).finalize() }
72        } else {
73            unsafe { (*self.inner.soft).finalize_mut() }
74        }
75    }
76}
77
78impl Clone for State {
79    fn clone(&self) -> Self {
80        let inner = if self.token.get() {
81            Inner {
82                avx2: ManuallyDrop::new(unsafe { (*self.inner.avx2).clone() }),
83            }
84        } else {
85            Inner {
86                soft: ManuallyDrop::new(unsafe { (*self.inner.soft).clone() }),
87            }
88        };
89
90        Self {
91            inner,
92            token: self.token,
93        }
94    }
95}
96
97#[cfg(feature = "zeroize")]
98impl Drop for State {
99    fn drop(&mut self) {
100        use zeroize::Zeroize;
101        const SIZE: usize = core::mem::size_of::<State>();
102        let state = unsafe { &mut *(self as *mut State as *mut [u8; SIZE]) };
103        state.zeroize();
104    }
105}