npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ukeyfe/react-native-nfc-litecard

v1.0.11

Published

NFC read/write for MIFARE Ultralight AES (LiteCard mnemonic storage)

Readme

@ukeyfe/react-native-nfc-litecard

English | 中文

React Native NFC 读写库,适用于 MIFARE Ultralight AES(MF0AES(H)20),专为 LiteCard 助记词存储设计。

设计原则:库只返回状态码(code)和数据(data),提供面向用户的提示文案。调用方应根据 NfcStatusCode 自行映射本地化字符串。

功能概览

| 功能 | 方法 | 说明 | |------|------|------| | 检测卡片 | checkCard() | 检测卡片是否为空或已有数据 | | 读取助记词 | readMnemonic() | 读取 BIP-39 助记词(需密码) | | 读取昵称 | readUserNickname() | 读取卡片上的用户昵称 | | 读取重试次数 | readMnemonicRetryCount() | 读取 PIN 重试计数器 | | 重置重试次数 | resetRetryCountTo10() | 将 PIN 重试计数器重置为默认值(10) | | 初始化卡片 | initializeCard() | 用出厂密码认证,写入助记词 + 设置新密码 | | 更新卡片 | updateCard() | 更新助记词和密码(需旧密码) | | 修改密码 | updatePassword() | 仅修改密码(需旧密码) | | 写入昵称 | writeUserNickname() | 写入用户昵称 | | 重置卡片 | resetCard() | 清除助记词数据,设置新密码,保持保护开启 | | 卡片版本 | getCardVersion() | 读取卡片产品版本信息(无需认证) | | 真伪校验 | readOriginality() | 读取 ECC 原厂签名,验证 NXP 正品 |

安装

npm install @ukeyfe/react-native-nfc-litecard

对等依赖

本库需要以下对等依赖:

npm install react-native react-native-nfc-manager

快速开始

import {
  NfcStatusCode,
  checkCard,
  readMnemonic,
  initializeCard,
  updateCard,
  updatePassword,
  writeUserNickname,
  readUserNickname,
  resetCard,
  readMnemonicRetryCount,
  resetRetryCountTo10,
  getCardVersion,
  readOriginality,
} from '@ukeyfe/react-native-nfc-litecard';

API 文档

checkCard(password?, onCardIdentified?)

检测卡片状态(空卡 / 有数据)。

无密码(快速探测):

const result = await checkCard();
if (result.code === NfcStatusCode.CHECK_EMPTY) {
  // 空卡 – 可以初始化
} else if (result.code === NfcStatusCode.CHECK_HAS_DATA) {
  // 有数据(或已读保护,无法确定)
}

有密码(认证后深度检测,适用于读保护卡):

const result = await checkCard('password');
if (result.code === NfcStatusCode.CHECK_EMPTY) {
  // 空卡 – 可以写入
} else if (result.code === NfcStatusCode.CHECK_HAS_DATA) {
  // 已有合法助记词备份,类型:result.data?.type
} else if (result.code === NfcStatusCode.AUTH_WRONG_PASSWORD) {
  // 密码错误
}

参数: | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | password | string | 否 | 卡片保护密码。不传时直接读取(适合未加密卡片);传入时先进行 AES 认证,再读取全量数据并 CRC16 校验(适合读保护卡,结果更准确)。 | | onCardIdentified | () => void | 否 | NFC 会话建立后回调,可用于 UI 提示如"已检测到卡片"。 |

返回状态码: | 状态码 | 含义 | |--------|------| | NfcStatusCode.CHECK_EMPTY (10104) | 空卡 – 无助记词数据 | | NfcStatusCode.CHECK_HAS_DATA (10105) | 卡片有数据。传入密码时,data.type 包含助记词类型(如 "12 words (128-bit)")。 | | NfcStatusCode.AUTH_WRONG_PASSWORD (40002) | 密码错误(仅在传入密码时可能出现) | | NfcStatusCode.NFC_CONNECT_FAILED (40001) | NFC 连接失败 – 未贴卡或设备不支持 |


readMnemonic(password, onCardIdentified?)

读取助记词(需密码认证)。认证前自动递减重试计数器,认证成功后重置为 10。

const result = await readMnemonic('your-password');
if (result.success) {
  console.log('助记词:', result.data?.mnemonic);
  console.log('类型:', result.data?.type);           // "12 words (128-bit)"
  console.log('昵称:', result.data?.nickname);
  console.log('重试次数:', result.data?.retryCount);
}

参数: | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | password | string | 是 | 卡片保护密码,用于 AES-128 认证 | | onCardIdentified | () => void | 否 | 认证成功、开始读取前回调,可用于 UI 提示如"正在读取"。 |

返回 data 字段: | 字段 | 类型 | 说明 | |------|------|------| | mnemonic | string | BIP-39 助记词(如 "abandon abandon ... about") | | type | string | 助记词类型(如 "12 words (128-bit)""24 words (256-bit)") | | entropyHex | string | 熵的十六进制字符串 | | rawBytes | string | 卡上原始数据的十六进制字符串(类型 + 熵) | | nickname | string | 用户昵称(如卡上已设置) | | retryCount | number | 认证成功后的重试次数(正常为 10) |


initializeCard(mnemonic, newPassword, defaultPassword, onCardIdentified?)

初始化卡片:用出厂默认密码认证,写入助记词,设置新密码。卡片必须已启用 AES 保护(出厂默认)。

const result = await initializeCard(
  'abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about',
  'your-password',
  '000000'  // 出厂默认密码
);
if (result.code === NfcStatusCode.INIT_SUCCESS) {
  console.log('初始化成功');
}

参数: | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | mnemonic | string | 是 | BIP-39 助记词,支持 12/15/18/21/24 个词。库会转为熵 + CRC16 写入卡片。 | | newPassword | string | 是 | 新保护密码,用于派生 AES-128 密钥写入卡片。 | | defaultPassword | string | 是 | 卡片当前(出厂默认)密码,用于认证。 | | onCardIdentified | () => void | 否 | 认证成功、开始写入前回调,可用于 UI 提示如"正在写入"。 |


updateCard(oldPassword, newPassword, newMnemonic, onCardIdentified?, options?)

更新卡片:使用旧密码认证后,写入新助记词和新密码。认证前自动递减重试计数器。

const result = await updateCard('old-password', 'new-password', 'new mnemonic words ...');
if (result.code === NfcStatusCode.WRITE_SUCCESS) {
  console.log('更新成功');
}

预检查已有备份:

const result = await updateCard('old-password', 'new-password', 'mnemonic ...', undefined, {
  precheckExistingMnemonic: true,
});
if (result.code === NfcStatusCode.PRECHECK_HAS_BACKUP) {
  // 卡片已有合法助记词备份,跳过写入
  console.log('已有备份,类型:', result.data?.type);
} else if (result.code === NfcStatusCode.WRITE_SUCCESS) {
  console.log('写入成功');
}

参数: | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | oldPassword | string | 是 | 当前卡片密码,用于 AES 认证 | | newPassword | string | 是 | 新密码,写入后卡片使用此密码保护 | | newMnemonic | string | 是 | 新 BIP-39 助记词(12/15/18/21/24 个词) | | onCardIdentified | () => void | 否 | 认证成功后回调,可用于 UI 提示如"正在写入"。 | | options.precheckExistingMnemonic | boolean | 否 | 为 true 时,认证后先读取卡片数据并校验 CRC16,若已有合法助记词则返回 PRECHECK_HAS_BACKUP,不执行写入。 |


updatePassword(oldPassword, newPassword, onCardIdentified?)

仅修改密码,卡片上的助记词数据不变。认证前自动递减重试计数器。

const result = await updatePassword('old-password', 'new-password');
if (result.code === NfcStatusCode.UPDATE_PASSWORD_SUCCESS) {
  console.log('密码修改成功');
}

参数: | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | oldPassword | string | 是 | 当前卡片密码,用于 AES 认证 | | newPassword | string | 是 | 新密码,修改后卡片使用此密码 | | onCardIdentified | () => void | 否 | 认证成功后回调 |


writeUserNickname(password, nickname, onCardIdentified?)

写入用户昵称。昵称以 UTF-8 编码,最大 12 字节(超出部分截断)。

const result = await writeUserNickname('your-password', 'MyCard');
if (result.code === NfcStatusCode.WRITE_NICKNAME_SUCCESS) {
  console.log('昵称写入成功');
}

参数: | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | password | string | 是 | 卡片保护密码,用于 AES 认证 | | nickname | string | 是 | 用户昵称;UTF-8 最大 12 字节(约 4 个中文字符或 12 个英文字母) | | onCardIdentified | () => void | 否 | 认证成功后回调 |


