📄 pgp_denv.c
字号:
/****************************************************************************
* *
* cryptlib PGP De-enveloping Routines *
* Copyright Peter Gutmann 1996-2008 *
* *
****************************************************************************/
#if defined( INC_ALL )
#include "envelope.h"
#include "pgp_rw.h"
#else
#include "envelope/envelope.h"
#include "misc/pgp_rw.h"
#endif /* Compiler-specific includes */
#ifdef USE_PGP
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Sanity-check the envelope state */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static BOOLEAN sanityCheck( const ENVELOPE_INFO *envelopeInfoPtr )
{
/* Make sure that general envelope state is in order */
if( !( envelopeInfoPtr->flags & ENVELOPE_ISDEENVELOPE ) )
return( FALSE );
if( envelopeInfoPtr->pgpDeenvState < PGP_DEENVSTATE_NONE || \
envelopeInfoPtr->pgpDeenvState >= PGP_DEENVSTATE_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 );
/* Make sure that the payload size is within bounds */
if( envelopeInfoPtr->payloadSize != CRYPT_UNUSED && \
( envelopeInfoPtr->payloadSize < 0 || \
envelopeInfoPtr->payloadSize >= MAX_INTLENGTH ) )
return( FALSE );
/* Make sure that the out-of-band buffer state is OK. The oobDataLeft
value is the general size of a data packet header plus the maximum
possible length for the variable-length filename portion */
if( envelopeInfoPtr->oobEventCount < 0 || \
envelopeInfoPtr->oobEventCount > 10 || \
envelopeInfoPtr->oobDataLeft < 0 || \
envelopeInfoPtr->oobDataLeft >= 32 + 256 )
return( FALSE );
/* Make sure that the packet data information is within bounds */
if( envelopeInfoPtr->segmentSize < 0 || \
envelopeInfoPtr->segmentSize >= MAX_INTLENGTH || \
envelopeInfoPtr->dataLeft < 0 || \
envelopeInfoPtr->dataLeft >= MAX_INTLENGTH )
return( FALSE );
return( TRUE );
}
/* Get information on a PGP data packet. If the isIndefinite value is
present then an indefinite length (i.e. partial packet lengths) is
permitted, otherwise it isn't. If the allowDummyPackets flag is set then
we allow shorter-than-normal dummy packets (PGP_PACKET_MARKER), otherwise
we enforce a sensible minimum packet size */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3, 4 ) ) \
static int getPacketInfo( INOUT STREAM *stream,
INOUT ENVELOPE_INFO *envelopeInfoPtr,
OUT_ENUM_OPT( PGP_PACKET ) PGP_PACKET_TYPE *packetType,
OUT_LENGTH_Z long *length,
OUT_OPT_BOOL BOOLEAN *isIndefinite,
IN_LENGTH_SHORT int minPacketSize )
{
int ctb, version, status;
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
assert( isWritePtr( packetType, sizeof( PGP_PACKET_TYPE ) ) );
assert( isWritePtr( length, sizeof( long ) ) );
assert( isIndefinite == NULL || \
isWritePtr( isIndefinite, sizeof( BOOLEAN ) ) );
ENSURES( minPacketSize > 0 && minPacketSize < MAX_INTLENGTH_SHORT );
/* Clear return values */
*packetType = PGP_PACKET_NONE;
*length = 0;
if( isIndefinite != NULL )
*isIndefinite = FALSE;
/* Read the packet header and extract information from the CTB. The
assignment of version numbers is a bit complicated since it's
possible to use PGP 2.x packet headers to wrap up OpenPGP packets,
and in fact a number of apps mix version numbers. We treat the
version to report as the highest one that we find */
if( isIndefinite != NULL )
status = pgpReadPacketHeaderI( stream, &ctb, length, minPacketSize );
else
status = pgpReadPacketHeader( stream, &ctb, length, minPacketSize );
if( cryptStatusError( status ) )
{
if( status != OK_SPECIAL )
return( status );
ENSURES( isIndefinite != NULL );
/* Remember that the packet uses an indefinite-length encoding */
envelopeInfoPtr->dataFlags &= ~ENVDATA_NOSEGMENT;
*isIndefinite = TRUE;
}
version = pgpGetPacketVersion( ctb );
if( version > envelopeInfoPtr->version )
envelopeInfoPtr->version = version;
*packetType = pgpGetPacketType( ctb );
/* Extract and return the packet type */
return( CRYPT_OK );
}
/****************************************************************************
* *
* Read Key Exchange/Signature Packets *
* *
****************************************************************************/
/* Add information about an object to an envelope's content information list */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int addContentListItem( INOUT ENVELOPE_INFO *envelopeInfoPtr,
INOUT_OPT STREAM *stream,
const BOOLEAN isContinuedSignature )
{
QUERY_INFO queryInfo;
CONTENT_LIST *contentListItem;
void *object = NULL;
int objectSize = 0, status;
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
assert( ( stream == NULL && \
envelopeInfoPtr->actionList == NULL && \
envelopeInfoPtr->contentList == NULL ) || \
isWritePtr( stream, sizeof( STREAM ) ) );
REQUIRES( ( stream == NULL && \
envelopeInfoPtr->actionList == NULL && \
envelopeInfoPtr->contentList == NULL ) || \
( stream != NULL ) );
/* Make sure that there's room to add another list item */
if( !moreContentItemsPossible( envelopeInfoPtr->contentList ) )
return( CRYPT_ERROR_OVERFLOW );
/* PGP 2.x password-encrypted data is detected by the absence of any
other keying object rather than by finding a concrete object type so
if we're passed a null stream we add a password pseudo-object */
if( stream == NULL )
{
CONTENT_ENCR_INFO *encrInfo;
status = createContentListItem( &contentListItem,
envelopeInfoPtr->memPoolState,
CRYPT_FORMAT_PGP, NULL, 0, FALSE );
if( cryptStatusError( status ) )
return( status );
encrInfo = &contentListItem->clEncrInfo;
contentListItem->envInfo = CRYPT_ENVINFO_PASSWORD;
encrInfo->cryptAlgo = CRYPT_ALGO_IDEA;
encrInfo->cryptMode = CRYPT_MODE_CFB;
encrInfo->keySetupAlgo = CRYPT_ALGO_MD5;
appendContentListItem( envelopeInfoPtr, contentListItem );
return( CRYPT_OK );
}
/* Find the size of the object, allocate a buffer for it if necessary,
and copy it across. This call verifies that all of the object data
is present in the stream so in theory we don't have to check the
following reads, but we check them anyway just to be sure */
status = queryPgpObject( stream, &queryInfo );
if( cryptStatusError( status ) )
return( status );
if( queryInfo.type == CRYPT_OBJECT_SIGNATURE && \
queryInfo.dataStart <= 0 )
{
ENSURES( !isContinuedSignature );
/* It's a one-pass signature packet, the signature information
follows in another packet that will be added later */
status = sSkip( stream, ( int ) queryInfo.size );
if( cryptStatusError( status ) )
return( status );
}
else
{
objectSize = ( int ) queryInfo.size;
if( ( object = clAlloc( "addContentListItem", objectSize ) ) == NULL )
return( CRYPT_ERROR_MEMORY );
status = sread( stream, object, objectSize );
if( cryptStatusError( status ) )
{
clFree( "addContentListItem", object );
return( status );
}
}
/* If it's the rest of the signature data from a one-pass signature,
locate the first half of the signature info and complete the
information. In theory this could get ugly because there could be
multiple one-pass signature packets present but PGP handles multiple
signatures by nesting them so this isn't a problem */
if( isContinuedSignature )
{
int iterationCount;
for( contentListItem = envelopeInfoPtr->contentList, \
iterationCount = 0;
contentListItem != NULL && \
contentListItem->envInfo != CRYPT_ENVINFO_SIGNATURE && \
iterationCount < FAILSAFE_ITERATIONS_MED;
contentListItem = contentListItem->next, iterationCount++ );
ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
ENSURES( contentListItem != NULL && \
contentListItem->object == NULL && \
contentListItem->objectSize == 0 );
/* Consistency check, make sure that the hash algorithm and key ID
that we've been working with match what's in the signature */
if( contentListItem->clSigInfo.hashAlgo != queryInfo.hashAlgo || \
contentListItem->keyIDsize != queryInfo.keyIDlength || \
memcmp( contentListItem->keyID, queryInfo.keyID,
queryInfo.keyIDlength ) )
{
clFree( "addContentListItem", object );
return( CRYPT_ERROR_BADDATA );
}
/* We've got the right content list entry, point it to the newly-
acquired signature data */
contentListItem->object = object;
contentListItem->objectSize = objectSize;
}
else
{
/* Allocate memory for the new content list item and copy information
on the item across */
status = createContentListItem( &contentListItem,
envelopeInfoPtr->memPoolState,
CRYPT_FORMAT_PGP, object, objectSize,
( queryInfo.type == CRYPT_OBJECT_SIGNATURE ) ? \
TRUE : FALSE );
if( cryptStatusError( status ) )
{
if( object != NULL )
clFree( "addContentListItem", object );
return( status );
}
}
if( queryInfo.type == CRYPT_OBJECT_PKCENCRYPTED_KEY || \
queryInfo.type == CRYPT_OBJECT_SIGNATURE )
{
/* Remember details of the enveloping info that we require to
continue */
if( queryInfo.type == CRYPT_OBJECT_PKCENCRYPTED_KEY )
{
CONTENT_ENCR_INFO *encrInfo = &contentListItem->clEncrInfo;
contentListItem->envInfo = CRYPT_ENVINFO_PRIVATEKEY;
encrInfo->cryptAlgo = queryInfo.cryptAlgo;
}
else
{
CONTENT_SIG_INFO *sigInfo = &contentListItem->clSigInfo;
contentListItem->envInfo = CRYPT_ENVINFO_SIGNATURE;
sigInfo->hashAlgo = queryInfo.hashAlgo;
if( queryInfo.attributeStart > 0 )
{
REQUIRES( rangeCheck( queryInfo.attributeStart,
queryInfo.attributeLength,
objectSize ) );
sigInfo->extraData = \
( BYTE * ) contentListItem->object + \
queryInfo.attributeStart;
sigInfo->extraDataLength = queryInfo.attributeLength;
}
if( queryInfo.unauthAttributeStart > 0 )
{
REQUIRES( rangeCheck( queryInfo.unauthAttributeStart,
queryInfo.unauthAttributeLength,
objectSize ) );
sigInfo->extraData2 = \
( BYTE * ) contentListItem->object + \
queryInfo.unauthAttributeStart;
sigInfo->extraData2Length = queryInfo.unauthAttributeLength;
}
}
REQUIRES( queryInfo.keyIDlength > 0 && \
queryInfo.keyIDlength <= CRYPT_MAX_HASHSIZE );
memcpy( contentListItem->keyID, queryInfo.keyID,
queryInfo.keyIDlength );
contentListItem->keyIDsize = queryInfo.keyIDlength;
if( queryInfo.iAndSStart > 0 )
{
REQUIRES( rangeCheck( queryInfo.iAndSStart,
queryInfo.iAndSLength, objectSize ) );
contentListItem->issuerAndSerialNumber = \
( BYTE * ) contentListItem->object + queryInfo.iAndSStart;
contentListItem->issuerAndSerialNumberSize = queryInfo.iAndSLength;
}
}
if( queryInfo.type == CRYPT_OBJECT_ENCRYPTED_KEY )
{
CONTENT_ENCR_INFO *encrInfo = &contentListItem->clEncrInfo;
/* Remember details of the enveloping info that we require to
continue */
if( queryInfo.keySetupAlgo != CRYPT_ALGO_NONE )
{
contentListItem->envInfo = CRYPT_ENVINFO_PASSWORD;
encrInfo->keySetupAlgo = queryInfo.keySetupAlgo;
encrInfo->keySetupIterations = queryInfo.keySetupIterations;
REQUIRES( queryInfo.saltLength > 0 && \
queryInfo.saltLength <= CRYPT_MAX_IVSIZE );
memcpy( encrInfo->saltOrIV, queryInfo.salt,
queryInfo.saltLength );
encrInfo->saltOrIVsize = queryInfo.saltLength;
}
else
contentListItem->envInfo = CRYPT_ENVINFO_KEY;
encrInfo->cryptAlgo = queryInfo.cryptAlgo;
encrInfo->cryptMode = CRYPT_MODE_CFB;
}
if( queryInfo.dataStart > 0 )
{
REQUIRES( rangeCheck( queryInfo.dataStart, queryInfo.dataLength,
objectSize ) );
contentListItem->payload = \
( BYTE * ) contentListItem->object + queryInfo.dataStart;
contentListItem->payloadSize = queryInfo.dataLength;
}
if( queryInfo.version > envelopeInfoPtr->version )
envelopeInfoPtr->version = queryInfo.version;
/* If we're completing the read of the data in a one-pass signature
packet, we're done */
if( isContinuedSignature )
return( CRYPT_OK );
/* If it's signed data, create a hash action to process it. Because PGP
only applies one level of signing per packet nesting level we don't
have to worry that this will add redundant hash actions as there'll
only ever be one */
if( queryInfo.type == CRYPT_OBJECT_SIGNATURE )
{
MESSAGE_CREATEOBJECT_INFO createInfo;
REQUIRES( moreActionsPossible( envelopeInfoPtr->actionList ) );
/* Append a new hash action to the action list */
setMessageCreateObjectInfo( &createInfo,
contentListItem->clSigInfo.hashAlgo );
status = krnlSendMessage( SYSTEM_OBJECT_HANDLE,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -