From 222fe2f84ecf87befcfd553b88c84feef4d56481 Mon Sep 17 00:00:00 2001 From: Carl Niklas Rydberg Date: Mon, 22 Dec 2025 11:42:41 +0100 Subject: [PATCH] tgk1-edge: accept unknown hash ids and add regression test --- AUDITS.md | 33 +++++++- src/adapters/tgk_store_mem/tgk_store_mem.c | 82 +++++++++++++++++++- src/near_core/enc/tgk1_edge.c | 11 +-- tests/enc/test_tgk1_edge.c | 64 ++++++++++++++++ tests/tgk/test_tgk_store_mem.c | 87 ++++++++++++++++++++++ 5 files changed, 268 insertions(+), 9 deletions(-) diff --git a/AUDITS.md b/AUDITS.md index 83c41a6..e081e99 100644 --- a/AUDITS.md +++ b/AUDITS.md @@ -38,8 +38,8 @@ Status legend: ✅ completed, ⬜ pending. 10. ✅ `tier1/enc-pel1-result-1.md` 11. ✅ `tier1/pel-trace-dag-1.md` 12. ✅ `tier1/enc-pel-trace-dag-1.md` -13. ⬜ `tier1/tgk-1-core.md` -14. ⬜ `tier1/enc-tgk1-edge-1.md` +13. ✅ `tier1/tgk-1-core.md` +14. ✅ `tier1/enc-tgk1-edge-1.md` 15. ⬜ `tier1/tgk-store-1.md` 16. ⬜ `tier1/tgk-prov-1.md` 17. ⬜ `tier1/opreg-pel1-kernel.md` @@ -185,3 +185,32 @@ Status legend: ✅ completed, ⬜ pending. but permit unknown IDs and digest lengths, matching ENC/ASL1-CORE v1 behavior. - Tests: command not provided — pass (user reported “100% tests passed, 0 tests failed out of 14”). + +## 2025-12-22 — TGK/1-CORE (`tier1/tgk-1-core.md`) +- Scope: TGK/1-CORE EdgeArtifact recognition, EdgeBody invariants, profile + configuration, and deterministic graph projection behavior in TGK stores. +- Findings: `amduat_tgk_store_mem_init` does not validate + `config.tgk_profiles.edge_tags`/`edge_tags_len` or + `config.tgk_profiles.edge_types`/`edge_types_len`, so null pointers with + non-zero lengths can lead to undefined behavior and prevent the required + `EDGE_TAG_SET`/edge-type catalogs from being well-defined; no consistency + checks ensure edge tags correspond to active edge encodings. +- Resolution: added validation for edge tag/type list pointers and duplicates; + enforced `TYPE_TAG_TGK1_EDGE_V1` presence when `TGK1_EDGE_ENC_V1` is active, + and rejection when the encoding is inactive. +- Tests: command not provided — pass (user reported “100% tests passed, 0 tests + failed out of 14”). + +## 2025-12-22 — ENC/TGK1-EDGE/1 (`tier1/enc-tgk1-edge-1.md`) +- Scope: TGK1 EdgeBody encoding/decoding, EncodedRef framing, and profile + invariants for `TGK1_EDGE_ENC_V1`. +- Findings: `amduat_enc_tgk1_edge_encode_v1` rejects edges whose references use + unknown (non-registry) `hash_id` values because it requires a + registry-backed digest length when calculating `EncodedRef` sizes, which + contradicts `ENC/ASL1-CORE v1.x` and §2.4’s requirement to accept unknown hash + IDs with only reserved-ID rejection and length checks when known. +- Resolution: updated `amduat_enc_tgk1_edge_encode_v1` sizing to allow unknown + hash IDs per `ENC/ASL1-CORE`; added regression test for unknown `hash_id` + edge references. +- Tests: command not provided — pass (user reported “100% tests passed, 0 tests + failed out of 14”). diff --git a/src/adapters/tgk_store_mem/tgk_store_mem.c b/src/adapters/tgk_store_mem/tgk_store_mem.c index eb04bdb..dd2ebbe 100644 --- a/src/adapters/tgk_store_mem/tgk_store_mem.c +++ b/src/adapters/tgk_store_mem/tgk_store_mem.c @@ -98,6 +98,46 @@ static bool amduat_tgk_store_mem_encoding_impl_supported( } } +static bool amduat_tgk_store_mem_u32_list_has(const uint32_t *values, + size_t len, + uint32_t value) { + size_t i; + + if (len == 0) { + return false; + } + if (values == NULL) { + return false; + } + for (i = 0; i < len; ++i) { + if (values[i] == value) { + return true; + } + } + return false; +} + +static bool amduat_tgk_store_mem_u32_list_unique(const uint32_t *values, + size_t len) { + size_t i; + size_t j; + + if (len == 0) { + return true; + } + if (values == NULL) { + return false; + } + for (i = 0; i < len; ++i) { + for (j = i + 1; j < len; ++j) { + if (values[i] == values[j]) { + return false; + } + } + } + return true; +} + static bool amduat_tgk_store_mem_decode_profile( amduat_asl_encoding_profile_id_t profile_id, amduat_octets_t bytes, @@ -1189,6 +1229,7 @@ bool amduat_tgk_store_mem_init(amduat_tgk_store_mem_t *mem, size_t stored = 0; amduat_tgk_graph_edge_view_t *edges = NULL; size_t edges_len = 0; + bool has_tgk1_encoding = false; if (mem == NULL) { return false; @@ -1208,9 +1249,46 @@ bool amduat_tgk_store_mem_init(amduat_tgk_store_mem_t *mem, config.tgk_profiles.encodings == NULL) { return false; } + if (config.tgk_profiles.edge_tags_len != 0 && + config.tgk_profiles.edge_tags == NULL) { + return false; + } + if (config.tgk_profiles.edge_types_len != 0 && + config.tgk_profiles.edge_types == NULL) { + return false; + } + if (!amduat_tgk_store_mem_u32_list_unique( + config.tgk_profiles.edge_tags, + config.tgk_profiles.edge_tags_len)) { + return false; + } + if (!amduat_tgk_store_mem_u32_list_unique( + config.tgk_profiles.edge_types, + config.tgk_profiles.edge_types_len)) { + return false; + } for (i = 0; i < config.tgk_profiles.encodings_len; ++i) { - if (!amduat_tgk_store_mem_encoding_impl_supported( - config.tgk_profiles.encodings[i])) { + amduat_asl_encoding_profile_id_t profile_id = + config.tgk_profiles.encodings[i]; + if (!amduat_tgk_store_mem_encoding_impl_supported(profile_id)) { + return false; + } + if (profile_id == TGK1_EDGE_ENC_V1) { + has_tgk1_encoding = true; + } + } + if (has_tgk1_encoding) { + if (!amduat_tgk_store_mem_u32_list_has( + config.tgk_profiles.edge_tags, + config.tgk_profiles.edge_tags_len, + TYPE_TAG_TGK1_EDGE_V1)) { + return false; + } + } else { + if (amduat_tgk_store_mem_u32_list_has( + config.tgk_profiles.edge_tags, + config.tgk_profiles.edge_tags_len, + TYPE_TAG_TGK1_EDGE_V1)) { return false; } } diff --git a/src/near_core/enc/tgk1_edge.c b/src/near_core/enc/tgk1_edge.c index 22056a1..077ebab 100644 --- a/src/near_core/enc/tgk1_edge.c +++ b/src/near_core/enc/tgk1_edge.c @@ -63,16 +63,17 @@ static bool amduat_reference_bytes_len(amduat_reference_t ref, size_t *out_len) if (ref.digest.len != 0 && ref.digest.data == NULL) { return false; } + if (amduat_hash_asl1_is_reserved(ref.hash_id)) { + return false; + } desc = amduat_hash_asl1_desc_lookup(ref.hash_id); - if (desc == NULL || desc->digest_len == 0) { - return false; - } - if (ref.digest.len != desc->digest_len) { + if (desc != NULL && desc->digest_len != 0 && + ref.digest.len != desc->digest_len) { return false; } - *out_len = 2 + desc->digest_len; + *out_len = 2 + ref.digest.len; return true; } diff --git a/tests/enc/test_tgk1_edge.c b/tests/enc/test_tgk1_edge.c index 9a194c2..bfa876c 100644 --- a/tests/enc/test_tgk1_edge.c +++ b/tests/enc/test_tgk1_edge.c @@ -35,11 +35,23 @@ static void fill_digest(uint8_t *out, uint8_t value) { memset(out, value, 32); } +static void fill_digest_len(uint8_t *out, size_t len, uint8_t value) { + memset(out, value, len); +} + static amduat_reference_t make_ref(uint8_t value, uint8_t *storage) { fill_digest(storage, value); return amduat_reference(0x0001, amduat_octets(storage, 32)); } +static amduat_reference_t make_ref_len(uint16_t hash_id, + uint8_t value, + uint8_t *storage, + size_t len) { + fill_digest_len(storage, len, value); + return amduat_reference(hash_id, amduat_octets(storage, len)); +} + static bool bytes_equal(amduat_octets_t bytes, const uint8_t *expected, size_t expected_len) { @@ -132,6 +144,55 @@ static int test_invalid_edge_version(void) { return 0; } +static int test_unknown_hash_id_round_trip(void) { + amduat_tgk_edge_body_t edge; + amduat_reference_t from_refs[1]; + amduat_octets_t encoded; + amduat_tgk_edge_body_t decoded; + uint8_t f0[5]; + uint8_t p0[7]; + int exit_code = 1; + + memset(&edge, 0, sizeof(edge)); + edge.type = 0x2b; + + from_refs[0] = make_ref_len(0x1234, 0x44, f0, sizeof(f0)); + edge.from = from_refs; + edge.from_len = 1; + + edge.payload = make_ref_len(0x2345, 0x55, p0, sizeof(p0)); + + if (!amduat_enc_tgk1_edge_encode_v1(&edge, &encoded)) { + fprintf(stderr, "encode failed for unknown hash_id\n"); + return exit_code; + } + + if (!amduat_enc_tgk1_edge_decode_v1(encoded, &decoded)) { + fprintf(stderr, "decode failed for unknown hash_id\n"); + goto cleanup; + } + + if (decoded.type != edge.type || decoded.from_len != 1 || + decoded.to_len != 0) { + fprintf(stderr, "decoded field mismatch for unknown hash_id\n"); + goto cleanup_decoded; + } + + if (!amduat_reference_eq(decoded.from[0], edge.from[0]) || + !amduat_reference_eq(decoded.payload, edge.payload)) { + fprintf(stderr, "decoded refs mismatch for unknown hash_id\n"); + goto cleanup_decoded; + } + + exit_code = 0; + +cleanup_decoded: + amduat_enc_tgk1_edge_free(&decoded); +cleanup: + free((void *)encoded.data); + return exit_code; +} + static int test_empty_endpoints(void) { amduat_tgk_edge_body_t edge; amduat_octets_t encoded; @@ -184,6 +245,9 @@ int main(void) { if (test_invalid_edge_version() != 0) { return 1; } + if (test_unknown_hash_id_round_trip() != 0) { + return 1; + } if (test_empty_endpoints() != 0) { return 1; } diff --git a/tests/tgk/test_tgk_store_mem.c b/tests/tgk/test_tgk_store_mem.c index 9daaf16..cf97c19 100644 --- a/tests/tgk/test_tgk_store_mem.c +++ b/tests/tgk/test_tgk_store_mem.c @@ -346,6 +346,92 @@ static int test_init_rejects_duplicate_hash_id(void) { return 0; } +static int test_init_rejects_invalid_profile_config(void) { + amduat_tgk_store_mem_t mem; + amduat_tgk_store_config_t config; + amduat_asl_encoding_profile_id_t encodings[1]; + uint32_t edge_tags[2]; + amduat_tgk_edge_type_id_t edge_types[2]; + + encodings[0] = TGK1_EDGE_ENC_V1; + edge_tags[0] = TYPE_TAG_TGK1_EDGE_V1; + edge_tags[1] = TYPE_TAG_TGK1_EDGE_V1; + edge_types[0] = 0x10; + edge_types[1] = 0x10; + + memset(&mem, 0, sizeof(mem)); + memset(&config, 0, sizeof(config)); + config.tgk_profiles.encodings = encodings; + config.tgk_profiles.encodings_len = 1; + config.tgk_profiles.edge_tags_len = 1; + config.tgk_profiles.edge_tags = NULL; + + if (amduat_tgk_store_mem_init(&mem, config, NULL, 0)) { + fprintf(stderr, "init accepted null edge_tags\n"); + amduat_tgk_store_mem_free(&mem); + return 1; + } + + memset(&mem, 0, sizeof(mem)); + memset(&config, 0, sizeof(config)); + config.tgk_profiles.encodings = encodings; + config.tgk_profiles.encodings_len = 1; + config.tgk_profiles.edge_types_len = 1; + config.tgk_profiles.edge_types = NULL; + + if (amduat_tgk_store_mem_init(&mem, config, NULL, 0)) { + fprintf(stderr, "init accepted null edge_types\n"); + amduat_tgk_store_mem_free(&mem); + return 1; + } + + memset(&mem, 0, sizeof(mem)); + memset(&config, 0, sizeof(config)); + config.tgk_profiles.encodings = encodings; + config.tgk_profiles.encodings_len = 1; + + if (amduat_tgk_store_mem_init(&mem, config, NULL, 0)) { + fprintf(stderr, "init accepted encoding without edge tags\n"); + amduat_tgk_store_mem_free(&mem); + return 1; + } + + memset(&mem, 0, sizeof(mem)); + memset(&config, 0, sizeof(config)); + config.tgk_profiles.edge_tags = edge_tags; + config.tgk_profiles.edge_tags_len = 1; + + if (amduat_tgk_store_mem_init(&mem, config, NULL, 0)) { + fprintf(stderr, "init accepted edge tag without encoding\n"); + amduat_tgk_store_mem_free(&mem); + return 1; + } + + memset(&mem, 0, sizeof(mem)); + memset(&config, 0, sizeof(config)); + config.tgk_profiles.edge_tags = edge_tags; + config.tgk_profiles.edge_tags_len = 2; + + if (amduat_tgk_store_mem_init(&mem, config, NULL, 0)) { + fprintf(stderr, "init accepted duplicate edge tags\n"); + amduat_tgk_store_mem_free(&mem); + return 1; + } + + memset(&mem, 0, sizeof(mem)); + memset(&config, 0, sizeof(config)); + config.tgk_profiles.edge_types = edge_types; + config.tgk_profiles.edge_types_len = 2; + + if (amduat_tgk_store_mem_init(&mem, config, NULL, 0)) { + fprintf(stderr, "init accepted duplicate edge types\n"); + amduat_tgk_store_mem_free(&mem); + return 1; + } + + return 0; +} + static int test_duplicate_edge_ref_same_artifact(void) { amduat_tgk_store_mem_t mem; amduat_tgk_store_t store; @@ -1348,6 +1434,7 @@ int main(void) { test_resolve_edge_unsupported(&env) != 0 || test_init_rejects_unsupported_encoding() != 0 || test_init_rejects_duplicate_hash_id() != 0 || + test_init_rejects_invalid_profile_config() != 0 || test_duplicate_edge_ref_same_artifact() != 0 || test_duplicate_edge_ref_conflict() != 0 || test_scan_edges_pagination() != 0 ||