Coverage Report

Created: 2023-12-08 07:00

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