//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1996 - 1999
//
//  File:       jvimage.cpp
//
//  Contents:   Microsoft SIP Provider (JAVA utilities)
//
//  History:    15-Feb-1997 pberkman   created
//
//--------------------------------------------------------------------------

#include "global.hxx"

BOOL SeekAndReadFile(HANDLE hFile, DWORD lFileOffset,BYTE *pb,
                     DWORD cb);

BOOL SeekAndWriteFile(HANDLE hFile, DWORD lFileOffset, BYTE *pb,
                      DWORD cb);

typedef void *HSHPDIGESTDATA;
typedef BOOL (WINAPI *PFN_SHP_DIGEST_DATA)(HSHPDIGESTDATA hDigestData,
                                           BYTE *pbData,
                                           DWORD cbData);

typedef void *HSHPSIGNFILE;


typedef struct _JAVA_OPEN_ARG 
{
    HANDLE  hFile;
} JAVA_OPEN_ARG, *PJAVA_OPEN_ARG;


typedef struct _JAVA_FUNC_PARA 
{
    HANDLE              hFile;
    BYTE                *pbSignedData;
} JAVA_FUNC_PARA, *PJAVA_FUNC_PARA;

typedef struct _JAVA_DIGEST_PARA 
{
    BOOL                fDisableDigest;
    PFN_SHP_DIGEST_DATA pfnDigestData;
    HSHPDIGESTDATA      hDigestData;
} JAVA_DIGEST_PARA, *PJAVA_DIGEST_PARA;

typedef struct _JAVA_SIGN_PARA 
{
    WORD                wConstPoolCount;
    WORD                wSignConstPoolIndex;
    LONG                lSignConstPoolOffset;
    WORD                wAttrCount;
    LONG                lAttrCountOffset;
    WORD                wSignAttrIndex;
    DWORD               dwSignAttrLength;
    LONG                lSignAttrOffset;
    LONG                lEndOfFileOffset;
} JAVA_SIGN_PARA, *PJAVA_SIGN_PARA;

typedef struct _JAVA_READ_PARA 
{
    BOOL                fResult;
    DWORD               dwLastError;
    LONG                lFileOffset;
    DWORD               cbCacheRead;
    DWORD               cbCacheRemain;
} JAVA_READ_PARA, *PJAVA_READ_PARA;

#define JAVA_READ_CACHE_LEN 512

typedef struct _JAVA_PARA 
{
    JAVA_FUNC_PARA      Func;
    JAVA_DIGEST_PARA    Digest;
    JAVA_SIGN_PARA      Sign;
    JAVA_READ_PARA      Read;
    BYTE                rgbCache[JAVA_READ_CACHE_LEN];
} JAVA_PARA, *PJAVA_PARA;



#define JAVA_MAGIC          0xCAFEBABE
#define JAVA_MINOR_VERSION  3
#define JAVA_MAJOR_VERSION  45

// Constant Pool tags
//
// Note: CONSTANT_Long and CONSTANT_Double use two constant pool indexes.
enum 
{
    CONSTANT_Utf8                   = 1,
    CONSTANT_Unicode                = 2,
    CONSTANT_Integer                = 3,
    CONSTANT_Float                  = 4,
    CONSTANT_Long                   = 5,
    CONSTANT_Double                 = 6,
    CONSTANT_Class                  = 7,
    CONSTANT_String                 = 8,
    CONSTANT_Fieldref               = 9,
    CONSTANT_Methodref              = 10,
    CONSTANT_InterfaceMethodref     = 11,
    CONSTANT_NameAndType            = 12
};

// Constant Pool Info lengths (excludes the tag)
DWORD rgConstPoolLength[] = 
{
    0, // tag of zero not used
    0, // CONSTANT_Utf8 (special case)
    0, // CONSTANT_Unicode (special case)
    4, // CONSTANT_Integer_info
    4, // CONSTANT_Float_info
    8, // CONSTANT_Long_info
    8, // CONSTANT_Double_info
    2, // CONSTANT_Class_info
    2, // CONSTANT_String_info
    4, // CONSTANT_Fieldref_info
    4, // CONSTANT_Methodref_info
    4, // CONSTANT_InterfaceMethodref_info
    4  // CONSTANT_NameAndType_info
};

static inline void *ShpAlloc(DWORD cbytes)
{
    void    *pvRet;

    pvRet = (void *)new BYTE[cbytes];

    if (!(pvRet))
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
    }
    return(pvRet);
}

static inline void ShpFree(void *pv)
{
    if (pv)
    {
        delete pv;
    }
}

// The following functions convert to/from Java bytes which are big endian
static inline void ToJavaU2(WORD w, BYTE rgb[])
{
    rgb[0] = HIBYTE(w);
    rgb[1] = LOBYTE(w);
}
static inline WORD FromJavaU2(BYTE rgb[])
{
    return  ((WORD)rgb[0]<<8) | ((WORD)rgb[1]<<0);
}

static inline void ToJavaU4(DWORD dw, BYTE rgb[])
{
    rgb[0] = HIBYTE(HIWORD(dw));
    rgb[1] = LOBYTE(HIWORD(dw));
    rgb[2] = HIBYTE(LOWORD(dw));
    rgb[3] = LOBYTE(LOWORD(dw));
}
static inline DWORD FromJavaU4(BYTE rgb[])
{
    return  ((DWORD)rgb[0]<<24) |
            ((DWORD)rgb[1]<<16) |
            ((DWORD)rgb[2]<<8)  |
            ((DWORD)rgb[3]<<0);
}

#define CONST_POOL_COUNT_OFFSET     8
#define UTF8_HDR_LENGTH             (1+2)
#define ATTR_HDR_LENGTH             (2+4)
#define SIGN_ATTR_NAME_LENGTH       19
#define SIGN_CONST_POOL_LENGTH      (UTF8_HDR_LENGTH + SIGN_ATTR_NAME_LENGTH)

static const char rgchSignAttrName[SIGN_ATTR_NAME_LENGTH + 1] =
                                "_digital_signature_";


//+-------------------------------------------------------------------------
//  Shift the bytes in the file.
//
//  If lbShift is positive, the bytes are shifted toward the end of the file.
//  If lbShift is negative, the bytes are shifted toward the start of the file.
//--------------------------------------------------------------------------
static
BOOL
JavaShiftFileBytes(
    IN HANDLE hFile,
    IN PBYTE pbCache,
    IN LONG cbCache,
    IN LONG lStartOffset,
    IN LONG lEndOffset,
    IN LONG lbShift
    )
{
    LONG cbTotalMove, cbMove;

    cbTotalMove = lEndOffset - lStartOffset;
    while (cbTotalMove) {
        cbMove = min(cbTotalMove, cbCache);

        if (lbShift > 0) {
            if (!SeekAndReadFile(hFile, lEndOffset - cbMove,
                    pbCache, cbMove))
                return FALSE;
            if (!SeekAndWriteFile(hFile, (lEndOffset - cbMove) + lbShift,
                    pbCache, cbMove))
                return FALSE;
            lEndOffset -= cbMove;
        } else if (lbShift < 0) {
            if (!SeekAndReadFile(hFile, lStartOffset, pbCache, cbMove))
                return FALSE;
            if (!SeekAndWriteFile(hFile, lStartOffset + lbShift,
                    pbCache, cbMove))
                return FALSE;
            lStartOffset += cbMove;
        }
        cbTotalMove -= cbMove;
    }
    return TRUE;
}


