/*
* Licensed Materials - Property of IBM
*
* trousers - An open source TCG Software Stack
*
* (C) Copyright International Business Machines Corp. 2004
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "trousers/tss.h"
#include "trousers_types.h"
#include "tcs_tsp.h"
#include "tcs_utils.h"
#include "tcs_int_literals.h"
#include "capabilities.h"
#include "tcslog.h"
#include "tcsps.h"
TSS_RESULT
TCS_RegisterKey_Internal(TCS_CONTEXT_HANDLE hContext, /* in */
TSS_UUID *WrappingKeyUUID, /* in */
TSS_UUID *KeyUUID, /* in */
UINT32 cKeySize, /* in */
BYTE * rgbKey, /* in */
UINT32 cVendorData, /* in */
BYTE * gbVendorData) /* in */
{
TSS_RESULT result;
TSS_BOOL is_reg;
if ((result = ctx_verify_context(hContext)))
return result;
/* Check if key is already regisitered */
if (isUUIDRegistered(KeyUUID, &is_reg) != TSS_SUCCESS) {
LogError("Failed checking if UUID is registered.");
return TCSERR(TSS_E_INTERNAL_ERROR);
}
if (is_reg == TRUE || TSS_UUID_IS_OWNEREVICT(KeyUUID)) {
LogDebug("UUID is already registered");
return TCSERR(TSS_E_KEY_ALREADY_REGISTERED);
}
LogDebugUnrollKey(rgbKey);
/* Go ahead and store it in system persistant storage */
if ((result = ps_write_key(KeyUUID, WrappingKeyUUID, gbVendorData, cVendorData, rgbKey,
cKeySize))) {
LogError("Error writing key to file");
return result;
}
return TSS_SUCCESS;
}
TSS_RESULT
TCS_UnregisterKey_Internal(TCS_CONTEXT_HANDLE hContext, /* in */
TSS_UUID KeyUUID) /* in */
{
TSS_RESULT result;
if ((result = ctx_verify_context(hContext)))
return result;
return ps_remove_key(&KeyUUID);
}
TSS_RESULT
TCS_EnumRegisteredKeys_Internal(TCS_CONTEXT_HANDLE hContext, /* in */
TSS_UUID * pKeyUUID, /* in */
UINT32 * pcKeyHierarchySize, /* out */
TSS_KM_KEYINFO ** ppKeyHierarchy) /* out */
{
TSS_RESULT result = TSS_SUCCESS;
UINT32 count = 0, i;
TSS_KM_KEYINFO *ret = NULL;
TSS_UUID tmp_uuid;
struct key_disk_cache *disk_ptr, *tmp_ptrs[MAX_KEY_CHILDREN];
struct key_mem_cache *mem_ptr;
TSS_BOOL is_reg = FALSE;
LogDebug("Enum Reg Keys");
if (pcKeyHierarchySize == NULL || ppKeyHierarchy == NULL)
return TCSERR(TSS_E_BAD_PARAMETER);
if ((result = ctx_verify_context(hContext)))
return result;
if (pKeyUUID != NULL) {
/* First have to verify the key is registered */
if ((result = isUUIDRegistered(pKeyUUID, &is_reg)))
return result;
if (is_reg == FALSE) {
/* This return code is not listed as possible in the TSS 1.1 spec,
* but it makes more sense than just TCS_SUCCESS or TSS_E_FAIL */
return TCSERR(TSS_E_PS_KEY_NOTFOUND);
}
}
/* this entire operation needs to be atomic wrt registered keys. We must
* lock the mem cache as well to test if a given key is loaded. */
MUTEX_LOCK(disk_cache_lock);
MUTEX_LOCK(mem_cache_lock);
/* return an array of all registered keys if pKeyUUID == NULL */
if (pKeyUUID == NULL) {
/* determine the number of registered keys */
for (disk_ptr = key_disk_cache_head; disk_ptr; disk_ptr = disk_ptr->next) {
if (disk_ptr->flags & CACHE_FLAG_VALID)
count++;
}
/* malloc a structure for each of them */
if (count != 0) {
ret = calloc(count, sizeof(TSS_KM_KEYINFO));
if (ret == NULL) {
LogError("malloc of %zd bytes failed.",
(count * sizeof(TSS_KM_KEYINFO)));
count = 0;
result = TCSERR(TSS_E_OUTOFMEMORY);
goto done;
}
} else {
goto done;
}
/* fill out the structure for each key */
i = 0;
for (disk_ptr = key_disk_cache_head; disk_ptr; disk_ptr = disk_ptr->next) {
if (disk_ptr->flags & CACHE_FLAG_VALID) {
/* look for a mem cache entry to check if its loaded */
for (mem_ptr = key_mem_cache_head; mem_ptr; mem_ptr = mem_ptr->next) {
if (!memcmp(&mem_ptr->uuid, &disk_ptr->uuid, sizeof(TSS_UUID))) {
if ((result = fill_key_info(disk_ptr, mem_ptr, &ret[i]))) {
free(ret);
ret = NULL;
count = 0;
goto done;
}
break;
}
}
/* if there is no mem cache entry for this key, go ahead and call
* fill_key_info(), it will pull everything from disk */
if (mem_ptr == NULL) {
if ((result = fill_key_info(disk_ptr, NULL, &ret[i]))) {
free(ret);
ret = NULL;
count = 0;
goto done;
}
}
i++;
}
}
} else {
/* return a chain of a key and its parents up to the SRK */
/* determine the number of keys in the chain */
memcpy(&tmp_uuid, pKeyUUID, sizeof(TSS_UUID));
disk_ptr = key_disk_cache_head;
while (disk_ptr != NULL && count < MAX_KEY_CHILDREN)
{
if (disk_ptr->flags & CACHE_FLAG_VALID &&
!memcmp(&disk_ptr->uuid, &tmp_uuid, sizeof(TSS_UUID)))
{
/* increment count, then search for the parent */
count++;
/* save a pointer to this cache entry */
tmp_ptrs[count - 1] = disk_ptr;
/* if the parent of this key is NULL, we're at the root of the tree */
if (!memcmp(&disk_ptr->parent_uuid, &NULL_UUID, sizeof(TSS_UUID)))
break;
/* overwrite tmp_uuid with the parent, which we will now search for */
memcpy(&tmp_uuid, &disk_ptr->parent_uuid, sizeof(TSS_UUID));
disk_ptr = key_disk_cache_head;
continue;
}
disk_ptr = disk_ptr->next;
}
/* when we reach this point, we have an array of TSS_UUID's that leads from the
* requested key up to the SRK*/
/* malloc a structure for each of them */
if (count != 0) {
ret = calloc(count, sizeof(TSS_KM_KEYINFO));
if (ret == NULL) {
LogError("malloc of %zd bytes failed.",
(count * sizeof(TSS_KM_KEYINFO)));
count = 0;
result = TCSERR(TSS_E_OUTOFMEMORY);
goto done;
}
} else {
goto done;
}
for (i = 0; i < count; i++) {
/* look for a mem cache entry to check if its loaded */
for (mem_ptr = key_mem_cache_head; mem_ptr; mem_ptr = mem_ptr->next) {
if (!memcmp(&mem_ptr->uuid, &tmp_ptrs[i]->uuid, sizeof(TSS_UUID))) {
if ((result = fill_key_info(tmp_ptrs[i], mem_ptr, &ret[i]))) {
free(ret);
ret = NULL;
count = 0;
goto done;
}
break;
}
}
/* if there is no mem cache entry for this key, go ahead and call
* fill_key_info(), it will pull everything from disk */
if (mem_ptr == NULL) {
if ((result = fill_key_info(tmp_ptrs[i], NULL, &ret[i]))) {
free(ret);
ret = NULL;
count = 0;
goto done;
}
}
}
}
done:
MUTEX_UNLOCK(disk_cache_lock);
MUTEX_UNLOCK(mem_cache_lock);
*ppKeyHierarchy = ret;
*pcKeyHierarchySize = count;
return result;
}
TSS_RESULT
TCS_EnumRegisteredKeys_Internal2(TCS_CONTEXT_HANDLE hContext, /* in */
TSS_UUID * pKeyUUID, /* in */
UINT32 * pcKeyHierarchySize, /* out */
TSS_KM_KEYINFO2 ** ppKeyHierarchy) /* out */
{
TSS_RESULT result = TSS_SUCCESS;
UINT32 count = 0, i;
TSS_KM_KEYINFO2 *ret = NULL;
TSS_UUID tmp_uuid;
struct key_disk_cache *disk_ptr, *tmp_ptrs[MAX_KEY_CHILDREN];
struct key_mem_cache *mem_ptr;
TSS_BOOL is_reg = FALSE;
LogDebug("Enum Reg Keys2");
if (pcKeyHierarchySize == NULL || ppKeyHierarchy == NULL)
return TCSERR(TSS_E_BAD_PARAMETER);
if ((result = ctx_verify_context(hContext)))
return result;
if (pKeyUUID != NULL) {
/* First have to verify the key is registered */
if ((result = isUUIDRegistered(pKeyUUID, &is_reg)))
return result;
if (is_reg == FALSE) {
/* This return code is not listed as possible in the TSS 1.1 spec,
* but it makes more sense than just TCS_SUCCESS or TSS_E_FAIL */
return TCSERR(TSS_E_PS_KEY_NOTFOUND);
}
}
/* this entire operation needs to be atomic wrt registered keys. We must
* lock the mem cache as well to test if a given key is loaded. */
MUTEX_LOCK(disk_cache_lock);
MUTEX_LOCK(mem_cache_lock);
/* return an array of all registered keys if pKeyUUID == NULL */
if (pKeyUUID == NULL) {
/* determine the number of registered keys */
for (disk_ptr = key_disk_cache_head; disk_ptr; disk_ptr = disk_ptr->next) {
if (disk_ptr->flags & CACHE_FLAG_VALID)
count++;
}
/* malloc a structure for each of them */
if (count != 0) {
ret = calloc(count, sizeof(TSS_KM_KEYINFO2));
if (ret == NULL) {
LogError("malloc of %zd bytes failed.",
(count * sizeof(TSS_KM_KEYINFO2)));
count = 0;
result = TCSERR(TSS_E_OUTOFMEMORY);
goto done;
}
} else {
goto done;
}
/* fill out the structure for each key */
i = 0;
for (disk_ptr = key_disk_cache_head; disk_ptr; disk_ptr = disk_ptr->next) {
if (disk_ptr->flags & CACHE_FLAG_VALID) {
/* look for a mem cache entry to check if its loaded */
for (mem_ptr = key_mem_cache_head; mem_ptr; mem_ptr = mem_ptr->next) {
if (!memcmp(&mem_ptr->uuid, &disk_ptr->uuid, sizeof(TSS_UUID))) {
if ((result = fill_key_info2(disk_ptr, mem_ptr, &ret[i]))) {
free(ret);
ret = NULL;
count = 0;
goto done;
}
break;
}
}
/* if there is no mem cache entry for this key, go ahead and call
* fill_key_info2(), it will pull everything from disk */
if (mem_ptr == NULL) {
if ((result = fill_key_info2(disk_ptr, NULL, &ret[i]))) {
free(ret);
ret = NULL;
count = 0;
goto done;
}
}
i++;
}
}
} else {
/* return a chain of a key and its parents up to the SRK */
/* determine the number of keys in the chain */
memcpy(&tmp_uuid, pKeyUUID, sizeof(TSS_UUID));
disk_ptr = key_disk_cache_head;
while (disk_ptr != NULL && count < MAX_KEY_CHILDREN)
{
if (disk_ptr->flags & CACHE_FLAG_VALID &&
!memcmp(&disk_ptr->uuid, &tmp_uuid, sizeof(TSS_UUID)))
{
/* increment count, then search for the parent */
count++;
/* save a pointer to this cache entry */
tmp_ptrs[count - 1] = disk_ptr;
/* if the parent of this key is NULL, we're at the root of the tree */
if (!memcmp(&disk_ptr->parent_uuid, &NULL_UUID, sizeof(TSS_UUID)))
break;
/* overwrite tmp_uuid with the parent, which we will now search for */
memcpy(&tmp_uuid, &disk_ptr->parent_uuid, sizeof(TSS_UUID));
disk_ptr = key_disk_cache_head;
continue;
}
disk_ptr = disk_ptr->next;
}
/* when we reach this point, we have an array of TSS_UUID's that leads from the
* requested key up to the SRK*/
/* malloc a structure for each of them */
if (count != 0) {
ret = calloc(count, sizeof(TSS_KM_KEYINFO2));
if (ret == NULL) {
LogError("malloc of %zd bytes failed.",
(count * sizeof(TSS_KM_KEYINFO2)));
count = 0;
result = TCSERR(TSS_E_OUTOFMEMORY);
goto done;
}
} else {
goto done;
}
for (i = 0; i < count; i++) {
/* look for a mem cache entry to check if its loaded */
for (mem_ptr = key_mem_cache_head; mem_ptr; mem_ptr = mem_ptr->next) {
if (!memcmp(&mem_ptr->uuid, &tmp_ptrs[i]->uuid, sizeof(TSS_UUID))) {
if ((result = fill_key_info2(tmp_ptrs[i], mem_ptr, &ret[i]))) {
free(ret);
ret = NULL;
count = 0;
goto done;
}
break;
}
}
/* if there is no mem cache entry for this key, go ahead and call
* fill_key_info(), it will pull everything from disk */
if (mem_ptr == NULL) {
if ((result = fill_key_info2(tmp_ptrs[i], NULL, &ret[i]))) {
free(ret);
ret = NULL;
count = 0;
goto done;
}
}
}
}
done:
MUTEX_UNLOCK(disk_cache_lock);
MUTEX_UNLOCK(mem_cache_lock);
*ppKeyHierarchy = ret;
*pcKeyHierarchySize = count;
return result;
}
TSS_RESULT
TCS_GetRegisteredKey_Internal(TCS_CONTEXT_HANDLE hContext, /* in */
TSS_UUID *KeyUUID, /* in */
TSS_KM_KEYINFO ** ppKeyInfo) /* out */
{
TSS_RESULT result;
UINT64 offset;
BYTE tcpaKeyBlob[1024];
TSS_KEY tcpaKey;
UINT16 keySize = sizeof (tcpaKeyBlob);
TSS_UUID parentUUID;
/* This should be set in case we return before the malloc */
*ppKeyInfo = NULL;
if ((result = ctx_verify_context(hContext)))
return result;
if ((result = ps_get_key_by_uuid(KeyUUID, tcpaKeyBlob, &keySize))) {
return TCSERR(TSS_E_PS_KEY_NOTFOUND);
}
if ((result = getParentUUIDByUUID(KeyUUID, &parentUUID)))
return TCSERR(TSS_E_FAIL);
*ppKeyInfo = malloc(sizeof(TSS_KM_KEYINFO));
if (*ppKeyInfo == NULL) {
LogError("malloc of %zd bytes failed.", sizeof(TSS_KM_KEYINFO));
return TCSERR(TSS_E_OUTOFMEMORY);
}
offset = 0;
UnloadBlob_TSS_KEY(&offset, tcpaKeyBlob, &tcpaKey);
(*ppKeyInfo)->bAuthDataUsage = tcpaKey.authDataUsage;
(*ppKeyInfo)->fIsLoaded = FALSE;
if (tcpaKey.hdr.key12.tag == TPM_TAG_KEY12) {
(*ppKeyInfo)->versionInfo.bMajor = TSS_SPEC_MAJOR;
(*ppKeyInfo)->versionInfo.bMinor = TSS_SPEC_MINOR;
(*ppKeyInfo)->versionInfo.bRevMajor = 0;
(*ppKeyInfo)->versionInfo.bRevMinor = 0;
} else {
(*ppKeyInfo)->versionInfo.bMajor = tcpaKey.hdr.key11.ver.major;
(*ppKeyInfo)->versionInfo.bMinor = tcpaKey.hdr.key11.ver.minor;
(*ppKeyInfo)->versionInfo.bRevMajor = tcpaKey.hdr.key11.ver.revMajor;
(*ppKeyInfo)->versionInfo.bRevMinor = tcpaKey.hdr.key11.ver.revMinor;
}
memcpy(&((*ppKeyInfo)->keyUUID), KeyUUID, sizeof(TSS_UUID));
(*ppKeyInfo)->ulVendorDataLength = 0;
(*ppKeyInfo)->rgbVendorData = 0;
memcpy(&((*ppKeyInfo)->parentKeyUUID), &parentUUID, sizeof(TSS_UUID));
return TSS_SUCCESS;
}
TSS_RESULT
TCS_GetRegisteredKeyBlob_Internal(TCS_CONTEXT_HANDLE hContext, /* in */
TSS_UUID *KeyUUID, /* in */
UINT32 * pcKeySize, /* out */
BYTE ** prgbKey) /* out */
{
UINT16 keySize;
BYTE buffer[4096];
TSS_RESULT result;
if ((result = ctx_verify_context(hContext)))
return result;
keySize = sizeof(buffer);
if ((result = ps_get_key_by_uuid(KeyUUID, buffer, &keySize)))
return TCSERR(TSS_E_PS_KEY_NOTFOUND);
*prgbKey = calloc(1, keySize);
if (*prgbKey == NULL) {
LogError("malloc of %d bytes failed.", keySize);
return TCSERR(TSS_E_OUTOFMEMORY);
} else {
memcpy(*prgbKey, buffer, keySize);
}
*pcKeySize = keySize;
return TSS_SUCCESS;
}
TSS_RESULT
TCSP_LoadKeyByUUID_Internal(TCS_CONTEXT_HANDLE hContext, /* in */
TSS_UUID *KeyUUID, /* in */
TCS_LOADKEY_INFO * pLoadKeyInfo, /* in, out */
TCS_KEY_HANDLE * phKeyTCSI) /* out */
{
UINT32 keyslot = 0, keySize;
UINT32 ordinal;
TSS_RESULT result;
TSS_UUID parentUuid;
BYTE keyBlob[0x1000];
UINT16 blobSize = sizeof(keyBlob);
UINT64 offset;
TCS_KEY_HANDLE parentTCSKeyHandle;
if (TPM_VERSION_IS(1,2))
ordinal = TPM_ORD_LoadKey2;
else
ordinal = TPM_ORD_LoadKey;
LogDebugFn("Enter: uuid: 0x%lx auth? 0x%x ***********", (unsigned long)KeyUUID,
pLoadKeyInfo == NULL ? 0xdeadbeef : pLoadKeyInfo->authData.AuthHandle);
if ((result = ctx_verify_context(hContext)))
return result;
memset(&parentUuid, 0, sizeof(TSS_UUID));
if (pLoadKeyInfo &&
memcmp(&pLoadKeyInfo->parentKeyUUID, &parentUuid, sizeof(TSS_UUID))) {
if (ps_get_key_by_uuid(&pLoadKeyInfo->keyUUID, keyBlob, &blobSize))
return TCSERR(TSS_E_PS_KEY_NOTFOUND);
if (mc_get_handles_by_uuid(&pLoadKeyInfo->parentKeyUUID, &parentTCSKeyHandle,
&keyslot))
return TCSERR(TCS_E_KM_LOADFAILED);
return LoadKeyByBlob_Internal(ordinal, hContext, parentTCSKeyHandle,
blobSize, keyBlob,
&pLoadKeyInfo->authData,
phKeyTCSI, &keyslot);
}
/* if KeyUUID is already loaded, increment the ref count and return */
if (mc_get_handles_by_uuid(KeyUUID, phKeyTCSI, &keyslot) == TSS_SUCCESS) {
if (keyslot) {
if (ctx_mark_key_loaded(hContext, *phKeyTCSI)) {
LogError("Error marking key as loaded");
return TCSERR(TSS_E_INTERNAL_ERROR);
}
return TSS_SUCCESS;
}
}
/*********************************************************************
* The first thing to do in this func is setup all the info and make sure
* that we get it all from either the keyfile or the keyCache
* also, it's important to return if the key is already loaded
***********************************************************************/
LogDebugFn("calling ps_get_key_by_uuid");
if (ps_get_key_by_uuid(KeyUUID, keyBlob, &blobSize))
return TCSERR(TSS_E_PS_KEY_NOTFOUND);
/* convert UINT16 to UIN32 */
keySize = blobSize;
LogDebugFn("calling getParentUUIDByUUID");
/*--- Get my parent's UUID. Since My key is registered, my parent should be as well. */
if ((result = getParentUUIDByUUID(KeyUUID, &parentUuid)))
return TCSERR(TCS_E_KM_LOADFAILED);
if ((result = TCSP_LoadKeyByUUID_Internal(hContext, &parentUuid,
pLoadKeyInfo, &parentTCSKeyHandle)))
return result;
LogDebugFn("calling LoadKeyByBlob_Internal");
/*******************************************************
* If no errors have happend up till now, then the parent is loaded and ready for use.
* The parent's TCS Handle should be in parentTCSKeyHandle.
******************************************************/
if ((result = LoadKeyByBlob_Internal(ordinal, hContext, parentTCSKeyHandle,
keySize, keyBlob,
NULL,
phKeyTCSI, &keyslot))) {
LogDebugFn("LoadKeyByBlob_Internal returned 0x%x", result);
if (result == TCPA_E_AUTHFAIL && pLoadKeyInfo) {
BYTE blob[1000];
/* set up a load key info struct */
memcpy(&pLoadKeyInfo->parentKeyUUID, &parentUuid, sizeof(TSS_UUID));
memcpy(&pLoadKeyInfo->keyUUID, KeyUUID, sizeof(TSS_UUID));
/* calculate the paramDigest */
offset = 0;
LoadBlob_UINT32(&offset, ordinal, blob);
LoadBlob(&offset, keySize, blob, keyBlob);
if (Hash(TSS_HASH_SHA1, offset, blob,
(BYTE *)&pLoadKeyInfo->paramDigest.digest))
result = TCSERR(TSS_E_INTERNAL_ERROR);
result = TCSERR(TCS_E_KM_LOADFAILED);
}
}
return result;
}
TSS_RESULT
TCSP_GetRegisteredKeyByPublicInfo_Internal(TCS_CONTEXT_HANDLE tcsContext, /* in */
TCPA_ALGORITHM_ID algID, /* in */
UINT32 ulPublicInfoLength, /* in */
BYTE * rgbPublicInfo, /* in */
UINT32 * keySize, /* out */
BYTE ** keyBlob) /* out */
{
TCPA_STORE_PUBKEY pubKey;
TSS_RESULT result = TCSERR(TSS_E_FAIL);
pubKey.key = NULL;
if ((result = ctx_verify_context(tcsContext)))
return result;
if (algID == TCPA_ALG_RSA) {
/*--- Convert Public info to a structure */
pubKey.keyLength = ulPublicInfoLength;
pubKey.key = malloc(pubKey.keyLength);
if (pubKey.key == NULL) {
LogError("malloc of %d bytes failed.", pubKey.keyLength);
return TCSERR(TSS_E_OUTOFMEMORY);
}
memcpy(pubKey.key, rgbPublicInfo, pubKey.keyLength);
if ((result = ps_get_key_by_pub(&pubKey, keySize, keyBlob))) {
LogDebug("Public key data not found in PS");
free(pubKey.key);
return TCSERR(TSS_E_PS_KEY_NOTFOUND);
}
}
free(pubKey.key);
return result;
}