/*---------------------------------------------------------------------------*
  Project:  TwlSDK - MATH - libraries
  File:     dgt_sha256.c

  Copyright 2007-2008 Nintendo. All rights reserved.

  These coded instructions, statements, and computer programs contain
  proprietary information of Nintendo of America Inc. and/or Nintendo
  Company Ltd., and are protected by Federal copyright law. They may
  not be disclosed to third parties or copied or duplicated in any form,
  in whole or in part, without the prior written consent of Nintendo.

  $Date::$
  $Rev:$
  $Author:$
 *---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*
    SHA-256
 *---------------------------------------------------------------------------*/


#include <nitro/math/dgt.h>
#include <nitro/mi/memory.h>
#include <nitro/mi/endian.h>
#include "hmac.h"


/*---------------------------------------------------------------------------*/
#define NOT_USE_SHA256FILL       0      // Do not use MATH_SHA256Fill
                                        // Instead, consumption of the 64byte RAM increases

#define memcpy(_x_, _y_, _z_)    MI_CpuCopy8(_y_, _x_, _z_)
#define memset                   MI_CpuFill8
#define NETConvert32HToBE(val)   MI_HToBE32(val)

#if !defined(MATH_SHA256_ASM)
static void MATH_SHA256iProcessBlock(MATHSHA256Context *context);
#else
extern void MATH_SHA256iProcessBlock(MATHSHA256Context *context);
#endif


/*---------------------------------------------------------------------------*
    32-bit operations required in SHA-256 calculation
 *---------------------------------------------------------------------------*/
#define SHA256_ROTR(r, x)       (NETRotateLeft32((32-r),x))
#define SHA256_SHR(s, x)        (x>>s)

#define SHA256_CH(x, y, z)      (((x)&(y))^((~x)&(z)))
#define SHA256_MAJ(x, y, z)     (((x)&(y))^((x)&(z))^((y)&(z)))

#define SHA256_LSIGMA_0(x)  \
    (SHA256_ROTR(2,x) ^ SHA256_ROTR(13,x) ^ SHA256_ROTR(22,x))

#define SHA256_LSIGMA_1(x)  \
    (SHA256_ROTR(6,x) ^ SHA256_ROTR(11,x) ^ SHA256_ROTR(25,x))

#define SHA256_SSIGMA_0(x)  \
    (SHA256_ROTR(7,x) ^ SHA256_ROTR(18,x) ^ SHA256_SHR(3,x))

#define SHA256_SSIGMA_1(x)  \
    (SHA256_ROTR(17,x) ^ SHA256_ROTR(19,x) ^ SHA256_SHR(10,x))


/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*
    SHA-256 Constant table
 *---------------------------------------------------------------------------*/
#if !defined(MATH_SHA256_ASM)
static u32 MATH_SHA256iConst[64] =
#else
u32 MATH_SHA256iConst[64] =
#endif
{
    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
    0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
    0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
    0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
    0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
    0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
    0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
    0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
    0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
    0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
    0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
    0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
    0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
    0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
    0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
    0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};


/*---------------------------------------------------------------------------*
  Name:         NETRotateLeft32

  Description:  Bit-rotates the 32-bit value
  
  Arguments:    shift Rotation bit count
                value  Rotated value
  
  Returns:      Rotated 32-bit value
 *---------------------------------------------------------------------------*/
inline static u32 NETRotateLeft32(int shift, u32 value)
{
    return (u32)((value << shift) | (value >> (u32)(32 - shift)));
}

/*---------------------------------------------------------------------------*
  Name:         MATH_SHA256Init

  Description:  Initializes the MATHSHA256Context structure used for finding the SHA-256 value.
  
  Arguments:    context:   MATHSHA256Context structure
  
  Returns:      None.
 *---------------------------------------------------------------------------*/
void
MATH_SHA256Init(MATHSHA256Context* context)
{
    context->Nl = 0;
    context->Nh = 0;
    context->num = 0;
    context->h[0] = 0x6a09e667;
    context->h[1] = 0xbb67ae85;
    context->h[2] = 0x3c6ef372;
    context->h[3] = 0xa54ff53a;
    context->h[4] = 0x510e527f;
    context->h[5] = 0x9b05688c;
    context->h[6] = 0x1f83d9ab;
    context->h[7] = 0x5be0cd19;
}

/*---------------------------------------------------------------------------*
  Name:         MATH_SHA256Update

  Description:  Updates the SHA-256 value with added data.
  
  Arguments:    context:   MATHSHA256Context structure
                input:   Pointer to input data.
                length:  Length of input data.
  
  Returns:      None.
 *---------------------------------------------------------------------------*/
void
MATH_SHA256Update(MATHSHA256Context* context, const void* input, u32 length)
{
    while (length > 0)
    {
        /* Fill data to the block margins */
        u32     rest = MATHSHA256_CBLOCK - (u32)context->num;
		u8      *dat;
        if (rest > length)
        {
            rest = length;
        }
		dat = (u8*)context->data;
        (void)memcpy(&dat[context->num], input, rest);
        input = (const u8 *)input + rest;
        length -= rest;
        context->num += rest;
        /* Execute the hash calculation if the block has expired. */
        if (context->num >= MATHSHA256_CBLOCK)
        {
            MATH_SHA256iProcessBlock(context);
            context->num = 0;
            ++context->Nl;
            if (!context->Nl)
            {
                ++context->Nh;
            }
        }
    }
}

#if (NOT_USE_SHA256FILL == 0)
/*---------------------------------------------------------------------------*
  Name:         MATH_SHA256Fill

  Description:  Update with continuous data of fixed values that specified SHA-256.
  
  Arguments:    context:   MATHSHA256Context structure
                input	u8 value to be input as continuous data
                length:  Length of input data.
  
  Returns:      None.
 *---------------------------------------------------------------------------*/
static void
MATH_SHA256Fill(MATHSHA256Context* context, u8 input, u32 length)
{
    while (length > 0)
    {
        /* Fill data to the block margins */
        u32     rest = MATHSHA256_CBLOCK - (u32)context->num;
		u8      *dat;
        if (rest > length)
        {
            rest = length;
        }
		dat = (u8*)context->data;
		(void)memset(&dat[context->num], input, rest);
        length -= rest;
        context->num += rest;
        /* Execute the hash calculation if the block has expired. */
        if (context->num >= MATHSHA256_CBLOCK)
        {
            MATH_SHA256iProcessBlock(context);
            context->num = 0;
            ++context->Nl;
            if (!context->Nl)
            {
                ++context->Nh;
            }
        }
    }
}
#endif  /* (NOT_USE_SHA256FILL == 0) */

/*---------------------------------------------------------------------------*
  Name:         MATH_SHA256GetDigest

  Description:  Gets the final SHA-256 value.
  
  Arguments:    context:   MATHSHA256Context structure
                digest:   Pointer to the location where SHA-256 is stored.
  
  Returns:      None.
 *---------------------------------------------------------------------------*/