//+-------------------------------------------------------------------------
//  Low level functions for reading the Java Class File.
//
//  If not disabled, the read bytes are also hashed.
//
//  For an error, remaining unread values are zero'ed and
//  pPara->Read.fResult = FALSE.
//--------------------------------------------------------------------------
static void ReadJavaBytes(
    IN PJAVA_PARA pPara,
    OUT OPTIONAL BYTE *pb,  // if NULL, bytes are hashed and then skipped
    IN DWORD cb
    )
{
    DWORD cbCacheRemain = pPara->Read.cbCacheRemain;
    DWORD lFileOffset = pPara->Read.lFileOffset;
    BOOL fDisableDigest = pPara->Digest.pfnDigestData == NULL ||
                                pPara->Digest.fDisableDigest;

    if (!pPara->Read.fResult)
        goto ErrorReturn;

    while (cb > 0) {
        DWORD cbCopy;
        BYTE *pbCache;

        if (cbCacheRemain == 0) {
            if (!ReadFile(pPara->Func.hFile, pPara->rgbCache,
                    sizeof(pPara->rgbCache), &cbCacheRemain, NULL))
                goto ErrorReturn;
            if (cbCacheRemain == 0) goto ErrorReturn;
            pPara->Read.cbCacheRead = cbCacheRemain;
        }

        cbCopy = min(cb, cbCacheRemain);
        pbCache = &pPara->rgbCache[pPara->Read.cbCacheRead - cbCacheRemain];
        if (!fDisableDigest) {
            if (!pPara->Digest.pfnDigestData(
                    pPara->Digest.hDigestData,
                    pbCache,
                    cbCopy)) goto ErrorReturn;
        }
        if (pb) {
            memcpy(pb, pbCache, cbCopy);
            pb += cbCopy;
        }
        cb -= cbCopy;
        cbCacheRemain -= cbCopy;
        lFileOffset += cbCopy;
    }
    goto CommonReturn;

ErrorReturn:
    if (pPara->Read.fResult) {
        // First error
        pPara->Read.fResult = FALSE;
        pPara->Read.dwLastError = GetLastError();
    }
    if (pb && cb)
        memset(pb, 0, cb);
CommonReturn:
    pPara->Read.cbCacheRemain = cbCacheRemain;
    pPara->Read.lFileOffset = lFileOffset;

}

static void SkipJavaBytes(IN PJAVA_PARA pPara, IN DWORD cb)
{
    ReadJavaBytes(pPara, NULL, cb);
}

static BYTE ReadJavaU1(IN PJAVA_PARA pPara)
{
    BYTE b;
    ReadJavaBytes(pPara, &b, 1);
    return b;
}
static WORD ReadJavaU2(IN PJAVA_PARA pPara) 
{
    BYTE rgb[2];
    ReadJavaBytes(pPara, rgb, 2);
    return FromJavaU2(rgb);
}
static DWORD ReadJavaU4(IN PJAVA_PARA pPara) 
{
    BYTE rgb[4];
    ReadJavaBytes(pPara, rgb, 4);
    return FromJavaU4(rgb);
}


//+-------------------------------------------------------------------------
//  .
//--------------------------------------------------------------------------
static
BOOL
GetSignedDataFromJavaClassFile(
    IN HSHPSIGNFILE hSignFile,
    OUT BYTE **ppbSignedData,
    OUT DWORD *pcbSignedData
    )
{
    BOOL fResult;
    PJAVA_PARA pPara = (PJAVA_PARA) hSignFile;
    BYTE *pbSignedData = NULL;
    DWORD cbSignedData;

    cbSignedData = pPara->Sign.dwSignAttrLength;
    if (cbSignedData == 0) {
        SetLastError((DWORD)TRUST_E_NOSIGNATURE);
        goto ErrorReturn;
    }

    pbSignedData = pPara->Func.pbSignedData;
    if (pbSignedData == NULL) {
        if (NULL == (pbSignedData = (BYTE *) ShpAlloc(cbSignedData)))
            goto ErrorReturn;
        if (!SeekAndReadFile(
                pPara->Func.hFile,
                pPara->Sign.lSignAttrOffset + ATTR_HDR_LENGTH,
                pbSignedData,
                cbSignedData))
            goto ErrorReturn;
        pPara->Func.pbSignedData = pbSignedData;
    }

    fResult = TRUE;
    goto CommonReturn;

ErrorReturn:
    if (pbSignedData) {
        ShpFree(pbSignedData);
        pbSignedData = NULL;
    }
    cbSignedData = 0;
    fResult = FALSE;
CommonReturn:
    *ppbSignedData = pbSignedData;
    *pcbSignedData = cbSignedData;
    return fResult;
}

