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
......
......@@ -34,6 +34,17 @@ Modified Jan Lindström jan.lindstrom@mariadb.com
#include "ha_prototypes.h" // IB_LOG_
#include "my_crypt.h"
#define UNENCRYPTED_KEY_VER 0
/* If true, enable redo log encryption. */
extern my_bool srv_encrypt_log;
#include <algorithm> // std::sort
#include <deque>
/* If true, enable redo log encryption. */
UNIV_INTERN my_bool srv_encrypt_log = FALSE;
/*
......@@ -41,106 +52,23 @@ UNIV_INTERN my_bool srv_encrypt_log = FALSE;
Set and used to validate crypto msg.
*/
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
* encryption/decryption. */
byte aes_ctr_nonce[MY_AES_BLOCK_SIZE] = {0};
#define LOG_DEFAULT_ENCRYPTION_KEY 1
/*********************************************************************//**
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.
It is called when:
- redo logs do not exist when start up, or
- transition from without crypto.
Note:
We should not use flags and conditions such as:
(srv_encrypt_log &&
debug_use_static_keys &&
get_latest_encryption_key_version() == UNENCRYPTED_KEY_VER)
because they haven't been read and set yet in the situation of resetting
redo logs.
/*
Store this many keys into each checkpoint info
*/
UNIV_INTERN
void
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) != MY_AES_OK)
{
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: generate "
"%u-byte random number as crypto msg failed.",
PURPOSE_BYTE_LEN);
abort();
}
static const size_t kMaxSavedKeys = LOG_CRYPT_MAX_ENTRIES;
if (my_random_bytes(aes_ctr_nonce, MY_AES_BLOCK_SIZE) != MY_AES_OK)
{
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: generate "
"%u-byte random number as AES_CTR nonce failed.",
MY_AES_BLOCK_SIZE);
abort();
}
}
struct crypt_info_t {
ulong checkpoint_no; /*!< checkpoint no */
uint key_version; /*!< mysqld key version */
byte crypt_msg[MY_AES_BLOCK_SIZE];
byte crypt_key[MY_AES_BLOCK_SIZE];
byte crypt_nonce[MY_AES_BLOCK_SIZE];
};
/*********************************************************************//**
Generate crypt key from crypt msg. */
UNIV_INTERN
void
log_init_crypt_key(
/*===============*/
const byte* crypt_msg, /*< in: crypt msg */
const uint crypt_ver, /*< in: key version */
byte* key) /*< out: crypt key*/
{
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)
{
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: msg type mismatched. "
"Expected: %x; Actual: %x.",
redo_log_purpose_byte, crypt_msg[PURPOSE_BYTE_OFFSET]);
abort();
}
byte mysqld_key[MY_AES_BLOCK_SIZE] = {0};
uint keylen= sizeof(mysqld_key);
if (encryption_key_get(LOG_DEFAULT_ENCRYPTION_KEY, crypt_ver, mysqld_key, &keylen))
{
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: getting mysqld crypto key "
"from key version failed.");
abort();
}
uint32 dst_len;
int rc= my_aes_encrypt_ecb(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 != MY_AES_OK || dst_len != MY_AES_BLOCK_SIZE)
{
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: getting redo log crypto key "
"failed.");
abort();
}
}
static std::deque<crypt_info_t> crypt_info;
/*********************************************************************//**
Get a log block's start lsn.
......@@ -158,30 +86,74 @@ log_block_get_start_lsn(
return start_lsn;
}
static
const crypt_info_t*
get_crypt_info(
/*===========*/
ib_uint64_t checkpoint_no)
{
/* so that no one is modifying array while we search */
ut_ad(mutex_own(&(log_sys->mutex)));
/* a log block only stores 4-bytes of checkpoint no */
checkpoint_no &= 0xFFFFFFFF;
for (size_t i = 0; i < crypt_info.size(); i++) {
struct crypt_info_t* it = &crypt_info[i];
if (it->checkpoint_no == checkpoint_no) {
return it;
}
}
return NULL;
}
static
const crypt_info_t*
get_crypt_info(
/*===========*/
const byte* log_block) {
ib_uint64_t checkpoint_no = log_block_get_checkpoint_no(log_block);
return get_crypt_info(checkpoint_no);
}
/*********************************************************************//**
Call AES CTR to encrypt/decrypt log blocks. */
static
Crypt_result
log_blocks_crypt(
/*=============*/
const byte* block, /*!< in: blocks before encrypt/decrypt*/
const ulint size, /*!< in: size of block, must be multiple of a log block*/
byte* dst_block, /*!< out: blocks after encrypt/decrypt */
const bool is_encrypt) /*!< in: encrypt or decrypt*/
const byte* block, /*!< in: blocks before encrypt/decrypt*/
ulint size, /*!< in: size of block */
byte* dst_block, /*!< out: blocks after encrypt/decrypt */
bool is_encrypt) /*!< in: encrypt or decrypt*/
{
byte *log_block = (byte*)block;
Crypt_result rc = MY_AES_OK;
uint32 src_len, dst_len;
uint32 dst_len;
byte aes_ctr_counter[MY_AES_BLOCK_SIZE];
ulint log_block_no, log_block_start_lsn;
ulint lsn = is_encrypt ? log_sys->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)
{
log_block_no = log_block_get_hdr_no(log_block);
log_block_start_lsn = log_block_get_start_lsn(lsn, log_block_no);
const int src_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE;
for (ulint i = 0; i < size ; i += OS_FILE_LOG_BLOCK_SIZE) {
ulint log_block_no = log_block_get_hdr_no(log_block);
ulint log_block_start_lsn = log_block_get_start_lsn(
lsn, log_block_no);
const crypt_info_t* info = get_crypt_info(log_block);
#ifdef DEBUG_CRYPT
fprintf(stderr,
"%s %lu chkpt: %lu key: %u lsn: %lu\n",
is_encrypt ? "crypt" : "decrypt",
log_block_no,
log_block_get_checkpoint_no(log_block),
info ? info->key_version : 0,
log_block_start_lsn);
#endif
if (info == NULL ||
info->key_version == UNENCRYPTED_KEY_VER) {
memcpy(dst_block, log_block, OS_FILE_LOG_BLOCK_SIZE);
goto next;
}
// Assume log block header is not encrypted
memcpy(dst_block, log_block, LOG_BLOCK_HDR_SIZE);
......@@ -190,34 +162,31 @@ log_blocks_crypt(
// (8-byte) + lbn (4-byte) + abn
// (1-byte, only 5 bits are used). "+" means concatenate.
bzero(aes_ctr_counter, MY_AES_BLOCK_SIZE);
memcpy(aes_ctr_counter, &aes_ctr_nonce, 3);
memcpy(aes_ctr_counter, info->crypt_nonce, 3);
mach_write_to_8(aes_ctr_counter + 3, log_block_start_lsn);
mach_write_to_4(aes_ctr_counter + 11, log_block_no);
bzero(aes_ctr_counter + 15, 1);
int rc;
if (is_encrypt) {
ut_a(log_sys);
ut_a(log_sys->redo_log_crypt_ver != UNENCRYPTED_KEY_VER);
rc = encryption_encrypt(log_block + LOG_BLOCK_HDR_SIZE, src_len,
dst_block + LOG_BLOCK_HDR_SIZE, &dst_len,
(unsigned char*)(log_sys->redo_log_crypt_key), 16,
(unsigned char*)(info->crypt_key), 16,
aes_ctr_counter, MY_AES_BLOCK_SIZE, 1,
LOG_DEFAULT_ENCRYPTION_KEY,
log_sys->redo_log_crypt_ver);
info->key_version);
} else {
ut_a(recv_sys);
ut_a(recv_sys->recv_log_crypt_ver != UNENCRYPTED_KEY_VER);
rc = encryption_decrypt(log_block + LOG_BLOCK_HDR_SIZE, src_len,
dst_block + LOG_BLOCK_HDR_SIZE, &dst_len,
(unsigned char*)(recv_sys->recv_log_crypt_key), 16,
(unsigned char*)(info->crypt_key), 16,
aes_ctr_counter, MY_AES_BLOCK_SIZE, 1,
LOG_DEFAULT_ENCRYPTION_KEY,
recv_sys->recv_log_crypt_ver);
info->key_version);
}
ut_a(rc == MY_AES_OK);
ut_a(dst_len == src_len);
next:
log_block += OS_FILE_LOG_BLOCK_SIZE;
dst_block += OS_FILE_LOG_BLOCK_SIZE;
}
......@@ -225,6 +194,75 @@ log_blocks_crypt(
return rc;
}
/*********************************************************************//**
Generate crypt key from crypt msg. */
static
void
init_crypt_key(
/*===========*/
crypt_info_t* info) /*< in/out: crypt info */
{
if (info->key_version == UNENCRYPTED_KEY_VER) {
memset(info->crypt_key, 0, sizeof(info->crypt_key));
memset(info->crypt_msg, 0, sizeof(info->crypt_msg));
memset(info->crypt_nonce, 0, sizeof(info->crypt_nonce));
return;
}
byte mysqld_key[MY_AES_BLOCK_SIZE] = {0};
uint keylen= sizeof(mysqld_key);
if (encryption_key_get(LOG_DEFAULT_ENCRYPTION_KEY, info->key_version, mysqld_key, &keylen))
{
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: getting mysqld crypto key "
"from key version failed.");
ut_error;
}
uint dst_len;
int rc= my_aes_encrypt_ecb(info->crypt_msg, sizeof(info->crypt_msg), //src, srclen
info->crypt_key, &dst_len, //dst, &dstlen
(unsigned char*)&mysqld_key, sizeof(mysqld_key),
NULL, 0, 1);
if (rc != MY_AES_OK || dst_len != MY_AES_BLOCK_SIZE) {
fprintf(stderr,
"\nInnodb redo log crypto: getting redo log crypto key "
"failed.\n");
ut_error;
}
}
static bool mysort(const crypt_info_t& i,
const crypt_info_t& j)
{
return i.checkpoint_no > j.checkpoint_no;
}
static
bool add_crypt_info(crypt_info_t* info)
{
/* so that no one is searching array while we modify it */
ut_ad(mutex_own(&(log_sys->mutex)));
if (get_crypt_info(info->checkpoint_no) != NULL) {
// already present...
return false;
}
init_crypt_key(info);
crypt_info.push_back(*info);
/* a log block only stores 4-bytes of checkpoint no */
crypt_info.back().checkpoint_no &= 0xFFFFFFFF;
// keep keys sorted, assuming that last added key will be used most
std::sort(crypt_info.begin(), crypt_info.end(), mysort);
return true;
}
/*********************************************************************//**
Encrypt log blocks. */
UNIV_INTERN
......@@ -238,19 +276,6 @@ log_blocks_encrypt(
return log_blocks_crypt(block, size, dst_block, true);
}
/*********************************************************************//**
Decrypt log blocks. */
UNIV_INTERN
Crypt_result
log_blocks_decrypt(
/*===============*/
const byte* block, /*!< in: blocks before decryption */
const ulint size, /*!< in: size of blocks, must be multiple of a log block */
byte* dst_block) /*!< out: blocks after decryption */
{
return log_blocks_crypt(block, size, dst_block, false);
}
/*********************************************************************//**
Set next checkpoint's key version to latest one, and generate current
key. Key version 0 means no encryption. */
......@@ -258,40 +283,102 @@ 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)
{
bool encrypted;
if (srv_encrypt_log) {
unsigned int vkey;
vkey = encryption_key_get_latest_version(LOG_DEFAULT_ENCRYPTION_KEY);
encrypted = true;
crypt_info_t info;
info.checkpoint_no = next_checkpoint_no;
if (vkey == UNENCRYPTED_KEY_VER ||
vkey == ENCRYPTION_KEY_VERSION_INVALID) {
encrypted = false;
if (!srv_encrypt_log) {
info.key_version = UNENCRYPTED_KEY_VER;
} else {
info.key_version = encryption_key_get_latest_version(LOG_DEFAULT_ENCRYPTION_KEY);
}
ib_logf(IB_LOG_LEVEL_WARN,
"Redo log crypto: Can't initialize to key version %du.", vkey);
ib_logf(IB_LOG_LEVEL_WARN,
"Disabling redo log encryption.");
if (info.key_version == UNENCRYPTED_KEY_VER) {
memset(info.crypt_msg, 0, sizeof(info.crypt_msg));
memset(info.crypt_nonce, 0, sizeof(info.crypt_nonce));
} else {
if (my_random_bytes(info.crypt_msg, MY_AES_BLOCK_SIZE) != MY_AES_OK) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: generate "
"%u-byte random number as crypto msg failed.",
MY_AES_BLOCK_SIZE);
ut_error;
}
srv_encrypt_log = FALSE;
} else {
key_ver = vkey;
if (my_random_bytes(info.crypt_nonce, MY_AES_BLOCK_SIZE) != MY_AES_OK) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: generate "
"%u-byte random number as AES_CTR nonce failed.",
MY_AES_BLOCK_SIZE);
ut_error;
}
} else {
encrypted = false;
}
add_crypt_info(&info);
}
/********************************************************
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 */
{
ut_ad(size % OS_FILE_LOG_BLOCK_SIZE == 0);
const crypt_info_t* info = get_crypt_info(next_checkpoint_no);
if (info == NULL) {
return;
}
if (!encrypted) {
key_ver = UNENCRYPTED_KEY_VER;
memset(crypt_key, 0, MY_AES_BLOCK_SIZE);
if (info->key_version == UNENCRYPTED_KEY_VER) {
return;
}
log_init_crypt_key(redo_log_crypt_msg, key_ver, crypt_key);
byte* dst_frame = (byte*)malloc(size);
//encrypt log blocks content
Crypt_result result = log_blocks_crypt(block, size, dst_frame, true);
if (result == MY_AES_OK) {
ut_ad(block[0] == dst_frame[0]);
memcpy(block, dst_frame, size);
}
free(dst_frame);
if (unlikely(result != MY_AES_OK)) {
ut_error;
}
}
/********************************************************
Decrypt a specified log segment after they are read from a log file to a buffer.
*/
void
log_decrypt_after_read(
/*==========================*/
byte* frame, /*!< in/out: log segment */
const ulint size) /*!< in: log segment size */
{
ut_ad(size % OS_FILE_LOG_BLOCK_SIZE == 0);
byte* dst_frame = (byte*)malloc(size);
// decrypt log blocks content
Crypt_result result = log_blocks_crypt(frame, size, dst_frame, false);
if (result == MY_AES_OK) {
memcpy(frame, dst_frame, size);
}
free(dst_frame);
if (unlikely(result != MY_AES_OK)) {
ut_error;
}
}
/*********************************************************************//**
......@@ -304,15 +391,99 @@ log_crypt_write_checkpoint_buf(
/*===========================*/
byte* buf) /*!< in/out: checkpoint buffer */
{
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);
byte *save = buf;
// Only write kMaxSavedKeys (sort keys to remove oldest)
std::sort(crypt_info.begin(), crypt_info.end(), mysort);
while (crypt_info.size() > kMaxSavedKeys) {
crypt_info.pop_back();
}
bool encrypted = false;
for (size_t i = 0; i < crypt_info.size(); i++) {
const crypt_info_t & it = crypt_info[i];
if (it.key_version != UNENCRYPTED_KEY_VER) {
encrypted = true;
break;
}
}
if (encrypted == false) {
// if no encryption is inuse then zero out
// crypt data for upward/downward compability
memset(buf + LOG_CRYPT_VER, 0, LOG_CRYPT_SIZE);
return;
}
ib_uint64_t checkpoint_no = mach_read_from_8(buf + LOG_CHECKPOINT_NO);
buf += LOG_CRYPT_VER;
mach_write_to_1(buf + 0, redo_log_purpose_byte);
mach_write_to_1(buf + 1, crypt_info.size());
buf += 2;
for (size_t i = 0; i < crypt_info.size(); i++) {
struct crypt_info_t* it = &crypt_info[i];
mach_write_to_4(buf + 0, it->checkpoint_no);
mach_write_to_4(buf + 4, it->key_version);
memcpy(buf + 8, it->crypt_msg, MY_AES_BLOCK_SIZE);
memcpy(buf + 24, it->crypt_nonce, MY_AES_BLOCK_SIZE);
buf += LOG_CRYPT_ENTRY_SIZE;
}
#ifdef DEBUG_CRYPT
fprintf(stderr, "write chk: %lu [ chk key ]: ", checkpoint_no);
for (size_t i = 0; i < crypt_info.size(); i++) {
struct crypt_info_t* it = &crypt_info[i];
fprintf(stderr, "[ %lu %u ] ",
it->checkpoint_no,
it->key_version);
}
fprintf(stderr, "\n");
#else
(void)checkpoint_no; // unused variable
#endif
ut_a((buf - save) <= OS_FILE_LOG_BLOCK_SIZE);
}
/*********************************************************************//**
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 */
buf += LOG_CRYPT_VER;
byte scheme = buf[0];
if (scheme != redo_log_purpose_byte) {
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);
buf++;
size_t n = buf[0];
buf++;
for (size_t i = 0; i < n; i++) {
struct crypt_info_t info;
info.checkpoint_no = mach_read_from_4(buf + 0);
info.key_version = mach_read_from_4(buf + 4);
memcpy(info.crypt_msg, buf + 8, MY_AES_BLOCK_SIZE);
memcpy(info.crypt_nonce, buf + 24, MY_AES_BLOCK_SIZE);
add_crypt_info(&info);
buf += LOG_CRYPT_ENTRY_SIZE;
}
#ifdef DEBUG_CRYPT
fprintf(stderr, "read [ chk key ]: ");
for (size_t i = 0; i < crypt_info.size(); i++) {
struct crypt_info_t* it = &crypt_info[i];
fprintf(stderr, "[ %lu %u ] ",
it->checkpoint_no,
it->key_version);
}
fprintf(stderr, "\n");
#endif
}
......@@ -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
......
......@@ -34,6 +34,17 @@ Modified Jan Lindström jan.lindstrom@mariadb.com
#include "ha_prototypes.h" // IB_LOG_
#include "my_crypt.h"
#define UNENCRYPTED_KEY_VER 0
/* If true, enable redo log encryption. */
extern my_bool srv_encrypt_log;
#include <algorithm> // std::sort
#include <deque>
/* If true, enable redo log encryption. */
UNIV_INTERN my_bool srv_encrypt_log = FALSE;
/*
......@@ -41,106 +52,23 @@ UNIV_INTERN my_bool srv_encrypt_log = FALSE;
Set and used to validate crypto msg.
*/
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
* encryption/decryption. */
byte aes_ctr_nonce[MY_AES_BLOCK_SIZE] = {0};
#define LOG_DEFAULT_ENCRYPTION_KEY 1
/*********************************************************************//**
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.
It is called when:
- redo logs do not exist when start up, or
- transition from without crypto.
Note:
We should not use flags and conditions such as:
(srv_encrypt_log &&
debug_use_static_keys &&
get_latest_encryption_key_version() == UNENCRYPTED_KEY_VER)
because they haven't been read and set yet in the situation of resetting
redo logs.
/*
Store this many keys into each checkpoint info
*/
UNIV_INTERN
void
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) != MY_AES_OK)
{
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: generate "
"%u-byte random number as crypto msg failed.",
PURPOSE_BYTE_LEN);
abort();
}
static const size_t kMaxSavedKeys = LOG_CRYPT_MAX_ENTRIES;
if (my_random_bytes(aes_ctr_nonce, MY_AES_BLOCK_SIZE) != MY_AES_OK)
{
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: generate "
"%u-byte random number as AES_CTR nonce failed.",
MY_AES_BLOCK_SIZE);
abort();
}
}
struct crypt_info_t {
ulong checkpoint_no; /*!< checkpoint no */
uint key_version; /*!< mysqld key version */
byte crypt_msg[MY_AES_BLOCK_SIZE];
byte crypt_key[MY_AES_BLOCK_SIZE];
byte crypt_nonce[MY_AES_BLOCK_SIZE];
};
/*********************************************************************//**
Generate crypt key from crypt msg. */
UNIV_INTERN
void
log_init_crypt_key(
/*===============*/
const byte* crypt_msg, /*< in: crypt msg */
const uint crypt_ver, /*< in: key version */
byte* key) /*< out: crypt key*/
{
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)
{
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: msg type mismatched. "
"Expected: %x; Actual: %x.",
redo_log_purpose_byte, crypt_msg[PURPOSE_BYTE_OFFSET]);
abort();
}
byte mysqld_key[MY_AES_BLOCK_SIZE] = {0};
uint keylen= sizeof(mysqld_key);
if (encryption_key_get(LOG_DEFAULT_ENCRYPTION_KEY, crypt_ver, mysqld_key, &keylen))
{
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: getting mysqld crypto key "
"from key version failed.");
abort();
}
uint32 dst_len;
int rc= my_aes_encrypt_ecb(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 != MY_AES_OK || dst_len != MY_AES_BLOCK_SIZE)
{
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: getting redo log crypto key "
"failed.");
abort();
}
}
static std::deque<crypt_info_t> crypt_info;
/*********************************************************************//**
Get a log block's start lsn.
......@@ -158,30 +86,74 @@ log_block_get_start_lsn(
return start_lsn;
}
static
const crypt_info_t*
get_crypt_info(
/*===========*/
ib_uint64_t checkpoint_no)
{
/* so that no one is modifying array while we search */
ut_ad(mutex_own(&(log_sys->mutex)));
/* a log block only stores 4-bytes of checkpoint no */
checkpoint_no &= 0xFFFFFFFF;
for (size_t i = 0; i < crypt_info.size(); i++) {
struct crypt_info_t* it = &crypt_info[i];
if (it->checkpoint_no == checkpoint_no) {
return it;
}
}
return NULL;
}
static
const crypt_info_t*
get_crypt_info(
/*===========*/
const byte* log_block) {
ib_uint64_t checkpoint_no = log_block_get_checkpoint_no(log_block);
return get_crypt_info(checkpoint_no);
}
/*********************************************************************//**
Call AES CTR to encrypt/decrypt log blocks. */
static
Crypt_result
log_blocks_crypt(
/*=============*/
const byte* block, /*!< in: blocks before encrypt/decrypt*/
const ulint size, /*!< in: size of block, must be multiple of a log block*/
byte* dst_block, /*!< out: blocks after encrypt/decrypt */
const bool is_encrypt) /*!< in: encrypt or decrypt*/
const byte* block, /*!< in: blocks before encrypt/decrypt*/
ulint size, /*!< in: size of block */
byte* dst_block, /*!< out: blocks after encrypt/decrypt */
bool is_encrypt) /*!< in: encrypt or decrypt*/
{
byte *log_block = (byte*)block;
Crypt_result rc = MY_AES_OK;
uint32 src_len, dst_len;
uint32 dst_len;
byte aes_ctr_counter[MY_AES_BLOCK_SIZE];
ulint log_block_no, log_block_start_lsn;
ulint lsn = is_encrypt ? log_sys->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)
{
log_block_no = log_block_get_hdr_no(log_block);
log_block_start_lsn = log_block_get_start_lsn(lsn, log_block_no);
const int src_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE;
for (ulint i = 0; i < size ; i += OS_FILE_LOG_BLOCK_SIZE) {
ulint log_block_no = log_block_get_hdr_no(log_block);
ulint log_block_start_lsn = log_block_get_start_lsn(
lsn, log_block_no);
const crypt_info_t* info = get_crypt_info(log_block);
#ifdef DEBUG_CRYPT
fprintf(stderr,
"%s %lu chkpt: %lu key: %u lsn: %lu\n",
is_encrypt ? "crypt" : "decrypt",
log_block_no,
log_block_get_checkpoint_no(log_block),
info ? info->key_version : 0,
log_block_start_lsn);
#endif
if (info == NULL ||
info->key_version == UNENCRYPTED_KEY_VER) {
memcpy(dst_block, log_block, OS_FILE_LOG_BLOCK_SIZE);
goto next;
}
// Assume log block header is not encrypted
memcpy(dst_block, log_block, LOG_BLOCK_HDR_SIZE);
......@@ -190,34 +162,31 @@ log_blocks_crypt(
// (8-byte) + lbn (4-byte) + abn
// (1-byte, only 5 bits are used). "+" means concatenate.
bzero(aes_ctr_counter, MY_AES_BLOCK_SIZE);
memcpy(aes_ctr_counter, &aes_ctr_nonce, 3);
memcpy(aes_ctr_counter, info->crypt_nonce, 3);
mach_write_to_8(aes_ctr_counter + 3, log_block_start_lsn);
mach_write_to_4(aes_ctr_counter + 11, log_block_no);
bzero(aes_ctr_counter + 15, 1);
int rc;
if (is_encrypt) {
ut_a(log_sys);
ut_a(log_sys->redo_log_crypt_ver != UNENCRYPTED_KEY_VER);
rc = encryption_encrypt(log_block + LOG_BLOCK_HDR_SIZE, src_len,
dst_block + LOG_BLOCK_HDR_SIZE, &dst_len,
(unsigned char*)(log_sys->redo_log_crypt_key), 16,
(unsigned char*)(info->crypt_key), 16,
aes_ctr_counter, MY_AES_BLOCK_SIZE, 1,
LOG_DEFAULT_ENCRYPTION_KEY,
log_sys->redo_log_crypt_ver);
info->key_version);
} else {
ut_a(recv_sys);
ut_a(recv_sys->recv_log_crypt_ver != UNENCRYPTED_KEY_VER);
rc = encryption_decrypt(log_block + LOG_BLOCK_HDR_SIZE, src_len,
dst_block + LOG_BLOCK_HDR_SIZE, &dst_len,
(unsigned char*)(recv_sys->recv_log_crypt_key), 16,
(unsigned char*)(info->crypt_key), 16,
aes_ctr_counter, MY_AES_BLOCK_SIZE, 1,
LOG_DEFAULT_ENCRYPTION_KEY,
recv_sys->recv_log_crypt_ver);
info->key_version);
}
ut_a(rc == MY_AES_OK);
ut_a(dst_len == src_len);
next:
log_block += OS_FILE_LOG_BLOCK_SIZE;
dst_block += OS_FILE_LOG_BLOCK_SIZE;
}
......@@ -225,6 +194,75 @@ log_blocks_crypt(
return rc;
}
/*********************************************************************//**
Generate crypt key from crypt msg. */
static
void
init_crypt_key(
/*===========*/
crypt_info_t* info) /*< in/out: crypt info */
{
if (info->key_version == UNENCRYPTED_KEY_VER) {
memset(info->crypt_key, 0, sizeof(info->crypt_key));
memset(info->crypt_msg, 0, sizeof(info->crypt_msg));
memset(info->crypt_nonce, 0, sizeof(info->crypt_nonce));
return;
}
byte mysqld_key[MY_AES_BLOCK_SIZE] = {0};
uint keylen= sizeof(mysqld_key);
if (encryption_key_get(LOG_DEFAULT_ENCRYPTION_KEY, info->key_version, mysqld_key, &keylen))
{
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: getting mysqld crypto key "
"from key version failed.");
ut_error;
}
uint dst_len;
int rc= my_aes_encrypt_ecb(info->crypt_msg, sizeof(info->crypt_msg), //src, srclen
info->crypt_key, &dst_len, //dst, &dstlen
(unsigned char*)&mysqld_key, sizeof(mysqld_key),
NULL, 0, 1);
if (rc != MY_AES_OK || dst_len != MY_AES_BLOCK_SIZE) {
fprintf(stderr,
"\nInnodb redo log crypto: getting redo log crypto key "
"failed.\n");
ut_error;
}
}
static bool mysort(const crypt_info_t& i,
const crypt_info_t& j)
{
return i.checkpoint_no > j.checkpoint_no;
}
static
bool add_crypt_info(crypt_info_t* info)
{
/* so that no one is searching array while we modify it */
ut_ad(mutex_own(&(log_sys->mutex)));
if (get_crypt_info(info->checkpoint_no) != NULL) {
// already present...
return false;
}
init_crypt_key(info);
crypt_info.push_back(*info);
/* a log block only stores 4-bytes of checkpoint no */
crypt_info.back().checkpoint_no &= 0xFFFFFFFF;
// keep keys sorted, assuming that last added key will be used most
std::sort(crypt_info.begin(), crypt_info.end(), mysort);
return true;
}
/*********************************************************************//**
Encrypt log blocks. */
UNIV_INTERN
......@@ -238,19 +276,6 @@ log_blocks_encrypt(
return log_blocks_crypt(block, size, dst_block, true);
}
/*********************************************************************//**
Decrypt log blocks. */
UNIV_INTERN
Crypt_result
log_blocks_decrypt(
/*===============*/
const byte* block, /*!< in: blocks before decryption */
const ulint size, /*!< in: size of blocks, must be multiple of a log block */
byte* dst_block) /*!< out: blocks after decryption */
{
return log_blocks_crypt(block, size, dst_block, false);
}
/*********************************************************************//**
Set next checkpoint's key version to latest one, and generate current
key. Key version 0 means no encryption. */
......@@ -258,40 +283,102 @@ 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)
{
bool encrypted;
if (srv_encrypt_log) {
unsigned int vkey;
vkey = encryption_key_get_latest_version(LOG_DEFAULT_ENCRYPTION_KEY);
encrypted = true;
crypt_info_t info;
info.checkpoint_no = next_checkpoint_no;
if (vkey == UNENCRYPTED_KEY_VER ||
vkey == ENCRYPTION_KEY_VERSION_INVALID) {
encrypted = false;
if (!srv_encrypt_log) {
info.key_version = UNENCRYPTED_KEY_VER;
} else {
info.key_version = encryption_key_get_latest_version(LOG_DEFAULT_ENCRYPTION_KEY);
}
ib_logf(IB_LOG_LEVEL_WARN,
"Redo log crypto: Can't initialize to key version %du.", vkey);
ib_logf(IB_LOG_LEVEL_WARN,
"Disabling redo log encryption.");
if (info.key_version == UNENCRYPTED_KEY_VER) {
memset(info.crypt_msg, 0, sizeof(info.crypt_msg));
memset(info.crypt_nonce, 0, sizeof(info.crypt_nonce));
} else {
if (my_random_bytes(info.crypt_msg, MY_AES_BLOCK_SIZE) != MY_AES_OK) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: generate "
"%u-byte random number as crypto msg failed.",
MY_AES_BLOCK_SIZE);
ut_error;
}
srv_encrypt_log = FALSE;
} else {
key_ver = vkey;
if (my_random_bytes(info.crypt_nonce, MY_AES_BLOCK_SIZE) != MY_AES_OK) {
ib_logf(IB_LOG_LEVEL_ERROR,
"Redo log crypto: generate "
"%u-byte random number as AES_CTR nonce failed.",
MY_AES_BLOCK_SIZE);
ut_error;
}
} else {
encrypted = false;
}
add_crypt_info(&info);
}
/********************************************************
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 */
{
ut_ad(size % OS_FILE_LOG_BLOCK_SIZE == 0);
const crypt_info_t* info = get_crypt_info(next_checkpoint_no);
if (info == NULL) {
return;
}
if (!encrypted) {
key_ver = UNENCRYPTED_KEY_VER;
memset(crypt_key, 0, MY_AES_BLOCK_SIZE);
if (info->key_version == UNENCRYPTED_KEY_VER) {
return;
}
log_init_crypt_key(redo_log_crypt_msg, key_ver, crypt_key);
byte* dst_frame = (byte*)malloc(size);
//encrypt log blocks content
Crypt_result result = log_blocks_crypt(block, size, dst_frame, true);
if (result == MY_AES_OK) {
ut_ad(block[0] == dst_frame[0]);
memcpy(block, dst_frame, size);
}
free(dst_frame);
if (unlikely(result != MY_AES_OK)) {
ut_error;
}
}
/********************************************************
Decrypt a specified log segment after they are read from a log file to a buffer.
*/
void
log_decrypt_after_read(
/*==========================*/
byte* frame, /*!< in/out: log segment */
const ulint size) /*!< in: log segment size */
{
ut_ad(size % OS_FILE_LOG_BLOCK_SIZE == 0);
byte* dst_frame = (byte*)malloc(size);
// decrypt log blocks content
Crypt_result result = log_blocks_crypt(frame, size, dst_frame, false);
if (result == MY_AES_OK) {
memcpy(frame, dst_frame, size);
}
free(dst_frame);
if (unlikely(result != MY_AES_OK)) {
ut_error;
}
}
/*********************************************************************//**
......@@ -304,15 +391,99 @@ log_crypt_write_checkpoint_buf(
/*===========================*/
byte* buf) /*!< in/out: checkpoint buffer */
{
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);
byte *save = buf;
// Only write kMaxSavedKeys (sort keys to remove oldest)
std::sort(crypt_info.begin(), crypt_info.end(), mysort);
while (crypt_info.size() > kMaxSavedKeys) {
crypt_info.pop_back();
}
bool encrypted = false;
for (size_t i = 0; i < crypt_info.size(); i++) {
const crypt_info_t & it = crypt_info[i];
if (it.key_version != UNENCRYPTED_KEY_VER) {
encrypted = true;
break;
}
}
if (encrypted == false) {
// if no encryption is inuse then zero out
// crypt data for upward/downward compability
memset(buf + LOG_CRYPT_VER, 0, LOG_CRYPT_SIZE);
return;
}
ib_uint64_t checkpoint_no = mach_read_from_8(buf + LOG_CHECKPOINT_NO);
buf += LOG_CRYPT_VER;
mach_write_to_1(buf + 0, redo_log_purpose_byte);
mach_write_to_1(buf + 1, crypt_info.size());
buf += 2;
for (size_t i = 0; i < crypt_info.size(); i++) {
struct crypt_info_t* it = &crypt_info[i];
mach_write_to_4(buf + 0, it->checkpoint_no);
mach_write_to_4(buf + 4, it->key_version);
memcpy(buf + 8, it->crypt_msg, MY_AES_BLOCK_SIZE);
memcpy(buf + 24, it->crypt_nonce, MY_AES_BLOCK_SIZE);
buf += LOG_CRYPT_ENTRY_SIZE;
}
#ifdef DEBUG_CRYPT
fprintf(stderr, "write chk: %lu [ chk key ]: ", checkpoint_no);
for (size_t i = 0; i < crypt_info.size(); i++) {
struct crypt_info_t* it = &crypt_info[i];
fprintf(stderr, "[ %lu %u ] ",
it->checkpoint_no,
it->key_version);
}
fprintf(stderr, "\n");
#else
(void)checkpoint_no; // unused variable
#endif
ut_a((buf - save) <= OS_FILE_LOG_BLOCK_SIZE);
}
/*********************************************************************//**
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 */
buf += LOG_CRYPT_VER;
byte scheme = buf[0];
if (scheme != redo_log_purpose_byte) {
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);
buf++;
size_t n = buf[0];
buf++;
for (size_t i = 0; i < n; i++) {
struct crypt_info_t info;
info.checkpoint_no = mach_read_from_4(buf + 0);
info.key_version = mach_read_from_4(buf + 4);
memcpy(info.crypt_msg, buf + 8, MY_AES_BLOCK_SIZE);
memcpy(info.crypt_nonce, buf + 24, MY_AES_BLOCK_SIZE);
add_crypt_info(&info);
buf += LOG_CRYPT_ENTRY_SIZE;
}
#ifdef DEBUG_CRYPT
fprintf(stderr, "read [ chk key ]: ");
for (size_t i = 0; i < crypt_info.size(); i++) {
struct crypt_info_t* it = &crypt_info[i];
fprintf(stderr, "[ %lu %u ] ",
it->checkpoint_no,
it->key_version);
}
fprintf(stderr, "\n");
#endif
}
......@@ -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