Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions mtr/binlog_streaming/include/rs_roundtrip_body.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# ==== Purpose ====
#
# Round-trip MySQL -> Binlog Server storage -> COM_BINLOG_DUMP body shared
# between 'rs_roundtrip' and 'rs_roundtrip_encryption'. Each caller is
# responsible for setting up (and tearing down) the Binlog Server environment
# with or without encryption before sourcing this file.
#
# ==== Usage ====
#
# --source ../include/rs_roundtrip_body.inc
#
# Expected inputs set by set_up_binsrv_environment.inc:
# $binsrv_config_file_path
# $binsrv_replication_source_port
# $stmt_show_binary_log_status

--echo
--echo *** Determining the first binary log name.
--let $first_binlog = query_get_value($stmt_show_binary_log_status, File, 1)

--echo
--echo *** Creating source_db.tbl and inserting the first batch of rows.
CREATE DATABASE source_db;
CREATE TABLE source_db.tbl(
id INT UNSIGNED NOT NULL,
value VARCHAR(32) NOT NULL,
PRIMARY KEY(id)
) ENGINE=InnoDB;
INSERT INTO source_db.tbl VALUES (1, 'one'), (2, 'two'), (3, 'three');

--echo
--echo *** Flushing the source binary log.
FLUSH BINARY LOGS;

--echo
--echo *** Inserting the second batch of rows.
INSERT INTO source_db.tbl VALUES (4, 'four'), (5, 'five'), (6, 'six');

--echo
--echo *** Fetching the source binary logs in position mode.
--exec $BINSRV fetch $binsrv_config_file_path > /dev/null

--echo
--echo *** Starting Binlog Server Utility in background in pull mode.
--let $proc_command_line = $BINSRV pull $binsrv_config_file_path > /dev/null
--source ../include/start_proc_in_background.inc

--echo
--echo *** Waiting for the replication source listener port ready to accept
--echo *** connections.
--let $listening_port = $binsrv_replication_source_port
--source ../include/wait_for_listening_port.inc

--echo
--echo *** Dumping the binary logs from the Binlog Server replication source.
--let $roundtrip_dump = $MYSQL_TMP_DIR/rs_roundtrip.sql
--exec $MYSQL_BINLOG --read-from-remote-server --host=127.0.0.1 --port=$binsrv_replication_source_port --user=rpl --password=password --default-auth=caching_sha2_password $first_binlog > $roundtrip_dump 2>/dev/null

--echo
--echo *** Sending SIGTERM signal to the Binlog Server Utility and waiting for
--echo *** the process to terminate
--source ../include/terminate_proc.inc

--echo
--echo *** Moving source_db.tbl to backup_db.tbl.
CREATE DATABASE backup_db;
RENAME TABLE source_db.tbl TO backup_db.tbl;
DROP DATABASE source_db;

--echo
--echo *** Restoring source_db from the replication source dump.
--exec $MYSQL < $roundtrip_dump

--echo
--echo *** Comparing the restored and backup tables.
--let $diff_tables = source_db.tbl, backup_db.tbl
--source include/diff_tables.inc

--echo
--echo *** Dropping the round-trip databases.
DROP DATABASE source_db;
DROP DATABASE backup_db;

--remove_file $roundtrip_dump
63 changes: 63 additions & 0 deletions mtr/binlog_streaming/r/rs_roundtrip_encryption.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
*** Resetting replication at the very beginning of the test.

*** Generating keyring data file in JSON format.

*** Generating a configuration file in JSON format for the Binlog
*** Server utility.

*** Determining binlog file directory from the server.

*** Creating a temporary directory <BINSRV_STORAGE_PATH> for storing
*** binlog files downloaded via the Binlog Server utility.

*** Determining the first binary log name.

*** Creating source_db.tbl and inserting the first batch of rows.
CREATE DATABASE source_db;
CREATE TABLE source_db.tbl(
id INT UNSIGNED NOT NULL,
value VARCHAR(32) NOT NULL,
PRIMARY KEY(id)
) ENGINE=InnoDB;
INSERT INTO source_db.tbl VALUES (1, 'one'), (2, 'two'), (3, 'three');

*** Flushing the source binary log.
FLUSH BINARY LOGS;

*** Inserting the second batch of rows.
INSERT INTO source_db.tbl VALUES (4, 'four'), (5, 'five'), (6, 'six');

*** Fetching the source binary logs in position mode.

*** Starting Binlog Server Utility in background in pull mode.
include/read_file_to_var.inc

*** Waiting for the replication source listener port ready to accept
*** connections.

*** Dumping the binary logs from the Binlog Server replication source.

*** Sending SIGTERM signal to the Binlog Server Utility and waiting for
*** the process to terminate

*** Moving source_db.tbl to backup_db.tbl.
CREATE DATABASE backup_db;
RENAME TABLE source_db.tbl TO backup_db.tbl;
DROP DATABASE source_db;

*** Restoring source_db from the replication source dump.

*** Comparing the restored and backup tables.
include/diff_tables.inc [source_db.tbl, backup_db.tbl]

*** Dropping the round-trip databases.
DROP DATABASE source_db;
DROP DATABASE backup_db;

*** Removing the Binlog Server utility storage directory.

*** Removing the Binlog Server utility log file.

*** Removing the Binlog Server utility configuration file.

*** Deleting keyring data file in JSON format.
69 changes: 1 addition & 68 deletions mtr/binlog_streaming/t/rs_roundtrip.test
Original file line number Diff line number Diff line change
Expand Up @@ -18,74 +18,7 @@ eval $stmt_reset_binary_logs_and_gtids;
--let $binsrv_replication_mode = position
--source ../include/set_up_binsrv_environment.inc

--echo
--echo *** Determining the first binary log name.
--let $first_binlog = query_get_value($stmt_show_binary_log_status, File, 1)

--echo
--echo *** Creating source_db.tbl and inserting the first batch of rows.
CREATE DATABASE source_db;
CREATE TABLE source_db.tbl(
id INT UNSIGNED NOT NULL,
value VARCHAR(32) NOT NULL,
PRIMARY KEY(id)
) ENGINE=InnoDB;
INSERT INTO source_db.tbl VALUES (1, 'one'), (2, 'two'), (3, 'three');

--echo
--echo *** Flushing the source binary log.
FLUSH BINARY LOGS;

--echo
--echo *** Inserting the second batch of rows.
INSERT INTO source_db.tbl VALUES (4, 'four'), (5, 'five'), (6, 'six');

--echo
--echo *** Fetching the source binary logs in position mode.
--exec $BINSRV fetch $binsrv_config_file_path > /dev/null

--echo
--echo *** Starting Binlog Server Utility in background in pull mode.
--let $proc_command_line = $BINSRV pull $binsrv_config_file_path > /dev/null
--source ../include/start_proc_in_background.inc

--echo
--echo *** Waiting for the replication source listener port ready to accept
--echo *** connections.
--let $listening_port = $binsrv_replication_source_port
--source ../include/wait_for_listening_port.inc

--echo
--echo *** Dumping the binary logs from the Binlog Server replication source.
--let $roundtrip_dump = $MYSQL_TMP_DIR/rs_roundtrip.sql
--exec $MYSQL_BINLOG --read-from-remote-server --host=127.0.0.1 --port=$binsrv_replication_source_port --user=rpl --password=password --default-auth=caching_sha2_password $first_binlog > $roundtrip_dump 2>/dev/null

--echo
--echo *** Sending SIGTERM signal to the Binlog Server Utility and waiting for
--echo *** the process to terminate
--source ../include/terminate_proc.inc

--echo
--echo *** Moving source_db.tbl to backup_db.tbl.
CREATE DATABASE backup_db;
RENAME TABLE source_db.tbl TO backup_db.tbl;
DROP DATABASE source_db;

--echo
--echo *** Restoring source_db from the replication source dump.
--exec $MYSQL < $roundtrip_dump

--echo
--echo *** Comparing the restored and backup tables.
--let $diff_tables = source_db.tbl, backup_db.tbl
--source include/diff_tables.inc

--echo
--echo *** Dropping the round-trip databases.
DROP DATABASE source_db;
DROP DATABASE backup_db;

--remove_file $roundtrip_dump
--source ../include/rs_roundtrip_body.inc

--source ../include/tear_down_binsrv_environment.inc

Expand Down
39 changes: 39 additions & 0 deletions mtr/binlog_streaming/t/rs_roundtrip_encryption.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
--source ../include/have_binsrv.inc

--source ../include/v80_v84_compatibility_defines.inc

--source include/count_sessions.inc

--echo *** Resetting replication at the very beginning of the test.
--disable_query_log
eval $stmt_reset_binary_logs_and_gtids;
--enable_query_log

--source ../include/identify_storage_backend.inc

# generating keyring data file with the predefined key set
--let $binsrv_keyring_data_file_path = $MYSQL_TMP_DIR/keyring_data.json
--source ../include/generate_keyring_data_file.inc

--let $binsrv_connect_timeout = 3
--let $binsrv_read_timeout = 3
--let $binsrv_idle_time = 1
--let $binsrv_verify_checksum = TRUE
--let $binsrv_replication_mode = position
--let $binsrv_encryption_cipher = AES-256-CTR
--let $binsrv_encryption_kek_id = gamma32
--source ../include/set_up_binsrv_environment.inc

--source ../include/rs_roundtrip_body.inc

--source ../include/tear_down_binsrv_environment.inc
--source ../include/delete_keyring_data_file.inc

# As the Binlog Server Utility interrupts the connection upon timeout, here we
# need to close it on the MySQL server side as well in order to make sure that
# MTR 'check-test' before and after the test produces the same output.
--source ../include/kill_binlog_dump_connection.inc

