diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dd069c..b153bef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -230,6 +230,16 @@ target_link_libraries(amduat_test_pel1_result ) add_test(NAME pel1_result COMMAND amduat_test_pel1_result) +add_executable(amduat_test_tgk1_edge tests/enc/test_tgk1_edge.c) +target_include_directories(amduat_test_tgk1_edge + PRIVATE ${AMDUAT_INTERNAL_DIR} + PRIVATE ${AMDUAT_INCLUDE_DIR} +) +target_link_libraries(amduat_test_tgk1_edge + PRIVATE amduat_enc amduat_hash_asl1 amduat_asl amduat_util +) +add_test(NAME tgk1_edge COMMAND amduat_test_tgk1_edge) + add_executable(amduat_test_pel_program_dag_exec tests/pel/test_pel_program_dag_exec.c) target_include_directories(amduat_test_pel_program_dag_exec diff --git a/include/amduat/enc/tgk1_edge.h b/include/amduat/enc/tgk1_edge.h index 460b22b..e58eea0 100644 --- a/include/amduat/enc/tgk1_edge.h +++ b/include/amduat/enc/tgk1_edge.h @@ -1,13 +1,25 @@ #ifndef AMDUAT_ENC_TGK1_EDGE_H #define AMDUAT_ENC_TGK1_EDGE_H +#include "amduat/tgk/core.h" + +#include + #ifdef __cplusplus extern "C" { #endif +enum { TGK1_EDGE_ENC_V1 = 0x0201u }; + enum { TYPE_TAG_TGK1_EDGE_V1 = 0x00000201u }; enum { AMDUAT_TYPE_TAG_TGK1_EDGE_V1 = TYPE_TAG_TGK1_EDGE_V1 }; +bool amduat_enc_tgk1_edge_encode_v1(const amduat_tgk_edge_body_t *edge, + amduat_octets_t *out_bytes); +bool amduat_enc_tgk1_edge_decode_v1(amduat_octets_t bytes, + amduat_tgk_edge_body_t *out_edge); +void amduat_enc_tgk1_edge_free(amduat_tgk_edge_body_t *edge); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/include/amduat/tgk/core.h b/include/amduat/tgk/core.h index cd30ba5..86559ef 100644 --- a/include/amduat/tgk/core.h +++ b/include/amduat/tgk/core.h @@ -1,7 +1,11 @@ #ifndef AMDUAT_TGK_CORE_H #define AMDUAT_TGK_CORE_H -#include "amduat/enc/tgk1_edge.h" +#include "amduat/asl/core.h" + +#include +#include +#include #ifdef __cplusplus extern "C" { @@ -9,6 +13,24 @@ extern "C" { /* Core TGK header re-exports encoding constants for convenience. */ +typedef uint32_t amduat_tgk_edge_type_id_t; + +typedef struct { + amduat_tgk_edge_type_id_t type; + amduat_reference_t *from; + size_t from_len; + amduat_reference_t *to; + size_t to_len; + amduat_reference_t payload; +} amduat_tgk_edge_body_t; + +bool amduat_tgk_edge_body_has_endpoints(const amduat_tgk_edge_body_t *edge); +bool amduat_tgk_edge_body_eq(const amduat_tgk_edge_body_t *a, + const amduat_tgk_edge_body_t *b); +void amduat_tgk_edge_body_free(amduat_tgk_edge_body_t *edge); + +#include "amduat/enc/tgk1_edge.h" + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/src/kernel/tgk/core.c b/src/kernel/tgk/core.c index e69de29..601e77c 100644 --- a/src/kernel/tgk/core.c +++ b/src/kernel/tgk/core.c @@ -0,0 +1,80 @@ +#include "amduat/tgk/core.h" + +#include + +static void amduat_reference_free(amduat_reference_t *ref) { + if (ref == NULL) { + return; + } + free((void *)ref->digest.data); + ref->digest.data = NULL; + ref->digest.len = 0; +} + +bool amduat_tgk_edge_body_has_endpoints(const amduat_tgk_edge_body_t *edge) { + if (edge == NULL) { + return false; + } + return edge->from_len > 0 || edge->to_len > 0; +} + +bool amduat_tgk_edge_body_eq(const amduat_tgk_edge_body_t *a, + const amduat_tgk_edge_body_t *b) { + size_t i; + + if (a == b) { + return true; + } + if (a == NULL || b == NULL) { + return false; + } + if (a->type != b->type) { + return false; + } + if (a->from_len != b->from_len || a->to_len != b->to_len) { + return false; + } + if (a->from_len != 0 && (a->from == NULL || b->from == NULL)) { + return false; + } + if (a->to_len != 0 && (a->to == NULL || b->to == NULL)) { + return false; + } + for (i = 0; i < a->from_len; ++i) { + if (!amduat_reference_eq(a->from[i], b->from[i])) { + return false; + } + } + for (i = 0; i < a->to_len; ++i) { + if (!amduat_reference_eq(a->to[i], b->to[i])) { + return false; + } + } + return amduat_reference_eq(a->payload, b->payload); +} + +void amduat_tgk_edge_body_free(amduat_tgk_edge_body_t *edge) { + size_t i; + + if (edge == NULL) { + return; + } + if (edge->from != NULL) { + for (i = 0; i < edge->from_len; ++i) { + amduat_reference_free(&edge->from[i]); + } + free(edge->from); + } + if (edge->to != NULL) { + for (i = 0; i < edge->to_len; ++i) { + amduat_reference_free(&edge->to[i]); + } + free(edge->to); + } + amduat_reference_free(&edge->payload); + + edge->from = NULL; + edge->from_len = 0; + edge->to = NULL; + edge->to_len = 0; +} diff --git a/src/near_core/enc/tgk1_edge.c b/src/near_core/enc/tgk1_edge.c index e69de29..043b8d1 100644 --- a/src/near_core/enc/tgk1_edge.c +++ b/src/near_core/enc/tgk1_edge.c @@ -0,0 +1,360 @@ +#include "amduat/enc/tgk1_edge.h" + +#include "amduat/enc/asl1_core_codec.h" +#include "amduat/hash/asl1.h" + +#include +#include +#include +#include + +typedef struct { + const uint8_t *data; + size_t len; + size_t offset; +} amduat_cursor_t; + +static void amduat_store_u16_be(uint8_t *out, uint16_t value) { + out[0] = (uint8_t)((value >> 8) & 0xffu); + out[1] = (uint8_t)(value & 0xffu); +} + +static void amduat_store_u32_be(uint8_t *out, uint32_t value) { + out[0] = (uint8_t)((value >> 24) & 0xffu); + out[1] = (uint8_t)((value >> 16) & 0xffu); + out[2] = (uint8_t)((value >> 8) & 0xffu); + out[3] = (uint8_t)(value & 0xffu); +} + +static bool amduat_read_u16(amduat_cursor_t *cur, uint16_t *out) { + const uint8_t *data; + if (cur->len - cur->offset < 2) { + return false; + } + data = cur->data + cur->offset; + *out = (uint16_t)((data[0] << 8) | data[1]); + cur->offset += 2; + return true; +} + +static bool amduat_read_u32(amduat_cursor_t *cur, uint32_t *out) { + const uint8_t *data; + if (cur->len - cur->offset < 4) { + return false; + } + data = cur->data + cur->offset; + *out = ((uint32_t)data[0] << 24) | ((uint32_t)data[1] << 16) | + ((uint32_t)data[2] << 8) | (uint32_t)data[3]; + cur->offset += 4; + return true; +} + +static bool amduat_add_size(size_t *acc, size_t add) { + if (*acc > SIZE_MAX - add) { + return false; + } + *acc += add; + return true; +} + +static bool amduat_reference_bytes_len(amduat_reference_t ref, size_t *out_len) { + const amduat_hash_asl1_desc_t *desc; + + if (ref.digest.len != 0 && ref.digest.data == NULL) { + 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) { + return false; + } + + *out_len = 2 + desc->digest_len; + return true; +} + +static bool amduat_encoded_ref_len(amduat_reference_t ref, size_t *out_len) { + size_t ref_len; + + if (!amduat_reference_bytes_len(ref, &ref_len)) { + return false; + } + if (ref_len > UINT32_MAX) { + return false; + } + *out_len = 4 + ref_len; + return true; +} + +static bool amduat_write_encoded_ref(uint8_t *buffer, + size_t buffer_len, + size_t *offset, + amduat_reference_t ref) { + size_t ref_len; + + if (!amduat_reference_bytes_len(ref, &ref_len)) { + return false; + } + if (ref_len > UINT32_MAX) { + return false; + } + if (buffer_len - *offset < 4 + ref_len) { + return false; + } + + amduat_store_u32_be(buffer + *offset, (uint32_t)ref_len); + *offset += 4; + amduat_store_u16_be(buffer + *offset, ref.hash_id); + *offset += 2; + if (ref.digest.len != 0) { + memcpy(buffer + *offset, ref.digest.data, ref.digest.len); + *offset += ref.digest.len; + } + + return true; +} + +static bool amduat_read_encoded_ref(amduat_cursor_t *cur, + amduat_reference_t *out_ref) { + uint32_t ref_len_u32; + amduat_octets_t ref_bytes; + + if (!amduat_read_u32(cur, &ref_len_u32)) { + return false; + } + if (ref_len_u32 < 2) { + return false; + } + if (cur->len - cur->offset < ref_len_u32) { + return false; + } + ref_bytes = amduat_octets(cur->data + cur->offset, ref_len_u32); + if (!amduat_enc_asl1_core_decode_reference_v1(ref_bytes, out_ref)) { + return false; + } + cur->offset += ref_len_u32; + return true; +} + +static void amduat_reference_free(amduat_reference_t *ref) { + if (ref == NULL) { + return; + } + free((void *)ref->digest.data); + ref->digest.data = NULL; + ref->digest.len = 0; +} + +void amduat_enc_tgk1_edge_free(amduat_tgk_edge_body_t *edge) { + size_t i; + + if (edge == NULL) { + return; + } + if (edge->from != NULL) { + for (i = 0; i < edge->from_len; ++i) { + amduat_reference_free(&edge->from[i]); + } + free(edge->from); + } + if (edge->to != NULL) { + for (i = 0; i < edge->to_len; ++i) { + amduat_reference_free(&edge->to[i]); + } + free(edge->to); + } + amduat_reference_free(&edge->payload); + + edge->from = NULL; + edge->from_len = 0; + edge->to = NULL; + edge->to_len = 0; +} + +bool amduat_enc_tgk1_edge_encode_v1(const amduat_tgk_edge_body_t *edge, + amduat_octets_t *out_bytes) { + size_t total_len = 0; + size_t offset = 0; + uint8_t *buffer; + size_t i; + size_t enc_len; + + if (edge == NULL || out_bytes == NULL) { + return false; + } + + out_bytes->data = NULL; + out_bytes->len = 0; + + if (edge->from_len == 0 && edge->to_len == 0) { + return false; + } + if (edge->from_len > UINT32_MAX || edge->to_len > UINT32_MAX) { + return false; + } + if (edge->from_len != 0 && edge->from == NULL) { + return false; + } + if (edge->to_len != 0 && edge->to == NULL) { + return false; + } + + if (!amduat_add_size(&total_len, 2 + 4 + 4)) { + return false; + } + for (i = 0; i < edge->from_len; ++i) { + if (!amduat_encoded_ref_len(edge->from[i], &enc_len) || + !amduat_add_size(&total_len, enc_len)) { + return false; + } + } + if (!amduat_add_size(&total_len, 4)) { + return false; + } + for (i = 0; i < edge->to_len; ++i) { + if (!amduat_encoded_ref_len(edge->to[i], &enc_len) || + !amduat_add_size(&total_len, enc_len)) { + return false; + } + } + if (!amduat_encoded_ref_len(edge->payload, &enc_len) || + !amduat_add_size(&total_len, enc_len)) { + return false; + } + + buffer = (uint8_t *)malloc(total_len); + if (buffer == NULL) { + return false; + } + + amduat_store_u16_be(buffer + offset, 1); + offset += 2; + amduat_store_u32_be(buffer + offset, edge->type); + offset += 4; + + amduat_store_u32_be(buffer + offset, (uint32_t)edge->from_len); + offset += 4; + for (i = 0; i < edge->from_len; ++i) { + if (!amduat_write_encoded_ref(buffer, total_len, &offset, + edge->from[i])) { + free(buffer); + return false; + } + } + + amduat_store_u32_be(buffer + offset, (uint32_t)edge->to_len); + offset += 4; + for (i = 0; i < edge->to_len; ++i) { + if (!amduat_write_encoded_ref(buffer, total_len, &offset, edge->to[i])) { + free(buffer); + return false; + } + } + + if (!amduat_write_encoded_ref(buffer, total_len, &offset, edge->payload)) { + free(buffer); + return false; + } + + out_bytes->data = buffer; + out_bytes->len = total_len; + return true; +} + +bool amduat_enc_tgk1_edge_decode_v1(amduat_octets_t bytes, + amduat_tgk_edge_body_t *out_edge) { + amduat_cursor_t cur; + uint16_t edge_version; + uint32_t from_count; + uint32_t to_count; + size_t i; + + if (out_edge == NULL) { + return false; + } + if (bytes.len != 0 && bytes.data == NULL) { + return false; + } + + memset(out_edge, 0, sizeof(*out_edge)); + + cur.data = bytes.data; + cur.len = bytes.len; + cur.offset = 0; + + if (!amduat_read_u16(&cur, &edge_version)) { + return false; + } + if (edge_version != 1) { + return false; + } + if (!amduat_read_u32(&cur, &out_edge->type)) { + return false; + } + if (!amduat_read_u32(&cur, &from_count)) { + return false; + } + if (from_count != 0) { + if (from_count > SIZE_MAX / sizeof(amduat_reference_t)) { + return false; + } + out_edge->from = (amduat_reference_t *)calloc(from_count, + sizeof(amduat_reference_t)); + if (out_edge->from == NULL) { + return false; + } + } + out_edge->from_len = from_count; + + for (i = 0; i < out_edge->from_len; ++i) { + if (!amduat_read_encoded_ref(&cur, &out_edge->from[i])) { + amduat_enc_tgk1_edge_free(out_edge); + return false; + } + } + + if (!amduat_read_u32(&cur, &to_count)) { + amduat_enc_tgk1_edge_free(out_edge); + return false; + } + if (to_count != 0) { + if (to_count > SIZE_MAX / sizeof(amduat_reference_t)) { + amduat_enc_tgk1_edge_free(out_edge); + return false; + } + out_edge->to = + (amduat_reference_t *)calloc(to_count, sizeof(amduat_reference_t)); + if (out_edge->to == NULL) { + amduat_enc_tgk1_edge_free(out_edge); + return false; + } + } + out_edge->to_len = to_count; + + for (i = 0; i < out_edge->to_len; ++i) { + if (!amduat_read_encoded_ref(&cur, &out_edge->to[i])) { + amduat_enc_tgk1_edge_free(out_edge); + return false; + } + } + + if (out_edge->from_len == 0 && out_edge->to_len == 0) { + amduat_enc_tgk1_edge_free(out_edge); + return false; + } + + if (!amduat_read_encoded_ref(&cur, &out_edge->payload)) { + amduat_enc_tgk1_edge_free(out_edge); + return false; + } + + if (cur.offset != cur.len) { + amduat_enc_tgk1_edge_free(out_edge); + return false; + } + + return true; +} diff --git a/tests/enc/test_tgk1_edge.c b/tests/enc/test_tgk1_edge.c new file mode 100644 index 0000000..9a194c2 --- /dev/null +++ b/tests/enc/test_tgk1_edge.c @@ -0,0 +1,194 @@ +#include "amduat/enc/tgk1_edge.h" + +#include +#include +#include +#include +#include + +static const uint8_t k_expected_edge_bytes[] = { + 0x00, 0x01, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x22, 0x00, 0x01, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, + 0x00, 0x00, 0x00, 0x22, 0x00, 0x01, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x01, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x22, + 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, +}; + +static const uint8_t k_empty_edge_bytes[] = { + 0x00, 0x01, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x00, 0x01, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, +}; + +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(0x0001, amduat_octets(storage, 32)); +} + +static bool bytes_equal(amduat_octets_t bytes, + const uint8_t *expected, + size_t expected_len) { + if (bytes.len != expected_len) { + return false; + } + if (bytes.len == 0) { + return true; + } + return memcmp(bytes.data, expected, expected_len) == 0; +} + +static int test_edge_round_trip(void) { + amduat_tgk_edge_body_t edge; + amduat_reference_t from_refs[2]; + amduat_reference_t to_refs[1]; + amduat_octets_t encoded; + amduat_tgk_edge_body_t decoded; + uint8_t f0[32], f1[32], t0[32], p0[32]; + int exit_code = 1; + + memset(&edge, 0, sizeof(edge)); + edge.type = 0x2a; + + from_refs[0] = make_ref(0x10, f0); + from_refs[1] = make_ref(0x11, f1); + edge.from = from_refs; + edge.from_len = 2; + + to_refs[0] = make_ref(0x20, t0); + edge.to = to_refs; + edge.to_len = 1; + + edge.payload = make_ref(0x30, p0); + + if (!amduat_enc_tgk1_edge_encode_v1(&edge, &encoded)) { + fprintf(stderr, "encode failed\n"); + return exit_code; + } + + if (!bytes_equal(encoded, k_expected_edge_bytes, + sizeof(k_expected_edge_bytes))) { + fprintf(stderr, "encoded bytes mismatch\n"); + goto cleanup; + } + + if (!amduat_enc_tgk1_edge_decode_v1(encoded, &decoded)) { + fprintf(stderr, "decode failed\n"); + goto cleanup; + } + + if (decoded.type != edge.type || decoded.from_len != 2 || + decoded.to_len != 1) { + fprintf(stderr, "decoded field mismatch\n"); + goto cleanup_decoded; + } + + if (!amduat_reference_eq(decoded.from[0], edge.from[0]) || + !amduat_reference_eq(decoded.from[1], edge.from[1]) || + !amduat_reference_eq(decoded.to[0], edge.to[0]) || + !amduat_reference_eq(decoded.payload, edge.payload)) { + fprintf(stderr, "decoded references mismatch\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_invalid_edge_version(void) { + uint8_t bad_bytes[sizeof(k_expected_edge_bytes)]; + amduat_octets_t bytes; + amduat_tgk_edge_body_t decoded; + + memcpy(bad_bytes, k_expected_edge_bytes, sizeof(k_expected_edge_bytes)); + bad_bytes[1] = 0x02; + + bytes = amduat_octets(bad_bytes, sizeof(bad_bytes)); + if (amduat_enc_tgk1_edge_decode_v1(bytes, &decoded)) { + fprintf(stderr, "invalid edge version accepted\n"); + amduat_enc_tgk1_edge_free(&decoded); + return 1; + } + + return 0; +} + +static int test_empty_endpoints(void) { + amduat_tgk_edge_body_t edge; + amduat_octets_t encoded; + amduat_tgk_edge_body_t decoded; + uint8_t p0[32]; + + memset(&edge, 0, sizeof(edge)); + edge.type = 0x2a; + edge.payload = make_ref(0x30, p0); + + if (amduat_enc_tgk1_edge_encode_v1(&edge, &encoded)) { + fprintf(stderr, "encode accepted empty endpoints\n"); + free((void *)encoded.data); + return 1; + } + + if (amduat_enc_tgk1_edge_decode_v1( + amduat_octets(k_empty_edge_bytes, sizeof(k_empty_edge_bytes)), + &decoded)) { + fprintf(stderr, "decode accepted empty endpoints\n"); + amduat_enc_tgk1_edge_free(&decoded); + return 1; + } + + return 0; +} + +static int test_trailing_bytes(void) { + uint8_t bad_bytes[sizeof(k_expected_edge_bytes) + 1]; + amduat_octets_t bytes; + amduat_tgk_edge_body_t decoded; + + memcpy(bad_bytes, k_expected_edge_bytes, sizeof(k_expected_edge_bytes)); + bad_bytes[sizeof(k_expected_edge_bytes)] = 0x00; + + bytes = amduat_octets(bad_bytes, sizeof(bad_bytes)); + if (amduat_enc_tgk1_edge_decode_v1(bytes, &decoded)) { + fprintf(stderr, "trailing bytes accepted\n"); + amduat_enc_tgk1_edge_free(&decoded); + return 1; + } + + return 0; +} + +int main(void) { + if (test_edge_round_trip() != 0) { + return 1; + } + if (test_invalid_edge_version() != 0) { + return 1; + } + if (test_empty_endpoints() != 0) { + return 1; + } + if (test_trailing_bytes() != 0) { + return 1; + } + return 0; +}