/****************************************************************************
*																			*
*					cryptlib PKCS #11 Init/Shutdown Routines				*
*						Copyright Peter Gutmann 1998-2005					*
*																			*
****************************************************************************/

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

#ifdef USE_PKCS11

/* The max. number of drivers we can work with and the max.number of slots
   per driver */

#define MAX_PKCS11_DRIVERS		5
#define MAX_PKCS11_SLOTS		16

/* The default slot to look for tokens in */

#define DEFAULT_SLOT			0

/* Define the following to explicitly link each PKCS #11 function rather than
   using C_GetFunctionList().  Note that this will require changes to the way
   the functions are accessed, since currently it's done via a global function
   pointer table in the pkcs11Info struct */

/* #define USE_EXPLICIT_LINKING */

/* Prototypes for functions in pkcs11.c */

time_t getTokenTime( CK_TOKEN_INFO *tokenInfo );
int genericEndFunction( CONTEXT_INFO *contextInfoPtr );

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

/* Get random data from the device */

static int getRandomFunction( DEVICE_INFO *deviceInfo, void *buffer,
							  const int length )
	{
	CK_RV status;
	PKCS11_INFO *pkcs11Info = deviceInfo->devicePKCS11;

	status = C_GenerateRandom( pkcs11Info->hSession, buffer, length );
	return( pkcs11MapError( pkcs11Info, status, CRYPT_ERROR_FAILED ) );
	}

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

/* Whether the PKCS #11 library has been initialised or not, this is
   initialised on demand the first time it's accessed */

static BOOLEAN pkcs11Initialised = FALSE;

#ifdef DYNAMIC_LOAD

/* Since we can be using multiple PKCS #11 drivers, we define an array of
   them and access the appropriate one by name */

typedef struct {
	char name[ 32 + 1 + 8 ];		/* Name of device */
	INSTANCE_HANDLE hPKCS11;		/* Handle to driver */
	CK_FUNCTION_LIST_PTR functionListPtr;	/* Driver access info */
#ifdef USE_EXPLICIT_LINKING
	CK_C_CloseSession pC_CloseSession;	/* Interface function pointers */
	CK_C_CreateObject pC_CreateObject;
	CK_C_Decrypt pC_Decrypt;
	CK_C_DecryptInit pC_DecryptInit;
	CK_C_DestroyObject pC_DestroyObject;
	CK_C_Encrypt pC_Encrypt;
	CK_C_EncryptInit pC_EncryptInit;
	CK_C_Finalize pC_Finalize;
	CK_C_FindObjects pC_FindObjects;
	CK_C_FindObjectsFinal pC_FindObjectsFinal;
	CK_C_FindObjectsInit pC_FindObjectsInit;
	CK_C_GenerateKey pC_GenerateKey;
	CK_C_GenerateKeyPair pC_GenerateKeyPair;
	CK_C_GenerateRandom pC_GenerateRandom;
	CK_C_GetAttributeValue pC_GetAttributeValue;
	CK_C_GetMechanismInfo pC_GetMechanismInfo;
	CK_C_GetSlotInfo pC_GetSlotInfo;
	CK_C_GetSlotList pC_GetSlotList;
	CK_C_GetTokenInfo pC_GetTokenInfo;
	CK_C_InitPIN pC_InitPIN;
	CK_C_InitToken pC_InitToken;
	CK_C_Login pC_Login;
	CK_C_Logout pC_Logout;
	CK_C_OpenSession pC_OpenSession;
	CK_C_SetAttributeValue pC_SetAttributeValue;
	CK_C_SetPIN pC_SetPIN;
	CK_C_Sign pC_Sign;
	CK_C_SignFinal pC_SignFinal;
	CK_C_SignInit pC_SignInit;
	CK_C_SignUpdate pC_SignUpdate;
	CK_C_UnwrapKey pC_UnwrapKey;
	CK_C_Verify pC_Verify;
	CK_C_VerifyInit pC_VerifyInit;
#endif /* USE_EXPLICIT_LINKING */
	} PKCS11_DRIVER_INFO;

static PKCS11_DRIVER_INFO pkcs11InfoTbl[ MAX_PKCS11_DRIVERS + 8 ];

/* The use of dynamically bound function pointers vs.statically linked
   functions requires a bit of sleight of hand since we can't give the
   pointers the same names as prototyped functions.  To get around this we
   redefine the actual function names to the names of the pointers */

#ifdef USE_EXPLICIT_LINKING

#define C_CloseSession		pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_CloseSession
#define C_CreateObject		pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_CreateObject
#define C_Decrypt			pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_Decrypt
#define C_DecryptInit		pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_DecryptInit
#define C_DestroyObject		pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_DestroyObject
#define C_Encrypt			pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_Encrypt
#define C_EncryptInit		pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_EncryptInit
#define C_Finalize			pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_Finalize
#define C_FindObjects		pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_FindObjects
#define C_FindObjectsFinal	pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_FindObjectsFinal
#define C_FindObjectsInit	pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_FindObjectsInit
#define C_GenerateKey		pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_GenerateKey
#define C_GenerateKeyPair	pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_GenerateKeyPair
#define C_GenerateRandom	pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_GenerateRandom
#define C_GetAttributeValue	pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_GetAttributeValue
#define C_GetMechanismInfo	pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_GetMechanismInfo
#define C_GetSlotInfo		pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_GetSlotInfo
#define C_GetSlotList		pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_GetSlotList
#define C_GetTokenInfo		pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_GetTokenInfo
#define C_Initialize		pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_Initialize
#define C_InitPIN			pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_InitPIN
#define C_InitToken			pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_InitToken
#define C_Login				pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_Login
#define C_Logout			pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_Logout
#define C_OpenSession		pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_OpenSession
#define C_SetAttributeValue	pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_SetAttributeValue
#define C_SetPIN			pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_SetPIN
#define C_Sign				pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_Sign
#define C_SignFinal			pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_SignFinal
#define C_SignInit			pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_SignInit
#define C_SignUpdate		pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_SignUpdate
#define C_UnwrapKey			pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_UnwrapKey
#define C_Verify			pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_Verify
#define C_VerifyInit		pkcs11InfoTbl[ pkcs11Info->deviceNo ].pC_VerifyInit

#endif /* USE_EXPLICIT_LINKING */

/* Dynamically load and unload any necessary PKCS #11 drivers */

