/**************************************************************************** * * * 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 */