📄 decode.c
字号:
doesn't segment data the length is implicitly defined as "until we
run out of input". This odd situation is encountered in some cases
when working with PGP data such as compressed data for which there's
no length stored or when we're synchronising the envelope data prior
to processing and there are abitrary further packets (typically PGP
signature packets, where we want to process the packets in a
connected series rather than stopping at the end of the first packet
in the series) following the current one. In both cases we don't
know the overall length because we'd need to be able to look ahead
an arbitrary distance in the stream to figure out where the
compressed data or any further packets end */
if( envelopeInfoPtr->dataFlags & ENVDATA_NOLENGTHINFO )
{
REQUIRES( envelopeInfoPtr->segmentSize <= 0 );
*segmentStatus = SEGMENT_FIXEDLENGTH;
return( OK_SPECIAL );
}
/* If there's not enough data left to contain the header for a
reasonable-sized segment, tell the caller to try again with more data
(the bytesConsumed value has already been set to zero earlier). For
a PGP envelope a partial header is a single byte, for a PKCS #7/CMS
envelope it's two bytes (tag + length) but most segments will be
longer than 256 bytes, requiring at least three bytes of tag + length
data. A reasonable tradeoff seems to be to require three bytes
before trying to decode the length */
if( length < 3 )
{
*segmentStatus = SEGMENT_INSUFFICIENTDATA;
return( CRYPT_OK );
}
/* Get the sub-segment info */
sMemConnect( &stream, buffer, length );
#ifdef USE_PGP
if( envelopeInfoPtr->type == CRYPT_FORMAT_PGP )
{
status = processPgpSegment( envelopeInfoPtr, &stream,
&segmentLength );
}
else
#endif /* USE_PGP */
{
status = processSegment( envelopeInfoPtr, &stream,
&segmentLength );
}
if( cryptStatusOK( status ) )
*bytesConsumed = stell( &stream );
sMemDisconnect( &stream );
if( cryptStatusError( status ) )
{
/* If we got an underflow error this isn't fatal since we can
continue when the user pushes more data, so we return normally
with bytesConsumed set to zero */
if( status == CRYPT_ERROR_UNDERFLOW )
{
*segmentStatus = SEGMENT_INSUFFICIENTDATA;
return( CRYPT_OK );
}
return( status );
}
ENSURES( *bytesConsumed > 0 && *bytesConsumed <= length );
/* We got the length, return the information to the caller */
envelopeInfoPtr->segmentSize = segmentLength;
ENSURES( sanityCheck( envelopeInfoPtr ) );
return( CRYPT_OK );
}
/****************************************************************************
* *
* Copy to Envelope *
* *
****************************************************************************/
/* Copy encrypted data blocks into the envelope buffer with any overflow
held in the block buffer. Only complete blocks are copied into the main
envelope buffer, if there's not enough data present for a complete block
it's temporarily held in the block buffer:
bytesFromBB bytesToBB
bufPos--+ | |
v<+>|<-- qBytesToCopy ->|<+>|
+-----------+-----------------------+ |
| |///| | | | | Main buffer
+-----------+-----------------------+-------+
^ ^ |///| | Overflow block buffer
| | +-------+
Prev.bBuf New data ^
contents |
New data remaining */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
static int copyEncryptedDataBlocks( INOUT ENVELOPE_INFO *envelopeInfoPtr,
IN_BUFFER( length ) const BYTE *buffer,
IN_LENGTH const int length,
OUT_LENGTH_Z int *bytesCopied )
{
BYTE *bufPtr = envelopeInfoPtr->buffer + envelopeInfoPtr->bufPos;
int bytesFromBB = 0, quantizedBytesToCopy, bytesToBB, status;
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
assert( isReadPtr( buffer, length ) );
assert( isWritePtr( bytesCopied, sizeof( int ) ) );
REQUIRES( sanityCheck( envelopeInfoPtr ) );
REQUIRES( length > 0 && length < MAX_INTLENGTH && \
envelopeInfoPtr->bufPos + \
envelopeInfoPtr->blockBufferPos + \
length <= envelopeInfoPtr->bufSize + \
envelopeInfoPtr->blockSize );
REQUIRES( !( envelopeInfoPtr->dataFlags & ENVDATA_NOLENGTHINFO ) );
/* Clear return value */
*bytesCopied = 0;
/* If the new data will fit entirely into the block buffer, copy it in
now and return */
if( envelopeInfoPtr->blockBufferPos + length < envelopeInfoPtr->blockSize )
{
memcpy( envelopeInfoPtr->blockBuffer + envelopeInfoPtr->blockBufferPos,
buffer, length );
envelopeInfoPtr->blockBufferPos += length;
/* Adjust the segment size based on what we've consumed */
envelopeInfoPtr->segmentSize -= length;
*bytesCopied = length;
ENSURES( sanityCheck( envelopeInfoPtr ) );
return( CRYPT_OK );
}
/* If there isn't room in the main buffer for even one more block, exit
without doing anything (bytesCopied is still set to zero from the
earlier code). This leads to slightly anomalous behaviour where,
with no room for a complete block in the main buffer, copying in a
data length smaller than the block buffer will lead to the data being
absorbed by the block buffer due to the previous section of code, but
copying in a length larger than the block buffer will result in no
data at all being absorbed even if there's still room in the block
buffer, see the long comment in copyData() for a full discussion of
this process */
if( envelopeInfoPtr->bufPos + \
envelopeInfoPtr->blockSize > envelopeInfoPtr->bufSize )
{
/* There's no room for even one more block */
return( CRYPT_OK );
}
/* There's room for at least one more block in the buffer. First, if
there are leftover bytes in the block buffer, move them into the main
buffer */
if( envelopeInfoPtr->blockBufferPos > 0 )
{
bytesFromBB = envelopeInfoPtr->blockBufferPos;
memcpy( bufPtr, envelopeInfoPtr->blockBuffer, bytesFromBB );
}
envelopeInfoPtr->blockBufferPos = 0;
/* Determine how many bytes we can copy into the buffer to fill it to
the nearest available block size */
quantizedBytesToCopy = ( length + bytesFromBB ) & \
envelopeInfoPtr->blockSizeMask;
quantizedBytesToCopy -= bytesFromBB;
ENSURES( quantizedBytesToCopy > 0 && quantizedBytesToCopy <= length && \
envelopeInfoPtr->bufPos + bytesFromBB + \
quantizedBytesToCopy <= envelopeInfoPtr->bufSize );
ENSURES( ( ( bytesFromBB + quantizedBytesToCopy ) & \
( envelopeInfoPtr->blockSize - 1 ) ) == 0 );
/* Now copy across a number of bytes which is a multiple of the block
size and decrypt them. Note that we have to use memmove() rather
than memcpy() because if we're sync'ing data in the buffer we're
doing a copy within the buffer rather than copying in data from
an external source */
memmove( bufPtr + bytesFromBB, buffer, quantizedBytesToCopy );
status = krnlSendMessage( envelopeInfoPtr->iCryptContext,
IMESSAGE_CTX_DECRYPT, bufPtr,
bytesFromBB + quantizedBytesToCopy );
if( cryptStatusError( status ) )
return( status );
envelopeInfoPtr->bufPos += bytesFromBB + quantizedBytesToCopy;
envelopeInfoPtr->segmentSize -= length;
ENSURES( envelopeInfoPtr->bufPos >= 0 && \
envelopeInfoPtr->bufPos <= envelopeInfoPtr->bufSize );
ENSURES( envelopeInfoPtr->segmentSize >= 0 && \
envelopeInfoPtr->segmentSize < MAX_INTLENGTH );
/* If the payload has a definite length and we've reached its end, set
the EOC flag to make sure that we don't go any further */
if( envelopeInfoPtr->payloadSize != CRYPT_UNUSED && \
envelopeInfoPtr->segmentSize <= 0 )
{
status = processDataEnd( envelopeInfoPtr );
if( cryptStatusError( status ) )
return( status );
ENSURES( sanityCheck( envelopeInfoPtr ) );
*bytesCopied = length;
return( CRYPT_OK );
}
/* Copy any remainder (the difference between the amount to copy and the
blocksize-quantized amount) into the block buffer */
bytesToBB = length - quantizedBytesToCopy;
REQUIRES( bytesToBB >= 0 && bytesToBB <= envelopeInfoPtr->blockSize );
if( bytesToBB > 0 )
{
memcpy( envelopeInfoPtr->blockBuffer, buffer + quantizedBytesToCopy,
bytesToBB );
}
envelopeInfoPtr->blockBufferPos = bytesToBB;
ENSURES( sanityCheck( envelopeInfoPtr ) );
*bytesCopied = length;
return( CRYPT_OK );
}
/* Copy possibly encrypted data into the envelope with special handling for
block encryption modes. Returns the number of bytes copied:
bPos bSize
| |
v v
+-----------------------+---------------+
| | | | | | Main buffer
+-----------------------+---------------+
+-------+
|///| | Overflow block buffer
+-------+
^ ^
|blBufSize
blBufPos
The main buffer only contains data amounts quantised to the encryption
block size. Any additional data is copied into the block buffer, a
staging buffer used to accumulate data until it can be transferred to
the main buffer for decryption */
CHECK_RETVAL STDC_NONNULL_ARG( ( 1, 2, 4 ) ) \
static int copyData( INOUT ENVELOPE_INFO *envelopeInfoPtr,
IN_BUFFER( length ) const BYTE *buffer,
IN_LENGTH const int length,
OUT_LENGTH_Z int *bytesCopied )
{
BYTE *bufPtr = envelopeInfoPtr->buffer + envelopeInfoPtr->bufPos;
int bytesToCopy = length, bytesLeft, status;
assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
assert( isReadPtr( buffer, length ) );
assert( isWritePtr( bytesCopied, sizeof( int ) ) );
REQUIRES( sanityCheck( envelopeInfoPtr ) );
REQUIRES( length > 0 && length < MAX_INTLENGTH );
/* Clear return value */
*bytesCopied = 0;
/* Figure out how much we can copy across. First we calculate the
minimum of the amount of data passed in and the amount remaining in
the current segment */
if( !( envelopeInfoPtr->dataFlags & ENVDATA_NOLENGTHINFO ) && \
bytesToCopy > envelopeInfoPtr->segmentSize )
bytesToCopy = envelopeInfoPtr->segmentSize;
/* Now we check to see if this is affected by the total free space
remaining in the buffer. If we're processing data blocks we can have
two cases, one in which the limit is the amount of buffer space
available and the other in which the limit is the amount of data
available. If the limit is set by the available data then we don't
have to worry about flushing extra data out of the block buffer into
the main buffer but if the limit is set by the available buffer space
we have to reduce the amount that we can copy in based on any extra
data that will be flushed out of the block buffer.
There are two possible approaches that can be used when the block
buffer is involved. The first one copies as much as we can into the
buffer and, if that isn't enough, maxes out the block buffer with as
much remaining data as possible. The second only copies in as much as
can fit into the buffer, even if there's room in the block buffer for
a few more bytes. The second approach is preferable because although
either will give the impression of a not-quite-full buffer into which
no more data can be copied, the second minimizes the amount of data
which is moved into and out of the block buffer.
The first approach may seem slightly more logical, but will only
cause confusion in the long run. Consider copying (say) 43 bytes to
a 43-byte buffer. The first time this will succeed, after which there
will be 40 bytes in the buffer (reported to the caller) and 3 in the
block buffer. If the caller tries to copy in 3 more bytes to "fill"
the main buffer, they'll again vanish into the block buffer. A second
call with three more bytes will copy 2 bytes and return with 1 byte
uncopied. In effect this method of using the block buffer extends the
blocksize-quantized main buffer by the size of the block buffer, which
will only cause confusion when data appears to vanish when copied into
it.
In the following length calculation the block buffer content is
counted as part of the total content in order to implement the second
buffer-filling strategy */
bytesLeft = envelopeInfoPtr->bufSize - \
( envelopeInfoPtr->bufPos + envelopeInfoPtr->blockBufferPos );
if( bytesLeft <= 0 )
{
/* There's no room left to copy anything in, return now (bytesCopied
is still set to zero from the earlier code). We can't check this
in the calling code because it doesn't know about the internal
buffer-handling strategy that we use so we perform an explicit
check here */
return( CRYPT_OK );
}
if( bytesLeft < bytesToCopy )
bytesToCopy = bytesLeft;
ENSURES( bytesToCopy > 0 && bytesToCopy <= length );
/* If its a block encryption mode we need to provide special handling for
odd data lengths that don't match the block size */
if( envelopeInfoPtr->blockSize > 1 )
{
return( copyEncryptedDataBlocks( envelopeInfoPtr, buffer,
bytesToCopy, bytesCopied ) );
}
/* It's unencrypted data or data that's encrypted with a stream cipher,
just copy over as much of the segment as we can and decrypt it if
necessary. We use memmove() for the same reason as given above */
memmove( bufPtr, buffer, bytesToCopy );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -