@umutcansu/react-native-pinvault 0.0.0-stage → 2.3.2
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 +55 -0
- package/LICENSE +21 -0
- package/README.md +386 -2
- package/RNPinVault.podspec +43 -0
- package/android/build.gradle +68 -0
- package/android/consumer-rules.pro +25 -0
- package/android/src/main/AndroidManifest.xml +16 -0
- package/android/src/main/java/io/github/umutcansu/pinvault/reactnative/ConfigParser.kt +300 -0
- package/android/src/main/java/io/github/umutcansu/pinvault/reactnative/JsEnvironmentGuard.kt +53 -0
- package/android/src/main/java/io/github/umutcansu/pinvault/reactnative/NativeSecurity.kt +239 -0
- package/android/src/main/java/io/github/umutcansu/pinvault/reactnative/PinVaultModule.kt +410 -0
- package/android/src/main/java/io/github/umutcansu/pinvault/reactnative/PinVaultNetworking.kt +296 -0
- package/android/src/main/java/io/github/umutcansu/pinvault/reactnative/PinVaultPackage.kt +25 -0
- package/android/src/main/java/io/github/umutcansu/pinvault/reactnative/PinnedFetch.kt +131 -0
- package/android/src/main/java/io/github/umutcansu/pinvault/reactnative/ResultMapper.kt +188 -0
- package/android/src/main/java/io/github/umutcansu/pinvault/reactnative/StrictJson.kt +225 -0
- package/android/src/main/java/io/github/umutcansu/pinvault/reactnative/VaultTokenStore.kt +63 -0
- package/ios/Core/ConfigParser.swift +276 -0
- package/ios/Core/JSEnvironmentGuard.swift +129 -0
- package/ios/Core/NativeSecurity.swift +197 -0
- package/ios/Core/PinnedFetch.swift +119 -0
- package/ios/Core/ReactNetworking.swift +172 -0
- package/ios/Core/ResultMapper.swift +163 -0
- package/ios/Core/StrictJSON.swift +214 -0
- package/ios/Core/VaultTokenStore.swift +66 -0
- package/ios/PinVaultBridge.swift +412 -0
- package/ios/PinVaultReactNetworking.swift +47 -0
- package/ios/RNPinVault.h +7 -0
- package/ios/RNPinVault.mm +293 -0
- package/ios/RNPinVaultURLRequestHandler.mm +92 -0
- package/lib/module/NativePinVault.js +13 -0
- package/lib/module/NativePinVault.js.map +1 -0
- package/lib/module/index.js +312 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/NativePinVault.d.ts +50 -0
- package/lib/typescript/src/NativePinVault.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +150 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +346 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +128 -4
- package/src/NativePinVault.ts +72 -0
- package/src/index.ts +385 -0
- package/src/types.ts +351 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
// PinVault for React Native: a thin, typed bridge over the native PinVault
|
|
2
|
+
// libraries. Pinning, keys, signature checks and vault decryption all stay
|
|
3
|
+
// native; this file only shapes inputs and outputs and runs the app's
|
|
4
|
+
// environment guard. Nothing here stores anything (no AsyncStorage) and nothing
|
|
5
|
+
// here logs outside development builds.
|
|
6
|
+
import NativePinVault, { type GuardRequest } from './NativePinVault';
|
|
7
|
+
import type {
|
|
8
|
+
AttestationStatus,
|
|
9
|
+
AttestationTokenResult,
|
|
10
|
+
ClientCertEnrollmentResult,
|
|
11
|
+
ContentEncoding,
|
|
12
|
+
EnvironmentGuard,
|
|
13
|
+
GuardedOperation,
|
|
14
|
+
InitResult,
|
|
15
|
+
KeySecurityLevel,
|
|
16
|
+
PinVaultConfig,
|
|
17
|
+
PinVaultConnectionEvent,
|
|
18
|
+
PinVaultRequestInit,
|
|
19
|
+
PinVaultResponse,
|
|
20
|
+
SigningStatus,
|
|
21
|
+
UpdateResult,
|
|
22
|
+
VaultFileResult,
|
|
23
|
+
VaultFileStatus,
|
|
24
|
+
VaultFileUnlockPrompt,
|
|
25
|
+
VaultFileUnlockResult,
|
|
26
|
+
} from './types';
|
|
27
|
+
|
|
28
|
+
export * from './types';
|
|
29
|
+
|
|
30
|
+
/** Error codes of rejected promises. */
|
|
31
|
+
export type PinVaultErrorCode =
|
|
32
|
+
/** The config (or a request) was refused by the strict native parser or a builder rule. */
|
|
33
|
+
| 'E_INVALID_CONFIG'
|
|
34
|
+
| 'E_INVALID_ARGUMENT'
|
|
35
|
+
/** An operation that needs `start()` first. */
|
|
36
|
+
| 'E_NOT_STARTED'
|
|
37
|
+
/** The pinned request failed: pin mismatch, TLS, network, timeout, size limit. */
|
|
38
|
+
| 'E_FETCH'
|
|
39
|
+
| 'E_NO_ACTIVITY'
|
|
40
|
+
| 'E_NATIVE'
|
|
41
|
+
/** `requirePinnedReactNativeNetworking`: React Native's own networking is not pinned. */
|
|
42
|
+
| 'E_NETWORKING_NOT_PINNED';
|
|
43
|
+
|
|
44
|
+
/** A rejected PinVault call. `exception` is the native exception (class name + message). */
|
|
45
|
+
export class PinVaultError extends Error {
|
|
46
|
+
readonly code: PinVaultErrorCode;
|
|
47
|
+
readonly exception: { name: string; message: string | null } | null;
|
|
48
|
+
|
|
49
|
+
constructor(code: PinVaultErrorCode, message: string, exception: PinVaultError['exception'] = null) {
|
|
50
|
+
super(message);
|
|
51
|
+
this.name = 'PinVaultError';
|
|
52
|
+
this.code = code;
|
|
53
|
+
this.exception = exception;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const KNOWN_CODES: ReadonlySet<string> = new Set([
|
|
58
|
+
'E_INVALID_CONFIG',
|
|
59
|
+
'E_INVALID_ARGUMENT',
|
|
60
|
+
'E_NOT_STARTED',
|
|
61
|
+
'E_FETCH',
|
|
62
|
+
'E_NO_ACTIVITY',
|
|
63
|
+
'E_NATIVE',
|
|
64
|
+
'E_NETWORKING_NOT_PINNED',
|
|
65
|
+
]);
|
|
66
|
+
|
|
67
|
+
/** Native rejections → PinVaultError (keeps the native code, message and exception). */
|
|
68
|
+
function toPinVaultError(error: unknown): PinVaultError {
|
|
69
|
+
if (error instanceof PinVaultError) return error;
|
|
70
|
+
const e = (error ?? {}) as { code?: unknown; message?: unknown; userInfo?: unknown };
|
|
71
|
+
const code = typeof e.code === 'string' && KNOWN_CODES.has(e.code) ? (e.code as PinVaultErrorCode) : 'E_NATIVE';
|
|
72
|
+
const message = typeof e.message === 'string' ? e.message : 'PinVault native call failed';
|
|
73
|
+
const info = (e.userInfo ?? null) as { exceptionName?: unknown; exceptionMessage?: unknown } | null;
|
|
74
|
+
const exception =
|
|
75
|
+
info && typeof info.exceptionName === 'string'
|
|
76
|
+
? {
|
|
77
|
+
name: info.exceptionName,
|
|
78
|
+
message: typeof info.exceptionMessage === 'string' ? info.exceptionMessage : null,
|
|
79
|
+
}
|
|
80
|
+
: null;
|
|
81
|
+
return new PinVaultError(code, message, exception);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async function call<T>(promise: Promise<unknown>): Promise<T> {
|
|
85
|
+
try {
|
|
86
|
+
return (await promise) as T;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw toPinVaultError(error);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── Strict JSON ─────────────────────────────────────────────────────────────
|
|
93
|
+
// JSON.stringify silently drops functions and turns NaN into null; the native
|
|
94
|
+
// parser could then not refuse them. Anything that is not plain data is refused here.
|
|
95
|
+
|
|
96
|
+
/** The native parsers' nesting limit (the root object is level 0). */
|
|
97
|
+
const MAX_DEPTH = 8;
|
|
98
|
+
|
|
99
|
+
function assertPlainData(value: unknown, path: string, code: PinVaultErrorCode, depth = 0): void {
|
|
100
|
+
// Before recursing further: a deep structure would otherwise overflow the JS stack.
|
|
101
|
+
if (depth > MAX_DEPTH) throw new PinVaultError(code, `${path}: nested too deeply`);
|
|
102
|
+
if (value === null) return;
|
|
103
|
+
switch (typeof value) {
|
|
104
|
+
case 'string':
|
|
105
|
+
case 'boolean':
|
|
106
|
+
return;
|
|
107
|
+
case 'number':
|
|
108
|
+
if (!Number.isFinite(value)) throw new PinVaultError(code, `${path}: not a finite number`);
|
|
109
|
+
return;
|
|
110
|
+
case 'object': {
|
|
111
|
+
if (Array.isArray(value)) {
|
|
112
|
+
value.forEach((item, i) => {
|
|
113
|
+
if (item === undefined) throw new PinVaultError(code, `${path}[${i}]: undefined in a list`);
|
|
114
|
+
assertPlainData(item, `${path}[${i}]`, code, depth + 1);
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const proto = Object.getPrototypeOf(value);
|
|
119
|
+
if (proto !== Object.prototype && proto !== null) {
|
|
120
|
+
throw new PinVaultError(code, `${path}: must be a plain object`);
|
|
121
|
+
}
|
|
122
|
+
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
|
|
123
|
+
if (v === undefined) continue; // an optional field left out
|
|
124
|
+
assertPlainData(v, `${path}.${k}`, code, depth + 1);
|
|
125
|
+
}
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
default:
|
|
129
|
+
throw new PinVaultError(code, `${path}: ${typeof value} is not allowed`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function strictJson(value: unknown, path: string, code: PinVaultErrorCode): string {
|
|
134
|
+
assertPlainData(value, path, code);
|
|
135
|
+
return JSON.stringify(value);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ── Environment guard ───────────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
export const DEFAULT_GUARD_TIMEOUT_MS = 5000;
|
|
141
|
+
|
|
142
|
+
let guardSubscription: { remove(): void } | null = null;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Runs the app's guard with a timeout. Anything but a `true` within the time —
|
|
146
|
+
* `false`, another value, a thrown error, a rejected promise, no answer — is a refusal.
|
|
147
|
+
*/
|
|
148
|
+
export async function evaluateGuard(
|
|
149
|
+
guard: EnvironmentGuard,
|
|
150
|
+
operation: GuardedOperation,
|
|
151
|
+
timeoutMs: number,
|
|
152
|
+
): Promise<boolean> {
|
|
153
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
154
|
+
try {
|
|
155
|
+
const verdict = await Promise.race<unknown>([
|
|
156
|
+
Promise.resolve().then(() => guard(operation)),
|
|
157
|
+
new Promise<boolean>((resolve) => {
|
|
158
|
+
timer = setTimeout(() => resolve(false), timeoutMs);
|
|
159
|
+
}),
|
|
160
|
+
]);
|
|
161
|
+
return verdict === true;
|
|
162
|
+
} catch (error) {
|
|
163
|
+
if (__DEV__) console.warn(`PinVault: environmentGuard threw for ${operation}; refused`, error);
|
|
164
|
+
return false;
|
|
165
|
+
} finally {
|
|
166
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const GUARDED_OPERATIONS: ReadonlySet<string> = new Set(['INIT', 'ENROLL', 'FETCH_FILE', 'UNLOCK_FILE']);
|
|
171
|
+
|
|
172
|
+
function installGuard(guard: EnvironmentGuard | undefined, timeoutMs: number): void {
|
|
173
|
+
guardSubscription?.remove();
|
|
174
|
+
guardSubscription = null;
|
|
175
|
+
if (!guard) return;
|
|
176
|
+
// The JS answer must arrive before the native deadline; leave it a little room.
|
|
177
|
+
const jsTimeout = Math.max(50, timeoutMs - 100);
|
|
178
|
+
guardSubscription = NativePinVault.onGuardRequest((request: GuardRequest) => {
|
|
179
|
+
const operation = request.operation;
|
|
180
|
+
if (!GUARDED_OPERATIONS.has(operation)) {
|
|
181
|
+
NativePinVault.answerGuard(request.requestId, false);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
void evaluateGuard(guard, operation as GuardedOperation, jsTimeout).then((allowed) =>
|
|
185
|
+
NativePinVault.answerGuard(request.requestId, allowed),
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ── Start / config ──────────────────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Starts PinVault (`PinVault.init(context, config)` / `PinVault.shared.start(config:)`).
|
|
194
|
+
* Fail closed: until it returns `{ type: 'ready' }`, every pinned request is refused.
|
|
195
|
+
* An invalid config rejects with `E_INVALID_CONFIG` (unknown keys and wrong types included).
|
|
196
|
+
*/
|
|
197
|
+
export async function start(config: PinVaultConfig): Promise<InitResult> {
|
|
198
|
+
if (config === null || typeof config !== 'object' || Array.isArray(config)) {
|
|
199
|
+
throw new PinVaultError('E_INVALID_CONFIG', 'config: must be an object');
|
|
200
|
+
}
|
|
201
|
+
const { environmentGuard, environmentGuardTimeoutMs, ...rest } = config;
|
|
202
|
+
if (environmentGuard !== undefined && typeof environmentGuard !== 'function') {
|
|
203
|
+
throw new PinVaultError('E_INVALID_CONFIG', 'config.environmentGuard: must be a function');
|
|
204
|
+
}
|
|
205
|
+
const timeoutMs = environmentGuardTimeoutMs ?? DEFAULT_GUARD_TIMEOUT_MS;
|
|
206
|
+
if (
|
|
207
|
+
typeof timeoutMs !== 'number' ||
|
|
208
|
+
!Number.isInteger(timeoutMs) ||
|
|
209
|
+
timeoutMs < 100 ||
|
|
210
|
+
timeoutMs > 30000
|
|
211
|
+
) {
|
|
212
|
+
throw new PinVaultError('E_INVALID_CONFIG', 'config.environmentGuardTimeoutMs: an integer between 100 and 30000');
|
|
213
|
+
}
|
|
214
|
+
const nativeConfig: Record<string, unknown> = { ...rest };
|
|
215
|
+
if (environmentGuard) nativeConfig.environmentGuard = { timeoutMs };
|
|
216
|
+
const json = strictJson(nativeConfig, 'config', 'E_INVALID_CONFIG');
|
|
217
|
+
installGuard(environmentGuard, timeoutMs);
|
|
218
|
+
return call<InitResult>(NativePinVault.start(json));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export const updateNow = (): Promise<UpdateResult> => call(NativePinVault.updateNow());
|
|
222
|
+
export const currentVersion = (): Promise<number> => call(NativePinVault.currentVersion());
|
|
223
|
+
export const hostPinVersions = (): Promise<Record<string, number>> => call(NativePinVault.hostPinVersions());
|
|
224
|
+
|
|
225
|
+
/** Active pins of a host (Base64, no `sha256/`), or null when the host has no entry. */
|
|
226
|
+
export async function pinsForHost(hostname: string): Promise<string[] | null> {
|
|
227
|
+
const result = await call<{ pins: string[] | null }>(NativePinVault.pinsForHost(hostname));
|
|
228
|
+
return result.pins;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export const signingStatus = (configApiId?: string): Promise<SigningStatus | null> =>
|
|
232
|
+
call(NativePinVault.signingStatus(configApiId ?? null));
|
|
233
|
+
export const isForceUpdate = (): Promise<boolean> => call(NativePinVault.isForceUpdate());
|
|
234
|
+
/** Drops the active pins; the next `start` begins again (watermarks kept). */
|
|
235
|
+
export const reset = (): Promise<void> => call(NativePinVault.reset());
|
|
236
|
+
export const schedulePeriodicUpdates = (intervalHours?: number): Promise<boolean> =>
|
|
237
|
+
call(NativePinVault.schedulePeriodicUpdates(intervalHours ?? null));
|
|
238
|
+
export const cancelPeriodicUpdates = (): Promise<void> => call(NativePinVault.cancelPeriodicUpdates());
|
|
239
|
+
/** Library debug logs. Ignored (returns false) in release builds of the app. */
|
|
240
|
+
export const enableDebugLogging = (): Promise<boolean> => call(NativePinVault.enableDebugLogging());
|
|
241
|
+
|
|
242
|
+
// ── Pinned HTTP ─────────────────────────────────────────────────────────────
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* A request through the native pinned client (`PinVault.getClient()` /
|
|
246
|
+
* `PinVault.shared.session()`): pin check, attestation token, pin-mismatch
|
|
247
|
+
* recovery. HTTPS only; it never falls back to an unpinned path. A pin
|
|
248
|
+
* mismatch rejects with `E_FETCH` (`exception.name` e.g. `SSLPeerUnverifiedException`).
|
|
249
|
+
*/
|
|
250
|
+
export async function fetch(url: string, init: PinVaultRequestInit = {}): Promise<PinVaultResponse> {
|
|
251
|
+
if (typeof url !== 'string') throw new PinVaultError('E_INVALID_ARGUMENT', 'url: must be a string');
|
|
252
|
+
if (init === null || typeof init !== 'object' || Array.isArray(init)) {
|
|
253
|
+
throw new PinVaultError('E_INVALID_ARGUMENT', 'init: must be an object');
|
|
254
|
+
}
|
|
255
|
+
const json = strictJson({ ...init, url }, 'request', 'E_INVALID_ARGUMENT');
|
|
256
|
+
const response = await call<Omit<PinVaultResponse, 'ok'>>(NativePinVault.fetch(json));
|
|
257
|
+
return { ...response, ok: response.status >= 200 && response.status < 300 };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// ── Enrollment ──────────────────────────────────────────────────────────────
|
|
261
|
+
|
|
262
|
+
/** The device id PinVault sends (`ANDROID_ID` / `identifierForVendor`, lowercased). */
|
|
263
|
+
export const deviceId = (): Promise<string | null> => call(NativePinVault.deviceId());
|
|
264
|
+
|
|
265
|
+
/** Token or enrollment-code enrollment. The token goes to native memory for this call only. */
|
|
266
|
+
export const enrollForResult = (token: string, label?: string): Promise<ClientCertEnrollmentResult> =>
|
|
267
|
+
call(NativePinVault.enrollForResult(token, label ?? null));
|
|
268
|
+
export const autoEnrollForResult = (): Promise<ClientCertEnrollmentResult> =>
|
|
269
|
+
call(NativePinVault.autoEnrollForResult());
|
|
270
|
+
export const checkPendingEnrollment = (): Promise<ClientCertEnrollmentResult> =>
|
|
271
|
+
call(NativePinVault.checkPendingEnrollment());
|
|
272
|
+
export const isEnrolled = (label?: string): Promise<boolean> => call(NativePinVault.isEnrolled(label ?? null));
|
|
273
|
+
export const isEnrollmentPending = (label?: string): Promise<boolean> =>
|
|
274
|
+
call(NativePinVault.isEnrollmentPending(label ?? null));
|
|
275
|
+
export const enrollmentVerificationCode = (label?: string): Promise<string | null> =>
|
|
276
|
+
call(NativePinVault.enrollmentVerificationCode(label ?? null));
|
|
277
|
+
export const enrolledClientCN = (label?: string): Promise<string | null> =>
|
|
278
|
+
call(NativePinVault.enrolledClientCN(label ?? null));
|
|
279
|
+
export const enrolledClientNotAfter = (label?: string): Promise<number | null> =>
|
|
280
|
+
call(NativePinVault.enrolledClientNotAfter(label ?? null));
|
|
281
|
+
export const unenroll = (label?: string, options: { wipeVaultFiles?: boolean } = {}): Promise<void> =>
|
|
282
|
+
call(NativePinVault.unenroll(label ?? null, options.wipeVaultFiles === true));
|
|
283
|
+
export const identityKeySecurityLevel = (label?: string): Promise<KeySecurityLevel | null> =>
|
|
284
|
+
call(NativePinVault.identityKeySecurityLevel(label ?? null));
|
|
285
|
+
|
|
286
|
+
// ── Vault files ─────────────────────────────────────────────────────────────
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* The access token of a `TOKEN` / `TOKEN_MTLS` file. Kept in native memory only
|
|
290
|
+
* (never written to disk, gone when the process ends); `null` forgets it.
|
|
291
|
+
*/
|
|
292
|
+
export const setVaultToken = (key: string, token: string | null): Promise<void> =>
|
|
293
|
+
call(NativePinVault.setVaultToken(key, token));
|
|
294
|
+
/** Forgets every vault token (e.g. on a `REENROLL_REQUIRED` event). Returns how many there were. */
|
|
295
|
+
export const clearVaultTokens = (): Promise<number> => call(NativePinVault.clearVaultTokens());
|
|
296
|
+
|
|
297
|
+
/** Downloads and stores a file. `token` = `setVaultToken(key, token)` before the download. */
|
|
298
|
+
export const fetchFile = (key: string, options: { token?: string } = {}): Promise<VaultFileResult> =>
|
|
299
|
+
call(NativePinVault.fetchFile(key, options.token ?? null));
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* The stored copy (checked on every read), or null — `fileStatus` says why.
|
|
303
|
+
* Files behind the screen lock never open here: use `unlockFile`.
|
|
304
|
+
* Keep the content in memory only as long as you need it, and never log it.
|
|
305
|
+
*/
|
|
306
|
+
export const loadFile = (key: string, encoding: ContentEncoding = 'utf8'): Promise<string | null> =>
|
|
307
|
+
call(NativePinVault.loadFile(key, encoding));
|
|
308
|
+
export const fileStatus = (key: string): Promise<VaultFileStatus> => call(NativePinVault.fileStatus(key));
|
|
309
|
+
|
|
310
|
+
/** Shows the native screen-lock prompt (BiometricPrompt / LAContext), then returns the content. */
|
|
311
|
+
export const unlockFile = (
|
|
312
|
+
key: string,
|
|
313
|
+
prompt: VaultFileUnlockPrompt,
|
|
314
|
+
encoding: ContentEncoding = 'utf8',
|
|
315
|
+
): Promise<VaultFileUnlockResult> =>
|
|
316
|
+
call(NativePinVault.unlockFile(key, strictJson({ ...prompt, encoding }, 'prompt', 'E_INVALID_ARGUMENT')));
|
|
317
|
+
|
|
318
|
+
export const isFileLocked = (key: string): Promise<boolean> => call(NativePinVault.isFileLocked(key));
|
|
319
|
+
export const hasFile = (key: string): Promise<boolean> => call(NativePinVault.hasFile(key));
|
|
320
|
+
export const fileVersion = (key: string): Promise<number> => call(NativePinVault.fileVersion(key));
|
|
321
|
+
export const clearFile = (key: string): Promise<void> => call(NativePinVault.clearFile(key));
|
|
322
|
+
export const syncAllFiles = (): Promise<Record<string, VaultFileResult>> => call(NativePinVault.syncAllFiles());
|
|
323
|
+
|
|
324
|
+
// ── Attestation ─────────────────────────────────────────────────────────────
|
|
325
|
+
|
|
326
|
+
export const attestNow = (configApiId?: string): Promise<AttestationStatus> =>
|
|
327
|
+
call(NativePinVault.attestNow(configApiId ?? null));
|
|
328
|
+
/** `PinVault-Token` for your own client (a bearer credential: never log or store it). */
|
|
329
|
+
export const fetchAttestationToken = (host?: string): Promise<AttestationTokenResult> =>
|
|
330
|
+
call(NativePinVault.fetchAttestationToken(host ?? null));
|
|
331
|
+
export const attestationStatus = (configApiId?: string): Promise<AttestationStatus> =>
|
|
332
|
+
call(NativePinVault.attestationStatus(configApiId ?? null));
|
|
333
|
+
export const attestationHeaderName = (): string => 'PinVault-Token';
|
|
334
|
+
|
|
335
|
+
// ── Events ──────────────────────────────────────────────────────────────────
|
|
336
|
+
|
|
337
|
+
/** Connection telemetry (`onConnectionEvent`). Returns a subscription; call `remove()` when done. */
|
|
338
|
+
export function addConnectionListener(listener: (event: PinVaultConnectionEvent) => void): { remove(): void } {
|
|
339
|
+
return NativePinVault.onConnectionEvent((event) => listener(event as PinVaultConnectionEvent));
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** Everything above as one object, like `PinVault.shared` / the Kotlin `PinVault` object. */
|
|
343
|
+
const PinVault = {
|
|
344
|
+
start,
|
|
345
|
+
updateNow,
|
|
346
|
+
currentVersion,
|
|
347
|
+
hostPinVersions,
|
|
348
|
+
pinsForHost,
|
|
349
|
+
signingStatus,
|
|
350
|
+
isForceUpdate,
|
|
351
|
+
reset,
|
|
352
|
+
schedulePeriodicUpdates,
|
|
353
|
+
cancelPeriodicUpdates,
|
|
354
|
+
enableDebugLogging,
|
|
355
|
+
fetch,
|
|
356
|
+
deviceId,
|
|
357
|
+
enrollForResult,
|
|
358
|
+
autoEnrollForResult,
|
|
359
|
+
checkPendingEnrollment,
|
|
360
|
+
isEnrolled,
|
|
361
|
+
isEnrollmentPending,
|
|
362
|
+
enrollmentVerificationCode,
|
|
363
|
+
enrolledClientCN,
|
|
364
|
+
enrolledClientNotAfter,
|
|
365
|
+
unenroll,
|
|
366
|
+
identityKeySecurityLevel,
|
|
367
|
+
setVaultToken,
|
|
368
|
+
clearVaultTokens,
|
|
369
|
+
fetchFile,
|
|
370
|
+
loadFile,
|
|
371
|
+
fileStatus,
|
|
372
|
+
unlockFile,
|
|
373
|
+
isFileLocked,
|
|
374
|
+
hasFile,
|
|
375
|
+
fileVersion,
|
|
376
|
+
clearFile,
|
|
377
|
+
syncAllFiles,
|
|
378
|
+
attestNow,
|
|
379
|
+
fetchAttestationToken,
|
|
380
|
+
attestationStatus,
|
|
381
|
+
attestationHeaderName,
|
|
382
|
+
addConnectionListener,
|
|
383
|
+
} as const;
|
|
384
|
+
|
|
385
|
+
export default PinVault;
|