Skip to main content

aegis_guardian 3.6.2 copy "aegis_guardian: ^3.6.2" to clipboard
aegis_guardian: ^3.6.2 copied to clipboard

Native Android and iOS security, device signing, screen privacy, App Attest, and independent AegisPulse event analytics for Flutter.

AegisGuardian for Flutter #

AegisPulse analytics (3.5.0) #

Android and iOS analytics use a dedicated native channel and persisted queue. Use PulseAnalytics (see example/lib/pulse_sample.dart for a separate entrypoint) with matching native 3.6.0 dependencies. Physical-device and simulator results are listed in VERIFICATION.md in this package.

AegisPulse push notifications (3.6.0) #

PulsePush registers the device token (FCM, Huawei or the iOS APNs token) with the analytics identity, renders data-only pushes on Android with buttons and dismiss tracking (PulsePush.show, also from a background handler) and reports opens. Keep your messaging plugin, e.g. firebase_messaging.

pub.dev Flutter Dart License

Flutter plugin 3.6.2 wrapping AegisGuardian Android 3.6.0 (Maven Central) and iOS 3.6.0 (CocoaPods or SwiftPM). Android API 24+, iOS 15+. The iOS binary is built and tested with Xcode 27 (Swift 6.4); older Xcode versions are untested. Development and validation use Flutter 3.44.4 / Dart 3.12.2. Only Android and iOS are registered.

Local security checks work offline. In 3.5.0, iOS Sentinel monitoring needs only URL + Client ID by default and sends UNVERIFIED telemetry. Optional appAttestEnabled: true enables automatic Apple enrollment and verified telemetry. Android monitors with URL + Client ID through the native enrolled device key (no Google Play Services needed), with manual Play Integrity still available.

Limitations of the Flutter preview #

The plugin exposes a subset of the native SDKs. Implemented does not mean verified on physical devices or against real attacks. See the support matrix for individual APIs and VERIFICATION.md for test results.

Platform and native API differences #

  • iOS screenshots cannot be blocked. Screen privacy uses an inactive/capture overlay; capture notifications can arrive after a frame. Android uses FLAG_SECURE.
  • USB debugging and developer-options checks are unsupported on iOS. Their report status is unsupported, not clear.
  • App identity checks differ: Android compares the APK signing certificate; iOS compares the bundle identifier only. These checks do not provide equivalent tamper protection.
  • Attestation is platform-specific. Android key-attestation challenges/certificate chains are not Apple App Attest. iOS rejects attestationChallenge, returns an empty certificate chain, and exposes separate App Attest helpers. App Attest calls on Android return unsupported.
  • Key lookup on iOS: hasDeviceKey() and getDevicePublicKey() return unsupported because the current native SDK has no non-mutating lookup API. Obtain the public key from createDeviceKey() instead; it creates a key if absent.
  • Reset/delete on Android: resetDeviceId() and deleteDeviceKey() return unsupported because these operations are not exposed by the current native SDK. Also note that Android createDeviceKey() replaces the existing key, while iOS reuses it.
  • Web and desktop are unsupported. The plugin is registered only for Android and iOS.

Native features not wrapped in version 3.1 #

OkHttp/URLSession SPKI pinning, the Encryption Gateway client, native tapjacking/touch filtering, and native memory-wiping helpers are not exposed through this Flutter API. Native builder/strict-policy callbacks are represented by explicit Dart policy evaluation and screen-protection calls, rather than a direct wrapper of those APIs.

Installing the plugin does not protect Dio or Dart http traffic automatically. Networking needs a separate integration. Dart buffers and platform-channel copies also prevent this wrapper from guaranteeing native-style memory wiping. Missing wrappers are implementation gaps; they do not mean Flutter cannot call those native features.

Verification and physical hardware testing #

Automated integration tests passed on physical Android devices and emulators (API 35), and on a physical iPhone and iOS 26.5 simulator. Clean stock baselines, hardware key attestation / Keychain device IDs, screen privacy toggles, and AOT release builds have been verified on hardware. See VERIFICATION.md for full test results. Items that remain to be verified include:

  • Real App Attest live enrollment/assertions with configured entitlements and a backend verifier.
  • Detection effectiveness against live malicious tools (physical devices are preserved clean as uncompromised baselines).
  • Runtime compatibility on the minimum supported Android API 24 and iOS 15 physical hardware.

Unsupported, unavailable, or unconfigured checks must not be treated as clean results. evaluatePolicy() blocks when a required check is not clear; Android's native SDK still has the probe-failure reporting limitation described below.

Install #

flutter pub add aegis_guardian

For development from this repository, use a local dependency:

dependencies:
  aegis_guardian:
    path: /path/to/aegis-guardian/flutter/aegis_guardian

The Android build resolves id.aegis-sentinel:aegis-guardian 3.6.0 from Maven Central. iOS resolves AegisGuardian 3.6.0 using CocoaPods or aegis-guardian-ios-binary 3.6.0 using SwiftPM. Native SDK source is not duplicated in the Flutter package. The native iOS dependency includes its privacy manifest.

Inspect and evaluate policy #

import 'package:aegis_guardian/aegis_guardian.dart';

final guardian = AegisGuardian();
try {
  final report = await guardian.scan();
  final decision = report.evaluatePolicy({
    SecurityCheck.root,
    SecurityCheck.frida,
    SecurityCheck.hooking,
  });
  if (!decision.allowed) {
    // Apply your app's policy. Verify integrity on your backend too.
  }
} catch (error) {
  // Inspection unavailable: do not treat a failed call as a clean device.
}

Use scan(expectedSignatureSha256: fingerprint) on Android (64 hex characters, with or without colons) or scan(expectedBundleIdentifier: bundleId) on iOS. Supplying the other platform's option raises PlatformException(code: 'unsupported'). Bundle ID comparison does not provide Android-style APK signature verification.

Reports contain the same ten check names and five statuses on Android, iOS, and Flutter: clear, detected, unavailable, notConfigured, and unsupported. The shared check names are root, emulator, debugger, frida, hooking, proxy, vpn, appIdentity, usbDebugging, and developerOptions. iOS marks Android-only checks unsupported; local premium Frida checks are unavailable; omitted identity expectations are notConfigured. Android's legacy detectors remain best-effort and cannot distinguish every failed probe from a negative finding. Flutter's isSecure is derived from normalized statuses: detected, unavailable, missing, and configured-but-unchecked results fail; unsupported platform checks and an unconfigured optional identity check are skipped. It is not cryptographic proof. A required unsupported/unavailable/unconfigured check blocks evaluatePolicy.

Optional automatic App Attest (3.5.0) #

For iOS, opt in without supplying enrollmentId or appAttestKeyId:

final guardian = AegisGuardian(
  sentinelConfig: SentinelConfig(
    baseUrl: Uri.parse('https://YOUR-SENTINEL-HOST'),
    clientId: 'YOUR-PUBLIC-CLIENT-ID',
    appAttestEnabled: true,
  ),
);
final result = await guardian.scanAndMonitor(
  expectedBundleIdentifier: 'com.example.app',
);
if (result.isSuccess) {
  print(result.value!.verificationStatus); // VERIFIED only after server validation
}

The native iOS SDK handles Apple enrollment, device-only Keychain storage, and assertions. The bridge returns the validated native assessment; Client Secrets are never sent. With only URL + Client ID and appAttestEnabled: false (the default), iOS uses /api/v2/telemetry/ios and sends UNVERIFIED telemetry without enrollment or Apple evidence, including on simulators. App signature status is unavailable; isModAppDetected() returns a failure because no signature proof is available. App Attest failures remain SentinelResult.failure with no unverified fallback. resetAppAttestEnrollment() explicitly clears local enrollment when recovery is needed; do not call it for each request. This option has no effect on Android's existing enrollment flow. Explicit legacy manual IDs still select the existing manual verified flow; Client-ID-only iOS configuration now shares the native Swift UNVERIFIED default.

The host still needs signed App Attest entitlements, a physical supported iPhone, and matching Bundle ID/Team ID in Sentinel. Development App Attest evidence requires Sentinel's explicit development opt-in. Install Flutter 3.5.0 or later and its matching native dependencies. For local testing, both the Flutter plugin and native iOS framework must use the updated build; a Dart path override alone is insufficient.

Aegis Sentinel premium security #

AegisGuardian() remains offline without sentinelConfig. Basic checks continue to work, while Sentinel Frida, server-authorized app identity/MOD, and blacklist results require a configured Sentinel subscription. Android uses verified enrollment; iOS can send unverified telemetry or opt in to App Attest:

final guardian = AegisGuardian();
final report = await guardian.scan(); // local only

On Android, the dashboard URL and public Client ID are enough. The plugin uses the native SDK's enrolled device key (Android key attestation): it enrolls automatically on first use, re-enrolls if Sentinel no longer knows the key, and needs no Google Play Services, so devices without Google (for example Huawei) also report telemetry. Sentinel grades software-only attestation as WEAK and accepts it only if the project allows weak Android integrity.

final guardian = AegisGuardian(
  sentinelConfig: SentinelConfig(
    baseUrl: Uri.parse('https://sentinel.example.com'),
    clientId: 'client-id',
  ),
);
final assessment = await guardian.scanAndMonitor(); // Android: enrolled device key

The manual verified flow below remains available: supply a previously returned enrollment ID and the platform evidence configuration. Android then uses Play Integrity (Play Cloud project number); iOS uses the registered App Attest key ID.

final guardian = AegisGuardian(
  sentinelConfig: SentinelConfig(
    baseUrl: Uri.parse('https://sentinel.example.com'),
    clientId: 'client-id',
    enrollmentId: registeredEnrollmentId,
    cloudProjectNumber: androidCloudProjectNumber, // Android
    appAttestKeyId: registeredAppAttestKeyId, // iOS
  ),
);

final result = await guardian.scanAndMonitor(
  userId: currentUserId,
  sessionId: currentSessionId,
);
result.fold(
  onSuccess: (assessment) {
    if (assessment.isBlacklisted) showBlacklistedStatus(assessment.action);
    else showConnectedStatus(assessment.riskTier);
  },
  onFailure: showTelemetryUnavailable, // Safe to ignore; request failure is a value.
);

scan() remains local. It marks premium Frida checks unavailable; a supplied Android signing fingerprint or iOS bundle ID can still produce a local diagnostic identity result. scanAndMonitor() requires verified enrollment and platform evidence, then validates the Client ID, active subscription, package/bundle ID and certificate policy on Sentinel before returning authoritative premium results. It returns SentinelResult<SentinelAssessment>; the convenience premium methods return SentinelResult<bool>. Network errors, invalid configuration, unknown Client IDs, and server rejections are failures in the result value. Ignoring a failed result does not throw or stop the caller's task. The Android plugin includes android.permission.INTERNET; iOS needs no equivalent permission and uses HTTPS under App Transport Security.

Use SentinelResult.failureCode for a stable category when a request fails. Migration notes are in CHANGELOG.md.

The Client Secret field exists for configuration compatibility but is intentionally never transmitted or used by the app. Keep it on a trusted backend for /api/v1/assess; Flutter telemetry uses the public Client ID.

Identity and signing #

Device ID is already available in the published API. It does not require App Attest, key enrollment, or a backend to read:

final deviceId = await AegisGuardian().getDeviceId();

The example app's Read device ID button displays the identifier independently of the security scan and reports native errors without retaining a stale ID. Android returns a 64-character SHA-256 hex identifier; iOS returns an app-scoped Keychain UUID. These are not a shared identifier across platforms/apps, and permanence after reinstall, data clearing, or factory reset is not guaranteed.

import 'dart:typed_data';

final id = await guardian.getDeviceId();
final enrollment = await guardian.createDeviceKey();
final signature = await guardian.sign(Uint8List.fromList([1, 2, 3]));
// Send enrollment.publicKey and signature to your backend for verification.
  • Public keys are P-256 X.509 SubjectPublicKeyInfo DER; signatures are SHA-256/ECDSA ASN.1 DER on both platforms.
  • Android: createDeviceKey replaces the existing native signing key. Enroll intentionally, not on every launch. Supply a fresh backend attestationChallenge (1..128 bytes) for Android Key Attestation and send the returned leaf-first DER certificate chain to the server. A returned chain does not guarantee hardware attestation; the SDK can fall back to a plain key. Verify trust, challenge and app identity on the server.
  • iOS: createDeviceKey reuses the key if present and requires Secure Enclave by default. The certificate chain is empty; Secure Enclave signing is not App Attest. Explicit requireSecureEnclave: false on both create/sign permits development software keys on a simulator. There is no silent downgrade.
  • iOS identity/signing use a separate namespace derived from the host bundle ID plus .aegis.flutter. Existing native app keys are not automatically migrated. Android uses the existing native SDK key alias.
  • Device IDs have platform-specific lifetimes. They are identifiers, not authentication credentials.

Screen privacy #

Call await guardian.setScreenProtection(true) after the Flutter window is attached; pass false to release the plugin's protection. Android sets FLAG_SECURE on the current Activity and reapplies it after configuration changes. The plugin preserves a flag already set by the host. Coordinate ownership if another component also changes the same flag after enabling this plugin.

iOS uses the native inactive/capture overlay on the current Flutter window. It cannot prevent screenshots, and capture notification can arrive after a frame. Enable separately for each engine/window you use; multi-window transitions require device testing. Screen operations remain on the main thread; scan/key operations run on a serial worker queue.

Apple App Attest #

The iOS helpers are isAppAttestSupported, generateAppAttestKey, attestAppAttestKey and generateAppAttestAssertion. Configure the host's App Attest entitlement and backend first. Persist your key ID, and pass a 32-byte SHA-256 hash of server-bound client data for attestation/assertion. The plugin does not invent challenges, configure entitlements, or verify server trust. Android calls return unsupported.

Obfuscation (optional) #

Guardian works the same with or without obfuscation. Flutter already enables R8 for Android release builds. To also obfuscate Dart code, build with:

flutter build apk --release --obfuscate --split-debug-info=build/symbols/android
flutter build ipa --release --obfuscate --split-debug-info=build/symbols/ios

Keep each --split-debug-info directory with its release to symbolize crashes. Guardian telemetry is unchanged by --obfuscate; string literals such as the Sentinel URL and Client ID stay readable.

Support matrix #

“Implemented” means bridged; see VERIFICATION.md for what actually ran.

Function Android iOS
Scan root/jailbreak, emulator/simulator, debugger, Frida, hooking, VPN, proxy Implemented Implemented
App identity APK signing certificate SHA-256 Bundle identifier only
USB debugging, developer options Implemented unsupported
Device ID Native Widevine/ANDROID_ID/fallback App-scoped Keychain UUID
Create key / sign Android Keystore Secure Enclave; explicit development fallback
Key attestation challenge / certificate chain Implemented unsupported challenge; empty chain
hasDeviceKey, getDevicePublicKey Implemented unsupported; use create result for public key
deleteDeviceKey, resetDeviceId unsupported by current native API Implemented
Policy evaluation Dart policy over native report Dart policy over native report
Screen protection FLAG_SECURE Inactive/capture overlay, no screenshot prevention
App Attest unsupported Helpers implemented; requires device/backend setup
Play Integrity as Sentinel v2 evidence Implemented; requires cloud project and server verification Not applicable
App Attest as Sentinel v2 evidence Not applicable Implemented; requires entitlement and server enrollment
OkHttp / URLSession SPKI pinning Not wrapped in 3.1 Not wrapped in 3.1
Encryption Gateway client Not wrapped in 3.1 No matching native module in this SDK
Native tapjacking/touch filtering Not wrapped in 3.1 No equivalent exposed
Native memory wiping Not exposed to Dart No equivalent exposed
Native builder / strict callback APIs Use Dart policy and explicit screen calls Use Dart policy and explicit screen calls

Networking through Dio or Dart http is not automatically routed through OkHttp/URLSession and is not protected by installing this package. Adding native pinning alone would not cover that traffic. Managed Dart buffers/platform-channel copies also cannot provide native memory-wiping guarantees. Web/desktop are not supported and calls raise MissingPluginException.

Test #

flutter pub get
flutter analyze
flutter test
cd example
flutter test
flutter test integration_test/plugin_integration_test.dart -d DEVICE_ID
flutter build apk --debug
flutter build ios --release --no-codesign

The integration command builds and runs on the selected simulator/device. On the tested Xcode 27 setup, generic dual-architecture simulator builds need the ARM64 workaround described in VERIFICATION.md. The unsigned iOS release build validates compilation only.

Use the disposable example application for integration tests: they replace Android signing keys and reset/delete iOS example identities/keys. The integration test independently verifies native signatures with PointyCastle and rejects altered messages; these dependencies are test-only.

The example demonstrates scanning and screen privacy. Beyond the Android emulator screenshot check, live App Attest, hardware attestation trust, threat injection, physical-device screenshot behavior and background/foreground behavior require additional configured-device testing; mocks are not evidence that these protections work on a device.

Plugin architecture follows Flutter platform channels.

AegisPulse quick start #

import 'package:aegis_guardian/aegis_guardian.dart';

final pulse = await PulseAnalytics.start(PulseConfig(
  baseUrl: Uri.parse('https://YOUR-SENTINEL-HOST'),
  trackingKey: 'ap_YOUR_32_HEX_CHARACTERS',
));
await pulse.track('screen_view', properties: {'screen': 'checkout'});
await pulse.setUserId('member_123');
await pulse.track('purchase', properties: {
  'transaction_id': 'order_1001', 'value': 99000, 'currency': 'IDR',
});
await pulse.setUserId(null);
final delivery = await pulse.flush();

No security initialization, enrollment or App Attest is needed for Pulse. Analytics uses its own API and quota. Network/server failures return delivery statuses and retain a bounded retry queue; quota exhaustion pauses collection until reset. Invalid configuration/properties and stopped/replaced clients can throw: handle those errors at the application's analytics boundary.

The installation ID stays on events through login/logout; set your member ID only after authentication succeeds and clear it on logout. Each distinct purchase needs a new transaction ID; retries reuse it. Refunds reference that transaction and use a separate refund ID. Properties accept scalar values; nested objects/arrays are not supported. Actual endpoint/key values belong in runtime configuration, never committed samples.

0
likes
150
points
1.17k
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Native Android and iOS security, device signing, screen privacy, App Attest, and independent AegisPulse event analytics for Flutter.

License

Apache-2.0 (license)

Dependencies

crypto, flutter

More

Packages that depend on aegis_guardian

Packages that implement aegis_guardian