Files
xen/stubdom/vtpmmgr/disk_crypto.c
T
Daniel De Graaf 5c3705c900 vtpmmgr: add TPM group support
This is a complete rewrite of the disk format and key hierarchy for the
TPM Manager. The new format supports multiple groups of vTPMs which
define the permitted configurations where a given vTPM's keys are
available, allowing upgrades of critical components while retaining the
secrecy of cryptographic keys.

New features of the TPM Manager are explained in the README and in the
definitions of the management commands in vtpm_manager.h.

New features for vTPMs:

1. The size of the state blob for a vTPM is expanded from 52 to 64
bytes in order to support future vTPMs using SHA-2/3 instead of SHA-1.

2. vTPMs can obtain a quote from the physical TPM with certain
resettable PCRs set to include information about the vTPM. This can be
used by a vTPM to provide evidence of its integrity, including the
secrecy of its EK, and for deep quotes.

Some additional changes made by this rewrite that may impact existing
users:

1. The value of WELLKNOWN_OWNER_AUTH was incorrect for the physical TPM;
the convention is to use all zero bits for well-known authentication
values, not all one bits.

2. Randomly generating the owner auth value for the physical TPM is no
longer supported, as it prevents later creation or certification of
AIKs (which the old manager did not support).

3. The vTPM Manager needs to be provisioned with a PCR composite and an
upgrade authority's public key before it will save data across boots.

The current implementation still has some limitations:
 * 5 valid system PCR selections per group
 * The vTPM Manager's disk can use at most 2MB of space
 * The vTPM domain's build hash is always set to null/zero

Most of the code relating to upgrade and rollback protection is
currently stubbed out, but future versions can add:
 * Support for using the TPM's monotonic counter to prevent rollback
   of vTPM data by taking and restoring disk snapshots
 * Masking the master disk encryption key using a value stored in the
   TPM's NVRAM so that revocation of old data is possible without
   relying on all previously authorized software stacks to respect the
   monotonic counter's value

Signed-off-by: Daniel De Graaf <dgdegra@tycho.nsa.gov>
Acked-by: Ian Campbell <ian.campbell@citrix.com>
2014-04-23 11:56:41 +01:00

232 lines
5.0 KiB
C

