/*
* Licensed Materials - Property of IBM
*
* trousers - An open source TCG Software Stack
*
* (C) Copyright International Business Machines Corp. 2004-2007
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "trousers/tss.h"
#include "trousers/trousers.h"
#include "trousers_types.h"
#include "spi_utils.h"
#include "capabilities.h"
#include "tsplog.h"
#include "obj.h"
TSS_RESULT
Tspi_TPM_CreateEndorsementKey(TSS_HTPM hTPM, /* in */
TSS_HKEY hKey, /* in */
TSS_VALIDATION * pValidationData) /* in, out */
{
TCPA_NONCE antiReplay;
TCPA_DIGEST digest;
TSS_RESULT result;
UINT32 ekSize;
BYTE *ek;
TSS_KEY dummyKey;
UINT64 offset;
TCPA_DIGEST hash;
UINT32 newEKSize;
BYTE *newEK;
TSS_HCONTEXT tspContext;
TCPA_PUBKEY pubEK;
Trspi_HashCtx hashCtx;
__tspi_memset(&pubEK, 0, sizeof(TCPA_PUBKEY));
__tspi_memset(&dummyKey, 0, sizeof(TSS_KEY));
if ((result = obj_tpm_get_tsp_context(hTPM, &tspContext)))
return result;
if ((result = obj_rsakey_get_blob(hKey, &ekSize, &ek)))
return result;
offset = 0;
if ((result = UnloadBlob_TSS_KEY(&offset, ek, &dummyKey)))
return result;
offset = 0;
Trspi_LoadBlob_KEY_PARMS(&offset, ek, &dummyKey.algorithmParms);
free_key_refs(&dummyKey);
ekSize = offset;
if (pValidationData == NULL) {
if ((result = get_local_random(tspContext, FALSE, sizeof(TCPA_NONCE),
(BYTE **)antiReplay.nonce))) {
LogError("Failed to create random nonce");
return TSPERR(TSS_E_INTERNAL_ERROR);
}
} else {
if (pValidationData->ulExternalDataLength < sizeof(antiReplay.nonce))
return TSPERR(TSS_E_BAD_PARAMETER);
memcpy(antiReplay.nonce, pValidationData->rgbExternalData,
sizeof(antiReplay.nonce));
}
if ((result = TCS_API(tspContext)->CreateEndorsementKeyPair(tspContext, antiReplay, ekSize,
ek, &newEKSize, &newEK,
&digest)))
return result;
if (pValidationData == NULL) {
result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
result |= Trspi_HashUpdate(&hashCtx, newEKSize, newEK);
result |= Trspi_HashUpdate(&hashCtx, TCPA_SHA1_160_HASH_LEN, antiReplay.nonce);
if ((result |= Trspi_HashFinal(&hashCtx, hash.digest)))
goto done;
if (memcmp(hash.digest, digest.digest, TCPA_SHA1_160_HASH_LEN)) {
LogError("Internal verification failed");
result = TSPERR(TSS_E_EK_CHECKSUM);
goto done;
}
} else {
pValidationData->rgbData = calloc_tspi(tspContext, newEKSize);
if (pValidationData->rgbData == NULL) {
LogError("malloc of %u bytes failed.", newEKSize);
result = TSPERR(TSS_E_OUTOFMEMORY);
goto done;
}
pValidationData->ulDataLength = newEKSize;
memcpy(pValidationData->rgbData, newEK, newEKSize);
memcpy(&pValidationData->rgbData[ekSize], antiReplay.nonce,
sizeof(antiReplay.nonce));
pValidationData->rgbValidationData = calloc_tspi(tspContext,
TCPA_SHA1_160_HASH_LEN);
if (pValidationData->rgbValidationData == NULL) {
LogError("malloc of %d bytes failed.", TCPA_SHA1_160_HASH_LEN);
free_tspi(tspContext, pValidationData->rgbData);
pValidationData->rgbData = NULL;
pValidationData->ulDataLength = 0;
result = TSPERR(TSS_E_OUTOFMEMORY);
goto done;
}
pValidationData->ulValidationDataLength = TCPA_SHA1_160_HASH_LEN;
memcpy(pValidationData->rgbValidationData, digest.digest, TCPA_SHA1_160_HASH_LEN);
}
if ((result = obj_rsakey_set_pubkey(hKey, FALSE, newEK)) && pValidationData) {
free_tspi(tspContext, pValidationData->rgbValidationData);
free_tspi(tspContext, pValidationData->rgbData);
pValidationData->rgbData = NULL;
pValidationData->ulDataLength = 0;
pValidationData->rgbValidationData = NULL;
pValidationData->ulValidationDataLength = 0;
}
done:
free(newEK);
return result;
}
TSS_RESULT
Tspi_TPM_GetPubEndorsementKey(TSS_HTPM hTPM, /* in */
TSS_BOOL fOwnerAuthorized, /* in */
TSS_VALIDATION *pValidationData, /* in, out */
TSS_HKEY *phEndorsementPubKey) /* out */
{
TCPA_DIGEST digest;
TSS_RESULT result;
UINT64 offset;
UINT32 pubEKSize;
BYTE *pubEK;
TCPA_NONCE antiReplay;
TCPA_DIGEST checkSum;
TSS_HOBJECT retKey;
TSS_HCONTEXT tspContext;
TCPA_PUBKEY pubKey;
Trspi_HashCtx hashCtx;
__tspi_memset(&pubKey, 0, sizeof(TCPA_PUBKEY));
if (phEndorsementPubKey == NULL)
return TSPERR(TSS_E_BAD_PARAMETER);
if ((result = obj_tpm_get_tsp_context(hTPM, &tspContext)))
return result;
if (fOwnerAuthorized)
return owner_get_pubek(tspContext, hTPM, phEndorsementPubKey);
if (pValidationData == NULL) {
if ((result = get_local_random(tspContext, FALSE, sizeof(TCPA_NONCE),
(BYTE **)antiReplay.nonce))) {
LogDebug("Failed to generate random nonce");
return TSPERR(TSS_E_INTERNAL_ERROR);
}
} else {
if (pValidationData->ulExternalDataLength < sizeof(antiReplay.nonce))
return TSPERR(TSS_E_BAD_PARAMETER);
memcpy(antiReplay.nonce, pValidationData->rgbExternalData,
sizeof(antiReplay.nonce));
}
/* call down to the TPM */
if ((result = TCS_API(tspContext)->ReadPubek(tspContext, antiReplay, &pubEKSize, &pubEK,
&checkSum)))
return result;
/* validate the returned hash, or set up the return so that the user can */
if (pValidationData == NULL) {
result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
result |= Trspi_HashUpdate(&hashCtx, pubEKSize, pubEK);
result |= Trspi_HashUpdate(&hashCtx, TPM_SHA1_160_HASH_LEN, antiReplay.nonce);
if ((result |= Trspi_HashFinal(&hashCtx, digest.digest)))
goto done;
/* check validation of the entire pubkey structure */
if (memcmp(digest.digest, checkSum.digest, TPM_SHA1_160_HASH_LEN)) {
/* validation failed, unload the pubEK in order to hash
* just the pubKey portion of the pubEK. This is done on
* Atmel chips specifically.
*/
offset = 0;
__tspi_memset(&pubKey, 0, sizeof(TCPA_PUBKEY));
if ((result = Trspi_UnloadBlob_PUBKEY(&offset, pubEK, &pubKey)))
goto done;
result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
result |= Trspi_HashUpdate(&hashCtx, pubKey.pubKey.keyLength,
pubKey.pubKey.key);
result |= Trspi_HashUpdate(&hashCtx, TPM_SHA1_160_HASH_LEN,
antiReplay.nonce);
if ((result |= Trspi_HashFinal(&hashCtx, digest.digest)))
goto done;
if (memcmp(digest.digest, checkSum.digest, TCPA_SHA1_160_HASH_LEN)) {
result = TSPERR(TSS_E_EK_CHECKSUM);
goto done;
}
}
} else {
/* validate the entire TCPA_PUBKEY structure */
pValidationData->ulDataLength = pubEKSize + TCPA_SHA1_160_HASH_LEN;
pValidationData->rgbData = calloc_tspi(tspContext,
pValidationData->ulDataLength);
if (pValidationData->rgbData == NULL) {
LogError("malloc of %u bytes failed.",
pValidationData->ulDataLength);
pValidationData->ulDataLength = 0;
result = TSPERR(TSS_E_OUTOFMEMORY);
goto done;
}
memcpy(pValidationData->rgbData, pubEK, pubEKSize);
memcpy(&pValidationData->rgbData[pubEKSize], antiReplay.nonce,
TCPA_SHA1_160_HASH_LEN);
pValidationData->ulValidationDataLength = TCPA_SHA1_160_HASH_LEN;
pValidationData->rgbValidationData = calloc_tspi(tspContext,
TCPA_SHA1_160_HASH_LEN);
if (pValidationData->rgbValidationData == NULL) {
LogError("malloc of %d bytes failed.", TCPA_SHA1_160_HASH_LEN);
pValidationData->ulValidationDataLength = 0;
pValidationData->ulDataLength = 0;
free_tspi(tspContext,pValidationData->rgbData);
result = TSPERR(TSS_E_OUTOFMEMORY);
goto done;
}
memcpy(pValidationData->rgbValidationData, checkSum.digest,
TPM_SHA1_160_HASH_LEN);
}
if ((result = obj_rsakey_add(tspContext, TSS_KEY_SIZE_2048|TSS_KEY_TYPE_LEGACY, &retKey)))
goto done;
if ((result = obj_rsakey_set_pubkey(retKey, TRUE, pubEK)))
goto done;
*phEndorsementPubKey = retKey;
done:
free(pubEK);
return result;
}
#ifdef TSS_BUILD_TSS12
TSS_RESULT
Tspi_TPM_CreateRevocableEndorsementKey(TSS_HTPM hTPM, /* in */
TSS_HKEY hKey, /* in */
TSS_VALIDATION * pValidationData,/* in, out */
UINT32 * pulEkResetDataLength, /* in, out */
BYTE ** prgbEkResetData) /* in, out */
{
TPM_NONCE antiReplay;
TPM_DIGEST digest;
TSS_RESULT result;
UINT32 ekSize;
BYTE *ek;
TSS_KEY dummyKey;
UINT64 offset;
TSS_BOOL genResetAuth;
TPM_DIGEST eKResetAuth;
TPM_DIGEST hash;
UINT32 newEKSize;
BYTE *newEK;
TSS_HCONTEXT tspContext;
TPM_PUBKEY pubEK;
Trspi_HashCtx hashCtx;
__tspi_memset(&pubEK, 0, sizeof(TPM_PUBKEY));
__tspi_memset(&dummyKey, 0, sizeof(TSS_KEY));
__tspi_memset(&eKResetAuth, 0xff, sizeof(eKResetAuth));
if (!pulEkResetDataLength || !prgbEkResetData)
return TSPERR(TSS_E_BAD_PARAMETER);
if (*pulEkResetDataLength != 0) {
if (*prgbEkResetData == NULL)
return TSPERR(TSS_E_BAD_PARAMETER);
if (*pulEkResetDataLength < sizeof(eKResetAuth.digest))
return TSPERR(TSS_E_BAD_PARAMETER);
memcpy(eKResetAuth.digest, *prgbEkResetData, sizeof(eKResetAuth.digest));
genResetAuth = FALSE;
} else {
if (*prgbEkResetData != NULL)
return TSPERR(TSS_E_BAD_PARAMETER);
genResetAuth = TRUE;
}
if ((result = obj_tpm_get_tsp_context(hTPM, &tspContext)))
return result;
if ((result = obj_rsakey_get_blob(hKey, &ekSize, &ek)))
return result;
offset = 0;
if ((result = UnloadBlob_TSS_KEY(&offset, ek, &dummyKey)))
return result;
offset = 0;
Trspi_LoadBlob_KEY_PARMS(&offset, ek, &dummyKey.algorithmParms);
free_key_refs(&dummyKey);
ekSize = offset;
if (pValidationData == NULL) {
if ((result = get_local_random(tspContext, FALSE, sizeof(TPM_NONCE),
(BYTE **)antiReplay.nonce))) {
LogError("Failed to create random nonce");
return TSPERR(TSS_E_INTERNAL_ERROR);
}
} else {
if (pValidationData->ulExternalDataLength < sizeof(antiReplay.nonce))
return TSPERR(TSS_E_BAD_PARAMETER);
memcpy(antiReplay.nonce, pValidationData->rgbExternalData,
sizeof(antiReplay.nonce));
}
if ((result = RPC_CreateRevocableEndorsementKeyPair(tspContext, antiReplay, ekSize, ek,
genResetAuth, &eKResetAuth, &newEKSize,
&newEK, &digest)))
return result;
if (pValidationData == NULL) {
result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
result |= Trspi_HashUpdate(&hashCtx, newEKSize, newEK);
result |= Trspi_HashUpdate(&hashCtx, TPM_SHA1_160_HASH_LEN, antiReplay.nonce);
if ((result |= Trspi_HashFinal(&hashCtx, hash.digest)))
goto done;
if (memcmp(hash.digest, digest.digest, TPM_SHA1_160_HASH_LEN)) {
LogError("Internal verification failed");
result = TSPERR(TSS_E_EK_CHECKSUM);
goto done;
}
} else {
pValidationData->rgbData = calloc_tspi(tspContext, newEKSize);
if (pValidationData->rgbData == NULL) {
LogError("malloc of %u bytes failed.", newEKSize);
result = TSPERR(TSS_E_OUTOFMEMORY);
goto done;
}
pValidationData->ulDataLength = newEKSize;
memcpy(pValidationData->rgbData, newEK, newEKSize);
memcpy(&pValidationData->rgbData[ekSize], antiReplay.nonce,
sizeof(antiReplay.nonce));
pValidationData->rgbValidationData = calloc_tspi(tspContext,
TPM_SHA1_160_HASH_LEN);
if (pValidationData->rgbValidationData == NULL) {
LogError("malloc of %d bytes failed.", TPM_SHA1_160_HASH_LEN);
free_tspi(tspContext, pValidationData->rgbData);
pValidationData->rgbData = NULL;
pValidationData->ulDataLength = 0;
result = TSPERR(TSS_E_OUTOFMEMORY);
goto done;
}
pValidationData->ulValidationDataLength = TPM_SHA1_160_HASH_LEN;
memcpy(pValidationData->rgbValidationData, digest.digest, TPM_SHA1_160_HASH_LEN);
}
if ((result = obj_rsakey_set_pubkey(hKey, FALSE, newEK))) {
if (pValidationData) {
free_tspi(tspContext, pValidationData->rgbValidationData);
free_tspi(tspContext, pValidationData->rgbData);
pValidationData->rgbData = NULL;
pValidationData->ulDataLength = 0;
pValidationData->rgbValidationData = NULL;
pValidationData->ulValidationDataLength = 0;
}
goto done;
}
if (genResetAuth) {
if ((*prgbEkResetData = calloc_tspi(tspContext, sizeof(eKResetAuth.digest))) == NULL) {
LogError("malloc of %zd bytes failed.", sizeof(eKResetAuth.digest));
if (pValidationData) {
free_tspi(tspContext, pValidationData->rgbValidationData);
free_tspi(tspContext, pValidationData->rgbData);
pValidationData->rgbData = NULL;
pValidationData->ulDataLength = 0;
pValidationData->rgbValidationData = NULL;
pValidationData->ulValidationDataLength = 0;
}
goto done;
}
memcpy(*prgbEkResetData, eKResetAuth.digest, sizeof(eKResetAuth.digest));
*pulEkResetDataLength = sizeof(eKResetAuth.digest);
}
done:
free(newEK);
return result;
}
TSS_RESULT
Tspi_TPM_RevokeEndorsementKey(TSS_HTPM hTPM, /* in */
UINT32 ulEkResetDataLength, /* in */
BYTE * rgbEkResetData) /* in */
{
TSS_HCONTEXT tspContext;
TPM_DIGEST eKResetAuth;
TSS_RESULT result;
if ((result = obj_tpm_get_tsp_context(hTPM, &tspContext)))
return result;
if (ulEkResetDataLength < sizeof(eKResetAuth.digest) || !rgbEkResetData)
return TSPERR(TSS_E_BAD_PARAMETER);
memcpy(eKResetAuth.digest, rgbEkResetData, sizeof(eKResetAuth.digest));
if ((result = RPC_RevokeEndorsementKeyPair(tspContext, &eKResetAuth)))
return result;
return result;
}
#endif