#include "p_apps.h"
#include "p_mgcp.h"
#include "v_task.h"
#include "my_mgcp.h"

/*
Ki:			16 Bytes, defined by CNC.
MGID:		16 Bytes, defined by CNC.
g:			constant, g = 0x02;
p:			constant, P[96] = {	0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,
								0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,
								0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6,
								0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD,
								0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,
								0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,
								0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9,
								0xA6,0x3A,0x36,0x20,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
a:			private value, which generated by MG.
MgDH:		DH exchange number of MG, MgDH = (g exp a) mod p.
MgRand:		Random value of MG, for MD5, 8-64 bytes.
b:			private value, which generated by MGC.
CaDH:		DH exchange number of CA, CaDH = (g exp b) mod p.
CaRand:		Random value of CA, for MD5, 8-64 bytes.
Method:		The Encryption serial number, now it is 1.

Step1:
MG send:
RSIP 57 aaln/0@gaohong1.bj.cnc MGCP 1.0
RM:restart
X-EA:  Method
X-RANDOM:  MgRand
X-DH:  MgDH
X-AUTH:  	MD5(  Ki  +  MGID  +  MgDH +  MgRand )

Step2:
CA send:
RQNT 100 alln/0@gaohong1.bj.cnc MGCP 1.0
X: 123
R: auth/authoc
S: auth/dh(CaDH), auth/authreq( MD5(Kpub+Ki+CaDH+CaRand), Method, CaRand )

Here Kpub = (MgDH exp b) mod p

Step 3:
After MG got the message in Step2, it will do the calculation as:
Kpeer= (CaDH exp a) mod p
Diget = MD5(Kpeer+Ki+CaDH+CaRand),
if Diget is same as the auth/authreq is first parameter MD5(Kpub+Ki+CaDH+CaRand), 
then MG known the auth is OK now.
*/

#define MGCP_CNC_MGRAND		0xb0
#define MGCP_CNC_MGDH		0xb1
#define MGCP_CNC_MGAUTH		0xb2
#define MGCP_CNC_CADH		0xb3
#define MGCP_CNC_CAAUTH		0xb4
#define MGCP_CNC_CARAND		0xb5
#define MGCP_CNC_KEYMG		0xb6
#define MGCP_CNC_SSRES		0xb7	

BOOLEAN Cnc_bReqAuthoc;

static BOOLEAN _bRecvCaDH;

#define CNC_MGRAND_LEN	16
#define CNC_KI_LEN		16
#define CNC_MGID_LEN	16

static CCHAR _cKi[] = "11.255.254.50";
static CCHAR _cMGID[] = "[11.255.254.50]";

UCHAR _Char2Hex(UCHAR iVal)
{
	DCHAR iChar;

	iChar = iVal;
	if (iChar >= '0' && iChar <= '9')
		return (iChar - '0');
	if (iChar >= 'A' && iChar <= 'F')
		return (iChar - 'A' + 10);
	if (iChar >= 'a' && iChar <= 'f')
		return (iChar - 'a' + 10);
	return 0;
}

UCHAR _Str2Hex(PXCHAR pDst, PXCHAR pSrcStr)
{
	DCHAR i, j, iLen;
	PXCHAR_D pSrc;

	pSrc = pSrcStr;
	iLen = p_strlen_x(pSrc);
	if (!iLen || (iLen & 1))	return 0;
	
	for (i = 0, j = 0; i < iLen; i += 2)
	{
		pDst[j] = (UCHAR)(_Char2Hex(pSrc[i]) << 4) + _Char2Hex(pSrc[i+1]);
		j ++;
	}
	return j;
}

BOOLEAN _CmpSdram(UCHAR iDst, UCHAR iSrc)
{
	DCHAR iLen1, iLen2;

	iLen1 = SdramReadData((USHORT)(PROTOCOL_SDRAM_POS+iSrc));
	p_memcpy_x2x((PXCHAR)Sdram_pDataCache2, (PXCHAR)Sdram_pDataCache, iLen1);
	iLen2 = SdramReadData((USHORT)(PROTOCOL_SDRAM_POS+iDst));
	if (iLen1 != iLen2)	
		return FALSE;
	if (p_memcmp_x2x((PXCHAR)Sdram_pDataCache2, (PXCHAR)Sdram_pDataCache, iLen1))
		return FALSE;
	return TRUE;
}

void _InitMgRand()
{
	p_rand_array((PXCHAR)Sdram_pDataCache, CNC_MGRAND_LEN);
	SdramWriteData(PROTOCOL_SDRAM_POS+MGCP_CNC_MGRAND, CNC_MGRAND_LEN);
}

// assume a = 1 for now, so MGDH = 2, KEY-MG = CADH
// MgDH = (g exp a) mod p
void _InitMGDH()
{
	X_VAL(Sdram_pDataCache, 0) = 2;
	SdramWriteData(PROTOCOL_SDRAM_POS+MGCP_CNC_MGDH, 1);
}

//MG-AUTH = MD5(Ki, MGID, MgDH, MgRand)
void _CalcMG_AUTH()
{
	DCHAR iTotalLen, iLen;

	iTotalLen = p_strlen_c(_cKi);
	p_memcpy_c2x((PXCHAR)Sdram_pDataCache2, _cKi, iTotalLen);
	iLen = p_strlen_c(_cMGID);
	p_memcpy_c2x((PXCHAR)(Sdram_pDataCache2 + iTotalLen), _cMGID, iLen);
	iTotalLen += iLen;
	iLen = SdramReadData(PROTOCOL_SDRAM_POS+MGCP_CNC_MGDH);
	p_memcpy_x2x((PXCHAR)(Sdram_pDataCache2 + iTotalLen), (PXCHAR)Sdram_pDataCache, iLen);
	iTotalLen += iLen;
	iLen = SdramReadData(PROTOCOL_SDRAM_POS+MGCP_CNC_MGRAND);
	p_memcpy_x2x((PXCHAR)(Sdram_pDataCache2 + iTotalLen), (PXCHAR)Sdram_pDataCache, iLen);
	iTotalLen += iLen;
	MD5GenValue((PXCHAR)Sdram_pDataCache, (PXCHAR)Sdram_pDataCache2, iTotalLen);
	SdramWriteData(PROTOCOL_SDRAM_POS+MGCP_CNC_MGAUTH, MD5_SIGNATURE_SIZE);
}

// KEY-MG = (CaDH exp a) mod p
void _CalcKEY_MG()
{
	DCHAR iLen;
	
	iLen = SdramReadData(PROTOCOL_SDRAM_POS+MGCP_CNC_CADH);
	SdramWriteData(PROTOCOL_SDRAM_POS+MGCP_CNC_KEYMG, iLen);
}

// SS-RES = MD5(KEY-MG, Ki, CaDH, CaRand)
void _CalcSS_RES()
{
	DCHAR iTotalLen, iLen;

	iTotalLen = SdramReadData(PROTOCOL_SDRAM_POS+MGCP_CNC_KEYMG);
	p_memcpy_x2x((PXCHAR)Sdram_pDataCache2, (PXCHAR)Sdram_pDataCache, iTotalLen);
	iLen = p_strlen_c(_cKi);
	p_memcpy_c2x((PXCHAR)Sdram_pDataCache2, _cKi, iLen);
	iTotalLen += iLen;
	iLen = SdramReadData(PROTOCOL_SDRAM_POS+MGCP_CNC_CADH);
	p_memcpy_x2x((PXCHAR)(Sdram_pDataCache2 + iTotalLen), (PXCHAR)Sdram_pDataCache, iLen);
	iTotalLen += iLen;
	iLen = SdramReadData(PROTOCOL_SDRAM_POS+MGCP_CNC_CARAND);
	p_memcpy_x2x((PXCHAR)(Sdram_pDataCache2 + iTotalLen), (PXCHAR)Sdram_pDataCache, iLen);
	iTotalLen += iLen;
	MD5GenValue((PXCHAR)Sdram_pDataCache, (PXCHAR)Sdram_pDataCache2, iTotalLen);
	SdramWriteData(PROTOCOL_SDRAM_POS+MGCP_CNC_SSRES, MD5_SIGNATURE_SIZE);
}

