/*
 * The Initial Developer of the Original Code is International
 * Business Machines Corporation. Portions created by IBM
 * Corporation are Copyright (C) 2005 International Business
 * Machines Corporation. All Rights Reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the Common Public License as published by
 * IBM Corporation; either version 1 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * Common Public License for more details.
 *
 * You should have received a copy of the Common Public License
 * along with this program; if not, a copy can be viewed at
 * http://www.opensource.org/licenses/cpl1.0.php.
 */

#include "tpm_tspi.h"

TSS_UUID SRK_UUID = TSS_UUID_SRK;
extern TSS_HCONTEXT hContext;

const char *mapUnknown = "Unknown";

const char *usageSigning = "Signing";
const char *usageStorage = "Storage";
const char *usageIdentity = "Identity";
const char *usageAuthChange = "AuthChange";
const char *usageBind = "Bind";
const char *usageLegacy = "Legacy";

const int flagMax = 7;
const char *flagMap[] = {
	"!VOLATILE, !MIGRATABLE, !REDIRECTION",
	"!VOLATILE, !MIGRATABLE,  REDIRECTION",
	"!VOLATILE,  MIGRATABLE, !REDIRECTION",
	"!VOLATILE,  MIGRATABLE,  REDIRECTION",
	" VOLATILE, !MIGRATABLE, !REDIRECTION",
	" VOLATILE, !MIGRATABLE,  REDIRECTION",
	" VOLATILE,  MIGRATABLE, !REDIRECTION",
	" VOLATILE,  MIGRATABLE,  REDIRECTION",
};

const char *authUsageNever = "Never";
const char *authUsageAlways = "Always";

const char *algRsa = "RSA";
const char *algDes = "DES";
const char *alg3Des = "3DES";
const char *algSha = "SHA";
const char *algHmac = "HMAC";
const char *algAes = "AES";

const char *encNone = "None";
const char *encRsaPkcs15 = "RSAESPKCSv15";
const char *encRsaOaepSha1Mgf1 = "RSAESOAEP_SHA1_MGF1";

const char *sigNone = "None";
const char *sigRsaPkcs15Sha1 = "RSASSAPKCS1v15_SHA1";
const char *sigRsaPkcs15Der = "RSASSAPKCS1v15_DER";


const char *displayKeyUsageMap(UINT32 a_uiData)
{

	switch (a_uiData) {
	case TSS_KEYUSAGE_SIGN:
		return usageSigning;

	case TSS_KEYUSAGE_STORAGE:
		return usageStorage;

	case TSS_KEYUSAGE_IDENTITY:
		return usageIdentity;

	case TSS_KEYUSAGE_AUTHCHANGE:
		return usageAuthChange;

	case TSS_KEYUSAGE_BIND:
		return usageBind;

	case TSS_KEYUSAGE_LEGACY:
		return usageLegacy;
	}

	return mapUnknown;
}

const char *displayKeyFlagsMap(UINT32 a_uiFlags)
{

	int iPos = a_uiFlags & flagMax;

	return flagMap[iPos];
}

const char *displayAuthUsageMap(UINT32 a_uiData)
{

	switch (a_uiData) {
	case TPM_AUTH_NEVER:
		return authUsageNever;

	case TPM_AUTH_ALWAYS:
		return authUsageAlways;
	}

	return mapUnknown;
}

const char *displayAlgorithmMap(UINT32 a_uiData)
{

	switch (a_uiData) {
	case TSS_ALG_RSA:
		return algRsa;

	case TSS_ALG_DES:
		return algDes;

	case TSS_ALG_3DES:
		return alg3Des;

	case TSS_ALG_SHA:
		return algSha;

	case TSS_ALG_HMAC:
		return algHmac;

	case TSS_ALG_AES:
		return algAes;
	}

	return mapUnknown;
}

const char *displayEncSchemeMap(UINT32 a_uiData)
{

	switch (a_uiData) {
	case TSS_ES_NONE:
		return encNone;

	case TSS_ES_RSAESPKCSV15:
		return encRsaPkcs15;

	case TSS_ES_RSAESOAEP_SHA1_MGF1:
		return encRsaOaepSha1Mgf1;
	}

	return mapUnknown;
}

