|
3 | 3 |
|
4 | 4 | use crate::Error; |
5 | 5 | use crate::low::ghash::{Ghash, GhashTable}; |
6 | | -use crate::low::{AesKey, Entry, aes_gcm, ct_equal}; |
| 6 | +use crate::low::{AesKey, Entry, aes_gcm, ct_equal_u128}; |
7 | 7 |
|
8 | 8 | /// An AES-GCM key. |
9 | 9 | /// |
@@ -66,16 +66,13 @@ impl AesGcm { |
66 | 66 | // computations. see low::generic::aes_gcm for model version. |
67 | 67 | aes_gcm::encrypt(&self.key, &mut ghash, &counter, aad, cipher_inout); |
68 | 68 |
|
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); |
73 | 71 |
|
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()); |
79 | 76 | } |
80 | 77 |
|
81 | 78 | /// Decrypts and verifies the given message. |
@@ -107,23 +104,33 @@ impl AesGcm { |
107 | 104 |
|
108 | 105 | aes_gcm::decrypt(&self.key, &mut ghash, &counter, aad, cipher_inout); |
109 | 106 |
|
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; |
| 107 | + let lengths: u128 = Self::pack_bit_lengths(aad, cipher_inout); |
| 108 | + ghash.one_block(lengths); |
| 109 | + |
| 110 | + let actual_tag = ghash.into_u128() ^ u128::from_be_bytes(e_y0); |
| 111 | + let tag: Result<[u8; 16], _> = tag.try_into(); |
| 112 | + match tag { |
| 113 | + Ok(tag) => { |
| 114 | + // TODO add a constant-time u128 comparison function |
| 115 | + if ct_equal_u128(u128::from_be_bytes(tag), actual_tag) { |
| 116 | + Ok(()) |
| 117 | + } else { |
| 118 | + // avoid unauthenticated plaintext leak |
| 119 | + cipher_inout.fill(0x00); |
| 120 | + Err(Error::DecryptFailed) |
| 121 | + } |
| 122 | + }, |
| 123 | + Err(_) => { |
| 124 | + cipher_inout.fill(0x00); |
| 125 | + Err(Error::DecryptFailed) |
| 126 | + } |
118 | 127 | } |
| 128 | + } |
119 | 129 |
|
120 | | - if ct_equal(&actual_tag, tag) { |
121 | | - Ok(()) |
122 | | - } else { |
123 | | - // avoid unauthenticated plaintext leak |
124 | | - cipher_inout.fill(0x00); |
125 | | - Err(Error::DecryptFailed) |
126 | | - } |
| 130 | + fn pack_bit_lengths(high: &[u8], low: &[u8]) -> u128 { |
| 131 | + let lengths = (high.len() * 8) as u128; |
| 132 | + let lengths = lengths << 64; |
| 133 | + lengths | ((low.len() * 8) as u128) |
127 | 134 | } |
128 | 135 |
|
129 | 136 | fn nonce_to_y0(&self, nonce: &[u8; 12]) -> [u8; 16] { |
|
0 commit comments