// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
//
// Copyright (c) 1985-2000 Microsoft Corporation
//
// This file is part of the Microsoft Research IPv6 Network Protocol Stack.
// You should have received a copy of the Microsoft End-User License Agreement
// for this software along with this release; see the file "license.txt".
// If not, please see http://www.research.microsoft.com/msripv6/license.htm,
// or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
//
// Abstract:
//
// HMAC (Hashed? Message Authentication Code) wrapper for MD5 algorithm.
// This code is based on the algorithm given in RFC 2104, which for
// HMAC-MD5 is MD5(Key XOR outer pad, MD5(Key XOR inner pad, text)), where
// the inner pad is 64 bytes of 0x36 and the outer pad is 64 bytes of 0x5c.
//

#include <string.h>
#include "oscfg.h"
#include "md5.h"


//* HMAC_MD5KeyPrep - preprocess raw keying data into directly usuable form.
//
//  This routine is called to convert raw keying information into the most
//  convienient form for later processing.  For MD5, we hash keys larger than
//  64 bytes down to 64 bytes.  We also perform the XOR operations between the
//  key and the inner and outer pads here since they don't change once we have
//  the key.  Thus we return 128 bytes of data: 64 bytes of the (possibly
//  folded) key XOR'd with the inner pad, and 64 bytes of the key XOR'd with
//  the outer pad.
//
//  REVIEW: Instead of "Temp", we could operate directly "*Key".
//
void
HMAC_MD5KeyPrep(
    uchar *RawKey,    // Raw keying information.
    uint RawKeySize,  // Size of above in bytes.
    uchar *Key)       // Resulting 128 bytes of preprocessed key info.
{
    uchar Temp[128];
    uint Loop;

    //
    // Load raw key into temp storage.
    // Constrain size of keying information to 64 bytes.
    //
    memset(Temp, 0, 64);
    if (RawKeySize > 64) {
        MD5_CTX Context;

        //
        // Use MD5 to hash key down to 16 bytes.
        //
        MD5Init(&Context);
        MD5Update(&Context, RawKey, RawKeySize);
        MD5Final(&Context);
        memcpy(Temp, Context.digest, MD5DIGESTLEN);

    } else
        memcpy(Temp, RawKey, RawKeySize);

    //
    // The first 64 bytes of "Temp" contain our (possibly hashed) key.
    // Make a copy of this in the second 64 bytes.
    //
    memcpy(&Temp[64], Temp, 64);

    //
    // XOR the first 64 bytes with the inner pad, and the second 64 bytes
    // with the outer pad.
    //
    for (Loop = 0; Loop < 64; Loop++) {
        Temp[Loop] ^= 0x36;       // Inner Pad.
        Temp[Loop + 64] ^= 0x5c;  // Outer Pad.
    }

    //
    // Return the result to location designated by our caller.
    //
    memcpy(Key, Temp, 128);

    //
    // Zero sensitive information.
    // REVIEW: bother?
    //
    memset(Temp, 0, 16);
}


//* HMAC_MD5Init - prepare to process data.
// 
void
HMAC_MD5Init(
    void *Context,  // HMAC-MD5 context maintained across operations.
    uchar *Key)     // Keying information.
{
    MD5_CTX *MD5_Context = Context;

    //
    // Start off the inner hash.  I.e. "MD5(Key XOR inner pad, ...".
    //
    MD5Init(MD5_Context);
    MD5Update(MD5_Context, Key, 64);
}


//* HMAC_MD5Op - Process a chunk of data.
// 
void
HMAC_MD5Op(
    void *Context,  // HMAC-MD5 context maintained across operations.
    uchar *Key,     // Keying information.
    uchar *Data,    // Data to process.
    uint Len)       // Amount of above in bytes.
{
    MD5_CTX *MD5_Context = Context;

    //
    // Continue the inner hash.  I.e. "MD5(..., text)".
    //
    MD5Update(MD5_Context, Data, Len);
}


//* HMAC_MD5Finalize - close off processing current data and return result.
// 
//  REVIEW: Instead of "Temp", we could operate directly "*Result".
//
void
HMAC_MD5Final(
    void *Context,  // HMAC-MD5 context maintained across operations.
    uchar *Key,     // Keying information.
    uchar *Result)  // Where to put result of this process.
{
    uchar Temp[16];
    MD5_CTX *MD5_Context = Context;

    //
    // Finish the inner hash.
    //
    MD5Final(MD5_Context);
    memcpy(Temp, MD5_Context->digest, MD5DIGESTLEN);

    //
    // Perform the outer hash.  I.e. MD5(Key XOR outer pad, ...).
    // MD5Final returns the result directly to our caller.
    //
    MD5Init(MD5_Context);
    MD5Update(MD5_Context, &Key[64], 64);
    MD5Update(MD5_Context, Temp, 16);
    MD5Final(MD5_Context);
    memcpy(Result, MD5_Context->digest, MD5DIGESTLEN);

    //
    // Zero sensitive information.
    // REVIEW: bother?
    //
    memset(Temp, 0, 16);
}

void
HMAC_MD5_96Final(
    void *Context,  // HMAC-MD5 context maintained across operations.
    uchar *Key,     // Keying information.
    uchar *Result)  // Where to put result of this process.
{
    uchar Temp[16];
    MD5_CTX *MD5_Context = Context;

    //
    // Finish the inner hash.
    //
    MD5Final(MD5_Context);
    memcpy(Temp, MD5_Context->digest, MD5DIGESTLEN);

    //
    // Perform the outer hash.  I.e. MD5(Key XOR outer pad, ...).
    //
    MD5Init(MD5_Context);
    MD5Update(MD5_Context, &Key[64], 64);
    MD5Update(MD5_Context, Temp, 16);
    MD5Final(MD5_Context);

    //
    // Truncate the MD5 16 byte output to 12 bytes.
    // The first 12 bytes from the left are stored.
    //
    memcpy(Result, MD5_Context->digest, 12);

    //
    // Zero sensitive information.
    // REVIEW: bother?
    //
    memset(Temp, 0, 16);
}