[
  {
    "path": ".babelrc",
    "content": "{\n\t\"plugins\": [\n\t\t\"@babel/plugin-transform-modules-commonjs\",\n\t\t\"@babel/plugin-syntax-object-rest-spread\",\n\t\t\"@babel/plugin-transform-class-properties\"\n\t]\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "# EditorConfig\n# https://www.editorconfig.org/\nroot = true\n\n[*]\nindent_style = tab\nindent_size = 4\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n[{package.json,*.yaml,*.yml}]\nindent_style = space\nindent_size = 2\n\n[*.md]\ntrim_trailing_whitespace = false\n\n# JetBrains IDEs do NOT use empty final newline\n# for their metadata files (e.g., .xml/.iml files in .idea dir and .iml everywhere).\n# Also, using `**/.idea/**/*` stopped working in recent versions,\n# so now `.idea/**/*` is used instead.\n[{.idea/**/*,*.iml}]\nindent_style = space\nindent_size = 2\ninsert_final_newline = false\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "#\n# GitHub Actions Workflow\n#   reference: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions\n#\n#   useful example: https://github.com/mikeal/bundle-size-action/blob/master/.github/workflows/mikeals-workflow.yml\n#\n\nname: CI\n\non: [ push, pull_request ]\n\njobs:\n\n  build:\n\n    runs-on: ${{ matrix.os }}\n\n    strategy:\n      fail-fast: false\n      matrix:\n        # https://github.com/actions/runner-images#available-images\n        # macos-latest-large is x64\n        # macos-latest is arm64\n        os: [ ubuntu-latest, windows-latest ]\n        node-version: [ 8.x, 10.x, 12.x, 13.x, 14.x, 16.x, 18.x, 20.x ]\n        exclude:\n          # exclude Node.js 8.x on ubuntu-latest\n          # node-gyp fails during in gyp's Python code:\n          #   AttributeError: module 'collections' has no attribute 'MutableSet'\n          # TODO: try to fix\n          - os: ubuntu-latest\n            node-version: 8.x\n          # temporarily exclude Node.js 8.x-14.x on windows-latest\n          # node-gyp cannot find compatible Microsoft Visual Studio\n          # TODO: try to fix (install manually an older MVS)\n          - os: windows-latest\n            node-version: 8.x\n          - os: windows-latest\n            node-version: 10.x\n          - os: windows-latest\n            node-version: 12.x\n          - os: windows-latest\n            node-version: 13.x\n          - os: windows-latest\n            node-version: 14.x\n        include:\n          # macos-latest is arm64 which supports only the latest Node.js versions\n          - os: macos-latest\n            node-version: 18.x\n          - os: macos-latest\n            node-version: 20.x\n\n    steps:\n\n      # https://github.com/actions/checkout\n      - uses: actions/checkout@v4\n\n      - name: Use Node.js ${{ matrix.node-version }}\n        # https://github.com/actions/setup-node\n        uses: actions/setup-node@v4\n        with:\n          node-version: ${{ matrix.node-version }}\n\n      - name: Install pcsclite\n        run: sudo apt-get install -y libpcsclite1 libpcsclite-dev pcscd\n        if: matrix.os == 'ubuntu-latest'\n\n      - name: Install dependencies\n        run: npm install --verbose\n\n      - name: Build dist\n        run: npm run build\n\n      - name: Run basic test\n        run: node test/_node-version-test.js\n\n      - name: Run tests\n        run: npm test\n        # TODO: enable once tests do not get stuck on Windows\n        # AVA supports only officially supported Node.js versions\n        if: matrix.os != 'windows-latest' && contains(fromJSON('[\"18.x\", \"20.x\"]'), matrix.node-version)\n"
  },
  {
    "path": ".gitignore",
    "content": "### GENERAL\n\n# Windows\nThumbs.db\nDesktop.ini\n\n# macOS\n.DS_Store\n.supported\n\n# common code editors\n.atom/\n.vscode/\n\n# NetBeans\nnbproject/private/\n\n# JetBrains IDEs\n# see https://intellij-support.jetbrains.com/hc/en-us/articles/206544839\n**/.idea/workspace.xml\n**/.idea/tasks.xml\n**/.idea/usage.statistics.xml\n**/.idea/shelf/\n**/.idea/httpRequests/\n**/.idea/dataSources/\n**/.idea/dataSources.local.xml\ncaptures\n\n# common files\n*.log\n\n\n### PROJECT\n\n# node, npm\nnode_modules/\n\n# built source (pushed into npm)\n/dist/\n"
  },
  {
    "path": ".idea/codeStyles/Project.xml",
    "content": "<component name=\"ProjectCodeStyleConfiguration\">\n  <code_scheme name=\"Project\" version=\"173\">\n    <option name=\"OTHER_INDENT_OPTIONS\">\n      <value>\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </value>\n    </option>\n    <DartCodeStyleSettings>\n      <option name=\"DELEGATE_TO_DARTFMT\" value=\"false\" />\n    </DartCodeStyleSettings>\n    <HTMLCodeStyleSettings>\n      <option name=\"HTML_ATTRIBUTE_WRAP\" value=\"0\" />\n      <option name=\"HTML_TEXT_WRAP\" value=\"0\" />\n      <option name=\"HTML_SPACE_INSIDE_EMPTY_TAG\" value=\"true\" />\n      <option name=\"HTML_DO_NOT_INDENT_CHILDREN_OF\" value=\"\" />\n      <option name=\"HTML_ENFORCE_QUOTES\" value=\"true\" />\n    </HTMLCodeStyleSettings>\n    <JSCodeStyleSettings version=\"0\">\n      <option name=\"USE_DOUBLE_QUOTES\" value=\"false\" />\n      <option name=\"ENFORCE_TRAILING_COMMA\" value=\"WhenMultiline\" />\n      <option name=\"SPACES_WITHIN_OBJECT_LITERAL_BRACES\" value=\"true\" />\n      <option name=\"SPACES_WITHIN_IMPORTS\" value=\"true\" />\n    </JSCodeStyleSettings>\n    <TypeScriptCodeStyleSettings version=\"0\">\n      <option name=\"SPACES_WITHIN_IMPORTS\" value=\"true\" />\n    </TypeScriptCodeStyleSettings>\n    <codeStyleSettings language=\"CMake\">\n      <indentOptions>\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"CSS\">\n      <indentOptions>\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"CoffeeScript\">\n      <indentOptions>\n        <option name=\"INDENT_SIZE\" value=\"4\" />\n        <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\n        <option name=\"TAB_SIZE\" value=\"4\" />\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"Dart\">\n      <option name=\"RIGHT_MARGIN\" value=\"300\" />\n      <option name=\"CALL_PARAMETERS_WRAP\" value=\"0\" />\n      <option name=\"METHOD_PARAMETERS_WRAP\" value=\"0\" />\n      <option name=\"BINARY_OPERATION_WRAP\" value=\"0\" />\n      <option name=\"TERNARY_OPERATION_WRAP\" value=\"0\" />\n      <option name=\"TERNARY_OPERATION_SIGNS_ON_NEXT_LINE\" value=\"false\" />\n      <option name=\"ASSIGNMENT_WRAP\" value=\"0\" />\n      <indentOptions>\n        <option name=\"INDENT_SIZE\" value=\"4\" />\n        <option name=\"TAB_SIZE\" value=\"4\" />\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"Gherkin\">\n      <indentOptions>\n        <option name=\"INDENT_SIZE\" value=\"4\" />\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"Groovy\">\n      <indentOptions>\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"HTML\">\n      <option name=\"RIGHT_MARGIN\" value=\"120\" />\n      <indentOptions>\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"Haml\">\n      <indentOptions>\n        <option name=\"INDENT_SIZE\" value=\"4\" />\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"JAVA\">\n      <indentOptions>\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"JSON\">\n      <indentOptions>\n        <option name=\"INDENT_SIZE\" value=\"4\" />\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"Jade\">\n      <indentOptions>\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"JavaScript\">\n      <option name=\"ELSE_ON_NEW_LINE\" value=\"true\" />\n      <indentOptions>\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"LESS\">\n      <indentOptions>\n        <option name=\"INDENT_SIZE\" value=\"4\" />\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"Lua\">\n      <indentOptions>\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"ObjectiveC\">\n      <indentOptions>\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"Python\">\n      <indentOptions>\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"SASS\">\n      <indentOptions>\n        <option name=\"INDENT_SIZE\" value=\"4\" />\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"SCSS\">\n      <indentOptions>\n        <option name=\"INDENT_SIZE\" value=\"4\" />\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"Stylus\">\n      <indentOptions>\n        <option name=\"INDENT_SIZE\" value=\"4\" />\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"Swift\">\n      <indentOptions>\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"TypeScript\">\n      <indentOptions>\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"XML\">\n      <indentOptions>\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n    <codeStyleSettings language=\"kotlin\">\n      <indentOptions>\n        <option name=\"USE_TAB_CHARACTER\" value=\"true\" />\n      </indentOptions>\n    </codeStyleSettings>\n  </code_scheme>\n</component>"
  },
  {
    "path": ".idea/codeStyles/codeStyleConfig.xml",
    "content": "<component name=\"ProjectCodeStyleConfiguration\">\n  <state>\n    <option name=\"USE_PER_PROJECT_SETTINGS\" value=\"true\" />\n  </state>\n</component>"
  },
  {
    "path": ".idea/encodings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"Encoding\">\n    <file url=\"PROJECT\" charset=\"UTF-8\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/jsLibraryMappings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"JavaScriptLibraryMappings\">\n    <includedPredefinedLibrary name=\"Node.js Core\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/misc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"JavaScriptSettings\">\n    <option name=\"languageLevel\" value=\"ES6\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/modules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n      <module fileurl=\"file://$PROJECT_DIR$/.idea/nfc-pcsc.iml\" filepath=\"$PROJECT_DIR$/.idea/nfc-pcsc.iml\" />\n    </modules>\n  </component>\n</project>"
  },
  {
    "path": ".idea/nfc-pcsc.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module type=\"WEB_MODULE\" version=\"4\">\n  <component name=\"NewModuleRootManager\">\n    <content url=\"file://$MODULE_DIR$\">\n      <sourceFolder url=\"file://$MODULE_DIR$/test\" isTestSource=\"true\" />\n    </content>\n    <orderEntry type=\"inheritedJdk\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n  </component>\n</module>"
  },
  {
    "path": ".idea/vcs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/watcherTasks.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectTasksOptions\" suppressed-tasks=\"Babel\" />\n</project>"
  },
  {
    "path": ".npmignore",
    "content": "# Keeping files out of your package\n# docs: https://docs.npmjs.com/misc/developers#keeping-files-out-of-your-package\n# note: when both .npmignore and .gitignore are present, only .npmignore has effect\n# related: https://stackoverflow.com/questions/24942161/does-npm-ignore-files-listed-in-gitignore\n\n### GENERAL\n\n# Windows\nThumbs.db\nDesktop.ini\n\n# macOS\n.DS_Store\n.supported\n\n# common code editors\n.atom/\n.vscode/\n\n# NetBeans\nnbproject/private/\n\n# JetBrains IDEs\n# no need to ship metadata\n.idea/\n\n# common files\n*.log\n\n\n### PROJECT\n\n# node, npm\nnode_modules/\n"
  },
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2016-present Martin Endler\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# nfc-pcsc\n\n[![npm](https://img.shields.io/npm/v/nfc-pcsc.svg)](https://www.npmjs.com/package/nfc-pcsc)\n[![build status](https://img.shields.io/github/actions/workflow/status/pokusew/nfc-pcsc/ci.yml?logo=github)](https://github.com/pokusew/nfc-pcsc/actions/workflows/ci.yml)\n[![nfc-pcsc channel on discord](https://img.shields.io/badge/discord-join%20chat-61dafb.svg)](https://discord.gg/bg3yazg)\n\nEasy **reading and writing NFC tags and cards** in Node.js\n\nBuilt-in support for auto-reading **card UIDs** and reading tags emulated with [**Android HCE**](https://developer.android.com/guide/topics/connectivity/nfc/hce.html).\n\n> **NOTE:** Reading tag UID and methods for writing and reading tag content **depend on NFC reader commands support**.\nIt is tested to work with **ACR122 USB reader** but it should work with **all PC/SC compliant devices**.  \nWhen detecting tags does not work see [Alternative usage](#alternative-usage).\n\nThis library uses pcsclite native bindings [pokusew/node-pcsclite](https://github.com/pokusew/node-pcsclite) under the hood.\n\n**Psst!** Problems upgrading to 0.6.0? Check out [this migration note](#migration-from-older-versions-to-060).\n\n\n<!-- _**Psst!** You are browsing the documentation for the master branch, [look here](https://github.com/pokusew/nfc-pcsc/tree/v0.6.0) to see the usage of latest published version._ -->\n\n\n## Content\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n\n- [Installation](#installation)\n- [Flow of handling tags](#flow-of-handling-tags)\n- [Basic usage](#basic-usage)\n\t- [Running examples locally](#running-examples-locally)\n- [Alternative usage](#alternative-usage)\n- [Reading and writing data](#reading-and-writing-data)\n- [More examples](#more-examples)\n- [FAQ](#faq)\n  - [Migration from older versions to 0.6.0](#migration-from-older-versions-to-060)\n  - [Can I use this library in my Electron app?](#can-i-use-this-library-in-my-electron-app)\n  - [Can I use this library in my angular-electron app?](#can-i-use-this-library-in-my-angular-electron-app)\n  - [Do I have to use Babel in my app too?](#do-i-have-to-use-babel-in-my-app-too)\n  - [Which Node.js versions are supported?](#which-nodejs-versions-are-supported)\n  - [How do I require/import this library?](#how-do-i-requireimport-this-library)\n  - [Can I read a NDEF formatted tag?](#can-i-read-a-ndef-formatted-tag)\n  - [Can I use this library in my React Native app?](#can-i-use-this-library-in-my-react-native-app)\n- [Frequent errors](#frequent-errors)\n  - [TypeError: NFC is not a constructor](#typeerror-nfc-is-not-a-constructor)\n  - [Transaction failed error when using `CONNECT_MODE_DIRECT`](#transaction-failed-error-when-using-connect_mode_direct)\n  - [MIFARE Classic: Authentication Error after Multiple Writes](#mifare-classic-authentication-error-after-multiple-writes)\n  - [Reading data from a type 4 tags inside a Elsys.se sensors](#reading-data-from-a-type-4-tags-inside-a-elsysse-sensors)\n- [License](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n\n## Installation\n\n**Requirements:** **at least Node.js 8 or newer** (see [this FAQ](#which-nodejs-versions-are-supported) for more info)\n\n**Note:** This library can be used only in **Node.js** environments on Linux/UNIX, macOS and Windows. Read why [here](#can-i-use-this-library-in-my-react-native-app).\n\n1. **Node Native Modules build tools**\n\n    Because this library (via [pokusew/node-pcsclite](https://github.com/pokusew/node-pcsclite) under the hood) uses Node Native Modules (C++ Addons),\n    which are automatically built (using [node-gyp](https://github.com/nodejs/node-gyp))\n    when installing via npm or yarn, you need to have installed **C/C++ compiler\n    toolchain and some other tools** depending on your OS.\n    \n    **Please refer to the [node-gyp > Installation](https://github.com/nodejs/node-gyp#installation)**\n    for the list of required tools depending on your OS and steps how to install them.\n\n2. **PC/SC API in your OS**\n\n    On **macOS** and **Windows** you **don't have to install** anything,\n    **pcsclite API** is provided by the OS.\n    \n    On Linux/UNIX you'd probably need to install pcsclite library and daemon**.\n\n    > For example, in Debian/Ubuntu:\n    > ```bash\n    > apt-get install libpcsclite1 libpcsclite-dev\n    > ```\n    > To run any code you will also need to have installed the pcsc daemon:\n    > ```bash\n    > apt-get install pcscd\n    > ```\n\n3. **Once you have all needed libraries, you can install nfc-pcsc using npm:**\n\n    ```bash\n    npm install nfc-pcsc --save\n    ```\n    \n    or using Yarn:\n    \n    ```bash\n    yarn add nfc-pcsc\n    ```\n\n\n## Flow of handling tags\n\nWhen a NFC tag (card) is attached to the reader, the following is done:\n\n1. it tries to find out the standard of card (`TAG_ISO_14443_3` or `TAG_ISO_14443_4`)\n\n2. it will connect to the card, so any other card specific commands could be sent\n\n3. handling of card\n\t\n\t- when `autoProcessing` is true (default value) it will handle card by the standard:  \n\t\t\n\t\t`TAG_ISO_14443_3` *(MIFARE Ultralight, 1K ...)*: sends GET_DATA command to retrieve **card UID**  \n\t\t`TAG_ISO_14443_4` *(e.g.: Android HCE)*: sends SELECT_APDU command to retrieve data by file\n\t\t\n\t\t**then `card` event is fired, for which you can listen and then you can read or write data on the card**  \n\t\tsee [Basic usage](#basic-usage) how to do it\n\t\t\n\t- when `autoProcessing` is false (default value) it will only fire `card` event  \n\t  then you can send whatever commands you want using `reader.transmit` method  \n\t  see [Alternative usage](#alternative-usage) how to do it\n\t  \n4. you can read data, write data and send other commands\n\n\n## Basic usage\n\n> ### Running examples locally\n> If you want see it in action, clone this repository, install dependencies with npm and run `npm run example`.\n> Of course, instead of npm you can Yarn if you want.\n> See scripts section of [package.json](/package.json) for all available examples run commands.\n> ```bash\n> git clone https://github.com/pokusew/nfc-pcsc.git\n> cd nfc-pcsc\n> npm install\n> npm run example\n> ```\n\nYou can use this library in any Node.js 8+ environment (even in an Electron app). \n\n```javascript\n// in ES6\nimport { NFC } from 'nfc-pcsc';\n\n// without Babel in ES2015\nconst { NFC } = require('nfc-pcsc');\n\nconst nfc = new NFC(); // optionally you can pass logger\n\nnfc.on('reader', reader => {\n\n\tconsole.log(`${reader.reader.name}  device attached`);\n\n\t// enable when you want to auto-process ISO 14443-4 tags (standard=TAG_ISO_14443_4)\n\t// when an ISO 14443-4 is detected, SELECT FILE command with the AID is issued\n\t// the response is available as card.data in the card event\n\t// see examples/basic.js line 17 for more info\n\t// reader.aid = 'F222222222';\n\n\treader.on('card', card => {\n\n\t\t// card is object containing following data\n\t\t// [always] String type: TAG_ISO_14443_3 (standard nfc tags like MIFARE) or TAG_ISO_14443_4 (Android HCE and others)\n\t\t// [always] String standard: same as type\n\t\t// [only TAG_ISO_14443_3] String uid: tag uid\n\t\t// [only TAG_ISO_14443_4] Buffer data: raw data from select APDU response\n\n\t\tconsole.log(`${reader.reader.name}  card detected`, card);\n\n\t});\n\n\treader.on('card.off', card => {\n\t\tconsole.log(`${reader.reader.name}  card removed`, card);\n\t});\n\n\treader.on('error', err => {\n\t\tconsole.log(`${reader.reader.name}  an error occurred`, err);\n\t});\n\n\treader.on('end', () => {\n\t\tconsole.log(`${reader.reader.name}  device removed`);\n\t});\n\n});\n\nnfc.on('error', err => {\n\tconsole.log('an error occurred', err);\n});\n```\n\n\n## Alternative usage\n\nYou can **disable auto processing of tags** and process them yourself.\nIt may be useful when you are using other than ACR122 USB reader or non-standard tags.\n\n```javascript\n// in ES6\nimport { NFC } from 'nfc-pcsc';\n\n// without Babel in ES2015\nconst { NFC } = require('nfc-pcsc');\n\nconst nfc = new NFC(); // optionally you can pass logger\n\nnfc.on('reader', reader => {\n\n\t// disable auto processing\n\treader.autoProcessing = false;\n\n\tconsole.log(`${reader.reader.name}  device attached`);\n\n\treader.on('card', card => {\n\n\t\t// card is object containing following data\n\t\t// String standard: TAG_ISO_14443_3 (standard nfc tags like MIFARE Ultralight) or TAG_ISO_14443_4 (Android HCE and others)\n\t\t// String type: same as standard\n\t\t// Buffer atr\n\n\t\tconsole.log(`${reader.reader.name}  card inserted`, card);\n\n\t\t// you can use reader.transmit to send commands and retrieve data\n\t\t// see https://github.com/pokusew/nfc-pcsc/blob/master/src/Reader.js#L291\n\n\t});\n\t\n\treader.on('card.off', card => {\t\n\t\tconsole.log(`${reader.reader.name}  card removed`, card);\n\t});\n\n\treader.on('error', err => {\n\t\tconsole.log(`${reader.reader.name}  an error occurred`, err);\n\t});\n\n\treader.on('end', () => {\n\t\tconsole.log(`${reader.reader.name}  device removed`);\n\t});\n\n});\n\nnfc.on('error', err => {\n\tconsole.log('an error occurred', err);\n});\n```\n\n\n## Reading and writing data\n\nYou can read from and write to numerous NFC tags including MIFARE Ultralight (tested), MIFARE Classic, MIFARE DESFire, ...\n\n> Actually, you can even read/write any possible non-standard NFC tag and card, via sending APDU commands according card's technical documentation via `reader.transmit`.\n\nHere is **a simple example** showing reading and writing data to simple card **without authenticating** (e.g. MIFARE Ultralight):  \n_See [Basic usage](#basic-usage) how to set up reader or [look here for full code](/examples/from-readme-3.js)_\n\n```javascript\nreader.on('card', async card => {\n\n\tconsole.log();\n\tconsole.log(`card detected`, card);\n\n\t// example reading 12 bytes assuming containing text in utf8\n\ttry {\n\n\t\t// reader.read(blockNumber, length, blockSize = 4, packetSize = 16)\n\t\tconst data = await reader.read(4, 12); // starts reading in block 4, continues to 5 and 6 in order to read 12 bytes\n\t\tconsole.log(`data read`, data);\n\t\tconst payload = data.toString(); // utf8 is default encoding\n\t\tconsole.log(`data converted`, payload);\n\n\t} catch (err) {\n\t\tconsole.error(`error when reading data`, err);\n\t}\n\n\t// example write 12 bytes containing text in utf8\n\ttry {\n\n\t\tconst data = Buffer.allocUnsafe(12);\n\t\tdata.fill(0);\n\t\tconst text = (new Date()).toTimeString();\n\t\tdata.write(text); // if text is longer than 12 bytes, it will be cut off\n\t\t// reader.write(blockNumber, data, blockSize = 4)\n\t\tawait reader.write(4, data); // starts writing in block 4, continues to 5 and 6 in order to write 12 bytes\n\t\tconsole.log(`data written`);\n\n\t} catch (err) {\n\t\tconsole.error(`error when writing data`, err);\n\t}\n\n});\n```\n\n## More examples\n\n📦📦📦 You can find more examples in [examples folder](/examples), including:\n\n* [read-write.js](/examples/read-write.js) – detecting, reading and writing cards standard ISO/IEC 14443-3 cards (NTAG, MIFARE Ultralight, ...)\n* [mifare-classic.js](/examples/mifare-classic.js) – authenticating, reading and writing MIFARE Classic cards\n* [mifare-desfire.js](/examples/mifare-desfire.js) – authenticating and accessing data on MIFARE DESFire cards\n* [mifare-ultralight-c.js](/examples/mifare-ultralight-ntag.js) – an example implementation of MIFARE Ultralight C (3DES authentication)\n* [mifare-ultralight-ntag.js](/examples/mifare-ultralight-ntag.js) – an example implementation of Mifare Ultralight EV1 and NTAG specific commands\n* [basic.js](/examples/basic.js) – reader events explanation\n* [led.js](/examples/led.js) – controlling LED and buzzer of ACR122U reader\n* [uid-logger.js](/examples/uid-logger.js) – logs uid when a card is detected\n\nFeel free to open pull request, if you have any useful example, that you'd like to add. \n\n\n## FAQ\n\n### Migration from older versions to 0.6.0\n\nThere was a **breaking change in 0.6.0**, as the default export was removed _(because of non-standard behaviour of ES6 modules in ES5 env (see [#12](https://github.com/pokusew/nfc-pcsc/issues/12) and [v0.6.0 release changelog](https://github.com/pokusew/nfc-pcsc/releases/tag/v0.6.0)))_.\n\nYou have to **update all requires or imports** of this library to the following _(note the brackets around NFC)_:\n```javascript\n// in ES6 environment\nimport { NFC } from 'nfc-pcsc';\n\n// in ES2015 environment\nconst { NFC } = require('nfc-pcsc');\n```\n\n### Can I use this library in my [Electron](https://electron.atom.io/) app?\n\n**Yes, you can!** It works well.\n\n**But please note**, that this library uses [Node Native Modules](https://nodejs.org/api/addons.html) (underlying library [pokusew/node-pcsclite](https://github.com/pokusew/node-pcsclite) which provides access to PC/SC API).\n\nRead carefully **[Using Native Node Modules](https://electron.atom.io/docs/tutorial/using-native-node-modules/) guide in Electron documentation** to fully understand the problematic.\n\n**Note**, that because of Node Native Modules, you must build your app on target platform (you must run Windows build on Windows machine, etc.).  \nYou can use CI/CD server to build your app for certain platforms.  \nFor Windows, I recommend you to use [AppVeyor](https://appveyor.com/).  \nFor macOS and Linux build, there are plenty of services to choose from, for example [CircleCI](https://circleci.com/), [Travis CI](https://travis-ci.com/) [CodeShip](https://codeship.com/).\n\n### Can I use this library in my [angular-electron](https://github.com/maximegris/angular-electron) app?\n\n**Yes, you can!** But as this library uses Node Native Modules, you must change some config in `package.json` and `webpack.config.js` as described in [this comment](https://github.com/pokusew/nfc-pcsc/issues/24#issuecomment-327038188).\n\n### Do I have to use Babel in my app too?\n\n**No, you don't have to.** This library works great **in any Node.js 8+ environment** (even in an **Electron** app).\n\n> Psst! Instead of using **async/await** (like in examples), you can use Promises.\n> ```\n> reader\n>   .read(...)\n>   .then(data => ...)\n>   .catch(err => ...))\n> ```\n\nBabel is used under the hood to transpile features, that are not supported in **Node.js 8** (for example ES6 modules – import/export, see [.babelrc](/.babelrc) for list of used plugins). The transpiled code (in the dist folder) is then published into npm and when you install and require the library, the transpiled code is used, so you don't have to worry about anything.\n\n### Which Node.js versions are supported?\n\nnfc-pcsc officially supports the following Node.js versions: **8.x, 9.x, 10.x, 11.x, 12.x, 13.x, 14.x, 16.x, 18.x, 20.x**.\n\n### How do I require/import this library?\n\n```javascript\n// in ES6 environment\nimport { NFC } from 'nfc-pcsc';\n\n// in ES2015 environment\nconst { NFC } = require('nfc-pcsc');\n```\n\nIf you want to import uncompiled source and transpile it yourself (not recommended), you can do it as follows:\n\n```javascript\nimport { NFC } from 'nfc-pcsc/src';\n```\n\n### Can I read a NDEF formatted tag?\n\n**Yes, you can!** You can read raw byte card data with `reader.read` method, and then you can parse it with any NDEF parser, e.g. [TapTrack/NdefJS](https://github.com/TapTrack/NdefJS).\n\n**Psst!** There is also an example ([ndef.js](/examples/ndef.js)), but it is not finished yet. Feel free to contribute.\n\n### Can I use this library in my React Native app?\n\nShort answer: **NO**\n\nExplanation: **Mobile support is virtually impossible** because nfc-pcsc uses **Node Native Modules**\nto access system **PC/SC API** _(actually under the hood, the pcsclite native binding\nis implemented in [@pokusew/pcsclite](https://github.com/pokusew/node-pcsclite))_.\nSo the **Node.js runtime and PC/SC API** are required for nfc-pcsc to run.\nThat makes it possible to use it on the most of OS (Windows, macOS, Linux)\n**directly in Node.js** or in **Electron.js and NW.js** desktop apps.\n\n\n## Frequent errors\n\n### TypeError: NFC is not a constructor\n\nNo worry, just check that you import/require the library like this _(note the brackets around NFC)_:\n```javascript\n// in ES6 environment\nimport { NFC } from 'nfc-pcsc';\n\n// in ES2015 environment\nconst { NFC } = require('nfc-pcsc');\n```\n\nTake a look at [How do I require/import this library?](#how-do-i-requireimport-this-library) section for more info.\n\n> **Note**, that `const NFC = require('nfc-pcsc');` or `import NFC from 'nfc-pcsc'` (NFC without brackets) won't work, because there is no default export.  \nIt was removed for non-standard behaviour of ES6 modules in ES5 env (see [#12](https://github.com/pokusew/nfc-pcsc/issues/12) and [v0.6.0 release changelog](https://github.com/pokusew/nfc-pcsc/releases/tag/v0.6.0))\n\n### Transaction failed error when using `CONNECT_MODE_DIRECT`\n\nNo worry, just needs a proper configuration, see [explanation and instructions here](https://github.com/pokusew/nfc-pcsc/issues/13#issuecomment-302482621).\n\n### MIFARE Classic: Authentication Error after Multiple Writes\n\nNo worry, you have probably modified a sector trailer instead of a data block, see [explanation and instructions here](https://github.com/pokusew/nfc-pcsc/issues/16#issuecomment-304989178).\n\n### Reading data from a type 4 tags inside a [Elsys.se](https://www.elsys.se/en/) sensors\n\nAccording to [@martijnthe](https://github.com/martijnthe)'s findings, it seems to be necessary to change the CLASS of READ BINARY APDU command\nfrom the default value of `0xFF` to `0x00` in order to make a successful read.\n\nIf you experience the same problems, you can try setting the fourth argument (readClass) of the\n[`reader.read(blockNumber, length, blockSize, packetSize, readClass)`](https://github.com/pokusew/nfc-pcsc/blob/master/src/Reader.js#L493) method to value `0x00`.\n\nRelevant conversation: https://github.com/pokusew/nfc-pcsc/pull/55#issuecomment-450120232\n\n\n## License\n\n[MIT](/LICENSE.md)\n"
  },
  {
    "path": "ava.config.js",
    "content": "\"use strict\";\n\n// run AVA\n//   npx ava --match='*foo'\n//   ./node_modules/.bin/ava --match='*foo'\n// see https://github.com/avajs/ava/blob/main/docs/05-command-line.md\n\n// https://github.com/avajs/ava/blob/main/docs/06-configuration.md\nmodule.exports = {\n\t// https://github.com/avajs/ava/blob/main/docs/recipes/typescript.md\n\textensions: {\n\t\t// https://github.com/avajs/ava/blob/main/docs/06-configuration.md#configuring-module-formats\n\t\tjs: true,\n\t\tts: 'commonjs',\n\t},\n\t// extensions: ['.js', '.ts'],\n\trequire: [\n\t\t'@babel/register',\n\t],\n\tfiles: [\n\t\t'./test/**/*',\n\t],\n\twatchMode: {\n\t\tignoreChanges: [\n\t\t\t//\n\t\t\t// some files and directories are ignored by default,\n\t\t\t// see https://github.com/avajs/ava/blob/main/docs/recipes/watch-mode.md#ignoring-changes\n\t\t\t//\n\t\t\t// note:\n\t\t\t//   AVA dependency tracking (https://github.com/avajs/ava/blob/main/docs/recipes/watch-mode.md#dependency-tracking)\n\t\t\t//   currently does not seem to work. However, since AVA v6 it should work.\n\t\t\t//   See\n\t\t\t//     https://github.com/avajs/ava/issues/2388\n\t\t\t//     https://github.com/avajs/ava/pull/3123\n\t\t\t//     https://github.com/avajs/ava/issues/2905\n\t\t\t//\n\t\t\t// use the following to debug:\n\t\t\t//   DEBUG=ava:watcher npx ava --watch\n\t\t\t//   or\n\t\t\t//   DEBUG=ava:* npx ava --watch\n\t\t\t//\n\t\t\t'./.idea/',\n\t\t\t'./temp/',\n\t\t\t'./dist/',\n\t\t],\n\t},\n};\n"
  },
  {
    "path": "examples/BitSet.js",
    "content": "\"use strict\";\n\nimport { column } from './utils';\n\n\n// TODO: cover with tests\nexport class BitSet {\n\n\t/**\n\t * Creates a new BitSet (bit view for the Buffer instance)\n\t * If an existing Buffer instance is given, then it will be used and no additional memory will be allocated.\n\t * If an integer is given, then a new Buffer instance will be created allocating specified memory (bitsLength / 8)\n\t * @param bitsLength bit length or existing Buffer instance\n\t */\n\tconstructor(bitsLength) {\n\t\tthis.b = (bitsLength instanceof Buffer) ? bitsLength : Buffer.allocUnsafe(bitsLength / 8).fill(0);\n\t}\n\n\tclone() {\n\t\t// copies data into new buffer, allocates new memory\n\t\treturn Buffer.from(this.b);\n\t}\n\n\tget buffer() {\n\t\treturn this.b;\n\t}\n\n\tstatic getBufferPos(pos) {\n\t\treturn Math.trunc(pos / 8);\n\t}\n\n\tstatic getMask(pos) {\n\t\treturn 1 << (pos % 8);\n\t}\n\n\tset(pos) {\n\t\tthis.b[BitSet.getBufferPos(pos)] |= BitSet.getMask(pos);\n\t}\n\n\ttest(pos) {\n\t\treturn (this.b[BitSet.getBufferPos(pos)] & BitSet.getMask(pos)) !== 0;\n\t}\n\n\tclear(pos) {\n\t\tthis.b[BitSet.getBufferPos(pos)] &= ~BitSet.getMask(pos);\n\t}\n\n\ttoggle(pos) {\n\t\tthis.b[BitSet.getBufferPos(pos)] ^= BitSet.getMask(pos);\n\t}\n\n\ttoArray(useBooleans = true) {\n\t\tconst s = this.b.length * 8;\n\t\tconst a = [];\n\t\tfor (let pos = 0; pos < s; pos++) {\n\t\t\tif (useBooleans) {\n\t\t\t\ta.push(this.test(pos));\n\t\t\t}\n\t\t\telse {\n\t\t\t\ta.push(this.test(pos) ? 1 : 0);\n\t\t\t}\n\t\t}\n\t\treturn a;\n\t}\n\n\tprint(name, appendBlankLine = true) {\n\n\t\tconst s = this.b.length * 8;\n\n\t\tlet l1 = ' |       data:';\n\t\tlet l2 = ' |            ';\n\t\tlet l3 = ' | bit number:';\n\n\t\tfor (let pos = s - 1; pos >= 0; pos--) {\n\t\t\t// console.log(pos);\n\t\t\tconst numberString = column(pos, null, 3, 1);\n\t\t\tl1 += column(this.test(pos) ? 1 : 0, numberString.length - 1, 3, 1);\n\t\t\tl2 += column('↑', numberString.length - 1, 3, 1);\n\t\t\tl3 += numberString;\n\t\t}\n\n\t\tconsole.log(`BitSet:${name ? ' ' + name : ''}`, this.b);\n\t\tconsole.log(l1);\n\t\tconsole.log(l2);\n\t\tconsole.log(l3);\n\t\tif (appendBlankLine) {\n\t\t\tconsole.log();\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "examples/basic.js",
    "content": "\"use strict\";\n\n// #############\n// Example: Basic usage\n// - see \"Basic usage\" section in README for an explanation\n// #############\n\nimport { NFC } from '../src/index';\n\n\nconst nfc = new NFC(); // optionally you can pass logger\n\nnfc.on('reader', reader => {\n\n\tconsole.log(`${reader.reader.name}  device attached`);\n\n\t// enable when you want to auto-process ISO 14443-4 tags (standard=TAG_ISO_14443_4)\n\t// when an ISO 14443-4 is detected, SELECT FILE command with the AID is issued\n\t// the response is available as card.data in the card event\n\t// you can set reader.aid to:\n\t// 1. a HEX string (which will be parsed automatically to Buffer)\n\treader.aid = 'F222222222';\n\t// 2. an instance of Buffer containing the AID bytes\n\t// reader.aid = Buffer.from('F222222222', 'hex');\n\t// 3. a function which must return an instance of a Buffer when invoked with card object (containing standard and atr)\n\t//    the function may generate AIDs dynamically based on the detected card\n\t// reader.aid = ({ standard, atr }) => {\n\t//\n\t// \treturn Buffer.from('F222222222', 'hex');\n\t//\n\t// };\n\n\treader.on('card', card => {\n\n\t\t// card is object containing following data\n\t\t// [always] String type: TAG_ISO_14443_3 (standard nfc tags like MIFARE) or TAG_ISO_14443_4 (Android HCE and others)\n\t\t// [always] String standard: same as type\n\t\t// [only TAG_ISO_14443_3] String uid: tag uid\n\t\t// [only TAG_ISO_14443_4] Buffer data: raw data from select APDU response\n\n\t\tconsole.log(`${reader.reader.name}  card detected`, card);\n\n\t});\n\n\treader.on('card.off', card => {\n\t\tconsole.log(`${reader.reader.name}  card removed`, card);\n\t});\n\n\treader.on('error', err => {\n\t\tconsole.log(`${reader.reader.name}  an error occurred`, err);\n\t});\n\n\treader.on('end', () => {\n\t\tconsole.log(`${reader.reader.name}  device removed`);\n\t});\n\n});\n\nnfc.on('error', err => {\n\tconsole.log('an error occurred', err);\n});\n"
  },
  {
    "path": "examples/from-readme-3.js",
    "content": "\"use strict\";\n\n// #############\n// Example from \"Reading and writing data\" section of project's README\n// #############\n\nimport { NFC } from '../src/index';\n\n\nconst nfc = new NFC();\n\nnfc.on('reader', reader => {\n\n\tconsole.log(`${reader.reader.name}  device attached`);\n\n\treader.on('card', async card => {\n\n\t\tconsole.log();\n\t\tconsole.log(`card detected`, card);\n\n\t\t// example reading 12 bytes assuming containing text in utf8\n\t\ttry {\n\n\t\t\t// reader.read(blockNumber, length, blockSize = 4, packetSize = 16)\n\t\t\tconst data = await reader.read(4, 12); // starts reading in block 4, continues to 5 and 6 in order to read 12 bytes\n\t\t\tconsole.log(`data read`, data);\n\t\t\tconst payload = data.toString(); // utf8 is default encoding\n\t\t\tconsole.log(`data converted`, payload);\n\n\t\t} catch (err) {\n\t\t\tconsole.error(`error when reading data`, err);\n\t\t}\n\n\t\t// example write 12 bytes containing text in utf8\n\t\ttry {\n\n\t\t\tconst data = Buffer.allocUnsafe(12);\n\t\t\tdata.fill(0);\n\t\t\tconst text = (new Date()).toTimeString();\n\t\t\tdata.write(text); // if text is longer than 12 bytes, it will be cut off\n\t\t\t// reader.write(blockNumber, data, blockSize = 4)\n\t\t\tawait reader.write(4, data); // starts writing in block 4, continues to 5 and 6 in order to write 12 bytes\n\t\t\tconsole.log(`data written`);\n\n\t\t} catch (err) {\n\t\t\tconsole.error(`error when writing data`, err);\n\t\t}\n\n\t});\n\n\treader.on('error', err => {\n\t\tconsole.log(`${reader.reader.name}  an error occurred`, err);\n\t});\n\n\treader.on('end', () => {\n\t\tconsole.log(`${reader.reader.name}  device removed`);\n\t});\n\n});\n\nnfc.on('error', err => {\n\tconsole.log('an error occurred', err);\n});\n"
  },
  {
    "path": "examples/led.js",
    "content": "\"use strict\";\n\n// #############\n// Example: Controlling LED and buzzer on ACR122U\n// - what is covered:\n//   - custom led blinks\n//   - custom buzzer output\n//   - repeated beeping on unsuccessful read/write operation\n// - TODO:\n//   - document how to allow escape commands (direct communication without card)\n//   - meanwhile please see https://github.com/pokusew/nfc-pcsc/issues/13\n// #############\n\nimport { NFC, CONNECT_MODE_DIRECT } from '../src/index';\nimport pretty from './pretty-logger';\n\n\nconst nfc = new NFC(pretty); // const nfc = new NFC(pretty); // optionally you can pass logger to see internal debug logs\n\nnfc.on('reader', async reader => {\n\n\tpretty.info(`device attached`, reader);\n\n\ttry {\n\t\tawait reader.connect(CONNECT_MODE_DIRECT);\n\t\tawait reader.setBuzzerOutput(false);\n\t\tawait reader.disconnect();\n\t} catch (err) {\n\t\tpretty.info(`initial sequence error`, reader, err);\n\t}\n\n\treader.on('card', async card => {\n\n\t\tpretty.info(`card detected`, reader, card);\n\n\t\ttry {\n\n\t\t\t// red error\n\t\t\tawait reader.led(0b01011101, [0x02, 0x01, 0x05, 0x01]);\n\n\t\t\t// green success\n\t\t\tawait reader.led(0b00101110, [0x01, 0x00, 0x01, 0x01]);\n\n\t\t} catch (err) {\n\t\t\tpretty.error(`error when writing led`, reader, err);\n\t\t}\n\n\t});\n\n\treader.on('error', err => {\n\t\tpretty.error(`an error occurred`, reader, err);\n\t});\n\n\treader.on('end', () => {\n\t\tpretty.info(`device removed`, reader);\n\t});\n\n});\n\nnfc.on('error', err => {\n\tpretty.error(`an error occurred`, err);\n});\n"
  },
  {
    "path": "examples/mifare-classic.js",
    "content": "\"use strict\";\n\n// #############\n// Example: MIFARE Classic\n// - should work well with any compatible PC/SC card reader\n// - what is covered:\n//   - authentication\n//   - reading data from card\n//   - writing data to card\n// - what is NOT covered yet:\n//   - using sector trailers to update access rights\n// #############\n\n// ## Note about the card's data structure\n//\n// ### MIFARE Classic EV1 1K\n// - 1024 × 8 bit EEPROM memory\n// - 16 sectors of 4 blocks\n// - see https://www.nxp.com/docs/en/data-sheet/MF1S50YYX_V1.pdf\n//\n// ### MIFARE Classic EV1 4K\n// - 4096 × 8 bit EEPROM memory\n// - 32 sectors of 4 blocks and 8 sectors of 16 blocks\n// - see https://www.nxp.com/docs/en/data-sheet/MF1S70YYX_V1.pdf\n//\n// One block contains 16 bytes.\n// Don't forget specify the blockSize argument blockSize=16 in reader.read and reader.write calls.\n// The smallest amount of data to write is one block. You can write only the entire blocks (card limitation).\n//\n// sector 0\n// \tblock 0 - manufacturer data (read only)\n// \tblock 1 - data block\n// \tblock 2 - data block\n// \tblock 3 - sector trailer 0\n// \t\tbytes 00-05: Key A (default 0xFFFFFFFFFFFF) (6 bytes)\n// \t\tbytes 06-09: Access Bits (default 0xFF0780) (4 bytes)\n// \t\tbytes 10-15: Key B (optional) (default 0xFFFFFFFFFFFF) (6 bytes)\n// sector 1:\n// \tblock 4 - data block\n// \tblock 5 - data block\n// \tblock 6 - data block\n// \tblock 7 - sector trailer 1\n// sector 2:\n// \tblock 8 - data block\n// \tblock 9 - data block\n// \tblock 10 - data block\n// \tblock 11 - sector trailer 2\n// ... and so on ...\n\nimport { NFC, TAG_ISO_14443_3, TAG_ISO_14443_4, KEY_TYPE_A, KEY_TYPE_B } from '../src/index';\nimport pretty from './pretty-logger';\n\n\nconst nfc = new NFC(); // const nfc = new NFC(pretty); // optionally you can pass logger to see internal debug logs\n\nnfc.on('reader', async reader => {\n\n\tpretty.info(`device attached`, reader);\n\n\treader.on('card', async card => {\n\n\t\t// MIFARE Classic is ISO/IEC 14443-3 tag\n\t\t// skip other standards\n\t\tif (card.type !== TAG_ISO_14443_3) {\n\t\t\treturn;\n\t\t}\n\n\t\tpretty.info(`card detected`, reader, card);\n\n\t\t// Reading and writing data from/to MIFARE Classic cards (e.g. MIFARE 1K) ALWAYS requires authentication!\n\n\t\t// How does the MIFARE Classic authentication work?\n\t\t// 1. You authenticate to a specific sector using a specific key (key + keyType).\n\t\t// 2. After the successful authentication, you are granted permissions according to the access conditions\n\t\t//    for the given key (access conditions are specified in the trailer section of each sector).\n\t\t//    Depending on the access conditions, you can read from / write to the blocks of this sector.\n\t\t// 3. If you want to access data in another sectors, you have to authenticate to that sector.\n\t\t//    Then you can access the data from the block within that sector (only from that sector).\n\t\t// summary: MIFARE Classic will only grant permissions based on the last authentication attempt.\n\t\t//          Consequently, if multiple reader.authenticate(...) commands are used,\n\t\t//          only the last one has an effect on all subsequent read/write operations.\n\n\t\t// reader.authenticate(blockNumber, keyType, key, obsolete = false)\n\t\t// - blockNumber - the number of any block withing the sector we want to authenticate\n\t\t// - keyType - type of key - either KEY_TYPE_A or KEY_TYPE_B\n\t\t// - key - 6 bytes - a Buffer instance, an array of bytes, or 12-chars HEX string\n\t\t// - obsolete - (default - false for PC/SC V2.07) use true for PC/SC V2.01\n\n\t\t// Don't forget to fill YOUR keys and types! (default ones are stated below)\n\t\tconst key = 'FFFFFFFFFFFF'; // key must be a 12-chars HEX string, an instance of Buffer, or array of bytes\n\t\tconst keyType = KEY_TYPE_A;\n\n\t\ttry {\n\n\t\t\t// we want to authenticate sector 1\n\t\t\t// authenticating one block within the sector will authenticate all blocks within that sector\n\t\t\t// so in our case, we choose block 4 that is within the sector 1, all blocks (4, 5, 6, 7)\n\t\t\t// will be authenticated with the given key\n\t\t\tawait reader.authenticate(4, keyType, key);\n\n\t\t\t// Note: writing might require to authenticate with a different key (based on the sector access conditions)\n\n\t\t\tpretty.info(`sector 1 successfully authenticated`, reader);\n\n\t\t} catch (err) {\n\t\t\tpretty.error(`error when authenticating block 4 within the sector 1`, reader, err);\n\t\t\treturn;\n\t\t}\n\n\n\t\t// example reading 16 bytes (one block) assuming containing 32bit integer\n\t\t// !!! note that we don't need 16 bytes - 32bit integer takes only 4 bytes !!!\n\t\ttry {\n\n\t\t\t// reader.read(blockNumber, length, blockSize = 4, packetSize = 16)\n\t\t\t// - blockNumber - memory block number where to start reading\n\t\t\t// - length - how many bytes to read\n\t\t\t// - blockSize - 4 for MIFARE Ultralight, 16 for MIFARE Classic\n\t\t\t// ! Caution! length must be divisible by blockSize\n\t\t\t// ! Caution! MIFARE Classic cards have sector trailers\n\t\t\t//   containing access bits instead of data, each last block in sector is sector trailer\n\t\t\t//   (e.g. block 3, 7, 11, 14)\n\t\t\t//   see memory structure above or https://github.com/pokusew/nfc-pcsc/issues/16#issuecomment-304989178\n\n\t\t\tconst data = await reader.read(4, 16, 16); // blockSize=16 must specified for MIFARE Classic cards\n\n\t\t\tpretty.info(`data read`, reader, data);\n\n\t\t\tconst payload = data.readInt32BE(0);\n\n\t\t\tpretty.info(`data converted`, reader, payload);\n\n\t\t} catch (err) {\n\t\t\tpretty.error(`error when reading data`, reader, err);\n\t\t}\n\n\n\t\t// example write 16 bytes containing 32bit integer\n\t\t// !!! note that we don't need 16 bytes - 32bit integer takes just 4 bytes !!!\n\t\ttry {\n\n\t\t\t// reader.write(blockNumber, data, blockSize = 4, packetSize = 16)\n\t\t\t// - blockNumber - memory block number where to start writing\n\t\t\t// - data - what to write\n\t\t\t// - blockSize - 4 for MIFARE Ultralight, 16 for MIFARE Classic\n\t\t\t// ! Caution! data.length must be divisible by blockSize\n\t\t\t// ! Caution! MIFARE Classic cards have sector trailers\n\t\t\t//   containing access bits instead of data, each last block in sector is sector trailer\n\t\t\t//   (e.g. block 3, 7, 11, 14)\n\t\t\t//   ee memory structure above or https://github.com/pokusew/nfc-pcsc/issues/16#issuecomment-304989178\n\n\t\t\tconst data = Buffer.allocUnsafe(16);\n\t\t\tdata.fill(0);\n\t\t\tconst randomNumber = Math.round(Math.random() * 1000);\n\t\t\tdata.writeInt32BE(randomNumber, 0);\n\n\t\t\tawait reader.write(4, data, 16); // blockSize=16 must specified for MIFARE Classic cards\n\n\t\t\tpretty.info(`data written`, reader, randomNumber, data);\n\n\t\t} catch (err) {\n\t\t\tpretty.error(`error when writing data`, reader, err);\n\t\t}\n\n\n\t});\n\n\treader.on('error', err => {\n\t\tpretty.error(`an error occurred`, reader, err);\n\t});\n\n\treader.on('end', () => {\n\t\tpretty.info(`device removed`, reader);\n\t});\n\n\n});\n\nnfc.on('error', err => {\n\tpretty.error(`an error occurred`, err);\n});\n"
  },
  {
    "path": "examples/mifare-desfire.js",
    "content": "\"use strict\";\n\n// #############\n// Example: MIFARE DESFire\n// - what is covered:\n//   - 3DES authentication\n//   - reading data files\n// - known issue:\n//   - [mac0S Sierra and greater] when an error occurs during the authentication process,\n//     the NFC must be reinitialized or the reader reconnected\n//     in order to allow subsequent successful operations (TODO: add appropriate links, fix and test)\n// #############\n\nimport { NFC } from '../src/index';\nimport pretty from './pretty-logger';\nimport crypto from 'crypto';\n\n\n// config\nconst desfire = {\n\tkey: '00000000000000000000000000000000',\n\tappId: [0x00, 0x00, 0x00],\n\tkeyId: [0x00],\n\tread: { // supply location of an existing data\n\t\tfileId: [0x02],\n\t\toffset: [0x00, 0x00, 0x00],\n\t\tlength: [14, 0x00, 0x00],\n\t},\n};\n\n\nfunction decrypt(key, data, iv = Buffer.alloc(8).fill(0)) {\n\n\tconst decipher = crypto.createDecipheriv('DES-EDE-CBC', key, iv);\n\tdecipher.setAutoPadding(false);\n\n\treturn Buffer.concat([decipher.update(data), decipher.final()]);\n\n}\n\nfunction encrypt(key, data, iv = Buffer.alloc(8).fill(0)) {\n\n\tconst decipher = crypto.createCipheriv('DES-EDE-CBC', key, iv);\n\tdecipher.setAutoPadding(false);\n\n\treturn Buffer.concat([decipher.update(data), decipher.final()]);\n\n}\n\n\nconst nfc = new NFC();\n\nnfc.on('reader', async reader => {\n\n\tpretty.info(`device attached`, reader);\n\n\t// we have to handle MIFARE DESFire\n\treader.autoProcessing = false;\n\n\t// just handy shortcut to send data\n\tconst send = async (cmd, comment = null, responseMaxLength = 40) => {\n\n\t\tconst b = Buffer.from(cmd);\n\n\t\tconsole.log((comment ? `[${comment}] ` : '') + `sending`, b);\n\n\t\tconst data = await reader.transmit(b, responseMaxLength);\n\n\t\tconsole.log((comment ? `[${comment}] ` : '') + `received data`, data);\n\n\t\treturn data;\n\n\t};\n\n\tconst wrap = (cmd, dataIn) => ([0x90, cmd, 0x00, 0x00, dataIn.length, ...dataIn, 0x00]);\n\n\treader.on('card', async card => {\n\n\t\tpretty.info(`card detected`, reader, card);\n\n\t\tconst selectApplication = async () => {\n\n\t\t\t// 1: [0x5A] SelectApplication(appId) [4 bytes] - Selects one specific application for further access\n\t\t\t// DataIn: appId (3 bytes)\n\t\t\tconst res = await send(wrap(0x5a, desfire.appId), 'step 1 - select app');\n\n\t\t\t// something went wrong\n\t\t\tif (res.slice(-1)[0] !== 0x00) {\n\t\t\t\tthrow new Error('error in step 1');\n\t\t\t}\n\n\n\t\t};\n\n\t\tconst authenticate = async (key) => {\n\n\t\t\t// 2: [0x0a] Authenticate(keyId) [2bytes]\n\t\t\t// DataIn: keyId (1 byte)\n\t\t\tconst res1 = await send(wrap(0x0a, desfire.keyId), 'step 2 - authenticate');\n\n\t\t\t// something went wrong\n\t\t\tif (res1.slice(-1)[0] !== 0xaf) {\n\t\t\t\tthrow new Error('error in step 2 - authenticate');\n\t\t\t}\n\n\t\t\t// encrypted RndB from reader\n\t\t\t// cut out status code (last 2 bytes)\n\t\t\tconst ecRndB = res1.slice(0, -2);\n\n\t\t\t// decrypt it\n\t\t\tconst RndB = decrypt(key, ecRndB);\n\n\t\t\t// rotate RndB\n\t\t\tconst RndBp = Buffer.concat([RndB.slice(1, 8), RndB.slice(0, 1)]);\n\n\t\t\t// generate a 8 byte Random Number A\n\t\t\tconst RndA = crypto.randomBytes(8);\n\n\t\t\t// concat RndA and RndBp\n\t\t\tconst msg = encrypt(key, Buffer.concat([RndA, RndBp]));\n\n\t\t\t// send it back to the reader\n\t\t\tconst res2 = await send(wrap(0xaf, msg), 'step 2 - set up RndA');\n\n\t\t\t// something went wrong\n\t\t\tif (res2.slice(-1)[0] !== 0x00) {\n\t\t\t\tthrow new Error('error in step 2 - set up RndA');\n\t\t\t}\n\n\t\t\t// encrypted RndAp from reader\n\t\t\t// cut out status code (last 2 bytes)\n\t\t\tconst ecRndAp = res2.slice(0, -2);\n\n\t\t\t// decrypt to get rotated value of RndA2\n\t\t\tconst RndAp = decrypt(key, ecRndAp);\n\n\t\t\t// rotate\n\t\t\tconst RndA2 = Buffer.concat([RndAp.slice(7, 8), RndAp.slice(0, 7)]);\n\n\t\t\t// compare decrypted RndA2 response from reader with our RndA\n\t\t\t// if it equals authentication process was successful\n\t\t\tif (!RndA.equals(RndA2)) {\n\t\t\t\tthrow new Error('error in step 2 - match RndA random bytes');\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tRndA,\n\t\t\t\tRndB,\n\t\t\t};\n\n\t\t};\n\n\t\tconst readData = async () => {\n\n\t\t\t// 3: [0xBD] ReadData(FileNo,Offset,Length) [8bytes] - Reads data from Standard Data Files or Backup Data Files\n\t\t\tconst res = await send(wrap(0xbd, [desfire.read.fileId, ...desfire.read.offset, ...desfire.read.length]), 'step 3 - read', 255);\n\n\t\t\t// something went wrong\n\t\t\tif (res.slice(-1)[0] !== 0x00) {\n\t\t\t\tthrow new Error('error in step 3 - read');\n\t\t\t}\n\n\t\t\tconsole.log('data', res);\n\n\t\t};\n\n\n\t\ttry {\n\n\t\t\t// step 1\n\t\t\tawait selectApplication();\n\n\t\t\t// step 2\n\t\t\tconst key = Buffer.from(desfire.key, 'hex');\n\t\t\tawait authenticate(key);\n\n\t\t\t// step 3\n\t\t\tawait readData();\n\n\n\t\t} catch (err) {\n\t\t\tpretty.error(`error occurred during processing steps`, reader, err);\n\t\t}\n\n\n\t});\n\n\treader.on('error', err => {\n\t\tpretty.error(`an error occurred`, reader, err);\n\t});\n\n\treader.on('end', () => {\n\t\tpretty.info(`device removed`, reader);\n\t});\n\n\n});\n\nnfc.on('error', err => {\n\tpretty.error(`an error occurred`, err);\n});\n"
  },
  {
    "path": "examples/mifare-ultralight-c.js",
    "content": "'use strict';\n\n// #############\n// Example: MIFARE Ultralight C (MF0ICU2) - 3DES authentication\n// - Note: This example ONLY works with the ACR122U USB NFC reader or possibly any reader\n//         that uses the NXP PN533 or similar NFC frontends.\n// - Docs (descriptions of the commands and data structure):\n//   - MIFARE Ultralight C - see https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf\n//   - ACR122U - see https://www.acs.com.hk/download-manual/419/API-ACR122U-2.04.pdf\n//   - NXP PN533 (embedded in the ACR122U) - https://www.nxp.com/docs/en/user-guide/157830_PN533_um080103.pdf\n// #############\n\nimport { NFC, TAG_ISO_14443_3, TAG_ISO_14443_4, KEY_TYPE_A, KEY_TYPE_B, TransmitError } from '../src/index';\nimport pretty from './pretty-logger';\nimport crypto from 'crypto';\nimport assert from 'assert/strict';\n\nexport class MifareUltralight3DESAuthenticationError extends TransmitError {\n\n\tconstructor(code, message, previousError) {\n\n\t\tsuper(code, message, previousError);\n\n\t\tthis.name = 'MifareUltralight3DESAuthenticationError';\n\n\t}\n\n}\n\nexport class MifareUltralightReadError extends TransmitError {\n\n\tconstructor(code, message, previousError) {\n\n\t\tsuper(code, message, previousError);\n\n\t\tthis.name = 'MifareUltralightReadError';\n\n\t}\n\n}\n\nexport class MifareUltralightWriteError extends TransmitError {\n\n\tconstructor(code, message, previousError) {\n\n\t\tsuper(code, message, previousError);\n\n\t\tthis.name = 'MifareUltralightWriteError';\n\n\t}\n\n}\n\n/**\n * Validates that the given data is a Buffer or a HEX string of the specified byte length\n * @param name {string} data name for debugging\n * @param data {Buffer|string} a Buffer or a HEX string\n * @param length {number} number of bytes\n * @returns {Buffer} the data converted to a Buffer\n */\nconst parseBytes = (name, data, length) => {\n\n\tif (!(data instanceof Buffer) && typeof data !== 'string') {\n\t\tthrow new Error(`${name} must an instance of Buffer or a HEX string.`);\n\t}\n\n\tif (Buffer.isBuffer(data)) {\n\n\t\tif (data.length !== length) {\n\t\t\tthrow new Error(`${name} must be ${length} bytes long.`);\n\t\t}\n\n\t\treturn data;\n\n\t}\n\n\tif (typeof data === 'string') {\n\n\t\tif (data.length !== length * 2) {\n\t\t\tthrow new Error(`${name} must be a ${length * 2} char HEX string.`);\n\t\t}\n\n\t\treturn Buffer.from(data, 'hex');\n\n\t}\n\n\tthrow new Error(`${name} must an instance of Buffer or a HEX string.`);\n\n};\n\n/**\n * Constructs a ACR122U Direct Transmit command\n *\n * Docs:\n * - ACR122U - see https://www.acs.com.hk/download-manual/419/API-ACR122U-2.04.pdf\n *   - Section 6.1 Direct Transmit\n *\n * @param payload {Buffer|ArrayBuffer|Uint8Array|number[]}\n * @returns {Buffer}\n */\nconst ACR122U_DirectTransmit = (payload) => {\n\n\tif (Array.isArray(payload) || ArrayBuffer.isView(payload)) {\n\t\tpayload = Buffer.from(payload);\n\t}\n\telse if (!Buffer.isBuffer(payload)) {\n\t\tthrow new Error(`payload must be a Buffer`);\n\t}\n\n\t// ACR122U Direct Transmit supports up to 255 bytes\n\tif (payload.length > 255) {\n\t\tthrow new Error(`payload cannot be longer than 255 bytes`);\n\t}\n\n\t// Direct Transmit command (see ACR122U docs, Section 6.1 Direct Transmit)\n\treturn Buffer.from([\n\t\t0xFF, // Class\n\t\t0x00, // INS\n\t\t0x00, // P1\n\t\t0x00, // P2\n\t\tpayload.length, // Lc: Length of the Direct Transmit Payload\n\t\t...payload, // Data In\n\t]);\n\n}\n\n/**\n * Constructs a NXP PN533 InDataExchange command\n *\n * Docs:\n * - NXP PN533 (embedded in the ACR122U) - https://www.nxp.com/docs/en/user-guide/157830_PN533_um080103.pdf\n *   - Section 8.4.8 InDataExchange\n *\n * @param tg {number}\n * @param dataOut {Buffer|ArrayBuffer|Uint8Array|number[]}\n * @returns {Buffer}\n */\nconst PN533_InDataExchange = (tg, dataOut) => {\n\n\tif (!Number.isInteger(tg) || tg < 0 || tg > 0xFF) {\n\t\tthrow new Error(`tg must be an integer in range [0, 255]`);\n\t}\n\n\tif (Array.isArray(dataOut) || ArrayBuffer.isView(dataOut)) {\n\t\tdataOut = Buffer.from(dataOut);\n\t}\n\telse if (!Buffer.isBuffer(dataOut)) {\n\t\tthrow new Error(`dataOut must be a Buffer`);\n\t}\n\n\tif (dataOut.length > 263) {\n\t\tthrow new Error(`dataOut cannot be longer than 264 bytes`);\n\t}\n\n\t// InDataExchange command (see NXP PN533 docs, Section 8.4.8 InDataExchange)\n\treturn Buffer.from([\n\t\t0xD4,\n\t\t0x40,\n\t\ttg,\n\t\t...dataOut,\n\t]);\n\n}\n\n/**\n * Constructs a NXP PN533 InCommunicateThru command\n *\n * Docs:\n * - NXP PN533 (embedded in the ACR122U) - https://www.nxp.com/docs/en/user-guide/157830_PN533_um080103.pdf\n *   - Section 8.4.9 InCommunicateThru\n *\n * @param dataOut {Buffer|ArrayBuffer|Uint8Array|number[]}\n * @returns {Buffer}\n */\nconst PN533_InCommunicateThru = (dataOut) => {\n\n\tif (Array.isArray(dataOut) || ArrayBuffer.isView(dataOut)) {\n\t\tdataOut = Buffer.from(dataOut);\n\t}\n\telse if (!Buffer.isBuffer(dataOut)) {\n\t\tthrow new Error(`dataOut must be a Buffer`);\n\t}\n\n\tif (dataOut.length > 264) {\n\t\tthrow new Error(`dataOut cannot be longer than 264 bytes`);\n\t}\n\n\t// InCommunicateThru command (see NXP PN533 docs, Section 8.4.9 InCommunicateThru)\n\treturn Buffer.from([\n\t\t0xD4,\n\t\t0x42,\n\t\t...dataOut,\n\t]);\n\n}\n\nclass MifareUltralightC {\n\n\t// See Section 7.5 Memory organization\n\tstatic NUM_PAGES = 48; // first 0x00, last 0x2F\n\tstatic PAGE_SIZE = 4; // 4 bytes (48 * 4 = 192 bytes EEPROM)\n\t// The first 4 memory pages (0x00 - 0x03) contain the 7-byte UID and its 2 Block Check Character Bytes (BCC),\n\t// 1 byte internal data (INT), 2 LOCK bytes, and 4 OTP bytes (7 + 2 + 1 + 2 + 4 = 16 bytes)\n\t// 36 user memory pages (app data, freeform), 36 * 4 = 144 bytes\n\tstatic USER_PAGE_FIRST = 0x04;\n\tstatic USER_PAGE_LAST = 0x27;\n\t// page 0x28 contain 2 LOCK bytes (LOCK2, LOCK3), the other 2 bytes of the page are not usable\n\t// page 0x29 contain one 16-bit counter, the other 2 bytes of the page are not usable\n\tstatic AUTH0_PAGE = 0x2A;\n\tstatic AUTH1_PAGE = 0x2B;\n\tstatic AUTH_KEY_PAGE_1 = 0x2C;\n\tstatic AUTH_KEY_PAGE_2 = 0x2D;\n\tstatic AUTH_KEY_PAGE_3 = 0x2E;\n\tstatic AUTH_KEY_PAGE_4 = 0x2F;\n\tstatic MEMORY_ACCESS_ONLY_WRITE_RESTRICTED = 0x01;\n\tstatic MEMORY_ACCESS_READ_WRITE_RESTRICTED = 0x00;\n\n\tconstructor(reader) {\n\t\tthis.reader = reader;\n\t}\n\n\t/**\n\t * Performs the 3DES authentication using the AUTHENTICATE command\n\t *\n\t * Docs:\n\t * - MIFARE Ultralight C - see https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf\n\t *   - Section 7.5.5 3DES Authentication\n\t *   - Section 9.5 AUTHENTICATE\n\t * - ACR122U - see https://www.acs.com.hk/download-manual/419/API-ACR122U-2.04.pdf\n\t *   - Section 6.1 Direct Transmit\n\t * - NXP PN533 (embedded in the ACR122U) - https://www.nxp.com/docs/en/user-guide/157830_PN533_um080103.pdf\n\t *   - Section 8.4.9 InCommunicateThru\n\t *\n\t * @param key {Buffer|string} the 16-bytes 3DES (DES-EDE-CBC) authentication key,\n\t *                            exactly the same byte order (**little-endian**) as when writing\n\t *                            to the auth key pages 0x2C-2F,\n\t *                            the first 8 bytes (0-7) correspond to the Key 1 (K1)\n\t *                            and the second 8 bytes (8-15) correspond to the Key 2 (K2),\n\t *                            see {@link MifareUltralightC.swapKeyEndianness}\n\t *                            for more info about the keys endianness (byte order)\n\t * @throws MifareUltralight3DESAuthenticationError\n\t * @returns {Promise<void>}\n\t */\n\tasync authenticate3DES(key) {\n\n\t\tkey = parseBytes('key', key, 16);\n\t\tthis.reader.logger.debug('key', key);\n\t\tconst keyBE = MifareUltralightC.swapKeyEndianness(key);\n\t\tthis.reader.logger.debug('keyBE', keyBE);\n\n\t\t// See MIFARE Ultralight C docs, Section 7.5.5 3DES Authentication, Table 8\n\t\t// Note 1:\n\t\t//   The MIFARE Ultralight C docs use the || symbol which (in that context) denotes concatenation,\n\t\t//   e.g., X || Y means concatenate(X, Y).\n\t\t//   We use this symbol with the same meaning in the following code comments.\n\t\t// Note 2:\n\t\t//   In the variable names, we use `2` instead of `'`. For example, RndB2 instead of RndB'.\n\t\t// Note 3:\n\t\t//   The numbering of the steps in the code below does not match the numbering used in Table 8.\n\n\t\t// 1. Get the encrypted RndB (8 bytes) from the PICC (MIFARE Ultralight C).\n\t\t//    This starts the authentication process.\n\t\tconst ekRndB = await this._authenticatePart1();\n\t\tthis.reader.logger.debug('ekRndB', ekRndB);\n\n\t\t// 2. Generate an 8-byte random number RndA.\n\t\tconst RndA = crypto.randomBytes(8);\n\t\tthis.reader.logger.debug('RndA', RndA);\n\n\t\t// 3. Compute ek(RndA || RndB').\n\t\t// First, get RndB by decrypting ekRndB.\n\t\t// The 1st encryption/decryptions uses the all zero IV.\n\t\tconst iv1 = MifareUltralightC.ZERO_IV;\n\t\tconst RndB = MifareUltralightC.decrypt(keyBE, ekRndB, iv1);\n\t\tthis.reader.logger.debug('RndB', RndB);\n\t\t// Then, compute RndB' by rotating the original RndB left by 8 bits.\n\t\t//   RndB  = [ byte 0, byte 1, byte 2, byte 3, byte 4, byte 5, byte 6, byte 7 ]\n\t\t//   RndB' = [ byte 1, byte 2, byte 3, byte 4, byte 5, byte 6, byte 7, byte 0 ]\n\t\tconst RndB2 = Buffer.concat([RndB.subarray(1, 8), RndB.subarray(0, 1)]);\n\t\tthis.reader.logger.debug('RndB2', RndB2);\n\t\t// Finally, compute ek(RndA || RndB').\n\t\t// For the subsequent encryptions/decryptions, the IV must be the last ciphertext block.\n\t\tconst iv2 = ekRndB;\n\t\tconst ekRndARndB2 = MifareUltralightC.encrypt(keyBE, Buffer.concat([RndA, RndB2]), iv2);\n\t\tthis.reader.logger.debug('ekRndARndB2', ekRndARndB2);\n\n\t\t// 4. Send ek(RndA || RndB') to get the encrypted RndA' from the PICC (MIFARE Ultralight C).\n\t\t//    This is the second and final authentication command.\n\t\tconst ekRndA2 = await this._authenticatePart2(ekRndARndB2);\n\t\tthis.reader.logger.debug('ekRndA2', ekRndA2);\n\n\t\t// 5. Decrypt the ekRndA' and un-rotate it to get the RndA from the PICC (MIFARE Ultralight C)\n\t\t//    for comparison with our RndA.\n\t\t// First, decrypt.\n\t\t// For the subsequent encryptions/decryptions, the IV must be the last ciphertext block.\n\t\t// ekRndARndB2 is 16 bytes, i.e., 2 ciphertext blocks, and we want the last one\n\t\tconst iv3 = ekRndARndB2.subarray(8, 16);\n\t\tconst RndA2 = MifareUltralightC.decrypt(keyBE, ekRndA2, iv3);\n\t\t// Then, un-rotate.\n\t\tconst RndAFromUltralight = Buffer.concat([RndA2.subarray(7, 8), RndA2.subarray(0, 7)]);\n\t\tthis.reader.logger.debug('RndA           (local)', RndA);\n\t\tthis.reader.logger.debug('RndA (from Ultralight)', RndAFromUltralight);\n\n\t\t// 6. Finally, compare the decrypted RndA from the PICC (MIFARE Ultralight C) (RndAFromUltralight)\n\t\t//    with the RndA value we generated in our code in the step 2.\n\t\t//    If they are equal, the authentication process was successful.\n\t\tif (!RndA.equals(RndAFromUltralight)) {\n\t\t\tthrow new MifareUltralight3DESAuthenticationError(\n\t\t\t\t'rnd_a_differs',\n\t\t\t\t'The RndA received from the MIFARE Ultralight C is different from the RndA that was sent. This means that the authentication process was not successful.',\n\t\t\t);\n\t\t}\n\n\t\tthis.reader.logger.debug('authenticate3DES: RndA from Ultralight matches, successfully authenticated');\n\n\t}\n\n\t/**\n\t * Creates a copy of the given authentication key but with swapped endianness (byte ordering) of the individual keys\n\t *\n\t * The authentication key is 16 bytes, where the first 8 bytes (0-7) correspond to the Key 1 (K1)\n\t * and the second 8 bytes (8-15) correspond to the Key 2 (K2).\n\t *\n\t * This function preserve the keys order (`input key = [ K1 K2 ]`, `output key = [ K1 K2 ]`),\n\t * but it changes byte ordering within the individual keys.\n\t * ```\n\t *    input key = [ K1B0 K1B1 K1B2 K1B3 K1B4 K1B5 K1B6 K1B7 K2B0 K2B1 K2B2 K2B3 K2B4 K2B5 K2B6 K2B7 ]\n\t *   output key = [ K1B7 K1B6 K1B5 K1B4 K1B3 K1B2 K1B1 K1B0 K2B7 K2B6 K2B5 K2B4 K2B3 K2B2 K2B1 K2B0 ]\n\t * ```\n\t *\n\t * @param key {Buffer} the two keys for DES-EDE-CBC stored as 16 bytes (2 x 8 bytes = 16 bytes),\n\t *                     where the first 8 bytes (0-7) correspond to the Key 1 (K1)\n\t *                     and the second 8 bytes (8-15) correspond to the Key 2 (K2).\n\t * @returns {Buffer} a copy of the given key but with swapped byte ordering within the individual keys,\n\t *                   BIG-endian to little-endian, little-endian to BIG-endian\n\t */\n\tstatic swapKeyEndianness(key) {\n\t\tconst keyCopy = Buffer.from(key);\n\t\t// since each key is 8 bytes, we can use the built-in swap64() method\n\t\t// to swap byte order of the two individual 8-byte keys\n\t\tkeyCopy.swap64();\n\t\treturn keyCopy;\n\t\t// alternatively, we could do it manually like this:\n\t\t// return Buffer.from([\n\t\t// \t/* Key 1 */ key[7], key[6], key[5], key[4], key[3], key[2], key[1], key[0],\n\t\t// \t/* Key 2 */ key[15], key[14], key[13], key[12], key[11], key[10], key[9], key[8],\n\t\t// ]);\n\t}\n\n\tstatic ZERO_IV = Buffer.alloc(8).fill(0);\n\n\t/**\n\t * Decrypts the given data using the given key and the given IV using the `DES-EDE-CBC` algorithm\n\t * (Two key triple DES EDE in CBC mode). This algorithm is used during the MIFARE Ultralight C authentication.\n\t *\n\t * From [MIFARE Ultralight C docs](https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf),\n\t * Section 7.5.5 3DES Authentication:\n\t * > The 3DES Authentication implemented in the MF0ICU2 proves that two entities\n\t * > hold the same secret and each entity can be seen as a reliable partner for onwards communication.\n\t * > The applied encryption algorithm ek() is the 2 key 3DES encryption\n\t * > in Cipher-Block Chaining (CBC) mode as described in ISO/IEC 10116.\n\t * > The Initial Value (IV) of the first encryption of the protocol is the all zero block.\n\t * > IMPORTANT! For the subsequent encryptions/decryptions, the IV consists of the last ciphertext block._\n\t *\n\t * @param keyBE {Buffer} the two keys for DES-EDE-CBC stored as 16 bytes (2 x 8 bytes = 16 bytes),\n\t *                       where the first 8 bytes (0-7) correspond to the Key 1 (K1)\n\t *                       and the second 8 bytes (8-15) correspond to the Key 2 (K2),\n\t *                       the individual keys (K1 and K2) must be **BIG-endian**,\n\t *                       see {@link MifareUltralightC.swapKeyEndianness}\n\t *                       for more info about the keys endianness (byte order)\n\t * @param data {Buffer} the data to decrypt, the length must be a multiple of 8 bytes,\n\t *                      which is the block size of DES-EDE-CBC\n\t * @param iv {Buffer} the IV (8 bytes) (Initial Value, also called Initialization Vector)\n\t *                    The 1st encryption/decryption during the MIFARE Ultralight C authentication\n\t *                    uses the all zero IV. **IMPORTANT!** For the subsequent encryptions/decryptions,\n\t *                    the IV must be the last ciphertext block.\n\t * @returns {Buffer} the decrypted data, the returned Buffer has the same length (size) as the input data\n\t */\n\tstatic decrypt(keyBE, data, iv) {\n\t\t// DES-EDE-CBC = Two key triple DES EDE in CBC mode\n\t\t//   (https://docs.openssl.org/3.4/man1/openssl-enc/#supported-ciphers)\n\t\t//   It has block size 8 bytes and the two keys are stored in the 16-bytes-long key (128 bits).\n\t\t//   However, only 112 bits are used, see https://crypto.stackexchange.com/a/63459.\n\t\tconst decipher = crypto.createDecipheriv('DES-EDE-CBC', keyBE, iv);\n\t\tdecipher.setAutoPadding(false);\n\t\treturn Buffer.concat([decipher.update(data), decipher.final()]);\n\t}\n\n\t/**\n\t * Encrypts the given data using the given key and the given IV using the `DES-EDE-CBC` algorithm\n\t * (Two key triple DES EDE in CBC mode). This algorithm is used during the MIFARE Ultralight C authentication.\n\t *\n\t * From [MIFARE Ultralight C docs](https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf),\n\t * Section 7.5.5 3DES Authentication:\n\t * > The 3DES Authentication implemented in the MF0ICU2 proves that two entities\n\t * > hold the same secret and each entity can be seen as a reliable partner for onwards communication.\n\t * > The applied encryption algorithm ek() is the 2 key 3DES encryption\n\t * > in Cipher-Block Chaining (CBC) mode as described in ISO/IEC 10116.\n\t * > The Initial Value (IV) of the first encryption of the protocol is the all zero block.\n\t * > IMPORTANT! For the subsequent encryptions/decryptions, the IV consists of the last ciphertext block._\n\t *\n\t * @param keyBE {Buffer} the two keys for DES-EDE-CBC stored as 16 bytes (2 x 8 bytes = 16 bytes),\n\t *                       where the first 8 bytes (0-7) correspond to the Key 1 (K1)\n\t *                       and the second 8 bytes (8-15) correspond to the Key 2 (K2),\n\t *                       the individual keys (K1 and K2) must be **BIG-endian**,\n\t *                       see {@link MifareUltralightC.swapKeyEndianness}\n\t *                       for more info about the keys endianness (byte order)\n\t * @param data {Buffer} the data to encrypt, the length must be a multiple of 8 bytes,\n\t *                      which is the block size of DES-EDE-CBC\n\t * @param iv {Buffer} the IV (8 bytes) (Initial Value, also called Initialization Vector)\n\t *                    The 1st encryption/decryption during the MIFARE Ultralight C authentication\n\t *                    uses the all zero IV. **IMPORTANT!** For the subsequent encryptions/decryptions,\n\t *                    the IV must be the last ciphertext block.\n\t * @returns {Buffer} the encrypted data, the returned Buffer has the same length (size) as the input data\n\t */\n\tstatic encrypt(keyBE, data, iv) {\n\t\t// DES-EDE-CBC = Two key triple DES EDE in CBC mode\n\t\t//   (https://docs.openssl.org/3.4/man1/openssl-enc/#supported-ciphers)\n\t\t//   It has block size 8 bytes and the two keys are stored in the 16-bytes-long key (128 bits).\n\t\t//   However, only 112 bits are used, see https://crypto.stackexchange.com/a/63459.\n\t\tconst encipher = crypto.createCipheriv('DES-EDE-CBC', keyBE, iv);\n\t\tencipher.setAutoPadding(false);\n\t\treturn Buffer.concat([encipher.update(data), encipher.final()]);\n\t}\n\n\t/**\n\t * Sends the AUTHENTICATE part 1 command and parses the response\n\t *\n\t * @see {authenticate3DES}\n\t * @throws MifareUltralight3DESAuthenticationError\n\t * @returns {Promise<Buffer>} ekRndB (8 bytes) - the encrypted RndB from the PICC (MIFARE Ultralight C)\n\t */\n\tasync _authenticatePart1() {\n\n\t\tconst cmdAuthenticatePart1 = ACR122U_DirectTransmit(\n\t\t\tPN533_InCommunicateThru([\n\t\t\t\t// AUTHENTICATE part 1 command\n\t\t\t\t// see MIFARE Ultralight C docs, Section 9.5 AUTHENTICATE, Table 23\n\t\t\t\t0x1A, // Cmd: authentication part 1\n\t\t\t\t0x00, // Arg: fixed value 00h as argument\n\t\t\t]),\n\t\t);\n\t\tthis.reader.logger.debug('cmdAuthenticatePart1', cmdAuthenticatePart1);\n\n\t\t/** @var {Buffer} */\n\t\tconst resAuthenticatePart1 = await this.reader.transmit(\n\t\t\tcmdAuthenticatePart1,\n\t\t\t// expected response max length:\n\t\t\t// AUTHENTICATE part 1 response should look like the following (14 bytes)\n\t\t\t// D5 43 00 AF xx xx xx xx xx xx xx xx 90 00\n\t\t\t// bytes 0-1: D5 43 InCommunicateThru output prefix (see NXP PN533 docs, Section 8.4.9 InCommunicateThru)\n\t\t\t// byte 2: InCommunicateThru status, 0x00 is success (see NXP PN533 docs, Table 15. Error code list)\n\t\t\t// byte 3: AUTHENTICATE part 1 first response byte (0xAF) that indicates\n\t\t\t//         the authentication process needs a second command part\n\t\t\t// bytes 4-11 (8 bytes): ek(RndB) - 8-byte encrypted PICC random number RndB\n\t\t\t// bytes 12-13 (last 2 bytes): ACR122U success code 0x90 0x00\n\t\t\t14,\n\t\t);\n\t\tthis.reader.logger.debug('resAuthenticatePart1', resAuthenticatePart1);\n\n\t\tif (resAuthenticatePart1.length !== 14) {\n\t\t\tthrow new MifareUltralight3DESAuthenticationError(\n\t\t\t\t'unexpected_response_length',\n\t\t\t\t`Unexpected response length for cmdAuthenticatePart1. Expected 14 bytes but got ${resAuthenticatePart1.length} bytes.`,\n\t\t\t);\n\t\t}\n\n\t\tif (\n\t\t\tresAuthenticatePart1[0] !== 0xD5 ||\n\t\t\tresAuthenticatePart1[1] !== 0x43 ||\n\t\t\tresAuthenticatePart1[2] !== 0x00 ||\n\t\t\tresAuthenticatePart1[3] !== 0xAF ||\n\t\t\tresAuthenticatePart1[12] !== 0x90 ||\n\t\t\tresAuthenticatePart1[13] !== 0x00\n\t\t) {\n\t\t\tthrow new MifareUltralight3DESAuthenticationError(\n\t\t\t\t'unexpected_response',\n\t\t\t\t`Unexpected response format for cmdAuthenticatePart1.`,\n\t\t\t);\n\t\t}\n\n\t\t// ekRndB - the encrypted RndB from the PICC (MIFARE Ultralight C)\n\t\treturn resAuthenticatePart1.subarray(4, 12);\n\n\t}\n\n\t/**\n\t * Sends the AUTHENTICATE part 2 command and parses the response\n\t *\n\t * @see {authenticate3DES}\n\t * @param ekRndARndB2 {Buffer} ek(RndA || RndB'): 16-byte encrypted random numbers (RndA concatenated with RndB')\n\t * @throws MifareUltralight3DESAuthenticationError\n\t * @returns {Promise<Buffer>} ekRndA2 (8 bytes) - the encrypted RndA' from the PICC (MIFARE Ultralight C)\n\t */\n\tasync _authenticatePart2(ekRndARndB2) {\n\n\t\tconst cmdAuthenticatePart2 = ACR122U_DirectTransmit(\n\t\t\tPN533_InCommunicateThru([\n\t\t\t\t// AUTHENTICATE part 2 command\n\t\t\t\t// see MIFARE Ultralight C docs, Section 9.5 AUTHENTICATE, Table 26\n\t\t\t\t0xAF, // Cmd: fixed first byte for the AUTHENTICATE part 2 command\n\t\t\t\t...ekRndARndB2, // ek(RndA || RndB'): 16-byte encrypted random numbers: RndA concatenated with RndB'\n\t\t\t]),\n\t\t);\n\t\tthis.reader.logger.debug('cmdAuthenticatePart2', cmdAuthenticatePart2);\n\n\t\t/** @var {Buffer} */\n\t\tconst resAuthenticatePart2 = await this.reader.transmit(\n\t\t\tcmdAuthenticatePart2,\n\t\t\t// expected response max length:\n\t\t\t// AUTHENTICATE part 1 response should look like the following (14 bytes)\n\t\t\t// D5 43 00 00 xx xx xx xx xx xx xx xx 90 00\n\t\t\t// bytes 0-1: D5 43 InCommunicateThru output prefix (see NXP PN533 docs, Section 8.4.9 InCommunicateThru)\n\t\t\t// byte 2: InCommunicateThru status, 0x00 is success (see NXP PN533 docs, Table 15. Error code list)\n\t\t\t// byte 3: AUTHENTICATE part 2 first response byte (0x00) that indicates\n\t\t\t//         the authentication process is finished after this command\n\t\t\t// bytes 4-11 (8 bytes): ek(RndA') - 8-byte encrypted, shifted PCD random number RndA'\n\t\t\t// bytes 12-13 (last 2 bytes): ACR122U success code 0x90 0x00\n\t\t\t14,\n\t\t);\n\t\tthis.reader.logger.debug('resAuthenticatePart2', resAuthenticatePart2);\n\n\t\tif (resAuthenticatePart2.length !== 14) {\n\t\t\tthrow new MifareUltralight3DESAuthenticationError(\n\t\t\t\t'unexpected_response_length',\n\t\t\t\t`Unexpected response length for cmdAuthenticatePart2. Expected 14 bytes but got ${resAuthenticatePart2.length} bytes.`,\n\t\t\t);\n\t\t}\n\n\t\tif (\n\t\t\tresAuthenticatePart2[0] !== 0xD5 ||\n\t\t\tresAuthenticatePart2[1] !== 0x43 ||\n\t\t\tresAuthenticatePart2[2] !== 0x00 ||\n\t\t\tresAuthenticatePart2[3] !== 0x00 ||\n\t\t\tresAuthenticatePart2[12] !== 0x90 ||\n\t\t\tresAuthenticatePart2[13] !== 0x00\n\t\t) {\n\t\t\tthrow new MifareUltralight3DESAuthenticationError(\n\t\t\t\t'unexpected_response',\n\t\t\t\t`Unexpected response format for cmdAuthenticatePart2.`,\n\t\t\t);\n\t\t}\n\n\t\t// ekRndA2 - the encrypted RndA' from the PICC (MIFARE Ultralight C)\n\t\treturn resAuthenticatePart2.subarray(4, 12);\n\n\t}\n\n\t/**\n\t * Sends the READ command and parses the response\n\t *\n\t * Docs:\n\t * - MIFARE Ultralight C - see https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf\n\t *   - Section 9.2 READ\n\t *\n\t * @param page {number} the start page address [0x00, 0x2B]\n\t * @throws MifareUltralightReadError\n\t * @returns {Promise<Buffer>} the read data (16 bytes)\n\t */\n\tasync read(page) {\n\n\t\tconst cmdRead = ACR122U_DirectTransmit(\n\t\t\tPN533_InCommunicateThru([\n\t\t\t\t// READ command\n\t\t\t\t// see MIFARE Ultralight C docs, Section 9.2 READ, Table 17\n\t\t\t\t0x30, // Cmd: read four pages\n\t\t\t\tpage, // Addr: start page address [0x00, 0x2B]\n\t\t\t]),\n\t\t);\n\t\tthis.reader.logger.debug('cmdRead', cmdRead);\n\n\t\t/** @var {Buffer} */\n\t\tconst resRead = await this.reader.transmit(\n\t\t\tcmdRead,\n\t\t\t// expected response max length:\n\t\t\t// READ response should look like the following (21 bytes)\n\t\t\t// D5 41 00 [d0] ... [d15] 90 00\n\t\t\t// bytes 0-1: D5 43 InCommunicateThru output prefix (see NXP PN533 docs, Section 8.4.9 InCommunicateThru)\n\t\t\t// byte 2: InCommunicateThru status, 0x00 is success (see NXP PN533 docs, Table 15. Error code list)\n\t\t\t// bytes 3-18 (16 bytes): the read data\n\t\t\t// bytes 19-20 (last 2 bytes): ACR122U success code 0x90 0x00\n\t\t\t21,\n\t\t);\n\t\tthis.reader.logger.debug('resRead', resRead);\n\n\t\tif (resRead.length !== 21) {\n\t\t\tthrow new MifareUltralightReadError(\n\t\t\t\t'unexpected_response_length',\n\t\t\t\t`Unexpected response length for cmdRead. Expected 21 bytes but got ${resRead.length} bytes.`,\n\t\t\t);\n\t\t}\n\n\t\tif (\n\t\t\tresRead[0] !== 0xD5 ||\n\t\t\tresRead[1] !== 0x43 ||\n\t\t\tresRead[2] !== 0x00 ||\n\t\t\tresRead[19] !== 0x90 ||\n\t\t\tresRead[20] !== 0x00\n\t\t) {\n\t\t\tthrow new MifareUltralightReadError(\n\t\t\t\t'unexpected_response',\n\t\t\t\t`Unexpected response format for cmdRead.`,\n\t\t\t);\n\t\t}\n\n\t\t// the read data\n\t\treturn resRead.subarray(3, 19);\n\n\t}\n\n\t/**\n\t * Sends the WRITE command and parses the response\n\t *\n\t * Docs:\n\t * - MIFARE Ultralight C - see https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf\n\t *   - Section 9.3 WRITE\n\t *\n\t * @param page {number} the page address [0x02, 0x2F]\n\t * @param data {Buffer} the page data to write (4 bytes)\n\t * @throws MifareUltralightReadError\n\t * @returns {Promise<void>}\n\t */\n\tasync write(page, data) {\n\n\t\tconst cmdWrite = ACR122U_DirectTransmit(\n\t\t\t// Interestingly, the InCommunicateThru command works as well, but the response\n\t\t\t// is always D5 43 02 90 00 (0x02 = \"A CRC error has been detected by the CIU\"),\n\t\t\t// even though that the WRITE command succeeds. Maybe it is because the WRITE command\n\t\t\t// does not have any data in its response?\n\t\t\t// Nevertheless, InDataExchange seems to work without this problem,\n\t\t\t// so we used it here instead of InCommunicateThru.\n\t\t\tPN533_InDataExchange(\n\t\t\t\t// PN533 supports only one target at the time.\n\t\t\t\t// By testing empirically, we figured that the Tg value should be always set to 1\n\t\t\t\t// (at least when the InDataExchange command is used in the standard ACR122U reader flow).\n\t\t\t\t1,\n\t\t\t\t[\n\t\t\t\t\t// WRITE command\n\t\t\t\t\t// see MIFARE Ultralight C docs, Section 9.3 WRITE, Table 19\n\t\t\t\t\t0xA2, // Cmd: write one page\n\t\t\t\t\tpage, // Addr: the page address [0x02, 0x2F]\n\t\t\t\t\t...data, // Data: the page data to write (4 bytes)\n\t\t\t\t],\n\t\t\t),\n\t\t);\n\t\tthis.reader.logger.debug('cmdWrite', cmdWrite);\n\n\t\t/** @var {Buffer} */\n\t\tconst resWrite = await this.reader.transmit(\n\t\t\tcmdWrite,\n\t\t\t// expected response max length:\n\t\t\t// WRITE response should look like the following (5 bytes)\n\t\t\t// D5 41 00 90 00\n\t\t\t// bytes 0-1: D5 41 InDataExchange output prefix (see NXP PN533 docs, Section 8.4.8 InDataExchange)\n\t\t\t// byte 2: InDataExchange status, 0x00 is success (see NXP PN533 docs, Table 15. Error code list)\n\t\t\t// bytes 3-4 (last 2 bytes): ACR122U success code 0x90 0x00\n\t\t\t5,\n\t\t);\n\t\tthis.reader.logger.debug('resWrite', resWrite);\n\n\t\tif (resWrite.length !== 5) {\n\t\t\tthrow new MifareUltralightWriteError(\n\t\t\t\t'unexpected_response_length',\n\t\t\t\t`Unexpected response length for cmdWrite. Expected 5 bytes but got ${resWrite.length} bytes.`,\n\t\t\t);\n\t\t}\n\n\t\tif (\n\t\t\tresWrite[0] !== 0xD5 ||\n\t\t\tresWrite[1] !== 0x41 ||\n\t\t\tresWrite[2] !== 0x00 ||\n\t\t\tresWrite[3] !== 0x90 ||\n\t\t\tresWrite[4] !== 0x00\n\t\t) {\n\t\t\tthrow new MifareUltralightWriteError(\n\t\t\t\t'unexpected_response',\n\t\t\t\t`Unexpected response format for cmdWrite.`,\n\t\t\t);\n\t\t}\n\n\t}\n\n\t/**\n\t * Writes the given AUTH0 byte value\n\t *\n\t * Docs:\n\t * - MIFARE Ultralight C - see https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf\n\t *   - Section 7.5.8 Configuration for memory access via 3DES Authentication\n\t *\n\t * @param value {number} The AUTH0 byte value defines the page address from which the authentication is required.\n\t *                       Valid address values are from `0x03` (all pages are protected)\n\t *                       to `0x30` (memory protection effectively disabled).\n\t * @see writeAuth1\n\t * @returns {Promise<void>}\n\t */\n\tasync writeAuth0(value) {\n\t\tif (!Number.isInteger(value) || value < 0x03 || value > 0x30) {\n\t\t\tthrow new Error('Invalid AUTH0 value!');\n\t\t}\n\t\tawait this.write(MifareUltralightC.AUTH0_PAGE, Buffer.from([\n\t\t\tvalue,\n\t\t\t0x00,\n\t\t\t0x00,\n\t\t\t0x00,\n\t\t]));\n\t}\n\n\t/**\n\t * Writes the given AUTH1 byte value\n\t *\n\t * Docs:\n\t * - MIFARE Ultralight C - see https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf\n\t *   - Section 7.5.8 Configuration for memory access via 3DES Authentication\n\t *\n\t * @param value {number} The AUTH1 byte value determines if only write access is restricted\n\t *                       (`AUTH1 = 0bxxxxxxx1`), or if both read and write access\n\t *                       are restricted (`AUTH1 = 0bxxxxxxx0`). The `x` symbol denotes ignored bits.\n\t *                       The ignored are persisted, so they can be used for storing additional app-specific data.\n\t * @see writeAuth0\n\t * @returns {Promise<void>}\n\t */\n\tasync writeAuth1(value) {\n\t\tif (!Number.isInteger(value) || value < 0x00 || value > 0xFF) {\n\t\t\tthrow new Error('Invalid AUTH1 value!');\n\t\t}\n\t\tawait this.write(MifareUltralightC.AUTH1_PAGE, Buffer.from([\n\t\t\tvalue,\n\t\t\t0x00,\n\t\t\t0x00,\n\t\t\t0x00,\n\t\t]));\n\t}\n\n}\n\n// This is the default factory key of MIFARE Ultralight C\n// See Section 7.5.10 Initial memory configuration, Table 13. Initial memory organization,\n// https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf.\nconst DEFAULT_KEY = Buffer.from('BREAKMEIFYOUCAN!', 'utf-8');\n\n// Note that some other implementations of the authenticate3DES\n// might require the authentication key with a different byte order,\n// see the MifareUltralightC.swapKeyEndianness() method above for more info.\nassert.deepEqual(MifareUltralightC.swapKeyEndianness(DEFAULT_KEY), Buffer.from('IEMKAERB!NACUOYF', 'utf-8'));\n\nconst ZERO_KEY = Buffer.from('00000000000000000000000000000000', 'hex');\nconst ONES_KEY = Buffer.from('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 'hex');\nconst DEMO_KEY = Buffer.from('AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD', 'hex');\n\nconst nfc = new NFC(pretty); // we pass an optional logger to see internal debug logs\n\nnfc.on('reader', async reader => {\n\n\tpretty.info(`device attached`, reader);\n\n\tconst ultralightC = new MifareUltralightC(reader);\n\n\treader.on('card', async card => {\n\n\t\tpretty.info('card detected', reader, card);\n\n\t\ttry {\n\n\t\t\t// Note:\n\t\t\t//   Depending on your MIFARE Ultralight C configuration, authentication might not be required.\n\t\t\t//   In the factory state, all read/write operations are allowed without authentication.\n\t\t\t//   Nevertheless, we can always perform authentication.\n\t\t\tawait ultralightC.authenticate3DES(DEFAULT_KEY);\n\t\t\t// await ultralightC.authenticate3DES(ZERO_KEY);\n\t\t\t// await ultralightC.authenticate3DES(ONES_KEY);\n\t\t\t// await ultralightC.authenticate3DES(DEMO_KEY);\n\t\t\tpretty.info('successfully authenticated');\n\n\t\t\t// # Update the authentication key\n\n\t\t\t// const key = DEFAULT_KEY;\n\t\t\t// // (Section 7.5.7 Programming of 3DES key to memory)\n\t\t\t// // // write data using the universal read/write methods (works with many standard PC/SC readers)\n\t\t\t// await reader.write(0x2C, key.subarray(0, 4), 4);\n\t\t\t// await reader.write(0x2D, key.subarray(4, 8), 4);\n\t\t\t// await reader.write(0x2E, key.subarray(8, 12), 4);\n\t\t\t// await reader.write(0x2F, key.subarray(12, 16), 4);\n\t\t\t// pretty.info('authentication key successfully written');\n\t\t\t// // // alternatively, use the WRITE command directly (only works with ACR122U NFC USB reader)\n\t\t\t// // await ultralightC.write(0x2C, key.subarray(0, 4));\n\t\t\t// // await ultralightC.write(0x2D, key.subarray(4, 8));\n\t\t\t// // await ultralightC.write(0x2E, key.subarray(8, 12));\n\t\t\t// // await ultralightC.write(0x2F, key.subarray(12, 16));\n\t\t\t// // pretty.info('authentication key successfully written');\n\n\t\t\t// # Protect memory from write and optionally read\n\n\t\t\t// // See Section 7.5.8 Configuration for memory access via 3DES Authentication of MF0ICU2.pdf.\n\t\t\t// const firstAuthProtectedPage = 0x28; // an example\n\t\t\t// const disableProtection = 0x30; // factory default\n\t\t\t// await ultralightC.writeAuth0(disableProtection);\n\t\t\t// // read-write protection, factory default\n\t\t\t// await ultralightC.writeAuth1(MifareUltralightC.MEMORY_ACCESS_READ_WRITE_RESTRICTED);\n\t\t\t// // only write protection\n\t\t\t// // await ultralightC.writeAuth1(MifareUltralightC.MEMORY_ACCESS_ONLY_WRITE_RESTRICTED);\n\n\t\t\t// Note that you can also use LOCK bytes LOCK 0-4 to turn selected pages permanently into a read-only memory.\n\t\t\t// See Section 7.5.2 and Section 7.5.3 of MF0ICU2.pdf.\n\n\t\t\t// // # Write data\n\t\t\t//\n\t\t\t// const text = Buffer.from('ahoy', 'utf8');\n\t\t\t// // write data using the universal read/write methods (works with many standard PC/SC readers)\n\t\t\t// await reader.write(0x20, text, 4);\n\t\t\t// // // alternatively, use the WRITE command directly (only works with ACR122U NFC USB reader)\n\t\t\t// // await ultralightC.write(0x20, text);\n\t\t\t//\n\t\t\t// // # Read data\n\t\t\t//\n\t\t\t// // read data using the universal read/write methods (works with many standard PC/SC readers)\n\t\t\t// const data = await reader.read(0x20, 4, 4);\n\t\t\t// pretty.info('data', data);\n\t\t\t// pretty.info('data as UTF8', data.toString('utf8'));\n\t\t\t// // // alternatively, use the READ command directly (only works with ACR122U NFC USB reader)\n\t\t\t// // const data = await ultralightC.read(0x20);\n\t\t\t// // pretty.info('data', data.subarray(0, 4));\n\t\t\t// // pretty.info('data as UTF8', data.subarray(0, 4).toString('utf8'));\n\n\t\t} catch (err) {\n\t\t\tpretty.error('error:', err);\n\t\t}\n\n\t});\n\n\treader.on('error', err => {\n\t\tpretty.error(`an error occurred`, reader, err);\n\t});\n\n\treader.on('end', () => {\n\t\tpretty.info(`device removed`, reader);\n\t});\n\n});\n\nnfc.on('error', err => {\n\tpretty.error(`an error occurred`, err);\n});\n\n/**\n * This is the 3DES Authentication example from Section 7.5.6 (Table 9)\n * of [MIFARE Ultralight C docs](https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf).\n *\n * The function contains asserts (the expected values taken from the docs).\n * When the function returns without throwing an error,\n * it means our encryption/decryption methods work correctly.\n */\nfunction numerical3DESExampleFromMF0ICU2() {\n\n\tconst keyBE = parseBytes('key', '49454D4B41455242214E4143554F5946', 16);\n\n\tconst PICC_RndB = parseBytes('PICC_RndB', '51E764602678DF2B', 8);\n\tconst PICC_ekRndB = parseBytes('PICC_ekRndB', '577293FD2F34CA51', 8);\n\n\tconst PCD_ekRndB = PICC_ekRndB;\n\tconst PCD_RndB = MifareUltralightC.decrypt(keyBE, PCD_ekRndB, MifareUltralightC.ZERO_IV);\n\tif (!PCD_RndB.equals(PICC_RndB)) {\n\t\tthrow new Error('PCD_RndB');\n\t}\n\n\tconst PCD_RndA = parseBytes('PCD_RndA', 'A8AF3B256C75ED40', 8);\n\tconst PCD_RndB2 = Buffer.concat([PCD_RndB.subarray(1, 8), PCD_RndB.subarray(0, 1)]);\n\tconst PCD_RndARndB2 = Buffer.concat([PCD_RndA, PCD_RndB2]);\n\tconst PCD_ekRndARndB2 = MifareUltralightC.encrypt(keyBE, PCD_RndARndB2, PCD_ekRndB);\n\tconst expected_PCD_ekRndARndB2 = parseBytes('expected_PCD_ekRndARndB2', '0A638559FC7737F9F15D7862EBBE967A', 16);\n\tif (!PCD_ekRndARndB2.equals(expected_PCD_ekRndARndB2)) {\n\t\tthrow new Error('PCD_ekRndARndB2');\n\t}\n\n\tconst PICC_ekRndARndB2 = PCD_ekRndARndB2;\n\tconst PICC_RndARndB2 = MifareUltralightC.decrypt(keyBE, PICC_ekRndARndB2, PICC_ekRndB);\n\tif (!PICC_RndARndB2.equals(PCD_RndARndB2)) {\n\t\tthrow new Error('PICC_RndARndB2');\n\t}\n\tconst PICC_RndA = PICC_RndARndB2.subarray(0, 8);\n\tif (!PICC_RndA.equals(PCD_RndA)) {\n\t\tthrow new Error('PICC_RndA');\n\t}\n\tconst PICC_RndA2 = Buffer.concat([PICC_RndA.subarray(1, 8), PICC_RndA.subarray(0, 1)]);\n\tconst expected_PICC_ekRndA2 = parseBytes('expected_PICC_ekRndA2', '3B884FA07C137CE1', 8);\n\tconst PICC_ekRndA2 = MifareUltralightC.encrypt(keyBE, PICC_RndA2, PICC_ekRndARndB2.subarray(8, 16));\n\tif (!PICC_ekRndA2.equals(expected_PICC_ekRndA2)) {\n\t\tthrow new Error('PICC_ekRndA2');\n\t}\n\n\tconst PCD_ekRndA2 = PICC_ekRndA2;\n\tconst PCD_RndA2 = MifareUltralightC.decrypt(keyBE, PCD_ekRndA2, PCD_ekRndARndB2.subarray(8, 16));\n\tif (!PCD_RndA2.equals(PICC_RndA2)) {\n\t\tthrow new Error('PCD_RndA2');\n\t}\n\tconst PCD_RndA_fromPICC = Buffer.concat([PCD_RndA2.subarray(7, 8), PCD_RndA2.subarray(0, 7)]);\n\tif (!PCD_RndA_fromPICC.equals(PCD_RndA)) {\n\t\tthrow new Error('PCD_RndA_fromPICC');\n\t}\n\n}\n\nnumerical3DESExampleFromMF0ICU2();\n"
  },
  {
    "path": "examples/mifare-ultralight-ntag.js",
    "content": "'use strict';\n\n// #############\n// Example: MIFARE Ultralight EV1 and NTAG 213/215/216 – implementation of card's specific commands\n// - note: for instructions on reading and writing the data,\n//   please see read-write.js, which is common for all ISO/IEC 14443-3 tags\n// - note: this guide applies to NTAG 213/215/216 cards as-well\n//   (the commands and configuration pages structure is same or very similar,\n//   the only difference is the location of the pages due to different user memory size)\n// - docs (descriptions of the commands and data structure):\n//   - MIFARE Ultralight EV1 – see https://www.nxp.com/docs/en/data-sheet/MF0ULX1.pdf\n//   - NTAG 213/215/216 – https://www.nxp.com/docs/en/data-sheet/NTAG213_215_216.pdf\n// - note: works ONLY on ACR122U reader or possibly any reader which uses NXP PN533 and similar NFC frontends\n// - what is covered:\n//   - password authentication – PWD_AUTH command\n//   - fast read – FAST_READ command\n//   - setting card configuration pages – set custom password and access conditions\n// #############\n\nimport { NFC, TAG_ISO_14443_3, TAG_ISO_14443_4, KEY_TYPE_A, KEY_TYPE_B, TransmitError } from '../src/index';\nimport pretty from './pretty-logger';\n\n\nexport class MifareUltralightPasswordAuthenticationError extends TransmitError {\n\n\tconstructor(code, message, previousError) {\n\n\t\tsuper(code, message, previousError);\n\n\t\tthis.name = 'MifareUltralightPasswordAuthenticationError';\n\n\t}\n\n}\n\nexport class MifareUltralightFastReadError extends TransmitError {\n\n\tconstructor(code, message, previousError) {\n\n\t\tsuper(code, message, previousError);\n\n\t\tthis.name = 'MifareUltralightFastReadError';\n\n\t}\n\n}\n\nconst parseBytes = (name, data, length) => {\n\n\tif (!(data instanceof Buffer) && typeof data !== 'string') {\n\t\tthrow new Error(`${name} must an instance of Buffer or a HEX string.`);\n\t}\n\n\tif (Buffer.isBuffer(data)) {\n\n\t\tif (data.length !== length) {\n\t\t\tthrow new Error(`${name} must be ${length} bytes long.`);\n\t\t}\n\n\t\treturn data;\n\n\t}\n\n\tif (typeof data === 'string') {\n\n\t\tif (data.length !== length * 2) {\n\t\t\tthrow new Error(`${name} must be a ${length * 2} char HEX string.`);\n\t\t}\n\n\t\treturn Buffer.from(data, 'hex');\n\n\t}\n\n\tthrow new Error(`${name} must an instance of Buffer or a HEX string.`);\n\n};\n\nclass MifareUltralight {\n\n\tconstructor(reader) {\n\t\tthis.reader = reader;\n\t}\n\n\t// PWD_AUTH\n\tasync passwordAuthenticate(password, pack) {\n\n\t\t// PASSWORD (4 bytes) (stored on card in page 18)\n\t\t// PACK (2 bytes) (stored in page 19 as first two bytes)\n\t\t// PACK is the response from card in case of successful PWD_AUTH cmd\n\n\t\tpassword = parseBytes('Password', password, 4);\n\t\tpack = parseBytes('Pack', pack, 2);\n\n\t\t// CMD: PWD_AUTH via Direct Transmit (ACR122U) and Data Exchange (PN533)\n\t\tconst cmd = Buffer.from([\n\t\t\t0xff, // Class\n\t\t\t0x00, // Direct Transmit (see ACR122U docs)\n\t\t\t0x00, // ...\n\t\t\t0x00, // ...\n\t\t\t0x07, // Length of Direct Transmit payload\n\t\t\t// Payload (7 bytes)\n\t\t\t0xd4, // Data Exchange Command (see PN533 docs)\n\t\t\t0x42, // InCommunicateThru\n\t\t\t0x1b, // PWD_AUTH\n\t\t\t...password,\n\t\t]);\n\n\t\tthis.reader.logger.debug('pwd_auth cmd', cmd);\n\n\n\t\tconst response = await this.reader.transmit(cmd, 7);\n\n\t\tthis.reader.logger.debug('pwd_auth response', response);\n\t\t// pwd_auth response should look like the following (7 bytes)\n\t\t// d5 43 00 ab cd 90 00\n\t\t// byte 0: d5 prefix for response of Data Exchange Command (see PN533 docs)\n\t\t// byte 1: 43 prefix for response of Data Exchange Command (see PN533 docs)\n\t\t// byte 2: Data Exchange Command Status 0x00 is success (see PN533 docs, Table 15. Error code list)\n\t\t// bytes 3-4: Data Exchange Command Response – our PACK (set on card in page 19, in bytes 0-1) from card\n\t\t// bytes 5-6: ACR122U success code\n\n\t\tif (response.length < 5) {\n\t\t\tthrow new MifareUltralightPasswordAuthenticationError('invalid_response_length', `Invalid response length ${response.length}. Expected minimal length was 2 bytes.`)\n\t\t}\n\n\t\tif (response[2] !== 0x00 || response.length < 7) {\n\t\t\tthrow new MifareUltralightPasswordAuthenticationError('invalid_password', `Authentication failed. Might be invalid password or unsupported card.`);\n\t\t}\n\n\t\tif (!response.slice(3, 5).equals(pack)) {\n\t\t\tthrow new MifareUltralightPasswordAuthenticationError('pack_mismatch', `Pack mismatch.`)\n\t\t}\n\n\t\treturn;\n\n\t}\n\n\t// FAST_READ\n\tasync fastRead(startPage, endPage) {\n\n\t\t// CMD: PWD_AUTH via Direct Transmit (ACR122U) and Data Exchange (PN533)\n\t\tconst cmd = Buffer.from([\n\t\t\t0xff, // Class\n\t\t\t0x00, // Direct Transmit (see ACR122U docs)\n\t\t\t0x00, // ...\n\t\t\t0x00, // ...\n\t\t\t0x07, // Length of Direct Transmit payload\n\t\t\t// Payload (7 bytes)\n\t\t\t0xd4, // Data Exchange Command (see PN533 docs)\n\t\t\t0x42, // InCommunicateThru\n\t\t\t0x3a, // PWD_AUTH\n\t\t\tstartPage,\n\t\t\tendPage,\n\t\t]);\n\n\t\tconst length = 3 + ((endPage - startPage + 1) * 4) + 2;\n\n\t\tconst response = await this.reader.transmit(cmd, length);\n\n\t\tif (response < length) {\n\t\t\tthrow new MifareUltralightFastReadError('invalid_response_length', `Invalid response length ${response.length}. Expected length was ${length} bytes.`)\n\t\t}\n\n\t\treturn response.slice(3, -2);\n\n\t}\n\n}\n\nconst nfc = new NFC(pretty); // const nfc = new NFC(pretty); // optionally you can pass logger to see internal debug logs\n\nnfc.on('reader', async reader => {\n\n\tpretty.info(`device attached`, reader);\n\n\tconst ultralight = new MifareUltralight(reader);\n\n\treader.on('card', async card => {\n\n\t\tpretty.info('card detected', reader, card);\n\n\t\tconst password = 'FFFFFFFF'; // default password\n\t\tconst pack = '0000'; // default pack\n\n\t\ttry {\n\n\t\t\tawait ultralight.passwordAuthenticate(password, pack);\n\n\t\t\tpretty.info('passwordAuthenticate: successfully authenticated');\n\n\t\t} catch (err) {\n\t\t\tpretty.error('passwordAuthenticate error:', err);\n\t\t}\n\n\t\ttry {\n\n\t\t\tconst data = await ultralight.fastRead(16, 19);\n\n\t\t\tpretty.info('fastRead data:', data);\n\n\t\t} catch (err) {\n\t\t\tpretty.error('fastRead error:', err);\n\t\t\treturn;\n\t\t}\n\n\t\t// Note! UPDATE locations of configuration pages according to the version of your card!\n\t\t// (see memory layout in your card's docs)\n\n\t\t// try {\n\t\t//\n\t\t// \t// set custom PASSWORD (4 bytes) (stored in page 18)\n\t\t// \tawait reader.write(19, password);\n\t\t//\n\t\t// \t// set custom PACK (2 bytes) (stored in page 19 as first two bytes\n\t\t// \tconst packPage = await reader.read(19, 4);\n\t\t// \tpackPage[0] = pack[0];\n\t\t// \tpackPage[1] = pack[1];\n\t\t// \tawait reader.write(19, packPage);\n\t\t//\n\t\t// \t// read current configuration\n\t\t// \tconst config = await reader.read(16, 8);\n\t\t//\n\t\t// \t// Configuration page 16\n\t\t// \tconsole.log(config[0]);\n\t\t// \tconsole.log(config[1]);\n\t\t// \tconsole.log(config[2]);\n\t\t// \tconsole.log(config[3]); // AUTH0 (default: 0xff)\n\t\t//\n\t\t// \t// Configuration page 17\n\t\t// \tconsole.log(config[4]); // ACCESS\n\t\t// \tconsole.log(config[5]); // VCTID (default: 0x05)\n\t\t// \tconsole.log(config[6]);\n\t\t// \tconsole.log(config[7]);\n\t\t//\n\t\t// \t// Protect everything (start with first data page)\n\t\t// \tconfig[3] = 0x04;\n\t\t//\n\t\t// \t// set ACCESS bits\n\t\t// \t// bit 7: PROT One bit inside the ACCESS byte defining the memory protection\n\t\t// \t//          0b ... write access is protected by the password verification\n\t\t// \t//          1b ... read and write access is protected by the password verification\n\t\t// \t// bit 6: CFGLCK Write locking bit for the user configuration\n\t\t// \t//        - 0b ... user configuration open to write access\n\t\t// \t//        - 1b ... user configuration permanently locked against write access\n\t\t// \t// bits 5-3: reserved\n\t\t// \t// bits 2-0: AUTHLIM\n\t\t// \t// bit number-76543210\n\t\t// \t//            ||||||||\n\t\t// \tconfig[4] = 0b10000000;\n\t\t//\n\t\t// \t// set custom access rules\n\t\t// \tawait reader.write(16, config);\n\t\t//\n\t\t// } catch (err) {\n\t\t// \tpretty.error('configuration write error:', err);\n\t\t// }\n\n\t});\n\n\treader.on('error', err => {\n\t\tpretty.error(`an error occurred`, reader, err);\n\t});\n\n\treader.on('end', () => {\n\t\tpretty.info(`device removed`, reader);\n\t});\n\n});\n\nnfc.on('error', err => {\n\tpretty.error(`an error occurred`, err);\n});\n"
  },
  {
    "path": "examples/mini-logger.js",
    "content": "\"use strict\";\n\n// mini-logger for debugging\n\nfunction log() {\n\tconsole.log(...arguments);\n}\n\nconst logger = {\n\tlog: log,\n\tdebug: log,\n\tinfo: log,\n\twarn: log,\n\terror: log,\n};\n\nexport default logger;\n"
  },
  {
    "path": "examples/ndef.js",
    "content": "\"use strict\";\n\n// #############\n// example not finished, it in progress !!!\n// Read NDEF formatted data\n// #############\n\nimport { NFC, TAG_ISO_14443_3, TAG_ISO_14443_4, KEY_TYPE_A, KEY_TYPE_B } from '../src/index';\nimport pretty from './pretty';\n"
  },
  {
    "path": "examples/pretty-logger.js",
    "content": "\"use strict\";\n\n// pretty-logger for debugging\n// uses great winston logger library, visit https://github.com/winstonjs/winston\n\nimport util from 'util';\nimport chalk from 'chalk';\nimport winston from 'winston';\nimport { SPLAT } from 'triple-beam';\n\n\nconst colors = {\n\texception: 'red',\n\terror: 'red',\n\twarn: 'yellow',\n\tinfo: 'green',\n\tverbose: 'blue',\n\tdebug: 'blue',\n\tsilly: 'gray',\n};\n\nwinston.addColors(colors);\n\n// we could use instanceof but to avoid import we simply check obj structure\nconst isReader = obj => typeof obj === 'object' && obj.reader && obj.name;\n\nconst printf = winston.format.printf(({ timestamp, level, message, [SPLAT]: splat }) => {\n\n\tlet splatString = '';\n\n\tlet reader = '';\n\n\tif (splat) {\n\n\t\tlet readerObj = splat.find(isReader);\n\n\t\tif (readerObj) {\n\t\t\treader = chalk.cyan(readerObj.name) + ' ';\n\t\t\tsplat = splat.filter(obj => !isReader(obj));\n\t\t}\n\n\t\tif (splat.length > 1) {\n\t\t\tsplatString = ' ' + util.inspect(splat, { colors: true });\n\t\t}\n\t\telse if (splat.length > 0) {\n\t\t\tsplatString = ' ' + util.inspect(splat[0], { colors: true });\n\t\t}\n\n\t}\n\n\t// see https://stackoverflow.com/questions/10729276/how-can-i-get-the-full-object-in-node-jss-console-log-rather-than-object\n\treturn `${timestamp ? timestamp + ' – ' : ''}${reader}${level}: ${message}${splatString}`;\n\n});\n\nconst FORMAT = winston.format.combine(\n\twinston.format.timestamp({\n\t\tformat: () => chalk.gray(new Date().toLocaleTimeString()),\n\t}),\n\twinston.format.colorize(),\n\tprintf,\n);\n\nconst logger = winston.createLogger({\n\ttransports: [\n\t\tnew (winston.transports.Console)({\n\t\t\tlevel: 'silly',\n\t\t\tformat: FORMAT,\n\t\t}),\n\t],\n\texitOnError: true,\n\n});\n\n\nexport default logger;\n"
  },
  {
    "path": "examples/read-write.js",
    "content": "\"use strict\";\n\n// #############\n// Example: Reading and writing data\n// - should work well with any compatible PC/SC card reader\n// - tested with MIFARE Ultralight cards but should work with many others (e.g. NTAG)\n// - what is covered:\n//   - example reading and writing data on from/to card\n// - NOTE! for reading and writing data from/to MIFARE Classic please see examples/mifare-classic.js which explains MIFARE Classic specifics\n// #############\n\nimport { NFC, TAG_ISO_14443_3, TAG_ISO_14443_4, KEY_TYPE_A, KEY_TYPE_B } from '../src/index';\nimport pretty from './pretty-logger';\n\n\nconst nfc = new NFC(); // const nfc = new NFC(pretty); // optionally you can pass logger to see internal debug logs\n\nnfc.on('reader', async reader => {\n\n\tpretty.info(`device attached`, reader);\n\n\t// enable when you want to auto-process ISO 14443-4 tags (standard=TAG_ISO_14443_4)\n\t// when an ISO 14443-4 is detected, SELECT FILE command with the AID is issued\n\t// the response is available as card.data in the card event\n\t// you can set reader.aid to:\n\t// 1. a HEX string (which will be parsed automatically to Buffer)\n\treader.aid = 'F222222222';\n\t// 2. an instance of Buffer containing the AID bytes\n\t// reader.aid = Buffer.from('F222222222', 'hex');\n\t// 3. a function which must return an instance of a Buffer when invoked with card object (containing standard and atr)\n\t//    the function may generate AIDs dynamically based on the detected card\n\t// reader.aid = ({ standard, atr }) => {\n\t//\n\t// \treturn Buffer.from('F222222222', 'hex');\n\t//\n\t// };\n\n\treader.on('card', async card => {\n\n\t\tpretty.info(`card detected`, reader, card);\n\n\t\t// example reading 4 bytes assuming containing 16bit integer\n\t\t// !!! note that we don't need 4 bytes - 16bit integer takes just 2 bytes !!!\n\t\ttry {\n\n\t\t\t// reader.read(blockNumber, length, blockSize = 4, packetSize = 16)\n\t\t\t// - blockNumber - memory block number where to start reading\n\t\t\t// - length - how many bytes to read\n\t\t\t// - blockSize - 4 for MIFARE Ultralight, 16 for MIFARE Classic\n\t\t\t// ! Caution! length must be divisible by blockSize (we have to read the whole block(s))\n\n\t\t\tconst data = await reader.read(4, 4);\n\n\t\t\tpretty.info(`data read`, reader, data);\n\n\t\t\tconst payload = data.readInt16BE(0);\n\n\t\t\tpretty.info(`data converted`, reader, payload);\n\n\t\t} catch (err) {\n\t\t\tpretty.error(`error when reading data`, reader, err);\n\t\t}\n\n\n\t\t// example write 4 bytes containing 16bit integer\n\t\t// !!! note that we don't need 16 bytes - 16bit integer takes just 2 bytes !!!\n\t\ttry {\n\n\t\t\t// reader.write(blockNumber, data, blockSize = 4, packetSize = 16)\n\t\t\t// - blockNumber - memory block number where to start writing\n\t\t\t// - data - what to write\n\t\t\t// - blockSize - 4 for MIFARE Ultralight, 16 for MIFARE Classic\n\t\t\t// ! Caution! data.length must be divisible by blockSize (we have to write the whole block(s))\n\n\t\t\tconst data = Buffer.allocUnsafe(4).fill(0);\n\t\t\tconst randomNumber = Math.round(Math.random() * 1000);\n\t\t\tdata.writeInt16BE(randomNumber, 0);\n\n\t\t\tawait reader.write(4, data);\n\n\t\t\tpretty.info(`data written`, reader, randomNumber, data);\n\n\t\t} catch (err) {\n\t\t\tpretty.error(`error when writing data`, reader, err);\n\t\t}\n\n\n\t});\n\n\treader.on('error', err => {\n\t\tpretty.error(`an error occurred`, reader, err);\n\t});\n\n\treader.on('end', () => {\n\t\tpretty.info(`device removed`, reader);\n\t});\n\n\n});\n\nnfc.on('error', err => {\n\tpretty.error(`an error occurred`, err);\n});\n"
  },
  {
    "path": "examples/uid-logger.js",
    "content": "\"use strict\";\n\n// #############\n// Logs cards' uid\n// #############\n\nimport { NFC } from '../src/index';\n\n\nconst nfc = new NFC();\n\nnfc.on('reader', reader => {\n\n\tconsole.log(reader.name + ' reader attached, waiting for cards ...');\n\n\treader.on('card', card => {\n\t\tconsole.log(card.uid);\n\t});\n\n\treader.on('error', err => {\n\t\tconsole.error('reader error', err);\n\t});\n\n\treader.on('end', () => {\n\t\tconsole.log(reader.name + ' reader disconnected.');\n\t});\n\n\n});\n\nnfc.on('error', err => {\n\tconsole.error(err);\n});\n"
  },
  {
    "path": "examples/utils.js",
    "content": "\"use strict\";\n\n\n// TODO: cover with tests\n\nexport const isDefined = value => value !== null && value !== undefined;\n\nexport const crop = (string, length, align = 1) => {\n\n\tif (string.length <= length) {\n\t\treturn string;\n\t}\n\n\tif (align === 1 || align === 2) {\n\t\treturn string.slice(length - string.length);\n\t}\n\n\tif (align === 3) {\n\t\treturn string.slice(0, -length - string.length);\n\t}\n\n};\n\n/**\n * Prefixes/suffixes the given string with the given prefix (defaults to '0') to match the given length\n * e.g. '789', length 8 => '00000789'\n * e.g. 'hi', length 5 => '000hi'\n * @param string String\n * @param length Integer desired length\n * @param prefix String string to use as prefix/suffix/padding\n * @param align 1 left / 2 center / 3 right\n * @param finalize function to finalize the padded string\n *                 defaults to crop if desired length is exceeded\n *                 – e.g. when using 2-char or more prefix or center align\n * @return String\n */\nexport const paddy = (string, length, prefix = '0', align = 1, finalize = crop) => {\n\n\tif (string.length >= length) {\n\t\treturn string;\n\t}\n\n\twhile (string.length < length) {\n\t\tstring = (align === 3 || align === 2 ? prefix : '') + string + (align === 1 || align === 2 ? prefix : '');\n\t}\n\n\treturn finalize(string, length, align);\n\n};\n\nexport const column = (value, size, align = 1, paddingLeft = 0, paddingRight = 0) => {\n\n\tconst v = value.toString();\n\tconst s = (isDefined(size) ? size : v.length);\n\n\treturn paddy('', paddingLeft, ' ') + paddy(v, s, ' ', align) + paddy('', paddingRight, ' ');\n\n};\n"
  },
  {
    "path": "examples/without-auto.js",
    "content": "\"use strict\";\n\n// #############\n// Alternative usage\n// - see \"Alternative usage\" section in README for an explanation\n// #############\n\nimport { NFC } from '../src/index';\n\n\nconst nfc = new NFC(); // optionally you can pass logger\n\nnfc.on('reader', reader => {\n\n\t// disable auto processing\n\treader.autoProcessing = false;\n\n\tconsole.log(`${reader.reader.name}  device attached`);\n\n\treader.on('card', card => {\n\n\t\t// card is object containing following data\n\t\t// String standard: TAG_ISO_14443_3 (standard nfc tags like MIFARE) or TAG_ISO_14443_4 (Android HCE and others)\n\t\t// String type: same as standard\n\t\t// Buffer atr\n\n\t\tconsole.log(`${reader.reader.name}  card inserted`, card);\n\n\t\t// you can use reader.transmit to send commands and retrieve data\n\t\t// see https://github.com/pokusew/nfc-pcsc/blob/master/src/Reader.js#L288\n\n\t});\n\n\treader.on('card.off', card => {\n\t\tconsole.log(`${reader.reader.name}  card removed`, card);\n\t});\n\n\treader.on('error', err => {\n\t\tconsole.log(`${reader.reader.name}  an error occurred`, err);\n\t});\n\n\treader.on('end', () => {\n\t\tconsole.log(`${reader.reader.name}  device removed`);\n\t});\n\n});\n\nnfc.on('error', err => {\n\tconsole.log('an error occurred', err);\n});\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"nfc-pcsc\",\n  \"version\": \"0.8.1\",\n  \"description\": \"Easy reading and writing NFC tags and cards\",\n  \"keywords\": [\n    \"arc122\",\n    \"card\",\n    \"desfire\",\n    \"mifare\",\n    \"ndef\",\n    \"nfc\",\n    \"pcsc\",\n    \"pcsclite\",\n    \"tag\",\n    \"ultralight\"\n  ],\n  \"homepage\": \"https://github.com/pokusew/nfc-pcsc#readme\",\n  \"bugs\": {\n    \"url\": \"https://github.com/pokusew/nfc-pcsc/issues\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/pokusew/nfc-pcsc.git\"\n  },\n  \"license\": \"MIT\",\n  \"author\": {\n    \"name\": \"Martin Endler\",\n    \"url\": \"https://github.com/pokusew\"\n  },\n  \"contributors\": [\n    {\n      \"name\": \"foxxyz\",\n      \"url\": \"https://github.com/foxxyz\"\n    }\n  ],\n  \"main\": \"dist/index.js\",\n  \"scripts\": {\n    \"build\": \"babel src --out-dir dist\",\n    \"example\": \"node -r @babel/register examples/read-write.js\",\n    \"example-basic\": \"node -r @babel/register examples/basic.js\",\n    \"example-from-readme-3\": \"node -r @babel/register examples/from-readme-3.js\",\n    \"example-led\": \"node -r @babel/register examples/led.js\",\n    \"example-mifare-classic\": \"node -r @babel/register examples/mifare-classic.js\",\n    \"example-mifare-desfire\": \"node -r @babel/register examples/mifare-desfire.js\",\n    \"example-mifare-ultralight-c\": \"node -r @babel/register examples/mifare-ultralight-c.js\",\n    \"example-mifare-ultralight-ntag\": \"node -r @babel/register examples/mifare-ultralight-ntag.js\",\n    \"example-ndef\": \"node -r @babel/register examples/ndef.js\",\n    \"example-uid-logger\": \"node -r @babel/register examples/uid-logger.js\",\n    \"example-without-auto\": \"node -r @babel/register examples/without-auto.js\",\n    \"prepack\": \"yarn build && yarn test\",\n    \"test\": \"cross-env NODE_ENV=test ava test/tests.js --verbose\"\n  },\n  \"dependencies\": {\n    \"@pokusew/pcsclite\": \"^0.6.0\"\n  },\n  \"devDependencies\": {\n    \"@babel/cli\": \"^7.26.4\",\n    \"@babel/core\": \"^7.26.0\",\n    \"@babel/plugin-syntax-object-rest-spread\": \"^7.8.3\",\n    \"@babel/plugin-transform-class-properties\": \"^7.25.9\",\n    \"@babel/plugin-transform-modules-commonjs\": \"^7.26.3\",\n    \"@babel/register\": \"^7.25.9\",\n    \"ava\": \"^6.2.0\",\n    \"chalk\": \"^4.0.0\",\n    \"cross-env\": \"^7.0.3\",\n    \"mock-require\": \"^3.0.3\",\n    \"triple-beam\": \"^1.4.1\",\n    \"winston\": \"^3.17.0\"\n  }\n}\n"
  },
  {
    "path": "src/ACR122Reader.js",
    "content": "\"use strict\";\n\nimport Reader from './Reader';\nimport {\n\tConnectError,\n\tDisconnectError,\n\tTransmitError,\n\tControlError,\n\tAuthenticationError,\n\tLoadAuthenticationKeyError,\n\tReadError,\n\tWriteError,\n\tGetUIDError,\n\tCARD_NOT_CONNECTED,\n\tOPERATION_FAILED,\n\tUNKNOWN_ERROR,\n\tFAILURE,\n} from './errors';\n\n\nclass ACR122Reader extends Reader {\n\n\tasync inAutoPoll() {\n\n\t\tconst payload = [\n\t\t\t0xD4,\n\t\t\t0x60,\n\t\t\t0xFF, // PollNr (0xFF = Endless polling)\n\t\t\t0x01, // Period (0x01 – 0x0F) indicates the polling period in units of 150 ms\n\t\t\t0x00, // Type 1 0x00 = Generic passive 106 kbps (ISO/IEC14443-4A, Mifare and DEP)\n\t\t];\n\n\t\t// CMD: Direct Transmit (to inner PN532 chip InAutoPoll CMD)\n\t\tconst packet = Buffer.from([\n\t\t\t0xff, // Class\n\t\t\t0x00, // INS\n\t\t\t0x00, // P1\n\t\t\t0x00, // P2\n\t\t\tpayload.length, // Lc: Number of Bytes to send (Maximum 255 bytes)\n\t\t\t...payload,\n\t\t]);\n\n\t\tconsole.log(packet);\n\n\t\tlet response = null;\n\n\t\ttry {\n\n\t\t\tresponse = await this.control(packet, 2);\n\n\t\t\tthis.logger.debug('response received', response);\n\n\t\t\t// Red OFF Green OFF  0x00\n\t\t\t// Red ON  Green OFF  0x01\n\t\t\t// Red OFF Green ON   0x02\n\t\t\t// Red ON  Green ON   0x03\n\n\t\t\tconsole.log(response.slice(1));\n\n\n\t\t} catch (err) {\n\n\t\t\tthrow err;\n\n\t\t}\n\n\t\t// const statusCode = response.readUInt16BE(0);\n\t\t//\n\t\t// if (statusCode !== 0x9000) {\n\t\t// \t//throw new LoadAuthenticationKeyError(OPERATION_FAILED, `Load authentication key operation failed: Status code: ${statusCode}`);\n\t\t// }\n\n\t}\n\n\tasync led(led, blinking) {\n\n\t\t// P2: LED State Control (1 byte = 8 bits)\n\t\t// format:\n\t\t/*\n\t\t +-----+----------------------------------+-------------------------------------+\n\t\t | Bit |               Item               |             Description             |\n\t\t +-----+----------------------------------+-------------------------------------+\n\t\t |   0 | Final Red LED State              | 1 = On; 0 = Off                     |\n\t\t |   1 | Final Green LED State            | 1 = On; 0 = Off                     |\n\t\t |   2 | Red LED State Mask               | 1 = Update the State; 0 = No change |\n\t\t |   3 | Green LED State Mask             | 1 = Update the State; 0 = No change |\n\t\t |   4 | Initial Red LED Blinking State   | 1 = On; 0 = Off                     |\n\t\t |   5 | Initial Green LED Blinking State | 1 = On; 0 = Off                     |\n\t\t |   6 | Red LED Blinking Mask            | 1 = Blink; 0 = Not Blink            |\n\t\t |   7 | Green LED Blinking Mask          | 1 = Blink; 0 = Not Blink            |\n\t\t +-----+----------------------------------+-------------------------------------+\n\t\t */\n\n\t\t//const led = 0b00001111;\n\t\t//const led = 0x50;\n\n\t\t// Data In: Blinking Duration Control (4 bytes)\n\t\t// Byte 0: T1 Duration Initial Blinking State (Unit = 100 ms)\n\t\t// Byte 1: T2 Duration Toggle Blinking State (Unit = 100 ms)\n\t\t// Byte 2: Number of repetition\n\t\t// Byte 3: Link to Buzzer\n\t\t// - 00: The buzzer will not turn on\n\t\t// - 01: The buzzer will turn on during the T1 Duration\n\t\t// - 02: The buzzer will turn on during the T2 Duration\n\t\t// - 03: The buzzer will turn on during the T1 and T2 Duration\n\n\t\t// const blinking = [\n\t\t// \t0x00,\n\t\t// \t0x00,\n\t\t// \t0x00,\n\t\t// \t0x00\n\t\t// ];\n\n\n\t\t// CMD: Bi-Color LED and Buzzer Control\n\t\tconst packet = Buffer.from([\n\t\t\t0xff, // Class\n\t\t\t0x00, // INS\n\t\t\t0x40, // P1\n\t\t\tled, // P2: LED State Control\n\t\t\t0x04, // Lc\n\t\t\t...blinking, // Data In: Blinking Duration Control (4 bytes)\n\t\t]);\n\n\t\tconsole.log(packet);\n\n\t\tlet response = null;\n\n\t\ttry {\n\n\t\t\tresponse = await this.control(packet, 2);\n\n\t\t\tthis.logger.debug('response received', response);\n\n\t\t\t// Red OFF Green OFF  0x00\n\t\t\t// Red ON  Green OFF  0x01\n\t\t\t// Red OFF Green ON   0x02\n\t\t\t// Red ON  Green ON   0x03\n\n\t\t\tconsole.log(response.slice(1));\n\n\n\t\t} catch (err) {\n\n\t\t\tthrow err;\n\n\t\t}\n\n\t\t// const statusCode = response.readUInt16BE(0);\n\t\t//\n\t\t// if (statusCode !== 0x9000) {\n\t\t// \t//throw new LoadAuthenticationKeyError(OPERATION_FAILED, `Load authentication key operation failed: Status code: ${statusCode}`);\n\t\t// }\n\n\t}\n\n\tasync setBuzzerOutput(enabled = true) {\n\n\n\t\t// CMD: Set Buzzer Output Enable for Card Detection\n\t\tconst packet = Buffer.from([\n\t\t\t0xff, // Class\n\t\t\t0x00, // INS\n\t\t\t0x52, // P1\n\t\t\tenabled ? 0xff : 0x00, // P2: PollBuzzStatus\n\t\t\t0x00, // Le\n\t\t]);\n\n\t\tconsole.log(packet);\n\n\t\tlet response = null;\n\n\t\ttry {\n\n\t\t\tresponse = await this.control(packet, 2);\n\n\t\t\tthis.logger.debug('response received', response);\n\n\n\t\t} catch (err) {\n\n\t\t\tthrow err;\n\n\t\t}\n\n\t\tconst statusCode = response.readUInt16BE(0);\n\n\t\tif (statusCode !== 0x9000) {\n\t\t\t//throw new LoadAuthenticationKeyError(OPERATION_FAILED, `Load authentication key operation failed: Status code: ${statusCode}`);\n\t\t}\n\n\t}\n\n\tasync setPICC(picc) {\n\n\t\t// just enable Auto ATS Generation\n\t\t// const picc = 0b01000000;\n\n\t\t// CMD: Set PICC Operating Parameter\n\t\tconst packet = Buffer.from([\n\t\t\t0xff, // Class\n\t\t\t0x00, // INS\n\t\t\t0x51, // P1\n\t\t\tpicc, // P2: New PICC Operating Parameter\n\t\t\t0x00, // Le\n\t\t]);\n\n\t\tconsole.log(packet);\n\n\t\tlet response = null;\n\n\t\ttry {\n\n\t\t\tresponse = await this.control(packet, 1);\n\n\t\t\tthis.logger.debug('response received', response);\n\n\n\t\t} catch (err) {\n\n\t\t\tthrow err;\n\n\t\t}\n\n\t\t// const statusCode = response.readUInt16BE(0);\n\t\t//\n\t\t// if (statusCode !== 0x9000) {\n\t\t// \t//throw new LoadAuthenticationKeyError(OPERATION_FAILED, `Load authentication key operation failed: Status code: ${statusCode}`);\n\t\t// }\n\n\t}\n\n}\n\nexport default ACR122Reader;\n"
  },
  {
    "path": "src/NFC.js",
    "content": "\"use strict\";\n\nimport pcsclite from '@pokusew/pcsclite';\nimport EventEmitter from 'events';\nimport Reader from './Reader';\nimport ACR122Reader from './ACR122Reader';\n\n\nclass NFC extends EventEmitter {\n\n\tpcsc = null;\n\tlogger = null;\n\n\tconstructor(logger) {\n\t\tsuper();\n\n\t\tthis.pcsc = pcsclite();\n\n\t\tif (logger) {\n\t\t\tthis.logger = logger;\n\t\t}\n\t\telse {\n\t\t\tthis.logger = {\n\t\t\t\tlog: function () {\n\t\t\t\t},\n\t\t\t\tdebug: function () {\n\t\t\t\t},\n\t\t\t\tinfo: function () {\n\t\t\t\t},\n\t\t\t\twarn: function () {\n\t\t\t\t},\n\t\t\t\terror: function () {\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tthis.pcsc.on('reader', (reader) => {\n\n\t\t\tthis.logger.debug('new reader detected', reader.name);\n\n\t\t\t// create special object for ARC122U reader with commands specific to this reader\n\t\t\tif (\n\n\t\t\t\t// 'acr122' matches ARC122U\n\t\t\t\treader.name.toLowerCase().indexOf('acr122') !== -1\n\n\t\t\t\t// 'acr125' matches ACR1252U reader because ACR1252U has some common commands with ARC122U\n\t\t\t\t//   ACR1252U product page: https://www.acs.com.hk/en/products/342/acr1252u-usb-nfc-reader-iii-nfc-forum-certified-reader/\n\t\t\t\t//   TODO: in the future, this should be refactored:\n\t\t\t\t//         see discussion in PR#111 https://github.com/pokusew/nfc-pcsc/pull/111\n\t\t\t\t|| reader.name.toLowerCase().indexOf('acr125') !== -1\n\n\t\t\t) {\n\n\t\t\t\tconst device = new ACR122Reader(reader, this.logger);\n\n\t\t\t\tthis.emit('reader', device);\n\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\tconst device = new Reader(reader, this.logger);\n\n\t\t\tthis.emit('reader', device);\n\n\t\t});\n\n\t\tthis.pcsc.on('error', (err) => {\n\n\t\t\tthis.logger.error('PCSC error', err.message);\n\n\t\t\tthis.emit('error', err);\n\n\t\t});\n\n\t}\n\n\tget readers() {\n\t\treturn this.pcsc.readers;\n\t}\n\n\tclose() {\n\t\tthis.pcsc.close();\n\t}\n\n}\n\nexport default NFC;\n"
  },
  {
    "path": "src/Reader.js",
    "content": "\"use strict\";\n\nimport EventEmitter from 'events';\nimport {\n\tConnectError,\n\tDisconnectError,\n\tTransmitError,\n\tControlError,\n\tAuthenticationError,\n\tLoadAuthenticationKeyError,\n\tReadError,\n\tWriteError,\n\tGetUIDError,\n\tCARD_NOT_CONNECTED,\n\tOPERATION_FAILED,\n\tUNKNOWN_ERROR,\n\tFAILURE,\n} from './errors';\n\n\nexport const TAG_ISO_14443_3 = 'TAG_ISO_14443_3'; // ISO/IEC 14443-3 tags\nexport const TAG_ISO_14443_4 = 'TAG_ISO_14443_4'; // ISO/IEC 14443-4 tags\n\nexport const KEY_TYPE_A = 0x60;\nexport const KEY_TYPE_B = 0x61;\n\nexport const CONNECT_MODE_DIRECT = 'CONNECT_MODE_DIRECT';\nexport const CONNECT_MODE_CARD = 'CONNECT_MODE_CARD';\n\n\nclass Reader extends EventEmitter {\n\n\treader = null;\n\tlogger = null;\n\n\tconnection = null;\n\tcard = null;\n\n\tautoProcessing = true;\n\t_aid = null;\n\n\tkeyStorage = {\n\t\t'0': null,\n\t\t'1': null,\n\t};\n\n\tpendingLoadAuthenticationKey = {};\n\n\t/**\n\t * Reverses a copy of a given buffer\n\t * Does NOT mutate the given buffer, returns a reversed COPY\n\t * For mutating reverse use native .reverse() method on a buffer instance\n\t * @param src {Buffer} a Buffer instance\n\t * @returns {Buffer}\n\t */\n\tstatic reverseBuffer(src) {\n\n\t\tconst buffer = Buffer.allocUnsafe(src.length);\n\n\t\tfor (let i = 0, j = src.length - 1; i <= j; ++i, --j) {\n\t\t\tbuffer[i] = src[j];\n\t\t\tbuffer[j] = src[i];\n\t\t}\n\n\t\treturn buffer;\n\n\t}\n\n\tstatic selectStandardByAtr(atr) {\n\n\t\t// TODO: better detecting card types\n\t\tif (atr[5] && atr[5] === 0x4f) {\n\t\t\treturn TAG_ISO_14443_3;\n\t\t}\n\t\telse {\n\t\t\treturn TAG_ISO_14443_4;\n\t\t}\n\n\t}\n\n\tget aid() {\n\t\treturn this._aid;\n\t}\n\n\tset aid(value) {\n\n\t\tif (typeof value === 'function' || Buffer.isBuffer(value)) {\n\t\t\tthis._aid = value;\n\t\t\treturn;\n\t\t}\n\n\t\tif (typeof value !== 'string') {\n\t\t\tthrow new Error(`AID must be a HEX string or an instance of Buffer or a function.`);\n\t\t}\n\n\t\tthis._aid = Buffer.from(value, 'hex');\n\n\t}\n\n\tget name() {\n\t\treturn this.reader.name;\n\t}\n\n\tconstructor(reader, logger) {\n\n\t\tsuper();\n\n\t\tthis.reader = reader;\n\n\t\tif (logger) {\n\t\t\tthis.logger = logger;\n\t\t}\n\t\telse {\n\t\t\tthis.logger = {\n\t\t\t\tlog: function () {\n\t\t\t\t},\n\t\t\t\tdebug: function () {\n\t\t\t\t},\n\t\t\t\tinfo: function () {\n\t\t\t\t},\n\t\t\t\twarn: function () {\n\t\t\t\t},\n\t\t\t\terror: function () {\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tthis.reader.on('error', (err) => {\n\n\t\t\tthis.logger.error(err);\n\n\t\t\tthis.emit('error', err);\n\n\t\t});\n\n\t\tthis.reader.on('status', async status => {\n\n\t\t\tthis.logger.debug('status', status);\n\n\t\t\t// check what has changed\n\t\t\tconst changes = this.reader.state ^ status.state;\n\n\t\t\tthis.logger.debug('changes', changes);\n\n\t\t\tif (changes) {\n\n\t\t\t\tif ((changes & this.reader.SCARD_STATE_EMPTY) && (status.state & this.reader.SCARD_STATE_EMPTY)) {\n\n\t\t\t\t\tthis.logger.debug('card removed');\n\n\t\t\t\t\tif (this.card) {\n\t\t\t\t\t\tthis.emit('card.off', { ...this.card });\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\n\t\t\t\t\t\tthis.card = null;\n\t\t\t\t\t\tif (this.connection) {\n\t\t\t\t\t\t\tawait this.disconnect();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t} catch (err) {\n\n\t\t\t\t\t\tthis.emit(err);\n\n\t\t\t\t\t}\n\n\t\t\t\t}\n\t\t\t\telse if ((changes & this.reader.SCARD_STATE_PRESENT) && (status.state & this.reader.SCARD_STATE_PRESENT)) {\n\n\t\t\t\t\tconst atr = status.atr;\n\n\t\t\t\t\tthis.logger.debug('card inserted', atr);\n\n\t\t\t\t\tthis.card = {};\n\n\t\t\t\t\tif (atr) {\n\t\t\t\t\t\tthis.card.atr = atr;\n\t\t\t\t\t\tthis.card.standard = Reader.selectStandardByAtr(atr);\n\t\t\t\t\t\tthis.card.type = this.card.standard;\n\t\t\t\t\t}\n\n\t\t\t\t\ttry {\n\n\t\t\t\t\t\tawait this.connect();\n\n\t\t\t\t\t\tif (!this.autoProcessing) {\n\t\t\t\t\t\t\tthis.emit('card', { ...this.card });\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tthis.handleTag();\n\n\t\t\t\t\t} catch (err) {\n\n\t\t\t\t\t\tthis.emit(err);\n\n\t\t\t\t\t}\n\n\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tthis.reader.on('end', () => {\n\n\t\t\tthis.logger.debug('reader removed');\n\n\t\t\tthis.emit('end');\n\n\t\t});\n\n\t}\n\n\tconnect(mode = CONNECT_MODE_CARD) {\n\n\t\tconst modes = {\n\t\t\t[CONNECT_MODE_DIRECT]: this.reader.SCARD_SHARE_DIRECT,\n\t\t\t[CONNECT_MODE_CARD]: this.reader.SCARD_SHARE_SHARED,\n\t\t};\n\n\t\tif (!modes[mode]) {\n\t\t\tthrow new ConnectError('invalid_mode', 'Invalid mode')\n\t\t}\n\n\t\tthis.logger.debug('trying to connect', mode, modes[mode]);\n\n\t\treturn new Promise((resolve, reject) => {\n\n\t\t\t// connect card\n\t\t\tthis.reader.connect({\n\t\t\t\tshare_mode: modes[mode],\n\t\t\t\t//protocol: this.reader.SCARD_PROTOCOL_UNDEFINED\n\t\t\t}, (err, protocol) => {\n\n\t\t\t\tif (err) {\n\t\t\t\t\tconst error = new ConnectError(FAILURE, 'An error occurred while connecting.', err);\n\t\t\t\t\tthis.logger.error(error);\n\t\t\t\t\treturn reject(error);\n\t\t\t\t}\n\n\t\t\t\tthis.connection = {\n\t\t\t\t\ttype: modes[mode],\n\t\t\t\t\tprotocol: protocol,\n\t\t\t\t};\n\n\t\t\t\tthis.logger.debug('connected', this.connection);\n\n\t\t\t\treturn resolve(this.connection);\n\n\t\t\t});\n\n\t\t});\n\n\t}\n\n\tdisconnect() {\n\n\t\tif (!this.connection) {\n\t\t\tthrow new DisconnectError('not_connected', 'Reader in not connected. No need for disconnecting.')\n\t\t}\n\n\t\tthis.logger.debug('trying to disconnect', this.connection);\n\n\t\treturn new Promise((resolve, reject) => {\n\n\t\t\t// disconnect removed\n\t\t\tthis.reader.disconnect(this.reader.SCARD_LEAVE_CARD, (err) => {\n\n\t\t\t\tif (err) {\n\t\t\t\t\tconst error = new DisconnectError(FAILURE, 'An error occurred while disconnecting.', err);\n\t\t\t\t\tthis.logger.error(error);\n\t\t\t\t\treturn reject(error);\n\t\t\t\t}\n\n\t\t\t\tthis.connection = null;\n\n\t\t\t\tthis.logger.debug('disconnected');\n\n\t\t\t\treturn resolve(true);\n\n\t\t\t});\n\n\t\t});\n\n\t}\n\n\ttransmit(data, responseMaxLength) {\n\n\t\tif (!this.card || !this.connection) {\n\t\t\tthrow new TransmitError(CARD_NOT_CONNECTED, 'No card or connection available.');\n\t\t}\n\n\t\treturn new Promise((resolve, reject) => {\n\n\t\t\tthis.logger.debug('transmitting', data, responseMaxLength);\n\n\t\t\tthis.reader.transmit(data, responseMaxLength, this.connection.protocol, (err, response) => {\n\n\t\t\t\tif (err) {\n\t\t\t\t\tconst error = new TransmitError(FAILURE, 'An error occurred while transmitting.', err);\n\t\t\t\t\treturn reject(error);\n\t\t\t\t}\n\n\t\t\t\tthis.logger.debug('transmit response received', response, response && response.length);\n\n\t\t\t\treturn resolve(response);\n\n\t\t\t});\n\n\t\t});\n\n\t}\n\n\tcontrol(data, responseMaxLength) {\n\n\t\tif (!this.connection) {\n\t\t\tthrow new ControlError('not_connected', 'No connection available.');\n\t\t}\n\n\t\treturn new Promise((resolve, reject) => {\n\n\t\t\tthis.logger.debug('transmitting control', data, responseMaxLength);\n\n\t\t\tthis.reader.control(data, this.reader.IOCTL_CCID_ESCAPE, responseMaxLength, (err, response) => {\n\n\t\t\t\tif (err) {\n\t\t\t\t\tconst error = new ControlError(FAILURE, 'An error occurred while transmitting control.', err);\n\t\t\t\t\treturn reject(error);\n\t\t\t\t}\n\n\t\t\t\tthis.logger.debug('control response received', response, response && response.length);\n\n\t\t\t\treturn resolve(response);\n\n\t\t\t});\n\n\t\t});\n\n\t}\n\n\tasync loadAuthenticationKey(keyNumber, key) {\n\n\t\tif (!(keyNumber === 0 || keyNumber === 1)) {\n\t\t\tthrow new LoadAuthenticationKeyError('invalid_key_number');\n\t\t}\n\n\t\tif (!Buffer.isBuffer(key) && !Array.isArray(key)) {\n\n\t\t\tif (typeof key !== 'string') {\n\t\t\t\tthrow new LoadAuthenticationKeyError(\n\t\t\t\t\t'invalid_key',\n\t\t\t\t\t'Key must an instance of Buffer or an array of bytes or a string.',\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tkey = Buffer.from(key, 'hex');\n\n\t\t}\n\n\t\tif (key.length !== 6) {\n\t\t\tthrow new LoadAuthenticationKeyError('invalid_key', 'Key length must be 6 bytes.');\n\t\t}\n\n\t\t// CMD: Load Authentication Keys\n\t\tconst packet = Buffer.from([\n\t\t\t0xff, // Class\n\t\t\t0x82, // INS\n\t\t\t0x00, // P1: Key Structure (0x00 = Key is loaded into the reader volatile memory.)\n\t\t\tkeyNumber, // P2: Key Number (00h ~ 01h = Key Location. The keys will disappear once the reader is disconnected from the PC)\n\t\t\tkey.length, // Lc: Length of the key (6)\n\t\t\t...key, // Data In: Key (6 bytes)\n\t\t]);\n\n\t\tlet response = null;\n\n\t\ttry {\n\n\t\t\tresponse = await this.transmit(packet, 2);\n\n\n\t\t} catch (err) {\n\n\t\t\tthrow new LoadAuthenticationKeyError(null, null, err);\n\n\t\t}\n\n\t\tconst statusCode = response.readUInt16BE(0);\n\n\t\tif (statusCode !== 0x9000) {\n\t\t\tthrow new LoadAuthenticationKeyError(OPERATION_FAILED, `Load authentication key operation failed: Status code: ${statusCode}`);\n\t\t}\n\n\t\tthis.keyStorage[keyNumber] = key;\n\n\t\treturn keyNumber;\n\n\t}\n\n\t// for PC/SC V2.01 use obsolete = true\n\t// for PC/SC V2.07 use obsolete = false [default]\n\tasync authenticate(blockNumber, keyType, key, obsolete = false) {\n\n\t\tlet keyNumber = Object.keys(this.keyStorage).find(n => this.keyStorage[n] === key);\n\n\t\t// key is not in the storage\n\t\tif (!keyNumber) {\n\n\t\t\t// If there isn't already an authentication process happening for this key, start it\n\t\t\tif (!this.pendingLoadAuthenticationKey[key]) {\n\n\t\t\t\t// set key number to first\n\t\t\t\tkeyNumber = Object.keys(this.keyStorage)[0];\n\n\t\t\t\t// if this number is not free\n\t\t\t\tif (this.keyStorage[keyNumber] !== null) {\n\t\t\t\t\t// try to find any free number\n\t\t\t\t\tconst freeNumber = Object.keys(this.keyStorage).find(n => this.keyStorage[n] === null);\n\t\t\t\t\t// if we find, we use it, otherwise the first will be used and rewritten\n\t\t\t\t\tif (freeNumber) {\n\t\t\t\t\t\tkeyNumber = freeNumber;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Store the authentication promise in case other blocks are in process of authentication\n\t\t\t\tthis.pendingLoadAuthenticationKey[key] = this.loadAuthenticationKey(parseInt(keyNumber), key);\n\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tkeyNumber = await this.pendingLoadAuthenticationKey[key];\n\t\t\t} catch (err) {\n\t\t\t\tthrow new AuthenticationError('unable_to_load_key', 'Could not load authentication key into reader.', err);\n\t\t\t} finally {\n\t\t\t\t// remove the loadAuthenticationKey Promise from pendingLoadAuthenticationKey\n\t\t\t\t// as it is already resolved or rejected at this point\n\t\t\t\tdelete this.pendingLoadAuthenticationKey[key];\n\t\t\t}\n\n\t\t}\n\n\t\tconst packet = !obsolete ? (\n\t\t\t// CMD: Authentication\n\t\t\tBuffer.from([\n\t\t\t\t0xff, // Class\n\t\t\t\t0x86, // INS\n\t\t\t\t0x00, // P1\n\t\t\t\t0x00, // P2\n\t\t\t\t0x05, // Lc\n\t\t\t\t// Data In: Authenticate Data Bytes (5 bytes)\n\t\t\t\t0x01, // Byte 1: Version\n\t\t\t\t0x00, // Byte 2\n\t\t\t\tblockNumber, // Byte 3: Block Number\n\t\t\t\tkeyType, // Byte 4: Key Type\n\t\t\t\tkeyNumber, // Byte 5: Key Number\n\t\t\t])\n\t\t) : (\n\t\t\t// CMD: Authentication (obsolete)\n\t\t\tBuffer.from([\n\t\t\t\t0xff, // Class\n\t\t\t\t0x88, // INS\n\t\t\t\t0x00, // P1\n\t\t\t\tblockNumber, // P2: Block Number\n\t\t\t\tkeyType, // P3: Key Type\n\t\t\t\tkeyNumber, // Data In: Key Number\n\t\t\t])\n\t\t);\n\n\t\tlet response = null;\n\n\t\ttry {\n\n\t\t\tresponse = await this.transmit(packet, 2);\n\n\t\t} catch (err) {\n\n\t\t\tthrow new AuthenticationError(null, null, err);\n\n\t\t}\n\n\t\tconst statusCode = response.readUInt16BE(0);\n\n\t\tif (statusCode !== 0x9000) {\n\t\t\tthis.logger.error('[authentication operation failed][request packet]', packet);\n\t\t\tthrow new AuthenticationError(OPERATION_FAILED, `Authentication operation failed: Status code: 0x${statusCode.toString(16)}`);\n\t\t}\n\n\t\treturn true;\n\n\t}\n\n\tasync read(blockNumber, length, blockSize = 4, packetSize = 16, readClass = 0xff) {\n\n\t\tif (!this.card) {\n\t\t\tthrow new ReadError(CARD_NOT_CONNECTED);\n\t\t}\n\n\t\tthis.logger.debug('reading data from card', this.card);\n\n\t\tif (length > packetSize) {\n\n\t\t\tconst p = Math.ceil(length / packetSize);\n\n\t\t\tconst commands = [];\n\n\t\t\tfor (let i = 0; i < p; i++) {\n\n\t\t\t\tconst block = blockNumber + ((i * packetSize) / blockSize);\n\n\t\t\t\tconst size = ((i + 1) * packetSize) < length ? packetSize : length - ((i) * packetSize);\n\n\t\t\t\t// console.log(i, block, size);\n\n\t\t\t\tcommands.push(this.read(block, size, blockSize, packetSize, readClass));\n\n\t\t\t}\n\n\t\t\treturn Promise.all(commands)\n\t\t\t\t.then(values => {\n\t\t\t\t\t// console.log(values);\n\t\t\t\t\treturn Buffer.concat(values, length);\n\t\t\t\t});\n\n\t\t}\n\n\t\t// APDU CMD: Read Binary Blocks\n\t\tconst packet = Buffer.from([\n\t\t\treadClass, // Class\n\t\t\t0xb0, // Ins\n\t\t\t(blockNumber >> 8) & 0xFF, // P1\n\t\t\tblockNumber & 0xFF, // P2: Block Number\n\t\t\tlength,  // Le: Number of Bytes to Read (Maximum 16 bytes)\n\t\t]);\n\n\t\tlet response = null;\n\n\t\ttry {\n\n\t\t\tresponse = await this.transmit(packet, length + 2);\n\n\t\t} catch (err) {\n\n\t\t\tthrow new ReadError(null, null, err);\n\n\t\t}\n\n\t\tif (response.length < 2) {\n\t\t\tthrow new ReadError(OPERATION_FAILED, `Read operation failed: Invalid response length ${response.length}. Expected minimal length is 2 bytes.`);\n\t\t}\n\n\t\tconst statusCode = response.slice(-2).readUInt16BE(0);\n\n\t\tif (statusCode !== 0x9000) {\n\t\t\tthrow new ReadError(OPERATION_FAILED, `Read operation failed: Status code: 0x${statusCode.toString(16)}`);\n\t\t}\n\n\t\tconst data = response.slice(0, -2);\n\n\t\tthis.logger.debug('data', data);\n\n\t\treturn data;\n\n\t}\n\n\tasync write(blockNumber, data, blockSize = 4) {\n\n\t\tif (!this.card) {\n\t\t\tthrow new WriteError(CARD_NOT_CONNECTED);\n\t\t}\n\n\t\tthis.logger.debug('writing data to card', this.card);\n\n\t\tif (data.length < blockSize || data.length % blockSize !== 0) {\n\t\t\tthrow new WriteError('invalid_data_length', 'Invalid data length. You can only update the entire data block(s).');\n\t\t}\n\n\t\tif (data.length > blockSize) {\n\n\t\t\tconst p = data.length / blockSize;\n\n\t\t\tconst commands = [];\n\n\t\t\tfor (let i = 0; i < p; i++) {\n\n\t\t\t\tconst block = blockNumber + i;\n\n\t\t\t\tconst start = i * blockSize;\n\t\t\t\tconst end = (i + 1) * blockSize;\n\n\t\t\t\tconst part = data.slice(start, end);\n\n\t\t\t\t// console.log(i, block, start, end, part);\n\n\t\t\t\tcommands.push(this.write(block, part, blockSize));\n\n\t\t\t}\n\n\t\t\treturn Promise.all(commands)\n\t\t\t\t.then(values => {\n\t\t\t\t\t// console.log(values);\n\t\t\t\t\treturn values;\n\t\t\t\t});\n\n\t\t}\n\n\t\t// APDU CMD: Update Binary Block\n\t\tconst packetHeader = Buffer.from([\n\t\t\t0xff, // Class\n\t\t\t0xd6, // Ins\n\t\t\t0x00, // P1\n\t\t\tblockNumber, // P2: Block Number\n\t\t\tblockSize, // Le: Number of Bytes to Update\n\t\t]);\n\n\t\tconst packet = Buffer.concat([packetHeader, data]);\n\n\t\tlet response = null;\n\n\t\ttry {\n\n\t\t\tresponse = await this.transmit(packet, 2);\n\n\t\t} catch (err) {\n\n\t\t\tthrow new WriteError(null, null, err);\n\n\t\t}\n\n\t\tif (response.length < 2) {\n\t\t\tthrow new WriteError(OPERATION_FAILED, `Write operation failed: Invalid response length ${response.length}. Expected minimal length is 2 bytes.`);\n\t\t}\n\n\t\tconst statusCode = response.slice(-2).readUInt16BE(0);\n\n\t\tif (statusCode !== 0x9000) {\n\t\t\tthrow new WriteError(OPERATION_FAILED, `Write operation failed: Status code: 0x${statusCode.toString(16)}`);\n\t\t}\n\n\t\treturn true;\n\n\t}\n\n\thandleTag() {\n\n\t\tif (!this.card) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.logger.debug('handling tag', this.card);\n\n\t\tswitch (this.card.standard) {\n\n\t\t\tcase TAG_ISO_14443_3:\n\t\t\t\treturn this.handle_Iso_14443_3_Tag();\n\n\t\t\tcase TAG_ISO_14443_4:\n\t\t\t\treturn this.handle_Iso_14443_4_Tag();\n\n\t\t\tdefault:\n\t\t\t\treturn this.handle_Iso_14443_3_Tag();\n\n\t\t}\n\n\t}\n\n\t// TODO: improve error handling and debugging\n\tasync handle_Iso_14443_3_Tag() {\n\n\t\tif (!this.card || !this.connection) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.logger.debug('processing ISO 14443-3 tag', this.card);\n\n\t\t// APDU CMD: Get Data\n\t\tconst packet = Buffer.from([\n\t\t\t0xff, // Class\n\t\t\t0xca, // INS\n\t\t\t0x00, // P1: Get current card UID\n\t\t\t0x00, // P2\n\t\t\t0x00, // Le: Full Length of UID\n\t\t]);\n\n\t\ttry {\n\n\t\t\tconst response = await this.transmit(packet, 12);\n\n\t\t\tif (response.length < 2) {\n\n\t\t\t\tconst error = new GetUIDError('invalid_response', `Invalid response length ${response.length}. Expected minimal length is 2 bytes.`);\n\t\t\t\tthis.emit('error', error);\n\n\t\t\t\treturn;\n\n\t\t\t}\n\n\t\t\t// last 2 bytes are the status code\n\t\t\tconst statusCode = response.slice(-2).readUInt16BE(0);\n\n\t\t\t// an error occurred\n\t\t\tif (statusCode !== 0x9000) {\n\n\t\t\t\tconst error = new GetUIDError(OPERATION_FAILED, 'Could not get card UID.');\n\t\t\t\tthis.emit('error', error);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// strip out the status code (the rest is UID)\n\t\t\tconst uid = response.slice(0, -2).toString('hex');\n\t\t\t// const uidReverse = Reader.reverseBuffer(response.slice(0, -2)).toString('hex');\n\n\t\t\tthis.card.uid = uid;\n\n\t\t\tthis.emit('card', { ...this.card });\n\n\n\t\t} catch (err) {\n\n\t\t\tconst error = new GetUIDError(null, null, err);\n\n\t\t\tthis.emit('error', error);\n\n\t\t}\n\n\t}\n\n\t// TODO: improve error handling and debugging\n\tasync handle_Iso_14443_4_Tag() {\n\n\t\tif (!this.card || !this.connection) {\n\t\t\treturn false;\n\t\t}\n\n\t\tthis.logger.debug('processing ISO 14443-4 tag', this.card);\n\n\t\tif (!this.aid) {\n\t\t\tthis.emit('error', new Error('Cannot process ISO 14443-4 tag because AID was not set.'));\n\t\t\treturn;\n\t\t}\n\n\t\tconst aid = typeof this.aid === 'function' ? this.aid(this.card) : this.aid;\n\n\t\tif (!Buffer.isBuffer(aid)) {\n\t\t\tthis.emit('error', new Error('AID must be an instance of Buffer.'));\n\t\t\treturn;\n\t\t}\n\n\t\t// APDU CMD: SELECT FILE\n\t\t// see http://cardwerk.com/smart-card-standard-iso7816-4-section-6-basic-interindustry-commands/#chap6_11_3\n\t\tconst packet = Buffer.from([\n\t\t\t0x00, // Class\n\t\t\t0xa4, // INS\n\t\t\t0x04, // P1\n\t\t\t0x00, // P2\n\t\t\taid.length, // Lc\n\t\t\t...aid, // AID\n\t\t\t0x00, // Le\n\t\t]);\n\n\t\ttry {\n\n\t\t\tconst response = await this.transmit(packet, 40);\n\n\t\t\tif (response.length === 2 && response.readUInt16BE(0) === 0x6a82) {\n\n\t\t\t\tconst err = new Error(`Not found response. Tag not compatible with AID ${aid.toString('hex').toUpperCase()}.`);\n\t\t\t\tthis.emit('error', err);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (response.length < 2) {\n\n\t\t\t\tconst err = new Error(`Invalid response length ${response.length}. Expected minimal length is 2 bytes.`);\n\t\t\t\tthis.emit('error', err);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// another possibility const statusCode = parseInt(response.slice(-2).toString('hex'), 16)\n\t\t\tconst statusCode = response.slice(-2).readUInt16BE(0);\n\n\t\t\t// an error occurred\n\t\t\tif (statusCode !== 0x9000) {\n\n\t\t\t\tconst err = new Error(`Response status error.`);\n\t\t\t\tthis.emit('error', err);\n\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// strip out the status code\n\t\t\tconst data = response.slice(0, -2);\n\n\t\t\tthis.logger.debug('Data cropped', data);\n\n\t\t\tthis.emit('card', {\n\t\t\t\t...this.card,\n\t\t\t\tdata: data,\n\t\t\t});\n\n\t\t} catch (err) {\n\n\t\t\tconst error = new GetUIDError(null, null, err);\n\n\t\t\tthis.emit('error', error);\n\n\t\t}\n\n\t}\n\n\tclose() {\n\t\tthis.reader.close();\n\t}\n\n\ttoString() {\n\t\treturn this.name;\n\t}\n\n}\n\nexport default Reader;\n"
  },
  {
    "path": "src/errors.js",
    "content": "\"use strict\";\n\n\nexport const UNKNOWN_ERROR = 'unknown_error';\n\nexport class BaseError extends Error {\n\n\tconstructor(code, message, previousError) {\n\n\t\tsuper(message);\n\n\t\tError.captureStackTrace(this, this.constructor);\n\n\t\tthis.name = 'BaseError';\n\n\t\tif (!message && previousError) {\n\t\t\tthis.message = previousError.message;\n\t\t}\n\n\t\tthis.code = code;\n\n\t\tif (previousError) {\n\t\t\tthis.previous = previousError;\n\t\t}\n\n\t}\n\n}\n\nexport const FAILURE = 'failure';\nexport const CARD_NOT_CONNECTED = 'card_not_connected';\nexport const OPERATION_FAILED = 'operation_failed';\n\nexport class TransmitError extends BaseError {\n\n\tconstructor(code, message, previousError) {\n\n\t\tsuper(code, message, previousError);\n\n\t\tthis.name = 'TransmitError';\n\n\t}\n\n}\n\nexport class ControlError extends BaseError {\n\n\tconstructor(code, message, previousError) {\n\n\t\tsuper(code, message, previousError);\n\n\t\tthis.name = 'ControlError';\n\n\t}\n\n}\n\nexport class ReadError extends BaseError {\n\n\tconstructor(code, message, previousError) {\n\n\t\tsuper(code, message, previousError);\n\n\t\tthis.name = 'ReadError';\n\n\t}\n\n}\n\nexport class WriteError extends BaseError {\n\n\tconstructor(code, message, previousError) {\n\n\t\tsuper(code, message, previousError);\n\n\t\tthis.name = 'WriteError';\n\n\t}\n\n}\n\nexport class LoadAuthenticationKeyError extends BaseError {\n\n\tconstructor(code, message, previousError) {\n\n\t\tsuper(code, message, previousError);\n\n\t\tthis.name = 'LoadAuthenticationKeyError';\n\n\t}\n\n}\n\nexport class AuthenticationError extends BaseError {\n\n\tconstructor(code, message, previousError) {\n\n\t\tsuper(code, message, previousError);\n\n\t\tthis.name = 'AuthenticationError';\n\n\t}\n\n}\n\nexport class ConnectError extends BaseError {\n\n\tconstructor(code, message, previousError) {\n\n\t\tsuper(code, message, previousError);\n\n\t\tthis.name = 'ConnectError';\n\n\t}\n\n}\n\nexport class DisconnectError extends BaseError {\n\n\tconstructor(code, message, previousError) {\n\n\t\tsuper(code, message, previousError);\n\n\t\tthis.name = 'DisconnectError';\n\n\t}\n\n}\n\nexport class GetUIDError extends BaseError {\n\n\tconstructor(code, message, previousError) {\n\n\t\tsuper(code, message, previousError);\n\n\t\tthis.name = 'GetUIDError';\n\n\t}\n\n}\n"
  },
  {
    "path": "src/index.js",
    "content": "\"use strict\";\n\nimport NFC from './NFC';\nimport Reader, {\n\tTAG_ISO_14443_3,\n\tTAG_ISO_14443_4,\n\tKEY_TYPE_A,\n\tKEY_TYPE_B,\n\tCONNECT_MODE_CARD,\n\tCONNECT_MODE_DIRECT,\n} from './Reader';\nimport ACR122Reader from './ACR122Reader';\nimport {\n\tUNKNOWN_ERROR,\n\tFAILURE,\n\tCARD_NOT_CONNECTED,\n\tOPERATION_FAILED,\n\tBaseError,\n\tTransmitError,\n\tControlError,\n\tReadError,\n\tWriteError,\n\tLoadAuthenticationKeyError,\n\tAuthenticationError,\n\tConnectError,\n\tDisconnectError,\n\tGetUIDError,\n} from './errors';\n\nexport {\n\tNFC,\n\tReader,\n\tTAG_ISO_14443_3,\n\tTAG_ISO_14443_4,\n\tKEY_TYPE_A,\n\tKEY_TYPE_B,\n\tCONNECT_MODE_CARD,\n\tCONNECT_MODE_DIRECT,\n\tACR122Reader,\n\tUNKNOWN_ERROR,\n\tFAILURE,\n\tCARD_NOT_CONNECTED,\n\tOPERATION_FAILED,\n\tBaseError,\n\tTransmitError,\n\tControlError,\n\tReadError,\n\tWriteError,\n\tLoadAuthenticationKeyError,\n\tAuthenticationError,\n\tConnectError,\n\tDisconnectError,\n\tGetUIDError,\n};\n"
  },
  {
    "path": "test/README.md",
    "content": "# Tests\n\n**There are no tests yet 😢**\n\nTo test the features of this library, we need to develop mock version of pcsclite library to simulate cards. Feel free to contribute.\n\n---\n\nWe are going to use [AVA: Futuristic JavaScript test runner](https://github.com/avajs/ava).  \n\nIt is fully set up, so you can already run tests with:\n\n```bash\nnpm run test\n```\n\nIt is a shortcut for `cross-env NODE_ENV=test ava test --verbose` which runs all the tests in the test folder _(except for helpers, fixtures folders as well as files prefixed with `_`)_.\n"
  },
  {
    "path": "test/_node-version-test.js",
    "content": "\"use strict\";\n\nconst mock = require('mock-require');\n\nmock('@pokusew/pcsclite', {});\n\nconst {\n\tNFC,\n\tReader, TAG_ISO_14443_3, TAG_ISO_14443_4, KEY_TYPE_A, KEY_TYPE_B,\n\tACR122Reader,\n\tUNKNOWN_ERROR,\n\tFAILURE,\n\tCARD_NOT_CONNECTED,\n\tOPERATION_FAILED,\n\tBaseError,\n\tTransmitError,\n\tControlError,\n\tReadError,\n\tWriteError,\n\tLoadAuthenticationKeyError,\n\tAuthenticationError,\n\tConnectError,\n\tDisconnectError,\n\tGetUIDError\n} = require('../dist/index');\n"
  },
  {
    "path": "test/helpers/pcsclite-mock.js",
    "content": "\"use strict\";\n\nimport EventEmitter from 'events';\n\n\nclass MockPCSC extends EventEmitter {\n\n\tconstructor(name) {\n\t\tsuper();\n\t}\n\n\tsimulateReader(reader) {\n\t\tthis.emit('reader', reader);\n\t}\n\n}\n\n\nclass MockReader extends EventEmitter {\n\n\tname = null;\n\n\tconstructor(name) {\n\t\tsuper();\n\t\tthis.name = name || 'MockReader';\n\t}\n\n\tsimulateCard(card) {\n\t\tthis.emit('card', card);\n\t}\n\n}\n\n\nclass MockCard extends EventEmitter {\n\n\tconstructor() {\n\t\tsuper();\n\t}\n\n}\n\n\nexport default function () {\n\n\treturn new MockPCSC();\n\n}\n"
  },
  {
    "path": "test/tests.js",
    "content": "\"use strict\";\n\nimport test from 'ava';\nimport mock from 'mock-require';\n\n// mock @pokusew/pcsclite to allow to simulate cards\nimport pcscliteMock from './helpers/pcsclite-mock';\nmock('@pokusew/pcsclite', pcscliteMock);\nconst NFC = require('../src/NFC').default;\n\n\n\ntest('first', t => {\n\n\tconst nfc = new NFC();\n\n\tt.truthy(1 === 1);\n\n});\n"
  }
]