/*
* Copyright (c) 2005-2006 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:  A port of gnu md.c useful functions to Symbian.
*
*/


/** A port of gnu md.c useful functions to Symbian **/

#include <stdlib.h>
#include <hash.h>
#include "xmlsecc_config.h"
#include "xmlsecc_cryptowrapper.h"
#include "xmlsec_error_flag.h"


/* This object is used to hold a handle to a message digest object.
   This structure is private - only to be used by the public sc_md_*
   macros.  */
typedef struct sc_md_handle 
{
  /* Actual context.  */
  CMessageDigest *digest;
  
  /* Buffer management.  */
  HBufC8 *hash;
  
  /* Indicate support for HMAC */
  int hmac;

} *sc_md_hd_t;

/****************
 * Open a message digest handle for use with algorithm ALGO.
 * More algorithms may be added by md_enable(). The initial algorithm
 * may be 0.
 */

static TInt
md_open (sc_md_hd_t *h, int algo, int secure, int hmac)
{
  TInt err = KErrNone;
  //int bufsize = secure ? 512 : 1024;
  sc_md_hd_t hd;
  hd = (sc_md_hd_t)malloc (sizeof (struct sc_md_handle));
  if (! hd)
    err = KErrNoMemory;
  if (! err)
    {
      /* Setup the globally visible data (bctl in the diagram).*/
      switch(algo)
      {
      	case SC_MD_SHA1:
      		TRAP(err, hd->digest = CSHA1::NewL()); 
      		hd->hash = NULL;
      		break;
      	default:
      		free(hd);
			return KErrNotSupported;	  	      		      	
      }
  	  //HMAC
  	  hd->hmac = hmac;
    }
  if (! err)
  {
    *h = hd; 
  }
  else
  {
    free(hd);  	
  }
  return err;
}

/* Create a message digest object for algorithm ALGO.  FLAGS may be
   given as an bitwise OR of the sc_md_flags values.  ALGO may be
   given as 0 if the algorithms to be used are later set using
   sc_md_enable. H is guaranteed to be a valid handle or NULL on
   error.  */
TInt
sc_md_open (sc_md_hd_t *h, int algo, unsigned int flags)
{
  TInt err = KErrNone;
  sc_md_hd_t hd=NULL;

  if ((flags & ~(SC_MD_FLAG_SECURE | SC_MD_FLAG_HMAC)))
    err = KErrArgument;
  else
    {

      err = md_open (&hd, algo, (flags & SC_MD_FLAG_SECURE),
		     (flags & SC_MD_FLAG_HMAC));

    }

  *h = err? NULL : hd;
  return err;
}

/****************
 * Return the length of the digest in bytes.
 * This function will return 0 in case of errors.
 */
unsigned int
sc_md_get_algo_dlen (sc_md_hd_t hd)
{

  TInt len = hd->digest->HashSize();
  
  return len;
}

const byte *
md_read(sc_md_hd_t hd)
{
	TRAPD(leaveValue,hd->hash = HBufC8::NewL(hd->digest->HashSize()));
	if (leaveValue != KErrNone)
	    {
	    xmlSecSetErrorFlag( leaveValue );
	    return NULL;
	    }	
	*(hd->hash)=hd->digest->Final();
	return hd->hash->Ptr();
}

/*
 * Read out the complete digest, this function implictly finalizes
 * the hash.
 */
const byte *
sc_md_read (sc_md_hd_t hd, int /*algo*/)
{
  const byte *ret;

  if (hd->hash)
  {
  	delete hd->hash;
  	hd->hash = NULL;
  } 
  ret = md_read (hd);
  return ret;
}

void
sc_md_write (sc_md_hd_t hd, unsigned char *inbuf, size_t inlen)
{

  TPtrC8 inbufPtr(inbuf, inlen);
  hd->digest->Update(inbufPtr);
  
}

void
sc_md_close (sc_md_hd_t hd)
{

  if (hd->digest)
  {
  	delete hd->digest;
  	hd->digest = NULL;  	
  }

  if (hd->hash)
  {
  	delete hd->hash;
  	hd->hash = NULL;
  }
  
  free (hd);
}

void
sc_md_final (sc_md_hd_t /*a*/)
{
 	// Do nothing, sc_md_read will call final()
}


/* Set key for HMAC */
int sc_md_setkey(sc_md_hd_t hd, unsigned char *buffer, size_t length)
{
  TInt err;
  
  if (!hd || !buffer || length <=0)
  	return KErrArgument;
  
  TPtrC8 keyPtr(buffer, length);
   
  if (hd->hmac) 
  {
  	// Initialize HMAC
  	 TRAP(err, hd->digest = CHMAC::NewL(keyPtr, hd->digest));
  	 if ( err != KErrNone )
  	    {
  	    xmlSecSetErrorFlag( err );
  	    }
  }  		 		
  
  return err;
}
