blob: 50876960b6c92bb3a72fdfe3d6d297fb33764e0d [file] [log] [blame]
/* Software-based Trusted Platform Module (TPM) Emulator
* Copyright (C) 2004-2010 Mario Strasser <mast@gmx.net>
*
* This module is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* This module is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* $Id: tpm_migration.c 367 2010-02-13 15:52:18Z mast $
*/
#include "tpm_emulator.h"
#include "tpm_commands.h"
#include "tpm_handles.h"
#include "tpm_data.h"
#include "tpm_marshalling.h"
#include "crypto/sha1.h"
#include "crypto/hmac.h"
/*
* Migration ([TPM_Part3], Section 11)
*/
static int tpm_compute_migration_digest(TPM_PUBKEY *migrationKey,
TPM_MIGRATE_SCHEME migrationScheme,
TPM_NONCE *tpmProof, TPM_DIGEST *digest)
{
tpm_sha1_ctx_t sha1;
UINT32 len = sizeof_TPM_PUBKEY((*migrationKey));
BYTE *buf, *ptr, buf2[2];
buf = ptr = tpm_malloc(len);
if (buf == NULL
|| tpm_marshal_TPM_PUBKEY(&ptr, &len, migrationKey)) {
tpm_free(buf);
return -1;
}
/* compute SHA1 hash */
tpm_sha1_init(&sha1);
tpm_sha1_update(&sha1, buf, sizeof_TPM_PUBKEY((*migrationKey)));
ptr = buf2; len = 2;
tpm_marshal_UINT16(&ptr, &len, migrationScheme);
tpm_sha1_update(&sha1, buf2, 2);
tpm_sha1_update(&sha1, tpmProof->nonce, sizeof(TPM_NONCE));
tpm_sha1_final(&sha1, digest->digest);
tpm_free(buf);
return 0;
}
static int tpm_verify_migration_digest(TPM_MIGRATIONKEYAUTH *migrationKeyAuth,
TPM_NONCE *tpmProof)
{
TPM_DIGEST digest;
if (tpm_compute_migration_digest(&migrationKeyAuth->migrationKey,
migrationKeyAuth->migrationScheme, tpmProof, &digest)) return -1;
return memcmp(digest.digest, migrationKeyAuth->digest.digest, sizeof(TPM_DIGEST));
}
TPM_RESULT TPM_CreateMigrationBlob(TPM_KEY_HANDLE parentHandle,
TPM_MIGRATE_SCHEME migrationType,
TPM_MIGRATIONKEYAUTH *migrationKeyAuth,
UINT32 encDataSize, BYTE *encData,
TPM_AUTH *auth1, TPM_AUTH *auth2,
UINT32 *randomSize, BYTE **random,
UINT32 *outDataSize, BYTE **outData)
{
TPM_RESULT res;
TPM_KEY_DATA *parent;
TPM_SESSION_DATA *session;
BYTE *key_buf;
UINT32 key_buf_size;
TPM_STORE_ASYMKEY store;
TPM_PUBKEY_DATA key;
info("TPM_CreateMigrationBlob()");
/* get parent key */
parent = tpm_get_key(parentHandle);
if (parent == NULL) return TPM_INVALID_KEYHANDLE;
/* verify parent authorization */
res = tpm_verify_auth(auth1, parent->usageAuth, parentHandle);
if (res != TPM_SUCCESS) return res;
session = tpm_get_auth(auth2->authHandle);
if (session == NULL || session->type != TPM_ST_OIAP) return TPM_AUTHFAIL;
/* verify key properties */
if (parent->keyUsage != TPM_KEY_STORAGE) return TPM_INVALID_KEYUSAGE;
/* decrypt private key */
if (tpm_decrypt_private_key(parent, encData, encDataSize,
&store, &key_buf, &key_buf_size)
|| store.payload != TPM_PT_ASYM) {
tpm_free(key_buf);
return TPM_DECRYPT_ERROR;
}
debug("key size: %d / %d", store.privKey.keyLength, key_buf_size);
/* verify migration authorization */
res = tpm_verify_auth(auth2, store.migrationAuth, TPM_INVALID_HANDLE);
if (res != TPM_SUCCESS) {
tpm_free(key_buf);
return TPM_MIGRATEFAIL;
}
if (tpm_verify_migration_digest(migrationKeyAuth,
&tpmData.permanent.data.tpmProof)) {
debug("tpm_verify_migration_digest() failed");
tpm_free(key_buf);
return TPM_MIGRATEFAIL;
}
debug("migration authorization is valid.");
/* set public key */
if (tpm_setup_pubkey_data(&migrationKeyAuth->migrationKey, &key) != 0) {
debug("tpm_setup_pubkey() failed");
tpm_free(key_buf);
return TPM_FAIL;
}
/* perform migration */
if (migrationType == TPM_MS_REWRAP) {
/* re-encrypt raw key data */
debug("migrationType = TPM_MS_REWRAP");
*random = NULL;
*randomSize = 0;
*outDataSize = key.key.size >> 3;
*outData = tpm_malloc(*outDataSize);
if (*outData == NULL) {
free_TPM_PUBKEY_DATA(key);
tpm_free(*outData);
tpm_free(key_buf);
return TPM_FAIL;
}
if (tpm_encrypt_public(&key, key_buf, key_buf_size,
*outData, outDataSize) != 0) {
free_TPM_PUBKEY_DATA(key);
tpm_free(*outData);
tpm_free(key_buf);
return TPM_ENCRYPT_ERROR;
}
} else if (migrationType == TPM_MS_MIGRATE) {
BYTE *ptr, *buf;
UINT32 len;
size_t buf_len;
/* generate an OAEP encoding of the TPM_MIGRATE_ASYMKEY structure:
0x00|seed|K1|0x00-pad|0x01|TPM_MIGRATE_ASYMKEY */
debug("migrationType = TPM_MS_MIGRATE");
buf_len = key.key.size >> 3;
ptr = buf = tpm_malloc(buf_len);
*randomSize = buf_len;
*random = tpm_malloc(*randomSize);
if (buf == NULL || *random == NULL) {
free_TPM_PUBKEY_DATA(key);
tpm_free(buf);
tpm_free(*random);
tpm_free(key_buf);
return TPM_NOSPACE;
}
buf[0] = 0x00;
memcpy(&buf[1], store.migrationAuth, sizeof(TPM_SECRET));
ptr = &buf[1 + sizeof(TPM_SECRET)];
len = 4;
tpm_marshal_UINT32(&ptr, &len, store.privKey.keyLength);
memcpy(ptr, store.privKey.key, 16);
memset(ptr + 16, 0, buf_len - 41);
len = 46 + store.privKey.keyLength - 16;
ptr = &buf[buf_len - len];
tpm_marshal_BYTE(&ptr, &len, 0x01);
tpm_marshal_TPM_PAYLOAD_TYPE(&ptr, &len, TPM_PT_MIGRATE);
tpm_marshal_TPM_SECRET(&ptr, &len, &store.usageAuth);
tpm_marshal_TPM_DIGEST(&ptr, &len, &store.pubDataDigest);
tpm_marshal_UINT32(&ptr, &len, store.privKey.keyLength - 16);
memcpy(ptr, &store.privKey.key[16], store.privKey.keyLength - 16);
tpm_rsa_mask_generation(&buf[1], SHA1_DIGEST_LENGTH,
&buf[1 + SHA1_DIGEST_LENGTH], buf_len - SHA1_DIGEST_LENGTH - 1);
tpm_rsa_mask_generation(&buf[1 + SHA1_DIGEST_LENGTH],
buf_len - SHA1_DIGEST_LENGTH - 1, &buf[1], SHA1_DIGEST_LENGTH);
/* XOR encrypt OAEP encoding */
tpm_get_random_bytes(*random, *randomSize);
for (len = 0; len < buf_len; len++) buf[len] ^= (*random)[len];
/* RSA encrypt OAEP encoding */
if (tpm_rsa_encrypt(&key.key, RSA_ES_PLAIN, buf, buf_len, buf, &buf_len)) {
debug("tpm_rsa_encrypt() failed");
free_TPM_PUBKEY_DATA(key);
tpm_free(buf);
tpm_free(*random);
tpm_free(key_buf);
return TPM_ENCRYPT_ERROR;
}
*outDataSize = buf_len;
*outData = buf;
} else {
debug("invalid migration type: %d", migrationType);
free_TPM_PUBKEY_DATA(key);
tpm_free(key_buf);
return TPM_BAD_PARAMETER;
}
free_TPM_PUBKEY_DATA(key);
tpm_free(key_buf);
return TPM_SUCCESS;
}
TPM_RESULT TPM_ConvertMigrationBlob(TPM_KEY_HANDLE parentHandle,
UINT32 inDataSize, BYTE *inData,
UINT32 randomSize, BYTE *random,
TPM_AUTH *auth1,
UINT32 *outDataSize,BYTE **outData)
{
TPM_RESULT res;
TPM_KEY_DATA *parent;
BYTE *ptr, *buf;
UINT32 len;
size_t buf_len;
TPM_STORE_ASYMKEY store;
info("TPM_ConvertMigrationBlob()");
/* get parent key */
parent = tpm_get_key(parentHandle);
if (parent == NULL) return TPM_INVALID_KEYHANDLE;
/* verify parent authorization */
if (auth1->authHandle != TPM_INVALID_HANDLE
|| parent->authDataUsage != TPM_AUTH_NEVER) {
res = tpm_verify_auth(auth1, parent->usageAuth, parentHandle);
if (res != TPM_SUCCESS) return res;
}
/* verify key properties */
if (parent->keyUsage != TPM_KEY_STORAGE) return TPM_INVALID_KEYUSAGE;
/* decrypt private key */
buf_len = parent->key.size >> 3;
buf = tpm_malloc(buf_len);
if (buf == NULL) return TPM_NOSPACE;
/* RSA decrypt OAEP encoding */
if (tpm_rsa_decrypt(&parent->key, RSA_ES_PLAIN, inData, inDataSize, buf, &buf_len)
|| buf_len != randomSize) {
debug("tpm_rsa_decrypt() failed");
tpm_free(buf);
return TPM_DECRYPT_ERROR;
}
/* XOR decrypt OAEP encoding */
for (len = 0; len < buf_len; len++) buf[len] ^= random[len];
/* unmask OAEP encoding */
tpm_rsa_mask_generation(&buf[1 + SHA1_DIGEST_LENGTH],
buf_len - SHA1_DIGEST_LENGTH - 1, &buf[1], SHA1_DIGEST_LENGTH);
tpm_rsa_mask_generation(&buf[1], SHA1_DIGEST_LENGTH,
&buf[1 + SHA1_DIGEST_LENGTH], buf_len - SHA1_DIGEST_LENGTH - 1);
/* create a TPM_STORE_ASYMKEY structure */
memcpy(store.migrationAuth, &buf[1], sizeof(TPM_SECRET));
for (ptr = &buf[1 + sizeof(TPM_SECRET) + 20]; *ptr == 0x00; ptr++);
if (ptr[0] != 0x01 || ptr[1] != TPM_PT_MIGRATE) {
tpm_free(buf);
return TPM_DECRYPT_ERROR;
}
ptr += 2;
len = buf_len - (ptr - buf);
store.payload = TPM_PT_ASYM;
tpm_unmarshal_TPM_SECRET(&ptr, &len, &store.usageAuth);
tpm_unmarshal_TPM_DIGEST(&ptr, &len, &store.pubDataDigest);
tpm_unmarshal_UINT32(&ptr, &len, &store.privKey.keyLength);
store.privKey.keyLength += 16;
memmove(&buf[1 + sizeof(TPM_SECRET) + 20], ptr, len);
store.privKey.key = &buf[1 + sizeof(TPM_SECRET) + 4];
/* encrypt private key */
*outDataSize = parent->key.size >> 3;
*outData = tpm_malloc(*outDataSize);
if (*outData == NULL) {
tpm_free(buf);
return TPM_NOSPACE;
}
if (tpm_encrypt_private_key(parent, &store, *outData, outDataSize)) {
debug("tpm_encrypt_private_key() failed");
tpm_free(*outData);
tpm_free(buf);
return TPM_ENCRYPT_ERROR;
}
tpm_free(buf);
return TPM_SUCCESS;
}
static int tpm_copy_pubkey(TPM_PUBKEY *in, TPM_PUBKEY *out)
{
memcpy(out, in, sizeof(TPM_PUBKEY));
out->pubKey.key = tpm_malloc(out->pubKey.keyLength);
if (out->pubKey.key == NULL) return -1;
memcpy(out->pubKey.key, in->pubKey.key, out->pubKey.keyLength);
out->algorithmParms.parms.rsa.exponent =
tpm_malloc(out->algorithmParms.parms.rsa.exponentSize);
if (out->algorithmParms.parms.rsa.exponent == NULL) {
tpm_free(out->pubKey.key);
return -1;
}
memcpy(out->algorithmParms.parms.rsa.exponent,
in->algorithmParms.parms.rsa.exponent,
out->algorithmParms.parms.rsa.exponentSize);
return 0;
}
TPM_RESULT TPM_AuthorizeMigrationKey(TPM_MIGRATE_SCHEME migrateScheme,
TPM_PUBKEY *migrationKey, TPM_AUTH *auth1,
TPM_MIGRATIONKEYAUTH *outData)
{
TPM_RESULT res;
info("TPM_AuthorizeMigrationKey()");
/* verify authorization */
res = tpm_verify_auth(auth1, tpmData.permanent.data.ownerAuth, TPM_KH_OWNER);
if (res != TPM_SUCCESS) return res;
/* verify the key size and encryption scheme */
if (migrationKey->algorithmParms.encScheme != TPM_ES_RSAESOAEP_SHA1_MGF1
|| migrationKey->algorithmParms.algorithmID != TPM_ALG_RSA)
return TPM_INAPPROPRIATE_ENC;
if (migrationKey->algorithmParms.parms.rsa.keyLength < 2048)
return TPM_BAD_KEY_PROPERTY;
/* create migration key authorization */
if (tpm_compute_migration_digest(migrationKey, migrateScheme,
&tpmData.permanent.data.tpmProof, &outData->digest) != 0) {
debug("tpm_compute_migration_digest() failed");
return TPM_FAIL;
}
outData->migrationScheme = migrateScheme;
if (tpm_copy_pubkey(migrationKey, &outData->migrationKey) != 0) {
debug("tpm_copy_pubkey() failed");
return TPM_FAIL;
}
return TPM_SUCCESS;
}
TPM_RESULT TPM_MigrateKey(TPM_KEY_HANDLE maKeyHandle, TPM_PUBKEY *pubKey,
UINT32 inDataSize, BYTE *inData, TPM_AUTH *auth1,
UINT32 *outDataSize, BYTE **outData)
{
TPM_RESULT res;
TPM_KEY_DATA *key;
TPM_PUBKEY_DATA key2;
UINT32 size;
info("TPM_MigrateKey()");
key = tpm_get_key(maKeyHandle);
if (key == NULL) return TPM_INVALID_KEYHANDLE;
/* verify key authorization */
res = tpm_verify_auth(auth1, key->usageAuth, maKeyHandle);
if (res != TPM_SUCCESS) return res;
/* verify key usage */
if (key->keyUsage != TPM_KEY_MIGRATE) return TPM_BAD_KEY_PROPERTY;
if (key->encScheme != TPM_ES_RSAESOAEP_SHA1_MGF1) return TPM_INAPPROPRIATE_ENC;
/* verify public key */
if (pubKey->algorithmParms.algorithmID != TPM_ALG_RSA
|| pubKey->algorithmParms.parms.rsa.keyLength < (inDataSize << 3))
return TPM_BAD_KEY_PROPERTY;
if (tpm_setup_pubkey_data(pubKey, &key2) != 0) return TPM_FAIL;
/* decrypt inData and re-encrypt it with the public key */
*outDataSize = size = pubKey->algorithmParms.parms.rsa.keyLength >> 3;
*outData = tpm_malloc(*outDataSize);
if (*outData == NULL) {
free_TPM_PUBKEY_DATA(key2);
return TPM_FAIL;
}
if (tpm_decrypt(key, inData, inDataSize, *outData, &size) != 0) {
free_TPM_PUBKEY_DATA(key2);
tpm_free(*outData);
return TPM_DECRYPT_ERROR;
}
if (tpm_encrypt_public(&key2, *outData, size, *outData, outDataSize) != 0) {
free_TPM_PUBKEY_DATA(key2);
tpm_free(*outData);
return TPM_ENCRYPT_ERROR;
}
free_TPM_PUBKEY_DATA(key2);
return TPM_SUCCESS;
}
TPM_RESULT TPM_CMK_SetRestrictions(TPM_CMK_DELEGATE restriction,
TPM_AUTH *auth1)
{
TPM_RESULT res;
info("TPM_CMK_SetRestrictions()");
/* verify authorization */
res = tpm_verify_auth(auth1, tpmData.permanent.data.ownerAuth, TPM_KH_OWNER);
if (res != TPM_SUCCESS) return res;
/* update delegation restriction */
tpmData.permanent.data.restrictDelegate = restriction;
return TPM_SUCCESS;
}
TPM_RESULT TPM_CMK_ApproveMA(TPM_DIGEST *migrationAuthorityDigest,
TPM_AUTH *auth1, TPM_HMAC *outData)
{
TPM_RESULT res;
BYTE buf[2];
tpm_hmac_ctx_t ctx;
info("TPM_CMK_ApproveMA()");
/* verify authorization */
res = tpm_verify_auth(auth1, tpmData.permanent.data.ownerAuth, TPM_KH_OWNER);
if (res != TPM_SUCCESS) return res;
/* create hmac of a TPM_CMK_MA_APPROVAL structure */
buf[0] = (TPM_TAG_CMK_MA_APPROVAL >> 8) & 0xff;
buf[1] = TPM_TAG_CMK_MA_APPROVAL & 0xff;
tpm_hmac_init(&ctx, tpmData.permanent.data.tpmProof.nonce, sizeof(TPM_NONCE));
tpm_hmac_update(&ctx, buf, 2);
tpm_hmac_update(&ctx, migrationAuthorityDigest->digest, sizeof(TPM_DIGEST));
tpm_hmac_final(&ctx, outData->digest);
return TPM_SUCCESS;
}
TPM_RESULT TPM_CMK_CreateKey(TPM_KEY_HANDLE parentHandle,
TPM_ENCAUTH *dataUsageAuth,
TPM_KEY *keyInfo,
TPM_HMAC *migrationAuthorityApproval,
TPM_DIGEST *migrationAuthorityDigest,
TPM_AUTH *auth1, TPM_AUTH *auth2,
TPM_KEY *wrappedKey)
{
TPM_RESULT res;
TPM_KEY_DATA *parent;
TPM_SESSION_DATA *session;
tpm_hmac_ctx_t ctx;
BYTE buf[SHA1_DIGEST_LENGTH];
TPM_STORE_ASYMKEY store;
tpm_rsa_private_key_t rsa;
UINT32 key_length;
TPM_PUBKEY pubKey;
TPM_DIGEST keyDigest;
info("TPM_CMK_CreateKey()");
/* get parent key */
parent = tpm_get_key(parentHandle);
if (parent == NULL) return TPM_INVALID_KEYHANDLE;
/* verify authorization */
res = tpm_verify_auth(auth1, parent->usageAuth, parentHandle);
if (res != TPM_SUCCESS) return res;
auth1->continueAuthSession = FALSE;
session = tpm_get_auth(auth1->authHandle);
if (session->type != TPM_ST_OSAP) return TPM_AUTHFAIL;
/* must be TPM_KEY12 */
if (keyInfo->tag != TPM_TAG_KEY12) return TPM_INVALID_STRUCTURE;
/* verify key parameters */
if (parent->keyUsage != TPM_KEY_STORAGE
|| parent->encScheme == TPM_ES_NONE
|| parent->keyFlags & TPM_KEY_FLAG_MIGRATABLE
|| !(keyInfo->keyFlags & TPM_KEY_FLAG_MIGRATABLE)
|| !(keyInfo->keyFlags & TPM_KEY_FLAG_AUTHORITY)
|| keyInfo->keyUsage == TPM_KEY_IDENTITY
|| keyInfo->keyUsage == TPM_KEY_AUTHCHANGE) return TPM_INVALID_KEYUSAGE;
if (keyInfo->algorithmParms.algorithmID != TPM_ALG_RSA
|| keyInfo->algorithmParms.parmSize == 0
|| keyInfo->algorithmParms.parms.rsa.keyLength < 512
|| keyInfo->algorithmParms.parms.rsa.numPrimes != 2
|| keyInfo->algorithmParms.parms.rsa.exponentSize != 0)
return TPM_BAD_KEY_PROPERTY;
if (tpmData.permanent.flags.FIPS
&& (keyInfo->algorithmParms.parms.rsa.keyLength < 1024
|| keyInfo->authDataUsage == TPM_AUTH_NEVER
|| keyInfo->keyUsage == TPM_KEY_LEGACY)) return TPM_NOTFIPS;
if ((keyInfo->keyUsage == TPM_KEY_STORAGE
|| keyInfo->keyUsage == TPM_KEY_MIGRATE)
&& (keyInfo->algorithmParms.algorithmID != TPM_ALG_RSA
|| keyInfo->algorithmParms.parms.rsa.keyLength != 2048
|| keyInfo->algorithmParms.sigScheme != TPM_SS_NONE
|| keyInfo->algorithmParms.encScheme != TPM_ES_RSAESOAEP_SHA1_MGF1))
return TPM_BAD_KEY_PROPERTY;
/* verify migration authority */
buf[0] = (TPM_TAG_CMK_MA_APPROVAL >> 8) & 0xff;
buf[1] = TPM_TAG_CMK_MA_APPROVAL & 0xff;
tpm_hmac_init(&ctx, tpmData.permanent.data.tpmProof.nonce, sizeof(TPM_NONCE));
tpm_hmac_update(&ctx, buf, 2);
tpm_hmac_update(&ctx, migrationAuthorityDigest->digest, sizeof(TPM_DIGEST));
tpm_hmac_final(&ctx, buf);
if (memcmp(migrationAuthorityApproval->digest, buf, sizeof(TPM_HMAC)) != 0)
return TPM_MA_AUTHORITY;
/* setup the wrapped key */
memcpy(wrappedKey, keyInfo, sizeof(TPM_KEY));
/* setup key store */
store.payload = TPM_PT_MIGRATE_RESTRICTED;
tpm_decrypt_auth_secret(*dataUsageAuth, session->sharedSecret,
&session->lastNonceEven, store.usageAuth);
/* compute PCR digest */
if (keyInfo->PCRInfoSize > 0) {
tpm_compute_pcr_digest(&keyInfo->PCRInfo.creationPCRSelection,
&keyInfo->PCRInfo.digestAtCreation, NULL);
keyInfo->PCRInfo.localityAtCreation =
tpmData.stany.flags.localityModifier;
}
/* generate key and store it */
key_length = keyInfo->algorithmParms.parms.rsa.keyLength;
if (tpm_rsa_generate_key(&rsa, key_length)) {
debug("TPM_CreateWrapKey(): tpm_rsa_generate_key() failed.");
return TPM_FAIL;
}
wrappedKey->pubKey.keyLength = key_length >> 3;
wrappedKey->pubKey.key = tpm_malloc(wrappedKey->pubKey.keyLength);
store.privKey.keyLength = key_length >> 4;
store.privKey.key = tpm_malloc(store.privKey.keyLength);
wrappedKey->encDataSize = parent->key.size >> 3;
wrappedKey->encData = tpm_malloc(wrappedKey->encDataSize);
if (wrappedKey->pubKey.key == NULL || store.privKey.key == NULL
|| wrappedKey->encData == NULL) {
tpm_rsa_release_private_key(&rsa);
tpm_free(wrappedKey->pubKey.key);
tpm_free(store.privKey.key);
tpm_free(wrappedKey->encData);
return TPM_NOSPACE;
}
tpm_rsa_export_modulus(&rsa, wrappedKey->pubKey.key, NULL);
tpm_rsa_export_prime1(&rsa, store.privKey.key, NULL);
tpm_rsa_release_private_key(&rsa);
/* create hmac of TPM_CMK_MIGAUTH */
buf[0] = (TPM_TAG_CMK_MIGAUTH >> 8) & 0xff;
buf[1] = TPM_TAG_CMK_MIGAUTH & 0xff;
memcpy(&pubKey.algorithmParms, &wrappedKey->algorithmParms,
sizeof(TPM_KEY_PARMS));
memcpy(&pubKey.pubKey, &wrappedKey->pubKey, sizeof(TPM_STORE_PUBKEY));
if (tpm_compute_pubkey_digest(&pubKey, &keyDigest) !=0 ) {
debug("tpm_compute_pubkey_digest() failed");
return TPM_FAIL;
}
tpm_hmac_init(&ctx, tpmData.permanent.data.tpmProof.nonce, sizeof(TPM_NONCE));
tpm_hmac_update(&ctx, buf, 2);
tpm_hmac_update(&ctx, migrationAuthorityDigest->digest, sizeof(TPM_DIGEST));
tpm_hmac_update(&ctx, keyDigest.digest, sizeof(TPM_DIGEST));
tpm_hmac_final(&ctx, store.migrationAuth);
/* compute the digest of the wrapped key (without encData) */
if (tpm_compute_key_digest(wrappedKey, &store.pubDataDigest)) {
debug("TPM_CreateWrapKey(): tpm_compute_key_digest() failed.");
return TPM_FAIL;
}
/* encrypt private key data */
if (tpm_encrypt_private_key(parent, &store, wrappedKey->encData,
&wrappedKey->encDataSize)) {
tpm_free(wrappedKey->pubKey.key);
tpm_free(store.privKey.key);
tpm_free(wrappedKey->encData);
return TPM_ENCRYPT_ERROR;
}
tpm_free(store.privKey.key);
return TPM_SUCCESS;
}
TPM_RESULT TPM_CMK_CreateTicket(TPM_PUBKEY *verificationKey,
TPM_DIGEST *signedData,
UINT32 signatureValueSize,
BYTE *signatureValue, TPM_AUTH *auth1,
TPM_DIGEST *sigTicket)
{
TPM_RESULT res;
TPM_PUBKEY_DATA key;
BYTE buf[2];
TPM_DIGEST keyDigest;
tpm_hmac_ctx_t ctx;
info("TPM_CMK_CreateTicket()");
/* verify authorization */
res = tpm_verify_auth(auth1, tpmData.permanent.data.ownerAuth, TPM_KH_OWNER);
/* verify key type and algorithm */
if (verificationKey->algorithmParms.algorithmID != TPM_ALG_RSA
|| verificationKey->algorithmParms.encScheme != TPM_ES_NONE)
return TPM_BAD_KEY_PROPERTY;
if (verificationKey->algorithmParms.sigScheme != TPM_SS_RSASSAPKCS1v15_SHA1
&& verificationKey->algorithmParms.sigScheme != TPM_SS_RSASSAPKCS1v15_INFO)
return TPM_BAD_KEY_PROPERTY;
/* verify signature */
if (tpm_setup_pubkey_data(verificationKey, &key) != 0) return TPM_FAIL;
res = tpm_verify(&key, auth1, FALSE, signedData->digest, sizeof(TPM_DIGEST),
signatureValue, signatureValueSize);
free_TPM_PUBKEY_DATA(key);
if (res != TPM_SUCCESS) return res;
/* create hmac on TPM_CMK_SIGTICKET */
buf[0] = (TPM_TAG_CMK_SIGTICKET >> 8) & 0xff;
buf[1] = TPM_TAG_CMK_SIGTICKET & 0xff;
if (tpm_compute_pubkey_digest(verificationKey, &keyDigest) !=0 ) {
debug("tpm_compute_pubkey_digest() failed");
return TPM_FAIL;
}
tpm_hmac_init(&ctx, tpmData.permanent.data.tpmProof.nonce, sizeof(TPM_NONCE));
tpm_hmac_update(&ctx, buf, 2);
tpm_hmac_update(&ctx, keyDigest.digest, sizeof(TPM_DIGEST));
tpm_hmac_update(&ctx, signedData->digest, sizeof(TPM_DIGEST));
tpm_hmac_final(&ctx, sigTicket->digest);
return TPM_SUCCESS;
}
TPM_RESULT TPM_CMK_CreateBlob(TPM_KEY_HANDLE parentHandle,
TPM_MIGRATE_SCHEME migrationType,
TPM_MIGRATIONKEYAUTH *migrationKeyAuth,
TPM_DIGEST *pubSourceKeyDigest,
TPM_MSA_COMPOSITE *msaList,
TPM_CMK_AUTH *restrictTicket,
TPM_HMAC *sigTicket,
UINT32 encDataSize, BYTE *encData,
TPM_AUTH *auth1,
UINT32 *randomSize, BYTE **random,
UINT32 *outDataSize, BYTE **outData)
{
TPM_RESULT res;
TPM_KEY_DATA *parent;
TPM_STORE_ASYMKEY store;
BYTE *key_buf;
UINT32 key_buf_size;
tpm_hmac_ctx_t hmac_ctx;
tpm_sha1_ctx_t sha1_ctx;
BYTE tag[2], hmac[SHA1_DIGEST_LENGTH];
BYTE *ptr, *buf;
UINT32 i, len;
size_t buf_len;
TPM_DIGEST migKeyDigest;
TPM_DIGEST msaListDigest;
TPM_DIGEST ticketDigest;
TPM_PUBKEY_DATA key;
info("TPM_CMK_CreateBlob()");
/* get parent key */
parent = tpm_get_key(parentHandle);
if (parent == NULL) return TPM_INVALID_KEYHANDLE;
/* verify authorization */
res = tpm_verify_auth(auth1, parent->usageAuth, parentHandle);
if (res != TPM_SUCCESS) return res;
/* migrationType must match */
if (migrationType != migrationKeyAuth->migrationScheme) return TPM_BAD_MODE;
if (parent->keyFlags & TPM_KEY_FLAG_MIGRATABLE) return TPM_BAD_KEY_PROPERTY;
/* decrypt private key */
if (tpm_decrypt_private_key(parent, encData, encDataSize,
&store, &key_buf, &key_buf_size)
|| (store.payload != TPM_PT_MIGRATE_RESTRICTED
&& store.payload != TPM_PT_MIGRATE_EXTERNAL)) {
tpm_free(key_buf);
return TPM_DECRYPT_ERROR;
}
if (tpm_verify_migration_digest(migrationKeyAuth,
&tpmData.permanent.data.tpmProof)) {
debug("tpm_verify_migration_digest() failed");
tpm_free(key_buf);
return TPM_MIGRATEFAIL;
}
/* verify the migration authority list */
len = sizeof_TPM_MSA_COMPOSITE((*msaList));
ptr = buf = tpm_malloc(len);
if (buf == NULL || tpm_marshal_TPM_MSA_COMPOSITE(&ptr, &len, msaList)) {
debug("tpm_marshal_TPM_MSA_COMPOSITE() failed");
tpm_free(buf);
tpm_free(key_buf);
return TPM_FAIL;
}
tpm_sha1_init(&sha1_ctx);
tpm_sha1_update(&sha1_ctx, buf, sizeof_TPM_MSA_COMPOSITE((*msaList)));
tpm_sha1_final(&sha1_ctx, msaListDigest.digest);
tpm_free(buf);
tag[0] = (TPM_TAG_CMK_MIGAUTH >> 8) & 0xff;
tag[1] = TPM_TAG_CMK_MIGAUTH & 0xff;
tpm_hmac_init(&hmac_ctx, tpmData.permanent.data.tpmProof.nonce, sizeof(TPM_NONCE));
tpm_hmac_update(&hmac_ctx, tag, 2);
tpm_hmac_update(&hmac_ctx, msaListDigest.digest, sizeof(TPM_DIGEST));
tpm_hmac_update(&hmac_ctx, pubSourceKeyDigest->digest, sizeof(TPM_DIGEST));
tpm_hmac_final(&hmac_ctx, hmac);
if (memcmp(hmac, store.migrationAuth, sizeof(TPM_SECRET)) != 0) {
tpm_free(key_buf);
return TPM_MA_AUTHORITY;
}
if (tpm_compute_pubkey_digest(&migrationKeyAuth->migrationKey, &migKeyDigest) !=0 ) {
debug("tpm_compute_pubkey_digest() failed");
tpm_free(key_buf);
return TPM_FAIL;
}
len = sizeof_TPM_CMK_AUTH((*restrictTicket));
ptr = buf = tpm_malloc(len);
if (buf == NULL || tpm_marshal_TPM_CMK_AUTH(&ptr, &len, restrictTicket)) {
debug("tpm_marshal_TPM_CMK_AUTH() failed");
tpm_free(buf);
tpm_free(key_buf);
return TPM_FAIL;
}
tpm_sha1_init(&sha1_ctx);
tpm_sha1_update(&sha1_ctx, buf, sizeof_TPM_CMK_AUTH((*restrictTicket)));
tpm_sha1_final(&sha1_ctx, ticketDigest.digest);
tpm_free(buf);
/* verify the migration destination */
if (migrationKeyAuth->migrationScheme == TPM_MS_RESTRICT_MIGRATE) {
for (i = 0; i < msaList->MSAlist; i++) {
if (memcmp(msaList->migAuthDigest[i].digest, migKeyDigest.digest,
sizeof(TPM_DIGEST)) == 0) break;
}
if (i >= msaList->MSAlist) {
tpm_free(key_buf);
return TPM_MA_AUTHORITY;
}
/* verify the key type and algorithm */
if (migrationKeyAuth->migrationKey.algorithmParms.algorithmID != TPM_ALG_RSA
|| migrationKeyAuth->migrationKey.algorithmParms.encScheme != TPM_ES_RSAESOAEP_SHA1_MGF1
|| migrationKeyAuth->migrationKey.algorithmParms.sigScheme != TPM_SS_NONE) {
tpm_free(key_buf);
return TPM_BAD_KEY_PROPERTY;
}
} else if (migrationKeyAuth->migrationScheme == TPM_MS_RESTRICT_APPROVE) {
if (restrictTicket == NULL || sigTicket == NULL) {
tpm_free(key_buf);
return TPM_BAD_PARAMETER;
}
for (i = 0; i < msaList->MSAlist; i++) {
/* create hmac of TPM_CMK_SIGTICKET */
tag[0] = (TPM_TAG_CMK_SIGTICKET >> 8) & 0xff;
tag[1] = TPM_TAG_CMK_SIGTICKET & 0xff;
tpm_hmac_init(&hmac_ctx, tpmData.permanent.data.tpmProof.nonce,
sizeof(TPM_NONCE));
tpm_hmac_update(&hmac_ctx, tag, 2);
tpm_hmac_update(&hmac_ctx, msaList->migAuthDigest[i].digest,
sizeof(TPM_DIGEST));
tpm_hmac_update(&hmac_ctx, ticketDigest.digest, sizeof(TPM_DIGEST));
tpm_hmac_final(&hmac_ctx, hmac);
if (memcmp(hmac, sigTicket->digest, sizeof(TPM_DIGEST)) == 0) break;
}
if (i >= msaList->MSAlist) {
tpm_free(key_buf);
return TPM_MA_AUTHORITY;
}
if (memcmp(&restrictTicket->destinationKeyDigest, &migKeyDigest,
sizeof(TPM_DIGEST)) != 0) {
tpm_free(key_buf);
return TPM_MA_DESTINATION;
}
if (memcmp(&restrictTicket->sourceKeyDigest, pubSourceKeyDigest,
sizeof(TPM_DIGEST)) != 0) {
tpm_free(key_buf);
return TPM_MA_SOURCE;
}
} else {
tpm_free(key_buf);
return TPM_BAD_PARAMETER;
}
/* set public key */
if (tpm_setup_pubkey_data(&migrationKeyAuth->migrationKey, &key) != 0) {
debug("tpm_setup_pubkey() failed");
tpm_free(key_buf);
return TPM_FAIL;
}
/* generate an OAEP encoding of the TPM_MIGRATE_ASYMKEY structure:
0x00|seed|K1|0x00-pad|0x01|TPM_MIGRATE_ASYMKEY */
buf_len = key.key.size >> 3;
ptr = buf = tpm_malloc(buf_len);
*randomSize = buf_len;
*random = tpm_malloc(*randomSize);
if (buf == NULL || *random == NULL) {
free_TPM_PUBKEY_DATA(key);
tpm_free(buf);
tpm_free(*random);
tpm_free(key_buf);
return TPM_NOSPACE;
}
buf[0] = 0x00;
tpm_sha1_init(&sha1_ctx);
tpm_sha1_update(&sha1_ctx, msaListDigest.digest, sizeof(TPM_DIGEST));
tpm_sha1_update(&sha1_ctx, pubSourceKeyDigest->digest, sizeof(TPM_DIGEST));
tpm_sha1_final(&sha1_ctx, &buf[1]);
ptr = &buf[1 + sizeof(TPM_DIGEST)];
len = 4;
tpm_marshal_UINT32(&ptr, &len, store.privKey.keyLength);
memcpy(ptr, store.privKey.key, 16);
memset(ptr + 16, 0, buf_len - 41);
len = 46 + store.privKey.keyLength - 16;
ptr = &buf[buf_len - len];
tpm_marshal_BYTE(&ptr, &len, 0x01);
tpm_marshal_TPM_PAYLOAD_TYPE(&ptr, &len, TPM_PT_CMK_MIGRATE);
tpm_marshal_TPM_SECRET(&ptr, &len, &store.usageAuth);
tpm_marshal_TPM_DIGEST(&ptr, &len, &store.pubDataDigest);
tpm_marshal_UINT32(&ptr, &len, store.privKey.keyLength - 16);
memcpy(ptr, &store.privKey.key[16], store.privKey.keyLength - 16);
tpm_rsa_mask_generation(&buf[1], SHA1_DIGEST_LENGTH,
&buf[1 + SHA1_DIGEST_LENGTH], buf_len - SHA1_DIGEST_LENGTH - 1);
tpm_rsa_mask_generation(&buf[1 + SHA1_DIGEST_LENGTH],
buf_len - SHA1_DIGEST_LENGTH - 1, &buf[1], SHA1_DIGEST_LENGTH);
/* XOR encrypt OAEP encoding */
tpm_get_random_bytes(*random, *randomSize);
for (len = 0; len < buf_len; len++) buf[len] ^= (*random)[len];
/* RSA encrypt OAEP encoding */
if (tpm_rsa_encrypt(&key.key, RSA_ES_PLAIN, buf, buf_len, buf, &buf_len)) {
debug("tpm_rsa_encrypt() failed");
free_TPM_PUBKEY_DATA(key);
tpm_free(buf);
tpm_free(*random);
tpm_free(key_buf);
return TPM_ENCRYPT_ERROR;
}
*outDataSize = buf_len;
*outData = buf;
free_TPM_PUBKEY_DATA(key);
tpm_free(key_buf);
return TPM_SUCCESS;
}
TPM_RESULT TPM_CMK_ConvertMigration(TPM_KEY_HANDLE parentHandle,
TPM_CMK_AUTH *restrictTicket,
TPM_HMAC *sigTicket, TPM_KEY *migratedKey,
TPM_MSA_COMPOSITE *msaList,
UINT32 randomSize, BYTE *random,
TPM_AUTH *auth1,
UINT32 *outDataSize, BYTE **outData)
{
TPM_RESULT res;
TPM_KEY_DATA *parent;
BYTE *ptr, *buf, *buf2;
UINT32 i, len;
size_t buf_len;
BYTE tag[2], hmac[SHA1_DIGEST_LENGTH];
TPM_STORE_ASYMKEY store;
tpm_sha1_ctx_t sha1_ctx;
tpm_hmac_ctx_t hmac_ctx;
TPM_PUBKEY migratedPubKey;
TPM_PUBKEY parentPubKey;
TPM_DIGEST migKeyDigest;
TPM_DIGEST msaListDigest;
TPM_DIGEST ticketDigest;
TPM_DIGEST parentDigest;
info("TPM_CMK_ConvertMigration()");
/* get parent key */
parent = tpm_get_key(parentHandle);
if (parent == NULL) return TPM_INVALID_KEYHANDLE;
/* verify authorization */
if (auth1->authHandle != TPM_INVALID_HANDLE
|| parent->authDataUsage != TPM_AUTH_NEVER) {
res = tpm_verify_auth(auth1, parent->usageAuth, parentHandle);
if (res != TPM_SUCCESS) return res;
}
/* verify key properties */
if (parent->keyUsage != TPM_KEY_STORAGE
|| parent->keyFlags & TPM_KEY_FLAG_MIGRATABLE) return TPM_INVALID_KEYUSAGE;
if (!(migratedKey->keyFlags & TPM_KEY_FLAG_MIGRATABLE)
&& (!(migratedKey->keyFlags & TPM_KEY_FLAG_AUTHORITY))) return TPM_INVALID_KEYUSAGE;
/* decrypt private key */
buf_len = parent->key.size >> 3;
buf = tpm_malloc(buf_len);
if (buf == NULL) return TPM_NOSPACE;
/* RSA decrypt OAEP encoding */
if (tpm_rsa_decrypt(&parent->key, RSA_ES_PLAIN, migratedKey->encData,
migratedKey->encDataSize, buf, &buf_len)
|| buf_len != randomSize) {
debug("tpm_rsa_decrypt() failed");
tpm_free(buf);
return TPM_DECRYPT_ERROR;
}
/* XOR decrypt OAEP encoding */
for (len = 0; len < buf_len; len++) buf[len] ^= random[len];
/* unmask OAEP encoding */
tpm_rsa_mask_generation(&buf[1 + SHA1_DIGEST_LENGTH],
buf_len - SHA1_DIGEST_LENGTH - 1, &buf[1], SHA1_DIGEST_LENGTH);
tpm_rsa_mask_generation(&buf[1], SHA1_DIGEST_LENGTH,
&buf[1 + SHA1_DIGEST_LENGTH], buf_len - SHA1_DIGEST_LENGTH - 1);
/* compute digest of migrated public key */
memcpy(&migratedPubKey.algorithmParms, &migratedKey->algorithmParms,
sizeof(TPM_KEY_PARMS));
memcpy(&migratedPubKey.pubKey, &migratedKey->pubKey, sizeof(TPM_STORE_PUBKEY));
if (tpm_compute_pubkey_digest(&migratedPubKey, &migKeyDigest) != 0) {
debug("tpm_compute_pubkey_digest() failed");
tpm_free(buf);
return TPM_FAIL;
}
/* compute digest of parent key */
parentPubKey.pubKey.keyLength = parent->key.size >> 3;
parentPubKey.pubKey.key = tpm_malloc(parentPubKey.pubKey.keyLength);
if (parentPubKey.pubKey.key == NULL) {
tpm_free(buf);
return TPM_NOSPACE;
}
tpm_rsa_export_modulus(&parent->key, parentPubKey.pubKey.key, NULL);
if (tpm_setup_key_parms(parent, &parentPubKey.algorithmParms) != 0) {
debug("tpm_setup_key_parms() failed.");
tpm_free(parentPubKey.pubKey.key);
tpm_free(buf);
return TPM_FAIL;
}
if (tpm_compute_pubkey_digest(&parentPubKey, &parentDigest) != 0) {
debug("tpm_compute_pubkey_digest() failed.");
free_TPM_PUBKEY(parentPubKey);
tpm_free(buf);
return TPM_FAIL;
}
free_TPM_PUBKEY(parentPubKey);
/* compute digest of msaList */
len = sizeof_TPM_MSA_COMPOSITE((*msaList));
ptr = buf2 = tpm_malloc(len);
if (buf2 == NULL || tpm_marshal_TPM_MSA_COMPOSITE(&ptr, &len, msaList)) {
debug("tpm_marshal_TPM_MSA_COMPOSITE() failed");
tpm_free(buf2);
tpm_free(buf);
return TPM_FAIL;
}
tpm_sha1_init(&sha1_ctx);
tpm_sha1_update(&sha1_ctx, buf2, sizeof_TPM_MSA_COMPOSITE((*msaList)));
tpm_sha1_final(&sha1_ctx, msaListDigest.digest);
tpm_free(buf2);
/* compute digest of restrictedTicket */
len = sizeof_TPM_CMK_AUTH((*restrictTicket));
ptr = buf2 = tpm_malloc(len);
if (buf2 == NULL || tpm_marshal_TPM_CMK_AUTH(&ptr, &len, restrictTicket)) {
debug("tpm_marshal_TPM_CMK_AUTH() failed");
tpm_free(buf2);
tpm_free(buf);
return TPM_FAIL;
}
tpm_sha1_init(&sha1_ctx);
tpm_sha1_update(&sha1_ctx, buf2, sizeof_TPM_CMK_AUTH((*restrictTicket)));
tpm_sha1_final(&sha1_ctx, ticketDigest.digest);
tpm_free(buf2);
/* verify decoded data */
tpm_sha1_init(&sha1_ctx);
tpm_sha1_update(&sha1_ctx, msaListDigest.digest, sizeof(TPM_DIGEST));
tpm_sha1_update(&sha1_ctx, migKeyDigest.digest, sizeof(TPM_DIGEST));
tpm_sha1_final(&sha1_ctx, hmac);
if (memcmp(&buf[1], hmac, sizeof(TPM_DIGEST)) != 0) {
tpm_free(buf);
return TPM_INVALID_STRUCTURE;
}
/* create a TPM_STORE_ASYMKEY structure */
for (ptr = &buf[1 + sizeof(TPM_SECRET) + 20]; *ptr == 0x00; ptr++);
if (ptr[0] != 0x01 || ptr[1] != TPM_PT_CMK_MIGRATE) {
tpm_free(buf);
return TPM_DECRYPT_ERROR;
}
ptr += 2;
len = buf_len - (ptr - buf);
store.payload = TPM_PT_MIGRATE_EXTERNAL;
tpm_unmarshal_TPM_SECRET(&ptr, &len, &store.usageAuth);
tpm_unmarshal_TPM_DIGEST(&ptr, &len, &store.pubDataDigest);
tpm_unmarshal_UINT32(&ptr, &len, &store.privKey.keyLength);
store.privKey.keyLength += 16;
memmove(&buf[1 + sizeof(TPM_SECRET) + 20], ptr, len);
store.privKey.key = &buf[1 + sizeof(TPM_SECRET) + 4];
tag[0] = (TPM_TAG_CMK_MIGAUTH >> 8) & 0xff;
tag[1] = TPM_TAG_CMK_MIGAUTH & 0xff;
tpm_hmac_init(&hmac_ctx, tpmData.permanent.data.tpmProof.nonce, sizeof(TPM_NONCE));
tpm_hmac_update(&hmac_ctx, tag, 2);
tpm_hmac_update(&hmac_ctx, msaListDigest.digest, sizeof(TPM_DIGEST));
tpm_hmac_update(&hmac_ctx, migKeyDigest.digest, sizeof(TPM_DIGEST));
tpm_hmac_final(&hmac_ctx, store.migrationAuth);
/* verify the migration destination */
for (i = 0; i < msaList->MSAlist; i++) {
/* create hmac of TPM_CMK_SIGTICKET */
tag[0] = (TPM_TAG_CMK_SIGTICKET >> 8) & 0xff;
tag[1] = TPM_TAG_CMK_SIGTICKET & 0xff;
tpm_hmac_init(&hmac_ctx, tpmData.permanent.data.tpmProof.nonce,
sizeof(TPM_NONCE));
tpm_hmac_update(&hmac_ctx, tag, 2);
tpm_hmac_update(&hmac_ctx, msaList->migAuthDigest[i].digest,
sizeof(TPM_DIGEST));
tpm_hmac_update(&hmac_ctx, ticketDigest.digest, sizeof(TPM_DIGEST));
tpm_hmac_final(&hmac_ctx, hmac);
if (memcmp(hmac, sigTicket->digest, sizeof(TPM_DIGEST)) == 0) break;
}
if (i >= msaList->MSAlist) {
tpm_free(buf);
return TPM_MA_AUTHORITY;
}
if (memcmp(&restrictTicket->destinationKeyDigest, &parentDigest,
sizeof(TPM_DIGEST)) != 0) {
tpm_free(buf);
return TPM_MA_DESTINATION;
}
if (memcmp(&restrictTicket->sourceKeyDigest, &migKeyDigest,
sizeof(TPM_DIGEST)) != 0) {
tpm_free(buf);
return TPM_MA_SOURCE;
}
/* encrypt private key */
*outDataSize = parent->key.size >> 3;
*outData = tpm_malloc(*outDataSize);
if (*outData == NULL) {
tpm_free(buf);
return TPM_NOSPACE;
}
if (tpm_encrypt_private_key(parent, &store, *outData, outDataSize)) {
debug("tpm_encrypt_private_key() failed");
tpm_free(*outData);
tpm_free(buf);
return TPM_ENCRYPT_ERROR;
}
tpm_free(buf);
return TPM_SUCCESS;
}