blob: 70e8be238af8831e61b57df92ff1cea2cf7002cf [file] [log] [blame]
/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <debug.h>
#include <dev/fbcon.h>
#include <target.h>
#include <mmc.h>
#include <partition_parser.h>
#include <platform.h>
#include <crypto_hash.h>
#include <malloc.h>
#include <sha.h>
#include <string.h>
#include <rand.h>
#include <stdlib.h>
#include <boot_verifier.h>
#include <image_verify.h>
#include <qtimer.h>
#include "scm.h"
#include "mdtp.h"
#include "mdtp_fs.h"
#define DIP_ENCRYPT (0)
#define DIP_DECRYPT (1)
#define MAX_CIPHER_DIP_SCM_CALLS (3)
#define MDTP_MAJOR_VERSION (0)
#define MDTP_MINOR_VERSION (2)
#define MDTP_CORRECT_PIN_DELAY_MSEC (1000)
/** Extract major version number from complete version. */
#define MDTP_GET_MAJOR_VERSION(version) ((version) >> 16)
/** UT defines **/
#define BAD_PARAM_SIZE 0
#define BAD_PARAM_VERIF_RATIO 101
#define BAD_HASH_MODE 10
/********************************************************************************/
static int mdtp_tzbsp_dec_verify_DIP(DIP_t *enc_dip, DIP_t *dec_dip, uint32_t *verified);
static int mdtp_tzbsp_enc_hash_DIP(DIP_t *dec_dip, DIP_t *enc_dip);
static void mdtp_tzbsp_disallow_cipher_DIP(void);
uint32_t g_mdtp_version = (((MDTP_MAJOR_VERSION << 16) & 0xFFFF0000) | (MDTP_MINOR_VERSION & 0x0000FFFF));
static int is_mdtp_activated = -1;
extern int check_aboot_addr_range_overlap(uintptr_t start, uint32_t size);
void free_mdtp_image(void);
/********************************************************************************/
/* Read the DIP from EMMC */
static int read_DIP(DIP_t *dip)
{
unsigned long long ptn = 0;
uint32_t actual_partition_size;
uint32_t block_size = mmc_get_device_blocksize();
int index = INVALID_PTN;
ASSERT(dip != NULL);
index = partition_get_index("dip");
ptn = partition_get_offset(index);
if(ptn == 0)
{
return -1;
}
actual_partition_size = ROUNDUP(sizeof(DIP_t), block_size);
if(mmc_read(ptn, (void *)dip, actual_partition_size))
{
dprintf(CRITICAL, "mdtp: read_DIP: ERROR, cannot read DIP info\n");
return -1;
}
dprintf(SPEW, "mdtp: read_DIP: SUCCESS, read %d bytes\n", actual_partition_size);
return 0;
}
/* Store the DIP into the EMMC */
static int write_DIP(DIP_t *dip)
{
unsigned long long ptn = 0;
uint32_t block_size = mmc_get_device_blocksize();
int index = INVALID_PTN;
ASSERT(dip != NULL);
index = partition_get_index("dip");
ptn = partition_get_offset(index);
if(ptn == 0)
{
return -1;
}
if(mmc_write(ptn, ROUNDUP(sizeof(DIP_t), block_size), (void *)dip))
{
dprintf(CRITICAL, "mdtp: write_DIP: ERROR, cannot write DIP info\n");
return -1;
}
dprintf(SPEW, "mdtp: write_DIP: SUCCESS, write %d bytes\n", ROUNDUP(sizeof(DIP_t), block_size));
return 0;
}
/* Deactivate MDTP by storing the default DIP into the EMMC */
static void write_deactivated_DIP()
{
DIP_t *enc_dip;
DIP_t *dec_dip;
int ret;
enc_dip = malloc(sizeof(DIP_t));
if (enc_dip == NULL)
{
dprintf(CRITICAL, "mdtp: write_deactivated_DIP: ERROR, cannot allocate DIP\n");
return;
}
dec_dip = malloc(sizeof(DIP_t));
if (dec_dip == NULL)
{
dprintf(CRITICAL, "mdtp: write_deactivated_DIP: ERROR, cannot allocate DIP\n");
free(enc_dip);
return;
}
memset(dec_dip, 0, sizeof(DIP_t));
dec_dip->status = DIP_STATUS_DEACTIVATED;
dec_dip->version = g_mdtp_version;
ret = mdtp_tzbsp_enc_hash_DIP(dec_dip, enc_dip);
if(ret < 0)
{
dprintf(CRITICAL, "mdtp: write_deactivated_DIP: ERROR, cannot cipher DIP\n");
goto out;
}
ret = write_DIP(enc_dip);
if(ret < 0)
{
dprintf(CRITICAL, "mdtp: write_deactivated_DIP: ERROR, cannot write DIP\n");
goto out;
}
out:
free(enc_dip);
free(dec_dip);
}
/* Validate a hash calculated on entire given partition */
static int verify_partition_single_hash(char *name, uint64_t size, DIP_hash_table_entry_t *hash_table)
{
unsigned char digest[HASH_LEN]={0};
unsigned long long ptn = 0;
int index = INVALID_PTN;
unsigned char *buf = (unsigned char *)target_get_scratch_address() + MDTP_SCRATCH_OFFSET;
uint32_t block_size = mmc_get_device_blocksize();
uint64_t actual_partition_size = ROUNDUP(size, block_size);
dprintf(SPEW, "mdtp: verify_partition_single_hash: %s, %llu\n", name, size);
ASSERT(name != NULL);
ASSERT(hash_table != NULL);
ASSERT(size > 0);
index = partition_get_index(name);
ptn = partition_get_offset(index);
if(ptn == 0) {
dprintf(CRITICAL, "mdtp: verify_partition_single_hash: %s: partition was not found\n", name);
return -1;
}
if (mmc_read(ptn, (void *)buf, actual_partition_size))
{
dprintf(CRITICAL, "mdtp: verify_partition_single_hash: %s: mmc_read() fail.\n", name);
return -1;
}
/* calculating the hash value using HW crypto */
target_crypto_init_params();
if(strcmp(name, "mdtp") == 0){
buf[0] = 0; // removes first byte
dprintf(INFO, "mdtp: verify_partition_single_hash: removes 1st byte\n");
}
hash_find(buf, size, digest, CRYPTO_AUTH_ALG_SHA256);
if (memcmp(digest, hash_table->hash, HASH_LEN))
{
dprintf(CRITICAL, "mdtp: verify_partition_single_hash: %s: Failed partition hash verification\n", name);
return -1;
}
dprintf(SPEW, "verify_partition_single_hash: %s: VERIFIED!\n", name);
return 0;
}
/* Validate a hash table calculated per block of a given partition */
static int verify_partition_block_hash(char *name,
uint64_t size,
uint32_t verify_num_blocks,
DIP_hash_table_entry_t *hash_table,
uint8_t *force_verify_block)
{
unsigned char digest[HASH_LEN]={0};
unsigned long long ptn = 0;
int index = INVALID_PTN;
unsigned char *buf = (unsigned char *)target_get_scratch_address() + MDTP_SCRATCH_OFFSET;
uint32_t bytes_to_read;
uint32_t block_num = 0;
uint32_t total_num_blocks = ((size - 1) / MDTP_FWLOCK_BLOCK_SIZE) + 1;
uint32_t rand_int;
uint32_t block_size = mmc_get_device_blocksize();
dprintf(SPEW, "mdtp: verify_partition_block_hash: %s, %llu\n", name, size);
ASSERT(name != NULL);
ASSERT(hash_table != NULL);
ASSERT(size > 0);
ASSERT(force_verify_block != NULL);
index = partition_get_index(name);
ptn = partition_get_offset(index);
if(ptn == 0) {
dprintf(CRITICAL, "mdtp: verify_partition_block_hash: %s: partition was not found\n", name);
return -1;
}
/* initiating parameters for hash calculation using HW crypto */
target_crypto_init_params();
if (check_aboot_addr_range_overlap((uintptr_t)buf, ROUNDUP(MDTP_FWLOCK_BLOCK_SIZE, block_size)))
{
dprintf(CRITICAL, "mdtp: verify_partition_block_hash: %s: image buffer address overlaps with aboot addresses.\n", name);
return -1;
}
while (MDTP_FWLOCK_BLOCK_SIZE * block_num < size)
{
if (*force_verify_block == 0)
{
if(scm_random((uintptr_t *)&rand_int, sizeof(rand_int)))
{
dprintf(CRITICAL,"mdtp: scm_call for random failed\n");
return -1;
}
/* Skip validation of this block with probability of verify_num_blocks / total_num_blocks */
if ((rand_int % total_num_blocks) >= verify_num_blocks)
{
block_num++;
hash_table += 1;
force_verify_block += 1;
dprintf(CRITICAL, "mdtp: verify_partition_block_hash: %s: skipped verification of block %d\n", name, block_num);
continue;
}
}
if ((size - (MDTP_FWLOCK_BLOCK_SIZE * block_num) < MDTP_FWLOCK_BLOCK_SIZE))
{
bytes_to_read = size - (MDTP_FWLOCK_BLOCK_SIZE * block_num);
} else
{
bytes_to_read = MDTP_FWLOCK_BLOCK_SIZE;
}
if (mmc_read(ptn + (MDTP_FWLOCK_BLOCK_SIZE * block_num), (void *)buf, ROUNDUP(bytes_to_read, block_size)))
{
dprintf(CRITICAL, "mdtp: verify_partition_block_hash: %s: mmc_read() fail.\n", name);
return -1;
}
/* calculating the hash value using HW */
hash_find(buf, bytes_to_read, digest, CRYPTO_AUTH_ALG_SHA256);
if (memcmp(digest, hash_table->hash, HASH_LEN))
{
dprintf(CRITICAL, "mdtp: verify_partition_block_hash: %s: Failed partition hash[%d] verification\n", name, block_num);
return -1;
}
block_num++;
hash_table += 1;
force_verify_block += 1;
}
dprintf(SPEW, "verify_partition_block_hash: %s: VERIFIED!\n", name);
return 0;
}
/* Validate the partition parameters read from DIP */
static int validate_partition_params(uint64_t size,
mdtp_fwlock_mode_t hash_mode,
uint32_t verify_ratio)
{
if (size == 0 || size > (uint64_t)MDTP_FWLOCK_BLOCK_SIZE * (uint64_t)MAX_BLOCKS ||
hash_mode > MDTP_FWLOCK_MODE_FILES || verify_ratio > 100)
{
dprintf(CRITICAL, "mdtp: validate_partition_params: error, size=%llu, hash_mode=%d, verify_ratio=%d\n",
size, hash_mode, verify_ratio);
return -1;
}
return 0;
}
/* Verify a given partitinon */
static int verify_partition(char *name,
uint64_t size,
mdtp_fwlock_mode_t hash_mode,
uint32_t verify_num_blocks,
DIP_hash_table_entry_t *hash_table,
uint8_t *force_verify_block)
{
if (hash_mode == MDTP_FWLOCK_MODE_SINGLE)
{
return verify_partition_single_hash(name, size, hash_table);
} else if (hash_mode == MDTP_FWLOCK_MODE_BLOCK || hash_mode == MDTP_FWLOCK_MODE_FILES)
{
return verify_partition_block_hash(name, size, verify_num_blocks, hash_table, force_verify_block);
}
/* Illegal value of hash_mode */
return -1;
}
static int validate_dip(DIP_t *dip)
{
uint8_t *dip_p;
ASSERT(dip != NULL);
/* Make sure DIP version is supported by current SW */
if (MDTP_GET_MAJOR_VERSION(dip->version) != MDTP_MAJOR_VERSION)
{
dprintf(CRITICAL, "mdtp: validate_dip: Wrong DIP version 0x%x\n", dip->version);
return -1;
}
/* Make sure that deactivated DIP content is as expected */
if (dip->status == DIP_STATUS_DEACTIVATED)
{
dip_p = (uint8_t*)&dip->mdtp_cfg;
while (dip_p < dip->hash)
{
if (*dip_p != 0)
{
dprintf(CRITICAL, "mdtp: validate_dip: error in deactivated DIP\n");
return -1;
}
dip_p++;
}
}
return 0;
}
/* Display the recovery UI in case mdtp image is corrupted */
static void display_mdtp_fail_recovery_ui(){
display_error_msg_mdtp();
}
/* Display the recovery UI to allow the user to enter the PIN and continue boot */
static void display_recovery_ui(mdtp_cfg_t *mdtp_cfg)
{
uint32_t pin_length = 0;
char entered_pin[MDTP_PIN_LEN+1] = {0};
uint32_t i;
int pin_mismatch = -1;
if (mdtp_cfg->enable_local_pin_authentication)
{
dprintf(SPEW, "mdtp: display_recovery_ui: Local deactivation enabled\n");
pin_length = strlen(mdtp_cfg->mdtp_pin.mdtp_pin);
if (pin_length != MDTP_PIN_LEN)
{
dprintf(CRITICAL, "mdtp: display_recovery_ui: Error, invalid PIN length\n");
display_error_msg(); /* This will never return */
}
// Set entered_pin to initial '0' string + null terminator
for (i=0; i<pin_length; i++)
{
entered_pin[i] = '0';
}
// Allow the user to enter the PIN as many times as he wishes
// (with INVALID_PIN_DELAY_MSECONDS after each failed attempt)
while (1)
{
pin_mismatch = pin_length;
get_pin_from_user(entered_pin, pin_length);
// Go over the entire PIN in any case, to prevent side-channel attacks
for (i=0; i<pin_length; i++)
{
// If current digit match, reduce 1 from pin_mismatch
pin_mismatch -= (((mdtp_cfg->mdtp_pin.mdtp_pin[i] ^ entered_pin[i]) == 0) ? 1 : 0);
}
if (0 == pin_mismatch)
{
// Valid PIN - deactivate and continue boot
dprintf(SPEW, "mdtp: display_recovery_ui: valid PIN, continue boot\n");
write_deactivated_DIP();
goto out;
}
else
{
// Invalid PIN - display an appropriate message (which also includes a wait
// for INVALID_PIN_DELAY_MSECONDS), and allow the user to try again
dprintf(CRITICAL, "mdtp: display_recovery_ui: ERROR, invalid PIN\n");
display_invalid_pin_msg();
}
}
}
else
{
dprintf(CRITICAL, "mdtp: display_recovery_ui: Local deactivation disabled, unable to display recovery UI\n");
display_error_msg(); /* This will never return */
}
out:
display_image_on_screen();
free_mdtp_image();
mdelay(MDTP_CORRECT_PIN_DELAY_MSEC);
}
/* Verify the boot or recovery partitions using boot_verifier. */
static int verify_ext_partition(mdtp_ext_partition_verification_t *ext_partition)
{
int ret = 0;
bool restore_to_orange = false;
unsigned long long ptn = 0;
int index = INVALID_PTN;
uint32_t bootstate;
/* If image was already verified in aboot, return its status */
if (ext_partition->integrity_state == MDTP_PARTITION_STATE_INVALID)
{
dprintf(CRITICAL, "mdtp: verify_ext_partition: image %s verified externally and failed.\n",
ext_partition->partition == MDTP_PARTITION_BOOT ? "boot" : "recovery");
return -1;
}
else if (ext_partition->integrity_state == MDTP_PARTITION_STATE_VALID)
{
dprintf(CRITICAL, "mdtp: verify_ext_partition: image %s verified externally succesfully.\n",
ext_partition->partition == MDTP_PARTITION_BOOT ? "boot" : "recovery");
return 0;
}
/* If image was not verified in aboot, verify it ourselves using boot_verifier. */
/* 1) Initialize keystore. We don't care about return value which is Verified Boot's state machine state. */
boot_verify_keystore_init();
/* 2) If boot_verifier is ORANGE, it will prevent verifying an image. So
* temporarly change boot_verifier state to BOOT_INIT.
*/
if (boot_verify_get_state() == ORANGE)
restore_to_orange = true;
boot_verify_send_event(BOOT_INIT);
switch (ext_partition->partition)
{
case MDTP_PARTITION_BOOT:
case MDTP_PARTITION_RECOVERY:
/* 3) Signature may or may not be at the end of the image. Read the signature if needed. */
if (!ext_partition->sig_avail)
{
if (check_aboot_addr_range_overlap((uintptr_t)(ext_partition->image_addr + ext_partition->image_size), ext_partition->page_size))
{
dprintf(CRITICAL, "ERROR: Signature read buffer address overlaps with aboot addresses.\n");
return -1;
}
index = partition_get_index(ext_partition->partition == MDTP_PARTITION_BOOT ? "boot" : "recovery");
ptn = partition_get_offset(index);
if(ptn == 0) {
dprintf(CRITICAL, "ERROR: partition %s not found\n",
ext_partition->partition == MDTP_PARTITION_BOOT ? "boot" : "recovery");
return -1;
}
if(mmc_read(ptn + ext_partition->image_size, (void *)(ext_partition->image_addr + ext_partition->image_size), ext_partition->page_size))
{
dprintf(CRITICAL, "ERROR: Cannot read %s image signature\n",
ext_partition->partition == MDTP_PARTITION_BOOT ? "boot" : "recovery");
return -1;
}
}
/* 4) Verify the image using its signature. */
ret = boot_verify_image((unsigned char *)ext_partition->image_addr,
ext_partition->image_size,
ext_partition->partition == MDTP_PARTITION_BOOT ? "/boot" : "/recovery",
&bootstate);
break;
default:
/* Only boot and recovery are legal here */
dprintf(CRITICAL, "ERROR: wrong partition %d\n", ext_partition->partition);
return -1;
}
if (ret)
{
dprintf(INFO, "mdtp: verify_ext_partition: image %s verified succesfully in MDTP.\n",
ext_partition->partition == MDTP_PARTITION_BOOT ? "boot" : "recovery");
}
else
{
dprintf(CRITICAL, "mdtp: verify_ext_partition: image %s verification failed in MDTP.\n",
ext_partition->partition == MDTP_PARTITION_BOOT ? "boot" : "recovery");
}
/* 5) Restore the right boot_verifier state upon exit. */
if (restore_to_orange)
{
boot_verify_send_event(DEV_UNLOCK);
}
return ret ? 0 : -1;
}
/* Verify all protected partitinons according to the DIP */
static void verify_all_partitions(DIP_t *dip,
mdtp_ext_partition_verification_t *ext_partition,
verify_result_t *verify_result)
{
int i;
int verify_failure = 0;
int verify_temp_result = 0;
int ext_partition_verify_failure = 0;
uint32_t total_num_blocks;
ASSERT(dip != NULL);
ASSERT(verify_result != NULL);
*verify_result = VERIFY_FAILED;
if (validate_dip(dip))
{
dprintf(CRITICAL, "mdtp: verify_all_partitions: failed DIP validation\n");
return;
}
if (dip->status == DIP_STATUS_DEACTIVATED)
{
*verify_result = VERIFY_SKIPPED;
return;
}
else
{
if (ext_partition->partition != MDTP_PARTITION_NONE)
{
for(i=0; i<MAX_PARTITIONS; i++)
{
verify_temp_result = 0;
if(dip->partition_cfg[i].lock_enabled && dip->partition_cfg[i].size)
{
total_num_blocks = ((dip->partition_cfg[i].size - 1) / MDTP_FWLOCK_BLOCK_SIZE);
if (validate_partition_params(dip->partition_cfg[i].size,
dip->partition_cfg[i].hash_mode,
dip->partition_cfg[i].verify_ratio))
{
dprintf(CRITICAL, "mdtp: verify_all_partitions: Wrong partition parameters\n");
verify_failure = TRUE;
break;
}
verify_temp_result |= (verify_partition(dip->partition_cfg[i].name,
dip->partition_cfg[i].size,
dip->partition_cfg[i].hash_mode,
(dip->partition_cfg[i].verify_ratio * total_num_blocks) / 100,
dip->partition_cfg[i].hash_table,
dip->partition_cfg[i].force_verify_block) != 0);
if((verify_temp_result) && (strcmp("mdtp",dip->partition_cfg[i].name) == 0)){
*verify_result = VERIFY_MDTP_FAILED;
}
verify_failure |= verify_temp_result;
}
}
ext_partition_verify_failure = verify_ext_partition(ext_partition);
if (verify_failure || ext_partition_verify_failure)
{
dprintf(CRITICAL, "mdtp: verify_all_partitions: Failed partition verification\n");
return;
}
}
is_mdtp_activated = 1;
}
*verify_result = VERIFY_OK;
return;
}
/* Verify the DIP and all protected partitions */
static void validate_DIP_and_firmware(mdtp_ext_partition_verification_t *ext_partition)
{
int ret;
DIP_t *enc_dip;
DIP_t *dec_dip;
uint32_t verified = 0;
verify_result_t verify_result;
uint32_t block_size = mmc_get_device_blocksize();
mdtp_cfg_t mdtp_cfg;
enc_dip = memalign(CACHE_LINE, ROUNDUP(sizeof(DIP_t), block_size));
if (enc_dip == NULL)
{
dprintf(CRITICAL, "mdtp: validate_DIP_and_firmware: ERROR, cannot allocate DIP\n");
display_error_msg(); /* This will never return */
}
dec_dip = memalign(CACHE_LINE, ROUNDUP(sizeof(DIP_t), block_size));
if (dec_dip == NULL)
{
dprintf(CRITICAL, "mdtp: validate_DIP_and_firmware: ERROR, cannot allocate DIP\n");
free(enc_dip);
display_error_msg(); /* This will never return */
}
/* Read the DIP holding the MDTP Firmware Lock state from the DIP partition */
ret = read_DIP(enc_dip);
if(ret < 0)
{
dprintf(CRITICAL, "mdtp: validate_DIP_and_firmware: ERROR, cannot read DIP\n");
display_error_msg(); /* This will never return */
}
/* Decrypt and verify the integrity of the DIP */
ret = mdtp_tzbsp_dec_verify_DIP(enc_dip, dec_dip, &verified);
if(ret < 0)
{
dprintf(CRITICAL, "mdtp: validate_DIP_and_firmware: ERROR, cannot verify DIP\n");
display_error_msg(); /* This will never return */
}
/* In case DIP integrity verification fails, notify the user and halt */
if(!verified)
{
dprintf(CRITICAL, "mdtp: validate_DIP_and_firmware: ERROR, corrupted DIP\n");
display_error_msg(); /* This will never return */
}
/* Verify the integrity of the partitions which are protected, according to the content of the DIP */
verify_all_partitions(dec_dip, ext_partition, &verify_result);
mdtp_cfg = dec_dip->mdtp_cfg;
/* Clear decrypted DIP since we don't need it anymore */
memset(dec_dip, 0, sizeof(DIP_t));
if (verify_result == VERIFY_OK)
{
dprintf(SPEW, "mdtp: validate_DIP_and_firmware: Verify OK\n");
}
else if (verify_result == VERIFY_SKIPPED)
{
dprintf(SPEW, "mdtp: validate_DIP_and_firmware: Verify skipped\n");
}
else if(verify_result == VERIFY_MDTP_FAILED)
{
dprintf(CRITICAL, "mdtp: validate_DIP_and_firmware: ERROR, corrupted mdtp image\n");
display_mdtp_fail_recovery_ui();
}
else /* VERIFY_FAILED */
{
dprintf(CRITICAL, "mdtp: validate_DIP_and_firmware: ERROR, corrupted firmware\n");
display_recovery_ui(&mdtp_cfg);
}
memset(&mdtp_cfg, 0, sizeof(mdtp_cfg));
free(enc_dip);
free(dec_dip);
return;
}
/********************************************************************************/
/** Entry point of the MDTP Firmware Lock.
* If needed, verify the DIP and all protected partitions.
* Allow passing information about partition verified using an external method
* (either boot or recovery). For boot and recovery, either use aboot's
* verification result, or use boot_verifier APIs to verify internally.
**/
void mdtp_fwlock_verify_lock(mdtp_ext_partition_verification_t *ext_partition)
{
int ret;
bool enabled;
if(mdtp_fs_init() != 0){
dprintf(CRITICAL, "mdtp: mdtp_img: ERROR, image file could not be loaded\n");
display_error_msg_mdtp(); /* This will never return */
}
/* sets the default value of this global to be MDTP not activated */
is_mdtp_activated = 0;
do {
if (ext_partition == NULL)
{
dprintf(CRITICAL, "mdtp: mdtp_fwlock_verify_lock: ERROR, external partition is NULL\n");
display_error_msg(); /* This will never return */
break;
}
ret = mdtp_fuse_get_enabled(&enabled);
if(ret)
{
dprintf(CRITICAL, "mdtp: mdtp_fwlock_verify_lock: ERROR, cannot get enabled fuse\n");
display_error_msg(); /* This will never return */
}
/* Continue with Firmware Lock verification only if enabled by eFuse */
if (enabled)
{
/* This function will handle firmware verification failure via UI */
validate_DIP_and_firmware(ext_partition);
}
} while (0);
/* Disallow CIPHER_DIP SCM call from this point, unless we are in recovery */
/* The recovery image will disallow CIPHER_DIP SCM call by itself. */
if (ext_partition->partition == MDTP_PARTITION_BOOT)
{
mdtp_tzbsp_disallow_cipher_DIP();
}
}
/********************************************************************************/
/** Indicates whether the MDTP is currently in ACTIVATED state **/
int mdtp_activated(bool * activated){
if(is_mdtp_activated < 0){
/* mdtp_fwlock_verify_lock was not called before, the value is not valid */
return is_mdtp_activated;
}
*activated = is_mdtp_activated;
return 0;
}
/********************************************************************************/
/* Decrypt a given DIP and verify its integrity */
static int mdtp_tzbsp_dec_verify_DIP(DIP_t *enc_dip, DIP_t *dec_dip, uint32_t *verified)
{
unsigned char hash[HASH_LEN];
SHA256_CTX sha256_ctx;
int ret;
ASSERT(enc_dip != NULL);
ASSERT(dec_dip != NULL);
ASSERT(verified != NULL);
arch_clean_invalidate_cache_range((addr_t)enc_dip, sizeof(DIP_t));
arch_invalidate_cache_range((addr_t)dec_dip, sizeof(DIP_t));
ret = mdtp_cipher_dip_cmd((uint8_t*)enc_dip, sizeof(DIP_t),
(uint8_t*)dec_dip, sizeof(DIP_t),
DIP_DECRYPT);
if (ret)
{
dprintf(CRITICAL, "mdtp: mdtp_tzbsp_dec_verify_DIP: ERROR, cannot cipher DIP\n");
*verified = 0;
memset(dec_dip, 0, sizeof(DIP_t));
return -1;
}
arch_invalidate_cache_range((addr_t)dec_dip, sizeof(DIP_t));
SHA256_Init(&sha256_ctx);
SHA256_Update(&sha256_ctx, dec_dip, sizeof(DIP_t) - HASH_LEN);
SHA256_Final(hash, &sha256_ctx);
if (memcmp(hash, dec_dip->hash, HASH_LEN))
{
*verified = 0;
memset(dec_dip, 0, sizeof(DIP_t));
}
else
{
*verified = 1;
}
return 0;
}
/* Encrypt a given DIP and calculate its integrity information */
static int mdtp_tzbsp_enc_hash_DIP(DIP_t *dec_dip, DIP_t *enc_dip)
{
SHA256_CTX sha256_ctx;
int ret;
ASSERT(dec_dip != NULL);
ASSERT(enc_dip != NULL);
SHA256_Init(&sha256_ctx);
SHA256_Update(&sha256_ctx, dec_dip, sizeof(DIP_t) - HASH_LEN);
SHA256_Final(dec_dip->hash, &sha256_ctx);
arch_clean_invalidate_cache_range((addr_t)dec_dip, sizeof(DIP_t));
arch_invalidate_cache_range((addr_t)enc_dip, sizeof(DIP_t));
ret = mdtp_cipher_dip_cmd((uint8_t*)dec_dip, sizeof(DIP_t),
(uint8_t*)enc_dip, sizeof(DIP_t),
DIP_ENCRYPT);
if (ret)
{
dprintf(CRITICAL, "mdtp: mdtp_tzbsp_enc_hash_DIP: ERROR, cannot cipher DIP\n");
return -1;
}
arch_invalidate_cache_range((addr_t)enc_dip, sizeof(DIP_t));
return 0;
}
/* Disallow the CIPHER_DIP SCM call */
static void mdtp_tzbsp_disallow_cipher_DIP(void)
{
DIP_t *dip;
int i;
dip = malloc(sizeof(DIP_t));
if (dip == NULL)
{
dprintf(CRITICAL, "mdtp: mdtp_tzbsp_disallow_cipher_DIP: ERROR, cannot allocate DIP\n");
/* Could not allocate DIP - stop device from booting */
display_error_msg(); /* This will never return */
}
/* Disallow the CIPHER_DIP SCM by calling it MAX_CIPHER_DIP_SCM_CALLS times */
for (i=0; i<MAX_CIPHER_DIP_SCM_CALLS; i++)
{
mdtp_tzbsp_enc_hash_DIP(dip, dip);
}
free(dip);
}
/********************************************************************************/
/** UT functions **/
/** Hashing fuctions UT **/
int mdtp_verify_hash_ut(){
unsigned char digest[HASH_LEN]={0};
unsigned int hash_expected_result = 0xD42B0A29;
char *buf = "MTDP LK UT hashing functions sanity check";
int size = 0;
DIP_hash_table_entry_t partition_hash_table;
uint8_t partition_force_verify_block = 0;
char ptr = buf[0];
while(ptr){
ptr = buf[++size];
}
//Bad partition name - single mode
if(verify_partition_single_hash("BAD_PARTITION", 1, &partition_hash_table) != -1){
dprintf(INFO, "verify_hash_ut: [FAIL (1)].\n");
return -1;
}
//Bad partition name - block mode
if(verify_partition_block_hash("BAD_PARTITION", 1, 1, &partition_hash_table, &partition_force_verify_block) != -1){
dprintf(INFO, "verify_hash_ut: [FAIL (2)].\n");
return -1;
}
//Hashing sanity check
hash_find((unsigned char*)buf, size, digest, CRYPTO_AUTH_ALG_SHA256);
unsigned int *hash_res = (unsigned int *)digest;
if (*hash_res != hash_expected_result){
dprintf(INFO, "verify_hash_ut: [FAIL (3)].\n");
return -1;
}
dprintf(INFO, "verify_hash_ut: [PASS].\n");
return 0;
}
/** Validate partitions params UT **/
int mdtp_validate_partition_params_ut(){
int partition_size = 10;
//Bad size
if(validate_partition_params(BAD_PARAM_SIZE, MDTP_FWLOCK_MODE_SINGLE, 1) != -1){
dprintf(INFO, "validate_partition_params_ut: [FAIL (1)].\n");
return -1;
}
//Bad size
if(validate_partition_params((uint64_t)MDTP_FWLOCK_BLOCK_SIZE * (uint64_t)MAX_BLOCKS + 1,
MDTP_FWLOCK_MODE_SINGLE, 1) != -1){
dprintf(INFO, "validate_partition_params_ut: [FAIL (2)].\n");
return -1;
}
//Bad verification ratio
if(validate_partition_params(partition_size, MDTP_FWLOCK_MODE_SIZE, BAD_PARAM_VERIF_RATIO) != -1){
dprintf(INFO, "validate_partition_params_ut: [FAIL (3)].\n");
return -1;
}
dprintf(INFO, "MDTP LK UT: validate_partition_params_ut [ PASS ]\n");
return 0;
}
/** Verify partition UT **/
int mdtp_verify_partition_ut(){
uint8_t partition_force_verify_block = 0;
DIP_hash_table_entry_t partition_hash_table;
int verify_num_blocks = 10,partition_size = 1;
//Unkown hashing mode
if(verify_partition("system", partition_size, BAD_HASH_MODE, verify_num_blocks,
&partition_hash_table, &partition_force_verify_block) != -1){
dprintf(INFO, "verify_partition_ut: Failed Test 1.\n");
dprintf(INFO, "MDTP LK UT: verify_partition_ut [ FAIL ]\n");
return -1;
}
dprintf(INFO, "MDTP LK UT: verify_partition_ut [ PASS ]\n");
return 0;
}