/*********************************************************************
 *
 *  SSLv3 Protocol Client and Server Implementation
 *  Module for Microchip TCP/IP Stack
 *    - Implements an SSL layer supporting both client and server
 *		operation for any given TCP socket.
 *
 **********************************************************************
 * FileName:        SSL.c
 * Dependencies:    TCPIP.h
 *					SSL.h
 *                  TCP.h
 * Processor:       PIC18, PIC24F, PIC24H, dsPIC30F, dsPIC33F, PIC32
 * Compiler:        Microchip C32 v1.05 or higher
 *					Microchip C30 v3.12 or higher
 *					Microchip C18 v3.30 or higher
 * Company:         Microchip Technology, Inc.
 *
 * Software License Agreement
 *
 * Copyright (C) 2002-2009 Microchip Technology Inc.  All rights
 * reserved.
 *
 * Microchip licenses to you the right to use, modify, copy, and
 * distribute:
 * (i)  the Software when embedded on a Microchip microcontroller or
 *      digital signal controller product ("Device") which is
 *      integrated into Licensee's product; or
 * (ii) ONLY the Software driver source files ENC28J60.c, ENC28J60.h,
 *		ENCX24J600.c and ENCX24J600.h ported to a non-Microchip device
 *		used in conjunction with a Microchip ethernet controller for
 *		the sole purpose of interfacing with the ethernet controller.
 *
 * You should refer to the license agreement accompanying this
 * Software for additional information regarding your rights and
 * obligations.
 *
 * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT
 * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT
 * LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL
 * MICROCHIP BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF
 * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS
 * BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE
 * THEREOF), ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER
 * SIMILAR COSTS, WHETHER ASSERTED ON THE BASIS OF CONTRACT, TORT
 * (INCLUDING NEGLIGENCE), BREACH OF WARRANTY, OR OTHERWISE.
 *
 *
 * Author               Date        Comment
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * Elliott Wood			6/20/07		Original
 * Elliott Wood			12/17/07	Rewritten to integrate with TCP
 *									 and support both client & server
 ********************************************************************/
#define __SSL_C

#include "TCPIPConfig.h"

#if defined(STACK_USE_SSL_SERVER) || defined(STACK_USE_SSL_CLIENT)

#include "TCPIP Stack/TCPIP.h"
	
/****************************************************************************
  Section:
	SSL Connection State Global Variables
  ***************************************************************************/
	#if defined(__18CXX) && !defined(HI_TECH_C)	
		#pragma udata SSL_STATE_RAM
	#endif
	static SSL_STUB sslStub;		// The current SSL stub
	static BYTE sslStubID;			// Which SSL_STUB is loaded
	static SSL_KEYS sslKeys;		// The current SSL session
	static BYTE sslKeysID;			// Which SSL_KEYS are loaded
	static BYTE sslBufferID;		// Which buffer is loaded
	static BYTE sslHashID;			// Which hash is loaded
	static BYTE sslSessionID;		// Which session is loaded
	static BOOL sslSessionUpdated;	// Whether or not it has been updated
	static BYTE sslRSAStubID;		// Which stub is using RSA, if any
	
	#if defined(__18CXX) && !defined(HI_TECH_C)	
		#pragma udata SSL_LARGE_RAM
	#endif
	SSL_BUFFER sslBuffer;			// SBox and RSA storage

	#if defined(__18CXX) && !defined(HI_TECH_C)	
		#pragma udata SSL_HASH_RAM
	#endif
	HASH_SUM sslHash;				// Hash storage

	#if defined(__18CXX) && !defined(HI_TECH_C)	
		#pragma udata SSL_SESSION_RAM
	#endif
	SSL_SESSION sslSession;			// Current session data
	
	// 8 byte session stubs
	SSL_SESSION_STUB sslSessionStubs[MAX_SSL_SESSIONS];
	
	BYTE *ptrHS;					// Used in buffering handshake results

	extern ROM WORD SSL_CERT_LEN;	// RSA public certificate length		?
	extern ROM BYTE SSL_CERT[];		// RSA public certificate data			?

	#if defined(__18CXX) && !defined(HI_TECH_C)	
		#pragma udata						
	#endif
	
/****************************************************************************
  Section:
	Function Prototypes
  ***************************************************************************/
	// Section: Cryptographic Calculation Functions
	static RSA_STATUS SSLRSAOperation(void);
	static void GenerateHashRounds(BYTE num, BYTE* rand1, BYTE* rand2);
	static void CalculateFinishedHash(BYTE hashID, BOOL fromClient, BYTE *result);
	static void GenerateSessionKeys(void);

	// Section: Ethernet Buffer RAM Management
	static void SSLStubSync(BYTE id);
	static BOOL SSLStubAlloc(void);
	static void SSLStubFree(BYTE id);
	static void SSLKeysSync(BYTE id);
	static void SSLHashSync(BYTE id);
	static void SSLHashAlloc(BYTE *id);
	static void SSLHashFree(BYTE *id);
	static void SSLBufferSync(BYTE id);
	static void SSLBufferAlloc(BYTE *id);
	static void SSLBufferFree(BYTE *id);
	static BYTE SSLSessionNew(void);
	static void SSLSessionSync(BYTE id);
	#define SSLSessionUpdated()		sslSessionUpdated = TRUE;
	static void SaveOffChip(BYTE *ramAddr, PTR_BASE ethAddr, WORD len);
	static void LoadOffChip(BYTE *ramAddr, PTR_BASE ethAddr, WORD len);
	
	// Section: Handshake Hash and I/O Functions
	static void HSStart(void);
	static void HSEnd(void);
	static WORD HSGet(TCP_SOCKET skt, BYTE *b);
	static WORD HSGetWord(TCP_SOCKET skt, WORD *w);
	static WORD HSGetArray(TCP_SOCKET skt, BYTE *data, WORD len);
	static WORD HSPut(TCP_SOCKET skt, BYTE b);
	static WORD HSPutWord(TCP_SOCKET skt, WORD w);
	static WORD HSPutArray(TCP_SOCKET skt, BYTE *data, WORD len);
	#if defined(STACK_USE_SSL_SERVER)
	static WORD HSPutROMArray(TCP_SOCKET skt, ROM BYTE *data, WORD len);
	#endif
	
	// Section: Client messages
	#if defined(STACK_USE_SSL_CLIENT)
		static BYTE SSLSessionMatchIP(IP_ADDR ip);
		static void SSLTxClientHello(TCP_SOCKET hTCP);
		static void SSLRxServerHello(TCP_SOCKET hTCP);
		static void SSLRxServerCertificate(TCP_SOCKET hTCP);
		static void SSLTxClientKeyExchange(TCP_SOCKET hTCP);
	#endif
	
	// Section: Server messages
	#if defined(STACK_USE_SSL_SERVER)
		static BYTE SSLSessionMatchID(BYTE* SessionID);
		static void SSLRxAntiqueClientHello(TCP_SOCKET hTCP);
		static void SSLRxClientHello(TCP_SOCKET hTCP);
		static void SSLTxServerHello(TCP_SOCKET hTCP);
		static void SSLTxServerCertificate(TCP_SOCKET hTCP);
		static void SSLTxServerHelloDone(TCP_SOCKET hTCP);
		static void SSLRxClientKeyExchange(TCP_SOCKET hTCP);
	#endif
	
	// Section: Client and server messages
	static void SSLTxCCSFin(TCP_SOCKET hTCP);
	static void SSLRxCCS(TCP_SOCKET hTCP);
	static void SSLRxFinished(TCP_SOCKET hTCP);
	static void SSLRxAlert(TCP_SOCKET hTCP);

/****************************************************************************
  Section:
	Macros and Definitions
  ***************************************************************************/
	#define mMIN(a, b)	((a<b)?a:b)

	#define SSL_RSA_EXPORT_WITH_ARCFOUR_40_MD5	0x0003u
	#define SSL_RSA_WITH_ARCFOUR_128_MD5		0x0004u

/****************************************************************************
  Section:
	Resource Management Variables
  ***************************************************************************/
WORD isStubUsed;			// Indicates which stubs are in use
WORD isHashUsed;			// Indicates which hashes are in use
WORD isBufferUsed;			// Indicates which buffers are in use

// Masks for each bit in the is*Used variables
static ROM WORD masks[16] = { 0x0001, 0x0002, 0x0004, 0x0008,
							  0x0010, 0x0020, 0x0040, 0x0080, 
							  0x0100, 0x0200, 0x0400, 0x0800,
							  0x1000, 0x2000, 0x4000, 0x8000 };

// Base address for SSL stubs
#define SSL_BASE_STUB_ADDR			(BASE_SSLB_ADDR)

// Base address for SSL keys
#define SSL_BASE_KEYS_ADDR			(BASE_SSLB_ADDR + SSL_STUB_SPACE)

// Base address for SSL hashes
#define SSL_BASE_HASH_ADDR			(BASE_SSLB_ADDR + SSL_STUB_SPACE + SSL_KEYS_SPACE)

// Base address for SSL buffers
#define SSL_BASE_BUFFER_ADDR		(BASE_SSLB_ADDR + SSL_STUB_SPACE + SSL_KEYS_SPACE + SSL_HASH_SPACE)

// Base address for SSL sessions
#define SSL_BASE_SESSION_ADDR		(BASE_SSLB_ADDR + SSL_STUB_SPACE + SSL_KEYS_SPACE + SSL_HASH_SPACE + SSL_BUFFER_SPACE)


/****************************************************************************
  ===========================================================================
  Section:
	SSL Management Functions
  ===========================================================================
  ***************************************************************************/

/*****************************************************************************
  Function:
	void SSLInit(void)

  Description:
	Initializes the SSL engine.

  Precondition:
	None

  Parameters:
	None

  Returns:
  	None
  	
  Remarks:
	This function is called only one during lifetime of the application.
  ***************************************************************************/
void SSLInit(void)
{
	// Set all resources to unused
	isStubUsed = 0;
	isHashUsed = 0;
	isBufferUsed = 0;
	for(sslSessionID = 0; sslSessionID < MAX_SSL_SESSIONS; sslSessionID++)
		sslSessionStubs[sslSessionID].tag.Val = 0;
		
	// Indicate that nothing is loaded
	sslHashID = SSL_INVALID_ID;
	sslStubID = SSL_INVALID_ID;
	sslSessionID = SSL_INVALID_ID;
	sslKeysID = SSL_INVALID_ID;
	sslBufferID = SSL_INVALID_ID;
	sslSessionUpdated = FALSE;
	sslRSAStubID = SSL_INVALID_ID;
}	

/*****************************************************************************
  Function:
	void SSLPeriodic(TCP_SOCKET hTCP, BYTE id)

  Summary:
	Performs any periodic tasks for the SSL module.

  Description:
	This function performs periodic tasks for the SSL module.  This includes
	processing for RSA operations.

  Precondition:
	SSL has already been initialized.

  Parameters:
	hTCP - the socket for which to perform periodic functions
	id - the SSL stub to use
	
  Returns:
  	None
  	
  ***************************************************************************/
void SSLPeriodic(TCP_SOCKET hTCP, BYTE id)
{
	// Sync the SSL Stub
	SSLStubSync(id);
	
	// For new sessions, try to claim a session
	if(sslStub.Flags.bNewSession && sslStub.idSession == SSL_INVALID_ID)
	{
		sslStub.idSession = SSLSessionNew();
	}
	
	// If RSA is in progress, do some RSA work
	if(sslStub.Flags.bRSAInProgress)
	{			
		if(SSLRSAOperation() == RSA_DONE)
		{// Move on with the connection
			sslStub.Flags.bRSAInProgress = 0;

			// For clients, request the CKE message
			#if defined(STACK_USE_SSL_CLIENT)
			if(!sslStub.Flags.bIsServer)
				TCPRequestSSLMessage(hTCP, SSL_CLIENT_KEY_EXCHANGE);
			#endif
			
			// For servers, copy the decoded message to the session data
			#if defined(STACK_USE_SSL_SERVER)
			if(sslStub.Flags.bIsServer)
			{
				// Copy over the pre-master secret
				SSLSessionSync(sslStub.idSession);
				memcpy((void*)sslSession.masterSecret, (void*)&sslBuffer.full[16], 48);
												
				// Generate the Master Secret
				SSLKeysSync(sslStubID);
				SSLBufferSync(SSL_INVALID_ID);
				GenerateHashRounds(3, sslKeys.Remote.random, sslKeys.Local.random);
				memcpy(sslSession.masterSecret, (void*)sslBuffer.hashRounds.temp, 48);
				
				// Note the new session data and release RSA engine
				SSLSessionUpdated();
				RSAEndUsage();
				sslRSAStubID = SSL_INVALID_ID;
			}
			
			// Continue receiving the CCS and Finished messages
			TCPSSLHandleIncoming(hTCP);
			#endif
		}
	}
}

/*****************************************************************************
  Function:
	BYTE SSLStartSession(TCP_SOCKET hTCP, BYTE * buffer, BYTE supDataType)

  Description:
	Begins a new SSL session for the given TCP connection.

  Precondition:
	SSL has been initialized and hTCP is connected.

  Parameters:
	hTCP - the socket to begin the SSL connection on
	buffer - pointer to a supplementary data buffer
	supDataType - type of supplementary data to store
	
  Return Values:
  	SSL_INVALID_ID - insufficient SSL resources to start a new connection
  	others - the allocated SSL stub ID
  ***************************************************************************/
BYTE SSLStartSession(TCP_SOCKET hTCP, void * buffer, BYTE supDataType)
{
	BYTE i;
	
	// Allocate a stub for use, or fail
	if(!SSLStubAlloc())
		return SSL_INVALID_ID;
	
	// Clear stub state
	sslStub.wRxBytesRem = 0;
	sslStub.wRxHsBytesRem = 0;
	*(WORD*)&sslStub.Flags = 0x0000;
	
	// Clear any allocations
	sslStub.idSession = SSL_INVALID_ID;
	sslStub.idRxHash = SSL_INVALID_ID;
	sslStub.idMD5 = SSL_INVALID_ID;
	sslStub.idSHA1 = SSL_INVALID_ID;
	sslStub.idRxBuffer = SSL_INVALID_ID;
	sslStub.idTxBuffer = SSL_INVALID_ID;
	sslStub.requestedMessage = SSL_NO_MESSAGE;
	sslStub.dwTemp.Val = 0;
	sslStub.supplementaryBuffer = buffer;
    sslStub.supplementaryDataType = supDataType;

	// Allocate handshake hashes for use, or fail
	SSLHashAlloc(&sslStub.idMD5);
	SSLHashAlloc(&sslStub.idSHA1);
	if(sslStub.idMD5 == SSL_INVALID_ID || sslStub.idSHA1 == SSL_INVALID_ID)
	{
		SSLHashFree(&sslStub.idMD5);
		SSLHashFree(&sslStub.idSHA1);
		SSLStubFree(sslStubID);
		return SSL_INVALID_ID;
	}
	
	// Initialize the handshake hashes
	SSLHashSync(sslStub.idSHA1);
	SHA1Initialize(&sslHash);
	SSLHashSync(sslStub.idMD5);
	MD5Initialize(&sslHash);
	
	// Set up Local.random (4 byte UTC time, 28 bytes random)
	SSLKeysSync(sslStubID);
	#if defined(STACK_USE_SNTP_CLIENT)
	{
		DWORD_VAL temp;

		temp.Val = SNTPGetUTCSeconds();
		sslKeys.Local.random[0] = temp.v[3];
		sslKeys.Local.random[1] = temp.v[2];
		sslKeys.Local.random[2] = temp.v[1];
		sslKeys.Local.random[3] = temp.v[0];
		i = 4;
	}
	#else
		i = 0;
	#endif
	while(i < 32u)
		sslKeys.Local.random[i++] = RandomGet();
		
	// Return the ID
	return sslStubID;
}

/*****************************************************************************
  Function:
	void SSLTerminate(BYTE id)

  Description:
	Terminates an SSL connection and releases allocated resources.

  Precondition:
	None

  Parameters:
	id - the SSL stub ID to terminate
	
  Returns:
  	None
  ***************************************************************************/
void SSLTerminate(BYTE id)
{
	// Sync in the right stub
	SSLStubSync(id);
	
	// If no CloseNotify, then invalidate the session so it cannot resume
	// ( This restriction is not presently enforced.  IE incorrectly
	//   completes the handshake, then disconnects without a CloseNotify
	//   when it decides to prompt the user whether or not to accept a 
	//   unverifiable certificate. )
	//if(!sslStub.Flags.bCloseNotify)
	//{
	//	sslSessionStubs[sslStub.idSession].tag.Val = 0;
	//}	
	
	// Free up resources
	SSLBufferFree(&sslStub.idRxBuffer);
	SSLBufferFree(&sslStub.idTxBuffer);
	SSLHashFree(&sslStub.idMD5);
	SSLHashFree(&sslStub.idSHA1);
	SSLHashFree(&sslStub.idRxHash);
	SSLStubFree(id);
	if(sslRSAStubID == id)
	{
		sslRSAStubID = SSL_INVALID_ID;
		RSAEndUsage();
	}
	
}

/****************************************************************************
  ===========================================================================
  Section:
	SSL Record Processing Functions
  ===========================================================================
  ***************************************************************************/

/*****************************************************************************
  Function:
	WORD SSLRxRecord(TCP_SOCKET hTCP, BYTE id)

  Summary:
	Receives an SSL record.

  Description:
	Reads at most one SSL Record header from the TCP stream and determines what
	to do with the rest of the data.  If not all of the data is available for 
	the record, then the function returns and future call(s) to SSLRxRecord() 
	will process the remaining data until the end of the record is reached.  
	If this call process data from a past record, the next record will not be 
	started until the next call.

  Precondition:
	The specified SSL stub is initialized and the TCP socket is connected.

  Parameters:
	hTCP - The TCP socket from which to read
	id - The active SSL stub ID
	
  Returns:
  	WORD indicating the number of data bytes there were decrypted but left in 
  	the stream.

  Remarks:
  	SSL record headers, MAC footers, and symetric cipher block padding (if any) 
  	will be extracted from the TCP stream by this function.  Data will be 
  	decrypted but left in the stream.
  ***************************************************************************/
WORD SSLRxRecord(TCP_SOCKET hTCP, BYTE id)
{	
	BYTE temp[32];
	WORD wLen;
	
	SSLStubSync(id);
	
	// Don't do anything for terminated connections
	if(sslStub.Flags.bDone)
		return 0;

	// If this is a new record, then read the header
	// When bytes remain, a message is not yet fully read, so
	// the switch statement will continue handling the data
	if(sslStub.wRxBytesRem == 0u)
	{
		// See if we expect a MAC
		if(sslStub.Flags.bExpectingMAC)
		{// Receive and verify the MAC
			if(TCPIsGetReady(hTCP) < 16u)
				return 0;
				
			// Read the MAC
			TCPGetArray(hTCP, temp, 16);
			
			// Calculate the expected MAC
			SSLBufferSync(sslStub.idRxBuffer);
			SSLKeysSync(id);
			SSLHashSync(sslStub.idRxHash);
			
			ARCFOURCrypt(&sslKeys.Remote.app.cryptCtx, temp, 16);
			SSLMACCalc(sslKeys.Remote.app.MACSecret, &temp[16]);
			
			// MAC no longer expected
			sslStub.Flags.bExpectingMAC = 0;
			
			// Verify the MAC
			if(memcmp((void*)temp, (void*)&temp[16], 16) != 0)
			{// MAC fails
				TCPRequestSSLMessage(hTCP, SSL_ALERT_BAD_RECORD_MAC);
				return 0;
			}
		}	
		
		// Check if a new header is available
		// Also ignore data if SSL is terminated
		if(TCPIsGetReady(hTCP) < 5u)
			return 0;
		
		// Read the record type (BYTE)
		TCPGet(hTCP, &sslStub.rxProtocol);
		
		#if defined(STACK_USE_SSL_SERVER)
		// Check if we've received an SSLv2 ClientHello message
		// Client-only implementations don't need to deal with this
		if((sslStub.rxProtocol & 0x80) == 0x80)
		{
			// After MSB, next 15 bits are the length
			((BYTE*)&sslStub.wRxBytesRem)[1] = sslStub.rxProtocol & 0x7F;
			TCPGet(hTCP, ((BYTE*)&sslStub.wRxBytesRem));
			
			// Tell the handshaker what message to expect
			sslStub.wRxHsBytesRem = sslStub.wRxBytesRem;
			sslStub.rxProtocol = SSL_HANDSHAKE;
			sslStub.rxHSType = SSL_ANTIQUE_CLIENT_HELLO;
		}
		
		// Otherwise, this is a normal SSLv3 message
		// Read the rest of the record header and proceed normally
		else
		#endif
		{
			// Read version (WORD, currently ignored)
			TCPGet(hTCP, NULL);
			TCPGet(hTCP, NULL);
	
			// Read length (WORD)
			TCPGet(hTCP, ((BYTE*)&sslStub.wRxBytesRem)+1);
			TCPGet(hTCP, ((BYTE*)&sslStub.wRxBytesRem));
			
			// Determine if a MAC is expected
			if(sslStub.Flags.bRemoteChangeCipherSpec)
			{
				sslStub.Flags.bExpectingMAC = 1;
				sslStub.wRxBytesRem -= 16;
							
				// Set up the MAC
				SSLKeysSync(sslStubID);
				SSLHashSync(sslStub.idRxHash);
				SSLMACBegin(sslKeys.Remote.app.MACSecret, 
					sslKeys.Remote.app.sequence++, 
					sslStub.rxProtocol, sslStub.wRxBytesRem);
			}
		}
		
	}
	
	// See if data is ready that needs decryption
	wLen = TCPIsGetReady(hTCP);

	// Decrypt and MAC if necessary
	if(sslStub.Flags.bRemoteChangeCipherSpec && wLen)
	{// Need to decrypt the data
		
		// Only decrypt up to end of record
		if(wLen > sslStub.wRxBytesRem)
			wLen = sslStub.wRxBytesRem;
						
		// Prepare for decryption
		SSLKeysSync(id);
		SSLBufferSync(sslStub.idRxBuffer);
		SSLHashSync(sslStub.idRxHash);

		// Decrypt application data to proper location, non-app in place
		TCPSSLDecryptMAC(hTCP, &sslKeys.Remote.app.cryptCtx, wLen);
	}
	
	// Determine what to do with the rest of the data
	switch(sslStub.rxProtocol)
	{
		case SSL_HANDSHAKE:
			SSLRxHandshake(hTCP);
			break;
			
		case SSL_CHANGE_CIPHER_SPEC:
			SSLRxCCS(hTCP);
			break;
			
		case SSL_APPLICATION:
			// Data was handled above
			// Just note that it's all been read
			sslStub.wRxBytesRem -= wLen;
			return wLen;
		
		case SSL_ALERT:
			SSLRxAlert(hTCP);
			break;
	}
	
	return 0;
}

/*****************************************************************************
  Function:
	void SSLTxRecord(TCP_SOCKET hTCP, BYTE id, BYTE txProtocol)

  Summary:
	Transmits an SSL record.

  Description:
	Transmits all pending data in the TCP TX buffer as an SSL record using
	the specified protocol.  This function transparently encrypts and MACs
	the data if there is an active cipher spec.

  Precondition:
	The specified SSL stub is initialized and the TCP socket is connected.

  Parameters:
	hTCP - The TCP socket with data waiting to be transmitted
	id - The active SSL stub ID
	txPortocol - The SSL protocol number to attach to this record
	
  Returns:
  	None
  ***************************************************************************/
void SSLTxRecord(TCP_SOCKET hTCP, BYTE id, BYTE txProtocol)
{
	WORD_VAL wLen;
	BYTE hdr[5];
	
	SSLStubSync(id);
	
	// If stub is done, prevent writing data
	if(sslStub.Flags.bDone)
		return;
	
	// Determine how many bytes are ready to write
	wLen.Val = TCPSSLGetPendingTxSize(hTCP);
	if(wLen.Val == 0u)
		return;
	
	// Determine if a MAC is required
	if(sslStub.Flags.bLocalChangeCipherSpec)
	{// Perform the encryption and MAC
		// Sync needed data
		SSLKeysSync(sslStubID);
		SSLHashSync(SSL_INVALID_ID);
		SSLBufferSync(sslStub.idTxBuffer);
		
		// Start the MAC calculation
		SSLMACBegin(sslKeys.Local.app.MACSecret, 
			sslKeys.Local.app.sequence, txProtocol, wLen.Val);
		sslKeys.Local.app.sequence++;
		
		// Get ready to send
		TCPSSLInPlaceMACEncrypt(hTCP, &sslKeys.Local.app.cryptCtx,
				sslKeys.Local.app.MACSecret, wLen.Val);
		
		// Add MAC length to the data length
		wLen.Val += 16;
	}
	
	// Prepare the header
	hdr[0] = txProtocol;
	hdr[1] = SSL_VERSION_HI;
	hdr[2] = SSL_VERSION_LO;
	hdr[3] = wLen.v[1];
	hdr[4] = wLen.v[0];
	
	// Put the record header and send the data
	TCPSSLPutRecordHeader(hTCP, hdr, TRUE);
	
}

/*****************************************************************************
  Function:
	void SSLStartPartialRecord(TCP_SOCKET hTCP, BYTE id, BYTE txProtocol,
								 WORD wLen)

  Summary:
	Begins a long SSL record.

  Description:
	This function allows messages longer than the TCP buffer to be sent,
	which is frequently the case for the Certificate handshake message.  The
	final message length is required to be known in order to transmit the
	header.  Once called, SSLFlushPartialRecord and SSLFinishPartialRecord
	must be called to write remaining data, finalize, and prepare for a new
	record.

  Precondition:
	The specified SSL stub is initialized and the TCP socket is connected.

  Parameters:
	hTCP - The TCP socket with data waiting to be transmitted
	id - The active SSL stub ID
	txPortocol - The SSL protocol number to attach to this record
	wLen - The length of all the data to be sent
	
  Returns:
  	None
  
  Remarks:
	Partial messages do not support the current cipher spec, so this can
	only be used during the handshake procedure.
  ***************************************************************************/
#if defined(STACK_USE_SSL_SERVER)
void SSLStartPartialRecord(TCP_SOCKET hTCP, BYTE id, BYTE txProtocol, WORD wLen)
{
	BYTE hdr[5];
	
	SSLStubSync(id);
	
	// Prepare the header
	hdr[0] = txProtocol;
	hdr[1] = SSL_VERSION_HI;
	hdr[2] = SSL_VERSION_LO;
	hdr[3] = wLen >> 8;
	hdr[4] = wLen;
	
	// Put the record header and send the data
	TCPSSLPutRecordHeader(hTCP, hdr, FALSE);
	
}
#endif

/*****************************************************************************
  Function:
	void SSLTxMessage(TCP_SOCKET hTCP, BYTE id, BYTE msg)

  Summary:
	Transmits an SSL message.

  Description:
	This function transmits a specific SSL message for handshakes and alert
	messages.  Supported messages are listed in SSL_MESSAGES.

  Precondition:
	The specified SSL stub is initialized and the TCP socket is connected.

  Parameters:
	hTCP - The TCP socket with data waiting to be transmitted
	id - The active SSL stub ID
	msg - One of the SSL_MESSAGES types to send
	
  Returns:
  	None
  ***************************************************************************/