--source include/wait_until_count_sessions.inc

# End of test.
60 changes: 41 additions & 19 deletions src/binsrv/storage_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,22 @@ storage_core::fetch_event_block(events::composite_binlog_name &binlog_name,
auto result_buffer{
backend_->get_object(resolved_binlog_name.str(), resolved_range)};

// If the binlog file is encrypted, decrypt the fetched ciphertext block
// in place.
if (record_it->encryption.has_value()) {
const auto &encryption_record{*record_it->encryption};
const auto file_key_decrypted{decrypt_file_key(encryption_record)};

auto data_decryption_context{opensslpp::cipher_context::create_with_offset(
read_offset, opensslpp::cipher_context_operation_type::decryption,
encryption_record.data_cipher, file_key_decrypted,
encryption_record.iv_for_data_encryption)};

const util::byte_span result_buffer_v{result_buffer};
data_decryption_context.update(result_buffer_v, result_buffer_v);
data_decryption_context.finalize();
}

binlog_name = std::move(resolved_binlog_name);
range = resolved_range;
buffer = std::move(result_buffer);
Expand Down Expand Up @@ -1063,46 +1079,52 @@ storage_core::generate_binlog_encryption_record() const {
return encryption_record;
}

void storage_core::write_data_to_stream(
util::const_byte_span data,
const optional_binlog_encryption_record &encryption_record,
std::uint64_t offset) {
if (!encryption_record.has_value()) {
// an early return when no encryption is needed
backend_->write_data_to_stream(data);
return;
}

util::hex_value_storage storage_core::decrypt_file_key(
const binlog_encryption_record &encryption_record) const {
// as for security reasons our intent is to not store file keys in plaintext
// permanently, we need to decrypt the file key with the KEK before we can
// use it for data encryption.
// use it for data encryption / decryption.

const auto &keyring_record{keyring_->get_key(encryption_record->kek_id)};
const auto &keyring_record{keyring_->get_key(encryption_record.kek_id)};

const auto &kek_cipher{keyring_record.get<"cipher">()};
const auto &kek{keyring_record.get<"data_hex">().get_data()};

util::const_byte_span iv_for_file_key_encryption_v{};
if (encryption_record->iv_for_file_key_encryption.has_value()) {
if (encryption_record.iv_for_file_key_encryption.has_value()) {
iv_for_file_key_encryption_v =
*encryption_record->iv_for_file_key_encryption;
*encryption_record.iv_for_file_key_encryption;
};
util::const_byte_span tag_of_file_key_encryption_v{};
if (encryption_record->tag_of_file_key_encryption.has_value()) {
if (encryption_record.tag_of_file_key_encryption.has_value()) {
tag_of_file_key_encryption_v =
*encryption_record->tag_of_file_key_encryption;
*encryption_record.tag_of_file_key_encryption;
}

// creating a context for the file key decryption
opensslpp::cipher_context file_key_decryption_context{
opensslpp::cipher_context_operation_type::decryption, kek_cipher, kek,
iv_for_file_key_encryption_v, tag_of_file_key_encryption_v};
util::hex_value_storage file_key_decrypted{
std::size(encryption_record->file_key_encrypted_with_kek)};
std::size(encryption_record.file_key_encrypted_with_kek)};
file_key_decryption_context.update(
encryption_record->file_key_encrypted_with_kek, file_key_decrypted);
encryption_record.file_key_encrypted_with_kek, file_key_decrypted);
file_key_decryption_context.finalize();

return file_key_decrypted;
}

void storage_core::write_data_to_stream(
util::const_byte_span data,
const optional_binlog_encryption_record &encryption_record,
std::uint64_t offset) {
if (!encryption_record.has_value()) {
// an early return when no encryption is needed
backend_->write_data_to_stream(data);
return;
}

const auto file_key_decrypted{decrypt_file_key(*encryption_record)};

// creating an context for data encryption with the data cipher, the file
// key (decrypted previously), and the IV for data encryption

Expand Down
3 changes: 3 additions & 0 deletions src/binsrv/storage_core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,9 @@ class [[nodiscard]] storage_core {
[[nodiscard]] optional_binlog_encryption_record
generate_binlog_encryption_record() const;

[[nodiscard]] util::hex_value_storage
decrypt_file_key(const binlog_encryption_record &encryption_record) const;

void write_data_to_stream(
util::const_byte_span data,
const optional_binlog_encryption_record &encryption_record,
Expand Down
1 change: 0 additions & 1 deletion src/opensslpp/cipher_context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ class cipher_context {
// accepts non-const EVP_CIPHER_CTX pointer
void extract_updated_iv(util::byte_span ivec);

// TODO: implement void update_inplace(util::byte_span inoutput)
void update(util::const_byte_span input, util::byte_span output);
void finalize(util::byte_span output_tag = {});

Expand Down
Loading
Loading