/* Blake2s.c -- BLAKE2s and BLAKE2sp Hash
2015-06-30 : Igor Pavlov : Public domain
2015 : Samuel Neves : Public domain */

#include <string.h>

#include "Blake2.h"
#include "CpuArch.h"
#include "RotateDefs.h"

#define rotr32 rotrFixed

#define BLAKE2S_NUM_ROUNDS 10
#define BLAKE2S_FINAL_FLAG (~(UInt32)0)

static const UInt32 k_Blake2s_IV[8] =
{
  0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
  0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
};

static const Byte k_Blake2s_Sigma[BLAKE2S_NUM_ROUNDS][16] =
{
  {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15 } ,
  { 14, 10,  4,  8,  9, 15, 13,  6,  1, 12,  0,  2, 11,  7,  5,  3 } ,
  { 11,  8, 12,  0,  5,  2, 15, 13, 10, 14,  3,  6,  7,  1,  9,  4 } ,
  {  7,  9,  3,  1, 13, 12, 11, 14,  2,  6,  5, 10,  4,  0, 15,  8 } ,
  {  9,  0,  5,  7,  2,  4, 10, 15, 14,  1, 11, 12,  6,  8,  3, 13 } ,
  {  2, 12,  6, 10,  0, 11,  8,  3,  4, 13,  7,  5, 15, 14,  1,  9 } ,
  { 12,  5,  1, 15, 14, 13,  4, 10,  0,  7,  6,  3,  9,  2,  8, 11 } ,
  { 13, 11,  7, 14, 12,  1,  3,  9,  5,  0, 15,  4,  8,  6,  2, 10 } ,
  {  6, 15, 14,  9, 11,  3,  0,  8, 12,  2, 13,  7,  1,  4, 10,  5 } ,
  { 10,  2,  8,  4,  7,  6,  1,  5, 15, 11,  9, 14,  3, 12, 13 , 0 } ,
};


void Blake2s_Init0(CBlake2s *p)
{
  unsigned i;
  for (i = 0; i < 8; i++)
    p->h[i] = k_Blake2s_IV[i];
  p->t[0] = 0;
  p->t[1] = 0;
  p->f[0] = 0;
  p->f[1] = 0;
  p->bufPos = 0;
  p->lastNode_f1 = 0;
}


static void Blake2s_Compress(CBlake2s *p)
{
  UInt32 m[16];
  UInt32 v[16];
  
  {
    unsigned i;
    
    for (i = 0; i < 16; i++)
      m[i] = GetUi32(p->buf + i * sizeof(m[i]));
    
    for (i = 0; i < 8; i++)
      v[i] = p->h[i];
  }

  v[ 8] = k_Blake2s_IV[0];
  v[ 9] = k_Blake2s_IV[1];
  v[10] = k_Blake2s_IV[2];
  v[11] = k_Blake2s_IV[3];
  
  v[12] = p->t[0] ^ k_Blake2s_IV[4];
  v[13] = p->t[1] ^ k_Blake2s_IV[5];
  v[14] = p->f[0] ^ k_Blake2s_IV[6];
  v[15] = p->f[1] ^ k_Blake2s_IV[7];

  #define G(r,i,a,b,c,d) \
    a += b + m[sigma[2*i+0]];  d ^= a; d = rotr32(d, 16);  c += d;  b ^= c; b = rotr32(b, 12); \
    a += b + m[sigma[2*i+1]];  d ^= a; d = rotr32(d,  8);  c += d;  b ^= c; b = rotr32(b,  7); \

  #define R(r) \
    G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \
    G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \
    G(r,2,v[ 2],v[ 6],v[10],v[14]); \
    G(r,3,v[ 3],v[ 7],v[11],v[15]); \
    G(r,4,v[ 0],v[ 5],v[10],v[15]); \
    G(r,5,v[ 1],v[ 6],v[11],v[12]); \
    G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \
    G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \

  {
    unsigned r;
    for (r = 0; r < BLAKE2S_NUM_ROUNDS; r++)
    {
      const Byte *sigma = k_Blake2s_Sigma[r];
      R(r);
    }
    /* R(0); R(1); R(2); R(3); R(4); R(5); R(6); R(7); R(8); R(9); */
  }

  #undef G
  #undef R

  {
    unsigned i;
    for (i = 0; i < 8; i++)
      p->h[i] ^= v[i] ^ v[i + 8];
  }
}


#define Blake2s_Increment_Counter(S, inc) \
  { p->t[0] += (inc); p->t[1] += (p->t[0] < (inc)); }

