
/*
 * 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 <time.h>
#include <errno.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"
#include "authsess.h"

TSS_RESULT
Tspi_ChangeAuth(TSS_HOBJECT hObjectToChange,	/* in */
		TSS_HOBJECT hParentObject,	/* in */
		TSS_HPOLICY hNewPolicy)		/* in */
{
	UINT32 keyToChangeHandle;
	TSS_RESULT result;
	TSS_HCONTEXT tspContext;

	if ((result = obj_policy_get_tsp_context(hNewPolicy, &tspContext)))
		return result;

	/* if the object to change is the TPM object, then the parent should
	 * be NULL.  If the object to change is not the TPM, then the parent
	 * object must be either an rsakey or the TPM */
	if (obj_is_tpm(hObjectToChange)) {
		if (hParentObject != NULL_HOBJECT)
			return TSPERR(TSS_E_BAD_PARAMETER);
	} else if (!obj_is_rsakey(hParentObject) && !obj_is_tpm(hParentObject)) {
		return TSPERR(TSS_E_INVALID_HANDLE);
	}

	if (obj_is_tpm(hObjectToChange)) {
		if ((result = changeauth_owner(tspContext, hObjectToChange, NULL_HTPM, hNewPolicy)))
			return result;
	} else if (obj_is_rsakey(hObjectToChange)) {
		if ((result = obj_rsakey_get_tcs_handle(hObjectToChange, &keyToChangeHandle)))
			return result;

		if (keyToChangeHandle == TPM_KEYHND_SRK) {
			if ((result = changeauth_srk(tspContext, hObjectToChange, hParentObject,
						     hNewPolicy)))
				return result;
		} else {
			if ((result = changeauth_key(tspContext, hObjectToChange, hParentObject,
						     hNewPolicy)))
				return result;
		}
	} else if (obj_is_encdata(hObjectToChange)) {
		if ((result = changeauth_encdata(tspContext, hObjectToChange, hParentObject,
						 hNewPolicy)))
			return result;
	} else if (obj_is_policy(hObjectToChange) || obj_is_hash(hObjectToChange) ||
		   obj_is_pcrs(hObjectToChange) || obj_is_context(hObjectToChange)) {
		return TSPERR(TSS_E_BAD_PARAMETER);
	} else {
		return TSPERR(TSS_E_INVALID_HANDLE);
	}

	if ((result = obj_policy_set_type(hNewPolicy, TSS_POLICY_USAGE)))
		return result;

	return Tspi_Policy_AssignToObject(hNewPolicy, hObjectToChange);

}

