/* hash.c - an implementation of HAS-160 Algorithm.
 *
 * Copyright: 2009-2012 Aleksey Kravchenko <rhash.admin@gmail.com>
 *
 * Permission is hereby granted,  free of charge,  to any person  obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction,  including without limitation
 * the rights to  use, copy, modify,  merge, publish, distribute, sublicense,
 * and/or sell copies  of  the Software,  and to permit  persons  to whom the
 * Software is furnished to do so.
 *
 * This program  is  distributed  in  the  hope  that it will be useful,  but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  Use this program  at  your own risk!
 *
 * HAS-160 is a cryptographic hash function designed for use with the
 * Korean KCDSA digital signature algorithm. It derives from SHA-1,
 * with assorted changes intended to increase its security.
 * It produces a 160-bit message digest.
 *
 * HAS-160 was developed in 1998 by KISA
 * (Korea Information Security Agency) + Academic.
 */

#include <string.h>
#include "gost.h" // yuck!
#include "has160.h"

/**
 * Initialize algorithm context before calculaing hash.
 *
 * @param ctx context to initialize
 */
void rhash_has160_init(has160_ctx *ctx)
{
  ctx->length = 0;

  /* initialize algorithm state */
  ctx->hash[0] = 0x67452301;
  ctx->hash[1] = 0xefcdab89;
  ctx->hash[2] = 0x98badcfe;
  ctx->hash[3] = 0x10325476;
  ctx->hash[4] = 0xc3d2e1f0;
}

/* HAS-160 boolean functions:
 * F1(x,y,z) == (x AND y) OR ((NOT x) AND z) = ((y XOR z) AND x) XOR z
 * F2(x,y,z) == x XOR y XOR z
 * F3(x,y,z) == y XOR (x OR (NOT Z))
 * F4(x,y,z) == x XOR y XOR z                 */
#define STEP_F1(A, B, C, D, E, msg, rot) \
  E += ROTL32(A, rot) + (D ^ (B & (C ^ D))) + msg; \
  B  = ROTL32(B, 10);
#define STEP_F2(A, B, C, D, E, msg, rot) \
  E += ROTL32(A, rot) + (B ^ C ^ D) + msg + 0x5A827999; \
  B  = ROTL32(B, 17);
#define STEP_F3(A, B, C, D, E, msg, rot) \
  E += ROTL32(A, rot) + (C ^ (B | ~D)) + msg + 0x6ED9EBA1; \
  B  = ROTL32(B, 25);
#define STEP_F4(A, B, C, D, E, msg, rot) \
  E += ROTL32(A, rot) + (B ^ C ^ D) + msg + 0x8F1BBCDC; \
  B  = ROTL32(B, 30);

/**
 * The core transformation. Process a 512-bit block.
 *
 * @param hash algorithm state
 * @param block the message block to process
 */