#define Blake2s_Set_LastBlock(p) \
  { p->f[0] = BLAKE2S_FINAL_FLAG; p->f[1] = p->lastNode_f1; }


static void Blake2s_Update(CBlake2s *p, const Byte *data, size_t size)
{
  while (size != 0)
  {
    unsigned pos = (unsigned)p->bufPos;
    unsigned rem = BLAKE2S_BLOCK_SIZE - pos;

    if (size <= rem)
    {
      memcpy(p->buf + pos, data, size);
      p->bufPos += (UInt32)size;
      return;
    }

    memcpy(p->buf + pos, data, rem);
    Blake2s_Increment_Counter(S, BLAKE2S_BLOCK_SIZE);
    Blake2s_Compress(p);
    p->bufPos = 0;
    data += rem;
    size -= rem;
  }
}


static void Blake2s_Final(CBlake2s *p, Byte *digest)
{
  unsigned i;

  Blake2s_Increment_Counter(S, (UInt32)p->bufPos);
  Blake2s_Set_LastBlock(p);
  memset(p->buf + p->bufPos, 0, BLAKE2S_BLOCK_SIZE - p->bufPos);
  Blake2s_Compress(p);

  for (i = 0; i < 8; i++)
    SetUi32(digest + sizeof(p->h[i]) * i, p->h[i]);
}


/* ---------- BLAKE2s ---------- */

/* we need to xor CBlake2s::h[i] with input parameter block after Blake2s_Init0() */
/*
typedef struct
{
  Byte  digest_length;
  Byte  key_length;
  Byte  fanout;
  Byte  depth;
  UInt32 leaf_length;
  Byte  node_offset[6];
  Byte  node_depth;
  Byte  inner_length;
  Byte  salt[BLAKE2S_SALTBYTES];
  Byte  personal[BLAKE2S_PERSONALBYTES];
} CBlake2sParam;
*/


static void Blake2sp_Init_Spec(CBlake2s *p, unsigned node_offset, unsigned node_depth)
{
  Blake2s_Init0(p);
  
  p->h[0] ^= (BLAKE2S_DIGEST_SIZE | ((UInt32)BLAKE2SP_PARALLEL_DEGREE << 16) | ((UInt32)2 << 24));
  p->h[2] ^= ((UInt32)node_offset);
  p->h[3] ^= ((UInt32)node_depth << 16) | ((UInt32)BLAKE2S_DIGEST_SIZE << 24);
  /*
  P->digest_length = BLAKE2S_DIGEST_SIZE;
  P->key_length = 0;
  P->fanout = BLAKE2SP_PARALLEL_DEGREE;
  P->depth = 2;
  P->leaf_length = 0;
  store48(P->node_offset, node_offset);
  P->node_depth = node_depth;
  P->inner_length = BLAKE2S_DIGEST_SIZE;
  */
}


void Blake2sp_Init(CBlake2sp *p)
{
  unsigned i;
  
  p->bufPos = 0;

  for (i = 0; i < BLAKE2SP_PARALLEL_DEGREE; i++)
    Blake2sp_Init_Spec(&p->S[i], i, 0);

  p->S[BLAKE2SP_PARALLEL_DEGREE - 1].lastNode_f1 = BLAKE2S_FINAL_FLAG;
}


void Blake2sp_Update(CBlake2sp *p, const Byte *data, size_t size)
{
  unsigned pos = p->bufPos;
  while (size != 0)
  {
    unsigned index = pos / BLAKE2S_BLOCK_SIZE;
    unsigned rem = BLAKE2S_BLOCK_SIZE - (pos & (BLAKE2S_BLOCK_SIZE - 1));
    if (rem > size)
      rem = (unsigned)size;
    Blake2s_Update(&p->S[index], data, rem);
    size -= rem;
    data += rem;
    pos += rem;
    pos &= (BLAKE2S_BLOCK_SIZE * BLAKE2SP_PARALLEL_DEGREE - 1);
  }
  p->bufPos = pos;
}


void Blake2sp_Final(CBlake2sp *p, Byte *digest)
{
  CBlake2s R;
  unsigned i;

  Blake2sp_Init_Spec(&R, 0, 1);
  R.lastNode_f1 = BLAKE2S_FINAL_FLAG;

  for (i = 0; i < BLAKE2SP_PARALLEL_DEGREE; i++)
  {
    Byte hash[BLAKE2S_DIGEST_SIZE];
    Blake2s_Final(&p->S[i], hash);
    Blake2s_Update(&R, hash, BLAKE2S_DIGEST_SIZE);
  }

  Blake2s_Final(&R, digest);
}
