Skip to main content

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

Native Android and iOS runtime security checks, device signing, screen privacy, and Apple App Attest for Flutter.

AegisGuardian for Flutter #

pub.dev Flutter Dart License

Flutter plugin next version 3.1.0 wrapping AegisGuardian Android 3.1.0 (Maven Central) and iOS 3.1.0 (CocoaPods and SwiftPM). Android API 24+, iOS 15+. The iOS binary requires Xcode 26+; the open-source Swift package source supports Swift 5.9+. Development and validation use Flutter 3.44.4 / Dart 3.12.2. Only Android and iOS are registered.

Version 0.3.0 adds fail-closed Sentinel v2 monitoring with Android Play Integrity plus enrolled device-key signing, and iOS App Attest assertions. Server configuration and device enrollment are required; see the verified telemetry contract.

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.0 #

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 hardware (Huawei MIS-LX9, Android 12) and emulators (API 35), and on a physical iPhone 11 (Apple A13 Bionic, iOS 27) 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 aegis-guardian 3.1.0 from Maven Central. iOS resolves AegisGuardian 3.1.0 using CocoaPods or aegis-guardian-ios-binary 3.1.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.

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 verified enrollment:

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

To opt in, enroll the device with Sentinel first. Supply the returned enrollment ID and the platform evidence configuration as well as the dashboard URL and public Client ID. Android uses a 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. The 3.1.0 migration guide includes examples for each platform.

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.

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.0 Not wrapped in 3.1.0
Encryption Gateway client Not wrapped in 3.1.0 No matching native module in this SDK
Native tapjacking/touch filtering Not wrapped in 3.1.0 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.

0
likes
0
points
1.17k
downloads

Publisher

unverified uploader

Weekly Downloads

Native Android and iOS runtime security checks, device signing, screen privacy, and Apple App Attest for Flutter.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

crypto, flutter

More

Packages that depend on aegis_guardian

Packages that implement aegis_guardian