diff --git a/src/adapters/tgk_store_fs/tgk_store_fs.c b/src/adapters/tgk_store_fs/tgk_store_fs.c index 8be7897..09543b0 100644 --- a/src/adapters/tgk_store_fs/tgk_store_fs.c +++ b/src/adapters/tgk_store_fs/tgk_store_fs.c @@ -6,6 +6,56 @@ #include #include +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) { if (ref == NULL) { return; @@ -166,6 +216,7 @@ bool amduat_tgk_store_fs_init(amduat_tgk_store_fs_t *fs, amduat_reference_t *refs = NULL; size_t refs_len = 0; amduat_tgk_store_mem_artifact_t *artifacts = NULL; + size_t artifacts_len = 0; size_t i; 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)); + if (!amduat_tgk_store_fs_id_space_valid(config.id_space)) { + return false; + } + if (!amduat_tgk_store_fs_load_manifest(manifest_path, manifest_format, &refs, @@ -191,23 +246,49 @@ bool amduat_tgk_store_fs_init(amduat_tgk_store_fs_t *fs, for (i = 0; i < refs_len; ++i) { amduat_asl_store_error_t err; - artifacts[i].ref = refs[i]; - artifacts[i].artifact = amduat_artifact(amduat_octets(NULL, 0)); - err = amduat_asl_store_get(asl_store, refs[i], &artifacts[i].artifact); + amduat_artifact_t artifact = amduat_artifact(amduat_octets(NULL, 0)); + bool duplicate = false; + bool conflict = false; + size_t j; + + err = amduat_asl_store_get(asl_store, refs[i], &artifact); if (err != AMDUAT_ASL_STORE_OK) { - size_t j; - for (j = 0; j < i; ++j) { - amduat_tgk_store_fs_artifact_bytes_free(&artifacts[j]); - } - amduat_asl_ref_list_free(refs, refs_len); - free(artifacts); - return false; + goto cleanup; } + + 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++; } - free(refs); + amduat_asl_ref_list_free(refs, refs_len); + refs = NULL; fs->artifacts = artifacts; - fs->artifacts_len = refs_len; + fs->artifacts_len = artifacts_len; if (!amduat_tgk_store_mem_init(&fs->mem, config, fs->artifacts, fs->artifacts_len)) { @@ -215,6 +296,16 @@ bool amduat_tgk_store_fs_init(amduat_tgk_store_fs_t *fs, return false; } 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( diff --git a/tests/tgk/test_tgk_store_fs.c b/tests/tgk/test_tgk_store_fs.c index d9fd81a..3d571f0 100644 --- a/tests/tgk/test_tgk_store_fs.c +++ b/tests/tgk/test_tgk_store_fs.c @@ -387,6 +387,225 @@ cleanup: return exit_code; } -int main(void) { - return test_manifest_load(); +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]; + 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; }