⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sess_rw.c

📁 cryptlib是功能强大的安全工具集。允许开发人员快速在自己的软件中集成加密和认证服务。
💻 C
📖 第 1 页 / 共 3 页
字号:
/****************************************************************************
*																			*
*				cryptlib Session Read/Write Support Routines				*
*					  Copyright Peter Gutmann 1998-2004						*
*																			*
****************************************************************************/

#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#if defined( INC_ALL )
  #include "crypt.h"
  #include "asn1.h"
  #include "session.h"
#elif defined( INC_CHILD )
  #include "../crypt.h"
  #include "../misc/asn1.h"
  #include "session.h"
#else
  #include "crypt.h"
  #include "misc/asn1.h"
  #include "session/session.h"
#endif /* Compiler-specific includes */

#ifdef USE_SESSIONS

/* Common code to read and write data over the secure connection.  This
   is called by the protocol-specific handlers, which supply three functions:

	readHeaderFunction()	- Reads the header for a packet and sets up
							  length information.
	processBodyFunction()	- Processes the body of a packet.
	preparePacketFunction()	- Wraps a packet in preparation for sending it.

   The behaviour of the network-level stream handlers is as follows:

	Timeout		byteCount		Result
	-------		---------		------
		  - error -				error
	  0			  0				0
	  0			> 0				byteCount
	> 0			  0				CRYPT_ERROR_TIMEOUT
	> 0			> 0				byteCount

   Errors in the processBodyFunction() and preparePacketFunction() are 
   always fatal.  In theory we could try to recover, however the functions 
   update assorted crypto state such as packet sequence numbers and IVs that 
   would be tricky to roll back, and in practice recoverable errors are 
   likely to be extremely rare (at best perhaps a CRYPT_ERROR_TIMEOUT for a 
   context tied to a device, however even this won't occur since the 
   conventional encryption and MAC contexts are all internal native 
   contexts), so there's little point in trying to make the functions 
   recoverable */

/****************************************************************************
*																			*
*						Secure Session Data Read Functions					*
*																			*
****************************************************************************/

/* The read data code uses a helper function tryRead() that either reads
   everything which is available or to the end of the current packet.  In
   other words it's an atomic, all-or-nothing function that can be used by
   higher-level code to handle network-level packetisation.  Buffer
   management is handled as follows: The bPos index always points to the end
   of the decoded data (i.e. data that can be used by the user), if there's
   no partial packet present this index is the same as bEnd:

	----+------------------------
	////|
	----+------------------------
		^
		|
	bEnd/bPos

   If there's a partial packet present, pendingPacketRemaining contains the
   number of bytes required to complete the packet and bEnd points to the
   end of the received data, and is advanced as more data is read:

							<----> pPR
	----+-------------------+----+----
	////|///////////////////|....|
	----+-------------------+----+----
		^					^
		|					|
	  bPos				  bEnd

   Once the complete packet is read (pPR reaches 0), it's decrypted, and
   bPos and bEnd are adjusted to point to the end of the new data:

	----+------------------------+----
	////|////////////////////////|
	----+------------------------+----
								 ^
								 |
							 bEnd/bPos

   The handling of any header data present at the start of the packet
   depends on the packet format, if the header is independent of the
   encrypted data it's handled entirely by the readHeaderFunction() and 
   there's no need to provide special-case handling.  If the header is part 
   of the encrypted data, decryption is a two-stage operation in which
   readHeaderFunction() decrypts just enough of the packet to extract and
   process the header (depositing any leftover non-header data at the start
   of the buffer) and processBodyFunction() processes the rest of the data.

   Errors in the readHeaderFunction() are fatal if they come from the session
   protocol level (e.g. a MAC failure or bad packet) and nonfatal if they
   come from the network layer below the session (the stream-level code has
   its own handling of fatal vs. nonfatal errors, so we don't try and get
   down to that level) */

