//+-------------------------------------------------------------------------
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 2001 - 2001
//
//  File:       mincrypt.h
//
//  Contents:   Minimal Cryptographic API Prototypes and Definitions
//
//              Contains cryptographic functions to verify PKCS #7 Signed Data
//              messages, X.509 certificate chains, Authenticode signed
//              files and file hashes in system catalogs.
//
//              These APIs rely on the APIs defined in minasn1.h for doing
//              the low level ASN.1 parsing.
//
//              These APIs are implemented to be self contained and to
//              allow for code obfuscation. These APIs will be included
//              in such applications as, DRM or licensing verification.
//
//              If the file name or file handle option is selected,
//              the following APIs will need to call the kernel32.dll APIs
//              to open, map and unmap files:
//                  MinCryptHashFile
//                  MinCryptVerifySignedFile
//              The following API will need to call kernel32.dll and
//              wintrust.dll APIs to find, open, map and unmap files:
//                  MinCryptVerifyHashInSystemCatalogs
//              Except for the calls in the above APIs,
//              no calls to APIs in other DLLs.
//
//              Additionally, since these APIs have been pared down
//              from their wincrypt.h and crypt32.dll counterparts they are
//              a good candidate for applications with minimal memory and CPU
//              resources.
//
//  APIs: 
//              MinCryptDecodeHashAlgorithmIdentifier
//              MinCryptHashMemory
//              MinCryptVerifySignedHash
//              MinCryptVerifyCertificate
//              MinCryptVerifySignedData
//              MinCryptHashFile
//              MinCryptVerifySignedFile
//              MinCryptVerifyHashInSystemCatalogs
//
//----------------------------------------------------------------------------

#ifndef __MINCRYPT_H__
#define __MINCRYPT_H__


#if defined (_MSC_VER)

#if ( _MSC_VER >= 800 )
#if _MSC_VER >= 1200
#pragma warning(push)
#endif
#pragma warning(disable:4201)    /* Nameless struct/union */
#endif

#if (_MSC_VER > 1020)
#pragma once
#endif

#endif


#include <wincrypt.h>
#include <minasn1.h>

#ifdef __cplusplus
extern "C" {
#endif



#define MINCRYPT_MAX_HASH_LEN               20
#define MINCRYPT_SHA1_HASH_LEN              20
#define MINCRYPT_MD5_HASH_LEN               16
#define MINCRYPT_MD2_HASH_LEN               16


//+-------------------------------------------------------------------------
//  Decodes an ASN.1 encoded Algorithm Identifier and converts to
//  a CAPI Hash AlgID, such as, CALG_SHA1 or CALG_MD5.
//
//  Returns 0 if there isn't a CAPI AlgId corresponding to the Algorithm
//  Identifier.
//
//  Only CALG_SHA1, CALG_MD5 and CALG_MD2 are supported.
//--------------------------------------------------------------------------
ALG_ID
WINAPI
MinCryptDecodeHashAlgorithmIdentifier(
    IN PCRYPT_DER_BLOB pAlgIdValueBlob
    );

//+-------------------------------------------------------------------------
//  Hashes one or more memory blobs according to the Hash ALG_ID.
//
//  rgbHash is updated with the resultant hash. *pcbHash is updated with
//  the length associated with the hash algorithm.
//
//  If the function succeeds, the return value is ERROR_SUCCESS. Otherwise,
//  a nonzero error code is returned.
//
//  Only CALG_SHA1, CALG_MD5 and CALG_MD2 are supported.
//--------------------------------------------------------------------------
LONG
WINAPI
MinCryptHashMemory(
    IN ALG_ID HashAlgId,
    IN DWORD cBlob,
    IN PCRYPT_DER_BLOB rgBlob,
    OUT BYTE rgbHash[MINCRYPT_MAX_HASH_LEN],
    OUT DWORD *pcbHash
    );


//+-------------------------------------------------------------------------
//  Verifies a signed hash.
//
//  The ASN.1 encoded Public Key Info is parsed and used to decrypt the
//  signed hash. The decrypted signed hash is compared with the input
//  hash.
//
//  If the signed hash was successfully verified, ERROR_SUCCESS is returned.
//  Otherwise, a nonzero error code is returned.
//
//  Only RSA signatures are supported.
//
//  Only MD2, MD5 and SHA1 hashes are supported.
//--------------------------------------------------------------------------
LONG
WINAPI
MinCryptVerifySignedHash(
    IN ALG_ID HashAlgId,
    IN BYTE *pbHash,
    IN DWORD cbHash,
    IN PCRYPT_DER_BLOB pSignedHashContentBlob,
    IN PCRYPT_DER_BLOB pPubKeyInfoValueBlob
    );



//+-------------------------------------------------------------------------
//  Verifies a previously parsed X.509 Certificate.
//
//  Assumes the ASN.1 encoded X.509 certificate was parsed via
//  MinAsn1ParseCertificate() and the set of potential issuer certificates
//  were parsed via one or more of:
//   - MinAsn1ParseCertificate()
//   - MinAsn1ParseSignedDataCertificates()
//   - MinAsn1ExtractParsedCertificatesFromSignedData()
//
//  Iteratively finds the issuer certificate via its encoded name. The
//  public key in the issuer certificate is used to verify the subject
//  certificate's signature. This is repeated until finding a self signed
//  certificate or a baked in root identified by its encoded name.
//  For a self signed certificate, compares against the baked in root
//  public keys.
//
//  If the certificate and its issuers were successfully verified to a
//  baked in root, ERROR_SUCCESS is returned.  Otherwise, a nonzero error
//  code is returned.
//--------------------------------------------------------------------------
LONG
WINAPI
MinCryptVerifyCertificate(
    IN CRYPT_DER_BLOB rgSubjectCertBlob[MINASN1_CERT_BLOB_CNT],
    IN DWORD cIssuerCert,
    IN CRYPT_DER_BLOB rgrgIssuerCertBlob[][MINASN1_CERT_BLOB_CNT]
    );



//+-------------------------------------------------------------------------
//  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.
//--------------------------------------------------------------------------

// Content Object Identifier content bytes (OID)
#define MINCRYPT_VER_SIGNED_DATA_CONTENT_OID_IDX                    0

// Content data content bytes excluding "[0] EXPLICIT" tag
// (OPTIONAL MinAsn1ParseCTL, MinAsn1ParseIndirectData)
#define MINCRYPT_VER_SIGNED_DATA_CONTENT_DATA_IDX                   1

// Signer certificate's encoded bytes (MinAsn1ParseCertificate)
#define MINCRYPT_VER_SIGNED_DATA_SIGNER_CERT_IDX                    2

// Authenticated attributes value bytes including "[0] IMPLICIT" tag
// (OPTIONAL, MinAsn1ParseAttributes)
#define MINCRYPT_VER_SIGNED_DATA_AUTH_ATTRS_IDX                     3

// Unauthenticated attributes value bytes including "[1] IMPLICIT" tag
// (OPTIONAL, MinAsn1ParseAttributes)
#define MINCRYPT_VER_SIGNED_DATA_UNAUTH_ATTRS_IDX                   4

#define MINCRYPT_VER_SIGNED_DATA_BLOB_CNT                           5

LONG
WINAPI
MinCryptVerifySignedData(
    IN const BYTE *pbEncoded,
    IN DWORD cbEncoded,
    OUT CRYPT_DER_BLOB rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_BLOB_CNT]
    );



//+-------------------------------------------------------------------------
//  File Type Definitions
//
//  Specifies the type of the "const VOID *pvFile" parameter
//--------------------------------------------------------------------------

// pvFile - LPCWSTR pwszFilename
#define MINCRYPT_FILE_NAME          1

// pvFile - HANDLE hFile
#define MINCRYPT_FILE_HANDLE        2

// pvFile - PCRYPT_DATA_BLOB pFileBlob
#define MINCRYPT_FILE_BLOB          3


//+-------------------------------------------------------------------------
//  Hashes the file according to the Hash ALG_ID.
//
//  According to dwFileType, pvFile can be a pwszFilename, hFile or pFileBlob.
//  Only requires READ access.
//
//  dwFileType:
//      MINCRYPT_FILE_NAME      : pvFile - LPCWSTR pwszFilename
//      MINCRYPT_FILE_HANDLE    : pvFile - HANDLE hFile
//      MINCRYPT_FILE_BLOB      : pvFile - PCRYPT_DATA_BLOB pFileBlob
//
//  rgbHash is updated with the resultant hash. *pcbHash is updated with
//  the length associated with the hash algorithm.
//
//  If the function succeeds, the return value is ERROR_SUCCESS. Otherwise,
//  a nonzero error code is returned.
//
//  Only CALG_SHA1 and CALG_MD5 are supported.
//
//  If a NT PE 32 bit file format, hashed according to imagehlp rules, ie, skip
//  section containing potential signature, ... . Otherwise, the entire file
//  is hashed.
//--------------------------------------------------------------------------
LONG
WINAPI
MinCryptHashFile(
    IN DWORD dwFileType,
    IN const VOID *pvFile,
    IN ALG_ID HashAlgId,
    OUT BYTE rgbHash[MINCRYPT_MAX_HASH_LEN],
    OUT DWORD *pcbHash
    );


//+-------------------------------------------------------------------------
//  Verifies a previously signed file.
//
//  According to dwFileType, pvFile can be a pwszFilename, hFile or pFileBlob.
//  Only requires READ access.
//
//  dwFileType:
//      MINCRYPT_FILE_NAME      : pvFile - LPCWSTR pwszFilename
//      MINCRYPT_FILE_HANDLE    : pvFile - HANDLE hFile
//      MINCRYPT_FILE_BLOB      : pvFile - PCRYPT_DATA_BLOB pFileBlob
//
//  Checks if the file has an embedded PKCS #7 Signed Data message containing
//  Indirect Data. The PKCS #7 is verified via MinCryptVerifySignedData().
//  The Indirect Data is parsed via MinAsn1ParseIndirectData() to get the
//  HashAlgId and the file hash.  MinCryptHashFile() is called to hash the
//  file. The returned hash is compared against the Indirect Data's hash.
//
//  The caller can request one or more signer authenticated attribute values
//  to be returned. The still encoded values are returned in the
//  caller allocated memory. The beginning of this returned memory will
//  be set to an array of attribute value blobs pointing to these
//  encoded values (CRYPT_DER_BLOB rgAttrBlob[cAttrOID]).
//  For performance reasons, the caller should make every attempt to allow
//  for a single pass call. The necessary memory size is:
//      (cAttrOID * sizeof(CRYPT_DER_BLOB)) +
//          total length of encoded attribute values.
//
//  *pcbAttr will be updated with the number of bytes required to contain
//  the attribute blobs and values. If the input memory is insufficient,
//  ERROR_INSUFFICIENT_BUFFER will be returned if no other error.
//
//  For a multi-valued attribute, only the first value is returned.
//
//  If the function succeeds, the return value is ERROR_SUCCESS. Otherwise,
//  a nonzero error code is returned.
//
//  Only NT, PE 32 bit file formats are supported.
//--------------------------------------------------------------------------
LONG
WINAPI
MinCryptVerifySignedFile(
    IN DWORD dwFileType,
    IN const VOID *pvFile,

    IN OPTIONAL DWORD cAttrOID,
    IN OPTIONAL CRYPT_DER_BLOB rgAttrEncodedOIDBlob[],
    // CRYPT_DER_BLOB rgAttrBlob[cAttrOID] header is at beginning
    // with the bytes pointed to immediately following
    OUT OPTIONAL CRYPT_DER_BLOB *rgAttrValueBlob,
    IN OUT OPTIONAL DWORD *pcbAttr
    );

//+-------------------------------------------------------------------------
//  Verifies the hashes in the system catalogs.
//
//  Iterates through the hashes and attempts to find the system catalog
//  containing it. If found, the system catalog file is verified as a
//  PKCS #7 Signed Data message with its signer cert verified up to a baked
//  in root.
//
//  The following mscat32.dll APIs are called to find the system catalog file:
//      CryptCATAdminAcquireContext
//      CryptCATAdminReleaseContext
//      CryptCATAdminEnumCatalogFromHash
//      CryptCATAdminReleaseCatalogContext
//      CryptCATCatalogInfoFromContext
//
//  If the hash was successfully verified, rglErr[] is set to ERROR_SUCCESS.
//  Otherwise, rglErr[] is set to a nonzero error code.
//
//  The caller can request one or more catalog subject attribute,
//  extension or signer authenticated attribute values to be returned for
//  each hash.  The still encoded values are returned in the
//  caller allocated memory. The beginning of this returned memory will
//  be set to a 2 dimensional array of attribute value blobs pointing to these
//  encoded values (CRYPT_DER_BLOB rgrgAttrValueBlob[cHash][cAttrOID]).
//  For performance reasons, the caller should make every attempt to allow
//  for a single pass call. The necessary memory size is:
//      (cHash * cAttrOID * sizeof(CRYPT_DER_BLOB)) +
//          total length of encoded attribute values.
//
//  *pcbAttr will be updated with the number of bytes required to contain
//  the attribute blobs and values. If the input memory is insufficient,
//  ERROR_INSUFFICIENT_BUFFER will be returned if no other error.
//
//  For a multi-valued attribute, only the first value is returned.
//
//  If the function succeeds, the return value is ERROR_SUCCESS. This may
//  be returned for unsuccessful rglErr[] values. Otherwise,
//  a nonzero error code is returned.
//--------------------------------------------------------------------------
LONG
WINAPI
MinCryptVerifyHashInSystemCatalogs(
    IN ALG_ID HashAlgId,
    IN DWORD cHash,
    IN CRYPT_HASH_BLOB rgHashBlob[],
    OUT LONG rglErr[],

    IN OPTIONAL DWORD cAttrOID,
    IN OPTIONAL CRYPT_DER_BLOB rgAttrEncodedOIDBlob[],
    // CRYPT_DER_BLOB rgrgAttrValueBlob[cHash][cAttrOID] header is at beginning
    // with the bytes pointed to immediately following
    OUT OPTIONAL CRYPT_DER_BLOB *rgrgAttrValueBlob,
    IN OUT OPTIONAL DWORD *pcbAttr
    );
    



#ifdef __cplusplus
}       // Balance extern "C" above
#endif

#if defined (_MSC_VER)
#if ( _MSC_VER >= 800 )

#if _MSC_VER >= 1200
#pragma warning(pop)
#else
#pragma warning(default:4201)
#endif

#endif
#endif

#endif // __MINCRYPT_H__