/****************************************************************************
*																			*
*							cryptlib CryptoAPI Routines						*
*						Copyright Peter Gutmann 1998-2006					*
*																			*
****************************************************************************/

/* The following code is purely a test framework used to test the ability to
   work with CryptoAPI keys.  Much of the code is only present as a rough
   sketch.  It's not part of cryptlib, and shouldn't be used as a cryptlib
   component */

#if defined( INC_ALL )
  #include "crypt.h"
  #include "context.h"
  #include "device.h"
  #include "asn1.h"
  #include "asn1_ext.h"
#else
  #include "crypt.h"
  #include "context/context.h"
  #include "device/device.h"
  #include "misc/asn1.h"
  #include "misc/asn1_ext.h"
#endif /* Compiler-specific includes */

/* The size of the (packed) header used for key blobs */

#define BLOBHEADER_SIZE			8

/* Occasionally we need to read things into host memory from a device in a
   manner that can't be handled by a dynBuf since the data is coming from a
   device rather than a cryptlib object.  The following value defines the 
   maximum size of the on-stack buffer, if the data is larger than this we 
   dynamically allocate the buffer (this almost never occurs) */

#define MAX_BUFFER_SIZE			1024

/* Prototypes for functions in cryptcap.c */

const void FAR_BSS *findCapabilityInfo( const void FAR_BSS *capabilityInfoPtr,
										const CRYPT_ALGO_TYPE cryptAlgo );

#ifdef USE_CRYPTOAPI

/* The following define is needed to enable crypto functions in the include
   file.  This would probably be defined by the compiler since it's not 
   defined in any header file, but it doesn't seem to be enabled by 
   default */

#ifndef _WIN32_WINNT
  #define _WIN32_WINNT	0x0500
#endif /* _WIN32_WINNT */

/* cryptlib.h includes a trap for inclusion of wincrypt.h before cryptlib.h
   which results in a compiler error if both files are included.  To disable 
   this, we need to undefine the CRYPT_MODE_ECB defined in cryptlib.h */

#undef CRYPT_MODE_ECB

#include <wincrypt.h>

/* CryptoAPI uses the same mode names as cryptlib but different values, 
   fortunately this is done with #defines so we can remove them at this
   point */

#undef CRYPT_MODE_ECB
#undef CRYPT_MODE_CBC
#undef CRYPT_MODE_CFB
#undef CRYPT_MODE_OFB

/* Some parts of CryptoAPI (inconsistently) require the use of Unicode,
   winnls.h was already included via the global include of windows.h however
   it isn't needed for any other part of cryptlib so it was disabled via
   NONLS.  Since winnls.h is now locked out, we have to un-define the guards
   used earlier to get it included */

#undef _WINNLS_
#undef NONLS
#include <winnls.h>

/* Older versions of wincrypt.h don't contain defines and typedefs that we
   require.  Defines can be detected with #ifdef but typedefs can't, to 
   handle this we rely on checking for values that aren't defined in older 
   versions of wincrypt.h, which only go up to KP_PUB_EX_VAL */

#ifndef KP_ADMIN_PIN
  /* Misc values */
  #define HCERTCHAINENGINE				void *
  #define USAGE_MATCH_TYPE_AND			0
  #define USAGE_MATCH_TYPE_OR			1
  #define CERT_COMPARE_KEY_IDENTIFIER	15
  #define CERT_FIND_KEY_IDENTIFIER		( CERT_COMPARE_KEY_IDENTIFIER << CERT_COMPARE_SHIFT )
  #define CERT_CHAIN_CACHE_END_CERT					0x00000001
  #define CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY	0x80000000
  #define CRYPT_ACQUIRE_CACHE_FLAG					0x00000001

  /* Cert chain match info */
  typedef struct {
	DWORD dwType;
	CERT_ENHKEY_USAGE Usage;
	} CERT_USAGE_MATCH;
  typedef struct {
	DWORD cbSize;
	CERT_USAGE_MATCH RequestedUsage;
	CERT_USAGE_MATCH RequestedIssuancePolicy;
	DWORD dwUrlRetrievalTimeout;
	BOOL fCheckRevocationFreshnessTime;
	DWORD dwRevocationFreshnessTime;
	} CERT_CHAIN_PARA, *PCERT_CHAIN_PARA;

  /* Cert chain info */
  typedef struct {
	DWORD dwErrorStatus;
	DWORD dwInfoStatus;
	} CERT_TRUST_STATUS, *PCERT_TRUST_STATUS;
  typedef struct {
	DWORD cbSize;
	PCCERT_CONTEXT pCertContext;
	CERT_TRUST_STATUS TrustStatus;
	void *pRevocationInfo;		// PCERT_REVOCATION_INFO pRevocationInfo;
	void *pIssuanceUsage;		// PCERT_ENHKEY_USAGE pIssuanceUsage;
	void *pApplicationUsage;	// PCERT_ENHKEY_USAGE pApplicationUsage;
	LPCWSTR pwszExtendedErrorInfo;
	} CERT_CHAIN_ELEMENT, *PCERT_CHAIN_ELEMENT;
  typedef struct {
	DWORD cbSize;
	PCTL_ENTRY pCtlEntry;
	PCCTL_CONTEXT pCtlContext;
	} CERT_TRUST_LIST_INFO, *PCERT_TRUST_LIST_INFO;
  typedef struct {
	DWORD cbSize;
	CERT_TRUST_STATUS TrustStatus;
	DWORD cElement;
	PCERT_CHAIN_ELEMENT *rgpElement;
	PCERT_TRUST_LIST_INFO pTrustListInfo;
	BOOL fHasRevocationFreshnessTime;
	DWORD dwRevocationFreshnessTime;
	} CERT_SIMPLE_CHAIN, *PCERT_SIMPLE_CHAIN;
  typedef struct CCC {
	DWORD cbSize;
	CERT_TRUST_STATUS TrustStatus;
	DWORD cChain;
	PCERT_SIMPLE_CHAIN *rgpChain;
	DWORD cLowerQualityChainContext;
	struct CCC **rgpLowerQualityChainContext;
	BOOL fHasRevocationFreshnessTime;
	DWORD dwRevocationFreshnessTime;
	} CERT_CHAIN_CONTEXT;
  typedef const CERT_CHAIN_CONTEXT *PCCERT_CHAIN_CONTEXT;
#endif /* Pre-1999 wincrypt.h */

/****************************************************************************
*																			*
*						 		Init/Shutdown Routines						*
*																			*
****************************************************************************/

/* Global function pointers.  These are necessary because the functions need
   to be dynamically linked since not all systems contain the necessary
   DLL's.  Explicitly linking to them will make cryptlib unloadable on some
   systems */

#define NULL_HINSTANCE	( HINSTANCE ) NULL

static HINSTANCE hCryptoAPI = NULL_HINSTANCE;
static HINSTANCE hAdvAPI32 = NULL_HINSTANCE;

typedef BOOL ( WINAPI *CERTADDCERTIFICATETOSTORE )( HCERTSTORE hCertStore,
					PCCERT_CONTEXT pCertContext, DWORD dwAddDisposition,
					PCCERT_CONTEXT *ppStoreContext );
typedef BOOL ( WINAPI *CERTADDENCODEDCERTIFICATETOSTORE )( HCERTSTORE hCertStore,
					DWORD dwCertEncodingType, const BYTE *pbCertEncoded, 
					DWORD cbCertEncoded, DWORD dwAddDisposition, 
					PCCERT_CONTEXT *ppCertContext );
typedef BOOL ( WINAPI *CERTCLOSESTORE )( HCERTSTORE hCertStore, DWORD dwFlags );
typedef PCCERT_CONTEXT ( WINAPI *CERTCREATECERTIFICATECONTEXT )( DWORD dwCertEncodingType,
					const BYTE *pbCertEncoded, DWORD cbCertEncoded );
typedef BOOL ( WINAPI *CERTDELETECERTIFICATEFROMSTORE )( PCCERT_CONTEXT pCertContext );
typedef PCCERT_CONTEXT ( WINAPI *CERTFINDCERTIFICATEINSTORE )( HCERTSTORE hCertStore,
					DWORD dwCertEncodingType, DWORD dwFindFlags, 
					DWORD dwFindType, const void *pvFindPara, 
					PCCERT_CONTEXT pPrevCertContext );
typedef VOID ( WINAPI *CERTFREECERTIFICATECHAIN )( PCCERT_CHAIN_CONTEXT pChainContext );
typedef BOOL ( WINAPI *CERTFREECERTIFICATECONTEXT )( PCCERT_CONTEXT pCertContext );
typedef BOOL ( WINAPI *CERTGETCERTIFICATECHAIN )( HCERTCHAINENGINE hChainEngine,
					PCCERT_CONTEXT pCertContext, LPFILETIME pTime, 
					HCERTSTORE hAdditionalStore, PCERT_CHAIN_PARA pChainPara,
					DWORD dwFlags, LPVOID pvReserved, 
					PCCERT_CHAIN_CONTEXT *ppChainContext );
typedef BOOL ( WINAPI *CERTGETCERTIFICATECONTEXTPROPERTY )( PCCERT_CONTEXT pCertContext,
					DWORD dwPropId, void *pvData, DWORD *pcbData );
typedef PCCERT_CONTEXT ( WINAPI *CERTGETSUBJECTCERTIFICATEFROMSTORE )( HCERTSTORE hCertStore,
					DWORD dwCertEncodingType, PCERT_INFO pCertId );
typedef BOOL ( WINAPI *CERTSETCERTIFICATEPROPERTY )( PCCERT_CONTEXT pCertContext,
					DWORD dwPropId, DWORD dwFlags, const void *pvData );
typedef HCERTSTORE ( WINAPI *CERTOPENSYSTEMSTORE )( HCRYPTPROV hprov,
					LPCSTR szSubsystemProtocol );

typedef BOOL ( WINAPI *CRYPTACQUIRECERTIFICATEPRIVATEKEY )( PCCERT_CONTEXT pCert,
					DWORD dwFlags, void *pvReserved, HCRYPTPROV *phCryptProv, 
					DWORD *pdwKeySpec, BOOL *pfCallerFreeProv );
typedef BOOL ( WINAPI *CRYPTACQUIRECONTEXTA )( HCRYPTPROV *phProv, LPCSTR pszContainer,
					LPCSTR pszProvider, DWORD dwProvType, DWORD dwFlags );
typedef BOOL ( WINAPI *CRYPTCREATEHASH )( HCRYPTPROV hProv, ALG_ID Algid, 
					HCRYPTKEY hKey, DWORD dwFlags, HCRYPTHASH* phHash );
typedef BOOL ( WINAPI *CRYPTDECRYPT )( HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final,
					DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen );
typedef BOOL ( WINAPI *CRYPTDESTROYHASH )( HCRYPTHASH hHash );
typedef BOOL ( WINAPI *CRYPTDESTROYKEY )( HCRYPTKEY hKey );
typedef BOOL ( WINAPI *CRYPTENCRYPT )( HCRYPTKEY hKey, HCRYPTHASH hHash, BOOL Final,
					DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen, DWORD dwBufLen );
typedef BOOL ( WINAPI *CRYPTEXPORTKEY )( HCRYPTKEY hKey, HCRYPTKEY hExpKey,
					DWORD dwBlobType, DWORD dwFlags, BYTE *pbData, DWORD *pdwDataLen );
typedef BOOL ( WINAPI *CRYPTFINDCERTIFICATEKEYPROVINFO )( PCCERT_CONTEXT pCert,
					DWORD dwFlags, void *pvReserved );
typedef BOOL ( WINAPI *CRYPTGENKEY )( HCRYPTPROV hProv, ALG_ID Algid, DWORD dwFlags,
					HCRYPTKEY *phKey );
typedef BOOL ( WINAPI *CRYPTGENRANDOM )( HCRYPTPROV hProv, DWORD dwLen, BYTE *pbBuffer );
typedef BOOL ( WINAPI *CRYPTGETKEYPARAM )( HCRYPTKEY hKey, DWORD dwParam, BYTE* pbData,
					DWORD* pdwDataLen, DWORD dwFlags );
typedef BOOL ( WINAPI *CRYPTGETPROVPARAM )( HCRYPTPROV hProv, DWORD dwParam, 
					BYTE *pbData, DWORD *pdwDataLen, DWORD dwFlags );
typedef BOOL ( WINAPI *CRYPTGETUSERKEY )( HCRYPTPROV hProv, DWORD dwKeySpec, 
					HCRYPTKEY* phUserKey );
typedef BOOL ( WINAPI *CRYPTHASHDATA )( HCRYPTHASH hHash, BYTE *pbData, DWORD dwDataLen,
					DWORD dwFlags );
typedef BOOL ( WINAPI *CRYPTIMPORTKEY )( HCRYPTPROV hProv, CONST BYTE *pbData,
					DWORD dwDataLen, HCRYPTKEY hPubKey, DWORD dwFlags, HCRYPTKEY *phKey );
typedef BOOL ( WINAPI *CRYPTRELEASECONTEXT )( HCRYPTPROV hProv, DWORD dwFlags );
typedef BOOL ( WINAPI *CRYPTSETHASHPARAM )( HCRYPTHASH hHash, DWORD dwParam, 
					BYTE* pbData, DWORD dwFlags );
typedef BOOL ( WINAPI *CRYPTSETKEYPARAM )( HCRYPTKEY hKey, DWORD dwParam, 
					BYTE *pbData, DWORD dwFlags );
typedef BOOL ( WINAPI *CRYPTSIGNHASH )( HCRYPTHASH hHash, DWORD dwKeySpec, 
					LPCTSTR sDescription, DWORD dwFlags, BYTE* pbSignature, 
					DWORD* pdwSigLen );

static CERTADDCERTIFICATETOSTORE pCertAddCertificateContextToStore = NULL;
static CERTADDENCODEDCERTIFICATETOSTORE pCertAddEncodedCertificateToStore = NULL;
static CERTCREATECERTIFICATECONTEXT pCertCreateCertificateContext = NULL;
static CERTDELETECERTIFICATEFROMSTORE pCertDeleteCertificateFromStore = NULL;
static CERTCLOSESTORE pCertCloseStore = NULL;
static CERTFINDCERTIFICATEINSTORE pCertFindCertificateInStore = NULL;
static CERTFREECERTIFICATECHAIN pCertFreeCertificateChain = NULL;
static CERTFREECERTIFICATECONTEXT pCertFreeCertificateContext = NULL;
static CERTGETCERTIFICATECHAIN pCertGetCertificateChain = NULL;
static CERTGETCERTIFICATECONTEXTPROPERTY pCertGetCertificateContextProperty = NULL;
static CERTGETSUBJECTCERTIFICATEFROMSTORE pCertGetSubjectCertificateFromStore = NULL;
static CERTSETCERTIFICATEPROPERTY pCertSetCertificateContextProperty = NULL;
static CERTOPENSYSTEMSTORE pCertOpenSystemStore = NULL;

static CRYPTACQUIRECERTIFICATEPRIVATEKEY pCryptAcquireCertificatePrivateKey = NULL;
static CRYPTACQUIRECONTEXTA pCryptAcquireContextA = NULL;
static CRYPTCREATEHASH pCryptCreateHash = NULL;
static CRYPTDECRYPT pCryptDecrypt = NULL;
static CRYPTDESTROYHASH pCryptDestroyHash = NULL;
static CRYPTDESTROYKEY pCryptDestroyKey = NULL;
static CRYPTENCRYPT pCryptEncrypt = NULL;
static CRYPTEXPORTKEY pCryptExportKey = NULL;
static CRYPTFINDCERTIFICATEKEYPROVINFO pCryptFindCertificateKeyProvInfo = NULL;
static CRYPTGENKEY pCryptGenKey = NULL;
static CRYPTGENRANDOM pCryptGenRandom = NULL;
static CRYPTGETKEYPARAM pCryptGetKeyParam = NULL;
static CRYPTGETPROVPARAM pCryptGetProvParam = NULL;
static CRYPTGETUSERKEY pCryptGetUserKey = NULL;
static CRYPTHASHDATA pCryptHashData = NULL;
static CRYPTIMPORTKEY pCryptImportKey = NULL;
static CRYPTRELEASECONTEXT pCryptReleaseContext = NULL;
static CRYPTSETHASHPARAM pCryptSetHashParam = NULL;
static CRYPTSETKEYPARAM pCryptSetKeyParam = NULL;
static CRYPTSIGNHASH pCryptSignHash = NULL;

/* Dynamically load and unload any necessary DBMS libraries */

int deviceInitCryptoAPI( void )
	{
	/* If the CryptoAPI module is already linked in, don't do anything */
	if( hCryptoAPI != NULL_HINSTANCE )
		return( CRYPT_OK );

	/* Obtain handles to the modules containing the CryptoAPI functions */
	if( ( hAdvAPI32 = GetModuleHandle( "AdvAPI32.DLL" ) ) == NULL )
		return( CRYPT_ERROR );
	if( ( hCryptoAPI = LoadLibrary( "Crypt32.dll" ) ) == NULL_HINSTANCE )
		return( CRYPT_ERROR );

	/* Get pointers to the crypt functions */
	pCryptAcquireCertificatePrivateKey = ( CRYPTACQUIRECERTIFICATEPRIVATEKEY ) GetProcAddress( hCryptoAPI, "CryptAcquireCertificatePrivateKey" );
	pCryptAcquireContextA = ( CRYPTACQUIRECONTEXTA ) GetProcAddress( hAdvAPI32, "CryptAcquireContextA" );
	pCryptCreateHash = ( CRYPTCREATEHASH ) GetProcAddress( hAdvAPI32, "CryptCreateHash" );
	pCryptDecrypt = ( CRYPTDECRYPT ) GetProcAddress( hAdvAPI32, "CryptDecrypt" );
	pCryptDestroyHash = ( CRYPTDESTROYHASH ) GetProcAddress( hAdvAPI32, "CryptDestroyHash" );
	pCryptDestroyKey = ( CRYPTDESTROYKEY ) GetProcAddress( hAdvAPI32, "CryptDestroyKey" );
	pCryptEncrypt = ( CRYPTENCRYPT ) GetProcAddress( hAdvAPI32, "CryptEncrypt" );
	pCryptExportKey = ( CRYPTEXPORTKEY ) GetProcAddress( hAdvAPI32, "CryptExportKey" );
	pCryptFindCertificateKeyProvInfo = ( CRYPTFINDCERTIFICATEKEYPROVINFO ) GetProcAddress( hCryptoAPI, "CryptFindCertificateKeyProvInfo" );
	pCryptGenKey = ( CRYPTGENKEY ) GetProcAddress( hAdvAPI32, "CryptGenKey" );
	pCryptGenRandom = ( CRYPTGENRANDOM ) GetProcAddress( hAdvAPI32, "CryptGenRandom" );
	pCryptGetKeyParam = ( CRYPTGETKEYPARAM ) GetProcAddress( hAdvAPI32, "CryptGetKeyParam" );
	pCryptGetProvParam = ( CRYPTGETPROVPARAM ) GetProcAddress( hAdvAPI32, "CryptGetProvParam" );
	pCryptGetUserKey = ( CRYPTGETUSERKEY ) GetProcAddress( hAdvAPI32, "CryptGetUserKey" );
	pCryptHashData = ( CRYPTHASHDATA ) GetProcAddress( hAdvAPI32, "CryptHashData" );
	pCryptImportKey = ( CRYPTIMPORTKEY ) GetProcAddress( hAdvAPI32, "CryptImportKey" );
	pCryptReleaseContext = ( CRYPTRELEASECONTEXT ) GetProcAddress( hAdvAPI32, "CryptReleaseContext" );
	pCryptSetHashParam = ( CRYPTSETHASHPARAM ) GetProcAddress( hAdvAPI32, "CryptSetHashParam" );
	pCryptSetKeyParam = ( CRYPTSETKEYPARAM ) GetProcAddress( hAdvAPI32, "CryptSetKeyParam" );
	pCryptSignHash = ( CRYPTSIGNHASH ) GetProcAddress( hAdvAPI32, "CryptSignHashA" );

	/* Get pointers to the cert functions */
	pCertAddCertificateContextToStore = ( CERTADDCERTIFICATETOSTORE ) GetProcAddress( hCryptoAPI, "CertAddCertificateContextToStore" );
	pCertAddEncodedCertificateToStore = ( CERTADDENCODEDCERTIFICATETOSTORE ) GetProcAddress( hCryptoAPI, "CertAddEncodedCertificateToStore" );
	pCertCreateCertificateContext = ( CERTCREATECERTIFICATECONTEXT ) GetProcAddress( hCryptoAPI, "CertCreateCertificateContext" );
	pCertDeleteCertificateFromStore = ( CERTDELETECERTIFICATEFROMSTORE ) GetProcAddress( hCryptoAPI, "CertDeleteCertificateFromStore" );
	pCertCloseStore = ( CERTCLOSESTORE ) GetProcAddress( hCryptoAPI, "CertCloseStore" );
	pCertFindCertificateInStore = ( CERTFINDCERTIFICATEINSTORE ) GetProcAddress( hCryptoAPI, "CertFindCertificateInStore" );
	pCertFreeCertificateChain = ( CERTFREECERTIFICATECHAIN ) GetProcAddress( hCryptoAPI, "CertFreeCertificateChain" );
	pCertFreeCertificateContext = ( CERTFREECERTIFICATECONTEXT )  GetProcAddress( hCryptoAPI, "CertFreeCertificateContext" );
	pCertGetCertificateChain = ( CERTGETCERTIFICATECHAIN ) GetProcAddress( hCryptoAPI, "CertGetCertificateChain" );
	pCertGetCertificateContextProperty = ( CERTGETCERTIFICATECONTEXTPROPERTY ) GetProcAddress( hCryptoAPI, "CertGetCertificateContextProperty" );
	pCertGetSubjectCertificateFromStore = ( CERTGETSUBJECTCERTIFICATEFROMSTORE ) GetProcAddress( hCryptoAPI, "CertGetSubjectCertificateFromStore" );
	pCertSetCertificateContextProperty = ( CERTSETCERTIFICATEPROPERTY ) GetProcAddress( hCryptoAPI, "CertSetCertificateContextProperty" );
	pCertOpenSystemStore = ( CERTOPENSYSTEMSTORE ) GetProcAddress( hCryptoAPI, "CertOpenSystemStoreA" );

	/* Make sure that we got valid pointers for every CryptoAPI function */
	if( pCertAddCertificateContextToStore == NULL || \
		pCertAddEncodedCertificateToStore == NULL || 
		pCertCreateCertificateContext == NULL ||
		pCertDeleteCertificateFromStore == NULL ||
		pCertCloseStore == NULL || pCertFindCertificateInStore == NULL ||
		pCertFreeCertificateChain == NULL || 
		pCertFreeCertificateContext == NULL || 
		pCertGetCertificateChain == NULL ||
		pCertGetCertificateContextProperty  == NULL ||
		pCertGetSubjectCertificateFromStore == NULL || 
		pCertSetCertificateContextProperty == NULL ||
		pCertOpenSystemStore == NULL || 
		pCryptAcquireCertificatePrivateKey == NULL ||
		pCryptAcquireContextA == NULL || pCryptCreateHash == NULL || 
		pCryptDecrypt == NULL || pCryptEncrypt == NULL || 
		pCryptExportKey == NULL || pCryptDestroyHash == NULL || 
		pCryptDestroyKey == NULL || 
		pCryptFindCertificateKeyProvInfo == NULL || pCryptGenKey == NULL || 
		pCryptGenRandom == NULL || pCryptGetKeyParam == NULL || 
		pCryptGetProvParam == NULL || pCryptGetUserKey == NULL || 
		pCryptHashData == NULL || pCryptImportKey == NULL || 
		pCryptReleaseContext == NULL || pCryptSetHashParam == NULL || 
		pCryptSetKeyParam == NULL || pCryptSignHash == NULL )
		{
		/* Free the library reference and reset the handle */
		FreeLibrary( hCryptoAPI );
		hCryptoAPI = NULL_HINSTANCE;
		return( CRYPT_ERROR );
		}

	return( CRYPT_OK );
	}

