
/*
 * Licensed Materials - Property of IBM
 *
 * trousers - An open source TCG Software Stack
 *
 * (C) Copyright International Business Machines Corp. 2004-2006
 *
 */


#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "trousers/tss.h"
#include "trousers/trousers.h"
#include "trousers_types.h"
#include "trousers_types.h"
#include "spi_utils.h"
#include "capabilities.h"
#include "tsplog.h"
#include "tcs_tsp.h"
#include "tspps.h"
#include "hosttable.h"
#include "tcsd_wrap.h"
#include "tcsd.h"
#include "obj.h"

TSS_UUID owner_evict_uuid = {0, 0, 0, 0, 0, {0, 0, 0, 0, 1, 0}};

TSS_RESULT
Tspi_Context_LoadKeyByUUID(TSS_HCONTEXT tspContext,		/* in */
			   TSS_FLAG persistentStorageType,	/* in */
			   TSS_UUID uuidData,			/* in */
			   TSS_HKEY * phKey)			/* out */
{
	TSS_RESULT result;
	TSS_UUID parentUUID;
	UINT32 keyBlobSize, parentPSType;
	BYTE *keyBlob = NULL;
	TCS_KEY_HANDLE tcsKeyHandle;
	TSS_HKEY parentTspHandle;
	TCS_LOADKEY_INFO info;
	UINT32		ulPubKeyLength;
	BYTE		*rgbPubKey;
	TPM_COMMAND_CODE ordinal;

	if (phKey == NULL)
		return TSPERR(TSS_E_BAD_PARAMETER);

	if ((!obj_is_context(tspContext)))
		return TSPERR(TSS_E_INVALID_HANDLE);

	if ((result = obj_context_get_loadkey_ordinal(tspContext, &ordinal)))
		return result;

	/* This key is in the System Persistant storage */
	if (persistentStorageType == TSS_PS_TYPE_SYSTEM) {
#if 1
		__tspi_memset(&info, 0, sizeof(TCS_LOADKEY_INFO));

		result = RPC_LoadKeyByUUID(tspContext, uuidData, &info, &tcsKeyHandle);

		if (TSS_ERROR_CODE(result) == TCS_E_KM_LOADFAILED) {
			TSS_HKEY keyHandle;
			TSS_HPOLICY hPolicy;

			/* load failed, due to some key in the chain needing auth
			 * which doesn't yet exist at the TCS level. However, the
			 * auth may already be set in policies at the TSP level.
			 * To find out, get the key handle of the key requiring
			 * auth. First, look at the list of keys in memory. */
			if ((obj_rsakey_get_by_uuid(&info.parentKeyUUID, &keyHandle))) {
				/* If that failed, look on disk, in User PS. */
				if (ps_get_key_by_uuid(tspContext, &info.parentKeyUUID,
						       &keyHandle))
					return result;
			}

			if (obj_rsakey_get_policy(keyHandle, TSS_POLICY_USAGE,
						  &hPolicy, NULL))
				return result;

			if (secret_PerformAuth_OIAP(keyHandle, ordinal, hPolicy, FALSE,
						    &info.paramDigest, &info.authData))
				return result;

			if ((result = RPC_LoadKeyByUUID(tspContext, uuidData, &info,
							&tcsKeyHandle)))
				return result;
		} else if (result)
			return result;

		/*check if provided UUID has an owner evict key UUID prefix */
		if (!memcmp(&uuidData, &owner_evict_uuid, sizeof(TSS_UUID)-1)) {
			if ((result = obj_rsakey_add(tspContext, TSS_RSAKEY_FLAG_OWNEREVICT,
						      phKey)))
				return result;
			if ((result = obj_rsakey_set_tcs_handle(*phKey, tcsKeyHandle)))
				return result;

			//The cached public key portion of the owner evict key is used 
			//further by TPM_KEY_CONTROLOWNER command for sanity check
			if ((result = Tspi_Key_GetPubKey(*phKey, &ulPubKeyLength, &rgbPubKey)))
				return result;

			result = obj_rsakey_set_pubkey(*phKey, FALSE, rgbPubKey);

			free_tspi(tspContext,rgbPubKey);		
			if (result != TSS_SUCCESS)
				return result;
		} else {
			if ((result = RPC_GetRegisteredKeyBlob(tspContext, uuidData, &keyBlobSize,
							       &keyBlob)))
				return result;

			if ((result = obj_rsakey_add_by_key(tspContext, &uuidData, keyBlob,
							    TSS_OBJ_FLAG_SYSTEM_PS, phKey))) {
				free (keyBlob);
				return result;
			}
	
			result = obj_rsakey_set_tcs_handle(*phKey, tcsKeyHandle);

			free (keyBlob);
		}
#else
		if ((result = load_from_system_ps(tspContext, &uuidData, phKey)))
			return result;
#endif
	} else if (persistentStorageType == TSS_PS_TYPE_USER) {
		if ((result = ps_get_parent_uuid_by_uuid(&uuidData, &parentUUID)))
			return result;

		/* If the parent is not in memory, recursively call ourselves on it */
		if (obj_rsakey_get_by_uuid(&parentUUID, &parentTspHandle) != TSS_SUCCESS) {
			if ((result = ps_get_parent_ps_type_by_uuid(&uuidData, &parentPSType)))
				return result;

			if ((result = Tspi_Context_LoadKeyByUUID(tspContext, parentPSType,
								 parentUUID, &parentTspHandle)))
				return result;
		}

		if ((result = ps_get_key_by_uuid(tspContext, &uuidData, phKey)))
			return result;

		/* The parent is loaded and we have the parent key handle, so call the TCS to
		 * actually load the child. */
		return Tspi_Key_LoadKey(*phKey, parentTspHandle);
	} else {
		return TSPERR(TSS_E_BAD_PARAMETER);
	}

	return TSS_SUCCESS;
}

