/** @file
 * IPRT - Crypto - Microsoft SPC / Authenticode.
 */

/*
 * Copyright (C) 2006-2017 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 *
 * The contents of this file may alternatively be used under the terms
 * of the Common Development and Distribution License Version 1.0
 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
 * VirtualBox OSE distribution, in which case the provisions of the
 * CDDL are applicable instead of those of the GPL.
 *
 * You may elect to license modified versions of this file under the
 * terms and conditions of either the GPL or the CDDL or both.
 */

#ifndef ___iprt_crypto_spc_h
#define ___iprt_crypto_spc_h

#include <iprt/asn1.h>
#include <iprt/crypto/x509.h>
#include <iprt/crypto/pkcs7.h>
#include <iprt/md5.h>
#include <iprt/sha.h>


RT_C_DECLS_BEGIN

/** @defgroup grp_rt_cr_spc  RTCrSpc - Microsoft Authenticode
 * @ingroup grp_rt_crypto
 * @{
 */

/**
 * PE Image page hash table, generic union.
 *
 * @remarks This table isn't used by ldrPE.cpp, it walks the table in a generic
 *          fashion using the hash size. So, we can ditch it if we feel like it.
 */
typedef union RTCRSPCPEIMAGEPAGEHASHES
{
    /** MD5 page hashes. */
    struct
    {
        /** The file offset.  */
        uint32_t        offFile;
        /** The hash. */
        uint8_t         abHash[RTSHA1_HASH_SIZE];
    } aMd5[1];

    /** SHA-1 page hashes. */
    struct
    {
        /** The file offset.  */
        uint32_t        offFile;
        /** The hash. */
        uint8_t         abHash[RTSHA1_HASH_SIZE];
    } aSha1[1];

    /** SHA-256 page hashes. */
    struct
    {
        /** The file offset.  */
        uint32_t        offFile;
        /** The hash. */
        uint8_t         abHash[RTSHA256_HASH_SIZE];
    } aSha256[1];

    /** SHA-512 page hashes. */
    struct
    {
        /** The file offset.  */
        uint32_t        offFile;
        /** The hash. */
        uint8_t         abHash[RTSHA512_HASH_SIZE];
    } aSha512[1];

    /** Generic view of ONE hash. */
    struct
    {
        /** The file offset. */
        uint32_t        offFile;
        /** Variable length hash field. */
        uint8_t         abHash[1];
    } Generic;
} RTCRSPCPEIMAGEPAGEHASHES;
/** Pointer to a PE image page hash table union. */
typedef RTCRSPCPEIMAGEPAGEHASHES *PRTCRSPCPEIMAGEPAGEHASHES;
/** Pointer to a const PE image page hash table union. */
typedef RTCRSPCPEIMAGEPAGEHASHES const *PCRTCRSPCPEIMAGEPAGEHASHES;


/**
 * Serialization wrapper for raw RTCRSPCPEIMAGEPAGEHASHES data.
 */
typedef struct RTCRSPCSERIALIZEDPAGEHASHES
{
    /** The page hashes are within a set. Dunno if there could be multiple
     * entries in this set, never seen it yet, so I doubt it. */
    RTASN1SETCORE                   SetCore;
    /** Octet string containing the raw data. */
    RTASN1OCTETSTRING               RawData;

    /** Pointer to the hash data within that string.
     * The hash algorithm is given by the object attribute type in
     * RTCRSPCSERIALIZEDOBJECTATTRIBUTE.  It is generally the same as for the
     * whole image hash. */
    PCRTCRSPCPEIMAGEPAGEHASHES      pData;
    /** Field the user can use to store the number of pages in pData. */
    uint32_t                        cPages;
} RTCRSPCSERIALIZEDPAGEHASHES;
/** Pointer to a serialized wrapper for page hashes.  */
typedef RTCRSPCSERIALIZEDPAGEHASHES *PRTCRSPCSERIALIZEDPAGEHASHES;
/** Pointer to a const serialized wrapper for page hashes.  */
typedef RTCRSPCSERIALIZEDPAGEHASHES const *PCRTCRSPCSERIALIZEDPAGEHASHES;
RTASN1TYPE_STANDARD_PROTOTYPES(RTCRSPCSERIALIZEDPAGEHASHES, RTDECL, RTCrSpcSerializedPageHashes, SetCore.Asn1Core);

RTDECL(int) RTCrSpcSerializedPageHashes_UpdateDerivedData(PRTCRSPCSERIALIZEDPAGEHASHES pThis);


