In the spirit of my other "make unloved hardware actually work on Linux" writeups, here is another one. This time the win came with a lesson attached: I spent a day proving the thing was impossible, was completely right about the evidence, and completely wrong about the conclusion.
The sensor now unlocks sudo and my lock screen. But the honest story is the part before that.
The device, and why bother
ASUS VivoBook X513EAN. The fingerprint reader is an ELAN, SPI-connected, ACPI id ELAN7001, touchpad PID 04f3:3104, chip eFSA80SC — a tiny 80×80 swipe sensor.
On Linux it does nothing out of the box. There is a genuinely great project, mincrmatt12/elan-spi-fingerprint, that reverse-engineered the SPI protocol and wrote a libfprint driver for the family. But my exact PID was flagged as "a different setup" and guarded out, with an open, unanswered issue saying it just crashes.
So: known-hard, but not known-impossible. Worth a look.
Capture: the easy part
Getting images off the sensor took two small fixes to the driver.
The first was a wrong quirk flag. The driver tagged 0x3104 with an X571 mode that uses a shorter SPI transfer format. My sensor wants the standard one with the quirk set, calibration just sat there and timed out. The second was a missing build dependency (pixman) that wasn't being pulled in for this driver.
With those, fprintd-enroll worked on the first try. Eight swipes, done. I was maybe twenty minutes from a fingerprint login, I thought.
Then:
$ fprintd-verify
Verify result: verify-no-match
Every single time. Score 0/24. It captured my finger, stored it, and then refused to recognise it.
The measurement that "settled" it
libfprint matches with NBIS/bozorth3: find the minutiae (ridge endings, forks), then compare their geometric pattern. So I wrote a small tool to dump what the matcher actually got.
Seven minutiae per scan. A usable template wants twenty to sixty. And the seven it found didn't line up between two scans of the same finger.
I found reasons. The driver upscaled the image 2× before matching (pushing the ridge spacing out of the band NBIS expects), and set a flag that throws away "perimeter" minutiae which, on a thin swipe strip, is nearly all of them. I fixed both, added a little smoothing, and the counts jumped to 15–45. First verify-match ever.
One time in five. Not a login.
So I stopped guessing and measured properly: a labelled set of captures one finger many times, other fingers as impostors cross-matched every pair offline. The result was brutal.
| bozorth3 score | |
|---|---|
| Genuine (same finger) | 0–26, median ~7 |
| Impostor (different finger) | 0–27, median ~9 |
The two distributions sat on top of each other. An impostor pair scored higher than any genuine pair, and above the accept threshold. There is no cutoff that lets you in and keeps other people out. (An impostor scoring above every genuine match isn't a tuning problem. It's the matcher telling you it can't see a difference.)
The cause is geometry. This is a ~6 mm strip where the ridges run nearly parallel the whole way across. bozorth3 compares constellations of points by distance and angle and near-parallel strips produce lookalike constellations for anybody's finger. Better images raised the genuine scores and the impostor scores together, in lockstep.
I wrote it up as a data-backed conclusion: not securable with libfprint's matcher; do not wire this into login. Every measurement in that writeup was correct and reproducible.
The conclusion was still wrong.
The search that un-settled it
The failure was specific to bozorth3. Which meant the real question was never "how do I fix bozorth3" it was "has anyone doing small-sensor matching on Linux hit exactly this, and gone around it?"
They had. The goodix-fp-linux-dev folks build drivers for tiny Goodix sensors with the same disease, and they wrote SIGFM "SIFT Is Good For Matching." It throws out the minutiae idea entirely and matches with SIFT, the computer-vision feature descriptor, plus a ratio test and a geometric consistency check. It already lives in a libfprint fork, behind a one-line per-driver
switch:
img_class->algorithm = FPI_DEVICE_ALGO_SIGFM;
I re-scored my exact same captures with it. Same images. Different matcher.
| SIGFM score | |
|---|---|
| Impostor (different finger) | 0–7 |
| Genuine (same finger) | 549–5,400,000 |
The weakest genuine match came in around eighty times the strongest impostor. Where bozorth3 saw noise, SIFT saw a fingerprint.
Live, through fprintd, with the elanspi driver pointed at SIGFM: 5 genuine swipes accepted out of 5, 0 impostor swipes accepted out of 3. Two pam-auth-update commands later:
$ sudo -k && sudo echo it-works
Swipe your right index finger across the fingerprint reader
it-works
Enabling it is one pam-auth-update screen tick Fingerprint, leave Unix auth on (that is your password fallback; never remove it):
The GNOME lock screen takes it too. Password still works as a fallback that part matters, keep it.
The honest status
What you're actually signing up for, if you do this:
It's a custom
libfprintbuild, installed into/usr/localover the distro copy. It depends on OpenCV at runtime. A big distro update tolibfprintorfprintdcan shadow it or break the ABI, and you rebuild. (None of this is packaged yet.)SIGFM is not in upstream libfprint. It's a community fork. Excellent work, but you're living slightly off the map.
Verification is reliable, not magic. A fast or crooked swipe still misses; you swipe again. The security margin, though, is real and measured not vibes.
For a laptop I unlock a hundred times a day, that trade is easily worth it. For a locked-down machine, decide for yourself.
If you want to do this too
The full step-by-step — udev rules, the patch, the build, PAM — plus the diagnostic tools and the complete matching analysis are in the repo:
https://github.com/r4nd3l/elan-3104-fingerprint-linux
The shape of it:
Bind the sensor to
spidevwith a udev rule, and setspidev.bufsiz=32768(the eFSA80SC sends a whole frame in one message; the default buffer truncates it).Build the goodix SIGFM
libfprintfork with a small patch: add the0x3104entry, drop the 2× resize and the PARTIAL flag, add smoothing, selectFPI_DEVICE_ALGO_SIGFM.Install to
/usr/local, restartfprintd, thenfprintd-enroll/fprintd-verify.pam-auth-update, tick Fingerprint, leave Unix auth on.
It's the same sensor family across a lot of ASUS VivoBook and ExpertBook models,so if
ls /sys/bus/spi/devices/ | grep -i elan
shows ELAN7001, this is probably for you.
The sticky note
Works: enroll, verify, and PAM (sudo, login, lock screen) on
04f3:3104/ eFSA80SC as far as I can tell, the first working report for this PID.The fix that mattered: not the driver patches swapping NBIS/bozorth3 for SIGFM. NBIS fundamentally can't discriminate fingers on these narrow strips (measured: genuine and impostor scores overlap). SIGFM separates them by orders of magnitude.
The catch: custom libfprint build in
/usr/local, OpenCV runtime dep, not upstream — expect to rebuild after big system updates.
The most useful thing I did all project was a web search after I'd concluded I was finished. My measurements were right. My conclusion had quietly promoted "this matcher can't do it" into "this can't be done." Those are not the same sentence, and the gap between them was one working fingerprint login.
Credit to mincrmatt12 for the driver and the protocol work, and to goodix-fp-linux-dev for SIGFM — a matcher built for one vendor's sensors that ended up rescuing another's. If you get it going on a different model, drop it on the issue thread; the more PIDs documented, the better for whoever's searching at 1 a.m.
Enjoying the content? If you'd like to support my work and keep the ideas flowing, consider buying me a coffee! Your support means the world to me!


Top comments (0)