//+-------------------------------------------------------------------------
//  .
//--------------------------------------------------------------------------
static
BOOL
SetSignedDataIntoJavaClassFile(
    IN HSHPSIGNFILE hSignFile,
    IN const BYTE *pbSignedData,
    IN DWORD cbSignedData
    )
{
    PJAVA_PARA pPara = (PJAVA_PARA) hSignFile;
    HANDLE hFile = pPara->Func.hFile;

    if (pbSignedData == NULL || cbSignedData == 0)
        // Length only
        return TRUE;

    if (pPara->Sign.wSignConstPoolIndex == pPara->Sign.wConstPoolCount) {
        BYTE rgb[SIGN_CONST_POOL_LENGTH];
        // Add a new constant pool entry for the name of the
        // signed data attribute.

        // First, make room in the file by shifting all the bytes that follow.
        if (!JavaShiftFileBytes(
                pPara->Func.hFile,
                pPara->rgbCache,
                sizeof(pPara->rgbCache),
                pPara->Sign.lSignConstPoolOffset,
                pPara->Sign.lEndOfFileOffset,
                SIGN_CONST_POOL_LENGTH))
            return FALSE;

        // Update offsets that have been shifted
        pPara->Sign.lAttrCountOffset += SIGN_CONST_POOL_LENGTH;
        pPara->Sign.lSignAttrOffset += SIGN_CONST_POOL_LENGTH;
        pPara->Sign.lEndOfFileOffset += SIGN_CONST_POOL_LENGTH;

        // Increment u2 constant_pool_count and update in file
        pPara->Sign.wConstPoolCount++;
        ToJavaU2(pPara->Sign.wConstPoolCount, rgb);
        if (!SeekAndWriteFile(hFile, CONST_POOL_COUNT_OFFSET, rgb, 2))
            return FALSE;

        // Add constant pool entry for the sign attr name and update in file
        rgb[0] = CONSTANT_Utf8;
        ToJavaU2(SIGN_ATTR_NAME_LENGTH, &rgb[1]);
        memcpy(&rgb[1+2], rgchSignAttrName, SIGN_ATTR_NAME_LENGTH);
        if (!SeekAndWriteFile(hFile, pPara->Sign.lSignConstPoolOffset, rgb,
                SIGN_CONST_POOL_LENGTH))
            return FALSE;
    }

    if (pPara->Sign.dwSignAttrLength == 0) {
        // Add a new attribute for the signed data. The attribute will
        // be added at the end of the file.
        assert(pPara->Sign.lSignAttrOffset == pPara->Sign.lEndOfFileOffset);
        pPara->Sign.lEndOfFileOffset += ATTR_HDR_LENGTH + cbSignedData;

        // Increment u2 attribute_count and update in file
        BYTE rgb[2];
        pPara->Sign.wAttrCount++;
        ToJavaU2(pPara->Sign.wAttrCount, rgb);
        if (!SeekAndWriteFile(hFile, pPara->Sign.lAttrCountOffset, rgb, 2))
            return FALSE;

    } else {
        // The file already has a signed data attribute.

        // If its length is different from the new signed data
        // then, the bytes that follow the attribute will
        // need to be shifted by the difference in length of the old and new
        // signed data
        LONG lbShift = cbSignedData - pPara->Sign.dwSignAttrLength;
        if (lbShift != 0) {
            if (!JavaShiftFileBytes(
                    pPara->Func.hFile,
                    pPara->rgbCache,
                    sizeof(pPara->rgbCache),
                    pPara->Sign.lSignAttrOffset +
                            (ATTR_HDR_LENGTH + pPara->Sign.dwSignAttrLength),
                    pPara->Sign.lEndOfFileOffset,
                    lbShift))
            return FALSE;
            
            pPara->Sign.lEndOfFileOffset += lbShift;
        }
    }
    pPara->Sign.dwSignAttrLength = cbSignedData;

    {
        // Update the file with the signed data attribute
        BYTE rgb[ATTR_HDR_LENGTH];
        DWORD cbWritten;
        ToJavaU2(pPara->Sign.wSignConstPoolIndex, rgb); // u2 attribute_name
        ToJavaU4(cbSignedData, &rgb[2]);                // u4 attribute_length
        if (!SeekAndWriteFile(hFile, pPara->Sign.lSignAttrOffset, rgb,
                ATTR_HDR_LENGTH))
            return FALSE;
        if (!WriteFile(hFile, pbSignedData, cbSignedData, &cbWritten, NULL) ||
                cbWritten != cbSignedData)
            return FALSE;
    }

    // Set end of file
    if (0xFFFFFFFF == SetFilePointer(
            hFile,
            pPara->Sign.lEndOfFileOffset,
            NULL,           // lpDistanceToMoveHigh
            FILE_BEGIN))
        return FALSE;
    return SetEndOfFile(hFile);
}

