1/*
2 Copyright (c) 2013 Google Inc.
3 Copyright (c) 2014, 2015 MariaDB Corporation
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; version 2 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
17
18#include "maria_def.h"
19#include "ma_blockrec.h"
20#include <my_crypt.h>
21
22#define CRYPT_SCHEME_1 1
23#define CRYPT_SCHEME_1_ID_LEN 4 /* 4 bytes for counter-block */
24#define CRYPT_SCHEME_1_IV_LEN 16
25#define CRYPT_SCHEME_1_KEY_VERSION_SIZE 4
26
27#ifdef HAVE_PSI_INTERFACE
28PSI_mutex_key key_CRYPT_DATA_lock;
29#endif
30
31struct st_crypt_key
32{
33 uint key_version;
34 uchar key[CRYPT_SCHEME_1_IV_LEN];
35};
36
37struct st_maria_crypt_data
38{
39 struct st_encryption_scheme scheme;
40 uint space;
41 mysql_mutex_t lock; /* protecting keys */
42};
43
44/**
45 determine what key id to use for Aria encryption
46
47 Same logic as for tempfiles: if key id 2 exists - use it,
48 otherwise use key id 1.
49
50 Key id 1 is system, it always exists. Key id 2 is optional,
51 it allows to specify fast low-grade encryption for temporary data.
52*/
53static uint get_encryption_key_id(MARIA_SHARE *share)
54{
55 if (share->options & HA_OPTION_TMP_TABLE &&
56 encryption_key_id_exists(ENCRYPTION_KEY_TEMPORARY_DATA))
57 return ENCRYPTION_KEY_TEMPORARY_DATA;
58 else
59 return ENCRYPTION_KEY_SYSTEM_DATA;
60}
61
62uint
63ma_crypt_get_data_page_header_space()
64{
65 return CRYPT_SCHEME_1_KEY_VERSION_SIZE;
66}
67
68uint
69ma_crypt_get_index_page_header_space(MARIA_SHARE *share)
70{
71 if (share->base.born_transactional)
72 {
73 return CRYPT_SCHEME_1_KEY_VERSION_SIZE;
74 }
75 else
76 {
77 /* if the index is not transactional, we add 7 bytes LSN anyway
78 to be used for counter block
79 */
80 return LSN_STORE_SIZE + CRYPT_SCHEME_1_KEY_VERSION_SIZE;
81 }
82}
83
84uint
85ma_crypt_get_file_length()
86{
87 return 2 + CRYPT_SCHEME_1_IV_LEN + CRYPT_SCHEME_1_ID_LEN;
88}
89
90static void crypt_data_scheme_locker(struct st_encryption_scheme *scheme,
91 int unlock)
92{
93 MARIA_CRYPT_DATA *crypt_data = (MARIA_CRYPT_DATA*)scheme;
94 if (unlock)
95 mysql_mutex_unlock(&crypt_data->lock);
96 else
97 mysql_mutex_lock(&crypt_data->lock);
98}
99
100int
101ma_crypt_create(MARIA_SHARE* share)
102{
103 MARIA_CRYPT_DATA *crypt_data=
104 (MARIA_CRYPT_DATA*)my_malloc(sizeof(MARIA_CRYPT_DATA), MYF(MY_ZEROFILL));
105 crypt_data->scheme.type= CRYPT_SCHEME_1;
106 crypt_data->scheme.locker= crypt_data_scheme_locker;
107 mysql_mutex_init(key_CRYPT_DATA_lock, &crypt_data->lock, MY_MUTEX_INIT_FAST);
108 crypt_data->scheme.key_id= get_encryption_key_id(share);
109 my_random_bytes(crypt_data->scheme.iv, sizeof(crypt_data->scheme.iv));
110 my_random_bytes((uchar*)&crypt_data->space, sizeof(crypt_data->space));
111 share->crypt_data= crypt_data;
112 share->crypt_page_header_space= CRYPT_SCHEME_1_KEY_VERSION_SIZE;
113 return 0;
114}
115
116void
117ma_crypt_free(MARIA_SHARE* share)
118{
119 if (share->crypt_data != NULL)
120 {
121 mysql_mutex_destroy(&share->crypt_data->lock);
122 my_free(share->crypt_data);
123 share->crypt_data= NULL;
124 }
125}
126
127int
128ma_crypt_write(MARIA_SHARE* share, File file)
129{
130 MARIA_CRYPT_DATA *crypt_data= share->crypt_data;
131 uchar buff[2 + 4 + sizeof(crypt_data->scheme.iv)];
132 if (crypt_data == 0)
133 return 0;
134
135 buff[0]= crypt_data->scheme.type;
136 buff[1]= sizeof(buff) - 2;
137
138 int4store(buff + 2, crypt_data->space);
139 memcpy(buff + 6, crypt_data->scheme.iv, sizeof(crypt_data->scheme.iv));
140
141 if (mysql_file_write(file, buff, sizeof(buff), MYF(MY_NABP)))
142 return 1;
143
144 return 0;
145}
146
147uchar*
148ma_crypt_read(MARIA_SHARE* share, uchar *buff)
149{
150 uchar type= buff[0];
151 uchar iv_length= buff[1];
152
153 /* currently only supported type */
154 if (type != CRYPT_SCHEME_1 ||
155 iv_length != sizeof(((MARIA_CRYPT_DATA*)1)->scheme.iv) + 4)
156 {
157 my_printf_error(HA_ERR_UNSUPPORTED,
158 "Unsupported crypt scheme! type: %d iv_length: %d\n",
159 MYF(ME_FATALERROR|ME_NOREFRESH),
160 type, iv_length);
161 return 0;
162 }
163
164 if (share->crypt_data == NULL)
165 {
166 /* opening a table */
167 MARIA_CRYPT_DATA *crypt_data=
168 (MARIA_CRYPT_DATA*)my_malloc(sizeof(MARIA_CRYPT_DATA), MYF(MY_ZEROFILL));
169
170 crypt_data->scheme.type= type;
171 mysql_mutex_init(key_CRYPT_DATA_lock, &crypt_data->lock,
172 MY_MUTEX_INIT_FAST);
173 crypt_data->scheme.locker= crypt_data_scheme_locker;
174 crypt_data->scheme.key_id= get_encryption_key_id(share);
175 crypt_data->space= uint4korr(buff + 2);
176 memcpy(crypt_data->scheme.iv, buff + 6, sizeof(crypt_data->scheme.iv));
177 share->crypt_data= crypt_data;
178 }
179
180 share->crypt_page_header_space= CRYPT_SCHEME_1_KEY_VERSION_SIZE;
181 return buff + 2 + iv_length;
182}
183
184static int ma_encrypt(MARIA_SHARE *, MARIA_CRYPT_DATA *, const uchar *,
185 uchar *, uint, uint, LSN, uint *);
186static int ma_decrypt(MARIA_SHARE *, MARIA_CRYPT_DATA *, const uchar *,
187 uchar *, uint, uint, LSN, uint);
188
189static my_bool ma_crypt_pre_read_hook(PAGECACHE_IO_HOOK_ARGS *args)
190{
191 MARIA_SHARE *share= (MARIA_SHARE*) args->data;
192 uchar *crypt_buf= my_malloc(share->block_size, MYF(0));
193 if (crypt_buf == NULL)
194 {
195 args->crypt_buf= NULL; /* for post-hook */
196 return 1;
197 }
198
199 /* swap pointers to read into crypt_buf */
200 args->crypt_buf= args->page;
201 args->page= crypt_buf;
202
203 return 0;
204}
205
206static my_bool ma_crypt_data_post_read_hook(int res,
207 PAGECACHE_IO_HOOK_ARGS *args)
208{
209 MARIA_SHARE *share= (MARIA_SHARE*) args->data;
210 const uint size= share->block_size;
211 const uchar page_type= args->page[PAGE_TYPE_OFFSET] & PAGE_TYPE_MASK;
212 const uint32 key_version_offset= (page_type <= TAIL_PAGE) ?
213 KEY_VERSION_OFFSET : FULL_PAGE_KEY_VERSION_OFFSET;
214
215 if (res == 0)
216 {
217 const uchar *src= args->page;
218 uchar* dst= args->crypt_buf;
219 uint pageno= (uint)args->pageno;
220 LSN lsn= lsn_korr(src);
221 const uint head= (page_type <= TAIL_PAGE) ?
222 PAGE_HEADER_SIZE(share) : FULL_PAGE_HEADER_SIZE(share);
223 const uint tail= CRC_SIZE;
224 const uint32 key_version= uint4korr(src + key_version_offset);
225
226 /* 1 - copy head */
227 memcpy(dst, src, head);
228 /* 2 - decrypt page */
229 res= ma_decrypt(share, share->crypt_data,
230 src + head, dst + head, size - (head + tail), pageno, lsn,
231 key_version);
232 /* 3 - copy tail */
233 memcpy(dst + size - tail, src + size - tail, tail);
234 /* 4 clear key version to get correct crc */
235 int4store(dst + key_version_offset, 0);
236 }
237
238 if (args->crypt_buf != NULL)
239 {
240 uchar *tmp= args->page;
241 args->page= args->crypt_buf;
242 args->crypt_buf= NULL;
243 my_free(tmp);
244 }
245
246 return maria_page_crc_check_data(res, args);
247}
248
249static void store_rand_lsn(uchar * page)
250{
251 LSN lsn= 0;
252 lsn+= rand();
253 lsn<<= 32;
254 lsn+= rand();
255 lsn_store(page, lsn);
256}
257
258static my_bool ma_crypt_data_pre_write_hook(PAGECACHE_IO_HOOK_ARGS *args)
259{
260 MARIA_SHARE *share= (MARIA_SHARE*) args->data;
261 const uint size= share->block_size;
262 uint key_version;
263 uchar *crypt_buf= my_malloc(share->block_size, MYF(0));
264
265 if (crypt_buf == NULL)
266 {
267 args->crypt_buf= NULL; /* for post-hook */
268 return 1;
269 }
270
271 if (!share->now_transactional)
272 {
273 /* store a random number instead of LSN (for counter block) */
274 store_rand_lsn(args->page);
275 }
276
277 maria_page_crc_set_normal(args);
278
279 {
280 const uchar *src= args->page;
281 uchar* dst= crypt_buf;
282 uint pageno= (uint)args->pageno;
283 LSN lsn= lsn_korr(src);
284 const uchar page_type= src[PAGE_TYPE_OFFSET] & PAGE_TYPE_MASK;
285 const uint head= (page_type <= TAIL_PAGE) ?
286 PAGE_HEADER_SIZE(share) : FULL_PAGE_HEADER_SIZE(share);
287 const uint tail= CRC_SIZE;
288 const uint32 key_version_offset= (page_type <= TAIL_PAGE) ?
289 KEY_VERSION_OFFSET : FULL_PAGE_KEY_VERSION_OFFSET;
290
291 DBUG_ASSERT(page_type < MAX_PAGE_TYPE);
292
293 /* 1 - copy head */
294 memcpy(dst, src, head);
295 /* 2 - encrypt page */
296 if (ma_encrypt(share, share->crypt_data,
297 src + head, dst + head, size - (head + tail), pageno, lsn,
298 &key_version))
299 return 1;
300 /* 3 - copy tail */
301 memcpy(dst + size - tail, src + size - tail, tail);
302 /* 4 - store key version */
303 int4store(dst + key_version_offset, key_version);
304 }
305
306 /* swap pointers to instead write out the encrypted block */
307 args->crypt_buf= args->page;
308 args->page= crypt_buf;
309
310 return 0;
311}
312
313static void ma_crypt_post_write_hook(int res,
314 PAGECACHE_IO_HOOK_ARGS *args)
315{
316 if (args->crypt_buf != NULL)
317 {
318 uchar *tmp= args->page;
319 args->page= args->crypt_buf;
320 args->crypt_buf= NULL;
321 my_free(tmp);
322 }
323
324 maria_page_write_failure(res, args);
325}
326
327void ma_crypt_set_data_pagecache_callbacks(PAGECACHE_FILE *file,
328 MARIA_SHARE *share
329 __attribute__((unused)))
330{
331 /* Only use encryption if we have defined it */
332 if (encryption_key_id_exists(get_encryption_key_id(share)))
333 {
334 file->pre_read_hook= ma_crypt_pre_read_hook;
335 file->post_read_hook= ma_crypt_data_post_read_hook;
336 file->pre_write_hook= ma_crypt_data_pre_write_hook;
337 file->post_write_hook= ma_crypt_post_write_hook;
338 }
339}
340
341static my_bool ma_crypt_index_post_read_hook(int res,
342 PAGECACHE_IO_HOOK_ARGS *args)
343{
344 MARIA_SHARE *share= (MARIA_SHARE*) args->data;
345 const uint block_size= share->block_size;
346 const uint page_used= _ma_get_page_used(share, args->page);
347
348 if (res == 0 && page_used <= block_size - CRC_SIZE)
349 {
350 const uchar *src= args->page;
351 uchar* dst= args->crypt_buf;
352 uint pageno= (uint)args->pageno;
353 LSN lsn= lsn_korr(src);
354 const uint head= share->keypage_header;
355 const uint tail= CRC_SIZE;
356 const uint32 key_version= _ma_get_key_version(share, src);
357 /* page_used includes header (but not trailer) */
358 const uint size= page_used - head;
359
360 /* 1 - copy head */
361 memcpy(dst, src, head);
362 /* 2 - decrypt page */
363 res= ma_decrypt(share, share->crypt_data,
364 src + head, dst + head, size, pageno, lsn, key_version);
365 /* 3 - copy tail */
366 memcpy(dst + block_size - tail, src + block_size - tail, tail);
367 /* 4 clear key version to get correct crc */
368 _ma_store_key_version(share, dst, 0);
369 }
370
371 if (args->crypt_buf != NULL)
372 {
373 uchar *tmp= args->page;
374 args->page= args->crypt_buf;
375 args->crypt_buf= NULL;
376 my_free(tmp);
377 }
378
379 return maria_page_crc_check_index(res, args);
380}
381
382static my_bool ma_crypt_index_pre_write_hook(PAGECACHE_IO_HOOK_ARGS *args)
383{
384 MARIA_SHARE *share= (MARIA_SHARE*) args->data;
385 const uint block_size= share->block_size;
386 const uint page_used= _ma_get_page_used(share, args->page);
387 uint key_version;
388 uchar *crypt_buf= my_malloc(block_size, MYF(0));
389 if (crypt_buf == NULL)
390 {
391 args->crypt_buf= NULL; /* for post-hook */
392 return 1;
393 }
394
395 if (!share->now_transactional)
396 {
397 /* store a random number instead of LSN (for counter block) */
398 store_rand_lsn(args->page);
399 }
400
401 maria_page_crc_set_index(args);
402
403 {
404 const uchar *src= args->page;
405 uchar* dst= crypt_buf;
406 uint pageno= (uint)args->pageno;
407 LSN lsn= lsn_korr(src);
408 const uint head= share->keypage_header;
409 const uint tail= CRC_SIZE;
410 /* page_used includes header (but not trailer) */
411 const uint size= page_used - head;
412
413 /* 1 - copy head */
414 memcpy(dst, src, head);
415 /* 2 - encrypt page */
416 if (ma_encrypt(share, share->crypt_data,
417 src + head, dst + head, size, pageno, lsn, &key_version))
418 {
419 my_free(crypt_buf);
420 return 1;
421 }
422 /* 3 - copy tail */
423 memcpy(dst + block_size - tail, src + block_size - tail, tail);
424 /* 4 - store key version */
425 _ma_store_key_version(share, dst, key_version);
426#ifdef HAVE_valgrind
427 /* 5 - keep valgrind happy by zeroing not used bytes */
428 bzero(dst+head+size, block_size - size - tail - head);
429#endif
430 }
431
432 /* swap pointers to instead write out the encrypted block */
433 args->crypt_buf= args->page;
434 args->page= crypt_buf;
435
436 return 0;
437}
438
439void ma_crypt_set_index_pagecache_callbacks(PAGECACHE_FILE *file,
440 MARIA_SHARE *share
441 __attribute__((unused)))
442{
443 file->pre_read_hook= ma_crypt_pre_read_hook;
444 file->post_read_hook= ma_crypt_index_post_read_hook;
445 file->pre_write_hook= ma_crypt_index_pre_write_hook;
446 file->post_write_hook= ma_crypt_post_write_hook;
447}
448
449static int ma_encrypt(MARIA_SHARE *share, MARIA_CRYPT_DATA *crypt_data,
450 const uchar *src, uchar *dst, uint size,
451 uint pageno, LSN lsn,
452 uint *key_version)
453{
454 int rc;
455 uint32 dstlen= 0; /* Must be set because of error message */
456
457 *key_version = encryption_key_get_latest_version(crypt_data->scheme.key_id);
458 if (*key_version == ENCRYPTION_KEY_VERSION_INVALID)
459 {
460 /*
461 We use this error for both encryption and decryption, as in normal
462 cases it should be impossible to get an error here.
463 */
464 my_errno= HA_ERR_DECRYPTION_FAILED;
465 my_printf_error(HA_ERR_DECRYPTION_FAILED,
466 "Unknown key id %u. Can't continue!",
467 MYF(ME_FATALERROR|ME_NOREFRESH),
468 crypt_data->scheme.key_id);
469 return 1;
470 }
471
472 rc= encryption_scheme_encrypt(src, size, dst, &dstlen,
473 &crypt_data->scheme, *key_version,
474 crypt_data->space, pageno, lsn);
475
476 /* The following can only fail if the encryption key is wrong */
477 DBUG_ASSERT(!my_assert_on_error || rc == MY_AES_OK);
478 DBUG_ASSERT(!my_assert_on_error || dstlen == size);
479 if (! (rc == MY_AES_OK && dstlen == size))
480 {
481 my_errno= HA_ERR_DECRYPTION_FAILED;
482 my_printf_error(HA_ERR_DECRYPTION_FAILED,
483 "failed to encrypt '%s' rc: %d dstlen: %u size: %u\n",
484 MYF(ME_FATALERROR|ME_NOREFRESH),
485 share->open_file_name.str, rc, dstlen, size);
486 return 1;
487 }
488
489 return 0;
490}
491
492static int ma_decrypt(MARIA_SHARE *share, MARIA_CRYPT_DATA *crypt_data,
493 const uchar *src, uchar *dst, uint size,
494 uint pageno, LSN lsn,
495 uint key_version)
496{
497 int rc;
498 uint32 dstlen= 0; /* Must be set because of error message */
499
500 rc= encryption_scheme_decrypt(src, size, dst, &dstlen,
501 &crypt_data->scheme, key_version,
502 crypt_data->space, pageno, lsn);
503
504 DBUG_ASSERT(!my_assert_on_error || rc == MY_AES_OK);
505 DBUG_ASSERT(!my_assert_on_error || dstlen == size);
506 if (! (rc == MY_AES_OK && dstlen == size))
507 {
508 my_errno= HA_ERR_DECRYPTION_FAILED;
509 my_printf_error(HA_ERR_DECRYPTION_FAILED,
510 "failed to decrypt '%s' rc: %d dstlen: %u size: %u\n",
511 MYF(ME_FATALERROR|ME_NOREFRESH),
512 share->open_file_name.str, rc, dstlen, size);
513 return 1;
514 }
515 return 0;
516}
517