[Top][All Lists]
[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
- Re: [Qemu-devel] [PATCH V8 10/14] Encrypt state blobs using AES CBC encryption, Michael S. Tsirkin, 2011/09/01
- Re: [Qemu-devel] [PATCH V8 10/14] Encrypt state blobs using AES CBC encryption, Stefan Berger, 2011/09/01
- Re: [Qemu-devel] [PATCH V8 10/14] Encrypt state blobs using AES CBC encryption,
Michael S. Tsirkin <=
- Re: [Qemu-devel] [PATCH V8 10/14] Encrypt state blobs using AES CBC encryption, Michael S. Tsirkin, 2011/09/07
- Re: [Qemu-devel] [PATCH V8 10/14] Encrypt state blobs using AES CBC encryption, Stefan Berger, 2011/09/07
- Re: [Qemu-devel] [PATCH V8 10/14] Encrypt state blobs using AES CBC encryption, Michael S. Tsirkin, 2011/09/08
- Re: [Qemu-devel] [PATCH V8 10/14] Encrypt state blobs using AES CBC encryption, Stefan Berger, 2011/09/08
- Re: [Qemu-devel] [PATCH V8 10/14] Encrypt state blobs using AES CBC encryption, Michael S. Tsirkin, 2011/09/08
- Re: [Qemu-devel] [PATCH V8 10/14] Encrypt state blobs using AES CBC encryption, Stefan Berger, 2011/09/08