@ukeyfe/react-native-nfc-litecard 1.0.1 → 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 +277 -238
- package/README.zh.md +499 -0
- package/dist/constants.d.ts +30 -0
- package/dist/constants.js +40 -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 +13 -6
- package/dist/writer.js +116 -94
- package/package.json +2 -2
- package/README.en.md +0 -461
package/README.en.md
DELETED
|
@@ -1,461 +0,0 @@
|
|
|
1
|
-
# @ukeyfe/react-native-nfc-litecard
|
|
2
|
-
|
|
3
|
-
English | [中文](./README.md)
|
|
4
|
-
|
|
5
|
-
React Native NFC read/write library for **MIFARE Ultralight AES** (MF0AES(H)20), designed for LiteCard mnemonic storage.
|
|
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 `ResultCode` to their own localised strings.
|
|
8
|
-
|
|
9
|
-
## Features
|
|
10
|
-
|
|
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 data, reset password to "000000" |
|
|
23
|
-
|
|
24
|
-
## Installation
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
npm install @ukeyfe/react-native-nfc-litecard
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### Peer Dependencies
|
|
31
|
-
|
|
32
|
-
This library requires the following peer dependencies:
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
npm install react-native react-native-nfc-manager
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Quick Start
|
|
39
|
-
|
|
40
|
-
```typescript
|
|
41
|
-
import {
|
|
42
|
-
ResultCode,
|
|
43
|
-
checkCard,
|
|
44
|
-
readMnemonic,
|
|
45
|
-
initializeCard,
|
|
46
|
-
updateCard,
|
|
47
|
-
updatePassword,
|
|
48
|
-
writeUserNickname,
|
|
49
|
-
readUserNickname,
|
|
50
|
-
resetCard,
|
|
51
|
-
readMnemonicRetryCount,
|
|
52
|
-
resetRetryCountTo10,
|
|
53
|
-
} from '@ukeyfe/react-native-nfc-litecard';
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## API Reference
|
|
57
|
-
|
|
58
|
-
### `checkCard(password?, onCardIdentified?)`
|
|
59
|
-
|
|
60
|
-
Detect card status (empty / has data).
|
|
61
|
-
|
|
62
|
-
**Without password (quick probe):**
|
|
63
|
-
```typescript
|
|
64
|
-
const result = await checkCard();
|
|
65
|
-
if (result.code === ResultCode.CHECK_EMPTY) {
|
|
66
|
-
// Empty card – ready to initialize
|
|
67
|
-
} else if (result.code === ResultCode.CHECK_HAS_DATA) {
|
|
68
|
-
// Has data (or read-protected, cannot determine)
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
**With password (authenticated deep check, for read-protected cards):**
|
|
73
|
-
```typescript
|
|
74
|
-
const result = await checkCard('password');
|
|
75
|
-
if (result.code === ResultCode.CHECK_EMPTY) {
|
|
76
|
-
// Empty card – ready to write
|
|
77
|
-
} else if (result.code === ResultCode.CHECK_HAS_DATA) {
|
|
78
|
-
// Valid mnemonic backup exists, type: result.data?.type
|
|
79
|
-
} else if (result.code === ResultCode.AUTH_WRONG_PASSWORD) {
|
|
80
|
-
// Wrong password
|
|
81
|
-
}
|
|
82
|
-
```
|
|
83
|
-
|
|
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
|
-
|
|
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
|
-
|
|
98
|
-
---
|
|
99
|
-
|
|
100
|
-
### `readMnemonic(password, onCardIdentified?)`
|
|
101
|
-
|
|
102
|
-
Read the mnemonic (password authentication required). The retry counter is decremented before authentication and reset to 10 after a successful authentication.
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
const result = await readMnemonic('your-password');
|
|
106
|
-
if (result.success) {
|
|
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
|
-
}
|
|
112
|
-
```
|
|
113
|
-
|
|
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
|
-
|
|
130
|
-
---
|
|
131
|
-
|
|
132
|
-
### `initializeCard(mnemonic, password, onCardIdentified?)`
|
|
133
|
-
|
|
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
|
-
|
|
136
|
-
```typescript
|
|
137
|
-
const result = await initializeCard(
|
|
138
|
-
'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
|
|
139
|
-
'your-password'
|
|
140
|
-
);
|
|
141
|
-
if (result.code === ResultCode.INIT_SUCCESS) {
|
|
142
|
-
console.log('Initialization successful');
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
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
|
-
|
|
153
|
-
---
|
|
154
|
-
|
|
155
|
-
### `updateCard(oldPassword, newPassword, newMnemonic, onCardIdentified?, options?)`
|
|
156
|
-
|
|
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
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
const result = await updateCard('old-password', 'new-password', 'new mnemonic words ...');
|
|
161
|
-
if (result.code === ResultCode.WRITE_SUCCESS) {
|
|
162
|
-
console.log('Update successful');
|
|
163
|
-
}
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
**Pre-check for existing backup:**
|
|
167
|
-
|
|
168
|
-
```typescript
|
|
169
|
-
const result = await updateCard('old-password', 'new-password', 'mnemonic ...', undefined, {
|
|
170
|
-
precheckExistingMnemonic: true,
|
|
171
|
-
});
|
|
172
|
-
if (result.code === ResultCode.PRECHECK_HAS_BACKUP) {
|
|
173
|
-
// Card already has a valid mnemonic backup; write was skipped
|
|
174
|
-
console.log('Backup exists, type:', result.data?.type);
|
|
175
|
-
} else if (result.code === ResultCode.WRITE_SUCCESS) {
|
|
176
|
-
console.log('Write successful');
|
|
177
|
-
}
|
|
178
|
-
```
|
|
179
|
-
|
|
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. |
|
|
188
|
-
|
|
189
|
-
---
|
|
190
|
-
|
|
191
|
-
### `updatePassword(oldPassword, newPassword, onCardIdentified?)`
|
|
192
|
-
|
|
193
|
-
Change the password only; mnemonic data on the card is unchanged. The retry counter is decremented automatically before authentication.
|
|
194
|
-
|
|
195
|
-
```typescript
|
|
196
|
-
const result = await updatePassword('old-password', 'new-password');
|
|
197
|
-
if (result.code === ResultCode.UPDATE_PASSWORD_SUCCESS) {
|
|
198
|
-
console.log('Password updated');
|
|
199
|
-
}
|
|
200
|
-
```
|
|
201
|
-
|
|
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 |
|
|
208
|
-
|
|
209
|
-
---
|
|
210
|
-
|
|
211
|
-
### `writeUserNickname(password, nickname, onCardIdentified?)`
|
|
212
|
-
|
|
213
|
-
Write a user nickname to the card. The nickname is UTF-8 encoded, max 12 bytes (longer input is truncated).
|
|
214
|
-
|
|
215
|
-
```typescript
|
|
216
|
-
const result = await writeUserNickname('your-password', 'MyCard');
|
|
217
|
-
if (result.code === ResultCode.WRITE_NICKNAME_SUCCESS) {
|
|
218
|
-
console.log('Nickname written');
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
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 |
|
|
228
|
-
|
|
229
|
-
---
|
|
230
|
-
|
|
231
|
-
### `readUserNickname(password?, onCardIdentified?)`
|
|
232
|
-
|
|
233
|
-
Read the user nickname from the card.
|
|
234
|
-
|
|
235
|
-
```typescript
|
|
236
|
-
const result = await readUserNickname('your-password');
|
|
237
|
-
if (result.success) {
|
|
238
|
-
console.log('Nickname:', result.data?.nickname);
|
|
239
|
-
}
|
|
240
|
-
```
|
|
241
|
-
|
|
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 |
|
|
247
|
-
|
|
248
|
-
---
|
|
249
|
-
|
|
250
|
-
### `resetCard(password?, onCardIdentified?)`
|
|
251
|
-
|
|
252
|
-
Reset the card: wipe all user data (mnemonic, nickname), set password to `"000000"`, and disable read/write protection.
|
|
253
|
-
|
|
254
|
-
```typescript
|
|
255
|
-
const result = await resetCard('your-password');
|
|
256
|
-
if (result.code === ResultCode.RESET_SUCCESS) {
|
|
257
|
-
console.log('Reset successful');
|
|
258
|
-
}
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
> ⚠️ This operation is irreversible; mnemonic data on the card is permanently erased.
|
|
262
|
-
|
|
263
|
-
**Parameters:**
|
|
264
|
-
| Parameter | Type | Required | Description |
|
|
265
|
-
|-----------|------|----------|-------------|
|
|
266
|
-
| `password` | `string` | No | Current card password. Required if the card is protected; otherwise optional. The retry counter is decremented automatically before authentication. |
|
|
267
|
-
| `onCardIdentified` | `() => void` | No | Called after successful authentication |
|
|
268
|
-
|
|
269
|
-
---
|
|
270
|
-
|
|
271
|
-
### `readMnemonicRetryCount(onCardIdentified?)`
|
|
272
|
-
|
|
273
|
-
Read the PIN retry counter on the card. No password authentication is required (the counter page is outside the protected area).
|
|
274
|
-
|
|
275
|
-
```typescript
|
|
276
|
-
const result = await readMnemonicRetryCount();
|
|
277
|
-
if (result.success) {
|
|
278
|
-
console.log('Remaining retries:', result.data?.retryCount);
|
|
279
|
-
}
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
**Parameters:**
|
|
283
|
-
| Parameter | Type | Required | Description |
|
|
284
|
-
|-----------|------|----------|-------------|
|
|
285
|
-
| `onCardIdentified` | `() => void` | No | Called after the NFC session is established |
|
|
286
|
-
|
|
287
|
-
---
|
|
288
|
-
|
|
289
|
-
### `resetRetryCountTo10(onCardIdentified?)`
|
|
290
|
-
|
|
291
|
-
Reset the PIN retry counter to its default value (10). No password authentication is required.
|
|
292
|
-
|
|
293
|
-
```typescript
|
|
294
|
-
const result = await resetRetryCountTo10();
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
**Parameters:**
|
|
298
|
-
| Parameter | Type | Required | Description |
|
|
299
|
-
|-----------|------|----------|-------------|
|
|
300
|
-
| `onCardIdentified` | `() => void` | No | Called after the NFC session is established |
|
|
301
|
-
|
|
302
|
-
---
|
|
303
|
-
|
|
304
|
-
### NFC Lock Management
|
|
305
|
-
|
|
306
|
-
For app-level NFC session lifecycle management:
|
|
307
|
-
|
|
308
|
-
```typescript
|
|
309
|
-
import {
|
|
310
|
-
isNfcOperationLocked,
|
|
311
|
-
releaseNfcOperationLock,
|
|
312
|
-
markNfcOperationCancelledByCleanup,
|
|
313
|
-
consumeNfcOperationCancelledByCleanup,
|
|
314
|
-
} from '@ukeyfe/react-native-nfc-litecard';
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
| Method | Description |
|
|
318
|
-
|--------|-------------|
|
|
319
|
-
| `isNfcOperationLocked()` | Check if the NFC operation lock is held |
|
|
320
|
-
| `releaseNfcOperationLock()` | Force-release the lock (use on page unmount) |
|
|
321
|
-
| `markNfcOperationCancelledByCleanup()` | Mark current operation as interrupted by cleanup |
|
|
322
|
-
| `consumeNfcOperationCancelledByCleanup()` | Consume the cleanup flag (returns whether it was set) |
|
|
323
|
-
|
|
324
|
-
## NfcResult Structure
|
|
325
|
-
|
|
326
|
-
All APIs return a unified `NfcResult`:
|
|
327
|
-
|
|
328
|
-
```typescript
|
|
329
|
-
interface NfcResult {
|
|
330
|
-
code: number; // Status code – compare against ResultCode
|
|
331
|
-
success: boolean; // Whether the operation succeeded
|
|
332
|
-
data?: { // Optional data, only present for some operations
|
|
333
|
-
mnemonic?: string;
|
|
334
|
-
type?: string;
|
|
335
|
-
entropyHex?: string;
|
|
336
|
-
rawBytes?: string;
|
|
337
|
-
nickname?: string;
|
|
338
|
-
retryCount?: number;
|
|
339
|
-
aesKeyHex?: string;
|
|
340
|
-
crc16?: number;
|
|
341
|
-
};
|
|
342
|
-
}
|
|
343
|
-
```
|
|
344
|
-
|
|
345
|
-
## Error Handling Example
|
|
346
|
-
|
|
347
|
-
```typescript
|
|
348
|
-
import { ResultCode, readMnemonic } from '@ukeyfe/react-native-nfc-litecard';
|
|
349
|
-
|
|
350
|
-
const result = await readMnemonic('password');
|
|
351
|
-
|
|
352
|
-
switch (result.code) {
|
|
353
|
-
case ResultCode.READ_SUCCESS:
|
|
354
|
-
console.log('Read successful:', result.data?.mnemonic);
|
|
355
|
-
break;
|
|
356
|
-
case ResultCode.AUTH_WRONG_PASSWORD:
|
|
357
|
-
alert('Wrong password');
|
|
358
|
-
break;
|
|
359
|
-
case ResultCode.NFC_CONNECT_FAILED:
|
|
360
|
-
alert('NFC connection failed, please re-tap the card');
|
|
361
|
-
break;
|
|
362
|
-
case ResultCode.NFC_USER_CANCELED:
|
|
363
|
-
// iOS user cancelled – handle silently
|
|
364
|
-
break;
|
|
365
|
-
case ResultCode.READ_TIMEOUT:
|
|
366
|
-
alert('Read timeout – remove and re-tap the card');
|
|
367
|
-
break;
|
|
368
|
-
case ResultCode.RETRY_COUNT_EXHAUSTED:
|
|
369
|
-
alert('Retry count exhausted – card is locked');
|
|
370
|
-
break;
|
|
371
|
-
default:
|
|
372
|
-
alert('Operation failed');
|
|
373
|
-
}
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
## Result Codes
|
|
377
|
-
|
|
378
|
-
### Success Codes
|
|
379
|
-
|
|
380
|
-
| Constant | Value | Description |
|
|
381
|
-
|----------|-------|-------------|
|
|
382
|
-
| `READ_SUCCESS` | 10102 | Mnemonic read successful |
|
|
383
|
-
| `READ_NICKNAME_SUCCESS` | 10103 | Nickname read successful |
|
|
384
|
-
| `CHECK_EMPTY` | 10104 | Empty card |
|
|
385
|
-
| `CHECK_HAS_DATA` | 10105 | Card has data |
|
|
386
|
-
| `READ_RETRY_COUNT_SUCCESS` | 10106 | Retry count read successful |
|
|
387
|
-
| `INIT_SUCCESS` | 10201 | Initialization successful |
|
|
388
|
-
| `WRITE_SUCCESS` | 10203 | Write/update successful |
|
|
389
|
-
| `UPDATE_PASSWORD_SUCCESS` | 10204 | Password change successful |
|
|
390
|
-
| `WRITE_NICKNAME_SUCCESS` | 10205 | Nickname written |
|
|
391
|
-
| `RESET_SUCCESS` | 10206 | Card reset successful |
|
|
392
|
-
| `PRECHECK_HAS_BACKUP` | 10207 | Card already has a valid backup; write was skipped |
|
|
393
|
-
|
|
394
|
-
### Error Codes
|
|
395
|
-
|
|
396
|
-
| Constant | Value | Description |
|
|
397
|
-
|----------|-------|-------------|
|
|
398
|
-
| `NFC_CONNECT_FAILED` | 40001 | NFC connection failed |
|
|
399
|
-
| `AUTH_WRONG_PASSWORD` | 40002 | Wrong password |
|
|
400
|
-
| `AUTH_INVALID_RESPONSE` | 40003 | Invalid authentication response |
|
|
401
|
-
| `AUTH_VERIFY_FAILED` | 40004 | Authentication verification failed |
|
|
402
|
-
| `READ_FAILED` | 40005 | Read failed |
|
|
403
|
-
| `WRITE_FAILED` | 40006 | Write failed |
|
|
404
|
-
| `INVALID_MNEMONIC` | 40007 | Invalid mnemonic |
|
|
405
|
-
| `UNSUPPORTED_MNEMONIC_LENGTH` | 40008 | Unsupported mnemonic length |
|
|
406
|
-
| `INVALID_CARD_DATA` | 40009 | Invalid card data |
|
|
407
|
-
| `UNKNOWN_ERROR` | 40010 | Unknown error |
|
|
408
|
-
| `NFC_USER_CANCELED` | 40011 | User cancelled NFC scan (iOS) |
|
|
409
|
-
| `READ_TIMEOUT` | 40012 | Read timeout |
|
|
410
|
-
| `NFC_LOCK_TIMEOUT` | 40013 | NFC lock timeout |
|
|
411
|
-
| `CRC16_CHECK_FAILED` | 40014 | CRC16 check failed |
|
|
412
|
-
| `RETRY_COUNT_EXHAUSTED` | 40015 | PIN retry count exhausted, card locked |
|
|
413
|
-
|
|
414
|
-
## Storage Format
|
|
415
|
-
|
|
416
|
-
The card stores BIP-39 mnemonics using entropy compression:
|
|
417
|
-
|
|
418
|
-
```
|
|
419
|
-
[type 1B] [entropy 16-32B] [CRC16 2B]
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
| Type | Mnemonic Length | Entropy Size |
|
|
423
|
-
|------|----------------|--------------|
|
|
424
|
-
| 0x01 | 12 words | 16 bytes (128-bit) |
|
|
425
|
-
| 0x02 | 15 words | 20 bytes (160-bit) |
|
|
426
|
-
| 0x03 | 18 words | 24 bytes (192-bit) |
|
|
427
|
-
| 0x04 | 21 words | 28 bytes (224-bit) |
|
|
428
|
-
| 0x05 | 24 words | 32 bytes (256-bit) |
|
|
429
|
-
|
|
430
|
-
## Security
|
|
431
|
-
|
|
432
|
-
- **AES-128 hardware-level mutual authentication** (3-pass)
|
|
433
|
-
- **SHA-256 key derivation**: password → SHA-256 → first 16 bytes as AES key
|
|
434
|
-
- **CRC16-Modbus checksum**: data integrity verification
|
|
435
|
-
- **PIN retry counter**: auto-decrements on wrong password, resets to 10 on success
|
|
436
|
-
- **Secure random**: authentication uses `crypto.getRandomValues()` (requires Hermes ≥ 0.72 or `react-native-get-random-values` polyfill)
|
|
437
|
-
|
|
438
|
-
## Project Structure
|
|
439
|
-
|
|
440
|
-
```
|
|
441
|
-
src/
|
|
442
|
-
├── index.ts # Public API exports
|
|
443
|
-
├── constants.ts # Shared constants (page addresses, NFC commands, mnemonic types)
|
|
444
|
-
├── types.ts # Unified ResultCode, NfcResult interface, error mapping
|
|
445
|
-
├── crypto.ts # AES encrypt/decrypt, key derivation, secure random
|
|
446
|
-
├── utils.ts # CRC16, hex conversion, array utilities
|
|
447
|
-
├── nfc-core.ts # NFC lock, transceive, authentication, retry counter
|
|
448
|
-
├── reader.ts # Reader API
|
|
449
|
-
└── writer.ts # Writer API
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
## Platform Support
|
|
453
|
-
|
|
454
|
-
| Platform | Technology | Requirements |
|
|
455
|
-
|----------|-----------|--------------|
|
|
456
|
-
| iOS | MifareIOS | iPhone 7 or later |
|
|
457
|
-
| Android | NfcA | NFC-capable device |
|
|
458
|
-
|
|
459
|
-
## License
|
|
460
|
-
|
|
461
|
-
MIT
|