|
cryptnox-pos 1.0.0
Standalone USDC payment terminal firmware (ESP32 + Cryptnox smart card)
|
The config portal: one web app for setting a terminal up and for administering it afterwards, in two modes. More...
Go to the source code of this file.
Macros | |
| #define | PROV_WINDOW_MIN 15U |
| How long the portal stays up before closing itself, minutes. | |
Enumerations | |
| enum | prov_mode_t { PROV_MODE_OFF , PROV_MODE_WIZARD , PROV_MODE_ADMIN } |
| Which of the two portals is running. More... | |
| enum | prov_step_t { PROV_STEP_IDLE , PROV_STEP_AUTH , PROV_STEP_ADDR , PROV_STEP_WIFI , PROV_STEP_DONE , PROV_STEP_ADMIN } |
| Where the wizard has got to. More... | |
| enum | prov_ask_t { PROV_ASK_NONE , PROV_ASK_PAYOUT_ETH , PROV_ASK_PAYOUT_TRON , PROV_ASK_CONTRACT_ETH , PROV_ASK_CONTRACT_TRON } |
| What the panel is being asked to accept, for prov_pending. More... | |
Functions | |
| bool | prov_start (prov_mode_t mode, ui_event_cb_t cb) |
| Raise the portal. | |
| void | prov_stop (void) |
| Stop the portal, drop the AP, and withdraw anything unaccepted. | |
| prov_mode_t | prov_mode (void) |
| Which mode is running, or PROV_MODE_OFF. | |
| void | prov_set_step (prov_step_t step) |
| Tell the portal which wizard step is current. | |
| prov_step_t | prov_step (void) |
| The current step. | |
| unsigned | prov_window_left_min (void) |
| Minutes left before the portal closes itself, 0 once it has. | |
| const char * | prov_ap_ssid (void) |
| AP SSID, or "" while no portal is up. | |
| const char * | prov_ap_pass (void) |
| AP passphrase, or "" while no portal is up. | |
| const char * | prov_qr_payload (void) |
| What the panel's QR code should carry. | |
| bool | prov_auth_pending (void) |
| Whether a browser has asked to be authorised. | |
| void | prov_auth_resolve (bool grant) |
| Answer a pending authorisation request from the panel. | |
| bool | prov_authed (void) |
| Whether the browser session is authorised to change anything. | |
| bool | prov_pair_code (char *out, size_t out_size) |
| The 4-digit pairing code of the browser asking to be let in. | |
| void | prov_set_wifi_only (void) |
| Cut the wizard down to the Wi-Fi step, with no admin code. | |
| bool | prov_wifi_only (void) |
| Whether the portal is the cut-down Wi-Fi-only flow. | |
| void | prov_set_note (const char *note) |
| Put a one-line message on the page. | |
| void | prov_set_scan (const net_wifi_ap_t *aps, uint16_t n) |
| Hand the portal a Wi-Fi scan for the browser to choose from. | |
| bool | prov_pending (prov_ask_t *kind, char *label, size_t label_n, char *value, size_t value_n) |
| Fetch the value a browser proposed but nobody has accepted. | |
| bool | prov_pending_commit (bool accept) |
| Resolve a pending proposal from the panel. | |
| bool | prov_propose (prov_ask_t kind, const char *addr) |
| Propose a value on behalf of the panel itself. | |
The config portal: one web app for setting a terminal up and for administering it afterwards, in two modes.
Typing a 42-character recipient address on a 240x320 resistive panel is the worst part of setting one of these up, and telling an operator to type an IP address is only marginally better. Scanning a QR code should land them on the form.
Both modes raise a WPA2 SoftAP with a fresh per-session passphrase and run a captive portal on it: a DNS responder answers every lookup with the AP's address, and the HTTP server deliberately fails each OS's connectivity probe so the phone concludes the network is captive and opens the browser by itself. Miss either half and the phone joins in silence. The QR code on the panel carries "WIFI:T:WPA;S:...;P:...;;", which both iOS and Android cameras join from directly — so there is never a URL to type.
The AP is the only interface while a portal is up: net_ap_start drops the station association and net_ap_stop restores it. That is not tidiness. esp_http_server binds every interface and offers no bind address, so a portal running beside a station association also answers the venue LAN — where every device holding the venue PSK can reach the payout forms, and where the perimeter this module actually relies on (a passphrase on the panel, in front of the person asking) means nothing at all. So there is no remote administration here, by construction: you are in front of the terminal or you are nowhere.
What that buys, besides the smaller attack surface, is one transport instead of two — no TLS server, no self-signed identity in NVS, no certificate warning to explain on a panel, and no second code path through the same forms.
PROV_MODE_WIZARD — a blank terminal, or one whose saved network has become unreachable. Walks the setup steps one at a time and stays up until setup ends.
PROV_MODE_ADMIN — a configured terminal, opened from behind the admin code in the settings menu. Serves everything at once and closes itself after PROV_WINDOW_MIN, because it has taken a working terminal off its network to do this.
The browser has no admin-code field. It asks to be let in, and the terminal shows its admin-code screen; the operator types the code on the panel, and the portal session becomes authorised. So the code never crosses the network in either direction, on either mode's transport, and a browser that reaches the page without the terminal in reach can do nothing at all.
One exception, and it is the wizard's alone: prov_set_wifi_only, the flow a configured terminal gets when all it has lost is its network. See there.
Everything that changes where money goes goes further than that: a browser may propose a payout address or a token contract, and the value is then displayed on the panel for somebody to accept there. Nothing is stored until they do. Firmware follows the same rule — an upload is verified and staged, and only an on-screen accept makes it bootable.
Not an oversight:
Definition in file provision.h.
| #define PROV_WINDOW_MIN 15U |
How long the portal stays up before closing itself, minutes.
Definition at line 101 of file provision.h.
Referenced by prov_start().
| enum prov_ask_t |
What the panel is being asked to accept, for prov_pending.
Definition at line 128 of file provision.h.
| enum prov_mode_t |
Which of the two portals is running.
| Enumerator | |
|---|---|
| PROV_MODE_OFF | Not running. |
| PROV_MODE_WIZARD | Setup: one step at a time, no self-close. |
| PROV_MODE_ADMIN | Administration: everything at once, times out. |
Definition at line 104 of file provision.h.
| enum prov_step_t |
Where the wizard has got to.
The order is the flow: create the admin code on the panel, show the QR code, have the browser authorised from the panel, set the payout addresses, join the venue network, done. Only the wizard walks these; admin mode sits on PROV_STEP_ADMIN and serves everything at once.
Definition at line 118 of file provision.h.
| const char * prov_ap_pass | ( | void | ) |
AP passphrase, or "" while no portal is up.
Definition at line 1452 of file provision.cpp.
References s_pass.
Referenced by build_prov(), and open_portal_window().
| const char * prov_ap_ssid | ( | void | ) |
AP SSID, or "" while no portal is up.
Definition at line 1451 of file provision.cpp.
References s_ssid.
Referenced by build_prov(), and open_portal_window().
| bool prov_auth_pending | ( | void | ) |
Whether a browser has asked to be authorised.
Set by POST /api/auth. The panel answers by taking the admin code and calling prov_auth_resolve. Reported as UI_EVENT_PROV_AUTH.
Definition at line 1455 of file provision.cpp.
References s_auth_pending.
| void prov_auth_resolve | ( | bool | grant | ) |
Answer a pending authorisation request from the panel.
| [in] | grant | true if the operator entered the correct admin code. |
Definition at line 1457 of file provision.cpp.
References s_auth_pending, s_authed, s_cb, s_token, TAG, and UI_EVENT_PROV_NEXT.
Referenced by admin_submit(), and btn_event_cb().
| bool prov_authed | ( | void | ) |
Whether the browser session is authorised to change anything.
Definition at line 1475 of file provision.cpp.
References s_authed.
Referenced by build_prov(), and run_wizard().
| prov_mode_t prov_mode | ( | void | ) |
Which mode is running, or PROV_MODE_OFF.
Definition at line 1436 of file provision.cpp.
References s_mode.
Referenced by admin_submit(), app_main(), btn_event_cb(), run_wizard(), and ui_task().
| bool prov_pair_code | ( | char * | out, |
| size_t | out_size ) |
The 4-digit pairing code of the browser asking to be let in.
Derived from the session token (its first 16 bits, mod 10000), so the page can compute the same number from the token it holds. Shown on the panel's admin prompt and on the page, so the operator approves the browser in their hand and not whichever one on the access point asked first.
| [out] | out | NUL-terminated code on success. |
| [in] | out_size | >= 5. |
Definition at line 1477 of file provision.cpp.
References s_token.
Referenced by build_admin_unlock().
| bool prov_pending | ( | prov_ask_t * | kind, |
| char * | label, | ||
| size_t | label_n, | ||
| char * | value, | ||
| size_t | value_n ) |
Fetch the value a browser proposed but nobody has accepted.
| [out] | kind | What is being asked, may be NULL. |
| [out] | label | Human name for the panel ("Ethereum payout"), may be NULL. |
| [in] | label_n | Capacity of label. |
| [out] | value | The proposed address, may be NULL. |
| [in] | value_n | Capacity of value. |
Definition at line 1547 of file provision.cpp.
References ask_label(), PROV_ASK_NONE, s_ask, s_ask_lock, and s_ask_val.
Referenced by build_prov_confirm(), and state_get().
| bool prov_pending_commit | ( | bool | accept | ) |
Resolve a pending proposal from the panel.
| [in] | accept | true to commit it to NVS, false to discard it. |
Definition at line 1569 of file provision.cpp.
References POS_CHAIN_ETH_USDC, POS_CHAIN_TRON_USDT, PROV_ASK_CONTRACT_ETH, PROV_ASK_CONTRACT_TRON, PROV_ASK_NONE, PROV_ASK_PAYOUT_ETH, PROV_ASK_PAYOUT_TRON, s_ask, s_ask_lock, s_ask_val, s_cb, settings_set_contract(), settings_set_payout(), UI_EVENT_PROV_VALUE_NO, and UI_EVENT_PROV_VALUE_SET.
Referenced by btn_event_cb().
| bool prov_propose | ( | prov_ask_t | kind, |
| const char * | addr ) |
Propose a value on behalf of the panel itself.
Used by the card-derived route: the terminal reads an address off a Cryptnox card, then puts it through the very same accept-on-the-panel handshake a browser submission goes through, so there is one code path that stores an address and one screen that approves one.
| [in] | kind | What the value is. |
| [in] | addr | The address; checked by the caller. |
Definition at line 1527 of file provision.cpp.
References ask_label(), PROV_ASK_NONE, s_ask, s_ask_lock, s_ask_val, s_cb, TAG, and UI_EVENT_PROV_VALUE.
Referenced by app_main(), run_wizard(), and value_post().
| const char * prov_qr_payload | ( | void | ) |
What the panel's QR code should carry.
"WIFI:T:WPA;S:<ssid>;P:<pass>;;" — a camera joins the AP from it and the captive portal takes over, so one code is enough and there is no URL to read off the panel. Empty while no portal is up.
Definition at line 1453 of file provision.cpp.
References s_qr.
Referenced by build_prov(), and open_portal_window().
| void prov_set_note | ( | const char * | note | ) |
Put a one-line message on the page.
For the things only the device can know — a Wi-Fi network that would not join, a step that cannot be left yet. The browser is where the operator is looking, so that is where the reason has to appear; the panel is showing a QR code.
| [in] | note | Message, copied. NULL or "" clears it. |
Definition at line 1493 of file provision.cpp.
References s_note, and s_share_mux.
Referenced by app_main(), and run_wizard().
| void prov_set_scan | ( | const net_wifi_ap_t * | aps, |
| uint16_t | n ) |
Hand the portal a Wi-Fi scan for the browser to choose from.
The scan itself stays on the main task — scanning makes the radio hop channels, which briefly drops anyone joined to the SoftAP, so it happens deliberately at known moments (entering the Wi-Fi step, or a rescan the browser asked for) and never inside an HTTP handler.
| [in] | aps | Scanned networks; copied. |
| [in] | n | How many. |
Definition at line 1516 of file provision.cpp.
References PROV_MAX_APS, s_ap_count, s_aps, s_scan_gen, and s_share_mux.
Referenced by app_main(), and run_wizard().
| void prov_set_step | ( | prov_step_t | step | ) |
Tell the portal which wizard step is current.
Definition at line 1438 of file provision.cpp.
References s_step.
Referenced by run_wizard().
| void prov_set_wifi_only | ( | void | ) |
Cut the wizard down to the Wi-Fi step, with no admin code.
For a terminal that is already configured and has only lost its network. Call it right after prov_start (which clears it) and before any browser arrives; the first browser to ask is then let in without the panel demanding the code, and the panel drops the step numbering, since steps 1 and 3-4 do not happen.
The relaxation is bounded: the perimeter here is the AP's per-device passphrase, which is on the panel in front of whoever is asking, and everything that decides where money goes still has to be accepted on that panel. What it buys is an operator whose till has moved venues typing a password instead of walking three screens to be allowed to.
Definition at line 1489 of file provision.cpp.
References s_wifi_only.
Referenced by run_wizard().
| bool prov_start | ( | prov_mode_t | mode, |
| ui_event_cb_t | cb ) |
Raise the portal.
Idempotent for the same mode; a different mode is refused rather than silently switched, since the two serve different steps to the same page. Stop it first.
Raising the portal takes the terminal off its network for the duration (see above); prov_stop puts it back.
A new AP passphrase is drawn on every call and never stored: it is shown on a screen a customer can see, so a photograph of it must not still open the wifi_only portal — which asks for no admin code — weeks later. prov_stop() wipes it, and a reboot mid-setup simply shows the next one.
| [in] | mode | Which portal to run. |
| [in] | cb | Where submissions are reported; the same callback the UI task uses, so a form and a screen tap are indistinguishable to the main task. Must outlive the call. |
Definition at line 1221 of file provision.cpp.
References ap_pass_load(), ap_ssid_build(), dns_task(), net_ap_start(), net_ap_stop(), net_wifi_init(), PORTAL_URL, PROV_MODE_ADMIN, PROV_MODE_OFF, PROV_MODE_WIZARD, PROV_STEP_ADMIN, PROV_STEP_AUTH, PROV_WINDOW_MIN, register_handlers(), s_ask_lock, s_auth_pending, s_authed, s_cb, s_deadline_us, s_dns_run, s_dns_task, s_httpd, s_mode, s_pass, s_qr, s_ssid, s_step, s_token, s_wifi_only, and TAG.
Referenced by open_portal_window(), and run_wizard().
| prov_step_t prov_step | ( | void | ) |
The current step.
Definition at line 1440 of file provision.cpp.
References s_step.
Referenced by run_wizard().
| void prov_stop | ( | void | ) |
Stop the portal, drop the AP, and withdraw anything unaccepted.
Also re-joins the network the AP displaced, so a configured terminal is back online when the page closes rather than at the next reboot.
Definition at line 1374 of file provision.cpp.
References net_ap_stop(), ota_abort(), PROV_ASK_NONE, PROV_MODE_OFF, PROV_STEP_IDLE, s_ask, s_ask_lock, s_ask_val, s_auth_pending, s_authed, s_deadline_us, s_dns_run, s_dns_task, s_httpd, s_mode, s_pass, s_qr, s_step, s_token, s_wifi_only, and TAG.
Referenced by app_main(), and run_wizard().
| bool prov_wifi_only | ( | void | ) |
Whether the portal is the cut-down Wi-Fi-only flow.
Definition at line 1491 of file provision.cpp.
References s_wifi_only.
Referenced by build_prov().
| unsigned prov_window_left_min | ( | void | ) |
Minutes left before the portal closes itself, 0 once it has.
Definition at line 1442 of file provision.cpp.
References s_deadline_us, and s_httpd.
Referenced by open_portal_window(), state_get(), and ui_task().