815 lines
26 KiB
C++
815 lines
26 KiB
C++
//+-------------------------------------------------------------------------
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1995 - 1999
|
|
//
|
|
// File: asn1util.cpp
|
|
//
|
|
// Contents: ASN.1 utility helper functions.
|
|
//
|
|
// Functions: Asn1UtilDecodeLength
|
|
// Asn1UtilExtractContent
|
|
// Asn1UtilIsPKCS7WithoutContentType
|
|
// Asn1UtilAdjustEncodedLength
|
|
// Asn1UtilExtractValues
|
|
// Asn1UtilExtractPKCS7SignedDataContent
|
|
// Asn1UtilExtractCertificateToBeSignedContent
|
|
// Asn1UtilExtractCertificatePublicKeyInfo
|
|
// Asn1UtilExtractKeyIdFromCertInfo
|
|
//
|
|
// History: 04-Dec-96 philh created from kevinr's wincrmsg version
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "global.hxx"
|
|
#include <dbgdef.h>
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Get the number of contents octets in a BER encoding.
|
|
//
|
|
// Parameters:
|
|
// pcbContent - receives the number of contents octets if definite
|
|
// encoding, else CMSG_INDEFINITE_LENGTH
|
|
// pbLength - points to the first length octet
|
|
// cbDER - number of bytes remaining in the encoding
|
|
//
|
|
// Returns:
|
|
// success - the number of bytes in the length field, >0
|
|
// failure - <0
|
|
//--------------------------------------------------------------------------
|
|
LONG
|
|
WINAPI
|
|
Asn1UtilDecodeLength(
|
|
OUT DWORD *pcbContent,
|
|
IN const BYTE *pbLength,
|
|
IN DWORD cbEncoded)
|
|
{
|
|
long i;
|
|
BYTE cbLength;
|
|
const BYTE *pb;
|
|
|
|
if (cbEncoded < 1)
|
|
goto TooLittleData;
|
|
|
|
if (0x80 == *pbLength) {
|
|
*pcbContent = CMSG_INDEFINITE_LENGTH;
|
|
i = 1;
|
|
goto CommonReturn;
|
|
}
|
|
|
|
// determine the number of length octets and contents octets
|
|
if ((cbLength = *pbLength) & 0x80) {
|
|
cbLength &= ~0x80; // low 7 bits have number of bytes
|
|
if (cbLength > 4)
|
|
goto LengthTooLargeError;
|
|
if (cbLength >= cbEncoded)
|
|
goto TooLittleData;
|
|
*pcbContent = 0;
|
|
for (i=cbLength, pb=pbLength+1; i>0; i--, pb++)
|
|
*pcbContent = (*pcbContent << 8) + (const DWORD)*pb;
|
|
i = cbLength + 1;
|
|
} else {
|
|
*pcbContent = (DWORD)cbLength;
|
|
i = 1;
|
|
}
|
|
|
|
CommonReturn:
|
|
return i; // how many bytes there were in the length field
|
|
|
|
ErrorReturn:
|
|
i = -1;
|
|
goto CommonReturn;
|
|
TooLittleData:
|
|
i = ASN1UTIL_INSUFFICIENT_DATA;
|
|
goto CommonReturn;
|
|
TRACE_ERROR(LengthTooLargeError)
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Point to the content octets in a BER-encoded blob.
|
|
//
|
|
// Returns:
|
|
// success - the number of bytes skipped, >=0
|
|
// failure - <0
|
|
//
|
|
// NB- If the blob is indefinite-length encoded, *pcbContent is set to
|
|
// CMSG_INDEFINITE_LENGTH
|
|
//--------------------------------------------------------------------------
|
|
LONG
|
|
WINAPI
|
|
Asn1UtilExtractContent(
|
|
IN const BYTE *pbEncoded,
|
|
IN DWORD cbEncoded,
|
|
OUT DWORD *pcbContent,
|
|
OUT const BYTE **ppbContent)
|
|
{
|
|
#define TAG_MASK 0x1f
|
|
DWORD cbIdentifier;
|
|
DWORD cbContent;
|
|
LONG cbLength;
|
|
LONG lHeader;
|
|
const BYTE *pb = pbEncoded;
|
|
|
|
if (0 == cbEncoded--)
|
|
goto TooLittleData;
|
|
|
|
// Skip over the identifier octet(s)
|
|
if (TAG_MASK == (*pb++ & TAG_MASK)) {
|
|
// high-tag-number form
|
|
cbIdentifier = 2;
|
|
while (TRUE) {
|
|
if (0 == cbEncoded--)
|
|
goto TooLittleData;
|
|
if (0 == (*pb++ & 0x80))
|
|
break;
|
|
cbIdentifier++;
|
|
}
|
|
} else {
|
|
// low-tag-number form
|
|
cbIdentifier = 1;
|
|
}
|
|
|
|
if (0 > (cbLength = Asn1UtilDecodeLength( &cbContent, pb, cbEncoded))) {
|
|
lHeader = cbLength;
|
|
goto CommonReturn;
|
|
}
|
|
|
|
pb += cbLength;
|
|
|
|
*pcbContent = cbContent;
|
|
*ppbContent = pb;
|
|
|
|
lHeader = cbLength + cbIdentifier;
|
|
CommonReturn:
|
|
return lHeader;
|
|
|
|
TooLittleData:
|
|
lHeader = ASN1UTIL_INSUFFICIENT_DATA;
|
|
goto CommonReturn;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Returns TRUE if we believe this is a Bob special that has ommitted the
|
|
// PKCS #7 ContentType.
|
|
//
|
|
// For PKCS #7: an Object Identifier tag (0x06) immediately follows the
|
|
// identifier and length octets. For a Bob special: an integer tag (0x02)
|
|
// follows the identifier and length octets.
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
WINAPI
|
|
Asn1UtilIsPKCS7WithoutContentType(
|
|
IN const BYTE *pbDER,
|
|
IN DWORD cbDER)
|
|
{
|
|
DWORD cbContent;
|
|
const BYTE *pbContent;
|
|
|
|
// Handle MappedFile Exceptions
|
|
__try {
|
|
|
|
if (0 < Asn1UtilExtractContent(pbDER, cbDER, &cbContent, &pbContent) &&
|
|
(pbContent < pbDER + cbDER) &&
|
|
(0x02 == *pbContent))
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
SetLastError(GetExceptionCode());
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Decode the Asn1 length bytes to possibly downward adjust the length.
|
|
//
|
|
// The returned length is always <= cbDER.
|
|
//--------------------------------------------------------------------------
|
|
DWORD
|
|
WINAPI
|
|
Asn1UtilAdjustEncodedLength(
|
|
IN const BYTE *pbDER,
|
|
IN DWORD cbDER
|
|
)
|
|
{
|
|
// Decode the header to get the real length. I've seen files with extra
|
|
// stuff.
|
|
|
|
LONG lLen;
|
|
DWORD cbLen;
|
|
DWORD cbContent;
|
|
const BYTE *pbContent;
|
|
lLen = Asn1UtilExtractContent(pbDER, cbDER, &cbContent, &pbContent);
|
|
if ((lLen >= 0) && (cbContent != CMSG_INDEFINITE_LENGTH)) {
|
|
cbLen = (DWORD)lLen + cbContent;
|
|
if (cbLen < cbDER)
|
|
cbDER = cbLen;
|
|
// else if (cbLen > cbDER)
|
|
// DER length exceeds input file
|
|
}
|
|
// else
|
|
// Can't decode DER length
|
|
|
|
return cbDER;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Extract one or more tagged values from the ASN.1 encoded byte array.
|
|
//
|
|
// Either steps into the value's content octets (ASN1UTIL_STEP_INTO_VALUE_OP)
|
|
// or steps over the value's tag, length and content octets
|
|
// (ASN1UTIL_STEP_OVER_VALUE_OP or ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP).
|
|
//
|
|
// For tag matching, only supports single byte tags. STEP_OVER values
|
|
// must be definite-length encoded.
|
|
//
|
|
// *pcValue is updated with the number of values successfully extracted.
|
|
//
|
|
// Returns:
|
|
// success - >= 0 => length of all values successfully extracted. For
|
|
// STEP_INTO, only the tag and length octets.
|
|
// failure - < 0 => negative (offset + 1) of first bad tagged value
|
|
// LastError is updated with the error.
|
|
//
|
|
// A non-NULL rgValueBlob[] is updated with the pointer to and length of the
|
|
// tagged value or its content octets. For OPTIONAL_STEP_OVER, if tag isn't
|
|
// found, pbData and cbData are set to 0. If a STEP_INTO value is
|
|
// indefinite-length encoded, cbData is set to CMSG_INDEFINITE_LENGTH.
|
|
// If ASN1UTIL_DEFINITE_LENGTH_FLAG is set, then, all returned lengths
|
|
// are definite-length, ie, CMSG_INDEFINITE_LENGTH is never returned.
|
|
//
|
|
// If ASN1UTIL_RETURN_VALUE_BLOB_FLAG is set, pbData points to
|
|
// the tag. cbData includes the tag, length and content octets.
|
|
//
|
|
// If ASN1UTIL_RETURN_CONTENT_BLOB_FLAG is set, pbData points to the content
|
|
// octets. cbData includes only the content octets.
|
|
//
|
|
// If neither BLOB_FLAG is set, rgValueBlob[] isn't updated.
|
|
//--------------------------------------------------------------------------
|
|
LONG
|
|
WINAPI
|
|
Asn1UtilExtractValues(
|
|
IN const BYTE *pbEncoded,
|
|
IN DWORD cbEncoded,
|
|
IN DWORD dwFlags,
|
|
IN OUT DWORD *pcValue,
|
|
IN const ASN1UTIL_EXTRACT_VALUE_PARA *rgValuePara,
|
|
OUT OPTIONAL PCRYPT_DER_BLOB rgValueBlob
|
|
)
|
|
{
|
|
DWORD cValue = *pcValue;
|
|
const BYTE *pb = pbEncoded;
|
|
DWORD cb = cbEncoded;
|
|
|
|
DWORD iValue;
|
|
LONG lAllValues;
|
|
|
|
for (iValue = 0; iValue < cValue; iValue++) {
|
|
DWORD dwParaFlags = rgValuePara[iValue].dwFlags;
|
|
DWORD dwOp = dwParaFlags & ASN1UTIL_MASK_VALUE_OP;
|
|
const BYTE *pbParaTag = rgValuePara[iValue].rgbTag;
|
|
BOOL fValueBlob = (dwParaFlags & (ASN1UTIL_RETURN_VALUE_BLOB_FLAG |
|
|
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG)) && rgValueBlob;
|
|
|
|
LONG lTagLength;
|
|
DWORD cbContent;
|
|
const BYTE *pbContent;
|
|
DWORD cbValue;
|
|
|
|
if (0 == cb) {
|
|
if (ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP != dwOp)
|
|
goto TooLittleData;
|
|
if (fValueBlob) {
|
|
rgValueBlob[iValue].pbData = NULL;
|
|
rgValueBlob[iValue].cbData = 0;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Assumption: single byte tag for doing comparison
|
|
if (pbParaTag) {
|
|
// Check if the encoded tag matches one of the expected tags
|
|
|
|
BYTE bEncodedTag;
|
|
BYTE bParaTag;
|
|
|
|
bEncodedTag = *pb;
|
|
while ((bParaTag = *pbParaTag) && bParaTag != bEncodedTag)
|
|
pbParaTag++;
|
|
|
|
if (0 == bParaTag) {
|
|
if (ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP != dwOp)
|
|
goto InvalidTag;
|
|
if (fValueBlob) {
|
|
rgValueBlob[iValue].pbData = NULL;
|
|
rgValueBlob[iValue].cbData = 0;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
lTagLength = Asn1UtilExtractContent(
|
|
pb,
|
|
cb,
|
|
&cbContent,
|
|
&pbContent
|
|
);
|
|
if (0 >= lTagLength || (DWORD) lTagLength > cb)
|
|
goto InvalidTagOrLength;
|
|
|
|
if (CMSG_INDEFINITE_LENGTH == cbContent) {
|
|
if (ASN1UTIL_STEP_INTO_VALUE_OP != dwOp)
|
|
goto UnsupportedIndefiniteLength;
|
|
else if (fValueBlob && (dwFlags & ASN1UTIL_DEFINITE_LENGTH_FLAG))
|
|
goto NotAllowedIndefiniteLength;
|
|
cbValue = CMSG_INDEFINITE_LENGTH;
|
|
} else {
|
|
cbValue = cbContent + lTagLength;
|
|
if (cbValue > cb)
|
|
goto TooLittleData;
|
|
}
|
|
|
|
if (fValueBlob) {
|
|
if (dwParaFlags & ASN1UTIL_RETURN_CONTENT_BLOB_FLAG) {
|
|
rgValueBlob[iValue].pbData = (BYTE *) pbContent;
|
|
rgValueBlob[iValue].cbData = cbContent;
|
|
} else if (dwParaFlags & ASN1UTIL_RETURN_VALUE_BLOB_FLAG) {
|
|
rgValueBlob[iValue].pbData = (BYTE *) pb;
|
|
rgValueBlob[iValue].cbData = cbValue;
|
|
}
|
|
}
|
|
|
|
switch (dwOp) {
|
|
case ASN1UTIL_STEP_INTO_VALUE_OP:
|
|
pb += lTagLength;
|
|
cb -= lTagLength;
|
|
break;
|
|
case ASN1UTIL_STEP_OVER_VALUE_OP:
|
|
case ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP:
|
|
pb += cbValue;
|
|
cb -= cbValue;
|
|
break;
|
|
default:
|
|
goto InvalidArg;
|
|
|
|
}
|
|
}
|
|
|
|
lAllValues = (LONG)(pb - pbEncoded);
|
|
assert((DWORD) lAllValues <= cbEncoded);
|
|
|
|
CommonReturn:
|
|
*pcValue = iValue;
|
|
return lAllValues;
|
|
|
|
ErrorReturn:
|
|
lAllValues = -((LONG)(pb - pbEncoded)) - 1;
|
|
goto CommonReturn;
|
|
|
|
SET_ERROR(TooLittleData, ERROR_INVALID_DATA)
|
|
SET_ERROR(InvalidTag, ERROR_INVALID_DATA)
|
|
SET_ERROR(InvalidTagOrLength, ERROR_INVALID_DATA)
|
|
SET_ERROR(InvalidArg, E_INVALIDARG)
|
|
SET_ERROR(UnsupportedIndefiniteLength, ERROR_INVALID_DATA)
|
|
SET_ERROR(NotAllowedIndefiniteLength, ERROR_INVALID_DATA)
|
|
}
|
|
|
|
static const BYTE rgbSeqTag[] = {ASN1UTIL_TAG_SEQ, 0};
|
|
static const BYTE rgbSetTag[] = {ASN1UTIL_TAG_SET, 0};
|
|
static const BYTE rgbOIDTag[] = {ASN1UTIL_TAG_OID, 0};
|
|
static const BYTE rgbIntegerTag[] = {ASN1UTIL_TAG_INTEGER, 0};
|
|
static const BYTE rgbBitStringTag[] = {ASN1UTIL_TAG_BITSTRING, 0};
|
|
static const BYTE rgbConstructedContext0Tag[] =
|
|
{ASN1UTIL_TAG_CONSTRUCTED_CONTEXT_0, 0};
|
|
|
|
static const ASN1UTIL_EXTRACT_VALUE_PARA rgExtractSignedDataContentPara[] = {
|
|
// 0 - ContentInfo ::= SEQUENCE {
|
|
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
|
|
// 1 - contentType ContentType,
|
|
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG |
|
|
ASN1UTIL_STEP_OVER_VALUE_OP, rgbOIDTag,
|
|
// 2 - content [0] EXPLICIT ANY -- OPTIONAL
|
|
ASN1UTIL_STEP_INTO_VALUE_OP, rgbConstructedContext0Tag,
|
|
// 3 - SignedData ::= SEQUENCE {
|
|
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
|
|
// 4 - version INTEGER,
|
|
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG |
|
|
ASN1UTIL_STEP_OVER_VALUE_OP, rgbIntegerTag,
|
|
// 5 - digestAlgorithms DigestAlgorithmIdentifiers,
|
|
ASN1UTIL_STEP_OVER_VALUE_OP, rgbSetTag,
|
|
// 6 - ContentInfo ::= SEQUENCE {
|
|
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG |
|
|
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
|
|
// 7 - contentType ContentType,
|
|
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG |
|
|
ASN1UTIL_STEP_OVER_VALUE_OP, rgbOIDTag,
|
|
// 8 - content [0] EXPLICIT ANY -- OPTIONAL
|
|
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG |
|
|
ASN1UTIL_STEP_INTO_VALUE_OP, rgbConstructedContext0Tag,
|
|
};
|
|
|
|
#define SIGNED_DATA_CONTENT_OUTER_OID_VALUE_INDEX 1
|
|
#define SIGNED_DATA_CONTENT_VERSION_VALUE_INDEX 4
|
|
#define SIGNED_DATA_CONTENT_INNER_OID_VALUE_INDEX 7
|
|
#define SIGNED_DATA_CONTENT_INFO_SEQ_VALUE_INDEX 6
|
|
#define SIGNED_DATA_CONTENT_CONTEXT_0_VALUE_INDEX 8
|
|
#define SIGNED_DATA_CONTENT_VALUE_COUNT \
|
|
(sizeof(rgExtractSignedDataContentPara) / \
|
|
sizeof(rgExtractSignedDataContentPara[0]))
|
|
|
|
static const ASN1UTIL_EXTRACT_VALUE_PARA rgExtractCertSignedContent[] = {
|
|
// 0 - SignedContent ::= SEQUENCE {
|
|
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
|
|
// 1 - toBeSigned NOCOPYANY,
|
|
ASN1UTIL_RETURN_VALUE_BLOB_FLAG |
|
|
ASN1UTIL_STEP_OVER_VALUE_OP, NULL,
|
|
// 2 - algorithm AlgorithmIdentifier,
|
|
ASN1UTIL_STEP_OVER_VALUE_OP, rgbSeqTag,
|
|
// 3 - signature BITSTRING
|
|
ASN1UTIL_STEP_OVER_VALUE_OP, rgbBitStringTag,
|
|
};
|
|
|
|
#define CERT_TO_BE_SIGNED_VALUE_INDEX 1
|
|
#define CERT_SIGNED_CONTENT_VALUE_COUNT \
|
|
(sizeof(rgExtractCertSignedContent) / \
|
|
sizeof(rgExtractCertSignedContent[0]))
|
|
|
|
static const ASN1UTIL_EXTRACT_VALUE_PARA rgExtractCertPublicKeyInfo[] = {
|
|
// 0 - SignedContent ::= SEQUENCE {
|
|
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
|
|
// 1 - CertificateToBeSigned ::= SEQUENCE {
|
|
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
|
|
// 2 - version [0] CertificateVersion DEFAULT v1,
|
|
ASN1UTIL_OPTIONAL_STEP_OVER_VALUE_OP, rgbConstructedContext0Tag,
|
|
// 3 - serialNumber CertificateSerialNumber,
|
|
ASN1UTIL_STEP_OVER_VALUE_OP, rgbIntegerTag,
|
|
// 4 - signature AlgorithmIdentifier,
|
|
ASN1UTIL_STEP_OVER_VALUE_OP, rgbSeqTag,
|
|
// 5 - issuer NOCOPYANY, -- really Name
|
|
ASN1UTIL_STEP_OVER_VALUE_OP, rgbSeqTag,
|
|
// 6 - validity Validity,
|
|
ASN1UTIL_STEP_OVER_VALUE_OP, rgbSeqTag,
|
|
// 7 - subject NOCOPYANY, -- really Name
|
|
ASN1UTIL_STEP_OVER_VALUE_OP, rgbSeqTag,
|
|
// 8 - subjectPublicKeyInfo SubjectPublicKeyInfo,
|
|
ASN1UTIL_RETURN_VALUE_BLOB_FLAG |
|
|
ASN1UTIL_STEP_OVER_VALUE_OP, rgbSeqTag
|
|
};
|
|
|
|
#define CERT_PUBLIC_KEY_INFO_VALUE_INDEX 8
|
|
#define CERT_PUBLIC_KEY_INFO_VALUE_COUNT \
|
|
(sizeof(rgExtractCertPublicKeyInfo) / \
|
|
sizeof(rgExtractCertPublicKeyInfo[0]))
|
|
|
|
|
|
// #define szOID_RSA_signedData "1.2.840.113549.1.7.2"
|
|
static const BYTE rgbOIDSignedData[] =
|
|
{0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02};
|
|
|
|
static const CRYPT_DER_BLOB EncodedOIDSignedData = {
|
|
sizeof(rgbOIDSignedData), (BYTE *) rgbOIDSignedData
|
|
};
|
|
|
|
#ifdef CMS_PKCS7
|
|
|
|
// #define szOID_RSA_Data "1.2.840.113549.1.7.1"
|
|
static const BYTE rgbOIDData[] =
|
|
{0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01};
|
|
|
|
static const CRYPT_DER_BLOB EncodedOIDData = {
|
|
sizeof(rgbOIDData), (BYTE *) rgbOIDData
|
|
};
|
|
|
|
#endif // CMS_PKCS7
|
|
|
|
|
|
// The encoded OID only includes the content octets. Excludes the tag and
|
|
// length octets.
|
|
static BOOL CompareEncodedOID(
|
|
IN const CRYPT_DER_BLOB *pEncodedOID1,
|
|
IN const CRYPT_DER_BLOB *pEncodedOID2
|
|
)
|
|
{
|
|
if (pEncodedOID1->cbData == pEncodedOID2->cbData &&
|
|
0 == memcmp(pEncodedOID1->pbData, pEncodedOID2->pbData,
|
|
pEncodedOID1->cbData))
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Skips past PKCS7 ASN.1 encoded values to get to the SignedData content.
|
|
//
|
|
// Checks that the outer ContentType has the SignedData OID and optionally
|
|
// checks the inner SignedData content's ContentType.
|
|
//
|
|
// Returns:
|
|
// success - the number of bytes skipped, >=0
|
|
// failure - <0
|
|
//
|
|
// If the SignedData content is indefinite-length encoded,
|
|
// *pcbContent is set to CMSG_INDEFINITE_LENGTH
|
|
//--------------------------------------------------------------------------
|
|
LONG
|
|
WINAPI
|
|
Asn1UtilExtractPKCS7SignedDataContent(
|
|
IN const BYTE *pbEncoded,
|
|
IN DWORD cbEncoded,
|
|
IN OPTIONAL const CRYPT_DER_BLOB *pEncodedInnerOID,
|
|
OUT DWORD *pcbContent,
|
|
OUT const BYTE **ppbContent
|
|
)
|
|
{
|
|
LONG lSkipped;
|
|
DWORD cValue;
|
|
CRYPT_DER_BLOB rgValueBlob[SIGNED_DATA_CONTENT_VALUE_COUNT];
|
|
|
|
DWORD cbContent;
|
|
const BYTE *pbContent;
|
|
DWORD cbSeq;
|
|
const BYTE *pbSeq;
|
|
|
|
cValue = SIGNED_DATA_CONTENT_VALUE_COUNT;
|
|
if (0 >= (lSkipped = Asn1UtilExtractValues(
|
|
pbEncoded,
|
|
cbEncoded,
|
|
0, // dwFlags
|
|
&cValue,
|
|
rgExtractSignedDataContentPara,
|
|
rgValueBlob
|
|
)))
|
|
goto ExtractValuesError;
|
|
|
|
pbContent = rgValueBlob[SIGNED_DATA_CONTENT_CONTEXT_0_VALUE_INDEX].pbData;
|
|
cbContent = rgValueBlob[SIGNED_DATA_CONTENT_CONTEXT_0_VALUE_INDEX].cbData;
|
|
|
|
// For definite-length encoded, check that the content wasn't
|
|
// omitted.
|
|
//
|
|
// Note, for indefinite-length encoding, if the content was omitted,
|
|
// we would have had a 0 tag instead of the CONTEXT_0 tag.
|
|
cbSeq = rgValueBlob[SIGNED_DATA_CONTENT_INFO_SEQ_VALUE_INDEX].cbData;
|
|
pbSeq = rgValueBlob[SIGNED_DATA_CONTENT_INFO_SEQ_VALUE_INDEX].pbData;
|
|
if (CMSG_INDEFINITE_LENGTH != cbSeq && pbContent >= (pbSeq + cbSeq))
|
|
goto NoSignedDataError;
|
|
|
|
#ifdef CMS_PKCS7
|
|
// For V3 SignedData, non Data types are wrapped, encapsulated with a
|
|
// OCTET string
|
|
if (1 == rgValueBlob[SIGNED_DATA_CONTENT_VERSION_VALUE_INDEX].cbData &&
|
|
CMSG_SIGNED_DATA_V3 <=
|
|
*(rgValueBlob[SIGNED_DATA_CONTENT_VERSION_VALUE_INDEX].pbData)
|
|
&&
|
|
!CompareEncodedOID(
|
|
&rgValueBlob[SIGNED_DATA_CONTENT_INNER_OID_VALUE_INDEX],
|
|
&EncodedOIDData)
|
|
&&
|
|
0 != cbContent && ASN1UTIL_TAG_OCTETSTRING == *pbContent
|
|
) {
|
|
LONG lTagLength;
|
|
const BYTE *pbInner;
|
|
DWORD cbInner;
|
|
|
|
// Advance past the outer OCTET wrapper
|
|
lTagLength = Asn1UtilExtractContent(
|
|
pbContent,
|
|
cbEncoded - lSkipped,
|
|
&cbInner,
|
|
&pbInner
|
|
);
|
|
if (0 < lTagLength) {
|
|
lSkipped += lTagLength;
|
|
cbContent = cbInner;
|
|
pbContent = pbInner;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (CMSG_INDEFINITE_LENGTH == cbContent) {
|
|
// Extract the pbContent and attempt to get its definite length
|
|
|
|
LONG lTagLength;
|
|
const BYTE *pbInner;
|
|
DWORD cbInner;
|
|
|
|
lTagLength = Asn1UtilExtractContent(
|
|
pbContent,
|
|
cbEncoded - lSkipped,
|
|
&cbInner,
|
|
&pbInner
|
|
);
|
|
if (0 < lTagLength && CMSG_INDEFINITE_LENGTH != cbInner)
|
|
cbContent = cbInner + lTagLength;
|
|
}
|
|
|
|
// Verify that the outer ContentType is SignedData and the inner
|
|
// ContentType is the specified type
|
|
if (!CompareEncodedOID(
|
|
&rgValueBlob[SIGNED_DATA_CONTENT_OUTER_OID_VALUE_INDEX],
|
|
&EncodedOIDSignedData
|
|
))
|
|
goto NotSignedDataContentType;
|
|
if (pEncodedInnerOID && !CompareEncodedOID(
|
|
&rgValueBlob[SIGNED_DATA_CONTENT_INNER_OID_VALUE_INDEX],
|
|
pEncodedInnerOID
|
|
))
|
|
goto UnexpectedInnerContentTypeError;
|
|
|
|
*pcbContent = cbContent;
|
|
*ppbContent = pbContent;
|
|
|
|
CommonReturn:
|
|
return lSkipped;
|
|
|
|
ErrorReturn:
|
|
if (0 <= lSkipped)
|
|
lSkipped = -lSkipped - 1;
|
|
*pcbContent = 0;
|
|
*ppbContent = NULL;
|
|
goto CommonReturn;
|
|
|
|
TRACE_ERROR(ExtractValuesError)
|
|
SET_ERROR(NoSignedDataError, ERROR_INVALID_DATA)
|
|
SET_ERROR(NotSignedDataContentType, ERROR_INVALID_DATA)
|
|
SET_ERROR(UnexpectedInnerContentTypeError, ERROR_INVALID_DATA)
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Verifies this is a certificate ASN.1 encoded signed content.
|
|
// Returns the pointer to and length of the ToBeSigned content.
|
|
//
|
|
// Returns an error if the ToBeSigned content isn't definite length
|
|
// encoded.
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
WINAPI
|
|
Asn1UtilExtractCertificateToBeSignedContent(
|
|
IN const BYTE *pbEncoded,
|
|
IN DWORD cbEncoded,
|
|
OUT DWORD *pcbContent,
|
|
OUT const BYTE **ppbContent
|
|
)
|
|
{
|
|
BOOL fResult;
|
|
DWORD cValue;
|
|
CRYPT_DER_BLOB rgValueBlob[CERT_SIGNED_CONTENT_VALUE_COUNT];
|
|
|
|
cValue = CERT_SIGNED_CONTENT_VALUE_COUNT;
|
|
if (0 >= Asn1UtilExtractValues(
|
|
pbEncoded,
|
|
cbEncoded,
|
|
ASN1UTIL_DEFINITE_LENGTH_FLAG,
|
|
&cValue,
|
|
rgExtractCertSignedContent,
|
|
rgValueBlob
|
|
))
|
|
goto ExtractValuesError;
|
|
|
|
*ppbContent = rgValueBlob[CERT_TO_BE_SIGNED_VALUE_INDEX].pbData;
|
|
*pcbContent = rgValueBlob[CERT_TO_BE_SIGNED_VALUE_INDEX].cbData;
|
|
|
|
fResult = TRUE;
|
|
CommonReturn:
|
|
return fResult;
|
|
|
|
ErrorReturn:
|
|
fResult = FALSE;
|
|
*ppbContent = NULL;
|
|
*pcbContent = 0;
|
|
goto CommonReturn;
|
|
|
|
TRACE_ERROR(ExtractValuesError)
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Returns the pointer to and length of the SubjectPublicKeyInfo value in
|
|
// a signed and encoded X.509 certificate.
|
|
//
|
|
// Returns an error if the value isn't definite length encoded.
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
WINAPI
|
|
Asn1UtilExtractCertificatePublicKeyInfo(
|
|
IN const BYTE *pbEncoded,
|
|
IN DWORD cbEncoded,
|
|
OUT DWORD *pcbPublicKeyInfo,
|
|
OUT const BYTE **ppbPublicKeyInfo
|
|
)
|
|
{
|
|
BOOL fResult;
|
|
DWORD cValue;
|
|
CRYPT_DER_BLOB rgValueBlob[CERT_PUBLIC_KEY_INFO_VALUE_COUNT];
|
|
|
|
cValue = CERT_PUBLIC_KEY_INFO_VALUE_COUNT;
|
|
if (0 >= Asn1UtilExtractValues(
|
|
pbEncoded,
|
|
cbEncoded,
|
|
ASN1UTIL_DEFINITE_LENGTH_FLAG,
|
|
&cValue,
|
|
rgExtractCertPublicKeyInfo,
|
|
rgValueBlob
|
|
))
|
|
goto ExtractValuesError;
|
|
|
|
*ppbPublicKeyInfo = rgValueBlob[CERT_PUBLIC_KEY_INFO_VALUE_INDEX].pbData;
|
|
*pcbPublicKeyInfo = rgValueBlob[CERT_PUBLIC_KEY_INFO_VALUE_INDEX].cbData;
|
|
|
|
fResult = TRUE;
|
|
CommonReturn:
|
|
return fResult;
|
|
|
|
ErrorReturn:
|
|
fResult = FALSE;
|
|
*ppbPublicKeyInfo = NULL;
|
|
*pcbPublicKeyInfo = 0;
|
|
goto CommonReturn;
|
|
|
|
TRACE_ERROR(ExtractValuesError)
|
|
}
|
|
|
|
|
|
// Special RDN containing the KEY_ID. Its value type is CERT_RDN_OCTET_STRING.
|
|
//#define szOID_KEYID_RDN "1.3.6.1.4.1.311.10.7.1"
|
|
static const BYTE rgbOIDKeyIdRDN[] =
|
|
{0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x0A, 0x07, 0x01};
|
|
|
|
static const CRYPT_DER_BLOB EncodedOIDKeyIdRDN = {
|
|
sizeof(rgbOIDKeyIdRDN), (BYTE *) rgbOIDKeyIdRDN
|
|
};
|
|
|
|
static const BYTE rgbOctetStringTag[] = {ASN1UTIL_TAG_OCTETSTRING, 0};
|
|
|
|
static const ASN1UTIL_EXTRACT_VALUE_PARA rgExtractNameKeyIdRDNPara[] = {
|
|
// 0 - Name ::= SEQUENCE OF RelativeDistinguishedName
|
|
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
|
|
// 1 - RelativeDistinguishedName ::= SET OF AttributeTypeValue
|
|
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSetTag,
|
|
// 2 - AttributeTypeValue ::= SEQUENCE
|
|
ASN1UTIL_STEP_INTO_VALUE_OP, rgbSeqTag,
|
|
// 3 - type EncodedObjectID,
|
|
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG |
|
|
ASN1UTIL_STEP_OVER_VALUE_OP, rgbOIDTag,
|
|
// 4 - value NOCOPYANY
|
|
ASN1UTIL_RETURN_CONTENT_BLOB_FLAG |
|
|
ASN1UTIL_STEP_OVER_VALUE_OP, rgbOctetStringTag,
|
|
};
|
|
|
|
#define NAME_KEYID_RDN_OID_VALUE_INDEX 3
|
|
#define NAME_KEYID_RDN_OCTET_VALUE_INDEX 4
|
|
#define NAME_KEYID_RDN_VALUE_COUNT \
|
|
(sizeof(rgExtractNameKeyIdRDNPara) / \
|
|
sizeof(rgExtractNameKeyIdRDNPara[0]))
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// If the Issuer and SerialNumber in the CERT_INFO contains a special
|
|
// KeyID RDN attribute returns TRUE with pKeyId's cbData and pbData updated
|
|
// with the RDN attribute's OCTET_STRING value. Otherwise, returns FALSE.
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
WINAPI
|
|
Asn1UtilExtractKeyIdFromCertInfo(
|
|
IN PCERT_INFO pCertInfo,
|
|
OUT PCRYPT_HASH_BLOB pKeyId
|
|
)
|
|
{
|
|
DWORD cValue;
|
|
CRYPT_DER_BLOB rgValueBlob[NAME_KEYID_RDN_VALUE_COUNT];
|
|
|
|
if (0 == pCertInfo->SerialNumber.cbData ||
|
|
0 != *pCertInfo->SerialNumber.pbData)
|
|
goto NoKeyId;
|
|
|
|
cValue = NAME_KEYID_RDN_VALUE_COUNT;
|
|
if (0 > Asn1UtilExtractValues(
|
|
pCertInfo->Issuer.pbData,
|
|
pCertInfo->Issuer.cbData,
|
|
0, // dwFlags
|
|
&cValue,
|
|
rgExtractNameKeyIdRDNPara,
|
|
rgValueBlob
|
|
))
|
|
goto NoKeyId;
|
|
|
|
if (!CompareEncodedOID(
|
|
&rgValueBlob[NAME_KEYID_RDN_OID_VALUE_INDEX],
|
|
&EncodedOIDKeyIdRDN))
|
|
goto NoKeyId;
|
|
|
|
if (CMSG_INDEFINITE_LENGTH ==
|
|
rgValueBlob[NAME_KEYID_RDN_OCTET_VALUE_INDEX].cbData)
|
|
goto NoKeyId;
|
|
|
|
pKeyId->pbData = rgValueBlob[NAME_KEYID_RDN_OCTET_VALUE_INDEX].pbData;
|
|
pKeyId->cbData = rgValueBlob[NAME_KEYID_RDN_OCTET_VALUE_INDEX].cbData;
|
|
return TRUE;
|
|
|
|
NoKeyId:
|
|
pKeyId->pbData = NULL;
|
|
pKeyId->cbData = 0;
|
|
return FALSE;
|
|
}
|