// MD5(KEY-MG, MGID, CaRand)
void _CalcRegAuth()
{
	DCHAR iTotalLen, iLen;

	iTotalLen = SdramReadData(PROTOCOL_SDRAM_POS+MGCP_CNC_KEYMG);
	p_memcpy_x2x((PXCHAR)Sdram_pDataCache2, (PXCHAR)Sdram_pDataCache, iTotalLen);
	iLen = p_strlen_c(_cMGID);
	p_memcpy_c2x((PXCHAR)(Sdram_pDataCache2 + iTotalLen), _cMGID, iLen);
	iTotalLen += iLen;
	iLen = SdramReadData(PROTOCOL_SDRAM_POS+MGCP_CNC_CARAND);
	p_memcpy_x2x((PXCHAR)(Sdram_pDataCache2 + iTotalLen), (PXCHAR)Sdram_pDataCache, iLen);
	iTotalLen += iLen;
	MD5GenValue((PXCHAR)Sdram_pDataCache, (PXCHAR)Sdram_pDataCache2, iTotalLen);
	SdramWriteData(PROTOCOL_SDRAM_POS+MGCP_CNC_MGAUTH, MD5_SIGNATURE_SIZE);
}

void CncAuthInit()
{
	SdramWriteData(PROTOCOL_SDRAM_POS+MGCP_CNC_CADH, 0);
	SdramWriteData(PROTOCOL_SDRAM_POS+MGCP_CNC_CAAUTH, 0);
	SdramWriteData(PROTOCOL_SDRAM_POS+MGCP_CNC_CARAND, 0);
	_bRecvCaDH = FALSE;
	_InitMgRand();
	_InitMGDH();
	_CalcMG_AUTH();
}	

void CncSendRSIP()
{
	DCHAR iLen;

	CncAuthInit();

	MgcpCommandLine(MGCP_VERB_RSIP, TRUE);

	p_line_add_c((PCCHAR)"RM: restart");
	p_line_add_newline();

	p_line_add_c((PCCHAR)"X-EA: 1");
	p_line_add_newline();

	p_line_add_c((PCCHAR)"X-RANDOM");
	p_line_add_colon();
	iLen = SdramReadData(PROTOCOL_SDRAM_POS+MGCP_CNC_MGRAND);
	p_char2asc_str(p_line_get_buf(), (PXCHAR)Sdram_pDataCache, iLen, TRUE);
	p_line_update_len();
	p_line_add_newline();

	p_line_add_c((PCCHAR)"X-DH");
	p_line_add_colon();
	iLen = SdramReadData(PROTOCOL_SDRAM_POS+MGCP_CNC_MGDH);
	p_char2asc_str(p_line_get_buf(), (PXCHAR)Sdram_pDataCache, iLen, TRUE);
	p_line_update_len();
	p_line_add_newline();

	p_line_add_c((PCCHAR)"X-AUTH");
	p_line_add_colon();
	iLen = SdramReadData(PROTOCOL_SDRAM_POS+MGCP_CNC_MGAUTH);
	p_char2asc_str(p_line_get_buf(), (PXCHAR)Sdram_pDataCache, iLen, TRUE);
	p_line_update_len();
	p_line_add_newline();

	MgcpSendReq(MGCP_VERB_RSIP);
}

