
Connexion par empreinte digitale au bureau Linux à l'aide d'un capteur Grow R503 + Arduino + d'un démon de remplacement de fprintd en Rust
Un lecteur d'empreintes digitales USB fait de pièces détachées pour les bureaux Linux. Coût total des pièces
moins de 15 $. Remplacement direct du fprintd amont — PAM, Paramètres KDE,
Paramètres GNOME, fprintd-verify, sudo avec le doigt, déverrouillage d'écran avec le
doigt fonctionnent tous.
À partir de fw=1.0 / r503d 1.0.0 la liaison Arduino↔hôte est authentifiée :
chaque commande et réponse porte un MAC SipHash-2-4 lié à un
secret apparié par TOFU dans l'EEPROM. Les attaques par rejeu et par échange à chaud contre la liaison série USB
sont bloquées. Voir SPEC.md §13 pour la conception complète,
y compris ce que le modèle de menace ne couvre pas.

si seulement j'avais une imprimante 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, …
## Pourquoi
Les lecteurs d'empreintes digitales USB matériels pour Linux sont rares, chers, et ceux qui existent (Validity, Synaptics, etc.) sont rétro-ingénierés via des pilotes libfprint instables qui cessent de fonctionner avec les mises à jour du firmware des fabricants. Le protocole du Grow R503 est **public**, le côté Arduino est votre propre code, et la couche de compatibilité libfprint n'est que du D-Bus.
Vous vous retrouvez aussi avec un lecteur d'empreintes dont vous pouvez lire le code source, de haut en bas.
## Liste du matériel
| Composant | Notes | Coût approx. |
|------|-------|------|
| Capteur d'empreintes digitales capacitif Grow R503 | Le modèle rond avec l'anneau RGB | ~$10 |
| Arduino Uno R3 / Nano / Mega / toute carte ATmega328 | Tout ce qui exécute SoftwareSerial | $5–$25 |
| 4–6 fils de connexion | Dupont / breadboard | négligeable |
C'est tout. **Ni level shifter, ni diviseur de tension** — voir [`SPEC.md` §3.1](https://github.com/matpb/linux-fingerprint-r503/blob/HEAD/SPEC.md) pour savoir pourquoi (la ligne RX du R503 tolère 5 V en pratique ; la fiche technique ment).
## Câblage```
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 votre R503 est livré avec le connecteur JST-SH, coupez un pigtail 6 broches JST-SH vers Dupont pour sortir les fils. Le brun est parfois vert selon le vendeur — vérifiez par rapport au fil qui va dans la broche RXD du connecteur JST, pas la couleur.
Testé sur Fedora 44 KDE ; devrait fonctionner sur toute distribution basée sur systemd avec fprintd, pam_fprintd et une toolchain Rust récente.
Paquets système :
| Distribution | Compilation | Exécution |
|---|---|---|
| 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 |
Les paquets tss-esapi ne sont nécessaires que si vous prévoyez d'utiliser --pair --seal-tpm (SPEC §13.12). Le démon se compile et s'exécute sans TPM sinon — tss-esapi est une dépendance de compilation obligatoire mais une dépendance d'exécution facultative (le chemin de code n'est emprunté que lorsque /var/lib/r503d/key.tpm existe).
Rust 1.95+, arduino-cli dans votre $PATH.
Avez-vous un TPM2 ?```bash ls /dev/tpmrm0 && tpm2_pcrread sha256:7 | head -3
Si les deux réussissent, votre hôte peut utiliser le chemin de la clé scellée. Si `/dev/tpmrm0` est
absent (matériel ancien, TPM désactivé dans le BIOS, ou une VM sans TPM virtuel),
restez avec le flux de clé en texte brut par défaut.
## Compilation et installation
### 1. Flasher le firmware
Ouvrez `firmware/r503fp/r503fp.ino` dans l'IDE Arduino et téléversez. Ou avec
`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`
Le firmware utilise Adafruit_Fingerprint. L'IDE proposera de l'installer
lors de la première compilation.
Si arduino-cli upload échoue avec not in sync: resp=0x7e, votre bootloader
est l'autre variante — remplacez atmega328 ↔ atmega328old et réessayez. Les deux
fonctionnent ; la différence réside uniquement dans le débit en bauds du bootloader.
Nécessite Rust 1.95+.```bash cd pcside/daemon cargo build --release
### 3. Installation```bash
sudo bash pcside/daemon/dist/install.sh
Ce script :
target/release/r503d vers /usr/local/bin/r503d/var/lib/r503d/ (mode 0700 root:root) pour la clé, l'état, et
le registre d'emplacements utilisateur/dev/r503 et verrouille le
nœud de périphérique en root:root 0600 (seul le démon, qui tourne en root, en a besoin ;
cela ferme le chemin par défaut 0660 root:dialout afin qu'aucun autre utilisateur local ne puisse
ouvrir le port — audit de sécurité 2026-05-28 / H1). Conséquence : après
l'installation, toute commande manuelle arduino-cli/serial-monitor à destination de /dev/r503
nécessite sudo./etc/systemd/system/r503d.service)net.reactivated.Fprint/usr/share/polkit-1/actions/net.reactivated.fprint.device.r503d.policy)
utilisée par le contrôle d'identité de l'appelantC'est idempotent — relancez-le après chaque cargo build --release pour
redéployer le nouveau binaire.
Un Nano fraîchement flashé n'est pas appairé — le démon lui parlerait, mais le firmware rejetterait chaque commande tramée. Choisissez l'un des deux flux ci-dessous ; les deux aboutissent à un Nano appairé et un démon fonctionnel. Le flux scellé par TPM est recommandé si votre hôte dispose d'un TPM2 (voir Prérequis pour la vérification rapide).
Le fichier opt-in (/etc/r503d/allow-pair) utilisé dans les deux flux existe pour
déjouer un attaquant qui se précipite à votre bureau avec son propre Nano — l'appairage
sans root est impossible. r503d --pair supprime le marqueur avant
d'envoyer la clé au Nano : si l'hôte plante entre la validation côté Nano
et la persistance côté hôte, la porte est déjà fermée, donc la prochaine tentative
d'appairage nécessite qu'un administrateur exécute touch sur le marqueur à nouveau. Un abandon avant envoi
(pas de marqueur, ou « déjà appairé ») laisse le marqueur intact pour une nouvelle tentative.
Utilisez ceci si vous n'avez pas de périphérique TPM2, ou si vous n'avez pas besoin de résistance aux attaques par disque hors ligne.```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
The input chunk is empty — there is no source text provided to translate. Please supply chunk 15 of 37 and I will translate it into French.```bash
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)
Même procédure, avec en plus --seal-tpm. La clé générée est scellée sur PCR7
(politique Secure Boot + clés) et écrite dans /var/lib/r503d/key.tpm
au lieu du fichier key en clair. Les attaquants accédant au disque hors ligne (dd d'une
partition non montée, insertion du SSD dans un hôte hostile) n'obtiennent que du texte chiffré.```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
I don't see any source text to translate — the input after `INPUT:` is empty. Please provide the actual chunk content (19/37) and I'll translate it into French right away.```bash
sudo r503d --status
# port: /dev/r503
# firmware: fw=1.1 fmt=2
# firmware paired: true
# firmware counter: 12
# host key.tpm: /var/lib/r503d/key.tpm
# host key: (missing)
# host key.bak: (missing)
# tpm device: /dev/tpmrm0
# allow-pair: (absent)
Les mises à jour du noyau, les mises à jour de l'initrd, les mises à jour du firmware UEFI via fwupd et les mises à jour de grub2 ne modifient pas PCR7 et ne nécessitent pas de rescellage. PCR7 ne change qu'en cas de modifications de la politique Secure Boot, d'inscriptions MOK ou de déplacement du disque vers un autre hôte — auquel cas le démon refuse de démarrer avec TPM_RC_POLICY_FAIL et dist/reseal-tpm.sh récupère en ~90 secondes. Voir Récupération : PCR7 modifié.
fprintd-enroll mat
fprintd-verify mat
sudo whoami
Les boîtes de dialogue d'empreintes digitales du compte utilisateur
de KDE Settings (Plasma 6) et de GNOME Control Center pilotent `r503d` exactement comme elles pilotent le `fprintd` en amont.
### Réappairage / rotation de clé
Si vous voulez une nouvelle clé (clé compromise, remplacement matériel prévu, paranoïa):```bash
sudo systemctl stop r503d
sudo r503d --unpair # framed; wipes Nano EEPROM + host key
sudo touch /etc/r503d/allow-pair
sudo r503d --pair # plaintext-key rotation
# - or -
sudo r503d --pair --seal-tpm # TPM-sealed rotation
sudo systemctl start r503d
Reprenez le même chemin d'appairage que celui d'origine. Si vous avez initialement utilisé --seal-tpm, effectuez la rotation avec --seal-tpm — sinon la rotation vous rétrograde silencieusement vers une clé en clair sur le disque.
Si vous avez utilisé --pair --seal-tpm et avez ensuite modifié un élément que PCR7 mesure (Secure Boot désactivé/activé, nouveau MOK inscrit, disque déplacé vers une autre machine), le démon refusera de démarrer avec un message du journal concernant TPM_RC_POLICY_FAIL. La récupération se fait en une commande :```bash
sudo bash pcside/daemon/dist/reseal-tpm.sh
Le script arrête `r503d`, reflashe `firmware/r503fp_wipe/` pour effacer l'EEPROM du Nano, reflashe le firmware principal, crée `/etc/r503d/allow-pair`, exécute `r503d --reseal-tpm` pour générer une nouvelle clé scellée au PCR7 *actuel*, puis relance le démon. Temps réel : ~90 secondes. Les doigts enregistrés sont conservés — les gabarits vivent sur la flash du capteur R503, pas sur le Nano.
Le script nécessite `arduino-cli`. S'il est installé dans le `$HOME/.local/bin` de votre utilisateur, il est détecté automatiquement via `$SUDO_USER` ; sinon, définissez `ARDUINO_CLI=/full/path/to/arduino-cli` avant de l'exécuter.
### Récupération : `state.json` perdu (désynchronisation du compteur)
Si la clé hôte est intacte mais que `/var/lib/r503d/state.json` est manquant ou a été restauré (récupération d'une ancienne sauvegarde, rm accidentel), le compteur du démon retombe derrière le `last_seen` du Nano et chaque commande tramée se heurte à `ERR replay`. `r503d --status` signale ce problème ; la solution est une seule commande :```bash
sudo systemctl stop r503d
sudo r503d --resync # reads Nano last_seen, sets host counter to last_seen+1
sudo systemctl start r503d
Pas de réappairage, pas de reflash — la clé ne bouge jamais. La requête status sur laquelle s'appuie --resync n'est pas authentifiée, mais elle ne peut que faire avancer le compteur hôte vers l'avant pour correspondre à ce que le Nano a déjà validé, donc elle ne peut jamais rendre une ancienne trame rejouable (dans le pire des cas, un MITM menteur force un autre ERR replay, ce qu'il pouvait déjà faire en brouillant les trames). Voir SPEC.md §13.11.
La commande authentifiée --unpair a besoin de la clé pour autoriser. Si toutes les copies sur disque ont disparu (panne disque, rm accidentel, key + key.bak toutes deux supprimées, ou blob key.tpm perdu), vous avez besoin de la trappe de secours reflash-to-wipe — la même procédure que dist/reseal-tpm.sh automatise pour le cas de changement de PCR7 ci-dessus :```bash
sudo systemctl stop r503d
sudo arduino-cli upload --fqbn arduino:avr:nano:cpu=atmega328 --port /dev/r503 firmware/r503fp_wipe/
sudo arduino-cli upload --fqbn arduino:avr:nano:cpu=atmega328 --port /dev/r503 firmware/r503fp/ sudo touch /etc/r503d/allow-pair sudo r503d --pair sudo systemctl start r503d
Si `sudo arduino-cli` signale command-not-found (arduino-cli se trouve dans votre
`~/.local/bin`, pas dans le `PATH` de root), lancez-le via
`sudo env "PATH=$PATH" arduino-cli …` ou indiquez le chemin absolu.
Ce n'est pas une porte dérobée qu'un attaquant pourrait utiliser : le ré-appairage nécessite root sur
l'hôte (le fichier d'opt-in et la CLI `--pair` nécessitent tous deux root), donc un
Nano reflashé ne peut pas être mis en confiance sans que vous soyez déjà
root.
### Désinstaller```bash
sudo bash pcside/daemon/dist/uninstall.sh
Restaure tout, démasque fprintd, laisse /var/lib/r503d/ (clé,
état, utilisateurs) en place au cas où vous voudriez réinstaller plus tard. Supprimez ce
répertoire manuellement si vous voulez une véritable table rase.
L'Arduino exécute un petit firmware à protocole ASCII (firmware/r503fp/)
qui parle le protocole binaire natif R30x ("Sync Word") du R503 sur son
côté UART et échange des commandes texte orientées lignes avec l'hôte via
USB-CDC: ping, info, enroll N, verify, delete N, clear,
led off. Protocole v1 complet dans SPEC.md §5.
Depuis fw=1.0 (jalon E des travaux sur le canal authentifié v2), chaque
commande et réponse est encapsulée dans une trame C <counter> <body> M <mac> /
R <counter> <seq> <body> M <mac>, authentifiée par MAC avec SipHash-2-4 sur une
clé de 128 bits appariée par TOFU. Le Nano conserve un compteur monotone à nivellement d'usure
dans l'EEPROM ; le démon conserve un compteur correspondant dans /var/lib/r503d/state.json.
Les tentatives de rejeu (côté firmware incoming <= last_seen) sont rejetées comme
ERR replay ; les trames falsifiées sont rejetées avec ERR mac_invalid. Spécification complète, modèle de
menace et limitations connues dans SPEC.md §13.
Le démon Rust (r503d) parle D-Bus sur net.reactivated.Fprint — bit pour bit
la même interface que celle exposée par fprintd en amont — donc chaque client fprintd
fonctionne sans modification. Un sidecar JSON dans /var/lib/r503d/users.json mappe
(utilisateur, doigt) vers les indices d'emplacement dans la mémoire flash interne du R503.
Disposition :``` firmware/r503fp/ Arduino firmware (v2 framed ASCII protocol) firmware/r503fp_wipe/ Emergency one-shot EEPROM wipe (lost-key recovery) firmware/* Diagnostic / development sketches (ping, loopback, ...) pcside/daemon/ Rust daemon (the fprintd replacement) pcside/daemon/src/{crypto,framing,keystore,state,pairing}.rs v2 wire protocol implementation pcside/daemon/src/auth.rs caller-identity gating for D-Bus methods pcside/daemon/dist/ udev rule, systemd unit, polkit + bus policy, install scripts docs/ Decision logs + troubleshooting SPEC.md Full architecture + protocol spec (§13 = v2 auth)
## Modèle de sécurité — résumé rapide
L'authentification de niveau filaire cible une menace spécifique —
**« femme de chambre malveillante avec cinq minutes et un Nano de rechange »** plus un processus local hostile sur `/dev/r503` — pas des États-nations ni des attaquants matériels avec des laboratoires. Déploiement sur poste de travail mono-utilisateur avec une liste documentée des cas hors périmètre. Le modèle de menace complet se trouve dans [`SPEC.md` §13.1](https://github.com/matpb/linux-fingerprint-r503/blob/HEAD/SPEC.md) ; les preuves d'implémentation et de revue se trouvent dans [`docs/REVIEW-2026-05-28.md`](https://github.com/matpb/linux-fingerprint-r503/blob/HEAD/docs/REVIEW-2026-05-28.md). Un audit distinct d'élévation de privilèges de type adversaire (2026-05-28) et sa passe de validation/remédiation par revendication sont dans [`docs/SECURITY-AUDIT-2026-05-28.html`](https://github.com/matpb/linux-fingerprint-r503/blob/HEAD/docs/SECURITY-AUDIT-2026-05-28.html) et [`docs/SECURITY-AUDIT-2026-05-28-VALIDATION.html`](https://github.com/matpb/linux-fingerprint-r503/blob/HEAD/docs/SECURITY-AUDIT-2026-05-28-VALIDATION.html).
**Défendu :**
- Échange à chaud du Nano avec une unité hostile (pas de clé → toutes les trames échouent au MAC).
- Processus local injectant de fausses réponses de correspondance sur `/dev/r503`. Deux couches : le nœud de périphérique est `root:root 0600` (règle udev) et le démon le maintient avec `TIOCEXCL`, donc un processus non root ne peut pas l'ouvrir — et même s'il le pouvait, il n'a pas la clé, donc la trame échoue à la vérification MAC.
- Rejeu de trames `OK match=...` enregistrées lors d'une session ultérieure.
- Altération par bascule de bit de tout champ de trame (comparaison MAC à temps constant).
- Contre le brick par épuisement du compteur : un pair (ou un MITM ponctuel pendant `--resync`) qui pousse le compteur monotone vers `u64::MAX` et coince définitivement le canal est bloqué par un plafond de compteur réservé appliqué aux deux extrémités (`fw=1.1+` ; audit SPEC §13.4 / 2026-05-28, DoS-2).
- Déni de service local du capteur par un utilisateur local : un unique verrou de slot de capture plafonne le travail d'enrôlement/vérification en vol et les chemins de suppression sont contrôlés par action, donc une inondation de `Start`/`Stop` (ou de suppressions concurrentes) ne peut pas bloquer l'authentification.
- Implantation / effacement / énumération d'empreintes d'un autre utilisateur par un utilisateur local non root (par ex. `mallory` appelant `Claim "root"` puis enrôlant son propre doigt) — l'identité de l'appelant est vérifiée sur chaque méthode D-Bus prenant un `username`, et la politique du bus système refuse les appelants non-`wheel` au niveau du courtier.
- **Attaques hors ligne sur le disque contre la clé hôte** *lorsqu'elle est associée à `--seal-tpm`* : la clé sur disque est scellée TPM2 vers PCR7, donc un `dd` d'une partition démontée ou un échange de SSD dans un hôte hostile ne donne que du texte chiffré. Le déballage n'a lieu que sur la même machine sous la même politique Secure Boot. Voir [SPEC §13.12](https://github.com/matpb/linux-fingerprint-r503/blob/HEAD/SPEC.md).
**Non défendu :**
- Compromission root de l'hôte (la clé est dans `/var/lib/r503d/key`, `0600 root:root`). Le root sur un hôte en fonctionnement peut également désceller la variante scellée TPM — le scellement atténue les attaques *hors ligne*, pas les attaques en ligne.
- Attaque physique sur le Nano (lecture de l'EEPROM en ~30 s avec ISP ; décapsulation de la puce ; etc.).
- Attaque par reflashage du firmware (le bootloader Arduino n'a pas de signature — mais le ré-appairage nécessite root sur l'hôte, donc un Nano reflashé ne peut pas être mis en confiance sans compromission de l'hôte de toute façon).
- Compromission côté R503 (le protocole R30x n'a aucune authentification ; hors de notre périmètre).
- **Posture cryptographique.** MAC SipHash-2-4, clé partagée de 128 bits, sortie MAC de 64 bits, entrées MAC à séparation de domaine. Deux implémentations indépendantes (C++ codée à la main sur l'AVR avec auto-test KAT au démarrage ; Rust codé à la main sur l'hôte, validé bit à bit par recoupement avec la crate tierce `siphasher` sur 1024 vecteurs aléatoires dans le CI). La comparaison MAC côté hôte utilise `subtle::ConstantTimeEq`. Les parseurs filaires sont fuzzés par propriétés à chaque exécution CI (~135 000 entrées). `cargo audit` propre. La clé SipHash est enveloppée dans `zeroize::Zeroizing<...>` pour être purgée lors du drop (tout comme les tampons d'entrée MAC par trame). Une cible libFuzzer `cargo fuzz` est fournie dans `pcside/daemon/fuzz/` pour des exécutions sur corpus long en nightly. Aucun audit humain tiers payant — cela reste précieux, les PR sont bienvenues.
Modèle de menace complet avec justification : [`SPEC.md` §13.1](https://github.com/matpb/linux-fingerprint-r503/blob/HEAD/SPEC.md).
## Limitations
- **Le multi-utilisateur fonctionne, mais uniquement pour les membres de `wheel`.** L'identité de l'appelant est vérifiée sur chaque méthode D-Bus qui prend un `username` (`Claim`, `EnrollStart`, `VerifyStart`, `ListEnrolledFingers`, `DeleteEnrolledFingers`) ; les auto-requêtes et `uid 0` (PAM) réussissent silencieusement, les requêtes inter-utilisateurs provenant d'un appelant non root sont refusées avec `net.reactivated.Fprint.Error.PermissionDenied`. La politique du bus système restreint en outre les comptes qui peuvent même démarrer une conversation : seuls `root` et les membres de `wheel` atteignent le démon, tous les autres obtiennent `org.freedesktop.DBus.Error.AccessDenied` au niveau du courtier. Besoin d'un enrôlement inter-utilisateurs ? Devenez root : `sudo fprintd-enroll target-user`. Besoin d'assouplir la barrière inter-utilisateurs pour un kiosque / laboratoire multi-utilisateurs ? Déposez une règle JS dans `/etc/polkit-1/rules.d/` ciblant [`net.reactivated.fprint.device.setusername`](https://gitlab.freedesktop.org/libfprint/fprintd/-/blob/master/src/net.reactivated.fprint.device.policy.in) — le nom d'action reprend mot pour mot le fprintd en amont.
- **Un seul lecteur.** Le démon expose un unique objet Device sur D-Bus. Les configurations multi-lecteurs nécessitent une extension du Manager.
- **Aucune émission `PropertiesChanged`** pour les propriétés d'indication `finger-present` / `finger-needed`. Chaque client fprintd courant (PAM, KDE Settings, GNOME) s'appuie sur les signaux `EnrollStatus` / `VerifyStatus` (qui sont émis), pas sur ces indications interrogées — mais un client strict qui fait `Get + PropertiesChanged` verra des valeurs obsolètes.
- **Un seul Nano = point de défaillance unique.** Si le Nano meurt, la connexion par empreinte est perdue jusqu'à ce que vous reflashiez un Nano de rechange et le ré-appairiez. Gardez une méthode d'authentification par mot de passe activée en secours.
- **La perte de State.json est récupérable en une commande.** Si `state.json` est perdu alors que le firmware a toujours un `last_seen` élevé, le démon obtient `ERR replay` à la première émission. Exécutez `sudo r503d --resync` pour lire le compteur du Nano et réaligner l'hôte — aucun ré-appairage nécessaire. Voir [`SPEC.md` §13.11](https://github.com/matpb/linux-fingerprint-r503/blob/HEAD/SPEC.md).
## Dépannage```bash
# Daemon logs:
sudo journalctl -u r503d.service -f
# Confirm the sensor enumerates correctly:
ls -l /dev/r503
busctl --system call net.reactivated.Fprint /net/reactivated/Fprint/Device/0 \
net.reactivated.Fprint.Device ListEnrolledFingers s ""
# Confirm fprintd is masked and r503d owns the bus name:
systemctl is-enabled fprintd # should print "masked"
busctl --system list | grep -i fprint
Si le démon ne démarre pas ou si le capteur ne répond jamais, la solution la plus courante
est le câblage — voir SPEC.md §3, en particulier la
note "no voltage divider" du §3.1. Un guide plus détaillé est disponible
dans docs/TROUBLESHOOTING.md.
MIT — voir LICENSE.
fprintd — pour avoir conçu une interface D-Bus propre que
ce démon pouvait implémenter sans jamais lire
le code source de libfprint./etc/dbus-1/system.d/net.reactivated.Fprint.conf) — seuls les membres de root et
wheel peuvent parler au démon ; tous les autres reçoivent
AccessDenied au niveau du courtier, avant que le démon ne voie l'appelfprintd.service amontr503d.service