📄 encode.c
字号:
/****************************************************************************
* *
* cryptlib Datagram Encoding Routines *
* Copyright Peter Gutmann 1996-2008 *
* *
****************************************************************************/
#if defined( INC_ALL )
#include "envelope.h"
#include "asn1.h"
#else
#include "envelope/envelope.h"
#include "misc/asn1.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 );
/* If we're drained the envelope buffer, we're done */
if( envelopeInfoPtr->segmentStart == 0 && \
envelopeInfoPtr->segmentDataStart == 0 && \
envelopeInfoPtr->bufPos == 0 )
return( TRUE );
/* Make sure that the buffer internal bookeeping is OK. First we apply
the general one-size-fits-all checks, then we apply further
situation-specific checks */
if( envelopeInfoPtr->segmentStart < 0 || \
envelopeInfoPtr->segmentStart > envelopeInfoPtr->segmentDataStart || \
envelopeInfoPtr->segmentStart > MAX_INTLENGTH )
return( FALSE );
if( envelopeInfoPtr->segmentDataStart < 0 || \
envelopeInfoPtr->segmentDataStart > envelopeInfoPtr->bufPos || \
envelopeInfoPtr->segmentDataStart > MAX_INTLENGTH )
return( FALSE );
/* The sitaution-specific checks get a bit complicated because we have
to distinguish between definite- and indefinite-length encodings.
For the definite length segmentStart == segmentDataStart since there
are no intermediate segment headers, for the indefinite length
segmentStart < segmentDataStart to accomodate the intervening header.
In some rare cases segmentDataStart can be the same as bufPos if
we're using compression and all input data was absorbed by the
zStream buffer so we check for segmentDataStart > bufPos rather than
segmentDataStart >= bufPos */
if( envelopeInfoPtr->payloadSize != CRYPT_UNUSED )
{
/* It's a non-segmenting encoding so segmentStart must track
segmentDataStart */
if( envelopeInfoPtr->segmentStart != \
envelopeInfoPtr->segmentDataStart )
return( FALSE );
return( TRUE );
}
if( envelopeInfoPtr->segmentStart >= envelopeInfoPtr->bufPos )
return( FALSE );
if( envelopeInfoPtr->dataFlags & ENVDATA_SEGMENTCOMPLETE )
{
/* If we're just started a new segment and there's no data left in
the envelope buffer, segmentStart may be the same as
segmentDataStart */
if( envelopeInfoPtr->segmentStart == 0 && \
envelopeInfoPtr->segmentDataStart == 0 )
return( TRUE );
}
if( envelopeInfoPtr->segmentStart >= envelopeInfoPtr->segmentDataStart )
return( FALSE );
return( TRUE );
}
/* Apply the hash/MAC actions in the action list to data. This function is
shared with decode.c */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2 ) ) \
int hashEnvelopeData( const ACTION_LIST *hashActionPtr,
IN_BUFFER( dataLength ) const void *data,
IN_LENGTH const int dataLength )
{
int iterationCount, status;
assert( isReadPtr( hashActionPtr, sizeof( ACTION_LIST ) ) );
assert( dataLength == 0 || isReadPtr( data, dataLength ) );
REQUIRES( data != NULL );
REQUIRES( dataLength >= 0 && dataLength < MAX_INTLENGTH );
for( iterationCount = 0;
hashActionPtr != NULL && \
( hashActionPtr->action == ACTION_HASH || \
hashActionPtr->action == ACTION_MAC ) && \
iterationCount < FAILSAFE_ITERATIONS_MED;
hashActionPtr = hashActionPtr->next, iterationCount++ )
{
status = krnlSendMessage( hashActionPtr->iCryptHandle,
IMESSAGE_CTX_HASH, ( void * ) data,
dataLength );
if( cryptStatusError( status ) )
return( status );
}
ENSURES( iterationCount < FAILSAFE_ITERATIONS_MED );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Header Processing Routines *
* *
****************************************************************************/
/* Determine the length of the encoded length value and the threshold at which the
length encoding changes for constructed indefinite-length strings. The
length encoding is the actual length if <= 127, or a one-byte length-of-
length followed by the length if > 127 */
#define TAG_SIZE 1 /* Useful symbolic define */
#if INT_MAX > 32767
#define lengthOfLength( length ) ( ( length < 0x80 ) ? 1 : \
( length < 0x100 ) ? 2 : \
( length < 0x10000 ) ? 3 : \
( length < 0x1000000 ) ? 4 : 5 )
#define findThreshold( length ) ( ( length < 0x80 ) ? 0x7F : \
( length < 0x100 ) ? 0xFF : \
( length < 0x10000 ) ? 0xFFFF : \
( length < 0x1000000 ) ? 0xFFFFFF : INT_MAX )
#else
#define lengthOfLength( length ) ( ( length < 0x80 ) ? 1 : \
( length < 0x100 ) ? 2 : 3 )
#define findThreshold( length ) ( ( length < 0x80 ) ? 127 : \
( length < 0x100 ) ? 0xFF : INT_MAX )
#endif /* 32-bit ints */
/* Begin a new segment in the buffer. The layout is:
bufPos
v
tag len payload
+-------+-+---+---------------------+-------+
| | | | | |
+-------+-+---+---------------------+-------+
^ ^ ^
| | |
sStart sDataStart sDataEnd
If we're using a definite-length encoding then
segmentStart == segmentDataStart = bufPos */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int beginSegment( INOUT ENVELOPE_INFO *envelopeInfoPtr )
{
int segHeaderSize = 0;
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
REQUIRES( sanityCheck( envelopeInfoPtr ) );
/* If we're using an indefinite-length encoding we have to factor in the
length of the segment header. Since we can't determine the overall
segment data size at this point we use a worst-case estimate in which
the segment fills the entire buffer */
if( envelopeInfoPtr->payloadSize == CRYPT_UNUSED )
{
segHeaderSize = TAG_SIZE + \
lengthOfLength( envelopeInfoPtr->bufSize );
}
/* Make sure that there's enough room in the buffer to accommodate the
start of a new segment. In the worst case this is 6 bytes (OCTET
STRING tag + 5-byte length) + 15 bytes (blockBuffer contents for a
128-bit block cipher). Although in practice we could eliminate this
condition, it would require tracking a lot of state information to
record which data had been encoded into the buffer and whether the
blockBuffer data had been copied into the buffer, so to keep it
simple we require enough room to do everything at once */
if( envelopeInfoPtr->bufPos + segHeaderSize + \
envelopeInfoPtr->blockBufferPos >= envelopeInfoPtr->bufSize )
return( CRYPT_ERROR_OVERFLOW );
/* Adjust the buffer position indicators to handle potential
intermediate headers */
envelopeInfoPtr->segmentStart = envelopeInfoPtr->bufPos;
if( envelopeInfoPtr->payloadSize == CRYPT_UNUSED )
{
/* Begin a new segment after the end of the current segment. We
always leave enough room for the largest allowable length field
because we may have a short segment at the end of the buffer which
is moved to the start of the buffer after data is copied out,
turning it into a longer segment. For this reason we rely on
completeSegment() to get the length right and move any data down
as required */
envelopeInfoPtr->bufPos += segHeaderSize;
}
envelopeInfoPtr->segmentDataStart = envelopeInfoPtr->bufPos;
ENSURES( envelopeInfoPtr->bufPos + \
envelopeInfoPtr->blockBufferPos <= envelopeInfoPtr->bufSize );
/* Now copy anything left in the block buffer to the start of the new
segment. We know that everything will fit because we've checked
earlier on that the header and blockbuffer contents will fit into
the remaining space */
if( envelopeInfoPtr->blockBufferPos > 0 )
{
memcpy( envelopeInfoPtr->buffer + envelopeInfoPtr->bufPos,
envelopeInfoPtr->blockBuffer, envelopeInfoPtr->blockBufferPos );
envelopeInfoPtr->bufPos += envelopeInfoPtr->blockBufferPos;
}
envelopeInfoPtr->blockBufferPos = 0;
/* We've started the new segment, mark it as incomplete */
envelopeInfoPtr->dataFlags &= ~ENVDATA_SEGMENTCOMPLETE;
ENSURES( sanityCheck( envelopeInfoPtr ) );
return( CRYPT_OK );
}
/* Complete a segment of data in the buffer. This is incredibly complicated
because we need to take into account the indefinite-length encoding (which
has a variable-size length field) and the quantization to the cipher block
size. In particular the indefinite-length encoding means that we can
never encode a block with a size of 130 bytes (we get tag + length + 127 =
129, then tag + length-of-length + length + 128 = 131), and the same for
the next boundary at 256 bytes */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int encodeSegmentHeader( INOUT ENVELOPE_INFO *envelopeInfoPtr )
{
STREAM stream;
const BOOLEAN isEncrypted = \
( envelopeInfoPtr->iCryptContext != CRYPT_ERROR ) ? TRUE : FALSE;
const int oldHdrLen = envelopeInfoPtr->segmentDataStart - \
envelopeInfoPtr->segmentStart;
BOOLEAN needsPadding = \
( envelopeInfoPtr->dataFlags & ENVDATA_NEEDSPADDING ) ? TRUE : FALSE;
int dataLen = envelopeInfoPtr->bufPos - envelopeInfoPtr->segmentDataStart;
int hdrLen, remainder = 0, status;
REQUIRES( sanityCheck( envelopeInfoPtr ) );
/* If we're adding PKCS #5 padding try and add one block's worth of
pseudo-data. This adjusted data length is then fed into the block
size quantisation process, after which any odd-sized remainder is
ignored, and the necessary padding bytes are added to account for the
difference between the actual and padded size */
if( needsPadding )
{
/* Check whether the padding will fit onto the end of the data. This
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -