Botan 3.7.1
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/cpuid.h>
14#include <botan/internal/ct_utils.h>
15#include <botan/internal/loadstor.h>
16#include <botan/internal/stl_util.h>
17
18namespace Botan {
19
20std::string GHASH::provider() const {
21#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
23 return "clmul";
24 }
25#endif
26
27#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM)
28 if(CPUID::has_vperm()) {
29 return "vperm";
30 }
31#endif
32
33 return "base";
34}
35
36void GHASH::ghash_multiply(std::span<uint8_t, GCM_BS> x, std::span<const uint8_t> input, size_t blocks) {
37 BOTAN_ASSERT_NOMSG(input.size() % GCM_BS == 0);
38
39#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
41 BOTAN_ASSERT_NOMSG(!m_H_pow.empty());
42 return ghash_multiply_cpu(x.data(), m_H_pow.data(), input.data(), blocks);
43 }
44#endif
45
46#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM)
47 if(CPUID::has_vperm()) {
48 return ghash_multiply_vperm(x.data(), m_HM.data(), input.data(), blocks);
49 }
50#endif
51
52 auto scope = CT::scoped_poison(x);
53
55
56 BufferSlicer in(input);
57 for(size_t b = 0; b != blocks; ++b) {
58 const auto I = load_be<std::array<uint64_t, 2>>(in.take<GCM_BS>());
59 X[0] ^= I[0];
60 X[1] ^= I[1];
61
62 std::array<uint64_t, 2> Z{};
63
64 for(size_t i = 0; i != 64; ++i) {
65 const auto X0MASK = CT::Mask<uint64_t>::expand_top_bit(X[0]);
66 const auto X1MASK = CT::Mask<uint64_t>::expand_top_bit(X[1]);
67
68 X[0] <<= 1;
69 X[1] <<= 1;
70
71 Z[0] = X0MASK.select(Z[0] ^ m_HM[4 * i], Z[0]);
72 Z[1] = X0MASK.select(Z[1] ^ m_HM[4 * i + 1], Z[1]);
73
74 Z[0] = X1MASK.select(Z[0] ^ m_HM[4 * i + 2], Z[0]);
75 Z[1] = X1MASK.select(Z[1] ^ m_HM[4 * i + 3], Z[1]);
76 }
77
78 X[0] = Z[0];
79 X[1] = Z[1];
80 }
81
82 store_be(x, X);
83}
84
86 return !m_HM.empty();
87}
88
89void GHASH::key_schedule(std::span<const uint8_t> key) {
90 m_H_ad = {0};
91 m_ad_len = 0;
92 m_text_len = 0;
93
94 BOTAN_ASSERT_NOMSG(key.size() == GCM_BS);
95 auto H = load_be<std::array<uint64_t, 2>>(key.first<GCM_BS>());
96
97 const uint64_t R = 0xE100000000000000;
98
99 m_HM.resize(256);
100
101 // precompute the multiples of H
102 for(size_t i = 0; i != 2; ++i) {
103 for(size_t j = 0; j != 64; ++j) {
104 /*
105 we interleave H^1, H^65, H^2, H^66, H3, H67, H4, H68
106 to make indexing nicer in the multiplication code
107 */
108 m_HM[4 * j + 2 * i] = H[0];
109 m_HM[4 * j + 2 * i + 1] = H[1];
110
111 // GCM's bit ops are reversed so we carry out of the bottom
112 const uint64_t carry = CT::Mask<uint64_t>::expand(H[1] & 1).if_set_return(R);
113 H[1] = (H[1] >> 1) | (H[0] << 63);
114 H[0] = (H[0] >> 1) ^ carry;
115 }
116 }
117
118#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
120 m_H_pow.resize(8);
121 ghash_precompute_cpu(key.data(), m_H_pow.data());
122 }
123#endif
124}
125
126void GHASH::start(std::span<const uint8_t> nonce) {
127 BOTAN_ARG_CHECK(nonce.size() == 16, "GHASH requires a 128-bit nonce");
128 auto& n = m_nonce.emplace();
129 copy_mem(n, nonce);
130 copy_mem(m_ghash, m_H_ad);
131}
132
133void GHASH::set_associated_data(std::span<const uint8_t> input) {
134 BOTAN_STATE_CHECK(!m_nonce);
135
137 m_H_ad = {0};
138 ghash_update(m_H_ad, input);
139 ghash_zeropad(m_H_ad);
140 m_ad_len = input.size();
141}
142
143void GHASH::update_associated_data(std::span<const uint8_t> ad) {
145 ghash_update(m_ghash, ad);
146 m_ad_len += ad.size();
147}
148
149void GHASH::update(std::span<const uint8_t> input) {
151 BOTAN_STATE_CHECK(m_nonce);
152 ghash_update(m_ghash, input);
153 m_text_len += input.size();
154}
155
156void GHASH::final(std::span<uint8_t> mac) {
157 BOTAN_ARG_CHECK(!mac.empty() && mac.size() <= GCM_BS, "GHASH output length");
158 BOTAN_STATE_CHECK(m_nonce);
160
161 ghash_zeropad(m_ghash);
162 ghash_final_block(m_ghash, m_ad_len, m_text_len);
163
164 xor_buf(mac, std::span{m_ghash}.first(mac.size()), std::span{*m_nonce}.first(mac.size()));
165
166 secure_scrub_memory(m_ghash);
167 m_text_len = 0;
168 m_nonce.reset();
169}
170
171void GHASH::nonce_hash(secure_vector<uint8_t>& y0, std::span<const uint8_t> nonce) {
173 BOTAN_STATE_CHECK(!m_nonce);
174 BOTAN_ARG_CHECK(y0.size() == GCM_BS, "ghash state must be 16 bytes");
175
176 auto sy0 = std::span<uint8_t, GCM_BS>{y0};
177 ghash_update(sy0, nonce);
178 ghash_zeropad(sy0);
179 ghash_final_block(sy0, 0, nonce.size());
180}
181
183 zap(m_HM);
184 reset();
185}
186
188 m_H_ad = {0};
189 secure_scrub_memory(m_ghash);
190 if(m_nonce) {
191 secure_scrub_memory(m_nonce.value());
192 m_nonce.reset();
193 }
194 m_buffer.clear();
195 m_text_len = m_ad_len = 0;
196}
197
198void GHASH::ghash_update(std::span<uint8_t, GCM_BS> x, std::span<const uint8_t> input) {
199 BufferSlicer in(input);
200 while(!in.empty()) {
201 if(const auto one_block = m_buffer.handle_unaligned_data(in)) {
202 ghash_multiply(x, one_block.value(), 1);
203 }
204
205 if(m_buffer.in_alignment()) {
206 const auto [aligned_data, full_blocks] = m_buffer.aligned_data_to_process(in);
207 if(full_blocks > 0) {
208 ghash_multiply(x, aligned_data, full_blocks);
209 }
210 }
211 }
212 BOTAN_ASSERT_NOMSG(in.empty());
213}
214
215void GHASH::ghash_zeropad(std::span<uint8_t, GCM_BS> x) {
216 if(!m_buffer.in_alignment()) {
217 m_buffer.fill_up_with_zeros();
218 ghash_multiply(x, m_buffer.consume(), 1);
219 }
220}
221
222void GHASH::ghash_final_block(std::span<uint8_t, GCM_BS> x, uint64_t ad_len, uint64_t text_len) {
224 const auto final_block = store_be(8 * ad_len, 8 * text_len);
225 ghash_multiply(x, final_block, 1);
226}
227
228} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_STATE_CHECK(expr)
Definition assert.h:41
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
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)
std::span< const T > consume()
static bool has_vperm()
Definition cpuid.h:335
static bool has_carryless_multiply()
Definition cpuid.h:366
static constexpr Mask< T > expand(T v)
Definition ct_utils.h:408
static constexpr Mask< T > expand_top_bit(T v)
Definition ct_utils.h:426
void update_associated_data(std::span< const uint8_t > ad)
Incremental update of associated data used in the GMAC use-case.
Definition ghash.cpp:143
std::string provider() const
Definition ghash.cpp:20
void nonce_hash(secure_vector< uint8_t > &y0, std::span< const uint8_t > nonce)
Hashing of non-default length nonce values for both GCM and GMAC use-cases.
Definition ghash.cpp:171
void final(std::span< uint8_t > out)
Definition ghash.cpp:156
void clear() override
Definition ghash.cpp:182
void update(std::span< const uint8_t > in)
Definition ghash.cpp:149
void reset()
Definition ghash.cpp:187
void start(std::span< const uint8_t > nonce)
Definition ghash.cpp:126
bool has_keying_material() const override
Definition ghash.cpp:85
void set_associated_data(std::span< const uint8_t > ad)
Monolithic setting of associated data usid in the GCM use-case.
Definition ghash.cpp:133
void assert_key_material_set() const
Definition sym_algo.h:139
FE_25519 Z
Definition ge.cpp:27
FE_25519 X
Definition ge.cpp:25
constexpr auto scoped_poison(const Ts &... xs)
Definition ct_utils.h:216
void zap(std::vector< T, Alloc > &vec)
Definition secmem.h:117
void secure_scrub_memory(void *ptr, size_t n)
Definition mem_utils.cpp:19
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:342
const SIMD_8x32 & b
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:147
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:773
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:530