]> git.proxmox.com Git - ceph.git/blob - ceph/src/msg/async/crypto_onwire.cc
import 15.2.5
[ceph.git] / ceph / src / msg / async / crypto_onwire.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include <array>
5 #include <openssl/evp.h>
6
7 #include "crypto_onwire.h"
8
9 #include "common/debug.h"
10 #include "common/ceph_crypto.h"
11 #include "include/types.h"
12
13 #define dout_subsys ceph_subsys_ms
14
15 namespace ceph::crypto::onwire {
16
17 static constexpr const std::size_t AESGCM_KEY_LEN{16};
18 static constexpr const std::size_t AESGCM_IV_LEN{12};
19 static constexpr const std::size_t AESGCM_TAG_LEN{16};
20 static constexpr const std::size_t AESGCM_BLOCK_LEN{16};
21
22 struct nonce_t {
23 ceph_le32 fixed;
24 ceph_le64 counter;
25
26 bool operator==(const nonce_t& rhs) const {
27 return !memcmp(this, &rhs, sizeof(*this));
28 }
29 } __attribute__((packed));
30 static_assert(sizeof(nonce_t) == AESGCM_IV_LEN);
31
32 using key_t = std::array<std::uint8_t, AESGCM_KEY_LEN>;
33
34 // http://www.mindspring.com/~dmcgrew/gcm-nist-6.pdf
35 // https://www.openssl.org/docs/man1.0.2/crypto/EVP_aes_128_gcm.html#GCM-mode
36 // https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption
37 // https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
38 class AES128GCM_OnWireTxHandler : public ceph::crypto::onwire::TxHandler {
39 CephContext* const cct;
40 std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)> ectx;
41 ceph::bufferlist buffer;
42 nonce_t nonce, initial_nonce;
43 bool used_initial_nonce;
44 bool new_nonce_format; // 64-bit counter?
45 static_assert(sizeof(nonce) == AESGCM_IV_LEN);
46
47 public:
48 AES128GCM_OnWireTxHandler(CephContext* const cct,
49 const key_t& key,
50 const nonce_t& nonce,
51 bool new_nonce_format)
52 : cct(cct),
53 ectx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free),
54 nonce(nonce), initial_nonce(nonce), used_initial_nonce(false),
55 new_nonce_format(new_nonce_format) {
56 ceph_assert_always(ectx);
57 ceph_assert_always(key.size() * CHAR_BIT == 128);
58
59 if (1 != EVP_EncryptInit_ex(ectx.get(), EVP_aes_128_gcm(),
60 nullptr, nullptr, nullptr)) {
61 throw std::runtime_error("EVP_EncryptInit_ex failed");
62 }
63
64 if(1 != EVP_EncryptInit_ex(ectx.get(), nullptr, nullptr,
65 key.data(), nullptr)) {
66 throw std::runtime_error("EVP_EncryptInit_ex failed");
67 }
68 }
69
70 ~AES128GCM_OnWireTxHandler() override {
71 ::ceph::crypto::zeroize_for_security(&nonce, sizeof(nonce));
72 ::ceph::crypto::zeroize_for_security(&initial_nonce, sizeof(initial_nonce));
73 }
74
75 void reset_tx_handler(const uint32_t* first, const uint32_t* last) override;
76
77 void authenticated_encrypt_update(const ceph::bufferlist& plaintext) override;
78 ceph::bufferlist authenticated_encrypt_final() override;
79 };
80
81 void AES128GCM_OnWireTxHandler::reset_tx_handler(const uint32_t* first,
82 const uint32_t* last)
83 {
84 if (nonce == initial_nonce) {
85 if (used_initial_nonce) {
86 throw ceph::crypto::onwire::TxHandlerError("out of nonces");
87 }
88 used_initial_nonce = true;
89 }
90
91 if(1 != EVP_EncryptInit_ex(ectx.get(), nullptr, nullptr, nullptr,
92 reinterpret_cast<const unsigned char*>(&nonce))) {
93 throw std::runtime_error("EVP_EncryptInit_ex failed");
94 }
95
96 ceph_assert(buffer.get_append_buffer_unused_tail_length() == 0);
97 buffer.reserve(std::accumulate(first, last, AESGCM_TAG_LEN));
98
99 if (!new_nonce_format) {
100 // msgr2.0: 32-bit counter followed by 64-bit fixed field,
101 // susceptible to overflow!
102 nonce.fixed = nonce.fixed + 1;
103 } else {
104 nonce.counter = nonce.counter + 1;
105 }
106 }
107
108 void AES128GCM_OnWireTxHandler::authenticated_encrypt_update(
109 const ceph::bufferlist& plaintext)
110 {
111 ceph_assert(buffer.get_append_buffer_unused_tail_length() >=
112 plaintext.length());
113 auto filler = buffer.append_hole(plaintext.length());
114
115 for (const auto& plainbuf : plaintext.buffers()) {
116 int update_len = 0;
117
118 if(1 != EVP_EncryptUpdate(ectx.get(),
119 reinterpret_cast<unsigned char*>(filler.c_str()),
120 &update_len,
121 reinterpret_cast<const unsigned char*>(plainbuf.c_str()),
122 plainbuf.length())) {
123 throw std::runtime_error("EVP_EncryptUpdate failed");
124 }
125 ceph_assert_always(update_len >= 0);
126 ceph_assert(static_cast<unsigned>(update_len) == plainbuf.length());
127 filler.advance(update_len);
128 }
129
130 ldout(cct, 15) << __func__
131 << " plaintext.length()=" << plaintext.length()
132 << " buffer.length()=" << buffer.length()
133 << dendl;
134 }
135
136 ceph::bufferlist AES128GCM_OnWireTxHandler::authenticated_encrypt_final()
137 {
138 int final_len = 0;
139 ceph_assert(buffer.get_append_buffer_unused_tail_length() ==
140 AESGCM_BLOCK_LEN);
141 auto filler = buffer.append_hole(AESGCM_BLOCK_LEN);
142 if(1 != EVP_EncryptFinal_ex(ectx.get(),
143 reinterpret_cast<unsigned char*>(filler.c_str()),
144 &final_len)) {
145 throw std::runtime_error("EVP_EncryptFinal_ex failed");
146 }
147 ceph_assert_always(final_len == 0);
148
149 static_assert(AESGCM_BLOCK_LEN == AESGCM_TAG_LEN);
150 if(1 != EVP_CIPHER_CTX_ctrl(ectx.get(),
151 EVP_CTRL_GCM_GET_TAG, AESGCM_TAG_LEN,
152 filler.c_str())) {
153 throw std::runtime_error("EVP_CIPHER_CTX_ctrl failed");
154 }
155
156 ldout(cct, 15) << __func__
157 << " buffer.length()=" << buffer.length()
158 << " final_len=" << final_len
159 << dendl;
160 return std::move(buffer);
161 }
162
163 // RX PART
164 class AES128GCM_OnWireRxHandler : public ceph::crypto::onwire::RxHandler {
165 CephContext* const cct;
166 std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)> ectx;
167 nonce_t nonce;
168 bool new_nonce_format; // 64-bit counter?
169 static_assert(sizeof(nonce) == AESGCM_IV_LEN);
170
171 public:
172 AES128GCM_OnWireRxHandler(CephContext* const cct,
173 const key_t& key,
174 const nonce_t& nonce,
175 bool new_nonce_format)
176 : cct(cct),
177 ectx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free),
178 nonce(nonce), new_nonce_format(new_nonce_format) {
179 ceph_assert_always(ectx);
180 ceph_assert_always(key.size() * CHAR_BIT == 128);
181
182 if (1 != EVP_DecryptInit_ex(ectx.get(), EVP_aes_128_gcm(),
183 nullptr, nullptr, nullptr)) {
184 throw std::runtime_error("EVP_DecryptInit_ex failed");
185 }
186
187 if(1 != EVP_DecryptInit_ex(ectx.get(), nullptr, nullptr,
188 key.data(), nullptr)) {
189 throw std::runtime_error("EVP_DecryptInit_ex failed");
190 }
191 }
192
193 ~AES128GCM_OnWireRxHandler() override {
194 ::ceph::crypto::zeroize_for_security(&nonce, sizeof(nonce));
195 }
196
197 std::uint32_t get_extra_size_at_final() override {
198 return AESGCM_TAG_LEN;
199 }
200 void reset_rx_handler() override;
201 void authenticated_decrypt_update(ceph::bufferlist& bl) override;
202 void authenticated_decrypt_update_final(ceph::bufferlist& bl) override;
203 };
204
205 void AES128GCM_OnWireRxHandler::reset_rx_handler()
206 {
207 if(1 != EVP_DecryptInit_ex(ectx.get(), nullptr, nullptr, nullptr,
208 reinterpret_cast<const unsigned char*>(&nonce))) {
209 throw std::runtime_error("EVP_DecryptInit_ex failed");
210 }
211
212 if (!new_nonce_format) {
213 // msgr2.0: 32-bit counter followed by 64-bit fixed field,
214 // susceptible to overflow!
215 nonce.fixed = nonce.fixed + 1;
216 } else {
217 nonce.counter = nonce.counter + 1;
218 }
219 }
220
221 void AES128GCM_OnWireRxHandler::authenticated_decrypt_update(
222 ceph::bufferlist& bl)
223 {
224 // discard cached crcs as we will be writing through c_str()
225 bl.invalidate_crc();
226 for (auto& buf : bl.buffers()) {
227 auto p = reinterpret_cast<unsigned char*>(const_cast<char*>(buf.c_str()));
228 int update_len = 0;
229
230 if (1 != EVP_DecryptUpdate(ectx.get(), p, &update_len, p, buf.length())) {
231 throw std::runtime_error("EVP_DecryptUpdate failed");
232 }
233 ceph_assert_always(update_len >= 0);
234 ceph_assert(static_cast<unsigned>(update_len) == buf.length());
235 }
236 }
237
238 void AES128GCM_OnWireRxHandler::authenticated_decrypt_update_final(
239 ceph::bufferlist& bl)
240 {
241 unsigned orig_len = bl.length();
242 ceph_assert(orig_len >= AESGCM_TAG_LEN);
243
244 // decrypt optional data. Caller is obliged to provide only signature but it
245 // may supply ciphertext as well. Combining the update + final is reflected
246 // combined together.
247 ceph::bufferlist auth_tag;
248 bl.splice(orig_len - AESGCM_TAG_LEN, AESGCM_TAG_LEN, &auth_tag);
249 if (bl.length() > 0) {
250 authenticated_decrypt_update(bl);
251 }
252
253 // we need to ensure the tag is stored in continuous memory.
254 if (1 != EVP_CIPHER_CTX_ctrl(ectx.get(), EVP_CTRL_GCM_SET_TAG,
255 AESGCM_TAG_LEN, auth_tag.c_str())) {
256 throw std::runtime_error("EVP_CIPHER_CTX_ctrl failed");
257 }
258
259 // I expect that 0 bytes will be appended. The call is supposed solely to
260 // authenticate the message.
261 {
262 int final_len = 0;
263 if (0 >= EVP_DecryptFinal_ex(ectx.get(), nullptr, &final_len)) {
264 throw MsgAuthError();
265 }
266 ceph_assert_always(final_len == 0);
267 ceph_assert(bl.length() + AESGCM_TAG_LEN == orig_len);
268 }
269 }
270
271 ceph::crypto::onwire::rxtx_t ceph::crypto::onwire::rxtx_t::create_handler_pair(
272 CephContext* cct,
273 const AuthConnectionMeta& auth_meta,
274 bool new_nonce_format,
275 bool crossed)
276 {
277 if (auth_meta.is_mode_secure()) {
278 ceph_assert_always(auth_meta.connection_secret.length() >= \
279 sizeof(key_t) + 2 * sizeof(nonce_t));
280 const char* secbuf = auth_meta.connection_secret.c_str();
281
282 key_t key;
283 {
284 ::memcpy(key.data(), secbuf, sizeof(key));
285 secbuf += sizeof(key);
286 }
287
288 nonce_t rx_nonce;
289 {
290 ::memcpy(&rx_nonce, secbuf, sizeof(rx_nonce));
291 secbuf += sizeof(rx_nonce);
292 }
293
294 nonce_t tx_nonce;
295 {
296 ::memcpy(&tx_nonce, secbuf, sizeof(tx_nonce));
297 secbuf += sizeof(tx_nonce);
298 }
299
300 return {
301 std::make_unique<AES128GCM_OnWireRxHandler>(
302 cct, key, crossed ? tx_nonce : rx_nonce, new_nonce_format),
303 std::make_unique<AES128GCM_OnWireTxHandler>(
304 cct, key, crossed ? rx_nonce : tx_nonce, new_nonce_format)
305 };
306 } else {
307 return { nullptr, nullptr };
308 }
309 }
310
311 } // namespace ceph::crypto::onwire