static int tryRead( SESSION_INFO *sessionInfoPtr, READSTATE_INFO *readInfo )
	{
	int bytesLeft, status;

	/* Clear return value */
	*readInfo = READINFO_NONE;

	/* If there's no pending packet information present, try and read it.
	   This can return one of four classes of values:

		1. An error code.
		2. Zero, to indicate that nothing was read.
		3. OK_SPECIAL and read info READINFO_NOOP to indicate that header
		   data but no payload data was read.
		4. A byte count and read info READINFO_HEADERPAYLOAD to indicate
		   that some payload data was read as part of the header */
	if( sessionInfoPtr->pendingPacketLength <= 0 )
		{
		status = sessionInfoPtr->readHeaderFunction( sessionInfoPtr, readInfo );
		if( status <= 0 && status != OK_SPECIAL )
			return( status );
		assert( ( status == OK_SPECIAL && *readInfo == READINFO_NOOP ) || \
				( status > 0 && *readInfo == READINFO_HEADERPAYLOAD ) );
		if( *readInfo == READINFO_HEADERPAYLOAD )
			{
			/* Some protocols treat the header information for a secured
			   data packet as part of the data, so when we read the header we
			   can get part of the payload included in the read.  When the
			   protocol-specific header read code obtained some payload data
			   alongside the header, it returns READINFO_HEADERPAYLOAD to
			   indicate that the packet info needs to be adjusted for the
			   packet header data that was just read */
			sessionInfoPtr->receiveBufEnd += status;
			sessionInfoPtr->pendingPacketPartialLength = status;
			sessionInfoPtr->pendingPacketRemaining -= status;
			}
		}
	bytesLeft = sessionInfoPtr->receiveBufSize - sessionInfoPtr->receiveBufEnd;

	assert( sessionInfoPtr->partialHeaderLength == 0 );

	/* Sanity-check the read state */
	if( sessionInfoPtr->receiveBufEnd < 0 || \
		sessionInfoPtr->receiveBufEnd > sessionInfoPtr->receiveBufSize || \
		sessionInfoPtr->receiveBufPos < 0 || \
		sessionInfoPtr->receiveBufPos > sessionInfoPtr->receiveBufEnd || \
		sessionInfoPtr->pendingPacketLength < 0 || \
		sessionInfoPtr->pendingPacketRemaining <= 0 || \
		sessionInfoPtr->pendingPacketPartialLength < 0 )
		{
		assert( NOTREACHED );
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Internal error: Inconsistent state detected in session "
				"read stream" );
		}

	/* If there's not enough room in the receive buffer to read at least 1K
	   of packet data, don't try anything until the user has emptied more
	   data from the buffer */
	if( bytesLeft < min( sessionInfoPtr->pendingPacketRemaining, 1024 ) )
		return( 0 );

	/* Try and read more of the packet */
	status = sread( &sessionInfoPtr->stream,
					sessionInfoPtr->receiveBuffer + sessionInfoPtr->receiveBufEnd,
					min( sessionInfoPtr->pendingPacketRemaining, bytesLeft ) );
	if( cryptStatusError( status ) )
		{
		sNetGetErrorInfo( &sessionInfoPtr->stream,
						  sessionInfoPtr->errorMessage,
						  &sessionInfoPtr->errorCode );
		return( status );
		}
	if( status <= 0 )
		/* Nothing read, try again later.  This happens only if we're using
		   non-blocking reads (i.e. polled I/O), if any kind of timeout is
		   specified we'll get a timeout error if no data is read */
		return( 0 );
	sessionInfoPtr->receiveBufEnd += status;
	sessionInfoPtr->pendingPacketRemaining -= status;
	if( sessionInfoPtr->pendingPacketRemaining > 0 )
		{
		/* We got some but not all of the data, try again later */
		*readInfo = READINFO_PARTIAL;
		return( OK_SPECIAL );
		}
	assert( sessionInfoPtr->pendingPacketRemaining == 0 );

	/* We've got a complete packet in the buffer, process it */
	return( sessionInfoPtr->processBodyFunction( sessionInfoPtr, readInfo ) );
	}

/* Get data from the remote system */

static int getData( SESSION_INFO *sessionInfoPtr, 
					BYTE *buffer, const int length, int *bytesCopied )
	{
	const int bytesToCopy = min( length, sessionInfoPtr->receiveBufPos );
	READSTATE_INFO readInfo;
	int remainder, status;

	assert( bytesToCopy >= 0 );

	/* Clear return value */
	*bytesCopied = 0;

	/* Sanity-check the read state */
	if( sessionInfoPtr->receiveBufPos < 0 || \
		sessionInfoPtr->receiveBufPos > sessionInfoPtr->receiveBufEnd || \
		sessionInfoPtr->receiveBufEnd < 0 || \
		sessionInfoPtr->receiveBufEnd > sessionInfoPtr->receiveBufSize )
		{
		assert( NOTREACHED );
		retExt( sessionInfoPtr, CRYPT_ERROR_BADDATA,
				"Internal error: Inconsistent state detected in session "
				"read stream" );
		}

	/* Copy as much data as we can across and move any remaining data down
	   to the start of the receive buffer.  We copy out up to receiveBufPos,
	   the end of the decoded data, but move up to receiveBufEnd, the 
	   combined decoded data and any as-yet-undecoded partial data that
	   follows the decoded data */
	if( bytesToCopy > 0 )
		{
		memcpy( buffer, sessionInfoPtr->receiveBuffer, bytesToCopy );
		remainder = sessionInfoPtr->receiveBufEnd - bytesToCopy;
		assert( remainder >= 0 );
		if( remainder > 0 )
			{
			/* There's decoded and/or non-decoded data left, move it down to
			   the start of the buffer */
			memmove( sessionInfoPtr->receiveBuffer,
					 sessionInfoPtr->receiveBuffer + bytesToCopy, remainder );
			sessionInfoPtr->receiveBufPos -= bytesToCopy;
			sessionInfoPtr->receiveBufEnd = remainder;
			}
		else
			/* We've consumed all of the data in the buffer, reset the buffer
			   info */
			sessionInfoPtr->receiveBufPos = sessionInfoPtr->receiveBufEnd = 0;
		assert( sessionInfoPtr->receiveBufPos >= 0 );

		/* Remember how much we've copied and, if we've satisfied the 
		   request, exit */
		*bytesCopied = bytesToCopy;
		if( bytesToCopy >= length )
			return( CRYPT_OK );
		}
	assert( sessionInfoPtr->receiveBufPos == 0 );

	/* Try and read a complete packet.  This can return one of four classes 
	   of values:

		1. An error code.
		2. Zero to indicate that nothing was read (only happens on non-
		   blocking reads (polled I/O), a blocking read will return a 
		   timeout error) or that there isn't enough room left in the read 
		   buffer to read any more.
		3a.OK_SPECIAL and read info READINFO_PARTIAL to indicate that a
		   partial packet (not enough to process) was read.
		3b.OK_SPECIAL and read info READINFO_NOOP to indicate that a no-op 
		   packet was read and the caller should try again without changing 
		   the read timeout value.
		4. A byte count if a complete packet was read and processed */
	status = tryRead( sessionInfoPtr, &readInfo );
	if( cryptStatusError( status ) && status != OK_SPECIAL )
		{
		/* If there's an error reading data, only return an error status if 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -