Pareidolia#
Pareidolia is a facial recognition login service for Linux.
License#
Pareidolia is distributed under the MIT and Apache 2.0 licenses.
Adds a "Liveness detection (model-free PAD)" section to CONTRIBUTING.md
covering: the evidence/veto composition (noisy-OR capped by veto product,
why not a weighted average, veto-only and fail-closed semantics, the
NotApplicable additivity that makes strobing structurally more secure);
the NIR reflectance gate; the removed motion-magnitude dead end and its
intended planar-homography replacement; the mandatory observe-only-until-
tuned posture and the `calibrate-liveness` workflow; and honest scope
(casual vs determined attacks, ISO/IEC 30107-3 per-attack evaluation).
Updates the "future milestone" PAD note to reflect what now exists, and
extends the defense matrix with a tuned-liveness column.
The guided live-vs-spoof profiling workflow that turns the liveness
checks' guessed thresholds into data-derived ones. Incremental and
GUI-shaped — each invocation does one thing, accumulating into a
persisted profile:
- `capture <class> [--label .. --append --conditions ..]` — capture a
labeled batch (live / printed-photo / screen / other). Opens the IR
camera, runs the same detect → liveness-policy pipeline auth uses,
harvests each check's sub-score `detail` into a feature vector, and
folds the batch into the profile (replacing the class, or appending).
- `status` — per-class sample counts.
- `report` — per-feature live-vs-spoof separation (means ± std, d-prime).
- `suggest-threshold` — per-feature threshold separating live from each
spoof, with the false-reject / false-accept it would incur.
- `reset [<class>]` — clear all, or one class.
Each verb is one core operation, so a future GUI can drive the same
flow — and because core ingests caller-supplied frames/features, a GUI
that owns the camera for preview can feed its own frames rather than
contend for the device.
Features are namespaced by check (e.g. `nir_reflectance.brightness`) so
multiple checks' sub-scores don't collide. A no-face capture (e.g. a
screen that's dark in IR) is skipped and counted — and an all-skipped
batch errors with a note that no-face is itself the dual-modality
defense. Gated on v4l2+inference with an arg-compatible fallback;
verified the --no-default-features build still compiles.
`LivenessProfileStore`: a single-file CBOR store for the accumulating
guided-calibration profile. Like the camera calibration store and unlike
the enrollment store, no HMAC — it holds tuning data, not secrets, and
liveness degrades gracefully on an absent/unreadable profile. Atomic
writes (temp + fsync + rename) so a crash never leaves a torn file;
versioned (refuses unknown future versions). `load` returns None when
absent (uncalibrated, normal), `load_or_default` for read-modify-write
verbs, plus `save`/`remove`.
Tested: missing-is-None, save/load round-trip, idempotent remove, no
temp-file leakage, an arbitrary-profile round-trip proptest, and a
16-thread concurrent-writer stress test asserting the file is never torn
(every post-race load decodes; last-writer-wins on content).
Property tests over the profile analysis:
- FeatureStats: min <= mean <= max, std >= 0, exact count, for any
finite value set.
- suggest_threshold: reported error rates are always valid fractions in
[0,1]; and — the headline invariant — the chosen threshold *minimises*
total error (live-reject + spoof-accept) over the sample set: no other
observed cut point does better. That's the guarantee that the
suggester actually picks the best separating threshold.
- separation: its sign follows the class-mean ordering; analysis never
panics on arbitrary data.
The data side of guided liveness calibration: an accumulating profile of
labeled samples plus pure analysis over it. No hardware — a sample is a
named feature vector (the CheckEvaluation `detail` a check produces) with
a class label, so the caller supplies samples (CLI from V4L2, a future
GUI from frames it owns). This keeps the camera-ownership boundary clean:
core never opens a device.
- `SampleClass`: Live / PrintedPhoto / Screen / Other(label) — flexible
per-attack-type labeling for ISO-30107-3-style reporting.
- `LivenessSample`: { class, features: Vec<(name, value)>, timestamp }.
Stores computed features, not raw frames — privacy (no face images in
a non-HMAC file), size, and check-agnostic extensibility. Format
versioned so raw-frame retention could be added later.
- `LivenessProfile`: accumulate via `replace_class` (default re-capture)
or `append_batch` (--append), plus `clear_class`, `class_counts`,
`feature_names`. Carries a free-form `conditions` note since a profile
is only valid for similar capture conditions.
- Analysis: `FeatureStats`, `separation` (d-prime-like class separation
per feature), and `suggest_threshold` (sweeps cut points to minimise
live-reject + spoof-accept, orienting correctly whether live reads
higher or lower than the spoof). Suggestions only — never auto-applied.
Unit-tested: accumulation semantics, stats, separation magnitude, and
threshold suggestion on cleanly-separable / overlapping / inverted-sense
data.
Pareidolia is a facial recognition login service for Linux.
Pareidolia is distributed under the MIT and Apache 2.0 licenses.