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
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
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
48 #include <grub/cryptodisk.h>
49 #include <grub/types.h>
50 #include <grub/misc.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. */
64 #include <grub/util/misc.h>
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");
75 grub_util_error ("%s", _("Couldn't load sha256"));
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");
85 grub_util_error ("%s", _("Couldn't load sha512"));
89 #define GRUB_MD_SHA256 grub_md_sha256_real()
90 #define GRUB_MD_SHA512 grub_md_sha512_real()
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
;
107 grub_uint16_t keylen
;
108 grub_uint16_t unused3
[5];
109 grub_uint32_t sector_size
;
110 grub_uint8_t keys_used
;
112 grub_uint8_t salt
[64];
113 struct grub_geli_key keys
[2];
114 } __attribute__ ((packed
));
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
[] = {
131 /* FIXME: 0x05 is skipjack, but we don't have it. */
133 /* FIXME: 0x10 is null. */
134 [0x15] = "camellia128",
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
;
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
);
156 return grub_crypto_gcry_error (gcry_err
);
158 return grub_cryptodisk_setkey (dev
, (grub_uint8_t
*) key
,
159 dev
->rekey_derived_size
);
165 if (c
>= '0' && c
<= '9')
167 if (c
>= 'a' && c
<= 'f')
169 if (c
>= 'A' && c
<= 'F')
174 static inline gcry_err_code_t
175 make_uuid (const struct grub_geli_phdr
*header
,
178 grub_uint8_t uuidbin
[GRUB_MD_SHA256
->mdlen
];
183 err
= grub_crypto_hmac_buffer (GRUB_MD_SHA256
,
184 header
->salt
, sizeof (header
->salt
),
185 "uuid", sizeof ("uuid") - 1, uuidbin
);
190 for (iptr
= uuidbin
; iptr
< &uuidbin
[ARRAY_SIZE (uuidbin
)]; iptr
++)
192 grub_snprintf (optr
, 3, "%02x", *iptr
);
196 return GPG_ERR_NO_ERROR
;
202 #include <sys/types.h>
203 #include <sys/stat.h>
205 #include <grub/emu/hostdisk.h>
208 #include <grub/emu/misc.h>
211 grub_util_get_geli_uuid (const char *dev
)
213 int fd
= open (dev
, O_RDONLY
);
215 unsigned log_secsize
;
216 grub_uint8_t hdr
[512];
217 struct grub_geli_phdr
*header
;
224 s
= grub_util_get_fd_size (fd
, dev
, &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
);
249 static grub_cryptodisk_t
250 configure_ciphers (grub_disk_t disk
, const char *check_uuid
,
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
;
263 sector
= grub_disk_get_size (disk
);
264 if (sector
== GRUB_DISK_SIZE_UNKNOWN
|| sector
== 0)
267 /* Read the GELI header. */
268 err
= grub_disk_read (disk
, sector
- 1, 0, sizeof (header
), &header
);
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]);
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
));
290 if (grub_le_to_cpu32 (header
.flags
) & GRUB_GELI_FLAGS_ONETIME
)
292 grub_dprintf ("geli", "skipping one-time volume\n");
296 if (boot_only
&& !(grub_le_to_cpu32 (header
.flags
) & GRUB_GELI_FLAGS_BOOT
))
298 grub_dprintf ("geli", "not a boot volume\n");
302 gcry_err
= make_uuid (&header
, uuid
);
305 grub_crypto_gcry_error (gcry_err
);
309 if (check_uuid
&& grub_strcasecmp (check_uuid
, uuid
) != 0)
311 grub_dprintf ("geli", "%s != %s\n", uuid
, check_uuid
);
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
));
323 ciphername
= algorithms
[grub_le_to_cpu16 (header
.alg
)];
324 ciph
= grub_crypto_lookup_cipher_by_name (ciphername
);
327 grub_error (GRUB_ERR_FILE_NOT_FOUND
, "Cipher %s isn't available",
332 /* Configure the cipher used for the bulk data. */
333 cipher
= grub_crypto_cipher_open (ciph
);
337 if (grub_le_to_cpu16 (header
.alg
) == 0x16)
339 secondary_cipher
= grub_crypto_cipher_open (ciph
);
340 if (!secondary_cipher
)
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
));
351 newdev
= grub_zalloc (sizeof (struct grub_cryptodisk
));
354 newdev
->cipher
= cipher
;
355 newdev
->secondary_cipher
= secondary_cipher
;
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
;
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;
385 newdev
->modname
= "geli";
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);
395 recover_key (grub_disk_t source
, grub_cryptodisk_t dev
)
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
] = "";
404 gcry_err_code_t gcry_err
;
405 struct grub_geli_phdr header
;
407 grub_disk_addr_t sector
;
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
);
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. */
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
? : "",
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
),
442 sizeof (header
.salt
),
443 grub_le_to_cpu32 (header
.niter
),
444 pbkdf_key
, sizeof (pbkdf_key
));
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
);
452 return grub_crypto_gcry_error (gcry_err
);
456 struct grub_crypto_hmac_handle
*hnd
;
458 hnd
= grub_crypto_hmac_init (dev
->hash
, NULL
, 0);
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
);
467 return grub_crypto_gcry_error (gcry_err
);
470 gcry_err
= grub_crypto_hmac_buffer (dev
->hash
, geomkey
,
471 sizeof (geomkey
), "\1", 1, digest
);
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
);
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
)))
492 grub_dprintf ("geli", "Trying keyslot %d\n", i
);
494 gcry_err
= grub_crypto_cipher_set_key (dev
->cipher
,
497 return grub_crypto_gcry_error (gcry_err
);
499 gcry_err
= grub_crypto_cbc_decrypt (dev
->cipher
, &candidate_key
,
501 sizeof (candidate_key
),
504 return grub_crypto_gcry_error (gcry_err
);
506 gcry_err
= grub_crypto_hmac_buffer (dev
->hash
, verify_key
,
509 (sizeof (candidate_key
)
510 - sizeof (candidate_key
.hmac
)),
513 return grub_crypto_gcry_error (gcry_err
);
515 if (grub_memcmp (candidate_key
.hmac
, key_hmac
, dev
->hash
->mdlen
) != 0)
517 grub_printf_ (N_("Slot %d opened\n"), i
);
519 /* Set the master key. */
522 grub_size_t real_keysize
= keysize
;
523 if (grub_le_to_cpu16 (header
.alg
) == 0x16)
525 gcry_err
= grub_cryptodisk_setkey (dev
, candidate_key
.cipher_key
,
528 return grub_crypto_gcry_error (gcry_err
);
532 grub_size_t real_keysize
= keysize
;
533 if (grub_le_to_cpu16 (header
.alg
) == 0x16)
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
563 grub_cryptodisk_dev_register (&geli_crypto
);
568 grub_cryptodisk_dev_unregister (&geli_crypto
);