// Copyright (C) 2000
//          by the Massachusetts Institute of Technology
//
//    Export of this software from the United States of America may
//    require a specific license from the United States Government.  It
//    is the responsibility of any person or organization contemplating
//    export to obtain such a license before exporting.
//
// WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
// distribute this software and its documentation for any purpose and
// without fee is hereby granted, provided that the above copyright
// notice appear in all copies and that both that copyright notice and
// this permission notice appear in supporting documentation, and that
// the name of M.I.T. not be used in advertising or publicity pertaining
// to distribution of the software without specific, written prior
// permission.  M.I.T. makes no representations about the suitability of
// this software for any purpose.  It is provided "as is" without express
// or implied warranty.

// ssh.cpp
//  Secure Shell Class Implementation

#include "ssh.h"

//#include <memory.h>
//#include <string.h>

#include "stdafx.h"
#include "Errors.h"
#include "crc32.h"

// PTY Encodings (These should probably go somewhere else.

static unsigned char g_pucPtyEncodings[109] = {
	0xC1, 0x00, 0x00, 0x25, 0x80,		// TTY_OP_ISPEED = 9600 BAUD
	0xC0, 0x00, 0x00, 0x25, 0x80,		// TTY_OP_OSPEED = 9600 BAUD
	0x01, 0x03,	0x02, 0x1C,				// VINTR (CTRL-C), VQUIT (CTRL-\)
	0x03, 0x7F, 0x04, 0x16,
	0x05, 0x04, 0x06, 0x00,
	0x07, 0x00, 0x08, 0x11,
	0x09, 0x13, 0x0A, 0x1A,
	0x0C, 0x12, 0x0D, 0x17,
	0x0E, 0x16, 0x12, 0x0F,
	0x1E, 0x00, 0x1F, 0x00,				// IGNPAR, PARMARK
	0x20, 0x00, 0x21, 0x00,
	0x22, 0x00, 0x23, 0x00,
	0x24, 0x01, 0x25, 0x00,
	0x26, 0x00, 0x27, 0x00,				
	0x28, 0x00, 0x29, 0x00,
	0x32, 0x01, 0x33, 0x01,
	0x34, 0x01, 0x35, 0x01,				
	0x36, 0x01, 0x37, 0x01,
	0x38, 0x00, 0x39, 0x00,
	0x3A, 0x00, 0x3B, 0x01,
	0x3C, 0x01, 0x3D, 0x01,
	0x3E, 0x01, 0x46, 0x01,
	0x47, 0x00, 0x48, 0x01,
	0x49, 0x00, 0x4A, 0x00,
	0x4B, 0x00, 0x5A, 0x00,
	0x5B, 0x01, 0x5C, 0x00,
	0x5D, 0x00, 0x00						// PARODD, TTY_OP_END
};

static unsigned char g_pucPtyEncodingsAltOne[27] = {
	0xC1, 0x00, 0x00, 0x25, 0x80,		// TTY_OP_ISPEED = 9600 BAUD
	0xC0, 0x00, 0x00, 0x25, 0x80,		// TTY_OP_OSPEED = 9600 BAUD
	0x01, 0x03,	0x02, 0x1C,				// VINTR (CTRL-C), VQUIT (CTRL-\)
	0x1E, 0x00, 0x1F, 0x00,				// IGNPAR, PARMARK
	0x32, 0x01, 0x33, 0x00,
	0x47, 0x00, 0x48, 0x01,
	0x00						// PARODD, TTY_OP_END
};

static unsigned char g_pucPtyEncodingsThree[59] = {
	0xC1, 0x00, 0x00, 0x25, 0x80,		// TTY_OP_ISPEED = 9600 BAUD
	0xC0, 0x00, 0x00, 0x25, 0x80,		// TTY_OP_OSPEED = 9600 BAUD
	
	// Special Control Characters. (8)
	// VQUIT (CTRL-\), VSUSP (CTRL-Z), VSTART (CTRL-Q), VSTOP (CTRL-S), 
	0x02, 0x1C, 0x06, 0x1A, 0x08, 0x11, 0x09, 0x13,	

	// Input Modes (18)
	0x1E, 0x01, 0x1F, 0x00,				// IGNPAR, PARMARK
	0x20, 0x00, 0x22, 0x00, 
	0x23, 0x00,	0x24, 0x01, 
	0x26, 0x01, 0x27, 0x00,				
	0x28, 0x00, 
	
	// Local Modes (8)
	0x32, 0x01, 0x33, 0x00,
	0x35, 0x01, 0x3B, 0x00,

	// Output Modes (8)
	0x46, 0x01, 0x48, 0x01,
	0x49, 0x00, 0x4A, 0x00,

	// Control Modes (7)
	0x5B, 0x01, 0x5C, 0x00,
	0x5D, 0x00, 0x00						// PARODD, TTY_OP_END
};

//------------------------------------------------//
// Inline Functions for 16/32bit MSB conversions. //
//------------------------------------------------//

inline unsigned int uiGet32Bit(unsigned char *pszWord) {
	return (unsigned int)( (pszWord[0] << 24) | (pszWord[1] << 16) |
		(pszWord[2] << 8) | (pszWord[3])); }

inline unsigned int uiGet16Bit(unsigned char *pszWord) { 
	return (unsigned int) (pszWord[0] << 8) | (pszWord[1]); }

inline void Put32Bit(unsigned char *pszWord, unsigned int uiValue) {
  pszWord[0] = uiValue >> 24; pszWord[1] = uiValue >> 16; 
  pszWord[2] = uiValue >> 8; pszWord[3] = uiValue; } 

inline void Put16Bit(unsigned char *pszWord, unsigned int uiValue) {
  pszWord[0] = uiValue >> 8; pszWord[1] = uiValue; } 

//--------------------------//
// Constructor / Destructor //
//--------------------------//

CSsh::CSsh() {

	if ((m_pucSessionKey = new unsigned char[SSH_SESSION_KEY_LENGTH]) == NULL) {
		// Fatal Error Handling
	}

	sprintf(szProtocolVersion, "%s", SSH_PROTOCOL_VERSION);
	m_uiEncryptionMethod = SSH_CIPHER_3DES;			// Set to 3DES by default.

	g_pszErrorMessage = (char *) m_pucErrorMessage;

#ifdef USE_CRYPTLIB
	cryptInit();
	
	// Poll for random information for random number generation
	cryptAddRandom (NULL, CRYPT_RANDOM_SLOWPOLL);
#endif

}

CSsh::~CSsh() {

#ifdef USE_CRYPTLIB
	cryptEnd();
#endif

#ifdef DEBUG_SSH
	Debug("CSsh::~CSsh() - Destroying Class CSsh Object");
#endif 
	delete m_pucSessionKey;

	// Cleanup Memory Remants
	CleanSessionData();

#ifdef _DEBUG
	AfxCheckMemory();
#endif
}

//------------------//
// Public Functions //
//------------------//

int CSsh::iProcessIncomingPacket(int iExpectedPacket, unsigned char *pucPacket) {
	
	int	iPacketLength;
	int	iPadding;
	int	iPacketType;
	int	iWordLength;

	iPacketLength = iGetWord(pucPacket);
	iPadding = 8 - (iPacketLength % 8);

#ifdef DEBUG_SSH

	Debug("CSsh::iProcessIncomingPacket() - Processing Incoming Packet");

	// HexDump Network Content 
	DebugPlain("Received SSH Packet - RAW:");
	DebugPacket((unsigned char *)(LPCSTR)pucPacket, iPacketLength + iPadding + 4);
#endif 

	// Check if we need to decrypt packet, if so - decrypt.
	if ( (iExpectedPacket == SSH_SMSG_PUBLIC_KEY) || (m_uiEncryptionMethod == 0)) {
	

	} // end if SSH_SMSG_PUBLIC_KEY
	else {
		DecryptData(pucPacket);
#ifdef DEBUG_SSH
		// HexDump Network Content 
		Debug("CSsh::iProcessIncomingPacket() - Decrypted Data");
		Debug("CSsh::iProcessIncomingPacket() - Packet Length = %d", iPacketLength);
		Debug("CSsh::iProcessIncomingPacket() - Packet Padding = %d", iPadding);
		DebugPlain("Received SSH Packet - DECRYPTED:");
		DebugPacketWithASCII((unsigned char *)(LPCSTR)pucPacket, iPacketLength + iPadding + 4);
#endif 
	} // end else

	// Do CRC32 Check
	unsigned long iCheckBytes;

	// Checksum performed on padding + packet type + data
	iCheckBytes = iGetCrc32(pucPacket + 4, iPacketLength + iPadding - 4);
	if (iCheckBytes != (int) iGetWord(pucPacket + iPacketLength + iPadding)) {
		// TODO: Add error - CRC Check did not pass
		// 
#ifdef DEBUG_SSH
		Debug("CSsh::iProcessIncomingPacket() - Unsuccesful Checksum Match. CRC32= %X", iCheckBytes);
#endif
		return ERROR_SSH_CHECKSUM_MISMATCH;
	}
#ifdef DEBUG_SSH
		Debug("CSsh::iProcessIncomingPacket() - Succesful Checksum Match. CRC32= %X", iCheckBytes);
#endif

	if (m_uiCompressionLevel > 0) {
		// TODO: Decompress
	}

	iPacketType = pucPacket[4 + iPadding];

	// TODO: Check Packet Type w/ExpectedPacket
#ifdef DEBUG_SSH
		Debug("CSsh::iProcessIncomingPacket() - Received PacketType: %d", 
			(unsigned int)iPacketType);
#endif

	switch (iPacketType) {

		case SSH_SMSG_PUBLIC_KEY:

			return SmsgPublicKey(pucPacket);

			break;
	
		case SSH_SMSG_SUCCESS:
			return (SSH_SMSG_SUCCESS);
			break;

		case SSH_SMSG_FAILURE:
			return (SSH_SMSG_FAILURE);
			break;

		case SSH_SMSG_STDOUT_DATA:
			m_iStdoutLength = (int) uiGet32Bit(pucPacket + iPadding + 5);
			m_pucStdout = pucPacket + iPadding + 9;
			return (SSH_SMSG_STDOUT_DATA);

		case SSH_SMSG_STDERR_DATA:
			m_iStdoutLength = (int) uiGet32Bit(pucPacket + iPadding + 5);
			m_pucStdout = pucPacket + iPadding + 9;
			return (SSH_SMSG_STDOUT_DATA);

		case SSH_MSG_DISCONNECT:
			iWordLength = (int) uiGet32Bit(pucPacket + iPadding + 5);
			memcpy(m_pucErrorMessage, pucPacket + iPadding + 9, iWordLength);
			m_pucErrorMessage[iWordLength+1] = NULL;
			return (ERROR_SSH_MSG_DISCONNECT);
			break;

		case SSH_SMSG_EXITSTATUS:
			return (SSH_SMSG_EXITSTATUS);

		case SSH_MSG_DEBUG:
			iWordLength = (int) uiGet32Bit(pucPacket + iPadding + 5);
			memcpy(m_pucErrorMessage, pucPacket + iPadding + 9, iWordLength);
			m_pucErrorMessage[iWordLength] = NULL;
#ifdef DEBUG_SSH
			Debug("CSsh::iProcessIncomingPacket() - SSH_MSG_DEBUG: %s: ", m_pucErrorMessage);
#endif
			return (SSH_MSG_DEBUG);
			break;

		default: // should never get here.
			break;
	} // end switch

	return ERROR_SSH_UNKNOWN_PACKET_TYPE;
} // end CSsh::iProcessIncomingPacket()

int CSsh::SmsgPublicKey(unsigned char * szPacket) {

	char szWord[4];
	int iPacketLength;
	int iPadding;
	int iPacketType;
		
	// Information in SSH_SMSG_PUBLIC_KEY
	unsigned char		szAntiSpoofingCookie[8];

	unsigned int		iServerKeyBits;
	unsigned char *	pszServerKeyPublicExponent;
	int					iServerKeyPublicExponentBitSize;
	unsigned char *	pszServerKeyPublicModulus;
	int					iServerKeyPublicModulusBitSize;

	unsigned int		iHostKeyBits;
	unsigned char *	pszHostKeyPublicExponent;
	int					iHostKeyPublicExponentBitSize;
	unsigned char *	pszHostKeyPublicModulus;
	int					iHostKeyPublicModulusBitSize;

	unsigned int	iProtocolFlags;
	int	iKeyByteSize;			// Temp For converting bits to byte size

#ifdef DEBUG_SSH
	Debug("CSsh::SmsgPublicKey() - Entering Function");
#endif

	m_iRecvBufOffset = 0;

	// Get Length
	memcpy (szWord, szPacket, 4);
	iPacketLength = iGetWord(szPacket);

	// Calculate Padding Size
	iPadding = 8 - (iPacketLength % 8); // defined in RFC
	m_iRecvBufOffset += iPadding;

	// Get Packet Type
	iPacketType = szPacket[m_iRecvBufOffset];
	m_iRecvBufOffset++;

	// Get AntiSpoofing Cookie
	memcpy (szAntiSpoofingCookie, szPacket + m_iRecvBufOffset, 8); 
	m_iRecvBufOffset += 8;

	// Get ServerKeyBits, Exponent and Modulus
	iServerKeyBits = iGetWord(szPacket + m_iRecvBufOffset);	
	pszServerKeyPublicExponent = pszGetMpInt(szPacket + m_iRecvBufOffset, 
								&iServerKeyPublicExponentBitSize);
	pszServerKeyPublicModulus  = pszGetMpInt(szPacket + m_iRecvBufOffset, 
								&iServerKeyPublicModulusBitSize);

	// Get Hostkey Bits, Exponent and Modulus
	iHostKeyBits = iGetWord(szPacket + m_iRecvBufOffset);
	pszHostKeyPublicExponent = pszGetMpInt(szPacket + m_iRecvBufOffset,
							&iHostKeyPublicExponentBitSize);
	pszHostKeyPublicModulus  = pszGetMpInt(szPacket + m_iRecvBufOffset,
							&iHostKeyPublicModulusBitSize);

	// Get the rest
	iProtocolFlags							= iGetWord(szPacket + m_iRecvBufOffset);
	m_uiServerEncryptionMethods		= iGetWord(szPacket + m_iRecvBufOffset);
	m_uiServerAuthenticationMethods	= iGetWord(szPacket + m_iRecvBufOffset);

	//--------------------------//
	// Calculate the Session Id //
	//--------------------------//

	unsigned char		szSessionIdString[1024];
	unsigned char		szSessionIdHashed[16];
	int iTempOffset;

	// Concatenate Host Modulus + Server Modulus + Cookie
	iKeyByteSize = (iHostKeyPublicModulusBitSize + 7) / 8;
	memcpy (szSessionIdString, pszHostKeyPublicModulus, iKeyByteSize);
 	iTempOffset = iKeyByteSize;
	iKeyByteSize = (iServerKeyPublicModulusBitSize + 7) / 8;
	memcpy (szSessionIdString + iTempOffset, pszServerKeyPublicModulus, 
		iKeyByteSize);
	iTempOffset += iKeyByteSize;
	memcpy (szSessionIdString + iTempOffset, szAntiSpoofingCookie, 8);
	iTempOffset += 8;


	// Calculate MD5 of resulting szSessionIdString to get szSessionId
#ifdef USE_CRYPTLIB
	CRYPT_QUERY_INFO cryptQueryInfo;
	CRYPT_CONTEXT cryptContext;

	cryptCreateContext(&cryptContext, CRYPT_ALGO_MD5, CRYPT_MODE_NONE);
	cryptEncrypt(cryptContext, szSessionIdString, iTempOffset);
	cryptEncrypt(cryptContext, szSessionIdString, 0);
	cryptQueryContext(cryptContext, &cryptQueryInfo);
	cryptDestroyContext(cryptContext);

	// Copy hashed value into SessionId pointer
	memcpy (szSessionIdHashed, cryptQueryInfo.hashValue, cryptQueryInfo.blockSize);

#ifdef DEBUG_SSH
	Debug("CSsh::SmsgPublicKey() - MD5 Hashed Session ID Created: %X", szSessionIdHashed);
#endif

	// Generate Random data for Session Key
	cryptGetRandom ((void *) m_pucSessionKey, SSH_SESSION_KEY_LENGTH);
#endif // end USE_CRYPTLIB
	
	SetSessionContexts();

#ifdef DEBUG_SSH
	DebugPlain("Session Key:");
	DebugPacket(m_pucSessionKey, 32);

#endif

	// According to RFC, XOR first 16 bits of SessionID with first 16 bits of SessionKey

	unsigned char szTempEncrypted[SSH_SESSION_KEY_LENGTH];

	for (int iCount=0; iCount < 32; iCount++) {
		if (iCount < 16) 
			szTempEncrypted[iCount] = m_pucSessionKey[iCount] ^ szSessionIdHashed[iCount];
		else
			szTempEncrypted[iCount] = m_pucSessionKey[iCount];
	}


	// Encrypt Session Key twice, using first the smaller modulus of server or host key.
#ifdef USE_CRYPTLIB
	CRYPT_CONTEXT	cryptServerContext, cryptHostContext; 
	CRYPT_PKCINFO_RSA *rsaServerKey, *rsaHostKey; 
	int iStatus;

	if ((rsaServerKey = new CRYPT_PKCINFO_RSA) == NULL) {
		// TODO: Fatal Error Handling
	}

	if ((rsaHostKey = new CRYPT_PKCINFO_RSA) == NULL) {
		// TODO: Fatal Error Handling
	}

	// Prepare Server Key Context
	cryptCreateContext (&cryptServerContext, CRYPT_ALGO_RSA, CRYPT_MODE_PKC);
	cryptInitComponents(rsaServerKey, CRYPT_COMPONENTS_BIGENDIAN, CRYPT_KEYTYPE_PUBLIC);
	cryptSetComponent(rsaServerKey->n, pszServerKeyPublicModulus, iServerKeyPublicModulusBitSize); 
	cryptSetComponent(rsaServerKey->e, pszServerKeyPublicExponent, iServerKeyPublicExponentBitSize); 
	cryptLoadKey(cryptServerContext, rsaServerKey, CRYPT_UNUSED);
	cryptDestroyComponents(rsaServerKey);

	// Prepare Host Key Context
	cryptCreateContext (&cryptHostContext, CRYPT_ALGO_RSA, CRYPT_MODE_PKC);
	cryptInitComponents(rsaHostKey, CRYPT_COMPONENTS_BIGENDIAN, CRYPT_KEYTYPE_PUBLIC);
	cryptSetComponent(rsaHostKey->n, pszHostKeyPublicModulus, iHostKeyPublicModulusBitSize); 
	cryptSetComponent(rsaHostKey->e, pszHostKeyPublicExponent, iHostKeyPublicExponentBitSize); 
	cryptLoadKey(cryptHostContext, rsaHostKey, CRYPT_UNUSED);
	cryptDestroyComponents(rsaHostKey);
	
	delete rsaHostKey;
	delete rsaServerKey;
	
	unsigned char * pszFirstEncryption;
	unsigned char * pszSecondEncryption;

	// Encrypt first with key that has the smaller modulus then encrypt using the other
	// key.
	// Build a MPINT according to PKCS#1 Spec. - first byte = 00, second byte 01.
	if (iServerKeyBits < iHostKeyBits) {

		iKeyByteSize = (iServerKeyPublicModulusBitSize + 7) / 8;
		// MP int length = number of bytes to represent the modulus of smaller key.
		if ((pszFirstEncryption = new unsigned char[iKeyByteSize]) == NULL) {
			// TODO: Fatal Memory Error Handling
		}
		CreateRsaMpInt (pszFirstEncryption, iKeyByteSize,
			szTempEncrypted, SSH_SESSION_KEY_LENGTH);

		// Public Key Algorithms encrypt single block of data equal in length to 
		// the public key being used.
		iStatus = cryptEncrypt(cryptServerContext, pszFirstEncryption, CRYPT_USE_DEFAULT);
		
		// TODO: Check of iStatus to ensure that everything worked.

		// Encrypt resulting data with second larger key.
		iKeyByteSize = (iHostKeyPublicModulusBitSize + 7) / 8;
		if ((pszSecondEncryption = new unsigned char[iKeyByteSize]) == NULL) {
			// TODO: Fatal Memory Error Handling
		}
		CreateRsaMpInt (pszSecondEncryption, iKeyByteSize,
					pszFirstEncryption, (iServerKeyPublicModulusBitSize + 7) / 8);

		iStatus = cryptEncrypt(cryptHostContext, pszSecondEncryption, CRYPT_USE_DEFAULT);
	} // end if (iServerKeyBits < iHostKeyBits)
	else if (iHostKeyBits <= iServerKeyBits) {
		// TODO: Vice Versa of if statement.

	} // end else if (iHostKeyBits <= iServerKeyBits)

	cryptDestroyContext (cryptServerContext);
	cryptDestroyContext (cryptHostContext);
	
#ifdef DEBUG_SSH
	Debug("CSsh::SmsgPublicKey() - Created Encrypted Key");
#endif

#endif // end USE_CRYPTLIB

	// Start building the SSH_CMSG_SESSION_KEY packet.
	StartPacket(SSH_CMSG_SESSION_KEY);
	m_pucSendBuf[m_iSendBufOffset++] = m_uiEncryptionMethod;
	memcpy(m_pucSendBuf + m_iSendBufOffset, szAntiSpoofingCookie, 8);
	m_iSendBufOffset += 8;

	AddMpInt(pszSecondEncryption, (iHostKeyPublicModulusBitSize + 7 ) / 8);
	AddWord(0);
	FinishPacket();

#ifdef DEBUG_SSH
	Debug("CSsh::SmsgPublicKey() - Built SSH_CMSG_SESSION_KEY Packet");
#endif

	//-------------------------------------//
	// Cleanup Memory for Security reasons
	//-------------------------------------//
	memset(szAntiSpoofingCookie, 0, 8);
	iServerKeyBits=0;
	memset(pszServerKeyPublicExponent, 0, (iServerKeyPublicExponentBitSize + 7) / 8);
	delete(pszServerKeyPublicExponent);
	iServerKeyPublicExponentBitSize=0;

	memset(pszServerKeyPublicModulus, 0, (iServerKeyPublicModulusBitSize + 7) / 8);
	delete(pszServerKeyPublicModulus);
	iServerKeyPublicModulusBitSize=0;

	iHostKeyBits=0;
	memset(pszHostKeyPublicExponent, 0, (iHostKeyPublicExponentBitSize + 7) / 8);
	delete(pszHostKeyPublicExponent);
	iHostKeyPublicExponentBitSize=0;

	memset(pszHostKeyPublicModulus, 0, (iHostKeyPublicModulusBitSize + 7) / 8);
	delete(pszHostKeyPublicModulus);
	iHostKeyPublicModulusBitSize=0;

	delete(pszFirstEncryption);
	delete(pszSecondEncryption);

	// Start Encrypting after this flag
	m_iStartEncryptionFlag = 1;

	return SSH_SMSG_PUBLIC_KEY;
} // end CSsh::SmsgPublicKey()