static int loadPKCS11driver( PKCS11_DRIVER_INFO *pkcs11Info,
							 const char *driverName )
	{
#ifdef USE_EXPLICIT_LINKING
	CK_C_GetInfo pC_GetInfo;
	CK_C_Initialize pC_Initialize;
#else
	CK_C_GetFunctionList pC_GetFunctionList;
#endif /* USE_EXPLICIT_LINKING */
	CK_INFO info;
	CK_RV status;
#ifdef __WIN16__
	UINT errorMode;
#endif /* __WIN16__ */
	BOOLEAN isInitialised = FALSE;
	int i = 32;

	/* Obtain a handle to the device driver module */
#ifdef __WIN16__
	errorMode = SetErrorMode( SEM_NOOPENFILEERRORBOX );
	pkcs11Info->hPKCS11 = LoadLibrary( driverName );
	SetErrorMode( errorMode );
	if( pkcs11Info->hPKCS11 < HINSTANCE_ERROR )
		{
		pkcs11Info->hPKCS11 = NULL_HINSTANCE;
		return( CRYPT_ERROR );
		}
#else
	if( ( pkcs11Info->hPKCS11 = DynamicLoad( driverName ) ) == NULL_INSTANCE )
		return( CRYPT_ERROR );
#endif /* OS-specific dynamic load */

	/* Now get pointers to the functions */
#ifdef USE_EXPLICIT_LINKING
	pC_GetInfo = ( CK_C_GetInfo ) DynamicBind( pkcs11Info->hPKCS11, "C_GetInfo" );
	pC_Initialize = ( CK_C_Initialize ) DynamicBind( pkcs11Info->hPKCS11, "C_Initialize" );
	pkcs11Info->pC_CloseSession = ( CK_C_CloseSession ) DynamicBind( pkcs11Info->hPKCS11, "C_CloseSession" );
	pkcs11Info->pC_CreateObject = ( CK_C_CreateObject ) DynamicBind( pkcs11Info->hPKCS11, "C_CreateObject" );
	pkcs11Info->pC_Decrypt = ( CK_C_Decrypt ) DynamicBind( pkcs11Info->hPKCS11, "C_Decrypt" );
	pkcs11Info->pC_DecryptInit = ( CK_C_DecryptInit ) DynamicBind( pkcs11Info->hPKCS11, "C_DecryptInit" );
	pkcs11Info->pC_DestroyObject = ( CK_C_DestroyObject ) DynamicBind( pkcs11Info->hPKCS11, "C_DestroyObject" );
	pkcs11Info->pC_Encrypt = ( CK_C_Encrypt ) DynamicBind( pkcs11Info->hPKCS11, "C_Encrypt" );
	pkcs11Info->pC_EncryptInit = ( CK_C_EncryptInit ) DynamicBind( pkcs11Info->hPKCS11, "C_EncryptInit" );
	pkcs11Info->pC_Finalize = ( CK_C_Finalize ) DynamicBind( pkcs11Info->hPKCS11, "C_Finalize" );
	pkcs11Info->pC_FindObjects = ( CK_C_FindObjects ) DynamicBind( pkcs11Info->hPKCS11, "C_FindObjects" );
	pkcs11Info->pC_FindObjectsFinal = ( CK_C_FindObjectsFinal ) DynamicBind( pkcs11Info->hPKCS11, "C_FindObjectsFinal" );
	pkcs11Info->pC_FindObjectsInit = ( CK_C_FindObjectsInit ) DynamicBind( pkcs11Info->hPKCS11, "C_FindObjectsInit" );
	pkcs11Info->pC_GenerateKey = ( CK_C_GenerateKey ) DynamicBind( pkcs11Info->hPKCS11, "C_GenerateKey" );
	pkcs11Info->pC_GenerateKeyPair = ( CK_C_GenerateKeyPair ) DynamicBind( pkcs11Info->hPKCS11, "C_GenerateKeyPair" );
	pkcs11Info->pC_GenerateRandom = ( CK_C_GenerateRandom ) DynamicBind( pkcs11Info->hPKCS11, "C_GenerateRandom" );
	pkcs11Info->pC_GetAttributeValue = ( CK_C_GetAttributeValue ) DynamicBind( pkcs11Info->hPKCS11, "C_GetAttributeValue" );
	pkcs11Info->pC_GetMechanismInfo = ( CK_C_GetMechanismInfo ) DynamicBind( pkcs11Info->hPKCS11, "C_GetMechanismInfo" );
	pkcs11Info->pC_GetSlotInfo = ( CK_C_GetSlotInfo ) DynamicBind( pkcs11Info->hPKCS11, "C_GetSlotInfo" );
	pkcs11Info->pC_GetSlotList = ( CK_C_GetSlotList ) DynamicBind( pkcs11Info->hPKCS11, "C_GetSlotList" );
	pkcs11Info->pC_GetTokenInfo = ( CK_C_GetTokenInfo ) DynamicBind( pkcs11Info->hPKCS11, "C_GetTokenInfo" );
	pkcs11Info->pC_InitPIN = ( CK_C_InitPIN ) DynamicBind( pkcs11Info->hPKCS11, "C_InitPIN" );
	pkcs11Info->pC_InitToken = ( CK_C_InitToken ) DynamicBind( pkcs11Info->hPKCS11, "C_InitToken" );
	pkcs11Info->pC_Login = ( CK_C_Login ) DynamicBind( pkcs11Info->hPKCS11, "C_Login" );
	pkcs11Info->pC_Logout = ( CK_C_Logout ) DynamicBind( pkcs11Info->hPKCS11, "C_Logout" );
	pkcs11Info->pC_OpenSession = ( CK_C_OpenSession ) DynamicBind( pkcs11Info->hPKCS11, "C_OpenSession" );
	pkcs11Info->pC_SetAttributeValue = ( CK_C_SetAttributeValue ) DynamicBind( pkcs11Info->hPKCS11, "C_SetAttributeValue" );
	pkcs11Info->pC_SetPIN = ( CK_C_SetPIN ) DynamicBind( pkcs11Info->hPKCS11, "C_SetPIN" );
	pkcs11Info->pC_SignFinal = ( CK_C_SignFinal ) DynamicBind( pkcs11Info->hPKCS11, "C_SignFinal" );
	pkcs11Info->pC_SignInit = ( CK_C_SignInit ) DynamicBind( pkcs11Info->hPKCS11, "C_SignInit" );
	pkcs11Info->pC_SignUpdate = ( CK_C_SignUpdate ) DynamicBind( pkcs11Info->hPKCS11, "C_SignUpdate" );
	pkcs11Info->pC_UnwrapKey = ( CK_C_UnwrapKey ) DynamicBind( pkcs11Info->hPKCS11, "C_UnwrapKey" );
	pkcs11Info->pC_Verify = ( CK_C_Verify ) DynamicBind( pkcs11Info->hPKCS11, "C_Verify" );
	pkcs11Info->pC_VerifyInit = ( CK_C_VerifyInit ) DynamicBind( pkcs11Info->hPKCS11, "C_VerifyInit" );

	/* Make sure that we got valid pointers for every device function.  
	   C_FindObjectsFinal() wasn't added until 2.x and some drivers don't
	   implement it (a smaller subset of them nevertheless claim to be 2.x 
	   drivers), so we allow this to be null - the code won't call it if it's
	   not present */
	if( pC_GetInfo == NULL || pC_Initialize == NULL ||
		pkcs11Info->pC_CloseSession == NULL ||
		pkcs11Info->pC_CreateObject == NULL ||
		pkcs11Info->pC_Decrypt == NULL ||
		pkcs11Info->pC_DecryptInit == NULL ||
		pkcs11Info->pC_DestroyObject == NULL ||
		pkcs11Info->pC_Encrypt == NULL ||
		pkcs11Info->pC_EncryptInit == NULL ||
		pkcs11Info->pC_Finalize == NULL ||
		pkcs11Info->pC_FindObjects == NULL ||
		pkcs11Info->pC_FindObjectsInit == NULL ||
		pkcs11Info->pC_GenerateRandom == NULL ||
		pkcs11Info->pC_GenerateKey == NULL ||
		pkcs11Info->pC_GenerateKeyPair == NULL ||
		pkcs11Info->pC_GetAttributeValue == NULL ||
		pkcs11Info->pC_GetMechanismInfo == NULL ||
		pkcs11Info->pC_GetSlotInfo == NULL ||
		pkcs11Info->pC_GetSlotList == NULL ||
		pkcs11Info->pC_GetTokenInfo == NULL || 
		pkcs11Info->pC_InitPIN == NULL || 
		pkcs11Info->pC_InitToken == NULL || pkcs11Info->pC_Login == NULL ||
		pkcs11Info->pC_Logout == NULL || pkcs11Info->pC_OpenSession == NULL ||
		pkcs11Info->pC_SetAttributeValue == NULL ||
		pkcs11Info->pC_SetPIN == NULL || pkcs11Info->pC_Sign == NULL ||
		pkcs11Info->pC_SignFinal == NULL || pkcs11Info->pC_SignInit == NULL || 
		pkcs11Info->pC_SignUpdate == NULL || 
		pkcs11Info->pC_UnwrapKey == NULL || pkcs11Info->pC_Verify == NULL || 
		pkcs11Info->pC_VerifyInit == NULL )
		{
		/* Free the library reference and clear the info */
		DynamicUnload( pkcs11Info->hPKCS11 );
		memset( pkcs11Info, 0, sizeof( PKCS11_DRIVER_INFO ) );
		return( CRYPT_ERROR );
		}

	/* Initialise the PKCS #11 library and get info on the device.  There are 
	   four types of PKCS #11 driver around: v1, v1-like claiming to be v2, 
	   v2-like claiming to be v1, and v2.  cryptlib can in theory handle all 
	   of these, however there are some problem areas with v1 (for example v1 
	   uses 16-bit values while v2 uses 32-bit ones, this is usually OK 
	   because data is passed around as 32-bit values with the high bits 
	   zeroed but some implementations may leave garbage in the high 16 bits 
	   that leads to all sorts of confusion).  Because of this we explicitly 
	   fail if something claims to be v1 even though it might work in 
	   practice */
	status = pC_Initialize( NULL_PTR ) & 0xFFFF;
	if( status == CKR_OK || status == CKR_CRYPTOKI_ALREADY_INITIALIZED )
		{
		isInitialised = TRUE;
		status = pC_GetInfo( &info ) & 0xFFFF;
		}
	if( status == CKR_OK && info.cryptokiVersion.major <= 1 )
		/* It's v1, we can't work with it */
		status = CKR_FUNCTION_NOT_SUPPORTED;
	if( status != CKR_OK )
		{
		if( isInitialised )
			pkcs11Info->pC_Finalize( NULL_PTR );
		DynamicUnload( pkcs11Info->hPKCS11 );
		memset( pkcs11Info, 0, sizeof( PKCS11_DRIVER_INFO ) );
		return( CRYPT_ERROR );
		}
#else
	/* Get the access information for the PKCS #11 library, initialise it, 
	   and get info on the device.  There are four types of PKCS #11 driver 
	   around: v1, v1-like claiming to be v2, v2-like claiming to be v1, and 
	   v2.  cryptlib can in theory handle all of these, however there are 
	   some problem areas with v1 (for example v1 uses 16-bit values while 
	   v2 uses 32-bit ones, this is usually OK because data is passed around 
	   as 32-bit values with the high bits zeroed but some implementations 
	   may leave garbage in the high 16 bits that leads to all sorts of 
	   confusion).  Because of this we explicitly fail if something claims 
	   to be v1 even though it might work in practice */
	pC_GetFunctionList = ( CK_C_GetFunctionList ) DynamicBind( pkcs11Info->hPKCS11, "C_GetFunctionList" );
	if( pC_GetFunctionList == NULL )
		status = CKR_GENERAL_ERROR;
	else
		status = pC_GetFunctionList( &pkcs11Info->functionListPtr ) & 0xFFFF;
	if( status != CKR_OK )
		{
		/* Free the library reference and clear the info */
		DynamicUnload( pkcs11Info->hPKCS11 );
		memset( pkcs11Info, 0, sizeof( PKCS11_DRIVER_INFO ) );
		return( CRYPT_ERROR );
		}
	status = C_Initialize( NULL_PTR ) & 0xFFFF;
	if( status == CKR_OK || status == CKR_CRYPTOKI_ALREADY_INITIALIZED )
		{
		isInitialised = TRUE;
		status = C_GetInfo( &info ) & 0xFFFF;
		}
	if( status == CKR_OK && info.cryptokiVersion.major <= 1 )
		/* It's v1, we can't work with it */
		status = CKR_FUNCTION_NOT_SUPPORTED;
	if( status != CKR_OK )
		{
		if( isInitialised )
			C_Finalize( NULL_PTR );
		DynamicUnload( pkcs11Info->hPKCS11 );
		memset( pkcs11Info, 0, sizeof( PKCS11_DRIVER_INFO ) );
		return( CRYPT_ERROR );
		}
#endif /* USE_EXPLICIT_LINKING */

	/* Copy out the device driver's name so that the user can access it by 
	   name.  Some vendors erroneously null-terminate the string so we check 
	   for nulls as well */
	memcpy( pkcs11Info->name, info.libraryDescription, 32 );
	while( i > 0 && ( pkcs11Info->name[ i - 1 ] == ' ' || \
					  !pkcs11Info->name[ i - 1 ] ) )
		i--;
	pkcs11Info->name[ i ] = '\0';

	return( CRYPT_OK );
	}

