/****************************************************************************
*																			*
*							Kernel Storage Regions							*
*						Copyright Peter Gutmann 1997-2025					*
*																			*
****************************************************************************/

#ifdef __STDC__
  #include <stddef.h>		/* For offsetof() */
#endif /* __STDC__ */
#include "crypt.h"

/* General storage includes */
#if defined( INC_ALL )
  #ifdef USE_CERTIFICATES
	#include "trustmgr_int.h"
  #endif /* USE_CERTIFICATES */
  #include "acl.h"
  #ifdef USE_TCP
	#include "tcp_int.h"
  #endif /* USE_TCP */
  #include "kernel.h"
  #include "user_int.h"
  #include "random_int.h"
  #ifdef USE_TLS
	#include "scorebrd_int.h"
  #endif /* USE_TLS */
#else
  #ifdef USE_CERTIFICATES
	#include "cert/trustmgr_int.h"		/* Trust information */
  #endif /* USE_CERTIFICATES */
  #ifdef USE_TCP
	#include "io/tcp_int.h"				/* Network socket pool */
  #endif /* USE_TCP */
  #include "kernel/acl.h"
  #include "kernel/kernel.h"
  #include "misc/user_int.h"
  #include "random/random_int.h"
  #ifdef USE_TLS
	#include "session/scorebrd_int.h"	/* Session scoreboard */
  #endif /* USE_TLS */
#endif /* Compiler-specific includes */

/* Object-specific storage includes */
#if defined( INC_ALL )
  #include "context.h"
  #include "aes.h"
  #include "gcm.h"
  #include "sha.h"
  #include "sha2.h"
  #include "device.h"
  #ifdef USE_KEYSETS
	#include "keyset.h"
  #endif /* USE_KEYSETS */
  #include "user.h"
#else
  #include "context/context.h"
  #include "crypt/aes.h"
  #include "crypt/gcm.h"
  #include "crypt/sha.h"
  #include "crypt/sha2.h"
  #include "device/device.h"
  #ifdef USE_KEYSETS
	#include "keyset/keyset.h"
  #endif /* USE_KEYSETS */
  #include "misc/user.h"
#endif /* Compiler-specific includes */

/* Define the following to print a trace of the alloc/free operations */

#if !defined( NDEBUG ) && 0
  #define TRACE_DIAG( message ) \
		  DEBUG_DIAG( message )
#else
  #define TRACE_DIAG( message )
#endif /* NDEBUG */

/****************************************************************************
*																			*
*							Object-specific Storage							*
*																			*
****************************************************************************/

/* Alongside the global kernel storage in storage.c, cryptlib also reserves 
   static storage space for object data.  This makes it possible to create a 
   given number of commonly-used objects without having to use dynamic 
   allocation.  
   
   In addition to the general-purpose object storage we also allocate room 
   for a device object for the system device and a user object for the 
   default user object, since these are allocated at init time they're 
   always assigned to the system device and default user.
   
   For example for SSH we need two AES contexts and two HMAC-SHA2 contexts, 
   for TLS we need two AES context, two SHA-2 contexts (for handshake 
   hashes), and two HMAC-SHA2 contexts, and for both we need a SHA-1 context 
   for legacy hashing before SHA-2 can be negotiated.  The full context list 
   is:

	Selftest for object creation: AES.

	SSH: SHA1 + SHA2 to hash the handshake.
		 (DH + RSA for keyex)
		 AES x 2 + HMAC-SHA2 x 2 for the session.

	TLS: MD5 + SHA1 + SHA2 to hash the handshake.
		 (DH + RSA for keyex, RSA for certs)
		 SHA2 for the keyex hash.
		   SHA2 clone for initiator vs.responder hash.
		 AES x 2 + HMAC-SHA2 x 2 for the session.

	Envelopes: AES + SHA1 + SHA2 + HMAC-SHA2 from sessions should cover it.

	Keysets: AES + HMAC-SHA2 from sessions should cover it.

   The following values define how many blocks of storage we reserve for each 
   context type */

#define NO_AES_CONTEXTS			2
#define NO_SHA1_CONTEXTS		1
#define NO_SHA2_CONTEXTS		2
#define NO_HMAC_SHA2_CONTEXTS	2

/* Device and user objects, to store the system object, default user object,
   and optional crypto object.  Since each object has subtype-specific 
   storage following it we also allocate a block of storage for the subtype 
   that follows the object storage which isn't accessed directly but 
   implicitly follows the object storage.
   
   Some compilers will warn about this because the extra storage is usually
   dynamically allocated at the end of the struct and don't know that the
   statically-placed value replaces this, so we disable the warning for this
   one instance.
   
   Non-clang versions of IBM's xlc also produce a warning, "1506-997 (W) 
   Structure members cannot follow a flexible array member/zero extent 
   array", but there's no way to disable it, "#pragma report( disable, 
   "1506-995" )" is C++ only and "#pragma info( none )" has no effect 
   because it doesn't apply to warnings */

#ifdef __clang__
  #pragma clang diagnostic ignored "-Wgnu-variable-sized-type-not-at-end"
#endif /* __clang__ */

typedef struct {
	DEVICE_INFO deviceInfo;
	SYSTEMDEV_INFO deviceInfoStorage;
	} SYSTEM_DEVICE_STORAGE;

typedef struct {
	USER_INFO userObjectInfo;
	} USER_OBJECT_STORAGE;

#if defined( CONFIG_CRYPTO_HW1 ) || defined( CONFIG_CRYPTO_HW2 )
typedef struct {
	DEVICE_INFO deviceInfo;
	HARDWARE_INFO deviceInfoStorage;
	} CRYPTO_DEVICE_STORAGE;
#endif /* CONFIG_CRYPTO_HW1 || CONFIG_CRYPTO_HW2 */

