Skip to content

Commit

Permalink
[rom_ext_e2e] Test the MinBl0SecVer request
Browse files Browse the repository at this point in the history
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 lowRISC#23259.

Signed-off-by: Chris Frantz <[email protected]>
(cherry picked from commit 49bbeb2)
  • Loading branch information
cfrantz committed Aug 15, 2024
1 parent 6f8b359 commit d09e282
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 18 deletions.
1 change: 1 addition & 0 deletions sw/device/silicon_creator/lib/error.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)), \
\
Expand Down
70 changes: 69 additions & 1 deletion sw/device/silicon_creator/rom_ext/e2e/boot_svc/BUILD
Original file line number Diff line number Diff line change
@@ -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"])

Expand Down Expand Up @@ -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",
],
)
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@

typedef enum boot_svc_test {
kBootSvcTestEmpty = 1,
kBootSvcTestNextBl0 = 2,
kBootSvcTestBadNextBl0 = 3,
kBootSvcTestBl0MinSecVer = 4,
} boot_svc_test_t;

typedef enum boot_svc_test_state {
kBootSvcTestStateInit = 0,
kBootSvcTestStateCheckEmpty,
kBootSvcTestStateNextSideB,
kBootSvcTestStateReturnSideA,
kBootSvcTestStateMinSecAdvance,
kBootSvcTestStateMinSecTooFar,
kBootSvcTestStateMinSecGoBack,
kBootSvcTestStateFinal,
} boot_svc_test_state_t;

Expand Down
49 changes: 32 additions & 17 deletions sw/device/silicon_creator/rom_ext/rom_ext.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down

0 comments on commit d09e282

Please sign in to comment.