//+-------------------------------------------------------------------------
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1995 - 1999
//
//  File:       ossutil.cpp
//
//  Contents:   OSS ASN.1 compiler utility helper functions.
//
//  Functions:  OssUtilReverseBytes
//              OssUtilAllocAndReverseBytes
//              OssUtilGetOctetString
//              OssUtilSetHugeInteger
//              OssUtilFreeHugeInteger
//              OssUtilGetHugeInteger
//              OssUtilSetHugeUINT
//              OssUtilGetHugeUINT
//              OssUtilSetBitString
//              OssUtilGetBitString
//              OssUtilSetBitStringWithoutTrailingZeroes
//              OssUtilGetIA5String
//              OssUtilSetUnicodeConvertedToIA5String
//              OssUtilFreeUnicodeConvertedToIA5String
//              OssUtilGetIA5StringConvertedToUnicode
//              OssUtilGetBMPString
//              OssUtilSetAny
//              OssUtilGetAny
//              OssUtilEncodeInfoEx
//              OssUtilEncodeInfo
//              OssUtilDecodeAndAllocInfo
//              OssUtilFreeInfo
//              OssUtilAllocStructInfoEx
//              OssUtilDecodeAndAllocInfoEx
//
//  The Get functions decrement *plRemainExtra and advance
//  *ppbExtra. When *plRemainExtra becomes negative, the functions continue
//  with the length calculation but stop doing any copies.
//  The functions don't return an error for a negative *plRemainExtra.
//
//  History:    17-Nov-96    philh   created
//--------------------------------------------------------------------------

#include "global.hxx"
#include <dbgdef.h>

// All the *pvInfo extra stuff needs to be aligned
#define INFO_LEN_ALIGN(Len)  ((Len + 7) & ~7)

//+-------------------------------------------------------------------------
//  Reverses a buffer of bytes in place
//--------------------------------------------------------------------------
void
WINAPI
OssUtilReverseBytes(
			IN OUT PBYTE pbIn,
			IN DWORD cbIn
            )
{
    // reverse in place
    PBYTE	pbLo;
    PBYTE	pbHi;
    BYTE	bTmp;

    if (0 == cbIn)
        return;

    for (pbLo = pbIn, pbHi = pbIn + cbIn - 1; pbLo < pbHi; pbHi--, pbLo++) {
        bTmp = *pbHi;
        *pbHi = *pbLo;
        *pbLo = bTmp;
    }
}

//+-------------------------------------------------------------------------
//  Reverses a buffer of bytes to a new buffer. OssUtilFree() must be
//  called to free allocated bytes.
//--------------------------------------------------------------------------
PBYTE
WINAPI
OssUtilAllocAndReverseBytes(
			IN PBYTE pbIn,
			IN DWORD cbIn
            )
{
    PBYTE	pbOut;
    PBYTE	pbSrc;
    PBYTE	pbDst;
    DWORD	cb;

    if (NULL == (pbOut = (PBYTE)OssUtilAlloc(cbIn)))
        return NULL;

    for (pbSrc = pbIn, pbDst = pbOut + cbIn - 1, cb = cbIn; cb > 0; cb--)
        *pbDst-- = *pbSrc++;
    return pbOut;
}


//+-------------------------------------------------------------------------
//  Get Octet String
//--------------------------------------------------------------------------
void
WINAPI
OssUtilGetOctetString(
        IN unsigned int OssLength,
        IN unsigned char *OssValue,
        IN DWORD dwFlags,
        OUT PCRYPT_DATA_BLOB pInfo,
        IN OUT BYTE **ppbExtra,
        IN OUT LONG *plRemainExtra
        )
{
    if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG) {
        if (*plRemainExtra >= 0) {
            pInfo->cbData = OssLength;
            pInfo->pbData = OssValue;
        }
    } else {
        LONG lRemainExtra = *plRemainExtra;
        BYTE *pbExtra = *ppbExtra;
        LONG lAlignExtra;
        LONG lData;
    
        lData = (LONG) OssLength;
        lAlignExtra = INFO_LEN_ALIGN(lData);
        lRemainExtra -= lAlignExtra;
        if (lRemainExtra >= 0) {
            if (lData > 0) {
                pInfo->pbData = pbExtra;
                pInfo->cbData = (DWORD) lData;
                memcpy(pbExtra, OssValue, lData);
            } else
                memset(pInfo, 0, sizeof(*pInfo));
            pbExtra += lAlignExtra;
        }
        *plRemainExtra = lRemainExtra;
        *ppbExtra = pbExtra;
    }
}

