@oxyhq/services 5.17.7 → 5.17.9

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 (119) hide show
  1. package/lib/commonjs/crypto/index.js +0 -23
  2. package/lib/commonjs/crypto/index.js.map +1 -1
  3. package/lib/commonjs/index.js +0 -15
  4. package/lib/commonjs/index.js.map +1 -1
  5. package/lib/commonjs/ui/components/Icon.js.map +1 -1
  6. package/lib/commonjs/ui/components/IconButton/utils.js.map +1 -1
  7. package/lib/commonjs/ui/components/TextField/Adornment/utils.js.map +1 -1
  8. package/lib/commonjs/ui/components/TextField/helpers.js.map +1 -1
  9. package/lib/commonjs/ui/components/TouchableRipple/utils.js.map +1 -1
  10. package/lib/commonjs/ui/components/Typography/AnimatedText.js.map +1 -1
  11. package/lib/commonjs/ui/context/OxyContext.js +37 -589
  12. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  13. package/lib/commonjs/ui/context/OxyContextBase.js.map +1 -1
  14. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +60 -425
  15. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  16. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +8 -112
  17. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  18. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +2 -27
  19. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
  20. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js +2 -27
  21. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -1
  22. package/lib/commonjs/ui/hooks/useSessionSocket.js +2 -88
  23. package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
  24. package/lib/commonjs/ui/screens/OxyAuthScreen.js +0 -1
  25. package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
  26. package/lib/commonjs/ui/stores/authStore.js +52 -15
  27. package/lib/commonjs/ui/stores/authStore.js.map +1 -1
  28. package/lib/commonjs/ui/utils/avatarUtils.js +2 -32
  29. package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
  30. package/lib/module/crypto/index.js +4 -6
  31. package/lib/module/crypto/index.js.map +1 -1
  32. package/lib/module/index.js +6 -3
  33. package/lib/module/index.js.map +1 -1
  34. package/lib/module/ui/components/Icon.js.map +1 -1
  35. package/lib/module/ui/components/IconButton/utils.js.map +1 -1
  36. package/lib/module/ui/components/TextField/Adornment/utils.js.map +1 -1
  37. package/lib/module/ui/components/TextField/helpers.js.map +1 -1
  38. package/lib/module/ui/components/TouchableRipple/utils.js.map +1 -1
  39. package/lib/module/ui/components/Typography/AnimatedText.js.map +1 -1
  40. package/lib/module/ui/context/OxyContext.js +35 -588
  41. package/lib/module/ui/context/OxyContext.js.map +1 -1
  42. package/lib/module/ui/context/OxyContextBase.js.map +1 -1
  43. package/lib/module/ui/context/hooks/useAuthOperations.js +60 -424
  44. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  45. package/lib/module/ui/hooks/mutations/useAccountMutations.js +8 -112
  46. package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  47. package/lib/module/ui/hooks/queries/useAccountQueries.js +2 -27
  48. package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
  49. package/lib/module/ui/hooks/queries/useServicesQueries.js +2 -27
  50. package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -1
  51. package/lib/module/ui/hooks/useSessionSocket.js +2 -88
  52. package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
  53. package/lib/module/ui/screens/OxyAuthScreen.js +0 -1
  54. package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
  55. package/lib/module/ui/stores/authStore.js +52 -15
  56. package/lib/module/ui/stores/authStore.js.map +1 -1
  57. package/lib/module/ui/utils/avatarUtils.js +2 -32
  58. package/lib/module/ui/utils/avatarUtils.js.map +1 -1
  59. package/lib/typescript/crypto/index.d.ts +2 -5
  60. package/lib/typescript/crypto/index.d.ts.map +1 -1
  61. package/lib/typescript/crypto/types.d.ts +6 -2
  62. package/lib/typescript/crypto/types.d.ts.map +1 -1
  63. package/lib/typescript/index.d.ts +4 -2
  64. package/lib/typescript/index.d.ts.map +1 -1
  65. package/lib/typescript/ui/components/IconButton/utils.d.ts +1 -1
  66. package/lib/typescript/ui/components/TextField/Adornment/utils.d.ts +1 -1
  67. package/lib/typescript/ui/components/TextField/Adornment/utils.d.ts.map +1 -1
  68. package/lib/typescript/ui/components/TextField/helpers.d.ts +6 -6
  69. package/lib/typescript/ui/components/types.d.ts +0 -4
  70. package/lib/typescript/ui/components/types.d.ts.map +1 -1
  71. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  72. package/lib/typescript/ui/context/OxyContextBase.d.ts +2 -39
  73. package/lib/typescript/ui/context/OxyContextBase.d.ts.map +1 -1
  74. package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts +10 -25
  75. package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  76. package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  77. package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  78. package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
  79. package/lib/typescript/ui/hooks/useSessionSocket.d.ts +1 -14
  80. package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
  81. package/lib/typescript/ui/stores/authStore.d.ts +27 -4
  82. package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
  83. package/lib/typescript/ui/utils/avatarUtils.d.ts +0 -2
  84. package/lib/typescript/ui/utils/avatarUtils.d.ts.map +1 -1
  85. package/package.json +2 -2
  86. package/src/crypto/index.ts +3 -11
  87. package/src/crypto/types.ts +6 -2
  88. package/src/index.ts +6 -11
  89. package/src/ui/components/Icon.tsx +1 -1
  90. package/src/ui/components/IconButton/utils.ts +1 -1
  91. package/src/ui/components/TextField/Adornment/utils.ts +2 -2
  92. package/src/ui/components/TextField/helpers.tsx +8 -8
  93. package/src/ui/components/TouchableRipple/utils.ts +2 -2
  94. package/src/ui/components/Typography/AnimatedText.tsx +2 -2
  95. package/src/ui/components/types.tsx +0 -6
  96. package/src/ui/context/OxyContext.tsx +33 -637
  97. package/src/ui/context/OxyContextBase.tsx +5 -23
  98. package/src/ui/context/hooks/useAuthOperations.ts +84 -460
  99. package/src/ui/hooks/mutations/useAccountMutations.ts +12 -110
  100. package/src/ui/hooks/queries/useAccountQueries.ts +3 -27
  101. package/src/ui/hooks/queries/useServicesQueries.ts +3 -27
  102. package/src/ui/hooks/useSessionSocket.ts +2 -106
  103. package/src/ui/screens/OxyAuthScreen.tsx +1 -1
  104. package/src/ui/stores/authStore.ts +57 -18
  105. package/src/ui/utils/avatarUtils.ts +4 -36
  106. package/lib/commonjs/crypto/keyManager.js +0 -511
  107. package/lib/commonjs/crypto/keyManager.js.map +0 -1
  108. package/lib/commonjs/crypto/signatureService.js +0 -269
  109. package/lib/commonjs/crypto/signatureService.js.map +0 -1
  110. package/lib/module/crypto/keyManager.js +0 -508
  111. package/lib/module/crypto/keyManager.js.map +0 -1
  112. package/lib/module/crypto/signatureService.js +0 -266
  113. package/lib/module/crypto/signatureService.js.map +0 -1
  114. package/lib/typescript/crypto/keyManager.d.ts +0 -97
  115. package/lib/typescript/crypto/keyManager.d.ts.map +0 -1
  116. package/lib/typescript/crypto/signatureService.d.ts +0 -77
  117. package/lib/typescript/crypto/signatureService.d.ts.map +0 -1
  118. package/src/crypto/keyManager.ts +0 -545
  119. package/src/crypto/signatureService.ts +0 -301
@@ -1,545 +0,0 @@
1
- /**
2
- * Key Manager - ECDSA secp256k1 Key Generation and Storage
3
- *
4
- * Handles secure generation, storage, and retrieval of cryptographic keys.
5
- * Private keys are stored securely using expo-secure-store and never leave the device.
6
- */
7
-
8
- import { ec as EC } from 'elliptic';
9
- import type { ECKeyPair } from 'elliptic';
10
- import { Platform } from 'react-native';
11
-
12
- // Lazy imports for React Native specific modules
13
- let SecureStore: typeof import('expo-secure-store') | null = null;
14
- let ExpoCrypto: typeof import('expo-crypto') | null = null;
15
-
16
- const ec = new EC('secp256k1');
17
-
18
- const STORAGE_KEYS = {
19
- PRIVATE_KEY: 'oxy_identity_private_key',
20
- PUBLIC_KEY: 'oxy_identity_public_key',
21
- BACKUP_PRIVATE_KEY: 'oxy_identity_backup_private_key',
22
- BACKUP_PUBLIC_KEY: 'oxy_identity_backup_public_key',
23
- BACKUP_TIMESTAMP: 'oxy_identity_backup_timestamp',
24
- } as const;
25
-
26
- /**
27
- * Initialize React Native specific modules
28
- * This allows the module to work in both Node.js and React Native environments
29
- */
30
- async function initSecureStore(): Promise<typeof import('expo-secure-store')> {
31
- if (!SecureStore) {
32
- try {
33
- SecureStore = await import('expo-secure-store');
34
- } catch (error) {
35
- const errorMessage = error instanceof Error ? error.message : String(error);
36
- throw new Error(`Failed to load expo-secure-store: ${errorMessage}. Make sure expo-secure-store is installed and properly configured.`);
37
- }
38
- }
39
- if (!SecureStore) {
40
- throw new Error('expo-secure-store module is not available');
41
- }
42
- return SecureStore;
43
- }
44
-
45
- /**
46
- * Check if we're in a React Native environment
47
- */
48
- function isReactNative(): boolean {
49
- return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
50
- }
51
-
52
- /**
53
- * Check if we're in a Node.js environment
54
- */
55
- function isNodeJS(): boolean {
56
- return typeof process !== 'undefined' && process.versions != null && process.versions.node != null;
57
- }
58
-
59
- /**
60
- * Check if we're on web platform
61
- * Identity storage is only available on native platforms (iOS/Android)
62
- */
63
- function isWebPlatform(): boolean {
64
- try {
65
- return Platform.OS === 'web';
66
- } catch {
67
- // Fallback if Platform is not available
68
- return typeof window !== 'undefined' && typeof navigator !== 'undefined' && navigator.product !== 'ReactNative';
69
- }
70
- }
71
-
72
- async function initExpoCrypto(): Promise<typeof import('expo-crypto')> {
73
- if (!ExpoCrypto) {
74
- ExpoCrypto = await import('expo-crypto');
75
- }
76
- return ExpoCrypto;
77
- }
78
-
79
- /**
80
- * Convert Uint8Array to hexadecimal string
81
- * Works in both Node.js and React Native
82
- */
83
- function uint8ArrayToHex(bytes: Uint8Array): string {
84
- return Array.from(bytes)
85
- .map(b => b.toString(16).padStart(2, '0'))
86
- .join('');
87
- }
88
-
89
- /**
90
- * Generate cryptographically secure random bytes
91
- */
92
- async function getSecureRandomBytes(length: number): Promise<Uint8Array> {
93
- // In React Native, always use expo-crypto
94
- if (isReactNative() || !isNodeJS()) {
95
- const Crypto = await initExpoCrypto();
96
- return Crypto.getRandomBytes(length);
97
- }
98
-
99
- // In Node.js, use Node's crypto module
100
- // Use Function constructor to prevent Metro bundler from statically analyzing this require
101
- // This ensures the require is only evaluated in Node.js runtime, not during Metro bundling
102
- try {
103
- // eslint-disable-next-line @typescript-eslint/no-implied-eval
104
- const getCrypto = new Function('return require("crypto")');
105
- const crypto = getCrypto();
106
- return new Uint8Array(crypto.randomBytes(length));
107
- } catch (error) {
108
- // Fallback to expo-crypto if Node crypto fails
109
- const Crypto = await initExpoCrypto();
110
- return Crypto.getRandomBytes(length);
111
- }
112
- }
113
-
114
- export interface KeyPair {
115
- publicKey: string;
116
- privateKey: string;
117
- }
118
-
119
- export class KeyManager {
120
- // In-memory cache for identity state (invalidated on identity changes)
121
- private static cachedPublicKey: string | null = null;
122
- private static cachedHasIdentity: boolean | null = null;
123
-
124
- /**
125
- * Invalidate cached identity state
126
- * Called internally when identity is created/deleted/imported
127
- */
128
- private static invalidateCache(): void {
129
- KeyManager.cachedPublicKey = null;
130
- KeyManager.cachedHasIdentity = null;
131
- }
132
-
133
- /**
134
- * Generate a new ECDSA secp256k1 key pair
135
- * Returns the keys in hexadecimal format
136
- */
137
- static generateKeyPairSync(): KeyPair {
138
- const keyPair = ec.genKeyPair();
139
- return {
140
- privateKey: keyPair.getPrivate('hex'),
141
- publicKey: keyPair.getPublic('hex'),
142
- };
143
- }
144
-
145
- /**
146
- * Generate a new key pair using secure random bytes
147
- */
148
- static async generateKeyPair(): Promise<KeyPair> {
149
- const randomBytes = await getSecureRandomBytes(32);
150
- const privateKeyHex = uint8ArrayToHex(randomBytes);
151
- const keyPair = ec.keyFromPrivate(privateKeyHex);
152
-
153
- return {
154
- privateKey: keyPair.getPrivate('hex'),
155
- publicKey: keyPair.getPublic('hex'),
156
- };
157
- }
158
-
159
- /**
160
- * Generate and securely store a new key pair on the device
161
- * Returns only the public key (private key is stored securely)
162
- */
163
- static async createIdentity(): Promise<string> {
164
- if (isWebPlatform()) {
165
- throw new Error('Identity creation is only available on native platforms (iOS/Android). Please use the native app to create your identity.');
166
- }
167
- const store = await initSecureStore();
168
- const { privateKey, publicKey } = await KeyManager.generateKeyPair();
169
-
170
- await store.setItemAsync(STORAGE_KEYS.PRIVATE_KEY, privateKey, {
171
- keychainAccessible: store.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
172
- });
173
-
174
- await store.setItemAsync(STORAGE_KEYS.PUBLIC_KEY, publicKey);
175
-
176
- // Update cache
177
- KeyManager.cachedPublicKey = publicKey;
178
- KeyManager.cachedHasIdentity = true;
179
-
180
- return publicKey;
181
- }
182
-
183
- /**
184
- * Import an existing key pair (e.g., from backup file)
185
- */
186
- static async importKeyPair(privateKey: string): Promise<string> {
187
- if (isWebPlatform()) {
188
- throw new Error('Identity import is only available on native platforms (iOS/Android). Please use the native app to import your identity.');
189
- }
190
- const store = await initSecureStore();
191
-
192
- const keyPair = ec.keyFromPrivate(privateKey);
193
- const publicKey = keyPair.getPublic('hex');
194
-
195
- await store.setItemAsync(STORAGE_KEYS.PRIVATE_KEY, privateKey, {
196
- keychainAccessible: store.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
197
- });
198
- await store.setItemAsync(STORAGE_KEYS.PUBLIC_KEY, publicKey);
199
-
200
- // Update cache
201
- KeyManager.cachedPublicKey = publicKey;
202
- KeyManager.cachedHasIdentity = true;
203
-
204
- return publicKey;
205
- }
206
-
207
- /**
208
- * Get the stored private key
209
- * WARNING: Only use this for signing operations within the app
210
- */
211
- static async getPrivateKey(): Promise<string | null> {
212
- if (isWebPlatform()) {
213
- return null; // Identity storage is only available on native platforms
214
- }
215
- try {
216
- const store = await initSecureStore();
217
- return await store.getItemAsync(STORAGE_KEYS.PRIVATE_KEY);
218
- } catch (error) {
219
- // If secure store is not available, return null (no identity)
220
- // This allows the app to continue functioning even if secure store fails to load
221
- if (__DEV__) {
222
- console.warn('[KeyManager] Failed to access secure store:', error);
223
- }
224
- return null;
225
- }
226
- }
227
-
228
- /**
229
- * Get the stored public key (cached for performance)
230
- */
231
- static async getPublicKey(): Promise<string | null> {
232
- if (isWebPlatform()) {
233
- return null; // Identity storage is only available on native platforms
234
- }
235
- if (KeyManager.cachedPublicKey !== null) {
236
- return KeyManager.cachedPublicKey;
237
- }
238
-
239
- try {
240
- const store = await initSecureStore();
241
- const publicKey = await store.getItemAsync(STORAGE_KEYS.PUBLIC_KEY);
242
-
243
- // Cache result (null is a valid cache value meaning no identity)
244
- KeyManager.cachedPublicKey = publicKey;
245
-
246
- return publicKey;
247
- } catch (error) {
248
- // If secure store is not available, return null (no identity)
249
- // Cache null to avoid repeated failed attempts
250
- KeyManager.cachedPublicKey = null;
251
- if (__DEV__) {
252
- console.warn('[KeyManager] Failed to access secure store:', error);
253
- }
254
- return null;
255
- }
256
- }
257
-
258
- /**
259
- * Check if an identity (key pair) exists on this device (cached for performance)
260
- */
261
- static async hasIdentity(): Promise<boolean> {
262
- if (isWebPlatform()) {
263
- return false; // Identity storage is only available on native platforms
264
- }
265
- if (KeyManager.cachedHasIdentity !== null) {
266
- return KeyManager.cachedHasIdentity;
267
- }
268
-
269
- try {
270
- const privateKey = await KeyManager.getPrivateKey();
271
- const hasIdentity = privateKey !== null;
272
-
273
- // Cache result
274
- KeyManager.cachedHasIdentity = hasIdentity;
275
-
276
- return hasIdentity;
277
- } catch (error) {
278
- // If we can't check, assume no identity (safer default)
279
- // Cache false to avoid repeated failed attempts
280
- KeyManager.cachedHasIdentity = false;
281
- if (__DEV__) {
282
- console.warn('[KeyManager] Failed to check identity:', error);
283
- }
284
- return false;
285
- }
286
- }
287
-
288
- /**
289
- * Delete the stored identity (both keys)
290
- * Use with EXTREME caution - this is irreversible without a backup file
291
- * This should ONLY be called when explicitly requested by the user
292
- * @param skipBackup - If true, skip backup before deletion (default: false)
293
- * @param force - If true, skip confirmation checks (default: false)
294
- * @param userConfirmed - If true, user has explicitly confirmed deletion (default: false)
295
- */
296
- static async deleteIdentity(
297
- skipBackup: boolean = false,
298
- force: boolean = false,
299
- userConfirmed: boolean = false
300
- ): Promise<void> {
301
- if (isWebPlatform()) {
302
- return; // Identity storage is only available on native platforms, nothing to delete
303
- }
304
- // CRITICAL SAFEGUARD: Require explicit user confirmation unless force is true
305
- if (!force && !userConfirmed) {
306
- throw new Error('Identity deletion requires explicit user confirmation. This is a safety measure to prevent accidental data loss.');
307
- }
308
-
309
- if (!force) {
310
- const hasIdentity = await KeyManager.hasIdentity();
311
- if (!hasIdentity) {
312
- return; // Nothing to delete
313
- }
314
- }
315
-
316
- const store = await initSecureStore();
317
-
318
- // ALWAYS create backup before deletion unless explicitly skipped
319
- if (!skipBackup) {
320
- try {
321
- const backupSuccess = await KeyManager.backupIdentity();
322
- if (!backupSuccess && typeof __DEV__ !== 'undefined' && __DEV__) {
323
- console.warn('[KeyManager] Failed to backup identity before deletion - proceeding anyway');
324
- }
325
- } catch (backupError) {
326
- if (typeof __DEV__ !== 'undefined' && __DEV__) {
327
- console.warn('[KeyManager] Failed to backup identity before deletion:', backupError);
328
- }
329
- }
330
- }
331
-
332
- await store.deleteItemAsync(STORAGE_KEYS.PRIVATE_KEY);
333
- await store.deleteItemAsync(STORAGE_KEYS.PUBLIC_KEY);
334
-
335
- // Invalidate cache
336
- KeyManager.invalidateCache();
337
-
338
- // Also clear backup if force deletion
339
- if (force) {
340
- try {
341
- await store.deleteItemAsync(STORAGE_KEYS.BACKUP_PRIVATE_KEY);
342
- await store.deleteItemAsync(STORAGE_KEYS.BACKUP_PUBLIC_KEY);
343
- await store.deleteItemAsync(STORAGE_KEYS.BACKUP_TIMESTAMP);
344
- } catch (error) {
345
- // Ignore backup deletion errors
346
- }
347
- }
348
- }
349
-
350
- /**
351
- * Backup identity to SecureStore (separate backup storage)
352
- * This provides a recovery mechanism if primary storage fails
353
- */
354
- static async backupIdentity(): Promise<boolean> {
355
- if (isWebPlatform()) {
356
- return false; // Identity storage is only available on native platforms
357
- }
358
- try {
359
- const store = await initSecureStore();
360
- const privateKey = await KeyManager.getPrivateKey();
361
- const publicKey = await KeyManager.getPublicKey();
362
-
363
- if (!privateKey || !publicKey) {
364
- return false; // Nothing to backup
365
- }
366
-
367
- // Store backup in SecureStore (still secure, but separate from primary storage)
368
- await store.setItemAsync(STORAGE_KEYS.BACKUP_PRIVATE_KEY, privateKey, {
369
- keychainAccessible: store.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
370
- });
371
- await store.setItemAsync(STORAGE_KEYS.BACKUP_PUBLIC_KEY, publicKey);
372
- await store.setItemAsync(STORAGE_KEYS.BACKUP_TIMESTAMP, Date.now().toString());
373
-
374
- return true;
375
- } catch (error) {
376
- if (typeof __DEV__ !== 'undefined' && __DEV__) {
377
- console.error('[KeyManager] Failed to backup identity:', error);
378
- }
379
- return false;
380
- }
381
- }
382
-
383
- /**
384
- * Verify identity integrity - checks if keys are valid and accessible
385
- */
386
- static async verifyIdentityIntegrity(): Promise<boolean> {
387
- if (isWebPlatform()) {
388
- return false; // Identity storage is only available on native platforms
389
- }
390
- try {
391
- const privateKey = await KeyManager.getPrivateKey();
392
- const publicKey = await KeyManager.getPublicKey();
393
-
394
- if (!privateKey || !publicKey) {
395
- return false;
396
- }
397
-
398
- // Validate private key format
399
- if (!KeyManager.isValidPrivateKey(privateKey)) {
400
- return false;
401
- }
402
-
403
- // Validate public key format
404
- if (!KeyManager.isValidPublicKey(publicKey)) {
405
- return false;
406
- }
407
-
408
- // Verify public key can be derived from private key
409
- const derivedPublicKey = KeyManager.derivePublicKey(privateKey);
410
- if (derivedPublicKey !== publicKey) {
411
- return false; // Keys don't match
412
- }
413
-
414
- // Verify we can create a key pair object (tests elliptic curve operations)
415
- const keyPair = await KeyManager.getKeyPairObject();
416
- if (!keyPair) {
417
- return false;
418
- }
419
-
420
- return true;
421
- } catch (error) {
422
- if (typeof __DEV__ !== 'undefined' && __DEV__) {
423
- console.error('[KeyManager] Identity integrity check failed:', error);
424
- }
425
- return false;
426
- }
427
- }
428
-
429
- /**
430
- * Restore identity from backup if primary storage is corrupted
431
- */
432
- static async restoreIdentityFromBackup(): Promise<boolean> {
433
- if (isWebPlatform()) {
434
- return false; // Identity storage is only available on native platforms
435
- }
436
- try {
437
- const store = await initSecureStore();
438
-
439
- // Check if backup exists
440
- const backupPrivateKey = await store.getItemAsync(STORAGE_KEYS.BACKUP_PRIVATE_KEY);
441
- const backupPublicKey = await store.getItemAsync(STORAGE_KEYS.BACKUP_PUBLIC_KEY);
442
-
443
- if (!backupPrivateKey || !backupPublicKey) {
444
- return false; // No backup available
445
- }
446
-
447
- // Verify backup integrity
448
- if (!KeyManager.isValidPrivateKey(backupPrivateKey)) {
449
- return false;
450
- }
451
-
452
- if (!KeyManager.isValidPublicKey(backupPublicKey)) {
453
- return false;
454
- }
455
-
456
- // Verify keys match
457
- const derivedPublicKey = KeyManager.derivePublicKey(backupPrivateKey);
458
- if (derivedPublicKey !== backupPublicKey) {
459
- return false; // Backup keys don't match
460
- }
461
-
462
- await store.setItemAsync(STORAGE_KEYS.PRIVATE_KEY, backupPrivateKey, {
463
- keychainAccessible: store.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
464
- });
465
- await store.setItemAsync(STORAGE_KEYS.PUBLIC_KEY, backupPublicKey);
466
-
467
- const restored = await KeyManager.verifyIdentityIntegrity();
468
- if (restored) {
469
- // Update cache
470
- KeyManager.cachedPublicKey = backupPublicKey;
471
- KeyManager.cachedHasIdentity = true;
472
-
473
- await store.setItemAsync(STORAGE_KEYS.BACKUP_TIMESTAMP, Date.now().toString());
474
- return true;
475
- }
476
-
477
- return false;
478
- } catch (error) {
479
- if (typeof __DEV__ !== 'undefined' && __DEV__) {
480
- console.error('[KeyManager] Failed to restore identity from backup:', error);
481
- }
482
- return false;
483
- }
484
- }
485
-
486
- /**
487
- * Get the elliptic curve key object from the stored private key
488
- * Used internally for signing operations
489
- */
490
- static async getKeyPairObject(): Promise<ECKeyPair | null> {
491
- if (isWebPlatform()) {
492
- return null; // Identity storage is only available on native platforms
493
- }
494
- const privateKey = await KeyManager.getPrivateKey();
495
- if (!privateKey) return null;
496
- return ec.keyFromPrivate(privateKey);
497
- }
498
-
499
- /**
500
- * Derive public key from a private key (without storing)
501
- */
502
- static derivePublicKey(privateKey: string): string {
503
- const keyPair = ec.keyFromPrivate(privateKey);
504
- return keyPair.getPublic('hex');
505
- }
506
-
507
- /**
508
- * Validate that a string is a valid public key
509
- */
510
- static isValidPublicKey(publicKey: string): boolean {
511
- try {
512
- ec.keyFromPublic(publicKey, 'hex');
513
- return true;
514
- } catch {
515
- return false;
516
- }
517
- }
518
-
519
- /**
520
- * Validate that a string is a valid private key
521
- */
522
- static isValidPrivateKey(privateKey: string): boolean {
523
- try {
524
- const keyPair = ec.keyFromPrivate(privateKey);
525
- // Verify it can derive a public key
526
- keyPair.getPublic('hex');
527
- return true;
528
- } catch {
529
- return false;
530
- }
531
- }
532
-
533
- /**
534
- * Get a shortened version of the public key for display
535
- * Format: first 8 chars...last 8 chars
536
- */
537
- static shortenPublicKey(publicKey: string): string {
538
- if (publicKey.length <= 20) return publicKey;
539
- return `${publicKey.slice(0, 8)}...${publicKey.slice(-8)}`;
540
- }
541
- }
542
-
543
- export default KeyManager;
544
-
545
-