/*++

Copyright (c) 2015 Minoca Corp. All Rights Reserved

Module Name:

    sha256.c

Abstract:

    This module implements the SHA-256 hashing function.

Author:

    Evan Green 13-Jan-2015

Environment:

    Any

--*/

//
// ------------------------------------------------------------------- Includes
//

#include "cryptop.h"

//
// --------------------------------------------------------------------- Macros
//

#define SHA256_ROTATE_LEFT(_Value, _Count) \
    (((_Value) << (_Count)) | ((_Value) >> (32 - (_Count))))

#define SHA256_ROTATE_RIGHT(_Value, _Count) \
    (((_Value) >> (_Count)) | ((_Value) << (32 - (_Count))))

#define SHA256_CH(_ValueX, _ValueY, _ValueZ) \
    (((_ValueX) & (_ValueY)) ^ (~(_ValueX) & (_ValueZ)))

#define SHA256_MAJ(_ValueX, _ValueY, _ValueZ)               \
    (((_ValueX) & (_ValueY)) ^ ((_ValueX) & (_ValueZ)) ^    \
     ((_ValueY) & (_ValueZ)))

#define SHA256_EP0(_Value)              \
    (SHA256_ROTATE_RIGHT(_Value, 2) ^   \
     SHA256_ROTATE_RIGHT(_Value, 13) ^  \
     SHA256_ROTATE_RIGHT(_Value, 22))

#define SHA256_EP1(_Value)              \
    (SHA256_ROTATE_RIGHT(_Value, 6) ^   \
     SHA256_ROTATE_RIGHT(_Value, 11) ^  \
     SHA256_ROTATE_RIGHT(_Value, 25))

#define SHA256_SIG0(_Value)             \
    (SHA256_ROTATE_RIGHT(_Value, 7) ^   \
     SHA256_ROTATE_RIGHT(_Value, 18) ^  \
     ((_Value) >> 3))

#define SHA256_SIG1(_Value)             \
    (SHA256_ROTATE_RIGHT(_Value, 17) ^   \
     SHA256_ROTATE_RIGHT(_Value, 19) ^  \
     ((_Value) >> 10))

//
// ---------------------------------------------------------------- Definitions
//

//
// ------------------------------------------------------ Data Type Definitions
//

//
// ----------------------------------------------- Internal Function Prototypes
//

VOID
CypSha256ProcessMessage (
    PSHA256_CONTEXT Context
    );

VOID
CypSha256PadMessage (
    PSHA256_CONTEXT Context
    );

//
// -------------------------------------------------------------------- Globals
//

const ULONG CySha256KConstants[64] = {
    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
};

//
// ------------------------------------------------------------------ Functions
//

CRYPTO_API
VOID
CySha256Initialize (
    PSHA256_CONTEXT Context
    )

/*++

Routine Description:

    This routine initializes a SHA-256 context structure, preparing it to
    accept and hash data.

Arguments:

    Context - Supplies a pointer to the context to initialize.

Return Value:

    None.

--*/

{

    Context->Length = 0;
    Context->BlockIndex = 0;
    Context->IntermediateHash[0] = 0x6A09E667;
    Context->IntermediateHash[1] = 0xBB67AE85;
    Context->IntermediateHash[2] = 0x3C6EF372;
    Context->IntermediateHash[3] = 0xA54FF53A;
    Context->IntermediateHash[4] = 0x510E527F;
    Context->IntermediateHash[5] = 0x9B05688C;
    Context->IntermediateHash[6] = 0x1F83D9AB;
    Context->IntermediateHash[7] = 0x5BE0CD19;
    return;
}

CRYPTO_API
VOID
CySha256AddContent (
    PSHA256_CONTEXT Context,
    PVOID Message,
    UINTN Length
    )

/*++

Routine Description:

    This routine adds data to a SHA-256 digest.

Arguments:

    Context - Supplies a pointer to the initialized SHA-256 context.

    Message - Supplies a pointer to the buffer containing the bytes.

    Length - Supplies the length of the message buffer, in bytes.

Return Value:

    None.

--*/

{

    PUCHAR Bytes;

    Bytes = Message;
    while (Length != 0) {
        Context->MessageBlock[Context->BlockIndex] = *Bytes;
        Context->BlockIndex += 1;
        if (Context->BlockIndex == sizeof(Context->MessageBlock)) {
            CypSha256ProcessMessage(Context);
            Context->Length += sizeof(Context->MessageBlock) * BITS_PER_BYTE;
            Context->BlockIndex = 0;
        }

        Bytes += 1;
        Length -= 1;
    }

    return;
}

CRYPTO_API
VOID
CySha256GetHash (
    PSHA256_CONTEXT Context,
    UCHAR Hash[SHA256_HASH_SIZE]
    )

/*++

Routine Description:

    This routine computes and returns the final SHA-256 hash value for the
    messages that have been previously entered.

Arguments:

    Context - Supplies a pointer to the initialized SHA-256 context.

    Hash - Supplies a pointer where the final hash value will be returned. This
        buffer must be SHA256_HASH_SIZE length in bytes.

Return Value:

    None.

--*/

{

    INT DigestIndex;
    INT ShiftAmount;

    CypSha256PadMessage(Context);
    Context->Length = 0;

    //
    // Copy the key and convert to big endian at the same time.
    //

    for (DigestIndex = 0; DigestIndex < SHA256_HASH_SIZE; DigestIndex += 1) {
        ShiftAmount = (sizeof(ULONG) - 1 - (DigestIndex % sizeof(ULONG))) *
                      BITS_PER_BYTE;

        Hash[DigestIndex] =
                      Context->IntermediateHash[DigestIndex / sizeof(ULONG)] >>
                      ShiftAmount;
    }

    return;
}

//
// --------------------------------------------------------- Internal Functions
//

VOID
CypSha256ProcessMessage (
    PSHA256_CONTEXT Context
    )

/*++

Routine Description:

    This routine processes the 512 bits in the current message block and adds
    them to the digest.

Arguments:

    Context - Supplies a pointer to the initialized SHA-256 context with a full
        message block.

Return Value:

    None.

--*/

{

    ULONG Block[64];
    ULONG BlockA;
    ULONG BlockB;
    ULONG BlockC;
    ULONG BlockD;
    ULONG BlockE;
    ULONG BlockF;
    ULONG BlockG;
    ULONG BlockH;
    UINTN BlockIndex;
    UINTN ByteIndex;
    ULONG Working1;
    ULONG Working2;

    ByteIndex = 0;
    for (BlockIndex = 0; BlockIndex < 16; BlockIndex += 1) {
        Block[BlockIndex] = (Context->MessageBlock[ByteIndex] << 24) |
                            (Context->MessageBlock[ByteIndex + 1] << 16) |
                            (Context->MessageBlock[ByteIndex + 2] << 8) |
                            Context->MessageBlock[ByteIndex + 3];

        ByteIndex += 4;
    }

    while (BlockIndex < 64) {
        Block[BlockIndex] = SHA256_SIG1(Block[BlockIndex - 2]) +
                            Block[BlockIndex - 7] +
                            SHA256_SIG0(Block[BlockIndex - 15]) +
                            Block[BlockIndex - 16];

        BlockIndex += 1;
    }

    BlockA = Context->IntermediateHash[0];
    BlockB = Context->IntermediateHash[1];
    BlockC = Context->IntermediateHash[2];
    BlockD = Context->IntermediateHash[3];
    BlockE = Context->IntermediateHash[4];
    BlockF = Context->IntermediateHash[5];
    BlockG = Context->IntermediateHash[6];
    BlockH = Context->IntermediateHash[7];
    for (BlockIndex = 0; BlockIndex < 64; BlockIndex += 1) {
        Working1 = BlockH +
                   SHA256_EP1(BlockE) +
                   SHA256_CH(BlockE, BlockF, BlockG) +
                   CySha256KConstants[BlockIndex] +
                   Block[BlockIndex];

        Working2 = SHA256_EP0(BlockA) + SHA256_MAJ(BlockA, BlockB, BlockC);
        BlockH = BlockG;
        BlockG = BlockF;
        BlockF = BlockE;
        BlockE = BlockD + Working1;
        BlockD = BlockC;
        BlockC = BlockB;
        BlockB = BlockA;
        BlockA = Working1 + Working2;
    }

    Context->IntermediateHash[0] += BlockA;
    Context->IntermediateHash[1] += BlockB;
    Context->IntermediateHash[2] += BlockC;
    Context->IntermediateHash[3] += BlockD;
    Context->IntermediateHash[4] += BlockE;
    Context->IntermediateHash[5] += BlockF;
    Context->IntermediateHash[6] += BlockG;
    Context->IntermediateHash[7] += BlockH;
    return;
}

VOID
CypSha256PadMessage (
    PSHA256_CONTEXT Context
    )

/*++

Routine Description:

    This routine pads the message out to an even multiple of 512 bits.
    The standard specifies that the first padding bit must be a 1, and then the
    last 64 bits represent the length of the original message.

Arguments:

    Context - Supplies a pointer to the initialized SHA-256 context with a full
        message block.

Return Value:

    None.

--*/

{

    UINTN Index;

    //
    // Check to see if the current message block is too small to hold the
    // initial padding bits and length. If so, process the block, then continue
    // padding onto a second block.
    //

    Index = Context->BlockIndex;
    if (Index < 56) {
        Context->MessageBlock[Index] = 0x80;
        Index += 1;
        while (Index < 56) {
            Context->MessageBlock[Index] = 0;
            Index += 1;
        }

    } else {
        Context->MessageBlock[Index] = 0x80;
        Index += 1;
        while (Index < sizeof(Context->MessageBlock)) {
            Context->MessageBlock[Index] = 0;
            Index += 1;
        }

        CypSha256ProcessMessage(Context);
        RtlZeroMemory(Context->MessageBlock, 56);
    }

    Context->Length += Context->BlockIndex * BITS_PER_BYTE;

    //
    // Store the message length in the last 8 octets.
    //

    Context->MessageBlock[56] = (UCHAR)(Context->Length >> (32 + 24));
    Context->MessageBlock[57] = (UCHAR)(Context->Length >> (32 + 16));
    Context->MessageBlock[58] = (UCHAR)(Context->Length >> (32 + 8));
    Context->MessageBlock[59] = (UCHAR)(Context->Length >> 32);
    Context->MessageBlock[60] = (UCHAR)(Context->Length >> 24);
    Context->MessageBlock[61] = (UCHAR)(Context->Length >> 16);
    Context->MessageBlock[62] = (UCHAR)(Context->Length >> 8);
    Context->MessageBlock[63] = (UCHAR)(Context->Length);
    CypSha256ProcessMessage(Context);
    return;
}