TSS_RESULT
Tspi_Context_RegisterKey(TSS_HCONTEXT tspContext,		/* in */
			 TSS_HKEY hKey,				/* in */
			 TSS_FLAG persistentStorageType,	/* in */
			 TSS_UUID uuidKey,			/* in */
			 TSS_FLAG persistentStorageTypeParent,	/* in */
			 TSS_UUID uuidParentKey)		/* in */
{
	BYTE *keyBlob;
	UINT32 keyBlobSize;
	TSS_RESULT result;
	TSS_BOOL answer;

	if (!obj_is_context(tspContext) || !obj_is_rsakey(hKey))
		return TSPERR(TSS_E_INVALID_HANDLE);

	if (persistentStorageType == TSS_PS_TYPE_SYSTEM) {
		if (persistentStorageTypeParent == TSS_PS_TYPE_USER) {
			return TSPERR(TSS_E_NOTIMPL);
		} else if (persistentStorageTypeParent == TSS_PS_TYPE_SYSTEM) {
			if ((result = obj_rsakey_get_blob(hKey, &keyBlobSize,
							  &keyBlob)))
				return result;

			if ((result = RPC_RegisterKey(tspContext, uuidParentKey, uuidKey,
						      keyBlobSize, keyBlob,
						      strlen(PACKAGE_STRING) + 1,
						      (BYTE *)PACKAGE_STRING)))
				return result;
		} else {
			return TSPERR(TSS_E_BAD_PARAMETER);
		}
	} else if (persistentStorageType == TSS_PS_TYPE_USER) {
		if ((result = ps_is_key_registered(&uuidKey, &answer)))
			return result;

		if (answer == TRUE)
			return TSPERR(TSS_E_KEY_ALREADY_REGISTERED);

		if ((result = obj_rsakey_get_blob (hKey, &keyBlobSize, &keyBlob)))
			return result;

		if ((result = ps_write_key(&uuidKey, &uuidParentKey,
					   persistentStorageTypeParent,
					   keyBlobSize, keyBlob)))
			return result;
	} else {
		return TSPERR(TSS_E_BAD_PARAMETER);
	}

	if ((result = obj_rsakey_set_uuid(hKey, persistentStorageType, &uuidKey)))
		return result;

	return TSS_SUCCESS;
}

TSS_RESULT
Tspi_Context_UnregisterKey(TSS_HCONTEXT tspContext,		/* in */
			   TSS_FLAG persistentStorageType,	/* in */
			   TSS_UUID uuidKey,			/* in */
			   TSS_HKEY *phKey)			/* out */
{
	BYTE *keyBlob = NULL;
	UINT32 keyBlobSize;
	TSS_RESULT result;

	if (phKey == NULL)
		return TSPERR(TSS_E_BAD_PARAMETER);

	if ((!obj_is_context(tspContext)))
		return TSPERR(TSS_E_INVALID_HANDLE);

	if (persistentStorageType == TSS_PS_TYPE_SYSTEM) {
		/* get the key first, so it doesn't disappear when we
		 * unregister it */
		if ((result = RPC_GetRegisteredKeyBlob(tspContext, uuidKey, &keyBlobSize,
						       &keyBlob)))
			return result;

		if ((obj_rsakey_add_by_key(tspContext, &uuidKey, keyBlob, TSS_OBJ_FLAG_SYSTEM_PS,
					   phKey))) {
			free(keyBlob);
			return result;
		}

		free(keyBlob);

		/* now unregister it */
		if ((result = RPC_UnregisterKey(tspContext, uuidKey)))
			return result;
	} else if (persistentStorageType == TSS_PS_TYPE_USER) {
		/* get the key first, so it doesn't disappear when we
		 * unregister it */
		if ((result = ps_get_key_by_uuid(tspContext, &uuidKey, phKey)))
			return result;

		/* now unregister it */
		if ((result = ps_remove_key(&uuidKey)))
			return result;
	} else {
		return TSPERR(TSS_E_BAD_PARAMETER);
	}

	return TSS_SUCCESS;
}

TSS_RESULT
Tspi_Context_GetKeyByUUID(TSS_HCONTEXT tspContext,		/* in */
			  TSS_FLAG persistentStorageType,	/* in */
			  TSS_UUID uuidData,			/* in */
			  TSS_HKEY * phKey)			/* out */
{
	TCPA_RESULT result;
	UINT32 keyBlobSize = 0;
	BYTE *keyBlob = NULL;

	if (phKey == NULL)
		return TSPERR(TSS_E_BAD_PARAMETER);

	if ((!obj_is_context(tspContext)))
		return TSPERR(TSS_E_INVALID_HANDLE);

	if (persistentStorageType == TSS_PS_TYPE_SYSTEM) {
		if ((result = RPC_GetRegisteredKeyBlob(tspContext, uuidData, &keyBlobSize,
						       &keyBlob)))
			return result;

		if ((obj_rsakey_add_by_key(tspContext, &uuidData, keyBlob, TSS_OBJ_FLAG_SYSTEM_PS,
					   phKey))) {
			free(keyBlob);
			return result;
		}

		free(keyBlob);
	} else if (persistentStorageType == TSS_PS_TYPE_USER) {
		if (!obj_is_context(tspContext))
			return TSPERR(TSS_E_INVALID_HANDLE);

		if ((result = ps_get_key_by_uuid(tspContext, &uuidData, phKey)))
			return result;
	} else
		return TSPERR(TSS_E_BAD_PARAMETER);

	return TSS_SUCCESS;
}