//+-------------------------------------------------------------------------
//  Set/Free/Get HugeInteger
//
//  BYTE reversal::
//   - this only needs to be done for little endian processors
//
//  For OssUtilSetInteger, OssUtilFreeInteger must be called to free
//  the allocated OssValue.
//--------------------------------------------------------------------------
BOOL
WINAPI
OssUtilSetHugeInteger(
        IN PCRYPT_INTEGER_BLOB pInfo,
        OUT unsigned int *pOssLength,
        OUT unsigned char **ppOssValue
        )
{
    if (pInfo->cbData > 0) {
        if (NULL == (*ppOssValue = OssUtilAllocAndReverseBytes(
                pInfo->pbData, pInfo->cbData))) {
            *pOssLength = 0;
            return FALSE;
        }
    } else
        *ppOssValue = NULL;
    *pOssLength = pInfo->cbData;
    return TRUE;
}

void
WINAPI
OssUtilFreeHugeInteger(
        IN unsigned char *pOssValue
        )
{
    // Only for BYTE reversal
    OssUtilFree(pOssValue);
}

void
WINAPI
OssUtilGetHugeInteger(
        IN unsigned int OssLength,
        IN unsigned char *pOssValue,
        IN DWORD dwFlags,
        OUT PCRYPT_INTEGER_BLOB pInfo,
        IN OUT BYTE **ppbExtra,
        IN OUT LONG *plRemainExtra
        )
{
    // Since bytes need to be reversed, always need to do a copy (dwFlags = 0)
    OssUtilGetOctetString(OssLength, pOssValue, 0,
        pInfo, ppbExtra, plRemainExtra);
    if (*plRemainExtra >= 0 && pInfo->cbData > 0)
        OssUtilReverseBytes(pInfo->pbData, pInfo->cbData);
}

//+-------------------------------------------------------------------------
//  Set/Free/Get Huge Unsigned Integer
//
//  Set inserts a leading 0x00 before reversing. Note, any extra leading
//  0x00's are removed by OSS before ASN.1 encoding.
//
//  Get removes a leading 0x00 if present, after reversing.
//
//  OssUtilFreeHugeUINT must be called to free the allocated OssValue.
//  OssUtilFreeHugeUINT has been #define'd to OssUtilFreeHugeInteger.
//--------------------------------------------------------------------------
BOOL
WINAPI
OssUtilSetHugeUINT(
        IN PCRYPT_UINT_BLOB pInfo,
        OUT unsigned int *pOssLength,
        OUT unsigned char **ppOssValue
        )
{
    BOOL fResult;
    DWORD cb = pInfo->cbData;
    BYTE *pb;
    DWORD i;

    if (cb > 0) {
        if (NULL == (pb = (BYTE *) OssUtilAlloc(cb + 1)))
            goto ErrorReturn;
        *pb = 0x00;
        for (i = 0; i < cb; i++)
            pb[1 + i] = pInfo->pbData[cb - 1 - i];
        cb++;
    } else
        pb = NULL;
    fResult = TRUE;
CommonReturn:
    *pOssLength = cb;
    *ppOssValue = pb;
    return fResult;
ErrorReturn:
    cb = 0;
    fResult = FALSE;
    goto CommonReturn;
}


void
WINAPI
OssUtilGetHugeUINT(
        IN unsigned int OssLength,
        IN unsigned char *pOssValue,
        IN DWORD dwFlags,
        OUT PCRYPT_UINT_BLOB pInfo,
        IN OUT BYTE **ppbExtra,
        IN OUT LONG *plRemainExtra
        )
{
    // Check for and advance past a leading 0x00.
    if (OssLength > 1 && *pOssValue == 0) {
        pOssValue++;
        OssLength--;
    }
    OssUtilGetHugeInteger(
        OssLength,
        pOssValue,
        dwFlags,
        pInfo,
        ppbExtra,
        plRemainExtra
        );
}

//+-------------------------------------------------------------------------
//  Set/Get BitString
//--------------------------------------------------------------------------
void
WINAPI
OssUtilSetBitString(
        IN PCRYPT_BIT_BLOB pInfo,
        OUT unsigned int *pOssBitLength,
        OUT unsigned char **ppOssValue
        )
{
    if (pInfo->cbData) {
        *ppOssValue = pInfo->pbData;
        assert(pInfo->cUnusedBits <= 7);
        *pOssBitLength = pInfo->cbData * 8 - pInfo->cUnusedBits;
    } else {
        *ppOssValue = NULL;
        *pOssBitLength = 0;
    }
}

static const BYTE rgbUnusedAndMask[8] =
    {0xFF, 0xFE, 0xFC, 0xF8, 0xF0, 0xE0, 0xC0, 0x80};

void
WINAPI
OssUtilGetBitString(
        IN unsigned int OssBitLength,
        IN unsigned char *pOssValue,
        IN DWORD dwFlags,
        OUT PCRYPT_BIT_BLOB pInfo,
        IN OUT BYTE **ppbExtra,
        IN OUT LONG *plRemainExtra
        )
{
    if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG && 0 == (OssBitLength % 8)) {
        if (*plRemainExtra >= 0) {
            pInfo->cbData = OssBitLength / 8;
            pInfo->cUnusedBits = 0;
            pInfo->pbData = pOssValue;
        }
    } else {
        LONG lRemainExtra = *plRemainExtra;
        BYTE *pbExtra = *ppbExtra;
        LONG lAlignExtra;
        LONG lData;
        DWORD cUnusedBits;
    
        lData = (LONG) OssBitLength / 8;
        cUnusedBits = OssBitLength % 8;
        if (cUnusedBits) {
            cUnusedBits = 8 - cUnusedBits;
            lData++;
        }
        lAlignExtra = INFO_LEN_ALIGN(lData);
        lRemainExtra -= lAlignExtra;
        if (lRemainExtra >= 0) {
            if (lData > 0) {
                pInfo->pbData = pbExtra;
                pInfo->cbData = (DWORD) lData;
                pInfo->cUnusedBits = cUnusedBits;
                memcpy(pbExtra, pOssValue, lData);
                if (cUnusedBits)
                    *(pbExtra + lData - 1) &= rgbUnusedAndMask[cUnusedBits];
            } else
                memset(pInfo, 0, sizeof(*pInfo));
            pbExtra += lAlignExtra;
        }
        *plRemainExtra = lRemainExtra;
        *ppbExtra = pbExtra;
    }
}

//+-------------------------------------------------------------------------
//  Set BitString Without Trailing Zeroes
//--------------------------------------------------------------------------
void
WINAPI
OssUtilSetBitStringWithoutTrailingZeroes(
        IN PCRYPT_BIT_BLOB pInfo,
        OUT unsigned int *pOssBitLength,
        OUT unsigned char **ppOssValue
        )
{
    DWORD cbData;
    DWORD cUnusedBits;

    cbData = pInfo->cbData;
    cUnusedBits = pInfo->cUnusedBits;
    assert(cUnusedBits <= 7);

    if (cbData) {
        BYTE *pb;

        // Until we find a nonzero byte (starting with the last byte),
        // decrement cbData. For the last byte don't look at any unused bits.
        pb = pInfo->pbData + cbData - 1;
        if (0 == (*pb & rgbUnusedAndMask[cUnusedBits])) {
            cUnusedBits = 0;
            cbData--;
            pb--;

            for ( ; 0 < cbData && 0 == *pb; cbData--, pb--)
                ;
        }
    }

    if (cbData) {
        BYTE b;

        // Determine the number of unused bits in the last byte. Treat any
        // trailing zeroes as unused.
        b = *(pInfo->pbData + cbData - 1);
        assert(b);
        if (cUnusedBits)
            b = b >> cUnusedBits;
        
        for (; 7 > cUnusedBits && 0 == (b & 0x01); cUnusedBits++) {
            b = b >> 1;
        }
        assert(b & 0x01);
        assert(cUnusedBits <= 7);

        *ppOssValue = pInfo->pbData;
        *pOssBitLength = cbData * 8 - cUnusedBits;
    } else {
        *ppOssValue = NULL;
        *pOssBitLength = 0;
    }
}

//+-------------------------------------------------------------------------
//  Get IA5 String
//--------------------------------------------------------------------------
void
WINAPI
OssUtilGetIA5String(
        IN unsigned int OssLength,
        IN char *pOssValue,
        IN DWORD dwFlags,
        OUT LPSTR *ppsz,
        IN OUT BYTE **ppbExtra,
        IN OUT LONG *plRemainExtra
        )
{
    LONG lRemainExtra = *plRemainExtra;
    BYTE *pbExtra = *ppbExtra;
    LONG lAlignExtra;
    LONG lData;

    lData = (LONG) OssLength;
    lAlignExtra = INFO_LEN_ALIGN(lData + 1);
    lRemainExtra -= lAlignExtra;
    if (lRemainExtra >= 0) {
        if (lData > 0)
            memcpy(pbExtra, pOssValue, lData);
        *(pbExtra + lData) = 0;
        *ppsz = (LPSTR) pbExtra;
        pbExtra += lAlignExtra;
    }
    *plRemainExtra = lRemainExtra;
    *ppbExtra = pbExtra;
}

//+-------------------------------------------------------------------------
//  Set/Free/Get Unicode mapped to IA5 String
//--------------------------------------------------------------------------
BOOL
WINAPI
OssUtilSetUnicodeConvertedToIA5String(
        IN LPWSTR pwsz,
        OUT unsigned int *pOssLength,
        OUT char **ppOssValue
        )
{
    BOOL fResult;
    LPSTR psz = NULL;
    int cchUTF8;
    int cchWideChar;
    int i;

    cchWideChar = wcslen(pwsz);
    if (cchWideChar == 0) {
        *pOssLength = 0;
        *ppOssValue = 0;
        return TRUE;
    }
    // Check that the input string contains valid IA5 characters
    for (i = 0; i < cchWideChar; i++) {
        if (pwsz[i] > 0x7F) {
            SetLastError((DWORD) CRYPT_E_INVALID_IA5_STRING);
            *pOssLength = (unsigned int) i;
            goto InvalidIA5;
        }
    }

    cchUTF8 = WideCharToUTF8(
        pwsz,
        cchWideChar,
        NULL,       // lpUTF8Str
        0           // cchUTF8
        );

    if (cchUTF8 <= 0)
        goto ErrorReturn;
    if (NULL == (psz = (LPSTR) OssUtilAlloc(cchUTF8)))
        goto ErrorReturn;
    cchUTF8 = WideCharToUTF8(
        pwsz,
        cchWideChar,
        psz,
        cchUTF8
        );
    *ppOssValue = psz;
    *pOssLength = cchUTF8;
    fResult = TRUE;
    goto CommonReturn;

ErrorReturn:
    *pOssLength = 0;
InvalidIA5:
    *ppOssValue = NULL;
    fResult = FALSE;
CommonReturn:
    return fResult;
}

