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


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

#include "trousers/tss.h"
#include "trousers/trousers.h"
#include "tcs_tsp.h"
#include "trousers_types.h"
#include "spi_utils.h"
#include "capabilities.h"
#include "tsplog.h"
#include "obj.h"
#include "tsp_tcsi_param.h"

TSS_RESULT
obj_context_add(TSS_HOBJECT *phObject)
{
	TSS_RESULT result;
	struct tr_context_obj *context = calloc(1, sizeof(struct tr_context_obj));

	if (context == NULL) {
		LogError("malloc of %zd bytes failed.", sizeof(struct tr_context_obj));
		return TSPERR(TSS_E_OUTOFMEMORY);
	}

#ifndef TSS_NO_GUI
	context->silentMode = TSS_TSPATTRIB_CONTEXT_NOT_SILENT;
#else
	context->silentMode = TSS_TSPATTRIB_CONTEXT_SILENT;
#endif
	if ((result = get_tcsd_hostname((char **)&context->machineName,
					&context->machineNameLength)) != TSS_SUCCESS) {
		free(context);
		return result;
	}

	LogDebug("Hostname to be used by the context is %s.", context->machineName);

	context->hashMode = TSS_TSPATTRIB_HASH_MODE_NOT_NULL;
	context->connection_policy = TSS_TSPATTRIB_CONTEXT_VERSION_V1_1;

	if ((result = obj_list_add(&context_list, NULL_HCONTEXT, 0, context, phObject))) {
		free(context->machineName);
		free(context);
		return result;
	}

	/* Add the default policy */
	if ((result = obj_policy_add(*phObject, TSS_POLICY_USAGE, &context->policy))) {
		obj_list_remove(&context_list, &__tspi_obj_context_free, *phObject, *phObject);
		return result;
	}

	context->tcs_api = &tcs_normal_api;

	return TSS_SUCCESS;
}

struct tcs_api_table *
obj_context_get_tcs_api(TSS_HCONTEXT tspContext)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;
	struct tcs_api_table *t;

	/* If the object cannot be found with the given handle, return a safe value, the normal TCS
	 * API pointer.  Since the handle is bad, the RPC_ function will barf in looking up the
	 * corresponding TCS context handle and an invalid handle error will be returned. */
	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return &tcs_normal_api;

	context = (struct tr_context_obj *)obj->data;

	/* Return the current API set we're using, either the normal API, or the transport encrypted
	 * API.  The context->tcs_api variable is switched back and forth between the two sets by
	 * the obj_context_transport_set_control function through a set attrib. */
	t = context->tcs_api;

	obj_list_put(&context_list);

	return t;
}

void
__tspi_obj_context_free(void *data)
{
	struct tr_context_obj *context = (struct tr_context_obj *)data;

	free(context->machineName);
	free(context);
}

TSS_BOOL
obj_is_context(TSS_HOBJECT hObject)
{
	TSS_BOOL answer = FALSE;

	if ((obj_list_get_obj(&context_list, hObject))) {
		answer = TRUE;
		obj_list_put(&context_list);
	}

	return answer;
}

/* Clean up transport session if necessary. */
void
obj_context_close(TSS_HCONTEXT tspContext)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return;

	context = (struct tr_context_obj *)obj->data;

#ifdef TSS_BUILD_TRANSPORT
	if (context->transAuth.AuthHandle) {
		RPC_FlushSpecific(tspContext, context->transAuth.AuthHandle, TPM_RT_TRANS);

		__tspi_memset(&context->transPub, 0, sizeof(TPM_TRANSPORT_PUBLIC));
		__tspi_memset(&context->transMod, 0, sizeof(TPM_MODIFIER_INDICATOR));
		__tspi_memset(&context->transSecret, 0, sizeof(TPM_TRANSPORT_AUTH));
		__tspi_memset(&context->transAuth, 0, sizeof(TPM_AUTH));
		__tspi_memset(&context->transLogIn, 0, sizeof(TPM_TRANSPORT_LOG_IN));
		__tspi_memset(&context->transLogOut, 0, sizeof(TPM_TRANSPORT_LOG_OUT));
		__tspi_memset(&context->transLogDigest, 0, sizeof(TPM_DIGEST));
	}
#endif

	obj_list_put(&context_list);
}

TSS_RESULT
obj_context_get_policy(TSS_HCONTEXT tspContext, UINT32 policyType, TSS_HPOLICY *phPolicy)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;
	TSS_RESULT result = TSS_SUCCESS;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	switch (policyType) {
		case TSS_POLICY_USAGE:
			*phPolicy = context->policy;
			break;
		default:
			result = TSPERR(TSS_E_BAD_PARAMETER);
	}

	obj_list_put(&context_list);

	return result;
}

TSS_RESULT
obj_context_get_machine_name(TSS_HCONTEXT tspContext, UINT32 *size, BYTE **data)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;
	TSS_RESULT result;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	if (context->machineNameLength == 0) {
		*data = NULL;
		*size = 0;
		LogDebug("context->machineName is NULL.");
	} else {
		/*
		 * Don't use calloc_tspi because this memory is
		 * not freed using "free_tspi"
		 */
		*data = calloc(1, context->machineNameLength);
		if (*data == NULL) {
			LogError("malloc of %u bytes failed.",
					context->machineNameLength);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto done;
		}
		*size = context->machineNameLength;
		LogDebug("context->machineName: %s.", context->machineName);
		memcpy(*data, context->machineName, *size);
	}

	result = TSS_SUCCESS;

done:
	obj_list_put(&context_list);

	return result;
}

/* This function converts the machine name to a TSS_UNICODE string before
 * returning it, as Tspi_GetAttribData would like. We could do the conversion
 * in Tspi_GetAttribData, but we don't have access to the TSP context there */