TSS_RESULT
Tspi_Context_GetKeyByPublicInfo(TSS_HCONTEXT tspContext,	/* in */
				TSS_FLAG persistentStorageType,	/* in */
				TSS_ALGORITHM_ID algID,		/* in */
				UINT32 ulPublicInfoLength,	/* in */
				BYTE * rgbPublicInfo,		/* in */
				TSS_HKEY * phKey)		/* out */
{
	TCPA_ALGORITHM_ID tcsAlgID;
	UINT32 keyBlobSize;
	BYTE *keyBlob;
	TSS_RESULT result;
	TSS_HKEY keyOutHandle;
	UINT32 flag = 0;
	TSS_KEY keyContainer;
	UINT64 offset;

	if (phKey == NULL)
		return TSPERR(TSS_E_BAD_PARAMETER);

	if (!obj_is_context(tspContext))
		return TSPERR(TSS_E_INVALID_HANDLE);

	switch (algID) {
		case TSS_ALG_RSA:
			tcsAlgID = TCPA_ALG_RSA;
			break;
		default:
			LogError("Algorithm ID was not type RSA.");
			return TSPERR(TSS_E_BAD_PARAMETER);
	}

	if (persistentStorageType == TSS_PS_TYPE_SYSTEM) {
		if ((result = RPC_GetRegisteredKeyByPublicInfo(tspContext, tcsAlgID,
							       ulPublicInfoLength, rgbPublicInfo,
							       &keyBlobSize, &keyBlob)))
			return result;

	} else if (persistentStorageType == TSS_PS_TYPE_USER) {
		return ps_get_key_by_pub(tspContext, ulPublicInfoLength, rgbPublicInfo,
					 phKey);
	} else
		return TSPERR(TSS_E_BAD_PARAMETER);

	/* need to setup the init flags of the create object based on
	 * the size of the blob's pubkey */
	offset = 0;
	if ((result = UnloadBlob_TSS_KEY(&offset, keyBlob, &keyContainer))) {
		free(keyBlob);
		return result;
	}

	/* begin setting up the key object */
	switch (keyContainer.pubKey.keyLength) {
		case 16384/8:
			flag |= TSS_KEY_SIZE_16384;
			break;
		case 8192/8:
			flag |= TSS_KEY_SIZE_8192;
			break;
		case 4096/8:
			flag |= TSS_KEY_SIZE_4096;
			break;
		case 2048/8:
			flag |= TSS_KEY_SIZE_2048;
			break;
		case 1024/8:
			flag |= TSS_KEY_SIZE_1024;
			break;
		case 512/8:
			flag |= TSS_KEY_SIZE_512;
			break;
		default:
			LogError("Key was not a known keylength.");
			free(keyBlob);
			free_key_refs(&keyContainer);
			return TSPERR(TSS_E_INTERNAL_ERROR);
	}

	if (keyContainer.keyUsage == TPM_KEY_SIGNING)
		flag |= TSS_KEY_TYPE_SIGNING;
	else if (keyContainer.keyUsage == TPM_KEY_STORAGE)
		flag |= TSS_KEY_TYPE_STORAGE;
	else if (keyContainer.keyUsage == TPM_KEY_IDENTITY)
		flag |= TSS_KEY_TYPE_IDENTITY;
	else if (keyContainer.keyUsage == TPM_KEY_AUTHCHANGE)
		flag |= TSS_KEY_TYPE_AUTHCHANGE;
	else if (keyContainer.keyUsage == TPM_KEY_BIND)
		flag |= TSS_KEY_TYPE_BIND;
	else if (keyContainer.keyUsage == TPM_KEY_LEGACY)
		flag |= TSS_KEY_TYPE_LEGACY;

	if (keyContainer.authDataUsage == TPM_AUTH_NEVER)
		flag |= TSS_KEY_NO_AUTHORIZATION;
	else
		flag |= TSS_KEY_AUTHORIZATION;

	if (keyContainer.keyFlags & TPM_MIGRATABLE)
		flag |= TSS_KEY_MIGRATABLE;
	else
		flag |= TSS_KEY_NOT_MIGRATABLE;

	if (keyContainer.keyFlags & TPM_VOLATILE)
		flag |= TSS_KEY_VOLATILE;
	else
		flag |= TSS_KEY_NON_VOLATILE;

#ifdef TSS_BUILD_CMK
	if (keyContainer.keyFlags & TPM_MIGRATEAUTHORITY)
		flag |= TSS_KEY_CERTIFIED_MIGRATABLE;
	else
		flag |= TSS_KEY_NOT_CERTIFIED_MIGRATABLE;
#endif

	/* Create a new Key Object */
	if ((result = obj_rsakey_add(tspContext, flag, &keyOutHandle))) {
		free(keyBlob);
		free_key_refs(&keyContainer);
		return result;
	}
	/* Stick the info into this net KeyObject */
	if ((result = obj_rsakey_set_tcpakey(keyOutHandle, keyBlobSize, keyBlob))) {
		free(keyBlob);
		free_key_refs(&keyContainer);
		return result;
	}

	free(keyBlob);
	free_key_refs(&keyContainer);
	*phKey = keyOutHandle;

	return TSS_SUCCESS;
}

TSS_RESULT
Tspi_Context_GetRegisteredKeysByUUID(TSS_HCONTEXT tspContext,		/* in */
				     TSS_FLAG persistentStorageType,	/* in */
				     TSS_UUID * pUuidData,		/* in */
				     UINT32 * pulKeyHierarchySize,	/* out */
				     TSS_KM_KEYINFO ** ppKeyHierarchy)	/* out */
{
	TSS_RESULT result;
	TSS_KM_KEYINFO *tcsHier, *tspHier;
	UINT32 tcsHierSize, tspHierSize;
	TSS_UUID tcs_uuid;

	if (pulKeyHierarchySize == NULL || ppKeyHierarchy == NULL)
		return TSPERR(TSS_E_BAD_PARAMETER);

	if (!obj_is_context(tspContext))
		return TSPERR(TSS_E_INVALID_HANDLE);

	if (pUuidData) {
		if (persistentStorageType == TSS_PS_TYPE_SYSTEM) {
			if ((result = RPC_EnumRegisteredKeys(tspContext, pUuidData,
							     pulKeyHierarchySize,
							     ppKeyHierarchy)))
				return result;
		} else if (persistentStorageType == TSS_PS_TYPE_USER) {
			if ((result = ps_get_registered_keys(pUuidData, &tcs_uuid,
							     &tspHierSize, &tspHier)))
				return result;

			if ((result = RPC_EnumRegisteredKeys(tspContext, &tcs_uuid, &tcsHierSize,
							     &tcsHier))) {
				free(tspHier);
				return result;
			}

			result = merge_key_hierarchies(tspContext, tspHierSize, tspHier,
						       tcsHierSize, tcsHier, pulKeyHierarchySize,
						       ppKeyHierarchy);
			free(tcsHier);
			free(tspHier);
		} else
			return TSPERR(TSS_E_BAD_PARAMETER);
	} else {
		if ((result = RPC_EnumRegisteredKeys(tspContext, pUuidData, &tcsHierSize,
						     &tcsHier)))
			return result;

		if ((result = ps_get_registered_keys(pUuidData, NULL, &tspHierSize, &tspHier))) {
			free(tcsHier);
			return result;
		}

		result = merge_key_hierarchies(tspContext, tspHierSize, tspHier, tcsHierSize,
					       tcsHier, pulKeyHierarchySize, ppKeyHierarchy);
		free(tcsHier);
		free(tspHier);
	}

	if ((result = __tspi_add_mem_entry(tspContext, *ppKeyHierarchy))) {
		free(*ppKeyHierarchy);
		*ppKeyHierarchy = NULL;
		*pulKeyHierarchySize = 0;
	}

	return result;
}

