SecureLocalStorage
Features
- Strictly typed — native support for
String,double,int,bool, andBigIntwithout manual casting - Batch writes — call
put()multiple times, thensaveToFile()once; no redundant disk I/O - Integrity verification — HMAC-based hashing detects tampering and corruption on every read
- ChaCha20 encryption — faster than AES-256 on mobile; unlike AES-ECB, leaks no plaintext patterns
- Argon2id key hardening — optional secure key derivation hardens passwords against offline attacks
- Pure Dart — no native platform dependencies, no async boilerplate for reads and writes
Getting started
Add the dependency to your pubspec.yaml:
dependencies:
secure_local_key_value_storage: ^1.0.0
Then run:
dart pub get
Usage
Basic (unencrypted, integrity-checked)
import 'dart:io';
import 'package:secure_local_key_value_storage/secure_local_key_value_storage.dart';
final storage = SecureLocalKeyValueStorage(
sourceFile: File('my_data.bin'),
);
storage.readFromFile();
storage.put('username', 'alice');
storage.put('score', 9001);
storage.put('premium', true);
storage.saveToFile(); // Write once after all puts
print(storage.get('username')); // alice
print(storage.get('score')); // 9001
Encrypted
final storage = SecureLocalKeyValueStorage(
key: 'my-secret-password',
sourceFile: File('my_data.bin'),
);
storage.readFromFile();
storage.put('token', 'abc123');
storage.saveToFile();
With Argon2id key hardening
For high-security use cases, set deriveSecureKey: true to run Argon2id key derivation before encryption. This significantly hardens the key against brute-force and offline attacks.
⚠️ Performance note: Argon2id derivation is intentionally expensive (~300ms–3 seconds depending on device). Use a static or singleton instance to avoid re-deriving on every app start.
final storage = SecureLocalKeyValueStorage(
key: 'my-secret-password',
sourceFile: File('my_data.bin'),
deriveSecureKey: true, // Enables Argon2id — initialize once and reuse
);
storage.readFromFile();
API
Constructor
SecureLocalKeyValueStorage({
String? key,
required File sourceFile,
bool deriveSecureKey = false,
})
| Parameter | Description |
|---|---|
key |
Optional encryption password. If omitted, data is stored unencrypted but still integrity-checked. |
sourceFile |
The file to read from and write to. |
deriveSecureKey |
If true, derives a hardened key from key using Argon2id. Expensive — reuse the instance. |
Methods
| Method | Description |
|---|---|
readFromFile() |
Loads and decrypts data from the source file. Call once on startup. |
saveToFile() |
Serializes, hashes, and writes the current state to disk. |
put(String key, dynamic value) |
Stores a value. Supported types: String, double, int, bool, BigInt. |
get(String key) |
Returns the typed value for a key, or null if not found. |
containsKey(String key) |
Returns true if the key exists in storage. |
remove(String key) |
Removes a key from the in-memory storage. Call saveToFile() to persist. |
delete() |
Deletes the file from disk and clears in-memory storage. |
Security notes
- Data is encrypted with ChaCha20 using a nonce derived from the content hash, ensuring a unique nonce per write without storing it separately.
- Integrity is verified on every
readFromFile()using HMAC-SHA-256. A hash mismatch throws aFormatExceptionand returns empty storage — tampered or corrupted files are never silently accepted. - When
deriveSecureKeyis enabled, the password is pre-hashed 100 times with SHA-256 before being passed to Argon2id, guarding against side-channel attacks at the native code boundary. - If no
keyis provided, data is stored in plaintext but still protected by the integrity hash.
Supported types
| Dart type | Stored as |
|---|---|
String |
Plain text |
int |
Decimal string |
double |
Decimal string |
bool |
"true" / "false" |
BigInt |
Decimal string |
License
MIT