@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.
Files changed (117) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +2 -0
  4. package/NecessaryReactNative.podspec +23 -0
  5. package/README.md +429 -2
  6. package/android/build.gradle +83 -0
  7. package/android/src/main/AndroidManifest.xml +41 -0
  8. package/android/src/main/cpp/CMakeLists.txt +7 -0
  9. package/android/src/main/cpp/code_integrity.cpp +484 -0
  10. package/android/src/main/java/com/necessarysdk/reactnative/Causes.kt +49 -0
  11. package/android/src/main/java/com/necessarysdk/reactnative/InstallationKeyManager.kt +388 -0
  12. package/android/src/main/java/com/necessarysdk/reactnative/IntegrityProbeManager.kt +1157 -0
  13. package/android/src/main/java/com/necessarysdk/reactnative/KeyPresence.kt +62 -0
  14. package/android/src/main/java/com/necessarysdk/reactnative/NecessaryModules.kt +94 -0
  15. package/android/src/main/java/com/necessarysdk/reactnative/NecessaryNative.kt +338 -0
  16. package/android/src/main/java/com/necessarysdk/reactnative/NecessaryPackage.kt +38 -0
  17. package/android/src/main/java/com/necessarysdk/reactnative/ProbeReads.kt +333 -0
  18. package/android/src/main/java/com/necessarysdk/reactnative/ScreenGuard.kt +56 -0
  19. package/android/src/main/java/com/necessarysdk/reactnative/SecureStore.kt +127 -0
  20. package/android/src/main/java/com/necessarysdk/reactnative/StepUpKeyEntry.kt +57 -0
  21. package/android/src/main/java/com/necessarysdk/reactnative/StepUpKeyManager.kt +767 -0
  22. package/contract/report-1/android-8-ios-5/RULES.md +149 -0
  23. package/contract/report-1/android-8-ios-5/report.schema.json +2753 -0
  24. package/contract/report-1/android-8-ios-5/samples/android-8-complete.json +205 -0
  25. package/contract/report-1/android-8-ios-5/samples/android-8-incomplete.json +205 -0
  26. package/contract/report-1/android-8-ios-5/samples/android-8-invalid.json +205 -0
  27. package/contract/report-1/android-8-ios-5/samples/android-8-location-one-fix.json +228 -0
  28. package/contract/report-1/android-8-ios-5/samples/android-8-location-two-fixes.json +228 -0
  29. package/contract/report-1/android-8-ios-5/samples/android-8-refused.json +208 -0
  30. package/contract/report-1/android-8-ios-5/samples/ios-5-complete.json +106 -0
  31. package/contract/report-1/android-8-ios-5/samples/ios-5-incomplete.json +105 -0
  32. package/contract/report-1/android-8-ios-5/samples/ios-5-invalid.json +107 -0
  33. package/contract/report-1/android-8-ios-5/samples/ios-5-partial.json +110 -0
  34. package/contract/report-1/android-8-ios-5/samples/ios-5-refused.json +109 -0
  35. package/doc/integration_guide.md +771 -0
  36. package/doc/measurements.md +774 -0
  37. package/ios/InstallationKeyManager.swift +694 -0
  38. package/ios/IntegrityProbeManager.swift +959 -0
  39. package/ios/NecessaryModules.h +34 -0
  40. package/ios/NecessaryModules.mm +208 -0
  41. package/ios/NecessaryNative.swift +327 -0
  42. package/ios/PrivacyInfo.xcprivacy +14 -0
  43. package/ios/ProbeReads.swift +370 -0
  44. package/lib/client.d.ts +105 -0
  45. package/lib/client.js +176 -0
  46. package/lib/collector.d.ts +18 -0
  47. package/lib/collector.js +49 -0
  48. package/lib/deviceTrust.d.ts +253 -0
  49. package/lib/deviceTrust.js +699 -0
  50. package/lib/errors.d.ts +43 -0
  51. package/lib/errors.js +56 -0
  52. package/lib/index.d.ts +18 -0
  53. package/lib/index.js +34 -0
  54. package/lib/installationKey.d.ts +62 -0
  55. package/lib/installationKey.js +185 -0
  56. package/lib/installationStore.d.ts +49 -0
  57. package/lib/installationStore.js +127 -0
  58. package/lib/native/NativeNecessaryInstallationKey.d.ts +10 -0
  59. package/lib/native/NativeNecessaryInstallationKey.js +4 -0
  60. package/lib/native/NativeNecessaryIntegrity.d.ts +8 -0
  61. package/lib/native/NativeNecessaryIntegrity.js +4 -0
  62. package/lib/native/NativeNecessarySecureScreen.d.ts +7 -0
  63. package/lib/native/NativeNecessarySecureScreen.js +4 -0
  64. package/lib/native/NativeNecessaryStepUpKey.d.ts +9 -0
  65. package/lib/native/NativeNecessaryStepUpKey.js +4 -0
  66. package/lib/native/NativeNecessaryStorage.d.ts +14 -0
  67. package/lib/native/NativeNecessaryStorage.js +4 -0
  68. package/lib/protocol.d.ts +24 -0
  69. package/lib/protocol.js +50 -0
  70. package/lib/reinstallHint.d.ts +23 -0
  71. package/lib/reinstallHint.js +65 -0
  72. package/lib/report.d.ts +62 -0
  73. package/lib/report.js +134 -0
  74. package/lib/requestProof.d.ts +116 -0
  75. package/lib/requestProof.js +220 -0
  76. package/lib/secureScreen.d.ts +15 -0
  77. package/lib/secureScreen.js +32 -0
  78. package/lib/stepUpKey.d.ts +105 -0
  79. package/lib/stepUpKey.js +187 -0
  80. package/lib/transport.d.ts +39 -0
  81. package/lib/transport.js +117 -0
  82. package/lib/util/answers.d.ts +6 -0
  83. package/lib/util/answers.js +16 -0
  84. package/lib/util/bytes.d.ts +18 -0
  85. package/lib/util/bytes.js +207 -0
  86. package/lib/util/native.d.ts +20 -0
  87. package/lib/util/native.js +49 -0
  88. package/lib/util/release.d.ts +2 -0
  89. package/lib/util/release.js +7 -0
  90. package/lib/util/target.d.ts +18 -0
  91. package/lib/util/target.js +128 -0
  92. package/package.json +81 -4
  93. package/src/client.ts +221 -0
  94. package/src/collector.ts +64 -0
  95. package/src/deviceTrust.ts +851 -0
  96. package/src/errors.ts +71 -0
  97. package/src/index.ts +19 -0
  98. package/src/installationKey.ts +223 -0
  99. package/src/installationStore.ts +143 -0
  100. package/src/native/NativeNecessaryInstallationKey.ts +12 -0
  101. package/src/native/NativeNecessaryIntegrity.ts +15 -0
  102. package/src/native/NativeNecessarySecureScreen.ts +9 -0
  103. package/src/native/NativeNecessaryStepUpKey.ts +11 -0
  104. package/src/native/NativeNecessaryStorage.ts +16 -0
  105. package/src/protocol.ts +52 -0
  106. package/src/reinstallHint.ts +69 -0
  107. package/src/report.ts +168 -0
  108. package/src/requestProof.ts +295 -0
  109. package/src/secureScreen.ts +29 -0
  110. package/src/stepUpKey.ts +238 -0
  111. package/src/transport.ts +145 -0
  112. package/src/util/answers.ts +15 -0
  113. package/src/util/bytes.ts +194 -0
  114. package/src/util/native.ts +56 -0
  115. package/src/util/release.ts +6 -0
  116. package/src/util/target.ts +132 -0
  117. package/vectors/client_server.json +264 -0
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ /** The native integrity collector: measures what a challenge asks for and the app enabled, and nothing else. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.NativeIntegrityCollector = void 0;
5
+ const errors_1 = require("./errors");
6
+ const native_1 = require("./util/native");
7
+ const INTEGRITY_MODULE_MISSING = () => new errors_1.NativeKeyException('NATIVE_INTEGRITY_PLUGIN_MISSING', "The SDK's integrity module is not linked into this app: rebuild the app.");
8
+ class NativeIntegrityCollector {
9
+ /**
10
+ * [enabledMeasurements]: the personal measurements this app's developers allow; with
11
+ * [optionalProbes], what the backend asks for, the collector measures only what is in both.
12
+ */
13
+ async collect(args) {
14
+ const native = (0, native_1.integrityModule)();
15
+ if (native === null) {
16
+ throw INTEGRITY_MODULE_MISSING();
17
+ }
18
+ let value;
19
+ try {
20
+ // What the server asks for and what this app allows; the native collector measures only what
21
+ // is in both.
22
+ value = await native.collect(args.requiredProbes, args.challengeNonce, args.optionalProbes ?? [], args.enabledMeasurements);
23
+ }
24
+ catch (error) {
25
+ throw (0, native_1.nativeFailure)(error, 'The native integrity collector failed.');
26
+ }
27
+ if (!(0, native_1.isObject)(value)) {
28
+ throw new errors_1.NativeKeyException('EMPTY_INTEGRITY_RESPONSE', 'The native integrity collector returned no measurements.');
29
+ }
30
+ return value;
31
+ }
32
+ /**
33
+ * Asks the person for the OS location permission and returns what the app now holds: precise,
34
+ * approximate or none. Only the app asks, from its own screen; a scan never prompts.
35
+ */
36
+ async requestLocationPermission() {
37
+ const native = (0, native_1.integrityModule)();
38
+ if (native === null) {
39
+ throw INTEGRITY_MODULE_MISSING();
40
+ }
41
+ try {
42
+ return (await native.requestLocationPermission()) ?? 'none';
43
+ }
44
+ catch (error) {
45
+ throw (0, native_1.nativeFailure)(error, 'The location permission request failed.');
46
+ }
47
+ }
48
+ }
49
+ exports.NativeIntegrityCollector = NativeIntegrityCollector;
@@ -0,0 +1,253 @@
1
+ /**
2
+ * DeviceTrust: the one object an app configures -- its backend, the personal measurements it
3
+ * enables, step-up and the app PIN -- and what it asks of it: start, scan, and sign a request (the
4
+ * public guide).
5
+ */
6
+ import { DeviceTrustClient } from './client';
7
+ import { NativeIntegrityCollector } from './collector';
8
+ import { NativeKeyException } from './errors';
9
+ import type { InstallationIdentity } from './installationKey';
10
+ import { InstallationStore } from './installationStore';
11
+ import { ReinstallHintReader } from './reinstallHint';
12
+ import { type ScanChallenge, type SignedReport } from './report';
13
+ import { RequestProof } from './requestProof';
14
+ import { NativeStepUpKey, StepUpKeyMetadata, StepUpOptions } from './stepUpKey';
15
+ import { type DeviceTrustTransport, type JsonObject } from './transport';
16
+ /**
17
+ * The personal measurements an app may enable (the public guide, section 8). Each is collected
18
+ * only when the backend asks for it as well -- the location only with the person's permission too.
19
+ */
20
+ export declare const PersonalMeasurement: {
21
+ readonly localAddresses: "local_addresses";
22
+ readonly accessibility: "accessibility";
23
+ readonly notificationAccess: "notification_access";
24
+ readonly otherAccess: "other_access";
25
+ readonly screenCapture: "screen_capture";
26
+ readonly location: "location";
27
+ readonly mockLocation: "mock_location";
28
+ };
29
+ /** The name the report and the backend use for one personal measurement. */
30
+ export type PersonalMeasurement = (typeof PersonalMeasurement)[keyof typeof PersonalMeasurement];
31
+ /** Every personal measurement, in the guide's order. */
32
+ export declare const personalMeasurements: readonly PersonalMeasurement[];
33
+ /** Where the backend takes the app PIN's two calls (the public guide, section 4). */
34
+ export declare class AppPinOptions {
35
+ readonly path: string;
36
+ readonly proofPath: string;
37
+ constructor(options?: {
38
+ path?: string;
39
+ proofPath?: string;
40
+ });
41
+ }
42
+ /** One scan: the challenge it answered, the report as signed, and the backend's answer, whole. */
43
+ export declare class ScanResult {
44
+ readonly challenge: ScanChallenge;
45
+ readonly report: SignedReport;
46
+ readonly answer: JsonObject;
47
+ constructor(value: {
48
+ challenge: ScanChallenge;
49
+ report: SignedReport;
50
+ answer: JsonObject;
51
+ });
52
+ /** The stored report (`integrity.report_id`), which a scan made for one request sends back with that request. */
53
+ get reportId(): string | null;
54
+ }
55
+ export interface DeviceTrustOptions {
56
+ /** What this app enables; the backend asks for what it needs of them. */
57
+ personalMeasurements?: Iterable<PersonalMeasurement>;
58
+ /** How the step-up key is made, or null for no step-up. */
59
+ stepUp?: StepUpOptions | null;
60
+ /** The app PIN's calls, or null for no app PIN. */
61
+ appPin?: AppPinOptions | null;
62
+ /** Every registration's answer, whole, awaited before the installation goes on. */
63
+ onRegistered?: ((answer: JsonObject) => void | Promise<void>) | null;
64
+ }
65
+ /** The same, with the parts tests replace. */
66
+ export interface DeviceTrustParts extends DeviceTrustOptions {
67
+ store?: InstallationStore;
68
+ collector?: NativeIntegrityCollector;
69
+ hintReader?: ReinstallHintReader;
70
+ stepUpKey?: NativeStepUpKey;
71
+ /** The phone's platform, `android` or `ios`, for the registration. */
72
+ platform?: () => string;
73
+ }
74
+ export declare class DeviceTrust {
75
+ /** The guide's calls, for an app that makes them itself. */
76
+ readonly client: DeviceTrustClient;
77
+ /**
78
+ * What this app enables; the backend asks for what it needs of them. The app may change it
79
+ * whenever the person does -- in a privacy setting, say: the next scan measures what it holds
80
+ * then, and the installation, its session and its step-up key stay as they are.
81
+ */
82
+ personalMeasurements: Set<PersonalMeasurement>;
83
+ /** How the step-up key is made, or null for no step-up. An app that takes these from its backend sets them before [start]. */
84
+ stepUp: StepUpOptions | null;
85
+ /** The app PIN's calls, or null for no app PIN. */
86
+ readonly appPin: AppPinOptions | null;
87
+ /**
88
+ * Every registration's answer, whole -- also those made again inside a scan, when the backend had
89
+ * lost the installation -- awaited before the installation goes on.
90
+ */
91
+ readonly onRegistered: ((answer: JsonObject) => void | Promise<void>) | null;
92
+ private readonly store;
93
+ private readonly collector;
94
+ private readonly hintReader;
95
+ private readonly stepUpKeyNative;
96
+ private identityValue;
97
+ private session;
98
+ private loading;
99
+ private starting;
100
+ private proving;
101
+ private readonly scanning;
102
+ private generation;
103
+ private resetting;
104
+ private saving;
105
+ private reenrolling;
106
+ private readonly mutations;
107
+ private offeredStepUpKey;
108
+ private hint;
109
+ private hintRead;
110
+ /** Why step-up is off on this phone although the app turned it on -- no screen lock, say -- or null. */
111
+ stepUpUnavailable: NativeKeyException | null;
112
+ /** Talks to your backend over JSON and HTTPS under [baseUrl], the SDK's default API (the public guide, section 4). */
113
+ constructor(options: DeviceTrustOptions & {
114
+ baseUrl: string;
115
+ });
116
+ /** The same over the app's own [transport]. */
117
+ constructor(options: DeviceTrustParts & {
118
+ transport: DeviceTrustTransport;
119
+ });
120
+ /** The same over the app's own [transport]; [parts] replace the native ones in tests. */
121
+ static over(transport: DeviceTrustTransport, parts?: DeviceTrustParts): DeviceTrust;
122
+ get identity(): InstallationIdentity | null;
123
+ /** The installation session from proving the key, or null before [start]. */
124
+ get installationSession(): string | null;
125
+ /** The step-up key offered at the last registration. */
126
+ get stepUpKey(): StepUpKeyMetadata | null;
127
+ /** The wire names of [personalMeasurements], in the guide's order. */
128
+ get enabledMeasurements(): string[];
129
+ /**
130
+ * The installation: its key, made on first use, and its ID. A key made now is a new installation
131
+ * ([InstallationIdentity.createdThisLaunch]): whatever the app kept for the one before belongs to
132
+ * that one. Calls at once share one load, so one key gets one ID.
133
+ */
134
+ loadIdentity(): Promise<InstallationIdentity>;
135
+ private load;
136
+ /**
137
+ * A persistent write or a key change for the installation [generation]: refused before it starts
138
+ * when reset() or close() has ended that installation, and waited for by a reset that began while
139
+ * it ran. The order is the whole guarantee: endCalls() moves the generation on at once, in the
140
+ * same turn as reset() is called, so a write asked for after that throws here without writing;
141
+ * one asked for before is in [mutations], which the reset waits for before it deletes anything.
142
+ */
143
+ private mutate;
144
+ /** Throws when reset() or close() ended the installation [generation] belongs to: a call keeps nothing of one that is gone. */
145
+ private current;
146
+ /**
147
+ * Loads or makes the key, registers it -- with the step-up key, when step-up is on -- and proves
148
+ * the installation holds it: the installation session. A refusal outside the guide's protocol
149
+ * errors goes to the app as it came. A second call while one runs gets its result.
150
+ */
151
+ start(): Promise<InstallationIdentity>;
152
+ private startOnce;
153
+ /** A new installation session: the key's challenge, answered. A second call while one runs gets its result. */
154
+ prove(): Promise<string>;
155
+ /**
156
+ * Deletes the key, the installation ID (InstallationStore.clear) and the step-up key, and forgets
157
+ * the session: the next start is a new installation, linked to this one by nothing it holds.
158
+ * Calls still running end with InterruptedCallException and keep nothing; calls made from now on
159
+ * wait for the deletion, and never join one made before.
160
+ */
161
+ reset(): Promise<void>;
162
+ /** Moves on to the next installation: the calls running now keep nothing, and the next ones start afresh. */
163
+ private endCalls;
164
+ private proveOnce;
165
+ private register;
166
+ /**
167
+ * Whether the backend holds the step-up key offered: it takes one only when it first registers an
168
+ * installation, so a key made later -- after the screen lock was removed and set again, say -- it
169
+ * takes by re-enrolment alone. Its answer says so (`stepup_key_matches`, or for an installation
170
+ * new to it `stepup_key_registered`); until it holds this key, approvals wait for reenrolStepUp,
171
+ * across restarts too. A registration without a key, the phone unable to make one, leaves any key
172
+ * made later waiting the same way. A backend whose answer says nothing is taken at its word unless
173
+ * a re-enrolment is pending.
174
+ */
175
+ private settleStepUpKey;
176
+ /**
177
+ * The step-up key as [stepUp] asks: an existing key is kept as it is. Step-up is optional, so a
178
+ * key the phone cannot make leaves it off. Its use waits for the backend's answer
179
+ * (settleStepUpKey).
180
+ */
181
+ private prepareStepUpKey;
182
+ /** A step-up key a re-enrolment replaces: one that can never sign again, or one the keystore cannot vouch for. */
183
+ private static replaceableKey;
184
+ /** What approvals answer while the backend does not hold the step-up key. */
185
+ private static reenrolPending;
186
+ /**
187
+ * A scan: under the installation session -- or, for a scan that approves one [operation]
188
+ * ([describeOperation]), under the app's account [session] for this installation, the one that
189
+ * will send that request. The guide's protocol errors are acted on once; an account session is
190
+ * the app's to renew. A second call for the same scan while one runs gets its result; a scan
191
+ * whose answer was lost is simply asked for again.
192
+ */
193
+ scan(options?: {
194
+ session?: string | null;
195
+ operation?: Record<string, unknown> | null;
196
+ }): Promise<ScanResult>;
197
+ private scanWithProtocol;
198
+ private scanOnce;
199
+ /**
200
+ * Signs one request to your backend under [bearer], your account session: [body] is sent exactly
201
+ * as given. With [stepUp] the person approves it with the step-up key -- the phone asks, saying
202
+ * [reason]; with [appPin] the backend checks the PIN for it first; [reportId] names the scan made
203
+ * for it alone. Send it with RequestProof.headers beside `Authorization: Bearer`, or with [send];
204
+ * each proof serves this request once.
205
+ */
206
+ signRequest(args: {
207
+ method: string;
208
+ path: string;
209
+ bearer: string;
210
+ body?: string;
211
+ stepUp?: boolean;
212
+ reason?: string;
213
+ appPin?: string | null;
214
+ reportId?: string | null;
215
+ }): Promise<RequestProof>;
216
+ /**
217
+ * Re-enrols the step-up key under the account [session], with the person's own
218
+ * [reauthentication] -- the account's password, say: `{password: ...}`. For a key the phone has
219
+ * lost or can no longer use -- the screen lock removed (`STEPUP_KEY_INVALIDATED`) -- or one made
220
+ * for other options: the person unlocks the new key to sign its proof, the phone showing
221
+ * [reason], and the backend takes it for that account alone (the public guide, section 3).
222
+ * Approvals use it from then on. A key replaced before the backend took the new one -- the person
223
+ * cancelled, the password was wrong, the answer was lost -- leaves step-up waiting: approvals
224
+ * answer `STEPUP_REENROL_PENDING`, across restarts too, until a call of this succeeds.
225
+ */
226
+ reenrolStepUp(args: {
227
+ session: string;
228
+ reauthentication?: Record<string, unknown>;
229
+ reason?: string;
230
+ }): Promise<JsonObject>;
231
+ private reenrolStepUpOnce;
232
+ /** Sends [proof]'s request through the SDK's transport. */
233
+ send(proof: RequestProof): Promise<JsonObject>;
234
+ /**
235
+ * Sets the account's app PIN under the account [session], with the person's own
236
+ * [reauthentication] -- the account's password, say: `{password: ...}`.
237
+ */
238
+ setAppPin(args: {
239
+ session: string;
240
+ pin: string;
241
+ reauthentication?: Record<string, unknown>;
242
+ }): Promise<JsonObject>;
243
+ /**
244
+ * Asks the person for the location permission, for the location measurements: from a button on
245
+ * the app's own screen, since a scan never asks. Answers `precise`, `approximate` or `none`.
246
+ */
247
+ requestLocationPermission(): Promise<string>;
248
+ /**
249
+ * Closes the transport. Calls still running end with InterruptedCallException, or with the
250
+ * transport's own failure, and keep nothing; the key and the installation stay as they are.
251
+ */
252
+ close(): void;
253
+ }