1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
5 #include <openssl/evp.h>
7 #include "crypto_onwire.h"
9 #include "common/debug.h"
10 #include "common/ceph_crypto.h"
11 #include "include/types.h"
13 #define dout_subsys ceph_subsys_ms
15 namespace ceph::crypto::onwire
{
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};
26 bool operator==(const nonce_t
& rhs
) const {
27 return !memcmp(this, &rhs
, sizeof(*this));
29 } __attribute__((packed
));
30 static_assert(sizeof(nonce_t
) == AESGCM_IV_LEN
);
32 using key_t
= std::array
<std::uint8_t, AESGCM_KEY_LEN
>;
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
);
48 AES128GCM_OnWireTxHandler(CephContext
* const cct
,
51 bool new_nonce_format
)
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);
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");
64 if(1 != EVP_EncryptInit_ex(ectx
.get(), nullptr, nullptr,
65 key
.data(), nullptr)) {
66 throw std::runtime_error("EVP_EncryptInit_ex failed");
70 ~AES128GCM_OnWireTxHandler() override
{
71 ::TOPNSPC::crypto::zeroize_for_security(&nonce
, sizeof(nonce
));
72 ::TOPNSPC::crypto::zeroize_for_security(&initial_nonce
, sizeof(initial_nonce
));
75 void reset_tx_handler(const uint32_t* first
, const uint32_t* last
) override
;
77 void authenticated_encrypt_update(const ceph::bufferlist
& plaintext
) override
;
78 ceph::bufferlist
authenticated_encrypt_final() override
;
81 void AES128GCM_OnWireTxHandler::reset_tx_handler(const uint32_t* first
,
84 if (nonce
== initial_nonce
) {
85 if (used_initial_nonce
) {
86 throw ceph::crypto::onwire::TxHandlerError("out of nonces");
88 used_initial_nonce
= true;
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");
96 ceph_assert(buffer
.get_append_buffer_unused_tail_length() == 0);
97 buffer
.reserve(std::accumulate(first
, last
, AESGCM_TAG_LEN
));
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;
104 nonce
.counter
= nonce
.counter
+ 1;
108 void AES128GCM_OnWireTxHandler::authenticated_encrypt_update(
109 const ceph::bufferlist
& plaintext
)
111 ceph_assert(buffer
.get_append_buffer_unused_tail_length() >=
113 auto filler
= buffer
.append_hole(plaintext
.length());
115 for (const auto& plainbuf
: plaintext
.buffers()) {
118 if(1 != EVP_EncryptUpdate(ectx
.get(),
119 reinterpret_cast<unsigned char*>(filler
.c_str()),
121 reinterpret_cast<const unsigned char*>(plainbuf
.c_str()),
122 plainbuf
.length())) {
123 throw std::runtime_error("EVP_EncryptUpdate failed");
125 ceph_assert_always(update_len
>= 0);
126 ceph_assert(static_cast<unsigned>(update_len
) == plainbuf
.length());
127 filler
.advance(update_len
);
130 ldout(cct
, 15) << __func__
131 << " plaintext.length()=" << plaintext
.length()
132 << " buffer.length()=" << buffer
.length()
136 ceph::bufferlist
AES128GCM_OnWireTxHandler::authenticated_encrypt_final()
139 ceph_assert(buffer
.get_append_buffer_unused_tail_length() ==
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()),
145 throw std::runtime_error("EVP_EncryptFinal_ex failed");
147 ceph_assert_always(final_len
== 0);
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
,
153 throw std::runtime_error("EVP_CIPHER_CTX_ctrl failed");
156 ldout(cct
, 15) << __func__
157 << " buffer.length()=" << buffer
.length()
158 << " final_len=" << final_len
160 return std::move(buffer
);
164 class AES128GCM_OnWireRxHandler
: public ceph::crypto::onwire::RxHandler
{
165 std::unique_ptr
<EVP_CIPHER_CTX
, decltype(&::EVP_CIPHER_CTX_free
)> ectx
;
167 bool new_nonce_format
; // 64-bit counter?
168 static_assert(sizeof(nonce
) == AESGCM_IV_LEN
);
171 AES128GCM_OnWireRxHandler(CephContext
* const cct
,
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);
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");
185 if(1 != EVP_DecryptInit_ex(ectx
.get(), nullptr, nullptr,
186 key
.data(), nullptr)) {
187 throw std::runtime_error("EVP_DecryptInit_ex failed");
191 ~AES128GCM_OnWireRxHandler() override
{
192 ::TOPNSPC::crypto::zeroize_for_security(&nonce
, sizeof(nonce
));
195 std::uint32_t get_extra_size_at_final() override
{
196 return AESGCM_TAG_LEN
;
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
;
203 void AES128GCM_OnWireRxHandler::reset_rx_handler()
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");
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;
215 nonce
.counter
= nonce
.counter
+ 1;
219 void AES128GCM_OnWireRxHandler::authenticated_decrypt_update(
220 ceph::bufferlist
& bl
)
222 // discard cached crcs as we will be writing through c_str()
224 for (auto& buf
: bl
.buffers()) {
225 auto p
= reinterpret_cast<unsigned char*>(const_cast<char*>(buf
.c_str()));
228 if (1 != EVP_DecryptUpdate(ectx
.get(), p
, &update_len
, p
, buf
.length())) {
229 throw std::runtime_error("EVP_DecryptUpdate failed");
231 ceph_assert_always(update_len
>= 0);
232 ceph_assert(static_cast<unsigned>(update_len
) == buf
.length());
236 void AES128GCM_OnWireRxHandler::authenticated_decrypt_update_final(
237 ceph::bufferlist
& bl
)
239 unsigned orig_len
= bl
.length();
240 ceph_assert(orig_len
>= AESGCM_TAG_LEN
);
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
);
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");
257 // I expect that 0 bytes will be appended. The call is supposed solely to
258 // authenticate the message.
261 if (0 >= EVP_DecryptFinal_ex(ectx
.get(), nullptr, &final_len
)) {
262 throw MsgAuthError();
264 ceph_assert_always(final_len
== 0);
265 ceph_assert(bl
.length() + AESGCM_TAG_LEN
== orig_len
);
269 ceph::crypto::onwire::rxtx_t
ceph::crypto::onwire::rxtx_t::create_handler_pair(
271 const AuthConnectionMeta
& auth_meta
,
272 bool new_nonce_format
,
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();
282 ::memcpy(key
.data(), secbuf
, sizeof(key
));
283 secbuf
+= sizeof(key
);
288 ::memcpy(&rx_nonce
, secbuf
, sizeof(rx_nonce
));
289 secbuf
+= sizeof(rx_nonce
);
294 ::memcpy(&tx_nonce
, secbuf
, sizeof(tx_nonce
));
295 secbuf
+= sizeof(tx_nonce
);
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
)
305 return { nullptr, nullptr };
309 } // namespace ceph::crypto::onwire