//+-----------------------------------------------------------------------
//
// File:        MDWRAP.C
//
// Contents:    MDx Wrapper functions
//
//
// History:     25 Feb 92,  RichardW    Created
//
//------------------------------------------------------------------------


#ifndef KERNEL_MODE 

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>

#else 

#include <ntifs.h>

#endif

#include <string.h>
#include <malloc.h>



#include <kerbcon.h>
#include <security.h>
#include <cryptdll.h>
#ifdef WIN32_CHICAGO
#include <assert.h>
#undef ASSERT
#define ASSERT(exp) assert(exp)
#endif // WIN32_CHICAGO

#include "md4.h"
#include "md5.h"
#include "des.h"

typedef struct _MD5_DES_STATE_BUFFER {
    PCHECKSUM_BUFFER DesContext;
    MD5_CTX Md5Context;
} MD5_DES_STATE_BUFFER, *PMD5_DES_STATE_BUFFER;

typedef struct _MD5_DES_1510_STATE_BUFFER {
    PCHECKSUM_BUFFER DesContext;
    MD5_CTX Md5Context;
    UCHAR Confounder[DES_BLOCKLEN];
} MD5_DES_1510_STATE_BUFFER, *PMD5_DES_1510_STATE_BUFFER;

typedef struct _MD5_HMAC_STATE_BUFFER {
    MD5_CTX Md5Context;
    ULONG KeySize;
    UCHAR Key[ANYSIZE_ARRAY];
} MD5_HMAC_STATE_BUFFER, *PMD5_HMAC_STATE_BUFFER;


NTSTATUS NTAPI md4Initialize(ULONG, PCHECKSUM_BUFFER *);
NTSTATUS NTAPI md4InitializeEx(PUCHAR,ULONG, ULONG, PCHECKSUM_BUFFER *);
NTSTATUS NTAPI md4Sum(PCHECKSUM_BUFFER, ULONG, PUCHAR);
NTSTATUS NTAPI md4Finalize(PCHECKSUM_BUFFER, PUCHAR);
NTSTATUS NTAPI md4Finish(PCHECKSUM_BUFFER *);

NTSTATUS NTAPI md5Initialize(ULONG, PCHECKSUM_BUFFER *);
NTSTATUS NTAPI md5InitializeEx(PUCHAR, ULONG, ULONG, PCHECKSUM_BUFFER *);
NTSTATUS NTAPI md5Sum(PCHECKSUM_BUFFER, ULONG, PUCHAR);
NTSTATUS NTAPI md5Finalize(PCHECKSUM_BUFFER, PUCHAR);
NTSTATUS NTAPI md5Finish(PCHECKSUM_BUFFER *);


NTSTATUS NTAPI md25Initialize(ULONG, PCHECKSUM_BUFFER *);
NTSTATUS NTAPI md25InitializeEx(PUCHAR, ULONG, ULONG, PCHECKSUM_BUFFER *);
NTSTATUS NTAPI md25Sum(PCHECKSUM_BUFFER, ULONG, PUCHAR);
NTSTATUS NTAPI md25Finalize(PCHECKSUM_BUFFER, PUCHAR);
NTSTATUS NTAPI md25Finish(PCHECKSUM_BUFFER *);


NTSTATUS NTAPI md5DesInitialize(ULONG, PCHECKSUM_BUFFER *);
NTSTATUS NTAPI md5DesInitializeEx(PUCHAR, ULONG, ULONG,  PCHECKSUM_BUFFER *);
NTSTATUS NTAPI md5DesSum(PCHECKSUM_BUFFER, ULONG, PUCHAR);
NTSTATUS NTAPI md5DesFinalize(PCHECKSUM_BUFFER, PUCHAR);
NTSTATUS NTAPI md5DesFinish(PCHECKSUM_BUFFER *);

NTSTATUS NTAPI md5Rc4Initialize(ULONG, PCHECKSUM_BUFFER *);
NTSTATUS NTAPI md5Rc4InitializeEx(PUCHAR, ULONG, ULONG, PCHECKSUM_BUFFER *);
NTSTATUS NTAPI md5Rc4Sum(PCHECKSUM_BUFFER, ULONG, PUCHAR);
NTSTATUS NTAPI md5Rc4Finalize(PCHECKSUM_BUFFER, PUCHAR);
NTSTATUS NTAPI md5Rc4Finish(PCHECKSUM_BUFFER *);

NTSTATUS NTAPI md5HmacInitialize(ULONG, PCHECKSUM_BUFFER *);
NTSTATUS NTAPI md5HmacInitializeEx(PUCHAR, ULONG, ULONG, PCHECKSUM_BUFFER *);
NTSTATUS NTAPI md5Hmac2InitializeEx(PUCHAR, ULONG, ULONG, PCHECKSUM_BUFFER *);

NTSTATUS NTAPI md5HmacSum(PCHECKSUM_BUFFER, ULONG, PUCHAR);
NTSTATUS NTAPI md5HmacFinalize(PCHECKSUM_BUFFER, PUCHAR);
NTSTATUS NTAPI md5HmacFinish(PCHECKSUM_BUFFER *);

NTSTATUS NTAPI md5Des1510InitializeEx(PUCHAR, ULONG, ULONG,  PCHECKSUM_BUFFER *);
NTSTATUS NTAPI md5Des1510InitializeEx2(PUCHAR, ULONG, PUCHAR, ULONG, PCHECKSUM_BUFFER *);
NTSTATUS NTAPI md5Des1510Finalize(PCHECKSUM_BUFFER, PUCHAR);
NTSTATUS NTAPI md5Des1510Finish(PCHECKSUM_BUFFER *);

#ifdef KERNEL_MODE
#pragma alloc_text( PAGEMSG, md25Initialize )
#pragma alloc_text( PAGEMSG, md25InitializeEx )
#pragma alloc_text( PAGEMSG, md25Sum )
#pragma alloc_text( PAGEMSG, md25Finalize )
#pragma alloc_text( PAGEMSG, md25Finish )
#pragma alloc_text( PAGEMSG, md5DesInitialize )
#pragma alloc_text( PAGEMSG, md5DesInitializeEx )
#pragma alloc_text( PAGEMSG, md5DesSum )
#pragma alloc_text( PAGEMSG, md5DesFinalize )
#pragma alloc_text( PAGEMSG, md5DesFinish )
#pragma alloc_text( PAGEMSG, md5Rc4Initialize )
#pragma alloc_text( PAGEMSG, md5Rc4InitializeEx )
#pragma alloc_text( PAGEMSG, md5Rc4Sum )
#pragma alloc_text( PAGEMSG, md5Rc4Finalize )
#pragma alloc_text( PAGEMSG, md5Rc4Finish )
#pragma alloc_text( PAGEMSG, md5HmacInitialize )
#pragma alloc_text( PAGEMSG, md5HmacInitializeEx )
#pragma alloc_text( PAGEMSG, md5Hmac2InitializeEx )
#pragma alloc_text( PAGEMSG, md5HmacSum )
#pragma alloc_text( PAGEMSG, md5HmacFinalize )
#pragma alloc_text( PAGEMSG, md5HmacFinish )
#pragma alloc_text( PAGEMSG, md5Des1510InitializeEx )
#pragma alloc_text( PAGEMSG, md5Des1510InitializeEx2 )
#pragma alloc_text( PAGEMSG, md5Des1510Finalize )
#pragma alloc_text( PAGEMSG, md5Des1510Finish )
#endif 


CHECKSUM_FUNCTION    csfMD4 = {
    KERB_CHECKSUM_MD4,          // Checksum type
    MD4_LEN,                    // Checksum length
    0,
    md4Initialize,
    md4Sum,
    md4Finalize,
    md4Finish,
    md4InitializeEx,
    NULL};

CHECKSUM_FUNCTION    csfMD5 = {
    KERB_CHECKSUM_MD5,          // Checksum type
    MD5_LEN,                    // Checksum length
    0,
    md5Initialize,
    md5Sum,
    md5Finalize,
    md5Finish,
    md5InitializeEx,
    NULL};

CHECKSUM_FUNCTION csfMD25 = {
    KERB_CHECKSUM_MD25,                 // Checksum type
    (MD5_LEN / 2),                      // Checksum length
    CKSUM_KEYED,
    md25Initialize,
    md25Sum,
    md25Finalize,
    md25Finish,
    md25InitializeEx};

CHECKSUM_FUNCTION    csfDES_MAC_MD5 = {
    KERB_CHECKSUM_DES_MAC_MD5,          // Checksum type
    DES_BLOCKLEN,                       // Checksum length
    CKSUM_KEYED,
    md5DesInitialize,
    md5DesSum,
    md5DesFinalize,
    md5DesFinish,
    md5DesInitializeEx,
    NULL};

CHECKSUM_FUNCTION    csfRC4_MD5 = {
    KERB_CHECKSUM_RC4_MD5,              // Checksum type
    MD5_LEN,                            // Checksum length
    CKSUM_KEYED,
    md5Rc4Initialize,
    md5Rc4Sum,
    md5Rc4Finalize,
    md5Rc4Finish,
    md5Rc4InitializeEx,
    NULL};