//+-------------------------------------------------------------------------
//  Reads and optionally digests the Java Class file. Locates the signed data.
//--------------------------------------------------------------------------
static
BOOL
ProcessJavaClassFile(
    PJAVA_PARA pPara,
    BOOL fInit
    )
{
    char rgchTmpSignAttrName[SIGN_ATTR_NAME_LENGTH];
    WORD wLength;
    DWORD dwLength;
    WORD wCount;
    WORD wConstPoolCount;
    WORD wConstPoolIndex;
    WORD wSignConstPoolIndex;
    WORD wAttrCount;
    WORD wAttrIndex;
    WORD wAttrName;
    WORD wSignAttrIndex;
    LONG lAddConstPoolOffset;
    int i;

    memset(&pPara->Read, 0, sizeof(pPara->Read));
    pPara->Read.fResult = TRUE;
    if (0xFFFFFFFF == SetFilePointer(
            pPara->Func.hFile,
            0,              // lDistanceToMove
            NULL,           // lpDistanceToMoveHigh
            FILE_BEGIN))
        return FALSE;
    if (fInit) {
        memset(&pPara->Digest, 0, sizeof(pPara->Digest));
        memset(&pPara->Sign, 0, sizeof(pPara->Sign));
    }

    // Default is to be digested. We'll disable where appropriate. Note,
    // skipped bytes are still digested.
    pPara->Digest.fDisableDigest = FALSE;

    // Read / skip the fields at the beginning of the class file
    if (ReadJavaU4(pPara) != JAVA_MAGIC) 
    {  // u4 magic
        SetLastError(ERROR_BAD_FORMAT);
        return FALSE;
    }
    SkipJavaBytes(pPara, 2 + 2);            // u2 minor_version
                                            // u2 major_version

    pPara->Digest.fDisableDigest = TRUE;
    wConstPoolCount = ReadJavaU2(pPara);    // u2 constant_pool_count
    pPara->Digest.fDisableDigest = FALSE;

    // For fInit, wSignConstPoolIndex has already been zeroed
    wSignConstPoolIndex = pPara->Sign.wSignConstPoolIndex;

    // Iterate through the constant pools. Don't digest the constant pool
    // containing the _digital_signature_ name (wSignConstPoolIndex).
    // For fInit, find the last "_digital_signature_".
    //
    // Note: constant pool index 0 isn't stored in the file.
    wConstPoolIndex = 1;
    while (wConstPoolIndex < wConstPoolCount) {
        BYTE bTag;

        if (wConstPoolIndex == wSignConstPoolIndex)
            pPara->Digest.fDisableDigest = TRUE;

        bTag = ReadJavaU1(pPara);
        switch (bTag) {
        case CONSTANT_Utf8:
            wLength = ReadJavaU2(pPara);
            if (fInit && wLength == SIGN_ATTR_NAME_LENGTH) {
                ReadJavaBytes(pPara, (BYTE *) rgchTmpSignAttrName,
                    SIGN_ATTR_NAME_LENGTH);
                if (memcmp(rgchSignAttrName, rgchTmpSignAttrName,
                        SIGN_ATTR_NAME_LENGTH) == 0) {
                    wSignConstPoolIndex = wConstPoolIndex;
                    pPara->Sign.lSignConstPoolOffset =
                        pPara->Read.lFileOffset - SIGN_CONST_POOL_LENGTH;

                }
            } else
                SkipJavaBytes(pPara, wLength);
            break;
        case CONSTANT_Unicode:
            wLength = ReadJavaU2(pPara);
            SkipJavaBytes(pPara, ((DWORD) wLength) * 2);
            break;
        case CONSTANT_Integer:
        case CONSTANT_Float:
        case CONSTANT_Long:
        case CONSTANT_Double:
        case CONSTANT_Class:
        case CONSTANT_String:
        case CONSTANT_Fieldref:
        case CONSTANT_Methodref:
        case CONSTANT_InterfaceMethodref:
        case CONSTANT_NameAndType:
            SkipJavaBytes(pPara, rgConstPoolLength[bTag]);
            break;
        default:
            SetLastError(ERROR_BAD_FORMAT);
            return FALSE;
        }
            
        pPara->Digest.fDisableDigest = FALSE;

        if (bTag == CONSTANT_Long || bTag == CONSTANT_Double)
            wConstPoolIndex += 2;
        else
            wConstPoolIndex++;
    }

    if (fInit) {
        lAddConstPoolOffset = pPara->Read.lFileOffset;
        if (wSignConstPoolIndex == 0) {
            // Didn't find a constant pool for the digital_signature. Update
            // with where it will need to be added
            wSignConstPoolIndex = wConstPoolCount;
            pPara->Sign.lSignConstPoolOffset = lAddConstPoolOffset;
        }
    }

    // Globble up and hash the bytes until we reach the attributes which are
    // at the end of the file.

    SkipJavaBytes(pPara, 2 + 2 + 2);        // u2 access_flags
                                            // u2 this_class
                                            // u2 super_class
    wCount = ReadJavaU2(pPara);             // u2 interfaces_count
    // u2 interfaces[interfaces_count]
    SkipJavaBytes(pPara, ((DWORD) wCount) * 2);

    // Since fields and methods have identical class file storage, do this
    // twice.
    i = 2;
    while (i--) {
        wCount = ReadJavaU2(pPara);         // u2 fields_count | methods_count
        while (wCount--) {
            SkipJavaBytes(pPara, 2 + 2 + 2);    // u2 access_flags
                                                // u2 name_index
                                                // u2 signature_index
            wAttrCount = ReadJavaU2(pPara);     // u2 attributes_count
            while (wAttrCount--) {
                SkipJavaBytes(pPara, 2);            // u2 attribute_name
                dwLength = ReadJavaU4(pPara);       // u4 attribute_length
                SkipJavaBytes(pPara, dwLength);     // u1 info[attribute_length]
            }
        }
    }

    // Finally, the attributes. This is where the signed data is

    pPara->Sign.lAttrCountOffset = pPara->Read.lFileOffset;
    pPara->Digest.fDisableDigest = TRUE;
    wAttrCount = ReadJavaU2(pPara);         // u2 attributes_count
    pPara->Digest.fDisableDigest = FALSE;

    if (fInit) {
        pPara->Sign.wAttrCount = wAttrCount;
        wSignAttrIndex = 0xFFFF;
    } else
        wSignAttrIndex = pPara->Sign.wSignAttrIndex;

    for (wAttrIndex = 0; wAttrIndex < wAttrCount; wAttrIndex++) {
        if (wAttrIndex == wSignAttrIndex)
            pPara->Digest.fDisableDigest = TRUE;

        wAttrName = ReadJavaU2(pPara);
        dwLength = ReadJavaU4(pPara);       // u4 attribute_length
        SkipJavaBytes(pPara, dwLength);     // u1 info[attribute_length]
        if (fInit && wAttrName == wSignConstPoolIndex && dwLength > 0 &&
                wSignConstPoolIndex < wConstPoolCount) {
            wSignAttrIndex = wAttrIndex;
            pPara->Sign.lSignAttrOffset =
                pPara->Read.lFileOffset - (ATTR_HDR_LENGTH + dwLength);
            pPara->Sign.dwSignAttrLength = dwLength;
        }

        pPara->Digest.fDisableDigest = FALSE;
    }

    if (fInit) {
        if (wSignAttrIndex == 0xFFFF) {
            // Didn't find an attribute for the digital_signature. Update
            // with where it will need to be added
            wSignAttrIndex = wAttrCount;
            pPara->Sign.lSignAttrOffset = pPara->Read.lFileOffset;

            // Also, force us to use a new const pool for the name of the
            // attribute
            wSignConstPoolIndex = wConstPoolCount;
            pPara->Sign.lSignConstPoolOffset = lAddConstPoolOffset;
        }

        pPara->Sign.wSignConstPoolIndex = wSignConstPoolIndex;
        pPara->Sign.wConstPoolCount = wConstPoolCount;
        pPara->Sign.wSignAttrIndex = wSignAttrIndex;
        pPara->Sign.lEndOfFileOffset = pPara->Read.lFileOffset;
    }

    // Now check if we got any hash or file errors while processing the file
    return pPara->Read.fResult;
}