/* Keyset objects, to store file keysets.  If we're using a separate crypto 
   object then we need an extra keyset since the hardware device that
   implements it uses a keyset for backing storage */

#ifdef USE_KEYSETS
typedef struct {
	KEYSET_INFO keysetInfo;
	FILE_INFO fileInfoStorage;
	} KEYSET_STORAGE;
#endif /* USE_KEYSETS */

#if defined( CONFIG_CRYPTO_HW1 ) || defined( CONFIG_CRYPTO_HW2 )
  #define NO_KEYSET_OBJECTS	2
#else
  #define NO_KEYSET_OBJECTS	1
#endif /* CONFIG_CRYPTO_HW1 || CONFIG_CRYPTO_HW2 */

/* Context data alignment/padding specifiers, from cryptctx.c */

#define CONTEXT_INFO_ALIGN_SIZE	\
		roundUp( sizeof( CONTEXT_INFO ), CONTEXT_STORAGE_ALIGN_SIZE )

/* AES data alignment/padding specifiers, from ctx_aes.c */

#define AES_EKEY			aes_encrypt_ctx
#define AES_DKEY			aes_decrypt_ctx
#define AES_GCM_CTX			gcm_ctx
#define UNIT_SIZE			16
#define BYTE_SIZE( x )		( UNIT_SIZE * ( ( sizeof( x ) + UNIT_SIZE - 1 ) / UNIT_SIZE ) )
 #define KS_SIZE			( BYTE_SIZE( AES_EKEY ) + BYTE_SIZE( AES_DKEY ) + UNIT_SIZE )
typedef unsigned long _unit;
typedef struct {	
	_unit ksch[ ( KS_SIZE + sizeof( _unit ) - 1 ) / sizeof( _unit ) ];
	} AES_CTX;
#ifdef USE_GCM
  #define AES_KEYDATA_SIZE	( sizeof( AES_GCM_CTX ) + UNIT_SIZE )
#else
  #define AES_KEYDATA_SIZE	( sizeof( AES_CTX ) + UNIT_SIZE )
#endif /* USE_GCM */

/* SHA-1 data alignment/padding specifiers, from ctx_sha1.c */

#define SHA1_STATE_SIZE		sizeof( SHA_CTX )

/* SHA-2 data alignment/padding specifiers, from ctx_sha2.c */

#define SHA2_STATE_SIZE		sizeof( sha2_ctx )

/* HMAC-SHA2 data alignment/padding specifiers, from ctx_sha2.c */

typedef struct {
	sha2_ctx macState, initialMacState;
	} SHA2_MAC_STATE;
#define SHA2_MAC_STATE_SIZE		sizeof( SHA2_MAC_STATE )

/* Storage requirements for each context type.  There's no explicit subtype 
   alignment size included for AES since it uses its own alignment rather
   than the default CONTEXT_STORAGE_ALIGN_SIZE */

#define CONV_STORAGE( size ) \
		( CONTEXT_INFO_ALIGN_SIZE + sizeof( CONV_INFO ) + ( size ) )
#define HASH_STORAGE( size ) \
		( CONTEXT_INFO_ALIGN_SIZE + sizeof( HASH_INFO ) + ( size ) + CONTEXT_STORAGE_ALIGN_SIZE )
#define MAC_STORAGE( size ) \
		( CONTEXT_INFO_ALIGN_SIZE + sizeof( MAC_INFO ) + ( size ) + CONTEXT_STORAGE_ALIGN_SIZE )

typedef BYTE AES_STORAGE[ CONV_STORAGE( AES_KEYDATA_SIZE ) ];
typedef BYTE SHA1_STORAGE[ HASH_STORAGE( SHA1_STATE_SIZE ) ];
typedef BYTE SHA2_STORAGE[ HASH_STORAGE( SHA2_STATE_SIZE ) ];
typedef BYTE HMAC_SHA2_STORAGE[ MAC_STORAGE( SHA2_MAC_STATE_SIZE ) ];

/****************************************************************************
*																			*
*								Static Storage								*
*																			*
****************************************************************************/

/* The fixed storage block statically allocates data items in adjacent 
   memory locations, which means that we can use it like the safe buffer 
   functions by placing canaries between the items.  This detects off-by-one 
   array overruns and more general larger memory overwrites that eventually 
   hit one of the canaries.

   There are two types of data items in the fixed storage, a single 
   structure like the kernel data and an array of items like the object 
   table.  For the single structures we place a canary at the end of the 
   structure, this is however unlikely to catch anything because it's not an 
   array to run off the end of so at most it'll catch general corruption.  
   For the arrays we over-allocate by two entries and overwrite the first 
   over-allocated entry with canary data.

   In terms of how to do the canaries, for the lesser-used array items like 
   the socket pool, scoreboard, and configuration options we use larger 
   canaries because they're rarely checked, because the fields of the 
   structure stored in the array cover a considerable area so we can't just 
   use a CANARY_SIZE_SHORT canary, and because the storage is free as it's 
   part of the over-allocated array.

   For the more frequently-used items like the kernel data and object table 
   as well as single-structure items we use a quick-to-check/smaller
   CANARY_SIZE_SHORT canary, this covers initial fields like the object type 
   and optionally subtype which is sanity-checked so an attempt to write to 
   a field past the canary, that wouldn't be detected by the canary check, 
   will fail because the object won't pass its sanity check */

#define CANARY_SIZE_SHORT	SAFEBUFFER_COOKIE_SIZE
#define CANARY_SIZE			( 4 * SAFEBUFFER_COOKIE_SIZE )

typedef BYTE STORAGE_CANARY_DATA[ CANARY_SIZE_SHORT ];

static const BYTE canaryData[] = { 
	SAFEBUFFER_COOKIE_DATA, SAFEBUFFER_COOKIE_DATA, 
	SAFEBUFFER_COOKIE_DATA, SAFEBUFFER_COOKIE_DATA };

/* cryptlib uses a preset amount of fixed storage for kernel data structures
   and built-in objects, which can be allocated statically at compile time
   rather than dynamically.  The following structure contains this fixed 
   storage, consisting of the kernel data, the object table, and any other 
   fixed storage blocks that might be needed.  This is allocated in non-
   pageable storage if the underlying OS supports it, but the fact that it's 
   all co-located in a few constantly-accessed pages also greatly reduces 
   its chances of being paged out */

#ifdef _MSC_VER
  #pragma warning( push )
  #pragma warning( disable: 4324 )	/* Structure was padded to align */
#endif /* _MSC_VER */

typedef struct {
	/* The kernel data */
	ALIGN_STRUCT_FIELD KERNEL_DATA krnlData;
	const STORAGE_CANARY_DATA krnlDataCanary;

	/* The object table */
	ALIGN_STRUCT_FIELD OBJECT_INFO objectTable[ MAX_NO_OBJECTS + 2 ];
	
	/* The randomness information */
	ALIGN_STRUCT_FIELD RANDOM_INFO randomInfo;
	const STORAGE_CANARY_DATA randomInfoCanary;

	/* The certificate trust information */
#ifdef USE_CERTIFICATES
	ALIGN_STRUCT_FIELD TRUST_INFO_CONTAINER trustInfoContainer;	
	const STORAGE_CANARY_DATA trustInfoContainerCanary;
#endif /* USE_CERTIFICATES */

	/* The network socket pool */
#ifdef USE_TCP
	ALIGN_STRUCT_FIELD SOCKET_INFO socketInfo[ SOCKETPOOL_SIZE + 2 ];
#endif /* USE_TCP */

	/* The session scoreboard */
#ifdef USE_TLS
	ALIGN_STRUCT_FIELD SCOREBOARD_INFO scoreboardInfo;
	const STORAGE_CANARY_DATA scoreboardInfoCanary;
#endif /* USE_TLS */

	/* The config option information */
	ALIGN_STRUCT_FIELD OPTION_INFO optionInfo[ OPTION_INFO_COUNT + 2 ];

	/* Object-specific storage.  We have to individually align each structure
	   rather than just using 'X_STORAGE xStorage[ NO_ELEMENTS ]' because 
	   some compilers on some architectures will only align the first element 
	   of the array, not every element in it.  An example is older clang on 
	   MIPS64 which 64-bit aligns the first element but only 32-bit aligns the
	   successor ones */
	ALIGN_STRUCT_FIELD SYSTEM_DEVICE_STORAGE systemDeviceStorage;
	BOOLEAN systemDeviceStorageUsed;
	ALIGN_STRUCT_FIELD USER_OBJECT_STORAGE userObjectStorage;
	BOOLEAN userObjectStorageUsed;
#if defined( CONFIG_CRYPTO_HW1 ) || defined( CONFIG_CRYPTO_HW2 )
	ALIGN_STRUCT_FIELD \
	CRYPTO_DEVICE_STORAGE cryptoDeviceStorage;
	BOOLEAN cryptoDeviceStorageUsed;
#endif /* CONFIG_CRYPTO_HW1 || CONFIG_CRYPTO_HW2 */
#ifdef USE_KEYSETS
	ALIGN_STRUCT_FIELD KEYSET_STORAGE keysetStorage0;
  #if NO_KEYSET_OBJECTS > 1
	ALIGN_STRUCT_FIELD KEYSET_STORAGE keysetStorage1;
  #endif /* NO_KEYSET_OBJECTS > 1 */
	BOOLEAN keysetStorageUsed[ NO_KEYSET_OBJECTS ];
#endif /* USE_KEYSETS */
	ALIGN_STRUCT_FIELD AES_STORAGE aesStorage0;
	ALIGN_STRUCT_FIELD AES_STORAGE aesStorage1;
	BOOLEAN aesStorageUsed[ NO_AES_CONTEXTS ];
	ALIGN_STRUCT_FIELD SHA1_STORAGE sha1Storage;
	BOOLEAN sha1StorageUsed[ NO_SHA1_CONTEXTS ];
	ALIGN_STRUCT_FIELD SHA2_STORAGE sha2Storage0;
	ALIGN_STRUCT_FIELD SHA2_STORAGE sha2Storage1;
	BOOLEAN sha2StorageUsed[ NO_SHA2_CONTEXTS ];
	ALIGN_STRUCT_FIELD HMAC_SHA2_STORAGE hmacSha2Storage0;
	ALIGN_STRUCT_FIELD HMAC_SHA2_STORAGE hmacSha2Storage1;
	BOOLEAN hmacSha2StorageUsed[ NO_HMAC_SHA2_CONTEXTS ];
	} STORAGE_STRUCT;

static STORAGE_STRUCT systemStorage = {
	{ 0 }, { SAFEBUFFER_COOKIE_DATA },		/* Kernel data */
	{ 0 },									/* Object table */
	{ 0 }, { SAFEBUFFER_COOKIE_DATA },		/* Random info */
#ifdef USE_CERTIFICATES
	{ 0 }, { SAFEBUFFER_COOKIE_DATA },		/* Trust info */
#endif /* USE_CERTIFICATES */
#ifdef USE_TCP
	{ 0 },									/* Socket pool */
#endif /* USE_TCP */
#ifdef USE_TLS
	{ 0 }, { SAFEBUFFER_COOKIE_DATA },		/* Scoreboard */
#endif /* USE_TLS */
	{ 0 }									/* Option info */
	};

#ifdef _MSC_VER
  #pragma warning( pop )
#endif /* _MSC_VER */

/****************************************************************************
*																			*
*							Static Storage Management						*
*																			*
****************************************************************************/

/* Initialise and destroy the built-in storage info */

void initBuiltinStorage( void )
	{
	( void ) lockMemory( &systemStorage, sizeof( STORAGE_STRUCT ) );

	/* Make sure that the canaries will fit into the array data fields.  
	   The OBJECT_INFO is relatively large but for the smaller SOCKET_INFO 
	   and OPTION_INFO items the canary covers both of the two over-
	   allocated array entries */
	static_assert( sizeof( OBJECT_INFO ) >= 32,
				   "OBJECT_INFO is too small to fit the canary" );
	static_assert( ( 2 * sizeof( SOCKET_INFO ) ) >= 32,
				   "SOCKET_INFO is too small to fit the canary" );
	static_assert( ( 2 * sizeof( OPTION_INFO ) ) >= 32,
				   "OPTION_INFO is too small to fit the canary" );

	/* Set up the canaries for the array data.  Note that we set the object
	   table canary to the full-size value even though we only check the 
	   short form to make sure that what's there is obviously invalid data 
	   if it's ever read */
	memcpy( &systemStorage.objectTable[ MAX_NO_OBJECTS ],
			canaryData, CANARY_SIZE );
#ifdef USE_TCP
	memcpy( &systemStorage.socketInfo[ SOCKETPOOL_SIZE ],
			canaryData, CANARY_SIZE );
#endif /* USE_TCP */
	memcpy( &systemStorage.optionInfo[ OPTION_INFO_COUNT ],
			canaryData, CANARY_SIZE );

	/* Some of the fields in structures within the built-in storage block
	   need to be aligned to CPU-specific boundaries for CPUs that prefer
	   aligned accesses.  This is handled through the ALIGN_STRUCT_FIELD 
	   macro, in the debug build we perform a check that the fields are 
	   indeed aligned */
	assert( ALIGN_FIELD_CHECK( &systemStorage.krnlData ) );
	assert( ALIGN_FIELD_CHECK( &systemStorage.objectTable ) );
	assert( ALIGN_FIELD_CHECK( &systemStorage.randomInfo ) );
	assert( ALIGN_FIELD_CHECK( &systemStorage.systemDeviceStorage ) );
	assert( ALIGN_FIELD_CHECK( &systemStorage.userObjectStorage ) );
#if defined( CONFIG_CRYPTO_HW1 ) || defined( CONFIG_CRYPTO_HW2 )
	assert( ALIGN_FIELD_CHECK( &systemStorage.cryptoDeviceStorage ) );
#endif /* CONFIG_CRYPTO_HW1 || CONFIG_CRYPTO_HW2 */
#ifdef USE_KEYSETS
	assert( ALIGN_FIELD_CHECK( &systemStorage.keysetStorage0 ) );
#endif /* USE_KEYSETS */
	assert( ALIGN_FIELD_CHECK( &systemStorage.aesStorage0 ) );
	assert( ALIGN_FIELD_CHECK( &systemStorage.aesStorage1 ) );
	assert( ALIGN_FIELD_CHECK( &systemStorage.sha1Storage ) );
	assert( ALIGN_FIELD_CHECK( &systemStorage.sha2Storage0 ) );
	assert( ALIGN_FIELD_CHECK( &systemStorage.sha2Storage1 ) );
	assert( ALIGN_FIELD_CHECK( &systemStorage.hmacSha2Storage0 ) );
	assert( ALIGN_FIELD_CHECK( &systemStorage.hmacSha2Storage1 ) );

	/* Check that the canaries are alive */
	assert( !memcmp( systemStorage.krnlDataCanary, canaryData, 
					 CANARY_SIZE_SHORT ) );
	assert( !memcmp( &systemStorage.objectTable[ MAX_NO_OBJECTS ],
					 canaryData, CANARY_SIZE ) );
	assert( !memcmp( systemStorage.randomInfoCanary,
					 canaryData, CANARY_SIZE_SHORT ) );
#ifdef USE_CERTIFICATES
	assert( !memcmp( systemStorage.trustInfoContainerCanary,
					 canaryData, CANARY_SIZE_SHORT ) );
#endif /* USE_CERTIFICATES */
#ifdef USE_TCP
	assert( !memcmp( &systemStorage.socketInfo[ SOCKETPOOL_SIZE ],
					 canaryData, CANARY_SIZE ) );
#endif /* USE_TCP */
#ifdef USE_TLS
	assert( !memcmp( systemStorage.scoreboardInfoCanary,
					 canaryData, CANARY_SIZE_SHORT ) );
#endif /* USE_TLS */
	assert( !memcmp( &systemStorage.optionInfo[ OPTION_INFO_COUNT ],
					 canaryData, CANARY_SIZE ) );
	}

void destroyBuiltinStorage( void )
	{
	/* The following memory-clear can trigger warnings from code-analysis
	   tools, there's no easy way around this because the struct contains a
	   few const fields, these are required to be const because they should
	   never be written but this does introduce enough const-ness to parts
	   of the struct for tools to warn about it */
	zeroise( &systemStorage, sizeof( STORAGE_STRUCT ) );
	unlockMemory( &systemStorage, sizeof( STORAGE_STRUCT ), FALSE );
	}

/* When we start up and shut down the kernel, we need to clear the kernel
   data.  However, the init lock may have been set by an external management
   function, so we can't clear that part of the kernel data.  In addition,
   on shutdown the shutdown level value must stay set so that any threads
   still running will be forced to exit at the earliest possible instance,
   and remain set after the shutdown has completed.  To handle this, we use
   the following macro to clear only the appropriate area of the kernel data
   block */

void clearKernelData( void )
	{
	KERNEL_DATA *krnlDataPtr = &systemStorage.krnlData;

#if defined( __STDC__ )
	zeroise( ( BYTE * ) krnlDataPtr + offsetof( KERNEL_DATA, initLevel ), 
			 sizeof( KERNEL_DATA ) - offsetof( KERNEL_DATA, initLevel ) );
#else
	assert( &krnlDataPtr->endMarker - &krnlDataPtr->initLevel < sizeof( KERNEL_DATA ) ); 
	zeroise( ( void * ) &krnlDataPtr->initLevel, 
			 &krnlDataPtr->endMarker - &krnlDataPtr->initLevel );
#endif /* C89 compilers */
	}

/* Access functions for the built-in storage, the first for kernel-internal 
   storage, the second for general storage */

void *getSystemStorage( IN_ENUM( BUILTIN_STORAGE ) \
							const BUILTIN_STORAGE_TYPE storageType )
	{
	REQUIRES_N( isEnumRange( storageType, BUILTIN_STORAGE ) );

	switch( storageType )
		{
		case SYSTEM_STORAGE_KRNLDATA:
			return( &systemStorage.krnlData );

		case SYSTEM_STORAGE_OBJECT_TABLE:
			return( systemStorage.objectTable );

		default:
			retIntError_Null();
		}

	retIntError_Null();
	}

void *getBuiltinStorage( IN_ENUM( BUILTIN_STORAGE ) \
							const BUILTIN_STORAGE_TYPE storageType )
	{
	REQUIRES_N( isEnumRange( storageType, BUILTIN_STORAGE ) );

	switch( storageType )
		{
		case BUILTIN_STORAGE_RANDOM_INFO:
			return( &systemStorage.randomInfo );

#ifdef USE_CERTIFICATES
		case BUILTIN_STORAGE_TRUSTMGR:
			return( &systemStorage.trustInfoContainer );
#endif /* USE_CERTIFICATES */

#ifdef USE_TCP
		case BUILTIN_STORAGE_SOCKET_POOL:
			return( &systemStorage.socketInfo );
#endif /* USE_TCP */

#ifdef USE_TLS
		case BUILTIN_STORAGE_SCOREBOARD:
			return( &systemStorage.scoreboardInfo );
#endif /* USE_TLS */

		case BUILTIN_STORAGE_OPTION_INFO:
			return( &systemStorage.optionInfo );
		
		default:
			retIntError_Null();
		}

	retIntError_Null();
	}

/* Check the canary for a storage type, used after working with that storage
   block to make sure that we haven't overrun the block */

CHECK_RETVAL_BOOL \
BOOLEAN checkBuiltinStorage( IN_ENUM( BUILTIN_STORAGE ) \
								const BUILTIN_STORAGE_TYPE storageType )
	{
	REQUIRES_B( isEnumRange( storageType, BUILTIN_STORAGE ) );

	switch( storageType )
		{
		case SYSTEM_STORAGE_KRNLDATA:
			return( !memcmp( systemStorage.krnlDataCanary, canaryData,
							 CANARY_SIZE_SHORT ) ? TRUE : FALSE );
			#define NEXT_CANARY_NAME	systemStorage.krnlDataCanary

		case SYSTEM_STORAGE_OBJECT_TABLE:
			return( !memcmp( NEXT_CANARY_NAME, canaryData,
							 CANARY_SIZE_SHORT ) && \
					!memcmp( &systemStorage.objectTable[ MAX_NO_OBJECTS ],
							 canaryData, CANARY_SIZE_SHORT ) ? \
					TRUE : FALSE );
			#undef NEXT_CANARY_NAME
			#define NEXT_CANARY_NAME	&systemStorage.objectTable[ MAX_NO_OBJECTS ]

		case BUILTIN_STORAGE_RANDOM_INFO:
			return( !memcmp( NEXT_CANARY_NAME, canaryData,
							 CANARY_SIZE_SHORT ) && \
					!memcmp( systemStorage.randomInfoCanary,
							 canaryData, CANARY_SIZE_SHORT ) ? \
					TRUE : FALSE );
			#undef NEXT_CANARY_NAME
			#define NEXT_CANARY_NAME	systemStorage.randomInfoCanary
			#define NEXT_CANARY_SIZE	CANARY_SIZE_SHORT
			
#ifdef USE_CERTIFICATES
		case BUILTIN_STORAGE_TRUSTMGR:
			return( !memcmp( NEXT_CANARY_NAME, canaryData,
							 NEXT_CANARY_SIZE ) && \
					!memcmp( systemStorage.trustInfoContainerCanary,
							 canaryData, CANARY_SIZE_SHORT ) ? \
					TRUE : FALSE );
			#undef NEXT_CANARY_NAME
			#define NEXT_CANARY_NAME	systemStorage.trustInfoContainerCanary
#endif /* USE_CERTIFICATES */

#ifdef USE_TCP
		case BUILTIN_STORAGE_SOCKET_POOL:
			return( !memcmp( NEXT_CANARY_NAME, canaryData,
							 NEXT_CANARY_SIZE ) && \
					!memcmp( &systemStorage.socketInfo[ SOCKETPOOL_SIZE ],
							 canaryData, CANARY_SIZE ) ? \
					TRUE : FALSE );
			#undef NEXT_CANARY_NAME
			#define NEXT_CANARY_NAME	&systemStorage.socketInfo[ SOCKETPOOL_SIZE ]
			#undef NEXT_CANARY_SIZE
			#define NEXT_CANARY_SIZE	CANARY_SIZE
#endif /* USE_TCP */

#ifdef USE_TLS
		case BUILTIN_STORAGE_SCOREBOARD:
			return( !memcmp( NEXT_CANARY_NAME, canaryData,
							 NEXT_CANARY_SIZE ) && \
					!memcmp( systemStorage.scoreboardInfoCanary,
							 canaryData, CANARY_SIZE_SHORT ) ? \
					TRUE : FALSE );
			#undef NEXT_CANARY_NAME
			#define NEXT_CANARY_NAME	systemStorage.scoreboardInfoCanary
			#undef NEXT_CANARY_SIZE
			#define NEXT_CANARY_SIZE	CANARY_SIZE_SHORT
#endif /* USE_TLS */

		case BUILTIN_STORAGE_OPTION_INFO:
			return( !memcmp( NEXT_CANARY_NAME, canaryData,
							 NEXT_CANARY_SIZE ) && \
					!memcmp( &systemStorage.optionInfo[ OPTION_INFO_COUNT ],
							 canaryData, CANARY_SIZE ) ? \
					TRUE : FALSE );

		default:
			retIntError_Boolean();
		}

	retIntError_Boolean();
	}

