From d09e282b26a113e4a5d5be2031991c1d3b70470c Mon Sep 17 00:00:00 2001 From: Chris Frantz Date: Wed, 22 May 2024 16:05:59 -0700 Subject: [PATCH] [rom_ext_e2e] Test the `MinBl0SecVer` request 1. Verify the manifests of both slots to determine a maximum `min_sec_ver`. We prevent advancing the `min_sec_ver` beyond the value associated with any valid firmware in flash. 2. Test the `MinBl0SecVer` request by advancing the minimum security version forward. Attempt to go beyond the maximum allowed value and confirm that the request is rejected. Attempt to go backwards and confirm that the request is rejected. This addresses #23259. Signed-off-by: Chris Frantz (cherry picked from commit 49bbeb2d679f6643bbf286767ce6f2ed42f9d481) --- sw/device/silicon_creator/lib/error.h | 1 + .../rom_ext/e2e/boot_svc/BUILD | 70 +++++++++- .../e2e/boot_svc/boot_svc_min_sec_ver_test.c | 126 ++++++++++++++++++ .../rom_ext/e2e/boot_svc/boot_svc_test_lib.h | 6 + sw/device/silicon_creator/rom_ext/rom_ext.c | 49 ++++--- 5 files changed, 234 insertions(+), 18 deletions(-) create mode 100644 sw/device/silicon_creator/rom_ext/e2e/boot_svc/boot_svc_min_sec_ver_test.c diff --git a/sw/device/silicon_creator/lib/error.h b/sw/device/silicon_creator/lib/error.h index 0b40e956240ca..bd702f480f80f 100644 --- a/sw/device/silicon_creator/lib/error.h +++ b/sw/device/silicon_creator/lib/error.h @@ -163,6 +163,7 @@ enum module_ { \ X(kErrorBootSvcBadHeader, ERROR_(1, kModuleBootSvc, kInternal)), \ X(kErrorBootSvcBadSlot, ERROR_(2, kModuleBootSvc, kInvalidArgument)), \ + X(kErrorBootSvcBadSecVer, ERROR_(3, kModuleBootSvc, kInvalidArgument)), \ \ X(kErrorRomExtBootFailed, ERROR_(1, kModuleRomExt, kFailedPrecondition)), \ \ diff --git a/sw/device/silicon_creator/rom_ext/e2e/boot_svc/BUILD b/sw/device/silicon_creator/rom_ext/e2e/boot_svc/BUILD index 3efd9ee5bebc7..41f70d4a73cf1 100644 --- a/sw/device/silicon_creator/rom_ext/e2e/boot_svc/BUILD +++ b/sw/device/silicon_creator/rom_ext/e2e/boot_svc/BUILD @@ -1,15 +1,17 @@ # Copyright lowRISC contributors (OpenTitan project). # Licensed under the Apache License, Version 2.0, see LICENSE for details. # SPDX-License-Identifier: Apache-2.0 +load("//rules:const.bzl", "CONST", "hex") load( "//rules/opentitan:defs.bzl", "DEFAULT_TEST_FAILURE_MSG", "DEFAULT_TEST_SUCCESS_MSG", "EARLGREY_TEST_ENVS", - "cw310_params", "fpga_params", + "opentitan_binary", "opentitan_test", ) +load("//rules:manifest.bzl", "manifest") package(default_visibility = ["//visibility:public"]) @@ -138,3 +140,69 @@ opentitan_test( "//sw/device/silicon_creator/lib/drivers:retention_sram", ], ) + +manifest({ + "name": "manifest_version_4", + "security_version": "4", + "identifier": hex(CONST.OWNER), +}) + +opentitan_binary( + name = "min_sec_ver_4", + testonly = True, + srcs = ["boot_svc_min_sec_ver_test.c"], + exec_env = [ + "//hw/top_earlgrey:fpga_cw310_rom_ext", + ], + linker_script = "//sw/device/lib/testing/test_framework:ottf_ld_silicon_owner_slot_virtual", + manifest = ":manifest_version_4", + deps = [ + ":boot_svc_test_lib", + "//sw/device/lib/base:status", + "//sw/device/lib/runtime:log", + "//sw/device/lib/testing/test_framework:check", + "//sw/device/lib/testing/test_framework:ottf_main", + "//sw/device/silicon_creator/lib:boot_log", + "//sw/device/silicon_creator/lib/boot_svc:boot_svc_min_bl0_sec_ver", + "//sw/device/silicon_creator/lib/boot_svc:boot_svc_msg", + "//sw/device/silicon_creator/lib/drivers:retention_sram", + "//sw/device/silicon_creator/lib/drivers:rstmgr", + ], +) + +opentitan_test( + name = "boot_svc_min_sec_ver_test", + srcs = ["boot_svc_min_sec_ver_test.c"], + exec_env = { + "//hw/top_earlgrey:fpga_cw310_rom_ext": None, + }, + fpga = fpga_params( + assemble = "{rom_ext}@0 {firmware}@0x10000 {min_sec_ver_4:signed_bin}@0x90000", + binaries = { + ":min_sec_ver_4": "min_sec_ver_4", + }, + test_cmd = """ + --exec="transport init" + --exec="fpga clear-bitstream" + --exec="fpga load-bitstream {bitstream}" + --exec="bootstrap --clear-uart=true {firmware}" + --exec="console --non-interactive --exit-success='{exit_success}' --exit-failure='{exit_failure}'" + --exec="fpga clear-bitstream" + no-op + """, + ), + linker_script = "//sw/device/lib/testing/test_framework:ottf_ld_silicon_owner_slot_virtual", + manifest = "//sw/device/silicon_owner:manifest", + deps = [ + ":boot_svc_test_lib", + "//sw/device/lib/base:status", + "//sw/device/lib/runtime:log", + "//sw/device/lib/testing/test_framework:check", + "//sw/device/lib/testing/test_framework:ottf_main", + "//sw/device/silicon_creator/lib:boot_log", + "//sw/device/silicon_creator/lib/boot_svc:boot_svc_min_bl0_sec_ver", + "//sw/device/silicon_creator/lib/boot_svc:boot_svc_msg", + "//sw/device/silicon_creator/lib/drivers:retention_sram", + "//sw/device/silicon_creator/lib/drivers:rstmgr", + ], +) diff --git a/sw/device/silicon_creator/rom_ext/e2e/boot_svc/boot_svc_min_sec_ver_test.c b/sw/device/silicon_creator/rom_ext/e2e/boot_svc/boot_svc_min_sec_ver_test.c new file mode 100644 index 0000000000000..fd164bff03c1d --- /dev/null +++ b/sw/device/silicon_creator/rom_ext/e2e/boot_svc/boot_svc_min_sec_ver_test.c @@ -0,0 +1,126 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "sw/device/lib/base/status.h" +#include "sw/device/lib/runtime/log.h" +#include "sw/device/lib/testing/test_framework/check.h" +#include "sw/device/lib/testing/test_framework/ottf_main.h" +#include "sw/device/silicon_creator/lib/boot_svc/boot_svc_min_bl0_sec_ver.h" +#include "sw/device/silicon_creator/lib/drivers/retention_sram.h" +#include "sw/device/silicon_creator/lib/drivers/rstmgr.h" +#include "sw/device/silicon_creator/rom_ext/e2e/boot_svc/boot_svc_test_lib.h" + +OTTF_DEFINE_TEST_CONFIG(); + +#define MANIFEST_SEC_VER 4 + +static status_t initialize(retention_sram_t *retram, boot_svc_retram_t *state) { + boot_svc_msg_t msg = {0}; + boot_svc_empty_init(&msg.empty); + boot_svc_min_bl0_sec_ver_req_init(2, &msg.min_bl0_sec_ver_req); + retram->creator.boot_svc_msg = msg; + state->state = kBootSvcTestStateMinSecAdvance; + rstmgr_reset(); + return INTERNAL(); +} + +static status_t advance(retention_sram_t *retram, boot_svc_retram_t *state) { + boot_svc_msg_t msg = retram->creator.boot_svc_msg; + TRY(boot_svc_header_check(&msg.header)); + TRY_CHECK(msg.header.type == kBootSvcMinBl0SecVerResType); + LOG_INFO("Response: status=%08x min_bl0_sec_ver=%d", + msg.min_bl0_sec_ver_res.status, + msg.min_bl0_sec_ver_res.min_bl0_sec_ver); + + TRY_CHECK(msg.min_bl0_sec_ver_res.status == kErrorOk); + + if (msg.min_bl0_sec_ver_res.min_bl0_sec_ver < MANIFEST_SEC_VER) { + // Advance by one and check again for success + boot_svc_min_bl0_sec_ver_req_init( + msg.min_bl0_sec_ver_res.min_bl0_sec_ver + 1, &msg.min_bl0_sec_ver_req); + retram->creator.boot_svc_msg = msg; + rstmgr_reset(); + } + + if (msg.min_bl0_sec_ver_res.min_bl0_sec_ver == MANIFEST_SEC_VER) { + // Advance by one and check for failure + state->state = kBootSvcTestStateMinSecTooFar; + boot_svc_min_bl0_sec_ver_req_init( + msg.min_bl0_sec_ver_res.min_bl0_sec_ver + 1, &msg.min_bl0_sec_ver_req); + retram->creator.boot_svc_msg = msg; + rstmgr_reset(); + } + return INTERNAL(); +} + +static status_t too_far(retention_sram_t *retram, boot_svc_retram_t *state) { + boot_svc_msg_t msg = retram->creator.boot_svc_msg; + TRY(boot_svc_header_check(&msg.header)); + TRY_CHECK(msg.header.type == kBootSvcMinBl0SecVerResType); + LOG_INFO("Response: status=%08x min_bl0_sec_ver=%d", + msg.min_bl0_sec_ver_res.status, + msg.min_bl0_sec_ver_res.min_bl0_sec_ver); + TRY_CHECK(msg.min_bl0_sec_ver_res.status == kErrorBootSvcBadSecVer); + TRY_CHECK(msg.min_bl0_sec_ver_res.min_bl0_sec_ver == MANIFEST_SEC_VER); + + // Try to go back + state->state = kBootSvcTestStateMinSecGoBack; + boot_svc_min_bl0_sec_ver_req_init(msg.min_bl0_sec_ver_res.min_bl0_sec_ver - 1, + &msg.min_bl0_sec_ver_req); + retram->creator.boot_svc_msg = msg; + rstmgr_reset(); + return INTERNAL(); +} + +static status_t go_back(retention_sram_t *retram, boot_svc_retram_t *state) { + boot_svc_msg_t msg = retram->creator.boot_svc_msg; + TRY(boot_svc_header_check(&msg.header)); + TRY_CHECK(msg.header.type == kBootSvcMinBl0SecVerResType); + LOG_INFO("Response: status=%08x min_bl0_sec_ver=%d", + msg.min_bl0_sec_ver_res.status, + msg.min_bl0_sec_ver_res.min_bl0_sec_ver); + TRY_CHECK(msg.min_bl0_sec_ver_res.status == kErrorBootSvcBadSecVer); + TRY_CHECK(msg.min_bl0_sec_ver_res.min_bl0_sec_ver == MANIFEST_SEC_VER); + + // End of test sequence. + state->state = kBootSvcTestStateFinal; + return OK_STATUS(); +} + +static status_t min_sec_ver_test(void) { + retention_sram_t *retram = retention_sram_get(); + TRY(boot_svc_test_init(retram, kBootSvcTestBl0MinSecVer)); + boot_svc_retram_t *state = (boot_svc_retram_t *)&retram->owner; + + for (;;) { + LOG_INFO("Test state = %d", state->state); + switch (state->state) { + case kBootSvcTestStateInit: + TRY(initialize(retram, state)); + break; + case kBootSvcTestStateMinSecAdvance: + TRY(advance(retram, state)); + break; + case kBootSvcTestStateMinSecTooFar: + TRY(too_far(retram, state)); + break; + case kBootSvcTestStateMinSecGoBack: + TRY(go_back(retram, state)); + break; + case kBootSvcTestStateFinal: + LOG_INFO("FinalBootLog: %d:%s", state->boots, state->partition); + return OK_STATUS(); + default: + return UNKNOWN(); + } + } +} + +bool test_main(void) { + status_t sts = min_sec_ver_test(); + if (status_err(sts)) { + LOG_ERROR("min_sec_ver_test: %r", sts); + } + return status_ok(sts); +} diff --git a/sw/device/silicon_creator/rom_ext/e2e/boot_svc/boot_svc_test_lib.h b/sw/device/silicon_creator/rom_ext/e2e/boot_svc/boot_svc_test_lib.h index ba4747179820e..a0ad699f42bcf 100644 --- a/sw/device/silicon_creator/rom_ext/e2e/boot_svc/boot_svc_test_lib.h +++ b/sw/device/silicon_creator/rom_ext/e2e/boot_svc/boot_svc_test_lib.h @@ -9,6 +9,9 @@ typedef enum boot_svc_test { kBootSvcTestEmpty = 1, + kBootSvcTestNextBl0 = 2, + kBootSvcTestBadNextBl0 = 3, + kBootSvcTestBl0MinSecVer = 4, } boot_svc_test_t; typedef enum boot_svc_test_state { @@ -16,6 +19,9 @@ typedef enum boot_svc_test_state { kBootSvcTestStateCheckEmpty, kBootSvcTestStateNextSideB, kBootSvcTestStateReturnSideA, + kBootSvcTestStateMinSecAdvance, + kBootSvcTestStateMinSecTooFar, + kBootSvcTestStateMinSecGoBack, kBootSvcTestStateFinal, } boot_svc_test_state_t; diff --git a/sw/device/silicon_creator/rom_ext/rom_ext.c b/sw/device/silicon_creator/rom_ext/rom_ext.c index b6b30059d366c..0acefc169d245 100644 --- a/sw/device/silicon_creator/rom_ext/rom_ext.c +++ b/sw/device/silicon_creator/rom_ext/rom_ext.c @@ -704,33 +704,48 @@ static rom_error_t boot_svc_min_sec_ver_handler(boot_svc_msg_t *boot_svc_msg, const uint32_t current_min_sec_ver = boot_data->min_security_version_bl0; const uint32_t requested_min_sec_ver = boot_svc_msg->next_boot_bl0_slot_req.next_bl0_slot; - const uint32_t diff = requested_min_sec_ver - current_min_sec_ver; // Ensure the requested minimum security version isn't lower than the current // minimum security version. if (launder32(requested_min_sec_ver) > current_min_sec_ver) { HARDENED_CHECK_GT(requested_min_sec_ver, current_min_sec_ver); - HARDENED_CHECK_LT(diff, requested_min_sec_ver); + uint32_t max_sec_ver = current_min_sec_ver; + + // Check the two flash slots for valid manifests and determine the maximum + // value of the new minimum_security_version. This prevents a malicious + // MinBl0SecVer request from making the chip un-bootable. + const manifest_t *manifest = rom_ext_boot_policy_manifest_a_get(); + rom_error_t error = rom_ext_verify(manifest, boot_data); + if (error == kErrorOk && manifest->security_version > max_sec_ver) { + max_sec_ver = manifest->security_version; + } + manifest = rom_ext_boot_policy_manifest_b_get(); + error = rom_ext_verify(manifest, boot_data); + if (error == kErrorOk && manifest->security_version > max_sec_ver) { + max_sec_ver = manifest->security_version; + } - // Update boot data to the requested minimum BL0 security version. - boot_data->min_security_version_bl0 = requested_min_sec_ver; + if (requested_min_sec_ver <= max_sec_ver) { + HARDENED_CHECK_LE(requested_min_sec_ver, max_sec_ver); + // Update boot data to the requested minimum BL0 security version. + boot_data->min_security_version_bl0 = requested_min_sec_ver; - // Write boot data, updating relevant fields and recomputing the digest. - HARDENED_RETURN_IF_ERROR(boot_data_write(boot_data)); - // Read the boot data back to ensure the correct policy is used this boot. - HARDENED_RETURN_IF_ERROR(boot_data_read(lc_state, boot_data)); + // Write boot data, updating relevant fields and recomputing the digest. + HARDENED_RETURN_IF_ERROR(boot_data_write(boot_data)); + // Read the boot data back to ensure the correct policy is used this boot. + HARDENED_RETURN_IF_ERROR(boot_data_read(lc_state, boot_data)); - boot_svc_min_bl0_sec_ver_res_init(boot_data->min_security_version_bl0, - kErrorOk, - &boot_svc_msg->min_bl0_sec_ver_res); + boot_svc_min_bl0_sec_ver_res_init(boot_data->min_security_version_bl0, + kErrorOk, + &boot_svc_msg->min_bl0_sec_ver_res); - HARDENED_CHECK_EQ(requested_min_sec_ver, - boot_data->min_security_version_bl0); - } else { - boot_svc_min_bl0_sec_ver_res_init(current_min_sec_ver, kErrorOk, - &boot_svc_msg->min_bl0_sec_ver_res); + HARDENED_CHECK_EQ(requested_min_sec_ver, + boot_data->min_security_version_bl0); + return kErrorOk; + } } - + boot_svc_min_bl0_sec_ver_res_init(current_min_sec_ver, kErrorBootSvcBadSecVer, + &boot_svc_msg->min_bl0_sec_ver_res); return kErrorOk; }