github paolostivanin/OTPClient v5.0.0

6 hours ago

First stable release of the OTPClient 5.0 series — a complete GUI rewrite on GTK4 + libadwaita, multi-database support, token grouping, an opt-in trigger keyword for the desktop search provider, and a full sweep of crypto/import-path hardening across the shared core. Existing v4.x databases unlock and migrate automatically.

For the per-milestone changelog see the alpha/beta/rc release notes (v5.0.0-alpha1v5.0.0-rc1). What follows is the upgrade-from-4.x summary.

New

  • Complete GTK4 + libadwaita UI rewrite. Zero deprecated APIs. GtkColumnView token list, AdwDialog forms, GtkFileDialog async, AdwOverlaySplitView sidebar. AdwDialog-based shortcuts view replaces GtkShortcutsWindow.
  • Multi-database support with sidebar management, primary-database checkmark, and right-click "Move to…" between databases. Dedicated "No database yet" page on fresh install.
  • Token grouping (e.g. "Work", "Personal") with header-bar dropdown, group: / # search prefix, right-click assignment. Groups round-trip through Aegis/AuthPro/2FAS import/export.
  • Cross-database search across every loaded database, with auto-select-and-copy when results narrow to one row.
  • Hidden-by-default OTPs with click-to-reveal and auto-hide.
  • Async unlock with KDF spinner — the main loop no longer blocks while Argon2id derives the key.
  • Search-provider trigger keyword (default otp) so OTP results no longer drown under file/app runner output. KRunner subtitle no longer leaks live codes. ActivateResult now copies the OTP to the clipboard.
  • Welcome and What's New dialogs covering the redesign, groups, shortcuts, and search.
  • Native database backup as the default Settings → Backup action; format-specific export becomes migration-only with a plaintext warning.
  • Settings import/export via CLI and GUI (JSON, capped at 1 MiB).
  • CLI--output=table|json|csv for show/list/list-databases, translated user-facing strings, --list-databases, HOTP counter in CSV, bash/zsh/fish completions, --password-file refuses group/world-readable files (O_NOFOLLOW).
  • UX — paste-to-fill otpauth:// URI, KDF presets, friendly timeouts, backup-age banner with snooze and real-time clock, lock-time clipboard wipe, plural-correct strings, technical-field tooltips, countdown color pickers, optional level bar instead of seconds, drag-and-drop polish.
  • Tray — StatusNotifierItem D-Bus protocol replaces libayatana-appindicator (broader DE compatibility).
  • Performance — KDF-derived key cache, lowercased entry fields for filter, deferred HOTP writes, lazy cross-DB OTP, pre-folded labels in the search provider.

Security

  • Argon2id header validation on unlock (refuses iter / memcost / parallelism outside ARGON2ID_MIN/MAX_* bounds — closes a memory-exhaustion / KDF-weakening DoS vector).
  • KDF byte-length fixgcry_kdf_* was being passed g_utf8_strlen (character count) instead of byte count, weakening keys for non-ASCII passwords. Existing v2 databases unlock via a transparent retry path and are silently re-encrypted on the next write.
  • O_NOFOLLOW everywherepath_open_safe_regular_file() (O_RDONLY | O_NOFOLLOW | O_CLOEXEC + fstat S_ISREG) on every importer (Aegis, AuthPro, 2FAS, FreeOTP+) and both database read sites. Subsequent reads via /proc/self/fd/<n> close the symlink-swap TOCTOU window.
  • chmod 0600 on database backups so a permissive umask cannot leave .bak files group/world-readable.
  • Core-dump suppression via prctl(PR_SET_DUMPABLE,0) + RLIMIT_CORE=0 so a crash with secrets in memory cannot leak them to disk.
  • otpauth:// URI capped at 4 KB, HOTP counter capped at 2^48, PNG QR capped at 4096×4096.
  • Search-provider hardening — refuses Match, Run, ActivateResult, GetInitialResultSet, GetSubsearchResultSet when the keyword is empty (closes arbitrary local D-Bus enumeration of accounts).
  • AEAD validationgcry_cipher_checktag now catches every failure (was returning unverified plaintext on non-checksum errors). 2FAS no longer silently accepts plaintext on tag mismatch.
  • HOTP counter increment is now transactional in the CLI (rolled back if update_db fails) so a failed save no longer desyncs the counter.
  • AuthPro / 2FAS / FreeOTP+ writes NULL-check g_file_replace; secure-buffer leaks closed; allocator mismatches fixed (gcry_free for json_dumps() output throughout).
  • Aegis export plaintext now lives in secure heap.
  • Clipboard wipe on SIGINT / SIGTERM / SIGHUP and on dispose.
  • Settings import capped at 1 MiB.
  • Secret service disabled by default; purpose clarified in dialogs.
  • HOTP dedupe is now collision-aware via json_equal; the 32-bit hash no longer silently drops distinct tokens on import when two entries collide.

Fixes (selection)

  • Use-after-free in async secret lookup; double-free of filter_model in window dispose; DBus assertion on exit.
  • 2FAS importer NULL-derefs on missing servicesEncrypted, malformed payload, undersized AEAD ciphertext; secure-buffer leaks on decrypt and tag-check failure.
  • Aegis importer NULL-deref when header.slots is missing or the password slot lacks key_params.
  • AuthPro importer leaked GFile / GFileInputStream on the plain-backup path.
  • Right-click row selection no longer requires a prior left-click.
  • Notification spam suppressed during store rebuilds and search-bar close; stable notification IDs.
  • Group dropdown now restored on startup; window size persisted across sessions.
  • Schema now compiled on install; icon cache updated on install.
  • Recover gracefully when the database file is missing.
  • AdwSpinner template crash on libadwaita 1.5.

Breaking changes

  • GTK 4.10+ and libadwaita 1.5+ required (1.6+ uses native AdwButtonRow; 1.5 falls back to a backported OtpButtonRow).
  • Configuration migrated to GSettings (CLI, GUI, search provider). GKeyFile settings are not migrated automatically — reconfigure on first run.
  • libayatana-appindicator dependency removed in favour of native StatusNotifierItem.

Dependency floors

https://github.com/paolostivanin/OTPClient#requirements

Changes since rc1

  • search-provider: ActivateResult now copies the OTP to the clipboard (was a no-op aside from the notification).
  • About dialog: credit Tobias Bernard as the icon artist (was listed as designer).

sha256: 472aa2a8443554ff6ca70f66fd1c1763f07fb9ae10a5a000e6cdf12dd1d3ac3b

Don't miss a new OTPClient release

NewReleases is sending notifications on new releases.