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};
23 std::uint32_t random_seq
;
24 std::uint64_t random_rest
;
25 } __attribute__((packed
));
26 static_assert(sizeof(nonce_t
) == AESGCM_IV_LEN
);
28 using key_t
= std::array
<std::uint8_t, AESGCM_KEY_LEN
>;
30 // http://www.mindspring.com/~dmcgrew/gcm-nist-6.pdf
31 // https://www.openssl.org/docs/man1.0.2/crypto/EVP_aes_128_gcm.html#GCM-mode
32 // https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption
33 // https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf
34 class AES128GCM_OnWireTxHandler
: public ceph::crypto::onwire::TxHandler
{
35 CephContext
* const cct
;
36 std::unique_ptr
<EVP_CIPHER_CTX
, decltype(&::EVP_CIPHER_CTX_free
)> ectx
;
37 ceph::bufferlist buffer
;
39 static_assert(sizeof(nonce
) == AESGCM_IV_LEN
);
42 AES128GCM_OnWireTxHandler(CephContext
* const cct
,
46 ectx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free
),
48 ceph_assert_always(ectx
);
49 ceph_assert_always(key
.size() * CHAR_BIT
== 128);
51 if (1 != EVP_EncryptInit_ex(ectx
.get(), EVP_aes_128_gcm(),
52 nullptr, nullptr, nullptr)) {
53 throw std::runtime_error("EVP_EncryptInit_ex failed");
56 if(1 != EVP_EncryptInit_ex(ectx
.get(), nullptr, nullptr,
57 key
.data(), nullptr)) {
58 throw std::runtime_error("EVP_EncryptInit_ex failed");
62 ~AES128GCM_OnWireTxHandler() override
{
63 ::ceph::crypto::zeroize_for_security(&nonce
, sizeof(nonce
));
66 std::uint32_t calculate_segment_size(std::uint32_t size
) override
71 void reset_tx_handler(
72 std::initializer_list
<std::uint32_t> update_size_sequence
) override
;
74 void authenticated_encrypt_update(const ceph::bufferlist
& plaintext
) override
;
75 ceph::bufferlist
authenticated_encrypt_final() override
;
78 void AES128GCM_OnWireTxHandler::reset_tx_handler(
79 std::initializer_list
<std::uint32_t> update_size_sequence
)
81 if(1 != EVP_EncryptInit_ex(ectx
.get(), nullptr, nullptr, nullptr,
82 reinterpret_cast<const unsigned char*>(&nonce
))) {
83 throw std::runtime_error("EVP_EncryptInit_ex failed");
86 buffer
.reserve(std::accumulate(std::begin(update_size_sequence
),
87 std::end(update_size_sequence
), AESGCM_TAG_LEN
));
92 void AES128GCM_OnWireTxHandler::authenticated_encrypt_update(
93 const ceph::bufferlist
& plaintext
)
95 auto filler
= buffer
.append_hole(plaintext
.length());
97 for (const auto& plainbuf
: plaintext
.buffers()) {
100 if(1 != EVP_EncryptUpdate(ectx
.get(),
101 reinterpret_cast<unsigned char*>(filler
.c_str()),
103 reinterpret_cast<const unsigned char*>(plainbuf
.c_str()),
104 plainbuf
.length())) {
105 throw std::runtime_error("EVP_EncryptUpdate failed");
107 ceph_assert_always(update_len
>= 0);
108 ceph_assert(static_cast<unsigned>(update_len
) == plainbuf
.length());
109 filler
.advance(update_len
);
112 ldout(cct
, 15) << __func__
113 << " plaintext.length()=" << plaintext
.length()
114 << " buffer.length()=" << buffer
.length()
118 ceph::bufferlist
AES128GCM_OnWireTxHandler::authenticated_encrypt_final()
121 auto filler
= buffer
.append_hole(AESGCM_BLOCK_LEN
);
122 if(1 != EVP_EncryptFinal_ex(ectx
.get(),
123 reinterpret_cast<unsigned char*>(filler
.c_str()),
125 throw std::runtime_error("EVP_EncryptFinal_ex failed");
127 ceph_assert_always(final_len
== 0);
129 static_assert(AESGCM_BLOCK_LEN
== AESGCM_TAG_LEN
);
130 if(1 != EVP_CIPHER_CTX_ctrl(ectx
.get(),
131 EVP_CTRL_GCM_GET_TAG
, AESGCM_TAG_LEN
,
133 throw std::runtime_error("EVP_CIPHER_CTX_ctrl failed");
136 ldout(cct
, 15) << __func__
137 << " buffer.length()=" << buffer
.length()
138 << " final_len=" << final_len
140 return std::move(buffer
);
144 class AES128GCM_OnWireRxHandler
: public ceph::crypto::onwire::RxHandler
{
145 CephContext
* const cct
;
146 std::unique_ptr
<EVP_CIPHER_CTX
, decltype(&::EVP_CIPHER_CTX_free
)> ectx
;
148 static_assert(sizeof(nonce
) == AESGCM_IV_LEN
);
151 AES128GCM_OnWireRxHandler(CephContext
* const cct
,
153 const nonce_t
& nonce
)
155 ectx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free
),
158 ceph_assert_always(ectx
);
159 ceph_assert_always(key
.size() * CHAR_BIT
== 128);
161 if (1 != EVP_DecryptInit_ex(ectx
.get(), EVP_aes_128_gcm(),
162 nullptr, nullptr, nullptr)) {
163 throw std::runtime_error("EVP_DecryptInit_ex failed");
166 if(1 != EVP_DecryptInit_ex(ectx
.get(), nullptr, nullptr,
167 key
.data(), nullptr)) {
168 throw std::runtime_error("EVP_DecryptInit_ex failed");
172 ~AES128GCM_OnWireRxHandler() override
{
173 ::ceph::crypto::zeroize_for_security(&nonce
, sizeof(nonce
));
176 std::uint32_t get_extra_size_at_final() override
{
177 return AESGCM_TAG_LEN
;
179 void reset_rx_handler() override
;
180 ceph::bufferlist
authenticated_decrypt_update(
181 ceph::bufferlist
&& ciphertext
,
182 std::uint32_t alignment
) override
;
183 ceph::bufferlist
authenticated_decrypt_update_final(
184 ceph::bufferlist
&& ciphertext
,
185 std::uint32_t alignment
) override
;
188 void AES128GCM_OnWireRxHandler::reset_rx_handler()
190 if(1 != EVP_DecryptInit_ex(ectx
.get(), nullptr, nullptr, nullptr,
191 reinterpret_cast<const unsigned char*>(&nonce
))) {
192 throw std::runtime_error("EVP_DecryptInit_ex failed");
197 ceph::bufferlist
AES128GCM_OnWireRxHandler::authenticated_decrypt_update(
198 ceph::bufferlist
&& ciphertext
,
199 std::uint32_t alignment
)
201 ceph_assert(ciphertext
.length() > 0);
202 //ceph_assert(ciphertext.length() % AESGCM_BLOCK_LEN == 0);
204 // NOTE: we might consider in-place transformations in the future. AFAIK
205 // OpenSSL's might sustain that but lack of clear confirmation postpones.
206 auto plainnode
= ceph::buffer::ptr_node::create(buffer::create_aligned(
207 ciphertext
.length(), alignment
));
208 auto* plainbuf
= reinterpret_cast<unsigned char*>(plainnode
->c_str());
210 for (const auto& cipherbuf
: ciphertext
.buffers()) {
214 if (1 != EVP_DecryptUpdate(ectx
.get(),
217 reinterpret_cast<const unsigned char*>(cipherbuf
.c_str()),
218 cipherbuf
.length())) {
219 throw std::runtime_error("EVP_DecryptUpdate failed");
221 ceph_assert_always(update_len
>= 0);
222 ceph_assert(cipherbuf
.length() == static_cast<unsigned>(update_len
));
224 plainbuf
+= update_len
;
227 ceph::bufferlist outbl
;
228 outbl
.push_back(std::move(plainnode
));
233 ceph::bufferlist
AES128GCM_OnWireRxHandler::authenticated_decrypt_update_final(
234 ceph::bufferlist
&& ciphertext_and_tag
,
235 std::uint32_t alignment
)
237 const auto cnt_len
= ciphertext_and_tag
.length();
238 ceph_assert(cnt_len
>= AESGCM_TAG_LEN
);
240 // decrypt optional data. Caller is obliged to provide only signature but it
241 // may supply ciphertext as well. Combining the update + final is reflected
242 // combined together.
243 ceph::bufferlist plainbl
;
244 ceph::bufferlist auth_tag
;
246 const auto tag_off
= cnt_len
- AESGCM_TAG_LEN
;
247 ceph::bufferlist ciphertext
;
248 ciphertext_and_tag
.splice(0, tag_off
, &ciphertext
);
250 // the rest is the signature (a.k.a auth tag)
251 auth_tag
= std::move(ciphertext_and_tag
);
253 if (ciphertext
.length()) {
254 plainbl
= authenticated_decrypt_update(std::move(ciphertext
), alignment
);
258 // we need to ensure the tag is stored in continuous memory.
259 if (1 != EVP_CIPHER_CTX_ctrl(ectx
.get(), EVP_CTRL_GCM_SET_TAG
,
260 AESGCM_TAG_LEN
, auth_tag
.c_str())) {
261 throw std::runtime_error("EVP_CIPHER_CTX_ctrl failed");
264 // I expect that 0 bytes will be appended. The call is supposed solely to
265 // authenticate the message.
268 if (0 >= EVP_DecryptFinal_ex(ectx
.get(), nullptr, &final_len
)) {
269 ldout(cct
, 15) << __func__
270 << " plainbl.length()=" << plainbl
.length()
271 << " final_len=" << final_len
273 throw MsgAuthError();
275 ceph_assert_always(final_len
== 0);
276 ceph_assert_always(plainbl
.length() + final_len
+ AESGCM_TAG_LEN
== cnt_len
);
283 ceph::crypto::onwire::rxtx_t
ceph::crypto::onwire::rxtx_t::create_handler_pair(
285 const AuthConnectionMeta
& auth_meta
,
288 if (auth_meta
.is_mode_secure()) {
289 ceph_assert_always(auth_meta
.connection_secret
.length() >= \
290 sizeof(key_t
) + 2 * sizeof(nonce_t
));
291 const char* secbuf
= auth_meta
.connection_secret
.c_str();
295 ::memcpy(key
.data(), secbuf
, sizeof(key
));
296 secbuf
+= sizeof(key
);
301 ::memcpy(&rx_nonce
, secbuf
, sizeof(rx_nonce
));
302 secbuf
+= sizeof(rx_nonce
);
307 ::memcpy(&tx_nonce
, secbuf
, sizeof(tx_nonce
));
308 secbuf
+= sizeof(tx_nonce
);
312 std::make_unique
<AES128GCM_OnWireRxHandler
>(
313 cct
, key
, crossed
? tx_nonce
: rx_nonce
),
314 std::make_unique
<AES128GCM_OnWireTxHandler
>(
315 cct
, key
, crossed
? rx_nonce
: tx_nonce
)
318 return { nullptr, nullptr };
322 } // namespace ceph::crypto::onwire