π‘οΈ nemo_crypto
The core cryptography engine for the Nemo Crypto ecosystem. It provides a production-ready key hierarchy, Argon2id key stretching, and authenticated encryption (XChaCha20-Poly1305) built on libsodium.
Built on Dart. Without Flutter dependency and enforced storage engine.
Core of the Nemo Crypto.
π¦ Ecosystem Adapters
While nemo_crypto provides the cryptographic primitives, it requires external storage interfaces to persist data. Check out the official adapters for Flutter apps:
- π
nemo_crypto_keystore: Enable biometric/silent unlock using iOS Keychain, Android Keystore, and other OS-level secure storage. - π
nemo_crypto_hive: Persist your wrapped keys using a Hive box.
π Installation
dependencies:
nemo_crypto: ^0.1.0
π οΈ Usage Guide
1. Initialize the Engine
Initialization must occur once before interacting with any cryptographic functions.
import 'package:nemo_crypto/nemo_crypto.dart';
await Nemo.initialize();
2. Configure the Keyring
The Keyring manages the key hierarchy and lifecycle state. It requires a WrapStore to persist encrypted keys (use HiveWrapStore in production).
final store = InMemoryWrapStore();
final keyring = Keyring(store);
await keyring.init(trySilentUnlock: false);
3. Create or Unlock a Store
if (keyring.status == NemoStatus.none) {
// Calibrates Argon2id to the device hardware and creates the master key
final recoveryKey = await keyring.create(passphrase: 'correct horse battery staple');
print('Save this recovery key: $recoveryKey');
} else {
// Unlocks an existing store
final success = await keyring.unlockWithPassphrase('correct horse battery staple');
}
4. Encrypt and Decrypt Records
Every record gets its own 32-byte content key. sealPadded implements ISO/IEC 7816-4 padding to mask the true length of your plaintext.
// --- Encrypt ---
final contentKey = keyring.newContentKey();
final sealed = NemoCipher.sealPadded(plainTextBytes, contentKey);
// We wrap the content key for storage, not the data itself
final wrap = keyring.wrapContentKey(contentKey);
contentKey.dispose(); // Always dispose of keys after use
// --- Decrypt ---
final unlockedKey = keyring.unwrapContentKey(wrap, WrapSource.primary);
final decrypted = NemoCipher.openPadded(sealed, unlockedKey);
unlockedKey.dispose();
π Security Architecture
- Key Wrapping: Master keys are encrypted under KEKs (Key Encryption Keys), enabling multiple independent unlock paths (passphrase, recovery key, biometrics).
- Length Obfuscation: Ciphertext sizes are rounded to predefined buckets to prevent metadata leaks based on payload length.
- The Vault: An isolated inner compartment with its own passphrase. Opening the primary keyring does not unlock the Vault, making it perfect for highly sensitive records.
- Memory Hygiene: Long-lived key material is stored in libsodium
SecureKeyinstances, utilizing guarded native memory pages that zero out automatically upondispose().
For detailed information on the Threat Model and architecture, refer to the Main Workspace Repository.
Libraries
- nemo_crypto
- Key hierarchy and authenticated encryption primitives for E2EE local stores