readUserNickname(password?, onCardIdentified?)

读取卡片上的用户昵称。

const result = await readUserNickname('your-password');
if (result.success) {
  console.log('昵称:', result.data?.nickname);
}

参数: | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | password | string | 否 | 卡片密码。读保护启用时(PROT=1)必填,否则可选。 | | onCardIdentified | () => void | 否 | 认证成功后回调 |


resetCard(password, newPassword, onCardIdentified?)

重置卡片:清除助记词数据,设置新密码,关闭读写保护。昵称保留不变。

const result = await resetCard('old-password', 'new-password');
if (result.code === NfcStatusCode.RESET_SUCCESS) {
  console.log('重置成功');
}

⚠️ 此操作不可逆,卡片上的助记词数据将被永久擦除。

参数: | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | password | string \| undefined | 否 | 当前卡片密码。卡片已保护时必填,否则传 undefined。认证前自动递减重试计数器。 | | newPassword | string | 是 | 重置后要设置的密码。 | | onCardIdentified | () => void | 否 | 认证成功后回调 |


readMnemonicRetryCount(onCardIdentified?)

读取卡片上的 PIN 重试计数器。无需密码认证(计数器页面在保护区域之外)。

const result = await readMnemonicRetryCount();
if (result.success) {
  console.log('剩余重试次数:', result.data?.retryCount);
}

参数: | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | onCardIdentified | () => void | 否 | NFC 会话建立后回调 |


resetRetryCountTo10(onCardIdentified?)

将 PIN 重试计数器重置为默认值(10)。无需密码认证。

const result = await resetRetryCountTo10();

参数: | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | onCardIdentified | () => void | 否 | NFC 会话建立后回调 |


getCardVersion(onCardIdentified?)

读取卡片产品版本信息。无需密码认证。

const result = await getCardVersion();
if (result.success) {
  console.log('厂商:', result.data?.version?.vendorId);   // 0x04 = NXP
  console.log('类型:', result.data?.version?.productType);   // 0x03 = Ultralight
  console.log('版本:', result.data?.version?.majorVersion); // 0x04 = AES
}

readOriginality(onCardIdentified?)

读取 ECC 原厂签名,验证卡片是否为 NXP 正品。无需密码认证。

const result = await readOriginality();
if (result.success) {
  console.log('签名:', result.data?.signature); // 96 字符 hex 字符串
}

NFC 锁管理

用于应用层 NFC 会话生命周期管理:

import {
  isNfcOperationLocked,
  releaseNfcOperationLock,
  markNfcOperationCancelledByCleanup,
  consumeNfcOperationCancelledByCleanup,
} from '@ukeyfe/react-native-nfc-litecard';

| 方法 | 说明 | |------|------| | isNfcOperationLocked() | 检查 NFC 操作锁是否被持有 | | releaseNfcOperationLock() | 强制释放锁(用于页面卸载时) | | markNfcOperationCancelledByCleanup() | 标记当前操作被清理中断 | | consumeNfcOperationCancelledByCleanup() | 消费清理标志(返回是否被设置过) |

NfcResult 结构

所有 API 返回统一的 NfcResult

interface NfcResult {
  code: number;       // 状态码 – 与 NfcStatusCode 比较
  success: boolean;   // 操作是否成功
  data?: {            // 可选数据,仅部分操作返回
    mnemonic?: string;
    type?: string;
    entropyHex?: string;
    rawBytes?: string;
    nickname?: string;
    retryCount?: number;
    aesKeyHex?: string;
    crc16?: number;
  };
}

错误处理示例

import { NfcStatusCode, readMnemonic } from '@ukeyfe/react-native-nfc-litecard';

const result = await readMnemonic('password');

switch (result.code) {
  case NfcStatusCode.READ_SUCCESS:
    console.log('读取成功:', result.data?.mnemonic);
    break;
  case NfcStatusCode.AUTH_WRONG_PASSWORD:
    alert('密码错误');
    break;
  case NfcStatusCode.NFC_CONNECT_FAILED:
    alert('NFC 连接失败,请重新贴卡');
    break;
  case NfcStatusCode.NFC_USER_CANCELED:
    // iOS 用户取消 – 静默处理
    break;
  case NfcStatusCode.READ_TIMEOUT:
    alert('读取超时 – 请移开卡片后重新贴卡');
    break;
  case NfcStatusCode.RETRY_COUNT_EXHAUSTED:
    alert('重试次数已耗尽 – 卡片已锁定');
    break;
  default:
    alert('操作失败');
}

状态码

成功码

| 常量 | 值 | 说明 | |------|------|------| | READ_SUCCESS | 10102 | 助记词读取成功 | | READ_NICKNAME_SUCCESS | 10103 | 昵称读取成功 | | CHECK_EMPTY | 10104 | 空卡 | | CHECK_HAS_DATA | 10105 | 卡片有数据 | | READ_RETRY_COUNT_SUCCESS | 10106 | 重试次数读取成功 | | GET_VERSION_SUCCESS | 10107 | 卡片版本读取成功 | | READ_SIG_SUCCESS | 10108 | 原厂签名读取成功 | | INIT_SUCCESS | 10201 | 初始化成功 | | WRITE_SUCCESS | 10203 | 写入/更新成功 | | UPDATE_PASSWORD_SUCCESS | 10204 | 密码修改成功 | | WRITE_NICKNAME_SUCCESS | 10205 | 昵称写入成功 | | RESET_SUCCESS | 10206 | 卡片重置成功 | | PRECHECK_HAS_BACKUP | 10207 | 卡片已有合法备份,跳过写入 |

错误码

| 常量 | 值 | 说明 | |------|------|------| | NFC_CONNECT_FAILED | 40001 | NFC 连接失败 | | AUTH_WRONG_PASSWORD | 40002 | 密码错误 | | AUTH_INVALID_RESPONSE | 40003 | 认证响应无效 | | AUTH_VERIFY_FAILED | 40004 | 认证校验失败 | | READ_FAILED | 40005 | 读取失败 | | WRITE_FAILED | 40006 | 写入失败 | | INVALID_MNEMONIC | 40007 | 助记词无效 | | UNSUPPORTED_MNEMONIC_LENGTH | 40008 | 不支持的助记词长度 | | INVALID_CARD_DATA | 40009 | 卡片数据无效 | | UNKNOWN_ERROR | 40010 | 未知错误 | | NFC_USER_CANCELED | 40011 | 用户取消 NFC 扫描(iOS) | | READ_TIMEOUT | 40012 | 读取超时 | | NFC_LOCK_TIMEOUT | 40013 | NFC 锁超时 | | CRC16_CHECK_FAILED | 40014 | CRC16 校验失败 | | RETRY_COUNT_EXHAUSTED | 40015 | PIN 重试次数耗尽,卡片已锁定 |

存储格式

卡片使用熵压缩方式存储 BIP-39 助记词:

[类型 1B] [熵 16-32B] [CRC16 2B]

| 类型 | 助记词长度 | 熵大小 | |------|-----------|--------| | 0x01 | 12 个词 | 16 字节(128 位) | | 0x02 | 15 个词 | 20 字节(160 位) | | 0x03 | 18 个词 | 24 字节(192 位) | | 0x04 | 21 个词 | 28 字节(224 位) | | 0x05 | 24 个词 | 32 字节(256 位) |

安全性

  • AES-128 硬件级双向认证(3-pass)
  • SHA-256 密钥派生:密码 → SHA-256 → 取前 16 字节作为 AES 密钥
  • CRC16-Modbus 校验和:数据完整性校验
  • CMAC 安全消息:认证后所有命令附带 AES-CMAC(NIST 800-38B)防篡改
  • PIN 重试计数器:密码错误时自动递减,认证成功后重置为 10
  • 安全随机数:认证使用 crypto.getRandomValues()(需要 Hermes ≥ 0.72 或 react-native-get-random-values polyfill)

项目结构

src/
├── index.ts        # 公共 API 导出
├── constants.ts    # 共享常量(页地址、NFC 指令、助记词类型)
├── types.ts        # 统一 NfcStatusCode、NfcResult 接口、错误映射
├── crypto.ts       # AES 加解密、密钥派生、安全随机数
├── utils.ts        # CRC16、十六进制转换、数组工具
├── nfc-core.ts     # NFC 锁、transceive、认证、重试计数器
├── reader.ts       # 读取 API
└── writer.ts       # 写入 API

平台支持

| 平台 | 技术 | 要求 | |------|------|------| | iOS | MifareIOS | iPhone 7 及以上 | | Android | NfcA | 支持 NFC 的设备 |

许可证

MIT