CHECKSUM_FUNCTION    csfMD5_HMAC = {
    KERB_CHECKSUM_MD5_HMAC,              // Checksum type
    MD5_LEN,                            // Checksum length
    CKSUM_KEYED,
    md5HmacInitialize,
    md5HmacSum,
    md5HmacFinalize,
    md5HmacFinish,
    md5HmacInitializeEx,
    NULL};

CHECKSUM_FUNCTION    csfHMAC_MD5 = {
    KERB_CHECKSUM_HMAC_MD5,              // Checksum type
    MD5_LEN,                            // Checksum length
    CKSUM_KEYED,
    md5HmacInitialize,
    md5HmacSum,
    md5HmacFinalize,
    md5HmacFinish,
    md5Hmac2InitializeEx,
    NULL};

CHECKSUM_FUNCTION    csfDES_MAC_MD5_1510 = {
    KERB_CHECKSUM_MD5_DES,              // Checksum type
    DES_BLOCKLEN + MD5_LEN,             // Checksum length
    CKSUM_KEYED,
    md5DesInitialize,
    md5DesSum,
    md5Des1510Finalize,
    md5Des1510Finish,
    md5Des1510InitializeEx,
    md5Des1510InitializeEx2};

///////////////////////////////////////////////////////////////////////////

NTSTATUS NTAPI
md4Initialize(  ULONG               dwSeed,
                PCHECKSUM_BUFFER *   ppcsBuffer)
{
    MD4_CTX *   pMD4Context;

#ifdef KERNEL_MODE
    pMD4Context = ExAllocatePool(NonPagedPool, sizeof(MD4_CTX));
#else
    pMD4Context = malloc(sizeof(MD4_CTX));
#endif
    if (!pMD4Context)
    {
        return(STATUS_NO_MEMORY);
    }
    MD4Init(pMD4Context);
    *ppcsBuffer = pMD4Context;
    return(STATUS_SUCCESS);

}

NTSTATUS NTAPI
md4InitializeEx(
    PUCHAR Key,
    ULONG  KeySize,
    ULONG  MessageType,
    PCHECKSUM_BUFFER * ppcsBuffer
    )
{
    return(md4Initialize(0, ppcsBuffer));
}


NTSTATUS NTAPI
md4Sum( PCHECKSUM_BUFFER     pcsBuffer,
        ULONG               cbData,
        PUCHAR               pbData)
{
    MD4Update((MD4_CTX *) pcsBuffer, pbData, cbData);
    return(STATUS_SUCCESS);
}

NTSTATUS NTAPI
md4Finalize(    PCHECKSUM_BUFFER pcsBuffer,
                PUCHAR           pbSum)
{
    MD4Final((MD4_CTX *) pcsBuffer);
    memcpy(pbSum, ((MD4_CTX *) pcsBuffer)->digest, 16);
    return(STATUS_SUCCESS);
}

NTSTATUS NTAPI
md4Finish(  PCHECKSUM_BUFFER *   ppcsBuffer)
{
#ifdef KERNEL_MODE
    ExFreePool(*ppcsBuffer);
#else
    free(*ppcsBuffer);
#endif
    *ppcsBuffer = 0;
    return(STATUS_SUCCESS);
}

///////////////////////////////////////////////////////////////////////////

NTSTATUS NTAPI
md5Initialize(
    ULONG dwSeed,
    PCHECKSUM_BUFFER *  ppcsBuffer)
{
    MD5_CTX *   pMD5Context;

#ifdef KERNEL_MODE
    pMD5Context = ExAllocatePool(NonPagedPool, sizeof(MD5_CTX));
#else
    pMD5Context = malloc(sizeof(MD5_CTX));
#endif
    if (!pMD5Context)
    {
        return(STATUS_NO_MEMORY);
    }
    MD5Init(pMD5Context);
    *ppcsBuffer = pMD5Context;
    return(STATUS_SUCCESS);

}

NTSTATUS NTAPI
md5InitializeEx(
    PUCHAR Key,
    ULONG KeySize,
    ULONG  MessageType,
    PCHECKSUM_BUFFER *  ppcsBuffer)
{
    return(md5Initialize(0, ppcsBuffer));
}


NTSTATUS NTAPI
md5Sum( PCHECKSUM_BUFFER     pcsBuffer,
        ULONG               cbData,
        PUCHAR               pbData)
{
    MD5Update((MD5_CTX *) pcsBuffer, pbData, cbData);
    return(STATUS_SUCCESS);
}

NTSTATUS NTAPI
md5Finalize(    PCHECKSUM_BUFFER pcsBuffer,
                PUCHAR           pbSum)
{
    MD5Final((MD5_CTX *) pcsBuffer);
    memcpy(pbSum, ((MD5_CTX *) pcsBuffer)->digest, 16);
    return(STATUS_SUCCESS);
}

NTSTATUS NTAPI
md5Finish(  PCHECKSUM_BUFFER *   ppcsBuffer)
{
#ifdef KERNEL_MODE
    ExFreePool(*ppcsBuffer);
#else
    free(*ppcsBuffer);
#endif
    *ppcsBuffer = 0;
    return(STATUS_SUCCESS);
}

///////////////////////////////////////////////////////////////////////////

NTSTATUS NTAPI desPlainInitialize(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *);
NTSTATUS NTAPI desEncrypt(PCRYPT_STATE_BUFFER, PUCHAR, ULONG, PUCHAR, PULONG);
NTSTATUS NTAPI desFinish(PCRYPT_STATE_BUFFER *);

NTSTATUS NTAPI desMacInitialize(ULONG, PCHECKSUM_BUFFER *);
NTSTATUS NTAPI desMacInitializeEx(PUCHAR,ULONG, ULONG, PCHECKSUM_BUFFER *);
NTSTATUS NTAPI desMacSum(PCHECKSUM_BUFFER, ULONG, PUCHAR);
NTSTATUS NTAPI desMacFinalize(PCHECKSUM_BUFFER, PUCHAR);
NTSTATUS NTAPI desMacFinish(PCHECKSUM_BUFFER *);

NTSTATUS NTAPI
md25Initialize(
    ULONG dwSeed,
    PCHECKSUM_BUFFER *  ppcsBuffer)
{
    return(STATUS_NOT_IMPLEMENTED);
}

NTSTATUS NTAPI
md25InitializeEx(
    PUCHAR Key,
    ULONG KeySize,
    ULONG  MessageType,
    PCHECKSUM_BUFFER *  ppcsBuffer)
{
    MD5_CTX *   pMD5Context;
    NTSTATUS Status = STATUS_SUCCESS;
    UCHAR TempBuffer[16];
    ULONG OutputSize;
    UCHAR TempKey[DES_KEYSIZE];
    PCRYPT_STATE_BUFFER DesContext = NULL;
    PCRYPTO_SYSTEM DesSystem;
    ULONG Index;

    memset(
        TempBuffer,
        0,
        sizeof(TempBuffer)
        );

    if (KeySize != DES_KEYSIZE)
    {
        return(STATUS_INVALID_PARAMETER);
    }

#ifdef KERNEL_MODE
    pMD5Context = (MD5_CTX *) ExAllocatePool(NonPagedPool,sizeof(MD5_CTX));
#else
    pMD5Context = (MD5_CTX *) LocalAlloc(0,sizeof(MD5_CTX));
#endif
    if (!pMD5Context)
    {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

    MD5Init(pMD5Context);

    //
    // Prepare the key by byte reversing it.
    //

    for (Index = 0; Index  < DES_KEYSIZE ; Index++ )
    {
        TempKey[Index] = Key[(DES_KEYSIZE - 1) - Index];
    }

    Status = CDLocateCSystem(
                KERB_ETYPE_DES_PLAIN,
                &DesSystem
                );
    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }

    Status = DesSystem->Initialize(
                TempKey,
                DES_KEYSIZE,
                0,              // no options
                &DesContext
                );
    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }
    Status = DesSystem->Encrypt(
                DesContext,
                TempBuffer,
                sizeof(TempBuffer),
                TempBuffer,
                &OutputSize
                );
    ASSERT(OutputSize == sizeof(TempBuffer));
    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }

    (VOID) DesSystem->Discard(&DesContext);

    //
    // Now MD5 update with the encrypted buffer
    //

    MD5Update(
        pMD5Context,
        TempBuffer,
        sizeof(TempBuffer)
        );


    *ppcsBuffer = (PCHECKSUM_BUFFER) pMD5Context;

Cleanup:
    if (!NT_SUCCESS(Status) && (pMD5Context != NULL))
    {
#ifdef KERNEL_MODE
        ExFreePool(pMD5Context);
#else
        LocalFree(pMD5Context);
#endif
    }
    return(Status);
}


NTSTATUS NTAPI
md25Sum( PCHECKSUM_BUFFER     pcsBuffer,
        ULONG               cbData,
        PUCHAR               pbData)
{
    MD5Update((MD5_CTX *) pcsBuffer, pbData, cbData);
    return(STATUS_SUCCESS);
}

NTSTATUS NTAPI
md25Finalize(
    PCHECKSUM_BUFFER pcsBuffer,
    PUCHAR pbSum)
{
    MD5Final((MD5_CTX *) pcsBuffer);
    memcpy(pbSum, ((MD5_CTX *) pcsBuffer)->digest, MD5_LEN/2);
    return(STATUS_SUCCESS);
}

NTSTATUS NTAPI
md25Finish(  PCHECKSUM_BUFFER *   ppcsBuffer)
{
#ifdef KERNEL_MODE
    ExFreePool(*ppcsBuffer);
#else
    LocalFree(*ppcsBuffer);
#endif
    *ppcsBuffer = 0;
    return(STATUS_SUCCESS);
}


///////////////////////////////////////////////////////////////////////////


NTSTATUS NTAPI
md5DesInitialize(
    ULONG dwSeed,
    PCHECKSUM_BUFFER *  ppcsBuffer)
{
    return(STATUS_NOT_IMPLEMENTED);
}

