2025-12-21 19:23:41 +01:00
|
|
|
#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 <dirent.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 20:42:20 +01:00
|
|
|
static int test_init_rejects_duplicate_hash_id(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_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];
|
2025-12-21 21:23:55 +01:00
|
|
|
uint8_t digest_edge[32];
|
2025-12-21 20:42:20 +01:00
|
|
|
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);
|
2025-12-21 21:23:55 +01:00
|
|
|
ref_edge = make_ref(0x71, digest_edge);
|
2025-12-21 20:42:20 +01:00
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 21:23:55 +01:00
|
|
|
static int test_ingest_remove_epoch(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_edge_body_t edge;
|
|
|
|
|
amduat_reference_t edge_from[1];
|
|
|
|
|
amduat_reference_t edge_to[1];
|
|
|
|
|
amduat_tgk_snapshot_id_t epoch_before = 0;
|
|
|
|
|
amduat_tgk_snapshot_id_t epoch_after = 0;
|
|
|
|
|
amduat_tgk_graph_scan_result_t scan;
|
|
|
|
|
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(&edge, 0, sizeof(edge));
|
|
|
|
|
edge_bytes = amduat_octets(NULL, 0);
|
|
|
|
|
|
|
|
|
|
root = make_temp_root();
|
|
|
|
|
if (root == NULL) {
|
|
|
|
|
fprintf(stderr, "fs epoch temp root failed\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
if (!join_path(root, "manifest.txt", &manifest_path)) {
|
|
|
|
|
fprintf(stderr, "fs epoch manifest path failed\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
if (!write_manifest(manifest_path, NULL, 0)) {
|
|
|
|
|
fprintf(stderr, "fs epoch 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, "fs epoch 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;
|
|
|
|
|
|
|
|
|
|
if (!amduat_tgk_store_fs_init(&fs, tgk_config, manifest_path,
|
|
|
|
|
AMDUAT_FORMAT_REF_HEX, &asl_store)) {
|
|
|
|
|
fprintf(stderr, "fs epoch 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_snapshot_id(&store, &epoch_before)) {
|
|
|
|
|
fprintf(stderr, "fs epoch get failed\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node_a = make_ref(0xa1, digest_node_a);
|
|
|
|
|
node_b = make_ref(0xb1, digest_node_b);
|
|
|
|
|
payload = make_ref(0xe1, digest_payload);
|
|
|
|
|
|
|
|
|
|
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, "fs epoch edge encode failed\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!amduat_tgk_store_ingest_artifact(
|
|
|
|
|
&store, ref_edge,
|
|
|
|
|
amduat_artifact_with_type(edge_bytes,
|
|
|
|
|
amduat_type_tag(TYPE_TAG_TGK1_EDGE_V1)))) {
|
|
|
|
|
fprintf(stderr, "fs epoch ingest failed\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!amduat_tgk_store_snapshot_id(&store, &epoch_after)) {
|
|
|
|
|
fprintf(stderr, "fs epoch get after ingest failed\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
if (epoch_after != epoch_before + 1u) {
|
|
|
|
|
fprintf(stderr, "fs epoch did not increment on ingest\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!amduat_tgk_store_scan_edges(&store,
|
|
|
|
|
(amduat_tgk_edge_type_filter_t){0},
|
|
|
|
|
amduat_octets(NULL, 0), false, &scan)) {
|
|
|
|
|
fprintf(stderr, "fs epoch scan failed\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
if (scan.edges.len != 1) {
|
|
|
|
|
fprintf(stderr, "fs epoch scan count mismatch\n");
|
|
|
|
|
amduat_tgk_graph_scan_result_free(&scan);
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
amduat_tgk_graph_scan_result_free(&scan);
|
|
|
|
|
|
|
|
|
|
if (!amduat_tgk_store_remove_artifact(&store, ref_edge)) {
|
|
|
|
|
fprintf(stderr, "fs epoch remove failed\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
if (!amduat_tgk_store_snapshot_id(&store, &epoch_after)) {
|
|
|
|
|
fprintf(stderr, "fs epoch get after remove failed\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
if (epoch_after != epoch_before + 2u) {
|
|
|
|
|
fprintf(stderr, "fs epoch did not increment on remove\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!amduat_tgk_store_scan_edges(&store,
|
|
|
|
|
(amduat_tgk_edge_type_filter_t){0},
|
|
|
|
|
amduat_octets(NULL, 0), false, &scan)) {
|
|
|
|
|
fprintf(stderr, "fs epoch scan after remove failed\n");
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
if (scan.edges.len != 0) {
|
|
|
|
|
fprintf(stderr, "fs epoch scan not empty after remove\n");
|
|
|
|
|
amduat_tgk_graph_scan_result_free(&scan);
|
|
|
|
|
goto cleanup;
|
|
|
|
|
}
|
|
|
|
|
amduat_tgk_graph_scan_result_free(&scan);
|
|
|
|
|
|
|
|
|
|
exit_code = 0;
|
|
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 19:23:41 +01:00
|
|
|
int main(void) {
|
2025-12-21 20:42:20 +01:00
|
|
|
if (test_manifest_load() != 0 ||
|
|
|
|
|
test_init_rejects_duplicate_hash_id() != 0 ||
|
2025-12-21 21:23:55 +01:00
|
|
|
test_manifest_duplicate_refs() != 0 ||
|
|
|
|
|
test_ingest_remove_epoch() != 0) {
|
2025-12-21 20:42:20 +01:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2025-12-21 19:23:41 +01:00
|
|
|
}
|