@ukeyfe/react-native-nfc-litecard 1.0.6 → 1.0.8

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/dist/writer.js DELETED
@@ -1,623 +0,0 @@
1
- "use strict";
2
- /**
3
- * NFC Writer Module
4
- *
5
- * Public API:
6
- * - initializeCard() – write mnemonic + set password on a blank card
7
- * - updateCard() – update mnemonic & password (old password required)
8
- * - updatePassword() – change password only
9
- * - writeUserNickname() – write user nickname (password required)
10
- * - resetCard() – wipe card and set password to "000000"
11
- *
12
- * Based on MIFARE Ultralight AES (MF0AES(H)20) datasheet.
13
- */
14
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
15
- if (k2 === undefined) k2 = k;
16
- var desc = Object.getOwnPropertyDescriptor(m, k);
17
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
18
- desc = { enumerable: true, get: function() { return m[k]; } };
19
- }
20
- Object.defineProperty(o, k2, desc);
21
- }) : (function(o, m, k, k2) {
22
- if (k2 === undefined) k2 = k;
23
- o[k2] = m[k];
24
- }));
25
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
26
- Object.defineProperty(o, "default", { enumerable: true, value: v });
27
- }) : function(o, v) {
28
- o["default"] = v;
29
- });
30
- var __importStar = (this && this.__importStar) || (function () {
31
- var ownKeys = function(o) {
32
- ownKeys = Object.getOwnPropertyNames || function (o) {
33
- var ar = [];
34
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
35
- return ar;
36
- };
37
- return ownKeys(o);
38
- };
39
- return function (mod) {
40
- if (mod && mod.__esModule) return mod;
41
- var result = {};
42
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
43
- __setModuleDefault(result, mod);
44
- return result;
45
- };
46
- })();
47
- Object.defineProperty(exports, "__esModule", { value: true });
48
- exports.releaseNfcOperationLock = exports.isNfcOperationLocked = exports.NfcStatusCode = void 0;
49
- exports.initializeCard = initializeCard;
50
- exports.updateCard = updateCard;
51
- exports.updatePassword = updatePassword;
52
- exports.writeUserNickname = writeUserNickname;
53
- exports.resetCard = resetCard;
54
- const react_native_1 = require("react-native");
55
- const bip39 = __importStar(require("bip39"));
56
- const constants_1 = require("./constants");
57
- const types_1 = require("./types");
58
- Object.defineProperty(exports, "NfcStatusCode", { enumerable: true, get: function () { return types_1.NfcStatusCode; } });
59
- const utils_1 = require("./utils");
60
- const crypto_1 = require("./crypto");
61
- const nfc_core_1 = require("./nfc-core");
62
- Object.defineProperty(exports, "isNfcOperationLocked", { enumerable: true, get: function () { return nfc_core_1.isNfcOperationLocked; } });
63
- Object.defineProperty(exports, "releaseNfcOperationLock", { enumerable: true, get: function () { return nfc_core_1.releaseNfcOperationLock; } });
64
- // ===========================================================================
65
- // Internal write helpers
66
- // ===========================================================================
67
- async function readPage(page) {
68
- return await (0, nfc_core_1.transceive)([constants_1.CMD_READ, page]);
69
- }
70
- async function writePage(page, data) {
71
- if (data.length !== 4)
72
- throw new Error('INVALID_CARD_DATA');
73
- await (0, nfc_core_1.transceive)([constants_1.CMD_WRITE, page, ...data]);
74
- }
75
- /**
76
- * Write data to mnemonic area (pages 0x08–0x24).
77
- * Does NOT touch the nickname area (pages 0x25–0x27).
78
- * iOS: inserts small delays and periodic keep-alive reads.
79
- */
80
- async function writeUserMemory(data) {
81
- const buffer = new Uint8Array(constants_1.MNEMONIC_MEMORY_SIZE);
82
- buffer.set(data.slice(0, constants_1.MNEMONIC_MEMORY_SIZE), 0);
83
- const totalPages = constants_1.MNEMONIC_PAGE_END - constants_1.USER_PAGE_START + 1;
84
- for (let i = 0; i < totalPages; i++) {
85
- const page = constants_1.USER_PAGE_START + i;
86
- const offset = i * constants_1.PAGE_SIZE;
87
- const pageData = Array.from(buffer.slice(offset, offset + constants_1.PAGE_SIZE));
88
- await writePage(page, pageData);
89
- if (react_native_1.Platform.OS === 'ios' && i < totalPages - 1) {
90
- await new Promise(r => setTimeout(r, constants_1.DELAY.IOS_PAGE_WRITE));
91
- // Keep-alive every 4 pages to prevent iOS session timeout
92
- if ((i + 1) % constants_1.DELAY.KEEPALIVE_FREQ === 0) {
93
- try {
94
- await (0, nfc_core_1.transceive)([constants_1.CMD_READ, 0x00]);
95
- }
96
- catch { /* non-fatal */ }
97
- }
98
- }
99
- }
100
- }
101
- /**
102
- * Write a 16-byte AES key to AES_KEY0 (pages 0x30–0x33).
103
- * Byte order is reversed per the datasheet.
104
- */
105
- async function writeAesKey(key) {
106
- for (let i = 0; i < 4; i++) {
107
- const page = constants_1.PAGE_AES_KEY0_START + i;
108
- const off = (3 - i) * 4;
109
- const pageData = [key[off + 3], key[off + 2], key[off + 1], key[off + 0]];
110
- await writePage(page, pageData);
111
- if (react_native_1.Platform.OS === 'ios' && i < 3)
112
- await new Promise(r => setTimeout(r, constants_1.DELAY.IOS_KEY_WRITE));
113
- }
114
- }
115
- /** Write a UTF-8 nickname into the last 3 pages of user memory. */
116
- async function writeNicknameToCard(nickname) {
117
- const safeNickname = nickname || '';
118
- const encoder = new TextEncoder();
119
- let nicknameBytes = encoder.encode(String(safeNickname));
120
- if (nicknameBytes.length > constants_1.USER_NICKNAME_MAX_LENGTH) {
121
- nicknameBytes = nicknameBytes.slice(0, constants_1.USER_NICKNAME_MAX_LENGTH);
122
- }
123
- const buffer = new Uint8Array(constants_1.USER_NICKNAME_MAX_LENGTH);
124
- buffer.set(nicknameBytes, 0);
125
- const totalPages = constants_1.USER_NICKNAME_PAGE_END - constants_1.USER_NICKNAME_PAGE_START + 1;
126
- for (let i = 0; i < totalPages; i++) {
127
- const page = constants_1.USER_NICKNAME_PAGE_START + i;
128
- const offset = i * constants_1.PAGE_SIZE;
129
- await writePage(page, Array.from(buffer.slice(offset, offset + constants_1.PAGE_SIZE)));
130
- if (react_native_1.Platform.OS === 'ios' && i < totalPages - 1)
131
- await new Promise(r => setTimeout(r, constants_1.DELAY.IOS_NICKNAME_WRITE));
132
- }
133
- }
134
- // ===========================================================================
135
- // Auth configuration
136
- // ===========================================================================
137
- /** Enable read+write protection and secure messaging starting from USER_PAGE_START. */
138
- async function configureAuth() {
139
- const cfg0 = await readPage(constants_1.PAGE_CFG0);
140
- cfg0[0] = cfg0[0] | constants_1.SEC_MSG_ACT_MASK; // Enable CMAC secure messaging
141
- cfg0[3] = constants_1.USER_PAGE_START;
142
- await writePage(constants_1.PAGE_CFG0, cfg0.slice(0, 4));
143
- if (react_native_1.Platform.OS === 'ios')
144
- await new Promise(r => setTimeout(r, constants_1.DELAY.IOS_CONFIG));
145
- const cfg1 = await readPage(constants_1.PAGE_CFG1);
146
- cfg1[0] = cfg1[0] | 0x80; // PROT bit = 1
147
- await writePage(constants_1.PAGE_CFG1, cfg1.slice(0, 4));
148
- }
149
- /** Disable auth protection (AUTH0 = 0x3C, PROT = 0). */
150
- async function disableAuth() {
151
- if (react_native_1.Platform.OS === 'ios') {
152
- try {
153
- await (0, nfc_core_1.transceive)([constants_1.CMD_READ, 0x00]);
154
- }
155
- catch { /* keep-alive */ }
156
- }
157
- const cfg0 = await readPage(constants_1.PAGE_CFG0);
158
- cfg0[3] = 0x3c;
159
- await writePage(constants_1.PAGE_CFG0, cfg0.slice(0, 4));
160
- if (react_native_1.Platform.OS === 'ios')
161
- await new Promise(r => setTimeout(r, constants_1.DELAY.IOS_CONFIG));
162
- const cfg1 = await readPage(constants_1.PAGE_CFG1);
163
- cfg1[0] = cfg1[0] & 0x7f; // Clear PROT bit
164
- await writePage(constants_1.PAGE_CFG1, cfg1.slice(0, 4));
165
- }
166
- // ===========================================================================
167
- // Mnemonic → entropy conversion
168
- // ===========================================================================
169
- /**
170
- * Convert a BIP-39 mnemonic into the on-card storage format:
171
- * [type 1B] [entropy 16-32B] [CRC16 2B]
172
- */
173
- function mnemonicToEntropyWithCRC(mnemonic) {
174
- if (!bip39.validateMnemonic(mnemonic))
175
- throw new Error('INVALID_MNEMONIC');
176
- const entropyHex = bip39.mnemonicToEntropy(mnemonic);
177
- const entropyBytes = (0, utils_1.hexToBytes)(entropyHex);
178
- const { typeId, description: typeStr } = (0, utils_1.getMnemonicTypeByEntropyLength)(entropyBytes.length);
179
- const dataBlock = new Uint8Array(1 + entropyBytes.length);
180
- dataBlock[0] = typeId;
181
- dataBlock.set(entropyBytes, 1);
182
- const crc16 = (0, utils_1.calculateCRC16)(dataBlock);
183
- const result = new Uint8Array(dataBlock.length + 2);
184
- result.set(dataBlock, 0);
185
- result.set((0, utils_1.crc16ToBytes)(crc16), dataBlock.length);
186
- return { data: result, type: typeStr, entropyHex, rawBytes: (0, utils_1.bytesToHex)(result), crc16 };
187
- }
188
- // ===========================================================================
189
- // Public API
190
- // ===========================================================================
191
- /**
192
- * Initialize a card: authenticate with the default password, then write
193
- * mnemonic and set a new password.
194
- *
195
- * @param mnemonic BIP-39 mnemonic to write.
196
- * @param newPassword Password to protect the card with after initialization.
197
- * @param defaultPassword Current (factory-default) password on the card.
198
- */
199
- async function initializeCard(mnemonic, newPassword, defaultPassword, onCardIdentified) {
200
- try {
201
- await (0, nfc_core_1.acquireNfcLock)();
202
- }
203
- catch {
204
- return { code: types_1.NfcStatusCode.UNKNOWN_ERROR, success: false };
205
- }
206
- let retryCountAfterPreDecrement;
207
- try {
208
- try {
209
- await (0, nfc_core_1.requestNfcTech)();
210
- }
211
- catch {
212
- (0, nfc_core_1.releaseNfcLock)();
213
- return { code: types_1.NfcStatusCode.NFC_CONNECT_FAILED, success: false };
214
- }
215
- try {
216
- const entropyResult = mnemonicToEntropyWithCRC(mnemonic);
217
- // Decrement retry counter before auth
218
- try {
219
- const n = await (0, nfc_core_1.decrementRetryCountInSession)();
220
- if (typeof n === 'number')
221
- retryCountAfterPreDecrement = n;
222
- }
223
- catch (error) {
224
- const code = (0, types_1.errorToCode)(error);
225
- if (code === types_1.NfcStatusCode.RETRY_COUNT_EXHAUSTED) {
226
- await (0, nfc_core_1.releaseNfcTech)();
227
- (0, nfc_core_1.releaseNfcLock)();
228
- return (0, types_1.nfcResultRetryCountExhausted)();
229
- }
230
- }
231
- // Authenticate with factory-default password
232
- const oldKey = (0, crypto_1.passwordToAesKey)(defaultPassword);
233
- await (0, nfc_core_1.authenticate)(oldKey);
234
- // Auth succeeded – reset retry counter
235
- try {
236
- await (0, nfc_core_1.writeRetryCountInSession)(constants_1.DEFAULT_PIN_RETRY_COUNT);
237
- retryCountAfterPreDecrement = constants_1.DEFAULT_PIN_RETRY_COUNT;
238
- }
239
- catch { /* non-fatal */ }
240
- onCardIdentified?.();
241
- // Write mnemonic (while still authenticated)
242
- await writeUserMemory(entropyResult.data);
243
- // Switch to new password
244
- const newKey = (0, crypto_1.passwordToAesKey)(newPassword);
245
- await disableAuth();
246
- await writeAesKey(newKey);
247
- await configureAuth();
248
- await (0, nfc_core_1.releaseNfcTech)();
249
- (0, nfc_core_1.releaseNfcLock)();
250
- return {
251
- code: types_1.NfcStatusCode.INIT_SUCCESS,
252
- success: true,
253
- data: {
254
- type: entropyResult.type,
255
- entropyHex: entropyResult.entropyHex,
256
- rawBytes: entropyResult.rawBytes,
257
- crc16: entropyResult.crc16,
258
- retryCount: constants_1.DEFAULT_PIN_RETRY_COUNT,
259
- },
260
- };
261
- }
262
- catch (error) {
263
- await (0, nfc_core_1.releaseNfcTech)();
264
- (0, nfc_core_1.releaseNfcLock)();
265
- const code = (0, types_1.errorToCode)(error);
266
- return {
267
- code,
268
- success: false,
269
- data: typeof retryCountAfterPreDecrement === 'number' ? { retryCount: retryCountAfterPreDecrement } : undefined,
270
- };
271
- }
272
- }
273
- catch (error) {
274
- (0, nfc_core_1.releaseNfcLock)();
275
- throw error;
276
- }
277
- }
278
- /**
279
- * Update card: authenticate with old password, then write new mnemonic + new password.
280
- *
281
- * When `options.precheckExistingMnemonic` is true, the card is read after authentication.
282
- * If a valid mnemonic backup already exists, the write is skipped and PRECHECK_HAS_BACKUP
283
- * is returned (`success: true` — operation completed; distinguish outcome by `code`).
284
- * Otherwise the normal write flow proceeds.
285
- */
286
- async function updateCard(oldPassword, newPassword, newMnemonic, onCardIdentified, options) {
287
- try {
288
- await (0, nfc_core_1.acquireNfcLock)();
289
- }
290
- catch {
291
- return { code: types_1.NfcStatusCode.UNKNOWN_ERROR, success: false };
292
- }
293
- let retryCountAfterPreDecrement;
294
- try {
295
- try {
296
- await (0, nfc_core_1.requestNfcTech)();
297
- }
298
- catch {
299
- (0, nfc_core_1.releaseNfcLock)();
300
- return { code: types_1.NfcStatusCode.NFC_CONNECT_FAILED, success: false };
301
- }
302
- try {
303
- try {
304
- const n = await (0, nfc_core_1.decrementRetryCountInSession)();
305
- if (typeof n === 'number')
306
- retryCountAfterPreDecrement = n;
307
- }
308
- catch (error) {
309
- const code = (0, types_1.errorToCode)(error);
310
- if (code === types_1.NfcStatusCode.RETRY_COUNT_EXHAUSTED) {
311
- await (0, nfc_core_1.releaseNfcTech)();
312
- (0, nfc_core_1.releaseNfcLock)();
313
- return (0, types_1.nfcResultRetryCountExhausted)();
314
- }
315
- // Communication failure — counter was not decremented, continue to auth
316
- }
317
- const oldKey = (0, crypto_1.passwordToAesKey)(oldPassword);
318
- await (0, nfc_core_1.authenticate)(oldKey);
319
- try {
320
- await (0, nfc_core_1.writeRetryCountInSession)(constants_1.DEFAULT_PIN_RETRY_COUNT);
321
- retryCountAfterPreDecrement = constants_1.DEFAULT_PIN_RETRY_COUNT;
322
- }
323
- catch { /* non-fatal */ }
324
- onCardIdentified?.();
325
- if (options?.precheckExistingMnemonic) {
326
- try {
327
- const data = await (0, nfc_core_1.readUserMemory)();
328
- const decoded = (0, utils_1.validateMnemonicPayload)(data);
329
- await (0, nfc_core_1.releaseNfcTech)();
330
- (0, nfc_core_1.releaseNfcLock)();
331
- return {
332
- code: types_1.NfcStatusCode.PRECHECK_HAS_BACKUP,
333
- success: true,
334
- data: { type: decoded.type, retryCount: constants_1.DEFAULT_PIN_RETRY_COUNT },
335
- };
336
- }
337
- catch (e) {
338
- const c = (0, types_1.errorToCode)(e);
339
- if (c === types_1.NfcStatusCode.CHECK_EMPTY ||
340
- c === types_1.NfcStatusCode.CRC16_CHECK_FAILED ||
341
- c === types_1.NfcStatusCode.INVALID_CARD_DATA) {
342
- // No valid mnemonic on card — fall through to write
343
- }
344
- else {
345
- await (0, nfc_core_1.releaseNfcTech)();
346
- (0, nfc_core_1.releaseNfcLock)();
347
- return { code: c, success: false };
348
- }
349
- }
350
- }
351
- const entropyResult = mnemonicToEntropyWithCRC(newMnemonic);
352
- await writeUserMemory(entropyResult.data);
353
- await disableAuth();
354
- const newKey = (0, crypto_1.passwordToAesKey)(newPassword);
355
- const aesKeyHex = (0, utils_1.bytesToHex)(newKey);
356
- await writeAesKey(newKey);
357
- await configureAuth();
358
- await (0, nfc_core_1.releaseNfcTech)();
359
- (0, nfc_core_1.releaseNfcLock)();
360
- return {
361
- code: types_1.NfcStatusCode.WRITE_SUCCESS,
362
- success: true,
363
- data: {
364
- type: entropyResult.type,
365
- entropyHex: entropyResult.entropyHex,
366
- rawBytes: entropyResult.rawBytes,
367
- aesKeyHex,
368
- retryCount: constants_1.DEFAULT_PIN_RETRY_COUNT,
369
- },
370
- };
371
- }
372
- catch (error) {
373
- const isTransceiveErr = error?.message?.includes('transceive fail') ||
374
- error?.message?.includes('TagLost');
375
- await (0, nfc_core_1.releaseNfcTech)(isTransceiveErr);
376
- (0, nfc_core_1.releaseNfcLock)();
377
- const code = (0, types_1.errorToCode)(error);
378
- return {
379
- code,
380
- success: false,
381
- data: typeof retryCountAfterPreDecrement === 'number' ? { retryCount: retryCountAfterPreDecrement } : undefined,
382
- };
383
- }
384
- }
385
- catch (error) {
386
- (0, nfc_core_1.releaseNfcLock)();
387
- throw error;
388
- }
389
- }
390
- /** Change password only (old password required). */
391
- async function updatePassword(oldPassword, newPassword, onCardIdentified) {
392
- try {
393
- await (0, nfc_core_1.acquireNfcLock)();
394
- }
395
- catch {
396
- return { code: types_1.NfcStatusCode.UNKNOWN_ERROR, success: false };
397
- }
398
- let retryCountAfterPreDecrement;
399
- try {
400
- try {
401
- await (0, nfc_core_1.requestNfcTech)();
402
- }
403
- catch {
404
- (0, nfc_core_1.releaseNfcLock)();
405
- return { code: types_1.NfcStatusCode.NFC_CONNECT_FAILED, success: false };
406
- }
407
- try {
408
- try {
409
- const n = await (0, nfc_core_1.decrementRetryCountInSession)();
410
- if (typeof n === 'number')
411
- retryCountAfterPreDecrement = n;
412
- }
413
- catch (error) {
414
- const code = (0, types_1.errorToCode)(error);
415
- if (code === types_1.NfcStatusCode.RETRY_COUNT_EXHAUSTED) {
416
- await (0, nfc_core_1.releaseNfcTech)();
417
- (0, nfc_core_1.releaseNfcLock)();
418
- return (0, types_1.nfcResultRetryCountExhausted)();
419
- }
420
- // Communication failure — counter was not decremented, continue to auth
421
- }
422
- const oldKey = (0, crypto_1.passwordToAesKey)(oldPassword);
423
- await (0, nfc_core_1.authenticate)(oldKey);
424
- try {
425
- await (0, nfc_core_1.writeRetryCountInSession)(constants_1.DEFAULT_PIN_RETRY_COUNT);
426
- retryCountAfterPreDecrement = constants_1.DEFAULT_PIN_RETRY_COUNT;
427
- }
428
- catch { /* non-fatal */ }
429
- onCardIdentified?.();
430
- await disableAuth();
431
- const newKey = (0, crypto_1.passwordToAesKey)(newPassword);
432
- const aesKeyHex = (0, utils_1.bytesToHex)(newKey);
433
- await writeAesKey(newKey);
434
- await configureAuth();
435
- await (0, nfc_core_1.releaseNfcTech)();
436
- (0, nfc_core_1.releaseNfcLock)();
437
- return {
438
- code: types_1.NfcStatusCode.UPDATE_PASSWORD_SUCCESS,
439
- success: true,
440
- data: { aesKeyHex, retryCount: constants_1.DEFAULT_PIN_RETRY_COUNT },
441
- };
442
- }
443
- catch (error) {
444
- await (0, nfc_core_1.releaseNfcTech)();
445
- (0, nfc_core_1.releaseNfcLock)();
446
- const code = (0, types_1.errorToCode)(error);
447
- return {
448
- code,
449
- success: false,
450
- data: typeof retryCountAfterPreDecrement === 'number' ? { retryCount: retryCountAfterPreDecrement } : undefined,
451
- };
452
- }
453
- }
454
- catch (error) {
455
- (0, nfc_core_1.releaseNfcLock)();
456
- throw error;
457
- }
458
- }
459
- /** Write a user nickname (password required for authentication). */
460
- async function writeUserNickname(password, nickname, onCardIdentified) {
461
- try {
462
- await (0, nfc_core_1.acquireNfcLock)();
463
- }
464
- catch {
465
- return { code: types_1.NfcStatusCode.UNKNOWN_ERROR, success: false };
466
- }
467
- try {
468
- try {
469
- await (0, nfc_core_1.requestNfcTech)();
470
- }
471
- catch {
472
- (0, nfc_core_1.releaseNfcLock)();
473
- return { code: types_1.NfcStatusCode.NFC_CONNECT_FAILED, success: false };
474
- }
475
- try {
476
- // Pre-decrement retry counter before auth
477
- try {
478
- await (0, nfc_core_1.decrementRetryCountInSession)();
479
- }
480
- catch (error) {
481
- const code = (0, types_1.errorToCode)(error);
482
- if (code === types_1.NfcStatusCode.RETRY_COUNT_EXHAUSTED) {
483
- await (0, nfc_core_1.releaseNfcTech)();
484
- (0, nfc_core_1.releaseNfcLock)();
485
- return (0, types_1.nfcResultRetryCountExhausted)();
486
- }
487
- }
488
- const aesKey = (0, crypto_1.passwordToAesKey)(password);
489
- await (0, nfc_core_1.authenticate)(aesKey);
490
- // Auth succeeded – reset retry counter
491
- try {
492
- await (0, nfc_core_1.writeRetryCountInSession)(constants_1.DEFAULT_PIN_RETRY_COUNT);
493
- }
494
- catch { /* non-fatal */ }
495
- onCardIdentified?.();
496
- await disableAuth();
497
- await writeNicknameToCard(nickname);
498
- await configureAuth();
499
- await (0, nfc_core_1.releaseNfcTech)();
500
- (0, nfc_core_1.releaseNfcLock)();
501
- return {
502
- code: types_1.NfcStatusCode.WRITE_NICKNAME_SUCCESS,
503
- success: true,
504
- };
505
- }
506
- catch (error) {
507
- await (0, nfc_core_1.releaseNfcTech)();
508
- (0, nfc_core_1.releaseNfcLock)();
509
- const code = (0, types_1.errorToCode)(error);
510
- return { code, success: false };
511
- }
512
- }
513
- catch (error) {
514
- (0, nfc_core_1.releaseNfcLock)();
515
- throw error;
516
- }
517
- }
518
- /**
519
- * Reset card: wipe mnemonic data and set a new password.
520
- * @param password – current card password (required if protection is enabled).
521
- * @param newPassword – password to set after reset.
522
- * @param onCardIdentified – callback when card is identified.
523
- */
524
- async function resetCard(password, newPassword, onCardIdentified) {
525
- try {
526
- await (0, nfc_core_1.acquireNfcLock)();
527
- }
528
- catch {
529
- return { code: types_1.NfcStatusCode.UNKNOWN_ERROR, success: false };
530
- }
531
- let retryCountAfterPreDecrement;
532
- try {
533
- try {
534
- await (0, nfc_core_1.requestNfcTech)();
535
- }
536
- catch {
537
- (0, nfc_core_1.releaseNfcLock)();
538
- return { code: types_1.NfcStatusCode.NFC_CONNECT_FAILED, success: false };
539
- }
540
- try {
541
- if (password) {
542
- try {
543
- const n = await (0, nfc_core_1.decrementRetryCountInSession)();
544
- if (typeof n === 'number')
545
- retryCountAfterPreDecrement = n;
546
- }
547
- catch (error) {
548
- const code = (0, types_1.errorToCode)(error);
549
- if (code === types_1.NfcStatusCode.RETRY_COUNT_EXHAUSTED) {
550
- await (0, nfc_core_1.releaseNfcTech)();
551
- (0, nfc_core_1.releaseNfcLock)();
552
- return (0, types_1.nfcResultRetryCountExhausted)();
553
- }
554
- // Communication failure — counter was not decremented, continue to auth
555
- }
556
- const aesKey = (0, crypto_1.passwordToAesKey)(password);
557
- try {
558
- await (0, nfc_core_1.authenticate)(aesKey);
559
- try {
560
- await (0, nfc_core_1.writeRetryCountInSession)(constants_1.DEFAULT_PIN_RETRY_COUNT);
561
- retryCountAfterPreDecrement = constants_1.DEFAULT_PIN_RETRY_COUNT;
562
- }
563
- catch { /* non-fatal */ }
564
- }
565
- catch (authError) {
566
- const msg = authError?.message || '';
567
- if (msg.includes('AUTH_WRONG_PASSWORD') ||
568
- (react_native_1.Platform.OS === 'ios' && (msg.includes('AUTH_INVALID_RESPONSE') || !msg.trim()))) {
569
- throw new Error('AUTH_WRONG_PASSWORD');
570
- }
571
- throw authError;
572
- }
573
- onCardIdentified?.();
574
- // iOS: verify connection after auth
575
- if (react_native_1.Platform.OS === 'ios') {
576
- try {
577
- await readPage(constants_1.USER_PAGE_START);
578
- }
579
- catch { /* non-fatal */ }
580
- }
581
- }
582
- else {
583
- try {
584
- await (0, nfc_core_1.authenticate)((0, crypto_1.passwordToAesKey)(newPassword));
585
- }
586
- catch {
587
- throw new Error('AUTH_WRONG_PASSWORD');
588
- }
589
- onCardIdentified?.();
590
- }
591
- // Wipe mnemonic data (nickname is preserved)
592
- await writeUserMemory(new Uint8Array(constants_1.MNEMONIC_MEMORY_SIZE));
593
- // Set new password and keep protection enabled
594
- const resetKey = (0, crypto_1.passwordToAesKey)(newPassword);
595
- await disableAuth();
596
- await writeAesKey(resetKey);
597
- await configureAuth();
598
- await (0, nfc_core_1.releaseNfcTech)();
599
- (0, nfc_core_1.releaseNfcLock)();
600
- return {
601
- code: types_1.NfcStatusCode.RESET_SUCCESS,
602
- success: true,
603
- data: { retryCount: constants_1.DEFAULT_PIN_RETRY_COUNT },
604
- };
605
- }
606
- catch (error) {
607
- await (0, nfc_core_1.releaseNfcTech)();
608
- (0, nfc_core_1.releaseNfcLock)();
609
- const rawMsg = error.message || '';
610
- const isWriteErr = rawMsg.includes('WRITE_FAILED') || rawMsg.includes('transceive');
611
- const code = (0, types_1.errorToCode)(isWriteErr ? new Error('WRITE_FAILED') : error);
612
- return {
613
- code,
614
- success: false,
615
- data: typeof retryCountAfterPreDecrement === 'number' ? { retryCount: retryCountAfterPreDecrement } : undefined,
616
- };
617
- }
618
- }
619
- catch (error) {
620
- (0, nfc_core_1.releaseNfcLock)();
621
- throw error;
622
- }
623
- }