1#![cfg_attr(docsrs, feature(doc_cfg))]
6#![cfg_attr(not(any(test, doctest, feature = "std")), no_std)]
7#![cfg_attr(not(any(feature = "std", test)), deny(clippy::std_instead_of_core))]
8
9mod backend;
10pub mod ghash;
11mod poly;
12
13use core::slice;
14
15pub use subtle::Choice;
16use subtle::ConstantTimeEq;
17
18pub use crate::poly::{Polyval, PolyvalLite};
19
20pub const KEY_SIZE: usize = 16;
22
23pub const BLOCK_SIZE: usize = 16;
25
26#[derive(Copy, Clone, Debug)]
28pub struct Tag(pub(crate) [u8; 16]);
29
30impl ConstantTimeEq for Tag {
31 #[inline]
32 fn ct_eq(&self, other: &Self) -> Choice {
33 self.0.ct_eq(&other.0)
34 }
35}
36
37impl From<Tag> for [u8; 16] {
38 #[inline]
39 fn from(tag: Tag) -> Self {
40 tag.0
41 }
42}
43
44#[inline(always)]
46const fn as_blocks(blocks: &[u8]) -> (&[[u8; BLOCK_SIZE]], &[u8]) {
47 #[allow(clippy::arithmetic_side_effects)]
48 let len_rounded_down = (blocks.len() / BLOCK_SIZE) * BLOCK_SIZE;
49 let (head, tail) = unsafe { blocks.split_at_unchecked(len_rounded_down) };
53 let new_len = head.len() / BLOCK_SIZE;
54 let head = unsafe { slice::from_raw_parts(head.as_ptr().cast(), new_len) };
57 (head, tail)
58}
59
60macro_rules! impl_state {
61 ($name:ident, $endian:ident) => {
62 #[derive(Clone, Default)]
64 pub struct $name {
65 pub(crate) y: $crate::backend::FieldElement,
66 }
67
68 #[cfg(feature = "zeroize")]
69 #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
70 impl ::zeroize::ZeroizeOnDrop for $name {}
71
72 impl Drop for $name {
73 #[inline]
74 fn drop(&mut self) {
75 cfg_if::cfg_if! {
76 if #[cfg(feature = "zeroize")] {
77 ::zeroize::Zeroize::zeroize(&mut self.y);
78 } else {
79 self.y = ::core::hint::black_box(Default::default());
80 }
81 }
82 }
83 }
84
85 impl ::core::fmt::Debug for $name {
86 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
87 f.debug_struct(stringify!($name)).finish_non_exhaustive()
88 }
89 }
90 };
91}
92pub(crate) use impl_state;
93
94macro_rules! impl_hash {
95 (
96 $(#[$meta:meta])*
97 $vis:vis struct $name:ident($inner:ty);
98 ) => {
99 $(#[$meta])*
100 #[derive(Clone)]
101 $vis struct $name($inner);
102
103 impl $name {
104 #[inline]
108 pub fn new(key: &[u8; $crate::KEY_SIZE]) -> Option<Self> {
109 use ::subtle::ConstantTimeEq;
110
111 if bool::from(key.ct_eq(&[0; $crate::KEY_SIZE])) {
112 None
113 } else {
114 Some(Self::new_unchecked(key))
115 }
116 }
117
118 #[inline]
127 pub fn new_unchecked(key: &[u8; $crate::KEY_SIZE]) -> Self {
128 Self(<$inner>::new(key))
129 }
130
131 #[inline]
133 pub fn update_block(&mut self, block: &[u8; $crate::BLOCK_SIZE]) {
134 self.0.update_block(block);
135 }
136
137 #[inline]
139 pub fn update_blocks(&mut self, blocks: &[[u8; $crate::BLOCK_SIZE]]) {
140 self.0.update_blocks(blocks);
141 }
142
143 #[inline]
149 pub fn update_padded(&mut self, blocks: &[u8]) {
150 let (head, tail) = $crate::as_blocks(blocks);
151 if !head.is_empty() {
152 self.update_blocks(head);
153 }
154 if !tail.is_empty() {
155 let mut block = [0u8; $crate::BLOCK_SIZE];
156 #[allow(
157 clippy::indexing_slicing,
158 reason = "The compiler can prove the slice is in bounds."
159 )]
160 block[..tail.len()].copy_from_slice(tail);
161 self.update_block(&block);
162 }
163 }
164
165 #[inline]
167 pub fn tag(self) -> $crate::Tag {
168 $crate::Tag(self.0.tag())
169 }
170
171 #[inline]
174 pub fn verify(self, expected_tag: &$crate::Tag) -> ::subtle::Choice {
175 ::subtle::ConstantTimeEq::ct_eq(&self.tag(), expected_tag)
176 }
177
178 #[inline]
180 #[cfg(feature = "experimental")]
181 #[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
182 pub fn export(&self) -> State {
183 let y = self.0.export();
184 State { y }
185 }
186
187 #[inline]
189 #[cfg(feature = "experimental")]
190 #[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
191 pub fn reset(&mut self, state: &State) {
192 self.0.reset(state.y)
193 }
194
195 #[inline]
198 #[cfg(feature = "experimental")]
199 #[cfg_attr(docsrs, doc(cfg(feature = "experimental")))]
200 pub fn current_tag(&self) -> $crate::Tag {
201 $crate::Tag(self.0.tag())
202 }
203 }
204
205 #[cfg(feature = "zeroize")]
206 #[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
207 impl ::zeroize::ZeroizeOnDrop for $name {}
208
209 impl Drop for $name {
210 #[inline]
211 fn drop(&mut self) {
212 #[cfg(feature = "zeroize")]
213 unsafe {
216 zeroize::zeroize_flat_type(self);
217 }
218 }
219 }
220
221 impl ::core::fmt::Debug for $name {
222 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
223 f.debug_struct(stringify!($name)).finish_non_exhaustive()
224 }
225 }
226 };
227}
228pub(crate) use impl_hash;