TSS_RESULT
Tspi_ChangeAuthAsym(TSS_HOBJECT hObjectToChange,	/* in */
		    TSS_HOBJECT hParentObject,		/* in */
		    TSS_HKEY hIdentKey,			/* in */
		    TSS_HPOLICY hNewPolicy)		/* in */
{
#if 0
	TPM_AUTH auth;
	UINT64 offset;
	BYTE hashBlob[0x1000];
	TCPA_DIGEST digest;
	TCPA_RESULT result;
	UINT32 keyHandle;
	UINT32 idHandle;
	TSS_HPOLICY hPolicy;
	TSS_HPOLICY hParentPolicy;
	UINT32 keyToChangeHandle;
	TCPA_NONCE antiReplay;
	UINT32 bytesRequested;
	UINT64 tempSize;
	BYTE tempKey[512];
	TCPA_KEY_PARMS keyParms;
	/* XXX Wow... */
	BYTE ephParms[] = { 0, 0, 0x08, 0, 0, 0, 0, 0x02, 0, 0, 0, 0 };
	UINT32 KeySizeOut;
	BYTE *KeyDataOut;
	UINT32 CertifyInfoSize;
	BYTE *CertifyInfo;
	UINT32 sigSize;
	BYTE *sig;
	UINT32 ephHandle;
	TPM_CHANGEAUTH_VALIDATE caValidate;
	TCPA_SECRET newSecret, oldSecret;
	BYTE seed[20];
	BYTE a1[256];
	UINT32 a1Size;
	TSS_KEY ephemeralKey;
	TCPA_DIGEST newAuthLink;
	UINT32 encObjectSize;
	BYTE *encObject = NULL;
	UINT32 encDataSizeOut;
	BYTE *encDataOut;
	TCPA_NONCE saltNonce;
	TCPA_DIGEST changeProof;
	TSS_HPOLICY hOldPolicy;
	UINT32 caValidSize;
	UINT32 keyObjectSize;
	BYTE *keyObject;
	TSS_KEY keyContainer;
	TCPA_STORED_DATA dataContainer;
	BYTE *dataObject;
	UINT32 dataObjectSize;
	UINT16 entityType;
	TSS_BOOL useAuth = TRUE; // XXX
	TPM_AUTH *pAuth;
	BYTE dataBlob[1024];
	TSS_HCONTEXT tspContext;
	Trspi_HashCtx hashCtx;

	if ((result = obj_policy_get_tsp_context(hNewPolicy, &tspContext)))
		return result;

	/*  grab all of the needed handles */
	if ((result = obj_rsakey_get_tcs_handle(hIdentKey, &idHandle)))
		return result;

	/*  get the secret for the parent */
	if ((result = obj_rsakey_get_policy(hIdentKey, TSS_POLICY_USAGE, &hPolicy, &useAuth)))
		return result;

	/*  get the parent secret */
	if ((result = Tspi_GetPolicyObject(hParentObject, TSS_POLICY_USAGE, &hParentPolicy)))
		return result;

	if (!obj_is_rsakey(hParentObject) && !obj_is_tpm(hParentObject))
		return TSPERR(TSS_E_INVALID_HANDLE);

	/*  get the keyObject  */
	if ((result = obj_rsakey_get_tcs_handle(hParentObject, &keyHandle)))
		return result;

	if (obj_is_rsakey(hObjectToChange) ||
	    obj_is_encdata(hObjectToChange)) {

		if ((result = obj_rsakey_get_tcs_handle(hObjectToChange, &keyToChangeHandle)))
			return result;

		if (keyToChangeHandle == TPM_KEYHND_SRK) {
			return TSPERR(TSS_E_BAD_PARAMETER);
		} else {
			/*  generate container for ephemeral key */
			keyParms.algorithmID = 1;	/* rsa */
			keyParms.encScheme = 3;
			keyParms.sigScheme = 1;
			keyParms.parmSize = 12;
			keyParms.parms = malloc(12);
			if (keyParms.parms == NULL) {
				LogError("malloc of %d bytes failed.", 12);
				return TSPERR(TSS_E_OUTOFMEMORY);
			}
			memcpy(keyParms.parms, ephParms, 12);

			tempSize = 0;
			Trspi_LoadBlob_KEY_PARMS(&tempSize, tempKey, &keyParms);

			/*  generate antireplay nonce */
			bytesRequested = 20;
			if ((result = get_local_random(tspContext, FALSE, bytesRequested,
						       (BYTE **)antiReplay.nonce)))
				return result;

			/* caluculate auth data */
			result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
			result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_ChangeAuthAsymStart);
			result |= Trspi_HashUpdate(&hashCtx, TCPA_SHA1_160_HASH_LEN,
						   antiReplay.nonce);
			result |= Trspi_Hash_KEY_PARMS(&hashCtx, &keyParms);
			if ((result |= Trspi_HashFinal(&hashCtx, digest.digest)))
				return result;

			if (useAuth) {
				if ((result = secret_PerformAuth_OIAP(hIdentKey,
								      TPM_ORD_ChangeAuthAsymStart,
								      hPolicy, FALSE, &digest,
								      &auth)))
					return result;

				pAuth = &auth;
			} else {
				pAuth = NULL;
			}

			if ((result = TCSP_ChangeAuthAsymStart(tspContext, idHandle, antiReplay,
							       tempSize, tempKey, pAuth,
							       &KeySizeOut, &KeyDataOut,
							       &CertifyInfoSize, &CertifyInfo,
							       &sigSize, &sig, &ephHandle)))
				return result;

			/* Validate the Auth's */
			result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
			result |= Trspi_Hash_UINT32(&hashCtx, result);
			result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_ChangeAuthAsymStart);
			result |= Trspi_HashUpdate(&hashCtx, CertifyInfoSize, CertifyInfo);
			result |= Trspi_Hash_UINT32(&hashCtx, sigSize);
			result |= Trspi_HashUpdate(&hashCtx, sigSize, sig);
			result |= Trspi_Hash_UINT32(&hashCtx, ephHandle);
			result |= Trspi_HashUpdate(&hashCtx, KeySizeOut, KeyDataOut);
			if ((result |= Trspi_HashFinal(&hashCtx, digest.digest)))
				return result;

			if (useAuth) {
				if ((result = obj_policy_validate_auth_oiap(hPolicy, &digest,
									    &auth)))
					return result;
			}

			/*  generate random data for asymfinish */
			if ((result = get_local_random(tspContext, FALSE, bytesRequested,
						       (BYTE **)&caValidate.n1.nonce)))
				return result;

			if ((result = get_local_random(tspContext, FALSE, bytesRequested,
						       (BYTE **)&antiReplay.nonce)))
				return result;

			if ((result = get_local_random(tspContext, FALSE, bytesRequested,
						       (BYTE **)&seed)))
				return result;

			if ((result = Tspi_GetPolicyObject(hObjectToChange, TSS_POLICY_USAGE,
							  &hOldPolicy)))
				return result;

			if ((result = obj_policy_get_secret(hNewPolicy, TR_SECRET_CTX_NEW,
							    &newSecret)))
				return result;
			if ((result = obj_policy_get_secret(hOldPolicy, TR_SECRET_CTX_NOT_NEW,
							    &oldSecret)))
				return result;

			/* Encrypt the ChangeAuthValidate structure with the
			 * ephemeral key */

			memcpy(caValidate.newAuthSecret.authdata, newSecret.authdata, 20);

			offset = 0;
			Trspi_LoadBlob_CHANGEAUTH_VALIDATE(&offset, hashBlob, &caValidate);
			caValidSize = offset;

			offset = 0;
			if ((result = UnloadBlob_TSS_KEY(&offset, KeyDataOut, &ephemeralKey)))
				return result;

			Trspi_RSA_Encrypt(hashBlob, caValidSize, a1, &a1Size,
				       ephemeralKey.pubKey.key,
				       ephemeralKey.pubKey.keyLength);

			free_key_refs(&ephemeralKey);

			Trspi_HMAC(TSS_HASH_SHA1, 20, oldSecret.authdata,
					 20, newSecret.authdata,
					 newAuthLink.digest);

			if (obj_is_rsakey(hObjectToChange)) {
				if ((result = obj_rsakey_get_blob(hObjectToChange,
						   &keyObjectSize, &keyObject)))
					return result;

				__tspi_memset(&keyContainer, 0, sizeof(TSS_KEY));

				offset = 0;
				if ((result = UnloadBlob_TSS_KEY(&offset,
								 keyObject,
								 &keyContainer)))
					return result;

				encObjectSize = keyContainer.encSize;
				encObject = malloc(encObjectSize);
				if (encObject == NULL) {
					LogError("malloc of %d bytes failed.",
							encObjectSize);
					free_key_refs(&keyContainer);
					return TSPERR(TSS_E_OUTOFMEMORY);
				}
				memcpy(encObject, keyContainer.encData,
						encObjectSize);
				entityType = TCPA_ET_KEY;
			} else {
				if ((result = obj_encdata_get_data(hObjectToChange,
						   &dataObjectSize, &dataObject)))
					return result;

				offset = 0;
				if ((result = Trspi_UnloadBlob_STORED_DATA(&offset,
									   dataObject,
									   &dataContainer)))
					return result;

				encObjectSize = dataContainer.encDataSize;
				encObject = malloc(encObjectSize);
				if (encObject == NULL) {
					LogError("malloc of %d bytes failed.", encObjectSize);
					free(dataContainer.sealInfo);
					free(dataContainer.encData);
					return TSPERR(TSS_E_OUTOFMEMORY);
				}
				memcpy(encObject, dataContainer.encData,
						encObjectSize);
				entityType = TCPA_ET_DATA;
			}

			result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
			result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_ChangeAuthAsymFinish);
			result |= Trspi_Hash_UINT16(&hashCtx, entityType);
			result |= Trspi_HashUpdate(&hashCtx, TCPA_SHA1_160_HASH_LEN,
						   newAuthLink.digest);
			result |= Trspi_Hash_UINT32(&hashCtx, a1Size);
			result |= Trspi_HashUpdate(&hashCtx, a1Size, a1);
			result |= Trspi_Hash_UINT32(&hashCtx, encObjectSize);
			result |= Trspi_HashUpdate(&hashCtx, encObjectSize, encObject);
			if ((result |= Trspi_HashFinal(&hashCtx, digest.digest)))
				return result;

			if (useAuth) {
				if ((result = secret_PerformAuth_OIAP(hParentObject,
								      TPM_ORD_ChangeAuthAsymFinish,
								      hParentPolicy, FALSE,
								      &digest, &auth))) {
					free(encObject);
					free_key_refs(&keyContainer);
					return result;
				}
				pAuth = &auth;
			} else {
				pAuth = NULL;
			}

			if ((result = TCSP_ChangeAuthAsymFinish(tspContext, keyHandle, ephHandle,
							       entityType, newAuthLink, a1Size, a1,
							       encObjectSize, encObject, pAuth,
							       &encDataSizeOut, &encDataOut,
							       &saltNonce, &changeProof))) {
				free_key_refs(&keyContainer);
				free(encObject);
				return result;
			}

			/* ---  Validate the Auth's */
			result = Trspi_HashInit(&hashCtx, TSS_HASH_SHA1);
			result |= Trspi_Hash_UINT32(&hashCtx, result);
			result |= Trspi_Hash_UINT32(&hashCtx, TPM_ORD_ChangeAuthAsymFinish);
			result |= Trspi_Hash_UINT32(&hashCtx, encDataSizeOut);
			result |= Trspi_HashUpdate(&hashCtx, encDataSizeOut, encDataOut);
			result |= Trspi_HashUpdate(&hashCtx, TCPA_SHA1_160_HASH_LEN,
						   saltNonce.nonce);
			result |= Trspi_HashUpdate(&hashCtx, TCPA_SHA1_160_HASH_LEN,
						   changeProof.digest);
			if ((result |= Trspi_HashFinal(&hashCtx, digest.digest)))
				return result;

			if (useAuth) {
				if ((result = obj_policy_validate_auth_oiap(hParentPolicy,
									&digest,
									&auth))) {
					free_key_refs(&keyContainer);
					free(encObject);
					return result;
				}
			}

			if (entityType == TCPA_ET_KEY ||
			    entityType == TCPA_ET_KEYHANDLE) {
				memcpy(keyContainer.encData, encDataOut, encDataSizeOut);
				keyContainer.encSize = encDataSizeOut;

				offset = 0;
				LoadBlob_TSS_KEY(&offset, keyObject, &keyContainer);
				free_key_refs(&keyContainer);
				if ((result = obj_rsakey_set_tcpakey(hObjectToChange, offset,
								     keyObject))) {
					free(encObject);
					return result;
				}
			}

			if (entityType == TCPA_ET_DATA) {
				memcpy(dataContainer.encData, encDataOut,
						encDataSizeOut);
				dataContainer.encDataSize = encDataSizeOut;

				offset = 0;
				Trspi_LoadBlob_STORED_DATA(&offset, dataBlob,
							   &dataContainer);
				free(dataContainer.sealInfo);
				free(dataContainer.encData);
				obj_encdata_set_data(hObjectToChange,
						   offset, dataBlob);
			}
		}
	} else
		return TSPERR(TSS_E_BAD_PARAMETER);

	free(encObject);

	return Tspi_Policy_AssignToObject(hNewPolicy, hObjectToChange);
#else
	return TSPERR(TSS_E_NOTIMPL);
#endif
}

