#include <gtest/gtest.h>

extern "C" {
  #include "pa_certificate.h"
  #include "config.h"
  #include "crypto.h"
}

static uint8_t g_id = 0;
static size_t g_id_size = 0;
static uint8_t g_five_signature = 0;
static size_t g_five_signature_size = 0;
static uint8_t g_pa_app_name = 0;
static size_t g_pa_app_name_size = 0;
static int g_flags = 0;
static PaConfig g_pa_config = {0};
static PaCertificate_t g_certificate = {0};

static int g_pa_tz_encoder_pa_data_return = 0;
static PaTzResult g_crypto_hmac_signature_generate_return = PA_TZ_SUCCESS;
static PaTzResult g_rsa_signature_verification_return = PA_TZ_SUCCESS;
static PaTzResult g_crypto_hmac_signature_verification_return = PA_TZ_SUCCESS;
static PaTzResult g_validation_key_get_by_type_return = PA_TZ_SUCCESS;
static const int kIncorrectCertificateVersion = 123456;
static const int kIncorrectAppNameLength = 101101;
static const int kIncorrectPaIdLength = 11;
static const int kIncorrectSignatureLength = 17;

extern "C" const PaConfig *GetConfig(void) {
  return &g_pa_config;
}

extern "C" PaTzResult RsaSignatureVerification(const uint8_t *data, size_t data_len,
                                               const uint8_t *signature, size_t singature_len,
                                               const RsaPublicKey *public_key,
                                               HashType hash_type) {
  return g_rsa_signature_verification_return;
}

extern "C" PaTzResult CryptoHmacSignatureVerification(const uint8_t *data,
                                                      size_t data_length,
                                                      HashType hash_type,
                                                      const uint8_t *signature,
                                                      size_t signature_length) {
  return g_crypto_hmac_signature_verification_return;
}

extern "C" int PaEncodePaData(const PaData_t *command, void *memory,
                              uint32_t *size_buffer) {
  return g_pa_tz_encoder_pa_data_return;
}

extern "C" PaTzResult CryptoHmacSignatureGenerate(const uint8_t *data,
                                                  size_t data_length,
                                                  HashType hash_type,
                                                  uint8_t *signature,
                                                  size_t *signature_length) {
  return g_crypto_hmac_signature_generate_return;  
}

extern "C" PaTzResult ValidationKeyGetByType(BuildType type, RsaPublicKey *key) {
  return g_validation_key_get_by_type_return;
}

extern "C" PaTzResult CryptoSha256(const uint8_t *data, size_t data_length, uint8_t *out_md) {
  return PA_TZ_SUCCESS;
}

class PaCertificateCreateTest : public ::testing::Test {
protected:
  virtual void SetUp() {
    g_id = 0;
    g_id_size = kPaIdLength;
    g_five_signature = 0;
    g_pa_app_name = 0;
    g_pa_app_name_size = 0;
    g_flags = 0;

    memset(&g_certificate, 0, sizeof(g_certificate));

    g_pa_tz_encoder_pa_data_return = 0;
    g_crypto_hmac_signature_generate_return = PA_TZ_SUCCESS;
  }

  virtual void TearDown() {
  }
};

class PaCertificateValidateTest : public ::testing::Test {
protected:
  virtual void SetUp() {
    g_pa_tz_encoder_pa_data_return = 0;
    g_rsa_signature_verification_return = PA_TZ_SUCCESS;
    g_crypto_hmac_signature_verification_return = PA_TZ_SUCCESS;
    g_validation_key_get_by_type_return = PA_TZ_SUCCESS;
    g_certificate.paData.paVersion = 1;
    g_certificate.paData.paId.size = kPaIdLength;
    g_certificate.paData.paId.buf = &g_id;
    g_certificate.paData.paAppName.size = 256;
    g_certificate.paData.paAppName.buf = &g_pa_app_name;
    g_certificate.paData.fiveSignatureHash.size = kSha256Size;

  }

  virtual void TearDown() {
  }
};


TEST_F(PaCertificateCreateTest, AllParametersValid) {
  PaTzResult result;
  PaCertificate_t *out_certificate = NULL;

  result = PaCertificateCreate(&g_id, g_id_size,
                               &g_five_signature, g_five_signature_size,
                               &g_pa_app_name, g_pa_app_name_size,
                               g_flags, &out_certificate);

  EXPECT_TRUE(PA_TZ_SUCCESS == result);
}

TEST_F(PaCertificateCreateTest, IdNull) {
  PaTzResult result;
  uint8_t *id_null = NULL;
  PaCertificate_t *out_certificate = NULL;

  result = PaCertificateCreate(id_null, g_id_size,
                               &g_five_signature, g_five_signature_size,
                               &g_pa_app_name, g_pa_app_name_size,
                               g_flags, &out_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(PaCertificateCreateTest, FiveSignatureNull) {
  PaTzResult result;
  uint8_t *five_signature_null = NULL;
  PaCertificate_t *out_certificate = NULL;

  result = PaCertificateCreate(&g_id, g_id_size,
                               five_signature_null, g_five_signature_size,
                               &g_pa_app_name, g_pa_app_name_size,
                               g_flags, &out_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(PaCertificateCreateTest, OutCertificateNull) {
  PaTzResult result;
  PaCertificate_t **out_certificate = NULL;

  result = PaCertificateCreate(&g_id, g_id_size,
                               &g_five_signature, g_five_signature_size,
                               &g_pa_app_name, g_pa_app_name_size,
                               g_flags, out_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(PaCertificateCreateTest, FailedPaTzEncoderPaData) {
  PaTzResult result;
  PaCertificate_t *out_certificate = NULL;

  g_pa_tz_encoder_pa_data_return = -1;

  result = PaCertificateCreate(&g_id, g_id_size,
                               &g_five_signature, g_five_signature_size,
                               &g_pa_app_name, g_pa_app_name_size,
                               g_flags, &out_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(PaCertificateCreateTest, FailedCryptoHmacSignatureGenerate) {
  PaTzResult result;
  PaCertificate_t *out_certificate = NULL;

  g_crypto_hmac_signature_generate_return = PA_TZ_GENERAL_ERROR;

  result = PaCertificateCreate(&g_id, g_id_size,
                               &g_five_signature, g_five_signature_size,
                               &g_pa_app_name, g_pa_app_name_size,
                               g_flags, &out_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(PaCertificateCreateTest, PaAppNameNull_Failed) {
  PaTzResult result;
  uint8_t *pa_app_name_null = NULL;
  PaCertificate_t *out_certificate = NULL;

  result = PaCertificateCreate(&g_id, g_id_size,
                               &g_five_signature, g_five_signature_size,
                               pa_app_name_null, g_pa_app_name_size,
                               g_flags, &out_certificate);

  ASSERT_EQ(PA_TZ_GENERAL_ERROR, result);
}

TEST_F(PaCertificateCreateTest, WrongPaAppNameLength_Failed) {
  PaTzResult result;
  size_t pa_app_name_size = kIncorrectAppNameLength;
  PaCertificate_t *out_certificate = NULL;

  result = PaCertificateCreate(&g_id, g_id_size,
                               &g_five_signature, g_five_signature_size,
                               &g_pa_app_name, pa_app_name_size,
                               g_flags, &out_certificate);

  ASSERT_EQ(PA_TZ_GENERAL_ERROR, result);
}

TEST_F(PaCertificateCreateTest, WrongPaIdLength_Failed) {
  PaTzResult result;
  size_t id_size = kIncorrectPaIdLength;
  PaCertificate_t *out_certificate = NULL;

  result = PaCertificateCreate(&g_id, id_size,
                               &g_five_signature, g_five_signature_size,
                               &g_pa_app_name, g_pa_app_name_size,
                               g_flags, &out_certificate);

  ASSERT_EQ(PA_TZ_GENERAL_ERROR, result);
}

TEST_F(PaCertificateValidateTest, AllParametersValid) {
  PaTzResult result;

  result = PaCertificateValidate(&g_certificate);

  EXPECT_TRUE(PA_TZ_SUCCESS == result);
}

TEST_F(PaCertificateValidateTest, CertificateNull) {
  PaTzResult result;
  PaCertificate_t *null_certificate = NULL;

  result = PaCertificateValidate(null_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(PaCertificateValidateTest, FailedPaTzEncoderPaData) {
  PaTzResult result;

  g_pa_tz_encoder_pa_data_return = -1;

  result = PaCertificateValidate(&g_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(PaCertificateValidateTest, FailedValidationKeyGetByType) {
  PaTzResult result;

  g_validation_key_get_by_type_return = PA_TZ_GENERAL_ERROR;

  result = PaCertificateValidate(&g_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(PaCertificateValidateTest, FailedRsaSignatureVerification) {
  PaTzResult result;

  g_rsa_signature_verification_return = PA_TZ_GENERAL_ERROR;

  result = PaCertificateValidate(&g_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(PaCertificateValidateTest, FailedCryptoHmacSignatureVerification) {
  PaTzResult result;

  g_certificate.paData.paFlags = 1 << PaFlagBits_bitHmac;
  g_crypto_hmac_signature_verification_return = PA_TZ_GENERAL_ERROR;

  result = PaCertificateValidate(&g_certificate);

  EXPECT_TRUE(PA_TZ_GENERAL_ERROR == result);
}

TEST_F(PaCertificateValidateTest, FailedIncorrectCertificateVersion) {
  PaTzResult result;

  g_certificate.paData.paVersion = kIncorrectCertificateVersion;

  result = PaCertificateValidate(&g_certificate);

  ASSERT_EQ(PA_TZ_GENERAL_ERROR, result);
}

TEST_F(PaCertificateValidateTest, PaAppNameTooLong_Failed) {
  PaTzResult result;

  g_certificate.paData.paAppName.size = kIncorrectAppNameLength;

  result = PaCertificateValidate(&g_certificate);

  ASSERT_EQ(PA_TZ_GENERAL_ERROR, result);
}

TEST_F(PaCertificateValidateTest, PaAppNameNull_Failed) {
  PaTzResult result;

  g_certificate.paData.paAppName.buf = NULL;

  result = PaCertificateValidate(&g_certificate);

  ASSERT_EQ(PA_TZ_GENERAL_ERROR, result);
}

TEST_F(PaCertificateValidateTest, PaAppIdNull_Failed) {
  PaTzResult result;

  g_certificate.paData.paId.buf = NULL;

  result = PaCertificateValidate(&g_certificate);

  ASSERT_EQ(PA_TZ_GENERAL_ERROR, result);
}

TEST_F(PaCertificateValidateTest, WrongPaIdLength_Failed) {
  PaTzResult result;

  g_certificate.paData.paId.size = kIncorrectPaIdLength;

  result = PaCertificateValidate(&g_certificate);

  ASSERT_EQ(PA_TZ_GENERAL_ERROR, result);
}

TEST_F(PaCertificateValidateTest, WrongSignatureLength_Failed) {
  PaTzResult result;

  g_certificate.paData.fiveSignatureHash.size = kIncorrectSignatureLength;

  result = PaCertificateValidate(&g_certificate);

  ASSERT_EQ(PA_TZ_GENERAL_ERROR, result);
}
