@ukeyfe/react-native-nfc-litecard 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +218 -216
- package/{README.en.md → README.zh.md} +7 -6
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +8 -1
- package/dist/writer.d.ts +4 -2
- package/dist/writer.js +22 -13
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
1
|
# @ukeyfe/react-native-nfc-litecard
|
|
2
2
|
|
|
3
|
-
[
|
|
3
|
+
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
|
-
>
|
|
7
|
+
> **Design principle**: The library only returns status codes (`code`) and data (`data`). It does **not** provide user-facing messages. The caller should map `ResultCode` to their own localised strings.
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Features
|
|
10
10
|
|
|
11
|
-
|
|
|
12
|
-
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
16
|
-
|
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
11
|
+
| Feature | Method | Description |
|
|
12
|
+
|---------|--------|-------------|
|
|
13
|
+
| Check card | `checkCard()` | Detect whether the card is empty or contains data |
|
|
14
|
+
| Read mnemonic | `readMnemonic()` | Read BIP-39 mnemonic (password required) |
|
|
15
|
+
| Read nickname | `readUserNickname()` | Read user nickname from card |
|
|
16
|
+
| Read retry count | `readMnemonicRetryCount()` | Read PIN retry counter |
|
|
17
|
+
| Reset retry count | `resetRetryCountTo10()` | Reset PIN retry counter to default (10) |
|
|
18
|
+
| Initialize card | `initializeCard()` | Write mnemonic + set password on a blank card |
|
|
19
|
+
| Update card | `updateCard()` | Update mnemonic & password (old password required) |
|
|
20
|
+
| Change password | `updatePassword()` | Change password only (old password required) |
|
|
21
|
+
| Write nickname | `writeUserNickname()` | Write user nickname to card |
|
|
22
|
+
| Reset card | `resetCard()` | Wipe mnemonic data, set a new password |
|
|
23
23
|
|
|
24
|
-
##
|
|
24
|
+
## Installation
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
27
|
npm install @ukeyfe/react-native-nfc-litecard
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
###
|
|
30
|
+
### Peer Dependencies
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
This library requires the following peer dependencies:
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
35
|
npm install react-native react-native-nfc-manager
|
|
36
36
|
```
|
|
37
37
|
|
|
38
|
-
##
|
|
38
|
+
## Quick Start
|
|
39
39
|
|
|
40
40
|
```typescript
|
|
41
41
|
import {
|
|
@@ -53,85 +53,85 @@ import {
|
|
|
53
53
|
} from '@ukeyfe/react-native-nfc-litecard';
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
## API
|
|
56
|
+
## API Reference
|
|
57
57
|
|
|
58
58
|
### `checkCard(password?, onCardIdentified?)`
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
Detect card status (empty / has data).
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
**Without password (quick probe):**
|
|
63
63
|
```typescript
|
|
64
64
|
const result = await checkCard();
|
|
65
65
|
if (result.code === ResultCode.CHECK_EMPTY) {
|
|
66
|
-
//
|
|
66
|
+
// Empty card – ready to initialize
|
|
67
67
|
} else if (result.code === ResultCode.CHECK_HAS_DATA) {
|
|
68
|
-
//
|
|
68
|
+
// Has data (or read-protected, cannot determine)
|
|
69
69
|
}
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
-
|
|
72
|
+
**With password (authenticated deep check, for read-protected cards):**
|
|
73
73
|
```typescript
|
|
74
74
|
const result = await checkCard('password');
|
|
75
75
|
if (result.code === ResultCode.CHECK_EMPTY) {
|
|
76
|
-
//
|
|
76
|
+
// Empty card – ready to write
|
|
77
77
|
} else if (result.code === ResultCode.CHECK_HAS_DATA) {
|
|
78
|
-
//
|
|
78
|
+
// Valid mnemonic backup exists, type: result.data?.type
|
|
79
79
|
} else if (result.code === ResultCode.AUTH_WRONG_PASSWORD) {
|
|
80
|
-
//
|
|
80
|
+
// Wrong password
|
|
81
81
|
}
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
|
86
|
-
|
|
87
|
-
| `password` | `string` |
|
|
88
|
-
| `onCardIdentified` | `() => void` |
|
|
84
|
+
**Parameters:**
|
|
85
|
+
| Parameter | Type | Required | Description |
|
|
86
|
+
|-----------|------|----------|-------------|
|
|
87
|
+
| `password` | `string` | No | Card protection password. If omitted, the library reads directly (suited for unencrypted cards). If provided, AES authentication runs first, then full data is read and CRC16 is verified (suited for read-protected cards; more accurate). |
|
|
88
|
+
| `onCardIdentified` | `() => void` | No | Called after the NFC session is established; use for UI such as "card detected". |
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
|
92
|
-
|
|
93
|
-
| `ResultCode.CHECK_EMPTY` (10104) |
|
|
94
|
-
| `ResultCode.CHECK_HAS_DATA` (10105) |
|
|
95
|
-
| `ResultCode.AUTH_WRONG_PASSWORD` (40002) |
|
|
96
|
-
| `ResultCode.NFC_CONNECT_FAILED` (40001) | NFC
|
|
90
|
+
**Result codes:**
|
|
91
|
+
| Code | Meaning |
|
|
92
|
+
|------|---------|
|
|
93
|
+
| `ResultCode.CHECK_EMPTY` (10104) | Empty card – no mnemonic data |
|
|
94
|
+
| `ResultCode.CHECK_HAS_DATA` (10105) | Card has data. When a password is supplied, `data.type` contains the mnemonic type (e.g. `"12 words (128-bit)"`). |
|
|
95
|
+
| `ResultCode.AUTH_WRONG_PASSWORD` (40002) | Wrong password (only possible when a password is provided) |
|
|
96
|
+
| `ResultCode.NFC_CONNECT_FAILED` (40001) | NFC connection failed – card not tapped or device unsupported |
|
|
97
97
|
|
|
98
98
|
---
|
|
99
99
|
|
|
100
100
|
### `readMnemonic(password, onCardIdentified?)`
|
|
101
101
|
|
|
102
|
-
|
|
102
|
+
Read the mnemonic (password authentication required). The retry counter is decremented before authentication and reset to 10 after a successful authentication.
|
|
103
103
|
|
|
104
104
|
```typescript
|
|
105
105
|
const result = await readMnemonic('your-password');
|
|
106
106
|
if (result.success) {
|
|
107
|
-
console.log('
|
|
108
|
-
console.log('
|
|
109
|
-
console.log('
|
|
110
|
-
console.log('
|
|
107
|
+
console.log('Mnemonic:', result.data?.mnemonic);
|
|
108
|
+
console.log('Type:', result.data?.type); // "12 words (128-bit)"
|
|
109
|
+
console.log('Nickname:', result.data?.nickname);
|
|
110
|
+
console.log('Retry count:', result.data?.retryCount);
|
|
111
111
|
}
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
|
|
115
|
-
|
|
|
116
|
-
|
|
117
|
-
| `password` | `string` |
|
|
118
|
-
| `onCardIdentified` | `() => void` |
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
|
122
|
-
|
|
123
|
-
| `mnemonic` | `string` | BIP-39
|
|
124
|
-
| `type` | `string` |
|
|
125
|
-
| `entropyHex` | `string` |
|
|
126
|
-
| `rawBytes` | `string` |
|
|
127
|
-
| `nickname` | `string` |
|
|
128
|
-
| `retryCount` | `number` |
|
|
114
|
+
**Parameters:**
|
|
115
|
+
| Parameter | Type | Required | Description |
|
|
116
|
+
|-----------|------|----------|-------------|
|
|
117
|
+
| `password` | `string` | Yes | Card protection password for AES-128 authentication |
|
|
118
|
+
| `onCardIdentified` | `() => void` | No | Called after successful authentication and before reading data; use for UI such as "reading". |
|
|
119
|
+
|
|
120
|
+
**Returned `data` fields:**
|
|
121
|
+
| Field | Type | Description |
|
|
122
|
+
|-------|------|-------------|
|
|
123
|
+
| `mnemonic` | `string` | BIP-39 mnemonic (e.g. `"abandon abandon ... about"`) |
|
|
124
|
+
| `type` | `string` | Mnemonic type (e.g. `"12 words (128-bit)"`, `"24 words (256-bit)"`) |
|
|
125
|
+
| `entropyHex` | `string` | Entropy as a hexadecimal string |
|
|
126
|
+
| `rawBytes` | `string` | Hex string of raw on-card data (type + entropy) |
|
|
127
|
+
| `nickname` | `string` | User nickname if set on the card |
|
|
128
|
+
| `retryCount` | `number` | Retry count after successful authentication (normally 10) |
|
|
129
129
|
|
|
130
130
|
---
|
|
131
131
|
|
|
132
132
|
### `initializeCard(mnemonic, password, onCardIdentified?)`
|
|
133
133
|
|
|
134
|
-
|
|
134
|
+
Initialize a blank card: convert the mnemonic to BIP-39 entropy, write it to the card, enable AES password protection, and require read/write authentication.
|
|
135
135
|
|
|
136
136
|
```typescript
|
|
137
137
|
const result = await initializeCard(
|
|
@@ -139,170 +139,172 @@ const result = await initializeCard(
|
|
|
139
139
|
'your-password'
|
|
140
140
|
);
|
|
141
141
|
if (result.code === ResultCode.INIT_SUCCESS) {
|
|
142
|
-
console.log('
|
|
142
|
+
console.log('Initialization successful');
|
|
143
143
|
}
|
|
144
144
|
```
|
|
145
145
|
|
|
146
|
-
|
|
147
|
-
|
|
|
148
|
-
|
|
149
|
-
| `mnemonic` | `string` |
|
|
150
|
-
| `password` | `string` |
|
|
151
|
-
| `onCardIdentified` | `() => void` |
|
|
146
|
+
**Parameters:**
|
|
147
|
+
| Parameter | Type | Required | Description |
|
|
148
|
+
|-----------|------|----------|-------------|
|
|
149
|
+
| `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
|
+
| `password` | `string` | Yes | Protection password to set; used to derive the AES-128 key written to the card. |
|
|
151
|
+
| `onCardIdentified` | `() => void` | No | Called after NFC connection is established and before writing begins; use for UI such as "writing". |
|
|
152
152
|
|
|
153
153
|
---
|
|
154
154
|
|
|
155
155
|
### `updateCard(oldPassword, newPassword, newMnemonic, onCardIdentified?, options?)`
|
|
156
156
|
|
|
157
|
-
|
|
157
|
+
Update the card: authenticate with the old password, then write the new mnemonic and new password. The retry counter is decremented automatically before authentication.
|
|
158
158
|
|
|
159
159
|
```typescript
|
|
160
160
|
const result = await updateCard('old-password', 'new-password', 'new mnemonic words ...');
|
|
161
161
|
if (result.code === ResultCode.WRITE_SUCCESS) {
|
|
162
|
-
console.log('
|
|
162
|
+
console.log('Update successful');
|
|
163
163
|
}
|
|
164
164
|
```
|
|
165
165
|
|
|
166
|
-
|
|
166
|
+
**Pre-check for existing backup:**
|
|
167
167
|
|
|
168
168
|
```typescript
|
|
169
169
|
const result = await updateCard('old-password', 'new-password', 'mnemonic ...', undefined, {
|
|
170
170
|
precheckExistingMnemonic: true,
|
|
171
171
|
});
|
|
172
172
|
if (result.code === ResultCode.PRECHECK_HAS_BACKUP) {
|
|
173
|
-
|
|
173
|
+
// Card already has a valid mnemonic backup; write was skipped
|
|
174
|
+
console.log('Backup exists, type:', result.data?.type);
|
|
174
175
|
} else if (result.code === ResultCode.WRITE_SUCCESS) {
|
|
175
|
-
console.log('
|
|
176
|
+
console.log('Write successful');
|
|
176
177
|
}
|
|
177
178
|
```
|
|
178
179
|
|
|
179
|
-
|
|
180
|
-
|
|
|
181
|
-
|
|
182
|
-
| `oldPassword` | `string` |
|
|
183
|
-
| `newPassword` | `string` |
|
|
184
|
-
| `newMnemonic` | `string` |
|
|
185
|
-
| `onCardIdentified` | `() => void` |
|
|
186
|
-
| `options.precheckExistingMnemonic` | `boolean` |
|
|
180
|
+
**Parameters:**
|
|
181
|
+
| Parameter | Type | Required | Description |
|
|
182
|
+
|-----------|------|----------|-------------|
|
|
183
|
+
| `oldPassword` | `string` | Yes | Current card password for AES authentication |
|
|
184
|
+
| `newPassword` | `string` | Yes | New password; the card will be protected with this password after the write |
|
|
185
|
+
| `newMnemonic` | `string` | Yes | New BIP-39 mnemonic (12/15/18/21/24 words) |
|
|
186
|
+
| `onCardIdentified` | `() => void` | No | Called after successful authentication; use for UI such as "writing". |
|
|
187
|
+
| `options.precheckExistingMnemonic` | `boolean` | No | When `true`, after authentication the library reads card data and verifies CRC16. If a valid mnemonic already exists, returns `PRECHECK_HAS_BACKUP` and does not write. |
|
|
187
188
|
|
|
188
189
|
---
|
|
189
190
|
|
|
190
191
|
### `updatePassword(oldPassword, newPassword, onCardIdentified?)`
|
|
191
192
|
|
|
192
|
-
|
|
193
|
+
Change the password only; mnemonic data on the card is unchanged. The retry counter is decremented automatically before authentication.
|
|
193
194
|
|
|
194
195
|
```typescript
|
|
195
196
|
const result = await updatePassword('old-password', 'new-password');
|
|
196
197
|
if (result.code === ResultCode.UPDATE_PASSWORD_SUCCESS) {
|
|
197
|
-
console.log('
|
|
198
|
+
console.log('Password updated');
|
|
198
199
|
}
|
|
199
200
|
```
|
|
200
201
|
|
|
201
|
-
|
|
202
|
-
|
|
|
203
|
-
|
|
204
|
-
| `oldPassword` | `string` |
|
|
205
|
-
| `newPassword` | `string` |
|
|
206
|
-
| `onCardIdentified` | `() => void` |
|
|
202
|
+
**Parameters:**
|
|
203
|
+
| Parameter | Type | Required | Description |
|
|
204
|
+
|-----------|------|----------|-------------|
|
|
205
|
+
| `oldPassword` | `string` | Yes | Current card password for AES authentication |
|
|
206
|
+
| `newPassword` | `string` | Yes | New password; the card will use this password after the change |
|
|
207
|
+
| `onCardIdentified` | `() => void` | No | Called after successful authentication |
|
|
207
208
|
|
|
208
209
|
---
|
|
209
210
|
|
|
210
211
|
### `writeUserNickname(password, nickname, onCardIdentified?)`
|
|
211
212
|
|
|
212
|
-
|
|
213
|
+
Write a user nickname to the card. The nickname is UTF-8 encoded, max 12 bytes (longer input is truncated).
|
|
213
214
|
|
|
214
215
|
```typescript
|
|
215
216
|
const result = await writeUserNickname('your-password', 'MyCard');
|
|
216
217
|
if (result.code === ResultCode.WRITE_NICKNAME_SUCCESS) {
|
|
217
|
-
console.log('
|
|
218
|
+
console.log('Nickname written');
|
|
218
219
|
}
|
|
219
220
|
```
|
|
220
221
|
|
|
221
|
-
|
|
222
|
-
|
|
|
223
|
-
|
|
224
|
-
| `password` | `string` |
|
|
225
|
-
| `nickname` | `string` |
|
|
226
|
-
| `onCardIdentified` | `() => void` |
|
|
222
|
+
**Parameters:**
|
|
223
|
+
| Parameter | Type | Required | Description |
|
|
224
|
+
|-----------|------|----------|-------------|
|
|
225
|
+
| `password` | `string` | Yes | Card protection password for AES authentication |
|
|
226
|
+
| `nickname` | `string` | Yes | User nickname; UTF-8 max 12 bytes (roughly 4 CJK characters or 12 Latin letters) |
|
|
227
|
+
| `onCardIdentified` | `() => void` | No | Called after successful authentication |
|
|
227
228
|
|
|
228
229
|
---
|
|
229
230
|
|
|
230
231
|
### `readUserNickname(password?, onCardIdentified?)`
|
|
231
232
|
|
|
232
|
-
|
|
233
|
+
Read the user nickname from the card.
|
|
233
234
|
|
|
234
235
|
```typescript
|
|
235
236
|
const result = await readUserNickname('your-password');
|
|
236
237
|
if (result.success) {
|
|
237
|
-
console.log('
|
|
238
|
+
console.log('Nickname:', result.data?.nickname);
|
|
238
239
|
}
|
|
239
240
|
```
|
|
240
241
|
|
|
241
|
-
|
|
242
|
-
|
|
|
243
|
-
|
|
244
|
-
| `password` | `string` |
|
|
245
|
-
| `onCardIdentified` | `() => void` |
|
|
242
|
+
**Parameters:**
|
|
243
|
+
| Parameter | Type | Required | Description |
|
|
244
|
+
|-----------|------|----------|-------------|
|
|
245
|
+
| `password` | `string` | No | Card password. Required if read protection is enabled (`PROT=1`); otherwise optional. |
|
|
246
|
+
| `onCardIdentified` | `() => void` | No | Called after successful authentication |
|
|
246
247
|
|
|
247
248
|
---
|
|
248
249
|
|
|
249
|
-
### `resetCard(password
|
|
250
|
+
### `resetCard(password, newPassword, onCardIdentified?)`
|
|
250
251
|
|
|
251
|
-
|
|
252
|
+
Reset the card: wipe mnemonic data, set a new password, and disable read/write protection. Nickname is preserved.
|
|
252
253
|
|
|
253
254
|
```typescript
|
|
254
|
-
const result = await resetCard('
|
|
255
|
+
const result = await resetCard('old-password', 'new-password');
|
|
255
256
|
if (result.code === ResultCode.RESET_SUCCESS) {
|
|
256
|
-
console.log('
|
|
257
|
+
console.log('Reset successful');
|
|
257
258
|
}
|
|
258
259
|
```
|
|
259
260
|
|
|
260
|
-
> ⚠️
|
|
261
|
+
> ⚠️ This operation is irreversible; mnemonic data on the card is permanently erased.
|
|
261
262
|
|
|
262
|
-
|
|
263
|
-
|
|
|
264
|
-
|
|
265
|
-
| `password` | `string` |
|
|
266
|
-
| `
|
|
263
|
+
**Parameters:**
|
|
264
|
+
| Parameter | Type | Required | Description |
|
|
265
|
+
|-----------|------|----------|-------------|
|
|
266
|
+
| `password` | `string \| undefined` | No | Current card password. Required if the card is protected; pass `undefined` otherwise. The retry counter is decremented automatically before authentication. |
|
|
267
|
+
| `newPassword` | `string` | Yes | Password to set after reset. |
|
|
268
|
+
| `onCardIdentified` | `() => void` | No | Called after successful authentication |
|
|
267
269
|
|
|
268
270
|
---
|
|
269
271
|
|
|
270
272
|
### `readMnemonicRetryCount(onCardIdentified?)`
|
|
271
273
|
|
|
272
|
-
|
|
274
|
+
Read the PIN retry counter on the card. No password authentication is required (the counter page is outside the protected area).
|
|
273
275
|
|
|
274
276
|
```typescript
|
|
275
277
|
const result = await readMnemonicRetryCount();
|
|
276
278
|
if (result.success) {
|
|
277
|
-
console.log('
|
|
279
|
+
console.log('Remaining retries:', result.data?.retryCount);
|
|
278
280
|
}
|
|
279
281
|
```
|
|
280
282
|
|
|
281
|
-
|
|
282
|
-
|
|
|
283
|
-
|
|
284
|
-
| `onCardIdentified` | `() => void` |
|
|
283
|
+
**Parameters:**
|
|
284
|
+
| Parameter | Type | Required | Description |
|
|
285
|
+
|-----------|------|----------|-------------|
|
|
286
|
+
| `onCardIdentified` | `() => void` | No | Called after the NFC session is established |
|
|
285
287
|
|
|
286
288
|
---
|
|
287
289
|
|
|
288
290
|
### `resetRetryCountTo10(onCardIdentified?)`
|
|
289
291
|
|
|
290
|
-
|
|
292
|
+
Reset the PIN retry counter to its default value (10). No password authentication is required.
|
|
291
293
|
|
|
292
294
|
```typescript
|
|
293
295
|
const result = await resetRetryCountTo10();
|
|
294
296
|
```
|
|
295
297
|
|
|
296
|
-
|
|
297
|
-
|
|
|
298
|
-
|
|
299
|
-
| `onCardIdentified` | `() => void` |
|
|
298
|
+
**Parameters:**
|
|
299
|
+
| Parameter | Type | Required | Description |
|
|
300
|
+
|-----------|------|----------|-------------|
|
|
301
|
+
| `onCardIdentified` | `() => void` | No | Called after the NFC session is established |
|
|
300
302
|
|
|
301
303
|
---
|
|
302
304
|
|
|
303
|
-
### NFC
|
|
305
|
+
### NFC Lock Management
|
|
304
306
|
|
|
305
|
-
|
|
307
|
+
For app-level NFC session lifecycle management:
|
|
306
308
|
|
|
307
309
|
```typescript
|
|
308
310
|
import {
|
|
@@ -313,22 +315,22 @@ import {
|
|
|
313
315
|
} from '@ukeyfe/react-native-nfc-litecard';
|
|
314
316
|
```
|
|
315
317
|
|
|
316
|
-
|
|
|
317
|
-
|
|
318
|
-
| `isNfcOperationLocked()` |
|
|
319
|
-
| `releaseNfcOperationLock()` |
|
|
320
|
-
| `markNfcOperationCancelledByCleanup()` |
|
|
321
|
-
| `consumeNfcOperationCancelledByCleanup()` |
|
|
318
|
+
| Method | Description |
|
|
319
|
+
|--------|-------------|
|
|
320
|
+
| `isNfcOperationLocked()` | Check if the NFC operation lock is held |
|
|
321
|
+
| `releaseNfcOperationLock()` | Force-release the lock (use on page unmount) |
|
|
322
|
+
| `markNfcOperationCancelledByCleanup()` | Mark current operation as interrupted by cleanup |
|
|
323
|
+
| `consumeNfcOperationCancelledByCleanup()` | Consume the cleanup flag (returns whether it was set) |
|
|
322
324
|
|
|
323
|
-
## NfcResult
|
|
325
|
+
## NfcResult Structure
|
|
324
326
|
|
|
325
|
-
|
|
327
|
+
All APIs return a unified `NfcResult`:
|
|
326
328
|
|
|
327
329
|
```typescript
|
|
328
330
|
interface NfcResult {
|
|
329
|
-
code: number; //
|
|
330
|
-
success: boolean; //
|
|
331
|
-
data?: { //
|
|
331
|
+
code: number; // Status code – compare against ResultCode
|
|
332
|
+
success: boolean; // Whether the operation succeeded
|
|
333
|
+
data?: { // Optional data, only present for some operations
|
|
332
334
|
mnemonic?: string;
|
|
333
335
|
type?: string;
|
|
334
336
|
entropyHex?: string;
|
|
@@ -341,7 +343,7 @@ interface NfcResult {
|
|
|
341
343
|
}
|
|
342
344
|
```
|
|
343
345
|
|
|
344
|
-
##
|
|
346
|
+
## Error Handling Example
|
|
345
347
|
|
|
346
348
|
```typescript
|
|
347
349
|
import { ResultCode, readMnemonic } from '@ukeyfe/react-native-nfc-litecard';
|
|
@@ -350,110 +352,110 @@ const result = await readMnemonic('password');
|
|
|
350
352
|
|
|
351
353
|
switch (result.code) {
|
|
352
354
|
case ResultCode.READ_SUCCESS:
|
|
353
|
-
console.log('
|
|
355
|
+
console.log('Read successful:', result.data?.mnemonic);
|
|
354
356
|
break;
|
|
355
357
|
case ResultCode.AUTH_WRONG_PASSWORD:
|
|
356
|
-
alert('
|
|
358
|
+
alert('Wrong password');
|
|
357
359
|
break;
|
|
358
360
|
case ResultCode.NFC_CONNECT_FAILED:
|
|
359
|
-
alert('NFC
|
|
361
|
+
alert('NFC connection failed, please re-tap the card');
|
|
360
362
|
break;
|
|
361
363
|
case ResultCode.NFC_USER_CANCELED:
|
|
362
|
-
// iOS
|
|
364
|
+
// iOS user cancelled – handle silently
|
|
363
365
|
break;
|
|
364
366
|
case ResultCode.READ_TIMEOUT:
|
|
365
|
-
alert('
|
|
367
|
+
alert('Read timeout – remove and re-tap the card');
|
|
366
368
|
break;
|
|
367
369
|
case ResultCode.RETRY_COUNT_EXHAUSTED:
|
|
368
|
-
alert('
|
|
370
|
+
alert('Retry count exhausted – card is locked');
|
|
369
371
|
break;
|
|
370
372
|
default:
|
|
371
|
-
alert('
|
|
373
|
+
alert('Operation failed');
|
|
372
374
|
}
|
|
373
375
|
```
|
|
374
376
|
|
|
375
|
-
##
|
|
376
|
-
|
|
377
|
-
###
|
|
378
|
-
|
|
379
|
-
|
|
|
380
|
-
|
|
381
|
-
| `READ_SUCCESS` | 10102 |
|
|
382
|
-
| `READ_NICKNAME_SUCCESS` | 10103 |
|
|
383
|
-
| `CHECK_EMPTY` | 10104 |
|
|
384
|
-
| `CHECK_HAS_DATA` | 10105 |
|
|
385
|
-
| `READ_RETRY_COUNT_SUCCESS` | 10106 |
|
|
386
|
-
| `INIT_SUCCESS` | 10201 |
|
|
387
|
-
| `WRITE_SUCCESS` | 10203 |
|
|
388
|
-
| `UPDATE_PASSWORD_SUCCESS` | 10204 |
|
|
389
|
-
| `WRITE_NICKNAME_SUCCESS` | 10205 |
|
|
390
|
-
| `RESET_SUCCESS` | 10206 |
|
|
391
|
-
| `PRECHECK_HAS_BACKUP` | 10207 |
|
|
392
|
-
|
|
393
|
-
###
|
|
394
|
-
|
|
395
|
-
|
|
|
396
|
-
|
|
397
|
-
| `NFC_CONNECT_FAILED` | 40001 | NFC
|
|
398
|
-
| `AUTH_WRONG_PASSWORD` | 40002 |
|
|
399
|
-
| `AUTH_INVALID_RESPONSE` | 40003 |
|
|
400
|
-
| `AUTH_VERIFY_FAILED` | 40004 |
|
|
401
|
-
| `READ_FAILED` | 40005 |
|
|
402
|
-
| `WRITE_FAILED` | 40006 |
|
|
403
|
-
| `INVALID_MNEMONIC` | 40007 |
|
|
404
|
-
| `UNSUPPORTED_MNEMONIC_LENGTH` | 40008 |
|
|
405
|
-
| `INVALID_CARD_DATA` | 40009 |
|
|
406
|
-
| `UNKNOWN_ERROR` | 40010 |
|
|
407
|
-
| `NFC_USER_CANCELED` | 40011 |
|
|
408
|
-
| `READ_TIMEOUT` | 40012 |
|
|
409
|
-
| `NFC_LOCK_TIMEOUT` | 40013 | NFC
|
|
410
|
-
| `CRC16_CHECK_FAILED` | 40014 | CRC16
|
|
411
|
-
| `RETRY_COUNT_EXHAUSTED` | 40015 | PIN
|
|
412
|
-
|
|
413
|
-
##
|
|
414
|
-
|
|
415
|
-
|
|
377
|
+
## Result Codes
|
|
378
|
+
|
|
379
|
+
### Success Codes
|
|
380
|
+
|
|
381
|
+
| Constant | Value | Description |
|
|
382
|
+
|----------|-------|-------------|
|
|
383
|
+
| `READ_SUCCESS` | 10102 | Mnemonic read successful |
|
|
384
|
+
| `READ_NICKNAME_SUCCESS` | 10103 | Nickname read successful |
|
|
385
|
+
| `CHECK_EMPTY` | 10104 | Empty card |
|
|
386
|
+
| `CHECK_HAS_DATA` | 10105 | Card has data |
|
|
387
|
+
| `READ_RETRY_COUNT_SUCCESS` | 10106 | Retry count read successful |
|
|
388
|
+
| `INIT_SUCCESS` | 10201 | Initialization successful |
|
|
389
|
+
| `WRITE_SUCCESS` | 10203 | Write/update successful |
|
|
390
|
+
| `UPDATE_PASSWORD_SUCCESS` | 10204 | Password change successful |
|
|
391
|
+
| `WRITE_NICKNAME_SUCCESS` | 10205 | Nickname written |
|
|
392
|
+
| `RESET_SUCCESS` | 10206 | Card reset successful |
|
|
393
|
+
| `PRECHECK_HAS_BACKUP` | 10207 | Card already has a valid backup; write was skipped |
|
|
394
|
+
|
|
395
|
+
### Error Codes
|
|
396
|
+
|
|
397
|
+
| Constant | Value | Description |
|
|
398
|
+
|----------|-------|-------------|
|
|
399
|
+
| `NFC_CONNECT_FAILED` | 40001 | NFC connection failed |
|
|
400
|
+
| `AUTH_WRONG_PASSWORD` | 40002 | Wrong password |
|
|
401
|
+
| `AUTH_INVALID_RESPONSE` | 40003 | Invalid authentication response |
|
|
402
|
+
| `AUTH_VERIFY_FAILED` | 40004 | Authentication verification failed |
|
|
403
|
+
| `READ_FAILED` | 40005 | Read failed |
|
|
404
|
+
| `WRITE_FAILED` | 40006 | Write failed |
|
|
405
|
+
| `INVALID_MNEMONIC` | 40007 | Invalid mnemonic |
|
|
406
|
+
| `UNSUPPORTED_MNEMONIC_LENGTH` | 40008 | Unsupported mnemonic length |
|
|
407
|
+
| `INVALID_CARD_DATA` | 40009 | Invalid card data |
|
|
408
|
+
| `UNKNOWN_ERROR` | 40010 | Unknown error |
|
|
409
|
+
| `NFC_USER_CANCELED` | 40011 | User cancelled NFC scan (iOS) |
|
|
410
|
+
| `READ_TIMEOUT` | 40012 | Read timeout |
|
|
411
|
+
| `NFC_LOCK_TIMEOUT` | 40013 | NFC lock timeout |
|
|
412
|
+
| `CRC16_CHECK_FAILED` | 40014 | CRC16 check failed |
|
|
413
|
+
| `RETRY_COUNT_EXHAUSTED` | 40015 | PIN retry count exhausted, card locked |
|
|
414
|
+
|
|
415
|
+
## Storage Format
|
|
416
|
+
|
|
417
|
+
The card stores BIP-39 mnemonics using entropy compression:
|
|
416
418
|
|
|
417
419
|
```
|
|
418
|
-
[
|
|
420
|
+
[type 1B] [entropy 16-32B] [CRC16 2B]
|
|
419
421
|
```
|
|
420
422
|
|
|
421
|
-
|
|
|
422
|
-
|
|
423
|
-
| 0x01 | 12
|
|
424
|
-
| 0x02 | 15
|
|
425
|
-
| 0x03 | 18
|
|
426
|
-
| 0x04 | 21
|
|
427
|
-
| 0x05 | 24
|
|
423
|
+
| Type | Mnemonic Length | Entropy Size |
|
|
424
|
+
|------|----------------|--------------|
|
|
425
|
+
| 0x01 | 12 words | 16 bytes (128-bit) |
|
|
426
|
+
| 0x02 | 15 words | 20 bytes (160-bit) |
|
|
427
|
+
| 0x03 | 18 words | 24 bytes (192-bit) |
|
|
428
|
+
| 0x04 | 21 words | 28 bytes (224-bit) |
|
|
429
|
+
| 0x05 | 24 words | 32 bytes (256-bit) |
|
|
428
430
|
|
|
429
|
-
##
|
|
431
|
+
## Security
|
|
430
432
|
|
|
431
|
-
- **AES-128
|
|
432
|
-
- **SHA-256
|
|
433
|
-
- **CRC16-Modbus
|
|
434
|
-
- **PIN
|
|
435
|
-
-
|
|
433
|
+
- **AES-128 hardware-level mutual authentication** (3-pass)
|
|
434
|
+
- **SHA-256 key derivation**: password → SHA-256 → first 16 bytes as AES key
|
|
435
|
+
- **CRC16-Modbus checksum**: data integrity verification
|
|
436
|
+
- **PIN retry counter**: auto-decrements on wrong password, resets to 10 on success
|
|
437
|
+
- **Secure random**: authentication uses `crypto.getRandomValues()` (requires Hermes ≥ 0.72 or `react-native-get-random-values` polyfill)
|
|
436
438
|
|
|
437
|
-
##
|
|
439
|
+
## Project Structure
|
|
438
440
|
|
|
439
441
|
```
|
|
440
442
|
src/
|
|
441
|
-
├── index.ts #
|
|
442
|
-
├── constants.ts #
|
|
443
|
-
├── types.ts #
|
|
444
|
-
├── crypto.ts # AES
|
|
445
|
-
├── utils.ts # CRC16
|
|
446
|
-
├── nfc-core.ts # NFC
|
|
447
|
-
├── reader.ts #
|
|
448
|
-
└── writer.ts #
|
|
443
|
+
├── index.ts # Public API exports
|
|
444
|
+
├── constants.ts # Shared constants (page addresses, NFC commands, mnemonic types)
|
|
445
|
+
├── types.ts # Unified ResultCode, NfcResult interface, error mapping
|
|
446
|
+
├── crypto.ts # AES encrypt/decrypt, key derivation, secure random
|
|
447
|
+
├── utils.ts # CRC16, hex conversion, array utilities
|
|
448
|
+
├── nfc-core.ts # NFC lock, transceive, authentication, retry counter
|
|
449
|
+
├── reader.ts # Reader API
|
|
450
|
+
└── writer.ts # Writer API
|
|
449
451
|
```
|
|
450
452
|
|
|
451
|
-
##
|
|
453
|
+
## Platform Support
|
|
452
454
|
|
|
453
|
-
|
|
|
454
|
-
|
|
455
|
-
| iOS | MifareIOS |
|
|
456
|
-
| Android | NfcA |
|
|
455
|
+
| Platform | Technology | Requirements |
|
|
456
|
+
|----------|-----------|--------------|
|
|
457
|
+
| iOS | MifareIOS | iPhone 7 or later |
|
|
458
|
+
| Android | NfcA | NFC-capable device |
|
|
457
459
|
|
|
458
460
|
## License
|
|
459
461
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @ukeyfe/react-native-nfc-litecard
|
|
2
2
|
|
|
3
|
-
English
|
|
3
|
+
[English](./README.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
|
|
|
@@ -19,7 +19,7 @@ React Native NFC read/write library for **MIFARE Ultralight AES** (MF0AES(H)20),
|
|
|
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 data,
|
|
22
|
+
| Reset card | `resetCard()` | Wipe mnemonic data, set a new password |
|
|
23
23
|
|
|
24
24
|
## Installation
|
|
25
25
|
|
|
@@ -247,12 +247,12 @@ if (result.success) {
|
|
|
247
247
|
|
|
248
248
|
---
|
|
249
249
|
|
|
250
|
-
### `resetCard(password
|
|
250
|
+
### `resetCard(password, newPassword, onCardIdentified?)`
|
|
251
251
|
|
|
252
|
-
Reset the card: wipe
|
|
252
|
+
Reset the card: wipe mnemonic data, set a new password, and disable read/write protection. Nickname is preserved.
|
|
253
253
|
|
|
254
254
|
```typescript
|
|
255
|
-
const result = await resetCard('
|
|
255
|
+
const result = await resetCard('old-password', 'new-password');
|
|
256
256
|
if (result.code === ResultCode.RESET_SUCCESS) {
|
|
257
257
|
console.log('Reset successful');
|
|
258
258
|
}
|
|
@@ -263,7 +263,8 @@ if (result.code === ResultCode.RESET_SUCCESS) {
|
|
|
263
263
|
**Parameters:**
|
|
264
264
|
| Parameter | Type | Required | Description |
|
|
265
265
|
|-----------|------|----------|-------------|
|
|
266
|
-
| `password` | `string` | No | Current card password. Required if the card is protected; otherwise
|
|
266
|
+
| `password` | `string \| undefined` | No | Current card password. Required if the card is protected; pass `undefined` otherwise. The retry counter is decremented automatically before authentication. |
|
|
267
|
+
| `newPassword` | `string` | Yes | Password to set after reset. |
|
|
267
268
|
| `onCardIdentified` | `() => void` | No | Called after successful authentication |
|
|
268
269
|
|
|
269
270
|
---
|
package/dist/constants.d.ts
CHANGED
|
@@ -43,6 +43,10 @@ export declare const MNEMONIC_TYPE_18 = 3;
|
|
|
43
43
|
export declare const MNEMONIC_TYPE_21 = 4;
|
|
44
44
|
/** 24-word mnemonic (256-bit entropy, 32 bytes) */
|
|
45
45
|
export declare const MNEMONIC_TYPE_24 = 5;
|
|
46
|
+
/** Mnemonic data end page (stops before nickname area) */
|
|
47
|
+
export declare const MNEMONIC_PAGE_END: number;
|
|
48
|
+
/** Mnemonic data size: (0x24 - 0x08 + 1) * 4 = 116 bytes */
|
|
49
|
+
export declare const MNEMONIC_MEMORY_SIZE: number;
|
|
46
50
|
/** Nickname start page */
|
|
47
51
|
export declare const USER_NICKNAME_PAGE_START: number;
|
|
48
52
|
/** Nickname end page */
|
package/dist/constants.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Shared constants for MIFARE Ultralight AES (MF0AES(H)20) NFC operations.
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.DEFAULT_PIN_RETRY_COUNT = exports.RETRY_COUNTER_OFFSET = exports.RETRY_COUNTER_PAGE = exports.USER_NICKNAME_MAX_LENGTH = exports.USER_NICKNAME_PAGE_END = exports.USER_NICKNAME_PAGE_START = exports.MNEMONIC_TYPE_24 = exports.MNEMONIC_TYPE_21 = exports.MNEMONIC_TYPE_18 = exports.MNEMONIC_TYPE_15 = exports.MNEMONIC_TYPE_12 = exports.USER_CARD_INFO_SIZE = exports.USER_MEMORY_SIZE = exports.PAGE_AES_KEY0_START = exports.PAGE_CFG1 = exports.PAGE_CFG0 = exports.USER_CARD_INFO_PAGE_END = exports.USER_CARD_INFO_PAGE_START = exports.USER_PAGE_END = exports.USER_PAGE_START = exports.PAGE_SIZE = exports.KEY_NO_DATA_PROT = exports.CMD_AUTH_PART2 = exports.CMD_AUTH_PART1 = exports.CMD_FAST_READ = exports.CMD_WRITE = exports.CMD_READ = void 0;
|
|
6
|
+
exports.DEFAULT_PIN_RETRY_COUNT = exports.RETRY_COUNTER_OFFSET = exports.RETRY_COUNTER_PAGE = exports.USER_NICKNAME_MAX_LENGTH = exports.USER_NICKNAME_PAGE_END = exports.USER_NICKNAME_PAGE_START = exports.MNEMONIC_MEMORY_SIZE = exports.MNEMONIC_PAGE_END = exports.MNEMONIC_TYPE_24 = exports.MNEMONIC_TYPE_21 = exports.MNEMONIC_TYPE_18 = exports.MNEMONIC_TYPE_15 = exports.MNEMONIC_TYPE_12 = exports.USER_CARD_INFO_SIZE = exports.USER_MEMORY_SIZE = exports.PAGE_AES_KEY0_START = exports.PAGE_CFG1 = exports.PAGE_CFG0 = exports.USER_CARD_INFO_PAGE_END = exports.USER_CARD_INFO_PAGE_START = exports.USER_PAGE_END = exports.USER_PAGE_START = exports.PAGE_SIZE = exports.KEY_NO_DATA_PROT = exports.CMD_AUTH_PART2 = exports.CMD_AUTH_PART1 = exports.CMD_FAST_READ = exports.CMD_WRITE = exports.CMD_READ = void 0;
|
|
7
7
|
// ---------------------------------------------------------------------------
|
|
8
8
|
// NFC command codes
|
|
9
9
|
// ---------------------------------------------------------------------------
|
|
@@ -59,6 +59,13 @@ exports.MNEMONIC_TYPE_21 = 0x04;
|
|
|
59
59
|
/** 24-word mnemonic (256-bit entropy, 32 bytes) */
|
|
60
60
|
exports.MNEMONIC_TYPE_24 = 0x05;
|
|
61
61
|
// ---------------------------------------------------------------------------
|
|
62
|
+
// Mnemonic data area (excludes nickname pages)
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
/** Mnemonic data end page (stops before nickname area) */
|
|
65
|
+
exports.MNEMONIC_PAGE_END = exports.USER_PAGE_END - 3; // 0x24
|
|
66
|
+
/** Mnemonic data size: (0x24 - 0x08 + 1) * 4 = 116 bytes */
|
|
67
|
+
exports.MNEMONIC_MEMORY_SIZE = (exports.MNEMONIC_PAGE_END - exports.USER_PAGE_START + 1) * exports.PAGE_SIZE;
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
62
69
|
// User nickname area (last 3 pages of user memory)
|
|
63
70
|
// ---------------------------------------------------------------------------
|
|
64
71
|
/** Nickname start page */
|
package/dist/writer.d.ts
CHANGED
|
@@ -33,7 +33,9 @@ export declare function updatePassword(oldPassword: string, newPassword: string,
|
|
|
33
33
|
/** Write a user nickname (password required for authentication). */
|
|
34
34
|
export declare function writeUserNickname(password: string, nickname: string, onCardIdentified?: () => void): Promise<NfcResult>;
|
|
35
35
|
/**
|
|
36
|
-
* Reset card: wipe
|
|
36
|
+
* Reset card: wipe mnemonic data and set a new password.
|
|
37
37
|
* @param password – current card password (required if protection is enabled).
|
|
38
|
+
* @param newPassword – password to set after reset.
|
|
39
|
+
* @param onCardIdentified – callback when card is identified.
|
|
38
40
|
*/
|
|
39
|
-
export declare function resetCard(password
|
|
41
|
+
export declare function resetCard(password: string | undefined, newPassword: string, onCardIdentified?: () => void): Promise<NfcResult>;
|
package/dist/writer.js
CHANGED
|
@@ -77,13 +77,14 @@ async function writePage(page, data) {
|
|
|
77
77
|
await (0, nfc_core_1.transceive)([constants_1.CMD_WRITE, page, ...data]);
|
|
78
78
|
}
|
|
79
79
|
/**
|
|
80
|
-
* Write data to
|
|
80
|
+
* Write data to mnemonic area (pages 0x08–0x24).
|
|
81
|
+
* Does NOT touch the nickname area (pages 0x25–0x27).
|
|
81
82
|
* iOS: inserts small delays and periodic keep-alive reads.
|
|
82
83
|
*/
|
|
83
84
|
async function writeUserMemory(data) {
|
|
84
|
-
const buffer = new Uint8Array(constants_1.
|
|
85
|
-
buffer.set(data, 0);
|
|
86
|
-
const totalPages = constants_1.
|
|
85
|
+
const buffer = new Uint8Array(constants_1.MNEMONIC_MEMORY_SIZE);
|
|
86
|
+
buffer.set(data.slice(0, constants_1.MNEMONIC_MEMORY_SIZE), 0);
|
|
87
|
+
const totalPages = constants_1.MNEMONIC_PAGE_END - constants_1.USER_PAGE_START + 1;
|
|
87
88
|
for (let i = 0; i < totalPages; i++) {
|
|
88
89
|
const page = constants_1.USER_PAGE_START + i;
|
|
89
90
|
const offset = i * constants_1.PAGE_SIZE;
|
|
@@ -303,7 +304,6 @@ async function updateCard(oldPassword, newPassword, newMnemonic, onCardIdentifie
|
|
|
303
304
|
return { code: types_1.ResultCode.NFC_CONNECT_FAILED, success: false };
|
|
304
305
|
}
|
|
305
306
|
try {
|
|
306
|
-
const entropyResult = mnemonicToEntropyWithCRC(newMnemonic);
|
|
307
307
|
try {
|
|
308
308
|
const n = await (0, nfc_core_1.decrementRetryCountInSession)();
|
|
309
309
|
if (typeof n === 'number')
|
|
@@ -316,11 +316,13 @@ async function updateCard(oldPassword, newPassword, newMnemonic, onCardIdentifie
|
|
|
316
316
|
(0, nfc_core_1.releaseNfcLock)();
|
|
317
317
|
return (0, types_1.nfcResultRetryCountExhausted)();
|
|
318
318
|
}
|
|
319
|
+
// Communication failure — counter was not decremented, continue to auth
|
|
319
320
|
}
|
|
320
321
|
const oldKey = (0, crypto_1.passwordToAesKey)(oldPassword);
|
|
321
322
|
await (0, nfc_core_1.authenticate)(oldKey);
|
|
322
323
|
try {
|
|
323
324
|
await (0, nfc_core_1.writeRetryCountInSession)(constants_1.DEFAULT_PIN_RETRY_COUNT);
|
|
325
|
+
retryCountAfterPreDecrement = constants_1.DEFAULT_PIN_RETRY_COUNT;
|
|
324
326
|
}
|
|
325
327
|
catch { /* non-fatal */ }
|
|
326
328
|
onCardIdentified?.();
|
|
@@ -350,8 +352,9 @@ async function updateCard(oldPassword, newPassword, newMnemonic, onCardIdentifie
|
|
|
350
352
|
}
|
|
351
353
|
}
|
|
352
354
|
}
|
|
353
|
-
|
|
355
|
+
const entropyResult = mnemonicToEntropyWithCRC(newMnemonic);
|
|
354
356
|
await writeUserMemory(entropyResult.data);
|
|
357
|
+
await disableAuth();
|
|
355
358
|
const newKey = (0, crypto_1.passwordToAesKey)(newPassword);
|
|
356
359
|
const aesKeyHex = (0, utils_1.bytesToHex)(newKey);
|
|
357
360
|
await writeAesKey(newKey);
|
|
@@ -418,11 +421,13 @@ async function updatePassword(oldPassword, newPassword, onCardIdentified) {
|
|
|
418
421
|
(0, nfc_core_1.releaseNfcLock)();
|
|
419
422
|
return (0, types_1.nfcResultRetryCountExhausted)();
|
|
420
423
|
}
|
|
424
|
+
// Communication failure — counter was not decremented, continue to auth
|
|
421
425
|
}
|
|
422
426
|
const oldKey = (0, crypto_1.passwordToAesKey)(oldPassword);
|
|
423
427
|
await (0, nfc_core_1.authenticate)(oldKey);
|
|
424
428
|
try {
|
|
425
429
|
await (0, nfc_core_1.writeRetryCountInSession)(constants_1.DEFAULT_PIN_RETRY_COUNT);
|
|
430
|
+
retryCountAfterPreDecrement = constants_1.DEFAULT_PIN_RETRY_COUNT;
|
|
426
431
|
}
|
|
427
432
|
catch { /* non-fatal */ }
|
|
428
433
|
onCardIdentified?.();
|
|
@@ -498,10 +503,12 @@ async function writeUserNickname(password, nickname, onCardIdentified) {
|
|
|
498
503
|
}
|
|
499
504
|
}
|
|
500
505
|
/**
|
|
501
|
-
* Reset card: wipe
|
|
506
|
+
* Reset card: wipe mnemonic data and set a new password.
|
|
502
507
|
* @param password – current card password (required if protection is enabled).
|
|
508
|
+
* @param newPassword – password to set after reset.
|
|
509
|
+
* @param onCardIdentified – callback when card is identified.
|
|
503
510
|
*/
|
|
504
|
-
async function resetCard(password, onCardIdentified) {
|
|
511
|
+
async function resetCard(password, newPassword, onCardIdentified) {
|
|
505
512
|
try {
|
|
506
513
|
await (0, nfc_core_1.acquireNfcLock)();
|
|
507
514
|
}
|
|
@@ -531,12 +538,14 @@ async function resetCard(password, onCardIdentified) {
|
|
|
531
538
|
(0, nfc_core_1.releaseNfcLock)();
|
|
532
539
|
return (0, types_1.nfcResultRetryCountExhausted)();
|
|
533
540
|
}
|
|
541
|
+
// Communication failure — counter was not decremented, continue to auth
|
|
534
542
|
}
|
|
535
543
|
const aesKey = (0, crypto_1.passwordToAesKey)(password);
|
|
536
544
|
try {
|
|
537
545
|
await (0, nfc_core_1.authenticate)(aesKey);
|
|
538
546
|
try {
|
|
539
547
|
await (0, nfc_core_1.writeRetryCountInSession)(constants_1.DEFAULT_PIN_RETRY_COUNT);
|
|
548
|
+
retryCountAfterPreDecrement = constants_1.DEFAULT_PIN_RETRY_COUNT;
|
|
540
549
|
}
|
|
541
550
|
catch { /* non-fatal */ }
|
|
542
551
|
}
|
|
@@ -567,11 +576,11 @@ async function resetCard(password, onCardIdentified) {
|
|
|
567
576
|
}
|
|
568
577
|
onCardIdentified?.();
|
|
569
578
|
}
|
|
570
|
-
// Wipe
|
|
571
|
-
await writeUserMemory(new Uint8Array(constants_1.
|
|
572
|
-
// Set
|
|
573
|
-
const
|
|
574
|
-
await writeAesKey(
|
|
579
|
+
// Wipe mnemonic data (nickname is preserved)
|
|
580
|
+
await writeUserMemory(new Uint8Array(constants_1.MNEMONIC_MEMORY_SIZE));
|
|
581
|
+
// Set new password
|
|
582
|
+
const resetKey = (0, crypto_1.passwordToAesKey)(newPassword);
|
|
583
|
+
await writeAesKey(resetKey);
|
|
575
584
|
// Ensure protection is disabled
|
|
576
585
|
try {
|
|
577
586
|
await disableAuth();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ukeyfe/react-native-nfc-litecard",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "NFC read/write for MIFARE Ultralight AES (LiteCard mnemonic storage)",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -28,6 +28,6 @@
|
|
|
28
28
|
"files": [
|
|
29
29
|
"dist",
|
|
30
30
|
"README.md",
|
|
31
|
-
"README.
|
|
31
|
+
"README.zh.md"
|
|
32
32
|
]
|
|
33
33
|
}
|