From 441b0191e84e0173eaa6b50583edd5f0ad50f47e Mon Sep 17 00:00:00 2001 From: Carl Niklas Rydberg Date: Sat, 20 Dec 2025 17:23:37 +0100 Subject: [PATCH] Add PEL seed/run tools and quickstart README --- CMakeLists.txt | 21 ++ README.md | 98 ++++++ src/tools/amduat_pel_run.c | 455 ++++++++++++++++++++++++ src/tools/amduat_pel_seed.c | 676 ++++++++++++++++++++++++++++++++++++ 4 files changed, 1250 insertions(+) create mode 100644 README.md create mode 100644 src/tools/amduat_pel_run.c create mode 100644 src/tools/amduat_pel_seed.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 76f733a..758825b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,27 @@ target_link_libraries(amduat_asl_cli ) set_target_properties(amduat_asl_cli PROPERTIES OUTPUT_NAME amduat-asl) +add_executable(amduat_pel_seed src/tools/amduat_pel_seed.c) +target_include_directories(amduat_pel_seed + PRIVATE ${AMDUAT_INTERNAL_DIR} + PRIVATE ${AMDUAT_INCLUDE_DIR} +) +target_link_libraries(amduat_pel_seed + PRIVATE amduat_asl_store_fs amduat_asl amduat_enc amduat_hash_asl1 amduat_util +) +set_target_properties(amduat_pel_seed PROPERTIES OUTPUT_NAME amduat-pel-seed) + +add_executable(amduat_pel_run src/tools/amduat_pel_run.c) +target_include_directories(amduat_pel_run + PRIVATE ${AMDUAT_INTERNAL_DIR} + PRIVATE ${AMDUAT_INCLUDE_DIR} +) +target_link_libraries(amduat_pel_run + PRIVATE amduat_pel amduat_asl_store_fs amduat_asl amduat_enc + amduat_hash_asl1 amduat_util +) +set_target_properties(amduat_pel_run PROPERTIES OUTPUT_NAME amduat-pel-run) + enable_testing() add_executable(amduat_test_pel_program_dag tests/enc/test_pel_program_dag.c) diff --git a/README.md b/README.md new file mode 100644 index 0000000..022c95f --- /dev/null +++ b/README.md @@ -0,0 +1,98 @@ +# Amduat (PEL quickstart) + +This repo contains the PEL/1 stack and small CLI tools to seed and run +PEL/PROGRAM-DAG/1 programs against an ASL store. + +## Build + +``` +cmake --build build +``` + +## Initialize an ASL store + +``` +amduat-asl init --root .amduat-asl +``` + +## Seed a PEL program artifact + +`amduat-pel-seed` creates a Program DAG in memory, encodes it under +`ENC/PEL-PROGRAM-DAG/1`, tags it as `TYPE_TAG_PEL_PROGRAM_DAG_1`, and +stores it in ASL. + +``` +concat1_ref=$(amduat-pel-seed --seed concat1 --root .amduat-asl --quiet) +``` + +Available seeds: + +- `const` (const "hello", no inputs) +- `concat` (concat input 0 + input 1) +- `concat1` (identity: concat input 0 only) +- `slice` (slice input 0, offset=1 length=3) +- `hash` (HASH-ASL1-256 of input 0) +- `const-hash` (const "hello" then hash) + +## Store an input artifact + +``` +input_ref=$(printf 'hello' | amduat-asl put --root .amduat-asl --input - --quiet) +``` + +## Run a program and print output bytes + +`amduat-pel-run` executes a Program via `PEL/1-SURF`, writes outputs and +result artifacts to the store, and can stream an output artifact's bytes +to stdout (like `amduat-asl get`). + +``` +amduat-pel-run --root .amduat-asl \ + --program-ref "$concat1_ref" \ + --input-ref "$input_ref" \ + --output-raw --quiet +``` + +The command above prints `hello` to stdout. Without `--quiet`, execution +status and refs are printed to stderr. + +## Notes + +- Program artifacts are standard ASL artifacts whose bytes are encoded + `ProgramBytes` (`ENC/PEL-PROGRAM-DAG/1`) and whose type tag is + `TYPE_TAG_PEL_PROGRAM_DAG_1` (`0x00000101`). +- `amduat-pel-run` reports `result_ref`, `trace_ref`, and `output_ref[N]` + when not using `--output-raw`. + +## PEL reference + +- Scheme ref (PEL/PROGRAM-DAG/1): `amduat_pel_program_dag_scheme_ref()` +- Type tags: + - Program DAG: `0x00000101` (`TYPE_TAG_PEL_PROGRAM_DAG_1`) + - Trace DAG: `0x00000102` (`TYPE_TAG_PEL_TRACE_DAG_1`) + - Surface result: `0x00000103` (`TYPE_TAG_PEL1_RESULT_1`) +- Encoding profile IDs: + - Program DAG: `0x0101` (`PEL_ENC_PROGRAM_DAG_V1`) + - Trace DAG: `0x0102` (`PEL_ENC_TRACE_DAG_V1`) + - Surface result: `0x0103` (`PEL_ENC_EXECUTION_RESULT_V1`) + +## TGK handoff (stub) + +TGK should consume PEL surface artifacts (result + trace) and their refs to +construct provenance graphs and overlays. Inputs to expect: + +- `result_ref`: a `PEL/1-SURF` execution result artifact. +- `trace_ref`: a `PEL/TRACE-DAG/1` trace artifact (optional but recommended). +- `program_ref` + `input_refs`: available inside result/trace payloads. + +Recommended next step: map trace nodes/outputs to TGK nodes/edges and build +overlay indexes (e.g., lookup by artifact ref, program ref, op name). + +## Common mistakes + +- Passing a **program ref** as an input ref. Program artifacts are encoded DAGs, + so printing their bytes yields strings like `pel.bytes.const` plus embedded + params (e.g. `hello`), not the intended data. +- Forgetting that `concat1` is identity: output bytes match input bytes. +- Expecting `params_ref` to affect DAG execution (it is accepted by the surface + API but not used in the current DAG executor). diff --git a/src/tools/amduat_pel_run.c b/src/tools/amduat_pel_run.c new file mode 100644 index 0000000..d53835d --- /dev/null +++ b/src/tools/amduat_pel_run.c @@ -0,0 +1,455 @@ +#include "amduat/asl/asl_store_fs.h" +#include "amduat/asl/asl_store_fs_meta.h" +#include "amduat/asl/io.h" +#include "amduat/asl/parse.h" +#include "amduat/asl/ref_text.h" +#include "amduat/asl/store.h" +#include "amduat/enc/pel1_result.h" +#include "amduat/pel/program_dag_desc.h" +#include "amduat/pel/surf.h" + +#include +#include +#include +#include +#include + +enum { + AMDUAT_PEL_RUN_EXIT_OK = 0, + AMDUAT_PEL_RUN_EXIT_USAGE = 2, + AMDUAT_PEL_RUN_EXIT_IO = 3, + AMDUAT_PEL_RUN_EXIT_NOT_FOUND = 4, + AMDUAT_PEL_RUN_EXIT_STORE = 5, + AMDUAT_PEL_RUN_EXIT_UNSUPPORTED = 6, + AMDUAT_PEL_RUN_EXIT_CODEC = 7, + AMDUAT_PEL_RUN_EXIT_CONFIG = 8 +}; + +static const char *const AMDUAT_PEL_RUN_DEFAULT_ROOT = ".amduat-asl"; + +typedef struct { + const char *root; + const char *program_ref; + const char *params_ref; + const char **input_refs; + size_t input_refs_len; + size_t output_index; + bool output_raw; + bool has_params_ref; + bool quiet; +} amduat_pel_run_opts_t; + +static void amduat_pel_run_print_usage(FILE *stream) { + fprintf(stream, + "usage:\n" + " amduat-pel-run --program-ref REF [--input-ref REF ...]\n" + " [--params-ref REF] [--output-raw]\n" + " [--output-index N] [--root PATH] [--quiet]\n" + "\n" + "defaults:\n" + " --root %s\n" + " --output-index 0\n" + "\n" + "notes:\n" + " REF values are ASL reference hex strings.\n" + " --output-raw writes output bytes to stdout and exec result to stderr.\n", + AMDUAT_PEL_RUN_DEFAULT_ROOT); +} + +static const char *amduat_pel_run_store_error_str( + amduat_asl_store_error_t err) { + switch (err) { + case AMDUAT_ASL_STORE_ERR_NOT_FOUND: + return "not found"; + case AMDUAT_ASL_STORE_ERR_UNSUPPORTED: + return "unsupported"; + case AMDUAT_ASL_STORE_ERR_INTEGRITY: + return "integrity"; + case AMDUAT_ASL_STORE_OK: + return "ok"; + default: + return "unknown"; + } +} + +static int amduat_pel_run_map_store_error(amduat_asl_store_error_t err) { + switch (err) { + case AMDUAT_ASL_STORE_ERR_NOT_FOUND: + return AMDUAT_PEL_RUN_EXIT_NOT_FOUND; + case AMDUAT_ASL_STORE_ERR_UNSUPPORTED: + return AMDUAT_PEL_RUN_EXIT_UNSUPPORTED; + case AMDUAT_ASL_STORE_ERR_INTEGRITY: + return AMDUAT_PEL_RUN_EXIT_STORE; + case AMDUAT_ASL_STORE_OK: + default: + return AMDUAT_PEL_RUN_EXIT_STORE; + } +} + +static void amduat_pel_run_free_reference(amduat_reference_t *ref) { + if (ref == NULL) { + return; + } + free((void *)ref->digest.data); + ref->digest.data = NULL; + ref->digest.len = 0u; +} + +static void amduat_pel_run_free_artifact(amduat_artifact_t *artifact) { + if (artifact == NULL) { + return; + } + free((void *)artifact->bytes.data); + artifact->bytes.data = NULL; + artifact->bytes.len = 0u; +} + +static void amduat_pel_run_free_refs(amduat_reference_t *refs, + size_t refs_len) { + size_t i; + + if (refs == NULL) { + return; + } + for (i = 0; i < refs_len; ++i) { + amduat_pel_run_free_reference(&refs[i]); + } + free(refs); +} + +static const char *amduat_pel_run_status_name( + amduat_pel_execution_status_t status) { + switch (status) { + case AMDUAT_PEL_EXEC_STATUS_OK: + return "OK"; + case AMDUAT_PEL_EXEC_STATUS_SCHEME_UNSUPPORTED: + return "SCHEME_UNSUPPORTED"; + case AMDUAT_PEL_EXEC_STATUS_INVALID_PROGRAM: + return "INVALID_PROGRAM"; + case AMDUAT_PEL_EXEC_STATUS_INVALID_INPUTS: + return "INVALID_INPUTS"; + case AMDUAT_PEL_EXEC_STATUS_RUNTIME_FAILED: + return "RUNTIME_FAILED"; + default: + return "UNKNOWN"; + } +} + +static const char *amduat_pel_run_error_kind_name( + amduat_pel_execution_error_kind_t kind) { + switch (kind) { + case AMDUAT_PEL_EXEC_ERROR_NONE: + return "NONE"; + case AMDUAT_PEL_EXEC_ERROR_SCHEME: + return "SCHEME"; + case AMDUAT_PEL_EXEC_ERROR_PROGRAM: + return "PROGRAM"; + case AMDUAT_PEL_EXEC_ERROR_INPUTS: + return "INPUTS"; + case AMDUAT_PEL_EXEC_ERROR_RUNTIME: + return "RUNTIME"; + default: + return "UNKNOWN"; + } +} + +int main(int argc, char **argv) { + amduat_pel_run_opts_t opts; + amduat_asl_store_fs_config_t cfg; + amduat_asl_store_fs_t fs; + amduat_asl_store_t store; + amduat_reference_t program_ref; + amduat_reference_t params_ref; + amduat_reference_t *input_refs = NULL; + amduat_reference_t *output_refs = NULL; + size_t output_refs_len = 0; + amduat_reference_t result_ref; + amduat_artifact_t result_artifact; + amduat_pel_surface_execution_result_t result_value; + bool has_result_value = false; + bool has_trace_ref = false; + amduat_reference_t trace_ref; + amduat_artifact_t output_artifact; + bool has_output_artifact = false; + int exit_code = AMDUAT_PEL_RUN_EXIT_OK; + int i; + + memset(&opts, 0, sizeof(opts)); + opts.root = AMDUAT_PEL_RUN_DEFAULT_ROOT; + opts.output_index = 0u; + + for (i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--root") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --root requires a path\n"); + return AMDUAT_PEL_RUN_EXIT_USAGE; + } + opts.root = argv[++i]; + } else if (strcmp(argv[i], "--program-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --program-ref requires a value\n"); + return AMDUAT_PEL_RUN_EXIT_USAGE; + } + opts.program_ref = argv[++i]; + } else if (strcmp(argv[i], "--input-ref") == 0) { + const char **next_inputs; + if (i + 1 >= argc) { + fprintf(stderr, "error: --input-ref requires a value\n"); + return AMDUAT_PEL_RUN_EXIT_USAGE; + } + next_inputs = (const char **)realloc( + opts.input_refs, + (opts.input_refs_len + 1u) * sizeof(*opts.input_refs)); + if (next_inputs == NULL) { + fprintf(stderr, "error: out of memory\n"); + return AMDUAT_PEL_RUN_EXIT_STORE; + } + opts.input_refs = next_inputs; + opts.input_refs[opts.input_refs_len++] = argv[++i]; + } else if (strcmp(argv[i], "--params-ref") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --params-ref requires a value\n"); + return AMDUAT_PEL_RUN_EXIT_USAGE; + } + opts.params_ref = argv[++i]; + opts.has_params_ref = true; + } else if (strcmp(argv[i], "--output-raw") == 0) { + opts.output_raw = true; + } else if (strcmp(argv[i], "--output-index") == 0) { + uint32_t index = 0; + if (i + 1 >= argc) { + fprintf(stderr, "error: --output-index requires a value\n"); + return AMDUAT_PEL_RUN_EXIT_USAGE; + } + if (!amduat_asl_parse_u32(argv[++i], &index)) { + fprintf(stderr, "error: invalid --output-index\n"); + return AMDUAT_PEL_RUN_EXIT_USAGE; + } + opts.output_index = (size_t)index; + } else if (strcmp(argv[i], "--quiet") == 0) { + opts.quiet = true; + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + amduat_pel_run_print_usage(stdout); + return AMDUAT_PEL_RUN_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_RUN_EXIT_USAGE; + } + } + + if (opts.program_ref == NULL) { + fprintf(stderr, "error: --program-ref is required\n"); + return AMDUAT_PEL_RUN_EXIT_USAGE; + } + + if (!amduat_asl_store_fs_load_config(opts.root, &cfg)) { + fprintf(stderr, "error: failed to load store config: %s\n", opts.root); + return AMDUAT_PEL_RUN_EXIT_CONFIG; + } + if (!amduat_asl_store_fs_init(&fs, cfg.config, opts.root)) { + fprintf(stderr, "error: failed to initialize store\n"); + return AMDUAT_PEL_RUN_EXIT_CONFIG; + } + amduat_asl_store_init(&store, cfg.config, amduat_asl_store_fs_ops(), &fs); + + memset(&program_ref, 0, sizeof(program_ref)); + if (!amduat_asl_ref_decode_hex(opts.program_ref, &program_ref)) { + fprintf(stderr, "error: invalid --program-ref\n"); + return AMDUAT_PEL_RUN_EXIT_USAGE; + } + + if (opts.has_params_ref) { + memset(¶ms_ref, 0, sizeof(params_ref)); + if (!amduat_asl_ref_decode_hex(opts.params_ref, ¶ms_ref)) { + amduat_pel_run_free_reference(&program_ref); + fprintf(stderr, "error: invalid --params-ref\n"); + return AMDUAT_PEL_RUN_EXIT_USAGE; + } + } else { + memset(¶ms_ref, 0, sizeof(params_ref)); + } + + if (opts.input_refs_len > 0) { + input_refs = (amduat_reference_t *)calloc( + opts.input_refs_len, sizeof(*input_refs)); + if (input_refs == NULL) { + amduat_pel_run_free_reference(&program_ref); + if (opts.has_params_ref) { + amduat_pel_run_free_reference(¶ms_ref); + } + fprintf(stderr, "error: out of memory\n"); + return AMDUAT_PEL_RUN_EXIT_STORE; + } + for (i = 0; i < (int)opts.input_refs_len; ++i) { + if (!amduat_asl_ref_decode_hex(opts.input_refs[i], &input_refs[i])) { + size_t j; + fprintf(stderr, "error: invalid --input-ref\n"); + for (j = 0; j <= (size_t)i; ++j) { + amduat_pel_run_free_reference(&input_refs[j]); + } + free(input_refs); + amduat_pel_run_free_reference(&program_ref); + if (opts.has_params_ref) { + amduat_pel_run_free_reference(¶ms_ref); + } + return AMDUAT_PEL_RUN_EXIT_USAGE; + } + } + } + + memset(&result_ref, 0, sizeof(result_ref)); + if (!amduat_pel_surf_run( + &store, + amduat_pel_program_dag_scheme_ref(), + program_ref, + input_refs, + opts.input_refs_len, + opts.has_params_ref, + params_ref, + &output_refs, + &output_refs_len, + &result_ref)) { + fprintf(stderr, "error: PEL surface execution failed\n"); + exit_code = AMDUAT_PEL_RUN_EXIT_STORE; + goto cleanup; + } + + memset(&trace_ref, 0, sizeof(trace_ref)); + memset(&result_artifact, 0, sizeof(result_artifact)); + memset(&output_artifact, 0, sizeof(output_artifact)); + if (amduat_asl_store_get(&store, result_ref, &result_artifact) == + AMDUAT_ASL_STORE_OK) { + if (amduat_enc_pel1_result_decode_v1(result_artifact.bytes, + &result_value)) { + has_result_value = true; + if (result_value.has_trace_ref) { + trace_ref = result_value.trace_ref; + has_trace_ref = true; + } + } + } + + if (opts.output_raw) { + if (opts.output_index >= output_refs_len) { + fprintf(stderr, "error: output index out of range\n"); + exit_code = AMDUAT_PEL_RUN_EXIT_USAGE; + goto cleanup; + } + if (amduat_asl_store_get(&store, output_refs[opts.output_index], + &output_artifact) != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "error: failed to load output artifact\n"); + exit_code = AMDUAT_PEL_RUN_EXIT_STORE; + goto cleanup; + } + has_output_artifact = true; + if (!amduat_asl_write_stream(stdout, + output_artifact.bytes.data, + output_artifact.bytes.len)) { + fprintf(stderr, "error: failed to write output bytes\n"); + exit_code = AMDUAT_PEL_RUN_EXIT_IO; + goto cleanup; + } + if (!opts.quiet) { + char *hex_ref = NULL; + fprintf(stderr, "status=%s(%u)\n", + has_result_value + ? amduat_pel_run_status_name(result_value.core_result.status) + : "UNKNOWN", + has_result_value ? (unsigned int)result_value.core_result.status + : 0u); + fprintf(stderr, "error_kind=%s(%u)\n", + has_result_value + ? amduat_pel_run_error_kind_name( + result_value.core_result.summary.kind) + : "UNKNOWN", + has_result_value + ? (unsigned int)result_value.core_result.summary.kind + : 0u); + fprintf(stderr, "status_code=%u\n", + has_result_value + ? (unsigned int)result_value.core_result.summary.status_code + : 0u); + if (amduat_asl_ref_encode_hex(result_ref, &hex_ref)) { + fprintf(stderr, "result_ref=%s\n", hex_ref); + free(hex_ref); + } + if (has_trace_ref && amduat_asl_ref_encode_hex(trace_ref, &hex_ref)) { + fprintf(stderr, "trace_ref=%s\n", hex_ref); + free(hex_ref); + } + if (amduat_asl_ref_encode_hex(output_refs[opts.output_index], &hex_ref)) { + fprintf(stderr, "output_ref[%zu]=%s\n", opts.output_index, hex_ref); + free(hex_ref); + } + } + } else { + if (!opts.quiet) { + char *hex_ref = NULL; + fprintf(stderr, "root=%s\n", opts.root); + if (amduat_asl_ref_encode_hex(program_ref, &hex_ref)) { + fprintf(stderr, "program_ref=%s\n", hex_ref); + free(hex_ref); + } + if (opts.has_params_ref && + amduat_asl_ref_encode_hex(params_ref, &hex_ref)) { + fprintf(stderr, "params_ref=%s\n", hex_ref); + free(hex_ref); + } + } + + { + char *hex_ref = NULL; + if (!amduat_asl_ref_encode_hex(result_ref, &hex_ref)) { + fprintf(stderr, "error: failed to encode result ref\n"); + exit_code = AMDUAT_PEL_RUN_EXIT_CODEC; + } else { + fprintf(stdout, "result_ref=%s\n", hex_ref); + free(hex_ref); + } + } + + if (has_trace_ref) { + char *hex_ref = NULL; + if (!amduat_asl_ref_encode_hex(trace_ref, &hex_ref)) { + fprintf(stderr, "error: failed to encode trace ref\n"); + exit_code = AMDUAT_PEL_RUN_EXIT_CODEC; + } else { + fprintf(stdout, "trace_ref=%s\n", hex_ref); + free(hex_ref); + } + } + + for (i = 0; i < (int)output_refs_len; ++i) { + char *hex_ref = NULL; + if (!amduat_asl_ref_encode_hex(output_refs[i], &hex_ref)) { + fprintf(stderr, "error: failed to encode output ref\n"); + exit_code = AMDUAT_PEL_RUN_EXIT_CODEC; + break; + } + fprintf(stdout, "output_ref[%d]=%s\n", i, hex_ref); + free(hex_ref); + } + } + +cleanup: + if (has_result_value) { + amduat_enc_pel1_result_free(&result_value); + } + if (has_output_artifact) { + amduat_pel_run_free_artifact(&output_artifact); + } + if (output_refs != NULL) { + amduat_pel_surf_free_refs(output_refs, output_refs_len); + } + amduat_pel_surf_free_ref(&result_ref); + amduat_pel_run_free_artifact(&result_artifact); + if (input_refs != NULL) { + amduat_pel_run_free_refs(input_refs, opts.input_refs_len); + } + if (opts.has_params_ref) { + amduat_pel_run_free_reference(¶ms_ref); + } + amduat_pel_run_free_reference(&program_ref); + free(opts.input_refs); + return exit_code; +} diff --git a/src/tools/amduat_pel_seed.c b/src/tools/amduat_pel_seed.c new file mode 100644 index 0000000..0695b73 --- /dev/null +++ b/src/tools/amduat_pel_seed.c @@ -0,0 +1,676 @@ +#include "amduat/asl/asl_store_fs.h" +#include "amduat/asl/asl_store_fs_meta.h" +#include "amduat/asl/io.h" +#include "amduat/asl/ref_text.h" +#include "amduat/asl/store.h" +#include "amduat/enc/asl1_core_codec.h" +#include "amduat/enc/pel_program_dag.h" +#include "amduat/pel/opreg_kernel.h" +#include "amduat/pel/program_dag_desc.h" + +#include +#include +#include +#include +#include + +enum { + AMDUAT_PEL_SEED_EXIT_OK = 0, + AMDUAT_PEL_SEED_EXIT_USAGE = 2, + AMDUAT_PEL_SEED_EXIT_IO = 3, + AMDUAT_PEL_SEED_EXIT_NOT_FOUND = 4, + AMDUAT_PEL_SEED_EXIT_STORE = 5, + AMDUAT_PEL_SEED_EXIT_UNSUPPORTED = 6, + AMDUAT_PEL_SEED_EXIT_CODEC = 7, + AMDUAT_PEL_SEED_EXIT_CONFIG = 8 +}; + +static const char *const AMDUAT_PEL_SEED_DEFAULT_ROOT = ".amduat-asl"; + +typedef enum { + AMDUAT_PEL_SEED_REF_HEX = 0, + AMDUAT_PEL_SEED_REF_BYTES = 1 +} amduat_pel_seed_ref_format_t; + +typedef enum { + AMDUAT_PEL_SEED_CONST = 0, + AMDUAT_PEL_SEED_CONCAT = 1, + AMDUAT_PEL_SEED_CONCAT1 = 2, + AMDUAT_PEL_SEED_SLICE = 3, + AMDUAT_PEL_SEED_HASH = 4, + AMDUAT_PEL_SEED_CONST_HASH = 5 +} amduat_pel_seed_kind_t; + +typedef struct { + amduat_pel_program_t program; + amduat_pel_node_t *nodes; + amduat_pel_root_ref_t *roots; + amduat_pel_dag_input_t **inputs; + amduat_octets_t *params; + size_t nodes_len; + size_t roots_len; +} amduat_pel_seed_program_t; + +typedef struct { + const char *root; + const char *output_path; + amduat_pel_seed_kind_t seed; + amduat_pel_seed_ref_format_t ref_format; + bool quiet; + bool list_only; +} amduat_pel_seed_opts_t; + +static void amduat_pel_seed_print_usage(FILE *stream) { + fprintf(stream, + "usage:\n" + " amduat-pel-seed [--root PATH] [--seed NAME]\n" + " [--ref-format hex|bytes] [--output PATH|-]\n" + " [--quiet]\n" + " amduat-pel-seed --list\n" + "\n" + "defaults:\n" + " --root %s\n" + " --seed const\n" + " --ref-format hex\n" + " --output -\n", + AMDUAT_PEL_SEED_DEFAULT_ROOT); +} + +static void amduat_pel_seed_print_list(FILE *stream) { + fprintf(stream, + "seeds:\n" + " const Constant bytes \"hello\" (no inputs)\n" + " concat Concatenate external inputs 0 and 1\n" + " concat1 Concatenate external input 0 (identity)\n" + " slice Slice external input 0 (offset=1 length=3)\n" + " hash Hash external input 0 (HASH-ASL1-256)\n" + " const-hash Const \"hello\" then hash (no inputs)\n"); +} + +static bool amduat_pel_seed_parse_ref_format( + const char *text, + amduat_pel_seed_ref_format_t *out_fmt) { + if (text == NULL || out_fmt == NULL) { + return false; + } + if (strcmp(text, "hex") == 0) { + *out_fmt = AMDUAT_PEL_SEED_REF_HEX; + return true; + } + if (strcmp(text, "bytes") == 0) { + *out_fmt = AMDUAT_PEL_SEED_REF_BYTES; + return true; + } + return false; +} + +static bool amduat_pel_seed_parse_kind(const char *text, + amduat_pel_seed_kind_t *out_kind) { + if (text == NULL || out_kind == NULL) { + return false; + } + if (strcmp(text, "const") == 0) { + *out_kind = AMDUAT_PEL_SEED_CONST; + return true; + } + if (strcmp(text, "concat") == 0) { + *out_kind = AMDUAT_PEL_SEED_CONCAT; + return true; + } + if (strcmp(text, "concat1") == 0) { + *out_kind = AMDUAT_PEL_SEED_CONCAT1; + return true; + } + if (strcmp(text, "slice") == 0) { + *out_kind = AMDUAT_PEL_SEED_SLICE; + return true; + } + if (strcmp(text, "hash") == 0) { + *out_kind = AMDUAT_PEL_SEED_HASH; + return true; + } + if (strcmp(text, "const-hash") == 0) { + *out_kind = AMDUAT_PEL_SEED_CONST_HASH; + return true; + } + return false; +} + +static const char *amduat_pel_seed_kind_name(amduat_pel_seed_kind_t kind) { + switch (kind) { + case AMDUAT_PEL_SEED_CONST: + return "const"; + case AMDUAT_PEL_SEED_CONCAT: + return "concat"; + case AMDUAT_PEL_SEED_CONCAT1: + return "concat1"; + case AMDUAT_PEL_SEED_SLICE: + return "slice"; + case AMDUAT_PEL_SEED_HASH: + return "hash"; + case AMDUAT_PEL_SEED_CONST_HASH: + return "const-hash"; + default: + return "unknown"; + } +} + +static const char *amduat_pel_seed_store_error_str( + amduat_asl_store_error_t err) { + switch (err) { + case AMDUAT_ASL_STORE_ERR_NOT_FOUND: + return "not found"; + case AMDUAT_ASL_STORE_ERR_UNSUPPORTED: + return "unsupported"; + case AMDUAT_ASL_STORE_ERR_INTEGRITY: + return "integrity"; + case AMDUAT_ASL_STORE_OK: + return "ok"; + default: + return "unknown"; + } +} + +static int amduat_pel_seed_map_store_error(amduat_asl_store_error_t err) { + switch (err) { + case AMDUAT_ASL_STORE_ERR_NOT_FOUND: + return AMDUAT_PEL_SEED_EXIT_NOT_FOUND; + case AMDUAT_ASL_STORE_ERR_UNSUPPORTED: + return AMDUAT_PEL_SEED_EXIT_UNSUPPORTED; + case AMDUAT_ASL_STORE_ERR_INTEGRITY: + return AMDUAT_PEL_SEED_EXIT_STORE; + case AMDUAT_ASL_STORE_OK: + default: + return AMDUAT_PEL_SEED_EXIT_STORE; + } +} + +static void amduat_pel_seed_free_reference(amduat_reference_t *ref) { + if (ref == NULL) { + return; + } + free((void *)ref->digest.data); + ref->digest.data = NULL; + ref->digest.len = 0u; +} + +static void amduat_store_u16_be(uint8_t *out, uint16_t value) { + out[0] = (uint8_t)((value >> 8) & 0xffu); + out[1] = (uint8_t)(value & 0xffu); +} + +static void amduat_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 amduat_octets_t amduat_make_const_params(const uint8_t *bytes, + size_t len) { + size_t total = 1u + 8u + len; + uint8_t *buffer = (uint8_t *)malloc(total); + if (buffer == NULL) { + return amduat_octets(NULL, 0); + } + buffer[0] = 0x00u; + amduat_store_u64_be(buffer + 1, (uint64_t)len); + if (len != 0 && bytes != NULL) { + memcpy(buffer + 1 + 8, bytes, len); + } + return amduat_octets(buffer, total); +} + +static amduat_octets_t amduat_make_slice_params(uint64_t offset, + uint64_t length) { + uint8_t *buffer = (uint8_t *)malloc(16u); + if (buffer == NULL) { + return amduat_octets(NULL, 0); + } + amduat_store_u64_be(buffer, offset); + amduat_store_u64_be(buffer + 8, length); + return amduat_octets(buffer, 16u); +} + +static amduat_octets_t amduat_make_hash_params(uint16_t hash_id) { + uint8_t *buffer = (uint8_t *)malloc(2u); + if (buffer == NULL) { + return amduat_octets(NULL, 0); + } + amduat_store_u16_be(buffer, hash_id); + return amduat_octets(buffer, 2u); +} + +static bool amduat_pel_seed_program_init(amduat_pel_seed_program_t *seed, + size_t nodes_len, + size_t roots_len) { + if (seed == NULL) { + return false; + } + memset(seed, 0, sizeof(*seed)); + seed->nodes = (amduat_pel_node_t *)calloc(nodes_len, sizeof(*seed->nodes)); + seed->roots = (amduat_pel_root_ref_t *)calloc(roots_len, + sizeof(*seed->roots)); + seed->inputs = (amduat_pel_dag_input_t **)calloc(nodes_len, + sizeof(*seed->inputs)); + seed->params = (amduat_octets_t *)calloc(nodes_len, sizeof(*seed->params)); + if (seed->nodes == NULL || seed->roots == NULL || + seed->inputs == NULL || seed->params == NULL) { + return false; + } + seed->nodes_len = nodes_len; + seed->roots_len = roots_len; + seed->program.nodes = seed->nodes; + seed->program.nodes_len = nodes_len; + seed->program.roots = seed->roots; + seed->program.roots_len = roots_len; + return true; +} + +static void amduat_pel_seed_program_free(amduat_pel_seed_program_t *seed) { + size_t i; + + if (seed == NULL) { + return; + } + for (i = 0; i < seed->nodes_len; ++i) { + free(seed->inputs[i]); + if (seed->params[i].data != NULL) { + free((void *)seed->params[i].data); + } + } + free(seed->inputs); + free(seed->params); + free(seed->nodes); + free(seed->roots); + memset(seed, 0, sizeof(*seed)); +} + +static bool amduat_pel_seed_build_const(amduat_pel_seed_program_t *seed) { + static const uint8_t k_const_bytes[] = {'h', 'e', 'l', 'l', 'o'}; + const char *op_const = AMDUAT_PEL_KERNEL_OP_CONST_NAME; + amduat_octets_t params; + + if (!amduat_pel_seed_program_init(seed, 1u, 1u)) { + return false; + } + + params = amduat_make_const_params(k_const_bytes, sizeof(k_const_bytes)); + if (params.data == NULL) { + return false; + } + seed->params[0] = params; + + seed->nodes[0].id = 1u; + seed->nodes[0].op.name = amduat_octets(op_const, strlen(op_const)); + seed->nodes[0].op.version = 1u; + seed->nodes[0].inputs = NULL; + seed->nodes[0].inputs_len = 0u; + seed->nodes[0].params = params; + + seed->roots[0].node_id = 1u; + seed->roots[0].output_index = 0u; + return true; +} + +static bool amduat_pel_seed_build_concat(amduat_pel_seed_program_t *seed) { + const char *op_concat = AMDUAT_PEL_KERNEL_OP_CONCAT_NAME; + amduat_pel_dag_input_t *inputs; + + if (!amduat_pel_seed_program_init(seed, 1u, 1u)) { + return false; + } + + inputs = (amduat_pel_dag_input_t *)calloc(2u, sizeof(*inputs)); + if (inputs == NULL) { + return false; + } + inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + inputs[0].value.external.input_index = 0u; + inputs[1].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + inputs[1].value.external.input_index = 1u; + seed->inputs[0] = inputs; + + seed->nodes[0].id = 1u; + seed->nodes[0].op.name = amduat_octets(op_concat, strlen(op_concat)); + seed->nodes[0].op.version = 1u; + seed->nodes[0].inputs = inputs; + seed->nodes[0].inputs_len = 2u; + seed->nodes[0].params = amduat_octets(NULL, 0u); + seed->params[0] = seed->nodes[0].params; + + seed->roots[0].node_id = 1u; + seed->roots[0].output_index = 0u; + return true; +} + +static bool amduat_pel_seed_build_concat1(amduat_pel_seed_program_t *seed) { + const char *op_concat = AMDUAT_PEL_KERNEL_OP_CONCAT_NAME; + amduat_pel_dag_input_t *inputs; + + if (!amduat_pel_seed_program_init(seed, 1u, 1u)) { + return false; + } + + inputs = (amduat_pel_dag_input_t *)calloc(1u, sizeof(*inputs)); + if (inputs == NULL) { + return false; + } + inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + inputs[0].value.external.input_index = 0u; + seed->inputs[0] = inputs; + + seed->nodes[0].id = 1u; + seed->nodes[0].op.name = amduat_octets(op_concat, strlen(op_concat)); + seed->nodes[0].op.version = 1u; + seed->nodes[0].inputs = inputs; + seed->nodes[0].inputs_len = 1u; + seed->nodes[0].params = amduat_octets(NULL, 0u); + seed->params[0] = seed->nodes[0].params; + + seed->roots[0].node_id = 1u; + seed->roots[0].output_index = 0u; + return true; +} + +static bool amduat_pel_seed_build_slice(amduat_pel_seed_program_t *seed) { + const char *op_slice = AMDUAT_PEL_KERNEL_OP_SLICE_NAME; + amduat_pel_dag_input_t *inputs; + amduat_octets_t params; + + if (!amduat_pel_seed_program_init(seed, 1u, 1u)) { + return false; + } + + params = amduat_make_slice_params(1u, 3u); + if (params.data == NULL) { + return false; + } + seed->params[0] = params; + + inputs = (amduat_pel_dag_input_t *)calloc(1u, sizeof(*inputs)); + if (inputs == NULL) { + return false; + } + inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + inputs[0].value.external.input_index = 0u; + seed->inputs[0] = inputs; + + seed->nodes[0].id = 1u; + seed->nodes[0].op.name = amduat_octets(op_slice, strlen(op_slice)); + seed->nodes[0].op.version = 1u; + seed->nodes[0].inputs = inputs; + seed->nodes[0].inputs_len = 1u; + seed->nodes[0].params = params; + + seed->roots[0].node_id = 1u; + seed->roots[0].output_index = 0u; + return true; +} + +static bool amduat_pel_seed_build_hash(amduat_pel_seed_program_t *seed) { + const char *op_hash = AMDUAT_PEL_KERNEL_OP_HASH_ASL1_NAME; + amduat_pel_dag_input_t *inputs; + amduat_octets_t params; + + if (!amduat_pel_seed_program_init(seed, 1u, 1u)) { + return false; + } + + params = amduat_make_hash_params(0x0001u); + if (params.data == NULL) { + return false; + } + seed->params[0] = params; + + inputs = (amduat_pel_dag_input_t *)calloc(1u, sizeof(*inputs)); + if (inputs == NULL) { + return false; + } + inputs[0].kind = AMDUAT_PEL_DAG_INPUT_EXTERNAL; + inputs[0].value.external.input_index = 0u; + seed->inputs[0] = inputs; + + seed->nodes[0].id = 1u; + seed->nodes[0].op.name = amduat_octets(op_hash, strlen(op_hash)); + seed->nodes[0].op.version = 1u; + seed->nodes[0].inputs = inputs; + seed->nodes[0].inputs_len = 1u; + seed->nodes[0].params = params; + + seed->roots[0].node_id = 1u; + seed->roots[0].output_index = 0u; + return true; +} + +static bool amduat_pel_seed_build_const_hash(amduat_pel_seed_program_t *seed) { + static const uint8_t k_const_bytes[] = {'h', 'e', 'l', 'l', 'o'}; + const char *op_const = AMDUAT_PEL_KERNEL_OP_CONST_NAME; + const char *op_hash = AMDUAT_PEL_KERNEL_OP_HASH_ASL1_NAME; + amduat_pel_dag_input_t *inputs; + amduat_octets_t const_params; + amduat_octets_t hash_params; + + if (!amduat_pel_seed_program_init(seed, 2u, 1u)) { + return false; + } + + const_params = amduat_make_const_params(k_const_bytes, + sizeof(k_const_bytes)); + if (const_params.data == NULL) { + return false; + } + seed->params[0] = const_params; + + hash_params = amduat_make_hash_params(0x0001u); + if (hash_params.data == NULL) { + return false; + } + seed->params[1] = hash_params; + + seed->nodes[0].id = 1u; + seed->nodes[0].op.name = amduat_octets(op_const, strlen(op_const)); + seed->nodes[0].op.version = 1u; + seed->nodes[0].inputs = NULL; + seed->nodes[0].inputs_len = 0u; + seed->nodes[0].params = const_params; + + inputs = (amduat_pel_dag_input_t *)calloc(1u, sizeof(*inputs)); + if (inputs == NULL) { + return false; + } + inputs[0].kind = AMDUAT_PEL_DAG_INPUT_NODE; + inputs[0].value.node.node_id = 1u; + inputs[0].value.node.output_index = 0u; + seed->inputs[1] = inputs; + + seed->nodes[1].id = 2u; + seed->nodes[1].op.name = amduat_octets(op_hash, strlen(op_hash)); + seed->nodes[1].op.version = 1u; + seed->nodes[1].inputs = inputs; + seed->nodes[1].inputs_len = 1u; + seed->nodes[1].params = hash_params; + + seed->roots[0].node_id = 2u; + seed->roots[0].output_index = 0u; + return true; +} + +static bool amduat_pel_seed_build(amduat_pel_seed_kind_t kind, + amduat_pel_seed_program_t *out_seed) { + switch (kind) { + case AMDUAT_PEL_SEED_CONST: + return amduat_pel_seed_build_const(out_seed); + case AMDUAT_PEL_SEED_CONCAT: + return amduat_pel_seed_build_concat(out_seed); + case AMDUAT_PEL_SEED_CONCAT1: + return amduat_pel_seed_build_concat1(out_seed); + case AMDUAT_PEL_SEED_SLICE: + return amduat_pel_seed_build_slice(out_seed); + case AMDUAT_PEL_SEED_HASH: + return amduat_pel_seed_build_hash(out_seed); + case AMDUAT_PEL_SEED_CONST_HASH: + return amduat_pel_seed_build_const_hash(out_seed); + default: + return false; + } +} + +int main(int argc, char **argv) { + amduat_pel_seed_opts_t opts; + amduat_asl_store_fs_config_t cfg; + amduat_asl_store_fs_t fs; + amduat_asl_store_t store; + amduat_pel_seed_program_t seed_program; + amduat_octets_t encoded; + amduat_artifact_t artifact; + amduat_reference_t ref; + amduat_octets_t encoded_ref; + int exit_code = AMDUAT_PEL_SEED_EXIT_OK; + int i; + + memset(&opts, 0, sizeof(opts)); + opts.root = AMDUAT_PEL_SEED_DEFAULT_ROOT; + opts.output_path = "-"; + opts.seed = AMDUAT_PEL_SEED_CONST; + opts.ref_format = AMDUAT_PEL_SEED_REF_HEX; + + for (i = 1; i < argc; ++i) { + if (strcmp(argv[i], "--root") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --root requires a path\n"); + return AMDUAT_PEL_SEED_EXIT_USAGE; + } + opts.root = argv[++i]; + } else if (strcmp(argv[i], "--seed") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --seed requires a value\n"); + return AMDUAT_PEL_SEED_EXIT_USAGE; + } + if (!amduat_pel_seed_parse_kind(argv[++i], &opts.seed)) { + fprintf(stderr, "error: invalid seed\n"); + return AMDUAT_PEL_SEED_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--ref-format") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --ref-format requires a value\n"); + return AMDUAT_PEL_SEED_EXIT_USAGE; + } + if (!amduat_pel_seed_parse_ref_format(argv[++i], &opts.ref_format)) { + fprintf(stderr, "error: invalid ref-format\n"); + return AMDUAT_PEL_SEED_EXIT_USAGE; + } + } else if (strcmp(argv[i], "--output") == 0) { + if (i + 1 >= argc) { + fprintf(stderr, "error: --output requires a path or -\n"); + return AMDUAT_PEL_SEED_EXIT_USAGE; + } + opts.output_path = argv[++i]; + } else if (strcmp(argv[i], "--quiet") == 0) { + opts.quiet = true; + } else if (strcmp(argv[i], "--list") == 0) { + opts.list_only = true; + } else if (strcmp(argv[i], "--help") == 0 || strcmp(argv[i], "-h") == 0) { + amduat_pel_seed_print_usage(stdout); + return AMDUAT_PEL_SEED_EXIT_OK; + } else { + fprintf(stderr, "error: unknown option: %s\n", argv[i]); + return AMDUAT_PEL_SEED_EXIT_USAGE; + } + } + + if (opts.list_only) { + amduat_pel_seed_print_list(stdout); + return AMDUAT_PEL_SEED_EXIT_OK; + } + + if (!amduat_asl_store_fs_load_config(opts.root, &cfg)) { + fprintf(stderr, "error: failed to load store config: %s\n", opts.root); + return AMDUAT_PEL_SEED_EXIT_CONFIG; + } + if (!amduat_asl_store_fs_init(&fs, cfg.config, opts.root)) { + fprintf(stderr, "error: failed to initialize store\n"); + return AMDUAT_PEL_SEED_EXIT_CONFIG; + } + amduat_asl_store_init(&store, cfg.config, amduat_asl_store_fs_ops(), &fs); + + memset(&seed_program, 0, sizeof(seed_program)); + if (!amduat_pel_seed_build(opts.seed, &seed_program)) { + amduat_pel_seed_program_free(&seed_program); + fprintf(stderr, "error: failed to build seed program\n"); + return AMDUAT_PEL_SEED_EXIT_CODEC; + } + + encoded = amduat_octets(NULL, 0); + if (!amduat_enc_pel_program_dag_encode_v1(&seed_program.program, &encoded)) { + amduat_pel_seed_program_free(&seed_program); + fprintf(stderr, "error: failed to encode program\n"); + return AMDUAT_PEL_SEED_EXIT_CODEC; + } + + artifact = amduat_artifact_with_type( + encoded, amduat_type_tag(AMDUAT_PEL_TYPE_TAG_PROGRAM_DAG_1)); + memset(&ref, 0, sizeof(ref)); + { + amduat_asl_store_error_t err = + amduat_asl_store_put(&store, artifact, &ref); + if (err != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "error: store put failed: %s\n", + amduat_pel_seed_store_error_str(err)); + exit_code = amduat_pel_seed_map_store_error(err); + } + } + + if (exit_code == AMDUAT_PEL_SEED_EXIT_OK) { + if (opts.ref_format == AMDUAT_PEL_SEED_REF_HEX) { + char *hex_ref = NULL; + if (!amduat_asl_ref_encode_hex(ref, &hex_ref)) { + fprintf(stderr, "error: failed to encode reference\n"); + exit_code = AMDUAT_PEL_SEED_EXIT_CODEC; + } else { + if (!amduat_asl_write_text_line(opts.output_path, hex_ref)) { + fprintf(stderr, "error: failed to write output: %s\n", + opts.output_path); + exit_code = AMDUAT_PEL_SEED_EXIT_IO; + } + free(hex_ref); + } + } else { + if (!amduat_enc_asl1_core_encode_reference_v1(ref, &encoded_ref)) { + fprintf(stderr, "error: failed to encode reference\n"); + exit_code = AMDUAT_PEL_SEED_EXIT_CODEC; + } else { + if (!amduat_asl_write_path(opts.output_path, + encoded_ref.data, + encoded_ref.len)) { + fprintf(stderr, "error: failed to write output: %s\n", + opts.output_path); + exit_code = AMDUAT_PEL_SEED_EXIT_IO; + } + free((void *)encoded_ref.data); + } + } + } + + if (exit_code == AMDUAT_PEL_SEED_EXIT_OK && !opts.quiet) { + char *hex_ref = NULL; + fprintf(stderr, "root=%s\n", opts.root); + fprintf(stderr, "seed=%s\n", amduat_pel_seed_kind_name(opts.seed)); + fprintf(stderr, "bytes=%zu\n", encoded.len); + fprintf(stderr, "type_tag=0x%08x\n", + (unsigned int)artifact.type_tag.tag_id); + if (amduat_asl_ref_encode_hex(ref, &hex_ref)) { + fprintf(stderr, "ref=%s\n", hex_ref); + free(hex_ref); + } + } + + amduat_pel_seed_program_free(&seed_program); + free((void *)encoded.data); + amduat_pel_seed_free_reference(&ref); + return exit_code; +}