@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/README.md
CHANGED
|
@@ -4,7 +4,7 @@ English | [中文](./README.zh.md)
|
|
|
4
4
|
|
|
5
5
|
React Native NFC read/write library for **MIFARE Ultralight AES** (MF0AES(H)20), designed for LiteCard mnemonic storage.
|
|
6
6
|
|
|
7
|
-
> **Design principle**: The library only returns status codes (`code`) and data (`data`). It does **not** provide user-facing messages. The caller should map `
|
|
7
|
+
> **Design principle**: The library only returns status codes (`code`) and data (`data`). It does **not** provide user-facing messages. The caller should map `NfcStatusCode` to their own localised strings.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
@@ -15,11 +15,13 @@ React Native NFC read/write library for **MIFARE Ultralight AES** (MF0AES(H)20),
|
|
|
15
15
|
| Read nickname | `readUserNickname()` | Read user nickname from card |
|
|
16
16
|
| Read retry count | `readMnemonicRetryCount()` | Read PIN retry counter |
|
|
17
17
|
| Reset retry count | `resetRetryCountTo10()` | Reset PIN retry counter to default (10) |
|
|
18
|
-
| Initialize card | `initializeCard()` |
|
|
18
|
+
| Initialize card | `initializeCard()` | Authenticate with default password, write mnemonic + set new password |
|
|
19
19
|
| Update card | `updateCard()` | Update mnemonic & password (old password required) |
|
|
20
20
|
| Change password | `updatePassword()` | Change password only (old password required) |
|
|
21
21
|
| Write nickname | `writeUserNickname()` | Write user nickname to card |
|
|
22
|
-
| Reset card | `resetCard()` | Wipe mnemonic data, set a new password |
|
|
22
|
+
| Reset card | `resetCard()` | Wipe mnemonic data, set a new password, keep protection enabled |
|
|
23
|
+
| Card version | `getCardVersion()` | Read card product version info (no auth required) |
|
|
24
|
+
| Originality check | `readOriginality()` | Read ECC originality signature for NXP authenticity verification |
|
|
23
25
|
|
|
24
26
|
## Installation
|
|
25
27
|
|
|
@@ -39,7 +41,7 @@ npm install react-native react-native-nfc-manager
|
|
|
39
41
|
|
|
40
42
|
```typescript
|
|
41
43
|
import {
|
|
42
|
-
|
|
44
|
+
NfcStatusCode,
|
|
43
45
|
checkCard,
|
|
44
46
|
readMnemonic,
|
|
45
47
|
initializeCard,
|
|
@@ -50,6 +52,8 @@ import {
|
|
|
50
52
|
resetCard,
|
|
51
53
|
readMnemonicRetryCount,
|
|
52
54
|
resetRetryCountTo10,
|
|
55
|
+
getCardVersion,
|
|
56
|
+
readOriginality,
|
|
53
57
|
} from '@ukeyfe/react-native-nfc-litecard';
|
|
54
58
|
```
|
|
55
59
|
|
|
@@ -62,9 +66,9 @@ Detect card status (empty / has data).
|
|
|
62
66
|
**Without password (quick probe):**
|
|
63
67
|
```typescript
|
|
64
68
|
const result = await checkCard();
|
|
65
|
-
if (result.code ===
|
|
69
|
+
if (result.code === NfcStatusCode.CHECK_EMPTY) {
|
|
66
70
|
// Empty card – ready to initialize
|
|
67
|
-
} else if (result.code ===
|
|
71
|
+
} else if (result.code === NfcStatusCode.CHECK_HAS_DATA) {
|
|
68
72
|
// Has data (or read-protected, cannot determine)
|
|
69
73
|
}
|
|
70
74
|
```
|
|
@@ -72,11 +76,11 @@ if (result.code === ResultCode.CHECK_EMPTY) {
|
|
|
72
76
|
**With password (authenticated deep check, for read-protected cards):**
|
|
73
77
|
```typescript
|
|
74
78
|
const result = await checkCard('password');
|
|
75
|
-
if (result.code ===
|
|
79
|
+
if (result.code === NfcStatusCode.CHECK_EMPTY) {
|
|
76
80
|
// Empty card – ready to write
|
|
77
|
-
} else if (result.code ===
|
|
81
|
+
} else if (result.code === NfcStatusCode.CHECK_HAS_DATA) {
|
|
78
82
|
// Valid mnemonic backup exists, type: result.data?.type
|
|
79
|
-
} else if (result.code ===
|
|
83
|
+
} else if (result.code === NfcStatusCode.AUTH_WRONG_PASSWORD) {
|
|
80
84
|
// Wrong password
|
|
81
85
|
}
|
|
82
86
|
```
|
|
@@ -90,10 +94,10 @@ if (result.code === ResultCode.CHECK_EMPTY) {
|
|
|
90
94
|
**Result codes:**
|
|
91
95
|
| Code | Meaning |
|
|
92
96
|
|------|---------|
|
|
93
|
-
| `
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
+
| `NfcStatusCode.CHECK_EMPTY` (10104) | Empty card – no mnemonic data |
|
|
98
|
+
| `NfcStatusCode.CHECK_HAS_DATA` (10105) | Card has data. When a password is supplied, `data.type` contains the mnemonic type (e.g. `"12 words (128-bit)"`). |
|
|
99
|
+
| `NfcStatusCode.AUTH_WRONG_PASSWORD` (40002) | Wrong password (only possible when a password is provided) |
|
|
100
|
+
| `NfcStatusCode.NFC_CONNECT_FAILED` (40001) | NFC connection failed – card not tapped or device unsupported |
|
|
97
101
|
|
|
98
102
|
---
|
|
99
103
|
|
|
@@ -129,16 +133,17 @@ if (result.success) {
|
|
|
129
133
|
|
|
130
134
|
---
|
|
131
135
|
|
|
132
|
-
### `initializeCard(mnemonic,
|
|
136
|
+
### `initializeCard(mnemonic, newPassword, defaultPassword, onCardIdentified?)`
|
|
133
137
|
|
|
134
|
-
Initialize a
|
|
138
|
+
Initialize a card: authenticate with the factory-default password, write the mnemonic, and set a new password. The card must already have AES protection enabled (factory default).
|
|
135
139
|
|
|
136
140
|
```typescript
|
|
137
141
|
const result = await initializeCard(
|
|
138
142
|
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
|
|
139
|
-
'your-password'
|
|
143
|
+
'your-password',
|
|
144
|
+
'000000' // factory default password
|
|
140
145
|
);
|
|
141
|
-
if (result.code ===
|
|
146
|
+
if (result.code === NfcStatusCode.INIT_SUCCESS) {
|
|
142
147
|
console.log('Initialization successful');
|
|
143
148
|
}
|
|
144
149
|
```
|
|
@@ -147,8 +152,9 @@ if (result.code === ResultCode.INIT_SUCCESS) {
|
|
|
147
152
|
| Parameter | Type | Required | Description |
|
|
148
153
|
|-----------|------|----------|-------------|
|
|
149
154
|
| `mnemonic` | `string` | Yes | BIP-39 mnemonic; supports 12/15/18/21/24 words. The library converts to entropy + CRC16 and writes to the card. |
|
|
150
|
-
| `
|
|
151
|
-
| `
|
|
155
|
+
| `newPassword` | `string` | Yes | New protection password; used to derive the AES-128 key written to the card. |
|
|
156
|
+
| `defaultPassword` | `string` | Yes | Current (factory-default) password on the card for authentication. |
|
|
157
|
+
| `onCardIdentified` | `() => void` | No | Called after successful authentication and before writing begins; use for UI such as "writing". |
|
|
152
158
|
|
|
153
159
|
---
|
|
154
160
|
|
|
@@ -158,7 +164,7 @@ Update the card: authenticate with the old password, then write the new mnemonic
|
|
|
158
164
|
|
|
159
165
|
```typescript
|
|
160
166
|
const result = await updateCard('old-password', 'new-password', 'new mnemonic words ...');
|
|
161
|
-
if (result.code ===
|
|
167
|
+
if (result.code === NfcStatusCode.WRITE_SUCCESS) {
|
|
162
168
|
console.log('Update successful');
|
|
163
169
|
}
|
|
164
170
|
```
|
|
@@ -169,10 +175,10 @@ if (result.code === ResultCode.WRITE_SUCCESS) {
|
|
|
169
175
|
const result = await updateCard('old-password', 'new-password', 'mnemonic ...', undefined, {
|
|
170
176
|
precheckExistingMnemonic: true,
|
|
171
177
|
});
|
|
172
|
-
if (result.code ===
|
|
178
|
+
if (result.code === NfcStatusCode.PRECHECK_HAS_BACKUP) {
|
|
173
179
|
// Card already has a valid mnemonic backup; write was skipped
|
|
174
180
|
console.log('Backup exists, type:', result.data?.type);
|
|
175
|
-
} else if (result.code ===
|
|
181
|
+
} else if (result.code === NfcStatusCode.WRITE_SUCCESS) {
|
|
176
182
|
console.log('Write successful');
|
|
177
183
|
}
|
|
178
184
|
```
|
|
@@ -194,7 +200,7 @@ Change the password only; mnemonic data on the card is unchanged. The retry coun
|
|
|
194
200
|
|
|
195
201
|
```typescript
|
|
196
202
|
const result = await updatePassword('old-password', 'new-password');
|
|
197
|
-
if (result.code ===
|
|
203
|
+
if (result.code === NfcStatusCode.UPDATE_PASSWORD_SUCCESS) {
|
|
198
204
|
console.log('Password updated');
|
|
199
205
|
}
|
|
200
206
|
```
|
|
@@ -214,7 +220,7 @@ Write a user nickname to the card. The nickname is UTF-8 encoded, max 12 bytes (
|
|
|
214
220
|
|
|
215
221
|
```typescript
|
|
216
222
|
const result = await writeUserNickname('your-password', 'MyCard');
|
|
217
|
-
if (result.code ===
|
|
223
|
+
if (result.code === NfcStatusCode.WRITE_NICKNAME_SUCCESS) {
|
|
218
224
|
console.log('Nickname written');
|
|
219
225
|
}
|
|
220
226
|
```
|
|
@@ -253,7 +259,7 @@ Reset the card: wipe mnemonic data, set a new password, and disable read/write p
|
|
|
253
259
|
|
|
254
260
|
```typescript
|
|
255
261
|
const result = await resetCard('old-password', 'new-password');
|
|
256
|
-
if (result.code ===
|
|
262
|
+
if (result.code === NfcStatusCode.RESET_SUCCESS) {
|
|
257
263
|
console.log('Reset successful');
|
|
258
264
|
}
|
|
259
265
|
```
|
|
@@ -302,6 +308,34 @@ const result = await resetRetryCountTo10();
|
|
|
302
308
|
|
|
303
309
|
---
|
|
304
310
|
|
|
311
|
+
### `getCardVersion(onCardIdentified?)`
|
|
312
|
+
|
|
313
|
+
Read card product version info. No authentication required.
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
const result = await getCardVersion();
|
|
317
|
+
if (result.success) {
|
|
318
|
+
console.log('Vendor:', result.data?.version?.vendorId); // 0x04 = NXP
|
|
319
|
+
console.log('Type:', result.data?.version?.productType); // 0x03 = Ultralight
|
|
320
|
+
console.log('Version:', result.data?.version?.majorVersion); // 0x04 = AES
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
### `readOriginality(onCardIdentified?)`
|
|
327
|
+
|
|
328
|
+
Read the ECC originality signature to verify the card is a genuine NXP product. No authentication required.
|
|
329
|
+
|
|
330
|
+
```typescript
|
|
331
|
+
const result = await readOriginality();
|
|
332
|
+
if (result.success) {
|
|
333
|
+
console.log('Signature:', result.data?.signature); // 96-char hex string
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
---
|
|
338
|
+
|
|
305
339
|
### NFC Lock Management
|
|
306
340
|
|
|
307
341
|
For app-level NFC session lifecycle management:
|
|
@@ -328,7 +362,7 @@ All APIs return a unified `NfcResult`:
|
|
|
328
362
|
|
|
329
363
|
```typescript
|
|
330
364
|
interface NfcResult {
|
|
331
|
-
code: number; // Status code – compare against
|
|
365
|
+
code: number; // Status code – compare against NfcStatusCode
|
|
332
366
|
success: boolean; // Whether the operation succeeded
|
|
333
367
|
data?: { // Optional data, only present for some operations
|
|
334
368
|
mnemonic?: string;
|
|
@@ -346,27 +380,27 @@ interface NfcResult {
|
|
|
346
380
|
## Error Handling Example
|
|
347
381
|
|
|
348
382
|
```typescript
|
|
349
|
-
import {
|
|
383
|
+
import { NfcStatusCode, readMnemonic } from '@ukeyfe/react-native-nfc-litecard';
|
|
350
384
|
|
|
351
385
|
const result = await readMnemonic('password');
|
|
352
386
|
|
|
353
387
|
switch (result.code) {
|
|
354
|
-
case
|
|
388
|
+
case NfcStatusCode.READ_SUCCESS:
|
|
355
389
|
console.log('Read successful:', result.data?.mnemonic);
|
|
356
390
|
break;
|
|
357
|
-
case
|
|
391
|
+
case NfcStatusCode.AUTH_WRONG_PASSWORD:
|
|
358
392
|
alert('Wrong password');
|
|
359
393
|
break;
|
|
360
|
-
case
|
|
394
|
+
case NfcStatusCode.NFC_CONNECT_FAILED:
|
|
361
395
|
alert('NFC connection failed, please re-tap the card');
|
|
362
396
|
break;
|
|
363
|
-
case
|
|
397
|
+
case NfcStatusCode.NFC_USER_CANCELED:
|
|
364
398
|
// iOS user cancelled – handle silently
|
|
365
399
|
break;
|
|
366
|
-
case
|
|
400
|
+
case NfcStatusCode.READ_TIMEOUT:
|
|
367
401
|
alert('Read timeout – remove and re-tap the card');
|
|
368
402
|
break;
|
|
369
|
-
case
|
|
403
|
+
case NfcStatusCode.RETRY_COUNT_EXHAUSTED:
|
|
370
404
|
alert('Retry count exhausted – card is locked');
|
|
371
405
|
break;
|
|
372
406
|
default:
|
|
@@ -385,6 +419,8 @@ switch (result.code) {
|
|
|
385
419
|
| `CHECK_EMPTY` | 10104 | Empty card |
|
|
386
420
|
| `CHECK_HAS_DATA` | 10105 | Card has data |
|
|
387
421
|
| `READ_RETRY_COUNT_SUCCESS` | 10106 | Retry count read successful |
|
|
422
|
+
| `GET_VERSION_SUCCESS` | 10107 | Card version read successful |
|
|
423
|
+
| `READ_SIG_SUCCESS` | 10108 | Originality signature read successful |
|
|
388
424
|
| `INIT_SUCCESS` | 10201 | Initialization successful |
|
|
389
425
|
| `WRITE_SUCCESS` | 10203 | Write/update successful |
|
|
390
426
|
| `UPDATE_PASSWORD_SUCCESS` | 10204 | Password change successful |
|
|
@@ -434,6 +470,7 @@ The card stores BIP-39 mnemonics using entropy compression:
|
|
|
434
470
|
- **SHA-256 key derivation**: password → SHA-256 → first 16 bytes as AES key
|
|
435
471
|
- **CRC16-Modbus checksum**: data integrity verification
|
|
436
472
|
- **PIN retry counter**: auto-decrements on wrong password, resets to 10 on success
|
|
473
|
+
- **CMAC secure messaging**: all commands after authentication are protected with AES-CMAC (NIST 800-38B) to prevent tampering
|
|
437
474
|
- **Secure random**: authentication uses `crypto.getRandomValues()` (requires Hermes ≥ 0.72 or `react-native-get-random-values` polyfill)
|
|
438
475
|
|
|
439
476
|
## Project Structure
|
|
@@ -442,7 +479,7 @@ The card stores BIP-39 mnemonics using entropy compression:
|
|
|
442
479
|
src/
|
|
443
480
|
├── index.ts # Public API exports
|
|
444
481
|
├── constants.ts # Shared constants (page addresses, NFC commands, mnemonic types)
|
|
445
|
-
├── types.ts # Unified
|
|
482
|
+
├── types.ts # Unified NfcStatusCode, NfcResult interface, error mapping
|
|
446
483
|
├── crypto.ts # AES encrypt/decrypt, key derivation, secure random
|
|
447
484
|
├── utils.ts # CRC16, hex conversion, array utilities
|
|
448
485
|
├── nfc-core.ts # NFC lock, transceive, authentication, retry counter
|