Add support for full AES-GCM including double encryption of AES-XTS :D

This commit is contained in:
Vitaliy Filippov
2026-07-05 14:58:24 +03:00
parent 784fd7d233
commit a5dbf74123
16 changed files with 358 additions and 245 deletions
+18
View File
@@ -792,6 +792,24 @@ jobs:
echo ""
done
test_snapshot_chain_enc_gcm:
runs-on: ubuntu-latest
needs: build
container: ${{env.TEST_IMAGE}}:${{github.sha}}
steps:
- name: Run test
id: test
timeout-minutes: 3
run: TEST_NAME=enc_gcm ENCRYPTED=1 VITASTOR_CFG=',"proto_checksums":"gcm"' /root/vitastor/tests/test_snapshot_chain.sh
- name: Print logs
if: always() && steps.test.outcome == 'failure'
run: |
for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do
echo "-------- $i --------"
cat $i
echo ""
done
test_old_snapshot_chain:
runs-on: ubuntu-latest
needs: build
+40 -16
View File
@@ -197,19 +197,34 @@ osd_messenger_t::~osd_messenger_t()
destroy_tls();
}
static int parse_proto_checksums(const json11::Json & val, int default_value)
{
if (val.is_string())
{
const auto & str = val.string_value();
if (str == "full")
return MSGR_CSUM_FULL;
else if (str == "payload")
return MSGR_CSUM_PAYLOAD;
else if (str == "gcm")
return MSGR_CSUM_GCM;
else if (str == "none")
return 0;
else if (str == "")
return default_value;
}
else if (val.is_null())
return default_value;
fprintf(stderr, "proto_checksums should be \"full\", \"payload\", \"gcm\", \"none\""
", \"\" or null (default), but it is: %s\n", val.dump().c_str());
exit(1);
}
void osd_messenger_t::parse_config(const json11::Json & config, bool init)
{
this->max_cipher_pool_size = config["max_cipher_pool_size"].uint64_value();
if (!this->max_cipher_pool_size)
this->max_cipher_pool_size = 256;
if (config["proto_checksums"].is_null())
this->use_proto_checksums = MSGR_CSUM_PAYLOAD;
else if (config["proto_checksums"].is_bool())
this->use_proto_checksums = config["proto_checksums"].bool_value() ? MSGR_CSUM_FULL : 0;
else if (config["proto_checksums"].string_value() != "")
this->use_proto_checksums = config["proto_checksums"].string_value() == "full" ? MSGR_CSUM_FULL : MSGR_CSUM_PAYLOAD;
else
this->use_proto_checksums = 0;
this->receive_buffer_size = (uint32_t)config["tcp_header_buffer_size"].uint64_value();
if (!this->receive_buffer_size || this->receive_buffer_size > 1024*1024*1024)
this->receive_buffer_size = 65536;
@@ -275,6 +290,8 @@ void osd_messenger_t::parse_config(const json11::Json & config, bool init)
osd_tls_ca = config["osd_ca"].string_value();
client_tls_ca = config["client_ca"].string_value();
}
this->use_proto_checksums = parse_proto_checksums(config["proto_checksums"], MSGR_CSUM_PAYLOAD);
this->force_proto_checksums = parse_proto_checksums(config["force_proto_checksums"], tls_cert != "" ? MSGR_CSUM_PAYLOAD : 0);
if (!osd_num)
this->iothread_count = (uint32_t)config["client_iothread_count"].uint64_value();
else
@@ -653,6 +670,21 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
err = !check_config_hook(cl, config);
}
}
if (!err && use_proto_checksums)
{
auto peer_csums = config["features"]["proto_checksums"].uint64_value();
if (peer_csums == MSGR_CSUM_GCM && use_proto_checksums == MSGR_CSUM_GCM && cl->gcm_enabled)
cl->proto_csum_status = MSGR_CSUM_GCM;
else if (peer_csums == MSGR_CSUM_FULL && use_proto_checksums == MSGR_CSUM_FULL)
cl->proto_csum_status = MSGR_CSUM_FULL;
else if (peer_csums && use_proto_checksums)
cl->proto_csum_status = MSGR_CSUM_PAYLOAD;
if (cl->proto_csum_status < force_proto_checksums)
{
fprintf(stderr, "Error: OSD %ju use_proto_checksums security level is lower than force_proto_checksums\n", cl->osd_num);
err = true;
}
}
if (err)
{
osd_num_t peer_osd = cl->osd_num;
@@ -661,14 +693,6 @@ void osd_messenger_t::check_peer_config(osd_client_t *cl)
delete op;
return;
}
if (use_proto_checksums)
{
auto peer_csums = config["features"]["proto_checksums"].uint64_value();
if (peer_csums == MSGR_CSUM_FULL && use_proto_checksums == MSGR_CSUM_FULL)
cl->proto_csum_status = MSGR_CSUM_FULL;
else if (peer_csums && use_proto_checksums)
cl->proto_csum_status = MSGR_CSUM_PAYLOAD;
}
#ifdef WITH_RDMA
if (!use_rdmacm && cl->rdma_conn && config["rdma_address"].is_string())
{
+3 -4
View File
@@ -41,7 +41,8 @@
#define MSGR_CSUM_PAYLOAD 1
#define MSGR_CSUM_FULL 2
#define MSGR_CSUM_NEG 4
#define MSGR_CSUM_GCM 4
#define MSGR_CSUM_NEG 8
#define VITASTOR_CONFIG_PATH "/etc/vitastor/vitastor.conf"
@@ -92,9 +93,6 @@ struct osd_client_t
msgr_rdma_connection_t *rdma_conn = NULL;
#endif
uint8_t *ssl_out_buf = NULL;
size_t ssl_out_buf_size = 0, ssl_out_buf_cap = 0;
bool gcm_enabled = false;
msgr_handshake_i *hs = NULL;
msgr_handshake_result_t hs_result;
@@ -290,6 +288,7 @@ public:
std::vector<std::string> all_osd_networks;
std::vector<addr_mask_t> all_osd_network_masks;
int use_proto_checksums = 0;
int force_proto_checksums = 0;
// op statistics
osd_op_stats_t stats, recovery_stats;
+68 -8
View File
@@ -44,9 +44,10 @@ op_aes_xts_encrypt_t::~op_aes_xts_encrypt_t()
free(tmp);
}
void op_aes_xts_encrypt_t::start(uint8_t *key, uint64_t start_offset, size_t block_size)
void op_aes_xts_encrypt_t::start(osd_client_t *cl, uint8_t *key, uint64_t start_offset, size_t block_size)
{
assert(!encrypted);
this->cl = cl;
this->start_offset = start_offset;
this->key = key;
this->block_size = block_size;
@@ -91,6 +92,28 @@ void op_aes_xts_encrypt_t::encrypt_block(uint8_t *in, uint8_t *out)
#endif
}
static inline void copy_or_gcm(osd_client_t *cl, uint8_t *out, uint8_t *in, size_t n)
{
if (cl->proto_csum_status != MSGR_CSUM_GCM)
memcpy(out, in, n);
else
{
#ifdef WITH_ISAL_CRYPTO
int r = isal_aes_gcm_enc_256_update(&cl->my_key_isal, cl->enc_ctx, out, in, n);
assert(!r);
#else
int actual_out;
if (EVP_EncryptUpdate(cl->enc_ctx, out, &actual_out, in, n) != 1)
{
fprintf(stderr, "EncryptUpdate error: ");
ERR_print_errors_fp(stderr);
abort();
}
assert(actual_out == n);
#endif
}
}
void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out)
{
// Fucking AES-XTS implementations (all of them) don't have streaming support,
@@ -104,7 +127,7 @@ void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
assert(tmp);
if (max_out > block_size - tmp_pos)
max_out = block_size - tmp_pos;
memcpy(out, tmp + tmp_pos, max_out);
copy_or_gcm(cl, out, tmp + tmp_pos, max_out);
done_out += max_out;
tmp_pos += max_out;
if (tmp_pos >= block_size)
@@ -137,7 +160,7 @@ void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
memcpy(tmp + offset%block_size, in, max_in);
encrypt_block(tmp, tmp);
encrypted = true;
memcpy(out, tmp, max_out);
copy_or_gcm(cl, out, tmp, max_out);
tmp_pos = max_out;
done_in += max_in-1;
offset += max_in;
@@ -147,6 +170,8 @@ void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
{
// Full block - simplest case
encrypt_block(in, out);
if (cl->proto_csum_status == MSGR_CSUM_GCM)
copy_or_gcm(cl, out, out, block_size);
done_in += block_size;
offset += block_size;
done_out += block_size;
@@ -158,6 +183,8 @@ void op_aes_xts_encrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
max_in = block_size - offset%block_size;
memcpy(tmp + offset%block_size, in, max_in);
encrypt_block(tmp, out);
if (cl->proto_csum_status == MSGR_CSUM_GCM)
copy_or_gcm(cl, out, out, block_size);
done_in += max_in;
offset += max_in;
done_out += block_size;
@@ -196,9 +223,10 @@ op_aes_xts_decrypt_t::~op_aes_xts_decrypt_t()
free(tmp);
}
void op_aes_xts_decrypt_t::start(uint8_t **key_chain, size_t chain_size, void *key_indexes, uint64_t start_offset, size_t block_size)
void op_aes_xts_decrypt_t::start(osd_client_t *cl, uint8_t **key_chain, size_t chain_size, void *key_indexes, uint64_t start_offset, size_t block_size)
{
assert(!decrypted);
this->cl = cl;
this->start_offset = start_offset;
this->key_chain = key_chain;
this->chain_size = chain_size;
@@ -269,6 +297,23 @@ void op_aes_xts_decrypt_t::decrypt_block(uint8_t *in, uint8_t *out)
#endif
}
static inline void gcm_dec(osd_client_t *cl, uint8_t *out, uint8_t *in, size_t n)
{
#ifdef WITH_ISAL_CRYPTO
int r = isal_aes_gcm_dec_256_update(&cl->peer_key_isal, cl->dec_ctx, out, in, n);
assert(!r);
#else
int actual_out;
if (EVP_DecryptUpdate(cl->dec_ctx, out, &actual_out, in, n) != 1)
{
fprintf(stderr, "DecryptUpdate error: ");
ERR_print_errors_fp(stderr);
abort();
}
assert(actual_out == n);
#endif
}
// out may be NULL, in this case all input is still decrypted to calculate checksums,
// but part of it is skipped and not copied to out
void op_aes_xts_decrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out)
@@ -316,6 +361,8 @@ void op_aes_xts_decrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
}
max_in = block_size - offset%block_size;
memcpy(tmp + offset%block_size, in, max_in);
if (cl->proto_csum_status == MSGR_CSUM_GCM)
gcm_dec(cl, tmp, tmp, block_size);
decrypt_block(tmp, tmp);
decrypted = true;
if (out)
@@ -328,7 +375,18 @@ void op_aes_xts_decrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
else if (!(offset%block_size))
{
// Full block - simplest case
if (out)
if (cl->proto_csum_status == MSGR_CSUM_GCM)
{
if (!tmp)
{
tmp = (uint8_t*)malloc_or_die(block_size);
tmp_size = block_size;
}
gcm_dec(cl, tmp, in, block_size);
if (out)
decrypt_block(tmp, out);
}
else
decrypt_block(in, out);
done_in += block_size;
offset += block_size;
@@ -341,6 +399,8 @@ void op_aes_xts_decrypt_t::update(uint8_t *in, size_t max_in, uint8_t *out, size
max_in = block_size - offset%block_size;
memcpy(tmp + offset%block_size, in, max_in);
assert(out);
if (cl->proto_csum_status == MSGR_CSUM_GCM)
gcm_dec(cl, tmp, tmp, block_size);
decrypt_block(tmp, out);
done_in += max_in;
offset += max_in;
@@ -365,7 +425,7 @@ void osd_messenger_t::op_encrypted_copy_buf(osd_client_t *cl, uint8_t *enc_buf,
else
cl->xts_enc_ctx = new op_aes_xts_encrypt_t();
assert(cl->write_op->enc->key_chain[0]);
cl->xts_enc_ctx->start(cl->write_op->enc->key_chain[0], cl->write_op->req.rw.offset, cl->write_op->enc->bitmap_granularity);
cl->xts_enc_ctx->start(cl, cl->write_op->enc->key_chain[0], cl->write_op->req.rw.offset, cl->write_op->enc->bitmap_granularity);
}
while (done_plain < plain_len && done_enc < enc_len)
{
@@ -411,7 +471,7 @@ void osd_messenger_t::op_decrypt_start(osd_client_t* cl)
cl->xts_dec_ctx = new op_aes_xts_decrypt_t();
auto & enc = cl->read_op->enc;
assert(cl->read_op->req.hdr.opcode == OSD_OP_READ);
cl->xts_dec_ctx->start(enc->key_chain, enc->chain_size,
cl->xts_dec_ctx->start(cl, enc->key_chain, enc->chain_size,
(cl->read_op->req.rw.flags & OSD_OP_RETURN_CHAIN) ? (uint8_t*)cl->read_op->bitmap + enc->read_chain_bitmap_pos : 0,
cl->read_op->req.rw.offset, enc->bitmap_granularity);
}
@@ -561,7 +621,7 @@ void osd_messenger_t::init_tls_client(osd_client_t *cl)
cl->gcm_enabled = true;
cl->hs = hs_ctx->create();
cl->hs->init(cl->is_incoming);
if (cl->hs->get_out().size())
if (cl->hs->out_size())
{
if (cl->write_state == 0)
{
+6 -2
View File
@@ -12,11 +12,14 @@
#include <openssl/evp.h>
#include <openssl/err.h>
struct osd_client_t;
class op_aes_xts_encrypt_t
{
#ifndef WITH_ISAL_CRYPTO
EVP_CIPHER_CTX *ctx = NULL;
#endif
osd_client_t *cl = NULL;
uint64_t start_offset = 0;
uint8_t *key = NULL;
size_t offset = 0;
@@ -32,7 +35,7 @@ public:
op_aes_xts_encrypt_t();
~op_aes_xts_encrypt_t();
void start(uint8_t *key, uint64_t start_offset, size_t block_size);
void start(osd_client_t *cl, uint8_t *key, uint64_t start_offset, size_t block_size);
void update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out);
};
@@ -43,6 +46,7 @@ class op_aes_xts_decrypt_t
#ifndef WITH_ISAL_CRYPTO
EVP_CIPHER_CTX *ctx = NULL;
#endif
osd_client_t *cl = NULL;
uint64_t start_offset = 0;
uint8_t **key_chain = NULL;
size_t chain_size = 0;
@@ -61,7 +65,7 @@ public:
op_aes_xts_decrypt_t();
~op_aes_xts_decrypt_t();
void start(uint8_t **key_chain, size_t chain_size, void *key_indexes, uint64_t start_offset, size_t block_size);
void start(osd_client_t *cl, uint8_t **key_chain, size_t chain_size, void *key_indexes, uint64_t start_offset, size_t block_size);
void update(uint8_t *in, size_t max_in, uint8_t *out, size_t max_out, size_t & done_in, size_t & done_out);
};
+56 -11
View File
@@ -18,6 +18,7 @@
#include <openssl/err.h>
#include "msgr_handshake.h"
#include "malloc_or_die.h"
#include "openssl_util.h"
#include "str_util.h"
@@ -84,7 +85,8 @@ class msgr_handshake_t: public msgr_handshake_i
std::vector<uint8_t> full_handshake;
std::vector<uint8_t> in_buf;
std::vector<uint8_t> out_buf;
uint8_t *out_buf = NULL;
size_t out_buf_size = 0;
std::string error;
int state = 0;
@@ -125,7 +127,10 @@ public:
bool init(bool server_mode) override;
ssize_t handle(uint8_t* in_buf, size_t in_size) override;
bool done() override;
std::vector<uint8_t>& get_out() override;
uint8_t *get_out() override;
size_t out_size() override;
void eat_out(size_t n) override;
void reset_out() override;
msgr_handshake_result_t get_result() override;
std::string get_error() override;
};
@@ -247,6 +252,8 @@ bool msgr_handshake_ctx_t::derive_kdf(const uint8_t* insecret, size_t insecret_l
msgr_handshake_t::~msgr_handshake_t()
{
if (out_buf)
free(out_buf);
if (peer_cert)
X509_free(peer_cert);
if (ec_key)
@@ -288,6 +295,12 @@ static void copy_to(std::vector<uint8_t> & buf, const void* src, uint32_t len)
memcpy(buf.data() + old_size, src, len);
}
static void copy_to_raw(uint8_t* & buf, const void* src, size_t len)
{
memcpy(buf, src, len);
buf += len;
}
static void copy_to_with_len(std::vector<uint8_t> & buf, const void* src, uint32_t len)
{
copy_to(buf, &len, sizeof(len));
@@ -442,9 +455,10 @@ bool msgr_handshake_t::make_client_init()
size_t key_len = EVP_PKEY_get1_encoded_public_key(ec_key, &key);
if (!key_len)
return on_error("EVP_PKEY_get1_encoded_public_key: ");
const size_t old_len = out_buf.size();
out_buf.resize(out_buf.size() + key_len + sizeof(msgr_handshake_hdr_t));
uint8_t *buf = out_buf.data() + old_len;
const size_t old_out_size = out_buf_size;
out_buf_size += key_len + sizeof(msgr_handshake_hdr_t);
out_buf = (uint8_t*)realloc_or_die(out_buf, out_buf_size);
uint8_t *buf = out_buf + old_out_size;
msgr_handshake_hdr_t *hdr = (msgr_handshake_hdr_t *)buf;
hdr->msg_len = key_len + sizeof(msgr_handshake_hdr_t);
hdr->magic = MSGR_HS_MAGIC;
@@ -485,9 +499,12 @@ bool msgr_handshake_t::make_server_reply()
}
// Construct message
hdr.msg_len = sizeof(msgr_handshake_hdr_t) + 4 + key_len + encrypt_data.size();
copy_to(out_buf, &hdr, sizeof(hdr));
copy_to_with_len(out_buf, key, key_len);
copy_to(out_buf, encrypt_data.data(), encrypt_data.size());
out_buf = (uint8_t*)realloc_or_die(out_buf, (out_buf_size += hdr.msg_len));
uint8_t *cur = out_buf + out_buf_size - hdr.msg_len;
copy_to_raw(cur, &hdr, sizeof(hdr));
copy_to_raw(cur, &key_len, 4);
copy_to_raw(cur, key, key_len);
copy_to_raw(cur, encrypt_data.data(), encrypt_data.size());
OPENSSL_free(key);
return true;
}
@@ -510,8 +527,10 @@ bool msgr_handshake_t::make_client_reply()
return false;
// Construct message
hdr.msg_len = sizeof(msgr_handshake_hdr_t) + encrypt_data.size();
copy_to(out_buf, &hdr, sizeof(hdr));
copy_to(out_buf, encrypt_data.data(), encrypt_data.size());
out_buf = (uint8_t*)realloc_or_die(out_buf, (out_buf_size += hdr.msg_len));
uint8_t *cur = out_buf + out_buf_size - hdr.msg_len;
copy_to_raw(cur, &hdr, sizeof(hdr));
copy_to_raw(cur, encrypt_data.data(), encrypt_data.size());
return true;
}
@@ -742,11 +761,37 @@ bool msgr_handshake_t::done()
return (state == MSGR_HS_DONE);
}
std::vector<uint8_t>& msgr_handshake_t::get_out()
uint8_t *msgr_handshake_t::get_out()
{
return out_buf;
}
size_t msgr_handshake_t::out_size()
{
return out_buf_size;
}
void msgr_handshake_t::eat_out(size_t n)
{
if (n >= out_buf_size)
{
free(out_buf);
out_buf = NULL;
out_buf_size = 0;
}
else
{
memmove(out_buf, out_buf + n, out_buf_size - n);
out_buf_size -= n;
}
}
void msgr_handshake_t::reset_out()
{
out_buf = NULL;
out_buf_size = 0;
}
msgr_handshake_result_t msgr_handshake_t::get_result()
{
if (state != MSGR_HS_DONE)
+4 -1
View File
@@ -35,7 +35,10 @@ public:
virtual bool init(bool server_mode) = 0;
virtual ssize_t handle(uint8_t* in_buf, size_t in_size) = 0;
virtual bool done() = 0;
virtual std::vector<uint8_t> & get_out() = 0;
virtual uint8_t *get_out() = 0;
virtual size_t out_size() = 0;
virtual void eat_out(size_t n) = 0;
virtual void reset_out() = 0;
virtual msgr_handshake_result_t get_result() = 0;
virtual std::string get_error() = 0;
};
-4
View File
@@ -24,10 +24,6 @@ osd_op_t::~osd_op_t()
// So we don't reuse it, but free it every time
free(buf);
}
if (enc_buf)
{
free(enc_buf);
}
}
bool osd_op_t::is_recovery_related()
-1
View File
@@ -186,7 +186,6 @@ struct __attribute__((visibility("default"))) osd_op_t
uint8_t *bitmap_buf = NULL;
void *rmw_buf = NULL;
std::shared_ptr<osd_op_enc_t> enc;
uint8_t *enc_buf = NULL;
uint64_t csum = 0; // network layer checksum
osd_primary_op_data_t* op_data = NULL;
std::function<void(osd_op_t*)> callback;
+5 -2
View File
@@ -735,9 +735,12 @@ void osd_messenger_t::handle_rdma_events(msgr_rdma_context_t *rdma_context)
if (rc->send_done_pos == rc->send_out_size)
rc->send_done_pos = 0;
assert(rc->send_done_pos < rc->send_out_size);
while (cl->send_free_ops.front())
while (osd_op_t *op = cl->send_free_ops.front())
{
delete cl->send_free_ops.front();
if (!((size_t)op & 7))
delete op;
else
free((void*)((size_t)op & ~(size_t)7));
cl->send_free_ops.pop_front();
}
cl->send_free_ops.pop_front();
+28 -40
View File
@@ -170,41 +170,17 @@ public:
if (done >= bufsize)
return false;
size_t n = dst_len-from;
if (!(flags & RDR_GCM))
if (n > bufsize-done)
n = bufsize-done;
if (flags & RDR_XTS)
{
if (n > bufsize-done)
n = bufsize-done;
if (flags & RDR_XTS)
{
msgr->op_decrypted_copy_buf(cl, curbuf, bufsize, dst, dst_len, from, done);
n = 0;
}
else
{
if (cl->read_csum_state && !(flags & RDR_NO_CSUM))
{
// data may be skipped if dst == NULL but checksum is still calculated
XXH3_64bits_update(cl->read_csum_state, curbuf+done, n);
}
// Here, dst == NULL is allowed
if (dst != NULL)
memcpy(dst+from, curbuf+done, n);
done += n;
}
cl->read_op_pos += n;
from += n;
if (from < dst_len)
{
return false;
}
msgr->op_decrypted_copy_buf(cl, curbuf, bufsize, dst, dst_len, from, done);
n = 0;
}
else
else if (flags & RDR_GCM)
{
// Here, dst == NULL is not allowed
assert(dst != NULL);
size_t n = dst_len-from;
if (n > bufsize-done)
n = bufsize-done;
#ifdef WITH_ISAL_CRYPTO
int r = isal_aes_gcm_dec_256_update(&cl->peer_key_isal, cl->dec_ctx, dst+from, curbuf+done, n);
assert(!r);
@@ -223,12 +199,24 @@ public:
XXH3_64bits_update(cl->read_csum_state, dst+from, n);
}
done += n;
from += n;
cl->read_op_pos += n;
if (from < dst_len)
}
else
{
if (cl->read_csum_state && !(flags & RDR_NO_CSUM))
{
return false;
// data may be skipped if dst == NULL but checksum is still calculated
XXH3_64bits_update(cl->read_csum_state, curbuf+done, n);
}
// Here, dst == NULL is allowed
if (dst != NULL)
memcpy(dst+from, curbuf+done, n);
done += n;
}
cl->read_op_pos += n;
from += n;
if (from < dst_len)
{
return false;
}
from = 0;
return true;
@@ -605,7 +593,7 @@ bool osd_messenger_t::handle_read_buffer(osd_client_t *cl, uint8_t *curbuf, size
}
curbuf += done;
bufsize -= done;
if (cl->hs->get_out().size())
if (cl->hs->out_size())
{
if (cl->write_state == 0)
{
@@ -613,7 +601,7 @@ bool osd_messenger_t::handle_read_buffer(osd_client_t *cl, uint8_t *curbuf, size
write_ready_clients.push_back(cl->client_id);
}
}
if (cl->hs->done() && !cl->hs->get_out().size())
if (cl->hs->done() && !cl->hs->out_size())
{
// Delete hs when done and nothing to send
delete cl->hs;
@@ -933,7 +921,7 @@ bool osd_messenger_t::op_read_from(osd_client_t *cl, msgr_op_reader_t & rdr)
{
if (!rdr.read((uint8_t*)op->bitmap, op->req.sec_rw.attr_len, RDR_GCM))
return false;
if (!rdr.read((uint8_t*)op->buf, op->req.sec_rw.len, 0))
if (!rdr.read((uint8_t*)op->buf, op->req.sec_rw.len, cl->proto_csum_status == MSGR_CSUM_GCM ? RDR_GCM : 0))
return false;
}
else if (op->req.hdr.opcode == OSD_OP_SEC_STABILIZE ||
@@ -949,7 +937,7 @@ bool osd_messenger_t::op_read_from(osd_client_t *cl, msgr_op_reader_t & rdr)
}
else if (op->req.hdr.opcode == OSD_OP_WRITE)
{
if (!rdr.read((uint8_t*)op->buf, op->req.rw.len, 0))
if (!rdr.read((uint8_t*)op->buf, op->req.rw.len, cl->proto_csum_status == MSGR_CSUM_GCM ? RDR_GCM : 0))
return false;
}
else if (op->req.hdr.opcode == OSD_OP_SHOW_CONFIG)
@@ -973,7 +961,7 @@ switched_type:
if (op->reply.hdr.retval > 0)
{
for (int i = 0; i < op->iov.count; i++)
if (!rdr.read((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len, 0))
if (!rdr.read((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len, (cl->proto_csum_status == MSGR_CSUM_GCM ? RDR_GCM : 0)))
return false;
}
}
@@ -987,7 +975,7 @@ switched_type:
if (op->reply.hdr.retval > 0)
{
for (int i = 0; i < op->iov.count; i++)
if (!rdr.read((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len, (op->enc ? RDR_XTS : 0)))
if (!rdr.read((uint8_t*)op->iov.buf[i].iov_base, op->iov.buf[i].iov_len, (op->enc ? RDR_XTS : 0) | (cl->proto_csum_status == MSGR_CSUM_GCM ? RDR_GCM : 0)))
return false;
}
}
+102 -138
View File
@@ -15,6 +15,8 @@
#define WR_XTS 2
#define WR_NO_CSUM 4
#define GCM_TMP_BUF_SIZE 4096
class msgr_op_writer_t
{
public:
@@ -180,28 +182,11 @@ public:
from -= src_len;
return true;
}
if (!(flags & WR_GCM))
if (flags & WR_XTS)
{
if (flags & WR_XTS)
{
msgr->op_encrypted_copy_buf(cl, curbuf, bufsize, src, src_len, from, done);
}
else
{
size_t n = src_len-from;
if (n > bufsize-done)
n = bufsize-done;
if (!n)
return false;
if (cl->write_csum_state && !(flags & WR_NO_CSUM))
XXH3_64bits_update(cl->write_csum_state, src+from, n);
memcpy(curbuf+done, src+from, n);
done += n;
cl->write_op_pos += n;
from += n;
}
msgr->op_encrypted_copy_buf(cl, curbuf, bufsize, src, src_len, from, done);
}
else
else if (flags & WR_GCM)
{
size_t n = src_len-from;
if (n > bufsize-done)
@@ -227,6 +212,20 @@ public:
cl->write_op_pos += n;
from += n;
}
else
{
size_t n = src_len-from;
if (n > bufsize-done)
n = bufsize-done;
if (!n)
return false;
if (cl->write_csum_state && !(flags & WR_NO_CSUM))
XXH3_64bits_update(cl->write_csum_state, src+from, n);
memcpy(curbuf+done, src+from, n);
done += n;
cl->write_op_pos += n;
from += n;
}
if (from < src_len)
return false;
from = 0;
@@ -297,63 +296,49 @@ class get_op_writer_t: public msgr_op_writer_t
osd_client_t* cl;
size_t from;
size_t done;
size_t op_enc;
size_t enc_size;
size_t done_enc;
uint8_t *enc_buf;
public:
get_op_writer_t(osd_messenger_t* msgr, osd_client_t* cl, uint8_t*, size_t):
msgr(msgr), cl(cl), from(cl->write_op_pos), done(0), enc_size(0), done_enc(0)
msgr(msgr), cl(cl), from(cl->write_op_pos), done(0), enc_size(0), done_enc(0), enc_buf(NULL)
{
}
static void ssl_extend_buf(osd_client_t *cl, size_t more = 0)
{
size_t min_cap = cl->ssl_out_buf_size*2;
if (min_cap < cl->ssl_out_buf_size+more)
min_cap = cl->ssl_out_buf_size+more;
if (min_cap < 16384)
min_cap = 16384;
if (cl->ssl_out_buf_cap < min_cap)
{
uintptr_t old_buf = (uintptr_t)cl->ssl_out_buf;
uintptr_t old_end = old_buf + cl->ssl_out_buf_cap;
cl->ssl_out_buf = (uint8_t*)realloc_or_die(cl->ssl_out_buf, min_cap);
cl->ssl_out_buf_cap = min_cap;
for (auto & iov: cl->send_list)
{
if ((uintptr_t)iov.iov_base >= old_buf && (uintptr_t)iov.iov_base < old_end)
iov.iov_base = cl->ssl_out_buf + ((uintptr_t)iov.iov_base - old_buf);
}
}
}
static void send_out_buf(osd_client_t *cl, size_t n)
{
if (cl->send_list.size() > 0)
{
iovec& last = cl->send_list.back();
if (last.iov_base+last.iov_len == cl->ssl_out_buf+cl->ssl_out_buf_size)
{
last.iov_len += n;
cl->ssl_out_buf_size += n;
return;
}
}
cl->send_list.push_back((iovec){ .iov_base = cl->ssl_out_buf+cl->ssl_out_buf_size, .iov_len = n });
cl->ssl_out_buf_size += n;
}
void reset()
{
op_enc = 0;
from = cl->write_op_pos;
enc_size = 0;
done_enc = 0;
if (cl->gcm_enabled)
{
gcm_op_writer_t::init_ctx(msgr, cl);
}
}
void extend_tmp(size_t n)
{
if (!enc_buf || done_enc + n > enc_size)
{
enc_size = n < GCM_TMP_BUF_SIZE ? GCM_TMP_BUF_SIZE : n;
enc_buf = (uint8_t*)malloc_or_die(enc_size);
done_enc = 0;
assert(!((size_t)enc_buf & 7));
cl->send_free_ops.push_back((osd_op_t*)((size_t)enc_buf | 1));
}
}
void send_tmp(size_t n)
{
if (cl->send_list.size() && cl->send_list.back().iov_base == (enc_buf + done_enc))
cl->send_list.back().iov_len += n;
else
cl->send_list.push_back((iovec){ .iov_base = enc_buf + done_enc, .iov_len = n });
done += n;
done_enc += n;
}
bool write(uint8_t *src, size_t src_len, int flags) override
{
if (from >= src_len)
@@ -362,63 +347,54 @@ public:
from -= src_len;
return true;
}
if (cl->send_list.size() >= IOV_MAX)
if (cl->send_list.size() >= IOV_MAX-1)
{
// Make sure tag always fits
return false;
}
if (flags & WR_GCM)
{
if (cl->gcm_enabled)
{
// Encrypt data to client's temporary output buffer (all at once)
size_t n = src_len-from;
ssl_extend_buf(cl, n);
#ifdef WITH_ISAL_CRYPTO
int r = isal_aes_gcm_enc_256_update(&cl->my_key_isal, cl->enc_ctx, cl->ssl_out_buf+cl->ssl_out_buf_size, src+from, n);
assert(!r);
#else
int actual_out;
if (EVP_EncryptUpdate(cl->enc_ctx, cl->ssl_out_buf+cl->ssl_out_buf_size, &actual_out, src+from, n) != 1)
{
fprintf(stderr, "EncryptUpdate error: ");
ERR_print_errors_fp(stderr);
abort();
}
assert(actual_out == n);
#endif
if (cl->write_csum_state && !(flags & WR_NO_CSUM))
XXH3_64bits_update(cl->write_csum_state, src+from, n);
send_out_buf(cl, n);
done += n;
cl->write_op_pos += n;
from += n;
if (from < src_len)
return false;
from = 0;
return true;
}
}
if (flags & WR_XTS)
{
if (!cl->write_op->enc_buf)
// Allocate a temporary buffer and encrypt data to it
if (!op_enc)
{
if (cl->send_list.size() >= IOV_MAX-1)
{
// Make sure that 1 encrypted buffer and 1 checksum fits
return false;
}
// No way except than to allocate a temporary buffer and encrypt data to it
assert(cl->write_op->req.hdr.opcode == OSD_OP_WRITE);
enc_size = cl->write_op->req.rw.len - from + (from % 16);
assert(enc_size > 0);
cl->write_op->enc_buf = (uint8_t*)malloc_or_die(enc_size);
cl->send_list.push_back((iovec){ .iov_base = cl->write_op->enc_buf, .iov_len = enc_size });
done += enc_size;
op_enc = cl->write_op->req.rw.len - from + (from % 16);
assert(op_enc > 0);
extend_tmp(op_enc);
}
assert(enc_size > 0);
msgr->op_encrypted_copy_buf(cl, cl->write_op->enc_buf, enc_size, src, src_len, from, done_enc);
size_t new_done = done_enc;
msgr->op_encrypted_copy_buf(cl, enc_buf, enc_size, src, src_len, from, new_done);
send_tmp(new_done-done_enc);
assert(from == src_len);
}
else if ((flags & WR_GCM) && cl->gcm_enabled)
{
// Allocate a temporary buffer and encrypt data to it
size_t n = src_len-from;
extend_tmp(n);
#ifdef WITH_ISAL_CRYPTO
int r = isal_aes_gcm_enc_256_update(&cl->my_key_isal, cl->enc_ctx, enc_buf+done_enc, src+from, n);
assert(!r);
#else
int actual_out;
if (EVP_EncryptUpdate(cl->enc_ctx, enc_buf+done_enc, &actual_out, src+from, n) != 1)
{
fprintf(stderr, "EncryptUpdate error: ");
ERR_print_errors_fp(stderr);
abort();
}
assert(actual_out == n);
#endif
if (cl->write_csum_state && !(flags & WR_NO_CSUM))
XXH3_64bits_update(cl->write_csum_state, src+from, n);
send_tmp(n);
cl->write_op_pos += n;
from += n;
if (from < src_len)
return false;
from = 0;
return true;
}
else
{
if (cl->write_csum_state && !(flags & WR_NO_CSUM))
@@ -435,13 +411,10 @@ public:
{
if (cl->enc_ctx)
{
if (cl->send_list.size() >= IOV_MAX)
return false;
// Tag is 16 bytes
ssl_extend_buf(cl, 16);
gcm_op_writer_t::write_tag_to(msgr, cl, cl->ssl_out_buf+cl->ssl_out_buf_size);
send_out_buf(cl, 16);
done += 16;
extend_tmp(16);
gcm_op_writer_t::write_tag_to(msgr, cl, enc_buf + done_enc);
send_tmp(16);
gcm_op_writer_t::free_ctx(msgr, cl);
}
return true;
@@ -527,14 +500,15 @@ bool osd_messenger_t::try_send(osd_client_t *cl)
if (cl->hs)
{
// Send handshake message
if (cl->hs->get_out().size())
if (cl->hs->out_size())
{
get_op_writer_t::ssl_extend_buf(cl, cl->hs->get_out().size());
memcpy(cl->ssl_out_buf+cl->ssl_out_buf_size, cl->hs->get_out().data(), cl->hs->get_out().size());
get_op_writer_t::send_out_buf(cl, cl->hs->get_out().size());
cl->hs->get_out().clear();
uint8_t *out = cl->hs->get_out();
cl->send_list.push_back((iovec){ .iov_base = out, .iov_len = cl->hs->out_size() });
assert(!((size_t)out & 7));
cl->send_free_ops.push_back((osd_op_t*)((size_t)out | 1));
cl->hs->reset_out();
}
if (!cl->hs->get_out().size() && cl->hs->done())
if (!cl->hs->out_size() && cl->hs->done())
{
delete cl->hs;
cl->hs = NULL;
@@ -621,13 +595,13 @@ size_t osd_messenger_t::copy_ops_to(osd_client_t *cl, uint8_t *dst, size_t dst_l
{
// Send handshake message
size_t n = 0;
if (cl->hs->get_out().size())
if (cl->hs->out_size())
{
n = cl->hs->get_out().size() < dst_len ? cl->hs->get_out().size() : dst_len;
memcpy(dst, cl->hs->get_out().data(), n);
cl->hs->get_out().erase(cl->hs->get_out().begin(), cl->hs->get_out().begin() + n);
n = cl->hs->out_size() < dst_len ? cl->hs->out_size() : dst_len;
memcpy(dst, cl->hs->get_out(), n);
cl->hs->eat_out(n);
}
if (!cl->hs->get_out().size() && cl->hs->done())
if (!cl->hs->out_size() && cl->hs->done())
{
delete cl->hs;
cl->hs = NULL;
@@ -750,24 +724,14 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
for (auto op: cl->send_free_ops)
{
if (more)
{
assert(!((size_t)op & 7));
cl->zc_free_list.push_back(op);
}
else
else if (!((size_t)op & 7))
delete op;
else
free((void*)((size_t)op & ~(size_t)7));
}
if (more)
{
if (cl->ssl_out_buf_size)
{
cl->zc_free_list.push_back((osd_op_t*)((size_t)cl->ssl_out_buf | 1));
cl->ssl_out_buf = NULL;
cl->ssl_out_buf_cap = 0;
}
cl->zc_free_list.push_back(NULL); // end marker
}
cl->ssl_out_buf_size = 0;
cl->send_free_ops.clear();
cl->write_state = 0;
if (cl->write_op || cl->write_ops.size())
@@ -875,7 +839,7 @@ bool osd_messenger_t::op_write_to(osd_client_t *cl, msgr_op_writer_t & wr)
for (int i = 0; i < cl->write_op->iov.count; i++)
{
auto & iov = cl->write_op->iov.buf[i];
if (!wr.write((uint8_t*)iov.iov_base, iov.iov_len, (op->enc ? WR_XTS : 0)))
if (!wr.write((uint8_t*)iov.iov_base, iov.iov_len, (op->enc ? WR_XTS : 0) | (cl->proto_csum_status == MSGR_CSUM_GCM ? WR_GCM : 0)))
return false;
}
}
+4 -6
View File
@@ -202,7 +202,10 @@ osd_client_t::~osd_client_t()
{
if (op)
{
delete op;
if (!((size_t)op & 7))
delete op;
else
free((void*)((size_t)op & ~(size_t)7));
}
}
for (osd_op_t *op: zc_free_list)
@@ -243,11 +246,6 @@ osd_client_t::~osd_client_t()
#endif
dec_ctx = NULL;
}
if (ssl_out_buf)
{
free(ssl_out_buf);
ssl_out_buf = NULL;
}
if (hs)
{
delete hs;
+11 -2
View File
@@ -376,14 +376,23 @@ void osd_t::exec_show_config(osd_op_t *cur_op)
if (msgr.use_proto_checksums)
{
auto peer_csums = req_json["features"]["proto_checksums"].uint64_value();
if (peer_csums == MSGR_CSUM_FULL || peer_csums == MSGR_CSUM_PAYLOAD)
if (peer_csums == MSGR_CSUM_FULL || peer_csums == MSGR_CSUM_PAYLOAD ||
cl->gcm_enabled && peer_csums == MSGR_CSUM_GCM)
{
if (msgr.use_proto_checksums == MSGR_CSUM_FULL && peer_csums == MSGR_CSUM_FULL)
if (msgr.use_proto_checksums == MSGR_CSUM_GCM && peer_csums == MSGR_CSUM_GCM)
cl->proto_csum_status = MSGR_CSUM_GCM|MSGR_CSUM_NEG;
else if (msgr.use_proto_checksums == MSGR_CSUM_FULL && peer_csums == MSGR_CSUM_FULL)
cl->proto_csum_status = MSGR_CSUM_FULL|MSGR_CSUM_NEG;
else
cl->proto_csum_status = MSGR_CSUM_PAYLOAD|MSGR_CSUM_NEG;
features["proto_checksums"] = msgr.use_proto_checksums;
}
if (peer_csums < msgr.force_proto_checksums)
{
fprintf(stderr, "Error: Client %ju use_proto_checksums security level is lower than force_proto_checksums\n", cl->client_id);
msgr.stop_client(cl->client_id);
return;
}
}
// Expose sensitive configuration values so peers can check them
json11::Json::object wire_config = json11::Json::object {
+12 -10
View File
@@ -663,11 +663,12 @@ void test_msgr_encrypt()
uint8_t *crypt2 = (uint8_t*)malloc_or_die(sz);
uint8_t *key = (uint8_t*)malloc_or_die(64);
RAND_bytes(key, 64);
osd_client_t cl;
// Basic encrypt+decrypt and also get reference data
auto enc = new op_aes_xts_encrypt_t();
enc->start(key, 4096 * 113, 4096);
enc->start(&cl, key, 4096 * 113, 4096);
size_t in_pos = 0;
size_t out_pos = 0;
while (out_pos < sz)
@@ -676,7 +677,7 @@ void test_msgr_encrypt()
}
auto dec = new op_aes_xts_decrypt_t();
dec->start(&key, 1, NULL, 4096 * 113, 4096);
dec->start(&cl, &key, 1, NULL, 4096 * 113, 4096);
in_pos = out_pos = 0;
while (out_pos < sz)
{
@@ -687,7 +688,7 @@ void test_msgr_encrypt()
// Insufficient output encrypt
printf("...insufficient output encrypt\n");
enc->start(key, 4096 * 114, 4096);
enc->start(&cl, key, 4096 * 114, 4096);
in_pos = out_pos = 0;
enc->update(src+4096, 4096, crypt2, 4095, in_pos, out_pos);
assert(in_pos == 4095);
@@ -699,7 +700,7 @@ void test_msgr_encrypt()
// Fragmented encrypt
printf("...fragmented encrypt\n");
enc->start(key, 4096 * 114, 4096);
enc->start(&cl, key, 4096 * 114, 4096);
in_pos = out_pos = 0;
enc->update(src+4096, 2000, crypt2, 4095, in_pos, out_pos);
assert(in_pos == 2000);
@@ -712,7 +713,7 @@ void test_msgr_encrypt()
// Fragmented decrypt
// Input: 1000 + 2000 + 3000 + 2192, output: 500 + 3000 + 1000 + 3000 + 692
printf("...fragmented decrypt\n");
dec->start(&key, 1, NULL, 4096 * 114, 4096);
dec->start(&cl, &key, 1, NULL, 4096 * 114, 4096);
in_pos = out_pos = 0;
dec->update(crypt+4096, 1000, decrypt, 500, in_pos, out_pos);
assert(in_pos == 1000);
@@ -746,7 +747,7 @@ void test_msgr_encrypt()
// Extra size decrypt
// Input: 8192, output: 4096
printf("...extra size decrypt\n");
dec->start(&key, 1, NULL, 4096 * 114, 4096);
dec->start(&cl, &key, 1, NULL, 4096 * 114, 4096);
in_pos = out_pos = 0;
dec->update(crypt+4096, 8192, decrypt, 4096, in_pos, out_pos);
assert(in_pos == 4096);
@@ -776,6 +777,7 @@ void test_msgr_decrypt_chain()
RAND_bytes(key, 64);
uint8_t *key2 = (uint8_t*)malloc_or_die(64);
RAND_bytes(key2, 64);
osd_client_t cl;
// Chained decryption with multiple keys
@@ -783,17 +785,17 @@ void test_msgr_decrypt_chain()
size_t in_pos = 0, out_pos = 0;
auto enc = new op_aes_xts_encrypt_t();
// block 1 with key1
enc->start(key, 4096 * 113, 4096);
enc->start(&cl, key, 4096 * 113, 4096);
enc->update(src, 4096, crypt, 4096, in_pos, out_pos);
assert(in_pos == 4096 && out_pos == 4096);
// block 2 as plain
memcpy(crypt + 4096, src + 4096, 4096);
// block 3 with key2
enc->start(key2, 4096 * 115, 4096);
enc->start(&cl, key2, 4096 * 115, 4096);
enc->update(src + 2*4096, 4096, crypt + 2*4096, 4096, in_pos, out_pos);
assert(in_pos == 2*4096 && out_pos == 2*4096);
// block 4 again with key1
enc->start(key, 4096 * 116, 4096);
enc->start(&cl, key, 4096 * 116, 4096);
enc->update(src + 3*4096, 4096, crypt + 3*4096, 4096, in_pos, out_pos);
assert(in_pos == 3*4096 && out_pos == 3*4096);
@@ -801,7 +803,7 @@ void test_msgr_decrypt_chain()
uint8_t* keys[3] = { key, key2, NULL };
uint8_t chain_info[4] = { 0, 2, 1, 0 };
auto dec = new op_aes_xts_decrypt_t();
dec->start(keys, 3, chain_info, 4096 * 113, 4096);
dec->start(&cl, keys, 3, chain_info, 4096 * 113, 4096);
in_pos = out_pos = 0;
while (out_pos < sz)
{
+1
View File
@@ -63,6 +63,7 @@ OLD=1 ./test_move_reappear.sh
./test_snapshot_chain.sh
SCHEME=ec ./test_snapshot_chain.sh
ENCRYPTED=1 ./test_snapshot_chain.sh
TEST_NAME=enc_gcm ENCRYPTED=1 VITASTOR_CFG=',"proto_checksums":"gcm"' ./test_snapshot_chain.sh
OLD=1 ./test_snapshot_chain.sh
OLD=1 SCHEME=ec ./test_snapshot_chain.sh