/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
////////////////////////////////////////////////////////////////////////////////
#Title: xbNetNTLM
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<12/02/2014: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/
#include "stdafx.h"

#include "xbNetNTLM.h"
#include "xbData.h"
#include "xbVariant.h"
#include "xbDnItf.h"
#include <time.h>

//#define _xbnetNTLM_verbose
//#define _xbnetNTLM_desVerbose
//#define _xbnetNTLM_clientAs98
#ifdef _DEBUG
//#	define _xbnetNTLM2_support
#	define _xbnetNTLM_verbose
#endif

#pragma pack( push )
#pragma pack( 1 )

////////////////////////////////////////////////////////////////////////////////////
//
//
// IMPLEMENTATIONS
//
//
////////////////////////////////////////////////////////////////////////////////////
//Envoi d'un challenge : WWW-Authenticate xbnetNTLM_AUTH_NAME chalenge (returned by xbnetNTLM_serverNewChallenge
//orig code: send_ntlm_challenge
void xbnetNTLM_serverNewChallenge(LPCSTR pDom,bool bDomIsHost,xbnetNTLM_sspInfo *ntlmssp,const xbnetNTLM_Nonce &nonce,xbString *pCh,bool bNtlm2)
{
	if (!nonce.m_bInit)
		xbEapp::Throw("Unexpected NTLM authentication","xbnetNTLM_serverNewChallenge : info not initialized, v2="+xbString(bNtlm2));
	
	if (!xbnetNTLMSSP_NEGOTIATE_OK(ntlmssp->m_flags))
		xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_serverNewChallenge : bad flags (flags="+xbString(ntlmssp->m_flags,xbStringNum_hexa)+"), v2="+xbString(bNtlm2));
	
	xbTdata<xbnetNTLM_msgChallenge>	chlg;
	if (ntlmssp->m_flags & xbnetNTLMSSP_NEGOTIATE_UNICODE)
	{
		chlg.Length = (int)(sizeof(xbnetNTLM_msgChallenge) + (pDom?strlen(pDom)*2:0));
		memset(chlg,0,chlg.Length);
		chlg->flags = xbnetNTLMSSP_NEGOTIATE_UNICODE;
	}
	else
	{
		chlg.Length = (int)(sizeof(xbnetNTLM_msgChallenge) + (pDom?strlen(pDom):0));
		memset(chlg,0,chlg.Length);
		chlg->flags = xbnetNTLMSSP_NEGOTIATE_OEM;
	}

#ifdef _xbnetNTLM2_support
	if ( /*bNtlm2 || */ (ntlmssp->m_flags & xbnetNTLMSSP_NEGOTIATE_NTLM2))
	{
		//todo: 128/64 bits mode support
		chlg->flags |= xbnetNTLMSSP_NEGOTIATE2_SERVERBASE;
	}
	else
#endif
	chlg->flags |= xbnetNTLMSSP_NEGOTIATE_SERVERBASE;

	if (pDom)
	{
		chlg->flags |= (bDomIsHost?NTLMSSP_CHALLENGE_TARGET_IS_SERVER:NTLMSSP_CHALLENGE_TARGET_IS_DOMAIN)
										  | xbnetNTLMSSP_REQUEST_TARGET;
		
		ntlmssp->m_domain = pDom;
		
		int		offs = sizeof(xbnetNTLM_msgChallenge);
		uchar	*ptr = (uchar*)(const void*)chlg + offs;
		if (ntlmssp->m_flags & xbnetNTLMSSP_NEGOTIATE_UNICODE)
		{
			int lg = ntlmssp->m_domain.Length*2;
			chlg->target_len = chlg->target_lenmax = lg;
			chlg->target_off = offs;
			xbBSTR bstrDom(ntlmssp->m_domain);
			memcpy(ptr,(LPCWSTR)bstrDom,lg);
			offs += lg;
			ptr += lg;
		}
		else
		{
			int lg = ntlmssp->m_domain.Length;
			chlg->target_len = chlg->target_lenmax = lg;
			chlg->target_off = offs;
			memcpy(ptr,(LPCSTR)ntlmssp->m_domain,lg);
			offs += lg;
			ptr += lg;
		}
	}
	
	strcpy(chlg->protocol, xbnetNTLMSSP_SIGNATURE);
	chlg->type = 0x02;
	memcpy(chlg->nonce, nonce.key, sizeof(chlg->nonce));
	
	*pCh = ::xbBase64encode(chlg.Begin(),chlg.Length);
}

//xbnetNTLM_serverAuthenticate: Return false if new challenge requested, true if ok, exceptions on errors
void xbnetNTLM_serverDecodeMsg(const xbString &msg,xbnetNTLM_sspInfo *ntlmssp,bool bNtlm2);
bool xbnetNTLM_serverAuthenticate(const xbString &authResponse,
								xbnetNTLM_sspInfo *ntlmssp,bool bNewChallengeAllowed,bool bNtlm2)
{
	xbString msg(::xbBase64decode(authResponse));
	
	xbnetNTLM_serverDecodeMsg(msg,ntlmssp,bNtlm2);
	
	xbDbgOutput("xbnetNTLM_serverAuthenticate : usr="+ntlmssp->m_user+", v2="+xbString(bNtlm2));
	switch (ntlmssp->m_msgType) 
	{
	case 1:
		if (!bNewChallengeAllowed)
										  xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_serverAuthenticate : new challenge requested not supported here (type="+xbString(ntlmssp->m_msgType)+"), v2="+xbString(bNtlm2));
		return false;
	case 3:
		break;
	default:
		xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_serverAuthenticate : bad request (type="+xbString(ntlmssp->m_msgType)+"), v2="+xbString(bNtlm2));
	}
	return true;
}

void xbnetNTLM_clientNewChallenge(const xbnetNTLM_sspInfo *ntlmssp,xbString *pCh,bool bNtlm2)
{
	xbTdata<xbnetNTLM_msgNego>	nego;
	if (xbnetNTLMSSP_NEGOTIATE_CLIENTDFLT & xbnetNTLMSSP_NEGOTIATE_UNICODE)
	{
		nego.Length = sizeof(xbnetNTLM_msgNego) + ntlmssp->m_domain.Length 
										  + ntlmssp->m_host.Length;
		memset(nego,0,nego.Length);
	}
	else
	{
		xbAssert(sizeof(xbnetNTLM_msgNego) == 32);
		nego.Length = sizeof(xbnetNTLM_msgNego)+16;
		memset(nego,0,nego.Length);
		uchar *ptr = (uchar*)(xbnetNTLM_msgNego*)nego + nego.Length;
		ptr[-4] = 0x30;
		ptr[-12] = 0x30;
	}
#ifdef _xbnetNTLM2_support
	if (bNtlm2)
		nego->flags = xbnetNTLMSSP_NEGOTIATE2_CLIENTDFLT;
	else
#endif
	nego->flags = xbnetNTLMSSP_NEGOTIATE_CLIENTDFLT;

	int		offs = sizeof(xbnetNTLM_msgNego);
	uchar	*ptr = (uchar*)(const void*)nego + offs;
	if (xbnetNTLMSSP_NEGOTIATE_CLIENTDFLT & xbnetNTLMSSP_NEGOTIATE_UNICODE)
	{
		nego->flags |= xbnetNTLMSSP_OEM_DOMAIN_SUPPLIED | xbnetNTLMSSP_OEM_HOST_SUPPLIED;
		if (ntlmssp->m_domain.IsValid())
		{
			int lg = ntlmssp->m_domain.Length;
			nego->dom_len = nego->dom_lenmax = lg;
			nego->dom_off = offs;
			memcpy(ptr,(LPCSTR)ntlmssp->m_domain,lg);
			offs += lg;
			ptr += lg;
		}
		if (ntlmssp->m_host.IsValid())
		{
			int lg = ntlmssp->m_host.Length;
			nego->host_len = nego->host_lenmax = lg;
			nego->host_off = offs;
			memcpy(ptr,(LPCSTR)ntlmssp->m_host,lg);
			offs += lg;
			ptr += lg;
		}
	}
	strcpy(nego->protocol, xbnetNTLMSSP_SIGNATURE);
	nego->type = 0x01;
	
	*pCh = ::xbBase64encode(nego.Begin(), nego.Length);
}

void xbnetNTLM_clientDecodeMsg(const xbString &msg,xbnetNTLM_sspInfo *ntlmssp,
							 xbnetNTLM_Nonce *nonce,bool bNtlm2);
void xbnetNTLM_clientGetChallenge(const xbString &authResponse,xbnetNTLM_sspInfo *ntlmssp,
								xbnetNTLM_Nonce *nonce,bool bNtlm2)
{
	xbString msg(::xbBase64decode(authResponse));
	
	xbnetNTLM_clientDecodeMsg(msg,ntlmssp,nonce,bNtlm2);
}

void xbnetNTLM_clientAuthenticate(xbnetNTLM_sspInfo *ntlmssp,const xbString &passw,
								const xbnetNTLM_Nonce &nonce,xbString *pAuth,bool bNtlm2,
								xbnetNTLM_NonceServer *pNonceSrv)
{
	
	if (!xbnetNTLMSSP_NEGOTIATE_OK(ntlmssp->m_flags))
		xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_clientAuthenticate() : bad flags (flags="+xbString(ntlmssp->m_flags,xbStringNum_hexa)+"), v2="+xbString(bNtlm2));
	
#ifdef _xbnetNTLM2_support
	if (ntlmssp->m_flags & xbnetNTLMSSP_NEGOTIATE_NTLM2)
	{
		xbnetNTLM_Nonce	clinonce;
		pNonceSrv->NewAuth(&clinonce);

		ntlmssp->m_lm = xbString((LPCSTR)clinonce.key,8);
		ntlmssp->m_lm.Pad(24,'\0');

		xbnetNTLM2_makeNThashPW(nonce,passw,ntlmssp->m_lm,&ntlmssp->m_nt);
	}
	else
#endif
	{
		if (ntlmssp->m_flags & xbnetNTLMSSP_NEGOTIATE_UNICODE)
			xbnetNTLM_makeLMandNThashPW(nonce,passw,&ntlmssp->m_lm,&ntlmssp->m_nt);
		else
		{
			xbnetNTLM_makeLMandNThashPW(nonce,passw,&ntlmssp->m_lm,NULL);
			ntlmssp->m_nt.Empty();
		}
	}
	
	xbTdata<xbnetNTLM_msgAuthenticate>	auth;
	if (ntlmssp->m_flags & xbnetNTLMSSP_NEGOTIATE_UNICODE)
	{
		auth.Length = sizeof(xbnetNTLM_msgAuthenticate) 
			  + ntlmssp->m_domain.Length*2 
			  + ntlmssp->m_host.Length*2 
			  + ntlmssp->m_user.Length*2
			  + ntlmssp->m_lm.Length
			  + ntlmssp->m_nt.Length
			  ;
		memset(auth,0,auth.Length);
		auth->flags = xbnetNTLMSSP_NEGOTIATE_UNICODE;
	}
	else
	{
		auth.Length = sizeof(xbnetNTLM_msgAuthenticate) 
			  + ntlmssp->m_domain.Length 
			  + ntlmssp->m_host.Length
			  + ntlmssp->m_user.Length
			  + ntlmssp->m_lm.Length
			  + ntlmssp->m_nt.Length
			  ;
		memset(auth,0,auth.Length);
		auth->flags = xbnetNTLMSSP_NEGOTIATE_OEM;
	}
#ifdef _xbnetNTLM2_support
	if (ntlmssp->m_flags & xbnetNTLMSSP_NEGOTIATE_NTLM2)
		auth->flags |= xbnetNTLMSSP_NEGOTIATE2_CLIENTBASE;
	else
#endif
		auth->flags |= xbnetNTLMSSP_NEGOTIATE_CLIENTBASE;
	int		offs = sizeof(xbnetNTLM_msgAuthenticate);
	uchar	*ptr = (uchar*)(const void*)auth + offs;
	if (ntlmssp->m_flags & xbnetNTLMSSP_NEGOTIATE_UNICODE)
	{
		if (ntlmssp->m_domain.IsValid())
		{
			  int lg = ntlmssp->m_domain.Length*2;
			  auth->dom_len = auth->dom_lenmax = lg;
			  auth->dom_off = offs;
			  xbBSTR bstrDom(ntlmssp->m_domain);
			  memcpy(ptr,(LPCWSTR)bstrDom,lg);
			  offs += lg;
			  ptr += lg;
		}
		if (ntlmssp->m_user.IsValid())
		{
			  int lg = ntlmssp->m_user.Length * 2;
			  auth->user_len = auth->user_lenmax = lg;
			  auth->user_off = offs;
			  xbBSTR bstrUser(ntlmssp->m_user);
			  memcpy(ptr,(LPCWSTR)bstrUser,lg);
			  offs += lg;
			  ptr += lg;
		}
		if (ntlmssp->m_host.IsValid())
		{
			int lg = ntlmssp->m_host.Length * 2;
			auth->host_len = auth->host_lenmax = lg;
			auth->host_off = offs;
			xbBSTR bstrHost(ntlmssp->m_host);
			memcpy(ptr,(LPCWSTR)bstrHost,lg);
			offs += lg;
			ptr += lg;
		}
	}
	else
	{
		if (ntlmssp->m_domain.IsValid())
		{
			int lg = ntlmssp->m_domain.Length;
			auth->dom_len = auth->dom_lenmax = lg;
			auth->dom_off = offs;
			memcpy(ptr,(LPCSTR)ntlmssp->m_domain,lg);
			offs += lg;
			ptr += lg;
		}
		if (ntlmssp->m_user.IsValid())
		{
			int lg = ntlmssp->m_user.Length;
			auth->user_len = auth->user_lenmax = lg;
			auth->user_off = offs;
			memcpy(ptr,(LPCSTR)ntlmssp->m_user,lg);
			offs += lg;
			ptr += lg;
		}
		if (ntlmssp->m_host.IsValid())
		{
			int lg = ntlmssp->m_host.Length;
			auth->host_len = auth->host_lenmax = lg;
			auth->host_off = offs;
			memcpy(ptr,(LPCSTR)ntlmssp->m_host,lg);
			offs += lg;
			ptr += lg;
		}
	}
	if (ntlmssp->m_lm.IsValid())
	{
		int lg = ntlmssp->m_lm.Length;
		auth->lm_len = auth->lm_lenmax = lg;
		auth->lm_off = offs;
		memcpy(ptr,(LPCSTR)ntlmssp->m_lm,lg);
		offs += lg;
		ptr += lg;
	}
	if (ntlmssp->m_nt.IsValid())
	{
		int lg = ntlmssp->m_nt.Length;
		auth->nt_len = auth->nt_lenmax = lg;
		memcpy(ptr,(LPCSTR)ntlmssp->m_nt,lg);
		auth->nt_off = offs;
		offs += lg;
		ptr += lg;
	}
	else
		auth->nt_off = offs;
	strcpy(auth->protocol, xbnetNTLMSSP_SIGNATURE);
	auth->type = 0x03;
	auth->msg_len = offs;
	
	*pAuth = ::xbBase64encode(auth.Begin(), auth.Length);
}

/*
* turns a 56 bit key into the 64 bit, odd parity key and sets the key.
* The key schedule ks is also set.
*/
void xbnetNTLM_setupDESkey(const uchar *key_56, xbArray<ulong> &arrkeys)
{
	uchar key[8];
    key[0] = key_56[0];
    key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1);
    key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2);
    key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3);
    key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4);
    key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5);
    key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6);
    key[7] =  (key_56[6] << 1) & 0xFF;
	
	xbnetDES_createKeys(xbString((LPCSTR)key,8),arrkeys);
}

/*
* takes a 21 byte array and treats it as 3 56-bit DES keys. The
* 8 byte nonce is encrypted with each key and the resulting 24
* bytes are stored in the results array.
*/
void xbnetNTLM_calcResp(const char *keys, const xbString &nonce, xbString *pwHash)
{
	xbArray<ulong>	arrkeys;
	
	xbString ret1,ret2,ret3;
	
    xbnetNTLM_setupDESkey((const uchar*)keys, arrkeys);
    xbnetDES(arrkeys, nonce,true,NULL,&ret1);
	
    xbnetNTLM_setupDESkey((const uchar*)keys+7, arrkeys);
    xbnetDES(arrkeys, nonce, true, NULL, &ret2);
	
    xbnetNTLM_setupDESkey((const uchar*)keys+14, arrkeys);
    xbnetDES(arrkeys, nonce, true, NULL, &ret3);
	*pwHash = ret1 + ret2 + ret3;
}

xbnetNTLMret xbnetNTLM_serverCheckLMandNTresponses(const xbnetNTLM_sspInfo &ntlmssp,const xbnetNTLM_Nonce &nonce,
		const xbString &passw, 
		bool bNtlm2,
		xbString *pLmCalc,xbString *pNtCalc)
{
	if (ntlmssp.m_lm.IsEmpty() && ntlmssp.m_nt.IsEmpty())
		return xbnetNTLM_error;
#ifdef _xbnetNTLM2_support
	if (ntlmssp.m_flags & xbnetNTLMSSP_NEGOTIATE_NTLM2)
	{
		xbString nt;
		xbnetNTLM2_makeNThashPW(nonce,passw,ntlmssp.m_lm,&nt);
		if (pNtCalc) *pNtCalc = nt;

		if (nt.Length != ntlmssp.m_nt.Length
			|| memcmp(nt,ntlmssp.m_nt,nt.Length) != 0)
			return xbnetNTLM_ntlm2error;

		return xbnetNTLM_ntlm2ok;
	}
#endif
	xbString lm,nt;
	xbnetNTLM_makeLMandNThashPW(nonce,passw,
		ntlmssp.m_lm.IsValid()?&lm:NULL,
		ntlmssp.m_nt.IsValid()?&nt:NULL);
	if (pNtCalc) *pNtCalc = nt;
	if (pLmCalc) *pLmCalc = lm;

	if (ntlmssp.m_lm.IsValid())
	{
		if (lm.Length != ntlmssp.m_lm.Length
			|| memcmp(lm,ntlmssp.m_lm,lm.Length) != 0)
			return xbnetNTLM_lmerror;
		
		if (ntlmssp.m_nt.IsEmpty())
			return xbnetNTLM_lmok;
	}

	if (ntlmssp.m_nt.IsValid())
	{
		if (nt.Length != ntlmssp.m_nt.Length
			|| memcmp(nt,ntlmssp.m_nt,nt.Length) != 0)
			return xbnetNTLM_nterror;

		if (ntlmssp.m_lm.IsEmpty())
			return xbnetNTLM_ntok;
	}

	return xbnetNTLM_ok;
}

void xbnetNTLM_makeLMandNThashPW(const xbnetNTLM_Nonce &nonce,const xbString &passw,xbString *lmHash,xbString *ntHash)
{
	if (!nonce.m_bInit)
		xbEapp::Throw("Unexpected NTLM authentication","xbnetNTLM_makeLMandNThashPW : info not initialized");
	
	xbString		lm_hpw;
	
	if (lmHash != NULL)
	{
		uchar  lm_pw[14];
		{
			xbString upperPassw(passw);
			upperPassw.Upper();

			int   len = upperPassw.Length;
			if (len > sizeof(lm_pw))  
				len = sizeof(lm_pw);

			memcpy(lm_pw,(LPCSTR)upperPassw, len);
			if (len < sizeof(lm_pw))
				memset(lm_pw+len,0,sizeof(lm_pw) - len);
		}
		
		
		/* create LanManager hashed password */
		uchar magic[] = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 };
		
		xbArray<ulong>	arrkeys;
		xbnetNTLM_setupDESkey(lm_pw, arrkeys);
		xbnetDES(arrkeys,xbString((LPCSTR)magic,8),true,NULL,&lm_hpw);
		{
			xbnetNTLM_setupDESkey(lm_pw+7, arrkeys);
			xbString		lm_hpw2;
			xbnetDES(arrkeys,xbString((LPCSTR)magic,8), true, NULL, &lm_hpw2);
			lm_hpw += lm_hpw2;
		}
		lm_hpw += xbStringPad(5,(char)0);
#ifdef _xbnetNTLM_desVerbose
		xbDataTrace("lm_hpw jsdes",xbData(lm_hpw,lm_hpw.Length));
#endif
	}
	
	
    /* create NT hashed password */
	xbString		nt_hpw;
	
	if (ntHash != NULL)
	{
		uchar buf_nt_hpw[21];
		
		xbBSTR bstr(passw);
		
		xbnetMD4_CTX context;
		xbnetMD4_init(&context);
		xbnetMD4_update(&context, (const uchar*)(const wchar*)bstr, 2*passw.Length);
		xbnetMD4_final(&context, buf_nt_hpw);
		
		memset(buf_nt_hpw+16, 0, 5);
		nt_hpw = xbString((LPCSTR)buf_nt_hpw,21);
	}
	
    /* create 24 bytes responses */
	xbString snonce(nonce.key,8);	
	if (lmHash != NULL)
		xbnetNTLM_calcResp(lm_hpw, snonce, lmHash);
	if (ntHash != NULL)
		xbnetNTLM_calcResp(nt_hpw, snonce, ntHash);
}

#ifdef _xbnetNTLM2_support
void xbnetNTLM2_makeNThashPW(const xbnetNTLM_Nonce &nonce,const xbString &passw,const xbString &lmResp,xbString *ntHash)
{
    /* create NT hashed password */
	xbString		nt_hpw;
	{
		uchar buf_nt_hpw[21];
		
		xbBSTR bstr(passw);
		
		xbnetMD4_CTX context;
		xbnetMD4_init(&context);
		xbnetMD4_update(&context, (const uchar*)(const wchar*)bstr, 2*passw.Length);
		xbnetMD4_final(&context, buf_nt_hpw);
		
		memset(buf_nt_hpw+16, 0, 5);
		nt_hpw = xbString((LPCSTR)buf_nt_hpw,21);
	}

	uchar srvcliNonce[16];
	{
		xbnetMD5_CTX context;
		xbnetMD5_init(&context);
		xbnetMD5_update(&context, (const uchar*)nonce.key, 8);
		xbnetMD5_update(&context, (const uchar*)(const char*)lmResp, 8);
		xbnetMD5_final(&context, srvcliNonce);
	}

	xbnetNTLM_calcResp(nt_hpw, xbString((LPCSTR)srvcliNonce,8), ntHash);
}
#endif

int xbnetNTLM_getMsgType(const xbString &msg,bool bNtlm2)
{
    const xbnetNTLM_msgNego *msg1 = (const xbnetNTLM_msgNego*)(LPCSTR)msg;
	
    if (msg.Length < 9) 
		xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_getMsgType : bad auth length (lg="+xbString(msg.Length)+"), v2="+xbString(bNtlm2));
    if (memcmp(msg1->protocol, xbnetNTLMSSP_SIGNATURE, 8) != 0)
		xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_getMsgType : bad auth protocol (="+xbString(msg1->protocol,7)+"), v2="+xbString(bNtlm2));
    return msg1->type;
}

bool xbnetNTLM_getString(xbString *pDest, 
					   const xbString &msg, const long &off, long len,xbOpMode opm)
{
	LPCSTR	pMsg = msg;
	int		lg = msg.Length;
	if ((const char*)&off < pMsg)
		xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_getString(msg lg="+xbString(lg)+") : bad member offset (2)");
	if (opm == xbOp_Optional)
	{
		if ((const char*)&off+sizeof(long) > pMsg + lg)
			return false;
		if (off == 0 || len == 0)
			return false;
	}
	else
	{
		if ((const char*)&off+sizeof(long) > pMsg + lg)
			xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_getString(msg lg="+xbString(lg)+") : bad member offset");
	}
	if (off < 9 || off >= lg)
		xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_getString(msg lg="+xbString(lg)+") : bad offset ="+xbString(off));
	if (len < 0 || off+len-1 > lg)
		xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_getString(msg lg="+xbString(lg)+") : bad length/offset ="+xbString(len)+"/"+xbString(off));
	*pDest = xbString(pMsg+off,len);
	return true;
}

