Coverage Report

Created: 2024-06-28 06:08

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