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 §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/ — especially
docs/PINS.md and 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:
- Format the SD card as FAT32.
- 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. - 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 smallnsigner.offsetfile on the SD so the offset survives power cycles). - The
encrypt/decryptverbs 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
§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.
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).
Building and flashing
Requires ESP-IDF v5.x (tested with v5.4.2). The classic ESP32 target uses the
xtensa-esp-elf unified toolchain.
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:
- Open
examples/cyd_webserial_demo.htmlin Chrome/Edge. - Click Connect Web Serial, select the CH340 port (
1a86:7523). - On the CYD touchscreen, enter or generate a mnemonic to reach the "ready" state.
- 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_nip44encrypt+decrypt, andencrypt/decrypt(otp).
API
The CYD firmware speaks the same algorithm-based API as the host n_signer
(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/decryptresponse. 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. NoCONFIG_MBEDTLS_SHA3_Cor 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 thembedtls_ed25519_*functions. RequiresCONFIG_MBEDTLS_PSA_CRYPTO_C=y(set insdkconfig.defaults). - secp256k1: the vendored secp256k1 component (schnorr + ECDSA).
- PQ (ML-DSA-65, SLH-DSA-128s, ML-KEM-768): PQClean via the
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 and the
firmware/README.md CYD section for details.
Reference documentation
docs/— upstream CYD hardware docs (PINS, SETUP, TROUBLESHOOTING, ADDONS, etc.)plans/cyd_signer_port.md— original port plan (hardware comparison, architecture, UI flow)plans/cyd_algorithm_api_upgrade.md— v0.0.2 API upgrade planfirmware/README.md— shared firmware README (PQ crypto, mbedtls backend, feather target)README.md§4 — the authoritative n_signer API reference