flutter_asset_signatures 1.0.1+1
flutter_asset_signatures: ^1.0.1+1 copied to clipboard
A flutter asset transformer that signs assets at build time.
flutter_asset_signatures #
A Flutter asset transformer that cryptographically signs your bundled assets at build time and verifies them at runtime. This lets your app detect if an asset shipped inside the bundle has been tampered with or replaced, before the bytes are ever handed to your widgets.
Signatures are created using Ed25519 (via sodium). Only the holder of the private key can produce valid signatures, while the app ships with the corresponding public key and can only verify them.
Table of Contents #
- Features
- How it works
- Installation
- Usage
- CLI reference
- Security considerations
- Platform support
- Documentation
Features #
- Build-time signing – integrates with Flutter's native asset-transformer pipeline; every asset is signed automatically when you build your app.
- Runtime verification – a drop-in
AssetBundlethat verifies the signature of each asset the moment it is loaded and throws if it does not match. - Ed25519 signatures – fast, modern public-key signatures backed by
libsodium's
crypto_signprimitives. - Simple key management – a bundled CLI generates a key pair and stores the private key for building and the public key for your app.
- No native setup – the required libsodium binary is bundled automatically through Dart's native-assets mechanism; there is no manual native configuration on the supported platforms.
How it works #
- You generate a temporary Ed25519 key pair right before building. The private key stays on your build machine, the public key ships with the app.
- During a build, Flutter runs each matched asset through this package's transformer, which signs the asset bytes with the private key and replaces the bundled asset with the signed payload.
- At runtime the app wraps its
AssetBundlewith aSignedAssetBundle. Everyloadcall verifies the signed payload against the public key and returns the original bytes only if the signature is valid — otherwise it throws.
Installation #
Add the package to your app:
flutter pub add flutter_asset_signatures
Prerequisites #
- The signing key pair must be available at build time (see Usage below).
- The public key must be provided to the running app as a compile-time
environment variable named
ASSET_SIGNATURE_PUBLIC_KEY.
Usage #
1. Generate a signing key pair #
Run the bundled CLI from the root of your app. This creates a new key pair,
writes the private key to build/asset_signature.key (used for signing) and
appends the public key to your .env file as ASSET_SIGNATURE_PUBLIC_KEY
(used for verification):
dart run flutter_asset_signatures keygen
The
build/directory is git-ignored by default, which keeps the private key out of version control. This is by design, as the key should be generated anew for every build and never committed. The only imported part is that the public used when compiling the app matches the private key used to sign the assets.
2. Configure the asset transformer #
Attach the transformer to the assets you want to protect in your app's
pubspec.yaml:
flutter:
assets:
- path: assets/
transformers:
- package: flutter_asset_signatures
From now on, every build signs the matched assets automatically. If your private key lives somewhere other than the default location, pass it explicitly:
transformers:
- package: flutter_asset_signatures
args:
- --private-key-path=secrets/asset_signature.key
3. Verify assets at runtime #
Wrap your app (or any subtree) with DefaultSignedAssetBundle. It installs a
verifying AssetBundle for the subtree, so any descendant that loads assets via
DefaultAssetBundle.of(context) automatically gets signature-verified bytes:
import 'package:flutter/material.dart';
import 'package:flutter_asset_signatures/flutter_asset_signatures.dart';
void main() {
runApp(const DefaultSignedAssetBundle(child: MyApp()));
}
While the bundle initializes, DefaultSignedAssetBundle shows a fallback widget
(an empty SizedBox by default). You can customize it:
DefaultSignedAssetBundle(
fallbackBuilder: (context, state) =>
const Center(child: CircularProgressIndicator()),
child: const MyApp(),
);
If you need more control, construct a SignedAssetBundle yourself. Use
createFromEnv to resolve the public key and initialize libsodium for you:
final bundle = await SignedAssetBundle.createFromEnv();
// Loading a tampered or unsigned asset throws instead of returning bytes.
final data = await bundle.loadString('assets/config.json');
Finally, if you manage your own AssetBundle (e.g. a NetworkAssetBundle), you
can wrap it with SignedAssetBundle to add signature verification:
final networkBundle = NetworkAssetBundle(Uri.parse('https://example.com/'));
final signedBundle = await SignedAssetBundle.createFromEnv(
originalBundle: networkBundle,
);
final data = await signedBundle.loadString('config.json');
4. Provide the public key when building or running #
The public key is read from the ASSET_SIGNATURE_PUBLIC_KEY compile-time
environment variable. The easiest way is to point Flutter at the .env file
that keygen produced:
flutter run --dart-define-from-file=.env
flutter build apk --dart-define-from-file=.env
Alternatively, pass the key inline:
flutter run --dart-define=ASSET_SIGNATURE_PUBLIC_KEY=<your-public-key>
You can also pass the key directly in Dart instead of using the environment variable:
DefaultSignedAssetBundle(
encodedPublicKey: '<your-public-key>',
child: const MyApp(),
);
CLI reference #
The CLI is invoked with dart run flutter_asset_signatures <command>. If no
command is given, sign is used.
Global options (available on all commands):
| Option | Abbr | Default | Description |
|---|---|---|---|
--private-key-path |
-p |
build/asset_signature.key |
Path to the private key file used to create signatures. |
keygen — generate a new key pair. #
| Option | Abbr | Default | Description |
|---|---|---|---|
--env-path |
-e |
.env |
File the public key is written to. key=value format, or a JSON object if the file ends in .json. |
--public-key-name |
-n |
ASSET_SIGNATURE_PUBLIC_KEY |
Variable name used for the public key in the target file. |
--[no-]override |
-O |
false |
Allow overwriting an existing key in the target locations. |
sign — sign a single file. #
This is the command Flutter invokes for each asset; you normally do not run it by hand.
| Option | Abbr | Description |
|---|---|---|
--input |
-i |
Path to the input (unsigned) file. |
--output |
-o |
Path to the output (signed) file. |
Security considerations #
- Protect the private key. It is meant to be a temporary key that only has value while building your app. If it is leaked, an attacker can forge valid asset signatures and bypass your app's verification. The private key should never be committed to version control or shipped with the app. It should be generated anew for every build and deleted afterward.
- The public key is not a secret and is meant to be embedded in the app.
- This scheme guarantees integrity and authenticity of assets — it proves an asset was produced by the holder of the private key and was not modified. It does not encrypt assets; signed assets are still readable.
- Verification only protects assets loaded through a
SignedAssetBundle. Make sure your asset loads go throughDefaultAssetBundle.of(context)(or aSignedAssetBundleyou created), not directly throughrootBundle.
Platform support #
The native libsodium binary is provided automatically via Dart's native-assets
build hook, so no manual native setup is required on native platforms
(Android, iOS, Windows, macOS, Linux). For web support, libsodium must be
initialized in the browser — see the
sodium package documentation for details.
Documentation #
- API documentation is available on pub.dev.
- A complete, runnable setup is provided in the
exampledirectory.
Made with ❤️ by BRICKMAKERS.