const char *displaySigSchemeMap(UINT32 a_uiData)
{

	switch (a_uiData) {
	case TSS_SS_NONE:
		return sigNone;

	case TSS_SS_RSASSAPKCS1V15_SHA1:
		return sigRsaPkcs15Sha1;

	case TSS_SS_RSASSAPKCS1V15_DER:
		return sigRsaPkcs15Der;
	}

	return mapUnknown;
}

TSS_RESULT displayKey(TSS_HKEY a_hKey)
{

	TSS_RESULT result;
	UINT32 uiAttr, uiAttrSize;
	BYTE *pAttr;
	UINT32 uiAlg;

	result =
	    getAttribData(a_hKey, TSS_TSPATTRIB_KEY_INFO,
			  TSS_TSPATTRIB_KEYINFO_VERSION, &uiAttrSize,
			  &pAttr);
	if (result != TSS_SUCCESS)
		return result;
	logMsg(_("  Version:   "));
	logHex(uiAttrSize, pAttr);

	result =
	    getAttribUint32(a_hKey, TSS_TSPATTRIB_KEY_INFO,
			    TSS_TSPATTRIB_KEYINFO_USAGE, &uiAttr);
	if (result != TSS_SUCCESS)
		return result;
	logMsg(_("  Usage:     0x%04x (%s)\n"), uiAttr, displayKeyUsageMap(uiAttr));

	result =
	    getAttribUint32(a_hKey, TSS_TSPATTRIB_KEY_INFO,
			    TSS_TSPATTRIB_KEYINFO_KEYFLAGS, &uiAttr);
	if (result != TSS_SUCCESS)
		return result;
	logMsg(_("  Flags:     0x%08x (%s)\n"), uiAttr, displayKeyFlagsMap(uiAttr));

	result =
	    getAttribUint32(a_hKey, TSS_TSPATTRIB_KEY_INFO,
			    TSS_TSPATTRIB_KEYINFO_AUTHUSAGE, &uiAttr);
	if (result != TSS_SUCCESS)
		return result;
	logMsg(_("  AuthUsage: 0x%02x (%s)\n"), uiAttr, displayAuthUsageMap(uiAttr));

	result =
	    getAttribUint32(a_hKey, TSS_TSPATTRIB_KEY_INFO,
			    TSS_TSPATTRIB_KEYINFO_ALGORITHM, &uiAlg);
	if (result != TSS_SUCCESS)
		return result;
	logMsg(_("  Algorithm:         0x%08x (%s)\n"), uiAlg, displayAlgorithmMap(uiAlg));

	result =
	    getAttribUint32(a_hKey, TSS_TSPATTRIB_KEY_INFO,
			    TSS_TSPATTRIB_KEYINFO_ENCSCHEME, &uiAttr);
	if (result != TSS_SUCCESS)
		return result;
	logMsg(_("  Encryption Scheme: 0x%08x (%s)\n"), uiAttr, displayEncSchemeMap(uiAttr));

	result =
	    getAttribUint32(a_hKey, TSS_TSPATTRIB_KEY_INFO,
			    TSS_TSPATTRIB_KEYINFO_SIGSCHEME, &uiAttr);
	if (result != TSS_SUCCESS)
		return result;
	logMsg(_("  Signature Scheme:  0x%08x (%s)\n"), uiAttr, displaySigSchemeMap(uiAttr));

	if (uiAlg == TSS_ALG_RSA) {
		result =
		    getAttribUint32(a_hKey, TSS_TSPATTRIB_RSAKEY_INFO,
				    TSS_TSPATTRIB_KEYINFO_RSA_KEYSIZE,
				    &uiAttr);
		if (result != TSS_SUCCESS)
			return result;
		logMsg(_("  Key Size:          %d bits\n"), uiAttr);
	}

	result =
	    getAttribData(a_hKey, TSS_TSPATTRIB_RSAKEY_INFO,
			  TSS_TSPATTRIB_KEYINFO_RSA_MODULUS, &uiAttrSize,
			  &pAttr);
	if (result != TSS_SUCCESS)
		return result;
	logMsg(_("  Public Key:"));
	logHex(uiAttrSize, pAttr);

	return result;
}