void SSLTxMessage(TCP_SOCKET hTCP, BYTE id, BYTE msg)
{
	SSLStubSync(id);
	
	// Don't do anything for terminated connections
	if(sslStub.Flags.bDone)
		return;
	
	// Transmit the requested message
	switch(msg)
	{
		#if defined(STACK_USE_SSL_CLIENT)
		case SSL_CLIENT_HELLO:
			SSLTxClientHello(hTCP);
			break;
		case SSL_CLIENT_KEY_EXCHANGE:
			SSLTxClientKeyExchange(hTCP);
			break;
		#endif
		
		#if defined(STACK_USE_SSL_SERVER)
		case SSL_SERVER_HELLO:
			SSLTxServerHello(hTCP);
			break;
		case SSL_CERTIFICATE:
			SSLTxServerCertificate(hTCP);
			break;
		case SSL_SERVER_HELLO_DONE:
			SSLTxServerHelloDone(hTCP);
			break;
		#endif
		
		case SSL_CHANGE_CIPHER_SPEC:
			SSLTxCCSFin(hTCP);
			break;
			
		// Handle all alert messages
		default:
			if((msg & 0x80) != 0x80)
				break;
			
			// Make sure we can write the message
			if(TCPIsPutReady(hTCP) < 2u)
				break;
			
			// Select FATAL or WARNING
			if(msg == SSL_ALERT_CLOSE_NOTIFY)
			{
				TCPPut(hTCP, SSL_ALERT_WARNING);
				sslStub.Flags.bCloseNotify = 1;
			}
			else
				TCPPut(hTCP, SSL_ALERT_FATAL);
			
			// Put the message byte
			TCPPut(hTCP, msg - 0x80);
			
			// Flush the message
			SSLTxRecord(hTCP, sslStubID, SSL_ALERT);
			TCPRequestSSLMessage(hTCP, SSL_NO_MESSAGE);
			
			// Mark session as terminated
			sslStub.Flags.bDone = 1;
	}
	
}

/*****************************************************************************
  Function:
	void SSLRxHandshake(TCP_SOCKET hTCP)

  Summary:
	Receives a handshake message.

  Description:
	This function receives handshake messages, reads the handshake header,
	and passes the data off to the appropriate handshake parser. 

  Precondition:
	The specified SSL stub is initialized and the TCP socket is connected.
	Also requires that rxBytesRem has been populated and the current SSL stub
	has been synced into memory.

  Parameters:
	hTCP - The TCP socket to read a handshake message from
	
  Returns:
  	None
  ***************************************************************************/
void SSLRxHandshake(TCP_SOCKET hTCP)
{
	WORD wLen;
	
	// Start reading handshake data
	HSStart();
	
	// If this is a new handshake message, read the header
	// If the message has already been started, there will
	// still be bytes remaining and the switch statement will 
	// handle the rest.
	if(sslStub.wRxHsBytesRem == 0u)
	{
		// Make sure entire header is in the buffer
		if(TCPIsGetReady(hTCP) < 4u)
			return;
		
		// Read the message type (BYTE)
		HSGet(hTCP, &sslStub.rxHSType);
		
		// Read the length (3 BYTES)
		HSGet(hTCP, NULL);
		HSGetWord(hTCP, &wLen);
		sslStub.wRxHsBytesRem = wLen;
	}
	
	// Determine what to do with the rest of the data
	switch(sslStub.rxHSType)
	{
		#if defined(STACK_USE_SSL_CLIENT)
		case SSL_SERVER_HELLO:
			SSLRxServerHello(hTCP);
			break;

		case SSL_CERTIFICATE:
			SSLRxServerCertificate(hTCP);
			break;
			
		case SSL_SERVER_HELLO_DONE:
			// This message contains no data
			// Record that message was received
			sslStub.Flags.bServerHelloDone = 1;
			break;
		#endif
		
		#if defined(STACK_USE_SSL_SERVER)
		case SSL_ANTIQUE_CLIENT_HELLO:
			SSLRxAntiqueClientHello(hTCP);
			break;

		case SSL_CLIENT_HELLO:
			SSLRxClientHello(hTCP);
			break;
			
		case SSL_CLIENT_KEY_EXCHANGE:
			SSLRxClientKeyExchange(hTCP);
			break;
		#endif
		
		case SSL_FINISHED:
			SSLRxFinished(hTCP);
			break;
	}
	
	// End reading handshake data
	HSEnd();
	
}	


/****************************************************************************
  ===========================================================================
  Section:
	SSL Message Processing Functions
  ===========================================================================
  ***************************************************************************/

/*********************************************************************
 * Function:        BYTE SSLTxClientHello(TCP_SOCKET hTCP)
 *
 * PreCondition:    Enough space is available in hTCP to write the
 *					entire message. 
 *
 * Input:           hTCP - the TCP Socket to send the message to
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Transmits the ClientHello message to initiate a
 *					new SSL session with the server.
 *
 * Note:            None
 ********************************************************************/
#if defined(STACK_USE_SSL_CLIENT)
static void SSLTxClientHello(TCP_SOCKET hTCP)
{	
	// Restart the handshake hasher
	HSStart();
	
	// Indicate that we're the client
	//sslStub.Flags.bIsServer = 0;  // This is the default already
	
	// Make sure enough space is available to transmit
	if(TCPIsPutReady(hTCP) < 100u)
		return;
	
	// Look for a valid session to reuse
	sslStub.idSession = SSLSessionMatchIP(TCPGetRemoteInfo(hTCP)->remote.IPAddr);
	sslStub.Flags.bNewSession = (sslStub.idSession == SSL_INVALID_ID);
	
	// If none is found, generate a new one
	if(sslStub.Flags.bNewSession)
	{
		sslStub.idSession = SSLSessionNew();
		if(sslStub.idSession == SSL_INVALID_ID)
		{// No free sessions, so abort
			return;
		}

		// Mark session as using this IP
		memcpy((void*)&sslSessionStubs[sslStub.idSession].tag.v[0],
				(void*)&(TCPGetRemoteInfo(hTCP)->remote.IPAddr), 4);
	}

	// Send handshake message header (hashed)
	HSPut(hTCP, SSL_CLIENT_HELLO);
	HSPut(hTCP, 0x00);				
	HSPut(hTCP, 0x00);				// Message length is 40 bytes,
	if(sslStub.Flags.bNewSession)	// plus 32 more if a session
		HSPut(hTCP, 43);			// ID is being included.
	else
		HSPut(hTCP, 43+32);
	
	// Send 
	HSPut(hTCP, SSL_VERSION_HI);
	HSPut(hTCP, SSL_VERSION_LO);
	
	// Put Client.Random
	HSPutArray(hTCP, sslKeys.Local.random, 32);
	
	// Put Session ID
	if(sslStub.Flags.bNewSession)
	{// Send no session ID
		HSPut(hTCP, 0x00);
	}
	else
	{// Send the requested Session ID
		SSLSessionSync(sslStub.idSession);
		HSPut(hTCP, 0x20);
		HSPutArray(hTCP, sslSession.sessionID, 32);
	}
	
	// Put Cipher Suites List
	HSPutWord(hTCP, 0x0004);
	HSPutWord(hTCP, SSL_RSA_WITH_ARCFOUR_128_MD5);
	HSPutWord(hTCP, SSL_RSA_EXPORT_WITH_ARCFOUR_40_MD5);
	
	// Put Compression Methods List (just null)
	HSPut(hTCP, 0x01);
	HSPut(hTCP, 0x00);
	
	// End the handshake and save the hash
	HSEnd();
	
	// Send record
	SSLTxRecord(hTCP, sslStubID, SSL_HANDSHAKE);
	
	// Record that message was sent
	TCPRequestSSLMessage(hTCP, SSL_NO_MESSAGE);
	sslStub.Flags.bClientHello = 1;

}
#endif

/*********************************************************************
 * Function:        BYTE SSLRxClientHello(TCP_SOCKET hTCP)
 *
 * PreCondition:    Handshake hasher is started, and SSL has a stub
 *					assigned. 
 *
 * Input:           hTCP - the TCP Socket to send the message to
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Receives the ClientHello message, initiating a
 *					new SSL session with a client
 *
 * Note:            None
 ********************************************************************/
#if defined(STACK_USE_SSL_SERVER)
static void SSLRxClientHello(TCP_SOCKET hTCP)
{
	WORD w;
	BYTE c, *ptrID;
	
	// Make sure entire message is ready
	if(TCPIsGetReady(hTCP) < sslStub.wRxHsBytesRem)
		return;
		
	// Verify handshake message sequence
	if(sslStub.Flags.bClientHello)
		TCPRequestSSLMessage(hTCP, SSL_ALERT_HANDSHAKE_FAILURE);
	
	// Indicate that we're the server
	sslStub.Flags.bIsServer = 1;
	
	// Read the version again
	HSGetWord(hTCP, &w);
	// Ignore the version here.  It must be at least 3.0 to receive this type
	// of message, and Safari 3.1 sends 0x0301 (TLS 1.0) even when the last 
	// connection was only 0x0300 (SSL 3.0)
		
	// Make sure the session keys are synced
	SSLKeysSync(sslStubID);
	
	// Read the Client.Random array
	HSGetArray(hTCP, sslKeys.Remote.random, 32);
	
	// Read the Session ID length
	HSGet(hTCP, &c);
	
	// Read the Session ID if it exists
	sslStub.Flags.bNewSession = TRUE;
	if(c > 0u)
	{
		// Note where it will be stored in RAM
		ptrID = ptrHS;
		HSGetArray(hTCP, NULL, c);
		
		// Try to match it with a known session
		sslStub.idSession = SSLSessionMatchID(ptrID);
		if(sslStub.idSession != SSL_INVALID_ID)
			sslStub.Flags.bNewSession = FALSE;
	}
	
	// If we we're starting a new session, try to obtain a free one
	if(sslStub.Flags.bNewSession)
		sslStub.idSession = SSLSessionNew();
	
	// Read CipherSuites length
	HSGetWord(hTCP, &w);
	
	// Check for an acceptable CipherSuite
	// Right now we just ignore this and assume support for 
	// SSL_RSA_WITH_ARCFOUR_128_MD5.  If we request this suite later 
	// and it isn't supported, the client will kill the connection.
	HSGetArray(hTCP, NULL, w);
	
	// Read the Compression Methods length
	HSGet(hTCP, &c);
	
	// Check for an acceptable Compression Method
	// Right now we just ignore this and assume support for
	// NULL_COMPRESSION.  If we request this later and the client
	// doesn't really support it, they'll kill the connection.
	HSGetArray(hTCP, NULL, c);
	
	// For TLS compatibility, we must ignore further bytes in ClientHello.
	// FF2+ may send "extensions" ad other things we don't support
	HSGetArray(hTCP, NULL, sslStub.wRxBytesRem);
	
	// Mark message as received and request a ServerHello
	sslStub.Flags.bClientHello = 1;
	TCPRequestSSLMessage(hTCP, SSL_SERVER_HELLO);

}
#endif

/*********************************************************************
 * Function:        BYTE SSLRxAntiqueClientHello(TCP_SOCKET hTCP)
 *
 * PreCondition:    Handshake hasher is started, and SSL has a stub
 *					assigned.
 *
 * Input:           hTCP - the TCP Socket to send the message to
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Receives the SSLv2 ClientHello message, initiating
 *					a new SSL session with a client
 *
 * Note:            This is the only SSLv2 message we support, and
 *					is provided for browsers seeking backwards
 *					compatibility.  Connections must be upgraded to
 *					SSLv3.0 immediately following, otherwise the 
 *					connection will fail.
 ********************************************************************/
#if defined(STACK_USE_SSL_SERVER)
static void SSLRxAntiqueClientHello(TCP_SOCKET hTCP)
{
	WORD suiteLen, idLen, randLen;
	BYTE c;
	
	// Make sure entire message is ready
	if(TCPIsGetReady(hTCP) < sslStub.wRxHsBytesRem)
		return;
		
	// Verify handshake message sequence
	if(sslStub.Flags.bClientHello)
		TCPRequestSSLMessage(hTCP, SSL_ALERT_HANDSHAKE_FAILURE);
		
	// Indicate that we're the server
	sslStub.Flags.bIsServer = 1;
	
	// Make sure the session keys are synced
	SSLKeysSync(sslStubID);
	
	// Read and verify the handshake message type
	HSGet(hTCP, &c);
	if(c != 0x01u)
	{// This message is not supported, so handshake fails
		TCPRequestSSLMessage(hTCP, SSL_ALERT_HANDSHAKE_FAILURE);
		return;
	}
	
	// Read and verify the version
	HSGet(hTCP, &c);
	if(c != SSL_VERSION_HI)
	{// Version is too low, so handshake fails
		TCPRequestSSLMessage(hTCP, SSL_ALERT_HANDSHAKE_FAILURE);
		return;
	}
	HSGet(hTCP, &c);	// Ignore low byte of version number
	
	// Read the CipherSuite length
	HSGetWord(hTCP, &suiteLen);
	
	// Read Session ID Length
	HSGetWord(hTCP, &idLen);
	
	// Read Challenge (Client.Random) length
	HSGetWord(hTCP, &randLen);
		
	// Check for an acceptable CipherSuite
	// Right now we just ignore this and assume support for 
	// SSL_RSA_WITH_ARCFOUR_128_MD5.  If we request this suite later 
	// and it isn't supported, the client will kill the connection.
	HSGetArray(hTCP, NULL, suiteLen);
	
	// Read the SessionID
	// SSLv3 clients will send a v3 ClientHello when resuming, so
	// this is always a new session.  Therefore, ignore the ID
	HSGetArray(hTCP, NULL, idLen);
	
	// Obtain a new session
	sslStub.idSession = SSLSessionNew();
	sslStub.Flags.bNewSession = 1;
	
	// Read Client.Random
	// This needs to be 32 bytes, so zero-pad the left side
	for(c = 0; c < 32 - randLen; c++)
		sslKeys.Remote.random[c] = 0;
	HSGetArray(hTCP, &sslKeys.Remote.random[c], randLen);
	
	// Mark message as received and request a ServerHello
	TCPRequestSSLMessage(hTCP, SSL_SERVER_HELLO);
	sslStub.Flags.bClientHello = 1;
}
#endif

/*********************************************************************
 * Function:        void SSLRxServerHello(TCP_SOCKET hTCP)
 *
 * PreCondition:    sslStub is synchronized and HSStart() has been
 *					called.
 *
 * Input:           hTCP - the TCP Socket to read from
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Receives the ServerHello from the remote server
 *
 * Note:            None
 ********************************************************************/
