diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 5ab607146..bf0b1cde2 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -234,6 +234,11 @@ grub_cryptodisk_decrypt (struct grub_cryptodisk *dev, case GRUB_CRYPTODISK_MODE_IV_PLAIN: iv[0] = grub_cpu_to_le32 (sector & 0xFFFFFFFF); break; + case GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64: + iv[1] = grub_cpu_to_le32 (sector >> (32 - dev->log_sector_size)); + iv[0] = grub_cpu_to_le32 ((sector << dev->log_sector_size) + & 0xFFFFFFFF); + break; case GRUB_CRYPTODISK_MODE_IV_BENBI: { grub_uint64_t num = (sector << dev->benbi_log) + 1; diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index 6ffc7c26e..334a06340 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -95,6 +95,7 @@ const char *algorithms[] = { [0x0b] = "aes", /* FIXME: 0x10 is null. */ [0x15] = "camellia128", + [0x16] = "aes" }; #define MAX_PASSPHRASE 256 @@ -135,7 +136,7 @@ static grub_cryptodisk_t configure_ciphers (const struct grub_geli_phdr *header) { grub_cryptodisk_t newdev; - grub_crypto_cipher_handle_t cipher = NULL; + grub_crypto_cipher_handle_t cipher = NULL, secondary_cipher = NULL; const struct gcry_cipher_spec *ciph; const char *ciphername = NULL; const gcry_md_spec_t *hash = NULL, *iv_hash = NULL; @@ -196,6 +197,13 @@ configure_ciphers (const struct grub_geli_phdr *header) if (!cipher) return NULL; + if (grub_le_to_cpu16 (header->alg) == 0x16) + { + secondary_cipher = grub_crypto_cipher_open (ciph); + if (!secondary_cipher) + return NULL; + } + if (grub_le_to_cpu16 (header->keylen) > 1024) { grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid keysize %d", @@ -225,12 +233,20 @@ configure_ciphers (const struct grub_geli_phdr *header) if (!newdev) return NULL; newdev->cipher = cipher; + newdev->secondary_cipher = secondary_cipher; newdev->offset = 0; newdev->source_disk = NULL; newdev->benbi_log = 0; - newdev->mode = GRUB_CRYPTODISK_MODE_CBC; - newdev->mode_iv = GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH; - newdev->secondary_cipher = NULL; + if (grub_le_to_cpu16 (header->alg) == 0x16) + { + 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; + } newdev->essiv_cipher = NULL; newdev->essiv_hash = NULL; newdev->hash = hash; @@ -360,17 +376,23 @@ recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header, /* Set the master key. */ if (!dev->rekey) { + grub_size_t real_keysize = keysize; + if (grub_le_to_cpu16 (header->alg) == 0x16) + real_keysize *= 2; gcry_err = grub_cryptodisk_setkey (dev, candidate_key.cipher_key, - keysize); + real_keysize); if (gcry_err) return grub_crypto_gcry_error (gcry_err); } else { + grub_size_t real_keysize = keysize; + if (grub_le_to_cpu16 (header->alg) == 0x16) + real_keysize *= 2; /* For a reason I don't know, the IV key is used in rekeying. */ grub_memcpy (dev->rekey_key, candidate_key.iv_key, sizeof (candidate_key.iv_key)); - dev->rekey_derived_size = keysize; + dev->rekey_derived_size = real_keysize; dev->last_rekey = -1; COMPILE_TIME_ASSERT (sizeof (dev->rekey_key) >= sizeof (candidate_key.iv_key)); diff --git a/include/grub/cryptodisk.h b/include/grub/cryptodisk.h index 2da3f76d2..1911c04be 100644 --- a/include/grub/cryptodisk.h +++ b/include/grub/cryptodisk.h @@ -38,6 +38,7 @@ typedef enum GRUB_CRYPTODISK_MODE_IV_PLAIN64, GRUB_CRYPTODISK_MODE_IV_ESSIV, GRUB_CRYPTODISK_MODE_IV_BENBI, + GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64, GRUB_CRYPTODISK_MODE_IV_BYTECOUNT64_HASH } grub_cryptodisk_mode_iv_t;