Implement ASL store FS adapter and ENC/ASL1-CORE registry

This commit is contained in:
Carl Niklas Rydberg 2025-12-19 23:16:38 +01:00
parent a0bb089f08
commit 15b36b1f1d
7 changed files with 1072 additions and 2 deletions

View file

@ -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 <stdbool.h>
#include <stddef.h>
#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 */

View file

@ -3,6 +3,7 @@
#include "amduat/asl/store.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
@ -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

View file

@ -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

View file

@ -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"}

View file

@ -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.<hex4>@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.

View file

@ -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 <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#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;
}

View file

@ -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;
}