@necessarysdk/react-native 0.0.0-stage → 0.1.5

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 (114) hide show
  1. package/CHANGELOG.md +22 -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 +1152 -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 +323 -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 +314 -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 +762 -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 +960 -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 +353 -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 +690 -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 +140 -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/bytes.d.ts +18 -0
  83. package/lib/util/bytes.js +207 -0
  84. package/lib/util/native.d.ts +20 -0
  85. package/lib/util/native.js +49 -0
  86. package/lib/util/release.d.ts +2 -0
  87. package/lib/util/release.js +7 -0
  88. package/lib/util/target.d.ts +18 -0
  89. package/lib/util/target.js +128 -0
  90. package/package.json +81 -4
  91. package/src/client.ts +221 -0
  92. package/src/collector.ts +64 -0
  93. package/src/deviceTrust.ts +842 -0
  94. package/src/errors.ts +71 -0
  95. package/src/index.ts +19 -0
  96. package/src/installationKey.ts +223 -0
  97. package/src/installationStore.ts +143 -0
  98. package/src/native/NativeNecessaryInstallationKey.ts +12 -0
  99. package/src/native/NativeNecessaryIntegrity.ts +15 -0
  100. package/src/native/NativeNecessarySecureScreen.ts +9 -0
  101. package/src/native/NativeNecessaryStepUpKey.ts +11 -0
  102. package/src/native/NativeNecessaryStorage.ts +16 -0
  103. package/src/protocol.ts +52 -0
  104. package/src/reinstallHint.ts +69 -0
  105. package/src/report.ts +174 -0
  106. package/src/requestProof.ts +295 -0
  107. package/src/secureScreen.ts +29 -0
  108. package/src/stepUpKey.ts +238 -0
  109. package/src/transport.ts +145 -0
  110. package/src/util/bytes.ts +194 -0
  111. package/src/util/native.ts +56 -0
  112. package/src/util/release.ts +6 -0
  113. package/src/util/target.ts +132 -0
  114. package/vectors/client_server.json +264 -0
