windows-nt/Source/XPSP1/NT/ds/security/cryptoapi/mincrypt/lib/verdata.cpp

362 lines
12 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+-------------------------------------------------------------------------
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 2001 - 2001
//
// File: verdata.cpp
//
// Contents: Minimal Cryptographic functions to verify PKCS #7 Signed Data
// message
//
//
// Functions: MinCryptVerifySignedData
//
// History: 19-Jan-01 philh created
//--------------------------------------------------------------------------
#include "global.hxx"
#define MAX_SIGNED_DATA_CERT_CNT 10
#define MAX_SIGNED_DATA_AUTH_ATTR_CNT 10
// #define szOID_RSA_signedData "1.2.840.113549.1.7.2"
const BYTE rgbOID_RSA_signedData[] =
{0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02};
// #define szOID_RSA_messageDigest "1.2.840.113549.1.9.4"
const BYTE rgbOID_RSA_messageDigest[] =
{0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x09, 0x04};
const CRYPT_DER_BLOB RSA_messageDigestEncodedOIDBlob = {
sizeof(rgbOID_RSA_messageDigest),
(BYTE *) rgbOID_RSA_messageDigest
};
PCRYPT_DER_BLOB
WINAPI
I_MinCryptFindSignerCertificateByIssuerAndSerialNumber(
IN PCRYPT_DER_BLOB pIssuerNameValueBlob,
IN PCRYPT_DER_BLOB pIssuerSerialNumberContentBlob,
IN DWORD cCert,
IN CRYPT_DER_BLOB rgrgCertBlob[][MINASN1_CERT_BLOB_CNT]
)
{
DWORD i;
const BYTE *pbName = pIssuerNameValueBlob->pbData;
DWORD cbName = pIssuerNameValueBlob->cbData;
const BYTE *pbSerial = pIssuerSerialNumberContentBlob->pbData;
DWORD cbSerial = pIssuerSerialNumberContentBlob->cbData;
if (0 == cbName || 0 == cbSerial)
return NULL;
for (i = 0; i < cCert; i++) {
PCRYPT_DER_BLOB rgCert = rgrgCertBlob[i];
if (cbName == rgCert[MINASN1_CERT_ISSUER_IDX].cbData &&
cbSerial == rgCert[MINASN1_CERT_SERIAL_NUMBER_IDX].cbData
&&
0 == memcmp(pbSerial,
rgCert[MINASN1_CERT_SERIAL_NUMBER_IDX].pbData,
cbSerial)
&&
0 == memcmp(pbName,
rgCert[MINASN1_CERT_ISSUER_IDX].pbData,
cbName))
return rgCert;
}
return NULL;
}
// Verifies that the input hash matches the
// szOID_RSA_messageDigest ("1.2.840.113549.1.9.4") authenticated attribute.
//
// Replaces the input hash with a hash of the authenticated attributes.
LONG
WINAPI
I_MinCryptVerifySignerAuthenticatedAttributes(
IN ALG_ID HashAlgId,
IN OUT BYTE rgbHash[MINCRYPT_MAX_HASH_LEN],
IN OUT DWORD *pcbHash,
IN PCRYPT_DER_BLOB pAttrsValueBlob
)
{
LONG lErr;
DWORD cAttr;
CRYPT_DER_BLOB rgrgAttrBlob[MAX_SIGNED_DATA_AUTH_ATTR_CNT][MINASN1_ATTR_BLOB_CNT];
PCRYPT_DER_BLOB rgDigestAuthAttr;
const BYTE *pbDigestAuthValue;
DWORD cbDigestAuthValue;
CRYPT_DER_BLOB rgAuthHashBlob[2];
const BYTE bTagSet = MINASN1_TAG_SET;
// Parse the authenticated attributes
cAttr = MAX_SIGNED_DATA_AUTH_ATTR_CNT;
if (0 >= MinAsn1ParseAttributes(
pAttrsValueBlob,
&cAttr,
rgrgAttrBlob) || 0 == cAttr)
goto MissingAuthAttrs;
// Find the szOID_RSA_messageDigest ("1.2.840.113549.1.9.4")
// attribute value
rgDigestAuthAttr = MinAsn1FindAttribute(
(PCRYPT_DER_BLOB) &RSA_messageDigestEncodedOIDBlob,
cAttr,
rgrgAttrBlob
);
if (NULL == rgDigestAuthAttr)
goto MissingDigestAuthAttr;
// Skip past the digest's outer OCTET tag and length octets
if (0 >= MinAsn1ExtractContent(
rgDigestAuthAttr[MINASN1_ATTR_VALUE_IDX].pbData,
rgDigestAuthAttr[MINASN1_ATTR_VALUE_IDX].cbData,
&cbDigestAuthValue,
&pbDigestAuthValue
))
goto InvalidDigestAuthAttr;
// Check that the authenticated digest bytes match the input
// content hash.
if (*pcbHash != cbDigestAuthValue ||
0 != memcmp(rgbHash, pbDigestAuthValue, cbDigestAuthValue))
goto InvalidContentHash;
// Hash the authenticated attributes. This hash will be compared against
// the decrypted signature.
// Note, the authenticated attributes "[0] Implicit" tag needs to be changed
// to a "SET OF" tag before doing the hash.
rgAuthHashBlob[0].pbData = (BYTE *) &bTagSet;
rgAuthHashBlob[0].cbData = 1;
assert(0 < pAttrsValueBlob->cbData);
rgAuthHashBlob[1].pbData = pAttrsValueBlob->pbData + 1;
rgAuthHashBlob[1].cbData = pAttrsValueBlob->cbData - 1;
lErr = MinCryptHashMemory(
HashAlgId,
2, // cBlob
rgAuthHashBlob,
rgbHash,
pcbHash
);
CommonReturn:
return lErr;
MissingAuthAttrs:
MissingDigestAuthAttr:
InvalidDigestAuthAttr:
lErr = CRYPT_E_AUTH_ATTR_MISSING;
goto CommonReturn;
InvalidContentHash:
lErr = CRYPT_E_HASH_VALUE;
goto CommonReturn;
}
//+-------------------------------------------------------------------------
// Function: MinCryptVerifySignedData
//
// Verifies an ASN.1 encoded PKCS #7 Signed Data Message.
//
// Assumes the PKCS #7 message is definite length encoded.
// Assumes PKCS #7 version 1.5, ie, not the newer CMS version.
// We only look at the first signer.
//
// The Signed Data message is parsed. Its signature is verified. Its
// signer certificate chain is verified to a baked in root public key.
//
// If the Signed Data was successfully verified, ERROR_SUCCESS is returned.
// Otherwise, a nonzero error code is returned.
//
// Here are some interesting errors that can be returned:
// CRYPT_E_BAD_MSG - unable to ASN1 parse as a signed data message
// ERROR_NO_DATA - the content is empty
// CRYPT_E_NO_SIGNER - not signed or unable to find signer cert
// CRYPT_E_UNKNOWN_ALGO- unknown MD5 or SHA1 ASN.1 algorithm identifier
// CERT_E_UNTRUSTEDROOT- the signer chain's root wasn't baked in
// CERT_E_CHAINING - unable to build signer chain to a root
// CRYPT_E_AUTH_ATTR_MISSING - missing digest authenticated attribute
// CRYPT_E_HASH_VALUE - content hash != authenticated digest attribute
// NTE_BAD_ALGID - unsupported hash or public key algorithm
// NTE_BAD_PUBLIC_KEY - not a valid RSA public key
// NTE_BAD_SIGNATURE - bad PKCS #7 or signer chain signature
//
// The rgVerSignedDataBlob[] is updated with pointer to and length of the
// following fields in the encoded PKCS #7 message.
//--------------------------------------------------------------------------
LONG
WINAPI
MinCryptVerifySignedData(
IN const BYTE *pbEncoded,
IN DWORD cbEncoded,
OUT CRYPT_DER_BLOB rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_BLOB_CNT]
)
{
LONG lErr;
CRYPT_DER_BLOB rgParseSignedDataBlob[MINASN1_SIGNED_DATA_BLOB_CNT];
DWORD cCert;
CRYPT_DER_BLOB rgrgCertBlob[MAX_SIGNED_DATA_CERT_CNT][MINASN1_CERT_BLOB_CNT];
PCRYPT_DER_BLOB rgSignerCert;
ALG_ID HashAlgId;
BYTE rgbHash[MINCRYPT_MAX_HASH_LEN];
DWORD cbHash;
CRYPT_DER_BLOB ContentBlob;
memset(rgVerSignedDataBlob, 0,
sizeof(CRYPT_DER_BLOB) * MINCRYPT_VER_SIGNED_DATA_BLOB_CNT);
// Parse the message and verify that it's ASN.1 PKCS #7 SignedData
if (0 >= MinAsn1ParseSignedData(
pbEncoded,
cbEncoded,
rgParseSignedDataBlob
))
goto ParseSignedDataError;
// Only support szOID_RSA_signedData - "1.2.840.113549.1.7.2"
if (sizeof(rgbOID_RSA_signedData) !=
rgParseSignedDataBlob[MINASN1_SIGNED_DATA_OUTER_OID_IDX].cbData
||
0 != memcmp(rgbOID_RSA_signedData,
rgParseSignedDataBlob[MINASN1_SIGNED_DATA_OUTER_OID_IDX].pbData,
sizeof(rgbOID_RSA_signedData)))
goto NotSignedDataOID;
// Verify this isn't an empty SignedData message
if (0 == rgParseSignedDataBlob[MINASN1_SIGNED_DATA_CONTENT_OID_IDX].cbData
||
0 == rgParseSignedDataBlob[MINASN1_SIGNED_DATA_CONTENT_DATA_IDX].cbData)
goto NoContent;
rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_CONTENT_OID_IDX] =
rgParseSignedDataBlob[MINASN1_SIGNED_DATA_CONTENT_OID_IDX];
rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_CONTENT_DATA_IDX] =
rgParseSignedDataBlob[MINASN1_SIGNED_DATA_CONTENT_DATA_IDX];
// Check that the message has a signer
if (0 == rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_ENCODED_IDX].cbData)
goto NoSigner;
// Get the message's bag of certs
cCert = MAX_SIGNED_DATA_CERT_CNT;
if (0 >= MinAsn1ParseSignedDataCertificates(
&rgParseSignedDataBlob[MINASN1_SIGNED_DATA_CERTS_IDX],
&cCert,
rgrgCertBlob
) || 0 == cCert)
goto NoCerts;
// Get the signer certificate
rgSignerCert = I_MinCryptFindSignerCertificateByIssuerAndSerialNumber(
&rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_ISSUER_IDX],
&rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_SERIAL_NUMBER_IDX],
cCert,
rgrgCertBlob
);
if (NULL == rgSignerCert)
goto NoSignerCert;
rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_SIGNER_CERT_IDX] =
rgSignerCert[MINASN1_CERT_ENCODED_IDX];
rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_AUTH_ATTRS_IDX] =
rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_AUTH_ATTRS_IDX];
rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_UNAUTH_ATTRS_IDX] =
rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_UNAUTH_ATTRS_IDX];
// Verify the signer certificate up to a baked in, trusted root
lErr = MinCryptVerifyCertificate(
rgSignerCert,
cCert,
rgrgCertBlob
);
if (ERROR_SUCCESS != lErr)
goto ErrorReturn;
// Hash the message's content octets according to the signer's hash
// algorithm
HashAlgId = MinCryptDecodeHashAlgorithmIdentifier(
&rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_DIGEST_ALGID_IDX]
);
if (0 == HashAlgId)
goto UnknownHashAlgId;
// Note, the content's tag and length octets aren't included in the hash
if (0 >= MinAsn1ExtractContent(
rgParseSignedDataBlob[MINASN1_SIGNED_DATA_CONTENT_DATA_IDX].pbData,
rgParseSignedDataBlob[MINASN1_SIGNED_DATA_CONTENT_DATA_IDX].cbData,
&ContentBlob.cbData,
(const BYTE **) &ContentBlob.pbData
))
goto InvalidContent;
lErr = MinCryptHashMemory(
HashAlgId,
1, // cBlob
&ContentBlob,
rgbHash,
&cbHash
);
if (ERROR_SUCCESS != lErr)
goto ErrorReturn;
// If we have authenticated attributes, then, need to compare the
// above hash with the szOID_RSA_messageDigest ("1.2.840.113549.1.9.4")
// attribute value. After a successful comparison, the above hash
// is replaced with a hash of the authenticated attributes.
if (0 != rgParseSignedDataBlob[
MINASN1_SIGNED_DATA_SIGNER_INFO_AUTH_ATTRS_IDX].cbData) {
lErr = I_MinCryptVerifySignerAuthenticatedAttributes(
HashAlgId,
rgbHash,
&cbHash,
&rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_AUTH_ATTRS_IDX]
);
if (ERROR_SUCCESS != lErr)
goto ErrorReturn;
}
// Verify the signature using either the authenticated attributes hash
// or the content hash
lErr = MinCryptVerifySignedHash(
HashAlgId,
rgbHash,
cbHash,
&rgParseSignedDataBlob[MINASN1_SIGNED_DATA_SIGNER_INFO_ENCYRPT_DIGEST_IDX],
&rgSignerCert[MINASN1_CERT_PUBKEY_INFO_IDX]
);
ErrorReturn:
CommonReturn:
return lErr;
ParseSignedDataError:
NotSignedDataOID:
InvalidContent:
lErr = CRYPT_E_BAD_MSG;
goto CommonReturn;
NoContent:
lErr = ERROR_NO_DATA;
goto CommonReturn;
NoSigner:
NoCerts:
NoSignerCert:
lErr = CRYPT_E_NO_SIGNER;
goto CommonReturn;
UnknownHashAlgId:
lErr = CRYPT_E_UNKNOWN_ALGO;
goto CommonReturn;
}