1/*****************************************************************************
2
3Copyright (C) 2013, 2015, Google Inc. All Rights Reserved.
4Copyright (C) 2014, 2017, MariaDB Corporation. All Rights Reserved.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
18*****************************************************************************/
19/**************************************************//**
20@file log0crypt.cc
21Innodb log encrypt/decrypt
22
23Created 11/25/2013 Minli Zhu Google
24Modified Jan Lindström jan.lindstrom@mariadb.com
25MDEV-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 */
37my_bool srv_encrypt_log;
38
39/** Redo log encryption key ID */
40#define LOG_DEFAULT_ENCRYPTION_KEY 1
41
42typedef union {
43 uint32_t words[MY_AES_BLOCK_SIZE / sizeof(uint32_t)];
44 byte bytes[MY_AES_BLOCK_SIZE];
45} aes_block_t;
46
47struct 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 */
62static crypt_info_t info;
63
64/** Crypt info when upgrading from 10.1 */
65static crypt_info_t infos[5];
66
67/*********************************************************************//**
68Get a log block's start lsn.
69@return a log block's start lsn */
70static inline
71lsn_t
72log_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/*********************************************************************//**
84Get crypt info from checkpoint.
85@return a crypt info or NULL if not present. */
86static
87const crypt_info_t*
88get_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 */
110UNIV_INTERN
111void
112log_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 */
169static
170bool
171init_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
212when creating a new redo log.
213The 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 */
217UNIV_INTERN
218bool
219log_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 */
247UNIV_INTERN
248bool
249log_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 */
275UNIV_INTERN
276bool
277log_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 */
328UNIV_INTERN
329void
330log_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 */
348UNIV_INTERN
349bool
350log_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 */
379UNIV_INTERN
380bool
381log_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