📄 pgp_denv.c
字号:
/* If we're down to stripping raw header data, clean up and
exit */
if( !envelopeInfoPtr->oobEventCount )
{
/* We've successfully stripped all the out-of-band data. If
it's compressed data (which doesn't have a 1:1
correspondence between input and output and that has an
unknown-length encoding so there's no length information
to adjust), exit */
envelopeInfoPtr->oobDataLeft = 0;
if( envelopeInfoPtr->usage == ACTION_COMPRESS )
{
state = PGP_DEENVSTATE_DONE;
continue;
}
/* Adjust the current data count by what we've removed. 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 */
envelopeInfoPtr->segmentSize -= length;
assert( envelopeInfoPtr->segmentSize >= 0 );
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, not the entire
packet) */
if( envelopeInfoPtr->usage == ACTION_SIGN )
envelopeInfoPtr->dataFlags |= ENVDATA_HASHACTIONSACTIVE;
/* We're done */
state = PGP_DEENVSTATE_DONE;
continue;
}
/* Read the header information and see what we've got */
sMemConnect( &headerStream, buffer, length );
packetType = getPacketInfo( &headerStream, envelopeInfoPtr,
&packetLength );
if( cryptStatusError( packetType ) )
{
sMemClose( &headerStream );
status = packetType;
break;
}
/* 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 )
{
/* 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->payloadSize = \
envelopeInfoPtr->segmentSize = \
stell( &headerStream ) + packetLength;
else
/* It's an indefinite-length packet, if we got length
information earlier from the outer packet use that */
if( envelopeInfoPtr->payloadSize != CRYPT_UNUSED )
envelopeInfoPtr->segmentSize = \
envelopeInfoPtr->payloadSize;
else
{
/* Both the outer and inner packets are indefinite-
length, we can't process the data because we don't
know its length */
sMemClose( &headerStream );
status = CRYPT_ERROR_BADDATA;
break;
}
}
/* If it's a literal data packet, parse it so that we can strip
it from the data that we return to the caller */
if( packetType == PGP_PACKET_DATA )
{
int extraLen;
sgetc( &headerStream ); /* Skip content type */
extraLen = sgetc( &headerStream );
envelopeInfoPtr->oobDataLeft = stell( &headerStream ) + \
extraLen + 4;
sMemDisconnect( &headerStream );
/* We've processed enough of the header to know what to do
next, move on to the next stage where we just consume all
the input */
envelopeInfoPtr->oobEventCount--;
}
else
{
static const struct {
const int pgpType; const int cryptlibType;
} typeMapTbl[] = {
{ PGP_PACKET_COPR, CRYPT_CONTENT_COMPRESSEDDATA },
{ PGP_PACKET_ENCR, CRYPT_CONTENT_ENCRYPTEDDATA },
{ PGP_PACKET_ENCR_MDC, CRYPT_CONTENT_ENCRYPTEDDATA },
{ PGP_PACKET_SKE, CRYPT_CONTENT_ENCRYPTEDDATA },
{ PGP_PACKET_PKE, CRYPT_CONTENT_ENVELOPEDDATA },
{ PGP_PACKET_SIGNATURE, CRYPT_CONTENT_SIGNEDDATA },
{ PGP_PACKET_SIGNATURE_ONEPASS, CRYPT_CONTENT_SIGNEDDATA },
{ CRYPT_ERROR, CRYPT_ERROR },
};
int i;
sMemDisconnect( &headerStream );
/* If it's a known packet type, indicate it as the nested
content type */
for( i = 0; typeMapTbl[ i ].pgpType != CRYPT_ERROR; i++ )
if( typeMapTbl[ i ].pgpType == packetType )
{
envelopeInfoPtr->contentType = \
typeMapTbl[ i ].cryptlibType;
break;
}
if( typeMapTbl[ i ].pgpType == CRYPT_ERROR )
{
status = CRYPT_ERROR_BADDATA;
break;
}
/* If it's not compressed data (which doesn't have a 1:1
correspondence between input and output), 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;
}
/* Don't try and process the content any further */
envelopeInfoPtr->oobEventCount = \
envelopeInfoPtr->oobDataLeft = 0;
state = PGP_DEENVSTATE_DONE;
}
}
}
if( iterationCount >= 256 )
return( CRYPT_ERROR_FAILED );
envelopeInfoPtr->pgpDeenvState = state;
assert( streamPos >= 0 && envelopeInfoPtr->bufPos - streamPos >= 0 );
/* Consume the input we've processed so far by moving everything past the
current position down to the start of the envelope buffer */
length = envelopeInfoPtr->bufPos - streamPos;
if( length > 0 && streamPos > 0 )
memmove( envelopeInfoPtr->buffer, envelopeInfoPtr->buffer + streamPos,
length );
envelopeInfoPtr->bufPos = length;
/* If all went OK but we're still not out of the header information,
return an underflow error */
if( cryptStatusOK( status ) && state != PGP_DEENVSTATE_DONE )
status = CRYPT_ERROR_UNDERFLOW;
/* Clean up */
sMemDisconnect( &stream );
return( status );
}
static int processPostamble( ENVELOPE_INFO *envelopeInfoPtr )
{
CONTENT_LIST *contentListPtr;
const BOOLEAN hasMDC = \
( envelopeInfoPtr->usage == ACTION_CRYPT && \
( envelopeInfoPtr->dataFlags & ENVDATA_HASHACTIONSACTIVE ) ) ? \
TRUE : FALSE;
int status = CRYPT_OK;
/* If that's all there is, return */
if( envelopeInfoPtr->usage != ACTION_SIGN && !hasMDC )
return( CRYPT_OK );
/* If there's an MDC packet present, complete the hashing and make sure
the integrity check matches */
if( hasMDC )
{
/* Make sure that there's enough data left in the stream to obtain
the MDC info, and get the MDC packet */
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 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) sig 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. 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, since the
functionality is neither at the cryptenv.c level nor at the
env_dec.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 ASN.1
variable-length length encoding where changing data by one byte
can result in a variable change of length for inner lengths,
making it impossible to encode some data lengths to the fixed size
required by a CBC-mode cipher). 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 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, so bufPos will
never be zero), the following code is present only as a
representative example */
if( envelopeInfoPtr->dataLeft == PGP_MDC_PACKET_SIZE )
{
BYTE buffer[ PGP_MDC_PACKET_SIZE ];
envelopeInfoPtr->copyFromEnvelopeFunction( envelopeInfoPtr,
buffer, PGP_MDC_PACKET_SIZE );
if( buffer[ 0 ] != 0xD0 || buffer[ 1 ] != 0x14 )
return( CRYPT_ERROR_BADDATA );
/* Hash the trailer bytes (the start of the MDC packet) and wrap
up the hashing */
envelopeInfoPtr->processExtraData( envelopeInfoPtr, buffer + 2,
PGP_MDC_PACKET_SIZE - 2 );
status = envelopeInfoPtr->processExtraData( envelopeInfoPtr,
"", 0 );
if( cryptStatusError( status ) )
return( status );
}
return( CRYPT_OK );
}
/* Find the signature information in the content list. In theory this
could get ugly because there could be multiple one-pass signature
packets present, however PGP handles multiple signatures by nesting
them so this isn't a problem */
for( contentListPtr = envelopeInfoPtr->contentList;
contentListPtr != NULL && \
contentListPtr->envInfo != CRYPT_ENVINFO_SIGNATURE;
contentListPtr = contentListPtr->next );
/* PGP 2.x prepended (!!) signatures to the signed data, OpenPGP fixed
this by splitting the signature into a header with signature info and
a trailer with the actual signature. If we're processing a PGP 2.x
signature, we'll already have the signature data present, so we only
check for signature data if it's not already available */
if( contentListPtr->object == NULL )
{
STREAM stream;
long packetLength;
int packetType;
/* Make sure that there's enough data left in the stream to do
something with. This isn't strictly necessary for the following
code to work but is required to avoid triggering the zero-length
stream check */
if( envelopeInfoPtr->bufPos - envelopeInfoPtr->dataLeft < \
PGP_MAX_HEADER_SIZE )
return( CRYPT_ERROR_UNDERFLOW );
/* Read the signature packet at the end of the payload */
sMemConnect( &stream, envelopeInfoPtr->buffer + envelopeInfoPtr->dataLeft,
envelopeInfoPtr->bufPos - envelopeInfoPtr->dataLeft );
packetType = getPacketInfo( &stream, envelopeInfoPtr, &packetLength );
if( !cryptStatusError( packetType ) && \
packetType != PGP_PACKET_SIGNATURE )
packetType = CRYPT_ERROR_BADDATA;
if( cryptStatusError( packetType ) )
{
sMemDisconnect( &stream );
return( packetType );
}
sseek( &stream, 0 );
status = addContentListItem( &stream, envelopeInfoPtr, TRUE );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
}
/* When we reach this point there may still be unhashed data left in the
buffer (it won't have been hashed yet because the hashing is performed
when the data is copied out, after unwrapping and whatnot) so we hash
it before we exit. Since we don't wrap up the hashing as we do with
any other format (PGP hashes in all sorts of odds and ends after
hashing the message body), we have to manually turn off hashing here */
if( envelopeInfoPtr->dataLeft > 0 )
status = envelopeInfoPtr->processExtraData( envelopeInfoPtr,
envelopeInfoPtr->buffer, envelopeInfoPtr->dataLeft );
envelopeInfoPtr->dataFlags &= ~ENVDATA_HASHACTIONSACTIVE;
return( status );
}
/****************************************************************************
* *
* Envelope Access Routines *
* *
****************************************************************************/
void initPGPDeenveloping( ENVELOPE_INFO *envelopeInfoPtr )
{
/* Set the access method pointers */
envelopeInfoPtr->processPreambleFunction = processPreamble;
envelopeInfoPtr->processPostambleFunction = processPostamble;
/* Set up the processing state information */
envelopeInfoPtr->pgpDeenvState = PGP_DEENVSTATE_NONE;
}
#endif /* USE_PGP */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -