Use SDK container store for HSM key objects

Preserve allocation, authorization, rollback, and the existing flash format.

Signed-off-by: Pol Henarejos <pol.henarejos@cttc.es>
This commit is contained in:
Pol Henarejos
2026-07-26 20:47:16 +02:00
parent a7095270b8
commit d2f6b72602
3 changed files with 142 additions and 468 deletions
+1
View File
@@ -174,6 +174,7 @@ if(NOT ESP_PLATFORM)
add_executable(hsm_key_container_test
${CMAKE_CURRENT_LIST_DIR}/pico-keys-sdk/src/fs/object_container.c
${CMAKE_CURRENT_LIST_DIR}/pico-keys-sdk/src/fs/object_container_store.c
${CMAKE_CURRENT_LIST_DIR}/pico-keys-sdk/src/fs/object_policy.c
${CMAKE_CURRENT_LIST_DIR}/pico-keys-sdk/src/fs/object_store_txn.c
${CMAKE_CURRENT_LIST_DIR}/src/hsm/key_container.c
+140 -467
View File
@@ -17,6 +17,7 @@
#include "picokeys.h"
#include "key_container.h"
#include "object_container_store.h"
#include "object_authorization.h"
#include "object_provider.h"
#include "sc_hsm.h"
@@ -32,19 +33,11 @@
#define HSM_KEY_RECORD_FID_MASK 0x1fffu
#define HSM_KEY_CONTAINER_COMMIT_TIMEOUT_MS 5000u
#define HSM_KEY_CONTAINER_MARKER_SIZE 8u
#define HSM_KEY_CONTAINER_MAX_MANIFEST_SIZE (FILE_OBJECT_MANIFEST_HEADER_SIZE + FILE_OBJECT_MANIFEST_MAX_OBJECTS * FILE_OBJECT_DESCRIPTOR_SIZE + FILE_OBJECT_AUTH_TAG_SIZE)
typedef struct hsm_key_manifest_candidate {
file_object_manifest_t manifest;
uint8_t slot;
bool valid;
} hsm_key_manifest_candidate_t;
static const uint8_t hsm_key_container_marker_magic[4] = { 'P', 'K', 'H', '1' };
static const uint8_t hsm_key_allocator_marker_magic[4] = { 'P', 'K', 'A', '1' };
static bool hsm_key_allocator_marker_valid(void);
static int hsm_key_container_unseal(const file_object_manifest_t *manifest, const file_object_descriptor_t *object, const file_object_record_protector_t *protector, uint8_t *data, size_t capacity, size_t *written);
static const uint8_t hsm_key_internal_policy[] = {
FILE_OBJECT_POLICY_FORMAT_VERSION, 1,
@@ -149,7 +142,8 @@ static int hsm_key_allocator_claim(void) {
return flash_commit_sync(HSM_KEY_CONTAINER_COMMIT_TIMEOUT_MS) ? PICOKEYS_OK : PICOKEYS_ERR_MEMORY_FATAL;
}
static int hsm_key_policy_hash(uint16_t policy_id, uint8_t hash[FILE_OBJECT_POLICY_HASH_SIZE]) {
static int hsm_key_policy_hash(void *ctx, uint16_t policy_id, uint8_t hash[FILE_OBJECT_POLICY_HASH_SIZE]) {
(void)ctx;
const uint8_t *policy = NULL;
size_t policy_size = 0;
if (policy_id == HSM_OBJECT_KEY_POLICY_ID) {
@@ -165,55 +159,20 @@ static int hsm_key_policy_hash(uint16_t policy_id, uint8_t hash[FILE_OBJECT_POLI
return file_object_policy_hash(policy, policy_size, hash);
}
static int hsm_key_manifest_parse_slot(uint8_t key_id, uint8_t slot, const file_object_authenticator_t *auth, hsm_key_manifest_candidate_t *candidate) {
memset(candidate, 0, sizeof(*candidate));
file_t *file = file_search(hsm_key_manifest_fid(key_id, slot));
if (!file_has_data(file)) {
return PICOKEYS_ERR_FILE_NOT_FOUND;
}
int r = file_object_manifest_parse(file_get_data(file), file_get_size(file), auth, NULL, NULL, &candidate->manifest);
if (r != PICOKEYS_OK) {
return r;
}
if (candidate->manifest.namespace_id != HSM_OBJECT_NAMESPACE || candidate->manifest.container_kind != HSM_KEY_CONTAINER_KIND || candidate->manifest.container_id != key_id || candidate->manifest.object_count == 0) {
return PICOKEYS_WRONG_DATA;
}
for (uint16_t i = 0; i < candidate->manifest.object_count; i++) {
const file_object_descriptor_t *object = &candidate->manifest.objects[i];
uint16_t record_fid = hsm_key_record_fid(object->record_id);
file_t *record = file_search(record_fid);
file_object_record_info_t info;
if (record_fid == UINT16_MAX || !file_has_data(record) || file_object_record_header_parse(file_get_data(record), file_get_size(record), object, &info) != PICOKEYS_OK) {
return PICOKEYS_WRONG_DATA;
}
}
candidate->slot = slot;
candidate->valid = true;
return PICOKEYS_OK;
static uint16_t hsm_key_layout_manifest_fid(void *ctx, uint32_t container_id, uint8_t slot) {
(void)ctx;
return hsm_key_manifest_fid((uint8_t)container_id, slot);
}
static int hsm_key_manifest_load(uint8_t key_id, const file_object_authenticator_t *auth, hsm_key_manifest_candidate_t candidates[2], hsm_key_manifest_candidate_t **current) {
bool found = false;
bool storage_present = false;
*current = NULL;
for (uint8_t slot = 0; slot < 2; slot++) {
storage_present |= file_has_data(file_search(hsm_key_manifest_fid(key_id, slot)));
int r = hsm_key_manifest_parse_slot(key_id, slot, auth, &candidates[slot]);
if (r == PICOKEYS_OK) {
if (*current && (*current)->manifest.generation == candidates[slot].manifest.generation) {
return PICOKEYS_WRONG_DATA;
}
if (!*current || candidates[slot].manifest.generation > (*current)->manifest.generation) {
*current = &candidates[slot];
}
found = true;
}
else if (r != PICOKEYS_ERR_FILE_NOT_FOUND && r != PICOKEYS_WRONG_DATA && r != PICOKEYS_WRONG_SIGNATURE) {
return r;
}
static int hsm_key_layout_record_fid(void *ctx, uint32_t container_id, const file_object_descriptor_t *object, uint16_t *fid) {
(void)ctx;
(void)container_id;
if (!object || !fid) {
return PICOKEYS_ERR_NULL_PARAM;
}
if (!found) {
return storage_present ? PICOKEYS_WRONG_DATA : PICOKEYS_ERR_FILE_NOT_FOUND;
*fid = hsm_key_record_fid(object->record_id);
if (*fid == UINT16_MAX) {
return PICOKEYS_WRONG_DATA;
}
return PICOKEYS_OK;
}
@@ -228,14 +187,6 @@ bool hsm_key_container_can_create(uint8_t key_id) {
return !file_search(HSM_KEY_ALLOCATOR_MARKER_FID) && !file_search(HSM_KEY_ALLOCATOR_RECORD_0_FID) && !file_search(HSM_KEY_ALLOCATOR_RECORD_1_FID) && !file_search(HSM_KEY_ALLOCATOR_COMMIT_0_FID) && !file_search(HSM_KEY_ALLOCATOR_COMMIT_1_FID);
}
bool hsm_key_container_can_resume(uint8_t key_id) {
const file_object_authenticator_t *auth = hsm_object_manifest_authenticator();
hsm_key_manifest_candidate_t candidates[2];
hsm_key_manifest_candidate_t *current = NULL;
return auth && hsm_key_manifest_load(key_id, auth, candidates, &current) == PICOKEYS_OK;
}
static int hsm_key_record_id_allocate(const file_object_authenticator_t *auth, uint64_t *record_id) {
int r = hsm_key_allocator_claim();
if (r != PICOKEYS_OK) {
@@ -255,110 +206,22 @@ static int hsm_key_record_id_allocate(const file_object_authenticator_t *auth, u
return PICOKEYS_ERR_NO_MEMORY;
}
static int hsm_key_descriptor_compare(const void *left, const void *right) {
const file_object_descriptor_t *a = (const file_object_descriptor_t *)left;
const file_object_descriptor_t *b = (const file_object_descriptor_t *)right;
if (a->object_type != b->object_type) {
return a->object_type < b->object_type ? -1 : 1;
static int hsm_key_layout_record_allocate(void *ctx, uint32_t container_id, uint8_t target_slot, const file_object_container_write_t *write, const file_object_authenticator_t *auth, uint64_t *record_id, uint16_t *fid) {
(void)ctx;
(void)container_id;
(void)target_slot;
(void)write;
if (!auth || !record_id || !fid) {
return PICOKEYS_ERR_NULL_PARAM;
}
if (a->object_tag != b->object_tag) {
return a->object_tag < b->object_tag ? -1 : 1;
}
return 0;
}
static file_object_descriptor_t *hsm_key_manifest_find(file_object_manifest_t *manifest, uint16_t object_type) {
for (uint16_t i = 0; i < manifest->object_count; i++) {
if (manifest->objects[i].object_type == object_type && manifest->objects[i].object_tag == 0) {
return &manifest->objects[i];
}
}
return NULL;
}
static bool hsm_key_manifest_references(const file_object_manifest_t *manifest, uint64_t record_id) {
for (uint16_t i = 0; i < manifest->object_count; i++) {
if (manifest->objects[i].record_id == record_id) {
return true;
}
}
return false;
}
static int hsm_key_manifest_records_validate(const hsm_key_manifest_candidate_t *candidate, const file_object_record_protector_t *protector) {
for (uint16_t i = 0; i < candidate->manifest.object_count; i++) {
const file_object_descriptor_t *object = &candidate->manifest.objects[i];
uint8_t *plaintext = NULL;
if (object->logical_size > 0) {
plaintext = (uint8_t *)calloc(1, object->logical_size);
if (!plaintext) {
return PICOKEYS_ERR_MEMORY_FATAL;
}
}
size_t written = 0;
int r = hsm_key_container_unseal(&candidate->manifest, object, protector, plaintext, object->logical_size, &written);
if (plaintext) {
mbedtls_platform_zeroize(plaintext, object->logical_size);
free(plaintext);
}
if (r != PICOKEYS_OK || written != object->logical_size) {
return r == PICOKEYS_OK ? PICOKEYS_WRONG_LENGTH : r;
}
int r = hsm_key_record_id_allocate(auth, record_id);
if (r != PICOKEYS_OK) {
return r;
}
*fid = hsm_key_record_fid(*record_id);
return PICOKEYS_OK;
}
static int hsm_key_record_write(uint8_t key_id, const hsm_key_container_write_t *write, uint32_t generation, const file_object_authenticator_t *auth, const file_object_record_protector_t *protector, file_object_descriptor_t *descriptor) {
uint64_t record_id = 0;
int r = hsm_key_record_id_allocate(auth, &record_id);
if (r != PICOKEYS_OK) {
return r;
}
*descriptor = (file_object_descriptor_t) {
.object_type = write->object_type,
.object_tag = 0,
.generation = generation,
.logical_size = write->data_size,
.record_id = record_id,
.stored_size = write->data_size,
.policy_id = write->policy_id,
.key_domain = write->key_domain,
.protection = write->protection,
.flags = write->flags
};
file_object_manifest_t record_manifest = {
.namespace_id = HSM_OBJECT_NAMESPACE,
.container_kind = HSM_KEY_CONTAINER_KIND,
.container_id = key_id,
.generation = generation,
.previous_generation = generation > 1 ? generation - 1 : 0,
.has_object = true,
.object = *descriptor
};
uint8_t policy_hash[FILE_OBJECT_POLICY_HASH_SIZE];
r = hsm_key_policy_hash(write->policy_id, policy_hash);
if (r != PICOKEYS_OK) {
return r;
}
size_t record_size = FILE_OBJECT_RECORD_HEADER_SIZE + (size_t)write->data_size + FILE_OBJECT_AUTH_TAG_SIZE;
uint8_t *record = (uint8_t *)calloc(1, record_size);
if (!record) {
return PICOKEYS_ERR_MEMORY_FATAL;
}
size_t written = 0;
r = file_object_record_seal(&record_manifest, policy_hash, protector, write->data, write->data_size, record, record_size, &written);
if (r == PICOKEYS_OK && written != record_size) {
r = PICOKEYS_WRONG_LENGTH;
}
if (r == PICOKEYS_OK) {
r = hsm_key_replace_file(hsm_key_record_fid(record_id), record, (uint32_t)record_size);
}
mbedtls_platform_zeroize(record, record_size);
free(record);
return r;
}
static int hsm_key_marker_write(uint8_t key_id) {
uint8_t marker[HSM_KEY_CONTAINER_MARKER_SIZE] = { 'P', 'K', 'H', '1', 1, key_id, 0, 0 };
int r = hsm_key_replace_file((HSM_OBJECT_PREFIX << 8) | key_id, marker, sizeof(marker));
@@ -368,6 +231,71 @@ static int hsm_key_marker_write(uint8_t key_id) {
return flash_commit_sync(HSM_KEY_CONTAINER_COMMIT_TIMEOUT_MS) ? PICOKEYS_OK : PICOKEYS_ERR_MEMORY_FATAL;
}
static int hsm_key_layout_activate(void *ctx, uint32_t container_id) {
(void)ctx;
uint8_t key_id = (uint8_t)container_id;
if (hsm_key_container_is_marker(file_search((HSM_OBJECT_PREFIX << 8) | key_id))) {
return PICOKEYS_OK;
}
return hsm_key_marker_write(key_id);
}
static int hsm_key_layout_deactivate(void *ctx, uint32_t container_id) {
(void)ctx;
file_t *marker = file_search((HSM_OBJECT_PREFIX << 8) | (uint8_t)container_id);
if (!marker) {
return PICOKEYS_OK;
}
return file_delete_no_commit(marker);
}
static int hsm_key_layout_retire(void *ctx, uint32_t container_id, const file_object_container_state_t *state, const file_object_manifest_t *next, uint8_t current_slot, uint8_t target_slot) {
(void)ctx;
(void)container_id;
if (!state->candidates[target_slot].valid) {
return PICOKEYS_OK;
}
const file_object_manifest_t *current = current_slot < FILE_OBJECT_CONTAINER_SLOT_COUNT ? &state->candidates[current_slot].manifest : NULL;
const file_object_manifest_t *overwritten = &state->candidates[target_slot].manifest;
for (uint16_t i = 0; i < overwritten->object_count; i++) {
uint64_t record_id = overwritten->objects[i].record_id;
if (!file_object_container_references(next, record_id) && (!current || !file_object_container_references(current, record_id))) {
file_t *record = file_search(hsm_key_record_fid(record_id));
if (record) {
file_delete_no_commit(record);
}
}
}
flash_commit();
return PICOKEYS_OK;
}
static const file_object_container_layout_t hsm_key_container_layout = {
.namespace_id = HSM_OBJECT_NAMESPACE,
.container_kind = HSM_KEY_CONTAINER_KIND,
.commit_timeout_ms = HSM_KEY_CONTAINER_COMMIT_TIMEOUT_MS,
.manifest_fid = hsm_key_layout_manifest_fid,
.record_fid = hsm_key_layout_record_fid,
.record_allocate = hsm_key_layout_record_allocate,
.policy_hash = hsm_key_policy_hash,
.activate = hsm_key_layout_activate,
.deactivate = hsm_key_layout_deactivate,
.retire = hsm_key_layout_retire,
.rollback_new_records = true
};
static bool hsm_key_crypto(file_object_container_crypto_t *crypto) {
crypto->auth = hsm_object_manifest_authenticator();
crypto->protector = hsm_object_record_protector();
return crypto->auth && crypto->protector;
}
bool hsm_key_container_can_resume(uint8_t key_id) {
file_object_container_crypto_t crypto;
file_object_container_state_t state;
return hsm_key_crypto(&crypto) && file_object_container_load(&hsm_key_container_layout, key_id, &crypto, NULL, &state) == PICOKEYS_OK;
}
int hsm_key_container_update(uint8_t key_id, const hsm_key_container_write_t *writes, size_t write_count) {
if (!writes || write_count == 0 || write_count > FILE_OBJECT_MANIFEST_MAX_OBJECTS) {
return PICOKEYS_ERR_NULL_PARAM;
@@ -375,154 +303,26 @@ int hsm_key_container_update(uint8_t key_id, const hsm_key_container_write_t *wr
if (!hsm_object_authorization_key_operation(FILE_OBJECT_OPERATION_UPDATE, false)) {
return PICOKEYS_NO_LOGIN;
}
const file_object_authenticator_t *auth = hsm_object_manifest_authenticator();
const file_object_record_protector_t *protector = hsm_object_record_protector();
if (!auth || !protector) {
file_object_container_crypto_t crypto;
if (!hsm_key_crypto(&crypto)) {
return PICOKEYS_EXEC_ERROR;
}
hsm_key_manifest_candidate_t candidates[2];
hsm_key_manifest_candidate_t *current = NULL;
int r = hsm_key_manifest_load(key_id, auth, candidates, &current);
if (r != PICOKEYS_OK && r != PICOKEYS_ERR_FILE_NOT_FOUND) {
return r;
}
if (current) {
int current_status = hsm_key_manifest_records_validate(current, protector);
hsm_key_manifest_candidate_t *previous = &candidates[current->slot ^ 1u];
if (current_status != PICOKEYS_OK && previous->valid && hsm_key_manifest_records_validate(previous, protector) == PICOKEYS_OK) {
current = previous;
}
else if (current_status != PICOKEYS_OK) {
return current_status;
}
}
bool creating = r == PICOKEYS_ERR_FILE_NOT_FOUND;
if (creating && !hsm_key_container_can_create(key_id)) {
if (!file_has_data(file_search(hsm_key_manifest_fid(key_id, 0))) && !file_has_data(file_search(hsm_key_manifest_fid(key_id, 1))) && !hsm_key_container_can_create(key_id)) {
return PICOKEYS_WRONG_DATA;
}
r = PICOKEYS_OK;
file_object_manifest_t next = { 0 };
if (current) {
next = current->manifest;
if (next.generation == UINT32_MAX || next.extension_size != 0) {
return PICOKEYS_WRONG_DATA;
}
for (uint16_t i = 0; i < next.object_count; i++) {
if (next.objects[i].extension_size != 0) {
return PICOKEYS_WRONG_DATA;
}
}
next.previous_generation = next.generation;
next.generation++;
}
else {
next.namespace_id = HSM_OBJECT_NAMESPACE;
next.container_kind = HSM_KEY_CONTAINER_KIND;
next.container_id = key_id;
next.generation = 1;
}
uint64_t new_record_ids[FILE_OBJECT_MANIFEST_MAX_OBJECTS] = { 0 };
size_t new_record_count = 0;
file_object_container_write_t container_writes[FILE_OBJECT_MANIFEST_MAX_OBJECTS];
for (size_t i = 0; i < write_count; i++) {
const hsm_key_container_write_t *write = &writes[i];
if ((!write->data && write->data_size > 0) || write->object_type == 0 || write->object_type == UINT16_MAX || (write->flags & FILE_OBJECT_FLAG_INLINE) != 0) {
r = PICOKEYS_WRONG_DATA;
break;
}
for (size_t j = 0; j < i; j++) {
if (writes[j].object_type == write->object_type) {
r = PICOKEYS_WRONG_DATA;
break;
}
}
if (r != PICOKEYS_OK) {
break;
}
file_object_descriptor_t *object = hsm_key_manifest_find(&next, write->object_type);
uint32_t object_generation = object ? object->generation + 1 : 1;
if (object && object->generation == UINT32_MAX) {
r = PICOKEYS_WRONG_DATA;
break;
}
file_object_descriptor_t replacement;
r = hsm_key_record_write(key_id, write, object_generation, auth, protector, &replacement);
if (r != PICOKEYS_OK) {
break;
}
new_record_ids[new_record_count++] = replacement.record_id;
if (object) {
*object = replacement;
}
else if (next.object_count < FILE_OBJECT_MANIFEST_MAX_OBJECTS) {
next.objects[next.object_count++] = replacement;
next.has_object = true;
}
else {
r = PICOKEYS_ERR_NO_MEMORY;
break;
}
container_writes[i] = (file_object_container_write_t) {
.object_type = writes[i].object_type,
.data = writes[i].data,
.data_size = writes[i].data_size,
.policy_id = writes[i].policy_id,
.key_domain = writes[i].key_domain,
.protection = writes[i].protection,
.flags = writes[i].flags
};
}
if (r == PICOKEYS_OK && !flash_commit_sync(HSM_KEY_CONTAINER_COMMIT_TIMEOUT_MS)) {
r = PICOKEYS_ERR_MEMORY_FATAL;
}
if (r != PICOKEYS_OK) {
for (size_t i = 0; i < new_record_count; i++) {
file_t *record = file_search(hsm_key_record_fid(new_record_ids[i]));
if (record) {
file_delete_no_commit(record);
}
}
flash_commit();
return r;
}
qsort(next.objects, next.object_count, sizeof(next.objects[0]), hsm_key_descriptor_compare);
uint8_t manifest_data[HSM_KEY_CONTAINER_MAX_MANIFEST_SIZE];
size_t manifest_size = 0;
r = file_object_manifest_build(&next, NULL, 0, auth, manifest_data, sizeof(manifest_data), &manifest_size);
uint8_t target_slot = current ? current->slot ^ 1u : 0;
if (r == PICOKEYS_OK) {
r = hsm_key_replace_file(hsm_key_manifest_fid(key_id, target_slot), manifest_data, (uint32_t)manifest_size);
}
memset(manifest_data, 0, sizeof(manifest_data));
if (r == PICOKEYS_OK && !flash_commit_sync(HSM_KEY_CONTAINER_COMMIT_TIMEOUT_MS)) {
r = PICOKEYS_ERR_MEMORY_FATAL;
}
if (r != PICOKEYS_OK) {
for (size_t i = 0; i < new_record_count; i++) {
file_t *record = file_search(hsm_key_record_fid(new_record_ids[i]));
if (record) {
file_delete_no_commit(record);
}
}
flash_commit();
return r;
}
file_t *marker = file_search((HSM_OBJECT_PREFIX << 8) | key_id);
if (!hsm_key_container_is_marker(marker)) {
r = hsm_key_marker_write(key_id);
if (r != PICOKEYS_OK) {
return r;
}
}
hsm_key_manifest_candidate_t *overwritten = &candidates[target_slot];
if (overwritten->valid) {
for (uint16_t i = 0; i < overwritten->manifest.object_count; i++) {
uint64_t record_id = overwritten->manifest.objects[i].record_id;
if (!hsm_key_manifest_references(&next, record_id) && (!current || !hsm_key_manifest_references(&current->manifest, record_id))) {
file_t *record = file_search(hsm_key_record_fid(record_id));
if (record) {
file_delete_no_commit(record);
}
}
}
flash_commit();
}
return PICOKEYS_OK;
return file_object_container_update(&hsm_key_container_layout, key_id, container_writes, write_count, &crypto, NULL);
}
int hsm_key_container_store_object(uint8_t key_id, uint16_t object_type, const uint8_t *data, uint32_t data_size) {
@@ -547,94 +347,45 @@ int hsm_key_container_store_object(uint8_t key_id, uint16_t object_type, const u
return hsm_key_container_update(key_id, &write, 1);
}
static bool hsm_key_object_read_allowed(const file_object_descriptor_t *object, uint16_t operation, bool internal_firmware) {
if (internal_firmware) {
return true;
typedef struct hsm_key_access_context {
uint16_t operation;
bool internal_firmware;
} hsm_key_access_context_t;
static int hsm_key_object_access(void *ctx, const file_object_descriptor_t *object) {
const hsm_key_access_context_t *access = (const hsm_key_access_context_t *)ctx;
if (access->internal_firmware) {
return PICOKEYS_OK;
}
if (object->object_type == HSM_KEY_OBJECT_PRIVATE) {
return hsm_object_authorization_key_operation(operation, false);
return hsm_object_authorization_key_operation(access->operation, false) ? PICOKEYS_OK : PICOKEYS_NO_LOGIN;
}
return operation == FILE_OBJECT_OPERATION_READ && object->protection == FILE_OBJECT_PROTECTION_AUTHENTICATED_PUBLIC && (object->flags & FILE_OBJECT_FLAG_GENERIC_READABLE) != 0;
bool readable = access->operation == FILE_OBJECT_OPERATION_READ && object->protection == FILE_OBJECT_PROTECTION_AUTHENTICATED_PUBLIC && (object->flags & FILE_OBJECT_FLAG_GENERIC_READABLE) != 0;
return readable ? PICOKEYS_OK : PICOKEYS_NO_LOGIN;
}
int hsm_key_container_object_size(uint8_t key_id, uint16_t object_type, bool internal_firmware, uint32_t *object_size) {
if (!object_size) {
return PICOKEYS_ERR_NULL_PARAM;
file_object_container_crypto_t crypto;
if (!hsm_key_crypto(&crypto)) {
return PICOKEYS_EXEC_ERROR;
}
*object_size = 0;
const file_object_authenticator_t *auth = hsm_object_manifest_authenticator();
hsm_key_manifest_candidate_t candidates[2];
hsm_key_manifest_candidate_t *current = NULL;
int r = hsm_key_manifest_load(key_id, auth, candidates, &current);
if (r != PICOKEYS_OK) {
return r;
}
file_object_descriptor_t *object = hsm_key_manifest_find(&current->manifest, object_type);
if (!object) {
return PICOKEYS_ERR_FILE_NOT_FOUND;
}
if (!hsm_key_object_read_allowed(object, FILE_OBJECT_OPERATION_READ, internal_firmware)) {
return PICOKEYS_NO_LOGIN;
}
*object_size = object->logical_size;
return PICOKEYS_OK;
}
static int hsm_key_container_unseal(const file_object_manifest_t *manifest, const file_object_descriptor_t *object, const file_object_record_protector_t *protector, uint8_t *data, size_t capacity, size_t *written) {
file_object_manifest_t record_manifest = *manifest;
record_manifest.object_count = 1;
record_manifest.has_object = true;
record_manifest.object = *object;
uint8_t policy_hash[FILE_OBJECT_POLICY_HASH_SIZE];
int r = hsm_key_policy_hash(object->policy_id, policy_hash);
if (r != PICOKEYS_OK) {
return r;
}
file_t *record = file_search(hsm_key_record_fid(object->record_id));
if (!file_has_data(record)) {
return PICOKEYS_ERR_FILE_NOT_FOUND;
}
return file_object_record_unseal(&record_manifest, policy_hash, protector, file_get_data(record), file_get_size(record), data, capacity, written);
hsm_key_access_context_t access = {
.operation = FILE_OBJECT_OPERATION_READ,
.internal_firmware = internal_firmware
};
return file_object_container_object_size(&hsm_key_container_layout, key_id, object_type, 0, &crypto, NULL, hsm_key_object_access, &access, object_size);
}
int hsm_key_container_read(uint8_t key_id, uint16_t object_type, uint16_t operation, bool internal_firmware, uint8_t *data, size_t capacity, size_t *written) {
if ((!data && capacity > 0) || !written) {
return PICOKEYS_ERR_NULL_PARAM;
file_object_container_crypto_t crypto;
if (!hsm_key_crypto(&crypto)) {
return PICOKEYS_EXEC_ERROR;
}
*written = 0;
const file_object_authenticator_t *auth = hsm_object_manifest_authenticator();
const file_object_record_protector_t *protector = hsm_object_record_protector();
hsm_key_manifest_candidate_t candidates[2];
hsm_key_manifest_candidate_t *current = NULL;
int r = hsm_key_manifest_load(key_id, auth, candidates, &current);
if (r != PICOKEYS_OK) {
return r;
}
file_object_descriptor_t *current_object = hsm_key_manifest_find(&current->manifest, object_type);
if (!current_object) {
return PICOKEYS_ERR_FILE_NOT_FOUND;
}
for (uint8_t attempt = 0; attempt < 2; attempt++) {
hsm_key_manifest_candidate_t *candidate = attempt == 0 ? current : &candidates[current->slot ^ 1u];
if (!candidate || !candidate->valid) {
continue;
}
file_object_descriptor_t *object = attempt == 0 ? current_object : hsm_key_manifest_find(&candidate->manifest, object_type);
if (!object) {
continue;
}
if (!hsm_key_object_read_allowed(object, operation, internal_firmware)) {
return PICOKEYS_NO_LOGIN;
}
r = hsm_key_container_unseal(&candidate->manifest, object, protector, data, capacity, written);
if (r == PICOKEYS_OK) {
return PICOKEYS_OK;
}
}
if (data && capacity > 0) {
memset(data, 0, capacity);
}
return r;
hsm_key_access_context_t access = {
.operation = operation,
.internal_firmware = internal_firmware
};
return file_object_container_read(&hsm_key_container_layout, key_id, object_type, 0, &crypto, NULL, hsm_key_object_access, &access, data, capacity, written);
}
int hsm_key_container_remove_object(uint8_t key_id, uint16_t object_type) {
@@ -644,67 +395,11 @@ int hsm_key_container_remove_object(uint8_t key_id, uint16_t object_type) {
if (!hsm_object_authorization_key_operation(FILE_OBJECT_OPERATION_DELETE, false)) {
return PICOKEYS_NO_LOGIN;
}
const file_object_authenticator_t *auth = hsm_object_manifest_authenticator();
const file_object_record_protector_t *protector = hsm_object_record_protector();
hsm_key_manifest_candidate_t candidates[2];
hsm_key_manifest_candidate_t *current = NULL;
int r = hsm_key_manifest_load(key_id, auth, candidates, &current);
if (r != PICOKEYS_OK) {
return r;
file_object_container_crypto_t crypto;
if (!hsm_key_crypto(&crypto)) {
return PICOKEYS_EXEC_ERROR;
}
int current_status = hsm_key_manifest_records_validate(current, protector);
hsm_key_manifest_candidate_t *previous = &candidates[current->slot ^ 1u];
if (current_status != PICOKEYS_OK && previous->valid && hsm_key_manifest_records_validate(previous, protector) == PICOKEYS_OK) {
current = previous;
}
else if (current_status != PICOKEYS_OK) {
return current_status;
}
file_object_manifest_t next = current->manifest;
file_object_descriptor_t *object = hsm_key_manifest_find(&next, object_type);
if (!object) {
return PICOKEYS_ERR_FILE_NOT_FOUND;
}
if (next.generation == UINT32_MAX || next.object_count <= 1 || next.extension_size != 0 || object->extension_size != 0) {
return PICOKEYS_WRONG_DATA;
}
size_t object_index = (size_t)(object - next.objects);
memmove(&next.objects[object_index], &next.objects[object_index + 1], (next.object_count - object_index - 1u) * sizeof(next.objects[0]));
memset(&next.objects[next.object_count - 1u], 0, sizeof(next.objects[0]));
next.object_count--;
next.previous_generation = next.generation;
next.generation++;
uint8_t manifest_data[HSM_KEY_CONTAINER_MAX_MANIFEST_SIZE];
size_t manifest_size = 0;
r = file_object_manifest_build(&next, NULL, 0, auth, manifest_data, sizeof(manifest_data), &manifest_size);
uint8_t target_slot = current->slot ^ 1u;
if (r == PICOKEYS_OK) {
r = hsm_key_replace_file(hsm_key_manifest_fid(key_id, target_slot), manifest_data, (uint32_t)manifest_size);
}
memset(manifest_data, 0, sizeof(manifest_data));
if (r == PICOKEYS_OK && !flash_commit_sync(HSM_KEY_CONTAINER_COMMIT_TIMEOUT_MS)) {
r = PICOKEYS_ERR_MEMORY_FATAL;
}
if (r != PICOKEYS_OK) {
return r;
}
hsm_key_manifest_candidate_t *overwritten = &candidates[target_slot];
if (overwritten->valid) {
for (uint16_t i = 0; i < overwritten->manifest.object_count; i++) {
uint64_t record_id = overwritten->manifest.objects[i].record_id;
if (!hsm_key_manifest_references(&next, record_id) && !hsm_key_manifest_references(&current->manifest, record_id)) {
file_t *record = file_search(hsm_key_record_fid(record_id));
if (record) {
file_delete_no_commit(record);
}
}
}
flash_commit();
}
return PICOKEYS_OK;
return file_object_container_remove(&hsm_key_container_layout, key_id, object_type, 0, &crypto, NULL);
}
int hsm_key_container_detach_sidecars(uint8_t key_id) {
@@ -752,31 +447,9 @@ int hsm_key_container_delete(uint8_t key_id) {
if (!hsm_object_authorization_key_operation(FILE_OBJECT_OPERATION_DELETE, false)) {
return PICOKEYS_NO_LOGIN;
}
const file_object_authenticator_t *auth = hsm_object_manifest_authenticator();
hsm_key_manifest_candidate_t candidates[2];
hsm_key_manifest_candidate_t *current = NULL;
int r = hsm_key_manifest_load(key_id, auth, candidates, &current);
if (r != PICOKEYS_OK) {
return r;
file_object_container_crypto_t crypto;
if (!hsm_key_crypto(&crypto)) {
return PICOKEYS_EXEC_ERROR;
}
(void)current;
for (uint8_t slot = 0; slot < 2; slot++) {
if (candidates[slot].valid) {
for (uint16_t i = 0; i < candidates[slot].manifest.object_count; i++) {
file_t *record = file_search(hsm_key_record_fid(candidates[slot].manifest.objects[i].record_id));
if (record) {
file_delete_no_commit(record);
}
}
}
file_t *manifest = file_search(hsm_key_manifest_fid(key_id, slot));
if (manifest) {
file_delete_no_commit(manifest);
}
}
file_t *marker = file_search((HSM_OBJECT_PREFIX << 8) | key_id);
if (marker) {
file_delete_no_commit(marker);
}
return flash_commit_sync(HSM_KEY_CONTAINER_COMMIT_TIMEOUT_MS) ? PICOKEYS_OK : PICOKEYS_ERR_MEMORY_FATAL;
return file_object_container_delete(&hsm_key_container_layout, key_id, &crypto, NULL);
}