@ukeyfe/react-native-nfc-litecard 1.0.0 → 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 CHANGED
@@ -1,41 +1,41 @@
1
1
  # @ukeyfe/react-native-nfc-litecard
2
2
 
3
- [English](./README.en.md) | 中文
3
+ English | [中文](./README.zh.md)
4
4
 
5
- 基于 **MIFARE Ultralight AES** (MF0AES(H)20) React Native NFC 读写库,用于 LiteCard 助记词存储。
5
+ React Native NFC read/write library for **MIFARE Ultralight AES** (MF0AES(H)20), designed for LiteCard mnemonic storage.
6
6
 
7
- > **设计原则**:库只返回状态码 (`code`) 和数据 (`data`),**不提供用户提示文案**。调用方根据 `ResultCode` 自行处理本地化提示。
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
- | 检测卡片 | `checkCard()` | 判断卡片是空卡还是已有数据 |
14
- | 读取助记词 | `readMnemonic()` | 密码认证后读取 BIP-39 助记词 |
15
- | 读取昵称 | `readUserNickname()` | 读取卡片上的用户昵称 |
16
- | 读取重试次数 | `readMnemonicRetryCount()` | 读取 PIN 重试计数器 |
17
- | 重置重试次数 | `resetRetryCountTo10()` | 重置 PIN 重试计数器为默认值 10 |
18
- | 初始化卡片 | `initializeCard()` | 向空卡写入助记词并设置密码保护 |
19
- | 更新卡片 | `updateCard()` | 更新助记词和密码(需要旧密码) |
20
- | 修改密码 | `updatePassword()` | 仅修改密码(需要旧密码) |
21
- | 写入昵称 | `writeUserNickname()` | 写入用户昵称到卡片 |
22
- | 重置卡片 | `resetCard()` | 清空数据,密码重置为 "000000" |
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
- 本库需要以下 peer dependencies,请确保项目中已安装:
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,70 +53,85 @@ import {
53
53
  } from '@ukeyfe/react-native-nfc-litecard';
54
54
  ```
55
55
 
56
- ## API 文档
56
+ ## API Reference
57
57
 
58
- ### `checkCard(onCardIdentified?)`
58
+ ### `checkCard(password?, onCardIdentified?)`
59
59
 
60
- 检测卡片状态(空卡 / 有数据)。
60
+ Detect card status (empty / has data).
61
61
 
62
+ **Without password (quick probe):**
62
63
  ```typescript
63
64
  const result = await checkCard();
64
65
  if (result.code === ResultCode.CHECK_EMPTY) {
65
- // 空卡,可以初始化
66
+ // Empty card – ready to initialize
66
67
  } else if (result.code === ResultCode.CHECK_HAS_DATA) {
67
- // 有数据,需要密码读取或更新
68
+ // Has data (or read-protected, cannot determine)
68
69
  }
69
70
  ```
70
71
 
71
- **参数:**
72
- | 参数 | 类型 | 必填 | 说明 |
73
- |------|------|------|------|
74
- | `onCardIdentified` | `() => void` | 否 | 卡片识别成功后的回调 |
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
+ ```
75
83
 
76
- **返回值 (`NfcResult`):**
77
- | code | 含义 |
78
- |------|------|
79
- | `ResultCode.CHECK_EMPTY` (10104) | 空卡 |
80
- | `ResultCode.CHECK_HAS_DATA` (10105) | 有数据 |
81
- | `ResultCode.NFC_CONNECT_FAILED` (40001) | NFC 连接失败 |
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 |
82
97
 
83
98
  ---
84
99
 
85
100
  ### `readMnemonic(password, onCardIdentified?)`
86
101
 
87
- 读取助记词(需要密码认证)。
102
+ Read the mnemonic (password authentication required). The retry counter is decremented before authentication and reset to 10 after a successful authentication.
88
103
 
89
104
  ```typescript
90
105
  const result = await readMnemonic('your-password');
91
106
  if (result.success) {
92
- console.log('助记词:', result.data?.mnemonic);
93
- console.log('类型:', result.data?.type); // "12 words (128-bit)"
94
- console.log('昵称:', result.data?.nickname);
95
- console.log('剩余重试:', result.data?.retryCount);
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);
96
111
  }
97
112
  ```
98
113
 
99
- **参数:**
100
- | 参数 | 类型 | 必填 | 说明 |
101
- |------|------|------|------|
102
- | `password` | `string` | | 卡片保护密码 |
103
- | `onCardIdentified` | `() => void` | | 认证成功后的回调 |
104
-
105
- **返回 `data` 字段:**
106
- | 字段 | 说明 |
107
- |------|------|
108
- | `mnemonic` | BIP-39 助记词 |
109
- | `type` | 助记词类型(如 "12 words (128-bit)" |
110
- | `entropyHex` | 熵的十六进制 |
111
- | `rawBytes` | 原始数据十六进制 |
112
- | `nickname` | 用户昵称(如果有) |
113
- | `retryCount` | 认证成功后重置的重试次数 |
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) |
114
129
 
115
130
  ---
116
131
 
117
132
  ### `initializeCard(mnemonic, password, onCardIdentified?)`
118
133
 
119
- 初始化空卡:写入助记词 + 设置密码保护。
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.
120
135
 
121
136
  ```typescript
122
137
  const result = await initializeCard(
@@ -124,120 +139,172 @@ const result = await initializeCard(
124
139
  'your-password'
125
140
  );
126
141
  if (result.code === ResultCode.INIT_SUCCESS) {
127
- console.log('初始化成功');
142
+ console.log('Initialization successful');
128
143
  }
129
144
  ```
130
145
 
131
- **参数:**
132
- | 参数 | 类型 | 必填 | 说明 |
133
- |------|------|------|------|
134
- | `mnemonic` | `string` | | BIP-39 助记词(12/15/18/21/24 词) |
135
- | `password` | `string` | | 要设置的保护密码 |
136
- | `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". |
137
152
 
138
153
  ---
139
154
 
140
- ### `updateCard(oldPassword, newPassword, newMnemonic, onCardIdentified?)`
155
+ ### `updateCard(oldPassword, newPassword, newMnemonic, onCardIdentified?, options?)`
141
156
 
142
- 更新卡片内容:使用旧密码认证后写入新助记词和新密码。
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.
143
158
 
144
159
  ```typescript
145
160
  const result = await updateCard('old-password', 'new-password', 'new mnemonic words ...');
146
161
  if (result.code === ResultCode.WRITE_SUCCESS) {
147
- console.log('更新成功');
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');
148
177
  }
149
178
  ```
150
179
 
151
- **参数:**
152
- | 参数 | 类型 | 必填 | 说明 |
153
- |------|------|------|------|
154
- | `oldPassword` | `string` | | 当前密码 |
155
- | `newPassword` | `string` | | 新密码 |
156
- | `newMnemonic` | `string` | | 新助记词 |
157
- | `onCardIdentified` | `() => void` | | 认证成功后的回调 |
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. |
158
188
 
159
189
  ---
160
190
 
161
191
  ### `updatePassword(oldPassword, newPassword, onCardIdentified?)`
162
192
 
163
- 仅修改密码,不更改助记词数据。
193
+ Change the password only; mnemonic data on the card is unchanged. The retry counter is decremented automatically before authentication.
164
194
 
165
195
  ```typescript
166
196
  const result = await updatePassword('old-password', 'new-password');
167
197
  if (result.code === ResultCode.UPDATE_PASSWORD_SUCCESS) {
168
- console.log('密码修改成功');
198
+ console.log('Password updated');
169
199
  }
170
200
  ```
171
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
+
172
209
  ---
173
210
 
174
- ### `writeUserNickname(password, nickname)`
211
+ ### `writeUserNickname(password, nickname, onCardIdentified?)`
175
212
 
176
- 写入用户昵称到卡片(最长 12 字节,UTF-8 编码)。
213
+ Write a user nickname to the card. The nickname is UTF-8 encoded, max 12 bytes (longer input is truncated).
177
214
 
178
215
  ```typescript
179
216
  const result = await writeUserNickname('your-password', 'MyCard');
180
217
  if (result.code === ResultCode.WRITE_NICKNAME_SUCCESS) {
181
- console.log('昵称写入成功');
218
+ console.log('Nickname written');
182
219
  }
183
220
  ```
184
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
+
185
229
  ---
186
230
 
187
- ### `readUserNickname(password?)`
231
+ ### `readUserNickname(password?, onCardIdentified?)`
188
232
 
189
- 读取卡片上的用户昵称。如果卡片开启了读保护,需要传密码。
233
+ Read the user nickname from the card.
190
234
 
191
235
  ```typescript
192
236
  const result = await readUserNickname('your-password');
193
237
  if (result.success) {
194
- console.log('昵称:', result.data?.nickname);
238
+ console.log('Nickname:', result.data?.nickname);
195
239
  }
196
240
  ```
197
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
+
198
248
  ---
199
249
 
200
- ### `resetCard(password?, onCardIdentified?)`
250
+ ### `resetCard(password, newPassword, onCardIdentified?)`
201
251
 
202
- 重置卡片:清空所有用户数据,密码设为 `"000000"`。
252
+ Reset the card: wipe mnemonic data, set a new password, and disable read/write protection. Nickname is preserved.
203
253
 
204
254
  ```typescript
205
- const result = await resetCard('your-password');
255
+ const result = await resetCard('old-password', 'new-password');
206
256
  if (result.code === ResultCode.RESET_SUCCESS) {
207
- console.log('重置成功');
257
+ console.log('Reset successful');
208
258
  }
209
259
  ```
210
260
 
211
- > ⚠️ 重置操作不可逆,请谨慎使用。
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 \| 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 |
212
269
 
213
270
  ---
214
271
 
215
- ### `readMnemonicRetryCount()`
272
+ ### `readMnemonicRetryCount(onCardIdentified?)`
216
273
 
217
- 读取当前 PIN 重试计数器的值。
274
+ Read the PIN retry counter on the card. No password authentication is required (the counter page is outside the protected area).
218
275
 
219
276
  ```typescript
220
277
  const result = await readMnemonicRetryCount();
221
278
  if (result.success) {
222
- console.log('剩余重试次数:', result.data?.retryCount);
279
+ console.log('Remaining retries:', result.data?.retryCount);
223
280
  }
224
281
  ```
225
282
 
283
+ **Parameters:**
284
+ | Parameter | Type | Required | Description |
285
+ |-----------|------|----------|-------------|
286
+ | `onCardIdentified` | `() => void` | No | Called after the NFC session is established |
287
+
226
288
  ---
227
289
 
228
- ### `resetRetryCountTo10()`
290
+ ### `resetRetryCountTo10(onCardIdentified?)`
229
291
 
230
- PIN 重试计数器重置为默认值 10
292
+ Reset the PIN retry counter to its default value (10). No password authentication is required.
231
293
 
232
294
  ```typescript
233
295
  const result = await resetRetryCountTo10();
234
296
  ```
235
297
 
298
+ **Parameters:**
299
+ | Parameter | Type | Required | Description |
300
+ |-----------|------|----------|-------------|
301
+ | `onCardIdentified` | `() => void` | No | Called after the NFC session is established |
302
+
236
303
  ---
237
304
 
238
- ### NFC 锁管理
305
+ ### NFC Lock Management
239
306
 
240
- 用于 App 页面生命周期中的 NFC 会话管理:
307
+ For app-level NFC session lifecycle management:
241
308
 
242
309
  ```typescript
243
310
  import {
@@ -248,22 +315,22 @@ import {
248
315
  } from '@ukeyfe/react-native-nfc-litecard';
249
316
  ```
250
317
 
251
- | 方法 | 说明 |
252
- |------|------|
253
- | `isNfcOperationLocked()` | 检查 NFC 操作锁是否被持有 |
254
- | `releaseNfcOperationLock()` | 强制释放锁(页面关闭时使用) |
255
- | `markNfcOperationCancelledByCleanup()` | 标记当前操作被页面清理中断 |
256
- | `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) |
257
324
 
258
- ## NfcResult 返回结构
325
+ ## NfcResult Structure
259
326
 
260
- 所有 API 统一返回 `NfcResult`:
327
+ All APIs return a unified `NfcResult`:
261
328
 
262
329
  ```typescript
263
330
  interface NfcResult {
264
- code: number; // 状态码,与 ResultCode 常量比较
265
- success: boolean; // 操作是否成功
266
- 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
267
334
  mnemonic?: string;
268
335
  type?: string;
269
336
  entropyHex?: string;
@@ -276,7 +343,7 @@ interface NfcResult {
276
343
  }
277
344
  ```
278
345
 
279
- ## 错误处理示例
346
+ ## Error Handling Example
280
347
 
281
348
  ```typescript
282
349
  import { ResultCode, readMnemonic } from '@ukeyfe/react-native-nfc-litecard';
@@ -285,105 +352,110 @@ const result = await readMnemonic('password');
285
352
 
286
353
  switch (result.code) {
287
354
  case ResultCode.READ_SUCCESS:
288
- console.log('读取成功:', result.data?.mnemonic);
355
+ console.log('Read successful:', result.data?.mnemonic);
289
356
  break;
290
357
  case ResultCode.AUTH_WRONG_PASSWORD:
291
- alert('密码错误');
358
+ alert('Wrong password');
292
359
  break;
293
360
  case ResultCode.NFC_CONNECT_FAILED:
294
- alert('NFC 连接失败,请重新贴卡');
361
+ alert('NFC connection failed, please re-tap the card');
295
362
  break;
296
363
  case ResultCode.NFC_USER_CANCELED:
297
- // iOS 用户取消,静默处理
364
+ // iOS user cancelled – handle silently
298
365
  break;
299
366
  case ResultCode.READ_TIMEOUT:
300
- alert('读取超时,请将卡片移开后重新贴近');
367
+ alert('Read timeout – remove and re-tap the card');
368
+ break;
369
+ case ResultCode.RETRY_COUNT_EXHAUSTED:
370
+ alert('Retry count exhausted – card is locked');
301
371
  break;
302
372
  default:
303
- alert('操作失败');
373
+ alert('Operation failed');
304
374
  }
305
375
  ```
306
376
 
