Skip to main content

polyval/backend/
intrinsics.rs

1//! Support for CPU feature autodetection with a portable pure Rust fallback.
2
3use crate::{Block, Key, ParBlocks, Tag, field_element::FieldElement};
4
5#[cfg_attr(target_arch = "aarch64", path = "intrinsics/armv8.rs")]
6#[cfg_attr(
7    any(target_arch = "x86", target_arch = "x86_64"),
8    path = "intrinsics/x86.rs"
9)]
10mod intrinsics_impl;
11use intrinsics_impl::InitToken;
12
13/// State of a POLYVAL hash operation.
14#[derive(Clone)]
15pub(crate) struct State {
16    /// Expanded key.
17    expanded_key: ExpandedKey,
18
19    /// Accumulator for the POLYVAL computation in progress (a.k.a. `y`).
20    acc: FieldElement,
21
22    /// CPU feature detection initialization token.
23    init_token: InitToken,
24}
25
26impl State {
27    pub(crate) fn new(h: &Key) -> Self {
28        let (init_token, has_intrinsics) = InitToken::init_get();
29
30        let expanded_key = if has_intrinsics {
31            // SAFETY: we have just used CPU feature detection to ensure intrinsics are available
32            unsafe { intrinsics_impl::expand_key(&h.0) }
33        } else {
34            // Fallback to software-only implementation
35            // TODO(tarcieri): use `ExpandedKey` space to store powers-of-H
36            ExpandedKey {
37                h1: FieldElement::from(*h),
38                ..Default::default()
39            }
40        };
41
42        let y = FieldElement::default();
43
44        Self {
45            expanded_key,
46            acc: y,
47            init_token,
48        }
49    }
50
51    pub(crate) fn proc_block(&mut self, block: &Block) {
52        self.acc = if self.has_intrinsics() {
53            // SAFETY: we have just used CPU feature detection to ensure intrinsics are available
54            unsafe { intrinsics_impl::proc_block(&self.expanded_key, self.acc, block) }
55        } else {
56            (self.acc + block.into()) * self.expanded_key.h1
57        };
58    }
59
60    pub(crate) fn proc_par_blocks(&mut self, par_blocks: &ParBlocks) {
61        if self.has_intrinsics() {
62            // SAFETY: we have just used CPU feature detection to ensure intrinsics are available
63            self.acc = unsafe {
64                intrinsics_impl::proc_par_blocks(&self.expanded_key, self.acc, par_blocks)
65            };
66        } else {
67            // TODO(tarcieri): use powers-of-H since we have the space in `ExpandedKey`
68            for block in par_blocks {
69                self.proc_block(block);
70            }
71        }
72    }
73
74    pub(crate) fn finalize(&self) -> Tag {
75        self.acc.into()
76    }
77
78    pub(crate) fn reset(&mut self) {
79        self.acc = FieldElement::default();
80    }
81
82    #[inline]
83    fn has_intrinsics(&self) -> bool {
84        self.init_token.get()
85    }
86}
87
88/// Precomputed key material for POLYVAL using R/F algorithm
89///
90/// Stores H and D values for each power, where D = swap(H) ⊕ (H0 × P1)
91#[derive(Clone, Default)]
92pub(crate) struct ExpandedKey {
93    /// H^1 packed as [h1_hi : h1_lo]
94    h1: FieldElement,
95    /// D^1 = computed from H^1
96    d1: FieldElement,
97    /// H^2
98    h2: FieldElement,
99    /// D^2
100    d2: FieldElement,
101    /// H^3
102    h3: FieldElement,
103    /// D^3
104    d3: FieldElement,
105    /// H^4
106    h4: FieldElement,
107    /// D^4
108    d4: FieldElement,
109}