@@ -0,0 +1,43 @@
1
+ /** The failures the SDK reports. */
2
+ /**
3
+ * A failure of the phone's keystore or of a prompt: [code] says which -- `KEY_UNAVAILABLE` (the
4
+ * keystore could not be reached for the installation key; no key was made over it: try again, or
5
+ * reset()), `STEPUP_KEY_INVALIDATED` (the key can never sign again: re-enrol),
6
+ * `STEPUP_KEY_UNAVAILABLE` (the keystore could not say whether the key is usable, or could not be
7
+ * reached; it was kept: try again, or re-enrol), `STEPUP_CANCELLED`, `STEPUP_REENROL_PENDING` and
8
+ * the like -- and [message] says it in words, followed by everything that failed beneath it
9
+ * (`[cause: ...]`) down to the keystore's own answer wherever the phone gives one.
10
+ */
11
+ export declare class NativeKeyException extends Error {
12
+ /** What failed, for the app to act on. */
13
+ readonly code: string;
14
+ constructor(code: string, message: string);
15
+ toString(): string;
16
+ }
17
+ /**
18
+ * A refusal from your backend, or a request that did not reach it: the backend's
19
+ * `{"error": {"code", "message", "details"}}` (the guide, section 4), with the HTTP status.
20
+ */
21
+ export declare class ApiException extends Error {
22
+ /** The HTTP status, when the backend answered. */
23
+ readonly statusCode?: number;
24
+ /** The backend's `code`: what the app acts on. */
25
+ readonly code?: string;
26
+ /** The backend's `details`, such as `retry_after_seconds`. */
27
+ readonly details?: Record<string, unknown>;
28
+ constructor(message: string, options?: {
29
+ statusCode?: number;
30
+ code?: string;
31
+ details?: Record<string, unknown>;
32
+ });
33
+ toString(): string;
34
+ }
35
+ /**
36
+ * A call that DeviceTrust.reset or DeviceTrust.close ended while it ran: what it found belongs to
37
+ * an installation that is gone, or to a client that is closed, so nothing of it was kept. After a
38
+ * reset, call again.
39
+ */
40
+ export declare class InterruptedCallException extends Error {
41
+ constructor(message: string);
42
+ toString(): string;
43
+ }
package/lib/errors.js ADDED
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ /** The failures the SDK reports. */
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.InterruptedCallException = exports.ApiException = exports.NativeKeyException = void 0;
5
+ /**
6
+ * A failure of the phone's keystore or of a prompt: [code] says which -- `KEY_UNAVAILABLE` (the
7
+ * keystore could not be reached for the installation key; no key was made over it: try again, or
8
+ * reset()), `STEPUP_KEY_INVALIDATED` (the key can never sign again: re-enrol),
9
+ * `STEPUP_KEY_UNAVAILABLE` (the keystore could not say whether the key is usable, or could not be
10
+ * reached; it was kept: try again, or re-enrol), `STEPUP_CANCELLED`, `STEPUP_REENROL_PENDING` and
11
+ * the like -- and [message] says it in words, followed by everything that failed beneath it
12
+ * (`[cause: ...]`) down to the keystore's own answer wherever the phone gives one.
13
+ */
14
+ class NativeKeyException extends Error {
15
+ constructor(code, message) {
16
+ super(message);
17
+ this.name = 'NativeKeyException';
18
+ this.code = code;
19
+ }
20
+ toString() {
21
+ return `${this.message} [${this.code}]`;
22
+ }
23
+ }
24
+ exports.NativeKeyException = NativeKeyException;
25
+ /**
26
+ * A refusal from your backend, or a request that did not reach it: the backend's
27
+ * `{"error": {"code", "message", "details"}}` (the guide, section 4), with the HTTP status.
28
+ */
29
+ class ApiException extends Error {
30
+ constructor(message, options = {}) {
31
+ super(message);
32
+ this.name = 'ApiException';
33
+ this.statusCode = options.statusCode;
34
+ this.code = options.code;
35
+ this.details = options.details;
36
+ }
37
+ toString() {
38
+ return this.message;
39
+ }
40
+ }
41
+ exports.ApiException = ApiException;
42
+ /**
43
+ * A call that DeviceTrust.reset or DeviceTrust.close ended while it ran: what it found belongs to
44
+ * an installation that is gone, or to a client that is closed, so nothing of it was kept. After a
45
+ * reset, call again.
46
+ */
47
+ class InterruptedCallException extends Error {
48
+ constructor(message) {
49
+ super(message);
50
+ this.name = 'InterruptedCallException';
51
+ }
52
+ toString() {
53
+ return this.message;
54
+ }
55
+ }
56
+ exports.InterruptedCallException = InterruptedCallException;
package/lib/index.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Device integrity for React Native apps: the client half of the public integration guide. A key
3
+ * the SDK never exports, signed measurements of the phone and the app, and request proofs; the
4
+ * app's backend decides what they mean.
5
+ */
6
+ export * from './client';
7
+ export * from './collector';
8
+ export * from './deviceTrust';
9
+ export * from './errors';
10
+ export * from './installationKey';
11
+ export * from './installationStore';
12
+ export * from './protocol';
13
+ export * from './reinstallHint';
14
+ export * from './report';
15
+ export * from './requestProof';
16
+ export * from './secureScreen';
17
+ export * from './stepUpKey';
18
+ export * from './transport';
package/lib/index.js ADDED
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ /**
3
+ * Device integrity for React Native apps: the client half of the public integration guide. A key
4
+ * the SDK never exports, signed measurements of the phone and the app, and request proofs; the
5
+ * app's backend decides what they mean.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
+ };
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ __exportStar(require("./client"), exports);
23
+ __exportStar(require("./collector"), exports);
24
+ __exportStar(require("./deviceTrust"), exports);
25
+ __exportStar(require("./errors"), exports);
26
+ __exportStar(require("./installationKey"), exports);
27
+ __exportStar(require("./installationStore"), exports);
28
+ __exportStar(require("./protocol"), exports);
29
+ __exportStar(require("./reinstallHint"), exports);
30
+ __exportStar(require("./report"), exports);
31
+ __exportStar(require("./requestProof"), exports);
32
+ __exportStar(require("./secureScreen"), exports);
33
+ __exportStar(require("./stepUpKey"), exports);
34
+ __exportStar(require("./transport"), exports);
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The installation key: one non-exportable P-256 key per installation, made and held by the
3
+ * phone's keystore -- StrongBox or the trusted execution environment, the Secure Enclave, or
4
+ * where that refuses the Keychain.
5
+ */
6
+ /** A P-256 public key as a JWK: `kty`, `crv`, `alg`, `x`, `y`. */
7
+ export type PublicJwk = Record<string, unknown>;
8
+ /** The members of [jwk] in the order the phone writes them: kty, crv, alg, x, y, then any other. */
9
+ export declare function orderedJwk(jwk: Record<string, unknown>): PublicJwk;
10
+ /** RFC 7638 thumbprint of an EC public JWK, in lowercase hex: the same form a backend derives. */
11
+ export declare function jwkThumbprint(jwk: PublicJwk): string;
12
+ export declare class NativeKeyMetadata {
13
+ readonly publicKey: PublicJwk;
14
+ readonly algorithm: string;
15
+ readonly keyAlias: string;
16
+ readonly provider: string;
17
+ readonly securityLevel: string;
18
+ readonly hardwareBacked: boolean;
19
+ readonly privateKeyExportable: boolean;
20
+ readonly signatureFormat: string;
21
+ readonly created: boolean;
22
+ constructor(value: {
23
+ publicKey: PublicJwk;
24
+ algorithm: string;
25
+ keyAlias: string;
26
+ provider: string;
27
+ securityLevel: string;
28
+ hardwareBacked: boolean;
29
+ privateKeyExportable: boolean;
30
+ signatureFormat: string;
31
+ created: boolean;
32
+ });
33
+ static fromPlatform(value: Record<string, unknown>): NativeKeyMetadata;
34
+ }
35
+ export declare class NativeInstallationKey {
36
+ getOrCreateKey(): Promise<NativeKeyMetadata>;
37
+ signPayload(payloadBase64Url: string): Promise<string>;
38
+ deleteKey(): Promise<void>;
39
+ }
40
+ export declare class InstallationIdentity {
41
+ readonly installationId: string;
42
+ readonly nativeKey: NativeInstallationKey;
43
+ readonly metadata: NativeKeyMetadata;
44
+ constructor(value: {
45
+ installationId: string;
46
+ nativeKey: NativeInstallationKey;
47
+ metadata: NativeKeyMetadata;
48
+ });
49
+ get publicKeyJson(): PublicJwk;
50
+ get algorithm(): string;
51
+ get keyAlias(): string;
52
+ get provider(): string;
53
+ get securityLevel(): string;
54
+ get hardwareBacked(): boolean;
55
+ get privateKeyExportable(): boolean;
56
+ get signatureFormat(): string;
57
+ get createdThisLaunch(): boolean;
58
+ /** The key's RFC 7638 thumbprint, in lowercase hex. */
59
+ get keyThumbprint(): string;
60
+ signPayload(payloadBase64Url: string): Promise<string>;
61
+ withInstallationId(canonicalInstallationId: string): InstallationIdentity;
62
+ }
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ /**
3
+ * The installation key: one non-exportable P-256 key per installation, made and held by the
4
+ * phone's keystore -- StrongBox or the trusted execution environment, the Secure Enclave, or
5
+ * where that refuses the Keychain.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.InstallationIdentity = exports.NativeInstallationKey = exports.NativeKeyMetadata = void 0;
9
+ exports.orderedJwk = orderedJwk;
10
+ exports.jwkThumbprint = jwkThumbprint;
11
+ const errors_1 = require("./errors");
12
+ const bytes_1 = require("./util/bytes");
13
+ const native_1 = require("./util/native");
14
+ /** The members of [jwk] in the order the phone writes them: kty, crv, alg, x, y, then any other. */
15
+ function orderedJwk(jwk) {
16
+ const ordered = {};
17
+ for (const name of ['kty', 'crv', 'alg', 'x', 'y']) {
18
+ if (name in jwk) {
19
+ ordered[name] = jwk[name];
20
+ }
21
+ }
22
+ for (const [name, value] of Object.entries(jwk)) {
23
+ if (!(name in ordered)) {
24
+ ordered[name] = value;
25
+ }
26
+ }
27
+ return ordered;
28
+ }
29
+ /** RFC 7638 thumbprint of an EC public JWK, in lowercase hex: the same form a backend derives. */
30
+ function jwkThumbprint(jwk) {
31
+ // RFC 7638 canonical member order for an EC public JWK.
32
+ return (0, bytes_1.sha256Hex)(JSON.stringify({ crv: jwk['crv'], kty: 'EC', x: jwk['x'], y: jwk['y'] }));
33
+ }
34
+ class NativeKeyMetadata {
35
+ constructor(value) {
36
+ this.publicKey = value.publicKey;
37
+ this.algorithm = value.algorithm;
38
+ this.keyAlias = value.keyAlias;
39
+ this.provider = value.provider;
40
+ this.securityLevel = value.securityLevel;
41
+ this.hardwareBacked = value.hardwareBacked;
42
+ this.privateKeyExportable = value.privateKeyExportable;
43
+ this.signatureFormat = value.signatureFormat;
44
+ this.created = value.created;
45
+ }
46
+ static fromPlatform(value) {
47
+ const rawPublicKey = value['public_key'];
48
+ if (!(0, native_1.isObject)(rawPublicKey)) {
49
+ throw new TypeError('The native key provider returned no public key.');
50
+ }
51
+ const publicKey = orderedJwk(rawPublicKey);
52
+ const requiredString = (field) => {
53
+ const item = value[field];
54
+ if (typeof item !== 'string' || item.length === 0) {
55
+ throw new TypeError(`The native key provider returned an invalid ${field}.`);
56
+ }
57
+ return item;
58
+ };
59
+ const requiredBool = (field) => {
60
+ const item = value[field];
61
+ if (typeof item !== 'boolean') {
62
+ throw new TypeError(`The native key provider returned an invalid ${field}.`);
63
+ }
64
+ return item;
65
+ };
66
+ if (publicKey['kty'] !== 'EC' ||
67
+ publicKey['crv'] !== 'P-256' ||
68
+ publicKey['alg'] !== 'ES256' ||
69
+ typeof publicKey['x'] !== 'string' ||
70
+ typeof publicKey['y'] !== 'string') {
71
+ throw new TypeError('The native key provider did not return an EC P-256 public JWK.');
72
+ }
73
+ return new NativeKeyMetadata({
74
+ publicKey,
75
+ algorithm: requiredString('algorithm'),
76
+ keyAlias: requiredString('key_alias'),
77
+ provider: requiredString('provider'),
78
+ securityLevel: requiredString('security_level'),
79
+ hardwareBacked: requiredBool('hardware_backed'),
80
+ privateKeyExportable: requiredBool('private_key_exportable'),
81
+ signatureFormat: requiredString('signature_format'),
82
+ created: requiredBool('created'),
83
+ });
84
+ }
85
+ }
86
+ exports.NativeKeyMetadata = NativeKeyMetadata;
87
+ const KEY_MODULE_MISSING = new errors_1.NativeKeyException('NATIVE_KEY_PLUGIN_MISSING', "The SDK's installation-key module is not linked into this app: rebuild the app.");
88
+ class NativeInstallationKey {
89
+ async getOrCreateKey() {
90
+ const native = (0, native_1.installationKeyModule)();
91
+ if (native === null) {
92
+ throw KEY_MODULE_MISSING;
93
+ }
94
+ let value;
95
+ try {
96
+ value = await native.getOrCreateKey();
97
+ }
98
+ catch (error) {
99
+ throw (0, native_1.nativeFailure)(error, 'The native key provider failed.');
100
+ }
101
+ if (!(0, native_1.isObject)(value)) {
102
+ throw new errors_1.NativeKeyException('EMPTY_NATIVE_RESPONSE', 'The native key provider returned no key metadata.');
103
+ }
104
+ return NativeKeyMetadata.fromPlatform(value);
105
+ }
106
+ async signPayload(payloadBase64Url) {
107
+ const native = (0, native_1.installationKeyModule)();
108
+ if (native === null) {
109
+ throw KEY_MODULE_MISSING;
110
+ }
111
+ let signature;
112
+ try {
113
+ signature = await native.sign(payloadBase64Url);
114
+ }
115
+ catch (error) {
116
+ throw (0, native_1.nativeFailure)(error, 'The native key provider could not sign the challenge.');
117
+ }
118
+ if (typeof signature !== 'string' || signature.length === 0) {
119
+ throw new errors_1.NativeKeyException('EMPTY_NATIVE_SIGNATURE', 'The native key provider returned no signature.');
120
+ }
121
+ return signature;
122
+ }
123
+ async deleteKey() {
124
+ const native = (0, native_1.installationKeyModule)();
125
+ if (native === null) {
126
+ throw KEY_MODULE_MISSING;
127
+ }
128
+ try {
129
+ await native.deleteKey();
130
+ }
131
+ catch (error) {
132
+ throw (0, native_1.nativeFailure)(error, 'The native installation key could not be deleted.');
133
+ }
134
+ }
135
+ }
136
+ exports.NativeInstallationKey = NativeInstallationKey;
137
+ class InstallationIdentity {
138
+ constructor(value) {
139
+ this.installationId = value.installationId;
140
+ this.nativeKey = value.nativeKey;
141
+ this.metadata = value.metadata;
142
+ }
143
+ get publicKeyJson() {
144
+ return this.metadata.publicKey;
145
+ }
146
+ get algorithm() {
147
+ return this.metadata.algorithm;
148
+ }
149
+ get keyAlias() {
150
+ return this.metadata.keyAlias;
151
+ }
152
+ get provider() {
153
+ return this.metadata.provider;
154
+ }
155
+ get securityLevel() {
156
+ return this.metadata.securityLevel;
157
+ }
158
+ get hardwareBacked() {
159
+ return this.metadata.hardwareBacked;
160
+ }
161
+ get privateKeyExportable() {
162
+ return this.metadata.privateKeyExportable;
163
+ }
164
+ get signatureFormat() {
165
+ return this.metadata.signatureFormat;
166
+ }
167
+ get createdThisLaunch() {
168
+ return this.metadata.created;
169
+ }
170
+ /** The key's RFC 7638 thumbprint, in lowercase hex. */
171
+ get keyThumbprint() {
172
+ return jwkThumbprint(this.publicKeyJson);
173
+ }
174
+ signPayload(payloadBase64Url) {
175
+ return this.nativeKey.signPayload(payloadBase64Url);
176
+ }
177
+ withInstallationId(canonicalInstallationId) {
178
+ return new InstallationIdentity({
179
+ installationId: canonicalInstallationId,
180
+ nativeKey: this.nativeKey,
181
+ metadata: this.metadata,
182
+ });
183
+ }
184
+ }
185
+ exports.InstallationIdentity = InstallationIdentity;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Where the SDK keeps the installation ID: in the platform's secure storage beside the key -- on
3
+ * iOS in the Keychain, on this device only, readable after the first unlock; elsewhere encrypted
4
+ * under a key the platform keystore holds.
5
+ */
6
+ import { InstallationIdentity, NativeInstallationKey } from './installationKey';
7
+ /** The secure storage the store writes to: replaced in tests. */
8
+ export interface SecureStorage {
9
+ read(key: string): Promise<string | null>;
10
+ write(key: string, value: string): Promise<void>;
11
+ delete(key: string): Promise<void>;
12
+ }
13
+ /** The phone's secure storage, through the SDK's native module. */
14
+ export declare class NativeSecureStorage implements SecureStorage {
15
+ read(key: string): Promise<string | null>;
16
+ write(key: string, value: string): Promise<void>;
17
+ delete(key: string): Promise<void>;
18
+ }
19
+ /** A random (version 4) UUID, from the platform's secure random source. */
20
+ export declare function randomUuid(): Promise<string>;
21
+ export declare class InstallationStore {
22
+ static readonly installationIdKey = "recognition.installation_id.v2";
23
+ static readonly stepUpReenrolKey = "recognition.stepup_reenrol_pending.v1";
24
+ readonly key: NativeInstallationKey;
25
+ protected readonly storage: SecureStorage;
26
+ private loading;
27
+ constructor(options?: {
28
+ key?: NativeInstallationKey;
29
+ storage?: SecureStorage;
30
+ });
31
+ /**
32
+ * The installation: its key, made on first use, and its ID. A key made now
33
+ * ([InstallationIdentity.createdThisLaunch]) is a new installation, so it gets a new ID even where
34
+ * one was stored: that ID belongs to the key that was lost, and a backend refuses a known ID under
35
+ * another key. Calls at once share one load, so that one key never gets two IDs.
36
+ */
37
+ loadOrCreate(): Promise<InstallationIdentity>;
38
+ private load;
39
+ /**
40
+ * Keeps the installation your backend answered for this key, which may not be the ID proposed.
41
+ * Only this opaque ID is stored here; the private key never leaves the platform's keystore.
42
+ */
43
+ saveInstallationId(installationId: string): Promise<void>;
44
+ /** Whether a step-up key was replaced and the backend has not yet taken the new one (DeviceTrust.reenrolStepUp) -- kept across restarts. */
45
+ stepUpReenrolPending(): Promise<boolean>;
46
+ setStepUpReenrolPending(pending: boolean): Promise<void>;
47
+ /** Deletes the key and the installation ID: the next start is a new installation. */
48
+ clear(): Promise<void>;
49
+ }
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ /**
3
+ * Where the SDK keeps the installation ID: in the platform's secure storage beside the key -- on
4
+ * iOS in the Keychain, on this device only, readable after the first unlock; elsewhere encrypted
5
+ * under a key the platform keystore holds.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.InstallationStore = exports.NativeSecureStorage = void 0;
9
+ exports.randomUuid = randomUuid;
10
+ const errors_1 = require("./errors");
11
+ const native_1 = require("./util/native");
12
+ const requestProof_1 = require("./requestProof");
13
+ const installationKey_1 = require("./installationKey");
14
+ const STORAGE_MODULE_MISSING = () => new errors_1.NativeKeyException('NATIVE_STORAGE_PLUGIN_MISSING', "The SDK's storage module is not linked into this app: rebuild the app.");
15
+ /** The phone's secure storage, through the SDK's native module. */
16
+ class NativeSecureStorage {
17
+ async read(key) {
18
+ const native = (0, native_1.storageModule)();
19
+ if (native === null)
20
+ throw STORAGE_MODULE_MISSING();
21
+ try {
22
+ const value = await native.read(key);
23
+ return typeof value === 'string' ? value : null;
24
+ }
25
+ catch (error) {
26
+ throw (0, native_1.nativeFailure)(error, 'The secure storage could not be read.');
27
+ }
28
+ }
29
+ async write(key, value) {
30
+ const native = (0, native_1.storageModule)();
31
+ if (native === null)
32
+ throw STORAGE_MODULE_MISSING();
33
+ try {
34
+ await native.write(key, value);
35
+ }
36
+ catch (error) {
37
+ throw (0, native_1.nativeFailure)(error, 'The secure storage could not be written.');
38
+ }
39
+ }
40
+ async delete(key) {
41
+ const native = (0, native_1.storageModule)();
42
+ if (native === null)
43
+ throw STORAGE_MODULE_MISSING();
44
+ try {
45
+ await native.remove(key);
46
+ }
47
+ catch (error) {
48
+ throw (0, native_1.nativeFailure)(error, 'The secure storage could not be cleared.');
49
+ }
50
+ }
51
+ }
52
+ exports.NativeSecureStorage = NativeSecureStorage;
53
+ /** A random (version 4) UUID, from the platform's secure random source. */
54
+ async function randomUuid() {
55
+ const bytes = await (0, requestProof_1.secureRandomBytes)(16);
56
+ bytes[6] = (bytes[6] & 0x0f) | 0x40;
57
+ bytes[8] = (bytes[8] & 0x3f) | 0x80;
58
+ const hexBytes = Array.from(bytes, (byte) => (byte < 16 ? '0' : '') + byte.toString(16));
59
+ return [
60
+ hexBytes.slice(0, 4).join(''),
61
+ hexBytes.slice(4, 6).join(''),
62
+ hexBytes.slice(6, 8).join(''),
63
+ hexBytes.slice(8, 10).join(''),
64
+ hexBytes.slice(10, 16).join(''),
65
+ ].join('-');
66
+ }
67
+ class InstallationStore {
68
+ constructor(options = {}) {
69
+ this.loading = null;
70
+ this.key = options.key ?? new installationKey_1.NativeInstallationKey();
71
+ this.storage = options.storage ?? new NativeSecureStorage();
72
+ }
73
+ /**
74
+ * The installation: its key, made on first use, and its ID. A key made now
75
+ * ([InstallationIdentity.createdThisLaunch]) is a new installation, so it gets a new ID even where
76
+ * one was stored: that ID belongs to the key that was lost, and a backend refuses a known ID under
77
+ * another key. Calls at once share one load, so that one key never gets two IDs.
78
+ */
79
+ loadOrCreate() {
80
+ const running = this.loading;
81
+ if (running !== null) {
82
+ return running;
83
+ }
84
+ const loading = this.load().finally(() => {
85
+ if (this.loading === loading) {
86
+ this.loading = null;
87
+ }
88
+ });
89
+ this.loading = loading;
90
+ return loading;
91
+ }
92
+ async load() {
93
+ const stored = await this.storage.read(InstallationStore.installationIdKey);
94
+ const metadata = await this.key.getOrCreateKey();
95
+ let installationId = stored ?? '';
96
+ if (installationId.length === 0 || metadata.created) {
97
+ installationId = await randomUuid();
98
+ await this.saveInstallationId(installationId);
99
+ }
100
+ return new installationKey_1.InstallationIdentity({ installationId, nativeKey: this.key, metadata });
101
+ }
102
+ /**
103
+ * Keeps the installation your backend answered for this key, which may not be the ID proposed.
104
+ * Only this opaque ID is stored here; the private key never leaves the platform's keystore.
105
+ */
106
+ saveInstallationId(installationId) {
107
+ return this.storage.write(InstallationStore.installationIdKey, installationId);
108
+ }
109
+ /** Whether a step-up key was replaced and the backend has not yet taken the new one (DeviceTrust.reenrolStepUp) -- kept across restarts. */
110
+ async stepUpReenrolPending() {
111
+ return (await this.storage.read(InstallationStore.stepUpReenrolKey)) !== null;
112
+ }
113
+ setStepUpReenrolPending(pending) {
114
+ return pending
115
+ ? this.storage.write(InstallationStore.stepUpReenrolKey, '1')
116
+ : this.storage.delete(InstallationStore.stepUpReenrolKey);
117
+ }
118
+ /** Deletes the key and the installation ID: the next start is a new installation. */
119
+ async clear() {
120
+ await this.key.deleteKey();
121
+ await this.storage.delete(InstallationStore.installationIdKey);
122
+ await this.storage.delete(InstallationStore.stepUpReenrolKey);
123
+ }
124
+ }
125
+ exports.InstallationStore = InstallationStore;
126
+ InstallationStore.installationIdKey = 'recognition.installation_id.v2';
127
+ InstallationStore.stepUpReenrolKey = 'recognition.stepup_reenrol_pending.v1';
@@ -0,0 +1,10 @@
1
+ import type { TurboModule } from 'react-native';
2
+ /** The installation key, in the phone's keystore: made on first use, never exported. */
3
+ export interface Spec extends TurboModule {
4
+ getOrCreateKey(): Promise<Object>;
5
+ sign(payload: string): Promise<string>;
6
+ deleteKey(): Promise<boolean>;
7
+ reinstallHintIdentifier(): Promise<string>;
8
+ }
9
+ declare const _default: Spec | null | undefined;
10
+ export default _default;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const react_native_1 = require("react-native");
4
+ exports.default = react_native_1.TurboModuleRegistry.get('NecessaryInstallationKey');
@@ -0,0 +1,8 @@
1
+ import type { TurboModule } from 'react-native';
2
+ /** The collector: measures what a challenge asks for and the app enabled, and nothing else. */
3
+ export interface Spec extends TurboModule {
4
+ collect(requiredProbes: string[], challengeNonce: string, optionalProbes: string[], enabledMeasurements: string[]): Promise<Object>;
5
+ requestLocationPermission(): Promise<string>;
6
+ }
7
+ declare const _default: Spec | null | undefined;
8
+ export default _default;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const react_native_1 = require("react-native");
4
+ exports.default = react_native_1.TurboModuleRegistry.get('NecessaryIntegrity');
@@ -0,0 +1,7 @@
1
+ import type { TurboModule } from 'react-native';
2
+ /** Protection of the app's window while a secret is typed. */
3
+ export interface Spec extends TurboModule {
4
+ protect(on: boolean): Promise<Object>;
5
+ }
6
+ declare const _default: Spec | null | undefined;
7
+ export default _default;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const react_native_1 = require("react-native");
4
+ exports.default = react_native_1.TurboModuleRegistry.get('NecessarySecureScreen');
@@ -0,0 +1,9 @@
1
+ import type { TurboModule } from 'react-native';
2
+ /** The step-up key: a second key that the person must unlock with the screen lock. */
3
+ export interface Spec extends TurboModule {
4
+ getOrCreateKey(factor: string, mode: string, windowSeconds: number): Promise<Object>;
5
+ sign(payload: string, reason: string, mode: string): Promise<string>;
6
+ deleteKey(): Promise<boolean>;
7
+ }
8
+ declare const _default: Spec | null | undefined;
9
+ export default _default;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const react_native_1 = require("react-native");
4
+ exports.default = react_native_1.TurboModuleRegistry.get('NecessaryStepUpKey');
@@ -0,0 +1,14 @@
1
+ import type { TurboModule } from 'react-native';
2
+ /**
3
+ * The phone's secure storage for the installation ID and the pending step-up re-enrolment (the
4
+ * Keychain, readable after the first unlock, on this device only; values encrypted under a key in
5
+ * the platform keystore), and the platform's secure random source.
6
+ */
7
+ export interface Spec extends TurboModule {
8
+ read(key: string): Promise<string | null>;
9
+ write(key: string, value: string): Promise<boolean>;
10
+ remove(key: string): Promise<boolean>;
11
+ randomBytes(count: number): Promise<string>;
12
+ }
13
+ declare const _default: Spec | null | undefined;
14
+ export default _default;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const react_native_1 = require("react-native");
4
+ exports.default = react_native_1.TurboModuleRegistry.get('NecessaryStorage');