Skip to content

vikassharmanaini/secure_env

Repository files navigation

secure_env

Dart Flutter MIT Pub CI

Encrypt secrets from a gitignored .secure_env plaintext file at build time into Rust + Dart embeds (AES-256-GCM). At runtime your app resolves values through dart:ffi so plaintext never lives as Dart constants and is much harder to lift with naive strings / decompilation.

Platforms Android (JNI .so), iOS (XCFramework .a), macOS (.dylib)
Not supported Flutter Web — use server-held secrets instead

Replace placeholder homepage, repository, issue_tracker, badge URLs, and CI shield targets in pubspec.yaml before publishing under your own org.


Features

  • Build-time codegen (dart run secure_env:generate): parses .secure_env, writes ciphertext into rust/src/generated/ and metadata into lib/src/generated/secure_env_data.g.dart.
  • Rust FFI: stripped release binaries, AES-GCM decrypt, zeroize for sensitive buffers.
  • Small Dart surface: SecureEnv with get / require, availableKeys, nativeAbiVersion, isAvailable.
  • Scripts under tool/ for NDK/Xcode builds and optional APK plaintext checks.

Installation

dependencies:
  secure_env: ^1.0.0

The package registers the generate executable:

dart run secure_env:generate --help

Quick start

  1. Keep .secure_env out of version control (see .gitignore patterns).

  2. Define secrets as KEY=value lines (quotes and # comments supported — see codegen help).

  3. From your app / monorepo root (must contain lib/ + rust/ as expected by the generator):

    dart run secure_env:generate

    Default env file path is <root>/.secure_env. Override with --env path/to/file and --root . when your shell cwd is elsewhere.

  4. Compile natives into the embedding plugin (see below), then flutter pub get and use the Dart API.

  5. Read at runtime:

    import 'package:secure_env/secure_env.dart';
    
    if (SecureEnv.isAvailable) {
      final apiKey = SecureEnv.require('MY_API_KEY'); // throws if missing/undecodable after FFI loads
      final optional = SecureEnv.get('OTHER');       // nullable
      final keys = SecureEnv.availableKeys();        // sorted key names only (plaintext values never listed)
    }

Native artifacts

Codegen updates Dart + Rust sources only. You still ship platform binaries:

Target Script / output
Android (arm64‑v8a, armeabi‑v7a, x86_64, x86) bash tool/build_android.shandroid/src/main/jniLibs/<abi>/libsecure_env.so
arm64‑only shortcut bash tool/build_android_arm64_manual.sh
iOS (device + simulators, XCFramework) bash tool/build_ios.shios/Frameworks/secure_env.xcframework
macOS bash tool/build_macos.shmacos/libsecure_env.dylib
One-shot local build bash tool/build_all.sh (needs ANDROID_NDK_HOME for Android)

Prerequisites: Rust with the triples listed in script output/README comments, ANDROID_NDK_HOME for Android, Xcode for Apple targets. See Flutter’s CocoaPods docs for pod install on iOS/macOS example apps.

FFI loaders: Android opens libsecure_env.so; iOS uses DynamicLibrary.process() after static linking; macOS prefers opening libsecure_env.dylib.


Code generator CLI

dart run secure_env:generate [options]

  --env            Path to plaintext env file (default: .secure_env)
  --root           Repository root containing rust/ + lib/
  --verbose        Log key names only (never values)
  --dry-run        Validate without writing files
  --master-key-hex Optional 64-hex deterministic key for reproducible CI (never ship real secrets)

Security model & limitations

  • Secrets are stored as ciphertext + obfuscated key material inside native/Dart codegen outputs; plaintext is not emitted into Dart literals.
  • This is meaningful hardening, not proof against a dedicated attacker with unlimited time, physical device access, memory scraping on a compromised device, or nation-state budgets.
  • Key names appear in generated Dart metadata; values are ciphertext.
  • SecureEnv.get returns null for missing keys or failed decrypt paths; SecureEnvDecryptionException is reserved for API evolution (secure_env.dart).
  • Web: not supported (dart:ffi).

Example app & tests

Bundled example/ demos every runtime call. Secrets there resolve from the parent plugin’s lib/ + rust/ embeds unless you regenerate with --env example/.secure_env --root . from the repo root. See example/README.md.

Golden fixture notes live in [doc/fixture_testing.md](doc/fixture_testing.md).

Developer checks:

dart analyze && flutter analyze
flutter test
(cd rust && cargo test)
bash tool/check_apk_plaintext_leak.sh <apk> <secret-substring>   # optional release hygiene

CI configuration: .github/workflows/ci.yml.


Publishing checklist

Substitute real URLs first, then:

dart format . --set-exit-if-changed
dart pub publish --dry-run
dart pub global run pana .

Scores around 140 / 160 are typical until Flutter’s Swift Package Manager lane is adopted alongside CocoaPods; this package documents the CocoaPods + XCFramework path first.

Keep CHANGELOG.md updated per release.


License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors