diff --git a/grub-core/disk/cryptodisk.c b/grub-core/disk/cryptodisk.c index 4a716602e..5ab607146 100644 --- a/grub-core/disk/cryptodisk.c +++ b/grub-core/disk/cryptodisk.c @@ -178,7 +178,7 @@ lrw_xor (const struct lrw_sector *sec, } gcry_err_code_t -grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, +grub_cryptodisk_decrypt (struct grub_cryptodisk *dev, grub_uint8_t * data, grub_size_t len, grub_disk_addr_t sector) { @@ -186,7 +186,7 @@ grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, gcry_err_code_t err; /* The only mode without IV. */ - if (dev->mode == GRUB_CRYPTODISK_MODE_ECB) + if (dev->mode == GRUB_CRYPTODISK_MODE_ECB && !dev->rekey) return grub_crypto_ecb_decrypt (dev->cipher, data, data, len); for (i = 0; i < len; i += (1U << dev->log_sector_size)) @@ -196,6 +196,18 @@ grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, / sizeof (grub_uint32_t)); grub_uint32_t iv[sz]; + if (dev->rekey) + { + grub_uint64_t zone = sector >> dev->rekey_shift; + if (zone != dev->last_rekey) + { + err = dev->rekey (dev, zone); + if (err) + return err; + dev->last_rekey = zone; + } + } + grub_memset (iv, 0, sz * sizeof (iv[0])); switch (dev->mode_iv) { @@ -291,6 +303,10 @@ grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, lrw_xor (&sec, dev, data + i); } break; + case GRUB_CRYPTODISK_MODE_ECB: + grub_crypto_ecb_decrypt (dev->cipher, data + i, data + i, + (1U << dev->log_sector_size)); + break; default: return GPG_ERR_NOT_IMPLEMENTED; } diff --git a/grub-core/disk/geli.c b/grub-core/disk/geli.c index 3c2cbb9ce..b3fa10cc5 100644 --- a/grub-core/disk/geli.c +++ b/grub-core/disk/geli.c @@ -104,6 +104,28 @@ static const struct grub_arg_option options[] = static int check_uuid, have_it; static char *search_uuid; +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; + } __attribute__ ((packed)) tohash + = { {'e', 'k', 'e', 'y'}, grub_cpu_to_le64 (zoneno) }; + grub_uint64_t key[(dev->hash->mdlen + 7) / 8]; + + 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) + return grub_crypto_gcry_error (gcry_err); + + return grub_cryptodisk_setkey (dev, (grub_uint8_t *) key, + dev->rekey_derived_size); +} + static grub_cryptodisk_t configure_ciphers (const struct grub_geli_phdr *header) { @@ -115,8 +137,8 @@ configure_ciphers (const struct grub_geli_phdr *header) /* Look for GELI magic sequence. */ if (grub_memcmp (header->magic, GELI_MAGIC, sizeof (GELI_MAGIC)) - || grub_le_to_cpu32 (header->version) > 3 - || grub_le_to_cpu32 (header->version) < 2) + || grub_le_to_cpu32 (header->version) > 5 + || grub_le_to_cpu32 (header->version) < 1) { grub_dprintf ("geli", "wrong magic %02x\n", header->magic[0]); return NULL; @@ -212,6 +234,12 @@ configure_ciphers (const struct grub_geli_phdr *header) for (newdev->log_sector_size = 0; (1U << newdev->log_sector_size) < grub_le_to_cpu32 (header->sector_size); newdev->log_sector_size++); + + if (grub_le_to_cpu32 (header->version) >= 5) + { + newdev->rekey = geli_rekey; + newdev->rekey_shift = 20; + } #if 0 grub_memcpy (newdev->uuid, uuid, sizeof (newdev->uuid)); #endif @@ -325,10 +353,23 @@ recover_key (grub_cryptodisk_t dev, const struct grub_geli_phdr *header, grub_printf ("Slot %d opened\n", i); /* Set the master key. */ - gcry_err = grub_cryptodisk_setkey (dev, candidate_key.cipher_key, - keysize); - if (gcry_err) - return grub_crypto_gcry_error (gcry_err); + if (!dev->rekey) + { + gcry_err = grub_cryptodisk_setkey (dev, candidate_key.cipher_key, + keysize); + if (gcry_err) + return grub_crypto_gcry_error (gcry_err); + } + else + { + /* 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->last_rekey = -1; + COMPILE_TIME_ASSERT (sizeof (dev->rekey_key) + >= sizeof (candidate_key.iv_key)); + } dev->iv_prefix_len = sizeof (candidate_key.iv_key); grub_memcpy (dev->iv_prefix, candidate_key.iv_key, diff --git a/grub-core/lib/crypto.c b/grub-core/lib/crypto.c index 5098f0a66..8876cc326 100644 --- a/grub-core/lib/crypto.c +++ b/grub-core/lib/crypto.c @@ -388,7 +388,7 @@ grub_crypto_hmac_fini (struct grub_crypto_hmac_handle *hnd, void *out) gcry_err_code_t grub_crypto_hmac_buffer (const struct gcry_md_spec *md, const void *key, grub_size_t keylen, - void *data, grub_size_t datalen, void *out) + const void *data, grub_size_t datalen, void *out) { struct grub_crypto_hmac_handle *hnd; diff --git a/include/grub/crypto.h b/include/grub/crypto.h index baccbcd06..10368d99f 100644 --- a/include/grub/crypto.h +++ b/include/grub/crypto.h @@ -244,7 +244,7 @@ grub_crypto_hmac_fini (struct grub_crypto_hmac_handle *hnd, void *out); gcry_err_code_t grub_crypto_hmac_buffer (const struct gcry_md_spec *md, const void *key, grub_size_t keylen, - void *data, grub_size_t datalen, void *out); + const void *data, grub_size_t datalen, void *out); extern gcry_md_spec_t _gcry_digest_spec_md5; extern gcry_md_spec_t _gcry_digest_spec_sha1; diff --git a/include/grub/cryptodisk.h b/include/grub/cryptodisk.h index 11181f42d..2da3f76d2 100644 --- a/include/grub/cryptodisk.h +++ b/include/grub/cryptodisk.h @@ -48,6 +48,12 @@ typedef enum #define GRUB_CRYPTODISK_GF_LOG_BYTES (GRUB_CRYPTODISK_GF_LOG_SIZE - 3) #define GRUB_CRYPTODISK_GF_BYTES (1U << GRUB_CRYPTODISK_GF_LOG_BYTES) +struct grub_cryptodisk; + +typedef gcry_err_code_t +(*grub_cryptodisk_rekey_func_t) (struct grub_cryptodisk *dev, + grub_uint64_t zoneno); + struct grub_cryptodisk { char *source; @@ -74,6 +80,11 @@ struct grub_cryptodisk int cheat_fd; #endif int log_sector_size; + grub_cryptodisk_rekey_func_t rekey; + int rekey_shift; + grub_uint8_t rekey_key[64]; + grub_uint64_t last_rekey; + int rekey_derived_size; struct grub_cryptodisk *next; }; typedef struct grub_cryptodisk *grub_cryptodisk_t; @@ -82,7 +93,7 @@ gcry_err_code_t grub_cryptodisk_setkey (grub_cryptodisk_t dev, grub_uint8_t *key, grub_size_t keysize); gcry_err_code_t -grub_cryptodisk_decrypt (const struct grub_cryptodisk *dev, +grub_cryptodisk_decrypt (struct grub_cryptodisk *dev, grub_uint8_t * data, grub_size_t len, grub_disk_addr_t sector); grub_err_t