2.0.1 • Public • Published


The library is developed handle communication of Android smartphones/iPhones with NFC TON Labs Security cards. It provides a useful API to work with all functionality (i.e. APDU commands) supported by NFC TON Labs Security card. The technical specification of TON Labs Security card can be found here

  • For the case of iPhone you must have iOS version >= 13 and iPhone model >= 7. For the case of Android you should also check whether it supports NFC feature.
  • Android native code of ton-nfc-client uses TonNfcClientAndroid library to handle NFC, and iOS native code respectively uses TonNfcClientSwift library.


$ npm install ton-nfc-client --save

Or take it from GitHub.

$ npm install git+

Additional steps for Android

Also you need to add NFC related stuff into AndroidManifest.xml of your React native app.

    	<action android:name="android.nfc.action.TECH_DISCOVERED" />
<meta-data android:name="android.nfc.action.TECH_DISCOVERED" android:resource="@xml/nfc_tech_filter" />

For this to work you must have an appropriate nfc_tech_filter.xml file in your xml subfolder (\app\src\main\res\xml).

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">

Additional steps for iOS

Run pod install from ios subdirectory of your React native app.

Also you must go through the following steps to make NFC working for you.

  • Open .workspace file of your project in Xcode.

  • Go to Signing & Capabilities tab and add capability Near Field Communication Tag Reading.

  • Add into info.plist the point Privacy - NFC Scan Usage Description and value for it Test NFC.

  • Add into info.plist the point ISO7816 application identifiers for NFC Tag Reader Session and add for it the following items: 313132323333343435353636, A000000151000000.

  • Add into info.plist the point and add for it string item TAG.

  • Check that everywhere you have iOS11+ deployment version. Otherwise, pod installation will complain about it.

  • Also there is possibility that you have to link libswiftCoreNFC library manually. For this go to Target->Application -> Build Phases -> Link Binary With Libraries and add libswiftCoreNFC.tbd.

  • Go to Project -> Application -> Build Settings -> Search Paths -> Library Search Paths and check that you have at least swift-5.2 everywhere.

Simple example

import {NfcCardModuleWrapper} from 'ton-nfc-client';