void deviceEndCryptoAPI( void )
	{
	if( hCryptoAPI != NULL_HINSTANCE )
		FreeLibrary( hCryptoAPI );
	hCryptoAPI = NULL_HINSTANCE;
	}

/****************************************************************************
*																			*
*						 		Utility Routines							*
*																			*
****************************************************************************/

/* Map a CryptoAPI-specific error to a cryptlib error */

static int mapError( CRYPTOAPI_INFO *cryptoapiInfo, const int defaultError )
	{
	const DWORD errorCode = GetLastError();
	int messageLength;

	/* Get the error code and corresponding error message.  FormatMessage()
	   adds EOL terminators so we have to strip those before we pass the
	   string back to the caller.  There's an incredibly arcane way of 
	   telling FormatMessage() to do this via escape codes passed in as
	   part of a va_arg argument list, but aside from being complex to set
	   up this also means that the function will try and insert information
	   such as filenames from the argument list when required (there's no
	   way to tell in advance which arguments are required), so this is
	   more trouble than it's worth */
	cryptoapiInfo->errorCode = ( int ) errorCode;
	FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, errorCode, 0,
				   cryptoapiInfo->errorMessage, MAX_ERRMSG_SIZE - 1, NULL );
	for( messageLength = strlen( cryptoapiInfo->errorMessage );
		 messageLength > 0 && \
			( cryptoapiInfo->errorMessage[ messageLength - 1 ] == '\n' || \
			  cryptoapiInfo->errorMessage[ messageLength - 1 ] == '\r' );
		 messageLength-- )
		cryptoapiInfo->errorMessage[ messageLength ] = '\0';

	/* Translate the CAPI error code into the cryptlib equivalent */
	switch( errorCode )
		{
		case CRYPT_E_UNKNOWN_ALGO:
			return( CRYPT_ERROR_NOTAVAIL );

		case ERROR_BUSY:
			return( CRYPT_ERROR_TIMEOUT );

		case ERROR_MORE_DATA:
			return( CRYPT_ERROR_OVERFLOW );

		case ERROR_NO_MORE_ITEMS:
			return( CRYPT_ERROR_COMPLETE );

		case NTE_BAD_DATA:
			return( CRYPT_ERROR_BADDATA );

		case CRYPT_E_EXISTS:
		case NTE_EXISTS:
			return( CRYPT_ERROR_DUPLICATE );

		case ERROR_NOT_ENOUGH_MEMORY:
		case NTE_NO_MEMORY:
			return( CRYPT_ERROR_MEMORY );

		case CRYPT_E_SECURITY_SETTINGS:
		case NTE_PERM:
			return( CRYPT_ERROR_PERMISSION );

		case NTE_BAD_SIGNATURE:
			return( CRYPT_ERROR_SIGNATURE );

		case CRYPT_E_NO_MATCH:
		case CRYPT_E_NOT_FOUND:
		case NTE_KEYSET_NOT_DEF:
		case NTE_NOT_FOUND:
		case NTE_PROV_DLL_NOT_FOUND:
		case NTE_PROV_TYPE_NO_MATCH:
		case NTE_PROV_TYPE_NOT_DEF:
			return( CRYPT_ERROR_NOTFOUND );
		}

	return( defaultError );
	}

static int mapDeviceError( CONTEXT_INFO *contextInfoPtr, const int defaultError )
	{
	CRYPT_DEVICE iCryptDevice;
	DEVICE_INFO *deviceInfo;
	int status;

	/* Get the device associated with this context, set the error information
	   in it, and exit */
	status = krnlSendMessage( contextInfoPtr->objectHandle, IMESSAGE_GETDEPENDENT, 
							  &iCryptDevice, OBJECT_TYPE_DEVICE );
	if( cryptStatusOK( status ) )
		status = krnlAcquireObject( iCryptDevice, OBJECT_TYPE_DEVICE, 
									( void ** ) &deviceInfo, 
									CRYPT_ERROR_SIGNALLED );
	if( cryptStatusError( status ) )
		return( status );
	status = mapError( deviceInfo->deviceCryptoAPI, defaultError );
	krnlReleaseObject( deviceInfo->objectHandle );
	return( status );
	}

/* Map cryptlib to/from CryptoAPI algorithm IDs */

typedef struct {
	const CRYPT_ALGO_TYPE cryptAlgo;
	const ALG_ID algID;
	} ALGO_MAP_INFO;
	
static const ALGO_MAP_INFO algoMap[] = {
	/* PKC algorithms */
	{ CRYPT_ALGO_RSA, CALG_RSA_SIGN },
	{ CRYPT_ALGO_RSA, CALG_RSA_KEYX },
	{ CRYPT_ALGO_DSA, CALG_DSS_SIGN },

	/* Encryption algorithms */
	{ CRYPT_ALGO_DES, CALG_DES },
	{ CRYPT_ALGO_3DES, CALG_3DES },
	{ CRYPT_ALGO_RC2, CALG_RC2 },
	{ CRYPT_ALGO_RC4, CALG_RC4 },
	{ CRYPT_ALGO_SKIPJACK, CALG_SKIPJACK },

	/* Hash algorithms */
	{ CRYPT_ALGO_MD2, CALG_MD2 },
	{ CRYPT_ALGO_MD5, CALG_MD5 },
	{ CRYPT_ALGO_SHA, CALG_SHA },

	{ CRYPT_ALGO_NONE, 0 }, { CRYPT_ALGO_NONE, 0 }
	};

static ALG_ID cryptlibToCapiID( const CRYPT_ALGO_TYPE cryptAlgo )
	{
	int i;

	for( i = 0; algoMap[ i ].cryptAlgo != CRYPT_ALGO_NONE && \
				i < FAILSAFE_ARRAYSIZE( algoMap, ALGO_MAP_INFO ); i++ )
		{
		if( algoMap[ i ].cryptAlgo == cryptAlgo )
			break;
		}
	if( i >= FAILSAFE_ARRAYSIZE( algoMap, ALGO_MAP_INFO ) )
		retIntError_Ext( 0 );
	if( algoMap[ i ].cryptAlgo == CRYPT_ALGO_NONE )
		return( 0 );
	return( algoMap[ i ].algID );
	}

static CRYPT_ALGO_TYPE capiToCryptlibID( const ALG_ID algID )
	{
	int i;

	for( i = 0; algoMap[ i ].cryptAlgo != CRYPT_ALGO_NONE && \
				i < FAILSAFE_ARRAYSIZE( algoMap, ALGO_MAP_INFO ); i++ )
		{
		if( algoMap[ i ].algID == algID )
			break;
		}
	if( i >= FAILSAFE_ARRAYSIZE( algoMap, ALGO_MAP_INFO ) )
		retIntError_Ext( CRYPT_ALGO_NONE );
	if( algoMap[ i ].cryptAlgo == CRYPT_ALGO_NONE )
		return( CRYPT_ALGO_NONE );
	return( algoMap[ i ].cryptAlgo );
	}

/* Copy an MPI into the little-endian order required by CryptoAPI, returning
   the end position of the copied MPI */

static BYTE *copyMPI( BYTE *dest, const BYTE *src, const int srcLen,
					  const int srcRequiredLen )
	{
	int i;

	dest += srcLen - 1;
	for( i = 0; i < srcLen; i++ )
		*dest-- = *src++;
	dest += srcLen + 1;
	if( srcLen < srcRequiredLen )
		{
		/* CryptoAPI blobs don't contain any length information but 
		   implicitly specify all lengths in terms of the size of the
		   main MPI component, so if the actual length is less than the
		   assumed length we pad the remainder out with zeroes */
		for( i = 0; i < srcRequiredLen - srcLen; i++ )
			*dest++ = 0;
		}
	
	return( dest );
	}

/* Create the special-case RSA key with e=1 that's needed to allow direct 
   key import and export */

static int createExportKey( const HCRYPTPROV hProv, HCRYPTKEY *hPrivateKey, 
							int *privateKeySize )
	{
	BLOBHEADER *blobHeaderPtr;
	RSAPUBKEY *pubKeyPtr;
	BYTE keyBlob[ 1024 + 8 ], *keyBlobPtr;
	BOOL result;
	int bitLen16, keyBlobLen = 1024;

	/* Generate a private key and export it as a private key blob:

		Ofs	Value

		  0	BLOBHEADER blobheader {
			  0	BYTE bType;
			  1	BYTE bVersion;
			  2 WORD reserved;
			  4	ALG_ID aiKeyAlg; }
		  8	RSAPUBKEY rsapubkey {
			  8 DWORD magic;
			 12	DWORD bitlen;
			 16	DWORD pubexp; }
		 20	BYTE modulus[ rsapubkey.bitlen / 8 ];
			BYTE prime1[ rsapubkey.bitlen / 16 ];
			BYTE prime2[ rsapubkey.bitlen / 16 ];
			BYTE exponent1[ rsapubkey.bitlen / 16 ];
			BYTE exponent2[ rsapubkey.bitlen / 16 ];
			BYTE coefficient[ rsapubkey.bitlen / 16 ];
			BYTE privateExponent[ rsapubkey.bitlen / 8 ]; */
	if( !pCryptGenKey( hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, hPrivateKey ) || \
		!pCryptExportKey( *hPrivateKey, 0, PRIVATEKEYBLOB, 0, keyBlob, &keyBlobLen ) || \
		!pCryptDestroyKey( *hPrivateKey ) )
		return( CRYPT_ERROR );

	/* Perform a general sanity check on the returned data */
	blobHeaderPtr = ( BLOBHEADER * ) keyBlob;
	if( blobHeaderPtr->bType != PRIVATEKEYBLOB || \
		blobHeaderPtr->bVersion != CUR_BLOB_VERSION || \
		blobHeaderPtr->aiKeyAlg != CALG_RSA_KEYX )
		{
		pCryptDestroyKey( *hPrivateKey );
		return( CRYPT_ERROR );
		}

	/* Set the public exponent to 1 (little-endian 32-bit value) and skip to 
	   the private exponents */
	pubKeyPtr = ( RSAPUBKEY * ) ( keyBlob + BLOBHEADER_SIZE );
	bitLen16 = ( pubKeyPtr->bitlen / 16 );
	pubKeyPtr->pubexp = 1;
	keyBlobPtr = keyBlob + 20 + ( pubKeyPtr->bitlen / 8 ) + bitLen16 + bitLen16;

	/* Set the two exponents to 1 */
	*keyBlobPtr++ = 1;
	memset( keyBlobPtr, 0, bitLen16 - 1 );
	keyBlobPtr += bitLen16 - 1;
	*keyBlobPtr++ = 1;
	memset( keyBlobPtr, 0, bitLen16 - 1 );
	keyBlobPtr += bitLen16 - 1;

	/* Set the private exponent to 1 */
	keyBlobPtr += bitLen16;		/* Skip coefficient */
	*keyBlobPtr++ = 1;
	memset( keyBlobPtr, 0, bitLen16 - 1 );
	keyBlobPtr += bitLen16 - 1;

	/* Finally, re-import the hacked key and clean up */
	result = pCryptImportKey( hProv, keyBlob, keyBlobLen, 0, 0, hPrivateKey );
	if( result )
		*privateKeySize = pubKeyPtr->bitlen / 8;
	else
		*hPrivateKey = 0;
	zeroise( keyBlob, keyBlobLen );

	return( result ? CRYPT_OK : CRYPT_ERROR );
	}

/* Import a raw session key using the exponent-one RSA key */

static int importPlainKey( const HCRYPTPROV hProv, 
						   const HCRYPTKEY hPrivateKey, 
						   const int privateKeySize, HCRYPTKEY *hSessionKey, 
						   const CRYPT_ALGO_TYPE cryptAlgo, const BYTE *keyData, 
						   const int keyDataSize, void *errorInfoPtr )
	{
	BLOBHEADER *blobHeaderPtr;
	BYTE keyBlob[ 1024 + 8 ], *keyBlobPtr;
	ALG_ID algID;
	DWORD *dwPtr;
	BOOL result;
	const int blobSize = sizeof( BLOBHEADER ) + sizeof( ALG_ID ) + privateKeySize;
	int i;

	/* Set up a SIMPLEBLOB:

		Ofs	Value
		  0	BLOBHEADER blobheader {
			  0	BYTE bType;
			  1	BYTE bVersion;
			  2 WORD reserved;
			  4	ALG_ID aiKeyAlg; }
		  8	ALG_ID algid;
		 12	BYTE encryptedkey[ rsapubkey.bitlen/8 ]; */
	memset( keyBlob, 0, 1024 );
	algID = cryptlibToCapiID( cryptAlgo );
	if( algID == 0 )
		return( CRYPT_ERROR_NOTAVAIL );

	/* Set up the BLOBHEADER part of the blob */
	blobHeaderPtr = ( BLOBHEADER * ) keyBlob;
    blobHeaderPtr->bType = SIMPLEBLOB;
	blobHeaderPtr->bVersion = CUR_BLOB_VERSION;
	blobHeaderPtr->aiKeyAlg = algID;

	/* Set up the private-key algorithm ID */
	dwPtr = ( DWORD * )( keyBlob + BLOBHEADER_SIZE );
	*dwPtr = CALG_RSA_KEYX;

	/* Store the key as byte-reversed PKCS #1 padded data (or at least close 
	   enough to it to work for the import) */
	keyBlobPtr = keyBlob + 12;
	for( i = keyDataSize - 1; i >= 0; i-- )
		*keyBlobPtr++ = keyData[ i ];
	*keyBlobPtr++ = 0;
	memset( keyBlobPtr, 2, privateKeySize - ( keyDataSize + 2 ) );

	/* Import the key from the faked PKCS #1 wrapped form */
	result = pCryptImportKey( hProv, keyBlob, blobSize, hPrivateKey, 0, hSessionKey );
	zeroise( keyBlob, blobSize );
	if( !result )
		return( mapDeviceError( errorInfoPtr, CRYPT_ERROR_FAILED ) );

	return( CRYPT_OK );
	}

/* Load a CryptoAPI public key into a cryptlib native context */

static int getPubkeyComponents( CRYPTOAPI_INFO *cryptoapiInfo,
								const HCRYPTKEY hKey, BYTE *n, int *nLen, 
								BYTE *e, int *eLen )
	{
	BLOBHEADER *blobHeaderPtr;
	RSAPUBKEY *pubKeyPtr;
	BYTE keyBlob[ 1024 + CRYPT_MAX_PKCSIZE + 8 ], *nPtr;
	BYTE buffer[ 16 + 8 ], *bufPtr = buffer;
	int keyBlobLen = 1024 + CRYPT_MAX_PKCSIZE, exponent, length;

	/* Clear return values */
	memset( n, 0, 8 );
	memset( e, 0, 8 );
	*nLen = *eLen = 0;

	/* Get the public key components */
	if( !pCryptExportKey( hKey, 0, PUBLICKEYBLOB, 0, keyBlob, &keyBlobLen ) )
		return( mapError( cryptoapiInfo, CRYPT_ERROR_FAILED ) );

	/* Perform a general sanity check on the returned data */
	blobHeaderPtr = ( BLOBHEADER * ) keyBlob;
	if( blobHeaderPtr->bType != PUBLICKEYBLOB || \
		blobHeaderPtr->bVersion != CUR_BLOB_VERSION )
		return( CRYPT_ERROR_FAILED );

	/* Extract the public key components */
	pubKeyPtr = ( RSAPUBKEY * ) ( keyBlob + BLOBHEADER_SIZE );
	exponent = pubKeyPtr->pubexp;
	nPtr = keyBlob + 20;
	length = pubKeyPtr->bitlen / 8;
	while( length > 0 && nPtr[ length ] == 0 )
		length--;
	copyMPI( n, nPtr, length, length );
	*nLen = length;
	length = ( exponent <= 0xFF ) ? 1 : \
			 ( exponent <= 0xFFFF ) ? 2 : \
			 ( exponent <= 0xFFFFFFL ) ? 3 : 4;
	mputLong( bufPtr, exponent );
	memcpy( e, buffer + ( 4 - length ), length );
	*eLen = length;

	return( CRYPT_OK );
	}

static int capiToCryptlibContext( CRYPTOAPI_INFO *cryptoapiInfo,
								  const HCRYPTKEY hKey, 
								  CRYPT_CONTEXT *cryptContextPtr )
	{
	CRYPT_PKCINFO_RSA rsaKey;
	MESSAGE_CREATEOBJECT_INFO createInfo;
	MESSAGE_DATA msgData;
	BYTE n[ CRYPT_MAX_PKCSIZE + 8 ], e[ CRYPT_MAX_PKCSIZE + 8 ];
	int nLen, eLen, status;

	/* Clear return value */
	*cryptContextPtr = CRYPT_ERROR;

	/* Extract the public-key components from the CryptoAPI context */
	status = getPubkeyComponents( cryptoapiInfo, hKey, n, &nLen, e, &eLen );
	if( cryptStatusError( status ) )
		return( status );

	/* Copy the public-key components into the cryptlib format */
	cryptInitComponents( &rsaKey, CRYPT_KEYTYPE_PUBLIC );
	cryptSetComponent( ( &rsaKey )->n, n, bytesToBits( nLen ) );
	cryptSetComponent( ( &rsaKey )->e, e, bytesToBits( eLen ) );
	zeroise( n, CRYPT_MAX_PKCSIZE );
	zeroise( e, CRYPT_MAX_PKCSIZE );

	/* Create the RSA context */
	setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_RSA );
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_CREATEOBJECT,
							  &createInfo, OBJECT_TYPE_CONTEXT );
	if( cryptStatusError( status ) )
		{
		cryptDestroyComponents( &rsaKey );
		return( status );
		}

	/* Load the key into the context */
	setMessageData( &msgData, "CryptoAPI RSA key", 17 );
	status = krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE_S,
							  &msgData, CRYPT_CTXINFO_LABEL );
	if( cryptStatusOK( status ) )
		{
		setMessageData( &msgData, &rsaKey, sizeof( CRYPT_PKCINFO_RSA ) );
		status = krnlSendMessage( createInfo.cryptHandle,
								  IMESSAGE_SETATTRIBUTE_S, &msgData,
								  CRYPT_CTXINFO_KEY_COMPONENTS );
		}
	cryptDestroyComponents( &rsaKey );
	if( cryptStatusError( status ) )
		{
		assert( NOTREACHED );
		krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
		return( status );
		}

	*cryptContextPtr = createInfo.cryptHandle;
	return( CRYPT_OK );
	}

/* Compare a CryptoAPI private key with a certificate to check whether the
   cert corresponds to the key */

static BOOLEAN isCertKey( const CRYPTOAPI_INFO *cryptoapiInfo,
						  const HCRYPTKEY hKey, 
						  const CRYPT_CERTIFICATE iCryptCert )
	{
	return( TRUE );
	}

/* Get a certificate using a key/certificate identifier */

static int getCertificate( const CRYPTOAPI_INFO *cryptoapiInfo,
						   const CRYPT_KEYID_TYPE keyIDtype,
						   const void *keyID, const int keyIDlength,
						   PCCERT_CONTEXT *pCertContextPtr )
	{
	PCCERT_CONTEXT pCertContext = NULL;

	switch( keyIDtype )
		{
		case CRYPT_KEYID_NAME:
			{
			CERT_RDN certRDN;
			CERT_RDN_ATTR certRDNAttr;

			/* Find a cert by CN */
			memset( &certRDNAttr, 0, sizeof( CERT_RDN_ATTR ) );
			certRDNAttr.pszObjId = szOID_COMMON_NAME;
			certRDNAttr.dwValueType = CERT_RDN_ANY_TYPE;
			certRDNAttr.Value.pbData = ( void * ) keyID;
			certRDNAttr.Value.cbData = keyIDlength;
			memset( &certRDN, 0, sizeof( CERT_RDN ) );
			certRDN.rgRDNAttr = &certRDNAttr;
			certRDN.cRDNAttr = 1;
			pCertContext = \
				pCertFindCertificateInStore( cryptoapiInfo->hCertStore,
							X509_ASN_ENCODING, 0, CERT_FIND_SUBJECT_ATTR,
							&certRDN, NULL );
			break;
			}

		case CRYPT_KEYID_URI:
			{
			CERT_RDN certRDN;
			CERT_RDN_ATTR certRDNAttr;

			/* There doesn't appear to be any way to locate a cert using the 
			   email address in an altName, so we have to restrict ourselves 
			   to the most commonly-used OID for certs in DNs */
			memset( &certRDNAttr, 0, sizeof( CERT_RDN_ATTR ) );
			certRDNAttr.pszObjId = szOID_RSA_emailAddr ;
			certRDNAttr.dwValueType = CERT_RDN_ANY_TYPE;
			certRDNAttr.Value.pbData = ( void * ) keyID;
			certRDNAttr.Value.cbData = keyIDlength;
			memset( &certRDN, 0, sizeof( CERT_RDN ) );
			certRDN.rgRDNAttr = &certRDNAttr;
			certRDN.cRDNAttr = 1;
			pCertContext = \
				pCertFindCertificateInStore( cryptoapiInfo->hCertStore,
							X509_ASN_ENCODING, 0, CERT_FIND_SUBJECT_ATTR,
							&certRDN, NULL );
			break;
			}

		case CRYPT_IKEYID_CERTID:
			{
			CRYPT_DATA_BLOB cryptDataBlob;

			memset( &cryptDataBlob, 0, sizeof( CRYPT_DATA_BLOB ) );
			cryptDataBlob.pbData = ( void * ) keyID;
			cryptDataBlob.cbData = keyIDlength;
			pCertContext = \
				pCertFindCertificateInStore( cryptoapiInfo->hCertStore,
							X509_ASN_ENCODING, 0, CERT_FIND_SHA1_HASH,
							&cryptDataBlob, NULL );
			break;
			}

		case CRYPT_IKEYID_KEYID:
			{
#if 0
			CERT_ID certID;

			certID.dwIdChoice = CERT_ID_KEY_IDENTIFIER;
			certID.KeyId.pbData = ( void * ) keyID;
			certID.KeyId.cbData = keyIDlength;
			pCertContext = \
				pCertFindCertificateInStore( cryptoapiInfo->hCertStore,
							X509_ASN_ENCODING, 0, CERT_FIND_CERT_ID,
							&certID, NULL );
#else
			CRYPT_HASH_BLOB hashBlob;

			hashBlob.pbData = ( void * ) keyID;
			hashBlob.cbData = keyIDlength;
			pCertContext = \
				pCertFindCertificateInStore( cryptoapiInfo->hCertStore,
							X509_ASN_ENCODING, 0, CERT_FIND_KEY_IDENTIFIER,
							&hashBlob, NULL );
#endif /* 0 */
			break;
			}

		case CRYPT_IKEYID_ISSUERANDSERIALNUMBER:
			{
			CERT_INFO certInfo;
			STREAM stream;
			int length;

			memset( &certInfo, 0, sizeof( CERT_INFO ) );
			sMemConnect( &stream, keyID, keyIDlength );
			readSequence( &stream, NULL );
			certInfo.Issuer.pbData = sMemBufPtr( &stream );
			readSequence( &stream, &length );		/* Issuer DN */
			certInfo.Issuer.cbData = ( int ) sizeofObject( length );
			sSkip( &stream, length );
			certInfo.SerialNumber.pbData = sMemBufPtr( &stream );
			readGenericHole( &stream, &length, 1, BER_INTEGER );/* Serial number */
			certInfo.SerialNumber.cbData = ( int ) sizeofObject( length );
			assert( sStatusOK( &stream ) );
			sMemDisconnect( &stream );
			pCertContext = \
				pCertGetSubjectCertificateFromStore( cryptoapiInfo->hCertStore,
							X509_ASN_ENCODING, &certInfo );
			}

		default:
			assert( NOTREACHED );
		}

	if( pCertContext == NULL )
		return( CRYPT_ERROR_NOTFOUND );
	*pCertContextPtr = pCertContext;
	return( CRYPT_OK );
	}

/* Get a certificate chain from a leaf certificate */

static int getCertificateChain( CRYPTOAPI_INFO *cryptoapiInfo,
								const PCCERT_CONTEXT pCertContext,
								PCCERT_CHAIN_CONTEXT *pChainContextPtr )
	{
	CERT_CHAIN_PARA chainPara;
	CERT_USAGE_MATCH certUsage;
	CERT_ENHKEY_USAGE enhkeyUsage;
	PCCERT_CHAIN_CONTEXT pChainContext;

	/* Clear return value */
	*pChainContextPtr = NULL;

	/* Get the chain from the supplied cert up to a root cert */
	memset( &enhkeyUsage, 0, sizeof( CERT_ENHKEY_USAGE ) );
	enhkeyUsage.cUsageIdentifier = 0;
	enhkeyUsage.rgpszUsageIdentifier = NULL;
	memset( &certUsage, 0, sizeof( CERT_USAGE_MATCH ) );
	certUsage.dwType = USAGE_MATCH_TYPE_AND;
	certUsage.Usage = enhkeyUsage;
	memset( &chainPara, 0, sizeof( CERT_CHAIN_PARA ) );
	chainPara.cbSize = sizeof( CERT_CHAIN_PARA );
	chainPara.RequestedUsage = certUsage;
	if( !pCertGetCertificateChain( NULL, pCertContext, NULL, NULL, &chainPara,
								   CERT_CHAIN_CACHE_END_CERT | \
								   CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY, 
								   NULL, &pChainContext ) )
		return( mapError( cryptoapiInfo, CRYPT_ERROR_NOTFOUND ) );
	*pChainContextPtr = pChainContext;
	return( CRYPT_OK );
	}

/* Get a certificate from a public/private key and vice versa */

static int getCertificateFromKey( CRYPTOAPI_INFO *cryptoapiInfo,
								  const HCRYPTKEY hKey,
								  const BOOLEAN isSigningKey,
								  PCCERT_CONTEXT *pCertContextPtr )
	{
	PCCERT_CONTEXT pCertContext;
	CERT_PUBLIC_KEY_INFO pubKeyInfo;
	BYTE keyBlob[ 1024 + CRYPT_MAX_PKCSIZE + 8 ];
	int keyBlobLen = 1024 + CRYPT_MAX_PKCSIZE;

	/* Clear return value */
	*pCertContextPtr = NULL;

	/* Extract the public-key components from the public or private key */
	if( !pCryptExportKey( hKey, 0, PUBLICKEYBLOB, 0, keyBlob, &keyBlobLen ) )
		{
		pCryptDestroyKey( hKey );
		return( CRYPT_ERROR_NOTFOUND );
		}

	/* Get the cert for the context's public key */
	memset( &pubKeyInfo, 0, sizeof( CERT_PUBLIC_KEY_INFO ) );
	pubKeyInfo.Algorithm.pszObjId = isSigningKey ? \
									CERT_DEFAULT_OID_PUBLIC_KEY_SIGN : \
									CERT_DEFAULT_OID_PUBLIC_KEY_XCHG;
	pubKeyInfo.PublicKey.pbData = keyBlob;
	pubKeyInfo.PublicKey.cbData = keyBlobLen;
	pCertContext = \
		pCertFindCertificateInStore( cryptoapiInfo->hCertStore,
						X509_ASN_ENCODING, 0, CERT_FIND_PUBLIC_KEY,
						&pubKeyInfo, NULL );
	if( pCertContext == NULL )
		return( mapError( cryptoapiInfo, CRYPT_ERROR_NOTFOUND ) );
	*pCertContextPtr = pCertContext;
	return( CRYPT_OK );
	}

#if 0

static int getPrivKeyFromCertificate( CRYPTOAPI_INFO *cryptoapiInfo,
									  const PCCERT_CONTEXT pCertContext,
									  HCRYPTKEY *hKeyPtr )
	{
	HCRYPTPROV hProv;
	HCRYPTKEY hKey;
	DWORD dwKeySpec;
	BOOL fCallerFreeProv;

	/* Clear return value */
	*hKeyPtr = 0;

	/* Get the provider and key-type from the cert and use that to get the
	   key */
	if( !pCryptAcquireCertificatePrivateKey( pCertContext, 
									CRYPT_ACQUIRE_CACHE_FLAG, NULL,
									&hProv, &dwKeySpec, &fCallerFreeProv ) )
		return( mapError( cryptoapiInfo, CRYPT_ERROR_NOTFOUND ) );
	if( !pCryptGetUserKey( hProv, dwKeySpec, &hKey ) )
		return( mapError( cryptoapiInfo, CRYPT_ERROR_NOTFOUND ) );
	*hKeyPtr = hKey;
	return( CRYPT_OK );
	}
#endif /* 0 */

/* Create a private-key context using a CryptoAPI native key */

