/*
 * SHA-256 (FIPS 180-4) and HMAC-SHA-256 (RFC 2104).
 * Written for LanMan98; placed in the public domain.
 *   Portions Copyright RISC OS Developments 2019+, credited to the RISC OS One Project.
 */

#include <string.h>

#include "sha256.h"

#define ROR(x, n) (((x) >> (n)) | ((x) << (32 - (n))))

#define CH(x, y, z)   (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x, y, z)  (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define BSIG0(x)      (ROR(x,  2) ^ ROR(x, 13) ^ ROR(x, 22))
#define BSIG1(x)      (ROR(x,  6) ^ ROR(x, 11) ^ ROR(x, 25))
#define SSIG0(x)      (ROR(x,  7) ^ ROR(x, 18) ^ ((x) >> 3))
#define SSIG1(x)      (ROR(x, 17) ^ ROR(x, 19) ^ ((x) >> 10))

static const sha256_uint32 K[64] =
{
    0x428a2f98u, 0x71374491u, 0xb5c0fbcfu, 0xe9b5dba5u,
    0x3956c25bu, 0x59f111f1u, 0x923f82a4u, 0xab1c5ed5u,
    0xd807aa98u, 0x12835b01u, 0x243185beu, 0x550c7dc3u,
    0x72be5d74u, 0x80deb1feu, 0x9bdc06a7u, 0xc19bf174u,
    0xe49b69c1u, 0xefbe4786u, 0x0fc19dc6u, 0x240ca1ccu,
    0x2de92c6fu, 0x4a7484aau, 0x5cb0a9dcu, 0x76f988dau,
    0x983e5152u, 0xa831c66du, 0xb00327c8u, 0xbf597fc7u,
    0xc6e00bf3u, 0xd5a79147u, 0x06ca6351u, 0x14292967u,
    0x27b70a85u, 0x2e1b2138u, 0x4d2c6dfcu, 0x53380d13u,
    0x650a7354u, 0x766a0abbu, 0x81c2c92eu, 0x92722c85u,
    0xa2bfe8a1u, 0xa81a664bu, 0xc24b8b70u, 0xc76c51a3u,
    0xd192e819u, 0xd6990624u, 0xf40e3585u, 0x106aa070u,
    0x19a4c116u, 0x1e376c08u, 0x2748774cu, 0x34b0bcb5u,
    0x391c0cb3u, 0x4ed8aa4au, 0x5b9cca4fu, 0x682e6ff3u,
    0x748f82eeu, 0x78a5636fu, 0x84c87814u, 0x8cc70208u,
    0x90befffau, 0xa4506cebu, 0xbef9a3f7u, 0xc67178f2u
};

/*
    One 64 byte block.  Everything in SHA-256 is big endian, which is the
    other way round from the rest of this module, so the bytes are put
    together explicitly rather than cast.
*/
static void sha256_block(struct sha256_context *ctx, const unsigned char *p)
{
    sha256_uint32 w[64];
    sha256_uint32 a, b, c, d, e, f, g, h, t1, t2;
    int i;

    for(i = 0; i < 16; i++)
        w[i] = (((sha256_uint32) p[4 * i    ]) << 24) |
               (((sha256_uint32) p[4 * i + 1]) << 16) |
               (((sha256_uint32) p[4 * i + 2]) <<  8) |
               (((sha256_uint32) p[4 * i + 3]));
    for(i = 16; i < 64; i++)
        w[i] = SSIG1(w[i - 2]) + w[i - 7] + SSIG0(w[i - 15]) + w[i - 16];

    a = ctx->state[0]; b = ctx->state[1]; c = ctx->state[2]; d = ctx->state[3];
    e = ctx->state[4]; f = ctx->state[5]; g = ctx->state[6]; h = ctx->state[7];

    for(i = 0; i < 64; i++)
    {
        t1 = h + BSIG1(e) + CH(e, f, g) + K[i] + w[i];
        t2 = BSIG0(a) + MAJ(a, b, c);
        h = g; g = f; f = e; e = d + t1;
        d = c; c = b; b = a; a = t1 + t2;
    }

    ctx->state[0] += a; ctx->state[1] += b; ctx->state[2] += c;
    ctx->state[3] += d; ctx->state[4] += e; ctx->state[5] += f;
    ctx->state[6] += g; ctx->state[7] += h;

    memset(w, 0, sizeof(w));
}

void sha256_init(struct sha256_context *ctx)
{
    ctx->state[0] = 0x6a09e667u; ctx->state[1] = 0xbb67ae85u;
    ctx->state[2] = 0x3c6ef372u; ctx->state[3] = 0xa54ff53au;
    ctx->state[4] = 0x510e527fu; ctx->state[5] = 0x9b05688cu;
    ctx->state[6] = 0x1f83d9abu; ctx->state[7] = 0x5be0cd19u;
    ctx->lo = 0;
    ctx->hi = 0;
    ctx->used = 0;
}

