Skip to main content

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::{UhfClosure, UniversalHash, common::BlockSizeUser, consts::U16};
5
6use crate::{Block, Key, Tag, backend};
7use core::mem::ManuallyDrop;
8
9cpufeatures::new!(avx2_cpuid, "avx2");
10
11pub(crate) 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(&mut self, f: impl UhfClosure<BlockSize = Self::BlockSize>) {
57        if self.token.get() {
58            unsafe { f.call(&mut *self.inner.avx2) }
59        } else {
60            unsafe { f.call(&mut *self.inner.soft) }
61        }
62    }
63
64    /// Finalize output producing a [`Tag`]
65    #[inline]
66    fn finalize(mut self) -> Tag {
67        if self.token.get() {
68            unsafe { (*self.inner.avx2).finalize() }
69        } else {
70            unsafe { (*self.inner.soft).finalize_mut() }
71        }
72    }
73}
74
75impl Clone for State {
76    fn clone(&self) -> Self {
77        let inner = if self.token.get() {
78            Inner {
79                avx2: ManuallyDrop::new(unsafe { (*self.inner.avx2).clone() }),
80            }
81        } else {
82            Inner {
83                soft: ManuallyDrop::new(unsafe { (*self.inner.soft).clone() }),
84            }
85        };
86
87        Self {
88            inner,
89            token: self.token,
90        }
91    }
92}
93
94#[cfg(feature = "zeroize")]
95impl Drop for State {
96    fn drop(&mut self) {
97        use zeroize::Zeroize;
98        const SIZE: usize = size_of::<State>();
99        let state = unsafe { &mut *core::ptr::from_mut::<State>(self).cast::<[u8; SIZE]>() };
100        state.zeroize();
101    }
102}