/* * Licensed Materials - Property of IBM * * trousers - An open source TCG Software Stack * * (C) Copyright International Business Machines Corp. 2004 * */ #include #include #include #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); 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; }