TSS_RESULT
obj_context_get_machine_name_attrib(TSS_HCONTEXT tspContext, UINT32 *size, BYTE **data)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;
	BYTE *utf_string;
	UINT32 utf_size;
	TSS_RESULT result;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	if (context->machineNameLength == 0) {
		*data = NULL;
		*size = 0;
	} else {
		utf_size = context->machineNameLength;
		utf_string = Trspi_Native_To_UNICODE(context->machineName,
						     &utf_size);
		if (utf_string == NULL) {
			result = TSPERR(TSS_E_INTERNAL_ERROR);
			goto done;
		}

		*data = calloc_tspi(obj->tspContext, utf_size);
		if (*data == NULL) {
			free(utf_string);
			LogError("malloc of %u bytes failed.", utf_size);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto done;
		}
		*size = utf_size;
		memcpy(*data, utf_string, utf_size);
		free(utf_string);
	}

	result = TSS_SUCCESS;

done:
	obj_list_put(&context_list);

	return result;
}

TSS_RESULT
obj_context_set_machine_name(TSS_HCONTEXT tspContext, BYTE *name, UINT32 len)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	free(context->machineName);
	context->machineName = name;
	context->machineNameLength = len;

	obj_list_put(&context_list);

	return TSS_SUCCESS;
}

TSS_BOOL
obj_context_is_silent(TSS_HCONTEXT tspContext)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;
	TSS_BOOL silent = FALSE;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return FALSE;

	context = (struct tr_context_obj *)obj->data;
	if (context->silentMode == TSS_TSPATTRIB_CONTEXT_SILENT)
		silent = TRUE;

	obj_list_put(&context_list);

	return silent;
}

TSS_RESULT
obj_context_get_mode(TSS_HCONTEXT tspContext, UINT32 *mode)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;
	*mode = context->silentMode;

	obj_list_put(&context_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_context_set_mode(TSS_HCONTEXT tspContext, UINT32 mode)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;
	context->silentMode = mode;

	obj_list_put(&context_list);

	return TSS_SUCCESS;
}

/* search the list of all policies bound to context @tspContext. If
 * one is found of type popup, return TRUE, else return FALSE. */
TSS_BOOL
obj_context_has_popups(TSS_HCONTEXT tspContext)
{
	struct tsp_object *obj;
	struct tr_policy_obj *policy;
	struct obj_list *list = &policy_list;
	TSS_BOOL ret = FALSE;

	MUTEX_LOCK(list->lock);

	for (obj = list->head; obj; obj = obj->next) {
		if (obj->tspContext == tspContext) {
			policy = (struct tr_policy_obj *)obj->data;
			if (policy->SecretMode == TSS_SECRET_MODE_POPUP)
				ret = TRUE;
			break;
		}
	}

	MUTEX_UNLOCK(list->lock);

	return ret;
}

TSS_RESULT
obj_context_get_hash_mode(TSS_HCONTEXT tspContext, UINT32 *mode)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;
	*mode = context->hashMode;

	obj_list_put(&context_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_context_set_hash_mode(TSS_HCONTEXT tspContext, UINT32 mode)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;

	switch (mode) {
		case TSS_TSPATTRIB_HASH_MODE_NULL:
		case TSS_TSPATTRIB_HASH_MODE_NOT_NULL:
			break;
		default:
			return TSPERR(TSS_E_INVALID_ATTRIB_DATA);
	}

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;
	context->hashMode = mode;

	obj_list_put(&context_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_context_get_connection_version(TSS_HCONTEXT tspContext, UINT32 *version)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	*version = context->current_connection;

	obj_list_put(&context_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_context_set_connection_policy(TSS_HCONTEXT tspContext, UINT32 policy)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;

	switch (policy) {
		case TSS_TSPATTRIB_CONTEXT_VERSION_V1_1:
		case TSS_TSPATTRIB_CONTEXT_VERSION_V1_2:
		case TSS_TSPATTRIB_CONTEXT_VERSION_AUTO:
			break;
		default:
			return TSPERR(TSS_E_INVALID_ATTRIB_DATA);
	}

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	context->connection_policy = policy;

	obj_list_put(&context_list);

	return TSS_SUCCESS;
}

#ifdef TSS_BUILD_TRANSPORT
TSS_RESULT
obj_context_set_transport_key(TSS_HCONTEXT tspContext, TSS_HKEY hKey)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	context->transKey = hKey;

	obj_list_put(&context_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_context_transport_get_mode(TSS_HCONTEXT tspContext, UINT32 value, UINT32 *out)
{
	TSS_RESULT result = TSS_SUCCESS;
	struct tsp_object *obj;
	struct tr_context_obj *context;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	switch (value) {
		case TSS_TSPATTRIB_TRANSPORT_NO_DEFAULT_ENCRYPTION:
			*out = context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_DEFAULT_ENCRYPT ?
			       FALSE : TRUE;
			break;
		case TSS_TSPATTRIB_TRANSPORT_DEFAULT_ENCRYPTION:
			*out = context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_DEFAULT_ENCRYPT ?
			       TRUE : FALSE;
			break;
		case TSS_TSPATTRIB_TRANSPORT_AUTHENTIC_CHANNEL:
			*out = context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_AUTHENTIC ?
			       TRUE : FALSE;
			break;
		case TSS_TSPATTRIB_TRANSPORT_EXCLUSIVE:
			*out = context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_EXCLUSIVE ?
			       TRUE : FALSE;
			break;
		case TSS_TSPATTRIB_TRANSPORT_STATIC_AUTH:
			*out = context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_STATIC_AUTH ?
			       TRUE : FALSE;
			break;
		default:
			LogError("Invalid attribute subflag: 0x%x", value);
			result = TSPERR(TSS_E_INVALID_ATTRIB_SUBFLAG);
			break;
	}

	obj_list_put(&context_list);

	return result;
}

TSS_RESULT
obj_context_transport_get_control(TSS_HCONTEXT tspContext, UINT32 value, UINT32 *out)
{
	TSS_RESULT result = TSS_SUCCESS;
	struct tsp_object *obj;
	struct tr_context_obj *context;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	switch (value) {
		case TSS_TSPATTRIB_DISABLE_TRANSPORT:
			*out = context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_ENABLED ? FALSE : TRUE;
			break;
		case TSS_TSPATTRIB_ENABLE_TRANSPORT:
			*out = context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_ENABLED ? TRUE : FALSE;
			break;
		default:
			LogError("Invalid attribute subflag: 0x%x", value);
			result = TSPERR(TSS_E_INVALID_ATTRIB_SUBFLAG);
			break;
	}

	obj_list_put(&context_list);

	return result;
}

TSS_RESULT
obj_context_transport_set_control(TSS_HCONTEXT tspContext, UINT32 value)
{
	TSS_RESULT result = TSS_SUCCESS;
	struct tsp_object *obj;
	struct tr_context_obj *context;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	switch (value) {
		case TSS_TSPATTRIB_ENABLE_TRANSPORT:
			context->flags |= TSS_CONTEXT_FLAGS_TRANSPORT_ENABLED;
			context->tcs_api = &tcs_transport_api;
			break;
		case TSS_TSPATTRIB_DISABLE_TRANSPORT:
			context->flags &= ~TSS_CONTEXT_FLAGS_TRANSPORT_ENABLED;
			context->tcs_api = &tcs_normal_api;
			break;
		default:
			LogError("Invalid attribute subflag: 0x%x", value);
			result = TSPERR(TSS_E_INTERNAL_ERROR);
			break;
	}

	obj_list_put(&context_list);

	return result;
}

TSS_RESULT
obj_context_transport_set_mode(TSS_HCONTEXT tspContext, UINT32 value)
{
	TSS_RESULT result = TSS_SUCCESS;
	struct tsp_object *obj;
	struct tr_context_obj *context;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	switch (value) {
		case TSS_TSPATTRIB_TRANSPORT_NO_DEFAULT_ENCRYPTION:
			context->flags &= ~TSS_CONTEXT_FLAGS_TRANSPORT_DEFAULT_ENCRYPT;
			break;
		case TSS_TSPATTRIB_TRANSPORT_DEFAULT_ENCRYPTION:
			context->flags |= TSS_CONTEXT_FLAGS_TRANSPORT_DEFAULT_ENCRYPT;
			break;
		case TSS_TSPATTRIB_TRANSPORT_AUTHENTIC_CHANNEL:
			context->flags |= TSS_CONTEXT_FLAGS_TRANSPORT_AUTHENTIC;
			break;
		case TSS_TSPATTRIB_TRANSPORT_EXCLUSIVE:
			context->flags |= TSS_CONTEXT_FLAGS_TRANSPORT_EXCLUSIVE;
			break;
		case TSS_TSPATTRIB_TRANSPORT_STATIC_AUTH:
			context->flags |= TSS_CONTEXT_FLAGS_TRANSPORT_STATIC_AUTH;
			break;
		default:
			LogError("Invalid attribute subflag: 0x%x", value);
			result = TSPERR(TSS_E_INTERNAL_ERROR);
			break;
	}

	obj_list_put(&context_list);

	return result;
}

#if 0
TSS_RESULT
get_trans_props(TSS_HCONTEXT tspContext, UINT32 *alg, UINT16 *enc)
{
	TSS_RESULT result;
	UINT32 algs[] = { TPM_ALG_MGF1, TPM_ALG_AES128, 0 }, a = 0;
	UINT16 encs[] = { TPM_ES_SYM_OFB, TPM_ES_SYM_CNT, TPM_ES_SYM_CBC_PKCS5PAD, 0 }, e = 0;
	BYTE *respData;
	UINT32 respLen, tcsSubCap32;
	UINT16 tcsSubCap16;

	if (*alg)
		goto check_es;

	for (a = 0; algs[a]; a++) {
		tcsSubCap32 = endian32(algs[a]);

		if ((result = RPC_GetTPMCapability(tspContext, TPM_CAP_TRANS_ALG, sizeof(UINT32),
						   (BYTE *)&tcsSubCap32, &respLen, &respData)))
			return result;

		if (*(TSS_BOOL *)respData == TRUE) {
			free(respData);
			break;
		}
		free(respData);
	}

	if (!algs[a]) {
		LogError("TPM reports no usable sym algorithms for transport session");
		return TSPERR(TSS_E_INTERNAL_ERROR);
	}

check_es:
	if (*enc || algs[a] == TPM_ALG_MGF1)
		goto done;

	for (e = 0; encs[e]; e++) {
		tcsSubCap16 = endian16(encs[e]);

		if ((result = RPC_GetTPMCapability(tspContext, TPM_CAP_TRANS_ES, sizeof(UINT16),
						   (BYTE *)&tcsSubCap16, &respLen, &respData)))
			return result;

		if (*(TSS_BOOL *)respData == TRUE) {
			free(respData);
			break;
		}
		free(respData);
	}

	if (!encs[e]) {
		LogError("TPM reports no usable sym modes for transport session");
		return TSPERR(TSS_E_INTERNAL_ERROR);
	}

	*alg = algs[a];
	*enc = encs[e];
done:
	return TSS_SUCCESS;
}
#endif

/* called before each TCSP_ExecuteTransport call */
TSS_RESULT
obj_context_transport_init(TSS_HCONTEXT tspContext)
{
	TSS_RESULT result = TSS_SUCCESS;
	struct tsp_object *obj;
	struct tr_context_obj *context;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	/* return immediately if we're not in a transport session */
	if (!(context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_ENABLED)) {
		result = TSPERR(TSS_E_INTERNAL_ERROR);
		goto done;
	}

	/* if the session is not yet established, setup and call EstablishTransport */
	if (!(context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_ESTABLISHED)) {
		if ((result = obj_context_transport_establish(tspContext, context)))
			goto done;
	}

	context->flags |= TSS_CONTEXT_FLAGS_TRANSPORT_ESTABLISHED;

	result = TSS_SUCCESS;
done:
	obj_list_put(&context_list);

	return result;
}

TSS_RESULT
obj_context_transport_establish(TSS_HCONTEXT tspContext, struct tr_context_obj *context)
{
	TSS_RESULT result;
	UINT32 tickLen, secretLen, transPubLen, exclusive = TSS_TCSATTRIB_TRANSPORT_DEFAULT;
	BYTE *ticks, *secret;
	UINT64 offset;
	Trspi_HashCtx hashCtx;
	TPM_DIGEST digest;
	TSS_HPOLICY hTransKeyPolicy;
	TPM_AUTH auth, *pAuth, *pTransAuth;
	TCS_KEY_HANDLE tcsTransKey;
	TSS_BOOL usesAuth = FALSE;
	UINT32 encKeyLen;
	BYTE encKey[256];
	BYTE transPubBlob[sizeof(TPM_TRANSPORT_PUBLIC)];
	BYTE transAuthBlob[sizeof(TPM_TRANSPORT_AUTH)];


	context->transPub.tag = TPM_TAG_TRANSPORT_PUBLIC;
	context->transSecret.tag = TPM_TAG_TRANSPORT_AUTH;

	if ((result = get_local_random(tspContext, FALSE, TPM_SHA1_160_HASH_LEN,
				       (BYTE **)context->transSecret.authData.authdata)))
		return result;

	if (context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_STATIC_AUTH)
		context->transKey = TPM_KH_TRANSPORT;

	if (context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_AUTHENTIC)
		context->transPub.transAttributes |= TPM_TRANSPORT_LOG;

	if (context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_EXCLUSIVE) {
		context->transPub.transAttributes |= TPM_TRANSPORT_EXCLUSIVE;
		exclusive = TSS_TCSATTRIB_TRANSPORT_EXCLUSIVE;
	}

	/* XXX implement AES128+CTR (Winbond, Infineon), then AES256+CTR (Atmel) */
	context->transPub.algId = TPM_ALG_MGF1;
	context->transPub.encScheme = TPM_ES_NONE;

	if (context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_DEFAULT_ENCRYPT) {
		context->transPub.transAttributes |= TPM_TRANSPORT_ENCRYPT;

		if (context->transKey == TPM_KH_TRANSPORT) {
			LogError("No transport key handle has been set yet. Use "
				 "Tspi_Context_SetTransEncryptionKey to set this handle");
			return TSPERR(TSS_E_INTERNAL_ERROR);
		}
	}

	if (context->transKey == TPM_KH_TRANSPORT) {
		secret = context->transSecret.authData.authdata;
		secretLen = TPM_SHA1_160_HASH_LEN;
	} else {
		offset = 0;
		Trspi_LoadBlob_TRANSPORT_AUTH(&offset, transAuthBlob, &context->transSecret);
		secretLen = offset;

		/* encrypt the sym key with the wrapping RSA key */
		encKeyLen = sizeof(encKey);
		if ((result = __tspi_rsa_encrypt(context->transKey, secretLen, transAuthBlob, &encKeyLen,
					  encKey)))
			return result;

		secret = encKey;
		secretLen = encKeyLen;
	}

	offset = 0;
	Trspi_LoadBlob_TRANSPORT_PUBLIC(&offset, transPubBlob, &context->transPub);
	transPubLen = offset;

	if (context->transKey != TPM_KH_TRANSPORT) {
		if ((result = obj_rsakey_get_tcs_handle(context->transKey, &tcsTransKey)))
			return result;

		if ((result = obj_rsakey_get_policy(context->transKey, TSS_POLICY_USAGE,
						    &hTransKeyPolicy, &usesAuth)))
			return result;

		if (!usesAuth) {
			LogError("Key used to establish a transport session must use auth");
			return TSPERR(TSS_E_TSP_TRANS_AUTHREQUIRED);
		}
	} else
		tcsTransKey = TPM_KH_TRANSPORT;

	/* If logging is on, do TPM commands spec rev106 step 8.a */
	__tspi_memset(context->transLogDigest.digest, 0, sizeof(TPM_DIGEST));
	if (context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_AUTHENTIC) {
		context->transLogIn.tag = TPM_TAG_TRANSPORT_LOG_IN;

		/* step 8.a, i */
		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_EstablishTransport);
		result |= Trspi_HashUpdate(&hashCtx, transPubLen, transPubBlob);
		result |= Trspi_Hash_UINT32(&hashCtx, secretLen);
		result |= Trspi_HashUpdate(&hashCtx, secretLen, secret);
		if ((result |= Trspi_HashFinal(&hashCtx, context->transLogIn.parameters.digest)))
			return result;

		/* step 8.a, ii */
		__tspi_memset(context->transLogIn.pubKeyHash.digest, 0, sizeof(TPM_DIGEST));

		/* step 8.a, iii */
		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_DIGEST(&hashCtx, context->transLogDigest.digest);
		result |= Trspi_Hash_TRANSPORT_LOG_IN(&hashCtx, &context->transLogIn);
		if ((result |= Trspi_HashFinal(&hashCtx, context->transLogDigest.digest)))
			return result;
	}

	if (usesAuth) {
		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_EstablishTransport);
		result |= Trspi_HashUpdate(&hashCtx, (UINT32)offset, (BYTE *)transPubBlob);
		result |= Trspi_Hash_UINT32(&hashCtx, secretLen);
		result |= Trspi_HashUpdate(&hashCtx, secretLen, secret);
		if ((result |= Trspi_HashFinal(&hashCtx, (BYTE *)&digest)))
			return result;

		/* open OIAP session with continueAuthSession = TRUE */
		if ((result = secret_PerformAuth_OIAP(context->transKey, TPM_ORD_EstablishTransport,
						      hTransKeyPolicy, TRUE, &digest, &auth)))
			return result;

		pAuth = &auth;
	} else
		pAuth = NULL;

	result = RPC_EstablishTransport(tspContext, exclusive, tcsTransKey, transPubLen,
					transPubBlob, secretLen, secret, pAuth, &context->transMod,
					&context->transAuth.AuthHandle, &tickLen, &ticks,
					&context->transAuth.NonceEven);
	if (result) {
		LogError("Establish Transport command failed: %s", Trspi_Error_String(result));
		return result;
	}

	result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
	result |= Trspi_Hash_UINT32(&hashCtx, result);
	result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_EstablishTransport);
	result |= Trspi_Hash_UINT32(&hashCtx, context->transMod);
	result |= Trspi_HashUpdate(&hashCtx, tickLen, ticks);
	result |= Trspi_Hash_NONCE(&hashCtx, context->transAuth.NonceEven.nonce);
	if ((result |= Trspi_HashFinal(&hashCtx, digest.digest)))
		return result;

	if (usesAuth) {
		if ((result = obj_policy_validate_auth_oiap(hTransKeyPolicy, &digest, pAuth)))
			return result;
	}

	/* step 8.b iii */
	offset = 0;
	Trspi_UnloadBlob_CURRENT_TICKS(&offset, ticks, &context->transLogOut.currentTicks);
	free(ticks);

	/* If logging is on, do TPM commands spec rev106 step 8.b */
	if (context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_AUTHENTIC) {
		context->transLogOut.tag = TPM_TAG_TRANSPORT_LOG_OUT;

		/* step 8.b i */
		memcpy(context->transLogOut.parameters.digest, digest.digest, sizeof(TPM_DIGEST));

		/* step 8.b ii */
		context->transLogOut.locality = context->transMod;

		/* step 8.b iii was done above */
		/* step 8.b iv */
		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_DIGEST(&hashCtx, context->transLogDigest.digest);
		result |= Trspi_Hash_TRANSPORT_LOG_OUT(&hashCtx, &context->transLogOut);
		if ((result |= Trspi_HashFinal(&hashCtx, context->transLogDigest.digest)))
			return result;
	}

	LogDebug("Transport session established successfully");

	pTransAuth = &context->transAuth;
	pTransAuth->fContinueAuthSession = TRUE;
	if ((result = get_local_random(tspContext, FALSE, sizeof(TPM_NONCE),
				       (BYTE **)pTransAuth->NonceOdd.nonce))) {
		LogError("Failed creating random nonce");
		return TSPERR(TSS_E_INTERNAL_ERROR);
	}

	return TSS_SUCCESS;
}

TSS_RESULT
do_transport_decryption(TPM_TRANSPORT_PUBLIC *transPub,
			TPM_AUTH *pTransAuth,
			BYTE *secret,
			UINT32 inLen,
			BYTE *in,
			UINT32 *outLen,
			BYTE **out)
{
	TSS_RESULT result;
	UINT32 i, decLen;
	UINT32 seedLen, ivLen;
	BYTE *dec;
	BYTE seed[(2 * sizeof(TPM_NONCE)) + strlen("out") + TPM_SHA1_160_HASH_LEN];

	/* allocate the most data anyone below might need */
	decLen = inLen;//((inLen / TSS_MAX_SYM_BLOCK_SIZE) + 1) * TSS_MAX_SYM_BLOCK_SIZE;
	if ((dec = malloc(decLen)) == NULL) {
		LogError("malloc of %u bytes failed", decLen);
		return TSPERR(TSS_E_OUTOFMEMORY);
	}

	/* set the common 3 initial values of 'seed', which is used to generate either the IV or
	 * mask */
	memcpy(seed, pTransAuth->NonceEven.nonce, sizeof(TPM_NONCE));
	memcpy(&seed[sizeof(TPM_NONCE)], pTransAuth->NonceOdd.nonce, sizeof(TPM_NONCE));
	memcpy(&seed[2 * sizeof(TPM_NONCE)], "out", strlen("out"));

	switch (transPub->algId) {
	case TPM_ALG_MGF1:
	{
		decLen = inLen;
		seedLen = sizeof(seed);

		/* add the secret data to the seed for MGF1 */
		memcpy(&seed[2 * sizeof(TPM_NONCE) + strlen("out")], secret, TPM_SHA1_160_HASH_LEN);

		if ((result = Trspi_MGF1(TSS_HASH_SHA1, seedLen, seed, decLen, dec))) {
			free(dec);
			return result;
		}

		for (i = 0; i < inLen; i++)
			dec[i] ^= in[i];
		break;
	}
	case TPM_ALG_AES128:
	{
		BYTE iv[TSS_MAX_SYM_BLOCK_SIZE];

		ivLen = TSS_MAX_SYM_BLOCK_SIZE;
		seedLen = (2 * sizeof(TPM_NONCE)) + strlen("out");

		if ((result = Trspi_MGF1(TSS_HASH_SHA1, seedLen, seed, ivLen, iv))) {
			free(dec);
			return result;
		}

		/* use the secret data as the key for AES */
		if ((result = Trspi_SymEncrypt(transPub->algId, transPub->encScheme, secret, iv, in,
					       inLen, dec, &decLen))) {
			free(dec);
			return result;
		}

		break;
	}
	default:
		LogDebug("Unknown algorithm for encrypted transport session: 0x%x",
			 transPub->algId);
		free(dec);
		return TSPERR(TSS_E_INTERNAL_ERROR);
	}

	*out = dec;
	*outLen = decLen;

	return result;
}

TSS_RESULT
do_transport_encryption(TPM_TRANSPORT_PUBLIC *transPub,
			TPM_AUTH *pTransAuth,
			BYTE *secret,
			UINT32 inLen,
			BYTE *in,
			UINT32 *outLen,
			BYTE **out)
{
	TSS_RESULT result;
	UINT32 i, encLen;
	UINT32 seedLen, ivLen;
	BYTE *enc;
	BYTE seed[(2 * sizeof(TPM_NONCE)) + strlen("in") + TPM_SHA1_160_HASH_LEN];

	/* allocate the most data anyone below might need */
	encLen = ((inLen / TSS_MAX_SYM_BLOCK_SIZE) + 1) * TSS_MAX_SYM_BLOCK_SIZE;
	if ((enc = malloc(encLen)) == NULL) {
		LogError("malloc of %u bytes failed", encLen);
		return TSPERR(TSS_E_OUTOFMEMORY);
	}

	/* set the common 3 initial values of 'seed', which is used to generate either the IV or
	 * mask */
	memcpy(seed, pTransAuth->NonceEven.nonce, sizeof(TPM_NONCE));
	memcpy(&seed[sizeof(TPM_NONCE)], pTransAuth->NonceOdd.nonce, sizeof(TPM_NONCE));
	memcpy(&seed[2 * sizeof(TPM_NONCE)], "in", strlen("in"));

	switch (transPub->algId) {
	case TPM_ALG_MGF1:
	{
		encLen = inLen;
		seedLen = sizeof(seed);

		/* add the secret data to the seed for MGF1 */
		memcpy(&seed[2 * sizeof(TPM_NONCE) + strlen("in")], secret, TPM_SHA1_160_HASH_LEN);

		if ((result = Trspi_MGF1(TSS_HASH_SHA1, seedLen, seed, encLen, enc))) {
			free(enc);
			return result;
		}

		for (i = 0; i < inLen; i++)
			enc[i] ^= in[i];
		break;
	}
	case TPM_ALG_AES128:
	{
		BYTE iv[TSS_MAX_SYM_BLOCK_SIZE];

		ivLen = TSS_MAX_SYM_BLOCK_SIZE;
		seedLen = (2 * sizeof(TPM_NONCE)) + strlen("in");

		if ((result = Trspi_MGF1(TSS_HASH_SHA1, seedLen, seed, ivLen, iv))) {
			free(enc);
			return result;
		}

		/* use the secret data as the key for AES */
		if ((result = Trspi_SymEncrypt(transPub->algId, transPub->encScheme, secret, iv, in,
					       inLen, enc, &encLen))) {
			free(enc);
			return result;
		}

		break;
	}
	default:
		LogDebug("Unknown algorithm for encrypted transport session: 0x%x",
			 transPub->algId);
		free(enc);
		return TSPERR(TSS_E_INTERNAL_ERROR);
	}

	*out = enc;
	*outLen = encLen;

	return result;
}

TSS_RESULT
obj_context_transport_execute(TSS_HCONTEXT     tspContext,
			      TPM_COMMAND_CODE ordinal,
			      UINT32           ulDataLen,
			      BYTE*            rgbData,
			      TPM_DIGEST*      pubKeyHash,
			      UINT32*          handlesLen,
			      TCS_HANDLE**     handles,
			      TPM_AUTH*        pAuth1,
			      TPM_AUTH*        pAuth2,
			      UINT32*          outLen,
			      BYTE**           out)
{
	TSS_RESULT result = TSS_SUCCESS;
	struct tsp_object *obj;
	struct tr_context_obj *context;
	UINT32 encLen, ulWrappedDataLen = 0;
	BYTE *pEnc = NULL, *rgbWrappedData = NULL;
	TPM_RESULT tpmResult;
	Trspi_HashCtx hashCtx;
	TPM_DIGEST etDigest, wDigest;
	TPM_AUTH *pTransAuth;
	UINT64 currentTicks;
	TSS_BOOL free_enc = FALSE;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	pTransAuth = &context->transAuth;

	/* TPM Commands spec rev106 step 6 */
	result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
	result |= Trspi_Hash_UINT32(&hashCtx, ordinal);

	switch (ordinal) {
		case TPM_ORD_OSAP:
		case TPM_ORD_OIAP:
			break;
		default:
			result |= Trspi_HashUpdate(&hashCtx, ulDataLen, rgbData);
			break;
	}

	if ((result |= Trspi_HashFinal(&hashCtx, wDigest.digest)))
		goto done;

	if (context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_AUTHENTIC) {
		/* TPM Commands spec rev106 step 10.b */
		memcpy(context->transLogIn.parameters.digest, wDigest.digest, sizeof(TPM_DIGEST));
		/* TPM Commands spec rev106 step 10.c, d or e, calculated by the caller */
		if (pubKeyHash)
			memcpy(context->transLogIn.pubKeyHash.digest, pubKeyHash->digest,
			       sizeof(TPM_DIGEST));
		else
			__tspi_memset(context->transLogIn.pubKeyHash.digest, 0, sizeof(TPM_DIGEST));

		/* TPM Commands spec rev106 step 10.f */
		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_DIGEST(&hashCtx, context->transLogDigest.digest);
		result |= Trspi_Hash_TRANSPORT_LOG_IN(&hashCtx, &context->transLogIn);
		if ((result |= Trspi_HashFinal(&hashCtx, context->transLogDigest.digest)))
			goto done;
	}

	/* TPM Commands spec rev106 step 7.a */
	result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
	result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_ExecuteTransport);
	result |= Trspi_Hash_UINT32(&hashCtx, ulDataLen + TSS_TPM_TXBLOB_HDR_LEN
							+ (*handlesLen * sizeof(UINT32))
							+ (pAuth1 ? TPM_AUTH_RQU_SIZE : 0)
							+ (pAuth2 ? TPM_AUTH_RQU_SIZE : 0));
	result |= Trspi_HashUpdate(&hashCtx, TPM_SHA1_160_HASH_LEN, wDigest.digest);
	if ((result |= Trspi_HashFinal(&hashCtx, etDigest.digest)))
		goto done;

	/* encrypt the data if necessary */
	if (ulDataLen && context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_DEFAULT_ENCRYPT) {
		switch (ordinal) {
		case TPM_ORD_OSAP:
		case TPM_ORD_OIAP:
			encLen = ulDataLen;
			pEnc = rgbData;
			break;
		case TPM_ORD_DSAP:
		{
			UINT64 offset;
			UINT32 tmpLen, entityValueLen;
			BYTE *tmpEnc, *entityValuePtr;

			/* DSAP is a special case where only entityValue is encrypted. So, we'll
			 * parse through rgbData until we get to entityValue, encrypt it, alloc
			 * new space for rgbData (since it could be up to a block length larger
			 * than it came in) and copy the unencrypted data and the encrypted
			 * entityValue to the new block, setting pEnc and encLen to new values. */

			offset = (2 * sizeof(UINT32)) + sizeof(TPM_NONCE);
			Trspi_UnloadBlob_UINT32(&offset, &entityValueLen, rgbData);

			entityValuePtr = &rgbData[offset];
			if ((result = do_transport_encryption(&context->transPub, pTransAuth,
							context->transSecret.authData.authdata,
							entityValueLen, entityValuePtr, &tmpLen,
							&tmpEnc)))
				goto done;

			/* offset is the amount of data before the block we encrypted and tmpLen is
			 * the size of the encrypted data */
			encLen = offset + tmpLen;
			if ((pEnc = malloc(encLen)) == NULL) {
				LogError("malloc of %u bytes failed.", encLen);
				result = TSPERR(TSS_E_OUTOFMEMORY);
				goto done;
			}
			memcpy(pEnc, rgbData, offset);
			memcpy(&pEnc[offset], tmpEnc, tmpLen);
			free(tmpEnc);

			free_enc = TRUE;
			break;
		}
		default:
			if ((result = do_transport_encryption(&context->transPub, pTransAuth,
							context->transSecret.authData.authdata,
							ulDataLen, rgbData, &encLen, &pEnc)))
				goto done;

			free_enc = TRUE;
			break;
		}
	} else {
		encLen = ulDataLen;
		pEnc = rgbData;
	}

	/* TPM Commands spec rev106 step 7.b */
	HMAC_Auth(context->transSecret.authData.authdata, etDigest.digest, pTransAuth);

	if ((result = RPC_ExecuteTransport(tspContext, ordinal, encLen, pEnc, handlesLen, handles,
					   pAuth1, pAuth2, pTransAuth, &currentTicks,
					   &context->transMod, &tpmResult, &ulWrappedDataLen,
					   &rgbWrappedData))) {
		LogDebugFn("Execute Transport failed: %s", Trspi_Error_String(result));
		goto done;
	}

	if (tpmResult) {
		LogDebug("Wrapped command ordinal 0x%x failed with result: 0x%x", ordinal,
			 tpmResult);
		result = tpmResult;
		goto done;
	}

	/* decrypt the returned wrapped data if necessary */
	if (ulWrappedDataLen && context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_DEFAULT_ENCRYPT) {
		switch (ordinal) {
		case TPM_ORD_OSAP:
		case TPM_ORD_OIAP:
		case TPM_ORD_DSAP:
			*outLen = ulWrappedDataLen;
			*out = rgbWrappedData;
			break;
		default:
			if ((result = do_transport_decryption(&context->transPub, pTransAuth,
							context->transSecret.authData.authdata,
							ulWrappedDataLen, rgbWrappedData, outLen,
							out)))
					goto done;

			free(rgbWrappedData);
		}
	} else {
		if (outLen) {
			*outLen = ulWrappedDataLen;
			*out = rgbWrappedData;
		}
	}

	/* TPM Commands spec rev106 step 14 */
	result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
	result |= Trspi_Hash_UINT32(&hashCtx, tpmResult);
	result |= Trspi_Hash_UINT32(&hashCtx, ordinal);

	switch (ordinal) {
		case TPM_ORD_OSAP:
		case TPM_ORD_OIAP:
			break;
		default:
			if (outLen)
				result |= Trspi_HashUpdate(&hashCtx, *outLen, *out);
			break;
	}

	if ((result |= Trspi_HashFinal(&hashCtx, wDigest.digest)))
		goto done;

	/* TPM Commands spec rev106 step 15 */
	result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
	result |= Trspi_Hash_UINT32(&hashCtx, result);
	result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_ExecuteTransport);
	result |= Trspi_Hash_UINT64(&hashCtx, currentTicks);
	result |= Trspi_Hash_UINT32(&hashCtx, context->transMod);
	result |= Trspi_Hash_UINT32(&hashCtx, (outLen ? *outLen : 0)
					      + TSS_TPM_TXBLOB_HDR_LEN
					      + (*handlesLen * sizeof(UINT32))
					      + (pAuth1 ? TPM_AUTH_RSP_SIZE : 0)
					      + (pAuth2 ? TPM_AUTH_RSP_SIZE : 0));
	result |= Trspi_HashUpdate(&hashCtx, TPM_SHA1_160_HASH_LEN, wDigest.digest);
	if ((result |= Trspi_HashFinal(&hashCtx, etDigest.digest)))
		goto done;

	if (validateReturnAuth(context->transSecret.authData.authdata, etDigest.digest,
			       pTransAuth)) {
		result = TSPERR(TSS_E_TSP_TRANS_AUTHFAIL);
		goto done;
	}

	if (context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_AUTHENTIC) {
		context->transLogOut.currentTicks.currentTicks = currentTicks;

		/* TPM Commands spec rev106 step 16.b */
		memcpy(context->transLogOut.parameters.digest, wDigest.digest, sizeof(TPM_DIGEST));
		/* TPM Commands spec rev106 step 16.c done above */
		/* TPM Commands spec rev106 step 16.d */
		context->transLogOut.locality = context->transMod;

		/* TPM Commands spec rev106 step 16.d */
		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_DIGEST(&hashCtx, context->transLogDigest.digest);
		result |= Trspi_Hash_TRANSPORT_LOG_OUT(&hashCtx, &context->transLogOut);
		if ((result |= Trspi_HashFinal(&hashCtx, context->transLogDigest.digest)))
			goto done;
	}

	/* Refresh nonceOdd for continued transport auth session */
	if ((result = get_local_random(tspContext, FALSE, sizeof(TPM_NONCE),
				       (BYTE **)pTransAuth->NonceOdd.nonce))) {
		LogError("Failed creating random nonce");
	}

done:
	if (free_enc)
		free(pEnc);
	obj_list_put(&context_list);

	return result;
}

/* called to close a transport session */
TSS_RESULT
obj_context_transport_close(TSS_HCONTEXT   tspContext,
			    TSS_HKEY       hKey,
			    TSS_HPOLICY    hPolicy,
			    TSS_BOOL       usesAuth,
			    TPM_SIGN_INFO* signInfo,
			    UINT32*        sigLen,
			    BYTE**         sig)
{
	TSS_RESULT result = TSS_SUCCESS;
	struct tsp_object *obj;
	struct tr_context_obj *context;
	Trspi_HashCtx hashCtx;
	TPM_DIGEST digest;
	TPM_AUTH auth, *pAuth;
	TCS_KEY_HANDLE tcsKey;
	BYTE *ticks = NULL;
	UINT32 tickLen;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	/* return immediately if we're not in a transport session */
	if (!(context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_ENABLED)) {
		result = TSPERR(TSS_E_INTERNAL_ERROR);
		goto done;
	}

	if ((result = obj_rsakey_get_tcs_handle(hKey, &tcsKey)))
		goto done;

	result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
	result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_ReleaseTransportSigned);
	result |= Trspi_Hash_NONCE(&hashCtx, signInfo->replay.nonce);
	if ((result |= Trspi_HashFinal(&hashCtx, (BYTE *)&digest)))
		goto done;

	if (usesAuth) {
		if ((result = secret_PerformAuth_OIAP(hKey, TPM_ORD_ReleaseTransportSigned,
						      hPolicy, FALSE, &digest, &auth)))
			goto done;

		pAuth = &auth;
	} else
		pAuth = NULL;

	/* continue the auth session established in obj_context_transport_establish */
	HMAC_Auth(context->transSecret.authData.authdata, digest.digest, &context->transAuth);

	if ((result = RPC_ReleaseTransportSigned(tspContext, tcsKey, &signInfo->replay, pAuth,
						 &context->transAuth,
						 &context->transLogOut.locality, &tickLen, &ticks,
						 sigLen, sig)))
		goto done;

	result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
	result |= Trspi_Hash_UINT32(&hashCtx, result);
	result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_ReleaseTransportSigned);
	result |= Trspi_Hash_UINT32(&hashCtx, context->transLogOut.locality);
	result |= Trspi_HashUpdate(&hashCtx, tickLen, ticks);
	result |= Trspi_Hash_UINT32(&hashCtx, *sigLen);
	result |= Trspi_HashUpdate(&hashCtx, *sigLen, *sig);
	if ((result |= Trspi_HashFinal(&hashCtx, (BYTE *)&digest)))
		goto done_disabled;

	/* validate the return data using the key's auth */
	if (pAuth) {
		if ((result = obj_policy_validate_auth_oiap(hPolicy, &digest, pAuth)))
			goto done_disabled;
	}

	/* validate again using the transport session's auth */
	if ((result = validateReturnAuth(context->transSecret.authData.authdata, digest.digest,
					 &context->transAuth))) {
		result = TSPERR(TSS_E_TSP_TRANS_AUTHFAIL);
		goto done_disabled;
	}

	if (context->flags & TSS_CONTEXT_FLAGS_TRANSPORT_AUTHENTIC) {
		UINT64 offset;

		/* TPM Commands Spec step 6.b */
		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_ReleaseTransportSigned);
		result |= Trspi_Hash_NONCE(&hashCtx, signInfo->replay.nonce);
		if ((result |= Trspi_HashFinal(&hashCtx, context->transLogOut.parameters.digest)))
			goto done_disabled;

		/* TPM Commands Spec step 6.c */
		offset = 0;
		Trspi_UnloadBlob_CURRENT_TICKS(&offset, ticks, &context->transLogOut.currentTicks);
		free(ticks);

		/* TPM Commands Spec step 6.d was set above */
		/* TPM Commands Spec step 6.e */
		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_DIGEST(&hashCtx, context->transLogDigest.digest);
		result |= Trspi_Hash_TRANSPORT_LOG_OUT(&hashCtx, &context->transLogOut);
		if ((result |= Trspi_HashFinal(&hashCtx, context->transLogDigest.digest)))
			goto done_disabled;
	}

	if ((signInfo->data = malloc(sizeof(TPM_DIGEST))) == NULL) {
		LogError("malloc %zd bytes failed.", sizeof(TPM_DIGEST));
		result = TSPERR(TSS_E_OUTOFMEMORY);
		goto done_disabled;
	}
	memcpy(signInfo->data, context->transLogDigest.digest, sizeof(TPM_DIGEST));
	signInfo->dataLen = sizeof(TPM_DIGEST);

	/* destroy all transport session info, except the key handle */
	__tspi_memset(&context->transPub, 0, sizeof(TPM_TRANSPORT_PUBLIC));
	__tspi_memset(&context->transMod, 0, sizeof(TPM_MODIFIER_INDICATOR));
	__tspi_memset(&context->transSecret, 0, sizeof(TPM_TRANSPORT_AUTH));
	__tspi_memset(&context->transAuth, 0, sizeof(TPM_AUTH));
	__tspi_memset(&context->transLogIn, 0, sizeof(TPM_TRANSPORT_LOG_IN));
	__tspi_memset(&context->transLogOut, 0, sizeof(TPM_TRANSPORT_LOG_OUT));
	__tspi_memset(&context->transLogDigest, 0, sizeof(TPM_DIGEST));