#if (NOT_USE_SHA256FILL == 1)
static u8 padding[64] =
{
 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
#endif
void
MATH_SHA256GetHash(MATHSHA256Context *context, void *digest)
{
    /* Method for filling packets is the same as SHA-1 */
    u32    footer[2];
    static const u8 padlead[1] = { 0x80 };
    static const u8 padalign[sizeof(footer)] = { 0x00, };
    /* setup footer field (in bits, big endian) */
    footer[1] = NETConvert32HToBE((u32)
        (context->Nl << (6 + 3)) + (context->num << (0 + 3)));
    footer[0] = NETConvert32HToBE((u32)
        (context->Nh << (6 + 3)) + (context->Nl >> (u32)(32 - (6 + 3))));
#if (NOT_USE_SHA256FILL == 0)
    /* add leading padbyte '0x80' */
    MATH_SHA256Update(context, padlead, sizeof(padlead));
    /* if necessary, add 2 padblocks */
    if (MATHSHA256_CBLOCK - context->num < sizeof(footer))
    {
        MATH_SHA256Update(context, padalign, MATHSHA256_CBLOCK - (u32)context->num);
    }
    /* add trailing padbytes '0x00' */
    MATH_SHA256Fill(context, 0x00, MATHSHA256_CBLOCK - context->num - sizeof(footer));
    /* add footer length */
    MATH_SHA256Update(context, footer, sizeof(footer));
#else  /* (NOT_USE_SHA256FILL == 1) */
	{
		u32 last, padn;
		
		last = context->num;
		padn = (last < 56) ? (56 - last) : (120 - last);
		MATH_SHA256Update(context, padding, padn);
		MATH_SHA256Update(context, footer, sizeof(footer));
	}
#endif  /* (NOT_USE_SHA256FILL == 1) */
    /* copy registers to the dst */
    context->h[0] = NETConvert32HToBE((u32)context->h[0]);
    context->h[1] = NETConvert32HToBE((u32)context->h[1]);
    context->h[2] = NETConvert32HToBE((u32)context->h[2]);
    context->h[3] = NETConvert32HToBE((u32)context->h[3]);
    context->h[4] = NETConvert32HToBE((u32)context->h[4]);
    context->h[5] = NETConvert32HToBE((u32)context->h[5]);
    context->h[6] = NETConvert32HToBE((u32)context->h[6]);
    context->h[7] = NETConvert32HToBE((u32)context->h[7]);
    (void)memcpy(digest, &context->h, MATHSHA256_DIGEST_LENGTH);
}

#if 0
/*---------------------------------------------------------------------------*
  Name:         NETGetSHA256Interface

  Description:  Gets the hash interface for SHA256.

  Arguments:    None.

  Returns:      Hash interface for SHA256
 *---------------------------------------------------------------------------*/
const NETHashInterface* NETGetSHA256Interface(void)
{
    static const NETHashInterface sha1template =
    {
        MATHSHA256_DIGEST_LENGTH,
        MATHSHA256_CBLOCK,
        sizeof(MATHSHA256Context),
        NULL,
        (void (*)(void*))MATH_SHA256Init,
        (void (*)(void*, const void*, u32))MATH_SHA256Update,
        (void (*)(void*, void *))MATH_SHA256GetDigest,
    };
    return &sha1template;
}
#endif

#if !defined(MATH_SHA256_ASM)
/*---------------------------------------------------------------------------*
  Name:         MATH_SHA256iProcessBlock

  Description:  Calculate hash using 1 block that expired in the SHA-256 context.
  
  Arguments:    context:   MATHSHA256Context structure
  
  Returns:      None.
 *---------------------------------------------------------------------------*/
static void MATH_SHA256iProcessBlock(MATHSHA256Context *context)
{
    u32     a = context->h[0];
    u32     b = context->h[1];
    u32     c = context->h[2];
    u32     d = context->h[3];
    u32     e = context->h[4];
    u32     f = context->h[5];
    u32     g = context->h[6];
    u32     h = context->h[7];
    u32     w[64];
    u32     tmp1, tmp2;
	u32     *prev;

    int     t;
    for (t = 0; t < 16; ++t)
    {
        w[t] = NETConvert32HToBE(((u32*)context->data)[t]);
    }
    
    for (; t < 64; ++t)
    {
        prev = &w[t - 16];
        w[t] = prev[ 0]
             + SHA256_SSIGMA_0(prev[ 1])
             + prev[ 9]
             + SHA256_SSIGMA_1(prev[14]);
    }
    
    for (t = 0; t < 64; ++t)
    {
        tmp1 = h
             + SHA256_LSIGMA_1(e)
             + SHA256_CH(e,f,g)
             + MATH_SHA256iConst[t]
             + w[t];
        
        tmp2 = SHA256_LSIGMA_0(a)
             + SHA256_MAJ(a,b,c);
        
        h    = g;
        g    = f;
        f    = e;
        e    = d + tmp1;
        d    = c;
        c    = b;
        b    = a;
        a    = tmp1 + tmp2;
    }
    
    context->h[0] += a;
    context->h[1] += b;
    context->h[2] += c;
    context->h[3] += d;
    context->h[4] += e;
    context->h[5] += f;
    context->h[6] += g;
    context->h[7] += h;
}
#endif	/* !defined(MATH_SHA256_ASM) */


/*---------------------------------------------------------------------------*
  Name:         MATH_CalcSHA256

  Description:  Calculates SHA-256.
  
  Arguments:    digest:   Pointer to the location where SHA-256 is stored.
                data:    Pointer to input data.
                dataLength:   Length of input data.
  
  Returns:      None.
 *---------------------------------------------------------------------------*/
void MATH_CalcSHA256(void *digest, const void *data, u32 dataLength)
{
    MATHSHA256Context context;
    MATH_SHA256Init(&context);
    MATH_SHA256Update(&context, data, dataLength);
    MATH_SHA256GetHash(&context, digest);
}



// HMAC

/*---------------------------------------------------------------------------*
  Name:         MATH_CalcHMACSHA256

  Description:  Calculates HMAC-SHA-256.
  
  Arguments:    digest:   Pointer to the location where the HMAC-SHA-256 value is stored.
                data:    Pointer to input data.
                dataLength:   Length of input data.
                key:    Pointer to the key
                keyLength:   Length of the key
  
  Returns:      None.
 *---------------------------------------------------------------------------*/
void MATH_CalcHMACSHA256(void *digest, const void *bin_ptr, u32 bin_len, const void *key_ptr, u32 key_len) 
{
    MATHSHA256Context context;
    unsigned char   hash_buf[ MATH_SHA256_DIGEST_SIZE ]; /* Hash value gotten from the hash function */
    
    MATHiHMACFuncs hash2funcs = {
        MATH_SHA256_DIGEST_SIZE,
        (512/8),
    };
    
    hash2funcs.context       = &context;
    hash2funcs.hash_buf      = hash_buf;
    hash2funcs.HashReset     = (void (*)(void*))                   MATH_SHA256Init;
    hash2funcs.HashSetSource = (void (*)(void*, const void*, u32)) MATH_SHA256Update;
    hash2funcs.HashGetDigest = (void (*)(void*, void*))            MATH_SHA256GetHash;
    
    MATHi_CalcHMAC(digest, bin_ptr, bin_len, key_ptr, key_len, &hash2funcs);
}

/*---------------------------------------------------------------------------*/
