Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
linux-fingerprint-r503 — Grow R503 센서 + Arduino + Rust 기반 fprintd 대체 데몬을 사용한 Linux 데스크톱 지문 로그인 | Kitploit
도구/GitHubGitHub/matpb/linux-fingerprint-r503
Embedded Systems SecurityCryptographyPenetration TestingHardware SecurityAuthenticationRed Teaming
GitHubmatpb/linux-fingerprint-r503

linux-fingerprint-r503

Grow R503 센서 + Arduino + Rust 기반 fprintd 대체 데몬을 사용한 Linux 데스크톱 지문 로그인

저장소 보기
44232개월 전Kitploit 검토 완료

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

linux-fingerprint-r503 — Grow R503 + Arduino를 사용한 Linux 지문 로그인

CI r503d firmware Rust 2024 Platform: Linux License: MIT

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을 참조하세요.

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

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, …

root@kitploit:~
## 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 / 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

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

root@kitploit:~
둘 다 성공하면 호스트에서 봉인된 키 경로를 사용할 수 있습니다. `/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)뿐입니다.

2. 데몬 빌드하기

Rust 1.95+가 필요합니다.```bash cd pcside/daemon cargo build --release

root@kitploit:~
### 3. 설치```bash
sudo bash pcside/daemon/dist/install.sh

해당 스크립트:

  • target/release/r503d를 /usr/local/bin/r503d에 설치합니다
  • 키, 상태 및 사용자 슬롯 레지스트리를 위해 /var/lib/r503d/를 생성합니다 (모드 0700 root:root)
  • Arduino를 /dev/r503로 노출하고 디바이스 노드를 root:root 0600으로 잠그는 udev 규칙을 작성합니다 (데몬만이 루트로 실행되므로 필요합니다; 이는 기본 0660 root:dialout 경로를 닫아 다른 로컬 사용자가 포트를 열 수 없도록 합니다 — 보안 감사 2026-05-28 / H1). 결과: 설치 후, /dev/r503에 대한 수동 arduino-cli/serial-monitor 명령은 sudo가 필요합니다.
  • systemd 유닛(/etc/systemd/system/r503d.service)을 설치합니다
  • net.reactivated.Fprint에 대한 D-Bus 자동 실행 항목을 재정의합니다
  • 호출자 식별 게이트에서 사용하는 polkit 액션(/usr/share/polkit-1/actions/net.reactivated.fprint.device.r503d.policy)을 설치합니다

멱등성을 가집니다 — cargo build --release 후에 다시 실행하여 새 바이너리를 재배포할 수 있습니다.

4. Nano와 데몬 페어링

새로 플래시된 Nano는 페어링되지 않은 상태입니다 — 데몬이 통신을 시도하지만 펌웨어가 모든 프레임 명령을 거부합니다. 아래 두 가지 흐름 중 하나를 선택하세요; 둘 다 페어링된 Nano와 작동하는 데몬으로 끝납니다. 호스트에 TPM2가 있는 경우 TPM 봉인 흐름이 권장됩니다 (빠른 확인은 전제 조건 참조).

두 흐름 모두에서 사용되는 옵트인 파일(/etc/r503d/allow-pair)은 공격자가 자신의 Nano를 가지고 책상으로 달려오는 것을 막기 위해 존재합니다 — 루트 없이는 페어링이 불가능합니다. r503d --pair는 키를 Nano로 보내기 전에 마커를 삭제합니다: Nano 측 커밋과 호스트 측 영속화 사이에 호스트가 충돌하면 게이트가 이미 닫혀 있으므로, 다음 페어링 시도는 관리자가 touch 명령으로 마커를 다시 생성해야 합니다. 전송 전 중단(마커 없음 또는 "이미 페어링됨")은 마커를 그대로 두어 재시도할 수 있습니다.

4a. 평문 키 페어링 (기본)

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

root@kitploit:~
root@kitploit:~
__                        ___       _

/ / ___ __ _ ___ / _ \ _ () ___ _ __ / / / _ \ / ` |/ _ \ / /)/| '| / _|/ _ | '
/ /
| () | (| | () | / __/ | | | _ \ () | | | | _/_/ _, |_/ / || ||/___/|| || |__/

root@kitploit:~
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`;

$setupInfo = @{
    region = "us-east-1"
    domain_name = "lab.corpo.com"
    create_ad_users = $true
    ec2_key_pair_name = "admin"
    use_elastic_ips = $true
    vpn_users = 0
    services_password = "MySecretPassword123"
    vpn_password = "MySecretVPNPassword456"
}
$setupInfo | ConvertTo-Json | Out-File setup-parameters.json

cfn-create -t main-setup.cfn.yml -p setup-parameters.json setup-stack
```

<br>
<br>
<br>
<p align="center">
 <img src="https://raw.githubusercontent.com/matpb/linux-fingerprint-r503/img/main_img.webp" alt="Main Image">
</p>

스택 생성 중에 CloudFormation은 리소스를 생성하고, Active Directory 도메인을 구성하고, 사용자를 생성하고, VPN 서버를 설정합니다. 이 프로세스는 일반적으로 완료하는 데 45~50분이 소요됩니다.

### **접근**

스택이 `CREATE_COMPLETE` 상태에 도달하면:

**옵션 A: 웹 인터페이스 (가장 쉬운 설정)**

- Amazon Web Services(<[email protected]>)로부터 제목이 `Your AWS CloudFormation stack 'setup-stack' has been created.`인 이메일을 받게 됩니다.
- 이 이메일에는 VPN 구성을 다운로드하고, Windows 인스턴스에 연결하고, 도메인 컨트롤러에 접근할 수 있는 웹 인터페이스로의 직접 링크가 포함되어 있습니다.

<p align="center">
 <img src="https://raw.githubusercontent.com/matpb/linux-fingerprint-r503/img/cloudformation_email.webp" alt="CloudFormation Email" width="80%">
</p>
 <p align="center"><em></em></p>

 `https://lab.corpo.com` 링크를 클릭하면 웹 인터페이스가 열립니다.```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 업데이트, `fwupd` UEFI 펌웨어 업데이트 및 grub2 업데이트는 PCR7을 **변경하지 않으며** 재봉인이 필요하지 않습니다. PCR7은 Secure Boot 정책 편집, MOK 등록 또는 디스크를 다른 호스트로 이동할 때만 변경됩니다. 이때 데몬은 `TPM_RC_POLICY_FAIL` 오류로 시작을 거부하며 `dist/reseal-tpm.sh`가 약 90초 안에 복구합니다. 참조: [복구: PCR7 변경됨](#recovery-pcr7-changed-need-to-reseal).

### 5. 등록 및 확인```bash
# Enroll a finger (use KDE Settings → Users → Fingerprint Auth for a GUI):
fprintd-enroll mat

# Verify:
fprintd-verify mat