done_disabled:
	context->flags &= ~TSS_CONTEXT_FLAGS_TRANSPORT_ESTABLISHED;
done:
	obj_list_put(&context_list);

	return result;
}
#endif

/* XXX change 0,1,2 to #defines */
TSS_RESULT
obj_context_set_tpm_version(TSS_HCONTEXT tspContext, UINT32 ver)
{
	TSS_RESULT result = TSS_SUCCESS;
	struct tsp_object *obj;
	struct tr_context_obj *context;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	switch (ver) {
		case 1:
			context->flags &= ~TSS_CONTEXT_FLAGS_TPM_VERSION_MASK;
			context->flags |= TSS_CONTEXT_FLAGS_TPM_VERSION_1;
			break;
		case 2:
			context->flags &= ~TSS_CONTEXT_FLAGS_TPM_VERSION_MASK;
			context->flags |= TSS_CONTEXT_FLAGS_TPM_VERSION_2;
			break;
		default:
			LogError("Invalid TPM version set: %u", ver);
			result = TSPERR(TSS_E_INTERNAL_ERROR);
			break;
	}

	obj_list_put(&context_list);

	return result;
}

/* XXX change 0,1,2 to #defines */
TSS_RESULT
obj_context_get_tpm_version(TSS_HCONTEXT tspContext, UINT32 *ver)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	if (context->flags & TSS_CONTEXT_FLAGS_TPM_VERSION_1)
		*ver = 1;
	else if (context->flags & TSS_CONTEXT_FLAGS_TPM_VERSION_2)
		*ver = 2;
	else
		*ver = 0;

	obj_list_put(&context_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_context_get_loadkey_ordinal(TSS_HCONTEXT tspContext, TPM_COMMAND_CODE *ordinal)
{
	struct tsp_object *obj;
	struct tr_context_obj *context;

	if ((obj = obj_list_get_obj(&context_list, tspContext)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	context = (struct tr_context_obj *)obj->data;

	switch (context->flags & TSS_CONTEXT_FLAGS_TPM_VERSION_MASK) {
		case TSS_CONTEXT_FLAGS_TPM_VERSION_2:
			*ordinal = TPM_ORD_LoadKey2;
			break;
		default:
			LogDebugFn("No TPM version set!");
			/* fall through */
		case TSS_CONTEXT_FLAGS_TPM_VERSION_1:
			*ordinal = TPM_ORD_LoadKey;
			break;
	}

	obj_list_put(&context_list);

	return TSS_SUCCESS;
}