/* Obtain and release context-specific storage from the built-in fixed 
   storage block.  Note that this function must be called with the 
   allocation mutex held */

CHECK_RETVAL_PTR \
void *getBuiltinObjectStorage( IN_ENUM( OBJECT_TYPE ) const OBJECT_TYPE type,
							   IN_ENUM( SUBTYPE ) const OBJECT_SUBTYPE subType,
							   IN_LENGTH_MIN( 32 ) const int size )
	{
	REQUIRES_N( isValidType( type ) );
	REQUIRES_N( subType > SUBTYPE_NONE && subType <= SUBTYPE_LAST );
	REQUIRES_N( isBufsizeRangeMin( size, 32 ) );

	/* There's a small but nonzero chance that the storage sizes of two 
	   context subtype objects are the same, the following checks test for 
	   this */
	static_assert( HASH_STORAGE( SHA1_STATE_SIZE ) != HASH_STORAGE( SHA2_STATE_SIZE ),
				   "SHA1/SHA2 storage" );

	switch( type )
		{
		case OBJECT_TYPE_DEVICE:
			if( subType == SUBTYPE_DEV_SYSTEM )
				{
				if( !systemStorage.systemDeviceStorageUsed )
					{
					TRACE_DIAG(( "Allocated static system device object" ));
					systemStorage.systemDeviceStorageUsed = TRUE;
					return( &systemStorage.systemDeviceStorage );
					}

				/* Since there should only be one system device, a failure 
				   to create it, meaning that it already exists, is an 
				   error */
				retIntError_Null();
				}
#if defined( CONFIG_CRYPTO_HW1 ) || defined( CONFIG_CRYPTO_HW2 )
			if( subType == SUBTYPE_DEV_HARDWARE )
				{
				if( !systemStorage.cryptoDeviceStorageUsed )
					{
					TRACE_DIAG(( "Allocated static crypto device object" ));
					systemStorage.cryptoDeviceStorageUsed = TRUE;
					return( &systemStorage.cryptoDeviceStorage );
					}

				/* Since there should only be one crypto device, a failure 
				   to create it, meaning that it already exists, is an 
				   error */
				retIntError_Null();
				}
#endif /* CONFIG_CRYPTO_HW1 || CONFIG_CRYPTO_HW2 */
			break;

		case OBJECT_TYPE_USER:
			if( subType == SUBTYPE_USER_SO )
				{
				if( !systemStorage.userObjectStorageUsed )
					{
					TRACE_DIAG(( "Allocated static user object" ));
					systemStorage.userObjectStorageUsed = TRUE;
					return( &systemStorage.userObjectStorage );
					}
				}
			break;

#ifdef USE_KEYSETS
		case OBJECT_TYPE_KEYSET:
			if( subType == SUBTYPE_KEYSET_FILE )
				{
  #if NO_KEYSET_OBJECTS > 1
				const int index = \
						!systemStorage.keysetStorageUsed[ 0 ] ? 0 : \
						!systemStorage.keysetStorageUsed[ 1 ] ? 1 : \
						CRYPT_ERROR;

				if( index == CRYPT_ERROR )
					break;
				TRACE_DIAG(( "Allocated static file keyset object #%d", 
							 index ));
				systemStorage.keysetStorageUsed[ index ] = TRUE;
				return( ( index == 0 ) ? &systemStorage.keysetStorage0 : \
										 &systemStorage.keysetStorage1 );
  #else
				if( systemStorage.keysetStorageUsed[ 0 ] )
					break;
				systemStorage.keysetStorageUsed[ 0 ] = TRUE;
				TRACE_DIAG(( "Allocated static file keyset object" ));
				return( &systemStorage.keysetStorage0 );
  #endif /* NO_KEYSET_OBJECTS > 1 */
				}
			break;
#endif /* USE_KEYSETS */

		case OBJECT_TYPE_CONTEXT:
			if( subType == SUBTYPE_CTX_CONV )
				{
				if( size == CONV_STORAGE( AES_KEYDATA_SIZE ) )
					{
					const int index = \
						!systemStorage.aesStorageUsed[ 0 ] ? 0 : \
						!systemStorage.aesStorageUsed[ 1 ] ? 1 : \
						CRYPT_ERROR;
					
					if( index == CRYPT_ERROR )
						break;
					TRACE_DIAG(( "Allocated static AES object #%d", index ));
					systemStorage.aesStorageUsed[ index ] = TRUE;
					return( ( index == 0 ) ? &systemStorage.aesStorage0 : \
											 &systemStorage.aesStorage1 );
					}
				break;
				}
			if( subType == SUBTYPE_CTX_HASH )
				{
				if( size == HASH_STORAGE( SHA1_STATE_SIZE ) )
					{
					if( !systemStorage.sha1StorageUsed[ 0 ] )
						{
						TRACE_DIAG(( "Allocated static SHA1 object" ));
						systemStorage.sha1StorageUsed[ 0 ] = TRUE;
						return( &systemStorage.sha1Storage );
						}
					}
				if( size == HASH_STORAGE( SHA2_STATE_SIZE ) )
					{
					const int index = \
						!systemStorage.sha2StorageUsed[ 0 ] ? 0 : \
						!systemStorage.sha2StorageUsed[ 1 ] ? 1 : \
						CRYPT_ERROR;
					
					if( index == CRYPT_ERROR )
						break;
					TRACE_DIAG(( "Allocated static SHA2 object #%d", index ));
					systemStorage.sha2StorageUsed[ index ] = TRUE;
					return( ( index == 0 ) ? &systemStorage.sha2Storage0 : \
											 &systemStorage.sha2Storage1 );
					}
				break;
				}
			if( subType == SUBTYPE_CTX_MAC )
				{
				if( size == MAC_STORAGE( SHA2_MAC_STATE_SIZE ) )
					{
					const int index = \
						!systemStorage.hmacSha2StorageUsed[ 0 ] ? 0 : \
						!systemStorage.hmacSha2StorageUsed[ 1 ] ? 1 : \
						CRYPT_ERROR;
					
					if( index == CRYPT_ERROR )
						break;
					TRACE_DIAG(( "Allocated static HMAC-SHA2 object #%d", index ));
					systemStorage.hmacSha2StorageUsed[ index ] = TRUE;
					return( ( index == 0 ) ? &systemStorage.hmacSha2Storage0 : \
											 &systemStorage.hmacSha2Storage1 );
					}
				break;
				}
			break;

		default:
			/* It's a type for which there's no static storage allocated, 
			   let the caller allocate it */
			return( NULL );
		}

	/* It's a type for which there's no static storage allocated or all 
	   static storage blocks for this type and subtype have been allocated, 
	   let the caller allocate it */
	return( NULL );
	}

