
/*
 * 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 "trousers/tss.h"
#include "trousers/trousers.h"
#include "trousers_types.h"
#include "spi_utils.h"
#include "capabilities.h"
#include "tsplog.h"
#include "obj.h"


TSS_RESULT
obj_pcrs_add(TSS_HCONTEXT tspContext, UINT32 type, TSS_HOBJECT *phObject)
{
	TSS_RESULT result;
	UINT32 ver;
	struct tr_pcrs_obj *pcrs;

	if ((pcrs = calloc(1, sizeof(struct tr_pcrs_obj))) == NULL) {
		LogError("malloc of %zd bytes failed.", sizeof(struct tr_pcrs_obj));
		return TSPERR(TSS_E_OUTOFMEMORY);
	}

	if (type == TSS_PCRS_STRUCT_DEFAULT) {
		if ((result = obj_context_get_connection_version(tspContext, &ver))) {
			free(pcrs);
			return result;
		}

		switch (ver) {
			case TSS_TSPATTRIB_CONTEXT_VERSION_V1_2:
				pcrs->type = TSS_PCRS_STRUCT_INFO_LONG;
				pcrs->info.infolong.localityAtRelease = TSS_LOCALITY_ALL;
				break;
			case TSS_TSPATTRIB_CONTEXT_VERSION_V1_1:
				/* fall through */
			default:
				pcrs->type = TSS_PCRS_STRUCT_INFO;
				break;
		}
	} else
		pcrs->type = type;

	if ((result = obj_list_add(&pcrs_list, tspContext, 0, pcrs, phObject))) {
		free(pcrs);
		return result;
	}

	return TSS_SUCCESS;
}

void
pcrs_free(void *data)
{
	struct tr_pcrs_obj *pcrs = (struct tr_pcrs_obj *)data;

	switch (pcrs->type) {
		case TSS_PCRS_STRUCT_INFO:
			free(pcrs->info.info11.pcrSelection.pcrSelect);
			free(pcrs->pcrs);
			break;
		case TSS_PCRS_STRUCT_INFO_SHORT:
			free(pcrs->info.infoshort.pcrSelection.pcrSelect);
			free(pcrs->pcrs);
			break;
		case TSS_PCRS_STRUCT_INFO_LONG:
			free(pcrs->info.infolong.creationPCRSelection.pcrSelect);
			free(pcrs->info.infolong.releasePCRSelection.pcrSelect);
			break;
		default:
			LogDebugFn("Undefined type of PCRs object");
			break;
	}

	free(pcrs);
}

TSS_RESULT
obj_pcrs_remove(TSS_HOBJECT hObject, TSS_HCONTEXT tspContext)
{
	TSS_RESULT result;

	if ((result = obj_list_remove(&pcrs_list, &pcrs_free, hObject, tspContext)))
		return result;

	return TSS_SUCCESS;
}

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

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

	return answer;
}

TSS_RESULT
obj_pcrs_get_tsp_context(TSS_HPCRS hPcrs, TSS_HCONTEXT *tspContext)
{
	struct tsp_object *obj;

	if ((obj = obj_list_get_obj(&pcrs_list, hPcrs)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	*tspContext = obj->tspContext;

	obj_list_put(&pcrs_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_pcrs_get_type(TSS_HPCRS hPcrs, UINT32 *type)
{
	struct tsp_object *obj;
	struct tr_pcrs_obj *pcrs;

	if ((obj = obj_list_get_obj(&pcrs_list, hPcrs)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	pcrs = (struct tr_pcrs_obj *)obj->data;

	*type = pcrs->type;

	obj_list_put(&pcrs_list);

	return TSS_SUCCESS;
}

TSS_RESULT
obj_pcrs_get_selection(TSS_HPCRS hPcrs, UINT32 *size, BYTE *out)
{
	struct tsp_object *obj;
	struct tr_pcrs_obj *pcrs;
	TSS_RESULT result = TSS_SUCCESS;
	TPM_PCR_SELECTION *tmp;
	UINT64 offset = 0;

	if ((obj = obj_list_get_obj(&pcrs_list, hPcrs)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	pcrs = (struct tr_pcrs_obj *)obj->data;

	switch (pcrs->type) {
		case TSS_PCRS_STRUCT_INFO:
			tmp = &pcrs->info.info11.pcrSelection;
			break;
		case TSS_PCRS_STRUCT_INFO_SHORT:
			tmp = &pcrs->info.infoshort.pcrSelection;
			break;
		case TSS_PCRS_STRUCT_INFO_LONG:
			tmp = &pcrs->info.infolong.creationPCRSelection;
			break;
		default:
			LogDebugFn("Undefined type of PCRs object");
			result = TSPERR(TSS_E_INTERNAL_ERROR);
			goto done;
	}

	Trspi_LoadBlob_PCR_SELECTION(&offset, out, tmp);
	*size = offset;
done:
	obj_list_put(&pcrs_list);

	return result;
}

TSS_RESULT
obj_pcrs_set_values(TSS_HPCRS hPcrs, TPM_PCR_COMPOSITE *pcrComp)
{
	TSS_RESULT result = TSS_SUCCESS;
	TPM_PCR_SELECTION *select = &(pcrComp->select);
	UINT16 i, val_idx = 0;

	for (i = 0; i < select->sizeOfSelect * 8; i++) {
		if (select->pcrSelect[i / 8] & (1 << (i % 8))) {
			if ((result = obj_pcrs_set_value(hPcrs, i, TCPA_SHA1_160_HASH_LEN,
							 (BYTE *)&pcrComp->pcrValue[val_idx])))
				return result;

			val_idx++;
		}
	}

	return result;
}

TSS_RESULT
obj_pcrs_set_value(TSS_HPCRS hPcrs, UINT32 idx, UINT32 size, BYTE *value)
{
	struct tsp_object *obj;
	struct tr_pcrs_obj *pcrs;
	TSS_RESULT result = TSS_SUCCESS;
	TPM_PCR_SELECTION *select;
	TPM_COMPOSITE_HASH *compHash;
	UINT16 bytes_to_hold = (idx / 8) + 1;

	if ((obj = obj_list_get_obj(&pcrs_list, hPcrs)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	pcrs = (struct tr_pcrs_obj *)obj->data;

	switch(pcrs->type) {
		case TSS_PCRS_STRUCT_INFO:
			bytes_to_hold = (bytes_to_hold < 2) ? 2 : bytes_to_hold;
			select = &pcrs->info.info11.pcrSelection;
			compHash = &pcrs->info.info11.digestAtRelease;
			break;
		case TSS_PCRS_STRUCT_INFO_SHORT:
			bytes_to_hold = (bytes_to_hold < 3) ? 3 : bytes_to_hold;
			select = &pcrs->info.infoshort.pcrSelection;
			compHash = &pcrs->info.infoshort.digestAtRelease;
			break;
		case TSS_PCRS_STRUCT_INFO_LONG:
			bytes_to_hold = (bytes_to_hold < 3) ? 3 : bytes_to_hold;
			select = &pcrs->info.infolong.releasePCRSelection;
			compHash = &pcrs->info.infolong.digestAtRelease;
			break;
		default:
			LogDebugFn("Undefined type of PCRs object");
			result = TSPERR(TSS_E_INTERNAL_ERROR);
			goto done;
			break;
	}

	/* allocate the selection structure */
	if (select->pcrSelect == NULL) {
		if ((select->pcrSelect = malloc(bytes_to_hold)) == NULL) {
			LogError("malloc of %d bytes failed.", bytes_to_hold);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto done;
		}
		select->sizeOfSelect = bytes_to_hold;
		__tspi_memset(select->pcrSelect, 0, bytes_to_hold);

		/* allocate the pcr array */
		if ((pcrs->pcrs = malloc(bytes_to_hold * 8 *
					 TCPA_SHA1_160_HASH_LEN)) == NULL) {
			LogError("malloc of %d bytes failed.",
				bytes_to_hold * 8 * TCPA_SHA1_160_HASH_LEN);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto done;
		}
	} else if (select->sizeOfSelect < bytes_to_hold) {
		if ((select->pcrSelect = realloc(select->pcrSelect, bytes_to_hold)) == NULL) {
			LogError("malloc of %d bytes failed.", bytes_to_hold);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto done;
		}
		/* set the newly allocated bytes to 0 */
		__tspi_memset(&select->pcrSelect[select->sizeOfSelect], 0,
			      bytes_to_hold - select->sizeOfSelect);
		select->sizeOfSelect = bytes_to_hold;

		/* realloc the pcrs array */
		if ((pcrs->pcrs = realloc(pcrs->pcrs, bytes_to_hold * 8 *
					  sizeof(TPM_PCRVALUE))) == NULL) {
			LogError("malloc of %d bytes failed.",
					bytes_to_hold * 8 * TCPA_SHA1_160_HASH_LEN);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto done;
		}
	}

	/* set the bit in the selection structure */
	select->pcrSelect[idx / 8] |= (1 << (idx % 8));

	/* set the value in the pcrs array */
	memcpy(&(pcrs->pcrs[idx]), value, size);

	result = pcrs_calc_composite(select, pcrs->pcrs, compHash);

done:
	obj_list_put(&pcrs_list);

	return result;
}

TSS_RESULT
obj_pcrs_get_value(TSS_HPCRS hPcrs, UINT32 idx, UINT32 *size, BYTE **value)
{
	struct tsp_object *obj;
	struct tr_pcrs_obj *pcrs;
	TSS_RESULT result = TSS_SUCCESS;
	TPM_PCR_SELECTION *select;

	if ((obj = obj_list_get_obj(&pcrs_list, hPcrs)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	pcrs = (struct tr_pcrs_obj *)obj->data;

	switch(pcrs->type) {
		case TSS_PCRS_STRUCT_INFO:
			select = &pcrs->info.info11.pcrSelection;
			break;
		case TSS_PCRS_STRUCT_INFO_SHORT:
			select = &pcrs->info.infoshort.pcrSelection;
			break;
		case TSS_PCRS_STRUCT_INFO_LONG:
			select = &pcrs->info.infolong.creationPCRSelection;
			break;
		default:
			LogDebugFn("Undefined type of PCRs object");
			result = TSPERR(TSS_E_INTERNAL_ERROR);
			goto done;
			break;
	}

	if (select->sizeOfSelect < (idx / 8) + 1) {
		result = TSPERR(TSS_E_BAD_PARAMETER);
		goto done;
	}

	if ((*value = calloc_tspi(obj->tspContext, TCPA_SHA1_160_HASH_LEN)) == NULL) {
		LogError("malloc of %d bytes failed.", TCPA_SHA1_160_HASH_LEN);
		result = TSPERR(TSS_E_OUTOFMEMORY);
		goto done;
	}

	*size = TCPA_SHA1_160_HASH_LEN;
	memcpy(*value, &pcrs->pcrs[idx], TCPA_SHA1_160_HASH_LEN);

done:
	obj_list_put(&pcrs_list);

	return result;
}

TSS_RESULT
obj_pcrs_get_digest_at_release(TSS_HPCRS hPcrs, UINT32 *size, BYTE **out)
{
	struct tsp_object *obj;
	struct tr_pcrs_obj *pcrs;
	TSS_RESULT result = TSS_SUCCESS;
	BYTE *digest;

	if ((obj = obj_list_get_obj(&pcrs_list, hPcrs)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	pcrs = (struct tr_pcrs_obj *)obj->data;

	switch(pcrs->type) {
		case TSS_PCRS_STRUCT_INFO:
#ifdef TSS_SPEC_COMPLIANCE
			result = TSPERR(TSS_E_INVALID_OBJ_ACCESS);
			goto done;
#else
            digest = (BYTE *)&pcrs->info.info11.digestAtRelease;
            break;
#endif
		case TSS_PCRS_STRUCT_INFO_SHORT:
			digest = (BYTE *)&pcrs->info.infoshort.digestAtRelease;
			break;
		case TSS_PCRS_STRUCT_INFO_LONG:
			digest = (BYTE *)&pcrs->info.infolong.digestAtRelease;
			break;
		default:
			LogDebugFn("Undefined type of PCRs object");
			result = TSPERR(TSS_E_INTERNAL_ERROR);
			goto done;
			break;
	}

	if ((*out = calloc_tspi(obj->tspContext, sizeof(TPM_COMPOSITE_HASH))) == NULL) {
		LogError("malloc of %zd bytes failed.", sizeof(TPM_COMPOSITE_HASH));
		result = TSPERR(TSS_E_OUTOFMEMORY);
		goto done;
	}
	memcpy(*out, digest, sizeof(TPM_COMPOSITE_HASH));
	*size = sizeof(TPM_COMPOSITE_HASH);

done:
	obj_list_put(&pcrs_list);

	return result;
}

TSS_RESULT
obj_pcrs_select_index(TSS_HPCRS hPcrs, UINT32 idx)
{
	struct tsp_object *obj;
	struct tr_pcrs_obj *pcrs;
	TSS_RESULT result = TSS_SUCCESS;
	TPM_PCR_SELECTION *select;
	UINT16 bytes_to_hold = (idx / 8) + 1;

	if ((obj = obj_list_get_obj(&pcrs_list, hPcrs)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	pcrs = (struct tr_pcrs_obj *)obj->data;

	switch(pcrs->type) {
		case TSS_PCRS_STRUCT_INFO:
			bytes_to_hold = (bytes_to_hold < 2) ? 2 : bytes_to_hold;
			select = &pcrs->info.info11.pcrSelection;
			break;
		case TSS_PCRS_STRUCT_INFO_SHORT:
		case TSS_PCRS_STRUCT_INFO_LONG:
			result = TSPERR(TSS_E_INVALID_OBJ_ACCESS);
			goto done;
		default:
			LogDebugFn("Undefined type of PCRs object");
			result = TSPERR(TSS_E_INTERNAL_ERROR);
			goto done;
			break;
	}

	/* allocate the selection structure */
	if (select->pcrSelect == NULL) {
		if ((select->pcrSelect = malloc(bytes_to_hold)) == NULL) {
			LogError("malloc of %d bytes failed.", bytes_to_hold);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto done;
		}
		select->sizeOfSelect = bytes_to_hold;
		__tspi_memset(select->pcrSelect, 0, bytes_to_hold);

		/* alloc the pcrs array */
		if ((pcrs->pcrs = malloc(bytes_to_hold * 8 * TCPA_SHA1_160_HASH_LEN)) == NULL) {
			LogError("malloc of %d bytes failed.", bytes_to_hold * 8 *
				 TCPA_SHA1_160_HASH_LEN);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto done;
		}
	} else if (select->sizeOfSelect < bytes_to_hold) {
		if ((select->pcrSelect = realloc(select->pcrSelect, bytes_to_hold)) == NULL) {
			LogError("malloc of %d bytes failed.", bytes_to_hold);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto done;
		}
		/* set the newly allocated bytes to 0 */
		__tspi_memset(&select->pcrSelect[select->sizeOfSelect], 0,
			      bytes_to_hold - select->sizeOfSelect);
		select->sizeOfSelect = bytes_to_hold;

		/* realloc the pcrs array */
		if ((pcrs->pcrs = realloc(pcrs->pcrs,
					  bytes_to_hold * 8 * TCPA_SHA1_160_HASH_LEN)) == NULL) {
			LogError("malloc of %d bytes failed.", bytes_to_hold * 8 *
				 TCPA_SHA1_160_HASH_LEN);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto done;
		}
	}

	/* set the bit in the selection structure */
	select->pcrSelect[idx / 8] |= (1 << (idx % 8));

done:
	obj_list_put(&pcrs_list);

	return result;
}

TSS_RESULT
obj_pcrs_select_index_ex(TSS_HPCRS hPcrs, UINT32 dir, UINT32 idx)
{
	struct tsp_object *obj;
	struct tr_pcrs_obj *pcrs;
	TSS_RESULT result = TSS_SUCCESS;
	TPM_PCR_SELECTION *select;
	UINT16 bytes_to_hold = (idx / 8) + 1;

	if ((obj = obj_list_get_obj(&pcrs_list, hPcrs)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	pcrs = (struct tr_pcrs_obj *)obj->data;

	switch(pcrs->type) {
		case TSS_PCRS_STRUCT_INFO:
			result = TSPERR(TSS_E_INVALID_OBJ_ACCESS);
			goto done;
		case TSS_PCRS_STRUCT_INFO_SHORT:
			if (dir == TSS_PCRS_DIRECTION_CREATION) {
				result = TSPERR(TSS_E_INVALID_OBJ_ACCESS);
				goto done;
			}
			bytes_to_hold = (bytes_to_hold < 3) ? 3 : bytes_to_hold;
			select = &pcrs->info.infoshort.pcrSelection;
			break;
		case TSS_PCRS_STRUCT_INFO_LONG:
			bytes_to_hold = (bytes_to_hold < 3) ? 3 : bytes_to_hold;
			if (dir == TSS_PCRS_DIRECTION_CREATION)
				select = &pcrs->info.infolong.creationPCRSelection;
			else
				select = &pcrs->info.infolong.releasePCRSelection;
			break;
		default:
			LogDebugFn("Undefined type of PCRs object");
			result = TSPERR(TSS_E_INTERNAL_ERROR);
			goto done;
			break;
	}

	/* allocate the selection structure */
	if (select->pcrSelect == NULL) {
		if ((select->pcrSelect = malloc(bytes_to_hold)) == NULL) {
			LogError("malloc of %d bytes failed.", bytes_to_hold);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto done;
		}
		select->sizeOfSelect = bytes_to_hold;
		__tspi_memset(select->pcrSelect, 0, bytes_to_hold);

		/* alloc the pcrs array */
		if ((pcrs->pcrs = malloc(bytes_to_hold * 8 * TCPA_SHA1_160_HASH_LEN)) == NULL) {
			LogError("malloc of %d bytes failed.", bytes_to_hold * 8 *
				 TCPA_SHA1_160_HASH_LEN);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto done;
		}
	} else if (select->sizeOfSelect < bytes_to_hold) {
		if ((select->pcrSelect = realloc(select->pcrSelect, bytes_to_hold)) == NULL) {
			LogError("malloc of %d bytes failed.", bytes_to_hold);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto done;
		}
		/* set the newly allocated bytes to 0 */
		__tspi_memset(&select->pcrSelect[select->sizeOfSelect], 0,
			      bytes_to_hold - select->sizeOfSelect);
		select->sizeOfSelect = bytes_to_hold;

		/* realloc the pcrs array */
		if ((pcrs->pcrs = realloc(pcrs->pcrs,
					  bytes_to_hold * 8 * TCPA_SHA1_160_HASH_LEN)) == NULL) {
			LogError("malloc of %d bytes failed.", bytes_to_hold * 8 *
				 TCPA_SHA1_160_HASH_LEN);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto done;
		}
	}

	/* set the bit in the selection structure */
	select->pcrSelect[idx / 8] |= (1 << (idx % 8));

done:
	obj_list_put(&pcrs_list);

	return result;
}

TSS_RESULT
obj_pcrs_create_info_type(TSS_HPCRS hPcrs, UINT32 *type, UINT32 *size, BYTE **info)
{
	TSS_RESULT result;

	/* If type equals 0, then we create the structure
	   based on how the object was created */
	if (*type == 0) {
		struct tsp_object *obj;
		struct tr_pcrs_obj *pcrs;

		if ((obj = obj_list_get_obj(&pcrs_list, hPcrs)) == NULL)
			return TSPERR(TSS_E_INVALID_HANDLE);

		pcrs = (struct tr_pcrs_obj *)obj->data;
		*type = pcrs->type;

		obj_list_put(&pcrs_list);
	}

	switch (*type) {
	case TSS_PCRS_STRUCT_INFO:
		result = obj_pcrs_create_info(hPcrs, size, info);
		break;
	case TSS_PCRS_STRUCT_INFO_LONG:
		result = obj_pcrs_create_info_long(hPcrs, size, info);
		break;
	case TSS_PCRS_STRUCT_INFO_SHORT:
		result = obj_pcrs_create_info_short(hPcrs, size, info);
		break;
	default:
		return TSPERR(TSS_E_INTERNAL_ERROR);
	}

	return result;
}

/* Create a PCR info struct based on the hPcrs object */
TSS_RESULT
obj_pcrs_create_info(TSS_HPCRS hPcrs, UINT32 *size, BYTE **info)
{
	struct tsp_object *obj;
	struct tr_pcrs_obj *pcrs;
	TSS_RESULT result = TSS_SUCCESS;
	TPM_PCR_INFO info11;
	UINT64 offset;
	UINT32 ret_size;
	BYTE *ret;

	if ((obj = obj_list_get_obj(&pcrs_list, hPcrs)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	pcrs = (struct tr_pcrs_obj *)obj->data;

	/* Set everything that is not assigned to be all zeroes */
	__tspi_memset(&info11, 0, sizeof(info11));

	switch (pcrs->type) {
		case TSS_PCRS_STRUCT_INFO:
			info11 = pcrs->info.info11;
			break;
		case TSS_PCRS_STRUCT_INFO_LONG:
			info11.pcrSelection = pcrs->info.infolong.releasePCRSelection;
			info11.digestAtRelease = pcrs->info.infolong.digestAtRelease;
			break;
		case TSS_PCRS_STRUCT_INFO_SHORT:
			info11.pcrSelection = pcrs->info.infoshort.pcrSelection;
			info11.digestAtRelease = pcrs->info.infoshort.digestAtRelease;
			break;
		default:
			result = TSPERR(TSS_E_INTERNAL_ERROR);
			goto done;
	}

	offset = 0;
	Trspi_LoadBlob_PCR_INFO(&offset, NULL, &info11);
	ret_size = offset;

	if ((ret = calloc(1, ret_size)) == NULL) {
		result = TSPERR(TSS_E_OUTOFMEMORY);
		LogDebug("malloc of %u bytes failed.", ret_size);
		goto done;
	}

	offset = 0;
	Trspi_LoadBlob_PCR_INFO(&offset, ret, &info11);

	*info = ret;
	*size = ret_size;

done:
	obj_list_put(&pcrs_list);

	return result;
}

TSS_RESULT
obj_pcrs_create_info_long(TSS_HPCRS hPcrs, UINT32 *size, BYTE **info)
{
	struct tsp_object *obj;
	struct tr_pcrs_obj *pcrs;
	TSS_RESULT result = TSS_SUCCESS;
	TPM_PCR_INFO_LONG infolong;
	BYTE dummyBits[3] = { 0, 0, 0 };
	TPM_PCR_SELECTION dummySelection = { 3, dummyBits };
	UINT64 offset;
	UINT32 ret_size;
	BYTE *ret;

	if ((obj = obj_list_get_obj(&pcrs_list, hPcrs)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	pcrs = (struct tr_pcrs_obj *)obj->data;

	/* Set everything that is not assigned to be all zeroes */
	__tspi_memset(&infolong, 0, sizeof(infolong));

	infolong.tag = TPM_TAG_PCR_INFO_LONG;
	/* localityAtCreation and creationPCRSelection certainly do not need to be set here, but
	 * some chips such as Winbond do not ignore them on input, so we must give them dummy
	 * "good" values */
	infolong.localityAtCreation = TPM_LOC_ZERO;
	infolong.creationPCRSelection = dummySelection;
	switch (pcrs->type) {
		case TSS_PCRS_STRUCT_INFO:
			infolong.localityAtRelease = TSS_LOCALITY_ALL;
			infolong.releasePCRSelection = pcrs->info.info11.pcrSelection;
			infolong.digestAtRelease = pcrs->info.info11.digestAtRelease;
			break;
		case TSS_PCRS_STRUCT_INFO_LONG:
			infolong.localityAtRelease = pcrs->info.infolong.localityAtRelease;
			infolong.releasePCRSelection = pcrs->info.infolong.releasePCRSelection;
			infolong.digestAtRelease = pcrs->info.infolong.digestAtRelease;
			break;
		case TSS_PCRS_STRUCT_INFO_SHORT:
			infolong.localityAtRelease = pcrs->info.infoshort.localityAtRelease;
			infolong.releasePCRSelection = pcrs->info.infoshort.pcrSelection;
			infolong.digestAtRelease = pcrs->info.infoshort.digestAtRelease;
			break;
		default:
			result = TSPERR(TSS_E_INTERNAL_ERROR);
			goto done;
	}

	offset = 0;
	Trspi_LoadBlob_PCR_INFO_LONG(&offset, NULL, &infolong);
	ret_size = offset;

	if ((ret = calloc(1, ret_size)) == NULL) {
		result = TSPERR(TSS_E_OUTOFMEMORY);
		LogDebug("malloc of %u bytes failed.", ret_size);
		goto done;
	}

	offset = 0;
	Trspi_LoadBlob_PCR_INFO_LONG(&offset, ret, &infolong);

	*info = ret;
	*size = ret_size;

done:
	obj_list_put(&pcrs_list);

	return result;
}

TSS_RESULT
obj_pcrs_create_info_short(TSS_HPCRS hPcrs, UINT32 *size, BYTE **info)
{
	struct tsp_object *obj;
	struct tr_pcrs_obj *pcrs;
	TSS_RESULT result = TSS_SUCCESS;
	TPM_PCR_INFO_SHORT infoshort;
	BYTE select[] = { 0, 0, 0 };
	UINT64 offset;
	UINT32 ret_size;
	BYTE *ret;

	/* Set everything that is not assigned to be all zeroes */
	__tspi_memset(&infoshort, 0, sizeof(infoshort));

	if (hPcrs != NULL_HPCRS) {
		if ((obj = obj_list_get_obj(&pcrs_list, hPcrs)) == NULL)
			return TSPERR(TSS_E_INVALID_HANDLE);

		pcrs = (struct tr_pcrs_obj *)obj->data;

		switch (pcrs->type) {
			case TSS_PCRS_STRUCT_INFO:
				infoshort.pcrSelection = pcrs->info.info11.pcrSelection;
				infoshort.localityAtRelease = TSS_LOCALITY_ALL;
				infoshort.digestAtRelease = pcrs->info.info11.digestAtRelease;
				break;
			case TSS_PCRS_STRUCT_INFO_LONG:
				infoshort.pcrSelection = pcrs->info.infolong.releasePCRSelection;
				infoshort.localityAtRelease = pcrs->info.infolong.localityAtRelease;
				infoshort.digestAtRelease = pcrs->info.infolong.digestAtRelease;
				break;
			case TSS_PCRS_STRUCT_INFO_SHORT:
				infoshort = pcrs->info.infoshort;
				break;
			default:
				result = TSPERR(TSS_E_INTERNAL_ERROR);
				goto done;
		}
	} else {
		infoshort.pcrSelection.sizeOfSelect = sizeof(select);
		infoshort.pcrSelection.pcrSelect = select;
		infoshort.localityAtRelease = TSS_LOCALITY_ALL;
	}

	offset = 0;
	Trspi_LoadBlob_PCR_INFO_SHORT(&offset, NULL, &infoshort);
	ret_size = offset;

	if ((ret = calloc(1, ret_size)) == NULL) {
		result = TSPERR(TSS_E_OUTOFMEMORY);
		LogDebug("malloc of %u bytes failed.", ret_size);
		goto done;
	}

	offset = 0;
	Trspi_LoadBlob_PCR_INFO_SHORT(&offset, ret, &infoshort);

	*info = ret;
	*size = ret_size;

done:
	if (hPcrs != NULL_HPCRS)
		obj_list_put(&pcrs_list);

	return result;
}

TSS_RESULT
obj_pcrs_get_locality(TSS_HPCRS hPcrs, UINT32 *out)
{
	struct tsp_object *obj;
	struct tr_pcrs_obj *pcrs;
	TSS_RESULT result = TSS_SUCCESS;
	BYTE *locality;

	if ((obj = obj_list_get_obj(&pcrs_list, hPcrs)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	pcrs = (struct tr_pcrs_obj *)obj->data;

	switch(pcrs->type) {
		case TSS_PCRS_STRUCT_INFO:
			result = TSPERR(TSS_E_INVALID_OBJ_ACCESS);
			goto done;
		case TSS_PCRS_STRUCT_INFO_SHORT:
			locality = &pcrs->info.infoshort.localityAtRelease;
			break;
		case TSS_PCRS_STRUCT_INFO_LONG:
			locality = &pcrs->info.infolong.localityAtRelease;
			break;
		default:
			LogDebugFn("Undefined type of PCRs object");
			result = TSPERR(TSS_E_INTERNAL_ERROR);
			goto done;
	}

	*out = (UINT32)*locality;

done:
	obj_list_put(&pcrs_list);

	return result;
}

TSS_RESULT
obj_pcrs_set_locality(TSS_HPCRS hPcrs, UINT32 locality)
{
	struct tsp_object *obj;
	struct tr_pcrs_obj *pcrs;
	TSS_RESULT result = TSS_SUCCESS;
	BYTE *loc;

	if ((obj = obj_list_get_obj(&pcrs_list, hPcrs)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	pcrs = (struct tr_pcrs_obj *)obj->data;

	switch(pcrs->type) {
		case TSS_PCRS_STRUCT_INFO:
			result = TSPERR(TSS_E_INVALID_OBJ_ACCESS);
			goto done;
		case TSS_PCRS_STRUCT_INFO_SHORT:
			loc = &pcrs->info.infoshort.localityAtRelease;
			break;
		case TSS_PCRS_STRUCT_INFO_LONG:
			loc = &pcrs->info.infolong.localityAtRelease;
			break;
		default:
			LogDebugFn("Undefined type of PCRs object");
			result = TSPERR(TSS_E_INTERNAL_ERROR);
			goto done;
	}

	*loc = locality;
done:
	obj_list_put(&pcrs_list);

	return result;
}

TSS_RESULT
obj_pcrs_set_digest_at_release(TSS_HPCRS hPcrs, TPM_COMPOSITE_HASH digest)
{
	struct tsp_object *obj;
	struct tr_pcrs_obj *pcrs;
	TSS_RESULT result = TSS_SUCCESS;
	TPM_COMPOSITE_HASH *dig;

	LogDebugFn("######## Digest to be set on TSS object:");
	LogDebugData(TCPA_SHA1_160_HASH_LEN, digest.digest);

	if ((obj = obj_list_get_obj(&pcrs_list, hPcrs)) == NULL)
		return TSPERR(TSS_E_INVALID_HANDLE);

	pcrs = (struct tr_pcrs_obj *)obj->data;

	switch(pcrs->type) {
	case TSS_PCRS_STRUCT_INFO:
		result = TSPERR(TSS_E_INVALID_OBJ_ACCESS);
		goto done;
	case TSS_PCRS_STRUCT_INFO_SHORT:
		dig = &pcrs->info.infoshort.digestAtRelease;
		break;
	case TSS_PCRS_STRUCT_INFO_LONG:
		dig = &pcrs->info.infolong.digestAtRelease;
		break;
	default:
		LogDebugFn("Undefined type of PCRs object");
		result = TSPERR(TSS_E_INTERNAL_ERROR);
		goto done;
	}

	/* Copy the digest information */
	memcpy(dig->digest,&digest.digest,TPM_SHA1_160_HASH_LEN);

	LogDebugFn("######## Digest SET on TSS object:");
	LogDebugData(TCPA_SHA1_160_HASH_LEN,pcrs->info.infoshort.digestAtRelease.digest);

done:
	obj_list_put(&pcrs_list);

	return result;
}

