Skip to content

Commit ce60a6e

Browse files
committed
Small speedups for AES-GCM:
* Eliminate a copy in the GHASH(length) computation at the end. * In the tag computation, instead of copying the 128-bit GHASH result into a u8 array and doing a bytewise XOR loop, load it into a u128 and do a vector XOR.
1 parent 0bbcb9e commit ce60a6e

3 files changed

Lines changed: 36 additions & 31 deletions

File tree

graviola/src/low/aarch64/ghash.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,18 @@ impl<'a> Ghash<'a> {
102102
}
103103
}
104104

105-
pub(crate) fn into_bytes(self) -> [u8; 16] {
106-
to_u128(self.current).to_be_bytes()
105+
#[cfg(test)]
106+
fn into_bytes(self) -> [u8; 16] {
107+
self.into_u128().to_be_bytes()
107108
}
108109

109-
fn one_block(&mut self, block: u128) {
110+
pub(crate) fn into_u128(self) -> u128 {
111+
// SAFETY: u128 and uint64x2_t have the same size and meaning of bits
112+
unsafe { mem::transmute(self.current) }
113+
}
114+
115+
// Input the 16 bytes of `block` to the computation.
116+
pub(crate) fn one_block(&mut self, block: u128) {
110117
// SAFETY: this crate requires the `neon` cpu feature
111118
self.current = unsafe { veorq_u64(self.current, from_u128(block)) };
112119
self.current = mul(self.current, self.table.powers[0]);
@@ -326,12 +333,6 @@ fn from_u128(u: u128) -> uint64x2_t {
326333
unsafe { mem::transmute(u) }
327334
}
328335

329-
#[inline]
330-
fn to_u128(u: uint64x2_t) -> u128 {
331-
// SAFETY: u128 and uint64x2_t have the same size and meaning of bits
332-
unsafe { mem::transmute(u) }
333-
}
334-
335336
#[inline]
336337
#[target_feature(enable = "neon")]
337338
// Make a copy of `u` with the bytes reversed, and cast to `uint64x2_t`.

graviola/src/low/x86_64/ghash.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ impl<'a> Ghash<'a> {
206206

207207
for chunk in whole_blocks.by_ref() {
208208
let u = u128::from_be_bytes(chunk.try_into().unwrap());
209-
self.one_block(u128_to_m128i(u));
209+
self.one_block(u);
210210
}
211211

212212
let bytes = whole_blocks.remainder();
@@ -215,7 +215,7 @@ impl<'a> Ghash<'a> {
215215
block[..bytes.len()].copy_from_slice(bytes);
216216

217217
let u = u128::from_be_bytes(block);
218-
self.one_block(u128_to_m128i(u));
218+
self.one_block(u);
219219
}
220220
}
221221

@@ -224,6 +224,11 @@ impl<'a> Ghash<'a> {
224224
unsafe { self._into_bytes() }
225225
}
226226

227+
pub(crate) fn into_u128(self) -> u128 {
228+
// SAFETY: sizeof(u128) == sizeof(__m128i), all bits have same meaning
229+
unsafe { mem::transmute(self.current) }
230+
}
231+
227232
#[target_feature(enable = "sse2,ssse3")]
228233
fn _into_bytes(self) -> [u8; 16] {
229234
let mut out: i128 = 0;
@@ -233,7 +238,9 @@ impl<'a> Ghash<'a> {
233238
out.to_le_bytes()
234239
}
235240

236-
fn one_block(&mut self, block: __m128i) {
241+
// Input the 16 bytes of `block` to the computation.
242+
pub(crate) fn one_block(&mut self, block: u128) {
243+
let block = u128_to_m128i(block);
237244
// SAFETY: this crate requires the `avx` and `pclmulqdq` cpu features
238245
unsafe {
239246
self.current = _mm_xor_si128(self.current, block);

graviola/src/mid/aes_gcm.rs

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,13 @@ impl AesGcm {
6666
// computations. see low::generic::aes_gcm for model version.
6767
aes_gcm::encrypt(&self.key, &mut ghash, &counter, aad, cipher_inout);
6868

69-
let mut lengths = [0u8; 16];
70-
lengths[..8].copy_from_slice(&((aad.len() * 8) as u64).to_be_bytes());
71-
lengths[8..].copy_from_slice(&((cipher_inout.len() * 8) as u64).to_be_bytes());
72-
ghash.add(&lengths);
69+
let lengths: u128 = Self::pack_bit_lengths(aad, cipher_inout);
70+
ghash.one_block(lengths);
7371

74-
let final_xi = ghash.into_bytes();
75-
76-
for ((out, x), e) in tag_out.iter_mut().zip(final_xi.iter()).zip(e_y0.iter()) {
77-
*out = *x ^ *e;
78-
}
72+
let final_xi = ghash.into_u128();
73+
let e = u128::from_be_bytes(e_y0);
74+
let tag = final_xi ^ e;
75+
tag_out.copy_from_slice(&tag.to_be_bytes());
7976
}
8077

8178
/// Decrypts and verifies the given message.
@@ -107,17 +104,11 @@ impl AesGcm {
107104

108105
aes_gcm::decrypt(&self.key, &mut ghash, &counter, aad, cipher_inout);
109106

110-
let mut lengths = [0u8; 16];
111-
lengths[..8].copy_from_slice(&((aad.len() * 8) as u64).to_be_bytes());
112-
lengths[8..].copy_from_slice(&((cipher_inout.len() * 8) as u64).to_be_bytes());
113-
ghash.add(&lengths);
114-
115-
let mut actual_tag = ghash.into_bytes();
116-
for (out, e) in actual_tag.iter_mut().zip(e_y0.iter()) {
117-
*out ^= *e;
118-
}
107+
let lengths: u128 = Self::pack_bit_lengths(aad, cipher_inout);
108+
ghash.one_block(lengths);
119109

120-
if ct_equal(&actual_tag, tag) {
110+
let actual_tag = ghash.into_u128() ^ u128::from_be_bytes(e_y0);
111+
if ct_equal(&actual_tag.to_be_bytes(), tag) {
121112
Ok(())
122113
} else {
123114
// avoid unauthenticated plaintext leak
@@ -126,6 +117,12 @@ impl AesGcm {
126117
}
127118
}
128119

120+
fn pack_bit_lengths(high: &[u8], low: &[u8]) -> u128 {
121+
let lengths = (high.len() * 8) as u128;
122+
let lengths = lengths << 64;
123+
lengths | ((low.len() * 8) as u128)
124+
}
125+
129126
fn nonce_to_y0(&self, nonce: &[u8; 12]) -> [u8; 16] {
130127
let mut y0 = [0u8; 16];
131128
y0[..12].copy_from_slice(nonce);

0 commit comments

Comments
 (0)