CHECK_RETVAL STDC_NONNULL_ARG( ( 3 ) ) \
int releaseBuiltinObjectStorage( IN_ENUM( OBJECT_TYPE ) const OBJECT_TYPE type,
								 IN_ENUM( SUBTYPE ) const OBJECT_SUBTYPE subType,
								 const void *address )
	{
	assert( isReadPtr( address, 16 ) );

	REQUIRES( isValidType( type ) );
	REQUIRES( subType > SUBTYPE_NONE && subType <= SUBTYPE_LAST );

	switch( type )
		{
		case OBJECT_TYPE_DEVICE:
			if( subType == SUBTYPE_DEV_SYSTEM )
				{
				if( address == &systemStorage.systemDeviceStorage )
					{
					ENSURES( systemStorage.systemDeviceStorageUsed == TRUE );
					TRACE_DIAG(( "Freed static system device object" ));
					systemStorage.systemDeviceStorageUsed = FALSE;
					return( CRYPT_OK );
					}
				}
#if defined( CONFIG_CRYPTO_HW1 ) || defined( CONFIG_CRYPTO_HW2 )
			if( subType == SUBTYPE_DEV_HARDWARE )
				{
				if( address == &systemStorage.cryptoDeviceStorage )
					{
					ENSURES( systemStorage.cryptoDeviceStorageUsed == TRUE );
					TRACE_DIAG(( "Freed static crypto device object" ));
					systemStorage.cryptoDeviceStorageUsed = FALSE;
					return( CRYPT_OK );
					}
				}
#endif /* CONFIG_CRYPTO_HW1 || CONFIG_CRYPTO_HW2 */
			break;

		case OBJECT_TYPE_USER:
			if( subType == SUBTYPE_USER_SO )
				{
				if( address == &systemStorage.userObjectStorage )
					{
					ENSURES( systemStorage.userObjectStorageUsed == TRUE );
					TRACE_DIAG(( "Freed static user object" ));
					systemStorage.userObjectStorageUsed = FALSE;
					return( CRYPT_OK );
					}
				}
			break;

#ifdef USE_KEYSETS
		case OBJECT_TYPE_KEYSET:
			if( subType == SUBTYPE_KEYSET_FILE )
				{
  #if NO_KEYSET_OBJECTS > 1
				const int index = \
						( address == &systemStorage.keysetStorage0 ) ? 0 : \
						( address == &systemStorage.keysetStorage1 ) ? 1 : \
						CRYPT_ERROR;
					
				if( index == CRYPT_ERROR )
					break;
				ENSURES( systemStorage.keysetStorageUsed[ index ] == TRUE );
				TRACE_DIAG(( "Freed static file keyset object #%d", index ));
				systemStorage.keysetStorageUsed[ index ] = FALSE;
				return( CRYPT_OK );
  #else
				if( address == &systemStorage.keysetStorage0 )
					{
					ENSURES( systemStorage.keysetStorageUsed[ 0 ] == TRUE );
					TRACE_DIAG(( "Freed static file keyset object" ));
					systemStorage.keysetStorageUsed[ 0 ] = FALSE;
					return( CRYPT_OK );
					}
  #endif /* NO_KEYSET_OBJECTS > 1 */
				}
			break;
#endif /* USE_KEYSETS */

		case OBJECT_TYPE_CONTEXT:
			if( subType == SUBTYPE_CTX_CONV )
				{
				const int index = \
						( address == &systemStorage.aesStorage0 ) ? 0 : \
						( address == &systemStorage.aesStorage1 ) ? 1 : \
						CRYPT_ERROR;
					
				if( index == CRYPT_ERROR )
					break;
				ENSURES( systemStorage.aesStorageUsed[ index ] == TRUE );
				TRACE_DIAG(( "Freed static AES object #%d", index ));
				systemStorage.aesStorageUsed[ index ] = FALSE;
				return( CRYPT_OK );
				}
			if( subType == SUBTYPE_CTX_HASH )
				{
				const int index = \
						( address == &systemStorage.sha2Storage0 ) ? 0 : \
						( address == &systemStorage.sha2Storage1 ) ? 1 : \
						CRYPT_ERROR;

				/* For the hash contexts we don't have any information beyond
				   the subtype, but at this point we can identify what's what 
				   based on the memory address */
				if( address == &systemStorage.sha1Storage )
					{
					ENSURES( systemStorage.sha1StorageUsed[ 0 ] == TRUE );
					TRACE_DIAG(( "Freed static SHA1 object" ));
					systemStorage.sha1StorageUsed[ 0 ] = FALSE;
					return( CRYPT_OK );
					}
				if( index == CRYPT_ERROR )
					break;
				ENSURES( systemStorage.sha2StorageUsed[ index ] == TRUE );
				TRACE_DIAG(( "Freed static SHA2 object #%d", index ));
				systemStorage.sha2StorageUsed[ index ] = FALSE;
				return( CRYPT_OK );
				}
			if( subType == SUBTYPE_CTX_MAC )
				{
				const int index = \
						( address == &systemStorage.hmacSha2Storage0 ) ? 0 : \
						( address == &systemStorage.hmacSha2Storage1 ) ? 1 : \
						CRYPT_ERROR;
					
				if( index == CRYPT_ERROR )
					break;
				ENSURES( systemStorage.hmacSha2StorageUsed[ index ] == TRUE );
				TRACE_DIAG(( "Freed static HMAC-SHA2 object #%d", index ));
				systemStorage.hmacSha2StorageUsed[ index ] = FALSE;
				return( CRYPT_OK );
				}
			break;

		default:
			retIntError();
		}

	retIntError();
	}

/* Helper functions used when debugging.  These return the sizes of the 
   various data structures for use with fault-injection testing */

#ifndef NDEBUG

int getSystemStorageSize( IN_ENUM( BUILTIN_STORAGE ) \
								const BUILTIN_STORAGE_TYPE storageType )
	{
	REQUIRES( isEnumRange( storageType, BUILTIN_STORAGE ) );

	switch( storageType )
		{
		case SYSTEM_STORAGE_KRNLDATA:
			return( sizeof( KERNEL_DATA ) );

		case SYSTEM_STORAGE_OBJECT_TABLE:
			return( sizeof( OBJECT_INFO ) * MAX_NO_OBJECTS );

		default:
			retIntError();
		}

	retIntError();
	}

int getBuiltinStorageSize( IN_ENUM( BUILTIN_STORAGE ) \
								const BUILTIN_STORAGE_TYPE storageType )
	{
	REQUIRES( isEnumRange( storageType, BUILTIN_STORAGE ) );

	switch( storageType )
		{
		case BUILTIN_STORAGE_RANDOM_INFO:
			return( sizeof( RANDOM_INFO ) );

#ifdef USE_CERTIFICATES
		case BUILTIN_STORAGE_TRUSTMGR:
			return( sizeof( TRUST_INFO_CONTAINER ) );
#endif /* USE_CERTIFICATES */

#ifdef USE_TCP
		case BUILTIN_STORAGE_SOCKET_POOL:
			return( sizeof( SOCKET_INFO ) * SOCKETPOOL_SIZE );
#endif /* USE_TCP */

#ifdef USE_TLS
		case BUILTIN_STORAGE_SCOREBOARD:
			return( sizeof( SCOREBOARD_INFO ) );
#endif /* USE_TLS */

		case BUILTIN_STORAGE_OPTION_INFO:
			return( OPTION_INFO_COUNT * sizeof( OPTION_INFO ) );
		
		default:
			retIntError();
		}

	retIntError();
	}

int getBuiltinObjectStorageSize( IN_ENUM( OBJECT_TYPE ) \
									const OBJECT_TYPE type,
								 IN_ENUM( SUBTYPE ) \
									const OBJECT_SUBTYPE subType,
								 IN_LENGTH_MIN( 32 ) const int size )
	{
	REQUIRES( isValidType( type ) );
	REQUIRES( subType > SUBTYPE_NONE && subType <= SUBTYPE_LAST );
	REQUIRES( isBufsizeRangeMin( size, 32 ) );

	switch( type )
		{
		case OBJECT_TYPE_DEVICE:
			if( subType == SUBTYPE_DEV_SYSTEM )
				return( sizeof( SYSTEM_DEVICE_STORAGE ) ); 
#if defined( CONFIG_CRYPTO_HW1 ) || defined( CONFIG_CRYPTO_HW2 )
			if( subType == SUBTYPE_DEV_HARDWARE )
				return( sizeof( CRYPTO_DEVICE_STORAGE ) ); 
#endif /* CONFIG_CRYPTO_HW1 || CONFIG_CRYPTO_HW2 */
			break;

		case OBJECT_TYPE_USER:
			return( sizeof( USER_OBJECT_STORAGE ) );

#ifdef USE_KEYSETS
		case OBJECT_TYPE_KEYSET:
			return( sizeof( KEYSET_STORAGE ) );
#endif /* USE_KEYSETS */

		case OBJECT_TYPE_CONTEXT:
			if( subType == SUBTYPE_CTX_CONV )
				return( sizeof( AES_STORAGE ) );
			if( subType == SUBTYPE_CTX_HASH )
				{
				if( size == HASH_STORAGE( SHA1_STATE_SIZE ) )
					return( sizeof( SHA1_STORAGE ) );
				else
					return( sizeof( SHA2_STORAGE ) );
				}
			if( subType == SUBTYPE_CTX_MAC )
				return( sizeof( HMAC_SHA2_STORAGE ) );
			break;

		default:
			retIntError();
		}

	retIntError();
	}
#endif /* !NDEBUG */
