Learning off by default: it makes matching worse on this hardware

Jorijn asked for the control that settles it -- a fresh template, tested with
learning off -- and ran it twice.

  fresh template, 0 folds     30/30, two consecutive blocks of fifteen
  same lineage, 40 folds      12/15
  same lineage, 185 folds     total failure, 108 consecutive rejections

Every one of those measured with learning switched off during the measurement
itself, so nothing moved underneath the numbers, and the fresh-template result is
replicated back to back. Three points, monotonic in fold count.

The mechanism has been visible since the 185-fold collapse: the frames one press
contributes are near-duplicates of a single image from one finger position, so
folding them spends the template's ninety-six slots on that position and evicts
the diversity a twenty-sample enrolment put there. Stock's updates are spread
across many separate presses hours apart, which is where diversity actually comes
from.

And there is nothing on the other side of the scale. A plain enrolment measures
thirty out of thirty, so learning has no headroom to improve anything, and it has
never once been observed to raise a rate under conditions worth defending -- the
run that once looked like proof was confounded by a freshly wiped sensor and a
user learning the technique, both of which Jorijn identified himself while the
numbers were still climbing.

The code stays behind --learn=1. The finding is about this trustlet's algorithm,
not about the idea.
This commit is contained in:
Jorijn van der Graaf 2026-09-05 02:11:43 +02:00
commit ba882ee47b
2 changed files with 42 additions and 6 deletions

View file

@ -109,6 +109,24 @@ 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 is implemented and OFF, because it makes matching worse on
this hardware.** Measured, with learning disabled during each measurement so
nothing moved underneath the numbers:
| template | folds | rate |
|---|---|---|
| fresh 20-sample enrolment | 0 | **30/30** |
| same lineage, later | 40 | 12/15 |
| same lineage, later still | 185 | total failure |
The frames one press contributes are near-duplicates of a single image from one
finger position. Folding them spends the template's 96 slots on that position
and evicts the diversity the enrolment put there. There is no upside to weigh
against it either: a plain enrolment already measures 30/30, and learning has
never once been observed to raise a rate under controlled conditions.
`--learn=1` enables it for experiments. What follows describes how it works.
**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

View file

@ -117,12 +117,30 @@ bool g_irqObserve = false;
bool g_edgeWake = false;
// TEMPLATE LEARNING: fold the frames of a successful press back into the
// stored template, as stock does. On by default because stock does it and
// because every match rate this project has measured was taken against a
// day-zero template; `--learn=0` turns it off so the two can be compared on
// one binary without a rebuild, which is the only way the comparison is
// single-variable.
bool g_learn = true;
// stored template, as stock does.
//
// OFF by default, and that is a measured decision rather than caution. Stock
// does it, this daemon can do it, and on this hardware it makes matching WORSE:
//
// fresh template, 0 folds 30/30 (two consecutive blocks of 15)
// same lineage, 40 folds 12/15
// same lineage, 185 folds total failure, 108 consecutive rejections
//
// all measured with learning switched off during the measurement itself, so
// nothing moved underneath the numbers. The mechanism is that the frames one
// press contributes are near-duplicates of a single image from one finger
// position, so folding them spends the template's slots -- 96 of them -- on
// that position and evicts the diversity a 20-sample enrolment put there.
//
// And there is no upside to weigh against it: a plain enrolment measures 30/30,
// so there is nothing for learning to improve. It has never once been observed
// to raise a rate under controlled conditions; the one run that suggested it
// did was confounded by a freshly wiped sensor and a user learning the
// technique, both spotted by Jorijn at the time.
//
// `--learn=1` turns it on for experiments. The code stays because the finding
// is about THIS trustlet's algorithm, not about the idea.
bool g_learn = false;
// Dump the trustlet's own log out of the response buffer after every command.
// See DumpTaLog: on pmOS this is the ONLY way to read it.
bool g_taLog = false;