
تسجيل الدخول ببصمة الإصبع على سطح مكتب لينكس باستخدام مستشعر Grow R503 + أردوينو + برنامج خفي بديل لـ fprintd مكتوب بلغة Rust
قارئ بصمات USB مبني من مكونات منفصلة لأجهزة سطح المكتب Linux. التكلفة الإجمالية للمكونات أقل من 15$. بديل متوافق مباشرة مع fprintd الأصلي — PAM، وإعدادات KDE، وإعدادات GNOME، وfprintd-verify، وsudo بالإصبع، وفتح الشاشة بالإصبع، جميعها تعمل.
اعتبارًا من fw=1.0 / r503d 1.0.0، يتم توثيق الاتصال بين Arduino↔المضيف: كل أمر واستجابة يحملان MAC من SipHash-2-4 مرتبطًا بسرّ مُقترن عبر TOFU في EEPROM. يتم حظر هجمات إعادة البث والتبديل السريع ضد وصلة USB التسلسلية. راجع SPEC.md §13 للتصميم الكامل، بما في ذلك ما لا يغطيه نموذج التهديد.

ليتني أملك طابعة ثلاثية الأبعاد…``` ┌──────────┐ 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, …
## لماذا
أجهزة قراءة بصمات الأصابع عبر USB لنظام لينكس نادرة ومكلفة، والموجودة منها (Validity وSynaptics وما إلى ذلك) تمت هندستها عكسيًا عبر برامج تشغيل libfprint غير مستقرة تتعطل مع تحديثات البرامج الثابتة من المورد. بروتوكول Grow R503 **عام**، وجانب Arduino هو كودك الخاص، وطبقة التوافق مع libfprint هي مجرد D-Bus.
كما ستحصل أيضًا على قارئ بصمات يمكنك قراءة مصدره بالكامل من الأعلى إلى الأسفل.
## قائمة المكونات
| الجزء | ملاحظات | التكلفة التقريبية |
|------|-------|------|
| مستشعر بصمات الأصابع السعوي Grow R503 | النوع الدائري ذو الحلقة RGB | ~$10 |
| لوحة Arduino Uno R3 / Nano / Mega / أي لوحة ATmega328 | أي شيء يشغّل SoftwareSerial | $5–$25 |
| 4–6 أسلاك توصيل | Dupont / لوحة تجارب | لا تُذكر |
هذا كل شيء. **لا حاجة لمحوّل مستويات ولا مقسم جهد** — انظر [`SPEC.md` §3.1](https://github.com/matpb/linux-fingerprint-r503/blob/HEAD/SPEC.md) لمعرفة السبب (خط RX في R503 يتحمل 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، فاقطع سلك توصيل (pigtail) من 6 سنون من نوع JST-SH إلى Dupont لتوزيع الأسلاك للخارج. يكون اللون البني أخضر أحيانًا اعتمادًا على البائع — تحقق من السلك الذي يتصل بدبوس RXD في موصل JST، وليس من اللون.
تم الاختبار على Fedora 44 KDE؛ يجب أن يعمل على أي توزيعة قائمة على systemd مع
fprintd وpam_fprintd وسلسلة أدوات Rust حديثة.
حزم النظام:
| التوزيعة | البناء | وقت التشغيل |
|---|---|---|
| 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+، وarduino-cli على $PATH.
هل لديك TPM2؟```bash ls /dev/tpmrm0 && tpm2_pcrread sha256:7 | head -3
إذا نجح كلاهما، يمكن لمضيفك استخدام مسار المفتاح المُختوم. إذا كان `/dev/tpmrm0`
مفقودًا (عتاد أقدم، أو TPM معطّل في BIOS، أو جهاز افتراضي بدون TPM افتراضي)،
فالتزم بتدفق المفتاح الافتراضي النصي الصريح.
## البناء والتثبيت
### 1. وميض البرنامج الثابت
افتح `firmware/r503fp/r503fp.ino` في Arduino IDE وارفعه. أو باستخدام
`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. ستعرض بيئة التطوير المتكاملة تثبيته عند أول تجميع.
إذا فشل أمر arduino-cli upload مع ظهور not in sync: resp=0x7e، فإن محمّل الإقلاع (bootloader) لديك هو النوع الآخر — بدّل atmega328 ↔ atmega328old وأعد المحاولة. كلاهما يعمل؛ الفرق هو فقط معدّل الباود لمحمّل الإقلاع.
يتطلب Rust 1.95+.```bash cd pcside/daemon cargo build --release
### 3. التثبيت```bash
sudo bash pcside/daemon/dist/install.sh
That script:
target/release/r503d إلى /usr/local/bin/r503d/var/lib/r503d/ (بصلاحية 0700 root:root) للمفتاح والحالة، و
سجل فتحات المستخدم/dev/r503 وتقفل
عقدة الجهاز على root:root 0600 (فقط الخفي الذي يعمل بصلاحية root يحتاجها؛
هذا يغلق المسار الافتراضي 0660 root:dialout بحيث لا يمكن لأي مستخدم محلي آخر
فتح المنفذ — تدقيق أمني 2026-05-28 / H1). النتيجة: بعد
التثبيت، أي أمر يدوي arduino-cli/serial-monitor ضد /dev/r503
يتطلب sudo./etc/systemd/system/r503d.service)net.reactivated.Fprint/usr/share/polkit-1/actions/net.reactivated.fprint.device.r503d.policy)
المستخدم من قبل بوابة التحقق من هوية المتصلإنه idempotent — أعد تشغيله بعد كل cargo build --release لإعادة
نشر الثنائي الجديد.
Nano المُومض حديثًا غير مقترن — قد يتحدث الخفي معه لكن البرنامج الثابت سيرفض كل أمر مؤطّر. اختر أحد التدفقين أدناه؛ كلاهما ينتهي بـ Nano مقترن وخفي يعمل. التدفق المُغلّف بـ TPM موصى به إذا كان مضيفك يحتوي على TPM2 (انظر المتطلبات الأساسية للفحص السريع).
ملف الاشتراك الاختياري (/etc/r503d/allow-pair) المستخدم في كلا التدفقين موجود
لمنع مهاجم يندفع إلى مكتبك حاملًا Nano خاصًا به — الاقتران بدون صلاحية root
مستحيل. الأمر r503d --pair يحذف العلامة قبل إرسال المفتاح إلى Nano:
إذا تعطل المضيف بين إتمام الجانب الخاص بـ Nano وحفظ جانب المضيف،
تكون البوابة قد أُغلقت بالفعل، لذا تتطلب محاولة الاقتران التالية
من المسؤول إعادة touch للعلامة. وأي خروج قبل الإرسال
(لا توجد علامة، أو "already paired") يترك العلامة سليمة لإعادة المحاولة.
استخدم هذا الخيار إذا لم يكن لديك جهاز 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
لم يتم تضمين أي محتوى في الرسالة بعد "INPUT:". يرجى إعادة إرسال الجزء (chunk) المطلوب ترجمته.```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)
نفس العملية، مع إضافة --seal-tpm. يتم ختم المفتاح المُنشأ على PCR7
(سياسة Secure Boot + المفاتيح) ويُكتب إلى /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
The input chunk is empty — there is no content to translate. Please provide the actual Markdown text for chunk 19 of 37.```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)
تحديثات النواة، وتحديثات initrd، وتحديثات البرامج الثابتة UEFI عبر fwupd، وتحديثات grub2
لا تغير PCR7 ولا تتطلب إعادة ختم. يتغير PCR7 فقط
عند تعديل سياسات Secure Boot، أو تسجيلات MOK، أو نقل القرص
إلى مضيف مختلف — وعندها يرفض الخفي التشغيل مع
TPM_RC_POLICY_FAIL ويستعيد dist/reseal-tpm.sh العمل خلال ~90 ثانية.
انظر Recovery: PCR7 changed.
fprintd-enroll mat
fprintd-verify mat
sudo whoami
كل من حوارات بصمة الإصبع في إعدادات KDE (Plasma 6) ومركز تحكم GNOME تقود `r503d` تمامًا كما تقود `fprintd` الرسمي.
### إعادة الاقتران / تدوير المفاتيح
إذا كنت تريد مفتاحًا جديدًا (مفتاح مخترَق، أو تخطيط لتبديل عتاد، أو بسبب الحذر الشديد):```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
طابِق مسار الاقتران الأصلي. إذا كنت قد استخدمت في البداية --seal-tpm، فقم بالتدوير باستخدام --seal-tpm — وإلا فإن عملية التدوير ستخفضك بصمت إلى مفتاح نص عادي على القرص.
إذا كنت قد استخدمت --pair --seal-tpm ثم غيّرت لاحقًا شيئًا يقيسه PCR7 (إيقاف تشغيل Secure Boot أو تشغيله، تسجيل MOK جديد، نقل القرص إلى جهاز آخر)، سيرفض البرنامج الخفي البدء مع رسالة في journal حول TPM_RC_POLICY_FAIL. الاسترداد يتم بأمر واحد:```bash
sudo bash pcside/daemon/dist/reseal-tpm.sh
يوقف السكربت `r503d`، ويعيد وميض `firmware/r503fp_wipe/` لمسح EEPROM الخاص بـ Nano، ويعيد وميض البرنامج الثابت الرئيسي، وينشئ `/etc/r503d/allow-pair`، ويشغّل `r503d --reseal-tpm` لتوليد مفتاح جديد مختوم بـ *الحالي* PCR7، ثم يعيد تشغيل الخفيّة. الوقت الفعلي: ~90 ثانية. يتم الحفاظ على الأصابع المسجّلة — القوالب موجودة على فلاش مستشعر R503، وليس على Nano.
يتطلب السكربت توفّر `arduino-cli`. إذا كان مثبّتًا في `$HOME/.local/bin` الخاص بمستخدمك، فسيتم اكتشافه تلقائيًا عبر `$SUDO_USER`؛ بخلاف ذلك، عيّن `ARDUINO_CLI=/full/path/to/arduino-cli` قبل التشغيل.
### الاسترداد: فقدان `state.json` (عدم تزامن العداد)
إذا كان مفتاح المضيف سليمًا لكن `/var/lib/r503d/state.json` مفقود أو تمت استعادته إلى نسخة قديمة (استعادة نسخة احتياطية قديمة، حذف عرضي)، فإن عداد الخفيّة يتأخر عن `last_seen` في Nano وكل أمر مُؤطَّر يرتد بـ `ERR replay`. يُشير `r503d --status` إلى ذلك؛ والحل هو أمر واحد:```bash
sudo systemctl stop r503d
sudo r503d --resync # reads Nano last_seen, sets host counter to last_seen+1
sudo systemctl start r503d
لا إعادة اقتران، لا إعادة وميض — المفتاح لا يتحرك أبدًا. استعلام status الذي يعتمد عليه --resync
غير مُصادَق عليه، لكنه يمكنه فقط تحريك عدّاد المضيف إلى الأمام
ليطابق ما التزمت به Nano بالفعل، لذا لا يمكنه أبدًا جعل إطار قديم
قابلاً لإعادة الإرسال (في أسوأ الحالات، يفرض MITM كاذب ERR replay آخر، وهو ما
كان يمكنه فعله بالفعل عن طريق تشويش الإطارات). انظر SPEC.md §13.11.
يحتاج --unpair المُصادَق عليه إلى المفتاح للتفويض. إذا اختفت جميع
النسخ الموجودة على القرص (تعطل القرص، rm عرضي، حذف كل من key و key.bak
أو فقدان blob key.tpm), فأنت بحاجة إلى مخرج الطوارئ reflash-to-wipe
— وهو نفس الإجراء الذي تؤتمته dist/reseal-tpm.sh لحالة
PCR7-changed أعلاه:```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
إذا أبلغ `sudo arduino-cli` عن "الأمر غير موجود" (arduino-cli موجود في `~/.local/bin`، وليس في `PATH` الخاص بـ root)، فقم بتشغيله كـ `sudo env "PATH=$PATH" arduino-cli …` أو أعطِ المسار المطلق.
هذا ليس بابًا خلفيًا يمكن للمهاجم استخدامه: إعادة الاقتران تتطلب صلاحيات root على المضيف (ملف الاشتراك وواجهة `--pair` CLI كلاهما يتطلب root)، لذا لا يمكن جعل Nano المعاد وميضه موثوقًا دون أن تكون root بالفعل.
### إلغاء التثبيت```bash
sudo bash pcside/daemon/dist/uninstall.sh
يعيد كل شيء، يزيل الإخفاء عن fprintd، يُبقي /var/lib/r503d/ (المفتاح،
الحالة، المستخدمون) في موضعه في حال أردت إعادة التثبيت لاحقًا. احذف ذلك
الدليل يدويًا إذا أردت بداية نظيفة تمامًا.
يشغّل الـ Arduino برنامجًا ثابتًا صغيرًا ببروتوكول ASCII (firmware/r503fp/)
الذي يستخدم البروتوكول الثنائي الأصلي R30x ("Sync Word") الخاص بـ R503 على
جانب UART ويتبادل أوامر نصية سطرية مع المضيف عبر
USB-CDC: ping, info, enroll N, verify, delete N, clear,
led off. البروتوكول الكامل v1 في SPEC.md §5.
منذ fw=1.0 (المرحلة E من العمل على القناة الموثّقة v2)، كل
أمر واستجابة يُغلّفان في إطار C <counter> <body> M <mac> /
R <counter> <seq> <body> M <mac>، يُضاف إليه MAC باستخدام SipHash-2-4 عبر
مفتاح 128-bit مقترن عبر TOFU. تحتفظ الـ Nano بعدّاد رتيب مع موازنة التآكل
في EEPROM؛ ويحتفظ البرنامج الخفي بعدّاد مطابق في /var/lib/r503d/state.json.
محاولات إعادة الإرسال (من جانب البرنامج الثابت incoming <= last_seen) تُرفض كـ
ERR replay؛ الإطارات التي عُبث بها تحصل على ERR mac_invalid. المواصفات الكاملة، نموذج التهديد
والقيود المعروفة في SPEC.md §13.
البرنامج الخفي في Rust (r503d) يتحدث عبر D-Bus على net.reactivated.Fprint — بتّة ببتّة
نفس الواجهة التي يعرضها fprintd الأصلي — لذلك كل عميل fprintd
يعمل دون تعديل. ملف JSON جانبي في /var/lib/r503d/users.json يربط
(المستخدم، الإصبع) بفهارس الفتحات في ذاكرة الفلاش الداخلية لـ R503.
التخطيط:``` 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)
## نموذج الأمان — ملخص سريع
المصادقة على مستوى الواجهة تستهدف تهديدًا محددًا —
**"خادمة شريرة مع خمس دقائق و Nano احتياطي"** بالإضافة إلى عملية محلية معادية
على `/dev/r503` — وليس دولًا قومية أو مهاجمي عتاد مع
مختبرات. النشر لسطح مكتب أحادي المستخدم مع قائمة موثقة خارج النطاق.
نموذج التهديد الكامل موجود في [`SPEC.md` §13.1](https://github.com/matpb/linux-fingerprint-r503/blob/HEAD/SPEC.md)؛
أدلة التنفيذ والمراجعة موجودة في
[`docs/REVIEW-2026-05-28.md`](https://github.com/matpb/linux-fingerprint-r503/blob/HEAD/docs/REVIEW-2026-05-28.md). مراجعة
عدائية منفصلة لتصعيد الامتيازات (2026-05-28) وتمرير التحقق/المعالجة
لكل ادعاء موجودة في
[`docs/SECURITY-AUDIT-2026-05-28.html`](https://github.com/matpb/linux-fingerprint-r503/blob/HEAD/docs/SECURITY-AUDIT-2026-05-28.html)
و
[`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).
**محمي ضد:**
- التبديل السريع للـ Nano بوحدة معادية (لا يوجد مفتاح ← كل الإطارات تفشل في MAC).
- عملية محلية تحقن استجابات مطابقة مزيفة على `/dev/r503`. طبقتان:
عقدة الجهاز هي `root:root 0600` (قاعدة udev) والدايمون يمسكها
مع `TIOCEXCL`، لذا لا يمكن لعملية غير root فتحها — وحتى لو
استطاعت، لا تملك مفتاحًا، لذا يفشل الإطار في التحقق من MAC.
- إعادة إرسال إطارات `OK match=...` المسجلة في جلسة مستقبلية.
- العبث بقلب بتات أي حقل إطار (مقارنة MAC بزمن ثابت).
- تعطيل استنزاف العداد: قيام نظير (أو MITM لمرة واحدة أثناء `--resync`)
بدفع العداد الرتيب إلى `u64::MAX` وتعطيل القناة بشكل دائم
يتم إحباطه بواسطة سقف عداد محجوز مفروض على كلا الطرفين
(`fw=1.1+`؛ SPEC §13.4 / تدقيق 2026-05-28 DoS-2).
- حرمان خدمة المستخدم المحلي للمستشعر: بوابة فتحة التقاط واحدة
تضع حدًا أقصى لعمليات التسجيل/التحقق قيد التنفيذ ومسارات الحذف
محكومة ببوابة إجراء، لذا لا يمكن لفيضان `Start`/`Stop` (أو حذف متزامن)
تعطيل المصادقة.
- زراعة / مسح / تعداد بصمات عبر المستخدمين من قبل مستخدم محلي غير root
(مثل `mallory` التي تستدعي `Claim "root"` ثم تسجل بصمتها الخاصة) —
يتم التحقق من هوية المتصل في كل طريقة D-Bus تأخذ `username`،
وسياسة ناقل النظام ترفض المتصلين غير التابعين لـ `wheel` عند
طبقة الوسيط.
- **هجمات القرص دون اتصال على مفتاح المضيف** *عند الاقتران بـ`--seal-tpm`*:
المفتاح على القرص مغلق بـ TPM2 إلى PCR7، لذا فإن `dd` لقسم
غير مُحمّل أو تبديل SSD إلى مضيف معادٍ ينتج نصًا مشفرًا فقط.
لا يُفك التغليف إلا على نفس الجهاز ضمن نفس سياسة Secure Boot.
انظر [SPEC §13.12](https://github.com/matpb/linux-fingerprint-r503/blob/HEAD/SPEC.md).
**غير محمي ضد:**
- اختراق root على المضيف (المفتاح في `/var/lib/r503d/key`، `0600 root:root`).
يمكن لـ root على مضيف يعمل فك غلق النسخة المغلقة بـ TPM أيضًا — فالإغلاق
يخفف الهجمات *دون اتصال*، وليس الهجمات *أثناء التشغيل*.
- هجوم مادي على الـ Nano (قراءة EEPROM خلال ~30 ثانية باستخدام ISP؛ إزالة غطاء الشريحة؛ إلخ).
- هجوم إعادة وميض البرنامج الثابت (محمل إقلاع Arduino لا يملك توقيعًا — ولكن
إعادة الاقتران تتطلب root على المضيف، لذا لا يمكن جعل Nano مُعاد وميضه
موثوقًا دون اختراق المضيف على أي حال).
- اختراق جانب R503 (بروتوكول R30x لا يحتوي على أي مصادقة؛ خارج نطاقنا).
- **الوضعية التشفيرية.** MACs بتقنية SipHash-2-4، مفتاح مشترك 128-بت،
مخرجات MAC بطول 64-بت، مدخلات MAC مفصولة بالنطاق. تطبيقان مستقلان
(C++ مكتوبة يدويًا على AVR مع اختبار ذاتي KAT عند الإقلاع؛
Rust مكتوبة يدويًا على المضيف، تم التحقق من تطابقها بتًا بتًا مع مكتبة
`siphasher` الخارجية على 1024 متجهًا عشوائيًا في CI). مقارنة MAC على المضيف تستخدم
`subtle::ConstantTimeEq`. محللات الأسلاك تم فحصها بالتشويش الخاص بالخصائص في كل تشغيل CI
(~135 000 إدخال). `cargo audit` نظيف. مفتاح SipHash مغلف في
`zeroize::Zeroizing<...>` بحيث يُمسح عند الإفلات (وكذلك
مخازن مدخلات MAC لكل إطار). هدف libFuzzer من `cargo fuzz`
يُشحن في `pcside/daemon/fuzz/` لتشغيل مجموعات نصوص طويلة على
nightly. لا يوجد تدقيق بشري خارجي مدفوع — سيكون ذلك
قيّمًا، نرحب بـ PRs.
نموذج التهديد الكامل مع الأساس المنطقي: [`SPEC.md` §13.1](https://github.com/matpb/linux-fingerprint-r503/blob/HEAD/SPEC.md).
## القيود
- **تعدد المستخدمين يعمل، لكن فقط لأعضاء `wheel`.** يتم التحقق
من هوية المتصل في كل طريقة D-Bus تأخذ `username` (`Claim`,
`EnrollStart`, `VerifyStart`, `ListEnrolledFingers`,
`DeleteEnrolledFingers`); الطلبات الذاتية و`uid 0` (PAM) تنجح
بصمت، والوصول عبر المستخدمين من متصل غير root يُرفض برسالة
`net.reactivated.Fprint.Error.PermissionDenied`. سياسة ناقل النظام
تقيد أيضًا الحسابات التي يمكنها حتى بدء محادثة: فقط
`root` وأعضاء `wheel` يصلون إلى الخفي، وكل من عدا ذلك يحصل على
`org.freedesktop.DBus.Error.AccessDenied` عند طبقة الوسيط.
هل تحتاج إلى تسجيل عبر المستخدمين؟ كن root: `sudo fprintd-enroll target-user`.
هل تحتاج إلى تخفيف بوابة العبور بين المستخدمين لبيئة كشك / مختبر متعدد المستخدمين؟ ضع
قاعدة JS في `/etc/polkit-1/rules.d/` تستهدف
[`net.reactivated.fprint.device.setusername`](https://gitlab.freedesktop.org/libfprint/fprintd/-/blob/master/src/net.reactivated.fprint.device.policy.in)
— اسم الإجراء يطابق fprintd المنبع حرفيًا.
- **قارئ واحد.** يعرض الخفي كائن Device واحدًا على D-Bus.
إعدادات القارئات المتعددة تحتاج إلى امتداد لـ Manager.
- **لا يُصدر `PropertiesChanged`** للخاصيتين `finger-present` / `finger-needed`
(خصائص التلميح). كل عميل fprintd شائع (PAM, KDE Settings, GNOME)
يعتمد على إشارتي `EnrollStatus` / `VerifyStatus` (اللتان تُصدران)،
وليس على تلك التلميحات التي تُستجوب — لكن عميلًا صارمًا يقوم بـ
`Get + PropertiesChanged` سيرى قيمًا قديمة.
- **Nano واحد = نقطة فشل واحدة.** إذا تعطل Nano، يختفي تسجيل الدخول بالبصمة
حتى تعيد وميض نسخة احتياطية وتعيد الاقتران. أبقِ طريقة مصادقة
بكلمة مرور مفعّلة كاحتياط.
- **فقدان State.json قابل للاسترداد بأمر واحد.** إذا فُقد `state.json`
بينما لا يزال البرنامج الثابت يحمل `last_seen` مرتفعًا، يصطدم الخفي بـ `ERR replay`
عند أول إرسال. نفّذ `sudo r503d --resync` لقراءة عداد الـ Nano وإعادة
محاذاة المضيف — لا حاجة لإعادة اقتران. انظر [`SPEC.md` §13.11](https://github.com/matpb/linux-fingerprint-r503/blob/HEAD/SPEC.md).
## استكشاف الأخطاء وإصلاحها```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
إذا لم يعمل البرنامج الخفي أو لم يستجب المستشعر أبدًا، فإن الإصلاح الأكثر شيوعًا هو التوصيلات — انظر SPEC.md §3، خاصةً ملاحظة "no voltage divider" في §3.1. يوجد دليل تشغيل أكثر تفصيلًا في docs/TROUBLESHOOTING.md.
MIT — انظر LICENSE.
fprintd — لتصميم واجهة D-Bus نظيفة يمكن لهذا البرنامج الخفي تنفيذها دون قراءة مصدر libfprint أبدًا./etc/dbus-1/system.d/net.reactivated.Fprint.conf) — فقط أعضاء root و
wheel يمكنهم التحدث إلى الخفي؛ أي شخص آخر يحصل على AccessDenied
عند الوسيط، قبل أن يرى الخفي الاستدعاءr503d.service