windows-nt/Source/XPSP1/NT/ds/security/services/ca/genreq/enc.cpp
2020-09-26 16:20:57 +08:00

771 lines
18 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1995 - 1999
//
// File: enc.cpp
//
//--------------------------------------------------------------------------
#include <pch.cpp>
#pragma hdrstop
#include "encode.h"
OIDTRANSLATE const *
LookupOidTranslate(
IN CHAR const *pszObjId)
{
DWORD i;
OIDTRANSLATE const *pOid = NULL;
for (i = 0; i < g_cOidTranslate; i++)
{
if (0 == strcmp(pszObjId, g_aOidTranslate[i].pszObjId))
{
pOid = &g_aOidTranslate[i];
break;
}
}
CSASSERT(NULL != pOid);
return(pOid);
}
long
EncodeObjId(
OPTIONAL OUT BYTE *pbEncoded,
IN CHAR const *pszObjId)
{
OIDTRANSLATE const *pOid;
long cbLength;
pOid = LookupOidTranslate(pszObjId);
if (NULL != pbEncoded)
{
*pbEncoded++ = BER_OBJECT_ID;
}
cbLength = EncodeLength(pbEncoded, pOid->cbOIDEncoded);
if (NULL != pbEncoded)
{
CopyMemory(
pbEncoded + cbLength,
pOid->abOIDEncoded,
pOid->cbOIDEncoded);
}
return(1 + cbLength + pOid->cbOIDEncoded);
}
//+*************************************************************************
// 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 pbEncoded parameter
// indicates if the result is to be written to the pbEncoded
// parameter. The function cannot fail and returns the
// number of total bytes in the encoded length.
// encoded length.
//**************************************************************************
// Notes: Encodes 0x0000 to 0x007f as <lobyte>
// Encodes 0x0080 to 0x00ff as <81>, <lobyte>
// Encodes 0x0100 to 0xffff as <82>, <hibyte>, <lobyte>
long
EncodeLength(
OPTIONAL OUT BYTE *pbEncoded,
IN DWORD dwLen)
{
// length is between 2^8 and 2^16 - 1
if (dwLen > 0xff)
{
if (NULL != pbEncoded)
{
pbEncoded[0] = 0x82;
pbEncoded[1] = (BYTE) (dwLen >> 8);
pbEncoded[2] = (BYTE) dwLen;
}
return(3);
}
// length is between 2^7 and 2^8 - 1
if (dwLen > 0x7f)
{
if (NULL != pbEncoded)
{
pbEncoded[0] = 0x81;
pbEncoded[1] = (BYTE) dwLen;
}
return(2);
}
// length is between 0 and 2^7 - 1
if (NULL != pbEncoded)
{
pbEncoded[0] = (BYTE) dwLen;
}
return(1);
}
long
EncodeNull(
OPTIONAL OUT BYTE *pbEncoded)
{
if (NULL != pbEncoded)
{
*pbEncoded++ = BER_NULL;
*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 pbEncoded 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(
OPTIONAL OUT BYTE *pbEncoded,
IN DWORD Algid)
{
DWORD i;
LONG cb = -1;
// determine the algorithm id which is to be encoded and
// copy the appropriate encoded algid into the destination
for (i = 0; i < g_cAlgIdTranslate; i++)
{
if (Algid == g_aAlgIdTranslate[i].AlgId)
{
cb = EncodeObjId(pbEncoded, g_aAlgIdTranslate[i].pszObjId);
break;
}
}
return(cb);
}
long
EncodeAlgorithm(
OPTIONAL OUT BYTE *pbEncoded,
IN DWORD AlgId)
{
BYTE abTemp[32];
long cbResult;
BYTE *pb;
pb = abTemp;
// Take a guess at the total length:
pb += EncodeHeader(pb, sizeof(abTemp));
cbResult = EncodeAlgid(pb, AlgId);
if (cbResult == -1)
{
return(-1);
}
pb += cbResult;
cbResult += EncodeNull(pb);
// Fix up the total length:
cbResult += EncodeHeader(abTemp, cbResult);
if (NULL != pbEncoded)
{
CopyMemory(pbEncoded, abTemp, cbResult);
}
return(cbResult);
}
//+*************************************************************************
// 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 pbEncoded
// indicates if the result is to be written to the pbEncoded
// parameter. The function cannot fail and returns the number
// of total bytes in the encoded integer.
// This implementation will only deal with positive integers.
//**************************************************************************
long
EncodeInteger(
OPTIONAL OUT BYTE *pbEncoded,
IN BYTE const *pbInt,
IN DWORD dwLen)
{
DWORD iInt;
long j; // Must be signed!
LONG cbResult;
LONG cbLength;
if (NULL != pbEncoded)
{
*pbEncoded++ = BER_INTEGER;
}
cbResult = 1;
// find the most significant non-zero byte
for (iInt = dwLen - 1; pbInt[iInt] == 0; iInt--)
{
if (iInt == 0) // if the integer value is 0
{
if (NULL != pbEncoded)
{
*pbEncoded++ = 0x01;
*pbEncoded++ = 0x00;
}
return(cbResult + 2);
}
}
// if the most significant bit of the most significant byte is set then add
// a 0 byte to the beginning.
if (pbInt[iInt] > 0x7f)
{
// encode the length
cbLength = EncodeLength(pbEncoded, iInt + 2);
// set the first byte of the integer to 0 and increment pointer
if (NULL != pbEncoded)
{
pbEncoded += cbLength;
*pbEncoded++ = 0;
}
cbResult++;
}
else
{
// encode the length
cbLength = EncodeLength(pbEncoded, iInt + 1);
if (NULL != pbEncoded)
{
pbEncoded += cbLength;
}
}
cbResult += cbLength;
// copy the integer bytes into the encoded buffer
if (NULL != pbEncoded)
{
// copy the integer bytes into the encoded buffer
for (j = iInt; j >= 0; j--)
{
*pbEncoded++ = pbInt[j];
}
}
cbResult += iInt + 1;
return(cbResult);
}
long
EncodeUnicodeString(
OPTIONAL OUT BYTE *pbEncoded,
IN WCHAR const *pwsz)
{
long cbLength;
long cbData = wcslen(pwsz) * sizeof(WCHAR);
if (NULL != pbEncoded)
{
*pbEncoded++ = BER_UNICODE_STRING;
}
cbLength = EncodeLength(pbEncoded, cbData);
if (NULL != pbEncoded)
{
pbEncoded += cbLength;
for ( ; L'\0' != *pwsz; pwsz++)
{
*pbEncoded++ = (BYTE) (*pwsz >> 8);
*pbEncoded++ = (BYTE) *pwsz;
}
}
return(1 + cbLength + cbData);
}
//+*************************************************************************
// EncodeIA5String 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 pbEncoded
// indicates if the result is to be written to the pbEncoded
// parameter. The function cannot fail and returns the number
// of total bytes in the encoded string.
//**************************************************************************
long
EncodeIA5String(
OPTIONAL OUT BYTE *pbEncoded,
IN BYTE const *pbStr,
IN DWORD dwLen)
{
long cbLength;
if (NULL != pbEncoded)
{
*pbEncoded++ = BER_IA5_STRING;
}
cbLength = EncodeLength(pbEncoded, dwLen);
if (NULL != pbEncoded)
{
CopyMemory(pbEncoded + cbLength, pbStr, dwLen);
}
return(1 + cbLength + dwLen);
}
//+*************************************************************************
// 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
// pbEncoded parameter indicates if the result is to be written
// to the pbEncoded parameter. The function cannot fail and
// returns the number of total bytes in the encoded octet string
//**************************************************************************
long
EncodeOctetString(
OPTIONAL OUT BYTE *pbEncoded,
IN BYTE const *pbStr,
IN DWORD dwLen)
{
long cbLength;
if (NULL != pbEncoded)
{
*pbEncoded++ = BER_OCTET_STRING;
}
cbLength = EncodeLength(pbEncoded, dwLen);
if (NULL != pbEncoded)
{
CopyMemory(pbEncoded + cbLength, pbStr, dwLen);
}
return(1 + cbLength + dwLen);
}
//+*************************************************************************
// 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 pbEncoded
// indicates if the result is to be written to the pbEncoded
// parameter. The function cannot fail and returns the number
// of total bytes in the encoded string. This function uses
// the DER.
//**************************************************************************
long
EncodeBitString(
OPTIONAL OUT BYTE *pbEncoded,
IN BYTE const *pbStr,
IN DWORD dwLen)
{
long cbLength;
if (NULL != pbEncoded)
{
*pbEncoded++ = BER_BIT_STRING;
}
cbLength = EncodeLength(pbEncoded, dwLen + 1);
if (NULL != pbEncoded)
{
pbEncoded += cbLength;
// 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++ = 0;
CopyMemory(pbEncoded, pbStr, dwLen);
}
return(1 + cbLength + 1 + dwLen);
}
//+---------------------------------------------------------------------------
//
// 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(
OPTIONAL OUT BYTE *pbEncoded,
IN FILETIME Time,
IN BOOL UTC)
{
if (NULL != pbEncoded)
{
SYSTEMTIME st;
FILETIME ft;
int count;
if (UTC)
{
ft = Time;
}
else
{
LocalFileTimeToFileTime(&Time, &ft);
}
FileTimeToSystemTime(&ft, &st);
*pbEncoded++ = BER_UTC_TIME;
count = EncodeLength(pbEncoded, 13);
// NOTE ON Y2K COMPLIANCE: This is test tool. WE WILL NOT FIX THIS
// CODE! It is only used to encode current dates, anyway,
pbEncoded++;
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';
}
// Tag(1) + Len(1) + Year(2) + Month(2) + Day(2) +
// Hour(2) + Min(2) + Sec(2) + 'Z'(1) --> 15
return(15);
}
//+*************************************************************************
// EncodeHeader ASN1 encodes a header for a sequence type. The
// dwLen is the length of the encoded information in the
// sequence. The pbEncoded indicates if the result is to be
// written to the pbEncoded parameter. The function cannot
// fail and returns the number of total bytes in the encoded
// header.
//**************************************************************************
// Notes: Encodes header as <BER_SEQUENCE>, <length>
long
EncodeHeader(
OPTIONAL OUT BYTE *pbEncoded,
IN DWORD dwLen)
{
if (NULL != pbEncoded)
{
*pbEncoded++ = BER_SEQUENCE;
}
return(1 + EncodeLength(pbEncoded, dwLen));
}
//+*************************************************************************
// EncodeSetOfHeader ASN1 encodes a header for a set of type.
// The dwLen is the length of the encoded information in the
// set of. The pbEncoded indicates if the result is to be
// written to the pbEncoded parameter. The function cannot
// fail and returns the number of total bytes in the encoded
// header.
//**************************************************************************
// Notes: Encodes header as <SET_OF_TAG>, <length>
long
EncodeSetOfHeader(
OPTIONAL OUT BYTE *pbEncoded,
IN DWORD dwLen)
{
if (NULL != pbEncoded)
{
*pbEncoded++ = BER_SET_RAW;
}
return(1 + EncodeLength(pbEncoded, dwLen));
}
// Notes: Encodes header as <BER_OPTIONAL | 0>, <length>
long
EncodeAttributeHeader(
OPTIONAL OUT BYTE *pbEncoded,
IN DWORD dwLen)
{
if (NULL != pbEncoded)
{
*pbEncoded++ = BER_OPTIONAL | 0;
}
return(1 + EncodeLength(pbEncoded, dwLen));
}
// Notes: Encodes header as <BER_SET>, <length>
long
EncodeSetHeader(
OPTIONAL OUT BYTE *pbEncoded,
IN DWORD dwLen)
{
if (NULL != pbEncoded)
{
*pbEncoded++ = BER_SET;
}
return(1 + EncodeLength(pbEncoded, dwLen));
}
//+*************************************************************************
// EncodeName ASN1 encodes a Name type. The pbName parameter is
// the name and dwLen is the length of the name in bytes.
// The pbEncoded indicates if the result is to be written to
// the pbEncoded parameter. The function cannot fail and
// returns the number of total bytes in the encoded name.
//**************************************************************************
long
EncodeName(
OPTIONAL OUT BYTE *pbEncoded,
IN BYTE const *pbName,
IN DWORD dwLen)
{
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
ValueLen = EncodeIA5String(Value, pbName, dwLen);
// 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;
// encode the header for the attribute
AttributeLen = EncodeHeader(Attribute, ValueLen + TypeLen);
// copy the attribute type and value into the attribute
CopyMemory(Attribute + AttributeLen, Type, TypeLen);
AttributeLen += TypeLen;
CopyMemory(Attribute + AttributeLen, Value, ValueLen);
AttributeLen += ValueLen;
// encode set of header
HdrLen = EncodeSetOfHeader(SetHdr, AttributeLen);
// encode Name header
NameLen = EncodeHeader(pbEncoded, HdrLen + AttributeLen);
if (NULL != pbEncoded)
{
CopyMemory(pbEncoded + NameLen, SetHdr, HdrLen);
}
NameLen += HdrLen;
if (NULL != pbEncoded)
{
CopyMemory(pbEncoded + NameLen, Attribute, AttributeLen);
}
return(NameLen + AttributeLen);
}
long
EncodeRDN(
OPTIONAL OUT BYTE *pbEncoded,
IN NAMEENTRY const *pNameEntry)
{
LONG cbResult;
LONG Length;
DWORD cbOIDandData;
DWORD cbSequence;
OIDTRANSLATE const *pOidName;
// Compute the size of the encoded OID and RDN string, with BER encoding
// tags and lengths.
pOidName = LookupOidTranslate(pNameEntry->pszObjId);
cbOIDandData =
1 +
EncodeLength(NULL, pOidName->cbOIDEncoded) +
pOidName->cbOIDEncoded +
1 +
EncodeLength(NULL, pNameEntry->cbData) +
pNameEntry->cbData;
cbSequence = 1 + EncodeLength(NULL, cbOIDandData) + cbOIDandData;
Length = EncodeSetHeader(pbEncoded, cbSequence);
if (NULL != pbEncoded)
{
pbEncoded += Length;
}
cbResult = EncodeHeader(pbEncoded, cbOIDandData);
if (NULL != pbEncoded)
{
pbEncoded += cbResult;
*pbEncoded++ = BER_OBJECT_ID;
}
Length += cbResult + 1;
cbResult = EncodeLength(pbEncoded, pOidName->cbOIDEncoded);
if (NULL != pbEncoded)
{
pbEncoded += cbResult;
CopyMemory(pbEncoded, pOidName->abOIDEncoded, pOidName->cbOIDEncoded);
pbEncoded += pOidName->cbOIDEncoded;
*pbEncoded++ = pNameEntry->BerTag;
}
Length += cbResult + pOidName->cbOIDEncoded + 1;
cbResult = EncodeLength(pbEncoded, pNameEntry->cbData);
Length += cbResult;
if (NULL != pbEncoded)
{
CopyMemory(pbEncoded + cbResult, pNameEntry->pbData, pNameEntry->cbData);
}
return(Length + pNameEntry->cbData);
}
long
EncodeDN(
OPTIONAL OUT BYTE *pbEncoded,
IN NAMETABLE const *pNameTable)
{
CHAR *pszNext;
CHAR *pszRDN;
long Result;
long Length;
long SaveResult;
NAMEENTRY const *pNameEntry;
DWORD i;
SaveResult = 0; // force one full iteration
pNameEntry = pNameTable->pNameEntry;
Length = 0;
for (i = 0; i < pNameTable->cnt; i++)
{
Length += 9 + pNameEntry->cbData;
pNameEntry++;
}
while (TRUE)
{
BYTE *pb;
pb = pbEncoded;
Result = EncodeHeader(pb, Length);
if (SaveResult == Result)
{
break;
}
if (NULL != pb)
{
pb += Result;
}
SaveResult = Result;
Length = 0;
pNameEntry = pNameTable->pNameEntry;
for (i = 0; i < pNameTable->cnt; i++)
{
Result = EncodeRDN(pb, pNameEntry);
if (Result < 0)
{
Length = 0;
goto error; // return(-1)
}
if (NULL != pb)
{
pb += Result;
}
Length += Result;
pNameEntry++;
}
}
error:
return(Result + Length);
}