eter_secure_buffer 1.0.0
eter_secure_buffer: ^1.0.0 copied to clipboard
Secure off-heap memory buffer for Flutter via FFI. Prevents plaintext from touching the Dart GC heap — malloc/mlock/memset/munlock outside Dart heap.
eter_secure_buffer #
A Flutter FFI package that provides secure off-heap memory buffers. Memory is allocated via malloc, locked to RAM with mlock (preventing swap to disk), and deterministically wiped with a volatile memset before free. The Dart garbage collector never sees plaintext.
What is this? #
Flutter's Uint8List lives on the Dart GC heap. That means:
- The runtime may copy it during GC compaction.
- Copies linger until the next GC cycle — potentially seconds or minutes.
- On a rooted device or with a memory dump, those bytes are recoverable.
SecureBuffer sidesteps all of this by allocating memory via native malloc, outside the Dart heap entirely. No GC. No copies. No lingering plaintext.
Security properties #
| Property | Mechanism |
|---|---|
| Heap isolation | malloc — memory is never on the Dart/VM heap |
| No GC exposure | Dart GC never touches the pointer |
| Swap prevention | mlock pins pages in RAM (best-effort) |
| Constant-time wipe | Volatile memset(0x00) — compiler cannot optimize away |
| Explicit lifecycle | You control alloc, write, read, wipe — no hidden copies |
Platform support #
| Platform | Architecture | Status |
|---|---|---|
| Android | arm64-v8a, x86_64 | Supported |
| iOS | arm64, arm64-simulator | Supported |
| macOS | arm64, x86_64 | Supported |
| Linux | x86_64 | Supported |
Installation #
dependencies:
eter_secure_buffer: ^1.0.0
Usage #
import 'package:eter_secure_buffer/eter_secure_buffer.dart';
// 1. Allocate off-heap memory (malloc + mlock)
final buffer = SecureBuffer.alloc(32);
// 2. Write data into native memory
// Data travels: Dart heap → temp native buffer → secure buffer → temp freed
final ciphertext = Uint8List.fromList([/* your bytes */]);
buffer.writeEncrypted(ciphertext);
// 3. Read data — asView() returns a LIVE pointer into native memory.
// Use immediately. Do NOT store the reference.
final view = buffer.asView();
processData(view); // use here, then let view go out of scope
// 4. Wipe: volatile memset(0x00) → munlock → free
// After this call, the buffer is invalid.
buffer.wipe();
// Checking state
print(buffer.isWiped); // true
WARNING:
asView()returns aUint8Listthat is a direct pointer into native memory. If you store this reference and then callwipe(), accessing the stored reference is undefined behavior (use-after-free). Always read and discard in the same scope.
Why not just use Uint8List? #
Uint8List is backed by the Dart GC heap. When you write final key = Uint8List(32), that object:
- Lives at a Dart-managed address.
- May be moved during GC compaction (old address still readable in a memory dump).
- Is not wiped when GC'd — just marked as free, data still in RAM.
- May be paged to disk by the OS under memory pressure.
SecureBuffer avoids all four issues.
mlock caveat on Android #
Android enforces RLIMIT_MEMLOCK (commonly 64 KB per process). mlock calls may fail silently for large buffers. SecureBuffer.alloc still succeeds — mlock failure is non-fatal by design. The volatile memset wipe is always applied regardless.
License #
Apache 2.0 — see LICENSE.