void
WINAPI
OssUtilFreeUnicodeConvertedToIA5String(
        IN char *pOssValue
        )
{
    OssUtilFree(pOssValue);
}

void
WINAPI
OssUtilGetIA5StringConvertedToUnicode(
        IN unsigned int OssLength,
        IN char *pOssValue,
        IN DWORD dwFlags,
        OUT LPWSTR *ppwsz,
        IN OUT BYTE **ppbExtra,
        IN OUT LONG *plRemainExtra
        )
{
    LONG lRemainExtra = *plRemainExtra;
    BYTE *pbExtra = *ppbExtra;
    LONG lAlignExtra;
    LONG lData;
    int cchWideChar;

    cchWideChar = UTF8ToWideChar(
        (LPSTR) pOssValue,
        OssLength,
        NULL,                   // lpWideCharStr
        0                       // cchWideChar
        );
    if (cchWideChar > 0)
        lData = cchWideChar * sizeof(WCHAR);
    else
        lData = 0;
    lAlignExtra = INFO_LEN_ALIGN(lData + sizeof(WCHAR));
    lRemainExtra -= lAlignExtra;
    if (lRemainExtra >= 0) {
        if (lData > 0)
            UTF8ToWideChar(pOssValue, OssLength,
                (LPWSTR) pbExtra, cchWideChar);
        memset(pbExtra + lData, 0, sizeof(WCHAR));
        *ppwsz = (LPWSTR) pbExtra;
        pbExtra += lAlignExtra;
    }
    *plRemainExtra = lRemainExtra;
    *ppbExtra = pbExtra;
}

//+-------------------------------------------------------------------------
//  Get BMP String
//--------------------------------------------------------------------------
void
WINAPI
OssUtilGetBMPString(
        IN unsigned int OssLength,
        IN unsigned short *pOssValue,
        IN DWORD dwFlags,
        OUT LPWSTR *ppwsz,
        IN OUT BYTE **ppbExtra,
        IN OUT LONG *plRemainExtra
        )
{
    LONG lRemainExtra = *plRemainExtra;
    BYTE *pbExtra = *ppbExtra;
    LONG lAlignExtra;
    LONG lData;

    lData = (LONG) OssLength * sizeof(WCHAR);
    lAlignExtra = INFO_LEN_ALIGN(lData + sizeof(WCHAR));
    lRemainExtra -= lAlignExtra;
    if (lRemainExtra >= 0) {
        if (lData > 0)
            memcpy(pbExtra, pOssValue, lData);
        memset(pbExtra + lData, 0, sizeof(WCHAR));
        *ppwsz = (LPWSTR) pbExtra;
        pbExtra += lAlignExtra;
    }
    *plRemainExtra = lRemainExtra;
    *ppbExtra = pbExtra;
}


//+-------------------------------------------------------------------------
//  Set/Get "Any" DER BLOB
//--------------------------------------------------------------------------
void
WINAPI
OssUtilSetAny(
        IN PCRYPT_OBJID_BLOB pInfo,
        OUT OpenType *pOss
        )
{
    memset(pOss, 0, sizeof(*pOss));
    pOss->encoded = pInfo->pbData;
    pOss->length = pInfo->cbData;
}

void
WINAPI
OssUtilGetAny(
        IN OpenType *pOss,
        IN DWORD dwFlags,
        OUT PCRYPT_OBJID_BLOB pInfo,
        IN OUT BYTE **ppbExtra,
        IN OUT LONG *plRemainExtra
        )
{
    if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG) {
        if (*plRemainExtra >= 0) {
            pInfo->cbData = pOss->length;
            pInfo->pbData = (BYTE *) pOss->encoded;
        }
    } else {
        LONG lRemainExtra = *plRemainExtra;
        BYTE *pbExtra = *ppbExtra;
        LONG lAlignExtra;
        LONG lData;
    
        lData = (LONG) pOss->length;
        lAlignExtra = INFO_LEN_ALIGN(lData);
        lRemainExtra -= lAlignExtra;
        if (lRemainExtra >= 0) {
            if (lData > 0) {
                pInfo->pbData = pbExtra;
                pInfo->cbData = (DWORD) lData;
                memcpy(pbExtra, pOss->encoded, lData);
            } else
                memset(pInfo, 0, sizeof(*pInfo));
            pbExtra += lAlignExtra;
        }
        *plRemainExtra = lRemainExtra;
        *ppbExtra = pbExtra;
    }
}


//+-------------------------------------------------------------------------
//  Encode an OSS formatted info structure.
//
//  If CRYPT_ENCODE_ALLOC_FLAG is set, allocate memory for pbEncoded and
//  return *((BYTE **) pvEncoded) = pbAllocEncoded. Otherwise,
//  pvEncoded points to byte array to be updated.
//--------------------------------------------------------------------------
BOOL
WINAPI
OssUtilEncodeInfoEx(
        IN OssGlobal *Pog,
        IN int pdunum,
        IN void *pvOssInfo,
        IN DWORD dwFlags,
        IN OPTIONAL PCRYPT_ENCODE_PARA pEncodePara,
        OUT OPTIONAL void *pvEncoded,
        IN OUT DWORD *pcbEncoded
        )
{
    BOOL fResult;
    DWORD cbEncoded;
    OssBuf OssEncoded;
    int OssStatus;
    unsigned char *value;

    if (NULL == pvEncoded || (dwFlags & CRYPT_ENCODE_ALLOC_FLAG))
        cbEncoded = 0;
    else
        cbEncoded = *pcbEncoded;

    OssEncoded.length = cbEncoded;
    if (cbEncoded == 0)
        value = NULL;
    else
        value = (unsigned char *) pvEncoded;
    OssEncoded.value = value;

    ossSetEncodingRules(Pog, OSS_DER);
    OssStatus = ossEncode(
        Pog,
        pdunum,
        pvOssInfo,
        &OssEncoded);
    cbEncoded = OssEncoded.length;

    if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) {
        PFN_CRYPT_ALLOC pfnAlloc;
        BYTE *pbEncoded;

        if (0 != OssStatus || 0 == cbEncoded) {
            ossFreeBuf(Pog, OssEncoded.value);
            *((void **) pvEncoded) = NULL;
            goto OssError;
        }

        pfnAlloc = PkiGetEncodeAllocFunction(pEncodePara);
        if (NULL == (pbEncoded = (BYTE *) pfnAlloc(cbEncoded))) {
            ossFreeBuf(Pog, OssEncoded.value);
            *((void **) pvEncoded) = NULL;
            goto OutOfMemory;
        }
        memcpy(pbEncoded, OssEncoded.value, cbEncoded);
        *((BYTE **) pvEncoded) = pbEncoded;
        ossFreeBuf(Pog, OssEncoded.value);
        goto SuccessReturn;
    } else if (value == NULL && OssEncoded.value) {
        // Length only calculation with a throw away allocation
        ossFreeBuf(Pog, OssEncoded.value);
        if (pvEncoded && 0 == OssStatus) {
            // Upon entry *pcbEncoded == 0
            goto LengthError;
        }
    }

    if (0 != OssStatus) {
        // For MORE_BUF:: redo as a length only calculation
        if (OssStatus == MORE_BUF && pvEncoded &&
                OssUtilEncodeInfoEx(
                Pog,
                pdunum,
                pvOssInfo,
                0,                  // dwFlags
                NULL,               // pEncodePara
                NULL,               // pbEncoded
                &cbEncoded))
            goto LengthError;
        else {
            cbEncoded = 0;
            goto OssError;
        }
    }

SuccessReturn:
    fResult = TRUE;
