/****************************************************************************
*																			*
*					  cryptlib Datagram Decoding Routines					*
*						Copyright Peter Gutmann 1996-2006					*
*																			*
****************************************************************************/

#if defined( INC_ALL )
  #include "envelope.h"
  #include "asn1.h"
  #include "misc_rw.h"
  #include "pgp.h"
#else
  #include "envelope/envelope.h"
  #include "misc/asn1.h"
  #include "misc/misc_rw.h"
  #include "misc/pgp.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

/****************************************************************************
*																			*
*							Header Processing Routines						*
*																			*
****************************************************************************/

/* Handle the EOC and PKCS #5 block padding if necessary:

			   pad
	+-------+-------+-------+
	|		|		|		|
	+-------+-------+-------+
			^		^
			|		|
		 padPtr	  bPos */

static int processEOC( ENVELOPE_INFO *envelopeInfoPtr )
	{
	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
	assert( envelopeInfoPtr->bufPos >= 0 && \
			envelopeInfoPtr->bufPos <= envelopeInfoPtr->bufSize && \
			envelopeInfoPtr->bufSize >= MIN_BUFFER_SIZE );

	/* 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 )
		{
		const BYTE *padPtr = envelopeInfoPtr->buffer + \
							 envelopeInfoPtr->bufPos - 1;
		const int padSize = *padPtr;
		int i;

		/* Make sure that the padding size is valid */
		if( padSize < 1 || padSize > envelopeInfoPtr->blockSize || \
			padSize > envelopeInfoPtr->bufPos )
			return( CRYPT_ERROR_BADDATA );

		/* Check the padding data */
		envelopeInfoPtr->bufPos -= padSize;
		padPtr -= padSize - 1;
		for( i = 0; i < padSize - 1; i++ )
			if( padPtr[ i ] != padSize )
				return( CRYPT_ERROR_BADDATA );
		assert( envelopeInfoPtr->bufPos >= 0 );
		}

	/* 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;

	return( CRYPT_OK );
	}

/* 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,	/* Fixed-length segment */
	SEGMENT_ENDOFDATA,		/* No more data to process */
	SEGMENT_LAST			/* Last possible segment status */
	} SEGMENT_STATUS;