static int createPrivkeyContext( DEVICE_INFO *deviceInfo,
								 CRYPT_CONTEXT *iCryptContext,
								 CRYPT_ALGO_TYPE *cryptAlgo,
								 const HCRYPTKEY hKey,
								 const char *label )
	{
	ALG_ID algID;
	DWORD dwDataLen = sizeof( ALG_ID );
	const CAPABILITY_INFO *capabilityInfoPtr = NULL;
	MESSAGE_DATA msgData;
	int status;

	/* Clear return values */
	*iCryptContext = CRYPT_ERROR;
	*cryptAlgo = CRYPT_ALGO_NONE;

	/* Get the algorithm type and look up the corresponding capability 
	   info */
	if( !pCryptGetKeyParam( hKey, KP_ALGID, ( BYTE * ) &algID, &dwDataLen, 
							0 ) || \
		( *cryptAlgo = capiToCryptlibID( algID ) ) == CRYPT_ALGO_NONE )
		return( CRYPT_ERROR_NOTAVAIL );
	capabilityInfoPtr = findCapabilityInfo( deviceInfo->capabilityInfoList, 
											*cryptAlgo );
	if( capabilityInfoPtr == NULL )
		return( CRYPT_ERROR_NOTAVAIL );

	/* Create a dummy context for the key, remember the device it's 
	   contained in, the handle for the device-internal key, and the 
	   object's label, and mark it as initialised (i.e. with a key loaded) */
	status = createContextFromCapability( iCryptContext, 
								deviceInfo->ownerHandle, capabilityInfoPtr, 
								CREATEOBJECT_FLAG_DUMMY );
	if( cryptStatusError( status ) )
		return( status );
	krnlSendMessage( *iCryptContext, IMESSAGE_SETDEPENDENT,
					 &deviceInfo->objectHandle, SETDEP_OPTION_INCREF );
	krnlSendMessage( *iCryptContext, IMESSAGE_SETATTRIBUTE, 
					 ( void * ) &hKey, CRYPT_IATTRIBUTE_DEVICEOBJECT );
	setMessageData( &msgData, ( void * ) label, 
					min( strlen( label ), CRYPT_MAX_TEXTSIZE ) );
	krnlSendMessage( *iCryptContext, IMESSAGE_SETATTRIBUTE_S,
					 &msgData, CRYPT_CTXINFO_LABEL );
#if 0
	if( cryptAlgo == CRYPT_ALGO_RSA )
		/* Send the keying info to the context.  This is only possible for
		   RSA keys since it's not possible to read y from a DSA private
		   key object (see the comments in the DSA code for more on this), 
		   however the only time this is necessary is when a cert is being 
		   generated for a key that was pre-generated in the device by 
		   someone else, which is typically done in Europe where DSA isn't 
		   used so this shouldn't be a problem */
		// Use getPubkeyComponents()
		cryptStatus = rsaSetPublicComponents( deviceInfo, *iCryptContext, 
											  hObject );
	else
		cryptStatus = krnlSendMessage( *iCryptContext, IMESSAGE_SETATTRIBUTE, 
									   &keySize, CRYPT_IATTRIBUTE_KEYSIZE );
#endif
	if( cryptStatusOK( status ) )
		status = krnlSendMessage( *iCryptContext, IMESSAGE_SETATTRIBUTE,
								   MESSAGE_VALUE_UNUSED, 
								   CRYPT_IATTRIBUTE_INITIALISED );
	if( cryptStatusError( status ) )
		{
		krnlSendNotifier( *iCryptContext, IMESSAGE_DECREFCOUNT );
		return( status );
		}

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*					Device Init/Shutdown/Device Control Routines			*
*																			*
****************************************************************************/

/* Prototypes for functions to get and free device capability information */

static int getCapabilities( DEVICE_INFO *deviceInfo );
static void freeCapabilities( DEVICE_INFO *deviceInfo );

/* Prototypes for device-specific functions */

static int getRandomFunction( DEVICE_INFO *deviceInfo, void *buffer,
							  const int length );

/* Close a previously-opened session with the device.  We have to have this
   before the init function since it may be called by it if the init process
   fails */

static void shutdownFunction( DEVICE_INFO *deviceInfo )
	{
	CRYPTOAPI_INFO *cryptoapiInfo = deviceInfo->deviceCryptoAPI;

	/* Log out and close the session with the device */
	if( deviceInfo->flags & DEVICE_LOGGEDIN )
		{
		if( cryptoapiInfo->hPrivateKey )
			pCryptDestroyKey( cryptoapiInfo->hPrivateKey );
		pCryptReleaseContext( cryptoapiInfo->hProv, 0 );
		}
	if( cryptoapiInfo->hCertStore != NULL )
		{
		pCertCloseStore( cryptoapiInfo->hCertStore, 0 );
		cryptoapiInfo->hCertStore = 0;
		}
	cryptoapiInfo->hProv = CRYPT_ERROR;
	deviceInfo->flags &= ~( DEVICE_ACTIVE | DEVICE_LOGGEDIN );

	/* Free the device capability information */
	freeCapabilities( deviceInfo );
	}

/* Open a session with the device */

static int initFunction( DEVICE_INFO *deviceInfo, const char *name,
						 const int nameLength )
	{
	CRYPTOAPI_INFO *cryptoapiInfo = deviceInfo->deviceCryptoAPI;
	HCRYPTPROV hProv;
	HCERTSTORE hCertStore;
	char providerNameBuffer[ CRYPT_MAX_TEXTSIZE + 1 + 8 ];
	char keysetNameBuffer[ CRYPT_MAX_TEXTSIZE + 1 + 8 ];
	const char *keysetName = NULL;
	DWORD value;
	int i, driverNameLength = nameLength, status;

	/* Check whether a keyset name has been specified */
	strlcpy_s( keysetNameBuffer, CRYPT_MAX_TEXTSIZE, "MY" );/* Default keyset */
	for( i = 1; i < nameLength - 1; i++ )
		{
		if( name[ i ] == ':' && name[ i + 1 ] == ':' )
			{
			const int keysetNameLength = nameLength - ( i + 2 );

			if( i > CRYPT_MAX_TEXTSIZE || keysetNameLength <= 0 || \
				keysetNameLength > CRYPT_MAX_TEXTSIZE )
				return( CRYPT_ARGERROR_STR1 );

			/* We've got a keyset name appended to the provider name, break 
			   out the provider and keyset names */
			memcpy( providerNameBuffer, name, i );
			providerNameBuffer[ i ] = '\0';
			memcpy( keysetNameBuffer, name + i + 2, keysetNameLength );
			keysetNameBuffer[ keysetNameLength ] = '\0';
			name = providerNameBuffer;
			keysetName = keysetNameBuffer;
			break;
			}
		}

	/* If we're auto-detecting the device, try various choices */
	if( driverNameLength == 12 && \
		!strnicmp( "[Autodetect]", name, driverNameLength ) )
		{
		if( CryptAcquireContextA( &hProv, keysetName, MS_ENHANCED_PROV, 
								  PROV_RSA_FULL, 0 ) )
			cryptoapiInfo->hProv = hProv;
		else
			if( CryptAcquireContextA( &hProv, keysetName, MS_DEF_PROV, 
									  PROV_RSA_FULL, 0 ) )
				cryptoapiInfo->hProv = hProv;
			else
				return( mapError( cryptoapiInfo, CRYPT_ERROR_NOTFOUND ) );
		}
	else
		{
		/* Try and find a specific provider */
		if( CryptAcquireContextA( &hProv, keysetName, name, PROV_RSA_FULL, 0 ) )
			cryptoapiInfo->hProv = hProv;
		}

	/* Get information on device-specific capabilities */
	value = CRYPT_MAX_TEXTSIZE + 1;
	if( !CryptGetProvParam( cryptoapiInfo->hProv, PP_NAME, 
							cryptoapiInfo->label, &value, 0 ) )
		return( mapError( cryptoapiInfo, CRYPT_ERROR_NOTFOUND ) );
	cryptoapiInfo->labelLen = value;
	deviceInfo->label = cryptoapiInfo->label;
	deviceInfo->labelLen = cryptoapiInfo->labelLen;
	deviceInfo->flags |= DEVICE_ACTIVE;

	/* Set up the capability information for this device */
	status = getCapabilities( deviceInfo );
	if( cryptStatusError( status ) )
		{
		shutdownFunction( deviceInfo );
		return( ( status == CRYPT_ERROR ) ? CRYPT_ERROR_OPEN : status );
		}

	/* Create the special-purpose key needed to allow symmetric key loads */
	status = createExportKey( cryptoapiInfo->hProv, 
							  &cryptoapiInfo->hPrivateKey, 
							  &cryptoapiInfo->privateKeySize );
	if( cryptStatusError( status ) )
		{
		shutdownFunction( deviceInfo );
		return( status );
		}

	/* Open the cert store used to store/retrieve certs */
	hCertStore = pCertOpenSystemStore( cryptoapiInfo->hProv, 
									   keysetNameBuffer );
	if( hCertStore == NULL )
		{
		shutdownFunction( deviceInfo );
		return( CRYPT_ERROR_OPEN );
		}
	cryptoapiInfo->hCertStore = hCertStore;

	return( CRYPT_OK );
	}

/* Handle device control functions */

static int controlFunction( DEVICE_INFO *deviceInfo,
							const CRYPT_ATTRIBUTE_TYPE type,
							const void *data, const int dataLength )
	{
	assert( NOTREACHED );
	return( CRYPT_ERROR_NOTAVAIL );	/* Get rid of compiler warning */
	}

/****************************************************************************
*																			*
*						 	Misc.Device Interface Routines					*
*																			*
****************************************************************************/

/* Get random data from the device */

static int getRandomFunction( DEVICE_INFO *deviceInfo, void *buffer,
							  const int length )
	{
	CRYPTOAPI_INFO *cryptoapiInfo = deviceInfo->deviceCryptoAPI;

	if( pCryptGenRandom( cryptoapiInfo->hProv, length, buffer ) )
		return( CRYPT_OK );
	return( mapError( cryptoapiInfo, CRYPT_ERROR_FAILED ) );
	}

/* Instantiate an object in a device.  This works like the create context
   function but instantiates a cryptlib object using data already contained
   in the device (for example a stored private key or certificate).  If the
   value being read is a public key and there's a certificate attached, the
   instantiated object is a native cryptlib object rather than a device
   object with a native certificate object attached because there doesn't 
   appear to be any good reason to create the public-key object in the 
   device.

   CryptoAPI doesn't have any concept of multiple private keys, only a 
   default encryption + signature key for the provider as a whole, and an 
   optional additional signature key to complement the encryption (and 
   signature if necessary) one.  In addition the ties between a private key 
   and its associated certificate(s) are rather tenuous, requiring jumping 
   through several levels of indirection in order to get from one to the 
   other.  To handle this, we have to use a meet-in-the-middle approach 
   where we try to go from private key to certificate if the identity of the 
   private key is obvious (the user has specifically asked for a private 
   decryption or signature key), or from certificate to private key in all 
   other cases */

static int getItemFunction( DEVICE_INFO *deviceInfo,
							CRYPT_CONTEXT *iCryptContext,
							const KEYMGMT_ITEM_TYPE itemType,
							const CRYPT_KEYID_TYPE keyIDtype,
							const void *keyID, const int keyIDlength,
							void *auxInfo, int *auxInfoLength, 
							const int flags )
	{
	CRYPTOAPI_INFO *cryptoapiInfo = deviceInfo->deviceCryptoAPI;
//	CRYPT_CERTIFICATE iCryptCert;
	HCRYPTKEY hKey = 0;
	PCCERT_CONTEXT pCertContext = NULL;
	PCCERT_CHAIN_CONTEXT pChainContext = NULL;
	DWORD dwKeySpec = 0;
	CRYPT_CERTIFICATE iCryptCert;
	CRYPT_ALGO_TYPE cryptAlgo = CRYPT_ALGO_NONE;
	const char *label = "Private key";
	int status;

	assert( itemType == KEYMGMT_ITEM_PUBLICKEY || \
			itemType == KEYMGMT_ITEM_PRIVATEKEY );

	/* If we're searching for the key by label and the user has specified 
	   one of the special-case key descriptions, get the appropriate key */
	if( keyIDtype == CRYPT_KEYID_NAME )
		{
		if( keyIDlength == 13 && \
			!memcmp( keyID, "Signature key", 13 ) )
			dwKeySpec = AT_SIGNATURE;
		else
			if( keyIDlength == 14 && \
				!memcmp( keyID, "Encryption key", 14 ) )
				dwKeySpec = AT_KEYEXCHANGE;
		}

	/* If we haven't got a key type from the label and we're looking for a 
	   particular usage, get the appropriate key */
	if( dwKeySpec == 0 && itemType == KEYMGMT_ITEM_PRIVATEKEY )
		{
		if( flags & KEYMGMT_FLAG_USAGE_SIGN ) 
			dwKeySpec = AT_SIGNATURE;
		else
			if( flags & KEYMGMT_FLAG_USAGE_CRYPT ) 
				dwKeySpec = AT_KEYEXCHANGE;
		}

	/* If we still haven't got a key type, try and get the certificate for
	   the given ID */
	if( dwKeySpec != 0 )
		{
		/* Get the required key type */
		if( !pCryptGetUserKey( cryptoapiInfo->hProv, dwKeySpec, &hKey ) )
			return( CRYPT_ERROR_NOTFOUND );
		label = ( dwKeySpec == AT_SIGNATURE ) ? \
				"Signature key" : "Encryption key";

		/* If we're only doing a presence check, we don't need the key and 
		   can exit */
		if( flags & KEYMGMT_FLAG_CHECK_ONLY )
			{
			pCryptDestroyKey( hKey ); 
			return( CRYPT_OK );
			}

		/* Since CryptoAPI doesn't have any concept of key labels, the best 
		   that we can do is provide a generic description of the intended 
		   key usage as a form of pseudo-label */
		if( flags & KEYMGMT_FLAG_LABEL_ONLY )
			{
			strlcpy_s( auxInfo, *auxInfoLength, label );
			*auxInfoLength = strlen( label );
			pCryptDestroyKey( hKey ); 
			return( CRYPT_OK );
			}

		/* We've got the key, try and get the associated certificate */
		status = getCertificateFromKey( cryptoapiInfo, hKey, 
										( flags & KEYMGMT_FLAG_USAGE_SIGN ) ? \
											TRUE : FALSE, &pCertContext );
		if( cryptStatusError( status ) && \
			itemType == KEYMGMT_ITEM_PUBLICKEY ) 
			{
			/* We couldn't get a cert for the key, if we're after a public 
			   key return it as a native context */
			status = capiToCryptlibContext( cryptoapiInfo, hKey, 
											iCryptContext );
			pCryptDestroyKey( hKey ); 
			return( status );
			}
		}
	else
		{
		assert( !( flags & KEYMGMT_FLAG_LABEL_ONLY ) );

		/* We don't have a definite private key ID indicator, go via the
		   certificate instead */
		status = getCertificate( cryptoapiInfo, keyIDtype, keyID, keyIDlength,
								 &pCertContext );
		if( cryptStatusError( status ) )
			return( status );

		/* If we're only doing a presence check, we don't need the key and 
		   can exit */
		if( flags & KEYMGMT_FLAG_CHECK_ONLY )
			{
			pCertFreeCertificateContext( pCertContext );
			return( CRYPT_OK );
			}

		/* If we're after a private key, try and find the one corresponding
		   to the cert */
		if( itemType == KEYMGMT_ITEM_PRIVATEKEY )
			{
#if 1
			CERT_KEY_CONTEXT certKeyContext;
			DWORD certKeyContextSize = sizeof( CERT_KEY_CONTEXT );

			if( !pCertGetCertificateContextProperty( pCertContext, 
									CERT_KEY_CONTEXT_PROP_ID,
									&certKeyContext, &certKeyContextSize ) || \
				!pCryptGetUserKey( certKeyContext.hCryptProv, 
								   certKeyContext.dwKeySpec, &hKey ) )
				return( mapError( cryptoapiInfo, CRYPT_ERROR_NOTFOUND ) );

#else		/* Need to uncomment gPKFC() above */
			status = getPrivKeyFromCertificate( cryptoapiInfo, pCertContext, 
												&hKey );
#endif
			if( cryptStatusError( status ) )
				{
				pCertFreeCertificateContext( pCertContext );
				return( status );
				}
			}
		}

	/* If we've got a priavte key, create a device context for it */
	if( hKey != 0 )
		{
		status = createPrivkeyContext( deviceInfo, iCryptContext, 
									   &cryptAlgo, hKey, label );
		if( cryptStatusError( status ) )
			{
			if( pCertContext != NULL )
				pCertFreeCertificateContext( pCertContext );
			return( status );
			}
		}

	/* If there's no certificate available for the key, we're done */
	if( pCertContext == NULL )
		return( CRYPT_OK );

	/* We've got a key and cert, try and get the rest of the chain for the 
	   cert */
{
BOOLEAN publicComponentsOnly = FALSE;

	status = iCryptImportCertIndirect( &iCryptCert,
								deviceInfo->objectHandle, keyIDtype, keyID,
								keyIDlength, publicComponentsOnly ? \
									KEYMGMT_FLAG_NONE : \
									KEYMGMT_FLAG_DATAONLY_CERT );
}

	if( cryptStatusOK( status ) )
		{
		/* If we're getting a public key, the returned info is the cert 
		   chain */
		if( itemType == KEYMGMT_ITEM_PUBLICKEY )
			*iCryptContext = iCryptCert;
		else
			{
			/* If we're getting a private key, attach the cert chain to the 
			   context.  The cert is an internal object used only by the 
			   context so we tell the kernel to mark it as owned by the 
			   context only */
			status = krnlSendMessage( *iCryptContext, IMESSAGE_SETDEPENDENT, 
									   &iCryptCert, SETDEP_OPTION_NOINCREF );
			}
		}
	
	/* Clean up */
	pCertFreeCertificateContext( pCertContext );
	pCertFreeCertificateChain( pChainContext );
	return( status );

#if 0
		{
		const int keyTemplateCount = ( keyID == NULL ) ? 1 : 2;

		/* Try and find the object with the given label/ID, or the first 
		   object of the given class if no ID is given */
		keyTemplate[ 0 ].pValue = ( CK_VOID_PTR ) \
								  ( ( itemType == KEYMGMT_ITEM_PUBLICKEY ) ? \
								  &pubkeyClass : &privkeyClass );
		if( keyIDtype != CRYPT_KEYID_NONE )
			{
			if( keyIDtype == CRYPT_IKEYID_KEYID )
				keyTemplate[ 1 ].type = CKA_ID;
			keyTemplate[ 1 ].pValue = ( CK_VOID_PTR ) keyID;
			keyTemplate[ 1 ].ulValueLen = keyIDlength;
			}
		cryptStatus = findObject( deviceInfo, &hObject, keyTemplate, 
								  keyTemplateCount );
		if( cryptStatus == CRYPT_ERROR_NOTFOUND && \
			itemType == KEYMGMT_ITEM_PUBLICKEY )
			{
			/* Some devices may only contain private key objects with 
			   associated certificates that can't be picked out of the other 
			   cruft that's present without going via the private key, so if 
			   we're looking for a public key and don't find one, we try 
			   again for a private key whose sole function is to point to an 
			   associated cert */
			keyTemplate[ 0 ].pValue = ( CK_VOID_PTR ) &privkeyClass;
			cryptStatus = findObject( deviceInfo, &hObject, keyTemplate, 
									  keyTemplateCount );
			if( cryptStatusError( cryptStatus ) )
				return( cryptStatus );
		
			/* Remember that although we've got a private key object, we only 
			   need it to find the associated cert and not finding an 
			   associated cert is an error */
			certViaPrivateKey = TRUE;
			}
		}

	/* If we're looking for any kind of private key and we either have an
	   explicit cert.ID but couldn't find a cert for it or we don't have a 
	   proper ID to search on and a generic search found more than one 
	   matching object, chances are we're after a generic decrypt key.  The 
	   former only occurs in misconfigured or limited-memory tokens, the 
	   latter only in rare tokens that store more than one private key, 
	   typically one for signing and one for verification.  
	   
	   If either of these cases occur we try again looking specifically for 
	   a decryption key.  Even this doesn't always work, there's are some
	   >1-key tokens that mark a signing key as a decryption key so we still 
	   get a CRYPT_ERROR_DUPLICATE error.
	   
	   Finally, if we can't find a decryption key either, we look for an
	   unwrapping key.  This may or may not work, depending on whether we 
	   have a decryption key marked as valid for unwrapping but not 
	   decryption, or a key that's genuinely only valid for unwrapping, but
	   at this point we're ready to try anything */
	if( itemType == KEYMGMT_ITEM_PRIVATEKEY && \
		( keyIDtype == CRYPT_IKEYID_ISSUERANDSERIALNUMBER && \
		  cryptStatus == CRYPT_ERROR_NOTFOUND ) || \
		( cryptStatus == CRYPT_ERROR_DUPLICATE ) )
		{
		static const CK_BBOOL bTrue = TRUE;
		CK_ATTRIBUTE decryptKeyTemplate[] = {
			{ CKA_CLASS, ( CK_VOID_PTR ) &privkeyClass, sizeof( CK_OBJECT_CLASS ) },
			{ CKA_DECRYPT, ( CK_VOID_PTR ) &bTrue, sizeof( CK_BBOOL ) }
			};

		cryptStatus = findObject( deviceInfo, &hObject, 
								  decryptKeyTemplate, 2 );
		if( cryptStatusError( cryptStatus ) )
			{
			decryptKeyTemplate[ 1 ].type = CKA_UNWRAP;
			cryptStatus = findObject( deviceInfo, &hObject, 
									  decryptKeyTemplate, 2 );
			}
		}
	if( cryptStatusError( cryptStatus ) )
		return( cryptStatus );

	/* If we're just checking whether an object exists, return now.  If all 
	   we want is the key label, copy it back to the caller and exit */
	if( flags & KEYMGMT_FLAG_CHECK_ONLY )
		return( CRYPT_OK );
	if( flags & KEYMGMT_FLAG_LABEL_ONLY )
		return( getObjectLabel( deviceInfo, hObject, auxInfo, 
								auxInfoLength ) );

	/* We found something, map the key type to a cryptlib algorithm ID,
	   determine the key size, and find its capabilities */
	keyTypeTemplate.pValue = &keyType;
	C_GetAttributeValue( cryptoapiInfo->hProv, hObject, 
						 &keyTypeTemplate, 1 );
	switch( ( int ) keyType )
		{
		case CKK_RSA:
			cryptAlgo = CRYPT_ALGO_RSA;
			keySizeTemplate.type = CKA_MODULUS;
			break;
		case CKK_DSA:
			cryptAlgo = CRYPT_ALGO_DSA;
			keySizeTemplate.type = CKA_PRIME;
			break;
		case CKK_DH:
			cryptAlgo = CRYPT_ALGO_DH;
			keySizeTemplate.type = CKA_PRIME;
			break;
		default:
			return( CRYPT_ERROR_NOTAVAIL );
		}
	C_GetAttributeValue( cryptoapiInfo->hProv, hObject, 
						 &keySizeTemplate, 1 );
	keySize = keySizeTemplate.ulValueLen;
	capabilityInfoPtr = findCapabilityInfo( deviceInfo->capabilityInfo, 
											cryptAlgo );
	if( capabilityInfoPtr == NULL )
		return( CRYPT_ERROR_NOTAVAIL );

	/* Try and find a certificate which matches the key.  The process is as
	   follows:

		if cert object found in issuerAndSerialNumber search
			create native data-only cert object
			attach cert object to key
		else
			if public key
				if cert
					create native cert (+context) object
				else
					create device pubkey object, mark as "key loaded"
			else
				create device privkey object, mark as "key loaded"
				if cert
					create native data-only cert object
					attach cert object to key

	   The reason for doing things this way is given in the comments earlier
	   on in this function */
	if( privateKeyViaCert )
		{
		/* We've already got the cert object handle, instantiate a native
		   data-only cert from it */
		cryptStatus = instantiateCert( deviceInfo, hCertificate, 
									   &iCryptCert, FALSE );
		if( cryptStatusError( cryptStatus ) )
			return( cryptStatus );
		certPresent = TRUE;
		}
	else
		{
		cryptStatus = findCertFromObject( deviceInfo, hObject, &iCryptCert, 
										  ( itemType == KEYMGMT_ITEM_PUBLICKEY ) ? \
										  FINDCERT_NORMAL : FINDCERT_DATAONLY );
		if( cryptStatusError( cryptStatus ) )
			{
			/* If we get a CRYPT_ERROR_NOTFOUND this is OK since it means 
			   there's no cert present, however anything else is an error. In 
			   addition if we've got a private key whose only function is to 
			   point to an associated cert then not finding anything is also 
			   an error */
			if( cryptStatus != CRYPT_ERROR_NOTFOUND || certViaPrivateKey )
				return( cryptStatus );
			}
		else
			{
			/* We got the cert, if we're being asked for a public key then
			   we've created a native object to contain it so we return that */
			certPresent = TRUE;
			if( itemType == KEYMGMT_ITEM_PUBLICKEY )
				{
				*iCryptContext = iCryptCert;
				return( CRYPT_OK );
				}
			}
		}
#endif
	}

/* Update a device with a certificate */

static int setItemFunction( DEVICE_INFO *deviceInfo, 
							const CRYPT_HANDLE iCryptHandle )
	{
	CRYPTOAPI_INFO *cryptoapiInfo = deviceInfo->deviceCryptoAPI;
	HCRYPTKEY hKey;
	DWORD dwKeySpec = 0;
	CRYPT_CERTIFICATE iCryptCert;
	BOOLEAN seenNonDuplicate = FALSE;
	int iterationCount = 0, status;

	/* Lock the cert for our exclusive use (in case it's a cert chain, we 
	   also select the first cert in the chain), update the device with the 
	   cert, and unlock it to allow others access */
	krnlSendMessage( iCryptHandle, IMESSAGE_GETDEPENDENT, &iCryptCert, 
					 OBJECT_TYPE_CERTIFICATE );
	krnlSendMessage( iCryptCert, IMESSAGE_SETATTRIBUTE, 
					 MESSAGE_VALUE_CURSORFIRST, 
					 CRYPT_CERTINFO_CURRENT_CERTIFICATE );
	status = krnlSendMessage( iCryptCert, IMESSAGE_SETATTRIBUTE, 
							  MESSAGE_VALUE_TRUE, CRYPT_IATTRIBUTE_LOCKED );
	if( cryptStatusError( status ) )
		return( status );

	/* Check whether the leaf cert matches any of the user's private keys */
	if( pCryptGetUserKey( cryptoapiInfo->hProv, AT_KEYEXCHANGE, &hKey ) )
		{
		if( isCertKey( cryptoapiInfo, hKey, iCryptCert ) )
			dwKeySpec = AT_KEYEXCHANGE;
		pCryptDestroyKey( hKey ); 
		}
	else
		if( pCryptGetUserKey( cryptoapiInfo->hProv, AT_SIGNATURE, &hKey ) )
			{
			if( isCertKey( cryptoapiInfo, hKey, iCryptCert ) )
				dwKeySpec = AT_SIGNATURE;
			pCryptDestroyKey( hKey ); 
			}

	/* Add each cert in the chain to the store */
	do
		{
		DYNBUF certDB;
		BOOL result = FALSE;

		/* Get the certificate data and add it to the store */
		status = dynCreate( &certDB, iCryptHandle, 
							CRYPT_CERTFORMAT_CERTIFICATE );
		if( cryptStatusError( status ) )
			return( status );

		/* If the cert corresponds to one of the user's private keys and
		   a binding between the key and a cert isn't already established,
		   establish one now */
		if( dwKeySpec != 0 )
			{
			PCCERT_CONTEXT pCertContext;
			CERT_KEY_CONTEXT certKeyContext;
			DWORD certKeyContextSize = sizeof( CERT_KEY_CONTEXT );

			/* Check whether the cert is already bound to a key and if not,
			   bind it to the appropriate private key */
			pCertContext = pCertCreateCertificateContext( X509_ASN_ENCODING, 
									dynData( certDB ), dynLength( certDB ) );
			if( pCertContext != NULL && \
				!pCertGetCertificateContextProperty( pCertContext, 
									CERT_KEY_CONTEXT_PROP_ID,
									&certKeyContext, &certKeyContextSize ) )
				{
#if 1
				/* Cert the cert stores synchronising the cert's 
				   CERT_KEY_PROV_INFO_PROP_ID with any present private keys
				   if required */
				pCryptFindCertificateKeyProvInfo( pCertContext, 0, NULL );
#elif 0
				CRYPT_KEY_PROV_INFO keyProvInfo;
				BYTE buffer[ 256 + 8 ];
				DWORD length = 256;
				wchar_t provName[ 256 + 8 ], container[ 256 + 8 ];

				if( pCryptGetProvParam( cryptoapiInfo->hProv, PP_NAME,
										buffer, &length, 0 ) )
					MultiByteToWideChar( GetACP(), 0, buffer, length + 1, 
										 provName, 256 );
				if( pCryptGetProvParam( cryptoapiInfo->hProv, PP_CONTAINER,
										buffer, &length, 0 ) )
					MultiByteToWideChar( GetACP(), 0, buffer, length + 1, 
										 container, 256 );
				memset( &keyProvInfo, 0, sizeof( CRYPT_KEY_PROV_INFO ) );
				keyProvInfo.pwszContainerName = container;
				keyProvInfo.pwszProvName = provName;
				keyProvInfo.dwProvType = PROV_RSA_FULL;
				keyProvInfo.cProvParam = 0;
				keyProvInfo.dwKeySpec = dwKeySpec;
				pCertSetCertificateContextProperty( pCertContext, 
									CERT_KEY_PROV_HANDLE_PROP_ID, 0,
									&keyProvInfo );
#elif 0
				memset( &certKeyContext, 0, sizeof( CERT_KEY_CONTEXT ) );
				certKeyContext.cbSize = sizeof( CERT_KEY_CONTEXT );
				certKeyContext.dwKeySpec = dwKeySpec;
				certKeyContext.hCryptProv = cryptoapiInfo->hProv;
				pCertSetCertificateContextProperty( pCertContext, 
									CERT_KEY_CONTEXT_PROP_ID, 0,
									&certKeyContext );
#endif
				}

			/* Add the cert to the store */
			if( pCertContext != NULL )
				{
				result = pCertAddCertificateContextToStore( cryptoapiInfo->hCertStore,
								pCertContext, CERT_STORE_ADD_NEW, NULL );
				pCertFreeCertificateContext( pCertContext );
				}

			/* We've now added a bound cert, don't bind the key to any more 
			   certs */
			dwKeySpec = 0;
			}
		else
			/* Add the cert to the store */
			result = pCertAddEncodedCertificateToStore( cryptoapiInfo->hCertStore,
							X509_ASN_ENCODING, dynData( certDB ), 
							dynLength( certDB ), CERT_STORE_ADD_NEW, NULL );
		dynDestroy( &certDB );
		if( result )
			seenNonDuplicate = TRUE;
		}
	while( krnlSendMessage( iCryptCert, IMESSAGE_SETATTRIBUTE, 
							MESSAGE_VALUE_CURSORNEXT,
							CRYPT_CERTINFO_CURRENT_CERTIFICATE ) == CRYPT_OK && \
		   iterationCount++ < FAILSAFE_ITERATIONS_MED );
	if( iterationCount >= FAILSAFE_ITERATIONS_MED )
		retIntError();

	krnlSendMessage( iCryptCert, IMESSAGE_SETATTRIBUTE, MESSAGE_VALUE_FALSE, 
					 CRYPT_IATTRIBUTE_LOCKED );

	return( seenNonDuplicate ? CRYPT_OK : CRYPT_ERROR_DUPLICATE );
	}

/* Delete an object in a device */

static int deleteItemFunction( DEVICE_INFO *deviceInfo,
							   const KEYMGMT_ITEM_TYPE itemType,
							   const CRYPT_KEYID_TYPE keyIDtype,
							   const void *keyID, const int keyIDlength )
	{
#if 0
	static const CK_OBJECT_CLASS pubkeyClass = CKO_PUBLIC_KEY;
	static const CK_OBJECT_CLASS privkeyClass = CKO_PRIVATE_KEY;
	static const CK_OBJECT_CLASS certClass = CKO_CERTIFICATE;
	static const CK_CERTIFICATE_TYPE certType = CKC_X_509;
	CK_ATTRIBUTE certTemplate[] = {
		{ CKA_CLASS, ( CK_VOID_PTR ) &certClass, sizeof( CK_OBJECT_CLASS ) },
		{ CKA_CERTIFICATE_TYPE, ( CK_VOID_PTR ) &certType, sizeof( CK_CERTIFICATE_TYPE ) },
		{ CKA_LABEL, ( CK_VOID_PTR ) keyID, keyIDlength }
		};
	CK_ATTRIBUTE keyTemplate[] = {
		{ CKA_CLASS, ( CK_VOID_PTR ) &pubkeyClass, sizeof( CK_OBJECT_CLASS ) },
		{ CKA_LABEL, ( CK_VOID_PTR ) keyID, keyIDlength }
		};
	CK_OBJECT_HANDLE hPrivkey = CRYPT_ERROR, hCertificate = CRYPT_ERROR;
	CK_OBJECT_HANDLE hPubkey = CRYPT_ERROR;
	CK_RV status;
	int cryptStatus;

	assert( itemType == KEYMGMT_ITEM_PUBLICKEY || \
			itemType == KEYMGMT_ITEM_PRIVATEKEY );
	assert( keyIDtype == CRYPT_KEYID_NAME );

	/* Find the object to delete based on the label.  Since we can have 
	   multiple related objects (e.g. a key and a cert) with the same label, 
	   a straight search for all objects with a given label could return
	   CRYPT_ERROR_DUPLICATE so we search for the objects by type as well as 
	   label.  In addition even a search for specific objects can return
	   CRYPT_ERROR_DUPLICATE so we use the Ex version of findObject() to make
	   sure we don't get an error if multiple objects exist.  Although
	   cryptlib won't allow more than one object with a given label to be
	   created, other applications might create duplicate labels.  The correct
	   behaviour in these circumstances is uncertain, what we do for now is
	   delete the first object we find that matches the label.
	   
	   First we try for a cert and use that to find associated keys */
	cryptStatus = findObjectEx( deviceInfo, &hCertificate, certTemplate, 3 );
	if( cryptStatusOK( cryptStatus ) )
		{
		/* We got a cert, if there are associated keys delete them as well */
		cryptStatus = findObjectFromObject( deviceInfo, hCertificate, 
											CKO_PUBLIC_KEY, &hPubkey );
		if( cryptStatusError( cryptStatus ) )
			hPubkey = CRYPT_ERROR;
		cryptStatus = findObjectFromObject( deviceInfo, hCertificate, 
											CKO_PRIVATE_KEY, &hPrivkey );
		if( cryptStatusError( cryptStatus ) )
			hPrivkey = CRYPT_ERROR;
		}
	else
		{
		/* We didn't find a cert with the given label, try for public and
		   private keys */
		cryptStatus = findObjectEx( deviceInfo, &hPubkey, keyTemplate, 2 );
		if( cryptStatusError( cryptStatus ) )
			hPubkey = CRYPT_ERROR;
		keyTemplate[ 0 ].pValue = ( CK_VOID_PTR ) &privkeyClass;
		cryptStatus = findObjectEx( deviceInfo, &hPrivkey, keyTemplate, 2 );
		if( cryptStatusError( cryptStatus ) )
			hPrivkey = CRYPT_ERROR;

		/* There may be an unlabelled cert present, try and find it by 
		   looking for a cert matching the key ID */
		if( hPubkey != CRYPT_ERROR || hPrivkey != CRYPT_ERROR )
			{
			cryptStatus = findObjectFromObject( deviceInfo, 
							( hPrivkey != CRYPT_ERROR ) ? hPrivkey : hPubkey, 
							CKO_CERTIFICATE, &hCertificate );
			if( cryptStatusError( cryptStatus ) )
				hCertificate = CRYPT_ERROR;
			}
		}

	/* If we found a public key with a given label but no private key, try 
	   and find a matching private key by ID, and vice versa */
	if( hPubkey != CRYPT_ERROR && hPrivkey == CRYPT_ERROR )
		{
		cryptStatus = findObjectFromObject( deviceInfo, hPubkey, 
											CKO_PRIVATE_KEY, &hPrivkey );
		if( cryptStatusError( cryptStatus ) )
			hPrivkey = CRYPT_ERROR;
		}
	if( hPrivkey != CRYPT_ERROR && hPubkey == CRYPT_ERROR )
		{
		cryptStatus = findObjectFromObject( deviceInfo, hPrivkey, 
											CKO_PUBLIC_KEY, &hPubkey );
		if( cryptStatusError( cryptStatus ) )
			hPubkey = CRYPT_ERROR;
		}
	if( hPrivkey == CRYPT_ERROR && hPubkey == CRYPT_ERROR )
		return( CRYPT_ERROR_NOTFOUND );

	/* Reset the status values, which may contain error values due to not 
	   finding various objects to delete above */
	cryptStatus = CRYPT_OK;
	status = CKR_OK;

	/* Delete the objects */
	if( hCertificate != CRYPT_ERROR )
		status = C_DestroyObject( cryptoapiInfo->hProv, hCertificate );
	if( hPubkey != CRYPT_ERROR )
		{
		int status2;

		status2 = C_DestroyObject( cryptoapiInfo->hProv, hPubkey );
		if( status2 != CKR_OK && status == CKR_OK )
			status = status2;
		}
	if( hPrivkey != CRYPT_ERROR )
		{
		int status2;

		status2 = C_DestroyObject( cryptoapiInfo->hProv, hPrivkey );
		if( status2 != CKR_OK && status == CKR_OK )
			status = status2;
		}
	if( status != CKR_OK )
		cryptStatus = mapError( deviceInfo, status, CRYPT_ERROR_FAILED );
	return( cryptStatus );
#endif

	return( CRYPT_ERROR );
	}

/* Get the sequence of certs in a chain from a device.  Unfortunately we 
   can't really make the cert chain fetch stateless without re-doing the 
   read of the entire chain from the CryptoAPI cert store for each fetch.
   To avoid this performance hit we cache the chain in the device info and
   pick out each cert in turn during the findNext */

static int getFirstItemFunction( DEVICE_INFO *deviceInfo, 
								 CRYPT_CERTIFICATE *iCertificate,
								 int *stateInfo, 
								 const CRYPT_KEYID_TYPE keyIDtype,
								 const void *keyID, const int keyIDlength,
								 const KEYMGMT_ITEM_TYPE itemType, 
								 const int options )
	{
	CRYPTOAPI_INFO *cryptoapiInfo = deviceInfo->deviceCryptoAPI;
	PCCERT_CONTEXT pCertContext;
	PCCERT_CHAIN_CONTEXT pChainContext;
	MESSAGE_CREATEOBJECT_INFO createInfo;
	int status;

	assert( isWritePtr( iCertificate, sizeof( CRYPT_CERTIFICATE ) ) );
	assert( keyIDtype == CRYPT_IKEYID_KEYID );
	assert( keyIDlength > 4 && isReadPtr( keyID, keyIDlength ) );
	assert( isWritePtr( stateInfo, sizeof( int ) ) );
	assert( itemType == KEYMGMT_ITEM_PUBLICKEY );

	/* Clear return values */
	*iCertificate = CRYPT_ERROR;
	*stateInfo = CRYPT_ERROR;
	cryptoapiInfo->hCertChain = NULL;

	/* Try and find the cert with the given ID.  This should work because 
	   we've just read the ID for the indirect-import that lead to the getFirst
	   call */
	status = getCertificate( cryptoapiInfo, keyIDtype, keyID, keyIDlength,
							 &pCertContext );
	if( cryptStatusError( status ) )
		{
		assert( NOTREACHED );
		return( status );
		}

	/* Now try and get the chain from the cert.  If this fails, we just use 
	   the standalone cert */
	status = getCertificateChain( cryptoapiInfo, pCertContext, 
								  &pChainContext );
	if( cryptStatusOK( status ) )
		cryptoapiInfo->hCertChain = pChainContext;

	/* Import the leaf cert as a cryptlib object */
	setMessageCreateObjectIndirectInfo( &createInfo, 
										pCertContext->pbCertEncoded, 
										pCertContext->cbCertEncoded,
										CRYPT_CERTTYPE_CERTIFICATE );
	createInfo.arg1 = CRYPT_CERTTYPE_CERTIFICATE;
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, 
							  IMESSAGE_DEV_CREATEOBJECT_INDIRECT,
							  &createInfo, OBJECT_TYPE_CERTIFICATE );
	pCertFreeCertificateContext( pCertContext );
	if( cryptStatusError( status ) )
		{
		if( cryptoapiInfo->hCertChain != NULL )
			pCertFreeCertificateChain( cryptoapiInfo->hCertChain );
		return( status );
		}

	/* Remember that we've got the leaf cert in the chain */
	assert( pChainContext->cChain == 1 );
	*stateInfo = 1;
	return( CRYPT_OK );
	}