void deviceEndPKCS11( void )
	{
	int i;

	if( pkcs11Initialised )
		for( i = 0; i < MAX_PKCS11_DRIVERS; i++ )
			{
			if( pkcs11InfoTbl[ i ].hPKCS11 != NULL_INSTANCE )
				{
#ifdef USE_EXPLICIT_LINKING
				pkcs11InfoTbl[ i ].pC_Finalize( NULL_PTR );
#else
				PKCS11_DRIVER_INFO *pkcs11Info = &pkcs11InfoTbl[ i ];

				C_Finalize( NULL_PTR );
#endif /* USE_EXPLICIT_LINKING */
				DynamicUnload( pkcs11InfoTbl[ i ].hPKCS11 );
				}
			pkcs11InfoTbl[ i ].hPKCS11 = NULL_INSTANCE;
			}
	pkcs11Initialised = FALSE;
	}

int deviceInitPKCS11( void )
	{
	int tblIndex = 0, optionIndex;

	/* If we've previously tried to init the drivers, don't try it again */
	if( pkcs11Initialised )
		return( CRYPT_OK );
	memset( pkcs11InfoTbl, 0, sizeof( pkcs11InfoTbl ) );

	/* Try and link in each driver specified in the config options.  Since
	   this is a general systemwide config option, we always query the built-
	   in default user object */
	for( optionIndex = 0; optionIndex < MAX_PKCS11_DRIVERS; optionIndex++ )
		{
		MESSAGE_DATA msgData;
		char deviceDriverName[ MAX_PATH_LENGTH + 1 + 8 ];
		int status;

		setMessageData( &msgData, deviceDriverName, MAX_PATH_LENGTH );
		status = krnlSendMessage( DEFAULTUSER_OBJECT_HANDLE, 
						IMESSAGE_GETATTRIBUTE_S, &msgData, 
						optionIndex + CRYPT_OPTION_DEVICE_PKCS11_DVR01 );
		if( cryptStatusError( status ) )
			continue;
		deviceDriverName[ msgData.length ] = '\0';
		status = loadPKCS11driver( &pkcs11InfoTbl[ tblIndex ], 
								   deviceDriverName );
		if( cryptStatusOK( status ) )
			{
			tblIndex++;
			pkcs11Initialised = TRUE;
			}
		}

	/* If it's a Unix system and there were no drivers explicitly specified,
	   try with the default driver name "libpkcs11.so".  Unlike Windows,
	   where there are large numbers of PKCS #11 vendors and we have to use
	   vendor-specific names, under Unix there are very few vendors and 
	   there's usually only one device/driver in use which inevitably 
	   co-opts /usr/lib for its own use, so we can always try for a standard
	   name and location.

	   As a backup measure, we try for the nCipher PKCS #11 driver, which is
	   by far the most commonly used one on Unix systems (this may sometimes 
	   be found as /usr/lib/libcknfast.so) */
#ifdef UNIX
	if( !pkcs11Initialised )
		{
		status = loadPKCS11driver( &pkcs11InfoTbl[ tblIndex ], 
								   "libpkcs11.so" );
		if( cryptStatusOK( status ) )
			pkcs11Initialised = TRUE;
		}
	if( !pkcs11Initialised )
		{
		status = loadPKCS11driver( &pkcs11InfoTbl[ tblIndex ], 
								   "/opt/nfast/toolkits/pkcs11/libcknfast.so" );
		if( cryptStatusOK( status ) )
			pkcs11Initialised = TRUE;
		}
#endif /* UNIX */
	
	return( pkcs11Initialised ? CRYPT_OK : CRYPT_ERROR );
	}

