Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / disk / geli.c
blobf9315df0de5872ae0cb658d3c4c87cdd65e3303d
1 /*
2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2003,2007,2010,2011 Free Software Foundation, Inc.
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.
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.
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/>.
19 /* This file is loosely based on FreeBSD geli implementation
20 (but no code was directly copied). FreeBSD geli is distributed under
21 following terms: */
22 /*-
23 * Copyright (c) 2005-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
24 * All rights reserved.
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions
28 * are met:
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 * notice, this list of conditions and the following disclaimer in the
33 * documentation and/or other materials provided with the distribution.
35 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
36 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
37 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
38 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
39 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
40 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
41 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
43 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
45 * SUCH DAMAGE.
48 #include <grub/cryptodisk.h>
49 #include <grub/types.h>
50 #include <grub/misc.h>
51 #include <grub/mm.h>
52 #include <grub/dl.h>
53 #include <grub/err.h>
54 #include <grub/disk.h>
55 #include <grub/crypto.h>
56 #include <grub/partition.h>
57 #include <grub/i18n.h>
59 GRUB_MOD_LICENSE ("GPLv3+");
61 /* Dirty trick to solve circular dependency. */
62 #ifdef GRUB_UTIL
64 #include <grub/util/misc.h>
66 #undef GRUB_MD_SHA256
67 #undef GRUB_MD_SHA512
69 static const gcry_md_spec_t *
70 grub_md_sha256_real (void)
72 const gcry_md_spec_t *ret;
73 ret = grub_crypto_lookup_md_by_name ("sha256");
74 if (!ret)
75 grub_util_error ("%s", _("Couldn't load sha256"));
76 return ret;
79 static const gcry_md_spec_t *
80 grub_md_sha512_real (void)
82 const gcry_md_spec_t *ret;
83 ret = grub_crypto_lookup_md_by_name ("sha512");
84 if (!ret)
85 grub_util_error ("%s", _("Couldn't load sha512"));
86 return ret;
89 #define GRUB_MD_SHA256 grub_md_sha256_real()
90 #define GRUB_MD_SHA512 grub_md_sha512_real()
91 #endif
93 struct grub_geli_key
95 grub_uint8_t iv_key[64];
96 grub_uint8_t cipher_key[64];
97 grub_uint8_t hmac[64];
98 } __attribute__ ((packed));
100 struct grub_geli_phdr
102 grub_uint8_t magic[16];
103 #define GELI_MAGIC "GEOM::ELI"
104 grub_uint32_t version;
105 grub_uint32_t flags;
106 grub_uint16_t alg;
107 grub_uint16_t keylen;
108 grub_uint16_t unused3[5];
109 grub_uint32_t sector_size;
110 grub_uint8_t keys_used;
111 grub_uint32_t niter;
112 grub_uint8_t salt[64];
113 struct grub_geli_key keys[2];
114 } __attribute__ ((packed));
116 enum
118 GRUB_GELI_FLAGS_ONETIME = 1,
119 GRUB_GELI_FLAGS_BOOT = 2,
122 /* FIXME: support version 0. */
123 /* FIXME: support big-endian pre-version-4 volumes. */
124 /* FIXME: support for keyfiles. */
125 /* FIXME: support for HMAC. */
126 const char *algorithms[] = {
127 [0x01] = "des",
128 [0x02] = "3des",
129 [0x03] = "blowfish",
130 [0x04] = "cast5",
131 /* FIXME: 0x05 is skipjack, but we don't have it. */
132 [0x0b] = "aes",
133 /* FIXME: 0x10 is null. */
134 [0x15] = "camellia128",
135 [0x16] = "aes"
138 #define MAX_PASSPHRASE 256
140 static gcry_err_code_t
141 geli_rekey (struct grub_cryptodisk *dev, grub_uint64_t zoneno)
143 gcry_err_code_t gcry_err;
144 const struct {
145 char magic[4];
146 grub_uint64_t zone;
147 } __attribute__ ((packed)) tohash
148 = { {'e', 'k', 'e', 'y'}, grub_cpu_to_le64 (zoneno) };
149 grub_uint64_t key[(dev->hash->mdlen + 7) / 8];
151 grub_dprintf ("geli", "rekeying %" PRIuGRUB_UINT64_T " keysize=%d\n",
152 zoneno, dev->rekey_derived_size);
153 gcry_err = grub_crypto_hmac_buffer (dev->hash, dev->rekey_key, 64,
154 &tohash, sizeof (tohash), key);
155 if (gcry_err)
156 return grub_crypto_gcry_error (gcry_err);
158 return grub_cryptodisk_setkey (dev, (grub_uint8_t *) key,
159 dev->rekey_derived_size);
162 static inline int
163 ascii2hex (char c)
165 if (c >= '0' && c <= '9')
166 return c - '0';
167 if (c >= 'a' && c <= 'f')
168 return c - 'a' + 10;
169 if (c >= 'A' && c <= 'F')
170 return c - 'A' + 10;
171 return 0;
174 static inline gcry_err_code_t
175 make_uuid (const struct grub_geli_phdr *header,
176 char *uuid)
178 grub_uint8_t uuidbin[GRUB_MD_SHA256->mdlen];
179 gcry_err_code_t err;
180 grub_uint8_t *iptr;
181 char *optr;
183 err = grub_crypto_hmac_buffer (GRUB_MD_SHA256,
184 header->salt, sizeof (header->salt),
185 "uuid", sizeof ("uuid") - 1, uuidbin);
186 if (err)
187 return err;
189 optr = uuid;
190 for (iptr = uuidbin; iptr < &uuidbin[ARRAY_SIZE (uuidbin)]; iptr++)
192 grub_snprintf (optr, 3, "%02x", *iptr);
193 optr += 2;
195 *optr = 0;
196 return GPG_ERR_NO_ERROR;
199 #ifdef GRUB_UTIL
201 #include <errno.h>
202 #include <sys/types.h>
203 #include <sys/stat.h>
204 #include <fcntl.h>
205 #include <grub/emu/hostdisk.h>
206 #include <unistd.h>
207 #include <string.h>
208 #include <grub/emu/misc.h>
210 char *
211 grub_util_get_geli_uuid (const char *dev)
213 int fd = open (dev, O_RDONLY);
214 grub_uint64_t s;
215 unsigned log_secsize;
216 grub_uint8_t hdr[512];
217 struct grub_geli_phdr *header;
218 char *uuid;
219 gcry_err_code_t err;
221 if (fd < 0)
222 return NULL;
224 s = grub_util_get_fd_size (fd, dev, &log_secsize);
225 s >>= log_secsize;
226 grub_util_fd_seek (fd, dev, (s << log_secsize) - 512);
228 uuid = xmalloc (GRUB_MD_SHA256->mdlen * 2 + 1);
229 if (grub_util_fd_read (fd, (void *) &hdr, 512) < 0)
230 grub_util_error ("%s", _("couldn't read ELI metadata"));
232 COMPILE_TIME_ASSERT (sizeof (header) <= 512);
233 header = (void *) &hdr;
235 /* Look for GELI magic sequence. */
236 if (grub_memcmp (header->magic, GELI_MAGIC, sizeof (GELI_MAGIC))
237 || grub_le_to_cpu32 (header->version) > 5
238 || grub_le_to_cpu32 (header->version) < 1)
239 grub_util_error ("%s", _("wrong ELI magic or version"));
241 err = make_uuid ((void *) &hdr, uuid);
242 if (err)
243 return NULL;
245 return uuid;
247 #endif
249 static grub_cryptodisk_t
250 configure_ciphers (grub_disk_t disk, const char *check_uuid,
251 int boot_only)
253 grub_cryptodisk_t newdev;
254 struct grub_geli_phdr header;
255 grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL;
256 const struct gcry_cipher_spec *ciph;
257 const char *ciphername = NULL;
258 gcry_err_code_t gcry_err;
259 char uuid[GRUB_MD_SHA256->mdlen * 2 + 1];
260 grub_disk_addr_t sector;
261 grub_err_t err;
263 sector = grub_disk_get_size (disk);
264 if (sector == GRUB_DISK_SIZE_UNKNOWN || sector == 0)
265 return NULL;
267 /* Read the GELI header. */
268 err = grub_disk_read (disk, sector - 1, 0, sizeof (header), &header);
269 if (err)
270 return NULL;
272 /* Look for GELI magic sequence. */
273 if (grub_memcmp (header.magic, GELI_MAGIC, sizeof (GELI_MAGIC))
274 || grub_le_to_cpu32 (header.version) > 5
275 || grub_le_to_cpu32 (header.version) < 1)
277 grub_dprintf ("geli", "wrong magic %02x\n", header.magic[0]);
278 return NULL;
281 if ((grub_le_to_cpu32 (header.sector_size)
282 & (grub_le_to_cpu32 (header.sector_size) - 1))
283 || grub_le_to_cpu32 (header.sector_size) == 0)
285 grub_dprintf ("geli", "incorrect sector size %d\n",
286 grub_le_to_cpu32 (header.sector_size));
287 return NULL;
290 if (grub_le_to_cpu32 (header.flags) & GRUB_GELI_FLAGS_ONETIME)
292 grub_dprintf ("geli", "skipping one-time volume\n");
293 return NULL;
296 if (boot_only && !(grub_le_to_cpu32 (header.flags) & GRUB_GELI_FLAGS_BOOT))
298 grub_dprintf ("geli", "not a boot volume\n");
299 return NULL;
302 gcry_err = make_uuid (&header, uuid);
303 if (gcry_err)
305 grub_crypto_gcry_error (gcry_err);
306 return NULL;
309 if (check_uuid && grub_strcasecmp (check_uuid, uuid) != 0)
311 grub_dprintf ("geli", "%s != %s\n", uuid, check_uuid);
312 return NULL;
315 if (grub_le_to_cpu16 (header.alg) >= ARRAY_SIZE (algorithms)
316 || algorithms[grub_le_to_cpu16 (header.alg)] == NULL)
318 grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher 0x%x unknown",
319 grub_le_to_cpu16 (header.alg));
320 return NULL;
323 ciphername = algorithms[grub_le_to_cpu16 (header.alg)];
324 ciph = grub_crypto_lookup_cipher_by_name (ciphername);
325 if (!ciph)
327 grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s isn't available",
328 ciphername);
329 return NULL;
332 /* Configure the cipher used for the bulk data. */
333 cipher = grub_crypto_cipher_open (ciph);
334 if (!cipher)
335 return NULL;
337 if (grub_le_to_cpu16 (header.alg) == 0x16)
339 secondary_cipher = grub_crypto_cipher_open (ciph);
340 if (!secondary_cipher)
341 return NULL;
344 if (grub_le_to_cpu16 (header.keylen) > 1024)
346 grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d",
347 grub_le_to_cpu16 (header.keylen));
348 return NULL;
351 newdev = grub_zalloc (sizeof (struct grub_cryptodisk));
352 if (!newdev)
353 return NULL;
354 newdev->cipher = cipher;
355 newdev->secondary_cipher = secondary_cipher;
356 newdev->offset = 0;
357 newdev->source_disk = NULL;
358 newdev->benbi_log = 0;
359 if (grub_le_to_cpu16 (header.alg) == 0x16)
361 newdev->mode = GRUB_CRYPTODISK_MODE_XTS;
362 newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64;
364 else
366 newdev->mode = GRUB_CRYPTODISK_MODE_CBC;
367 newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH;
369 newdev->essiv_cipher = NULL;
370 newdev->essiv_hash = NULL;
371 newdev->hash = GRUB_MD_SHA512;
372 newdev->iv_hash = GRUB_MD_SHA256;
374 for (newdev->log_sector_size = 0;
375 (1U << newdev->log_sector_size) < grub_le_to_cpu32 (header.sector_size);
376 newdev->log_sector_size++);
378 if (grub_le_to_cpu32 (header.version) >= 5)
380 newdev->rekey = geli_rekey;
381 newdev->rekey_shift = 20;
384 #ifdef GRUB_UTIL
385 newdev->modname = "geli";
386 #endif
388 newdev->total_length = grub_disk_get_size (disk) - 1;
389 grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid));
390 COMPILE_TIME_ASSERT (sizeof (newdev->uuid) >= 32 * 2 + 1);
391 return newdev;
394 static grub_err_t
395 recover_key (grub_disk_t source, grub_cryptodisk_t dev)
397 grub_size_t keysize;
398 grub_uint8_t digest[dev->hash->mdlen];
399 grub_uint8_t geomkey[dev->hash->mdlen];
400 grub_uint8_t verify_key[dev->hash->mdlen];
401 grub_uint8_t zero[dev->cipher->cipher->blocksize];
402 char passphrase[MAX_PASSPHRASE] = "";
403 unsigned i;
404 gcry_err_code_t gcry_err;
405 struct grub_geli_phdr header;
406 char *tmp;
407 grub_disk_addr_t sector;
408 grub_err_t err;
410 sector = grub_disk_get_size (source);
411 if (sector == GRUB_DISK_SIZE_UNKNOWN || sector == 0)
412 return grub_error (GRUB_ERR_BUG, "not a geli");
414 /* Read the GELI header. */
415 err = grub_disk_read (source, sector - 1, 0, sizeof (header), &header);
416 if (err)
417 return err;
419 keysize = grub_le_to_cpu16 (header.keylen) / 8;
420 grub_memset (zero, 0, sizeof (zero));
422 grub_puts_ (N_("Attempting to decrypt master key..."));
424 /* Get the passphrase from the user. */
425 tmp = NULL;
426 if (source->partition)
427 tmp = grub_partition_get_name (source->partition);
428 grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name,
429 source->partition ? "," : "", tmp ? : "",
430 dev->uuid);
431 grub_free (tmp);
432 if (!grub_password_get (passphrase, MAX_PASSPHRASE))
433 return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied");
435 /* Calculate the PBKDF2 of the user supplied passphrase. */
436 if (grub_le_to_cpu32 (header.niter) != 0)
438 grub_uint8_t pbkdf_key[64];
439 gcry_err = grub_crypto_pbkdf2 (dev->hash, (grub_uint8_t *) passphrase,
440 grub_strlen (passphrase),
441 header.salt,
442 sizeof (header.salt),
443 grub_le_to_cpu32 (header.niter),
444 pbkdf_key, sizeof (pbkdf_key));
446 if (gcry_err)
447 return grub_crypto_gcry_error (gcry_err);
449 gcry_err = grub_crypto_hmac_buffer (dev->hash, NULL, 0, pbkdf_key,
450 sizeof (pbkdf_key), geomkey);
451 if (gcry_err)
452 return grub_crypto_gcry_error (gcry_err);
454 else
456 struct grub_crypto_hmac_handle *hnd;
458 hnd = grub_crypto_hmac_init (dev->hash, NULL, 0);
459 if (!hnd)
460 return grub_crypto_gcry_error (GPG_ERR_OUT_OF_MEMORY);
462 grub_crypto_hmac_write (hnd, header.salt, sizeof (header.salt));
463 grub_crypto_hmac_write (hnd, passphrase, grub_strlen (passphrase));
465 gcry_err = grub_crypto_hmac_fini (hnd, geomkey);
466 if (gcry_err)
467 return grub_crypto_gcry_error (gcry_err);
470 gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey,
471 sizeof (geomkey), "\1", 1, digest);
472 if (gcry_err)
473 return grub_crypto_gcry_error (gcry_err);
475 gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey,
476 sizeof (geomkey), "\0", 1, verify_key);
477 if (gcry_err)
478 return grub_crypto_gcry_error (gcry_err);
480 grub_dprintf ("geli", "keylen = %" PRIuGRUB_SIZE "\n", keysize);
482 /* Try to recover master key from each active keyslot. */
483 for (i = 0; i < ARRAY_SIZE (header.keys); i++)
485 struct grub_geli_key candidate_key;
486 grub_uint8_t key_hmac[dev->hash->mdlen];
488 /* Check if keyslot is enabled. */
489 if (! (header.keys_used & (1 << i)))
490 continue;
492 grub_dprintf ("geli", "Trying keyslot %d\n", i);
494 gcry_err = grub_crypto_cipher_set_key (dev->cipher,
495 digest, keysize);
496 if (gcry_err)
497 return grub_crypto_gcry_error (gcry_err);
499 gcry_err = grub_crypto_cbc_decrypt (dev->cipher, &candidate_key,
500 &header.keys[i],
501 sizeof (candidate_key),
502 zero);
503 if (gcry_err)
504 return grub_crypto_gcry_error (gcry_err);
506 gcry_err = grub_crypto_hmac_buffer (dev->hash, verify_key,
507 sizeof (verify_key),
508 &candidate_key,
509 (sizeof (candidate_key)
510 - sizeof (candidate_key.hmac)),
511 key_hmac);
512 if (gcry_err)
513 return grub_crypto_gcry_error (gcry_err);
515 if (grub_memcmp (candidate_key.hmac, key_hmac, dev->hash->mdlen) != 0)
516 continue;
517 grub_printf_ (N_("Slot %d opened\n"), i);
519 /* Set the master key. */
520 if (!dev->rekey)
522 grub_size_t real_keysize = keysize;
523 if (grub_le_to_cpu16 (header.alg) == 0x16)
524 real_keysize *= 2;
525 gcry_err = grub_cryptodisk_setkey (dev, candidate_key.cipher_key,
526 real_keysize);
527 if (gcry_err)
528 return grub_crypto_gcry_error (gcry_err);
530 else
532 grub_size_t real_keysize = keysize;
533 if (grub_le_to_cpu16 (header.alg) == 0x16)
534 real_keysize *= 2;
535 /* For a reason I don't know, the IV key is used in rekeying. */
536 grub_memcpy (dev->rekey_key, candidate_key.iv_key,
537 sizeof (candidate_key.iv_key));
538 dev->rekey_derived_size = real_keysize;
539 dev->last_rekey = -1;
540 COMPILE_TIME_ASSERT (sizeof (dev->rekey_key)
541 >= sizeof (candidate_key.iv_key));
544 dev->iv_prefix_len = sizeof (candidate_key.iv_key);
545 grub_memcpy (dev->iv_prefix, candidate_key.iv_key,
546 sizeof (candidate_key.iv_key));
548 COMPILE_TIME_ASSERT (sizeof (dev->iv_prefix) >= sizeof (candidate_key.iv_key));
550 return GRUB_ERR_NONE;
553 return GRUB_ACCESS_DENIED;
556 struct grub_cryptodisk_dev geli_crypto = {
557 .scan = configure_ciphers,
558 .recover_key = recover_key
561 GRUB_MOD_INIT (geli)
563 grub_cryptodisk_dev_register (&geli_crypto);
566 GRUB_MOD_FINI (geli)
568 grub_cryptodisk_dev_unregister (&geli_crypto);