geli.c 16.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
/*
 *  GRUB  --  GRand Unified Bootloader
 *  Copyright (C) 2003,2007,2010,2011  Free Software Foundation, Inc.
 *
 *  GRUB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

/* This file is loosely based on FreeBSD geli implementation
   (but no code was directly copied). FreeBSD geli is distributed under
   following terms:  */
/*-
 * Copyright (c) 2005-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <grub/cryptodisk.h>
#include <grub/types.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/dl.h>
#include <grub/err.h>
#include <grub/disk.h>
#include <grub/crypto.h>
56
#include <grub/partition.h>
57 58 59 60
#include <grub/i18n.h>

GRUB_MOD_LICENSE ("GPLv3+");

61 62 63 64 65 66 67 68 69 70 71 72 73 74
/* Dirty trick to solve circular dependency.  */
#ifdef GRUB_UTIL

#include <grub/util/misc.h>

#undef GRUB_MD_SHA256
#undef GRUB_MD_SHA512

static const gcry_md_spec_t *
grub_md_sha256_real (void)
{
  const gcry_md_spec_t *ret;
  ret = grub_crypto_lookup_md_by_name ("sha256");
  if (!ret)
75
    grub_util_error ("%s", _("Couldn't load sha256"));
76 77 78 79 80 81 82 83 84
  return ret;
}

static const gcry_md_spec_t *
grub_md_sha512_real (void)
{
  const gcry_md_spec_t *ret;
  ret = grub_crypto_lookup_md_by_name ("sha512");
  if (!ret)
85
    grub_util_error ("%s", _("Couldn't load sha512"));
86 87 88 89 90 91 92
  return ret;
}

#define GRUB_MD_SHA256 grub_md_sha256_real()
#define GRUB_MD_SHA512 grub_md_sha512_real()
#endif

93 94 95 96 97
struct grub_geli_key
{
  grub_uint8_t iv_key[64];
  grub_uint8_t cipher_key[64];
  grub_uint8_t hmac[64];
98
} GRUB_PACKED;
99 100 101 102 103 104

struct grub_geli_phdr
{
  grub_uint8_t magic[16];
#define GELI_MAGIC "GEOM::ELI"
  grub_uint32_t version;
105
  grub_uint32_t flags;
106 107
  grub_uint16_t alg;
  grub_uint16_t keylen;
108 109
  grub_uint16_t unused3[5];
  grub_uint32_t sector_size;
110 111 112 113
  grub_uint8_t keys_used;
  grub_uint32_t niter;
  grub_uint8_t salt[64];
  struct grub_geli_key keys[2];
114
} GRUB_PACKED;
115

116 117 118 119 120 121
enum
  {
    GRUB_GELI_FLAGS_ONETIME = 1,
    GRUB_GELI_FLAGS_BOOT = 2,
  };

