A template restore destroys the template; refuse it, and stop caching a failed load
The paired test I built cannot exist on this hardware, and finding that out cost Jorijn his enrolled finger. Restoring an earlier container byte for byte made the trustlet read its header, UNLINK it, look for a .bak and advance the RPMB counter -- for both containers. QTEE binds every SFS object to a hardware anti-rollback counter, so an earlier version is not stale to it, it is tampered, and the response to tampering is deletion. No copy can undo that. The journal has warned about this counter since August in the context of the group index; it applies to the template itself just as hard, and I did not check before building a feature on the assumption that it did not. So restore refuses and explains, with the measurement in the message. Snapshots stay: they are still forensic evidence of what a template looked like, they just cannot be put back. Changing a template means enrolling again. The daemon compounded it. The group-selection cache stored "0 templates loaded" as a valid answer, so once the trustlet had unlinked everything, every later claim answered "skipped (already active, 0 template(s))" and never tried again. It now caches only a positive result, so a failed load is retried rather than made permanent.
This commit is contained in:
parent
756093e847
commit
30bb987c27
2 changed files with 38 additions and 3 deletions
|
|
@ -1230,7 +1230,12 @@ public:
|
|||
// store: an enrolment, or a template removed underneath us.
|
||||
int SetActiveGroup(std::uint32_t gid, bool force = false) {
|
||||
namespace ta = fingerprintd::ta;
|
||||
if (!force && gid == gid_ && templatesLoaded_ >= 0) {
|
||||
// > 0, never >= 0: caching a ZERO turns a failed load into a
|
||||
// permanent one. It did exactly that on 2026-09-05 -- the trustlet
|
||||
// unlinked a restored template, reported 0, and every later claim
|
||||
// answered "skipped (already active, 0 template(s))" without ever
|
||||
// trying again.
|
||||
if (!force && gid == gid_ && templatesLoaded_ > 0) {
|
||||
std::println("SET_ACTIVE_GROUP gid={} skipped (already active, {} template(s))",
|
||||
gid, templatesLoaded_);
|
||||
return templatesLoaded_;
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
# fplearn.sh -- the template-learning measurement, as a protocol rather than a
|
||||
# pile of remembered commands.
|
||||
#
|
||||
# fplearn.sh snapshot [name] save the current template aside
|
||||
# fplearn.sh restore <name> put a saved template back and reload it
|
||||
# fplearn.sh snapshot [name] save the current template aside (forensics
|
||||
# only -- see restore)
|
||||
# fplearn.sh restore <name> REFUSES: a restore DESTROYS the template on
|
||||
# this device, see the note in the command
|
||||
# fplearn.sh wipe remove every stored template (backup first)
|
||||
# fplearn.sh enrol [finger] enrol at the config's sample count
|
||||
# fplearn.sh base [n] [w] trial with learning OFF (the baseline)
|
||||
|
|
@ -113,6 +115,34 @@ snapshot)
|
|||
echo "restore with: fplearn.sh restore $NAME" ;;
|
||||
|
||||
restore)
|
||||
# DISABLED, and it must stay disabled: restoring a template DESTROYS it.
|
||||
#
|
||||
# Measured 2026-09-05. A byte-exact restore of an earlier container made the
|
||||
# trustlet read its header, UNLINK it, look for a .bak, and bump the RPMB
|
||||
# counter -- for both containers. The enrolled finger was gone, and no copy
|
||||
# can bring it back, because QTEE binds every SFS object to a hardware
|
||||
# anti-rollback counter that has since advanced. An older container is not
|
||||
# stale to QTEE, it is TAMPERED, and the response to tampering is deletion.
|
||||
#
|
||||
# So the paired within-session comparison this was built for is IMPOSSIBLE
|
||||
# on this hardware. A template can be measured, and it can be replaced by
|
||||
# enrolling again, but it can never be put back.
|
||||
cat <<'WARN'
|
||||
REFUSING: restoring a template DESTROYS it on this device.
|
||||
|
||||
QTEE binds every stored object to a hardware anti-rollback counter. An earlier
|
||||
container is not stale to it, it is tampered, and it responds by UNLINKING the
|
||||
file. Measured 2026-09-05: a byte-exact restore of the previous template made
|
||||
the trustlet unlink BOTH containers and advance the RPMB counter. The enrolled
|
||||
finger was destroyed and no copy could bring it back.
|
||||
|
||||
Snapshots are still worth taking -- they are forensic evidence of what a
|
||||
template looked like -- but they cannot be put back. To change the template,
|
||||
enrol again.
|
||||
WARN
|
||||
exit 1 ;;
|
||||
|
||||
restore-force-DESTRUCTIVE)
|
||||
NAME=${2:-}
|
||||
D=$SNAPDIR/$NAME
|
||||
[ -n "$NAME" ] && [ -d "$D" ] || { echo "usage: fplearn.sh restore <name>"; echo "available:"; ls "$SNAPDIR" 2>/dev/null | sed 's/^/ /'; exit 1; }
|
||||
|
|
|
|||
Loading…
Reference in a new issue