diff --git a/QUICK_START_TPM2.md b/QUICK_START_TPM2.md deleted file mode 100644 index 2fef4a5..0000000 --- a/QUICK_START_TPM2.md +++ /dev/null @@ -1,203 +0,0 @@ -# Quick Start: Using MASTR with TPM2 - -## Prerequisites - -1. **Your user is in the `tss` group** ✅ (Already done) -2. **TPM2 device is accessible** ✅ (Already verified) -3. **tpm2-pytss is installed** ✅ (Already installed) - -## Running MASTR with TPM2 - -### Basic Usage - -```bash -# Navigate to project directory -cd ~/Documents/embed\ project/MASTR - -# Run with TPM2 crypto backend -python -m host.main --crypto tpm2 --port /dev/ttyACM0 -``` - -### First Run (Key Provisioning) - -On the first run, TPM2Crypto will: -1. Check if permanent keys exist in TPM -2. If not found, generate new ECC P-256 keypair in TPM -3. Store private key at persistent handle `0x81000080` -4. Exchange public keys with the token -5. Store token's public key in NVRAM at `0x01C00002` - -You should see output like: -``` -[INFO] TPM initialized successfully. -[INFO] Generating new permanent keypair in TPM... -[SUCCESS] Permanent keypair generated in TPM -[INFO] Exchanging public keys with token... -``` - -### Subsequent Runs - -After initial provisioning, keys are loaded from TPM: -```bash -python -m host.main --crypto tpm2 --port /dev/ttyACM0 -``` - -Output will show: -``` -[INFO] TPM initialized successfully. -[SUCCESS] Permanent keys loaded from TPM -[INFO] Starting ECDH mutual authentication... -``` - -## Command Options - -```bash -# Full syntax -python -m host.main \ - --crypto tpm2 \ # Use TPM2 backend - --port /dev/ttyACM0 \ # Serial port - --baudrate 115200 \ # Baud rate (default: 115200) - --verbose \ # Enable verbose logging - --provision # Force key regeneration -``` - -### Common Flags - -- `--crypto tpm2` - Use TPM2 hardware crypto (required) -- `--port /dev/ttyACM0` - Serial port for token communication -- `--verbose` - Show detailed protocol messages -- `--provision` - Force regenerate keys (useful for testing) - -## Testing TPM2 Standalone - -Before running the full protocol, test TPM2 operations: - -```bash -# Test TPM2 crypto implementation -python3 test_tpm2_updated.py -``` - -Expected output: -``` -=== Testing TPM2 Basic Operations === - -1. Initializing TPM... - ✓ TPM initialized successfully - -2. Creating ECC primary key... - ✓ ECC key created successfully - ✓ Public key X: c89d3105d30bd6079... - ✓ Public key Y: 69023b4ae67a214c5... - -3. Testing ECDSA signing... - ✓ Signature R: b14d61ada66fa642... - ✓ Signature S: d58d2cc49e43b1c9... - -4. Creating ECDH key... - ✓ ECDH key created successfully - ... - -=== All Tests Passed! === -``` - -## Comparing with File-Based Crypto - -### Development Mode (File-Based) -```bash -# Uses files: host_permanent_privkey.pem, token_permanent_pubkey.bin -python -m host.main --crypto naive --port /dev/ttyACM0 -``` - -### Production Mode (TPM2) -```bash -# Uses TPM2 hardware storage -python -m host.main --crypto tpm2 --port /dev/ttyACM0 -``` - -**The protocol behavior is identical** - only key storage differs! - -## Protocol Flow with TPM2 - -``` -1. KEY PROVISIONING (Phase 0) - ┌─────────────────────────────────────┐ - │ Load or generate keys in TPM │ - │ Exchange public keys with token │ - └─────────────────────────────────────┘ - -2. MUTUAL AUTHENTICATION (Phase 1) - ┌─────────────────────────────────────┐ - │ Generate ephemeral keys in TPM │ - │ Sign ephemeral keys with TPM │ - │ Verify token's signature │ - │ Compute ECDH secret in TPM │ - │ Derive AES session key │ - └─────────────────────────────────────┘ - -3. CHANNEL VERIFICATION (Phase 1.5) - ┌─────────────────────────────────────┐ - │ Encrypted ping/pong challenge │ - │ Confirms session key matches │ - └─────────────────────────────────────┘ - -4. SECURE COMMUNICATION - ┌─────────────────────────────────────┐ - │ All messages encrypted with │ - │ AES-128-GCM using session key │ - └─────────────────────────────────────┘ -``` - -## Security Benefits - -### With TPM2Crypto -- ✅ Private key **never leaves** TPM hardware -- ✅ Signing operations performed **inside TPM** -- ✅ ECDH computation performed **inside TPM** -- ✅ Keys persist across reboots -- ✅ Hardware-protected storage -- ✅ Resistant to software-based key extraction - -### Without TPM2 (File-Based) -- ❌ Private key stored in PEM file on disk -- ❌ Vulnerable to file system access -- ❌ Key material in process memory -- ❌ Can be extracted by debugger - -## Troubleshooting - -### Error: "Failed to initialize TPM2 crypto" - -Check device permissions: -```bash -ls -la /dev/tpm* -groups -``` - -### Error: "ModuleNotFoundError: No module named 'tpm2_pytss'" - -Install the library: -```bash -sudo pacman -S python-tpm2-pytss -``` - -### TPM has old keys from previous test - -Reset TPM storage: -```bash -# Clear persistent key -tpm2_evictcontrol -C o -c 0x81000080 - -# Clear NVRAM -tpm2_nvundefine 0x01C00002 -C o - -# Or use --provision flag -python -m host.main --crypto tpm2 --provision -``` - -## What's Next? - -1. **Connect your token** to `/dev/ttyACM0` -2. **Run with TPM2**: `python -m host.main --crypto tpm2 --port /dev/ttyACM0` -3. **Watch the protocol execute** with full TPM2 hardware security! - -The entire ECDH key exchange, signing, and encryption now happens with **hardware-backed cryptography**! diff --git a/docs/provisioning_flow.md b/docs/provisioning_flow.md deleted file mode 100644 index 4dfb62e..0000000 --- a/docs/provisioning_flow.md +++ /dev/null @@ -1,258 +0,0 @@ -# One-time Host–Token Pairing (Provisioning) Plan - -This document explains the goal Lucas is aiming for and outlines the concrete API and UI flow to move the first‑run provisioning from debug prints to the HTTP API + web UI. - ---- -## Goal in plain terms -- On first boot, the Token (device) and a Host (PC/server) must pair exactly once. -- Today, this pairing uses debug console messages to exchange the Token public key and a "golden hash". The ask is to make this happen via API endpoints and a simple UI wizard so the user can copy/paste the necessary data. -- After pairing, the device should remember the Host and the golden hash, and normal operation begins; subsequent boots should not require pairing again unless reset. - ---- -## How it works today (CURRENT STATE) - -Mechanism today is console/serial driven: -1. Device boots and prints debug messages (via `print_dbg`) exposing: public key, intermediate protocol states, and the "golden hash". -2. A host Python script (or manual copy in a terminal) reads those debug lines, parses out required values. -3. Host responds (over serial protocol frames) with its public key and expected golden hash. -4. Device stores these in RAM (not persisted) and transitions the internal `protocol_state.current_state` to runtime (0x40). - -Limitations: -- Manual copy/paste, easy to mis-transcribe. -- No structured validation feedback (just debug prints). -- Values are ephemeral; reboot requires repeating unless separately saved. -- Harder to script remotely (must attach to USB serial). -- No UI representation; provisioning progress opaque unless watching terminal. - -Summary: Pairing is implicit, using the existing framed serial protocol and debug lines, not an explicit transactional API. - ---- -## Target architecture (AFTER CHANGES) - -Replace ad‑hoc serial exchange with a REST + UI wizard: -1. Device starts OPEN AP (no password) and advertises provisioning endpoints. -2. Browser hits `/api/provision/state` to see if `provisioned=false`. -3. User opens "Provisioning" page: UI calls `/api/provision/token_info` and displays token public key, optional attestation blob, and (optionally) device-generated golden hash. -4. User (or host software) supplies Host Public Key + Golden Hash via POST `/api/provision/host_submit`. -5. UI shows submitted values for confirmation; user presses "Confirm" -> POST `/api/provision/confirm`. -6. Device persists host key + golden hash + provisioned flag in flash; subsequent `/api/provision/state` returns `provisioned=true` and endpoints are locked. -7. Runtime heartbeat uses `/api/heartbeat` or existing `/api/status`. - -Benefits: -- Predictable JSON contracts; easy to automate. -- One-time explicit confirmation step. -- Copy/paste friendly UI (no terminal required). -- Persistence ensures reboot resilience. -- Clear error codes (400, 409) instead of generic debug lines. - ---- -## Delta: What must change - -Area | Current | Required Change ----- | ------- | --------------- -Public key exposure | Printed in debug | Serve via `/api/provision/token_info` JSON -Golden hash exchange | Printed / serial frame | Submit via `/api/provision/host_submit` body -Provisioning state | Implicit in protocol_state | Explicit `/api/provision/state` (provisioned + step) -Confirmation | None (implicit) | `/api/provision/confirm` finalizes & persists -Persistence | RAM only | Flash/EEPROM sector with CRC (host key + hash + flag) -UI | Debug terminal | Web wizard (three steps) in `index.html` -Security gating | N/A | Disable provisioning endpoints after success; require factory reset to re-enable -Attestation | Mixed debug prints | Optional structured field (`attestation.report`, `nonce`) -Errors | Freeform prints | HTTP status + JSON `{ "error": "..." }` - ---- -## Minimal state machine (new) - -State | Description | Transitions ------ | ----------- | ---------- -`start` | Device unprovisioned, waiting for user | -> `token_info` on first GET -`token_info` | Token info served | -> `await_host` after host submits data -`await_host` | Host data stored, waiting confirmation | -> `done` on confirm -`done` | Provisioned complete | (locked) unless factory reset - -Implementation hint: Store `current_provision_step` in RAM; persist only the final `provisioned` flag and artifacts to flash. - ---- -## Persistence strategy (flash layout example) - -| Offset | Length | Field | -| ------ | ------ | ----- | -| 0x00 | 4 | Magic (0x504B5450 'PKTP') | -| 0x04 | 2 | Version | -| 0x06 | 2 | Host key length | -| 0x08 | N | Host public key PEM (N bytes) | -| 0x08+N | 2 | Golden hash length | -| ... | M | Golden hash bytes (hex or raw) | -| ... | 1 | Provisioned flag (0x01) | -| ... | 4 | CRC32 over all preceding fields | - -Failure handling: -- On CRC mismatch -> treat as unprovisioned. -- On flash write failure -> return 500 from `/api/provision/confirm`. - ---- -## Validation rules (to implement) -- Host public key PEM: begins with `-----BEGIN PUBLIC KEY-----`, length within bounds (e.g. 512–2048 bytes). -- Golden hash: hex (even length) or base64; server normalizes to hex internally. -- Reject duplicate provisioning (respond 409 if `provisioned=true`). -- Attestation (if used): nonce length fixed (e.g. 16 bytes), report base64 size check. - ---- -## Migration plan (step-by-step) -1. Add in-memory scaffolding for new endpoints (no persistence yet) returning mock values. -2. Integrate real token public key extraction (from crypto subsystem) into `/api/provision/token_info`. -3. Implement host submission parsing & validation. -4. Implement flash persistence layer (read/write + CRC) and finalize confirm step. -5. Extend `index.html` with provisioning wizard section; hide existing metrics until provisioned. -6. Gate existing sensitive endpoints (if any) while `!provisioned` (optional). -7. Add factory reset mechanism (e.g., long button press or `/api/provision/reset` with physical presence check). -8. Remove or minimize debug prints of sensitive material (replace with informational logs only). - ---- -## Risks & Mitigations -Risk | Mitigation ----- | ---------- -Power loss during flash write | Write to temp sector then atomic flag set; verify CRC on boot. -Host submits malformed PEM | Strict parser + length limits + 400 response. -Replay / MITM on open AP | Physical proximity assumption + optional attestation challenge. -Accidental re-provision | Require factory reset or signed admin token. - ---- -## Token vs Host Responsibilities (recap) -Token: Serve provisioning endpoints, validate inputs, persist artifacts, enforce golden hash. -Host: Fetch token info, verify/record, POST host key + golden hash, confirm. - ---- -## Actors and artifacts -- Token (this device) - - Token Public Key (from secure element ATECC608A or internal key store) - - Attestation proof/evidence (optional but recommended) - - Golden Hash (reference hash of firmware/config or key material agreed upon with Host) -- Host (your PC/app) - - Host Public Key - - Expected Golden Hash - -Persistent on Token after pairing: -- host_public_key -- golden_hash -- provisioned flag (true) - ---- -## Trust model (high-level) -- First‑run pairing happens over the Token’s local AP; the user is physically present. -- The Token shows its identity (public key + optional attestation) so the Host can verify. -- The Host provides its public key and the golden hash the Token must enforce at runtime. -- Token stores both and marks itself provisioned. - ---- -## Proposed API surface (one-time provisioning) -All endpoints are unauthenticated only when not yet provisioned; once provisioned, they are either disabled or require auth. - -1) GET `/api/provision/state` -- Purpose: Tell the UI if pairing is needed and what step we’re at. -- Response: -``` -{ - "provisioned": false, - "step": "start" | "token_info" | "await_host" | "done" -} -``` - -2) GET `/api/provision/token_info` -- Purpose: Provide copy/pasteable Token identity to the user/Host. -- Response: -``` -{ - "token_pubkey_pem": "-----BEGIN PUBLIC KEY-----...", - "attestation": { "nonce": "...", "report": "base64..." }, - "golden_hash": "" -} -``` -- Notes: `golden_hash` can be the device’s computed reference hash OR left blank if the Host is the source of truth. - -3) POST `/api/provision/host_submit` -- Purpose: Host submits its identity and the expected golden hash. -- Body: -``` -{ - "host_pubkey_pem": "-----BEGIN PUBLIC KEY-----...", - "golden_hash": "" -} -``` -- Response: `{ "status": "ok" }` (or errors if invalid format/length) - -4) POST `/api/provision/confirm` -- Purpose: Finalize pairing. The Token verifies inputs, persists them, and marks provisioned. -- Body: `{ "confirm": true }` -- Response: `{ "status": "ok", "provisioned": true }` - -5) GET `/api/heartbeat` (optional) -- Purpose: UI runtime heartbeat after pairing. Returns a minimal JSON to show live status. -- Response: `{ "ok": true, "uptime_s": , "state": "0x40" }` - ---- -## UI wizard sketch (one page, three panels) -1) "Token identity" (auto-filled via GET `/api/provision/token_info`) - - Show Token Public Key (PEM textarea) - - Show Attestation blob/nonce (optional) - - Show/Copy the Token’s Golden Hash (if device-generated) - - Button: Copy to clipboard - -2) "Host details" - - Textarea: Paste Host Public Key (PEM) - - Input: Golden Hash (hex/base64) – if Host-sourced - - Button: Submit (POST `/api/provision/host_submit`) - -3) "Confirm" - - Button: Confirm (POST `/api/provision/confirm`) - - On success, show: "Provisioned ✓" and disable the wizard - -Quick safety notes: -- Disable these routes (or require auth) after `provisioned == true`. -- If you support a factory reset, re-enable the routes when reset occurs. - ---- -## Persistence & storage -- Store `host_pubkey` and `golden_hash` in non-volatile memory (flash/EEPROM or secure element slots, if available). -- Store a `provisioned` flag. -- On boot, read these; set `protocol_state.current_state = 0x40` only when provisioned and verified. - ---- -## Heartbeat on the UI -- Heartbeat messages don’t need to be the raw serial logs. The UI can simply poll `/api/heartbeat` (or `/api/status`) every few seconds and display a green dot, uptime, and current state. -- This gives users confidence during and after provisioning without exposing serial debug transports. - ---- -## Error modes & validations -- Invalid PEM formatting → 400 with message -- Golden hash wrong length/encoding → 400 -- Already provisioned → 409 (conflict) -- Persistence I/O error → 500 with reason - ---- -## Security considerations -- Lock provisioning endpoints after success, or require a temporary claim token/password. -- Bind Host public key tightly to the device once set; require explicit reset to change it. -- Consider including an attestation challenge/response to defend against token cloning. - ---- -## Minimal contracts (inputs/outputs) -Inputs: -- Host public key (PEM) -- Golden hash (hex/base64) -Outputs: -- Token public key (PEM) -- Optional attestation report -- Provisioned state boolean -Success criteria: -- Device stores host key + golden hash and reports `provisioned=true`. -- Subsequent boots treat device as paired and hide provisioning UI. - ---- -## Next steps to implement -1) Add new endpoints in `api.c` (state, token_info, host_submit, confirm, heartbeat). -2) Add simple persistence layer (flash sector) with CRC. -3) Build UI wizard in `index.html` to call the new endpoints. -4) Gate endpoints by `!provisioned` and add a factory reset path. - -(We can implement these in small PRs. Let me know and I’ll start with the endpoints + dummy persistence, then wire the UI.) diff --git a/src/net/ap/ap_manager.c b/src/net/ap/ap_manager.c index 93a830f..5610a2c 100644 --- a/src/net/ap/ap_manager.c +++ b/src/net/ap/ap_manager.c @@ -36,7 +36,7 @@ int start_access_point(const char *ssid, const char *pass) { int auth_mode = CYW43_AUTH_WPA2_AES_PSK; if (pass == NULL || strlen(pass) < 8) { auth_mode = CYW43_AUTH_OPEN; - ap_pass = ""; // driver will treat this as open + ap_pass = ""; print_dbg("WARNING: WiFi password too short, using open AP\n"); } @@ -46,7 +46,7 @@ int start_access_point(const char *ssid, const char *pass) { // Use non-blocking approach: wait briefly but yield to other tasks // This prevents blocking serial provisioning - int retries = 20; // up to ~2 seconds, but each retry yields + int retries = 20; while (retries-- > 0) { ap_ip4 = netif_ip4_addr(&cyw43_state.netif[CYW43_ITF_AP]); ap_nm4 = netif_ip4_netmask(&cyw43_state.netif[CYW43_ITF_AP]); @@ -99,8 +99,6 @@ int reconfigure_access_point(const char *ssid, const char *pass) { return 0; } - - /** * Get DHCP server instance for querying connected clients */ diff --git a/src/net/api/api.c b/src/net/api/api.c index 5348c04..43634b8 100644 --- a/src/net/api/api.c +++ b/src/net/api/api.c @@ -39,27 +39,20 @@ // State: has the device been claimed already? static bool g_claimed = false; // Cache the last generated password so we can optionally re-display or debug -static char g_last_psk[33] = ""; // 32 chars + NUL +static char g_last_psk[33] = ""; // 32 chars + NUL static const uint32_t CLAIM_GRACE_MS = 2000; // TCP close + client reconnect time // Bearer token authentication state static char g_bearer_token[65] = ""; // 64 hex chars + NUL (256-bit token) static bool g_bearer_token_generated = false; -// Token pubkey caching/prefetch handled by crypt.c now - -// Forward declarations (reset_timer_cb removed if not used) - -// NOTE: Removed timer+task pattern. Now using background task (crypto_spawn_wifi_password_task) -// The write is queued via crypto_queue_wifi_password_write() directly from claim_handler - // Generate a random WPA2 passphrase (length between 16 and 24) using get_rand_32() static void generate_random_psk(char *out, size_t out_len) { static const char charset[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; size_t charset_len = sizeof(charset) - 1; if (out_len == 0) return; uint32_t seed = get_rand_32(); - size_t target = 16; // fixed length for now + size_t target = 16; if (target > out_len - 1) target = out_len - 1; for (size_t i = 0; i < target; i++) { // Mix hardware random each iteration for unpredictability @@ -72,7 +65,7 @@ static void generate_random_psk(char *out, size_t out_len) { // Generate a random bearer token (256-bit = 64 hex chars) static void generate_bearer_token(char *out, size_t out_len) { static const char HEX[] = "0123456789abcdef"; - if (out_len < 65) return; // Need at least 65 bytes (64 hex + NUL) + if (out_len < 65) return; for (int chunk = 0; chunk < 8; ++chunk) { uint32_t r = get_rand_32(); @@ -207,8 +200,7 @@ static void generate_token_handler(struct tcp_pcb *pcb, const char *request) { http_send_json(pcb, 200, body); } -// CPU utilization tracking handled inside cpu_monitor (runtime-only) - +// Lightweight ping endpoint for connectivity checks static void ping_handler(struct tcp_pcb *pcb, const char *request) { (void)request; http_send_json(pcb, 200, "{\"message\":\"pong\"}"); @@ -459,12 +451,9 @@ static void token_info_handler(struct tcp_pcb *pcb, const char *request){ * Returns: {"status":"accepted"} or {"error":"..."} */ static void set_host_pubkey_handler(struct tcp_pcb *pcb, const char *request) { - ////print_dbg("API: set_host_pubkey_handler called (non-blocking)\n"); - // Find the request body (after double CRLF) const char *body_start = strstr(request, "\r\n\r\n"); if (!body_start) { - ////print_dbg("API: missing request body\n"); http_send_json(pcb, 400, "{\"error\":\"missing_body\"}"); return; } @@ -478,10 +467,8 @@ static void set_host_pubkey_handler(struct tcp_pcb *pcb, const char *request) { // Create null-terminated string for the hex data size_t hex_len = body_end - body_start; - ////print_dbg("API: received hex data length: %zu\n", hex_len); if (hex_len != 128) { - ////print_dbg("API: invalid hex length, expected 128, got %zu\n", hex_len); char error_msg[100]; snprintf(error_msg, sizeof(error_msg), "{\"error\":\"invalid_length\",\"expected\":128,\"got\":%zu}", hex_len); http_send_json(pcb, 400, error_msg); @@ -492,32 +479,25 @@ static void set_host_pubkey_handler(struct tcp_pcb *pcb, const char *request) { for (size_t i = 0; i < 128; i++) { char c = body_start[i]; if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) { - ////print_dbg("API: invalid hex character at position %zu: '%c'\n", i, c); http_send_json(pcb, 400, "{\"error\":\"invalid_hex_format\"}"); return; } } - ////print_dbg("API: hex validation passed, creating null-terminated string\n"); - // Create null-terminated string for the crypto function char hex_str[129]; memcpy(hex_str, body_start, 128); hex_str[128] = '\0'; - - ////print_dbg("API: requesting non-blocking host pubkey write\n"); - + // Use the new non-blocking crypto function bool write_ready, write_failed; bool accepted = crypto_request_host_pubkey_write(hex_str, &write_ready, &write_failed); if (!accepted) { - ////print_dbg("API: Host pubkey write request rejected (already pending)\n"); http_send_json(pcb, 409, "{\"error\":\"write_pending\",\"retry_ms\":100}"); return; } - - ////print_dbg("API: Host pubkey write request accepted\n"); + http_send_json(pcb, 202, "{\"status\":\"accepted\",\"message\":\"write_queued\"}"); } @@ -528,8 +508,6 @@ static void set_host_pubkey_handler(struct tcp_pcb *pcb, const char *request) { */ static void get_host_pubkey_handler(struct tcp_pcb *pcb, const char *request) { (void)request; - ////print_dbg("API: get_host_pubkey_handler called (non-blocking)\n"); - const char *hex_pubkey = NULL; bool ready = false; bool failed = false; @@ -537,7 +515,6 @@ static void get_host_pubkey_handler(struct tcp_pcb *pcb, const char *request) { crypto_get_cached_host_pubkey_hex(&hex_pubkey, &ready, &failed); if (failed) { - ////print_dbg("API: Host pubkey read failed\n"); http_send_json(pcb, 500, "{\"error\":\"read_failed\"}"); return; } @@ -547,13 +524,11 @@ static void get_host_pubkey_handler(struct tcp_pcb *pcb, const char *request) { char body[180]; int n = snprintf(body, sizeof(body), "{\"host_pubkey\":\"%s\",\"cached\":true}", hex_pubkey); (void)n; - ////print_dbg("API: Returning cached host pubkey\n"); http_send_json(pcb, 200, body); return; } // Still reading - ////print_dbg("API: Host pubkey not ready yet\n"); http_send_json(pcb, 503, "{\"status\":\"reading\",\"retry_ms\":100}"); } @@ -564,8 +539,6 @@ static void get_host_pubkey_handler(struct tcp_pcb *pcb, const char *request) { */ static void host_pubkey_status_handler(struct tcp_pcb *pcb, const char *request) { (void)request; - ////print_dbg("API: host_pubkey_status_handler called\n"); - bool write_ready = false; bool write_failed = false; @@ -694,7 +667,7 @@ static void reset_timer_cb(TimerHandle_t timer) { "Reset", DEFAULT_STACK_SIZE, NULL, - 15, // Medium priority + 15, NULL ); @@ -707,7 +680,7 @@ static void reset_timer_cb(TimerHandle_t timer) { } static void reset_api_handler(struct tcp_pcb *pcb, const char *request) { - (void)request; // Unused parameter + (void)request; print_dbg("[API] reset_api_handler called\n"); @@ -779,7 +752,7 @@ void api_register_routes(void) { // STATE 1: No password and not claimed → Only claim endpoint (NO bearer token) if (!claimed_or_password_set) { - http_register("/api/claim", claim_handler); // NO AUTH + http_register("/api/claim", claim_handler); print_dbg("API: UNCLAIMED state - only /api/claim exposed (no auth)\n"); return; } @@ -817,7 +790,6 @@ void api_register_routes(void) { crypto_spawn_pubkey_prefetch(); crypto_spawn_host_pubkey_task(); crypto_spawn_golden_hash_task(); - // WiFi password task already spawned above (line 791) for all states // Expose token_info and reset in all builds (for university module) if (provisioned) { diff --git a/src/net/dhcp/dhcpserver.c b/src/net/dhcp/dhcpserver.c index 529695b..7e673cc 100644 --- a/src/net/dhcp/dhcpserver.c +++ b/src/net/dhcp/dhcpserver.c @@ -63,18 +63,18 @@ #define PORT_DHCP_SERVER (67) #define PORT_DHCP_CLIENT (68) -#define DEFAULT_LEASE_TIME_S (24 * 60 * 60) // in seconds +#define DEFAULT_LEASE_TIME_S (24 * 60 * 60) #define MAC_LEN (6) #define MAKE_IP4(a, b, c, d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) typedef struct { - uint8_t op; // message opcode - uint8_t htype; // hardware address type - uint8_t hlen; // hardware address length + uint8_t op; // message opcode + uint8_t htype; // hardware address type + uint8_t hlen; // hardware address length uint8_t hops; - uint32_t xid; // transaction id, chosen by client - uint16_t secs; // client seconds elapsed + uint32_t xid; // transaction id, chosen by client + uint16_t secs; // client seconds elapsed uint16_t flags; uint8_t ciaddr[4]; // client IP address uint8_t yiaddr[4]; // your IP address @@ -86,10 +86,8 @@ typedef struct { uint8_t options[312]; // optional parameters, variable, starts with magic } dhcp_msg_t; -// Convert lwIP ip_addr_t to 4 bytes in dotted order (a.b.c.d) into buf[0..3]. -// Use the ip4_addrX() helpers to avoid endianness issues (ip4_addr_get_u32 -// can be in host byte order on little-endian platforms). Using the helpers -// guarantees we extract the octets in the correct network (dotted) order. +/* Convert an lwIP IPv4 address to 4 octets (network/dotted order) +using ip4_addrX() to avoid endianness/host-order issues. */ static void ipaddr_to_bytes(const ip_addr_t *a, uint8_t *buf) { const ip4_addr_t *ip4 = ip_2_ip4((ip_addr_t *)a); buf[0] = ip4_addr1(ip4); @@ -110,7 +108,7 @@ static int dhcp_socket_new_dgram(struct udp_pcb **udp, void *cb_data, udp_recv_f // Register callback udp_recv(*udp, cb_udp_recv, (void *)cb_data); - return 0; // success + return 0; } static void dhcp_socket_free(struct udp_pcb **udp) { @@ -121,7 +119,6 @@ static void dhcp_socket_free(struct udp_pcb **udp) { } static int dhcp_socket_bind(struct udp_pcb **udp, uint16_t port) { - // TODO convert lwIP errors to errno return udp_bind(*udp, IP_ANY_TYPE, port); } @@ -220,7 +217,6 @@ static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, uint8_t *msgtype = opt_find(opt, DHCP_OPT_MSG_TYPE); if (msgtype == NULL) { - // A DHCP package without MSG_TYPE? goto ignore_request; } @@ -299,9 +295,9 @@ static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, ipaddr_to_bytes(&d->nm, tmp_ip); opt_write_n(&opt, DHCP_OPT_SUBNET_MASK, 4, tmp_ip); ipaddr_to_bytes(&d->ip, tmp_ip); - opt_write_n(&opt, DHCP_OPT_ROUTER, 4, tmp_ip); // aka gateway; can have multiple addresses + opt_write_n(&opt, DHCP_OPT_ROUTER, 4, tmp_ip); ipaddr_to_bytes(&d->ip, tmp_ip); - opt_write_n(&opt, DHCP_OPT_DNS, 4, tmp_ip); // this server is the dns + opt_write_n(&opt, DHCP_OPT_DNS, 4, tmp_ip); opt_write_u32(&opt, DHCP_OPT_IP_LEASE_TIME, DEFAULT_LEASE_TIME_S); *opt++ = DHCP_OPT_END; struct netif *nif = ip_current_input_netif(); diff --git a/src/net/http/http_server.c b/src/net/http/http_server.c index 998db7f..5d47327 100644 --- a/src/net/http/http_server.c +++ b/src/net/http/http_server.c @@ -1,6 +1,3 @@ -// Rebuilt HTTP server file: cleaned duplicates, minimal single-connection server -// with deferred close via tcp_sent to reduce intermittent curl failures. - #include "http_server.h" #include "lwip/pbuf.h" #include "lwip/tcp.h" @@ -13,7 +10,7 @@ struct route_entry { const char *path; http_handler_fn handler; - bool requires_auth; // Does this route require bearer token auth? + bool requires_auth; }; // Make internal state visible for unit tests @@ -24,7 +21,7 @@ static struct route_entry routes[MAX_ROUTES]; #endif int http_register(const char *path, http_handler_fn handler) { - return http_register_auth(path, handler, false); // Public by default + return http_register_auth(path, handler, false); } int http_register_auth(const char *path, http_handler_fn handler, bool requires_auth) { @@ -58,7 +55,6 @@ static void reset_state(void) { g_state.in_use = false; g_state.close_when_sent = false; - // Track connection closing http_connection_closed(); } @@ -145,11 +141,7 @@ static err_t http_close(struct tcp_pcb *pcb) { tcp_recv(pcb, NULL); tcp_sent(pcb, NULL); tcp_err(pcb, NULL); - - // Try to close the connection err_t close_err = tcp_close(pcb); - - // Only reset state if close succeeded OR if we need to abort if (close_err == ERR_OK) { reset_state(); return ERR_OK; diff --git a/src/net/wifi_ap.c b/src/net/wifi_ap.c index 273cab2..bdd5aa4 100644 --- a/src/net/wifi_ap.c +++ b/src/net/wifi_ap.c @@ -17,8 +17,8 @@ static char wifi_pass_storage[65] = ""; // will be filled on claim static wifi_ap_config_t wifi_config = { .ssid = "MASTR-Token", - .password = wifi_pass_storage, // pointer always kept to storage - .ip_address = 0xC0A80401, // 192.168.4.1 + .password = wifi_pass_storage, // pointer always kept to storage + .ip_address = 0xC0A80401, // 192.168.4.1 .is_running = false }; @@ -50,7 +50,7 @@ bool wifi_ap_start(const wifi_ap_config_t *config) { return false; } // Copy fundamental fields but deep-copy password text into persistent storage. - wifi_config.ssid = config->ssid; // assume lifetime static/const or managed by caller + wifi_config.ssid = config->ssid; if (config->password) { size_t len = strlen(config->password); if (len >= sizeof(wifi_pass_storage)) len = sizeof(wifi_pass_storage) - 1; @@ -160,7 +160,7 @@ void http_server_task(void *params) { // Only run if AP is configured if (!wifi_config.is_running) { print_dbg("HTTP server: AP not running, task exiting\n"); - vTaskDelete(NULL); // Delete self + vTaskDelete(NULL); return; }