Jorijn worked out the technique and it changes what every number in this project means: "press and LIFT (quick tap) is wrong, holding the sensor until it gives the result is a 100% success rate." The logs agree, on a properly controlled comparison. Same template, same session, learning off for all four blocks, only the technique differing: tapped 4/15 and 3/15, held 15/15 and 15/15. The frame data says why. Over every frame this project has a verdict for, split at 2.5x the idle floor: full contact, interrupt settled 78/175 = 45% match full contact, interrupt asserted 28/142 = 20% partial, interrupt settled 1/9 = 11% partial, interrupt asserted 0/53 = 0% A tap is caught while the finger is still arriving or already leaving. Such a frame is not a hard verdict waiting to happen, it is a wasted one: with the rescan budget at 0 every frame is terminal, so its rejection ends the press. 62 partial frames produced exactly one match between them. So the tracker becomes a Schmitt trigger. A press now STARTS on settled contact and ENDS on the finger leaving, which means a frame taken mid-landing produces no event at all rather than a false rejection. A press that never settles simply yields no verdict and the loop waits for the next one, which is an honest try again. Enrolment is untouched: it passes one threshold for both and keeps its own sample-quality gate inside the trustlet. fptrial.sh now says hold, and defaults to fifteen presses. Instructing a tap for its whole life is what quietly made every rate this project has quoted a worst case, and a tap is not a case the product has -- nobody taps a phone sensor and walks away, they rest a finger until it unlocks.
61 lines
3 KiB
Shell
61 lines
3 KiB
Shell
#!/bin/sh
|
|
# fptrial.sh -- a labelled verification protocol against fingerprintd.
|
|
#
|
|
# fptrial.sh [correct_presses] [wrong_presses] defaults 15 and 0
|
|
#
|
|
# Runs fprintd-verify once per press and tells you which finger to use before
|
|
# each one. The daemon's transcript records every frame; THIS records the label
|
|
# and the wall-clock time from "press now" to the client seeing a result, which
|
|
# is the latency a user feels. Unlabelled runs cannot be turned into a rate.
|
|
#
|
|
# HOLD, DO NOT TAP -- and this script said the opposite for its whole life,
|
|
# which quietly made every rate this project has ever quoted a worst case.
|
|
#
|
|
# Jorijn, 2026-09-05: "press and LIFT (quick tap) is wrong, holding the sensor
|
|
# until it gives the result is a 100% success rate." The logs agree and say why.
|
|
# Same template, same session, learning off, only the technique differing:
|
|
#
|
|
# tapped 4/15 and 3/15
|
|
# held 15/15 and 15/15
|
|
#
|
|
# The mechanism is in the frame metrics. A tap is caught while the finger is
|
|
# still arriving or already leaving, and such a frame matched 0 times in 53;
|
|
# with the rescan budget at 0 that rejection is terminal and ends the press. A
|
|
# held finger yields a full-contact frame, and those match.
|
|
#
|
|
# It is also what a real user does: nobody taps a phone's fingerprint sensor and
|
|
# walks away, they rest a finger until it unlocks. Measuring taps was measuring
|
|
# a case the product does not have.
|
|
C=${1:-15}; W=${2:-0}
|
|
OUT=/tmp/fptrial-$(date +%Y%m%d-%H%M%S).log
|
|
now() { awk '{gsub(/\./,""); print $1 "0000000"}' /proc/uptime; }
|
|
run() { # $1 = label
|
|
printf '\n>>> %s -- press and HOLD until it answers ... ' "$1"
|
|
t0=$(now)
|
|
res=$(timeout 20 fprintd-verify user 2>&1 | grep -E 'Verify result' | tail -1)
|
|
t1=$(now)
|
|
ms=$(( (t1 - t0) / 1000000 ))
|
|
verdict=$(printf '%s' "$res" | grep -oE 'verify-(match|no-match|unknown-error)' || echo timeout)
|
|
printf '%s (%d ms)\n' "$verdict" "$ms"
|
|
printf '%s %s %d\n' "$1" "$verdict" "$ms" >> "$OUT"
|
|
sleep 1
|
|
}
|
|
echo "labelled trial -> $OUT"
|
|
i=1; while [ $i -le $C ]; do run "CORRECT $i/$C"; i=$((i+1)); done
|
|
# The wrong-finger half is skippable, and skipping it is the right default once
|
|
# the question it answers is settled. Across every run this project has made the
|
|
# wrong finger has matched ZERO times out of forty-odd taps; the open question
|
|
# is the FALSE NEGATIVE rate, and each wrong-finger tap spends a press that
|
|
# could have measured that instead. Pass a count to put the control back.
|
|
if [ "$W" -gt 0 ]; then
|
|
echo; echo "===== now switch to a DIFFERENT finger ====="; sleep 3
|
|
i=1; while [ $i -le $W ]; do run "WRONG $i/$W"; i=$((i+1)); done
|
|
fi
|
|
echo
|
|
echo "===== summary ====="
|
|
awk '$1=="CORRECT"{c++; if($3=="verify-match")cm++; ct+=$4}
|
|
$1=="WRONG" {w++; if($3=="verify-match")wm++; wt+=$4}
|
|
END{printf " correct finger: %d/%d matched (false negatives %d) mean %d ms\n", cm,c,c-cm,(c?ct/c:0);
|
|
if (w) printf " wrong finger: %d/%d matched (false ACCEPTS %d) mean %d ms\n", wm,w,wm,wt/w;
|
|
else printf " wrong finger: not run this trial\n"}' "$OUT"
|
|
echo " per-tap log: $OUT"
|