/* * Copyright (C) 2017 The Android Open Source Project * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "avb_atx_slot_verify.h" #include <libavb/avb_sha.h> #include <libavb/libavb.h> #include <libavb_atx/libavb_atx.h> /* Chosen to be generous but still require a huge number of increase operations * before exhausting the 64-bit space. */ static const uint64_t kRollbackIndexIncreaseThreshold = 1000000000; /* By convention, when a rollback index is not used the value remains zero. */ static const uint64_t kRollbackIndexNotUsed = 0; typedef struct _AvbAtxOpsContext { size_t key_version_location[AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS]; uint64_t key_version_value[AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS]; size_t next_key_version_slot; } AvbAtxOpsContext; typedef struct _AvbAtxOpsWithContext { AvbAtxOps atx_ops; AvbAtxOpsContext context; } AvbAtxOpsWithContext; /* Returns context associated with |atx_ops| returned by * setup_ops_with_context(). */ static AvbAtxOpsContext* get_ops_context(AvbAtxOps* atx_ops) { return &((AvbAtxOpsWithContext*)atx_ops)->context; } /* An implementation of AvbAtxOps::set_key_version that saves the key version * information to ops context data. */ static void save_key_version_to_context(AvbAtxOps* atx_ops, size_t rollback_index_location, uint64_t key_version) { AvbAtxOpsContext* context = get_ops_context(atx_ops); size_t offset = context->next_key_version_slot++; if (offset < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS) { context->key_version_location[offset] = rollback_index_location; context->key_version_value[offset] = key_version; } } /* Attaches context data to |existing_ops| and returns new ops. The * |ops_with_context| will be used to store the new combined ops and context. * The set_key_version function will be replaced in order to collect the key * version information in the context. */ static AvbAtxOps* setup_ops_with_context( const AvbAtxOps* existing_ops, AvbAtxOpsWithContext* ops_with_context) { avb_memset(ops_with_context, 0, sizeof(AvbAtxOpsWithContext)); ops_with_context->atx_ops = *existing_ops; // Close the loop on the circular reference. ops_with_context->atx_ops.ops->atx_ops = &ops_with_context->atx_ops; ops_with_context->atx_ops.set_key_version = save_key_version_to_context; return &ops_with_context->atx_ops; } /* Updates the stored rollback index value for |location| to match |value|. */ static AvbSlotVerifyResult update_rollback_index(AvbOps* ops, size_t location, uint64_t value) { AvbIOResult io_result = AVB_IO_RESULT_OK; uint64_t current_value; io_result = ops->read_rollback_index(ops, location, ¤t_value); if (io_result == AVB_IO_RESULT_ERROR_OOM) { return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; } else if (io_result != AVB_IO_RESULT_OK) { avb_error("Error getting rollback index for slot.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_IO; } if (current_value == value) { // No update necessary. return AVB_SLOT_VERIFY_RESULT_OK; } // The difference between the new and current value must not exceed the // increase threshold, and the value must not decrease. if (value - current_value > kRollbackIndexIncreaseThreshold) { avb_error("Rollback index value cannot increase beyond the threshold.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX; } // This should have been checked during verification, but check again here as // a safeguard. if (value < current_value) { avb_error("Rollback index value cannot decrease.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_ROLLBACK_INDEX; } io_result = ops->write_rollback_index(ops, location, value); if (io_result == AVB_IO_RESULT_ERROR_OOM) { return AVB_SLOT_VERIFY_RESULT_ERROR_OOM; } else if (io_result != AVB_IO_RESULT_OK) { avb_error("Error setting stored rollback index.\n"); return AVB_SLOT_VERIFY_RESULT_ERROR_IO; } return AVB_SLOT_VERIFY_RESULT_OK; } AvbSlotVerifyResult avb_atx_slot_verify( AvbAtxOps* atx_ops, const char* ab_suffix, AvbAtxLockState lock_state, AvbAtxSlotState slot_state, AvbAtxOemDataState oem_data_state, AvbSlotVerifyData** verify_data, uint8_t vbh_extension[AVB_SHA256_DIGEST_SIZE]) { const char* partitions_without_oem[] = {"boot", NULL}; const char* partitions_with_oem[] = {"boot", "oem_bootloader", NULL}; AvbSlotVerifyResult result = AVB_SLOT_VERIFY_RESULT_OK; size_t i = 0; AvbAtxOpsWithContext ops_with_context; atx_ops = setup_ops_with_context(atx_ops, &ops_with_context); result = avb_slot_verify(atx_ops->ops, (oem_data_state == AVB_ATX_OEM_DATA_NOT_USED) ? partitions_without_oem : partitions_with_oem, ab_suffix, (lock_state == AVB_ATX_UNLOCKED) ? AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR : AVB_SLOT_VERIFY_FLAGS_NONE, AVB_HASHTREE_ERROR_MODE_EIO, verify_data); if (result != AVB_SLOT_VERIFY_RESULT_OK || lock_state == AVB_ATX_UNLOCKED) { return result; } /* Compute the Android Things Verified Boot Hash (VBH) extension. */ avb_slot_verify_data_calculate_vbmeta_digest( *verify_data, AVB_DIGEST_TYPE_SHA256, vbh_extension); /* Increase rollback index values to match the verified slot. */ if (slot_state == AVB_ATX_SLOT_MARKED_SUCCESSFUL) { for (i = 0; i < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; i++) { uint64_t rollback_index_value = (*verify_data)->rollback_indexes[i]; if (rollback_index_value != kRollbackIndexNotUsed) { result = update_rollback_index(atx_ops->ops, i, rollback_index_value); if (result != AVB_SLOT_VERIFY_RESULT_OK) { goto out; } } } /* Also increase rollback index values for Android Things key version * locations. */ for (i = 0; i < AVB_MAX_NUMBER_OF_ROLLBACK_INDEX_LOCATIONS; i++) { size_t rollback_index_location = ops_with_context.context.key_version_location[i]; uint64_t rollback_index_value = ops_with_context.context.key_version_value[i]; if (rollback_index_value != kRollbackIndexNotUsed) { result = update_rollback_index( atx_ops->ops, rollback_index_location, rollback_index_value); if (result != AVB_SLOT_VERIFY_RESULT_OK) { goto out; } } } } out: if (result != AVB_SLOT_VERIFY_RESULT_OK) { avb_slot_verify_data_free(*verify_data); *verify_data = NULL; } return result; }