int getPgpPacketInfo( INOUT STREAM *stream, OUT QUERY_INFO *queryInfo ) { const long startPos = stell( stream ); long offset, length; int ctb, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) ); /* Clear return value */ memset( queryInfo, 0, sizeof( QUERY_INFO ) ); /* Read the packet header and extract information from the CTB. Note that the assignment of version numbers is speculative only because it's possible to use PGP 2.x packet headers to wrap up OpenPGP packets */ status = pgpReadPacketHeader( stream, &ctb, &length, 8 ); if( cryptStatusError( status ) ) return( status ); queryInfo->formatType = CRYPT_FORMAT_PGP; queryInfo->version = pgpGetPacketVersion( ctb ); offset = stell( stream ); if( cryptStatusError( offset ) ) return( offset ); queryInfo->size = ( offset - startPos ) + length; switch( pgpGetPacketType( ctb ) ) { case PGP_PACKET_SKE: queryInfo->type = CRYPT_OBJECT_ENCRYPTED_KEY; break; case PGP_PACKET_PKE: queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY; break; case PGP_PACKET_SIGNATURE: queryInfo->type = CRYPT_OBJECT_SIGNATURE; break; case PGP_PACKET_SIGNATURE_ONEPASS: /* First half of a one-pass signature, this is given a special type of 'none' since it's not a normal packet */ queryInfo->type = CRYPT_OBJECT_NONE; break; default: DEBUG_DIAG(( "Found unrecognised ctb %X", ctb )); assert( DEBUG_WARN ); return( CRYPT_ERROR_BADDATA ); } /* Make sure that all of the data is present without resetting the stream */ return( ( sMemDataLeft( stream ) < length ) ? \ CRYPT_ERROR_UNDERFLOW : CRYPT_OK ); }
CHECK_RETVAL_ERROR \ static int convertErrorStatus( IN_ERROR const int status ) { REQUIRES( cryptStatusError( status ) ); if( cryptArgError( status ) ) { DEBUG_DIAG(( "Error exit was passed argError status" )); assert( DEBUG_WARN ); return( CRYPT_ERROR_FAILED ); } return( status ); }
static BOOLEAN formatErrorString( OUT ERROR_INFO *errorInfoPtr, IN_STRING const char *format, IN va_list argPtr ) { assert( isWritePtr( errorInfoPtr, sizeof( ERROR_INFO ) ) ); assert( isReadPtr( format, 4 ) ); REQUIRES_B( verifyVAList( argPtr ) ); /* Clear return value */ memset( errorInfoPtr, 0, sizeof( ERROR_INFO ) ); /* This function is a bit tricky to deal with because of the braindamaged behaviour of some of the underlying functions that it may be mapped to. Specifically, (v)snprintf() returns the number of bytes it *could* have written had it felt like it rather than how many it actually wrote on non-Windows systems and an error indicator with no guarantee of null-termination on Windows systems. The latter isn't a problem because we both catch the error and don't require null termination, the former is more problematic because it can lead to a length indication that's larger than the actual buffer. To handle this we explicitly check for an overflow as well as an error/underflow */ errorInfoPtr->errorStringLength = \ vsprintf_s( errorInfoPtr->errorString, MAX_ERRMSG_SIZE, format, argPtr ); if( errorInfoPtr->errorStringLength <= 0 || \ errorInfoPtr->errorStringLength > MAX_ERRMSG_SIZE ) { DEBUG_DIAG(( "Invalid error string data" )); assert( DEBUG_WARN ); setErrorString( errorInfoPtr, "(Couldn't record error information)", 35 ); return( FALSE ); } return( TRUE ); }
int initCertMgmtACL( INOUT KERNEL_DATA *krnlDataPtr ) { int i; assert( isWritePtr( krnlDataPtr, sizeof( KERNEL_DATA ) ) ); /* Perform a consistency check on the cert management ACLs */ for( i = 0; certMgmtACLTbl[ i ].action != MECHANISM_NONE && \ i < FAILSAFE_ARRAYSIZE( certMgmtACLTbl, CERTMGMT_ACL ); i++ ) { const CERTMGMT_ACL *certMgmtACL = &certMgmtACLTbl[ i ]; /* Actions and permissions are consistent */ ENSURES( certMgmtACL->action > CRYPT_CERTACTION_NONE && \ certMgmtACL->action < CRYPT_CERTACTION_LAST ); ENSURES( certMgmtACL->access == ACTION_PERM_NONE || \ certMgmtACL->access == ACTION_PERM_NONE_EXTERNAL || \ certMgmtACL->access == ACTION_PERM_ALL ); /* If it's a no-access ACL, all mechanisms should be blocked */ if( certMgmtACL->access == ACTION_PERM_NONE ) { ENSURES( paramInfo( certMgmtACL, 0 ).valueType == PARAM_VALUE_NONE ); continue; } /* If it's an internal-only ACL, it always needs a request parameter */ if( certMgmtACL->access == ACTION_PERM_NONE_EXTERNAL ) { if( paramInfo( certMgmtACL, 1 ).valueType != PARAM_VALUE_OBJECT || \ ( paramInfo( certMgmtACL, 1 ).subTypeA & \ ~( ST_CERT_CERTREQ | ST_CERT_REQ_CERT | \ ST_CERT_REQ_REV | ST_CERT_CERT ) ) || \ paramInfo( certMgmtACL, 1 ).subTypeB != ST_NONE ) { DEBUG_DIAG(( "Certificate management ACLs inconsistent" )); retIntError(); } } /* If it requires a CA key parameter, it must be a private-key context with the key loaded and an attached CA certificate */ if( paramInfo( certMgmtACL, 0 ).valueType == PARAM_VALUE_OBJECT ) { ENSURES( paramInfo( certMgmtACL, 0 ).subTypeA == ST_CTX_PKC && \ paramInfo( certMgmtACL, 0 ).subTypeB == ST_NONE && \ paramInfo( certMgmtACL, 0 ).flags == ACL_FLAG_HIGH_STATE ); if( ( secParamInfo( certMgmtACL, 0 ).subTypeA & \ ~( ST_CERT_CERT | ST_CERT_CERTCHAIN ) ) || \ secParamInfo( certMgmtACL, 0 ).subTypeB != ST_NONE || \ secParamInfo( certMgmtACL, 0 ).flags != ACL_FLAG_HIGH_STATE ) { DEBUG_DIAG(( "Certificate management ACLs inconsistent" )); retIntError(); } continue; } ENSURES( paramInfo( certMgmtACL, 0 ).valueType == PARAM_VALUE_UNUSED ); } ENSURES( i < FAILSAFE_ARRAYSIZE( certMgmtACLTbl, CERTMGMT_ACL ) ); /* Set up the reference to the kernel data block */ krnlData = krnlDataPtr; return( CRYPT_OK ); }
static int loadPKCS11driver( PKCS11_DRIVER_INFO *pkcs11Info, const char *driverName ) { CK_C_GetFunctionList pC_GetFunctionList; CK_INFO info DUMMY_INIT_STRUCT; CK_RV status; #ifdef __WIN16__ UINT errorMode; #endif /* __WIN16__ */ BOOLEAN isInitialised = FALSE; int i = 32; assert( isWritePtr( pkcs11Info, sizeof( PKCS11_DRIVER_INFO ) ) ); assert( isReadPtr( driverName, 4 ) ); /* 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 */ /* Get the access information for the PKCS #11 library, initialise it, and get information 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 { CK_FUNCTION_LIST_PTR functionListPtr; /* The following two-step initialisation is needed because PKCS #11 uses a 1-byte alignment on structs, which means that if we pass in the pkcs11Info member address directly we run into alignment problems with 64-bit architectures */ status = pC_GetFunctionList( &functionListPtr ) & 0xFFFF; if( status == CKR_OK ) pkcs11Info->functionListPtr = functionListPtr; } if( status != CKR_OK ) { /* Free the library reference and clear the information */ DynamicUnload( pkcs11Info->hPKCS11 ); memset( pkcs11Info, 0, sizeof( PKCS11_DRIVER_INFO ) ); return( CRYPT_ERROR ); } status = C_Initialize( NULL_PTR ) & 0xFFFF; if( status == CKR_ARGUMENTS_BAD && \ strFindStr( driverName, strlen( driverName ), "softokn3.", 9 ) >= 0 ) { typedef struct CK_C_INITIALIZE_ARGS { CK_CREATEMUTEX CreateMutex; CK_DESTROYMUTEX DestroyMutex; CK_LOCKMUTEX LockMutex; CK_UNLOCKMUTEX UnlockMutex; CK_FLAGS flags; CK_VOID_PTR LibraryParameters; CK_VOID_PTR pReserved; } Netscape_CK_C_INITIALIZE_ARGS; Netscape_CK_C_INITIALIZE_ARGS initArgs; /* Netscape invented their own extension to CK_C_INITIALIZE_ARGS, adding a 'LibraryParameters' string to allow the specification of free-form parameters. If the Netscape library's C_Initialize() is called then it'll return CKR_ARGUMENTS_BAD (rather than assuming sensible default values), so that we have to call it again with explicitly-specified parameters. Even this doesn't work properly though, according to the Netscape docs we can set LibraryParameters to NULL, but this still results in C_Initialize() returning CKR_ARGUMENTS_BAD. Later in the docs is a requirement that the softokn3 library be passed a "parameters=" string, but it's unclear what parameters need to be set, to what values, and what effect these values may have on the behaviour of existing configs for applications like Firefox. The following config lets the C_Initialize() call succeed but causes odd failures later in other PKCS #11 functions */ DEBUG_DIAG(( "Driver is the buggy Netscape/NSS one that requires " "nonstandard initialisation, see the code comment for " "details" )); assert( DEBUG_WARN ); memset( &initArgs, 0, sizeof( Netscape_CK_C_INITIALIZE_ARGS ) ); initArgs.LibraryParameters = \ "configdir='.' certPrefix='' keyPrefix='' secmod='secmod.db' " "flags=noModDB,noCertDB,noKeyDB,optimizeSpace,readOnly"; status = C_Initialize( &initArgs ); } 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 ); } /* 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 ); }
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; assert( isWritePtr( deviceInfo, sizeof( DEVICE_INFO ) ) ); assert( isReadPtr( name, nameLength ) ); /* Get information on all available slots */ memset( slotList, 0, sizeof( slotList ) ); status = C_GetSlotList( TRUE, slotList, &slotCount ); if( status != CKR_OK ) return( pkcs11MapError( 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 && \ !strCompare( tokenName, tokenInfo.label, tokenNameLength ) ) break; } ENSURES( tokenSlot < FAILSAFE_ITERATIONS_MED ); 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( 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( 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 check is triggered then 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 (if we can detect them), which will (by definition) have the same time as the system time */ if( !( pkcs11InfoTbl[ pkcs11Info->deviceNo ].name[ 0 ] && \ !strCompare( pkcs11InfoTbl[ pkcs11Info->deviceNo ].name, "Software", 8 ) ) && \ theTime == currentTime ) { DEBUG_DIAG(( "PKCS #11 token time is the same as the host " "system time, token time may be faked by the " "driver." )); assert( DEBUG_WARN ); } } 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 */ DEBUG_DIAG(( "Driver reports suspicious minimum PIN size %lu, " "using 4", tokenInfo.ulMinPinLen )); 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 */ DEBUG_DIAG(( "Driver reports suspicious maximum PIN size %lu, " "using 8", tokenInfo.ulMinPinLen )); pkcs11Info->maxPinSize = 8; } labelPtr = ( char * ) 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; sanitiseString( pkcs11Info->label, CRYPT_MAX_TEXTSIZE, 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( 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 ); } ENSURES( 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 information 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 ); }
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 = isPkcAlgo( cryptAlgo ) ? 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 ) ) ); /* Set up canary values for the mechanism information in case the driver blindly reports success for every mechanism that we ask for */ memset( &pMechanism, 0, sizeof( CK_MECHANISM_INFO ) ); pMechanism.ulMinKeySize = 0xA5A5; pMechanism.ulMaxKeySize = 0x5A5A; /* 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 ); if( pMechanism.ulMinKeySize == 0xA5A5 && \ pMechanism.ulMaxKeySize == 0x5A5A ) { /* The driver reported that this mechanism is available but didn't update the mechanism information, it's lying */ DEBUG_DIAG(( "Driver reports that mechanism %X is available even " "though it isn't", mechanismInfoPtr->mechanism )); assert( DEBUG_WARN ); return( NULL ); } status = krnlSendMessage( deviceInfo->ownerHandle, IMESSAGE_GETATTRIBUTE, &hardwareOnly, CRYPT_OPTION_DEVICE_PKCS11_HARDWAREONLY ); if( cryptStatusOK( status ) && hardwareOnly && \ !( pMechanism.flags & CKF_HW ) ) { DEBUG_DIAG(( "Skipping mechanism %X, which is only available in " "software emulation", mechanismInfoPtr->mechanism )); return( NULL ); } if( mechanismInfoPtr->requiredFlags != CKF_NONE ) { /* Make sure that the driver flags indicate support for the specific functionality that we require */ if( ( mechanismInfoPtr->requiredFlags & \ pMechanism.flags ) != mechanismInfoPtr->requiredFlags ) { DEBUG_DIAG(( "Driver reports that mechanism %X only has " "capabilities %lX when we require %lX", mechanismInfoPtr->mechanism, mechanismInfoPtr->requiredFlags & pMechanism.flags, mechanismInfoPtr->requiredFlags )); ////////////////////////////////// // Kludge to allow it to be used ////////////////////////////////// // assert( DEBUG_WARN ); // 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++ ); ENSURES_N( i < FAILSAFE_ARRAYSIZE( capabilityTemplates, CAPABILITY_INFO ) ); ENSURES_N( 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 bounds (e.g. 1-bit RSA, "You naughty minKey" or alternatively 4Gbit RSA) so we adjust them to a sane value if necessary. We also limit the maximum key size to match the cryptlib native maximum key size, both for consistency and because cryptlib performs buffer allocation based on the maximum native buffer size */ if( pMechanism.ulMinKeySize < 0 || \ pMechanism.ulMinKeySize >= 10000L ) { DEBUG_DIAG(( "Driver reports invalid minimum key size %lu for " "%s algorithm", pMechanism.ulMinKeySize, capabilityInfo->algoName )); assert( DEBUG_WARN ); minKeySize = 0; } if( pMechanism.ulMaxKeySize < 0 || \ pMechanism.ulMaxKeySize >= 100000L ) { DEBUG_DIAG(( "Driver reports invalid maximum key size %lu for " "%s algorithm", pMechanism.ulMaxKeySize, capabilityInfo->algoName )); assert( DEBUG_WARN ); maxKeySize = 0; } 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 */ DEBUG_DIAG(( "Driver reports maximum key size %d < minimum key " "size %d for %s algorithm", capabilityInfo->maxKeySize, capabilityInfo->minKeySize, capabilityInfo->algoName )); assert( DEBUG_WARN ); if( isPKC ) { capabilityInfo->maxKeySize = \ max( capabilityInfo->minKeySize, bitsToBytes( 2048 ) ); } else capabilityInfo->maxKeySize = 16; } if( capabilityInfo->keySize > capabilityInfo->maxKeySize ) capabilityInfo->keySize = capabilityInfo->maxKeySize; capabilityInfo->endFunction = genericEndFunction; } /* Set up the device-specific handlers */ capabilityInfo->selfTestFunction = selfTestFunction; capabilityInfo->getInfoFunction = getDefaultInfo; if( !isPKC ) capabilityInfo->initParamsFunction = initGenericParams; 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_GCM: capabilityInfo->encryptGCMFunction = 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_GCM: capabilityInfo->decryptGCMFunction = 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( isConvAlgo( cryptAlgo ) || isMacAlgo( cryptAlgo ) ) { capabilityInfo->paramKeyType = mechanismInfoPtr->keyType; capabilityInfo->paramKeyGen = mechanismInfoPtr->keygenMechanism; capabilityInfo->paramDefaultMech = mechanismInfoPtr->defaultMechanism; } /* Some drivers report bizarre combinations of capabilities like (for RSA) sign, verify, and decrypt but not encrypt, which will fail later sanity checks. If we run into one of these we force the capabilities to be consistent by disabling any for which only partial capabilities are supported */ if( isPkcAlgo( cryptAlgo ) ) { if( capabilityInfo->decryptFunction != NULL && \ capabilityInfo->encryptFunction == NULL ) { DEBUG_DIAG(( "Driver reports decryption but not encryption " "capability for %s algorithm, disabling " "encryption", capabilityInfo->algoName )); capabilityInfo->decryptFunction = NULL; } if( capabilityInfo->signFunction != NULL && \ capabilityInfo->sigCheckFunction == NULL ) { DEBUG_DIAG(( "Driver reports signature-generation but not " "signature-verification capability for %s " "algorithm, disabling signing", capabilityInfo->algoName )); ////////////////////////////////// // Kludge to allow it to be used ////////////////////////////////// if( cryptAlgo == CRYPT_ALGO_ECDSA ) capabilityInfo->sigCheckFunction = capabilityInfo->signFunction; else capabilityInfo->signFunction = NULL; } /* If we've now disabled all capabilities, we can't use this algorithm */ if( capabilityInfo->decryptFunction == NULL && \ capabilityInfo->signFunction == NULL ) { DEBUG_DIAG(( "Use of algorithm %s disabled since no consistent " "set of capabilities is available", capabilityInfo->algoName )); clFree( "getCapability", capabilityInfo ); assert( DEBUG_WARN ); return( NULL ); } } /* If it's not a conventional encryption algo, we're done */ if( !isConvAlgo( cryptAlgo ) ) 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_GCM: if( pMechanism.flags & CKF_ENCRYPT ) capabilityInfo->encryptGCMFunction = \ mechanismInfoPtr->encryptFunction; if( pMechanism.flags & CKF_DECRYPT ) capabilityInfo->decryptGCMFunction = \ mechanismInfoPtr->decryptFunction; break; default: retIntError_Null(); } } ENSURES_N( iterationCount < maxMechanisms ); return( ( CAPABILITY_INFO * ) capabilityInfo ); }
int processChannelControlMessage( INOUT SESSION_INFO *sessionInfoPtr, INOUT STREAM *stream ) { SSH_INFO *sshInfo = sessionInfoPtr->sessionSSH; const long prevChannelNo = \ getCurrentChannelNo( sessionInfoPtr, CHANNEL_READ ); long channelNo; int status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); assert( isWritePtr( stream, sizeof( STREAM ) ) ); /* See what we've got. SSH has a whole pile of no-op equivalents that we have to handle as well as the obvious no-ops. We can also get global and channel requests for assorted reasons and a constant stream of window adjusts to implement the SSH performance handbrake */ switch( sshInfo->packetType ) { case SSH2_MSG_GLOBAL_REQUEST: status = processChannelRequest( sessionInfoPtr, stream, CRYPT_UNUSED ); if( cryptStatusError( status ) && status != OK_SPECIAL ) return( status ); return( OK_SPECIAL ); case SSH2_MSG_CHANNEL_OPEN: /* Process the channel-open request. In theory we could immediately reject any attempts by the server to open a channel to the client at this point, but unfortunately we have to process a considerable portion of the channel-open request in order to use the information in it to send a request-denied response back to the server */ status = processChannelOpen( sessionInfoPtr, stream ); if( cryptStatusError( status ) ) return( status ); /* Tell the caller that they have to process the new channel information before they can continue */ return( CRYPT_ENVELOPE_RESOURCE ); case SSH2_MSG_IGNORE: case SSH2_MSG_DEBUG: /* Nothing to see here, move along, move along: byte SSH2_MSG_IGNORE string data byte SSH2_MSG_DEBUG boolean always_display string message string language_tag */ return( OK_SPECIAL ); case SSH2_MSG_DISCONNECT: /* This only really seems to be used during the handshake phase, once a channel is open it (and the session as a whole) is disconnected with a channel EOF/close, but we handle it here anyway just in case */ return( getDisconnectInfo( sessionInfoPtr, stream ) ); #ifdef KPYM_HACK case SSH2_MSG_KEXDH_INIT/*SSH2_MSG_KEXDH_GEX_REQUEST_OLD*/: case SSH2_MSG_KEXDH_GEX_INIT: case SSH2_MSG_KEXDH_GEX_REQUEST_NEW: case SSH2_MSG_KEXINIT: case SSH2_MSG_NEWKEYS: { int status = ke_DH_ROUTER(sessionInfoPtr, stream, sshInfo->packetType); if( status == CRYPT_OK )return( OK_SPECIAL ); else return status; } #else case SSH2_MSG_KEXINIT: #endif /* The SSH spec is extremely vague about the sequencing of operations during a rehandshake. Unlike SSL there's no real indication of what happens to the connection-layer transfers while a transport-layer rehandshake is in progress. Also unlike SSL we can't refuse a rehandshake by ignoring the request, so once we've fallen we can't get up any more. This is most obvious with ssh.com's server, which starting with version 2.3.0 would do a rehandshake every hour (for a basic encrypted telnet session, while a high-volume IPsec link can run for hours before it feels the need to do this). To make things even messier, neither side can block for too long waiting for the rehandshake to complete before sending new data because the lack of WINDOW_ADJUSTs (in an implementation that sends these with almost every packet, as most do) will screw up flow control and lead to deadlock. This problem got so bad that as of 2.4.0 the ssh.com implementation would detect OpenSSH (the other main implementation at the time) and disable the rehandshake when it was talking to it, but it may not do this for other implementations. To avoid falling into this hole, or at least to fail obviously when the two sides can't agree on how to handle the layering mismatch problem, we report a rehandshake request as an error. Trying to handle it properly results in hard-to- diagnose errors (it depends on what the layers are doing at the time of the problem), typically some bad-packet error when the other side tries to interpret a connection-layer packet as part of the rehandshake, or when the two sides disagree on when to switch keys and one of the two decrypts with the wrong keys and gets a garbled packet type */ retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Unexpected KEXINIT request received" ) ); case SSH2_MSG_CHANNEL_DATA: case SSH2_MSG_CHANNEL_EXTENDED_DATA: case SSH2_MSG_CHANNEL_REQUEST: case SSH2_MSG_CHANNEL_WINDOW_ADJUST: case SSH2_MSG_CHANNEL_EOF: case SSH2_MSG_CHANNEL_CLOSE: /* All channel-specific messages end up here */ channelNo = readUint32( stream ); if( cryptStatusError( channelNo ) ) { /* We can't send an error response to a channel request at this point both because we haven't got to the response- required flag yet and because SSH doesn't provide a mechanism for returning an error response without an accompanying channel number. The best that we can do is to quietly ignore the packet */ retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Invalid channel number in channel-specific packet " "type %d", sshInfo->packetType ) ); } if( channelNo != getCurrentChannelNo( sessionInfoPtr, \ CHANNEL_READ ) ) { /* It's a request on something other than the current channel, try and select the new channel */ status = selectChannel( sessionInfoPtr, channelNo, CHANNEL_READ ); if( cryptStatusError( status ) ) { /* As before for error handling */ retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Invalid channel number %lX in " "channel-specific packet type %d, current " "channel is %lX", channelNo, sshInfo->packetType, prevChannelNo ) ); } } break; default: { BYTE buffer[ 16 + 8 ]; int length; /* We got something unexpected, throw an exception in the debug version and let the caller know the details */ DEBUG_DIAG(( "Unexpected control packet %d", sshInfo->packetType )); assert( DEBUG_WARN ); status = length = sread( stream, buffer, 8 ); if( cryptStatusError( status ) || length < 8 ) { /* There's not enough data present to dump the start of the packet, provide a more generic response */ retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Unexpected control packet type %d received", sshInfo->packetType ) ); } retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, SESSION_ERRINFO, "Unexpected control packet type %d received, " "beginning %02X %02X %02X %02X %02X %02X %02X %02X", sshInfo->packetType, buffer[ 0 ], buffer[ 1 ], buffer[ 2 ], buffer[ 3 ], buffer[ 4 ], buffer[ 5 ], buffer[ 6 ], buffer[ 7 ] ) ); } } /* From here on we're processing a channel-specific message that applies to the currently selected channel */ switch( sshInfo->packetType ) { case SSH2_MSG_CHANNEL_DATA: case SSH2_MSG_CHANNEL_EXTENDED_DATA: { int length; /* Get the payload length and make sure that it's (approximately) valid, more exact checking has already been done by the caller so we don't need to return extended error information as this is just a backup check */ status = length = readUint32( stream ); if( cryptStatusError( status ) || \ length < 0 || length > sessionInfoPtr->receiveBufSize ) return( CRYPT_ERROR_BADDATA ); /* These are messages that consume window space, adjust the data window and communicate changes to the other side if necessary */ status = handleWindowAdjust( sessionInfoPtr, channelNo, length ); if( cryptStatusError( status ) ) return( status ); /* If it's a standard data packet, we're done */ if( sshInfo->packetType == SSH2_MSG_CHANNEL_DATA ) return( CRYPT_OK ); /* The extended data message is used for out-of-band data sent over a channel, specifically output sent to stderr from a shell command. What to do with this is somewhat uncertain, the only possible action that we could take apart from just ignoring it is to convert it back to in-band data. However, something running a shell command may not expect to get anything returned in this manner (see the comment for the port-forwarding channel open in the client-side channel-open code for more on this) so for now we just ignore it and assume that the user will rely on results sent as in-band data. This should be fairly safe since this message type seems to be rarely (if ever) used, so apps will function without it */ return( OK_SPECIAL ); } case SSH2_MSG_CHANNEL_REQUEST: status = processChannelRequest( sessionInfoPtr, stream, prevChannelNo ); if( cryptStatusError( status ) && status != OK_SPECIAL ) return( status ); return( OK_SPECIAL ); case SSH2_MSG_CHANNEL_WINDOW_ADJUST: /* Another noop-equivalent (but a very performance-affecting one) */ return( OK_SPECIAL ); case SSH2_MSG_CHANNEL_EOF: /* According to the SSH docs the EOF packet is mostly a courtesy notification, however many implementations seem to use a channel EOF in place of a close before sending a disconnect message */ #ifdef KPYM_HACK #else return( OK_SPECIAL ); #endif case SSH2_MSG_CHANNEL_CLOSE: /* The peer has closed their side of the channel, if our side isn't already closed (in other words if this message isn't a response to a close that we sent), close our side as well */ if( getChannelStatusByChannelNo( sessionInfoPtr, channelNo ) == CHANNEL_BOTH ) { #ifdef KPYM_HACK status = sendChannelClose( sessionInfoPtr, channelNo, CHANNEL_BOTH, TRUE ); /* We've already closed our side of the channel, delete it */ status = deleteChannel( sessionInfoPtr, channelNo, CHANNEL_BOTH, TRUE ); #else status = sendChannelClose( sessionInfoPtr, channelNo, CHANNEL_BOTH, TRUE ); #endif } else { /* We've already closed our side of the channel, delete it */ status = deleteChannel( sessionInfoPtr, channelNo, CHANNEL_BOTH, TRUE ); } /* If this wasn't the last channel, we're done */ if( status != OK_SPECIAL ) return( OK_SPECIAL ); /* We've closed the last channel, indicate that the overall connection is now closed. This behaviour isn't mentioned in the spec but it seems to be the standard way of handling things, particularly for the most common case where channel == session */ sessionInfoPtr->flags |= SESSION_SENDCLOSED; retExt( CRYPT_ERROR_COMPLETE, ( CRYPT_ERROR_COMPLETE, SESSION_ERRINFO, "Remote system closed last remaining SSH channel" ) ); } retIntError(); }
int initKeymgmtACL( INOUT KERNEL_DATA *krnlDataPtr ) { int i; assert( isWritePtr( krnlDataPtr, sizeof( KERNEL_DATA ) ) ); /* If we're running a fuzzing build, skip the lengthy self-checks */ #ifdef CONFIG_FUZZ krnlData = krnlDataPtr; return( CRYPT_OK ); #endif /* CONFIG_FUZZ */ /* Perform a consistency check on the key management ACLs */ for( i = 0; keyManagementACL[ i ].itemType != KEYMGMT_ITEM_NONE && \ i < FAILSAFE_ARRAYSIZE( keyManagementACL, KEYMGMT_ACL ); i++ ) { const KEYMGMT_ACL *keyMgmtACL = &keyManagementACL[ i ]; int j; if( keyMgmtACL->keysetR_subTypeA != ST_NONE || \ ( keyMgmtACL->keysetR_subTypeB & ( SUBTYPE_CLASS_A | \ SUBTYPE_CLASS_C ) ) || \ ( keyMgmtACL->keysetR_subTypeB & \ ~( SUBTYPE_CLASS_B | ST_KEYSET_ANY | ST_DEV_P11 | \ ST_DEV_CAPI | ST_DEV_HW ) ) != 0 || \ keyMgmtACL->keysetR_subTypeC != ST_NONE ) { DEBUG_DIAG(( "Key management ACLs inconsistent" )); retIntError(); } if( keyMgmtACL->keysetR_subTypeA != ST_NONE || \ ( keyMgmtACL->keysetW_subTypeB & ( SUBTYPE_CLASS_A | \ SUBTYPE_CLASS_C ) ) || \ ( keyMgmtACL->keysetW_subTypeB & \ ~( SUBTYPE_CLASS_B | ST_KEYSET_ANY | ST_DEV_P11 | \ ST_DEV_CAPI | ST_DEV_HW ) ) != 0 || \ keyMgmtACL->keysetW_subTypeC != ST_NONE ) { DEBUG_DIAG(( "Key management ACLs inconsistent" )); retIntError(); } if( keyMgmtACL->keysetR_subTypeA != ST_NONE || \ ( keyMgmtACL->keysetD_subTypeB & ( SUBTYPE_CLASS_A | \ SUBTYPE_CLASS_C ) ) || \ ( keyMgmtACL->keysetD_subTypeB & \ ~( SUBTYPE_CLASS_B | ST_KEYSET_ANY | ST_DEV_P11 | \ ST_DEV_CAPI | ST_DEV_HW ) ) != 0 || \ keyMgmtACL->keysetD_subTypeC != ST_NONE ) { DEBUG_DIAG(( "Key management ACLs inconsistent" )); retIntError(); } if( keyMgmtACL->keysetR_subTypeA != ST_NONE || \ ( keyMgmtACL->keysetFN_subTypeB & ( SUBTYPE_CLASS_A | \ SUBTYPE_CLASS_C ) ) || \ ( keyMgmtACL->keysetFN_subTypeB & \ ~( SUBTYPE_CLASS_B | ST_KEYSET_ANY | ST_DEV_P11 | \ ST_DEV_CAPI | ST_DEV_HW ) ) != 0 || \ keyMgmtACL->keysetFN_subTypeC != ST_NONE ) { DEBUG_DIAG(( "Key management ACLs inconsistent" )); retIntError(); } if( keyMgmtACL->keysetR_subTypeA != ST_NONE || \ ( keyMgmtACL->keysetQ_subTypeB & ( SUBTYPE_CLASS_A | \ SUBTYPE_CLASS_C ) ) || \ ( keyMgmtACL->keysetQ_subTypeB & \ ~( SUBTYPE_CLASS_B | ST_KEYSET_ANY ) ) != 0 || \ keyMgmtACL->keysetQ_subTypeC != ST_NONE ) { DEBUG_DIAG(( "Key management ACLs inconsistent" )); retIntError(); } if( ( keyMgmtACL->objSubTypeA & ( SUBTYPE_CLASS_B | \ SUBTYPE_CLASS_C ) ) || \ ( keyMgmtACL->objSubTypeA & \ ~( SUBTYPE_CLASS_A | ST_CERT_ANY | ST_CTX_PKC | \ ST_CTX_CONV ) ) != 0 || \ keyMgmtACL->objSubTypeB != ST_NONE || \ keyMgmtACL->objSubTypeC != ST_NONE ) { DEBUG_DIAG(( "Key management ACLs inconsistent" )); retIntError(); } ENSURES( keyMgmtACL->allowedKeyIDs != NULL ); for( j = 0; keyMgmtACL->allowedKeyIDs[ j ] != CRYPT_KEYID_NONE && \ j < FAILSAFE_ITERATIONS_SMALL; j++ ) { ENSURES( keyMgmtACL->allowedKeyIDs[ j ] > CRYPT_KEYID_NONE && \ keyMgmtACL->allowedKeyIDs[ j ] < CRYPT_KEYID_LAST ); } ENSURES( j < FAILSAFE_ITERATIONS_SMALL ); ENSURES( keyMgmtACL->allowedFlags >= KEYMGMT_FLAG_NONE && \ keyMgmtACL->allowedFlags < KEYMGMT_FLAG_MAX ); if( keyMgmtACL->specificKeysetSubTypeA != ST_NONE || \ ( keyMgmtACL->specificKeysetSubTypeB & ( SUBTYPE_CLASS_A | \ SUBTYPE_CLASS_C ) ) || \ ( keyMgmtACL->specificKeysetSubTypeB & \ ~( SUBTYPE_CLASS_B | ST_KEYSET_ANY | ST_DEV_P11 | \ ST_DEV_CAPI ) ) != 0 || \ keyMgmtACL->specificKeysetSubTypeC != ST_NONE ) { DEBUG_DIAG(( "Key management ACLs inconsistent" )); retIntError(); } if( ( keyMgmtACL->specificObjSubTypeA & ( SUBTYPE_CLASS_B | \ SUBTYPE_CLASS_C ) ) || \ ( keyMgmtACL->specificObjSubTypeA & \ ~( SUBTYPE_CLASS_A | ST_CERT_ANY ) ) != 0 || \ keyMgmtACL->specificObjSubTypeB != ST_NONE || \ keyMgmtACL->specificObjSubTypeC != ST_NONE ) { DEBUG_DIAG(( "Key management ACLs inconsistent" )); retIntError(); } } ENSURES( i < FAILSAFE_ARRAYSIZE( keyManagementACL, KEYMGMT_ACL ) ); /* Perform a consistency check on the supplementary ID ACLs */ for( i = 0; idTypeACL[ i ].idType != CRYPT_KEYID_NONE && \ i < FAILSAFE_ARRAYSIZE( idTypeACL, IDTYPE_ACL ); i++ ) { const IDTYPE_ACL *idACL = &idTypeACL[ i ]; ENSURES( idACL->idType > CRYPT_KEYID_NONE && \ idACL->idType < CRYPT_KEYID_LAST ); if( ( idACL->keysetSubTypeB & \ ~( SUBTYPE_CLASS_B | ST_KEYSET_ANY | ST_DEV_P11 | \ ST_DEV_CAPI | ST_DEV_HW ) ) != 0 ) { DEBUG_DIAG(( "Key management supplementary ACLs inconsistent" )); retIntError(); } } ENSURES( i < FAILSAFE_ARRAYSIZE( idTypeACL, IDTYPE_ACL ) ); /* Set up the reference to the kernel data block */ krnlData = krnlDataPtr; return( CRYPT_OK ); }
static int activateConnection( INOUT SESSION_INFO *sessionInfoPtr ) { CRYPT_ATTRIBUTE_TYPE errorAttribute; int status; assert( isWritePtr( sessionInfoPtr, sizeof( SESSION_INFO ) ) ); /* Make sure that everything is set up ready to go */ errorAttribute = isServer( sessionInfoPtr ) ? \ checkServerParameters( sessionInfoPtr ) : \ checkClientParameters( sessionInfoPtr ); if( errorAttribute != CRYPT_ATTRIBUTE_NONE ) { setErrorInfo( sessionInfoPtr, errorAttribute, CRYPT_ERRTYPE_ATTR_ABSENT ); return( CRYPT_ERROR_NOTINITED ); } ENSURES( isServer( sessionInfoPtr ) || \ findSessionInfo( sessionInfoPtr->attributeList, CRYPT_SESSINFO_SERVER_NAME ) != NULL || \ sessionInfoPtr->networkSocket != CRYPT_ERROR ); ENSURES( findSessionInfo( sessionInfoPtr->attributeList, CRYPT_SESSINFO_SERVER_PORT ) != NULL || \ sessionInfoPtr->protocolInfo->port > 0 ); /* Allocate the send and receive buffers if necessary. The send buffer isn't used for request-response session types that use the receive buffer for both outgoing and incoming data so we only allocate it if it's actually required */ if( sessionInfoPtr->sendBuffer == NULL ) { REQUIRES( sessionInfoPtr->receiveBufSize >= MIN_BUFFER_SIZE && \ sessionInfoPtr->receiveBufSize < MAX_BUFFER_SIZE ); REQUIRES( ( sessionInfoPtr->sendBufSize >= MIN_BUFFER_SIZE && \ sessionInfoPtr->sendBufSize < MAX_BUFFER_SIZE ) || \ sessionInfoPtr->sendBufSize == CRYPT_UNUSED ); if( ( sessionInfoPtr->receiveBuffer = \ clAlloc( "activateConnection", \ sessionInfoPtr->receiveBufSize + 8 ) ) == NULL ) return( CRYPT_ERROR_MEMORY ); if( sessionInfoPtr->sendBufSize != CRYPT_UNUSED ) { /* When allocating the send buffer we use the size given for the receive buffer since the user may have overridden the default buffer size */ if( ( sessionInfoPtr->sendBuffer = \ clAlloc( "activateConnection", \ sessionInfoPtr->receiveBufSize + 8 ) ) == NULL ) { clFree( "activateConnection", sessionInfoPtr->receiveBuffer ); sessionInfoPtr->receiveBuffer = NULL; return( CRYPT_ERROR_MEMORY ); } sessionInfoPtr->sendBufSize = sessionInfoPtr->receiveBufSize; } } ENSURES( sessionInfoPtr->receiveBuffer != NULL && \ sessionInfoPtr->receiveBufSize >= MIN_BUFFER_SIZE && \ sessionInfoPtr->receiveBufSize < MAX_BUFFER_SIZE ); ENSURES( sessionInfoPtr->sendBufSize == CRYPT_UNUSED || \ sessionInfoPtr->sendBuffer != NULL ); /* Set timeouts if they're not set yet. If there's an error then we use the default value rather than aborting the entire session because of a minor difference in timeout values, although we also warn the caller in debug mode */ if( sessionInfoPtr->connectTimeout == CRYPT_ERROR ) { int timeout; status = krnlSendMessage( sessionInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE, &timeout, CRYPT_OPTION_NET_CONNECTTIMEOUT ); if( cryptStatusOK( status ) ) sessionInfoPtr->connectTimeout = timeout; else { DEBUG_DIAG(( "Couldn't get connect timeout config value" )); assert( DEBUG_WARN ); sessionInfoPtr->connectTimeout = 30; } } if( sessionInfoPtr->readTimeout == CRYPT_ERROR ) { int timeout; status = krnlSendMessage( sessionInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE, &timeout, CRYPT_OPTION_NET_READTIMEOUT ); if( cryptStatusOK( status ) ) sessionInfoPtr->readTimeout = timeout; else { DEBUG_DIAG(( "Couldn't get read timeout config value" )); assert( DEBUG_WARN ); sessionInfoPtr->readTimeout = 30; } } if( sessionInfoPtr->writeTimeout == CRYPT_ERROR ) { int timeout; status = krnlSendMessage( sessionInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE, &timeout, CRYPT_OPTION_NET_WRITETIMEOUT ); if( cryptStatusOK( status ) ) sessionInfoPtr->writeTimeout = timeout; else { DEBUG_DIAG(( "Couldn't get write timeout config value" )); assert( DEBUG_WARN ); sessionInfoPtr->writeTimeout = 30; } } /* Wait for any async driver binding to complete. We can delay this until this very late stage because no networking functionality is used until this point */ if( !krnlWaitSemaphore( SEMAPHORE_DRIVERBIND ) ) { /* The kernel is shutting down, bail out */ return( CRYPT_ERROR_PERMISSION ); } /* If this is the first time that we've got here, activate the session */ if( !( sessionInfoPtr->flags & SESSION_PARTIALOPEN ) ) { REQUIRES( !( sessionInfoPtr->flags & SESSION_ISOPEN ) ) status = sessionInfoPtr->connectFunction( sessionInfoPtr ); if( cryptStatusError( status ) ) return( status ); } /* If it's a secure data transport session, complete the session state setup. Note that some sessions dynamically change the protocol information during the handshake to accommodate parameters negotiated during the handshake so we can only access the protocol information after the handshake has completed */ if( !sessionInfoPtr->protocolInfo->isReqResp ) { /* Complete the session handshake to set up the secure state */ status = sessionInfoPtr->transactFunction( sessionInfoPtr ); if( cryptStatusError( status ) ) { /* If we need feedback from the user before we can complete the handshake (for example checking a user name and password or certificate supplied by the other side) we remain in the handshake state so that the user can re-activate the session after confirming (or denying) the check */ if( status == CRYPT_ENVELOPE_RESOURCE ) sessionInfoPtr->flags |= SESSION_PARTIALOPEN; return( status ); } /* Notify the kernel that the session key context is attached to the session object. Note that we increment its reference count even though it's an internal object used only by the session because otherwise it'll be automatically destroyed by the kernel as a zero-reference dependent object when the session object is destroyed (but before the session object itself since the context is just a dependent object). This automatic cleanup could cause problems for lower-level session management code that tries to work with the (apparently still-valid) handle, for example protocols that need to encrypt a close-channel message on session shutdown */ krnlSendMessage( sessionInfoPtr->objectHandle, IMESSAGE_SETDEPENDENT, &sessionInfoPtr->iCryptInContext, SETDEP_OPTION_INCREF ); /* Set up the buffer management variables */ sessionInfoPtr->receiveBufPos = sessionInfoPtr->receiveBufEnd = 0; sessionInfoPtr->sendBufPos = sessionInfoPtr->sendBufStartOfs; /* For data transport sessions, partial reads and writes (that is, sending and receiving partial packets in the presence of timeouts) are permitted */ sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_PARTIALREAD, TRUE ); sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_PARTIALWRITE, TRUE ); } /* The handshake has been completed, switch from the handshake timeout to the data transfer timeout and remember that the session has been successfully established */ sioctlSet( &sessionInfoPtr->stream, STREAM_IOCTL_HANDSHAKECOMPLETE, TRUE ); sessionInfoPtr->flags &= ~SESSION_PARTIALOPEN; sessionInfoPtr->flags |= SESSION_ISOPEN; return( CRYPT_OK ); }
static int getObjectInfo( INOUT STREAM *stream, OUT QUERY_INFO *queryInfo ) { const long startPos = stell( stream ); long value; int tag, length, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( queryInfo, sizeof( QUERY_INFO ) ) ); /* Clear return value */ memset( queryInfo, 0, sizeof( QUERY_INFO ) ); /* We always need at least MIN_CRYPT_OBJECTSIZE more bytes to do anything */ if( sMemDataLeft( stream ) < MIN_CRYPT_OBJECTSIZE ) return( CRYPT_ERROR_UNDERFLOW ); /* Get the type, length, and version information */ status = getStreamObjectLength( stream, &length ); if( cryptStatusError( status ) ) return( status ); queryInfo->formatType = CRYPT_FORMAT_CRYPTLIB; queryInfo->size = length; tag = peekTag( stream ); if( cryptStatusError( tag ) ) return( tag ); readGenericHole( stream, NULL, 16, tag ); status = readShortInteger( stream, &value ); if( cryptStatusError( status ) ) return( status ); queryInfo->version = value; switch( tag ) { case BER_SEQUENCE: /* This could be a signature or a PKC-encrypted key, see what follows */ switch( value ) { case KEYTRANS_VERSION: case KEYTRANS_EX_VERSION: queryInfo->type = CRYPT_OBJECT_PKCENCRYPTED_KEY; break; case SIGNATURE_VERSION: case SIGNATURE_EX_VERSION: queryInfo->type = CRYPT_OBJECT_SIGNATURE; break; default: return( CRYPT_ERROR_BADDATA ); } if( value == KEYTRANS_VERSION || value == SIGNATURE_VERSION ) queryInfo->formatType = CRYPT_FORMAT_CMS; break; case MAKE_CTAG( CTAG_RI_KEYAGREE ): /* It's CMS' wierd X9.42-inspired key agreement mechanism, we can't do much with this (mind you neither can anyone else) so we should probably really treat it as a CRYPT_ERROR_BADDATA if we encounter it rather than just ignoring it */ queryInfo->type = CRYPT_OBJECT_NONE; DEBUG_DIAG(( "Found keyAgreeRecipientInfo" )); assert( DEBUG_WARN ); break; case MAKE_CTAG( CTAG_RI_PWRI ): queryInfo->type = CRYPT_OBJECT_ENCRYPTED_KEY; break; default: queryInfo->type = CRYPT_OBJECT_NONE; if( tag > MAKE_CTAG( CTAG_RI_PWRI ) && \ tag <= MAKE_CTAG( CTAG_RI_MAX ) ) { /* This is probably a new RecipientInfo type, skip it */ DEBUG_DIAG(( "Found unknown RecipientInfo %X", tag )); assert( DEBUG_WARN ); break; } return( CRYPT_ERROR_BADDATA ); } /* Reset the stream and make sure that all of the data is present */ sseek( stream, startPos ); return( sMemDataLeft( stream ) < queryInfo->size ? \ CRYPT_ERROR_UNDERFLOW : CRYPT_OK ); }
static int readKeyIdentifiers( INOUT STREAM *stream, INOUT PKCS15_INFO *pkcs15infoPtr, IN_LENGTH const int endPos ) { int iterationCount, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( pkcs15infoPtr, sizeof( PKCS15_INFO ) ) ); REQUIRES( endPos > 0 && endPos > stell( stream ) && \ endPos < MAX_INTLENGTH ); for( status = CRYPT_OK, iterationCount = 0; cryptStatusOK( status ) && stell( stream ) < endPos && \ iterationCount < FAILSAFE_ITERATIONS_MED; iterationCount++ ) { long value; int payloadLength; /* Read each identifier type and copy the useful ones into the PKCS #15 information */ readSequence( stream, &payloadLength ); status = readShortInteger( stream, &value ); if( cryptStatusError( status ) ) return( status ); switch( value ) { case PKCS15_KEYID_ISSUERANDSERIALNUMBER: { HASHFUNCTION_ATOMIC hashFunctionAtomic; void *iAndSPtr = DUMMY_INIT_PTR; int iAndSLength, hashSize; /* If we've already got the iAndSID, use that version instead */ if( pkcs15infoPtr->iAndSIDlength > 0 ) { status = readUniversal( stream ); continue; } /* Hash the full issuerAndSerialNumber to get an iAndSID */ getHashAtomicParameters( CRYPT_ALGO_SHA1, 0, &hashFunctionAtomic, &hashSize ); status = getStreamObjectLength( stream, &iAndSLength ); if( cryptStatusOK( status ) ) status = sMemGetDataBlock( stream, &iAndSPtr, iAndSLength ); if( cryptStatusOK( status ) ) status = sSkip( stream, iAndSLength ); if( cryptStatusError( status ) ) return( status ); hashFunctionAtomic( pkcs15infoPtr->iAndSID, KEYID_SIZE, iAndSPtr, iAndSLength ); pkcs15infoPtr->iAndSIDlength = hashSize; break; } case PKCS15_KEYID_SUBJECTKEYIDENTIFIER: status = readOctetString( stream, pkcs15infoPtr->keyID, &pkcs15infoPtr->keyIDlength, 8, CRYPT_MAX_HASHSIZE ); break; case PKCS15_KEYID_ISSUERANDSERIALNUMBERHASH: /* If we've already got the iAndSID by hashing the issuerAndSerialNumber, use that version instead */ if( pkcs15infoPtr->iAndSIDlength > 0 ) { status = readUniversal( stream ); continue; } status = readOctetString( stream, pkcs15infoPtr->iAndSID, &pkcs15infoPtr->iAndSIDlength, KEYID_SIZE, KEYID_SIZE ); break; case PKCS15_KEYID_ISSUERNAMEHASH: status = readOctetString( stream, pkcs15infoPtr->issuerNameID, &pkcs15infoPtr->issuerNameIDlength, KEYID_SIZE, KEYID_SIZE ); break; case PKCS15_KEYID_SUBJECTNAMEHASH: status = readOctetString( stream, pkcs15infoPtr->subjectNameID, &pkcs15infoPtr->subjectNameIDlength, KEYID_SIZE, KEYID_SIZE ); break; case PKCS15_KEYID_PGP2: status = readOctetString( stream, pkcs15infoPtr->pgp2KeyID, &pkcs15infoPtr->pgp2KeyIDlength, PGP_KEYID_SIZE, PGP_KEYID_SIZE ); break; case PKCS15_KEYID_OPENPGP: status = readOctetString( stream, pkcs15infoPtr->openPGPKeyID, &pkcs15infoPtr->openPGPKeyIDlength, PGP_KEYID_SIZE, PGP_KEYID_SIZE ); break; default: status = readUniversal( stream ); } } if( iterationCount >= FAILSAFE_ITERATIONS_MED ) { /* This could be either an internal error or some seriously malformed data, since we can't tell without human intervention we throw a debug exception but otherwise treat it as bad data */ DEBUG_DIAG(( "Encountered more than %d key IDs", FAILSAFE_ITERATIONS_MED )); assert( DEBUG_WARN ); return( CRYPT_ERROR_BADDATA ); } return( status ); }
CHECK_RETVAL \ int loadDHcontext( IN_HANDLE const CRYPT_CONTEXT iDHContext, IN_LENGTH_SHORT_OPT const int requestedKeySize ) { MESSAGE_DATA msgData; const void *keyData; const int actualKeySize = \ ( requestedKeySize < 128 + 8 ) ? bitsToBytes( 1024 ) : \ ( requestedKeySize < 192 + 8 ) ? bitsToBytes( 1536 ) : \ ( requestedKeySize < 256 + 8 ) ? bitsToBytes( 2048 ) : \ ( requestedKeySize < 384 + 8 ) ? bitsToBytes( 3072 ) : \ 0; int keyDataLength, keyDataChecksum; REQUIRES( isHandleRangeValid( iDHContext ) ); REQUIRES( requestedKeySize >= MIN_PKCSIZE && \ requestedKeySize <= CRYPT_MAX_PKCSIZE ); /* Load the built-in DH key value that corresponds best to the client's requested key size. We allow for a bit of slop to avoid having something like a 1025-bit requested key size lead to the use of a 1536-bit key value. In theory we should probably generate a new DH key each time: status = krnlSendMessage( iDHContext, IMESSAGE_SETATTRIBUTE, ( MESSAGE_CAST ) &requestedKeySize, CRYPT_CTXINFO_KEYSIZE ); if( cryptStatusOK( status ) ) status = krnlSendMessage( iDHContext, IMESSAGE_CTX_GENKEY, NULL, FALSE ); however because the handshake is set up so that the client (rather than the server) chooses the key size we can't actually perform the generation until we're in the middle of the handshake. This means that the server will grind to a halt during each handshake as it generates a new key of whatever size takes the client's fancy (it also leads to a nice potential DoS attack on the server). To avoid this problem we use fixed keys of various common sizes. As late as 2014 Java still can't handle DH keys over 1024 bits (it only allows keys ranging from 512-1024 bits): java.security.InvalidAlgorithmParameterException: Prime size must be multiple of 64, and can only range from 512 to 1024 (inclusive) so if you need to talk to a system built in Java you need to hardcode the key size below to 1024 bits, the largest size that Java will allow */ switch( actualKeySize ) { case bitsToBytes( 1024 ): keyData = dh1024SSL; keyDataLength = sizeof( dh1024SSL ); keyDataChecksum = dh1024checksum; break; case bitsToBytes( 1536 ): keyData = dh1536SSL; keyDataLength = sizeof( dh1536SSL ); keyDataChecksum = dh1536checksum; break; case bitsToBytes( 2048 ): keyData = dh2048SSL; keyDataLength = sizeof( dh2048SSL ); keyDataChecksum = dh2048checksum; break; case bitsToBytes( 3072 ): default: /* Hier ist der mast zu ende */ keyData = dh3072SSL; keyDataLength = sizeof( dh3072SSL ); keyDataChecksum = dh3072checksum; break; } /* Make sure that the key data hasn't been corrupted */ if( keyDataChecksum != checksumData( keyData, keyDataLength ) ) { DEBUG_DIAG(( "Fixed DH value for %d-bit key has been corrupted", bytesToBits( actualKeySize ) )); retIntError(); } /* Load the fixed DH key into the context */ setMessageData( &msgData, ( MESSAGE_CAST ) keyData, keyDataLength ); return( krnlSendMessage( iDHContext, IMESSAGE_SETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_KEY_SSL ) ); }
static int readKey( INOUT STREAM *stream, INOUT PGP_INFO *pgpInfo, IN_INT_SHORT_Z const int keyGroupNo, INOUT ERROR_INFO *errorInfo ) { PGP_KEYINFO *keyInfo = &pgpInfo->key; HASHFUNCTION hashFunction; HASHINFO hashInfo; BYTE hash[ CRYPT_MAX_HASHSIZE + 8 ], packetHeader[ 64 + 8 ]; BOOLEAN isPublicKey = TRUE, isPrimaryKey = FALSE; void *pubKeyPayload; long packetLength; int pubKeyPos, pubKeyPayloadPos, endPos, pubKeyPayloadLen; int ctb, length, value, hashSize, iterationCount, status; assert( isWritePtr( stream, sizeof( STREAM ) ) ); assert( isWritePtr( pgpInfo, sizeof( PGP_INFO ) ) ); REQUIRES( keyGroupNo >= 0 && keyGroupNo < MAX_INTLENGTH_SHORT ); REQUIRES( errorInfo != NULL ); /* Process the CTB and packet length */ ctb = sPeek( stream ); if( cryptStatusError( ctb ) ) { /* If there was an error reading the CTB, which is the first byte of the packet group, it means that we've run out of data so we return the status as a not-found error rather than the actual stream status */ return( CRYPT_ERROR_NOTFOUND ); } switch( pgpGetPacketType( ctb ) ) { case PGP_PACKET_SECKEY_SUB: keyInfo = &pgpInfo->subKey; isPublicKey = FALSE; break; case PGP_PACKET_SECKEY: isPublicKey = FALSE; break; case PGP_PACKET_PUBKEY_SUB: keyInfo = &pgpInfo->subKey; break; case PGP_PACKET_PUBKEY: isPrimaryKey = TRUE; break; default: retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, errorInfo, "Invalid PGP CTB %02X for key packet group %d", ctb, keyGroupNo ) ); } status = pgpReadPacketHeader( stream, NULL, &packetLength, 64 ); if( cryptStatusError( status ) ) { retExt( status, ( status, errorInfo, "Invalid PGP key packet header for key packet group %d", keyGroupNo ) ); } if( packetLength < 64 || packetLength > sMemDataLeft( stream ) ) { retExt( CRYPT_ERROR_BADDATA, ( CRYPT_ERROR_BADDATA, errorInfo, "Invalid PGP key packet length %ld for key packet group %d", packetLength, keyGroupNo ) ); } /* Since there can (in theory) be arbitrary numbers of subkeys and other odds and ends attached to a key and the details of what to do with these things gets a bit vague, we just skip any further subkeys that may be present */ if( keyInfo->pkcAlgo != CRYPT_ALGO_NONE ) { status = sSkip( stream, packetLength ); for( iterationCount = 0; cryptStatusOK( status ) && \ iterationCount < FAILSAFE_ITERATIONS_MED; iterationCount++ ) { status = readUserID( stream, pgpInfo, isPrimaryKey ? &hashInfo : NULL ); } ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED ); if( cryptStatusError( status ) && status != OK_SPECIAL ) { retExt( status, ( status, errorInfo, "Invalid additional PGP subkey information for key " "packet group %d", keyGroupNo ) ); } /* We've skipped the current subkey, we're done */ return( CRYPT_OK ); } /* Determine which bits make up the public and the private key data. The public-key data starts at the version number and includes the date, validity, and public-key components. Since there's no length information included for this data block we have to record bookmarks and then later retroactively calculate the length based on how much data we've read in the meantime: pubKey pubKeyPayload privKey endPos | | | | v v v v +---+---------------------------+-------------------+ |hdr| | Public key | Private key | +---+---------------------------+-------------------+ | |<pubKeyPayloadLen->| | |<----- pubKeyDataLen ----->|<-- privKeyDLen -->| |<--------------- packetLength ---------------->| */ pubKeyPos = stell( stream ); endPos = pubKeyPos + packetLength; ENSURES( endPos > pubKeyPos && endPos < MAX_BUFFER_SIZE ); status = value = sgetc( stream ); if( cryptStatusError( status ) ) return( status ); if( value != PGP_VERSION_2 && value != PGP_VERSION_3 && \ value != PGP_VERSION_OPENPGP ) { /* Unknown version number, skip this packet */ return( OK_SPECIAL ); } pgpInfo->isOpenPGP = ( value == PGP_VERSION_OPENPGP ) ? TRUE : FALSE; /* Build the packet header, which is hashed along with the key components to get the OpenPGP keyID. This is generated anyway when the context is created but we need to generate it here as well in order to locate the key in the first place: byte ctb = 0x99 byte[2] length byte version = 4 byte[4] key generation time [ byte[2] validity time - PGP 2.x only ] byte[] key data We can't add the length or key data yet since we have to parse the key data to know how long it is, so we can only build the static part of the header at this point */ packetHeader[ 0 ] = 0x99; packetHeader[ 3 ] = PGP_VERSION_OPENPGP; /* Read the timestamp and validity period (for PGP 2.x keys) */ status = sread( stream, packetHeader + 4, 4 ); if( !cryptStatusError( status ) && !pgpInfo->isOpenPGP ) status = sSkip( stream, 2 ); if( cryptStatusError( status ) ) return( status ); /* Read the public key components */ pubKeyPayloadPos = stell( stream ); status = readPublicKeyComponents( stream, keyInfo, &length ); if( cryptStatusError( status ) ) { /* If the error status is OK_SPECIAL then the problem was an unrecognised algorithm or something similar so we just skip the packet */ if( status == OK_SPECIAL ) { DEBUG_DIAG(( "Encountered unrecognised algorithm while " "reading key" )); assert( DEBUG_WARN ); return( OK_SPECIAL ); } retExt( status, ( status, errorInfo, "Invalid PGP public-key components for key packet group %d", keyGroupNo ) ); } /* Now that we know where the public key data starts and finishes, we can set up references to it */ keyInfo->pubKeyDataLen = stell( stream ) - pubKeyPos; status = sMemGetDataBlockAbs( stream, pubKeyPos, &keyInfo->pubKeyData, keyInfo->pubKeyDataLen ); if( cryptStatusError( status ) ) { DEBUG_DIAG(( "Couldn't set up reference to key data" )); assert( DEBUG_WARN ); return( status ); } pubKeyPayloadLen = stell( stream ) - pubKeyPayloadPos; status = sMemGetDataBlockAbs( stream, pubKeyPayloadPos, &pubKeyPayload, pubKeyPayloadLen ); if( cryptStatusError( status ) ) { DEBUG_DIAG(( "Couldn't set up reference to key data" )); assert( DEBUG_WARN ); return( status ); } /* Complete the packet header that we read earlier on by adding the length information */ packetHeader[ 1 ] = intToByte( ( ( 1 + 4 + length ) >> 8 ) & 0xFF ); packetHeader[ 2 ] = intToByte( ( 1 + 4 + length ) & 0xFF ); /* Hash the data needed to generate the OpenPGP keyID */ getHashParameters( CRYPT_ALGO_SHA1, 0, &hashFunction, &hashSize ); hashFunction( hashInfo, NULL, 0, packetHeader, 1 + 2 + 1 + 4, HASH_STATE_START ); hashFunction( hashInfo, hash, CRYPT_MAX_HASHSIZE, pubKeyPayload, pubKeyPayloadLen, HASH_STATE_END ); memcpy( keyInfo->openPGPkeyID, hash + hashSize - PGP_KEYID_SIZE, PGP_KEYID_SIZE ); /* If it's a private keyring, process the private key components */ if( !isPublicKey ) { /* Handle decryption information for private-key components if necessary */ status = readPrivateKeyDecryptionInfo( stream, keyInfo ); if( cryptStatusError( status ) ) { /* If the error status is OK_SPECIAL then the problem was an unrecognised algorithm or something similar so we just skip the packet */ if( status == OK_SPECIAL ) { DEBUG_DIAG(( "Encountered unrecognised algorithm while " "reading key" )); assert( DEBUG_WARN ); return( OK_SPECIAL ); } retExt( status, ( status, errorInfo, "Invalid PGP private-key decryption information for " "key packet group %d", keyGroupNo ) ); } /* What's left is the private-key data */ keyInfo->privKeyDataLen = endPos - stell( stream ); status = sMemGetDataBlock( stream, &keyInfo->privKeyData, keyInfo->privKeyDataLen ); if( cryptStatusOK( status ) ) status = sSkip( stream, keyInfo->privKeyDataLen ); if( cryptStatusError( status ) ) return( status ); } /* If it's the primary key, start hashing it in preparation for performing signature checks on subpackets */ if( isPrimaryKey ) { packetHeader[ 0 ] = 0x99; packetHeader[ 1 ] = intToByte( ( keyInfo->pubKeyDataLen >> 8 ) & 0xFF ); packetHeader[ 2 ] = intToByte( keyInfo->pubKeyDataLen & 0xFF ); hashFunction( hashInfo, NULL, 0, packetHeader, 1 + 2, HASH_STATE_START ); hashFunction( hashInfo, NULL, 0, keyInfo->pubKeyData, keyInfo->pubKeyDataLen, HASH_STATE_CONTINUE ); } /* Read any associated subpacket(s), of which the only ones of real interest are the userID packet(s) */ for( iterationCount = 0; cryptStatusOK( status ) && \ iterationCount < FAILSAFE_ITERATIONS_MED; iterationCount++ ) { status = readUserID( stream, pgpInfo, isPrimaryKey ? &hashInfo : NULL ); } ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED ); if( cryptStatusError( status ) && status != OK_SPECIAL ) { retExt( status, ( status, errorInfo, "Invalid PGP userID information for key packet group %d", keyGroupNo ) ); } /* If there's no user ID present, set a generic label */ if( pgpInfo->lastUserID <= 0 ) { pgpInfo->userID[ 0 ] = "PGP key (no user ID found)"; pgpInfo->userIDlen[ 0 ] = 26; pgpInfo->lastUserID = 1; } return( CRYPT_OK ); }