/********************************************************************************/
/*										*/
/*			     The RSA key cache 					*/
/*			     Written by Ken Goldman				*/
/*		       IBM Thomas J. Watson Research Center			*/
/*            $Id: RsaKeyCache.c 1519 2019-11-15 20:43:51Z kgoldman $		*/
/*										*/
/*  Licenses and Notices							*/
/*										*/
/*  1. Copyright Licenses:							*/
/*										*/
/*  - Trusted Computing Group (TCG) grants to the user of the source code in	*/
/*    this specification (the "Source Code") a worldwide, irrevocable, 		*/
/*    nonexclusive, royalty free, copyright license to reproduce, create 	*/
/*    derivative works, distribute, display and perform the Source Code and	*/
/*    derivative works thereof, and to grant others the rights granted herein.	*/
/*										*/
/*  - The TCG grants to the user of the other parts of the specification 	*/
/*    (other than the Source Code) the rights to reproduce, distribute, 	*/
/*    display, and perform the specification solely for the purpose of 		*/
/*    developing products based on such documents.				*/
/*										*/
/*  2. Source Code Distribution Conditions:					*/
/*										*/
/*  - Redistributions of Source Code must retain the above copyright licenses, 	*/
/*    this list of conditions and the following disclaimers.			*/
/*										*/
/*  - Redistributions in binary form must reproduce the above copyright 	*/
/*    licenses, this list of conditions	and the following disclaimers in the 	*/
/*    documentation and/or other materials provided with the distribution.	*/
/*										*/
/*  3. Disclaimers:								*/
/*										*/
/*  - THE COPYRIGHT LICENSES SET FORTH ABOVE DO NOT REPRESENT ANY FORM OF	*/
/*  LICENSE OR WAIVER, EXPRESS OR IMPLIED, BY ESTOPPEL OR OTHERWISE, WITH	*/
/*  RESPECT TO PATENT RIGHTS HELD BY TCG MEMBERS (OR OTHER THIRD PARTIES)	*/
/*  THAT MAY BE NECESSARY TO IMPLEMENT THIS SPECIFICATION OR OTHERWISE.		*/
/*  Contact TCG Administration (admin@trustedcomputinggroup.org) for 		*/
/*  information on specification licensing rights available through TCG 	*/
/*  membership agreements.							*/
/*										*/
/*  - THIS SPECIFICATION IS PROVIDED "AS IS" WITH NO EXPRESS OR IMPLIED 	*/
/*    WARRANTIES WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY OR 	*/
/*    FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, COMPLETENESS, OR 		*/
/*    NONINFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS, OR ANY WARRANTY 		*/
/*    OTHERWISE ARISING OUT OF ANY PROPOSAL, SPECIFICATION OR SAMPLE.		*/
/*										*/
/*  - Without limitation, TCG and its members and licensors disclaim all 	*/
/*    liability, including liability for infringement of any proprietary 	*/
/*    rights, relating to use of information in this specification and to the	*/
/*    implementation of this specification, and TCG disclaims all liability for	*/
/*    cost of procurement of substitute goods or services, lost profits, loss 	*/
/*    of use, loss of data or any incidental, consequential, direct, indirect, 	*/
/*    or special damages, whether under contract, tort, warranty or otherwise, 	*/
/*    arising in any way out of use or reliance upon this specification or any 	*/
/*    information herein.							*/
/*										*/
/*  (c) Copyright IBM Corp. and others, 2016 - 2019				*/
/*										*/
/********************************************************************************/


/* 10.2.22 RsaKeyCache.c */
/* 10.2.22.2 Includes, Types, Locals, and Defines */
#include "Tpm.h"
#if USE_RSA_KEY_CACHE
#include  <stdio.h>
#include "RsaKeyCache_fp.h"
#if CRT_FORMAT_RSA == YES
#define CACHE_FILE_NAME "RsaKeyCacheCrt.data"
#else
#define CACHE_FILE_NAME "RsaKeyCacheNoCrt.data"
#endif
typedef struct _RSA_KEY_CACHE_
{
    TPM2B_PUBLIC_KEY_RSA        publicModulus;
    TPM2B_PRIVATE_KEY_RSA       privateExponent;
} RSA_KEY_CACHE;
/* Determine the number of RSA key sizes for the cache */
TPMI_RSA_KEY_BITS       SupportedRsaKeySizes[] = {
#if RSA_1024
						  1024,
#endif
#if RSA_2048
						  2048,
#endif
#if RSA_3072
						  3072,
#endif
#if RSA_4096
						  4096,
#endif
						  0
};
#define RSA_KEY_CACHE_ENTRIES (RSA_1024 + RSA_2048 + RSA_3072 + RSA_4096)

/* The key cache holds one entry for each of the supported key sizes */
RSA_KEY_CACHE        s_rsaKeyCache[RSA_KEY_CACHE_ENTRIES];
/* Indicates if the key cache is loaded. It can be loaded and enabled or disabled. */
BOOL                 s_keyCacheLoaded = 0;
/* Indicates if the key cache is enabled */
int                  s_rsaKeyCacheEnabled = FALSE;
/* 10.2.22.2.1 RsaKeyCacheControl() */
/* Used to enable and disable the RSA key cache. */
LIB_EXPORT void
RsaKeyCacheControl(
		   int             state
		   )
{
    s_rsaKeyCacheEnabled = state;
}
/* 10.2.22.2.2 InitializeKeyCache() */
/* This will initialize the key cache and attempt to write it to a file for later use. */
/* Return Value	Meaning */
/* TRUE(1)	success */
/* FALSE(0)	failure */
static BOOL
InitializeKeyCache(
		   OBJECT              *rsaKey,            // IN/OUT: The object structure in which
		   //          the key is created.
		   RAND_STATE          *rand               // IN: if not NULL, the deterministic
		   //     RNG state
		   )
{
    int                  index;
    TPM_KEY_BITS         keySave = rsaKey->publicArea.parameters.rsaDetail.keyBits;
    BOOL                 OK = TRUE;
    //
    s_rsaKeyCacheEnabled = FALSE;
    for(index = 0; OK && index < RSA_KEY_CACHE_ENTRIES; index++)
	{
	    rsaKey->publicArea.parameters.rsaDetail.keyBits
		= SupportedRsaKeySizes[index];
	    OK = (CryptRsaGenerateKey(rsaKey, rand) == TPM_RC_SUCCESS);
	    if(OK)
		{
		    s_rsaKeyCache[index].publicModulus = rsaKey->publicArea.unique.rsa;
		    s_rsaKeyCache[index].privatePrime = rsaKey->sensitive.sensitive.rsa;
		    s_rsaKeyCache[index].privateExponent = rsaKey->privateExponent;
		}
	}
    rsaKey->publicArea.parameters.rsaDetail.keyBits = keySave;
    s_keyCacheLoaded = OK;
#if SIMULATION && USE_RSA_KEY_CACHE && USE_KEY_CACHE_FILE
    if(OK)
	{
	    FILE                *cacheFile;
	    const char          *fn = CACHE_FILE_NAME;
#if defined _MSC_VER
	    if(fopen_s(&cacheFile, fn, "w+b") != 0)
#else
		cacheFile = fopen(fn, "w+b");
	    if(NULL == cacheFile)
#endif
		{
		    printf("Can't open %s for write.\n", fn);
		}
	    else
		{
		    fseek(cacheFile, 0, SEEK_SET);
		    if(fwrite(s_rsaKeyCache, 1, sizeof(s_rsaKeyCache), cacheFile)
		       != sizeof(s_rsaKeyCache))
			{
			    printf("Error writing cache to %s.", fn);
			}
		}
	    if(cacheFile)
		fclose(cacheFile);
	}
#endif
    return s_keyCacheLoaded;
}
/* 10.2.22.2.3	KeyCacheLoaded() */
/* Checks that key cache is loaded. */
/* Return Value	Meaning */
/* TRUE(1)	cache loaded */
/* FALSE(0)	cache not loaded */
static BOOL
KeyCacheLoaded(
	       OBJECT              *rsaKey,            // IN/OUT: The object structure in which
	       //          the key is created.
	       RAND_STATE          *rand               // IN: if not NULL, the deterministic
	       //     RNG state
	       )
{
#if SIMULATION && USE_RSA_KEY_CACHE && USE_KEY_CACHE_FILE
    if(!s_keyCacheLoaded)
	{
	    FILE            *cacheFile;
	    const char *     fn = CACHE_FILE_NAME;
#if defined _MSC_VER && 1
	    if(fopen_s(&cacheFile, fn, "r+b") == 0)
#else
		cacheFile = fopen(fn, "r+b");
	    if(NULL != cacheFile)
#endif
		{
		    fseek(cacheFile, 0L, SEEK_END);
		    if(ftell(cacheFile) == sizeof(s_rsaKeyCache))
			{
			    fseek(cacheFile, 0L, SEEK_SET);
			    s_keyCacheLoaded = (
						fread(&s_rsaKeyCache, 1, sizeof(s_rsaKeyCache), cacheFile)
						== sizeof(s_rsaKeyCache));
			}
		    fclose(cacheFile);
		}
	}
#endif
    if(!s_keyCacheLoaded)
	s_rsaKeyCacheEnabled = InitializeKeyCache(rsaKey, rand);
    return s_keyCacheLoaded;
}
/* 10.2.22.2.4	GetCachedRsaKey() */
/* Return Value	Meaning */
/* TRUE(1)	key loaded */
/* FALSE(0)	key not loaded */
BOOL
GetCachedRsaKey(
		OBJECT              *key,
		RAND_STATE          *rand               // IN: if not NULL, the deterministic
		//     RNG state
		)
{
    int                      keyBits = key->publicArea.parameters.rsaDetail.keyBits;
    int                      index;
    //
    if(KeyCacheLoaded(key, rand))
	{
	    for(index = 0; index < RSA_KEY_CACHE_ENTRIES; index++)
		{
		    if((s_rsaKeyCache[index].publicModulus.t.size * 8) == keyBits)
			{
			    key->publicArea.unique.rsa = s_rsaKeyCache[index].publicModulus;
			    key->sensitive.sensitive.rsa = s_rsaKeyCache[index].privatePrime;
			    key->privateExponent = s_rsaKeyCache[index].privateExponent;
			    key->attributes.privateExp = SET;
			    return TRUE;
			}
		}
	    return FALSE;
	}
    return s_keyCacheLoaded;
}
#endif  // defined SIMULATION && defined USE_RSA_KEY_CACHE