void CSsh::CmsgUser() {

	int iUserLength = strlen(m_szUsername);

#ifdef DEBUG_SSH
	Debug("CSsh::CmsgUser() - Username: %s", m_szUsername);
#endif 

	StartPacket(SSH_CMSG_USER);
	
	AddWord((unsigned int)iUserLength);
	memcpy(m_pucSendBuf + m_iSendBufOffset, m_szUsername, iUserLength);
	m_iSendBufOffset += iUserLength;

	FinishPacket();

} // end CSsh::CMsgUser()

void CSsh::CmsgAuthPassword() {

#ifdef DEBUG_SSH
	Debug("CSsh::CmsgAuthPassword() - Password: %s", m_szPassword);
	Debug("CSsh::CmsgAuthPassword() - Server Authentication Methods: %d", 
		m_uiServerAuthenticationMethods);
#endif

	int iPassLength = strlen(m_szPassword);

	StartPacket(SSH_CMSG_AUTH_PASSWORD);
	
	AddWord((unsigned int)iPassLength);
	memcpy(m_pucSendBuf + m_iSendBufOffset, m_szPassword, iPassLength);
	m_iSendBufOffset += iPassLength;
	FinishPacket();

#ifdef DEBUG_SSH
	Debug("CSsh::CmsgAuthPassword() - Password Length: %d", iPassLength);
#endif
	

} // end CSsh::CMsgAuthPassword

void CSsh::CmsgRequestPty(char * pszTermType, unsigned int iTermRows, 
		  unsigned int iTermCols, unsigned int iTermWidth, unsigned int iTermHeight) {

	StartPacket(SSH_CMSG_REQUEST_PTY);

	// Add the Terminal Type information
	int iLength = strlen(pszTermType);
	AddWord((unsigned int)iLength);
	memcpy(m_pucSendBuf + m_iSendBufOffset, pszTermType, iLength);
	m_iSendBufOffset += iLength;

	// Add Various Row/Column and Height/Width information
	AddWord(iTermRows);
	AddWord(iTermCols);
	AddWord(iTermWidth);
	AddWord(iTermHeight);

	memcpy(m_pucSendBuf + m_iSendBufOffset, (const char *)g_pucPtyEncodingsThree, 59);
	m_iSendBufOffset += 59;


	FinishPacket();

} // end CSsh::CmsgRequestPty()

void CSsh::CmsgX11RequestForwarding() {


}

void CSsh::CmsgExecShell() {

	StartPacket(SSH_CMSG_EXEC_SHELL);
	FinishPacket();

} // end CSsh::CmsgExecShell()

void CSsh::CmsgStdinData(unsigned char * pucString, int iStringLength) {

	StartPacket(SSH_CMSG_STDIN_DATA);
	AddWord(iStringLength);

	memcpy(m_pucSendBuf + m_iSendBufOffset, pucString, iStringLength);
	m_iSendBufOffset += iStringLength;

	FinishPacket();
}

//------------------//
// Cleanup Routines //
//------------------//

void CSsh::CleanSessionData() {

	m_iSendBufOffset=0;

	m_iRecvBufOffset=0;

	m_uiServerEncryptionMethods = 0;
	m_uiServerAuthenticationMethods = 0;
	m_iStartEncryptionFlag = 0;
	
	memset (m_pucSessionKey, 0, SSH_SESSION_KEY_LENGTH);

	// Currently Destroying the contexts forces an exception to be thrown
	// at program termination.  So for now we're not including this code.
	//if ((m_cryptcontextRecv >= 0) && (m_cryptcontextRecv <= 5))
	//	cryptDestroyContext(m_cryptcontextRecv);
	//if ((m_cryptcontextSend >= 0) && (m_cryptcontextSend <= 5))
	//	cryptDestroyContext(m_cryptcontextSend);

#ifdef DEBUG_SSH
	Debug("CSsh::CleanSessionData() - Session Data has been cleared.");
#endif

} // end CSsh::CleanSessionData()

//------------------------------------------------------------------------//
//---------------------//
// PROTECTED FUNCTIONS //
//---------------------//