#if defined(STACK_USE_SSL_CLIENT)
static void SSLRxServerHello(TCP_SOCKET hTCP)
{
	BYTE b, sessionID[32];
	WORD w;
		
	// Make sure entire message is ready
	if(TCPIsGetReady(hTCP) < sslStub.wRxHsBytesRem)
		return;
		
	// Verify handshake message sequence
	if(!sslStub.Flags.bClientHello || sslStub.Flags.bServerHello)
		TCPRequestSSLMessage(hTCP, SSL_ALERT_HANDSHAKE_FAILURE);
	
	// Make sure correct session and key set are loaded
	SSLKeysSync(sslStubID);
	
	// Read Version (2)
	HSGetWord(hTCP, NULL);
	
	// Read Server.Random (32)
	HSGetArray(hTCP, sslKeys.Remote.random, 32);
	
	// Read Session ID Length (byte)
	HSGet(hTCP, &b);
	
	// Read Session ID (if any)
	SSLSessionSync(sslStub.idSession);
	if(b != 0u)
	{
		HSGetArray(hTCP, sessionID, b);

		// If reusing a session, check if our session ID was accepted
		if(!sslStub.Flags.bNewSession &&
			memcmp((void*)sslSession.sessionID, (void*)sessionID, 32) == 0)
		{// Session restart was accepted
			// Nothing to do here...move along
		}
		else
		{// This is a new session
			memcpy((void*)sslSession.sessionID, (void*)sessionID, 32);

			// Reset the RxServerCertificate state machine
			sslStub.dwTemp.v[0] = RX_SERVER_CERT_START;
		}
	}
	else
	{
		// Session is non-resumable, so invalidate its tag
		sslSessionStubs[sslStub.idSession].tag.Val = 0;
	}
	
	// Read and verify Cipher Suite (WORD)
	HSGetWord(hTCP, &w);
	if(w != SSL_RSA_WITH_ARCFOUR_128_MD5)
		TCPRequestSSLMessage(hTCP, SSL_ALERT_HANDSHAKE_FAILURE);
	
	// Read and verify Compression Method (BYTE)
	HSGet(hTCP, &b);
	if(b != 0x00u)
		TCPRequestSSLMessage(hTCP, SSL_ALERT_HANDSHAKE_FAILURE);
	
	// Note that message was received
	sslStub.Flags.bServerHello = 1;
	
	// Note that we updated session data
	SSLSessionUpdated();
}
#endif

/*********************************************************************
 * Function:        BYTE SSLTxServerHello(TCP_SOCKET hTCP)
 *
 * PreCondition:    None
 *
 * Input:           hTCP - the TCP Socket to send the message to
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Transmits the ServerHello message.
 *
 * Note:            None
 ********************************************************************/
#if defined(STACK_USE_SSL_SERVER)
static void SSLTxServerHello(TCP_SOCKET hTCP)
{
	BYTE i;
	
	// Only continue if the session has been obtained
	if(sslStub.idSession == SSL_INVALID_ID)
		return;
	
	// Make sure enough space is available to transmit
	if(TCPIsPutReady(hTCP) < 78u)
		return;
	
	// Restart the handshake hasher
	HSStart();
	
	// Sync the session and keys
	SSLKeysSync(sslStubID);
	SSLSessionSync(sslStub.idSession);
	
	// If this session is new, generate an ID
	if(sslStub.Flags.bNewSession)
	{
		for(i = 0; i < 32u; i++)
			sslSession.sessionID[i] = RandomGet();
		SSLSessionUpdated();
		
		// Tag this session identifier
		memcpy((void*)&sslSessionStubs[sslStub.idSession].tag.v[1],
			(void*)(sslSession.sessionID), 3);
	}

	// Send handshake message header (hashed)
	HSPut(hTCP, SSL_SERVER_HELLO);
	HSPut(hTCP, 0x00);				
	HSPut(hTCP, 0x00);				
	HSPut(hTCP, 70);
	
	// Send the version number
	HSPut(hTCP, SSL_VERSION_HI);
	HSPut(hTCP, SSL_VERSION_LO);
	
	// Put Server.Random
	#if defined(STACK_USE_SNTP_CLIENT)
	{
		DWORD_VAL temp;
		
		temp.Val = SNTPGetUTCSeconds();
		sslKeys.Local.random[0] = temp.v[3];
		sslKeys.Local.random[1] = temp.v[2];
		sslKeys.Local.random[2] = temp.v[1];
		sslKeys.Local.random[3] = temp.v[0];
		i = 4;
	}
	#else
		i = 0;
	#endif
	while(i < 32u)
		sslKeys.Local.random[i++] = RandomGet();
	HSPutArray(hTCP, sslKeys.Local.random, 32);
	
	// Put Session ID
	HSPut(hTCP, 0x20);
	HSPutArray(hTCP, sslSession.sessionID, 32);
	
	// Put Cipher Suites
	HSPutWord(hTCP, SSL_RSA_WITH_ARCFOUR_128_MD5);
	
	// Put Compression Method (just null)
	HSPut(hTCP, 0x00);
	
	// End the handshake and save the hash
	HSEnd();
	
	// Send record
	SSLTxRecord(hTCP, sslStubID, SSL_HANDSHAKE);
	
	// Record that message was sent and request the next message
	sslStub.Flags.bServerHello = 1;
	TCPRequestSSLMessage(hTCP, SSL_NO_MESSAGE);
	if(sslStub.Flags.bNewSession)
		TCPRequestSSLMessage(hTCP, SSL_CERTIFICATE);
	else
		TCPRequestSSLMessage(hTCP, SSL_CHANGE_CIPHER_SPEC);
	
	// Set up to transmit certificate
	sslStub.dwTemp.Val = SSL_CERT_LEN;
}
#endif

/*********************************************************************
 * Function:        void SSLRxServerCertificate(TCP_SOCKET hTCP)
 *
 * PreCondition:    sslStub is synchronized and HSStart() has been
 *					called.
 *
 * Input:           hTCP - the TCP Socket to read from
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Receives ServerCertificate from the remote server,
 *					locates the public key information, and executes
 *					RSA operation.
 *
 * Note:            This shortcuts full parsing of the certificate by
 *					just finding the Public Key Algorithm identifier
 *					for RSA.  From there, the following ASN.1 struct
 *					is the public key.  That struct consists of the
 *					value for N, followed by the value for E.
 ********************************************************************/
#if defined(STACK_USE_SSL_CLIENT)
static void SSLRxServerCertificate(TCP_SOCKET hTCP)
{
	WORD len;
	BYTE i, e[3];
	
	// Verify handshake message sequence
	if(!sslStub.Flags.bServerHello || sslStub.Flags.bServerCertificate)
		TCPRequestSSLMessage(hTCP, SSL_ALERT_HANDSHAKE_FAILURE);
	
	// Check state machine variable
	switch(sslStub.dwTemp.v[0]) {

		case RX_SERVER_CERT_START:
			// Find RSA Public Key Algorithm identifier
			len = TCPFindROMArray(hTCP, (ROM BYTE*)"\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01", 9, 0, FALSE);
			
			if(len == 0xFFFFu)
			{// If not found, clear most of buffer and return to wait
				HSGetArray(hTCP, NULL, TCPIsGetReady(hTCP) - 10);
				return;
			}
			
			// Otherwise, read it and move on
			HSGetArray(hTCP, NULL, len + 9);
			sslStub.dwTemp.v[0]++;
					
		case RX_SERVER_CERT_FIND_KEY:
			// Search for beginning of struct
			len = TCPFind(hTCP, 0x30, 0, FALSE);
			
			if(len == 0xFFFFu)
			{// Not found, so clear and return
				HSGetArray(hTCP, NULL, TCPIsGetReady(hTCP));
			}
			
			// Clear up through the 0x30
			HSGetArray(hTCP, NULL, len + 1);
			
			// Make sure 2 more bytes remain
			if(TCPIsGetReady(hTCP) < 3u)
				return;
			
			// Read 1 or 2 length bytes (ignore)
			HSGet(hTCP, &i);
			if(i > 0x80)
				HSGet(hTCP, &i);
				
			// Increment and continue
			sslStub.dwTemp.v[0]++;
		
		case RX_SERVER_CERT_FIND_N:
			// Make sure tag and length bytes are ready, plus one more
			if(TCPIsGetReady(hTCP) < 4u)
				return;

			// Read 0x02 (integer identifier)
			HSGet(hTCP, NULL);
			
			// Read 1 or 2 length bytes to sslStub.dwTemp.v[1]
			HSGet(hTCP, &i);
			if(i > 0x80)
				HSGet(hTCP, &i);
			sslStub.dwTemp.v[1] = i;
			
			// If there's one odd byte, it's a leading zero that we don't need
			if(sslStub.dwTemp.v[1] & 0x01)
			{
				HSGet(hTCP, NULL);
				sslStub.dwTemp.v[1]--;
			}
			
			// The max modulus we support is 1024 bits
			if(sslStub.dwTemp.v[1] > 0x80)
			{
				TCPRequestSSLMessage(hTCP, SSL_ALERT_HANDSHAKE_FAILURE);
				sslStub.dwTemp.v[1] = 0x80;
			}

			// Increment and continue
			sslStub.dwTemp.v[0]++;
		
		case RX_SERVER_CERT_READ_N:
			// Make sure sslStub.dwTemp.v[1] bytes are ready
			if(TCPIsGetReady(hTCP) < sslStub.dwTemp.v[1])
				return;
			
			// N will be stored in sslBuffer, which is currently in use
			// for handshaking.  We can stop the handshake hashing, read 
			// and hash this data, then resume more handshake hashing
			HSEnd();
			
			// Claim an SSL Buffer for RSA operations
			SSLBufferAlloc(&sslStub.idRxBuffer);
			if(sslStub.idRxBuffer == SSL_INVALID_ID)
				return;
				
			// Make sure we can claim RSA Engine
			if(!RSABeginEncrypt(sslStub.dwTemp.v[1]))
				return;
			sslRSAStubID = sslStubID;
			
			// Read N to proper location
			for(i = 0; i < 128u - sslStub.dwTemp.v[1]; i++)
				sslBuffer.full[i] = 0x00;
			HSGetArray(hTCP, sslBuffer.full, sslStub.dwTemp.v[1]);
			
            if (sslStub.supplementaryDataType == SSL_SUPPLEMENTARY_DATA_CERT_PUBLIC_KEY)
            {
                SSL_PKEY_INFO * tmpPKeyPtr = ((SSL_PKEY_INFO *)sslStub.supplementaryBuffer);
                tmpPKeyPtr->pub_size_bytes = sslStub.dwTemp.v[1];
                if (tmpPKeyPtr->pub_size_bytes <= sizeof (tmpPKeyPtr->pub_key))
                    memcpy (&tmpPKeyPtr->pub_key[0], sslBuffer.full, tmpPKeyPtr->pub_size_bytes);
            }


			// Hash what we just read
			SSLHashSync(sslStub.idSHA1);
			HashAddData(&sslHash, sslBuffer.full, sslStub.dwTemp.v[1]);
			SSLHashSync(sslStub.idMD5);
			HashAddData(&sslHash, sslBuffer.full, sslStub.dwTemp.v[1]);
			
			// Generate { SSL_VERSION rand[46] } as pre-master secret & save
			SSLSessionSync(sslStub.idSession);
			sslSession.masterSecret[0] = SSL_VERSION_HI;
			sslSession.masterSecret[1] = SSL_VERSION_LO;
			for(i = 2; i < 48u; i++)
				sslSession.masterSecret[i] = RandomGet();
			SSLSessionUpdated();
			
			// Set RSA engine to use this data and key
			RSASetData(sslSession.masterSecret, 48, RSA_BIG_ENDIAN);
			RSASetN(sslBuffer.full, RSA_BIG_ENDIAN);
			RSASetResult(sslBuffer.full+128, RSA_BIG_ENDIAN);
			
			// Start a new hash
			HSStart();
			
			// Increment and continue
			sslStub.dwTemp.v[0]++;
			
		case RX_SERVER_CERT_READ_E:
			// Make sure 5 bytes are ready
			if(TCPIsGetReady(hTCP) < 5u)
				return;

			// Read 0x02
			HSGet(hTCP, NULL);
			
			// Read 1 length byte to temp
			HSGet(hTCP, &i);
			if(i > 3u)
				i = 3;
			
			// Read E to temp
			HSGetArray(hTCP, e, i);

            if (sslStub.supplementaryDataType == SSL_SUPPLEMENTARY_DATA_CERT_PUBLIC_KEY)
            {
                SSL_PKEY_INFO * tmpPKeyPtr = ((SSL_PKEY_INFO *)sslStub.supplementaryBuffer);
                if (i <= sizeof (tmpPKeyPtr->pub_e))
                    memcpy (&tmpPKeyPtr->pub_e[0], e, i);
            }

			// Set RSA engine to encrypt with E
			RSASetE(e, i, RSA_BIG_ENDIAN);
			
			// Increment and continue
			sslStub.dwTemp.v[0]++;
		
		case RX_SERVER_CERT_CLEAR:
			// Clear up to sslStub.wRxHsBytesRem from hTCP
			len = TCPIsGetReady(hTCP);
			if(len > sslStub.wRxHsBytesRem)
				len = sslStub.wRxHsBytesRem;
			HSGetArray(hTCP, NULL, len);
			
			// If we're done, kick off the RSA encryption next
			if(sslStub.wRxHsBytesRem == 0u)
			{
				// Set periodic function to do RSA operation
				sslStub.Flags.bRSAInProgress = 1;
				sslSessionStubs[sslStub.idSession].lastUsed += SSL_RSA_LIFETIME_EXTENSION;
				
				// Note that we've received this message
				sslStub.Flags.bServerCertificate = 1;	
			}
			
			break;
	}
}
#endif

/*********************************************************************
 * Function:        BYTE SSLTxServerCertificate(TCP_SOCKET hTCP)
 *
 * PreCondition:    None
 *
 * Input:           hTCP - the TCP Socket to send the message to
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Transmits the Certificate message with the 
 *					server's specified public key certificate.
 *
 * Note:            Certificate is defined in CustomSSLCert.c.
 *					This function requires special handling for
 *					partial records because the certificate will 
 *					likely be larger than the TCP buffer, and SSL
 *					handshake messages are constrained to fit in a
 *					single SSL handshake record
 ********************************************************************/
