|
|
//+-------------------------------------------------------------------------
// 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; }
|