static void rhash_has160_process_block(unsigned* hash, const unsigned* block)
{
  unsigned X[32];
  {
    unsigned j;
    for (j = 0; j < 16; j++) {
      X[j] = le2me_32(block[j]);
    }

    X[16] = X[ 0] ^ X[ 1] ^ X[ 2] ^ X[ 3]; /* for rounds  1..20 */
    X[17] = X[ 4] ^ X[ 5] ^ X[ 6] ^ X[ 7];
    X[18] = X[ 8] ^ X[ 9] ^ X[10] ^ X[11];
    X[19] = X[12] ^ X[13] ^ X[14] ^ X[15];
    X[20] = X[ 3] ^ X[ 6] ^ X[ 9] ^ X[12]; /* for rounds 21..40 */
    X[21] = X[ 2] ^ X[ 5] ^ X[ 8] ^ X[15];
    X[22] = X[ 1] ^ X[ 4] ^ X[11] ^ X[14];
    X[23] = X[ 0] ^ X[ 7] ^ X[10] ^ X[13];
    X[24] = X[ 5] ^ X[ 7] ^ X[12] ^ X[14]; /* for rounds 41..60 */
    X[25] = X[ 0] ^ X[ 2] ^ X[ 9] ^ X[11];
    X[26] = X[ 4] ^ X[ 6] ^ X[13] ^ X[15];
    X[27] = X[ 1] ^ X[ 3] ^ X[ 8] ^ X[10];
    X[28] = X[ 2] ^ X[ 7] ^ X[ 8] ^ X[13]; /* for rounds 61..80 */
    X[29] = X[ 3] ^ X[ 4] ^ X[ 9] ^ X[14];
    X[30] = X[ 0] ^ X[ 5] ^ X[10] ^ X[15];
    X[31] = X[ 1] ^ X[ 6] ^ X[11] ^ X[12];
  }


  {
    unsigned A, B, C, D, E;

    A = hash[0];
    B = hash[1];
    C = hash[2];
    D = hash[3];
    E = hash[4];

    STEP_F1(A,B,C,D,E,X[18], 5);
    STEP_F1(E,A,B,C,D,X[ 0],11);
    STEP_F1(D,E,A,B,C,X[ 1], 7);
    STEP_F1(C,D,E,A,B,X[ 2],15);
    STEP_F1(B,C,D,E,A,X[ 3], 6);
    STEP_F1(A,B,C,D,E,X[19],13);
    STEP_F1(E,A,B,C,D,X[ 4], 8);
    STEP_F1(D,E,A,B,C,X[ 5],14);
    STEP_F1(C,D,E,A,B,X[ 6], 7);
    STEP_F1(B,C,D,E,A,X[ 7],12);
    STEP_F1(A,B,C,D,E,X[16], 9);
    STEP_F1(E,A,B,C,D,X[ 8],11);
    STEP_F1(D,E,A,B,C,X[ 9], 8);
    STEP_F1(C,D,E,A,B,X[10],15);
    STEP_F1(B,C,D,E,A,X[11], 6);
    STEP_F1(A,B,C,D,E,X[17],12);
    STEP_F1(E,A,B,C,D,X[12], 9);
    STEP_F1(D,E,A,B,C,X[13],14);
    STEP_F1(C,D,E,A,B,X[14], 5);
    STEP_F1(B,C,D,E,A,X[15],13);

    STEP_F2(A,B,C,D,E,X[22], 5);
    STEP_F2(E,A,B,C,D,X[ 3],11);
    STEP_F2(D,E,A,B,C,X[ 6], 7);
    STEP_F2(C,D,E,A,B,X[ 9],15);
    STEP_F2(B,C,D,E,A,X[12], 6);
    STEP_F2(A,B,C,D,E,X[23],13);
    STEP_F2(E,A,B,C,D,X[15], 8);
    STEP_F2(D,E,A,B,C,X[ 2],14);
    STEP_F2(C,D,E,A,B,X[ 5], 7);
    STEP_F2(B,C,D,E,A,X[ 8],12);
    STEP_F2(A,B,C,D,E,X[20], 9);
    STEP_F2(E,A,B,C,D,X[11],11);
    STEP_F2(D,E,A,B,C,X[14], 8);
    STEP_F2(C,D,E,A,B,X[ 1],15);
    STEP_F2(B,C,D,E,A,X[ 4], 6);
    STEP_F2(A,B,C,D,E,X[21],12);
    STEP_F2(E,A,B,C,D,X[ 7], 9);
    STEP_F2(D,E,A,B,C,X[10],14);
    STEP_F2(C,D,E,A,B,X[13], 5);
    STEP_F2(B,C,D,E,A,X[ 0],13);

    STEP_F3(A,B,C,D,E,X[26], 5);
    STEP_F3(E,A,B,C,D,X[12],11);
    STEP_F3(D,E,A,B,C,X[ 5], 7);
    STEP_F3(C,D,E,A,B,X[14],15);
    STEP_F3(B,C,D,E,A,X[ 7], 6);
    STEP_F3(A,B,C,D,E,X[27],13);
    STEP_F3(E,A,B,C,D,X[ 0], 8);
    STEP_F3(D,E,A,B,C,X[ 9],14);
    STEP_F3(C,D,E,A,B,X[ 2], 7);
    STEP_F3(B,C,D,E,A,X[11],12);
    STEP_F3(A,B,C,D,E,X[24], 9);
    STEP_F3(E,A,B,C,D,X[ 4],11);
    STEP_F3(D,E,A,B,C,X[13], 8);
    STEP_F3(C,D,E,A,B,X[ 6],15);
    STEP_F3(B,C,D,E,A,X[15], 6);
    STEP_F3(A,B,C,D,E,X[25],12);
    STEP_F3(E,A,B,C,D,X[ 8], 9);
    STEP_F3(D,E,A,B,C,X[ 1],14);
    STEP_F3(C,D,E,A,B,X[10], 5);
    STEP_F3(B,C,D,E,A,X[ 3],13);

    STEP_F4(A,B,C,D,E,X[30], 5);
    STEP_F4(E,A,B,C,D,X[ 7],11);
    STEP_F4(D,E,A,B,C,X[ 2], 7);
    STEP_F4(C,D,E,A,B,X[13],15);
    STEP_F4(B,C,D,E,A,X[ 8], 6);
    STEP_F4(A,B,C,D,E,X[31],13);
    STEP_F4(E,A,B,C,D,X[ 3], 8);
    STEP_F4(D,E,A,B,C,X[14],14);
    STEP_F4(C,D,E,A,B,X[ 9], 7);
    STEP_F4(B,C,D,E,A,X[ 4],12);
    STEP_F4(A,B,C,D,E,X[28], 9);
    STEP_F4(E,A,B,C,D,X[15],11);
    STEP_F4(D,E,A,B,C,X[10], 8);
    STEP_F4(C,D,E,A,B,X[ 5],15);
    STEP_F4(B,C,D,E,A,X[ 0], 6);
    STEP_F4(A,B,C,D,E,X[29],12);
    STEP_F4(E,A,B,C,D,X[11], 9);
    STEP_F4(D,E,A,B,C,X[ 6],14);
    STEP_F4(C,D,E,A,B,X[ 1], 5);
    STEP_F4(B,C,D,E,A,X[12],13);

    hash[0] += A;
    hash[1] += B;
    hash[2] += C;
    hash[3] += D;
    hash[4] += E;
  }
}

/**
 * Calculate message hash.
 * Can be called repeatedly with chunks of the message to be hashed.
 *
 * @param ctx the algorithm context containing current hashing state
 * @param msg message chunk
 * @param size length of the message chunk
 */
void rhash_has160_update(has160_ctx *ctx, const unsigned char* msg, size_t size)
{
  unsigned index = (unsigned)ctx->length & 63;
  ctx->length += size;

  /* fill partial block */
  if (index) {
    unsigned left = has160_block_size - index;
    memcpy((char*)ctx->message + index, msg, (size < left ? size : left));
    if (size < left) return;

    /* process partial block */
    rhash_has160_process_block(ctx->hash, ctx->message);
    msg  += left;
    size -= left;
  }
  while (size >= has160_block_size) {
    unsigned* aligned_message_block;
    if (IS_ALIGNED_32(msg)) {
      /* the most common case is processing a 32-bit aligned message
      without copying it */
      aligned_message_block = (unsigned*)msg;
    } else {
      memcpy(ctx->message, msg, has160_block_size);
      aligned_message_block = ctx->message;
    }

    rhash_has160_process_block(ctx->hash, aligned_message_block);
    msg  += has160_block_size;
    size -= has160_block_size;
  }
  if (size) {
    /* save leftovers */
    memcpy(ctx->message, msg, size);
  }
}

/**
 * Compute and save calculated hash into the given array.
 *
 * @param ctx the algorithm context containing current hashing state
 * @param result calculated hash in binary form
 */
void rhash_has160_final(has160_ctx *ctx, unsigned char* result)
{
  unsigned shift = ((unsigned)ctx->length & 3) * 8;
  unsigned index = ((unsigned)ctx->length & 63) >> 2;

  /* pad message and run for last block */
#if ARCH_LITTLE_ENDIAN
  ctx->message[index]   &= ~(0xFFFFFFFFU << shift);
  ctx->message[index++] ^= 0x80U << shift;
#else
  ctx->message[index]   &= ~(0xFFFFFFFF >> shift);
  ctx->message[index++] ^= 0x80000000 >> shift;
#endif

  /* if no room left in the message to store 64-bit message length */
  if (index > 14) {
    /* then fill the rest with zeros and process it */
    while (index < 16) {
      ctx->message[index++] = 0;
    }
    rhash_has160_process_block(ctx->hash, ctx->message);
    index = 0;
  }
  while (index < 14) {
    ctx->message[index++] = 0;
  }
  ctx->message[14] = le2me_32( (unsigned)(ctx->length << 3)  );
  ctx->message[15] = le2me_32( (unsigned)(ctx->length >> 29) );
  rhash_has160_process_block(ctx->hash, ctx->message);

  le32_copy(result, 0, &ctx->hash, has160_hash_size);
}
