Added identity-domain validation and duplicate EdgeRef handling to the FS adapter, plus new FS tests

This commit is contained in:
Carl Niklas Rydberg 2025-12-21 20:42:20 +01:00
parent 070265085f
commit 47504644f7
2 changed files with 324 additions and 14 deletions

View file

@ -6,6 +6,56 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
static bool amduat_tgk_store_fs_id_space_valid(
amduat_tgk_id_space_config_t id_space) {
size_t i;
size_t j;
if (id_space.domains_len != 0 && id_space.domains == NULL) {
return false;
}
for (i = 0; i < id_space.domains_len; ++i) {
for (j = i + 1; j < id_space.domains_len; ++j) {
if (id_space.domains[i].hash_id == id_space.domains[j].hash_id) {
return false;
}
}
}
return true;
}
static bool amduat_tgk_store_fs_artifact_eq(amduat_artifact_t a,
amduat_artifact_t b) {
if ((a.bytes.len != 0 && a.bytes.data == NULL) ||
(b.bytes.len != 0 && b.bytes.data == NULL)) {
return false;
}
return amduat_artifact_eq(a, b);
}
static bool amduat_tgk_store_fs_reference_clone(amduat_reference_t src,
amduat_reference_t *dst) {
uint8_t *digest_copy;
if (dst == NULL) {
return false;
}
*dst = amduat_reference(src.hash_id, amduat_octets(NULL, 0));
if (src.digest.len == 0) {
return true;
}
if (src.digest.data == NULL) {
return false;
}
digest_copy = (uint8_t *)malloc(src.digest.len);
if (digest_copy == NULL) {
return false;
}
memcpy(digest_copy, src.digest.data, src.digest.len);
dst->digest = amduat_octets(digest_copy, src.digest.len);
return true;
}
static void amduat_tgk_store_fs_reference_free(amduat_reference_t *ref) { static void amduat_tgk_store_fs_reference_free(amduat_reference_t *ref) {
if (ref == NULL) { if (ref == NULL) {
return; return;
@ -166,6 +216,7 @@ bool amduat_tgk_store_fs_init(amduat_tgk_store_fs_t *fs,
amduat_reference_t *refs = NULL; amduat_reference_t *refs = NULL;
size_t refs_len = 0; size_t refs_len = 0;
amduat_tgk_store_mem_artifact_t *artifacts = NULL; amduat_tgk_store_mem_artifact_t *artifacts = NULL;
size_t artifacts_len = 0;
size_t i; size_t i;
if (fs == NULL || manifest_path == NULL || asl_store == NULL) { if (fs == NULL || manifest_path == NULL || asl_store == NULL) {
@ -173,6 +224,10 @@ bool amduat_tgk_store_fs_init(amduat_tgk_store_fs_t *fs,
} }
memset(fs, 0, sizeof(*fs)); memset(fs, 0, sizeof(*fs));
if (!amduat_tgk_store_fs_id_space_valid(config.id_space)) {
return false;
}
if (!amduat_tgk_store_fs_load_manifest(manifest_path, if (!amduat_tgk_store_fs_load_manifest(manifest_path,
manifest_format, manifest_format,
&refs, &refs,
@ -191,23 +246,49 @@ bool amduat_tgk_store_fs_init(amduat_tgk_store_fs_t *fs,
for (i = 0; i < refs_len; ++i) { for (i = 0; i < refs_len; ++i) {
amduat_asl_store_error_t err; amduat_asl_store_error_t err;
artifacts[i].ref = refs[i]; amduat_artifact_t artifact = amduat_artifact(amduat_octets(NULL, 0));
artifacts[i].artifact = amduat_artifact(amduat_octets(NULL, 0)); bool duplicate = false;
err = amduat_asl_store_get(asl_store, refs[i], &artifacts[i].artifact); bool conflict = false;
if (err != AMDUAT_ASL_STORE_OK) {
size_t j; size_t j;
for (j = 0; j < i; ++j) {
amduat_tgk_store_fs_artifact_bytes_free(&artifacts[j]); err = amduat_asl_store_get(asl_store, refs[i], &artifact);
} if (err != AMDUAT_ASL_STORE_OK) {
amduat_asl_ref_list_free(refs, refs_len); goto cleanup;
free(artifacts);
return false;
}
} }
free(refs); for (j = 0; j < artifacts_len; ++j) {
if (!amduat_reference_eq(artifacts[j].ref, refs[i])) {
continue;
}
duplicate = true;
if (!amduat_tgk_store_fs_artifact_eq(artifacts[j].artifact, artifact)) {
conflict = true;
}
break;
}
if (conflict) {
free((void *)artifact.bytes.data);
goto cleanup;
}
if (duplicate) {
free((void *)artifact.bytes.data);
continue;
}
if (!amduat_tgk_store_fs_reference_clone(refs[i],
&artifacts[artifacts_len].ref)) {
free((void *)artifact.bytes.data);
goto cleanup;
}
artifacts[artifacts_len].artifact = artifact;
artifacts_len++;
}
amduat_asl_ref_list_free(refs, refs_len);
refs = NULL;
fs->artifacts = artifacts; fs->artifacts = artifacts;
fs->artifacts_len = refs_len; fs->artifacts_len = artifacts_len;
if (!amduat_tgk_store_mem_init(&fs->mem, config, fs->artifacts, if (!amduat_tgk_store_mem_init(&fs->mem, config, fs->artifacts,
fs->artifacts_len)) { fs->artifacts_len)) {
@ -215,6 +296,16 @@ bool amduat_tgk_store_fs_init(amduat_tgk_store_fs_t *fs,
return false; return false;
} }
return true; return true;
cleanup:
if (refs != NULL) {
amduat_asl_ref_list_free(refs, refs_len);
}
for (i = 0; i < artifacts_len; ++i) {
amduat_tgk_store_fs_artifact_full_free(&artifacts[i]);
}
free(artifacts);
return false;
} }
bool amduat_tgk_store_fs_init_with_asl_fs( bool amduat_tgk_store_fs_init_with_asl_fs(

View file

@ -387,6 +387,225 @@ cleanup:
return exit_code; return exit_code;
} }
int main(void) { static int test_init_rejects_duplicate_hash_id(void) {
return test_manifest_load(); amduat_asl_store_config_t asl_config;
amduat_asl_store_fs_t asl_fs;
amduat_asl_store_t asl_store;
amduat_tgk_store_config_t tgk_config;
amduat_tgk_store_fs_t fs;
amduat_tgk_identity_domain_t domains[2];
char *root = NULL;
char *manifest_path = NULL;
int exit_code = 1;
memset(&tgk_config, 0, sizeof(tgk_config));
memset(&fs, 0, sizeof(fs));
root = make_temp_root();
if (root == NULL) {
fprintf(stderr, "dup hash temp root failed\n");
goto cleanup;
}
if (!join_path(root, "manifest.txt", &manifest_path)) {
fprintf(stderr, "dup hash manifest path failed\n");
goto cleanup;
}
if (!write_manifest(manifest_path, NULL, 0)) {
fprintf(stderr, "dup hash manifest write failed\n");
goto cleanup;
}
asl_config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
asl_config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_fs_init(&asl_fs, asl_config, root)) {
fprintf(stderr, "dup hash asl store fs init failed\n");
goto cleanup;
}
amduat_asl_store_init(&asl_store, asl_config, amduat_asl_store_fs_ops(),
&asl_fs);
domains[0].encoding_profile = AMDUAT_ENC_ASL1_CORE_V1;
domains[0].hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
domains[1].encoding_profile = AMDUAT_ENC_ASL1_CORE_V1;
domains[1].hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
tgk_config.id_space.domains = domains;
tgk_config.id_space.domains_len = 2;
if (amduat_tgk_store_fs_init(&fs, tgk_config, manifest_path,
AMDUAT_FORMAT_REF_HEX, &asl_store)) {
fprintf(stderr, "dup hash init accepted\n");
amduat_tgk_store_fs_free(&fs);
goto cleanup;
}
exit_code = 0;
cleanup:
free(manifest_path);
if (root != NULL) {
remove_tree(root);
}
free(root);
return exit_code;
}
static int test_manifest_duplicate_refs(void) {
amduat_asl_store_config_t asl_config;
amduat_asl_store_fs_t asl_fs;
amduat_asl_store_t asl_store;
amduat_tgk_store_config_t tgk_config;
amduat_tgk_store_t store;
amduat_tgk_store_fs_t fs;
amduat_tgk_identity_domain_t domains[1];
uint32_t edge_tags[1];
amduat_tgk_edge_type_id_t edge_types[1];
amduat_asl_encoding_profile_id_t encodings[1];
amduat_octets_t edge_bytes;
amduat_reference_t ref_edge;
amduat_reference_t node_a;
amduat_reference_t node_b;
amduat_reference_t payload;
uint8_t digest_node_a[32];
uint8_t digest_node_b[32];
uint8_t digest_payload[32];
amduat_tgk_graph_scan_result_t scan;
amduat_tgk_edge_body_t body;
amduat_tgk_edge_body_t edge;
amduat_reference_t edge_from[1];
amduat_reference_t edge_to[1];
char *root = NULL;
char *manifest_path = NULL;
int exit_code = 1;
bool fs_ready = false;
memset(&tgk_config, 0, sizeof(tgk_config));
memset(&fs, 0, sizeof(fs));
memset(&body, 0, sizeof(body));
edge_bytes = amduat_octets(NULL, 0);
root = make_temp_root();
if (root == NULL) {
fprintf(stderr, "dup refs temp root failed\n");
goto cleanup;
}
if (!join_path(root, "manifest.txt", &manifest_path)) {
fprintf(stderr, "dup refs manifest path failed\n");
goto cleanup;
}
asl_config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1;
asl_config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
if (!amduat_asl_store_fs_init(&asl_fs, asl_config, root)) {
fprintf(stderr, "dup refs asl store fs init failed\n");
goto cleanup;
}
amduat_asl_store_init(&asl_store, asl_config, amduat_asl_store_fs_ops(),
&asl_fs);
domains[0].encoding_profile = AMDUAT_ENC_ASL1_CORE_V1;
domains[0].hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
edge_tags[0] = TYPE_TAG_TGK1_EDGE_V1;
edge_types[0] = 0x10;
encodings[0] = TGK1_EDGE_ENC_V1;
tgk_config.id_space.domains = domains;
tgk_config.id_space.domains_len = 1;
tgk_config.artifact_scope.description = amduat_octets(NULL, 0);
tgk_config.tgk_profiles.edge_tags = edge_tags;
tgk_config.tgk_profiles.edge_tags_len = 1;
tgk_config.tgk_profiles.edge_types = edge_types;
tgk_config.tgk_profiles.edge_types_len = 1;
tgk_config.tgk_profiles.encodings = encodings;
tgk_config.tgk_profiles.encodings_len = 1;
node_a = make_ref(0xa1, digest_node_a);
node_b = make_ref(0xb1, digest_node_b);
payload = make_ref(0xe1, digest_payload);
memset(&edge, 0, sizeof(edge));
edge.type = 0x10;
edge_from[0] = node_a;
edge.from = edge_from;
edge.from_len = 1;
edge_to[0] = node_b;
edge.to = edge_to;
edge.to_len = 1;
edge.payload = payload;
if (!amduat_enc_tgk1_edge_encode_v1(&edge, &edge_bytes)) {
fprintf(stderr, "dup refs edge encode failed\n");
goto cleanup;
}
if (amduat_asl_store_put(
&asl_store,
amduat_artifact_with_type(edge_bytes,
amduat_type_tag(TYPE_TAG_TGK1_EDGE_V1)),
&ref_edge) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "dup refs asl store put failed\n");
goto cleanup;
}
{
amduat_reference_t refs[2];
refs[0] = ref_edge;
refs[1] = ref_edge;
if (!write_manifest(manifest_path, refs, 2)) {
fprintf(stderr, "dup refs manifest write failed\n");
goto cleanup;
}
}
if (!amduat_tgk_store_fs_init(&fs, tgk_config, manifest_path,
AMDUAT_FORMAT_REF_HEX, &asl_store)) {
fprintf(stderr, "dup refs tgk store fs init failed\n");
goto cleanup;
}
fs_ready = true;
amduat_tgk_store_init(&store, tgk_config, amduat_tgk_store_fs_ops(), &fs);
if (amduat_tgk_store_resolve_edge(&store, ref_edge, &body) != 0) {
fprintf(stderr, "dup refs resolve_edge failed\n");
goto cleanup;
}
amduat_tgk_edge_body_free(&body);
memset(&body, 0, sizeof(body));
if (!amduat_tgk_store_scan_edges(&store,
(amduat_tgk_edge_type_filter_t){0},
amduat_octets(NULL, 0), false, &scan)) {
fprintf(stderr, "dup refs scan_edges failed\n");
goto cleanup;
}
if (scan.edges.len != 1) {
fprintf(stderr, "dup refs scan_edges count mismatch\n");
amduat_tgk_graph_scan_result_free(&scan);
goto cleanup;
}
amduat_tgk_graph_scan_result_free(&scan);
exit_code = 0;
cleanup:
amduat_tgk_edge_body_free(&body);
if (fs_ready) {
amduat_tgk_store_fs_free(&fs);
}
free((void *)edge_bytes.data);
free_ref(&ref_edge);
free(manifest_path);
if (root != NULL) {
remove_tree(root);
}
free(root);
return exit_code;
}
int main(void) {
if (test_manifest_load() != 0 ||
test_init_rejects_duplicate_hash_id() != 0 ||
test_manifest_duplicate_refs() != 0) {
return 1;
}
return 0;
} }