MDEV-34372 Temporary tablespace decryption fails with ER_NOT_KEYFILE

Reason:
======
  Temporary tablespace uses the innodb redo encryption key and key
version(v1) to encrypt the temporary tablespace/files. When
encryption key rotation happens, InnoDB assigns the new redo
encryption key version(v2) during checkpoint. After this, InnoDB
encrypts the temporary tablespace pages using key version (v2).
This lead to failure of decrypting the temporary tablespace
pages which was encrypted using key version (v1).

Fix:
===
  Store the temporary file/tablespace key and key version
seperately during redo log initialization. Even though
InnoDB does key rotation, it doesn't affect temporary file
key and key version.

log_crypt_init(): Assign the temporary file/tablespace
key and key version

i_s_dict_fill_temp_tablespaces_encryption(): Adds the temporary
tablespace information if
innodb_encrypt_temporary_tables is enabled
parent 2c5d8376
......@@ -24,3 +24,7 @@ FROM information_schema.global_status
WHERE variable_name = 'innodb_encryption_n_temp_blocks_decrypted';
CAST(variable_value AS INT) > @old_decrypted
1
select * from information_schema.INNODB_TABLESPACES_ENCRYPTION
WHERE SPACE=4294967294;
SPACE NAME ENCRYPTION_SCHEME KEYSERVER_REQUESTS MIN_KEY_VERSION CURRENT_KEY_VERSION KEY_ROTATION_PAGE_NUMBER KEY_ROTATION_MAX_PAGE_NUMBER CURRENT_KEY_ID ROTATING_OR_FLUSHING
4294967294 innodb_temporary 1 1 1 1 NULL NULL 1 0
......@@ -52,6 +52,9 @@ SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_
AND NAME NOT LIKE 'innodb_undo%' AND NAME NOT LIKE 'mysql/innodb_%_stats' AND NAME NOT LIKE 'mysql/transaction_registry';
NAME
innodb_system
SELECT COUNT(*) FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE SPACE=4294967294;
COUNT(*)
0
# Success!
# Restart mysqld --innodb_encrypt_tables=0 --innodb_encryption_threads=0
# restart: --innodb_encrypt_tables=0 --innodb_encryption_threads=0
......
CREATE TEMPORARY TABLE temp(f1 INT NOT NULL, f2 INT NOT NULL)ENGINE=InnoDB;
INSERT INTO temp select seq, seq FROM seq_1_to_65536;
SET GLOBAL debug_key_management_version = 10;
SET GLOBAL innodb_log_checkpoint_now=1;
INSERT INTO temp select seq, seq FROM seq_1_to_65536;
SELECT COUNT(*) FROM temp;
COUNT(*)
131072
SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE SPACE=4294967294;
SPACE NAME ENCRYPTION_SCHEME KEYSERVER_REQUESTS MIN_KEY_VERSION CURRENT_KEY_VERSION KEY_ROTATION_PAGE_NUMBER KEY_ROTATION_MAX_PAGE_NUMBER CURRENT_KEY_ID ROTATING_OR_FLUSHING
4294967294 innodb_temporary 1 1 1 1 NULL NULL 1 0
# restart: --debug_key_management_version=10
CREATE TEMPORARY TABLE temp(f1 INT NOT NULL, f2 INT NOT NULL)ENGINE=InnoDB;
SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE SPACE=4294967294;
SPACE NAME ENCRYPTION_SCHEME KEYSERVER_REQUESTS MIN_KEY_VERSION CURRENT_KEY_VERSION KEY_ROTATION_PAGE_NUMBER KEY_ROTATION_MAX_PAGE_NUMBER CURRENT_KEY_ID ROTATING_OR_FLUSHING
4294967294 innodb_temporary 1 1 10 10 NULL NULL 1 0
SET GLOBAL debug_key_management_version = 1;
--innodb_buffer_pool_size=5M
--innodb_encrypt_temporary_tables=1
--innodb_tablespaces_encryption
......@@ -26,3 +26,6 @@ WHERE variable_name = 'innodb_encryption_n_temp_blocks_encrypted';
SELECT CAST(variable_value AS INT) > @old_decrypted
FROM information_schema.global_status
WHERE variable_name = 'innodb_encryption_n_temp_blocks_decrypted';
select * from information_schema.INNODB_TABLESPACES_ENCRYPTION
WHERE SPACE=4294967294;
......@@ -82,6 +82,8 @@ AND NAME NOT LIKE 'innodb_undo%' AND NAME NOT LIKE 'mysql/innodb_%_stats' AND NA
--sorted_result
SELECT NAME FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0
AND NAME NOT LIKE 'innodb_undo%' AND NAME NOT LIKE 'mysql/innodb_%_stats' AND NAME NOT LIKE 'mysql/transaction_registry';
# innodb_encrypt_temporary_tables is disabled. So it should print null
SELECT COUNT(*) FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE SPACE=4294967294;
--echo # Success!
--echo # Restart mysqld --innodb_encrypt_tables=0 --innodb_encryption_threads=0
......
--innodb_buffer_pool_size=5M
--innodb-encrypt-log=1
--plugin-load-add=$DEBUG_KEY_MANAGEMENT_SO
--innodb_encrypt_temporary_tables=1
--innodb_tablespaces_encryption
--source include/have_innodb.inc
--source include/have_debug.inc
--source include/have_sequence.inc
--source include/not_embedded.inc
CREATE TEMPORARY TABLE temp(f1 INT NOT NULL, f2 INT NOT NULL)ENGINE=InnoDB;
INSERT INTO temp select seq, seq FROM seq_1_to_65536;
SET GLOBAL debug_key_management_version = 10;
SET GLOBAL innodb_log_checkpoint_now=1;
INSERT INTO temp select seq, seq FROM seq_1_to_65536;
SELECT COUNT(*) FROM temp;
SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE SPACE=4294967294;
let $restart_parameters=--debug_key_management_version=10;
--source include/restart_mysqld.inc
CREATE TEMPORARY TABLE temp(f1 INT NOT NULL, f2 INT NOT NULL)ENGINE=InnoDB;
SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE SPACE=4294967294;
SET GLOBAL debug_key_management_version = 1;
......@@ -60,6 +60,7 @@ Modified Dec 29, 2014 Jan Lindström (Added sys_semaphore_waits)
#include "dict0crea.h"
#include "fts0vlc.h"
#include "log.h"
#include "log0crypt.h"
/** The latest successfully looked up innodb_fts_aux_table */
UNIV_INTERN table_id_t innodb_ft_aux_table_id;
......@@ -7041,6 +7042,40 @@ i_s_dict_fill_tablespaces_encryption(
skip:
DBUG_RETURN(0);
}
/** Function to fill INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION
for temporary tablespace
@param thd thread handle
@param table_to_fill I_S table to fill
@return 0 on success */
static
int i_s_dict_fill_temp_tablespace_encryption(THD* thd,
TABLE *table_to_fill)
{
DBUG_ENTER("i_s_dict_fill_temp_tablespaces_encryption");
if (!innodb_encrypt_temporary_tables)
DBUG_RETURN(0);
fil_space_t *space= fil_system.temp_space;
Field **fields= table_to_fill->field;
OK(fields[TABLESPACES_ENCRYPTION_SPACE]->store(space->id, true));
OK(field_store_string(fields[TABLESPACES_ENCRYPTION_NAME],
space->name));
OK(fields[TABLESPACES_ENCRYPTION_ENCRYPTION_SCHEME]->store(1, true));
OK(fields[TABLESPACES_ENCRYPTION_KEYSERVER_REQUESTS]->store(1, true));
OK(fields[TABLESPACES_ENCRYPTION_MIN_KEY_VERSION]->store(
tmp_key_version, true));
OK(fields[TABLESPACES_ENCRYPTION_CURRENT_KEY_VERSION]->store(
tmp_key_version, true));
/* LOG_DEFAULT_ENCRYPTION_KEY */
OK(fields[TABLESPACES_ENCRYPTION_CURRENT_KEY_ID]->store(1, true));
OK(fields[TABLESPACES_ENCRYPTION_ROTATING_OR_FLUSHING]->store(0, true));
fields[TABLESPACES_ENCRYPTION_KEY_ROTATION_PAGE_NUMBER]->set_null();
fields[TABLESPACES_ENCRYPTION_KEY_ROTATION_MAX_PAGE_NUMBER]->set_null();
OK(schema_table_store_record(thd, table_to_fill));
DBUG_RETURN(0);
}
/*******************************************************************//**
Function to populate INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION table.
Loop through each record in TABLESPACES_ENCRYPTION, and extract the column
......@@ -7084,6 +7119,7 @@ i_s_tablespaces_encryption_fill_table(
fil_system.freeze_space_list--;
mutex_exit(&fil_system.mutex);
i_s_dict_fill_temp_tablespace_encryption(thd, tables->table);
DBUG_RETURN(err);
}
/*******************************************************************//**
......
......@@ -122,4 +122,9 @@ log_tmp_block_decrypt(
/** @return whether temporary files are encrypted */
inline bool log_tmp_is_encrypted() { return srv_encrypt_log; }
/** Encryption key version for temporary file or tablespace */
extern uint tmp_key_version;
/** Secret key for temporary file or tablespace */
extern byte tmp_crypt_key[MY_AES_BLOCK_SIZE];
#endif // log0crypt.h
......@@ -60,6 +60,11 @@ static crypt_info_t infos[5 * 2];
/** First unused slot in infos[] */
static size_t infos_used;
/** Encryption key version for temporary file or tablespace */
uint tmp_key_version;
/** Secret key for temporary file or tablespace */
byte tmp_crypt_key[MY_AES_BLOCK_SIZE];
/*********************************************************************//**
Get a log block's start lsn.
@return a log block's start lsn */
......@@ -240,6 +245,8 @@ bool log_crypt_init()
info.key_version= 0;
func_exit:
tmp_key_version= info.key_version;
memcpy(tmp_crypt_key, info.crypt_key, MY_AES_BLOCK_SIZE);
return info.key_version != 0;
}
......@@ -413,12 +420,12 @@ log_tmp_block_encrypt(
int rc = encryption_crypt(
src, uint(size), dst, &dst_len,
const_cast<byte*>(info.crypt_key), MY_AES_BLOCK_SIZE,
const_cast<byte*>(tmp_crypt_key), MY_AES_BLOCK_SIZE,
reinterpret_cast<byte*>(iv), uint(sizeof iv),
encrypt
? ENCRYPTION_FLAG_ENCRYPT|ENCRYPTION_FLAG_NOPAD
: ENCRYPTION_FLAG_DECRYPT|ENCRYPTION_FLAG_NOPAD,
LOG_DEFAULT_ENCRYPTION_KEY, info.key_version);
LOG_DEFAULT_ENCRYPTION_KEY, tmp_key_version);
if (rc != MY_AES_OK) {
ib::error() << (encrypt ? "Encryption" : "Decryption")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment