@ukeyfe/react-native-nfc-litecard 1.0.2 → 1.0.3
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/README.md +71 -34
- package/README.zh.md +275 -238
- package/dist/constants.d.ts +26 -0
- package/dist/constants.js +33 -1
- package/dist/crypto.d.ts +22 -0
- package/dist/crypto.js +165 -15
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -3
- package/dist/nfc-core.d.ts +9 -0
- package/dist/nfc-core.js +97 -9
- package/dist/reader.d.ts +13 -2
- package/dist/reader.js +153 -66
- package/dist/types.d.ts +16 -4
- package/dist/types.js +26 -24
- package/dist/utils.d.ts +16 -0
- package/dist/utils.js +36 -28
- package/dist/writer.d.ts +9 -4
- package/dist/writer.js +95 -82
- package/package.json +1 -1
package/dist/reader.js
CHANGED
|
@@ -46,18 +46,20 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
46
46
|
};
|
|
47
47
|
})();
|
|
48
48
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
-
exports.DEFAULT_PIN_RETRY_COUNT = exports.clearNfcOperationCancelledByCleanup = exports.getNfcOperationCancelledByCleanupTimestamp = exports.consumeNfcOperationCancelledByCleanup = exports.markNfcOperationCancelledByCleanup = exports.releaseNfcOperationLock = exports.isNfcOperationLocked = exports.
|
|
49
|
+
exports.DEFAULT_PIN_RETRY_COUNT = exports.clearNfcOperationCancelledByCleanup = exports.getNfcOperationCancelledByCleanupTimestamp = exports.consumeNfcOperationCancelledByCleanup = exports.markNfcOperationCancelledByCleanup = exports.releaseNfcOperationLock = exports.isNfcOperationLocked = exports.NfcStatusCode = void 0;
|
|
50
50
|
exports.cardInfoToJson = cardInfoToJson;
|
|
51
51
|
exports.checkCard = checkCard;
|
|
52
52
|
exports.readMnemonic = readMnemonic;
|
|
53
53
|
exports.readUserNickname = readUserNickname;
|
|
54
54
|
exports.readMnemonicRetryCount = readMnemonicRetryCount;
|
|
55
55
|
exports.resetRetryCountTo10 = resetRetryCountTo10;
|
|
56
|
+
exports.getCardVersion = getCardVersion;
|
|
57
|
+
exports.readOriginality = readOriginality;
|
|
56
58
|
const bip39 = __importStar(require("bip39"));
|
|
57
59
|
const constants_1 = require("./constants");
|
|
58
60
|
Object.defineProperty(exports, "DEFAULT_PIN_RETRY_COUNT", { enumerable: true, get: function () { return constants_1.DEFAULT_PIN_RETRY_COUNT; } });
|
|
59
61
|
const types_1 = require("./types");
|
|
60
|
-
Object.defineProperty(exports, "
|
|
62
|
+
Object.defineProperty(exports, "NfcStatusCode", { enumerable: true, get: function () { return types_1.NfcStatusCode; } });
|
|
61
63
|
const utils_1 = require("./utils");
|
|
62
64
|
const crypto_1 = require("./crypto");
|
|
63
65
|
const nfc_core_1 = require("./nfc-core");
|
|
@@ -72,13 +74,6 @@ Object.defineProperty(exports, "clearNfcOperationCancelledByCleanup", { enumerab
|
|
|
72
74
|
// ===========================================================================
|
|
73
75
|
// Internal read helpers
|
|
74
76
|
// ===========================================================================
|
|
75
|
-
/** FAST_READ pages 0x08–0x27 (user memory). */
|
|
76
|
-
async function readUserMemory() {
|
|
77
|
-
const response = await (0, nfc_core_1.transceive)([constants_1.CMD_FAST_READ, constants_1.USER_PAGE_START, constants_1.USER_PAGE_END]);
|
|
78
|
-
if (!response || response.length < constants_1.USER_MEMORY_SIZE)
|
|
79
|
-
throw new Error('READ_FAILED');
|
|
80
|
-
return new Uint8Array(response.slice(0, constants_1.USER_MEMORY_SIZE));
|
|
81
|
-
}
|
|
82
77
|
/** FAST_READ pages 0x04–0x07 (card info area). */
|
|
83
78
|
async function readUserCardInfo() {
|
|
84
79
|
const response = await (0, nfc_core_1.transceive)([constants_1.CMD_FAST_READ, constants_1.USER_CARD_INFO_PAGE_START, constants_1.USER_CARD_INFO_PAGE_END]);
|
|
@@ -125,32 +120,7 @@ function entropyToMnemonic(data) {
|
|
|
125
120
|
throw new Error('INVALID_CARD_DATA');
|
|
126
121
|
if (data.every(b => b === 0))
|
|
127
122
|
throw new Error('EMPTY_CARD');
|
|
128
|
-
const
|
|
129
|
-
let entropyLength;
|
|
130
|
-
let typeStr;
|
|
131
|
-
switch (typeId) {
|
|
132
|
-
case constants_1.MNEMONIC_TYPE_12:
|
|
133
|
-
entropyLength = 16;
|
|
134
|
-
typeStr = '12 words (128-bit)';
|
|
135
|
-
break;
|
|
136
|
-
case constants_1.MNEMONIC_TYPE_15:
|
|
137
|
-
entropyLength = 20;
|
|
138
|
-
typeStr = '15 words (160-bit)';
|
|
139
|
-
break;
|
|
140
|
-
case constants_1.MNEMONIC_TYPE_18:
|
|
141
|
-
entropyLength = 24;
|
|
142
|
-
typeStr = '18 words (192-bit)';
|
|
143
|
-
break;
|
|
144
|
-
case constants_1.MNEMONIC_TYPE_21:
|
|
145
|
-
entropyLength = 28;
|
|
146
|
-
typeStr = '21 words (224-bit)';
|
|
147
|
-
break;
|
|
148
|
-
case constants_1.MNEMONIC_TYPE_24:
|
|
149
|
-
entropyLength = 32;
|
|
150
|
-
typeStr = '24 words (256-bit)';
|
|
151
|
-
break;
|
|
152
|
-
default: throw new Error('INVALID_CARD_DATA');
|
|
153
|
-
}
|
|
123
|
+
const { entropyLength, description } = (0, utils_1.getMnemonicTypeInfo)(data[0]);
|
|
154
124
|
const expectedTotal = 1 + entropyLength + 2;
|
|
155
125
|
if (data.length < expectedTotal)
|
|
156
126
|
throw new Error('INVALID_CARD_DATA');
|
|
@@ -163,7 +133,7 @@ function entropyToMnemonic(data) {
|
|
|
163
133
|
const entropyHex = (0, utils_1.bytesToHex)(Array.from(entropyBytes));
|
|
164
134
|
const rawBytes = (0, utils_1.bytesToHex)(Array.from(dataBlock));
|
|
165
135
|
const mnemonic = bip39.entropyToMnemonic(entropyHex);
|
|
166
|
-
return { mnemonic, entropyHex, type:
|
|
136
|
+
return { mnemonic, entropyHex, type: description, rawBytes, crc16: calcCRC };
|
|
167
137
|
}
|
|
168
138
|
// ===========================================================================
|
|
169
139
|
// Card info parsing
|
|
@@ -243,7 +213,7 @@ async function checkCard(password, onCardIdentified) {
|
|
|
243
213
|
await (0, nfc_core_1.acquireNfcLock)();
|
|
244
214
|
}
|
|
245
215
|
catch {
|
|
246
|
-
return { code: types_1.
|
|
216
|
+
return { code: types_1.NfcStatusCode.UNKNOWN_ERROR, success: false };
|
|
247
217
|
}
|
|
248
218
|
try {
|
|
249
219
|
try {
|
|
@@ -251,25 +221,41 @@ async function checkCard(password, onCardIdentified) {
|
|
|
251
221
|
}
|
|
252
222
|
catch {
|
|
253
223
|
(0, nfc_core_1.releaseNfcLock)();
|
|
254
|
-
return { code: types_1.
|
|
224
|
+
return { code: types_1.NfcStatusCode.NFC_CONNECT_FAILED, success: false };
|
|
255
225
|
}
|
|
256
226
|
try {
|
|
257
227
|
if (password) {
|
|
258
|
-
// Authenticated check: authenticate → read
|
|
228
|
+
// Authenticated check: decrement retry → authenticate → read → validate CRC16
|
|
229
|
+
try {
|
|
230
|
+
await (0, nfc_core_1.decrementRetryCountInSession)();
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
const code = (0, types_1.errorToCode)(error);
|
|
234
|
+
if (code === types_1.NfcStatusCode.RETRY_COUNT_EXHAUSTED) {
|
|
235
|
+
await (0, nfc_core_1.releaseNfcTech)();
|
|
236
|
+
(0, nfc_core_1.releaseNfcLock)();
|
|
237
|
+
return (0, types_1.nfcResultRetryCountExhausted)();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
259
240
|
const aesKey = (0, crypto_1.passwordToAesKey)(password);
|
|
260
241
|
await (0, nfc_core_1.authenticate)(aesKey);
|
|
242
|
+
// Auth succeeded – reset retry counter
|
|
243
|
+
try {
|
|
244
|
+
await (0, nfc_core_1.writeRetryCountInSession)(constants_1.DEFAULT_PIN_RETRY_COUNT);
|
|
245
|
+
}
|
|
246
|
+
catch { /* non-fatal */ }
|
|
261
247
|
onCardIdentified?.();
|
|
262
|
-
const data = await readUserMemory();
|
|
248
|
+
const data = await (0, nfc_core_1.readUserMemory)();
|
|
263
249
|
try {
|
|
264
250
|
const decoded = (0, utils_1.validateMnemonicPayload)(data);
|
|
265
251
|
await (0, nfc_core_1.releaseNfcTech)();
|
|
266
252
|
(0, nfc_core_1.releaseNfcLock)();
|
|
267
|
-
return { code: types_1.
|
|
253
|
+
return { code: types_1.NfcStatusCode.CHECK_HAS_DATA, success: true, data: { type: decoded.type } };
|
|
268
254
|
}
|
|
269
255
|
catch {
|
|
270
256
|
await (0, nfc_core_1.releaseNfcTech)();
|
|
271
257
|
(0, nfc_core_1.releaseNfcLock)();
|
|
272
|
-
return { code: types_1.
|
|
258
|
+
return { code: types_1.NfcStatusCode.CHECK_EMPTY, success: true };
|
|
273
259
|
}
|
|
274
260
|
}
|
|
275
261
|
else {
|
|
@@ -283,16 +269,16 @@ async function checkCard(password, onCardIdentified) {
|
|
|
283
269
|
await (0, nfc_core_1.releaseNfcTech)();
|
|
284
270
|
(0, nfc_core_1.releaseNfcLock)();
|
|
285
271
|
if (response && response.length >= 4) {
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
return { code: hasData ? types_1.
|
|
272
|
+
let hasData = false;
|
|
273
|
+
try {
|
|
274
|
+
(0, utils_1.getMnemonicTypeInfo)(response[0]);
|
|
275
|
+
hasData = true;
|
|
276
|
+
}
|
|
277
|
+
catch { /* not a valid type */ }
|
|
278
|
+
return { code: hasData ? types_1.NfcStatusCode.CHECK_HAS_DATA : types_1.NfcStatusCode.CHECK_EMPTY, success: true };
|
|
293
279
|
}
|
|
294
280
|
// Read failed = protection is on, assume has data
|
|
295
|
-
return { code: types_1.
|
|
281
|
+
return { code: types_1.NfcStatusCode.CHECK_HAS_DATA, success: true };
|
|
296
282
|
}
|
|
297
283
|
}
|
|
298
284
|
catch (error) {
|
|
@@ -318,7 +304,7 @@ async function readMnemonic(password, onCardIdentified) {
|
|
|
318
304
|
await (0, nfc_core_1.acquireNfcLock)();
|
|
319
305
|
}
|
|
320
306
|
catch {
|
|
321
|
-
return { code: types_1.
|
|
307
|
+
return { code: types_1.NfcStatusCode.UNKNOWN_ERROR, success: false };
|
|
322
308
|
}
|
|
323
309
|
let retryCountAfterPreDecrement;
|
|
324
310
|
try {
|
|
@@ -327,7 +313,7 @@ async function readMnemonic(password, onCardIdentified) {
|
|
|
327
313
|
}
|
|
328
314
|
catch {
|
|
329
315
|
(0, nfc_core_1.releaseNfcLock)();
|
|
330
|
-
return { code: types_1.
|
|
316
|
+
return { code: types_1.NfcStatusCode.NFC_CONNECT_FAILED, success: false };
|
|
331
317
|
}
|
|
332
318
|
try {
|
|
333
319
|
// Pre-decrement retry counter before auth attempt
|
|
@@ -338,7 +324,7 @@ async function readMnemonic(password, onCardIdentified) {
|
|
|
338
324
|
}
|
|
339
325
|
catch (error) {
|
|
340
326
|
const code = (0, types_1.errorToCode)(error);
|
|
341
|
-
if (code === types_1.
|
|
327
|
+
if (code === types_1.NfcStatusCode.RETRY_COUNT_EXHAUSTED) {
|
|
342
328
|
await (0, nfc_core_1.releaseNfcTech)();
|
|
343
329
|
(0, nfc_core_1.releaseNfcLock)();
|
|
344
330
|
return (0, types_1.nfcResultRetryCountExhausted)();
|
|
@@ -347,8 +333,7 @@ async function readMnemonic(password, onCardIdentified) {
|
|
|
347
333
|
const aesKey = (0, crypto_1.passwordToAesKey)(password);
|
|
348
334
|
await (0, nfc_core_1.authenticate)(aesKey);
|
|
349
335
|
onCardIdentified?.();
|
|
350
|
-
const data = await readUserMemory();
|
|
351
|
-
await readUserCardInfo(); // read but not used directly yet
|
|
336
|
+
const data = await (0, nfc_core_1.readUserMemory)();
|
|
352
337
|
const result = entropyToMnemonic(data);
|
|
353
338
|
let nickname;
|
|
354
339
|
try {
|
|
@@ -365,7 +350,7 @@ async function readMnemonic(password, onCardIdentified) {
|
|
|
365
350
|
await (0, nfc_core_1.releaseNfcTech)();
|
|
366
351
|
(0, nfc_core_1.releaseNfcLock)();
|
|
367
352
|
return {
|
|
368
|
-
code: types_1.
|
|
353
|
+
code: types_1.NfcStatusCode.READ_SUCCESS,
|
|
369
354
|
success: true,
|
|
370
355
|
data: {
|
|
371
356
|
mnemonic: result.mnemonic,
|
|
@@ -405,7 +390,7 @@ async function readUserNickname(password, onCardIdentified) {
|
|
|
405
390
|
await (0, nfc_core_1.acquireNfcLock)();
|
|
406
391
|
}
|
|
407
392
|
catch {
|
|
408
|
-
return { code: types_1.
|
|
393
|
+
return { code: types_1.NfcStatusCode.UNKNOWN_ERROR, success: false };
|
|
409
394
|
}
|
|
410
395
|
try {
|
|
411
396
|
try {
|
|
@@ -413,7 +398,7 @@ async function readUserNickname(password, onCardIdentified) {
|
|
|
413
398
|
}
|
|
414
399
|
catch {
|
|
415
400
|
(0, nfc_core_1.releaseNfcLock)();
|
|
416
|
-
return { code: types_1.
|
|
401
|
+
return { code: types_1.NfcStatusCode.NFC_CONNECT_FAILED, success: false };
|
|
417
402
|
}
|
|
418
403
|
try {
|
|
419
404
|
if (password) {
|
|
@@ -425,7 +410,7 @@ async function readUserNickname(password, onCardIdentified) {
|
|
|
425
410
|
await (0, nfc_core_1.releaseNfcTech)();
|
|
426
411
|
(0, nfc_core_1.releaseNfcLock)();
|
|
427
412
|
return {
|
|
428
|
-
code: types_1.
|
|
413
|
+
code: types_1.NfcStatusCode.READ_NICKNAME_SUCCESS,
|
|
429
414
|
success: true,
|
|
430
415
|
data: { nickname },
|
|
431
416
|
};
|
|
@@ -448,7 +433,7 @@ async function readMnemonicRetryCount(onCardIdentified) {
|
|
|
448
433
|
await (0, nfc_core_1.acquireNfcLock)();
|
|
449
434
|
}
|
|
450
435
|
catch {
|
|
451
|
-
return { code: types_1.
|
|
436
|
+
return { code: types_1.NfcStatusCode.UNKNOWN_ERROR, success: false };
|
|
452
437
|
}
|
|
453
438
|
try {
|
|
454
439
|
try {
|
|
@@ -456,7 +441,7 @@ async function readMnemonicRetryCount(onCardIdentified) {
|
|
|
456
441
|
}
|
|
457
442
|
catch {
|
|
458
443
|
(0, nfc_core_1.releaseNfcLock)();
|
|
459
|
-
return { code: types_1.
|
|
444
|
+
return { code: types_1.NfcStatusCode.NFC_CONNECT_FAILED, success: false };
|
|
460
445
|
}
|
|
461
446
|
try {
|
|
462
447
|
onCardIdentified?.();
|
|
@@ -465,7 +450,7 @@ async function readMnemonicRetryCount(onCardIdentified) {
|
|
|
465
450
|
await (0, nfc_core_1.releaseNfcTech)();
|
|
466
451
|
(0, nfc_core_1.releaseNfcLock)();
|
|
467
452
|
return {
|
|
468
|
-
code: types_1.
|
|
453
|
+
code: types_1.NfcStatusCode.READ_RETRY_COUNT_SUCCESS,
|
|
469
454
|
success: true,
|
|
470
455
|
data: { retryCount: 0 },
|
|
471
456
|
};
|
|
@@ -474,7 +459,7 @@ async function readMnemonicRetryCount(onCardIdentified) {
|
|
|
474
459
|
await (0, nfc_core_1.releaseNfcTech)();
|
|
475
460
|
(0, nfc_core_1.releaseNfcLock)();
|
|
476
461
|
return {
|
|
477
|
-
code: types_1.
|
|
462
|
+
code: types_1.NfcStatusCode.READ_RETRY_COUNT_SUCCESS,
|
|
478
463
|
success: true,
|
|
479
464
|
data: { retryCount },
|
|
480
465
|
};
|
|
@@ -497,7 +482,7 @@ async function resetRetryCountTo10(onCardIdentified) {
|
|
|
497
482
|
await (0, nfc_core_1.acquireNfcLock)();
|
|
498
483
|
}
|
|
499
484
|
catch {
|
|
500
|
-
return { code: types_1.
|
|
485
|
+
return { code: types_1.NfcStatusCode.UNKNOWN_ERROR, success: false };
|
|
501
486
|
}
|
|
502
487
|
try {
|
|
503
488
|
try {
|
|
@@ -505,7 +490,7 @@ async function resetRetryCountTo10(onCardIdentified) {
|
|
|
505
490
|
}
|
|
506
491
|
catch {
|
|
507
492
|
(0, nfc_core_1.releaseNfcLock)();
|
|
508
|
-
return { code: types_1.
|
|
493
|
+
return { code: types_1.NfcStatusCode.NFC_CONNECT_FAILED, success: false };
|
|
509
494
|
}
|
|
510
495
|
try {
|
|
511
496
|
onCardIdentified?.();
|
|
@@ -513,7 +498,7 @@ async function resetRetryCountTo10(onCardIdentified) {
|
|
|
513
498
|
await (0, nfc_core_1.releaseNfcTech)();
|
|
514
499
|
(0, nfc_core_1.releaseNfcLock)();
|
|
515
500
|
return {
|
|
516
|
-
code: types_1.
|
|
501
|
+
code: types_1.NfcStatusCode.READ_RETRY_COUNT_SUCCESS,
|
|
517
502
|
success: true,
|
|
518
503
|
data: { retryCount: constants_1.DEFAULT_PIN_RETRY_COUNT },
|
|
519
504
|
};
|
|
@@ -530,3 +515,105 @@ async function resetRetryCountTo10(onCardIdentified) {
|
|
|
530
515
|
throw error;
|
|
531
516
|
}
|
|
532
517
|
}
|
|
518
|
+
/**
|
|
519
|
+
* Read card product version info (GET_VERSION command).
|
|
520
|
+
* No authentication required.
|
|
521
|
+
*/
|
|
522
|
+
async function getCardVersion(onCardIdentified) {
|
|
523
|
+
try {
|
|
524
|
+
await (0, nfc_core_1.acquireNfcLock)();
|
|
525
|
+
}
|
|
526
|
+
catch {
|
|
527
|
+
return { code: types_1.NfcStatusCode.UNKNOWN_ERROR, success: false };
|
|
528
|
+
}
|
|
529
|
+
try {
|
|
530
|
+
try {
|
|
531
|
+
await (0, nfc_core_1.requestNfcTech)();
|
|
532
|
+
}
|
|
533
|
+
catch {
|
|
534
|
+
(0, nfc_core_1.releaseNfcLock)();
|
|
535
|
+
return { code: types_1.NfcStatusCode.NFC_CONNECT_FAILED, success: false };
|
|
536
|
+
}
|
|
537
|
+
try {
|
|
538
|
+
onCardIdentified?.();
|
|
539
|
+
const response = await (0, nfc_core_1.transceive)([constants_1.CMD_GET_VERSION]);
|
|
540
|
+
if (!response || response.length < 8) {
|
|
541
|
+
throw new Error('READ_FAILED');
|
|
542
|
+
}
|
|
543
|
+
await (0, nfc_core_1.releaseNfcTech)();
|
|
544
|
+
(0, nfc_core_1.releaseNfcLock)();
|
|
545
|
+
return {
|
|
546
|
+
code: types_1.NfcStatusCode.GET_VERSION_SUCCESS,
|
|
547
|
+
success: true,
|
|
548
|
+
data: {
|
|
549
|
+
version: {
|
|
550
|
+
vendorId: response[1],
|
|
551
|
+
productType: response[2],
|
|
552
|
+
productSubtype: response[3],
|
|
553
|
+
majorVersion: response[4],
|
|
554
|
+
minorVersion: response[5],
|
|
555
|
+
storageSize: response[6],
|
|
556
|
+
protocolType: response[7],
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
catch (error) {
|
|
562
|
+
await (0, nfc_core_1.releaseNfcTech)();
|
|
563
|
+
(0, nfc_core_1.releaseNfcLock)();
|
|
564
|
+
const code = (0, types_1.errorToCode)(error);
|
|
565
|
+
return { code, success: false };
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
catch (error) {
|
|
569
|
+
(0, nfc_core_1.releaseNfcLock)();
|
|
570
|
+
throw error;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Read the ECC originality signature (READ_SIG command).
|
|
575
|
+
* Verifies the card is a genuine NXP product.
|
|
576
|
+
* No authentication required (unless Random ID is enabled).
|
|
577
|
+
*/
|
|
578
|
+
async function readOriginality(onCardIdentified) {
|
|
579
|
+
try {
|
|
580
|
+
await (0, nfc_core_1.acquireNfcLock)();
|
|
581
|
+
}
|
|
582
|
+
catch {
|
|
583
|
+
return { code: types_1.NfcStatusCode.UNKNOWN_ERROR, success: false };
|
|
584
|
+
}
|
|
585
|
+
try {
|
|
586
|
+
try {
|
|
587
|
+
await (0, nfc_core_1.requestNfcTech)();
|
|
588
|
+
}
|
|
589
|
+
catch {
|
|
590
|
+
(0, nfc_core_1.releaseNfcLock)();
|
|
591
|
+
return { code: types_1.NfcStatusCode.NFC_CONNECT_FAILED, success: false };
|
|
592
|
+
}
|
|
593
|
+
try {
|
|
594
|
+
onCardIdentified?.();
|
|
595
|
+
const response = await (0, nfc_core_1.transceive)([constants_1.CMD_READ_SIG, 0x00]);
|
|
596
|
+
if (!response || response.length < 48) {
|
|
597
|
+
throw new Error('READ_FAILED');
|
|
598
|
+
}
|
|
599
|
+
const signatureHex = (0, utils_1.bytesToHex)(response.slice(0, 48));
|
|
600
|
+
await (0, nfc_core_1.releaseNfcTech)();
|
|
601
|
+
(0, nfc_core_1.releaseNfcLock)();
|
|
602
|
+
return {
|
|
603
|
+
code: types_1.NfcStatusCode.READ_SIG_SUCCESS,
|
|
604
|
+
success: true,
|
|
605
|
+
data: { signature: signatureHex },
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
catch (error) {
|
|
609
|
+
await (0, nfc_core_1.releaseNfcTech)();
|
|
610
|
+
(0, nfc_core_1.releaseNfcLock)();
|
|
611
|
+
const code = (0, types_1.errorToCode)(error);
|
|
612
|
+
return { code, success: false };
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
catch (error) {
|
|
616
|
+
(0, nfc_core_1.releaseNfcLock)();
|
|
617
|
+
throw error;
|
|
618
|
+
}
|
|
619
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -7,14 +7,16 @@
|
|
|
7
7
|
* - Failure : 4xxxx (shared)
|
|
8
8
|
*
|
|
9
9
|
* This library does NOT provide user-facing messages.
|
|
10
|
-
* The caller should map
|
|
10
|
+
* The caller should map NfcStatusCode to their own localised strings.
|
|
11
11
|
*/
|
|
12
|
-
export declare const
|
|
12
|
+
export declare const NfcStatusCode: {
|
|
13
13
|
readonly READ_SUCCESS: 10102;
|
|
14
14
|
readonly READ_NICKNAME_SUCCESS: 10103;
|
|
15
15
|
readonly CHECK_EMPTY: 10104;
|
|
16
16
|
readonly CHECK_HAS_DATA: 10105;
|
|
17
17
|
readonly READ_RETRY_COUNT_SUCCESS: 10106;
|
|
18
|
+
readonly GET_VERSION_SUCCESS: 10107;
|
|
19
|
+
readonly READ_SIG_SUCCESS: 10108;
|
|
18
20
|
readonly INIT_SUCCESS: 10201;
|
|
19
21
|
readonly WRITE_SUCCESS: 10203;
|
|
20
22
|
readonly UPDATE_PASSWORD_SUCCESS: 10204;
|
|
@@ -40,7 +42,7 @@ export declare const ResultCode: {
|
|
|
40
42
|
readonly RETRY_COUNT_EXHAUSTED: 40015;
|
|
41
43
|
};
|
|
42
44
|
export interface NfcResult {
|
|
43
|
-
/** Numeric result code – compare against
|
|
45
|
+
/** Numeric result code – compare against NfcStatusCode constants */
|
|
44
46
|
code: number;
|
|
45
47
|
/** Whether the operation succeeded */
|
|
46
48
|
success: boolean;
|
|
@@ -54,6 +56,16 @@ export interface NfcResult {
|
|
|
54
56
|
retryCount?: number;
|
|
55
57
|
aesKeyHex?: string;
|
|
56
58
|
crc16?: number;
|
|
59
|
+
version?: {
|
|
60
|
+
vendorId: number;
|
|
61
|
+
productType: number;
|
|
62
|
+
productSubtype: number;
|
|
63
|
+
majorVersion: number;
|
|
64
|
+
minorVersion: number;
|
|
65
|
+
storageSize: number;
|
|
66
|
+
protocolType: number;
|
|
67
|
+
};
|
|
68
|
+
signature?: string;
|
|
57
69
|
};
|
|
58
70
|
}
|
|
59
71
|
/**
|
|
@@ -62,7 +74,7 @@ export interface NfcResult {
|
|
|
62
74
|
*/
|
|
63
75
|
export declare function nfcResultRetryCountExhausted(): NfcResult;
|
|
64
76
|
/**
|
|
65
|
-
* Derive a
|
|
77
|
+
* Derive a NfcStatusCode from an error thrown during NFC operations.
|
|
66
78
|
* Handles iOS-specific cancel detection, known error strings, and fallback.
|
|
67
79
|
*/
|
|
68
80
|
export declare function errorToCode(error: any): number;
|
package/dist/types.js
CHANGED
|
@@ -8,23 +8,25 @@
|
|
|
8
8
|
* - Failure : 4xxxx (shared)
|
|
9
9
|
*
|
|
10
10
|
* This library does NOT provide user-facing messages.
|
|
11
|
-
* The caller should map
|
|
11
|
+
* The caller should map NfcStatusCode to their own localised strings.
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.
|
|
14
|
+
exports.NfcStatusCode = void 0;
|
|
15
15
|
exports.nfcResultRetryCountExhausted = nfcResultRetryCountExhausted;
|
|
16
16
|
exports.errorToCode = errorToCode;
|
|
17
17
|
const react_native_1 = require("react-native");
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
19
|
// Unified result codes
|
|
20
20
|
// ---------------------------------------------------------------------------
|
|
21
|
-
exports.
|
|
21
|
+
exports.NfcStatusCode = {
|
|
22
22
|
// Reader success (101xx)
|
|
23
23
|
READ_SUCCESS: 10102,
|
|
24
24
|
READ_NICKNAME_SUCCESS: 10103,
|
|
25
25
|
CHECK_EMPTY: 10104,
|
|
26
26
|
CHECK_HAS_DATA: 10105,
|
|
27
27
|
READ_RETRY_COUNT_SUCCESS: 10106,
|
|
28
|
+
GET_VERSION_SUCCESS: 10107,
|
|
29
|
+
READ_SIG_SUCCESS: 10108,
|
|
28
30
|
// Writer success (102xx)
|
|
29
31
|
INIT_SUCCESS: 10201,
|
|
30
32
|
WRITE_SUCCESS: 10203,
|
|
@@ -57,7 +59,7 @@ exports.ResultCode = {
|
|
|
57
59
|
*/
|
|
58
60
|
function nfcResultRetryCountExhausted() {
|
|
59
61
|
return {
|
|
60
|
-
code: exports.
|
|
62
|
+
code: exports.NfcStatusCode.RETRY_COUNT_EXHAUSTED,
|
|
61
63
|
success: false,
|
|
62
64
|
data: { retryCount: 0 },
|
|
63
65
|
};
|
|
@@ -66,7 +68,7 @@ function nfcResultRetryCountExhausted() {
|
|
|
66
68
|
// Error → code mapping (internal use)
|
|
67
69
|
// ---------------------------------------------------------------------------
|
|
68
70
|
/**
|
|
69
|
-
* Derive a
|
|
71
|
+
* Derive a NfcStatusCode from an error thrown during NFC operations.
|
|
70
72
|
* Handles iOS-specific cancel detection, known error strings, and fallback.
|
|
71
73
|
*/
|
|
72
74
|
function errorToCode(error) {
|
|
@@ -78,34 +80,34 @@ function errorToCode(error) {
|
|
|
78
80
|
if (error &&
|
|
79
81
|
typeof error === 'object' &&
|
|
80
82
|
(error.name === 'UserCancel' || error.constructor?.name === 'UserCancel')) {
|
|
81
|
-
return exports.
|
|
83
|
+
return exports.NfcStatusCode.NFC_USER_CANCELED;
|
|
82
84
|
}
|
|
83
85
|
if (!msg || msg.includes('User Canceled') || msg.includes('Unknown empty error')) {
|
|
84
|
-
return exports.
|
|
86
|
+
return exports.NfcStatusCode.NFC_USER_CANCELED;
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
if (msg.includes('NFC_USER_CANCELED_SIGNAL'))
|
|
88
|
-
return exports.
|
|
90
|
+
return exports.NfcStatusCode.NFC_USER_CANCELED;
|
|
89
91
|
const keywords = [
|
|
90
|
-
['RETRY_COUNT_EXHAUSTED', exports.
|
|
91
|
-
['AUTH_WRONG_PASSWORD', exports.
|
|
92
|
-
['AUTH_INVALID_RESPONSE', exports.
|
|
93
|
-
['AUTH_VERIFY_FAILED', exports.
|
|
94
|
-
['READ_FAILED', exports.
|
|
95
|
-
['WRITE_FAILED', exports.
|
|
96
|
-
['CRC16_CHECK_FAILED', exports.
|
|
97
|
-
['EMPTY_CARD', exports.
|
|
98
|
-
['INVALID_CARD_DATA', exports.
|
|
99
|
-
['INVALID_MNEMONIC', exports.
|
|
100
|
-
['UNSUPPORTED_MNEMONIC_LENGTH', exports.
|
|
101
|
-
['NFC_LOCK_TIMEOUT', exports.
|
|
102
|
-
['NFC_CONNECT_FAILED', exports.
|
|
103
|
-
['connection failed', exports.
|
|
104
|
-
['transceive fail', exports.
|
|
92
|
+
['RETRY_COUNT_EXHAUSTED', exports.NfcStatusCode.RETRY_COUNT_EXHAUSTED],
|
|
93
|
+
['AUTH_WRONG_PASSWORD', exports.NfcStatusCode.AUTH_WRONG_PASSWORD],
|
|
94
|
+
['AUTH_INVALID_RESPONSE', exports.NfcStatusCode.AUTH_INVALID_RESPONSE],
|
|
95
|
+
['AUTH_VERIFY_FAILED', exports.NfcStatusCode.AUTH_VERIFY_FAILED],
|
|
96
|
+
['READ_FAILED', exports.NfcStatusCode.READ_FAILED],
|
|
97
|
+
['WRITE_FAILED', exports.NfcStatusCode.WRITE_FAILED],
|
|
98
|
+
['CRC16_CHECK_FAILED', exports.NfcStatusCode.CRC16_CHECK_FAILED],
|
|
99
|
+
['EMPTY_CARD', exports.NfcStatusCode.CHECK_EMPTY],
|
|
100
|
+
['INVALID_CARD_DATA', exports.NfcStatusCode.INVALID_CARD_DATA],
|
|
101
|
+
['INVALID_MNEMONIC', exports.NfcStatusCode.INVALID_MNEMONIC],
|
|
102
|
+
['UNSUPPORTED_MNEMONIC_LENGTH', exports.NfcStatusCode.UNSUPPORTED_MNEMONIC_LENGTH],
|
|
103
|
+
['NFC_LOCK_TIMEOUT', exports.NfcStatusCode.NFC_LOCK_TIMEOUT],
|
|
104
|
+
['NFC_CONNECT_FAILED', exports.NfcStatusCode.NFC_CONNECT_FAILED],
|
|
105
|
+
['connection failed', exports.NfcStatusCode.NFC_CONNECT_FAILED],
|
|
106
|
+
['transceive fail', exports.NfcStatusCode.NFC_CONNECT_FAILED],
|
|
105
107
|
];
|
|
106
108
|
for (const [keyword, code] of keywords) {
|
|
107
109
|
if (msg.includes(keyword))
|
|
108
110
|
return code;
|
|
109
111
|
}
|
|
110
|
-
return exports.
|
|
112
|
+
return exports.NfcStatusCode.UNKNOWN_ERROR;
|
|
111
113
|
}
|
package/dist/utils.d.ts
CHANGED
|
@@ -15,6 +15,22 @@ export declare function crc16ToBytes(crc16: number): Uint8Array;
|
|
|
15
15
|
* @throws if buffer is shorter than 2 bytes.
|
|
16
16
|
*/
|
|
17
17
|
export declare function extractCRC16(data: Uint8Array): number;
|
|
18
|
+
/**
|
|
19
|
+
* Get entropy length and description for a mnemonic type ID.
|
|
20
|
+
* @throws INVALID_CARD_DATA if the type ID is unknown.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getMnemonicTypeInfo(typeId: number): {
|
|
23
|
+
entropyLength: number;
|
|
24
|
+
description: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Get type ID and description for a given entropy byte length.
|
|
28
|
+
* @throws UNSUPPORTED_MNEMONIC_LENGTH if the length is not recognized.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getMnemonicTypeByEntropyLength(entropyLength: number): {
|
|
31
|
+
typeId: number;
|
|
32
|
+
description: string;
|
|
33
|
+
};
|
|
18
34
|
/**
|
|
19
35
|
* Validate whether raw card data contains a valid mnemonic payload.
|
|
20
36
|
* Checks type byte, length, and CRC16 — does NOT decode the mnemonic.
|
package/dist/utils.js
CHANGED
|
@@ -8,6 +8,8 @@ exports.hexToBytes = hexToBytes;
|
|
|
8
8
|
exports.calculateCRC16 = calculateCRC16;
|
|
9
9
|
exports.crc16ToBytes = crc16ToBytes;
|
|
10
10
|
exports.extractCRC16 = extractCRC16;
|
|
11
|
+
exports.getMnemonicTypeInfo = getMnemonicTypeInfo;
|
|
12
|
+
exports.getMnemonicTypeByEntropyLength = getMnemonicTypeByEntropyLength;
|
|
11
13
|
exports.validateMnemonicPayload = validateMnemonicPayload;
|
|
12
14
|
const constants_1 = require("./constants");
|
|
13
15
|
// ---------------------------------------------------------------------------
|
|
@@ -23,7 +25,7 @@ function bytesToHex(bytes) {
|
|
|
23
25
|
function hexToBytes(hex) {
|
|
24
26
|
const bytes = new Uint8Array(hex.length / 2);
|
|
25
27
|
for (let i = 0; i < hex.length; i += 2) {
|
|
26
|
-
bytes[i / 2] = parseInt(hex.
|
|
28
|
+
bytes[i / 2] = parseInt(hex.substring(i, i + 2), 16);
|
|
27
29
|
}
|
|
28
30
|
return bytes;
|
|
29
31
|
}
|
|
@@ -64,6 +66,37 @@ function extractCRC16(data) {
|
|
|
64
66
|
return data[data.length - 2] | (data[data.length - 1] << 8);
|
|
65
67
|
}
|
|
66
68
|
// ---------------------------------------------------------------------------
|
|
69
|
+
// Mnemonic type info
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
/** Mnemonic type info table: [typeId, entropyLength, description] */
|
|
72
|
+
const MNEMONIC_TYPE_TABLE = [
|
|
73
|
+
[constants_1.MNEMONIC_TYPE_12, 16, '12 words (128-bit)'],
|
|
74
|
+
[constants_1.MNEMONIC_TYPE_15, 20, '15 words (160-bit)'],
|
|
75
|
+
[constants_1.MNEMONIC_TYPE_18, 24, '18 words (192-bit)'],
|
|
76
|
+
[constants_1.MNEMONIC_TYPE_21, 28, '21 words (224-bit)'],
|
|
77
|
+
[constants_1.MNEMONIC_TYPE_24, 32, '24 words (256-bit)'],
|
|
78
|
+
];
|
|
79
|
+
/**
|
|
80
|
+
* Get entropy length and description for a mnemonic type ID.
|
|
81
|
+
* @throws INVALID_CARD_DATA if the type ID is unknown.
|
|
82
|
+
*/
|
|
83
|
+
function getMnemonicTypeInfo(typeId) {
|
|
84
|
+
const entry = MNEMONIC_TYPE_TABLE.find(([id]) => id === typeId);
|
|
85
|
+
if (!entry)
|
|
86
|
+
throw new Error('INVALID_CARD_DATA');
|
|
87
|
+
return { entropyLength: entry[1], description: entry[2] };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get type ID and description for a given entropy byte length.
|
|
91
|
+
* @throws UNSUPPORTED_MNEMONIC_LENGTH if the length is not recognized.
|
|
92
|
+
*/
|
|
93
|
+
function getMnemonicTypeByEntropyLength(entropyLength) {
|
|
94
|
+
const entry = MNEMONIC_TYPE_TABLE.find(([, len]) => len === entropyLength);
|
|
95
|
+
if (!entry)
|
|
96
|
+
throw new Error('UNSUPPORTED_MNEMONIC_LENGTH');
|
|
97
|
+
return { typeId: entry[0], description: entry[2] };
|
|
98
|
+
}
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
67
100
|
// Mnemonic payload validation
|
|
68
101
|
// ---------------------------------------------------------------------------
|
|
69
102
|
/**
|
|
@@ -78,32 +111,7 @@ function validateMnemonicPayload(data) {
|
|
|
78
111
|
throw new Error('INVALID_CARD_DATA');
|
|
79
112
|
if (data.every(b => b === 0))
|
|
80
113
|
throw new Error('EMPTY_CARD');
|
|
81
|
-
const
|
|
82
|
-
let entropyLength;
|
|
83
|
-
let typeStr;
|
|
84
|
-
switch (typeId) {
|
|
85
|
-
case constants_1.MNEMONIC_TYPE_12:
|
|
86
|
-
entropyLength = 16;
|
|
87
|
-
typeStr = '12 words (128-bit)';
|
|
88
|
-
break;
|
|
89
|
-
case constants_1.MNEMONIC_TYPE_15:
|
|
90
|
-
entropyLength = 20;
|
|
91
|
-
typeStr = '15 words (160-bit)';
|
|
92
|
-
break;
|
|
93
|
-
case constants_1.MNEMONIC_TYPE_18:
|
|
94
|
-
entropyLength = 24;
|
|
95
|
-
typeStr = '18 words (192-bit)';
|
|
96
|
-
break;
|
|
97
|
-
case constants_1.MNEMONIC_TYPE_21:
|
|
98
|
-
entropyLength = 28;
|
|
99
|
-
typeStr = '21 words (224-bit)';
|
|
100
|
-
break;
|
|
101
|
-
case constants_1.MNEMONIC_TYPE_24:
|
|
102
|
-
entropyLength = 32;
|
|
103
|
-
typeStr = '24 words (256-bit)';
|
|
104
|
-
break;
|
|
105
|
-
default: throw new Error('INVALID_CARD_DATA');
|
|
106
|
-
}
|
|
114
|
+
const { entropyLength, description } = getMnemonicTypeInfo(data[0]);
|
|
107
115
|
const expectedTotal = 1 + entropyLength + 2;
|
|
108
116
|
if (data.length < expectedTotal)
|
|
109
117
|
throw new Error('INVALID_CARD_DATA');
|
|
@@ -112,5 +120,5 @@ function validateMnemonicPayload(data) {
|
|
|
112
120
|
const calcCRC = calculateCRC16(dataBlock);
|
|
113
121
|
if (storedCRC !== calcCRC)
|
|
114
122
|
throw new Error('CRC16_CHECK_FAILED');
|
|
115
|
-
return { type:
|
|
123
|
+
return { type: description };
|
|
116
124
|
}
|