diff --git a/grub-core/disk/luks.c b/grub-core/disk/luks.c index 8af087eea..775a10f20 100644 --- a/grub-core/disk/luks.c +++ b/grub-core/disk/luks.c @@ -74,7 +74,8 @@ typedef enum GRUB_LUKS_MODE_ECB, GRUB_LUKS_MODE_CBC, GRUB_LUKS_MODE_PCBC, - GRUB_LUKS_MODE_XTS + GRUB_LUKS_MODE_XTS, + GRUB_LUKS_MODE_LRW } luks_mode_t; typedef enum @@ -86,6 +87,10 @@ typedef enum GRUB_LUKS_MODE_IV_BENBI, } luks_mode_iv_t; +/* Our irreducible polynom is x^128+x^7+x^2+x+1. Lowest byte of it is: */ +#define GF_POLYNOM 0x87 +#define GF_SIZE 128 + struct grub_luks { char *source; @@ -102,6 +107,7 @@ struct grub_luks unsigned long id, source_id; enum grub_disk_dev_id source_dev_id; char uuid[sizeof (((struct grub_luks_phdr *) 0)->uuid) + 1]; + grub_uint8_t lrw_key[GF_SIZE / 8]; #ifdef GRUB_UTIL char *cheat; int cheat_fd; @@ -124,16 +130,13 @@ static const struct grub_arg_option options[] = {0, 0, 0, 0, 0, 0} }; -/* Our irreducible polynom is x^128+x^7+x^2+x+1. Lowest byte of it is: */ -#define POLYNOM 0x87 - static void gf_mul_x (grub_uint8_t *g) { int over = 0, over2 = 0; int j; - for (j = 0; j < 16; j++) + for (j = 0; j < GF_SIZE / 8; j++) { over2 = !!(g[j] & 0x80); g[j] <<= 1; @@ -141,7 +144,40 @@ gf_mul_x (grub_uint8_t *g) over = over2; } if (over) - g[0] ^= POLYNOM; + g[0] ^= GF_POLYNOM; +} + + +static void +gf_mul_x_be (grub_uint8_t *g) +{ + int over = 0, over2 = 0; + int j; + + for (j = GF_SIZE / 8 - 1; j >= 0; j--) + { + over2 = !!(g[j] & 0x80); + g[j] <<= 1; + g[j] |= over; + over = over2; + } + if (over) + g[GF_SIZE / 8 - 1] ^= GF_POLYNOM; +} + +static void +gf_mul_be (grub_uint8_t *o, const grub_uint8_t *a, const grub_uint8_t *b) +{ + int i; + grub_uint8_t t[GF_SIZE / 8]; + grub_memset (o, 0, GF_SIZE / 8); + grub_memcpy (t, b, GF_SIZE / 8); + for (i = 0; i < GF_SIZE; i++) + { + if (((a[GF_SIZE / 8 - i / 8 - 1] >> (i % 8))) & 1) + grub_crypto_xor (o, o, t, GF_SIZE / 8); + gf_mul_x_be (t); + } } static gcry_err_code_t @@ -249,6 +285,34 @@ luks_decrypt (const struct grub_luks *dev, } } break; + case GRUB_LUKS_MODE_LRW: + { + int j, k; + for (j = 0; + j < GRUB_DISK_SECTOR_SIZE; + j += dev->cipher->cipher->blocksize) + { + grub_uint8_t x[sz * sizeof (grub_uint32_t)]; + + gf_mul_be (x, dev->lrw_key, (grub_uint8_t *) iv); + grub_crypto_xor (data + i + j, data + i + j, x, + dev->cipher->cipher->blocksize); + err = grub_crypto_ecb_decrypt (dev->cipher, data + i + j, + data + i + j, + dev->cipher->cipher->blocksize); + if (err) + return err; + grub_crypto_xor (data + i + j, data + i + j, x, + dev->cipher->cipher->blocksize); + for (k = sz - 1; k >= 0; k++) + { + iv[k] = grub_cpu_to_be32 (grub_be_to_cpu32 (iv[k]) + 1); + if (iv[k] != 0) + break; + } + } + } + break; default: return GPG_ERR_NOT_IMPLEMENTED; } @@ -360,19 +424,33 @@ configure_ciphers (const struct grub_luks_phdr *header) grub_crypto_cipher_close (cipher); return NULL; } - if (cipher->cipher->blocksize != 16) + if (cipher->cipher->blocksize != GF_SIZE / 8) { + grub_crypto_cipher_close (cipher); grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d", cipher->cipher->blocksize); return NULL; } - if (secondary_cipher->cipher->blocksize != 16) + if (secondary_cipher->cipher->blocksize != GF_SIZE / 8) { + grub_crypto_cipher_close (cipher); grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported XTS block size: %d", secondary_cipher->cipher->blocksize); return NULL; } } + else if (grub_memcmp (ciphermode, "lrw-", sizeof ("lrw-") - 1) == 0) + { + mode = GRUB_LUKS_MODE_LRW; + cipheriv = ciphermode + sizeof ("lrw-") - 1; + if (cipher->cipher->blocksize != GF_SIZE / 8) + { + grub_crypto_cipher_close (cipher); + grub_error (GRUB_ERR_BAD_ARGUMENT, "Unsupported LRW block size: %d", + cipher->cipher->blocksize); + return NULL; + } + } else { grub_crypto_cipher_close (cipher); @@ -505,6 +583,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, for (i = 0; i < ARRAY_SIZE (header->keyblock); i++) { gcry_err_code_t gcry_err; + int real_keysize; /* Check if keyslot is enabled. */ if (grub_be_to_cpu32 (header->keyblock[i].active) != LUKS_KEY_ENABLED) @@ -530,10 +609,14 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, grub_dprintf ("luks", "PBKDF2 done\n"); + real_keysize = keysize; + if (dev->mode == GRUB_LUKS_MODE_XTS) + real_keysize /= 2; + if (dev->mode == GRUB_LUKS_MODE_LRW) + real_keysize -= dev->cipher->cipher->blocksize; + /* Set the PBKDF2 output as the cipher key. */ - gcry_err = grub_crypto_cipher_set_key (dev->cipher, digest, - (dev->mode == GRUB_LUKS_MODE_XTS) - ? (keysize / 2) : keysize); + gcry_err = grub_crypto_cipher_set_key (dev->cipher, digest, real_keysize); if (gcry_err) { grub_free (hashed_key); @@ -559,7 +642,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, if (dev->mode == GRUB_LUKS_MODE_XTS) { gcry_err = grub_crypto_cipher_set_key (dev->secondary_cipher, - digest + (keysize / 2), + digest + real_keysize, keysize / 2); if (gcry_err) { @@ -569,6 +652,10 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, } } + if (dev->mode == GRUB_LUKS_MODE_LRW) + grub_memcpy (dev->lrw_key, digest + real_keysize, + dev->cipher->cipher->blocksize); + length = grub_be_to_cpu32 (header->keyBytes) * grub_be_to_cpu32 (header->keyblock[i].stripes); @@ -634,8 +721,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, /* Set the master key. */ gcry_err = grub_crypto_cipher_set_key (dev->cipher, candidate_key, - (dev->mode == GRUB_LUKS_MODE_XTS) - ? (keysize / 2) : keysize); + real_keysize); if (gcry_err) { grub_free (hashed_key); @@ -662,7 +748,7 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, if (dev->mode == GRUB_LUKS_MODE_XTS) { gcry_err = grub_crypto_cipher_set_key (dev->secondary_cipher, - candidate_key + (keysize / 2), + candidate_key + real_keysize, keysize / 2); if (gcry_err) { @@ -672,6 +758,10 @@ luks_recover_key (grub_luks_t dev, const struct grub_luks_phdr *header, } } + if (dev->mode == GRUB_LUKS_MODE_LRW) + grub_memcpy (dev->lrw_key, candidate_key + real_keysize, + dev->cipher->cipher->blocksize); + grub_free (split_key); grub_free (hashed_key);