📄 cms_env.c
字号:
/****************************************************************************
* *
* cryptlib CMS Enveloping Routines *
* Copyright Peter Gutmann 1996-2008 *
* *
****************************************************************************/
#if defined( INC_ALL )
#include "envelope.h"
#include "asn1.h"
#include "asn1_ext.h"
#else
#include "envelope/envelope.h"
#include "misc/asn1.h"
#include "misc/asn1_ext.h"
#endif /* Compiler-specific includes */
/* Determine the size of the envelope payload after PKCS #5 block padding if
necessary. This isn't just the size rounded up to the nearest multiple of
the block size since if the size is already a multiple of the block size,
it expands by another block, so we make the payload look one byte longer
before rounding to the block size to ensure the one-block expansion */
#define paddedSize( size, blockSize ) \
( ( blockSize > 1 ) ? roundUp( size + 1, blockSize ) : size )
#ifdef USE_ENVELOPES
/****************************************************************************
* *
* Utility Functions *
* *
****************************************************************************/
/* Sanity-check the envelope state */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static BOOLEAN sanityCheck( const ENVELOPE_INFO *envelopeInfoPtr )
{
assert( isReadPtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
/* Make sure that general envelope state is in order */
if( envelopeInfoPtr->flags & ENVELOPE_ISDEENVELOPE )
return( FALSE );
if( envelopeInfoPtr->envState < ENVSTATE_NONE || \
envelopeInfoPtr->envState >= ENVSTATE_LAST )
return( FALSE );
/* Make sure that the buffer position is within bounds */
if( envelopeInfoPtr->buffer == NULL || \
envelopeInfoPtr->bufPos < 0 || \
envelopeInfoPtr->bufPos > envelopeInfoPtr->bufSize || \
envelopeInfoPtr->bufSize < MIN_BUFFER_SIZE || \
envelopeInfoPtr->bufSize >= MAX_INTLENGTH )
return( FALSE );
/* If the auxBuffer isn't being used, make sure that all values related
to it are clear */
if( envelopeInfoPtr->auxBuffer == NULL )
{
if( envelopeInfoPtr->auxBufPos != 0 || \
envelopeInfoPtr->auxBufSize != 0 )
return( FALSE );
return( TRUE );
}
/* Make sure that the auxBuffer position is within bounds */
if( envelopeInfoPtr->auxBufPos < 0 || \
envelopeInfoPtr->auxBufPos > envelopeInfoPtr->auxBufSize || \
envelopeInfoPtr->auxBufSize < 0 || \
envelopeInfoPtr->auxBufSize >= MAX_INTLENGTH )
return( FALSE );
return( TRUE );
}
/* Check that a requested algorithm type is valid with enveloped data */
CHECK_RETVAL_BOOL \
BOOLEAN cmsCheckAlgo( IN_ALGO const CRYPT_ALGO_TYPE cryptAlgo,
IN_MODE_OPT const CRYPT_MODE_TYPE cryptMode )
{
REQUIRES_B( cryptAlgo > CRYPT_ALGO_NONE && \
cryptAlgo < CRYPT_ALGO_LAST );
REQUIRES_B( ( cryptMode == CRYPT_MODE_NONE ) || \
( cryptMode > CRYPT_MODE_NONE && \
cryptMode < CRYPT_MODE_LAST ) );
return( checkAlgoID( cryptAlgo, cryptMode ) );
}
/* Get the OID for a CMS content type. If no type is explicitly given, we
assume raw data */
static const OID_INFO FAR_BSS contentOIDs[] = {
{ OID_CMS_DATA, CRYPT_CONTENT_DATA },
{ OID_CMS_SIGNEDDATA, CRYPT_CONTENT_SIGNEDDATA },
{ OID_CMS_ENVELOPEDDATA, CRYPT_CONTENT_ENVELOPEDDATA },
{ MKOID( "\x06\x09\x2A\x86\x48\x86\xF7\x0D\x01\x07\x04" ), CRYPT_CONTENT_SIGNEDANDENVELOPEDDATA },
{ OID_CMS_DIGESTEDDATA, CRYPT_CONTENT_DIGESTEDDATA },
{ OID_CMS_ENCRYPTEDDATA, CRYPT_CONTENT_ENCRYPTEDDATA },
{ OID_CMS_COMPRESSEDDATA, CRYPT_CONTENT_COMPRESSEDDATA },
{ OID_CMS_AUTHDATA, CRYPT_CONTENT_AUTHDATA },
{ OID_CMS_AUTHENVDATA, CRYPT_CONTENT_AUTHENVDATA },
{ OID_CMS_TSTOKEN, CRYPT_CONTENT_TSTINFO },
{ OID_MS_SPCINDIRECTDATACONTEXT, CRYPT_CONTENT_SPCINDIRECTDATACONTEXT },
{ OID_CRYPTLIB_RTCSREQ, CRYPT_CONTENT_RTCSREQUEST },
{ OID_CRYPTLIB_RTCSRESP, CRYPT_CONTENT_RTCSRESPONSE },
{ OID_CRYPTLIB_RTCSRESP_EXT, CRYPT_CONTENT_RTCSRESPONSE_EXT },
{ MKOID( "\x06\x06\x67\x81\x08\x01\x01\x01" ), CRYPT_CONTENT_MRTD },
{ NULL, 0 }, { NULL, 0 }
};
CHECK_RETVAL_PTR \
static const BYTE *getContentOID( IN_ENUM( CRYPT_CONTENT ) \
const CRYPT_CONTENT_TYPE contentType )
{
int i;
REQUIRES_N( contentType > CRYPT_CONTENT_NONE && \
contentType < CRYPT_CONTENT_LAST );
for( i = 0; contentOIDs[ i ].oid != NULL && \
i < FAILSAFE_ARRAYSIZE( contentOIDs, OID_INFO ); i++ )
{
if( contentOIDs[ i ].selectionID == contentType )
return( contentOIDs[ i ].oid );
}
retIntError_Null();
}
/* Copy as much post-data state information (i.e. signatures) from the
auxiliary buffer to the main buffer as possible */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int copyFromAuxBuffer( INOUT ENVELOPE_INFO *envelopeInfoPtr )
{
int bytesCopied, dataLeft;
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
/* Copy as much of the signature data as we can across */
bytesCopied = min( envelopeInfoPtr->bufSize - envelopeInfoPtr->bufPos,
envelopeInfoPtr->auxBufPos );
REQUIRES( bytesCopied > 0 && \
envelopeInfoPtr->bufPos + \
bytesCopied <= envelopeInfoPtr->bufSize );
memcpy( envelopeInfoPtr->buffer + envelopeInfoPtr->bufPos,
envelopeInfoPtr->auxBuffer, bytesCopied );
envelopeInfoPtr->bufPos += bytesCopied;
/* Since we're in the post-data state any necessary payload data
segmentation has been completed, however, the caller can't copy out
any post-payload data because it's past the end-of-segment position.
In order to allow the buffer to be emptied to make room for new data
from the auxBuffer we set the end-of-segment position to the end of
the new data */
envelopeInfoPtr->segmentDataEnd = envelopeInfoPtr->bufPos;
/* If there's anything left move it down in the buffer */
dataLeft = envelopeInfoPtr->auxBufPos - bytesCopied;
if( dataLeft > 0 )
{
memmove( envelopeInfoPtr->auxBuffer, \
envelopeInfoPtr->auxBuffer + bytesCopied, dataLeft );
}
envelopeInfoPtr->auxBufPos = dataLeft;
ENSURES( dataLeft >= 0 );
return( ( dataLeft > 0 ) ? CRYPT_ERROR_OVERFLOW : CRYPT_OK );
}
/* Write one or more indefinite-length end-of-contents indicators */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int writeEOCs( INOUT ENVELOPE_INFO *envelopeInfoPtr, const int count )
{
static const BYTE indefEOC[ 16 ] = \
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
const int dataLeft = envelopeInfoPtr->bufSize - envelopeInfoPtr->bufPos;
const int eocLength = count * 2;
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
REQUIRES( eocLength >= 2 && eocLength <= 16 ); /* Count = 1...8 */
if( dataLeft < eocLength )
return( CRYPT_ERROR_OVERFLOW );
memcpy( envelopeInfoPtr->buffer + envelopeInfoPtr->bufPos, indefEOC,
eocLength );
envelopeInfoPtr->bufPos += eocLength;
return( CRYPT_OK );
}
/****************************************************************************
* *
* Emit Content-Specific Headers *
* *
****************************************************************************/
/* Write the header fields that encapsulate any enveloped data:
SignedData/DigestedData */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int writeSignedDataHeader( INOUT STREAM *stream,
const ENVELOPE_INFO *envelopeInfoPtr,
const BOOLEAN isSignedData )
{
const BYTE *contentOID = getContentOID( envelopeInfoPtr->contentType );
ACTION_LIST *actionListPtr;
long dataSize;
int hashActionSize = 0, iterationCount, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
REQUIRES_S( contentOID != NULL );
/* Determine the size of the hash actions */
for( actionListPtr = envelopeInfoPtr->actionList, iterationCount = 0;
actionListPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MED;
actionListPtr = actionListPtr->next, iterationCount++ )
{
const int actionSize = \
sizeofContextAlgoID( actionListPtr->iCryptHandle,
CRYPT_ALGO_NONE );
if( cryptStatusError( actionSize ) )
return( actionSize );
hashActionSize += actionSize;
}
ENSURES_S( iterationCount < FAILSAFE_ITERATIONS_MED );
/* Determine the size of the SignedData/DigestedData */
if( envelopeInfoPtr->payloadSize == CRYPT_UNUSED || \
( envelopeInfoPtr->dataFlags & ENVDATA_HASINDEFTRAILER ) )
dataSize = CRYPT_UNUSED;
else
{
/* Determine the size of the content OID + content */
dataSize = ( envelopeInfoPtr->payloadSize > 0 ) ? \
sizeofObject( sizeofObject( envelopeInfoPtr->payloadSize ) ) : 0;
dataSize = sizeofObject( sizeofOID( contentOID ) + dataSize );
/* Determine the size of the version, hash algoID, content,
certificate chain, and signatures */
dataSize = sizeofShortInteger( 1 ) + sizeofObject( hashActionSize ) + \
dataSize + envelopeInfoPtr->extraDataSize + \
sizeofObject( envelopeInfoPtr->signActionSize );
}
ENSURES_S( dataSize == CRYPT_UNUSED || \
( dataSize >= MIN_CRYPT_OBJECTSIZE && \
dataSize < MAX_INTLENGTH ) );
/* Write the SignedData/DigestedData header, version number, and SET OF
DigestInfo */
if( isSignedData )
{
status = writeCMSheader( stream, OID_CMS_SIGNEDDATA,
sizeofOID( OID_CMS_SIGNEDDATA ),
dataSize, FALSE );
}
else
{
status = writeCMSheader( stream, OID_CMS_DIGESTEDDATA,
sizeofOID( OID_CMS_DIGESTEDDATA ),
dataSize, FALSE );
}
if( cryptStatusError( status ) )
return( status );
writeShortInteger( stream, 1, DEFAULT_TAG );
writeSet( stream, hashActionSize );
for( actionListPtr = envelopeInfoPtr->actionList, iterationCount = 0;
actionListPtr != NULL && iterationCount < FAILSAFE_ITERATIONS_MED;
actionListPtr = actionListPtr->next, iterationCount++ )
{
status = writeContextAlgoID( stream, actionListPtr->iCryptHandle,
CRYPT_ALGO_NONE );
if( cryptStatusError( status ) )
return( status );
}
ENSURES_S( iterationCount < FAILSAFE_ITERATIONS_MED );
/* Write the inner Data header */
return( writeCMSheader( stream, contentOID, sizeofOID( contentOID ),
envelopeInfoPtr->payloadSize, TRUE ) );
}
/* EncryptedContentInfo contained within EnvelopedData. This may also be
Authenticated or AuthEnc data so the encryption context can be
CRYPT_UNUSED */
CHECK_RETVAL STDC_NONNULL_ARG( ( 3 ) ) \
static int getBlockedPayloadSize( IN_LENGTH_INDEF const long payloadSize,
IN_LENGTH_IV const int blockSize,
OUT_LENGTH_INDEF long *blockedPayloadSize )
{
long blockedSize;
assert( isWritePtr( blockedPayloadSize, sizeof( long ) ) );
REQUIRES( payloadSize == CRYPT_UNUSED || \
( payloadSize > 0 && payloadSize < MAX_INTLENGTH ) );
REQUIRES( blockSize > 1 && blockSize <= CRYPT_MAX_IVSIZE );
/* Clear return value */
*blockedPayloadSize = 0;
/* If it's an indefinite length payload the blocked size is also of
indefinite length */
if( payloadSize == CRYPT_UNUSED )
{
*blockedPayloadSize = CRYPT_UNUSED;
return( CRYPT_OK );
}
/* Calculate the size of the payload after encryption blocking */
blockedSize = paddedSize( payloadSize, blockSize );
*blockedPayloadSize = blockedSize;
ENSURES( blockedSize >= 8 && \
blockedSize <= payloadSize + CRYPT_MAX_IVSIZE );
return( CRYPT_OK );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int writeEncryptedContentHeader( INOUT STREAM *stream,
IN_BUFFER( contentOIDlength ) const BYTE *contentOID,
IN_LENGTH_OID const int contentOIDlength,
IN_HANDLE const CRYPT_CONTEXT iCryptContext,
IN_LENGTH_INDEF const long payloadSize,
IN_LENGTH_IV const long blockSize )
{
long blockedPayloadSize;
int status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isReadPtr( contentOID, contentOIDlength ) );
REQUIRES( isHandleRangeValid( iCryptContext ) || \
iCryptContext == CRYPT_UNUSED );
REQUIRES( payloadSize == CRYPT_UNUSED || \
( payloadSize > 0 && payloadSize < MAX_INTLENGTH ) );
REQUIRES( blockSize > 1 && blockSize <= CRYPT_MAX_IVSIZE );
status = getBlockedPayloadSize( payloadSize, blockSize,
&blockedPayloadSize );
if( cryptStatusError( status ) )
return( status );
return( writeCMSencrHeader( stream, contentOID, contentOIDlength,
blockedPayloadSize, iCryptContext ) );
}
/* EncryptedData, EnvelopedData */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4, 5 ) ) \
static int getEncrypedContentSize( const ENVELOPE_INFO *envelopeInfoPtr,
IN_BUFFER( contentOIDlength ) const BYTE *contentOID,
IN_LENGTH_OID const int contentOIDlength,
OUT_LENGTH_INDEF long *blockedPayloadSize,
OUT_LENGTH_Z long *encrContentInfoSize )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -