https://github.com/harrytmthy/safebox
A blazing-fast drop-in replacement for EncryptedSharedPreferences on Android
https://github.com/harrytmthy/safebox
aes-gcm android android-keystore android-library android-sdk android-security chacha20-poly1305 cryptography encryptedsharedpreferences encryption kotlin mmap safebox secure-storage sharedpreferences
Last synced: about 1 month ago
JSON representation
A blazing-fast drop-in replacement for EncryptedSharedPreferences on Android
- Host: GitHub
- URL: https://github.com/harrytmthy/safebox
- Owner: harrytmthy
- License: mit
- Created: 2025-05-08T15:17:18.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2025-10-17T12:44:26.000Z (4 months ago)
- Last Synced: 2025-10-18T13:49:17.170Z (4 months ago)
- Topics: aes-gcm, android, android-keystore, android-library, android-sdk, android-security, chacha20-poly1305, cryptography, encryptedsharedpreferences, encryption, kotlin, mmap, safebox, secure-storage, sharedpreferences
- Language: Kotlin
- Homepage:
- Size: 1.16 MB
- Stars: 24
- Watchers: 1
- Forks: 2
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Codeowners: .github/CODEOWNERS
Awesome Lists containing this project
README
# SafeBox
[](https://github.com/harrytmthy/safebox/actions)
[](https://github.com/harrytmthy/safebox/blob/main/LICENSE)
[](https://github.com/harrytmthy/safebox/releases)
A secure, blazing-fast alternative to `EncryptedSharedPreferences`, designed for Android projects which demand both **speed** and **security**.
## 🚨 EncryptedSharedPreferences is Deprecated
As of **Jetpack Security 1.1.0-alpha07 (April 9, 2025)**, `EncryptedSharedPreferences` has been deprecated with no official replacement. Without continued support from Google, it may fall behind in cryptography standards, leaving sensitive data exposed.
SafeBox can help you [migrate](docs/MIGRATION.md) easily using the same `SharedPreferences` API. Since v1.2.0, SafeBox delivers **~184× faster init**, **~50× faster reads**, and **~9× faster writes** than EncryptedSharedPreferences.
## Why SafeBox?
| | SafeBox | EncryptedSharedPreferences |
|----------------|------------------------------------------------------|----------------------------------------------|
| Encryption | **Modern ChaCha20-Poly1305 with secure key vault** | Older AES setup tied to deprecated MasterKey |
| Storage format | **Memory-mapped binary file with minimal headers** | XML text file with tag/attribute overhead |
| I/O model | **New data is tail-appended** | New data rewrites the whole XML |
| Concurrency | **Stays smooth on concurrent writes** | Gets slower on concurrent writes |
| Scalability | **Stable performance on large size** | Keeps getting heavier as data grows |
| Durability | **Low-storage failures fallback to a recovery file** | Low-storage failures = data loss |
| Customization | **Cipher providers are replaceable** | Ciphers are not customizable |
SafeBox uses **deterministic encryption** for reference keys (for fast lookup) and **non-deterministic encryption** for values (for strong security). Both powered by a single ChaCha20 key protected via AES-GCM and stored securely.
🔑 SafeBox Key Derivation & Encryption Flow
```
[Android Keystore-backed AES-GCM Key]
↓
[ChaCha20-Poly1305 Key]
↙ ↘
Reference Keys Entry Values
(deterministic IV) (randomized IV)
```
Compared to EncryptedSharedPreferences:
```
[Android Keystore MasterKey (deprecated)]
↙ ↘
[AES-SIV Key] [AES-GCM Key]
↓ ↓
Reference Keys Entry Values
```
## Installation
```kotlin
dependencies {
implementation("io.github.harrytmthy:safebox:1.3.0")
// Optional: standalone crypto helper
implementation("io.github.harrytmthy:safebox-crypto:1.3.0")
}
```
## Basic Usage
Create the instance:
```kotlin
val prefs: SharedPreferences = SafeBox.create(context, PREF_FILE_NAME)
```
Then use it like any `SharedPreferences`:
```kotlin
prefs.edit()
.putInt("userId", 123)
.putString("name", "Luna Moonlight")
.apply()
val userId = prefs.getInt("userId", -1)
val email = prefs.getString("email", null)
```
Once created, you can retrieve the same instance without a `Context`:
```kotlin
SafeBox.get(PREF_FILE_NAME) // or SafeBox.create(context, PREF_FILE_NAME)
.edit()
.clear()
.commit()
```
> Prefer `SafeBox.getOrNull(fileName)` if you need a safe retrieval without throwing.
### Understanding SafeBox Behavior
SafeBox returns the same instance per filename:
```kotlin
val a1 = SafeBox.create(context, "fileA")
val a2 = SafeBox.create(context, "fileA")
val a3 = SafeBox.get("fileA")
assertTrue(a1 === a2) // same reference
assertTrue(a1 === a3) // same reference
val b = SafeBox.create(context, "fileB")
assertTrue(a1 !== b) // different filenames = different instances
```
> Repeating `SafeBox.create(context, fileName)` returns the existing instance for that `fileName`. When an instance already exists, **all parameters are ignored** except a non-null `stateListener`, which replaces the current listener.
> Need lifecycle hooks for diagnostics or analytics? ➡️ [Read the Observability Guide](docs/OBSERVABILITY.md)
## Migrating from EncryptedSharedPreferences
SafeBox is a drop-in replacement for `EncryptedSharedPreferences`.
➡️ [Read the Migration Guide](docs/MIGRATION.md)
## Text Encryption Support
SafeBox includes a simple text encryption helper (`SafeBoxCrypto`) you can use for things like
encrypting values before writing to Room or sending over the network:
```kotlin
// 1. Create a secret and store it in your own vault
val secret: String = SafeBoxCrypto.createSecret()
// 2. Use it to encrypt
val userEntity = UserEntity(
id = SafeBoxCrypto.encrypt(userId, secret),
// ...
)
// 3. Retrieve it later
val userId: String = SafeBoxCrypto.decrypt(userEntity.id, secret)
```
If you only need the helper, use the standalone `:safebox-crypto` module.
## Performance Benchmarks
Average times measured over **100 samples** on an emulator:
📊 v1.3.0 Benchmark



| Operation | SafeBox v1.3.0 | EncryptedSharedPreferences |
|-----------------------------|------------------------------|----------------------------|
| Initialization | **0.19ms** (*201.1× faster*) | 38.7ms |
| Get 1 entry | **0.01ms** (*76.9× faster*) | 0.50ms |
| Get 3 entries | **0.02ms** (*68.5× faster*) | 1.27ms |
| Get 5 entries | **0.03ms** (*76.8× faster*) | 2.25ms |
| Get 10 entries | **0.06ms** (*66.4× faster*) | 4.07ms |
| Put 1 entry, then commit | **0.17ms** (*7.8× faster*) | 1.31ms |
| Put 3 entries, then commit | **0.46ms** (*4.7× faster*) | 2.16ms |
| Put 5 entries, then commit | **0.73ms** (*4.5× faster*) | 3.32ms |
| Put 10 entries, then commit | **1.46ms** (*4.3× faster*) | 6.28ms |
Even on **multiple single commits**, SafeBox remains faster:
| Operation | SafeBox v1.3.0 | EncryptedSharedPreferences |
|---------------------------|-----------------------------|----------------------------|
| Commit 3 single entries | **0.52ms** (*9.5× faster*) | 4.90ms |
| Commit 5 single entries | **0.85ms** (*8.2× faster*) | 6.91ms |
| Commit 10 single entries | **1.71ms** (*6.6× faster*) | 11.27ms |
| Commit 100 single entries | **16.51ms** (*4.3× faster*) | 71.34ms |
📊 v1.2.0 Benchmark



| Operation | SafeBox v1.2.0 | EncryptedSharedPreferences |
|-----------------------------|------------------------------|----------------------------|
| Initialization | **0.21ms** (*184.3× faster*) | 38.7ms |
| Get 1 entry | **0.01ms** (*50.0× faster*) | 0.50ms |
| Get 3 entries | **0.04ms** (*31.8× faster*) | 1.27ms |
| Get 5 entries | **0.07ms** (*32.1× faster*) | 2.25ms |
| Get 10 entries | **0.15ms** (*27.1× faster*) | 4.07ms |
| Put 1 entry, then commit | **0.22ms** (*5.95× faster*) | 1.31ms |
| Put 3 entries, then commit | **0.52ms** (*4.15× faster*) | 2.16ms |
| Put 5 entries, then commit | **0.98ms** (*3.39× faster*) | 3.32ms |
| Put 10 entries, then commit | **1.64ms** (*3.83× faster*) | 6.28ms |
Even on **multiple single commits**, SafeBox remains faster:
| Operation | SafeBox v1.2.0 | EncryptedSharedPreferences |
|---------------------------|------------------------------|----------------------------|
| Commit 3 single entries | **0.53ms** (*9.25× faster*) | 4.90ms |
| Commit 5 single entries | **0.95ms** (*7.27× faster*) | 6.91ms |
| Commit 10 single entries | **1.96ms** (*5.75× faster*) | 11.27ms |
| Commit 100 single entries | **17.41ms** (*4.10× faster*) | 71.34ms |
📊 v1.1.0 Benchmark



| Operation | SafeBox v1.1.0 | EncryptedSharedPreferences |
|-----------------------------|----------------|----------------------------|
| Initialization | **0.38ms** | 38.7ms (*10,079% slower*) |
| Get 1 entry | **0.33ms** | 0.50ms (*52% slower*) |
| Get 3 entries | **0.94ms** | 1.27ms (*35% slower*) |
| Get 5 entries | **1.56ms** | 2.25ms (*44% slower*) |
| Get 10 entries | **3.06ms** | 4.07ms (*33% slower*) |
| Put 1 entry, then commit | **0.49ms** | 1.31ms (*167% slower*) |
| Put 3 entries, then commit | **1.34ms** | 2.16ms (*61% slower*) |
| Put 5 entries, then commit | **2.36ms** | 3.32ms (*41% slower*) |
| Put 10 entries, then commit | **4.20ms** | 6.28ms (*50% slower*) |
Even on **multiple single commits**, SafeBox remains faster:
| Operation | SafeBox v1.1.0 | EncryptedSharedPreferences |
|------------------------------|----------------|----------------------------|
| Commit 3 single entries | **1.50ms** | 4.90ms (*227% slower*) |
| Commit 5 single entries | **2.39ms** | 6.91ms (*189% slower*) |
| Commit 10 single entries | **5.07ms** | 11.27ms (*122% slower*) |
| Commit 100 single entries | **38.12ms** | 71.34ms (*87% slower*) |
📊 v1.0.0 Benchmark



| Operation | SafeBox v1.0.0 | EncryptedSharedPreferences |
|-----------------------------|----------------|----------------------------|
| Get 1 entry | **0.39ms** | 0.50ms (*28% slower*) |
| Get 3 entries | **0.94ms** | 1.27ms (*35% slower*) |
| Get 5 entries | **1.37ms** | 2.25ms (*64% slower*) |
| Get 10 entries | **3.29ms** | 4.07ms (*24% slower*) |
| Put 1 entry, then commit | **0.55ms** | 1.31ms (*138% slower*) |
| Put 3 entries, then commit | **1.25ms** | 2.16ms (*73% slower*) |
| Put 5 entries, then commit | **2.33ms** | 3.32ms (*42% slower*) |
| Put 10 entries, then commit | **4.73ms** | 6.28ms (*33% slower*) |
Even on **multiple single commits**, SafeBox remains faster:
| Operation | SafeBox v1.0.0 | EncryptedSharedPreferences |
|------------------------------|----------------|----------------------------|
| Commit 3 single entries | **1.94ms** | 4.90ms (*152% slower*) |
| Commit 5 single entries | **2.84ms** | 6.91ms (*143% slower*) |
| Commit 10 single entries | **5.47ms** | 11.27ms (*106% slower*) |
| Commit 100 single entries | **33.19ms** | 71.34ms (*115% slower*) |
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup, formatting, testing, and PR guidelines.
## 💖 Support SafeBox
If SafeBox helped secure your app or saved your time, consider sponsoring to support future improvements and maintenance!
[](https://github.com/sponsors/harrytmthy)
## License
```
MIT License
Copyright (c) 2025 Harry Timothy Tumalewa
```