2015-03-02 09:55:48 +01:00
|
|
|
/*****************************************************************************
|
|
|
|
|
|
|
|
Copyright (C) 2013, 2015, Google Inc. All Rights Reserved.
|
|
|
|
Copyright (C) 2014, 2015, MariaDB Corporation. All Rights Reserved.
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it under
|
|
|
|
the terms of the GNU General Public License as published by the Free Software
|
|
|
|
Foundation; version 2 of the License.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful, but WITHOUT
|
|
|
|
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
|
|
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
|
|
|
this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
|
|
|
|
*****************************************************************************/
|
2014-12-22 15:53:17 +01:00
|
|
|
/**************************************************//**
|
|
|
|
@file log0crypt.cc
|
|
|
|
Innodb log encrypt/decrypt
|
|
|
|
|
2015-03-02 09:55:48 +01:00
|
|
|
Created 11/25/2013 Minli Zhu Google
|
|
|
|
Modified Jan Lindström jan.lindstrom@mariadb.com
|
2014-12-22 15:53:17 +01:00
|
|
|
*******************************************************/
|
2014-12-29 20:55:20 +01:00
|
|
|
#include "m_string.h"
|
2014-12-22 15:53:17 +01:00
|
|
|
#include "log0crypt.h"
|
2014-12-29 20:55:20 +01:00
|
|
|
#include <my_crypt.h>
|
2015-02-25 12:26:57 +01:00
|
|
|
#include <my_aes.h>
|
2014-12-29 20:55:20 +01:00
|
|
|
|
2014-12-22 15:53:17 +01:00
|
|
|
#include "log0log.h"
|
|
|
|
#include "srv0start.h" // for srv_start_lsn
|
|
|
|
#include "log0recv.h" // for recv_sys
|
|
|
|
|
2015-03-02 09:55:48 +01:00
|
|
|
#include "ha_prototypes.h" // IB_LOG_
|
2015-02-25 12:26:57 +01:00
|
|
|
|
2014-12-22 15:53:17 +01:00
|
|
|
/* If true, enable redo log encryption. */
|
|
|
|
UNIV_INTERN my_bool srv_encrypt_log = FALSE;
|
|
|
|
/*
|
|
|
|
Sub system type for InnoDB redo log crypto.
|
|
|
|
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};
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
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 &&
|
2015-01-05 13:36:14 +01:00
|
|
|
get_latest_encryption_key_version() == UNENCRYPTED_KEY_VER)
|
2014-12-22 15:53:17 +01:00
|
|
|
because they haven't been read and set yet in the situation of resetting
|
|
|
|
redo logs.
|
|
|
|
*/
|
|
|
|
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) != AES_OK)
|
|
|
|
{
|
2015-03-02 09:55:48 +01:00
|
|
|
ib_logf(IB_LOG_LEVEL_ERROR,
|
|
|
|
"Redo log crypto: generate "
|
|
|
|
"%u-byte random number as crypto msg failed.",
|
2014-12-22 15:53:17 +01:00
|
|
|
PURPOSE_BYTE_LEN);
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (my_random_bytes(aes_ctr_nonce, MY_AES_BLOCK_SIZE) != AES_OK)
|
|
|
|
{
|
2015-03-02 09:55:48 +01:00
|
|
|
ib_logf(IB_LOG_LEVEL_ERROR,
|
|
|
|
"Redo log crypto: generate "
|
|
|
|
"%u-byte random number as AES_CTR nonce failed.",
|
2014-12-22 15:53:17 +01:00
|
|
|
MY_AES_BLOCK_SIZE);
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
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)
|
|
|
|
{
|
2015-03-02 09:55:48 +01:00
|
|
|
ib_logf(IB_LOG_LEVEL_INFO,
|
|
|
|
"Redo log crypto: unencrypted key ver.");
|
2014-12-22 15:53:17 +01:00
|
|
|
memset(key, 0, MY_AES_BLOCK_SIZE);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (crypt_msg[PURPOSE_BYTE_OFFSET] != redo_log_purpose_byte)
|
|
|
|
{
|
2015-03-02 09:55:48 +01:00
|
|
|
ib_logf(IB_LOG_LEVEL_ERROR,
|
|
|
|
"Redo log crypto: msg type mismatched. "
|
|
|
|
"Expected: %x; Actual: %x.",
|
2014-12-22 15:53:17 +01:00
|
|
|
redo_log_purpose_byte, crypt_msg[PURPOSE_BYTE_OFFSET]);
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
byte mysqld_key[MY_AES_BLOCK_SIZE] = {0};
|
2015-03-31 19:00:51 +02:00
|
|
|
uint keylen= sizeof(mysqld_key);
|
2015-04-01 21:25:02 +02:00
|
|
|
if (encryption_key_get(crypt_ver, mysqld_key, &keylen))
|
2014-12-22 15:53:17 +01:00
|
|
|
{
|
2015-03-02 09:55:48 +01:00
|
|
|
ib_logf(IB_LOG_LEVEL_ERROR,
|
|
|
|
"Redo log crypto: getting mysqld crypto key "
|
|
|
|
"from key version failed.");
|
2014-12-22 15:53:17 +01:00
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2014-12-29 20:55:20 +01:00
|
|
|
uint32 dst_len;
|
2015-03-31 19:32:35 +02:00
|
|
|
int rc= my_aes_encrypt_ecb(crypt_msg, MY_AES_BLOCK_SIZE, //src, srclen
|
2014-12-29 20:55:20 +01:00
|
|
|
key, &dst_len, //dst, &dstlen
|
|
|
|
(unsigned char*)&mysqld_key, sizeof(mysqld_key),
|
2015-03-31 19:32:35 +02:00
|
|
|
NULL, 0, 1);
|
2014-12-29 20:55:20 +01:00
|
|
|
|
2014-12-22 15:53:17 +01:00
|
|
|
if (rc != AES_OK || dst_len != MY_AES_BLOCK_SIZE)
|
|
|
|
{
|
2015-03-02 09:55:48 +01:00
|
|
|
ib_logf(IB_LOG_LEVEL_ERROR,
|
|
|
|
"Redo log crypto: getting redo log crypto key "
|
|
|
|
"failed.");
|
2014-12-22 15:53:17 +01:00
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Get a log block's start lsn.
|
|
|
|
@return a log block's start lsn */
|
|
|
|
static inline
|
|
|
|
lsn_t
|
|
|
|
log_block_get_start_lsn(
|
|
|
|
/*====================*/
|
|
|
|
lsn_t lsn, /*!< in: checkpoint lsn */
|
|
|
|
ulint log_block_no) /*!< in: log block number */
|
|
|
|
{
|
|
|
|
lsn_t start_lsn =
|
|
|
|
(lsn & (lsn_t)0xffffffff00000000ULL) |
|
|
|
|
(((log_block_no - 1) & (lsn_t)0x3fffffff) << 9);
|
|
|
|
return start_lsn;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
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*/
|
|
|
|
{
|
|
|
|
byte *log_block = (byte*)block;
|
|
|
|
Crypt_result rc = AES_OK;
|
2014-12-29 20:55:20 +01:00
|
|
|
uint32 src_len, dst_len;
|
2014-12-22 15:53:17 +01:00
|
|
|
byte aes_ctr_counter[MY_AES_BLOCK_SIZE];
|
|
|
|
ulint log_block_no, log_block_start_lsn;
|
|
|
|
byte *key;
|
|
|
|
ulint lsn;
|
|
|
|
if (is_encrypt)
|
|
|
|
{
|
|
|
|
ut_a(log_sys && log_sys->redo_log_crypt_ver != UNENCRYPTED_KEY_VER);
|
|
|
|
key = (byte *)(log_sys->redo_log_crypt_key);
|
|
|
|
lsn = log_sys->lsn;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
ut_a(recv_sys && recv_sys->recv_log_crypt_ver != UNENCRYPTED_KEY_VER);
|
|
|
|
key = (byte *)(recv_sys->recv_log_crypt_key);
|
|
|
|
lsn = srv_start_lsn;
|
|
|
|
}
|
|
|
|
ut_a(size % OS_FILE_LOG_BLOCK_SIZE == 0);
|
|
|
|
src_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE;
|
|
|
|
for (ulint i = 0; i < size ; i += OS_FILE_LOG_BLOCK_SIZE)
|
|
|
|
{
|
|
|
|
log_block_no = log_block_get_hdr_no(log_block);
|
|
|
|
log_block_start_lsn = log_block_get_start_lsn(lsn, log_block_no);
|
|
|
|
|
|
|
|
// Assume log block header is not encrypted
|
|
|
|
memcpy(dst_block, log_block, LOG_BLOCK_HDR_SIZE);
|
|
|
|
|
|
|
|
// aes_ctr_counter = nonce(3-byte) + start lsn to a log block
|
|
|
|
// (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);
|
|
|
|
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);
|
2014-12-29 20:55:20 +01:00
|
|
|
|
2015-04-01 21:25:02 +02:00
|
|
|
int rc = encryption_encrypt(log_block + LOG_BLOCK_HDR_SIZE, src_len,
|
|
|
|
dst_block + LOG_BLOCK_HDR_SIZE, &dst_len,
|
|
|
|
(unsigned char*)key, 16,
|
|
|
|
aes_ctr_counter, MY_AES_BLOCK_SIZE, 1,
|
|
|
|
log_sys->redo_log_crypt_ver);
|
2014-12-29 20:55:20 +01:00
|
|
|
|
|
|
|
ut_a(rc == AES_OK);
|
2014-12-22 15:53:17 +01:00
|
|
|
ut_a(dst_len == src_len);
|
|
|
|
log_block += OS_FILE_LOG_BLOCK_SIZE;
|
|
|
|
dst_block += OS_FILE_LOG_BLOCK_SIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Encrypt log blocks. */
|
|
|
|
UNIV_INTERN
|
|
|
|
Crypt_result
|
|
|
|
log_blocks_encrypt(
|
|
|
|
/*===============*/
|
|
|
|
const byte* block, /*!< in: blocks before encryption */
|
|
|
|
const ulint size, /*!< in: size of blocks, must be multiple of a log block */
|
|
|
|
byte* dst_block) /*!< out: blocks after encryption */
|
|
|
|
{
|
|
|
|
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. */
|
|
|
|
UNIV_INTERN
|
|
|
|
void
|
|
|
|
log_crypt_set_ver_and_key(
|
|
|
|
/*======================*/
|
|
|
|
uint& key_ver, /*!< out: latest key version */
|
|
|
|
byte* crypt_key) /*!< out: crypto key */
|
|
|
|
{
|
2015-02-25 12:26:57 +01:00
|
|
|
bool encrypted;
|
|
|
|
|
|
|
|
if (srv_encrypt_log) {
|
|
|
|
unsigned int vkey;
|
2015-04-01 21:25:02 +02:00
|
|
|
vkey = encryption_key_get_latest_version();
|
2015-02-25 12:26:57 +01:00
|
|
|
encrypted = true;
|
|
|
|
|
|
|
|
if (vkey == UNENCRYPTED_KEY_VER ||
|
2015-04-01 21:25:02 +02:00
|
|
|
vkey == ENCRYPTION_KEY_VERSION_INVALID) {
|
2015-02-25 12:26:57 +01:00
|
|
|
encrypted = false;
|
|
|
|
|
2015-03-02 09:55:48 +01:00
|
|
|
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.");
|
|
|
|
|
2015-02-25 12:26:57 +01:00
|
|
|
srv_encrypt_log = FALSE;
|
|
|
|
} else {
|
|
|
|
key_ver = vkey;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
encrypted = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!encrypted) {
|
2014-12-22 15:53:17 +01:00
|
|
|
key_ver = UNENCRYPTED_KEY_VER;
|
|
|
|
memset(crypt_key, 0, MY_AES_BLOCK_SIZE);
|
|
|
|
return;
|
|
|
|
}
|
2015-02-25 12:26:57 +01:00
|
|
|
|
2014-12-22 15:53:17 +01:00
|
|
|
log_init_crypt_key(redo_log_crypt_msg, key_ver, crypt_key);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*********************************************************************//**
|
|
|
|
Writes the crypto (version, msg and iv) info, which has been used for
|
|
|
|
log blocks with lsn <= this checkpoint's lsn, to a log header's
|
|
|
|
checkpoint buf. */
|
|
|
|
UNIV_INTERN
|
|
|
|
void
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
}
|