void CncSendNTFY()
{
	DCHAR iLen;

	MgcpCommandLine(MGCP_VERB_NTFY, TRUE);
	MgcpCopyFromSdram(MGCP_PARAM_X);

	p_line_add_c((PCCHAR)"O: auth/authoc(");
	iLen = SdramReadData(PROTOCOL_SDRAM_POS+MGCP_CNC_MGAUTH);
	p_char2asc_str(p_line_get_buf(), (PXCHAR)Sdram_pDataCache, iLen, TRUE);
	p_line_update_len();
	p_line_add_c((PCCHAR)",1)");
	p_line_add_newline();

	MgcpSendReq(MGCP_VERB_NTFY);
}

void _GetAuthEvent(PXCHAR pData)
{
	PXCHAR_D pCur;
	PXCHAR_D pParam;
	DCHAR iLen;

	pCur = pData;
	pParam = p_strchr_x(pCur, '(');
	if (!pParam)	return;
	pParam[0] = 0;
	
	if (!p_strcmp_c2x(pCur, (PCCHAR)"auth/dh"))
	{
		pCur = pParam;
		pCur ++;
		pParam = p_strchr_x(pCur, ')');
		if (pParam)		pParam[0] = 0;
		iLen = _Str2Hex((PXCHAR)Sdram_pDataCache, pCur);
		if (iLen)	SdramWriteData(PROTOCOL_SDRAM_POS+MGCP_CNC_CADH, iLen);
		_bRecvCaDH = TRUE;
	}
	else if (!p_strcmp_c2x(pCur, (PCCHAR)"auth/authreq"))
	{
		// authreq(key, ea, random)
		pCur = pParam;
		pCur ++;
		pParam = p_strchr_x(pCur, ',');
		if (!pParam)	return;
		pParam[0] = 0;
		iLen = _Str2Hex((PXCHAR)Sdram_pDataCache, pCur);
		if (iLen)	SdramWriteData(PROTOCOL_SDRAM_POS+MGCP_CNC_CAAUTH, iLen);

		pCur = pParam;
		pCur ++;
		pParam = p_strchr_x(pCur, ',');
		if (!pParam)	return;

		pCur = pParam;
		pCur ++;
		pParam = p_strchr_x(pCur, ')');
		if (pParam)		pParam[0] = 0;
		iLen = _Str2Hex((PXCHAR)Sdram_pDataCache, pCur);
		if (iLen)	SdramWriteData(PROTOCOL_SDRAM_POS+MGCP_CNC_CARAND, iLen);
	}
}

void CncHandleS()
{
	PXCHAR_D pCur;
	PXCHAR_D pTemp;

	if (!SdramReadString(PROTOCOL_SDRAM_POS+MGCP_RECV_S_LO))	
		return;

	_bRecvCaDH = FALSE;
	p_strcpy_x2x((PXCHAR)Sdram_pDataCache2, (PXCHAR)Sdram_pDataCache);
	pCur = (PXCHAR)(Sdram_pDataCache2 + 2);
	pCur += p_count_space(pCur);
	do
	{
		pTemp = _StrchrBrkt_x(pCur, ',');
		if (pTemp)	pTemp[0] = 0;
		_GetAuthEvent(pCur);
		if (pTemp)
		{
			pCur = pTemp;
			pCur ++; // pass ','
			pCur += p_count_space(pCur);
		}
	}while (pTemp);

	if (_bRecvCaDH)
	{
		//软交换下传DH
		_CalcKEY_MG();
		_CalcSS_RES();
		if (_CmpSdram(MGCP_CNC_SSRES, MGCP_CNC_CAAUTH))
		{
//			DebugCString((PCCHAR)"Unauthorized CA");
			OutputString(IDS_MGCP_UNAUTHORIZED_CA);
		}
		MgcpSendResp(200);
	}
	else
	{
		//软交换定期鉴权
		_CalcRegAuth();
		MgcpSendResp(200);
		CncSendNTFY();
	}
	Cnc_bReqAuthoc = FALSE;
}
