📄 pkcs11.c
字号:
/****************************************************************************
* *
* cryptlib PKCS #11 Routines *
* Copyright Peter Gutmann 1998-2005 *
* *
****************************************************************************/
#if defined( INC_ALL )
#include "crypt.h"
#include "context.h"
#include "device.h"
#include "pkcs11_api.h"
#include "dev_mech.h"
#include "asn1.h"
#else
#include "crypt.h"
#include "context/context.h"
#include "device/device.h"
#include "device/pkcs11_api.h"
#include "mechs/dev_mech.h"
#include "misc/asn1.h"
#endif /* Compiler-specific includes */
/* Define the following to generate conventional/MAC keys inside the PKCS
#11 device rather than in cryptlib. Note that this imposes a number of
restrictions on the use of encryption keys, see the note for
cipherGenerateKey() for more details */
#define USE_HW_KEYGEN
#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
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Map a PKCS #11-specific error to a cryptlib error */
int pkcs11MapError( PKCS11_INFO *pkcs11Info, const CK_RV errorCode,
const int defaultError )
{
ERROR_INFO *errorInfo = &pkcs11Info->errorInfo;
errorInfo->errorCode = ( int ) errorCode;
switch( ( int ) errorCode )
{
case CKR_OK:
return( CRYPT_OK );
case CKR_HOST_MEMORY:
case CKR_DEVICE_MEMORY:
return( CRYPT_ERROR_MEMORY );
case CKR_DEVICE_ERROR:
case CKR_DEVICE_REMOVED:
case CKR_TOKEN_NOT_PRESENT:
return( CRYPT_ERROR_SIGNALLED );
case CKR_PIN_INCORRECT:
case CKR_PIN_INVALID:
case CKR_PIN_LEN_RANGE:
case CKR_PIN_EXPIRED:
case CKR_PIN_LOCKED:
return( CRYPT_ERROR_WRONGKEY );
case CKR_DATA_INVALID:
case CKR_ENCRYPTED_DATA_INVALID:
case CKR_WRAPPED_KEY_INVALID:
return( CRYPT_ERROR_BADDATA );
case CKR_SIGNATURE_INVALID:
return( CRYPT_ERROR_SIGNATURE );
case CKR_KEY_NOT_WRAPPABLE:
case CKR_KEY_UNEXTRACTABLE:
case CKR_TOKEN_WRITE_PROTECTED:
case CKR_INFORMATION_SENSITIVE:
return( CRYPT_ERROR_PERMISSION );
case CKR_DATA_LEN_RANGE:
case CKR_ENCRYPTED_DATA_LEN_RANGE:
case CKR_SIGNATURE_LEN_RANGE:
case CKR_UNWRAPPING_KEY_SIZE_RANGE:
case CKR_WRAPPING_KEY_SIZE_RANGE:
case CKR_WRAPPED_KEY_LEN_RANGE:
return( CRYPT_ERROR_OVERFLOW );
case CKR_SESSION_EXISTS:
case CKR_SESSION_READ_ONLY_EXISTS:
case CKR_SESSION_READ_WRITE_SO_EXISTS:
case CKR_USER_ALREADY_LOGGED_IN:
case CKR_USER_ANOTHER_ALREADY_LOGGED_IN:
case CKR_CRYPTOKI_NOT_INITIALIZED:
return( CRYPT_ERROR_INITED );
case CKR_USER_NOT_LOGGED_IN:
case CKR_USER_PIN_NOT_INITIALIZED:
case CKR_CRYPTOKI_ALREADY_INITIALIZED:
return( CRYPT_ERROR_NOTINITED );
case CKR_RANDOM_NO_RNG:
return( CRYPT_ERROR_RANDOM );
case CKR_OPERATION_ACTIVE:
return( CRYPT_ERROR_TIMEOUT );
case CKR_TOKEN_NOT_RECOGNIZED:
return( CRYPT_ERROR_NOTFOUND );
}
return( defaultError );
}
/* Extract the time from a PKCS #11 tokenInfo structure */
time_t getTokenTime( CK_TOKEN_INFO *tokenInfo )
{
STREAM stream;
BYTE buffer[ 32 + 8 ];
time_t theTime = MIN_TIME_VALUE + 1;
int length, status;
/* Convert the token time to an ASN.1 time string that we can read using
the standard ASN.1 routines by writing a dummy time value and inserting
the token's time string in its place */
sMemOpen( &stream, buffer, 32 );
writeGeneralizedTime( &stream, theTime, DEFAULT_TAG );
length = stell( &stream );
sMemDisconnect( &stream );
memcpy( buffer + 2, tokenInfo->utcTime, 14 );
sMemConnect( &stream, buffer, length );
status = readGeneralizedTime( &stream, &theTime );
sMemDisconnect( &stream );
return( ( cryptStatusOK( status ) ) ? theTime : 0 );
}
/* Get access to the PKCS #11 device associated with a context */
int getContextDeviceInfo( const CRYPT_HANDLE iCryptContext,
CRYPT_DEVICE *iCryptDevice,
PKCS11_INFO **pkcs11InfoPtrPtr )
{
CRYPT_DEVICE iLocalDevice;
DEVICE_INFO *deviceInfo;
int cryptStatus;
/* Clear return values */
*iCryptDevice = CRYPT_ERROR;
*pkcs11InfoPtrPtr = NULL;
/* Get the the device associated with this context */
cryptStatus = krnlSendMessage( iCryptContext, IMESSAGE_GETDEPENDENT,
&iLocalDevice, OBJECT_TYPE_DEVICE );
if( cryptStatusError( cryptStatus ) )
return( cryptStatus );
/* Get the PKCS #11 info from the device info */
cryptStatus = krnlAcquireObject( iLocalDevice, OBJECT_TYPE_DEVICE,
( void ** ) &deviceInfo,
CRYPT_ERROR_SIGNALLED );
if( cryptStatusError( cryptStatus ) )
return( cryptStatus );
*iCryptDevice = iLocalDevice;
*pkcs11InfoPtrPtr = deviceInfo->devicePKCS11;
return( CRYPT_OK );
}
/****************************************************************************
* *
* Device Init/Shutdown/Device Control Routines *
* *
****************************************************************************/
/* Prototypes for functions to get and free device capability information */
static int getCapabilities( DEVICE_INFO *deviceInfo );
static void freeCapabilities( DEVICE_INFO *deviceInfo );
/* Handle device control functions */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int controlFunction( INOUT DEVICE_INFO *deviceInfo,
IN_ATTRIBUTE const CRYPT_ATTRIBUTE_TYPE type,
IN_BUFFER_OPT( dataLength ) void *data,
IN_LENGTH_SHORT_Z const int dataLength,
INOUT_OPT MESSAGE_FUNCTION_EXTINFO *messageExtInfo )
{
CK_RV status;
PKCS11_INFO *pkcs11Info = deviceInfo->devicePKCS11;
/* Handle token present/active checks */
if( type == CRYPT_DEVINFO_LOGGEDIN )
{
CK_TOKEN_INFO tokenInfo;
CK_SLOT_INFO slotInfo;
/* Check whether the user is still logged in. This is rather
problematic because some devices can't detect a token removal,
and if they do they often can't report it to the driver. It's
also possible in some devices to remove the token and re-insert
it later without that being regarded as logging out (or you can
remove the smart card and insert your frequent flyer card and
it's still regarded as a card present). In addition if the
reader supports its own authentication mechanisms (even if it
forces a logout if the token is removed) it's possible for the
user to reinsert the token and reauthenticate themselves and it
appears as if they never logged out. In fact the only totally
foolproof way to detect a token removal/change is to try and use
the token to perform a crypto operation, which is a rather
suboptimal detection mechanism.
Because of this, the best that we can do here is check the token-
present flag and report a token-changed error if it's not set.
In addition since some devices only do a minimal check with
C_GetSlotInfo() (e.g. checking whether a microswitch is held
open by something in the slot, see above) we first call
C_GetTokenInfo(), which has a greater chance of actually trying
to access the token, before we call C_GetSlotInfo().
If there's a problem reported, we don't perform an implicit
shutdown since the user may choose to re-authenticate to the
device or perform some other action that we have no control over
in response to the token-removed notification */
status = C_GetTokenInfo( pkcs11Info->slotID, &tokenInfo );
if( status != CKR_OK )
return( pkcs11MapError( pkcs11Info, status,
CRYPT_ERROR_SIGNALLED ) );
status = C_GetSlotInfo( pkcs11Info->slotID, &slotInfo );
if( status != CKR_OK )
return( pkcs11MapError( pkcs11Info, status,
CRYPT_ERROR_SIGNALLED ) );
if( !( slotInfo.flags & CKF_TOKEN_PRESENT ) )
return( CRYPT_ERROR_SIGNALLED );
return( CRYPT_OK );
}
/* Handle user authorisation */
if( type == CRYPT_DEVINFO_AUTHENT_USER || \
type == CRYPT_DEVINFO_AUTHENT_SUPERVISOR )
{
/* Make sure that the PIN is within range */
if( dataLength < pkcs11Info->minPinSize || \
dataLength > pkcs11Info->maxPinSize )
return( CRYPT_ARGERROR_NUM1 );
/* If the user is already logged in, log them out before we try
logging in with a new authentication value */
if( deviceInfo->flags & DEVICE_LOGGEDIN )
{
C_Logout( pkcs11Info->hSession );
deviceInfo->flags &= ~DEVICE_LOGGEDIN;
}
/* Authenticate the user to the device */
status = C_Login( pkcs11Info->hSession,
( type == CRYPT_DEVINFO_AUTHENT_USER ) ? \
CKU_USER : CKU_SO, ( CK_CHAR_PTR ) data,
( CK_ULONG ) dataLength );
if( status != CKR_OK )
{
static const CK_OBJECT_CLASS class = CKO_SECRET_KEY;
const CK_KEY_TYPE type = CKK_DES;
static const CK_BBOOL bFalse = FALSE, bTrue = TRUE;
CK_ATTRIBUTE keyTemplate[] = {
{ CKA_CLASS, ( CK_VOID_PTR ) &class, sizeof( CK_OBJECT_CLASS ) },
{ CKA_KEY_TYPE, ( CK_VOID_PTR ) &type, sizeof( CK_KEY_TYPE ) },
{ CKA_TOKEN, ( CK_VOID_PTR ) &bFalse, sizeof( CK_BBOOL ) },
{ CKA_PRIVATE, ( CK_VOID_PTR ) &bTrue, sizeof( CK_BBOOL ) },
{ CKA_SENSITIVE, ( CK_VOID_PTR ) &bTrue, sizeof( CK_BBOOL ) },
{ CKA_VALUE, "12345678", 8 } /* Dummy key value */
};
CK_OBJECT_HANDLE hObject;
/* The check for CKR_USER_ALREADY_LOGGED_IN is logical since we
may already be logged in from another session, however
several buggy drivers return CKR_USER_ALREADY_LOGGED_IN
without actually logging the user in, so that all further
operations fail with CKR_USER_NOT_LOGGED_IN. To try and
detect this, if we get a CKR_USER_ALREADY_LOGGED_IN we try
and create the sort of object that's likely to require a
login and use that to see whether we're really logged in or
not */
if( status != CKR_USER_ALREADY_LOGGED_IN )
return( pkcs11MapError( pkcs11Info, status,
CRYPT_ERROR_FAILED ) );
/* Try and create the sort of object that'd normally require a
login. This can fail for reasons other than the login bug
(for example DES isn't supported for this token type) so we
only check for the specific login bug error code */
status = C_CreateObject( pkcs11Info->hSession,
( CK_ATTRIBUTE_PTR ) keyTemplate, 6,
&hObject );
if( status == CKR_USER_NOT_LOGGED_IN )
{
assert( DEBUG_WARN );
return( CRYPT_ERROR_NOTINITED );
}
assert( hObject != CK_OBJECT_NONE );
C_DestroyObject( pkcs11Info->hSession, hObject );
}
/* The device is now ready for use */
deviceInfo->flags |= DEVICE_LOGGEDIN;
return( CRYPT_OK );
}
/* Handle authorisation value changes. The init SO/user PIN
functionality is a bit awkward in that it has to fill the gap between
C_InitToken() (which usually sets the SSO PIN but may also take an
initialisation PIN and leave the token in a state where the only valid
operation is to set the SSO PIN) and C_SetPIN() (which can only set the
SSO PIN for the SSO or the user PIN for the user). Setting the user
PIN by the SSO, which is usually required to perform any useful (non-
administrative) function with the token, requires the special-case
C_InitPIN(). In addition we can't speculatively set the user PIN to
be the same as the SSO PIN (which would be useful because in most
cases the user *is* the SSO, thus ensuring that the device behaves as
expected when the user isn't even aware that there are SSO and user
roles) because devices that implement an FSM for initialisation will
move into an undesired state once the SSO -> user change is triggered.
The FSM for initialisation on devices that perform a multi-stage
bootstrap and require all of the various intialisation functions to
be used one after the other (e.g. Fortezza) is:
uninitialised/zeroised
v
C_InitToken (enter init or SSO PIN)
v
initialised
v
C_SetPIN (change init PIN -> SSO PIN)
v
SSO initialised
v
C_InitPIN (set user PIN)
v
user initialised
v
C_Logout
C_Login (move from SO -> user state)
The final logout/login is only needed with some tokens, in others
the move to user state is automatic once the user PIN is set by the
SO */
if( type == CRYPT_DEVINFO_SET_AUTHENT_SUPERVISOR )
{
/* Make sure that the PIN is within range */
if( dataLength < pkcs11Info->minPinSize || \
dataLength > pkcs11Info->maxPinSize )
return( CRYPT_ARGERROR_NUM1 );
/* Make sure that there's an SSO PIN present from a previous device
initialisation */
if( pkcs11Info->defaultSSOPinLen <= 0 )
{
setErrorInfo( deviceInfo, CRYPT_DEVINFO_INITIALISE,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -