# n_signer CYD Firmware (ESP32-2432S028) The **Cheap Yellow Display** (ESP32-2432S028) is a $15 ESP32-WROOM-32 board with a 2.8" 320×240 ILI9341 resistive-touch display, CH340 USB-UART bridge, and a Micro SD card slot. This firmware turns it into a hardware n_signer that speaks the same algorithm-based API as the host ([`README.md`](../../README.md) §4). **Firmware version:** 0.0.2 (algorithm-based API) ## Hardware summary | Concern | Value | |---|---| | MCU | ESP32-WROOM-32 (classic, dual-core Xtensa, 512 KB SRAM, no PSRAM) | | USB-UART | CH340 (`1a86:7523`) → `/dev/ttyUSB0` | | Flash | 4 MB | | Display | 2.8" 320×240 ILI9341 (HSPI: DC=IO2, CS=IO15, SCK=IO14, MOSI=IO13, MISO=IO12, BL=IO21) | | Touch | XPT2046 resistive (bit-banged SPI: CLK=IO25, MOSI=IO32, CS=IO33, MISO=IO39, IRQ=IO36) | | SD card | Micro SD, VSPI (CS=IO5, SCK=IO18, MISO=IO19, MOSI=IO23) | | RGB LED | R=IO4, G=IO16, B=IO17 (active LOW) | | LDR | IO34 | | Speaker | IO26 (DAC) | | BOOT button | IO0 | | GUI | LVGL 8.3 | For the full pin map, connectors (P1/P3/CN1), and add-ons, see the upstream hardware docs copied to [`docs/`](docs/) — especially [`docs/PINS.md`](docs/PINS.md) and [`docs/SETUP.md`](docs/SETUP.md). ## SD card — size limits and OTP pad storage The CYD's Micro SD slot is wired to VSPI (IO5/18/19/23). ESP-IDF drives it via the SDSPI host + FATFS filesystem. The proven bring-up example is `07_sd_card` in the `esp32_playground/cyb-esp32-2432s028/` workspace. **Size limits:** - **SDSC (≤ 2 GB):** supported. - **SDHC (2 GB – 32 GB):** supported — this is the recommended range. The Makerfabs CYD ships with a 16 GB card, which works. - **SDXC (> 32 GB):** **not supported** out of the box. SDXC cards ship formatted as exFAT, and ESP-IDF's FATFS does not include exFAT. An SDXC card reformatted to FAT32 will work up to 32 GB; beyond that, FAT32's 32 GB limit applies. For OTP pad storage, 32 GB is vastly more than enough (see below). **Recommendation:** use any **SDHC card from 4–32 GB** formatted **FAT32**. ### Using the SD card for the OTP pad The current v0.0.2 firmware derives the OTP pad from the mnemonic seed via HKDF-SHA256 into a 1024-byte in-RAM pad (no SD card required). This keeps the wire contract identical to the host's `encrypt`/`decrypt` (algorithm:"otp") verbs but limits the pad to 1024 bytes per session. To hold a **large OTP pad** (the original n_signer host design binds a pad file from `--otp-pad-dir`), the SD card is the right storage. The plan: 1. Format the SD card as FAT32. 2. Place a pad file (e.g. `nsigner.pad`) on it — any size up to the card's free space. A 1 GB pad gives ~1 billion one-time-pad bytes before exhaustion. 3. The firmware mounts the SD card at boot via `esp_vfs_fat_sdmmc_mount()` on the SDSPI host, opens the pad file, and reads pad bytes on demand into a small ring buffer, advancing a persistent offset (stored in a small `nsigner.offset` file on the SD so the offset survives power cycles). 4. The `encrypt`/`decrypt` verbs XOR against the SD-backed pad instead of the HKDF-derived in-RAM pad. This is a planned enhancement (see [`plans/cyd_algorithm_api_upgrade.md`](../../plans/cyd_algorithm_api_upgrade.md) §13 — the current implementation uses the mnemonic-derived pad as the embedded fallback). The SD card slot is confirmed working and the pin map is in [`docs/PINS.md`](docs/PINS.md). **Note on simultaneous display + touch + SD:** The CYD's display (HSPI), touch (bit-banged), and SD (VSPI) use three different SPI buses. All three can run at the same time — the touch is bit-banged precisely so it doesn't contend with the other two hardware SPI buses (see [`docs/TROUBLESHOOTING.md`](docs/TROUBLESHOOTING.md)). ## Building and flashing Requires ESP-IDF v5.x (tested with v5.4.2). The classic ESP32 target uses the `xtensa-esp-elf` unified toolchain. ```bash source /home/user/esp/esp-idf/export.sh cd firmware/cyd_esp32_2432s028 idf.py build idf.py -p /dev/ttyUSB0 flash ``` If flashing fails with `Wrong boot mode detected (0x13)`, see the serial-reset hardware note below. ## Validation — Web Serial The CYD has no native USB; the CH340 bridge exposes a serial port. The browser transport is **Web Serial** (`navigator.serial`), Chromium-only. A full test page covering every algorithm and verb lives at [`examples/cyd_webserial_demo.html`](../../examples/cyd_webserial_demo.html): 1. Open [`examples/cyd_webserial_demo.html`](../../examples/cyd_webserial_demo.html) in Chrome/Edge. 2. Click **Connect Web Serial**, select the CH340 port (`1a86:7523`). 3. On the CYD touchscreen, enter or generate a mnemonic to reach the "ready" state. 4. Exercise each card: `get_public_key` (all 6 algorithms), `sign`/`verify`, `encapsulate`/`decapsulate`, `derive_shared_secret`, `derive`, `nostr_get_public_key`, `nostr_sign_event`, `nostr_mine_event`, `nostr_nip04`/`nostr_nip44` encrypt+decrypt, and `encrypt`/`decrypt` (otp). ## API The CYD firmware speaks the same algorithm-based API as the host n_signer ([`README.md`](../../README.md) §4). Supported verbs: | Verb | Algorithms | |---|---| | `get_public_key` | secp256k1, ed25519, x25519, ml-dsa-65, slh-dsa-128s, ml-kem-768 | | `sign` / `verify` | secp256k1 (schnorr/ecdsa), ed25519, ml-dsa-65, slh-dsa-128s | | `encapsulate` / `decapsulate` | ml-kem-768 | | `derive_shared_secret` | x25519 | | `derive` | secp256k1 (HMAC-SHA256) | | `encrypt` / `decrypt` | otp | | `nostr_get_public_key` | secp256k1 (NIP-06) | | `nostr_sign_event` | secp256k1 (NIP-06) | | `nostr_mine_event` | secp256k1 (NIP-06, single-threaded PoW) | | `nostr_nip04_encrypt` / `decrypt` | secp256k1 (NIP-06) | | `nostr_nip44_encrypt` / `decrypt` | secp256k1 (NIP-06) | All requests require an auth envelope (kind 27235). The `key_id` in every structured result is the first 16 hex characters of the public key, matching the host. Invalid `(verb, algorithm)` pairs return error `1010`. ### Embedded-specific notes - **OTP pad:** derived from the mnemonic seed (HKDF-SHA256, 1024 bytes) in v0.0.2. The offset advances monotonically and is reported in every `encrypt`/`decrypt` response. SD-card-backed pad is a planned enhancement (see above). - **`nostr_mine_event`:** single-threaded, hard 30 s default timeout, shows a "mining…" screen. Keep difficulty low (≤ 8) on ESP32. - **SLH-DSA-128s:** keygen and signing take 5–30 s. The UI shows a "deriving key…" / "signing…" indicator. ML-DSA-65 is much faster (~100 ms) and is the recommended PQ signature algorithm for interactive use. ## Crypto backend - **SHA-256 / SHA-512:** mbedtls (ESP32 hardware accelerated). - **SHA3 / SHAKE-128 / SHAKE-256:** vendored Keccak-f[1600] (FIPS 202) in [`resources/pqclean/common/crypto_backend_mbedtls.c`](../../resources/pqclean/common/crypto_backend_mbedtls.c). No `CONFIG_MBEDTLS_SHA3_C` or SHAKE menuconfig dependency. - **ed25519 / x25519:** PSA Crypto API (`psa_import_key`, `psa_sign_message`, `psa_raw_key_agreement`, etc.) — ESP-IDF v5.x mbedtls removed the `mbedtls_ed25519_*` functions. Requires `CONFIG_MBEDTLS_PSA_CRYPTO_C=y` (set in [`sdkconfig.defaults`](sdkconfig.defaults)). - **secp256k1:** the vendored secp256k1 component (schnorr + ECDSA). - **PQ (ML-DSA-65, SLH-DSA-128s, ML-KEM-768):** PQClean via the [`components/pqclean/`](components/pqclean/) component. ## Serial-reset hardware note (CH340 auto-reset) Opening `/dev/ttyUSB0` can reset the ESP32 because the CH340's DTR/RTS lines are wired into the ESP32 auto-reset circuit. Symptoms: the device returns to the startup menu when a host app opens the serial port. **Mitigation:** add a **10 µF capacitor between EN and GND** on the CYD board (negative leg to GND). Typical working range is 4.7–22 µF. This also fixes the `Wrong boot mode detected (0x13)` flashing error. See [`docs/TROUBLESHOOTING.md`](docs/TROUBLESHOOTING.md) and the [`firmware/README.md`](../README.md) CYD section for details. ## Reference documentation - [`docs/`](docs/) — upstream CYD hardware docs (PINS, SETUP, TROUBLESHOOTING, ADDONS, etc.) - [`plans/cyd_signer_port.md`](../../plans/cyd_signer_port.md) — original port plan (hardware comparison, architecture, UI flow) - [`plans/cyd_algorithm_api_upgrade.md`](../../plans/cyd_algorithm_api_upgrade.md) — v0.0.2 API upgrade plan - [`firmware/README.md`](../README.md) — shared firmware README (PQ crypto, mbedtls backend, feather target) - [`README.md`](../../README.md) §4 — the authoritative n_signer API reference