Botan 3.3.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*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#include <botan/internal/ghash.h>
10
11#include <botan/exceptn.h>
12#include <botan/internal/cpuid.h>
13#include <botan/internal/ct_utils.h>
14#include <botan/internal/loadstor.h>
15
16#include <array>
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(secure_vector<uint8_t>& x, std::span<const uint8_t> input, size_t blocks) {
37#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
39 BOTAN_ASSERT_NOMSG(!m_H_pow.empty());
40 return ghash_multiply_cpu(x.data(), m_H_pow.data(), input.data(), blocks);
41 }
42#endif
43
44#if defined(BOTAN_HAS_GHASH_CLMUL_VPERM)
45 if(CPUID::has_vperm()) {
46 return ghash_multiply_vperm(x.data(), m_HM.data(), input.data(), blocks);
47 }
48#endif
49
50 CT::poison(x.data(), x.size());
51
52 const uint64_t ALL_BITS = 0xFFFFFFFFFFFFFFFF;
53
54 uint64_t X[2] = {load_be<uint64_t>(x.data(), 0), load_be<uint64_t>(x.data(), 1)};
55
56 for(size_t b = 0; b != blocks; ++b) {
57 X[0] ^= load_be<uint64_t>(input.data(), 2 * b);
58 X[1] ^= load_be<uint64_t>(input.data(), 2 * b + 1);
59
60 uint64_t Z[2] = {0, 0};
61
62 for(size_t i = 0; i != 64; ++i) {
63 const uint64_t X0MASK = (ALL_BITS + (X[0] >> 63)) ^ ALL_BITS;
64 const uint64_t X1MASK = (ALL_BITS + (X[1] >> 63)) ^ ALL_BITS;
65
66 X[0] <<= 1;
67 X[1] <<= 1;
68
69 Z[0] ^= m_HM[4 * i] & X0MASK;
70 Z[1] ^= m_HM[4 * i + 1] & X0MASK;
71 Z[0] ^= m_HM[4 * i + 2] & X1MASK;
72 Z[1] ^= m_HM[4 * i + 3] & X1MASK;
73 }
74
75 X[0] = Z[0];
76 X[1] = Z[1];
77 }
78
79 store_be<uint64_t>(x.data(), X[0], X[1]);
80 CT::unpoison(x.data(), x.size());
81}
82
83void GHASH::ghash_update(secure_vector<uint8_t>& ghash, std::span<const uint8_t> input) {
84 assert_key_material_set(!m_H.empty());
85
86 /*
87 This assumes if less than block size input then we're just on the
88 final block and should pad with zeros
89 */
90
91 const size_t full_blocks = input.size() / GCM_BS;
92 const size_t final_bytes = input.size() - (full_blocks * GCM_BS);
93
94 if(full_blocks > 0) {
95 ghash_multiply(ghash, input.first(full_blocks * GCM_BS), full_blocks);
96 }
97
98 if(final_bytes) {
99 uint8_t last_block[GCM_BS] = {0};
100 copy_mem(last_block, input.subspan(full_blocks * GCM_BS).data(), final_bytes);
101 ghash_multiply(ghash, last_block, 1);
102 secure_scrub_memory(last_block, final_bytes);
103 }
104}
105
107 return !m_ghash.empty();
108}
109
110void GHASH::key_schedule(std::span<const uint8_t> key) {
111 m_H.assign(key.begin(), key.end()); // TODO: C++23 - std::vector<>::assign_range()
112 m_H_ad.resize(GCM_BS);
113 m_ad_len = 0;
114 m_text_len = 0;
115
116 uint64_t H0 = load_be<uint64_t>(m_H.data(), 0);
117 uint64_t H1 = load_be<uint64_t>(m_H.data(), 1);
118
119 const uint64_t R = 0xE100000000000000;
120
121 m_HM.resize(256);
122
123 // precompute the multiples of H
124 for(size_t i = 0; i != 2; ++i) {
125 for(size_t j = 0; j != 64; ++j) {
126 /*
127 we interleave H^1, H^65, H^2, H^66, H3, H67, H4, H68
128 to make indexing nicer in the multiplication code
129 */
130 m_HM[4 * j + 2 * i] = H0;
131 m_HM[4 * j + 2 * i + 1] = H1;
132
133 // GCM's bit ops are reversed so we carry out of the bottom
134 const uint64_t carry = R * (H1 & 1);
135 H1 = (H1 >> 1) | (H0 << 63);
136 H0 = (H0 >> 1) ^ carry;
137 }
138 }
139
140#if defined(BOTAN_HAS_GHASH_CLMUL_CPU)
142 m_H_pow.resize(8);
143 ghash_precompute_cpu(m_H.data(), m_H_pow.data());
144 }
145#endif
146}
147
148void GHASH::start(std::span<const uint8_t> nonce) {
149 BOTAN_ARG_CHECK(nonce.size() == 16, "GHASH requires a 128-bit nonce");
150 m_nonce.assign(nonce.begin(), nonce.end()); // TODO: C++23: assign_range
151 m_ghash = m_H_ad;
152}
153
154void GHASH::set_associated_data(std::span<const uint8_t> input) {
155 if(m_ghash.empty() == false) {
156 throw Invalid_State("Too late to set AD in GHASH");
157 }
158
159 zeroise(m_H_ad);
160
161 ghash_update(m_H_ad, input);
162 m_ad_len = input.size();
163}
164
165void GHASH::update_associated_data(std::span<const uint8_t> ad) {
167 m_ad_len += ad.size();
168 ghash_update(m_ghash, ad);
169}
170
171void GHASH::update(std::span<const uint8_t> input) {
173 m_text_len += input.size();
174 ghash_update(m_ghash, input);
175}
176
177void GHASH::add_final_block(secure_vector<uint8_t>& hash, size_t ad_len, size_t text_len) {
178 /*
179 * stack buffer is fine here since the text len is public
180 * and the length of the AD is probably not sensitive either.
181 */
182 std::array<uint8_t, GCM_BS> final_block;
183
184 const uint64_t ad_bits = 8 * ad_len;
185 const uint64_t text_bits = 8 * text_len;
186 store_be(final_block, ad_bits, text_bits);
187 ghash_update(hash, final_block);
188}
189
190void GHASH::final(std::span<uint8_t> mac) {
191 BOTAN_ARG_CHECK(!mac.empty() && mac.size() <= 16, "GHASH output length");
192
194 add_final_block(m_ghash, m_ad_len, m_text_len);
195
196 for(size_t i = 0; i != mac.size(); ++i) {
197 mac[i] = m_ghash[i] ^ m_nonce[i];
198 }
199
200 m_ghash.clear();
201 m_text_len = 0;
202}
203
204void GHASH::nonce_hash(secure_vector<uint8_t>& y0, std::span<const uint8_t> nonce) {
205 BOTAN_ASSERT(m_ghash.empty(), "nonce_hash called during wrong time");
206
207 ghash_update(y0, nonce);
208 add_final_block(y0, 0, nonce.size());
209}
210
212 zap(m_H);
213 zap(m_HM);
214 reset();
215}
216
218 zeroise(m_H_ad);
219 m_ghash.clear();
220 m_nonce.clear();
221 m_text_len = m_ad_len = 0;
222}
223
224} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:59
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
#define BOTAN_ASSERT(expr, assertion_made)
Definition assert.h:50
static bool has_vperm()
Definition cpuid.h:277
static bool has_carryless_multiply()
Definition cpuid.h:308
void update_associated_data(std::span< const uint8_t > ad)
Definition ghash.cpp:165
std::string provider() const
Definition ghash.cpp:20
void nonce_hash(secure_vector< uint8_t > &y0, std::span< const uint8_t > nonce)
Definition ghash.cpp:204
void ghash_update(secure_vector< uint8_t > &x, std::span< const uint8_t > input)
Definition ghash.cpp:83
void final(std::span< uint8_t > out)
Definition ghash.cpp:190
void add_final_block(secure_vector< uint8_t > &x, size_t ad_len, size_t pt_len)
Definition ghash.cpp:177
void clear() override
Definition ghash.cpp:211
void update(std::span< const uint8_t > in)
Definition ghash.cpp:171
void reset()
Definition ghash.cpp:217
void start(std::span< const uint8_t > nonce)
Definition ghash.cpp:148
bool has_keying_material() const override
Definition ghash.cpp:106
void set_associated_data(std::span< const uint8_t > ad)
Definition ghash.cpp:154
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
void poison(const T *p, size_t n)
Definition ct_utils.h:46
void unpoison(const T *p, size_t n)
Definition ct_utils.h:55
void zeroise(std::vector< T, Alloc > &vec)
Definition secmem.h:108
constexpr void store_be(T in, OutR &&out_range)
Definition loadstor.h:358
void zap(std::vector< T, Alloc > &vec)
Definition secmem.h:117
void secure_scrub_memory(void *ptr, size_t n)
Definition os_utils.cpp:87
void carry(int64_t &h0, int64_t &h1)
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:146