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