193 lines
10 KiB
Markdown
193 lines
10 KiB
Markdown
# n_signer FPGA Signing Core
|
|
|
|
**Status:** Concept — brainstorming. No plan yet.
|
|
|
|
An FPGA-based secp256k1 signing core that provides the **maximum physical
|
|
security** possible for Nostr signing: constant-time crypto (no instruction
|
|
timing leakage), key material in FPGA fabric (no bus access to the key), and no
|
|
firmware (no malware injection surface). The FPGA is a **signing oracle** — a
|
|
small, auditable hardware module that does one thing (secp256k1 schnorr/ECDSA
|
|
signing) with side-channel resistance that software on an MCU cannot match.
|
|
|
|
## The secure element gap
|
|
|
|
Commercial secure elements (NXP JCOP, Infineon, Microchip ATECC) support NIST
|
|
curves (P-256, P-384) and RSA, but **not secp256k1** — the smart card industry
|
|
standardized on NIST curves, and secp256k1 was treated as a "Bitcoin curve"
|
|
that didn't get hardware support. This is why every Nostr/Bitcoin hardware
|
|
wallet (Coldcard, Ledger, Trezor, Keystone) uses a **general-purpose MCU**
|
|
running software secp256k1, not a secure element.
|
|
|
|
An FPGA fills this gap: it gives us **hardware-level secp256k1** without relying
|
|
on a secure element vendor to support the curve. We write the secp256k1 core
|
|
ourselves in Verilog, with full control over the timing, the key storage, and
|
|
the side-channel resistance.
|
|
|
|
## Why an FPGA
|
|
|
|
| Property | MCU (software) | FPGA (hardware) |
|
|
|---|---|---|
|
|
| Timing leakage | Branch prediction, cache, instruction timing | **None** — fixed datapath, every op takes the same cycles |
|
|
| Key storage | RAM (accessible via bus/debug) | **FPGA fabric / BRAM** (no external bus access) |
|
|
| Firmware attacks | OS, USB stack, BT stack = injection surface | **No firmware** — bitstream is the entire program |
|
|
| Debug access | JTAG/SWD can read RAM | **No debug path to key** if not routed |
|
|
| Auditability | Large codebase (thousands of lines of C) | **Small Verilog core** (~2000 lines, auditable) |
|
|
| PQ crypto | Yes (software) | No (too complex for FPGA) |
|
|
|
|
## Architecture: hybrid FPGA + MCU
|
|
|
|
The practical design is a **two-chip hybrid**: the FPGA is the signing oracle,
|
|
the MCU handles the protocol/UI/transport. The MCU sends a message hash + key
|
|
index to the FPGA over SPI; the FPGA signs with the key in fabric; the FPGA
|
|
returns the 64-byte signature. The private key never leaves the FPGA.
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
subgraph Host_Side
|
|
Host[Host: laptop/phone<br/>n_signer client]
|
|
end
|
|
subgraph Signer_Device
|
|
MCU[MCU: RP2040 or nRF52840<br/>protocol + UI + transport<br/>PQ crypto in software]
|
|
FPGA[FPGA: iCE40-UP5K<br/>secp256k1 signing core<br/>ed25519 signing core<br/>SHA-256/512 cores<br/>key in BRAM]
|
|
Display[OLED / e-paper display]
|
|
Buttons[approve / deny buttons]
|
|
end
|
|
Host -->|USB / IR / NFC / BLE| MCU
|
|
MCU -->|SPI: msg_hash + key_index| FPGA
|
|
FPGA -->|SPI: 64-byte signature| MCU
|
|
MCU --> Display
|
|
Buttons --> MCU
|
|
MCU -->|response| Host
|
|
```
|
|
|
|
### Division of labor
|
|
|
|
| Function | Chip | Notes |
|
|
|---|---|---|
|
|
| Transport (USB/IR/NFC/BLE) | MCU | Ported from CYD/Teensy firmware |
|
|
| JSON-RPC dispatch | MCU | Ported from `handle_request()` |
|
|
| Auth envelope verify | MCU | secp256k1 schnorr verify (software) |
|
|
| Approval UI (display + buttons) | MCU | Ported from CYD UI |
|
|
| Mnemonic entry | MCU | BIP-39 wordlist + entry UI |
|
|
| BIP-32 / SLIP-0010 key derivation | **FPGA** | SHA-512 HMAC core + derivation FSM |
|
|
| secp256k1 schnorr sign | **FPGA** | Constant-time scalar multiply + schnorr |
|
|
| secp256k1 ECDSA sign | **FPGA** | Same scalar multiply + RFC 6979 nonce |
|
|
| ed25519 sign | **FPGA** | Curve25519 arithmetic core |
|
|
| x25519 ECDH | **FPGA** | Same curve as ed25519 |
|
|
| SHA-256 | **FPGA** | Hardware core (~1000 LUTs) |
|
|
| SHA-512 | **FPGA** | Hardware core (~2000 LUTs) — needed for BIP-32 |
|
|
| HMAC-SHA-256 | **FPGA** | SHA-256 core + FSM — for the `derive` verb |
|
|
| NIP-04 / NIP-44 encryption | MCU | AES + ChaCha20 in software |
|
|
| ML-DSA-65 / SLH-DSA-128s / ML-KEM-768 | MCU | PQClean in software (too complex for FPGA) |
|
|
| nostr_mine_event (PoW) | MCU | SHA-256 hash loop in software (or offload to FPGA) |
|
|
|
|
### Key storage in the FPGA
|
|
|
|
The mnemonic seed (64 bytes) is loaded into the FPGA's BRAM at boot (sent by
|
|
the MCU after the user enters the mnemonic). The FPGA derives secp256k1/ed25519/
|
|
x25519 keys on demand using its SHA-512 + modular arithmetic cores. The derived
|
|
private keys live in FPGA registers/BRAM and are **never readable from the SPI
|
|
interface** — the SPI interface only accepts "sign this hash with key index N"
|
|
commands and returns signatures. There is no "read key" command.
|
|
|
|
This is the key security property: **the private key is physically unreachable
|
|
from any external interface.** On an MCU, the key is in RAM and can be read via
|
|
JTAG/SWD or a firmware exploit. On the FPGA, the key is in fabric and there is
|
|
no path to it.
|
|
|
|
## FPGA board options
|
|
|
|
| Board | FPGA | LUTs | Toolchain | Price | Notes |
|
|
|---|---|---|---|---|---|
|
|
| **iCE40-UP5K** (e.g. iCEBreaker, Fomu) | iCE40UP5K | 5,300 | **Yosys + nextpnr** (open-source) | ~$15-20 | Best DIY choice. Open-source toolchain, DSP blocks, 128 KB BRAM, SPI flash. |
|
|
| **Gowin Tang Nano 9K** | GW1NR-9 | 8,640 | Yosys + nextpnr (open-source) | ~$8 | Cheapest. Newer open-source support. |
|
|
| **Lattice ECP5** (OrangeCrab, ULX3S) | LFE5U-12F / 25F / 45F | 12K-45K | Yosys + nextpnr (open-source) | ~$30-50 | More room. Supports **bitstream encryption** (important for production). |
|
|
| **Xilinx Artix-7** (Arty A7) | XC7A35T | 33,280 | Vivado (proprietary) | ~$100 | Professional. Overkill for a signer. |
|
|
|
|
**Recommendation: Lattice iCE40-UP5K for prototyping, ECP5 for production.**
|
|
The iCE40 has the most mature open-source toolchain (Yosys + nextpnr) and is
|
|
cheap. The ECP5 adds bitstream encryption (prevents bitstream cloning) for a
|
|
production device.
|
|
|
|
## The secp256k1 core (the hard part)
|
|
|
|
The secp256k1 signing core is the main development effort. It needs:
|
|
|
|
1. **256-bit modular arithmetic** over the secp256k1 prime field (p = 2²⁵⁶ - 2³² - 977):
|
|
- Modular add, subtract, multiply (Montgomery multiplication for performance)
|
|
- Modular inversion (Fermat's little theorem: a^(p-2) mod p, or extended Euclidean)
|
|
2. **Point operations** on the secp256k1 curve (y² = x³ + 7):
|
|
- Point addition (Jacobian coordinates)
|
|
- Point doubling
|
|
- Scalar multiplication (constant-time double-and-add, no conditional branches)
|
|
3. **Schnorr sign** (BIP-340):
|
|
- Deterministic nonce: k = HMAC-SHA256(d, x) where d is the key, x is the message hash
|
|
- R = k·G (point multiplication)
|
|
- e = tagged hash(R.x || P || m) (SHA-256)
|
|
- s = (k + e·x) mod n (scalar multiply + modular add)
|
|
- Signature = (R.x, s)
|
|
4. **ECDSA sign** (for the `scheme:"ecdsa"` option):
|
|
- RFC 6979 deterministic nonce: k = HMAC-SHA256(x, m) with rejection sampling
|
|
- R = k·G
|
|
- r = R.x mod n
|
|
- s = k⁻¹ · (m + r·x) mod n
|
|
- Signature = (r, s)
|
|
|
|
**Estimated size:** ~2000-5000 LUTs for the modular arithmetic + point
|
|
multiplication + schnorr/ECDSA FSM. Fits comfortably in an iCE40-UP5K (5,300
|
|
LUTs) alongside the SHA-256/512 cores and the SPI interface.
|
|
|
|
**Estimated sign time:** ~1-5 ms at 12 MHz (iCE40-UP5K typical clock). The
|
|
bottleneck is the 256-bit modular multiplication (~0.5-2 ms per multiply, ~256
|
|
multiplies per scalar multiplication). This is **much faster than software**
|
|
(the CYD's software schnorr sign takes ~10-50 ms).
|
|
|
|
## Open questions
|
|
|
|
- **Pure FPGA vs hybrid?** A pure-FPGA signer (no MCU) would implement the
|
|
entire dispatch + transport + UI in Verilog. This is extremely secure but
|
|
very hard to build (JSON parsing in Verilog is painful). The hybrid (FPGA
|
|
signing oracle + MCU protocol) is practical and still gives the key-isolation
|
|
benefit. **Lean toward hybrid.**
|
|
- **Which MCU?** RP2040 (cheap, no WiFi) or nRF52840 (low power, NFC, BLE)?
|
|
This determines the transport options (USB, IR, NFC, BLE).
|
|
- **Bitstream security:** iCE40 doesn't support bitstream encryption. If an
|
|
attacker reads the SPI flash, they get the bitstream (but not the key — the
|
|
key is loaded at runtime by the MCU, not stored in the bitstream). For
|
|
production, use ECP5 with bitstream encryption.
|
|
- **Key loading:** the MCU sends the mnemonic seed to the FPGA at boot over
|
|
SPI. Is this SPI transfer vulnerable to sniffing? It happens once, at boot,
|
|
inside the device. If the device is physically sealed, the SPI bus is not
|
|
accessible. For higher security, the FPGA could derive the key from the
|
|
mnemonic internally (the MCU sends the mnemonic string, the FPGA does
|
|
PBKDF2-HMAC-SHA512 + BIP-32 derivation in hardware).
|
|
- **ed25519 core:** worth implementing, or secp256k1-only? ed25519 is the same
|
|
field size (256-bit) but a different curve (Curve25519 vs secp256k1). The
|
|
modular arithmetic is similar but the curve operations differ. Adding
|
|
ed25519 roughly doubles the core size.
|
|
- **Open-source secp256k1 FPGA cores:** are there existing Verilog secp256k1
|
|
cores we can reuse or adapt? (There are Bitcoin mining cores, but those do
|
|
double-SHA256, not ECDSA. Academic ECDSA-on-FPGA papers exist but the code
|
|
is rarely open-sourced. We'd likely write the core from scratch.)
|
|
|
|
## Comparison to the other concepts
|
|
|
|
| | FPGA signer (hybrid) | MCU-only (CYD/Teensy) | Secure element |
|
|
|---|---|---|---|
|
|
| secp256k1 side-channel resistance | **Best** (constant-time, key in fabric) | Medium (software, timing leakage) | N/A (no secp256k1 support) |
|
|
| Key isolation | **Best** (no bus access to key) | Low (RAM, JTAG/SWD accessible) | Best (tamper-resistant) |
|
|
| Firmware attack surface | **Minimal** (no firmware on FPGA) | Large (OS, USB, BT stacks) | Minimal (fixed function) |
|
|
| PQ crypto | On MCU (software) | On MCU (software) | N/A |
|
|
| Development effort | **High** (Verilog secp256k1 core) | Low (port existing C code) | High (NDA + Java Card) |
|
|
| Cost | ~$15 FPGA + ~$4 MCU = ~$20 | ~$4-27 (MCU only) | ~$3-5 (chip only) |
|
|
| Auditability | **High** (small Verilog core, open toolchain) | Medium (large C codebase) | Low (proprietary, NDA) |
|
|
|
|
## Next steps
|
|
|
|
- Decide: hybrid (FPGA + MCU) vs pure FPGA
|
|
- Decide: iCE40-UP5K (prototype) vs ECP5 (production)
|
|
- Decide: secp256k1-only vs secp256k1 + ed25519
|
|
- Survey existing open-source secp256k1 / ECDSA FPGA cores
|
|
- Write a Verilog secp256k1 modular arithmetic core (the foundation)
|
|
- Write a plan (similar to [`plans/teensy41_signer_port.md`](../../plans/teensy41_signer_port.md))
|