Skip to content
KitploitKITPLOIT
ToolsBlog
Log in
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
linux-fingerprint-r503 — Linux desktop fingerprint login using a Grow R503 sensor + Arduino + a Rust fprintd-replacement daemon | Kitploit
Tools/GitHubGitHub/matpb/linux-fingerprint-r503
Embedded Systems SecurityCryptographyPenetration TestingHardware SecurityAuthenticationRed Teaming
GitHubmatpb/linux-fingerprint-r503

linux-fingerprint-r503

Linux desktop fingerprint login using a Grow R503 sensor + Arduino + a Rust fprintd-replacement daemon

View Repository
4421514 days agoReviewed by Kitploit

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

linux-fingerprint-r503 — fingerprint login for Linux using a Grow R503 + Arduino

CI r503d firmware Rust 2024 Platform: Linux License: MIT

A from-parts USB fingerprint reader for Linux desktops. Total parts cost under $15. Drop-in replacement for upstream fprintd — PAM, KDE Settings, GNOME Settings, fprintd-verify, sudo with finger, screen-unlock with finger all work.

As of fw=1.0 / r503d 1.0.0 the Arduino↔host wire is authenticated: every command and response carries a SipHash-2-4 MAC keyed to a TOFU-paired secret in EEPROM. Replay and hot-swap attacks against the USB serial link are blocked. See SPEC.md §13 for the full design, including what the threat model doesn't cover.

R503 sensor mounted in a hand-cut wooden enclosure, blue ring glowing

wish I had a 3d printer…

   ┌──────────┐   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

Hardware USB fingerprint readers for Linux are scarce, expensive, and the ones that exist (Validity, Synaptics, etc.) are reverse-engineered through unstable libfprint drivers that break with vendor firmware updates. The Grow R503's protocol is public, the Arduino side is your own code, and the libfprint compatibility layer is just D-Bus.

You also end up with a fingerprint reader you can read the source of, top to bottom.

Bill of materials

PartNotesApprox cost
Grow R503 capacitive fingerprint sensorThe round one with the RGB ring~$10
Arduino Uno R3 / Nano / Mega / any ATmega328 boardAnything that runs SoftwareSerial$5–$25
4–6 jumper wiresDupont / breadboardtrivial

That's it. No level shifter, no voltage divider — see SPEC.md §3.1 for why (the R503's RX line is 5V-tolerant in practice; the datasheet lies).

Wiring

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)

If your R503 ships with the JST-SH connector, snip a 6-pin JST-SH-to-Dupont pigtail to break the wires out. Brown is sometimes green depending on the seller — verify against the wire that goes into the RXD pin of the JST header, not the colour.

Prerequisites

Tested on Fedora 44 KDE; should work on any systemd-based distro with fprintd, pam_fprintd, and a recent Rust toolchain.

System packages:

DistroBuildRuntime
Fedora / RHELrust cargo arduino-cli tpm2-tss-develfprintd pam fprintd-pam tpm2-tss
Debian / Ubunturustc cargo arduino-cli libtss2-devfprintd libpam-fprintd libtss2-esys-3.0.2-0

The tss-esapi packages are only needed if you plan to use --pair --seal-tpm (SPEC §13.12). The daemon builds and runs without a TPM otherwise — tss-esapi is a hard build dep but a soft runtime dep (the code path is only entered when /var/lib/r503d/key.tpm exists).

Rust 1.95+, arduino-cli on your $PATH.

Do you have a TPM2?

ls /dev/tpmrm0 && tpm2_pcrread sha256:7 | head -3

If both succeed, your host can use the sealed-key path. If /dev/tpmrm0 is missing (older hardware, TPM disabled in BIOS, or a VM without a virtual TPM), stick with the default plaintext-key flow.

Build & install

1. Flash the firmware

Open firmware/r503fp/r503fp.ino in the Arduino IDE and upload. Or with arduino-cli:

# 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`

The firmware uses Adafruit_Fingerprint. The IDE will offer to install it on first compile.

If arduino-cli upload fails with not in sync: resp=0x7e, your bootloader is the other variant — swap atmega328 ↔ atmega328old and retry. Both work; the difference is just bootloader baud rate.

2. Build the daemon

Requires Rust 1.95+.

cd pcside/daemon
cargo build --release

3. Install

sudo bash pcside/daemon/dist/install.sh

That script:

  • installs target/release/r503d to /usr/local/bin/r503d
  • creates /var/lib/r503d/ (mode 0700 root:root) for the key, state, and user-slot registry
  • writes the udev rule that exposes the Arduino as /dev/r503 and locks the device node to root:root 0600 (only the daemon, running as root, needs it; this closes the default 0660 root:dialout path so no other local user can open the port — security audit 2026-05-28 / H1). Consequence: after install, any manual arduino-cli/serial-monitor command against /dev/r503 needs sudo.
  • installs the systemd unit (/etc/systemd/system/r503d.service)
  • overrides the D-Bus autolaunch entry for net.reactivated.Fprint
  • installs the polkit action (/usr/share/polkit-1/actions/net.reactivated.fprint.device.r503d.policy) used by the caller-identity gate
  • installs the restrictive system bus policy (/etc/dbus-1/system.d/net.reactivated.Fprint.conf) — only root and wheel members can talk to the daemon; everyone else hits AccessDenied at the broker, before the daemon sees the call
  • stops and masks upstream fprintd.service
  • starts r503d.service

It's idempotent — re-run it after every cargo build --release to redeploy the new binary.

4. Pair the Nano with the daemon

A freshly-flashed Nano is unpaired — the daemon would talk to it but the firmware would reject every framed command. Pick one of the two flows below; both end with a paired Nano and a working daemon. The TPM-sealed flow is recommended if your host has a TPM2 (see Prerequisites for the quick check).

Download Tool