void CSsh::SetSessionContexts() {

#ifdef USE_CRYPTLIB

	memset (m_pucInitVectorRecv, 0, 8);
	memset (m_pucInitVectorSend, 0, 8);


	switch (m_uiEncryptionMethod) {

		case SSH_CIPHER_NONE:

			break;

		case SSH_CIPHER_IDEA:
			// IDEA in CFB mode - use first 16 (128 bits) bytes of session key
			cryptCreateContext (&m_cryptcontextRecv, CRYPT_ALGO_IDEA, CRYPT_MODE_CFB);
			cryptLoadKey(m_cryptcontextRecv, m_pucSessionKey, 16);
			cryptLoadIV(m_cryptcontextRecv, (void *) m_pucInitVectorRecv, 8);

			cryptCreateContext (&m_cryptcontextSend, CRYPT_ALGO_IDEA, CRYPT_MODE_CFB);
			cryptLoadKey(m_cryptcontextSend, m_pucSessionKey, 16);
			cryptLoadIV(m_cryptcontextSend, (void *) m_pucInitVectorSend, 8);

#ifdef DEBUG_SSH
			Debug("CSsh::SetSessionContexts() - Session encryption using IDEA.");
#endif
			break;

		case SSH_CIPHER_DES:
			// DES in CBC mode - use first 8 bytes (64 bits) as session key.
			cryptCreateContext (&m_cryptcontextRecv, CRYPT_ALGO_DES, CRYPT_MODE_CBC);
			cryptLoadKey(m_cryptcontextRecv, m_pucSessionKey, 8);
			cryptLoadIV(m_cryptcontextRecv, (void *) m_pucInitVectorRecv, 8);

			cryptCreateContext (&m_cryptcontextSend, CRYPT_ALGO_DES, CRYPT_MODE_CBC);
			cryptLoadKey(m_cryptcontextSend, m_pucSessionKey, 8);
			cryptLoadIV(m_cryptcontextSend, (void *) m_pucInitVectorSend, 8);

#ifdef DEBUG_SSH
			Debug("CSsh::SetSessionContexts() - Session encryption using DES.");
#endif

			break;

		case SSH_CIPHER_3DES:
			// 3DES - use first 24 bytes, 8 bytes / key,
			// Note that the first key and third key are identical.			

//			memcpy (m_pucSessionKey + 16, m_pucSessionKey, 8);
			
			cryptCreateContext (&m_cryptcontextRecv, CRYPT_ALGO_3DES, CRYPT_MODE_CBC);
			cryptLoadKey(m_cryptcontextRecv, m_pucSessionKey, 24);
			cryptLoadIV(m_cryptcontextRecv, (void *) m_pucInitVectorRecv, 8);

			cryptCreateContext (&m_cryptcontextSend, CRYPT_ALGO_3DES, CRYPT_MODE_CBC);
			cryptLoadKey(m_cryptcontextSend, m_pucSessionKey, 24);
			cryptLoadIV(m_cryptcontextSend, (void *) m_pucInitVectorSend, 8);

#ifdef DEBUG_SSH
			Debug("CSsh::SetSessionContexts() - Session encryption using 3DES.");
#endif
			break;

		case SSH_CIPHER_TSS:

			break;

		case SSH_CIPHER_ARCFOUR:

			break;

		case SSH_CIPHER_BLOWFISH:
			// BLOWFISH in CBC mode, use 128 bits (16bytes) of Session key data.
			cryptCreateContext (&m_cryptcontextRecv, CRYPT_ALGO_BLOWFISH, CRYPT_MODE_CBC);
			cryptLoadKey(m_cryptcontextRecv, m_pucSessionKey, 16);
			cryptLoadIV(m_cryptcontextRecv, (void *) m_pucInitVectorRecv, 8);
			
			cryptCreateContext (&m_cryptcontextSend, CRYPT_ALGO_BLOWFISH, CRYPT_MODE_CBC);
			cryptLoadKey(m_cryptcontextSend, m_pucSessionKey, 16);
			cryptLoadIV(m_cryptcontextSend, (void *) m_pucInitVectorSend, 8);

#ifdef DEBUG_SSH
			Debug("CSsh::SetSessionContexts() - Session encryption using Blowfish.");
#endif

			break;
	}

#endif // end USE_CRYPTLIB

#ifdef USE_SSLEAY
	int iTemp1, iTemp2, iTemp3;

	switch (m_uiEncryptionMethod) {

		case SSH_CIPHER_3DES:
			iTemp1 = des_set_key((des_cblock *)m_pucSessionKey, deskeyscheduleOne);
			iTemp2 = des_set_key((des_cblock *)m_pucSessionKey + 8, deskeyscheduleTwo);
			iTemp3 = des_set_key((des_cblock *)m_pucSessionKey + 16, deskeyscheduleThree);
			memset(m_descblockInitVector, 0, 8);
#ifdef DEBUG_SSH
			Debug("CSsh::SetSessionContexts() - Session encryption using SSLEAY 3DES %d, %d, %d.",
				iTemp1, iTemp2, iTemp3);
#endif

			break;
	}

#endif // end USE_SSLEAY

	return;

} // end CSsh::SetSessionContexts()

//-----------------//
// Packet Crackers //
//-----------------//

