necessary 0.1.4
necessary: ^0.1.4 copied to clipboard
Device integrity for Flutter apps: a device-bound key the plugin never exports, signed measurements of the phone and the app, request proofs. Your backend decides.
0.1.4 #
- Android: a key is made only where the keystore, answering, holds none.
containsAliasalso answers "no such key" when the keystore cannot be reached — on Android 7 to 11 a binder failure, on 12 and later any keystore error — andstart()then generated a new installation key over the old one (and a new step-up key over the one the backend holds), since generating replaces whatever the alias holds. Now a "no" counts only when the keystore answers: the plugin deletes an alias that never holds a key — a keystore that answers says "not found", one that cannot be reached fails — and asks again. When it cannot be reached,start()fails with the newKEY_UNAVAILABLE, making no key and sending nothing, and the step-up key answersSTEPUP_KEY_UNAVAILABLE; a lookup that failed once and then answers finds the same key. Signing tells the two apart the same way:KEY_NOT_FOUNDandSTEPUP_KEY_NOT_FOUNDonly when the keystore says so. - Android: deleting a key —
reset(), a step-up re-enrolment — no longer skips a key a failed lookup did not report; it deletes, and fails withKEY_DELETE_FAILEDwhen the key stays. - Android: making and deleting a key run one at a time in the process, whichever Flutter engine asks; two first starts at once could each find no key, and the second generate over the first's.
0.1.3 #
- iOS (collector 7): an app image whose code the App Store encrypted on disk is no longer compared with
its file — the file holds encrypted bytes and memory the decrypted ones, so a genuine App Store
install would have differed in every encrypted byte. Such an image is skipped with its
__textbytes and named inapp_images_not_compared, soapp_completeis false; builds stored in the clear are compared as before. - Android (collector 9): the code comparison reads the memory map once and takes every region from that
snapshot. It used to reopen the map for each library, and a failed reopen counted as "not loaded", so a
region could read complete with a target unmeasured; a map that cannot be read now answers
checkedfalse withreasonmaps_unreadable(ormaps_empty,no_target_mapped). And one comparison runs at a time in the process, whichever engine or thread asks: a comparison makes execute-only pages readable while it reads them, and two at once could read a page the other had just locked again. - Android: the step-up key is deleted only when Android says it can never sign again
(
KeyPermanentlyInvalidatedException). A lookup that cannot establish whether it is usable — on Android 9 a binder error also reads as "no key entry", and a failed key-characteristics query surfaces asUnrecoverableKeyException— used to delete it too; the key is now kept and the approval fails with the newSTEPUP_KEY_UNAVAILABLE, which an app may retry.reenrolStepUpreplaces such a key, as it replaces an invalidated one. reset()andclose()hold for every call:signRequestandsetAppPinend withInterruptedCallExceptionwhen the installation was reset meanwhile, never handing out a proof or an answer of the installation that is gone; every persistent write and key change — the step-up marker, a step-up key being made or replaced — is refused once a reset has begun and waited for when it began before, so none outlives the reset. A step-up key the keystore will not delete now failsreset()(KEY_DELETE_FAILED), the installation itself gone, instead of being passed over.doc/measurements.mdbounds Android'sappregion plainly: the native code mapped from the APK and the libraries the SDK names beside it; a build that extracts its native libraries leaves those under other names outside it, andapp_completeis complete over that selection, not over all of the app's code.doc/measurements.mdis the same for every Necessary SDK: it says "the SDK" where it said "the plugin", and leaves what differs between SDKs — the collector versions, the Android and iOS versions, how an app turns on a personal measurement — to each SDK's README.doc/measurements.mdsays what the statusokmeans: measured, never "all clear" — the example app's badge already says measured. The report's value staysok.- The measurements and the integration guide, checked against the code:
root_shelltriescommand -v su, thenwhich su; Android'sappregion is what is mapped from the APK plus the libraries the SDK names beside it; the iOS jailbreak paths are listed in full; the guide citesRULES.mdsections 2 and 7, andRULES.mdsaysokmeans measured.
0.1.2 #
- The product is named Necessary: the package's description, the README, the integration guide and the
measurement contract's titles say "Necessary" or "device integrity" where they said "device trust".
No change to the code, the API (the class is still
DeviceTrust), the report or the contract's rules; the schema's$idstays as it was.
0.1.1 #
- iOS: the collector, now version 6, sends the memory page size with
device, and asks screen capture of the screen showing the app: with no window of the app's in the foreground,recordingis left out andrecording_statussaysunavailable, as on Android. doc/measurements.md: every measurement the plugin sends, field by field.- The README, like the example's, says how to install the example app, test it step by step, and watch each measurement change one at a time.
- The README opens with links to the integration guide, the measurements and the measurement contract;
it and the guide explain the contract folder's name,
android-8-ios-5: the lowest collector versions the contract accepts, not the Android and iOS versions.
0.1.0 #
- First release: an installation key the plugin never exports (Android Keystore, StrongBox where the
phone has it; the Secure Enclave on iOS; a software key where the secure hardware declines, and
key_securityreports where, as the phone says), registration and proof of possession, a signed proof on every request, signed scans answering the measurement contract (report format 1, Android collector 8, iOS collector 5), a scan made for one request, and, when turned on, step-up approval with a second key and the app PIN. - Sample backends in Python, TypeScript and C#, and the integration guide they are written from.
- A call that
reset()orclose()ends while it runs throwsInterruptedCallExceptionand keeps nothing; step-up waits forreenrolStepUp(STEPUP_REENROL_PENDING) whenever your backend does not hold the phone's step-up key. - Needs Flutter 3.44 and Dart 3.12 or later: the Android build uses the Kotlin Gradle plugin's
kotlin.compilerOptions, which Flutter supports from 3.44 (AGP 9, built-in Kotlin off). The example app needs Flutter 3.47.