/**************************************************************************** * * * cryptlib Encryption Context Routines * * Copyright Peter Gutmann 1992-2006 * * * ****************************************************************************/ /* "Modern cryptography is nothing more than a mathematical framework for debating the implications of various paranoid delusions" - Don Alvarez */ #define PKC_CONTEXT /* Indicate that we're working with PKC context */ #include "crypt.h" #ifdef INC_ALL #include "context.h" #include "asn1.h" #else #include "context/context.h" #include "misc/asn1.h" #endif /* Compiler-specific includes */ /* The default size of the salt for PKCS #5v2 key derivation, needed when we set the CRYPT_CTXINFO_KEYING_VALUE */ #define PKCS5_SALT_SIZE 8 /* 64 bits */ /* The number of bytes of data that we check to make sure the encryption operation succeeded (see the comment in encryptData() before changing this) */ #define ENCRYPT_CHECKSIZE 16 /* Prototypes for functions in ctx_misc.c */ const CAPABILITY_INFO FAR_BSS *findCapabilityInfo( const CAPABILITY_INFO_LIST *capabilityInfoList, const CRYPT_ALGO_TYPE cryptAlgo ); void clearTempBignums( PKC_INFO *pkcInfo ); void initContextBignums( PKC_INFO *pkcInfo, const BOOLEAN useSideChannelProtection ); void freeContextBignums( PKC_INFO *pkcInfo, int contextFlags ); /* Prototypes for functions in keyload.c */ void initKeyHandling( CONTEXT_INFO *contextInfoPtr ); /**************************************************************************** * * * Utility Functions * * * ****************************************************************************/ /* Exit after setting extended error information */ static int exitError( CONTEXT_INFO *contextInfoPtr, const CRYPT_ATTRIBUTE_TYPE errorLocus, const CRYPT_ERRTYPE_TYPE errorType, const int status ) { setErrorInfo( contextInfoPtr, errorLocus, errorType ); return( status ); } static int exitErrorInited( CONTEXT_INFO *contextInfoPtr, const CRYPT_ATTRIBUTE_TYPE errorLocus ) { return( exitError( contextInfoPtr, errorLocus, CRYPT_ERRTYPE_ATTR_PRESENT, CRYPT_ERROR_INITED ) ); } static int exitErrorNotInited( CONTEXT_INFO *contextInfoPtr, const CRYPT_ATTRIBUTE_TYPE errorLocus ) { return( exitError( contextInfoPtr, errorLocus, CRYPT_ERRTYPE_ATTR_ABSENT, CRYPT_ERROR_NOTINITED ) ); } static int exitErrorNotFound( CONTEXT_INFO *contextInfoPtr, const CRYPT_ATTRIBUTE_TYPE errorLocus ) { return( exitError( contextInfoPtr, errorLocus, CRYPT_ERRTYPE_ATTR_ABSENT, CRYPT_ERROR_NOTFOUND ) ); } /* Convert a key attribute type into a key format type */ static int attributeToFormatType( const CRYPT_ATTRIBUTE_TYPE attribute ) { switch( attribute ) { case CRYPT_IATTRIBUTE_KEY_SSH: return( KEYFORMAT_SSH ); case CRYPT_IATTRIBUTE_KEY_SSH1: return( KEYFORMAT_SSH1 ); case CRYPT_IATTRIBUTE_KEY_SSL: return( KEYFORMAT_SSL ); case CRYPT_IATTRIBUTE_KEY_PGP: case CRYPT_IATTRIBUTE_KEY_PGP_PARTIAL: return( KEYFORMAT_PGP ); case CRYPT_IATTRIBUTE_KEY_SPKI: case CRYPT_IATTRIBUTE_KEY_SPKI_PARTIAL: return( KEYFORMAT_CERT ); } assert( NOTREACHED ); return( CRYPT_ERROR ); /* Get rid of compiler warning */ } /**************************************************************************** * * * Misc. Context Functions * * * ****************************************************************************/ /* Initialise pointers to context-specific storage areas */ static void initContextStorage( CONTEXT_INFO *contextInfoPtr, const int storageSize ) { switch( contextInfoPtr->type ) { case CONTEXT_CONV: contextInfoPtr->ctxConv = ( CONV_INFO * ) contextInfoPtr->storage; contextInfoPtr->ctxConv->key = contextInfoPtr->storage + storageSize; break; case CONTEXT_HASH: contextInfoPtr->ctxHash = ( HASH_INFO * ) contextInfoPtr->storage; contextInfoPtr->ctxHash->hashInfo = contextInfoPtr->storage + storageSize; break; case CONTEXT_MAC: contextInfoPtr->ctxMAC = ( MAC_INFO * ) contextInfoPtr->storage; contextInfoPtr->ctxMAC->macInfo = contextInfoPtr->storage + storageSize; break; case CONTEXT_PKC: contextInfoPtr->ctxPKC = ( PKC_INFO * ) contextInfoPtr->storage; break; } } /* Perform any context-specific checks that a context meets the given requirements (general checks have already been performed by the kernel). Although these checks are automatically performed by the kernel when we try and use the context, they're duplicated here to allow for better error reporting by catching problems when the context is first passed to a cryptlib function rather than much later and at a lower level when the kernel disallows the action */ static int checkContext( CONTEXT_INFO *contextInfoPtr, const MESSAGE_CHECK_TYPE checkType ) { const CAPABILITY_INFO *capabilityInfoPtr = contextInfoPtr->capabilityInfo; /* If it's a check that an object's ready for key generation (which is algorithm-type independent), we check it before performing any algorithm-specific checks */ if( checkType == MESSAGE_CHECK_KEYGEN_READY ) { if( !needsKey( contextInfoPtr ) ) return( exitErrorInited( contextInfoPtr, CRYPT_CTXINFO_KEY ) ); return( CRYPT_OK ); } /* If it's a check for the (potential) ability to perform conventional encryption or MAC'ing at some point in the future, without currently having a key loaded for the task, we're done */ if( checkType == MESSAGE_CHECK_CRYPT_READY || \ checkType == MESSAGE_CHECK_MAC_READY ) return( CRYPT_OK ); /* Perform general checks */ if( contextInfoPtr->type != CONTEXT_HASH && needsKey( contextInfoPtr ) ) return( exitErrorNotInited( contextInfoPtr, CRYPT_CTXINFO_KEY ) ); /* If it's a hash, MAC, conventional encryption, or basic PKC check, we're done */ if( checkType == MESSAGE_CHECK_CRYPT || \ checkType == MESSAGE_CHECK_HASH || \ checkType == MESSAGE_CHECK_MAC || \ checkType == MESSAGE_CHECK_PKC ) return( CRYPT_OK ); /* Check for key-agreement algorithms */ if( isKeyxAlgo( capabilityInfoPtr->cryptAlgo ) ) /* DH can never be used for encryption or signatures (if it is then we call it Elgamal) and KEA is explicitly for key agreement only. Note that the status of DH is a bit ambiguous in that every DH key is both a public and private key, in order to avoid confusion in situations where we're checking for real private keys we always denote a DH context as key-agreement only without taking a side about whether it's a public or private key */ return( ( checkType == MESSAGE_CHECK_PKC_KA_EXPORT || \ checkType == MESSAGE_CHECK_PKC_KA_IMPORT ) ? \ CRYPT_OK : CRYPT_ARGERROR_OBJECT ); if( checkType == MESSAGE_CHECK_PKC_KA_EXPORT || \ checkType == MESSAGE_CHECK_PKC_KA_IMPORT ) return( CRYPT_ARGERROR_OBJECT ); /* Must be a key agreement algorithm */ /* We're down to various public-key checks */ assert( checkType == MESSAGE_CHECK_PKC_PRIVATE || \ checkType == MESSAGE_CHECK_PKC_ENCRYPT || \ checkType == MESSAGE_CHECK_PKC_DECRYPT || \ checkType == MESSAGE_CHECK_PKC_SIGCHECK || \ checkType == MESSAGE_CHECK_PKC_SIGN || \ checkType == MESSAGE_CHECK_CA ); /* Check that it's a private key if this is required */ if( ( checkType == MESSAGE_CHECK_PKC_PRIVATE || \ checkType == MESSAGE_CHECK_PKC_DECRYPT || \ checkType == MESSAGE_CHECK_PKC_SIGN ) && \ ( contextInfoPtr->flags & CONTEXT_ISPUBLICKEY ) ) return( CRYPT_ARGERROR_OBJECT ); return( CRYPT_OK ); } /* Derive a key into a context from a user-supplied keying value */ static int deriveKey( CONTEXT_INFO *contextInfoPtr, void *keyValue, const int keyValueLen ) { MECHANISM_DERIVE_INFO mechanismInfo; const CAPABILITY_INFO *capabilityInfoPtr = contextInfoPtr->capabilityInfo; int status = CRYPT_OK; assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) ); assert( contextInfoPtr->type == CONTEXT_CONV || \ contextInfoPtr->type == CONTEXT_MAC ); assert( needsKey( contextInfoPtr ) ); assert( isReadPtr( keyValue, keyValueLen ) ); /* If it's a persistent context, we need to have a key label set before we can continue */ if( ( contextInfoPtr->flags & CONTEXT_PERSISTENT ) && \ contextInfoPtr->labelSize <= 0 ) return( exitErrorNotInited( contextInfoPtr, CRYPT_CTXINFO_LABEL ) ); /* Set up various derivation parameters if they're not already set. Since there's only one MUST MAC algorithm for PKCS #5v2, we always force the key derivation algorithm to this value to avoid interop problems */ if( contextInfoPtr->type == CONTEXT_CONV ) { CONV_INFO *convInfo = contextInfoPtr->ctxConv; if( convInfo->saltLength <= 0 ) { MESSAGE_DATA nonceMsgData; setMessageData( &nonceMsgData, convInfo->salt, PKCS5_SALT_SIZE ); status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S, &nonceMsgData, CRYPT_IATTRIBUTE_RANDOM_NONCE ); if( cryptStatusError( status ) ) return( status ); convInfo->saltLength = PKCS5_SALT_SIZE; } convInfo->keySetupAlgorithm = CRYPT_ALGO_HMAC_SHA; setMechanismDeriveInfo( &mechanismInfo, convInfo->userKey, capabilityInfoPtr->getInfoFunction( CAPABILITY_INFO_KEYSIZE, contextInfoPtr, convInfo->userKeyLength ), keyValue, keyValueLen, convInfo->keySetupAlgorithm, convInfo->salt, convInfo->saltLength, convInfo->keySetupIterations ); if( mechanismInfo.iterations <= 0 ) { status = krnlSendMessage( contextInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE, &mechanismInfo.iterations, CRYPT_OPTION_KEYING_ITERATIONS ); if( cryptStatusError( status ) ) return( status ); convInfo->keySetupIterations = mechanismInfo.iterations; } } else { MAC_INFO *macInfo = contextInfoPtr->ctxMAC; if( macInfo->saltLength <= 0 ) { MESSAGE_DATA nonceMsgData; setMessageData( &nonceMsgData, macInfo->salt, PKCS5_SALT_SIZE ); status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S, &nonceMsgData, CRYPT_IATTRIBUTE_RANDOM_NONCE ); if( cryptStatusError( status ) ) return( status ); macInfo->saltLength = PKCS5_SALT_SIZE; } contextInfoPtr->ctxConv->keySetupAlgorithm = CRYPT_ALGO_HMAC_SHA; setMechanismDeriveInfo( &mechanismInfo, macInfo->userKey, capabilityInfoPtr->getInfoFunction( CAPABILITY_INFO_KEYSIZE, contextInfoPtr, macInfo->userKeyLength ), keyValue, keyValueLen, macInfo->keySetupAlgorithm, macInfo->salt, macInfo->saltLength, macInfo->keySetupIterations ); if( mechanismInfo.iterations <= 0 ) { status = krnlSendMessage( contextInfoPtr->ownerHandle, IMESSAGE_GETATTRIBUTE, &mechanismInfo.iterations, CRYPT_OPTION_KEYING_ITERATIONS ); if( cryptStatusError( status ) ) return( status ); macInfo->keySetupIterations = mechanismInfo.iterations; } } /* Turn the user key into an encryption context key and load the key into the context */ status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_DEV_DERIVE, &mechanismInfo, MECHANISM_DERIVE_PKCS5 ); if( cryptStatusOK( status ) ) status = contextInfoPtr->loadKeyFunction( contextInfoPtr, mechanismInfo.dataOut, mechanismInfo.dataOutLength ); if( cryptStatusOK( status ) ) contextInfoPtr->flags |= CONTEXT_KEY_SET; return( status ); } /* Load an encoded composite key into a context. This is used for two purposes, to load public key components into native contexts and to save encoded public-key values for use in certs associated with non-native contexts held in a device. The latter is necessary because there's no key data stored with the context itself, however it's necessary to have SubjectPublicKeyInfo available for certificate requests/certificates. Normally this is sufficient because cryptlib always generates native contexts for public keys/certs, and for private keys the data is generated in the device with the encoded public components attached to the context as described above. For DH keys this gets a bit more complex, since although the private key is generated in the device, in the case of the DH responder this is only the DH x value, with the parameters (p and g) being supplied externally by the initiator. This means that it's necessary to decode at least some of the public key data in order to create the y value after the x value has been generated in the device. The only situation where this functionality is currently needed is for the SSHv2 code, which at the moment always uses native DH contexts. For this reason we leave off resolving this issue until it's actually required */ static int setKey( CONTEXT_INFO *contextInfoPtr, const CRYPT_ATTRIBUTE_TYPE keyType, const void *keyData, const int keyDataLen ) { static const int actionFlags = \ MK_ACTION_PERM( MESSAGE_CTX_SIGCHECK, ACTION_PERM_NONE_EXTERNAL ) | \ MK_ACTION_PERM( MESSAGE_CTX_ENCRYPT, ACTION_PERM_NONE_EXTERNAL ); static const int actionFlagsDH = ACTION_PERM_NONE_EXTERNAL_ALL; static const int actionFlagsPGP = \ MK_ACTION_PERM( MESSAGE_CTX_SIGCHECK, ACTION_PERM_ALL ) | \ MK_ACTION_PERM( MESSAGE_CTX_ENCRYPT, ACTION_PERM_ALL ); const CAPABILITY_INFO *capabilityInfoPtr = contextInfoPtr->capabilityInfo; STREAM stream; int status; assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) ); assert( contextInfoPtr->type == CONTEXT_PKC ); assert( needsKey( contextInfoPtr ) || \ ( contextInfoPtr->flags & CONTEXT_DUMMY ) ); assert( keyType == CRYPT_IATTRIBUTE_KEY_SPKI || \ keyType == CRYPT_IATTRIBUTE_KEY_PGP || \ keyType == CRYPT_IATTRIBUTE_KEY_SSH || \ keyType == CRYPT_IATTRIBUTE_KEY_SSH1 || \ keyType == CRYPT_IATTRIBUTE_KEY_SSL || \ keyType == CRYPT_IATTRIBUTE_KEY_SPKI_PARTIAL || \ keyType == CRYPT_IATTRIBUTE_KEY_PGP_PARTIAL ); assert( isReadPtr( keyData, keyDataLen ) ); /* If the keys are held externally (e.g. in a crypto device), copy the encoded public key data in and set up any other information that we may need from it. This information is used when loading a context from a key contained in a device, where the actual key components aren't directly available in the context but may be needed in the future for things like cert requests and certs */ if( contextInfoPtr->flags & CONTEXT_DUMMY ) { assert( keyType == CRYPT_IATTRIBUTE_KEY_SPKI || \ keyType == CRYPT_IATTRIBUTE_KEY_SPKI_PARTIAL ); if( ( contextInfoPtr->ctxPKC->publicKeyInfo = \ clAlloc( "processSetAttributeS", keyDataLen ) ) == NULL ) return( CRYPT_ERROR_MEMORY ); memcpy( contextInfoPtr->ctxPKC->publicKeyInfo, keyData, keyDataLen ); contextInfoPtr->ctxPKC->publicKeyInfoSize = keyDataLen; return( contextInfoPtr->ctxPKC->calculateKeyIDFunction( contextInfoPtr ) ); } /* Read the appropriately-formatted key data into the context, applying a lowest-common-denominator set of usage flags to the loaded key (more specific usage restrictions will be set by higher-level code) */ sMemConnect( &stream, keyData, keyDataLen ); status = contextInfoPtr->ctxPKC->readPublicKeyFunction( &stream, contextInfoPtr, attributeToFormatType( keyType ) ); sMemDisconnect( &stream ); if( cryptStatusError( status ) ) return( status ); /* If it's a partial load of the initial public portions of a private key with further key component operations to follow, there's nothing more to do at this point and we're done */ if( keyType == CRYPT_IATTRIBUTE_KEY_SPKI_PARTIAL || \ keyType == CRYPT_IATTRIBUTE_KEY_PGP_PARTIAL ) return( contextInfoPtr->ctxPKC->calculateKeyIDFunction( contextInfoPtr ) ); /* Perform an internal load that uses the key component values that we've just read into the context */ contextInfoPtr->flags |= CONTEXT_ISPUBLICKEY; status = contextInfoPtr->loadKeyFunction( contextInfoPtr, NULL, 0 ); if( cryptStatusError( status ) ) /* Map the status to a more appropriate code if necessary */ return( cryptArgError( status ) ? CRYPT_ERROR_BADDATA : status ); contextInfoPtr->flags |= CONTEXT_KEY_SET; /* Restrict the key usage to public-key-only actions if necessary. For PGP key loads (which, apart from the restrictions specified with the stored key data aren't constrained by the presence of ACLs in the form of certs) we allow external usage, for DH (whose keys can be both public and private keys even though technically it's a public key) we allow both encryption and decryption usage, and for public keys read from certs we allow internal usage only */ status = krnlSendMessage( contextInfoPtr->objectHandle, IMESSAGE_SETATTRIBUTE, ( keyType == CRYPT_IATTRIBUTE_KEY_PGP ) ? \ ( void * ) &actionFlagsPGP : \ ( capabilityInfoPtr->cryptAlgo == CRYPT_ALGO_DH ) ? \ ( void * ) &actionFlagsDH : \ ( void * ) &actionFlags, CRYPT_IATTRIBUTE_ACTIONPERMS ); if( cryptStatusError( status ) ) return( status ); contextInfoPtr->flags |= CONTEXT_KEY_SET; return( contextInfoPtr->ctxPKC->calculateKeyIDFunction( contextInfoPtr ) ); } /* Load a composite key into a context */ #ifndef USE_FIPS140 static int setKeyComponents( CONTEXT_INFO *contextInfoPtr, const void *keyData, const int keyDataLen ) { static const int actionFlags = \ MK_ACTION_PERM( MESSAGE_CTX_SIGCHECK, ACTION_PERM_ALL ) | \ MK_ACTION_PERM( MESSAGE_CTX_ENCRYPT, ACTION_PERM_ALL ); const CAPABILITY_INFO *capabilityInfoPtr = contextInfoPtr->capabilityInfo; BOOLEAN isPublicKey; int status; assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) ); assert( contextInfoPtr->type == CONTEXT_PKC ); assert( needsKey( contextInfoPtr ) ); assert( isReadPtr( keyData, keyDataLen ) ); assert( keyDataLen == sizeof( CRYPT_PKCINFO_RSA ) || \ keyDataLen == sizeof( CRYPT_PKCINFO_DLP ) || \ keyDataLen == sizeof( CRYPT_PKCINFO_ECC ) ); /* If it's a private key we need to have a key label set before we can continue. The checking for this is a bit complex because at this point all that the context knows is that it's a generic PKC context, but it won't know whether it's a public- or private-key context until the key is actually loaded. To determine what it'll become, we look into the key data to see what's being loaded */ isPublicKey = isEccAlgo( capabilityInfoPtr->cryptAlgo ) ? \ ( ( CRYPT_PKCINFO_ECC * ) keyData )->isPublicKey : \ isDlpAlgo( capabilityInfoPtr->cryptAlgo ) ? \ ( ( CRYPT_PKCINFO_DLP * ) keyData )->isPublicKey : \ ( ( CRYPT_PKCINFO_RSA * ) keyData )->isPublicKey; if( !isPublicKey && contextInfoPtr->labelSize <= 0 ) return( exitErrorNotInited( contextInfoPtr, CRYPT_CTXINFO_LABEL ) ); /* Load the key components into the context */ status = contextInfoPtr->loadKeyFunction( contextInfoPtr, keyData, keyDataLen ); if( cryptStatusError( status ) ) return( status ); contextInfoPtr->flags |= CONTEXT_KEY_SET | CONTEXT_PBO; /* Restrict the key usage to public-key-only actions if it's a public key. DH keys act as both public and private keys so we don't restrict their usage */ if( ( contextInfoPtr->flags & CONTEXT_ISPUBLICKEY ) && \ ( capabilityInfoPtr->cryptAlgo != CRYPT_ALGO_DH ) ) status = krnlSendMessage( contextInfoPtr->objectHandle, IMESSAGE_SETATTRIBUTE, ( void * ) &actionFlags, CRYPT_IATTRIBUTE_ACTIONPERMS ); return( status ); } #endif /* !USE_FIPS140 */ /* Encrypt a block of data */ static int encryptData( CONTEXT_INFO *contextInfoPtr, void *data, const int dataLength ) { BYTE savedData[ ENCRYPT_CHECKSIZE + 8 ]; const CAPABILITY_INFO *capabilityInfoPtr = contextInfoPtr->capabilityInfo; const int savedDataLength = min( dataLength, ENCRYPT_CHECKSIZE ); int status; assert( isWritePtr( contextInfoPtr, sizeof( CONTEXT_INFO ) ) ); assert( contextInfoPtr->type == CONTEXT_CONV || \ contextInfoPtr->type == CONTEXT_PKC ); assert( contextInfoPtr->encryptFunction != NULL ); assert( isWritePtr( data, dataLength ) ); if( contextInfoPtr->type == CONTEXT_PKC ) { const BOOLEAN isDLP = isDlpAlgo( capabilityInfoPtr->cryptAlgo ); /* Key agreement algorithms are treated as a special case since they don't actually encrypt the data */ if( isKeyxAlgo( capabilityInfoPtr->cryptAlgo ) ) { assert( dataLength == sizeof( KEYAGREE_PARAMS ) ); status = contextInfoPtr->encryptFunction( contextInfoPtr, data, dataLength ); if( !( contextInfoPtr->flags & CONTEXT_DUMMY ) ) clearTempBignums( contextInfoPtr->ctxPKC ); return( status ); } assert( !isDLP || dataLength == sizeof( DLP_PARAMS ) ); memcpy( savedData, isDLP ? ( ( DLP_PARAMS * ) data )->inParam1 : \ data, ENCRYPT_CHECKSIZE ); status = contextInfoPtr->encryptFunction( contextInfoPtr, data, dataLength ); if( !( contextInfoPtr->flags & CONTEXT_DUMMY ) ) clearTempBignums( contextInfoPtr->ctxPKC ); if( cryptStatusError( status ) ) { zeroise( savedData, ENCRYPT_CHECKSIZE ); return( status ); } /* Check for a catastrophic failure of the encryption */ if( isDLP ) { DLP_PARAMS *dlpParams = ( DLP_PARAMS * ) data; if( !memcmp( savedData, dlpParams->outParam, ENCRYPT_CHECKSIZE ) ) { zeroise( dlpParams->outParam, dlpParams->outLen ); status = CRYPT_ERROR_FAILED; } } else if( !memcmp( savedData, data, ENCRYPT_CHECKSIZE ) ) { zeroise( data, dataLength ); status = CRYPT_ERROR_FAILED; } zeroise( savedData, ENCRYPT_CHECKSIZE ); return( status ); } assert( isStreamCipher( capabilityInfoPtr->cryptAlgo ) || \ !needsIV( contextInfoPtr->ctxConv->mode ) || ( contextInfoPtr->flags & CONTEXT_IV_SET ) ); assert( contextInfoPtr->ctxConv->key == \ contextInfoPtr->storage + sizeof( CONV_INFO ) ); memcpy( savedData, data, savedDataLength ); status = contextInfoPtr->encryptFunction( contextInfoPtr, data, dataLength ); if( cryptStatusError( status ) || savedDataLength <= 6 ) { zeroise( savedData, ENCRYPT_CHECKSIZE ); return( status ); } /* Check for a catastrophic failure of the encryption. A check of a single block unfortunately isn't completely foolproof for 64-bit blocksize ciphers in CBC mode because of the way the IV is applied to the input. For the CBC encryption operation: out = enc( in ^ IV ) if out == IV the operation turns into a no-op. Consider the simple case where IV == in, so IV ^ in == 0. Then out = enc( 0 ) == IV, with the input appearing again at the output. In fact this can occur during normal operation once every 2^32 blocks (for a 64-bit block cipher). Although the chances of this happening are fairly low (the collision would have to occur on the first encrypted block in a message, since that's the one we check), if possible we check the first two blocks if we're using a 64-bit block cipher in CBC mode in order to reduce false positives */ if( !memcmp( savedData, data, savedDataLength ) ) { zeroise( data, dataLength ); status = CRYPT_ERROR_FAILED; } zeroise( savedData, ENCRYPT_CHECKSIZE ); return( status ); } /**************************************************************************** * * * Context Attribute Handling Functions * * * ****************************************************************************/ /* Handle data sent to or read from a context */ static int processGetAttribute( CONTEXT_INFO *contextInfoPtr, void *messageDataPtr, const int messageValue ) { const CAPABILITY_INFO *capabilityInfoPtr = contextInfoPtr->capabilityInfo; const CONTEXT_TYPE contextType = contextInfoPtr->type; int *valuePtr = ( int * ) messageDataPtr, value; switch( messageValue ) { case CRYPT_ATTRIBUTE_ERRORTYPE: *valuePtr = contextInfoPtr->errorType; return( CRYPT_OK ); case CRYPT_ATTRIBUTE_ERRORLOCUS: *valuePtr = contextInfoPtr->errorLocus; return( CRYPT_OK ); case CRYPT_OPTION_MISC_SIDECHANNELPROTECTION: *valuePtr = ( contextInfoPtr->flags & \ CONTEXT_SIDECHANNELPROTECTION ) ? TRUE : FALSE; return( CRYPT_OK ); case CRYPT_CTXINFO_ALGO: *valuePtr = capabilityInfoPtr->cryptAlgo; return( CRYPT_OK ); case CRYPT_CTXINFO_MODE: assert( contextType == CONTEXT_CONV ); *valuePtr = contextInfoPtr->ctxConv->mode; return( CRYPT_OK ); case CRYPT_CTXINFO_KEYSIZE: switch( contextType ) { case CONTEXT_CONV: value = contextInfoPtr->ctxConv->userKeyLength; break; case CONTEXT_PKC: value = bitsToBytes( contextInfoPtr->ctxPKC->keySizeBits ); break; case CONTEXT_MAC: value = contextInfoPtr->ctxMAC->userKeyLength; break; default: assert( NOTREACHED ); return( CRYPT_ERROR ); } if( value <= 0 ) /* If a key hasn't been loaded yet, we return the default key size */ value = capabilityInfoPtr->keySize; *valuePtr = value; return( CRYPT_OK ); case CRYPT_CTXINFO_BLOCKSIZE: if( contextType == CONTEXT_CONV && \ ( contextInfoPtr->ctxConv->mode == CRYPT_MODE_CFB || \ contextInfoPtr->ctxConv->mode == CRYPT_MODE_OFB ) ) *valuePtr = 1; /* Block cipher in stream mode */ else *valuePtr = capabilityInfoPtr->blockSize; return( CRYPT_OK ); case CRYPT_CTXINFO_IVSIZE: assert( contextType == CONTEXT_CONV ); if( !needsIV( contextInfoPtr->ctxConv->mode ) || \ isStreamCipher( capabilityInfoPtr->cryptAlgo ) ) return( CRYPT_ERROR_NOTAVAIL ); *valuePtr = capabilityInfoPtr->blockSize; return( CRYPT_OK ); case CRYPT_CTXINFO_KEYING_ALGO: case CRYPT_OPTION_KEYING_ALGO: switch( contextType ) { case CONTEXT_CONV: value = contextInfoPtr->ctxConv->keySetupAlgorithm; break; case CONTEXT_MAC: value = contextInfoPtr->ctxMAC->keySetupAlgorithm; break; default: assert( NOTREACHED ); return( CRYPT_ERROR ); } if( value <= 0 ) return( exitErrorNotInited( contextInfoPtr, CRYPT_CTXINFO_KEYING_ALGO ) ); *valuePtr = value; return( CRYPT_OK ); case CRYPT_CTXINFO_KEYING_ITERATIONS: case CRYPT_OPTION_KEYING_ITERATIONS: switch( contextType ) { case CONTEXT_CONV: value = contextInfoPtr->ctxConv->keySetupIterations; break; case CONTEXT_MAC: value = contextInfoPtr->ctxMAC->keySetupIterations; break; default: assert( NOTREACHED ); return( CRYPT_ERROR ); } if( value <= 0 ) return( exitErrorNotInited( contextInfoPtr, CRYPT_CTXINFO_KEYING_ITERATIONS ) ); *valuePtr = value; return( CRYPT_OK ); case CRYPT_CTXINFO_PERSISTENT: *valuePtr = ( contextInfoPtr->flags & CONTEXT_PERSISTENT ) ? \ TRUE : FALSE; return( CRYPT_OK ); case CRYPT_IATTRIBUTE_KEYFEATURES: assert( contextType == CONTEXT_PKC ); *valuePtr = ( contextInfoPtr->flags & CONTEXT_PBO ) ? 1 : 0; #ifdef USE_DEVICES *valuePtr |= ( contextInfoPtr->deviceObject > 0 ) ? 2 : 0; #endif /* USE_DEVICES */ return( CRYPT_OK ); case CRYPT_IATTRIBUTE_DEVICEOBJECT: #ifdef USE_DEVICES if( contextInfoPtr->deviceObject < 0 ) return( CRYPT_ERROR_NOTFOUND ); *valuePtr = ( int ) contextInfoPtr->deviceObject; return( CRYPT_OK ); #else return( CRYPT_ERROR_NOTFOUND ); #endif /* USE_DEVICES */ } assert( NOTREACHED ); return( CRYPT_ERROR ); /* Get rid of compiler warning */ } static int processGetAttributeS( CONTEXT_INFO *contextInfoPtr, void *messageDataPtr, const int messageValue ) { const CAPABILITY_INFO *capabilityInfoPtr = contextInfoPtr->capabilityInfo; const CONTEXT_TYPE contextType = contextInfoPtr->type; MESSAGE_DATA *msgData = ( MESSAGE_DATA * ) messageDataPtr; int status; switch( messageValue ) { case CRYPT_CTXINFO_NAME_ALGO: return( attributeCopy( msgData, capabilityInfoPtr->algoName, strlen( capabilityInfoPtr->algoName ) ) ); case CRYPT_CTXINFO_NAME_MODE: assert( contextType == CONTEXT_CONV ); switch( contextInfoPtr->ctxConv->mode ) { case CRYPT_MODE_ECB: return( attributeCopy( msgData, "ECB", 3 ) ); case CRYPT_MODE_CBC: return( attributeCopy( msgData, "CBC", 3 ) ); case CRYPT_MODE_CFB: return( attributeCopy( msgData, "CFB", 3 ) ); case CRYPT_MODE_OFB: return( attributeCopy( msgData, "OFB", 3 ) ); } assert( NOTREACHED ); return( CRYPT_ERROR ); /* Get rid of compiler warning */ case CRYPT_CTXINFO_KEYING_SALT: assert( contextType == CONTEXT_CONV || \ contextType == CONTEXT_MAC ); if( contextType == CONTEXT_CONV ) { if( contextInfoPtr->ctxConv->saltLength <= 0 ) return( exitErrorInited( contextInfoPtr, CRYPT_CTXINFO_KEYING_SALT ) ); return( attributeCopy( msgData, contextInfoPtr->ctxConv->salt, contextInfoPtr->ctxConv->saltLength ) ); } if( contextInfoPtr->ctxMAC->saltLength <= 0 ) return( exitErrorInited( contextInfoPtr, CRYPT_CTXINFO_KEYING_SALT ) ); return( attributeCopy( msgData, contextInfoPtr->ctxMAC->salt, contextInfoPtr->ctxMAC->saltLength ) ); case CRYPT_CTXINFO_IV: assert( contextType == CONTEXT_CONV ); if( !needsIV( contextInfoPtr->ctxConv->mode ) || \ isStreamCipher( contextInfoPtr->capabilityInfo->cryptAlgo ) ) return( CRYPT_ERROR_NOTAVAIL ); if( !( contextInfoPtr->flags & CONTEXT_IV_SET ) ) return( exitErrorNotInited( contextInfoPtr, CRYPT_CTXINFO_IV ) ); return( attributeCopy( msgData, contextInfoPtr->ctxConv->iv, contextInfoPtr->ctxConv->ivLength ) ); case CRYPT_CTXINFO_HASHVALUE: assert( contextType == CONTEXT_HASH || \ contextType == CONTEXT_MAC ); if( !( contextInfoPtr->flags & CONTEXT_HASH_INITED ) ) return( CRYPT_ERROR_NOTINITED ); if( !( contextInfoPtr->flags & CONTEXT_HASH_DONE ) ) return( CRYPT_ERROR_INCOMPLETE ); return( attributeCopy( msgData, ( contextType == CONTEXT_HASH ) ? \ contextInfoPtr->ctxHash->hash : \ contextInfoPtr->ctxMAC->mac, capabilityInfoPtr->blockSize ) ); case CRYPT_CTXINFO_LABEL: if( contextInfoPtr->labelSize <= 0 ) return( exitErrorNotInited( contextInfoPtr, CRYPT_CTXINFO_LABEL ) ); return( attributeCopy( msgData, contextInfoPtr->label, contextInfoPtr->labelSize ) ); case CRYPT_IATTRIBUTE_KEYID: assert( contextType == CONTEXT_PKC ); return( attributeCopy( msgData, contextInfoPtr->ctxPKC->keyID, KEYID_SIZE ) ); case CRYPT_IATTRIBUTE_KEYID_PGP: assert( contextType == CONTEXT_PKC ); if( contextInfoPtr->capabilityInfo->cryptAlgo != CRYPT_ALGO_RSA ) return( CRYPT_ERROR_NOTFOUND ); return( attributeCopy( msgData, contextInfoPtr->ctxPKC->pgpKeyID, PGP_KEYID_SIZE ) ); case CRYPT_IATTRIBUTE_KEYID_OPENPGP: assert( contextType == CONTEXT_PKC ); assert( contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_RSA || \ contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_DSA || \ contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_ELGAMAL ); return( attributeCopy( msgData, contextInfoPtr->ctxPKC->openPgpKeyID, PGP_KEYID_SIZE ) ); #ifdef USE_KEA case CRYPT_IATTRIBUTE_KEY_KEADOMAINPARAMS: assert( contextType == CONTEXT_PKC ); return( attributeCopy( msgData, contextInfoPtr->ctxPKC->domainParamPtr, contextInfoPtr->ctxPKC->domainParamSize ) ); case CRYPT_IATTRIBUTE_KEY_KEAPUBLICVALUE: assert( contextType == CONTEXT_PKC ); return( attributeCopy( msgData, contextInfoPtr->ctxPKC->publicValuePtr, contextInfoPtr->ctxPKC->publicValueSize ) ); #else case CRYPT_IATTRIBUTE_KEY_KEADOMAINPARAMS: case CRYPT_IATTRIBUTE_KEY_KEAPUBLICVALUE: return( CRYPT_ERROR_NOTFOUND ); #endif /* USE_KEA */ case CRYPT_IATTRIBUTE_KEY_SPKI: assert( contextType == CONTEXT_PKC ); assert( contextInfoPtr->flags & CONTEXT_KEY_SET ); if( contextInfoPtr->ctxPKC->publicKeyInfo != NULL ) { /* If the data is available in pre-encoded form, copy it out */ return( attributeCopy( msgData, contextInfoPtr->ctxPKC->publicKeyInfo, contextInfoPtr->ctxPKC->publicKeyInfoSize ) ); } /* Drop through */ case CRYPT_IATTRIBUTE_KEY_SSH: case CRYPT_IATTRIBUTE_KEY_SSH1: case CRYPT_IATTRIBUTE_KEY_SSL: { STREAM stream; assert( contextType == CONTEXT_PKC ); assert( contextInfoPtr->flags & CONTEXT_KEY_SET ); /* Write the appropriately-formatted key data from the context */ sMemOpen( &stream, msgData->data, msgData->length ); status = contextInfoPtr->ctxPKC->writePublicKeyFunction( &stream, contextInfoPtr, attributeToFormatType( messageValue ), "public" ); if( cryptStatusOK( status ) ) msgData->length = stell( &stream ); sMemDisconnect( &stream ); return( status ); } case CRYPT_IATTRIBUTE_PGPVALIDITY: assert( contextType == CONTEXT_PKC ); *( ( time_t * ) msgData->data ) = \ contextInfoPtr->ctxPKC->pgpCreationTime; return( CRYPT_OK ); } assert( NOTREACHED ); return( CRYPT_ERROR ); /* Get rid of compiler warning */ } static int processSetAttribute( CONTEXT_INFO *contextInfoPtr, void *messageDataPtr, const int messageValue ) { const CAPABILITY_INFO *capabilityInfoPtr = contextInfoPtr->capabilityInfo; const CONTEXT_TYPE contextType = contextInfoPtr->type; const int value = *( ( int * ) messageDataPtr ); int *valuePtr; int status; switch( messageValue ) { case CRYPT_OPTION_MISC_SIDECHANNELPROTECTION: if( value ) contextInfoPtr->flags |= CONTEXT_SIDECHANNELPROTECTION; else contextInfoPtr->flags &= ~CONTEXT_SIDECHANNELPROTECTION; return( CRYPT_OK ); case CRYPT_CTXINFO_MODE: assert( contextType == CONTEXT_CONV ); /* If the mode isn't set to the initial default, it's already been explicitly set and we can't change it again */ if( contextInfoPtr->ctxConv->mode != \ ( isStreamCipher( capabilityInfoPtr->cryptAlgo ) ? \ CRYPT_MODE_OFB : CRYPT_MODE_CBC ) ) return( exitErrorInited( contextInfoPtr, CRYPT_CTXINFO_MODE ) ); /* Set the en/decryption mode */ assert( capabilityInfoPtr->initKeyParamsFunction != NULL ); return( capabilityInfoPtr->initKeyParamsFunction( contextInfoPtr, NULL, 0, value ) ); case CRYPT_CTXINFO_KEYSIZE: assert( capabilityInfoPtr->getInfoFunction != NULL ); switch( contextType ) { case CONTEXT_CONV: valuePtr = &contextInfoPtr->ctxConv->userKeyLength; break; case CONTEXT_PKC: valuePtr = &contextInfoPtr->ctxPKC->keySizeBits; break; case CONTEXT_MAC: valuePtr = &contextInfoPtr->ctxMAC->userKeyLength; break; default: assert( NOTREACHED ); return( CRYPT_ERROR ); } if( *valuePtr ) return( exitErrorInited( contextInfoPtr, CRYPT_CTXINFO_KEYSIZE ) ); /* Trim the user-supplied value to the correct shape, taking into account various issues such as limitations with the underlying crypto code/hardware and the (in)ability to export overly long keys using short public keys */ status = capabilityInfoPtr->getInfoFunction( CAPABILITY_INFO_KEYSIZE, contextInfoPtr, value ); if( cryptStatusError( status ) ) return( status ); *valuePtr = ( contextType == CONTEXT_PKC ) ? \ bytesToBits( status ) : status; return( CRYPT_OK ); case CRYPT_CTXINFO_KEYING_ALGO: case CRYPT_OPTION_KEYING_ALGO: { CRYPT_ALGO_TYPE *algoValuePtr; assert( contextType == CONTEXT_CONV || \ contextType == CONTEXT_MAC ); algoValuePtr = ( contextType == CONTEXT_CONV ) ? \ &contextInfoPtr->ctxConv->keySetupAlgorithm : \ &contextInfoPtr->ctxMAC->keySetupAlgorithm; if( *algoValuePtr != CRYPT_ALGO_NONE ) return( exitErrorInited( contextInfoPtr, CRYPT_CTXINFO_KEYING_ALGO ) ); *algoValuePtr = value; return( CRYPT_OK ); } case CRYPT_CTXINFO_KEYING_ITERATIONS: case CRYPT_OPTION_KEYING_ITERATIONS: assert( contextType == CONTEXT_CONV || \ contextType == CONTEXT_MAC ); valuePtr = ( contextType == CONTEXT_CONV ) ? \ &contextInfoPtr->ctxConv->keySetupIterations : \ &contextInfoPtr->ctxMAC->keySetupIterations; if( *valuePtr ) return( exitErrorInited( contextInfoPtr, CRYPT_CTXINFO_KEYING_ITERATIONS ) ); *valuePtr = value; return( CRYPT_OK ); case CRYPT_CTXINFO_PERSISTENT: /* The is-object-persistent attribute functions as follows: | Software | Hardware -----+----------+------------------- PKC | R/O (1) | R/O (2) -----+----------+------------------- Conv | R/O (1) | R/W low state (3) | | R/O high state (1) = Set if stored to or read from a keyset. (2) = Always set. Private-key objects are automatically created as persistent objects, public-key objects are (transparently) created as software objects since the operation is much faster on the host system than by going via the device. (3) = Can be set in the low state, if set then the object is created as a persistent object in the device. Most of these checks are enforced by the kernel, the one thing that the ACL language can't specify is the requirement that a persistent conventional-encryption object be tied to a device, which we do explicitly here */ if( value && !( contextInfoPtr->flags & CONTEXT_DUMMY ) ) return( CRYPT_ERROR_PERMISSION ); /* Set or clear the persistent flag as required */ if( value ) contextInfoPtr->flags |= CONTEXT_PERSISTENT; else contextInfoPtr->flags &= ~CONTEXT_PERSISTENT; return( CRYPT_OK ); case CRYPT_IATTRIBUTE_INITIALISED: return( CRYPT_OK ); case CRYPT_IATTRIBUTE_KEYSIZE: /* If it's a private key context or a persistent context, we need to have a key label set before we can continue */ if( ( ( contextInfoPtr->type == CONTEXT_PKC ) || \ ( contextInfoPtr->flags & CONTEXT_PERSISTENT ) ) && \ contextInfoPtr->labelSize <= 0 ) return( exitErrorNotInited( contextInfoPtr, CRYPT_CTXINFO_LABEL ) ); /* If the key is held outside the context (e.g. in a device), we need to manually supply the key-related information needed by the context, which in this case is the key size. Once this is set, there is (effectively) a key loaded, although the actual keying values are held anderswhere */ switch( contextType ) { case CONTEXT_CONV: contextInfoPtr->ctxConv->userKeyLength = value; break; case CONTEXT_PKC: contextInfoPtr->ctxPKC->keySizeBits = bytesToBits( value ); break; case CONTEXT_MAC: contextInfoPtr->ctxMAC->userKeyLength = value; break; default: assert( NOTREACHED ); return( CRYPT_ERROR ); } contextInfoPtr->flags |= CONTEXT_KEY_SET; return( CRYPT_OK ); case CRYPT_IATTRIBUTE_DEVICEOBJECT: #ifdef USE_DEVICES contextInfoPtr->deviceObject = value; #endif /* USE_DEVICES */ return( CRYPT_OK ); } assert( NOTREACHED ); return( CRYPT_ERROR ); /* Get rid of compiler warning */ } static int processSetAttributeS( CONTEXT_INFO *contextInfoPtr, void *messageDataPtr, const int messageValue ) { const CAPABILITY_INFO *capabilityInfoPtr = contextInfoPtr->capabilityInfo; const CONTEXT_TYPE contextType = contextInfoPtr->type; const MESSAGE_DATA *msgData = ( MESSAGE_DATA * ) messageDataPtr; int status; switch( messageValue ) { case CRYPT_CTXINFO_KEYING_SALT: assert( contextType == CONTEXT_CONV || \ contextType == CONTEXT_MAC ); if( contextType == CONTEXT_CONV ) { if( contextInfoPtr->ctxConv->saltLength > 0 ) return( exitErrorInited( contextInfoPtr, CRYPT_CTXINFO_KEYING_SALT ) ); memcpy( contextInfoPtr->ctxConv->salt, msgData->data, msgData->length ); contextInfoPtr->ctxConv->saltLength = msgData->length; return( CRYPT_OK ); } if( contextInfoPtr->ctxMAC->saltLength > 0 ) return( exitErrorInited( contextInfoPtr, CRYPT_CTXINFO_KEYING_SALT ) ); memcpy( contextInfoPtr->ctxMAC->salt, msgData->data, msgData->length ); contextInfoPtr->ctxMAC->saltLength = msgData->length; return( CRYPT_OK ); case CRYPT_CTXINFO_KEYING_VALUE: return( deriveKey( contextInfoPtr, msgData->data, msgData->length ) ); case CRYPT_CTXINFO_KEY: assert( contextType == CONTEXT_CONV || \ contextType == CONTEXT_MAC ); assert( needsKey( contextInfoPtr ) ); /* If it's a persistent context, we need to have a key label set before we can continue */ if( ( contextInfoPtr->flags & CONTEXT_PERSISTENT ) && \ contextInfoPtr->labelSize <= 0 ) return( exitErrorNotInited( contextInfoPtr, CRYPT_CTXINFO_LABEL ) ); /* The kernel performs a general check on the size of this attribute but doesn't know about context subtype-specific limits, so we perform a context-specific check here */ if( msgData->length < capabilityInfoPtr->minKeySize || \ msgData->length > capabilityInfoPtr->maxKeySize ) return( CRYPT_ARGERROR_NUM1 ); /* Load the key into the context */ status = contextInfoPtr->loadKeyFunction( contextInfoPtr, msgData->data, msgData->length ); if( cryptStatusOK( status ) ) contextInfoPtr->flags |= CONTEXT_KEY_SET; return( status ); #ifndef USE_FIPS140 case CRYPT_CTXINFO_KEY_COMPONENTS: return( setKeyComponents( contextInfoPtr, msgData->data, msgData->length ) ); #endif /* !USE_FIPS140 */ case CRYPT_CTXINFO_IV: assert( contextType == CONTEXT_CONV ); /* If it's a mode that doesn't use an IV, the load IV operation is meaningless */ if( !needsIV( contextInfoPtr->ctxConv->mode ) || \ isStreamCipher( contextInfoPtr->capabilityInfo->cryptAlgo ) ) return( CRYPT_ERROR_NOTAVAIL ); /* Make sure that the data size is valid */ if( msgData->length != capabilityInfoPtr->blockSize ) return( CRYPT_ARGERROR_NUM1 ); /* Load the IV */ assert( capabilityInfoPtr->initKeyParamsFunction != NULL ); return( capabilityInfoPtr->initKeyParamsFunction( contextInfoPtr, msgData->data, msgData->length, CRYPT_MODE_NONE ) ); case CRYPT_CTXINFO_LABEL: { CRYPT_HANDLE cryptHandle; if( contextInfoPtr->labelSize > 0 ) return( exitErrorInited( contextInfoPtr, CRYPT_CTXINFO_LABEL ) ); /* Check any device object that the context is associated with to ensure that nothing with that label already exists in the device. For keysets the check for duplicates is performed when the context is explicitly added to the keyset, but with devices the context will be implicitly created within the device at some future point that depends on the device (at context creation, on key load/generation, or at some other point). Because of this we perform a pre-emptive check for duplicates to avoid a potentially confusing error condition at some point in the future. Since objects are typed, we have to check for the three possible { label, type } combinations. In theory we could require that labels are only unique for an object type, but this can lead to problems with underlying devices or keysets that only support a check by label and not by { label, type } combination. In addition, we can't send the message to the context because the kernel won't forward this message type (sending a get-key message to a context doesn't make sense) so we have to explicitly get the dependent device and send the get-key directly to it */ status = krnlSendMessage( contextInfoPtr->objectHandle, IMESSAGE_GETDEPENDENT, &cryptHandle, OBJECT_TYPE_DEVICE ); if( cryptStatusOK( status ) && \ ( cryptHandle != SYSTEM_OBJECT_HANDLE ) ) { MESSAGE_KEYMGMT_INFO getkeyInfo; setMessageKeymgmtInfo( &getkeyInfo, CRYPT_KEYID_NAME, msgData->data, msgData->length, NULL, 0, KEYMGMT_FLAG_CHECK_ONLY ); status = krnlSendMessage( cryptHandle, IMESSAGE_KEY_GETKEY, &getkeyInfo, KEYMGMT_ITEM_SECRETKEY ); if( cryptStatusError( status ) ) status = krnlSendMessage( cryptHandle, IMESSAGE_KEY_GETKEY, &getkeyInfo, KEYMGMT_ITEM_PUBLICKEY ); if( cryptStatusError( status ) ) status = krnlSendMessage( cryptHandle, IMESSAGE_KEY_GETKEY, &getkeyInfo, KEYMGMT_ITEM_PRIVATEKEY ); if( cryptStatusOK( status ) ) { /* We've found something with this label already present, we can't use it again */ return( CRYPT_ERROR_DUPLICATE ); } assert( !cryptArgError( status ) ); } /* Fall through */ } case CRYPT_IATTRIBUTE_EXISTINGLABEL: /* The difference between CRYPT_CTXINFO_LABEL and CRYPT_IATTRIBUTE_EXISTINGLABEL is that the latter is used to set a label for a context that's being instantiated from a persistent object in a device. We can't perform the duplicate-label check in this case because we'll always get a match for the device object's label */ if( contextInfoPtr->labelSize > 0 ) return( exitErrorInited( contextInfoPtr, CRYPT_CTXINFO_LABEL ) ); /* Set the label */ memcpy( contextInfoPtr->label, msgData->data, msgData->length ); contextInfoPtr->labelSize = msgData->length; return( CRYPT_OK ); case CRYPT_IATTRIBUTE_KEYID_OPENPGP: assert( contextType == CONTEXT_PKC ); assert( contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_RSA || \ contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_DSA || \ contextInfoPtr->capabilityInfo->cryptAlgo == CRYPT_ALGO_ELGAMAL ); assert( msgData->length == PGP_KEYID_SIZE ); memcpy( contextInfoPtr->ctxPKC->openPgpKeyID, msgData->data, msgData->length ); contextInfoPtr->ctxPKC->openPgpKeyIDSet = TRUE; /* If it's a non-PGP 2.x key type, set the PGP 2.x keyID to the OpenPGP keyID. This is necessary because non-PGP 2.x keys can be used with PGP 2.x message formats which would imply the use of a PGP 2.x keyID, except that it's not defined for this key type */ if( contextInfoPtr->capabilityInfo->cryptAlgo != CRYPT_ALGO_RSA ) memcpy( contextInfoPtr->ctxPKC->pgpKeyID, contextInfoPtr->ctxPKC->openPgpKeyID, PGP_KEYID_SIZE ); return( CRYPT_OK ); case CRYPT_IATTRIBUTE_KEY_SPKI: case CRYPT_IATTRIBUTE_KEY_PGP: case CRYPT_IATTRIBUTE_KEY_SSH: case CRYPT_IATTRIBUTE_KEY_SSH1: case CRYPT_IATTRIBUTE_KEY_SSL: case CRYPT_IATTRIBUTE_KEY_SPKI_PARTIAL: case CRYPT_IATTRIBUTE_KEY_PGP_PARTIAL: return( setKey( contextInfoPtr, messageValue, msgData->data, msgData->length ) ); case CRYPT_IATTRIBUTE_PGPVALIDITY: assert( contextType == CONTEXT_PKC ); contextInfoPtr->ctxPKC->pgpCreationTime = \ *( ( time_t * ) msgData->data ); return( CRYPT_OK ); } assert( NOTREACHED ); return( CRYPT_ERROR ); /* Get rid of compiler warning */ } static int processDeleteAttribute( CONTEXT_INFO *contextInfoPtr, const int messageValue ) { const CONTEXT_TYPE contextType = contextInfoPtr->type; switch( messageValue ) { case CRYPT_CTXINFO_KEYING_ALGO: assert( contextType == CONTEXT_CONV || \ contextType == CONTEXT_MAC ); if( contextType == CONTEXT_CONV ) { if( contextInfoPtr->ctxConv->keySetupAlgorithm == CRYPT_ALGO_NONE ) return( exitErrorNotFound( contextInfoPtr, CRYPT_CTXINFO_KEYING_ALGO ) ); contextInfoPtr->ctxConv->keySetupAlgorithm = CRYPT_ALGO_NONE; return( CRYPT_OK ); } if( contextInfoPtr->ctxMAC->keySetupAlgorithm == CRYPT_ALGO_NONE ) return( exitErrorNotFound( contextInfoPtr, CRYPT_CTXINFO_KEYING_ALGO ) ); contextInfoPtr->ctxMAC->keySetupAlgorithm = CRYPT_ALGO_NONE; return( CRYPT_OK ); case CRYPT_CTXINFO_KEYING_ITERATIONS: assert( contextType == CONTEXT_CONV || \ contextType == CONTEXT_MAC ); if( contextType == CONTEXT_CONV ) { if( contextInfoPtr->ctxConv->keySetupIterations == 0 ) return( exitErrorNotFound( contextInfoPtr, CRYPT_CTXINFO_KEYING_ITERATIONS ) ); contextInfoPtr->ctxConv->keySetupIterations = 0; return( CRYPT_OK ); } if( contextInfoPtr->ctxMAC->keySetupIterations == 0 ) return( exitErrorNotFound( contextInfoPtr, CRYPT_CTXINFO_KEYING_ITERATIONS ) ); contextInfoPtr->ctxMAC->keySetupIterations = 0; return( CRYPT_OK ); case CRYPT_CTXINFO_KEYING_SALT: assert( contextType == CONTEXT_CONV || \ contextType == CONTEXT_MAC ); if( contextType == CONTEXT_CONV ) { if( contextInfoPtr->ctxConv->saltLength == 0 ) return( exitErrorNotFound( contextInfoPtr, CRYPT_CTXINFO_KEYING_SALT ) ); zeroise( contextInfoPtr->ctxConv->salt, CRYPT_MAX_HASHSIZE ); contextInfoPtr->ctxConv->saltLength = 0; return( CRYPT_OK ); } if( contextInfoPtr->ctxMAC->saltLength == 0 ) return( exitErrorNotFound( contextInfoPtr, CRYPT_CTXINFO_KEYING_SALT ) ); zeroise( contextInfoPtr->ctxMAC->salt, CRYPT_MAX_HASHSIZE ); contextInfoPtr->ctxMAC->saltLength = 0; return( CRYPT_OK ); case CRYPT_CTXINFO_IV: assert( contextType == CONTEXT_CONV ); if( !needsIV( contextInfoPtr->ctxConv->mode ) || \ isStreamCipher( contextInfoPtr->capabilityInfo->cryptAlgo ) ) return( exitErrorNotFound( contextInfoPtr, CRYPT_CTXINFO_IV ) ); contextInfoPtr->ctxConv->ivLength = \ contextInfoPtr->ctxConv->ivCount = 0; contextInfoPtr->flags &= ~CONTEXT_IV_SET; return( CRYPT_OK ); case CRYPT_CTXINFO_LABEL: if( contextInfoPtr->labelSize <= 0 ) return( exitErrorNotFound( contextInfoPtr, CRYPT_CTXINFO_LABEL ) ); zeroise( contextInfoPtr->label, contextInfoPtr->labelSize ); contextInfoPtr->labelSize = 0; return( CRYPT_OK ); case CRYPT_CTXINFO_HASHVALUE: switch( contextType ) { case CONTEXT_HASH: zeroise( contextInfoPtr->ctxHash->hash, CRYPT_MAX_HASHSIZE ); break; case CONTEXT_MAC: zeroise( contextInfoPtr->ctxMAC->mac, CRYPT_MAX_HASHSIZE ); break; default: assert( NOTREACHED ); return( CRYPT_ERROR ); } contextInfoPtr->flags &= ~( CONTEXT_HASH_INITED | \ CONTEXT_HASH_DONE ); return( CRYPT_OK ); } assert( NOTREACHED ); return( CRYPT_ERROR ); /* Get rid of compiler warning */ } /**************************************************************************** * * * Context Message Handler * * * ****************************************************************************/ /* Handle a message sent to an encryption context */ static int contextMessageFunction( void *objectInfoPtr, const MESSAGE_TYPE message, void *messageDataPtr, const int messageValue ) { CONTEXT_INFO *contextInfoPtr = ( CONTEXT_INFO * ) objectInfoPtr; const CAPABILITY_INFO *capabilityInfo = contextInfoPtr->capabilityInfo; int status; /* Process destroy object messages */ if( message == MESSAGE_DESTROY ) { const CONTEXT_TYPE contextType = contextInfoPtr->type; #if 0 /* 9/12/02 We can never get here because we can't send a message to a busy object any more */ /* If the context is busy, abort the async.operation. We do this by setting the abort flag (which is OK, since the context is about to be destroyed anyway) and then waiting for the busy flag to be cleared */ contextInfoPtr->flags |= CONTEXT_ASYNC_ABORT; krnlSendMessage( cryptContext, IMESSAGE_GETATTRIBUTE, &status, CRYPT_IATTRIBUTE_STATUS ); if( status & OBJECT_FLAG_BUSY ) { int iterationCount = 0; /* Unlock the object so that the background thread can access it. Nothing else will get in because the object is in the signalled state */ unlockResource( contextInfoPtr ); /* Wait awhile and check whether we've left the busy state */ do { THREAD_SLEEP( 250 ); /* Wait 1/4s */ krnlSendMessage( cryptContext, IMESSAGE_GETATTRIBUTE, &status, CRYPT_IATTRIBUTE_STATUS ); } while( ( status & OBJECT_FLAG_BUSY ) && \ iterationCount++ < FAILSAFE_ITERATIONS_LARGE ); if( iterationCount >= FAILSAFE_ITERATIONS_LARGE ) retIntError(); getCheckInternalResource( cryptContext, contextInfoPtr, OBJECT_TYPE_CONTEXT ); } #endif /* 0 */ /* Perform any algorithm-specific shutdown */ if( capabilityInfo->endFunction != NULL ) capabilityInfo->endFunction( contextInfoPtr ); /* Perform context-type-specific cleanup */ if( contextType == CONTEXT_PKC ) freeContextBignums( contextInfoPtr->ctxPKC, contextInfoPtr->flags ); return( CRYPT_OK ); } /* Process attribute get/set/delete messages */ if( isAttributeMessage( message ) ) { if( message == MESSAGE_GETATTRIBUTE ) return( processGetAttribute( contextInfoPtr, messageDataPtr, messageValue ) ); if( message == MESSAGE_GETATTRIBUTE_S ) return( processGetAttributeS( contextInfoPtr, messageDataPtr, messageValue ) ); if( message == MESSAGE_SETATTRIBUTE ) return( processSetAttribute( contextInfoPtr, messageDataPtr, messageValue ) ); if( message == MESSAGE_SETATTRIBUTE_S ) return( processSetAttributeS( contextInfoPtr, messageDataPtr, messageValue ) ); if( message == MESSAGE_DELETEATTRIBUTE ) return( processDeleteAttribute( contextInfoPtr, messageValue ) ); assert( NOTREACHED ); return( CRYPT_ERROR ); /* Get rid of compiler warning */ } /* Process action messages */ if( isActionMessage( message ) ) { assert( message == MESSAGE_CTX_HASH || \ isWritePtr( messageDataPtr, messageValue ) ); switch( message ) { case MESSAGE_CTX_ENCRYPT: status = encryptData( contextInfoPtr, messageDataPtr, messageValue ); assert( cryptStatusOK( status ) ); break; case MESSAGE_CTX_DECRYPT: assert( contextInfoPtr->decryptFunction != NULL ); assert( contextInfoPtr->type == CONTEXT_PKC || \ ( isStreamCipher( capabilityInfo->cryptAlgo ) || \ !needsIV( contextInfoPtr->ctxConv->mode ) || ( contextInfoPtr->flags & CONTEXT_IV_SET ) ) ); status = contextInfoPtr->decryptFunction( contextInfoPtr, messageDataPtr, messageValue ); if( contextInfoPtr->type == CONTEXT_PKC && \ !( contextInfoPtr->flags & CONTEXT_DUMMY ) ) clearTempBignums( contextInfoPtr->ctxPKC ); assert( cryptStatusOK( status ) ); break; case MESSAGE_CTX_SIGN: assert( capabilityInfo->signFunction != NULL ); status = capabilityInfo->signFunction( contextInfoPtr, messageDataPtr, messageValue ); if( !( contextInfoPtr->flags & CONTEXT_DUMMY ) ) clearTempBignums( contextInfoPtr->ctxPKC ); assert( cryptStatusOK( status ) ); break; case MESSAGE_CTX_SIGCHECK: assert( capabilityInfo->sigCheckFunction != NULL ); status = capabilityInfo->sigCheckFunction( contextInfoPtr, messageDataPtr, messageValue ); if( !( contextInfoPtr->flags & CONTEXT_DUMMY ) ) clearTempBignums( contextInfoPtr->ctxPKC ); break; case MESSAGE_CTX_HASH: assert( capabilityInfo->encryptFunction != NULL ); assert( ( contextInfoPtr->type == CONTEXT_HASH && \ contextInfoPtr->ctxHash->hashInfo == \ contextInfoPtr->storage + sizeof( HASH_INFO ) ) || \ ( contextInfoPtr->type == CONTEXT_MAC && \ contextInfoPtr->ctxMAC->macInfo == \ contextInfoPtr->storage + sizeof( MAC_INFO ) ) ); /* If we've already completed the hashing/MACing, we can't continue */ if( contextInfoPtr->flags & CONTEXT_HASH_DONE ) return( CRYPT_ERROR_COMPLETE ); status = capabilityInfo->encryptFunction( contextInfoPtr, messageDataPtr, messageValue ); if( messageValue > 0 ) /* Usually the MAC initialisation happens when we load the key, but if we've deleted the MAC value to process another piece of data it'll happen on-demand, so we have to set the flag here */ contextInfoPtr->flags |= CONTEXT_HASH_INITED; else /* Usually a hash of zero bytes is used to wrap up an ongoing hash operation, however it can also be the only operation if a zero-byte string is being hashed. To handle this we have to set the inited flag as well as the done flag */ contextInfoPtr->flags |= CONTEXT_HASH_DONE | \ CONTEXT_HASH_INITED; assert( cryptStatusOK( status ) ); break; default: assert( NOTREACHED ); return( CRYPT_ERROR_FAILED ); } return( status ); } /* Process messages that compare object properties or clone the object */ if( message == MESSAGE_COMPARE ) { const MESSAGE_DATA *msgData = ( MESSAGE_DATA * ) messageDataPtr; assert( messageValue == MESSAGE_COMPARE_HASH || \ messageValue == MESSAGE_COMPARE_KEYID || \ messageValue == MESSAGE_COMPARE_KEYID_PGP || \ messageValue == MESSAGE_COMPARE_KEYID_OPENPGP ); switch( messageValue ) { case MESSAGE_COMPARE_HASH: /* If it's a hash or MAC context, compare the hash value */ if( !( contextInfoPtr->flags & CONTEXT_HASH_DONE ) ) return( CRYPT_ERROR_INCOMPLETE ); if( contextInfoPtr->type == CONTEXT_HASH && \ msgData->length == capabilityInfo->blockSize && \ !memcmp( msgData->data, contextInfoPtr->ctxHash->hash, msgData->length ) ) return( CRYPT_OK ); if( contextInfoPtr->type == CONTEXT_MAC && \ msgData->length == capabilityInfo->blockSize && \ !memcmp( msgData->data, contextInfoPtr->ctxMAC->mac, msgData->length ) ) return( CRYPT_OK ); break; case MESSAGE_COMPARE_KEYID: /* If it's a PKC context, compare the key ID */ if( contextInfoPtr->type == CONTEXT_PKC && \ msgData->length == KEYID_SIZE && \ !memcmp( msgData->data, contextInfoPtr->ctxPKC->keyID, KEYID_SIZE ) ) return( CRYPT_OK ); break; case MESSAGE_COMPARE_KEYID_PGP: /* If it's a PKC context, compare the PGP key ID */ if( contextInfoPtr->type == CONTEXT_PKC && \ msgData->length == PGP_KEYID_SIZE && \ !memcmp( msgData->data, contextInfoPtr->ctxPKC->pgpKeyID, PGP_KEYID_SIZE ) ) return( CRYPT_OK ); break; case MESSAGE_COMPARE_KEYID_OPENPGP: /* If it's a PKC context, compare the OpenPGP key ID */ if( contextInfoPtr->type == CONTEXT_PKC && \ contextInfoPtr->ctxPKC->openPgpKeyIDSet && \ msgData->length == PGP_KEYID_SIZE && \ !memcmp( msgData->data, contextInfoPtr->ctxPKC->openPgpKeyID, PGP_KEYID_SIZE ) ) return( CRYPT_OK ); break; default: assert( NOTREACHED ); /* Fall through to failure return */ } /* The comparison failed */ return( CRYPT_ERROR ); } /* Process messages that check a context */ if( message == MESSAGE_CHECK ) return( checkContext( contextInfoPtr, messageValue ) ); /* Process internal notification messages */ if( message == MESSAGE_CHANGENOTIFY ) { switch( messageValue ) { case MESSAGE_CHANGENOTIFY_STATUS: /* If the context is still busy and we're trying to reset its status from CRYPT_ERROR_TIMEOUT back to CRYPT_OK, set the abort flag to indicate that the operation which is keeping it busy should be cancelled, and return an error so that the busy status is maintained until the context has processed the abort */ if( !( contextInfoPtr->flags & CONTEXT_ASYNC_DONE ) ) { contextInfoPtr->flags |= CONTEXT_ASYNC_ABORT; return( CRYPT_ERROR_TIMEOUT ); } /* The context finished whatever it was doing, we're back to normal */ break; case MESSAGE_CHANGENOTIFY_STATE: /* State-change reflected down from the controlling cert object, this doesn't affect us */ break; case MESSAGE_CHANGENOTIFY_OBJHANDLE: assert( contextInfoPtr->type == CONTEXT_CONV || \ contextInfoPtr->type == CONTEXT_HASH || \ contextInfoPtr->type == CONTEXT_MAC ); assert( contextInfoPtr->objectHandle != \ *( ( int * ) messageDataPtr ) ); assert( contextInfoPtr->ctxConv != \ ( CONV_INFO * ) contextInfoPtr->storage ); /* We've been cloned, update the object handle and internal state pointers */ contextInfoPtr->objectHandle = *( ( int * ) messageDataPtr ); initContextStorage( contextInfoPtr, ( contextInfoPtr->type == CONTEXT_CONV ) ? \ sizeof( CONV_INFO ) : \ ( contextInfoPtr->type == CONTEXT_HASH ) ? \ sizeof( HASH_INFO ) : sizeof( MAC_INFO ) ); break; case MESSAGE_CHANGENOTIFY_OWNERHANDLE: /* The second stage of a cloning, update the owner handle */ contextInfoPtr->ownerHandle = *( ( int * ) messageDataPtr ); break; default: assert( NOTREACHED ); return( CRYPT_ERROR_FAILED ); } return( CRYPT_OK ); } /* Process object-specific messages */ if( message == MESSAGE_CTX_GENKEY ) { assert( contextInfoPtr->type == CONTEXT_CONV || \ contextInfoPtr->type == CONTEXT_MAC || contextInfoPtr->type == CONTEXT_PKC ); assert( needsKey( contextInfoPtr ) ); /* If it's a private key context or a persistent context, we need to have a key label set before we can continue */ if( ( ( contextInfoPtr->type == CONTEXT_PKC ) || \ ( contextInfoPtr->flags & CONTEXT_PERSISTENT ) ) && \ contextInfoPtr->labelSize <= 0 ) return( exitErrorNotInited( contextInfoPtr, CRYPT_CTXINFO_LABEL ) ); /* Generate a new key into the context */ status = contextInfoPtr->generateKeyFunction( contextInfoPtr, messageValue ); if( cryptStatusOK( status ) ) /* There's now a key loaded */ contextInfoPtr->flags |= CONTEXT_KEY_SET; else /* If the status is OK_SPECIAL, it's an async keygen that has begun, but that hasn't resulted in the context containing a key yet */ if( status == OK_SPECIAL ) status = CRYPT_OK; if( cryptStatusOK( status ) ) { static const int actionFlags = \ MK_ACTION_PERM( MESSAGE_CTX_ENCRYPT, ACTION_PERM_ALL ) | \ MK_ACTION_PERM( MESSAGE_CTX_DECRYPT, ACTION_PERM_ALL ) | \ MK_ACTION_PERM( MESSAGE_CTX_SIGN, ACTION_PERM_ALL ) | \ MK_ACTION_PERM( MESSAGE_CTX_SIGCHECK, ACTION_PERM_ALL ) | \ MK_ACTION_PERM( MESSAGE_CTX_HASH, ACTION_PERM_ALL ); /* There's a key loaded, disable further key generation. The kernel won't allow a keygen anyway once the object is in the high state, but taking this additional step can't hurt */ status = krnlSendMessage( contextInfoPtr->objectHandle, IMESSAGE_SETATTRIBUTE, ( void * ) &actionFlags, CRYPT_IATTRIBUTE_ACTIONPERMS ); } return( status ); } if( message == MESSAGE_CTX_GENIV ) { MESSAGE_DATA msgData; BYTE buffer[ CRYPT_MAX_IVSIZE + 8 ]; assert( contextInfoPtr->type == CONTEXT_CONV ); /* If it's not a conventional encryption context, or a mode that doesn't use an IV, the generate IV operation is meaningless */ if( !needsIV( contextInfoPtr->ctxConv->mode ) || \ isStreamCipher ( capabilityInfo->cryptAlgo ) ) return( CRYPT_ERROR_NOTAVAIL ); /* Generate a new IV and load it */ setMessageData( &msgData, buffer, CRYPT_MAX_IVSIZE ); status = krnlSendMessage( SYSTEM_OBJECT_HANDLE, IMESSAGE_GETATTRIBUTE_S, &msgData, CRYPT_IATTRIBUTE_RANDOM_NONCE ); if( cryptStatusOK( status ) ) status = capabilityInfo->initKeyParamsFunction( contextInfoPtr, buffer, CRYPT_USE_DEFAULT, CRYPT_MODE_NONE ); return( status ); } assert( NOTREACHED ); return( CRYPT_ERROR ); /* Get rid of compiler warning */ } /* Create an encryption context based on an encryption capability template. This is a common function called by devices to create a context once they've got the appropriate capability template */ int createContextFromCapability( CRYPT_CONTEXT *cryptContext, const CRYPT_USER cryptOwner, const CAPABILITY_INFO *capabilityInfoPtr, const int objectFlags ) { const CRYPT_ALGO_TYPE cryptAlgo = capabilityInfoPtr->cryptAlgo; const CONTEXT_TYPE contextType = \ ( ( cryptAlgo >= CRYPT_ALGO_FIRST_CONVENTIONAL ) && \ ( cryptAlgo <= CRYPT_ALGO_LAST_CONVENTIONAL ) ) ? CONTEXT_CONV : \ ( ( cryptAlgo >= CRYPT_ALGO_FIRST_PKC ) && \ ( cryptAlgo <= CRYPT_ALGO_LAST_PKC ) ) ? CONTEXT_PKC : \ ( ( cryptAlgo >= CRYPT_ALGO_FIRST_HASH ) && \ ( cryptAlgo <= CRYPT_ALGO_LAST_HASH ) ) ? CONTEXT_HASH : CONTEXT_MAC; CONTEXT_INFO *contextInfoPtr; BOOLEAN useSideChannelProtection; OBJECT_SUBTYPE subType; const int createFlags = objectFlags | \ ( needsSecureMemory( contextType ) ? \ CREATEOBJECT_FLAG_SECUREMALLOC : 0 ); int actionFlags = 0, actionPerms = ACTION_PERM_ALL; int storageSize, stateStorageSize = 0, status; assert( cryptAlgo > CRYPT_ALGO_NONE && cryptAlgo < CRYPT_ALGO_LAST_MAC ); /* Clear the return values */ *cryptContext = CRYPT_ERROR; /* Get general config information */ status = krnlSendMessage( cryptOwner, IMESSAGE_GETATTRIBUTE, &useSideChannelProtection, CRYPT_OPTION_MISC_SIDECHANNELPROTECTION ); if( cryptStatusError( status ) ) return( status ); /* Set up subtype-specific information */ switch( contextType ) { case CONTEXT_CONV: subType = SUBTYPE_CTX_CONV; storageSize = sizeof( CONV_INFO ); stateStorageSize = \ capabilityInfoPtr->getInfoFunction( CAPABILITY_INFO_STATESIZE, NULL, 0 ); if( capabilityInfoPtr->encryptFunction != NULL || \ capabilityInfoPtr->encryptCBCFunction != NULL || \ capabilityInfoPtr->encryptCFBFunction != NULL || \ capabilityInfoPtr->encryptOFBFunction != NULL ) actionFlags |= MK_ACTION_PERM( MESSAGE_CTX_ENCRYPT, ACTION_PERM_ALL ); if( capabilityInfoPtr->decryptFunction != NULL || \ capabilityInfoPtr->decryptCBCFunction != NULL || \ capabilityInfoPtr->decryptCFBFunction != NULL || \ capabilityInfoPtr->decryptOFBFunction != NULL ) actionFlags |= MK_ACTION_PERM( MESSAGE_CTX_DECRYPT, ACTION_PERM_ALL ); actionFlags |= MK_ACTION_PERM( MESSAGE_CTX_GENKEY, ACTION_PERM_ALL ); break; case CONTEXT_PKC: subType = SUBTYPE_CTX_PKC; storageSize = sizeof( PKC_INFO ); if( isDlpAlgo( cryptAlgo ) ) /* The DLP-based PKC's have somewhat specialised usage requirements so we don't allow direct access by users */ actionPerms = ACTION_PERM_NONE_EXTERNAL; if( capabilityInfoPtr->encryptFunction != NULL ) actionFlags |= MK_ACTION_PERM( MESSAGE_CTX_ENCRYPT, actionPerms ); if( capabilityInfoPtr->decryptFunction != NULL ) actionFlags |= MK_ACTION_PERM( MESSAGE_CTX_DECRYPT, actionPerms ); if( capabilityInfoPtr->signFunction != NULL ) actionFlags |= MK_ACTION_PERM( MESSAGE_CTX_SIGN, actionPerms ); if( capabilityInfoPtr->sigCheckFunction != NULL ) actionFlags |= MK_ACTION_PERM( MESSAGE_CTX_SIGCHECK, actionPerms ); actionFlags |= MK_ACTION_PERM( MESSAGE_CTX_GENKEY, ACTION_PERM_ALL ); break; case CONTEXT_HASH: subType = SUBTYPE_CTX_HASH; storageSize = sizeof( HASH_INFO ); stateStorageSize = \ capabilityInfoPtr->getInfoFunction( CAPABILITY_INFO_STATESIZE, NULL, 0 ); actionFlags = MK_ACTION_PERM( MESSAGE_CTX_HASH, ACTION_PERM_ALL ); break; case CONTEXT_MAC: subType = SUBTYPE_CTX_MAC; storageSize = sizeof( MAC_INFO ); stateStorageSize = \ capabilityInfoPtr->getInfoFunction( CAPABILITY_INFO_STATESIZE, NULL, 0 ); actionFlags = MK_ACTION_PERM( MESSAGE_CTX_HASH, ACTION_PERM_ALL ) | \ MK_ACTION_PERM( MESSAGE_CTX_GENKEY, ACTION_PERM_ALL ); break; default: assert( NOTREACHED ); return( CRYPT_ARGERROR_NUM1 ); } if( actionFlags == 0 ) { /* There are no actions enabled for this capability, bail out rather than creating an unusable context */ assert( NOTREACHED ); return( CRYPT_ERROR_NOTAVAIL ); } /* Create the context and initialise the variables in it */ status = krnlCreateObject( ( void ** ) &contextInfoPtr, sizeof( CONTEXT_INFO ) + storageSize + stateStorageSize, OBJECT_TYPE_CONTEXT, subType, createFlags, cryptOwner, actionFlags, contextMessageFunction ); if( cryptStatusError( status ) ) return( status ); *cryptContext = contextInfoPtr->objectHandle = status; contextInfoPtr->ownerHandle = cryptOwner; contextInfoPtr->capabilityInfo = capabilityInfoPtr; contextInfoPtr->type = contextType; #ifdef USE_DEVICES contextInfoPtr->deviceObject = \ contextInfoPtr->altDeviceObject = CRYPT_ERROR; #endif /* USE_DEVICES */ initContextStorage( contextInfoPtr, storageSize ); contextInfoPtr->storageSize = storageSize + stateStorageSize; if( useSideChannelProtection ) contextInfoPtr->flags |= CONTEXT_SIDECHANNELPROTECTION; if( contextInfoPtr->type == CONTEXT_PKC && \ !( objectFlags & CREATEOBJECT_FLAG_DUMMY ) ) initContextBignums( contextInfoPtr->ctxPKC, useSideChannelProtection ); if( contextInfoPtr->type == CONTEXT_CONV ) { /* Set the default encryption mode, which is always CBC if possible, and the corresponding en/decryption handler */ if( capabilityInfoPtr->encryptCBCFunction != NULL ) { contextInfoPtr->ctxConv->mode = CRYPT_MODE_CBC; contextInfoPtr->encryptFunction = \ capabilityInfoPtr->encryptCBCFunction; contextInfoPtr->decryptFunction = \ capabilityInfoPtr->decryptCBCFunction; } else /* There's no CBC mode available, fall back to increasingly sub-optimal choices of mode. For stream ciphers the only available mode is OFB so this isn't a problem, but for block ciphers it'll cause problems because most crypto protocols only allow CBC mode */ if( capabilityInfoPtr->encryptCFBFunction != NULL ) { contextInfoPtr->ctxConv->mode = CRYPT_MODE_CFB; contextInfoPtr->encryptFunction = \ capabilityInfoPtr->encryptCFBFunction; contextInfoPtr->decryptFunction = \ capabilityInfoPtr->decryptCFBFunction; } else if( capabilityInfoPtr->encryptOFBFunction != NULL ) { contextInfoPtr->ctxConv->mode = CRYPT_MODE_OFB; contextInfoPtr->encryptFunction = \ capabilityInfoPtr->encryptOFBFunction; contextInfoPtr->decryptFunction = \ capabilityInfoPtr->decryptOFBFunction; } else { contextInfoPtr->ctxConv->mode = CRYPT_MODE_ECB; contextInfoPtr->encryptFunction = \ capabilityInfoPtr->encryptFunction; contextInfoPtr->decryptFunction = \ capabilityInfoPtr->decryptFunction; } } else { /* There's only one possible en/decryption handler */ contextInfoPtr->encryptFunction = capabilityInfoPtr->encryptFunction; contextInfoPtr->decryptFunction = capabilityInfoPtr->decryptFunction; } if( contextInfoPtr->type != CONTEXT_HASH ) /* Set up the key handling functions */ initKeyHandling( contextInfoPtr ); if( contextInfoPtr->type == CONTEXT_PKC ) { /* Set up the key read/write functions */ initKeyRead( contextInfoPtr ); initKeyWrite( contextInfoPtr ); } assert( contextInfoPtr->type == CONTEXT_HASH || \ ( contextInfoPtr->loadKeyFunction != NULL && \ contextInfoPtr->generateKeyFunction != NULL ) ); assert( ( cryptAlgo == CRYPT_ALGO_DSA || \ cryptAlgo == CRYPT_ALGO_ECDSA ) || \ ( contextInfoPtr->encryptFunction != NULL && \ contextInfoPtr->decryptFunction != NULL ) ); assert( contextInfoPtr->type != CONTEXT_PKC || \ ( contextInfoPtr->ctxPKC->writePublicKeyFunction != NULL && \ contextInfoPtr->ctxPKC->writePrivateKeyFunction != NULL && \ contextInfoPtr->ctxPKC->readPublicKeyFunction != NULL && \ contextInfoPtr->ctxPKC->readPrivateKeyFunction != NULL ) ); /* If this is a dummy object, remember that it's just a placeholder, with actions handled externally. If it's a persistent object (backed by a permanent key in a crypto device), record this */ if( objectFlags & CREATEOBJECT_FLAG_DUMMY ) contextInfoPtr->flags |= CONTEXT_DUMMY; if( objectFlags & CREATEOBJECT_FLAG_PERSISTENT ) contextInfoPtr->flags |= CONTEXT_PERSISTENT; /* We've finished setting up the object type-specific info, tell the kernel that the object is ready for use */ status = krnlSendMessage( *cryptContext, IMESSAGE_SETATTRIBUTE, MESSAGE_VALUE_OK, CRYPT_IATTRIBUTE_STATUS ); if( cryptStatusOK( status ) && contextInfoPtr->type == CONTEXT_HASH ) { /* If it's a hash context there's no explicit keygen or load so we need to send an "object initialised" message to get the kernel to move it into the high state. If this isn't done, any attempt to use the object will be blocked */ status = krnlSendMessage( *cryptContext, IMESSAGE_SETATTRIBUTE, MESSAGE_VALUE_UNUSED, CRYPT_IATTRIBUTE_INITIALISED ); } if( cryptStatusError( status ) ) { *cryptContext = CRYPT_ERROR; return( status ); } return( CRYPT_OK ); } /* Create an encryption context object */ int createContext( MESSAGE_CREATEOBJECT_INFO *createInfo, const void *auxDataPtr, const int auxValue ) { CRYPT_CONTEXT iCryptContext; const CAPABILITY_INFO FAR_BSS *capabilityInfoPtr; int status; assert( auxDataPtr != NULL ); assert( createInfo->arg1 > CRYPT_ALGO_NONE && \ createInfo->arg1 < CRYPT_ALGO_LAST ); /* Find the capability corresponding to the algorithm */ capabilityInfoPtr = findCapabilityInfo( auxDataPtr, createInfo->arg1 ); if( capabilityInfoPtr == NULL ) return( CRYPT_ERROR_NOTAVAIL ); /* Pass the call on to the lower-level create function */ status = createContextFromCapability( &iCryptContext, createInfo->cryptOwner, capabilityInfoPtr, auxValue ); if( cryptStatusOK( status ) ) createInfo->cryptHandle = iCryptContext; return( status ); }