/rust/registry/src/index.crates.io-1949cf8c6b5b557f/polyval-0.6.2/src/backend/soft64.rs
Line | Count | Source |
1 | | //! Constant-time software implementation of POLYVAL for 64-bit architectures. |
2 | | //! Adapted from BearSSL's `ghash_ctmul64.c`: |
3 | | //! |
4 | | //! <https://bearssl.org/gitweb/?p=BearSSL;a=blob;f=src/hash/ghash_ctmul64.c;hb=4b6046412> |
5 | | //! |
6 | | //! Copyright (c) 2016 Thomas Pornin <pornin@bolet.org> |
7 | | |
8 | | use core::{ |
9 | | num::Wrapping, |
10 | | ops::{Add, Mul}, |
11 | | }; |
12 | | |
13 | | use universal_hash::{ |
14 | | consts::{U1, U16}, |
15 | | crypto_common::{BlockSizeUser, KeySizeUser, ParBlocksSizeUser}, |
16 | | KeyInit, Reset, UhfBackend, UniversalHash, |
17 | | }; |
18 | | |
19 | | #[cfg(feature = "zeroize")] |
20 | | use zeroize::Zeroize; |
21 | | |
22 | | use crate::{Block, Key, Tag}; |
23 | | |
24 | | /// **POLYVAL**: GHASH-like universal hash over GF(2^128). |
25 | | #[derive(Clone)] |
26 | | pub struct Polyval { |
27 | | /// GF(2^128) field element input blocks are multiplied by |
28 | | h: U64x2, |
29 | | |
30 | | /// Field element representing the computed universal hash |
31 | | s: U64x2, |
32 | | } |
33 | | |
34 | | impl Polyval { |
35 | | /// Initialize POLYVAL with the given `H` field element and initial block |
36 | 0 | pub fn new_with_init_block(h: &Key, init_block: u128) -> Self { |
37 | 0 | Self { |
38 | 0 | h: h.into(), |
39 | 0 | s: init_block.into(), |
40 | 0 | } |
41 | 0 | } |
42 | | } |
43 | | |
44 | | impl KeySizeUser for Polyval { |
45 | | type KeySize = U16; |
46 | | } |
47 | | |
48 | | impl KeyInit for Polyval { |
49 | | /// Initialize POLYVAL with the given `H` field element |
50 | 0 | fn new(h: &Key) -> Self { |
51 | 0 | Self::new_with_init_block(h, 0) |
52 | 0 | } |
53 | | } |
54 | | |
55 | | impl BlockSizeUser for Polyval { |
56 | | type BlockSize = U16; |
57 | | } |
58 | | |
59 | | impl ParBlocksSizeUser for Polyval { |
60 | | type ParBlocksSize = U1; |
61 | | } |
62 | | |
63 | | impl UhfBackend for Polyval { |
64 | 0 | fn proc_block(&mut self, x: &Block) { |
65 | 0 | let x = U64x2::from(x); |
66 | 0 | self.s = (self.s + x) * self.h; |
67 | 0 | } |
68 | | } |
69 | | |
70 | | impl UniversalHash for Polyval { |
71 | 0 | fn update_with_backend( |
72 | 0 | &mut self, |
73 | 0 | f: impl universal_hash::UhfClosure<BlockSize = Self::BlockSize>, |
74 | 0 | ) { |
75 | 0 | f.call(self); |
76 | 0 | } |
77 | | |
78 | | /// Get POLYVAL result (i.e. computed `S` field element) |
79 | 0 | fn finalize(self) -> Tag { |
80 | 0 | let mut block = Block::default(); |
81 | | |
82 | 0 | for (chunk, i) in block.chunks_mut(8).zip(&[self.s.0, self.s.1]) { |
83 | 0 | chunk.copy_from_slice(&i.to_le_bytes()); |
84 | 0 | } |
85 | | |
86 | 0 | block |
87 | 0 | } |
88 | | } |
89 | | |
90 | | impl Reset for Polyval { |
91 | 0 | fn reset(&mut self) { |
92 | 0 | self.s = U64x2::default(); |
93 | 0 | } |
94 | | } |
95 | | |
96 | | #[cfg(feature = "zeroize")] |
97 | | impl Drop for Polyval { |
98 | | fn drop(&mut self) { |
99 | | self.h.zeroize(); |
100 | | self.s.zeroize(); |
101 | | } |
102 | | } |
103 | | |
104 | | /// 2 x `u64` values |
105 | | #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] |
106 | | struct U64x2(u64, u64); |
107 | | |
108 | | impl From<&Block> for U64x2 { |
109 | 0 | fn from(bytes: &Block) -> U64x2 { |
110 | 0 | U64x2( |
111 | 0 | u64::from_le_bytes(bytes[..8].try_into().unwrap()), |
112 | 0 | u64::from_le_bytes(bytes[8..].try_into().unwrap()), |
113 | 0 | ) |
114 | 0 | } |
115 | | } |
116 | | |
117 | | impl From<u128> for U64x2 { |
118 | 0 | fn from(x: u128) -> Self { |
119 | 0 | U64x2((x >> 64) as u64, (x) as u64) |
120 | 0 | } |
121 | | } |
122 | | |
123 | | #[allow(clippy::suspicious_arithmetic_impl)] |
124 | | impl Add for U64x2 { |
125 | | type Output = Self; |
126 | | |
127 | | /// Adds two POLYVAL field elements. |
128 | 0 | fn add(self, rhs: Self) -> Self::Output { |
129 | 0 | U64x2(self.0 ^ rhs.0, self.1 ^ rhs.1) |
130 | 0 | } |
131 | | } |
132 | | |
133 | | #[allow(clippy::suspicious_arithmetic_impl)] |
134 | | impl Mul for U64x2 { |
135 | | type Output = Self; |
136 | | |
137 | | /// Computes carryless POLYVAL multiplication over GF(2^128) in constant time. |
138 | | /// |
139 | | /// Method described at: |
140 | | /// <https://www.bearssl.org/constanttime.html#ghash-for-gcm> |
141 | | /// |
142 | | /// POLYVAL multiplication is effectively the little endian equivalent of |
143 | | /// GHASH multiplication, aside from one small detail described here: |
144 | | /// |
145 | | /// <https://crypto.stackexchange.com/questions/66448/how-does-bearssls-gcm-modular-reduction-work/66462#66462> |
146 | | /// |
147 | | /// > The product of two bit-reversed 128-bit polynomials yields the |
148 | | /// > bit-reversed result over 255 bits, not 256. The BearSSL code ends up |
149 | | /// > with a 256-bit result in zw[], and that value is shifted by one bit, |
150 | | /// > because of that reversed convention issue. Thus, the code must |
151 | | /// > include a shifting step to put it back where it should |
152 | | /// |
153 | | /// This shift is unnecessary for POLYVAL and has been removed. |
154 | 0 | fn mul(self, rhs: Self) -> Self { |
155 | 0 | let h0 = self.0; |
156 | 0 | let h1 = self.1; |
157 | 0 | let h0r = rev64(h0); |
158 | 0 | let h1r = rev64(h1); |
159 | 0 | let h2 = h0 ^ h1; |
160 | 0 | let h2r = h0r ^ h1r; |
161 | | |
162 | 0 | let y0 = rhs.0; |
163 | 0 | let y1 = rhs.1; |
164 | 0 | let y0r = rev64(y0); |
165 | 0 | let y1r = rev64(y1); |
166 | 0 | let y2 = y0 ^ y1; |
167 | 0 | let y2r = y0r ^ y1r; |
168 | 0 | let z0 = bmul64(y0, h0); |
169 | 0 | let z1 = bmul64(y1, h1); |
170 | | |
171 | 0 | let mut z2 = bmul64(y2, h2); |
172 | 0 | let mut z0h = bmul64(y0r, h0r); |
173 | 0 | let mut z1h = bmul64(y1r, h1r); |
174 | 0 | let mut z2h = bmul64(y2r, h2r); |
175 | | |
176 | 0 | z2 ^= z0 ^ z1; |
177 | 0 | z2h ^= z0h ^ z1h; |
178 | 0 | z0h = rev64(z0h) >> 1; |
179 | 0 | z1h = rev64(z1h) >> 1; |
180 | 0 | z2h = rev64(z2h) >> 1; |
181 | | |
182 | 0 | let v0 = z0; |
183 | 0 | let mut v1 = z0h ^ z2; |
184 | 0 | let mut v2 = z1 ^ z2h; |
185 | 0 | let mut v3 = z1h; |
186 | | |
187 | 0 | v2 ^= v0 ^ (v0 >> 1) ^ (v0 >> 2) ^ (v0 >> 7); |
188 | 0 | v1 ^= (v0 << 63) ^ (v0 << 62) ^ (v0 << 57); |
189 | 0 | v3 ^= v1 ^ (v1 >> 1) ^ (v1 >> 2) ^ (v1 >> 7); |
190 | 0 | v2 ^= (v1 << 63) ^ (v1 << 62) ^ (v1 << 57); |
191 | | |
192 | 0 | U64x2(v2, v3) |
193 | 0 | } |
194 | | } |
195 | | |
196 | | #[cfg(feature = "zeroize")] |
197 | | impl Zeroize for U64x2 { |
198 | | fn zeroize(&mut self) { |
199 | | self.0.zeroize(); |
200 | | self.1.zeroize(); |
201 | | } |
202 | | } |
203 | | |
204 | | /// Multiplication in GF(2)[X], truncated to the low 64-bits, with “holes” |
205 | | /// (sequences of zeroes) to avoid carry spilling. |
206 | | /// |
207 | | /// When carries do occur, they wind up in a "hole" and are subsequently masked |
208 | | /// out of the result. |
209 | 0 | fn bmul64(x: u64, y: u64) -> u64 { |
210 | 0 | let x0 = Wrapping(x & 0x1111_1111_1111_1111); |
211 | 0 | let x1 = Wrapping(x & 0x2222_2222_2222_2222); |
212 | 0 | let x2 = Wrapping(x & 0x4444_4444_4444_4444); |
213 | 0 | let x3 = Wrapping(x & 0x8888_8888_8888_8888); |
214 | 0 | let y0 = Wrapping(y & 0x1111_1111_1111_1111); |
215 | 0 | let y1 = Wrapping(y & 0x2222_2222_2222_2222); |
216 | 0 | let y2 = Wrapping(y & 0x4444_4444_4444_4444); |
217 | 0 | let y3 = Wrapping(y & 0x8888_8888_8888_8888); |
218 | | |
219 | 0 | let mut z0 = ((x0 * y0) ^ (x1 * y3) ^ (x2 * y2) ^ (x3 * y1)).0; |
220 | 0 | let mut z1 = ((x0 * y1) ^ (x1 * y0) ^ (x2 * y3) ^ (x3 * y2)).0; |
221 | 0 | let mut z2 = ((x0 * y2) ^ (x1 * y1) ^ (x2 * y0) ^ (x3 * y3)).0; |
222 | 0 | let mut z3 = ((x0 * y3) ^ (x1 * y2) ^ (x2 * y1) ^ (x3 * y0)).0; |
223 | | |
224 | 0 | z0 &= 0x1111_1111_1111_1111; |
225 | 0 | z1 &= 0x2222_2222_2222_2222; |
226 | 0 | z2 &= 0x4444_4444_4444_4444; |
227 | 0 | z3 &= 0x8888_8888_8888_8888; |
228 | | |
229 | 0 | z0 | z1 | z2 | z3 |
230 | 0 | } |
231 | | |
232 | | /// Bit-reverse a `u64` in constant time |
233 | 0 | fn rev64(mut x: u64) -> u64 { |
234 | 0 | x = ((x & 0x5555_5555_5555_5555) << 1) | ((x >> 1) & 0x5555_5555_5555_5555); |
235 | 0 | x = ((x & 0x3333_3333_3333_3333) << 2) | ((x >> 2) & 0x3333_3333_3333_3333); |
236 | 0 | x = ((x & 0x0f0f_0f0f_0f0f_0f0f) << 4) | ((x >> 4) & 0x0f0f_0f0f_0f0f_0f0f); |
237 | 0 | x = ((x & 0x00ff_00ff_00ff_00ff) << 8) | ((x >> 8) & 0x00ff_00ff_00ff_00ff); |
238 | 0 | x = ((x & 0xffff_0000_ffff) << 16) | ((x >> 16) & 0xffff_0000_ffff); |
239 | 0 | (x << 32) | (x >> 32) |
240 | 0 | } |