From c5fc3afc77a314d6223c9b2df9d218cc7ba4e813 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Thu, 3 Sep 2026 17:46:33 +0200 Subject: [PATCH] Add fplearn.sh, because a cumulative measurement is a protocol not three commands Learning rewrites the template on every matched press, so run two is not a repeat of run one and an A/B against a moving template is not an A/B. The order is the measurement: enrol a fresh template at 20 samples with no position prompts, which is also the outstanding replication of the best result this lane has seen base learning off: the clean number for that template, and the only figure comparable to every rate already recorded trend learning on, three times: the rate should climb, with the container size as an independent witness that it is the template moving A rate that climbs while the body grows is learning. A rate that moves while the body does not is noise, and the daemon's own learn: lines say which. Run one of the trend is not evidence that learning helped; it starts on the template the baseline ended on. One limit the script states rather than letting someone discover it: learning only fires on a press that matched, so a template that never matches never improves. That is why the protocol re-enrols instead of building on a template already measured at 0 of 10. --- README.md | 25 +++++++++++ packaging/fplearn.sh | 98 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100755 packaging/fplearn.sh diff --git a/README.md b/README.md index 655a82d..476c4d6 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,31 @@ the shipped config sets `max_authentication_rescan_times` to 0, because at the stock budget a wrong finger never yields a terminal frame and a PAM client waits forever for the `verify-no-match` it needs. +**Template learning.** Stock rewrites the stored template on every successful +press — `0x1015 UPDATE_TEMPLATE` while the finger is still down, then a deferred +`SAVE_DATA` — and the stored body measurably grows over a template's life +(333278 bytes at enrolment to 371734 after one authentication session on the +reference device). This daemon does the same as of 0.1.0. It matters more than +any tuning knob: before it, every match rate measured against this device was +measured against a day-zero template that no stock user lives with. + +The harvest sends no event, so the matcher does not re-run and a verdict cannot +be revised by it. A quick tap pays almost nothing, because the finger is gone by +the time a verdict lands; a held press contributes the frames it was held for — +which means the frames learned from are, by construction, frames from real +unlock presses. The save is deferred until after the verdict reaches the client, +because ~350 ms of RPMB traffic does not belong on an unlock path; stock defers +it the same way. `--learn=0` turns the whole thing off so the comparison can be +made on one binary, which is the only way it is single-variable. + +**Reading the trustlet.** There is no `tzdbg` on mainline, but focal64 writes +its log into the response buffer, so `--ta-log` surfaces the matcher's own +verdicts (`auth success score`, `identify fail! FtVerifyByTemplate() = -2`, the +per-frame `image quality / coverage / humidity`). The log ring is ~150 lines per +session and is never reset, so the config dump alone can overflow it: the +shipped verbose config leaves the framework log off to reserve the ring for the +algorithm. Lower levels are more verbose and 6 is off. + **Known about the loop, from real use.** A verify frame is four QTEE round trips (~200 ms idle); a `REPORT_EVENT` that runs the matcher is ~300 ms, and the rising edge pays it twice because events 5 and 7 both reach the matcher. diff --git a/packaging/fplearn.sh b/packaging/fplearn.sh new file mode 100755 index 0000000..ac96237 --- /dev/null +++ b/packaging/fplearn.sh @@ -0,0 +1,98 @@ +#!/bin/sh +# fplearn.sh -- the template-learning measurement, as a protocol rather than a +# pile of remembered commands. +# +# fplearn.sh enrol [finger] re-enrol at the config's sample count +# fplearn.sh base [n] [w] trial with learning OFF (the baseline) +# fplearn.sh trend [n] [w] three trials with learning ON +# fplearn.sh sizes just print the template container sizes +# +# WHY IT IS SHAPED LIKE THIS. Learning is cumulative: every matched press folds +# frames into the stored template, so a second run is not a repeat of the first +# and an A/B against a moving template is not an A/B at all. The only honest +# comparison is on ONE template lineage, in order: +# +# 1. enrol a fresh template, 20 samples, no position prompts -- +# which is also the outstanding replication of the 7/10 +# result, the one measurement this lane was parked on +# 2. base learning off: the clean number for THIS template, and +# the only figure comparable to every rate in the journal +# 3. trend learning on, three times: the rate should climb, and +# the container size is an independent witness that it +# is the template moving and not the weather +# +# Do NOT read run 1 of the trend as "learning made it better". Run 1 starts on +# the same template the baseline ended on; it is the first run that can improve +# it, not one that has already been improved. +set -u + +GROUP=/mnt/persist/data/RIY7A+mQm3EA4FsCUmkJo0b9dFUYP2YZ4P5hmMiZgeA_Alt +UNIT=fingerprintd-test +BIN=/tmp/fingerprintd +COMMON="--daemon --verbose --edge-wake --sfs-root=/var/lib/fingerprintd/sfs --sfs-writable --rpmb-write" + +sizes() { + # A template is stored twice, the container and its backup, so the sizes + # come in pairs. The BODY is the container minus its 4096-byte header. + sudo ls -la "$GROUP" 2>/dev/null | awk '$5 > 200000 { printf " %9d body %9d %s\n", $5, $5-4096, $9 }' | sort -u +} + +restart() { # $1 = extra args + sudo systemctl stop "$UNIT" 2>/dev/null + sleep 2 + sudo systemd-run --unit="$UNIT" --collect $BIN $COMMON $1 >/dev/null 2>&1 + printf 'daemon starting' + i=0 + while [ $i -lt 40 ]; do + if busctl --system list 2>/dev/null | grep -q net.reactivated.Fprint; then + # Owning the name is not the same as being ready: the session comes + # up on the worker thread afterwards. + sleep 6; printf ' ready\n'; return 0 + fi + printf '.'; sleep 1; i=$((i+1)) + done + printf ' TIMED OUT\n'; return 1 +} + +case "${1:-}" in +sizes) + echo "template containers now:"; sizes ;; + +enrol) + F=${2:-right-middle-finger} + echo "=== enrol $F ===" + echo "sizes before:"; sizes + restart "--learn=1" || exit 1 + /tmp/fpenrol.sh "$F" + echo; echo "sizes after the enrolment:"; sizes + echo; echo "next: fplearn.sh base" ;; + +base) + N=${2:-10}; W=${3:-5} + echo "=== BASELINE: learning OFF ===" + restart "--learn=0" || exit 1 + echo "sizes before:"; sizes + /tmp/fptrial.sh "$N" "$W" + echo; echo "sizes after (MUST be unchanged -- learning was off):"; sizes + echo; echo "next: fplearn.sh trend" ;; + +trend) + N=${2:-10}; W=${3:-5} + echo "=== TREND: learning ON, three runs on one template ===" + restart "--learn=1" || exit 1 + echo "sizes at the start:"; sizes + r=1 + while [ $r -le 3 ]; do + echo; echo "----- learning run $r of 3 -----" + /tmp/fptrial.sh "$N" "$W" + echo "sizes after run $r:"; sizes + r=$((r+1)) + done + echo + echo "Read it as a trend, not three numbers. A rate that climbs while the" + echo "container grows is learning; a rate that moves while the container" + echo "does not is noise, and the daemon's own 'learn:' lines say which." ;; + +*) + sed -n '2,30p' "$0"; exit 1 ;; +esac