Commit 71ec0463 authored by Jan Lindström's avatar Jan Lindström Committed by Sergei Golubchik

Encryption cleanup

Step 3:

-- Make encrytion_algorithm changeable by SUPER
-- Remove AES_ECB method from encryption_algorithms
-- Support AES method change by storing used method on InnoDB/XtraDB objects
-- Store used AES method to crypt_data as different crypt types
-- Store used AES method to redo/undo logs and checkpoint
-- Store used AES method on every encrypted page after key_version
-- Add test
parent 0ba9fa35
......@@ -115,8 +115,8 @@ extern MYSQL_PLUGIN_IMPORT my_aes_decrypt_dynamic_type my_aes_decrypt_dynamic;
enum enum_my_aes_encryption_algorithm
{
MY_AES_ALGORITHM_NONE, MY_AES_ALGORITHM_ECB, MY_AES_ALGORITHM_CBC,
MY_AES_ALGORITHM_CTR
MY_AES_ALGORITHM_NONE, MY_AES_ALGORITHM_CTR,
MY_AES_ALGORITHM_CBC, MY_AES_ALGORITHM_ECB
};
my_aes_decrypt_dynamic_type get_aes_decrypt_func(enum enum_my_aes_encryption_algorithm method);
......
[cbc]
encryption-algorithm=aes_cbc
[ecb]
encryption-algorithm=aes_ecb
[ctr]
encryption-algorithm=aes_ctr
......@@ -167,8 +167,8 @@ The following options may be given as the first argument:
execution)
--encryption-algorithm=name
Which encryption algorithm to use for table encryption.
aes_cbc is the recommended one.. One of: none, aes_ecb,
aes_cbc, aes_ctr
aes_cbc is the recommended one.. One of: none, aes_cbc,
aes_ctr
--enforce-storage-engine=name
Force the use of a storage engine for new tables
--event-scheduler[=name]
......
SET @start_global_value = @@global.innodb_encryption_threads;
SET GLOBAL innodb_file_format = `Barracuda`;
SET GLOBAL innodb_file_per_table = ON;
SHOW VARIABLES LIKE 'innodb_encrypt%';
Variable_name Value
innodb_encrypt_log ON
innodb_encrypt_tables OFF
innodb_encryption_rotate_key_age 15
innodb_encryption_rotation_iops 100
innodb_encryption_threads 0
DESCRIBE INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION;
Field Type Null Key Default Extra
SPACE int(11) unsigned NO 0
NAME varchar(655) YES NULL
ENCRYPTION_SCHEME int(11) unsigned NO 0
KEYSERVER_REQUESTS int(11) unsigned NO 0
MIN_KEY_VERSION int(11) unsigned NO 0
CURRENT_KEY_VERSION int(11) unsigned NO 0
KEY_ROTATION_PAGE_NUMBER bigint(21) unsigned YES NULL
KEY_ROTATION_MAX_PAGE_NUMBER bigint(21) unsigned YES NULL
create table innodb_normal(c1 bigint not null, b char(200)) engine=innodb;
create table innodb_compact(c1 bigint not null, b char(200)) engine=innodb row_format=compact;
create table innodb_dynamic(c1 bigint not null, b char(200)) engine=innodb row_format=dynamic;
create table innodb_compressed(c1 bigint not null, b char(200)) engine=innodb row_format=compressed;
create table innodb_redundant(c1 bigint not null, b char(200)) engine=innodb row_format=redundant;
create procedure innodb_insert_proc (repeat_count int)
begin
declare current_num int;
set current_num = 0;
while current_num < repeat_count do
insert into innodb_normal values(current_num, substring(MD5(RAND()), -64));
set current_num = current_num + 1;
end while;
end//
commit;
set autocommit=0;
call innodb_insert_proc(2000);
commit;
set autocommit=1;
insert into innodb_compact select * from innodb_normal;
insert into innodb_dynamic select * from innodb_normal;
insert into innodb_compressed select * from innodb_normal;
insert into innodb_redundant select * from innodb_normal;
" Start encrypt tablespaces
SET GLOBAL innodb_encrypt_tables = on;
SET GLOBAL innodb_encryption_threads = 4;
# Wait max 5 min for key encryption threads to encrypt one space
# Success!
# Wait max 10 min for key encryption threads to encrypt all space
# Success!
# Now turn off encryption and wait for threads to decrypt everything
SET GLOBAL innodb_encrypt_tables = off;
set GLOBAL encryption_algorithm = aes_cbc;
# Wait max 10 min for key encryption threads to decrypt all space
# Success!
# Shutdown innodb_encryption_threads
SET GLOBAL innodb_encryption_threads=0;
# Turn on encryption
# since threads are off tables should remain unencrypted
SET GLOBAL innodb_encrypt_tables = on;
# Wait 15s to check that nothing gets encrypted
# Success!
# Startup innodb_encryption_threads
SET GLOBAL innodb_encryption_threads=4;
# Wait 1 min to check that it start encrypting again
# Success!
# Wait max 10 min for key encryption threads to decrypt all space
# Success!
SELECT variable_value >= 0 FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_num_pages_page_encrypted';
variable_value >= 0
1
SELECT variable_value >= 0 FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_num_pages_page_decrypted';
variable_value >= 0
1
SELECT variable_value = 0 FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_num_pages_page_encryption_error';
variable_value = 0
1
SELECT variable_value > 0 FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_num_pages_page_compressed';
variable_value > 0
0
SELECT variable_value > 0 FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_num_pages_page_decompressed';
variable_value > 0
0
drop procedure innodb_insert_proc;
drop table innodb_normal;
drop table innodb_compact;
drop table innodb_dynamic;
drop table innodb_compressed;
drop table innodb_redundant;
--aria-encrypt-tables=ON
--encrypt-tmp-disk-tables=ON
--innodb-encryption-rotate-key-age=15
--innodb-encryption-threads=0
--innodb-tablespaces-encryption
--innodb-encrypt-log=ON
#
#
#
-- source include/have_innodb.inc
-- source include/have_example_key_management_plugin.inc
# embedded does not support restart
-- source include/not_embedded.inc
--disable_query_log
let $orig_algorithm=`SELECT @@encryption_algorithm`;
let $innodb_file_format_orig = `SELECT @@innodb_file_format`;
let $innodb_file_per_table_orig = `SELECT @@innodb_file_per_table`;
let $encrypt_tables_orig = `SELECT @@innodb_encrypt_tables`;
--enable_query_log
SET @start_global_value = @@global.innodb_encryption_threads;
SET GLOBAL innodb_file_format = `Barracuda`;
SET GLOBAL innodb_file_per_table = ON;
--disable_query_log
EVAL SET GLOBAL encryption_algorithm = $orig_algorithm;
--enable_query_log
SHOW VARIABLES LIKE 'innodb_encrypt%';
DESCRIBE INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION;
create table innodb_normal(c1 bigint not null, b char(200)) engine=innodb;
create table innodb_compact(c1 bigint not null, b char(200)) engine=innodb row_format=compact;
create table innodb_dynamic(c1 bigint not null, b char(200)) engine=innodb row_format=dynamic;
create table innodb_compressed(c1 bigint not null, b char(200)) engine=innodb row_format=compressed;
create table innodb_redundant(c1 bigint not null, b char(200)) engine=innodb row_format=redundant;
delimiter //;
create procedure innodb_insert_proc (repeat_count int)
begin
declare current_num int;
set current_num = 0;
while current_num < repeat_count do
insert into innodb_normal values(current_num, substring(MD5(RAND()), -64));
set current_num = current_num + 1;
end while;
end//
delimiter ;//
commit;
set autocommit=0;
call innodb_insert_proc(2000);
commit;
set autocommit=1;
insert into innodb_compact select * from innodb_normal;
insert into innodb_dynamic select * from innodb_normal;
insert into innodb_compressed select * from innodb_normal;
insert into innodb_redundant select * from innodb_normal;
--echo " Start encrypt tablespaces
SET GLOBAL innodb_encrypt_tables = on;
SET GLOBAL innodb_encryption_threads = 4;
--echo # Wait max 5 min for key encryption threads to encrypt one space
let $cnt=300;
while ($cnt)
{
let $success=`SELECT COUNT(*) > 0 FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION > 0`;
if ($success)
{
let $cnt=0;
}
if (!$success)
{
real_sleep 1;
dec $cnt;
}
}
if (!$success)
{
SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION;
SHOW STATUS LIKE 'innodb_encryption%';
-- die Timeout waiting for encryption threads
}
--echo # Success!
--echo # Wait max 10 min for key encryption threads to encrypt all space
let $cnt=600;
while ($cnt)
{
let $success=`SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION = 0`;
if ($success)
{
let $cnt=0;
}
if (!$success)
{
real_sleep 1;
dec $cnt;
}
}
if (!$success)
{
SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION;
SHOW STATUS LIKE 'innodb_encryption%';
-- die Timeout waiting for encryption threads
}
--echo # Success!
--echo # Now turn off encryption and wait for threads to decrypt everything
SET GLOBAL innodb_encrypt_tables = off;
set GLOBAL encryption_algorithm = aes_cbc;
--echo # Wait max 10 min for key encryption threads to decrypt all space
let $cnt=600;
while ($cnt)
{
let $success=`SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0`;
if ($success)
{
let $cnt=0;
}
if (!$success)
{
real_sleep 1;
dec $cnt;
}
}
if (!$success)
{
SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION;
SHOW STATUS LIKE 'innodb_encryption%';
-- die Timeout waiting for encryption threads
}
--echo # Success!
--echo # Shutdown innodb_encryption_threads
SET GLOBAL innodb_encryption_threads=0;
--echo # Turn on encryption
--echo # since threads are off tables should remain unencrypted
SET GLOBAL innodb_encrypt_tables = on;
--echo # Wait 15s to check that nothing gets encrypted
let $cnt=15;
while ($cnt)
{
let $success=`SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0`;
if ($success)
{
real_sleep 1;
dec $cnt;
}
if (!$success)
{
SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0;
-- die Failure, tablespace getting encrypted even if innodb_encryption_threads=0
}
}
--echo # Success!
--echo # Startup innodb_encryption_threads
SET GLOBAL innodb_encryption_threads=4;
--echo # Wait 1 min to check that it start encrypting again
let $cnt=60;
while ($cnt)
{
let $success=`SELECT COUNT(*) > 0 FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0 OR KEY_ROTATION_PAGE_NUMBER IS NOT NULL`;
if ($success)
{
let $cnt=0;
}
if (!$success)
{
real_sleep 1;
dec $cnt;
}
}
if (!$success)
{
SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION;
SHOW STATUS LIKE 'innodb_encryption%';
-- die Timeout waiting for encryption threads
}
--echo # Success!
--echo # Wait max 10 min for key encryption threads to decrypt all space
let $cnt=600;
while ($cnt)
{
let $success=`SELECT COUNT(*) = 0 FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION WHERE MIN_KEY_VERSION <> 0`;
if ($success)
{
let $cnt=0;
}
if (!$success)
{
real_sleep 1;
dec $cnt;
}
}
if (!$success)
{
SELECT * FROM INFORMATION_SCHEMA.INNODB_TABLESPACES_ENCRYPTION;
SHOW STATUS LIKE 'innodb_encryption%';
-- die Timeout waiting for encryption threads
}
--echo # Success!
SELECT variable_value >= 0 FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_num_pages_page_encrypted';
SELECT variable_value >= 0 FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_num_pages_page_decrypted';
SELECT variable_value = 0 FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_num_pages_page_encryption_error';
SELECT variable_value > 0 FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_num_pages_page_compressed';
SELECT variable_value > 0 FROM information_schema.global_status WHERE LOWER(variable_name) = 'innodb_num_pages_page_decompressed';
drop procedure innodb_insert_proc;
drop table innodb_normal;
drop table innodb_compact;
drop table innodb_dynamic;
drop table innodb_compressed;
drop table innodb_redundant;
# reset system
--disable_query_log
EVAL SET GLOBAL innodb_encrypt_tables = $encrypt_tables_orig;
SET GLOBAL innodb_encryption_threads=@start_global_value;
EVAL SET GLOBAL encryption_algorithm = $orig_algorithm;
EVAL SET GLOBAL innodb_file_per_table = $innodb_file_per_table_orig;
EVAL SET GLOBAL innodb_file_format = $innodb_file_format_orig;
--enable_query_log
......@@ -3,5 +3,6 @@ select @@global.encryption_algorithm;
none
select @@session.encryption_algorithm;
ERROR HY000: Variable 'encryption_algorithm' is a GLOBAL variable
set global encryption_algorithm="none";
ERROR HY000: Variable 'encryption_algorithm' is a read only variable
set global encryption_algorithm = aes_cbc;
set global encryption_algorithm = aes_cbc;
ERROR 42000: Access denied; you need (at least one of) the SUPER privilege(s) for this operation
......@@ -706,8 +706,8 @@ VARIABLE_COMMENT Which encryption algorithm to use for table encryption. aes_cbc
NUMERIC_MIN_VALUE NULL
NUMERIC_MAX_VALUE NULL
NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST none,aes_ecb,aes_cbc,aes_ctr
READ_ONLY YES
ENUM_VALUE_LIST none,aes_cbc,aes_ctr
READ_ONLY NO
COMMAND_LINE_ARGUMENT REQUIRED
VARIABLE_NAME ENCRYPT_TMP_DISK_TABLES
SESSION_VALUE NULL
......
# bool global
# enum global
--disable_query_log
let $orig_algorithm=`SELECT @@encryption_algorithm`;
--enable_query_log
#
# exists as global only
#
select @@global.encryption_algorithm;
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
select @@session.encryption_algorithm;
set global encryption_algorithm = aes_cbc;
#
# show that it's not writable
# Check changeable only by super
#
--error 1238
set global encryption_algorithm="none";
--source include/add_anonymous_users.inc
connect (con1,localhost,user_1,,);
connection con1;
--error 1227
set global encryption_algorithm = aes_cbc;
connection default;
--source include/delete_anonymous_users.inc
# reset system
--disable_query_log
EVAL SET GLOBAL encryption_algorithm = $orig_algorithm;
--enable_query_log
......@@ -63,7 +63,7 @@
#include "opt_range.h"
#include "rpl_parallel.h"
#include "encryption_keys.h"
#include "my_aes.h"
/*
The rule for this file: everything should be 'static'. When a sys_var
variable or a function from this file is - in very rare cases - needed
......@@ -5185,13 +5185,30 @@ static Sys_var_mybool Sys_encrypt_tmp_disk_tables(
GLOBAL_VAR(encrypt_tmp_disk_tables),
CMD_LINE(OPT_ARG), DEFAULT(FALSE));
static bool fix_encryption_algorithm(sys_var *self, THD *thd,
enum_var_type type)
{
if (my_aes_init_dynamic_encrypt((enum_my_aes_encryption_algorithm)
encryption_algorithm))
{
fprintf(stderr, "Can't initialize encryption algorithm to \"%s\".\nCheck that the program is linked with the right library (openssl?)\n",
encryption_algorithm_names[encryption_algorithm]);
return true;
}
return false;
}
const char *encryption_algorithm_names[]=
{ "none", "aes_ecb", "aes_cbc", "aes_ctr", 0 };
{ "none", "aes_cbc", "aes_ctr", 0 };
static Sys_var_enum Sys_encryption_algorithm(
"encryption_algorithm",
"Which encryption algorithm to use for table encryption. aes_cbc is the recommended one.",
READ_ONLY GLOBAL_VAR(encryption_algorithm),CMD_LINE(REQUIRED_ARG),
encryption_algorithm_names, DEFAULT(0));
GLOBAL_VAR(encryption_algorithm),CMD_LINE(REQUIRED_ARG),
encryption_algorithm_names, DEFAULT(0),
NO_MUTEX_GUARD, NOT_IN_BINLOG,
ON_CHECK(0), ON_UPDATE(fix_encryption_algorithm));
static bool check_pseudo_slave_mode(sys_var *self, THD *thd, set_var *var)
{
......
......@@ -2454,6 +2454,19 @@ buf_block_align_instance(
== 0xffffffff);
break;
case BUF_BLOCK_FILE_PAGE:
if (block->page.space
!= page_get_space_id(page_align(ptr))) {
fprintf(stderr, "Block space id %du page space id %lu page type %s\n",
block->page.space, page_get_space_id(page_align(ptr)),
fil_get_page_type_name(mach_read_from_4(ptr + FIL_PAGE_TYPE)));
}
if (block->page.offset
!= page_get_page_no(page_align(ptr))) {
fprintf(stderr, "Block offset %du page offset %lu page type %s\n",
block->page.offset, page_get_page_no(page_align(ptr)),
fil_get_page_type_name(mach_read_from_4(ptr + FIL_PAGE_TYPE)));
}
ut_ad(block->page.space
== page_get_space_id(page_align(ptr)));
ut_ad(block->page.offset
......
......@@ -23,8 +23,6 @@ Created Jonas Oreland Google
Modified Jan Lindström jan.lindstrom@mariadb.com
*******************************************************/
#include "fil0fil.h"
#include "fil0crypt.h"
#include "srv0srv.h"
#include "srv0start.h"
#include "mach0data.h"
......@@ -37,6 +35,8 @@ Modified Jan Lindström jan.lindstrom@mariadb.com
#include "fsp0fsp.h"
#include "fil0pagecompress.h"
#include "ha_prototypes.h" // IB_LOG_
#include "fil0fil.h"
#include "fil0crypt.h"
#include <my_crypt.h>
......@@ -119,34 +119,6 @@ static const unsigned char CRYPT_MAGIC[MAGIC_SZ] = {
static const unsigned char EMPTY_PATTERN[MAGIC_SZ] = {
0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
/******************************************************************
Map used AES method to crypt scheme
@return used AES crypt scheme */
UNIV_INTERN
uint
fil_crypt_get_aes_method(
/*=====================*/
uint aes_method)
{
switch (aes_method) {
case MY_AES_ALGORITHM_NONE:
return (uint) CRYPT_SCHEME_1_UNENCRYPTED;
break;
case MY_AES_ALGORITHM_CTR:
return (uint) CRYPT_SCHEME_1_CTR;
break;
case MY_AES_ALGORITHM_CBC:
return (uint) CRYPT_SCHEME_1_CBC;
break;
default:
ib_logf(IB_LOG_LEVEL_FATAL,
"Current AES method %d not supported.\n", aes_method);
ut_error;
}
return (uint) CRYPT_SCHEME_1_UNENCRYPTED;
}
/*********************************************************************
Init space crypt */
UNIV_INTERN
......@@ -659,29 +631,6 @@ fil_space_check_encryption_write(
return true;
}
/******************************************************************
Map current aes method
@return AES method */
UNIV_INTERN
uint
fil_crypt_map_aes_method(
/*=====================*/
uint aes_method) /*!< in: AES method */
{
switch((fil_crypt_method_t)aes_method) {
case CRYPT_SCHEME_1_CTR:
return (uint)MY_AES_ALGORITHM_CTR;
break;
case CRYPT_SCHEME_1_CBC:
return (uint)MY_AES_ALGORITHM_CBC;
break;
default:
ib_logf(IB_LOG_LEVEL_FATAL,
"Current AES method %d not supported.\n", (int)aes_method);
ut_error;
}
}
/******************************************************************
Encrypt a page */
UNIV_INTERN
......
......@@ -6858,7 +6858,7 @@ Get crypt data for a tablespace */
UNIV_INTERN
fil_space_crypt_t*
fil_space_get_crypt_data(
/*==================*/
/*=====================*/
ulint id) /*!< in: space id */
{
fil_space_t* space;
......@@ -6879,12 +6879,29 @@ fil_space_get_crypt_data(
return(crypt_data);
}
/******************************************************************
Get crypt data for a tablespace */
UNIV_INTERN
fil_space_crypt_t*
fil_space_crypt_data(
/*=================*/
fil_space_t* space) /*!< in: filespace */
{
fil_space_crypt_t* crypt_data = NULL;
if (space != NULL) {
crypt_data = space->crypt_data;
}
return(crypt_data);
}
/******************************************************************
Get crypt data for a tablespace */
UNIV_INTERN
void
fil_space_set_crypt_data(
/*==================*/
/*=====================*/
ulint id, /*!< in: space id */
fil_space_crypt_t* crypt_data) /*!< in: crypt data */
{
......
......@@ -87,6 +87,7 @@ static ulint srv_data_read, srv_data_written;
For page compressed pages compress the page before actual write
operation.
@return compressed page to be written*/
UNIV_INTERN
byte*
fil_compress_page(
/*==============*/
......@@ -379,6 +380,7 @@ fil_compress_page(
/****************************************************************//**
For page compressed pages decompress the page after actual read
operation. */
UNIV_INTERN
void
fil_decompress_page(
/*================*/
......
......@@ -67,3 +67,54 @@ fil_page_encryption_status(
}
return 0;
}
/******************************************************************
Map used AES method to crypt scheme
@return used AES crypt scheme */
UNIV_INLINE
uint
fil_crypt_get_aes_method(
/*=====================*/
uint aes_method)
{
switch (aes_method) {
case MY_AES_ALGORITHM_NONE:
return (uint) CRYPT_SCHEME_1_UNENCRYPTED;
break;
case MY_AES_ALGORITHM_CTR:
return (uint) CRYPT_SCHEME_1_CTR;
break;
case MY_AES_ALGORITHM_CBC:
return (uint) CRYPT_SCHEME_1_CBC;
break;
default:
ib_logf(IB_LOG_LEVEL_FATAL,
"Current AES method %d not supported.\n", aes_method);
ut_error;
}
return (uint) CRYPT_SCHEME_1_UNENCRYPTED;
}
/******************************************************************
Map current aes method
@return AES method */
UNIV_INLINE
uint
fil_crypt_map_aes_method(
/*=====================*/
uint aes_method) /*!< in: AES method */
{
switch((fil_crypt_method_t)aes_method) {
case CRYPT_SCHEME_1_CTR:
return (uint)MY_AES_ALGORITHM_CTR;
break;
case CRYPT_SCHEME_1_CBC:
return (uint)MY_AES_ALGORITHM_CBC;
break;
default:
ib_logf(IB_LOG_LEVEL_FATAL,
"Current AES method %d not supported.\n", (int)aes_method);
ut_error;
}
}
......@@ -34,6 +34,7 @@ Created 11/12/2013 Jan Lindström jan.lindstrom@skysql.com
Returns the page compression level flag of the space, or 0 if the space
is not compressed. The tablespace must be cached in the memory cache.
@return page compression level if page compressed, ULINT_UNDEFINED if space not found */
UNIV_INTERN
ulint
fil_space_get_page_compression_level(
/*=================================*/
......@@ -42,6 +43,7 @@ fil_space_get_page_compression_level(
Returns the page compression flag of the space, or false if the space
is not compressed. The tablespace must be cached in the memory cache.
@return true if page compressed, false if not or space not found */
UNIV_INTERN
ibool
fil_space_is_page_compressed(
/*=========================*/
......@@ -50,6 +52,7 @@ fil_space_is_page_compressed(
Returns the page compression flag of the space, or false if the space
is not compressed. The tablespace must be cached in the memory cache.
@return true if page compressed, false if not or space not found */
UNIV_INTERN
ibool
fil_space_get_page_compressed(
/*=========================*/
......@@ -58,6 +61,7 @@ fil_space_get_page_compressed(
Returns the atomic writes flag of the space, or false if the space
is not using atomic writes. The tablespace must be cached in the memory cache.
@return atomic write table option value */
UNIV_INTERN
atomic_writes_t
fil_space_get_atomic_writes(
/*=========================*/
......@@ -65,6 +69,7 @@ fil_space_get_atomic_writes(
/*******************************************************************//**
Find out wheather the page is index page or not
@return true if page type index page, false if not */
UNIV_INTERN
ibool
fil_page_is_index_page(
/*===================*/
......@@ -74,6 +79,7 @@ fil_page_is_index_page(
Get the name of the compression algorithm used for page
compression.
@return compression algorithm name or "UNKNOWN" if not known*/
UNIV_INTERN
const char*
fil_get_compression_alg_name(
/*=========================*/
......@@ -83,6 +89,7 @@ fil_get_compression_alg_name(
For page compressed pages compress the page before actual write
operation.
@return compressed page to be written*/
UNIV_INTERN
byte*
fil_compress_page(
/*==============*/
......@@ -102,6 +109,7 @@ fil_compress_page(
For page compressed pages decompress the page after actual read
operation.
@return uncompressed page */
UNIV_INTERN
void
fil_decompress_page(
/*================*/
......@@ -115,6 +123,7 @@ fil_decompress_page(
/****************************************************************//**
Get space id from fil node
@return space id*/
UNIV_INTERN
ulint
fil_node_get_space_id(
/*==================*/
......@@ -123,13 +132,16 @@ fil_node_get_space_id(
/****************************************************************//**
Get block size from fil node
@return block size*/
UNIV_INTERN
ulint
fil_node_get_block_size(
/*====================*/
fil_node_t* node); /*!< in: Node where to get block
size */
/*******************************************************************//**
Find out wheather the page is page compressed
@return true if page is page compressed*/
@return true if page is page compressed */
UNIV_INTERN
ibool
fil_page_is_compressed(
/*===================*/
......@@ -138,6 +150,7 @@ fil_page_is_compressed(
/*******************************************************************//**
Find out wheather the page is page compressed with lzo method
@return true if page is page compressed with lzo method*/
UNIV_INTERN
ibool
fil_page_is_lzo_compressed(
/*=======================*/
......
......@@ -23,6 +23,8 @@ extern my_bool srv_encrypt_log;
extern byte redo_log_crypt_msg[MY_AES_BLOCK_SIZE];
/* IV to concatenate with counter used by AES_CTR for redo log crypto. */
extern byte aes_ctr_nonce[MY_AES_BLOCK_SIZE];
/* AES method used for redo log encryption/decryption. */
extern byte redo_aes_method;
/*********************************************************************//**
Generate a 128-bit random message used to generate redo log crypto key.
......
......@@ -678,7 +678,7 @@ extern log_t* log_sys;
#define LOG_CHECKPOINT_OFFSET_HIGH32 (16 + LOG_CHECKPOINT_ARRAY_END)
#define LOG_CRYPT_VER (20 + LOG_CHECKPOINT_ARRAY_END)
/*!< 32-bit key version. Corresponding
key has been used for log records with
key has been used for log records with
lsn <= the checkpoint' lsn */
#define LOG_CRYPT_MSG (24 + LOG_CHECKPOINT_ARRAY_END)
/*!< a 128-bit value used to
......@@ -688,8 +688,10 @@ extern log_t* log_sys;
15-byte random number.*/
#define LOG_CRYPT_IV (40 + LOG_CHECKPOINT_ARRAY_END)
/*!< a 128-bit random number used as
AES-CTR iv/nonce for redo log */
#define LOG_CHECKPOINT_SIZE (56 + LOG_CHECKPOINT_ARRAY_END)
AES-* iv/nonce for redo log */
#define LOG_CRYPT_METHOD (56 + LOG_CHECKPOINT_ARRAY_END)
/*!< AES method used */
#define LOG_CHECKPOINT_SIZE (57 + LOG_CHECKPOINT_ARRAY_END)
/* Offsets of a log file header */
#define LOG_GROUP_ID 0 /* log group number */
......
......@@ -30,6 +30,9 @@ Modified Jan Lindström jan.lindstrom@mariadb.com
#include "log0log.h"
#include "srv0start.h" // for srv_start_lsn
#include "log0recv.h" // for recv_sys
#include "ha_prototypes.h" // ib_logf
#include "fil0fil.h" // for page types
#include "fil0crypt.h" // for aes_method
#include "mysql/plugin_encryption_key_management.h" // for BAD_ENCRYPTION_KEY_VERSION
#include "ha_prototypes.h" // IB_LOG_
......@@ -43,15 +46,17 @@ UNIV_INTERN my_bool srv_encrypt_log = FALSE;
static const byte redo_log_purpose_byte = 0x02;
/* Plain text used by AES_ECB to generate redo log crypt key. */
byte redo_log_crypt_msg[MY_AES_BLOCK_SIZE] = {0};
/* IV to concatenate with counter used by AES_CTR for redo log
/* IV to concatenate with counter used by AES for redo log
* encryption/decryption. */
byte aes_ctr_nonce[MY_AES_BLOCK_SIZE] = {0};
/* AES method used for redo log encryption/decryption. */
byte redo_aes_method = 0;
/*********************************************************************//**
Generate a 128-bit value used to generate crypt key for redo log.
It is generated via the concatenation of 1 purpose byte (0x02) and 15-byte
random number.
Init AES-CTR iv/nonce with random number.
Init AES iv/nonce with random number.
It is called when:
- redo logs do not exist when start up, or
- transition from without crypto.
......@@ -69,8 +74,8 @@ log_init_crypt_msg_and_nonce(void)
/*==============================*/
{
mach_write_to_1(redo_log_crypt_msg, redo_log_purpose_byte);
if (my_random_bytes(redo_log_crypt_msg + 1, PURPOSE_BYTE_LEN) != AES_OK)
{
if (my_random_bytes(redo_log_crypt_msg + 1, PURPOSE_BYTE_LEN) != AES_OK) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: generate "
"%u-byte random number as crypto msg failed.",
......@@ -78,14 +83,16 @@ log_init_crypt_msg_and_nonce(void)
abort();
}
if (my_random_bytes(aes_ctr_nonce, MY_AES_BLOCK_SIZE) != AES_OK)
{
if (my_random_bytes(aes_ctr_nonce, MY_AES_BLOCK_SIZE) != AES_OK) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: generate "
"%u-byte random number as AES_CTR nonce failed.",
"%u-byte random number failed.",
MY_AES_BLOCK_SIZE);
abort();
}
/* Set up current AES method */
redo_aes_method = (byte)fil_crypt_get_aes_method(current_aes_dynamic_method);
}
/*********************************************************************//**
......@@ -98,16 +105,14 @@ log_init_crypt_key(
const uint crypt_ver, /*< in: key version */
byte* key) /*< out: crypt key*/
{
if (crypt_ver == UNENCRYPTED_KEY_VER)
{
if (crypt_ver == UNENCRYPTED_KEY_VER) {
ib_logf(IB_LOG_LEVEL_INFO,
"Redo log crypto: unencrypted key ver.");
memset(key, 0, MY_AES_BLOCK_SIZE);
return;
}
if (crypt_msg[PURPOSE_BYTE_OFFSET] != redo_log_purpose_byte)
{
if (crypt_msg[PURPOSE_BYTE_OFFSET] != redo_log_purpose_byte) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: msg type mismatched. "
"Expected: %x; Actual: %x.",
......@@ -116,8 +121,8 @@ log_init_crypt_key(
}
byte mysqld_key[MY_AES_BLOCK_SIZE] = {0};
if (get_encryption_key(crypt_ver, mysqld_key, MY_AES_BLOCK_SIZE))
{
if (get_encryption_key(crypt_ver, mysqld_key, MY_AES_BLOCK_SIZE)) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: getting mysqld crypto key "
"from key version failed.");
......@@ -125,15 +130,16 @@ log_init_crypt_key(
}
uint32 dst_len;
my_aes_encrypt_dynamic_type func= get_aes_encrypt_func(MY_AES_ALGORITHM_ECB);
int rc= (*func)(crypt_msg, MY_AES_BLOCK_SIZE, //src, srclen
key, &dst_len, //dst, &dstlen
(unsigned char*)&mysqld_key, sizeof(mysqld_key),
NULL, 0,
1);
if (rc != AES_OK || dst_len != MY_AES_BLOCK_SIZE)
{
if (rc != AES_OK || dst_len != MY_AES_BLOCK_SIZE) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: getting redo log crypto key "
"failed.");
......@@ -158,7 +164,7 @@ log_block_get_start_lsn(
}
/*********************************************************************//**
Call AES CTR to encrypt/decrypt log blocks. */
Call AES to encrypt/decrypt log blocks. */
static
Crypt_result
log_blocks_crypt(
......@@ -175,21 +181,22 @@ log_blocks_crypt(
ulint log_block_no, log_block_start_lsn;
byte *key;
ulint lsn;
if (is_encrypt)
{
uint aes_method = (uint)redo_aes_method;
if (is_encrypt) {
ut_a(log_sys && log_sys->redo_log_crypt_ver != UNENCRYPTED_KEY_VER);
key = (byte *)(log_sys->redo_log_crypt_key);
lsn = log_sys->lsn;
} else {
ut_a(recv_sys && recv_sys->recv_log_crypt_ver != UNENCRYPTED_KEY_VER);
key = (byte *)(recv_sys->recv_log_crypt_key);
lsn = srv_start_lsn;
}
ut_a(size % OS_FILE_LOG_BLOCK_SIZE == 0);
src_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE;
for (ulint i = 0; i < size ; i += OS_FILE_LOG_BLOCK_SIZE)
{
for (ulint i = 0; i < size ; i += OS_FILE_LOG_BLOCK_SIZE) {
log_block_no = log_block_get_hdr_no(log_block);
log_block_start_lsn = log_block_get_start_lsn(lsn, log_block_no);
......@@ -205,11 +212,13 @@ log_blocks_crypt(
mach_write_to_4(aes_ctr_counter + 11, log_block_no);
bzero(aes_ctr_counter + 15, 1);
int rc = (* my_aes_encrypt_dynamic)(log_block + LOG_BLOCK_HDR_SIZE, src_len,
dst_block + LOG_BLOCK_HDR_SIZE, &dst_len,
(unsigned char*)key, 16,
aes_ctr_counter, MY_AES_BLOCK_SIZE,
1);
my_aes_encrypt_dynamic_type func = get_aes_encrypt_func((enum_my_aes_encryption_algorithm)fil_crypt_map_aes_method(aes_method));
int rc = (*func)(log_block + LOG_BLOCK_HDR_SIZE, src_len,
dst_block + LOG_BLOCK_HDR_SIZE, &dst_len,
(unsigned char*)key, 16,
aes_ctr_counter, MY_AES_BLOCK_SIZE,
1);
ut_a(rc == AES_OK);
ut_a(dst_len == src_len);
......@@ -302,13 +311,17 @@ log_crypt_write_checkpoint_buf(
{
ut_a(log_sys);
mach_write_to_4(buf + LOG_CRYPT_VER, log_sys->redo_log_crypt_ver);
if (!srv_encrypt_log ||
log_sys->redo_log_crypt_ver == UNENCRYPTED_KEY_VER) {
memset(buf + LOG_CRYPT_MSG, 0, MY_AES_BLOCK_SIZE);
memset(buf + LOG_CRYPT_IV, 0, MY_AES_BLOCK_SIZE);
memset(buf + LOG_CRYPT_METHOD, 0, 1);
return;
}
ut_a(redo_log_crypt_msg[PURPOSE_BYTE_OFFSET] == redo_log_purpose_byte);
memcpy(buf + LOG_CRYPT_MSG, redo_log_crypt_msg, MY_AES_BLOCK_SIZE);
memcpy(buf + LOG_CRYPT_IV, aes_ctr_nonce, MY_AES_BLOCK_SIZE);
mach_write_to_1(buf + LOG_CRYPT_METHOD, redo_aes_method);
}
......@@ -3088,13 +3088,16 @@ recv_recovery_from_checkpoint_start_func(
#ifdef UNIV_LOG_ARCHIVE
archived_lsn = mach_read_from_8(buf + LOG_CHECKPOINT_ARCHIVED_LSN);
#endif /* UNIV_LOG_ARCHIVE */
recv_crypt_ver = mach_read_from_4(buf + LOG_CRYPT_VER);
if (recv_crypt_ver == UNENCRYPTED_KEY_VER)
{
log_init_crypt_msg_and_nonce();
} else {
ut_memcpy(redo_log_crypt_msg, buf + LOG_CRYPT_MSG, MY_AES_BLOCK_SIZE);
ut_memcpy(aes_ctr_nonce, buf + LOG_CRYPT_IV, MY_AES_BLOCK_SIZE);
redo_aes_method = (byte)mach_read_from_1(buf + LOG_CRYPT_METHOD);
}
/* Read the first log file header to print a note if this is
......
......@@ -2450,6 +2450,19 @@ buf_block_align_instance(
== 0xffffffff);
break;
case BUF_BLOCK_FILE_PAGE:
if (block->page.space
!= page_get_space_id(page_align(ptr))) {
fprintf(stderr, "Block space id %du page space id %lu page type %s\n",
block->page.space, page_get_space_id(page_align(ptr)),
fil_get_page_type_name(mach_read_from_4(ptr + FIL_PAGE_TYPE)));
}
if (block->page.offset
!= page_get_page_no(page_align(ptr))) {
fprintf(stderr, "Block offset %du page offset %lu page type %s\n",
block->page.offset, page_get_page_no(page_align(ptr)),
fil_get_page_type_name(mach_read_from_4(ptr + FIL_PAGE_TYPE)));
}
ut_ad(block->page.space
== page_get_space_id(page_align(ptr)));
ut_ad(block->page.offset
......
......@@ -119,33 +119,6 @@ static const unsigned char CRYPT_MAGIC[MAGIC_SZ] = {
static const unsigned char EMPTY_PATTERN[MAGIC_SZ] = {
0x0, 0x0, 0x0, 0x0, 0x0, 0x0 };
/******************************************************************
Map used AES method to crypt scheme
@return used AES crypt scheme */
UNIV_INTERN
uint
fil_crypt_get_aes_method(
/*=====================*/
uint aes_method)
{
switch (aes_method) {
case MY_AES_ALGORITHM_NONE:
return (uint) CRYPT_SCHEME_1_UNENCRYPTED;
break;
case MY_AES_ALGORITHM_CTR:
return (uint) CRYPT_SCHEME_1_CTR;
break;
case MY_AES_ALGORITHM_CBC:
return (uint) CRYPT_SCHEME_1_CBC;
break;
default:
ib_logf(IB_LOG_LEVEL_FATAL,
"Current AES method %d not supported.\n", aes_method);
ut_error;
}
return (uint) CRYPT_SCHEME_1_UNENCRYPTED;
}
/*********************************************************************
Init space crypt */
......@@ -659,29 +632,6 @@ fil_space_check_encryption_write(
return true;
}
/******************************************************************
Map current aes method
@return AES method */
UNIV_INTERN
uint
fil_crypt_map_aes_method(
/*=====================*/
uint aes_method) /*!< in: AES method */
{
switch((fil_crypt_method_t)aes_method) {
case CRYPT_SCHEME_1_CTR:
return (uint)MY_AES_ALGORITHM_CTR;
break;
case CRYPT_SCHEME_1_CBC:
return (uint)MY_AES_ALGORITHM_CBC;
break;
default:
ib_logf(IB_LOG_LEVEL_FATAL,
"Current AES method %d not supported.\n", (int)aes_method);
ut_error;
}
}
/******************************************************************
Encrypt a page */
UNIV_INTERN
......
......@@ -6992,7 +6992,7 @@ Get crypt data for a tablespace */
UNIV_INTERN
fil_space_crypt_t*
fil_space_get_crypt_data(
/*==================*/
/*=====================*/
ulint id) /*!< in: space id */
{
fil_space_t* space;
......@@ -7013,12 +7013,29 @@ fil_space_get_crypt_data(
return(crypt_data);
}
/******************************************************************
Get crypt data for a tablespace */
UNIV_INTERN
fil_space_crypt_t*
fil_space_crypt_data(
/*=================*/
fil_space_t* space) /*!< in: filespace */
{
fil_space_crypt_t* crypt_data = NULL;
if (space != NULL) {
crypt_data = space->crypt_data;
}
return(crypt_data);
}
/******************************************************************
Get crypt data for a tablespace */
UNIV_INTERN
void
fil_space_set_crypt_data(
/*==================*/
/*=====================*/
ulint id, /*!< in: space id */
fil_space_crypt_t* crypt_data) /*!< in: crypt data */
{
......
......@@ -87,6 +87,7 @@ static ulint srv_data_read, srv_data_written;
For page compressed pages compress the page before actual write
operation.
@return compressed page to be written*/
UNIV_INTERN
byte*
fil_compress_page(
/*==============*/
......@@ -373,6 +374,7 @@ fil_compress_page(
/****************************************************************//**
For page compressed pages decompress the page after actual read
operation. */
UNIV_INTERN
void
fil_decompress_page(
/*================*/
......
......@@ -67,3 +67,54 @@ fil_page_encryption_status(
}
return 0;
}
/******************************************************************
Map used AES method to crypt scheme
@return used AES crypt scheme */
UNIV_INLINE
uint
fil_crypt_get_aes_method(
/*=====================*/
uint aes_method)
{
switch (aes_method) {
case MY_AES_ALGORITHM_NONE:
return (uint) CRYPT_SCHEME_1_UNENCRYPTED;
break;
case MY_AES_ALGORITHM_CTR:
return (uint) CRYPT_SCHEME_1_CTR;
break;
case MY_AES_ALGORITHM_CBC:
return (uint) CRYPT_SCHEME_1_CBC;
break;
default:
ib_logf(IB_LOG_LEVEL_FATAL,
"Current AES method %d not supported.\n", aes_method);
ut_error;
}
return (uint) CRYPT_SCHEME_1_UNENCRYPTED;
}
/******************************************************************
Map current aes method
@return AES method */
UNIV_INLINE
uint
fil_crypt_map_aes_method(
/*=====================*/
uint aes_method) /*!< in: AES method */
{
switch((fil_crypt_method_t)aes_method) {
case CRYPT_SCHEME_1_CTR:
return (uint)MY_AES_ALGORITHM_CTR;
break;
case CRYPT_SCHEME_1_CBC:
return (uint)MY_AES_ALGORITHM_CBC;
break;
default:
ib_logf(IB_LOG_LEVEL_FATAL,
"Current AES method %d not supported.\n", (int)aes_method);
ut_error;
}
}
......@@ -34,6 +34,7 @@ Created 11/12/2013 Jan Lindström jan.lindstrom@skysql.com
Returns the page compression level flag of the space, or 0 if the space
is not compressed. The tablespace must be cached in the memory cache.
@return page compression level if page compressed, ULINT_UNDEFINED if space not found */
UNIV_INTERN
ulint
fil_space_get_page_compression_level(
/*=================================*/
......@@ -42,6 +43,7 @@ fil_space_get_page_compression_level(
Returns the page compression flag of the space, or false if the space
is not compressed. The tablespace must be cached in the memory cache.
@return true if page compressed, false if not or space not found */
UNIV_INTERN
ibool
fil_space_is_page_compressed(
/*=========================*/
......@@ -50,6 +52,7 @@ fil_space_is_page_compressed(
Returns the page compression flag of the space, or false if the space
is not compressed. The tablespace must be cached in the memory cache.
@return true if page compressed, false if not or space not found */
UNIV_INTERN
ibool
fil_space_get_page_compressed(
/*=========================*/
......@@ -58,6 +61,7 @@ fil_space_get_page_compressed(
Returns the atomic writes flag of the space, or false if the space
is not using atomic writes. The tablespace must be cached in the memory cache.
@return atomic write table option value */
UNIV_INTERN
atomic_writes_t
fil_space_get_atomic_writes(
/*=========================*/
......@@ -65,6 +69,7 @@ fil_space_get_atomic_writes(
/*******************************************************************//**
Find out wheather the page is index page or not
@return true if page type index page, false if not */
UNIV_INTERN
ibool
fil_page_is_index_page(
/*===================*/
......@@ -74,6 +79,7 @@ fil_page_is_index_page(
Get the name of the compression algorithm used for page
compression.
@return compression algorithm name or "UNKNOWN" if not known*/
UNIV_INTERN
const char*
fil_get_compression_alg_name(
/*=========================*/
......@@ -83,6 +89,7 @@ fil_get_compression_alg_name(
For page compressed pages compress the page before actual write
operation.
@return compressed page to be written*/
UNIV_INTERN
byte*
fil_compress_page(
/*==============*/
......@@ -102,6 +109,7 @@ fil_compress_page(
For page compressed pages decompress the page after actual read
operation.
@return uncompressed page */
UNIV_INTERN
void
fil_decompress_page(
/*================*/
......@@ -115,6 +123,7 @@ fil_decompress_page(
/****************************************************************//**
Get space id from fil node
@return space id*/
UNIV_INTERN
ulint
fil_node_get_space_id(
/*==================*/
......@@ -123,13 +132,16 @@ fil_node_get_space_id(
/****************************************************************//**
Get block size from fil node
@return block size*/
UNIV_INTERN
ulint
fil_node_get_block_size(
/*====================*/
fil_node_t* node); /*!< in: Node where to get block
size */
/*******************************************************************//**
Find out wheather the page is page compressed
@return true if page is page compressed*/
@return true if page is page compressed */
UNIV_INTERN
ibool
fil_page_is_compressed(
/*===================*/
......@@ -138,6 +150,7 @@ fil_page_is_compressed(
/*******************************************************************//**
Find out wheather the page is page compressed with lzo method
@return true if page is page compressed with lzo method*/
UNIV_INTERN
ibool
fil_page_is_lzo_compressed(
/*=======================*/
......
......@@ -23,6 +23,8 @@ extern my_bool srv_encrypt_log;
extern byte redo_log_crypt_msg[MY_AES_BLOCK_SIZE];
/* IV to concatenate with counter used by AES_CTR for redo log crypto. */
extern byte aes_ctr_nonce[MY_AES_BLOCK_SIZE];
/* AES method used for redo log encryption/decryption. */
extern byte redo_aes_method;
/*********************************************************************//**
Generate a 128-bit random message used to generate redo log crypto key.
......
......@@ -748,7 +748,7 @@ extern log_t* log_sys;
#define LOG_CHECKPOINT_OFFSET_HIGH32 (16 + LOG_CHECKPOINT_ARRAY_END)
#define LOG_CRYPT_VER (20 + LOG_CHECKPOINT_ARRAY_END)
/*!< 32-bit key version. Corresponding
key has been used for log records with
key has been used for log records with
lsn <= the checkpoint' lsn */
#define LOG_CRYPT_MSG (24 + LOG_CHECKPOINT_ARRAY_END)
/*!< a 128-bit value used to
......@@ -758,8 +758,10 @@ extern log_t* log_sys;
15-byte random number.*/
#define LOG_CRYPT_IV (40 + LOG_CHECKPOINT_ARRAY_END)
/*!< a 128-bit random number used as
AES-CTR iv/nonce for redo log */
#define LOG_CHECKPOINT_SIZE (56 + LOG_CHECKPOINT_ARRAY_END)
AES-* iv/nonce for redo log */
#define LOG_CRYPT_METHOD (56 + LOG_CHECKPOINT_ARRAY_END)
/*!< AES method used */
#define LOG_CHECKPOINT_SIZE (57 + LOG_CHECKPOINT_ARRAY_END)
/* Offsets of a log file header */
#define LOG_GROUP_ID 0 /* log group number */
......
......@@ -31,6 +31,8 @@ Modified Jan Lindström jan.lindstrom@mariadb.com
#include "log0log.h"
#include "srv0start.h" // for srv_start_lsn
#include "log0recv.h" // for recv_sys
#include "fil0fil.h" // for page types
#include "fil0crypt.h" // for aes_method
#include "mysql/plugin_encryption_key_management.h" // for BAD_ENCRYPTION_KEY_VERSION
#include "ha_prototypes.h" // IB_LOG_
......@@ -44,15 +46,17 @@ UNIV_INTERN my_bool srv_encrypt_log = FALSE;
static const byte redo_log_purpose_byte = 0x02;
/* Plain text used by AES_ECB to generate redo log crypt key. */
byte redo_log_crypt_msg[MY_AES_BLOCK_SIZE] = {0};
/* IV to concatenate with counter used by AES_CTR for redo log
/* IV to concatenate with counter used by AES for redo log
* encryption/decryption. */
byte aes_ctr_nonce[MY_AES_BLOCK_SIZE] = {0};
/* AES method used for redo log encryption/decryption. */
byte redo_aes_method = 0;
/*********************************************************************//**
Generate a 128-bit value used to generate crypt key for redo log.
It is generated via the concatenation of 1 purpose byte (0x02) and 15-byte
random number.
Init AES-CTR iv/nonce with random number.
Init AES iv/nonce with random number.
It is called when:
- redo logs do not exist when start up, or
- transition from without crypto.
......@@ -70,8 +74,8 @@ log_init_crypt_msg_and_nonce(void)
/*==============================*/
{
mach_write_to_1(redo_log_crypt_msg, redo_log_purpose_byte);
if (my_random_bytes(redo_log_crypt_msg + 1, PURPOSE_BYTE_LEN) != AES_OK)
{
if (my_random_bytes(redo_log_crypt_msg + 1, PURPOSE_BYTE_LEN) != AES_OK) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: generate "
"%u-byte random number as crypto msg failed.",
......@@ -79,14 +83,16 @@ log_init_crypt_msg_and_nonce(void)
abort();
}
if (my_random_bytes(aes_ctr_nonce, MY_AES_BLOCK_SIZE) != AES_OK)
{
if (my_random_bytes(aes_ctr_nonce, MY_AES_BLOCK_SIZE) != AES_OK) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: generate "
"%u-byte random number as AES_CTR nonce failed.",
"%u-byte random number failed.",
MY_AES_BLOCK_SIZE);
abort();
}
/* Set up current AES method */
redo_aes_method = (byte)fil_crypt_get_aes_method(current_aes_dynamic_method);
}
/*********************************************************************//**
......@@ -99,16 +105,14 @@ log_init_crypt_key(
const uint crypt_ver, /*< in: key version */
byte* key) /*< out: crypt key*/
{
if (crypt_ver == UNENCRYPTED_KEY_VER)
{
if (crypt_ver == UNENCRYPTED_KEY_VER) {
ib_logf(IB_LOG_LEVEL_INFO,
"Redo log crypto: unencrypted key ver.");
memset(key, 0, MY_AES_BLOCK_SIZE);
return;
}
if (crypt_msg[PURPOSE_BYTE_OFFSET] != redo_log_purpose_byte)
{
if (crypt_msg[PURPOSE_BYTE_OFFSET] != redo_log_purpose_byte) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: msg type mismatched. "
"Expected: %x; Actual: %x.",
......@@ -117,8 +121,8 @@ log_init_crypt_key(
}
byte mysqld_key[MY_AES_BLOCK_SIZE] = {0};
if (get_encryption_key(crypt_ver, mysqld_key, MY_AES_BLOCK_SIZE))
{
if (get_encryption_key(crypt_ver, mysqld_key, MY_AES_BLOCK_SIZE)) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: getting mysqld crypto key "
"from key version failed.");
......@@ -126,15 +130,16 @@ log_init_crypt_key(
}
uint32 dst_len;
my_aes_encrypt_dynamic_type func= get_aes_encrypt_func(MY_AES_ALGORITHM_ECB);
int rc= (*func)(crypt_msg, MY_AES_BLOCK_SIZE, //src, srclen
key, &dst_len, //dst, &dstlen
(unsigned char*)&mysqld_key, sizeof(mysqld_key),
NULL, 0,
1);
if (rc != AES_OK || dst_len != MY_AES_BLOCK_SIZE)
{
if (rc != AES_OK || dst_len != MY_AES_BLOCK_SIZE) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: getting redo log crypto key "
"failed.");
......@@ -159,7 +164,7 @@ log_block_get_start_lsn(
}
/*********************************************************************//**
Call AES CTR to encrypt/decrypt log blocks. */
Call AES to encrypt/decrypt log blocks. */
static
Crypt_result
log_blocks_crypt(
......@@ -176,21 +181,22 @@ log_blocks_crypt(
ulint log_block_no, log_block_start_lsn;
byte *key;
ulint lsn;
if (is_encrypt)
{
uint aes_method = (uint)redo_aes_method;
if (is_encrypt) {
ut_a(log_sys && log_sys->redo_log_crypt_ver != UNENCRYPTED_KEY_VER);
key = (byte *)(log_sys->redo_log_crypt_key);
lsn = log_sys->lsn;
} else {
ut_a(recv_sys && recv_sys->recv_log_crypt_ver != UNENCRYPTED_KEY_VER);
key = (byte *)(recv_sys->recv_log_crypt_key);
lsn = srv_start_lsn;
}
ut_a(size % OS_FILE_LOG_BLOCK_SIZE == 0);
src_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE;
for (ulint i = 0; i < size ; i += OS_FILE_LOG_BLOCK_SIZE)
{
for (ulint i = 0; i < size ; i += OS_FILE_LOG_BLOCK_SIZE) {
log_block_no = log_block_get_hdr_no(log_block);
log_block_start_lsn = log_block_get_start_lsn(lsn, log_block_no);
......@@ -206,11 +212,13 @@ log_blocks_crypt(
mach_write_to_4(aes_ctr_counter + 11, log_block_no);
bzero(aes_ctr_counter + 15, 1);
int rc = (* my_aes_encrypt_dynamic)(log_block + LOG_BLOCK_HDR_SIZE, src_len,
dst_block + LOG_BLOCK_HDR_SIZE, &dst_len,
(unsigned char*)key, 16,
aes_ctr_counter, MY_AES_BLOCK_SIZE,
1);
my_aes_encrypt_dynamic_type func = get_aes_encrypt_func((enum_my_aes_encryption_algorithm)fil_crypt_map_aes_method(aes_method));
int rc = (*func)(log_block + LOG_BLOCK_HDR_SIZE, src_len,
dst_block + LOG_BLOCK_HDR_SIZE, &dst_len,
(unsigned char*)key, 16,
aes_ctr_counter, MY_AES_BLOCK_SIZE,
1);
ut_a(rc == AES_OK);
ut_a(dst_len == src_len);
......@@ -303,13 +311,17 @@ log_crypt_write_checkpoint_buf(
{
ut_a(log_sys);
mach_write_to_4(buf + LOG_CRYPT_VER, log_sys->redo_log_crypt_ver);
if (!srv_encrypt_log ||
log_sys->redo_log_crypt_ver == UNENCRYPTED_KEY_VER) {
memset(buf + LOG_CRYPT_MSG, 0, MY_AES_BLOCK_SIZE);
memset(buf + LOG_CRYPT_IV, 0, MY_AES_BLOCK_SIZE);
memset(buf + LOG_CRYPT_METHOD, 0, 1);
return;
}
ut_a(redo_log_crypt_msg[PURPOSE_BYTE_OFFSET] == redo_log_purpose_byte);
memcpy(buf + LOG_CRYPT_MSG, redo_log_crypt_msg, MY_AES_BLOCK_SIZE);
memcpy(buf + LOG_CRYPT_IV, aes_ctr_nonce, MY_AES_BLOCK_SIZE);
mach_write_to_1(buf + LOG_CRYPT_METHOD, redo_aes_method);
}
......@@ -3164,13 +3164,16 @@ recv_recovery_from_checkpoint_start_func(
#ifdef UNIV_LOG_ARCHIVE
archived_lsn = mach_read_from_8(buf + LOG_CHECKPOINT_ARCHIVED_LSN);
#endif /* UNIV_LOG_ARCHIVE */
recv_crypt_ver = mach_read_from_4(buf + LOG_CRYPT_VER);
if (recv_crypt_ver == UNENCRYPTED_KEY_VER)
{
log_init_crypt_msg_and_nonce();
} else {
ut_memcpy(redo_log_crypt_msg, buf + LOG_CRYPT_MSG, MY_AES_BLOCK_SIZE);
ut_memcpy(aes_ctr_nonce, buf + LOG_CRYPT_IV, MY_AES_BLOCK_SIZE);
redo_aes_method = (byte)mach_read_from_1(buf + LOG_CRYPT_METHOD);
}
/* Read the first log file header to print a note if this is
......
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