307
- ## 返回码一览
308
-
309
- ### 成功码
310
-
311
- | 常量 | | 说明 |
312
- |------|------|------|
313
- | `READ_SUCCESS` | 10102 | 读取助记词成功 |
314
- | `READ_NICKNAME_SUCCESS` | 10103 | 读取昵称成功 |
315
- | `CHECK_EMPTY` | 10104 | 空卡 |
316
- | `CHECK_HAS_DATA` | 10105 | 卡片有数据 |
317
- | `READ_RETRY_COUNT_SUCCESS` | 10106 | 读取重试次数成功 |
318
- | `INIT_SUCCESS` | 10201 | 初始化成功 |
319
- | `WRITE_SUCCESS` | 10203 | 写入/更新成功 |
320
- | `UPDATE_PASSWORD_SUCCESS` | 10204 | 修改密码成功 |
321
- | `WRITE_NICKNAME_SUCCESS` | 10205 | 写入昵称成功 |
322
- | `RESET_SUCCESS` | 10206 | 重置卡片成功 |
323
-
324
- ### 错误码
325
-
326
- | 常量 | 值 | 说明 |
327
- |------|------|------|
328
- | `NFC_CONNECT_FAILED` | 40001 | NFC 连接失败 |
329
- | `AUTH_WRONG_PASSWORD` | 40002 | 密码错误 |
330
- | `AUTH_INVALID_RESPONSE` | 40003 | 认证响应无效 |
331
- | `AUTH_VERIFY_FAILED` | 40004 | 认证验证失败 |
332
- | `READ_FAILED` | 40005 | 读取失败 |
333
- | `WRITE_FAILED` | 40006 | 写入失败 |
334
- | `INVALID_MNEMONIC` | 40007 | 无效的助记词 |
335
- | `UNSUPPORTED_MNEMONIC_LENGTH` | 40008 | 不支持的助记词长度 |
336
- | `INVALID_CARD_DATA` | 40009 | 卡片数据无效 |
337
- | `UNKNOWN_ERROR` | 40010 | 未知错误 |
338
- | `NFC_USER_CANCELED` | 40011 | 用户取消 NFC 扫描 (iOS) |
339
- | `READ_TIMEOUT` | 40012 | 读取超时 |
340
- | `NFC_LOCK_TIMEOUT` | 40013 | NFC 锁超时 |
341
- | `CRC16_CHECK_FAILED` | 40014 | CRC16 校验失败 |
342
-
343
- ## 存储格式
344
-
345
- 卡片使用 BIP-39 熵压缩存储助记词:
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:
346
418
 
347
419
  ```
348
- [类型 1字节] [熵数据 16-32字节] [CRC16 2字节]
420
+ [type 1B] [entropy 16-32B] [CRC16 2B]
349
421
  ```
350
422
 
351
- | 类型值 | 助记词长度 | 熵长度 |
352
- |--------|-----------|--------|
353
- | 0x01 | 12 | 16 字节 (128-bit) |
354
- | 0x02 | 15 | 20 字节 (160-bit) |
355
- | 0x03 | 18 | 24 字节 (192-bit) |
356
- | 0x04 | 21 | 28 字节 (224-bit) |
357
- | 0x05 | 24 | 32 字节 (256-bit) |
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) |
358
430
 
359
- ## 安全机制
431
+ ## Security
360
432
 
361
- - **AES-128 硬件级双向认证**(3-pass mutual authentication
362
- - **SHA-256 密钥派生**:用户密码 → SHA-256 → 取前 16 字节作为 AES 密钥
363
- - **CRC16-Modbus 校验**:数据完整性验证
364
- - **PIN 重试计数器**:密码错误自动递减,成功后恢复为 10
365
- - **安全随机数**:认证使用 `crypto.getRandomValues()`(需 Hermes ≥ 0.72 `react-native-get-random-values` polyfill
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)
366
438
 
367
- ## 项目结构
439
+ ## Project Structure
368
440
 
369
441
  ```
370
442
  src/
371
- ├── index.ts # 公共 API 导出
372
- ├── constants.ts # 共享常量(页地址、NFC 命令、助记词类型)
373
- ├── types.ts # 统一 ResultCodeNfcResult 接口、错误映射
374
- ├── crypto.ts # AES 加解密、密钥派生、安全随机数生成
375
- ├── utils.ts # CRC16hex 转换、数组工具
376
- ├── nfc-core.ts # NFC 锁、transceive、认证、重试计数器
377
- ├── reader.ts # 读卡 API
378
- └── writer.ts # 写卡 API
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
379
451
  ```
380
452
 
381
- ## 平台支持
453
+ ## Platform Support
382
454
 
383
- | 平台 | 技术 | 说明 |
384
- |------|------|------|
385
- | iOS | MifareIOS | 需要 iPhone 7 及以上 |
386
- | Android | NfcA | 需要设备支持 NFC |
455
+ | Platform | Technology | Requirements |
456
+ |----------|-----------|--------------|
457
+ | iOS | MifareIOS | iPhone 7 or later |
458
+ | Android | NfcA | NFC-capable device |
387
459
 
388
460
  ## License
389
461