From f42b774020839b1e07c5fa0ad7be4735d35cc705 Mon Sep 17 00:00:00 2001 From: Maxim Fomin Date: Fri, 8 Jan 2021 20:00:31 +0000 Subject: [PATCH] Support for multiple LUKS passphrase attempts --- grub-core/disk/luks.c | 273 ++++++++++++++++++++++-------------------- 1 file changed, 141 insertions(+), 132 deletions(-) diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index eea85338d..3f98df287 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -34,6 +34,8 @@ GRUB_MOD_LICENSE ("GPLv3+"); #define LUKS_KEY_ENABLED 0x00AC71F3 +#define LUKS_PASSPHRASE_ATTEMPTS 3 + /* On disk LUKS header */ struct grub_luks_phdr { @@ -182,6 +184,7 @@ luks_recover_key (grub_disk_t source, grub_size_t max_stripes = 1; char *tmp; grub_uint32_t sector; + unsigned int attempts = LUKS_PASSPHRASE_ATTEMPTS; err = GRUB_ERR_NONE; @@ -211,145 +214,151 @@ luks_recover_key (grub_disk_t source, if (!split_key) return grub_errno; - if (keyfile_bytes) - { - /* Use bytestring from key file as passphrase */ - passphrase = keyfile_bytes; - passphrase_length = keyfile_bytes_size; - } - else + while (attempts) { - /* Get the passphrase from the user. */ - tmp = NULL; - if (source->partition) - tmp = grub_partition_get_name (source->partition); - grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name, - source->partition ? "," : "", tmp ? : "", dev->uuid); - grub_free (tmp); - if (!grub_password_get (interactive_passphrase, MAX_PASSPHRASE)) + if (keyfile_bytes) { - grub_free (split_key); - return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied"); + /* Use bytestring from key file as passphrase */ + passphrase = keyfile_bytes; + passphrase_length = keyfile_bytes_size; + keyfile_bytes = NULL; /* use it only once */ + } + else + { + /* Get the passphrase from the user. */ + tmp = NULL; + if (source->partition) + tmp = grub_partition_get_name (source->partition); + grub_printf_ (N_("Enter passphrase for %s%s%s (%s): "), source->name, + source->partition ? "," : "", tmp ? : "", dev->uuid); + grub_free (tmp); + if (!grub_password_get (interactive_passphrase, MAX_PASSPHRASE)) + { + grub_free (split_key); + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Passphrase not supplied"); + } + + passphrase = (grub_uint8_t *)interactive_passphrase; + passphrase_length = grub_strlen (interactive_passphrase); } - passphrase = (grub_uint8_t *)interactive_passphrase; - passphrase_length = grub_strlen (interactive_passphrase); + /* Try to recover master key from each active keyslot. */ + for (i = 0; i < ARRAY_SIZE (header.keyblock); i++) + { + gcry_err_code_t gcry_err; + grub_uint8_t candidate_key[GRUB_CRYPTODISK_MAX_KEYLEN]; + grub_uint8_t digest[GRUB_CRYPTODISK_MAX_KEYLEN]; + + /* Check if keyslot is enabled. */ + if (grub_be_to_cpu32 (header.keyblock[i].active) != LUKS_KEY_ENABLED) + continue; + + grub_dprintf ("luks", "Trying keyslot %d\n", i); + + /* Calculate the PBKDF2 of the user supplied passphrase. */ + gcry_err = grub_crypto_pbkdf2 (dev->hash, (grub_uint8_t *) passphrase, + passphrase_length, + header.keyblock[i].passwordSalt, + sizeof (header.keyblock[i].passwordSalt), + grub_be_to_cpu32 (header.keyblock[i]. + passwordIterations), + digest, keysize); + + if (gcry_err) + { + grub_free (split_key); + return grub_crypto_gcry_error (gcry_err); + } + + grub_dprintf ("luks", "PBKDF2 done\n"); + + gcry_err = grub_cryptodisk_setkey (dev, digest, keysize); + if (gcry_err) + { + grub_free (split_key); + return grub_crypto_gcry_error (gcry_err); + } + + sector = grub_be_to_cpu32 (header.keyblock[i].keyMaterialOffset); + length = (keysize * grub_be_to_cpu32 (header.keyblock[i].stripes)); + + /* Read and decrypt the key material from the disk. */ + if (hdr) + { + grub_file_seek (hdr, sector * 512); + if (grub_file_read (hdr, split_key, length) != (grub_ssize_t)length) + err = GRUB_ERR_READ_ERROR; + } + else + err = grub_disk_read (source, sector, 0, length, split_key); + if (err) + { + grub_free (split_key); + return err; + } + + gcry_err = grub_cryptodisk_decrypt (dev, split_key, length, 0, + GRUB_LUKS1_LOG_SECTOR_SIZE); + if (gcry_err) + { + grub_free (split_key); + return grub_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, + grub_be_to_cpu32 (header.keyblock[i].stripes)); + if (gcry_err) + { + grub_free (split_key); + return grub_crypto_gcry_error (gcry_err); + } + + grub_dprintf ("luks", "candidate key recovered\n"); + + /* Calculate the PBKDF2 of the candidate master key. */ + gcry_err = grub_crypto_pbkdf2 (dev->hash, candidate_key, + grub_be_to_cpu32 (header.keyBytes), + header.mkDigestSalt, + sizeof (header.mkDigestSalt), + grub_be_to_cpu32 + (header.mkDigestIterations), + candidate_digest, + sizeof (candidate_digest)); + if (gcry_err) + { + grub_free (split_key); + return grub_crypto_gcry_error (gcry_err); + } + + /* Compare the calculated PBKDF2 to the digest stored + in the header to see if it's correct. */ + if (grub_memcmp (candidate_digest, header.mkDigest, + sizeof (header.mkDigest)) != 0) + { + grub_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. */ + grub_printf_ (N_("Slot %d opened\n"), i); + + /* Set the master key. */ + gcry_err = grub_cryptodisk_setkey (dev, candidate_key, keysize); + if (gcry_err) + { + grub_free (split_key); + return grub_crypto_gcry_error (gcry_err); + } - } + grub_free (split_key); - /* Try to recover master key from each active keyslot. */ - for (i = 0; i < ARRAY_SIZE (header.keyblock); i++) - { - gcry_err_code_t gcry_err; - grub_uint8_t candidate_key[GRUB_CRYPTODISK_MAX_KEYLEN]; - grub_uint8_t digest[GRUB_CRYPTODISK_MAX_KEYLEN]; - - /* Check if keyslot is enabled. */ - if (grub_be_to_cpu32 (header.keyblock[i].active) != LUKS_KEY_ENABLED) - continue; - - grub_dprintf ("luks", "Trying keyslot %d\n", i); - - /* Calculate the PBKDF2 of the user supplied passphrase. */ - gcry_err = grub_crypto_pbkdf2 (dev->hash, (grub_uint8_t *) passphrase, - passphrase_length, - header.keyblock[i].passwordSalt, - sizeof (header.keyblock[i].passwordSalt), - grub_be_to_cpu32 (header.keyblock[i]. - passwordIterations), - digest, keysize); - - if (gcry_err) - { - grub_free (split_key); - return grub_crypto_gcry_error (gcry_err); - } - - grub_dprintf ("luks", "PBKDF2 done\n"); - - gcry_err = grub_cryptodisk_setkey (dev, digest, keysize); - if (gcry_err) - { - grub_free (split_key); - return grub_crypto_gcry_error (gcry_err); - } - - sector = grub_be_to_cpu32 (header.keyblock[i].keyMaterialOffset); - length = (keysize * grub_be_to_cpu32 (header.keyblock[i].stripes)); - - /* Read and decrypt the key material from the disk. */ - if (hdr) - { - grub_file_seek (hdr, sector * 512); - if (grub_file_read (hdr, split_key, length) != (grub_ssize_t)length) - err = GRUB_ERR_READ_ERROR; + return GRUB_ERR_NONE; } - else - err = grub_disk_read (source, sector, 0, length, split_key); - if (err) - { - grub_free (split_key); - return err; - } - - gcry_err = grub_cryptodisk_decrypt (dev, split_key, length, 0, - GRUB_LUKS1_LOG_SECTOR_SIZE); - if (gcry_err) - { - grub_free (split_key); - return grub_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, - grub_be_to_cpu32 (header.keyblock[i].stripes)); - if (gcry_err) - { - grub_free (split_key); - return grub_crypto_gcry_error (gcry_err); - } - - grub_dprintf ("luks", "candidate key recovered\n"); - - /* Calculate the PBKDF2 of the candidate master key. */ - gcry_err = grub_crypto_pbkdf2 (dev->hash, candidate_key, - grub_be_to_cpu32 (header.keyBytes), - header.mkDigestSalt, - sizeof (header.mkDigestSalt), - grub_be_to_cpu32 - (header.mkDigestIterations), - candidate_digest, - sizeof (candidate_digest)); - if (gcry_err) - { - grub_free (split_key); - return grub_crypto_gcry_error (gcry_err); - } - - /* Compare the calculated PBKDF2 to the digest stored - in the header to see if it's correct. */ - if (grub_memcmp (candidate_digest, header.mkDigest, - sizeof (header.mkDigest)) != 0) - { - grub_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. */ - grub_printf_ (N_("Slot %d opened\n"), i); - - /* Set the master key. */ - gcry_err = grub_cryptodisk_setkey (dev, candidate_key, keysize); - if (gcry_err) - { - grub_free (split_key); - return grub_crypto_gcry_error (gcry_err); - } - - grub_free (split_key); - - return GRUB_ERR_NONE; + grub_printf_ (N_("Failed to decrypt master key.\n")); + if (--attempts) grub_printf_ (N_("%u attempt%s remaining.\n"), attempts, + (attempts==1) ? "" : "s"); } grub_free (split_key); -- 2.30.0