176 lines
8.3 KiB
Markdown
176 lines
8.3 KiB
Markdown
# 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
|