122
/* FIXME: support version 0.  */
123 124 125
/* FIXME: support big-endian pre-version-4 volumes.  */
/* FIXME: support for keyfiles.  */
/* FIXME: support for HMAC.  */
126
const char *algorithms[] = {
127 128 129 130
  [0x01] = "des",
  [0x02] = "3des",
  [0x03] = "blowfish",
  [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"
136 137 138 139
};

#define MAX_PASSPHRASE 256

140 141 142 143 144 145 146
static gcry_err_code_t
geli_rekey (struct grub_cryptodisk *dev, grub_uint64_t zoneno)
{
  gcry_err_code_t gcry_err;
  const struct {
    char magic[4];
    grub_uint64_t zone;
147
  } GRUB_PACKED tohash
148
      = { {'e', 'k', 'e', 'y'}, grub_cpu_to_le64 (zoneno) };
149 150 151 152
  GRUB_PROPERLY_ALIGNED_ARRAY (key, GRUB_CRYPTO_MAX_MDLEN);

  if (dev->hash->mdlen > GRUB_CRYPTO_MAX_MDLEN)
    return GPG_ERR_INV_ARG;
153 154 155 156 157 158

  grub_dprintf ("geli", "rekeying %" PRIuGRUB_UINT64_T " keysize=%d\n",
		zoneno, dev->rekey_derived_size);
  gcry_err = grub_crypto_hmac_buffer (dev->hash, dev->rekey_key, 64,
				      &tohash, sizeof (tohash), key);
  if (gcry_err)
159
    return gcry_err;
160 161 162 163 164

  return grub_cryptodisk_setkey (dev, (grub_uint8_t *) key,
				 dev->rekey_derived_size); 
}

165 166 167
static inline gcry_err_code_t
make_uuid (const struct grub_geli_phdr *header,
	   char *uuid)
168
{
169
  grub_uint8_t uuidbin[GRUB_CRYPTODISK_MAX_UUID_LENGTH];
170
  gcry_err_code_t err;
171 172
  grub_uint8_t *iptr;
  char *optr;
173

174 175
  if (2 * GRUB_MD_SHA256->mdlen + 1 > GRUB_CRYPTODISK_MAX_UUID_LENGTH)
    return GPG_ERR_TOO_LARGE;
176 177 178 179 180 181 182
  err = grub_crypto_hmac_buffer (GRUB_MD_SHA256,
				 header->salt, sizeof (header->salt),
				 "uuid", sizeof ("uuid") - 1, uuidbin);
  if (err)
    return err;

  optr = uuid;
183
  for (iptr = uuidbin; iptr < &uuidbin[GRUB_MD_SHA256->mdlen]; iptr++)
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
    {
      grub_snprintf (optr, 3, "%02x", *iptr);
      optr += 2;
    }
  *optr = 0;
  return GPG_ERR_NO_ERROR;
}

#ifdef GRUB_UTIL

#include <grub/emu/hostdisk.h>
#include <grub/emu/misc.h>

char *
grub_util_get_geli_uuid (const char *dev)
{
200
  grub_util_fd_t fd;
201 202 203 204 205 206 207
  grub_uint64_t s;
  unsigned log_secsize;
  grub_uint8_t hdr[512];
  struct grub_geli_phdr *header;
  char *uuid; 
  gcry_err_code_t err;

208 209
  fd = grub_util_fd_open (dev, GRUB_UTIL_FD_O_RDONLY);

210
  if (!GRUB_UTIL_FD_IS_VALID (fd))
211 212
    return NULL;

213 214
  s = grub_util_get_fd_size (fd, dev, &log_secsize);
  s >>= log_secsize;
215 216
  if (grub_util_fd_seek (fd, (s << log_secsize) - 512) < 0)
    grub_util_error ("%s", _("couldn't read ELI metadata"));
217 218 219

  uuid = xmalloc (GRUB_MD_SHA256->mdlen * 2 + 1);
  if (grub_util_fd_read (fd, (void *) &hdr, 512) < 0)
220
    grub_util_error ("%s", _("couldn't read ELI metadata"));
221 222

  grub_util_fd_close (fd);
223 224 225
	  
  COMPILE_TIME_ASSERT (sizeof (header) <= 512);
  header = (void *) &hdr;
226 227 228

  /* Look for GELI magic sequence.  */
  if (grub_memcmp (header->magic, GELI_MAGIC, sizeof (GELI_MAGIC))
Kris Moore's avatar
Kris Moore committed
229
      || grub_le_to_cpu32 (header->version) > 7
230
      || grub_le_to_cpu32 (header->version) < 1)
231
    grub_util_error ("%s", _("wrong ELI magic or version"));
232 233 234

  err = make_uuid ((void *) &hdr, uuid);
  if (err)
235 236 237 238
    {
      grub_free (uuid);
      return NULL;
    }
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253

  return uuid;
}
#endif

static grub_cryptodisk_t
configure_ciphers (grub_disk_t disk, const char *check_uuid,
		   int boot_only)
{
  grub_cryptodisk_t newdev;
  struct grub_geli_phdr header;
  grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL;
  const struct gcry_cipher_spec *ciph;
  const char *ciphername = NULL;
  gcry_err_code_t gcry_err;
254
  char uuid[GRUB_CRYPTODISK_MAX_UUID_LENGTH];
255 256 257
  grub_disk_addr_t sector;
  grub_err_t err;

258 259 260
  if (2 * GRUB_MD_SHA256->mdlen + 1 > GRUB_CRYPTODISK_MAX_UUID_LENGTH)
    return NULL;

261 262 263 264 265 266 267 268 269 270 271
  sector = grub_disk_get_size (disk);
  if (sector == GRUB_DISK_SIZE_UNKNOWN || sector == 0)
    return NULL;

  /* Read the GELI header.  */
  err = grub_disk_read (disk, sector - 1, 0, sizeof (header), &header);
  if (err)
    return NULL;

  /* Look for GELI magic sequence.  */
  if (grub_memcmp (header.magic, GELI_MAGIC, sizeof (GELI_MAGIC))
Kris Moore's avatar
Kris Moore committed
272
      || grub_le_to_cpu32 (header.version) > 7
273
      || grub_le_to_cpu32 (header.version) < 1)
274
    {
275
      grub_dprintf ("geli", "wrong magic %02x\n", header.magic[0]);
276 277
      return NULL;
    }
278 279 280 281

  if ((grub_le_to_cpu32 (header.sector_size)
       & (grub_le_to_cpu32 (header.sector_size) - 1))
      || grub_le_to_cpu32 (header.sector_size) == 0)
282 283
    {
      grub_dprintf ("geli", "incorrect sector size %d\n",
284
		    grub_le_to_cpu32 (header.sector_size));
285
      return NULL;
286 287
    }

288
  if (grub_le_to_cpu32 (header.flags) & GRUB_GELI_FLAGS_ONETIME)
289 290 291 292 293
    {
      grub_dprintf ("geli", "skipping one-time volume\n");
      return NULL;
    }

294
  if (boot_only && !(grub_le_to_cpu32 (header.flags) & GRUB_GELI_FLAGS_BOOT))
295 296 297 298
    {
      grub_dprintf ("geli", "not a boot volume\n");
      return NULL;
    }    
299

300
  gcry_err = make_uuid (&header, uuid);
301 302 303 304 305
  if (gcry_err)
    {
      grub_crypto_gcry_error (gcry_err);
      return NULL;
    }
306

307
  if (check_uuid && grub_strcasecmp (check_uuid, uuid) != 0)
308
    {
309
      grub_dprintf ("geli", "%s != %s\n", uuid, check_uuid);
310 311 312
      return NULL;
    }

313 314
  if (grub_le_to_cpu16 (header.alg) >= ARRAY_SIZE (algorithms)
      || algorithms[grub_le_to_cpu16 (header.alg)] == NULL)
315 316
    {
      grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher 0x%x unknown",
317
		  grub_le_to_cpu16 (header.alg));
318 319 320
      return NULL;
    }

321
  ciphername = algorithms[grub_le_to_cpu16 (header.alg)];
322 323 324 325 326 327 328 329 330 331 332 333 334
  ciph = grub_crypto_lookup_cipher_by_name (ciphername);
  if (!ciph)
    {
      grub_error (GRUB_ERR_FILE_NOT_FOUND, "Cipher %s isn't available",
		  ciphername);
      return NULL;
    }

  /* Configure the cipher used for the bulk data.  */
  cipher = grub_crypto_cipher_open (ciph);
  if (!cipher)
    return NULL;

335
  if (grub_le_to_cpu16 (header.alg) == 0x16)
336 337 338
    {
      secondary_cipher = grub_crypto_cipher_open (ciph);
      if (!secondary_cipher)
339 340 341 342 343
	{
	  grub_crypto_cipher_close (cipher);
	  return NULL;
	}

344 345
    }

346
  if (grub_le_to_cpu16 (header.keylen) > 1024)
347 348
    {
      grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d",
349
		  grub_le_to_cpu16 (header.keylen));
350 351
      grub_crypto_cipher_close (cipher);
      grub_crypto_cipher_close (secondary_cipher);
352 353 354 355 356
      return NULL;
    }

  newdev = grub_zalloc (sizeof (struct grub_cryptodisk));
  if (!newdev)
