Coverage Report

Created: 2024-11-29 06:10

/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
196
      m_tag_size(tag_size), m_cipher_name(cipher->name()) {
26
196
   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
196
   if(m_tag_size != 8 && (m_tag_size < 12 || m_tag_size > 16)) {
33
0
      throw Invalid_Argument(fmt("{} cannot use a tag of {} bytes", name(), m_tag_size));
34
0
   }
35
36
196
   m_ctr = std::make_unique<CTR_BE>(std::move(cipher), 4);
37
196
   m_ghash = std::make_unique<GHASH>();
38
196
}
39
40
196
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
0
std::string GCM_Mode::name() const {
53
0
   return fmt("{}/GCM({})", m_cipher_name, tag_size());
54
0
}
55
56
0
std::string GCM_Mode::provider() const {
57
0
   return m_ghash->provider();
58
0
}
59
60
0
size_t GCM_Mode::update_granularity() const {
61
0
   return GCM_BS;
62
0
}
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
194
bool GCM_Mode::valid_nonce_length(size_t len) const {
69
   // GCM does not support empty nonces
70
194
   return (len > 0);
71
194
}
72
73
196
Key_Length_Specification GCM_Mode::key_spec() const {
74
196
   return m_ctr->key_spec();
75
196
}
76
77
0
bool GCM_Mode::has_keying_material() const {
78
0
   return m_ctr->has_keying_material();
79
0
}
80
81
196
void GCM_Mode::key_schedule(std::span<const uint8_t> key) {
82
196
   m_ctr->set_key(key);
83
84
196
   const std::vector<uint8_t> zeros(GCM_BS);
85
196
   m_ctr->set_iv(zeros.data(), zeros.size());
86
87
196
   secure_vector<uint8_t> H(GCM_BS);
88
196
   m_ctr->encipher(H);
89
196
   m_ghash->set_key(H);
90
196
}
91
92
194
void GCM_Mode::set_associated_data_n(size_t idx, std::span<const uint8_t> ad) {
93
194
   BOTAN_ARG_CHECK(idx == 0, "GCM: cannot handle non-zero index in set_associated_data_n");
94
194
   m_ghash->set_associated_data(ad);
95
194
}
96
97
194
void GCM_Mode::start_msg(const uint8_t nonce[], size_t nonce_len) {
98
194
   if(!valid_nonce_length(nonce_len)) {
99
0
      throw Invalid_IV_Length(name(), nonce_len);
100
0
   }
101
102
194
   if(m_y0.size() != GCM_BS) {
103
156
      m_y0.resize(GCM_BS);
104
156
   }
105
106
194
   clear_mem(m_y0.data(), m_y0.size());
107
108
194
   if(nonce_len == 12) {
109
194
      copy_mem(m_y0.data(), nonce, nonce_len);
110
194
      m_y0[15] = 1;
111
194
   } else {
112
0
      m_ghash->nonce_hash(m_y0, {nonce, nonce_len});
113
0
   }
114
115
194
   m_ctr->set_iv(m_y0.data(), m_y0.size());
116
117
194
   clear_mem(m_y0.data(), m_y0.size());
118
194
   m_ctr->encipher(m_y0);
119
120
194
   m_ghash->start(m_y0);
121
194
   clear_mem(m_y0.data(), m_y0.size());
122
194
}
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
92
void GCM_Encryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
132
92
   BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset");
133
92
   const size_t sz = buffer.size() - offset;
134
92
   uint8_t* buf = buffer.data() + offset;
135
136
92
   m_ctr->cipher(buf, buf, sz);
137
92
   m_ghash->update({buf, sz});
138
139
92
   std::array<uint8_t, 16> mac = {0};
140
92
   m_ghash->final(std::span(mac).first(tag_size()));
141
92
   buffer += std::make_pair(mac.data(), tag_size());
142
92
}
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
102
void GCM_Decryption::finish_msg(secure_vector<uint8_t>& buffer, size_t offset) {
152
102
   BOTAN_ARG_CHECK(offset <= buffer.size(), "Invalid offset");
153
102
   const size_t sz = buffer.size() - offset;
154
102
   uint8_t* buf = buffer.data() + offset;
155
156
102
   BOTAN_ARG_CHECK(sz >= tag_size(), "input did not include the tag");
157
158
102
   const size_t remaining = sz - tag_size();
159
160
   // handle any final input before the tag
161
102
   if(remaining) {
162
99
      m_ghash->update({buf, remaining});
163
99
      m_ctr->cipher(buf, buf, remaining);
164
99
   }
165
166
102
   std::array<uint8_t, 16> mac = {0};
167
102
   m_ghash->final(std::span(mac).first(tag_size()));
168
169
102
   const uint8_t* included_tag = &buffer[remaining + offset];
170
171
102
   if(!CT::is_equal(mac.data(), included_tag, tag_size()).as_bool()) {
172
102
      throw Invalid_Authentication_Tag("GCM tag check failed");
173
102
   }
174
175
0
   buffer.resize(offset + remaining);
176
0
}
177
178
}  // namespace Botan