/* * VasEBoot -- GRand Unified Bootloader * Copyright (C) 2003,2007,2010,2011 Free Software Foundation, Inc. * * VasEBoot 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. * * VasEBoot 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 VasEBoot. If not, see . */ #include #include #include #include #include #include #include #include #include #include VasEBoot_MOD_LICENSE ("GPLv3+"); #define MAX_PASSPHRASE 256 #define LUKS_KEY_ENABLED 0x00AC71F3 /* On disk LUKS header */ struct VasEBoot_luks_phdr { VasEBoot_uint8_t magic[6]; #define LUKS_MAGIC "LUKS\xBA\xBE" VasEBoot_uint16_t version; char cipherName[32]; char cipherMode[32]; char hashSpec[32]; VasEBoot_uint32_t payloadOffset; VasEBoot_uint32_t keyBytes; VasEBoot_uint8_t mkDigest[20]; VasEBoot_uint8_t mkDigestSalt[32]; VasEBoot_uint32_t mkDigestIterations; char uuid[40]; struct { VasEBoot_uint32_t active; VasEBoot_uint32_t passwordIterations; VasEBoot_uint8_t passwordSalt[32]; VasEBoot_uint32_t keyMaterialOffset; VasEBoot_uint32_t stripes; } keyblock[8]; } VasEBoot_PACKED; typedef struct VasEBoot_luks_phdr *VasEBoot_luks_phdr_t; gcry_err_code_t AF_merge (const gcry_md_spec_t * hash, VasEBoot_uint8_t * src, VasEBoot_uint8_t * dst, VasEBoot_size_t blocksize, VasEBoot_size_t blocknumbers); static VasEBoot_cryptodisk_t configure_ciphers (VasEBoot_disk_t disk, const char *check_uuid, int check_boot) { VasEBoot_cryptodisk_t newdev; const char *iptr; struct VasEBoot_luks_phdr header; char *optr; char uuid[sizeof (header.uuid) + 1]; char ciphername[sizeof (header.cipherName) + 1]; char ciphermode[sizeof (header.cipherMode) + 1]; char *cipheriv = NULL; char hashspec[sizeof (header.hashSpec) + 1]; VasEBoot_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL; VasEBoot_crypto_cipher_handle_t essiv_cipher = NULL; const gcry_md_spec_t *hash = NULL, *essiv_hash = NULL; const struct gcry_cipher_spec *ciph; VasEBoot_cryptodisk_mode_t mode; VasEBoot_cryptodisk_mode_iv_t mode_iv = VasEBoot_CRYPTODISK_MODE_IV_PLAIN64; int benbi_log = 0; VasEBoot_err_t err; if (check_boot) return NULL; /* Read the LUKS header. */ err = VasEBoot_disk_read (disk, 0, 0, sizeof (header), &header); if (err) { if (err == VasEBoot_ERR_OUT_OF_RANGE) VasEBoot_errno = VasEBoot_ERR_NONE; return NULL; } /* Look for LUKS magic sequence. */ if (VasEBoot_memcmp (header.magic, LUKS_MAGIC, sizeof (header.magic)) || VasEBoot_be_to_cpu16 (header.version) != 1) return NULL; optr = uuid; for (iptr = header.uuid; iptr < &header.uuid[ARRAY_SIZE (header.uuid)]; iptr++) { if (*iptr != '-') *optr++ = *iptr; } *optr = 0; if (check_uuid && VasEBoot_strcasecmp (check_uuid, uuid) != 0) { VasEBoot_dprintf ("luks", "%s != %s\n", uuid, check_uuid); return NULL; } /* Make sure that strings are null terminated. */ VasEBoot_memcpy (ciphername, header.cipherName, sizeof (header.cipherName)); ciphername[sizeof (header.cipherName)] = 0; VasEBoot_memcpy (ciphermode, header.cipherMode, sizeof (header.cipherMode)); ciphermode[sizeof (header.cipherMode)] = 0; VasEBoot_memcpy (hashspec, header.hashSpec, sizeof (header.hashSpec)); hashspec[sizeof (header.hashSpec)] = 0; ciph = VasEBoot_crypto_lookup_cipher_by_name (ciphername); if (!ciph) { VasEBoot_error (VasEBoot_ERR_FILE_NOT_FOUND, "Cipher %s isn't available", ciphername); return NULL; } /* Configure the cipher used for the bulk data. */ cipher = VasEBoot_crypto_cipher_open (ciph); if (!cipher) return NULL; if (VasEBoot_be_to_cpu32 (header.keyBytes) > 1024) { VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, "invalid keysize %d", VasEBoot_be_to_cpu32 (header.keyBytes)); VasEBoot_crypto_cipher_close (cipher); return NULL; } /* Configure the cipher mode. */ if (VasEBoot_strcmp (ciphermode, "ecb") == 0) { mode = VasEBoot_CRYPTODISK_MODE_ECB; mode_iv = VasEBoot_CRYPTODISK_MODE_IV_PLAIN; cipheriv = NULL; } else if (VasEBoot_strcmp (ciphermode, "plain") == 0) { mode = VasEBoot_CRYPTODISK_MODE_CBC; mode_iv = VasEBoot_CRYPTODISK_MODE_IV_PLAIN; cipheriv = NULL; } else if (VasEBoot_memcmp (ciphermode, "cbc-", sizeof ("cbc-") - 1) == 0) { mode = VasEBoot_CRYPTODISK_MODE_CBC; cipheriv = ciphermode + sizeof ("cbc-") - 1; } else if (VasEBoot_memcmp (ciphermode, "pcbc-", sizeof ("pcbc-") - 1) == 0) { mode = VasEBoot_CRYPTODISK_MODE_PCBC; cipheriv = ciphermode + sizeof ("pcbc-") - 1; } else if (VasEBoot_memcmp (ciphermode, "xts-", sizeof ("xts-") - 1) == 0) { mode = VasEBoot_CRYPTODISK_MODE_XTS; cipheriv = ciphermode + sizeof ("xts-") - 1; secondary_cipher = VasEBoot_crypto_cipher_open (ciph); if (!secondary_cipher) { VasEBoot_crypto_cipher_close (cipher); return NULL; } if (cipher->cipher->blocksize != VasEBoot_CRYPTODISK_GF_BYTES) { VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d", cipher->cipher->blocksize); VasEBoot_crypto_cipher_close (cipher); VasEBoot_crypto_cipher_close (secondary_cipher); return NULL; } if (secondary_cipher->cipher->blocksize != VasEBoot_CRYPTODISK_GF_BYTES) { VasEBoot_crypto_cipher_close (cipher); VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d", secondary_cipher->cipher->blocksize); VasEBoot_crypto_cipher_close (secondary_cipher); return NULL; } } else if (VasEBoot_memcmp (ciphermode, "lrw-", sizeof ("lrw-") - 1) == 0) { mode = VasEBoot_CRYPTODISK_MODE_LRW; cipheriv = ciphermode + sizeof ("lrw-") - 1; if (cipher->cipher->blocksize != VasEBoot_CRYPTODISK_GF_BYTES) { VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, "Unsupported LRW block size: %d", cipher->cipher->blocksize); VasEBoot_crypto_cipher_close (cipher); return NULL; } } else { VasEBoot_crypto_cipher_close (cipher); VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, "Unknown cipher mode: %s", ciphermode); return NULL; } if (cipheriv == NULL); else if (VasEBoot_memcmp (cipheriv, "plain", sizeof ("plain") - 1) == 0) mode_iv = VasEBoot_CRYPTODISK_MODE_IV_PLAIN; else if (VasEBoot_memcmp (cipheriv, "plain64", sizeof ("plain64") - 1) == 0) mode_iv = VasEBoot_CRYPTODISK_MODE_IV_PLAIN64; else if (VasEBoot_memcmp (cipheriv, "benbi", sizeof ("benbi") - 1) == 0) { if (cipher->cipher->blocksize & (cipher->cipher->blocksize - 1) || cipher->cipher->blocksize == 0) VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, "Unsupported benbi blocksize: %d", cipher->cipher->blocksize); /* FIXME should we return an error here? */ for (benbi_log = 0; (cipher->cipher->blocksize << benbi_log) < VasEBoot_DISK_SECTOR_SIZE; benbi_log++); mode_iv = VasEBoot_CRYPTODISK_MODE_IV_BENBI; } else if (VasEBoot_memcmp (cipheriv, "null", sizeof ("null") - 1) == 0) mode_iv = VasEBoot_CRYPTODISK_MODE_IV_NULL; else if (VasEBoot_memcmp (cipheriv, "essiv:", sizeof ("essiv:") - 1) == 0) { char *hash_str = cipheriv + 6; mode_iv = VasEBoot_CRYPTODISK_MODE_IV_ESSIV; /* Configure the hash and cipher used for ESSIV. */ essiv_hash = VasEBoot_crypto_lookup_md_by_name (hash_str); if (!essiv_hash) { VasEBoot_crypto_cipher_close (cipher); VasEBoot_crypto_cipher_close (secondary_cipher); VasEBoot_error (VasEBoot_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", hash_str); return NULL; } essiv_cipher = VasEBoot_crypto_cipher_open (ciph); if (!essiv_cipher) { VasEBoot_crypto_cipher_close (cipher); VasEBoot_crypto_cipher_close (secondary_cipher); return NULL; } } else { VasEBoot_crypto_cipher_close (cipher); VasEBoot_crypto_cipher_close (secondary_cipher); VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, "Unknown IV mode: %s", cipheriv); return NULL; } /* Configure the hash used for the AF splitter and HMAC. */ hash = VasEBoot_crypto_lookup_md_by_name (hashspec); if (!hash) { VasEBoot_crypto_cipher_close (cipher); VasEBoot_crypto_cipher_close (essiv_cipher); VasEBoot_crypto_cipher_close (secondary_cipher); VasEBoot_error (VasEBoot_ERR_FILE_NOT_FOUND, "Couldn't load %s hash", hashspec); return NULL; } newdev = VasEBoot_zalloc (sizeof (struct VasEBoot_cryptodisk)); if (!newdev) { VasEBoot_crypto_cipher_close (cipher); VasEBoot_crypto_cipher_close (essiv_cipher); VasEBoot_crypto_cipher_close (secondary_cipher); return NULL; } newdev->cipher = cipher; newdev->offset = VasEBoot_be_to_cpu32 (header.payloadOffset); newdev->source_disk = NULL; newdev->benbi_log = benbi_log; newdev->mode = mode; newdev->mode_iv = mode_iv; newdev->secondary_cipher = secondary_cipher; newdev->essiv_cipher = essiv_cipher; newdev->essiv_hash = essiv_hash; newdev->hash = hash; newdev->log_sector_size = 9; newdev->total_length = VasEBoot_disk_get_size (disk) - newdev->offset; VasEBoot_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid)); newdev->modname = "luks"; COMPILE_TIME_ASSERT (sizeof (newdev->uuid) >= sizeof (uuid)); return newdev; } static VasEBoot_err_t luks_recover_key (VasEBoot_disk_t source, VasEBoot_cryptodisk_t dev) { struct VasEBoot_luks_phdr header; VasEBoot_size_t keysize; VasEBoot_uint8_t *split_key = NULL; char passphrase[MAX_PASSPHRASE] = ""; VasEBoot_uint8_t candidate_digest[sizeof (header.mkDigest)]; unsigned i; VasEBoot_size_t length; VasEBoot_err_t err; VasEBoot_size_t max_stripes = 1; char *tmp; err = VasEBoot_disk_read (source, 0, 0, sizeof (header), &header); if (err) return err; VasEBoot_puts_ (N_("Attempting to decrypt master key...")); keysize = VasEBoot_be_to_cpu32 (header.keyBytes); if (keysize > VasEBoot_CRYPTODISK_MAX_KEYLEN) return VasEBoot_error (VasEBoot_ERR_BAD_FS, "key is too long"); for (i = 0; i < ARRAY_SIZE (header.keyblock); i++) if (VasEBoot_be_to_cpu32 (header.keyblock[i].active) == LUKS_KEY_ENABLED && VasEBoot_be_to_cpu32 (header.keyblock[i].stripes) > max_stripes) max_stripes = VasEBoot_be_to_cpu32 (header.keyblock[i].stripes); split_key = VasEBoot_malloc (keysize * max_stripes); if (!split_key) return VasEBoot_errno; /* Get the passphrase from the user. */ tmp = NULL; if (source->partition) tmp = VasEBoot_partition_get_name (source->partition); VasEBoot_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name, source->partition ? "," : "", tmp ? : "", dev->uuid); VasEBoot_free (tmp); if (!VasEBoot_password_get (passphrase, MAX_PASSPHRASE)) { VasEBoot_free (split_key); return VasEBoot_error (VasEBoot_ERR_BAD_ARGUMENT, "Passphrase not supplied"); } /* Try to recover master key from each active keyslot. */ for (i = 0; i < ARRAY_SIZE (header.keyblock); i++) { gcry_err_code_t gcry_err; VasEBoot_uint8_t candidate_key[VasEBoot_CRYPTODISK_MAX_KEYLEN]; VasEBoot_uint8_t digest[VasEBoot_CRYPTODISK_MAX_KEYLEN]; /* Check if keyslot is enabled. */ if (VasEBoot_be_to_cpu32 (header.keyblock[i].active) != LUKS_KEY_ENABLED) continue; VasEBoot_dprintf ("luks", "Trying keyslot %d\n", i); /* Calculate the PBKDF2 of the user supplied passphrase. */ gcry_err = VasEBoot_crypto_pbkdf2 (dev->hash, (VasEBoot_uint8_t *) passphrase, VasEBoot_strlen (passphrase), header.keyblock[i].passwordSalt, sizeof (header.keyblock[i].passwordSalt), VasEBoot_be_to_cpu32 (header.keyblock[i]. passwordIterations), digest, keysize); if (gcry_err) { VasEBoot_free (split_key); return VasEBoot_crypto_gcry_error (gcry_err); } VasEBoot_dprintf ("luks", "PBKDF2 done\n"); gcry_err = VasEBoot_cryptodisk_setkey (dev, digest, keysize); if (gcry_err) { VasEBoot_free (split_key); return VasEBoot_crypto_gcry_error (gcry_err); } length = (keysize * VasEBoot_be_to_cpu32 (header.keyblock[i].stripes)); /* Read and decrypt the key material from the disk. */ err = VasEBoot_disk_read (source, VasEBoot_be_to_cpu32 (header.keyblock [i].keyMaterialOffset), 0, length, split_key); if (err) { VasEBoot_free (split_key); return err; } gcry_err = VasEBoot_cryptodisk_decrypt (dev, split_key, length, 0); if (gcry_err) { VasEBoot_free (split_key); return VasEBoot_crypto_gcry_error (gcry_err); } /* Merge the decrypted key material to get the candidate master key. */ gcry_err = AF_merge (dev->hash, split_key, candidate_key, keysize, VasEBoot_be_to_cpu32 (header.keyblock[i].stripes)); if (gcry_err) { VasEBoot_free (split_key); return VasEBoot_crypto_gcry_error (gcry_err); } VasEBoot_dprintf ("luks", "candidate key recovered\n"); /* Calculate the PBKDF2 of the candidate master key. */ gcry_err = VasEBoot_crypto_pbkdf2 (dev->hash, candidate_key, VasEBoot_be_to_cpu32 (header.keyBytes), header.mkDigestSalt, sizeof (header.mkDigestSalt), VasEBoot_be_to_cpu32 (header.mkDigestIterations), candidate_digest, sizeof (candidate_digest)); if (gcry_err) { VasEBoot_free (split_key); return VasEBoot_crypto_gcry_error (gcry_err); } /* Compare the calculated PBKDF2 to the digest stored in the header to see if it's correct. */ if (VasEBoot_memcmp (candidate_digest, header.mkDigest, sizeof (header.mkDigest)) != 0) { VasEBoot_dprintf ("luks", "bad digest\n"); continue; } /* TRANSLATORS: It's a cryptographic key slot: one element of an array where each element is either empty or holds a key. */ VasEBoot_printf_ (N_("Slot %d opened\n"), i); /* Set the master key. */ gcry_err = VasEBoot_cryptodisk_setkey (dev, candidate_key, keysize); if (gcry_err) { VasEBoot_free (split_key); return VasEBoot_crypto_gcry_error (gcry_err); } VasEBoot_free (split_key); return VasEBoot_ERR_NONE; } VasEBoot_free (split_key); return VasEBoot_ACCESS_DENIED; } struct VasEBoot_cryptodisk_dev luks_crypto = { .scan = configure_ciphers, .recover_key = luks_recover_key }; VasEBoot_MOD_INIT (luks) { COMPILE_TIME_ASSERT (sizeof (((struct VasEBoot_luks_phdr *) 0)->uuid) < VasEBoot_CRYPTODISK_MAX_UUID_LENGTH); VasEBoot_cryptodisk_dev_register (&luks_crypto); } VasEBoot_MOD_FINI (luks) { VasEBoot_cryptodisk_dev_unregister (&luks_crypto); }