]>
Commit | Line | Data |
---|---|---|
f003a8c5 VS |
1 | /* |
2 | * GRUB -- GRand Unified Bootloader | |
3 | * Copyright (C) 2011 Free Software Foundation, Inc. | |
4 | * | |
5 | * GRUB 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; either version 3 of the License, or | |
8 | * (at your option) any later version. | |
9 | * | |
10 | * GRUB is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with GRUB. If not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include <grub/err.h> | |
20 | #include <grub/file.h> | |
21 | #include <grub/mm.h> | |
22 | #include <grub/misc.h> | |
23 | #include <grub/disk.h> | |
24 | #include <grub/partition.h> | |
d84e89f4 | 25 | #include <grub/safemath.h> |
f003a8c5 VS |
26 | #include <grub/dl.h> |
27 | #include <grub/types.h> | |
28 | #include <grub/zfs/zfs.h> | |
29 | #include <grub/zfs/zio.h> | |
30 | #include <grub/zfs/dnode.h> | |
31 | #include <grub/zfs/uberblock_impl.h> | |
32 | #include <grub/zfs/vdev_impl.h> | |
33 | #include <grub/zfs/zio_checksum.h> | |
34 | #include <grub/zfs/zap_impl.h> | |
35 | #include <grub/zfs/zap_leaf.h> | |
36 | #include <grub/zfs/zfs_znode.h> | |
37 | #include <grub/zfs/dmu.h> | |
38 | #include <grub/zfs/dmu_objset.h> | |
39 | #include <grub/zfs/sa_impl.h> | |
40 | #include <grub/zfs/dsl_dir.h> | |
41 | #include <grub/zfs/dsl_dataset.h> | |
42 | #include <grub/crypto.h> | |
43 | #include <grub/extcmd.h> | |
44 | #include <grub/i18n.h> | |
45 | ||
46 | GRUB_MOD_LICENSE ("GPLv3+"); | |
47 | ||
2f53a9ed VS |
48 | /* |
49 | Mostly based on following article: | |
50 | https://blogs.oracle.com/darren/entry/zfs_encryption_what_is_on | |
51 | */ | |
52 | ||
f003a8c5 VS |
53 | enum grub_zfs_algo |
54 | { | |
55 | GRUB_ZFS_ALGO_CCM, | |
56 | GRUB_ZFS_ALGO_GCM, | |
57 | }; | |
58 | ||
59 | struct grub_zfs_key | |
60 | { | |
61 | grub_uint64_t algo; | |
62 | grub_uint8_t enc_nonce[13]; | |
63 | grub_uint8_t unused[3]; | |
64 | grub_uint8_t enc_key[48]; | |
65 | grub_uint8_t unknown_purpose_nonce[13]; | |
66 | grub_uint8_t unused2[3]; | |
67 | grub_uint8_t unknown_purpose_key[48]; | |
68 | }; | |
69 | ||
70 | struct grub_zfs_wrap_key | |
71 | { | |
72 | struct grub_zfs_wrap_key *next; | |
ed746949 VS |
73 | grub_size_t keylen; |
74 | int is_passphrase; | |
75 | grub_uint64_t key[0]; | |
f003a8c5 VS |
76 | }; |
77 | ||
78 | static struct grub_zfs_wrap_key *zfs_wrap_keys; | |
79 | ||
80 | grub_err_t | |
ed746949 VS |
81 | grub_zfs_add_key (grub_uint8_t *key_in, |
82 | grub_size_t keylen, | |
83 | int passphrase) | |
f003a8c5 VS |
84 | { |
85 | struct grub_zfs_wrap_key *key; | |
d84e89f4 PJ |
86 | grub_size_t sz; |
87 | ||
ed746949 VS |
88 | if (!passphrase && keylen > 32) |
89 | keylen = 32; | |
d84e89f4 PJ |
90 | if (grub_add (sizeof (*key), keylen, &sz)) |
91 | return GRUB_ERR_OUT_OF_RANGE; | |
92 | key = grub_malloc (sz); | |
f003a8c5 VS |
93 | if (!key) |
94 | return grub_errno; | |
ed746949 VS |
95 | key->is_passphrase = passphrase; |
96 | key->keylen = keylen; | |
97 | grub_memcpy (key->key, key_in, keylen); | |
f003a8c5 VS |
98 | key->next = zfs_wrap_keys; |
99 | zfs_wrap_keys = key; | |
100 | return GRUB_ERR_NONE; | |
101 | } | |
102 | ||
4a19b601 | 103 | static gcry_err_code_t |
f003a8c5 VS |
104 | grub_ccm_decrypt (grub_crypto_cipher_handle_t cipher, |
105 | grub_uint8_t *out, const grub_uint8_t *in, | |
106 | grub_size_t psize, | |
107 | void *mac_out, const void *nonce, | |
108 | unsigned l, unsigned m) | |
109 | { | |
110 | grub_uint8_t iv[16]; | |
111 | grub_uint8_t mul[16]; | |
112 | grub_uint32_t mac[4]; | |
113 | unsigned i, j; | |
4a19b601 | 114 | gcry_err_code_t err; |
f003a8c5 VS |
115 | |
116 | grub_memcpy (iv + 1, nonce, 15 - l); | |
117 | ||
118 | iv[0] = (l - 1) | (((m-2) / 2) << 3); | |
119 | for (j = 0; j < l; j++) | |
120 | iv[15 - j] = psize >> (8 * j); | |
121 | err = grub_crypto_ecb_encrypt (cipher, mac, iv, 16); | |
122 | if (err) | |
123 | return err; | |
124 | ||
125 | iv[0] = l - 1; | |
126 | ||
127 | for (i = 0; i < (psize + 15) / 16; i++) | |
128 | { | |
129 | grub_size_t csize; | |
130 | csize = 16; | |
131 | if (csize > psize - 16 * i) | |
132 | csize = psize - 16 * i; | |
133 | for (j = 0; j < l; j++) | |
134 | iv[15 - j] = (i + 1) >> (8 * j); | |
135 | err = grub_crypto_ecb_encrypt (cipher, mul, iv, 16); | |
136 | if (err) | |
137 | return err; | |
138 | grub_crypto_xor (out + 16 * i, in + 16 * i, mul, csize); | |
139 | grub_crypto_xor (mac, mac, out + 16 * i, csize); | |
140 | err = grub_crypto_ecb_encrypt (cipher, mac, mac, 16); | |
141 | if (err) | |
142 | return err; | |
143 | } | |
144 | for (j = 0; j < l; j++) | |
145 | iv[15 - j] = 0; | |
146 | err = grub_crypto_ecb_encrypt (cipher, mul, iv, 16); | |
147 | if (err) | |
148 | return err; | |
149 | if (mac_out) | |
150 | grub_crypto_xor (mac_out, mac, mul, m); | |
8b66bb5d | 151 | return GPG_ERR_NO_ERROR; |
f003a8c5 VS |
152 | } |
153 | ||
bc1de0bc VS |
154 | static void |
155 | grub_gcm_mul_x (grub_uint8_t *a) | |
156 | { | |
157 | int i; | |
158 | int c = 0, d = 0; | |
159 | for (i = 0; i < 16; i++) | |
160 | { | |
161 | c = a[i] & 0x1; | |
162 | a[i] = (a[i] >> 1) | (d << 7); | |
163 | d = c; | |
164 | } | |
165 | if (d) | |
166 | a[0] ^= 0xe1; | |
167 | } | |
168 | ||
169 | static void | |
170 | grub_gcm_mul (grub_uint8_t *a, const grub_uint8_t *b) | |
171 | { | |
172 | grub_uint8_t res[16], bs[16]; | |
173 | int i; | |
174 | grub_memcpy (bs, b, 16); | |
175 | grub_memset (res, 0, 16); | |
176 | for (i = 0; i < 128; i++) | |
177 | { | |
178 | if ((a[i / 8] << (i % 8)) & 0x80) | |
179 | grub_crypto_xor (res, res, bs, 16); | |
180 | grub_gcm_mul_x (bs); | |
181 | } | |
182 | ||
183 | grub_memcpy (a, res, 16); | |
184 | } | |
185 | ||
4a19b601 | 186 | static gcry_err_code_t |
bc1de0bc VS |
187 | grub_gcm_decrypt (grub_crypto_cipher_handle_t cipher, |
188 | grub_uint8_t *out, const grub_uint8_t *in, | |
189 | grub_size_t psize, | |
190 | void *mac_out, const void *nonce, | |
191 | unsigned nonce_len, unsigned m) | |
192 | { | |
193 | grub_uint8_t iv[16]; | |
194 | grub_uint8_t mul[16]; | |
195 | grub_uint8_t mac[16], h[16], mac_xor[16]; | |
196 | unsigned i, j; | |
4a19b601 | 197 | gcry_err_code_t err; |
bc1de0bc VS |
198 | |
199 | grub_memset (mac, 0, sizeof (mac)); | |
200 | ||
201 | err = grub_crypto_ecb_encrypt (cipher, h, mac, 16); | |
202 | if (err) | |
203 | return err; | |
204 | ||
205 | if (nonce_len == 12) | |
206 | { | |
207 | grub_memcpy (iv, nonce, 12); | |
208 | iv[12] = 0; | |
209 | iv[13] = 0; | |
210 | iv[14] = 0; | |
211 | iv[15] = 1; | |
212 | } | |
213 | else | |
214 | { | |
215 | grub_memset (iv, 0, sizeof (iv)); | |
216 | grub_memcpy (iv, nonce, nonce_len); | |
217 | grub_gcm_mul (iv, h); | |
218 | iv[15] ^= nonce_len * 8; | |
219 | grub_gcm_mul (iv, h); | |
220 | } | |
221 | ||
222 | err = grub_crypto_ecb_encrypt (cipher, mac_xor, iv, 16); | |
223 | if (err) | |
224 | return err; | |
225 | ||
226 | for (i = 0; i < (psize + 15) / 16; i++) | |
227 | { | |
228 | grub_size_t csize; | |
229 | csize = 16; | |
230 | if (csize > psize - 16 * i) | |
231 | csize = psize - 16 * i; | |
232 | for (j = 0; j < 4; j++) | |
233 | { | |
234 | iv[15 - j]++; | |
235 | if (iv[15 - j] != 0) | |
236 | break; | |
237 | } | |
238 | grub_crypto_xor (mac, mac, in + 16 * i, csize); | |
239 | grub_gcm_mul (mac, h); | |
240 | err = grub_crypto_ecb_encrypt (cipher, mul, iv, 16); | |
241 | if (err) | |
242 | return err; | |
243 | grub_crypto_xor (out + 16 * i, in + 16 * i, mul, csize); | |
244 | } | |
245 | for (j = 0; j < 8; j++) | |
916733ea | 246 | mac[15 - j] ^= ((((grub_uint64_t) psize) * 8) >> (8 * j)); |
bc1de0bc VS |
247 | grub_gcm_mul (mac, h); |
248 | ||
249 | if (mac_out) | |
250 | grub_crypto_xor (mac_out, mac, mac_xor, m); | |
251 | ||
8b66bb5d | 252 | return GPG_ERR_NO_ERROR; |
bc1de0bc VS |
253 | } |
254 | ||
255 | ||
4a19b601 | 256 | static gcry_err_code_t |
bc1de0bc VS |
257 | algo_decrypt (grub_crypto_cipher_handle_t cipher, grub_uint64_t algo, |
258 | grub_uint8_t *out, const grub_uint8_t *in, | |
259 | grub_size_t psize, | |
260 | void *mac_out, const void *nonce, | |
261 | unsigned l, unsigned m) | |
262 | { | |
263 | switch (algo) | |
264 | { | |
265 | case 0: | |
8b66bb5d VS |
266 | return grub_ccm_decrypt (cipher, out, in, psize, |
267 | mac_out, nonce, l, m); | |
bc1de0bc | 268 | case 1: |
8b66bb5d VS |
269 | return grub_gcm_decrypt (cipher, out, in, psize, |
270 | mac_out, nonce, | |
bc1de0bc VS |
271 | 15 - l, m); |
272 | default: | |
8b66bb5d | 273 | return GPG_ERR_CIPHER_ALGO; |
bc1de0bc VS |
274 | } |
275 | } | |
276 | ||
f003a8c5 | 277 | static grub_err_t |
bc1de0bc VS |
278 | grub_zfs_decrypt_real (grub_crypto_cipher_handle_t cipher, |
279 | grub_uint64_t algo, | |
280 | void *nonce, | |
f003a8c5 VS |
281 | char *buf, grub_size_t size, |
282 | const grub_uint32_t *expected_mac, | |
283 | grub_zfs_endian_t endian) | |
284 | { | |
285 | grub_uint32_t mac[4]; | |
286 | unsigned i; | |
287 | grub_uint32_t sw[4]; | |
8b66bb5d | 288 | gcry_err_code_t err; |
f003a8c5 VS |
289 | |
290 | grub_memcpy (sw, nonce, 16); | |
3ae17eb8 VS |
291 | if (endian != GRUB_ZFS_BIG_ENDIAN) |
292 | for (i = 0; i < 4; i++) | |
293 | sw[i] = grub_swap_bytes32 (sw[i]); | |
f003a8c5 VS |
294 | |
295 | if (!cipher) | |
296 | return grub_error (GRUB_ERR_ACCESS_DENIED, | |
d61386e2 | 297 | N_("no decryption key available")); |
bc1de0bc VS |
298 | err = algo_decrypt (cipher, algo, |
299 | (grub_uint8_t *) buf, | |
300 | (grub_uint8_t *) buf, | |
301 | size, mac, | |
302 | sw + 1, 3, 12); | |
f003a8c5 | 303 | if (err) |
8b66bb5d | 304 | return grub_crypto_gcry_error (err); |
f003a8c5 VS |
305 | |
306 | for (i = 0; i < 3; i++) | |
307 | if (grub_zfs_to_cpu32 (expected_mac[i], endian) | |
308 | != grub_be_to_cpu32 (mac[i])) | |
d61386e2 | 309 | return grub_error (GRUB_ERR_BAD_FS, N_("MAC verification failed")); |
f003a8c5 VS |
310 | return GRUB_ERR_NONE; |
311 | } | |
312 | ||
313 | static grub_crypto_cipher_handle_t | |
314 | grub_zfs_load_key_real (const struct grub_zfs_key *key, | |
ed746949 | 315 | grub_size_t keysize, |
bc1de0bc VS |
316 | grub_uint64_t salt, |
317 | grub_uint64_t algo) | |
f003a8c5 VS |
318 | { |
319 | unsigned keylen; | |
320 | struct grub_zfs_wrap_key *wrap_key; | |
321 | grub_crypto_cipher_handle_t ret = NULL; | |
f003a8c5 VS |
322 | |
323 | if (keysize != sizeof (*key)) | |
324 | { | |
325 | grub_dprintf ("zfs", "Unexpected key size %" PRIuGRUB_SIZE "\n", keysize); | |
326 | return 0; | |
327 | } | |
328 | ||
329 | if (grub_memcmp (key->enc_key + 32, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) | |
330 | == 0) | |
331 | keylen = 16; | |
332 | else if (grub_memcmp (key->enc_key + 40, "\0\0\0\0\0\0\0\0", 8) == 0) | |
333 | keylen = 24; | |
334 | else | |
335 | keylen = 32; | |
336 | ||
337 | for (wrap_key = zfs_wrap_keys; wrap_key; wrap_key = wrap_key->next) | |
338 | { | |
339 | grub_crypto_cipher_handle_t cipher; | |
ed746949 | 340 | grub_uint8_t decrypted[32], mac[32], wrap_key_real[32]; |
4a19b601 | 341 | gcry_err_code_t err; |
f003a8c5 VS |
342 | cipher = grub_crypto_cipher_open (GRUB_CIPHER_AES); |
343 | if (!cipher) | |
344 | { | |
345 | grub_errno = GRUB_ERR_NONE; | |
346 | return 0; | |
347 | } | |
ed746949 | 348 | grub_memset (wrap_key_real, 0, sizeof (wrap_key_real)); |
4a19b601 | 349 | err = 0; |
ed746949 VS |
350 | if (!wrap_key->is_passphrase) |
351 | grub_memcpy(wrap_key_real, wrap_key->key, | |
352 | wrap_key->keylen < keylen ? wrap_key->keylen : keylen); | |
353 | else | |
4a19b601 VS |
354 | err = grub_crypto_pbkdf2 (GRUB_MD_SHA1, |
355 | (const grub_uint8_t *) wrap_key->key, | |
356 | wrap_key->keylen, | |
357 | (const grub_uint8_t *) &salt, sizeof (salt), | |
358 | 1000, wrap_key_real, keylen); | |
359 | if (err) | |
360 | { | |
361 | grub_errno = GRUB_ERR_NONE; | |
fa13e605 | 362 | grub_crypto_cipher_close (cipher); |
4a19b601 VS |
363 | continue; |
364 | } | |
ed746949 VS |
365 | |
366 | err = grub_crypto_cipher_set_key (cipher, wrap_key_real, | |
f003a8c5 VS |
367 | keylen); |
368 | if (err) | |
369 | { | |
370 | grub_errno = GRUB_ERR_NONE; | |
fa13e605 | 371 | grub_crypto_cipher_close (cipher); |
f003a8c5 VS |
372 | continue; |
373 | } | |
374 | ||
bc1de0bc VS |
375 | err = algo_decrypt (cipher, algo, decrypted, key->unknown_purpose_key, 32, |
376 | mac, key->unknown_purpose_nonce, 2, 16); | |
f003a8c5 VS |
377 | if (err || (grub_crypto_memcmp (mac, key->unknown_purpose_key + 32, 16) |
378 | != 0)) | |
379 | { | |
380 | grub_dprintf ("zfs", "key loading failed\n"); | |
381 | grub_errno = GRUB_ERR_NONE; | |
fa13e605 | 382 | grub_crypto_cipher_close (cipher); |
f003a8c5 VS |
383 | continue; |
384 | } | |
385 | ||
bc1de0bc VS |
386 | err = algo_decrypt (cipher, algo, decrypted, key->enc_key, keylen, mac, |
387 | key->enc_nonce, 2, 16); | |
f003a8c5 VS |
388 | if (err || grub_crypto_memcmp (mac, key->enc_key + keylen, 16) != 0) |
389 | { | |
390 | grub_dprintf ("zfs", "key loading failed\n"); | |
391 | grub_errno = GRUB_ERR_NONE; | |
fa13e605 | 392 | grub_crypto_cipher_close (cipher); |
f003a8c5 VS |
393 | continue; |
394 | } | |
395 | ret = grub_crypto_cipher_open (GRUB_CIPHER_AES); | |
396 | if (!ret) | |
397 | { | |
398 | grub_errno = GRUB_ERR_NONE; | |
fa13e605 | 399 | grub_crypto_cipher_close (cipher); |
f003a8c5 VS |
400 | continue; |
401 | } | |
402 | err = grub_crypto_cipher_set_key (ret, decrypted, keylen); | |
403 | if (err) | |
404 | { | |
3900726f AB |
405 | grub_errno = GRUB_ERR_NONE; |
406 | grub_crypto_cipher_close (ret); | |
407 | grub_crypto_cipher_close (cipher); | |
408 | continue; | |
409 | } | |
fa13e605 | 410 | grub_crypto_cipher_close (cipher); |
f003a8c5 VS |
411 | return ret; |
412 | } | |
413 | return NULL; | |
414 | } | |
415 | ||
416 | static const struct grub_arg_option options[] = | |
417 | { | |
418 | {"raw", 'r', 0, N_("Assume input is raw."), 0, 0}, | |
419 | {"hex", 'h', 0, N_("Assume input is hex."), 0, 0}, | |
420 | {"passphrase", 'p', 0, N_("Assume input is passphrase."), 0, 0}, | |
421 | {0, 0, 0, 0, 0, 0} | |
422 | }; | |
423 | ||
424 | static grub_err_t | |
425 | grub_cmd_zfs_key (grub_extcmd_context_t ctxt, int argc, char **args) | |
426 | { | |
427 | grub_uint8_t buf[1024]; | |
428 | grub_ssize_t real_size; | |
429 | ||
430 | if (argc > 0) | |
431 | { | |
432 | grub_file_t file; | |
ca0a4f68 | 433 | file = grub_file_open (args[0], GRUB_FILE_TYPE_ZFS_ENCRYPTION_KEY); |
f003a8c5 VS |
434 | if (!file) |
435 | return grub_errno; | |
436 | real_size = grub_file_read (file, buf, 1024); | |
437 | if (real_size < 0) | |
438 | return grub_errno; | |
439 | } | |
ed746949 | 440 | else |
f003a8c5 | 441 | { |
6e0632e2 | 442 | grub_xputs (_("Enter ZFS password: ")); |
ed746949 VS |
443 | if (!grub_password_get ((char *) buf, 1023)) |
444 | return grub_errno; | |
445 | real_size = grub_strlen ((char *) buf); | |
f003a8c5 VS |
446 | } |
447 | ||
448 | if (ctxt->state[1].set) | |
449 | { | |
450 | int i; | |
451 | grub_err_t err; | |
ed746949 | 452 | for (i = 0; i < real_size / 2; i++) |
f003a8c5 VS |
453 | { |
454 | char c1 = grub_tolower (buf[2 * i]) - '0'; | |
455 | char c2 = grub_tolower (buf[2 * i + 1]) - '0'; | |
456 | if (c1 > 9) | |
457 | c1 += '0' - 'a' + 10; | |
458 | if (c2 > 9) | |
459 | c2 += '0' - 'a' + 10; | |
460 | buf[i] = (c1 << 4) | c2; | |
461 | } | |
ed746949 | 462 | err = grub_zfs_add_key (buf, real_size / 2, 0); |
f003a8c5 VS |
463 | if (err) |
464 | return err; | |
465 | return GRUB_ERR_NONE; | |
466 | } | |
ed746949 VS |
467 | |
468 | return grub_zfs_add_key (buf, real_size, | |
469 | ctxt->state[2].set | |
470 | || (argc == 0 && !ctxt->state[0].set | |
471 | && !ctxt->state[1].set)); | |
f003a8c5 VS |
472 | } |
473 | ||
474 | static grub_extcmd_t cmd_key; | |
475 | ||
e2d22baf | 476 | GRUB_MOD_INIT(zfscrypt) |
f003a8c5 VS |
477 | { |
478 | grub_zfs_decrypt = grub_zfs_decrypt_real; | |
479 | grub_zfs_load_key = grub_zfs_load_key_real; | |
480 | cmd_key = grub_register_extcmd ("zfskey", grub_cmd_zfs_key, 0, | |
6e0632e2 VS |
481 | N_("[-h|-p|-r] [FILE]"), |
482 | N_("Import ZFS wrapping key stored in FILE."), | |
f003a8c5 VS |
483 | options); |
484 | } | |
485 | ||
e2d22baf | 486 | GRUB_MOD_FINI(zfscrypt) |
f003a8c5 VS |
487 | { |
488 | grub_zfs_decrypt = 0; | |
489 | grub_zfs_load_key = 0; | |
490 | grub_unregister_extcmd (cmd_key); | |
491 | } |