
/*
 * 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 "obj.h"


TSS_RESULT
Tspi_Key_CertifyKey(TSS_HKEY hKey,			/* in */
		    TSS_HKEY hCertifyingKey,		/* in */
		    TSS_VALIDATION * pValidationData)	/* in, out */
{
	TCPA_RESULT result;
	TPM_AUTH certAuth;
	TPM_AUTH keyAuth;
	TCPA_DIGEST digest;
	TCPA_NONCE antiReplay;
	UINT32 CertifyInfoSize;
	BYTE *CertifyInfo;
	UINT32 outDataSize;
	BYTE *outData;
	TSS_HPOLICY hPolicy;
	TSS_HPOLICY hCertPolicy;
	TCS_KEY_HANDLE certifyTCSKeyHandle, keyTCSKeyHandle;
	TSS_BOOL useAuthCert;
	TSS_BOOL useAuthKey;
	TPM_AUTH *pCertAuth = &certAuth;
	TPM_AUTH *pKeyAuth = &keyAuth;
	TSS_HCONTEXT tspContext;
	Trspi_HashCtx hashCtx;


	if ((result = obj_rsakey_get_tsp_context(hKey, &tspContext)))
		return result;

	if ((result = obj_rsakey_get_policy(hKey, TSS_POLICY_USAGE,
					    &hPolicy, &useAuthKey)))
		return result;

	if ((result = obj_rsakey_get_policy(hCertifyingKey, TSS_POLICY_USAGE,
					    &hCertPolicy, &useAuthCert)))
		return result;

	if ((result = obj_rsakey_get_tcs_handle(hCertifyingKey, &certifyTCSKeyHandle)))
		return result;

	if ((result = obj_rsakey_get_tcs_handle(hKey, &keyTCSKeyHandle)))
		return result;

	if (pValidationData == NULL) {
		LogDebug("Internal Verify");
		if ((result = get_local_random(tspContext, FALSE, sizeof(TCPA_NONCE),
					       (BYTE **)antiReplay.nonce)))
			return result;
	} else {
		LogDebug("External Verify");
		if (pValidationData->ulExternalDataLength < sizeof(antiReplay.nonce))
			return TSPERR(TSS_E_BAD_PARAMETER);

		memcpy(antiReplay.nonce, pValidationData->rgbExternalData,
		       sizeof(antiReplay.nonce));
	}

	if (useAuthCert && !useAuthKey)
		return TSPERR(TSS_E_BAD_PARAMETER);

	/* Setup the auths */
	if (useAuthCert || useAuthKey) {
		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_CertifyKey);
		result |= Trspi_HashUpdate(&hashCtx, sizeof(antiReplay.nonce), antiReplay.nonce);
		if ((result |= Trspi_HashFinal(&hashCtx, digest.digest)))
			return result;
	}

	if (useAuthKey) {
		if ((result = secret_PerformAuth_OIAP(hKey, TPM_ORD_CertifyKey, hPolicy, FALSE,
						      &digest, &keyAuth)))
			return result;
	} else
		pKeyAuth = NULL;

	if (useAuthCert) {
		if ((result = secret_PerformAuth_OIAP(hCertifyingKey, TPM_ORD_CertifyKey,
						      hCertPolicy, FALSE, &digest,
						      &certAuth)))
			return result;
	} else
		pCertAuth = NULL;

	/* XXX free CertifyInfo */
	if ((result = TCS_API(tspContext)->CertifyKey(tspContext, certifyTCSKeyHandle,
						      keyTCSKeyHandle, &antiReplay, pCertAuth,
						      pKeyAuth, &CertifyInfoSize, &CertifyInfo,
						      &outDataSize, &outData)))
		return result;

	/* Validate auth */
	if (useAuthCert || useAuthKey) {
		result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
		result |= Trspi_Hash_UINT32(&hashCtx, result);
		result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_CertifyKey);
		result |= Trspi_HashUpdate(&hashCtx, CertifyInfoSize, CertifyInfo);
		result |= Trspi_Hash_UINT32(&hashCtx, outDataSize);
		result |= Trspi_HashUpdate(&hashCtx, outDataSize, outData);
		if ((result |= Trspi_HashFinal(&hashCtx, digest.digest)))
			goto cleanup;

		if (useAuthKey)
			if ((result = obj_policy_validate_auth_oiap(hPolicy, &digest, &keyAuth)))
				goto cleanup;

		if (useAuthCert)
			if ((result = obj_policy_validate_auth_oiap(hCertPolicy, &digest,
								    &certAuth)))
				goto cleanup;
	}

	if (pValidationData == NULL) {
		if ((result = Trspi_Hash(TSS_HASH_SHA1, CertifyInfoSize, CertifyInfo,
					 digest.digest)))
			goto cleanup;


		if ((result = __tspi_rsa_verify(hCertifyingKey, TSS_HASH_SHA1, TPM_SHA1_160_HASH_LEN,
					 digest.digest, outDataSize, outData))){
			result = TSPERR(TSS_E_VERIFICATION_FAILED);
			goto cleanup;
		}
	} else {
		pValidationData->ulDataLength = CertifyInfoSize;
		pValidationData->rgbData = calloc_tspi(tspContext, CertifyInfoSize);
		if (pValidationData->rgbData == NULL) {
			LogError("malloc of %u bytes failed.", CertifyInfoSize);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto cleanup;
		}
		memcpy(pValidationData->rgbData, CertifyInfo, CertifyInfoSize);
		pValidationData->ulValidationDataLength = outDataSize;
		pValidationData->rgbValidationData = calloc_tspi(tspContext, outDataSize);
		if (pValidationData->rgbValidationData == NULL) {
			LogError("malloc of %u bytes failed.", outDataSize);
			result = TSPERR(TSS_E_OUTOFMEMORY);
			goto cleanup;
		}
		memcpy(pValidationData->rgbValidationData, outData, outDataSize);
	}

	result = TSS_SUCCESS;

	cleanup:
	free(CertifyInfo);
	free(outData);
	return result;
}

