📄 decode.c
字号:
/****************************************************************************
* *
* cryptlib Datagram Decoding Routines *
* Copyright Peter Gutmann 1996-2008 *
* *
****************************************************************************/
#if defined( INC_ALL )
#include "envelope.h"
#include "asn1.h"
#include "misc_rw.h"
#include "pgp_rw.h"
#else
#include "envelope/envelope.h"
#include "misc/asn1.h"
#include "misc/misc_rw.h"
#include "misc/pgp_rw.h"
#endif /* Compiler-specific includes */
/* .... NO! ... ... MNO! ...
..... MNO!! ...................... MNNOO! ...
..... MMNO! ......................... MNNOO!! .
.... MNOONNOO! MMMMMMMMMMPPPOII! MNNO!!!! .
... !O! NNO! MMMMMMMMMMMMMPPPOOOII!! NO! ....
...... ! MMMMMMMMMMMMMPPPPOOOOIII! ! ...
........ MMMMMMMMMMMMPPPPPOOOOOOII!! .....
........ MMMMMOOOOOOPPPPPPPPOOOOMII! ...
....... MMMMM.. OPPMMP .,OMI! ....
...... MMMM:: o.,OPMP,.o ::I!! ...
.... NNM:::.,,OOPM!P,.::::!! ....
.. MMNNNNNOOOOPMO!!IIPPO!!O! .....
... MMMMMNNNNOO:!!:!!IPPPPOO! ....
.. MMMMMNNOOMMNNIIIPPPOO!! ......
...... MMMONNMMNNNIIIOO!..........
....... MN MOMMMNNNIIIIIO! OO ..........
......... MNO! IiiiiiiiiiiiI OOOO ...........
...... NNN.MNO! . O!!!!!!!!!O . OONO NO! ........
.... MNNNNNO! ...OOOOOOOOOOO . MMNNON!........
...... MNNNNO! .. PPPPPPPPP .. MMNON!........
...... OO! ................. ON! .......
................................
Be very careful when modifying this code, the data manipulation that it
performs is somewhat tricky */
#ifdef USE_ENVELOPES
/****************************************************************************
* *
* Utility Routines *
* *
****************************************************************************/
/* Sanity-check the envelope state */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static BOOLEAN sanityCheck( const ENVELOPE_INFO *envelopeInfoPtr )
{
assert( isReadPtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
/* 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 block buffer position is within bounds */
if( envelopeInfoPtr->blockSize > 0 && \
( envelopeInfoPtr->blockBufferPos < 0 || \
envelopeInfoPtr->blockBufferPos >= envelopeInfoPtr->blockSize || \
envelopeInfoPtr->blockSize > CRYPT_MAX_IVSIZE ) )
return( FALSE );
/* Make sure that the out-of-band data buffer is within bounds */
if( envelopeInfoPtr->oobBufPos < 0 || \
envelopeInfoPtr->oobBufPos > OOB_BUFFER_SIZE )
return( FALSE );
/* Make sure that the envelope internal bookeeping is OK */
if( envelopeInfoPtr->segmentSize < 0 || \
envelopeInfoPtr->segmentSize >= MAX_INTLENGTH || \
envelopeInfoPtr->dataLeft < 0 || \
envelopeInfoPtr->dataLeft >= MAX_INTLENGTH )
return( FALSE );
return( TRUE );
}
/****************************************************************************
* *
* Header Processing Routines *
* *
****************************************************************************/
/* Handle the end-of-data and PKCS #5 block padding if necessary:
pad
+-------+-------+-------+
| | | |
+-------+-------+-------+
^ ^
| |
padPtr bPos */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int processDataEnd( INOUT ENVELOPE_INFO *envelopeInfoPtr )
{
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
REQUIRES( sanityCheck( envelopeInfoPtr ) );
/* If we're using a block cipher, undo the PKCS #5 padding which is
present at the end of the block */
if( envelopeInfoPtr->blockSize > 1 )
{
int padSize, i;
REQUIRES( envelopeInfoPtr->bufPos > 0 );
/* Make sure that the padding size is valid */
padSize = envelopeInfoPtr->buffer[ envelopeInfoPtr->bufPos - 1 ];
if( padSize < 1 || padSize > envelopeInfoPtr->blockSize || \
padSize > envelopeInfoPtr->bufPos )
return( CRYPT_ERROR_BADDATA );
/* Check the padding data */
envelopeInfoPtr->bufPos -= padSize;
for( i = 0; i < padSize - 1; i++ )
{
if( envelopeInfoPtr->buffer[ envelopeInfoPtr->bufPos + i ] != padSize )
return( CRYPT_ERROR_BADDATA );
}
ENSURES( envelopeInfoPtr->bufPos >= 0 && \
envelopeInfoPtr->bufPos < MAX_INTLENGTH );
}
/* Remember that we've reached the end of the payload and where the
payload ends ("This was the end of the river all right") */
envelopeInfoPtr->dataFlags |= ENVDATA_ENDOFCONTENTS;
envelopeInfoPtr->dataLeft = envelopeInfoPtr->bufPos;
ENSURES( sanityCheck( envelopeInfoPtr ) );
return( CRYPT_OK );
}
/* Process a sub-segment */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
static int processSegment( INOUT ENVELOPE_INFO *envelopeInfoPtr,
INOUT STREAM *stream,
OUT_LENGTH_Z long *segmentLength )
{
int status;
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( segmentLength, sizeof( long ) ) );
REQUIRES( sanityCheck( envelopeInfoPtr ) );
/* Clear return value */
*segmentLength = 0;
/* Check for the EOCs that mark the end of the overall data */
status = checkEOC( stream );
if( cryptStatusError( status ) )
return( status );
if( status == TRUE )
{
/* We've seen the EOC, wrap up the processing */
return( processDataEnd( envelopeInfoPtr ) );
}
/* It's a new sub-segment, get its length */
status = readLongGenericHole( stream, segmentLength, BER_OCTETSTRING );
if( cryptStatusError( status ) )
return( status );
if( *segmentLength == CRYPT_UNUSED )
{
/* An indefinite-length encoding within a constructed data item
isn't allowed */
return( CRYPT_ERROR_BADDATA );
}
return( CRYPT_OK );
}
#ifdef USE_PGP
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 3 ) ) \
static int processPgpSegment( INOUT ENVELOPE_INFO *envelopeInfoPtr,
INOUT STREAM *stream,
OUT_LENGTH_Z long *segmentLength )
{
int status;
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
assert( isWritePtr( stream, sizeof( STREAM ) ) );
assert( isWritePtr( segmentLength, sizeof( long ) ) );
REQUIRES( sanityCheck( envelopeInfoPtr ) );
/* Clear return value */
*segmentLength = 0;
/* Get the next sub-segment's length */
status = pgpReadPartialLength( stream, segmentLength );
if( cryptStatusError( status ) )
{
/* If we get an OK_SPECIAL returned it's just an indication that
we've got another partial length (with other segments to follow)
and not an actual error */
if( status == OK_SPECIAL )
return( CRYPT_OK );
/* Alongside normal errors this may also be an OK_SPECIAL to
indicate that we got another partial length (with other segments
to follow), which the caller will handle as a non-error */
return( status );
}
/* We've read a length that doesn't use the indefinite-length encoding
so it's the last data segment, shift from indefinite to definite-
length mode */
#if 0 /* 2/04/08 It's not really a NOSEGMENT it's just a short definite-
length segment */
envelopeInfoPtr->dataFlags |= ENVDATA_NOSEGMENT;
#endif /* 0 */
if( *segmentLength <= 0 )
{
/* It's a terminating zero-length segment, wrap up the processing.
Unlike CMS, PGP can add other odds and ends at this point so we
don't exit yet but fall through to the code that follows */
status = processDataEnd( envelopeInfoPtr );
if( cryptStatusError( status ) )
return( status );
}
/* If this is a packet with an MDC packet tacked on, adjust the data
length for the length of the MDC packet */
if( envelopeInfoPtr->dataFlags & ENVDATA_HASATTACHEDOOB )
{
/* If the MDC data is larger than the length of the last segment,
adjust its effefctive size to zero. This is rather problematic
in that if the sender chooses to break the MDC packet across the
partial-header boundary it'll include some of the MDC data with
the payload, but there's no easy solution to this, the problem
lies in the PGP spec for allowing a length encoding form that
makes one-pass processing impossible. Hopefully implementations
will realise this and never break the MDC data over a partial-
length header */
*segmentLength -= PGP_MDC_PACKET_SIZE;
if( *segmentLength < 0 )
{
assert( DEBUG_WARN );
*segmentLength = 0;
}
}
/* Convert the last segment into a definite-length segment. When we
return from this the calling code will immediately call
getNextSegment() again since we've consumed some input, at which
point the definite-length payload size will be set and the call will
return with OK_SPECIAL to tell the caller that there's no more length
information to fetch */
envelopeInfoPtr->payloadSize = *segmentLength;
*segmentLength = 0;
ENSURES( sanityCheck( envelopeInfoPtr ) );
return( CRYPT_OK );
}
#endif /* USE_PGP */
/* Decode the header for the next segment in the buffer. Returns the number
of bytes consumed or zero if more data is required to decode the header */
typedef enum {
SEGMENT_NONE, /* No segment status */
SEGMENT_FIXEDLENGTH, /* Single fixed-length segment, no more segments
to process */
SEGMENT_INSUFFICIENTDATA,/* Need more data to continue */
SEGMENT_ENDOFDATA, /* No more data to process */
SEGMENT_LAST /* Last possible segment status */
} SEGMENT_STATUS;
CHECK_RETVAL_SPECIAL STDC_NONNULL_ARG( ( 1, 2, 4, 5 ) ) \
static int getNextSegment( INOUT ENVELOPE_INFO *envelopeInfoPtr,
IN_BUFFER( length ) const BYTE *buffer,
IN_LENGTH const int length,
OUT_LENGTH_SHORT_Z int *bytesConsumed,
OUT_ENUM_OPT( SEGMENT ) SEGMENT_STATUS *segmentStatus )
{
STREAM stream;
long segmentLength;
int status;
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
assert( isReadPtr( buffer, length ) );
assert( isWritePtr( bytesConsumed, sizeof( int ) ) );
assert( isWritePtr( segmentStatus, sizeof( SEGMENT_STATUS ) ) );
REQUIRES( sanityCheck( envelopeInfoPtr ) );
REQUIRES( length > 0 && length < MAX_INTLENGTH );
/* Clear return values */
*bytesConsumed = 0;
*segmentStatus = SEGMENT_NONE;
/* If we've already processed the entire payload, don't do anything.
This can happen when we're using the definite encoding form, since
the EOC flag is set elsewhere as soon as the entire payload has been
copied to the buffer */
if( envelopeInfoPtr->dataFlags & ENVDATA_ENDOFCONTENTS )
{
REQUIRES( envelopeInfoPtr->segmentSize <= 0 );
*segmentStatus = SEGMENT_ENDOFDATA;
return( OK_SPECIAL );
}
/* If we're using the definite encoding form there's a single segment
equal in length to the entire payload */
if( envelopeInfoPtr->payloadSize != CRYPT_UNUSED )
{
envelopeInfoPtr->segmentSize = envelopeInfoPtr->payloadSize;
*segmentStatus = SEGMENT_FIXEDLENGTH;
return( OK_SPECIAL );
}
/* If we're using the indefinite form but it's an envelope type that
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -