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) {
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();
return _fault();
}
- auto& epilogue = reinterpret_cast<epilogue_block_t&>(*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);
return _fault();
}
} else {
+ auto& epilogue = reinterpret_cast<epilogue_crc_block_t&>(*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);
static_assert(sizeof(preamble_block_t) % CRYPTO_BLOCK_SIZE == 0);
static_assert(std::is_standard_layout<preamble_block_t>::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<epilogue_block_t>::value);
+static_assert(sizeof(epilogue_crc_block_t) % CRYPTO_BLOCK_SIZE == 0);
+static_assert(std::is_standard_layout<epilogue_crc_block_t>::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 <class T>
struct Frame {
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);
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);
// 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);