Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 38 additions & 25 deletions protocol/host_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,66 +169,79 @@ libhoth_error libhoth_hostcmd_exec(struct libhoth_device* dev, uint16_t command,
size_t req_payload_size, void* resp_buf,
size_t resp_buf_size,
size_t* out_resp_size) {
struct {
struct hoth_host_request hdr;
uint8_t
payload_buf[LIBHOTH_MAILBOX_SIZE - sizeof(struct hoth_host_request)];
} req;
if (req_payload_size > sizeof(req.payload_buf)) {
fprintf(stderr, "req_payload_size too large: %d > %d\n",
(int)req_payload_size, (int)sizeof(req.payload_buf));
union {
struct {
struct hoth_host_request hdr;
uint8_t payload_buf[LIBHOTH_MAX_MAILBOX_SIZE -
sizeof(struct hoth_host_request)];
} req;
struct {
struct hoth_host_response hdr;
uint8_t payload_buf[LIBHOTH_MAX_MAILBOX_SIZE -
sizeof(struct hoth_host_response)];
} resp;
} buf;
if (req_payload_size > sizeof(buf.req.payload_buf)) {
fprintf(stderr, "req_payload_size too large: %zu > %zu\n", req_payload_size,
sizeof(buf.req.payload_buf));
return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH,
LIBHOTH_ERR_OUT_UNDERFLOW);
}
if (resp_buf_size > sizeof(buf.resp.payload_buf)) {
fprintf(stderr, "resp_buf_size too large: %zu > %zu\n", resp_buf_size,
sizeof(buf.resp.payload_buf));
return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH,
LIBHOTH_ERR_OUT_UNDERFLOW);
}
if (req_payload) {
memcpy(req.payload_buf, req_payload, req_payload_size);
memcpy(buf.req.payload_buf, req_payload, req_payload_size);
}
libhoth_status status = populate_ec_request_header(
command, version, req.payload_buf, req_payload_size, &req.hdr);
command, version, buf.req.payload_buf, req_payload_size, &buf.req.hdr);
if (status != LIBHOTH_OK) {
fprintf(stderr, "populate_ec_request_header() failed: %d\n", status);
return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH,
status);
}
libhoth_error err =
libhoth_send_request(dev, &req, sizeof(req.hdr) + req_payload_size);
libhoth_error err = libhoth_send_request(
dev, &buf.req, sizeof(buf.req.hdr) + req_payload_size);
if (err != HOTH_SUCCESS) {
fprintf(stderr, "libhoth_send_request() failed: 0x%016llx\n",
(unsigned long long)err);
return err;
}
struct {
struct hoth_host_response hdr;
uint8_t
payload_buf[LIBHOTH_MAILBOX_SIZE - sizeof(struct hoth_host_response)];
} resp;
size_t max_resp_size = sizeof(struct hoth_host_response) + resp_buf_size;
Comment thread
ckungler marked this conversation as resolved.
Resolved
if (max_resp_size < LIBHOTH_MAILBOX_SIZE) {
max_resp_size = LIBHOTH_MAILBOX_SIZE;
}
size_t resp_size = 0;
err = libhoth_receive_response(dev, &resp, sizeof(resp), &resp_size,
err = libhoth_receive_response(dev, &buf.resp, max_resp_size, &resp_size,
HOTH_CMD_TIMEOUT_MS_DEFAULT);
if (err != HOTH_SUCCESS) {
fprintf(stderr, "libhoth_receive_response() failed: 0x%016llx\n",
(unsigned long long)err);
return err;
}
status = validate_ec_response_header(&resp.hdr, resp.payload_buf, resp_size);
status = validate_ec_response_header(&buf.resp.hdr, buf.resp.payload_buf,
resp_size);
if (status != LIBHOTH_OK) {
fprintf(stderr, "EC response header invalid: %d\n", status);
return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH,
status);
}
if (resp.hdr.result != HOTH_RES_SUCCESS) {
fprintf(stderr, "EC response contained error: %d", resp.hdr.result);
if (resp.hdr.data_len >= 4) {
if (buf.resp.hdr.result != HOTH_RES_SUCCESS) {
fprintf(stderr, "EC response contained error: %d", buf.resp.hdr.result);
if (buf.resp.hdr.data_len >= 4) {
uint32_t error_code;
memcpy(&error_code, resp.payload_buf, sizeof(error_code));
memcpy(&error_code, buf.resp.payload_buf, sizeof(error_code));
fprintf(stderr, " (extended: 0x%08x)\n", error_code);
return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC,
HOTH_HOST_SPACE_PIEROT_ERR, error_code);
} else {
fprintf(stderr, "\n");
}
return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_EC,
resp.hdr.result);
buf.resp.hdr.result);
}

size_t resp_payload_size = resp_size - sizeof(struct hoth_host_response);
Expand All @@ -251,7 +264,7 @@ libhoth_error libhoth_hostcmd_exec(struct libhoth_device* dev, uint16_t command,
}
}
if (resp_buf) {
memcpy(resp_buf, resp.payload_buf, resp_payload_size);
memcpy(resp_buf, buf.resp.payload_buf, resp_payload_size);
}
if (out_resp_size) {
*out_resp_size = resp_payload_size;
Expand Down
45 changes: 44 additions & 1 deletion protocol/host_cmd_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include "protocol/host_cmd.h"
#include <vector>

#include "test/libhoth_device_mock.h"
#include "transports/libhoth_device.h"

Expand Down Expand Up @@ -72,3 +73,45 @@ TEST_F(LibHothTest, response_failure_extended) {
EXPECT_EQ(LIBHOTH_ERR_GET_SPACE(err), HOTH_HOST_SPACE_PIEROT_ERR);
EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), 0x89c70005);
}

TEST_F(LibHothTest, large_request_accepted_up_to_max_mailbox) {
std::vector<uint8_t> payload(4096);
for (size_t i = 0; i < payload.size(); i++) {
payload[i] = static_cast<uint8_t>(i);
}

size_t sent_size = 0;
std::vector<uint8_t> sent_payload;
EXPECT_CALL(mock_, send(_, UsesCommand(kCmd), _))
.WillOnce([&](struct libhoth_device*, const void* request,
size_t request_size) {
const uint8_t* bytes = static_cast<const uint8_t*>(request);
sent_size = request_size;
sent_payload.assign(bytes + sizeof(struct hoth_host_request),
bytes + request_size);
return LIBHOTH_OK;
});
const uint8_t empty_resp = 0;
EXPECT_CALL(mock_, receive)
.WillOnce(DoAll(CopyResp(&empty_resp, 0), Return(LIBHOTH_OK)));

EXPECT_EQ(libhoth_hostcmd_exec(&hoth_dev_, kCmd, 0, payload.data(),
payload.size(), nullptr, 0, nullptr),
HOTH_SUCCESS);

EXPECT_EQ(sent_size, sizeof(struct hoth_host_request) + payload.size());
EXPECT_EQ(sent_payload, payload);
}

TEST_F(LibHothTest, oversized_request_rejected_above_max_mailbox) {
EXPECT_CALL(mock_, send).Times(0);

const size_t max_payload =
LIBHOTH_MAX_MAILBOX_SIZE - sizeof(struct hoth_host_request);
std::vector<uint8_t> payload(max_payload + 1, 0);
libhoth_error err = libhoth_hostcmd_exec(&hoth_dev_, kCmd, 0, payload.data(),
payload.size(), nullptr, 0, nullptr);

EXPECT_NE(err, HOTH_SUCCESS);
EXPECT_EQ(LIBHOTH_ERR_GET_CODE(err), LIBHOTH_ERR_OUT_UNDERFLOW);
}
40 changes: 40 additions & 0 deletions protocol/key_rotation.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,52 @@ libhoth_error libhoth_key_rotation_payload_status(
return HOTH_SUCCESS;
}

static libhoth_error key_rotation_write_commit(struct libhoth_device* dev,
const uint8_t* record,
size_t size) {
if (size < STRUCT_CHUNK_SIZE || (size % 4) != 0 ||
size > KEY_ROTATION_RECORD_WRITE_COMMIT_MAX_SIZE) {
fprintf(stderr,
"Invalid key rotation record size: %zu (must be 4-byte aligned in "
"[%d, %zu]).\n",
size, STRUCT_CHUNK_SIZE,
(size_t)KEY_ROTATION_RECORD_WRITE_COMMIT_MAX_SIZE);
return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH,
LIBHOTH_ERR_INVALID_PARAMETER);
}

struct {
struct hoth_request_key_rotation_record hdr;
uint8_t data[KEY_ROTATION_RECORD_WRITE_COMMIT_MAX_SIZE];
} __hoth_align4 request;

request.hdr.operation = KEY_ROTATION_RECORD_WRITE_COMMIT;
request.hdr.packet_offset = 0;
request.hdr.packet_size = (uint16_t)size;
request.hdr.reserved = 0;
memcpy(request.data, record, size);

fprintf(stderr, "Writing and committing the key rotation record.\n");
libhoth_error ret = libhoth_hostcmd_exec(
dev, HOTH_CMD_BOARD_SPECIFIC_BASE + HOTH_PRV_CMD_HAVEN_KEY_ROTATION_OP, 0,
&request, sizeof(request.hdr) + size, NULL, 0, NULL);
if (ret != HOTH_SUCCESS) {
fprintf(stderr, "HOTH_KEY_ROTATION_WRITE_COMMIT error code: 0x%016llx\n",
(unsigned long long)ret);
return ret;
}
return HOTH_SUCCESS;
}

libhoth_error libhoth_key_rotation_update(struct libhoth_device* dev,
const uint8_t* image, size_t size) {
if (dev == NULL || image == NULL) {
return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH,
LIBHOTH_ERR_INVALID_PARAMETER);
}
if (size >= 4 && memcmp(image, KEY_ROTATION_SIGNED_RECORD_MAGIC, 4) == 0) {
return key_rotation_write_commit(dev, image, size);
}
if (size <= KEY_ROTATION_RECORD_SIGNATURE_SIZE ||
size > KEY_ROTATION_FLASH_AREA_SIZE) {
fprintf(stderr, "Data chunk size invalid.\n");
Expand Down
10 changes: 10 additions & 0 deletions protocol/key_rotation.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ extern "C" {
#define KEY_ROTATION_RECORD_WRITE_MAX_SIZE \
(LIBHOTH_MAILBOX_SIZE - sizeof(struct hoth_host_request) - \
sizeof(struct hoth_request_key_rotation_record))
// Maximum signed key rotation record container ("KRSD") size accepted over the
// 6 KiB OpenTitan mailbox. Note that after stripping the hybrid signature
// container, the inner sealed descriptor stored in OpenTitan flash must fit in
// KEY_ROTATION_FLASH_AREA_SIZE - 56 = 1,992 bytes.
#define KEY_ROTATION_RECORD_WRITE_COMMIT_MAX_SIZE \
(LIBHOTH_MAX_MAILBOX_SIZE - sizeof(struct hoth_host_request) - \
sizeof(struct hoth_request_key_rotation_record))
#define KEY_ROTATION_SIGNED_RECORD_MAGIC "KRSD"
#define KEY_ROTATION_RECORD_READ_MAX_SIZE \
(LIBHOTH_MAILBOX_SIZE - sizeof(struct hoth_host_response) - \
sizeof(struct hoth_request_key_rotation_record) - \
Expand Down Expand Up @@ -78,6 +86,8 @@ enum key_rotation_record_op {
// allows
KEY_ROTATION_RECORD_SET_MAUV = 10, // Set Key Rotation Record MAUV
KEY_ROTATION_RECORD_GET_MAUV = 11, // Get Key Rotation Record MAUV
KEY_ROTATION_RECORD_WRITE_COMMIT = 12, // Write, validate and commit a whole
// record. OpenTitan only.
};

#define KEY_ROTATION_CHUNK_TYPE_CODE_PKEY (0x59454B50)
Expand Down
82 changes: 81 additions & 1 deletion protocol/key_rotation_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <vector>

#include "protocol/host_cmd.h"
#include "protocol/status.h"
Expand Down Expand Up @@ -353,7 +354,7 @@ TEST_F(LibHothTest, key_rotation_update_failure) {
}

TEST_F(LibHothTest, key_rotation_update_initiate_failure) {
uint8_t data[KEY_ROTATION_RECORD_WRITE_MAX_SIZE];
uint8_t data[KEY_ROTATION_RECORD_WRITE_MAX_SIZE] = {0};

EXPECT_CALL(mock_, send(_, UsesCommand(kCmd), _))
.WillRepeatedly(Return(LIBHOTH_OK));
Expand Down Expand Up @@ -600,3 +601,82 @@ TEST_F(LibHothTest, key_rotation_get_mauv_failure_invalid_response_size) {
LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_EC,
HOTH_RES_INVALID_RESPONSE_SIZE));
}

TEST_F(LibHothTest, update_krsd_sends_whole_record_in_one_write_commit) {
std::vector<uint8_t> record(4096);
for (size_t i = 0; i < record.size(); i++) {
record[i] = static_cast<uint8_t>(i * 7);
}
std::memcpy(record.data(), KEY_ROTATION_SIGNED_RECORD_MAGIC, 4);

struct hoth_request_key_rotation_record hdr = {};
std::vector<uint8_t> sent_record;
EXPECT_CALL(mock_, send(_, UsesCommand(kCmd), _))
.Times(1)
.WillOnce([&](struct libhoth_device*, const void* request,
size_t request_size) {
const uint8_t* payload = static_cast<const uint8_t*>(request) +
sizeof(struct hoth_host_request);
const size_t payload_size =
request_size - sizeof(struct hoth_host_request);
std::memcpy(&hdr, payload, sizeof(hdr));
sent_record.assign(payload + sizeof(hdr), payload + payload_size);
return LIBHOTH_OK;
});
EXPECT_CALL(mock_, receive)
.Times(1)
.WillOnce(DoAll(CopyResp(record.data(), 0), Return(LIBHOTH_OK)));

EXPECT_EQ(
libhoth_key_rotation_update(&hoth_dev_, record.data(), record.size()),
HOTH_SUCCESS);

EXPECT_EQ(hdr.operation, KEY_ROTATION_RECORD_WRITE_COMMIT);
EXPECT_EQ(hdr.packet_offset, 0);
EXPECT_EQ(hdr.packet_size, record.size());
EXPECT_EQ(sent_record, record);
}

TEST_F(LibHothTest, update_krsd_accepts_record_at_max_capacity) {
std::vector<uint8_t> record(KEY_ROTATION_RECORD_WRITE_COMMIT_MAX_SIZE, 0xa5);
std::memcpy(record.data(), KEY_ROTATION_SIGNED_RECORD_MAGIC, 4);

EXPECT_CALL(mock_, send(_, UsesCommand(kCmd), _))
.WillOnce(Return(LIBHOTH_OK));
EXPECT_CALL(mock_, receive)
.WillOnce(DoAll(CopyResp(record.data(), 0), Return(LIBHOTH_OK)));

EXPECT_EQ(
libhoth_key_rotation_update(&hoth_dev_, record.data(), record.size()),
HOTH_SUCCESS);
}

TEST_F(LibHothTest, update_krsd_rejects_invalid_sizes_and_alignment) {
EXPECT_CALL(mock_, send).Times(0);

const libhoth_error expected =
LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH,
LIBHOTH_ERR_INVALID_PARAMETER);

std::vector<uint8_t> record(KEY_ROTATION_RECORD_WRITE_COMMIT_MAX_SIZE + 4, 0);
std::memcpy(record.data(), KEY_ROTATION_SIGNED_RECORD_MAGIC, 4);
EXPECT_EQ(
libhoth_key_rotation_update(&hoth_dev_, record.data(), record.size()),
expected);
EXPECT_EQ(libhoth_key_rotation_update(&hoth_dev_, record.data(), 4),
expected);
EXPECT_EQ(libhoth_key_rotation_update(&hoth_dev_, record.data(), 10),
expected);
}

TEST_F(LibHothTest, update_krsd_reports_device_failure) {
uint8_t record[8] = {0};
std::memcpy(record, KEY_ROTATION_SIGNED_RECORD_MAGIC, 4);
const libhoth_error dev_err = LIBHOTH_ERR_CONSTRUCT(
HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, LIBHOTH_ERR_FAIL);

EXPECT_CALL(mock_, send(_, UsesCommand(kCmd), _)).WillOnce(Return(dev_err));

EXPECT_EQ(libhoth_key_rotation_update(&hoth_dev_, record, sizeof(record)),
dev_err);
}
4 changes: 2 additions & 2 deletions protocol/test/libhoth_device_mock.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ ACTION_P(CopyResp, response, resp_size) {

struct {
struct hoth_host_response hdr;
uint8_t
payload_buf[LIBHOTH_MAILBOX_SIZE - sizeof(struct hoth_host_response)];
uint8_t payload_buf[LIBHOTH_MAX_MAILBOX_SIZE -
sizeof(struct hoth_host_response)];
} resp;

ASSERT_LE(full_resp_size, sizeof(resp));
Expand Down
2 changes: 1 addition & 1 deletion tools/htool/htool_raw_host_command.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ int command_raw_host_command(const struct htool_invocation* inv) {
return 0;
}

uint8_t req_payload[LIBHOTH_MAILBOX_SIZE -
uint8_t req_payload[LIBHOTH_MAX_MAILBOX_SIZE -
sizeof(struct hoth_host_request)] = {0};

if (req.data_len > sizeof(req_payload)) {
Expand Down
Loading
Loading