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
#include "univ.i"
#include "ut0byte.h"
#include "ut0lst.h"
#include "ut0rnd.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;
/* If true, enable redo log encryption. */
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. */
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. */
/***********************************************************************
Set next checkpoint's key version to latest one, and generate new key */
UNIV_INTERN
void
log_crypt_set_ver_and_key(
/*======================*/
uint& key_ver, /*!< out: latest key version */
byte* crypt_key); /*!< out: crypto key */
ib_uint64_t next_checkpoint_no);
/*********************************************************************//**
Writes the crypto (version, msg and iv) info, which has been used for
......@@ -83,4 +35,34 @@ log_crypt_write_checkpoint_buf(
/*===========================*/
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
......@@ -677,19 +677,15 @@ extern log_t* log_sys;
#endif
#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
lsn <= the checkpoint' lsn */
#define LOG_CRYPT_MSG (24 + LOG_CHECKPOINT_ARRAY_END)
/*!< a 128-bit value used to
derive cryto key for redo log.
It is generated via the concatenation
of 1 purpose byte T (0x02) and a
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)
#define LOG_CRYPT_MAX_ENTRIES (5)
#define LOG_CRYPT_ENTRY_SIZE (4 + 4 + 2 * MY_AES_BLOCK_SIZE)
#define LOG_CRYPT_SIZE (1 + 1 + \
(LOG_CRYPT_MAX_ENTRIES * \
LOG_CRYPT_ENTRY_SIZE))
#define LOG_CHECKPOINT_SIZE (20 + LOG_CHECKPOINT_ARRAY_END + \
LOG_CRYPT_SIZE)
/* Offsets of a log file header */
#define LOG_GROUP_ID 0 /* log group number */
......@@ -794,10 +790,6 @@ struct log_t{
lsn_t lsn; /*!< log sequence number */
ulint buf_free; /*!< first free offset within the log
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
ib_mutex_t mutex; /*!< mutex protecting the log */
......
......@@ -434,11 +434,6 @@ struct recv_sys_t{
scan find a corrupt log block, or a corrupt
log record, or there is a log parsing
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
log_group_t* archive_group;
/*!< in archive recovery: the log group whose
......
This diff is collapsed.
......@@ -902,7 +902,6 @@ log_init(void)
/*----------------------------*/
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->n_pending_checkpoint_writes = 0;
......@@ -1295,36 +1294,6 @@ log_block_store_checksum(
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. */
UNIV_INTERN
......@@ -1431,14 +1400,8 @@ log_group_write_buf(
ut_a(next_offset / UNIV_PAGE_SIZE <= ULINT_MAX);
if (srv_encrypt_log &&
log_sys->redo_log_crypt_ver != UNENCRYPTED_KEY_VER &&
!log_group_encrypt_before_write(group, buf, write_len))
{
fprintf(stderr,
"\nInnodb redo log encryption failed.\n");
abort();
}
log_encrypt_before_write(log_sys->next_checkpoint_no,
buf, write_len);
fil_io(OS_FILE_WRITE | OS_FILE_LOG, true, group->space_id, 0,
(ulint) (next_offset / UNIV_PAGE_SIZE),
......@@ -2201,12 +2164,15 @@ log_checkpoint(
}
#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();
/* 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);
mutex_exit(&(log_sys->mutex));
......@@ -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. */
UNIV_INTERN
......@@ -2419,12 +2358,7 @@ log_group_read_log_seg(
(ulint) (source_offset % UNIV_PAGE_SIZE),
len, buf, NULL, 0);
if (recv_sys->recv_log_crypt_ver != UNENCRYPTED_KEY_VER &&
!log_group_decrypt_after_read(group, buf, len))
{
fprintf(stderr, "Innodb redo log decryption failed.\n");
abort();
}
log_decrypt_after_read(buf, len);
start_lsn += len;
buf += len;
......@@ -2649,13 +2583,8 @@ log_group_archive(
MONITOR_INC(MONITOR_LOG_IO);
if (srv_encrypt_log &&
log_sys->redo_log_crypt_ver != UNENCRYPTED_KEY_VER &&
!log_group_encrypt_before_write(group, buf, len))
{
fprintf(stderr, "Innodb redo log encryption failed.\n");
abort();
}
//TODO (jonaso): This must be dead code??
log_encrypt_before_write(log_sys->next_checkpoint_no, buf, len);
fil_io(OS_FILE_WRITE | OS_FILE_LOG, false, group->archive_space_id,
(ulint) (next_offset / UNIV_PAGE_SIZE),
......
......@@ -805,6 +805,7 @@ recv_find_max_checkpoint(
buf + LOG_CHECKPOINT_OFFSET_HIGH32)) << 32;
checkpoint_no = mach_read_from_8(
buf + LOG_CHECKPOINT_NO);
log_crypt_read_checkpoint_buf(buf);
#ifdef UNIV_DEBUG
if (log_debug_writes) {
......@@ -935,6 +936,12 @@ log_block_checksum_is_ok_or_old_format(
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);
}
......@@ -2746,6 +2753,13 @@ recv_scan_log_recs(
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;
}
......@@ -3028,7 +3042,6 @@ recv_recovery_from_checkpoint_start_func(
ulint max_cp_field;
lsn_t checkpoint_lsn;
ib_uint64_t checkpoint_no;
uint recv_crypt_ver;
lsn_t group_scanned_lsn = 0;
lsn_t contiguous_lsn;
#ifdef UNIV_LOG_ARCHIVE
......@@ -3088,14 +3101,6 @@ 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);
}
/* Read the first log file header to print a note if this is
a recovery from a restored InnoDB Hot Backup */
......@@ -3152,15 +3157,10 @@ recv_recovery_from_checkpoint_start_func(
/* Start reading the log groups from the checkpoint lsn up. The
variable contiguous_lsn contains an lsn up to which the log is
known to be contiguously written to all log groups. */
recv_sys->parse_start_lsn = checkpoint_lsn;
recv_sys->scanned_lsn = checkpoint_lsn;
recv_sys->scanned_checkpoint_no = 0;
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;
}
......@@ -3330,8 +3330,9 @@ recv_recovery_from_checkpoint_start_func(
log_sys->next_checkpoint_lsn = checkpoint_lsn;
log_sys->next_checkpoint_no = checkpoint_no + 1;
log_crypt_set_ver_and_key(log_sys->redo_log_crypt_ver,
log_sys->redo_log_crypt_key);
/* here the checkpoint info is written without any redo logging ongoing
* 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
log_sys->archived_lsn = archived_lsn;
......@@ -3362,8 +3363,7 @@ recv_recovery_from_checkpoint_start_func(
log_sys->lsn - log_sys->last_checkpoint_lsn);
log_sys->next_checkpoint_no = checkpoint_no + 1;
log_crypt_set_ver_and_key(log_sys->redo_log_crypt_ver,
log_sys->redo_log_crypt_key);
log_crypt_set_ver_and_key(log_sys->next_checkpoint_no);
#ifdef UNIV_LOG_ARCHIVE
if (archived_lsn == LSN_MAX) {
......@@ -3566,16 +3566,6 @@ recv_reset_logs(
log_sys->next_checkpoint_no = 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
log_sys->archived_lsn = log_sys->lsn;
......
......@@ -2912,6 +2912,15 @@ innobase_start_or_create_for_mysql(void)
(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) {
/* In the insert buffer we may have even bigger tablespace
id's, because we may have dropped those tablespaces, but
......
......@@ -9,69 +9,21 @@ Created 11/25/2013 Minli Zhu
#include "univ.i"
#include "ut0byte.h"
#include "ut0lst.h"
#include "ut0rnd.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;
/* If true, enable redo log encryption. */
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. */
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. */
/***********************************************************************
Set next checkpoint's key version to latest one, and generate new key */
UNIV_INTERN
void
log_crypt_set_ver_and_key(
/*======================*/
uint& key_ver, /*!< out: latest key version */
byte* crypt_key); /*!< out: crypto key */
ib_uint64_t next_checkpoint_no);
/*********************************************************************//**
Writes the crypto (version, msg and iv) info, which has been used for
......@@ -83,4 +35,34 @@ log_crypt_write_checkpoint_buf(
/*===========================*/
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
......@@ -747,19 +747,15 @@ extern log_t* log_sys;
#endif
#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
lsn <= the checkpoint' lsn */
#define LOG_CRYPT_MSG (24 + LOG_CHECKPOINT_ARRAY_END)
/*!< a 128-bit value used to
derive cryto key for redo log.
It is generated via the concatenation
of 1 purpose byte T (0x02) and a
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)
#define LOG_CRYPT_MAX_ENTRIES (5)
#define LOG_CRYPT_ENTRY_SIZE (4 + 4 + 2 * MY_AES_BLOCK_SIZE)
#define LOG_CRYPT_SIZE (1 + 1 + \
(LOG_CRYPT_MAX_ENTRIES * \
LOG_CRYPT_ENTRY_SIZE))
#define LOG_CHECKPOINT_SIZE (20 + LOG_CHECKPOINT_ARRAY_END + \
LOG_CRYPT_SIZE)
/* Offsets of a log file header */
#define LOG_GROUP_ID 0 /* log group number */
......@@ -867,10 +863,6 @@ struct log_t{
lsn_t lsn; /*!< log sequence number */
ulint buf_free; /*!< first free offset within the log
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
ib_prio_mutex_t mutex; /*!< mutex protecting the log */
......
......@@ -471,11 +471,6 @@ struct recv_sys_t{
scan find a corrupt log block, or a corrupt
log record, or there is a log parsing
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
log_group_t* archive_group;
/*!< in archive recovery: the log group whose
......
This diff is collapsed.
......@@ -1005,7 +1005,6 @@ log_init(void)
/*----------------------------*/
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->n_pending_checkpoint_writes = 0;
......@@ -1403,36 +1402,6 @@ log_block_store_checksum(
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. */
UNIV_INTERN
......@@ -1539,14 +1508,8 @@ log_group_write_buf(
ut_a(next_offset / UNIV_PAGE_SIZE <= ULINT_MAX);
if (srv_encrypt_log &&
log_sys->redo_log_crypt_ver != UNENCRYPTED_KEY_VER &&
!log_group_encrypt_before_write(group, buf, write_len))
{
fprintf(stderr,
"\nInnodb redo log encryption failed.\n");
abort();
}
log_encrypt_before_write(log_sys->next_checkpoint_no,
buf, write_len);
fil_io(OS_FILE_WRITE | OS_FILE_LOG, true, group->space_id, 0,
(ulint) (next_offset / UNIV_PAGE_SIZE),
......@@ -2350,12 +2313,15 @@ log_checkpoint(
}
#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();
/* 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);
mutex_exit(&(log_sys->mutex));
......@@ -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
before the I/O. */
......@@ -2641,12 +2580,7 @@ log_group_read_log_seg(
(ulint) (source_offset % UNIV_PAGE_SIZE),
len, buf, (type == LOG_ARCHIVE) ? &log_archive_io : NULL, 0);
if (recv_sys->recv_log_crypt_ver != UNENCRYPTED_KEY_VER &&
!log_group_decrypt_after_read(group, buf, len))
{
fprintf(stderr, "Innodb redo log decryption failed.\n");
abort();
}
log_decrypt_after_read(buf, len);
start_lsn += len;
buf += len;
......@@ -2940,13 +2874,8 @@ log_group_archive(
MONITOR_INC(MONITOR_LOG_IO);
if (srv_encrypt_log &&
log_sys->redo_log_crypt_ver != UNENCRYPTED_KEY_VER &&
!log_group_encrypt_before_write(group, buf, len))
{
fprintf(stderr, "Innodb redo log encryption failed.\n");
abort();
}
//TODO (jonaso): This must be dead code??
log_encrypt_before_write(log_sys->next_checkpoint_no, buf, len);
fil_io(OS_FILE_WRITE | OS_FILE_LOG, false, group->archive_space_id,
0,
......
......@@ -810,6 +810,7 @@ recv_find_max_checkpoint(
buf + LOG_CHECKPOINT_OFFSET_HIGH32)) << 32;
checkpoint_no = mach_read_from_8(
buf + LOG_CHECKPOINT_NO);
log_crypt_read_checkpoint_buf(buf);
#ifdef UNIV_DEBUG
if (log_debug_writes) {
......@@ -1000,6 +1001,12 @@ log_block_checksum_is_ok_or_old_format(
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);
}
......@@ -2816,6 +2823,13 @@ recv_scan_log_recs(
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;
}
......@@ -3099,7 +3113,6 @@ recv_recovery_from_checkpoint_start_func(
ulint log_hdr_log_block_size;
lsn_t checkpoint_lsn;
ib_uint64_t checkpoint_no;
uint recv_crypt_ver;
lsn_t group_scanned_lsn = 0;
lsn_t contiguous_lsn;
#ifdef UNIV_LOG_ARCHIVE
......@@ -3164,14 +3177,6 @@ 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);
}
/* Read the first log file header to print a note if this is
a recovery from a restored InnoDB Hot Backup */
......@@ -3245,15 +3250,10 @@ recv_recovery_from_checkpoint_start_func(
/* Start reading the log groups from the checkpoint lsn up. The
variable contiguous_lsn contains an lsn up to which the log is
known to be contiguously written to all log groups. */
recv_sys->parse_start_lsn = checkpoint_lsn;
recv_sys->scanned_lsn = checkpoint_lsn;
recv_sys->scanned_checkpoint_no = 0;
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;
}
......@@ -3423,8 +3423,9 @@ recv_recovery_from_checkpoint_start_func(
log_sys->next_checkpoint_lsn = checkpoint_lsn;
log_sys->next_checkpoint_no = checkpoint_no + 1;
log_crypt_set_ver_and_key(log_sys->redo_log_crypt_ver,
log_sys->redo_log_crypt_key);
/* here the checkpoint info is written without any redo logging ongoing
* 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
log_sys->archived_lsn = archived_lsn;
......@@ -3455,8 +3456,7 @@ recv_recovery_from_checkpoint_start_func(
log_sys->lsn - log_sys->last_checkpoint_lsn);
log_sys->next_checkpoint_no = checkpoint_no + 1;
log_crypt_set_ver_and_key(log_sys->redo_log_crypt_ver,
log_sys->redo_log_crypt_key);
log_crypt_set_ver_and_key(log_sys->next_checkpoint_no);
#ifdef UNIV_LOG_ARCHIVE
if (archived_lsn == LSN_MAX) {
......@@ -3658,16 +3658,6 @@ recv_reset_logs(
log_sys->next_checkpoint_no = 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
log_sys->archived_lsn = log_sys->lsn;
......
......@@ -3013,6 +3013,15 @@ innobase_start_or_create_for_mysql(void)
(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) {
/* In the insert buffer we may have even bigger tablespace
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