const nfcWrapper = new NfcCardModuleWrapper();
try {
	let hdIndex = "1";     
	let result = await nfcWrapper.getPublicKey(hdIndex);
	const publicKey = result.message;
  	alert("Public key: " + publicKey);
catch (e) {

Another way to use ton-nfc-client functions looks as follows.

   .then((result) => alert("Public key for HD path m/44'/396'/0'/0'/" + hdIndex + "' : " + result.message))
   .catch((e) => alert(e.message));

Note: You can not work with NFC using simulator. You must run it on smartphone/iPhone. So for iPhone you should set the development team.

More about responses format and errors

To get more information about responses formats and errors please visit this pages: Android error list, iOS error list, Android readme, iOS readme.

Card activation

Detailed information about card activation is available here Android readme, iOS readme, card activation doc. Here we just give exemplary code for React native app.

import {NfcCardModuleWrapper, CardResponseMessage, CardStates, CardResponseStatus} from 'ton-nfc-client';

const nfcWrapper = new NfcCardModuleWrapper();
try {
	result = await nfcWrapper.getHashes();
	const hashOfCommonSecret = result.ecsHash;
	// check that hashOfCommonSecret is correct based on the data from smartcontract

	const hashOfEncryptedPassword = result.epHash;
	// check that hashOfEncryptedPassword is correct based on the data from smartcontract
	const newPin = "7777";
	// prepare authenticationPassword, commonSecret, initialVector based on the data from smartcontract
	const serialNumber =;

	await nfcWrapper.turnOnWallet(authenticationPassword, commonSecret, initialVector);
	//await nfcWrapper.turnOnWalletWithPin(newPin, authenticationPassword, commonSecret, initialVector)

catch (e) {

Recovery module

Detailed information about recovery functionality is available in Android readme, iOS readme. Here we just give the exemplary code for React native app. There is a snippet demonstrating the structure of recovery data and the way of adding it into TON Labs wallet applet.

let aesjs = require('aes-js');
try {
	// get aesKeyHexString from TON Labs service
	const aesKeyBytes = aesjs.utils.hex.toBytes(aesKeyHexString);
	const surfPublicKey = "B81F0E0E07416DAB6C320ECC6BF3DBA48A70101C5251CC31B1D8F831B36E9F2A";
	const multisigAddr = "A11F0E0E07416DAB6C320ECC6BF3DBA48A70121C5251CC31B1D8F8A1B36E0F2F";
	const authenticationPassword = "F4B072E1DF2DB7CF6CD0CD681EC5CD2D071458D278E6546763CBB4860F8082FE14418C8A8A55E2106CBC6CB1174F4BA6D827A26A2D205F99B7E00401DA4C15ACC943274B92258114B5E11C16DA64484034F93771547FBE60DA70E273E6BD64F8A4201A9913B386BCA55B6678CFD7E7E68A646A7543E9E439DD5B60B9615079FE";
	const commonSecret = "7256EFE7A77AFC7E9088266EF27A93CB01CD9432E0DB66D600745D506EE04AC4";
	//in real app take surfPublicKey, multisigAddr, authenticationPassword, commonSecret from TON Labs service
	const recoveryDataJson = JSON.stringify( {
            surfPublicKey:  surfPublicKey,
            multisigAddress:  multisigAddr,
            p1: authenticationPassword, 
            cs: commonSecret 
	const recoveryDataBytes = aesjs.utils.utf8.toBytes(recoveryDataJson);
	const aesCtr = new aesjs.ModeOfOperation.ctr(aesKeyBytes, new aesjs.Counter(5));
  	const encryptedBytes = aesCtr.encrypt(recoveryDataBytes);
	const encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes);
	const result = await nfcWrapper.addRecoveryData(encryptedHex);
	const addRes = result.message;
  	console.log("Add recovery data into card result  = " + addRes);
catch (e) {

There is an exemplary snippet demonstrating how to get the recovery data from TON Labs wallet applet.

try {
	// get aesKeyHexString from somewhere
	const aesKeyBytes = aesjs.utils.hex.toBytes(aesKeyHexString);
	const result = await nfcWrapper.getRecoveryData();
  	const encryptedRecoveryDataFromSecurityCard = result.message;
	const encryptedRecoveryDataFromSecurityCardBytes =  aesjs.utils.hex.toBytes(encryptedRecoveryDataFromSecurityCard);
	const aesCtr = new aesjs.ModeOfOperation.ctr(aesKeyBytes, new aesjs.Counter(5));
  	const decryptedBytes = aesCtr.decrypt(encryptedRecoveryDataFromSecurityCardBytes);
  	const decryptedRecoveryDataJson = aesjs.utils.utf8.fromBytes(decryptedBytes);
  	console.log("Decrypted recovery data : " + decryptedRecoveryDataJson);
catch (e) {

Protection against MITM

We protect the most critical card operations (APDU commands) against MITM attack by HMAC SHA256 signature. In this case the data field of such APDU is extended by 32-bytes sault generated by the card and the final byte array is signed. The obtained signature is added to the end of APDU data, i.e. its data field has the structure: payload || sault || sign(payload || sault). When the card gets such APDU, first it verifies sault and signature.

The secret key for HMAC SHA256 is produced based on card activation data. This key is saved into Android keystore or iOS keychain and then is used by the app to sign APDU commands data fields. Usually after correct card activation in the app (call of nfcWrapper.turnOnWallet) this key is produced and saved. So no extra code is required.

Another situation is possible. Let's suppose you activated the card earlier. After that you reinstalled the app working with NFC TON Labs security card or you started using new device. Then Android keystore/iOS keychain does not have the key to sign APDU commands. You must create it.

 nfcWrapper.createKeyForHmac(authenticationPassword, commonSecret, serialNumber)

You may work with multiple NFC TON Labs security cards. In this case in your Android keystore/iOS keychain there is a bunch of keys. Each key is marked by corresponding serial number. And you can get the list of serial numbers for which you have the key in keystore/keychain.

The list of operations protected by HMAC SHA256:

  • verifyPin, signForDefaultHdPath, sign, verifyPinAndSign, verifyPinAndSignForDefaultHdPath, checkSerialNumberAndSign, checkSerialNumberAndVerifyPinAndSign, checkSerialNumberAndSignForDefaultHdPath, checkSerialNumberAndVerifyPinAndSignForDefaultHdPath;
  • all functions related to card keychain.

Request ED25519 signature

The basic functionality provided by NFC TON Labs security card is Ed25519 signature. You may request public key and request the signature for some message.

import nacl from "tweetnacl";

hexStringToByteArray(hexStr) {
  	let bytes = [];
  	while (hexStr.length >= 2) {
     		bytes.push(parseInt(hexStr.substring(0, 2), 16));
     		hexStr = hexStr.substring(2, hexStr.length);
  	return new Uint8Array(bytes);

try {
	const msg = "0000"; //Some hex string of even length 
	const pin = "5555";
	let result = await nfcWrapper.verifyPinAndSignForDefaultHdPath(msg, pin);
	const signature = result.message;
	result = await nfcWrapper.getPublicKeyForDefaultPath();
  	const pubKey = result.message;
	const msgBytes = hexStringToByteArray(msg);
  	const signatureBytes = hexStringToByteArray(signature);
  	const pubKeyBytes = hexStringToByteArray(pubKey);
  	const sigVerificationRes = nacl.sign.detached.verify(msgBytes, signatureBytes, pubKeyBytes);
  	if (sigVerificationRes == false) {
       		throw new Error("Signature is not correct.");
catch (e) {

Note: Functions verifyPinAndSignForDefaultHdPath, verifyPinAndSign are protected by HMAC SHA256 signature. But also there is an additional protection for them by PIN code. You have 10 attempts to enter PIN. After 10th fail you will not be able to use existing seed (keys for ed25519). The only way to unblock these functions is to reset the seed (see resetWallet function) and generate new seed (see generateSeed). After resetting the seed PIN will be also reset to default value 5555.

Card keychain

The detailed information about card keychain is available here Android readme, iOS readme. Here we just give the exemplary code for React native app. The below snippet demonstrates the work with keychain. We add one key and then retrieve it from the card. Then we replace it by a new key of the same length. In the end we delete the key.

try {
  	const key = "B81F0E0E07416DAB6C320ECC6BF3DBA48A70101C5251CC31B1D8F831B36E9F2A";
  	let result = await nfcWrapper.addKeyIntoKeyChain(key);
	const keyHmac = result.message;
	result = await nfcWrapper.getKeyFromKeyChain(keyHmac);
	const keyFromCard = result.message;
	const newKey = "AA1F0E0E07416DAB6C320ECC6BF3DBA48A70101C5251CC31B1D8F831B36E9F25";
	result = await nfcWrapper.changeKey(newKey, keyHmac);
	const newKeyHmac = result.message;
	result = await nfcWrapper.getNumberOfKeys();
	let numberOfKeys = result.message;
  	console.log("Number of keys = " + numberOfKeys);
  	await nfcWrapper.deleteKeyFromKeyChain(newKeyHmac);	
	result = await nfcWrapper.getNumberOfKeys();
	numberOfKeys = result.message;
	console.log("Number of keys = " + numberOfKeys);
catch (e) {

Full functions list

The full list of functions provided by the library to communicate with the card you will find here

Catching NFC related events from ton-nfc-client

For Android add into your app the following exemplary code:

import {NfcEventsEmitterWrapper} from 'ton-nfc-client';
import Toast from 'react-native-simple-toast';

NfcEventsEmitterWrapper.addListener((msg) => {
  Toast.showWithGravity(msg, Toast.LONG, Toast.TOP);

For iOS you additionaly should add this.

if (Platform.OS === "ios") {

Package Sidebar


npm i ton-nfc-client

Weekly Downloads






Unpacked Size

1.36 MB

Total Files


Last publish


  • tonlabs