static int getNextSegment( ENVELOPE_INFO *envelopeInfoPtr, const BYTE *buffer,
						   const int length, SEGMENT_STATUS *segmentStatus )
	{
	STREAM stream;
	long segmentLength;
	int bytesRead, status;

	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
	assert( length > 0 );
	assert( isReadPtr( buffer, length ) );

	/* Clear return value */
	*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 )
		{
		assert( 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
	   doesn't segment data, the length is implicitly defined as "until we
	   run out of input" */
	if( envelopeInfoPtr->dataFlags & ENVDATA_NOSEGMENT )
		{
		envelopeInfoPtr->segmentSize = CRYPT_UNUSED;
		*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.  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 )
		return( 0 );

	/* Get the sub-segment info */
	sMemConnect( &stream, buffer, length );
	if( envelopeInfoPtr->type == CRYPT_FORMAT_PGP )
		{
		status = pgpReadPartialLength( &stream, &segmentLength );
		if( cryptStatusError( status ) )
			{
			/* If we got an OK_SPECIAL return it's just an indication that
			   we got another partial length (with other segments to
			   follow), and not an actual error */
			if( status != OK_SPECIAL )
				return( status );
			}
		else
			{
			/* We've read a length that doesn't use the indefinite-length
			   encoding, it's the last data segment, shift from indefinite
			   to definite-length mode */
			envelopeInfoPtr->dataFlags |= ENVDATA_NOSEGMENT;
			if( segmentLength > 0 )
				{
				/* 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 likes 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 )
						segmentLength = 0;
					}

				/* Convert the last segment into a definite-length segment.
				   When we return the calling code will immediately call
				   getNextSegment() again since we've consumed some input,
				   at that 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;
				}
			else
				/* It's a terminating zero-length segment, wrap up the
				   processing */
				status = processEOC( envelopeInfoPtr );
			}
		}
	else
		{
		/* checkEOC() can also return an error code alongside the TRUE/FALSE
		   indication, in which case we drop through to the error-handler
		   that follows this code block without doing anything else */
		status = checkEOC( &stream );
		if( status == FALSE )
			{
			/* It's a new sub-segment, get its length */
			status = readLongGenericHole( &stream, &segmentLength,
										  BER_OCTETSTRING );
			if( cryptStatusOK( status ) && segmentLength == CRYPT_UNUSED )
				/* If it's an (invalid) indefinite-length encoding, we can't
				   do anything with it */
				status = CRYPT_ERROR_BADDATA;
			}
		else
			/* If we've seen the EOC, wrap up the processing.  Any other
			   value for the return status is an error code */
			if( status == TRUE )
				{
				status = processEOC( envelopeInfoPtr );
				segmentLength = 0;
				}
		}
	bytesRead = stell( &stream );
	sMemDisconnect( &stream );
	if( cryptStatusError( status ) )
		{
		/* If we got an underflow error, record it as zero bytes read so
		   that we'll retry the read next time */
		if( status == CRYPT_ERROR_UNDERFLOW )
			return( 0 );
		return( status );
		}

	/* We got the length, return the information to the caller */
	envelopeInfoPtr->segmentSize = segmentLength;
	return( bytesRead );
	}

/****************************************************************************
*																			*
*								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 (see the diagram for copyData()
   below for more details) */

static int copyEncryptedDataBlocks( ENVELOPE_INFO *envelopeInfoPtr,
									const BYTE *buffer, const int length )
	{
	BYTE *bufPtr = envelopeInfoPtr->buffer + envelopeInfoPtr->bufPos;
	int bytesCopied = 0, quantizedBytesToCopy, blockBufferBToC, status;

	assert( length > 0 );

	/* If the new data will fit entirely into the block buffer, copy it in
	   now and return */
	if( length < envelopeInfoPtr->blockSize - envelopeInfoPtr->blockBufferPos )
		{
		memcpy( envelopeInfoPtr->blockBuffer + envelopeInfoPtr->blockBufferPos,
				buffer, length );
		envelopeInfoPtr->blockBufferPos += length;

		/* Adjust the segment size based on what we've consumed */
		envelopeInfoPtr->segmentSize -= length;

		return( length );
		}

	/* If there isn't room in the main buffer for even one more block, exit
	   without doing anything.  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 */
	if( envelopeInfoPtr->bufSize - envelopeInfoPtr->bufPos < \
		envelopeInfoPtr->blockSize )
		return( 0 );	/* No room for even one more block */

	/* 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 )
		{
		memcpy( bufPtr, envelopeInfoPtr->blockBuffer,
				envelopeInfoPtr->blockBufferPos );
		bytesCopied = envelopeInfoPtr->blockBufferPos;
		}
	envelopeInfoPtr->blockBufferPos = 0;

	/* Determine how many bytes we can copy into the buffer to fill it to
	   the nearest available block size */
	quantizedBytesToCopy = ( length + bytesCopied ) & \
						   envelopeInfoPtr->blockSizeMask;
	quantizedBytesToCopy -= bytesCopied;
	if( length < 0 || quantizedBytesToCopy <= 0 || \
		quantizedBytesToCopy > length )
		{
		/* Sanity check */
		assert( NOTREACHED );
		return( CRYPT_ERROR_FAILED );
		}
	assert( !( ( bytesCopied + quantizedBytesToCopy ) & \
			   ( envelopeInfoPtr->blockSize - 1 ) ) );

	/* 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 + bytesCopied, buffer, quantizedBytesToCopy );
	envelopeInfoPtr->bufPos += bytesCopied + quantizedBytesToCopy;
	envelopeInfoPtr->segmentSize -= length;
	status = krnlSendMessage( envelopeInfoPtr->iCryptContext,
							  IMESSAGE_CTX_DECRYPT, bufPtr,
							  bytesCopied + quantizedBytesToCopy );
	if( cryptStatusError( status ) )
		return( status );
	assert( envelopeInfoPtr->bufPos >=0 && \
			envelopeInfoPtr->bufPos <= envelopeInfoPtr->bufSize );
	assert( envelopeInfoPtr->segmentSize >= 0 );

	/* 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 = processEOC( envelopeInfoPtr );
		if( cryptStatusError( status ) )
			return( status );

		return( length );
		}

	/* Copy any remainder (the difference between the amount to copy and the
	   blocksize-quantized amount) into the block buffer */
	blockBufferBToC = length - quantizedBytesToCopy;
	assert( blockBufferBToC >=0 && \
			blockBufferBToC <= envelopeInfoPtr->bufSize );
	if( blockBufferBToC > 0 )
		memcpy( envelopeInfoPtr->blockBuffer, buffer + quantizedBytesToCopy,
				blockBufferBToC );
	envelopeInfoPtr->blockBufferPos = blockBufferBToC;

	return( length );
	}

/* Copy possibly encrypted data into the envelope with special handling for
   block encryption modes.  Returns the number of bytes copied.  The buffers
   work as follows:

						  bPos			  bSize
							|				|
							v				v
	+-----------------------+---------------+
	|		|		|		|		|		|	Main buffer
	+-----------------------+---------------+

							+-------+
							|///|	|			Overflow block buffer
							+-------+
								^	^
								| bBufSize
							 bBufPos

    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 */

static int copyData( ENVELOPE_INFO *envelopeInfoPtr, const BYTE *buffer,
					 const int length )
	{
	BYTE *bufPtr = envelopeInfoPtr->buffer + envelopeInfoPtr->bufPos;
	int bytesToCopy = length, bytesLeft, status;

	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
	assert( length > 0 );
	assert( isReadPtr( buffer, length ) );
	assert( envelopeInfoPtr->bufPos >= 0 && \
			envelopeInfoPtr->bufPos <= envelopeInfoPtr->bufSize || \
			envelopeInfoPtr->bufSize >= MIN_BUFFER_SIZE );
	assert( envelopeInfoPtr->segmentSize == CRYPT_UNUSED || \
			envelopeInfoPtr->segmentSize > 0 );
	assert( ( envelopeInfoPtr->blockSize == 0 ) || \
			( envelopeInfoPtr->blockBufferPos >= 0 && \
			  envelopeInfoPtr->blockBufferPos < envelopeInfoPtr->blockSize ) );

	/* 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->segmentSize != CRYPT_UNUSED && \
		envelopeInfoPtr->segmentSize < bytesToCopy )
		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, 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
	   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 in.

	   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.  We can't
		   check this in the caller because it doesn't know about the 
		   internal buffer-handling strategy that we use, so we perform an
		   explicit check here.  Note that we check specifically for a size 
		   of zero bytes, a below-zero value is an error that's caught 
		   below */
		return( 0 );
	if( bytesLeft < bytesToCopy )
		bytesToCopy = bytesLeft;
	if( bytesToCopy <= 0 || envelopeInfoPtr->blockBufferPos < 0 ||
		envelopeInfoPtr->blockBufferPos > envelopeInfoPtr->blockSize )
		{
		/* Sanity check that verifies segmentSize, length, bufPos, and
		   blockBufferPos before we start into the following code */
		assert( NOTREACHED );
		return( CRYPT_ERROR_INTERNAL );
		}

	/* 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 ) );

	/* It's unencrypted or 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( envelopeInfoPtr->buffer + envelopeInfoPtr->bufPos, buffer,
			 bytesToCopy );
	envelopeInfoPtr->bufPos += bytesToCopy;
	if( envelopeInfoPtr->segmentSize != CRYPT_UNUSED )
		envelopeInfoPtr->segmentSize -= bytesToCopy;
	if( envelopeInfoPtr->iCryptContext != CRYPT_ERROR )
		{
		status = krnlSendMessage( envelopeInfoPtr->iCryptContext,
								  IMESSAGE_CTX_DECRYPT, bufPtr,
								  bytesToCopy );
		if( cryptStatusError( status ) )
			return( status );
		}

	/* 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 = processEOC( envelopeInfoPtr );
		if( cryptStatusError( status ) )
			return( status );
		}

	return( bytesToCopy );
	}

/* Copy data into the de-enveloping envelope.  Returns the number of bytes
   copied */

static int copyToDeenvelope( ENVELOPE_INFO *envelopeInfoPtr,
							 const BYTE *buffer, const int length )
	{
	BYTE *bufPtr = ( BYTE * ) buffer;
	int currentLength = length, bytesCopied, iterationCount;

	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );
	assert( length > 0 );
	assert( isReadPtr( buffer, length ) );

	/* Sanity-check the envelope state */
	if( envelopeInfoPtr->bufPos < 0 || \
		envelopeInfoPtr->bufPos > envelopeInfoPtr->bufSize || \
		envelopeInfoPtr->bufSize < MIN_BUFFER_SIZE || \
		( envelopeInfoPtr->blockSize > 0 && \
		  ( envelopeInfoPtr->blockBufferPos < 0 || \
			envelopeInfoPtr->blockBufferPos >= envelopeInfoPtr->blockSize ) ) )
		{
		assert( NOTREACHED );
		return( CRYPT_ERROR_FAILED );
		}

	/* If we're trying to copy data into a full buffer, return a count of 0
	   bytes (the calling routine may convert this to an overflow error if
	   necessary) */
	if( envelopeInfoPtr->bufPos >= envelopeInfoPtr->bufSize )
		return( 0 );

	/* If we're verifying a detached signature, just hash the data and exit.
	   We don't have to check whether hashing is active or not since it'll
	   always be active for detached data, which is hashed and discarded */
	if( envelopeInfoPtr->flags & ENVELOPE_DETACHED_SIG )
		{
		ACTION_LIST *hashActionPtr;
		int status;

		assert( envelopeInfoPtr->dataFlags & ENVDATA_HASHACTIONSACTIVE );
		assert( envelopeInfoPtr->actionList != NULL );

		iterationCount = 0;
		for( hashActionPtr = envelopeInfoPtr->actionList;
			 hashActionPtr != NULL && \
				hashActionPtr->action == ACTION_HASH && \
				iterationCount++ < FAILSAFE_ITERATIONS_MED;
			 hashActionPtr = hashActionPtr->next )
			{
			status = krnlSendMessage( hashActionPtr->iCryptHandle,
									  IMESSAGE_CTX_HASH,
									  ( void * ) buffer, currentLength );
			if( cryptStatusError( status ) )
				return( status );
			}
		if( iterationCount >= FAILSAFE_ITERATIONS_MED )
			retIntError();
		return( currentLength );
		}

	/* Keep processing data until either we run out of input or we can't copy
	   in any more data.  The code sequence within this loop acts as a simple
	   FSM so that if we exit at any point then the next call to this
	   function will resume where we left off */
	iterationCount = 0;
	do
		{
		int segmentCount, status;

		/* If there's no segment information currently available, we need to
		   process a segment header before we can handle any data.  The use
		   of a loop is necessary to handle some broken implementations that
		   emit zero-length sub-segments (as a corollary, it also helps
		   avoid a pile of special-case code to manage PGP's strange way of
		   handling the last segment in indefinite-length encodings).  We
		   limit the segment count to 10 sub-segments to make sure that we
		   don't spend forever trying to process extremely broken data */
		for( segmentCount = 0; \
			 segmentCount < 10 && envelopeInfoPtr->segmentSize <= 0; \
			 segmentCount++ )
			{
			SEGMENT_STATUS segmentStatus;
			int bytesRead;

			bytesRead = status = \
				getNextSegment( envelopeInfoPtr, bufPtr, currentLength,
								&segmentStatus );
			if( status == OK_SPECIAL )
				{
				/* If we've reached the end of the payload, we're done */
				if( segmentStatus == SEGMENT_ENDOFDATA )
					return( length - currentLength );

				/* We got the length via some other mechanism because it's a
				   definite-length or non-segmenting encoding, no input was
				   consumed and we can exit */
				assert( segmentStatus == SEGMENT_FIXEDLENGTH );
				break;
				}
			if( cryptStatusError( status ) )
				return( status );
			if( bytesRead <= 0 )
				/* We don't have enough input data left to read the
				   information for the next segment, exit */
				return( length - currentLength );
			bufPtr += bytesRead;
			currentLength -= bytesRead;

			/* If we've reached the EOC or consumed all of the input data,
			   exit */
			if( ( envelopeInfoPtr->dataFlags & ENVDATA_ENDOFCONTENTS ) || \
				currentLength <= 0 )
				return( length - currentLength );
			}
		if( segmentCount >= 10 )
			/* We've processed ten consecutive sub-segments in a row, there's
			   something wrong with the input data */
			return( CRYPT_ERROR_BADDATA );
		assert( currentLength > 0 );

		/* Copy the data into the envelope, decrypting it as we go if
		   necessary.  In theory we could also check to see whether any
		   more data can fit into the buffer before trying to copy it in,
		   but since this would require knowledge of the internal buffer-
		   handling strategy used by copyData() we always call it and rely
		   on a bytesCopied value of zero to indicate that the tank is
		   full */
		bytesCopied = copyData( envelopeInfoPtr, bufPtr, currentLength );
		if( cryptStatusError( bytesCopied ) )
			return( bytesCopied );
		bufPtr += bytesCopied;
		currentLength -= bytesCopied;

		assert( envelopeInfoPtr->bufPos >= 0 && \
				envelopeInfoPtr->bufPos <= envelopeInfoPtr->bufSize && \
				envelopeInfoPtr->bufSize >= MIN_BUFFER_SIZE );
		assert( currentLength >= 0 );
		assert( ( envelopeInfoPtr->segmentSize >= 0 ) || \
				( ( envelopeInfoPtr->dataFlags & ENVDATA_NOSEGMENT ) && \
				  ( envelopeInfoPtr->payloadSize == CRYPT_UNUSED ) && \
				  ( envelopeInfoPtr->segmentSize == CRYPT_UNUSED ) ) );
		}
	while( currentLength > 0 && bytesCopied > 0 && \
		   iterationCount++ < FAILSAFE_ITERATIONS_MAX );
	if( iterationCount >= FAILSAFE_ITERATIONS_MAX )
		retIntError();

	/* Make sure that we've left everything in a valid state */
	assert( envelopeInfoPtr->bufPos >= 0 && \
			envelopeInfoPtr->bufPos <= envelopeInfoPtr->bufSize && \
			envelopeInfoPtr->bufSize >= MIN_BUFFER_SIZE );
	assert( ( envelopeInfoPtr->blockSize == 0 ) || \
			( envelopeInfoPtr->blockBufferPos >= 0 && \
			  envelopeInfoPtr->blockBufferPos < envelopeInfoPtr->blockSize ) );

	return( length - currentLength );
	}

/****************************************************************************
*																			*
*								Copy from Envelope							*
*																			*
****************************************************************************/

/* Copy data from the envelope.  Returns the number of bytes copied */

static int copyFromDeenvelope( ENVELOPE_INFO *envelopeInfoPtr, BYTE *buffer,
							   const int length )
	{
	BYTE *bufPtr = envelopeInfoPtr->buffer;
	const BOOLEAN isLookaheadRead = ( length < 0 ) ? TRUE : FALSE;
	int bytesToCopy, bytesCopied, oobBytesCopied = 0, remainder;

	assert( isWritePtr( envelopeInfoPtr, sizeof( ENVELOPE_INFO ) ) );

	/* Sanity-check the envelope state */
	if( envelopeInfoPtr->bufPos < 0 || \
		envelopeInfoPtr->bufPos > envelopeInfoPtr->bufSize || \
		envelopeInfoPtr->bufSize < MIN_BUFFER_SIZE || \
		envelopeInfoPtr->oobBufPos < 0 || \
		envelopeInfoPtr->oobBufPos > OOB_BUFFER_SIZE || \
		( envelopeInfoPtr->blockSize > 0 && \
		  ( envelopeInfoPtr->blockBufferPos < 0 || \
			envelopeInfoPtr->blockBufferPos >= envelopeInfoPtr->blockSize ) ) )
		{
		assert( NOTREACHED );
		return( CRYPT_ERROR_FAILED );
		}

	/* Remember how much data we need to copy.  A negative length specifies
	   that this is a speculative/lookahead read, so we turn it into a
	   positive value if necessary */
	bytesToCopy = ( length < 0 ) ? -length : length;
	if( bytesToCopy <= 0 )
		{
		/* Sanity check */
		assert( NOTREACHED );
		return( CRYPT_ERROR_FAILED );
		}
	assert( isReadPtr( buffer, bytesToCopy ) );
	assert( !isLookaheadRead || \
			( bytesToCopy > 0 && bytesToCopy <= OOB_BUFFER_SIZE ) );

	/* If we're verifying a detached sig, the data is communicated out-of-
	   band so there's nothing to copy out */
	if( envelopeInfoPtr->flags & ENVELOPE_DETACHED_SIG )
		return( 0 );

	/* If there's buffered out-of-band data from a lookahead read present,
	   insert it into the output stream */
	if( envelopeInfoPtr->oobBufPos > 0 )
		{
		oobBytesCopied = min( bytesToCopy, envelopeInfoPtr->oobBufPos );
		assert( oobBytesCopied > 0 );
		memcpy( buffer, envelopeInfoPtr->oobBuffer, oobBytesCopied );
		if( !isLookaheadRead )
			{
			/* If we moved the data out of the OOB buffer, adjust the OOB
			   buffer contents */
			if( envelopeInfoPtr->oobBufPos > oobBytesCopied )
				memmove( envelopeInfoPtr->oobBuffer,
						 envelopeInfoPtr->oobBuffer + oobBytesCopied,
						 envelopeInfoPtr->oobBufPos - oobBytesCopied );
			envelopeInfoPtr->oobBufPos -= oobBytesCopied;
			}
		bytesToCopy -= oobBytesCopied;
		buffer += oobBytesCopied;
		if( bytesToCopy <= 0 )
			return( oobBytesCopied );
		}

	/* If we're using compression, expand the data from the buffer to the
	   output via the zStream */
#ifdef USE_COMPRESSION
	if( envelopeInfoPtr->flags & ENVELOPE_ZSTREAMINITED )
		{
		const int inLength = bytesToCopy;
		const int bytesIn = \
			( envelopeInfoPtr->dataLeft > 0 && \
			  envelopeInfoPtr->dataLeft < envelopeInfoPtr->bufPos ) ? \
			envelopeInfoPtr->dataLeft : envelopeInfoPtr->bufPos;
		int status;

		/* Decompress the data into the output buffer.  Note that we use the
		   length value to determine the length of the output rather than
		   bytesToCopy since the ratio of bytes in the buffer to bytes of
		   output isn't 1:1 as it is for other content types.

		   When using PGP 2.x-compatible decompression, we have to allow a
		   return status of Z_BUF_ERROR because it uses a compression format
		   from a pre-release version of InfoZip that doesn't include
		   header or trailer information, so the decompression code can't
		   definitely tell that it's reached the end of its input data but
		   can only report that it can't go any further.

		   We can also get a Z_BUF_ERROR for some types of (non-fatal) error
		   situations, for example if we're flushing out data still present
		   in the zstream (avail_in == 0) and there's a problem such as the
		   compressor needing more data but there's none available, the zlib
		   code will report it as a Z_BUF_ERROR.  In this case we convert it
		   into a (recoverable) underflow error, which isn't always accurate
		   but is more useful than the generic CRYPT_ERROR_FAILED */
		envelopeInfoPtr->zStream.next_in = bufPtr;
		envelopeInfoPtr->zStream.avail_in = bytesIn;
		envelopeInfoPtr->zStream.next_out = buffer;
		envelopeInfoPtr->zStream.avail_out = bytesToCopy;
		status = inflate( &envelopeInfoPtr->zStream, Z_SYNC_FLUSH );
		if( status != Z_OK && status != Z_STREAM_END && \
			!( status == Z_BUF_ERROR && \
			   envelopeInfoPtr->type == CRYPT_FORMAT_PGP ) )
			{
			assert( status != Z_STREAM_ERROR );	/* Parameter error */
			return( ( status == Z_DATA_ERROR ) ? CRYPT_ERROR_BADDATA : \
					( status == Z_MEM_ERROR ) ? CRYPT_ERROR_MEMORY : \
					( status == Z_BUF_ERROR ) ? CRYPT_ERROR_UNDERFLOW : \
					CRYPT_ERROR_FAILED );
			}

		/* Adjust the status information based on the data copied from the
		   buffer into the zStream (bytesCopied) and the data flushed from
		   the zStream to the output (bytesToCopy) */
		bytesCopied = bytesIn - envelopeInfoPtr->zStream.avail_in;
		bytesToCopy -= envelopeInfoPtr->zStream.avail_out;
		assert( bytesCopied >= 0 && bytesToCopy >= 0 );

		/* If we consumed all of the input and there's extra data left after
		   the end of the data stream, it's EOC information, mark that as
		   consumed as well */
		if( envelopeInfoPtr->zStream.avail_in <= 0 && \
			envelopeInfoPtr->dataLeft > 0 && \
			envelopeInfoPtr->dataLeft < envelopeInfoPtr->bufPos )
			{
			if( envelopeInfoPtr->type != CRYPT_FORMAT_PGP && \
				( !( envelopeInfoPtr->dataFlags & ENVDATA_ENDOFCONTENTS ) || \
				  ( envelopeInfoPtr->bufPos - envelopeInfoPtr->dataLeft != 2 ) ) )
				{
				/* We should only have the EOC octets { 0x00 0x00 } present
				   at this point */
				assert( NOTREACHED );
				return( CRYPT_ERROR_FAILED );
				}
			envelopeInfoPtr->dataLeft = envelopeInfoPtr->bufPos;
			}

		/* If we're doing a lookahead read, we can't just copy the data out
		   as we would for any other content type because we can't undo the
		   decompression step, so we remember the output data in a local
		   buffer and insert it into the output stream on the next read */
		if( isLookaheadRead )
			{
			assert( envelopeInfoPtr->oobBufPos + inLength <= OOB_BUFFER_SIZE );
			memcpy( envelopeInfoPtr->oobBuffer + envelopeInfoPtr->oobBufPos,
					buffer, inLength );
			envelopeInfoPtr->oobBufPos += inLength;
			}
		}
	else
#endif /* USE_COMPRESSION */
		{
		ACTION_LIST *hashActionPtr;

		/* Copy out as much of the data as we can, making sure that we don't
		   overrun into any following data */
		if( bytesToCopy > envelopeInfoPtr->bufPos )
			bytesToCopy = envelopeInfoPtr->bufPos;
		if( envelopeInfoPtr->dataLeft > 0 && \
			bytesToCopy > envelopeInfoPtr->dataLeft )
			bytesToCopy = envelopeInfoPtr->dataLeft;
		if( bytesToCopy < 0 )
			{
			/* Sanity check */
			assert( NOTREACHED );
			return( CRYPT_ERROR_FAILED );
			}

		/* If we're using a block encryption mode and we haven't seen the
		   end-of-contents yet and there's no data waiting in the block
		   buffer (which would mean that there's more data to come), we
		   can't copy out the last block because it might contain padding,
		   so we decrease the effective data amount by one block's worth */
		if( envelopeInfoPtr->blockSize > 1 && \
			!( envelopeInfoPtr->dataFlags & ENVDATA_ENDOFCONTENTS ) && \
			envelopeInfoPtr->blockBufferPos > 0 )
			bytesToCopy -= envelopeInfoPtr->blockSize;

		/* If we've ended up with nothing to copy (e.g. due to blocking
		   requirements), exit */
		if( bytesToCopy <= 0 )
			return( oobBytesCopied );
		assert( bytesToCopy > 0 );

		/* If we've seen the end-of-contents octets and there's no payload
		   left to copy out, exit */
		if( ( envelopeInfoPtr->dataFlags & ENVDATA_ENDOFCONTENTS ) && \
			envelopeInfoPtr->dataLeft <= 0 )
			return( oobBytesCopied );

		/* If we're doing a lookahead read, just copy the data out without
		   adjusting the read-data values */
		if( isLookaheadRead )
			{
			memcpy( buffer, bufPtr, bytesToCopy );
			return( bytesToCopy );
			}

		/* Hash the payload data if necessary */
		if( envelopeInfoPtr->dataFlags & ENVDATA_HASHACTIONSACTIVE )
			{
			int iterationCount = 0;
			
			for( hashActionPtr = envelopeInfoPtr->actionList;
				 hashActionPtr != NULL && \
					hashActionPtr->action == ACTION_HASH && \
					iterationCount++ < FAILSAFE_ITERATIONS_MED;
				 hashActionPtr = hashActionPtr->next )
				{
				int status;

				status = krnlSendMessage( hashActionPtr->iCryptHandle,
										  IMESSAGE_CTX_HASH, bufPtr,
										  bytesToCopy );
				if( cryptStatusError( status ) )
					return( status );
				}
			if( iterationCount >= FAILSAFE_ITERATIONS_MED )
				retIntError();
			}

		/* We're not using compression, copy the data across directly */
		memcpy( buffer, bufPtr, bytesToCopy );
		bytesCopied = bytesToCopy;
		}

	/* Sanity check */
	if( envelopeInfoPtr->bufPos - bytesCopied < 0 )
		{
		assert( NOTREACHED );
		return( CRYPT_ERROR_FAILED );
		}

	/* Move any remaining data down to the start of the buffer  */
	remainder = envelopeInfoPtr->bufPos - bytesCopied;
	if( remainder > 0 && bytesCopied > 0 )
		memmove( bufPtr, bufPtr + bytesCopied, remainder );
	envelopeInfoPtr->bufPos = remainder;

	/* If there's data following the payload, adjust the end-of-payload
	   pointer to reflect the data that we've just copied out */
	if( envelopeInfoPtr->dataLeft > 0 && bytesCopied > 0 )
		envelopeInfoPtr->dataLeft -= bytesCopied;
	assert( envelopeInfoPtr->dataLeft >= 0 );

	return( oobBytesCopied + bytesToCopy );
	}

/****************************************************************************
*																			*
*						Extra Data Management Functions						*
*																			*
****************************************************************************/

/* Synchronise the deenveloping data stream */

static int syncDeenvelopeData( ENVELOPE_INFO *envelopeInfoPtr,
							   STREAM *stream )
	{
	const int dataStartPos = stell( stream );
	const int oldBufPos = envelopeInfoPtr->bufPos;
	const int bytesLeft = sMemDataLeft( stream );
	int bytesCopied;

	/* After the envelope header has been processed, what's left is payload
	   data that requires special processing because of segmenting and
	   decryption and hashing requirements, so we feed it in via a
	   copyToDeenvelope() of the data in the buffer.  This is a rather ugly
	   hack, but it works because we're moving data backwards in the buffer
	   so there shouldn't be any problems for the rare instances where the
	   data overlaps.  In the worst case (PKCS #7/CMS short definite-length
	   OCTET STRING) we only consume two bytes, the tag and one-byte length,
	   but since we're using memmove() in copyData() this shouldn't be a
	   problem.

	   Since we're in effect restarting from the payload data, we reset
	   everything that counts to point back to the start of the buffer where
	   we'll be moving the payload data.  We don't have to worry about the
	   copyToDeenvelope() overflowing the envelope since the source is the
	   envelope buffer so the data must fit within the envelope */
	envelopeInfoPtr->bufPos = 0;
	if( bytesLeft <= 0 )
		{
		/* Handle the special case of the data ending at exactly this point */
		sseek( stream, 0 );
		return( CRYPT_ERROR_UNDERFLOW );
		}
	sMemDisconnect( stream );
	sMemConnect( stream, envelopeInfoPtr->buffer, bytesLeft );
	bytesCopied = envelopeInfoPtr->copyToEnvelopeFunction( envelopeInfoPtr,
							envelopeInfoPtr->buffer + dataStartPos, bytesLeft );
	if( cryptStatusError( bytesCopied ) )
		{
		/* Undo the buffer position reset.  This isn't 100% effective if
		   there are multiple segments present and we hit an error after
		   we've copied down enough data to overwrite what's at the start,
		   but in most cases it allows us to undo the copyToEnvelope() - if
		   the data is corrupted we won't get any further anyway */
		envelopeInfoPtr->bufPos = oldBufPos;
		return( bytesCopied );
		}
	assert( bytesCopied >= 0 );

	/* If we've reached the end of the payload, remember where the payload
	   ends.  If there's anything that followed the payload, we need to move
	   it down to the end of the decoded payload data, since
	   copyToDeenvelope() stops copying as soon as it hits the end-of-
	   contents octets.  We use memmove() rather than memcpy() since we're
	   copying to/from the same buffer */
	if( ( envelopeInfoPtr->dataFlags & ENVDATA_ENDOFCONTENTS ) && \
		bytesCopied < bytesLeft )
		{
		const int bytesToCopy = bytesLeft - bytesCopied;

		assert( bytesToCopy > 0 );
		memmove( envelopeInfoPtr->buffer + envelopeInfoPtr->dataLeft,
				 envelopeInfoPtr->buffer + bytesCopied + dataStartPos,
				 bytesToCopy );
		envelopeInfoPtr->bufPos = envelopeInfoPtr->dataLeft + bytesToCopy;
		}

	return( CRYPT_OK );
	}

/* Process additional out-of-band data that doesn't get copied into/out of
   the de-enveloping envelope */

static int processExtraData( ENVELOPE_INFO *envelopeInfoPtr,
							 const void *buffer, const int length )
	{
	ACTION_LIST *hashActionPtr;
	int iterationCount = 0;

	assert( length >= 0 );

	/* If the hash value was supplied externally (which means that there's
	   nothing for us to hash, since it's already been done by the caller),
	   there won't be any hash actions active and we can return immediately */
	if( !( envelopeInfoPtr->dataFlags & ENVDATA_HASHACTIONSACTIVE ) )
		return( length ? CRYPT_ERROR_BADDATA : CRYPT_OK );

	/* The enveloping code uses a null buffer to signify a flush, but the
	   lower-level encryption actions don't allow a null buffer.  If we're
	   given a null buffer we substitute an empty (non-null) one */
	if( buffer == NULL )
		buffer = "";

	/* Hash the data or wrap up the hashing as appropriate */
	for( hashActionPtr = envelopeInfoPtr->actionList;
		 hashActionPtr != NULL && hashActionPtr->action == ACTION_HASH && \
			iterationCount++ < FAILSAFE_ITERATIONS_MED;
		 hashActionPtr = hashActionPtr->next )
		{
		int status;

		status = krnlSendMessage( hashActionPtr->iCryptHandle,
								  IMESSAGE_CTX_HASH, ( void * ) buffer,
								  length );
		if( cryptStatusError( status ) )
			return( status );
		}
	if( iterationCount >= FAILSAFE_ITERATIONS_MED )
		retIntError();

	/* If we've finished the hashing, clear the hashing-active flag to
	   prevent data from being hashed again if it's processed by other
	   code such as copyFromDeenvelope() */
	if( length <= 0 )
		envelopeInfoPtr->dataFlags &= ~ENVDATA_HASHACTIONSACTIVE;

	return( CRYPT_OK );
	}

/****************************************************************************
*																			*
*							Envelope Access Routines						*
*																			*
****************************************************************************/

void initDeenvelopeStreaming( ENVELOPE_INFO *envelopeInfoPtr )
	{
	/* Set the access method pointers */
	envelopeInfoPtr->copyToEnvelopeFunction = copyToDeenvelope;
	envelopeInfoPtr->copyFromEnvelopeFunction = copyFromDeenvelope;
	envelopeInfoPtr->syncDeenvelopeData = syncDeenvelopeData;
	envelopeInfoPtr->processExtraData = processExtraData;
	}
#endif /* USE_ENVELOPES */
