Commit d259376f authored by Jan Lindström's avatar Jan Lindström

MDEV-8041: InnoDB redo log encryption

Merged new version of InnoDB/XtraDB redo log encryption from Google
provided by Jonas Oreland.
parent ab54f5a8
...@@ -9,69 +9,21 @@ Created 11/25/2013 Minli Zhu ...@@ -9,69 +9,21 @@ Created 11/25/2013 Minli Zhu
#include "univ.i" #include "univ.i"
#include "ut0byte.h" #include "ut0byte.h"
#include "ut0lst.h"
#include "ut0rnd.h"
#include "my_crypt.h" #include "my_crypt.h"
#define PURPOSE_BYTE_LEN MY_AES_BLOCK_SIZE - 1
#define PURPOSE_BYTE_OFFSET 0
#define UNENCRYPTED_KEY_VER ENCRYPTION_KEY_NOT_ENCRYPTED
typedef int Crypt_result; typedef int Crypt_result;
/* If true, enable redo log encryption. */ /* If true, enable redo log encryption. */
extern my_bool srv_encrypt_log; extern my_bool srv_encrypt_log;
/* Plain text used by AES_ECB to generate redo log crypt key. */
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];
/*********************************************************************//**
Generate a 128-bit random message used to generate redo log crypto key.
Init AES-CTR iv/nonce with random number.
It is called only when clean startup (i.e., redo logs do not exist). */
UNIV_INTERN
void
log_init_crypt_msg_and_nonce(void);
/*===============================*/
/*********************************************************************//**
Init log_sys redo log crypto key. */
UNIV_INTERN
void
log_init_crypt_key(
/*===============*/
const byte* crypt_msg, /*< in: crypt msg */
const uint crypt_ver, /*< in: mysqld key version */
byte* crypt_key); /*< out: crypt struct with key and iv */
/*********************************************************************//**
Encrypt log blocks. */
UNIV_INTERN
Crypt_result
log_blocks_encrypt(
/*===============*/
const byte* blocks, /*!< in: blocks before encryption */
const ulint size, /*!< in: size of blocks, must be multiple of a log block */
byte* dst_blocks); /*!< out: blocks after encryption */
/*********************************************************************//** /***********************************************************************
Decrypt log blocks. */ Set next checkpoint's key version to latest one, and generate new key */
UNIV_INTERN
Crypt_result
log_blocks_decrypt(
/*===============*/
const byte* blocks, /*!< in: blocks before decryption */
const ulint size, /*!< in: size of blocks, must be multiple of a log block */
byte* dst_blocks); /*!< out: blocks after decryption */
/*********************************************************************//**
Set next checkpoint's key version to latest one, and generate current
key. Key version 0 means no encryption. */
UNIV_INTERN UNIV_INTERN
void void
log_crypt_set_ver_and_key( log_crypt_set_ver_and_key(
/*======================*/ /*======================*/
uint& key_ver, /*!< out: latest key version */ ib_uint64_t next_checkpoint_no);
byte* crypt_key); /*!< out: crypto key */
/*********************************************************************//** /*********************************************************************//**
Writes the crypto (version, msg and iv) info, which has been used for Writes the crypto (version, msg and iv) info, which has been used for
...@@ -83,4 +35,34 @@ log_crypt_write_checkpoint_buf( ...@@ -83,4 +35,34 @@ log_crypt_write_checkpoint_buf(
/*===========================*/ /*===========================*/
byte* buf); /*!< in/out: checkpoint buffer */ byte* buf); /*!< in/out: checkpoint buffer */
/*********************************************************************//**
Read the crypto (version, msg and iv) info, which has been used for
log blocks with lsn <= this checkpoint's lsn, from a log header's
checkpoint buf. */
UNIV_INTERN
void
log_crypt_read_checkpoint_buf(
/*===========================*/
const byte* buf); /*!< in: checkpoint buffer */
/********************************************************
Encrypt one or more log block before it is flushed to disk */
UNIV_INTERN
void
log_encrypt_before_write(
/*===========================*/
ib_uint64_t next_checkpoint_no, /*!< in: log group to be flushed */
byte* block, /*!< in/out: pointer to a log block */
const ulint size); /*!< in: size of log blocks */
/********************************************************
Decrypt a specified log segment after they are read from a log file to a buffer.
*/
UNIV_INTERN
void
log_decrypt_after_read(
/*==========================*/
byte* frame, /*!< in/out: log segment */
const ulint size); /*!< in: log segment size */
#endif // log0crypt.h #endif // log0crypt.h
...@@ -677,19 +677,15 @@ extern log_t* log_sys; ...@@ -677,19 +677,15 @@ extern log_t* log_sys;
#endif #endif
#define LOG_CHECKPOINT_OFFSET_HIGH32 (16 + LOG_CHECKPOINT_ARRAY_END) #define LOG_CHECKPOINT_OFFSET_HIGH32 (16 + LOG_CHECKPOINT_ARRAY_END)
#define LOG_CRYPT_VER (20 + 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 #define LOG_CRYPT_MAX_ENTRIES (5)
lsn <= the checkpoint' lsn */ #define LOG_CRYPT_ENTRY_SIZE (4 + 4 + 2 * MY_AES_BLOCK_SIZE)
#define LOG_CRYPT_MSG (24 + LOG_CHECKPOINT_ARRAY_END) #define LOG_CRYPT_SIZE (1 + 1 + \
/*!< a 128-bit value used to (LOG_CRYPT_MAX_ENTRIES * \
derive cryto key for redo log. LOG_CRYPT_ENTRY_SIZE))
It is generated via the concatenation
of 1 purpose byte T (0x02) and a #define LOG_CHECKPOINT_SIZE (20 + LOG_CHECKPOINT_ARRAY_END + \
15-byte random number.*/ LOG_CRYPT_SIZE)
#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)
/* Offsets of a log file header */ /* Offsets of a log file header */
#define LOG_GROUP_ID 0 /* log group number */ #define LOG_GROUP_ID 0 /* log group number */
...@@ -794,10 +790,6 @@ struct log_t{ ...@@ -794,10 +790,6 @@ struct log_t{
lsn_t lsn; /*!< log sequence number */ lsn_t lsn; /*!< log sequence number */
ulint buf_free; /*!< first free offset within the log ulint buf_free; /*!< first free offset within the log
buffer */ buffer */
uint redo_log_crypt_ver;
/*!< 32-bit crypto ver */
byte redo_log_crypt_key[MY_AES_BLOCK_SIZE];
/*!< crypto key to encrypt redo log */
#ifndef UNIV_HOTBACKUP #ifndef UNIV_HOTBACKUP
ib_mutex_t mutex; /*!< mutex protecting the log */ ib_mutex_t mutex; /*!< mutex protecting the log */
......
...@@ -434,11 +434,6 @@ struct recv_sys_t{ ...@@ -434,11 +434,6 @@ struct recv_sys_t{
scan find a corrupt log block, or a corrupt scan find a corrupt log block, or a corrupt
log record, or there is a log parsing log record, or there is a log parsing
buffer overflow */ buffer overflow */
uint recv_log_crypt_ver;
/*!< mysqld key version to generate redo
log crypt key for recovery */
byte recv_log_crypt_key[MY_AES_BLOCK_SIZE];
/*!< crypto key to decrypt redo log for recovery */
#ifdef UNIV_LOG_ARCHIVE #ifdef UNIV_LOG_ARCHIVE
log_group_t* archive_group; log_group_t* archive_group;
/*!< in archive recovery: the log group whose /*!< in archive recovery: the log group whose
......
This diff is collapsed.
...@@ -902,7 +902,6 @@ log_init(void) ...@@ -902,7 +902,6 @@ log_init(void)
/*----------------------------*/ /*----------------------------*/
log_sys->next_checkpoint_no = 0; log_sys->next_checkpoint_no = 0;
log_sys->redo_log_crypt_ver = UNENCRYPTED_KEY_VER;
log_sys->last_checkpoint_lsn = log_sys->lsn; log_sys->last_checkpoint_lsn = log_sys->lsn;
log_sys->n_pending_checkpoint_writes = 0; log_sys->n_pending_checkpoint_writes = 0;
...@@ -1295,36 +1294,6 @@ log_block_store_checksum( ...@@ -1295,36 +1294,6 @@ log_block_store_checksum(
log_block_set_checksum(block, log_block_calc_checksum(block)); log_block_set_checksum(block, log_block_calc_checksum(block));
} }
/******************************************************//**
Encrypt one or more log block before it is flushed to disk
@return true if encryption succeeds. */
static
bool
log_group_encrypt_before_write(
/*===========================*/
const log_group_t* group, /*!< in: log group to be flushed */
byte* block, /*!< in/out: pointer to a log block */
const ulint size) /*!< in: size of log blocks */
{
Crypt_result result = MY_AES_OK;
ut_ad(size % OS_FILE_LOG_BLOCK_SIZE == 0);
byte* dst_frame = (byte*)malloc(size);
//encrypt log blocks content
result = log_blocks_encrypt(block, size, dst_frame);
if (result == MY_AES_OK)
{
ut_ad(block[0] == dst_frame[0]);
memcpy(block, dst_frame, size);
}
free(dst_frame);
return (result == MY_AES_OK);
}
/******************************************************//** /******************************************************//**
Writes a buffer to a log file group. */ Writes a buffer to a log file group. */
UNIV_INTERN UNIV_INTERN
...@@ -1431,14 +1400,8 @@ log_group_write_buf( ...@@ -1431,14 +1400,8 @@ log_group_write_buf(
ut_a(next_offset / UNIV_PAGE_SIZE <= ULINT_MAX); ut_a(next_offset / UNIV_PAGE_SIZE <= ULINT_MAX);
if (srv_encrypt_log && log_encrypt_before_write(log_sys->next_checkpoint_no,
log_sys->redo_log_crypt_ver != UNENCRYPTED_KEY_VER && buf, write_len);
!log_group_encrypt_before_write(group, buf, write_len))
{
fprintf(stderr,
"\nInnodb redo log encryption failed.\n");
abort();
}
fil_io(OS_FILE_WRITE | OS_FILE_LOG, true, group->space_id, 0, fil_io(OS_FILE_WRITE | OS_FILE_LOG, true, group->space_id, 0,
(ulint) (next_offset / UNIV_PAGE_SIZE), (ulint) (next_offset / UNIV_PAGE_SIZE),
...@@ -2201,12 +2164,15 @@ log_checkpoint( ...@@ -2201,12 +2164,15 @@ log_checkpoint(
} }
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
/* generate key version and key used to encrypt future blocks,
*
* NOTE: the +1 is as the next_checkpoint_no will be updated once
* the checkpoint info has been written and THEN blocks will be encrypted
* with new key
*/
log_crypt_set_ver_and_key(log_sys->next_checkpoint_no + 1);
log_groups_write_checkpoint_info(); log_groups_write_checkpoint_info();
/* generate key version and key used to encrypt next log block */
log_crypt_set_ver_and_key(log_sys->redo_log_crypt_ver,
log_sys->redo_log_crypt_key);
MONITOR_INC(MONITOR_NUM_CHECKPOINT); MONITOR_INC(MONITOR_NUM_CHECKPOINT);
mutex_exit(&(log_sys->mutex)); mutex_exit(&(log_sys->mutex));
...@@ -2339,33 +2305,6 @@ log_checkpoint_margin(void) ...@@ -2339,33 +2305,6 @@ log_checkpoint_margin(void)
} }
} }
/******************************************************//**
Decrypt a specified log segment after they are read from a log file to a buffer.
@return true if decryption succeeds. */
static
bool
log_group_decrypt_after_read(
/*==========================*/
const log_group_t* group, /*!< in: log group to be read from */
byte* frame, /*!< in/out: log segment */
const ulint size) /*!< in: log segment size */
{
Crypt_result result;
ut_ad(size % OS_FILE_LOG_BLOCK_SIZE == 0);
byte* dst_frame = (byte*)malloc(size);
// decrypt log blocks content
result = log_blocks_decrypt(frame, size, dst_frame);
if (result == MY_AES_OK)
{
memcpy(frame, dst_frame, size);
}
free(dst_frame);
return (result == MY_AES_OK);
}
/******************************************************//** /******************************************************//**
Reads a specified log segment to a buffer. */ Reads a specified log segment to a buffer. */
UNIV_INTERN UNIV_INTERN
...@@ -2419,12 +2358,7 @@ log_group_read_log_seg( ...@@ -2419,12 +2358,7 @@ log_group_read_log_seg(
(ulint) (source_offset % UNIV_PAGE_SIZE), (ulint) (source_offset % UNIV_PAGE_SIZE),
len, buf, NULL, 0); len, buf, NULL, 0);
if (recv_sys->recv_log_crypt_ver != UNENCRYPTED_KEY_VER && log_decrypt_after_read(buf, len);
!log_group_decrypt_after_read(group, buf, len))
{
fprintf(stderr, "Innodb redo log decryption failed.\n");
abort();
}
start_lsn += len; start_lsn += len;
buf += len; buf += len;
...@@ -2649,13 +2583,8 @@ log_group_archive( ...@@ -2649,13 +2583,8 @@ log_group_archive(
MONITOR_INC(MONITOR_LOG_IO); MONITOR_INC(MONITOR_LOG_IO);
if (srv_encrypt_log && //TODO (jonaso): This must be dead code??
log_sys->redo_log_crypt_ver != UNENCRYPTED_KEY_VER && log_encrypt_before_write(log_sys->next_checkpoint_no, buf, len);
!log_group_encrypt_before_write(group, buf, len))
{
fprintf(stderr, "Innodb redo log encryption failed.\n");
abort();
}
fil_io(OS_FILE_WRITE | OS_FILE_LOG, false, group->archive_space_id, fil_io(OS_FILE_WRITE | OS_FILE_LOG, false, group->archive_space_id,
(ulint) (next_offset / UNIV_PAGE_SIZE), (ulint) (next_offset / UNIV_PAGE_SIZE),
......
...@@ -805,6 +805,7 @@ recv_find_max_checkpoint( ...@@ -805,6 +805,7 @@ recv_find_max_checkpoint(
buf + LOG_CHECKPOINT_OFFSET_HIGH32)) << 32; buf + LOG_CHECKPOINT_OFFSET_HIGH32)) << 32;
checkpoint_no = mach_read_from_8( checkpoint_no = mach_read_from_8(
buf + LOG_CHECKPOINT_NO); buf + LOG_CHECKPOINT_NO);
log_crypt_read_checkpoint_buf(buf);
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
if (log_debug_writes) { if (log_debug_writes) {
...@@ -935,6 +936,12 @@ log_block_checksum_is_ok_or_old_format( ...@@ -935,6 +936,12 @@ log_block_checksum_is_ok_or_old_format(
return(TRUE); return(TRUE);
} }
fprintf(stderr, "BROKEN: block: %lu checkpoint: %lu %.8lx %.8lx\n",
log_block_get_hdr_no(block),
log_block_get_checkpoint_no(block),
log_block_calc_checksum(block),
log_block_get_checksum(block));
return(FALSE); return(FALSE);
} }
...@@ -2746,6 +2753,13 @@ recv_scan_log_recs( ...@@ -2746,6 +2753,13 @@ recv_scan_log_recs(
finished = TRUE; finished = TRUE;
/* Crash if we encounter a garbage log block */
if (!srv_force_recovery) {
fputs("InnoDB: Set innodb_force_recovery"
" to ignore this error.\n", stderr);
ut_error;
}
break; break;
} }
...@@ -3028,7 +3042,6 @@ recv_recovery_from_checkpoint_start_func( ...@@ -3028,7 +3042,6 @@ recv_recovery_from_checkpoint_start_func(
ulint max_cp_field; ulint max_cp_field;
lsn_t checkpoint_lsn; lsn_t checkpoint_lsn;
ib_uint64_t checkpoint_no; ib_uint64_t checkpoint_no;
uint recv_crypt_ver;
lsn_t group_scanned_lsn = 0; lsn_t group_scanned_lsn = 0;
lsn_t contiguous_lsn; lsn_t contiguous_lsn;
#ifdef UNIV_LOG_ARCHIVE #ifdef UNIV_LOG_ARCHIVE
...@@ -3088,14 +3101,6 @@ recv_recovery_from_checkpoint_start_func( ...@@ -3088,14 +3101,6 @@ recv_recovery_from_checkpoint_start_func(
#ifdef UNIV_LOG_ARCHIVE #ifdef UNIV_LOG_ARCHIVE
archived_lsn = mach_read_from_8(buf + LOG_CHECKPOINT_ARCHIVED_LSN); archived_lsn = mach_read_from_8(buf + LOG_CHECKPOINT_ARCHIVED_LSN);
#endif /* UNIV_LOG_ARCHIVE */ #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);
}
/* Read the first log file header to print a note if this is /* Read the first log file header to print a note if this is
a recovery from a restored InnoDB Hot Backup */ a recovery from a restored InnoDB Hot Backup */
...@@ -3152,15 +3157,10 @@ recv_recovery_from_checkpoint_start_func( ...@@ -3152,15 +3157,10 @@ recv_recovery_from_checkpoint_start_func(
/* Start reading the log groups from the checkpoint lsn up. The /* Start reading the log groups from the checkpoint lsn up. The
variable contiguous_lsn contains an lsn up to which the log is variable contiguous_lsn contains an lsn up to which the log is
known to be contiguously written to all log groups. */ known to be contiguously written to all log groups. */
recv_sys->parse_start_lsn = checkpoint_lsn; recv_sys->parse_start_lsn = checkpoint_lsn;
recv_sys->scanned_lsn = checkpoint_lsn; recv_sys->scanned_lsn = checkpoint_lsn;
recv_sys->scanned_checkpoint_no = 0; recv_sys->scanned_checkpoint_no = 0;
recv_sys->recovered_lsn = checkpoint_lsn; recv_sys->recovered_lsn = checkpoint_lsn;
recv_sys->recv_log_crypt_ver = recv_crypt_ver;
log_init_crypt_key(redo_log_crypt_msg,
recv_sys->recv_log_crypt_ver,
recv_sys->recv_log_crypt_key);
srv_start_lsn = checkpoint_lsn; srv_start_lsn = checkpoint_lsn;
} }
...@@ -3330,8 +3330,9 @@ recv_recovery_from_checkpoint_start_func( ...@@ -3330,8 +3330,9 @@ recv_recovery_from_checkpoint_start_func(
log_sys->next_checkpoint_lsn = checkpoint_lsn; log_sys->next_checkpoint_lsn = checkpoint_lsn;
log_sys->next_checkpoint_no = checkpoint_no + 1; log_sys->next_checkpoint_no = checkpoint_no + 1;
log_crypt_set_ver_and_key(log_sys->redo_log_crypt_ver, /* here the checkpoint info is written without any redo logging ongoing
log_sys->redo_log_crypt_key); * and next_checkpoint_no is updated directly hence no +1 */
log_crypt_set_ver_and_key(log_sys->next_checkpoint_no);
#ifdef UNIV_LOG_ARCHIVE #ifdef UNIV_LOG_ARCHIVE
log_sys->archived_lsn = archived_lsn; log_sys->archived_lsn = archived_lsn;
...@@ -3362,8 +3363,7 @@ recv_recovery_from_checkpoint_start_func( ...@@ -3362,8 +3363,7 @@ recv_recovery_from_checkpoint_start_func(
log_sys->lsn - log_sys->last_checkpoint_lsn); log_sys->lsn - log_sys->last_checkpoint_lsn);
log_sys->next_checkpoint_no = checkpoint_no + 1; log_sys->next_checkpoint_no = checkpoint_no + 1;
log_crypt_set_ver_and_key(log_sys->redo_log_crypt_ver, log_crypt_set_ver_and_key(log_sys->next_checkpoint_no);
log_sys->redo_log_crypt_key);
#ifdef UNIV_LOG_ARCHIVE #ifdef UNIV_LOG_ARCHIVE
if (archived_lsn == LSN_MAX) { if (archived_lsn == LSN_MAX) {
...@@ -3566,16 +3566,6 @@ recv_reset_logs( ...@@ -3566,16 +3566,6 @@ recv_reset_logs(
log_sys->next_checkpoint_no = 0; log_sys->next_checkpoint_no = 0;
log_sys->last_checkpoint_lsn = 0; log_sys->last_checkpoint_lsn = 0;
/* redo_log_crypt_ver will be set by log_checkpoint() to the
latest key version. */
log_sys->redo_log_crypt_ver = UNENCRYPTED_KEY_VER;
/*
Note: flags (srv_encrypt_log and debug_use_static_keys)
haven't been read and set yet!
So don't use condition such as:
if (srv_encrypt_log && debug_use_static_keys)
*/
log_init_crypt_msg_and_nonce();
#ifdef UNIV_LOG_ARCHIVE #ifdef UNIV_LOG_ARCHIVE
log_sys->archived_lsn = log_sys->lsn; log_sys->archived_lsn = log_sys->lsn;
......
...@@ -2912,6 +2912,15 @@ innobase_start_or_create_for_mysql(void) ...@@ -2912,6 +2912,15 @@ innobase_start_or_create_for_mysql(void)
(ulong) srv_force_recovery); (ulong) srv_force_recovery);
} }
if (!srv_read_only_mode) {
/*
Create a checkpoint before logging anything new, so that
the current encryption key in use is definitely logged
before any log blocks encrypted with that key.
*/
log_make_checkpoint_at(LSN_MAX, TRUE);
}
if (srv_force_recovery == 0) { if (srv_force_recovery == 0) {
/* In the insert buffer we may have even bigger tablespace /* In the insert buffer we may have even bigger tablespace
id's, because we may have dropped those tablespaces, but id's, because we may have dropped those tablespaces, but
......
...@@ -9,69 +9,21 @@ Created 11/25/2013 Minli Zhu ...@@ -9,69 +9,21 @@ Created 11/25/2013 Minli Zhu
#include "univ.i" #include "univ.i"
#include "ut0byte.h" #include "ut0byte.h"
#include "ut0lst.h"
#include "ut0rnd.h"
#include "my_crypt.h" #include "my_crypt.h"
#define PURPOSE_BYTE_LEN MY_AES_BLOCK_SIZE - 1
#define PURPOSE_BYTE_OFFSET 0
#define UNENCRYPTED_KEY_VER ENCRYPTION_KEY_NOT_ENCRYPTED
typedef int Crypt_result; typedef int Crypt_result;
/* If true, enable redo log encryption. */ /* If true, enable redo log encryption. */
extern my_bool srv_encrypt_log; extern my_bool srv_encrypt_log;
/* Plain text used by AES_ECB to generate redo log crypt key. */
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];
/*********************************************************************//**
Generate a 128-bit random message used to generate redo log crypto key.
Init AES-CTR iv/nonce with random number.
It is called only when clean startup (i.e., redo logs do not exist). */
UNIV_INTERN
void
log_init_crypt_msg_and_nonce(void);
/*===============================*/
/*********************************************************************//**
Init log_sys redo log crypto key. */
UNIV_INTERN
void
log_init_crypt_key(
/*===============*/
const byte* crypt_msg, /*< in: crypt msg */
const uint crypt_ver, /*< in: mysqld key version */
byte* crypt_key); /*< out: crypt struct with key and iv */
/*********************************************************************//**
Encrypt log blocks. */
UNIV_INTERN
Crypt_result
log_blocks_encrypt(
/*===============*/
const byte* blocks, /*!< in: blocks before encryption */
const ulint size, /*!< in: size of blocks, must be multiple of a log block */
byte* dst_blocks); /*!< out: blocks after encryption */
/*********************************************************************//** /***********************************************************************
Decrypt log blocks. */ Set next checkpoint's key version to latest one, and generate new key */
UNIV_INTERN
Crypt_result
log_blocks_decrypt(
/*===============*/
const byte* blocks, /*!< in: blocks before decryption */
const ulint size, /*!< in: size of blocks, must be multiple of a log block */
byte* dst_blocks); /*!< out: blocks after decryption */
/*********************************************************************//**
Set next checkpoint's key version to latest one, and generate current
key. Key version 0 means no encryption. */
UNIV_INTERN UNIV_INTERN
void void
log_crypt_set_ver_and_key( log_crypt_set_ver_and_key(
/*======================*/ /*======================*/
uint& key_ver, /*!< out: latest key version */ ib_uint64_t next_checkpoint_no);
byte* crypt_key); /*!< out: crypto key */
/*********************************************************************//** /*********************************************************************//**
Writes the crypto (version, msg and iv) info, which has been used for Writes the crypto (version, msg and iv) info, which has been used for
...@@ -83,4 +35,34 @@ log_crypt_write_checkpoint_buf( ...@@ -83,4 +35,34 @@ log_crypt_write_checkpoint_buf(
/*===========================*/ /*===========================*/
byte* buf); /*!< in/out: checkpoint buffer */ byte* buf); /*!< in/out: checkpoint buffer */
/*********************************************************************//**
Read the crypto (version, msg and iv) info, which has been used for
log blocks with lsn <= this checkpoint's lsn, from a log header's
checkpoint buf. */
UNIV_INTERN
void
log_crypt_read_checkpoint_buf(
/*===========================*/
const byte* buf); /*!< in: checkpoint buffer */
/********************************************************
Encrypt one or more log block before it is flushed to disk */
UNIV_INTERN
void
log_encrypt_before_write(
/*===========================*/
ib_uint64_t next_checkpoint_no, /*!< in: log group to be flushed */
byte* block, /*!< in/out: pointer to a log block */
const ulint size); /*!< in: size of log blocks */
/********************************************************
Decrypt a specified log segment after they are read from a log file to a buffer.
*/
UNIV_INTERN
void
log_decrypt_after_read(
/*==========================*/
byte* frame, /*!< in/out: log segment */
const ulint size); /*!< in: log segment size */
#endif // log0crypt.h #endif // log0crypt.h
...@@ -747,19 +747,15 @@ extern log_t* log_sys; ...@@ -747,19 +747,15 @@ extern log_t* log_sys;
#endif #endif
#define LOG_CHECKPOINT_OFFSET_HIGH32 (16 + LOG_CHECKPOINT_ARRAY_END) #define LOG_CHECKPOINT_OFFSET_HIGH32 (16 + LOG_CHECKPOINT_ARRAY_END)
#define LOG_CRYPT_VER (20 + 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 #define LOG_CRYPT_MAX_ENTRIES (5)
lsn <= the checkpoint' lsn */ #define LOG_CRYPT_ENTRY_SIZE (4 + 4 + 2 * MY_AES_BLOCK_SIZE)
#define LOG_CRYPT_MSG (24 + LOG_CHECKPOINT_ARRAY_END) #define LOG_CRYPT_SIZE (1 + 1 + \
/*!< a 128-bit value used to (LOG_CRYPT_MAX_ENTRIES * \
derive cryto key for redo log. LOG_CRYPT_ENTRY_SIZE))
It is generated via the concatenation
of 1 purpose byte T (0x02) and a #define LOG_CHECKPOINT_SIZE (20 + LOG_CHECKPOINT_ARRAY_END + \
15-byte random number.*/ LOG_CRYPT_SIZE)
#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)
/* Offsets of a log file header */ /* Offsets of a log file header */
#define LOG_GROUP_ID 0 /* log group number */ #define LOG_GROUP_ID 0 /* log group number */
...@@ -867,10 +863,6 @@ struct log_t{ ...@@ -867,10 +863,6 @@ struct log_t{
lsn_t lsn; /*!< log sequence number */ lsn_t lsn; /*!< log sequence number */
ulint buf_free; /*!< first free offset within the log ulint buf_free; /*!< first free offset within the log
buffer */ buffer */
uint redo_log_crypt_ver;
/*!< 32-bit crypto ver */
byte redo_log_crypt_key[MY_AES_BLOCK_SIZE];
/*!< crypto key to encrypt redo log */
#ifndef UNIV_HOTBACKUP #ifndef UNIV_HOTBACKUP
ib_prio_mutex_t mutex; /*!< mutex protecting the log */ ib_prio_mutex_t mutex; /*!< mutex protecting the log */
......
...@@ -471,11 +471,6 @@ struct recv_sys_t{ ...@@ -471,11 +471,6 @@ struct recv_sys_t{
scan find a corrupt log block, or a corrupt scan find a corrupt log block, or a corrupt
log record, or there is a log parsing log record, or there is a log parsing
buffer overflow */ buffer overflow */
uint recv_log_crypt_ver;
/*!< mysqld key version to generate redo
log crypt key for recovery */
byte recv_log_crypt_key[MY_AES_BLOCK_SIZE];
/*!< crypto key to decrypt redo log for recovery */
#ifdef UNIV_LOG_ARCHIVE #ifdef UNIV_LOG_ARCHIVE
log_group_t* archive_group; log_group_t* archive_group;
/*!< in archive recovery: the log group whose /*!< in archive recovery: the log group whose
......
This diff is collapsed.
...@@ -1005,7 +1005,6 @@ log_init(void) ...@@ -1005,7 +1005,6 @@ log_init(void)
/*----------------------------*/ /*----------------------------*/
log_sys->next_checkpoint_no = 0; log_sys->next_checkpoint_no = 0;
log_sys->redo_log_crypt_ver = UNENCRYPTED_KEY_VER;
log_sys->last_checkpoint_lsn = log_sys->lsn; log_sys->last_checkpoint_lsn = log_sys->lsn;
log_sys->n_pending_checkpoint_writes = 0; log_sys->n_pending_checkpoint_writes = 0;
...@@ -1403,36 +1402,6 @@ log_block_store_checksum( ...@@ -1403,36 +1402,6 @@ log_block_store_checksum(
log_block_set_checksum(block, log_block_calc_checksum(block)); log_block_set_checksum(block, log_block_calc_checksum(block));
} }
/******************************************************//**
Encrypt one or more log block before it is flushed to disk
@return true if encryption succeeds. */
static
bool
log_group_encrypt_before_write(
/*===========================*/
const log_group_t* group, /*!< in: log group to be flushed */
byte* block, /*!< in/out: pointer to a log block */
const ulint size) /*!< in: size of log blocks */
{
Crypt_result result = MY_AES_OK;
ut_ad(size % OS_FILE_LOG_BLOCK_SIZE == 0);
byte* dst_frame = (byte*)malloc(size);
//encrypt log blocks content
result = log_blocks_encrypt(block, size, dst_frame);
if (result == MY_AES_OK)
{
ut_ad(block[0] == dst_frame[0]);
memcpy(block, dst_frame, size);
}
free(dst_frame);
return (result == MY_AES_OK);
}
/******************************************************//** /******************************************************//**
Writes a buffer to a log file group. */ Writes a buffer to a log file group. */
UNIV_INTERN UNIV_INTERN
...@@ -1539,14 +1508,8 @@ log_group_write_buf( ...@@ -1539,14 +1508,8 @@ log_group_write_buf(
ut_a(next_offset / UNIV_PAGE_SIZE <= ULINT_MAX); ut_a(next_offset / UNIV_PAGE_SIZE <= ULINT_MAX);
if (srv_encrypt_log && log_encrypt_before_write(log_sys->next_checkpoint_no,
log_sys->redo_log_crypt_ver != UNENCRYPTED_KEY_VER && buf, write_len);
!log_group_encrypt_before_write(group, buf, write_len))
{
fprintf(stderr,
"\nInnodb redo log encryption failed.\n");
abort();
}
fil_io(OS_FILE_WRITE | OS_FILE_LOG, true, group->space_id, 0, fil_io(OS_FILE_WRITE | OS_FILE_LOG, true, group->space_id, 0,
(ulint) (next_offset / UNIV_PAGE_SIZE), (ulint) (next_offset / UNIV_PAGE_SIZE),
...@@ -2350,12 +2313,15 @@ log_checkpoint( ...@@ -2350,12 +2313,15 @@ log_checkpoint(
} }
#endif /* UNIV_DEBUG */ #endif /* UNIV_DEBUG */
/* generate key version and key used to encrypt future blocks,
*
* NOTE: the +1 is as the next_checkpoint_no will be updated once
* the checkpoint info has been written and THEN blocks will be encrypted
* with new key
*/
log_crypt_set_ver_and_key(log_sys->next_checkpoint_no + 1);
log_groups_write_checkpoint_info(); log_groups_write_checkpoint_info();
/* generate key version and key used to encrypt next log block */
log_crypt_set_ver_and_key(log_sys->redo_log_crypt_ver,
log_sys->redo_log_crypt_key);
MONITOR_INC(MONITOR_NUM_CHECKPOINT); MONITOR_INC(MONITOR_NUM_CHECKPOINT);
mutex_exit(&(log_sys->mutex)); mutex_exit(&(log_sys->mutex));
...@@ -2554,33 +2520,6 @@ log_checkpoint_margin(void) ...@@ -2554,33 +2520,6 @@ log_checkpoint_margin(void)
} }
} }
/******************************************************//**
Decrypt a specified log segment after they are read from a log file to a buffer.
@return true if decryption succeeds. */
static
bool
log_group_decrypt_after_read(
/*==========================*/
const log_group_t* group, /*!< in: log group to be read from */
byte* frame, /*!< in/out: log segment */
const ulint size) /*!< in: log segment size */
{
Crypt_result result;
ut_ad(size % OS_FILE_LOG_BLOCK_SIZE == 0);
byte* dst_frame = (byte*)malloc(size);
// decrypt log blocks content
result = log_blocks_decrypt(frame, size, dst_frame);
if (result == MY_AES_OK)
{
memcpy(frame, dst_frame, size);
}
free(dst_frame);
return (result == MY_AES_OK);
}
/******************************************************//** /******************************************************//**
Reads a specified log segment to a buffer. Optionally releases the log mutex Reads a specified log segment to a buffer. Optionally releases the log mutex
before the I/O. */ before the I/O. */
...@@ -2641,12 +2580,7 @@ log_group_read_log_seg( ...@@ -2641,12 +2580,7 @@ log_group_read_log_seg(
(ulint) (source_offset % UNIV_PAGE_SIZE), (ulint) (source_offset % UNIV_PAGE_SIZE),
len, buf, (type == LOG_ARCHIVE) ? &log_archive_io : NULL, 0); len, buf, (type == LOG_ARCHIVE) ? &log_archive_io : NULL, 0);
if (recv_sys->recv_log_crypt_ver != UNENCRYPTED_KEY_VER && log_decrypt_after_read(buf, len);
!log_group_decrypt_after_read(group, buf, len))
{
fprintf(stderr, "Innodb redo log decryption failed.\n");
abort();
}
start_lsn += len; start_lsn += len;
buf += len; buf += len;
...@@ -2940,13 +2874,8 @@ log_group_archive( ...@@ -2940,13 +2874,8 @@ log_group_archive(
MONITOR_INC(MONITOR_LOG_IO); MONITOR_INC(MONITOR_LOG_IO);
if (srv_encrypt_log && //TODO (jonaso): This must be dead code??
log_sys->redo_log_crypt_ver != UNENCRYPTED_KEY_VER && log_encrypt_before_write(log_sys->next_checkpoint_no, buf, len);
!log_group_encrypt_before_write(group, buf, len))
{
fprintf(stderr, "Innodb redo log encryption failed.\n");
abort();
}
fil_io(OS_FILE_WRITE | OS_FILE_LOG, false, group->archive_space_id, fil_io(OS_FILE_WRITE | OS_FILE_LOG, false, group->archive_space_id,
0, 0,
......
...@@ -810,6 +810,7 @@ recv_find_max_checkpoint( ...@@ -810,6 +810,7 @@ recv_find_max_checkpoint(
buf + LOG_CHECKPOINT_OFFSET_HIGH32)) << 32; buf + LOG_CHECKPOINT_OFFSET_HIGH32)) << 32;
checkpoint_no = mach_read_from_8( checkpoint_no = mach_read_from_8(
buf + LOG_CHECKPOINT_NO); buf + LOG_CHECKPOINT_NO);
log_crypt_read_checkpoint_buf(buf);
#ifdef UNIV_DEBUG #ifdef UNIV_DEBUG
if (log_debug_writes) { if (log_debug_writes) {
...@@ -1000,6 +1001,12 @@ log_block_checksum_is_ok_or_old_format( ...@@ -1000,6 +1001,12 @@ log_block_checksum_is_ok_or_old_format(
return(TRUE); return(TRUE);
} }
fprintf(stderr, "BROKEN: block: %lu checkpoint: %lu %.8lx %.8lx\n",
log_block_get_hdr_no(block),
log_block_get_checkpoint_no(block),
log_block_calc_checksum(block),
log_block_get_checksum(block));
return(FALSE); return(FALSE);
} }
...@@ -2816,6 +2823,13 @@ recv_scan_log_recs( ...@@ -2816,6 +2823,13 @@ recv_scan_log_recs(
finished = TRUE; finished = TRUE;
/* Crash if we encounter a garbage log block */
if (!srv_force_recovery) {
fputs("InnoDB: Set innodb_force_recovery"
" to ignore this error.\n", stderr);
ut_error;
}
break; break;
} }
...@@ -3099,7 +3113,6 @@ recv_recovery_from_checkpoint_start_func( ...@@ -3099,7 +3113,6 @@ recv_recovery_from_checkpoint_start_func(
ulint log_hdr_log_block_size; ulint log_hdr_log_block_size;
lsn_t checkpoint_lsn; lsn_t checkpoint_lsn;
ib_uint64_t checkpoint_no; ib_uint64_t checkpoint_no;
uint recv_crypt_ver;
lsn_t group_scanned_lsn = 0; lsn_t group_scanned_lsn = 0;
lsn_t contiguous_lsn; lsn_t contiguous_lsn;
#ifdef UNIV_LOG_ARCHIVE #ifdef UNIV_LOG_ARCHIVE
...@@ -3164,14 +3177,6 @@ recv_recovery_from_checkpoint_start_func( ...@@ -3164,14 +3177,6 @@ recv_recovery_from_checkpoint_start_func(
#ifdef UNIV_LOG_ARCHIVE #ifdef UNIV_LOG_ARCHIVE
archived_lsn = mach_read_from_8(buf + LOG_CHECKPOINT_ARCHIVED_LSN); archived_lsn = mach_read_from_8(buf + LOG_CHECKPOINT_ARCHIVED_LSN);
#endif /* UNIV_LOG_ARCHIVE */ #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);
}
/* Read the first log file header to print a note if this is /* Read the first log file header to print a note if this is
a recovery from a restored InnoDB Hot Backup */ a recovery from a restored InnoDB Hot Backup */
...@@ -3245,15 +3250,10 @@ recv_recovery_from_checkpoint_start_func( ...@@ -3245,15 +3250,10 @@ recv_recovery_from_checkpoint_start_func(
/* Start reading the log groups from the checkpoint lsn up. The /* Start reading the log groups from the checkpoint lsn up. The
variable contiguous_lsn contains an lsn up to which the log is variable contiguous_lsn contains an lsn up to which the log is
known to be contiguously written to all log groups. */ known to be contiguously written to all log groups. */
recv_sys->parse_start_lsn = checkpoint_lsn; recv_sys->parse_start_lsn = checkpoint_lsn;
recv_sys->scanned_lsn = checkpoint_lsn; recv_sys->scanned_lsn = checkpoint_lsn;
recv_sys->scanned_checkpoint_no = 0; recv_sys->scanned_checkpoint_no = 0;
recv_sys->recovered_lsn = checkpoint_lsn; recv_sys->recovered_lsn = checkpoint_lsn;
recv_sys->recv_log_crypt_ver = recv_crypt_ver;
log_init_crypt_key(redo_log_crypt_msg,
recv_sys->recv_log_crypt_ver,
recv_sys->recv_log_crypt_key);
srv_start_lsn = checkpoint_lsn; srv_start_lsn = checkpoint_lsn;
} }
...@@ -3423,8 +3423,9 @@ recv_recovery_from_checkpoint_start_func( ...@@ -3423,8 +3423,9 @@ recv_recovery_from_checkpoint_start_func(
log_sys->next_checkpoint_lsn = checkpoint_lsn; log_sys->next_checkpoint_lsn = checkpoint_lsn;
log_sys->next_checkpoint_no = checkpoint_no + 1; log_sys->next_checkpoint_no = checkpoint_no + 1;
log_crypt_set_ver_and_key(log_sys->redo_log_crypt_ver, /* here the checkpoint info is written without any redo logging ongoing
log_sys->redo_log_crypt_key); * and next_checkpoint_no is updated directly hence no +1 */
log_crypt_set_ver_and_key(log_sys->next_checkpoint_no);
#ifdef UNIV_LOG_ARCHIVE #ifdef UNIV_LOG_ARCHIVE
log_sys->archived_lsn = archived_lsn; log_sys->archived_lsn = archived_lsn;
...@@ -3455,8 +3456,7 @@ recv_recovery_from_checkpoint_start_func( ...@@ -3455,8 +3456,7 @@ recv_recovery_from_checkpoint_start_func(
log_sys->lsn - log_sys->last_checkpoint_lsn); log_sys->lsn - log_sys->last_checkpoint_lsn);
log_sys->next_checkpoint_no = checkpoint_no + 1; log_sys->next_checkpoint_no = checkpoint_no + 1;
log_crypt_set_ver_and_key(log_sys->redo_log_crypt_ver, log_crypt_set_ver_and_key(log_sys->next_checkpoint_no);
log_sys->redo_log_crypt_key);
#ifdef UNIV_LOG_ARCHIVE #ifdef UNIV_LOG_ARCHIVE
if (archived_lsn == LSN_MAX) { if (archived_lsn == LSN_MAX) {
...@@ -3658,16 +3658,6 @@ recv_reset_logs( ...@@ -3658,16 +3658,6 @@ recv_reset_logs(
log_sys->next_checkpoint_no = 0; log_sys->next_checkpoint_no = 0;
log_sys->last_checkpoint_lsn = 0; log_sys->last_checkpoint_lsn = 0;
/* redo_log_crypt_ver will be set by log_checkpoint() to the
latest key version. */
log_sys->redo_log_crypt_ver = UNENCRYPTED_KEY_VER;
/*
Note: flags (srv_encrypt_log and debug_use_static_keys)
haven't been read and set yet!
So don't use condition such as:
if (srv_encrypt_log && debug_use_static_keys)
*/
log_init_crypt_msg_and_nonce();
#ifdef UNIV_LOG_ARCHIVE #ifdef UNIV_LOG_ARCHIVE
log_sys->archived_lsn = log_sys->lsn; log_sys->archived_lsn = log_sys->lsn;
......
...@@ -3013,6 +3013,15 @@ innobase_start_or_create_for_mysql(void) ...@@ -3013,6 +3013,15 @@ innobase_start_or_create_for_mysql(void)
(ulong) srv_force_recovery); (ulong) srv_force_recovery);
} }
if (!srv_read_only_mode) {
/*
Create a checkpoint before logging anything new, so that
the current encryption key in use is definitely logged
before any log blocks encrypted with that key.
*/
log_make_checkpoint_at(LSN_MAX, TRUE);
}
if (srv_force_recovery == 0) { if (srv_force_recovery == 0) {
/* In the insert buffer we may have even bigger tablespace /* In the insert buffer we may have even bigger tablespace
id's, because we may have dropped those tablespaces, but id's, because we may have dropped those tablespaces, but
......
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