mirror of
https://github.com/MariaDB/server.git
synced 2025-12-24 11:21:21 +03:00
MDEV-8041: InnoDB redo log encryption
Merged new version of InnoDB/XtraDB redo log encryption from Google provided by Jonas Oreland.
This commit is contained in:
@@ -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;
|
||||
crypt_info_t info;
|
||||
info.checkpoint_no = next_checkpoint_no;
|
||||
|
||||
if (srv_encrypt_log) {
|
||||
unsigned int vkey;
|
||||
vkey = encryption_key_get_latest_version(LOG_DEFAULT_ENCRYPTION_KEY);
|
||||
encrypted = true;
|
||||
|
||||
if (vkey == UNENCRYPTED_KEY_VER ||
|
||||
vkey == ENCRYPTION_KEY_VERSION_INVALID) {
|
||||
encrypted = false;
|
||||
|
||||
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.");
|
||||
|
||||
srv_encrypt_log = FALSE;
|
||||
} else {
|
||||
key_ver = vkey;
|
||||
}
|
||||
if (!srv_encrypt_log) {
|
||||
info.key_version = UNENCRYPTED_KEY_VER;
|
||||
} else {
|
||||
encrypted = false;
|
||||
info.key_version = encryption_key_get_latest_version(LOG_DEFAULT_ENCRYPTION_KEY);
|
||||
}
|
||||
|
||||
if (!encrypted) {
|
||||
key_ver = UNENCRYPTED_KEY_VER;
|
||||
memset(crypt_key, 0, MY_AES_BLOCK_SIZE);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
log_init_crypt_key(redo_log_crypt_msg, key_ver, crypt_key);
|
||||
if (info->key_version == UNENCRYPTED_KEY_VER) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
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 @@ loop:
|
||||
|
||||
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 @@ loop:
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************//**
|
||||
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 @@ loop:
|
||||
(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 @@ loop:
|
||||
|
||||
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 @@ files_checked:
|
||||
(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;
|
||||
crypt_info_t info;
|
||||
info.checkpoint_no = next_checkpoint_no;
|
||||
|
||||
if (srv_encrypt_log) {
|
||||
unsigned int vkey;
|
||||
vkey = encryption_key_get_latest_version(LOG_DEFAULT_ENCRYPTION_KEY);
|
||||
encrypted = true;
|
||||
|
||||
if (vkey == UNENCRYPTED_KEY_VER ||
|
||||
vkey == ENCRYPTION_KEY_VERSION_INVALID) {
|
||||
encrypted = false;
|
||||
|
||||
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.");
|
||||
|
||||
srv_encrypt_log = FALSE;
|
||||
} else {
|
||||
key_ver = vkey;
|
||||
}
|
||||
if (!srv_encrypt_log) {
|
||||
info.key_version = UNENCRYPTED_KEY_VER;
|
||||
} else {
|
||||
encrypted = false;
|
||||
info.key_version = encryption_key_get_latest_version(LOG_DEFAULT_ENCRYPTION_KEY);
|
||||
}
|
||||
|
||||
if (!encrypted) {
|
||||
key_ver = UNENCRYPTED_KEY_VER;
|
||||
memset(crypt_key, 0, MY_AES_BLOCK_SIZE);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
log_init_crypt_key(redo_log_crypt_msg, key_ver, crypt_key);
|
||||
if (info->key_version == UNENCRYPTED_KEY_VER) {
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
|
||||
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;
|
||||
}
|
||||
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 @@ loop:
|
||||
|
||||
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 @@ loop:
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************//**
|
||||
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 @@ loop:
|
||||
(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 @@ loop:
|
||||
|
||||
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 @@ files_checked:
|
||||
(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
|
||||
|
||||
Reference in New Issue
Block a user