mirror of
https://github.com/polhenarejos/pico-hsm.git
synced 2026-08-23 05:06:53 +01:00
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:
@@ -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
|
||||
|
||||
+1
-1
Submodule pico-keys-sdk updated: e3ade9854d...a24137a2b8
+140
-467
@@ -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, ¤t) == 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, ¤t);
|
||||
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(¤t->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, ¤t);
|
||||
if (r != PICOKEYS_OK) {
|
||||
return r;
|
||||
}
|
||||
file_object_descriptor_t *object = hsm_key_manifest_find(¤t->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, ¤t);
|
||||
if (r != PICOKEYS_OK) {
|
||||
return r;
|
||||
}
|
||||
file_object_descriptor_t *current_object = hsm_key_manifest_find(¤t->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, ¤t);
|
||||
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(¤t->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, ¤t);
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user