#if defined(STACK_USE_SSL_SERVER)
static void SSLTxServerCertificate(TCP_SOCKET hTCP)
{
	WORD len;
	ROM BYTE* loc;
	
	// Restart the handshake hasher
	HSStart();
	
	// See how much we can write
	len = TCPIsPutReady(hTCP);
	
	// If full certificate remains, write the headers
	if(sslStub.dwTemp.Val == SSL_CERT_LEN)
	{
		// Make sure we can send all headers plus one byte
		if(len < 11u)
			return;
		
		// Transmit the handshake headers
		HSPut(hTCP, SSL_CERTIFICATE);
		HSPut(hTCP, 0x00);
		HSPutWord(hTCP, SSL_CERT_LEN + 3 + 3);
		
		// Send length of all certificates
		HSPut(hTCP, 0x00);
		HSPutWord(hTCP, SSL_CERT_LEN + 3);
		
		// Send length of this (only) certificate
		HSPut(hTCP, 0x00);
		HSPutWord(hTCP, SSL_CERT_LEN);
		
		// Put in the record header and begin the partial record sending
		SSLStartPartialRecord(hTCP, sslStubID, SSL_HANDSHAKE, SSL_CERT_LEN + 3 + 3 + 4);
		
		// Update free space
		len -= 10;
	}
	
	// Figure out where to start, and how much to send
	loc = SSL_CERT + (SSL_CERT_LEN - sslStub.dwTemp.Val);
	if(sslStub.dwTemp.Val < len)
		len = sslStub.dwTemp.Val;
		
	// Write the bytes
	HSPutROMArray(hTCP, loc, len);
	sslStub.dwTemp.Val -= len;
	
	// End the handshake and save the hash
	HSEnd();
	
	// Send record
	SSLFlushPartialRecord(hTCP);
		
	// Check if entire certificate was sent
	if(sslStub.dwTemp.Val == 0u)
	{
		// Finish the partial record
		SSLFinishPartialRecord(hTCP);
		
		// Record that message was sent and request a Certificate
		TCPRequestSSLMessage(hTCP, SSL_NO_MESSAGE);
		TCPRequestSSLMessage(hTCP, SSL_SERVER_HELLO_DONE);
		sslStub.Flags.bServerCertificate = 1;
	}
}
#endif

/*********************************************************************
 * Function:        BYTE SSLTxServerHelloDone(TCP_SOCKET hTCP)
 *
 * PreCondition:    None
 *
 * Input:           hTCP - the TCP Socket to send the message to
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Transmits the ServerHelloDone message.
 *
 * Note:            None
 ********************************************************************/
#if defined(STACK_USE_SSL_SERVER)
static void SSLTxServerHelloDone(TCP_SOCKET hTCP)
{
	// Make sure enough space is available to transmit
	if(TCPIsPutReady(hTCP) < 4u)
		return;
	
	// Restart the handshake hasher
	HSStart();
	
	// Send handshake message header (hashed)
	HSPut(hTCP, SSL_SERVER_HELLO_DONE);
	HSPut(hTCP, 0x00);				
	HSPut(hTCP, 0x00);				
	HSPut(hTCP, 0x00);

	// Message has no content, so we're done
	
	// End the handshake and save the hash
	HSEnd();
	
	// Send record
	SSLTxRecord(hTCP, sslStubID, SSL_HANDSHAKE);
	
	// Record that message was sent
	TCPRequestSSLMessage(hTCP, SSL_NO_MESSAGE);
	sslStub.Flags.bServerHelloDone = 1;
}
#endif

/*********************************************************************
 * Function:        void SSLTxClientKeyExchange(TCP_SOCKET hTCP)
 *
 * PreCondition:    sslStub is synchronized, sslStub.dwTemp.v[1]
 *					contains the length of the public key, and 
 *					the RxBuffer contains the encrypted pre-master
 *					secret at address 0x80.
 *
 * Input:           hTCP - the TCP Socket to write the message to
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Transmits the encrypted pre-master secret to the
 *					server and requests the Change Cipher Spec.  Also
 *					generates the Master Secret from the pre-master
 *					secret that was used.
 *
 * Note:            None
 ********************************************************************/
#if defined(STACK_USE_SSL_CLIENT)
static void SSLTxClientKeyExchange(TCP_SOCKET hTCP)
{
	BYTE len;
	
	// Load length of modulus from RxServerCertificate
	len = sslStub.dwTemp.v[1];
	
	// Make sure there's len+9 bytes free
	if(TCPIsPutReady(hTCP) < len + 9)
		return;
	
	// Start the handshake processor
	HSStart();

	// Send handshake message header (hashed)
	HSPut(hTCP, SSL_CLIENT_KEY_EXCHANGE);
	HSPut(hTCP, 0x00);				
	HSPut(hTCP, 0x00);				// Message length is (length of key) bytes
	HSPut(hTCP, len);
	
	// Suspend the handshake hasher and load the buffer
	HSEnd();
	SSLBufferSync(sslStub.idRxBuffer);	

	// Send encrypted pre-master secret
	TCPPutArray(hTCP, (BYTE*) sslBuffer.full + 0x80, len);
	
	// Free the RSA Engine
	RSAEndUsage();
	sslRSAStubID = SSL_INVALID_ID;

	// Hash what we just sent
	SSLHashSync(sslStub.idSHA1);
	HashAddData(&sslHash, sslBuffer.full + 0x80, len);
	SSLHashSync(sslStub.idMD5);
	HashAddData(&sslHash, sslBuffer.full + 0x80, len);
	
	// Generate the Master Secret
	SSLKeysSync(sslStubID);
	SSLSessionSync(sslStub.idSession);
	GenerateHashRounds(3, sslKeys.Local.random, sslKeys.Remote.random);
	memcpy(sslSession.masterSecret, (void*)sslBuffer.hashRounds.temp, 48);
	SSLSessionUpdated();
	
	// Free the buffer with the encrypted pre-master secret
	SSLBufferFree(&sslStub.idRxBuffer);
	
	// Restart the handshaker
	HSStart();
	
	// Send the record
	SSLTxRecord(hTCP, sslStubID, SSL_HANDSHAKE);
	
	// Request a Change Cipher Spec and Finished message
	TCPRequestSSLMessage(hTCP, SSL_NO_MESSAGE);
	TCPRequestSSLMessage(hTCP, SSL_CHANGE_CIPHER_SPEC);
	
	// Note that this message was sent
	sslStub.Flags.bClientKeyExchange = 1;
}
#endif

/*********************************************************************
 * Function:        void SSLRxClientKeyExchange(TCP_SOCKET hTCP)
 *
 * PreCondition:    sslStub is synchronized and HSStart() has been
 *					called.
 *
 * Input:           hTCP - the TCP Socket to read from
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Receives the ClientKeyExchange message and begins
 *					the decryption process.
 *
 * Note:            None
 ********************************************************************/
#if defined(STACK_USE_SSL_SERVER)
static void SSLRxClientKeyExchange(TCP_SOCKET hTCP)
{
	WORD wKeyLength;
	
	// TODO: Add a sanity check here to see if sslStub.wRxHsBytesRem is equal 
	// to the chosen RSA modulus length (ex: 128 for 1024 bit).  If it is 
	// wrong, we need to generate a failure alert.
	
	// Make sure entire message is ready
	if(TCPIsGetReady(hTCP) < sslStub.wRxHsBytesRem)
		return;
	
	// Verify handshake message sequence
	if(!sslStub.Flags.bServerHello || sslStub.Flags.bClientKeyExchange)
		TCPRequestSSLMessage(hTCP, SSL_ALERT_HANDSHAKE_FAILURE);
	
	// Obtain a buffer to use
	SSLBufferAlloc(&sslStub.idRxBuffer);
	if(sslStub.idRxBuffer == SSL_INVALID_ID)
		return;
	
	// Claim the RSA engine
	if(!RSABeginDecrypt())
		return;
	sslRSAStubID = sslStubID;
	
	// Read the data
	wKeyLength = sslStub.wRxHsBytesRem;
	HSEnd();
	HSStart();
	HSGetArray(hTCP, NULL, wKeyLength);
	HSEnd();
	RSASetData(sslBuffer.full, wKeyLength, RSA_BIG_ENDIAN);
	sslBufferID = sslStub.idRxBuffer;
	
	// Note that message was received
	sslStub.Flags.bClientKeyExchange = 1;
	
	// Kick off the RSA decryptor
	sslStub.Flags.bRSAInProgress = 1;
	sslSessionStubs[sslStub.idSession].lastUsed += SSL_RSA_LIFETIME_EXTENSION;
	
}
#endif

/*********************************************************************
 * Function:        void SSLTxCCSFin(TCP_SOCKET hTCP)
 *
 * PreCondition:    sslStub is synchronized, and the current session
 *					has a valid pre-master secret to use.
 *
 * Input:           hTCP - the TCP Socket to write the message to
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Generates the session keys from the master secret,
 *					then allocates and generates the encryption 
 *					context.  Once processing is complete, transmits
 *					the Change Cipher Spec message and the Finished
 *					handshake message to the server.
 *
 * Note:            None
 ********************************************************************/
static void SSLTxCCSFin(TCP_SOCKET hTCP)
{
	BYTE data[20];
	
	// Make sure enough space is available for both
	if(TCPIsPutReady(hTCP) < 68u)
		return;

	// Sync up the session
	SSLSessionSync(sslStub.idSession);
	SSLKeysSync(sslStubID);
	SSLBufferSync(SSL_INVALID_ID);
			
	// Send the CCS (not a handshake message)
	TCPPut(hTCP, 1);
	SSLTxRecord(hTCP, sslStubID, SSL_CHANGE_CIPHER_SPEC);
	sslStub.Flags.bLocalChangeCipherSpec = 1;
	
	// If keys are not ready, generate them
	if(!sslStub.Flags.bKeysValid)
	{
		// Obtain two full buffers for the Sboxes
		SSLBufferAlloc(&sslStub.idTxBuffer);
		SSLBufferAlloc(&sslStub.idRxBuffer);
		if(sslStub.idTxBuffer == SSL_INVALID_ID || sslStub.idRxBuffer == SSL_INVALID_ID)
			return;

		// Generate the keys
		SSLHashSync(SSL_INVALID_ID);
		GenerateSessionKeys();
		sslStub.Flags.bKeysValid = 1;
	}
	
	// Reset the sequence counters
	sslKeys.Local.app.sequence = 0;	
	
	// Start the handshake data processor
	HSStart();

	// First, write the handshake header
	HSPut(hTCP, SSL_FINISHED);
	HSPut(hTCP, 0x00);
	HSPut(hTCP, 0x00);
	HSPut(hTCP, 0x24);

	// Calculate the Finished hashes
	CalculateFinishedHash(sslStub.idMD5, !sslStub.Flags.bIsServer, data);
	HSPutArray(hTCP, data, 16);
	CalculateFinishedHash(sslStub.idSHA1, !sslStub.Flags.bIsServer, data);
	HSPutArray(hTCP, data, 20);	

	// Hash this message to the handshake hash
	HSEnd();

	// Send the record
	SSLTxRecord(hTCP, sslStubID, SSL_HANDSHAKE);
	
	// Update the connection state
	TCPRequestSSLMessage(hTCP, SSL_NO_MESSAGE);
	sslStub.Flags.bLocalFinished = 1;
	
	// If complete, note that
	if(sslStub.Flags.bRemoteFinished)
	{
		TCPSSLHandshakeComplete(hTCP);
		SSLHashFree(&sslStub.idMD5);
		SSLHashFree(&sslStub.idSHA1);
	}

}

/*********************************************************************
 * Function:        void SSLRxCCS(TCP_SOCKET hTCP)
 *
 * PreCondition:    sslStub is synchronized.
 *
 * Input:           hTCP - the TCP Socket to read from
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Receives a ChangeCipherSpec from the remote server
 *
 * Note:            None
 ********************************************************************/
static void SSLRxCCS(TCP_SOCKET hTCP)
{
	// Only proceed if RSA is done
	if(sslStub.Flags.bRSAInProgress)
		return;
	
	// Verify handshake message sequence
	if(!sslStub.Flags.bClientHello || !sslStub.Flags.bServerHello)
		TCPRequestSSLMessage(hTCP, SSL_ALERT_HANDSHAKE_FAILURE);
	
	// Allocate a hash for MACing data
	SSLHashAlloc(&sslStub.idRxHash);

	// Make sure entire message is ready and an RX hash is allocated
	if(TCPIsGetReady(hTCP) < sslStub.wRxBytesRem 
		|| sslStub.idRxHash == SSL_INVALID_ID)
		return;
	
	// If keys are not ready, generate them
	if(!sslStub.Flags.bKeysValid)
	{
		// Sync up the session
		SSLSessionSync(sslStub.idSession);
		SSLKeysSync(sslStubID);
		SSLBufferSync(SSL_INVALID_ID);

		// Obtain two full buffers for the Sboxes
		SSLBufferAlloc(&sslStub.idTxBuffer);
		SSLBufferAlloc(&sslStub.idRxBuffer);
		if(sslStub.idTxBuffer == SSL_INVALID_ID || sslStub.idRxBuffer == SSL_INVALID_ID)
			return;

		// Generate the keys
		SSLHashSync(SSL_INVALID_ID);
		GenerateSessionKeys();
		sslStub.Flags.bKeysValid = 1;
	}
	
	// Read the CCS message (ignoring its contents)
	sslStub.wRxBytesRem -= TCPGetArray(hTCP, NULL, sslStub.wRxBytesRem);
	
	// Note that message was received
	SSLKeysSync(sslStubID);
	sslKeys.Remote.app.sequence = 0;
	sslStub.Flags.bRemoteChangeCipherSpec = 1;
}

/*********************************************************************
 * Function:        void SSLRxFinished(TCP_SOCKET hTCP)
 *
 * PreCondition:    sslStub is synchronized and HSStart() has been
 *					called.
 *
 * Input:           hTCP - the TCP Socket to read from
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Receives the Finished message from remote node
 *
 * Note:            None
 ********************************************************************/
