📄 env_enc.c
字号:
/****************************************************************************
* *
* cryptlib Datatgram Encoding Routines *
* Copyright Peter Gutmann 1996-2003 *
* *
****************************************************************************/
#include <string.h>
#if defined( INC_ALL )
#include "envelope.h"
#include "asn1_rw.h"
#elif defined( INC_CHILD )
#include "envelope.h"
#include "../misc/asn1_rw.h"
#else
#include "envelope/envelope.h"
#include "misc/asn1_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 it
performs is somewhat tricky */
/****************************************************************************
* *
* Header Processing Routines *
* *
****************************************************************************/
/* Determine the quantization level and length threshold for the length
encoding of 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 < 128 ) ? 1 : \
( length < 0xFF ) ? 2 : \
( length < 0xFFFF ) ? 3 : \
( length < 0xFFFFFF ) ? 4 : 5 )
#define findThreshold( length ) ( ( length < 128 ) ? 127 : \
( length < 0xFF ) ? ( 0xFF - 1 ) : \
( length < 0xFFFF ) ? ( 0xFFFF - 1 ) : \
( length < 0xFFFFFF ) ? ( 0xFFFFFF - 1 ) : INT_MAX )
#else
#define lengthOfLength( length ) ( ( length < 128 ) ? 1 : \
( length < 0xFF ) ? 2 : 3 )
#define findThreshold( length ) ( ( length < 128 ) ? 127 : \
( length < 0xFF ) ? ( 0xFF - 1 ) : INT_MAX )
#endif /* 32-bit ints */
/* Begin a new segment in the buffer. The layout is:
tag len payload
+-------+-+---+---------------------+-------+
| | | | | |
+-------+-+---+---------------------+-------+
^ ^ ^
| | |
sStart sDataStart sDataEnd
The segment starts at segmentDataStart - TAG_SIZE */
static int beginSegment( ENVELOPE_INFO *envelopeInfoPtr )
{
const int lLen = lengthOfLength( envelopeInfoPtr->bufSize );
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
assert( envelopeInfoPtr->bufPos >= 0 && \
envelopeInfoPtr->bufPos <= envelopeInfoPtr->bufSize );
assert( ( envelopeInfoPtr->blockSize == 0 ) || \
( envelopeInfoPtr->blockBufferPos >= 0 && \
envelopeInfoPtr->blockBufferPos < envelopeInfoPtr->blockSize ) );
/* 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->bufSize - envelopeInfoPtr->bufPos < \
TAG_SIZE + lLen + envelopeInfoPtr->blockBufferPos )
return( CRYPT_ERROR_OVERFLOW );
/* If we're encoding data with a definite length, there's no real segment
boundary apart from the artificial ones created by encryption
blocking */
if( envelopeInfoPtr->payloadSize != CRYPT_UNUSED )
envelopeInfoPtr->segmentStart = envelopeInfoPtr->bufPos;
else
{
/* 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 the
completeSegment() code to get the length right and move any data
down as required */
envelopeInfoPtr->buffer[ envelopeInfoPtr->bufPos ] = BER_OCTETSTRING;
envelopeInfoPtr->segmentStart = envelopeInfoPtr->bufPos + TAG_SIZE;
envelopeInfoPtr->bufPos += TAG_SIZE + lLen;
}
envelopeInfoPtr->segmentDataStart = envelopeInfoPtr->bufPos;
/* 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;
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 */
static BOOLEAN encodeSegmentHeader( ENVELOPE_INFO *envelopeInfoPtr,
const BOOLEAN isEncrypted )
{
BYTE *segmentDataPtr = envelopeInfoPtr->buffer + \
envelopeInfoPtr->segmentStart;
const int oldLLen = TAG_SIZE + ( envelopeInfoPtr->segmentDataStart - \
envelopeInfoPtr->segmentStart );
int dLen = envelopeInfoPtr->bufPos - envelopeInfoPtr->segmentDataStart;
int lLen, qTot, threshold, remainder = 0;
BOOLEAN needsPadding = envelopeInfoPtr->dataFlags & ENVDATA_NEEDSPADDING;
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
assert( envelopeInfoPtr->bufPos >= 0 && \
envelopeInfoPtr->bufPos <= envelopeInfoPtr->bufSize );
assert( envelopeInfoPtr->segmentStart >= 0 && \
envelopeInfoPtr->segmentStart < envelopeInfoPtr->bufPos );
assert( envelopeInfoPtr->segmentDataStart >= \
envelopeInfoPtr->segmentStart && \
envelopeInfoPtr->segmentDataStart < envelopeInfoPtr->bufPos );
/* 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
check isn't completely accurate since the length encoding might
shrink by one or two bytes and allow a little extra data to be
squeezed in, however the extra data could cause the length
encoding to expand again, requiring a complex adjustment process.
To make things easier we ignore this possibility at the expense of
emitting one more segment than is necessary in a few very rare
cases */
if( envelopeInfoPtr->segmentDataStart + dLen + \
envelopeInfoPtr->blockSize < envelopeInfoPtr->bufSize )
dLen += envelopeInfoPtr->blockSize;
else
needsPadding = FALSE;
}
/* Now that we've made any necessary adjustments to the data length,
determine the length of the length encoding (which may have grown or
shrunk since we initially calculated it when we began the segment) and
any combined data lengths based on it */
lLen = ( envelopeInfoPtr->payloadSize == CRYPT_UNUSED ) ? \
TAG_SIZE + lengthOfLength( dLen ) : 0;
qTot = lLen + dLen;
/* Quantize and adjust the length if we're encrypting in a block mode */
if( isEncrypted )
{
qTot = dLen & envelopeInfoPtr->blockSizeMask;
threshold = findThreshold( qTot );
if( qTot <= threshold && dLen > threshold )
/* The block-size quantisation has moved the quantised length
across a length-of-length encoding boundary, adjust lLen to
account for this */
lLen--;
remainder = dLen - qTot;
dLen = qTot; /* Data length has now shrunk to quantised size */
}
assert( ( envelopeInfoPtr->payloadSize != CRYPT_UNUSED && lLen == 0 ) || \
( envelopeInfoPtr->payloadSize == CRYPT_UNUSED && \
lLen > 0 && lLen <= 6 ) );
assert( remainder >= 0 && \
( envelopeInfoPtr->blockSize == 0 || \
remainder < envelopeInfoPtr->blockSize ) );
/* If there's not enough data present to do anything, tell the caller
that we couldn't do anything */
if( qTot <= 0 )
return( FALSE );
assert( dLen >= 0 );
/* If there's a header between segments and the header length encoding
has shrunk (either due to the cipher block size quantization shrinking
the segment or because we've wrapped up a segment at less than the
original projected length), move the data down. The complete segment
starts at segmentStart - TAG_SIZE, in the worst case the shrinking can
cover several bytes if we go from a > 255 byte segment to a <= 127
byte one */
if( lLen > 0 && lLen < oldLLen )
{
const int delta = oldLLen - lLen;
memmove( segmentDataPtr - TAG_SIZE + lLen,
segmentDataPtr - TAG_SIZE + oldLLen,
envelopeInfoPtr->bufPos - envelopeInfoPtr->segmentDataStart );
envelopeInfoPtr->bufPos -= delta;
envelopeInfoPtr->segmentDataStart -= delta;
}
assert( envelopeInfoPtr->bufPos >= 0 && \
envelopeInfoPtr->bufPos <= envelopeInfoPtr->bufSize );
assert( envelopeInfoPtr->segmentDataStart >= \
envelopeInfoPtr->segmentStart && \
envelopeInfoPtr->segmentDataStart + dLen <= \
envelopeInfoPtr->bufSize );
/* If we need to add PKCS #5 block padding, try and do so now. Since the
extension of the data length to allow for padding data is performed by
adding one block of pseudo-data and letting the block quantisation
system take care of any discrepancies, we can calculate the padding
amount as the difference between any remainder after quantisation and
the block size */
if( needsPadding )
{
const int padSize = envelopeInfoPtr->blockSize - remainder;
int i;
/* Add the block padding and set the remainder to zero, since we're
now at an even block boundary */
for( i = 0; i < padSize; i++ )
envelopeInfoPtr->buffer[ envelopeInfoPtr->bufPos + i ] = padSize;
envelopeInfoPtr->bufPos += padSize;
envelopeInfoPtr->dataFlags &= ~ENVDATA_NEEDSPADDING;
remainder = 0;
}
/* Move any leftover bytes into the block buffer */
if( remainder > 0 )
{
memcpy( envelopeInfoPtr->blockBuffer,
envelopeInfoPtr->buffer + envelopeInfoPtr->bufPos - \
remainder, remainder );
envelopeInfoPtr->blockBufferPos = remainder;
envelopeInfoPtr->bufPos -= remainder;
}
/* If we're using the definite length form, exit */
if( envelopeInfoPtr->payloadSize != CRYPT_UNUSED )
return( TRUE );
/* If it's a short length we can encode it in a single byte */
if( dLen < 128 )
{
*segmentDataPtr = dLen;
return( TRUE );
}
/* It's a long length, encode it as a variable-length value */
lLen -= 2; /* Tag + length of length */
*segmentDataPtr++ = 0x80 | lLen;
#if INT_MAX > 32767
if( lLen > 3 )
{
*segmentDataPtr++ = dLen >> 24;
dLen &= 0xFFFFFFL;
}
if( lLen > 2 )
{
*segmentDataPtr++ = dLen >> 16;
dLen &= 0xFFFFL;
}
#endif /* 32-bit ints */
if( lLen > 1 )
{
*segmentDataPtr++ = dLen >> 8;
dLen &= 0xFF;
}
*segmentDataPtr++ = dLen;
return( TRUE );
}
static int completeSegment( ENVELOPE_INFO *envelopeInfoPtr,
const BOOLEAN forceCompletion )
{
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
assert( envelopeInfoPtr->bufPos >= 0 && \
envelopeInfoPtr->bufPos <= envelopeInfoPtr->bufSize );
/* If we're enveloping data using indefinite encoding and we're not at
the end of the data, don't emit a sub-segment containing less then 10
bytes of data. This is to protect against users who write code that
performs byte-at-a-time enveloping, at least we can quantize the data
amount to make it slightly more efficient. As a side-effect, it
avoids occasional inefficiencies at boundaries where one or two bytes
may still be hanging around from a previous data block, since they'll
be coalesced into the following block */
if( !forceCompletion && \
!( envelopeInfoPtr->flags & ENVELOPE_ISDEENVELOPE ) && \
envelopeInfoPtr->payloadSize == CRYPT_UNUSED && \
( envelopeInfoPtr->bufPos - envelopeInfoPtr->segmentDataStart ) < 10 )
{
/* We can't emit any of the small sub-segment, however there may be
(non-)data preceding this that we can hand over so we set the
segment data end value to the start of the segment (the complete
segment starts at segmentStart - TAG_SIZE) */
envelopeInfoPtr->segmentDataEnd = envelopeInfoPtr->segmentStart - \
TAG_SIZE;
return( CRYPT_OK );
}
/* Wrap up the segment */
if( !( envelopeInfoPtr->dataFlags & ENVDATA_NOSEGMENT ) && \
!encodeSegmentHeader( envelopeInfoPtr,
( envelopeInfoPtr->iCryptContext != CRYPT_ERROR ) ? \
TRUE : FALSE ) )
/* Not enough data to complete the segment */
return( CRYPT_ERROR_UNDERFLOW );
if( envelopeInfoPtr->iCryptContext != CRYPT_ERROR )
{
int status;
status = krnlSendMessage( envelopeInfoPtr->iCryptContext,
IMESSAGE_CTX_ENCRYPT,
envelopeInfoPtr->buffer + \
envelopeInfoPtr->segmentDataStart,
envelopeInfoPtr->bufPos - \
envelopeInfoPtr->segmentDataStart );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -