Linux NFC-to-PC/SC bridge for using an existing KeePassXC YubiKey HMAC-SHA1 challenge-response factor on a postmarketOS OnePlus 6 (oneplus-enchilada).
The bridge is operational. KeePassXC 2.7.12 detects both programmed YubiKey OTP slots over NFC and successfully reopened a throwaway KDBX protected by a password plus YubiKey challenge-response. The production vault was not modified.
This is the recommended path. It installs the PC/SC bridge, GNOME Secrets, py3-pyscard, and the touch-friendly YubiKey provider in one deployment.
The supported baseline is the official postmarketOS v26.06
linux-postmarketos-qcom-sdm845 package. It already contains the OnePlus 6/6T
PN553 device-tree node and the NXP NCI I²C driver; no custom kernel build or
patch is required. The deployment script checks the running kernel and refuses
to install unless nfc0 is available.
On the phone, install its native build dependencies once:
sudo apk add build-base pkgconf pcsc-lite-dev libnl3-dev
On the workstation, download or clone this repository and ensure a C compiler,
Python 3, pkg-config, pcsc-lite and libnl3 development files, OpenSSL headers,
wget, SSH, and SCP are available. Then run:
PHONE=user@oneplus.home.arpa ./scripts/deploy-phone
Set PHONE to the phone's SSH target. REMOTE_DIR optionally overrides the
home-relative Projects/oneplus-yubikey-nfc upload path. The script:
It uses the shared SSH ControlMaster path ~/.ssh/cm/%r@%h:%p and prompts for
the phone's sudo password during installation. After it completes, launch the
normal Secrets icon and follow the
touch workflow.
This works for me and is mostly written by Kimi K3. USE AT YOUR OWN RISK.
oneplus-enchilada)7.1.0-rc1-sdm845nxp-nci_i2c, exposed as nfc0AF_NFC, NFC_SOCKPROTO_RAW, ISO-DEP/ISO 14443This setup uses the official postmarketOS v26.06 kernel package, not a locally
patched kernel. The release
sdm845-oneplus-common.dtsi
defines the PN553 at I²C address 0x28, and the release
kernel configuration
enables CONFIG_NFC_NXP_NCI_I2C.
KeePassXC
│ existing PC/SC YubiKey backend and OTP APDUs
▼
pcscd (pcscd user, CAP_NET_ADMIN only)
│ libnlnfc IFD handler
▼
Linux generic-netlink + AF_NFC ISO-DEP
▼
PN553 → YubiKey OTP applet
KeePassXC and the KDBX format are unchanged. The HMAC response is never written to a keyfile. KeePassXC receives no capability; only pcscd receives CAP_NET_ADMIN, which is required to power and poll the NFC controller.
The GNOME Secrets overlay preserves password, key file, and YubiKey as three distinct KDBX4 credential contributions. Its process-local PyKeePass adapter normalizes the selected key file and applies KeePassXC's composite-key order; the component envelope and HMAC response exist only in memory.
PrivateUsers=false is required in the pcscd service override. A capability inside systemd's private user namespace does not satisfy the physical NFC device's host-namespace generic-netlink check.
vendor/ifdnlnfc/: PC/SC IFD handler based on StarGate01/ifdnlnfc revision 86703e844652ce99bfd5a4d2aa4fceb7c3fb2a5asrc/ykchal_nfc.c: standalone OTP challenge-response diagnostic probetests/ifd_contract.c: synthetic IFD boundary and error-contract checkspackaging/reader.conf: pcsc-lite reader definition for NFC adapter index 0packaging/pcscd.service.d/oneplus-nfc.conf: constrained systemd capability/address-family override and persistent daemon commandpackaging/oneplus-nfc-init.service: root PN553 rebind ordered before each pcscd.service startscripts/install-on-phone: root installation helperscripts/deploy-phone: workstation-to-phone build and deployment flowscripts/oneplus-nfc-init: bounded pre-PCSC controller initializationsecrets-overlay/gsecrets/provider/: GNOME Secrets 9.6 PC/SC YubiKey provider overlayscripts/check-kernel-nfc: non-mutating device, kernel-option, driver, and nfc0 preflightscripts/install-secrets-on-phone: pinned Secrets installation with dry-run/apply modesscripts/secrets-nfc: overlay-aware GNOME Secrets launcherThe vendored IFD handler includes local fixes for adapter lookup, netlink receive failures, cleanup after initialization errors, deterministic power-up failure, uninitialized kernel attributes, APDU types, and PC/SC output-buffer validation.
Install the IFD handler dependencies on postmarketOS:
sudo apk add build-base pkgconf pcsc-lite-dev libnl3-dev
Build the production bridge and run its synthetic contract checks:
make driver test
The diagnostic probe additionally needs OpenSSL headers and wget:
sudo apk add openssl-dev wget
make probe
make all test builds both binaries. The probe build downloads libfido2 1.16.0 and verifies SHA-256 7d86088ef4a48f9faad4ff6f41343328157849153a8dc94d88f4b5461cb29474.
Use this path when the project checkout is already on the phone:
./scripts/check-kernel-nfc
make driver test
sudo ./scripts/install-on-phone
./scripts/install-secrets-on-phone --dry-run
sudo ./scripts/install-secrets-on-phone --apply
The bridge installer:
/usr/local/lib/libnlnfc.so.0.0.0./usr/local/lib./etc/reader.conf.d/libnlnfc.pcscd.service override.pcscd.service.pcscd.socket and restarts pcscd.service.nfc0 is missingFirst update the official release packages and reboot:
sudo apk update
sudo apk upgrade
sudo reboot
After reconnecting, run:
apk policy linux-postmarketos-qcom-sdm845
./scripts/check-kernel-nfc
Do not build or flash a custom kernel merely for this project. If the official
v26.06 package is installed and the preflight still reports no nfc0, inspect
the nxp_nci_i2c probe failure:
sudo dmesg | grep -Ei 'nfc|nxp|pn553'
Users intentionally running another kernel must provide both the official OnePlus PN553 device-tree node and the four NFC options checked by the preflight script. Building and flashing such a kernel is device-recovery work outside this installer; it is not an automatic or safe fallback.
Both installation paths install Alpine's GNOME Secrets 9.6 and py3-pyscard at
their tested versions, then place the PC/SC provider only under /usr/local.
They do not patch Alpine-owned Python modules, alter the KDBX format, or create
a challenge-response keyfile.
For touch use:
/usr/local/bin/secrets-nfc.OnePlus 6 NFC 00 00 — Slot 1 or — Slot 2
credential. Secrets never guesses an unknown database's slot.Secrets preserves its native inactivity lock and clipboard-clearing behavior. Browser-extension autofill is not part of this touch workflow.
List the virtual reader:
pcsc_scan -r
Expected:
0: OnePlus 6 NFC 00 00
Then hold the YubiKey flat against the upper rear of the phone:
pcsc_scan -n -t 30
The verified card event and ATR were:
Card state: Card inserted
ATR: 3B 8D 80 01 80 73 C0 21 C0 57 59 75 62 69 4B 65 79 F9
Open KeePassXC's database credentials dialog while the key is present. The YubiKey challenge-response selector should list the programmed slots.
The diagnostic used this public 64-byte challenge for slot 2:
000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f
The NFC YubiKey and an identically programmed workstation key returned the same 20-byte response. Its value is intentionally omitted because the keys mirror a production credential.
The probe selects OTP applet AID A0000005272001, sends instruction 0x01, addresses slot 1 as 0x30 or slot 2 as 0x38, and returns the same 20-byte HMAC-SHA1 response KeePassXC expects. FIDO2 and database rekeying are not involved.
To repeat the diagnostic:
sudo build/ykchal-nfc 2 \
000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f
Do not assign file capabilities to a binary in a user-writable directory.
On the tested kernel, the PN553 needs one driver rebind immediately before its first NFC power request. The pcscd override requires oneplus-nfc-init.service; because the one-shot does not remain active, it reruns as root before every pcscd.service start.
The override removes pcscd --auto-exit: pcsc-lite 2.3.3 on this phone otherwise unlinks /run/pcscd/pcscd.comm when it exits while pcscd.socket remains active, leaving subsequent clients unable to reactivate the daemon. Verify the latest initializer run with:
systemctl status oneplus-nfc-init.service
Reboot the phone if this initializer or pcsc_scan -r later fails. Do not use rfkill toggles as a recovery mechanism for this controller.
Do not manually unbind a PN553 after an I²C error: one observed failure put both the unbind process and irq/176-nxp-nci into uninterruptible D state. A later rfkill reset also left the controller unable to power up.
Relevant timeout evidence from the diagnostic path:
generic-netlink: ETIMEDOUT (110)
nxp-nci_i2c 3-0028: NFC: Read failed with error -121
-121 is EREMOTEIO. The long-term fix belongs in the PN553 power/GPIO sequencing in the kernel, not in KeePassXC or the KDBX path.
CAP_NET_ADMIN to KeePassXC.Project-owned code and integration scripts are GPL-3.0-or-later; the root
COPYING contains the GPLv3 terms. The GNOME Secrets provider overlay is
GPL-3.0-only and adapts the GNOME Secrets 9.6 provider architecture. The
vendored ifdnlnfc IFD handler remains GPL-2.0-only; its upstream COPYING
and copyright notices are preserved. The diagnostic probe compiles
libfido2's BSD-2-Clause generic-netlink implementation from a
checksum-pinned source archive and follows OTP APDU behavior demonstrated by
GPL-3.0 ykDroid.
5 commits
Python
64.0%
Shell
16.6%
C
15.0%
Makefile
4.5%
Linux NFC-to-PC/SC bridge for using an existing KeePassXC YubiKey HMAC-SHA1 challenge-response factor on a postmarketOS OnePlus 6 (oneplus-enchilada).
The bridge is operational. KeePassXC 2.7.12 detects both programmed YubiKey OTP slots over NFC and successfully reopened a throwaway KDBX protected by a password plus YubiKey challenge-response. The production vault was not modified.
This is the recommended path. It installs the PC/SC bridge, GNOME Secrets, py3-pyscard, and the touch-friendly YubiKey provider in one deployment.
The supported baseline is the official postmarketOS v26.06
linux-postmarketos-qcom-sdm845 package. It already contains the OnePlus 6/6T
PN553 device-tree node and the NXP NCI I²C driver; no custom kernel build or
patch is required. The deployment script checks the running kernel and refuses
to install unless nfc0 is available.
On the phone, install its native build dependencies once:
sudo apk add build-base pkgconf pcsc-lite-dev libnl3-dev
On the workstation, download or clone this repository and ensure a C compiler,
Python 3, pkg-config, pcsc-lite and libnl3 development files, OpenSSL headers,
wget, SSH, and SCP are available. Then run:
PHONE=user@oneplus.home.arpa ./scripts/deploy-phone
Set PHONE to the phone's SSH target. REMOTE_DIR optionally overrides the
home-relative Projects/oneplus-yubikey-nfc upload path. The script:
It uses the shared SSH ControlMaster path ~/.ssh/cm/%r@%h:%p and prompts for
the phone's sudo password during installation. After it completes, launch the
normal Secrets icon and follow the
touch workflow.
This works for me and is mostly written by Kimi K3. USE AT YOUR OWN RISK.
oneplus-enchilada)7.1.0-rc1-sdm845nxp-nci_i2c, exposed as nfc0AF_NFC, NFC_SOCKPROTO_RAW, ISO-DEP/ISO 14443This setup uses the official postmarketOS v26.06 kernel package, not a locally
patched kernel. The release
sdm845-oneplus-common.dtsi
defines the PN553 at I²C address 0x28, and the release
kernel configuration
enables CONFIG_NFC_NXP_NCI_I2C.
KeePassXC
│ existing PC/SC YubiKey backend and OTP APDUs
▼
pcscd (pcscd user, CAP_NET_ADMIN only)
│ libnlnfc IFD handler
▼
Linux generic-netlink + AF_NFC ISO-DEP
▼
PN553 → YubiKey OTP applet
KeePassXC and the KDBX format are unchanged. The HMAC response is never written to a keyfile. KeePassXC receives no capability; only pcscd receives CAP_NET_ADMIN, which is required to power and poll the NFC controller.
The GNOME Secrets overlay preserves password, key file, and YubiKey as three distinct KDBX4 credential contributions. Its process-local PyKeePass adapter normalizes the selected key file and applies KeePassXC's composite-key order; the component envelope and HMAC response exist only in memory.
PrivateUsers=false is required in the pcscd service override. A capability inside systemd's private user namespace does not satisfy the physical NFC device's host-namespace generic-netlink check.
vendor/ifdnlnfc/: PC/SC IFD handler based on StarGate01/ifdnlnfc revision 86703e844652ce99bfd5a4d2aa4fceb7c3fb2a5asrc/ykchal_nfc.c: standalone OTP challenge-response diagnostic probetests/ifd_contract.c: synthetic IFD boundary and error-contract checkspackaging/reader.conf: pcsc-lite reader definition for NFC adapter index 0packaging/pcscd.service.d/oneplus-nfc.conf: constrained systemd capability/address-family override and persistent daemon commandpackaging/oneplus-nfc-init.service: root PN553 rebind ordered before each pcscd.service startscripts/install-on-phone: root installation helperscripts/deploy-phone: workstation-to-phone build and deployment flowscripts/oneplus-nfc-init: bounded pre-PCSC controller initializationsecrets-overlay/gsecrets/provider/: GNOME Secrets 9.6 PC/SC YubiKey provider overlayscripts/check-kernel-nfc: non-mutating device, kernel-option, driver, and nfc0 preflightscripts/install-secrets-on-phone: pinned Secrets installation with dry-run/apply modesscripts/secrets-nfc: overlay-aware GNOME Secrets launcherThe vendored IFD handler includes local fixes for adapter lookup, netlink receive failures, cleanup after initialization errors, deterministic power-up failure, uninitialized kernel attributes, APDU types, and PC/SC output-buffer validation.
Install the IFD handler dependencies on postmarketOS:
sudo apk add build-base pkgconf pcsc-lite-dev libnl3-dev
Build the production bridge and run its synthetic contract checks:
make driver test
The diagnostic probe additionally needs OpenSSL headers and wget:
sudo apk add openssl-dev wget
make probe
make all test builds both binaries. The probe build downloads libfido2 1.16.0 and verifies SHA-256 7d86088ef4a48f9faad4ff6f41343328157849153a8dc94d88f4b5461cb29474.
Use this path when the project checkout is already on the phone:
./scripts/check-kernel-nfc
make driver test
sudo ./scripts/install-on-phone
./scripts/install-secrets-on-phone --dry-run
sudo ./scripts/install-secrets-on-phone --apply
The bridge installer:
/usr/local/lib/libnlnfc.so.0.0.0./usr/local/lib./etc/reader.conf.d/libnlnfc.pcscd.service override.pcscd.service.pcscd.socket and restarts pcscd.service.nfc0 is missingFirst update the official release packages and reboot:
sudo apk update
sudo apk upgrade
sudo reboot
After reconnecting, run:
apk policy linux-postmarketos-qcom-sdm845
./scripts/check-kernel-nfc
Do not build or flash a custom kernel merely for this project. If the official
v26.06 package is installed and the preflight still reports no nfc0, inspect
the nxp_nci_i2c probe failure:
sudo dmesg | grep -Ei 'nfc|nxp|pn553'
Users intentionally running another kernel must provide both the official OnePlus PN553 device-tree node and the four NFC options checked by the preflight script. Building and flashing such a kernel is device-recovery work outside this installer; it is not an automatic or safe fallback.
Both installation paths install Alpine's GNOME Secrets 9.6 and py3-pyscard at
their tested versions, then place the PC/SC provider only under /usr/local.
They do not patch Alpine-owned Python modules, alter the KDBX format, or create
a challenge-response keyfile.
For touch use:
/usr/local/bin/secrets-nfc.OnePlus 6 NFC 00 00 — Slot 1 or — Slot 2
credential. Secrets never guesses an unknown database's slot.Secrets preserves its native inactivity lock and clipboard-clearing behavior. Browser-extension autofill is not part of this touch workflow.
List the virtual reader:
pcsc_scan -r
Expected:
0: OnePlus 6 NFC 00 00
Then hold the YubiKey flat against the upper rear of the phone:
pcsc_scan -n -t 30
The verified card event and ATR were:
Card state: Card inserted
ATR: 3B 8D 80 01 80 73 C0 21 C0 57 59 75 62 69 4B 65 79 F9
Open KeePassXC's database credentials dialog while the key is present. The YubiKey challenge-response selector should list the programmed slots.
The diagnostic used this public 64-byte challenge for slot 2:
000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f
The NFC YubiKey and an identically programmed workstation key returned the same 20-byte response. Its value is intentionally omitted because the keys mirror a production credential.
The probe selects OTP applet AID A0000005272001, sends instruction 0x01, addresses slot 1 as 0x30 or slot 2 as 0x38, and returns the same 20-byte HMAC-SHA1 response KeePassXC expects. FIDO2 and database rekeying are not involved.
To repeat the diagnostic:
sudo build/ykchal-nfc 2 \
000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f
Do not assign file capabilities to a binary in a user-writable directory.
On the tested kernel, the PN553 needs one driver rebind immediately before its first NFC power request. The pcscd override requires oneplus-nfc-init.service; because the one-shot does not remain active, it reruns as root before every pcscd.service start.
The override removes pcscd --auto-exit: pcsc-lite 2.3.3 on this phone otherwise unlinks /run/pcscd/pcscd.comm when it exits while pcscd.socket remains active, leaving subsequent clients unable to reactivate the daemon. Verify the latest initializer run with:
systemctl status oneplus-nfc-init.service
Reboot the phone if this initializer or pcsc_scan -r later fails. Do not use rfkill toggles as a recovery mechanism for this controller.
Do not manually unbind a PN553 after an I²C error: one observed failure put both the unbind process and irq/176-nxp-nci into uninterruptible D state. A later rfkill reset also left the controller unable to power up.
Relevant timeout evidence from the diagnostic path:
generic-netlink: ETIMEDOUT (110)
nxp-nci_i2c 3-0028: NFC: Read failed with error -121
-121 is EREMOTEIO. The long-term fix belongs in the PN553 power/GPIO sequencing in the kernel, not in KeePassXC or the KDBX path.
CAP_NET_ADMIN to KeePassXC.Project-owned code and integration scripts are GPL-3.0-or-later; the root
COPYING contains the GPLv3 terms. The GNOME Secrets provider overlay is
GPL-3.0-only and adapts the GNOME Secrets 9.6 provider architecture. The
vendored ifdnlnfc IFD handler remains GPL-2.0-only; its upstream COPYING
and copyright notices are preserved. The diagnostic probe compiles
libfido2's BSD-2-Clause generic-netlink implementation from a
checksum-pinned source archive and follows OTP APDU behavior demonstrated by
GPL-3.0 ykDroid.
5 commits
Python
64.0%
Shell
16.6%
C
15.0%
Makefile
4.5%