void sha256_update(struct sha256_context *ctx, const void *data, size_t size)
{
    const unsigned char *p = (const unsigned char *) data;
    sha256_uint32 add;
    unsigned int n;

    /* Length in bits, kept in two halves so that no 64 bit type is needed */
    add = (sha256_uint32) (size << 3);
    ctx->lo += add;
    if(ctx->lo < add)
        ctx->hi++;
    ctx->hi += (sha256_uint32) (size >> 29);

    while(size > 0)
    {
        n = SHA256_BLOCKLEN - ctx->used;
        if((size_t) n > size)
            n = (unsigned int) size;
        memcpy(ctx->buffer + ctx->used, p, n);
        ctx->used += n;
        p += n;
        size -= n;
        if(ctx->used == SHA256_BLOCKLEN)
        {
            sha256_block(ctx, ctx->buffer);
            ctx->used = 0;
        }
    }
}

void sha256_final(struct sha256_context *ctx,
                  unsigned char result[SHA256_RESULTLEN])
{
    unsigned char length[8];
    static const unsigned char padding[SHA256_BLOCKLEN] = { 0x80, 0 };
    unsigned int n;
    int i;

    length[0] = (unsigned char) (ctx->hi >> 24);
    length[1] = (unsigned char) (ctx->hi >> 16);
    length[2] = (unsigned char) (ctx->hi >>  8);
    length[3] = (unsigned char) (ctx->hi);
    length[4] = (unsigned char) (ctx->lo >> 24);
    length[5] = (unsigned char) (ctx->lo >> 16);
    length[6] = (unsigned char) (ctx->lo >>  8);
    length[7] = (unsigned char) (ctx->lo);

    /* Pad to eight short of a block, then the length */
    n = (ctx->used < 56) ? (56 - ctx->used) : (120 - ctx->used);
    sha256_update(ctx, padding, n);
    sha256_update(ctx, length, 8);

    for(i = 0; i < 8; i++)
    {
        result[4 * i    ] = (unsigned char) (ctx->state[i] >> 24);
        result[4 * i + 1] = (unsigned char) (ctx->state[i] >> 16);
        result[4 * i + 2] = (unsigned char) (ctx->state[i] >>  8);
        result[4 * i + 3] = (unsigned char) (ctx->state[i]);
    }
    memset(ctx, 0, sizeof(*ctx));
}

void sha256_get_digest(const void *data, size_t size,
                       unsigned char result[SHA256_RESULTLEN])
{
    struct sha256_context ctx;

    sha256_init(&ctx);
    sha256_update(&ctx, data, size);
    sha256_final(&ctx, result);
}

void hmac_sha256_2(const unsigned char *key, size_t key_len,
                   const void *d1, size_t n1,
                   const void *d2, size_t n2,
                   unsigned char result[SHA256_RESULTLEN])
{
    struct sha256_context ctx;
    unsigned char k[SHA256_BLOCKLEN];
    unsigned char pad[SHA256_BLOCKLEN];
    unsigned char inner[SHA256_RESULTLEN];
    int i;

    /* A key longer than the block is replaced by its own digest */
    memset(k, 0, sizeof(k));
    if(key_len > SHA256_BLOCKLEN)
        sha256_get_digest(key, key_len, k);
    else
        memcpy(k, key, key_len);

    for(i = 0; i < SHA256_BLOCKLEN; i++)
        pad[i] = k[i] ^ 0x36;
    sha256_init(&ctx);
    sha256_update(&ctx, pad, SHA256_BLOCKLEN);
    if(n1) sha256_update(&ctx, d1, n1);
    if(n2) sha256_update(&ctx, d2, n2);
    sha256_final(&ctx, inner);

    for(i = 0; i < SHA256_BLOCKLEN; i++)
        pad[i] = k[i] ^ 0x5c;
    sha256_init(&ctx);
    sha256_update(&ctx, pad, SHA256_BLOCKLEN);
    sha256_update(&ctx, inner, SHA256_RESULTLEN);
    sha256_final(&ctx, result);

    memset(k, 0, sizeof(k));
    memset(pad, 0, sizeof(pad));
    memset(inner, 0, sizeof(inner));
}

void hmac_sha256(const unsigned char *key, size_t key_len,
                 const void *data, size_t size,
                 unsigned char result[SHA256_RESULTLEN])
{
    hmac_sha256_2(key, key_len, data, size, NULL, 0, result);
}
