/* ========================================================================== */
/*! \file
 * \brief Keyed-Hash Message Authentication Code
 *
 * Copyright (c) 2012-2016 by the developers. See the LICENSE file for details.
 *
 * If nothing else is specified, function return zero to indicate success
 * and a negative value to indicate an error.
 */


/* ========================================================================== */
/* Include headers */

#include "posix.h"  /* Include this first because of feature test macros */

#include "config.h"

#if CFG_USE_TLS  /* This requires data from "config.h" */
#  include <openssl/evp.h>
#  include <openssl/hmac.h>
#endif  /* CFG_USE_TLS */
#include <string.h>

#include "hmac.h"
#include "main.h"


/* ========================================================================== */
/*! \addtogroup HMAC
 *
 * This module provides optional support for Keyed-Hash Message Authentication
 * Codes. It is intended to comply with RFC2104.
 *
 * \attention
 * This module currently works only if TLS support is available (because both
 * share the cryptographic functions provided by OpenSSL).
 */
/* @{ */


/* ========================================================================== */
/* Data types */

enum hmac_alg
{
   HMAC_ALG_SHA1_160,
   HMAC_ALG_SHA2_256
};


/* ========================================================================== */
/* Delegate digest calculation to OpenSSL */

static int  hmac_openssl(enum hmac_alg  alg, const char*  text,
                         size_t  text_len, const char*  key, size_t  key_len,
                         unsigned char*  mac)
{
   int  res = -1;
#if CFG_USE_TLS
   HMAC_CTX*  ctx = NULL;
   const EVP_MD*  hmac_id;
   size_t  mac_len_check = 1;  /* Value never used by real digest */
   unsigned int  mac_len = 0;

   /* Select digest algorithm */
   switch(alg)
   {
      case HMAC_ALG_SHA1_160:
      {
         hmac_id = EVP_sha1();
         mac_len_check = HMAC_SHA1_160_LEN;
         break;
      }
      case HMAC_ALG_SHA2_256:
      {
         hmac_id = EVP_sha256();
         mac_len_check = HMAC_SHA2_256_LEN;
         break;
      }
      default:
      {
         print_error("HMAC: Requested algorithm not supported");
         hmac_id = NULL;
         break;
      }
   }
   if(NULL != hmac_id)
   {
      /* Create context (even if it is currently not used) */
#if  CFG_USE_OPENSSL_API_1_1
      ctx = HMAC_CTX_new();
#else   /* CFG_USE_OPENSSL_API_1_1 */
      ctx = (HMAC_CTX*) posix_malloc(sizeof(HMAC_CTX));
#endif   /* CFG_USE_OPENSSL_API_1_1 */
      if(NULL == ctx)  { print_error("HMAC: Creating context failed"); }

      /* Check whether length values can be represented as data type 'int' */
      else if(!(POSIX_INT_MAX >= text_len && POSIX_INT_MAX >= key_len))
      {
         print_error("HMAC: Message or key is too long");
      }
      else
      {
         /* Delegate job to OpenSSL */
#if  CFG_USE_OPENSSL_API_1_1
         HMAC_CTX_reset(ctx);
#else   /* CFG_USE_OPENSSL_API_1_1 */
         HMAC_CTX_init(ctx);
#endif   /* CFG_USE_OPENSSL_API_1_1 */
         mac = HMAC(hmac_id, (void*) key, (int) key_len, (unsigned char*) text,
                    (int) text_len, (unsigned char*) mac, &mac_len);
         if(NULL != mac && mac_len_check == (size_t) mac_len)  { res = 0; }
      }
   }

   /* Destroy context */
   if(NULL != ctx)
   {
#if   CFG_USE_OPENSSL_API_1_1
      HMAC_CTX_free(ctx);
#else   /* CFG_USE_OPENSSL_API_1_1 */
      HMAC_CTX_cleanup(ctx);
      posix_free((void*) ctx);
#endif   /* CFG_USE_OPENSSL_API_1_1 */
   }
#endif  /* CFG_USE_TLS */

   return(res);
}


/* ========================================================================== */
/*! \brief Message Authentication Code based on SHA1-160 hash algorithm
 *
 * \param[in]  text      Message
 * \param[in]  text_len  Message length
 * \param[in]  key       Key
 * \param[in]  key_len   Key length
 * \param[out] mac       Result
 *
 * The values of paramaters \e text_len and \e key_len must represent bytes.
 *
 * On success, the result is written to \e mac and has a length of
 * \c HMAC_SHA1_160_LEN bytes.
 *
 * \return
 * - 0 on success
 * - -1 on error
 */

int  hmac_sha1_160(const char*  text, size_t  text_len,
                   const char*  key, size_t  key_len, unsigned char*  mac)
{
   return(hmac_openssl(HMAC_ALG_SHA1_160, text, text_len, key, key_len, mac));
}


/* ========================================================================== */
/*! \brief Message Authentication Code based on SHA2-256 hash algorithm
 *
 * \param[in]  text      Message
 * \param[in]  text_len  Message length
 * \param[in]  key       Key
 * \param[in]  key_len   Key length
 * \param[out] mac       Result
 *
 * The values of paramaters \e text_len and \e key_len must represent bytes.
 *
 * On success, the result is written to \e md and has a length of
 * \c HMAC_SHA2_256_LEN bytes.
 *
 * \return
 * - 0 on success
 * - -1 on error
 */

int  hmac_sha2_256(const char*  text, size_t  text_len,
                   const char*  key, size_t  key_len, unsigned char*  mac)
{
   return(hmac_openssl(HMAC_ALG_SHA2_256, text, text_len, key, key_len, mac));
}


/* ========================================================================== */
/*! \brief Initialize HMAC module */

void  hmac_init(void)
{
   return;
}


/* ========================================================================== */
/*! \brief Shutdown HMAC module */

void  hmac_exit(void)
{
   return;
}


/* @} */

/* EOF */