static void SSLRxFinished(TCP_SOCKET hTCP)
{
	BYTE rxHash[20], expectedHash[20];
	
	// Make sure entire message is ready
	if(TCPIsGetReady(hTCP) < sslStub.wRxHsBytesRem)
		return;
	
	// Verify handshake message sequence
	if(!sslStub.Flags.bRemoteChangeCipherSpec)
		TCPRequestSSLMessage(hTCP, SSL_ALERT_HANDSHAKE_FAILURE);
	
	// Make sure correct session and key set are loaded
	SSLSessionSync(sslStub.idSession);
	SSLKeysSync(sslStubID);
	
	// Read md5_sum to temporary location
	HSGetArray(hTCP, rxHash, 16);
	
	// Calculate expected MD5 hash
	CalculateFinishedHash(sslStub.idMD5, sslStub.Flags.bIsServer, expectedHash);	
	if(memcmp((void*)rxHash, (void*)expectedHash, 16) != 0)
	{// Handshake hash fails
		TCPRequestSSLMessage(hTCP, SSL_ALERT_HANDSHAKE_FAILURE);
	}
	
	// Read sha_sum to temporary location
	HSGetArray(hTCP, rxHash, 20);
	
	// Calculate expected SHA-1 hash	
	CalculateFinishedHash(sslStub.idSHA1, sslStub.Flags.bIsServer, expectedHash);
	if(memcmp((void*)rxHash, (void*)expectedHash, 20) != 0)
	{// Handshake hash fails
		TCPRequestSSLMessage(hTCP, SSL_ALERT_HANDSHAKE_FAILURE);
	}
	
	// Note that message was received
	sslStub.Flags.bRemoteFinished = 1;
	
	// If complete, note that, otherwise, request our own CCS message
	if(sslStub.Flags.bLocalFinished)
	{
		TCPSSLHandshakeComplete(hTCP);
		SSLHashFree(&sslStub.idMD5);
		SSLHashFree(&sslStub.idSHA1);
	}
	else
		TCPRequestSSLMessage(hTCP, SSL_CHANGE_CIPHER_SPEC);

}

/*********************************************************************
 * Function:        void SSLRxAlert(TCP_SOCKET hTCP)
 *
 * PreCondition:    sslStub is synchronized
 *
 * Input:           hTCP - the TCP Socket to read from
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Receives an alert message and decides what to do
 *
 * Note:            None
 ********************************************************************/
static void SSLRxAlert(TCP_SOCKET hTCP)
{
	BYTE bLevel, bDesc;
	
	// Make sure entire message is ready
	if(TCPIsGetReady(hTCP) < sslStub.wRxBytesRem)
		return;
	
	// Read the alert message
	TCPGet(hTCP, &bLevel);
	TCPGet(hTCP, &bDesc);
	sslStub.wRxBytesRem -= 2;
	
	// Determine what to do
	switch(bLevel)
	{
		case SSL_ALERT_WARNING:
			// Check if a close notify was received
			if(bDesc + 0x80 == SSL_ALERT_CLOSE_NOTIFY)
				sslStub.Flags.bCloseNotify = 1;
			
			// We don't support non-fatal warnings besides CloseNotify,
			// so the connection is always done now.  When the TCP 
			// session closes, the resources will be cleaned up.
			
			// No break here:
			// Session is terminated, so still mark Done below
			
		case SSL_ALERT_FATAL:
			// Mark session as terminated
			sslStub.Flags.bDone = 1;			
	}
	
}

/****************************************************************************
  ===========================================================================
  Section:
	SSL Key Processing Functions
  ===========================================================================
  ***************************************************************************/

/*********************************************************************
 * Function:        RSA_STATUS SSLRSAOperation(void)
 *
 * PreCondition:    The RSA Module has been secured, an RSA operation
 *					is pending, sslStub.wRxHsBytesRem is the value of
 *					sslStub.wRxBytesRem after completion, and 
 *					sslStub.wRxBytesRem is the value of 
 *					sslStub.rxProtocol after completion.  Also requires
 *					sslStub to be synchronized.
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Pauses connection processing until RSA calculation
 *					is complete.
 *
 * Note:            This function exists outside of the handshaking 
 *					functions so that the system does not incur the
 *					expense of resuming and suspending handshake 
 *					hashes.
 ********************************************************************/
RSA_STATUS SSLRSAOperation(void)
{
	SSLBufferSync(sslStub.idRxBuffer);
		
	// Call RSAStep to perform some RSA processing
	return RSAStep();
}

/*********************************************************************
 * Function:        void GenerateHashRounds(BYTE num, BYTE* rand1,
 *												BYTE* rand2)
 *
 * PreCondition:    The SSL buffer is allocated for temporary usage
 *					and the data to run rounds on is in
 *					sslSession.masterSecret
 *
 * Input:           num   - how many rounds to compute
 *					rand1 - the first random data block to use
 *					rand2 - the second random data block to use
 *
 * Output:          None
 *
 * Side Effects:    Destroys the SSL Buffer space
 *
 * Overview:        Generates hash rounds to find either the
 *					Master Secret or the Key Block.
 *
 * Note:            This function will overflow the buffer after 7
 *					rounds, but in practice num = 3 or num = 4.
 ********************************************************************/
void GenerateHashRounds(BYTE num, BYTE* rand1, BYTE* rand2)
{
	BYTE i, j, c, *res;
	
	c = 'A';
	res = sslBuffer.hashRounds.temp;
	
	for(i = 1; i <= num; i++, c++, res += 16)
	{
		SHA1Initialize(&sslBuffer.hashRounds.hash);
		for(j = 0; j < i; j++)
			HashAddData(&sslBuffer.hashRounds.hash, &c, 1);
		HashAddData(&sslBuffer.hashRounds.hash, sslSession.masterSecret, 48);
		HashAddData(&sslBuffer.hashRounds.hash, rand1, 32);
		HashAddData(&sslBuffer.hashRounds.hash, rand2, 32);
		SHA1Calculate(&sslBuffer.hashRounds.hash, sslBuffer.hashRounds.sha_hash);
		MD5Initialize(&sslBuffer.hashRounds.hash);
		HashAddData(&sslBuffer.hashRounds.hash, sslSession.masterSecret, 48);
		HashAddData(&sslBuffer.hashRounds.hash, sslBuffer.hashRounds.sha_hash, 20);
		MD5Calculate(&sslBuffer.hashRounds.hash, res);
	}	
}

/*********************************************************************
 * Function:        void GenerateSessionKeys(void)
 *
 * PreCondition:    The SSL buffer is allocated for temporary usage,
 *					session keys are synced, and the TX and RX buffers
 *					are allocated for S-boxes.
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    Destroys the SSL Buffer Space
 *
 * Overview:        Generates the session write keys and MAC secrets
 *
 * Note:            None
 ********************************************************************/
void GenerateSessionKeys(void)
{
	// This functionality differs slightly for client and server operations

	#if defined(STACK_USE_SSL_SERVER)
	if(sslStub.Flags.bIsServer)
	{
		// Generate the key expansion block
		GenerateHashRounds(4, sslKeys.Local.random, sslKeys.Remote.random);
		memcpy(sslKeys.Remote.app.MACSecret, (void*)sslBuffer.hashRounds.temp, 16);
		memcpy(sslKeys.Local.app.MACSecret, (void*)sslBuffer.hashRounds.temp+16, 16);
	
		// Save write keys elsewhere temporarily
		SSLHashSync(SSL_INVALID_ID);
		memcpy(&sslHash, (void*)sslBuffer.hashRounds.temp+32, 32);
	
		// Generate ARCFOUR Sboxes
		SSLBufferSync(sslStub.idRxBuffer);
		sslKeys.Remote.app.cryptCtx.Sbox = sslBuffer.full;
		ARCFOURInitialize(&(sslKeys.Remote.app.cryptCtx), (BYTE*)(&sslHash), 16);
		SSLBufferSync(sslStub.idTxBuffer);
		sslKeys.Local.app.cryptCtx.Sbox = sslBuffer.full;
		ARCFOURInitialize(&(sslKeys.Local.app.cryptCtx), (BYTE*)(&sslHash)+16, 16);
		
		return;
	}
	#endif
	
	#if defined(STACK_USE_SSL_CLIENT)
	// Generate the key expansion block
	GenerateHashRounds(4, sslKeys.Remote.random, sslKeys.Local.random);
	memcpy(sslKeys.Local.app.MACSecret, (void*)sslBuffer.hashRounds.temp, 16);
	memcpy(sslKeys.Remote.app.MACSecret, (void*)sslBuffer.hashRounds.temp+16, 16);

	// Save write keys elsewhere temporarily
	SSLHashSync(SSL_INVALID_ID);
	memcpy(&sslHash, (void*)sslBuffer.hashRounds.temp+32, 32);

	// Generate ARCFOUR Sboxes
	SSLBufferSync(sslStub.idTxBuffer);
	sslKeys.Local.app.cryptCtx.Sbox = sslBuffer.full;
	ARCFOURInitialize(&(sslKeys.Local.app.cryptCtx), (BYTE*)(&sslHash), 16);
	SSLBufferSync(sslStub.idRxBuffer);
	sslKeys.Remote.app.cryptCtx.Sbox = sslBuffer.full;
	ARCFOURInitialize(&(sslKeys.Remote.app.cryptCtx), (BYTE*)(&sslHash)+16, 16);
	#endif
	
}

/*********************************************************************
 * Function:        static void CalculateFinishedHash(BYTE hashID,
 *									BOOL fromClient, BYTE *result)
 *
 * PreCondition:    hashID has all handshake data hashed so far and
 *					the current session is synced in.
 *
 * Input:           hashID     - the hash sum to use
 *					fromClient - TRUE if client is sender
 *					result     - where to store results
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:		Calculates the handshake hash over the data.
 *					hashID can be either MD5 or SHA-1, and this
 *					function will calculate accordingly.
 *
 * Note:            None
 ********************************************************************/
static void CalculateFinishedHash(BYTE hashID, BOOL fromClient, BYTE *result)
{
	BYTE i;

	// Load the hash, but make sure updates aren't saved
	SSLHashSync(hashID);
	sslHashID = SSL_INVALID_ID;
	
	// Sync the session data so masterSecret is available
	SSLSessionSync(sslStub.idSession);
	
	// Hash in the sender phrase & master secret
	if(fromClient)
		HashAddROMData(&sslHash, (ROM BYTE*)"CLNT", 4);
	else
		HashAddROMData(&sslHash, (ROM BYTE*)"SRVR", 4);
	HashAddData(&sslHash, sslSession.masterSecret, 48);
	
	// Hash in the pad1
	i = 6;
	if(sslHash.hashType == HASH_SHA1)
		i--;
	for(; i > 0u; i--)
		HashAddROMData(&sslHash, (ROM BYTE*)"\x36\x36\x36\x36\x36\x36\x36\x36", 8);
	
	// Calculate the inner hash result and store, start new hash
	if(sslHash.hashType == HASH_MD5)
	{
		MD5Calculate(&sslHash, result);
		MD5Initialize(&sslHash);
	}
	else
	{
		SHA1Calculate(&sslHash, result);
		SHA1Initialize(&sslHash);
	}
	
	// Hash in master secret
	HashAddData(&sslHash, sslSession.masterSecret, 48);
	
	// Hash in pad2
	i = 6;
	if(sslHash.hashType == HASH_SHA1)
		i--;
	for(; i > 0u; i--)
		HashAddROMData(&sslHash, (ROM BYTE*)"\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c", 8);
	
	// Hash in the inner hash result and calculate
	if(sslHash.hashType == HASH_MD5)
	{
		HashAddData(&sslHash, result, 16);
		MD5Calculate(&sslHash, result);
	}
	else
	{
		HashAddData(&sslHash, result, 20);
		SHA1Calculate(&sslHash, result);
	}
	
}


/****************************************************************************
  ===========================================================================
  Section:
	SSL Memory Management Functions
  ===========================================================================
  ***************************************************************************/

#define Debug(a,b)	//do{putrsUART("\r\n" a); while(BusyUART()); WriteUART(b+'0');} while(0)


/*********************************************************************
 * Function:        static void SSLStubAlloc(void)
 *
 * PreCondition:    None
 *
 * Inputs:          None
 *
 * Outputs:         None
 *
 * Returns:			TRUE if stub was allocated, FALSE otherwise
 *
 * Side Effects:    None
 *
 * Overview:        Allocates a stub for use.
 *
 * Note:            None
 ********************************************************************/
static BOOL SSLStubAlloc(void)
{
	BYTE i;
	
	// Search for a free stub
	for(i = 0; i != MAX_SSL_CONNECTIONS; i++)
	{
		if(!(isStubUsed & masks[i]))
		{// Stub is free, so claim it
			isStubUsed |= masks[i];
			
			// Save stub currently in RAM
			if(sslStubID != SSL_INVALID_ID)
				SaveOffChip((BYTE*)&sslStub,
					SSL_BASE_STUB_ADDR+SSL_STUB_SIZE*sslStubID,
					SSL_STUB_SIZE);

			// Switch to new stub and return
			Debug("S",i);
			sslStubID = i;
			return TRUE;
		}
	}
	
	// No stub was found to be free
	return FALSE;
	
}

/*********************************************************************
 * Function:        static void SSLStubFree(BYTE id)
 *
 * PreCondition:    None
 *
 * Inputs:          id - the stub ID to free
 *
 * Outputs:         None
 *
 * Returns:			None
 *
 * Side Effects:    None
 *
 * Overview:        Specified stub is released
 *
 * Note:            None
 ********************************************************************/
static void SSLStubFree(BYTE id)
{
	// If ID is not valid
	if(id >= MAX_SSL_CONNECTIONS)
		return;
	
	// If currently in RAM, mark as unused
	if(sslStubID == id)
		sslStubID = SSL_INVALID_ID;

	// Release the stub
	Debug("s",id);
	isStubUsed &= ~masks[id];
}

