Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [v0.3.3](https://github.com/MicroClub-USTHB/M-Security/releases/tag/v0.3.3) - 2026-03-28

### Added

- Zero-copy vault reads via `memmap2` memory-mapped I/O — `vault_read` and `vault_read_stream` now slice directly into mapped file pages instead of allocating heap buffers with `read_exact`.
- `VaultMmap` wrapper with `mlock()` on unix to pin ciphertext pages in RAM and prevent swap-to-disk. Graceful fallback to heap reads when mmap fails (32-bit targets, low `mlock` limits).
- mmap invalidation/recreation after every vault mutation (write, delete, defrag, resize).
- ELF linker version script (`ffi-exports.map`) restricting Android/Linux `.so` dynamic symbol table to FRB FFI symbols only — hides `#[no_mangle]` symbols leaked by dependencies.
- `build.rs` for conditional version script application on Android/Linux cdylib builds.

### Changed

- Switched all 50 FRB functions from SSE to CST+DCO codec via `full_dep: true` — `Vec<u8>` returns now use `allo-isolate` `ExternalTypedData` (pointer transfer, no memcpy) instead of SSE serialization.
- Release profile hardened: `lto = "fat"`, `codegen-units = 1`, `strip = "symbols"`, `opt-level = 3`.
- `VaultHandle` field order: `mmap` before `file` so `munlock`/`munmap` runs while the fd is still open.

### Security

- Symbol stripping removes internal Rust function names from release binaries — `nm -D` shows only FRB entry points and libc on ELF targets.
- `mlock()` prevents OS from swapping mmap'd ciphertext pages to disk swap.
- Fewer intermediate `Vec<u8>` copies means shorter plaintext residency in memory.
- ZeroizeOnDrop behavior preserved — no regression from zero-copy refactoring.

## [v0.3.2](https://github.com/MicroClub-USTHB/M-Security/releases/tag/v0.3.2) - 2026-03-26

### Added
Expand Down
13 changes: 5 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,11 @@ Thank you for your interest in contributing to M-Security! This project is built

## Project Vision

M-Security aims to be a complete security toolkit for Flutter. The current release (v0.3.2) includes the cryptographic foundation, streaming encryption, compression, EVFS v2 (defragmentation, resize, health check), EVFS v3 (streaming I/O), and an encrypted virtual file system. Future releases will continue building on top of it:

1. **Cryptographic primitives** (v0.1.0): AEAD encryption, hashing, password hashing, key derivation
2. **Streaming, compression, and EVFS** (v0.3.0): Chunk-based streaming encryption with progress callbacks, Zstd/Brotli compression pipeline, `.vault` container with named segments, WAL crash recovery, shadow index, and secure deletion
3. **EVFS v2: Defrag, resize, health** (v0.3.1): Vault defragmentation with per-move WAL crash safety, vault resize (grow/shrink) with shadow index + WAL relocation, vault health check with consistency invariant, dynamic index sizing
4. **EVFS v3: Streaming I/O** (v0.3.2, current): Constant-memory streaming writes/reads with per-chunk AEAD, domain-separated nonces, VaultChunkAad for splice defense, progress callbacks, interop between streaming and monolithic segments
5. **Stealth storage** (planned): Ephemeral secrets in Rust-managed memory with derived-path obfuscation
6. **Hardware integration** (planned): Key wrap with Secure Enclave (iOS) / KeyStore (Android), biometric unlock flow (FaceID/Fingerprint), native Swift/Kotlin bridge layer
M-Security aims to be a unified, high-performance security layer for Flutter, making strong data protection simple to use and easy to integrate. It is designed to give developers a reliable foundation for handling sensitive data without sacrificing performance or developer experience.

The project focuses on securing data across its entire lifecycle, including storage, in-app processing, and access, while maintaining a consistent and intuitive API. It also serves as a cross-platform trust layer, bringing platform-specific security features into a single, cohesive interface.

Ultimately, M-Security strives to become a long-term security backbone for Flutter applications, where robust security is built in by default rather than added later.

Contributions to any of these areas are welcome. If you want to work on an upcoming feature, open an issue first to discuss the approach.

Expand Down
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Built and maintained by the **Dev Department** of [MicroClub](https://github.com
| **Password Hashing** | Argon2id | PHC winner, Mobile and Desktop presets |
| **Key Derivation** | HKDF-SHA256 | RFC 5869, extract-then-expand with domain separation |
| **Encrypted VFS (EVFS)** | `.vault` container | Named segments, WAL recovery, shadow index, secure deletion |
| **Zero-Copy I/O** | mmap + DCO codec | Memory-mapped vault reads, zero-copy Rust-to-Dart transfers |

**Security by design:**

Expand All @@ -38,14 +39,16 @@ Built and maintained by the **Dev Department** of [MicroClub](https://github.com
- AEAD tag verification prevents silent decryption of tampered data
- `panic = "abort"` in release profile, preventing undefined behavior from panics crossing FFI
- `clippy::unwrap_used = "deny"`, ensuring all operations return `Result<T, CryptoError>`
- Release builds strip all symbols except FRB entry points (LTO + ELF version script)
- `mlock()` pins mmap'd ciphertext pages to prevent swap-to-disk (unix)

## Installation

Add to your `pubspec.yaml`:

```yaml
dependencies:
m_security: ^0.3.2
m_security: ^0.3.3
```

Then run:
Expand Down Expand Up @@ -329,13 +332,13 @@ hkdf_expand(prk, info, output_len) -> Result<Vec<u8>>

## Testing

**Rust unit tests** (317 tests including EVFS streaming and defrag):
**Rust unit tests** (331 tests including EVFS streaming and defrag):

```bash
cd rust && cargo test
```

**Dart integration tests** (63 tests across all features, requires a running device/simulator):
**Dart integration tests** (76 tests across all features, requires a running device/simulator):

```bash
cd example
Expand All @@ -351,7 +354,7 @@ flutter test integration_test/
| Dart SDK | ^3.10.8 |
| Flutter SDK | >=3.3.0 |

**Rust crates:** `aes-gcm` 0.10, `chacha20poly1305` 0.10, `blake3` 1.8, `sha3` 0.10, `argon2` 0.5, `hkdf` 0.12, `zstd` 0.13, `brotli` 7.0, `zeroize` 1.8
**Rust crates:** `aes-gcm` 0.10, `chacha20poly1305` 0.10, `blake3` 1.8, `sha3` 0.10, `argon2` 0.5, `hkdf` 0.12, `zstd` 0.13, `brotli` 7.0, `zeroize` 1.8, `memmap2` 0.9

## Roadmap

Expand All @@ -361,7 +364,8 @@ flutter test integration_test/
| **Compression pipeline** | Zstd/Brotli compression integrated into streaming and EVFS | v0.3.0 |
| **Encrypted Virtual File System (EVFS)** | `.vault` container with named segments, WAL recovery, shadow index, secure deletion | v0.3.0 |
| **EVFS v2: Defrag & resize** | Online defragmentation, vault resizing, health diagnostics | v0.3.1 |
| **EVFS v3: Streaming I/O** | Constant-memory streaming reads/writes, per-chunk AEAD, progress callbacks | v0.3.2 |
| **EVFS v2: Streaming I/O** | Constant-memory streaming reads/writes, per-chunk AEAD, progress callbacks | v0.3.2 |
| **Zero-copy FFI optimization** | mmap vault reads, DCO codec, release profile hardening, symbol stripping | v0.3.3 |
| **EVFS v2: Key rotation** | Re-encrypt vault with new master key | Planned |
| **Stealth storage** | Ephemeral secrets in Rust-managed memory with derived-path obfuscation | Planned |
| **Hardware key wrap** | Master key in Secure Enclave (iOS) / KeyStore (Android) with biometric unlock | Planned |
Expand Down
16 changes: 9 additions & 7 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class ExampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'M-Security v0.3.2',
title: 'M-Security v0.3.3',
theme: ThemeData(colorSchemeSeed: Colors.blue, useMaterial3: true),
home: const DemoHome(),
);
Expand All @@ -41,7 +41,7 @@ class _DemoHomeState extends State<DemoHome> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('M-Security v0.3.2')),
appBar: AppBar(title: const Text('M-Security v0.3.3')),
body: IndexedStack(
index: _tab,
children: const [
Expand Down Expand Up @@ -774,7 +774,8 @@ class _VaultTabState extends State<_VaultTab> {
onProgress: (p) => setState(() => _streamProgress = p),
);

_status = 'Stream-wrote ${_fmtBytes(BigInt.from(sizeBytes))} as '
_status =
'Stream-wrote ${_fmtBytes(BigInt.from(sizeBytes))} as '
'"stream-${_streamSizeKb.text}kb.bin"';
await _refreshList();
await _refreshCapacity();
Expand Down Expand Up @@ -816,7 +817,8 @@ class _VaultTabState extends State<_VaultTab> {
offset += chunk.length;
}

_status = 'Stream-read "$name": ${_fmtBytes(BigInt.from(total))} '
_status =
'Stream-read "$name": ${_fmtBytes(BigInt.from(total))} '
'in ${chunks.length} chunks — ${match ? "PASS" : "FAIL"}';
} catch (e) {
_status = 'Error: $e';
Expand Down Expand Up @@ -1097,8 +1099,7 @@ class _VaultTabState extends State<_VaultTab> {
const Divider(height: 24),

// Streaming segment I/O
Text('Streaming I/O',
style: Theme.of(context).textTheme.titleSmall),
Text('Streaming I/O', style: Theme.of(context).textTheme.titleSmall),
const SizedBox(height: 8),
TextField(
controller: _streamSizeKb,
Expand Down Expand Up @@ -1133,7 +1134,8 @@ class _VaultTabState extends State<_VaultTab> {
Padding(
padding: const EdgeInsets.only(top: 8),
child: LinearProgressIndicator(
value: _loading ? _streamProgress : 1),
value: _loading ? _streamProgress : 1,
),
),
],

Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.3.2"
version: "0.3.3"
matcher:
dependency: transitive
description:
Expand Down
1 change: 1 addition & 0 deletions flutter_rust_bridge.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ rust_input: crate::api
rust_root: rust/
dart_output: lib/src/rust
enable_lifetime: true
full_dep: true
rust_features:
- compression
2 changes: 1 addition & 1 deletion ios/m_security.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#
Pod::Spec.new do |s|
s.name = 'm_security'
s.version = '0.3.2'
s.version = '0.3.3'
s.summary = 'A high-performance cryptographic SDK for Flutter powered by native Rust via FFI.'
s.description = <<-DESC
A high-performance cryptographic SDK for Flutter powered by native Rust via FFI.
Expand Down
2 changes: 1 addition & 1 deletion lib/src/rust/api/encryption.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,5 @@ Future<String> encryptionAlgorithmId({required CipherHandle cipher}) => RustLib
.api
.crateApiEncryptionEncryptionAlgorithmId(cipher: cipher);

// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<CipherHandle>>
// Rust type: RustOpaqueNom<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<CipherHandle>>
abstract class CipherHandle implements RustOpaqueInterface {}
6 changes: 4 additions & 2 deletions lib/src/rust/api/evfs/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import '../../frb_generated.dart';
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';

// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `eq`, `fmt`
// These functions are ignored because they are not marked as `pub`: `new`, `refresh_mmap`, `slice`
// These types are ignored because they are neither used by any `pub` functions nor (for structs and enums) marked `#[frb(unignore)]`: `VaultMmap`
// These function are ignored because they are on traits that is not defined in current crate (put an empty `#[frb]` on it to unignore): `clone`, `drop`, `eq`, `fmt`

// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<VaultHandle>>
// Rust type: RustOpaqueNom<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<VaultHandle>>
abstract class VaultHandle implements RustOpaqueInterface {
/// Compute health information from the in-memory index only (no file I/O or WAL writes).
Future<VaultHealthInfo> health();
Expand Down
2 changes: 1 addition & 1 deletion lib/src/rust/api/hashing.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,5 @@ Future<Uint8List> blake3Hash({required List<int> data}) =>
Future<Uint8List> sha3Hash({required List<int> data}) =>
RustLib.instance.api.crateApiHashingSha3Hash(data: data);

// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<HasherHandle>>
// Rust type: RustOpaqueNom<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<HasherHandle>>
abstract class HasherHandle implements RustOpaqueInterface {}
Loading
Loading