static int getNextItemFunction( DEVICE_INFO *deviceInfo, 
								CRYPT_CERTIFICATE *iCertificate,
								int *stateInfo, const int options )
	{
	CRYPTOAPI_INFO *cryptoapiInfo = deviceInfo->deviceCryptoAPI;
	PCCERT_CHAIN_CONTEXT pChainContext = cryptoapiInfo->hCertChain;
	PCERT_SIMPLE_CHAIN pCertSimpleChain;
	PCERT_CHAIN_ELEMENT pCertChainElement;
	MESSAGE_CREATEOBJECT_INFO createInfo;
	int status;

	assert( isWritePtr( iCertificate, sizeof( CRYPT_CERTIFICATE ) ) );
	assert( isWritePtr( stateInfo, sizeof( int ) ) );
	assert( *stateInfo == CRYPT_ERROR || *stateInfo >= 1 );

	/* Clear return value */
	*iCertificate = CRYPT_ERROR;

	/* If the previous cert was the last one, there's nothing left to fetch */
	if( cryptoapiInfo->hCertChain == NULL )
		{
		*stateInfo = CRYPT_ERROR;
		return( CRYPT_ERROR_NOTFOUND );
		}
	pCertSimpleChain = pChainContext->rgpChain[ 0 ];
	if( *stateInfo < 1 || *stateInfo > pCertSimpleChain->cElement )
		{
		pCertFreeCertificateChain( cryptoapiInfo->hCertChain );
		cryptoapiInfo->hCertChain = NULL;
		*stateInfo = CRYPT_ERROR;
		return( CRYPT_ERROR_NOTFOUND );
		}

	/* Get the next cert in the chain */
	pCertChainElement = pCertSimpleChain->rgpElement[ *stateInfo ];
	setMessageCreateObjectIndirectInfo( &createInfo, 
							pCertChainElement->pCertContext->pbCertEncoded, 
							pCertChainElement->pCertContext->cbCertEncoded,
							CRYPT_CERTTYPE_CERTIFICATE );
	createInfo.arg1 = CRYPT_CERTTYPE_CERTIFICATE;
	status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, 
							  IMESSAGE_DEV_CREATEOBJECT_INDIRECT,
							  &createInfo, OBJECT_TYPE_CERTIFICATE );
	if( cryptStatusError( status ) )
		{
		pCertFreeCertificateChain( cryptoapiInfo->hCertChain );
		cryptoapiInfo->hCertChain = NULL;
		*stateInfo = CRYPT_ERROR;
		return( status );
		}
	( *stateInfo )++;
	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*						 	Capability Interface Routines					*
*																			*
****************************************************************************/

/* Clean up the object associated with a context */

static int genericEndFunction( CONTEXT_INFO *contextInfoPtr )
	{
	/* Since the device object that corresponds to the cryptlib object is
	   created on-demand, it may not exist yet if the action that triggers
	   the on-demand creation hasn't been taken yet.  If no device object
	   exists, we're done */
	if( contextInfoPtr->deviceObject == CRYPT_ERROR )
		return( CRYPT_OK );

	/* Destroy the object */
	if( contextInfoPtr->capabilityInfo->keySize > 0 )
		pCryptDestroyKey( contextInfoPtr->deviceObject );
	else
		pCryptDestroyHash( contextInfoPtr->deviceObject );
	return( CRYPT_OK );
	}

/* RSA algorithm-specific mapping functions.  Since CryptoAPI adds its own
   PKCS #1 padding, we add/remove the cryptlib-added padding to fake out the 
   presence of a raw RSA mechanism */

static int rsaSetKeyInfo( CRYPTOAPI_INFO *cryptoapiInfo,
						  CONTEXT_INFO *contextInfoPtr )
	{
	BYTE n[ CRYPT_MAX_PKCSIZE + 8 ], e[ CRYPT_MAX_PKCSIZE + 8 ];
	BYTE keyDataBuffer[ ( CRYPT_MAX_PKCSIZE * 2 ) + 8 ];
	MESSAGE_DATA msgData;
	int nLen, eLen, keyDataSize, status;

	/* Extract the public-key components from the CryptoAPI context */
	status = getPubkeyComponents( cryptoapiInfo, 
								  contextInfoPtr->deviceObject, 
								  n, &nLen, e, &eLen );
	if( cryptStatusError( status ) )
		return( status );

	/* Send the public key data to the context.  We send the keying info as
	   CRYPT_IATTRIBUTE_KEY_SPKI_PARTIAL rather than CRYPT_IATTRIBUTE_KEY_SPKI
	   since the latter transitions the context into the high state.  We 
	   don't want to do this because we're already in the middle of processing
	   a message that does this on completion, all we're doing here is 
	   sending in encoded public key data for use by objects such as 
	   certificates */
	status = keyDataSize = writeFlatPublicKey( NULL, 0, CRYPT_ALGO_RSA, 
											   n, nLen, e, eLen, 
											   NULL, 0, NULL, 0 );
	if( !cryptStatusError( status ) )
		status = writeFlatPublicKey( keyDataBuffer, CRYPT_MAX_PKCSIZE * 2,
									 CRYPT_ALGO_RSA, n, nLen, e, eLen, 
									 NULL, 0, NULL, 0 );
	if( cryptStatusOK( status ) )
		krnlSendMessage( contextInfoPtr->objectHandle, 
						 IMESSAGE_SETATTRIBUTE, ( void * ) &nLen, 
						 CRYPT_IATTRIBUTE_KEYSIZE );
	if( cryptStatusOK( status ) )
		{
		setMessageData( &msgData, keyDataBuffer, keyDataSize );
		status = krnlSendMessage( contextInfoPtr->objectHandle, 
								  IMESSAGE_SETATTRIBUTE_S, &msgData, 
								  CRYPT_IATTRIBUTE_KEY_SPKI_PARTIAL );
		}
	return( status );
	}

static int rsaInitKey( CONTEXT_INFO *contextInfoPtr, const void *key, 
					   const int keyLength )
	{
	CRYPT_DEVICE iCryptDevice;
	DEVICE_INFO *deviceInfo;
	CRYPTOAPI_INFO *cryptoapiInfo;
	CRYPT_PKCINFO_RSA *rsaKey = ( CRYPT_PKCINFO_RSA * ) key;
	HCRYPTKEY hKey;
	BLOBHEADER *blobHeaderPtr;
	RSAPUBKEY *pubKeyPtr;
	BYTE keyBlob[ ( CRYPT_MAX_PKCSIZE * 8 ) + 8 ], *keyBlobPtr;
	DWORD exponent = 0L;
	BOOL result;
	const int nLen = bitsToBytes( rsaKey->nLen );
	int i, status;

	/* CryptoAPI sets some awkward constraints on key formats, only allowing
	   exponents that can be represented as 32-bit ints and requiring that
	   the lengths of all MPIs be explicitly specified by the modulus size,
	   so we have to check whether the key components are in this form 
	   before we can try and load the key */
	if( bitsToBytes( rsaKey->eLen ) > sizeof( DWORD ) )
		return( CRYPT_ERROR_BADDATA );
	if( !rsaKey->isPublicKey && \
		( ( bitsToBytes( rsaKey->pLen ) > nLen / 2 ) || \
		  ( bitsToBytes( rsaKey->qLen ) > nLen / 2 ) || \
		  ( bitsToBytes( rsaKey->e1Len ) > nLen / 2 ) || \
		  ( bitsToBytes( rsaKey->e2Len ) > nLen / 2 ) || \
		  ( bitsToBytes( rsaKey->uLen ) > nLen / 2 ) || \
		  ( bitsToBytes( rsaKey->dLen ) > nLen ) ) )
		return( CRYPT_ERROR_BADDATA );

	/* Get the exponent as a DWORD */
	for( i = 0; i < bitsToBytes( rsaKey->eLen ); i++ )
		exponent = ( exponent << 8 ) | rsaKey->e[ i ];

	/* Get the info for the device associated with this context */
	status = krnlSendMessage( contextInfoPtr->objectHandle, IMESSAGE_GETDEPENDENT, 
							  &iCryptDevice, OBJECT_TYPE_DEVICE );
	if( cryptStatusOK( status ) )
		status = krnlAcquireObject( iCryptDevice, OBJECT_TYPE_DEVICE, 
									( void ** ) &deviceInfo, 
									CRYPT_ERROR_SIGNALLED );
	if( cryptStatusError( status ) )
		return( status );
	cryptoapiInfo = deviceInfo->deviceCryptoAPI;
	assert( !( deviceInfo->flags & DEVICE_READONLY ) );

	/* Set up the blob header:

		Ofs	Value
		  0	BLOBHEADER blobheader {
			  0	BYTE bType;
			  1	BYTE bVersion;
			  2 WORD reserved;
			  4	ALG_ID aiKeyAlg; }
		  8	RSAPUBKEY rsapubkey {
			  8 DWORD magic;
			 12	DWORD bitlen;
			 16	DWORD pubexp; }
		 20	BYTE modulus[ rsapubkey.bitlen / 8 ];
			BYTE prime1[ rsapubkey.bitlen / 16 ];
			BYTE prime2[ rsapubkey.bitlen / 16 ];
			BYTE exponent1[ rsapubkey.bitlen / 16 ];
			BYTE exponent2[ rsapubkey.bitlen / 16 ];
			BYTE coefficient[ rsapubkey.bitlen / 16 ];
			BYTE privateExponent[ rsapubkey.bitlen / 8 ]; */
	memset( keyBlob, 0, CRYPT_MAX_PKCSIZE * 8 );
	blobHeaderPtr = ( BLOBHEADER * ) keyBlob;
	blobHeaderPtr->bType = ( rsaKey->isPublicKey ) ? \
						   PUBLICKEYBLOB : PRIVATEKEYBLOB;
	blobHeaderPtr->bVersion = CUR_BLOB_VERSION;
	blobHeaderPtr->aiKeyAlg = CALG_RSA_KEYX;

	/* Set up the public-key components.  CryptoAPI requires that the 
	   modulus length be a multiple of 8 bits, so we have to round up the 
	   length to the nearest byte boundary */
	pubKeyPtr = ( RSAPUBKEY * ) ( keyBlob + BLOBHEADER_SIZE );
	pubKeyPtr->magic = ( rsaKey->isPublicKey ) ? 0x31415352 : 0x32415352;
	pubKeyPtr->bitlen = roundUp( rsaKey->nLen, 8 );
	pubKeyPtr->pubexp = exponent;
	keyBlobPtr = keyBlob + BLOBHEADER_SIZE + ( 3 * sizeof( DWORD ) );
	keyBlobPtr = copyMPI( keyBlobPtr, rsaKey->n, nLen, nLen );

	/* Set up the private-key components if necessary */
	if( !rsaKey->isPublicKey )
		{
		keyBlobPtr = copyMPI( keyBlobPtr, rsaKey->p, 
							  bitsToBytes( rsaKey->pLen ), nLen / 2 );
		keyBlobPtr = copyMPI( keyBlobPtr, rsaKey->q, 
							  bitsToBytes( rsaKey->qLen ), nLen / 2 );
		keyBlobPtr = copyMPI( keyBlobPtr, rsaKey->e1, 
							  bitsToBytes( rsaKey->e1Len ), nLen / 2 );
		keyBlobPtr = copyMPI( keyBlobPtr, rsaKey->e2, 
							  bitsToBytes( rsaKey->e2Len ), nLen / 2 );
		keyBlobPtr = copyMPI( keyBlobPtr, rsaKey->u, 
							  bitsToBytes( rsaKey->uLen ), nLen / 2 );
		keyBlobPtr = copyMPI( keyBlobPtr, rsaKey->d, 
							  bitsToBytes( rsaKey->dLen ), nLen );
		}

	/* Import the key blob and clean up */
	result = pCryptImportKey( cryptoapiInfo->hProv, keyBlob, 
							  keyBlobPtr - keyBlob, 0, 0, &hKey );
	if( result )
		contextInfoPtr->deviceObject = hKey;
	else
		status = mapDeviceError( contextInfoPtr, CRYPT_ERROR_FAILED );
	zeroise( keyBlob, CRYPT_MAX_PKCSIZE * 8 );

	/* Send the keying info to the context and set up the key ID info */
	if( cryptStatusOK( status ) )
		status = rsaSetKeyInfo( cryptoapiInfo, contextInfoPtr );

	krnlReleaseObject( deviceInfo->objectHandle );
	return( status );
	}

static int rsaGenerateKey( CONTEXT_INFO *contextInfoPtr, const int keysizeBits )
	{
	CRYPT_DEVICE iCryptDevice;
	DEVICE_INFO *deviceInfo;
	CRYPTOAPI_INFO *cryptoapiInfo;
	HCRYPTKEY hPrivateKey;
	BOOL result;
	int status;

	/* Get the info for the device associated with this context */
	status = krnlSendMessage( contextInfoPtr->objectHandle, IMESSAGE_GETDEPENDENT, 
							  &iCryptDevice, OBJECT_TYPE_DEVICE );
	if( cryptStatusOK( status ) )
		status = krnlAcquireObject( iCryptDevice, OBJECT_TYPE_DEVICE, 
									( void ** ) &deviceInfo, 
									CRYPT_ERROR_SIGNALLED );
	if( cryptStatusError( status ) )
		return( status );
	cryptoapiInfo = deviceInfo->deviceCryptoAPI;
	assert( !( deviceInfo->flags & DEVICE_READONLY ) );

	/* Generate the key.  CryptoAPI kludges the key size information by 
	   masking it into the high bits of the flags parameter.  We make the
	   key exportable, because the ability to hand out keys in PKCS #12 
	   format is the prime (only?) motivation for anyone using CryptoAPI */
	result = pCryptGenKey( cryptoapiInfo->hProv, AT_KEYEXCHANGE, 
						   CRYPT_EXPORTABLE | ( keysizeBits << 16 ), 
						   &hPrivateKey );
	if( result )
		contextInfoPtr->deviceObject = hPrivateKey;
	else
		status = mapDeviceError( contextInfoPtr, CRYPT_ERROR_FAILED );

	/* Send the keying info to the context and set up the key ID info */
	if( cryptStatusOK( status ) )
		status = rsaSetKeyInfo( cryptoapiInfo, contextInfoPtr );

	krnlReleaseObject( deviceInfo->objectHandle );
	return( status );
	}

