Botan 3.13.0
Crypto and TLS for C&
ghash.cpp
Go to the documentation of this file.
1/*
2* GCM GHASH
3* (C) 2013,2015,2017 Jack Lloyd
4* (C) 2016 Daniel Neus, Rohde & Schwarz Cybersecurity
5* (C) 2024 René Meusel, Rohde & Schwarz Cybersecurity
6*
7* Botan is released under the Simplified BSD License (see license.txt)
8*/
9
10#include <botan/internal/ghash.h>
11
12#include <botan/exceptn.h>
13#include <botan/internal/ct_utils.h>
14#include <botan/internal/loadstor.h>
15
16#if defined(BOTAN_HAS_CPUID)
17 #include <botan/internal/cpuid.h>
18#endif
19
20namespace Botan {
21
22std::string GHASH::provider() const {
23#if defined(BOTAN_HAS_GHASH_AVX512_CLMUL)
25 return *feat;
26 }
27#endif
28
29#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
30 if(auto feat = CPUID::check(CPUID::Feature::HW_CLMUL)) {
31 return *feat;
32 }
33#endif
34
35#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM)
37 return *feat;
38 }
39#endif
40
41 return "base";
42}
43
44void GHASH::ghash_multiply(std::span<uint8_t, GCM_BS> x, std::span<const uint8_t> input, size_t blocks) {
45 BOTAN_ASSERT_NOMSG(input.size() % GCM_BS == 0);
46
47#if defined(BOTAN_HAS_GHASH_AVX512_CLMUL)
49 BOTAN_ASSERT_NOMSG(!m_H_pow.empty());
50 return ghash_multiply_avx512_clmul(x.data(), m_H_pow.data(), input.data(), blocks);
51 }
52#endif
53
54#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
56 BOTAN_ASSERT_NOMSG(!m_H_pow.empty());
57 return ghash_multiply_cpu(x.data(), m_H_pow, input.data(), blocks);
58 }
59#endif
60
61#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM)
63 return ghash_multiply_vperm(x.data(), m_HM.data(), input.data(), blocks);
64 }
65#endif
66
67 ghash_multiply_base(x, m_HM, input, blocks);
68}
69
70void GHASH::ghash_multiply_base(std::span<uint8_t, GCM_BS> x,
72 std::span<const uint8_t> input,
73 size_t blocks) {
74 auto scope = CT::scoped_poison(x);
75
77
78 BufferSlicer in(input);
79 for(size_t b = 0; b != blocks; ++b) {
80 const auto I = load_be<std::array<uint64_t, 2>>(in.take<GCM_BS>());
81 X[0] ^= I[0];
82 X[1] ^= I[1];
83
84 std::array<uint64_t, 2> Z{};
85
86 for(size_t i = 0; i != 64; ++i) {
87 const auto X0MASK = CT::Mask<uint64_t>::expand_top_bit(X[0]);
88 const auto X1MASK = CT::Mask<uint64_t>::expand_top_bit(X[1]);
89
90 X[0] <<= 1;
91 X[1] <<= 1;
92
93 Z[0] = X0MASK.select(Z[0] ^ HM[4 * i], Z[0]);
94 Z[1] = X0MASK.select(Z[1] ^ HM[4 * i + 1], Z[1]);
95
96 Z[0] = X1MASK.select(Z[0] ^ HM[4 * i + 2], Z[0]);
97 Z[1] = X1MASK.select(Z[1] ^ HM[4 * i + 3], Z[1]);
98 }
99
100 X[0] = Z[0];
101 X[1] = Z[1];
102 }
103
104 store_be(x, X);
105}
106
108 return !m_HM.empty() || !m_H_pow.empty();
109}
110
111void GHASH::key_schedule(std::span<const uint8_t> key) {
112 m_H_ad = {0};
113 m_ad_len = 0;
114 m_text_len = 0;
115
116 BOTAN_ASSERT_NOMSG(key.size() == GCM_BS);
117
118#if defined(BOTAN_HAS_GHASH_AVX512_CLMUL)
120 zap(m_HM);
121 if(m_H_pow.size() != 32) {
122 m_H_pow.resize(32);
123 }
124 ghash_precompute_avx512_clmul(key.data(), m_H_pow.data());
125 // m_HM left empty
126 return;
127 }
128#endif
129
130#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
132 zap(m_HM);
133 ghash_precompute_cpu(key.data(), m_H_pow);
134 // m_HM left empty
135 return;
136 }
137#endif
138
139 ghash_precompute_base(key.first<GCM_BS>(), m_HM);
140}
141
142void GHASH::ghash_precompute_base(std::span<const uint8_t, GCM_BS> key, secure_vector<uint64_t>& HM) {
143 auto H = load_be<std::array<uint64_t, 2>>(key);
144
145 const uint64_t R = 0xE100000000000000;
146
147 if(HM.size() != 256) {
148 HM.resize(256);
149 }
150
151 // precompute the multiples of H
152 for(size_t i = 0; i != 2; ++i) {
153 for(size_t j = 0; j != 64; ++j) {
154 /*
155 we interleave H^1, H^65, H^2, H^66, H3, H67, H4, H68
156 to make indexing nicer in the multiplication code
157 */
158 HM[4 * j + 2 * i] = H[0];
159 HM[4 * j + 2 * i + 1] = H[1];
160
161 // GCM's bit ops are reversed so we carry out of the bottom
162 const uint64_t carry = CT::Mask<uint64_t>::expand(H[1] & 1).if_set_return(R);
163 H[1] = (H[1] >> 1) | (H[0] << 63);
164 H[0] = (H[0] >> 1) ^ carry;
165 }
166 }
167}
168
169void GHASH::start(std::span<const uint8_t> nonce) {
170 BOTAN_ARG_CHECK(nonce.size() == 16, "GHASH requires a 128-bit nonce");
171 auto& n = m_nonce.emplace();
172 copy_mem(n, nonce);
173 copy_mem(m_ghash, m_H_ad);
174 m_buffer.clear();
175 m_text_len = 0;
176}
177
178void GHASH::set_associated_data(std::span<const uint8_t> input) {
179 BOTAN_STATE_CHECK(!m_nonce);
180
182 m_H_ad = {0};
183 ghash_update(m_H_ad, input);
184 ghash_zeropad(m_H_ad);
185 m_ad_len = input.size();
186}
187
189 // This should only be called in GMAC context
190 BOTAN_STATE_CHECK(m_text_len == 0);
192 m_H_ad = {0};
193 m_ad_len = 0;
194}
195
196void GHASH::update_associated_data(std::span<const uint8_t> ad) {
198 ghash_update(m_ghash, ad);
199 m_ad_len += ad.size();
200}
201
202void GHASH::update(std::span<const uint8_t> input) {
204 BOTAN_STATE_CHECK(m_nonce);
205 ghash_update(m_ghash, input);
206 m_text_len += input.size();
207
208 // NIST SP 800-38D limits plaintext/ciphertext to 2^39 - 256 bits
209 constexpr uint64_t GHASH_MAX_BYTES = (((static_cast<uint64_t>(1) << 39)) - 256) / 8;
210 if(m_text_len > GHASH_MAX_BYTES) {
211 throw Invalid_State("GCM message length limit exceeded");
212 }
213}
214
215void GHASH::final(std::span<uint8_t> mac) {
216 BOTAN_ARG_CHECK(!mac.empty() && mac.size() <= GCM_BS, "GHASH output length");
217 BOTAN_STATE_CHECK(m_nonce);
219
220 ghash_zeropad(m_ghash);
221 ghash_final_block(m_ghash, m_ad_len, m_text_len);
222
223 xor_buf(mac, std::span{m_ghash}.first(mac.size()), std::span{*m_nonce}.first(mac.size()));
224
225 secure_scrub_memory(m_ghash);
226 m_text_len = 0;
227 m_nonce.reset();
228}
229
230void GHASH::nonce_hash(std::span<uint8_t, GCM_BS> y0, std::span<const uint8_t> nonce) {
232 BOTAN_STATE_CHECK(!m_nonce);
233
234 ghash_update(y0, nonce);
235 ghash_zeropad(y0);
236 ghash_final_block(y0, 0, nonce.size());
237}
238
240 zap(m_HM);
241 zap(m_H_pow);
242 m_H_ad = {0};
243 m_ad_len = 0;
244 this->reset_state();
245}
246
248 secure_scrub_memory(m_ghash);
249 if(m_nonce) {
250 secure_scrub_memory(m_nonce.value());
251 m_nonce.reset();
252 }
253 m_buffer.clear();
254 m_text_len = 0;
255}
256
257void GHASH::ghash_update(std::span<uint8_t, GCM_BS> x, std::span<const uint8_t> input) {
258 BufferSlicer in(input);
259 while(!in.empty()) {
260 if(const auto one_block = m_buffer.handle_unaligned_data(in)) {
261 ghash_multiply(x, one_block.value(), 1);
262 }
263
264 if(m_buffer.in_alignment()) {
265 const auto [aligned_data, full_blocks] = m_buffer.aligned_data_to_process(in);
266 if(full_blocks > 0) {
267 ghash_multiply(x, aligned_data, full_blocks);
268 }
269 }
270 }
271 BOTAN_ASSERT_NOMSG(in.empty());
272}
273
274void GHASH::ghash_zeropad(std::span<uint8_t, GCM_BS> x) {
275 if(!m_buffer.in_alignment()) {
276 m_buffer.fill_up_with_zeros();
277 ghash_multiply(x, m_buffer.consume(), 1);
278 }
279}
280
281void GHASH::ghash_final_block(std::span<uint8_t, GCM_BS> x, uint64_t ad_len, uint64_t text_len) {
282 BOTAN_STATE_CHECK(m_buffer.in_alignment());
283 const auto final_block = store_be(8 * ad_len, 8 * text_len);
284 ghash_multiply(x, final_block, 1);
285}
286
287} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:49
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
std::tuple< std::span< const uint8_t >, size_t > aligned_data_to_process(BufferSlicer &slicer) const
std::optional< std::span< const T > > handle_unaligned_data(BufferSlicer &slicer)
static std::optional< std::string > check(CPUID::Feature feat)
Definition cpuid.h:67
static bool has(CPUID::Feature feat)
Definition cpuid.h:94
static constexpr Mask< T > expand(T v)
Definition ct_utils.h:392
static constexpr Mask< T > expand_top_bit(T v)
Definition ct_utils.h:415
void update_associated_data(std::span< const uint8_t > ad)
Incremental update of associated data used in the GMAC use-case.
Definition ghash.cpp:196
std::string provider() const
Definition ghash.cpp:22
void final(std::span< uint8_t > out)
Definition ghash.cpp:215
void nonce_hash(std::span< uint8_t, GCM_BS > y0, std::span< const uint8_t > nonce)
Hashing of non-default length nonce values for both GCM and GMAC use-cases.
Definition ghash.cpp:230
void reset_associated_data()
Reset the AAD state without resetting the key (used in GMAC::final_result).
Definition ghash.cpp:188
void reset_state()
Definition ghash.cpp:247
void clear() override
Definition ghash.cpp:239
void update(std::span< const uint8_t > in)
Definition ghash.cpp:202
void start(std::span< const uint8_t > nonce)
Definition ghash.cpp:169
bool has_keying_material() const override
Definition ghash.cpp:107
void set_associated_data(std::span< const uint8_t > ad)
Monolithic setting of associated data usid in the GCM use-case.
Definition ghash.cpp:178
void assert_key_material_set() const
Definition sym_algo.h:180
constexpr auto scoped_poison(const Ts &... xs)
Definition ct_utils.h:222
void zap(std::vector< T, Alloc > &vec)
Definition secmem.h:261
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
void secure_scrub_memory(void *ptr, size_t n)
Definition mem_utils.cpp:25
void carry(int64_t &h0, int64_t &h1)
constexpr void xor_buf(ranges::contiguous_output_range< uint8_t > auto &&out, ranges::contiguous_range< uint8_t > auto &&in)
Definition mem_ops.h:403
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:745
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:504