Coverage Report

Created: 2023-06-07 07:01

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