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 BinaryField128bGhash as GhashB128, WideMul,
16 arch::PackedPrimitiveType,
17 arithmetic_traits::{MulXWide, Square},
18};
19
20#[inline]
29pub fn ghash_mul<U: Underlier128bLanes>(x: U, y: U) -> U {
30 ghash_wide_mul(x, y).reduce()
31}
32
33#[inline]
37pub fn ghash_wide_mul<U: Underlier128bLanes>(x: U, y: U) -> WideGhashProduct<U> {
38 let (x1, x0) = U::split_hi_lo_64(x);
40 let (y1, y0) = U::split_hi_lo_64(y);
41
42 let x0r = x0.reverse_bits_64();
44 let x1r = x1.reverse_bits_64();
45 let x2 = x0 ^ x1;
46 let x2r = x0r ^ x1r;
47
48 let y0r = y0.reverse_bits_64();
49 let y1r = y1.reverse_bits_64();
50 let y2 = y0 ^ y1;
51 let y2r = y0r ^ y1r;
52
53 let z0 = bmul64(y0, x0);
54 let z1 = bmul64(y1, x1);
55 let mut z2 = bmul64(y2, x2);
56
57 let mut z0h = bmul64(y0r, x0r);
58 let mut z1h = bmul64(y1r, x1r);
59 let mut z2h = bmul64(y2r, x2r);
60
61 z2 ^= z0 ^ z1;
62 z2h ^= z0h ^ z1h;
63 z0h = z0h.reverse_bits_64().shr_64(1);
64 z1h = z1h.reverse_bits_64().shr_64(1);
65 z2h = z2h.reverse_bits_64().shr_64(1);
66
67 WideGhashProduct {
68 v0: z0,
69 v1: z0h ^ z2,
70 v2: z1 ^ z2h,
71 v3: z1h,
72 }
73}
74
75#[inline]
76pub fn ghash_square<U: Underlier128bLanes>(x: U) -> U {
77 let (hi, lo) = x.spread_bits_128();
79
80 let (v3, v2) = hi.split_hi_lo_64();
81 let (v1, v0) = lo.split_hi_lo_64();
82
83 reduce_64(v0, v1, v2, v3)
84}
85
86#[inline]
88fn reduce_64<U: Underlier128bLanes>(
89 mut v0: U::U64,
90 mut v1: U::U64,
91 mut v2: U::U64,
92 v3: U::U64,
93) -> U {
94 v1 ^= v3 ^ v3.shl_64(1) ^ v3.shl_64(2) ^ v3.shl_64(7);
96 v2 ^= v3.shr_64(63) ^ v3.shr_64(62) ^ v3.shr_64(57);
97 v0 ^= v2 ^ v2.shl_64(1) ^ v2.shl_64(2) ^ v2.shl_64(7);
98 v1 ^= v2.shr_64(63) ^ v2.shr_64(62) ^ v2.shr_64(57);
99
100 U::join_u64s(v1, v0)
102}
103
104#[derive(Clone, Copy, Default, Debug)]
108pub struct WideGhashProduct<U: Underlier128bLanes> {
109 v0: U::U64,
110 v1: U::U64,
111 v2: U::U64,
112 v3: U::U64,
113}
114
115impl<U: Underlier128bLanes> WideGhashProduct<U> {
116 #[inline]
118 pub fn reduce(self) -> U {
119 reduce_64(self.v0, self.v1, self.v2, self.v3)
120 }
121}
122
123impl<U: Underlier128bLanes> MulXWide for WideGhashProduct<U> {
124 #[inline]
130 fn mul_x_wide(self) -> Self {
131 Self {
132 v0: self.v0.shl_64(1),
133 v1: self.v1.shl_64(1) ^ self.v0.shr_64(63),
134 v2: self.v2.shl_64(1) ^ self.v1.shr_64(63),
135 v3: self.v3.shl_64(1) ^ self.v2.shr_64(63),
136 }
137 }
138}
139
140impl<U: Underlier128bLanes> Add for WideGhashProduct<U> {
141 type Output = Self;
142
143 #[inline]
144 fn add(self, rhs: Self) -> Self {
145 Self {
146 v0: self.v0 ^ rhs.v0,
147 v1: self.v1 ^ rhs.v1,
148 v2: self.v2 ^ rhs.v2,
149 v3: self.v3 ^ rhs.v3,
150 }
151 }
152}
153
154impl<U: Underlier128bLanes> AddAssign for WideGhashProduct<U> {
155 #[inline]
156 fn add_assign(&mut self, rhs: Self) {
157 self.v0 ^= rhs.v0;
158 self.v1 ^= rhs.v1;
159 self.v2 ^= rhs.v2;
160 self.v3 ^= rhs.v3;
161 }
162}
163
164impl<U: Underlier128bLanes> Sum for WideGhashProduct<U> {
165 #[inline]
166 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
167 iter.fold(Self::default(), |acc, x| acc + x)
168 }
169}
170
171impl<U: Underlier128bLanes> Sub for WideGhashProduct<U> {
173 type Output = Self;
174
175 #[inline]
176 fn sub(self, rhs: Self) -> Self {
177 Self {
178 v0: self.v0 ^ rhs.v0,
179 v1: self.v1 ^ rhs.v1,
180 v2: self.v2 ^ rhs.v2,
181 v3: self.v3 ^ rhs.v3,
182 }
183 }
184}
185
186impl<U: Underlier128bLanes> SubAssign for WideGhashProduct<U> {
187 #[inline]
188 fn sub_assign(&mut self, rhs: Self) {
189 self.v0 ^= rhs.v0;
190 self.v1 ^= rhs.v1;
191 self.v2 ^= rhs.v2;
192 self.v3 ^= rhs.v3;
193 }
194}
195
196#[repr(transparent)]
202#[derive(bytemuck::TransparentWrapper)]
203pub struct GhashWideMul<T>(T);
204
205impl<U: Underlier128bLanes> WideMul for GhashWideMul<PackedPrimitiveType<U, GhashB128>> {
206 type Output = WideGhashProduct<U>;
207
208 #[inline]
209 fn wide_mul(a: Self, b: Self) -> Self::Output {
210 let a = PackedPrimitiveType::peel(Self::peel(a));
211 let b = PackedPrimitiveType::peel(Self::peel(b));
212 ghash_wide_mul(a, b)
213 }
214
215 #[inline]
216 fn reduce(wide: Self::Output) -> Self {
217 Self::wrap(PackedPrimitiveType::wrap(wide.reduce()))
218 }
219}
220
221#[repr(transparent)]
227#[derive(TransparentWrapper)]
228pub struct GhashSoftMul<T>(T);
229
230impl<U: Underlier128bLanes> Square for GhashSoftMul<PackedPrimitiveType<U, GhashB128>> {
231 #[inline]
232 fn square(self) -> Self {
233 Self::wrap(PackedPrimitiveType::wrap(ghash_square(PackedPrimitiveType::peel(Self::peel(
234 self,
235 )))))
236 }
237}
238
239#[cfg(test)]
240mod tests {
241 use proptest::{prelude::any, proptest};
242
243 use super::{super::super::m128::M128, MulXWide, ghash_mul, ghash_wide_mul};
244
245 proptest! {
250 #[test]
252 fn wide_mul_then_reduce_matches_ghash_mul(a in any::<u128>(), b in any::<u128>()) {
253 let (a, b) = (M128::from(a), M128::from(b));
254 assert_eq!(ghash_wide_mul(a, b).reduce(), ghash_mul(a, b));
255 }
256
257 #[test]
259 fn wide_mul_deferred_accumulation(
260 a1 in any::<u128>(), b1 in any::<u128>(),
261 a2 in any::<u128>(), b2 in any::<u128>(),
262 ) {
263 let (a1, b1) = (M128::from(a1), M128::from(b1));
264 let (a2, b2) = (M128::from(a2), M128::from(b2));
265 let acc = ghash_wide_mul(a1, b1) + ghash_wide_mul(a2, b2);
266 assert_eq!(acc.reduce(), ghash_mul(a1, b1) ^ ghash_mul(a2, b2));
267 }
268
269 #[test]
272 fn mul_x_wide_commutes_with_reduce(a in any::<u128>(), b in any::<u128>()) {
273 let (a, b) = (M128::from(a), M128::from(b));
274 let wide = ghash_wide_mul(a, b);
275 assert_eq!(wide.mul_x_wide().reduce(), ghash_mul(wide.reduce(), M128::from(2u128)));
276 }
277 }
278}