/*
 * Not always reliable as this depends on the TSS system.data being intact
 */
BOOL isTpmOwned(TSS_HCONTEXT hContext)
{

	TSS_HKEY hSrk;
	BOOL iRc = FALSE;

	if (keyGetKeyByUUID(hContext, TSS_PS_TYPE_SYSTEM, SRK_UUID, &hSrk)
	    != TSS_SUCCESS)
		goto out;

	iRc = TRUE;

      out:
	return iRc;
}

void tspiDebug(const char *a_szName, TSS_RESULT a_iResult)
{

	logDebug(_("%s success\n"), a_szName);
}

void tspiError(const char *a_szName, TSS_RESULT a_iResult)
{

	logError(_("%s failed: 0x%08x - layer=%s, code=%04x (%d), %s\n"),
		 a_szName, a_iResult, Trspi_Error_Layer(a_iResult),
		 Trspi_Error_Code(a_iResult),
		 Trspi_Error_Code(a_iResult),
		 Trspi_Error_String(a_iResult));
}

void tspiResult(const char *a_szName, TSS_RESULT a_tResult)
{

	if (a_tResult == TSS_SUCCESS)
		tspiDebug(a_szName, a_tResult);
	else
		tspiError(a_szName, a_tResult);
}

BOOL mapTssBool(TSS_BOOL a_bValue)
{
	BOOL bRc;

	bRc = a_bValue ? TRUE : FALSE;

	return bRc;
}

TSS_RESULT contextCreate(TSS_HCONTEXT * a_hContext)
{
	TSS_RESULT result = Tspi_Context_Create(a_hContext);
	tspiResult("Tspi_Context_Create", result);

	return result;
}

TSS_RESULT contextClose(TSS_HCONTEXT a_hContext)
{

	TSS_RESULT result = Tspi_Context_FreeMemory(a_hContext, NULL);
	tspiResult("Tspi_Context_FreeMemory", result);

	result = Tspi_Context_Close(a_hContext);
	tspiResult("Tspi_Context_Close", result);

	return result;
}

TSS_RESULT contextConnect(TSS_HCONTEXT a_hContext)
{

	TSS_RESULT result = Tspi_Context_Connect(a_hContext, NULL);
	tspiResult("Tspi_Context_Connect", result);

	return result;
}


TSS_RESULT
contextCreateObject(TSS_HCONTEXT a_hContext,
		    TSS_FLAG a_fType,
		    TSS_FLAG a_fAttrs, TSS_HOBJECT * a_hObject)
{
	TSS_RESULT result =
	    Tspi_Context_CreateObject(a_hContext, a_fType, a_fAttrs,
				      a_hObject);
	tspiResult("Tspi_Context_CreateObject", result);

	return result;
}

TSS_RESULT
contextCloseObject(TSS_HCONTEXT a_hContext, TSS_HOBJECT a_hObject)
{
	TSS_RESULT result =
	    Tspi_Context_CloseObject(a_hContext, a_hObject);
	tspiResult("Tspi_Context_CloseObject", result);

	return result;
}

TSS_RESULT contextGetTpm(TSS_HCONTEXT a_hContext, TSS_HTPM * a_hTpm)
{

	TSS_RESULT result = Tspi_Context_GetTpmObject(a_hContext, a_hTpm);
	tspiResult("Tspi_Context_GetTpmObject", result);

	return result;
}


TSS_RESULT policyGet(TSS_HOBJECT a_hObject, TSS_HPOLICY * a_hPolicy)
{
	TSS_RESULT result =
	    Tspi_GetPolicyObject(a_hObject, TSS_POLICY_USAGE, a_hPolicy);
	tspiResult("Tspi_GetPolicyObject", result);

	return result;
}

TSS_RESULT policyAssign(TSS_HPOLICY a_hPolicy, TSS_HOBJECT a_hObject)
{
	TSS_RESULT result =
	    Tspi_Policy_AssignToObject(a_hPolicy, a_hObject);
	tspiResult("Tspi_Policy_AssignToObject", result);

	return result;
}

TSS_RESULT
policySetSecret(TSS_HPOLICY a_hPolicy,
		UINT32 a_uiSecretLen, BYTE * a_chSecret)
{
	TSS_RESULT result;
	BYTE wellKnown[] = TSS_WELL_KNOWN_SECRET;

	//If secret is TSS_WELL_KNOWN_SECRET, change secret mode to TSS_SECRET_MODE_SHA1
	if (a_chSecret &&
	    a_uiSecretLen == sizeof(wellKnown) &&
	    !memcmp(a_chSecret, (BYTE *)wellKnown, sizeof(wellKnown)))
		result =
			Tspi_Policy_SetSecret(a_hPolicy, TSS_SECRET_MODE_SHA1,
					a_uiSecretLen, a_chSecret);
	else
		result =
			Tspi_Policy_SetSecret(a_hPolicy, TSS_SECRET_MODE_PLAIN,
					a_uiSecretLen, a_chSecret);
	tspiResult("Tspi_Policy_SetSecret", result);

	return result;
}

TSS_RESULT policyFlushSecret(TSS_HPOLICY a_hPolicy)
{
	TSS_RESULT result = Tspi_Policy_FlushSecret(a_hPolicy);
	tspiResult("Tspi_Policy_FlushSecret", result);

	return result;
}

TSS_RESULT
tpmGetPubEk(TSS_HTPM a_hTpm,
	    TSS_BOOL a_fOwner,
	    TSS_VALIDATION * a_pValData, TSS_HKEY * a_phEPubKey)
{

	TSS_RESULT result = Tspi_TPM_GetPubEndorsementKey(a_hTpm, a_fOwner,
							  a_pValData,
							  a_phEPubKey);
	tspiResult("Tspi_TPM_GetPubEndorsementKey", result);

	return result;
}

TSS_RESULT
tpmSetStatus(TSS_HTPM a_hTpm, TSS_FLAG a_fStatus, TSS_BOOL a_bValue)
{

	TSS_RESULT result =
	    Tspi_TPM_SetStatus(a_hTpm, a_fStatus, a_bValue);
	tspiResult("Tspi_TPM_SetStatus", result);

	return result;
}

TSS_RESULT
tpmGetStatus(TSS_HTPM a_hTpm, TSS_FLAG a_fStatus, TSS_BOOL * a_bValue)
{

	TSS_RESULT result =
	    Tspi_TPM_GetStatus(a_hTpm, a_fStatus, a_bValue);
	tspiResult("Tspi_TPM_GetStatus", result);

	return result;
}

TSS_RESULT tpmGetRandom(TSS_HTPM a_hTpm, UINT32 a_length, BYTE ** a_data)
{

	TSS_RESULT result = Tspi_TPM_GetRandom(a_hTpm, a_length, a_data);
	tspiResult("Tspi_TPM_GetRandom", result);

	return result;
}


TSS_RESULT keyLoadKey(TSS_HKEY a_hKey, TSS_HKEY a_hWrapKey)
{

	TSS_RESULT result = Tspi_Key_LoadKey(a_hKey, a_hWrapKey);
	tspiResult("Tspi_Key_LoadKey", result);

	return result;
}

TSS_RESULT
keyLoadKeyByUUID(TSS_HCONTEXT a_hContext,
		 TSS_FLAG a_fStoreType,
		 TSS_UUID a_uKeyId, TSS_HKEY * a_hKey)
{
	TSS_RESULT result =
	    Tspi_Context_LoadKeyByUUID(a_hContext, a_fStoreType, a_uKeyId,
				       a_hKey);
	tspiResult("Tspi_Context_LoadKeyByUUID", result);

	return result;
}

TSS_RESULT
keyGetPubKey(TSS_HKEY a_hKey, UINT32 * a_uiKeyLen, BYTE ** a_pKey)
{

	TSS_RESULT result = Tspi_Key_GetPubKey(a_hKey, a_uiKeyLen, a_pKey);
	tspiResult("Tspi_Key_GetPubKey", result);

	return result;
}

TSS_RESULT
keyGetKeyByUUID(TSS_HCONTEXT a_hContext,
		TSS_FLAG a_fStoreType,
		TSS_UUID a_uKeyId, TSS_HKEY * a_hKey)
{

	TSS_RESULT result =
	    Tspi_Context_GetKeyByUUID(a_hContext, a_fStoreType, a_uKeyId,
				      a_hKey);
	tspiResult("Tspi_Context_GetKeyByUUID", result);

	return result;
}

TSS_RESULT
getAttribData(TSS_HOBJECT a_hObject,
	      TSS_FLAG a_fAttr,
	      TSS_FLAG a_fSubAttr, UINT32 * a_uiSize, BYTE ** a_pData)
{

	TSS_RESULT result =
	    Tspi_GetAttribData(a_hObject, a_fAttr, a_fSubAttr, a_uiSize,
			       a_pData);
	tspiResult("Tspi_GetAttribData", result);

	return result;
}

TSS_RESULT
getAttribUint32(TSS_HOBJECT a_hObject,
		TSS_FLAG a_fAttr, TSS_FLAG a_fSubAttr, UINT32 * a_uiData)
{

	TSS_RESULT result =
	    Tspi_GetAttribUint32(a_hObject, a_fAttr, a_fSubAttr, a_uiData);
	tspiResult("Tspi_GetAttribUint32", result);

	return result;
}

TSS_RESULT
getCapability(TSS_HTPM a_hTpm,
	      TSS_FLAG a_fCapArea,
	      UINT32 a_uiSubCapLen,
	      BYTE * a_pSubCap, UINT32 * a_uiResultLen, BYTE ** a_pResult)
{
	TSS_RESULT result =
	    Tspi_TPM_GetCapability(a_hTpm, a_fCapArea, a_uiSubCapLen,
				   a_pSubCap, a_uiResultLen, a_pResult);
	tspiResult("Tspi_TPM_GetCapability", result);

	return result;
}

TSS_RESULT 
keyCreateKey(TSS_HKEY a_hKey, TSS_HKEY a_hWrapKey,
		TSS_HPCRS a_hPcrs)
{
	TSS_RESULT result = Tspi_Key_CreateKey(a_hKey, a_hWrapKey, a_hPcrs);
	tspiResult("Tspi_Key_CreateKey", result);

	return result;
}

TSS_RESULT dataSeal(TSS_HENCDATA a_hEncdata, TSS_HKEY a_hKey,
			UINT32 a_len, BYTE * a_data,
			TSS_HPCRS a_hPcrs)
{

	TSS_RESULT result =
		Tspi_Data_Seal(a_hEncdata, a_hKey, a_len, a_data, a_hPcrs);
	tspiResult("Tspi_Data_Seal", result);

	return result;
}

TSS_RESULT
tpmPcrRead(TSS_HTPM a_hTpm, UINT32 a_Idx,
		UINT32 *a_PcrSize, BYTE **a_PcrValue)
{
	TSS_RESULT result =
		Tspi_TPM_PcrRead(a_hTpm, a_Idx, a_PcrSize, a_PcrValue);
	tspiResult("Tspi_TPM_PcrRead", result);

	return result;
}

TSS_RESULT
pcrcompositeSetPcrValue(TSS_HPCRS a_hPcrs, UINT32 a_Idx,
			UINT32 a_PcrSize, BYTE *a_PcrValue)
{
	TSS_RESULT result =
		Tspi_PcrComposite_SetPcrValue(a_hPcrs, a_Idx, a_PcrSize, a_PcrValue);
	tspiResult("Tspi_PcrComposite_SetPcrValue", result);

	return result;
}

#ifdef TSS_LIB_IS_12
/*
 * These getPasswd functions will wrap calls to the other functions and check to see if the TSS
 * library's context tells us to remove the NULL terminating chars from the end of the password
 * when unicode is on.
 */
char *
getPasswd12(const char *a_pszPrompt, int* a_iLen, BOOL a_bConfirm)
{
	return _getPasswd12( a_pszPrompt, a_iLen, a_bConfirm, useUnicode);
}

char *_getPasswd12(const char *a_pszPrompt, int* a_iLen, BOOL a_bConfirm, BOOL a_bUseUnicode)
{
	UINT32 status;
	char *passwd;

	passwd = _getPasswd(a_pszPrompt, a_iLen, a_bConfirm, a_bUseUnicode);

	if (passwd && a_bUseUnicode) {
		/* If we're running against a 1.2 TSS, it will include the null terminating
		 * characters based on the TSS_TSPATTRIB_SECRET_HASH_MODE attribute of the
		 * context. If this is set to TSS_TSPATTRIB_HASH_MODE_NOT_NULL, we need to
		 * trim the two zeros off the end of the unicode string returned by
		 * Trspi_Native_To_UNICODE. */
		if (getAttribUint32(hContext, TSS_TSPATTRIB_SECRET_HASH_MODE,
				    TSS_TSPATTRIB_SECRET_HASH_MODE_POPUP, &status))
			goto out;

		if (status == TSS_TSPATTRIB_HASH_MODE_NOT_NULL)
			*a_iLen -= sizeof(TSS_UNICODE);
	}
out:
	return passwd;
}

TSS_RESULT
unloadVersionInfo(UINT64 *offset, BYTE *blob, TPM_CAP_VERSION_INFO *v)
{
	TSS_RESULT result = Trspi_UnloadBlob_CAP_VERSION_INFO(offset, blob, v);
	tspiResult("Trspi_UnloadBlob_CAP_VERSION_INFO", result);
	return result;
}

TSS_RESULT
pcrcompositeSetPcrLocality(TSS_HPCRS a_hPcrs, UINT32 localityValue)
{
	TSS_RESULT result =
		Tspi_PcrComposite_SetPcrLocality(a_hPcrs, localityValue);
	tspiResult("Tspi_PcrComposite_SetPcrLocality", result);

	return result;
}

TSS_RESULT
NVDefineSpace(TSS_HNVSTORE hNVStore, TSS_HPCRS hReadPcrComposite ,
              TSS_HPCRS hWritePcrComposite)
{
	TSS_RESULT result =
	        Tspi_NV_DefineSpace(hNVStore, hReadPcrComposite,
	                            hWritePcrComposite);

	tspiResult("Tspi_NV_DefineSpace", result);

	return result;
}

TSS_RESULT
NVReleaseSpace(TSS_HNVSTORE hNVStore)
{
	TSS_RESULT result =
	        Tspi_NV_ReleaseSpace(hNVStore);

	tspiResult("Tspi_NV_ReleaseSpace", result);

	return result;
}

TSS_RESULT
NVWriteValue(TSS_HNVSTORE hNVStore, UINT32 offset,
             UINT32 ulDataLength, BYTE *rgbDataToWrite)
{
	TSS_RESULT result =
	        Tspi_NV_WriteValue(hNVStore, offset,
	                           ulDataLength, rgbDataToWrite);

	tspiResult("Tspi_NV_WriteValue", result);

	return result;
}

TSS_RESULT
NVReadValue(TSS_HNVSTORE hNVStore, UINT32 offset,
            UINT32 *ulDataLength, BYTE **rgbDataRead)
{
	TSS_RESULT result =
	        Tspi_NV_ReadValue(hNVStore, offset,
	                          ulDataLength, rgbDataRead);

	tspiResult("Tspi_NV_ReadValue", result);

	return result;
}

TSS_RESULT
unloadNVDataPublic(UINT64 *offset, BYTE *blob, UINT32 blob_len, TPM_NV_DATA_PUBLIC *v)
{
	UINT64 off = *offset;
	TSS_RESULT result;
	result = Trspi_UnloadBlob_NV_DATA_PUBLIC(&off, blob, NULL);
	if (result == TSS_SUCCESS) {
		if (off > blob_len)
			return TSS_E_BAD_PARAMETER;
		result = Trspi_UnloadBlob_NV_DATA_PUBLIC(offset, blob, v);
	}
	tspiResult("Trspi_UnloadBlob_NV_DATA_PUBLIC", result);
	return result;
}


#endif
