/*
 * SHA-256 hash implementation and interface functions
 * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
 *
 * This software may be distributed under the terms of the BSD license.
 * See README for more details.
 */

#include "sha256.h"
#include "sha256_i.h"

/**
 * sha256 - SHA256 hash for data vector
 * @num_elem: Number of elements in the data vector
 * @addr: Pointer to the data areas
 * @len: Length of the data blocks
 * @mac: Buffer for the hash
 * Returns: 0 on success, -1 of failure
 */
int sha256(const uint8_t *addr, const size_t len, uint8_t *mac)
{
  struct sha256_state ctx;

  sha256_init(&ctx);

  if (sha256_process(&ctx, addr, len))
    return -1;

  if (sha256_done(&ctx, mac))
    return -1;

  return 0;
}

/**
 * hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104)
 * @key: Key for HMAC operations
 * @key_len: Length of the key in bytes
 * @num_elem: Number of elements in the data vector
 * @addr: Pointers to the data areas
 * @len: Lengths of the data blocks
 * @mac: Buffer for the hash (32 bytes)
 * Returns: 0 on success, -1 on failure
 */
int hmac_sha256_vector(const uint8_t *key, size_t key_len, size_t num_elem,
                       const uint8_t *addr[], const size_t *len, uint8_t *mac)
{
  unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
  unsigned char tk[32];
  const uint8_t *_addr[6];
  size_t _len[6], i;

  if (num_elem > 5)
  {
    /*
     * Fixed limit on the number of fragments to avoid having to
     * allocate memory (which could fail).
     */
    return -1;
  }

  /* if key is longer than 64 bytes reset it to key = SHA256(key) */
  if (key_len > 64)
  {
    if (sha256_vector(1, &key, &key_len, tk) < 0)
      return -1;

    key = tk;
    key_len = 32;
  }

  /* the HMAC_SHA256 transform looks like:
   *
   * SHA256(K XOR opad, SHA256(K XOR ipad, text))
   *
   * where K is an n byte key
   * ipad is the byte 0x36 repeated 64 times
   * opad is the byte 0x5c repeated 64 times
   * and text is the data being protected */

  /* start out by storing key in ipad */
  memset(k_pad, 0, sizeof(k_pad));
  memcpy(k_pad, key, key_len);

  /* XOR key with ipad values */
  for (i = 0; i < 64; i++)
    k_pad[i] ^= 0x36;

  /* perform inner SHA256 */
  _addr[0] = k_pad;
  _len[0] = 64;

  for (i = 0; i < num_elem; i++)
  {
    _addr[i + 1] = addr[i];
    _len[i + 1] = len[i];
  }

  if (sha256_vector(1 + num_elem, _addr, _len, mac) < 0)
    return -1;

  memset(k_pad, 0, sizeof(k_pad));
  memcpy(k_pad, key, key_len);

  /* XOR key with opad values */
  for (i = 0; i < 64; i++)
    k_pad[i] ^= 0x5c;

  /* perform outer SHA256 */
  _addr[0] = k_pad;
  _len[0] = 64;
  _addr[1] = mac;
  _len[1] = SHA256_MAC_LEN;
  return sha256_vector(2, _addr, _len, mac);
}


/**
 * hmac_sha256 - HMAC-SHA256 over data buffer (RFC 2104)
 * @key: Key for HMAC operations
 * @key_len: Length of the key in bytes
 * @data: Pointers to the data area
 * @data_len: Length of the data area
 * @mac: Buffer for the hash (32 bytes)
 * Returns: 0 on success, -1 on failure
 */
int hmac_sha256(const uint8_t *key, size_t key_len, const uint8_t *data,
                size_t data_len, uint8_t *mac)
{
  return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
}