357 358 359 360 361
    {
      grub_crypto_cipher_close (cipher);
      grub_crypto_cipher_close (secondary_cipher);
      return NULL;
    }
362
  newdev->cipher = cipher;
363
  newdev->secondary_cipher = secondary_cipher;
364 365 366
  newdev->offset = 0;
  newdev->source_disk = NULL;
  newdev->benbi_log = 0;
367
  if (grub_le_to_cpu16 (header.alg) == 0x16)
368 369 370 371 372 373 374 375 376
    {
      newdev->mode = GRUB_CRYPTODISK_MODE_XTS;
      newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64;
    }
  else
    {
      newdev->mode = GRUB_CRYPTODISK_MODE_CBC;
      newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH;
    }
377 378
  newdev->essiv_cipher = NULL;
  newdev->essiv_hash = NULL;
379 380
  newdev->hash = GRUB_MD_SHA512;
  newdev->iv_hash = GRUB_MD_SHA256;
381 382

  for (newdev->log_sector_size = 0;
383
       (1U << newdev->log_sector_size) < grub_le_to_cpu32 (header.sector_size);
384
       newdev->log_sector_size++);
385

386
  if (grub_le_to_cpu32 (header.version) >= 5)
387 388 389 390
    {
      newdev->rekey = geli_rekey;
      newdev->rekey_shift = 20;
    }
391