#else

int deviceInitPKCS11( void )
	{
	int status;

	/* If we've previously tried to init the drivers, don't try it again */
	if( pkcs11Initialised )
		return( CRYPT_OK );

	status = C_Initialize( NULL_PTR );
	if( status != CKR_OK && status != CKR_CRYPTOKI_ALREADY_INITIALIZED )
		return( CRYPT_ERROR );
	pkcs11Initialised = TRUE;
	return( CRYPT_OK );
	}

void deviceEndPKCS11( void )
	{
	if( pkcs11Initialised )
		C_Finalize( NULL_PTR );
	pkcs11Initialised = FALSE;
	}
#endif /* DYNAMIC_LOAD */

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

/* The reported key size for PKCS #11 implementations is rather inconsistent,
   most are reported in bits, a number don't return a useful value, and a few
   are reported in bytes.  The following macros sort out which algorithms
   have valid key size info and which report the length in bytes */

#define keysizeValid( algo ) \
	( ( algo ) == CRYPT_ALGO_DH || ( algo ) == CRYPT_ALGO_RSA || \
	  ( algo ) == CRYPT_ALGO_DSA || ( algo ) == CRYPT_ALGO_RC2 || \
	  ( algo ) == CRYPT_ALGO_RC4 || ( algo ) == CRYPT_ALGO_RC5 || \
	  ( algo ) == CRYPT_ALGO_CAST )
#define keysizeInBytes( algo ) \
	( ( algo ) == CRYPT_ALGO_RC5 || ( algo ) == CRYPT_ALGO_CAST )

/* 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",
		MIN_KEYSIZE, bitsToBytes( 64 ), bitsToBytes( 64 ) },
	{ CRYPT_ALGO_3DES, bitsToBytes( 64 ), "3DES",
		bitsToBytes( 64 + 8 ), bitsToBytes( 128 ), bitsToBytes( 192 ) },
	{ CRYPT_ALGO_IDEA, bitsToBytes( 64 ), "IDEA",
		MIN_KEYSIZE, bitsToBytes( 128 ), bitsToBytes( 128 ) },
	{ CRYPT_ALGO_CAST, bitsToBytes( 64 ), "CAST-128",
		MIN_KEYSIZE, bitsToBytes( 128 ), bitsToBytes( 128 ) },
	{ CRYPT_ALGO_RC2, bitsToBytes( 64 ), "RC2",
		MIN_KEYSIZE, bitsToBytes( 128 ), bitsToBytes( 1024 ) },
	{ CRYPT_ALGO_RC4, bitsToBytes( 8 ), "RC4",
		MIN_KEYSIZE, bitsToBytes( 128 ), 256 },
	{ CRYPT_ALGO_RC5, bitsToBytes( 64 ), "RC5",
		MIN_KEYSIZE, bitsToBytes( 128 ), bitsToBytes( 832 ) },
	{ CRYPT_ALGO_AES, bitsToBytes( 128 ), "AES",
		bitsToBytes( 128 ), bitsToBytes( 128 ), bitsToBytes( 256 ) },
	{ CRYPT_ALGO_BLOWFISH, bitsToBytes( 64 ), "Blowfish",
		MIN_KEYSIZE, bitsToBytes( 128 ), bitsToBytes( 448 ) },
	{ 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_MD5, bitsToBytes( 128 ), "MD5",
		bitsToBytes( 0 ), bitsToBytes( 0 ), bitsToBytes( 0 ) },
	{ CRYPT_ALGO_SHA, bitsToBytes( 160 ), "SHA",
		bitsToBytes( 0 ), bitsToBytes( 0 ), bitsToBytes( 0 ) },
#ifdef USE_SHA2
	{ CRYPT_ALGO_SHA2, bitsToBytes( 256 ), "SHA2",
		bitsToBytes( 0 ), bitsToBytes( 0 ), bitsToBytes( 0 ) },
#endif /* USE_SHA2 */

	/* MAC capabilities */
	{ CRYPT_ALGO_HMAC_MD5, bitsToBytes( 128 ), "HMAC-MD5",
	  bitsToBytes( 64 ), bitsToBytes( 128 ), CRYPT_MAX_KEYSIZE },
	{ CRYPT_ALGO_HMAC_SHA, bitsToBytes( 160 ), "HMAC-SHA",
	  bitsToBytes( 64 ), bitsToBytes( 128 ), CRYPT_MAX_KEYSIZE },
	{ CRYPT_ALGO_HMAC_RIPEMD160, bitsToBytes( 160 ), "HMAC-RIPEMD160",
	  bitsToBytes( 64 ), bitsToBytes( 128 ), CRYPT_MAX_KEYSIZE },

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

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

/* Query a given capability for a device and fill out a capability info
   record for it if present */

