Skip to content
Open
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
11 changes: 11 additions & 0 deletions examples/htool.c
Original file line number Diff line number Diff line change
Expand Up @@ -2077,6 +2077,17 @@ static const struct htool_cmd CMDS[] = {
.desc = "The signed cert file."},
{}},
},
{
.verbs = (const char*[]){"provisioning", "get_encryption_key", NULL},
.desc = "Get the provisioning encryption key certificate chain",
.func = htool_provisioning_get_encryption_key,
.params =
(const struct htool_param[]){
{HTOOL_FLAG_VALUE, .name = "output",
.desc = "File to write the provisioning encryption key "
"certificate chain to."},
{}},
},
{
.verbs = (const char*[]){"provisioning", "store_secrets", NULL},
.desc = "Store secrets encrypted with the provisioning encryption key",
Expand Down
45 changes: 45 additions & 0 deletions examples/htool_provisioning.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,51 @@ static int get_secrets(const struct htool_invocation* inv, uint8_t* secrets,
secrets_size);
}

int htool_provisioning_get_encryption_key(const struct htool_invocation* inv) {
struct libhoth_device* dev = htool_libhoth_device();
if (!dev) {
fprintf(stderr, "Unable to retrieve libhoth_device\n");
return -1;
}

const char* output_file;
if (htool_get_param_string(inv, "output", &output_file) != 0 ||
strlen(output_file) == 0) {
fprintf(stderr, "--output must be specified.\n");
return -1;
}

uint8_t cert_chain[HOTH_KEY_PROVISIONING_MAX_ENCRYPTION_KEY_CERT_CHAIN_SIZE];
size_t cert_chain_size = 0;
libhoth_error err = libhoth_key_provisioning_get_encryption_key(
dev, cert_chain, sizeof(cert_chain), &cert_chain_size);
if (err != HOTH_SUCCESS) {
fprintf(stderr,
"Error: 'key_provisioning_get_encryption_key' failed (0x%016" PRIx64
"): ",
err);
libhoth_log_err(stderr, err);
return -1;
}

FILE* output_ptr = fopen(output_file, "wb");
if (output_ptr == NULL) {
fprintf(stderr, "Error: %s, when attempting to open file: %s\n",
strerror(errno), output_file);
return -1;
}

if (fwrite(cert_chain, sizeof(uint8_t), cert_chain_size, output_ptr) !=
cert_chain_size) {
fprintf(stderr, "Error writing to %s\n", output_file);
fclose(output_ptr);
return -1;
}

fclose(output_ptr);
return 0;
}