NTSTATUS NTAPI
md5DesInitializeEx(
    PUCHAR Key,
    ULONG KeySize,
    ULONG  MessageType,
    PCHECKSUM_BUFFER *  ppcsBuffer)
{
    PMD5_DES_STATE_BUFFER  pMD5Context;
    NTSTATUS Status = STATUS_SUCCESS;

    if (KeySize != DES_KEYSIZE)
    {
        return(STATUS_INVALID_PARAMETER);
    }

#ifdef KERNEL_MODE
    pMD5Context = (PMD5_DES_STATE_BUFFER) ExAllocatePool(NonPagedPool,sizeof(MD5_DES_STATE_BUFFER));
#else
    pMD5Context = (PMD5_DES_STATE_BUFFER) LocalAlloc(0,sizeof(MD5_DES_STATE_BUFFER));
#endif
    if (!pMD5Context)
    {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

    MD5Init(&pMD5Context->Md5Context);

    //
    // Compute the initialization for the MD5
    //

    Status = desPlainInitialize(
                Key,
                KeySize,
                0,
                &pMD5Context->DesContext
                );

    if (!NT_SUCCESS(Status))
    {
#ifdef KERNEL_MODE
        ExFreePool(pMD5Context);
#else
        LocalFree(pMD5Context);
#endif
        return(Status);
    }

    *ppcsBuffer = (PCHECKSUM_BUFFER) pMD5Context;
    return(STATUS_SUCCESS);
}


NTSTATUS NTAPI
md5DesSum( PCHECKSUM_BUFFER     pcsBuffer,
        ULONG               cbData,
        PUCHAR               pbData)
{
    PMD5_DES_STATE_BUFFER Context = (PMD5_DES_STATE_BUFFER) pcsBuffer;
    MD5Update(&Context->Md5Context, pbData, cbData);
    return(STATUS_SUCCESS);
}

NTSTATUS NTAPI
md5DesFinalize(    PCHECKSUM_BUFFER pcsBuffer,
                PUCHAR           pbSum)
{
    ULONG OutputLength;
    PMD5_DES_STATE_BUFFER Context = (PMD5_DES_STATE_BUFFER) pcsBuffer;
    MD5Final(&Context->Md5Context);

    desEncrypt(
        Context->DesContext,
        Context->Md5Context.digest,
        MD5_LEN,
        Context->Md5Context.digest,
        &OutputLength
        );

    RtlCopyMemory(
        pbSum,
        Context->Md5Context.digest + MD5_LEN-DES_BLOCKLEN,
        DES_BLOCKLEN
        );

    return(STATUS_SUCCESS);
}

NTSTATUS NTAPI
md5DesFinish(  PCHECKSUM_BUFFER *   ppcsBuffer)
{
    PMD5_DES_STATE_BUFFER Context = (PMD5_DES_STATE_BUFFER) *ppcsBuffer;
    desFinish(&Context->DesContext);
#ifdef KERNEL_MODE
    ExFreePool(Context);
#else
    LocalFree(Context);
#endif
    *ppcsBuffer = 0;
    return(STATUS_SUCCESS);
}


///////////////////////////////////////////////////////////////////////////


NTSTATUS NTAPI rc4Md4Initialize(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *);
NTSTATUS NTAPI rc4LmInitialize(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *);
NTSTATUS NTAPI rc4ShaInitialize(PUCHAR, ULONG, ULONG, PCRYPT_STATE_BUFFER *);
NTSTATUS NTAPI rc4Initialize(PUCHAR, ULONG, ULONG, ULONG, PCRYPT_STATE_BUFFER *);
NTSTATUS NTAPI rc4Encrypt(PCRYPT_STATE_BUFFER, PUCHAR, ULONG, PUCHAR, PULONG);
NTSTATUS NTAPI rc4Decrypt(PCRYPT_STATE_BUFFER, PUCHAR, ULONG, PUCHAR, PULONG);
NTSTATUS NTAPI rc4Finish(PCRYPT_STATE_BUFFER *);

NTSTATUS NTAPI
md5Rc4Initialize(
    ULONG dwSeed,
    PCHECKSUM_BUFFER *  ppcsBuffer)
{
    return(STATUS_NOT_IMPLEMENTED);
}

NTSTATUS NTAPI
md5Rc4InitializeEx(
    PUCHAR Key,
    ULONG KeySize,
    ULONG  MessageType,
    PCHECKSUM_BUFFER *  ppcsBuffer)
{
    PMD5_DES_STATE_BUFFER  pMD5Context;
    NTSTATUS Status = STATUS_SUCCESS;

#ifdef KERNEL_MODE
    pMD5Context = (PMD5_DES_STATE_BUFFER) ExAllocatePool(NonPagedPool,sizeof(MD5_DES_STATE_BUFFER));
#else
    pMD5Context = (PMD5_DES_STATE_BUFFER) LocalAlloc(0,sizeof(MD5_DES_STATE_BUFFER));
#endif
    if (!pMD5Context)
    {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

    MD5Init(&pMD5Context->Md5Context);


    Status = rc4Initialize(
                Key,
                KeySize,
                0,              // no options
                0,
                &pMD5Context->DesContext
                );
    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }


    if (!NT_SUCCESS(Status))
    {
#ifdef KERNEL_MODE
        ExFreePool(pMD5Context);
#else
        LocalFree(pMD5Context);
#endif
        return(Status);
    }

    *ppcsBuffer = (PCHECKSUM_BUFFER) pMD5Context;

Cleanup:
    if (!NT_SUCCESS(Status) && (pMD5Context != NULL))
    {
#ifdef KERNEL_MODE
        ExFreePool(pMD5Context);
#else
        LocalFree(pMD5Context);
#endif
    }
    return(STATUS_SUCCESS);
}


NTSTATUS NTAPI
md5Rc4Sum( PCHECKSUM_BUFFER     pcsBuffer,
        ULONG               cbData,
        PUCHAR               pbData)
{
    PMD5_DES_STATE_BUFFER Context = (PMD5_DES_STATE_BUFFER) pcsBuffer;
    MD5Update(&Context->Md5Context, pbData, cbData);
    return(STATUS_SUCCESS);
}

NTSTATUS NTAPI
md5Rc4Finalize(    PCHECKSUM_BUFFER pcsBuffer,
                PUCHAR           pbSum)
{
    PMD5_DES_STATE_BUFFER Context = (PMD5_DES_STATE_BUFFER) pcsBuffer;
    ULONG OutputSize;
    NTSTATUS Status;

    MD5Final(&Context->Md5Context);

    memcpy(
        pbSum,
        Context->Md5Context.digest,
        MD5_LEN
        );
    Status = rc4Encrypt(
                Context->DesContext,
                pbSum,
                MD5_LEN,
                pbSum,
                &OutputSize
                );


    ASSERT(OutputSize == MD5_LEN);

    if (!NT_SUCCESS(Status))
    {
        return(Status);
    }



    return(STATUS_SUCCESS);
}

NTSTATUS NTAPI
md5Rc4Finish(  PCHECKSUM_BUFFER *   ppcsBuffer)
{
    PMD5_DES_STATE_BUFFER Context = (PMD5_DES_STATE_BUFFER) *ppcsBuffer;

    (VOID) rc4Finish(&Context->DesContext);
#ifdef KERNEL_MODE
    ExFreePool(Context);
#else
    LocalFree(Context);
#endif
    *ppcsBuffer = 0;
    return(STATUS_SUCCESS);
}


///////////////////////////////////////////////////////////////////////////

BOOLEAN
md5Hmac(
    IN PUCHAR pbKeyMaterial,
    IN ULONG cbKeyMaterial,
    IN PUCHAR pbData,
    IN ULONG cbData,
    IN PUCHAR pbData2,
    IN ULONG cbData2,
    OUT PUCHAR HmacData
    );


NTSTATUS NTAPI
md5HmacInitialize(
    ULONG dwSeed,
    PCHECKSUM_BUFFER *  ppcsBuffer)
{
    return(STATUS_NOT_IMPLEMENTED);
}

NTSTATUS NTAPI
md5HmacInitializeEx(
    PUCHAR Key,
    ULONG KeySize,
    ULONG  MessageType,
    PCHECKSUM_BUFFER *  ppcsBuffer)
{
    PMD5_HMAC_STATE_BUFFER  pMD5Context = NULL;
    NTSTATUS Status = STATUS_SUCCESS;

#ifdef KERNEL_MODE
    pMD5Context = (PMD5_HMAC_STATE_BUFFER) ExAllocatePool(NonPagedPool,sizeof(MD5_HMAC_STATE_BUFFER) + KeySize);
#else
    pMD5Context = (PMD5_HMAC_STATE_BUFFER) LocalAlloc(0,sizeof(MD5_HMAC_STATE_BUFFER) + KeySize);
#endif
    if (!pMD5Context)
    {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

    RtlCopyMemory(
        pMD5Context->Key,
        Key,
        KeySize
        );
    pMD5Context->KeySize = KeySize;

    MD5Init(&pMD5Context->Md5Context);
    MD5Update(&pMD5Context->Md5Context, (PUCHAR) &MessageType, sizeof(ULONG));

    *ppcsBuffer = (PCHECKSUM_BUFFER) pMD5Context;

    return(STATUS_SUCCESS);
}

NTSTATUS NTAPI
md5Hmac2InitializeEx(
    PUCHAR Key,
    ULONG KeySize,
    ULONG  MessageType,
    PCHECKSUM_BUFFER *  ppcsBuffer)
{
    PMD5_HMAC_STATE_BUFFER  pMD5Context = NULL;
    NTSTATUS Status = STATUS_SUCCESS;

#ifdef KERNEL_MODE
    pMD5Context = (PMD5_HMAC_STATE_BUFFER) ExAllocatePool(NonPagedPool,sizeof(MD5_HMAC_STATE_BUFFER) + MD5_LEN);
#else
    pMD5Context = (PMD5_HMAC_STATE_BUFFER) LocalAlloc(0,sizeof(MD5_HMAC_STATE_BUFFER) + MD5_LEN);
#endif
    if (!pMD5Context)
    {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

    md5Hmac(
        Key,
        KeySize,
        "signaturekey",
        sizeof("signaturekey"),
        NULL,
        0,
        pMD5Context->Key
        );
    pMD5Context->KeySize = MD5_LEN;

    MD5Init(&pMD5Context->Md5Context);
    MD5Update(&pMD5Context->Md5Context, (PUCHAR) &MessageType, sizeof(ULONG));

    *ppcsBuffer = (PCHECKSUM_BUFFER) pMD5Context;

    return(STATUS_SUCCESS);
}


NTSTATUS NTAPI
md5HmacSum( PCHECKSUM_BUFFER     pcsBuffer,
        ULONG               cbData,
        PUCHAR               pbData)
{
    PMD5_HMAC_STATE_BUFFER Context = (PMD5_HMAC_STATE_BUFFER) pcsBuffer;
    MD5Update(&Context->Md5Context, pbData, cbData);
    return(STATUS_SUCCESS);
}

NTSTATUS NTAPI
md5HmacFinalize(    PCHECKSUM_BUFFER pcsBuffer,
                PUCHAR           pbSum)
{
    PMD5_HMAC_STATE_BUFFER Context = (PMD5_HMAC_STATE_BUFFER) pcsBuffer;

    MD5Final(&Context->Md5Context);

    if (!md5Hmac(
            Context->Key,
            Context->KeySize,
            Context->Md5Context.digest,
            MD5_LEN,
            NULL,               // no secondary material
            0,
            pbSum))
    {
        return(STATUS_UNSUCCESSFUL);
    }


    return(STATUS_SUCCESS);
}

NTSTATUS NTAPI
md5HmacFinish(  PCHECKSUM_BUFFER *   ppcsBuffer)
{
    PMD5_HMAC_STATE_BUFFER Context = (PMD5_HMAC_STATE_BUFFER) *ppcsBuffer;

#ifdef KERNEL_MODE
    ExFreePool(Context);
#else
    LocalFree(Context);
#endif
    *ppcsBuffer = 0;
    return(STATUS_SUCCESS);
}

NTSTATUS NTAPI
md5Des1510InitializeEx(
    PUCHAR Key,
    ULONG  KeySize,
    ULONG  MessageType,
    PCHECKSUM_BUFFER * ppcsBuffer
    )
{
    return(STATUS_NOT_IMPLEMENTED);
}

NTSTATUS NTAPI
md5Des1510InitializeEx2(
    PUCHAR Key,
    ULONG  KeySize,
    PUCHAR ChecksumToVerify,
    ULONG  MessageType,
    PCHECKSUM_BUFFER * ppcsBuffer
    )
{
    ULONG *pul;
    ULONG *pul2;
    UCHAR FinalKey[DES_KEYSIZE];
    PMD5_DES_1510_STATE_BUFFER  pContext = NULL;
    ULONG cb = DES_BLOCKLEN;
    NTSTATUS Status = STATUS_SUCCESS;

    //
    // Make sure we were passed an appropriate key
    //

    if (KeySize != DES_KEYSIZE)
    {
        Status = STATUS_INVALID_PARAMETER;
        goto Cleanup;
    }

#ifdef KERNEL_MODE
    pContext = (PMD5_DES_1510_STATE_BUFFER) ExAllocatePool(NonPagedPool,sizeof(MD5_DES_1510_STATE_BUFFER));
#else
    pContext = (PMD5_DES_1510_STATE_BUFFER) LocalAlloc(0,sizeof(MD5_DES_1510_STATE_BUFFER));
#endif
    if (!pContext)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto Cleanup;
    }

    //
    // create the final key table
    //
    pul = (ULONG*)FinalKey;
    pul2 = (ULONG*)Key;
    *pul = *pul2 ^ 0xf0f0f0f0;
    pul = (ULONG*)(FinalKey + sizeof(ULONG));
    pul2 = (ULONG*)(Key + sizeof(ULONG));
    *pul = *pul2 ^ 0xf0f0f0f0;

    Status = desPlainInitialize(
                FinalKey,
                DES_KEYSIZE,
                0,
                &pContext->DesContext
                );
    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }

    //
    // Checksum was not passed in so generate a confounder
    //
    if (NULL == ChecksumToVerify)
    {
        CDGenerateRandomBits(pContext->Confounder,DES_BLOCKLEN);
    }
    else
    {
        // the IV is all zero so no need to use CBC on first block
        Status = desEncrypt(
                        pContext->DesContext,
                        ChecksumToVerify,
                        DES_BLOCKLEN,
                        pContext->Confounder,
                        &cb
                        );
        if (!NT_SUCCESS(Status))
        {
            goto Cleanup;
        }
    }

    MD5Init(&pContext->Md5Context);

    // hash in the confounder
    MD5Update(&pContext->Md5Context, pContext->Confounder, DES_BLOCKLEN);

    *ppcsBuffer = (PCHECKSUM_BUFFER) pContext;
Cleanup:
    if (Status != STATUS_SUCCESS)
    {
        if (NULL != pContext)
        {
#ifdef KERNEL_MODE
            ExFreePool(pContext);
#else
            LocalFree(pContext);
#endif
        }
    }

    return(Status);

}

NTSTATUS NTAPI
md5Des1510Finalize(
    PCHECKSUM_BUFFER pcsBuffer,
    PUCHAR           pbSum)
{
    UCHAR TmpBuffer[DES_BLOCKLEN * 2];
    PMD5_DES_1510_STATE_BUFFER pContext = (PMD5_DES_1510_STATE_BUFFER) pcsBuffer;
    ULONG cb = DES_BLOCKLEN * 2;
    NTSTATUS Status = STATUS_SUCCESS;

    memcpy(TmpBuffer, pContext->Confounder, DES_BLOCKLEN);
    memcpy(TmpBuffer + DES_BLOCKLEN, pContext->Md5Context.digest, MD5_LEN);

    Status = desEncrypt(
                    pContext->DesContext,
                    TmpBuffer,
                    DES_BLOCKLEN * 2,
                    pbSum,
                    &cb
                    );

    return(Status);
}

NTSTATUS NTAPI
md5Des1510Finish(  PCHECKSUM_BUFFER *   ppcsBuffer)
{
    PMD5_DES_1510_STATE_BUFFER pContext = (PMD5_DES_1510_STATE_BUFFER) *ppcsBuffer;
    desFinish(&pContext->DesContext);
#ifdef KERNEL_MODE
    ExFreePool(pContext);
#else
    LocalFree(pContext);
#endif
    *ppcsBuffer = 0;
    return(STATUS_SUCCESS);
}