static CAPABILITY_INFO *getCapability( const DEVICE_INFO *deviceInfo,
									   const PKCS11_MECHANISM_INFO *mechanismInfoPtr,
									   const int maxMechanisms )
	{
	VARIABLE_CAPABILITY_INFO *capabilityInfo;
	CK_MECHANISM_INFO pMechanism;
	CK_RV status;
	const CRYPT_ALGO_TYPE cryptAlgo = mechanismInfoPtr->cryptAlgo;
	const BOOLEAN isPKC = ( cryptAlgo >= CRYPT_ALGO_FIRST_PKC && \
							cryptAlgo <= CRYPT_ALGO_LAST_PKC ) ? TRUE : FALSE;
	const CK_FLAGS keyGenFlag = isPKC ? CKF_GENERATE_KEY_PAIR : CKF_GENERATE;
	PKCS11_INFO *pkcs11Info = deviceInfo->devicePKCS11;
	int hardwareOnly, i, iterationCount;

	assert( isReadPtr( deviceInfo, sizeof( DEVICE_INFO ) ) );
	assert( isReadPtr( mechanismInfoPtr, \
					   maxMechanisms * sizeof( PKCS11_MECHANISM_INFO ) ) );

	/* Get the information for this mechanism.  Since many PKCS #11 drivers
	   implement some of their capabilities using God knows what sort of 
	   software implementation, we provide the option to skip emulated 
	   mechanisms if required */
	status = C_GetMechanismInfo( pkcs11Info->slotID, 
								 mechanismInfoPtr->mechanism,
								 &pMechanism );
	if( status != CKR_OK )
		return( NULL );
	krnlSendMessage( deviceInfo->ownerHandle, IMESSAGE_GETATTRIBUTE, 
					 &hardwareOnly, CRYPT_OPTION_DEVICE_PKCS11_HARDWAREONLY );
	if( hardwareOnly && !( pMechanism.flags & CKF_HW ) )
		return( NULL );

	/* Copy across the template for this capability */
	if( ( capabilityInfo = clAlloc( "getCapability", \
									sizeof( CAPABILITY_INFO ) ) ) == NULL )
		return( NULL );
	for( i = 0; capabilityTemplates[ i ].cryptAlgo != 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 if there's anything useful available */
	if( keysizeValid( cryptAlgo ) )
		{
		int minKeySize = ( int ) pMechanism.ulMinKeySize;
		int maxKeySize = ( int ) pMechanism.ulMaxKeySize;

		/* Adjust the key size to bytes and make sure that all values are 
		   consistent.  Some implementations report silly lower bounds (e.g. 
		   1-bit RSA, "You naughty minKey") so we adjust them to a sane value 
		   if necessary.  We also limit 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 */
		if( !keysizeInBytes( cryptAlgo ) )
			{
			minKeySize = bitsToBytes( minKeySize );
			maxKeySize = bitsToBytes( maxKeySize );
			}
		if( minKeySize > capabilityInfo->minKeySize )
			capabilityInfo->minKeySize = minKeySize;
		if( capabilityInfo->keySize < capabilityInfo->minKeySize )
			capabilityInfo->keySize = capabilityInfo->minKeySize;
		capabilityInfo->maxKeySize = min( maxKeySize, 
										  capabilityInfo->maxKeySize );
		if( capabilityInfo->maxKeySize < capabilityInfo->minKeySize )
			{
			/* Serious braindamage in the driver, we'll just have to make
			   a sensible guess */
			assert( NOTREACHED );
			capabilityInfo->maxKeySize = \
									( cryptAlgo == CRYPT_ALGO_RSA || \
									  isDlpAlgo( cryptAlgo ) ) ? 128 : 16;
			}
		if( capabilityInfo->keySize > capabilityInfo->maxKeySize )
			capabilityInfo->keySize = capabilityInfo->maxKeySize;
		capabilityInfo->endFunction = genericEndFunction;
		}

	/* Set up the device-specific handlers */
	capabilityInfo->getInfoFunction = getDefaultInfo;
	if( cryptAlgo != CRYPT_ALGO_DH && cryptAlgo != CRYPT_ALGO_RSA && \
		cryptAlgo != CRYPT_ALGO_DSA )
		capabilityInfo->initKeyParamsFunction = initKeyParams;
	capabilityInfo->endFunction = mechanismInfoPtr->endFunction;
	capabilityInfo->initKeyFunction = mechanismInfoPtr->initKeyFunction;
	if( pMechanism.flags & keyGenFlag )
		capabilityInfo->generateKeyFunction = \
									mechanismInfoPtr->generateKeyFunction;
	if( pMechanism.flags & CKF_SIGN )
		{
		/* cryptlib treats hashing as an encrypt/decrypt operation while 
		   PKCS #11 treats it as a sign/verify operation, so we have to
		   juggle the function pointers based on the underlying algorithm
		   type */
		if( isPKC )
			capabilityInfo->signFunction = mechanismInfoPtr->signFunction;
		else
			capabilityInfo->encryptFunction = mechanismInfoPtr->encryptFunction;
		}
	if( pMechanism.flags & CKF_VERIFY )
		{
		/* See comment above */
		if( isPKC )
			capabilityInfo->sigCheckFunction = mechanismInfoPtr->sigCheckFunction;
		else
			capabilityInfo->decryptFunction = mechanismInfoPtr->decryptFunction;
		}
	if( pMechanism.flags & CKF_ENCRYPT )
		{
		/* Not all devices implement all modes, so we have to be careful to 
		   set up the pointer for the exact mode that's supported */
		switch( mechanismInfoPtr->cryptMode )
			{
			case CRYPT_MODE_CBC:
				capabilityInfo->encryptCBCFunction = mechanismInfoPtr->encryptFunction;
				break;

			case CRYPT_MODE_CFB:
				capabilityInfo->encryptCFBFunction = mechanismInfoPtr->encryptFunction;
				break;

			case CRYPT_MODE_OFB:
				capabilityInfo->encryptOFBFunction = mechanismInfoPtr->encryptFunction;
				break;

			default:	/* ECB or a PKC */
				capabilityInfo->encryptFunction = mechanismInfoPtr->encryptFunction;
				break;
			}
		}
	if( pMechanism.flags & CKF_DECRYPT )
		{
		/* Not all devices implement all modes, so we have to be careful to 
		   set up the pointer for the exact mode that's supported */
		switch( mechanismInfoPtr->cryptMode )
			{
			case CRYPT_MODE_CBC:
				capabilityInfo->decryptCBCFunction = mechanismInfoPtr->decryptFunction;
				break;

			case CRYPT_MODE_CFB:
				capabilityInfo->decryptCFBFunction = mechanismInfoPtr->decryptFunction;
				break;

			case CRYPT_MODE_OFB:
				capabilityInfo->decryptOFBFunction = mechanismInfoPtr->decryptFunction;
				break;

			default:	/* ECB or a PKC */
				capabilityInfo->decryptFunction = mechanismInfoPtr->decryptFunction;
				break;
			}
		}
	if( cryptAlgo == CRYPT_ALGO_DH && pMechanism.flags & CKF_DERIVE )
		{
		/* DH is a special-case that doesn't really have an encrypt function 
		   and where "decryption" is actually a derivation */
		capabilityInfo->encryptFunction = mechanismInfoPtr->encryptFunction;
		capabilityInfo->decryptFunction = mechanismInfoPtr->decryptFunction;
		}

	/* Keygen capabilities are generally present as separate mechanisms,
	   sometimes CKF_GENERATE/CKF_GENERATE_KEY_PAIR is set for the main 
	   mechanism and sometimes it's set for the separate one so if it isn't 
	   present in the main one we check the alternative one */
	if( !( pMechanism.flags & keyGenFlag ) && \
		( mechanismInfoPtr->keygenMechanism != CKM_NONE ) )
		{
		status = C_GetMechanismInfo( pkcs11Info->slotID, 
									 mechanismInfoPtr->keygenMechanism,
									 &pMechanism );
		if( status == CKR_OK && ( pMechanism.flags & keyGenFlag ) && \
			( !hardwareOnly || ( pMechanism.flags & CKF_HW ) ) )
			/* Some tinkertoy tokens don't implement key generation in 
			   hardware but instead do it on the host PC (!!!) and load the
			   key into the token afterwards, so we have to perform another 
			   check here to make sure that they're doing things right */
			capabilityInfo->generateKeyFunction = \
									mechanismInfoPtr->generateKeyFunction;
		}

	/* Record mechanism-specific parameters if required */
	if( ( cryptAlgo >= CRYPT_ALGO_FIRST_CONVENTIONAL && \
		  cryptAlgo <= CRYPT_ALGO_LAST_CONVENTIONAL ) || \
		( cryptAlgo >= CRYPT_ALGO_FIRST_MAC && \
		  cryptAlgo <= CRYPT_ALGO_LAST_MAC ) )
		{
		capabilityInfo->paramKeyType = mechanismInfoPtr->keyType;
		capabilityInfo->paramKeyGen = mechanismInfoPtr->keygenMechanism;
		capabilityInfo->paramDefaultMech = mechanismInfoPtr->defaultMechanism;
		}

	/* If it's not a conventional encryption algo, we're done */
	if( cryptAlgo < CRYPT_ALGO_FIRST_CONVENTIONAL || \
		cryptAlgo > CRYPT_ALGO_LAST_CONVENTIONAL )
		return( ( CAPABILITY_INFO * ) capabilityInfo );

	/* PKCS #11 handles encryption modes by defining a separate mechanism for
	   each one.  In order to enumerate all the modes available for a 
	   particular algorithm we check for each mechanism in turn and set up 
	   the appropriate function pointers if it's available */
	for( mechanismInfoPtr++, iterationCount = 0; 
		 mechanismInfoPtr->cryptAlgo == cryptAlgo && \
			iterationCount < maxMechanisms; 
		 mechanismInfoPtr++, iterationCount++ )
		{
		/* There's a different form of the existing mechanism available,
		   check whether the driver implements it */
		status = C_GetMechanismInfo( pkcs11Info->slotID, 
									 mechanismInfoPtr->mechanism,
									 &pMechanism );
		if( status != CKR_OK )
			continue;

		/* Set up the pointer for the appropriate encryption mode */
		switch( mechanismInfoPtr->cryptMode )
			{
			case CRYPT_MODE_CBC:
				if( pMechanism.flags & CKF_ENCRYPT )
					capabilityInfo->encryptCBCFunction = \
										mechanismInfoPtr->encryptFunction;
				if( pMechanism.flags & CKF_DECRYPT )
					capabilityInfo->decryptCBCFunction = \
										mechanismInfoPtr->decryptFunction;
				break;
			case CRYPT_MODE_CFB:
				if( pMechanism.flags & CKF_ENCRYPT )
					capabilityInfo->encryptCFBFunction = \
										mechanismInfoPtr->encryptFunction;
				if( pMechanism.flags & CKF_DECRYPT )
					capabilityInfo->decryptCFBFunction = \
										mechanismInfoPtr->decryptFunction;
				break;
			case CRYPT_MODE_OFB:
				if( pMechanism.flags & CKF_ENCRYPT )
					capabilityInfo->encryptOFBFunction = \
										mechanismInfoPtr->encryptFunction;
				if( pMechanism.flags & CKF_DECRYPT )
					capabilityInfo->decryptOFBFunction = \
										mechanismInfoPtr->decryptFunction;
				break;

			default:
				assert( NOTREACHED );
			}
		}
	if( iterationCount >= maxMechanisms )
		retIntError_Null();

	return( ( CAPABILITY_INFO * ) capabilityInfo );
	}

/* Set the capability information based on device capabilities.  Since
   PKCS #11 devices can have assorted capabilities (and can vary depending
   on what's plugged in), 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,
							const PKCS11_MECHANISM_INFO *mechanismInfoPtr, 
							const int maxMechanisms )
	{
	CAPABILITY_INFO_LIST *capabilityInfoListTail = \
				( CAPABILITY_INFO_LIST * ) deviceInfo->capabilityInfoList;
	int i;

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

	/* Find the end of the list to add new capabilities */
	if( capabilityInfoListTail != NULL )
		{
		while( capabilityInfoListTail->next != NULL )
			capabilityInfoListTail = capabilityInfoListTail->next;
		}

	/* Add capability information for each recognised mechanism type */
	for( i = 0; mechanismInfoPtr[ i ].mechanism != CKM_NONE && \
				i < maxMechanisms; i++ )
		{
		CAPABILITY_INFO_LIST *newCapabilityList;
		CAPABILITY_INFO *newCapability;
		const CRYPT_ALGO_TYPE cryptAlgo = mechanismInfoPtr[ i ].cryptAlgo;

		/* If the assertion below triggers then the PKCS #11 driver 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 PKCS #11 
		   glue code to find out why an operation is failing.
		   
		   Because some tinkertoy implementations support only the bare 
		   minimum functionality (e.g.RSA private key ops and nothing else),
		   we allow asymmetric functionality for PKCs */
		newCapability = getCapability( deviceInfo, &mechanismInfoPtr[ i ],
									   maxMechanisms - i );
		if( newCapability == NULL )
			continue;
		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;

		/* Since there may be alternative mechanisms to the current one 
		   defined, we have to skip mechanisms until we find a ones for a
		   new algorithm */
		while( mechanismInfoPtr[ i + 1 ].cryptAlgo == cryptAlgo && \
			   i < maxMechanisms )
			i++;
		if( i >= maxMechanisms )
			retIntError();
		}
	if( i >= maxMechanisms )
		retIntError();

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

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

/* 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 )
	{
	PKCS11_INFO *pkcs11Info = deviceInfo->devicePKCS11;

	/* Log out and close the session with the device */
	if( deviceInfo->flags & DEVICE_LOGGEDIN )
		C_Logout( pkcs11Info->hSession );
	C_CloseSession( pkcs11Info->hSession );
	pkcs11Info->hSession = CK_OBJECT_NONE;
	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 )
	{
	CK_SESSION_HANDLE hSession;
	CK_SLOT_ID slotList[ MAX_PKCS11_SLOTS + 8 ];
	CK_ULONG slotCount = MAX_PKCS11_SLOTS;
	CK_SLOT_INFO slotInfo;
	CK_TOKEN_INFO tokenInfo;
	CK_RV status;
	PKCS11_INFO *pkcs11Info = deviceInfo->devicePKCS11;
	const PKCS11_MECHANISM_INFO *mechanismInfoPtr;
	char *labelPtr;
	int tokenSlot = DEFAULT_SLOT, i, labelLength, mechanismInfoSize;
	int cryptStatus, cryptStatus2;

	/* Get information on all available slots */
	memset( slotList, 0, sizeof( slotList ) );
	status = C_GetSlotList( TRUE, slotList, &slotCount );
	if( status != CKR_OK )
		return( pkcs11MapError( pkcs11Info, status, CRYPT_ERROR_OPEN ) );
	if( slotCount <= 0 )
		/* There are token slots present but no tokens in the slots */
		return( CRYPT_ERROR_OPEN );

	/* Check whether a token name (used to select the slot) has been 
	   specified */
	for( i = 1; i < nameLength - 1; i++ )
		if( name[ i ] == ':' && name[ i + 1 ] == ':' )
			break;
	if( i < nameLength - 1 )
		{
		const char *tokenName = name + i + 2;	/* Skip '::' */
		const int tokenNameLength = nameLength - ( i + 2 );

		if( tokenNameLength <= 0 )
			return( CRYPT_ARGERROR_STR1 );

		/* Some tokens don't implement named slots, so we also allow them to 
		   be specified using slot counts */
		if( tokenNameLength == 1 && isDigit( *tokenName ) )
			{
			tokenSlot = *tokenName - '0';
			if( tokenSlot < 0 || tokenSlot > 9 )
				return( CRYPT_ARGERROR_STR1 );
			if( tokenSlot > slotCount - 1 )	/* Slots numbered from zero */
				return( CRYPT_ERROR_NOTFOUND );
			status = C_GetTokenInfo( slotList[ tokenSlot ], &tokenInfo );
			if( status != CKR_OK )
				return( CRYPT_ERROR_NOTFOUND );
			}
		else
			{
			/* Check each (named) slot for a token matching the given name */
			for( tokenSlot = 0; tokenSlot < slotCount && \
								tokenSlot < FAILSAFE_ITERATIONS_MED; 
				 tokenSlot++ )
				{
				status = C_GetTokenInfo( slotList[ tokenSlot ], &tokenInfo );
				if( status == CKR_OK && \
					!strnicmp( tokenName, tokenInfo.label, tokenNameLength ) )
					break;
				}
			if( tokenSlot >= FAILSAFE_ITERATIONS_MED )
				retIntError();
			if( tokenSlot >= slotCount )
				return( CRYPT_ERROR_NOTFOUND );
			}
		}
	pkcs11Info->slotID = slotList[ tokenSlot ];

	/* Get information on device-specific capabilities */
	status = C_GetSlotInfo( pkcs11Info->slotID, &slotInfo );
	if( status != CKR_OK )
		{
		shutdownFunction( deviceInfo );
		return( pkcs11MapError( pkcs11Info, status, CRYPT_ERROR_OPEN ) );
		}
	if( slotInfo.flags & CKF_REMOVABLE_DEVICE )
		/* The device is removable */
		deviceInfo->flags |= DEVICE_REMOVABLE;
	status = C_GetTokenInfo( pkcs11Info->slotID, &tokenInfo );
	if( status != CKR_OK )
		{
		shutdownFunction( deviceInfo );
		return( pkcs11MapError( pkcs11Info, status, CRYPT_ERROR_OPEN ) );
		}
	if( tokenInfo.flags & CKF_RNG )
		/* The device has an onboard RNG that we can use */
		deviceInfo->getRandomFunction = getRandomFunction;
#if 0	/* The Spyrus driver for pre-Lynks-II cards returns the local system 
		   time (with a GMT/localtime offset), ignoring the fact that the 
		   token has an onboard clock, so having the CKF_CLOCK_ON_TOKEN not 
		   set is accurate, although having it ignore the presence of the 
		   clock isn't very valid */
	if( !( tokenInfo.flags & CKF_CLOCK_ON_TOKEN ) && \
		( !strCompare( tokenInfo.label, "Lynks Token", 11 ) || \
		  !strCompare( tokenInfo.model, "Rosetta", 7 ) ) )
		/* Fix buggy Spyrus PKCS #11 drivers which claim that the token
		   doesn't have a RTC even though it does (the Rosetta (smart card) 
		   form of the token is even worse, it returns garbage in the label 
		   and manufacturer fields, but the model field is OK).  There is a 
		   chance that there's a genuine problem with the clock (there are 
		   batches of tokens with bad clocks), but the time check that 
		   follows below will catch those */
		tokenInfo.flags |= CKF_CLOCK_ON_TOKEN;
#endif /* 0 */
	if( tokenInfo.flags & CKF_CLOCK_ON_TOKEN )
		{
		const time_t theTime = getTokenTime( &tokenInfo );
		const time_t currentTime = getTime();

		/* The token claims to have an onboard clock that we can use.  Since
		   this could be arbitrarily inaccurate, we compare it with the 
		   system time and only rely on it if it's within +/- 1 day of the
		   system time.
		   
		   There is a second check that we should make to catch drivers that
		   claim to read the time from the token but actually use the local
		   computer's time, but this isn't easy to do.  The most obvious way
		   is to set the system time to a bogus value and check whether this
		   matches the returned time, but this is somewhat drastic and 
		   requires superuser privs on most systems.  An alternative is to 
		   check whether the claimed token time exactly matches the system 
		   time, but this will produce false positives if (for example) the
		   token has been recently synchronised to the system time.  For now
		   all we can do is throw an exception if it appears that the token
		   time is faked */
		if( theTime > MIN_TIME_VALUE && \
			theTime >= currentTime - 86400 && \
			theTime <= currentTime + 86400 )
			deviceInfo->flags |= DEVICE_TIME;

		/* If this assertion is triggered, the token time may be faked, since
		   it's identical to the host system time - see the comment above for 
		   details.  We make an exception for soft-tokens, which will (by
		   definition) have the same time as the system time */
		assert( ( pkcs11InfoTbl[ pkcs11Info->deviceNo ].name[ 0 ] && \
				  !strCompare( pkcs11InfoTbl[ pkcs11Info->deviceNo ].name,
							   "Software", 8 ) ) || \
				theTime < currentTime - 1 || theTime > currentTime + 1 );
		}
	if( tokenInfo.flags & CKF_WRITE_PROTECTED )
		/* The device can't have data on it changed */
		deviceInfo->flags |= DEVICE_READONLY;
	if( ( tokenInfo.flags & CKF_LOGIN_REQUIRED ) || \
		!( tokenInfo.flags & CKF_USER_PIN_INITIALIZED ) )
		/* The user needs to log in before using various device functions.
		   We check for the absence of CKF_USER_PIN_INITIALIZED as well as 
		   the more obvious CKF_LOGIN_REQUIRED because if we've got an 
		   uninitialised device there's no PIN set so some devices will 
		   report that there's no login required (or at least none is 
		   possible).  We need to introduce some sort of pipeline stall if 
		   this is the case because otherwise the user could successfully 
		   perform some functions that don't require a login (where the 
		   exact details of what's allowed without a login are device-
		   specific) before running into mysterious failures when they get 
		   to functions that do require a login.  To avoid this, we make an 
		   uninitialised device look like a login-required device, so the 
		   user gets an invalid-PIN error if they try and proceed */
		deviceInfo->flags |= DEVICE_NEEDSLOGIN;
	if( ( pkcs11Info->minPinSize = ( int ) tokenInfo.ulMinPinLen ) < 4 )
		/* Some devices report silly PIN sizes */
		pkcs11Info->minPinSize = 4;
	if( ( pkcs11Info->maxPinSize = ( int ) tokenInfo.ulMaxPinLen ) < 4 )
		/* Some devices report silly PIN sizes (setting this to ULONG_MAX or
		   4GB, which becomes -1 as an int, counts as silly).  Since we can't
		   differentiate between 0xFFFFFFFF = bogus value and 0xFFFFFFFF = 
		   ULONG_MAX we play it safe and set the limit to 8 bytes, which most
		   devices should be able to handle */
		pkcs11Info->maxPinSize = 8;
	labelPtr = tokenInfo.label;
	for( labelLength = 32;
		 labelLength > 0 && \
		 ( labelPtr[ labelLength - 1 ] == ' ' || \
		   !labelPtr[ labelLength - 1 ] ); 
		  labelLength-- );	/* Strip trailing blanks/nulls */
	while( labelLength > 0 && *labelPtr == ' ' )
		{
		/* Strip leading blanks */
		labelPtr++;
		labelLength--;
		}
	if( labelLength > 0 )
		{
		memcpy( pkcs11Info->label, labelPtr, labelLength );
		pkcs11Info->labelLen = labelLength;
		}
	else
		{
		/* There's no label for the token, use the device label instead */
		if( pkcs11InfoTbl[ pkcs11Info->deviceNo ].name[ 0 ] )
			{
			labelLength = \
				min( strlen( pkcs11InfoTbl[ pkcs11Info->deviceNo ].name ),
					 CRYPT_MAX_TEXTSIZE );
			memcpy( pkcs11Info->label, 
					pkcs11InfoTbl[ pkcs11Info->deviceNo ].name, labelLength );
			}
		}
	pkcs11Info->hActiveSignObject = CK_OBJECT_NONE;
	deviceInfo->label = pkcs11Info->label;
	deviceInfo->labelLen = pkcs11Info->labelLen;

	/* Open a session with the device.  This gets a bit awkward because we 
	   can't tell whether a R/W session is OK without opening a session, but 
	   we can't open a session unless we know whether a R/W session is OK, 
	   so we first try for a RW session and if that fails we go for a read-
	   only session */
	status = C_OpenSession( pkcs11Info->slotID, 
							CKF_RW_SESSION | CKF_SERIAL_SESSION, NULL_PTR, 
							NULL_PTR, &hSession );
	if( status == CKR_TOKEN_WRITE_PROTECTED )
		status = C_OpenSession( pkcs11Info->slotID, 
								CKF_SERIAL_SESSION, NULL_PTR, NULL_PTR, 
								&hSession );
	if( status != CKR_OK )
		{
		cryptStatus = pkcs11MapError( pkcs11Info, status, CRYPT_ERROR_OPEN );
		if( cryptStatus == CRYPT_ERROR_OPEN && \
			!( tokenInfo.flags & CKF_USER_PIN_INITIALIZED ) )
			/* We couldn't do much with the error code, it could be that the
			   token hasn't been initialised yet but unfortunately PKCS #11 
			   doesn't define an error code for this condition.  In addition
			   many tokens will allow a session to be opened and then fail 
			   with a "PIN not set" error at a later point (which allows for
			   more accurate error reporting), however a small number won't
			   allow a session to be opened and return some odd-looking error
			   because there's nothing useful available.  The best way to
			   report this in a meaningful manner to the caller is to check
			   whether the user PIN has been initialised, if it hasn't then 
			   it's likely that the token as a whole hasn't been initialised 
			   so we return a not initialised error */
			cryptStatus = CRYPT_ERROR_NOTINITED;
		return( cryptStatus );
		}
	assert( hSession != CK_OBJECT_NONE );
	pkcs11Info->hSession = hSession;
	deviceInfo->flags |= DEVICE_ACTIVE;

	/* Set up the capability information for this device.  Since there can 
	   be devices that have one set of capabilities but not the other (e.g.
	   a smart card that only performs RSA ops), we allow one of the two
	   sets of mechanism info setups to fail, but not both */
	mechanismInfoPtr = getMechanismInfoPKC( &mechanismInfoSize );
	cryptStatus = getCapabilities( deviceInfo, mechanismInfoPtr, 
								   mechanismInfoSize );
	mechanismInfoPtr = getMechanismInfoConv( &mechanismInfoSize );
	cryptStatus2 = getCapabilities( deviceInfo, mechanismInfoPtr, 
									mechanismInfoSize );
	if( cryptStatusError( cryptStatus ) && cryptStatusError( cryptStatus2 ) )
		{
		shutdownFunction( deviceInfo );
		return( ( cryptStatus == CRYPT_ERROR ) ? \
				CRYPT_ERROR_OPEN : ( int ) cryptStatus );
		}

	return( CRYPT_OK );
	}

/* Set up the function pointers to the init/shutdown methods */

int initPKCS11Init( DEVICE_INFO *deviceInfo, const char *name, 
					const int nameLength )
	{
	PKCS11_INFO *pkcs11Info = deviceInfo->devicePKCS11;
#ifdef DYNAMIC_LOAD
	int i, driverNameLength = nameLength;
#else
	UNUSED( name );
#endif /* DYNAMIC_LOAD */

	/* Make sure that the PKCS #11 driver DLL's are loaded */
	if( !pkcs11Initialised )
		return( CRYPT_ERROR_OPEN );

#ifdef DYNAMIC_LOAD
	/* Check whether there's a token name appended to the driver name */
	for( i = 1; i < nameLength - 1; i++ )
		if( name[ i ] == ':' && name[ i + 1 ] == ':' )
			{
			driverNameLength = i;
			break;
			}

	/* If we're auto-detecting the device, use the first one that we find.  
	   There are two basic approaches to this, to keep going until we find
	   something that responds, or to try the first device and report an
	   error if it doesn't respond.  Both have their own problems, keeping
	   going will find (for example) the device in slot 2 if slot 1 is 
	   empty, but will also return a completely unexpected device if slot 1
	   contains a device that isn't responding for some reason.  Conversely,
	   only checking the first device will fail if slot 1 is empty but slot
	   2 isn't.  Users seem to prefer the obvious-fail approach, so we only
	   check the first device and fail if there's a problem.  If they
	   explicitly want a secondary slot, they can specify it by name */
	if( driverNameLength == 12 && \
		!strnicmp( "[Autodetect]", name, driverNameLength ) )
		{
		if( !pkcs11InfoTbl[ 0 ].name[ 0 ] )
			return( CRYPT_ERROR_NOTFOUND );
		pkcs11Info->deviceNo = 0;
		}
	else
		{
		/* Try and find the driver based on its name */
		for( i = 0; i < MAX_PKCS11_DRIVERS; i++ )
			if( !strnicmp( pkcs11InfoTbl[ i ].name, name, driverNameLength ) )
				break;
		if( i >= MAX_PKCS11_DRIVERS )
			return( CRYPT_ERROR_NOTFOUND );
		pkcs11Info->deviceNo = i;
		}
#endif /* DYNAMIC_LOAD */

	/* Set up remaining function and access info */
	deviceInfo->initFunction = initFunction;
	deviceInfo->shutdownFunction = shutdownFunction;
	deviceInfo->devicePKCS11->functionListPtr = \
					pkcs11InfoTbl[ pkcs11Info->deviceNo ].functionListPtr;

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