static int rsaSign( CONTEXT_INFO *contextInfoPtr, void *buffer, int length )
	{
	CRYPT_DEVICE iCryptDevice;
	DEVICE_INFO *deviceInfo;
	CRYPTOAPI_INFO *cryptoapiInfo;
	CRYPT_ALGO_TYPE cryptAlgo;
	STREAM stream;
	HCRYPTHASH hHash;
	BYTE tempBuffer[ CRYPT_MAX_PKCSIZE + 8 ];
	BYTE hashBuffer[ CRYPT_MAX_HASHSIZE + 8 ], *bufPtr = buffer;
	ALG_ID algID;
	BOOL result;
	int resultLength = length, i, status;

	/* CryptoAPI adds its own PKCS #1 padding, so we have to reverse-engineer
	   the encoding and padding that's already been added */
	assert( bufPtr[ 0 ] == 0 && bufPtr[ 1 ] == 1 );
	for( i = 2; i < length; i++ )
		if( bufPtr[ i ] == 0 )
			break;
	i++;	/* Skip final 0 byte */
	sMemConnect( &stream, bufPtr + i, length - i );
	status = readMessageDigest( &stream, &cryptAlgo, hashBuffer, 
								CRYPT_MAX_HASHSIZE, &i );
	sMemDisconnect( &stream );
	if( cryptStatusError( status ) )
		return( status );
	algID = cryptlibToCapiID( cryptAlgo );
	if( algID == 0 )
		return( CRYPT_ERROR_NOTAVAIL );

	/* Get the info for the device associated with this context */
	status = krnlSendMessage( contextInfoPtr->objectHandle, IMESSAGE_GETDEPENDENT, 
							  &iCryptDevice, OBJECT_TYPE_DEVICE );
	if( cryptStatusOK( status ) )
		status = krnlAcquireObject( iCryptDevice, OBJECT_TYPE_DEVICE, 
									( void ** ) &deviceInfo, 
									CRYPT_ERROR_SIGNALLED );
	if( cryptStatusError( status ) )
		return( status );
	cryptoapiInfo = deviceInfo->deviceCryptoAPI;
	assert( !( deviceInfo->flags & DEVICE_READONLY ) );

	/* CryptoAPI can only sign hash values inside hash objects, luckily 
	   there's a function whose sole purpose is to allow hash values to be 
	   loaded directly into hash objects that we can use to create the hash
	   to be signed.
	   
	   Once we've got the hash though, things get tricky.  CryptSignHash() 
	   doesn't let you specify which key you want to use for signing, but it 
	   does let you indicate that you want to use the default encryption-only 
	   key as your signature key.  This is sort of like building a car where 
	   the steering wheel won't turn, but in compensation the fuel tank will
	   explode if you hit the horn */
	result = pCryptCreateHash( cryptoapiInfo->hProv, algID, 0, 0, &hHash );
	if( !result )
		{
		status = mapDeviceError( contextInfoPtr, CRYPT_ERROR_FAILED );
		krnlReleaseObject( deviceInfo->objectHandle );
		return( status );
		}
	result = pCryptSetHashParam( hHash, HP_HASHVAL, hashBuffer, 0 );
	if( result )
		result = pCryptSignHash( hHash, AT_KEYEXCHANGE, NULL, 0, 
								 tempBuffer, &resultLength );
	pCryptDestroyHash( hHash );
	if( result )
		{
		copyMPI( buffer, tempBuffer, resultLength, resultLength );
		zeroise( tempBuffer, CRYPT_MAX_PKCSIZE + 8 );

		/* Restore any truncated leading zeroes if necessary */
		if( resultLength < length )
			{
			const int delta = length - resultLength;
	
			memmove( ( BYTE * ) buffer + delta, tempBuffer, resultLength );
			memset( buffer, 0, delta );
			}
		}
	if( !result )
		status = mapDeviceError( contextInfoPtr, CRYPT_ERROR_FAILED );

	krnlReleaseObject( deviceInfo->objectHandle );
	return( status );
	}

static int rsaVerify( CONTEXT_INFO *contextInfoPtr, void *buffer, int length )
	{
	/* This function is present but isn't used as part of any normal 
	   operation because cryptlib does the same thing much faster in 
	   software and more importanyly because there's no way to get at the
	   hash information that's needed to create the hash object, so that
	   the trick used in rsaSign() won't work */
	assert( NOTREACHED );

	return( CRYPT_ERROR_FAILED );
	}

static int rsaEncrypt( CONTEXT_INFO *contextInfoPtr, void *buffer, int length )
	{
	BYTE tempBuffer[ CRYPT_MAX_PKCSIZE + 8 ], *tempPtr, *bufPtr = buffer;
	int resultLength, i;

	/* CryptoAPI adds its own PKCS #1 padding, so we have to undo the 
	   padding that's already been added */
	assert( bufPtr[ 0 ] == 0 && bufPtr[ 1 ] == 2 );
	for( i = 2; i < length; i++ )
		if( bufPtr[ i ] == 0 )
			break;
	i++;	/* Skip final 0 byte */

	/* Change the data into the little-endian order required by CryptoAPI, 
	   encrypt it, reverse the order again */
	tempPtr = copyMPI( tempBuffer, bufPtr + i, length - i, length - i );
	resultLength = tempPtr - tempBuffer;
	if( !pCryptEncrypt( contextInfoPtr->deviceObject, 0, TRUE, 0, 
						tempBuffer, &resultLength, length ) )
		{
		zeroise( tempBuffer, CRYPT_MAX_PKCSIZE + 8 );
		return( mapDeviceError( contextInfoPtr, CRYPT_ERROR_FAILED ) );
		}
	copyMPI( buffer, tempBuffer, resultLength, resultLength );
	zeroise( tempBuffer, CRYPT_MAX_PKCSIZE + 8 );

	/* Restore any truncated leading zeroes if necessary */
	if( resultLength < length )
		{
		const int delta = length - resultLength;

		memmove( ( BYTE * ) buffer + delta, tempBuffer, resultLength );
		memset( buffer, 0, delta );
		}
	return( CRYPT_OK );
	}

static int rsaDecrypt( CONTEXT_INFO *contextInfoPtr, void *buffer, int length )
	{
	BYTE tempBuffer[ CRYPT_MAX_PKCSIZE + 8 ], *tempPtr, *bufPtr = buffer;
	int resultLength = length, i;

	/* Change the data into the little-endian order required by CryptoAPI, 
	   decrypt it, and reverse the order again */
	tempPtr = copyMPI( tempBuffer, bufPtr, length, length );
	resultLength = tempPtr - tempBuffer;
	if( !pCryptDecrypt( contextInfoPtr->deviceObject, 0, FALSE, 0, 
						tempBuffer, &resultLength ) )
		{
		zeroise( tempBuffer, CRYPT_MAX_PKCSIZE + 8 );
		return( mapDeviceError( contextInfoPtr, CRYPT_ERROR_FAILED ) );
		}
	copyMPI( buffer, tempBuffer, resultLength, resultLength );
	zeroise( tempBuffer, CRYPT_MAX_PKCSIZE + 8 );

	/* Redo the PKCS #1 padding that was stripped by CryptoAPI */
	memmove( bufPtr + length - resultLength, bufPtr, resultLength );
	bufPtr[ 0 ] = 0;
	bufPtr[ 1 ] = 2;
	for( i = 2; i < length - resultLength - 1; i++ )
		bufPtr[ i ] = 0xA5;
	bufPtr[ i ] = 0;
	assert( i + 1 + resultLength == length );
	return( CRYPT_OK );
	}

/* DSA algorithm-specific mapping functions */

#if 0

static int dsaSetKeyInfo( DEVICE_INFO *deviceInfo, CONTEXT_INFO *contextInfoPtr, 
//						  const CK_OBJECT_HANDLE hPrivateKey,
//						  const CK_OBJECT_HANDLE hPublicKey,
						  const void *p, const int pLen,
						  const void *q, const int qLen,
						  const void *g, const int gLen,
						  const void *y, const int yLen )
	{
	MESSAGE_DATA msgData;
	BYTE keyDataBuffer[ ( CRYPT_MAX_PKCSIZE * 2 ) + 8 ];
	BYTE idBuffer[ KEYID_SIZE + 8 ];
	int keyDataSize, cryptStatus;

	/* Send the public key data to the context.  We send the keying info as
	   CRYPT_IATTRIBUTE_KEY_SPKI_PARTIAL rather than CRYPT_IATTRIBUTE_KEY_SPKI
	   since the latter transitions the context into the high state.  We 
	   don't want to do this because we're already in the middle of processing
	   a message that does this on completion, all we're doing here is 
	   sending in encoded public key data for use by objects such as 
	   certificates */
	cryptStatus = keyDataSize = writeFlatPublicKey( NULL, 0, CRYPT_ALGO_DSA, 
													p, pLen, q, qLen, 
													g, gLen, y, yLen );
	if( !cryptStatusError( cryptStatus ) )
		cryptStatus = writeFlatPublicKey( keyDataBuffer, CRYPT_MAX_PKCSIZE * 2,
										  CRYPT_ALGO_DSA, p, pLen, q, qLen, 
										  g, gLen, y, yLen );
	if( !cryptStatusError( cryptStatus ) )
		cryptStatus = krnlSendMessage( contextInfoPtr->objectHandle, 
									   IMESSAGE_SETATTRIBUTE, 
									   ( void * ) &pLen, CRYPT_IATTRIBUTE_KEYSIZE );
	if( cryptStatusOK( cryptStatus ) )
		{
		setMessageData( &msgData, keyDataBuffer, keyDataSize );
		cryptStatus = krnlSendMessage( contextInfoPtr->objectHandle, 
									   IMESSAGE_SETATTRIBUTE_S, &msgData, 
									   CRYPT_IATTRIBUTE_KEY_SPKI_PARTIAL );
		}
	if( cryptStatusError( cryptStatus ) )
		return( cryptStatus );

	/* Remember what we've set up */
	krnlSendMessage( contextInfoPtr->objectHandle, IMESSAGE_SETATTRIBUTE,
					 ( void * ) &hPrivateKey, CRYPT_IATTRIBUTE_DEVICEOBJECT );

	/* Get the key ID from the context and use it as the object ID.  Since 
	   some objects won't allow after-the-even ID updates, we don't treat a
	   failure to update as an error */
	setMessageData( &msgData, idBuffer, KEYID_SIZE );
	cryptStatus = krnlSendMessage( contextInfoPtr->objectHandle, 
								   IMESSAGE_GETATTRIBUTE_S, &msgData, 
								   CRYPT_IATTRIBUTE_KEYID );
	if( cryptStatusOK( cryptStatus ) )
		{
		CK_ATTRIBUTE idTemplate = { CKA_ID, msgData.data, msgData.length };

		if( hPublicKey != CRYPT_UNUSED )
			C_SetAttributeValue( cryptoapiInfo->hProv, hPublicKey, 
								 &idTemplate, 1 );
		C_SetAttributeValue( cryptoapiInfo->hProv, hPrivateKey, 
							 &idTemplate, 1 );
		}
	
	return( cryptStatus );
	return( CRYPT_ERROR );
	}
#endif /* 0 */

static int dsaInitKey( CONTEXT_INFO *contextInfoPtr, const void *key, 
					   const int keyLength )
	{
	CRYPT_DEVICE iCryptDevice;
	DEVICE_INFO *deviceInfo;
	CRYPTOAPI_INFO *cryptoapiInfo;
	CRYPT_PKCINFO_DLP *dlpKey = ( CRYPT_PKCINFO_DLP * ) key;
	HCRYPTKEY hKey;
	BLOBHEADER *blobHeaderPtr;
	DSSPUBKEY *pubKeyPtr;
	BYTE keyBlob[ ( CRYPT_MAX_PKCSIZE * 4 ) + 8 ], *keyBlobPtr;
	BOOL result;
	const int pLen = bitsToBytes( dlpKey->pLen );
	int status;

	/* CryptoAPI sets some awkward constraints on key formats, only allowing
	   a prime size up to 1024 bits, so we have to check whether the key 
	   components are in this form before we can try and load the key */
	if( dlpKey->pLen > 1024 )
		return( CRYPT_ERROR_BADDATA );

	/* Get the info for the device associated with this context */
	status = krnlSendMessage( contextInfoPtr->objectHandle, IMESSAGE_GETDEPENDENT, 
							  &iCryptDevice, OBJECT_TYPE_DEVICE );
	if( cryptStatusOK( status ) )
		status = krnlAcquireObject( iCryptDevice, OBJECT_TYPE_DEVICE, 
									( void ** ) &deviceInfo, 
									CRYPT_ERROR_SIGNALLED );
	if( cryptStatusError( status ) )
		return( status );
	cryptoapiInfo = deviceInfo->deviceCryptoAPI;
	assert( !( deviceInfo->flags & DEVICE_READONLY ) );

	/* Set up the blob header:

		Ofs	Value
		  0	BLOBHEADER blobheader {
			  0	BYTE bType;
			  1	BYTE bVersion;
			  2 WORD reserved;
			  4	ALG_ID aiKeyAlg; }
		  8	DSSPUBKEY_VER3 dsspubkey {
			  8 DWORD magic;
			 12	DWORD bitlen; }
		 16	BYTE p[ 128 ];
			BYTE q[ 20 ];
			BYTE g[ 128 ];
			BYTE y[ 128 ]; */
	memset( keyBlob, 0, CRYPT_MAX_PKCSIZE * 4 );
	blobHeaderPtr = ( BLOBHEADER * ) keyBlob;
	blobHeaderPtr->bType = ( dlpKey->isPublicKey ) ? \
						   PUBLICKEYBLOB : PRIVATEKEYBLOB;
	blobHeaderPtr->bVersion = CUR_BLOB_VERSION;
	blobHeaderPtr->aiKeyAlg = CALG_DSS_SIGN;

	/* Set up the public-key components.  CryptoAPI requires that the 
	   modulus length be a multiple of 8 bits, so we have to round up the 
	   length to the nearest byte boundary */
	pubKeyPtr = ( DSSPUBKEY * ) ( keyBlob + BLOBHEADER_SIZE );
	pubKeyPtr->magic = 0x31535344;
	pubKeyPtr->bitlen = roundUp( dlpKey->pLen, 8 );
	keyBlobPtr = keyBlob + BLOBHEADER_SIZE + ( 2 * sizeof( DWORD ) );
	keyBlobPtr = copyMPI( keyBlobPtr, dlpKey->p, pLen, 128 );
	keyBlobPtr = copyMPI( keyBlobPtr, dlpKey->q, 
						  bitsToBytes( dlpKey->qLen ), 20 );
	keyBlobPtr = copyMPI( keyBlobPtr, dlpKey->g, 
						  bitsToBytes( dlpKey->gLen ), 128 );
	keyBlobPtr = copyMPI( keyBlobPtr, dlpKey->y, 
						  bitsToBytes( dlpKey->yLen ), 128 );

	/* Set up the private-key components if necessary */
	if( !dlpKey->isPublicKey )
		{
		}

	/* Import the key blob and clean up */
	result = pCryptImportKey( cryptoapiInfo->hProv, keyBlob, 
							  keyBlobPtr - keyBlob, 0, 0, &hKey );
	if( result )
		contextInfoPtr->deviceObject = hKey;
	else
		status = mapDeviceError( contextInfoPtr, CRYPT_ERROR_FAILED );
	zeroise( keyBlob, CRYPT_MAX_PKCSIZE * 4 );

	krnlReleaseObject( deviceInfo->objectHandle );
	return( status );
	}

static int dsaGenerateKey( CONTEXT_INFO *contextInfoPtr, const int keysizeBits )
	{
#if 0
	static const CK_MECHANISM mechanism = { CKM_DSA_KEY_PAIR_GEN, NULL_PTR, 0 };
	static const CK_BBOOL bTrue = TRUE;
	const CK_ULONG modulusBits = keysizeBits;
	CK_ATTRIBUTE privateKeyTemplate[] = {
		{ CKA_TOKEN, ( CK_VOID_PTR ) &bTrue, sizeof( CK_BBOOL ) },
		{ CKA_PRIVATE, ( CK_VOID_PTR ) &bTrue, sizeof( CK_BBOOL ) },
		{ CKA_SENSITIVE, ( CK_VOID_PTR ) &bTrue, sizeof( CK_BBOOL ) },
		{ CKA_LABEL, contextInfoPtr->label, contextInfoPtr->labelSize },
		{ CKA_SIGN, ( CK_VOID_PTR ) &bTrue, sizeof( CK_BBOOL ) },
		};
	CK_ATTRIBUTE publicKeyTemplate[] = {
		{ CKA_TOKEN, ( CK_VOID_PTR ) &bTrue, sizeof( CK_BBOOL ) },
		{ CKA_LABEL, contextInfoPtr->label, contextInfoPtr->labelSize },
		{ CKA_VERIFY, ( CK_VOID_PTR ) &bTrue, sizeof( CK_BBOOL ) },
		{ CKA_PRIME, NULL, 0 },
		{ CKA_SUBPRIME, NULL, 0 },
		{ CKA_BASE, NULL, 0 },
		};
	CK_ATTRIBUTE yValueTemplate = { CKA_VALUE, NULL, CRYPT_MAX_PKCSIZE * 2 };
	CK_OBJECT_HANDLE hPublicKey, hPrivateKey;
	MESSAGE_CREATEOBJECT_INFO createInfo;
	MESSAGE_DATA msgData;
	CRYPT_DEVICE iCryptDevice;
	DEVICE_INFO *deviceInfo;
	BYTE pubkeyBuffer[ ( CRYPT_MAX_PKCSIZE * 2 ) + 8 ], label[ 8 + 8 ];
	CK_RV status;
	STREAM stream;
	long length;
	int keyLength = bitsToBytes( keysizeBits ), cryptStatus;

	/* CKM_DSA_KEY_PAIR_GEN is really a Clayton's key generation mechanism 
	   since it doesn't actually generate the p, q, or g values (presumably 
	   it dates back to the original FIPS 186 shared domain parameters idea).
	   Because of this we'd have to generate half the key ourselves in a 
	   native context, then copy portions from the native context over in 
	   flat form and complete the keygen via the device.  The easiest way to
	   do this is to create a native DSA context, generate a key, grab the
	   public portions, and destroy the context again (i.e. generate a full
	   key on a superscalar 2GHz RISC CPU, then throw half of it away, and 
	   regenerate it on a 5MHz 8-bit tinkertoy).  Since the keygen can take 
	   awhile and doesn't require the device, we do it before we grab the 
	   device */
	setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_DSA );
	cryptStatus = krnlSendMessage( SYSTEM_OBJECT_HANDLE, 
								   IMESSAGE_DEV_CREATEOBJECT, &createInfo, 
								   OBJECT_TYPE_CONTEXT );
	if( cryptStatusError( cryptStatus ) )
		return( cryptStatus );
	setMessageData( &msgData, label, 8 );
	krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S, 
					 &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE );
	krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE_S,
					 &msgData, CRYPT_CTXINFO_LABEL );
	krnlSendMessage( createInfo.cryptHandle, IMESSAGE_SETATTRIBUTE,
					 ( int * ) &keyLength, CRYPT_CTXINFO_KEYSIZE );
	cryptStatus = krnlSendMessage( createInfo.cryptHandle, 
								   IMESSAGE_CTX_GENKEY, NULL, FALSE );
	if( cryptStatusOK( cryptStatus ) )
		{
		setMessageData( &msgData, pubkeyBuffer, CRYPT_MAX_PKCSIZE * 2 );
		cryptStatus = krnlSendMessage( createInfo.cryptHandle, 
									   IMESSAGE_GETATTRIBUTE_S, &msgData, 
									   CRYPT_IATTRIBUTE_KEY_SPKI );
		}
	krnlSendNotifier( createInfo.cryptHandle, IMESSAGE_DECREFCOUNT );
	if( cryptStatusError( cryptStatus ) )
		return( cryptStatus );

	/* Set up the public key info by extracting the flat values from the
	   SubjectPublicKeyInfo.  Note that the data used is represented in
	   DER-canonical form, there may be PKCS #11 implementations that
	   can't handle this (for example they may require q to be zero-padded
	   to make it exactly 20 bytes rather than (say) 19 bytes if the high
	   byte is zero) */
	sMemConnect( &stream, pubkeyBuffer, msgData.length );
	readSequence( &stream, NULL );				/* SEQUENCE */
	readSequence( &stream, NULL );					/* SEQUENCE */
	readUniversal( &stream );							/* OID */
	readSequence( &stream, NULL );						/* SEQUENCE */
	readGenericHole( &stream, &length );					/* p */
	publicKeyTemplate[ 3 ].pValue = sMemBufPtr( &stream );
	publicKeyTemplate[ 3 ].ulValueLen = length;
	sSkip( &stream, length );
	readGenericHole( &stream, &length );					/* q */
	publicKeyTemplate[ 4 ].pValue = sMemBufPtr( &stream );
	publicKeyTemplate[ 4 ].ulValueLen = length;
	sSkip( &stream, length );
	readGenericHole( &stream, &length );					/* g */
	publicKeyTemplate[ 5 ].pValue = sMemBufPtr( &stream );
	publicKeyTemplate[ 5 ].ulValueLen = length;
	assert( sStatusOK( &stream ) );
	sMemDisconnect( &stream );

	/* Get the info for the device associated with this context */
	cryptStatus = krnlSendMessage( contextInfoPtr->objectHandle, 
								   IMESSAGE_GETDEPENDENT, &iCryptDevice, 
								   OBJECT_TYPE_DEVICE );
	if( cryptStatusOK( cryptStatus ) )
		cryptStatus = krnlAcquireObject( iCryptDevice, OBJECT_TYPE_DEVICE, 
										 ( void ** ) &deviceInfo, 
										 CRYPT_ERROR_SIGNALLED );
	if( cryptStatusError( cryptStatus ) )
		return( cryptStatus );
	assert( !( deviceInfo->flags & DEVICE_READONLY ) );

	/* Generate the keys */
	status = C_GenerateKeyPair( cryptoapiInfo->hProv,
								( CK_MECHANISM_PTR ) &mechanism,
								( CK_ATTRIBUTE_PTR ) publicKeyTemplate, 5,
								( CK_ATTRIBUTE_PTR ) privateKeyTemplate, 4,
								&hPublicKey, &hPrivateKey );
	cryptStatus = mapError( deviceInfo, status, CRYPT_ERROR_FAILED );
	if( cryptStatusError( cryptStatus ) )
		{
		krnlReleaseObject( deviceInfo->objectHandle );
		return( cryptStatus );
		}

	/* Read back the generated y value, send the public key info to the 
	   context, and set up the key ID info.  The odd two-phase y value read 
	   is necessary for buggy implementations that fail if the given size 
	   isn't exactly the same as the data size */
	status = C_GetAttributeValue( cryptoapiInfo->hProv, hPublicKey,
								  &yValueTemplate, 1 );
	if( status == CKR_OK )
		{
		yValueTemplate.pValue = pubkeyBuffer;
		status = C_GetAttributeValue( cryptoapiInfo->hProv, hPublicKey, 
									  &yValueTemplate, 1 );
		}
	cryptStatus = mapError( deviceInfo, status, CRYPT_ERROR_FAILED );
	if( cryptStatusOK( cryptStatus ) )
		cryptStatus = dsaSetKeyInfo( deviceInfo, contextInfoPtr, 
			hPrivateKey, hPublicKey,
			publicKeyTemplate[ 3 ].pValue, publicKeyTemplate[ 3 ].ulValueLen, 
			publicKeyTemplate[ 4 ].pValue, publicKeyTemplate[ 4 ].ulValueLen, 
			publicKeyTemplate[ 5 ].pValue, publicKeyTemplate[ 5 ].ulValueLen,
			yValueTemplate.pValue, yValueTemplate.ulValueLen );
	if( cryptStatusError( cryptStatus ) )
		{
		C_DestroyObject( cryptoapiInfo->hProv, hPublicKey );
		C_DestroyObject( cryptoapiInfo->hProv, hPrivateKey );
		}

	krnlReleaseObject( deviceInfo->objectHandle );
	return( cryptStatus );
