/*++

Copyright (c) 2015 Minoca Corp. All Rights Reserved

Module Name:

    hmac.c

Abstract:

    This module computes a Hashed Message Authentication Code based on a
    message, key, and hash function.

Author:

    Evan Green 13-Jan-2015

Environment:

    Any

--*/

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

#include "cryptop.h"

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

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

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

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

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

CRYPTO_API
VOID
CySha1ComputeHmac (
    PUCHAR Message,
    ULONG Length,
    PUCHAR Key,
    ULONG KeyLength,
    UCHAR Digest[SHA1_HASH_SIZE]
    )

/*++

Routine Description:

    This routine obtains a SHA-1 HMAC signature based on the message and key.
    Note that the key must be no longer than the hash function's block size.

Arguments:

    Message - Supplies a pointer to the data buffer to hash and sign.

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

    Key - Supplies a pointer to the secret key buffer.

    KeyLength - Supplies the length of the secret key, in bytes. This must be
        less than or equal to 64 bytes, the block size of the SHA-1 hash
        function.

    Digest - Supplies a pointer where the HMAC digest will be returned. This
        buffer is assumed to be 20 bytes, the size of a SHA-1 hash.

Return Value:

    None.

--*/

{

    INT ByteIndex;
    SHA1_CONTEXT Context;
    UCHAR Ipad[64];
    UCHAR Opad[64];

    //
    // Copy the key into ipad and opad, and pad any remainder with zero.
    //

    if (KeyLength > sizeof(Ipad)) {
        KeyLength = sizeof(Ipad);
    }

    for (ByteIndex = 0; ByteIndex < KeyLength; ByteIndex += 1) {
        Ipad[ByteIndex] = Key[ByteIndex];
        Opad[ByteIndex] = Key[ByteIndex];
    }

    while (ByteIndex < sizeof(Ipad)) {
        Ipad[ByteIndex] = 0;
        Opad[ByteIndex] = 0;
        ByteIndex += 1;
    }

    //
    // XOR in the magic values to ipad and opad.
    //

    for (ByteIndex = 0; ByteIndex < sizeof(Ipad); ByteIndex += 1) {
        Ipad[ByteIndex] ^= 0x36;
        Opad[ByteIndex] ^= 0x5C;
    }

    //
    // Perform the double hash.
    //

    CySha1Initialize(&Context);
    CySha1AddContent(&Context, Ipad, sizeof(Ipad));
    CySha1AddContent(&Context, Message, Length);
    CySha1GetHash(&Context, Digest);
    CySha1Initialize(&Context);
    CySha1AddContent(&Context, Opad, sizeof(Opad));
    CySha1AddContent(&Context, Digest, SHA1_HASH_SIZE);
    CySha1GetHash(&Context, Digest);
    return;
}

CRYPTO_API
VOID
CySha256ComputeHmac (
    PUCHAR Message,
    ULONG Length,
    PUCHAR Key,
    ULONG KeyLength,
    UCHAR Digest[SHA256_HASH_SIZE]
    )

/*++

Routine Description:

    This routine obtains a SHA-256 HMAC signature based on the message and key.
    Note that the key must be no longer than the hash function's block size.

Arguments:

    Message - Supplies a pointer to the data buffer to hash and sign.

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

    Key - Supplies a pointer to the secret key buffer.

    KeyLength - Supplies the length of the secret key, in bytes. This must be
        less than or equal to 64 bytes, the block size of the SHA-1 hash
        function.

    Digest - Supplies a pointer where the HMAC digest will be returned. This
        buffer is assumed to be 64 bytes, the size of a SHA-256 hash.

Return Value:

    None.

--*/

{

    INT ByteIndex;
    SHA256_CONTEXT Context;
    UCHAR Ipad[64];
    UCHAR Opad[64];

    //
    // Copy the key into ipad and opad, and pad any remainder with zero.
    //

    if (KeyLength > sizeof(Ipad)) {
        KeyLength = sizeof(Ipad);
    }

    for (ByteIndex = 0; ByteIndex < KeyLength; ByteIndex += 1) {
        Ipad[ByteIndex] = Key[ByteIndex];
        Opad[ByteIndex] = Key[ByteIndex];
    }

    while (ByteIndex < sizeof(Ipad)) {
        Ipad[ByteIndex] = 0;
        Opad[ByteIndex] = 0;
        ByteIndex += 1;
    }

    //
    // XOR in the magic values to ipad and opad.
    //

    for (ByteIndex = 0; ByteIndex < sizeof(Ipad); ByteIndex += 1) {
        Ipad[ByteIndex] ^= 0x36;
        Opad[ByteIndex] ^= 0x5C;
    }

    //
    // Perform the double hash.
    //

    CySha256Initialize(&Context);
    CySha256AddContent(&Context, Ipad, sizeof(Ipad));
    CySha256AddContent(&Context, Message, Length);
    CySha256GetHash(&Context, Digest);
    CySha256Initialize(&Context);
    CySha256AddContent(&Context, Opad, sizeof(Opad));
    CySha256AddContent(&Context, Digest, SHA256_HASH_SIZE);
    CySha256GetHash(&Context, Digest);
    return;
}

CRYPTO_API
VOID
CyMd5ComputeHmac (
    PUCHAR Message,
    ULONG Length,
    PUCHAR Key,
    ULONG KeyLength,
    UCHAR Digest[MD5_HASH_SIZE]
    )

/*++

Routine Description:

    This routine obtains an MD5 HMAC signature based on the message and key.
    Note that the key must be no longer than the hash function's block size.

Arguments:

    Message - Supplies a pointer to the data buffer to hash and sign.

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

    Key - Supplies a pointer to the secret key buffer.

    KeyLength - Supplies the length of the secret key, in bytes. This must be
        less than or equal to 64 bytes, the block size of the SHA-1 hash
        function.

    Digest - Supplies a pointer where the HMAC digest will be returned. This
        buffer is assumed to be 16 bytes, the size of an MD5 hash.

Return Value:

    None.

--*/

{

    INT ByteIndex;
    MD5_CONTEXT Context;
    UCHAR Ipad[64];
    UCHAR Opad[64];

    //
    // Copy the key into ipad and opad, and pad any remainder with zero.
    //

    if (KeyLength > sizeof(Ipad)) {
        KeyLength = sizeof(Ipad);
    }

    for (ByteIndex = 0; ByteIndex < KeyLength; ByteIndex += 1) {
        Ipad[ByteIndex] = Key[ByteIndex];
        Opad[ByteIndex] = Key[ByteIndex];
    }

    while (ByteIndex < sizeof(Ipad)) {
        Ipad[ByteIndex] = 0;
        Opad[ByteIndex] = 0;
        ByteIndex += 1;
    }

    //
    // XOR in the magic values to ipad and opad.
    //

    for (ByteIndex = 0; ByteIndex < sizeof(Ipad); ByteIndex += 1) {
        Ipad[ByteIndex] ^= 0x36;
        Opad[ByteIndex] ^= 0x5C;
    }

    //
    // Perform the double hash.
    //

    CyMd5Initialize(&Context);
    CyMd5AddContent(&Context, Ipad, sizeof(Ipad));
    CyMd5AddContent(&Context, Message, Length);
    CyMd5GetHash(&Context, Digest);
    CyMd5Initialize(&Context);
    CyMd5AddContent(&Context, Opad, sizeof(Opad));
    CyMd5AddContent(&Context, Digest, MD5_HASH_SIZE);
    CyMd5GetHash(&Context, Digest);
    return;
}

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

