/* SPDX-License-Identifier: BSD-2-Clause */
/*******************************************************************************
 * Copyright 2018, Fraunhofer SIT sponsored by Infineon Technologies AG
 * All rights reserved.
 ******************************************************************************/

#ifdef HAVE_CONFIG_H
#include "config.h" // IWYU pragma: keep
#endif

#include <inttypes.h>    // for uint32_t
#include <json.h>        // for json_object_put, json_object
#include <openssl/evp.h> // for OpenSSL_add_all_digests
#include <stdio.h>       // for NULL, fprintf, stderr
#include <string.h>      // for strdup

#include "../helper/cmocka_all.h"   // for assert_int_equal, assert_non_null
#include "ifapi_eventlog.h"         // for ifapi_cleanup_event, IFAPI_EVENT
#include "ifapi_ima_eventlog.h"     // for ifapi_get_ima_eventname, ifapi_r...
#include "ifapi_json_deserialize.h" // for ifapi_json_IFAPI_EVENT_deserialize
#include "tss2_common.h"            // for TSS2_RC_SUCCESS, TSS2_RC
#include "util/aux_util.h"          // for SAFE_FREE

#define LOGMODULE tests
#include "util/log.h"

typedef struct {
    char *digest1;
    char *name;
} ascii_event;

ascii_event sml_ima_ng_sha1[6]
    = { { .digest1 = "1d8d532d463c9f8c205d0df7787669a85f93e260", .name = "boot_aggregate" },
        { .digest1 = "972d62ff5b3a74e89952e0980b2099eed49bf8f0", .name = "/init" },
        { .digest1 = "c76ee54d77352bee745ce43829247ff1706886df", .name = "/usr/bin/sh" },
        { .digest1 = "ef885dd41140c5fb993188f60a298c5a9b31d038",
          .name = "/usr/lib/x86_64-linux-gnu/ld-2.31.so" },
        { .digest1 = "a8fe19f5a9773edebf7b7df90bf9ce38b071e684", .name = "/etc/ld.so.cache" },
        { .digest1 = "0801daa94a2c43f41200143b319f19b28b43f745",
          .name = "/usr/lib/x86_64-linux-gnu/libc-2.31.so" } };

ascii_event sml_ima_sha1[12] = {
    { .digest1 = "719de8e521439498e9b77f6ed41e230b9821111e", .name = "boot_aggregate" },
    { .digest1 = "a3ff2672a129ea092ec2d41b88daafe632a8b65a", .name = "/init" },
    { .digest1 = "11fdfd51127b2a3853c55136d0c76411f6246892", .name = "/usr/bin/sh" },
    { .digest1 = "7a89e5da2a7299c7b3d76fbbbd4727d13818cc9a",
      .name = "/usr/lib/x86_64-linux-gnu/ld-2.31.so" },
    { .digest1 = "6d644a6a36001decfbc93ae98734473adc807c98", .name = "/etc/ld.so.cache" },
    { .digest1 = "70ff9de3f9a80361cca4f13edc027150318116da",
      .name = "/usr/lib/x86_64-linux-gnu/libc-2.31.so" },
    { .digest1 = "082ab538004eb50f2c8cdcbc8467037b11436ed6", .name = "/conf/arch.conf" },
    { .digest1 = "0cf46ceded0ffc71df00612568f6679208bb43d1", .name = "/conf/initramfs.conf" },
    { .digest1 = "37d29d0dbcfcc9b90fcd1986e452ab27a017e61a", .name = "/scripts/functions" },
    { .digest1 = "b81750d408faeb879a02f14cd8f42253308686fa", .name = "/scripts/init-top/ORDER" },
    { .digest1 = "47a8d6956a06cecf639af074b264bd1338c0942e",
      .name = "/scripts/init-top/all_generic_ide" },
    { .digest1 = "21044c1265a4b298dfd97ead49c355817b1a2493", .name = "/scripts/init-top/blacklist" }
};

ascii_event sml_ima_sig_sha256[9]
    = { { .digest1 = "bcb0e518b79de0d7f2cd20b8a29a7092e65db7ef", .name = "boot_aggregate" },
        { .digest1 = "539fdb622d9bc29e698e8cb87ad654b318844987", .name = "/init" },
        { .digest1 = "2675452da05e86c25345bc22d71b53fb1de3aea6", .name = "/usr/bin/sh" },
        { .digest1 = "9b98ed341fd71c6841eadda983f0bd067d7aae61",
          .name = "/usr/lib/x86_64-linux-gnu/ld-2.31.so" },
        { .digest1 = "4e1485e0515360012ae54a4e7775a09039da4cd6", .name = "/etc/ld.so.cache" },
        { .digest1 = "dbfdd0da61d79f6185b053e793c5e56a85c23eee",
          .name = "/usr/lib/x86_64-linux-gnu/libc-2.31.so" },
        { .digest1 = "4ca22ff0b5a3f5cec29d40b1a53c19b34b5e5d9a", .name = "/conf/arch.conf" },
        { .digest1 = "9f51391fdcb905d92ca5ca433dd1a9945c883164", .name = "/conf/initramfs.conf" },
        { .digest1 = "6b60a8edd48a777ac2e05fdcad20d2af09e8489c", .name = "/scripts/functions" } };

static void
check_eventlog(const char *file, ascii_event *ima_ascii_list, int n_ascii) {
    TSS2_RC      r;
    char        *json_string;
    json_object *json_event_list = NULL, *json_ascii_list = NULL, *json_digests, *json_digest,
                *json_digest_ary0, *json_bin,
                *json_content; // *json_name;
    size_t      n_bin, i;
    int         jso_result;
    const char *digest1; // *name;
    uint32_t    pcr_list[1] = { 10 };

    r = ifapi_read_ima_event_log(file, &pcr_list[0], 1, &json_event_list);
    assert_int_equal(r, TSS2_RC_SUCCESS);

    json_string = strdup(json_object_to_json_string_ext(json_event_list, JSON_C_TO_STRING_PRETTY));
    assert_non_null(json_string);

    n_bin = json_object_array_length(json_event_list);

    assert_int_equal(n_ascii, n_bin);

    fprintf(stderr, "\n%s\n", json_string);

    for (i = 0; i < n_bin; i++) {
        IFAPI_EVENT event;
        json_bin = json_object_array_get_idx(json_event_list, i);
        assert_non_null(json_bin);

        r = ifapi_json_IFAPI_EVENT_deserialize(json_bin, &event, DIGEST_CHECK_ERROR);
        assert_int_equal(r, TSS2_RC_SUCCESS);

        jso_result = json_object_object_get_ex(json_bin, "digests", &json_digests);
        assert_int_not_equal(jso_result, 0);

        json_digest_ary0 = json_object_array_get_idx(json_digests, 0);
        assert_non_null(json_digest_ary0);

        jso_result = json_object_object_get_ex(json_digest_ary0, "digest", &json_digest);
        assert_int_not_equal(jso_result, 0);

        digest1 = json_object_get_string(json_digest);
        assert_non_null(digest1);

        assert_string_equal(digest1, ima_ascii_list[i].digest1);

        jso_result = json_object_object_get_ex(json_bin, CONTENT, &json_content);
        assert_int_not_equal(jso_result, 0);

        ifapi_cleanup_event(&event);
    }

    assert_int_equal(r, TSS2_RC_SUCCESS);

    json_object_put(json_event_list);
    json_object_put(json_ascii_list);
    SAFE_FREE(json_string);
}

static void
check_get_name(void **state) {
    IFAPI_EVENT event;
    TSS2_RC     r;
    char       *name;
    char       *json_str = "{"
                           "    \"recnum\":0,"
                           "    \"pcr\":10,"
                           "    \"digests\":["
                           "      {"
                           "        \"hashAlg\":\"sha1\","
                           "        \"digest\":\"1d8d532d463c9f8c205d0df7787669a85f93e260\""
                           "      }"
                           "    ],"
                           "    \"content_type\":\"ima_template\","
                           "    \"content\":{"
                           "      \"template_name\":\"ima-ng\","
                           "      "
                           "\"template_data\":"
                           "\"1a000000736861313a0000000000000000000000000000000000000000000f000000626f6f7"
                           "45f61676772656761746500\""
                           "    }"
                           "}";

    json_object *jso = json_tokener_parse(json_str);
    assert_non_null(jso);
    OpenSSL_add_all_digests();
    r = ifapi_json_IFAPI_EVENT_deserialize(jso, &event, DIGEST_CHECK_ERROR);
    assert_int_equal(r, TSS2_RC_SUCCESS);

    r = ifapi_get_ima_eventname(&event.content.ima_event, &name);
    assert_int_equal(r, TSS2_RC_SUCCESS);
    assert_string_equal(name, "boot_aggregate");
    json_object_put(jso);
    ifapi_cleanup_event(&event);
}

static void
check_invalidate_event(void **state) {
    IFAPI_EVENT event;
    TSS2_RC     r;
    char       *name;
    char       *json_str = "{"
                           "    \"recnum\":0,"
                           "    \"pcr\":10,"
                           "    \"digests\":["
                           "      {"
                           "        \"hashAlg\":\"sha1\","
                           "        \"digest\":\"ffffffffffffffffffffffffffffffffffffffff\""
                           "      }"
                           "    ],"
                           "    \"content_type\":\"ima_template\","
                           "    \"content\":{"
                           "      \"template_name\":\"ima-ng\","
                           "      "
                           "\"template_data\":"
                           "\"1a000000736861313a0000000000000000000000000000000000000000000f000000626f6f7"
                           "45f61676772656761746500\""
                           "    }"
                           "}";

    json_object *jso = json_tokener_parse(json_str);
    assert_non_null(jso);
    OpenSSL_add_all_digests();

    /* Verification is not possible, but event can be deserialized. */
    r = ifapi_json_IFAPI_EVENT_deserialize(jso, &event, DO_NOT_CHECK_DIGEST);
    assert_int_equal(r, TSS2_RC_SUCCESS);

    r = ifapi_get_ima_eventname(&event.content.ima_event, &name);
    assert_int_equal(r, TSS2_RC_SUCCESS);
    assert_string_equal(name, "boot_aggregate");
    json_object_put(jso);
    ifapi_cleanup_event(&event);
}

static void
check_sml_ima_ng_sha1(void **state) {
    check_eventlog("test/data/fapi/eventlog/sml-ima-ng-sha1.bin", sml_ima_ng_sha1, 6);
}

static void
check_sml_ima_sha1(void **state) {
    check_eventlog("test/data/fapi/eventlog/sml-ima-sha1.bin", sml_ima_sha1, 12);
}

static void
check_sml_ima_sig_sha256(void **state) {
    check_eventlog("test/data/fapi/eventlog/sml-ima-sig-sha256.bin", sml_ima_sig_sha256, 9);
}

int
main(int argc, char *argv[]) {
    const struct CMUnitTest tests[] = {
        cmocka_unit_test(check_invalidate_event),   cmocka_unit_test(check_get_name),
        cmocka_unit_test(check_sml_ima_sha1),       cmocka_unit_test(check_sml_ima_ng_sha1),
        cmocka_unit_test(check_sml_ima_sig_sha256),
    };
    return cmocka_run_group_tests(tests, NULL, NULL);
}
