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 ::ceph::crypto::zeroize_for_security(&nonce
, sizeof(nonce
));
72 ::ceph::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 CephContext
* const cct
;
166 std::unique_ptr
<EVP_CIPHER_CTX
, decltype(&::EVP_CIPHER_CTX_free
)> ectx
;
168 bool new_nonce_format
; // 64-bit counter?
169 static_assert(sizeof(nonce
) == AESGCM_IV_LEN
);
172 AES128GCM_OnWireRxHandler(CephContext
* const cct
,
174 const nonce_t
& nonce
,
175 bool new_nonce_format
)
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);
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");
187 if(1 != EVP_DecryptInit_ex(ectx
.get(), nullptr, nullptr,
188 key
.data(), nullptr)) {
189 throw std::runtime_error("EVP_DecryptInit_ex failed");
193 ~AES128GCM_OnWireRxHandler() override
{
194 ::ceph::crypto::zeroize_for_security(&nonce
, sizeof(nonce
));
197 std::uint32_t get_extra_size_at_final() override
{
198 return AESGCM_TAG_LEN
;
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
;
205 void AES128GCM_OnWireRxHandler::reset_rx_handler()
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");
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;
217 nonce
.counter
= nonce
.counter
+ 1;
221 void AES128GCM_OnWireRxHandler::authenticated_decrypt_update(
222 ceph::bufferlist
& bl
)
224 // discard cached crcs as we will be writing through c_str()
226 for (auto& buf
: bl
.buffers()) {
227 auto p
= reinterpret_cast<unsigned char*>(const_cast<char*>(buf
.c_str()));
230 if (1 != EVP_DecryptUpdate(ectx
.get(), p
, &update_len
, p
, buf
.length())) {
231 throw std::runtime_error("EVP_DecryptUpdate failed");
233 ceph_assert_always(update_len
>= 0);
234 ceph_assert(static_cast<unsigned>(update_len
) == buf
.length());
238 void AES128GCM_OnWireRxHandler::authenticated_decrypt_update_final(
239 ceph::bufferlist
& bl
)
241 unsigned orig_len
= bl
.length();
242 ceph_assert(orig_len
>= AESGCM_TAG_LEN
);
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
);
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");
259 // I expect that 0 bytes will be appended. The call is supposed solely to
260 // authenticate the message.
263 if (0 >= EVP_DecryptFinal_ex(ectx
.get(), nullptr, &final_len
)) {
264 throw MsgAuthError();
266 ceph_assert_always(final_len
== 0);
267 ceph_assert(bl
.length() + AESGCM_TAG_LEN
== orig_len
);
271 ceph::crypto::onwire::rxtx_t
ceph::crypto::onwire::rxtx_t::create_handler_pair(
273 const AuthConnectionMeta
& auth_meta
,
274 bool new_nonce_format
,
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();
284 ::memcpy(key
.data(), secbuf
, sizeof(key
));
285 secbuf
+= sizeof(key
);
290 ::memcpy(&rx_nonce
, secbuf
, sizeof(rx_nonce
));
291 secbuf
+= sizeof(rx_nonce
);
296 ::memcpy(&tx_nonce
, secbuf
, sizeof(tx_nonce
));
297 secbuf
+= sizeof(tx_nonce
);
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
)
307 return { nullptr, nullptr };
311 } // namespace ceph::crypto::onwire