qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH V8 10/14] Encrypt state blobs using AES CBC encr


From: Michael S. Tsirkin
Subject: Re: [Qemu-devel] [PATCH V8 10/14] Encrypt state blobs using AES CBC encryption
Date: Sun, 4 Sep 2011 19:58:22 +0300
User-agent: Mutt/1.5.21 (2010-09-15)

On Thu, Sep 01, 2011 at 10:23:51PM -0400, Stefan Berger wrote:
> >>Checks are added that test
> >>- whether encryption is supported follwing the revision of the directory
> >>   structure (rev>= 2)
> >You never generate rev 1 code, right?
> I did this in the previous patch that implemented rev 1 that knew
> nothing about the encryption added in rev 2.
> >So why keep that support around in code?
> >The first version merged into qemu should be revision 0 (or 1, as you like).
> I chose '1'. See patch 9:
> 
> +#define BS_DIR_REV1         1
> +
> +#define BS_DIR_REV_CURRENT  BS_DIR_REV1
> +
> 
> So I think it's the proper thing to do to increase the revision
> number from 1 to 2 since it's in two separate patches (even if they
> were to be applied immediately).

Look, the code is not merged yet and you already have
legacy with revision history to support? Why create work?

> >Don't support legacy with old version of your patch.
> >
> >>- whether a key has been provided although all data are stored in clear-text
> >>- whether a key is missing for decryption.
> >>
> >>In either one of the cases the backend reports an error message to the user
> >>and Qemu terminates.
> >>
> >>-v7:
> >>   - cleaned up function parsing key
> >>
> >>-v6:
> >>   - changed the format of the key= to take the type of encryption into
> >>     account: key=aes-cbc:0x12345... and reworked code for encryption and
> >>     decryption of blobs;
> >separate type and data:
> >keytype=aes-cbc,key=0x123  to avoid introducing more option parsing.
> >Also, are people likely to have the key in a file?
> >If yes maybe read a key from there and skip parsing completely?
> >
> I think both choices should probably exist. Now what's a good file
> format? Would we expect to find a hex number in there or should it
> always be assumed to be a binary file?

binary

> >>   - modified directory entry to hold a uint_8 describing the encryption
> >>     type (none, aes-cbc) being used for the blobs.
> >>   - incrementing revision of the directory to '2' indicating encryption
> >>     support
> >>
> >>-v5:
> >>   - -tpmdev now also gets a key parameter
> >>   - add documentation about key parameter
> >>
> >>Signed-off-by: Stefan Berger<address@hidden>
> >>
> >>---
> >>  hw/tpm_builtin.c |  285 
> >> +++++++++++++++++++++++++++++++++++++++++++++++++++++--
> >>  qemu-config.c    |   10 +
> >>  qemu-options.hx  |   22 +++-
> >>  tpm.c            |   10 +
> >>  4 files changed, 318 insertions(+), 9 deletions(-)
> >>
> >>Index: qemu-git/hw/tpm_builtin.c
> >>===================================================================
> >>--- qemu-git.orig/hw/tpm_builtin.c
> >>+++ qemu-git/hw/tpm_builtin.c
> >>@@ -27,6 +27,7 @@
> >>  #include "hw/pc.h"
> >>  #include "migration.h"
> >>  #include "sysemu.h"
> >>+#include "aes.h"
> >>
> >>  #include<libtpms/tpm_library.h>
> >>  #include<libtpms/tpm_error.h>
> >>@@ -110,14 +111,27 @@ typedef struct BSDir {
> >>      uint16_t  rev;
> >>      uint32_t  checksum;
> >>      uint32_t  num_entries;
> >>-    uint32_t  reserved[10];
> >>+    uint8_t   enctype;
> >>+    uint8_t   reserved1[3];
> >>+    uint32_t  reserved[8];
> >>      BSEntry   entries[BS_DIR_MAX_NUM_ENTRIES];
> >>  } __attribute__((packed)) BSDir;
> >>
> >>
> >>  #define BS_DIR_REV1         1
> >>+/* rev 2 added encryption */
> >>+#define BS_DIR_REV2         2
> >>
> >>-#define BS_DIR_REV_CURRENT  BS_DIR_REV1
> >>+
> >>+#define BS_DIR_REV_CURRENT  BS_DIR_REV2
> >>+
> >>+/* above enctype */
> >>+enum BSEnctype {
> >>+    BS_DIR_ENCTYPE_NONE = 0,
> >>+    BS_DIR_ENCTYPE_AES_CBC,
> >>+
> >>+    BS_DIR_ENCTYPE_LAST,
> >>+};
> >>
> >>  /* local variables */
> >>
> >>@@ -150,6 +164,11 @@ static const unsigned char tpm_std_fatal
> >>
> >>  static char dev_description[80];
> >>
> >>+static struct enckey {
> >>+    uint8_t enctype;
> >>+    AES_KEY tpm_enc_key;
> >>+    AES_KEY tpm_dec_key;
> >>+} enckey;
> >>
> >>  static int tpm_builtin_load_sized_data_from_bs(BlockDriverState *bs,
> >>                                                 enum BSEntryType be,
> >>@@ -264,7 +283,7 @@ static uint32_t tpm_builtin_calc_dir_che
> >>
> >>  static bool tpm_builtin_is_valid_bsdir(BSDir *dir)
> >>  {
> >>-    if (dir->rev != BS_DIR_REV_CURRENT ||
> >>+    if (dir->rev>  BS_DIR_REV_CURRENT ||
> >>          dir->num_entries>  BS_DIR_MAX_NUM_ENTRIES) {
> >>          return false;
> >>      }
> >>@@ -295,6 +314,33 @@ static bool tpm_builtin_has_valid_conten
> >>      return rc;
> >>  }
> >>
> >>+static bool tpm_builtin_supports_encryption(const BSDir *dir)
> >>+{
> >>+    return (dir->rev>= BS_DIR_REV2);
> >>+}
> >>+
> >>+
> >>+static bool tpm_builtin_has_missing_key(const BSDir *dir)
> >>+{
> >>+    return ((dir->enctype   != BS_DIR_ENCTYPE_NONE)&&
> >>+            (enckey.enctype == BS_DIR_ENCTYPE_NONE));
> >>+}
> >>+
> >>+
> >>+static bool tpm_builtin_has_unnecessary_key(const BSDir *dir)
> >>+{
> >>+    return (((dir->enctype   == BS_DIR_ENCTYPE_NONE)&&
> >>+             (enckey.enctype != BS_DIR_ENCTYPE_NONE)) ||
> >>+            ((!tpm_builtin_supports_encryption(dir))&&
> >>+             (enckey.enctype != BS_DIR_ENCTYPE_NONE)));
> >>+}
> >>+
> >>+
> >>+static bool tpm_builtin_uses_unsupported_enctype(const BSDir *dir)
> >>+{
> >>+    return (dir->enctype>= BS_DIR_ENCTYPE_LAST);
> >>+}
> >>+
> >>
> >>  static int tpm_builtin_create_blank_dir(BlockDriverState *bs)
> >>  {
> >>@@ -306,6 +352,7 @@ static int tpm_builtin_create_blank_dir(
> >>      dir = (BSDir *)buf;
> >>      dir->rev = BS_DIR_REV_CURRENT;
> >>      dir->num_entries = 0;
> >>+    dir->enctype = enckey.enctype;
> >>
> >>      dir->checksum = tpm_builtin_calc_dir_checksum(dir);
> >>
> >>@@ -407,6 +454,38 @@ static int tpm_builtin_startup_bs(BlockD
> >>
> >>      tpm_builtin_dir_be_to_cpu(dir);
> >>
> >>+    if (tpm_builtin_is_valid_bsdir(dir)) {
> >>+        if (tpm_builtin_supports_encryption(dir)&&
> >>+            tpm_builtin_has_missing_key(dir)) {
> >>+            fprintf(stderr,
> >>+                    "tpm: the data are encrypted but I am missing the 
> >>key.\n");
> >>+            rc = -EIO;
> >>+            goto err_exit;
> >>+        }
> >>+        if (tpm_builtin_has_unnecessary_key(dir)) {
> >>+            fprintf(stderr,
> >>+                    "tpm: I have a key but the data are not encrypted.\n");
> >>+            rc = -EIO;
> >>+            goto err_exit;
> >>+        }
> >>+        if (tpm_builtin_supports_encryption(dir)&&
> >>+            tpm_builtin_uses_unsupported_enctype(dir)) {
> >>+            fprintf(stderr,
> >>+                    "tpm: State is encrypted with an unsupported 
> >>encryption "
> >>+                    "scheme.\n");
> >>+            rc = -EIO;
> >>+            goto err_exit;
> >>+        }
> >>+        if (tpm_builtin_supports_encryption(dir)&&
> >>+            (dir->enctype != BS_DIR_ENCTYPE_NONE)&&
> >>+            !tpm_builtin_has_valid_content(dir)) {
> >>+            fprintf(stderr, "tpm: cannot read the data - "
> >>+                    "is this the wrong key?\n");
> >>+            rc = -EIO;
> >>+            goto err_exit;
> >>+        }
> >>+    }
> >>+
> >>      if (!tpm_builtin_is_valid_bsdir(dir) ||
> >>          !tpm_builtin_has_valid_content(dir)) {
> >>          /* if it's encrypted and has something else than null-content,
> >>@@ -569,6 +648,105 @@ static int set_bs_entry_size_crc(BlockDr
> >>  }
> >>
> >>
> >>+static int tpm_builtin_blocksize_roundup(uint8_t enctype, int plainsize)
> >>+{
> >>+    switch (enctype) {
> >>+    case BS_DIR_ENCTYPE_NONE:
> >>+        return plainsize;
> >>+    case BS_DIR_ENCTYPE_AES_CBC:
> >>+        return ALIGN(plainsize, AES_BLOCK_SIZE);
> >>+    default:
> >>+        assert(false);
> >>+        return 0;
> >>+    }
> >>+}
> >>+
> >>+
> >>+static int tpm_builtin_bdrv_pread(BlockDriverState *bs, int64_t offset,
> >>+                                  void *buf, int count,
> >>+                                  enum BSEntryType type)
> >>+{
> >>+    int ret;
> >>+    union {
> >>+        uint64_t ll[2];
> >>+        uint8_t b[16];
> >>+    } ivec;
> >>+    int toread = count;
> >>+
> >>+    toread = tpm_builtin_blocksize_roundup(enckey.enctype, count);
> >>+
> >>+    ret = bdrv_pread(bs, offset, buf, toread);
> >>+
> >>+    if (ret != toread) {
> >>+        return ret;
> >>+    }
> >>+
> >>+    switch (enckey.enctype) {
> >>+    case BS_DIR_ENCTYPE_NONE:
> >>+        break;
> >>+    case BS_DIR_ENCTYPE_AES_CBC:
> >>+        ivec.ll[0] = cpu_to_be64(type);
> >>+        ivec.ll[1] = 0;
> >>+
> >>+        AES_cbc_encrypt(buf, buf, toread,&enckey.tpm_dec_key, ivec.b, 0);
> >>+        break;
> >>+    default:
> >>+        assert(false);
> >>+    }
> >>+
> >>+    return count;
> >>+}
> >>+
> >>+
> >>+static int tpm_builtin_bdrv_pwrite(BlockDriverState *bs, int64_t offset,
> >>+                                   void *buf, int count,
> >>+                                   enum BSEntryType type)
> >>+{
> >>+    int ret;
> >>+    union {
> >>+        uint64_t ll[2];
> >>+        uint8_t b[16];
> >>+    } ivec;
> >>+    int towrite = count;
> >>+    void *out_buf = buf;
> >>+
> >>+    switch (enckey.enctype) {
> >>+    case BS_DIR_ENCTYPE_NONE:
> >>+        break;
> >>+    case BS_DIR_ENCTYPE_AES_CBC:
> >>+        ivec.ll[0] = cpu_to_be64(type);
> >>+        ivec.ll[1] = 0;
> >>+
> >>+        towrite = ALIGN(count, AES_BLOCK_SIZE);
> >>+
> >>+        if (towrite != count) {
> >>+            out_buf = g_malloc(towrite);
> >>+
> >>+            if (out_buf == NULL) {
> >>+                return -ENOMEM;
> >>+            }
> >>+        }
> >>+
> >>+        AES_cbc_encrypt(buf, out_buf, towrite,&enckey.tpm_enc_key, ivec.b, 
> >>1);
> >>+        break;
> >>+    default:
> >>+        assert(false);
> >>+    }
> >>+
> >>+    ret = bdrv_pwrite(bs, offset, out_buf, towrite);
> >>+
> >>+    if (out_buf != buf) {
> >>+        g_free(out_buf);
> >>+    }
> >>+
> >>+    if (ret == towrite) {
> >>+        return count;
> >>+    }
> >>+
> >>+    return ret;
> >>+}
> >>+
> >>+
> >>  static int tpm_builtin_load_sized_data_from_bs(BlockDriverState *bs,
> >>                                                 enum BSEntryType be,
> >>                                                 TPMSizedBuffer *tsb)
> >>@@ -594,7 +772,7 @@ static int tpm_builtin_load_sized_data_f
> >>          goto err_exit;
> >>      }
> >>
> >>-    tsb->buffer = g_malloc(entry.blobsize);
> >>+    tsb->buffer = g_malloc(ALIGN(entry.blobsize, AES_BLOCK_SIZE));
> >>      if (!tsb->buffer) {
> >>          rc = -ENOMEM;
> >>          goto err_exit;
> >>@@ -602,7 +780,8 @@ static int tpm_builtin_load_sized_data_f
> >>
> >>      tsb->size = entry.blobsize;
> >>
> >>-    if (bdrv_pread(bs, entry.offset, tsb->buffer, tsb->size) != tsb->size) 
> >>{
> >>+    if (tpm_builtin_bdrv_pread(bs, entry.offset, tsb->buffer, tsb->size, 
> >>be) !=
> >>+        tsb->size) {
> >>          clear_sized_buffer(tsb);
> >>          fprintf(stderr, "tpm: Error while reading stored data!\n");
> >>          rc = -EIO;
> >>@@ -667,7 +846,8 @@ static int tpm_builtin_save_sized_data_t
> >>      }
> >>
> >>      if (data_len>  0) {
> >>-        if (bdrv_pwrite(bs, entry.offset, data, data_len) != data_len) {
> >>+        if (tpm_builtin_bdrv_pwrite(bs, entry.offset, data, data_len, be) 
> >>!=
> >>+            data_len) {
> >>              rc = -EIO;
> >>          }
> >>      }
> >>@@ -1492,11 +1672,77 @@ static const char *tpm_builtin_create_de
> >>  }
> >>
> >>
> >>+/*
> >>+ * Convert a string of hex digits to its binary representation.
> >>+ * The conversion stops once either the maximum size of the binary
> >>+ * array has been reached or an non-hex digit was encountered.
> >>+ */
> >Don't we care about non-hex following a valid key?
> Do you have an example? This function is meant to convert 0x1234567
> to a binary stream. An equivalent would be 0x1234567X, since it
> would terminate parsing on 'X'.

But than might be a typo.
0x1234567O
for example stops parsing at 'O' since this is not a digit but
looks like one.


> >Two empty lines in a row :)
> >
> Probably this is not the only occurrence... Is this a problem?

Yes. They aren't helpful. checkpatch should complain about these
not sure whether it does.

> >>+static bool tpm_builtin_parse_as_hexkey(const char *rawkey,
> >>+                                        unsigned char *keyvalue,
> >>+                                        int *keysize)
> >>+{
> >>+    size_t c = 0;
> >>+
> >>+    /* skip over leading '0x' */
> >>+    if (!strncmp(rawkey, "0x", 2)) {
> >>+        rawkey += 2;
> >>+    }
> >>+
> >>+    c = stream_to_bin(rawkey, keyvalue, *keysize);
> >>+
> >>+    if (c == 256/4) {
> >>+        *keysize = 256;
> >>+    } else if (c>= 192/4) {
> >>+        *keysize = 192;
> >>+    } else if (c>= 128/4) {
> >>+        *keysize = 128;
> >>+    } else {
> >>+        return false;
> >Want to tell the user what went wrong?
> Here's what the key parser handles:
> - all keys >= 256 bits are truncated to 256 bits
> - all keys >= 192 bits are truncated to 192 bits
> - all keys >= 128 bits are truncated to 128 bits
> - all keys < 128 bits are assumed to not be given as a hexadecimal
> number but the string itself is the key, i.e. 'HELLOWORLD' becomes a
> valid key.

Oh my. I presume this makes sense in some world ...

> >Also, you don't allow skipping leading zeroes?
> An AES key should be allowed to have leading zeros, no?
> >>+    }
> >>+
> >>+    return true;
> >Always put spaces around /.
> >But where does the /4 come from? 4 bits per character?
> >
> c is the number of 'nibbles'. 4 bits in a nibble - that's what the
> /4 comes from.
> >>+}
> >>+
> >>+
> >>  static TPMBackend *tpm_builtin_create(QemuOpts *opts, const char *id,
> >>                                        const char *model)
> >>  {
> >>      TPMBackend *driver;
> >>      const char *value;
> >>+    unsigned char keyvalue[256/8];
> >>+    int keysize = sizeof(keyvalue);
> >>
> >>      driver = g_malloc(sizeof(TPMBackend));
> >>      if (!driver) {
> >>@@ -1523,6 +1769,33 @@ static TPMBackend *tpm_builtin_create(Qe
> >>          goto err_exit;
> >>      }
> >>
> >>+    value = qemu_opt_get(opts, "key");
> >>+    if (value) {
> >>+        if (!strncasecmp(value, "aes-cbc:", 8)) {
> >>+            memset(keyvalue, 0x0, sizeof(keyvalue));
> >>+
> >>+            if (!tpm_builtin_parse_as_hexkey(&value[8], 
> >>keyvalue,&keysize)) {
> >>+                keysize = 128;
> >>+                strncpy((char *)keyvalue, value, 128/8);
> Here first the input is attempted to be parsed as hex key and if
> that fails the input string is taken as the key. It should be
> &value[8] here -- so that's a bug.
> 
>   Stefan

These should be different options.

key_format=hex/string

etc.

-- 
MST



reply via email to

[Prev in Thread] Current Thread [Next in Thread]