# sudo with finger:
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`으로 로테이트하십시오. 그렇지 않으면 로테이트 과정에서 자동으로 디스크의 일반 텍스트 키로 다운그레이드됩니다.

### 복구: PCR7이 변경됨, 다시 봉인해야 함

`--pair --seal-tpm`을 사용했고 나중에 PCR7이 측정하는 항목(보안 부팅 켜짐/꺼짐, 새 MOK 등록, 디스크를 다른 상자로 이동)이 변경되면, 데몬이 시작되지 않고 `TPM_RC_POLICY_FAIL`에 대한 저널 메시지를 표시합니다. 복구는 하나의 명령어입니다:```bash
sudo bash pcside/daemon/dist/reseal-tpm.sh
```
스크립트는 `r503d`를 중단하고, `firmware/r503fp_wipe/`를 다시 플래시하여 Nano EEPROM을 초기화한 후, 메인 펌웨어를 다시 플래시하고, `/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`이 사라졌거나 롤백된 경우(오래된 백업 복원, 실수로 삭제), 데몬의 카운터가 Nano의 `last_seen`보다 뒤처져 모든 프레임 명령이 `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
```
재페어링 없음, 재플래시 없음 — 키는 절대 이동하지 않습니다. `--resync`이 의존하는 `status` 쿼리는 인증되지 않았지만, 호스트 카운터를 Nano가 이미 커밋한 값과 일치하도록 *앞으로*만 이동시킬 수 있으므로 오래된 프레임을 재생 가능하게 만들 수 없습니다 (최악의 경우 거짓된 MITM이 다른 `ERR replay`를 강제할 수 있는데, 이는 프레임을 왜곡함으로써 이미 가능한 일입니다). 참조: [`SPEC.md` §13.11](https://github.com/matpb/linux-fingerprint-r503/blob/main/SPEC.md).

### 복구: 호스트 키를 완전히 분실한 경우

인증된 `--unpair`는 권한 부여를 위해 키가 필요합니다. 디스크 상의 모든 복사본이 사라진 경우(디스크 충돌, 실수로 rm, `key` 및 `key.bak` 둘 다 삭제, 또는 `key.tpm` blob 손실), **reflash-to-wipe** 탈출구가 필요합니다 — 위의 PCR7 변경 사례에 대해 `dist/reseal-tpm.sh`가 자동화하는 것과 동일한 절차입니다:```bash
sudo systemctl stop r503d
# /dev/r503 is root:root 0600 since install (audit H1), so the uploads need root.
sudo arduino-cli upload --fqbn arduino:avr:nano:cpu=atmega328 --port /dev/r503 firmware/r503fp_wipe/
# Wait ~1s for the wipe to complete (LED starts blinking — that's the wipe sketch).
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`가 command-not-found를 보고한다면 (arduino-cli는 사용자의 `~/.local/bin`에 있으며, root의 `PATH`에 없습니다), `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/`)를 실행하며, UART 측에서 R503의 네이티브 R30x("Sync Word") 바이너리 프로토콜과 통신하고, USB-CDC를 통해 호스트와 라인 지향 텍스트 명령(`ping`, `info`, `enroll N`, `verify`, `delete N`, `clear`, `led off`)을 교환합니다. 전체 v1 프로토콜은 [`SPEC.md` §5](https://github.com/matpb/linux-fingerprint-r503/blob/main/SPEC.md)에 있습니다.

`fw=1.0`(v2 인증 채널 작업의 마일스톤 E) 이후, 모든 명령과 응답은 `C <counter> <body> M <mac>` / `R <counter> <seq> <body> M <mac>` 프레임으로 감싸지며, TOFU로 페어링된 128비트 키로 SipHash-2-4를 사용해 MAC됩니다. Nano는 EEPROM에 마모 평준화된 단조 카운터를 유지하고, 데몬은 `/var/lib/r503d/state.json`에 일치하는 카운터를 유지합니다. 재시도 시도(펌웨어 측에서 `incoming <= last_seen`)는 `ERR replay`로 거부되고, 변조된 프레임은 `ERR mac_invalid`로 거부됩니다. 전체 사양, 위협 모델 및 알려진 제한 사항은 [`SPEC.md` §13](https://github.com/matpb/linux-fingerprint-r503/blob/main/SPEC.md)에 있습니다.

Rust 데몬(`r503d`)은 D-Bus를 통해 `net.reactivated.Fprint`에서 통신합니다. 이는 업스트림 `fprintd`가 노출하는 인터페이스와 완전히 동일하므로, 모든 `fprintd` 클라이언트가 수정 없이 작동합니다. `/var/lib/r503d/users.json`의 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)
```
## 보안 모델 — 간략 요약

와이어 레벨 인증은 특정 위협을 대상으로 합니다 — **"5분과 여분의 나노(Nano)가 있는 사악한 메이드"** 및 `/dev/r503`에 대한 적대적 로컬 프로세스 — 국가 수준이거나 연구실을 갖춘 하드웨어 공격자가 아닙니다. 문서화된 범위 외 목록과 함께 단일 사용자 데스크탑 배포. 전체 위협 모델은 [`SPEC.md` §13.1](https://github.com/matpb/linux-fingerprint-r503/blob/main/SPEC.md)에 있으며, 구현 및 검토 증거는 [`docs/REVIEW-2026-05-28.md`](https://github.com/matpb/linux-fingerprint-r503/blob/main/docs/REVIEW-2026-05-28.md)에 있습니다. 별도의 적대적 권한 상승 감사(2026-05-28) 및 각 주장에 대한 검증/수정 패스는 [`docs/SECURITY-AUDIT-2026-05-28.html`](https://github.com/matpb/linux-fingerprint-r503/blob/main/docs/SECURITY-AUDIT-2026-05-28.html) 및 [`docs/SECURITY-AUDIT-2026-05-28-VALIDATION.html`](https://github.com/matpb/linux-fingerprint-r503/blob/main/docs/SECURITY-AUDIT-2026-05-28-VALIDATION.html)에 있습니다.

**방어됨:**
- 나노를 적대적 장치로 핫스왑 (키 없음 → 모든 프레임이 MAC 실패).
- 로컬 프로세스가 `/dev/r503`에 가짜 일치 응답을 주입. 두 계층: 장치 노드는 `root:root 0600`(udev 규칙)이며 데몬이 `TIOCEXCL`로 잡고 있으므로 비-root 프로세스는 열 수 없습니다 — 설령 열 수 있다 하더라도 키가 없어 프레임이 MAC 검증에 실패합니다.
- 녹화된 `OK match=...` 프레임을 향후 세션에서 재전송.
- 모든 프레임 필드의 비트 플립 변조 (일정 시간 MAC 비교).
- 카운터 소진 브릭: 피어(또는 `--resync` 중 일회성 MITM)가 단조 카운터를 `u64::MAX`로 밀어 채널을 영구적으로 고정시키는 것은 양쪽 끝에서 시행되는 예약된 카운터 상한으로 차단됩니다 (`fw=1.1+`; SPEC §13.4 / 2026-05-28 감사 DoS-2).
- 센서의 로컬 사용자 서비스 거부: 단일 캡처 슬롯 게이트가 진행 중인 등록/확인 작업을 제한하고 삭제 경로는 액션 게이트되어 있으므로 `Start`/`Stop`(또는 동시 삭제) 플러드가 인증을 막을 수 없습니다.
- 로컬 비-root 사용자(예: `mallory`가 `Claim "root"`를 호출한 후 자신의 지문을 등록)의 교차 사용자 지문 심기/지우기/열거 — 호출자 신원은 `username`을 받는 모든 D-Bus 메서드에서 확인되며, 시스템 버스 정책은 브로커 계층에서 비-`wheel` 호출자를 거부합니다.
- **호스트 키에 대한 오프라인 디스크 공격** *`--seal-tpm`과 함께 사용하는 경우*: 디스크의 키는 TPM2로 PCR7에 봉인되어 있으므로 언마운트된 파티션의 `dd` 또는 적대적인 호스트로의 SSD 교체는 암호문만 생성합니다. 동일한 Secure Boot 정책 하에서 동일한 머신에서만 풀립니다. [SPEC §13.12](https://github.com/matpb/linux-fingerprint-r503/blob/main/SPEC.md) 참조.

**방어되지 않음:**
- 호스트 루트 침해 (키는 `/var/lib/r503d/key`, `0600 root:root`에 있음). 실행 중인 호스트의 루트는 TPM 봉인 변형도 풀 수 있습니다 — 봉인은 *오프라인* 공격을 무디게 할 뿐, 온라인 공격은 아닙니다.
- 나노에 대한 물리적 공격 (ISP를 통한 EEPROM 읽기 ~30초; 칩 디캡 등).
- 펌웨어 재플래시 공격 (Arduino 부트로더는 서명이 없음 — 하지만 재페어링에는 호스트의 루트 권한이 필요하므로, 재플래시된 나노는 어차피 호스트 침해 없이는 신뢰할 수 없습니다).
- R503 측 침해 (R30x 프로토콜에는 인증이 전혀 없음; 당사 범위 밖).
- **암호 상태.** SipHash-2-4 MAC, 128비트 공유 키, 64비트 MAC 출력, 도메인 분리 MAC 입력. 두 개의 독립적인 구현 (AVR에서 부트 타임 KAT 자체 테스트가 포함된 수작업 C++; 호스트에서 수작업 Rust, CI에서 1024개의 무작위 벡터에 대해 타사 `siphasher` 크레이트와 비트 단위 교차 검증). 호스트 MAC 비교는 `subtle::ConstantTimeEq` 사용. 와이어 파서는 모든 CI 실행에서 속성 퍼징됨 (~135,000 입력). `cargo audit` 클린. SipHash 키는 `zeroize::Zeroizing<...>`로 래핑되어 드롭 시 지워짐 (프레임별 MAC 입력 버퍼도 마찬가지). `cargo fuzz` libFuzzer 타겟이 `pcside/daemon/fuzz/`에 포함되어 nightly에서 긴 코퍼스 실행 가능. 유료 타사 인간 감사는 없음 — 여전히 가치가 있을 것이며, PR 환영.

합리성을 포함한 전체 위협 모델: [`SPEC.md` §13.1](https://github.com/matpb/linux-fingerprint-r503/blob/main/SPEC.md).

## 제한 사항

- **다중 사용자는 작동하지만 `wheel` 구성원에게만 해당됩니다.** `username`을 받는 모든 D-Bus 메서드(`Claim`, `EnrollStart`, `VerifyStart`, `ListEnrolledFingers`, `DeleteEnrolledFingers`)에서 호출자 신원이 확인됩니다. 자기 요청 및 `uid 0`(PAM)은 자동으로 성공하며, 비-root 호출자의 교차 사용자 요청은 `net.reactivated.Fprint.Error.PermissionDenied`로 거부됩니다. 시스템 버스 정책은 대화를 시작할 수 있는 계정을 추가로 제한합니다: `root` 및 `wheel` 구성원만 데몬에 도달할 수 있으며, 나머지는 브로커 계층에서 `org.freedesktop.DBus.Error.AccessDenied`를 받습니다. 교차 사용자 등록이 필요하신가요? 루트가 되어 `sudo fprintd-enroll target-user`를 실행하세요. 키오스크/다중 사용자 랩을 위해 교차 사용자 게이트를 완화해야 하나요? `/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)을 대상으로 하는 JS 규칙을 넣으세요 — 액션 이름은 업스트림 fprintd를 그대로 따릅니다.
- **리더 하나.** 데몬은 D-Bus에 단일 Device 객체를 노출합니다. 다중 리더 설정은 Manager에 대한 확장이 필요합니다.
- **`PropertiesChanged` 방출 없음** `finger-present` / `finger-needed` 힌트 속성에 대해. 모든 일반적인 fprintd 클라이언트(PAM, KDE 설정, GNOME)는 폴링된 힌트가 아닌 `EnrollStatus` / `VerifyStatus` 신호(방출됨)를 기반으로 작동하지만, `Get + PropertiesChanged`를 수행하는 엄격한 클라이언트는 오래된 값을 보게 됩니다.
- **단일 나노 = 단일 장애점.** 나노가 죽으면 지문 로그인은 여분을 재플래시하고 재페어링할 때까지 사용할 수 없습니다. 백업으로 비밀번호 인증 방법을 활성화해 두세요.
- **State.json 손실은 하나의 명령으로 복구 가능합니다.** 펌웨어에 여전히 높은 `last_seen`이 있는 상태에서 `state.json`이 손실되면 데몬이 첫 전송 시 `ERR replay`를 발생시킵니다. `sudo r503d --resync`를 실행하여 나노의 카운터를 읽고 호스트를 재정렬하세요 — 재페어링이 필요하지 않습니다. [`SPEC.md` §13.11](https://github.com/matpb/linux-fingerprint-r503/blob/main/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
```
If the daemon won't start or the sensor never responds, the most common
fix is the wiring — see [`SPEC.md` §3](https://github.com/matpb/linux-fingerprint-r503/blob/main/SPEC.md), particularly the
**"no voltage divider"** note in §3.1. There's a more detailed runbook
in [`docs/TROUBLESHOOTING.md`](https://github.com/matpb/linux-fingerprint-r503/blob/main/docs/TROUBLESHOOTING.md).

## License

MIT — see [LICENSE](https://github.com/matpb/linux-fingerprint-r503/blob/main/LICENSE).

## Credits

- [Adafruit_Fingerprint](https://github.com/adafruit/Adafruit-Fingerprint-Sensor-Library)
  — Arduino-side R30x protocol implementation.
- [zbus](https://github.com/dbus2/zbus),
  [serialport-rs](https://github.com/serialport/serialport-rs),
  [tokio](https://tokio.rs/) — the Rust D-Bus / serial / async stack.
- The `fprintd` project — for designing a clean D-Bus interface that
  this daemon could implement against without ever reading
  `libfprint`'s source.
도구 다운로드
  • 제한적인 시스템 버스 정책(/etc/dbus-1/system.d/net.reactivated.Fprint.conf)을 설치합니다 — root 및 wheel 그룹 구성원만 데몬과 통신할 수 있습니다; 그 외의 모든 사용자는 데몬이 호출을 보기도 전에 브로커에서 AccessDenied를 만납니다.
  • 업스트림 fprintd.service를 중지하고 마스킹합니다
  • r503d.service를 시작합니다