Coverage Report

Created: 2026-06-09 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/botan/src/lib/modes/aead/gcm/gcm.cpp
Line
Count
Source
1
/*
2
* GCM Mode Encryption
3
* (C) 2013,2015 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/gcm.h>
10
11
#include <botan/block_cipher.h>
12
#include <botan/exceptn.h>
13
#include <botan/mem_ops.h>
14
#include <botan/internal/ct_utils.h>
15
#include <botan/internal/ctr.h>
16
#include <botan/internal/fmt.h>
17
#include <botan/internal/ghash.h>
18
#include <botan/internal/int_utils.h>
19
#include <array>
20
21
namespace Botan {
22
23
/*
24
* GCM_Mode Constructor
25
*/
26
GCM_Mode::GCM_Mode(std::unique_ptr<BlockCipher> cipher, size_t tag_size) :
27
99
      m_tag_size(tag_size), m_cipher_name(cipher->name()) {
28
99
   if(cipher->block_size() != GCM_BS) {
29
0
      throw Invalid_Argument("Invalid block cipher for GCM");
30
0
   }
31
32
   /* We allow any of the values 128, 120, 112, 104, or 96 bits as a tag size */
33
   /* 64 bit tag is still supported but deprecated and will be removed in the future */
34
99
   if(m_tag_size != 8 && (m_tag_size < 12 || m_tag_size > 16)) {
35
0
      throw Invalid_Argument(fmt("{} cannot use a tag of {} bytes", name(), m_tag_size));
36
0
   }
37
38
99
   m_ctr = std::make_unique<CTR_BE>(std::move(cipher), 4);
39
99
   m_ghash = std::make_unique<GHASH>();
40
99
}
41
42
99
GCM_Mode::~GCM_Mode() = default;
43
44
0
void GCM_Mode::clear() {
45
0
   m_ctr->clear();
46
0
   m_ghash->clear();
47
0
   reset();
48
0
}
49
50
99
void GCM_Mode::reset() {
51
99
   m_ghash->reset_state();
52
99
   m_in_msg = false;
53
99
}
54
55
0
std::string GCM_Mode::name() const {
56
0
   return fmt("{}/GCM({})", m_cipher_name, tag_size());
57
0
}
58
59
0
std::string GCM_Mode::provider() const {
60
0
   return m_ghash->provider();
61
0
}
62
63
0
size_t GCM_Mode::update_granularity() const {
64
0
   return 1;
65
0
}
66
67
0
size_t GCM_Mode::ideal_granularity() const {
68
0
   return GCM_BS * std::max<size_t>(2, BlockCipher::ParallelismMult);
69
0
}
70
71
94
bool GCM_Mode::valid_nonce_length(size_t len) const {
72
   // GCM does not support empty nonces
73
94
   return (len > 0);
74
94
}
75
76
99
Key_Length_Specification GCM_Mode::key_spec() const {
77
99
   return m_ctr->key_spec();
78
99
}
79
80
0
bool GCM_Mode::has_keying_material() const {
81
0
   return m_ctr->has_keying_material();
82
0
}
83
84
99
void GCM_Mode::key_schedule(std::span<const uint8_t> key) {
85
99
   reset();
86
99
   m_ctr->set_key(key);
87
88
99
   std::array<uint8_t, GCM_BS> zeros{};
89
99
   m_ctr->set_iv(zeros);
90
91
99
   uint8_t H[GCM_BS] = {0};
92
99
   m_ctr->encipher(H);
93
99
   m_ghash->set_key(H);
94
99
}
95
96
94
void GCM_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
97
94
   BOTAN_ARG_CHECK(idx == 0, "GCM: cannot handle non-zero index in set_associated_data_n");
98
94
   m_ghash->set_associated_data(ad);
99
94
}
100
101
94
void GCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
102
94
   BOTAN_STATE_CHECK(!m_in_msg);
