Skip to content

2.1.1

Choose a tag to compare

@ioannisa ioannisa released this 02 Jun 23:10

A hardening + diagnostics release. Drop-in upgrade from 2.1.0 — the on-disk format is unchanged.

✨ New & improved

  • JVM persists without jdk.unsupported (#32). Compose Desktop release distributables whose trimmed jlink runtime omits the module no longer crash or drop writes — KSafe falls back to a software backend (same AES-256-GCM, key in a 0700 file) and migrates the data forward automatically when you add the module back.
  • Apple secureRandomBytes now sources from SecRandomCopyBytes — the Security-framework CSPRNG backing SecKey* / CryptoKit — for AES-256 master-key generation.
  • KSafe.protectionInfo is a live diagnostic — recomputed per access, so a JVM mid-process key-vault degrade is visible without restarting the app.
  • KSafe.VERSION / KSafeProtectionInfo.kSafeVersion expose the linked artifact version at runtime (handy for diagnostic UIs and audit logs).

🐛 Fixes

Data loss & correctness

  • Apple: the orphan sweep no longer deletes the v2 master key (the critical fix above).
  • getOrCreateSecret no longer silently rotates a secret it can't read back (which permanently orphaned e.g. a SQLCipher database) — it now throws instead.
  • Linux: a locked / unreachable login keyring is no longer mistaken for "key absent" (which let the orphan sweep delete recoverable ciphertext).
  • DataStore: switching a key between plain and encrypted no longer leaves a stale opposite-type value behind — including plaintext lingering on disk after a switch to encrypted.
  • A delete + HARDWARE_ISOLATED put for the same key inside one write-coalescing window no longer orphans the just-written entry.
  • clearAll() is now serialized with concurrent writes — a write issued just before it can no longer land after the wipe and resurrect data.
  • JVM appNamespace now isolates the data file, not just the OS-vault keys — two desktop apps sharing a fileName no longer clobber one file (see Upgrade notes).

Robustness & security

  • iOS biometrics: a cancelled prompt is now aborted (LAContext.invalidate()) and the continuation guarded — no "already resumed" crash or orphaned system prompt.
  • Web: applyBatch is now atomic — a QuotaExceededError mid-batch can't leave a value written without its metadata.
  • Apple in-memory cache clear() is now atomic (a write racing clearAll() could resurrect a cleared entry).
  • Biometric authorization caching uses a monotonic clock — moving the device clock backward can no longer extend a cached authorization past its window.
  • Android: rooted / userdebug devices are now detected on modern Android (API 30+), without false-positiving user-build emulators.
  • JVM SecurityChecker no longer fails KSafe(...) construction on a trimmed jlink runtime missing java.management.

🧭 Upgrade notes

  • Drop-in from 2.1.0 — the on-disk format is unchanged.
  • getOrCreateSecret can now throw IllegalStateException when a secret exists but can't be read back (vault unavailable / key invalidated / corrupt), instead of silently minting a new one. Handle it at the call site.
  • JVM appNamespace: the on-disk path changes for apps that explicitly set it — data now lives in a per-namespace subdirectory. Existing data is migrated forward automatically (copied, not moved), and apps that don't set appNamespace are unaffected.

📋 Full, detailed notes (mechanisms, regression tests, per-file specifics): CHANGELOG.md


Full Changelog: 2.1.0...2.1.1