📄 pgp_deen.c
字号:
envelopeInfoPtr->hashActionsActive = TRUE;
envelopeInfoPtr->payloadSize = 0;
state = PGP_DEENVSTATE_DONE;
}
else
state = PGP_DEENVSTATE_DATA;
break;
case PGP_PACKET_ENCR_MDC:
/* The encrypted-data-with-MDC packet is preceded by a
version number */
status = sgetc( &stream );
if( !cryptStatusError( status ) && status != 1 )
status = CRYPT_ERROR_BADDATA;
if( cryptStatusError( status ) )
break;
/* Fall through */
case PGP_PACKET_ENCR:
if( envelopeInfoPtr->usage != ACTION_NONE && \
envelopeInfoPtr->usage != ACTION_CRYPT )
{
status = CRYPT_ERROR_BADDATA;
break;
}
envelopeInfoPtr->usage = ACTION_CRYPT;
state = ( packetType == PGP_PACKET_ENCR_MDC ) ? \
PGP_DEENVSTATE_ENCR_MDC : PGP_DEENVSTATE_ENCR;
break;
case PGP_PACKET_MARKER:
/* Obsolete market packet, skip it */
status = sSkip( &stream, packetLength );
if( cryptStatusError( status ) )
break;
break;
default:
status = CRYPT_ERROR_BADDATA;
}
/* If there's a problem, exit */
if( cryptStatusError( status ) )
break;
/* Remember how far we got */
streamPos = ( int ) stell( &stream );
}
/* Process the start of an encrypted data packet */
if( state == PGP_DEENVSTATE_ENCR || \
state == PGP_DEENVSTATE_ENCR_MDC )
{
BYTE ivInfoBuffer[ CRYPT_MAX_IVSIZE + 2 ];
int ivSize;
/* 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
which 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 we've hit data encrypted with the default
IDEA/CFB derived from a user password using MD5 */
if( envelopeInfoPtr->contentList == NULL )
{
status = addContentListItem( NULL, envelopeInfoPtr, 0, 0 );
if( cryptStatusError( status ) )
break;
}
/* We can't continue until we're given some sort of keying
resource */
status = CRYPT_ENVELOPE_RESOURCE;
break;
}
assert( envelopeInfoPtr->actionList->action == ACTION_CRYPT );
/* Read and process PGP's peculiar two-stage IV */
status = krnlSendMessage( envelopeInfoPtr->actionList->iCryptHandle,
RESOURCE_IMESSAGE_GETATTRIBUTE,
&ivSize, CRYPT_CTXINFO_IVSIZE );
if( cryptStatusError( status ) )
break;
if( sread( &stream, ivInfoBuffer, ivSize + 2 ) != CRYPT_OK )
{
status = CRYPT_ERROR_UNDERFLOW;
break;
}
status = pgpProcessIV( envelopeInfoPtr->actionList->iCryptHandle,
ivInfoBuffer, ivSize, FALSE,
( state == PGP_DEENVSTATE_ENCR ) ? \
TRUE : FALSE );
if( cryptStatusError( status ) )
break;
envelopeInfoPtr->iCryptContext = \
envelopeInfoPtr->actionList->iCryptHandle;
/* Remember where we are and move on to the next state */
streamPos = stell( &stream );
state = PGP_DEENVSTATE_DATA;
}
/* Process the start of a data packet */
if( state == PGP_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 ) )
break;
streamPos = 0;
assert( actionsOK( envelopeInfoPtr ) );
/* Move on to the next state. For plain data we're done,
however for other content types we have to either process or
strip out the junk PGP puts at the start of the content */
if( envelopeInfoPtr->usage != ACTION_NONE )
{
envelopeInfoPtr->oobEventCount = 1;
state = PGP_DEENVSTATE_DATA_HEADER;
}
else
state = PGP_DEENVSTATE_DONE;
assert( actionsOK( envelopeInfoPtr ) );
}
/* PGP doesn't provide any indication of what the content of the
current packet is, so we have to burrow down into the encrypted
data to see whether the payload needs any further processing.
This state looks ahead into this data to see whether we need to
strip the header (for a plain data packet) or inform the user
that there's a nested content type */
if( state == PGP_DEENVSTATE_DATA_HEADER )
{
STREAM headerStream;
BYTE buffer[ 64 + 256 ];
int bytesWanted;
/* If there's no out-of-band data left to remove at the start of
the payload, we're done */
if( !envelopeInfoPtr->oobEventCount && \
!envelopeInfoPtr->oobDataLeft )
break;
/* 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 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. Thus we
read 8 bytes */
bytesWanted = envelopeInfoPtr->oobEventCount ? \
-8 : envelopeInfoPtr->oobDataLeft;
/* Try and read the the requested amount of out-of-band data
from the start of the payload */
length = envelopeInfoPtr->copyFromDeenvelope( envelopeInfoPtr,
buffer, bytesWanted );
if( cryptStatusError( length ) )
status = length;
else
if( length < \
( ( bytesWanted >= 0 ) ? bytesWanted : -bytesWanted ) )
status = CRYPT_ERROR_UNDERFLOW;
if( cryptStatusError( status ) )
break;
/* 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 which 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 (ie the entire current
segment is contained within the data present in the
buffer), remember that what's left still needs to be
processed (eg 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->hashActionsActive = TRUE;
/* 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. This also skips any MDC packets which may be
attached to the end of the plaintext */
if( envelopeInfoPtr->usage != ACTION_COMPRESS )
envelopeInfoPtr->payloadSize = envelopeInfoPtr->segmentSize = \
stell( &headerStream ) + packetLength;
/* If it's a literal data packet, parse it so we can strip it
from the data we return to the caller */
if( packetType == PGP_PACKET_DATA )
{
int extraLen;
sgetc( &headerStream ); /* Skip content type */
extraLen = sgetc( &headerStream );
envelopeInfoPtr->oobDataLeft = \
( int ) stell( &headerStream ) + extraLen + 4;
sMemClose( &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;
sMemClose( &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 (ie the entire current segment is
contained within the data present in the buffer), remember
that what's left still needs to be processed (eg 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;
}
}
}
envelopeInfoPtr->pgpDeenvState = state;
/* Consume the input we've processed so far by moving everything past the
current position down to the start of the memory buffer */
length = envelopeInfoPtr->bufPos - streamPos;
if( length && streamPos )
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;
int status = CRYPT_OK;
/* If that's all there is, return */
if( envelopeInfoPtr->usage != ACTION_SIGN )
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 );
/* If we're processing a one-pass signature, the signature data follows
rather than precedes the payload, so we have to read the signature
before we can continue */
if( contentListPtr->object == NULL )
{
STREAM stream;
long packetLength;
int packetType;
/* Make sure 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 );
}
status = addContentListItem( &stream, envelopeInfoPtr,
PGP_PACKET_SIGNATURE_SPECIAL,
packetLength );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
return( status );
}
/* We've seen all the signed data, complete the hashing. 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 wrap up the hashing */
if( envelopeInfoPtr->dataLeft )
status = envelopeInfoPtr->processExtraData( envelopeInfoPtr,
envelopeInfoPtr->buffer, envelopeInfoPtr->dataLeft );
if( cryptStatusOK( status ) )
/* After hashing the content, PGP also hashes in extra authenticated
attributes */
status = envelopeInfoPtr->processExtraData( envelopeInfoPtr,
contentListPtr->extraData,
contentListPtr->extraDataLength );
if( cryptStatusOK( status ) && contentListPtr->extraDataLength != 5 )
{
BYTE buffer[ 8 ], *bufPtr = buffer + 2;
/* In addition to the standard authenticated attributes, OpenPGP
hashes in even more stuff at the end */
buffer[ 0 ] = 0x04;
buffer[ 1 ] = 0xFF;
mputBLong( bufPtr, contentListPtr->extraDataLength );
status = envelopeInfoPtr->processExtraData( envelopeInfoPtr,
buffer, 6 );
}
if( cryptStatusOK( status ) )
status = envelopeInfoPtr->processExtraData( envelopeInfoPtr, "", 0 );
if( cryptStatusError( status ) )
return( status );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Envelope Access Routines *
* *
****************************************************************************/
void initPGPDeenveloping( ENVELOPE_INFO *envelopeInfoPtr )
{
/* Set the access method pointers */
envelopeInfoPtr->processPreamble = processPreamble;
envelopeInfoPtr->processPostamble = processPostamble;
/* Set up the processing state information */
envelopeInfoPtr->pgpDeenvState = PGP_DEENVSTATE_NONE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -