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 | |