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