#endif /* 0 */
	return( CRYPT_ERROR );
	}

static int dsaSign( CONTEXT_INFO *contextInfoPtr, void *buffer, int length )
	{
#if 0
	static const CK_MECHANISM mechanism = { CKM_DSA, NULL_PTR, 0 };
	CRYPT_DEVICE iCryptDevice;
	DLP_PARAMS *dlpParams = ( DLP_PARAMS * ) buffer;
	DEVICE_INFO *deviceInfo;
	BIGNUM *r, *s;
	BYTE signature[ 40 + 8 ];
	int cryptStatus;

	assert( length == sizeof( DLP_PARAMS ) );
	assert( dlpParams->inParam1 != NULL && \
			dlpParams->inLen1 == 20 );
	assert( dlpParams->inParam2 == NULL && dlpParams->inLen2 == 0 );
	assert( dlpParams->outParam != NULL && \
			dlpParams->outLen >= ( 2 + 20 ) * 2 );

	/* Get the info for the device associated with this context */
	cryptStatus = krnlSendMessage( contextInfoPtr->objectHandle, 
								   IMESSAGE_GETDEPENDENT, &iCryptDevice, 
								   OBJECT_TYPE_DEVICE );
	if( cryptStatusOK( cryptStatus ) )
		cryptStatus = krnlAcquireObject( iCryptDevice, OBJECT_TYPE_DEVICE, 
										 ( void ** ) &deviceInfo, 
										 CRYPT_ERROR_SIGNALLED );
	if( cryptStatusError( cryptStatus ) )
		return( cryptStatus );
	cryptStatus = genericSign( deviceInfo, contextInfoPtr, &mechanism, 
							   dlpParams->inParam1, dlpParams->inLen1,
							   signature, 40 );
	krnlReleaseObject( deviceInfo->objectHandle );
	if( cryptStatusError( cryptStatus ) )
		return( cryptStatus );

	/* Encode the result as a DL data block.  We have to do this as via 
	   bignums, but this isn't a big deal since DSA signing via tokens is
	   almost never used */
	r = BN_new();
	s = BN_new();
	if( r != NULL && s != NULL )
		{
		BN_bin2bn( signature, 20, r );
		BN_bin2bn( signature + 20, 20, s );
		cryptStatus = encodeDLValues( dlpParams->outParam, dlpParams->outLen, 
									  r, s, dlpParams->formatType );
		if( !cryptStatusError( cryptStatus ) )
			{
			dlpParams->outLen = cryptStatus;
			cryptStatus = CRYPT_OK;	/* encodeDLValues() returns a byte count */
			}
		BN_clear_free( s );
		BN_clear_free( r );
		}
	return( cryptStatus );
#endif /* 0 */
	return( CRYPT_ERROR );
	}

static int dsaVerify( CONTEXT_INFO *contextInfoPtr, void *buffer, int length )
	{
#if 0
	static const CK_MECHANISM mechanism = { CKM_DSA, NULL_PTR, 0 };
	CRYPT_DEVICE iCryptDevice;
	DLP_PARAMS *dlpParams = ( DLP_PARAMS * ) buffer;
	DEVICE_INFO *deviceInfo;
	BIGNUM *r, *s;
	BYTE signature[ 40 + 8 ];
	int cryptStatus;

	/* This function is present but isn't used as part of any normal 
	   operation because cryptlib does the same thing much faster in 
	   software and because some tokens don't support public-key 
	   operations */

	assert( length == sizeof( DLP_PARAMS ) );
	assert( dlpParams->inParam1 != NULL && dlpParams->inLen1 == 20 );
	assert( dlpParams->inParam2 != NULL && \
			( ( dlpParams->formatType == CRYPT_FORMAT_CRYPTLIB && \
				dlpParams->inLen2 >= 46 ) || \
			  ( dlpParams->formatType == CRYPT_FORMAT_PGP && \
				dlpParams->inLen2 == 44 ) || \
				( dlpParams->formatType == CRYPT_IFORMAT_SSH && \
				dlpParams->inLen2 == 40 ) ) );
	assert( dlpParams->outParam == NULL && dlpParams->outLen == 0 );

	/* Decode the values from a DL data block and make sure r and s are
	   valid */
	cryptStatus = decodeDLValues( dlpParams->inParam2, dlpParams->inLen2, 
								  &r, &s, dlpParams->formatType );
	if( cryptStatusError( cryptStatus ) )
		return( cryptStatus );

	/* This code can never be called, since DSA public-key contexts are 
	   always native contexts */
	assert( NOTREACHED );

	/* Get the info for the device associated with this context */
	cryptStatus = krnlSendMessage( contextInfoPtr->objectHandle, 
								   IMESSAGE_GETDEPENDENT, &iCryptDevice, 
								   OBJECT_TYPE_DEVICE );
	if( cryptStatusOK( cryptStatus ) )
		cryptStatus = krnlAcquireObject( iCryptDevice, OBJECT_TYPE_DEVICE, 
										 ( void ** ) &deviceInfo, 
										 CRYPT_ERROR_SIGNALLED );
	if( cryptStatusError( cryptStatus ) )
		return( cryptStatus );
	cryptStatus = genericVerify( deviceInfo, contextInfoPtr, &mechanism, buffer,
								 20, signature, 40 );
	krnlReleaseObject( deviceInfo->objectHandle );
	return( cryptStatus );
#endif /* 0 */
	return( CRYPT_ERROR );
	}

/* Conventional cipher-specific mapping functions */

static int cipherInitKey( CONTEXT_INFO *contextInfoPtr, const void *key, 
						  const int keyLength )
	{
	CRYPT_DEVICE iCryptDevice;
	DEVICE_INFO *deviceInfo;
	CRYPTOAPI_INFO *cryptoapiInfo;
	HCRYPTKEY hSessionKey;
	int keySize = keyLength, status;

	/* Get the info for the device associated with this context */
	status = krnlSendMessage( contextInfoPtr->objectHandle, IMESSAGE_GETDEPENDENT, 
							  &iCryptDevice, OBJECT_TYPE_DEVICE );
	if( cryptStatusOK( status ) )
		status = krnlAcquireObject( iCryptDevice, OBJECT_TYPE_DEVICE, 
									( void ** ) &deviceInfo, 
									CRYPT_ERROR_SIGNALLED );
	if( cryptStatusError( status ) )
		return( status );
	cryptoapiInfo = deviceInfo->deviceCryptoAPI;
	assert( !( deviceInfo->flags & DEVICE_READONLY ) );

	/* Copy the key to internal storage */
	if( contextInfoPtr->ctxConv->userKey != key )
		memcpy( contextInfoPtr->ctxConv->userKey, key, keyLength );
	contextInfoPtr->ctxConv->userKeyLength = keyLength;

	/* Special-case handling for 2-key vs.3-key 3DES */
	if( contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_3DES )
		{
		/* If the supplied key contains only two DES keys, adjust the key to
		   make it the equivalent of 3-key 3DES.  In addition since the
		   nominal keysize is for 2-key 3DES, we have to make the actual size
		   the maximum size, corresponding to 3-key 3DES */
		if( keyLength <= bitsToBytes( 64 * 2 ) )
			memcpy( contextInfoPtr->ctxConv->userKey + bitsToBytes( 64 * 2 ),
					contextInfoPtr->ctxConv->userKey, bitsToBytes( 64 ) );
		keySize = contextInfoPtr->capabilityInfo->maxKeySize;
		}

	/* Import the key via the hideous decrypt-with-exponent-one RSA key 
	   kludge */
	status = importPlainKey( cryptoapiInfo->hProv, 
							 cryptoapiInfo->hPrivateKey, 
							 cryptoapiInfo->privateKeySize, &hSessionKey, 
							 contextInfoPtr->capabilityInfo->cryptAlgo, 
							 key, keySize, contextInfoPtr );
	if( cryptStatusOK( status ) )
		contextInfoPtr->deviceObject = hSessionKey;

	krnlReleaseObject( deviceInfo->objectHandle );
	return( status );
	}

/* Set up algorithm-specific encryption parameters */

static int initCryptParams( CONTEXT_INFO *contextInfoPtr )
	{
	enum { CAPI_CRYPT_MODE_NONE, CAPI_CRYPT_MODE_CBC, 
		   CAPI_CRYPT_MODE_ECB, CAPI_CRYPT_MODE_OFB,
		   CAPI_CRYPT_MODE_CFB };
	const CRYPT_MODE_TYPE mode = contextInfoPtr->ctxConv->mode;
	DWORD dwMode;

	/* If it's a native stream cipher (rather than a block cipher being run
	   in stream cipher mode), there's nothing to do */
	if( isStreamCipher( contextInfoPtr->capabilityInfo->cryptAlgo ) )
		return( CRYPT_OK );

	/* Make sure that the values from the CryptoAPI header aren't 
	   overriding the cryptlib values */
	assert( CRYPT_MODE_ECB == 1 );
	assert( CRYPT_MODE_CBC == 2 );
	assert( CRYPT_MODE_CFB == 3 );
	assert( CRYPT_MODE_OFB == 4 );

	/* CryptoAPI uses the same mode names as cryptlib but different values, 
	   so we have to override the naming with our own names here and then 
	   map the cryptlib values to the CryptoAPI ones */
	switch( mode )
		{
		case CRYPT_MODE_ECB:
			dwMode = CAPI_CRYPT_MODE_ECB;
			break;
		case CRYPT_MODE_CBC:
			dwMode = CAPI_CRYPT_MODE_CBC;
			break;
		case CRYPT_MODE_CFB:
			dwMode = CAPI_CRYPT_MODE_CFB;
			break;
		case CRYPT_MODE_OFB:
			dwMode = CAPI_CRYPT_MODE_OFB;
			break;
		default:
			assert( NOTREACHED );
		}
		
	/* Set the mode parameter for the CryptoAPI object */
	if( !CryptSetKeyParam( contextInfoPtr->deviceObject, KP_MODE,
						   ( BYTE * ) &dwMode, 0 ) )
		return( mapDeviceError( contextInfoPtr, CRYPT_ERROR_NOTAVAIL ) );
	if( mode == CRYPT_MODE_CFB || mode == CRYPT_MODE_OFB )
		{
		const DWORD dwModeBits = contextInfoPtr->capabilityInfo->blockSize * 8;

		/* CryptoAPI defaults to 8-bit feedback for CFB and OFB (!!) so we 
		   have to fix the feedback amount if we're using a stream mode */
		if( !CryptSetKeyParam( contextInfoPtr->deviceObject, KP_MODE_BITS,
							   ( BYTE * ) &dwModeBits, 0 ) )
			return( mapDeviceError( contextInfoPtr, CRYPT_ERROR_NOTAVAIL ) );
		}

	/* If there's no IV present, we're done */
	if( mode == CRYPT_MODE_ECB )
		return( CRYPT_OK );

	/* Set the IV parameter for the CryptoAPI object */
	if( !CryptSetKeyParam( contextInfoPtr->deviceObject, KP_IV,
						   contextInfoPtr->ctxConv->currentIV, 0 ) )
		return( mapDeviceError( contextInfoPtr, CRYPT_ERROR_FAILED ) );

	return( CRYPT_OK );
	}

/* En/decrypt/hash data */

static int cipherEncrypt( CONTEXT_INFO *contextInfoPtr, void *buffer, int length )
	{
	int resultLength = length;

	/* Finalise the encryption parameters if necessary.  We couldn't do this
	   earlier because the device-level object isn't instantiated until the 
	   key is loaded */
	if( !( contextInfoPtr->flags & CONTEXT_DUMMY_INITED ) )
		{
		int status;

		status = initCryptParams( contextInfoPtr );
		if( cryptStatusError( status ) )
			return( status );
		contextInfoPtr->flags |= CONTEXT_DUMMY_INITED;
		}

	/* Encrypt the data.  We always set the bFinal flag to FALSE since 
	   setting it to TRUE tries to apply message padding, resets the IV, and 
	   various other unwanted side-effects */
	if( !pCryptEncrypt( contextInfoPtr->deviceObject, 0, FALSE, 0, buffer, 
						&resultLength, length ) )
		return( mapDeviceError( contextInfoPtr, CRYPT_ERROR_FAILED ) );
	return( CRYPT_OK );
	}

static int cipherDecrypt( CONTEXT_INFO *contextInfoPtr, void *buffer, int length )
	{
	int resultLength = length;

	/* Finalise the encryption parameters if necessary.  We couldn't do this
	   earlier because the device-level object isn't instantiated until the 
	   key is loaded */
	if( !( contextInfoPtr->flags & CONTEXT_DUMMY_INITED ) )
		{
		int status;

		status = initCryptParams( contextInfoPtr );
		if( cryptStatusError( status ) )
			return( status );
		contextInfoPtr->flags |= CONTEXT_DUMMY_INITED;
		}

	/* Decrypt the data.  We always set the bFinal flag to FALSE since 
	   setting it to TRUE tries to process message padding, resets the IV, 
	   and various other unwanted side-effects */
	if( !pCryptDecrypt( contextInfoPtr->deviceObject, 0, FALSE, 0, buffer, 
						&resultLength ) )
		return( mapDeviceError( contextInfoPtr, CRYPT_ERROR_FAILED ) );
	return( CRYPT_OK );
	}

#if 0	/* Not used, see the comment in the capability info */

static int hashFunction( CONTEXT_INFO *contextInfoPtr, void *buffer, int length )
	{
	if( !pCryptHashData( contextInfoPtr->deviceObject, buffer, length, 0 ) )
		return( mapDeviceError( contextInfoPtr, CRYPT_ERROR_FAILED ) );
	return( CRYPT_OK );
	}
#endif /* 0 */

/****************************************************************************
*																			*
*						 	Device Capability Routines						*
*																			*
****************************************************************************/

/* Templates for the various capabilities.  These contain only basic 
   information, the remaining fields are filled in when the capability is 
   set up */

static CAPABILITY_INFO FAR_BSS capabilityTemplates[] = {
	/* Encryption capabilities */
	{ CRYPT_ALGO_DES, bitsToBytes( 64 ), "DES",
		bitsToBytes( 40 ), bitsToBytes( 64 ), bitsToBytes( 64 ) },
	{ CRYPT_ALGO_3DES, bitsToBytes( 64 ), "3DES",
		bitsToBytes( 64 + 8 ), bitsToBytes( 128 ), bitsToBytes( 192 ) },
	{ CRYPT_ALGO_IDEA, bitsToBytes( 64 ), "IDEA",
		bitsToBytes( 40 ), bitsToBytes( 128 ), bitsToBytes( 128 ) },
	{ CRYPT_ALGO_CAST, bitsToBytes( 64 ), "CAST-128",
		bitsToBytes( 40 ), bitsToBytes( 128 ), bitsToBytes( 128 ) },
	{ CRYPT_ALGO_RC2, bitsToBytes( 64 ), "RC2",
		bitsToBytes( 40 ), bitsToBytes( 128 ), bitsToBytes( 1024 ) },
	{ CRYPT_ALGO_RC4, bitsToBytes( 8 ), "RC4",
		bitsToBytes( 40 ), bitsToBytes( 128 ), 256 },
	{ CRYPT_ALGO_RC5, bitsToBytes( 64 ), "RC5",
		bitsToBytes( 40 ), bitsToBytes( 128 ), bitsToBytes( 832 ) },
	{ CRYPT_ALGO_AES, bitsToBytes( 128 ), "AES",
		bitsToBytes( 128 ), bitsToBytes( 128 ), bitsToBytes( 256 ) },
	{ CRYPT_ALGO_SKIPJACK, bitsToBytes( 64 ), "Skipjack",
		bitsToBytes( 80 ), bitsToBytes( 80 ), bitsToBytes( 80 ) },

	/* Hash capabilities */
	{ CRYPT_ALGO_MD2, bitsToBytes( 128 ), "MD2",
		bitsToBytes( 0 ), bitsToBytes( 0 ), bitsToBytes( 0 ) },
	{ CRYPT_ALGO_MD4, bitsToBytes( 128 ), "MD4",
		bitsToBytes( 0 ), bitsToBytes( 0 ), bitsToBytes( 0 ) },
	{ CRYPT_ALGO_MD5, bitsToBytes( 128 ), "MD5",
		bitsToBytes( 0 ), bitsToBytes( 0 ), bitsToBytes( 0 ) },
	{ CRYPT_ALGO_SHA, bitsToBytes( 160 ), "SHA",
		bitsToBytes( 0 ), bitsToBytes( 0 ), bitsToBytes( 0 ) },
	{ CRYPT_ALGO_RIPEMD160, bitsToBytes( 160 ), "RIPEMD-160",
		bitsToBytes( 0 ), bitsToBytes( 0 ), bitsToBytes( 0 ) },

	/* Public-key capabilities */
	{ CRYPT_ALGO_RSA, bitsToBytes( 0 ), "RSA",
		bitsToBytes( 512 ), bitsToBytes( 1024 ), CRYPT_MAX_PKCSIZE },
	{ CRYPT_ALGO_DSA, bitsToBytes( 0 ), "DSA",
		bitsToBytes( 512 ), bitsToBytes( 1024 ), CRYPT_MAX_PKCSIZE },

	/* Hier ist der Mast zu ende */
	{ CRYPT_ERROR }, { CRYPT_ERROR }
	};

/* Mapping of CryptoAPI provider capabilities to cryptlib capabilities */

typedef struct {
	/* Mapping information from CryptoAPI to cryptlib algorithms.  For some
	   PKC algorithms CryptoAPI creates two virtual algorithm types (badly,
	   it's easily confused between the two), one for signing and one for 
	   encryption.  The first algorithm type is always the one with 
	   encryption capability, if there's one with signature capability or 
	   it's a signature-only algorithm we specify it as the optional 
	   alternative algorithm type */
	const ALG_ID algoID;				/* CryptoAPI algorithm type */
	const ALG_ID altAlgoID;				/* CryptoAPI alt.algorithm type */
	const CRYPT_ALGO_TYPE cryptAlgo;	/* cryptlib algo and mode */
	const CRYPT_MODE_TYPE cryptMode;

	/* Function pointers */
	int ( *endFunction )( CONTEXT_INFO *contextInfoPtr );
	int ( *initKeyFunction )( CONTEXT_INFO *contextInfoPtr, const void *key, const int keyLength );
	int ( *generateKeyFunction )( CONTEXT_INFO *contextInfoPtr, const int keySizeBits );
	int ( *encryptFunction )( CONTEXT_INFO *contextInfoPtr, void *buffer, int length );
	int ( *decryptFunction )( CONTEXT_INFO *contextInfoPtr, void *buffer, int length );
	int ( *signFunction )( CONTEXT_INFO *contextInfoPtr, void *buffer, int length );
	int ( *sigCheckFunction )( CONTEXT_INFO *contextInfoPtr, void *buffer, int length );
	} MECHANISM_INFO;

static const MECHANISM_INFO mechanismInfo[] = {
	{ CALG_RSA_KEYX, CALG_RSA_SIGN, CRYPT_ALGO_RSA, CRYPT_MODE_NONE, 
	  NULL, rsaInitKey, rsaGenerateKey, 
	  rsaEncrypt, rsaDecrypt, rsaSign, rsaVerify },
	{ CRYPT_ERROR, CALG_DSS_SIGN, CRYPT_ALGO_DSA, CRYPT_MODE_NONE, 
	  NULL, dsaInitKey, dsaGenerateKey, 
	  NULL, NULL, dsaSign, dsaVerify },
	{ CALG_DES, CRYPT_ERROR, CRYPT_ALGO_DES, CRYPT_MODE_ECB, 
	  genericEndFunction, cipherInitKey, NULL, 
	  cipherEncrypt, cipherDecrypt, NULL, NULL },
	{ CALG_DES, CRYPT_ERROR, CRYPT_ALGO_DES, CRYPT_MODE_CBC, 
	  genericEndFunction, cipherInitKey, NULL, 
	  cipherEncrypt, cipherDecrypt, NULL, NULL },
	{ CALG_3DES, CRYPT_ERROR, CRYPT_ALGO_3DES, CRYPT_MODE_ECB, 
	  genericEndFunction, cipherInitKey, NULL, 
	  cipherEncrypt, cipherDecrypt, NULL, NULL },
	{ CALG_3DES, CRYPT_ERROR, CRYPT_ALGO_3DES, CRYPT_MODE_CBC, 
	  genericEndFunction, cipherInitKey, NULL, 
	  cipherEncrypt, cipherDecrypt, NULL, NULL },
	{ CALG_RC2, CRYPT_ERROR, CRYPT_ALGO_RC2, CRYPT_MODE_ECB, 
	  genericEndFunction, cipherInitKey, NULL, 
	  cipherEncrypt, cipherDecrypt, NULL, NULL },
	{ CALG_RC2, CRYPT_ERROR, CRYPT_ALGO_RC2, CRYPT_MODE_CBC, 
	  genericEndFunction, cipherInitKey, NULL, 
	  cipherEncrypt, cipherDecrypt, NULL, NULL },
	{ CALG_RC4, CRYPT_ERROR, CRYPT_ALGO_RC4, CRYPT_MODE_OFB, 
	  genericEndFunction, cipherInitKey, NULL, 
	  cipherEncrypt, cipherDecrypt, NULL, NULL },
	{ CALG_SKIPJACK, CRYPT_ERROR, CRYPT_ALGO_SKIPJACK, CRYPT_MODE_ECB,
	  genericEndFunction, cipherInitKey, NULL, 
	  cipherEncrypt, cipherDecrypt, NULL, NULL },
	{ CALG_SKIPJACK, CRYPT_ERROR, CRYPT_ALGO_SKIPJACK, CRYPT_MODE_CBC,
	  genericEndFunction, cipherInitKey, NULL, 
	  cipherEncrypt, cipherDecrypt, NULL, NULL },
#if 0	/* Although CAPI supports the hash mechanisms, as with PKCS #11
		   we always use cryptlib native contexts for this */
	{ CALG_MD2, CRYPT_ERROR, CRYPT_ALGO_MD2, CRYPT_MODE_NONE,
	  genericEndFunction, NULL, NULL, 
	  hashFunction, hashFunction, NULL, NULL },
	{ CALG_MD4, CRYPT_ERROR, CRYPT_ALGO_MD4, CRYPT_MODE_NONE,
	  genericEndFunction, NULL, NULL, 
	  hashFunction, hashFunction, NULL, NULL },
	{ CALG_MD5, CRYPT_ERROR, CRYPT_ALGO_MD5, CRYPT_MODE_NONE,
	  genericEndFunction, NULL, NULL, 
	  hashFunction, hashFunction, NULL, NULL },
	{ CALG_SHA1, CRYPT_ERROR, CRYPT_ALGO_SHA, CRYPT_MODE_NONE,
	  genericEndFunction, NULL, NULL, 
	  hashFunction, hashFunction, NULL, NULL },
#endif /* 0 */
	{ CRYPT_ERROR, CRYPT_ERROR, CRYPT_ALGO_NONE, CRYPT_MODE_NONE },
		{ CRYPT_ERROR, CRYPT_ERROR, CRYPT_ALGO_NONE, CRYPT_MODE_NONE }
	};

/* Fill out a capability info based on CryptoAPI algorithm info */

static CAPABILITY_INFO *addCapability( const DEVICE_INFO *deviceInfo,
									   const PROV_ENUMALGS_EX *capiAlgoInfo,
									   const MECHANISM_INFO *mechanismInfoPtr,
									   const CAPABILITY_INFO *existingCapabilityInfo )
	{
	VARIABLE_CAPABILITY_INFO *capabilityInfo = \
					( VARIABLE_CAPABILITY_INFO * ) existingCapabilityInfo;
	int minKeySize, maxKeySize, i;

	/* If it's a new capability, copy across the template for this 
	   capability */
	if( capabilityInfo == NULL )
		{
		if( ( capabilityInfo = \
				clAlloc( "addCapability", sizeof( CAPABILITY_INFO ) ) ) == NULL )
			return( NULL );
		for( i = 0; 
			 capabilityTemplates[ i ].cryptAlgo != mechanismInfoPtr->cryptAlgo && \
				capabilityTemplates[ i ].cryptAlgo != CRYPT_ERROR && \
				i < FAILSAFE_ARRAYSIZE( capabilityTemplates, CAPABILITY_INFO ); \
			 i++ );
		if( i >= FAILSAFE_ARRAYSIZE( capabilityTemplates, CAPABILITY_INFO ) )
			retIntError_Null();
		assert( i < sizeof( capabilityTemplates ) / sizeof( CAPABILITY_INFO ) && \
				capabilityTemplates[ i ].cryptAlgo != CRYPT_ERROR );
		memcpy( capabilityInfo, &capabilityTemplates[ i ],
				sizeof( CAPABILITY_INFO ) );
		}

	/* Set up the keysize information, limiting the maximum key size to 
	   match the cryptlib native max.key size, both for consistency and 
	   because cryptlib performs buffer allocation based on the maximum 
	   native buffer size.  Since CryptoAPI specifies key sizes for unkeyed 
	   hash algorithms, we only set the keysize if there's really a key
	   present.  In addition it indicates the number of bits involved in 
	   keying rather than the nominal key size, so we have to adjust the
	   reported size to match the conventionally-used value */
	if( capabilityInfo->keySize > 0 )
		{
		minKeySize = bitsToBytes( capiAlgoInfo->dwMinLen );
		maxKeySize = bitsToBytes( capiAlgoInfo->dwMaxLen );
		if( mechanismInfoPtr->cryptAlgo == CRYPT_ALGO_DES && \
			minKeySize == 7 )
			/* Adjust 56 bits -> 8 bytes */
			minKeySize = maxKeySize = 8;
		if( mechanismInfoPtr->cryptAlgo == CRYPT_ALGO_3DES && \
			minKeySize == 21 )
			/* Adjust 168 bits -> 24 bytes */
			minKeySize = maxKeySize = 24;
		if( minKeySize > capabilityInfo->minKeySize )
			capabilityInfo->minKeySize = minKeySize;
		if( capabilityInfo->keySize < capabilityInfo->minKeySize )
			capabilityInfo->keySize = capabilityInfo->minKeySize;
		capabilityInfo->maxKeySize = min( maxKeySize, 
										  capabilityInfo->maxKeySize );
		if( capabilityInfo->keySize > capabilityInfo->maxKeySize )
			capabilityInfo->keySize = capabilityInfo->maxKeySize;
		capabilityInfo->endFunction = genericEndFunction;
		}

	/* Set up the device-specific handlers */
	capabilityInfo->getInfoFunction = getDefaultInfo;
	if( mechanismInfoPtr->cryptAlgo != CRYPT_ALGO_RSA && \
		mechanismInfoPtr->cryptAlgo != CRYPT_ALGO_DSA )
		capabilityInfo->initKeyParamsFunction = initKeyParams;
	capabilityInfo->endFunction = mechanismInfoPtr->endFunction;
	capabilityInfo->initKeyFunction = mechanismInfoPtr->initKeyFunction;
	capabilityInfo->generateKeyFunction = mechanismInfoPtr->generateKeyFunction;
	if( mechanismInfoPtr->algoID == capiAlgoInfo->aiAlgid )
		{
		if( mechanismInfoPtr->cryptMode == CRYPT_MODE_OFB )
			/* Stream ciphers have an implicit mode of OFB */
			capabilityInfo->encryptOFBFunction = mechanismInfoPtr->encryptFunction;
		else
			capabilityInfo->encryptFunction = mechanismInfoPtr->encryptFunction;
		if( mechanismInfoPtr->cryptMode == CRYPT_MODE_OFB )
			/* Stream ciphers have an implicit mode of OFB */
			capabilityInfo->decryptOFBFunction = mechanismInfoPtr->decryptFunction;
		else
			capabilityInfo->decryptFunction = mechanismInfoPtr->decryptFunction;
		if( mechanismInfoPtr->cryptMode != CRYPT_MODE_NONE && \
			mechanismInfoPtr->cryptMode != CRYPT_MODE_OFB )
			{
			capabilityInfo->encryptCBCFunction = \
										mechanismInfoPtr->encryptFunction;
			capabilityInfo->decryptCBCFunction = \
										mechanismInfoPtr->decryptFunction;
#if 0		/* CAPI requires the encryption of full blocks even in a stream 
			   cipher mode, which doesn't match the standard cryptlib 
			   behaviour.  To avoid this problem, we mark the stream cipher
			   modes as not available */
			capabilityInfo->encryptCFBFunction = \
										mechanismInfoPtr->encryptFunction;
			capabilityInfo->decryptCFBFunction = \
										mechanismInfoPtr->decryptFunction;
			capabilityInfo->encryptOFBFunction = \
										mechanismInfoPtr->encryptFunction;
			capabilityInfo->decryptOFBFunction = \
										mechanismInfoPtr->decryptFunction;
#endif /* 0 */
			}
		}
	if( mechanismInfoPtr->altAlgoID == capiAlgoInfo->aiAlgid )
		{
		capabilityInfo->signFunction = mechanismInfoPtr->signFunction;
		capabilityInfo->sigCheckFunction = mechanismInfoPtr->sigCheckFunction;
		}

	return( ( CAPABILITY_INFO * ) capabilityInfo );
	}

/* Set the capability information based on device capabilities.  Since
   CryptoAPI devices can have assorted capabilities, we have to build this 
   up on the fly rather than using a fixed table like the built-in 
   capabilities */

static void freeCapabilities( DEVICE_INFO *deviceInfo )
	{
	CAPABILITY_INFO_LIST *capabilityInfoListPtr = \
				( CAPABILITY_INFO_LIST * ) deviceInfo->capabilityInfoList;

	/* If the list was empty, return now */
	if( capabilityInfoListPtr == NULL )
		return;
	deviceInfo->capabilityInfoList = NULL;

	while( capabilityInfoListPtr != NULL )
		{
		CAPABILITY_INFO_LIST *listItemToFree = capabilityInfoListPtr;
		CAPABILITY_INFO *itemToFree = ( CAPABILITY_INFO * ) listItemToFree->info;

		capabilityInfoListPtr = capabilityInfoListPtr->next;
		zeroise( itemToFree, sizeof( CAPABILITY_INFO ) );
		clFree( "freeCapabilities", itemToFree );
		zeroise( listItemToFree, sizeof( CAPABILITY_INFO_LIST ) );
		clFree( "freeCapabilities", listItemToFree );
		}
	}

static int getCapabilities( DEVICE_INFO *deviceInfo )
	{
	CRYPTOAPI_INFO *cryptoapiInfo = deviceInfo->deviceCryptoAPI;
	CAPABILITY_INFO_LIST *capabilityInfoListTail = \
				( CAPABILITY_INFO_LIST * ) deviceInfo->capabilityInfoList;
	PROV_ENUMALGS_EX capiAlgoInfo;
	int length = sizeof( PROV_ENUMALGS_EX ), iterationCount = 0;

	assert( sizeof( CAPABILITY_INFO ) == sizeof( VARIABLE_CAPABILITY_INFO ) );

	/* Step through each available CryptoAPI algorithm type adding the 
	   appropriate cryptlib capability for it */
	if( !pCryptGetProvParam( cryptoapiInfo->hProv, PP_ENUMALGS_EX, 
							 ( BYTE * ) &capiAlgoInfo, &length, CRYPT_FIRST ) )
		return( CRYPT_ERROR );
	do
		{
		CAPABILITY_INFO_LIST *newCapabilityList, *capabilityInfoListPtr;
		CAPABILITY_INFO *newCapability;
		CRYPT_ALGO_TYPE cryptAlgo;
		int i;

		/* Check whether this algorithm type corresponds to a cryptlib
		   capability */
		for( i = 0; mechanismInfo[ i ].cryptAlgo != CRYPT_ALGO_NONE && \
					i < FAILSAFE_ARRAYSIZE( mechanismInfo, MECHANISM_INFO ); 
			 i++ )
			{
			if( mechanismInfo[ i ].algoID == capiAlgoInfo.aiAlgid || \
				( mechanismInfo[ i ].altAlgoID != CRYPT_ERROR && \
				  mechanismInfo[ i ].altAlgoID == capiAlgoInfo.aiAlgid ) )
				break;
			}
		if( i >= FAILSAFE_ARRAYSIZE( mechanismInfo, MECHANISM_INFO ) )
			retIntError();
		if( mechanismInfo[ i ].cryptAlgo == CRYPT_ALGO_NONE )
			continue;
		cryptAlgo = mechanismInfo[ i ].cryptAlgo;

		/* Check whether this is a variation of an existing capability */
		for( capabilityInfoListPtr = ( CAPABILITY_INFO_LIST * ) \
									 deviceInfo->capabilityInfoList; 
			 capabilityInfoListPtr != NULL && \
				capabilityInfoListPtr->info->cryptAlgo != cryptAlgo; 
			 capabilityInfoListPtr = capabilityInfoListPtr->next );
		if( capabilityInfoListPtr != NULL )
			{
			addCapability( deviceInfo, &capiAlgoInfo, &mechanismInfo[ i ], 
						   capabilityInfoListPtr->info );
			continue;
			}

		/* Add capabilities for all mechanisms corresponding to the current
		   CryptoAPI algorithm type.  If the assertion below triggers then 
		   the CryptoAPI provider is broken since it's returning 
		   inconsistent information such as illegal key length data, 
		   conflicting algorithm information, etc etc.  This assertion is 
		   included here to detect buggy drivers early on rather than 
		   forcing users to step through the CryptoAPI glue code to find out 
		   why an operation is failing.
		   
		   Because some providers mapped down to tinkertoy smart cards 
		   support only the bare minimum functionality (e.g.RSA private key 
		   ops and nothing else), we allow asymmetric functionality for 
		   PKCs */
		newCapability = addCapability( deviceInfo, &capiAlgoInfo, 
									   &mechanismInfo[ i ], NULL );
		if( newCapability == NULL )
			break;
		assert( capabilityInfoOK( newCapability, 
					( newCapability->cryptAlgo >= CRYPT_ALGO_FIRST_PKC && \
					  newCapability->cryptAlgo <= CRYPT_ALGO_LAST_PKC ) ? \
					  TRUE : FALSE ) );
		if( ( newCapabilityList = \
						clAlloc( "getCapabilities", \
								 sizeof( CAPABILITY_INFO_LIST ) ) ) == NULL )
			{
			clFree( "getCapabilities", newCapability );
			continue;
			}
		newCapabilityList->info = newCapability;
		newCapabilityList->next = NULL;
		if( deviceInfo->capabilityInfoList == NULL )
			deviceInfo->capabilityInfoList = newCapabilityList;
		else
			capabilityInfoListTail->next = newCapabilityList;
		capabilityInfoListTail = newCapabilityList;
		}
	while( pCryptGetProvParam( cryptoapiInfo->hProv, PP_ENUMALGS_EX, 
							   ( BYTE * ) &capiAlgoInfo, &length, 0 ) && \
		   iterationCount++ < FAILSAFE_ITERATIONS_LARGE );
	if( iterationCount >= FAILSAFE_ITERATIONS_LARGE )
		retIntError();

	return( ( deviceInfo->capabilityInfoList == NULL ) ? CRYPT_ERROR : CRYPT_OK );
	}

/****************************************************************************
*																			*
*						 	Device Access Routines							*
*																			*
****************************************************************************/

/* Mechanisms supported by CryptoAPI devices.  These are actually cryptlib 
   native mechanisms since many aren't supported by CryptoAPI, but not the 
   full set supported by the system device since functions like private key 
   export aren't available except in the nonstandard blob format invented 
   by Microsoft.  The list is sorted in order of frequency of use in order 
   to make lookups a bit faster */

static const FAR_BSS MECHANISM_FUNCTION_INFO mechanismFunctions[] = {
	{ MESSAGE_DEV_EXPORT, MECHANISM_ENC_PKCS1, ( MECHANISM_FUNCTION ) exportPKCS1 },
	{ MESSAGE_DEV_IMPORT, MECHANISM_ENC_PKCS1, ( MECHANISM_FUNCTION ) importPKCS1 },
	{ MESSAGE_DEV_SIGN, MECHANISM_SIG_PKCS1, ( MECHANISM_FUNCTION ) signPKCS1 },
	{ MESSAGE_DEV_SIGCHECK, MECHANISM_SIG_PKCS1, ( MECHANISM_FUNCTION ) sigcheckPKCS1 },
	{ MESSAGE_DEV_EXPORT, MECHANISM_ENC_PKCS1_RAW, ( MECHANISM_FUNCTION ) exportPKCS1 },
	{ MESSAGE_DEV_IMPORT, MECHANISM_ENC_PKCS1_RAW, ( MECHANISM_FUNCTION ) importPKCS1 },
#ifdef USE_PGP
	{ MESSAGE_DEV_EXPORT, MECHANISM_ENC_PKCS1_PGP, ( MECHANISM_FUNCTION ) exportPKCS1PGP },
	{ MESSAGE_DEV_IMPORT, MECHANISM_ENC_PKCS1_PGP, ( MECHANISM_FUNCTION ) importPKCS1PGP },
#endif /* USE_PGP */
	{ MESSAGE_DEV_EXPORT, MECHANISM_ENC_CMS, ( MECHANISM_FUNCTION ) exportCMS },
	{ MESSAGE_DEV_IMPORT, MECHANISM_ENC_CMS, ( MECHANISM_FUNCTION ) importCMS },
	{ MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_PKCS5, ( MECHANISM_FUNCTION ) derivePKCS5 },
#if defined( USE_PGP ) || defined( USE_PGPKEYS )
	{ MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_PGP, ( MECHANISM_FUNCTION ) derivePGP },
#endif /* USE_PGP || USE_PGPKEYS */
#ifdef USE_SSL
	{ MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_SSL, ( MECHANISM_FUNCTION ) deriveSSL },
	{ MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_TLS, ( MECHANISM_FUNCTION ) deriveTLS },
	{ MESSAGE_DEV_SIGN, MECHANISM_SIG_SSL, ( MECHANISM_FUNCTION ) signSSL },
	{ MESSAGE_DEV_SIGCHECK, MECHANISM_SIG_SSL, ( MECHANISM_FUNCTION ) sigcheckSSL },
#endif /* USE_SSL */
#ifdef USE_CMP
	{ MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_CMP, ( MECHANISM_FUNCTION ) deriveCMP },
#endif /* USE_CMP */
#ifdef USE_PKCS12
	{ MESSAGE_DEV_DERIVE, MECHANISM_DERIVE_PKCS12, ( MECHANISM_FUNCTION ) derivePKCS12 },
#endif /* USE_PKCS12 */
	{ MESSAGE_NONE, MECHANISM_NONE, NULL }, { MESSAGE_NONE, MECHANISM_NONE, NULL }
	};

/* Set up the function pointers to the device methods */

int setDeviceCryptoAPI( DEVICE_INFO *deviceInfo, const char *name, 
						const int nameLength )
	{
	/* Make sure that the CryptoAPI driver DLL is loaded */
	if( hCryptoAPI == NULL_HINSTANCE )
		return( CRYPT_ERROR_OPEN );

	deviceInfo->initFunction = initFunction;
	deviceInfo->shutdownFunction = shutdownFunction;
	deviceInfo->controlFunction = controlFunction;
	deviceInfo->getItemFunction = getItemFunction;
	deviceInfo->setItemFunction = setItemFunction;
	deviceInfo->deleteItemFunction = deleteItemFunction;
	deviceInfo->getFirstItemFunction = getFirstItemFunction;
	deviceInfo->getNextItemFunction = getNextItemFunction;
	deviceInfo->getRandomFunction = getRandomFunction;
	deviceInfo->mechanismFunctions = mechanismFunctions;
	deviceInfo->mechanismFunctionCount = \
		FAILSAFE_ARRAYSIZE( mechanismFunctions, MECHANISM_FUNCTION_INFO );

	return( CRYPT_OK );
	}
#endif /* USE_CRYPTOAPI */
