/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 |