diff --git a/include/amduat/hash/asl1.h b/include/amduat/hash/asl1.h index a1b441b..10845f0 100644 --- a/include/amduat/hash/asl1.h +++ b/include/amduat/hash/asl1.h @@ -10,15 +10,42 @@ extern "C" { #endif +typedef struct { + bool (*digest)(void *ctx, + amduat_octets_t input, + uint8_t *out, + size_t out_len); + void *ctx; +} amduat_hash_asl1_impl_t; + typedef struct { amduat_hash_id_t hash_id; const char *name; size_t digest_len; - void *impl; + amduat_hash_asl1_impl_t impl; } amduat_hash_asl1_desc_t; +enum { AMDUAT_HASH_ASL1_ID_SHA256 = 0x0001 }; + amduat_octets_t amduat_hash_asl1_key(amduat_hash_id_t hash_id, uint8_t out[2]); +const amduat_hash_asl1_desc_t *amduat_hash_asl1_desc_lookup( + amduat_hash_id_t hash_id); + +const amduat_hash_asl1_desc_t *amduat_hash_asl1_descs(size_t *out_len); + +bool amduat_hash_asl1_register_impl(amduat_hash_id_t hash_id, + amduat_hash_asl1_impl_t impl); + +bool amduat_hash_asl1_digest(amduat_hash_id_t hash_id, + amduat_octets_t input, + uint8_t *out, + size_t out_len); + +/* Optional: registers a non-conformant SHA-256 stub for wiring/tests. + * Define AMDUAT_HASH_ASL1_ENABLE_SHA256_STUB to enable. */ +bool amduat_hash_asl1_register_sha256_stub(void); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/registry/README.md b/registry/README.md new file mode 100644 index 0000000..dcdde73 --- /dev/null +++ b/registry/README.md @@ -0,0 +1,89 @@ +# Registry Model (draft) + +This directory documents how Amduat registries are represented in the repo +before the full stack and code generation are available. The goal is to keep +registry entries stable and machine-readable so they can drive docs, code, and +graph mappings later without rewriting identifiers. + +## Core idea + +- **Registry keys are concepts.** The key itself (for example a `HashId`) has a + stable handle like `amduat.hash.asl1.id.0001@1`. +- **Registry values are data.** Each entry has a descriptor row that is data + (name, digest length, status, notes). Values are not treated as concepts. + +This matches MS/1: concept handles are stable identifiers; the descriptor bytes +are data nodes that can be hashed and referenced. + +## JSONL manifests + +Each registry has a `registry/.jsonl` manifest. Each line is one entry. +The manifest is the source of truth for codegen and documentation tables. + +Common fields (registry-specific schemas may add fields): + +- `registry`: registry name (e.g. `HASH/ASL1`). +- `hash_id` (or other key field): registry key as hex string. +- `handle`: concept handle for the key. +- `name`, `digest_len`, `status`, `spec_ref`, `notes`: descriptor fields. +- `descriptor_sha256`: digest of the canonical descriptor (see below). + +## Handle naming scheme + +Handles are opaque identifiers in the `amduat` namespace and do **not** depend +on DNS: + +``` +amduat....@ +``` + +Example (HASH/ASL1): + +``` +amduat.hash.asl1.id.0001@1 +``` + +`id` is the canonical key (often lowercase hex, zero-padded). `version` is +incremented only when the meaning of that key changes (rare). + +## Descriptor digest rule + +`descriptor_sha256` is computed over a canonical JSON object with fields in a +fixed order. Each registry schema defines the exact ordering; for HASH/ASL1 +see `registry/hash-asl1.schema.md`. + +This digest can be used as a stable Data-node hash to anchor graphs, evidence, +or generated artifacts without requiring a full ASL encoder yet. + +## Canonical key encoding + +Each registry defines the byte encoding of its key. This is the encoding used +when building an ASL registry artifact: + +- HASH/ASL1: `hash_id` is encoded as big-endian `u16` (see + `amduat_hash_asl1_key`). + +## Mapping into ASL + +When the stack is available: + +1. Encode each descriptor row as an ASL Artifact (data bytes). +2. Store it to obtain a `Reference`. +3. Build a registry value where each entry maps: + +``` +key_bytes -> value_ref +``` + +This uses the generic registry container in `amduat/asl/registry`. + +## Current manifests + +- `registry/hash-asl1.jsonl` — ASL1 HashId assignments. + +## Design constraints + +- Handles MUST be stable and immutable once published. +- Reserved IDs are explicit rows with `status = reserved`. +- The manifest should be sufficient to regenerate code tables and docs without + additional context. diff --git a/registry/hash-asl1.jsonl b/registry/hash-asl1.jsonl new file mode 100644 index 0000000..2b688ea --- /dev/null +++ b/registry/hash-asl1.jsonl @@ -0,0 +1,4 @@ +{"registry":"HASH/ASL1","hash_id":"0x0000","handle":"amduat.hash.asl1.id.0000@1","name":null,"digest_len":null,"status":"reserved","spec_ref":"HASH/ASL1 v0.2.4","notes":"Reserved; never a valid algorithm.","descriptor_sha256":"aef821024f5ec2000b3920eaf6ea3a687690df87417a86b372359b064c7d1ac3"} +{"registry":"HASH/ASL1","hash_id":"0x0001","handle":"amduat.hash.asl1.id.0001@1","name":"HASH-ASL1-256","digest_len":32,"status":"mandatory","spec_ref":"HASH/ASL1 v0.2.4","notes":"Bit-for-bit SHA-256.","descriptor_sha256":"5082d8280750dc3cff48b48a7a2eda725a16f2902592f13eaf9b677908d7517e"} +{"registry":"HASH/ASL1","hash_id":"0x0002","handle":"amduat.hash.asl1.id.0002@1","name":"HASH-ASL1-512","digest_len":64,"status":"reserved","spec_ref":"HASH/ASL1 v0.2.4","notes":"Intended classical 512-bit algorithm.","descriptor_sha256":"2063bfe4878add4d2b6c575b95466d433f9c082df5284000ffde972f6a734f6d"} +{"registry":"HASH/ASL1","hash_id":"0x8001","handle":"amduat.hash.asl1.id.8001@1","name":"HASH-ASL1-PQ1","digest_len":null,"status":"reserved","spec_ref":"HASH/ASL1 v0.2.4","notes":"First PQ algorithm placeholder.","descriptor_sha256":"fca3b7afa0c3257cf0bbef7b8b338c76f0b7c3690e60c9a90ae4a1c0b99f25f0"} diff --git a/registry/hash-asl1.schema.md b/registry/hash-asl1.schema.md new file mode 100644 index 0000000..e89af3e --- /dev/null +++ b/registry/hash-asl1.schema.md @@ -0,0 +1,52 @@ +# HASH/ASL1 Registry Manifest (draft) + +This file documents the JSONL rows in `registry/hash-asl1.jsonl`. Each line is +one entry descriptor for a single `HashId` assignment. The key is the `HashId` +itself (a concept); the descriptor is data. + +## Field definitions + +- `registry`: string. Constant `"HASH/ASL1"`. +- `hash_id`: string. Hex `u16` formatted as `0x0000`. +- `handle`: string. Stable concept handle for this HashId assignment, e.g. + `amduat.hash.asl1.id.0001@1`. +- `name`: string or null. Algorithm name if assigned (e.g. `HASH-ASL1-256`). +- `digest_len`: integer or null. Digest length in bytes; null if unknown. +- `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 + +`hash_id` is the registry key. Its key bytes are the big-endian `u16` encoding +defined in `ENC/ASL1-CORE` (see `amduat_hash_asl1_key`). + +## Handle naming scheme + +Handles are opaque identifiers in the `amduat` namespace: + +``` +amduat.hash.asl1.id.@1 +``` + +`hex4` is the 4-digit lowercase hex of the `HashId` (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, hash_id, handle, name, digest_len, 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/near_core/hash/asl1.c b/src/near_core/hash/asl1.c index 011216d..c1c0047 100644 --- a/src/near_core/hash/asl1.c +++ b/src/near_core/hash/asl1.c @@ -1,7 +1,107 @@ #include "amduat/hash/asl1.h" +static amduat_hash_asl1_desc_t g_hash_asl1_descs[] = { + {0x0000, NULL, 0, {NULL, NULL}}, + {AMDUAT_HASH_ASL1_ID_SHA256, "HASH-ASL1-256", 32, {NULL, NULL}}, + {0x0002, "HASH-ASL1-512", 64, {NULL, NULL}}, + {0x8001, "HASH-ASL1-PQ1", 0, {NULL, NULL}}}; + +#ifdef AMDUAT_HASH_ASL1_ENABLE_SHA256_STUB +static bool amduat_hash_asl1_sha256_stub(void *ctx, + amduat_octets_t input, + uint8_t *out, + size_t out_len) { + size_t i; + + (void)ctx; + (void)input; + + if (out == NULL || out_len < 32) { + return false; + } + + for (i = 0; i < 32; ++i) { + out[i] = 0; + } + + return true; +} +#endif + amduat_octets_t amduat_hash_asl1_key(amduat_hash_id_t hash_id, uint8_t out[2]) { out[0] = (uint8_t)(hash_id >> 8); out[1] = (uint8_t)(hash_id & 0xff); return amduat_octets(out, 2); } + +const amduat_hash_asl1_desc_t *amduat_hash_asl1_desc_lookup( + amduat_hash_id_t hash_id) { + size_t i; + + for (i = 0; i < sizeof(g_hash_asl1_descs) / sizeof(g_hash_asl1_descs[0]); + ++i) { + if (g_hash_asl1_descs[i].hash_id == hash_id) { + return &g_hash_asl1_descs[i]; + } + } + + return NULL; +} + +const amduat_hash_asl1_desc_t *amduat_hash_asl1_descs(size_t *out_len) { + if (out_len != NULL) { + *out_len = sizeof(g_hash_asl1_descs) / sizeof(g_hash_asl1_descs[0]); + } + return g_hash_asl1_descs; +} + +bool amduat_hash_asl1_register_impl(amduat_hash_id_t hash_id, + amduat_hash_asl1_impl_t impl) { + size_t i; + + for (i = 0; i < sizeof(g_hash_asl1_descs) / sizeof(g_hash_asl1_descs[0]); + ++i) { + if (g_hash_asl1_descs[i].hash_id == hash_id) { + g_hash_asl1_descs[i].impl = impl; + return true; + } + } + + return false; +} + +bool amduat_hash_asl1_digest(amduat_hash_id_t hash_id, + amduat_octets_t input, + uint8_t *out, + size_t out_len) { + const amduat_hash_asl1_desc_t *desc; + + if (out == NULL) { + return false; + } + + desc = amduat_hash_asl1_desc_lookup(hash_id); + if (desc == NULL || desc->digest_len == 0) { + return false; + } + if (out_len < desc->digest_len) { + return false; + } + if (desc->impl.digest == NULL) { + return false; + } + + return desc->impl.digest(desc->impl.ctx, input, out, desc->digest_len); +} + +bool amduat_hash_asl1_register_sha256_stub(void) { +#ifdef AMDUAT_HASH_ASL1_ENABLE_SHA256_STUB + amduat_hash_asl1_impl_t impl; + + impl.digest = amduat_hash_asl1_sha256_stub; + impl.ctx = NULL; + return amduat_hash_asl1_register_impl(AMDUAT_HASH_ASL1_ID_SHA256, impl); +#else + return false; +#endif +}