392 393 394
  newdev->modname = "geli";

  newdev->total_length = grub_disk_get_size (disk) - 1;
395
  grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid));
396
  COMPILE_TIME_ASSERT (sizeof (newdev->uuid) >= 32 * 2 + 1);
397 398 399 400
  return newdev;
}

static grub_err_t
401
recover_key (grub_disk_t source, grub_cryptodisk_t dev)
402
{
403
  grub_size_t keysize;
404 405 406 407
  grub_uint8_t digest[GRUB_CRYPTO_MAX_MDLEN];
  grub_uint8_t geomkey[GRUB_CRYPTO_MAX_MDLEN];
  grub_uint8_t verify_key[GRUB_CRYPTO_MAX_MDLEN];
  grub_uint8_t zero[GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE];
Kris Moore's avatar
Kris Moore committed
408
  grub_uint8_t geli_cipher_key[64];
409 410 411
  char passphrase[MAX_PASSPHRASE] = "";
  unsigned i;
  gcry_err_code_t gcry_err;
412 413 414 415 416
  struct grub_geli_phdr header;
  char *tmp;
  grub_disk_addr_t sector;
  grub_err_t err;

417 418 419 420 421 422
  if (dev->cipher->cipher->blocksize > GRUB_CRYPTO_MAX_CIPHER_BLOCKSIZE)
    return grub_error (GRUB_ERR_BUG, "cipher block is too long");

  if (dev->hash->mdlen > GRUB_CRYPTO_MAX_MDLEN)
    return grub_error (GRUB_ERR_BUG, "mdlen is too long");

423 424
  sector = grub_disk_get_size (source);
  if (sector == GRUB_DISK_SIZE_UNKNOWN || sector == 0)
425
    return grub_error (GRUB_ERR_BUG, "not a geli");
426

427 428 429 430 431
  /* Read the GELI header.  */
  err = grub_disk_read (source, sector - 1, 0, sizeof (header), &header);
  if (err)
    return err;

432
  keysize = grub_le_to_cpu16 (header.keylen) / GRUB_CHAR_BIT;
433 434
  grub_memset (zero, 0, sizeof (zero));

435
  grub_puts_ (N_("Attempting to decrypt master key..."));
436 437

  /* Get the passphrase from the user.  */
438 439 440
  tmp = NULL;
  if (source->partition)
    tmp = grub_partition_get_name (source->partition);
441 442 443
  grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name,
		source->partition ? "," : "", tmp ? : "",
		dev->uuid);
444
  grub_free (tmp);
445 446 447
  if (!grub_password_get (passphrase, MAX_PASSPHRASE))
    return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied");

448
  /* Calculate the PBKDF2 of the user supplied passphrase.  */
449
  if (grub_le_to_cpu32 (header.niter) != 0)
450 451 452 453
    {
      grub_uint8_t pbkdf_key[64];
      gcry_err = grub_crypto_pbkdf2 (dev->hash, (grub_uint8_t *) passphrase,
				     grub_strlen (passphrase),
454 455 456
				     header.salt,
				     sizeof (header.salt),
				     grub_le_to_cpu32 (header.niter),
457
				     pbkdf_key, sizeof (pbkdf_key));
458

459 460
      if (gcry_err)
	return grub_crypto_gcry_error (gcry_err);
461

462 463 464 465 466 467 468 469 470 471 472 473 474
      gcry_err = grub_crypto_hmac_buffer (dev->hash, NULL, 0, pbkdf_key,
					  sizeof (pbkdf_key), geomkey);
      if (gcry_err)
	return grub_crypto_gcry_error (gcry_err);
    }
  else
    {
      struct grub_crypto_hmac_handle *hnd;

      hnd = grub_crypto_hmac_init (dev->hash, NULL, 0);
      if (!hnd)
	return grub_crypto_gcry_error (GPG_ERR_OUT_OF_MEMORY);

475
      grub_crypto_hmac_write (hnd, header.salt, sizeof (header.salt));
476 477 478 479 480 481
      grub_crypto_hmac_write (hnd, passphrase, grub_strlen (passphrase));

      gcry_err = grub_crypto_hmac_fini (hnd, geomkey);
      if (gcry_err)
	return grub_crypto_gcry_error (gcry_err);
    }
482 483

  gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey,
484
				      dev->hash->mdlen, "\1", 1, digest);
485 486 487 488
  if (gcry_err)
    return grub_crypto_gcry_error (gcry_err);

  gcry_err = grub_crypto_hmac_buffer (dev->hash, geomkey,
489
				      dev->hash->mdlen, "\0", 1, verify_key);