int htool_provisioning_store_secrets(const struct htool_invocation* inv) {
struct libhoth_device* dev = htool_libhoth_device();
if (!dev) {
Expand Down
3 changes: 3 additions & 0 deletions examples/htool_provisioning.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ int htool_get_provisioning_log(const struct htool_invocation* inv);
// Validate and Sign the provisioning log.
int htool_validate_and_sign(const struct htool_invocation* inv);

// Retrieves the provisioning encryption key certificate chain from the device.
int htool_provisioning_get_encryption_key(const struct htool_invocation* inv);

// Loads secrets that were encrypted with the provisioning encryption key.
int htool_provisioning_store_secrets(const struct htool_invocation* inv);

Expand Down
124 changes: 124 additions & 0 deletions examples/htool_provisioning_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,130 @@ TEST_F(HtoolProvisioningTest, LoadMldsaKeyMissingKey) {
ASSERT_EQ(htool_provisioning_load_mldsa_key(&inv), -1);
}

TEST_F(HtoolProvisioningTest, GetEncryptionKeySuccess) {
struct htool_invocation inv{};
std::string tmp_output_file =
tmp_dir_path_ + "/encryption_key_cert_chain.bin";
EXPECT_CALL(invocation_mock_, GetParamString("output", _))
.WillOnce(DoAll(SetArgPointee<1>(tmp_output_file.c_str()), Return(0)));

std::vector<uint8_t> cert_chain(724);
for (size_t i = 0; i < cert_chain.size(); ++i) {
cert_chain[i] = static_cast<uint8_t>(i & 0xff);
}

EXPECT_CALL(mock_, send(_, _, _))
.WillOnce([&](struct libhoth_device*, const void* req, size_t size) {
EXPECT_EQ(size,
sizeof(struct hoth_host_request) +
sizeof(struct hoth_key_provisioning_request_header));
const auto* hoth_req =
static_cast<const struct hoth_host_request*>(req);
EXPECT_EQ(hoth_req->command,
HOTH_BASE_CMD(HOTH_PRV_CMD_HOTH_KEY_PROVISIONING));
EXPECT_EQ(hoth_req->data_len,
sizeof(struct hoth_key_provisioning_request_header));
const auto* prov_req = reinterpret_cast<
const struct hoth_key_provisioning_request_header*>(
static_cast<const uint8_t*>(req) +
sizeof(struct hoth_host_request));
EXPECT_EQ(prov_req->version, HOTH_KEY_PROVISIONING_REQUEST_VERSION);
EXPECT_EQ(prov_req->command, HOTH_KEY_PROVISIONING_GET_ENCRYPTION_KEY);
EXPECT_EQ(prov_req->size,
sizeof(struct hoth_key_provisioning_request_header));
return LIBHOTH_OK;
});

EXPECT_CALL(mock_, receive(_, _, _, _, _))
.WillOnce(DoAll(CopyResp(cert_chain.data(), cert_chain.size()),
Return(LIBHOTH_OK)));

ASSERT_EQ(htool_provisioning_get_encryption_key(&inv), 0);

FILE* fp = fopen(tmp_output_file.c_str(), "rb");
ASSERT_NE(fp, nullptr);
std::vector<uint8_t> file_contents(cert_chain.size());
ASSERT_EQ(fread(file_contents.data(), 1, file_contents.size(), fp),
cert_chain.size());
EXPECT_EQ(fgetc(fp), EOF);
EXPECT_EQ(memcmp(file_contents.data(), cert_chain.data(), cert_chain.size()),
0);
fclose(fp);
remove(tmp_output_file.c_str());
}

TEST_F(HtoolProvisioningTest, GetEncryptionKeyDeviceError) {
struct htool_invocation inv{};
std::string tmp_output_file =
tmp_dir_path_ + "/encryption_key_cert_chain_err.bin";
EXPECT_CALL(invocation_mock_, GetParamString("output", _))
.WillOnce(DoAll(SetArgPointee<1>(tmp_output_file.c_str()), Return(0)));

EXPECT_CALL(mock_, send(_, _, _)).WillOnce(Return(LIBHOTH_OK));
EXPECT_CALL(mock_, receive(_, _, _, _, _))
.WillOnce(Return(LIBHOTH_ERR_INTERFACE_NOT_FOUND));

ASSERT_EQ(htool_provisioning_get_encryption_key(&inv), -1);
EXPECT_EQ(fopen(tmp_output_file.c_str(), "rb"), nullptr);
}

TEST_F(HtoolProvisioningTest, GetEncryptionKeyMissingOutput) {
struct htool_invocation inv{};
EXPECT_CALL(invocation_mock_, GetParamString("output", _))
.WillOnce(Return(-1))
.WillOnce(DoAll(SetArgPointee<1>(""), Return(0)));
EXPECT_CALL(mock_, send(_, _, _)).Times(0);
EXPECT_CALL(mock_, receive(_, _, _, _, _)).Times(0);

ASSERT_EQ(htool_provisioning_get_encryption_key(&inv), -1);
ASSERT_EQ(htool_provisioning_get_encryption_key(&inv), -1);
}

TEST_F(HtoolProvisioningTest, GetEncryptionKeyOutputFileNotAbleToBeOpened) {
struct htool_invocation inv{};
std::string tmp_output_file = "/path/to/nonexistent/file";
EXPECT_CALL(invocation_mock_, GetParamString("output", _))
.WillOnce(DoAll(SetArgPointee<1>(tmp_output_file.c_str()), Return(0)));

std::vector<uint8_t> cert_chain(724, 0xab);
EXPECT_CALL(mock_, send(_, _, _)).WillOnce(Return(LIBHOTH_OK));
EXPECT_CALL(mock_, receive(_, _, _, _, _))
.WillOnce(DoAll(CopyResp(cert_chain.data(), cert_chain.size()),
Return(LIBHOTH_OK)));

ASSERT_EQ(htool_provisioning_get_encryption_key(&inv), -1);
}

TEST_F(HtoolProvisioningTest, GetEncryptionKeyProtocolNullParams) {
uint8_t cert_chain[16] = {};
size_t out_size = 0;
EXPECT_CALL(mock_, send(_, _, _)).Times(0);
EXPECT_EQ(libhoth_key_provisioning_get_encryption_key(
&hoth_dev_, nullptr, sizeof(cert_chain), &out_size),
LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH,
LIBHOTH_ERR_INVALID_PARAMETER));
EXPECT_EQ(libhoth_key_provisioning_get_encryption_key(
&hoth_dev_, cert_chain, sizeof(cert_chain), nullptr),
LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH,
LIBHOTH_ERR_INVALID_PARAMETER));
}

TEST_F(HtoolProvisioningTest, GetEncryptionKeyProtocolBufferOverflow) {
std::vector<uint8_t> cert_chain(32, 0xab);
uint8_t small_buf[16] = {};
size_t out_size = 0;

EXPECT_CALL(mock_, send(_, _, _)).WillOnce(Return(LIBHOTH_OK));
EXPECT_CALL(mock_, receive(_, _, _, _, _))
.WillOnce(DoAll(CopyResp(cert_chain.data(), cert_chain.size()),
Return(LIBHOTH_OK)));

EXPECT_EQ(libhoth_key_provisioning_get_encryption_key(
&hoth_dev_, small_buf, sizeof(small_buf), &out_size),
LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH,
LIBHOTH_ERR_RESPONSE_BUFFER_OVERFLOW));
}