103
104
94
   if(!valid_nonce_length(nonce_len)) {
105
0
      throw Invalid_IV_Length(name(), nonce_len);
106
0
   }
107
108
94
   std::array<uint8_t, GCM_BS> y0 = {};
109
110
94
   if(nonce_len == 12) {
111
94
      copy_mem(y0.data(), nonce, nonce_len);
112
94
      y0[15] = 1;
113
94
   } else {
114
0
      m_ghash->nonce_hash(std::span<uint8_t, GCM_BS>(y0), {nonce, nonce_len});
115
0
   }
116
117
94
   m_ctr->set_iv(y0.data(), y0.size());
118
119
94
   clear_mem(y0.data(), y0.size());
120
94
   m_ctr->encipher(y0);
121
122
94
   m_ghash->start(y0);
123
94
   secure_scrub_memory(y0);
124
94
   m_in_msg = true;
125
94
}
126
127
27
size_t GCM_Encryption::output_length(size_t input_length) const {
128
27
   return add_or_throw(input_length, tag_size(), "GCM input too large");
129
27
}
130
131
0
size_t GCM_Encryption::process_msg(uint8_t buf[], size_t sz) {
132
0
   BOTAN_STATE_CHECK(m_in_msg);
133
0
   BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid buffer size");
134
0
   m_ctr->cipher(buf, buf, sz);
135
0
   m_ghash->update({buf, sz});
136
0
   return sz;
137
0
}
138
139
27
void GCM_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
140
27
   BOTAN_STATE_CHECK(m_in_msg);
141
27
   BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset");
142
27
   const size_t sz = buffer.size() - offset;
143
27
   uint8_t* buf = buffer.data() + offset;
144
145
27
   m_ctr->cipher(buf, buf, sz);
146
27
   m_ghash->update({buf, sz});
147
148
27
   std::array<uint8_t, 16> mac = {0};
149
27
   m_ghash->final(std::span(mac).first(tag_size()));
150
27
   buffer += std::make_pair(mac.data(), tag_size());
151
27
   m_in_msg = false;
152
27
}
153
154
67
size_t GCM_Decryption::output_length(size_t input_length) const {
155
67
   BOTAN_ARG_CHECK(input_length >= tag_size(), "Message too short to be valid");
156
67
   return input_length - tag_size();
157
67
}
158
159
0
size_t GCM_Decryption::process_msg(uint8_t buf[], size_t sz) {
160
0
   BOTAN_STATE_CHECK(m_in_msg);
161
0
   BOTAN_ARG_CHECK(sz % update_granularity() == 0, "Invalid buffer size");
162
0
   m_ghash->update({buf, sz});
163
0
   m_ctr->cipher(buf, buf, sz);
164
0
   return sz;
165
0
}
166
167
67
void GCM_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
168
67
   BOTAN_STATE_CHECK(m_in_msg);
169
67
   BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset");
170
67
   const size_t sz = buffer.size() - offset;
171
67
   uint8_t* buf = buffer.data() + offset;
172
173
67
   BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
174
175
67
   const size_t remaining = sz - tag_size();
176
177
   // handle any final input before the tag
178
67
   if(remaining > 0) {
179
66
      m_ghash->update({buf, remaining});
180
66
      m_ctr->cipher(buf, buf, remaining);
181
66
   }
182
183
67
   std::array<uint8_t, 16> mac = {0};
184
67
   m_ghash->final(std::span(mac).first(tag_size()));
185
186
67
   const uint8_t* included_tag = &buffer[remaining + offset];
187
188
67
   m_in_msg = false;
189
190
67
   if(!CT::is_equal(mac.data(), included_tag, tag_size()).as_bool()) {
191
67
      clear_mem(std::span{buffer}.subspan(offset, remaining));
192
67
      throw Invalid_Authentication_Tag("GCM tag check failed");
193
67
   }
194
195
0
   buffer.resize(offset + remaining);
196
0
}
197
198
}  // namespace Botan