| 1 | /***************************************************************************** |
| 2 | |
| 3 | Copyright (C) 2013, 2015, Google Inc. All Rights Reserved. |
| 4 | Copyright (C) 2014, 2017, MariaDB Corporation. All Rights Reserved. |
| 5 | |
| 6 | This program is free software; you can redistribute it and/or modify it under |
| 7 | the terms of the GNU General Public License as published by the Free Software |
| 8 | Foundation; version 2 of the License. |
| 9 | |
| 10 | This program is distributed in the hope that it will be useful, but WITHOUT |
| 11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 12 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
| 13 | |
| 14 | You should have received a copy of the GNU General Public License along with |
| 15 | this program; if not, write to the Free Software Foundation, Inc., |
| 16 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| 17 | |
| 18 | *****************************************************************************/ |
| 19 | /**************************************************//** |
| 20 | @file log0crypt.cc |
| 21 | Innodb log encrypt/decrypt |
| 22 | |
| 23 | Created 11/25/2013 Minli Zhu Google |
| 24 | Modified Jan Lindström jan.lindstrom@mariadb.com |
| 25 | MDEV-11782: Rewritten for MariaDB 10.2 by Marko Mäkelä, MariaDB Corporation. |
| 26 | *******************************************************/ |
| 27 | #include <my_global.h> |
| 28 | #include "m_string.h" |
| 29 | #include "log0crypt.h" |
| 30 | #include <mysql/service_my_crypt.h> |
| 31 | |
| 32 | #include "log0crypt.h" |
| 33 | #include "srv0start.h" // for srv_start_lsn |
| 34 | #include "log0recv.h" // for recv_sys |
| 35 | |
| 36 | /** innodb_encrypt_log: whether to encrypt the redo log */ |
| 37 | my_bool srv_encrypt_log; |
| 38 | |
| 39 | /** Redo log encryption key ID */ |
| 40 | #define LOG_DEFAULT_ENCRYPTION_KEY 1 |
| 41 | |
| 42 | typedef union { |
| 43 | uint32_t words[MY_AES_BLOCK_SIZE / sizeof(uint32_t)]; |
| 44 | byte bytes[MY_AES_BLOCK_SIZE]; |
| 45 | } aes_block_t; |
| 46 | |
| 47 | struct crypt_info_t { |
| 48 | ulint checkpoint_no; /*!< checkpoint no; 32 bits */ |
| 49 | uint key_version; /*!< mysqld key version */ |
| 50 | /** random string for encrypting the key */ |
| 51 | aes_block_t crypt_msg; |
| 52 | /** the secret key */ |
| 53 | aes_block_t crypt_key; |
| 54 | /** a random string for the per-block initialization vector */ |
| 55 | union { |
| 56 | uint32_t word; |
| 57 | byte bytes[4]; |
| 58 | } crypt_nonce; |
| 59 | }; |
| 60 | |
| 61 | /** The crypt info */ |
| 62 | static crypt_info_t info; |
| 63 | |
| 64 | /** Crypt info when upgrading from 10.1 */ |
| 65 | static crypt_info_t infos[5]; |
| 66 | |
| 67 | /*********************************************************************//** |
| 68 | Get a log block's start lsn. |
| 69 | @return a log block's start lsn */ |
| 70 | static inline |
| 71 | lsn_t |
| 72 | log_block_get_start_lsn( |
| 73 | /*====================*/ |
| 74 | lsn_t lsn, /*!< in: checkpoint lsn */ |
| 75 | ulint log_block_no) /*!< in: log block number */ |
| 76 | { |
| 77 | lsn_t start_lsn = |
| 78 | (lsn & (lsn_t)0xffffffff00000000ULL) | |
| 79 | (((log_block_no - 1) & (lsn_t)0x3fffffff) << 9); |
| 80 | return start_lsn; |
| 81 | } |
| 82 | |
| 83 | /*********************************************************************//** |
| 84 | Get crypt info from checkpoint. |
| 85 | @return a crypt info or NULL if not present. */ |
| 86 | static |
| 87 | const crypt_info_t* |
| 88 | get_crypt_info(ulint checkpoint_no) |
| 89 | { |
| 90 | /* a log block only stores 4-bytes of checkpoint no */ |
| 91 | checkpoint_no &= 0xFFFFFFFF; |
| 92 | for (unsigned i = 0; i < 5; i++) { |
| 93 | const crypt_info_t* it = &infos[i]; |
| 94 | |
| 95 | if (it->key_version && it->checkpoint_no == checkpoint_no) { |
| 96 | return it; |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | /* If checkpoint contains more than one key and we did not |
| 101 | find the correct one use the first one. */ |
| 102 | return infos; |
| 103 | } |
| 104 | |
| 105 | /** Encrypt or decrypt log blocks. |
| 106 | @param[in,out] buf log blocks to encrypt or decrypt |
| 107 | @param[in] lsn log sequence number of the start of the buffer |
| 108 | @param[in] size size of the buffer, in bytes |
| 109 | @param[in] decrypt whether to decrypt instead of encrypting */ |
| 110 | UNIV_INTERN |
| 111 | void |
| 112 | log_crypt(byte* buf, lsn_t lsn, ulint size, bool decrypt) |
| 113 | { |
| 114 | ut_ad(size % OS_FILE_LOG_BLOCK_SIZE == 0); |
| 115 | ut_a(info.key_version); |
| 116 | |
| 117 | uint dst_len; |
| 118 | uint32_t aes_ctr_iv[MY_AES_BLOCK_SIZE / sizeof(uint32_t)]; |
| 119 | compile_time_assert(sizeof(uint32_t) == 4); |
| 120 | |
| 121 | #define LOG_CRYPT_HDR_SIZE 4 |
| 122 | lsn &= ~lsn_t(OS_FILE_LOG_BLOCK_SIZE - 1); |
| 123 | |
| 124 | for (const byte* const end = buf + size; buf != end; |
| 125 | buf += OS_FILE_LOG_BLOCK_SIZE, lsn += OS_FILE_LOG_BLOCK_SIZE) { |
| 126 | uint32_t dst[(OS_FILE_LOG_BLOCK_SIZE - LOG_CRYPT_HDR_SIZE) |
| 127 | / sizeof(uint32_t)]; |
| 128 | |
| 129 | /* The log block number is not encrypted. */ |
| 130 | *aes_ctr_iv = |
| 131 | #ifdef WORDS_BIGENDIAN |
| 132 | ~LOG_BLOCK_FLUSH_BIT_MASK |
| 133 | #else |
| 134 | ~(LOG_BLOCK_FLUSH_BIT_MASK >> 24) |
| 135 | #endif |
| 136 | & (*dst = *reinterpret_cast<const uint32_t*>( |
| 137 | buf + LOG_BLOCK_HDR_NO)); |
| 138 | #if LOG_BLOCK_HDR_NO + 4 != LOG_CRYPT_HDR_SIZE |
| 139 | # error "LOG_BLOCK_HDR_NO has been moved; redo log format affected!" |
| 140 | #endif |
| 141 | aes_ctr_iv[1] = info.crypt_nonce.word; |
| 142 | mach_write_to_8(reinterpret_cast<byte*>(aes_ctr_iv + 2), lsn); |
| 143 | ut_ad(log_block_get_start_lsn(lsn, |
| 144 | log_block_get_hdr_no(buf)) |
| 145 | == lsn); |
| 146 | |
| 147 | int rc = encryption_crypt( |
| 148 | buf + LOG_CRYPT_HDR_SIZE, sizeof dst, |
| 149 | reinterpret_cast<byte*>(dst), &dst_len, |
| 150 | const_cast<byte*>(info.crypt_key.bytes), |
| 151 | sizeof info.crypt_key, |
| 152 | reinterpret_cast<byte*>(aes_ctr_iv), sizeof aes_ctr_iv, |
| 153 | decrypt |
| 154 | ? ENCRYPTION_FLAG_DECRYPT | ENCRYPTION_FLAG_NOPAD |
| 155 | : ENCRYPTION_FLAG_ENCRYPT | ENCRYPTION_FLAG_NOPAD, |
| 156 | LOG_DEFAULT_ENCRYPTION_KEY, |
| 157 | info.key_version); |
| 158 | |
| 159 | ut_a(rc == MY_AES_OK); |
| 160 | ut_a(dst_len == sizeof dst); |
| 161 | memcpy(buf + LOG_CRYPT_HDR_SIZE, dst, sizeof dst); |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | /** Generate crypt key from crypt msg. |
| 166 | @param[in,out] info encryption key |
| 167 | @param[in] upgrade whether to use the key in MariaDB 10.1 format |
| 168 | @return whether the operation was successful */ |
| 169 | static |
| 170 | bool |
| 171 | init_crypt_key(crypt_info_t* info, bool upgrade = false) |
| 172 | { |
| 173 | byte mysqld_key[MY_AES_MAX_KEY_LENGTH]; |
| 174 | uint keylen = sizeof mysqld_key; |
| 175 | |
| 176 | compile_time_assert(16 == sizeof info->crypt_key); |
| 177 | |
| 178 | if (uint rc = encryption_key_get(LOG_DEFAULT_ENCRYPTION_KEY, |
| 179 | info->key_version, mysqld_key, |
| 180 | &keylen)) { |
| 181 | ib::error() |
| 182 | << "Obtaining redo log encryption key version " |
| 183 | << info->key_version << " failed (" << rc |
| 184 | << "). Maybe the key or the required encryption " |
| 185 | << " key management plugin was not found." ; |
| 186 | return false; |
| 187 | } |
| 188 | |
| 189 | if (upgrade) { |
| 190 | while (keylen < sizeof mysqld_key) { |
| 191 | mysqld_key[keylen++] = 0; |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | uint dst_len; |
| 196 | int err= my_aes_crypt(MY_AES_ECB, |
| 197 | ENCRYPTION_FLAG_NOPAD | ENCRYPTION_FLAG_ENCRYPT, |
| 198 | info->crypt_msg.bytes, sizeof info->crypt_msg, |
| 199 | info->crypt_key.bytes, &dst_len, |
| 200 | mysqld_key, keylen, NULL, 0); |
| 201 | |
| 202 | if (err != MY_AES_OK || dst_len != MY_AES_BLOCK_SIZE) { |
| 203 | ib::error() << "Getting redo log crypto key failed: err = " |
| 204 | << err << ", len = " << dst_len; |
| 205 | return false; |
| 206 | } |
| 207 | |
| 208 | return true; |
| 209 | } |
| 210 | |
| 211 | /** Initialize the redo log encryption key and random parameters |
| 212 | when creating a new redo log. |
| 213 | The random parameters will be persisted in the log checkpoint pages. |
| 214 | @see log_crypt_write_checkpoint_buf() |
| 215 | @see log_crypt_read_checkpoint_buf() |
| 216 | @return whether the operation succeeded */ |
| 217 | UNIV_INTERN |
| 218 | bool |
| 219 | log_crypt_init() |
| 220 | { |
| 221 | ut_ad(log_mutex_own()); |
| 222 | ut_ad(log_sys.is_encrypted()); |
| 223 | |
| 224 | info.key_version = encryption_key_get_latest_version( |
| 225 | LOG_DEFAULT_ENCRYPTION_KEY); |
| 226 | |
| 227 | if (info.key_version == ENCRYPTION_KEY_VERSION_INVALID) { |
| 228 | ib::error() << "innodb_encrypt_log: cannot get key version" ; |
| 229 | info.key_version = 0; |
| 230 | return false; |
| 231 | } |
| 232 | |
| 233 | if (my_random_bytes(info.crypt_msg.bytes, sizeof info.crypt_msg) |
| 234 | != MY_AES_OK |
| 235 | || my_random_bytes(info.crypt_nonce.bytes, sizeof info.crypt_nonce) |
| 236 | != MY_AES_OK) { |
| 237 | ib::error() << "innodb_encrypt_log: my_random_bytes() failed" ; |
| 238 | return false; |
| 239 | } |
| 240 | |
| 241 | return init_crypt_key(&info); |
| 242 | } |
| 243 | |
| 244 | /** Read the MariaDB 10.1 checkpoint crypto (version, msg and iv) info. |
| 245 | @param[in] buf checkpoint buffer |
| 246 | @return whether the operation was successful */ |
| 247 | UNIV_INTERN |
| 248 | bool |
| 249 | log_crypt_101_read_checkpoint(const byte* buf) |
| 250 | { |
| 251 | buf += 20 + 32 * 9; |
| 252 | |
| 253 | const size_t n = *buf++ == 2 ? std::min(unsigned(*buf++), 5U) : 0; |
| 254 | |
| 255 | for (size_t i = 0; i < n; i++) { |
| 256 | struct crypt_info_t& info = infos[i]; |
| 257 | info.checkpoint_no = mach_read_from_4(buf); |
| 258 | info.key_version = mach_read_from_4(buf + 4); |
| 259 | memcpy(info.crypt_msg.bytes, buf + 8, sizeof info.crypt_msg); |
| 260 | memcpy(info.crypt_nonce.bytes, buf + 24, |
| 261 | sizeof info.crypt_nonce); |
| 262 | |
| 263 | if (!init_crypt_key(&info, true)) { |
| 264 | return false; |
| 265 | } |
| 266 | buf += 4 + 4 + 2 * MY_AES_BLOCK_SIZE; |
| 267 | } |
| 268 | |
| 269 | return true; |
| 270 | } |
| 271 | |
| 272 | /** Decrypt a MariaDB 10.1 redo log block. |
| 273 | @param[in,out] buf log block |
| 274 | @return whether the decryption was successful */ |
| 275 | UNIV_INTERN |
| 276 | bool |
| 277 | log_crypt_101_read_block(byte* buf) |
| 278 | { |
| 279 | ut_ad(log_block_calc_checksum_format_0(buf) |
| 280 | != log_block_get_checksum(buf)); |
| 281 | const crypt_info_t* info = get_crypt_info( |
| 282 | log_block_get_checkpoint_no(buf)); |
| 283 | |
| 284 | if (!info || info->key_version == 0) { |
| 285 | return false; |
| 286 | } |
| 287 | |
| 288 | byte dst[OS_FILE_LOG_BLOCK_SIZE]; |
| 289 | uint dst_len; |
| 290 | byte aes_ctr_iv[MY_AES_BLOCK_SIZE]; |
| 291 | |
| 292 | const uint src_len = OS_FILE_LOG_BLOCK_SIZE - LOG_BLOCK_HDR_SIZE; |
| 293 | |
| 294 | ulint log_block_no = log_block_get_hdr_no(buf); |
| 295 | |
| 296 | /* The log block header is not encrypted. */ |
| 297 | memcpy(dst, buf, LOG_BLOCK_HDR_SIZE); |
| 298 | |
| 299 | memcpy(aes_ctr_iv, info->crypt_nonce.bytes, 3); |
| 300 | mach_write_to_8(aes_ctr_iv + 3, |
| 301 | log_block_get_start_lsn(srv_start_lsn, log_block_no)); |
| 302 | memcpy(aes_ctr_iv + 11, buf, 4); |
| 303 | aes_ctr_iv[11] &= ~(LOG_BLOCK_FLUSH_BIT_MASK >> 24); |
| 304 | aes_ctr_iv[15] = 0; |
| 305 | |
| 306 | int rc = encryption_crypt(buf + LOG_BLOCK_HDR_SIZE, src_len, |
| 307 | dst + LOG_BLOCK_HDR_SIZE, &dst_len, |
| 308 | const_cast<byte*>(info->crypt_key.bytes), |
| 309 | MY_AES_BLOCK_SIZE, |
| 310 | aes_ctr_iv, MY_AES_BLOCK_SIZE, |
| 311 | ENCRYPTION_FLAG_DECRYPT |
| 312 | | ENCRYPTION_FLAG_NOPAD, |
| 313 | LOG_DEFAULT_ENCRYPTION_KEY, |
| 314 | info->key_version); |
| 315 | |
| 316 | if (rc != MY_AES_OK || dst_len != src_len |
| 317 | || log_block_calc_checksum_format_0(dst) |
| 318 | != log_block_get_checksum(dst)) { |
| 319 | return false; |
| 320 | } |
| 321 | |
| 322 | memcpy(buf, dst, sizeof dst); |
| 323 | return true; |
| 324 | } |
| 325 | |
| 326 | /** Add the encryption information to a redo log checkpoint buffer. |
| 327 | @param[in,out] buf checkpoint buffer */ |
| 328 | UNIV_INTERN |
| 329 | void |
| 330 | log_crypt_write_checkpoint_buf(byte* buf) |
| 331 | { |
| 332 | ut_ad(info.key_version); |
| 333 | compile_time_assert(16 == sizeof info.crypt_msg); |
| 334 | compile_time_assert(LOG_CHECKPOINT_CRYPT_MESSAGE |
| 335 | - LOG_CHECKPOINT_CRYPT_NONCE |
| 336 | == sizeof info.crypt_nonce); |
| 337 | |
| 338 | memcpy(buf + LOG_CHECKPOINT_CRYPT_MESSAGE, info.crypt_msg.bytes, |
| 339 | sizeof info.crypt_msg); |
| 340 | memcpy(buf + LOG_CHECKPOINT_CRYPT_NONCE, info.crypt_nonce.bytes, |
| 341 | sizeof info.crypt_nonce); |
| 342 | mach_write_to_4(buf + LOG_CHECKPOINT_CRYPT_KEY, info.key_version); |
| 343 | } |
| 344 | |
| 345 | /** Read the checkpoint crypto (version, msg and iv) info. |
| 346 | @param[in] buf checkpoint buffer |
| 347 | @return whether the operation was successful */ |
| 348 | UNIV_INTERN |
| 349 | bool |
| 350 | log_crypt_read_checkpoint_buf(const byte* buf) |
| 351 | { |
| 352 | info.checkpoint_no = mach_read_from_4(buf + (LOG_CHECKPOINT_NO + 4)); |
| 353 | info.key_version = mach_read_from_4(buf + LOG_CHECKPOINT_CRYPT_KEY); |
| 354 | |
| 355 | #if MY_AES_BLOCK_SIZE != 16 |
| 356 | # error "MY_AES_BLOCK_SIZE != 16; redo log checkpoint format affected" |
| 357 | #endif |
| 358 | compile_time_assert(16 == sizeof info.crypt_msg); |
| 359 | compile_time_assert(LOG_CHECKPOINT_CRYPT_MESSAGE |
| 360 | - LOG_CHECKPOINT_CRYPT_NONCE |
| 361 | == sizeof info.crypt_nonce); |
| 362 | |
| 363 | memcpy(info.crypt_msg.bytes, buf + LOG_CHECKPOINT_CRYPT_MESSAGE, |
| 364 | sizeof info.crypt_msg); |
| 365 | memcpy(info.crypt_nonce.bytes, buf + LOG_CHECKPOINT_CRYPT_NONCE, |
| 366 | sizeof info.crypt_nonce); |
| 367 | |
| 368 | return init_crypt_key(&info); |
| 369 | } |
| 370 | |
| 371 | /** Encrypt or decrypt a temporary file block. |
| 372 | @param[in] src block to encrypt or decrypt |
| 373 | @param[in] size size of the block |
| 374 | @param[out] dst destination block |
| 375 | @param[in] offs offset to block |
| 376 | @param[in] space_id tablespace id |
| 377 | @param[in] encrypt true=encrypt; false=decrypt |
| 378 | @return whether the operation succeeded */ |
| 379 | UNIV_INTERN |
| 380 | bool |
| 381 | log_tmp_block_encrypt( |
| 382 | const byte* src, |
| 383 | ulint size, |
| 384 | byte* dst, |
| 385 | uint64_t offs, |
| 386 | ulint space_id, |
| 387 | bool encrypt) |
| 388 | { |
| 389 | uint dst_len; |
| 390 | uint64_t aes_ctr_iv[MY_AES_BLOCK_SIZE / sizeof(uint64_t)]; |
| 391 | bzero(aes_ctr_iv, sizeof aes_ctr_iv); |
| 392 | aes_ctr_iv[0] = space_id; |
| 393 | aes_ctr_iv[1] = offs; |
| 394 | |
| 395 | int rc = encryption_crypt( |
| 396 | src, (uint)size, dst, &dst_len, |
| 397 | const_cast<byte*>(info.crypt_key.bytes), (uint)(sizeof info.crypt_key), |
| 398 | reinterpret_cast<byte*>(aes_ctr_iv), (uint)(sizeof aes_ctr_iv), |
| 399 | encrypt |
| 400 | ? ENCRYPTION_FLAG_ENCRYPT|ENCRYPTION_FLAG_NOPAD |
| 401 | : ENCRYPTION_FLAG_DECRYPT|ENCRYPTION_FLAG_NOPAD, |
| 402 | LOG_DEFAULT_ENCRYPTION_KEY, info.key_version); |
| 403 | |
| 404 | if (rc != MY_AES_OK) { |
| 405 | ib::error() << (encrypt ? "Encryption" : "Decryption" ) |
| 406 | << " failed for temporary file: " << rc; |
| 407 | } |
| 408 | |
| 409 | return rc == MY_AES_OK; |
| 410 | } |
| 411 | |