
Grow R503 센서 + Arduino + Rust 기반 fprintd 대체 데몬을 사용한 Linux 데스크톱 지문 로그인
Linux 데스크탑용 부품 기반 USB 지문 리더기입니다. 총 부품 비용 $15 미만. 상위 fprintd의 드롭인 대체품 — PAM, KDE 설정, GNOME 설정, fprintd-verify, 손가락으로 sudo, 손가락으로 화면 잠금 해제 등 모두 작동합니다.
fw=1.0 / r503d 1.0.0 기준 Arduino↔호스트 연결이 인증됩니다: 모든 명령과 응답에는 EEPROM에 저장된 TOFU 페어링된 비밀로 키가 설정된 SipHash-2-4 MAC이 포함됩니다. USB 직렬 링크에 대한 재생 및 핫스왑 공격이 차단됩니다. 위협 모델이 포함하지 않는 내용을 포함한 전체 설계는 SPEC.md §13을 참조하세요.

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, …
## Why
리눅스용 하드웨어 USB 지문 인식기는 드물고 비싸며,
존재하는 것들 (Validity, Synaptics 등)은 불안정한 libfprint 드라이버를 통해
역설계되었으며, 이 드라이버는 벤더 펌웨어 업데이트 시 망가집니다.
Grow R503의 프로토콜은 **공개**되어 있으며, Arduino 쪽은 사용자 코드이고,
libfprint 호환 레이어는 D-Bus일 뿐입니다.
결국 소스 코드를 처음부터 끝까지 읽을 수 있는 지문 인식기를 갖게 됩니다.
## 부품 목록
| Part | Notes | Approx cost |
|------|-------|------|
| Grow R503 용량성 지문 센서 | RGB 링이 있는 둥근 모델 | ~$10 |
| Arduino Uno R3 / Nano / Mega / 모든 ATmega328 보드 | SoftwareSerial을 실행할 수 있는 모든 것 | $5–$25 |
| 4–6 점퍼 와이어 | 듀폰 / 브레드보드 | 거의 무료 |
이게 다입니다. **레벨 시프터도, 전압 분배기도 필요 없습니다** — 이유는 [`SPEC.md` §3.1](https://github.com/matpb/linux-fingerprint-r503/blob/main/SPEC.md)을 참조하세요
(R503의 RX 라인은 실제로 5V에 내성이 있으며, 데이터시트는 거짓말을 합니다).
## 배선```
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)
R503에 JST-SH 커넥터가 함께 제공된다면, 6핀 JST-SH-투-듀폰 케이블을 잘라서 전선을 분리하세요. 판매자에 따라 갈색이 때로는 녹색이기도 합니다. 색상이 아니라 JST 헤더의 RXD 핀으로 연결되는 전선과 확인하세요.
Fedora 44 KDE에서 테스트 완료; fprintd, pam_fprintd 및 최신 Rust 툴체인이 포함된 systemd 기반 배포판에서 작동해야 합니다.
시스템 패키지:
| 배포판 | 빌드 | 런타임 |
|---|---|---|
| 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 |
tss-esapi 패키지는 --pair --seal-tpm(SPEC §13.12)을 사용할 계획이 있는 경우에만 필요합니다. 데몬은 TPM 없이도 빌드 및 실행됩니다. tss-esapi는 빌드 시 하드 종속성이지만 런타임 시 소프트 종속성입니다(코드 경로는 /var/lib/r503d/key.tpm이 존재할 때만 진입합니다).
Rust 1.95+, $PATH에 arduino-cli.
TPM2가 있습니까?```bash ls /dev/tpmrm0 && tpm2_pcrread sha256:7 | head -3
둘 다 성공하면 호스트에서 봉인된 키 경로를 사용할 수 있습니다. `/dev/tpmrm0`가
없는 경우(오래된 하드웨어, BIOS에서 TPM 비활성화, 또는 가상 TPM이 없는 VM),
기본 일반 텍스트 키 흐름을 사용하세요.
## 빌드 및 설치
### 1. 펌웨어 플래시
Arduino IDE에서 `firmware/r503fp/r503fp.ino`를 열고 업로드합니다. 또는
`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`
펌웨어는 Adafruit_Fingerprint를 사용합니다. IDE는 첫 번째 컴파일 시 이를 설치하도록 제안합니다.
arduino-cli upload가 not in sync: resp=0x7e 오류와 함께 실패하면, 부트로더가 다른 변형입니다 — atmega328 ↔ atmega328old를 바꾸고 다시 시도하세요. 둘 다 작동하며, 차이는 오직 부트로더의 전송 속도(bootloader baud rate)뿐입니다.
Rust 1.95+가 필요합니다.```bash cd pcside/daemon cargo build --release
### 3. 설치```bash
sudo bash pcside/daemon/dist/install.sh
해당 스크립트:
target/release/r503d를 /usr/local/bin/r503d에 설치합니다/var/lib/r503d/를 생성합니다 (모드 0700 root:root)/dev/r503로 노출하고 디바이스 노드를 root:root 0600으로 잠그는 udev 규칙을 작성합니다 (데몬만이 루트로 실행되므로 필요합니다; 이는 기본 0660 root:dialout 경로를 닫아 다른 로컬 사용자가 포트를 열 수 없도록 합니다 — 보안 감사 2026-05-28 / H1). 결과: 설치 후, /dev/r503에 대한 수동 arduino-cli/serial-monitor 명령은 sudo가 필요합니다./etc/systemd/system/r503d.service)을 설치합니다net.reactivated.Fprint에 대한 D-Bus 자동 실행 항목을 재정의합니다/usr/share/polkit-1/actions/net.reactivated.fprint.device.r503d.policy)을 설치합니다/etc/dbus-1/system.d/net.reactivated.Fprint.conf)을 설치합니다 — root 및 wheel 그룹 구성원만 데몬과 통신할 수 있습니다; 그 외의 모든 사용자는 데몬이 호출을 보기도 전에 브로커에서 AccessDenied를 만납니다.fprintd.service를 중지하고 마스킹합니다r503d.service를 시작합니다멱등성을 가집니다 — cargo build --release 후에 다시 실행하여 새 바이너리를 재배포할 수 있습니다.
새로 플래시된 Nano는 페어링되지 않은 상태입니다 — 데몬이 통신을 시도하지만 펌웨어가 모든 프레임 명령을 거부합니다. 아래 두 가지 흐름 중 하나를 선택하세요; 둘 다 페어링된 Nano와 작동하는 데몬으로 끝납니다. 호스트에 TPM2가 있는 경우 TPM 봉인 흐름이 권장됩니다 (빠른 확인은 전제 조건 참조).
두 흐름 모두에서 사용되는 옵트인 파일(/etc/r503d/allow-pair)은 공격자가 자신의 Nano를 가지고 책상으로 달려오는 것을 막기 위해 존재합니다 — 루트 없이는 페어링이 불가능합니다. r503d --pair는 키를 Nano로 보내기 전에 마커를 삭제합니다: Nano 측 커밋과 호스트 측 영속화 사이에 호스트가 충돌하면 게이트가 이미 닫혀 있으므로, 다음 페어링 시도는 관리자가 touch 명령으로 마커를 다시 생성해야 합니다. 전송 전 중단(마커 없음 또는 "이미 페어링됨")은 마커를 그대로 두어 재시도할 수 있습니다.
TPM2 장치가 없거나 오프라인 디스크 공격 저항이 필요하지 않은 경우 이 방법을 사용하세요.```bash sudo systemctl stop r503d sudo mkdir -p /etc/r503d sudo touch /etc/r503d/allow-pair # opt-in (see SPEC §13.5) sudo r503d --pair # 128-bit key → /var/lib/r503d/key sudo systemctl start r503d
__ ___ _
/ / ___ __ _ ___ / _ \ _ () ___ _ __
/ / / _ \ / ` |/ _ \ / /)/| '| / _|/ _ | '
/ /| () | (| | () | / __/ | | | _ \ () | | | |
_/_/ _, |_/ / || ||/___/|| ||
|__/
sudo r503d --status
# port: /dev/r503
# firmware: fw=1.1 fmt=2
# firmware paired: true
# firmware counter: 42
# host key.tpm: (absent)
# host key: /var/lib/r503d/key
# host key.bak: /var/lib/r503d/key.bak
# tpm device: (absent)
# allow-pair: (absent)
```
#### 4b. TPM 실링 페어링 (TPM2 호스트에서 권장, SPEC §13.12)
동일한 흐름에 `--seal-tpm`을 추가합니다. 생성된 키는 **PCR7**(보안 부팅 정책 + 키)에 실링되어 `/var/lib/r503d/key.tpm`에 기록되며, 평문 `key` 파일은 사용되지 않습니다. 오프라인 디스크 공격자(마운트되지 않은 파티션의 `dd`, 적대적 호스트로의 SSD 교체)는 암호문만 획득할 수 있습니다.```bash
sudo systemctl stop r503d
sudo mkdir -p /etc/r503d
sudo touch /etc/r503d/allow-pair
sudo r503d --pair --seal-tpm # seals new key to current PCR7
sudo systemctl start r503d
```
--service-password NewSuperSecretPassword123 --vpn-password NewSecretVPNPass456 --region us-east-1`;
```