| /* 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; |
| } |
| |