/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * Tests for vboot_firmware library.
 */

#include <stdio.h>
#include <stdlib.h>

#include "gbb_header.h"
#include "host_common.h"
#include "load_firmware_fw.h"
#include "test_common.h"
#include "vboot_common.h"
#include "vboot_nvstorage.h"
#include "vboot_struct.h"

/* Mock data */
static VbCommonParams cparams;
static VbSelectFirmwareParams fparams;
static VbKeyBlockHeader vblock[2];
static VbFirmwarePreambleHeader mpreamble[2];
static VbNvContext vnc;
static uint8_t shared_data[VB_SHARED_DATA_MIN_SIZE];
static VbSharedDataHeader* shared = (VbSharedDataHeader*)shared_data;
static uint8_t gbb_data[sizeof(GoogleBinaryBlockHeader) + 2048];
static GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)gbb_data;
static RSAPublicKey data_key;
static uint32_t digest_size;
static uint8_t* digest_returned;
static uint8_t* digest_expect_ptr;
static int hash_fw_index;

/* Reset mock data (for use before each test) */
static void ResetMocks(void) {
  int i;

  Memset(&cparams, 0, sizeof(cparams));
  cparams.shared_data_blob = shared_data;
  cparams.gbb_data = gbb_data;

  Memset(&fparams, 0, sizeof(fparams));
  fparams.verification_block_A = vblock;
  fparams.verification_size_A = sizeof(VbKeyBlockHeader);
  fparams.verification_block_B = vblock + 1;
  fparams.verification_size_B = sizeof(VbKeyBlockHeader);

  Memset(vblock, 0, sizeof(vblock));
  Memset(mpreamble, 0, sizeof(mpreamble));
  for (i = 0; i < 2; i++) {
    /* Default verification blocks to working in all modes */
    vblock[i].key_block_flags = 0x0F;
    vblock[i].data_key.key_version = 2;
    /* Fix up offsets to preambles */
    vblock[i].key_block_size =
        (uint8_t*)(mpreamble + i) - (uint8_t*)(vblock + i);

    mpreamble[i].header_version_minor = 1;  /* Supports preamble flags */
    mpreamble[i].firmware_version = 4;
    /* Point kernel subkey to some data following the key header */
    PublicKeyInit(&mpreamble[i].kernel_subkey,
                  (uint8_t*)&mpreamble[i].body_signature, 20);
    mpreamble[i].kernel_subkey.algorithm = 7 + i;
    mpreamble[i].body_signature.data_size = 20000 + 1000 * i;
  }

  Memset(&vnc, 0, sizeof(vnc));
  VbNvSetup(&vnc);

  Memset(&shared_data, 0, sizeof(shared_data));
  VbSharedDataInit(shared, sizeof(shared_data));
  shared->fw_version_tpm = 0x00020004;

  Memset(&gbb_data, 0, sizeof(gbb_data));
  gbb->rootkey_offset = sizeof(GoogleBinaryBlockHeader);

  Memset(&data_key, 0, sizeof(data_key));

  digest_size = 1234;
  digest_returned = NULL;
  digest_expect_ptr = NULL;
  hash_fw_index = -1;
}

/****************************************************************************/
/* Mocked verification functions */

int KeyBlockVerify(const VbKeyBlockHeader* block, uint64_t size,
                   const VbPublicKey *key, int hash_only) {

  TEST_EQ(hash_only, 0, "  Don't verify firmware with hash");
  TEST_PTR_EQ(key, gbb_data + gbb->rootkey_offset, "  Verify with root key");
  TEST_NEQ(block==vblock || block==vblock+1, 0, "  Verify a valid key block");

  /* Mock uses header_version_major to hold return value */
  return block->header_version_major;
}

int VerifyFirmwarePreamble(const VbFirmwarePreambleHeader* preamble,
                           uint64_t size, const RSAPublicKey* key) {
  TEST_PTR_EQ(key, &data_key, "  Verify preamble data key");
  TEST_NEQ(preamble==mpreamble || preamble==mpreamble+1, 0,
           "  Verify a valid preamble");

  /* Mock uses header_version_major to hold return value */
  return preamble->header_version_major;
}

RSAPublicKey* PublicKeyToRSA(const VbPublicKey* key) {
  /* Mock uses algorithm!0 to mean invalid key */
  if (key->algorithm)
    return NULL;
  /* Mock uses data key len to hold number of alloc'd keys */
  data_key.len++;
  return &data_key;
}

void RSAPublicKeyFree(RSAPublicKey* key) {
  TEST_PTR_EQ(key, &data_key, "  RSA data key");
  data_key.len--;
}

void DigestInit(DigestContext* ctx, int sig_algorithm) {
  digest_size = 0;
}

void DigestUpdate(DigestContext* ctx, const uint8_t* data, uint32_t len) {
  TEST_PTR_EQ(data, digest_expect_ptr, "  Digesting expected data");
  digest_size += len;
}

uint8_t* DigestFinal(DigestContext* ctx) {
  digest_returned = (uint8_t*)VbExMalloc(4);
  return digest_returned;
}

VbError_t VbExHashFirmwareBody(VbCommonParams* cparams,
                               uint32_t firmware_index) {
  if (VB_SELECT_FIRMWARE_A == firmware_index)
    hash_fw_index = 0;
  else if (VB_SELECT_FIRMWARE_B == firmware_index)
    hash_fw_index = 1;
  else
    return VBERROR_INVALID_PARAMETER;

  digest_expect_ptr = (uint8_t*)(vblock + hash_fw_index) + 5;
  VbUpdateFirmwareBodyHash(
      cparams, digest_expect_ptr,
      mpreamble[hash_fw_index].body_signature.data_size - 1024);
  VbUpdateFirmwareBodyHash(cparams, digest_expect_ptr, 1024);

  /* If signature offset is 42, hash the wrong amount and return success */
  if (42 == mpreamble[hash_fw_index].body_signature.sig_offset) {
    VbUpdateFirmwareBodyHash(cparams, digest_expect_ptr, 4);
    return VBERROR_SUCCESS;
  }

  /* Otherwise, mocked function uses body signature offset as returned value */
  return mpreamble[hash_fw_index].body_signature.sig_offset;
}

int VerifyDigest(const uint8_t* digest, const VbSignature *sig,
                 const RSAPublicKey* key) {
  TEST_PTR_EQ(digest, digest_returned, "Verifying expected digest");
  TEST_PTR_EQ(key, &data_key, "Verifying using data key");
  TEST_PTR_EQ(sig, &mpreamble[hash_fw_index].body_signature, "Verifying sig");
  /* Mocked function uses sig size as return value for verifying digest */
  return sig->sig_size;
}

/****************************************************************************/
/* Test LoadFirmware() and check expected return value and recovery reason */
static void TestLoadFirmware(VbError_t expected_retval,
                             uint8_t expected_recovery, const char* desc) {
  uint32_t rr = 256;

  TEST_EQ(LoadFirmware(&cparams, &fparams, &vnc), expected_retval, desc);
  VbNvGet(&vnc, VBNV_RECOVERY_REQUEST, &rr);
  TEST_EQ(rr, expected_recovery, "  recovery request");
  TEST_EQ(data_key.len, 0, "  Data key free must be paired with alloc");
}

/****************************************************************************/

static void LoadFirmwareTest(void) {
  uint32_t u;

  /* Require GBB */
  ResetMocks();
  cparams.gbb_data = NULL;
  TestLoadFirmware(VBERROR_INVALID_GBB, VBNV_RECOVERY_RO_UNSPECIFIED,
                   "No GBB");

  /* Key block flags must match */
  /* Normal boot */
  ResetMocks();
  vblock[0].key_block_flags = KEY_BLOCK_FLAG_DEVELOPER_1;
  vblock[1].key_block_flags =
      KEY_BLOCK_FLAG_DEVELOPER_0 | KEY_BLOCK_FLAG_RECOVERY_1;
  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
                    VBSD_LF_CHECK_REC_MISMATCH),
                   "Flags mismatch dev=0");
  TEST_EQ(shared->flags & VBSD_LF_DEV_SWITCH_ON, 0,
          "Dev flag in shared.flags dev=0");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_DEV_MISMATCH,
          "Key block flag mismatch for dev=0");
  TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_REC_MISMATCH,
          "Key block flag mismatch for rec=0");
  /* Developer boot */
  ResetMocks();
  shared->flags |= VBSD_BOOT_DEV_SWITCH_ON;
  vblock[0].key_block_flags = KEY_BLOCK_FLAG_DEVELOPER_0;
  vblock[1].key_block_flags =
      KEY_BLOCK_FLAG_DEVELOPER_1 | KEY_BLOCK_FLAG_RECOVERY_1;
  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
                    VBSD_LF_CHECK_REC_MISMATCH),
                   "Flags mismatch dev=1");
  TEST_NEQ(shared->flags & VBSD_LF_DEV_SWITCH_ON, 0,
          "Dev flag in shared.flags dev=1");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_DEV_MISMATCH,
          "Key block flag mismatch for dev=1");
  TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_REC_MISMATCH,
          "Key block flag mismatch for rec=1");

  /* Test key block verification with A and key version rollback with B */
  ResetMocks();
  vblock[0].header_version_major = 1;  /* Simulate failure */
  vblock[1].data_key.key_version = 1;  /* Simulate rollback */
  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
                    VBSD_LF_CHECK_KEY_ROLLBACK),
                   "Key block invalid / key version rollback");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VERIFY_KEYBLOCK,
          "Key block invalid");
  TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_KEY_ROLLBACK,
          "Key version rollback ");
  TEST_EQ(shared->fw_version_lowest, 0, "Lowest valid version");
  TEST_EQ(shared->fw_version_tpm, 0x20004, "TPM version");

  /* Test invalid key version with A and bad data key with B */
  ResetMocks();
  vblock[0].data_key.key_version = 0x10003;  /* Version > 0xFFFF is invalid */
  vblock[1].data_key.algorithm = 1;  /* Simulate invalid data key */
  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
                    VBSD_LF_CHECK_DATA_KEY_PARSE),
                   "Key version overflow / invalid data key");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_KEY_ROLLBACK,
          "Key version overflow");
  TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_DATA_KEY_PARSE,
          "Data key invalid");

  /* Test invalid key version with GBB bypass-rollback flag */
  ResetMocks();
  vblock[0].data_key.key_version = 1;  /* Simulate rollback */
  gbb->flags = GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK;
  TestLoadFirmware(VBERROR_SUCCESS, 0, "Key version check + GBB override");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VALID,
          "Key version rollback + GBB override");

  /* Test invalid preamble with A */
  ResetMocks();
  mpreamble[0].header_version_major = 1;  /* Simulate failure */
  vblock[1].key_block_flags = 0;  /* Invalid */
  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
                    VBSD_LF_CHECK_VERIFY_PREAMBLE),
                   "Preamble invalid");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VERIFY_PREAMBLE,
          "Preamble invalid A");

  /* Test invalid firmware versions */
  ResetMocks();
  mpreamble[0].firmware_version = 3;  /* Simulate rollback */
  mpreamble[1].firmware_version = 0x10001;  /* Check overflow */
  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
                    VBSD_LF_CHECK_FW_ROLLBACK),
                   "Firmware version check");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_FW_ROLLBACK,
          "Firmware version rollback");
  TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_FW_ROLLBACK,
          "Firmware version overflow");

  /* Test invalid firmware versions with GBB bypass-rollback flag */
  ResetMocks();
  mpreamble[0].firmware_version = 3;  /* Simulate rollback */
  mpreamble[1].firmware_version = 0x10001;  /* Check overflow */
  gbb->flags = GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK;
  TestLoadFirmware(VBERROR_SUCCESS, 0, "Firmware version check + GBB bypass");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VALID,
          "Firmware version rollback + GBB override");
  TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_HEADER_VALID,
          "Firmware version overflow + GBB override");

  /* Test RO normal with A */
  ResetMocks();
  mpreamble[0].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
  vblock[1].key_block_flags = 0;  /* Invalid */
  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
                    VBSD_LF_CHECK_NO_RO_NORMAL),
                   "Preamble asked for RO normal but fw doesn't support it");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_NO_RO_NORMAL,
          "No RO normal A");

  /* If RO normal is supported, don't need to verify the firmware body */
  ResetMocks();
  mpreamble[0].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
  /* Mock bad sig, to ensure we didn't use it */
  mpreamble[0].body_signature.sig_size = 1;
  shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT;
  vblock[1].key_block_flags = 0;  /* Invalid */
  TestLoadFirmware(VBERROR_SUCCESS, 0, "RO normal A");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VALID, "RO normal A valid");
  TEST_EQ(shared->firmware_index, 0, "Boot A shared index");
  TEST_EQ(shared->fw_keyblock_flags, vblock[0].key_block_flags,
          "Copy key block flags");
  TEST_EQ(shared->kernel_subkey.algorithm, 7, "Copy kernel subkey");

  /* If both A and B are valid and same version as TPM, A is selected
   * and B isn't attempted. */
  ResetMocks();
  mpreamble[0].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
  mpreamble[1].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
  shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT;
  TestLoadFirmware(VBERROR_SUCCESS, 0, "Check A then B");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VALID, "RO normal A valid");
  TEST_EQ(shared->check_fw_b_result, 0, "RO normal B not checked ");
  TEST_EQ(shared->firmware_index, 0, "Boot A");
  TEST_EQ(shared->flags & VBSD_FWB_TRIED, 0, "Didn't try firmware B");
  TEST_EQ(shared->kernel_subkey.algorithm, 7, "Copy kernel subkey");
  /* But if try B flag is set, B is selected and A not attempted */
  ResetMocks();
  mpreamble[0].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
  mpreamble[1].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
  shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT;
  VbNvSet(&vnc, VBNV_TRY_B_COUNT, 5);
  TestLoadFirmware(VBERROR_SUCCESS, 0, "Check B then A");
  TEST_EQ(shared->check_fw_a_result, 0, "RO normal A not checked ");
  TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_VALID, "RO normal B valid");
  TEST_EQ(shared->firmware_index, 1, "Boot B");
  TEST_NEQ(shared->flags & VBSD_FWB_TRIED, 0, "Tried firmware B");
  TEST_EQ(shared->kernel_subkey.algorithm, 8, "Copy kernel subkey");
  VbNvGet(&vnc, VBNV_TRY_B_COUNT, &u);
  TEST_EQ(u, 4, "Used up a try");

  /* If both A and B are valid and grater version than TPM, A is
   * selected and B preamble (but not body) is verified. */
  ResetMocks();
  mpreamble[0].flags = VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL;
  mpreamble[1].flags = 0;
  mpreamble[0].firmware_version = 5;
  mpreamble[1].firmware_version = 6;
  shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT;
  TestLoadFirmware(VBERROR_SUCCESS, 0, "Check A then B advancing version");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VALID, "RO normal A valid");
  TEST_EQ(shared->check_fw_b_result, VBSD_LF_CHECK_HEADER_VALID,
          "RO normal B header valid");
  TEST_EQ(shared->firmware_index, 0, "Boot A");
  TEST_EQ(shared->fw_keyblock_flags, vblock[0].key_block_flags, "Key block A");
  TEST_EQ(shared->kernel_subkey.algorithm, 7, "Copy kernel subkey");
  TEST_EQ(shared->fw_version_lowest, 0x20005, "Lowest valid version");
  TEST_EQ(shared->fw_version_tpm, 0x20005, "TPM version advanced");

  /* Verify firmware data */
  ResetMocks();
  vblock[1].key_block_flags = 0;  /* Invalid */
  TestLoadFirmware(VBERROR_SUCCESS, 0, "Verify firmware body");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VALID,
          "Firmware body A valid");
  TEST_EQ(shared->firmware_index, 0, "Boot A shared index");
  TEST_EQ(hash_fw_index, 0, "Hash firmware data A");
  TEST_EQ(digest_size, mpreamble[0].body_signature.data_size,
          "Verified all data expected");

  /* Test error getting firmware body */
  ResetMocks();
  mpreamble[0].body_signature.sig_offset = VBERROR_UNKNOWN;
  vblock[1].key_block_flags = 0;  /* Invalid */
  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
                    VBSD_LF_CHECK_GET_FW_BODY),
                   "Error getting firmware body");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_GET_FW_BODY,
          "Firmware body A");

  /* Test digesting the wrong amount */
  ResetMocks();
  mpreamble[0].body_signature.sig_offset = 42;  /* Mock hashing wrong amount */
  vblock[1].key_block_flags = 0;  /* Invalid */
  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
                    VBSD_LF_CHECK_HASH_WRONG_SIZE),
                   "Hash wrong size");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_HASH_WRONG_SIZE,
          "Firmware hash wrong size A");

  /* Test bad signature */
  ResetMocks();
  mpreamble[0].body_signature.sig_size = 1;  /* Mock bad sig */
  vblock[1].key_block_flags = 0;  /* Invalid */
  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
                    VBSD_LF_CHECK_VERIFY_BODY),
                   "Bad signature");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VERIFY_BODY,
          "Bad signature A");

  /* Test unable to store kernel data key */
  ResetMocks();
  mpreamble[0].kernel_subkey.key_size = VB_SHARED_DATA_MIN_SIZE + 1;
  vblock[1].key_block_flags = 0;  /* Invalid */
  TestLoadFirmware(VBERROR_LOAD_FIRMWARE,
                   (VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN +
                    VBSD_LF_CHECK_VALID),
                   "Kernel key too big");
  TEST_EQ(shared->check_fw_a_result, VBSD_LF_CHECK_VALID,
          "Kernel key too big");
}


/* disable MSVC warnings on unused arguments */
__pragma(warning (disable: 4100))

int main(int argc, char* argv[]) {
  int error_code = 0;

  LoadFirmwareTest();

  if (!gTestSuccess)
    error_code = 255;

  return error_code;
}
