Skip to content

Bump eu.anifantakis:ksafe from 3.0.0 to 3.1.0 - #117

Merged
patrickunterwegs merged 1 commit into
mainfrom
dependabot/gradle/eu.anifantakis-ksafe-3.1.0
Aug 28, 2026
Merged

Bump eu.anifantakis:ksafe from 3.0.0 to 3.1.0#117
patrickunterwegs merged 1 commit into
mainfrom
dependabot/gradle/eu.anifantakis-ksafe-3.1.0

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Aug 27, 2026

Copy link
Copy Markdown
Contributor

Bumps eu.anifantakis:ksafe from 3.0.0 to 3.1.0.

Release notes

Sourced from eu.anifantakis:ksafe's releases.

3.1.0

KSafe no longer depends on a third-party cryptography library — on any platform. Drop-in upgrade: existing data is untouched, nothing migrates.

implementation("eu.anifantakis:ksafe:3.1.0")
implementation("eu.anifantakis:ksafe-compose:3.1.0")     // optional
implementation("eu.anifantakis:ksafe-biometrics:3.1.0")  // optional

New

  • Own Apple CryptoKit bridgedev.whyoleg.cryptography is gone. Apple still does the crypto; the wrapper (and its transitive IrLinkageError risk) disappeared. iosArm64 footprint: 13 artifacts / 3.7 MB → 1 / 16 KB. Old ciphertext verified byte-compatible on a physical iPhone, 286 device tests green.
  • Mode-typed viewsKSafePlain, KSafeEncrypted, KSafeHardwareIsolated: the write mode becomes a type, not an argument. Own section below. 👇
  • Interrupted key rotation resumes automatically on the next KSafe instance — same generation, even under Policy.Never. Entries skipped by a completed rotation (e.g. device locked) are retried on later instances with a bounded budget: KSafeConfig(keyRotationRetryAttempts = 3). 📖 https://github.com/ioannisa/ksafe/blob/HEAD/docs/KEY_ROTATION.md
  • Typed AES key strengthKSafeConfig(aesKeySize = KSafeAesKeySize.BITS_128) (default BITS_256); WebCrypto now honours it too. New key material only; existing keys keep their size until rotateKeys().

Mode-typed views: KSafePlain / KSafeEncrypted / KSafeHardwareIsolated

When one store holds both preferences and secrets, the per-call mode = argument is a convention — and one forgotten argument silently writes with the wrong protection. The three view types turn that convention into a compiler rule: each wraps an existing KSafe instance and freezes the write mode at construction. No member of these types takes a mode parameter, so a call site can't store a secret in plaintext — or encrypt a trivial preference — by picking the wrong argument.

val prefs = KSafePlain(ksafe)            // or: ksafe.plain
val vault = KSafeHardwareIsolated(ksafe) // or: ksafe.hardwareIsolated
prefs.putDirect("theme", "dark")         // always Plain — nothing to forget
vault.put("master_key", secret)          // always requests StrongBox / Secure Enclave
var theme by prefs("dark")               // delegate: key = property name, writes Plain
val pin by vault.asWritableFlow("", key = "pin")   // .set() writes hardware-isolated

The full write surface is covered — put/putDirect, the by view(...) delegate, asFlow/asWritableFlow/asStateFlow/asMutableStateFlow/getStateFlow, and via :ksafe-compose both mutableStateOf and rememberKSafeState — so the guarantee has no gap where writes actually happen.

In Koin, the types replace stringly named(...) qualifiers. One store, distinct injectable views:

single { KSafe(context = androidApplication(), fileName = "app") }
single { KSafePlain(get()) }
single { KSafeHardwareIsolated(get()) }
class SettingsRepository(private val prefs: KSafePlain)          // writes always Plain
class AuthRepository(private val vault: KSafeHardwareIsolated)   // writes always request SE/StrongBox

All views over one instance share the same file, key namespace and cache — mixed-mode entries keep working, and a value written through one view is immediately visible through any other. KSafeEncrypted/KSafeHardwareIsolated also freeze the unlock policy: KSafeEncrypted(ksafe, requireUnlockedDevice = true) makes every write through that view strict, while the default constructor inherits the instance's configured policy.

Three honest boundaries:

  • Write-side only — reads stay mode-free and auto-detect each entry's protection.
  • KSafeHardwareIsolated requests, it doesn't guarantee — without StrongBox / Secure Enclave the write degrades to the documented next-best custody and reports it via protectionInfo.

... (truncated)

Changelog

Sourced from eu.anifantakis:ksafe's changelog.

[3.1.0] - 2026-08-24

Added

  • KSafe now owns its Apple CryptoKit integration. A small bundled Swift/C bridge calls CryptoKit.AES.GCM directly on iOS and macOS, while Kotlin owns validation and the frozen 12-byte nonce || ciphertext || 16-byte tag envelope. NIST AES-128/AES-256 vectors, an independent AAD vector, and tamper tests pin the bridge's output and compatibility.

  • AES key strength is typed and works on every platform. Configure KSafeConfig(aesKeySize = KSafeAesKeySize.BITS_128) or keep the default BITS_256. WebCrypto now honors the same choice when minting a new non-extractable key; existing keys on every platform retain their inherent size until rotateKeys() creates a new generation.

  • Interrupted key rotations resume automatically at the same generation on the next KSafe instance. A 3.1.0 rotation writes "r":1 atomically with its generation bump, before it touches any entry, and changes that lifecycle state to "r":0 only after the entry pass and superseded-master sweep finish. A process death therefore leaves a readable mixed-generation store that the next instance repairs idempotently at the same generation, even under KSafeKeyRotationPolicy.Never; this is one lifecycle bit, not a per-entry journal.

    3.0.0 compatibility is deliberately conservative. The released 3.0.0 generation record has no r field, so absence cannot distinguish a completed rotation from one interrupted by a crash. On its first 3.1.0 startup, KSafe treats every such record as a completed 3.0.0 rotation, adds "r":0, preserves its generation/timestamp, and performs no resume, generation bump, entry rewrite, key sweep, or same-launch MaxAge rotation. Normal policy resumes on the following launch. Thus upgrading can never make 3.1.0 guess wrong and disturb an already shipped 3.0.0 store; if 3.0.0 really did leave entries behind, they remain readable under their recorded old key and a later explicit rotateKeys() (or due MaxAge pass) moves them normally. Unknown future lifecycle values are preserved and rejected fail-closed.

  • A normally completed rotation no longer loses retryable work. If a pass returns with skipped entries — most importantly a strict requireUnlockedDevice entry while the device is locked — KSafe records "r":0 plus "rp":N, a bounded next-instance retry budget. KSafeConfig.keyRotationRetryAttempts controls the initial count (3 by default; 0 disables this retry path). The current instance starts no timer and performs no same-run retry. Each new KSafe instance consumes at most one attempt and retries the same generation, without minting another key or resetting the generation-birth "ts" clock; this lifecycle completion also runs under Never. The claim durably changes r:0,rp:N to r:1,rp:N-1 before work, so a crash cannot refill the budget; r:1,rp:0 can recover only the final already-claimed attempt. If MaxAge is already due, its fresh-generation rotation takes precedence. failed alone never arms retry because it denotes a definitive, not retryable, problem.

  • Mode-typed views: KSafePlain, KSafeEncrypted, KSafeHardwareIsolated. Thin wrappers over an existing KSafe instance that freeze the write mode at construction — no member of these types takes a mode parameter, so a call site can never accidentally encrypt a preference or store a secret in plaintext by picking the wrong argument. They cover the full write surface (put/putDirect, the by view(...) delegate, asFlow/asWritableFlow/ asStateFlow/asMutableStateFlow/getStateFlow, and — via :ksafe-composemutableStateOf and rememberKSafeState), and forward the nullable key untouched so key-from-property-name derivation behaves exactly as on KSafe. All views over one instance share the same file, key namespace and cache, so a single store keeps serving mixed-mode ezntries; in Koin the types

... (truncated)

Commits

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Bumps [eu.anifantakis:ksafe](https://github.com/ioannisa/ksafe) from 3.0.0 to 3.1.0.
- [Release notes](https://github.com/ioannisa/ksafe/releases)
- [Changelog](https://github.com/ioannisa/KSafe/blob/main/CHANGELOG.md)
- [Commits](ioannisa/KSafe@3.0.0...3.1.0)

---
updated-dependencies:
- dependency-name: eu.anifantakis:ksafe
  dependency-version: 3.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added dependencies Pull requests that update a dependency file java Pull requests that update java code labels Aug 27, 2026
@patrickunterwegs
patrickunterwegs merged commit 2d69532 into main Aug 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file java Pull requests that update java code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant