X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=ceph%2Fsrc%2Fmsg%2Fasync%2Fcrypto_onwire.cc;h=4e42340603e7d671e6ecb8846ba512dc62948bdf;hb=f6b5b4d738b87d88d2de35127b6b0e41eae2a272;hp=c39632cbd6e1e1b595fb6e97983dcf47188d4dda;hpb=12732ca2e80d168d344a265acffc1fbd1fa1f1b5;p=ceph.git diff --git a/ceph/src/msg/async/crypto_onwire.cc b/ceph/src/msg/async/crypto_onwire.cc index c39632cbd..4e4234060 100644 --- a/ceph/src/msg/async/crypto_onwire.cc +++ b/ceph/src/msg/async/crypto_onwire.cc @@ -20,8 +20,8 @@ static constexpr const std::size_t AESGCM_TAG_LEN{16}; static constexpr const std::size_t AESGCM_BLOCK_LEN{16}; struct nonce_t { - ceph_le32 random_seq; - ceph_le64 random_rest; + ceph_le32 fixed; + ceph_le64 counter; bool operator==(const nonce_t& rhs) const { return !memcmp(this, &rhs, sizeof(*this)); @@ -41,15 +41,18 @@ class AES128GCM_OnWireTxHandler : public ceph::crypto::onwire::TxHandler { ceph::bufferlist buffer; nonce_t nonce, initial_nonce; bool used_initial_nonce; + bool new_nonce_format; // 64-bit counter? static_assert(sizeof(nonce) == AESGCM_IV_LEN); public: AES128GCM_OnWireTxHandler(CephContext* const cct, const key_t& key, - const nonce_t& nonce) + const nonce_t& nonce, + bool new_nonce_format) : cct(cct), ectx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free), - nonce(nonce), initial_nonce(nonce), used_initial_nonce(false) { + nonce(nonce), initial_nonce(nonce), used_initial_nonce(false), + new_nonce_format(new_nonce_format) { ceph_assert_always(ectx); ceph_assert_always(key.size() * CHAR_BIT == 128); @@ -69,20 +72,14 @@ public: ::ceph::crypto::zeroize_for_security(&initial_nonce, sizeof(initial_nonce)); } - std::uint32_t calculate_segment_size(std::uint32_t size) override - { - return size; - } - - void reset_tx_handler( - std::initializer_list update_size_sequence) override; + void reset_tx_handler(const uint32_t* first, const uint32_t* last) override; void authenticated_encrypt_update(const ceph::bufferlist& plaintext) override; ceph::bufferlist authenticated_encrypt_final() override; }; -void AES128GCM_OnWireTxHandler::reset_tx_handler( - std::initializer_list update_size_sequence) +void AES128GCM_OnWireTxHandler::reset_tx_handler(const uint32_t* first, + const uint32_t* last) { if (nonce == initial_nonce) { if (used_initial_nonce) { @@ -96,15 +93,23 @@ void AES128GCM_OnWireTxHandler::reset_tx_handler( throw std::runtime_error("EVP_EncryptInit_ex failed"); } - buffer.reserve(std::accumulate(std::begin(update_size_sequence), - std::end(update_size_sequence), AESGCM_TAG_LEN)); + ceph_assert(buffer.get_append_buffer_unused_tail_length() == 0); + buffer.reserve(std::accumulate(first, last, AESGCM_TAG_LEN)); - nonce.random_seq = nonce.random_seq + 1; + if (!new_nonce_format) { + // msgr2.0: 32-bit counter followed by 64-bit fixed field, + // susceptible to overflow! + nonce.fixed = nonce.fixed + 1; + } else { + nonce.counter = nonce.counter + 1; + } } void AES128GCM_OnWireTxHandler::authenticated_encrypt_update( const ceph::bufferlist& plaintext) { + ceph_assert(buffer.get_append_buffer_unused_tail_length() >= + plaintext.length()); auto filler = buffer.append_hole(plaintext.length()); for (const auto& plainbuf : plaintext.buffers()) { @@ -131,6 +136,8 @@ void AES128GCM_OnWireTxHandler::authenticated_encrypt_update( ceph::bufferlist AES128GCM_OnWireTxHandler::authenticated_encrypt_final() { int final_len = 0; + ceph_assert(buffer.get_append_buffer_unused_tail_length() == + AESGCM_BLOCK_LEN); auto filler = buffer.append_hole(AESGCM_BLOCK_LEN); if(1 != EVP_EncryptFinal_ex(ectx.get(), reinterpret_cast(filler.c_str()), @@ -158,16 +165,17 @@ class AES128GCM_OnWireRxHandler : public ceph::crypto::onwire::RxHandler { CephContext* const cct; std::unique_ptr ectx; nonce_t nonce; + bool new_nonce_format; // 64-bit counter? static_assert(sizeof(nonce) == AESGCM_IV_LEN); public: AES128GCM_OnWireRxHandler(CephContext* const cct, const key_t& key, - const nonce_t& nonce) + const nonce_t& nonce, + bool new_nonce_format) : cct(cct), ectx(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free), - nonce(nonce) - { + nonce(nonce), new_nonce_format(new_nonce_format) { ceph_assert_always(ectx); ceph_assert_always(key.size() * CHAR_BIT == 128); @@ -190,12 +198,8 @@ public: return AESGCM_TAG_LEN; } void reset_rx_handler() override; - ceph::bufferlist authenticated_decrypt_update( - ceph::bufferlist&& ciphertext, - std::uint32_t alignment) override; - ceph::bufferlist authenticated_decrypt_update_final( - ceph::bufferlist&& ciphertext, - std::uint32_t alignment) override; + void authenticated_decrypt_update(ceph::bufferlist& bl) override; + void authenticated_decrypt_update_final(ceph::bufferlist& bl) override; }; void AES128GCM_OnWireRxHandler::reset_rx_handler() @@ -204,68 +208,46 @@ void AES128GCM_OnWireRxHandler::reset_rx_handler() reinterpret_cast(&nonce))) { throw std::runtime_error("EVP_DecryptInit_ex failed"); } - nonce.random_seq = nonce.random_seq + 1; + + if (!new_nonce_format) { + // msgr2.0: 32-bit counter followed by 64-bit fixed field, + // susceptible to overflow! + nonce.fixed = nonce.fixed + 1; + } else { + nonce.counter = nonce.counter + 1; + } } -ceph::bufferlist AES128GCM_OnWireRxHandler::authenticated_decrypt_update( - ceph::bufferlist&& ciphertext, - std::uint32_t alignment) +void AES128GCM_OnWireRxHandler::authenticated_decrypt_update( + ceph::bufferlist& bl) { - ceph_assert(ciphertext.length() > 0); - //ceph_assert(ciphertext.length() % AESGCM_BLOCK_LEN == 0); - - // NOTE: we might consider in-place transformations in the future. AFAIK - // OpenSSL's might sustain that but lack of clear confirmation postpones. - auto plainnode = ceph::buffer::ptr_node::create(buffer::create_aligned( - ciphertext.length(), alignment)); - auto* plainbuf = reinterpret_cast(plainnode->c_str()); - - for (const auto& cipherbuf : ciphertext.buffers()) { - // XXX: Why int? + // discard cached crcs as we will be writing through c_str() + bl.invalidate_crc(); + for (auto& buf : bl.buffers()) { + auto p = reinterpret_cast(const_cast(buf.c_str())); int update_len = 0; - if (1 != EVP_DecryptUpdate(ectx.get(), - plainbuf, - &update_len, - reinterpret_cast(cipherbuf.c_str()), - cipherbuf.length())) { + if (1 != EVP_DecryptUpdate(ectx.get(), p, &update_len, p, buf.length())) { throw std::runtime_error("EVP_DecryptUpdate failed"); } ceph_assert_always(update_len >= 0); - ceph_assert(cipherbuf.length() == static_cast(update_len)); - - plainbuf += update_len; + ceph_assert(static_cast(update_len) == buf.length()); } - - ceph::bufferlist outbl; - outbl.push_back(std::move(plainnode)); - return outbl; } - -ceph::bufferlist AES128GCM_OnWireRxHandler::authenticated_decrypt_update_final( - ceph::bufferlist&& ciphertext_and_tag, - std::uint32_t alignment) +void AES128GCM_OnWireRxHandler::authenticated_decrypt_update_final( + ceph::bufferlist& bl) { - const auto cnt_len = ciphertext_and_tag.length(); - ceph_assert(cnt_len >= AESGCM_TAG_LEN); + unsigned orig_len = bl.length(); + ceph_assert(orig_len >= AESGCM_TAG_LEN); // decrypt optional data. Caller is obliged to provide only signature but it // may supply ciphertext as well. Combining the update + final is reflected // combined together. - ceph::bufferlist plainbl; ceph::bufferlist auth_tag; - { - const auto tag_off = cnt_len - AESGCM_TAG_LEN; - ceph::bufferlist ciphertext; - ciphertext_and_tag.splice(0, tag_off, &ciphertext); - - // the rest is the signature (a.k.a auth tag) - auth_tag = std::move(ciphertext_and_tag); - - if (ciphertext.length()) { - plainbl = authenticated_decrypt_update(std::move(ciphertext), alignment); - } + bl.splice(orig_len - AESGCM_TAG_LEN, AESGCM_TAG_LEN, &auth_tag); + if (bl.length() > 0) { + authenticated_decrypt_update(bl); } // we need to ensure the tag is stored in continuous memory. @@ -279,23 +261,17 @@ ceph::bufferlist AES128GCM_OnWireRxHandler::authenticated_decrypt_update_final( { int final_len = 0; if (0 >= EVP_DecryptFinal_ex(ectx.get(), nullptr, &final_len)) { - ldout(cct, 15) << __func__ - << " plainbl.length()=" << plainbl.length() - << " final_len=" << final_len - << dendl; throw MsgAuthError(); - } else { - ceph_assert_always(final_len == 0); - ceph_assert_always(plainbl.length() + final_len + AESGCM_TAG_LEN == cnt_len); } + ceph_assert_always(final_len == 0); + ceph_assert(bl.length() + AESGCM_TAG_LEN == orig_len); } - - return plainbl; } ceph::crypto::onwire::rxtx_t ceph::crypto::onwire::rxtx_t::create_handler_pair( CephContext* cct, const AuthConnectionMeta& auth_meta, + bool new_nonce_format, bool crossed) { if (auth_meta.is_mode_secure()) { @@ -323,9 +299,9 @@ ceph::crypto::onwire::rxtx_t ceph::crypto::onwire::rxtx_t::create_handler_pair( return { std::make_unique( - cct, key, crossed ? tx_nonce : rx_nonce), + cct, key, crossed ? tx_nonce : rx_nonce, new_nonce_format), std::make_unique( - cct, key, crossed ? rx_nonce : tx_nonce) + cct, key, crossed ? rx_nonce : tx_nonce, new_nonce_format) }; } else { return { nullptr, nullptr };