/**
 * Data type selection for RTCRSPCSERIALIZEDOBJECTATTRIBUTE.
 */
typedef enum RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE
{
    /** Invalid zero entry. */
    RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_INVALID = 0,
    /** Not present pro forma. */
    RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_NOT_PRESENT,
    /** Unknown object. */
    RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_UNKNOWN,
    /** SHA-1 page hashes (pPageHashes). */
    RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1,
    /** SHA-256 page hashes (pPageHashes). */
    RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V2,
    /** End of valid values. */
    RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_END,
    /** Blow up the type to at least 32-bits. */
    RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_32BIT_HACK
} RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE;

/**
 * One serialized object attribute (PE image data).
 */
typedef struct RTCRSPCSERIALIZEDOBJECTATTRIBUTE
{
    /** Sequence core. */
    RTASN1SEQUENCECORE                  SeqCore;
    /** The attribute type. */
    RTASN1OBJID                         Type;
    /** The allocation of the data type. */
    RTASN1ALLOCATION                    Allocation;
    /** Indicates the valid value in the union. */
    RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE enmType;
    /** Union with data format depending on the Type. */
    union
    {
        /** The unknown value (RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_UNKNOWN). */
        PRTASN1CORE                         pCore;
        /** Page hashes (RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V1 or
         *  RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE_PAGE_HASHES_V2). */
        PRTCRSPCSERIALIZEDPAGEHASHES        pPageHashes;
    } u;
} RTCRSPCSERIALIZEDOBJECTATTRIBUTE;
/** Pointer to a serialized object attribute.  */
typedef RTCRSPCSERIALIZEDOBJECTATTRIBUTE *PRTCRSPCSERIALIZEDOBJECTATTRIBUTE;
/** Pointer to a const serialized object attribute.  */
typedef RTCRSPCSERIALIZEDOBJECTATTRIBUTE const *PCRTCRSPCSERIALIZEDOBJECTATTRIBUTE;
RTASN1TYPE_STANDARD_PROTOTYPES(RTCRSPCSERIALIZEDOBJECTATTRIBUTE, RTDECL, RTCrSpcSerializedObjectAttribute, SeqCore.Asn1Core);

/** @name RTCRSPCSERIALIZEDOBJECTATTRIBUTE::Type values
 * @{ */
/** Serialized object attribute type for page hashes version 1. */
#define RTCRSPC_PE_IMAGE_HASHES_V1_OID  "1.3.6.1.4.1.311.2.3.1"
/** Serialized object attribute type for page hashes version 2. */
#define RTCRSPC_PE_IMAGE_HASHES_V2_OID  "1.3.6.1.4.1.311.2.3.2"
/** @} */


/*
 * Set of serialized object attributes (PE image data).
 */
RTASN1_IMPL_GEN_SET_OF_TYPEDEFS_AND_PROTOS(RTCRSPCSERIALIZEDOBJECTATTRIBUTES, RTCRSPCSERIALIZEDOBJECTATTRIBUTE, RTDECL,
                                           RTCrSpcSerializedObjectAttributes);

/** The UUID found in RTCRSPCSERIALIZEDOBJECT::Uuid for
 *  RTCRSPCSERIALIZEDOBJECTATTRIBUTES. */
#define RTCRSPCSERIALIZEDOBJECT_UUID_STR "d586b5a6-a1b4-6624-ae05-a217da8e60d6"


/**
 * Decoded encapsulated data type selection in RTCRSPCSERIALIZEDOBJECT.
 */
typedef enum RTCRSPCSERIALIZEDOBJECTTYPE
{
    /** Invalid zero value. */
    RTCRSPCSERIALIZEDOBJECTTYPE_INVALID = 0,
    /** Serialized object attributes (RTCRSPCSERIALIZEDOBJECT_UUID_STR / pAttribs). */
    RTCRSPCSERIALIZEDOBJECTTYPE_ATTRIBUTES,
    /** End of valid values. */
    RTCRSPCSERIALIZEDOBJECTTYPE_END,
    /** MAke sure the type is at least 32-bit wide. */
    RTCRSPCSERIALIZEDOBJECTTYPE_32BIT_HACK = 0x7fffffff
} RTCRSPCSERIALIZEDOBJECTTYPE;

/**
 * A serialized object (PE image data).
 */
typedef struct RTCRSPCSERIALIZEDOBJECT
{
    /** Sequence core. */
    RTASN1SEQUENCECORE                  SeqCore;
    /** The UUID of the data object. */
    RTASN1OCTETSTRING                   Uuid;
    /** Serialized data object. */
    RTASN1OCTETSTRING                   SerializedData;

    /** Indicates the valid pointer in the union. */
    RTCRSPCSERIALIZEDOBJECTTYPE         enmType;
    /** Union of pointers shadowing SerializedData.pEncapsulated.  */
    union
    {
        /** Generic core pointer. */
        PRTASN1CORE                         pCore;
        /** Pointer to decoded data if Uuid is RTCRSPCSERIALIZEDOBJECT_UUID_STR. */
        PRTCRSPCSERIALIZEDOBJECTATTRIBUTES  pData;
    } u;
} RTCRSPCSERIALIZEDOBJECT;
/** Pointer to a serialized object (PE image data). */
typedef RTCRSPCSERIALIZEDOBJECT *PRTCRSPCSERIALIZEDOBJECT;
/** Pointer to a const serialized object (PE image data). */
typedef RTCRSPCSERIALIZEDOBJECT const *PCRTCRSPCSERIALIZEDOBJECT;
RTASN1TYPE_STANDARD_PROTOTYPES(RTCRSPCSERIALIZEDOBJECT, RTDECL, RTCrSpcSerializedObject, SeqCore.Asn1Core);


/**
 * RTCRSPCSTRING choices.
 */
typedef enum RTCRSPCSTRINGCHOICE
{
    /** Invalid zero value.  */
    RTCRSPCSTRINGCHOICE_INVALID = 0,
    /** Not present. */
    RTCRSPCSTRINGCHOICE_NOT_PRESENT,
    /** UCS-2 string (pUcs2). */
    RTCRSPCSTRINGCHOICE_UCS2,
    /** ASCII string (pAscii). */
    RTCRSPCSTRINGCHOICE_ASCII,
    /** End of valid values. */
    RTCRSPCSTRINGCHOICE_END,
    /** Blow the type up to 32-bit. */
    RTCRSPCSTRINGCHOICE_32BIT_HACK = 0x7fffffff
} RTCRSPCSTRINGCHOICE;

/**
 * Stupid microsoft choosy string type.
 */
typedef struct RTCRSPCSTRING
{
    /** Dummy core. */
    RTASN1DUMMY                         Dummy;
    /** Allocation of what the pointer below points to. */
    RTASN1ALLOCATION                    Allocation;
    /** Pointer choice.*/
    RTCRSPCSTRINGCHOICE                 enmChoice;
    /** Pointer union. */
    union
    {
        /** Tag 0, implicit: UCS-2 (BMP) string.  */
        PRTASN1STRING                   pUcs2;
        /** Tag 1, implicit: ASCII (IA5) string.  */
        PRTASN1STRING                   pAscii;
    } u;
} RTCRSPCSTRING;
/** Pointer to a stupid microsoft string choice.  */
typedef RTCRSPCSTRING *PRTCRSPCSTRING;
/** Pointer to a const stupid microsoft string choice.  */
typedef RTCRSPCSTRING const *PCRTCRSPCSTRING;
RTASN1TYPE_STANDARD_PROTOTYPES(RTCRSPCSTRING, RTDECL, RTCrSpcString, Dummy.Asn1Core);


/**
 * RTCRSPCSTRING choices.
 */
typedef enum RTCRSPCLINKCHOICE
{
    /** Invalid zero value.  */
    RTCRSPCLINKCHOICE_INVALID = 0,
    /** Not present. */
    RTCRSPCLINKCHOICE_NOT_PRESENT,
    /** URL (ASCII) string (pUrl). */
    RTCRSPCLINKCHOICE_URL,
    /** Serialized object (pMoniker). */
    RTCRSPCLINKCHOICE_MONIKER,
    /** Filename (pT2). */
    RTCRSPCLINKCHOICE_FILE,
    /** End of valid values. */
    RTCRSPCLINKCHOICE_END,
    /** Blow the type up to 32-bit. */
    RTCRSPCLINKCHOICE_32BIT_HACK = 0x7fffffff
} RTCRSPCLINKCHOICE;

/**
 * PE image data link.
 */
typedef struct RTCRSPCLINK
{
    /** Dummy core. */
    RTASN1DUMMY                         Dummy;
    /** Allocation of what the pointer below points to. */
    RTASN1ALLOCATION                    Allocation;
    /** Pointer choice.*/
    RTCRSPCLINKCHOICE                   enmChoice;
    /** Pointer union. */
    union
    {
        /** Tag 0, implicit: An URL encoded as an IA5 STRING.  */
        PRTASN1STRING                   pUrl;
        /** Tag 1, implicit: A serialized object.  */
        PRTCRSPCSERIALIZEDOBJECT        pMoniker;
        /** Tag 2, explicit: The default, a file name.
         * Documented to be set to "<<<Obsolete>>>" when used. */
        struct
        {
            /** Context tag 2. */
            RTASN1CONTEXTTAG2           CtxTag2;
            /** The file name string. */
            RTCRSPCSTRING               File;
        } *pT2;
    } u;
} RTCRSPCLINK;
/** Poitner to a PE image data link. */
typedef RTCRSPCLINK *PRTCRSPCLINK;
/** Poitner to a const PE image data link. */
typedef RTCRSPCLINK const *PCRTCRSPCLINK;
RTASN1TYPE_STANDARD_PROTOTYPES(RTCRSPCLINK, RTDECL, RTCrSpcLink, Dummy.Asn1Core);


#if 0 /** @todo Might not be the correct bit order. */
/**
 * Flag values for RTCRSPCPEIMAGEDATA::Flags and RTCRSPCPEIMAGEDATA::fFlags.
 */
typedef enum RTCRSPCPEIMAGEFLAGS
{
    RTCRSPCPEIMAGEFLAGS_INCLUDE_RESOURCES = 0,
    RTCRSPCPEIMAGEFLAGS_INCLUDE_DEBUG_INFO = 1,
    RTCRSPCPEIMAGEFLAGS_IMPORT_ADDRESS_TABLE = 2
} RTCRSPCPEIMAGEFLAGS;
#endif


/**
 * Authenticode PE Image data.
 */
typedef struct RTCRSPCPEIMAGEDATA
{
    /** Sequence core. */
    RTASN1SEQUENCECORE                  SeqCore;
    /** One of the RTCRSPCPEIMAGEFLAGS value, default is
     * RTCRSPCPEIMAGEFLAGS_INCLUDE_RESOURCES.  Obsolete with v2 page hashes? */
    RTASN1BITSTRING                     Flags;
    /** Tag 0, explicit: Link to the data. */
    struct
    {
        /** Context tag 0. */
        RTASN1CONTEXTTAG0               CtxTag0;
        /** Link to the data. */
        RTCRSPCLINK                     File;
    } T0;
} RTCRSPCPEIMAGEDATA;
/** Pointer to a authenticode PE image data representation. */
typedef RTCRSPCPEIMAGEDATA *PRTCRSPCPEIMAGEDATA;
/** Pointer to a const authenticode PE image data representation. */
typedef RTCRSPCPEIMAGEDATA const *PCRTCRSPCPEIMAGEDATA;
RTASN1TYPE_STANDARD_PROTOTYPES(RTCRSPCPEIMAGEDATA, RTDECL, RTCrSpcPeImageData, SeqCore.Asn1Core);

/** The object ID for SpcPeImageData. */
#define RTCRSPCPEIMAGEDATA_OID            "1.3.6.1.4.1.311.2.1.15"


/**
 * Data type selection for RTCRSPCATTRIBUTETYPEANDOPTIONALVALUE.
 */
typedef enum RTCRSPCAAOVTYPE
{
    /** Invalid zero entry. */
    RTCRSPCAAOVTYPE_INVALID = 0,
    /** Not present (pro forma). */
    RTCRSPCAAOVTYPE_NOT_PRESENT,
    /** Unknown object. */
    RTCRSPCAAOVTYPE_UNKNOWN,
    /** PE image data (pPeImage). */
    RTCRSPCAAOVTYPE_PE_IMAGE_DATA,
    /** End of valid values. */
    RTCRSPCAAOVTYPE_END,
    /** Blow up the type to at least 32-bits. */
    RTCRSPCAAOVTYPE_32BIT_HACK
} RTCRSPCAAOVTYPE;

/**
 * Authenticode attribute type and optional value.
 *
 * Note! Spec says the value should be explicitly tagged, but in real life
 *       it isn't.  So, not very optional?
 */