bool xbnetNTLM_getUnicode(xbString *pDest, 
						const xbString &msg, const long &off, long len,xbOpMode opm)
{
	LPCSTR	pMsg = msg;
	int		lg = msg.Length;
	if ((const char*)&off < pMsg)
		xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_getUnicode(msg lg="+xbString(lg)+") : bad member offset (2)");
	if (opm == xbOp_Optional)
	{
		if ((const char*)&off+sizeof(long) > pMsg + lg)
			return false;
		if (off == 0 || len == 0)
			return false;
	}
	else
	{
		if ((const char*)&off+sizeof(long) > pMsg + lg)
			xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_getUnicode(msg lg="+xbString(lg)+") : bad member offset");
	}
	if (off < 12 || off >= lg)
		xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_getUnicode(msg lg="+xbString(lg)+") : bad offset ="+xbString(off));
	if (len <= 0 || off+len-1 > lg || (len%2) != 0)
		xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_getUnicode(msg lg="+xbString(lg)+") : bad length/offset ="+xbString(len)+"/"+xbString(off));
	*pDest = xbString((const wchar*)(pMsg+off),len/2);
	return true;
}


void xbnetNTLM_serverDecodeMsg(const xbString &msg,struct xbnetNTLM_sspInfo *info,bool bNtlm2)
{
    switch (info->m_msgType = xbnetNTLM_getMsgType(msg,bNtlm2)) 
	{
    case 1: 
		{
			const xbnetNTLM_msgNego *msg1 = (const xbnetNTLM_msgNego*)(LPCSTR)msg;
			if (!xbnetNTLMSSP_NEGOTIATE_OK(msg1->flags))
				xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_serverDecodeMsg(1) : bad flags (flags="+xbString(msg1->flags,xbStringNum_hexa)+"), v2="+xbString(bNtlm2));
			//Always in ascii also with xbnetNTLMSSP_NEGOTIATE_UNICODE flag set
			xbnetNTLM_getString(&info->m_host,msg,msg1->host_off,msg1->host_len,xbOp_Optional);
			xbnetNTLM_getString(&info->m_domain,msg,msg1->dom_off,msg1->dom_len,xbOp_Optional);
			info->m_flags = msg1->flags;
		}
		return;
    case 3: 
		{
			const xbnetNTLM_msgAuthenticate *msg3 = (const xbnetNTLM_msgAuthenticate*)(LPCSTR)msg;
			if (msg3->flags != 0)
				info->m_flags = msg3->flags;
			if (!xbnetNTLMSSP_NEGOTIATE_OK(info->m_flags))
				xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_serverDecodeMsg(3) : bad flags (flags="+xbString(info->m_flags,xbStringNum_hexa)+"), v2="+xbString(bNtlm2));
			if (msg3->lm_len > 0)
				xbnetNTLM_getString(&info->m_lm,msg,msg3->lm_off,msg3->lm_len,xbOp_Required);
			else 
				info->m_lm.Empty();
			if (msg3->nt_len > 0)
				xbnetNTLM_getString(&info->m_nt,msg,msg3->nt_off,msg3->nt_len,xbOp_Required);
			else 
				info->m_nt.Empty();
			if (info->m_flags & xbnetNTLMSSP_NEGOTIATE_UNICODE)
			{
				xbnetNTLM_getUnicode(&info->m_user,msg,msg3->user_off,msg3->user_len,xbOp_Required);
				xbnetNTLM_getUnicode(&info->m_host,msg,msg3->host_off,msg3->host_len,xbOp_Optional);
				xbnetNTLM_getUnicode(&info->m_domain,msg,msg3->dom_off,msg3->dom_len,xbOp_Optional);
			}
			else
			{
				xbnetNTLM_getString(&info->m_user,msg,msg3->user_off,msg3->user_len,xbOp_Required);
				xbnetNTLM_getString(&info->m_host,msg,msg3->host_off,msg3->host_len,xbOp_Optional);
				xbnetNTLM_getString(&info->m_domain,msg,msg3->dom_off,msg3->dom_len,xbOp_Optional);
			}
		}
		return;
    }
	xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_serverDecodeMsg : bad request (type="+xbString(info->m_msgType)+"), v2="+xbString(bNtlm2));
}

void xbnetNTLM_clientDecodeMsg(const xbString &msg,xbnetNTLM_sspInfo *info,
							 xbnetNTLM_Nonce *nonce,bool bNtlm2)
{
    switch (info->m_msgType = xbnetNTLM_getMsgType(msg,bNtlm2)) 
	{
    case 2: 
		{
			const xbnetNTLM_msgChallenge *msg2 = (const xbnetNTLM_msgChallenge*)(LPCSTR)msg;
			if (!xbnetNTLMSSP_NEGOTIATE_OK(msg2->flags))
				xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_clientDecodeMsg(2) : bad flags (flags="+xbString(msg2->flags,xbStringNum_hexa)+"), v2="+xbString(bNtlm2));
			info->m_flags = msg2->flags;
			memcpy(nonce->key,msg2->nonce,sizeof(nonce->key));
			nonce->m_bInit = true;
			if (info->m_flags & xbnetNTLMSSP_NEGOTIATE_UNICODE)
			{
				xbnetNTLM_getUnicode(&info->m_domain,msg,msg2->target_off,msg2->target_len,xbOp_Optional);
			}
			else
			{
				xbnetNTLM_getString(&info->m_domain,msg,msg2->target_off,msg2->target_len,xbOp_Optional);
			}
		}
		return;
    }
	xbEapp::Throw("Bad NTLM authentication message","xbnetNTLM_clientDecodeMsg : bad request (type="+xbString(info->m_msgType)+"), v2="+xbString(bNtlm2));
}

//Original code taken from Paul Tero, July 2001 / javascript
//http://www.shopable.co.uk/des.html
//
//des
//this takes the key, the message, and whether to encrypt or decrypt
/*
Usage
des (string key, string message, boolean encrypt, [integer mode, string iv])

  
The des function accepts an 8 character string as the key (this is 64 bits, 
but the algorithm only uses 56) for normal DES or a 24 character string 
for triple DES, a message string, a boolean to say whether the data 
should be encrypted or decrypted, an optional mode (0 for ECB and 1 for CBC, 
ECB is the default), and an optional 8 character string input vector 
(not used in ECB mode). It returns the cipher text as a string. 

Here are several examples showing how it can be used: 

//encrypt using single DES (with an 8 byte key) in CBC mode with the given input vector
des ("8bytekey", "This is the message.", 1, 1, "inputvec");
//encrypt using triple DES (with a 24 byte key) in ECB mode
des ("this is a 24 byte key !!", "This is the message.", 1);
//decrypt using single DES in ECB mode
des ("8bytekey", "2384lf&*£90LSdsf", 0); 
*/

void xbnetDES(const xbString &key, const xbString &_message, bool encrypt, LPCSTR iv, xbString *pRet) 
{
	xbArray<ulong> arrkeys;
	xbnetDES_createKeys(key,arrkeys);
	xbnetDES(arrkeys,_message,encrypt,iv,pRet);
}

