
Login por impressão digital no desktop Linux usando um sensor Grow R503 + Arduino + um daemon substituto do fprintd escrito em Rust
Um leitor de impressão digital USB montado a partir de peças para desktops Linux. Custo total das peças inferior a US$ 15. Substituição direta do fprintd upstream — PAM, KDE Settings, GNOME Settings, fprintd-verify, sudo com o dedo, desbloqueio de tela com o dedo — tudo funciona.
A partir de fw=1.0 / r503d 1.0.0 o link Arduino↔host é autenticado: cada comando e resposta carrega um MAC SipHash-2-4 vinculado a um segredo pareado via TOFU na EEPROM. Ataques de replay e de troca a quente contra o link serial USB são bloqueados. Consulte SPEC.md §13 para o design completo, incluindo o que o modelo de ameaças não cobre.

quem me dera ter uma impressora 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, …
## Motivação
Leitores de impressão digital USB para Linux são escassos, caros e os
que existem (Validity, Synaptics, etc.) dependem de drivers libfprint
instáveis, criados por engenharia reversa, que quebram com atualizações
de firmware do fornecedor. O protocolo do Grow R503 é **público**, o lado
do Arduino é código seu, e a camada de compatibilidade libfprint é apenas
D-Bus.
Você também acaba com um leitor de impressão digital cujo código-fonte
você pode ler de cima a baixo.
## Lista de materiais
| Peça | Notas | Custo aproximado |
|------|-------|------|
| Sensor de impressão digital capacitivo Grow R503 | O redondo com o anel RGB | ~$10 |
| Placa Arduino Uno R3 / Nano / Mega / qualquer placa ATmega328 | Qualquer placa que rode SoftwareSerial | $5–$25 |
| 4–6 fios jumper | Dupont / protoboard | trivial |
## Ligação```
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)
Se o seu R503 vier com o conector JST-SH, corte um pigtail JST-SH para Dupont de 6 pinos para separar os fios. O fio marrom às vezes é verde, dependendo do vendedor — verifique em relação ao fio que vai para o pino RXD do conector JST, não pela cor.
Testado no Fedora 44 KDE; deve funcionar em qualquer distro baseada em systemd com fprintd, pam_fprintd e uma toolchain Rust recente.
Pacotes do sistema:
| Distro | Compilação | Tempo de execução |
|---|---|---|
| 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 |
Os pacotes tss-esapi só são necessários se você planeja usar --pair --seal-tpm (SPEC §13.12). O daemon compila e executa sem um TPM caso contrário — tss-esapi é uma dependência de compilação obrigatória, mas uma dependência de execução opcional (o caminho de código só é percorrido quando /var/lib/r503d/key.tpm existe).
Rust 1.95+, arduino-cli no seu $PATH.
Você tem um TPM2?```bash ls /dev/tpmrm0 && tpm2_pcrread sha256:7 | head -3
Se ambos forem bem-sucedidos, seu host poderá usar o caminho de chave selada. Se `/dev/tpmrm0` estiver ausente (hardware mais antigo, TPM desabilitado na BIOS ou uma VM sem TPM virtual), continue com o fluxo padrão de chave em texto puro.
## Compilação e instalação
### 1. Grave o firmware
Abra `firmware/r503fp/r503fp.ino` na IDE do Arduino e carregue. Ou com `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`
O firmware usa Adafruit_Fingerprint. A IDE se oferecerá para instalá-lo
na primeira compilação.
Se arduino-cli upload falhar com not in sync: resp=0x7e, o seu bootloader
é a outra variante — troque atmega328 ↔ atmega328old e tente novamente. Ambos
funcionam; a diferença é apenas a taxa de transmissão do bootloader.
Requer Rust 1.95+.```bash cd pcside/daemon cargo build --release
### 3. Instalação```bash
sudo bash pcside/daemon/dist/install.sh
Esse script:
target/release/r503d em /usr/local/bin/r503d/var/lib/r503d/ (modo 0700 root:root) para a chave, o estado e o
registro de slots de usuário/dev/r503 e bloqueia o
nó de dispositivo para root:root 0600 (somente o daemon, executando como root, precisa disso;
isso fecha o caminho padrão 0660 root:dialout para que nenhum outro usuário local possa
abrir a porta — auditoria de segurança 2026-05-28 / H1). Consequência: após a
instalação, qualquer comando manual de arduino-cli/serial-monitor contra /dev/r503
exige sudo./etc/systemd/system/r503d.service)net.reactivated.Fprint/usr/share/polkit-1/actions/net.reactivated.fprint.device.r503d.policy)
usada pela barreira de identidade do chamador/etc/dbus-1/system.d/net.reactivated.Fprint.conf) — somente membros de root e
wheel podem falar com o daemon; todos os outros recebem
AccessDenied no broker, antes de o daemon ver a chamadafprintd.service upstreamr503d.serviceÉ idempotente — execute-o novamente após cada cargo build --release para
reimplementar o novo binário.
Um Nano recém-gravado não está emparelhado — o daemon falaria com ele, mas o firmware rejeitaria cada comando enquadrado. Escolha um dos dois fluxos abaixo; ambos terminam com um Nano emparelhado e um daemon em funcionamento. O fluxo com selo TPM é recomendado se o seu host tiver um TPM2 (consulte os Pré-requisitos para a verificação rápida).