typedef struct RTCRSPCATTRIBUTETYPEANDOPTIONALVALUE
{
    /** Sequence core. */
    RTASN1SEQUENCECORE                  SeqCore;
    /** An object ID indicating the type of the value. */
    RTASN1OBJID                         Type;
    /** Allocation of the optional data value. */
    RTASN1ALLOCATION                    Allocation;
    /** The valid pointer. */
    RTCRSPCAAOVTYPE                     enmType;
    /** The value part depends on the Type. */
    union
    {
        /** RTCRSPCAAOVTYPE_UNKNOWN / Generic.  */
        PRTASN1CORE                     pCore;
        /** RTCRSPCAAOVTYPE_PE_IMAGE_DATA / RTCRSPCPEIMAGEDATA_OID. */
        PRTCRSPCPEIMAGEDATA             pPeImage;
    } uValue;
} RTCRSPCATTRIBUTETYPEANDOPTIONALVALUE;
/** Pointer to a authentication attribute type and optional value
 *  representation. */
typedef RTCRSPCATTRIBUTETYPEANDOPTIONALVALUE *PRTCRSPCATTRIBUTETYPEANDOPTIONALVALUE;
/** Pointer to a const authentication attribute type and optional value
 *  representation. */
typedef RTCRSPCATTRIBUTETYPEANDOPTIONALVALUE const *PCRTCRSPCATTRIBUTETYPEANDOPTIONALVALUE;
RTASN1TYPE_STANDARD_PROTOTYPES(RTCRSPCATTRIBUTETYPEANDOPTIONALVALUE, RTDECL, RTCrSpcAttributeTypeAndOptionalValue, SeqCore.Asn1Core);


/**
 * Authenticode indirect data content.
 */
typedef struct RTCRSPCINDIRECTDATACONTENT
{
    /** Sequence core. */
    RTASN1SEQUENCECORE                  SeqCore;
    /** Additional data. */
    RTCRSPCATTRIBUTETYPEANDOPTIONALVALUE Data;
    /** The whole image digest. */
    RTCRPKCS7DIGESTINFO                 DigestInfo;
} RTCRSPCINDIRECTDATACONTENT;
/** Pointer to a authenticode indirect data content representation. */
typedef RTCRSPCINDIRECTDATACONTENT *PRTCRSPCINDIRECTDATACONTENT;
/** Pointer to a const authenticode indirect data content representation. */
typedef RTCRSPCINDIRECTDATACONTENT const *PCRTCRSPCINDIRECTDATACONTENT;
RTASN1TYPE_STANDARD_PROTOTYPES(RTCRSPCINDIRECTDATACONTENT, RTDECL, RTCrSpcIndirectDataContent, SeqCore.Asn1Core);

/** The object ID for SpcIndirectDataContent. */
#define RTCRSPCINDIRECTDATACONTENT_OID    "1.3.6.1.4.1.311.2.1.4"

/**
 * Check the sanity of an Authenticode SPCIndirectDataContent object.
 *
 * @returns IPRT status code
 * @param   pIndData            The Authenticode SPCIndirectDataContent to
 *                              check.
 * @param   pSignedData         The related signed data object.
 * @param   fFlags              RTCRSPCINDIRECTDATACONTENT_SANITY_F_XXX.
 * @param   pErrInfo            Optional error info.
 */
RTDECL(int) RTCrSpcIndirectDataContent_CheckSanityEx(PCRTCRSPCINDIRECTDATACONTENT pIndData, PCRTCRPKCS7SIGNEDDATA pSignedData,
                                                     uint32_t fFlags, PRTERRINFO pErrInfo);
/** @name RTCRSPCINDIRECTDATACONTENT_SANITY_F_XXX for RTCrSpcIndirectDataContent_CheckSanityEx.
 * @{  */
/** The digest hash algorithm must be known to IPRT. */
#define RTCRSPCINDIRECTDATACONTENT_SANITY_F_ONLY_KNOWN_HASH   RT_BIT_32(0)
/** PE image signing, check expectations of the spec.  */
#define RTCRSPCINDIRECTDATACONTENT_SANITY_F_PE_IMAGE          RT_BIT_32(1)
/** @} */

/**
 * Gets the first SPC serialized object attribute in a SPC PE image.
 *
 * @returns Pointer to the attribute with the given type, NULL if not found.
 * @param   pThis               The Authenticode SpcIndirectDataContent.
 * @param   enmType             The type of attribute to get.
 */
RTDECL(PCRTCRSPCSERIALIZEDOBJECTATTRIBUTE)
RTCrSpcIndirectDataContent_GetPeImageObjAttrib(PCRTCRSPCINDIRECTDATACONTENT pThis,
                                               RTCRSPCSERIALIZEDOBJECTATTRIBUTETYPE enmType);

/** @} */

RT_C_DECLS_END

#endif