TEST_F(HtoolProvisioningTest, StoreSecretsProtocolTooLarge) {
std::vector<uint8_t> data(HOTH_KEY_PROVISIONING_MAX_SECRETS_SIZE + 1, 0x11);
EXPECT_CALL(mock_, send(_, _, _)).Times(0);
Expand Down
34 changes: 29 additions & 5 deletions protocol/provisioning.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,15 @@ static int exec_provisioning_log_cmd_legacy(struct libhoth_device* dev,
// Executes a key provisioning host command (0x3E43)
static libhoth_error exec_key_provisioning_cmd(struct libhoth_device* dev,
const void* req_payload,
size_t req_payload_size) {
size_t response_size = 0;
size_t req_payload_size,
void* resp_buf,
size_t resp_buf_size,
size_t* out_resp_size) {
return libhoth_hostcmd_exec_v2(dev,
/*command=*/HOTH_CMD_BOARD_SPECIFIC_BASE +
HOTH_PRV_CMD_HOTH_KEY_PROVISIONING,
/*version=*/0, req_payload, req_payload_size,
NULL, 0, &response_size);
resp_buf, resp_buf_size, out_resp_size);
}

int libhoth_provisioning_log_read(struct libhoth_device* dev, uint8_t* buf,
Expand Down Expand Up @@ -249,6 +251,24 @@ libhoth_error libhoth_provisioning_log_commit(struct libhoth_device* dev,
0, &response_size);
}

libhoth_error libhoth_key_provisioning_get_encryption_key(
struct libhoth_device* dev, uint8_t* cert_chain, size_t cert_chain_capacity,
size_t* out_size) {
if (cert_chain == NULL || out_size == NULL) {
return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH,
LIBHOTH_ERR_INVALID_PARAMETER);
}

struct hoth_key_provisioning_request_header req = {
.version = HOTH_KEY_PROVISIONING_REQUEST_VERSION,
.command = HOTH_KEY_PROVISIONING_GET_ENCRYPTION_KEY,
.size = sizeof(req),
};

return exec_key_provisioning_cmd(dev, &req, sizeof(req), cert_chain,
cert_chain_capacity, out_size);
}

libhoth_error libhoth_key_provisioning_store_secrets(struct libhoth_device* dev,
const uint8_t* secrets,
size_t size) {
Expand All @@ -270,7 +290,9 @@ libhoth_error libhoth_key_provisioning_store_secrets(struct libhoth_device* dev,
};
memcpy(req.secrets, secrets, size);

return exec_key_provisioning_cmd(dev, &req, request_size);
size_t response_size = 0;
return exec_key_provisioning_cmd(dev, &req, request_size, NULL, 0,
&response_size);
}

libhoth_error libhoth_key_provisioning_load_mldsa_key(
Expand Down Expand Up @@ -306,7 +328,9 @@ libhoth_error libhoth_key_provisioning_load_mldsa_key(
};
memcpy(req.data, key + offset, chunk_size);

libhoth_error err = exec_key_provisioning_cmd(dev, &req, req_size);
size_t response_size = 0;
libhoth_error err =
exec_key_provisioning_cmd(dev, &req, req_size, NULL, 0, &response_size);
if (err != HOTH_SUCCESS) {
return err;
}
Expand Down
10 changes: 10 additions & 0 deletions protocol/provisioning.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ struct hoth_key_provisioning_request_header {
uint16_t size;
} __attribute__((packed));

#define HOTH_KEY_PROVISIONING_MAX_ENCRYPTION_KEY_CERT_CHAIN_SIZE \
(LIBHOTH_MAILBOX_SIZE - sizeof(struct hoth_host_response))

#define HOTH_KEY_PROVISIONING_MAX_SECRETS_SIZE \
(LIBHOTH_MAILBOX_SIZE - sizeof(struct hoth_host_request) - \
sizeof(struct hoth_key_provisioning_request_header))
Expand Down Expand Up @@ -155,6 +158,13 @@ libhoth_error libhoth_provisioning_log_write(struct libhoth_device* dev,
libhoth_error libhoth_provisioning_log_commit(struct libhoth_device* dev,
const uint8_t* data, size_t size);

// Retrieves the provisioning encryption key certificate chain from the device
// into |cert_chain|, which must be able to hold |cert_chain_capacity| bytes.
// On success |out_size| receives the number of bytes written to |cert_chain|.
libhoth_error libhoth_key_provisioning_get_encryption_key(
struct libhoth_device* dev, uint8_t* cert_chain, size_t cert_chain_capacity,
size_t* out_size);

// Stores |size| bytes of secrets that were encrypted with the provisioning
// encryption key.
libhoth_error libhoth_key_provisioning_store_secrets(struct libhoth_device* dev,
Expand Down
Loading