//+-------------------------------------------------------------------------
//  Digest the appropriate bytes from a java file, for a digital signature.
//--------------------------------------------------------------------------
BOOL
JavaGetDigestStream(
    IN      HANDLE          FileHandle,
    IN      DWORD           DigestLevel,
    IN      DIGEST_FUNCTION DigestFunction,
    IN      DIGEST_HANDLE   DigestHandle
    )
{
    BOOL        fRet;
    JAVA_PARA   Para;
    memset( &Para.Func, 0, sizeof(Para.Func));

    assert( DigestLevel == 0);
    Para.Func.hFile = FileHandle;
    if (!ProcessJavaClassFile( &Para, TRUE))
        goto ProcessJavaClassFileTrueError;

    Para.Digest.pfnDigestData = DigestFunction;
    Para.Digest.hDigestData = DigestHandle;

    if (!ProcessJavaClassFile( &Para, FALSE))
        goto ProcessJavaClassFileFalseError;

    fRet = TRUE;
CommonReturn:
    if (Para.Func.pbSignedData)
        ShpFree( Para.Func.pbSignedData);

    return fRet;

ErrorReturn:
    fRet = FALSE;
    goto CommonReturn;
TRACE_ERROR_EX(DBG_SS,ProcessJavaClassFileTrueError)
TRACE_ERROR_EX(DBG_SS,ProcessJavaClassFileFalseError)
}


//+-------------------------------------------------------------------------
//  Add a digital signature to a java file.
//--------------------------------------------------------------------------
BOOL
JavaAddCertificate(
    IN      HANDLE              FileHandle,
    IN      LPWIN_CERTIFICATE   Certificate,
    OUT     PDWORD              Index
    )
{
    BOOL        fRet;
    JAVA_PARA   Para;
    memset( &Para.Func, 0, sizeof(Para.Func));

    Para.Func.hFile = FileHandle;
    if (!ProcessJavaClassFile( &Para, TRUE))
        goto ProcessJavaClassFileTrueError;

    if (!SetSignedDataIntoJavaClassFile(
                (HSHPSIGNFILE)&Para,
                (PBYTE)&(Certificate->bCertificate),
                Certificate->dwLength - OFFSETOF(WIN_CERTIFICATE,bCertificate)))
        goto SetSignedDataIntoJavaClassFileError;

    fRet = TRUE;
CommonReturn:
    if (Para.Func.pbSignedData)
        ShpFree( Para.Func.pbSignedData);

    return fRet;

ErrorReturn:
    fRet = FALSE;
    goto CommonReturn;
TRACE_ERROR_EX(DBG_SS,ProcessJavaClassFileTrueError)
TRACE_ERROR_EX(DBG_SS,SetSignedDataIntoJavaClassFileError)
}


//+-------------------------------------------------------------------------
//  Remove a digital signature from a java file.
//--------------------------------------------------------------------------
BOOL
JavaRemoveCertificate(
    IN      HANDLE   FileHandle,
    IN      DWORD    Index
    )
{
    return FALSE;       
}


//+-------------------------------------------------------------------------
//  Enum the digital signatures in a java file.
//--------------------------------------------------------------------------
BOOL
JavaEnumerateCertificates(
    IN      HANDLE  FileHandle,
    IN      WORD    TypeFilter,
    OUT     PDWORD  CertificateCount,
    IN OUT  PDWORD  Indices OPTIONAL,
    IN OUT  DWORD   IndexCount  OPTIONAL
    )
{
    return FALSE;       
}


//+-------------------------------------------------------------------------
//  Get a digital signature from a java file.
//--------------------------------------------------------------------------
static
BOOL
I_JavaGetCertificate(
    IN      HANDLE              FileHandle,
    IN      DWORD               CertificateIndex,
    OUT     LPWIN_CERTIFICATE   Certificate,
    IN OUT OPTIONAL PDWORD      RequiredLength
    )
{
    BOOL        fRet;
    JAVA_PARA   Para;
    memset( &Para.Func, 0, sizeof(Para.Func));
    BYTE       *pbSignedData = NULL;
    DWORD       cbSignedData;
    DWORD       cbCert;
    DWORD       dwError;

    if (CertificateIndex != 0)
        goto IndexNonZeroError;

    Para.Func.hFile = FileHandle;
    if (!ProcessJavaClassFile( &Para, TRUE))
        goto ProcessJavaClassFileTrueError;

    if (!GetSignedDataFromJavaClassFile(
                (HSHPSIGNFILE)&Para,
                &pbSignedData,
                &cbSignedData))
        goto GetSignedDataFromJavaClassFileError;

    cbCert = OFFSETOF(WIN_CERTIFICATE,bCertificate) + cbSignedData;
    dwError = 0;
    __try {
        if (RequiredLength) {
            // RequiredLength non-NULL only if getting cert data
            if (*RequiredLength < cbCert) {
                *RequiredLength = cbCert;
                dwError = ERROR_INSUFFICIENT_BUFFER;
            } else {
                memcpy( Certificate->bCertificate, pbSignedData, cbSignedData);
            }
        }
        if (dwError == 0) {
            Certificate->dwLength         = cbCert;
            Certificate->wRevision        = WIN_CERT_REVISION_1_0;
            Certificate->wCertificateType = WIN_CERT_TYPE_PKCS_SIGNED_DATA;
        }
    } __except(EXCEPTION_EXECUTE_HANDLER) {
        dwError = ERROR_INVALID_PARAMETER;
    }

    if (dwError) {
        SetLastError( dwError);
        fRet = FALSE;
    } else {
        fRet = TRUE;
    }
CommonReturn:
    ShpFree( Para.Func.pbSignedData);
    return fRet;

ErrorReturn:
    fRet = FALSE;
    goto CommonReturn;
TRACE_ERROR_EX(DBG_SS,IndexNonZeroError)
TRACE_ERROR_EX(DBG_SS,ProcessJavaClassFileTrueError)
TRACE_ERROR_EX(DBG_SS,GetSignedDataFromJavaClassFileError)
}


//+-------------------------------------------------------------------------
//  Get a digital signature from a java file.
//--------------------------------------------------------------------------
BOOL
JavaGetCertificateData(
    IN      HANDLE              FileHandle,
    IN      DWORD               CertificateIndex,
    OUT     LPWIN_CERTIFICATE   Certificate,
    IN OUT  PDWORD              RequiredLength
    )
{
    BOOL        fRet;

    if (RequiredLength == NULL)
        goto RequiredLengthNullError;

    fRet = I_JavaGetCertificate(
                    FileHandle,
                    CertificateIndex,
                    Certificate,
                    RequiredLength
                    );

CommonReturn:
    return fRet;

ErrorReturn:
    fRet = FALSE;
    goto CommonReturn;
SET_ERROR(RequiredLengthNullError, ERROR_INVALID_PARAMETER)
}


//+-------------------------------------------------------------------------
//  Get the header of a digital signature from a java file.
//--------------------------------------------------------------------------
BOOL
JavaGetCertificateHeader(
    IN      HANDLE              FileHandle,
    IN      DWORD               CertificateIndex,
    IN OUT  LPWIN_CERTIFICATE   Certificateheader
    )
{
    return I_JavaGetCertificate(
                    FileHandle,
                    CertificateIndex,
                    Certificateheader,
                    NULL
                    );
}

//+-------------------------------------------------------------------------
//  Seeks and writes bytes to file
//--------------------------------------------------------------------------
BOOL
SeekAndWriteFile(
    IN HANDLE hFile,
    IN DWORD lFileOffset,
    IN BYTE *pb,
    IN DWORD cb
    )
{
    DWORD cbWritten;

    if (0xFFFFFFFF == SetFilePointer(
            hFile,
            lFileOffset,
            NULL,           // lpDistanceToMoveHigh
            FILE_BEGIN))
        return FALSE;
    if (!WriteFile(hFile, pb, cb, &cbWritten, NULL) || cbWritten != cb)
        return FALSE;

    return TRUE;
}

//+-------------------------------------------------------------------------
//  Seeks and reads bytes from file
//--------------------------------------------------------------------------
BOOL
SeekAndReadFile(
    IN HANDLE hFile,
    IN DWORD lFileOffset,
    OUT BYTE *pb,
    IN DWORD cb
    )
{
    DWORD cbRead;

    if (0xFFFFFFFF == SetFilePointer(
            hFile,
            lFileOffset,
            NULL,           // lpDistanceToMoveHigh
            FILE_BEGIN))
        return FALSE;
    if (!ReadFile(hFile, pb, cb, &cbRead, NULL) || cbRead != cb)
        return FALSE;

    return TRUE;
}