CommonReturn:
    *pcbEncoded = cbEncoded;
    return fResult;

ErrorReturn:
    fResult = FALSE;
    goto CommonReturn;
TRACE_ERROR(OutOfMemory)
SET_ERROR(LengthError, ERROR_MORE_DATA)
SET_ERROR_VAR(OssError, CRYPT_E_OSS_ERROR + OssStatus)
}

//+-------------------------------------------------------------------------
//  Encode an OSS formatted info structure
//--------------------------------------------------------------------------
BOOL
WINAPI
OssUtilEncodeInfo(
        IN OssGlobal *Pog,
        IN int pdunum,
        IN void *pvOssInfo,
        OUT OPTIONAL BYTE *pbEncoded,
        IN OUT DWORD *pcbEncoded
        )
{
    return OssUtilEncodeInfoEx(
        Pog,
        pdunum,
        pvOssInfo,
        0,                  // dwFlags
        NULL,               // pEncodePara
        pbEncoded,
        pcbEncoded
        );
}


//+-------------------------------------------------------------------------
//  Decode into an allocated, OSS formatted info structure
//--------------------------------------------------------------------------
BOOL
WINAPI
OssUtilDecodeAndAllocInfo(
        IN OssGlobal *Pog,
        IN int pdunum,
        IN const BYTE *pbEncoded,
        IN DWORD cbEncoded,
        OUT void **ppvOssInfo
        )
{
    BOOL fResult;
    OssBuf OssEncoded;
    int OssStatus;

    OssEncoded.length = cbEncoded;
    OssEncoded.value = (unsigned char *) pbEncoded;

    ossSetEncodingRules(Pog, OSS_BER);
    *ppvOssInfo = NULL;
    if (0 != (OssStatus = ossDecode(
                Pog,
                &pdunum,
                &OssEncoded,
                ppvOssInfo)))
        goto OssError;
    fResult = TRUE;
CommonReturn:
    return fResult;

ErrorReturn:
    *ppvOssInfo = NULL;
    fResult = FALSE;
    goto CommonReturn;
SET_ERROR_VAR(OssError, CRYPT_E_OSS_ERROR + OssStatus)
}

//+-------------------------------------------------------------------------
//  Free an allocated, OSS formatted info structure
//--------------------------------------------------------------------------
void
WINAPI
OssUtilFreeInfo(
        IN OssGlobal *Pog,
        IN int pdunum,
        IN void *pvOssInfo
        )
{
    if (pvOssInfo) {
        ossFreePDU(Pog, pdunum, pvOssInfo);
    }
}

//+-------------------------------------------------------------------------
//  Call the callback to convert the OSS structure into the 'C' structure.
//  If CRYPT_DECODE_ALLOC_FLAG is set allocate memory for the 'C'
//  structure and call the callback initially to get the length and then
//  a second time to update the allocated 'C' structure.
//
//  Allocated structure is returned:
//      *((void **) pvStructInfo) = pvAllocStructInfo
//--------------------------------------------------------------------------
BOOL
WINAPI
OssUtilAllocStructInfoEx(
        IN void *pvOssInfo,
        IN DWORD dwFlags,
        IN OPTIONAL PCRYPT_DECODE_PARA pDecodePara,
        IN PFN_OSS_UTIL_DECODE_EX_CALLBACK pfnDecodeExCallback,
        OUT OPTIONAL void *pvStructInfo,
        IN OUT DWORD *pcbStructInfo
        )
{
    BOOL fResult;
    LONG lRemainExtra;
    DWORD cbStructInfo;

    if (NULL == pvStructInfo || (dwFlags & CRYPT_DECODE_ALLOC_FLAG)) {
        cbStructInfo = 0;
        lRemainExtra = 0;
    } else {
        cbStructInfo = *pcbStructInfo;
        lRemainExtra = (LONG) cbStructInfo;
    }

    if (!pfnDecodeExCallback(
            pvOssInfo,
            dwFlags & ~CRYPT_DECODE_ALLOC_FLAG,
            pDecodePara,
            pvStructInfo,
            &lRemainExtra
            )) goto DecodeCallbackError;

    if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) {
        void *pv;
        PFN_CRYPT_ALLOC pfnAlloc = PkiGetDecodeAllocFunction(pDecodePara);

        assert(0 > lRemainExtra);
        lRemainExtra = -lRemainExtra;
        cbStructInfo = (DWORD) lRemainExtra;

        if (NULL == (pv = pfnAlloc(cbStructInfo)))
            goto OutOfMemory;
        if (!pfnDecodeExCallback(
                pvOssInfo,
                dwFlags & ~CRYPT_DECODE_ALLOC_FLAG,
                pDecodePara,
                pv,
                &lRemainExtra
                )) {
            PFN_CRYPT_FREE pfnFree = PkiGetDecodeFreeFunction(pDecodePara);
            pfnFree(pv);
            goto DecodeCallbackError;
        }
        *((void **) pvStructInfo) = pv;
        assert(0 <= lRemainExtra);
    }

    if (0 <= lRemainExtra) {
        cbStructInfo = cbStructInfo - (DWORD) lRemainExtra;
    } else {
        cbStructInfo = cbStructInfo + (DWORD) -lRemainExtra;
        if (pvStructInfo) {
            SetLastError((DWORD) ERROR_MORE_DATA);
            fResult = FALSE;
            goto CommonReturn;
        }
    }

    fResult = TRUE;
CommonReturn:
    *pcbStructInfo = cbStructInfo;
    return fResult;

ErrorReturn:
    if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
        *((void **) pvStructInfo) = NULL;
    cbStructInfo = 0;
    fResult = FALSE;
    goto CommonReturn;
TRACE_ERROR(DecodeCallbackError)
TRACE_ERROR(OutOfMemory)
}

//+-------------------------------------------------------------------------
//  Decode the OSS formatted info structure and call the callback
//  function to convert the OSS structure to the 'C' structure.
//
//  If CRYPT_DECODE_ALLOC_FLAG is set allocate memory for the 'C'
//  structure and call the callback initially to get the length and then
//  a second time to update the allocated 'C' structure.
//
//  Allocated structure is returned:
//      *((void **) pvStructInfo) = pvAllocStructInfo
//--------------------------------------------------------------------------
BOOL
WINAPI
OssUtilDecodeAndAllocInfoEx(
        IN OssGlobal *Pog,
        IN int pdunum,
        IN const BYTE *pbEncoded,
        IN DWORD cbEncoded,
        IN DWORD dwFlags,
        IN OPTIONAL PCRYPT_DECODE_PARA pDecodePara,
        IN PFN_OSS_UTIL_DECODE_EX_CALLBACK pfnDecodeExCallback,
        OUT OPTIONAL void *pvStructInfo,
        IN OUT DWORD *pcbStructInfo
        )
{
    BOOL fResult;
    void *pvOssInfo = NULL;

    if (!OssUtilDecodeAndAllocInfo(
            Pog,
            pdunum,
            pbEncoded,
            cbEncoded,
            &pvOssInfo
            )) goto OssDecodeError;

    fResult = OssUtilAllocStructInfoEx(
        pvOssInfo,
        dwFlags,
        pDecodePara,
        pfnDecodeExCallback,
        pvStructInfo,
        pcbStructInfo
        );
CommonReturn:
    OssUtilFreeInfo(Pog, pdunum, pvOssInfo);
    return fResult;

ErrorReturn:
    if (dwFlags & CRYPT_DECODE_ALLOC_FLAG)
        *((void **) pvStructInfo) = NULL;
    *pcbStructInfo = 0;
    fResult = FALSE;
    goto CommonReturn;
TRACE_ERROR(OssDecodeError)
}