TSS_RESULT
Tspi_Context_GetRegisteredKeysByUUID2(TSS_HCONTEXT tspContext,		/* in */
				     TSS_FLAG persistentStorageType,	/* in */
				     TSS_UUID * pUuidData,		/* in */
				     UINT32 * pulKeyHierarchySize,	/* out */
				     TSS_KM_KEYINFO2 ** ppKeyHierarchy)	/* out */
{
	TSS_RESULT result;
	TSS_KM_KEYINFO2 *tcsHier, *tspHier;
	UINT32 tcsHierSize, tspHierSize;
	TSS_UUID tcs_uuid;

	/* If out parameters are NULL, return error */
	if (pulKeyHierarchySize == NULL || ppKeyHierarchy == NULL)
			return TSPERR(TSS_E_BAD_PARAMETER);

	if (!obj_is_context(tspContext))
			return TSPERR(TSS_E_INVALID_HANDLE);

	if (pUuidData) {
		/* TSS 1.2 Spec: If a certain key UUID is provided, the returned array of
		 * TSS_KM_KEYINFO2 structures only contains data reflecting the path of the key
		 * hierarchy regarding that key. The first array entry is the key addressed by the
		 * given UUID followed by its parent key up to and including the root key. */
		if (persistentStorageType == TSS_PS_TYPE_SYSTEM) {
			if ((result = RPC_EnumRegisteredKeys2(tspContext, pUuidData,
							      pulKeyHierarchySize, ppKeyHierarchy)))
				return result;
		} else if (persistentStorageType == TSS_PS_TYPE_USER) {
			if ((result = ps_get_registered_keys2(pUuidData, &tcs_uuid, &tspHierSize,
							      &tspHier)))
				return result;
			/* The tcs_uuid returned by ps_get_registered_key2 will always be a parent
			 * of some key into the system ps of a user key into the user ps. This key
			 * needs to be searched for in the system ps to be merged */
			if ((result = RPC_EnumRegisteredKeys2(tspContext, &tcs_uuid, &tcsHierSize,
							      &tcsHier))) {
				free(tspHier);
				return result;
			}

			result = merge_key_hierarchies2(tspContext, tspHierSize, tspHier,
							tcsHierSize, tcsHier, pulKeyHierarchySize,
							ppKeyHierarchy);
			free(tcsHier);
			free(tspHier);
		} else
			return TSPERR(TSS_E_BAD_PARAMETER);
	} else {
		/* If this field is set to NULL, the returned array of TSS_KM_KEYINFO2 structures
		 * contains data reflecting the entire key hierarchy starting with root key. The
		 * array will include keys from both the user and the system TSS key store. The
		 * persistentStorageType field will be ignored. */
		if ((result = RPC_EnumRegisteredKeys2(tspContext, pUuidData, &tcsHierSize,
						      &tcsHier)))
			return result;

		if ((result = ps_get_registered_keys2(pUuidData, NULL, &tspHierSize, &tspHier))) {
			free(tcsHier);
			return result;
		}

		result = merge_key_hierarchies2(tspContext, tspHierSize, tspHier, tcsHierSize,
						tcsHier, pulKeyHierarchySize, ppKeyHierarchy);
		free(tcsHier);
		free(tspHier);
	}

	if ((result = __tspi_add_mem_entry(tspContext, *ppKeyHierarchy))) {
		free(*ppKeyHierarchy);
		*ppKeyHierarchy = NULL;
		*pulKeyHierarchySize = 0;
	}

	return result;
}
