Secure boot v2 support for ESP32-S2

This commit is contained in:
Supreet Deshpande
2020-03-04 00:28:18 +05:30
committed by bot
parent ba717a298f
commit e640e148cf
11 changed files with 509 additions and 74 deletions

View File

@@ -11,43 +11,311 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp_secure_boot.h"
#include <string.h>
#include "esp_log.h"
#include "esp_secure_boot.h"
#include "soc/efuse_reg.h"
#include "bootloader_flash.h"
#include "bootloader_sha.h"
#include "bootloader_utility.h"
#include "esp_rom_crc.h"
#include "esp_efuse.h"
#include "esp_efuse_table.h"
#include "esp32s2/rom/secure_boot.h"
static const char *TAG = "secure_boot";
static const char *TAG = "secure_boot_v2";
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
esp_err_t esp_secure_boot_permanently_enable(void)
#define SIG_BLOCK_MAGIC_BYTE 0xe7
#define CRC_SIGN_BLOCK_LEN 1196
#define SIG_BLOCK_PADDING 4096
#define DIGEST_LEN 32
/* A signature block is valid when it has correct magic byte, crc and image digest. */
static esp_err_t validate_signature_block(int block_num, const ets_secure_boot_signature_t *sig_block, uint8_t *digest)
{
uint8_t hash[32];
if (esp_rom_efuse_is_secure_boot_enabled())
{
ESP_LOGI(TAG, "secure boot is already enabled, continuing..");
uint32_t crc = esp_rom_crc32_le(0, (uint8_t *)&sig_block->block[block_num], CRC_SIGN_BLOCK_LEN);
if (sig_block->block[block_num].magic_byte != SIG_BLOCK_MAGIC_BYTE) {
// All signature blocks have been parsed, no new signature block present.
ESP_LOGD(TAG, "Signature block(%d) invalid/absent.", block_num);
return ESP_FAIL;
}
if (sig_block->block[block_num].block_crc != crc) {
ESP_LOGE(TAG, "Magic byte correct but incorrect crc.");
return ESP_FAIL;
}
if (memcmp(digest, sig_block->block[block_num].image_digest, DIGEST_LEN)) {
ESP_LOGE(TAG, "Magic byte & CRC correct but incorrect image digest.");
return ESP_FAIL;
} else {
ESP_LOGD(TAG, "valid signature block(%d) found", block_num);
return ESP_OK;
}
ESP_LOGI(TAG, "Verifying bootloader signature...\n");
int r = ets_secure_boot_verify_bootloader(hash, false);
if (r != ESP_OK) {
ESP_LOGE(TAG, "Failed to verify bootloader signature");
return r;
return ESP_FAIL;
}
// Inputs the flash_offset and length of an image(app or bootloader), validates & verifies its secure boot v2 signature.
// Generates the public key digests of the valid public keys in a signature block and writes it into trusted_keys.
// The key_digests in trusted keys whose signature blocks are invalid will be set to NULL.
static esp_err_t secure_boot_v2_digest_generate(uint32_t flash_offset, uint32_t flash_size, ets_secure_boot_key_digests_t * const trusted_keys)
{
int i = 0;
esp_err_t ret = ESP_FAIL;
uint8_t image_digest[DIGEST_LEN] = {0};
uint8_t public_key_digests[SECURE_BOOT_NUM_BLOCKS][DIGEST_LEN];
size_t sig_block_addr = flash_offset + ALIGN_UP(flash_size, FLASH_SECTOR_SIZE);
ret = bootloader_sha256_flash_contents(flash_offset, sig_block_addr - flash_offset, image_digest);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "error generating image digest, %d", ret);
return ret;
}
esp_efuse_batch_write_begin(); /* Batch all efuse writes at the end of this function */
ESP_LOGD(TAG, "reading signature block");
const ets_secure_boot_signature_t *sig_block = bootloader_mmap(sig_block_addr, sizeof(ets_secure_boot_signature_t));
if (sig_block == NULL) {
ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", sig_block_addr, sizeof(ets_secure_boot_signature_t));
return ESP_FAIL;
}
for (i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
ret = validate_signature_block(i, sig_block, image_digest);
if (ret != ESP_OK) {
break;
}
/* Generating the SHA of the public key components in the signature block */
bootloader_sha256_handle_t sig_block_sha;
sig_block_sha = bootloader_sha256_start();
bootloader_sha256_data(sig_block_sha, &sig_block->block[i].key, sizeof(sig_block->block[i].key));
bootloader_sha256_finish(sig_block_sha, public_key_digests[i]);
memcpy((uint8_t *)trusted_keys->key_digests[0], public_key_digests[i], DIGEST_LEN); // Overwriting 0th index to verify each valid signature block
/* A signature block is verified when it is valid and the signature in its signature block can be verified with a valid public key */
uint8_t verified_digest[DIGEST_LEN] = {0};
ets_secure_boot_status_t r = ets_secure_boot_verify_signature(sig_block, image_digest, trusted_keys, verified_digest);
if (r != SB_SUCCESS) {
ESP_LOGE(TAG, "Secure boot key (%d) verification failed.", i);
ret = ESP_FAIL;
goto exit;
}
}
// At least 1 verified signature block found.
if (i > 0) {
// validate_signature_block returns ESP_FAIL when a sig block is absent, which isn't an error.
while (--i) {
trusted_keys->key_digests[i] = public_key_digests[i];
}
ret = ESP_OK;
}
exit:
/* Set the pointer to an invalid/absent block to NULL */
while (i < SECURE_BOOT_NUM_BLOCKS) {
trusted_keys->key_digests[i] = NULL;
i++;
}
ESP_LOGI(TAG, "Secure boot verification success.");
bootloader_munmap(sig_block);
return ret;
}
/* Traverses ets_secure_boot_key_digests_t to find the number of non-null key_digests */
static uint8_t get_signature_block_count(ets_secure_boot_key_digests_t * const trusted_keys) {
uint8_t bootloader_sig_block_count = 0;
for (uint8_t i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
if (trusted_keys->key_digests[i] != NULL) {
bootloader_sig_block_count++;
for (uint8_t j = 0; j < DIGEST_LEN ; j++) {
ESP_LOGD(TAG, "Secure Boot Digest %d: 0x%x", i, *(((uint8_t *)trusted_keys->key_digests[i]) + j));
}
}
}
return bootloader_sig_block_count;
}
esp_err_t esp_secure_boot_v2_permanently_enable(const esp_image_metadata_t *image_data)
{
ESP_LOGI(TAG, "enabling secure boot v2 - ESP32-S2...");
if (esp_secure_boot_enabled()) {
ESP_LOGI(TAG, "secure boot v2 is already enabled, continuing..");
return ESP_OK;
}
esp_err_t ret;
/* Verify the bootloader */
esp_image_metadata_t bootloader_data = { 0 };
ret = esp_image_verify_bootloader_data(&bootloader_data);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "bootloader image appears invalid! error %d", ret);
return ret;
}
/* Check if secure boot digests are present */
bool has_secure_boot_digest = ets_efuse_find_purpose(ETS_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST0, NULL);
has_secure_boot_digest |= ets_efuse_find_purpose(ETS_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST1, NULL);
has_secure_boot_digest |= ets_efuse_find_purpose(ETS_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST2, NULL);
ESP_LOGI(TAG, "Secure boot digests %s", has_secure_boot_digest ? "already present":"absent, generating..");
ets_efuse_clear_program_registers();
uint8_t i, j, bootloader_sig_block_count = 0;
if (!has_secure_boot_digest) {
ets_secure_boot_key_digests_t boot_trusted_keys, app_trusted_keys;
uint8_t boot_trusted_key_data[SECURE_BOOT_NUM_BLOCKS][DIGEST_LEN] = {0}, app_trusted_key_data[SECURE_BOOT_NUM_BLOCKS][DIGEST_LEN] = {0};
for(i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
boot_trusted_keys.key_digests[i] = boot_trusted_key_data[i];
app_trusted_keys.key_digests[i] = app_trusted_key_data[i];
}
/* Generate the bootloader public key digests */
ret = secure_boot_v2_digest_generate(bootloader_data.start_addr, bootloader_data.image_len - SIG_BLOCK_PADDING, &boot_trusted_keys);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Public key digest generation failed.");
return ret;
}
bootloader_sig_block_count = get_signature_block_count(&boot_trusted_keys);
if (bootloader_sig_block_count <= 0) {
ESP_LOGI(TAG, "No valid signature blocks found. %d signature block(s) found.", bootloader_sig_block_count);
return ESP_FAIL;
}
ESP_LOGI(TAG, "%d signature block(s) found appended to the bootloader.", bootloader_sig_block_count);
int unused_key_slots = ets_efuse_count_unused_key_blocks();
if (bootloader_sig_block_count > unused_key_slots) {
ESP_LOGE(TAG, "Bootloader signatures(%d) more than available key slots(%d).", bootloader_sig_block_count, unused_key_slots);
return ESP_FAIL;
}
for (i = 0; i < SECURE_BOOT_NUM_BLOCKS; i++) {
if (boot_trusted_keys.key_digests[i] == NULL) {
break;
}
const uint32_t secure_boot_key_purpose[SECURE_BOOT_NUM_BLOCKS] = { ETS_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST0,
ETS_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST1, ETS_EFUSE_KEY_PURPOSE_SECURE_BOOT_DIGEST2 };
ets_efuse_block_t block = ets_efuse_find_unused_key_block();
if (block == ETS_EFUSE_BLOCK_MAX) {
ESP_LOGE(TAG, "Key blocks not available.");
return ESP_FAIL;
}
int r = ets_efuse_write_key(block, secure_boot_key_purpose[i], boot_trusted_keys.key_digests[i], DIGEST_LEN);
if (r != 0) {
ESP_LOGE(TAG, "Failed to write efuse block %d with purpose %d. Can't continue.", block, secure_boot_key_purpose[i]);
return ESP_FAIL;
}
r = esp_efuse_set_write_protect(block);
if (r != 0) {
ESP_LOGE(TAG, "Failed to write protect efuse block %d. Can't continue.", block);
return ESP_FAIL;
}
}
/* Generate the application public key digests */
ret = secure_boot_v2_digest_generate(image_data->start_addr, image_data->image_len - SIG_BLOCK_PADDING, &app_trusted_keys);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Application signature block is invalid.");
return ret;
}
int app_sig_block_count = get_signature_block_count(&app_trusted_keys);
ESP_LOGI(TAG, "%d signature block(s) found appended to the application.", app_sig_block_count);
/* Confirm if atleast one the public key from the application matches a public key in the bootloader
(Also, ensure if that public revoke bit is not set for the matched key) */
bool match = false;
const uint32_t revoke_bits[SECURE_BOOT_NUM_BLOCKS] = { EFUSE_SECURE_BOOT_KEY_REVOKE0,
EFUSE_SECURE_BOOT_KEY_REVOKE1, EFUSE_SECURE_BOOT_KEY_REVOKE2 };
for (i = 0; i < SECURE_BOOT_NUM_BLOCKS && match == false; i++) {
if (REG_GET_BIT(EFUSE_RD_REPEAT_DATA1_REG, revoke_bits[i])) {
ESP_LOGI(TAG, "Key block(%d) has been revoked.", i);
continue; // skip if the key block is revoked
}
for (j = 0; j < SECURE_BOOT_NUM_BLOCKS; j++) {
if (!memcmp(boot_trusted_key_data[i], app_trusted_key_data[j], DIGEST_LEN)) {
ESP_LOGI(TAG, "Application key(%d) matches with bootloader key(%d).", j, i);
match = true;
break;
}
}
}
if (match == false) {
ESP_LOGE(TAG, "No application key digest matches the bootloader key digest.");
return ESP_FAIL;
}
/* Revoke the empty signature blocks */
if (bootloader_sig_block_count < SECURE_BOOT_NUM_BLOCKS) {
/* The revocation index can be 0, 1, 2. Bootloader count can be 1,2,3. */
for (uint8_t i = bootloader_sig_block_count; i < SECURE_BOOT_NUM_BLOCKS; i++) {
ESP_LOGI(TAG, "Revoking empty key digest slot (%d)...", i);
ets_secure_boot_revoke_public_key_digest(i);
}
}
}
esp_err_t err = esp_efuse_batch_write_begin();
if (err != ESP_OK) {
ESP_LOGI(TAG, "Error batch programming security eFuses.");
return err;
}
__attribute__((unused)) static const uint8_t enable = 1;
esp_efuse_write_field_bit(ESP_EFUSE_SECURE_BOOT_EN);
esp_efuse_write_field_bit(ESP_EFUSE_DIS_BOOT_REMAP);
esp_efuse_write_field_bit(ESP_EFUSE_DIS_LEGACY_SPI_BOOT);
// TODO: also disable JTAG here, etc
#ifdef CONFIG_SECURE_ENABLE_SECURE_ROM_DL_MODE
ESP_LOGI(TAG, "Enabling Security download mode...");
esp_efuse_write_field_bit(ESP_EFUSE_ENABLE_SECURITY_DOWNLOAD);
#else
ESP_LOGW(TAG, "Not enabling Security download mode - SECURITY COMPROMISED");
#endif
esp_err_t err = esp_efuse_batch_write_commit();
#ifndef CONFIG_SECURE_BOOT_ALLOW_JTAG
ESP_LOGI(TAG, "Disable hardware & software JTAG...");
esp_efuse_write_field_bit(ESP_EFUSE_HARD_DIS_JTAG);
esp_efuse_write_field_bit(ESP_EFUSE_SOFT_DIS_JTAG);
#else
ESP_LOGW(TAG, "Not disabling JTAG - SECURITY COMPROMISED");
#endif
if (err == ESP_OK) {
assert(esp_rom_efuse_is_secure_boot_enabled());
ESP_LOGI(TAG, "Secure boot permanently enabled");
#ifdef CONFIG_SECURE_BOOT_ENABLE_AGGRESSIVE_KEY_REVOKE
esp_efuse_write_field_bit(ESP_EFUSE_SECURE_BOOT_AGGRESSIVE_REVOKE);
#endif
esp_efuse_write_field_bit(ESP_EFUSE_SECURE_BOOT_EN);
err = esp_efuse_batch_write_commit();
if (err != ESP_OK) {
ESP_LOGI(TAG, "Error programming security eFuses.");
return err;
}
#ifdef CONFIG_SECURE_BOOT_ENABLE_AGGRESSIVE_KEY_REVOKE
assert(ets_efuse_secure_boot_aggressive_revoke_enabled());
#endif
assert(esp_rom_efuse_is_secure_boot_enabled());
ESP_LOGI(TAG, "Secure boot permanently enabled");
return ESP_OK;
}

View File

@@ -13,29 +13,32 @@
// limitations under the License.
#include "sdkconfig.h"
#include <string.h>
#include "esp_fault.h"
#include "bootloader_flash.h"
#include "bootloader_sha.h"
#include "bootloader_utility.h"
#include "esp_log.h"
#include "esp_image_format.h"
#include "esp_secure_boot.h"
#include "esp32s2/rom/secure_boot.h"
static const char* TAG = "secure_boot";
#define DIGEST_LEN 32
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
{
ets_secure_boot_key_digests_t trusted_keys = { 0 };
uint8_t digest[DIGEST_LEN];
uint8_t verified_digest[DIGEST_LEN] = { 0 }; /* Note: this function doesn't do any anti-FI checks on this buffer */
const uint8_t *data;
ESP_LOGD(TAG, "verifying signature src_addr 0x%x length 0x%x", src_addr, length);
if ((src_addr + length) % 4096 != 0) {
ESP_LOGE(TAG, "addr 0x%x length 0x%x doesn't end on a sector boundary", src_addr, length);
return ESP_ERR_INVALID_ARG;
}
/* Padding to round off the input to the nearest 4k boundary */
int padded_length = ALIGN_UP(length, FLASH_SECTOR_SIZE);
ESP_LOGD(TAG, "verifying src_addr 0x%x length", src_addr, padded_length);
data = bootloader_mmap(src_addr, length + sizeof(struct ets_secure_boot_sig_block));
if (data == NULL) {
@@ -43,23 +46,16 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
return ESP_FAIL;
}
// Calculate digest of main image
#ifdef BOOTLOADER_BUILD
bootloader_sha256_handle_t handle = bootloader_sha256_start();
bootloader_sha256_data(handle, data, length);
bootloader_sha256_finish(handle, digest);
#else
/* Use thread-safe esp-idf SHA function */
esp_sha(SHA2_256, data, length, digest);
#endif
int r = ets_secure_boot_read_key_digests(&trusted_keys);
if (r == ETS_OK) {
const ets_secure_boot_signature_t *sig = (const ets_secure_boot_signature_t *)(data + length);
// TODO: calling this function in IDF app context is unsafe
r = ets_secure_boot_verify_signature(sig, digest, &trusted_keys, verified_digest);
/* Calculate digest of main image */
esp_err_t err = bootloader_sha256_flash_contents(src_addr, padded_length, digest);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Digest calculation failed 0x%x, 0x%x", src_addr, padded_length);
bootloader_munmap(data);
return err;
}
const ets_secure_boot_signature_t *sig = (const ets_secure_boot_signature_t *)(data + length);
int r = esp_secure_boot_verify_rsa_signature_block(sig, digest, verified_digest);
bootloader_munmap(data);
return (r == ETS_OK) ? ESP_OK : ESP_FAIL;
@@ -68,15 +64,29 @@ esp_err_t esp_secure_boot_verify_signature(uint32_t src_addr, uint32_t length)
esp_err_t esp_secure_boot_verify_rsa_signature_block(const ets_secure_boot_signature_t *sig_block, const uint8_t *image_digest, uint8_t *verified_digest)
{
ets_secure_boot_key_digests_t trusted_keys;
ets_secure_boot_key_digests_t trusted_key_copies[2];
ETS_STATUS r;
int r = ets_secure_boot_read_key_digests(&trusted_keys);
if (r != 0) {
ESP_LOGE(TAG, "No trusted key digests were found in efuse!");
} else {
ESP_LOGD(TAG, "Verifying with RSA-PSS...");
// TODO: calling this function in IDF app context is unsafe
r = ets_secure_boot_verify_signature(sig_block, image_digest, &trusted_keys, verified_digest);
memset(&trusted_keys, 0, sizeof(ets_secure_boot_key_digests_t));
memset(trusted_key_copies, 0, 2 * sizeof(ets_secure_boot_key_digests_t));
if (!esp_secure_boot_enabled()) {
return ESP_OK;
}
return (r == 0) ? ESP_OK : ESP_ERR_IMAGE_INVALID;
r = ets_secure_boot_read_key_digests(&trusted_keys);
if (r != ETS_OK) {
ESP_LOGI(TAG, "Could not read secure boot digests!");
return ESP_FAIL;
}
// Create the copies for FI checks (assuming result is ETS_OK, if it's not then it'll fail the fault check anyhow)
ets_secure_boot_read_key_digests(&trusted_key_copies[0]);
ets_secure_boot_read_key_digests(&trusted_key_copies[1]);
ESP_FAULT_ASSERT(memcmp(&trusted_keys, &trusted_key_copies[0], sizeof(ets_secure_boot_key_digests_t)) == 0);
ESP_FAULT_ASSERT(memcmp(&trusted_keys, &trusted_key_copies[1], sizeof(ets_secure_boot_key_digests_t)) == 0);
ESP_LOGI(TAG, "Verifying with RSA-PSS boot...");
r = ets_secure_boot_verify_signature(sig_block, image_digest, &trusted_keys, verified_digest);
return (r == ETS_OK) ? ESP_OK : ESP_FAIL;
}