#include <inttypes.h>
#include <mini-os/byteorder.h>
#include <polarssl/aes.h>
#include <polarssl/sha2.h>
#include <polarssl/ctr_drbg.h>
#include "log.h"
#include "vtpmmgr.h"
#include "vtpm_disk.h"
#include "disk_io.h"
#include "disk_crypto.h"
// XXX defining this stubs out all disk encryption for easier debugging
#undef DISK_IS_PLAINTEXT
void do_random(void *buf, size_t size)
{
int rc = ctr_drbg_random(&vtpm_globals.ctr_drbg, buf, size);
if (rc) abort();
}
void aes_setup(aes_context *ctx, const struct key128 *key)
{
aes_setkey_enc(ctx, (void*)key, 128);
}
static void aes_encrypt_ecb(void *target, const void *src, const aes_context *key_e)
{
aes_crypt_ecb((void*)key_e, AES_ENCRYPT, src, target);
}
void aes_encrypt_one(void *target, const void *src, const struct key128 *key)
{
aes_context ctx;
aes_setkey_enc(&ctx, (void*)key, 128);
aes_crypt_ecb(&ctx, AES_ENCRYPT, src, target);
}
void aes_decrypt_one(void *target, const void *src, const struct key128 *key)
{
aes_context ctx;
aes_setkey_dec(&ctx, (void*)key, 128);
aes_crypt_ecb(&ctx, AES_DECRYPT, src, target);
}
static void aes_ctr_one(uint64_t out[2], uint64_t ctr[2], const aes_context *key_e)
{
#ifdef DISK_IS_PLAINTEXT
memset(out, 0, 16);
#else
aes_encrypt_ecb(out, ctr, key_e);
#endif
ctr[1]++;
}
void aes_encrypt_ctr(void *target, size_t target_size, const void *srcv, size_t pt_size, const aes_context *key_e)
{
uint64_t ctr[2];
uint64_t tmp[2];
uint64_t *dst = target;
const uint64_t *src = srcv;
do_random(ctr, sizeof(ctr));
dst[0] = ctr[0];
dst[1] = ctr[1];
dst += 2;
target_size -= 16;
if (pt_size > target_size)
abort(); // invalid argument: target too small for plaintext
while (pt_size >= 16) {
aes_ctr_one(tmp, ctr, key_e);
dst[0] = tmp[0] ^ src[0];
dst[1] = tmp[1] ^ src[1];
dst += 2;
src += 2;
pt_size -= 16;
target_size -= 16;
}
if (pt_size) {
uint64_t stmp[2];
uint64_t dtmp[2];
memset(stmp, 0, 16);
memcpy(stmp, src, pt_size);
aes_ctr_one(tmp, ctr, key_e);
dtmp[0] = tmp[0] ^ stmp[0];
dtmp[1] = tmp[1] ^ stmp[1];
if (target_size < 16) {
memcpy(dst, dtmp, target_size);
return;
} else {
memcpy(dst, dtmp, 16);
target_size -= 16;
}
}
while (target_size >= 16) {
aes_ctr_one(dst, ctr, key_e);
dst += 2;
target_size -= 16;
}
if (target_size)
abort(); // invalid argument: overlarge target size is not a full block
}
void aes_decrypt_ctr(void *target, size_t pt_size, const void *srcv, size_t src_size, const aes_context *key_e)
{
uint64_t ctr[2];
uint64_t tmp[2];
uint64_t *dst = target;
const uint64_t *src = srcv;
ctr[0] = src[0];
ctr[1] = src[1];
src += 2;
src_size -= 16;
if (pt_size > src_size)
abort(); // invalid argument: source too small for plaintext
// we discard src_size now
while (pt_size >= 16) {
aes_ctr_one(tmp, ctr, key_e);
dst[0] = tmp[0] ^ src[0];
dst[1] = tmp[1] ^ src[1];
dst += 2;
src += 2;
pt_size -= 16;
}
if (pt_size) {
uint64_t stmp[2];
uint64_t dtmp[2];
memset(stmp, 0, 16);
memcpy(stmp, src, pt_size);
aes_ctr_one(tmp, ctr, key_e);
dtmp[0] = tmp[0] ^ stmp[0];
dtmp[1] = tmp[1] ^ stmp[1];
memcpy(dst, dtmp, pt_size);
}
}
static void shl_128_mod_hex87(struct mac128 *dst, const struct mac128 *src)
{
int i;
int carry = 0x87 * !!(src->bits[0] & 0x80);
for(i=0; i < 15; i++)
dst->bits[i] = (src->bits[i] << 1) | (src->bits[i+1] >> 7);
dst->bits[15] = (src->bits[15] << 1) ^ carry;
}
static void xor128(struct mac128 *dst, const struct mac128 *s1, const struct mac128 *s2)
{
int i;
for(i=0; i < 16; i++)
dst->bits[i] = s1->bits[i] ^ s2->bits[i];
}
void aes_cmac(struct mac128 *target, const void *src, size_t size, const aes_context *key)
{
const struct mac128 *M = src;
struct mac128 x, y, L, K1, K2;
int i;
size_t bsize = (size - 1) / 16;
memset(&x, 0, sizeof(x));
aes_encrypt_ecb(&L, &x, key);
shl_128_mod_hex87(&K1, &L);
shl_128_mod_hex87(&K2, &K1);
for(i=0; i < bsize; i++) {
xor128(&y, &x, &M[i]);
aes_encrypt_ecb(&x, &y, key);
}
if (size & 0xF) {
struct mac128 z;
memset(&z, 0, sizeof(z));
memcpy(&z, M + bsize, size & 0xF);
xor128(&y, &x, &K2);
xor128(&x, &y, &z);
} else {
xor128(&y, &x, &K1);
xor128(&x, &y, M + bsize);
}
aes_encrypt_ecb(target, &x, key);
}
static int verify_128(const void *a, const void* b)
{
const volatile uint64_t *x = a;
const volatile uint64_t *y = b;
if ((x[0] ^ y[0]) | (x[1] ^ y[1]))
return 1;
return 0;
}
int aes_cmac_verify(const struct mac128 *target, const void *src, size_t size, const aes_context *key)
{
struct mac128 mac;
aes_cmac(&mac, src, size, key);
return verify_128(&mac, target);
}
static int verify_256(const void *a, const void* b)
{
const volatile uint64_t *x = a;
const volatile uint64_t *y = b;
if ((x[0] ^ y[0]) | (x[1] ^ y[1]) | (x[2] ^ y[2]) | (x[3] ^ y[3]))
return 1;
return 0;
}
void sha256(struct hash256 *target, const void *src, size_t size)
{
void* dst = target;
sha2(src, size, dst, 0);
}
int sha256_verify(const struct hash256 *targ, const void *data, size_t size)
{
struct hash256 hash;
sha256(&hash, data, size);
return verify_256(&hash, targ);
}