📄 deenvel.c
字号:
/****************************************************************************
* *
* cryptlib De-enveloping Routines *
* Copyright Peter Gutmann 1996-2002 *
* *
****************************************************************************/
#include <stdlib.h>
#include <string.h>
#if defined( INC_ALL )
#include "asn1.h"
#include "asn1objs.h"
#include "asn1oid.h"
#include "envelope.h"
#elif defined( INC_CHILD )
#include "../keymgmt/asn1.h"
#include "../keymgmt/asn1objs.h"
#include "../keymgmt/asn1oid.h"
#include "envelope.h"
#else
#include "keymgmt/asn1.h"
#include "keymgmt/asn1objs.h"
#include "keymgmt/asn1oid.h"
#include "envelope/envelope.h"
#endif /* Compiler-specific includes */
/* Prototypes for functions in asn1objs.c */
int queryObject( STREAM *stream, QUERY_INFO *queryInfo );
/* Prototypes for functions in resource.c */
void initContentListItem( CONTENT_LIST *contentListItem );
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* OID information used to read enveloped data */
static const OID_SELECTION envelopeOIDselection[] = {
{ OID_CMS_DATA, CRYPT_UNUSED, CRYPT_UNUSED, ACTION_NONE },
{ OID_CMS_SIGNEDDATA, 0, 3, ACTION_SIGN },
{ OID_CMS_ENVELOPEDDATA, 0, 2, ACTION_KEYEXCHANGE },
{ OID_CMS_DIGESTEDDATA, 0, 2, ACTION_HASH },
{ OID_CMS_ENCRYPTEDDATA, 0, 2, ACTION_CRYPT },
{ OID_CMS_COMPRESSEDDATA, 0, 0, ACTION_COMPRESS },
{ NULL, 0, 0, 0 }
};
static const OID_SELECTION nestedContentOIDselection[] = {
{ OID_CMS_DATA, CRYPT_UNUSED, CRYPT_UNUSED, CRYPT_CONTENT_DATA },
{ OID_CMS_SIGNEDDATA, CRYPT_UNUSED, CRYPT_UNUSED, CRYPT_CONTENT_SIGNEDDATA },
{ OID_CMS_ENVELOPEDDATA, CRYPT_UNUSED, CRYPT_UNUSED, CRYPT_CONTENT_ENVELOPEDDATA },
{ OID_CMS_ENCRYPTEDDATA, CRYPT_UNUSED, CRYPT_UNUSED, CRYPT_CONTENT_ENCRYPTEDDATA },
{ OID_CMS_COMPRESSEDDATA, CRYPT_UNUSED, CRYPT_UNUSED, CRYPT_CONTENT_COMPRESSEDDATA },
{ NULL, 0, 0, 0 }
};
/* Add information about an object to an envelope's content information list */
static int addContentListItem( STREAM *stream, ENVELOPE_INFO *envelopeInfoPtr )
{
QUERY_INFO queryInfo;
CONTENT_LIST *contentListItem;
void *object, *originalObjectPtr = sMemBufPtr( stream );
int status;
/* Find the size of the object, allocate a buffer for it, and copy it
across */
status = queryObject( stream, &queryInfo );
if( cryptStatusError( status ) )
return( status );
if( queryInfo.type == CRYPT_OBJECT_NONE )
{
/* It's a valid but unrecognised object type (a new RecipientInfo
type which was added after this version of cryptlib was released),
skip it and continue (if there are no recognised RecipientInfo
types, the code will automatically fall back to asking the user
for a raw session key). Alternatively, we could just add it to
the content list as an unrecognised object type, but this would
lead to confusion for the caller when non-object-types appear
when they query the current component */
sSkip( stream, queryInfo.size );
return( ( int ) queryInfo.size );
}
if( ( object = malloc( ( size_t ) queryInfo.size ) ) == NULL )
return( CRYPT_ERROR_MEMORY );
sread( stream, object, ( int ) queryInfo.size );
/* Allocate memory for the new content list item and copy information on
the item across */
contentListItem = createContentListItem( queryInfo.formatType,
object, ( int ) queryInfo.size );
if( contentListItem == NULL )
{
if( stream != NULL )
free( object );
return( CRYPT_ERROR_MEMORY );
}
if( queryInfo.type == CRYPT_OBJECT_PKCENCRYPTED_KEY || \
queryInfo.type == CRYPT_OBJECT_SIGNATURE )
{
/* Remember details of the enveloping info we require to continue */
if( queryInfo.type == CRYPT_OBJECT_PKCENCRYPTED_KEY )
contentListItem->envInfo = CRYPT_ENVINFO_PRIVATEKEY;
else
{
contentListItem->envInfo = CRYPT_ENVINFO_SIGNATURE;
contentListItem->hashAlgo = queryInfo.hashAlgo;
}
if( queryInfo.formatType == CRYPT_FORMAT_CMS )
{
contentListItem->issuerAndSerialNumber = \
( BYTE * ) contentListItem->object + \
( ( BYTE * ) queryInfo.iAndSStart - ( BYTE * ) originalObjectPtr );
contentListItem->issuerAndSerialNumberSize = queryInfo.iAndSLength;
}
else
{
memcpy( contentListItem->keyID, queryInfo.keyID,
queryInfo.keyIDlength );
contentListItem->keyIDsize = queryInfo.keyIDlength;
}
contentListItem->payload = \
( BYTE * ) contentListItem->object + \
( ( BYTE * ) queryInfo.dataStart - ( BYTE * ) originalObjectPtr );
contentListItem->payloadSize = queryInfo.dataLength;
}
if( queryInfo.type == CRYPT_OBJECT_ENCRYPTED_KEY )
{
/* Remember details of the enveloping info we require to continue */
if( queryInfo.keySetupAlgo != CRYPT_ALGO_NONE )
{
contentListItem->envInfo = CRYPT_ENVINFO_PASSWORD;
contentListItem->keySetupIterations = queryInfo.keySetupIterations;
memcpy( contentListItem->saltOrIV, queryInfo.salt,
queryInfo.saltLength );
contentListItem->saltOrIVsize = queryInfo.saltLength;
}
else
contentListItem->envInfo = CRYPT_ENVINFO_KEY;
contentListItem->cryptAlgo = queryInfo.cryptAlgo;
contentListItem->cryptMode = queryInfo.cryptMode;
contentListItem->payload = \
( BYTE * ) contentListItem->object + \
( ( BYTE * ) queryInfo.dataStart - ( BYTE * ) originalObjectPtr );
contentListItem->payloadSize = queryInfo.dataLength;
}
appendContentListItem( envelopeInfoPtr, contentListItem );
return( ( int ) queryInfo.size );
}
/****************************************************************************
* *
* Process Envelope Preamble/Postamble *
* *
****************************************************************************/
/* Process the non-data portions of an envelope. This is a complex event-
driven state machine, but instead of reading along a (hypothetical
Turing-machine) tape, someone has taken the tape and cut it into bits and
keeps feeding them to us and saying "See what you can do with this" (and
occasionally "Where's the bloody spoons?"). The following code implements
this state machine.
Encr. with key exchange: SET_ENCR -> ENCR -> ENCRCONTENT -> DATA
Encr. with key agreement: "
Encr.: ENCRCONTENT -> DATA
Signed: SET_HASH -> HASH -> CONTENT -> DATA */
static int processPreamble( ENVELOPE_INFO *envelopeInfoPtr )
{
DEENV_STATE state = envelopeInfoPtr->deenvState;
STREAM stream;
int length, streamPos = 0, status = CRYPT_OK;
sMemConnect( &stream, envelopeInfoPtr->buffer, envelopeInfoPtr->bufPos );
/* If we haven't started doing anything yet, try and read the outer
header fields */
if( state == DEENVSTATE_NONE )
{
BYTE algoIDbuffer[ MAX_OID_SIZE ];
int algoIDlength;
/* Read the outer CMS header */
status = readCMSheader( &stream, envelopeOIDselection,
&envelopeInfoPtr->payloadSize, FALSE );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
/* Determine the next state to continue processing */
switch( status )
{
case ACTION_KEYEXCHANGE:
envelopeInfoPtr->usage = ACTION_CRYPT;
if( peekTag( &stream ) != BER_SET )
{
/* There may be key agreement data present, try and read
the start of the [0] IMPLICIT SEQUENCE { [0] SET OF
Certificate } */
readConstructed( &stream, NULL, 0 );
status = readConstructed( &stream, NULL, 0 );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
}
state = DEENVSTATE_SET_ENCR;
break;
case ACTION_CRYPT:
envelopeInfoPtr->usage = ACTION_CRYPT;
state = DEENVSTATE_ENCRCONTENT;
break;
case ACTION_SIGN:
envelopeInfoPtr->usage = ACTION_SIGN;
state = DEENVSTATE_SET_HASH;
break;
case ACTION_COMPRESS:
/* With compressed data all we need to do is check that the
fixed AlgorithmIdentifier is present and set up the
decompression stream, after which we go straight to the
content */
envelopeInfoPtr->usage = ACTION_COMPRESS;
status = readRawObject( &stream, algoIDbuffer, &algoIDlength,
MAX_OID_SIZE, BER_SEQUENCE );
if( !cryptStatusError( status ) && \
( algoIDlength != sizeofOID( ALGOID_CMS_ZLIB ) || \
memcmp( algoIDbuffer, ALGOID_CMS_ZLIB, algoIDlength ) ) )
status = CRYPT_ERROR_BADDATA;
#ifdef NO_COMPRESSION
status = CRYPT_ERROR_NOTAVAIL;
#else
else
if( inflateInit( &envelopeInfoPtr->zStream ) == Z_OK )
envelopeInfoPtr->zStreamInited = TRUE;
else
status = CRYPT_ERROR_MEMORY;
#endif /* NO_COMPRESSION */
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
return( status );
}
state = DEENVSTATE_CONTENT;
break;
case ACTION_NONE:
/* Since we go straight to the data payload there's no nested
content type, so we explicitly set it to data */
envelopeInfoPtr->contentType = CRYPT_CONTENT_DATA;
state = DEENVSTATE_DATA;
break;
default:
assert( NOTREACHED );
}
/* Remember how far we got */
streamPos = ( int ) stell( &stream );
}
/* Keep consuming information until we run out of input or reach the data
payload */
while( state != DEENVSTATE_DONE )
{
/* Check that various values are within range. They can go out of
range if the header is corrupted */
if( envelopeInfoPtr->hdrSetLength < 0 && \
envelopeInfoPtr->hdrSetLength != CRYPT_UNUSED )
{
status = CRYPT_ERROR_BADDATA;
break;
}
/* Read the start of the cert set from a keyAgreement's [0] IMPLICIT
SEQUENCE { [0] SET OF Certificate } */
if( state == DEENVSTATE_SET_ENCR )
{
/* Read the SET tag and length */
status = readSet( &stream, &length );
if( cryptStatusError( status ) )
break;
/* Remember where we are and move on to the next state. Some
implementations use the indefinite-length encoding for this so
if there's no length given we have to look for the EOC after
each entry read */
streamPos = ( int ) stell( &stream );
envelopeInfoPtr->hdrSetLength = ( length ) ? length : CRYPT_UNUSED;
state = DEENVSTATE_ENCR;
}
/* Read and remember a key exchange object from an EncryptionKeyInfo
record */
if( state == DEENVSTATE_ENCR )
{
/* Add the object to the content information list */
status = addContentListItem( &stream, envelopeInfoPtr );
if( cryptStatusError( status ) )
break;
/* Remember where we are and move on to the next state if
necessary */
streamPos = ( int ) stell( &stream );
if( envelopeInfoPtr->hdrSetLength != CRYPT_UNUSED )
{
envelopeInfoPtr->hdrSetLength -= status;
if( envelopeInfoPtr->hdrSetLength <= 0 )
state = DEENVSTATE_ENCRCONTENT;
}
else
if( checkEOC( &stream ) )
state = DEENVSTATE_ENCRCONTENT;
}
/* Read the encrypted content information */
if( state == DEENVSTATE_ENCRCONTENT )
{
QUERY_INFO queryInfo;
CONTENT_LIST contentListItem;
/* Read the encrypted content header */
initContentListItem( &contentListItem );
contentListItem.envInfo = CRYPT_ENVINFO_SESSIONKEY;
status = readCMSencrHeader( &stream, nestedContentOIDselection,
NULL, &queryInfo );
if( cryptStatusError( status ) )
break;
envelopeInfoPtr->contentType = status;
envelopeInfoPtr->payloadSize = queryInfo.size;
contentListItem.cryptAlgo = queryInfo.cryptAlgo;
contentListItem.cryptMode = queryInfo.cryptMode;
memcpy( contentListItem.saltOrIV, queryInfo.iv,
queryInfo.ivLength );
contentListItem.saltOrIVsize = queryInfo.ivLength;
/* We've reached encrypted data, we can't go any further until we
can either recover the session key from a key exchange object
or are fed the session key directly */
if( envelopeInfoPtr->actionList == NULL )
{
CONTENT_LIST *contentListItemPtr;
/* Remember what we need for later and exit */
if( ( contentListItemPtr = malloc( sizeof( CONTENT_LIST ) ) ) == NULL )
{
status = CRYPT_ERROR_MEMORY;
break;
}
memcpy( contentListItemPtr, &contentListItem,
sizeof( CONTENT_LIST ) );
appendContentListItem( envelopeInfoPtr, contentListItemPtr );
streamPos = ( int ) stell( &stream );
state = DEENVSTATE_DATA;
status = CRYPT_ENVELOPE_RESOURCE;
break;
}
assert( envelopeInfoPtr->actionList->action == ACTION_CRYPT );
/* If the session key was recovered from a key exchange action but
we ran out of input data before we could read the
encryptedContent info, it'll be present in the action list so
we use it to set things up for the decryption. This can only
happen if the caller pushes in just enough data to get past the
key exchange actions but not enough to recover the
encryptedContent info and then pushes in a key exchange action
in response to the CRYPT_ERROR_UNDERFLOW error */
status = initEnvelopeEncryption( envelopeInfoPtr,
envelopeInfoPtr->actionList->iCryptHandle,
contentListItem.cryptAlgo, contentListItem.cryptMode,
contentListItem.saltOrIV, contentListItem.saltOrIVsize,
FALSE );
if( cryptStatusError( status ) )
break;
/* Remember where we are and move on to the next state */
streamPos = ( int ) stell( &stream );
state = DEENVSTATE_DATA;
}
/* Read the start of the SET OF DigestAlgorithmIdentifier */
if( state == DEENVSTATE_SET_HASH )
{
/* Read the SET tag and length */
status = readSet( &stream, &length );
if( cryptStatusError( status ) )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -