#include <gtest/gtest.h>

extern "C" {
  #include "authentication.h"
  #include "provisioning.h"
  #include "pa_tz_api.h"
}

static TaskInfo g_issuer;
static ProcessAddress g_mapped_apk;
static ProcessAddress g_source_file;
static ProcessAddress g_destination_file;
static char g_package_name;
static uint8_t g_rsa;
static size_t g_rsa_size;
static uint8_t g_five_signature[kMaxPaSignatureLength];
static size_t g_five_signature_length = kMaxPaSignatureLength;
static PaCertificate_t g_certificate = {0};

static PaTzResult g_pa_certificate_create_return = PA_TZ_SUCCESS;
static PaTzResult g_task_parse_pa_certificate_return = PA_TZ_SUCCESS;
static PaTzResult g_pa_certificate_validate_return = PA_TZ_SUCCESS;
static PaTzResult g_task_parse_five_signature_return = PA_TZ_SUCCESS;
static PaTzResult g_task_find_vma_with_process_address_mapped_apk_return = PA_TZ_SUCCESS;
static PaTzResult g_task_find_vma_with_process_address_source_file_return = PA_TZ_SUCCESS;
static PaTzResult g_task_find_vma_with_process_address_destination_file_return = PA_TZ_SUCCESS;

extern "C" PaTzResult CheckIntegritySigningRights(const TaskInfo *issuer) {
  if (issuer->integrity == kIntegrityPreloadWithSign) {
    return PA_TZ_SUCCESS;
  } else {
    return PA_TZ_CALLER_IS_FORBIDEN;
  }
}

extern "C" PaTzResult PaCertificateCreate(const uint8_t *id, size_t id_size,
                               const uint8_t *five_signature, size_t five_signature_size,
                               const uint8_t *pa_app_name, size_t pa_app_name_size,
                               int flags,
                               PaCertificate_t **certificate) {
  return g_pa_certificate_create_return;
}

extern "C" PaTzResult TaskParsePaCertificate(const TaskInfo *task, 
                                             ProcessAddress address,
                                             PaCertificate_t **certificate) {
  *certificate = &g_certificate;

  return g_task_parse_pa_certificate_return;
}

extern "C" PaTzResult PaCertificateValidate(const PaCertificate_t *certificate) {
  return g_pa_certificate_validate_return;
}

extern "C" void PaCertificateDestroy(PaCertificate_t *certificate) {
}

extern "C" PaTzResult TaskFindVmaWithProcessAddress(const TaskInfo *task,
                                                    ProcessAddress address,
                                                    VmaInfo *out_vma) {
  if (address == g_mapped_apk) {
    return g_task_find_vma_with_process_address_mapped_apk_return;
  }

  if (address == g_source_file) {
    return g_task_find_vma_with_process_address_source_file_return;
  }

  if (address == g_destination_file) {
    return g_task_find_vma_with_process_address_destination_file_return;
  }

  return PA_TZ_SUCCESS;
}

extern "C" PaTzResult TaskParseFiveSignature(KernelAddress file_struct,
                                             void *signature, size_t *signature_len) {  
  memcpy(signature, (void *)g_five_signature, sizeof(g_five_signature));
  *signature_len = g_five_signature_length;

  return g_task_parse_five_signature_return;
}


class CreateNewCertificateTest : public ::testing::Test {
protected:
  virtual void SetUp() {
    g_mapped_apk = 0x1;
    g_rsa_size = 128;
    memset(&g_issuer, 0, sizeof(g_issuer));

    g_issuer.integrity = kIntegrityPreloadWithSign;
    g_pa_certificate_create_return = PA_TZ_SUCCESS;
    g_task_find_vma_with_process_address_mapped_apk_return = PA_TZ_SUCCESS;
  }

  virtual void TearDown() {
  }
};

TEST_F(CreateNewCertificateTest, AllParametersValid) {
  PaTzResult result;
  PaCertificate_t *new_certificate = NULL;

  result = CreateNewCertificate(&g_issuer, g_mapped_apk, &g_package_name,
                                &g_rsa, g_rsa_size, &new_certificate);

  EXPECT_TRUE(PA_TZ_SUCCESS == result);
}

TEST_F(CreateNewCertificateTest, RsaSizeIsSmall) {
  PaTzResult result;
  PaCertificate_t *new_certificate = NULL;
  g_rsa_size = 10;

  result = CreateNewCertificate(&g_issuer, g_mapped_apk, &g_package_name,
                                &g_rsa, g_rsa_size, &new_certificate);

  EXPECT_EQ(PA_TZ_GENERAL_ERROR, result);
}

TEST_F(CreateNewCertificateTest, IssuerNull) {
  PaTzResult result;
  TaskInfo *issuer_null = NULL;
  PaCertificate_t *new_certificate = NULL;

  result = CreateNewCertificate(issuer_null, g_mapped_apk, &g_package_name,
                                &g_rsa, g_rsa_size, &new_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(CreateNewCertificateTest, IssuerWithoutPermissions) {
  PaTzResult result;
  PaCertificate_t *new_certificate = NULL;
  g_issuer.integrity = kIntegrityPreload;

  result = CreateNewCertificate(&g_issuer, g_mapped_apk, &g_package_name,
                                &g_rsa, g_rsa_size, &new_certificate);

  EXPECT_EQ(PA_TZ_CALLER_IS_FORBIDEN, result);
}

TEST_F(CreateNewCertificateTest, MappedApkZero) {
  PaTzResult result;
  ProcessAddress mapped_apk_zero = 0x0;
  PaCertificate_t *new_certificate = NULL;

  result = CreateNewCertificate(&g_issuer, mapped_apk_zero, &g_package_name,
                                &g_rsa, g_rsa_size, &new_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(CreateNewCertificateTest, PackageNameNull) {
  PaTzResult result;
  char *package_name_null = NULL;
  PaCertificate_t *new_certificate = NULL;

  result = CreateNewCertificate(&g_issuer, g_mapped_apk, package_name_null,
                                &g_rsa, g_rsa_size, &new_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(CreateNewCertificateTest, RsaNULL) {
  PaTzResult result;
  uint8_t *rsa_null = NULL;
  PaCertificate_t *new_certificate = NULL;

  result = CreateNewCertificate(&g_issuer, g_mapped_apk, &g_package_name,
                                rsa_null, g_rsa_size, &new_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(CreateNewCertificateTest, RsaSizeZero) {
  PaTzResult result;
  size_t rsa_size_zero = 0;
  PaCertificate_t *new_certificate = NULL;

  result = CreateNewCertificate(&g_issuer, g_mapped_apk, &g_package_name,
                                &g_rsa, rsa_size_zero, &new_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(CreateNewCertificateTest, NewCertificateNull) {
  PaTzResult result;
  PaCertificate_t **new_certificate_null = NULL;

  result = CreateNewCertificate(&g_issuer, g_mapped_apk, &g_package_name,
                                &g_rsa, g_rsa_size, new_certificate_null);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(CreateNewCertificateTest, FailedPaCertificateCreate) {
  PaTzResult result;
  PaCertificate_t *new_certificate = NULL;

  g_pa_certificate_create_return = PA_TZ_GENERAL_ERROR;

  result = CreateNewCertificate(&g_issuer, g_mapped_apk, &g_package_name,
                                &g_rsa, g_rsa_size, &new_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(CreateNewCertificateTest, FailedGetFiveSignatureOfMappedFile) {
  PaTzResult result;
  PaCertificate_t *new_certificate = NULL;

  g_task_find_vma_with_process_address_mapped_apk_return = PA_TZ_GENERAL_ERROR;

  result = CreateNewCertificate(&g_issuer, g_mapped_apk, &g_package_name,
                                &g_rsa, g_rsa_size, &new_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(CreateNewCertificateTest, FailedGetFiveSignatureOfMappedFileWhileParseSign) {
  PaTzResult result;
  PaCertificate_t *new_certificate = NULL;

  g_task_parse_five_signature_return = PA_TZ_GENERAL_ERROR;

  result = CreateNewCertificate(&g_issuer, g_mapped_apk, &g_package_name,
                                &g_rsa, g_rsa_size, &new_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}
