{"id":21529168,"url":"https://github.com/everx-labs/ton-nfc-client","last_synced_at":"2025-04-09T23:43:20.744Z","repository":{"id":38852028,"uuid":"339324473","full_name":"everx-labs/ton-nfc-client","owner":"everx-labs","description":null,"archived":false,"fork":false,"pushed_at":"2023-03-05T21:11:01.000Z","size":3988,"stargazers_count":4,"open_issues_count":15,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-04-25T17:22:11.021Z","etag":null,"topics":["everscale","nfc","react-native"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/everx-labs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-02-16T08:01:24.000Z","updated_at":"2024-04-25T17:22:10.900Z","dependencies_parsed_at":"2024-04-16T15:33:12.605Z","dependency_job_id":"173c0114-71f5-4a17-a7e1-0ee83efe52d7","html_url":"https://github.com/everx-labs/ton-nfc-client","commit_stats":null,"previous_names":["everx-labs/ton-nfc-client","tonlabs/ton-nfc-client"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/everx-labs%2Fton-nfc-client","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/everx-labs%2Fton-nfc-client/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/everx-labs%2Fton-nfc-client/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/everx-labs%2Fton-nfc-client/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/everx-labs","download_url":"https://codeload.github.com/everx-labs/ton-nfc-client/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248131467,"owners_count":21052819,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["everscale","nfc","react-native"],"created_at":"2024-11-24T01:56:03.829Z","updated_at":"2025-04-09T23:43:20.716Z","avatar_url":"https://github.com/everx-labs.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ton-nfc-client\n\nThe 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 https://ton.surf/scard.\n\n+ For the case of iPhone you must have iOS version \u003e= 13 and iPhone model \u003e= 7. For the case of Android you should also check whether it supports NFC feature.\n+ Android native code of ton-nfc-client uses [TonNfcClientAndroid library](https://github.com/tonlabs/TonNfcClientAndroid) to handle NFC, and iOS native code  respectively uses [TonNfcClientSwift library](https://github.com/tonlabs/TonNfcClientSwift).\n\n## Installation \n\n`$ npm install ton-nfc-client --save`\n\nOr take it from GitHub.\n\n`$ npm install git+https://github.com/tonlabs/ton-nfc-client`\n\n### Additional steps for Android\n\nAlso you need to add NFC related stuff into AndroidManifest.xml of your React native app.\n\n\u003c!--\n\u003cuses-permission android:name=\"android.permission.NFC\" /\u003e\n\u003caction android:name=\"android.nfc.action.TAG_DISCOVERED\" /\u003e\n--\u003e\n\n```xml\n\u003cintent-filter\u003e\n    \t\u003caction android:name=\"android.nfc.action.TECH_DISCOVERED\" /\u003e\n\u003c/intent-filter\u003e\n\u003cmeta-data android:name=\"android.nfc.action.TECH_DISCOVERED\" android:resource=\"@xml/nfc_tech_filter\" /\u003e\n```\n\nFor this to work you must have an appropriate nfc_tech_filter.xml file in your xml subfolder (\\app\\src\\main\\res\\xml).\n```xml\n\u003c?xml version=\"1.0\" encoding=\"utf-8\"?\u003e\n\u003cresources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\"\u003e\n\t\u003ctech-list\u003e\n\t\t\u003ctech\u003eandroid.nfc.tech.IsoDep\u003c/tech\u003e\n\t    \t\u003ctech\u003eandroid.nfc.tech.NfcA\u003c/tech\u003e\n  \t\u003c/tech-list\u003e\n\u003c/resources\u003e\n```\n### Additional steps for iOS\n\nRun `pod install` from **ios** subdirectory of your React native app.\n\nAlso you must go through the following steps to make NFC working for you. \n\n- Open .workspace file of your project in Xcode.\n\n- Go to Signing \u0026 Capabilities tab and add capability **Near Field Communication Tag Reading**.\n\n- Add into info.plist the point **Privacy - NFC Scan Usage Description** and value for it **Test NFC**.\n\n- Add into info.plist the point **ISO7816 application identifiers for NFC Tag Reader Session** and add for it the following items: 313132323333343435353636, A000000151000000.\n\n- Add into info.plist the point **com.apple.developer.nfc.readersession.formats** and add for it string item **TAG**.\n\n- Check that everywhere you have iOS11+ deployment version. Otherwise, pod installation will complain about it.\n\n- Also there is possibility that you have to link libswiftCoreNFC library manually. For this go to **Target-\u003eApplication -\u003e Build Phases -\u003e Link Binary With Libraries** and add **libswiftCoreNFC.tbd**.\n\n- Go to **Project -\u003e Application -\u003e Build Settings -\u003e Search Paths -\u003e Library Search Paths** and check that you have at least **swift-5.2** everywhere.\n\n## Simple example\n\n```javascript\nimport {NfcCardModuleWrapper} from 'ton-nfc-client';\n\nconst nfcWrapper = new NfcCardModuleWrapper();\ntry {\n\tlet hdIndex = \"1\";     \n\tlet result = await nfcWrapper.getPublicKey(hdIndex);\n\tconst publicKey = result.message;\n  \talert(\"Public key: \" + publicKey);\n}\ncatch (e) {\n        alert(e.message);\n}\n```\n\nAnother way to use ton-nfc-client functions looks as follows.\n\n```javascript\nnfcWrapper.getPublicKey(hdIndex)\n   .then((result) =\u003e alert(\"Public key for HD path m/44'/396'/0'/0'/\" + hdIndex + \"' : \" + result.message))\n   .catch((e) =\u003e alert(e.message));\n```\n\n_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.\n\n## More about responses format and errors\n\nTo get more information about responses formats and errors please visit this pages: [Android error list](https://github.com/tonlabs/TonNfcClientAndroid/blob/master/docs/ErrorList.md), [iOS error list](https://github.com/tonlabs/TonNfcClientSwift/blob/master/docs/ErrorList.md), [Android readme](https://github.com/tonlabs/TonNfcClientAndroid/blob/master/README.md), [iOS readme](https://github.com/tonlabs/TonNfcClientSwift/blob/master/README.md).\n\n## Card activation\n\nDetailed information about card activation is available here [Android readme](https://github.com/tonlabs/TonNfcClientAndroid/blob/master/README.md), [iOS readme](https://github.com/tonlabs/TonNfcClientSwift/blob/master/README.md), [card activation doc](https://github.com/tonlabs/TonNfcClientAndroid/blob/master/docs/CardActivation.md). Here we just give exemplary code for React native app.\n\n```javascript\nimport {NfcCardModuleWrapper, CardResponseMessage, CardStates, CardResponseStatus} from 'ton-nfc-client';\n\nconst nfcWrapper = new NfcCardModuleWrapper();\ntry {\n\tresult = await nfcWrapper.getHashes();\n\tconst hashOfCommonSecret = result.ecsHash;\n\t// check that hashOfCommonSecret is correct based on the data from smartcontract\n\n\tconst hashOfEncryptedPassword = result.epHash;\n\t// check that hashOfEncryptedPassword is correct based on the data from smartcontract\n\t\n\tconst newPin = \"7777\";\n\t// prepare authenticationPassword, commonSecret, initialVector based on the data from smartcontract\n\t\n\tconst serialNumber = result.sn;\n\n\tawait nfcWrapper.turnOnWallet(authenticationPassword, commonSecret, initialVector);\n\t//await nfcWrapper.turnOnWalletWithPin(newPin, authenticationPassword, commonSecret, initialVector)\n\n}\ncatch (e) {\n  console.log(e.message);\n}\n```\n\n\u003c!--This code must work without any problems for Android. You connect NFC card only once, then run this code via pressing some button for example. And it does all operations for you. However, it is more complicated for iOS. Each time when we call _nfcWrapper.someFunction()_ iPhone establishes new NFC session. You must reconnect the card each time. So in the above code snippet we need to reconnect the card 4-5 times. More over the above code will not work as it is. After finishing one NFC session iPhone need 5-10 seconds to be ready to establish new NFC session. So the following code may produce error \"System resources unavailable\" for iPhone.\n\n```javascript\nawait nfcWrapper.getHashes();\nawait nfcWrapper.turnOnWallet(authenticationPassword, commonSecret, initialVector);\n```\n\nYou may fix it in the following way.\n\n```javascript\nawait nfcWrapper.getHashes();\nawait new Promise(r =\u003e setTimeout(r, 5000));\nawait nfcWrapper.turnOnWallet(authenticationPassword, commonSecret, initialVector);\n```\nIf you do some time consuming actions between calls of two card operations, then additional delay is not required.--\u003e\n\n## Recovery module\n\nDetailed information about recovery functionality is available in [Android readme](https://github.com/tonlabs/TonNfcClientAndroid/blob/master/README.md), [iOS readme](https://github.com/tonlabs/TonNfcClientSwift/blob/master/README.md). 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.\n\n```javascript\nlet aesjs = require('aes-js');\ntry {\n\t// get aesKeyHexString from TON Labs service\n\tconst aesKeyBytes = aesjs.utils.hex.toBytes(aesKeyHexString);\n\tconst surfPublicKey = \"B81F0E0E07416DAB6C320ECC6BF3DBA48A70101C5251CC31B1D8F831B36E9F2A\";\n\tconst multisigAddr = \"A11F0E0E07416DAB6C320ECC6BF3DBA48A70121C5251CC31B1D8F8A1B36E0F2F\";\n\tconst authenticationPassword = \"F4B072E1DF2DB7CF6CD0CD681EC5CD2D071458D278E6546763CBB4860F8082FE14418C8A8A55E2106CBC6CB1174F4BA6D827A26A2D205F99B7E00401DA4C15ACC943274B92258114B5E11C16DA64484034F93771547FBE60DA70E273E6BD64F8A4201A9913B386BCA55B6678CFD7E7E68A646A7543E9E439DD5B60B9615079FE\";\n\tconst commonSecret = \"7256EFE7A77AFC7E9088266EF27A93CB01CD9432E0DB66D600745D506EE04AC4\";\n\t//in real app take surfPublicKey, multisigAddr, authenticationPassword, commonSecret from TON Labs service\n\tconst recoveryDataJson = JSON.stringify( {\n            surfPublicKey:  surfPublicKey,\n            multisigAddress:  multisigAddr,\n            p1: authenticationPassword, \n            cs: commonSecret \n        });\n\tconst recoveryDataBytes = aesjs.utils.utf8.toBytes(recoveryDataJson);\n\tconst aesCtr = new aesjs.ModeOfOperation.ctr(aesKeyBytes, new aesjs.Counter(5));\n  \tconst encryptedBytes = aesCtr.encrypt(recoveryDataBytes);\n\tconst encryptedHex = aesjs.utils.hex.fromBytes(encryptedBytes);\n\tconst result = await nfcWrapper.addRecoveryData(encryptedHex);\n\tconst addRes = result.message;\n  \tconsole.log(\"Add recovery data into card result  = \" + addRes);\n}\ncatch (e) {\n  \tconsole.log(e.message);\n}\n```\n\nThere is an exemplary snippet demonstrating how to get the recovery data from TON Labs wallet applet.\n\n```javascript\ntry {\n\t// get aesKeyHexString from somewhere\n\tconst aesKeyBytes = aesjs.utils.hex.toBytes(aesKeyHexString);\n\tconst result = await nfcWrapper.getRecoveryData();\n  \tconst encryptedRecoveryDataFromSecurityCard = result.message;\n\tconst encryptedRecoveryDataFromSecurityCardBytes =  aesjs.utils.hex.toBytes(encryptedRecoveryDataFromSecurityCard);\n\tconst aesCtr = new aesjs.ModeOfOperation.ctr(aesKeyBytes, new aesjs.Counter(5));\n  \tconst decryptedBytes = aesCtr.decrypt(encryptedRecoveryDataFromSecurityCardBytes);\n  \tconst decryptedRecoveryDataJson = aesjs.utils.utf8.fromBytes(decryptedBytes);\n  \tconsole.log(\"Decrypted recovery data : \" + decryptedRecoveryDataJson);\n}\ncatch (e) {\n  console.log(e.message);\n}\n```\n\n## Protection against MITM\n\nWe 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.  \n\nThe 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.\n\nAnother 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.\n\n```javascript\n nfcWrapper.createKeyForHmac(authenticationPassword, commonSecret, serialNumber)\n```\n     \nYou 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.\n\nThe list of operations protected by HMAC SHA256:\n\n- verifyPin, signForDefaultHdPath, sign, verifyPinAndSign, verifyPinAndSignForDefaultHdPath, checkSerialNumberAndSign, checkSerialNumberAndVerifyPinAndSign, checkSerialNumberAndSignForDefaultHdPath, checkSerialNumberAndVerifyPinAndSignForDefaultHdPath;\n- all functions related to card keychain.\n\n## Request ED25519 signature\n\nThe basic functionality provided by NFC TON Labs security card is Ed25519 signature. You may request public key and request the signature for some message.\n\n```javascript\nimport nacl from \"tweetnacl\";\n\nhexStringToByteArray(hexStr) {\n  \tlet bytes = [];\n  \twhile (hexStr.length \u003e= 2) {\n     \t\tbytes.push(parseInt(hexStr.substring(0, 2), 16));\n     \t\thexStr = hexStr.substring(2, hexStr.length);\n  \t}\n  \treturn new Uint8Array(bytes);\n}\n\ntry {\n\tconst msg = \"0000\"; //Some hex string of even length \n\tconst pin = \"5555\";\n\tlet result = await nfcWrapper.verifyPinAndSignForDefaultHdPath(msg, pin);\n\tconst signature = result.message;\n\tresult = await nfcWrapper.getPublicKeyForDefaultPath();\n  \tconst pubKey = result.message;\n\tconst msgBytes = hexStringToByteArray(msg);\n  \tconst signatureBytes = hexStringToByteArray(signature);\n  \tconst pubKeyBytes = hexStringToByteArray(pubKey);\n  \tconst sigVerificationRes = nacl.sign.detached.verify(msgBytes, signatureBytes, pubKeyBytes);\n  \tif (sigVerificationRes == false) {\n       \t\tthrow new Error(\"Signature is not correct.\");\n  \t}\n}\ncatch (e) {\n  console.log(e.message);\n}\n```\n\n_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_.\n\n## Card keychain\n\nThe detailed information about card keychain is available here [Android readme](https://github.com/tonlabs/TonNfcClientAndroid/blob/master/README.md), [iOS readme](https://github.com/tonlabs/TonNfcClientSwift/blob/master/README.md). 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.\n\n```javascript\ntry {\n  \tconst key = \"B81F0E0E07416DAB6C320ECC6BF3DBA48A70101C5251CC31B1D8F831B36E9F2A\";\n  \tlet result = await nfcWrapper.addKeyIntoKeyChain(key);\n\tconst keyHmac = result.message;\n\tresult = await nfcWrapper.getKeyFromKeyChain(keyHmac);\n\tconst keyFromCard = result.message;\n\tconst newKey = \"AA1F0E0E07416DAB6C320ECC6BF3DBA48A70101C5251CC31B1D8F831B36E9F25\";\n\tresult = await nfcWrapper.changeKey(newKey, keyHmac);\n\tconst newKeyHmac = result.message;\n\tresult = await nfcWrapper.getNumberOfKeys();\n\tlet numberOfKeys = result.message;\n  \tconsole.log(\"Number of keys = \" + numberOfKeys);\n  \tawait nfcWrapper.deleteKeyFromKeyChain(newKeyHmac);\t\n\tresult = await nfcWrapper.getNumberOfKeys();\n\tnumberOfKeys = result.message;\n\tconsole.log(\"Number of keys = \" + numberOfKeys);\n}\ncatch (e) {\n  console.log(e.message)\n}\n```\n\n\u003c!--_Note:_  await new Promise(r =\u003e setTimeout(r, 5000)) is necessary for running app on iPhone since multiple successive NFC sessions establishing may cause a trouble with system resourses. We need to make a pause between finishing one NFC session and starting a new NFC session. For Android it's not necessary.--\u003e\n\n## Full functions list \n\nThe full list of functions provided by the library to communicate with the card you will find [here](https://github.com/tonlabs/ton-nfc-client/blob/master/docs/FuntionsList.md)\n\n## Catching NFC related events from ton-nfc-client\n\nFor Android add into your app the following exemplary code:\n\n```javascript\nimport {NfcEventsEmitterWrapper} from 'ton-nfc-client';\nimport Toast from 'react-native-simple-toast';\n\nNfcEventsEmitterWrapper.addListener((msg) =\u003e {\n  Toast.showWithGravity(msg, Toast.LONG, Toast.TOP);\n  console.log(msg);\n});\n```\n\nFor iOS you additionaly should add this.\n\n```javascript\nif (Platform.OS === \"ios\") {\n  nfcCardModuleWrapper.setNfcNotificator();\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feverx-labs%2Fton-nfc-client","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feverx-labs%2Fton-nfc-client","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feverx-labs%2Fton-nfc-client/lists"}