]> git.proxmox.com Git - ceph.git/blob - ceph/src/msg/async/crypto_onwire.cc
update source to Ceph Pacific 16.2.2
[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 ::TOPNSPC::crypto::zeroize_for_security(&nonce, sizeof(nonce));
72 ::TOPNSPC::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 std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)> ectx;
166 nonce_t nonce;
167 bool new_nonce_format; // 64-bit counter?
168 static_assert(sizeof(nonce) == AESGCM_IV_LEN);
169
170 public:
171 AES128GCM_OnWireRxHandler(CephContext* const cct,
172 const key_t& key,
173 const nonce_t& nonce,
174 bool new_nonce_format)
175 : ectx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free),
176 nonce(nonce), new_nonce_format(new_nonce_format) {
177 ceph_assert_always(ectx);
178 ceph_assert_always(key.size() * CHAR_BIT == 128);
179
180 if (1 != EVP_DecryptInit_ex(ectx.get(), EVP_aes_128_gcm(),
181 nullptr, nullptr, nullptr)) {
182 throw std::runtime_error("EVP_DecryptInit_ex failed");
183 }
184
185 if(1 != EVP_DecryptInit_ex(ectx.get(), nullptr, nullptr,
186 key.data(), nullptr)) {
187 throw std::runtime_error("EVP_DecryptInit_ex failed");
188 }
189 }
190
191 ~AES128GCM_OnWireRxHandler() override {
192 ::TOPNSPC::crypto::zeroize_for_security(&nonce, sizeof(nonce));
193 }
194
195 std::uint32_t get_extra_size_at_final() override {
196 return AESGCM_TAG_LEN;
197 }
198 void reset_rx_handler() override;
199 void authenticated_decrypt_update(ceph::bufferlist& bl) override;
200 void authenticated_decrypt_update_final(ceph::bufferlist& bl) override;
201 };
202
203 void AES128GCM_OnWireRxHandler::reset_rx_handler()
204 {
205 if(1 != EVP_DecryptInit_ex(ectx.get(), nullptr, nullptr, nullptr,
206 reinterpret_cast<const unsigned char*>(&nonce))) {
207 throw std::runtime_error("EVP_DecryptInit_ex failed");
208 }
209
210 if (!new_nonce_format) {
211 // msgr2.0: 32-bit counter followed by 64-bit fixed field,
212 // susceptible to overflow!
213 nonce.fixed = nonce.fixed + 1;
214 } else {
215 nonce.counter = nonce.counter + 1;
216 }
217 }
218
219 void AES128GCM_OnWireRxHandler::authenticated_decrypt_update(
220 ceph::bufferlist& bl)
221 {
222 // discard cached crcs as we will be writing through c_str()
223 bl.invalidate_crc();
224 for (auto& buf : bl.buffers()) {
225 auto p = reinterpret_cast<unsigned char*>(const_cast<char*>(buf.c_str()));
226 int update_len = 0;
227
228 if (1 != EVP_DecryptUpdate(ectx.get(), p, &update_len, p, buf.length())) {
229 throw std::runtime_error("EVP_DecryptUpdate failed");
230 }
231 ceph_assert_always(update_len >= 0);
232 ceph_assert(static_cast<unsigned>(update_len) == buf.length());
233 }
234 }
235
236 void AES128GCM_OnWireRxHandler::authenticated_decrypt_update_final(
237 ceph::bufferlist& bl)
238 {
239 unsigned orig_len = bl.length();
240 ceph_assert(orig_len >= AESGCM_TAG_LEN);
241
242 // decrypt optional data. Caller is obliged to provide only signature but it
243 // may supply ciphertext as well. Combining the update + final is reflected
244 // combined together.
245 ceph::bufferlist auth_tag;
246 bl.splice(orig_len - AESGCM_TAG_LEN, AESGCM_TAG_LEN, &auth_tag);
247 if (bl.length() > 0) {
248 authenticated_decrypt_update(bl);
249 }
250
251 // we need to ensure the tag is stored in continuous memory.
252 if (1 != EVP_CIPHER_CTX_ctrl(ectx.get(), EVP_CTRL_GCM_SET_TAG,
253 AESGCM_TAG_LEN, auth_tag.c_str())) {
254 throw std::runtime_error("EVP_CIPHER_CTX_ctrl failed");
255 }
256
257 // I expect that 0 bytes will be appended. The call is supposed solely to
258 // authenticate the message.
259 {
260 int final_len = 0;
261 if (0 >= EVP_DecryptFinal_ex(ectx.get(), nullptr, &final_len)) {
262 throw MsgAuthError();
263 }
264 ceph_assert_always(final_len == 0);
265 ceph_assert(bl.length() + AESGCM_TAG_LEN == orig_len);
266 }
267 }
268
269 ceph::crypto::onwire::rxtx_t ceph::crypto::onwire::rxtx_t::create_handler_pair(
270 CephContext* cct,
271 const AuthConnectionMeta& auth_meta,
272 bool new_nonce_format,
273 bool crossed)
274 {
275 if (auth_meta.is_mode_secure()) {
276 ceph_assert_always(auth_meta.connection_secret.length() >= \
277 sizeof(key_t) + 2 * sizeof(nonce_t));
278 const char* secbuf = auth_meta.connection_secret.c_str();
279
280 key_t key;
281 {
282 ::memcpy(key.data(), secbuf, sizeof(key));
283 secbuf += sizeof(key);
284 }
285
286 nonce_t rx_nonce;
287 {
288 ::memcpy(&rx_nonce, secbuf, sizeof(rx_nonce));
289 secbuf += sizeof(rx_nonce);
290 }
291
292 nonce_t tx_nonce;
293 {
294 ::memcpy(&tx_nonce, secbuf, sizeof(tx_nonce));
295 secbuf += sizeof(tx_nonce);
296 }
297
298 return {
299 std::make_unique<AES128GCM_OnWireRxHandler>(
300 cct, key, crossed ? tx_nonce : rx_nonce, new_nonce_format),
301 std::make_unique<AES128GCM_OnWireTxHandler>(
302 cct, key, crossed ? rx_nonce : tx_nonce, new_nonce_format)
303 };
304 } else {
305 return { nullptr, nullptr };
306 }
307 }
308
309 } // namespace ceph::crypto::onwire