📄 pgp_denv.c
字号:
we have to do this is because segmentSize records the amount of
data copied in (rather than out, as we've done here) but since it
was copied directly into the envelope buffer as part of the
header-processing rather than via copyToDeenvelope() (which is
what usually adjusts segmentSize for us) we have to manually
adjust the value here */
if( envelopeInfoPtr->segmentSize > 0 )
{
envelopeInfoPtr->segmentSize -= length;
ENSURES( envelopeInfoPtr->segmentSize >= 0 && \
envelopeInfoPtr->segmentSize < MAX_INTLENGTH );
/* If we've reached the end of the data (i.e. the entire current
segment is contained within the data present in the buffer)
remember that what's left still needs to be processed (e.g.
hashed in the case of signed data) on the way out */
if( envelopeInfoPtr->segmentSize <= envelopeInfoPtr->bufPos )
{
envelopeInfoPtr->dataLeft = envelopeInfoPtr->segmentSize;
envelopeInfoPtr->segmentSize = 0;
}
}
/* We've processed the header, if this is signed data we start
hashing from this point (the PGP RFCs are wrong in this regard,
only the payload is hashed and not the entire packet) */
if( envelopeInfoPtr->usage == ACTION_SIGN )
envelopeInfoPtr->dataFlags |= ENVDATA_HASHACTIONSACTIVE;
/* We're done */
*state = PGP_DEENVSTATE_DONE;
ENSURES( sanityCheck( envelopeInfoPtr ) );
return( CRYPT_OK );
}
/* We have to perform all sorts of special-case processing to handle the
out-of-band packet header at the start of the payload. Initially, we
need to find out how much header data is actually present. The header
for a plain data packet consists of:
byte ctb
byte[] length
byte type = 'b' | 't'
byte filename length
byte[] filename
byte[4] timestamp
The smallest size for this header (1-byte length, no filename) is
1 + 1 + 1 + 1 + 4 = 8 bytes. This is also just enough to get us to
the filename length for a maximum-size header, which is 1 + 5 + 1 + 1
bytes up to the filename length and covers the type + length range
of every other packet type, which can be from 1 to 1 + 5 bytes. Thus
we read 8 bytes, setting the OOB data flag to indicate that this is a
read-ahead read that doesn't remove data from the buffer */
status = envelopeInfoPtr->copyFromEnvelopeFunction( envelopeInfoPtr,
buffer, 8, &length,
ENVCOPY_FLAG_OOBDATA );
if( cryptStatusError( status ) )
return( status );
if( length < 8 )
return( CRYPT_ERROR_UNDERFLOW );
/* Read the header information and see what we've got */
sMemConnect( &headerStream, buffer, length );
status = getPacketInfo( &headerStream, envelopeInfoPtr, &packetType,
&packetLength, NULL, 8 );
if( cryptStatusError( status ) )
{
sMemDisconnect( &headerStream );
return( status );
}
/* Remember the total data packet size unless it's compressed data,
which doesn't have a 1:1 correspondence between input and output */
if( envelopeInfoPtr->usage != ACTION_COMPRESS )
{
/******/
/* All of this only for definite-length packets or indefinite + EOC-seen */
/******/
/* If it's a definite-length packet, use the overall packet size.
This also skips any MDC packets that may be attached to the end
of the plaintext */
if( packetLength != CRYPT_UNUSED )
{
envelopeInfoPtr->segmentSize = stell( &headerStream ) + \
packetLength;
/* If we're using the definite-length encoding (which is the
default for PGP) then the overall payload size is equal to
the segment size */
if( envelopeInfoPtr->dataFlags & ENVDATA_NOSEGMENT )
envelopeInfoPtr->payloadSize = envelopeInfoPtr->segmentSize;
}
else
{
ENSURES( packetType == PGP_PACKET_COPR );
ENSURES( envelopeInfoPtr->payloadSize != CRYPT_UNUSED );
/* It's an arbitrary-length compressed data packet, use the
length that we got earlier from the outer packet */
if( envelopeInfoPtr->dataFlags & ENVDATA_ENDOFCONTENTS )
;
else
envelopeInfoPtr->segmentSize = envelopeInfoPtr->payloadSize;
}
}
/* If it's a literal data packet, parse it so that we can strip it from
the data that we return to the caller. We know that the reads can't
fail because the readahead read has confirmed that there are at least
8 bytes available, but we check anyway just to be sure */
if( packetType == PGP_PACKET_DATA )
{
int extraLen;
sgetc( &headerStream ); /* Skip content type */
status = extraLen = sgetc( &headerStream );
if( !cryptStatusError( status ) )
{
envelopeInfoPtr->oobDataLeft = stell( &headerStream ) + \
extraLen + 4;
}
sMemDisconnect( &headerStream );
if( cryptStatusError( status ) )
return( status );
/* Remember that this is a pure data packet */
envelopeInfoPtr->contentType = CRYPT_CONTENT_DATA;
/* We've processed enough of the header to know what to do next,
move on to the next sub-state where we just consume all of the
input. This has to be done as a sub-state within the
PGP_DEENVSTATE_DATA_HEADER state since we can encounter a
(recoverable) error between reading the out-of-band data header
and reading the out-of-band data itself */
envelopeInfoPtr->oobEventCount--;
ENSURES( sanityCheck( envelopeInfoPtr ) );
return( CRYPT_OK );
}
sMemDisconnect( &headerStream );
/* If it's a known packet type, indicate it as the nested content type */
status = mapValue( packetType, &value, typeMapTbl,
FAILSAFE_ARRAYSIZE( typeMapTbl, MAP_TABLE ) );
if( cryptStatusError( status ) )
return( CRYPT_ERROR_BADDATA );
envelopeInfoPtr->contentType = value;
#if 0 /* 12/4/06 See previous comment */
/* If it's not compressed data (which doesn't have a 1:1 correspondence
between input and output) and we've reached the end of the data (i.e.
the entire current segment is contained within the data present in
the buffer), remember that what's left still needs to be processed
(e.g. hashed in the case of signed data) on the way out */
if( envelopeInfoPtr->usage != ACTION_COMPRESS && \
envelopeInfoPtr->segmentSize <= envelopeInfoPtr->bufPos )
{
envelopeInfoPtr->dataLeft = envelopeInfoPtr->segmentSize;
envelopeInfoPtr->segmentSize = 0;
}
#endif /* 0 */
/* Don't try and process the content any further */
envelopeInfoPtr->oobEventCount = envelopeInfoPtr->oobDataLeft = 0;
*state = PGP_DEENVSTATE_DONE;
ENSURES( sanityCheck( envelopeInfoPtr ) );
return( CRYPT_OK );
}
/* Process the start of an encrypted data packet */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
static int processEncryptedPacket( INOUT ENVELOPE_INFO *envelopeInfoPtr,
INOUT STREAM *stream,
IN_ENUM( PGP_DEENVSTATE ) \
const PGP_DEENV_STATE state )
{
BYTE ivInfoBuffer[ CRYPT_MAX_IVSIZE + 2 + 8 ];
int ivSize, offset, status;
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
assert( isWritePtr( stream, sizeof( STREAM ) ) );
REQUIRES( sanityCheck( envelopeInfoPtr ) );
REQUIRES( state > PGP_DEENVSTATE_NONE && state < PGP_DEENVSTATE_LAST );
/* If there aren't any non-session-key keying resource objects present
we can't go any further until we get a session key */
if( envelopeInfoPtr->actionList == NULL )
{
/* There's no session key object present, add a pseudo-object that
takes the place of the (password-derived) session key object in
the content list. This can only occur for PGP 2.x conventionally-
encrypted data, which didn't encode any algorithm information
with the data, so if we get to this point we know that we've hit
data encrypted with the default IDEA/CFB encryption algorithm
derived from a user password using the default MD5 hash
algorithm */
if( envelopeInfoPtr->contentList == NULL )
{
status = addContentListItem( envelopeInfoPtr, NULL, FALSE );
if( cryptStatusError( status ) )
return( status );
}
/* We can't continue until we're given some sort of keying resource */
return( CRYPT_ENVELOPE_RESOURCE );
}
ENSURES( envelopeInfoPtr->actionList != NULL && \
envelopeInfoPtr->actionList->action == ACTION_CRYPT );
/* Read and process PGP's peculiar two-stage IV */
status = krnlSendMessage( envelopeInfoPtr->actionList->iCryptHandle,
IMESSAGE_GETATTRIBUTE, &ivSize,
CRYPT_CTXINFO_IVSIZE );
if( cryptStatusOK( status ) )
status = sread( stream, ivInfoBuffer, ivSize + 2 );
if( !cryptStatusError( status ) )
{
status = pgpProcessIV( envelopeInfoPtr->actionList->iCryptHandle,
ivInfoBuffer, ivSize + 2, ivSize, FALSE,
( state == PGP_DEENVSTATE_ENCR ) ? \
TRUE : FALSE );
}
if( cryptStatusError( status ) )
return( status );
envelopeInfoPtr->iCryptContext = \
envelopeInfoPtr->actionList->iCryptHandle;
#if 0 /************************************************/
{ /* For buggy McAfee PGP with indefinite lengths */
BYTE buffer[ 1024 ], *srcPtr = sMemBufPtr( stream );
memcpy( buffer, srcPtr - 14, 32 );
krnlSendMessage( envelopeInfoPtr->iCryptContext, IMESSAGE_CTX_DECRYPT,
buffer, 32 );
} /* For buggy McAfee PGP with indefinite lengths */
#endif /************************************************/
/* If we're keeping track of the outer packet size in case there's no
inner size info present, adjust it by the data that we've just
processed and any other data that may be present */
offset = stell( stream );
if( state == PGP_DEENVSTATE_ENCR_MDC )
{
/* If we're using a definite-length encoding, adjust the total data
length for the length of the tacked-on MDC packet */
if( envelopeInfoPtr->dataFlags & ENVDATA_NOSEGMENT )
{
/* There was a bug in all versions of GPG before 1.0.8, which
omitted the MDC packet length when a packet was encrypted
without compression. As a result uncompressed messages
generated by these versions can't be processed */
offset += PGP_MDC_PACKET_SIZE;
}
else
{
/* We're using an indefinite-length encoding, remember that we
have to adjust for the tacked-on MDC packet when we hit the
last data segment */
envelopeInfoPtr->dataFlags |= ENVDATA_HASATTACHEDOOB;
}
}
/******/
/* Is the IV part of the length? If so how to handle with indefinite lengths? */
/* (offset = IV size + optional MDC size, i.e. we adjust the length based on the */
/* IV bytes read) */
/******/
if( envelopeInfoPtr->payloadSize != CRYPT_UNUSED )
envelopeInfoPtr->payloadSize -= offset;
/* If there's an MDC packet present, prepare to hash the payload data */
if( state == PGP_DEENVSTATE_ENCR_MDC )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
REQUIRES( moreActionsPossible( envelopeInfoPtr->actionList ) );
/* Append a hash action to the action list */
setMessageCreateObjectInfo( &createInfo, CRYPT_ALGO_SHA1 );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
IMESSAGE_DEV_CREATEOBJECT,
&createInfo, OBJECT_TYPE_CONTEXT );
if( cryptStatusError( status ) )
return( status );
status = addAction( &envelopeInfoPtr->actionList,
envelopeInfoPtr->memPoolState, ACTION_HASH,
createInfo.cryptHandle );
if( cryptStatusError( status ) )
{
krnlSendNotifier( createInfo.cryptHandle,
IMESSAGE_DECREFCOUNT );
return( status );
}
envelopeInfoPtr->dataFlags |= ENVDATA_HASHACTIONSACTIVE;
}
ENSURES( sanityCheck( envelopeInfoPtr ) );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Trailer Processing Routines *
* *
****************************************************************************/
/* Process an MDC packet */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int processMDC( INOUT ENVELOPE_INFO *envelopeInfoPtr )
{
int status;
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
REQUIRES( sanityCheck( envelopeInfoPtr ) );
/* Make sure that there's enough data left in the stream to obtain the
MDC info */
if( envelopeInfoPtr->bufPos - \
envelopeInfoPtr->dataLeft < PGP_MDC_PACKET_SIZE )
return( CRYPT_ERROR_UNDERFLOW );
/* Processing beyond this point gets rather complex because we have to
defer reading the MDC packet until all of the remaining data has been
popped, while processing reaches this point when data is pushed.
Conventionally signed/hashed data hashes the plaintext so once we
reach this point we can wrap up the hashing ready for the (user-
initiated) signature check. The MDC packet however is still
encrypted at this point, along with some or all of the data to be
hashed, which means that we can't do anything yet:
<-- Decr. -><- Encrypted ->
--------+-------------+---------
.... |///////| MDC | ....
--------+-------------+---------
^ ^
bufPos bufEnd
In order to handle this special-case situation we'd have to add extra
capabilities to the data-popping code to tell it that after a certain
amount of data has been popped what's still left is MDC data. This
severely screws up the layering because the functionality required is
neither at the cryptenv.c level nor at the decode.c level, and
represents an approach that was abandoned in cryptlib 2.1 when it
proved impossible to get it working reliably under all circumstances
(it's provably impossible to do with the ASN.1 variable-length length
encoding where changing data by one byte can result in a variable
change of length for inner lengths. This makes it impossible to
encode some data lengths to the fixed size required by a CBC-mode
cipher, and OpenPGP's more recent variable-lengths are no better).
This is why cryptlib uses separate passes for each processing layer
rather than trying to combine encryption and signing into a single
pass.
Because of this, handling of MDC packets is only done if all of the
data in the envelope has been popped (but see the note below), fully
general handling won't be added unless there is sufficient user
demand to justify messing up the architectural layering of the
enveloping code. Note that this situation can never occur since
we're being called when data is pushed, the following code is present
only as a representative example */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -