From 15b36b1f1d743e3be6eb6c37ffa68c6f45b97273 Mon Sep 17 00:00:00 2001 From: Carl Niklas Rydberg Date: Fri, 19 Dec 2025 23:16:38 +0100 Subject: [PATCH] Implement ASL store FS adapter and ENC/ASL1-CORE registry --- include/amduat/asl/asl_store_fs.h | 27 +- include/amduat/enc/asl1_core.h | 14 + registry/README.md | 1 + registry/enc-asl1-core.jsonl | 1 + registry/enc-asl1-core.schema.md | 53 ++ src/adapters/asl_store_fs/asl_store_fs.c | 930 ++++++++++++++++++++++- src/near_core/enc/asl1_core.c | 48 ++ 7 files changed, 1072 insertions(+), 2 deletions(-) create mode 100644 registry/enc-asl1-core.jsonl create mode 100644 registry/enc-asl1-core.schema.md diff --git a/include/amduat/asl/asl_store_fs.h b/include/amduat/asl/asl_store_fs.h index 64a0d56..b096e37 100644 --- a/include/amduat/asl/asl_store_fs.h +++ b/include/amduat/asl/asl_store_fs.h @@ -1,6 +1,31 @@ #ifndef AMDUAT_ASL_STORE_FS_H #define AMDUAT_ASL_STORE_FS_H -/* Filesystem-backed ASL store adapter public API. */ +#include "amduat/asl/store.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum { AMDUAT_ASL_STORE_FS_ROOT_MAX = 1024 }; + +typedef struct { + amduat_asl_store_config_t config; + char root_path[AMDUAT_ASL_STORE_FS_ROOT_MAX]; +} amduat_asl_store_fs_t; + +/* Caller owns any heap allocations returned in out_ref or out_artifact. */ +bool amduat_asl_store_fs_init(amduat_asl_store_fs_t *fs, + amduat_asl_store_config_t config, + const char *root_path); + +amduat_asl_store_ops_t amduat_asl_store_fs_ops(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif #endif /* AMDUAT_ASL_STORE_FS_H */ diff --git a/include/amduat/enc/asl1_core.h b/include/amduat/enc/asl1_core.h index 93c1dfe..88346a5 100644 --- a/include/amduat/enc/asl1_core.h +++ b/include/amduat/enc/asl1_core.h @@ -3,6 +3,7 @@ #include "amduat/asl/store.h" +#include #include #include @@ -10,6 +11,8 @@ extern "C" { #endif +enum { AMDUAT_ENC_ASL1_CORE_V1 = 0x0001 }; + typedef struct { amduat_asl_encoding_profile_id_t profile_id; const char *name; @@ -21,6 +24,17 @@ amduat_octets_t amduat_enc_asl1_core_key( amduat_asl_encoding_profile_id_t profile_id, uint8_t out[2]); +const amduat_enc_asl1_core_profile_desc_t * +amduat_enc_asl1_core_desc_lookup( + amduat_asl_encoding_profile_id_t profile_id); + +const amduat_enc_asl1_core_profile_desc_t * +amduat_enc_asl1_core_descs(size_t *out_len); + +bool amduat_enc_asl1_core_register_impl( + amduat_asl_encoding_profile_id_t profile_id, + void *impl); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/registry/README.md b/registry/README.md index dcdde73..24ecad4 100644 --- a/registry/README.md +++ b/registry/README.md @@ -80,6 +80,7 @@ This uses the generic registry container in `amduat/asl/registry`. ## Current manifests - `registry/hash-asl1.jsonl` — ASL1 HashId assignments. +- `registry/enc-asl1-core.jsonl` — ASL1 core encoding profile assignments. ## Design constraints diff --git a/registry/enc-asl1-core.jsonl b/registry/enc-asl1-core.jsonl new file mode 100644 index 0000000..b09c2fd --- /dev/null +++ b/registry/enc-asl1-core.jsonl @@ -0,0 +1 @@ +{"registry":"ENC/ASL1-CORE","profile_id":"0x0001","handle":"amduat.enc.asl1.core.profile.0001@1","name":"ASL_ENC_CORE_V1","version":"0x0105","status":"mandatory","spec_ref":"ENC/ASL1-CORE v1.0.5","notes":"Canonical encoding profile for ASL/1-CORE artifacts and references.","descriptor_sha256":"ed4ce5f50d4cb62d5e420da377ffcc0a767939f795b2c9ea00e57d79e12d93c2"} diff --git a/registry/enc-asl1-core.schema.md b/registry/enc-asl1-core.schema.md new file mode 100644 index 0000000..a45dbf8 --- /dev/null +++ b/registry/enc-asl1-core.schema.md @@ -0,0 +1,53 @@ +# ENC/ASL1-CORE Registry Manifest (draft) + +This file documents the JSONL rows in `registry/enc-asl1-core.jsonl`. Each +line is one entry descriptor for a single encoding profile assignment. The key +is the profile ID (a concept); the descriptor is data. + +## Field definitions + +- `registry`: string. Constant `"ENC/ASL1-CORE"`. +- `profile_id`: string. Hex `u16` formatted as `0x0000`. +- `handle`: string. Stable concept handle for this profile assignment, e.g. + `amduat.enc.asl1.core.profile.0001@1`. +- `name`: string or null. Profile name if assigned (e.g. `ASL_ENC_CORE_V1`). +- `version`: string. Hex `u16` encoded version from the descriptor table. +- `status`: string. One of: `mandatory`, `reserved`, `deprecated`, + `experimental`. +- `spec_ref`: string. Document/version that defines the assignment. +- `notes`: string. Short human-readable note. +- `descriptor_sha256`: string. Lowercase hex SHA-256 digest of the canonical + descriptor (see below). + +## Canonical registry key + +`profile_id` is the registry key. Its key bytes are the big-endian `u16` +encoding defined in `ENC/ASL1-CORE` (see `amduat_enc_asl1_core_key`). + +## Handle naming scheme + +Handles are opaque identifiers in the `amduat` namespace: + +``` +amduat.enc.asl1.core.profile.@1 +``` + +`hex4` is the 4-digit lowercase hex of the profile ID (zero-padded). + +## Descriptor digest rule + +`descriptor_sha256` is computed as SHA-256 over the UTF-8 bytes of the +canonical JSON object with these fields **in order**: + +``` +registry, profile_id, handle, name, version, status, spec_ref, notes +``` + +The canonical JSON uses no extra whitespace and does **not** include the +`descriptor_sha256` field itself. + +## Intended use + +- Source of truth for codegen (C tables, enums) and documentation tables. +- `descriptor_sha256` can be treated as the stable digest for mapping entries + into graph references or evidence tables. diff --git a/src/adapters/asl_store_fs/asl_store_fs.c b/src/adapters/asl_store_fs/asl_store_fs.c index 5aeadb7..44e04cd 100644 --- a/src/adapters/asl_store_fs/asl_store_fs.c +++ b/src/adapters/asl_store_fs/asl_store_fs.c @@ -1,3 +1,931 @@ #include "amduat/asl/asl_store_fs.h" -/* TODO: implement filesystem-backed ASL store adapter. */ +#include "amduat/asl/core.h" +#include "amduat/enc/asl1_core.h" +#include "amduat/hash/asl1.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef O_DIRECTORY +#define O_DIRECTORY 0 +#endif + +enum { + AMDUAT_ASL_STORE_FS_MIN_DIGEST_BYTES = 2, + AMDUAT_ASL_STORE_FS_HAS_TAG_LEN = 1, + AMDUAT_ASL_STORE_FS_TAG_LEN = 4, + AMDUAT_ASL_STORE_FS_LEN_LEN = 8 +}; + +typedef enum { + AMDUAT_ASL_STORE_FS_WRITE_OK = 0, + AMDUAT_ASL_STORE_FS_WRITE_EXIST = 1, + AMDUAT_ASL_STORE_FS_WRITE_ERR = 2 +} amduat_asl_store_fs_write_status_t; + +typedef enum { + AMDUAT_ASL_STORE_FS_READ_OK = 0, + AMDUAT_ASL_STORE_FS_READ_NOT_FOUND = 1, + AMDUAT_ASL_STORE_FS_READ_ERR = 2 +} amduat_asl_store_fs_read_status_t; + +static void amduat_asl_store_fs_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 void amduat_asl_store_fs_store_u64_be(uint8_t *out, uint64_t value) { + out[0] = (uint8_t)((value >> 56) & 0xffu); + out[1] = (uint8_t)((value >> 48) & 0xffu); + out[2] = (uint8_t)((value >> 40) & 0xffu); + out[3] = (uint8_t)((value >> 32) & 0xffu); + out[4] = (uint8_t)((value >> 24) & 0xffu); + out[5] = (uint8_t)((value >> 16) & 0xffu); + out[6] = (uint8_t)((value >> 8) & 0xffu); + out[7] = (uint8_t)(value & 0xffu); +} + +static uint32_t amduat_asl_store_fs_load_u32_be(const uint8_t *data) { + return ((uint32_t)data[0] << 24) | ((uint32_t)data[1] << 16) | + ((uint32_t)data[2] << 8) | (uint32_t)data[3]; +} + +static uint64_t amduat_asl_store_fs_load_u64_be(const uint8_t *data) { + return ((uint64_t)data[0] << 56) | ((uint64_t)data[1] << 48) | + ((uint64_t)data[2] << 40) | ((uint64_t)data[3] << 32) | + ((uint64_t)data[4] << 24) | ((uint64_t)data[5] << 16) | + ((uint64_t)data[6] << 8) | (uint64_t)data[7]; +} + +static bool amduat_asl_store_fs_encode_artifact_v1( + amduat_artifact_t artifact, + uint8_t **out_bytes, + size_t *out_len) { + size_t header_len; + size_t total_len; + size_t offset; + uint8_t *buffer; + + if (out_bytes == NULL || out_len == NULL) { + return false; + } + *out_bytes = NULL; + *out_len = 0; + + if (artifact.bytes.len != 0 && artifact.bytes.data == NULL) { + return false; + } + if (artifact.bytes.len > UINT64_MAX) { + return false; + } + + header_len = AMDUAT_ASL_STORE_FS_HAS_TAG_LEN + AMDUAT_ASL_STORE_FS_LEN_LEN; + if (artifact.has_type_tag) { + header_len += AMDUAT_ASL_STORE_FS_TAG_LEN; + } + if (artifact.bytes.len > SIZE_MAX - header_len) { + return false; + } + + total_len = header_len + artifact.bytes.len; + buffer = (uint8_t *)malloc(total_len); + if (buffer == NULL) { + return false; + } + + offset = 0; + buffer[offset++] = artifact.has_type_tag ? 0x01u : 0x00u; + if (artifact.has_type_tag) { + amduat_asl_store_fs_store_u32_be(buffer + offset, artifact.type_tag.tag_id); + offset += AMDUAT_ASL_STORE_FS_TAG_LEN; + } + amduat_asl_store_fs_store_u64_be(buffer + offset, + (uint64_t)artifact.bytes.len); + offset += AMDUAT_ASL_STORE_FS_LEN_LEN; + + if (artifact.bytes.len != 0) { + memcpy(buffer + offset, artifact.bytes.data, artifact.bytes.len); + } + + *out_bytes = buffer; + *out_len = total_len; + return true; +} + +static bool amduat_asl_store_fs_decode_artifact_v1( + const uint8_t *bytes, + size_t len, + amduat_artifact_t *out_artifact) { + size_t offset; + uint8_t has_type_tag; + uint32_t tag_id; + uint64_t payload_len_u64; + size_t payload_len; + uint8_t *payload; + + if (bytes == NULL || out_artifact == NULL) { + return false; + } + if (len < AMDUAT_ASL_STORE_FS_HAS_TAG_LEN + AMDUAT_ASL_STORE_FS_LEN_LEN) { + return false; + } + + offset = 0; + has_type_tag = bytes[offset++]; + if (has_type_tag != 0x00u && has_type_tag != 0x01u) { + return false; + } + + tag_id = 0; + if (has_type_tag == 0x01u) { + if (len < offset + AMDUAT_ASL_STORE_FS_TAG_LEN + + AMDUAT_ASL_STORE_FS_LEN_LEN) { + return false; + } + tag_id = amduat_asl_store_fs_load_u32_be(bytes + offset); + offset += AMDUAT_ASL_STORE_FS_TAG_LEN; + } + + if (len < offset + AMDUAT_ASL_STORE_FS_LEN_LEN) { + return false; + } + payload_len_u64 = amduat_asl_store_fs_load_u64_be(bytes + offset); + offset += AMDUAT_ASL_STORE_FS_LEN_LEN; + if (payload_len_u64 > SIZE_MAX) { + return false; + } + payload_len = (size_t)payload_len_u64; + if (len - offset != payload_len) { + return false; + } + + payload = NULL; + if (payload_len != 0) { + payload = (uint8_t *)malloc(payload_len); + if (payload == NULL) { + return false; + } + memcpy(payload, bytes + offset, payload_len); + } + + out_artifact->bytes = amduat_octets(payload, payload_len); + out_artifact->has_type_tag = has_type_tag == 0x01u; + if (out_artifact->has_type_tag) { + out_artifact->type_tag = amduat_type_tag(tag_id); + } else { + out_artifact->type_tag.tag_id = 0; + } + return true; +} + +static void amduat_asl_store_fs_format_hex(const uint8_t *bytes, + size_t count, + char *out) { + static const char k_hex[] = "0123456789abcdef"; + size_t i; + + for (i = 0; i < count; ++i) { + const uint8_t value = bytes[i]; + out[i * 2u] = k_hex[value >> 4u]; + out[i * 2u + 1u] = k_hex[value & 0x0fu]; + } + out[count * 2u] = '\0'; +} + +static bool amduat_asl_store_fs_join_path(const char *base, + const char *segment, + char **out_path) { + size_t base_len; + size_t seg_len; + bool needs_sep; + size_t total_len; + char *buffer; + size_t offset; + + if (base == NULL || segment == NULL || out_path == NULL) { + return false; + } + if (base[0] == '\0' || segment[0] == '\0') { + return false; + } + + base_len = strlen(base); + seg_len = strlen(segment); + needs_sep = base[base_len - 1u] != '/'; + total_len = base_len + (needs_sep ? 1u : 0u) + seg_len + 1u; + + buffer = (char *)malloc(total_len); + if (buffer == NULL) { + return false; + } + + offset = 0u; + memcpy(buffer + offset, base, base_len); + offset += base_len; + if (needs_sep) { + buffer[offset++] = '/'; + } + memcpy(buffer + offset, segment, seg_len); + offset += seg_len; + buffer[offset] = '\0'; + + *out_path = buffer; + return true; +} + +static bool amduat_asl_store_fs_require_directory(const char *path) { + struct stat st; + + if (path == NULL || path[0] == '\0') { + return false; + } + if (stat(path, &st) != 0) { + return false; + } + return S_ISDIR(st.st_mode); +} + +static bool amduat_asl_store_fs_ensure_directory(const char *path) { + struct stat st; + + if (path == NULL || path[0] == '\0') { + return false; + } + if (stat(path, &st) == 0) { + return S_ISDIR(st.st_mode); + } + if (errno != ENOENT) { + return false; + } + if (mkdir(path, 0755) != 0) { + if (errno == EEXIST) { + return stat(path, &st) == 0 && S_ISDIR(st.st_mode); + } + return false; + } + return true; +} + +static amduat_asl_store_fs_write_status_t +amduat_asl_store_fs_write_atomic(const char *temp_dir, + const char *final_path, + const uint8_t *bytes, + size_t size) { + static const char suffix[] = "tmp.XXXXXX"; + size_t temp_len; + bool need_sep; + size_t template_len; + char *template_path; + int temp_fd; + size_t written; + + if (temp_dir == NULL || final_path == NULL) { + return AMDUAT_ASL_STORE_FS_WRITE_ERR; + } + if (size != 0u && bytes == NULL) { + return AMDUAT_ASL_STORE_FS_WRITE_ERR; + } + + temp_len = strlen(temp_dir); + if (temp_len == 0u) { + return AMDUAT_ASL_STORE_FS_WRITE_ERR; + } + need_sep = temp_dir[temp_len - 1u] != '/'; + template_len = temp_len + (need_sep ? 1u : 0u) + sizeof(suffix); + + template_path = (char *)malloc(template_len); + if (template_path == NULL) { + return AMDUAT_ASL_STORE_FS_WRITE_ERR; + } + if (need_sep) { + snprintf(template_path, template_len, "%s/%s", temp_dir, suffix); + } else { + snprintf(template_path, template_len, "%s%s", temp_dir, suffix); + } + + temp_fd = mkstemp(template_path); + if (temp_fd < 0) { + free(template_path); + return AMDUAT_ASL_STORE_FS_WRITE_ERR; + } + + written = 0u; + while (written < size) { + ssize_t rc = write(temp_fd, bytes + written, size - written); + if (rc < 0) { + if (errno == EINTR) { + continue; + } + close(temp_fd); + unlink(template_path); + free(template_path); + return AMDUAT_ASL_STORE_FS_WRITE_ERR; + } + written += (size_t)rc; + } + + if (fsync(temp_fd) != 0) { + close(temp_fd); + unlink(template_path); + free(template_path); + return AMDUAT_ASL_STORE_FS_WRITE_ERR; + } + + if (close(temp_fd) != 0) { + unlink(template_path); + free(template_path); + return AMDUAT_ASL_STORE_FS_WRITE_ERR; + } + + if (link(template_path, final_path) != 0) { + const int link_errno = errno; + unlink(template_path); + free(template_path); + if (link_errno == EEXIST) { + return AMDUAT_ASL_STORE_FS_WRITE_EXIST; + } + return AMDUAT_ASL_STORE_FS_WRITE_ERR; + } + + unlink(template_path); + free(template_path); + return AMDUAT_ASL_STORE_FS_WRITE_OK; +} + +static bool amduat_asl_store_fs_fsync_directory(const char *path) { + int fd; + int fsync_errno; + + if (path == NULL || path[0] == '\0') { + return false; + } + + fd = open(path, O_RDONLY | O_DIRECTORY); + if (fd < 0) { + return false; + } + while (fsync(fd) != 0) { + if (errno == EINTR) { + continue; + } + fsync_errno = errno; + close(fd); + errno = fsync_errno; + return false; + } + if (close(fd) != 0) { + return false; + } + return true; +} + +static amduat_asl_store_fs_read_status_t amduat_asl_store_fs_read_file( + const char *path, + uint8_t **out_bytes, + size_t *out_size) { + struct stat st; + size_t file_size; + uint8_t *buffer; + size_t total_read; + int fd; + + if (path == NULL || out_bytes == NULL || out_size == NULL) { + return AMDUAT_ASL_STORE_FS_READ_ERR; + } + *out_bytes = NULL; + *out_size = 0; + + if (stat(path, &st) != 0) { + if (errno == ENOENT || errno == ENOTDIR) { + return AMDUAT_ASL_STORE_FS_READ_NOT_FOUND; + } + return AMDUAT_ASL_STORE_FS_READ_ERR; + } + if (!S_ISREG(st.st_mode)) { + return AMDUAT_ASL_STORE_FS_READ_ERR; + } + if (st.st_size <= 0) { + return AMDUAT_ASL_STORE_FS_READ_ERR; + } + if ((uintmax_t)st.st_size > SIZE_MAX) { + return AMDUAT_ASL_STORE_FS_READ_ERR; + } + + file_size = (size_t)st.st_size; + buffer = (uint8_t *)malloc(file_size); + if (buffer == NULL) { + return AMDUAT_ASL_STORE_FS_READ_ERR; + } + + fd = open(path, O_RDONLY); + if (fd < 0) { + free(buffer); + if (errno == ENOENT || errno == ENOTDIR) { + return AMDUAT_ASL_STORE_FS_READ_NOT_FOUND; + } + return AMDUAT_ASL_STORE_FS_READ_ERR; + } + + total_read = 0u; + while (total_read < file_size) { + ssize_t rc = read(fd, buffer + total_read, file_size - total_read); + if (rc < 0) { + if (errno == EINTR) { + continue; + } + close(fd); + free(buffer); + return AMDUAT_ASL_STORE_FS_READ_ERR; + } + if (rc == 0) { + close(fd); + free(buffer); + return AMDUAT_ASL_STORE_FS_READ_ERR; + } + total_read += (size_t)rc; + } + + if (close(fd) != 0) { + free(buffer); + return AMDUAT_ASL_STORE_FS_READ_ERR; + } + + *out_bytes = buffer; + *out_size = file_size; + return AMDUAT_ASL_STORE_FS_READ_OK; +} + +static bool amduat_asl_store_fs_build_paths( + const amduat_asl_store_fs_t *fs, + const uint8_t *digest, + size_t digest_len, + char **out_profile_path, + char **out_hash_path, + char **out_level1_path, + char **out_level2_path, + char **out_object_path) { + char profile_hex[5]; + char hash_hex[5]; + char level1_segment[3]; + char level2_segment[3]; + char *digest_hex; + char *profile_path; + char *hash_path; + char *level1_path; + char *level2_path; + char *object_path; + bool ok; + + if (fs == NULL || digest == NULL || out_profile_path == NULL || + out_hash_path == NULL || out_level1_path == NULL || + out_level2_path == NULL || out_object_path == NULL) { + return false; + } + *out_profile_path = NULL; + *out_hash_path = NULL; + *out_level1_path = NULL; + *out_level2_path = NULL; + *out_object_path = NULL; + + if (digest_len < AMDUAT_ASL_STORE_FS_MIN_DIGEST_BYTES) { + return false; + } + if (digest_len > (SIZE_MAX - 1u) / 2u) { + return false; + } + + snprintf(profile_hex, sizeof(profile_hex), "%04x", + (unsigned int)fs->config.encoding_profile_id); + snprintf(hash_hex, sizeof(hash_hex), "%04x", + (unsigned int)fs->config.hash_id); + + amduat_asl_store_fs_format_hex(digest, 1u, level1_segment); + amduat_asl_store_fs_format_hex(digest + 1u, 1u, level2_segment); + + digest_hex = (char *)malloc(digest_len * 2u + 1u); + if (digest_hex == NULL) { + return false; + } + amduat_asl_store_fs_format_hex(digest, digest_len, digest_hex); + + profile_path = NULL; + hash_path = NULL; + level1_path = NULL; + level2_path = NULL; + object_path = NULL; + + ok = amduat_asl_store_fs_join_path(fs->root_path, profile_hex, &profile_path); + if (ok) { + ok = amduat_asl_store_fs_join_path(profile_path, hash_hex, &hash_path); + } + if (ok) { + ok = amduat_asl_store_fs_join_path(hash_path, level1_segment, &level1_path); + } + if (ok) { + ok = + amduat_asl_store_fs_join_path(level1_path, level2_segment, &level2_path); + } + if (ok) { + ok = amduat_asl_store_fs_join_path(level2_path, digest_hex, &object_path); + } + + free(digest_hex); + if (!ok) { + free(profile_path); + free(hash_path); + free(level1_path); + free(level2_path); + free(object_path); + return false; + } + + *out_profile_path = profile_path; + *out_hash_path = hash_path; + *out_level1_path = level1_path; + *out_level2_path = level2_path; + *out_object_path = object_path; + return true; +} + +static amduat_asl_store_error_t amduat_asl_store_fs_compare_existing( + const char *object_path, + const uint8_t *expected_bytes, + size_t expected_len) { + uint8_t *stored_bytes; + size_t stored_len; + amduat_asl_store_fs_read_status_t read_status; + + stored_bytes = NULL; + stored_len = 0u; + read_status = + amduat_asl_store_fs_read_file(object_path, &stored_bytes, &stored_len); + if (read_status == AMDUAT_ASL_STORE_FS_READ_NOT_FOUND) { + return AMDUAT_ASL_STORE_ERR_NOT_FOUND; + } + if (read_status != AMDUAT_ASL_STORE_FS_READ_OK) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + if (stored_len != expected_len) { + free(stored_bytes); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (expected_len != 0u && + memcmp(stored_bytes, expected_bytes, expected_len) != 0) { + free(stored_bytes); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + free(stored_bytes); + return AMDUAT_ASL_STORE_OK; +} + +static amduat_asl_store_error_t amduat_asl_store_fs_put_impl( + void *ctx, + amduat_artifact_t artifact, + amduat_reference_t *out_ref) { + amduat_asl_store_fs_t *fs; + const amduat_hash_asl1_desc_t *hash_desc; + uint8_t *artifact_bytes; + size_t artifact_len; + uint8_t *digest; + amduat_octets_t artifact_octets; + bool ok; + char *profile_path; + char *hash_path; + char *level1_path; + char *level2_path; + char *object_path; + amduat_asl_store_error_t cmp_err; + amduat_asl_store_fs_write_status_t write_status; + + if (ctx == NULL || out_ref == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + fs = (amduat_asl_store_fs_t *)ctx; + + if (fs->config.encoding_profile_id != AMDUAT_ENC_ASL1_CORE_V1) { + return AMDUAT_ASL_STORE_ERR_UNSUPPORTED; + } + hash_desc = amduat_hash_asl1_desc_lookup(fs->config.hash_id); + if (hash_desc == NULL || hash_desc->digest_len == 0 || + hash_desc->impl.digest == NULL) { + return AMDUAT_ASL_STORE_ERR_UNSUPPORTED; + } + if (hash_desc->digest_len < AMDUAT_ASL_STORE_FS_MIN_DIGEST_BYTES) { + return AMDUAT_ASL_STORE_ERR_UNSUPPORTED; + } + + artifact_bytes = NULL; + artifact_len = 0u; + if (!amduat_asl_store_fs_encode_artifact_v1(artifact, &artifact_bytes, + &artifact_len)) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + digest = (uint8_t *)malloc(hash_desc->digest_len); + if (digest == NULL) { + free(artifact_bytes); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + artifact_octets = amduat_octets(artifact_bytes, artifact_len); + if (!amduat_hash_asl1_digest(hash_desc->hash_id, artifact_octets, digest, + hash_desc->digest_len)) { + free(artifact_bytes); + free(digest); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + profile_path = NULL; + hash_path = NULL; + level1_path = NULL; + level2_path = NULL; + object_path = NULL; + ok = amduat_asl_store_fs_build_paths(fs, + digest, + hash_desc->digest_len, + &profile_path, + &hash_path, + &level1_path, + &level2_path, + &object_path); + if (!ok) { + free(artifact_bytes); + free(digest); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + if (!amduat_asl_store_fs_require_directory(fs->root_path) || + !amduat_asl_store_fs_ensure_directory(profile_path) || + !amduat_asl_store_fs_ensure_directory(hash_path) || + !amduat_asl_store_fs_ensure_directory(level1_path) || + !amduat_asl_store_fs_ensure_directory(level2_path)) { + free(artifact_bytes); + free(digest); + free(profile_path); + free(hash_path); + free(level1_path); + free(level2_path); + free(object_path); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + cmp_err = amduat_asl_store_fs_compare_existing(object_path, + artifact_bytes, + artifact_len); + if (cmp_err == AMDUAT_ASL_STORE_OK) { + out_ref->hash_id = fs->config.hash_id; + out_ref->digest = amduat_octets(digest, hash_desc->digest_len); + free(artifact_bytes); + free(profile_path); + free(hash_path); + free(level1_path); + free(level2_path); + free(object_path); + return AMDUAT_ASL_STORE_OK; + } + if (cmp_err == AMDUAT_ASL_STORE_ERR_INTEGRITY) { + free(artifact_bytes); + free(digest); + free(profile_path); + free(hash_path); + free(level1_path); + free(level2_path); + free(object_path); + return cmp_err; + } + + write_status = amduat_asl_store_fs_write_atomic(level2_path, + object_path, + artifact_bytes, + artifact_len); + if (write_status == AMDUAT_ASL_STORE_FS_WRITE_EXIST) { + cmp_err = amduat_asl_store_fs_compare_existing(object_path, + artifact_bytes, + artifact_len); + if (cmp_err != AMDUAT_ASL_STORE_OK) { + free(artifact_bytes); + free(digest); + free(profile_path); + free(hash_path); + free(level1_path); + free(level2_path); + free(object_path); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + } else if (write_status != AMDUAT_ASL_STORE_FS_WRITE_OK) { + free(artifact_bytes); + free(digest); + free(profile_path); + free(hash_path); + free(level1_path); + free(level2_path); + free(object_path); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + if (!amduat_asl_store_fs_fsync_directory(level2_path)) { + unlink(object_path); + free(artifact_bytes); + free(digest); + free(profile_path); + free(hash_path); + free(level1_path); + free(level2_path); + free(object_path); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + if (!amduat_asl_store_fs_fsync_directory(fs->root_path)) { + unlink(object_path); + free(artifact_bytes); + free(digest); + free(profile_path); + free(hash_path); + free(level1_path); + free(level2_path); + free(object_path); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + out_ref->hash_id = fs->config.hash_id; + out_ref->digest = amduat_octets(digest, hash_desc->digest_len); + free(artifact_bytes); + free(profile_path); + free(hash_path); + free(level1_path); + free(level2_path); + free(object_path); + return AMDUAT_ASL_STORE_OK; +} + +static amduat_asl_store_error_t amduat_asl_store_fs_get_impl( + void *ctx, + amduat_reference_t ref, + amduat_artifact_t *out_artifact) { + amduat_asl_store_fs_t *fs; + const amduat_hash_asl1_desc_t *hash_desc; + char *profile_path; + char *hash_path; + char *level1_path; + char *level2_path; + char *object_path; + uint8_t *stored_bytes; + size_t stored_len; + amduat_asl_store_fs_read_status_t read_status; + uint8_t *computed_digest; + amduat_octets_t stored_octets; + bool decoded; + + if (ctx == NULL || out_artifact == NULL) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + fs = (amduat_asl_store_fs_t *)ctx; + + if (fs->config.encoding_profile_id != AMDUAT_ENC_ASL1_CORE_V1) { + return AMDUAT_ASL_STORE_ERR_UNSUPPORTED; + } + if (ref.hash_id != fs->config.hash_id) { + return AMDUAT_ASL_STORE_ERR_UNSUPPORTED; + } + hash_desc = amduat_hash_asl1_desc_lookup(fs->config.hash_id); + if (hash_desc == NULL || hash_desc->digest_len == 0 || + hash_desc->impl.digest == NULL) { + return AMDUAT_ASL_STORE_ERR_UNSUPPORTED; + } + if (hash_desc->digest_len < AMDUAT_ASL_STORE_FS_MIN_DIGEST_BYTES) { + return AMDUAT_ASL_STORE_ERR_UNSUPPORTED; + } + if (ref.digest.len != hash_desc->digest_len || + (ref.digest.len != 0u && ref.digest.data == NULL)) { + return AMDUAT_ASL_STORE_ERR_UNSUPPORTED; + } + + profile_path = NULL; + hash_path = NULL; + level1_path = NULL; + level2_path = NULL; + object_path = NULL; + if (!amduat_asl_store_fs_build_paths(fs, + ref.digest.data, + ref.digest.len, + &profile_path, + &hash_path, + &level1_path, + &level2_path, + &object_path)) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + stored_bytes = NULL; + stored_len = 0u; + read_status = + amduat_asl_store_fs_read_file(object_path, &stored_bytes, &stored_len); + if (read_status == AMDUAT_ASL_STORE_FS_READ_NOT_FOUND) { + free(profile_path); + free(hash_path); + free(level1_path); + free(level2_path); + free(object_path); + return AMDUAT_ASL_STORE_ERR_NOT_FOUND; + } + if (read_status != AMDUAT_ASL_STORE_FS_READ_OK) { + free(profile_path); + free(hash_path); + free(level1_path); + free(level2_path); + free(object_path); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + computed_digest = (uint8_t *)malloc(hash_desc->digest_len); + if (computed_digest == NULL) { + free(stored_bytes); + free(profile_path); + free(hash_path); + free(level1_path); + free(level2_path); + free(object_path); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + stored_octets = amduat_octets(stored_bytes, stored_len); + if (!amduat_hash_asl1_digest(hash_desc->hash_id, + stored_octets, + computed_digest, + hash_desc->digest_len)) { + free(computed_digest); + free(stored_bytes); + free(profile_path); + free(hash_path); + free(level1_path); + free(level2_path); + free(object_path); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + if (memcmp(computed_digest, ref.digest.data, hash_desc->digest_len) != 0) { + free(computed_digest); + free(stored_bytes); + free(profile_path); + free(hash_path); + free(level1_path); + free(level2_path); + free(object_path); + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + free(computed_digest); + + decoded = amduat_asl_store_fs_decode_artifact_v1(stored_bytes, + stored_len, + out_artifact); + free(stored_bytes); + free(profile_path); + free(hash_path); + free(level1_path); + free(level2_path); + free(object_path); + if (!decoded) { + return AMDUAT_ASL_STORE_ERR_INTEGRITY; + } + + return AMDUAT_ASL_STORE_OK; +} + +bool amduat_asl_store_fs_init(amduat_asl_store_fs_t *fs, + amduat_asl_store_config_t config, + const char *root_path) { + size_t len; + + if (fs == NULL || root_path == NULL) { + return false; + } + len = strlen(root_path); + if (len == 0u || len >= AMDUAT_ASL_STORE_FS_ROOT_MAX) { + return false; + } + memcpy(fs->root_path, root_path, len); + fs->root_path[len] = '\0'; + fs->config = config; + return true; +} + +amduat_asl_store_ops_t amduat_asl_store_fs_ops(void) { + amduat_asl_store_ops_t ops; + + ops.put = amduat_asl_store_fs_put_impl; + ops.get = amduat_asl_store_fs_get_impl; + return ops; +} diff --git a/src/near_core/enc/asl1_core.c b/src/near_core/enc/asl1_core.c index 368ea6a..1b563c5 100644 --- a/src/near_core/enc/asl1_core.c +++ b/src/near_core/enc/asl1_core.c @@ -1,5 +1,8 @@ #include "amduat/enc/asl1_core.h" +static amduat_enc_asl1_core_profile_desc_t g_enc_asl1_core_profiles[] = { + {AMDUAT_ENC_ASL1_CORE_V1, "ASL_ENC_CORE_V1", 0x0105, NULL}}; + amduat_octets_t amduat_enc_asl1_core_key( amduat_asl_encoding_profile_id_t profile_id, uint8_t out[2]) { @@ -7,3 +10,48 @@ amduat_octets_t amduat_enc_asl1_core_key( out[1] = (uint8_t)(profile_id & 0xff); return amduat_octets(out, 2); } + +const amduat_enc_asl1_core_profile_desc_t * +amduat_enc_asl1_core_desc_lookup( + amduat_asl_encoding_profile_id_t profile_id) { + size_t i; + + for (i = 0; + i < sizeof(g_enc_asl1_core_profiles) / + sizeof(g_enc_asl1_core_profiles[0]); + ++i) { + if (g_enc_asl1_core_profiles[i].profile_id == profile_id) { + return &g_enc_asl1_core_profiles[i]; + } + } + + return NULL; +} + +const amduat_enc_asl1_core_profile_desc_t * +amduat_enc_asl1_core_descs(size_t *out_len) { + if (out_len != NULL) { + *out_len = + sizeof(g_enc_asl1_core_profiles) / + sizeof(g_enc_asl1_core_profiles[0]); + } + return g_enc_asl1_core_profiles; +} + +bool amduat_enc_asl1_core_register_impl( + amduat_asl_encoding_profile_id_t profile_id, + void *impl) { + size_t i; + + for (i = 0; + i < sizeof(g_enc_asl1_core_profiles) / + sizeof(g_enc_asl1_core_profiles[0]); + ++i) { + if (g_enc_asl1_core_profiles[i].profile_id == profile_id) { + g_enc_asl1_core_profiles[i].impl = impl; + return true; + } + } + + return false; +}