
Inicio de sesión por huella dactilar en el escritorio de Linux mediante un sensor Grow R503 + Arduino + un daemon de reemplazo de fprintd en Rust
Un lector USB de huellas dactilares para escritorios Linux construido a partir de componentes. Coste total de las piezas inferior a 15 $. Sustituto directo del fprintd oficial: PAM, Ajustes de KDE, Ajustes de GNOME, fprintd-verify, sudo con el dedo y desbloqueo de pantalla con el dedo, todo funciona.
A partir de fw=1.0 / r503d 1.0.0 el cable Arduino↔host está autenticado: cada comando y respuesta lleva un MAC SipHash-2-4 con clave asociada a un secreto emparejado por TOFU en la EEPROM. Los ataques de reproducción y de intercambio en caliente contra el enlace serie USB quedan bloqueados. Consulta SPEC.md §13 para ver el diseño completo, incluido lo que el modelo de amenazas no cubre.

ojalá tuviera una impresora 3D…``` ┌──────────┐ UART ┌─────────────┐ USB-CDC ┌──────────────────┐ │ Grow │ 57600 8N1│ Arduino │ /dev/r503 │ r503d daemon │ │ R503 │◀─────────▶│ (firmware) │◀──────────▶│ net.reactivated │ │ sensor │ 3.3V TTL │ │ framed, │ .Fprint on D-Bus│ └──────────┘ └─────────────┘ MAC'd └──────────────────┘ │ ▼ PAM, KDE, GNOME, fprintd-verify, …
## Por qué
Los lectores de huellas dactilares USB para Linux son escasos, caros y los
que existen (Validity, Synaptics, etc.) se basan en controladores libfprint
inestables creados mediante ingeniería inversa que se rompen con las
actualizaciones de firmware del proveedor. El protocolo del Grow R503 es **público**,
el lado de Arduino es tu propio código, y la capa de compatibilidad libfprint es solo D-Bus.
También terminas con un lector de huellas dactilares cuyo código fuente puedes leer de arriba
a abajo.
## Lista de materiales
| Pieza | Notas | Coste aprox. |
|------|-------|------|
| Sensor de huellas dactilares capacitivo Grow R503 | El redondo con el anillo RGB | ~$10 |
| Placa Arduino Uno R3 / Nano / Mega / cualquier placa ATmega328 | Cualquier placa que ejecute SoftwareSerial | $5–$25 |
| 4–6 cables puente | Dupont / placa de pruebas | trivial |
Eso es todo. **Sin cambiador de nivel, sin divisor de voltaje** — consulta [`SPEC.md` §3.1](https://github.com/matpb/linux-fingerprint-r503/blob/main/SPEC.md)
para saber por qué (la línea RX del R503 tolera 5V en la práctica; la ficha técnica miente).
## Conexiones```
R503 Arduino (Uno R3 / Nano / etc.)
---- ------------------------------
Red (VCC) 3V3
White (3.3VT) 3V3 (touch-IC supply; shares rail with red)
Black (GND) GND
Yellow (TXD) D2 ── SoftwareSerial RX
Brown (RXD) D3 ── SoftwareSerial TX (direct — no divider!)
Blue (WAKEUP) D4 (optional; not used by firmware yet)
Si tu R503 viene con el conector JST-SH, corta un pigtail de 6 pines JST-SH a Dupont para sacar los cables. El marrón a veces es verde dependiendo del vendedor — verifica contra el cable que va al pin RXD del conector JST, no por el color.
Probado en Fedora 44 KDE; debería funcionar en cualquier distro basada en systemd con fprintd, pam_fprintd y una toolchain reciente de Rust.
Paquetes del sistema:
| Distribución | Compilación | Ejecución |
|---|---|---|
| Fedora / RHEL | rust cargo arduino-cli tpm2-tss-devel | fprintd pam fprintd-pam tpm2-tss |
| Debian / Ubuntu | rustc cargo arduino-cli libtss2-dev | fprintd libpam-fprintd libtss2-esys-3.0.2-0 |
Los paquetes tss-esapi solo son necesarios si planeas usar --pair --seal-tpm (SPEC §13.12). El daemon se compila y funciona sin un TPM de lo contrario — tss-esapi es una dependencia de compilación obligatoria pero una dependencia de ejecución opcional (la ruta de código solo se ejecuta cuando existe /var/lib/r503d/key.tpm).
Rust 1.95+, arduino-cli en tu $PATH.
¿Tienes un TPM2?```bash ls /dev/tpmrm0 && tpm2_pcrread sha256:7 | head -3
Si ambos tienen éxito, tu host puede usar la ruta de clave sellada. Si falta
`/dev/tpmrm0` (hardware antiguo, TPM deshabilitado en la BIOS o una VM sin TPM
virtual), quédate con el flujo de clave en texto plano predeterminado.
## Compilar e instalar
### 1. Grabar el firmware
Abre `firmware/r503fp/r503fp.ino` en el Arduino IDE y súbelo. O con
`arduino-cli`:```bash
# Uno R3:
arduino-cli compile --fqbn arduino:avr:uno firmware/r503fp/
arduino-cli upload --fqbn arduino:avr:uno --port /dev/ttyACM0 firmware/r503fp/
# Nano (modern Optiboot, including most Elegoo / WAVGAT clones):
arduino-cli compile --fqbn arduino:avr:nano:cpu=atmega328 firmware/r503fp/
arduino-cli upload --fqbn arduino:avr:nano:cpu=atmega328 --port /dev/ttyUSB0 firmware/r503fp/
# Nano with legacy 57600-baud bootloader (older clones):
# replace `cpu=atmega328` with `cpu=atmega328old`
El firmware utiliza Adafruit_Fingerprint. El IDE ofrecerá instalarla
en la primera compilación.
Si arduino-cli upload falla con not in sync: resp=0x7e, tu bootloader
es la otra variante — intercambia atmega328 ↔ atmega328old y reintenta. Ambos
funcionan; la diferencia es solo la velocidad de baudios del bootloader.
Requiere Rust 1.95+.```bash cd pcside/daemon cargo build --release
### 3. Instalación```bash
sudo bash pcside/daemon/dist/install.sh
Ese script:
target/release/r503d en /usr/local/bin/r503d/var/lib/r503d/ (modo 0700 root:root) para la clave, el estado y el
registro de ranuras de usuario/dev/r503 y bloquea el
nodo de dispositivo a root:root 0600 (solo el daemon, que se ejecuta como root, lo necesita;
esto cierra la ruta predeterminada 0660 root:dialout para que ningún otro usuario local pueda
abrir el puerto — auditoría de seguridad 2026-05-28 / H1). Consecuencia: después de la
instalación, cualquier comando manual de arduino-cli/serial-monitor contra /dev/r503
requiere sudo./etc/systemd/system/r503d.service)net.reactivated.Fprint/usr/share/polkit-1/actions/net.reactivated.fprint.device.r503d.policy)
utilizada por la compuerta de identidad del llamante/etc/dbus-1/system.d/net.reactivated.Fprint.conf) — solo los miembros de root y
wheel pueden hablar con el daemon; todos los demás reciben
AccessDenied en el broker, antes de que el daemon vea la llamadafprintd.service aguas arribar503d.serviceEs idempotente — vuelve a ejecutarlo después de cada cargo build --release para
reimplementar el nuevo binario.