📄 cms_denv.c
字号:
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int processEOCTrailer( INOUT STREAM *stream,
IN_ENUM_OPT( ACTION ) const ACTION_TYPE usage )
{
const int noEOCs = ( usage == ACTION_NONE ) ? 2 : \
( usage == ACTION_SIGN || \
usage == ACTION_MAC ) ? 3 : \
( usage == ACTION_COMPRESS ) ? 5 : 4;
int i;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
REQUIRES( usage >= ACTION_NONE && usage < ACTION_LAST );
/* Consume any EOCs up to the maximum amount possible. In theory we
could be rather liberal with trailing EOCs since it's not really
necessary for the caller to push in every last one, however if we
assume that seeing at least one EOC is enough to signal the end of
all content this can lead to problems if adding the EOCs occurs
over a pushData boundary. What can happen here is that the code will
see the start of the string of EOCs on the first push, record the
end-of-data-reached state, and then report a CRYPT_ERROR_COMPLETE
when the remainder of the string of EOCs are pushed the next time
round. To avoid this problem we have to be pedantic and require
that callers push all EOCs */
for( i = 0; i < noEOCs; i++ )
{
const int value = checkEOC( stream );
if( cryptStatusError( value ) )
return( value );
if( value == FALSE )
return( CRYPT_ERROR_BADDATA );
}
return( CRYPT_OK );
}
/****************************************************************************
* *
* 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.: ENCRCONTENT -> DATA
Signed: SET_HASH -> HASH -> CONTENT -> DATA
MACd: SET_ENCR -> ENCR -> HASH -> CONTENT -> DATA */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int processPreamble( INOUT ENVELOPE_INFO *envelopeInfoPtr )
{
DEENV_STATE state = envelopeInfoPtr->deenvState;
STREAM stream;
int remainder, streamPos = 0, iterationCount = 0, status = CRYPT_OK;
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
REQUIRES( sanityCheck( envelopeInfoPtr ) );
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 )
{
status = processEnvelopeHeader( envelopeInfoPtr, &stream, &state );
if( cryptStatusError( status ) )
{
sMemDisconnect( &stream );
retExt( status,
( status, ENVELOPE_ERRINFO,
"Invalid CMS envelope header" ) );
}
/* Remember how far we got */
streamPos = stell( &stream );
}
/* Keep consuming information until we either run out of input or reach
the data payload. Although in theory we should really use
FAILSAFE_ITERATIONS_MED for this loop, in practice we have to use
FAILSAFE_ITERATIONS_LARGE because it's possible to generate S/MIME
messages with large numbers of recipients for mailing lists. This
would never occur in any normal usage, but we have to allow for it for
mailing-list use */
while( state != DEENVSTATE_DONE && \
iterationCount++ < FAILSAFE_ITERATIONS_LARGE )
{
/* Read the start of the SET OF RecipientInfo/SET OF
DigestAlgorithmIdentifier */
if( state == DEENVSTATE_SET_ENCR )
{
long setLongLength;
/* Read the SET tag and length. We have to read the length as
a long value in order to handle cases where there's a large
amount of key management data involving a great many
recipients */
status = readLongSet( &stream, &setLongLength );
if( cryptStatusError( status ) )
{
setErrorString( ENVELOPE_ERRINFO,
"Invalid SET OF RecipientInfo header", 35 );
break;
}
envelopeInfoPtr->hdrSetLength = setLongLength;
/* 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 (setLength == CRYPT_UNUSED) we
have to look for the EOC after each entry read */
streamPos = stell( &stream );
state = DEENVSTATE_ENCR;
}
if( state == DEENVSTATE_SET_HASH )
{
int setLength;
/* Read the SET tag and length */
status = readSetI( &stream, &setLength );
if( cryptStatusError( status ) )
{
setErrorString( ENVELOPE_ERRINFO,
"Invalid SET OF DigestAlgorithmIdentifier "
"header", 47 );
break;
}
envelopeInfoPtr->hdrSetLength = setLength;
/* 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 (setLength == CRYPT_UNUSED) we
have to look for the EOC after each entry read */
streamPos = stell( &stream );
state = DEENVSTATE_HASH;
}
/* Read and remember a key exchange object from an EncryptionKeyInfo
record */
if( state == DEENVSTATE_ENCR )
{
int contentItemLength;
/* Add the object to the content information list */
status = addContentListItem( envelopeInfoPtr, &stream, NULL,
&contentItemLength );
if( cryptStatusError( status ) )
{
setErrorString( ENVELOPE_ERRINFO,
"Invalid EncryptionKeyInfo key exchange "
"record", 45 );
break;
}
/* Remember where we are and move on to the next state if
necessary */
streamPos = stell( &stream );
if( envelopeInfoPtr->hdrSetLength != CRYPT_UNUSED )
{
if( contentItemLength > envelopeInfoPtr->hdrSetLength )
{
status = CRYPT_ERROR_BADDATA;
break;
}
envelopeInfoPtr->hdrSetLength -= contentItemLength;
if( envelopeInfoPtr->hdrSetLength <= 0 )
{
state = ( envelopeInfoPtr->usage == ACTION_MAC ) ? \
DEENVSTATE_HASH : DEENVSTATE_ENCRCONTENT;
}
}
else
{
const int value = checkEOC( &stream );
if( cryptStatusError( value ) )
{
status = value;
break;
}
if( value == TRUE )
{
state = ( envelopeInfoPtr->usage == ACTION_MAC ) ? \
DEENVSTATE_HASH : DEENVSTATE_ENCRCONTENT;
}
}
}
/* Read the encrypted content information */
if( state == DEENVSTATE_ENCRCONTENT )
{
status = processEncryptionHeader( envelopeInfoPtr, &stream );
if( cryptStatusError( status ) )
{
setErrorString( ENVELOPE_ERRINFO,
"Invalid encrypted content header", 32 );
break;
}
/* Remember where we are and move on to the next state */
streamPos = stell( &stream );
state = DEENVSTATE_DATA;
if( envelopeInfoPtr->actionList == NULL )
{
/* If we haven't got a session key to decrypt the data that
follows we can't go beyond this point */
status = CRYPT_ENVELOPE_RESOURCE;
break;
}
}
/* Read and remember a MAC object from a MACAlgorithmIdentifier
record */
if( state == DEENVSTATE_HASH && \
envelopeInfoPtr->usage == ACTION_MAC )
{
status = processHashHeader( envelopeInfoPtr, &stream );
if( cryptStatusError( status ) )
{
setErrorString( ENVELOPE_ERRINFO,
"Invalid hashed/MACd content header", 34 );
break;
}
/* Remember where we are and move on to the next state */
streamPos = stell( &stream );
state = DEENVSTATE_CONTENT;
}
/* Read and remember a hash object from a DigestAlgorithmIdentifier
record */
if( state == DEENVSTATE_HASH )
{
status = processHashHeader( envelopeInfoPtr, &stream );
if( cryptStatusError( status ) )
break;
/* Remember where we are and move on to the next state if
necessary */
if( envelopeInfoPtr->hdrSetLength != CRYPT_UNUSED )
{
const int hashInfoLength = stell( &stream ) - streamPos;
if( hashInfoLength < 0 || \
hashInfoLength > envelopeInfoPtr->hdrSetLength )
{
status = CRYPT_ERROR_BADDATA;
break;
}
envelopeInfoPtr->hdrSetLength -= hashInfoLength;
streamPos = stell( &stream );
if( envelopeInfoPtr->hdrSetLength <= 0 )
state = DEENVSTATE_CONTENT;
}
else
{
const int value = checkEOC( &stream );
if( cryptStatusError( value ) )
{
status = value;
break;
}
if( value == TRUE )
state = DEENVSTATE_CONTENT;
}
}
/* Read the encapsulated content header */
if( state == DEENVSTATE_CONTENT )
{
int contentType;
status = contentType = \
readCMSheader( &stream, nestedContentOIDinfo,
FAILSAFE_ARRAYSIZE( nestedContentOIDinfo, OID_INFO ),
&envelopeInfoPtr->payloadSize, TRUE );
if( cryptStatusError( status ) )
{
setErrorString( ENVELOPE_ERRINFO,
"Invalid encapsulated content header", 35 );
break;
}
envelopeInfoPtr->contentType = contentType;
/* If there's no content included and it's not an attributes-only
message then this is a detached signature with the content
supplied anderswhere */
if( envelopeInfoPtr->payloadSize == 0 && \
!( envelopeInfoPtr->flags & ENVELOPE_ATTRONLY ) )
envelopeInfoPtr->flags |= ENVELOPE_DETACHED_SIG;
/* Remember where we are and move on to the next state */
streamPos = stell( &stream );
state = ( envelopeInfoPtr->payloadSize == 0 && \
( envelopeInfoPtr->flags & ( ENVELOPE_DETACHED_SIG | \
ENVELOPE_ATTRONLY ) ) ) ? \
DEENVSTATE_DONE : DEENVSTATE_DATA;
/* If this is MACd data and we haven't loaded a key to MAC the
data that follows we can't go beyond this point */
if( envelopeInfoPtr->usage == ACTION_MAC )
{
if( envelopeInfoPtr->actionList == NULL )
{
status = CRYPT_ENVELOPE_RESOURCE;
break;
}
REQUIRES( envelopeInfoPtr->actionList->action == ACTION_MAC );
status = krnlSendMessage( envelopeInfoPtr->actionList->iCryptHandle,
IMESSAGE_CHECK, NULL,
MESSAGE_CHECK_MAC );
if( cryptStatusError( status ) )
{
status = CRYPT_ENVELOPE_RESOURCE;
break;
}
}
}
/* Start the decryption process if necessary */
if( state == DEENVSTATE_DATA )
{
/* Synchronise the data stream processing to the start of the
encrypted data and move back to the start of the data
stream */
status = envelopeInfoPtr->syncDeenvelopeData( envelopeInfoPtr,
&stream );
if( cryptStatusError( status ) )
{
setErrorString( ENVELOPE_ERRINFO,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -