]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
geli v5 (including rekeying support)
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sun, 24 Apr 2011 15:15:55 +0000 (17:15 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Sun, 24 Apr 2011 15:15:55 +0000 (17:15 +0200)
grub-core/disk/cryptodisk.c
grub-core/disk/geli.c
grub-core/lib/crypto.c
include/grub/crypto.h
include/grub/cryptodisk.h

index 4a716602e8e8bf42701497e9f20c8ff04a958373..5ab607146f8213d0d26487f61d768ec0cf60c6a0 100644 (file)
@@ -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;
        }
index 3c2cbb9ce80ff96e6ba81a038f1d86cbf299e5f4..b3fa10cc5ec84df5175ce0812ab92c922ba6157a 100644 (file)
@@ -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,
index 5098f0a66e92f2fe636246f5132fdfb8f45f6288..8876cc32695435b16b57a9ba7ed1c1560eed5333 100644 (file)
@@ -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;
 
index baccbcd0646d8c897a833b71409e1fffc3b97cdd..10368d99fc57832a8344ab73386784836cd46202 100644 (file)
@@ -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;
index 11181f42d0f9fd7c88c7fa50b78fbee5aa53118e..2da3f76d2c2ef0d33768eee3d8ab2147a56ed63c 100644 (file)
@@ -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