void xbnetDES(const xbArray<ulong> &arrkeys, const xbString &_message, bool encrypt, LPCSTR iv, xbString *pRet) 
{
	//declaring this locally speeds things up a bit
	static const ulong spfunction1[] = {0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004};
	static const ulong spfunction2[] = {0x80108020,0x80008000,0x8000,0x108020,0x100000,0x20,0x80100020,0x80008020,0x80000020,0x80108020,0x80108000,0x80000000,0x80008000,0x100000,0x20,0x80100020,0x108000,0x100020,0x80008020,0,0x80000000,0x8000,0x108020,0x80100000,0x100020,0x80000020,0,0x108000,0x8020,0x80108000,0x80100000,0x8020,0,0x108020,0x80100020,0x100000,0x80008020,0x80100000,0x80108000,0x8000,0x80100000,0x80008000,0x20,0x80108020,0x108020,0x20,0x8000,0x80000000,0x8020,0x80108000,0x100000,0x80000020,0x100020,0x80008020,0x80000020,0x100020,0x108000,0,0x80008000,0x8020,0x80000000,0x80100020,0x80108020,0x108000};
	static const ulong spfunction3[] = {0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200};
	static const ulong spfunction4[] = {0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080};
	static const ulong spfunction5[] = {0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,0x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100};
	static const ulong spfunction6[] = {0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010};
	static const ulong spfunction7[] = {0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002};
	static const ulong spfunction8[] = {0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000};
	
	const ulong *keys = arrkeys.Begin();
	
	/*	var m=0, i, j, temp, temp2, right1, right2, left, right, looping;
	var cbcleft, cbcleft2, cbcright, cbcright2
	var endloop, loopinc;
	var len = message.length;
	var chunk = 0;*/
	
	//set up the loops for single and triple des
	int iterations = arrkeys.Length == 32 ? 3 : 9; //single or triple des
	static const ulong loopingSingleEncrypt[] = {0, 32, 2};
	static const ulong loopingSingleDecrypt[] = {30, -2, -2};
	static const ulong loopingTripleEncrypt[] = {0, 32, 2, 62, 30, -2, 64, 96, 2};
	static const ulong loopingTripleDecrypt[] = {94, 62, -2, 32, 64, 2, 30, -2, -2};
	
	const ulong *looping;
	if (iterations == 3) 
	{
		looping = encrypt ? loopingSingleEncrypt : loopingSingleDecrypt;
	}
	else 
	{
		looping = encrypt ? loopingTripleEncrypt : loopingTripleDecrypt;
	}
	
	int len = _message.Length;
	xbString message(_message + xbStringPad(8,(char)0)); //pad the message out with null bytes
	
	//store the result here
	pRet->Empty();
	
	ulong cbcleft,cbcright;
	if (iv) 
	{ //CBC mode
		int m = -1;
		cbcleft = (ulong(iv[++m]) << 24); 
		cbcleft |= (ulong(iv[++m]) << 16);
		cbcleft |= (ulong(iv[++m]) << 8); 
		cbcleft |= ulong(iv[++m]);
		cbcright = (ulong(iv[++m]) << 24);
		cbcright |= (ulong(iv[++m]) << 16);
		cbcright |= (ulong(iv[++m]) << 8);
		cbcright |= ulong(iv[++m]);
	}
	
	//loop through each 64 bit chunk of the message
	ulong left,right,temp;
	ulong cbcleft2,cbcright2;
	ulong endloop,loopinc,right1,right2;
	const uchar *pMsg = (const uchar*)(LPCSTR)message;
	const uchar *pMsgEnd = (const uchar*)(LPCSTR)message + len;
	while (pMsg < pMsgEnd) 
	{
		left = (*(pMsg++) << 24);
		left |= (*(pMsg++) << 16);
		left |= (*(pMsg++) << 8);
		left |= *(pMsg++);
		right = (*(pMsg++) << 24);
		right |= (*(pMsg++) << 16);
		right |= (*(pMsg++) << 8) ;
		right |= *(pMsg++);
		
		//for Cipher Block Chaining mode, xor the message with the previous result
		if (iv) 
		{
			if (encrypt) 
			{
				left ^= cbcleft; right ^= cbcright;
			} 
			else 
			{
				cbcleft2 = cbcleft; cbcright2 = cbcright; cbcleft = left; cbcright = right;
			}
		}
		
		//first each 64 but chunk of the message must be permuted according to IP
		temp = ((left >> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
		temp = ((left >> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
		temp = ((right >> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
		temp = ((right >> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
		temp = ((left >> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
		
		left = ((left << 1) | (left >> 31)); 
		right = ((right << 1) | (right >> 31)); 
		
		//do this either 1 or 3 times for each chunk of the message
		for (int j=0; j<iterations; j+=3) 
		{
			endloop = looping[j+1];
			loopinc = looping[j+2];
			//now go through and perform the encryption or decryption  
			for (ulong i=looping[j]; i!=endloop; i+=loopinc) 
			{ //for efficiency
				right1 = right ^ keys[i]; 
				right2 = ((right >> 4) | (right << 28)) ^ keys[i+1];
				//the result is attained by passing these bytes through the S selection functions
				temp = left;
				left = right;
				right = temp ^ (spfunction2[(right1 >> 24) & 0x3f] | spfunction4[(right1 >> 16) & 0x3f]
					| spfunction6[(right1 >>  8) & 0x3f] | spfunction8[right1 & 0x3f]
					| spfunction1[(right2 >> 24) & 0x3f] | spfunction3[(right2 >> 16) & 0x3f]
					| spfunction5[(right2 >>  8) & 0x3f] | spfunction7[right2 & 0x3f]);
			}
			temp = left; left = right; right = temp; //unreverse left and right
		} //for either 1 or 3 iterations
		
		//move then each one bit to the right
		left = ((left >> 1) | (left << 31)); 
		right = ((right >> 1) | (right << 31)); 
		
		//now perform IP-1, which is IP in the opposite direction
		temp = ((left >> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
		temp = ((right >> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
		temp = ((right >> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
		temp = ((left >> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
		temp = ((left >> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
		
		//for Cipher Block Chaining mode, xor the message with the previous result
		if (iv) 
		{
			if (encrypt) 
			{
				cbcleft = left; cbcright = right;
			} 
			else 
			{
				left ^= cbcleft2; right ^= cbcright2;
			}
		}
		uchar buf[] = {uchar(left>>24), uchar((left>>16) & 0xff), uchar((left>>8) & 0xff), uchar(left & 0xff), 
			uchar(right>>24), uchar((right>>16) & 0xff), uchar((right>>8) & 0xff), uchar(right & 0xff)};
		
		pRet->Add((LPCSTR)buf,8);
	} //for every 8 characters, or 64 bits in the message
	
} //end of des



//des_createKeys
//this takes as input a 64 bit key (even though only 56 bits are used)
//as an array of 2 integers, and returns 16 48 bit keys
void xbnetDES_createKeys(const xbString &strkey,xbArray<ulong> &arrkeys) 
{
	//declaring this locally speeds things up a bit
	static const ulong pc2bytes0[]  = {0,0x4,0x20000000,0x20000004,0x10000,0x10004,0x20010000,0x20010004,0x200,0x204,0x20000200,0x20000204,0x10200,0x10204,0x20010200,0x20010204};
	static const ulong pc2bytes1[]  = {0,0x1,0x100000,0x100001,0x4000000,0x4000001,0x4100000,0x4100001,0x100,0x101,0x100100,0x100101,0x4000100,0x4000101,0x4100100,0x4100101};
	static const ulong pc2bytes2[]  = {0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808,0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808};
	static const ulong pc2bytes3[]  = {0,0x200000,0x8000000,0x8200000,0x2000,0x202000,0x8002000,0x8202000,0x20000,0x220000,0x8020000,0x8220000,0x22000,0x222000,0x8022000,0x8222000};
	static const ulong pc2bytes4[]  = {0,0x40000,0x10,0x40010,0,0x40000,0x10,0x40010,0x1000,0x41000,0x1010,0x41010,0x1000,0x41000,0x1010,0x41010};
	static const ulong pc2bytes5[]  = {0,0x400,0x20,0x420,0,0x400,0x20,0x420,0x2000000,0x2000400,0x2000020,0x2000420,0x2000000,0x2000400,0x2000020,0x2000420};
	static const ulong pc2bytes6[]  = {0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002,0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002};
	static const ulong pc2bytes7[]  = {0,0x10000,0x800,0x10800,0x20000000,0x20010000,0x20000800,0x20010800,0x20000,0x30000,0x20800,0x30800,0x20020000,0x20030000,0x20020800,0x20030800};
	static const ulong pc2bytes8[]  = {0,0x40000,0,0x40000,0x2,0x40002,0x2,0x40002,0x2000000,0x2040000,0x2000000,0x2040000,0x2000002,0x2040002,0x2000002,0x2040002};
	static const ulong pc2bytes9[]  = {0,0x10000000,0x8,0x10000008,0,0x10000000,0x8,0x10000008,0x400,0x10000400,0x408,0x10000408,0x400,0x10000400,0x408,0x10000408};
	static const ulong pc2bytes10[] = {0,0x20,0,0x20,0x100000,0x100020,0x100000,0x100020,0x2000,0x2020,0x2000,0x2020,0x102000,0x102020,0x102000,0x102020};
	static const ulong pc2bytes11[] = {0,0x1000000,0x200,0x1000200,0x200000,0x1200000,0x200200,0x1200200,0x4000000,0x5000000,0x4000200,0x5000200,0x4200000,0x5200000,0x4200200,0x5200200};
	static const ulong pc2bytes12[] = {0,0x1000,0x8000000,0x8001000,0x80000,0x81000,0x8080000,0x8081000,0x10,0x1010,0x8000010,0x8001010,0x80010,0x81010,0x8080010,0x8081010};
	static const ulong pc2bytes13[] = {0,0x4,0x100,0x104,0,0x4,0x100,0x104,0x1,0x5,0x101,0x105,0x1,0x5,0x101,0x105};
	
	//how many iterations (1 for des, 3 for triple des)
	int iterations = strkey.Length >= 24 ? 3 : 1;
	//stores the return keys
	arrkeys.Length = 32 * iterations;
	ulong *keys = arrkeys.Begin();
	//now define the left shifts which need to be done
	static ulong shifts[] = {0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0};
	//other variables
	ulong lefttemp, righttemp, n=0, temp;
	const uchar *key = (const uchar*)(LPCSTR)strkey;
#ifdef _xbnetNTLM_desVerbose
	xbDataTrace("jsdes key",xbData(key,strkey.Length));
#endif
	if (strkey.Length < iterations * 8)
		xbThrow xbEsys("xbnetDES_createKeys : bad key length");
	ulong left,right;
	int m = -1;
	for (int j=0; j<iterations; j++) 
	{ //either 1 or 3 iterations
		left = (key[++m] << 24);
		left |= (key[++m] << 16);
		left |= (key[++m] << 8);
		left |= key[++m];
		right = (key[++m] << 24);
		right |= (key[++m] << 16);
		right |= (key[++m] << 8);
		right |= key[++m];
#ifdef _xbnetNTLM_desVerbose
		xbRelOutput("jsdes right0="+xbString(right,true)+", left0="+xbString(left,true));
#endif
		
		temp = ((left >> 4) ^ right) & 0x0f0f0f0f; 
		right ^= temp; 
		left ^= (temp << 4);
#ifdef _xbnetNTLM_desVerbose
		xbRelOutput("jsdes right0a="+xbString(right,true)+", left0a="+xbString(left,true));
#endif
		
		temp = ((right >> (32-16)) ^ left) & 0x0000ffff; 
		left ^= temp; 
		right ^= (temp << (32-16));
#ifdef _xbnetNTLM_desVerbose
		xbRelOutput("jsdes right0b="+xbString(right,true)+", left0b="+xbString(left,true));
#endif
		
		temp = ((left >> 2) ^ right) & 0x33333333; 
		right ^= temp; 
		left ^= (temp << 2);
#ifdef _xbnetNTLM_desVerbose
		xbRelOutput("jsdes right0c="+xbString(right,true)+", left0c="+xbString(left,true));
#endif
		
		temp = ((right >> (32-16)) ^ left) & 0x0000ffff; 
		left ^= temp; 
		right ^= (temp << (32-16));
#ifdef _xbnetNTLM_desVerbose
		xbRelOutput("jsdes right0d="+xbString(right,true)+", left0d="+xbString(left,true));
#endif
		
		temp = ((left >> 1) ^ right) & 0x55555555; 
		right ^= temp; 
		left ^= (temp << 1);
#ifdef _xbnetNTLM_desVerbose
		xbRelOutput("jsdes right0e="+xbString(right,true)+", left0e="+xbString(left,true));
#endif
		
		temp = ((right >> 8) ^ left) & 0x00ff00ff; 
		left ^= temp; 
		right ^= (temp << 8);
#ifdef _xbnetNTLM_desVerbose
		xbRelOutput("jsdes right0f="+xbString(right,true)+", left0f="+xbString(left,true));
#endif
		
		temp = ((left >> 1) ^ right) & 0x55555555; 
		right ^= temp; 
		left ^= (temp << 1);
		
		//the right side needs to be shifted and to get the last four bits of the left side
		temp = (left << 8) | ((right >> 20) & 0x000000f0);
		//left needs to be put upside down
		left = (right << 24) | ((right << 8) & 0xff0000) 
			| ((right >> 8) & 0xff00) | ((right >> 24) & 0xf0);
		right = temp;
#ifdef _xbnetNTLM_desVerbose
		xbRelOutput("jsdes right1="+xbString(right,true)+", left1="+xbString(left,true));
#endif
		
		//now go through and perform these shifts on the left and right keys
		for (int i=0; i < 16; i++) 
		{
			//shift the keys either one or two bits to the left
			if (shifts[i]) 
			{
				left = (left << 2) | (left >> 26); right = (right << 2) | (right >> 26);
			}
			else 
			{
				left = (left << 1) | (left >> 27); right = (right << 1) | (right >> 27);
			}
			left &= 0xfffffff0; right &= 0xfffffff0;
			
#ifdef _xbnetNTLM_desVerbose
			xbRelOutput("jsdes right2="+xbString(right,true)+", left2="+xbString(left,true));
#endif
			//now apply PC-2, in such a way that E is easier when encrypting or decrypting
			//this conversion will look like PC-2 except only the last 6 bits of each byte are used
			//rather than 48 consecutive bits and the order of lines will be according to 
			//how the S selection functions will be applied: S2, S4, S6, S8, S1, S3, S5, S7
			lefttemp = pc2bytes0[left >> 28] | pc2bytes1[(left >> 24) & 0xf]
				| pc2bytes2[(left >> 20) & 0xf] | pc2bytes3[(left >> 16) & 0xf]
				| pc2bytes4[(left >> 12) & 0xf] | pc2bytes5[(left >> 8) & 0xf]
				| pc2bytes6[(left >> 4) & 0xf];
			righttemp = pc2bytes7[right >> 28] | pc2bytes8[(right >> 24) & 0xf]
                | pc2bytes9[(right >> 20) & 0xf] | pc2bytes10[(right >> 16) & 0xf]
                | pc2bytes11[(right >> 12) & 0xf] | pc2bytes12[(right >> 8) & 0xf]
                | pc2bytes13[(right >> 4) & 0xf];
			temp = ((righttemp >> 16) ^ lefttemp) & 0x0000ffff; 
			keys[n++] = lefttemp ^ temp; keys[n++] = righttemp ^ (temp << 16);
		}
	} //for each iterations

#ifdef _xbnetNTLM_desVerbose
//		xbDataTrace("lm_hpw jsdes",xbData(lm_hpw,lm_hpw.Length));
	{
		xbString str;
		ulong *k = arrkeys.Begin();
		ulong *kend = arrkeys.End();
		while(k < kend)
		{
			str += " "+xbString(*(k++),true);
		}

		xbRelOutput("desjs keys :" + str);
	}
#endif
}

/********************************************************************************/
/* MD4 crypto. */

//Extract from rfc1320
/* Constants for xbnetMD4_transform routine.
*/
#define _xbnetMD4_S11 3
#define _xbnetMD4_S12 7
#define _xbnetMD4_S13 11
#define _xbnetMD4_S14 19
#define _xbnetMD4_S21 3
#define _xbnetMD4_S22 5
#define _xbnetMD4_S23 9
#define _xbnetMD4_S24 13
#define _xbnetMD4_S31 3
#define _xbnetMD4_S32 9
#define _xbnetMD4_S33 11
#define _xbnetMD4_S34 15

void xbnetMD4_transform (ulong state[4],const uchar block[64]);
void xbnetMD4_encode(uchar *output, const ulong *input, uint inputLen);
void xbnetMD4_decode(ulong *output, const uchar *input, uint inputLen);

static uchar _xbnetMD4_PADDING[64] = 
{
	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

/* _xbnetMD4_F, _xbnetMD4_G and _xbnetMD4_H are basic MD4 functions.
*/
#define _xbnetMD4_F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define _xbnetMD4_G(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
#define _xbnetMD4_H(x, y, z) ((x) ^ (y) ^ (z))

/* _xbnetMD4_ROTATE_LEFT rotates x left n bits.
*/
#define _xbnetMD4_ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))

/* _xbnetMD4_FF, _xbnetMD4_GG and _xbnetMD4_HH are transformations for rounds 1, 2 and 3 */
/* Rotation is separate from addition to prevent recomputation */
#define _xbnetMD4_FF(a, b, c, d, x, s) { \
    (a) += _xbnetMD4_F ((b), (c), (d)) + (x); \
    (a) = _xbnetMD4_ROTATE_LEFT ((a), (s)); \
	}
#define _xbnetMD4_GG(a, b, c, d, x, s) { \
    (a) += _xbnetMD4_G ((b), (c), (d)) + (x) + (ulong)0x5a827999; \
    (a) = _xbnetMD4_ROTATE_LEFT ((a), (s)); \
	}
#define _xbnetMD4_HH(a, b, c, d, x, s) { \
    (a) += _xbnetMD4_H ((b), (c), (d)) + (x) + (ulong)0x6ed9eba1; \
    (a) = _xbnetMD4_ROTATE_LEFT ((a), (s)); \
	}

/* MD4 initialization. Begins an MD4 operation, writing a new context.
*/
void xbnetMD4_init(xbnetMD4_CTX *context)
{
	context->count[0] = context->count[1] = 0;
	
	/* Load magic initialization constants.
	*/
	context->state[0] = 0x67452301;
	context->state[1] = 0xefcdab89;
	context->state[2] = 0x98badcfe;
	context->state[3] = 0x10325476;
}

/* MD4 block update operation. Continues an MD4 message-digest
operation, processing another message block, and updating the
context.
*/
void xbnetMD4_update (
					xbnetMD4_CTX *context, /* context */
					const uchar *input,     /* input block */
					uint inputLen)    /* length of input block */
{
	uint i, index, partLen;
	
	/* Compute number of bytes mod 64 */
	index = (uint)((context->count[0] >> 3) & 0x3F);
	
	/* Update number of bits */
	if ((context->count[0] += ((ulong)inputLen << 3))
		< ((ulong)inputLen << 3))
		context->count[1]++;
	context->count[1] += ((ulong)inputLen >> 29);
	
	partLen = 64 - index;
	
	/* Transform as many times as possible.
	*/
	if (inputLen >= partLen) 
	{
		memcpy(&context->buffer[index], input, partLen);
		xbnetMD4_transform(context->state, context->buffer);
		
		for (i = partLen; i + 63 < inputLen; i += 64)
			xbnetMD4_transform (context->state, &input[i]);
		
		index = 0;
	}
	else
		i = 0;
	
	/* Buffer remaining input */
	memcpy(&context->buffer[index], &input[i],inputLen-i);
}

/* MD4 finalization. Ends an MD4 message-digest operation, writing the
the message digest and zeroizing the context.
*/
void xbnetMD4_final (xbnetMD4_CTX *context,/* context */
				   uchar digest[16]	/* message digest */
				   )			
{
	uchar bits[8];
	uint index, padLen;
	
	/* Save number of bits */
	xbnetMD4_encode (bits, context->count, 8);
	
	/* Pad out to 56 mod 64.
	*/
	index = (uint)((context->count[0] >> 3) & 0x3f);
	padLen = (index < 56) ? (56 - index) : (120 - index);
	xbnetMD4_update (context, _xbnetMD4_PADDING, padLen);
	
	/* Append length (before padding) */
	xbnetMD4_update (context, bits, 8);
	
	/* Store state in digest */
	xbnetMD4_encode (digest, context->state, 16);
	
	/* Zeroize sensitive information.
	*/
	memset (context, 0, sizeof (*context));
}

/* MD4 basic transformation. Transforms state based on block.
*/
void xbnetMD4_transform (
					   ulong state[4],
					   const uchar block[64])
{
	ulong a = state[0], b = state[1], c = state[2], d = state[3], x[16];
	
	xbnetMD4_decode (x, block, 64);
	
	/* Round 1 */
	_xbnetMD4_FF (a, b, c, d, x[ 0], _xbnetMD4_S11); /* 1 */
	_xbnetMD4_FF (d, a, b, c, x[ 1], _xbnetMD4_S12); /* 2 */
	_xbnetMD4_FF (c, d, a, b, x[ 2], _xbnetMD4_S13); /* 3 */
	_xbnetMD4_FF (b, c, d, a, x[ 3], _xbnetMD4_S14); /* 4 */
	_xbnetMD4_FF (a, b, c, d, x[ 4], _xbnetMD4_S11); /* 5 */
	_xbnetMD4_FF (d, a, b, c, x[ 5], _xbnetMD4_S12); /* 6 */
	_xbnetMD4_FF (c, d, a, b, x[ 6], _xbnetMD4_S13); /* 7 */
	_xbnetMD4_FF (b, c, d, a, x[ 7], _xbnetMD4_S14); /* 8 */
	_xbnetMD4_FF (a, b, c, d, x[ 8], _xbnetMD4_S11); /* 9 */
	_xbnetMD4_FF (d, a, b, c, x[ 9], _xbnetMD4_S12); /* 10 */
	_xbnetMD4_FF (c, d, a, b, x[10], _xbnetMD4_S13); /* 11 */
	_xbnetMD4_FF (b, c, d, a, x[11], _xbnetMD4_S14); /* 12 */
	_xbnetMD4_FF (a, b, c, d, x[12], _xbnetMD4_S11); /* 13 */
	_xbnetMD4_FF (d, a, b, c, x[13], _xbnetMD4_S12); /* 14 */
	_xbnetMD4_FF (c, d, a, b, x[14], _xbnetMD4_S13); /* 15 */
	_xbnetMD4_FF (b, c, d, a, x[15], _xbnetMD4_S14); /* 16 */
	
	/* Round 2 */
	_xbnetMD4_GG (a, b, c, d, x[ 0], _xbnetMD4_S21); /* 17 */
	_xbnetMD4_GG (d, a, b, c, x[ 4], _xbnetMD4_S22); /* 18 */
	_xbnetMD4_GG (c, d, a, b, x[ 8], _xbnetMD4_S23); /* 19 */
	_xbnetMD4_GG (b, c, d, a, x[12], _xbnetMD4_S24); /* 20 */
	_xbnetMD4_GG (a, b, c, d, x[ 1], _xbnetMD4_S21); /* 21 */
	_xbnetMD4_GG (d, a, b, c, x[ 5], _xbnetMD4_S22); /* 22 */
	_xbnetMD4_GG (c, d, a, b, x[ 9], _xbnetMD4_S23); /* 23 */
	_xbnetMD4_GG (b, c, d, a, x[13], _xbnetMD4_S24); /* 24 */
	_xbnetMD4_GG (a, b, c, d, x[ 2], _xbnetMD4_S21); /* 25 */
	_xbnetMD4_GG (d, a, b, c, x[ 6], _xbnetMD4_S22); /* 26 */
	_xbnetMD4_GG (c, d, a, b, x[10], _xbnetMD4_S23); /* 27 */
	_xbnetMD4_GG (b, c, d, a, x[14], _xbnetMD4_S24); /* 28 */
	_xbnetMD4_GG (a, b, c, d, x[ 3], _xbnetMD4_S21); /* 29 */
	_xbnetMD4_GG (d, a, b, c, x[ 7], _xbnetMD4_S22); /* 30 */
	_xbnetMD4_GG (c, d, a, b, x[11], _xbnetMD4_S23); /* 31 */
	_xbnetMD4_GG (b, c, d, a, x[15], _xbnetMD4_S24); /* 32 */
	
	/* Round 3 */
	_xbnetMD4_HH (a, b, c, d, x[ 0], _xbnetMD4_S31); /* 33 */
	_xbnetMD4_HH (d, a, b, c, x[ 8], _xbnetMD4_S32); /* 34 */
	_xbnetMD4_HH (c, d, a, b, x[ 4], _xbnetMD4_S33); /* 35 */
	_xbnetMD4_HH (b, c, d, a, x[12], _xbnetMD4_S34); /* 36 */
	_xbnetMD4_HH (a, b, c, d, x[ 2], _xbnetMD4_S31); /* 37 */
	_xbnetMD4_HH (d, a, b, c, x[10], _xbnetMD4_S32); /* 38 */
	_xbnetMD4_HH (c, d, a, b, x[ 6], _xbnetMD4_S33); /* 39 */
	_xbnetMD4_HH (b, c, d, a, x[14], _xbnetMD4_S34); /* 40 */
	_xbnetMD4_HH (a, b, c, d, x[ 1], _xbnetMD4_S31); /* 41 */
	_xbnetMD4_HH (d, a, b, c, x[ 9], _xbnetMD4_S32); /* 42 */
	_xbnetMD4_HH (c, d, a, b, x[ 5], _xbnetMD4_S33); /* 43 */
	_xbnetMD4_HH (b, c, d, a, x[13], _xbnetMD4_S34); /* 44 */
	_xbnetMD4_HH (a, b, c, d, x[ 3], _xbnetMD4_S31); /* 45 */
	_xbnetMD4_HH (d, a, b, c, x[11], _xbnetMD4_S32); /* 46 */
	_xbnetMD4_HH (c, d, a, b, x[ 7], _xbnetMD4_S33); /* 47 */
	_xbnetMD4_HH (b, c, d, a, x[15], _xbnetMD4_S34); /* 48 */
	
	state[0] += a;
	state[1] += b;
	state[2] += c;
	state[3] += d;
	
	/* Zeroize sensitive information.
	*/
	memset (x, 0, sizeof (x));
}

/* Encodes input (ulong) into output (uchar). Assumes len is
a multiple of 4.
*/
void xbnetMD4_encode (
					uchar *output,
					const ulong *input,
					uint len)
{
	uint i, j;
	
	for (i = 0, j = 0; j < len; i++, j += 4) {
		output[j] = (uchar)(input[i] & 0xff);
		output[j+1] = (uchar)((input[i] >> 8) & 0xff);
		output[j+2] = (uchar)((input[i] >> 16) & 0xff);
		output[j+3] = (uchar)((input[i] >> 24) & 0xff);
	}
}

/* Decodes input (uchar) into output (ulong). Assumes len is
a multiple of 4.
*/
void xbnetMD4_decode (
					ulong *output,
					const uchar *input,
					uint len)
{
	uint i, j;
	
	for (i = 0, j = 0; j < len; i++, j += 4)
		output[i] = ((ulong)input[j]) | (((ulong)input[j+1]) << 8) |
		(((ulong)input[j+2]) << 16) | (((ulong)input[j+3]) << 24);
}


//From rfc 3174
/*
* If you do not have the ISO standard stdint.h header file, then you
* must typdef the following:
*    name              meaning
*  ulong         unsigned 32 bit integer
*  uchar          unsigned 8 bit integer (i.e., unsigned char)
*  int    integer of >= 16 bits
*
*/

/*
*  sha1.c
*
*  Description:
*      This file implements the Secure Hashing Algorithm 1 as
*      defined in FIPS PUB 180-1 published April 17, 1995.
*
*      The SHA-1, produces a 160-bit message digest for a given
*      data stream.  It should take about 2**n steps to find a
*      message with the same digest as a given message and
*      2**(n/2) to find any two messages with the same digest,
*      when n is the digest size in bits.  Therefore, this
*      algorithm can serve as a means of providing a
*      "fingerprint" for a message.
*
*  Portability Issues:
*      SHA-1 is defined in terms of 32-bit "words".  This code
*      uses <stdint.h> (included via "sha1.h" to define 32 and 8
*      bit unsigned integer types.  If your C compiler does not
*      support 32 bit unsigned integers, this code is not
*      appropriate.
*
*  Caveats:
*      SHA-1 is designed to work with messages less than 2^64 bits
*      long.  Although SHA-1 allows a message digest to be generated
*      for messages of any number of bits less than 2^64, this
*      implementation only works with messages with a length that is
*      a multiple of the size of an 8-bit character.
*
*/

/*
*  Define the SHA1 circular left shift macro
*/
#define SHA1CircularShift(bits,word) \
(((word) << (bits)) | ((word) >> (32-(bits))))

/* Local Function Prototyptes */
void SHA1PadMessage(xbnetSHA1_CTX *);
void SHA1ProcessMessageBlock(xbnetSHA1_CTX *);

/*
*  xbnetSHA1_init
*
*  Description:
*      This function will initialize the xbnetSHA1_CTX in preparation
*      for computing a new SHA1 message digest.
*
*  Parameters:
*      context: [in/out]
*          The context to reset.
*
*  Returns:
*      sha Error Code.
*
*/
int xbnetSHA1_init(xbnetSHA1_CTX *context)
{
    if (!context)
    {
        return shaNull;
    }
	
    context->Length_Low             = 0;
    context->Length_High            = 0;
    context->Message_Block_Index    = 0;
	
    context->Intermediate_Hash[0]   = 0x67452301;
    context->Intermediate_Hash[1]   = 0xEFCDAB89;
    context->Intermediate_Hash[2]   = 0x98BADCFE;
    context->Intermediate_Hash[3]   = 0x10325476;
    context->Intermediate_Hash[4]   = 0xC3D2E1F0;
	
    context->Computed   = 0;
    context->Corrupted  = 0;
	
    return shaSuccess;
}

/*
*  xbnetSHA1_final
*
*  Description:
*      This function will return the 160-bit message digest into the
*      Message_Digest array  provided by the caller.
*      NOTE: The first octet of hash is stored in the 0th element,
*            the last octet of hash in the 19th element.
*
*  Parameters:
*      context: [in/out]
*          The context to use to calculate the SHA-1 hash.
*      Message_Digest: [out]
*          Where the digest is returned.
*
*  Returns:
*      sha Error Code.
*
*/
int xbnetSHA1_final( xbnetSHA1_CTX *context,
				  uchar Message_Digest[xbnetSHA1hashSize])
{
    int i;
	
    if (!context || !Message_Digest)
    {
        return shaNull;
    }
	
    if (context->Corrupted)
    {
        return context->Corrupted;
    }
	
    if (!context->Computed)
    {
        SHA1PadMessage(context);
        for(i=0; i<64; ++i)
        {
            /* message may be sensitive, clear it out */
            context->Message_Block[i] = 0;
        }
        context->Length_Low = 0;    /* and clear length */
        context->Length_High = 0;
        context->Computed = 1;
		
    }
	
    for(i = 0; i < xbnetSHA1hashSize; ++i)
    {
        Message_Digest[i] = uchar(context->Intermediate_Hash[i>>2]
			>> 8 * ( 3 - ( i & 0x03 ) ));
    }
	
    return shaSuccess;
}

/*
*  xbnetSHA1_update
*
*  Description:
*      This function accepts an array of octets as the next portion
*      of the message.
*
*  Parameters:
*      context: [in/out]
*          The SHA context to update
*      message_array: [in]
*          An array of characters representing the next portion of
*          the message.
*      length: [in]
*          The length of the message in message_array
*
*  Returns:
*      sha Error Code.
*
*/
int xbnetSHA1_update(    xbnetSHA1_CTX    *context,
				   const uchar  *message_array,
				   unsigned       length)
{
    if (!length)
    {
        return shaSuccess;
    }
	
    if (!context || !message_array)
    {
        return shaNull;
    }
	
    if (context->Computed)
    {
        context->Corrupted = shaStateError;
		
        return shaStateError;
    }
	
    if (context->Corrupted)
    {
		return context->Corrupted;
    }
    while(length-- && !context->Corrupted)
    {
		context->Message_Block[context->Message_Block_Index++] =
			(*message_array & 0xFF);
		
		context->Length_Low += 8;
		if (context->Length_Low == 0)
		{
			context->Length_High++;
			if (context->Length_High == 0)
			{
				/* Message is too long */
				context->Corrupted = 1;
			}
		}
		
		if (context->Message_Block_Index == 64)
		{
			SHA1ProcessMessageBlock(context);
		}
		
		message_array++;
    }
	
    return shaSuccess;
}

/*
*  SHA1ProcessMessageBlock
*
*  Description:
*      This function will process the next 512 bits of the message
*      stored in the Message_Block array.
*
*  Parameters:
*      None.
*
*  Returns:
*      Nothing.
*
*  Comments:

*      Many of the variable names in this code, especially the
*      single character names, were used because those were the
*      names used in the publication.
*
*
*/
void SHA1ProcessMessageBlock(xbnetSHA1_CTX *context)
{
    const ulong K[] =    
	{       /* Constants defined in SHA-1   */
		0x5A827999,
			0x6ED9EBA1,
			0x8F1BBCDC,
			0xCA62C1D6
	};
    int           t;                 /* Loop counter                */
    ulong      temp;              /* Temporary word value        */
    ulong      W[80];             /* Word sequence               */
    ulong      A, B, C, D, E;     /* Word buffers                */
	
								  /*
								  *  Initialize the first 16 words in the array W
	*/
    for(t = 0; t < 16; t++)
    {
        W[t] = context->Message_Block[t * 4] << 24;
        W[t] |= context->Message_Block[t * 4 + 1] << 16;
        W[t] |= context->Message_Block[t * 4 + 2] << 8;
        W[t] |= context->Message_Block[t * 4 + 3];
    }
	
    for(t = 16; t < 80; t++)
    {
		W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]);
    }
	
    A = context->Intermediate_Hash[0];
    B = context->Intermediate_Hash[1];
    C = context->Intermediate_Hash[2];
    D = context->Intermediate_Hash[3];
    E = context->Intermediate_Hash[4];
	
    for(t = 0; t < 20; t++)
    {
        temp =  SHA1CircularShift(5,A) +
			((B & C) | ((~B) & D)) + E + W[t] + K[0];
        E = D;
        D = C;
        C = SHA1CircularShift(30,B);
		
        B = A;
        A = temp;
    }
	
    for(t = 20; t < 40; t++)
    {
        temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1];
        E = D;
        D = C;
        C = SHA1CircularShift(30,B);
        B = A;
        A = temp;
    }
	
    for(t = 40; t < 60; t++)
    {
        temp = SHA1CircularShift(5,A) +
			((B & C) | (B & D) | (C & D)) + E + W[t] + K[2];
        E = D;
        D = C;
        C = SHA1CircularShift(30,B);
        B = A;
        A = temp;
    }
	
    for(t = 60; t < 80; t++)
    {
        temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3];
        E = D;
        D = C;
        C = SHA1CircularShift(30,B);
        B = A;
        A = temp;
    }
	
    context->Intermediate_Hash[0] += A;
    context->Intermediate_Hash[1] += B;
    context->Intermediate_Hash[2] += C;
    context->Intermediate_Hash[3] += D;
    context->Intermediate_Hash[4] += E;
	
    context->Message_Block_Index = 0;
}

/*
*  SHA1PadMessage
*

*  Description:
*      According to the standard, the message must be padded to an even
*      512 bits.  The first padding bit must be a '1'.  The last 64
*      bits represent the length of the original message.  All bits in
*      between should be 0.  This function will pad the message
*      according to those rules by filling the Message_Block array
*      accordingly.  It will also call the ProcessMessageBlock function
*      provided appropriately.  When it returns, it can be assumed that
*      the message digest has been computed.
*
*  Parameters:
*      context: [in/out]
*          The context to pad
*      ProcessMessageBlock: [in]
*          The appropriate SHA*ProcessMessageBlock function
*  Returns:
*      Nothing.
*
*/
  
void SHA1PadMessage(xbnetSHA1_CTX *context)
{
/*
*  Check to see if the current message block is too small to hold
*  the initial padding bits and length.  If so, we will pad the
*  block, process it, and then continue padding into a second
*  block.
  */
	if (context->Message_Block_Index > 55)
	{
		context->Message_Block[context->Message_Block_Index++] = 0x80;
		while(context->Message_Block_Index < 64)
		{
			context->Message_Block[context->Message_Block_Index++] = 0;
		}
		
		SHA1ProcessMessageBlock(context);
		
		while(context->Message_Block_Index < 56)
		{
			context->Message_Block[context->Message_Block_Index++] = 0;
		}
	}
	else
	{
		context->Message_Block[context->Message_Block_Index++] = 0x80;
		while(context->Message_Block_Index < 56)
		{
			
			context->Message_Block[context->Message_Block_Index++] = 0;
		}
	}
	
	/*
	*  Store the message length as the last 8 octets
	*/
	context->Message_Block[56] = uchar(context->Length_High >> 24);
	context->Message_Block[57] = uchar(context->Length_High >> 16);
	context->Message_Block[58] = uchar(context->Length_High >> 8);
	context->Message_Block[59] = uchar(context->Length_High);
	context->Message_Block[60] = uchar(context->Length_Low >> 24);
	context->Message_Block[61] = uchar(context->Length_Low >> 16);
	context->Message_Block[62] = uchar(context->Length_Low >> 8);
	context->Message_Block[63] = uchar(context->Length_Low);
	
	SHA1ProcessMessageBlock(context);
}


/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
rights reserved.

License to copy and use this software is granted provided that it
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
Algorithm" in all material mentioning or referencing this software
or this function.

License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work.

RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind.

Rivest                                                          [Page 8]

RFC 1321              MD5 Message-Digest Algorithm            April 1992


These notices must be retained in any copies of any part of this
documentation and/or software.
*/
#define xbnetMD5_S11 7
#define xbnetMD5_S12 12
#define xbnetMD5_S13 17
#define xbnetMD5_S14 22
#define xbnetMD5_S21 5
#define xbnetMD5_S22 9
#define xbnetMD5_S23 14
#define xbnetMD5_S24 20
#define xbnetMD5_S31 4
#define xbnetMD5_S32 11
#define xbnetMD5_S33 16
#define xbnetMD5_S34 23
#define xbnetMD5_S41 6
#define xbnetMD5_S42 10
#define xbnetMD5_S43 15
#define xbnetMD5_S44 21

void xbnetMD5_transform(ulong [4], const uchar [64]);
void xbnetMD5_encode(uchar *, const ulong *, uint);
void xbnetMD5_decode(ulong *, const uchar *, uint);

static unsigned char xbnetMD5_PADDING[64] = {
  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

/* xbnetMD5_F, xbnetMD5_G, xbnetMD5_H and xbnetMD5_I are basic MD5 functions.
*/
#define xbnetMD5_F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define xbnetMD5_G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define xbnetMD5_H(x, y, z) ((x) ^ (y) ^ (z))
#define xbnetMD5_I(x, y, z) ((y) ^ ((x) | (~z)))
  
/* xbnetMD5_ROTATE_LEFT rotates x left n bits.
*/
#define xbnetMD5_ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
  
/* xbnetMD5_FF, xbnetMD5_GG, xbnetMD5_HH, and xbnetMD5_II transformations for rounds 1, 2, 3, and 4.
Rotation is separate from addition to prevent recomputation.
*/
#define xbnetMD5_FF(a, b, c, d, x, s, ac) { \
	(a) += xbnetMD5_F ((b), (c), (d)) + (x) + (ulong)(ac); \
	(a) = xbnetMD5_ROTATE_LEFT ((a), (s)); \
	(a) += (b); \
	  }
#define xbnetMD5_GG(a, b, c, d, x, s, ac) { \
	(a) += xbnetMD5_G ((b), (c), (d)) + (x) + (ulong)(ac); \
	(a) = xbnetMD5_ROTATE_LEFT ((a), (s)); \
	(a) += (b); \
	  }
#define xbnetMD5_HH(a, b, c, d, x, s, ac) { \
	(a) += xbnetMD5_H ((b), (c), (d)) + (x) + (ulong)(ac); \
	(a) = xbnetMD5_ROTATE_LEFT ((a), (s)); \
	(a) += (b); \
	  }
#define xbnetMD5_II(a, b, c, d, x, s, ac) { \
	(a) += xbnetMD5_I ((b), (c), (d)) + (x) + (ulong)(ac); \
	(a) = xbnetMD5_ROTATE_LEFT ((a), (s)); \
	(a) += (b); \
	  }
  
/* MD5 initialization. Begins an MD5 operation, writing a new context.
*/
void xbnetMD5_init (
				  xbnetMD5_CTX *context)                                        /* context */
{
	context->count[0] = context->count[1] = 0;
	/* Load magic initialization constants.
	*/
	context->state[0] = 0x67452301;
	context->state[1] = 0xefcdab89;
	context->state[2] = 0x98badcfe;
	context->state[3] = 0x10325476;
}

/* MD5 block update operation. Continues an MD5 message-digest
operation, processing another message block, and updating the
context.
*/
void xbnetMD5_update (
					xbnetMD5_CTX *context,                                        /* context */
					const uchar *input,                                /* input block */
					uint inputLen)                     /* length of input block */
{
	unsigned int i, index, partLen;
	
	/* Compute number of bytes mod 64 */
	index = (unsigned int)((context->count[0] >> 3) & 0x3F);
	
	/* Update number of bits */
	if ((context->count[0] += ((ulong)inputLen << 3))
		< ((ulong)inputLen << 3))
		context->count[1]++;
	context->count[1] += ((ulong)inputLen >> 29);
	
	partLen = 64 - index;
	
	/* Transform as many times as possible.
	*/
	if (inputLen >= partLen) {
		memcpy
			(&context->buffer[index], input, partLen);
		xbnetMD5_transform (context->state, context->buffer);
		
		for (i = partLen; i + 63 < inputLen; i += 64)
			xbnetMD5_transform (context->state, &input[i]);
		
		index = 0;
	}
	else
		i = 0;
	
	/* Buffer remaining input */
	memcpy
		(&context->buffer[index], &input[i],
		inputLen-i);
}

/* MD5 finalization. Ends an MD5 message-digest operation, writing the
the message digest and zeroizing the context.
*/
void xbnetMD5_final (xbnetMD5_CTX *context,/* context */
				   unsigned char digest[16]) /* message digest */
				   
{
	unsigned char bits[8];
	unsigned int index, padLen;
	
	/* Save number of bits */
	xbnetMD5_encode (bits, context->count, 8);
	
	/* Pad out to 56 mod 64.
	*/
	index = (unsigned int)((context->count[0] >> 3) & 0x3f);
	padLen = (index < 56) ? (56 - index) : (120 - index);
	xbnetMD5_update (context, xbnetMD5_PADDING, padLen);
	
	/* Append length (before padding) */
	xbnetMD5_update (context, bits, 8);
	
	/* Store state in digest */
	xbnetMD5_encode (digest, context->state, 16);
	
	/* Zeroize sensitive information.
	*/
	memset (context, 0, sizeof (*context));
}

/* MD5 basic transformation. Transforms state based on block.
*/
void xbnetMD5_transform (
					   ulong state[4],
					   const uchar block[64])
{
	ulong a = state[0], b = state[1], c = state[2], d = state[3], x[16];
	
	xbnetMD5_decode (x, block, 64);
	
	/* Round 1 */
	xbnetMD5_FF (a, b, c, d, x[ 0], xbnetMD5_S11, 0xd76aa478); /* 1 */
	xbnetMD5_FF (d, a, b, c, x[ 1], xbnetMD5_S12, 0xe8c7b756); /* 2 */
	xbnetMD5_FF (c, d, a, b, x[ 2], xbnetMD5_S13, 0x242070db); /* 3 */
	xbnetMD5_FF (b, c, d, a, x[ 3], xbnetMD5_S14, 0xc1bdceee); /* 4 */
	xbnetMD5_FF (a, b, c, d, x[ 4], xbnetMD5_S11, 0xf57c0faf); /* 5 */
	xbnetMD5_FF (d, a, b, c, x[ 5], xbnetMD5_S12, 0x4787c62a); /* 6 */
	xbnetMD5_FF (c, d, a, b, x[ 6], xbnetMD5_S13, 0xa8304613); /* 7 */
	xbnetMD5_FF (b, c, d, a, x[ 7], xbnetMD5_S14, 0xfd469501); /* 8 */
	xbnetMD5_FF (a, b, c, d, x[ 8], xbnetMD5_S11, 0x698098d8); /* 9 */
	xbnetMD5_FF (d, a, b, c, x[ 9], xbnetMD5_S12, 0x8b44f7af); /* 10 */
	xbnetMD5_FF (c, d, a, b, x[10], xbnetMD5_S13, 0xffff5bb1); /* 11 */
	xbnetMD5_FF (b, c, d, a, x[11], xbnetMD5_S14, 0x895cd7be); /* 12 */
	xbnetMD5_FF (a, b, c, d, x[12], xbnetMD5_S11, 0x6b901122); /* 13 */
	xbnetMD5_FF (d, a, b, c, x[13], xbnetMD5_S12, 0xfd987193); /* 14 */
	xbnetMD5_FF (c, d, a, b, x[14], xbnetMD5_S13, 0xa679438e); /* 15 */
	xbnetMD5_FF (b, c, d, a, x[15], xbnetMD5_S14, 0x49b40821); /* 16 */
	
	/* Round 2 */
	xbnetMD5_GG (a, b, c, d, x[ 1], xbnetMD5_S21, 0xf61e2562); /* 17 */
	xbnetMD5_GG (d, a, b, c, x[ 6], xbnetMD5_S22, 0xc040b340); /* 18 */
	xbnetMD5_GG (c, d, a, b, x[11], xbnetMD5_S23, 0x265e5a51); /* 19 */
	xbnetMD5_GG (b, c, d, a, x[ 0], xbnetMD5_S24, 0xe9b6c7aa); /* 20 */
	xbnetMD5_GG (a, b, c, d, x[ 5], xbnetMD5_S21, 0xd62f105d); /* 21 */
	xbnetMD5_GG (d, a, b, c, x[10], xbnetMD5_S22,  0x2441453); /* 22 */
	xbnetMD5_GG (c, d, a, b, x[15], xbnetMD5_S23, 0xd8a1e681); /* 23 */
	xbnetMD5_GG (b, c, d, a, x[ 4], xbnetMD5_S24, 0xe7d3fbc8); /* 24 */
	xbnetMD5_GG (a, b, c, d, x[ 9], xbnetMD5_S21, 0x21e1cde6); /* 25 */
	xbnetMD5_GG (d, a, b, c, x[14], xbnetMD5_S22, 0xc33707d6); /* 26 */
	xbnetMD5_GG (c, d, a, b, x[ 3], xbnetMD5_S23, 0xf4d50d87); /* 27 */
	
	xbnetMD5_GG (b, c, d, a, x[ 8], xbnetMD5_S24, 0x455a14ed); /* 28 */
	xbnetMD5_GG (a, b, c, d, x[13], xbnetMD5_S21, 0xa9e3e905); /* 29 */
	xbnetMD5_GG (d, a, b, c, x[ 2], xbnetMD5_S22, 0xfcefa3f8); /* 30 */
	xbnetMD5_GG (c, d, a, b, x[ 7], xbnetMD5_S23, 0x676f02d9); /* 31 */
	xbnetMD5_GG (b, c, d, a, x[12], xbnetMD5_S24, 0x8d2a4c8a); /* 32 */
	
	/* Round 3 */
	xbnetMD5_HH (a, b, c, d, x[ 5], xbnetMD5_S31, 0xfffa3942); /* 33 */
	xbnetMD5_HH (d, a, b, c, x[ 8], xbnetMD5_S32, 0x8771f681); /* 34 */
	xbnetMD5_HH (c, d, a, b, x[11], xbnetMD5_S33, 0x6d9d6122); /* 35 */
	xbnetMD5_HH (b, c, d, a, x[14], xbnetMD5_S34, 0xfde5380c); /* 36 */
	xbnetMD5_HH (a, b, c, d, x[ 1], xbnetMD5_S31, 0xa4beea44); /* 37 */
	xbnetMD5_HH (d, a, b, c, x[ 4], xbnetMD5_S32, 0x4bdecfa9); /* 38 */
	xbnetMD5_HH (c, d, a, b, x[ 7], xbnetMD5_S33, 0xf6bb4b60); /* 39 */
	xbnetMD5_HH (b, c, d, a, x[10], xbnetMD5_S34, 0xbebfbc70); /* 40 */
	xbnetMD5_HH (a, b, c, d, x[13], xbnetMD5_S31, 0x289b7ec6); /* 41 */
	xbnetMD5_HH (d, a, b, c, x[ 0], xbnetMD5_S32, 0xeaa127fa); /* 42 */
	xbnetMD5_HH (c, d, a, b, x[ 3], xbnetMD5_S33, 0xd4ef3085); /* 43 */
	xbnetMD5_HH (b, c, d, a, x[ 6], xbnetMD5_S34,  0x4881d05); /* 44 */
	xbnetMD5_HH (a, b, c, d, x[ 9], xbnetMD5_S31, 0xd9d4d039); /* 45 */
	xbnetMD5_HH (d, a, b, c, x[12], xbnetMD5_S32, 0xe6db99e5); /* 46 */
	xbnetMD5_HH (c, d, a, b, x[15], xbnetMD5_S33, 0x1fa27cf8); /* 47 */
	xbnetMD5_HH (b, c, d, a, x[ 2], xbnetMD5_S34, 0xc4ac5665); /* 48 */
	
	/* Round 4 */
	xbnetMD5_II (a, b, c, d, x[ 0], xbnetMD5_S41, 0xf4292244); /* 49 */
	xbnetMD5_II (d, a, b, c, x[ 7], xbnetMD5_S42, 0x432aff97); /* 50 */
	xbnetMD5_II (c, d, a, b, x[14], xbnetMD5_S43, 0xab9423a7); /* 51 */
	xbnetMD5_II (b, c, d, a, x[ 5], xbnetMD5_S44, 0xfc93a039); /* 52 */
	xbnetMD5_II (a, b, c, d, x[12], xbnetMD5_S41, 0x655b59c3); /* 53 */
	xbnetMD5_II (d, a, b, c, x[ 3], xbnetMD5_S42, 0x8f0ccc92); /* 54 */
	xbnetMD5_II (c, d, a, b, x[10], xbnetMD5_S43, 0xffeff47d); /* 55 */
	xbnetMD5_II (b, c, d, a, x[ 1], xbnetMD5_S44, 0x85845dd1); /* 56 */
	xbnetMD5_II (a, b, c, d, x[ 8], xbnetMD5_S41, 0x6fa87e4f); /* 57 */
	xbnetMD5_II (d, a, b, c, x[15], xbnetMD5_S42, 0xfe2ce6e0); /* 58 */
	xbnetMD5_II (c, d, a, b, x[ 6], xbnetMD5_S43, 0xa3014314); /* 59 */
	xbnetMD5_II (b, c, d, a, x[13], xbnetMD5_S44, 0x4e0811a1); /* 60 */
	xbnetMD5_II (a, b, c, d, x[ 4], xbnetMD5_S41, 0xf7537e82); /* 61 */
	xbnetMD5_II (d, a, b, c, x[11], xbnetMD5_S42, 0xbd3af235); /* 62 */
	xbnetMD5_II (c, d, a, b, x[ 2], xbnetMD5_S43, 0x2ad7d2bb); /* 63 */
	xbnetMD5_II (b, c, d, a, x[ 9], xbnetMD5_S44, 0xeb86d391); /* 64 */
	
	state[0] += a;
	state[1] += b;
	state[2] += c;
	state[3] += d;
	
	/* Zeroize sensitive information.*/
	//memset (x, 0, sizeof (x));
}

/* Encodes input (ulong) into output (unsigned char). Assumes len is
a multiple of 4.
*/
void xbnetMD5_encode (
					uchar *output,
					const ulong *input,
					uint len)
{
	unsigned int i, j;
	
	for (i = 0, j = 0; j < len; i++, j += 4) 
	{
		output[j] = (unsigned char)(input[i] & 0xff);
		output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
		output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
		output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
	}
}

/* Decodes input (unsigned char) into output (ulong). Assumes len is
a multiple of 4.
*/
void xbnetMD5_decode (
					ulong *output,
					const uchar *input,
					uint len)
{
	unsigned int i, j;
	
	for (i = 0, j = 0; j < len; i++, j += 4)
		output[i] = ((ulong)input[j]) | (((ulong)input[j+1]) << 8) |
		(((ulong)input[j+2]) << 16) | (((ulong)input[j+3]) << 24);
}
  
/* Taken from http://samba.org/doxygen/samba/head/hmacmd5_8c-source.html

Unix SMB/CIFS implementation.
HMAC MD5 code for use in NTLMv2
Copyright (C) Luke Kenneth Casson Leighton 1996-2000
Copyright (C) Andrew Tridgell 1992-2000

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
  
/* taken direct from rfc2104 implementation and modified for suitable use
* for ntlmv2.
*/
  
/***********************************************************************
the rfc 2104 version of xbnetHmacMD5_make initialisation.
***********************************************************************/
void xbnetHmacMD5_init(xbnetHmacMD5_CTX *ctx, const uchar*  key, int key_len)
{
	int i;
	
	/* if key is longer than 64 bytes reset it to key=MD5(key) */
	if (key_len > 64)
	{
		uchar tk[16];
		xbnetMD5_CTX tctx;
		
		xbnetMD5_init(&tctx);
		xbnetMD5_update(&tctx, key, key_len);
		xbnetMD5_final(&tctx, tk);
		
		key = tk;
		key_len = 16;
	}
	
	/* start out by storing key in pads */
	memset(ctx->k_ipad,0,sizeof(ctx->k_ipad));
	memset(ctx->k_opad,0,sizeof(ctx->k_opad));
	memcpy( ctx->k_ipad, key, key_len);
	memcpy( ctx->k_opad, key, key_len);
	
	/* XOR key with ipad and opad values */
	for (i=0; i<64; i++)
	{
		ctx->k_ipad[i] ^= 0x36;
		ctx->k_opad[i] ^= 0x5c;
	}
	
	xbnetMD5_init(&ctx->ctx);
	xbnetMD5_update(&ctx->ctx, ctx->k_ipad, 64);  
}

/***********************************************************************
the microsoft version of xbnetHmacMD5_make initialisation.
***********************************************************************/
void xbnetHmacMD5_msinit(xbnetHmacMD5_CTX *ctx,const uchar* key, int key_len)
{
	int i;
	
	/* if key is longer than 64 bytes truncate it */
	if (key_len > 64)
	{
		key_len = 64;
	}
	
	/* start out by storing key in pads */
	memset(ctx->k_ipad,0,sizeof(ctx->k_ipad));
	memset(ctx->k_opad,0,sizeof(ctx->k_opad));
	memcpy( ctx->k_ipad, key, key_len);
	memcpy( ctx->k_opad, key, key_len);
	
	/* XOR key with ipad and opad values */
	for (i=0; i<64; i++) {
		ctx->k_ipad[i] ^= 0x36;
		ctx->k_opad[i] ^= 0x5c;
	}
	
	xbnetMD5_init(&ctx->ctx);
	xbnetMD5_update(&ctx->ctx, ctx->k_ipad, 64);  
}

/***********************************************************************
update xbnetHmacMD5_make "inner" buffer
***********************************************************************/
void xbnetHmacMD5_update(xbnetHmacMD5_CTX *ctx, const uchar* text, int text_len)
{
	xbnetMD5_update(&ctx->ctx, text, text_len); /* then text of datagram */
}

/***********************************************************************
finish off xbnetHmacMD5_make "inner" buffer and generate outer one.
***********************************************************************/
void xbnetHmacMD5_final(xbnetHmacMD5_CTX *ctx, uchar *digest)
{
	struct xbnetMD5_CTX ctx_o;
	
	xbnetMD5_final(&ctx->ctx, digest);          
	
	xbnetMD5_init(&ctx_o);
	xbnetMD5_update(&ctx_o, ctx->k_opad, 64);   
	xbnetMD5_update(&ctx_o, digest, 16); 
	xbnetMD5_final(&ctx_o, digest);
}

/***********************************************************
single function to calculate an HMAC MD5 digest from data.
use the microsoft hmacmd5 init method because the key is 16 bytes.
************************************************************/
void xbnetHmacMD5_make(const uchar key[16], uchar* data, int data_len, uchar* digest)
{
	xbnetHmacMD5_CTX ctx;
	xbnetHmacMD5_msinit(&ctx, key, 16);
	if (data_len != 0)
	{
		xbnetHmacMD5_update(&ctx, data, data_len);
	}
	xbnetHmacMD5_final(&ctx, digest);
}



//Taken from rfc 2759 on Microsoft PPP CHAP Extensions, Version 2
void xbnetNTLM2_ChallengeHash(int challengeSz,
							/*IN 16-octet or 8-octet*/  const uchar *PeerChallenge,
							/*IN 16-octet or 8-octet*/  const uchar *AuthenticatorChallenge,
							/*IN  0-to-256-char*/       const xbString &UserName,
							/*OUT 8-octet*/				uchar *Challenge)
{
/*
* SHAInit(), xbnetSHA1_update() and xbnetSHA1_final() functions are an
* implementation of Secure Hash Algorithm (SHA-1) [11]. These are
* available in public domain or can be licensed from
* RSA Data Security, Inc.
	*/
	uchar digest[xbnetSHA1hashSize];
	xbnetSHA1_CTX context;
	xbnetSHA1_init(&context);
	xbnetSHA1_update(&context, PeerChallenge, challengeSz);
	xbnetSHA1_update(&context, AuthenticatorChallenge, challengeSz);
	
	/*
	* Only the user name (as presented by the peer and
	* excluding any prepended domain name)
	* is used as input to xbnetSHA1_update().
	*/
	
	xbnetSHA1_update(&context, (const uchar*)(LPCSTR)UserName, UserName.Length);
	xbnetSHA1_final(&context, digest);
	memcpy(Challenge, digest, 8);
}


void xbnetNTLM2_makeNTresponse(int challengeSz,
							 /*IN  16-octet or 8-octet*/	const uchar *AuthenticatorChallenge,
							 /*IN  16-octet or 8-octet*/	const uchar *PeerChallenge,
							 /*IN  0-to-256-char*/			const xbString &UserName,
							 /*IN  0-to-256-unicode-char*/	const char *Password,
							 /*OUT 24-octet*/				xbString *response)
{
	uchar challenge[8];
	uchar passwordHash[21];
	
	xbnetNTLM2_ChallengeHash( challengeSz, PeerChallenge, AuthenticatorChallenge, UserName,
		/* giving */ challenge);
		
	{
		int   len = (int)strlen(Password);
		xbBSTR bstr(Password);
		
		xbnetMD4_CTX context;
		xbnetMD4_init(&context);
		xbnetMD4_update(&context, (const uchar*)(const wchar*)bstr, 2*len);
		xbnetMD4_final(&context, passwordHash);
	}

	memset(passwordHash+16,0,5);
	
	xbnetNTLM_calcResp((LPCSTR)passwordHash, xbString((char*)challenge,8), response);
}

//Extract from http://samba.org/doxygen/samba/head/auth__sam_8c-source.html
/* 
   Unix SMB/CIFS implementation.
   Password and authentication handling
   Copyright (C) Andrew Tridgell              1992-2000
   Copyright (C) Luke Kenneth Casson Leighton 1996-2000
   Copyright (C) Andrew Bartlett              2001
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
void ntv2_owf_gen(const uchar owf[16],  
	const xbString &user,
	const xbString &domain,
	uchar    kr_buf[16]);
void SMBOWFencrypt_ntv2  (  const uchar    kr[16],  
  const xbString &srv_chal,  
  const xbString &cli_chal,  
  char resp_buf[16] 
 );
bool smb_pwd_check_ntlmv2(const xbString &ntv2_response,
						  const xbString &password,
						  const xbString &nonce, //sec_blob
						  const xbString &user, 
						  const xbString &domain)
{
	if (password.IsEmpty())
		xbEapp::Throw("NTLM2 error","smb_pwd_check_ntlmv2() : No password set - DISALLOWING access");
	
	uchar passwordHash[16];
	{
		int   len = password.Length;
		xbBSTR bstr(password);
		
		xbnetMD4_CTX context;
		xbnetMD4_init(&context);
		xbnetMD4_update(&context, (const uchar*)(const wchar*)bstr, 2*len);
		xbnetMD4_final(&context, passwordHash);
	}
	
	/* Finish the encryption of part_passwd. */
	uchar kr[16];
	uchar value_from_encryption[16];
	uchar client_response[16];
	xbString client_key_data;
	
	if (ntv2_response.Length < 16)
		xbEapp::Throw("NTLM2 error","smb_pwd_check_ntlmv2() : incorrect password length ("+
			xbString(ntv2_response.Length)+")");
	
	client_key_data = xbString(((LPCSTR)ntv2_response)+16, ntv2_response.Length-16);
	memcpy(client_response, (LPCSTR)ntv2_response, sizeof(client_response));
	
	ntv2_owf_gen(passwordHash, user, domain, kr);
	SMBOWFencrypt_ntv2(kr, nonce, client_key_data, (char *)value_from_encryption);

	return (memcmp(value_from_encryption, client_response, 16) == 0);
}

void ntv2_owf_gen(const uchar owf[16],  
	const xbString &user,
	const xbString &domain,
	uchar    kr_buf[16])   
{
    xbnetHmacMD5_CTX ctx;
	
    int user_l = user.Length+1;
    int domain_l = domain.Length+1;

	xbBSTR uuser(domain);
	xbBSTR udomain(domain);
	
    xbnetHmacMD5_msinit(&ctx, owf, 16);
    xbnetHmacMD5_update(&ctx,(const uchar *)(LPCSTR)user, user.Length);
//    xbnetHmacMD5_update((const uchar *)(LPCWSTR)uuser, user_l * 2, &ctx);
//    xbnetHmacMD5_update((const uchar *)(LPCWSTR)udomain, domain_l * 2, &ctx);
    xbnetHmacMD5_final(&ctx, kr_buf);	
}

void SMBOWFencrypt_ntv2  (  const uchar    kr[16],  
  const xbString &srv_chal,  
  const xbString &cli_chal,  
  char resp_buf[16] 
 )   
{
	xbnetHmacMD5_CTX ctx;

	xbnetHmacMD5_msinit(&ctx, kr, 16);
	xbnetHmacMD5_update(&ctx, (const uchar*)(LPCSTR)srv_chal, srv_chal.Length);
	xbnetHmacMD5_update(&ctx, (const uchar*)(LPCSTR)cli_chal, cli_chal.Length);
	xbnetHmacMD5_final(&ctx, (unsigned char *)resp_buf);
}

void xbnetNTLM_NonceServer::NewAuth(xbnetNTLM_Nonce *pNonce)
{
	init();
	uint64 nb = uint64(::rand());
	if (nb == 0) nb = 123;
	xbSectLock lck(&m_sect);
	*((uint64*)&m_guid) += nb;
	memcpy(pNonce->key,((const char*)&m_guid),8);
	m_nb++;
	pNonce->m_bInit = true;
}

void xbnetNTLM_NonceServer::init()
{
	xbSectLock lck(&m_sect);
	if (m_bInit && m_nb < 50)
		return;
	lck.Unlock();
	HRtoE(::CoCreateGuid(&m_guid));
	srand( (unsigned)time( NULL ) );
	lck.Lock(&m_sect);
	m_nb = 0;
	m_bInit  = true;
}
#pragma pack( pop )
