Skip to main content

Native Integrity and Tampering Prevention

A core guarantee of TkushPatch is absolute runtime safety. Flutter Over-The-Air updates operate by hot-swapping AOT-compiled Dart machine code (application binary on Android, App.framework/App on iOS).

However, if a change touches native code (Java/Kotlin bytecode, C++ libraries, Swift/Obj-C frameworks, or OS manifest permissions), pushing an OTA patch will trigger severe runtime crashes (NoSuchMethodError, ClassNotFoundException, or SIGSEGV) because the store-installed binary on users' phones lacks those native symbols.

To eliminate this catastrophic risk, tk-ota-push incorporates a deterministic NativeIntegrityScanner (lib/src/cli/native_integrity.dart), verified by test/native_integrity_test.dart.


1. Why pubspec.lock Hashing Alone Is Insufficient​

Historically, code push tools solely verified the SHA-256 hash of pubspec.lock. This approach suffers from two severe limitations:

  1. Frequent False Positives: Bumping a pure Dart package (e.g., from intl ^0.18.0 to ^0.19.0) alters pubspec.lock, even though the code changes are 100% patchable via OTA.
  2. Dangerous False Negatives: Manually modifying Java/Kotlin files in android/app/src/main/ or Swift files in ios/Runner/ does not change pubspec.lock, yet will break the application if patched over the air.

The NativeIntegrityScanner in tk-ota-push overcomes this by directly inspecting the compiled binary artifacts.


2. How the NativeIntegrityScanner Operates​

During a release, the CLI computes and stores the baseline native footprint (native_code_hash) on the server. During a subsequent patch, it re-scans the newly compiled archive and compares both fingerprints.

Pre-Patch Native Integrity Check
  1. Archive Inspection: The CLI inspects the compiled application package (.apk, .aab, or .ipa).
  2. Native Code Audit: The OS manifest and native libraries are verified against the reference release.
  3. Integrity Verdict:
    • If native code is untouched: The Dart binary patch generation proceeds.
    • If native changes are detected: Build stops with a requirement to submit a full store release.

Android Inspection (scanAndroidArchive)​

For each entry inside the APK or AAB bundle, the scanner evaluates:

ComponentInspected FilesIntegrity Role
Java / Kotlin Bytecodeclasses.dex, classes2.dex...Detects native Android code additions or modifications
Android ManifestAndroidManifest.xmlDetects new permissions (CAMERA, LOCATION), services, or receivers
Native C++ Librarieslib/<arch>/*.so (e.g., libflutter.so, SQLite)Detects Flutter engine bumps or modified third-party C++ libs
Dart Snapshotlib/<arch>/application binarySTRICTLY EXCLUDED from the native hash because this is the code OTA intends to update!
Flutter Assetsassets/flutter_assets/EXCLUDED (handled separately by the asset manifest scanner)

iOS Inspection (scanIosApp)​

On the compiled Runner.app bundle, the scanner inspects:

  1. Info.plist: Detects new usage permissions (NSCameraUsageDescription, URL schemes, Bluetooth keys).
  2. Frameworks/: Verifies all dynamic native third-party frameworks, strictly excluding Frameworks/App.framework (which contains the Dart snapshot).
  3. Mach-O Main Binary: Computes the SHA-256 of the native application executable.

3. Behavior When Native Alteration Is Detected​

When running tk-ota-push patch, if the nativeCodeHash of the newly compiled code does not match the baseline Release registered on the server:

==================================================================
Native Code Integrity Verification
==================================================================
Locked Release on TkushPatch Server : b7e4a18c...
Locally compiled Patch : 8f92cd30...

❌ CRITICAL NATIVE INTEGRITY ERROR:
Native code alterations have been detected between the baseline
Release and the patch you are attempting to publish:
- classes.dex: MODIFIED (Kotlin/Java bytecode altered)
- AndroidManifest.xml: MODIFIED (New permission android.permission.CAMERA added)

⚠️ An OTA patch CANNOT alter native binaries or OS permissions.
Distributing this patch would cause immediate fatal crashes on user devices.

REQUIRED ACTION: You must create a NEW Store Release (e.g., 1.0.0+2)
via 'tk-ota-push release' and submit the new build to Google Play / App Store.

The CLI terminates immediately with exit code 1, and no patch payload is uploaded or signed on the server.


4. Asset Manifest Hash (computeAssetsManifestHash)​

To protect asset integrity, FlutterProject.computeAssetsManifestHash() computes a deterministic combined SHA-256 of all assets declared in pubspec.yaml:

declared:assets/images/,assets/i18n/
assets/images/logo.png=8a3f91...
assets/images/promo.png=c2d10e...
assets/i18n/fr.json=5b47a9...

Any added, deleted, or modified asset produces a distinct hash, recorded in Release and Patch metadata for validation by the server and the OtaAssetManager runtime SDK.


5. The --force (-f) Flag: When and How to Use It​

The --force option on tk-ota-push patch is designed for situations where pubspec.lock has changed (such as updating pure Dart packages without native dependencies) while the nativeCodeHash remains strictly identical:

# Force publication when pure Dart dependencies have drifted
tk-ota-push patch 1.0.0+1 -p android --force -m "Update intl package and fix currency formatting"
The --force Flag NEVER Bypasses Real Native Alterations

If the NativeIntegrityScanner detects an actual change in classes.dex, AndroidManifest.xml, or an iOS Mach-O binary, the CLI strictly refuses publication, even when --force is provided. The stability of your mobile fleet always supersedes instant delivery.