int CSsh::DecryptData(unsigned char * pszCipherText) {

	int iPadding;
	int iPacketLength;
	int iEncryptedLength;
	iPacketLength = iGetPacketLength(pszCipherText);
	iPadding = 8 - (iPacketLength % 8);

	iEncryptedLength = iPadding + iPacketLength;

	// Encrypted portion does not include length portion.
	m_iRecvBufOffset = 4;

#ifdef USE_CRYPTLIB

	switch (m_uiEncryptionMethod) {

		case SSH_CIPHER_NONE:

			break;

		case SSH_CIPHER_IDEA:
			// IDEA in CFB mode - use first 16 (128 bits) bytes of session key
			cryptDecrypt(m_cryptcontextRecv, pszCipherText + m_iRecvBufOffset, iEncryptedLength);
			break;

		case SSH_CIPHER_DES:
			// DES in CBC mode - use first 8 bytes (64 bits) as session key.
			cryptDecrypt(m_cryptcontextRecv, pszCipherText + m_iRecvBufOffset, iEncryptedLength);
			break;

		case SSH_CIPHER_3DES:
			// 3DES - use first 24 bytes, 8 bytes / key
			// Decrypt with the 3rd key, Encrypt with the 2nd, and Decrypt with the First.
			cryptDecrypt(m_cryptcontextRecv, pszCipherText + m_iRecvBufOffset, iEncryptedLength);

			break;

		case SSH_CIPHER_TSS:

			break;

		case SSH_CIPHER_ARCFOUR:

			break;

		case SSH_CIPHER_BLOWFISH:
			// BLOWFISH in CBC mode, use 128 bits (16bytes) of Session key data.
			cryptDecrypt(m_cryptcontextRecv, pszCipherText + m_iRecvBufOffset, iEncryptedLength);
			break;

		default:
			// TODO: Error handling

			break;
	}

#endif

#ifdef USE_SSLEAY
	
	unsigned char * pucTemp = new unsigned char[iEncryptedLength];

	switch (m_uiEncryptionMethod) {

		//TODO: Correct this.
		case SSH_CIPHER_BLOWFISH:
			des_ede3_cbc_encrypt((C_Block *)pszCipherText + m_iRecvBufOffset, 
				(C_Block *) pucTemp, iEncryptedLength, 
				deskeyscheduleOne, deskeyscheduleTwo, deskeyscheduleThree,
				(C_Block *)m_descblockInitVector, DES_DECRYPT);

#ifdef DEBUG_SSH
			Debug("CSsh::DecryptData() - Decrypt using SSLEAY des_ede3_cbc_encrypt()");
			DebugPlain("Decrypting using SSLEAY des_ede3_cbc_encrypt()");
			DebugPacket(pucTemp, iEncryptedLength);
#endif
			memset (m_descblockInitVector, 0, 8);
			des_ede2_cbc_encrypt((C_Block *)pszCipherText + m_iRecvBufOffset, 
				(C_Block *) pucTemp, iEncryptedLength, 
				deskeyscheduleOne, deskeyscheduleTwo,
				(C_Block *)m_descblockInitVector, DES_DECRYPT);
#ifdef DEBUG_SSH
			Debug("CSsh::DecryptData() - Decrypt using SSLEAY des_ede2_cbc_encrypt()");
			DebugPlain("Decrypting using SSLEAY des_ede2_cbc_encrypt()");
			DebugPacket(pucTemp, iEncryptedLength);
#endif

			break;
	} // end switch(m_uiEncryptionMethod);

	delete pucTemp;

#endif // end USE_SSLEAY
	
	
	return 1;

} // end CSsh::DecryptPacket()

unsigned int CSsh::iGetWord (unsigned char * szPacketSegment) {
	unsigned char szWord[4];
	unsigned int iWordValue;

	memcpy (szWord, szPacketSegment, 4);
	iWordValue = uiGet32Bit(szWord);
	m_iRecvBufOffset += 4;

	return iWordValue;
} // end CSsh::iGetWord()

unsigned int CSsh::iGetNibble (unsigned char * szPacketSegment) {

	unsigned char szNibble[2];
	unsigned int iNibbleValue;

	memcpy (szNibble, szPacketSegment, 2);
	iNibbleValue = uiGet16Bit(szNibble);
	m_iRecvBufOffset += 2;

	return iNibbleValue;
}

unsigned char * CSsh::pszGetMpInt(unsigned char * szPacketSegment, int *iMpBitSize) {

	unsigned char szNibble[2];
	int iMpByteSize;
	unsigned char * pszMp;
	memcpy (szNibble, szPacketSegment, 2);
	*iMpBitSize=uiGet16Bit(szNibble);

	iMpByteSize = (*iMpBitSize + 7) / 8;
	
	if ((pszMp = new unsigned char[iMpByteSize]) == NULL) {
		// TODO: Do some fatal error handling here rather than just returning NULL
		return NULL;
	}

	memcpy (pszMp, szPacketSegment + 2, iMpByteSize);
	m_iRecvBufOffset += iMpByteSize + 2;

	return pszMp;
} // end pszGetMpInt

//-----------------//
// Packet Creators //
//-----------------//

unsigned char CSsh::cGetRandomChar() {

	unsigned char cRandom;

#ifdef USE_CRYPTLIB
		cryptGetRandom ((void *) &cRandom, 1);
		while (cRandom == '\x00') 
			cryptGetRandom ((void *) &cRandom, 1);
#endif

	return cRandom;
} // end cGetRandomChar()

void CSsh::CreateRsaMpInt(unsigned char *pszInt, int iIntLength, unsigned char *pszData, int iDataLength) {

	// Fill first two bytes according to PKCS#1 Specification
	pszInt[0] = '\x00';
	pszInt[1] = '\x02';

	// Put Data in Least Significant bytes.
	memcpy (pszInt + iIntLength - iDataLength, pszData, iDataLength);

	// Precede the Data with a 0.
	pszInt[iIntLength - iDataLength - 1] = '\x00';
	
	// Fill in the rest of the space with random data.
	for (int iCount = 2; iCount < (iIntLength - iDataLength - 1); iCount++) {
		pszInt[iCount] = cGetRandomChar();
	} // end for

} // end CSsh::CreateRsaMpInt()

void CSsh::StartPacket(int iMessage) {

#ifdef DEBUG_SSH
	// Clear out memory if we're debugging, otherwise it's ineffecient..
	memset (m_pucSendBuf, 0, m_iSendBufOffset);
#endif

	// Start iPacketBufferLength of 12 (MAX PADDING + Length).  Thus we'll send an
	// offset when we send the packet.  This avoids the need for a Temp array
	// m_iPacketBufferLength Points to first empty space in array.
	m_iSendBufOffset = 12;

	m_pucSendBuf[m_iSendBufOffset++] = (unsigned char) iMessage;

}

void CSsh::AddWord(unsigned int iWord) {

	Put32Bit(m_pucSendBuf + m_iSendBufOffset, iWord);
	m_iSendBufOffset += 4;

}

void CSsh::AddNibble(unsigned int iNibble) {

	Put16Bit(m_pucSendBuf + m_iSendBufOffset, iNibble);
	m_iSendBufOffset += 2;

}

void CSsh::AddByte(unsigned int iByte) {
	
	unsigned char cTemp = iByte;
	
	memcpy(m_pucSendBuf + m_iSendBufOffset, &cTemp, 1);
	m_iSendBufOffset++;


} // end CSsh::AddByte()

