/*******************************************************************

Copyright (c) 2004 Open Mobile Alliance, Ltd.  All Rights Reserved.

This software is made available under the usage and distribution terms 
covered by the Common Public License 1.0 and by using this software you
are bound by the terms and conditions of the same.

EXCEPT AS EXPRESSLY SET FORTH IN THE COMMON PUBLIC LICENSE,
THIS PROGRAM IS PROVIDED ON AN "AS IS" BASIS, 
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 
EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, 
ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, 
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
   
Each recipient is solely responsible for determining the 
appropriateness of using and distributing this program and assumes 
all risks associated with its exercise of rights under the Common 
Public License, including but not limited to the risks and 
costs of program errors, compliance with applicable laws, 
damage to or loss of data, programs or equipment, and 
unavailability or interruption of operations.

********************************************************************/

#include "SctsAuth.h"
#include "SctsErrorLog.h"

#ifndef __SCTS_USING_RTK__

/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
 */

/* 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.

These notices must be retained in any copies of any part of this
documentation and/or software.
 */


/* Constants for MD5Transform routine.
 */

#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21

static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
static void Encode PROTO_LIST
  ((unsigned char *, UINT4 *, unsigned int));
static void Decode PROTO_LIST
  ((UINT4 *, unsigned char *, unsigned int));
static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));
static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int));

static unsigned char 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
};

/* F, G, H and I are basic MD5 functions.
 */
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))

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

/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
Rotation is separate from addition to prevent recomputation.
 */
#define FF(a, b, c, d, x, s, ac) { \
 (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = ROTATE_LEFT ((a), (s)); \
 (a) += (b); \
  }
#define GG(a, b, c, d, x, s, ac) { \
 (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = ROTATE_LEFT ((a), (s)); \
 (a) += (b); \
  }
#define HH(a, b, c, d, x, s, ac) { \
 (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = ROTATE_LEFT ((a), (s)); \
 (a) += (b); \
  }
#define II(a, b, c, d, x, s, ac) { \
 (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = ROTATE_LEFT ((a), (s)); \
 (a) += (b); \
  }

/* MD5 initialization. Begins an MD5 operation, writing a new context.
 */
void MD5Init (context)
MD5_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 MD5Update (context, input, inputLen)
MD5_CTX *context;                                        /* context */
char *input;                                /* input block */
unsigned int 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] += ((UINT4)inputLen << 3))
   < ((UINT4)inputLen << 3))
 context->count[1]++;
  context->count[1] += ((UINT4)inputLen >> 29);

  partLen = 64 - index;

  /* Transform as many times as possible.
*/
  if (inputLen >= partLen) {
 MD5_memcpy
   ((POINTER)&context->buffer[index], (POINTER)input, partLen);
 MD5Transform (context->state, context->buffer);

 for (i = partLen; i + 63 < inputLen; i += 64)
   MD5Transform (context->state, (unsigned char *)&input[i]);

 index = 0;
  }
  else
 i = 0;

  /* Buffer remaining input */
  MD5_memcpy
 ((POINTER)&context->buffer[index], (POINTER)&input[i],
  inputLen-i);
}

/* MD5 finalization. Ends an MD5 message-digest operation, writing the
  the message digest and zeroizing the context.
 */
void MD5Final (digest, context)
char digest[16];                         /* message digest */
MD5_CTX *context;                                       /* context */
{
  unsigned char bits[8];
  unsigned int index, padLen;

  /* Save number of bits */
  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);
  MD5Update (context, (char *)PADDING, padLen);

  /* Append length (before padding) */
  MD5Update (context, (char *)bits, 8);
  /* Store state in digest */
  Encode ((unsigned char *)digest, context->state, 16);

  /* Zeroize sensitive information.
*/
  MD5_memset ((POINTER)context, 0, sizeof (*context));
}

/* MD5 basic transformation. Transforms state based on block.
 */
static void MD5Transform (state, block)
UINT4 state[4];
unsigned char block[64];
{
  UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];

  Decode (x, block, 64);

  /* Round 1 */
  FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
  FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
  FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
  FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
  FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
  FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
  FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
  FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
  FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
  FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
  FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
  FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
  FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
  FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
  FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
  FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */

 /* Round 2 */
  GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
  GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
  GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
  GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
  GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
  GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
  GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
  GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
  GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
  GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
  GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
  GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
  GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
  GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
  GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
  GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */

  /* Round 3 */
  HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
  HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
  HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
  HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
  HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
  HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
  HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
  HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
  HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
  HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
  HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
  HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
  HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
  HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
  HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
  HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */

  /* Round 4 */
  II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
  II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
  II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
  II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
  II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
  II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
  II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
  II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
  II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
  II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
  II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
  II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
  II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
  II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
  II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
  II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */

  state[0] += a;
  state[1] += b;
  state[2] += c;
  state[3] += d;

  /* Zeroize sensitive information.*/
  MD5_memset ((POINTER)x, 0, sizeof (x));
}

/* Encodes input (UINT4) into output (unsigned char). Assumes len is
  a multiple of 4.
 */
static void Encode (output, input, len)
unsigned char *output;
UINT4 *input;
unsigned int 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 (UINT4). Assumes len is
  a multiple of 4.
 */
static void Decode (output, input, len)
UINT4 *output;
unsigned char *input;
unsigned int len;
{
  unsigned int i, j;

  for (i = 0, j = 0; j < len; i++, j += 4)
 output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
   (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
}

/* Note: Replace "for loop" with standard memcpy if possible.
 */

static void MD5_memcpy (output, input, len)
POINTER output;
POINTER input;
unsigned int len;
{
  unsigned int i;

  for (i = 0; i < len; i++)
    output[i] = input[i];
}

/* Note: Replace "for loop" with standard memset if possible.
 */
static void MD5_memset (output, value, len)
POINTER output;
int value;
unsigned int len;
{
  unsigned int i;

  for (i = 0; i < len; i++)
 ((char *)output)[i] = (char)value;
}

#endif

/* Base 64 decoding array */
unsigned char base64DecodeChars[]=
{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,62,0xFF,0xFF,0xFF,63,52,53,54,55,56,57,
58,59,60,61,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
16,17,18,19,20,21,22,23,24,25,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,26,27,28,29,30,31,32,33,
34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};

/* Base 64 encoding array */
char base64EncodeChars[]=	"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
							"abcdefghijklmnopqrstuvwxyz"
							"0123456789+/"
							"=";
/*****************************************************************
Function	: Base 64 encodes a string
In			: char *
			  The string to encode
In/Out		: int& 
			  The size of the input string/ouput string
Out			: char *
			  The encoded string 
*****************************************************************/
char *sctsBase64Encode(unsigned char *source,unsigned int& sourceSize)
{
	char *target,*targetPos;
	unsigned char *sourcePos;
	int targetSize=sourceSize%3;
	char c1,c2,c3,c4;
	if (!source || !sourceSize) return 0;
	switch (targetSize)
	{
	case 0:
		targetSize=sourceSize/3<<2;
		target=(char *)sctsMalloc((targetSize+1)*sizeof(char));
		if (!target)
		{
			sourceSize=0;
			return 0;
		}
		break;
	case 1:
		targetSize=(sourceSize+2)/3<<2;
		target=(char *)sctsMalloc((targetSize+1)*sizeof(char));
		if (!target)
		{
			sourceSize=0;
			return 0;
		}
		targetPos=target+targetSize-4;
		sourcePos=source+sourceSize-1;
		c1=base64EncodeChars[*sourcePos>>2];
		c2=base64EncodeChars[(*sourcePos&0x03)<<4];
		c3='=';
		c4='=';
		*targetPos++=c1;
		*targetPos++=c2;
		*targetPos++=c3;
		*targetPos++=c4;
		--sourceSize;
		break;
	case 2:
		targetSize=(sourceSize+1)/3<<2;
		target=(char *)sctsMalloc((targetSize+1)*sizeof(char));
		if (!target)
		{
			sourceSize=0;
			return 0;
		}
		targetPos=target+targetSize-4;
		sourcePos=source+sourceSize-2;
		c1=base64EncodeChars[*sourcePos>>2];
		c2=(char)((*sourcePos++&0x03)<<4);
		c2=base64EncodeChars[c2|(*sourcePos>>4)];
		c3=base64EncodeChars[(*sourcePos&0x0F)<<2];
		c4='=';
		*targetPos++=c1;
		*targetPos++=c2;
		*targetPos++=c3;
		*targetPos++=c4;
		sourceSize-=2;
		break;
	}
	target[targetSize]=0;
	sourcePos=source;
	targetPos=target;
	while (sourceSize)
	{
		c1=base64EncodeChars[*sourcePos>>2];
		c2=(char)((*sourcePos++&0x03)<<4);
		c2=base64EncodeChars[c2|(*sourcePos>>4)];
		c3=(char)((*sourcePos++&0x0F)<<2);
		c3=base64EncodeChars[c3|(*sourcePos>>6)];
		c4=base64EncodeChars[*sourcePos++&0x3F];
		*targetPos++=c1;
		*targetPos++=c2;
		*targetPos++=c3;
		*targetPos++=c4;
		sourceSize-=3;
	}
	sourceSize=targetSize;
	return target;
}

/*****************************************************************
Function	: Decoded a base 64 string
In			: char *
			  The string to decode
In/Out		: int& 
			  The size of the input string/ouput string
Out			: char *
			  The decoded string 
*****************************************************************/
unsigned char *sctsBase64Decode(char *source,unsigned int& sourceSize)
{
	unsigned char *target,*targetPos;
	unsigned char data;
	char bits=0;
	char *sourcePos=source;
	if (!source || !sourceSize) return 0;
	target=(unsigned char *)sctsMalloc(sizeof(unsigned char)*sourceSize);
	if (!target)
	{
		sourceSize=0;
		return 0;
	}
	targetPos=target;
	while (sourceSize-- && *sourcePos!='=')
	{
		data=base64DecodeChars[*sourcePos++];
		if (data==0xFE) continue;
		if (data==0xFF)
		{
			targetPos=0;
			break;
		}
		switch (bits)
		{
		case 0:
			*targetPos=data<<2;
			bits=6;
			break;
		case 2:
			*targetPos=*targetPos|data;
			++targetPos;
			bits=0;
			break;
		case 4:
			*targetPos=*targetPos|(data>>2);
			++targetPos;
			*targetPos=data<<6;
			bits=2;
			break;
		case 6:
			*targetPos=*targetPos|(data>>4);
			++targetPos;
			*targetPos=data<<4;
			bits=4;
			break;
		}
	}
	if (!targetPos)
	{
		sctsFree(target);
		return 0;
	}
	sourceSize=targetPos-target;
	return (unsigned char *)sctsRealloc(target,sourceSize);
}

/*****************************************************************
SctsAuth Definition
******************************************************************/
int SctsAuth::VerifyCred(SctsCred& cred,char *userID,char *password,SctsEncoding encoding)
{
	SctsMeta meta;
	SctsAuthType authType=AUTH_BASIC;
	SctsPcdata tempPcdata;
	bool isBase64=false;
	unsigned int _size;
	char *credData;
	if (!cred) return 407;
	/* Get the authentication scheme */
	cred.GetMeta(meta);
	meta.GetType(tempPcdata);
	if (tempPcdata)
	{
		if (tempPcdata=="syncml:auth-md5")
			authType=AUTH_MD5;
		else 
		{
			if (!(tempPcdata=="syncml:auth-basic"))
				authType=AUTH_NONE;
		}
	}
	/* Check whether the scheme is valid */
	if (authType==AUTH_NONE)
	{
		SctsErrorLog::AppendError("Cred->Meta->Type must be either 'syncm:auth-basic' or 'syncml:auth-md5', if absent it implies 'syncml:auth-basic'.");
		return 0;
	}
	/* Check whether it is the appropriate scheme */
	if (GetType()==AUTH_BASIC && authType==AUTH_MD5)
	{
		return 401;
	}
	if (GetType()==AUTH_MD5 && authType==AUTH_BASIC)
	{
		return 401;
	}
	/* Check whether the meta->format is correct */
	if (meta && meta.GetFormat(tempPcdata)=="b64") isBase64=true;
	/* V1.1.2 */
	if (authType!=AUTH_BASIC && !isBase64)
	{
		SctsErrorLog::AppendError("Cred->Meta->Format element is not 'b64'. Representation protocol (5.1.2) : When using syncml:auth-md5 or syncml:auth-MAC, the Meta Format for the NextNonce element MUST be specified and it MUST be b64");
		return 0;
	}
	/* Check whether the data element is present */
	if (!cred.GetData(tempPcdata)) 
	{
		SctsErrorLog::AppendError("Cred->Data element missing/invalid.");
		return 0;
	}
	if (!userID || !password) return 200;
	/* Base64 decode the credential string if necessary */
	_size=tempPcdata.GetLength();
	if (isBase64 && authType!=AUTH_BASIC)
		credData=(char *)sctsBase64Decode((char *)tempPcdata,_size);
	else
		credData=(char *)tempPcdata;
	/* Verify the credential string */
	if (!VerifyAuthString(userID,password,credData,_size))
	{
	//	char buffer[200];
		if (isBase64 && authType!=AUTH_BASIC) sctsFree(credData);
	/* 	sprintf(buffer,"Incorrect credentials (Expected - User ID : %s, Password : %s).",userID,password);
		SctsErrorLog::AppendError(buffer); */
		return 401;
	}
	if (isBase64 && authType!=AUTH_BASIC) sctsFree(credData);
	return 200;
}

void SctsAuth::Save(FILE *fp)
{
	/* 
	Write the auth type, based on which an object of the appropriate class
	can be constructed at load time
	*/
	fwrite(&type,sizeof(SctsAuthType),1,fp);
}

SctsAuth *SctsAuth::Load(FILE *fp)
{
	SctsAuthType type;
	SctsAuth *auth;
	/* Read the auth type and construct and object of the appropriate class */
	fread(&type,sizeof(SctsAuthType),1,fp);
	switch(type)
	{
	case AUTH_BASIC:
		auth=new SctsAuthBasic();
		break;
	case AUTH_MD5:
		auth=new SctsAuthMD5(fp);
		break;
	case AUTH_HMAC:
		auth=new SctsAuthHMAC(fp);
		break;
	default:
		return 0;
	}
	auth->type=type;
	return auth;
}

/*****************************************************************
SctsAuthBasic Definition
******************************************************************/
SctsAuthBasic::SctsAuthBasic() : SctsAuth(AUTH_BASIC) {};

SctsCred *SctsAuthBasic::BuildCred(char *userID,char *password,SctsEncoding encoding)
{
	SctsCred *pCred;
	SctsMeta meta;
	char *authString;
	unsigned int size;
	/* Build the credential string */
	authString=BuildAuthString(userID,password,size);
	if (!authString) return 0;
	/* Set the meta->type element */
	meta.SetType(&SctsPcdata("syncml:auth-basic"));
	/* Set the meta->format element */
	meta.SetFormat(&SctsPcdata("b64"));
	/* Construct the credential object */
	pCred=new SctsCred(&meta,&SctsPcdata(authString));
	sctsFree(authString);
	return pCred;
}

SctsChal *SctsAuthBasic::BuildChal(SctsEncoding encoding)
{
	SctsChal *pChal=new SctsChal();
	if (!pChal) return 0;
	if (encoding==XML) pChal->SetFormat(&SctsPcdata("b64"));
	pChal->SetType(&SctsPcdata("syncml:auth-basic"));
	return pChal;
}

char *SctsAuthBasic::BuildAuthString(char *userID,char *password,unsigned int& size)
{
	char buffer[MAX_USERID_SIZE+MAX_PASSWORD_SIZE+1];
	if (!userID || !password) return 0;
	if (sctsStrlen(userID) > MAX_USERID_SIZE || sctsStrlen(password) > MAX_PASSWORD_SIZE) return 0;
	/* Contruct auth string - userID:password */
	sctsStrcpy(buffer,userID);
	sctsStrcat(buffer,":");
	sctsStrcat(buffer,password);
	size=sctsStrlen(buffer);
	/* Base64 encode auth string */
	return sctsBase64Encode((unsigned char *)buffer,size);
}

bool SctsAuthBasic::VerifyAuthString(char *userID,char *password,char *credential,unsigned int _size)
{
	bool verified=true;
	char *_credential;
	if (!userID || !password) return true;
	if (sctsStrlen(userID) > MAX_USERID_SIZE || sctsStrlen(password) > MAX_PASSWORD_SIZE) return false;
	if (!credential) return false;
	/* Construct the base64 encoded auth string and compare it to the received credential string */
	_credential=BuildAuthString(userID,password,_size);
	SCTSASSERT(_credential);
	if (sctsStrlen(credential)!=_size || sctsMemcmp(credential,_credential,_size)) verified=false;
	sctsFree(_credential);
	return verified;
}

/*****************************************************************
SctsAuthMD5 Definition
******************************************************************/
SctsAuthMD5::SctsAuthMD5(char *_nonce) : SctsAuth(AUTH_MD5)
{
	SCTSASSERT(_nonce);
	size=sctsStrlen(_nonce);
	nonce=sctsBase64Decode(_nonce,size);
	SCTSASSERT(nonce && size);
}

SctsAuthMD5::SctsAuthMD5(unsigned char *_nonce,unsigned int _size) : SctsAuth(AUTH_MD5)
{
	SCTSASSERT(_nonce && _size);
	nonce=(unsigned char *)sctsMalloc(_size);
	sctsMemcpy(nonce,_nonce,_size);
	size=_size;
}

SctsCred *SctsAuthMD5::BuildCred(char *userID,char *password,SctsEncoding encoding)
{
	SctsCred *pCred;
	SctsMeta meta;
	unsigned int _size;
	char *authString;
	/* Build the credential string */
	authString=BuildAuthString(userID,password,_size);
	if (!authString) return 0;
	/* Set the meta->type element */
	meta.SetType(&SctsPcdata("syncml:auth-md5"));
	/* 
	Set the meta->format element. If the encoding is XML base64 encode the digest,
	otherwise in WBXML send it as opaque data
	*/
	/* V1.1.2 */
	char *b64AuthString=sctsBase64Encode((unsigned char *)authString,_size);
	sctsFree(authString);
	authString=b64AuthString;
	_size=0;
	meta.SetFormat(&SctsPcdata("b64"));
	pCred=new SctsCred(&meta,&SctsPcdata(authString));
	sctsFree(authString);
	return pCred;
}

SctsChal *SctsAuthMD5::BuildChal(SctsEncoding encoding)
{
	SctsChal *pChal=new SctsChal();
	char *_nonce;
	unsigned int _size=size;
	if (!pChal) return 0;
	/* Always base64 encode the nonce and set the meta->format regardless of the encoding */
	pChal->SetFormat(&SctsPcdata("b64"));
	pChal->SetType(&SctsPcdata("syncml:auth-md5"));
	_nonce=sctsBase64Encode(nonce,_size);
	pChal->SetNextNonce(&SctsPcdata(_nonce));
	sctsFree(_nonce);
	return pChal;
}

int SctsAuthMD5::VerifyCred(SctsCred& cred,char *userID,char *password,SctsEncoding encoding)
{
	int ret;
	ret=SctsAuth::VerifyCred(cred,userID,password,encoding);
	/* Generate new nonce */
	sctsFree(nonce);
	nonce=(unsigned char *)sctsMalloc(sizeof(SctsCred *));
	sctsMemcpy(nonce,&cred,sizeof(SctsCred *));
	size=sizeof(SctsCred *)/sizeof(char);
	return ret;
}

char *SctsAuthMD5::BuildAuthString(char *userID,char *password,unsigned int& _size)
{
	char buffer[MAX_USERID_SIZE+MAX_PASSWORD_SIZE+MAX_NONCE_SIZE+3];
	MD5_CTX md5Context;
	char md5Digest[16];
	char *digest;
	if (!nonce || !userID || !password) return 0;
	if (sctsStrlen(userID) > MAX_USERID_SIZE || sctsStrlen(password) > MAX_PASSWORD_SIZE) return false;
	/* Construct the auth string - userID:password */
	sctsStrcpy(buffer,userID);
	sctsStrcat(buffer,":");
	sctsStrcat(buffer,password);
	_size=sctsStrlen(buffer);
	/* Create the first digest */
	MD5Init(&md5Context);
	MD5Update(&md5Context,buffer,_size);
	MD5Final(md5Digest,&md5Context);
	_size=16;
	/* Base64 encode the digest, append :nonce and	create a new digest	*/
	digest=sctsBase64Encode((unsigned char *)md5Digest,_size);
	sctsStrcpy(buffer,digest);
	sctsFree(digest);
	buffer[_size]=':';
	++_size;
	sctsMemcpy(buffer+_size,nonce,size);
	_size+=size;
	MD5Init(&md5Context);
	MD5Update(&md5Context,buffer,_size);
	MD5Final(md5Digest,&md5Context);
	_size=16;

	/* Return the digest */
	digest=(char *)sctsMalloc(sizeof(char)*16);
	sctsMemcpy(digest,md5Digest,16);
	return digest;
}

bool SctsAuthMD5::VerifyAuthString(char *userID,char *password,char *credential,unsigned int _size)
{
	bool verified=true;
	char *_credential;
	if (!userID || !password) return true;
	if (sctsStrlen(userID) > MAX_USERID_SIZE || sctsStrlen(password) > MAX_PASSWORD_SIZE) return false;
	if (!credential) return false;
	/* Construct the digest and compare it to the received digest */
	if (_size!=16)
		verified=false;
	else
	{
		_credential=BuildAuthString(userID,password,_size);
		SCTSASSERT(_credential);
		if (sctsMemcmp(credential,_credential,_size)) verified=false;
		sctsFree(_credential);
	
	}
	return verified;
}

void SctsAuthMD5::Save(FILE *fp)
{
	/* Call the base class Save method */
	SctsAuth::Save(fp);
	/* Write the nonce */
	fwrite(&size,sizeof(int),1,fp);
	fwrite(nonce,sizeof(unsigned char),size,fp);
}

SctsAuthMD5::SctsAuthMD5(FILE *fp) : SctsAuth(AUTH_MD5)
{
	/* Read the nonce */
	fread(&size,sizeof(int),1,fp);
	nonce=(unsigned char *)sctsMalloc(size);
	fread(nonce,sizeof(unsigned char),size,fp);
}

/*****************************************************************
SctsAuthHMAC Definition
******************************************************************/

char *SctsAuthHMAC::BuildHMACDigest(char *userID,char *password,void *message,int messageLen)
{
	char buffer[300];
	MD5_CTX md5Context;
	char md5Digest[16];
	char *str;
	char *str1;
	unsigned int _size;
	if (!nonce || !userID || !password) return 0;

	/* Logging */
	sctsStatusLog("Building HMAC...\r\n");
	
	/* H(messageBody) */
	MD5Init(&md5Context);
	MD5Update(&md5Context,(char *)message,messageLen);
	MD5Final(md5Digest,&md5Context);
	_size=16;
	
	/* B64(H(messageBody) */
	str=sctsBase64Encode((unsigned char *)md5Digest,_size);
	messageLen=_size;

	/* Logging */
	sctsStatusLog("B64(H(messageBody)): ");	
	sctsStatusLog(str);
	sctsStatusLog("\r\n");

	/* H(username:password) */
	sctsStrcpy(buffer,userID);
	sctsStrcat(buffer,":");
	sctsStrcat(buffer,password);
	_size=sctsStrlen(buffer);

	MD5Init(&md5Context);
	MD5Update(&md5Context,(char *)buffer,_size);
	MD5Final(md5Digest,&md5Context);
	_size=16;

	/* B64(H(username:password)) */
	str1=sctsBase64Encode((unsigned char *)md5Digest,_size);
	
	/* Logging */
	sctsStatusLog("B64(H(username:password)): ");	
	sctsStatusLog(str1);
	sctsStatusLog("\r\n");

	/* B64(H(username:password)):nonce:B64(H(message body)) */
	sctsStrcpy(buffer,str1);
	sctsStrcat(buffer,":");
	_size++;
	sctsMemcpy(buffer+_size,nonce,size);
	_size+=size;
	sctsStrcpy(buffer+_size,":");
	_size++;
	sctsMemcpy(buffer+_size,str,messageLen);
	_size+=messageLen;

	/* Logging */
	sctsStatusLog("B64(H(username:password)):nonce:B64(H(message body)): ");	
	sctsStatusLog(buffer,_size);
	sctsStatusLog("\r\n");
	
	/* Interim Clean up */
	if (str1) sctsFree(str1);
	if (str) sctsFree(str);

	/* H(B64(H(username:password)):nonce:B64(H(message body))) */
	MD5Init(&md5Context);
	MD5Update(&md5Context,(char *)buffer,_size);
	MD5Final(md5Digest,&md5Context);
	_size=16;

	/* Return the base 64 of digest */
	str=sctsBase64Encode((unsigned char *)md5Digest,_size);	

	/* Logging */
	sctsStatusLog("B64(H(B64(H(username:password)):nonce:B64(H(message body)))): ");	
	sctsStatusLog(str,_size);
	sctsStatusLog("\r\n");
	
	return str;
}

bool SctsAuthHMAC::VerifyHMACDigest(char *userID,char *password,void *message,char *Digest,int len)
{
	bool flag;
	status=407;
	if (!userID || !password || !message || !Digest || !len) return false;
	char *digest=BuildHMACDigest(userID,password,message,len);
	flag=sctsStrcmp(digest,Digest)==0?true:false;
	sctsFree(digest);
	status=flag?200:401;
	return flag;
}

SctsChal *SctsAuthHMAC::BuildChal(SctsEncoding encoding)
{
	SctsChal *pChal=new SctsChal();
	char *_nonce;
	unsigned int _size=size;
	if (!pChal) return 0;
	/* Always base64 encode the nonce and set the meta->format regardless of the encoding */
	pChal->SetFormat(&SctsPcdata("b64"));
	pChal->SetType(&SctsPcdata("syncml:auth-MAC"));
	_nonce=sctsBase64Encode(nonce,_size);
	pChal->SetNextNonce(&SctsPcdata(_nonce));
	sctsFree(_nonce);
	return pChal;
}

SctsAuthHMAC::SctsAuthHMAC(char *_nonce) : SctsAuth(AUTH_HMAC)
{
	SCTSASSERT(_nonce);
	size=sctsStrlen(_nonce);
	nonce=sctsBase64Decode(_nonce,size);
	SCTSASSERT(nonce && size);
	status=407;
}

SctsAuthHMAC::SctsAuthHMAC(unsigned char *_nonce,unsigned int _size) : SctsAuth(AUTH_HMAC)
{
	SCTSASSERT(_nonce && _size);
	nonce=(unsigned char *)sctsMalloc(_size);
	sctsMemcpy(nonce,_nonce,_size);
	size=_size;
	status=401;
}

SctsAuthHMAC::SctsAuthHMAC() : SctsAuth(AUTH_HMAC)
{
	nonce=(unsigned char *)sctsStrdup("");
	size=0;
	status=500;
}

void SctsAuthHMAC::Save(FILE *fp)
{
	/* Call the base class Save method */
	SctsAuth::Save(fp);
	/* Write the nonce */
	fwrite(&size,sizeof(int),1,fp);
	fwrite(nonce,sizeof(unsigned char),size,fp);
}

SctsAuthHMAC::SctsAuthHMAC(FILE *fp) : SctsAuth(AUTH_HMAC)
{
	/* Read the nonce */
	fread(&size,sizeof(int),1,fp);
	nonce=(unsigned char *)sctsMalloc(size);
	fread(nonce,sizeof(unsigned char),size,fp);
	status=0;
}

int SctsAuthHMAC::VerifyCred(SctsCred& cred,char *userID,char *password,SctsEncoding encoding)
{
	if (!userID || !password) return 200;
	if (cred) return 401;
	return status;
}
