SecureLocalStorage

Features

  • Strictly typed — native support for String, double, int, bool, and BigInt without manual casting
  • Batch writes — call put() multiple times, then saveToFile() 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 a FormatException and returns empty storage — tampered or corrupted files are never silently accepted.
  • When deriveSecureKey is 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 key is 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