#!/usr/bin/env python3 """Canonical account handlers + production DB transactions; deterministic IO/NVS/crypto. Not a concurrent RTOS, cryptographic, real-NVS or target test. Run directly. """ from pathlib import Path import os import subprocess import tempfile ROOT = Path(__file__).resolve().parents[2] IDF = Path(os.environ.get("IDF_PATH", str(Path.home() / ".platformio/packages/framework-espidf"))) def function(source, name): start = source.index(name + "(") start = source.rfind("\n", 0, start) + 1 return source[start:source.index("\n}", start) + 2] + "\n" def strip_includes(text): return "\n".join(line for line in text.splitlines() if not line.startswith(("#include", "#pragma once"))) db = (ROOT / "src/user_database.c").read_text() console = (ROOT / "src/user_console.c").read_text() admin = (ROOT / "src/admin_ssh_console.c").read_text() prelude = r''' #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include typedef int esp_err_t; enum { ESP_OK, ESP_FAIL, ESP_ERR_INVALID_ARG, ESP_ERR_INVALID_STATE, ESP_ERR_NO_MEM, ESP_ERR_NOT_FOUND, ESP_ERR_NOT_ALLOWED, ESP_ERR_INVALID_RESPONSE, ESP_ERR_INVALID_VERSION, ESP_ERR_TIMEOUT }; #define pdTRUE 1 static bool snapshot_busy; static int last_wait; typedef void *SemaphoreHandle_t; #define portMAX_DELAY 0 #define NVS_READWRITE 1 typedef int nvs_handle_t; static unsigned locks, writes, commits, random_calls, prompts, checks, web_revokes, ssh_revokes; static unsigned fail_stage, revoke_prompt, revoke_check, derivation_invalidations; static bool invalidate_during_derivation; static bool owner_current = true, remote = true, web = true, mismatch, cancel_prompt, stale_prompt; static int notify_error = ESP_OK; static char revoked_name[17]; static void secure_wipe(void *p, size_t n) { memset(p, 0, n); } static int xSemaphoreTake(void *m, int t) { (void)m; last_wait=t; if (snapshot_busy) return 0; assert(!locks++); return pdTRUE; } static void xSemaphoreGive(void *m) { (void)m; assert(locks-- == 1); } static const char *esp_err_to_name(int e) { (void)e; return "injected error"; } static int nvs_open(const char *ns, int mode, int *h) { (void)ns; (void)mode; assert(locks); if (invalidate_during_derivation) { assert(derivation_invalidations==1 && !owner_current); } *h=1; return fail_stage==1 ? ESP_FAIL : ESP_OK; } static int nvs_set_blob(int h, const char *key, const void *data, size_t n) { (void)h; (void)key; (void)data; (void)n; ++writes; return fail_stage==2 ? ESP_FAIL : ESP_OK; } static int nvs_commit(int h) { (void)h; ++commits; return fail_stage==3 ? ESP_FAIL : ESP_OK; } static void nvs_close(int h) { (void)h; } static int secure_random_fill(void *p, size_t n) { memset(p, ++random_calls, n); return fail_stage==4 ? ESP_FAIL : ESP_OK; } static int derive_password(const uint8_t *p, size_t n, const uint8_t *s, uint32_t iterations, uint8_t *hash) { (void)p; (void)n; (void)s; (void)iterations; memset(hash, 7, 32); if (invalidate_during_derivation) { /* Model originating browser expiry/closure after operation admission. * This is a deterministic derivation double, not real PBKDF2/HTTPD. */ assert(locks==1 && prompts==2 && checks==2 && owner_current); assert(!writes && !commits); owner_current=false; ++derivation_invalidations; } return fail_stage==5 ? ESP_FAIL : ESP_OK; } static int mbedtls_sha256(const uint8_t *p, size_t n, uint8_t *h, int mode) { (void)p; (void)n; (void)h; (void)mode; assert(!"keys outside slice"); return -1; } ''' header = strip_includes((ROOT / "src/user_database.h").read_text()) state = db[db.index("#define USER_DATABASE_SCHEMA_VERSION"):db.index("static esp_err_t initialize_dummy_verifier(")] fakes = r''' bool user_database_key_valid(const uint8_t *t, size_t tn, const uint8_t *b, size_t bn) { (void)t; (void)tn; (void)b; (void)bn; assert(!"keys outside slice"); return false; } static stored_database_t candidate_storage; static user_principal_t actor; static bool admin_ssh_console_dispatch_is_remote(void) { return remote; } static bool admin_ssh_console_dispatch_is_web(void) { return web; } static const user_principal_t *admin_ssh_console_dispatch_principal(void) { return remote ? &actor : NULL; } static bool admin_ssh_console_dispatch_is_current(void) { bool current=false; ++checks; if (checks==revoke_check) owner_current=false; return owner_current && user_database_principal_is_current(&actor, ¤t)==ESP_OK && current; } static int admin_command_gate_take(void) { return ESP_OK; } static void admin_command_gate_give(void) {} static int console_input_read_hidden(const char *prompt, uint8_t *out, size_t cap, size_t min, size_t max, size_t *n) { (void)prompt; (void)min; (void)max; assert(cap>=13); ++prompts; memcpy(out, "test-password", 13); *n=13; if (mismatch && prompts==2) out[0]='X'; /* Simulate invalidation just after the prompt boundary returned success. */ if (prompts==revoke_prompt) owner_current=false; if (stale_prompt && prompts==2) ++actor.auth_generation; return cancel_prompt ? ESP_ERR_INVALID_STATE : ESP_OK; } static int web_serial_transport_revoke_user(const uint8_t *u, size_t n) { ++web_revokes; assert(n