#include "amduat/enc/asl_core_index.h" #include #include #include #include static void store_u16_le(uint8_t *out, uint16_t value) { out[0] = (uint8_t)(value & 0xffu); out[1] = (uint8_t)((value >> 8) & 0xffu); } static void store_u64_le(uint8_t *out, uint64_t value) { out[0] = (uint8_t)(value & 0xffu); out[1] = (uint8_t)((value >> 8) & 0xffu); out[2] = (uint8_t)((value >> 16) & 0xffu); out[3] = (uint8_t)((value >> 24) & 0xffu); out[4] = (uint8_t)((value >> 32) & 0xffu); out[5] = (uint8_t)((value >> 40) & 0xffu); out[6] = (uint8_t)((value >> 48) & 0xffu); out[7] = (uint8_t)((value >> 56) & 0xffu); } static uint64_t load_u64_le(const uint8_t *data) { return (uint64_t)data[0] | ((uint64_t)data[1] << 8) | ((uint64_t)data[2] << 16) | ((uint64_t)data[3] << 24) | ((uint64_t)data[4] << 32) | ((uint64_t)data[5] << 40) | ((uint64_t)data[6] << 48) | ((uint64_t)data[7] << 56); } static uint64_t crc64_ecma(const uint8_t *data, size_t len) { uint64_t crc = 0u; size_t i; for (i = 0; i < len; ++i) { uint64_t bit; uint8_t value = data[i]; crc ^= ((uint64_t)value) << 56; for (bit = 0; bit < 8; ++bit) { if (crc & 0x8000000000000000ull) { crc = (crc << 1) ^ 0x42f0e1eba9ea3693ull; } else { crc <<= 1; } } } return crc; } static void update_crc(amduat_octets_t bytes) { size_t footer_offset = bytes.len - AMDUAT_ASL_CORE_INDEX_FOOTER_SIZE; uint64_t crc = crc64_ecma(bytes.data, footer_offset); store_u64_le((uint8_t *)bytes.data + footer_offset, crc); } static void build_simple_segment(amduat_asl_core_index_segment_t *segment, amduat_asl_index_record_t *record, amduat_asl_extent_record_t *extent, uint8_t digest_bytes[8]) { memset(segment, 0, sizeof(*segment)); memset(record, 0, sizeof(*record)); memset(extent, 0, sizeof(*extent)); digest_bytes[0] = 0x01; digest_bytes[1] = 0x02; digest_bytes[2] = 0x03; digest_bytes[3] = 0x04; digest_bytes[4] = 0x05; digest_bytes[5] = 0x06; digest_bytes[6] = 0x07; digest_bytes[7] = 0x08; segment->header.snapshot_min = 5; segment->header.snapshot_max = 6; segment->header.segment_domain_id = 7; segment->header.segment_visibility = 1; segment->header.federation_version = 0; segment->header.flags = 0; segment->header.reserved0 = 0; record->hash_id = 9; record->digest_len = 8; record->extent_count = 1; record->total_length = 3; record->domain_id = 7; record->visibility = 1; record->has_cross_domain_source = 0; record->cross_domain_source = 0; record->flags = 0; extent->block_id = 123; extent->offset = 0; extent->length = 3; segment->records = record; segment->record_count = 1; segment->digests = amduat_octets(digest_bytes, 8); segment->extents = extent; segment->extent_count = 1; segment->footer.seal_snapshot = 10; segment->footer.seal_time_ns = 11; } static int test_round_trip(void) { amduat_asl_core_index_segment_t segment; amduat_asl_index_record_t record; amduat_asl_extent_record_t extent; uint8_t digest_bytes[8]; amduat_octets_t encoded; amduat_asl_core_index_segment_t decoded; int exit_code = 1; build_simple_segment(&segment, &record, &extent, digest_bytes); if (!amduat_enc_asl_core_index_encode_v1(&segment, &encoded)) { fprintf(stderr, "encode failed\n"); return exit_code; } if (!amduat_enc_asl_core_index_decode_v1(encoded, &decoded)) { fprintf(stderr, "decode failed\n"); goto cleanup; } if (decoded.header.version != AMDUAT_ASL_CORE_INDEX_VERSION || decoded.header.header_size != AMDUAT_ASL_CORE_INDEX_HEADER_SIZE) { fprintf(stderr, "header mismatch\n"); goto cleanup_decoded; } if (decoded.record_count != 1 || decoded.extent_count != 1) { fprintf(stderr, "count mismatch\n"); goto cleanup_decoded; } if (decoded.header.segment_domain_id != 7 || decoded.header.segment_visibility != 1) { fprintf(stderr, "segment federation mismatch\n"); goto cleanup_decoded; } if (decoded.records[0].hash_id != record.hash_id || decoded.records[0].digest_len != record.digest_len || decoded.records[0].total_length != record.total_length || decoded.records[0].domain_id != record.domain_id || decoded.records[0].visibility != record.visibility || decoded.records[0].flags != record.flags) { fprintf(stderr, "record mismatch\n"); goto cleanup_decoded; } if (decoded.digests.len != sizeof(digest_bytes) || memcmp(decoded.digests.data, digest_bytes, sizeof(digest_bytes)) != 0) { fprintf(stderr, "digest mismatch\n"); goto cleanup_decoded; } if (decoded.extents[0].block_id != extent.block_id || decoded.extents[0].offset != extent.offset || decoded.extents[0].length != extent.length) { fprintf(stderr, "extent mismatch\n"); goto cleanup_decoded; } if (decoded.footer.seal_snapshot != segment.footer.seal_snapshot || decoded.footer.seal_time_ns != segment.footer.seal_time_ns) { fprintf(stderr, "footer mismatch\n"); goto cleanup_decoded; } exit_code = 0; cleanup_decoded: amduat_enc_asl_core_index_free(&decoded); cleanup: free((void *)encoded.data); return exit_code; } static int test_crc_mismatch(void) { amduat_asl_core_index_segment_t segment; amduat_asl_index_record_t record; amduat_asl_extent_record_t extent; uint8_t digest_bytes[8]; amduat_octets_t encoded; amduat_asl_core_index_segment_t decoded; uint64_t digests_offset; int exit_code = 1; build_simple_segment(&segment, &record, &extent, digest_bytes); if (!amduat_enc_asl_core_index_encode_v1(&segment, &encoded)) { fprintf(stderr, "encode failed\n"); return exit_code; } digests_offset = load_u64_le(encoded.data + 64); ((uint8_t *)encoded.data)[digests_offset] ^= 0xffu; if (amduat_enc_asl_core_index_decode_v1(encoded, &decoded)) { fprintf(stderr, "decode unexpectedly succeeded\n"); amduat_enc_asl_core_index_free(&decoded); goto cleanup; } exit_code = 0; cleanup: free((void *)encoded.data); return exit_code; } static int test_invalid_offsets(void) { amduat_asl_core_index_segment_t segment; amduat_asl_index_record_t record; amduat_asl_extent_record_t extent; uint8_t digest_bytes[8]; amduat_octets_t encoded; amduat_asl_core_index_segment_t decoded; int exit_code = 1; build_simple_segment(&segment, &record, &extent, digest_bytes); if (!amduat_enc_asl_core_index_encode_v1(&segment, &encoded)) { fprintf(stderr, "encode failed\n"); return exit_code; } store_u64_le((uint8_t *)encoded.data + 72, 0); update_crc(encoded); if (amduat_enc_asl_core_index_decode_v1(encoded, &decoded)) { fprintf(stderr, "decode unexpectedly succeeded\n"); amduat_enc_asl_core_index_free(&decoded); goto cleanup; } exit_code = 0; cleanup: free((void *)encoded.data); return exit_code; } static int test_invalid_federation_fields(void) { amduat_asl_core_index_segment_t segment; amduat_asl_index_record_t record; amduat_asl_extent_record_t extent; uint8_t digest_bytes[8]; amduat_octets_t encoded; amduat_asl_core_index_segment_t decoded; uint64_t records_offset; int exit_code = 1; build_simple_segment(&segment, &record, &extent, digest_bytes); if (!amduat_enc_asl_core_index_encode_v1(&segment, &encoded)) { fprintf(stderr, "encode failed\n"); return exit_code; } records_offset = load_u64_le(encoded.data + 40); ((uint8_t *)encoded.data)[records_offset + 36] = 2; update_crc(encoded); if (amduat_enc_asl_core_index_decode_v1(encoded, &decoded)) { fprintf(stderr, "decode unexpectedly succeeded\n"); amduat_enc_asl_core_index_free(&decoded); goto cleanup; } exit_code = 0; cleanup: free((void *)encoded.data); return exit_code; } static int test_legacy_defaults(void) { amduat_asl_core_index_segment_t segment; amduat_asl_index_record_t record; amduat_asl_extent_record_t extent; uint8_t digest_bytes[8]; amduat_octets_t encoded; amduat_asl_core_index_segment_t decoded; uint64_t records_offset; int exit_code = 1; build_simple_segment(&segment, &record, &extent, digest_bytes); if (!amduat_enc_asl_core_index_encode_v1(&segment, &encoded)) { fprintf(stderr, "encode failed\n"); return exit_code; } store_u16_le((uint8_t *)encoded.data + 8, 2); ((uint8_t *)encoded.data)[100] = 1; records_offset = load_u64_le(encoded.data + 40); ((uint8_t *)encoded.data)[records_offset + 36] = 1; update_crc(encoded); if (!amduat_enc_asl_core_index_decode_v1(encoded, &decoded)) { fprintf(stderr, "decode failed\n"); goto cleanup; } if (decoded.header.segment_visibility != 0 || decoded.records[0].visibility != 0 || decoded.records[0].domain_id != 0 || decoded.records[0].has_cross_domain_source != 0 || decoded.records[0].cross_domain_source != 0) { fprintf(stderr, "legacy defaults not applied\n"); goto cleanup_decoded; } exit_code = 0; cleanup_decoded: amduat_enc_asl_core_index_free(&decoded); cleanup: free((void *)encoded.data); return exit_code; } int main(void) { if (test_round_trip() != 0) { return 1; } if (test_crc_mismatch() != 0) { return 1; } if (test_invalid_offsets() != 0) { return 1; } if (test_invalid_federation_fields() != 0) { return 1; } if (test_legacy_defaults() != 0) { return 1; } return 0; }