490 491 492 493 494 495
  if (gcry_err)
    return grub_crypto_gcry_error (gcry_err);

  grub_dprintf ("geli", "keylen = %" PRIuGRUB_SIZE "\n", keysize);

  /* Try to recover master key from each active keyslot.  */
496
  for (i = 0; i < ARRAY_SIZE (header.keys); i++)
497 498
    {
      struct grub_geli_key candidate_key;
499
      grub_uint8_t key_hmac[GRUB_CRYPTO_MAX_MDLEN];
500 501

      /* Check if keyslot is enabled.  */
502
      if (! (header.keys_used & (1 << i)))
503 504 505 506 507 508 509 510 511 512
	  continue;

      grub_dprintf ("geli", "Trying keyslot %d\n", i);

      gcry_err = grub_crypto_cipher_set_key (dev->cipher,
					     digest, keysize);
      if (gcry_err)
	return grub_crypto_gcry_error (gcry_err);

      gcry_err = grub_crypto_cbc_decrypt (dev->cipher, &candidate_key,
513
					  &header.keys[i],
514 515 516 517 518 519
					  sizeof (candidate_key),
					  zero);
      if (gcry_err)
	return grub_crypto_gcry_error (gcry_err);

      gcry_err = grub_crypto_hmac_buffer (dev->hash, verify_key,
520
					  dev->hash->mdlen,
521 522 523 524 525 526 527 528 529
					  &candidate_key,
					  (sizeof (candidate_key)
					   - sizeof (candidate_key.hmac)),
					  key_hmac);
      if (gcry_err)
	return grub_crypto_gcry_error (gcry_err);

      if (grub_memcmp (candidate_key.hmac, key_hmac, dev->hash->mdlen) != 0)
	continue;
530
      grub_printf_ (N_("Slot %d opened\n"), i);
531

Kris Moore's avatar
Kris Moore committed
532 533 534 535 536 537 538 539 540 541 542 543 544
      if (grub_le_to_cpu32 (header.version) >= 7)
        {
          /* GELI >=7 uses the cipher_key */
	  grub_memcpy (geli_cipher_key, candidate_key.cipher_key,
		sizeof (candidate_key.cipher_key));
        }
      else
        {
          /* GELI <=6 uses the iv_key */
	  grub_memcpy (geli_cipher_key, candidate_key.iv_key,
		sizeof (candidate_key.iv_key));
        }

545
      /* Set the master key.  */
546 547
      if (!dev->rekey)
	{
548
	  grub_size_t real_keysize = keysize;
549
	  if (grub_le_to_cpu16 (header.alg) == 0x16)
550
	    real_keysize *= 2;
551
	  gcry_err = grub_cryptodisk_setkey (dev, candidate_key.cipher_key,
552
					     real_keysize); 
553 554 555 556 557
	  if (gcry_err)
	    return grub_crypto_gcry_error (gcry_err);
	}
      else
	{
558
	  grub_size_t real_keysize = keysize;
559
	  if (grub_le_to_cpu16 (header.alg) == 0x16)
560
	    real_keysize *= 2;
Kris Moore's avatar
Kris Moore committed
561 562 563

	  grub_memcpy (dev->rekey_key, geli_cipher_key,
		       sizeof (geli_cipher_key));
564
	  dev->rekey_derived_size = real_keysize;
565 566
	  dev->last_rekey = -1;
	  COMPILE_TIME_ASSERT (sizeof (dev->rekey_key)
Kris Moore's avatar
Kris Moore committed
567
		       >= sizeof (geli_cipher_key));
568
	}
569 570 571 572 573 574 575 576 577 578 579 580 581

      dev->iv_prefix_len = sizeof (candidate_key.iv_key);
      grub_memcpy (dev->iv_prefix, candidate_key.iv_key,
		   sizeof (candidate_key.iv_key));

      COMPILE_TIME_ASSERT (sizeof (dev->iv_prefix) >= sizeof (candidate_key.iv_key));

      return GRUB_ERR_NONE;
    }

  return GRUB_ACCESS_DENIED;
}

582 583 584 585
struct grub_cryptodisk_dev geli_crypto = {
  .scan = configure_ciphers,
  .recover_key = recover_key
};
586 587 588

GRUB_MOD_INIT (geli)
{
589
  grub_cryptodisk_dev_register (&geli_crypto);
590 591 592 593
}

GRUB_MOD_FINI (geli)
{
594
  grub_cryptodisk_dev_unregister (&geli_crypto);
595
}