InstallSecret

Per-install secret used for deterministic idempotency and similar local-only features.

The secret is 32 random bytes generated once per install. It is wrapped with an Android Keystore AES-GCM key (parity with com.sarastarquant.kioskops.sdk.crypto.DatabaseEncryptionProvider) so the raw value never sits in SharedPreferences in the clear; the wrapping key never leaves the Keystore / TEE / StrongBox.

Installs upgraded from releases prior to 1.2.0 that stored the secret base64-encoded in plaintext are migrated on first access: the legacy value is wrapped in place and the unwrapped entry is removed. Migration runs at most once per install and is concurrent-safe via a monitor.

Returned byte arrays are caller-owned. Callers should pass them to the consuming operation and immediately zero the buffer; see zeroize. The secret itself never leaves the device unless the host explicitly exports diagnostics (it is not included by default).

Since

1.2.0 (wrapping + migration)

Functions

Link copied to clipboard
fun getOrCreate(context: Context, bytes: Int = DEFAULT_SECRET_BYTES): ByteArray

Return the per-install secret, creating it on first access or migrating a legacy unwrapped secret if one is present. Callers should zeroize the returned array after feeding it to the consuming operation.

Link copied to clipboard
fun zeroize(bytes: ByteArray)

Overwrite a sensitive byte array with zeros. Java does not guarantee the array isn't swapped to disk or retained by the GC's copying collector, so this is defense-in-depth, not a hard guarantee. Still worth doing for secrets held on the stack briefly.