Commit 70f0dbe4 authored by Marko Mäkelä's avatar Marko Mäkelä

Cleanup: log upgrade and encryption

log_crypt_101_read_checkpoint(), log_crypt_101_read_block():
Declare as ATTRIBUTE_COLD. These are only used when
checking that a MariaDB 10.1 encrypted redo log is clean.

log_block_calc_checksum_format_0(): Define in the only
compilation unit where it is needed. This is only used
when reading the checkpoint information from redo logs
before MariaDB 10.2.2.

crypt_info_t: Declare the byte arrays directly with alignas().

log_crypt(): Use memcpy_aligned instead of reinterpret_cast
on integers.
parent 522fbfcb
/*****************************************************************************
Copyright (C) 2013, 2015, Google Inc. All Rights Reserved.
Copyright (C) 2014, 2018, MariaDB Corporation.
Copyright (C) 2014, 2020, MariaDB Corporation.
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
......@@ -55,22 +55,18 @@ log_crypt_write_checkpoint_buf(
/** Read the MariaDB 10.1 checkpoint crypto (version, msg and iv) info.
@param[in] buf checkpoint buffer
@return whether the operation was successful */
UNIV_INTERN
bool
log_crypt_101_read_checkpoint(const byte* buf);
ATTRIBUTE_COLD bool log_crypt_101_read_checkpoint(const byte* buf);
/** Decrypt a MariaDB 10.1 redo log block.
@param[in,out] buf log block
@param[in] start_lsn server start LSN
@return whether the decryption was successful */
bool log_crypt_101_read_block(byte* buf, lsn_t start_lsn);
ATTRIBUTE_COLD bool log_crypt_101_read_block(byte* buf, lsn_t start_lsn);
/** Read the checkpoint crypto (version, msg and iv) info.
@param[in] buf checkpoint buffer
@return whether the operation was successful */
UNIV_INTERN
bool
log_crypt_read_checkpoint_buf(const byte* buf);
bool log_crypt_read_checkpoint_buf(const byte* buf);
/** log_crypt() operation code */
enum log_crypt_t {
......
......@@ -624,7 +624,7 @@ struct log_t{
@param[in] lsn log sequence number
@return offset within the log */
inline lsn_t calc_lsn_offset(lsn_t lsn) const;
lsn_t calc_lsn_offset_old(lsn_t lsn) const;
inline lsn_t calc_lsn_offset_old(lsn_t lsn) const;
/** Set the field values to correspond to a given lsn. */
void set_fields(lsn_t lsn)
......
/*****************************************************************************
Copyright (c) 1995, 2015, Oracle and/or its affiliates. All Rights Reserved.
Copyright (c) 2017, 2019, MariaDB Corporation.
Copyright (c) 2017, 2020, MariaDB Corporation.
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
......@@ -187,35 +187,6 @@ log_block_convert_lsn_to_no(
0xFUL, 0x3FFFFFFFUL)) + 1);
}
/** Calculate the checksum for a log block using the pre-5.7.9 algorithm.
@param[in] block log block
@return checksum */
UNIV_INLINE
ulint
log_block_calc_checksum_format_0(
const byte* block)
{
ulint sum;
ulint sh;
ulint i;
sum = 1;
sh = 0;
for (i = 0; i < OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_CHECKSUM; i++) {
ulint b = (ulint) block[i];
sum &= 0x7FFFFFFFUL;
sum += b;
sum += b << sh;
sh++;
if (sh > 24) {
sh = 0;
}
}
return(sum);
}
/** Calculate the CRC-32C checksum of a log block.
@param[in] block log block
@return checksum */
......
/*****************************************************************************
Copyright (C) 2013, 2015, Google Inc. All Rights Reserved.
Copyright (C) 2014, 2019, MariaDB Corporation.
Copyright (C) 2014, 2020, MariaDB Corporation.
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
......@@ -37,22 +37,15 @@ my_bool srv_encrypt_log;
/** Redo log encryption key ID */
#define LOG_DEFAULT_ENCRYPTION_KEY 1
struct aes_block_t {
byte bytes[MY_AES_BLOCK_SIZE];
};
struct crypt_info_t {
ulint checkpoint_no; /*!< checkpoint no; 32 bits */
uint key_version; /*!< mysqld key version */
/** random string for encrypting the key */
aes_block_t crypt_msg;
alignas(8) byte crypt_msg[MY_AES_BLOCK_SIZE];
/** the secret key */
aes_block_t crypt_key;
alignas(8) byte crypt_key[MY_AES_BLOCK_SIZE];
/** a random string for the per-block initialization vector */
union {
uint32_t word;
byte bytes[4];
} crypt_nonce;
alignas(4) byte crypt_nonce[4];
};
/** The crypt info */
......@@ -91,7 +84,7 @@ static bool init_crypt_key(crypt_info_t* info, bool upgrade = false)
byte mysqld_key[MY_AES_MAX_KEY_LENGTH];
uint keylen = sizeof mysqld_key;
compile_time_assert(16 == sizeof info->crypt_key.bytes);
compile_time_assert(16 == sizeof info->crypt_key);
compile_time_assert(16 == MY_AES_BLOCK_SIZE);
if (uint rc = encryption_key_get(LOG_DEFAULT_ENCRYPTION_KEY,
......@@ -114,8 +107,8 @@ static bool init_crypt_key(crypt_info_t* info, bool upgrade = false)
uint dst_len;
int err= my_aes_crypt(MY_AES_ECB,
ENCRYPTION_FLAG_NOPAD | ENCRYPTION_FLAG_ENCRYPT,
info->crypt_msg.bytes, MY_AES_BLOCK_SIZE,
info->crypt_key.bytes, &dst_len,
info->crypt_msg, MY_AES_BLOCK_SIZE,
info->crypt_key, &dst_len,
mysqld_key, keylen, NULL, 0);
if (err != MY_AES_OK || dst_len != MY_AES_BLOCK_SIZE) {
......@@ -139,32 +132,24 @@ bool log_crypt(byte* buf, lsn_t lsn, ulint size, log_crypt_t op)
ut_ad(ulint(buf) % OS_FILE_LOG_BLOCK_SIZE == 0);
ut_a(info.key_version);
uint32_t aes_ctr_iv[MY_AES_BLOCK_SIZE / sizeof(uint32_t)];
compile_time_assert(sizeof(uint32_t) == 4);
alignas(8) byte aes_ctr_iv[MY_AES_BLOCK_SIZE];
#define LOG_CRYPT_HDR_SIZE 4
lsn &= ~lsn_t(OS_FILE_LOG_BLOCK_SIZE - 1);
for (const byte* const end = buf + size; buf != end;
buf += OS_FILE_LOG_BLOCK_SIZE, lsn += OS_FILE_LOG_BLOCK_SIZE) {
uint32_t dst[(OS_FILE_LOG_BLOCK_SIZE - LOG_CRYPT_HDR_SIZE
- LOG_BLOCK_CHECKSUM)
/ sizeof(uint32_t)];
alignas(4) byte dst[OS_FILE_LOG_BLOCK_SIZE - LOG_CRYPT_HDR_SIZE
- LOG_BLOCK_CHECKSUM];
/* The log block number is not encrypted. */
*aes_ctr_iv =
#ifdef WORDS_BIGENDIAN
~LOG_BLOCK_FLUSH_BIT_MASK
#else
~(LOG_BLOCK_FLUSH_BIT_MASK >> 24)
#endif
& (*dst = *reinterpret_cast<const uint32_t*>(
buf + LOG_BLOCK_HDR_NO));
#if LOG_BLOCK_HDR_NO + 4 != LOG_CRYPT_HDR_SIZE
# error "LOG_BLOCK_HDR_NO has been moved; redo log format affected!"
#endif
aes_ctr_iv[1] = info.crypt_nonce.word;
mach_write_to_8(reinterpret_cast<byte*>(aes_ctr_iv + 2), lsn);
memcpy_aligned<4>(dst, buf + LOG_BLOCK_HDR_NO, 4);
memcpy_aligned<4>(aes_ctr_iv, buf + LOG_BLOCK_HDR_NO, 4);
*aes_ctr_iv &= ~(LOG_BLOCK_FLUSH_BIT_MASK >> 24);
static_assert(LOG_BLOCK_HDR_NO + 4 == LOG_CRYPT_HDR_SIZE,
"compatibility");
memcpy_aligned<4>(aes_ctr_iv + 4, info.crypt_nonce, 4);
mach_write_to_8(my_assume_aligned<8>(aes_ctr_iv + 8), lsn);
ut_ad(log_block_get_start_lsn(lsn,
log_block_get_hdr_no(buf))
== lsn);
......@@ -212,9 +197,9 @@ bool log_crypt(byte* buf, lsn_t lsn, ulint size, log_crypt_t op)
int rc = encryption_crypt(
buf + LOG_CRYPT_HDR_SIZE, dst_size,
reinterpret_cast<byte*>(dst), &dst_len,
const_cast<byte*>(info.crypt_key.bytes),
const_cast<byte*>(info.crypt_key),
MY_AES_BLOCK_SIZE,
reinterpret_cast<byte*>(aes_ctr_iv), sizeof aes_ctr_iv,
aes_ctr_iv, sizeof aes_ctr_iv,
op == LOG_DECRYPT
? ENCRYPTION_FLAG_DECRYPT | ENCRYPTION_FLAG_NOPAD
: ENCRYPTION_FLAG_ENCRYPT | ENCRYPTION_FLAG_NOPAD,
......@@ -234,37 +219,31 @@ The random parameters will be persisted in the log checkpoint pages.
@see log_crypt_write_checkpoint_buf()
@see log_crypt_read_checkpoint_buf()
@return whether the operation succeeded */
UNIV_INTERN
bool
log_crypt_init()
bool log_crypt_init()
{
info.key_version = encryption_key_get_latest_version(
LOG_DEFAULT_ENCRYPTION_KEY);
if (info.key_version == ENCRYPTION_KEY_VERSION_INVALID) {
ib::error() << "innodb_encrypt_log: cannot get key version";
info.key_version = 0;
return false;
}
if (my_random_bytes(tmp_iv, MY_AES_BLOCK_SIZE) != MY_AES_OK
|| my_random_bytes(info.crypt_msg.bytes, sizeof info.crypt_msg)
!= MY_AES_OK
|| my_random_bytes(info.crypt_nonce.bytes, sizeof info.crypt_nonce)
!= MY_AES_OK) {
ib::error() << "innodb_encrypt_log: my_random_bytes() failed";
return false;
}
return init_crypt_key(&info);
info.key_version=
encryption_key_get_latest_version(LOG_DEFAULT_ENCRYPTION_KEY);
if (info.key_version == ENCRYPTION_KEY_VERSION_INVALID)
ib::error() << "log_crypt_init(): cannot get key version";
else if (my_random_bytes(tmp_iv, MY_AES_BLOCK_SIZE) != MY_AES_OK ||
my_random_bytes(info.crypt_msg, sizeof info.crypt_msg) !=
MY_AES_OK ||
my_random_bytes(info.crypt_nonce, sizeof info.crypt_nonce) !=
MY_AES_OK)
ib::error() << "log_crypt_init(): my_random_bytes() failed";
else if (init_crypt_key(&info))
goto func_exit;
info.key_version= 0;
func_exit:
return info.key_version != 0;
}
/** Read the MariaDB 10.1 checkpoint crypto (version, msg and iv) info.
@param[in] buf checkpoint buffer
@return whether the operation was successful */
UNIV_INTERN
bool
log_crypt_101_read_checkpoint(const byte* buf)
ATTRIBUTE_COLD bool log_crypt_101_read_checkpoint(const byte* buf)
{
buf += 20 + 32 * 9;
......@@ -286,9 +265,8 @@ log_crypt_101_read_checkpoint(const byte* buf)
infos_used++;
info.checkpoint_no = checkpoint_no;
info.key_version = mach_read_from_4(buf + 4);
memcpy(info.crypt_msg.bytes, buf + 8, MY_AES_BLOCK_SIZE);
memcpy(info.crypt_nonce.bytes, buf + 24,
sizeof info.crypt_nonce);
memcpy(info.crypt_msg, buf + 8, MY_AES_BLOCK_SIZE);
memcpy(info.crypt_nonce, buf + 24, sizeof info.crypt_nonce);
if (!init_crypt_key(&info, true)) {
return false;
......@@ -304,10 +282,8 @@ log_crypt_101_read_checkpoint(const byte* buf)
@param[in,out] buf log block
@param[in] start_lsn server start LSN
@return whether the decryption was successful */
bool log_crypt_101_read_block(byte* buf, lsn_t start_lsn)
ATTRIBUTE_COLD bool log_crypt_101_read_block(byte* buf, lsn_t start_lsn)
{
ut_ad(log_block_calc_checksum_format_0(buf)
!= log_block_get_checksum(buf));
const uint32_t checkpoint_no
= uint32_t(log_block_get_checkpoint_no(buf));
const crypt_info_t* info = infos;
......@@ -337,7 +313,7 @@ bool log_crypt_101_read_block(byte* buf, lsn_t start_lsn)
/* The log block header is not encrypted. */
memcpy(dst, buf, LOG_BLOCK_HDR_SIZE);
memcpy(aes_ctr_iv, info->crypt_nonce.bytes, 3);
memcpy(aes_ctr_iv, info->crypt_nonce, 3);
mach_write_to_8(aes_ctr_iv + 3,
log_block_get_start_lsn(start_lsn, log_block_no));
memcpy(aes_ctr_iv + 11, buf, 4);
......@@ -346,7 +322,7 @@ bool log_crypt_101_read_block(byte* buf, lsn_t start_lsn)
int rc = encryption_crypt(buf + LOG_BLOCK_HDR_SIZE, src_len,
dst + LOG_BLOCK_HDR_SIZE, &dst_len,
const_cast<byte*>(info->crypt_key.bytes),
const_cast<byte*>(info->crypt_key),
MY_AES_BLOCK_SIZE,
aes_ctr_iv, MY_AES_BLOCK_SIZE,
ENCRYPTION_FLAG_DECRYPT
......@@ -369,15 +345,15 @@ void
log_crypt_write_checkpoint_buf(byte* buf)
{
ut_ad(info.key_version);
compile_time_assert(16 == sizeof info.crypt_msg.bytes);
compile_time_assert(16 == sizeof info.crypt_msg);
compile_time_assert(16 == MY_AES_BLOCK_SIZE);
compile_time_assert(LOG_CHECKPOINT_CRYPT_MESSAGE
- LOG_CHECKPOINT_CRYPT_NONCE
== sizeof info.crypt_nonce);
memcpy(buf + LOG_CHECKPOINT_CRYPT_MESSAGE, info.crypt_msg.bytes,
memcpy(buf + LOG_CHECKPOINT_CRYPT_MESSAGE, info.crypt_msg,
MY_AES_BLOCK_SIZE);
memcpy(buf + LOG_CHECKPOINT_CRYPT_NONCE, info.crypt_nonce.bytes,
memcpy(buf + LOG_CHECKPOINT_CRYPT_NONCE, info.crypt_nonce,
sizeof info.crypt_nonce);
mach_write_to_4(buf + LOG_CHECKPOINT_CRYPT_KEY, info.key_version);
}
......@@ -385,9 +361,7 @@ log_crypt_write_checkpoint_buf(byte* buf)
/** Read the checkpoint crypto (version, msg and iv) info.
@param[in] buf checkpoint buffer
@return whether the operation was successful */
UNIV_INTERN
bool
log_crypt_read_checkpoint_buf(const byte* buf)
bool log_crypt_read_checkpoint_buf(const byte* buf)
{
info.checkpoint_no = mach_read_from_4(buf + (LOG_CHECKPOINT_NO + 4));
info.key_version = mach_read_from_4(buf + LOG_CHECKPOINT_CRYPT_KEY);
......@@ -395,15 +369,15 @@ log_crypt_read_checkpoint_buf(const byte* buf)
#if MY_AES_BLOCK_SIZE != 16
# error "MY_AES_BLOCK_SIZE != 16; redo log checkpoint format affected"
#endif
compile_time_assert(16 == sizeof info.crypt_msg.bytes);
compile_time_assert(16 == sizeof info.crypt_msg);
compile_time_assert(16 == MY_AES_BLOCK_SIZE);
compile_time_assert(LOG_CHECKPOINT_CRYPT_MESSAGE
- LOG_CHECKPOINT_CRYPT_NONCE
== sizeof info.crypt_nonce);
memcpy(info.crypt_msg.bytes, buf + LOG_CHECKPOINT_CRYPT_MESSAGE,
memcpy(info.crypt_msg, buf + LOG_CHECKPOINT_CRYPT_MESSAGE,
MY_AES_BLOCK_SIZE);
memcpy(info.crypt_nonce.bytes, buf + LOG_CHECKPOINT_CRYPT_NONCE,
memcpy(info.crypt_nonce, buf + LOG_CHECKPOINT_CRYPT_NONCE,
sizeof info.crypt_nonce);
return init_crypt_key(&info);
......@@ -432,7 +406,7 @@ log_tmp_block_encrypt(
int rc = encryption_crypt(
src, uint(size), dst, &dst_len,
const_cast<byte*>(info.crypt_key.bytes), MY_AES_BLOCK_SIZE,
const_cast<byte*>(info.crypt_key), MY_AES_BLOCK_SIZE,
reinterpret_cast<byte*>(iv), uint(sizeof iv),
encrypt
? ENCRYPTION_FLAG_ENCRYPT|ENCRYPTION_FLAG_NOPAD
......
......@@ -1320,15 +1320,32 @@ static bool redo_file_sizes_are_correct()
return false;
}
/** Calculate the checksum for a log block using the pre-10.2.2 algorithm. */
inline uint32_t log_block_calc_checksum_format_0(const byte *b)
{
uint32_t sum= 1;
const byte *const end= &b[512 - 4];
for (uint32_t sh= 0; b < end; )
{
sum&= 0x7FFFFFFFUL;
sum+= uint32_t{*b} << sh++;
sum+= *b++;
if (sh > 24)
sh= 0;
}
return sum;
}
/** Determine if a redo log from before MariaDB 10.2.2 is clean.
@return error code
@retval DB_SUCCESS if the redo log is clean
@retval DB_CORRUPTION if the redo log is corrupted
@retval DB_ERROR if the redo log is not empty */
static dberr_t recv_log_recover_pre_10_2()
ATTRIBUTE_COLD static dberr_t recv_log_recover_pre_10_2()
{
uint64_t max_no= 0;
uint64_t checkpoint_no;
byte *buf= log_sys.buf;
ut_ad(log_sys.log.format == 0);
......@@ -1364,17 +1381,17 @@ static dberr_t recv_log_recover_pre_10_2()
continue;
}
checkpoint_no = mach_read_from_8(buf + LOG_CHECKPOINT_NO);
if (!log_crypt_101_read_checkpoint(buf))
{
ib::error() << "Decrypting checkpoint failed";
continue;
}
const uint64_t checkpoint_no= mach_read_from_8(buf);
DBUG_PRINT("ib_log", ("checkpoint " UINT64PF " at " LSN_PF " found",
checkpoint_no,
mach_read_from_8(buf + LOG_CHECKPOINT_LSN)));
mach_read_from_8(buf + CHECKPOINT_LSN)));
if (checkpoint_no >= max_no)
{
......@@ -1435,10 +1452,12 @@ static dberr_t recv_log_recover_pre_10_2()
return DB_ERROR;
}
/** Same as cals_lsn_offset() except that it supports multiple files */
lsn_t log_t::file::calc_lsn_offset_old(lsn_t lsn) const
/** Calculate the offset of a log sequence number
in an old redo log file (during upgrade check).
@param[in] lsn log sequence number
@return byte offset within the log */
inline lsn_t log_t::file::calc_lsn_offset_old(lsn_t lsn) const
{
ut_ad(log_sys.mutex.is_owned() || log_write_lock_own());
const lsn_t size= capacity() * recv_sys.files_size();
lsn_t l= lsn - this->lsn;
if (longlong(l) < 0)
......
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