/*********************************************************************
 * Function:        static void SSLStubSync(BYTE id)
 *
 * PreCondition:    None
 *
 * Input:           id - the stub ID to sync to RAM
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Specified stub is loaded to RAM.  Only loads if
 *					necessary, and saves any current stub before
 *					switching.
 *
 * Note:            None
 ********************************************************************/
static void SSLStubSync(BYTE id)
{
	// Check if already loaded
	if(sslStubID == id)
		return;
	
	// Save old stub
	if(sslStubID != SSL_INVALID_ID)
		SaveOffChip((BYTE*)&sslStub,
			SSL_BASE_STUB_ADDR+SSL_STUB_SIZE*sslStubID,
			SSL_STUB_SIZE);
	
	// Load new stub
	LoadOffChip((BYTE*)&sslStub,
		SSL_BASE_STUB_ADDR+SSL_STUB_SIZE*id,
		SSL_STUB_SIZE);
	sslStubID = id;
}

/*********************************************************************
 * Function:        static void SSLKeysSync(BYTE id)
 *
 * PreCondition:    None
 *
 * Input:           id - the key set ID to sync to RAM
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Specified key set is loaded to RAM.  Only loads if
 *					necessary, and saves any current key set before
 *					switching.
 *
 * Note:            None
 ********************************************************************/
static void SSLKeysSync(BYTE id)
{
	// Check if already loaded
	if(sslKeysID == id)
		return;
	
	// Save old stub
	if(sslKeysID != SSL_INVALID_ID)
		SaveOffChip((BYTE*)&sslKeys,
			SSL_BASE_KEYS_ADDR+SSL_KEYS_SIZE*sslKeysID,
			SSL_KEYS_SIZE);
	
	// Load new stub
	LoadOffChip((BYTE*)&sslKeys,
		SSL_BASE_KEYS_ADDR+SSL_KEYS_SIZE*id,
		SSL_KEYS_SIZE);
	sslKeysID = id;
}

/*********************************************************************
 * Function:        static void SSLHashAlloc(BYTE *id)
 *
 * PreCondition:    None
 *
 * Inputs:          id - Where to store the allocated ID
 *
 * Outputs:         id - Allocated hash ID, or SSL_INVALID_ID if 
 *							none available
 *
 * Side Effects:    None
 *
 * Overview:        Allocates a hash for use.
 *
 * Note:            None
 ********************************************************************/
static void SSLHashAlloc(BYTE *id)
{
	BYTE i;
	
	// If already allocated, just load it up
	if(*id != SSL_INVALID_ID)
	{
		SSLHashSync(*id);
		return;
	}
		
	// Search for a free hash
	for(i = 0; i != MAX_SSL_HASHES; i++)
	{
		if(!(isHashUsed & masks[i]))
		{// Hash is free, so claim it
			isHashUsed |= masks[i];
			
			// Save hash currently in RAM
			if(sslHashID != SSL_INVALID_ID)
				SaveOffChip((BYTE*)&sslHash,
					SSL_BASE_HASH_ADDR+SSL_HASH_SIZE*sslHashID,
					SSL_HASH_SIZE);

			// Switch to new hash and return
			Debug("H",i);
			sslHashID = i;
			*id = i;
			return;
		}
	}
}

/*********************************************************************
 * Function:        static void SSLHashFree(BYTE *id)
 *
 * PreCondition:    None
 *
 * Inputs:           id - the hash ID to free
 *
 * Outputs:          id - SSL_INVALID_ID
 *
 * Side Effects:    None
 *
 * Overview:        Specified hash is released
 *
 * Note:            None
 ********************************************************************/
static void SSLHashFree(BYTE *id)
{
	// Nothing to do for invalid hashes
	if(*id > MAX_SSL_HASHES)
		return;
	
	// Purge from RAM if not used
	if(sslHashID == *id)
		sslHashID = SSL_INVALID_ID;

	// Release the hash
	Debug("h",*id);
	isHashUsed &= ~masks[*id];
	*id = SSL_INVALID_ID;
}

/*********************************************************************
 * Function:        static void SSLHashSync(BYTE id)
 *
 * PreCondition:    None
 *
 * Input:           id - the hash ID to sync to RAM
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Specified hash is loaded to RAM.  Only loads if
 *					necessary, and saves any current hash before
 *					switching.
 *
 * Note:            None
 ********************************************************************/
static void SSLHashSync(BYTE id)
{
	// Check if already loaded
	if(sslHashID == id)
		return;
	
	// Save old hash
	if(sslHashID != SSL_INVALID_ID)
		SaveOffChip((BYTE*)&sslHash,
			SSL_BASE_HASH_ADDR+SSL_HASH_SIZE*sslHashID,
			SSL_HASH_SIZE);
	
	// Load new hash if not requesting a temporary hash
	if(id != SSL_INVALID_ID)
		LoadOffChip((BYTE*)&sslHash,
			SSL_BASE_HASH_ADDR+SSL_HASH_SIZE*id,
			SSL_HASH_SIZE);
	sslHashID = id;
}

/*********************************************************************
 * Function:        static void SSLBufferAlloc(BYTE *id)
 *
 * PreCondition:    None
 *
 * Input:           id - Where to store the allocated ID
 *
 * Output:          id - Allocated buffer ID, or SSL_INVALID_ID if 
 *							none available
 *
 * Side Effects:    None
 *
 * Overview:        Allocates a buffer for use.
 *
 * Note:            None
 ********************************************************************/
static void SSLBufferAlloc(BYTE *id)
{
	BYTE i;

	// If already allocated, just load it up
	if(*id != SSL_INVALID_ID)
	{
		SSLBufferSync(*id);
		return;
	}
	
	// Search for a free buffer
	for(i = 0; i != MAX_SSL_BUFFERS; i++)
	{
		if(!(isBufferUsed & masks[i]))
		{// Buffer is free, so claim it
			isBufferUsed |= masks[i];
			
			// Save buffer currently in RAM
			if(sslBufferID != SSL_INVALID_ID)
				SaveOffChip((BYTE*)&sslBuffer, 
					SSL_BASE_BUFFER_ADDR+SSL_BUFFER_SIZE*sslBufferID,
					SSL_BUFFER_SIZE);

			// Switch to new buffer and return
			Debug("B",i);
			sslBufferID = i;
			*id = i;
			return;
		}
	}
}

/*********************************************************************
 * Function:        static void SSLBufferFree(BYTE *id)
 *
 * PreCondition:    None
 *
 * Inputs:           id - the buffer ID to free
 *
 * Outputs:          id - SSL_INVALID_ID
 *
 * Side Effects:    None
 *
 * Overview:        Specified buffer is released
 *
 * Note:            None
 ********************************************************************/
static void SSLBufferFree(BYTE *id)
{
	// Nothing to do for invalid hashes
	if(*id > MAX_SSL_BUFFERS)
		return;

	// Purge from RAM if not used
	if(sslBufferID == *id)
		sslBufferID = SSL_INVALID_ID;\
		
	// Release the buffer
	Debug("b",*id);
	isBufferUsed &= ~masks[*id];
	*id = SSL_INVALID_ID;
}

/*********************************************************************
 * Function:        static void SSLBufferSync(BYTE id)
 *
 * PreCondition:    None
 *
 * Input:           id - the buffer ID to sync to RAM
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Specified buffer is loaded to RAM.  Only loads if
 *					necessary, and saves any current buffer before
 *					switching.
 *
 * Note:            None
 ********************************************************************/
static void SSLBufferSync(BYTE id)
{
	// Check if already loaded
	if(sslBufferID == id)
		return;
	
	// Save old buffer
	if(sslBufferID != SSL_INVALID_ID)
		SaveOffChip((BYTE*)&sslBuffer,
			SSL_BASE_BUFFER_ADDR+SSL_BUFFER_SIZE*sslBufferID,
			SSL_BUFFER_SIZE);
	
	// Load new buffer if not requesting temporary space
	if(id != SSL_INVALID_ID)
		LoadOffChip((BYTE*)&sslBuffer,
			SSL_BASE_BUFFER_ADDR+SSL_BUFFER_SIZE*id,
			SSL_BUFFER_SIZE);
	sslBufferID = id;
}

/*********************************************************************
 * Function:        static BYTE SSLSessionNew(void)
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          Allocated Session ID, or SSL_INVALID_ID if none available
 *
 * Side Effects:    None
 *
 * Overview:        Finds space for a new SSL session
 *
 * Note:            None
 ********************************************************************/
static BYTE SSLSessionNew(void)
{
	BYTE id, oldestID;
	DWORD now, age, oldest;
	
	// Set up the search
	oldestID = SSL_INVALID_ID;
	oldest = SSL_MIN_SESSION_LIFETIME;
	now = TickGet();
		
	// Search for a free session
	for(id = 0; id != MAX_SSL_SESSIONS; id++)
	{
		if(sslSessionStubs[id].tag.Val == 0u)
		{// Unused session, so claim immediately
			break;
		}
		
		// Check how old this session is
		age = now - sslSessionStubs[id].lastUsed;
		if(age > oldest)
		{// This is now the oldest one
			oldest = age;
			oldestID = id;
		}
	}
	
	// Check if we can claim a session
	if(id == MAX_SSL_SESSIONS && oldestID != SSL_INVALID_ID)
		id = oldestID;
	
	// If a valid ID was found, claim it
	if(id < MAX_SSL_SESSIONS)
	{
		// Save old one if needed
		if(sslSessionUpdated)
			SaveOffChip((BYTE*)&sslSession,
				SSL_BASE_SESSION_ADDR+SSL_SESSION_SIZE*sslSessionID,
				SSL_SESSION_SIZE);
		
		// Set up the new session
		sslSessionID = id;
		sslSessionStubs[id].lastUsed = now;
		SSLSessionUpdated();
		return id;
	}
	
	return SSL_INVALID_ID;
}

/*********************************************************************
 * Function:        static BYTE SSLSessionMatchID(BYTE* SessionID)
 *
 * PreCondition:    None
 *
 * Input:           SessionID - the session identifier to match
 *
 * Output:          The matched session ID, or SSL_INVALID_ID if not found
 *
 * Side Effects:    None
 *
 * Overview:        Locates a cached SSL session for reuse.  Syncs 
 *                  found session into RAM.
 *
 * Note:            None
 ********************************************************************/
#if defined(STACK_USE_SSL_SERVER)
static BYTE SSLSessionMatchID(BYTE* SessionID)
{
	BYTE i;
	
	for(i = 0; i < MAX_SSL_SESSIONS; i++)
	{
		// Check if tag matches the ID
		if(sslSessionStubs[i].tag.v[0] == 0u &&
			!memcmp((void*)&sslSessionStubs[i].tag.v[1], (void*)SessionID, 3) )
		{
			// Found a partial match, so load it to memory
			SSLSessionSync(i);
			
			// Verify complete match
			if(memcmp((void*)sslSession.sessionID, (void*)SessionID, 32) != 0)
				continue;
			
			// Mark it as being used now
			sslSessionStubs[i].lastUsed = TickGet();
			
			// Return this session for use
			return i;
		}
	}
	
	return SSL_INVALID_ID;

}
#endif

/*********************************************************************
 * Function:        static BYTE SSLSessionMatchHost(IP_ADDR ip)
 *
 * PreCondition:    None
 *
 * Input:           ip - the host session to match
 *
 * Output:          The matched session ID, or SSL_INVALID_ID if not found
 *
 * Side Effects:    None
 *
 * Overview:        Locates a cached SSL session for reuse
 *
 * Note:            None
 ********************************************************************/
#if defined(STACK_USE_SSL_CLIENT)
static BYTE SSLSessionMatchIP(IP_ADDR ip)
{
	BYTE i;
	
	for(i = 0; i < MAX_SSL_SESSIONS; i++)
	{
		// Check if tag matches the IP
		if(!memcmp((void*)&sslSessionStubs[i].tag.v[0], (void*)&ip, 4))
		{
			// Found a match, so load it to memory
			SSLSessionSync(i);
			
			// Mark it as being used now
			sslSessionStubs[i].lastUsed = TickGet();
			
			// Return this session for use
			return i;
		}
	}
	
	// No match so return invalid
	return SSL_INVALID_ID;
}
#endif

/*********************************************************************
 * Function:        static void SSLSessionSync(BYTE id)
 *
 * PreCondition:    None
 *
 * Input:           id - the session ID to sync to RAM
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Specified session is loaded to RAM.  Only loads if
 *					necessary, and saves any current session before
 *					switching if it has been updated.
 *
 * Note:            None
 ********************************************************************/
static void SSLSessionSync(BYTE id)
{
	// Check if already loaded
	if(sslSessionID == id)
		return;
	
	// Save old buffer
	if(sslSessionUpdated && sslSessionID != SSL_INVALID_ID)
		SaveOffChip((BYTE*)&sslSession,
			SSL_BASE_SESSION_ADDR+SSL_SESSION_SIZE*sslSessionID,
			SSL_SESSION_SIZE);
	
	// Load new buffer
	LoadOffChip((BYTE*)&sslSession,
		SSL_BASE_SESSION_ADDR+SSL_SESSION_SIZE*id,
		SSL_SESSION_SIZE);
	sslSessionID = id;
	sslSessionUpdated = FALSE;
}

/*********************************************************************
 * Function:        static void SaveOffChip(BYTE *ramAddr,
 *											WORD ethAddr, WORD len)
 *
 * PreCondition:    None
 *
 * Input:           ramAddr - source address in RAM
 *					ethAddr - destination address in Ethernet RAM
 *					len		- number of bytes to copy
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Copies data in PIC RAM to the Ethernet RAM
 *
 * Note:            None
 ********************************************************************/
static void SaveOffChip(BYTE *ramAddr, PTR_BASE ethAddr, WORD len)
{
	PTR_BASE oldPtr;
	
	oldPtr = MACSetWritePtr(ethAddr);
	MACPutArray(ramAddr, len);
	MACSetWritePtr(oldPtr);
}

