Install fingerprintd from the registry, so the image unlocks with a finger

The daemon's own package CI publishes it to the registry the same way imsd's
does, so the image takes it from there: the exact apk a user later gets via
apk upgrade, sha256-pinned, re-signed for the chroot. Section 3b now fetches
both sets, and every fetched file must have a pin -- the check used to be
`grep . | sha256sum -c`, which an empty pin list would have sailed through
with nothing checked.

Three apks: the daemon, its systemd units, and the session agent, which does
nothing until a user writes ~/.config/fingerprintd/fingers.conf. The daemon
needs the kernel aport's CONFIG_QCOMTEE=m (pkgrel 101) and fp6-vendor-blobs
1-r2's mbn directive to reassemble the trustlet, both built in this run;
0.2.3 says >=1-r2 so a mismatched pair is refused rather than installed.

The CI publish step skips fingerprintd-* like imsd-*: registry-sourced, not
ours to republish. README: fingerprint in the list, and the two things a user
will otherwise report as a dead sensor -- the lock screen listens for 60
seconds after it appears, and a held press is what the matcher was measured
on -- plus the untested question of stock Android's own fingerprints after
using this.

Verified on the dev phone (fp6 repo journal/fingerprint/, 2026-09-05): the
registry 0.2.2 package enrols through Plasma's Users page and unlocks the
lock screen; 0.2.3 differs by the dependency and a post-upgrade restart. The
image build itself, with the fprintd purge inside the chroot, runs first in
CI.
This commit is contained in:
Jorijn van der Graaf 2026-09-05 20:31:37 +02:00
commit d14fa38a98
Signed by: jorijnvdgraaf
GPG key ID: 2937E59CDCC1BCFB
3 changed files with 67 additions and 20 deletions

View file

@ -50,8 +50,8 @@ jobs:
# the FP6 patches to the next upstream version bump. Requires the
# PACKAGE_TOKEN repo secret (catbot account, package:write scope);
# skips quietly until it exists. 409 = same version already published.
# imsd is skipped: build.sh 3b took it FROM the registry (re-signed
# for the chroot), so it is not ours to publish.
# imsd and fingerprintd are skipped: build.sh 3b took them FROM the
# registry (re-signed for the chroot), so they are not ours to publish.
- name: Publish packages to the apk registry
env:
PACKAGE_TOKEN: ${{ secrets.PACKAGE_TOKEN }}
@ -65,7 +65,7 @@ jobs:
for f in /home/build/.local/var/pmbootstrap/packages/*/aarch64/*.apk; do
[ -e "$f" ] || continue
case "$(basename "$f")" in
imsd-*) echo "registry-sourced, not republished: $(basename "$f")"; continue ;;
imsd-*|fingerprintd-*) echo "registry-sourced, not republished: $(basename "$f")"; continue ;;
esac
found=1
code=$(curl -s -o /dev/null -w '%{http_code}' \

View file

@ -14,7 +14,9 @@ Maintained by [Jorijn van der Graaf](https://catcrafts.net/about)
Everything on the `combined-stable` branch: display, touch, wifi, cellular
data, NFC (reader), speaker audio, microphone, IMU, magnetometer, barometer,
ambient light/proximity plus VoLTE calls (both directions) through imsd.
ambient light/proximity, fingerprint unlock through
[fingerprintd](https://forgejo.catcrafts.net/Catcrafts/fingerprintd), plus
VoLTE calls (both directions) through imsd.
## Flashing
@ -49,4 +51,26 @@ Find it in a stock-firmware capture or your carrier's IMS documentation, then
`systemctl restart imsd` (the service is enabled at boot and waits for this
file to exist). See the
[imsd README](https://forgejo.catcrafts.net/Catcrafts/imsd) for the full
variable reference and carrier assumptions.
variable reference and carrier assumptions.
## Fingerprint
Enrol under **Settings → Users** (Plasma's own fingerprint page; the `fprintd`
command-line tools are not installed, fingerprintd replaces that package).
**Hold** the finger on the sensor for each of the ~20 presses rather than
tapping it; a held press is what the matcher was measured on.
Two things to know:
- The lock screen listens for a finger for **60 seconds after it appears**.
A press after that reaches nothing and looks like a dead sensor. Lock and
unlock again to re-arm it.
- The matcher is the phone's own proprietary trustlet, reassembled from the
stock modem partition on first boot and never shipped by us. Templates are
stored on the Android `persist` partition, sealed to the same hardware
anti-rollback counter stock Android uses. Whether fingerprints enrolled under
stock Android survive a return to it after using this has **not** been
tested.
A finger can also run something in your session on a match
(`~/.config/fingerprintd/fingers.conf`, see the fingerprintd README).

View file

@ -1,6 +1,7 @@
#!/bin/sh -eu
# fp6-img pipeline: build a flashable postmarketOS image for the Fairphone 6
# with the Catcrafts kernel (milos-linux combined-stable) and imsd (VoLTE)
# with the Catcrafts kernel (milos-linux combined-stable), imsd (VoLTE) and
# fingerprintd (fingerprint unlock)
# installed from the Catcrafts apk registry.
#
# Runs in CI inside an Alpine container on the privileged "pmos" runner
@ -30,6 +31,19 @@ IMSD_SHA256="
f1c317d7ff9448c05df068d683d31da08e4bfc074704d96cf52cb8acbdee6304 imsd-0.3.1-r0.apk
a78ef31fc2943ac02e120c46df26353d515ac9840d959cf5193b2afe1c665fa6 imsd-systemd-0.3.1-r0.apk
"
# fingerprintd (fingerprint unlock) comes from the same registry the same way:
# its repo's package CI is the only producer, and the same pinning rule holds.
# Three apks: the daemon, its systemd units, and the session agent (inert
# until a user writes ~/.config/fingerprintd/fingers.conf). Needs the kernel
# aport's CONFIG_QCOMTEE=m (pkgrel 101) and fp6-vendor-blobs >= 1-r2, both
# built in this run. 0.2.3: 0.2.2 (enrol, unlock, agent, actions) + the
# versioned blobs dependency + a post-upgrade daemon restart.
FPD_VERSION=0.2.3-r0
FPD_SHA256="
3e28f0c1a9a844592ab6878b2dfc0d8f91674549e44bdc1652e7d7d029de1765 fingerprintd-0.2.3-r0.apk
0cc46eba5c6c77d5bb54cd9f0e2902f7644720f9c98153062ffa33e19ca36889 fingerprintd-systemd-0.2.3-r0.apk
6dfdbc6f971ba4b8f811f828e5868869c7d71fea6c7045e2bffd51bf2736c040 fingerprintd-agent-0.2.3-r0.apk
"
PMAPORTS_REPO=https://gitlab.postmarketos.org/postmarketOS/pmaports.git
cd "$(dirname "$0")"
@ -215,7 +229,7 @@ aports = $WORK/pmaports
device = fairphone-fp6
ui = plasma-mobile
systemd = always
extra_packages = soc-fairphone-fp6-audio,callaudioshim,imsd,fp6-device-tweaks,fp6-charging-mode,catcrafts-fp6-repo,postmarketos-base-ui-audio-backend-pipewire,pipewire-pulse,pipewire-echo-cancel
extra_packages = soc-fairphone-fp6-audio,callaudioshim,imsd,fingerprintd,fingerprintd-systemd,fingerprintd-agent,fp6-device-tweaks,fp6-charging-mode,catcrafts-fp6-repo,postmarketos-base-ui-audio-backend-pipewire,pipewire-pulse,pipewire-echo-cancel
EOF
# All four source tarballs are generated locally above, so every checksum
@ -244,9 +258,9 @@ retry "build modemmanager" pmbootstrap $NOCROSS build --arch aarch64 modemmanage
# patched -r2 exists for the publish step even if the install set resolves
# it before the overlay is considered.
retry "build libcamera" pmbootstrap $NOCROSS build --arch aarch64 libcamera
# --- 3b. imsd: the published apk, not a local build --------------------------
# The imsd repo's package CI is the only producer of the imsd apk; the image
# installs the exact registry package users later get via 'apk upgrade'.
# --- 3b. imsd + fingerprintd: the published apks, not local builds -----------
# Each repo's package CI is the only producer of its apk; the image installs
# the exact registry package users later get via 'apk upgrade'.
# pmbootstrap has no knob for a third-party repository, and after the main
# 'apk add' it re-adds every package found in its local packages dir BY FILE
# PATH — which makes apk verify the package's own signature, and registry
@ -256,13 +270,21 @@ retry "build libcamera" pmbootstrap $NOCROSS build --arch aarch64 libcamera
# stay byte-identical, so the identity checksum equals the registry's), drop
# into the local packages dir, re-index. The abuild key exists because the
# builds above initialized the buildroot.
IMSD_DL="$WORK/imsd-apk"
rm -rf "$IMSD_DL"
mkdir -p "$IMSD_DL"
for _f in "imsd-$IMSD_VERSION.apk" "imsd-systemd-$IMSD_VERSION.apk"; do
retry "fetch $_f" curl -fsSL -o "$IMSD_DL/$_f" "$IMSD_REGISTRY/aarch64/$_f"
REG_DL="$WORK/registry-apks"
rm -rf "$REG_DL"
mkdir -p "$REG_DL"
for _f in "imsd-$IMSD_VERSION.apk" "imsd-systemd-$IMSD_VERSION.apk" \
"fingerprintd-$FPD_VERSION.apk" "fingerprintd-systemd-$FPD_VERSION.apk" \
"fingerprintd-agent-$FPD_VERSION.apk"; do
# every fetched file must have a pin: 'grep .' below drops empty lines,
# so an empty pin list would otherwise pass the check with nothing checked
printf '%s\n' "$IMSD_SHA256" "$FPD_SHA256" | grep -q " $_f\$" || {
echo "no sha256 pin for $_f - add it to IMSD_SHA256/FPD_SHA256" >&2
exit 1
}
retry "fetch $_f" curl -fsSL -o "$REG_DL/$_f" "$IMSD_REGISTRY/aarch64/$_f"
done
(cd "$IMSD_DL" && printf '%s\n' "$IMSD_SHA256" | grep . | sha256sum -c -)
(cd "$REG_DL" && printf '%s\n' "$IMSD_SHA256" "$FPD_SHA256" | grep . | sha256sum -c -)
ABUILD_KEY=$(echo "$WORKDIR"/config_abuild/*.rsa)
if [ ! -f "$ABUILD_KEY" ]; then
echo "expected exactly one abuild key in $WORKDIR/config_abuild" >&2
@ -271,9 +293,9 @@ fi
# abuild-keygen ran inside the chroot as pmbootstrap's user (uid 12345), so
# the key is 0600 to that uid and unreadable here (run 49 died on exactly
# this); sign from a private copy taken via sudo, then drop it.
KEYCOPY="$IMSD_DL/abuild-key.rsa"
KEYCOPY="$REG_DL/abuild-key.rsa"
sudo install -m 0600 -o "$(id -un)" "$ABUILD_KEY" "$KEYCOPY"
for _f in "$IMSD_DL"/*.apk; do
for _f in "$REG_DL"/*.apk; do
python3 ./apk-resign.py "$_f" "$KEYCOPY" "$(basename "$ABUILD_KEY").pub"
done
rm -f "$KEYCOPY"
@ -286,11 +308,11 @@ if [ ! -d "$PKGDIR" ]; then
echo "$PKGDIR missing - the package builds above should have created it" >&2
exit 1
fi
for _f in "$IMSD_DL"/*.apk; do
for _f in "$REG_DL"/*.apk; do
sudo install -m 0644 -o "$(stat -c %u "$PKGDIR")" -g "$(stat -c %g "$PKGDIR")" \
"$_f" "$PKGDIR/$(basename "$_f")"
done
rm -f "$IMSD_DL"/*.apk
rm -f "$REG_DL"/*.apk
pmbootstrap index
# --- 4. build the image -------------------------------------------------------
@ -323,6 +345,7 @@ cp README.md install.sh "$STAGE/fp6-img/"
echo "built: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "default login: user / 147147 (same as official postmarketOS images)"
echo "imsd: $IMSD_REGISTRY imsd-$IMSD_VERSION (registry package, sha256-pinned)"
echo "fingerprintd: $IMSD_REGISTRY fingerprintd-$FPD_VERSION (registry package, sha256-pinned)"
} > "$STAGE/fp6-img/build-info.txt"
# sums of the extracted contents
(cd "$STAGE/fp6-img" && sha256sum -- * > sha256sums.txt)