160 lines
6.1 KiB
Markdown
160 lines
6.1 KiB
Markdown
# att_lock
|
||
|
||
An Android-style **4×4 dot pattern** screen locker for `wlroots`-based Wayland
|
||
compositors (Sway, Hyprland, river, labwc, …). It uses the secure
|
||
[`ext-session-lock-v1`] protocol, verifies the drawn pattern against a salted
|
||
hash stored in an XDG-compliant directory, and falls back to **PAM** password
|
||
authentication.
|
||
|
||
[`ext-session-lock-v1`]: https://wayland.app/protocols/ext-session-lock-v1
|
||
|
||
## Features
|
||
|
||
- **4×4 dot grid**, drawn by dragging with a **pointer or touch** — including
|
||
Android's rule that intermediate dots on a straight line get captured.
|
||
- **Rotation / any aspect ratio**: the grid is a centred square sized from the
|
||
compositor-provided surface dimensions, so portrait, landscape and rotated
|
||
outputs all just work. HiDPI (integer `wl_output` scale) is honoured.
|
||
- **First-run setup**: with no stored pattern (or `--setup`) you draw the
|
||
pattern twice to confirm it. It is stored as a salted **yescrypt** hash
|
||
(falling back to sha512crypt) at `$XDG_DATA_HOME/att_lock/pattern.hash`
|
||
(mode `0600`), never in plaintext. The hash uses cost factor 9 (~0.22 s per
|
||
attempt) and covers your username as well as the pattern, so the same pattern
|
||
on two accounts produces unrelated hashes.
|
||
- **PAM password fallback**: start typing at the unlock screen to switch to a
|
||
password field authenticated through PAM (for "forgot pattern").
|
||
- **Background image** via a command-line argument (PNG), scaled to cover.
|
||
- Secure by construction: if the process dies while locked, the compositor
|
||
keeps the session locked (a guarantee of `ext-session-lock-v1`).
|
||
|
||
## Build
|
||
|
||
The build system is **Nix flakes**.
|
||
|
||
```sh
|
||
# One-off build:
|
||
nix build # -> ./result/bin/att_lock
|
||
|
||
# Or a dev shell + meson:
|
||
nix develop
|
||
meson setup build
|
||
ninja -C build # -> ./build/att_lock
|
||
```
|
||
|
||
Dependencies (provided by the flake): `wayland`, `wayland-scanner`, `cairo`,
|
||
`libxkbcommon`, `pam` (linux-pam), `libxcrypt`, plus `meson`/`ninja`/`pkg-config`.
|
||
|
||
## Usage
|
||
|
||
```
|
||
att_lock [options] [IMAGE]
|
||
|
||
-i, --image PATH Background image (PNG); also accepted positionally
|
||
-p, --pam-service NAME PAM service for the password fallback (default: login)
|
||
-m, --min-dots N Minimum dots in a pattern (default: 4)
|
||
--setup Force (re)creation of the stored pattern
|
||
-h, --help Show help
|
||
```
|
||
|
||
Examples:
|
||
|
||
```sh
|
||
att_lock ~/wallpaper.png # lock with a background
|
||
att_lock --setup # re-record the pattern, then unlock
|
||
att_lock -p att_lock -i bg.png # use a dedicated PAM service
|
||
```
|
||
|
||
### Unlocking
|
||
|
||
- **Draw** your pattern (≥ `--min-dots` dots) and release to unlock.
|
||
- **Type** any character to switch to the **password** field; press **Enter** to
|
||
authenticate via PAM, **Esc** to go back to the pattern.
|
||
|
||
After 4 failed attempts, further attempts are locked out for an escalating
|
||
delay — 15 s, then 30 s, 60 s, 120 s, 240 s, capped at 5 minutes — with the
|
||
remaining time shown on screen. The counter is shared between the pattern and
|
||
the password field, so switching to the password does not sidestep an active
|
||
lockout, and it is held in memory only (there is no way to restart the locker
|
||
to clear it, since the compositor keeps the session locked regardless). The
|
||
countdown uses a monotonic clock, so it cannot be skipped by changing the
|
||
system time or by suspending the machine.
|
||
|
||
## PAM configuration
|
||
|
||
The password fallback authenticates through PAM using the service named by
|
||
`--pam-service` (default `login`, which exists on most systems). For a
|
||
dedicated service, create one that stacks `pam_unix`:
|
||
|
||
**NixOS** (`configuration.nix`):
|
||
|
||
```nix
|
||
security.pam.services.att_lock = {}; # inherits sane defaults (pam_unix)
|
||
```
|
||
|
||
then run `att_lock --pam-service att_lock`.
|
||
|
||
**Other distros** (`/etc/pam.d/att_lock`):
|
||
|
||
```
|
||
auth include login
|
||
account include login
|
||
```
|
||
|
||
> The pattern itself is **not** checked by PAM — it is verified against the
|
||
> local salted hash. PAM only backs the password fallback.
|
||
|
||
## Wiring it into your compositor
|
||
|
||
Use it as your lock command, e.g. with `swayidle`:
|
||
|
||
```sh
|
||
swayidle -w \
|
||
timeout 300 'att_lock ~/wallpaper.png' \
|
||
before-sleep 'att_lock ~/wallpaper.png'
|
||
```
|
||
|
||
or bind it in Sway:
|
||
|
||
```
|
||
bindsym $mod+Escape exec att_lock ~/wallpaper.png
|
||
```
|
||
|
||
## Notes & limitations
|
||
|
||
- A pattern is not a password. The lockout above throttles *online* guessing at
|
||
the lock screen, but it cannot help offline: even at cost factor 9 there are
|
||
only 43,680 possible 4-dot patterns, so anyone who obtains `pattern.hash` can
|
||
search that space at their own pace. Raise `--min-dots` if that matters to
|
||
you; each extra dot multiplies the space by roughly an order of magnitude.
|
||
- Patterns are also far more shoulder-surfable than typed passwords, and on
|
||
touchscreens finger smudges can reveal them.
|
||
- Hash files written by versions before the `att_lock-hash-v3` format are not
|
||
readable; att_lock reports this and asks you to draw a new pattern. The PAM
|
||
password fallback still works in the meantime. In particular, v3 is the
|
||
format written since this project was renamed from `wlpattern`: the account
|
||
name and the project name are both baked into the hash input, so a pattern
|
||
enrolled under `wlpattern` (in `$XDG_DATA_HOME/wlpattern/`) cannot be carried
|
||
over and must be re-drawn once.
|
||
- Background images must be **PNG** (loaded via Cairo). Convert other formats
|
||
first (e.g. `magick in.jpg out.png`).
|
||
- The pattern is rendered identically on every connected output; the live
|
||
drag segment is drawn on the output receiving input.
|
||
- Rendering uses `wl_shm` + Cairo (no GPU/EGL dependency).
|
||
|
||
## Layout
|
||
|
||
```
|
||
flake.nix Nix flake: package + dev shell
|
||
meson.build build definition
|
||
protocols/ext-session-lock-v1.xml vendored Wayland protocol
|
||
src/main.c Wayland client, input, unlock/setup logic
|
||
src/pattern.c grid geometry + pattern sequence model
|
||
src/render.c wl_shm buffers + Cairo drawing
|
||
src/auth.c hashing/storage (crypt) + PAM
|
||
src/att_lock.h shared types and interfaces
|
||
```
|
||
|
||
## License
|
||
|
||
MIT. The vendored protocol XML is MIT (wlroots contributors).
|