binius_field/arch/portable/arithmetic/
ghash.rs1use std::{
7 iter::Sum,
8 ops::{Add, AddAssign, Sub, SubAssign},
9};
10
11use bytemuck::TransparentWrapper;
12
13use super::super::univariate_mul_utils_128::{Underlier64bLanes, Underlier128bLanes, bmul64};
14use crate::{
15 BinaryField, Divisible, Ghash128b, WideMul,
16 arithmetic_traits::{MulX, Square},
17 packed_fields::primitive::PackedPrimitiveType,
18 underlier::Underlier,
19};
20
21pub const POLY: u128 = 0x87;
23
24#[inline]
26pub const fn ghash_mul_x(x: u128) -> u128 {
27 let mask = (x >> 127).wrapping_neg();
31
32 (x << 1) ^ (POLY & mask)
33}
34
35#[inline]
44pub fn ghash_mul<U: Underlier128bLanes>(x: U, y: U) -> U {
45 ghash_wide_mul(x, y).reduce()
46}
47
48#[inline]
52pub fn ghash_wide_mul<U: Underlier128bLanes>(x: U, y: U) -> WideGhashProduct<U> {
53 let (x1, x0) = U::split_hi_lo_64(x);
55 let (y1, y0) = U::split_hi_lo_64(y);
56
57 let x0r = x0.reverse_bits_64();
59 let x1r = x1.reverse_bits_64();
60 let x2 = x0 ^ x1;
61 let x2r = x0r ^ x1r;
62
63 let y0r = y0.reverse_bits_64();
64 let y1r = y1.reverse_bits_64();
65 let y2 = y0 ^ y1;
66 let y2r = y0r ^ y1r;
67
68 let z0 = bmul64(y0, x0);
69 let z1 = bmul64(y1, x1);
70 let mut z2 = bmul64(y2, x2);
71
72 let mut z0h = bmul64(y0r, x0r);
73 let mut z1h = bmul64(y1r, x1r);
74 let mut z2h = bmul64(y2r, x2r);
75
76 z2 ^= z0 ^ z1;
77 z2h ^= z0h ^ z1h;
78 z0h = z0h.reverse_bits_64().shr_64(1);
79 z1h = z1h.reverse_bits_64().shr_64(1);
80 z2h = z2h.reverse_bits_64().shr_64(1);
81
82 WideGhashProduct {
83 v0: z0,
84 v1: z0h ^ z2,
85 v2: z1 ^ z2h,
86 v3: z1h,
87 }
88}
89
90#[inline]
91pub fn ghash_square<U: Underlier128bLanes>(x: U) -> U {
92 let (hi, lo) = x.spread_bits_128();
94
95 let (v3, v2) = hi.split_hi_lo_64();
96 let (v1, v0) = lo.split_hi_lo_64();
97
98 reduce_64(v0, v1, v2, v3)
99}
100
101#[inline]
103fn reduce_64<U: Underlier128bLanes>(
104 mut v0: U::U64,
105 mut v1: U::U64,
106 mut v2: U::U64,
107 v3: U::U64,
108) -> U {
109 v1 ^= v3 ^ v3.shl_64(1) ^ v3.shl_64(2) ^ v3.shl_64(7);
111 v2 ^= v3.shr_64(63) ^ v3.shr_64(62) ^ v3.shr_64(57);
112 v0 ^= v2 ^ v2.shl_64(1) ^ v2.shl_64(2) ^ v2.shl_64(7);
113 v1 ^= v2.shr_64(63) ^ v2.shr_64(62) ^ v2.shr_64(57);
114
115 U::join_u64s(v1, v0)
117}
118
119#[derive(Clone, Copy, Default, Debug)]
123pub struct WideGhashProduct<U: Underlier128bLanes> {
124 v0: U::U64,
125 v1: U::U64,
126 v2: U::U64,
127 v3: U::U64,
128}
129
130impl<U: Underlier128bLanes> WideGhashProduct<U> {
131 #[inline]
133 pub fn reduce(self) -> U {
134 reduce_64(self.v0, self.v1, self.v2, self.v3)
135 }
136}
137
138impl<U: Underlier128bLanes> MulX for WideGhashProduct<U> {
139 #[inline]
145 fn mul_x(self) -> Self {
146 Self {
147 v0: self.v0.shl_64(1),
148 v1: self.v1.shl_64(1) ^ self.v0.shr_64(63),
149 v2: self.v2.shl_64(1) ^ self.v1.shr_64(63),
150 v3: self.v3.shl_64(1) ^ self.v2.shr_64(63),
151 }
152 }
153}
154
155impl<U: Underlier128bLanes> Add for WideGhashProduct<U> {
156 type Output = Self;
157
158 #[inline]
159 fn add(self, rhs: Self) -> Self {
160 Self {
161 v0: self.v0 ^ rhs.v0,
162 v1: self.v1 ^ rhs.v1,
163 v2: self.v2 ^ rhs.v2,
164 v3: self.v3 ^ rhs.v3,
165 }
166 }
167}
168
169impl<U: Underlier128bLanes> AddAssign for WideGhashProduct<U> {
170 #[inline]
171 fn add_assign(&mut self, rhs: Self) {
172 self.v0 ^= rhs.v0;
173 self.v1 ^= rhs.v1;
174 self.v2 ^= rhs.v2;
175 self.v3 ^= rhs.v3;
176 }
177}
178
179impl<U: Underlier128bLanes> Sum for WideGhashProduct<U> {
180 #[inline]
181 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
182 iter.fold(Self::default(), |acc, x| acc + x)
183 }
184}
185
186impl<U: Underlier128bLanes> Sub for WideGhashProduct<U> {
188 type Output = Self;
189
190 #[inline]
191 fn sub(self, rhs: Self) -> Self {
192 Self {
193 v0: self.v0 ^ rhs.v0,
194 v1: self.v1 ^ rhs.v1,
195 v2: self.v2 ^ rhs.v2,
196 v3: self.v3 ^ rhs.v3,
197 }
198 }
199}
200
201impl<U: Underlier128bLanes> SubAssign for WideGhashProduct<U> {
202 #[inline]
203 fn sub_assign(&mut self, rhs: Self) {
204 self.v0 ^= rhs.v0;
205 self.v1 ^= rhs.v1;
206 self.v2 ^= rhs.v2;
207 self.v3 ^= rhs.v3;
208 }
209}
210
211#[repr(transparent)]
217#[derive(bytemuck::TransparentWrapper)]
218pub struct GhashWideMul<T>(T);
219
220impl<U: Underlier128bLanes> WideMul for GhashWideMul<PackedPrimitiveType<U, Ghash128b>> {
221 type Output = WideGhashProduct<U>;
222
223 #[inline]
224 fn wide_mul(a: Self, b: Self) -> Self::Output {
225 let a = PackedPrimitiveType::peel(Self::peel(a));
226 let b = PackedPrimitiveType::peel(Self::peel(b));
227 ghash_wide_mul(a, b)
228 }
229
230 #[inline]
231 fn reduce(wide: Self::Output) -> Self {
232 Self::wrap(PackedPrimitiveType::wrap(wide.reduce()))
233 }
234}
235
236#[repr(transparent)]
245#[derive(TransparentWrapper)]
246pub struct GhashMulX<T>(T);
247
248impl<U: Underlier + Divisible<u128>, F: BinaryField> MulX for GhashMulX<PackedPrimitiveType<U, F>> {
249 #[inline]
250 fn mul_x(self) -> Self {
251 let lanes = Divisible::<u128>::value_iter(PackedPrimitiveType::peel(Self::peel(self)))
252 .map(ghash_mul_x);
253
254 Self::wrap(PackedPrimitiveType::wrap(Divisible::<u128>::from_iter(lanes)))
255 }
256}
257
258#[repr(transparent)]
259#[derive(TransparentWrapper)]
260pub struct GhashSoftMul<T>(T);
261
262impl<U: Underlier128bLanes> Square for GhashSoftMul<PackedPrimitiveType<U, Ghash128b>> {
263 #[inline]
264 fn square(self) -> Self {
265 Self::wrap(PackedPrimitiveType::wrap(ghash_square(PackedPrimitiveType::peel(Self::peel(
266 self,
267 )))))
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use proptest::{prelude::any, proptest};
274
275 use super::{super::super::m128::M128, MulX, ghash_mul, ghash_wide_mul};
276
277 proptest! {
282 #[test]
284 fn wide_mul_then_reduce_matches_ghash_mul(a in any::<u128>(), b in any::<u128>()) {
285 let (a, b) = (M128::from(a), M128::from(b));
286 assert_eq!(ghash_wide_mul(a, b).reduce(), ghash_mul(a, b));
287 }
288
289 #[test]
291 fn wide_mul_deferred_accumulation(
292 a1 in any::<u128>(), b1 in any::<u128>(),
293 a2 in any::<u128>(), b2 in any::<u128>(),
294 ) {
295 let (a1, b1) = (M128::from(a1), M128::from(b1));
296 let (a2, b2) = (M128::from(a2), M128::from(b2));
297 let acc = ghash_wide_mul(a1, b1) + ghash_wide_mul(a2, b2);
298 assert_eq!(acc.reduce(), ghash_mul(a1, b1) ^ ghash_mul(a2, b2));
299 }
300
301 #[test]
304 fn mul_x_wide_commutes_with_reduce(a in any::<u128>(), b in any::<u128>()) {
305 let (a, b) = (M128::from(a), M128::from(b));
306 let wide = ghash_wide_mul(a, b);
307 assert_eq!(wide.mul_x().reduce(), ghash_mul(wide.reduce(), M128::from(2u128)));
308 }
309 }
310}