337 lines
9.4 KiB
C
337 lines
9.4 KiB
C
|
|
#include "amduat/enc/asl_core_index.h"
|
||
|
|
|
||
|
|
#include <stdbool.h>
|
||
|
|
#include <stdio.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <string.h>
|
||
|
|
|
||
|
|
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;
|
||
|
|
}
|