blob: e6bb1b44f7305a134e8af14925f26f4a76e6ab7d [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_integrity.c 364 2010-02-11 10:24:45Z mast $
*/
#include "tpm_emulator.h"
#include "tpm_commands.h"
#include "tpm_data.h"
#include "crypto/sha1.h"
#include "crypto/rsa.h"
#include "tpm_handles.h"
#include "tpm_marshalling.h"
/*
* Integrity Collection and Reporting ([TPM_Part3], Section 16)
* This section deals with what commands have direct access to the PCR.
*/
#define PCR_ATTRIB tpmData.permanent.data.pcrAttrib
#define PCR_VALUE tpmData.permanent.data.pcrValue
#define LOCALITY tpmData.stany.flags.localityModifier
TPM_RESULT TPM_Extend(TPM_PCRINDEX pcrNum, TPM_DIGEST *inDigest,
TPM_PCRVALUE *outDigest)
{
tpm_sha1_ctx_t ctx;
info("TPM_Extend()");
if (pcrNum >= TPM_NUM_PCR) return TPM_BADINDEX;
if (!(PCR_ATTRIB[pcrNum].pcrExtendLocal & (1 << LOCALITY))) return TPM_BAD_LOCALITY;
/* compute new PCR value as SHA-1(old PCR value || inDigest) */
tpm_sha1_init(&ctx);
tpm_sha1_update(&ctx, PCR_VALUE[pcrNum].digest, sizeof(PCR_VALUE[pcrNum].digest));
tpm_sha1_update(&ctx, inDigest->digest, sizeof(inDigest->digest));
tpm_sha1_final(&ctx, PCR_VALUE[pcrNum].digest);
/* set output digest */
if (tpmData.permanent.flags.disable) {
memset(outDigest->digest, 0, sizeof(*outDigest->digest));
} else {
memcpy(outDigest, &PCR_VALUE[pcrNum], sizeof(TPM_PCRVALUE));
}
return TPM_SUCCESS;
}
TPM_RESULT TPM_PCRRead(TPM_PCRINDEX pcrIndex, TPM_PCRVALUE *outDigest)
{
info("TPM_PCRRead()");
if (pcrIndex >= TPM_NUM_PCR) return TPM_BADINDEX;
memcpy(outDigest, &PCR_VALUE[pcrIndex], sizeof(TPM_PCRVALUE));
return TPM_SUCCESS;
}
TPM_RESULT TPM_Quote(TPM_KEY_HANDLE keyHandle, TPM_NONCE *extrnalData,
TPM_PCR_SELECTION *targetPCR, TPM_AUTH *auth1,
TPM_PCR_COMPOSITE *pcrData,
UINT32 *sigSize, BYTE **sig)
{
TPM_RESULT res;
TPM_KEY_DATA *key;
TPM_COMPOSITE_HASH hash;
BYTE buf[48];
info("TPM_Quote()");
/* get key */
key = tpm_get_key(keyHandle);
if (key == NULL) return TPM_INVALID_KEYHANDLE;
/* verify authorization */
if (auth1->authHandle != TPM_INVALID_HANDLE
|| key->authDataUsage != TPM_AUTH_NEVER) {
res = tpm_verify_auth(auth1, key->usageAuth, keyHandle);
if (res != TPM_SUCCESS) return res;
}
if (key->sigScheme != TPM_SS_RSASSAPKCS1v15_SHA1)
return TPM_INAPPROPRIATE_SIG;
if (key->keyUsage != TPM_KEY_SIGNING && key->keyUsage != TPM_KEY_LEGACY
&& key->keyUsage != TPM_KEY_IDENTITY)
return TPM_INVALID_KEYUSAGE;
/* compute composite hash */
res = tpm_compute_pcr_digest(targetPCR, &hash, pcrData);
if (res != TPM_SUCCESS) return res;
/* setup quote info and sign it */
memcpy(&buf[ 0], "\x01\x01\x00\x00QUOT", 8);
memcpy(&buf[ 8], hash.digest, 20);
memcpy(&buf[28], extrnalData->nonce, 20);
*sigSize = key->key.size >> 3;
*sig = tpm_malloc(*sigSize);
if (*sig == NULL) return TPM_FAIL;
if (tpm_rsa_sign(&key->key, RSA_SSA_PKCS1_SHA1, buf, 48, *sig)) {
tpm_free(*sig);
return TPM_FAIL;
}
return TPM_SUCCESS;
}
TPM_RESULT TPM_PCR_Reset(TPM_PCR_SELECTION *pcrSelection)
{
int i;
info("TPM_PCR_Reset()");
if ((pcrSelection->sizeOfSelect * 8) > TPM_NUM_PCR)
return TPM_INVALID_PCR_INFO;
/* this command must be atomic, thus we first verify that all
registers are resetable ... */
for (i = 0; i < pcrSelection->sizeOfSelect * 8; i++) {
/* is PCR number i selected ? */
if (pcrSelection->pcrSelect[i >> 3] & (1 << (i & 7))) {
if (!PCR_ATTRIB[i].pcrReset) return TPM_NOTRESETABLE;
if (!(PCR_ATTRIB[i].pcrResetLocal & (1 << LOCALITY))) return TPM_NOTLOCAL;
}
}
/* ... then we reset all registers at once */
for (i = 0; i < pcrSelection->sizeOfSelect * 8; i++) {
/* is PCR number i selected ? */
if (pcrSelection->pcrSelect[i >> 3] & (1 << (i & 7))) {
memset(PCR_VALUE[i].digest, 0, sizeof(PCR_VALUE[i].digest));
}
}
return TPM_SUCCESS;
}
TPM_RESULT tpm_compute_pcr_digest(TPM_PCR_SELECTION *pcrSelection,
TPM_COMPOSITE_HASH *digest,
TPM_PCR_COMPOSITE *composite)
{
int i,j;
TPM_PCR_COMPOSITE comp;
tpm_sha1_ctx_t ctx;
UINT32 len;
BYTE *buf, *ptr;
info("tpm_compute_pcr_digest()");
/* create PCR composite */
if ((pcrSelection->sizeOfSelect * 8) > TPM_NUM_PCR
|| pcrSelection->sizeOfSelect == 0) return TPM_INVALID_PCR_INFO;
for (i = 0, j = 0; i < pcrSelection->sizeOfSelect * 8; i++) {
/* is PCR number i selected ? */
if (pcrSelection->pcrSelect[i >> 3] & (1 << (i & 7))) {
memcpy(&comp.pcrValue[j++], &PCR_VALUE[i], sizeof(TPM_PCRVALUE));
}
}
memcpy(&comp.select, pcrSelection, sizeof(TPM_PCR_SELECTION));
comp.valueSize = j * sizeof(TPM_PCRVALUE);
debug("comp.valueSize = %d", comp.valueSize);
if (comp.valueSize > 0) {
/* marshal composite and compute hash */
len = sizeof_TPM_PCR_COMPOSITE(comp);
buf = ptr = tpm_malloc(len);
if (buf == NULL
|| tpm_marshal_TPM_PCR_COMPOSITE(&ptr, &len, &comp)) {
tpm_free(buf);
return TPM_FAIL;
}
tpm_sha1_init(&ctx);
tpm_sha1_update(&ctx, buf, sizeof_TPM_PCR_COMPOSITE(comp));
tpm_sha1_final(&ctx, digest->digest);
tpm_free(buf);
} else {
memset(digest, 0, sizeof(TPM_COMPOSITE_HASH));
}
/* copy composite if requested */
if (composite != NULL)
memcpy(composite, &comp, sizeof(TPM_PCR_COMPOSITE));
return TPM_SUCCESS;
}
TPM_RESULT tpm_verify_pcr(TPM_KEY_DATA *key, BOOL atrelease, BOOL atcreation)
{
TPM_RESULT res;
TPM_COMPOSITE_HASH digest;
info("tpm_verify_pcr()");
if (atrelease) {
res = tpm_compute_pcr_digest(&key->pcrInfo.releasePCRSelection,
&digest, NULL);
if (res != TPM_SUCCESS) return res;
if (memcmp(&digest, &key->pcrInfo.digestAtRelease,
sizeof(TPM_COMPOSITE_HASH))) return TPM_WRONGPCRVAL;
if (key->pcrInfo.tag == TPM_TAG_PCR_INFO_LONG
&& !(key->pcrInfo.localityAtRelease
& (1 << tpmData.stany.flags.localityModifier)))
return TPM_BAD_LOCALITY;
}
if (atcreation) {
res = tpm_compute_pcr_digest(&key->pcrInfo.creationPCRSelection,
&digest, NULL);
if (res != TPM_SUCCESS) return res;
if (memcmp(&digest, &key->pcrInfo.digestAtCreation,
sizeof(TPM_COMPOSITE_HASH))) return TPM_WRONGPCRVAL;
if (key->pcrInfo.tag == TPM_TAG_PCR_INFO_LONG
&& !(key->pcrInfo.localityAtCreation
& (1 << tpmData.stany.flags.localityModifier)))
return TPM_BAD_LOCALITY;
}
return TPM_SUCCESS;
}
TPM_RESULT TPM_Quote2(TPM_KEY_HANDLE keyHandle, TPM_NONCE *externalData,
TPM_PCR_SELECTION *targetPCR, BOOL addVersion,
TPM_AUTH *auth1, TPM_PCR_INFO_SHORT *pcrData,
UINT32 *versionInfoSize,
TPM_CAP_VERSION_INFO *versionInfo,
UINT32 *sigSize, BYTE **sig)
{
TPM_RESULT res;
TPM_KEY_DATA *key;
TPM_COMPOSITE_HASH H1;
TPM_QUOTE_INFO2 Q1;
tpm_sha1_ctx_t ctx;
TPM_DIGEST digest;
UINT32 respSize, len, size;
BYTE *resp, *ptr, *buf;
info("TPM_Quote2()");
/* get key by keyHandle*/
key = tpm_get_key(keyHandle);
if (key == NULL) return TPM_INVALID_KEYHANDLE;
/* 1. The TPM MUST validate the AuthData to use the key pointed
* to by keyhandle */
if (auth1->authHandle != TPM_INVALID_HANDLE
|| key->authDataUsage != TPM_AUTH_NEVER) {
res = tpm_verify_auth(auth1, key->usageAuth, keyHandle);
if (res != TPM_SUCCESS) return res;
}
/* 2. Validate that keyHandle->sigScheme is TPM_SS_RSASSAPKCS1v15_SHA1 or
TPM_SS_RSASSAPKCS1v15_INFO, if not return TPM_INAPPROPRIATE_SIG. */
if ((key->sigScheme != TPM_SS_RSASSAPKCS1v15_SHA1) &&
(key->sigScheme != TPM_SS_RSASSAPKCS1v15_INFO))
return TPM_INAPPROPRIATE_SIG;
/* 3. Validate that keyHandle->keyUsage is TPM_KEY_SIGNING, TPM_KEY_IDENTITY,
or TPM_KEY_LEGACY, if not return TPM_INVALID_KEYUSAGE */
if ((key->keyUsage != TPM_KEY_SIGNING) && (key->keyUsage != TPM_KEY_LEGACY)
&& (key->keyUsage != TPM_KEY_IDENTITY))
return TPM_INVALID_KEYUSAGE;
/* 4. Validate targetPCR is a valid TPM_PCR_SELECTION structure,
* on errors return TPM_INVALID_PCR_INFO */
if (targetPCR->sizeOfSelect > sizeof(targetPCR->pcrSelect))
return TPM_INVALID_PCR_INFO;
/* 5. Create H1 a SHA-1 hash of a TPM_PCR_COMPOSITE using the
* TPM_STCLEAR_DATA->PCR[] indicated by targetPCR->pcrSelect */
res = tpm_compute_pcr_digest(targetPCR, &H1, NULL);
if (res != TPM_SUCCESS) return res;
/* 6. Create S1 a TPM_PCR_INFO_SHORT */
/* a. Set S1->pcrSelection to targetPCR */
pcrData->pcrSelection.sizeOfSelect = targetPCR->sizeOfSelect;
memcpy(pcrData->pcrSelection.pcrSelect, targetPCR->pcrSelect, targetPCR->sizeOfSelect);
/* b. Set S1->localityAtRelease to TPM_STANY_DATA -> localityModifier */
pcrData->localityAtRelease = tpmData.stany.flags.localityModifier;
/* c. Set S1->digestAtRelease to H1 */
memcpy(&pcrData->digestAtRelease, &H1, sizeof(TPM_COMPOSITE_HASH));
/* 7. Create Q1 a TPM_QUOTE_INFO2 structure */
Q1.tag = TPM_TAG_QUOTE_INFO2;
/* a. Set Q1->fixed to "QUT2" */
Q1.fixed[0] = 'Q', Q1.fixed[1] = 'U', Q1.fixed[2] = 'T', Q1.fixed[3] = '2';
/* b. Set Q1->infoShort to S1 */
Q1.infoShort.pcrSelection.sizeOfSelect = pcrData->pcrSelection.sizeOfSelect;
memcpy(Q1.infoShort.pcrSelection.pcrSelect,
pcrData->pcrSelection.pcrSelect, pcrData->pcrSelection.sizeOfSelect);
Q1.infoShort.localityAtRelease = pcrData->localityAtRelease;
memcpy(Q1.infoShort.digestAtRelease.digest,
pcrData->digestAtRelease.digest, sizeof(TPM_COMPOSITE_HASH));
/* c. Set Q1->externalData to externalData */
memcpy(&Q1.externalData, externalData, sizeof(TPM_NONCE));
size = len = sizeof_TPM_QUOTE_INFO2(Q1);
buf = ptr = tpm_malloc(size);
if (buf == NULL) return TPM_NOSPACE;
if (tpm_marshal_TPM_QUOTE_INFO2(&ptr, &len, &Q1) || (len != 0)) {
debug("TPM_Quote2(): tpm_marshal_TPM_QUOTE_INFO2() failed.");
tpm_free(buf);
return TPM_FAIL;
}
/* 8. If addVersion is TRUE */
if (addVersion == TRUE) {
debug("TPM_Quote2(): addVersion == TRUE");
/* a. Concatenate to Q1 a TPM_CAP_VERSION_INFO structure */
res = TPM_GetCapability(TPM_CAP_VERSION_VAL, 0, NULL, &respSize, &resp);
if (res != TPM_SUCCESS) {
debug("TPM_Quote2(): cap_version_val() failed.");
tpm_free(buf);
return TPM_FAIL;
}
/* b. Set the output parameters for versionInfo */
ptr = resp;
len = respSize;
if (tpm_unmarshal_TPM_CAP_VERSION_INFO(&ptr, &len, versionInfo) ||
(len != 0)) {
debug("TPM_Quote2(): tpm_unmarshal_TPM_CAP_VERSION_INFO() failed.");
tpm_free(buf);
return TPM_FAIL;
}
*versionInfoSize = respSize;
} else { /* 9. Else */
debug("TPM_Quote2(): addVersion == FALSE");
/* a. Set versionInfoSize to 0 */
*versionInfoSize = 0;
/* b. Return no bytes in versionInfo */
}
/* 10. Sign a SHA-1 hash of Q1 using keyHandle as the signature key */
tpm_sha1_init(&ctx);
tpm_sha1_update(&ctx, buf, size);
tpm_free(buf);
if (addVersion == TRUE) {
tpm_sha1_update(&ctx, resp, respSize);
tpm_free(resp);
}
tpm_sha1_final(&ctx, digest.digest);
/* 11. Return the signature in sig */
return tpm_sign(key, auth1, FALSE, digest.digest, sizeof(TPM_DIGEST), sig, sigSize);
}