📄 encode.c
字号:
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 + dataLen + \
envelopeInfoPtr->blockSize < envelopeInfoPtr->bufSize )
dataLen += envelopeInfoPtr->blockSize;
else
needsPadding = FALSE;
}
ENSURES( dataLen > 0 && envelopeInfoPtr->segmentDataStart + \
dataLen <= envelopeInfoPtr->bufSize );
/* 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) */
hdrLen = ( envelopeInfoPtr->payloadSize == CRYPT_UNUSED ) ? \
TAG_SIZE + lengthOfLength( dataLen ) : 0;
/* Quantize and adjust the length if we're encrypting in a block mode:
segDataStart bufPos
segStart | |
v v<--------- dLen ---------->v
----+-------+---------------------------+
| hdr |///////////////////////| |
----+-------+---------------------------+
|<------ qTotLen ------>|<+>|
|
remainder */
if( isEncrypted )
{
int quantisedTotalLen, threshold;
/* Determine the length due to cipher block-size quantisation */
quantisedTotalLen = dataLen & envelopeInfoPtr->blockSizeMask;
/* If the block-size quantisation has moved the quantised length
across a length-of-length encoding boundary, adjust hdrLen to
account for this */
threshold = findThreshold( quantisedTotalLen );
if( quantisedTotalLen <= threshold && dataLen > threshold )
hdrLen--;
/* Remember how many bytes we can't fit into the current block
(these will be copied into the block buffer for later use), and
the new size of the data due to quantisation */
remainder = dataLen - quantisedTotalLen;
dataLen = quantisedTotalLen;
}
ENSURES( ( envelopeInfoPtr->payloadSize != CRYPT_UNUSED && hdrLen == 0 ) || \
( envelopeInfoPtr->payloadSize == CRYPT_UNUSED && \
hdrLen > 0 && hdrLen <= TAG_SIZE + 5 ) );
ENSURES( ( envelopeInfoPtr->blockSize == 0 && remainder == 0 ) || \
( envelopeInfoPtr->blockSize > 0 && \
remainder >= 0 && remainder < envelopeInfoPtr->blockSize && \
remainder <= CRYPT_MAX_IVSIZE ) );
/* If there's not enough data present to do anything, tell the caller */
if( dataLen <= 0 )
return( CRYPT_ERROR_UNDERFLOW );
ENSURES( dataLen > 0 && dataLen < MAX_INTLENGTH );
/* 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. In the
worst case the shrinking can cover several bytes if we go from a
> 255 byte segment to a <= 127 byte one:
segDataStart bufPos
segStart | |
v v v
----+-------+---------------------------+
| hdr |///////////////////////////| Before
----+-------+---------------------------+
|<--+-->|
|
oldHdrLen
segDataStart' bufPos'
segStart| |
v v v
----+-------+-----------------------+---+
|hdr|///////////////////////////| | After
----+-------+-----------------------+---+
|<+>|<+>| |<+>|
| | |
| delta delta
hdrLen */
if( hdrLen > 0 && hdrLen < oldHdrLen )
{
BYTE *segmentDataPtr = envelopeInfoPtr->buffer + \
envelopeInfoPtr->segmentStart;
const int delta = oldHdrLen - hdrLen;
memmove( segmentDataPtr + hdrLen, segmentDataPtr + oldHdrLen,
envelopeInfoPtr->bufPos - envelopeInfoPtr->segmentDataStart );
envelopeInfoPtr->bufPos -= delta;
envelopeInfoPtr->segmentDataStart -= delta;
}
ENSURES( sanityCheck( envelopeInfoPtr ) );
ENSURES( envelopeInfoPtr->segmentDataStart + \
dataLen <= envelopeInfoPtr->bufSize );
/* If we need to add PKCS #5 block padding, do so now (we know from the
needsPadding and quantisedTotalLen check above that there's enough
room for this). 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;
ENSURES( padSize > 0 && padSize <= envelopeInfoPtr->blockSize && \
envelopeInfoPtr->bufPos + \
padSize <= envelopeInfoPtr->bufSize );
/* 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;
ENSURES( envelopeInfoPtr->bufPos >= 0 && \
envelopeInfoPtr->bufPos <= envelopeInfoPtr->bufSize );
}
else
{
/* If there are any bytes left over move them across into the block
buffer */
if( remainder > 0 )
{
REQUIRES( envelopeInfoPtr->bufPos > remainder );
memcpy( envelopeInfoPtr->blockBuffer,
envelopeInfoPtr->buffer + envelopeInfoPtr->bufPos - \
remainder, remainder );
envelopeInfoPtr->blockBufferPos = remainder;
envelopeInfoPtr->bufPos -= remainder;
}
}
ENSURES( sanityCheck( envelopeInfoPtr ) );
/* If we're using the definite length form, exit */
if( envelopeInfoPtr->payloadSize != CRYPT_UNUSED )
return( CRYPT_OK );
/* Insert the OCTET STRING header into the data stream */
sMemOpen( &stream, envelopeInfoPtr->buffer + \
envelopeInfoPtr->segmentStart, hdrLen );
status = writeOctetStringHole( &stream, dataLen, DEFAULT_TAG );
ENSURES( cryptStatusOK( status ) && stell( &stream ) == hdrLen );
sMemDisconnect( &stream );
return( CRYPT_OK );
}
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int completeSegment( INOUT ENVELOPE_INFO *envelopeInfoPtr,
const BOOLEAN forceCompletion )
{
int status;
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
REQUIRES( sanityCheck( envelopeInfoPtr ) );
/* 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->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 */
envelopeInfoPtr->segmentDataEnd = envelopeInfoPtr->segmentStart;
return( CRYPT_OK );
}
/* Wrap up the segment */
if( !( envelopeInfoPtr->dataFlags & ENVDATA_NOSEGMENT ) )
{
status = encodeSegmentHeader( envelopeInfoPtr );
if( cryptStatusError( status ) )
return( status );
}
if( envelopeInfoPtr->iCryptContext != CRYPT_ERROR )
{
status = krnlSendMessage( envelopeInfoPtr->iCryptContext,
IMESSAGE_CTX_ENCRYPT,
envelopeInfoPtr->buffer + \
envelopeInfoPtr->segmentDataStart,
envelopeInfoPtr->bufPos - \
envelopeInfoPtr->segmentDataStart );
if( cryptStatusError( status ) )
return( status );
}
/* Remember how much data is now available to be read out */
envelopeInfoPtr->segmentDataEnd = envelopeInfoPtr->bufPos;
/* Mark this segment as complete */
envelopeInfoPtr->dataFlags |= ENVDATA_SEGMENTCOMPLETE;
return( CRYPT_OK );
}
/****************************************************************************
* *
* Copy to Envelope *
* *
****************************************************************************/
/* Flush any remaining data through into the envelope buffer */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1 ) ) \
static int flushEnvelopeData( INOUT ENVELOPE_INFO *envelopeInfoPtr )
{
BOOLEAN needNewSegment = \
( envelopeInfoPtr->dataFlags & ENVDATA_NEEDSPADDING ) ? \
TRUE : FALSE;
int status;
REQUIRES( sanityCheck( envelopeInfoPtr ) );
/* If we're using an explicit payload length make sure that we copied in
as much data as was explicitly declared */
if( envelopeInfoPtr->payloadSize != CRYPT_UNUSED && \
envelopeInfoPtr->segmentSize != 0 )
return( CRYPT_ERROR_UNDERFLOW );
#ifdef USE_COMPRESSION
/* If we're using compression, flush any remaining data out of the
zStream */
if( envelopeInfoPtr->flags & ENVELOPE_ZSTREAMINITED )
{
int bytesToCopy;
/* If we've just completed a segment, begin a new one. This action
is slightly anomalous in that normally a flush can't add more
data to the envelope and so we'd never need to start a new
segment during a flush, however since we can have arbitrarily
large amounts of data trapped in subspace via zlib we need to be
able to handle starting new segments at this point */
if( envelopeInfoPtr->dataFlags & ENVDATA_SEGMENTCOMPLETE )
{
status = beginSegment( envelopeInfoPtr );
if( cryptStatusError( status ) )
return( status );
if( envelopeInfoPtr->bufPos >= envelopeInfoPtr->bufSize )
return( CRYPT_ERROR_OVERFLOW );
}
/* Flush any remaining compressed data into the envelope buffer */
bytesToCopy = envelopeInfoPtr->bufSize - envelopeInfoPtr->bufPos;
envelopeInfoPtr->zStream.next_in = NULL;
envelopeInfoPtr->zStream.avail_in = 0;
envelopeInfoPtr->zStream.next_out = envelopeInfoPtr->buffer + \
envelopeInfoPtr->bufPos;
envelopeInfoPtr->zStream.avail_out = bytesToCopy;
status = deflate( &envelopeInfoPtr->zStream, Z_FINISH );
if( status != Z_STREAM_END && status != Z_OK )
{
/* There was some problem other than the output buffer being
full */
retIntError();
}
/* Adjust the status information based on the data flushed out of
the zStream. We don't have to check for the output buffer being
full because this case is already handled by the check of the
deflate() return value */
envelopeInfoPtr->bufPos += bytesToCopy - \
envelopeInfoPtr->zStream.avail_out;
ENSURES( envelopeInfoPtr->bufPos >= 0 && \
envelopeInfoPtr->bufPos <= envelopeInfoPtr->bufSize );
/* If we didn't finish flushing data because the output buffer is
full, complete the segment and tell the caller that they need to
pop some data */
if( status == Z_OK )
{
status = completeSegment( envelopeInfoPtr, TRUE );
return( cryptStatusError( status ) ? \
status : CRYPT_ERROR_OVERFLOW );
}
}
#endif /* USE_COMPRESSION */
/* If we're encrypting data with a block cipher we need to add PKCS #5
padding at the end of the last block */
if( envelopeInfoPtr->blockSize > 1 )
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -