@necessarysdk/react-native 0.0.0-stage → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/LICENSE +201 -0
- package/NOTICE +2 -0
- package/NecessaryReactNative.podspec +23 -0
- package/README.md +429 -2
- package/android/build.gradle +83 -0
- package/android/src/main/AndroidManifest.xml +41 -0
- package/android/src/main/cpp/CMakeLists.txt +7 -0
- package/android/src/main/cpp/code_integrity.cpp +484 -0
- package/android/src/main/java/com/necessarysdk/reactnative/Causes.kt +49 -0
- package/android/src/main/java/com/necessarysdk/reactnative/InstallationKeyManager.kt +388 -0
- package/android/src/main/java/com/necessarysdk/reactnative/IntegrityProbeManager.kt +1157 -0
- package/android/src/main/java/com/necessarysdk/reactnative/KeyPresence.kt +62 -0
- package/android/src/main/java/com/necessarysdk/reactnative/NecessaryModules.kt +94 -0
- package/android/src/main/java/com/necessarysdk/reactnative/NecessaryNative.kt +338 -0
- package/android/src/main/java/com/necessarysdk/reactnative/NecessaryPackage.kt +38 -0
- package/android/src/main/java/com/necessarysdk/reactnative/ProbeReads.kt +333 -0
- package/android/src/main/java/com/necessarysdk/reactnative/ScreenGuard.kt +56 -0
- package/android/src/main/java/com/necessarysdk/reactnative/SecureStore.kt +127 -0
- package/android/src/main/java/com/necessarysdk/reactnative/StepUpKeyEntry.kt +57 -0
- package/android/src/main/java/com/necessarysdk/reactnative/StepUpKeyManager.kt +767 -0
- package/contract/report-1/android-8-ios-5/RULES.md +149 -0
- package/contract/report-1/android-8-ios-5/report.schema.json +2753 -0
- package/contract/report-1/android-8-ios-5/samples/android-8-complete.json +205 -0
- package/contract/report-1/android-8-ios-5/samples/android-8-incomplete.json +205 -0
- package/contract/report-1/android-8-ios-5/samples/android-8-invalid.json +205 -0
- package/contract/report-1/android-8-ios-5/samples/android-8-location-one-fix.json +228 -0
- package/contract/report-1/android-8-ios-5/samples/android-8-location-two-fixes.json +228 -0
- package/contract/report-1/android-8-ios-5/samples/android-8-refused.json +208 -0
- package/contract/report-1/android-8-ios-5/samples/ios-5-complete.json +106 -0
- package/contract/report-1/android-8-ios-5/samples/ios-5-incomplete.json +105 -0
- package/contract/report-1/android-8-ios-5/samples/ios-5-invalid.json +107 -0
- package/contract/report-1/android-8-ios-5/samples/ios-5-partial.json +110 -0
- package/contract/report-1/android-8-ios-5/samples/ios-5-refused.json +109 -0
- package/doc/integration_guide.md +771 -0
- package/doc/measurements.md +774 -0
- package/ios/InstallationKeyManager.swift +694 -0
- package/ios/IntegrityProbeManager.swift +959 -0
- package/ios/NecessaryModules.h +34 -0
- package/ios/NecessaryModules.mm +208 -0
- package/ios/NecessaryNative.swift +327 -0
- package/ios/PrivacyInfo.xcprivacy +14 -0
- package/ios/ProbeReads.swift +370 -0
- package/lib/client.d.ts +105 -0
- package/lib/client.js +176 -0
- package/lib/collector.d.ts +18 -0
- package/lib/collector.js +49 -0
- package/lib/deviceTrust.d.ts +253 -0
- package/lib/deviceTrust.js +699 -0
- package/lib/errors.d.ts +43 -0
- package/lib/errors.js +56 -0
- package/lib/index.d.ts +18 -0
- package/lib/index.js +34 -0
- package/lib/installationKey.d.ts +62 -0
- package/lib/installationKey.js +185 -0
- package/lib/installationStore.d.ts +49 -0
- package/lib/installationStore.js +127 -0
- package/lib/native/NativeNecessaryInstallationKey.d.ts +10 -0
- package/lib/native/NativeNecessaryInstallationKey.js +4 -0
- package/lib/native/NativeNecessaryIntegrity.d.ts +8 -0
- package/lib/native/NativeNecessaryIntegrity.js +4 -0
- package/lib/native/NativeNecessarySecureScreen.d.ts +7 -0
- package/lib/native/NativeNecessarySecureScreen.js +4 -0
- package/lib/native/NativeNecessaryStepUpKey.d.ts +9 -0
- package/lib/native/NativeNecessaryStepUpKey.js +4 -0
- package/lib/native/NativeNecessaryStorage.d.ts +14 -0
- package/lib/native/NativeNecessaryStorage.js +4 -0
- package/lib/protocol.d.ts +24 -0
- package/lib/protocol.js +50 -0
- package/lib/reinstallHint.d.ts +23 -0
- package/lib/reinstallHint.js +65 -0
- package/lib/report.d.ts +62 -0
- package/lib/report.js +134 -0
- package/lib/requestProof.d.ts +116 -0
- package/lib/requestProof.js +220 -0
- package/lib/secureScreen.d.ts +15 -0
- package/lib/secureScreen.js +32 -0
- package/lib/stepUpKey.d.ts +105 -0
- package/lib/stepUpKey.js +187 -0
- package/lib/transport.d.ts +39 -0
- package/lib/transport.js +117 -0
- package/lib/util/answers.d.ts +6 -0
- package/lib/util/answers.js +16 -0
- package/lib/util/bytes.d.ts +18 -0
- package/lib/util/bytes.js +207 -0
- package/lib/util/native.d.ts +20 -0
- package/lib/util/native.js +49 -0
- package/lib/util/release.d.ts +2 -0
- package/lib/util/release.js +7 -0
- package/lib/util/target.d.ts +18 -0
- package/lib/util/target.js +128 -0
- package/package.json +81 -4
- package/src/client.ts +221 -0
- package/src/collector.ts +64 -0
- package/src/deviceTrust.ts +851 -0
- package/src/errors.ts +71 -0
- package/src/index.ts +19 -0
- package/src/installationKey.ts +223 -0
- package/src/installationStore.ts +143 -0
- package/src/native/NativeNecessaryInstallationKey.ts +12 -0
- package/src/native/NativeNecessaryIntegrity.ts +15 -0
- package/src/native/NativeNecessarySecureScreen.ts +9 -0
- package/src/native/NativeNecessaryStepUpKey.ts +11 -0
- package/src/native/NativeNecessaryStorage.ts +16 -0
- package/src/protocol.ts +52 -0
- package/src/reinstallHint.ts +69 -0
- package/src/report.ts +168 -0
- package/src/requestProof.ts +295 -0
- package/src/secureScreen.ts +29 -0
- package/src/stepUpKey.ts +238 -0
- package/src/transport.ts +145 -0
- package/src/util/answers.ts +15 -0
- package/src/util/bytes.ts +194 -0
- package/src/util/native.ts +56 -0
- package/src/util/release.ts +6 -0
- package/src/util/target.ts +132 -0
- package/vectors/client_server.json +264 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# The measurement contract: report format 1, Android collector 8, iOS collector 5
|
|
2
|
+
|
|
3
|
+
What a Necessary collector sends in its signed integrity report, and how to read it. Three parts,
|
|
4
|
+
all in this folder:
|
|
5
|
+
|
|
6
|
+
- `report.schema.json` — the report's shape (JSON Schema 2020-12): every measurement, field, type,
|
|
7
|
+
unit and bound. It says what is well-formed, nothing more.
|
|
8
|
+
- this file — what a schema cannot say: when a measurement is complete, how its figures add up, what
|
|
9
|
+
contradicts itself, and how versions change.
|
|
10
|
+
- `samples/` — reports as a collector signs them, for each platform, each with the plan it answers and
|
|
11
|
+
how it is judged.
|
|
12
|
+
|
|
13
|
+
The thresholds and the decisions are yours: which measurements to ask for, what each one costs, how
|
|
14
|
+
old a location may be, when to refuse. Nothing here scores anything.
|
|
15
|
+
|
|
16
|
+
## 1. The report
|
|
17
|
+
|
|
18
|
+
- The app sends `report_payload`, the base64url (without padding) of the report's UTF-8 JSON bytes,
|
|
19
|
+
and `report_signature`: ECDSA P-256 over SHA-256 of exactly those bytes, ASN.1 DER, base64url.
|
|
20
|
+
Verify over the bytes as sent, never over JSON you encoded again. The plugins' shared test vectors
|
|
21
|
+
(`client_server.json`) pin the encoding.
|
|
22
|
+
- At most 65,536 bytes decoded: refuse larger.
|
|
23
|
+
- `version` is 1. `challenge_nonce` is the challenge's 32-byte nonce exactly as issued (43
|
|
24
|
+
characters). `collected_at` is the phone's clock in Unix seconds, to be within 90 seconds of yours.
|
|
25
|
+
`collector_version` is the collector's version on its platform. `probe_results` holds one object per
|
|
26
|
+
measurement; `enabled_measurements` names the personal measurements this app's developers enabled.
|
|
27
|
+
- Numbers the schema types as integers are written without a fraction or an exponent, and a number is
|
|
28
|
+
never `true` or `false`.
|
|
29
|
+
|
|
30
|
+
## 2. What counts as a measurement
|
|
31
|
+
|
|
32
|
+
- Each measurement has a `status`: `ok`, measured; `error`, with `error` saying why (Android may add
|
|
33
|
+
`error_type`); `unsupported`, with `reason`; or `not_enabled`, a personal measurement this app does
|
|
34
|
+
not collect. `ok` means measured, never "all clear": what was found is in the fields.
|
|
35
|
+
- **`ok` says measured; the fields say how much.** A measurement is complete when it answered `ok` and carries
|
|
36
|
+
every field its `x-complete-when-ok` lists — for `code_integrity` with `checked` true, every field of
|
|
37
|
+
`x-complete-when-checked` too, and for a personal measurement every one of its `x-parts`.
|
|
38
|
+
- **`unread`** names the parts an `ok` measurement could not read, each with why: at most 64, a name
|
|
39
|
+
of at most 128 characters, a reason of at most 160. An unread part is not measured — never read it
|
|
40
|
+
as a clean "no". A measurement that could read nothing answers `error`.
|
|
41
|
+
- A field a collector version does not know is absent: read absent as not measured.
|
|
42
|
+
- A report that lacks a measurement you asked for is refused, not read. A failed or incomplete one
|
|
43
|
+
tells you something could not be measured; it is not proof of an attack. Ask again rather than
|
|
44
|
+
punish the person.
|
|
45
|
+
|
|
46
|
+
## 3. When each measurement comes
|
|
47
|
+
|
|
48
|
+
Each measurement's `x-answered` says it:
|
|
49
|
+
|
|
50
|
+
- **when the backend requests it** — ask, in the challenge, for every measurement you rely on, every
|
|
51
|
+
time, and vary the others from scan to scan;
|
|
52
|
+
- **volunteered on every scan** — `device`, `network`, and on iOS `code_integrity`. Rely on one only
|
|
53
|
+
if you decide to; until you ask for it, its absence or failure does not make a report incomplete;
|
|
54
|
+
- **personal** — only when the backend asks for it and the app's developers enabled it, and the
|
|
55
|
+
location only with the person's permission as well. Drop any personal measurement you did not ask
|
|
56
|
+
for before you store or read the report, after checking the signature over the bytes as sent.
|
|
57
|
+
|
|
58
|
+
## 4. Comparing code in memory with its files
|
|
59
|
+
|
|
60
|
+
**Android** compares three regions: `core` (the C library and the runtime), `ext` (the other system
|
|
61
|
+
libraries the app has mapped) and `app` (the app's own native code). In each region the expected bytes
|
|
62
|
+
are the compared, skipped and unreadable bytes together, and `<region>_complete` is true exactly when
|
|
63
|
+
some code was expected and all of it was compared. `checked` false: the comparison did not run.
|
|
64
|
+
`checked` true with nothing compared in a region that always holds code: nothing was measured there.
|
|
65
|
+
|
|
66
|
+
**iOS** can compare only the app's own code. The system libraries live in a shared cache no app can
|
|
67
|
+
read: `system_images_unreadable` counts them, and their figures are always 0 — not measured, never
|
|
68
|
+
clean. Every app image the loader has mapped is expected, and each is compared, unreadable (its file
|
|
69
|
+
could not be read in full) or skipped (no usable `__text`, a `__TEXT` too large, no live address);
|
|
70
|
+
`app_images_not_compared` names each one not compared, by its path in the app, with why. Images and
|
|
71
|
+
bytes both add up: expected = compared + unreadable + skipped. `app_complete` is true exactly when every
|
|
72
|
+
image was compared — the images decide, since an image whose `__text` size could not be read adds no
|
|
73
|
+
bytes — and then the main executable is among them (`main_executable_compared`).
|
|
74
|
+
|
|
75
|
+
On both, a comparison short of complete compared part of the code: never a complete clean comparison.
|
|
76
|
+
Any differing byte in compared code is a change in code that should never change.
|
|
77
|
+
|
|
78
|
+
## 5. Location
|
|
79
|
+
|
|
80
|
+
- Without the permission (`permission` `none`) no coordinates are sent.
|
|
81
|
+
- The location is the one the phone already holds — never a fresh fix, never a prompt — and
|
|
82
|
+
`age_seconds` says how old it is. How old is too old is yours to decide.
|
|
83
|
+
- With the permission and no coordinates, the phone holds no location — but only when every location
|
|
84
|
+
source was read: with a source in `unread`, the location may be there.
|
|
85
|
+
- Both answers describe one fix, read once per scan, and carry its time (`fix_time`, Unix seconds):
|
|
86
|
+
two different times are two observations presented as one.
|
|
87
|
+
- `mock_location` never carries coordinates, so asking for it alone never sends them. On iOS `mock` is
|
|
88
|
+
Apple's flag, from iOS 15; `accessory` marks a location from an external GPS accessory.
|
|
89
|
+
|
|
90
|
+
## 6. Lists
|
|
91
|
+
|
|
92
|
+
- **Apps** (`services`, `listeners`, `device_admins`, `remote_access_apps`): at most 32 from a
|
|
93
|
+
collector, each with `package`, and `service`, `system` and `installer` where Android shows them;
|
|
94
|
+
`truncated` is true when the phone had more. A list is complete only when `truncated` is false.
|
|
95
|
+
- **`split_apks`**: each installed split's file `name` and `sha256`, at most 64, **sorted by name, none
|
|
96
|
+
twice** — the one rule the schema cannot state. Empty without splits. `apk_sha256` is the base APK's
|
|
97
|
+
alone, so installs of one release with different splits share it.
|
|
98
|
+
- **`current_signers`, `signing_lineage`**: at most 8 lower-case hex SHA-256 digests each.
|
|
99
|
+
- **`addresses`** (`local_addresses`): at most 64, each an `interface` and an `address` of at most 64
|
|
100
|
+
characters.
|
|
101
|
+
|
|
102
|
+
## 7. Figures that cannot all be true
|
|
103
|
+
|
|
104
|
+
A report whose figures contradict each other is one to distrust: not a measurement, clean or
|
|
105
|
+
otherwise.
|
|
106
|
+
|
|
107
|
+
- code-comparison coverage that does not add up, or a `complete` flag that does not follow from it
|
|
108
|
+
(section 4);
|
|
109
|
+
- no writable-and-executable mapping, with the sizes of such mappings listed;
|
|
110
|
+
- screen recording `unsupported` on a platform version that measures it (iOS; Android 15 and later);
|
|
111
|
+
- two different `fix_time`s for the location and the mock-location answers.
|
|
112
|
+
|
|
113
|
+
These, by contrast, are failed reads — incomplete, not contradictions: a process map read as empty
|
|
114
|
+
(`maps_lines` 0), no thread found in a running process (`thread_count` 0), a code comparison that did
|
|
115
|
+
not run or compared nothing in a region that always holds code.
|
|
116
|
+
|
|
117
|
+
## 8. Versions
|
|
118
|
+
|
|
119
|
+
- `collector_version` counts per platform. This contract: Android 8, iOS 5 — the schema allows no
|
|
120
|
+
lower version on either, since an older collector's report is not this contract's.
|
|
121
|
+
- Before these versions a collector could report a failed read as a clean value (Android before 8) or
|
|
122
|
+
a partial comparison of the app's code as complete (iOS before 5). Refuse those versions, or keep
|
|
123
|
+
their scans apart as older evidence.
|
|
124
|
+
- Fields are only ever added; a field's meaning never changes. A new measurement is volunteered before
|
|
125
|
+
any backend asks for it, so that older apps keep working. Unknown fields and unknown measurements
|
|
126
|
+
are ignored.
|
|
127
|
+
- A new contract gets a folder of its own beside this one.
|
|
128
|
+
|
|
129
|
+
## 9. The samples
|
|
130
|
+
|
|
131
|
+
Each sample holds the report as a collector signs it (`report`), the plan it answers
|
|
132
|
+
(`required_probes`, and the personal `optional_probes`), and how a backend following these rules
|
|
133
|
+
judges it (`expect`, with `why`). The envelope's `installation_id`, `challenge_id`, `challenge_nonce`
|
|
134
|
+
and `collected_at` are placeholders a test replaces before signing. Each is checked, as published,
|
|
135
|
+
against the reference backend's judgment.
|
|
136
|
+
|
|
137
|
+
| Sample | Judged | What it shows |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| `android-8-complete` | complete | a clean phone, every requested measurement whole, one split APK listed apart |
|
|
140
|
+
| `android-8-incomplete` | incomplete | a setting in `unread`, a probe in `error` |
|
|
141
|
+
| `android-8-invalid` | invalid | coverage that does not add up |
|
|
142
|
+
| `android-8-location-one-fix` | complete | both location answers with one `fix_time` |
|
|
143
|
+
| `android-8-location-two-fixes` | invalid | the mock flag of another fix than the coordinates |
|
|
144
|
+
| `android-8-refused` | refused | an `unread` that is not an object |
|
|
145
|
+
| `ios-5-complete` | complete | a clean iPhone, every app image compared |
|
|
146
|
+
| `ios-5-incomplete` | incomplete | an `ok` without its fields |
|
|
147
|
+
| `ios-5-partial` | partial | the app-code comparison requested, one image unreadable |
|
|
148
|
+
| `ios-5-invalid` | invalid | images that do not add up |
|
|
149
|
+
| `ios-5-refused` | refused | an `app_images_not_compared` that is not an object |
|