blob: 6954961f7b2c6e2c7ede1067f51b7b6f97baafc5 [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_delegation.c 367 2010-02-13 15:52:18Z mast $
*/
#include "tpm_emulator.h"
#include "tpm_commands.h"
#include "tpm_marshalling.h"
#include "tpm_data.h"
#include "tpm_handles.h"
#include "crypto/hmac.h"
#include "crypto/rc4.h"
/*
* Delegation Commands ([TPM_Part3], Section 19)
*/
TPM_FAMILY_TABLE_ENTRY *tpm_get_family_row(TPM_FAMILY_ID id)
{
UINT32 i;
for (i = 0; i < TPM_NUM_FAMILY_TABLE_ENTRY; i++) {
if (tpmData.permanent.data.familyTable.famRow[i].valid
&& tpmData.permanent.data.familyTable.famRow[i].familyID == id)
return &tpmData.permanent.data.familyTable.famRow[i];
}
return NULL;
}
TPM_DELEGATE_TABLE_ROW *tpm_get_delegate_row(UINT32 row)
{
if (row < TPM_NUM_DELEGATE_TABLE_ENTRY
&& tpmData.permanent.data.delegateTable.delRow[row].valid)
return &tpmData.permanent.data.delegateTable.delRow[row];
return NULL;
}
void tpm_compute_owner_blob_digest(TPM_DELEGATE_OWNER_BLOB *blob,
TPM_DIGEST *digest)
{
tpm_hmac_ctx_t ctx;
BYTE buf[sizeof_TPM_DELEGATE_OWNER_BLOB((*blob))];
BYTE *ptr = buf;
UINT32 length = sizeof(buf);
tpm_marshal_TPM_DELEGATE_OWNER_BLOB(&ptr, &length, blob);
memset(&buf[2 + sizeof_TPM_DELEGATE_PUBLIC(blob->pub)], 0, 20);
tpm_hmac_init(&ctx, tpmData.permanent.data.tpmProof.nonce,
sizeof(tpmData.permanent.data.tpmProof.nonce));
tpm_hmac_update(&ctx, buf, sizeof(buf) - length);
tpm_hmac_final(&ctx, digest->digest);
}
void tpm_compute_key_blob_digest(TPM_DELEGATE_KEY_BLOB *blob,
TPM_DIGEST *digest)
{
tpm_hmac_ctx_t ctx;
BYTE buf[sizeof_TPM_DELEGATE_KEY_BLOB((*blob))];
BYTE *ptr = buf;
UINT32 length = sizeof(buf);
tpm_marshal_TPM_DELEGATE_KEY_BLOB(&ptr, &length, blob);
memset(&buf[2 + sizeof_TPM_DELEGATE_PUBLIC(blob->pub)], 0, 20);
tpm_hmac_init(&ctx, tpmData.permanent.data.tpmProof.nonce,
sizeof(tpmData.permanent.data.tpmProof.nonce));
tpm_hmac_update(&ctx, buf, sizeof(buf) - length);
tpm_hmac_final(&ctx, digest->digest);
}
int tpm_encrypt_sensitive(BYTE *iv, UINT32 iv_size,
TPM_DELEGATE_SENSITIVE *sensitive,
BYTE **enc, UINT32 *enc_size)
{
UINT32 len;
BYTE *ptr;
tpm_rc4_ctx_t rc4_ctx;
BYTE key[TPM_SYM_KEY_SIZE + iv_size];
/* marshal context */
*enc_size = len = sizeof_TPM_DELEGATE_SENSITIVE((*sensitive));
*enc = ptr = tpm_malloc(len);
if (*enc == NULL) return -1;
if (tpm_marshal_TPM_DELEGATE_SENSITIVE(&ptr, &len, sensitive)) {
tpm_free(*enc);
return -1;
}
/* encrypt context */
memcpy(key, tpmData.permanent.data.delegateKey, TPM_SYM_KEY_SIZE);
memcpy(&key[TPM_SYM_KEY_SIZE], iv, iv_size);
tpm_rc4_init(&rc4_ctx, key, sizeof(key));
tpm_rc4_crypt(&rc4_ctx, *enc, *enc, *enc_size);
return 0;
}
int tpm_decrypt_sensitive(BYTE *iv, UINT32 iv_size, BYTE *enc, UINT32 enc_size,
TPM_DELEGATE_SENSITIVE *sensitive, BYTE **buf)
{
UINT32 len;
BYTE *ptr;
tpm_rc4_ctx_t rc4_ctx;
BYTE key[TPM_SYM_KEY_SIZE + iv_size];
len = enc_size;
*buf = ptr = tpm_malloc(len);
if (*buf == NULL) return -1;
/* decrypt context */
memcpy(key, tpmData.permanent.data.delegateKey, TPM_SYM_KEY_SIZE);
memcpy(&key[TPM_SYM_KEY_SIZE], iv, iv_size);
tpm_rc4_init(&rc4_ctx, key, sizeof(key));
tpm_rc4_crypt(&rc4_ctx, enc, *buf, enc_size);
/* unmarshal context */
if (tpm_unmarshal_TPM_DELEGATE_SENSITIVE(&ptr, &len, sensitive)) {
tpm_free(*buf);
return -1;
}
return 0;
}
static TPM_FAMILY_TABLE_ENTRY *tpm_get_free_family_row(void) {
UINT32 i;
for(i = 0; i < TPM_NUM_FAMILY_TABLE_ENTRY; i++) {
if(!tpmData.permanent.data.familyTable.famRow[i].valid) {
tpmData.permanent.data.familyTable.famRow[i].valid = TRUE;
return &tpmData.permanent.data.familyTable.famRow[i];
}
}
return NULL;
}
TPM_RESULT TPM_Delegate_Manage(TPM_FAMILY_ID familyID,
TPM_FAMILY_OPERATION opFlag,
UINT32 opDataSize, BYTE *opData,
TPM_AUTH *auth1,
UINT32 *retDataSize, BYTE **retData)
{
TPM_RESULT res;
TPM_SESSION_DATA *session;
TPM_FAMILY_TABLE_ENTRY *fr;
UINT32 i;
info("[TPM_Delegate_Manage]");
/* if no new family row is to be created, get the existing one */
if (opFlag != TPM_FAMILY_CREATE) {
fr = tpm_get_family_row(familyID);
if (fr == NULL) return TPM_BADINDEX;
} else {
fr = NULL;
}
/* verify authorization */
session = tpm_get_auth(auth1->authHandle);
if (session == NULL) return TPM_AUTHFAIL;
if (auth1->authHandle != TPM_INVALID_AUTHHANDLE) {
res = tpm_verify_auth(auth1, tpmData.permanent.data.ownerAuth, TPM_KH_OWNER);
if (res != TPM_SUCCESS) return res;
if (session->type == TPM_ST_DSAP) {
if (session->familyID != familyID) return TPM_DELEGATE_FAMILY;
auth1->continueAuthSession = FALSE;
}
} else {
if (tpmData.permanent.flags.owned) return TPM_AUTHFAIL;
/* check delegate admin lock */
if (fr != NULL && (fr->flags & TPM_DELEGATE_ADMIN_LOCK)) {
debug("delegate admin lock is set");
return TPM_DELEGATE_LOCK;
}
/* verify maximal number of writes without an owner */
if (tpmData.permanent.data.noOwnerNVWrite >= TPM_MAX_NV_WRITE_NOOWNER)
return TPM_MAXNVWRITES;
tpmData.permanent.data.noOwnerNVWrite++;
}
/* invalidate all but this auth session */
for (i = 0; i < TPM_MAX_SESSIONS; i++) {
TPM_SESSION_DATA *s = &tpmData.stany.data.sessions[i];
if (s->type != TPM_ST_TRANSPORT && s != session) memset(s, 0, sizeof(*s));
}
tpmData.stclear.data.ownerReference = TPM_KH_OWNER;
/* perform requested operation */
if (opFlag == TPM_FAMILY_CREATE) {
BYTE *ptr;
UINT32 length;
debug("ofFlag = TPM_FAMILY_CREATE");
if (opDataSize != 1) return TPM_BAD_PARAM_SIZE;
/* get a free family row */
fr = tpm_get_free_family_row();
if (fr == NULL) return TPM_NOSPACE;
/* initialize the new row */
fr->tag = TPM_TAG_FAMILY_TABLE_ENTRY;
fr->familyLabel.label = *opData;
tpmData.permanent.data.lastFamilyID++;
fr->familyID = tpmData.permanent.data.lastFamilyID;
fr->verificationCount = 1;
fr->flags = 0;
/* return the familyID */
length = *retDataSize = 4;
ptr = *retData = tpm_malloc(*retDataSize);
if (*retData == NULL) {
debug("tpm_malloc() failed.");
fr->valid = FALSE;
return TPM_FAIL;
}
if (tpm_marshal_UINT32(&ptr, &length, fr->familyID) != 0) {
debug("tpm_marshal_UINT32() failed.");
tpm_free(*retData);
fr->valid = FALSE;
return TPM_FAIL;
}
} else if (opFlag == TPM_FAMILY_ADMIN) {
debug("opFlag = TPM_FAMILY_ADMIN");
if (opDataSize != 1) return TPM_BAD_PARAM_SIZE;
if (*opData)fr->flags |= TPM_DELEGATE_ADMIN_LOCK;
else fr->flags &= ~TPM_DELEGATE_ADMIN_LOCK;
*retDataSize = 0;
} else if (opFlag == TPM_FAMILY_ENABLE) {
debug("opFlag = TPM_FAMFLAG_ENABLED");
if (opDataSize != 1) return TPM_BAD_PARAM_SIZE;
if (*opData)fr->flags |= TPM_FAMFLAG_ENABLED;
else fr->flags &= ~TPM_FAMFLAG_ENABLED;
*retDataSize = 0;
return TPM_SUCCESS;
} else if (opFlag == TPM_FAMILY_INVALIDATE) {
debug("opFlag = TPM_FAMILY_INVALIDATE");
/* invalidate all family data */
memset(fr, 0, sizeof(*fr));
fr->valid = FALSE;
*retDataSize = 0;
} else {
debug("unknown opFlag value: %d", opFlag);
return TPM_BAD_PARAMETER;
}
return TPM_SUCCESS;
}
TPM_RESULT TPM_Delegate_CreateKeyDelegation(TPM_KEY_HANDLE keyHandle,
TPM_DELEGATE_PUBLIC *publicInfo,
TPM_ENCAUTH *delAuth,
TPM_AUTH *auth1,
TPM_DELEGATE_KEY_BLOB *blob)
{
TPM_RESULT res;
TPM_SESSION_DATA *session;
TPM_FAMILY_TABLE_ENTRY *fr;
TPM_SECRET secret;
TPM_KEY_DATA *key;
TPM_PUBKEY pubKey;
TPM_DELEGATE_SENSITIVE sensitive;
info("TPM_Delegate_CreateKeyDelegation()");
/* get key */
key = tpm_get_key(keyHandle);
if (key == NULL) return TPM_INVALID_KEYHANDLE;
/* verify authorization */
session = tpm_get_auth(auth1->authHandle);
if (session == NULL) return TPM_FAIL;
if (session->type != TPM_ST_OSAP && session->type != TPM_ST_DSAP) {
debug("session is neither of type OSAP nor DSAP");
return TPM_INVALID_AUTHHANDLE;
}
res = tpm_verify_auth(auth1, key->usageAuth, keyHandle);
if (res != TPM_SUCCESS) return res;
auth1->continueAuthSession = FALSE;
/* get specified family entry */
fr = tpm_get_family_row(publicInfo->familyID);
if (fr == NULL) return TPM_BADINDEX;
/* check delegation type */
if (publicInfo->permissions.delegateType != TPM_DEL_KEY_BITS) {
debug("invalid delegation type: %d", publicInfo->permissions.delegateType);
return TPM_BAD_PARAMETER;
}
blob->tag = TPM_TAG_DELEGATE_KEY_BLOB;
/* verify permissions if the access was delegated */
if (session->type == TPM_ST_DSAP) {
if (!(fr->flags & TPM_FAMFLAG_ENABLED)) return TPM_DISABLED_CMD;
if (session->familyID != publicInfo->familyID) return TPM_DELEGATE_FAMILY;
if (((session->permissions.per1 | publicInfo->permissions.per1)
!= session->permissions.per1)
|| ((session->permissions.per2 | publicInfo->permissions.per2)
!= session->permissions.per2)) return TPM_AUTHFAIL;
}
/* decrypt delegation secret */
tpm_decrypt_auth_secret(*delAuth, session->sharedSecret,
&session->lastNonceEven, secret);
/* compute key digest */
if (tpm_extract_pubkey(key, &pubKey)) {
debug("tpm_extraxt_pubkey() failed.");
return TPM_FAIL;
}
if (tpm_compute_pubkey_digest(&pubKey, &blob->pubKeyDigest)) {
debug("tpm_compute_pubkey_digest() failed");
free_TPM_PUBKEY(pubKey);
return TPM_FAIL;
}
free_TPM_PUBKEY(pubKey);
/* create a delegate sensitive structure */
sensitive.tag = TPM_TAG_DELEGATE_SENSITIVE;
memcpy(&sensitive.authValue, &secret, sizeof(TPM_SECRET));
/* generate IV and encrypt sensitive area */
blob->additionalSize = TPM_SYM_KEY_SIZE;
blob->additionalArea = tpm_malloc(blob->additionalSize);
if (blob->additionalArea == NULL) {
debug("tpm_malloc() failed.");
return TPM_NOSPACE;
}
tpm_get_random_bytes(blob->additionalArea, blob->additionalSize);
if (tpm_encrypt_sensitive(blob->additionalArea, blob->additionalSize,
&sensitive, &blob->sensitiveArea, &blob->sensitiveSize)) {
debug("tpm_encrypt_sensitive() failed.");
tpm_free(blob->additionalArea);
return TPM_ENCRYPT_ERROR;
}
/* copy public delegation information */
memcpy(&blob->pub, publicInfo, sizeof(TPM_DELEGATE_PUBLIC));
blob->pub.verificationCount = fr->verificationCount;
/* compute integrity digest */
tpm_compute_key_blob_digest(blob, &blob->integrityDigest);
return TPM_SUCCESS;
}
TPM_RESULT TPM_Delegate_CreateOwnerDelegation(BOOL increment,
TPM_DELEGATE_PUBLIC *publicInfo,
TPM_ENCAUTH *delAuth,
TPM_AUTH *auth1,
TPM_DELEGATE_OWNER_BLOB *blob)
{
TPM_RESULT res;
TPM_SESSION_DATA *session;
TPM_FAMILY_TABLE_ENTRY *fr;
TPM_SECRET secret;
TPM_DELEGATE_SENSITIVE sensitive;
info("[TPM_Delegate_CreateOwnerDelegation]");
/* verify authorization */
session = tpm_get_auth(auth1->authHandle);
if (session == NULL) return TPM_FAIL;
if (session->type != TPM_ST_OSAP && session->type != TPM_ST_DSAP) {
debug("session is neither of type OSAP nor DSAP");
return TPM_INVALID_AUTHHANDLE;
}
res = tpm_verify_auth(auth1, tpmData.permanent.data.ownerAuth, TPM_KH_OWNER);
if (res != TPM_SUCCESS) return res;
auth1->continueAuthSession = FALSE;
/* get specified family entry */
fr = tpm_get_family_row(publicInfo->familyID);
if (fr == NULL) return TPM_BADINDEX;
/* check delegation type */
if (publicInfo->permissions.delegateType != TPM_DEL_OWNER_BITS) {
debug("invalid delegation type: %d", publicInfo->permissions.delegateType);
return TPM_BAD_PARAMETER;
}
blob->tag = TPM_TAG_DELEGATE_OWNER_BLOB;
/* verify permissions if the access was delegated */
if (session->type == TPM_ST_DSAP) {
if (!(fr->flags & TPM_FAMFLAG_ENABLED)) return TPM_DISABLED_CMD;
if (session->familyID != publicInfo->familyID) return TPM_DELEGATE_FAMILY;
if (((session->permissions.per1 | publicInfo->permissions.per1)
!= session->permissions.per1)
|| ((session->permissions.per2 | publicInfo->permissions.per2)
!= session->permissions.per2)) return TPM_AUTHFAIL;
}
/* increment verification count if required */
if (increment) {
UINT32 i;
fr->verificationCount++;
debug("incrementing verificationCount to %d", fr->verificationCount);
tpmData.stclear.data.ownerReference = TPM_KH_OWNER;
/* invalidate all but this session */
for (i = 0; i < TPM_MAX_SESSIONS; i++) {
TPM_SESSION_DATA *s = &tpmData.stany.data.sessions[i];
if (s->type != TPM_ST_TRANSPORT && s != session) memset(s, 0, sizeof(*s));
}
}
/* decrypt delegation secret */
tpm_decrypt_auth_secret(*delAuth, session->sharedSecret,
&session->lastNonceEven, secret);
/* create a delegate sensitive structure */
sensitive.tag = TPM_TAG_DELEGATE_SENSITIVE;
memcpy(&sensitive.authValue, &secret, sizeof(TPM_SECRET));
/* generate IV and encrypt sensitive area */
blob->additionalSize = TPM_SYM_KEY_SIZE;
blob->additionalArea = tpm_malloc(blob->additionalSize);
if (blob->additionalArea == NULL) {
debug("tpm_malloc() failed.");
return TPM_NOSPACE;
}
tpm_get_random_bytes(blob->additionalArea, blob->additionalSize);
if (tpm_encrypt_sensitive(blob->additionalArea, blob->additionalSize,
&sensitive, &blob->sensitiveArea, &blob->sensitiveSize)) {
debug("tpm_encrypt_sensitive() failed.");
tpm_free(blob->additionalArea);
return TPM_ENCRYPT_ERROR;
}
/* copy public delegation information */
memcpy(&blob->pub, publicInfo, sizeof(TPM_DELEGATE_PUBLIC));
blob->pub.verificationCount = fr->verificationCount;
/* compute integrity digest */
tpm_compute_owner_blob_digest(blob, &blob->integrityDigest);
return TPM_SUCCESS;
}
TPM_RESULT TPM_Delegate_LoadOwnerDelegation(TPM_DELEGATE_INDEX index,
TPM_DELEGATE_OWNER_BLOB *blob,
TPM_AUTH *auth1)
{
TPM_RESULT res;
TPM_SESSION_DATA *session;
TPM_FAMILY_TABLE_ENTRY *fr;
TPM_DIGEST blobDigest;
TPM_DELEGATE_SENSITIVE sensitive;
TPM_DELEGATE_TABLE_ROW *dr;
BYTE *sens_buf;
UINT32 i;
info("TPM_Delegate_LoadOwnerDelegation()");
/* get specified family entry */
fr = tpm_get_family_row(blob->pub.familyID);
if (fr == NULL) return TPM_BADINDEX;
/* verify authorization */
session = tpm_get_auth(auth1->authHandle);
if (session == NULL) return TPM_AUTHFAIL;
if (auth1->authHandle != TPM_INVALID_AUTHHANDLE) {
res = tpm_verify_auth(auth1, tpmData.permanent.data.ownerAuth, TPM_KH_OWNER);
if (res != TPM_SUCCESS) return res;
if (session->type == TPM_ST_DSAP) {
if (session->familyID != blob->pub.familyID) return TPM_DELEGATE_FAMILY;
auth1->continueAuthSession = FALSE;
}
} else {
if (tpmData.permanent.flags.owned) return TPM_AUTHFAIL;
/* check delegate admin lock */
if (fr != NULL && (fr->flags & TPM_DELEGATE_ADMIN_LOCK)) {
debug("delegate admin lock is set");
return TPM_DELEGATE_LOCK;
}
/* verify maximal number of writes without an owner */
if (tpmData.permanent.data.noOwnerNVWrite >= TPM_MAX_NV_WRITE_NOOWNER)
return TPM_MAXNVWRITES;
tpmData.permanent.data.noOwnerNVWrite++;
}
/* verify the integrity of the blob and decode/decrypt the sensitive data */
if (!(fr->flags & TPM_FAMFLAG_ENABLED)) return TPM_DISABLED_CMD;
if (tpmData.permanent.flags.owned) {
tpm_compute_owner_blob_digest(blob, &blobDigest);
if (memcmp(&blob->integrityDigest, &blobDigest, sizeof(TPM_DIGEST)) != 0)
return TPM_AUTHFAIL;
/* decrypt sensitive data */
if (tpm_decrypt_sensitive(blob->additionalArea, blob->additionalSize,
blob->sensitiveArea, blob->sensitiveSize, &sensitive, &sens_buf)) {
debug("tpm_decrypt_sensitive() failed");
return TPM_DECRYPT_ERROR;
}
} else {
BYTE *ptr = blob->sensitiveArea;
UINT32 length = blob->sensitiveSize;
if (tpm_unmarshal_TPM_DELEGATE_SENSITIVE(&ptr, &length, &sensitive) != 0) {
debug("tpm_unmarshal_TPM_DELEGATE_SENSITIVE()");
return TPM_FAIL;
}
sens_buf = NULL;
}
if (sensitive.tag != TPM_TAG_DELEGATE_SENSITIVE) {
tpm_free(sens_buf);
return TPM_INVALID_STRUCTURE;
}
/* check that index is valid and copy data */
debug("index = %d", index);
if (index >= TPM_NUM_DELEGATE_TABLE_ENTRY) {
tpm_free(sens_buf);
return TPM_BADINDEX;
}
dr = &tpmData.permanent.data.delegateTable.delRow[index];
dr->valid = TRUE;
dr->tag = TPM_TAG_DELEGATE_TABLE_ROW;
memcpy(&dr->authValue, &sensitive.authValue, sizeof(TPM_SECRET));
memcpy(&dr->pub, &blob->pub, sizeof(TPM_DELEGATE_PUBLIC));
tpm_free(sens_buf);
/* invalidate all but this session */
for (i = 0; i < TPM_MAX_SESSIONS; i++) {
TPM_SESSION_DATA *s = &tpmData.stany.data.sessions[i];
if (s->type != TPM_ST_TRANSPORT && s != session) memset(s, 0, sizeof(*s));
}
tpmData.stclear.data.ownerReference = TPM_KH_OWNER;
return TPM_SUCCESS;
}
TPM_RESULT TPM_Delegate_ReadTable(UINT32 *familyTableSize, BYTE **familyTable,
UINT32 *delegateTableSize,
BYTE **delegateTable)
{
UINT32 i, length;
BYTE *ptr;
info("TPM_Delegate_ReadTable");
/* compute the size of the family table */
*familyTableSize = 0;
*familyTable = NULL;
for (i = 0; i < TPM_NUM_FAMILY_TABLE_ENTRY; i++) {
if (tpmData.permanent.data.familyTable.famRow[i].valid) {
*familyTableSize += sizeof_TPM_FAMILY_TABLE_ENTRY(
tpmData.permanent.data.familyTable.famRow[i]);
}
}
debug("family table size: %d", *familyTableSize);
/* allocate the table buffer and copy the family table */
if (*familyTableSize > 0) {
length = *familyTableSize;
ptr = *familyTable = tpm_malloc(*familyTableSize);
if (*familyTable == NULL) return TPM_RESOURCES;
for (i = 0; i < TPM_NUM_FAMILY_TABLE_ENTRY; i++) {
if (tpmData.permanent.data.familyTable.famRow[i].valid) {
debug("writing table row %d", i);
if (tpm_marshal_TPM_FAMILY_TABLE_ENTRY(&ptr, &length,
&tpmData.permanent.data.familyTable.famRow[i])) {
debug("tpm_marshal_TPM_FAMILY_TABLE_ENTRY() failed.");
tpm_free(*familyTable);
return TPM_FAIL;
}
}
}
}
/* computing the size of the delegation table */
*delegateTableSize = 0;
*delegateTable = NULL;
for (i = 0; i < TPM_NUM_DELEGATE_TABLE_ENTRY; i++) {
if (tpmData.permanent.data.delegateTable.delRow[i].valid) {
*delegateTableSize += sizeof_TPM_DELEGATE_PUBLIC(
tpmData.permanent.data.delegateTable.delRow[i].pub) + 4;
}
}
debug("delegation table size: %d", *delegateTableSize);
/* allocate the table buffer and copy the delegation table */
if (*delegateTableSize > 0) {
length = *delegateTableSize;
ptr = *delegateTable = tpm_malloc(*delegateTableSize);
if (*delegateTable == NULL) {
tpm_free(*familyTable);
return TPM_RESOURCES;
}
for (i = 0; i < TPM_NUM_DELEGATE_TABLE_ENTRY; i++) {
if (tpmData.permanent.data.delegateTable.delRow[i].valid) {
debug("writing delegate row %d", i);
if (tpm_marshal_TPM_DELEGATE_INDEX(&ptr, &length, i)
|| tpm_marshal_TPM_DELEGATE_PUBLIC(&ptr, &length,
&tpmData.permanent.data.delegateTable.delRow[i].pub)) {
debug("tpm_marshal_UINT32 or -TPM_DELEGATE_PUBLIC failed.");
tpm_free(*familyTable);
tpm_free(*delegateTable);
return TPM_FAIL;
}
}
}
}
return TPM_SUCCESS;
}
TPM_RESULT TPM_Delegate_UpdateVerification(UINT32 inputSize, BYTE *inputData,
TPM_AUTH *auth1, UINT32 *outputSize,
BYTE **outputData)
{
TPM_RESULT res;
TPM_SESSION_DATA *session;
info("TPM_Delegate_UpdateVerification()");
/* verify authorization */
session = tpm_get_auth(auth1->authHandle);
if (session == NULL) return TPM_AUTHFAIL;
res = tpm_verify_auth(auth1, tpmData.permanent.data.ownerAuth, TPM_KH_OWNER);
if (res != TPM_SUCCESS) return res;
/* determine the type of the input data */
if (inputSize == 4) {
TPM_DELEGATE_INDEX index;
TPM_DELEGATE_TABLE_ROW *dr;
TPM_FAMILY_TABLE_ENTRY *fr;
debug("TPM_DELEGATE_TABLE_ROW");
/* unmarshal delegate index */
if (tpm_unmarshal_TPM_DELEGATE_INDEX(&inputData, &inputSize, &index)) {
debug("tpm_unmarshal_TPM_DELEGATE_INDEX() failed.");
return TPM_FAIL;
}
/* get delegate and family row */
dr = tpm_get_delegate_row(index);
if (dr == NULL) return TPM_BADINDEX;
fr = tpm_get_family_row(dr->pub.familyID);
if (fr == NULL) return TPM_BADINDEX;
/* verify permissions if the access was delegated */
if (session->type == TPM_ST_DSAP) {
if (!(fr->flags & TPM_FAMFLAG_ENABLED)) return TPM_DISABLED_CMD;
if (session->familyID != fr->familyID) return TPM_DELEGATE_FAMILY;
}
/* update verification count */
dr->pub.verificationCount = fr->verificationCount;
*outputSize = 0;
*outputData = NULL;
} else if (inputData[0] == (TPM_TAG_DELEGATE_OWNER_BLOB >> 8)
&& inputData[1] == (TPM_TAG_DELEGATE_OWNER_BLOB & 0xff)) {
TPM_DELEGATE_OWNER_BLOB blob;
TPM_DIGEST blobDigest;
TPM_FAMILY_TABLE_ENTRY *fr;
UINT32 length;
BYTE *ptr;
debug("TPM_DELEGATE_OWNER_BLOB");
/* unmarshal the blob */
if (tpm_unmarshal_TPM_DELEGATE_OWNER_BLOB(&inputData, &inputSize, &blob)) {
debug("tpm_unmarshal_TPM_DELEGATE_OWNER_BLOB() failed.");
return TPM_FAIL;
}
/* validate the integrity of the blob */
tpm_compute_owner_blob_digest(&blob, &blobDigest);
if (memcmp(&blob.integrityDigest, &blobDigest, sizeof(TPM_DIGEST)) != 0)
return TPM_AUTHFAIL;
/* get family row */
fr = tpm_get_family_row(blob.pub.familyID);
if (fr == NULL) return TPM_BADINDEX;
/* verify permissions if the access was delegated */
if (session->type == TPM_ST_DSAP) {
if (!(fr->flags & TPM_FAMFLAG_ENABLED)) return TPM_DISABLED_CMD;
if (session->familyID != fr->familyID) return TPM_DELEGATE_FAMILY;
}
/* update verification count */
blob.pub.verificationCount = fr->verificationCount;
/* update the blob digest */
tpm_compute_owner_blob_digest(&blob, &blobDigest);
/* marshal the blob */
length = *outputSize = sizeof_TPM_DELEGATE_OWNER_BLOB(blob);
ptr = *outputData = tpm_malloc(*outputSize);
if (ptr == NULL) {
debug("tpm_malloc() failed.");
return TPM_NOSPACE;
}
if (tpm_marshal_TPM_DELEGATE_OWNER_BLOB(&ptr, &length, &blob) != 0) {
debug("tpm_marshal_TPM_DELEGATE_OWNER_BLOB() failed.");
tpm_free(*outputData);
return TPM_FAIL;
}
} else if (inputData[0] == (TPM_TAG_DELEGATE_KEY_BLOB >> 8)
&& inputData[1] == (TPM_TAG_DELEGATE_KEY_BLOB & 0xff)) {
TPM_DELEGATE_KEY_BLOB blob;
TPM_DIGEST blobDigest;
TPM_FAMILY_TABLE_ENTRY *fr;
UINT32 length;
BYTE *ptr;
debug("TPM_DELEGATE_KEY_BLOB");
/* unmarshal the blob */
if (tpm_unmarshal_TPM_DELEGATE_KEY_BLOB(&inputData, &inputSize, &blob)) {
debug("tpm_unmarshal_TPM_DELEGATE_KEY_BLOB() failed.");
return TPM_FAIL;
}
/* validate the integrity of the blob */
tpm_compute_key_blob_digest(&blob, &blobDigest);
if (memcmp(&blob.integrityDigest, &blobDigest, sizeof(TPM_DIGEST)) != 0)
return TPM_AUTHFAIL;
/* get family row */
fr = tpm_get_family_row(blob.pub.familyID);
if (fr == NULL) return TPM_BADINDEX;
/* verify permissions if the access was delegated */
if (session->type == TPM_ST_DSAP) {
if (!(fr->flags & TPM_FAMFLAG_ENABLED)) return TPM_DISABLED_CMD;
if (session->familyID != fr->familyID) return TPM_DELEGATE_FAMILY;
}
/* update verification count */
blob.pub.verificationCount = fr->verificationCount;
/* update the blob digest */
tpm_compute_key_blob_digest(&blob, &blobDigest);
/* marshal the blob */
length = *outputSize = sizeof_TPM_DELEGATE_KEY_BLOB(blob);
ptr = *outputData = tpm_malloc(*outputSize);
if (ptr == NULL) {
debug("tpm_malloc() failed.");
return TPM_NOSPACE;
}
if (tpm_marshal_TPM_DELEGATE_KEY_BLOB(&ptr, &length, &blob) != 0) {
debug("tpm_marshal_TPM_DELEGATE_KEY_BLOB() failed.");
tpm_free(*outputData);
return TPM_FAIL;
}
} else {
debug("unsupported input structure: %02x%02x", inputData[0], inputData[1]);
return TPM_BAD_PARAMETER;
}
return TPM_SUCCESS;
}
TPM_RESULT TPM_Delegate_VerifyDelegation(UINT32 delegateSize, BYTE *delegation)
{
info("TPM_Delegate_VerifyDelegation()");
if (delegation[0] == (TPM_TAG_DELEGATE_OWNER_BLOB >> 8)
&& delegation[1] == (TPM_TAG_DELEGATE_OWNER_BLOB & 0xff)) {
TPM_DELEGATE_OWNER_BLOB blob;
TPM_DIGEST blobDigest;
TPM_FAMILY_TABLE_ENTRY *fr;
TPM_DELEGATE_SENSITIVE sensitive;
BYTE *sens_buf;
debug("TPM_DELEGATE_OWNER_BLOB");
/* unmarshal the blob */
if (tpm_unmarshal_TPM_DELEGATE_OWNER_BLOB(&delegation, &delegateSize, &blob)) {
debug("tpm_unmarshal_TPM_DELEGATE_OWNER_BLOB() failed.");
return TPM_FAIL;
}
/* validate the integrity of the blob */
tpm_compute_owner_blob_digest(&blob, &blobDigest);
if (memcmp(&blob.integrityDigest, &blobDigest, sizeof(TPM_DIGEST)) != 0)
return TPM_AUTHFAIL;
/* get family row */
fr = tpm_get_family_row(blob.pub.familyID);
if (fr == NULL) return TPM_BADINDEX;
if (!(fr->flags & TPM_FAMFLAG_ENABLED)) return TPM_DISABLED_CMD;
/* verify verification count */
if (blob.pub.verificationCount != fr->verificationCount)
return TPM_FAMILYCOUNT;
/* decrypt and verify sensitive area */
if (tpm_decrypt_sensitive(blob.additionalArea, blob.additionalSize,
blob.sensitiveArea, blob.sensitiveSize, &sensitive, &sens_buf)) {
debug("tpm_decrypt_sensitive() failed");
return TPM_DECRYPT_ERROR;
}
tpm_free(sens_buf);
if (sensitive.tag != TPM_TAG_DELEGATE_SENSITIVE) return TPM_BAD_PARAMETER;
} else if (delegation[0] == (TPM_TAG_DELEGATE_KEY_BLOB >> 8)
&& delegation[1] == (TPM_TAG_DELEGATE_KEY_BLOB & 0xff)) {
TPM_DELEGATE_KEY_BLOB blob;
TPM_DIGEST blobDigest;
TPM_FAMILY_TABLE_ENTRY *fr;
TPM_DELEGATE_SENSITIVE sensitive;
BYTE *sens_buf;
debug("TPM_DELEGATE_KEY_BLOB");
/* unmarshal the blob */
if (tpm_unmarshal_TPM_DELEGATE_KEY_BLOB(&delegation, &delegateSize, &blob)) {
debug("tpm_unmarshal_TPM_DELEGATE_OWNER_BLOB() failed.");
return TPM_FAIL;
}
/* validate the integrity of the blob */
tpm_compute_key_blob_digest(&blob, &blobDigest);
if (memcmp(&blob.integrityDigest, &blobDigest, sizeof(TPM_DIGEST)) != 0)
return TPM_AUTHFAIL;
/* get family row */
fr = tpm_get_family_row(blob.pub.familyID);
if (fr == NULL) return TPM_BADINDEX;
if (!(fr->flags & TPM_FAMFLAG_ENABLED)) return TPM_DISABLED_CMD;
/* verify verification count */
if (blob.pub.verificationCount != fr->verificationCount)
return TPM_FAMILYCOUNT;
/* decrypt and verify sensitive area */
if (tpm_decrypt_sensitive(blob.additionalArea, blob.additionalSize,
blob.sensitiveArea, blob.sensitiveSize, &sensitive, &sens_buf)) {
debug("tpm_decrypt_sensitive() failed");
return TPM_DECRYPT_ERROR;
}
tpm_free(sens_buf);
if (sensitive.tag != TPM_TAG_DELEGATE_SENSITIVE) return TPM_BAD_PARAMETER;
} else {
debug("unsupported input structure: %02x%02x", delegation[0], delegation[1]);
return TPM_BAD_PARAMETER;
}
return TPM_SUCCESS;
}