void CSsh::AddMpInt(unsigned char *pszMpInt, int iMpByteSize) {

	// We'll do a hack of a bignum routine to figure out bitsize.
	// Bitsize = (Bytesize - 1) * 8 + iBitsleft where iBitsleft is the number
	// of time you shift the msb right (>>) till the byte equals zero.

	unsigned char cTemp;
	int iBitsLeft=0;
	int iTotalBits;

	cTemp = pszMpInt[0];
	while (cTemp != 0) {
		iBitsLeft++;
		cTemp = cTemp >> 1;
	}
	iTotalBits = (iMpByteSize - 1) * 8 + iBitsLeft;

	AddNibble(iTotalBits);
	memcpy (m_pucSendBuf + m_iSendBufOffset, pszMpInt, iMpByteSize);

	m_iSendBufOffset += iMpByteSize;

}

void CSsh::EncryptPacket() {

	int iPadding;
	int iPacketLength;
	int iEncryptedLength;

	iPacketLength = iGetPacketLength(m_pucSendBuf);
	iPadding = 8 - (iPacketLength % 8);

	iEncryptedLength = iPadding + iPacketLength;

#ifdef DEBUG_SSH
		Debug("CSsh::EncryptPacket() - Entering Function");
		Debug("CSsh::EncryptPacket() - PacketLength = %d", iPacketLength);
#endif
		

#ifdef USE_CRYPTLIB

	switch (m_uiEncryptionMethod) {

		case SSH_CIPHER_NONE:

			break;

		case SSH_CIPHER_IDEA:
			// IDEA in CFB mode - use first 16 (128 bits) bytes of session key
			cryptEncrypt(m_cryptcontextSend, m_pucSendBuf + 4, iEncryptedLength);

			break;

		case SSH_CIPHER_DES:
			// DES in CBC mode - use first 8 bytes (64 bits) as session key.
			cryptEncrypt(m_cryptcontextSend, m_pucSendBuf + 4, iEncryptedLength);
			break;

		case SSH_CIPHER_3DES:
			// 3DES - use first 24 bytes, 8 bytes / key
			// Decrypt with the 3rd key, Encrypt with the 2nd, and Decrypt with the First.
				cryptEncrypt(m_cryptcontextSend, m_pucSendBuf + 4, iEncryptedLength);

			break;

		case SSH_CIPHER_TSS:

			break;

		case SSH_CIPHER_ARCFOUR:

			break;

		case SSH_CIPHER_BLOWFISH:
			// BLOWFISH in CBC mode, use 128 bits (16bytes) of Session key data.
			cryptEncrypt(m_cryptcontextSend, m_pucSendBuf + 4, iEncryptedLength);
			break;

		default:
			// TODO: Error handling

			break;
	}

#endif // USE_CRYPTLIB

#ifdef USE_SSLEAY

	unsigned char * pucTemp = new unsigned char[iEncryptedLength];

	switch (m_uiEncryptionMethod) {

		//TODO: Correct this
		case SSH_CIPHER_BLOWFISH:
			des_ede3_cbc_encrypt((C_Block *)m_pucSendBuf + 4, 
				(C_Block *)pucTemp, iEncryptedLength, 
				deskeyscheduleOne, deskeyscheduleTwo, deskeyscheduleThree,
				(C_Block *)m_descblockInitVector, DES_ENCRYPT);
#ifdef DEBUG_SSH
			Debug("CSsh::EncryptData() - Encrypt using SSLEAY");
			DebugPlain("Encrypting using SSLEAY...");
			DebugPacket(pucTemp, iEncryptedLength);
#endif
			break;
	}
	delete pucTemp;

#endif


} // end CSsh::EncryptPacket()

void CSsh::FinishPacket() {
	
	int iPacketLength;
	int iPacketBufferPadding;
	unsigned long ulCrcCheck;

	iPacketLength = m_iSendBufOffset - 8; // current offset - 12 + 4 (CRC bytes)
	iPacketBufferPadding = 8 - (iPacketLength % 8);

	// Add the padding bytes
	for (int iCount = 12 - iPacketBufferPadding; iCount < 12; iCount++) {
		if ((m_iStartEncryptionFlag) && (m_uiEncryptionMethod > 0))
			m_pucSendBuf[iCount] = cGetRandomChar();
		else
			m_pucSendBuf[iCount] = 0;
	}
	
	// Add the length to front of packet
	Put32Bit (m_pucSendBuf + 12 - iPacketBufferPadding - 4, iPacketLength);

	// Move the packet into m_pucSendBuf[0] again.
	memmove(m_pucSendBuf, m_pucSendBuf + 8 - iPacketBufferPadding, 
		iPacketLength + 4 + iPacketBufferPadding);
	m_iSendBufOffset = iPacketLength + 4 + iPacketBufferPadding;

	// Calculate and Add CRC Check Bytes
	ulCrcCheck = iGetCrc32((unsigned char *) m_pucSendBuf + 4, 
		m_iSendBufOffset - 8);
	Put32Bit(m_pucSendBuf + iPacketLength + iPacketBufferPadding, ulCrcCheck);

#ifdef DEBUG_SSH
	// HexDump Network Content 
	DebugPlain("Sending SSH Packet - PLAINTEXT:");
	DebugPacket((unsigned char *)(LPCSTR)m_pucSendBuf, m_iSendBufOffset);
#endif 

	if (m_iStartEncryptionFlag) 
		EncryptPacket();

#ifdef DEBUG_SSH
	// HexDump Network Content 
	DebugPlain("Sending SSH Packet - ENCRYPTED:");
	DebugPacket((unsigned char *)(LPCSTR)m_pucSendBuf, m_iSendBufOffset);
#endif 


} // end CSsh::FinishPacket()