/*********************************************************************
 * Function:        static void LoadOffChip(BYTE *ramAddr,
 *											WORD ethAddr, WORD len)
 *
 * PreCondition:    None
 *
 * Input:           ramAddr - destination address in RAM
 *					ethAddr - source address in Ethernet RAM
 *					len		- number of bytes to copy
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:        Copies data from Ethernet RAM to local RAM
 *
 * Note:            None
 ********************************************************************/
static void LoadOffChip(BYTE *ramAddr, PTR_BASE ethAddr, WORD len)
{
    PTR_BASE oldPtr;
	
	oldPtr = MACSetReadPtr(ethAddr);
	MACGetArray(ramAddr, len);
	MACSetReadPtr(oldPtr);
}


/****************************************************************************
  ===========================================================================
  Section:
	SSL Handshake Hashing Functions
  ===========================================================================
  ***************************************************************************/

/*********************************************************************
 * Function:        static void HSStart()
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:		Sets up the buffer to store data for handshake
 *					hash tracking
 *
 * Note:            None
 ********************************************************************/
static void HSStart()
{
	// Allocate temporary storage and set the pointer to it
	SSLBufferSync(SSL_INVALID_ID);
	ptrHS = sslBuffer.full;
}

/*********************************************************************
 * Function:        static void HSEnd()
 *
 * PreCondition:    None
 *
 * Input:           None
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:		Hashes the message contents into the Handshake
 *					hash structures and begins a new handshake hash.
 *
 * Note:            None
 ********************************************************************/
static void HSEnd()
{
	// Hash in the received data and reset the pointer
	if(sslHashID == sslStub.idMD5)
	{
		HashAddData(&sslHash, sslBuffer.full, ptrHS - sslBuffer.full);
		SSLHashSync(sslStub.idSHA1);
		HashAddData(&sslHash, sslBuffer.full, ptrHS - sslBuffer.full);
	}
	else
	{
		SSLHashSync(sslStub.idSHA1);
		HashAddData(&sslHash, sslBuffer.full, ptrHS - sslBuffer.full);
		SSLHashSync(sslStub.idMD5);
		HashAddData(&sslHash, sslBuffer.full, ptrHS - sslBuffer.full);
	}
	ptrHS = sslBuffer.full;
}

/*********************************************************************
 * Function:        static WORD HSGet(TCP_SOCKET skt, BYTE *b)
 *
 * PreCondition:    None
 *
 * Input:           skt - socket to read data from
 *					b	- byte to read into
 *
 * Output:          Number of bytes read
 *
 * Side Effects:    None
 *
 * Overview:        Reads data from socket, transparently hashing it
 *					into the handshake hashes.
 *
 * Note:            None
 ********************************************************************/
static WORD HSGet(TCP_SOCKET skt, BYTE *b)
{
	BYTE c;
	
	// Must read to &c in case b is NULL (we still need to hash it)
	if(!TCPGet(skt, &c))
		return 0;
	
	*ptrHS = c;
	if(b)
		*b = c;
	ptrHS++;
	
	sslStub.wRxBytesRem--;
	sslStub.wRxHsBytesRem--;
	
	if(ptrHS > sslBuffer.full + 128)
	{
		HSEnd();
		HSStart();
	}

	return 1;
}

/*********************************************************************
 * Function:        static WORD HSGetWord(TCP_SOCKET skt, WORD *w)
 *
 * PreCondition:    None
 *
 * Input:           skt - socket to read data from
 *					w	- word to read into
 *
 * Output:          Number of bytes read
 *
 * Side Effects:    None
 *
 * Overview:        Reads data from socket, transparently hashing it
 *					into the handshake hashes.
 *
 * Note:            None
 ********************************************************************/
static WORD HSGetWord(TCP_SOCKET skt, WORD *w)
{
	if(w == NULL)
		return HSGet(skt, (BYTE*)w) + HSGet(skt, (BYTE*)w);
	else
		return HSGet(skt, (BYTE*)w+1) + HSGet(skt, (BYTE*)w);
}

/*********************************************************************
 * Function:        static WORD HSGetArray(TCP_SOCKET skt, 
 											 BYTE *data, WORD len)
 *
 * PreCondition:    None
 *
 * Input:           skt  - socket to read data from
 *					data - array to read into, or NULL
 *					len  - number of bytes to read
 *
 * Output:          Number of bytes read
 *
 * Side Effects:    None
 *
 * Overview:        Reads data from socket, transparently hashing it
 *					into the handshake hashes.
 *
 * Note:            None
 ********************************************************************/
static WORD HSGetArray(TCP_SOCKET skt, BYTE *data, WORD len)
{	
	WORD w;

/*	
	WORD wLenActual;
	WORD wChunkLen;

	wLenActual = 0;
	// Add all of this data to the running hash
	while(len)
	{
		wChunkLen = sizeof(sslBuffer.full) - (ptrHS - sslBuffer.full);
		if(wChunkLen == 0u)
		{
			HSEnd();
			wChunkLen = sizeof(sslBuffer.full);
		}	
		if(len < wChunkLen)
			wChunkLen = len;
		
		// Obtain data from TCP
		w = TCPGetArray(skt, ptrHS, wChunkLen);
		if(w == 0u)
			return wLenActual;
		
		// Copy data from hash area to *data area if a non-NULL pointer was 
		// provided
		if(data)
		{
			memcpy((void*)data, ptrHS, w);
			data += w;
		}
		
		// Update all tracking variables
		ptrHS += w;
		len -= w;
		wLenActual += w;
		sslStub.wRxBytesRem -= w;
		sslStub.wRxHsBytesRem -= w;
	}

	return wLenActual;
*/

	//if reading to NULL, we still have to hash
	if(!data)
	{
		WORD i, rem;
		for(i = 0; i < len; )
		{
			// Determine how much we can read
			rem = (sslBuffer.full + 255) - ptrHS;
			if(rem > len - i)
				rem = len - i;

			// Read that much
			rem = TCPGetArray(skt, ptrHS, rem);
			sslStub.wRxBytesRem -= rem;
			sslStub.wRxHsBytesRem -= rem;
						
			// Hash what we've got
			ptrHS += rem;
			HSEnd();
			HSStart();
			
			i += rem;
			
			// Make sure we aren't in an infinite loop
			if(rem == 0u)
				break;
		}
		
		return i;
	}
	
	len = TCPGetArray(skt, data, len);
	w = len;
	
	// TODO: BUG!!!: This must be fixed somehow so that buffer overflow is not 
	// possible
	memcpy(ptrHS, (void*)data, len);
	ptrHS += len;

	
//	// Add all of this data to the running hash
//	while(w)
//	{
//		WORD wChunkLen = sizeof(sslBuffer.full) - (ptrHS - sslBuffer.full);
//		if(wChunkLen == 0u)
//		{
//			HSEnd();
//			HSStart();
//			wChunkLen = sizeof(sslBuffer.full);
//		}	
//		if(w < wChunkLen)
//			wChunkLen = w;
//		memcpy(ptrHS, (void*)data, wChunkLen);
//		ptrHS += wChunkLen;
//		w -= wChunkLen;
//	}
	
	sslStub.wRxBytesRem -= len;
	sslStub.wRxHsBytesRem -= len;
	
	if(ptrHS > sslBuffer.full + 128)
	{
		HSEnd();
		HSStart();
	}
	
	return len;
}

/*********************************************************************
 * Function:        static WORD HSPut(TCP_SOCKET skt, BYTE b)
 *
 * PreCondition:    None
 *
 * Input:           skt  - socket to write data to
 *					b    - byte to write
 *
 * Output:          Number of bytes written
 *
 * Side Effects:    None
 *
 * Overview:        Writes data to socket, transparently hashing it
 *					into the handshake hashes.
 *
 * Note:            None
 ********************************************************************/
static WORD HSPut(TCP_SOCKET skt, BYTE b)
{
	if(!TCPPut(skt, b))
		return 0;

	// Ensure we don't overflow the Hash buffer	
	if(sizeof(sslBuffer.full) - (ptrHS - sslBuffer.full) == 0u)
		HSEnd();
	
	// Add this byte of data to the running hash
	*ptrHS = b;
	ptrHS++;

	return 1;
}

/*********************************************************************
 * Function:        static WORD HSPutWord(TCP_SOCKET skt, WORD w)
 *
 * PreCondition:    None
 *
 * Input:           skt  - socket to write data to
 *					w    - word to write
 *
 * Output:          Number of bytes written
 *
 * Side Effects:    None
 *
 * Overview:        Writes data to socket, transparently hashing it
 *					into the handshake hashes.
 *
 * Note:            None
 ********************************************************************/
static WORD HSPutWord(TCP_SOCKET skt, WORD w)
{
	return HSPut(skt, (BYTE)(w>>8)) + HSPut(skt, (BYTE)w);
}

/*********************************************************************
 * Function:        static WORD HSPutArray(TCP_SOCKET skt,
 											BYTE *data, BYTE len)
 *
 * PreCondition:    None
 *
 * Input:           skt  - socket to write data to
 *					data - data to write
 *					len  - number of bytes to write
 *
 * Output:          Number of bytes written
 *
 * Side Effects:    None
 *
 * Overview:        Writes data to socket, transparently hashing it
 *					into the handshake hashes.
 *
 * Note:            None
 ********************************************************************/
static WORD HSPutArray(TCP_SOCKET skt, BYTE *data, WORD len)
{	
	WORD w;
	
	len = TCPPutArray(skt, data, len);
	w = len;

	// Add all of this data to the running hash
	while(w)
	{
		WORD wChunkLen = sizeof(sslBuffer.full) - (ptrHS - sslBuffer.full);
		if(wChunkLen == 0u)
		{
			HSEnd();
			wChunkLen = sizeof(sslBuffer.full);
		}	
		if(w < wChunkLen)
			wChunkLen = w;
		memcpy(ptrHS, (void*)data, wChunkLen);
		data += wChunkLen;
		ptrHS += wChunkLen;
		w -= wChunkLen;
	}
	
	return len;
}

#if defined(STACK_USE_SSL_SERVER)
static WORD HSPutROMArray(TCP_SOCKET skt, ROM BYTE *data, WORD len)
{	
	WORD w;
	
	len = TCPPutROMArray(skt, data, len);
	w = len;

	// Add all of this data to the running hash
	while(w)
	{
		WORD wChunkLen = sizeof(sslBuffer.full) - (ptrHS - sslBuffer.full);
		if(wChunkLen == 0u)
		{
			HSEnd();
			wChunkLen = sizeof(sslBuffer.full);
		}	
		if(w < wChunkLen)
			wChunkLen = w;
		memcpypgm2ram(ptrHS, (ROM void*)data, wChunkLen);
		data += wChunkLen;
		ptrHS += wChunkLen;
		w -= wChunkLen;
	}

	return len;
}
#endif

/****************************************************************************
  ===========================================================================
  Section:
	SSL MAC Hashing Functions
  ===========================================================================
  ***************************************************************************/

/*********************************************************************
 * Function:        static void SSLMACBegin(BYTE *MACSecret, DWORD seq, 
 *											BYTE protocol, WORD len)
 *
 * PreCondition:    sslHash is ready to be written
 *					(any pending data saved, nothing useful stored)
 *
 * Input:           MACSecret - the MAC write secret
 *					seq       - the sequence number for this message
 *					protocol  - the SSL_PROTOCOL for this message
 *					len       - the length of the message being MAC'd
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:		Begins a MAC calculation in sslHash
 *
 * Note:            None
 ********************************************************************/
void SSLMACBegin(BYTE *MACSecret, DWORD seq, BYTE protocol, WORD len)
{
	BYTE i, temp[7];

	MD5Initialize(&sslHash);

	// Form the temp array
	temp[0] = *((BYTE*)&seq+3);
	temp[1] = *((BYTE*)&seq+2);
	temp[2] = *((BYTE*)&seq+1);
	temp[3] = *((BYTE*)&seq+0);
	temp[4] = protocol;
	temp[5] = *((BYTE*)&len+1);
	temp[6] = *((BYTE*)&len+0);
		
	// Hash the initial data (secret, padding, seq, protcol, len)
	HashAddData(&sslHash, MACSecret, 16);
	
	// Add in the padding
	for(i = 0; i < 6u; i++)
	{
		HashAddROMData(&sslHash, (ROM BYTE*)"\x36\x36\x36\x36\x36\x36\x36\x36", 8);
	}

	// Hash in the data length
	HashAddROMData(&sslHash, (ROM BYTE*)"\0\0\0\0", 4);
	HashAddData(&sslHash, temp, 7);	
}

/*********************************************************************
 * Function:        void SSLMACAdd(BYTE *data, WORD len)
 *
 * PreCondition:    sslHash is ready to be written
 *					(any pending data saved, SSLMACBegin called)
 *
 * Input:           data - the data to add to the MAC
 *					len  - the length of data
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:		Adds more data to a MAC in progress
 *
 * Note:            None
 ********************************************************************/
void SSLMACAdd(BYTE *data, WORD len)
{
	HashAddData(&sslHash, data, len);
}

/*********************************************************************
 * Function:        static void SSLMACCalc(BYTE *result)
 *
 * PreCondition:    sslHash is ready to be written
 *					(any pending data saved, SSLMACBegin called)
 *
 * Input:           MACSecret - the MAC write secret
 *					result    - a 16 byte buffer to store result
 *
 * Output:          None
 *
 * Side Effects:    None
 *
 * Overview:		Finishes the MAC calculation, and places the
 *					result in the *result array
 *
 * Note:            None
 ********************************************************************/
void SSLMACCalc(BYTE *MACSecret, BYTE *result)
{
	BYTE i;
	
	// Get inner hash result
	MD5Calculate(&sslHash, result);
	
	// Begin outer hash
	MD5Initialize(&sslHash);
	HashAddData(&sslHash, MACSecret, 16);
	
	// Add in padding
	for(i = 0; i < 6u; i++)
	{
		HashAddROMData(&sslHash, (ROM BYTE*)"\x5c\x5c\x5c\x5c\x5c\x5c\x5c\x5c", 8);
	}
	
	// Hash in the previous result and calculate
	HashAddData(&sslHash, result, 16);
	MD5Calculate(&sslHash, result);	
}

#endif
