/* SPDX-License-Identifier: LGPL-2.1-or-later */

#include <openssl/asn1t.h>

#include "authenticode.h"
#include "crypto-util.h"

/* OpenSSL's ASN1_SEQUENCE/ASN1_CHOICE/IMPLEMENT_ASN1_FUNCTIONS macros expand to code that references
 * libcrypto symbols directly, which would force us to link this object against libcrypto and defeat the
 * dlopen approach used everywhere else. We work around that in two different ways depending on where the
 * reference appears in the macro expansion:
 *
 *  - ASN1_item_new/ASN1_item_free/ASN1_item_d2i/ASN1_item_i2d are called from the bodies of the functions
 *    generated by IMPLEMENT_ASN1_FUNCTIONS. We can simply #define them to the matching sym_* variants, so
 *    the generated bodies end up calling our dlopen'd pointers.
 *
 *  - ASN1_ANY_it/ASN1_BIT_STRING_it/... (the "_it" getters) are embedded as function pointers in the
 *    static const ASN1_ADB / ASN1_TEMPLATE tables emitted by the ASN1_SEQUENCE/ASN1_CHOICE macros. Static
 *    initializers can only contain constant expressions, so we can't point them at the sym_* variables
 *    (which are non-const globals filled in at dlopen time). Instead we define static trampoline
 *    functions (openssl_ASN1_*_it) whose addresses are constant, each forwarding to the corresponding
 *    sym_* pointer at runtime, and #define the OpenSSL names to our trampolines before the tables are
 *    emitted.
 *
 * Note that this file must only call these redefined macros indirectly, via the IMPLEMENT_ASN1_FUNCTIONS
 * expansions, and callers of those generated wrappers (e.g. sbsign.c) must have dlopen_libcrypto()
 * before invoking them, otherwise the sym_* pointers will still be NULL. */

static const ASN1_ITEM* openssl_ASN1_ANY_it(void) {
        assert(sym_ASN1_ANY_it);
        return sym_ASN1_ANY_it();
}

static const ASN1_ITEM* openssl_ASN1_BIT_STRING_it(void) {
        assert(sym_ASN1_BIT_STRING_it);
        return sym_ASN1_BIT_STRING_it();
}

static const ASN1_ITEM* openssl_ASN1_BMPSTRING_it(void) {
        assert(sym_ASN1_BMPSTRING_it);
        return sym_ASN1_BMPSTRING_it();
}

static const ASN1_ITEM* openssl_ASN1_IA5STRING_it(void) {
        assert(sym_ASN1_IA5STRING_it);
        return sym_ASN1_IA5STRING_it();
}

static const ASN1_ITEM* openssl_ASN1_OBJECT_it(void) {
        assert(sym_ASN1_OBJECT_it);
        return sym_ASN1_OBJECT_it();
}

static const ASN1_ITEM* openssl_ASN1_OCTET_STRING_it(void) {
        assert(sym_ASN1_OCTET_STRING_it);
        return sym_ASN1_OCTET_STRING_it();
}

#define ASN1_item_new           sym_ASN1_item_new
#define ASN1_item_free          sym_ASN1_item_free
#define ASN1_item_d2i           sym_ASN1_item_d2i
#define ASN1_item_i2d           sym_ASN1_item_i2d
#define ASN1_ANY_it             openssl_ASN1_ANY_it
#define ASN1_BIT_STRING_it      openssl_ASN1_BIT_STRING_it
#define ASN1_BMPSTRING_it       openssl_ASN1_BMPSTRING_it
#define ASN1_IA5STRING_it       openssl_ASN1_IA5STRING_it
#define ASN1_OBJECT_it          openssl_ASN1_OBJECT_it
#define ASN1_OCTET_STRING_it    openssl_ASN1_OCTET_STRING_it

ASN1_SEQUENCE(SpcAttributeTypeAndOptionalValue) = {
        ASN1_SIMPLE(SpcAttributeTypeAndOptionalValue, type, ASN1_OBJECT),
        ASN1_OPT(SpcAttributeTypeAndOptionalValue, value, ASN1_ANY)
} ASN1_SEQUENCE_END(SpcAttributeTypeAndOptionalValue);

IMPLEMENT_ASN1_FUNCTIONS(SpcAttributeTypeAndOptionalValue);

ASN1_SEQUENCE(AlgorithmIdentifier) = {
        ASN1_SIMPLE(AlgorithmIdentifier, algorithm, ASN1_OBJECT),
        ASN1_OPT(AlgorithmIdentifier, parameters, ASN1_ANY)
} ASN1_SEQUENCE_END(AlgorithmIdentifier)

IMPLEMENT_ASN1_FUNCTIONS(AlgorithmIdentifier);

ASN1_SEQUENCE(DigestInfo) = {
        ASN1_SIMPLE(DigestInfo, digestAlgorithm, AlgorithmIdentifier),
        ASN1_SIMPLE(DigestInfo, digest, ASN1_OCTET_STRING)
} ASN1_SEQUENCE_END(DigestInfo);

IMPLEMENT_ASN1_FUNCTIONS(DigestInfo);

ASN1_SEQUENCE(SpcIndirectDataContent) = {
        ASN1_SIMPLE(SpcIndirectDataContent, data, SpcAttributeTypeAndOptionalValue),
        ASN1_SIMPLE(SpcIndirectDataContent, messageDigest, DigestInfo)
} ASN1_SEQUENCE_END(SpcIndirectDataContent);

IMPLEMENT_ASN1_FUNCTIONS(SpcIndirectDataContent);

ASN1_CHOICE(SpcString) = {
        ASN1_IMP_OPT(SpcString, value.unicode, ASN1_BMPSTRING, 0),
        ASN1_IMP_OPT(SpcString, value.ascii, ASN1_IA5STRING, 1)
} ASN1_CHOICE_END(SpcString);

IMPLEMENT_ASN1_FUNCTIONS(SpcString);

ASN1_SEQUENCE(SpcSerializedObject) = {
        ASN1_SIMPLE(SpcSerializedObject, classId, ASN1_OCTET_STRING),
        ASN1_SIMPLE(SpcSerializedObject, serializedData, ASN1_OCTET_STRING)
} ASN1_SEQUENCE_END(SpcSerializedObject);

IMPLEMENT_ASN1_FUNCTIONS(SpcSerializedObject);

ASN1_CHOICE(SpcLink) = {
        ASN1_IMP_OPT(SpcLink, value.url, ASN1_IA5STRING, 0),
        ASN1_IMP_OPT(SpcLink, value.moniker, SpcSerializedObject, 1),
        ASN1_EXP_OPT(SpcLink, value.file, SpcString, 2)
} ASN1_CHOICE_END(SpcLink);

IMPLEMENT_ASN1_FUNCTIONS(SpcLink);

ASN1_SEQUENCE(SpcPeImageData) = {
        ASN1_SIMPLE(SpcPeImageData, flags, ASN1_BIT_STRING),
        ASN1_EXP_OPT(SpcPeImageData, file, SpcLink, 0)
} ASN1_SEQUENCE_END(SpcPeImageData)

IMPLEMENT_ASN1_FUNCTIONS(SpcPeImageData);
