blob: 728fed2dc81c8a630deafdd1a5908049df60d18b [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_audit.c 385 2010-02-17 15:41:28Z mast $
*/
#include "tpm_emulator.h"
#include "tpm_commands.h"
#include "tpm_data.h"
#include "tpm_marshalling.h"
#include "tpm_handles.h"
#include <crypto/sha1.h>
/*
* Auditing ([TPM_Part3], Section 8)
* The TPM generates an audit event in response to the TPM executing a
* function that has the audit flag set to TRUE for that function. The
* TPM maintains an extended value for all audited operations.
*/
#define AUDIT_STATUS tpmData.permanent.data.ordinalAuditStatus
void tpm_audit_request(TPM_COMMAND_CODE ordinal, TPM_REQUEST *req)
{
tpm_sha1_ctx_t sha1_ctx;
BYTE buf[sizeof_TPM_AUDIT_EVENT_IN(x)], *ptr;
UINT32 len;
TPM_COMMAND_CODE ord = ordinal & TPM_ORD_INDEX_MASK;
if (ord < TPM_ORD_MAX
&& (AUDIT_STATUS[ord / 8] & (1 << (ord & 0x07)))) {
info("tpm_audit_request()");
/* is there already an audit session running? */
if (!tpmData.stany.data.auditSession) {
tpmData.stany.data.auditSession = TRUE;
tpmData.permanent.data.auditMonotonicCounter++;
}
/* update audit digest */
ptr = buf; len = sizeof(buf);
tpm_marshal_TPM_TAG(&ptr, &len, TPM_TAG_AUDIT_EVENT_IN);
tpm_marshal_TPM_COMMAND_CODE(&ptr, &len, ordinal);
tpm_sha1_init(&sha1_ctx);
tpm_sha1_update(&sha1_ctx, req->param, req->paramSize);
tpm_sha1_final(&sha1_ctx, ptr);
ptr += 20; len -= 20;
tpm_marshal_TPM_TAG(&ptr, &len, TPM_TAG_COUNTER_VALUE);
tpm_marshal_UINT32(&ptr, &len, 0);
tpm_marshal_UINT32(&ptr, &len, tpmData.permanent.data.auditMonotonicCounter);
tpm_sha1_init(&sha1_ctx);
tpm_sha1_update(&sha1_ctx, tpmData.stany.data.auditDigest.digest, sizeof(TPM_DIGEST));
tpm_sha1_update(&sha1_ctx, buf, sizeof(buf));
tpm_sha1_final(&sha1_ctx, tpmData.stany.data.auditDigest.digest);
}
}
void tpm_audit_response(TPM_COMMAND_CODE ordinal, TPM_RESPONSE *rsp)
{
tpm_sha1_ctx_t sha1_ctx;
BYTE buf[sizeof_TPM_AUDIT_EVENT_OUT(x)], *ptr;
UINT32 len;
TPM_COMMAND_CODE ord = ordinal & TPM_ORD_INDEX_MASK;
if (ord < TPM_ORD_MAX
&& (AUDIT_STATUS[ord / 8] & (1 << (ord & 0x07)))) {
info("tpm_audit_response()");
/* update audit digest */
ptr = buf; len = sizeof(buf);
tpm_marshal_TPM_TAG(&ptr, &len, TPM_TAG_AUDIT_EVENT_OUT);
tpm_marshal_TPM_COMMAND_CODE(&ptr, &len, ordinal);
tpm_sha1_init(&sha1_ctx);
tpm_sha1_update(&sha1_ctx, rsp->param, rsp->paramSize);
tpm_sha1_final(&sha1_ctx, ptr);
ptr += 20; len -= 20;
tpm_marshal_TPM_TAG(&ptr, &len, TPM_TAG_COUNTER_VALUE);
tpm_marshal_UINT32(&ptr, &len, 0);
tpm_marshal_UINT32(&ptr, &len, tpmData.permanent.data.auditMonotonicCounter);
tpm_marshal_TPM_RESULT(&ptr, &len, rsp->result);
tpm_sha1_init(&sha1_ctx);
tpm_sha1_update(&sha1_ctx, tpmData.stany.data.auditDigest.digest, sizeof(TPM_DIGEST));
tpm_sha1_update(&sha1_ctx, buf, sizeof(buf));
tpm_sha1_final(&sha1_ctx, tpmData.stany.data.auditDigest.digest);
}
}
/* number of bits to represent 0, 1, 2, 3 ... */
static uint8_t bits[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4 };
TPM_RESULT TPM_GetAuditDigest(UINT32 startOrdinal,
TPM_COUNTER_VALUE *counterValue,
TPM_DIGEST *auditDigest, BOOL *more,
UINT32 *ordSize, UINT32 **ordList)
{
UINT32 i, j, len, *ptr;
info("TPM_GetAuditDigest()");
/* compute (maximal) size of the ordinal list */
for (len = 0, i = startOrdinal/8; i < TPM_ORD_MAX/8; i++) {
len += bits[AUDIT_STATUS[i] & 0x0f];
len += bits[(AUDIT_STATUS[i] >> 4) & 0x0f];
}
/* setup ordinal list */
ptr = *ordList = tpm_malloc(len);
if (ptr == NULL) return TPM_FAIL;
for (*ordSize = 0, i = startOrdinal/8; i < TPM_ORD_MAX/8; i++) {
if (AUDIT_STATUS[i]) for (j = 0; j < 8; j++) {
if ((AUDIT_STATUS[i] & (1 << j)) && i * 8 + j > startOrdinal) {
*ptr++ = i * 8 + j;
*ordSize += 4;
}
}
}
counterValue->tag = TPM_TAG_COUNTER_VALUE;
memset(counterValue->label, 0, sizeof(counterValue->label));
counterValue->counter = tpmData.permanent.data.auditMonotonicCounter;
memcpy(auditDigest, &tpmData.stany.data.auditDigest, sizeof(TPM_DIGEST));
if (more != NULL) *more = FALSE;
return TPM_SUCCESS;
}
TPM_RESULT TPM_GetAuditDigestSigned(TPM_KEY_HANDLE keyHandle,
BOOL closeAudit, TPM_NONCE *antiReplay,
TPM_AUTH *auth1,
TPM_COUNTER_VALUE *counterValue,
TPM_DIGEST *auditDigest,
TPM_DIGEST *ordinalDigest,
UINT32 *sigSize, BYTE **sig)
{
TPM_RESULT res;
TPM_KEY_DATA *key;
UINT32 ordSize;
UINT32 *ordList;
BYTE buf[TPM_ORD_MAX * 4];
BYTE *ptr;
UINT32 len;
tpm_sha1_ctx_t ctx;
info("TPM_GetAuditDigestSigned()");
/* get key */
key = tpm_get_key(keyHandle);
if (key == NULL) return TPM_INVALID_KEYHANDLE;
if (key->keyUsage != TPM_KEY_SIGNING && key->keyUsage != TPM_KEY_IDENTITY
&& key->keyUsage != TPM_KEY_LEGACY) return TPM_INVALID_KEYUSAGE;
/* 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;
}
/* get audit digest */
res = TPM_GetAuditDigest(0, counterValue, auditDigest, NULL,
&ordSize, &ordList);
if (res != TPM_SUCCESS) return res;
/* allocate buffer memory */
len = sizeof(buf);
ptr = buf;
if (tpm_marshal_UINT32_ARRAY(&ptr, &len, ordList, ordSize/4) != 0) {
debug("tpm_marshal_UINT32_ARRAY() failed.");
tpm_free(ordList);
return TPM_FAIL;
}
tpm_free(ordList);
/* compute ordinal digest */
tpm_sha1_init(&ctx);
tpm_sha1_update(&ctx, buf, ordSize);
tpm_sha1_final(&ctx, ordinalDigest->digest);
/* setup a TPM_SIGN_INFO structure */
memset(buf, 0, sizeof(buf));
memcpy(&buf[0], "\x00\x05", 2);
memcpy(&buf[2], "ADIG", 4);
memcpy(&buf[6], antiReplay->nonce, 20);
len = sizeof(buf) - 26;
ptr = &buf[26];
if (tpm_marshal_UINT32(&ptr, &len,
20 + sizeof_TPM_COUNTER_VALUE((*counterValue)) + 20) != 0) {
debug("tpm_marshal_UINT32() failed.");
return TPM_FAIL;
}
memcpy(ptr, auditDigest->digest, 20);
len -= 20;
ptr += 20;
if (tpm_marshal_TPM_COUNTER_VALUE(&ptr, &len, counterValue) != 0) {
debug("tpm_marshal_TPM_COUNTER_VALUE() failed.");
return TPM_FAIL;
}
memcpy(ptr, ordinalDigest->digest, 20);
/* check key usage */
if (closeAudit) {
if (key->keyUsage == TPM_KEY_IDENTITY) {
memset(&tpmData.stany.data.auditDigest, 0, sizeof(TPM_DIGEST));
} else {
return TPM_INVALID_KEYUSAGE;
}
}
/* sign data */
if (key->sigScheme == TPM_SS_RSASSAPKCS1v15_SHA1) {
debug("TPM_SS_RSASSAPKCS1v15_SHA1");
len = 30 + 20 + sizeof_TPM_COUNTER_VALUE((*counterValue)) + 20;
tpm_sha1_init(&ctx);
tpm_sha1_update(&ctx, buf, len);
tpm_sha1_final(&ctx, buf);
res = tpm_sign(key, auth1, FALSE, buf, SHA1_DIGEST_LENGTH, sig, sigSize);
} else if (key->sigScheme == TPM_SS_RSASSAPKCS1v15_INFO) {
debug("TPM_SS_RSASSAPKCS1v15_INFO");
res = tpm_sign(key, auth1, TRUE, buf, sizeof(buf), sig, sigSize);
} else {
debug("unsupported signature scheme: %02x", key->sigScheme);
res = TPM_INVALID_KEYUSAGE;
}
return res;
}
TPM_RESULT TPM_SetOrdinalAuditStatus(TPM_COMMAND_CODE ordinalToAudit,
BOOL auditState, TPM_AUTH *auth1)
{
TPM_RESULT res;
info("TPM_SetOrdinalAuditStatus()");
/* verify authorization */
res = tpm_verify_auth(auth1, tpmData.permanent.data.ownerAuth, TPM_KH_OWNER);
if (res != TPM_SUCCESS) return res;
/* set ordinal's audit status */
if (ordinalToAudit > TPM_ORD_MAX) return TPM_BADINDEX;
ordinalToAudit &= TPM_ORD_INDEX_MASK;
if (auditState) {
AUDIT_STATUS[ordinalToAudit / 8] |= (1 << (ordinalToAudit & 0x07));
} else {
AUDIT_STATUS[ordinalToAudit / 8] &= ~(1 << (ordinalToAudit & 0x07));
}
return TPM_SUCCESS;
}