}
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)
{
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))
/ 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)
{
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;
}
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)
{
/* 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;
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
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,
#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;
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;
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