From 524202827baf020fde8b3c9d3ce0bc0f5d9f3d14 Mon Sep 17 00:00:00 2001 From: Radoslaw Zarzynski Date: Fri, 1 Mar 2019 20:18:11 +0100 Subject: [PATCH] msg/async, v2: epilogue size is variable in secure mode. Signed-off-by: Radoslaw Zarzynski --- src/msg/async/ProtocolV2.cc | 26 ++++++++++++++++---------- src/msg/async/ProtocolV2.h | 1 + src/msg/async/frames_v2.h | 34 ++++++++++++++++++++++------------ 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/src/msg/async/ProtocolV2.cc b/src/msg/async/ProtocolV2.cc index 20b3f8d151c08..ff69c657c3854 100644 --- a/src/msg/async/ProtocolV2.cc +++ b/src/msg/async/ProtocolV2.cc @@ -695,6 +695,16 @@ uint32_t ProtocolV2::get_onwire_size(uint32_t logical_size) const { return logical_size; } +uint32_t ProtocolV2::get_epilogue_size() const { + // In secure mode size of epilogue is flexible and depends on particular + // cipher implementation. See the comment for epilogue_crc_block_t. + if (session_stream_handlers.rx) { + return session_stream_handlers.rx->get_extra_size_at_final(); + } else { + return FRAME_CRC_EPILOGUE_SIZE; + } +} + CtPtr ProtocolV2::read(CONTINUATION_PARAM(next, ProtocolV2, char *, int), int len, char *buffer) { if (!buffer) { @@ -1163,7 +1173,7 @@ CtPtr ProtocolV2::handle_read_frame_segment(char *buffer, int r) { if (rx_segments_desc.size() == rx_segments_data.size()) { // OK, all segments planned to read are read. Can go with epilogue. - return READ(FRAME_EPILOGUE_SIZE, handle_read_frame_epilogue_main); + return READ(get_epilogue_size(), handle_read_frame_epilogue_main); } else { // TODO: for makeshift only. This will be more generic and throttled return read_frame_segment(); @@ -1264,23 +1274,17 @@ CtPtr ProtocolV2::handle_read_frame_epilogue_main(char *buffer, int r) return _fault(); } - auto& epilogue = reinterpret_cast(*buffer); - // FIXME: if (auth_meta->is_mode_secure()) { if (session_stream_handlers.rx) { // if we still have more bytes to read is because we signed or encrypted // the message payload ldout(cct, 1) << __func__ << " read frame epilogue bytes=" - << FRAME_EPILOGUE_SIZE << dendl; - - ceph_assert(session_stream_handlers.rx); - ceph_assert(FRAME_EPILOGUE_SIZE == \ - session_stream_handlers.rx->get_extra_size_at_final()); + << get_epilogue_size() << dendl; // I expect that ::temp_buffer is being used here. ceph::bufferlist epilogue_bl; - epilogue_bl.push_back(buffer::create_static(FRAME_EPILOGUE_SIZE, - epilogue.auth_tag)); + epilogue_bl.push_back(buffer::create_static(get_epilogue_size(), + buffer)); try { session_stream_handlers.rx->authenticated_decrypt_update_final( std::move(epilogue_bl), segment_t::DEFAULT_ALIGNMENT); @@ -1291,6 +1295,8 @@ CtPtr ProtocolV2::handle_read_frame_epilogue_main(char *buffer, int r) return _fault(); } } else { + auto& epilogue = reinterpret_cast(*buffer); + for (std::uint8_t idx = 0; idx < rx_segments_data.size(); idx++) { const __u32 expected_crc = epilogue.crc_values[idx]; const __u32 calculated_crc = rx_segments_data[idx].crc32c(-1); diff --git a/src/msg/async/ProtocolV2.h b/src/msg/async/ProtocolV2.h index b6acd2be4db4a..ab58a9a5da9d5 100644 --- a/src/msg/async/ProtocolV2.h +++ b/src/msg/async/ProtocolV2.h @@ -232,6 +232,7 @@ private: Ct *server_ready(); uint32_t get_onwire_size(uint32_t logical_size) const; + uint32_t get_epilogue_size() const; }; #endif /* _MSG_ASYNC_PROTOCOL_V2_ */ diff --git a/src/msg/async/frames_v2.h b/src/msg/async/frames_v2.h index 507dc471a8abd..e7f300d58a50e 100644 --- a/src/msg/async/frames_v2.h +++ b/src/msg/async/frames_v2.h @@ -104,20 +104,30 @@ struct preamble_block_t { static_assert(sizeof(preamble_block_t) % CRYPTO_BLOCK_SIZE == 0); static_assert(std::is_standard_layout::value); -// V2 epilogue conveys integrity/authentication information. It's added -// at the end of each frame holds: -// * CRC32 for MAX_NUM_SEGMENTS -- in plain mode, -// * cipher-specific data (e.g. auth tag for AES-GCM). -union epilogue_block_t { - char auth_tag[CRYPTO_BLOCK_SIZE]; +// Each Frame has an epilogue for integrity or authenticity validation. +// For plain mode it's quite straightforward - the structure stores up +// to MAX_NUM_SEGMENTS crc32 checksums, one per each segment. +// For secure mode things become very different. The fundamental thing +// is that epilogue format is **an implementation detail of particular +// cipher**. ProtocolV2 only knows: +// * where the data is placed (always at the end of ciphertext), +// * how long it is. RxHandler provides get_extra_size_at_final() but +// ProtocolV2 has NO WAY to alter this. +// +// The intention behind the contract is to provide flexibility of cipher +// selection. Currently AES in GCM mode is used and epilogue conveys its +// *auth tag* (following OpenSSL's terminology). However, it would be OK +// to switch to e.g. AES128-CBC + HMAC-SHA512 without affecting protocol +// (expect the cipher negotiation, of course). +struct epilogue_crc_block_t { std::array<__le32, MAX_NUM_SEGMENTS> crc_values; }; -static_assert(sizeof(epilogue_block_t) % CRYPTO_BLOCK_SIZE == 0); -static_assert(std::is_standard_layout::value); +static_assert(sizeof(epilogue_crc_block_t) % CRYPTO_BLOCK_SIZE == 0); +static_assert(std::is_standard_layout::value); static constexpr uint32_t FRAME_PREAMBLE_SIZE = sizeof(preamble_block_t); -static constexpr uint32_t FRAME_EPILOGUE_SIZE = sizeof(epilogue_block_t); +static constexpr uint32_t FRAME_CRC_EPILOGUE_SIZE = sizeof(epilogue_crc_block_t); template struct Frame { @@ -167,7 +177,7 @@ public: fill_preamble({segment_t{payload.length() - FRAME_PREAMBLE_SIZE, segment_t::DEFAULT_ALIGNMENT}}); - epilogue_block_t epilogue; + epilogue_crc_block_t epilogue; ::memset(&epilogue, 0, sizeof(epilogue)); ceph::bufferlist::const_iterator hdriter(&this->payload, FRAME_PREAMBLE_SIZE); @@ -373,7 +383,7 @@ struct SignedEncryptedFrame : public PayloadFrame { std::move(c.payload)); c.payload = session_stream_handlers.tx->authenticated_encrypt_final(); } else { - epilogue_block_t epilogue; + epilogue_crc_block_t epilogue; ::memset(&epilogue, 0, sizeof(epilogue)); ceph::bufferlist::const_iterator hdriter(&c.payload, FRAME_PREAMBLE_SIZE); @@ -637,7 +647,7 @@ struct MessageHeaderFrame // auth tag will be appended at the end f.payload = session_stream_handlers.tx->authenticated_encrypt_final(); } else { - epilogue_block_t epilogue; + epilogue_crc_block_t epilogue; ceph::bufferlist::const_iterator hdriter(&f.payload, FRAME_PREAMBLE_SIZE); epilogue.crc_values[SegmentIndex::Msg::HEADER] = hdriter.crc32c(hdriter.get_remaining(), -1); -- 2.39.5