Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2191 lines
51 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1995.
//
// File: encode.c
//
// Contents:
//
// Classes:
//
// Functions:
//
// History: 8-10-95 RichardW Grabbed from ACT
//
//----------------------------------------------------------------------------
#include "sslsspi.h"
#include "encode.h"
#include "ber.h"
#include "rc4.h"
#include "md5.h"
/************************************************************/
/* EncodeLength ASN1 encodes a length field. The parameter */
/* dwLen is the length to be encoded, it is a DWORD and */
/* therefore may be no larger than 2^32. The pbEncoded */
/* parameter is the encoded result, and memory must be */
/* allocated for it by the caller. The Writeflag parameter */
/* indicates if the result is to be written to the pbEncoded*/
/* parameter. The function returns a -1 if it fails and */
/* otherwise returns the number of total bytes in the */
/* encoded length. */
/************************************************************/
typedef struct __Algorithms {
ALG_ID Id;
UCHAR Sequence[16];
DWORD SequenceLen;
} _Algorithms;
#define iso_member 0x2a, // iso(1) memberbody(2)
#define us 0x86, 0x48, // us(840)
#define rsadsi 0x86, 0xf7, 0x0d, // rsadsi(113549)
#define pkcs 0x01, // pkcs(1)
#define pkcs_1 iso_member us rsadsi pkcs
#define pkcs_len 7
#define rsa_dsi iso_member us rsadsi
#define rsa_dsi_len 6
#define joint_iso_ccitt_ds 0x55,
#define attributetype 0x04,
#define attributeType joint_iso_ccitt_ds attributetype
#define attrtype_len 2
_Algorithms KnownAlgorithms[] = { {BASIC_RSA, {pkcs_1 1, 1}, pkcs_len + 2},
{MD2_WITH_RSA, {pkcs_1 1, 2}, pkcs_len + 2},
{MD5_WITH_RSA, {pkcs_1 1, 4}, pkcs_len + 2},
{RC4_STREAM, {rsa_dsi 3, 4}, rsa_dsi_len + 2}
};
typedef struct _NameTypes {
PSTR Prefix;
DWORD PrefixLen;
UCHAR Sequence[8];
DWORD SequenceLen;
} NameTypes;
#define CNTYPE_INDEX 0
NameTypes KnownNameTypes[] = { {"CN=", 3, {attributeType 3}, attrtype_len + 1},
{"C=", 2, {attributeType 6}, attrtype_len + 1},
{"L=", 2, {attributeType 7}, attrtype_len + 1},
{"S=", 2, {attributeType 8}, attrtype_len + 1},
{"O=", 2, {attributeType 10}, attrtype_len + 1},
{"OU=", 3, {attributeType 11}, attrtype_len + 1}
};
long
EncodeLength(
BYTE * pbEncoded,
DWORD dwLen,
BOOL Writeflag)
{
// length is between 2^7 - 1 and 2^16
if (dwLen > 0xFF)
{
if (Writeflag)
{
pbEncoded[0] = 0x82;
pbEncoded[1] = (BYTE) ((dwLen & 0xFF00) >> 8);
pbEncoded[2] = (BYTE) (dwLen & 0xFF);
}
return (3);
}
// length is between 2^15 - 1 and 2^16 - 1
else if (dwLen > 0x7F)
{
if (Writeflag)
{
pbEncoded[0] = 0x81;
pbEncoded[1] = (BYTE) (dwLen & 0xFF);
}
return (2);
}
// length is between 0 and 2^7
else
{
if (Writeflag)
pbEncoded[0] = (BYTE) (dwLen & 0x7F);
return (1);
}
}
long
EncodeNull(
PUCHAR pbEncoded,
BOOL WriteFlag )
{
if (WriteFlag)
{
*pbEncoded++ = NULL_TAG;
*pbEncoded = 0;
}
return( 2 );
}
/************************************************************/
/* EncodeAlgid ASN1 encodes an algorithm identifier. The */
/* parameter Algid is the algorithm identifier as an ALG_ID */
/* type. pbEncoded is the parameter used to pass back the */
/* encoded result, and memory must be allocated for it by */
/* the caller. The Writeflag parameter indicates if the */
/* result is to be written to the pbEncoded parameter */
/* The function returns a -1 if it fails and otherwise */
/* returns the number of total bytes in the encoded */
/* algorithm identifier. */
/************************************************************/
long
EncodeAlgid(
BYTE * pbEncoded,
ALG_ID Algid,
BOOL Writeflag)
{
DWORD i;
PUCHAR pbLen;
// determine the algorithm id which is to be encoded and
// copy the appropriated encoded alg id into the destination
if (Writeflag)
{
*pbEncoded++ = OBJECT_ID_TAG;
pbLen = pbEncoded++;
}
for (i = 0; i < sizeof(KnownAlgorithms) / sizeof(_Algorithms) ; i++ )
{
if (Algid == KnownAlgorithms[i].Id)
{
if (Writeflag)
{
CopyMemory( pbEncoded,
KnownAlgorithms[Algid].Sequence,
KnownAlgorithms[Algid].SequenceLen);
*pbLen = (UCHAR) KnownAlgorithms[Algid].SequenceLen;
}
return(KnownAlgorithms[Algid].SequenceLen + 2);
}
}
return(-1);
}
long
EncodeAlgorithm(
BYTE * pbEncoded,
ALG_ID AlgId,
BOOL WriteFlag)
{
UCHAR Temp[32];
long Result;
PUCHAR pBuffer;
long Sum;
long HeaderLength;
pBuffer = Temp;
Result = EncodeHeader( pBuffer, 32, TRUE );
HeaderLength = Result;
pBuffer += Result;
Result = EncodeAlgid( pBuffer, AlgId, TRUE );
pBuffer += Result;
Result = EncodeNull( pBuffer, TRUE );
Sum = Result + (pBuffer - Temp - HeaderLength);
Result = EncodeHeader( Temp, Sum, TRUE );
if (WriteFlag)
{
CopyMemory( pbEncoded, Temp, Sum + Result );
}
return(Result + Sum);
}
/****************************************************************/
/* EncodeInteger ASN1 encodes an integer. The pbInt parameter */
/* is the integer as an array of bytes, and dwLen is the number */
/* of bytes in the array. The least significant byte of the */
/* integer is the zeroth byte of the array. The encoded result */
/* is passed back in the pbEncoded parameter. The Writeflag */
/* indicates if the result is to be written to the pbEncoded */
/* parameter. The function returns a -1 if it fails and */
/* otherwise returns the number of total bytes in the encoded */
/* integer. */
/* This implementation will only deal with positive integers. */
/****************************************************************/
long
EncodeInteger(
BYTE * pbEncoded,
BYTE * pbInt,
DWORD dwLen,
BOOL Writeflag)
{
long count;
DWORD i;
long j;
if (Writeflag)
pbEncoded[0] = INTEGER_TAG;
count = 1;
i = dwLen - 1;
// find the most significant non-zero byte
while ((pbInt[i] == 0) && (i > 0))
i--;
if ((i == 0) && (pbInt[i] == 0))
// this means that the integer value is 0
{
if (Writeflag)
{
pbEncoded[1] = 0x01;
pbEncoded[2] = 0x00;
}
count += 2;
}
else
{
// if the most significant bit of the most sig byte is set
// then need to add a 0 byte to the beginning.
if (pbInt[i] > 0x7F)
{
// encode the length
count += EncodeLength (pbEncoded + count, i+2, Writeflag);
if (Writeflag)
{
// set the first byte of the integer to zero and increment count
pbEncoded[count++] = 0x00;
// copy the integer bytes into the encoded buffer
j = i;
while (j >= 0)
pbEncoded[count++] = pbInt[j--];
}
}
else
{
// encode the length
count += EncodeLength (pbEncoded + count, i+1, Writeflag);
// copy the integer bytes into the encoded buffer
if (Writeflag)
{
j = i;
while (j >= 0)
pbEncoded[count++] = pbInt[j--];
}
}
}
return (count);
}
/****************************************************************/
/* EncodeString ASN1 encodes a character string. The pbStr */
/* parameter is the string as an array of characters, and dwLen */
/* is the number of characters in the array. The encoded result*/
/* is passed back in the pbEncoded parameter. The Writeflag */
/* indicates if the result is to be written to the pbEncoded */
/* parameter. The function returns a -1 if it fails and */
/* otherwise returns the number of total bytes in the encoded */
/* string. */
/****************************************************************/
long
EncodeString(
BYTE * pbEncoded,
BYTE * pbStr,
DWORD dwLen,
BOOL Writeflag)
{
long index;
if (Writeflag)
pbEncoded[0] = CHAR_STRING_TAG;
index = 1;
index += EncodeLength (pbEncoded + 1, dwLen, Writeflag);
if (Writeflag)
{
CopyMemory( pbEncoded + index,
pbStr,
dwLen) ;
}
index += dwLen;
return (index);
}
/****************************************************************/
/* EncodeOctetString ASN1 encodes a string of hex valued */
/* characters. The pbStr parameter is an array of characters, */
/* and dwLen is the number of characters in the array. The */
/* encoded result is passed back in the pbEncoded parameter. The*/
/* Writeflag parameter indicates if the result is to be written */
/* to the pbEncoded parameter. The function returns a -1 if it */
/* fails and otherwise returns the number of total bytes in the */
/* encoded octet string. */
/****************************************************************/
long
EncodeOctetString(
BYTE * pbEncoded,
BYTE * pbStr,
DWORD dwLen,
BOOL Writeflag)
{
long index;
if (Writeflag)
pbEncoded[0] = OCTET_STRING_TAG;
index = 1;
index += EncodeLength (pbEncoded + 1, dwLen, Writeflag);
if (Writeflag)
{
CopyMemory( pbEncoded + index,
pbStr,
dwLen );
}
index += (long) dwLen;
return (index);
}
/****************************************************************/
/* EncodeBitString ASN1 encodes a string of bit characters. The */
/* pbStr parameter is an array of characters (bits), and dwLen */
/* is the number of characters in the array. The encoded result*/
/* is passed back in the pbEncoded parameter. The Writeflag */
/* indicates if the result is to be written to the pbEncoded */
/* parameter. The function returns a -1 if it fails and */
/* otherwise returns the number of total bytes in the encoded */
/* string. This function uses the DER. */
/****************************************************************/
long
EncodeBitString(
BYTE * pbEncoded,
BYTE * pbStr,
DWORD dwLen,
BOOL Writeflag)
{
long index;
if (Writeflag)
pbEncoded[0] = BIT_STRING_TAG;
index = 1;
index += EncodeLength (pbEncoded + 1, dwLen+1, Writeflag);
if (Writeflag)
{
// the next byte tells how many unused bits there are in the last byte,
// but this will always be zero in this implementation (DER)
pbEncoded[index] = 0;
}
index++;
if (Writeflag)
{
CopyMemory(pbEncoded + index, pbStr, dwLen);
}
index += (long) dwLen;
return (index);
}
//+---------------------------------------------------------------------------
//
// Function: EncodeFileTime
//
// Synopsis: Encodes a FILETIME to a ASN.1 format time string.
//
// Arguments: [pbEncoded] --
// [Time] --
// [UTC] -- Indicate Time is UTC (true) or local (false)
// [WriteFlag] --
//
// History: 8-10-95 RichardW Created
//
// Notes:
//
//----------------------------------------------------------------------------
long
EncodeFileTime(
BYTE * pbEncoded,
FILETIME Time,
BOOL UTC,
BOOL WriteFlag)
{
SYSTEMTIME st;
FILETIME utc;
int count;
if (!WriteFlag)
{
return(15); // Why 15?
}
if (UTC)
{
utc = Time;
}
else
{
LocalFileTimeToFileTime(&Time, &utc);
}
FileTimeToSystemTime(&utc, &st);
*pbEncoded ++ = UTCTIME_TAG;
count = EncodeLength( pbEncoded, 13, TRUE);
if (count < 0)
{
return(-1);
}
pbEncoded += count;
st.wYear %= 100;
*pbEncoded++ = (BYTE) ((st.wYear / 10) + '0');
*pbEncoded++ = (BYTE) ((st.wYear % 10) + '0');
*pbEncoded++ = (BYTE) ((st.wMonth / 10) + '0');
*pbEncoded++ = (BYTE) ((st.wMonth % 10) + '0');
*pbEncoded++ = (BYTE) ((st.wDay / 10) + '0');
*pbEncoded++ = (BYTE) ((st.wDay % 10) + '0');
*pbEncoded++ = (BYTE) ((st.wHour / 10) + '0');
*pbEncoded++ = (BYTE) ((st.wHour % 10) + '0');
*pbEncoded++ = (BYTE) ((st.wMinute / 10) + '0');
*pbEncoded++ = (BYTE) ((st.wMinute % 10) + '0');
*pbEncoded++ = (BYTE) ((st.wSecond / 10) + '0');
*pbEncoded++ = (BYTE) ((st.wSecond % 10) + '0');
*pbEncoded = 'Z';
return(15);
}
#if 0
/****************************************************************/
/* EncodeUTCTime ASN1 encodes a time_t value into a Universal */
/* time type. The Time parameter is the time passed into the */
/* function as a time_t type. The encoded result is passed back*/
/* in the pbEncoded parameter. The Writeflag indicates if the */
/* result is to be written to the pbEncoded parameter. The */
/* function returns a -1 if it fails and otherwise returns the */
/* number of total bytes in the encoded universal time. */
/****************************************************************/
long EncodeUTCTime (BYTE *pbEncoded, time_t Time, BOOL Writeflag)
{
struct tm *ptmTime;
long count;
ptmTime = gmtime (&Time);
if (Writeflag)
{
// place the encoded time content into temporary memory
// place the tag into the field
pbEncoded[0] = UTCTIME_TAG;
// encode the length
if ((count = EncodeLength (pbEncoded + 1, 13, TRUE)) < 0)
return -1;
// get the year information
pbEncoded[count + 1] = (BYTE)(ptmTime->tm_year / 0xA + 0x30);
pbEncoded[count + 2] = (BYTE)(ptmTime->tm_year % 0xA + 0x30);
// get the month information
pbEncoded[count + 3] = (BYTE)(ptmTime->tm_mon / 0xA + 0x30);
pbEncoded[count + 4] = (BYTE)(ptmTime->tm_mon % 0xA + 0x30);
// get the day information
pbEncoded[count + 5] = (BYTE)(ptmTime->tm_mday / 0xA + 0x30);
pbEncoded[count + 6] = (BYTE)(ptmTime->tm_mday % 0xA + 0x30);
// get the hour information
pbEncoded[count + 7] = (BYTE)(ptmTime->tm_hour / 0xA + 0x30);
pbEncoded[count + 8] = (BYTE)(ptmTime->tm_hour % 0xA + 0x30);
// get the minute information
pbEncoded[count + 9] = (BYTE)(ptmTime->tm_min / 0xA + 0x30);
pbEncoded[count + 10] = (BYTE)(ptmTime->tm_min % 0xA + 0x30);
// get the second information
pbEncoded[count + 11] = (BYTE)(ptmTime->tm_sec / 0xA + 0x30);
pbEncoded[count + 12] = (BYTE)(ptmTime->tm_sec % 0xA + 0x30);
pbEncoded[count + 13] = 'Z';
}
return 15;
}
#endif
/****************************************************************/
/* EncodeHeader ASN1 encodes a header for a sequence type. The */
/* dwLen is the length of the encoded information in the */
/* sequence. The Writeflag indicates if the result is to be */
/* written to the pbEncoded parameter. The function returns a */
/* -1 if it fails and otherwise returns the number of total */
/* bytes in the encoded header. */
/****************************************************************/
long
EncodeHeader(
BYTE * pbEncoded,
DWORD dwLen,
BOOL Writeflag)
{
long index;
if (Writeflag)
{
pbEncoded[0] = SEQUENCE_TAG;
}
index = 1;
index += EncodeLength (pbEncoded + 1, dwLen, Writeflag);
return (index);
}
/****************************************************************/
/* EncodeSetOfHeader ASN1 encodes a header for a set of type. */
/* The dwLen is the length of the encoded information in the */
/* set of. The Writeflag indicates if the result is to be */
/* written to the pbEncoded parameter. The function returns a */
/* -1 if it fails and otherwise returns the number of total */
/* bytes in the encoded header. */
/****************************************************************/
long
EncodeSetOfHeader(
BYTE * pbEncoded,
DWORD dwLen,
BOOL Writeflag)
{
long index;
if (Writeflag)
{
pbEncoded[0] = SET_OF_TAG;
}
index = 1;
index += EncodeLength (pbEncoded + 1, dwLen, Writeflag);
return (index);
}
long
EncodeSetHeader(
BYTE * pbEncoded,
DWORD dwLen,
BOOL WriteFlag )
{
long Length;
if (WriteFlag)
{
*pbEncoded++ = BER_SET;
}
Length = EncodeLength( pbEncoded, dwLen, WriteFlag);
return( Length + 1 );
}
/****************************************************************/
/* EncodeName ASN1 encodes a Name type. The pbName parameter is */
/* the name and dwLen is the length of the name in bytes. */
/* The Writeflag indicates if the result is to be written to */
/* the pbEncoded parameter. The function returns a -1 if it */
/* fails and otherwise returns the number of total bytes in the */
/* encoded name. */
/****************************************************************/
long
EncodeName(
BYTE * pbEncoded,
BYTE * pbName,
DWORD dwLen,
BOOL Writeflag)
{
BYTE Type[MAXOBJIDLEN];
long TypeLen;
BYTE Value[MAXNAMEVALUELEN+MINHEADERLEN];
long ValueLen;
BYTE Attribute[MAXNAMELEN];
long AttributeLen;
BYTE SetHdr[MINHEADERLEN];
long HdrLen;
long NameLen;
// encode the name value
if ((ValueLen = EncodeString (Value, pbName, dwLen, Writeflag)) == -1)
return (-1);
// encode the attribute type, this is an object identifier and here it
// is a fake encoding
Type[0] = 0x06;
Type[1] = 0x01;
Type[2] = 0x00;
TypeLen = 3;
// enocde the header for the attribute
if ((AttributeLen = EncodeHeader (Attribute, (WORD) (ValueLen + TypeLen), Writeflag)) == -1)
return (-1);
// copy the attribute type and value into the attribute
memcpy (Attribute + AttributeLen, Type, (size_t) TypeLen);
AttributeLen += TypeLen;
memcpy (Attribute + AttributeLen, Value, (size_t) ValueLen);
AttributeLen += ValueLen;
// encode set of header
if ((HdrLen = EncodeSetOfHeader (SetHdr, (WORD) AttributeLen, Writeflag)) == -1)
return (-1);
// encode Name header
if ((NameLen = EncodeHeader (pbEncoded, (WORD) (HdrLen + AttributeLen), Writeflag)) == -1)
return (-1);
memcpy (pbEncoded + NameLen, SetHdr, (size_t) HdrLen);
NameLen += HdrLen;
memcpy (pbEncoded + NameLen, Attribute, (size_t) AttributeLen);
return (NameLen + AttributeLen);
}
long
EncodeRDN(
BYTE * pbEncoded,
PSTR pszRDN,
BOOL WriteFlag)
{
DWORD i;
LONG Result;
DWORD Type;
DWORD RelLength;
long Length;
Type = (DWORD) -1;
for (i = 0 ; i < sizeof(KnownNameTypes) / sizeof(NameTypes) ; i++ )
{
if (_strnicmp( pszRDN,
KnownNameTypes[i].Prefix,
KnownNameTypes[i].PrefixLen) == 0)
{
Type = i;
break;
}
}
if (Type == (DWORD) -1)
{
return( -1 );
}
RelLength = strlen(pszRDN + KnownNameTypes[Type].PrefixLen);
// Prefix data takes up 9 bytes
Result = EncodeSetHeader( pbEncoded, RelLength + 9, WriteFlag );
Length = Result;
pbEncoded += Result;
Result = EncodeHeader( pbEncoded, RelLength + 7, WriteFlag );
Length += Result;
pbEncoded += Result;
if (WriteFlag)
{
*pbEncoded ++ = OBJECT_ID_TAG;
*pbEncoded ++ = (UCHAR) KnownNameTypes[Type].SequenceLen;
if (KnownNameTypes[Type].SequenceLen == 3)
{
*pbEncoded++ = KnownNameTypes[Type].Sequence[0];
*pbEncoded++ = KnownNameTypes[Type].Sequence[1];
*pbEncoded++ = KnownNameTypes[Type].Sequence[2];
Length += 5;
}
else
{
CopyMemory( pbEncoded,
KnownNameTypes[Type].Sequence,
KnownNameTypes[Type].SequenceLen);
pbEncoded += KnownNameTypes[Type].SequenceLen;
Length += 2 + KnownNameTypes[Type].SequenceLen;
}
}
else
{
pbEncoded += 2 + KnownNameTypes[Type].SequenceLen;
Length += 2 + KnownNameTypes[Type].SequenceLen;
}
if (WriteFlag)
{
if (Type != CNTYPE_INDEX)
{
*pbEncoded ++ = PRINTABLE_STRING_TAG;
}
else
{
*pbEncoded ++ = TELETEX_STRING_TAG;
}
}
else
{
pbEncoded ++;
}
Length ++;
Result = EncodeLength( pbEncoded, RelLength, WriteFlag);
pbEncoded += Result;
Length += Result;
if (WriteFlag)
{
CopyMemory( pbEncoded,
pszRDN + KnownNameTypes[Type].PrefixLen,
RelLength );
}
Length += RelLength;
return( Length );
}
long
EncodeDN(
BYTE * pbEncoded,
PSTR pszDN,
BOOL WriteFlag)
{
PSTR pszNext;
PSTR pszRDN;
long Result;
long Length;
PBYTE pBuffer;
long SaveResult;
pszRDN = pszDN;
pBuffer = pbEncoded;
Result = EncodeHeader( pbEncoded, strlen(pszDN) * 2, WriteFlag );
EncodeDN_RetryLength:
SaveResult = Result;
Length = 0;
pbEncoded += Result;
while (pszRDN && (*pszRDN))
{
pszNext = strchr(pszRDN, ',');
if (pszNext)
{
*pszNext = 0;
}
Result = EncodeRDN( pbEncoded, pszRDN, WriteFlag );
pbEncoded += Result;
Length += Result;
if (pszNext)
{
*pszNext = ',';
pszRDN = pszNext + 1;
while (*pszRDN == ' ')
{
pszRDN++;
}
}
else
{
break;
}
DebugLog((DEB_TRACE, "EncodeDN: Length = %d\n", Length));
}
Result = EncodeHeader( pBuffer, Length, WriteFlag );
if (Result != SaveResult)
{
pszRDN = pszDN;
pbEncoded = pBuffer;
goto EncodeDN_RetryLength;
}
return( Result + Length );
}
/************************************************************/
/* DecodeLength decodes an ASN1 encoded length field. The */
/* pbEncoded parameter is the encoded length. pdwLen is */
/* used to return the length therefore the length may be no */
/* larger than 2^32. The function returns a -1 if it fails */
/* and otherwise returns the number of total bytes in the */
/* encoded length. */
/************************************************************/
long
DecodeLength(
DWORD * pdwLen,
BYTE * pbEncoded)
{
long index = 0;
BYTE count;
// determine what the length of the length field is
if ((count = pbEncoded[0]) > 0x80)
{
// if there is more than one byte in the length field then
// the lower seven bits tells us the number of bytes
count = count ^ 0x80;
// this function only allows the length field to be 3 bytes
// if the field is longer then the function fails
if (count > 2)
{
*pdwLen = 0;
return (-1);
}
*pdwLen = 0;
// go through the bytes of the length field
for (index = 1; index <= count; index++)
{
*pdwLen = (*pdwLen << 8) + (DWORD) (pbEncoded[index]);
}
}
// the length field is just one byte long
else
{
*pdwLen = (DWORD) (pbEncoded[0]);
index = 1;
}
// return how many bytes there were in the length field
return index;
}
/************************************************************/
/* DecodeAlgid decodes an ASN1 encoded algorithm identifier.*/
/* pbEncoded parameter is the encoded identifier. pAlgid is */
/* the parameter used to return the ALG_ID type algorithm */
/* identifier. The Writeflag parameter tells the function */
/* whether to write to pAlgid or not, if TRUE write wlse */
/* don't. The function returns a -1 if it fails and */
/* otherwise returns the number of total bytes in the */
/* encoded algorithm identifier. */
/************************************************************/
long
DecodeAlgid(
ALG_ID * pAlgid,
BYTE * pbEncoded,
BOOL Writeflag)
{
DWORD i;
DWORD len;
if (*pbEncoded != OBJECT_ID_TAG)
{
return(-1);
}
pbEncoded++;
len = *pbEncoded++;
for (i = 0; i < sizeof(KnownAlgorithms) / sizeof(_Algorithms) ; i++ )
{
if (KnownAlgorithms[i].SequenceLen == len)
{
if (memcmp(pbEncoded, KnownAlgorithms[i].Sequence, len) == 0)
{
if (Writeflag)
{
*pAlgid = KnownAlgorithms[i].Id;
}
return(len + 2);
}
}
}
return(-1);
}
/************************************************************/
/* DecodeHeader decodes an ASN1 encoded sequence type header.*/
/* pbEncoded parameter is the encoded header. pdwLen is */
/* the parameter used to return the length of the encoded */
/* sequence. The function returns a -1 if it fails and */
/* otherwise returns the number of total bytes in the */
/* encoded header, not including the content. */
/************************************************************/
long
DecodeHeader(
DWORD * pdwLen,
BYTE * pbEncoded)
{
long len;
// make sure this is a sequence type
if (pbEncoded[0] != SEQUENCE_TAG)
return (-1);
// decode the length
if ((len = DecodeLength (pdwLen, pbEncoded + 1)) == -1)
return (-1);
return (len + 1);
}
/************************************************************/
/* DecodeSetOfHeader decodes an ASN1 encoded set of type */
/* header. pbEncoded parameter is the encoded header. pdwLen*/
/* is the parameter used to return the length of the encoded*/
/* set of. The function returns a -1 if it fails and */
/* otherwise returns the number of total bytes in the */
/* encoded header, not including the content. */
/************************************************************/
long
DecodeSetOfHeader(
DWORD * pdwLen,
BYTE * pbEncoded)
{
long len;
// make sure this is a sequence type
if (pbEncoded[0] != SET_OF_TAG)
return (-1);
// decode the length
if ((len = DecodeLength (pdwLen, pbEncoded + 1)) == -1)
return (-1);
return (len + 1);
}
long
DecodeSetHeader(
DWORD * pdwLen,
BYTE * pbEncoded)
{
long len;
if (*pbEncoded != BER_SET)
{
return(-1);
}
len = DecodeLength(pdwLen, pbEncoded + 1);
if (len < 0)
{
return(-1);
}
return(len + 1);
}
/****************************************************************/
/* DecodeInteger decodes an ASN1 encoded integer. The encoded */
/* integer is passed into the function with the pbEncoded */
/* parameter. The pbInt parameter is used to pass back the */
/* integer as an array of bytes, and dwLen is the number of */
/* in the array. The least significant byte of the integer */
/* is the zeroth byte of the array. The Writeflag indicates */
/* indicates if the result is to be written to the pbInt */
/* parameter. The function returns a -1 if it fails and */
/* otherwise returns the number of total bytes in the encoded */
/* integer. */
/* This implementation will only deal with positive integers. */
/****************************************************************/
long
DecodeInteger(
BYTE * pbInt,
DWORD * pdwLen,
BYTE * pbEncoded,
BOOL Writeflag)
{
long count;
long i;
// make sure this is tagged as an integer
if (pbEncoded[0] != INTEGER_TAG)
return (-1);
count = 1;
// decode the length field
if ((i = DecodeLength (pdwLen, pbEncoded + 1)) == -1)
return (-1);
count += i;
// write the integer out if suppose to
if (Writeflag)
{
if (pbEncoded[count] == 0)
{
count++;
(*pdwLen)--;
}
i = (*pdwLen) - 1;
while (i >= 0)
{
pbInt[i--] = pbEncoded[count++];
}
}
else
{
count += (long) *pdwLen;
}
// return the length of the encoded integer
return (count);
}
/****************************************************************/
/* DecodeString decodes an ASN1 encoded a character string. The*/
/* encoded string is passed into the function with the pbEncoded*/
/* parameter. The pbStr is used to pass the decoded string back*/
/* to the caller, and pdwLen is the number of characters in the */
/* decoded array. The Writeflag indicates if the result is to */
/* be written to the pbStr parameter. The function returns a */
/* -1 if it fails and otherwise returns the number of bytes in */
/* the encoded string. */
/****************************************************************/
long
DecodeString(
BYTE * pbStr,
DWORD * pdwLen,
BYTE * pbEncoded,
BOOL Writeflag)
{
long index;
if ((*pbEncoded != BER_PRINTABLE_STRING) &&
(*pbEncoded != BER_TELETEX_STRING) &&
(*pbEncoded != BER_GRAPHIC_STRING))
{
return(-1);
}
// determine how long the string is
if ((index = DecodeLength (pdwLen, pbEncoded + 1)) == -1)
{
return (-1);
}
index++;
if (Writeflag)
{
CopyMemory(pbStr, pbEncoded + index, *pdwLen);
}
return (index + *pdwLen);
}
/****************************************************************/
/* DecodeOctetString decodes an ASN1 encoded a octet string. The*/
/* encoded string is passed into the function with the pbEncoded*/
/* parameter. The pbStr is used to pass the decoded string back*/
/* to the caller, and pdwLen is the number of characters in the */
/* decoded array. The Writeflag indicates if the result is to */
/* be written to the pbStr parameter. The function returns a */
/* -1 if it fails and otherwise returns the number of bytes in */
/* the encoded string. */
/****************************************************************/
long
DecodeOctetString(
BYTE * pbStr,
DWORD * pdwLen,
BYTE * pbEncoded,
BOOL Writeflag)
{
long index;
if (pbEncoded[0] != OCTET_STRING_TAG)
return (-1);
// determine how long the string is
if ((index = DecodeLength (pdwLen, pbEncoded + 1)) == -1)
return (-1);
index++;
if (Writeflag)
{
CopyMemory(pbStr, pbEncoded + index, *pdwLen);
}
return (index + *pdwLen);
}
/****************************************************************/
/* DecodeBitString decodes an ASN1 encoded a bit string. The */
/* encoded string is passed into the function with the pbEncoded*/
/* parameter. The pbStr is used to pass the decoded string back*/
/* to the caller, and pdwLen is the number of characters in the */
/* decoded array. The Writeflag indicates if the result is to */
/* be written to the pbStr parameter. The function returns a */
/* -1 if it fails and otherwise returns the number of bytes in */
/* the encoded string. The DER are used in the decoding. */
/****************************************************************/
long
DecodeBitString(
BYTE * pbStr,
DWORD * pdwLen,
BYTE * pbEncoded,
BOOL Writeflag)
{
long index;
if (pbEncoded[0] != BIT_STRING_TAG)
return (-1);
// determine how long the string is
if ((index = DecodeLength (pdwLen, pbEncoded + 1)) == -1)
return (-1);
// move the index up two bytes, one for the tag and one for the byte after
// the length which tells the number of unused bits in the last byte, that
// byte is always zero in this implementation, so it is ignored
index += 2;
// subtract one from the length of the bit string (in bytes) since,
// to account for the byte after the length
(*pdwLen)--;
if (Writeflag)
{
CopyMemory(pbStr, pbEncoded + index, *pdwLen);
}
return (index + *pdwLen);
}
#if 0
/****************************************************************/
/* DecodeUTCTime decodes an ASN1 encoded Universal time type. */
/* time type. The Time parameter is the time passed into the */
/* function as a time_t type. The encoded result is passed back*/
/* in the pbEncoded parameter. The Writeflag indicates if the */
/* result is to be written to the pbEncoded parameter. The */
/* function returns a -1 if it fails and otherwise returns the */
/* number of total bytes in the encoded universal time. */
/****************************************************************/
long
DecodeUTCTime(time_t *pTime, BYTE *pbEncoded, BOOL Writeflag)
{
long count;
struct tm tmTime;
DWORD dwLen;
// check to make sure this is a universal time type
if (pbEncoded[0] != UTCTIME_TAG)
return -1;
// decode the length
if ((count = DecodeLength (&dwLen, pbEncoded + 1)) == -1)
return -1;
count++;
dwLen += count;
if (Writeflag)
{
// extract the year
tmTime.tm_year = (int)((pbEncoded[count] - 0x30) * 0xA
+ (pbEncoded[count + 1] - 0x30));
count += 2;
// extract the month
tmTime.tm_mon = (int)((pbEncoded[count] - 0x30) * 0xA
+ (pbEncoded[count + 1] - 0x30));
count += 2;
// extract the day
tmTime.tm_mday = (int)((pbEncoded[count] - 0x30) * 0xA
+ (pbEncoded[count + 1] - 0x30));
count += 2;
// extract the hour
tmTime.tm_hour = (int)((pbEncoded[count] - 0x30) * 0xA
+ (pbEncoded[count + 1] - 0x30));
count += 2;
// extract the minutes
tmTime.tm_min = (int)((pbEncoded[count] - 0x30) * 0xA
+ (pbEncoded[count + 1] - 0x30));
count += 2;
// extract the seconds
tmTime.tm_sec = (int)((pbEncoded[count] - 0x30) * 0xA
+ (pbEncoded[count + 1] - 0x30));
count += 2;
// make sure there is a Z at the end
if (pbEncoded[count] != 'Z')
return -1;
*pTime = mktime (&tmTime);
}
return (long)dwLen;
}
#endif
long
DecodeFileTime(
FILETIME * pTime,
BYTE * pbEncoded,
BOOL WriteFlag)
{
LONGLONG ft;
LONGLONG delta;
SYSTEMTIME st;
long count;
DWORD dwLen;
BOOL fUTC;
int Offset;
long TotalLen;
// check to make sure this is a universal time type
if (pbEncoded[0] != UTCTIME_TAG)
return -1;
// decode the length
if ((count = DecodeLength (&dwLen, pbEncoded + 1)) == -1)
return -1;
count++;
TotalLen = dwLen + count;
pbEncoded += count;
if (WriteFlag)
{
st.wYear = (WORD) ((pbEncoded[0] - '0') * 10) +
(pbEncoded[1] - '0');
if (st.wYear < 90)
{
st.wYear += 2000;
}
else
{
st.wYear += 1900;
}
pbEncoded += 2;
dwLen -= 2;
st.wMonth = (WORD) ((pbEncoded[0] - '0') * 10) +
(pbEncoded[1] - '0');
pbEncoded += 2;
dwLen -= 2;
st.wDay = (WORD) ((pbEncoded[0] - '0') * 10) +
(pbEncoded[1] - '0');
pbEncoded += 2;
dwLen -= 2;
st.wHour = (WORD) ((pbEncoded[0] - '0') * 10) +
(pbEncoded[1] - '0');
pbEncoded += 2;
dwLen -= 2;
st.wMinute = (WORD) ((pbEncoded[0] - '0') * 10) +
(pbEncoded[1] - '0');
pbEncoded += 2;
dwLen -= 2;
fUTC = FALSE;
Offset = 0;
st.wSecond = 0;
if (dwLen)
{
//
// Ok, still more to go:
//
if (*pbEncoded == 'Z')
{
DebugLog((DEB_TRACE, "FileTime: no seconds, Z term\n"));
//
// Ok, it is UTC.
//
dwLen++;
pbEncoded++;
}
else
{
if ((*pbEncoded == '+') ||
(*pbEncoded == '-') )
{
DebugLog((DEB_TRACE, "FileTime: no seconds, offset\n"));
//
// Yuck! Offset encoded!
//
if (dwLen != 5)
{
return( -1 );
}
Offset = (int) ((pbEncoded[1] - '0') * 10) +
(pbEncoded[2] - '0');
Offset *= 60;
Offset += (int) ((pbEncoded[3] - '0') * 10) +
(pbEncoded[4] - '0');
if (pbEncoded[0] == '-')
{
Offset *= -1;
}
}
else
{
st.wSecond = (WORD) ((pbEncoded[0] - '0') * 10) +
(pbEncoded[1] - '0');
if (dwLen == 3)
{
if (pbEncoded[2] != 'Z')
{
return( -1 );
}
}
else if (dwLen > 3)
{
Offset = (int) ((pbEncoded[3] - '0') * 10) +
(pbEncoded[4] - '0');
Offset *= 60;
Offset += (int) ((pbEncoded[5] - '0') * 10) +
(pbEncoded[6] - '0');
if (pbEncoded[2] == '-')
{
Offset *= -1;
}
}
}
}
}
st.wMilliseconds = 0;
SystemTimeToFileTime(&st, (FILETIME *) &ft);
if (Offset != 0)
{
delta = (LONGLONG) Offset * 10000000;
ft += delta;
}
*pTime = *((FILETIME *) &ft);
}
return(TotalLen);
}
/****************************************************************/
/* DecodeName decodes an ASN1 encoded Name type. The encoded */
/* name is passed into the function with the pbEncoded parameter*/
/* The pbName parameter is used to pass the name back to the */
/* caller and pdwLen is the length of the name in bytes. */
/* The Writeflag indicates if the result is to be written to */
/* the pbName parameter. The function returns a -1 if it */
/* fails and otherwise returns the number of total bytes in the */
/* encoded name. */
/****************************************************************/
long
DecodeName(
BYTE * pbName,
DWORD * pdwLen,
BYTE * pbEncoded,
BOOL Writeflag)
{
long index;
DWORD dwLen;
// decode the sequence header
if ((index = DecodeHeader (&dwLen, pbEncoded)) == -1)
return (-1);
// decode the set of header
if ((index += DecodeSetOfHeader (&dwLen, pbEncoded + index)) < index)
return (-1);
// decode the sequence header
if ((index += DecodeHeader (&dwLen, pbEncoded + index)) < index)
return (-1);
// decode the attribute type, in this implementation it is fake
index += 3; // 3 because this is the length of the fake OBJECT IDENTIFIER
// decode the string which is the name
if ((index += DecodeString (pbName, pdwLen, pbEncoded + index, Writeflag)) < index)
return (-1);
return index;
}
long
DecodeNull(
BYTE * pbEncoded)
{
if (*pbEncoded != NULL_TAG)
{
return(-1);
}
return(2);
}
long
DecodeNameType(
PSTR * ppPrefix,
BYTE * pbEncoded)
{
DWORD i;
DWORD len;
if (*pbEncoded != OBJECT_ID_TAG)
{
return(-1);
}
pbEncoded++;
len = *pbEncoded++;
for (i = 0; i < sizeof(KnownNameTypes) / sizeof(NameTypes) ; i++ )
{
if (KnownNameTypes[i].SequenceLen == len)
{
if (memcmp(pbEncoded, KnownNameTypes[i].Sequence, len) == 0)
{
*ppPrefix = KnownNameTypes[i].Prefix;
return(len + 2);
}
}
}
return(-1);
}
long
DecodeRDN(
PSTR pName,
DWORD * pdwComponentLength,
BYTE * pbEncoded,
BOOL WriteFlag)
{
long index;
DWORD dwLen;
PSTR Prefix;
long PrefixLen;
long CompLen;
long Processed;
index = DecodeSetHeader(&dwLen, pbEncoded);
if (index < 0)
{
return(-1);
}
CompLen = 0;
Processed = index;
//
// BUGBUG - do we handle sets with multiple names?
//
pbEncoded += index;
index = DecodeHeader(&dwLen, pbEncoded);
if (index < 0)
{
return(-1);
}
pbEncoded += index;
Processed += index;
index = DecodeNameType(&Prefix, pbEncoded);
if (index < 0)
{
return(-1);
}
pbEncoded += index;
Processed += index;
PrefixLen = strlen(Prefix);
if (WriteFlag)
{
memcpy(pName, Prefix, PrefixLen);
pName += PrefixLen;
}
CompLen = PrefixLen;
index = DecodeString(pName, &dwLen, pbEncoded, WriteFlag);
if (index < 0)
{
return(-1);
}
CompLen += dwLen;
*pdwComponentLength = CompLen;
Processed += index;
return(Processed);
}
long
DecodeDN(
PSTR pName,
DWORD * pdwLen,
BYTE * pbEncoded,
BOOL WriteFlag)
{
long index;
DWORD dwLen;
long TotalNameLength;
DWORD ComponentLength;
DWORD NameLength;
long EncodedNameLength;
index = DecodeHeader(&dwLen, pbEncoded);
if (index < 0)
{
return(-1);
}
EncodedNameLength = index + dwLen;
TotalNameLength = dwLen;
NameLength = 0;
while (TotalNameLength > 0)
{
pbEncoded += index;
index = DecodeRDN( pName,
&ComponentLength,
pbEncoded,
WriteFlag);
if (index < 0)
{
return(-1);
}
TotalNameLength -= index;
NameLength += ComponentLength;
if (WriteFlag)
{
pName += ComponentLength;
if (TotalNameLength > 0)
{
*pName++ = ',';
*pName++ = ' ';
NameLength += 2;
}
}
else
{
if (TotalNameLength > 0)
{
NameLength += 2;
}
}
}
NameLength++;
if (WriteFlag)
{
*pName++ = '\0';
}
*pdwLen = NameLength;
return(EncodedNameLength);
}
long
DecodeAlgorithm(
ALG_ID * pAlgId,
PBYTE pbEncoded,
BOOL WriteFlag)
{
long Result;
DWORD dwLen;
long Processed;
Result = DecodeHeader( &dwLen, pbEncoded);
if (Result < 0)
{
return(-1);
}
pbEncoded += Result;
Processed = Result;
Result = DecodeAlgid( pAlgId,
pbEncoded,
WriteFlag );
if (Result < 0)
{
return(-1);
}
pbEncoded += Result;
Processed += Result;
Result = DecodeNull(pbEncoded);
if (Result < 0)
{
return(-1);
}
return(Processed + Result);
}
long
DecodeBsafePubKey(
LPBSAFE_PUB_KEY * ppPubKey,
DWORD * pcbPubKey,
PBYTE pbEncoded)
{
LPBSAFE_PUB_KEY pk;
long Result;
long Bitstring;
DWORD dwLen;
DWORD Aligned;
Result = DecodeHeader(&dwLen, pbEncoded);
if (Result < 0)
{
goto DecodeKey_CleanUp;
}
Bitstring = Result + dwLen;
pbEncoded += Result;
Result = DecodeInteger(NULL, &dwLen, pbEncoded, FALSE);
if (Result < 0)
{
goto DecodeKey_CleanUp;
}
//
// If this is odd, then there is a leading zero due to DER encoding.
// compensate.
//
if (dwLen & 1)
{
dwLen --;
}
Aligned = (dwLen + sizeof(DWORD)) / sizeof(DWORD);
*pcbPubKey = sizeof(BSAFE_PUB_KEY) + (Aligned + 1) * sizeof(DWORD);
pk = (LPBSAFE_PUB_KEY) SslAlloc('yekP', LMEM_FIXED | LMEM_ZEROINIT, *pcbPubKey );
if (!pk)
{
goto DecodeKey_CleanUp;
}
pk->magic = RSA1;
pk->keylen = (Aligned + 1) * 4;
pk->bitlen = dwLen * 8;
pk->datalen = (pk->bitlen / 8) - 1;
Result = DecodeInteger((BYTE *) (pk + 1), &dwLen, pbEncoded, TRUE);
if (Result < 0)
{
goto DecodeKey_CleanUp;
}
pbEncoded += Result;
Result = DecodeInteger((PUCHAR) &pk->pubexp, &dwLen, pbEncoded, TRUE);
if (Result < 0)
{
goto DecodeKey_CleanUp;
}
*ppPubKey = pk;
return(Bitstring);
DecodeKey_CleanUp:
if (pk)
{
SslFree(pk);
}
return(-1);
}
long
DecryptOctetString(
DWORD * pdwLen,
BYTE * pbEncoded,
BOOL Writeflag,
PSTR pszPassword,
ALG_ID AlgId)
{
long index;
RC4_KEYSTRUCT Rc4Key;
MD5_CTX Md5;
if (pbEncoded[0] != OCTET_STRING_TAG)
return ( -1 );
// determine how long the string is
if ((index = DecodeLength (pdwLen, pbEncoded + 1)) == -1)
return (-1);
index++;
if (Writeflag)
{
MD5Init( &Md5 );
MD5Update( &Md5, pszPassword, strlen( pszPassword ) );
MD5Final( &Md5 );
rc4_key(&Rc4Key, 16, Md5.digest );
rc4(&Rc4Key, *pdwLen, pbEncoded + index );
}
return (index );
}
long
DecodePrivateKeyFile(
LPBSAFE_PRV_KEY * ppKey,
PBYTE pbEncoded,
DWORD cbEncoded,
PSTR Password )
{
DWORD dwLen;
PUCHAR pbScan;
long Result;
ALG_ID AlgId;
DWORD Version;
LPBSAFE_PRV_KEY pKey;
DWORD Aligned;
BSAFE_KEY_PARTS parts;
DWORD dwPriv;
DWORD dwPub;
DWORD dwBits;
Result = DecodeHeader( &dwLen, pbEncoded );
if (Result < 0)
{
return( -1 );
}
pbEncoded += Result;
Result = DecodeOctetString( NULL, &dwLen, pbEncoded, FALSE );
if (Result < 0)
{
return( -1 );
}
pbEncoded += Result;
Result = DecodeHeader( &dwLen, pbEncoded );
if (Result < 0)
{
return( -1 );
}
pbEncoded += Result;
Result = DecodeAlgorithm( &AlgId, pbEncoded, TRUE );
if (Result < 0)
{
return( -1 );
}
if (AlgId != RC4_STREAM)
{
return( -1 );
}
pbEncoded += Result;
//
// Now, the next item should be an octet string, which is encrypted
// with the password above. So, we need to skip into it, decrypt it,
// then treat it as a constructed type:
//
Result = DecryptOctetString(&dwLen,
pbEncoded,
TRUE,
Password,
AlgId );
if (Result < 0)
{
return( -1 );
}
pbEncoded += Result;
//
// The moment of truth
//
Result = DecodeHeader( &dwLen, pbEncoded );
if (Result < 0)
{
return( -1 );
}
pbEncoded += Result;
Version = 0;
Result = DecodeInteger( (PUCHAR) &Version, &dwLen, pbEncoded, FALSE );
if ((Result < 0) || ( dwLen > 4 ) )
{
return( -1 );
}
Result = DecodeInteger( (PUCHAR) &Version, &dwLen, pbEncoded, TRUE );
if (Version != 0)
{
return( -1 );
}
pbEncoded += Result;
Result = DecodeAlgorithm( &AlgId, pbEncoded, TRUE );
if ((Result < 0) || (AlgId != BASIC_RSA) )
{
return( -1 );
}
pbEncoded += Result;
//
// This is now the serialized rsa key.
//
if (*pbEncoded != OCTET_STRING_TAG)
{
return( -1 );
}
pbEncoded ++;
Result = DecodeLength( &dwLen, pbEncoded );
if (Result < 0)
{
return( -1 );
}
pbEncoded += Result;
//
// The sequence is the key...
//
Result = DecodeHeader( &dwLen, pbEncoded );
if (Result < 0)
{
return( -1 );
}
pbEncoded += Result;
//
// Skip past the version
//
Result = DecodeInteger( NULL, &dwLen, pbEncoded, FALSE );
if (Result < 0)
{
goto DecodePrivate_CleanUp;
}
pbEncoded += Result;
Result = DecodeInteger(NULL, &dwLen, pbEncoded, FALSE);
if (Result < 0)
{
goto DecodePrivate_CleanUp;
}
//
// If this is odd, then there is a leading zero due to DER encoding.
// compensate.
//
if (dwLen & 1)
{
dwLen --;
}
Aligned = (dwLen + sizeof(DWORD)) / sizeof(DWORD);
dwBits = dwLen * 8;
BSafeComputeKeySizes(&dwPub, &dwPriv, &dwBits );
pKey = SslAlloc('yekP', LMEM_FIXED | LMEM_ZEROINIT,
dwPriv );
if (!pKey)
{
return( -1 );
}
pKey->magic = RSA2;
pKey->keylen = (Aligned + 1) * 4;
pKey->bitlen = dwLen * 8;
pKey->datalen = dwLen - 1;
BSafeGetPrvKeyParts(pKey, &parts);
Result = DecodeInteger( parts.modulus, &dwLen, pbEncoded, TRUE );
if (Result < 0)
{
goto DecodePrivate_CleanUp;
}
pbEncoded += Result;
Result = DecodeInteger( NULL, &dwLen, pbEncoded,FALSE );
if ((Result < 0) || (dwLen > sizeof(DWORD) ))
{
goto DecodePrivate_CleanUp;
}
Result = DecodeInteger( (PUCHAR) &pKey->pubexp, &dwLen, pbEncoded, TRUE );
pbEncoded += Result;
Result = DecodeInteger( parts.prvexp, &dwLen, pbEncoded, TRUE );
pbEncoded += Result;
Result = DecodeInteger( parts.prime1, &dwLen, pbEncoded, TRUE );
pbEncoded += Result;
Result = DecodeInteger( parts.prime2, &dwLen, pbEncoded, TRUE );
pbEncoded += Result;
Result = DecodeInteger( parts.exp1, &dwLen, pbEncoded, TRUE );
pbEncoded += Result;
Result = DecodeInteger( parts.exp2, &dwLen, pbEncoded, TRUE );
pbEncoded += Result;
Result = DecodeInteger( parts.coef, &dwLen, pbEncoded, TRUE );
*ppKey = pKey;
return( 0 );
DecodePrivate_CleanUp:
SslFree( pKey );
return( -1 );
}