#include "amduat/asl/asl_store_fs.h" #include "amduat/asl/ref_text.h" #include "amduat/enc/asl1_core.h" #include "amduat/enc/tgk1_edge.h" #include "amduat/hash/asl1.h" #include "amduat/tgk/store.h" #include "amduat/tgk/tgk_store_fs.h" #include #include #include #include #include #include #include #include #include #include static void fill_digest(uint8_t *out, uint8_t value) { memset(out, value, 32); } static amduat_reference_t make_ref(uint8_t value, uint8_t *storage) { fill_digest(storage, value); return amduat_reference(AMDUAT_HASH_ASL1_ID_SHA256, amduat_octets(storage, 32)); } static void free_ref(amduat_reference_t *ref) { if (ref == NULL) { return; } free((void *)ref->digest.data); ref->digest.data = NULL; ref->digest.len = 0; } static bool join_path(const char *base, const char *segment, char **out_path) { size_t base_len; size_t seg_len; bool needs_sep; size_t total_len; char *buffer; size_t offset; if (base == NULL || segment == NULL || out_path == NULL) { return false; } if (base[0] == '\0' || segment[0] == '\0') { return false; } base_len = strlen(base); seg_len = strlen(segment); needs_sep = base[base_len - 1u] != '/'; total_len = base_len + (needs_sep ? 1u : 0u) + seg_len + 1u; buffer = (char *)malloc(total_len); if (buffer == NULL) { return false; } offset = 0u; memcpy(buffer + offset, base, base_len); offset += base_len; if (needs_sep) { buffer[offset++] = '/'; } memcpy(buffer + offset, segment, seg_len); offset += seg_len; buffer[offset] = '\0'; *out_path = buffer; return true; } static bool remove_tree(const char *path) { struct stat st; DIR *dir; struct dirent *entry; if (path == NULL) { return false; } if (lstat(path, &st) != 0) { return errno == ENOENT; } if (!S_ISDIR(st.st_mode)) { return unlink(path) == 0; } dir = opendir(path); if (dir == NULL) { return false; } while ((entry = readdir(dir)) != NULL) { char *child = NULL; if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { continue; } if (!join_path(path, entry->d_name, &child)) { closedir(dir); return false; } if (!remove_tree(child)) { free(child); closedir(dir); return false; } free(child); } if (closedir(dir) != 0) { return false; } return rmdir(path) == 0; } static char *make_temp_root(void) { char *templ; const char template_prefix[] = "/tmp/amduat_test_tgk_store_fs_XXXXXX"; templ = (char *)malloc(sizeof(template_prefix)); if (templ == NULL) { return NULL; } memcpy(templ, template_prefix, sizeof(template_prefix)); if (mkdtemp(templ) == NULL) { free(templ); return NULL; } return templ; } static bool write_manifest(const char *path, const amduat_reference_t *refs, size_t refs_len) { FILE *stream; size_t i; if (path == NULL || (refs_len != 0 && refs == NULL)) { return false; } stream = fopen(path, "wb"); if (stream == NULL) { return false; } for (i = 0; i < refs_len; ++i) { char *hex = NULL; if (!amduat_asl_ref_encode_hex(refs[i], &hex)) { fclose(stream); return false; } if (fprintf(stream, "%s\n", hex) < 0) { free(hex); fclose(stream); return false; } free(hex); } return fclose(stream) == 0; } static int test_manifest_load(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[2]; amduat_reference_t ref_edge1; amduat_reference_t ref_edge2; amduat_reference_t node_a; amduat_reference_t node_b; amduat_reference_t node_c; amduat_reference_t payload1; amduat_reference_t payload2; uint8_t digest_node_a[32]; uint8_t digest_node_b[32]; uint8_t digest_node_c[32]; uint8_t digest_payload1[32]; uint8_t digest_payload2[32]; amduat_tgk_graph_edge_view_list_t edges; amduat_tgk_edge_body_t body; amduat_tgk_edge_body_t edge1; amduat_tgk_edge_body_t edge2; amduat_reference_t edge1_from[1]; amduat_reference_t edge1_to[1]; amduat_reference_t edge2_from[1]; amduat_reference_t edge2_to[1]; char *root = NULL; char *manifest_path = NULL; int exit_code = 1; bool fs_ready = false; memset(&edges, 0, sizeof(edges)); memset(&body, 0, sizeof(body)); edge_bytes[0] = amduat_octets(NULL, 0); edge_bytes[1] = amduat_octets(NULL, 0); memset(&ref_edge1, 0, sizeof(ref_edge1)); memset(&ref_edge2, 0, sizeof(ref_edge2)); memset(&fs, 0, sizeof(fs)); root = make_temp_root(); if (root == NULL) { fprintf(stderr, "temp root failed\n"); goto cleanup; } if (!join_path(root, "manifest.txt", &manifest_path)) { fprintf(stderr, "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, "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; memset(&tgk_config, 0, sizeof(tgk_config)); 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); node_c = make_ref(0xc1, digest_node_c); payload1 = make_ref(0xe1, digest_payload1); payload2 = make_ref(0xe2, digest_payload2); memset(&edge1, 0, sizeof(edge1)); edge1.type = 0x10; edge1_from[0] = node_a; edge1.from = edge1_from; edge1.from_len = 1; edge1_to[0] = node_b; edge1.to = edge1_to; edge1.to_len = 1; edge1.payload = payload1; memset(&edge2, 0, sizeof(edge2)); edge2.type = 0x10; edge2_from[0] = node_a; edge2.from = edge2_from; edge2.from_len = 1; edge2_to[0] = node_c; edge2.to = edge2_to; edge2.to_len = 1; edge2.payload = payload2; if (!amduat_enc_tgk1_edge_encode_v1(&edge1, &edge_bytes[0]) || !amduat_enc_tgk1_edge_encode_v1(&edge2, &edge_bytes[1])) { fprintf(stderr, "edge encode failed\n"); goto cleanup; } if (amduat_asl_store_put( &asl_store, amduat_artifact_with_type(edge_bytes[0], amduat_type_tag(TYPE_TAG_TGK1_EDGE_V1)), &ref_edge1) != AMDUAT_ASL_STORE_OK || amduat_asl_store_put( &asl_store, amduat_artifact_with_type(edge_bytes[1], amduat_type_tag(TYPE_TAG_TGK1_EDGE_V1)), &ref_edge2) != AMDUAT_ASL_STORE_OK) { fprintf(stderr, "asl store put failed\n"); goto cleanup; } { amduat_reference_t refs[2]; refs[0] = ref_edge1; refs[1] = ref_edge2; if (!write_manifest(manifest_path, refs, 2)) { fprintf(stderr, "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, "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_edge1, &body) != 0) { fprintf(stderr, "resolve_edge failed\n"); goto cleanup; } if (body.type != 0x10 || body.from_len != 1 || body.to_len != 1 || !amduat_reference_eq(body.from[0], node_a) || !amduat_reference_eq(body.to[0], node_b)) { fprintf(stderr, "resolve_edge mismatch\n"); amduat_tgk_edge_body_free(&body); goto cleanup; } amduat_tgk_edge_body_free(&body); memset(&body, 0, sizeof(body)); { amduat_tgk_edge_type_filter_t filter; amduat_tgk_edge_type_id_t types[1] = {0x10}; size_t seen_b = 0; size_t seen_c = 0; size_t i; filter.types = types; filter.types_len = 1; if (!amduat_tgk_store_edges_from(&store, node_a, filter, &edges)) { fprintf(stderr, "edges_from failed\n"); goto cleanup; } if (edges.len != 2) { fprintf(stderr, "edges_from len mismatch\n"); goto cleanup; } for (i = 0; i < edges.len; ++i) { const amduat_tgk_graph_edge_view_t *edge = &edges.edges[i]; if (edge->body.type != 0x10 || edge->body.from_len != 1 || edge->body.to_len != 1 || !amduat_reference_eq(edge->body.from[0], node_a)) { fprintf(stderr, "edges_from body mismatch\n"); goto cleanup; } if (amduat_reference_eq(edge->body.to[0], node_b)) { seen_b++; } else if (amduat_reference_eq(edge->body.to[0], node_c)) { seen_c++; } else { fprintf(stderr, "edges_from unexpected endpoint\n"); goto cleanup; } } if (seen_b != 1 || seen_c != 1) { fprintf(stderr, "edges_from endpoints mismatch\n"); goto cleanup; } } exit_code = 0; cleanup: amduat_tgk_graph_edge_view_list_free(&edges); amduat_tgk_edge_body_free(&body); if (fs_ready) { amduat_tgk_store_fs_free(&fs); } free((void *)edge_bytes[0].data); free((void *)edge_bytes[1].data); free_ref(&ref_edge1); free_ref(&ref_edge2); free(manifest_path); if (root != NULL) { remove_tree(root); } free(root); return exit_code; } int main(void) { return test_manifest_load(); }