├── .gitignore ├── binding.gyp ├── package.json ├── test.js ├── index.js ├── README.md └── src ├── mifare.h └── nfc.cc /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | *.swp 10 | 11 | pids 12 | logs 13 | results 14 | 15 | build/ 16 | node_modules/ 17 | npm-debug.log 18 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ { 3 | "target_name": "nfc", 4 | "sources": [ "src/nfc.cc" ], 5 | "libraries": [ "-lnfc", "-L/usr/local/lib/" ], 6 | "include_dirs": [ 7 | "", 24 | "maintainers": [ 25 | { 26 | "name": "Jeroen Vollenbrock", 27 | "email": "jeroen@athom.nl", 28 | "url": "http://athom.nl" 29 | } 30 | ], 31 | "license": "MIT", 32 | "gypfile": true 33 | } 34 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var nfc = require('./index').nfc 2 | , util = require('util') 3 | , version = nfc.version() 4 | , devices = nfc.scan() 5 | ; 6 | 7 | console.log('version: ' + util.inspect(version, { depth: null })); 8 | console.log('devices: ' + util.inspect(devices, { depth: null })); 9 | 10 | function read(deviceID) { 11 | console.log(''); 12 | var nfcdev = new nfc.NFC(); 13 | 14 | nfcdev.on('read', function(tag) { 15 | console.log(util.inspect(tag, { depth: null })); 16 | if ((!!tag.data) && (!!tag.offset)) console.log(util.inspect(nfc.parse(tag.data.slice(tag.offset)), { depth: null })); 17 | nfcdev.stop(); 18 | }); 19 | 20 | nfcdev.on('error', function(err) { 21 | console.log(util.inspect(err, { depth: null })); 22 | }); 23 | 24 | nfcdev.on('stopped', function() { 25 | console.log('stopped'); 26 | }); 27 | 28 | console.log(nfcdev.start(deviceID)); 29 | } 30 | 31 | for (var deviceID in devices) read(deviceID); -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var nfc = require('bindings')('nfc') 2 | , events = require('events') 3 | , ndef = require('ndef') 4 | ; 5 | 6 | var inherits = function(target, source) { 7 | for (var k in source.prototype) { 8 | target.prototype[k] = source.prototype[k]; 9 | } 10 | }; 11 | inherits(nfc.NFC, events.EventEmitter); 12 | 13 | exports.nfc = { version : nfc.version 14 | , NFC : nfc.NFC 15 | }; 16 | 17 | exports.nfc.parse = function(data) { 18 | var bytes, i, results, tlv; 19 | 20 | results = []; 21 | 22 | for (i = 0, bytes = data.toJSON().data; i < bytes.length; i += tlv.len) { 23 | tlv = { type: bytes[i++] }; 24 | if ((tlv.type === 0xfe) || (i >= bytes.length)) { 25 | results.push(tlv); 26 | break; 27 | } 28 | 29 | tlv.len = bytes[i++]; 30 | if (tlv.len === 0xff) { 31 | if ((i + 1) >= bytes.length) break; 32 | tlv.len = bytes[i++] << 8; 33 | tlv.len += bytes[i++]; 34 | } 35 | if ((tlv.len > 0) && ((i + tlv.len) < bytes.length)) tlv.value = bytes.slice(i, i + tlv.len); 36 | if ((tlv.type === 0x03) && (!!tlv.value)) tlv.ndef = ndef.decodeMessage(tlv.value); 37 | if (!!tlv.value) tlv.value = ndef.util.bytesToHexString(tlv.value); 38 | results.push(tlv); 39 | } 40 | 41 | return results; 42 | }; 43 | 44 | exports.nfc.scan = function() { 45 | var device, devices, i, info, j, k, kv, mod, mods, prop, props, protocol, results, speeds, v, x; 46 | 47 | results = {}; 48 | 49 | devices = nfc.scan(); 50 | for (device in devices) if (devices.hasOwnProperty(device)) { 51 | props = devices[device].info.split('\n'); 52 | info = {}; 53 | for (i = 0; i < props.length; i++) { 54 | prop = props[i].trim(); 55 | if (prop === '') continue; 56 | 57 | x = prop.indexOf(':'); 58 | if (x < 1) { 59 | info[i] = prop; 60 | continue; 61 | } 62 | 63 | k = prop.substring(0, x); 64 | v = prop.substring(x + 1).trim(); 65 | if (v.indexOf(')') === -1) { 66 | info[k] = v; 67 | continue; 68 | } 69 | 70 | kv = v.split('), '); 71 | mods = {}; 72 | for (j = 0; j < kv.length; j++) { 73 | mod = kv[j].trim(); 74 | if (mod === '') continue; 75 | 76 | x = mod.indexOf(' ('); 77 | if (x < 1) { 78 | mods[j] = mod; 79 | continue; 80 | } 81 | protocol = mod.substring(0, x); 82 | speeds = mod.substring(x + 2).trim(); 83 | if (speeds.indexOf(')') === (speeds.length - 1)) speeds = speeds.substring(0, speeds.length - 1); 84 | mods[protocol] = speeds.split(', '); 85 | } 86 | 87 | info[k] = mods; 88 | } 89 | 90 | results[device] = { name: devices[device].name, info: info }; 91 | } 92 | 93 | return results; 94 | }; 95 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-nfc 2 | ======== 3 | A binding from libnfc to node. 4 | At present, 5 | only reading is supported. 6 | 7 | ## Installation 8 | 9 | ### Step 1: Prerequisites 10 | In order to use the module you need to install libnfc and libusb. 11 | Read more about [libnfc here](http://nfc-tools.org/index.php?title=Libnfc). 12 | 13 | On Linux, you want: 14 | 15 | sudo apt-get update 16 | sudo apt-get upgrade 17 | sudo apt-get install libusb-dev libnfc 18 | 19 | On MacOS X, you want: 20 | 21 | brew update 22 | brew doctor 23 | brew install libusb-compat libnfc 24 | 25 | ### Step 2: Installation 26 | 27 | To install it, use npm: 28 | 29 | npm install nfc 30 | 31 | Or, to compile it yourself, make sure you have node-gyp: 32 | 33 | node-gyp configure 34 | node-gyp build 35 | 36 | ### NPM errors 37 | 38 | - An error of **missing nfc.h** indicates that libnfc isn't installed correctly. 39 | 40 | ### Runtime errors 41 | 42 | - An error of **Unable to claim USB interface (Permission denied)** 43 | indicates that another process has the interface. On MacOS X you can try: 44 | 45 | $ sudo killall pcscd 46 | 47 | 48 | ## Initialization and Information 49 | 50 | var nfc = require('nfc').nfc 51 | , util = require('util') 52 | ; 53 | 54 | console.log('nfc.version(): ' + util.inspect(nfc.version(), { depth: null })); 55 | // { name: 'libfnc', version: '1.7.0' } 56 | 57 | console.log('nfc.scan(): ' + util.inspect(nfc.scan(), { depth: null })); 58 | // { 'pn53x_usb:160:012': { name: 'SCM Micro / SCL3711-NFC&RW', info: { chip: 'PN533 v2.7', ... } } } 59 | 60 | ## Reading 61 | 62 | var device = new nfc.NFC(); 63 | device.on('read', function(tag) { 64 | // { deviceID: '...', name: '...', uid: '...', type: 0x04 (Mifare Classic) or 0x44 (Mifare Ultralight) } 65 | 66 | if ((!!tag.data) && (!!tag.offset)) console.log(util.inspect(nfc.parse(tag.data.slice(tag.offset)), { depth: null })); 67 | }).on('error', function(err) { 68 | // handle background error; 69 | }).start(); 70 | // optionally the start function may include the deviceID (e.g., 'pn53x_usb:160:012') 71 | 72 | ## And an extra thanks to... 73 | 74 | [jeroenvollenbrock](https://github.com/jeroenvollenbrock) for the huge update he made to this project! 75 | 76 | ## License 77 | 78 | (The MIT License) 79 | 80 | Copyright (c) 2011 Camilo Tapia <camilo.tapia@gmail.com> 81 | 82 | Permission is hereby granted, free of charge, to any person obtaining 83 | a copy of this software and associated documentation files (the 84 | 'Software'), to deal in the Software without restriction, including 85 | without limitation the rights to use, copy, modify, merge, publish, 86 | distribute, sublicense, and/or sell copies of the Software, and to 87 | permit persons to whom the Software is furnished to do so, subject to 88 | the following conditions: 89 | 90 | The above copyright notice and this permission notice shall be 91 | included in all copies or substantial portions of the Software. 92 | 93 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 94 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 95 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 96 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 97 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 98 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 99 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 100 | -------------------------------------------------------------------------------- /src/mifare.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Free/Libre Near Field Communication (NFC) library 3 | * 4 | * Libnfc historical contributors: 5 | * Copyright (C) 2009 Roel Verdult 6 | * Copyright (C) 2009-2013 Romuald Conty 7 | * Copyright (C) 2010-2012 Romain Tartière 8 | * Copyright (C) 2010-2013 Philippe Teuwen 9 | * Copyright (C) 2012-2013 Ludovic Rousseau 10 | * See AUTHORS file for a more comprehensive list of contributors. 11 | * Additional contributors of this file: 12 | * 13 | * Redistribution and use in source and binary forms, with or without 14 | * modification, are permitted provided that the following conditions are met: 15 | * 1) Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 2 )Redistributions in binary form must reproduce the above copyright 18 | * notice, this list of conditions and the following disclaimer in the 19 | * documentation and/or other materials provided with the distribution. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | * 33 | * Note that this license only applies on the examples, NFC library itself is under LGPL 34 | * 35 | */ 36 | 37 | /** 38 | * @file mifare.h 39 | * @brief provide samples structs and functions to manipulate MIFARE Classic and Ultralight tags using libnfc 40 | */ 41 | 42 | #ifndef _LIBNFC_MIFARE_H_ 43 | # define _LIBNFC_MIFARE_H_ 44 | 45 | # include 46 | 47 | // Compiler directive, set struct alignment to 1 uint8_t for compatibility 48 | # pragma pack(1) 49 | 50 | typedef enum { 51 | MC_AUTH_A = 0x60, 52 | MC_AUTH_B = 0x61, 53 | MC_READ = 0x30, 54 | MC_WRITE = 0xA0, 55 | MC_TRANSFER = 0xB0, 56 | MC_DECREMENT = 0xC0, 57 | MC_INCREMENT = 0xC1, 58 | MC_STORE = 0xC2 59 | } mifare_cmd; 60 | 61 | // MIFARE command params 62 | struct mifare_param_auth { 63 | uint8_t abtKey[6]; 64 | uint8_t abtAuthUid[4]; 65 | }; 66 | 67 | struct mifare_param_data { 68 | uint8_t abtData[16]; 69 | }; 70 | 71 | struct mifare_param_value { 72 | uint8_t abtValue[4]; 73 | }; 74 | 75 | typedef union { 76 | struct mifare_param_auth mpa; 77 | struct mifare_param_data mpd; 78 | struct mifare_param_value mpv; 79 | } mifare_param; 80 | 81 | // Reset struct alignment to default 82 | # pragma pack() 83 | 84 | bool nfc_initiator_mifare_cmd(nfc_device *pnd, const mifare_cmd mc, const uint8_t ui8Block, mifare_param *pmp); 85 | 86 | // Compiler directive, set struct alignment to 1 uint8_t for compatibility 87 | # pragma pack(1) 88 | 89 | // MIFARE Classic 90 | typedef struct { 91 | uint8_t abtUID[4]; // beware for 7bytes UID it goes over next fields 92 | uint8_t btBCC; 93 | uint8_t btSAK; // beware it's not always exactly SAK 94 | uint8_t abtATQA[2]; 95 | uint8_t abtManufacturer[8]; 96 | } mifare_classic_block_manufacturer; 97 | 98 | typedef struct { 99 | uint8_t abtData[16]; 100 | } mifare_classic_block_data; 101 | 102 | typedef struct { 103 | uint8_t abtKeyA[6]; 104 | uint8_t abtAccessBits[4]; 105 | uint8_t abtKeyB[6]; 106 | } mifare_classic_block_trailer; 107 | 108 | typedef union { 109 | mifare_classic_block_manufacturer mbm; 110 | mifare_classic_block_data mbd; 111 | mifare_classic_block_trailer mbt; 112 | } mifare_classic_block; 113 | 114 | typedef struct { 115 | mifare_classic_block amb[256]; 116 | } mifare_classic_tag; 117 | 118 | // MIFARE Ultralight 119 | typedef struct { 120 | uint8_t sn0[3]; 121 | uint8_t btBCC0; 122 | uint8_t sn1[4]; 123 | uint8_t btBCC1; 124 | uint8_t internal; 125 | uint8_t lock[2]; 126 | uint8_t otp[4]; 127 | } mifareul_block_manufacturer; 128 | 129 | typedef struct { 130 | uint8_t abtData[16]; 131 | } mifareul_block_data; 132 | 133 | typedef union { 134 | mifareul_block_manufacturer mbm; 135 | mifareul_block_data mbd; 136 | } mifareul_block; 137 | 138 | typedef struct { 139 | mifareul_block amb[4]; 140 | } mifareul_tag; 141 | 142 | // Reset struct alignment to default 143 | # pragma pack() 144 | 145 | #endif // _LIBNFC_MIFARE_H_ 146 | -------------------------------------------------------------------------------- /src/nfc.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "mifare.h" 8 | 9 | using namespace v8; 10 | 11 | static const nfc_modulation nmMifare = { 12 | NMT_ISO14443A, 13 | NBR_106, 14 | }; 15 | static uint8_t keys[] = { 16 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 17 | 0xd3, 0xf7, 0xd3, 0xf7, 0xd3, 0xf7, 18 | 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 19 | 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 20 | 0x4d, 0x3a, 0x99, 0xc3, 0x51, 0xdd, 21 | 0x1a, 0x98, 0x2c, 0x7e, 0x45, 0x9a, 22 | 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 23 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 24 | 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56 25 | }; 26 | static size_t num_keys = sizeof(keys) / 6; 27 | 28 | 29 | namespace { 30 | 31 | class NFC: public Nan::ObjectWrap { 32 | public: 33 | static NAN_METHOD(New); 34 | static NAN_METHOD(Start); 35 | static NAN_METHOD(Stop); 36 | 37 | void stop() { 38 | run = false; 39 | while(claimed); 40 | if(pnd) { 41 | nfc_abort_command(pnd); 42 | nfc_close(pnd); 43 | pnd = NULL; 44 | } 45 | if(context) { 46 | nfc_exit(context); 47 | context = NULL; 48 | } 49 | } 50 | 51 | nfc_device *pnd; 52 | nfc_target nt; 53 | nfc_context *context; 54 | bool run; 55 | bool claimed; 56 | }; 57 | 58 | class NFCCard { 59 | public: 60 | NFCCard() { 61 | deviceID = name = uid = tag = error = NULL; 62 | type = data_size = offset = 0; 63 | data = NULL; 64 | } 65 | 66 | ~NFCCard() { 67 | delete deviceID; 68 | delete name; 69 | delete uid; 70 | delete tag; 71 | delete error; 72 | if(data) delete data; 73 | } 74 | 75 | void AddToNodeObject(Local object) { 76 | if(deviceID) object->Set(Nan::New("deviceID").ToLocalChecked(), Nan::New(deviceID).ToLocalChecked()); 77 | if(name) object->Set(Nan::New("name").ToLocalChecked(), Nan::New(name).ToLocalChecked()); 78 | if(uid) object->Set(Nan::New("uid").ToLocalChecked(), Nan::New(uid).ToLocalChecked()); 79 | if(type) object->Set(Nan::New("type").ToLocalChecked(), Nan::New(type)); 80 | if(tag) object->Set(Nan::New("tag").ToLocalChecked(), Nan::New(tag).ToLocalChecked()); 81 | if(error) object->Set(Nan::New("error").ToLocalChecked(), Nan::Error(error)); 82 | if(data) object->Set(Nan::New("data").ToLocalChecked(), Nan::NewBuffer(data, data_size).ToLocalChecked()); 83 | if(offset) object->Set(Nan::New("offset").ToLocalChecked(), Nan::New((int32_t)offset)); 84 | data = NULL; //ownership transferred to nodejs 85 | } 86 | 87 | void SetDeviceID(const char *deviceID) { 88 | if(this->deviceID) delete this->deviceID; 89 | this->deviceID = strdup(deviceID); 90 | } 91 | void SetName(const char *name) { 92 | if(this->name) delete this->name; 93 | this->name = strdup(name); 94 | } 95 | void SetUID(const char *uid) { 96 | if(this->uid) delete this->uid; 97 | this->uid = strdup(uid); 98 | } 99 | void SetType(int32_t type) { 100 | this->type = type; 101 | } 102 | void SetTag(const char *tag) { 103 | if(this->tag) delete this->tag; 104 | this->tag = strdup(tag); 105 | } 106 | void SetError(const char *error) { 107 | if(this->error) delete this->error; 108 | this->error = strdup(error); 109 | } 110 | void SetOffset(size_t offset) { 111 | this->offset = offset; 112 | } 113 | void SetData(const uint8_t *data, size_t data_size) { 114 | if(this->data) free(this->data); 115 | this->data_size = data_size; 116 | this->data = (char*)malloc(data_size); 117 | memcpy(this->data, data, data_size); 118 | } 119 | private: 120 | char *deviceID; 121 | char *name; 122 | char *uid; 123 | int32_t type; 124 | char *tag; 125 | char *error; 126 | size_t offset; 127 | size_t data_size; 128 | char *data; 129 | }; 130 | 131 | class NFCReadWorker : public Nan::AsyncProgressWorker { 132 | public: 133 | NFCReadWorker(NFC *baton, Localself) 134 | : Nan::AsyncProgressWorker(new Nan::Callback(self.As())), baton(baton) { 135 | SaveToPersistent("self", self); 136 | baton->run = true; 137 | } 138 | 139 | ~NFCReadWorker() { 140 | delete callback; //For some reason HandleProgressCallback only fires while callback exists. 141 | } 142 | 143 | void HandleOKCallback() { 144 | Local argv = Nan::New("stopped").ToLocalChecked(); 145 | 146 | Local self = GetFromPersistent("self").As(); 147 | Nan::MakeCallback(self, "emit", 1, &argv); 148 | } 149 | 150 | void HandleErrorCallback() { 151 | Local argv[2]; 152 | argv[0] = Nan::New("error").ToLocalChecked(); 153 | argv[1] = Nan::Error(AsyncProgressWorker::ErrorMessage()); 154 | 155 | Local self = GetFromPersistent("self").As(); 156 | Nan::MakeCallback(self, "emit", 2, argv); 157 | HandleOKCallback(); 158 | } 159 | 160 | void Execute(const AsyncProgressWorker::ExecutionProgress& progress) { 161 | while(baton->run && nfc_initiator_select_passive_target(baton->pnd, nmMifare, NULL, 0, &baton->nt) > 0) { 162 | baton->claimed = true; 163 | tag = new NFCCard(); 164 | if(baton->run) ReadTag(tag); 165 | baton->claimed = false; 166 | 167 | progress.Send(NULL, 0); 168 | 169 | int timeout = 5 * 1000; //5 second timeout 170 | while(baton->run && tag && --timeout > 0) { 171 | usleep(1000); 172 | } 173 | if(timeout <= 0) { 174 | //unresponsive VM, node was likely killed while this devide was not stopped. 175 | baton->stop(); 176 | baton->run = false; 177 | fprintf(stderr, "Node was stopped while some NFC devices where still started.\n"); 178 | } 179 | } 180 | } 181 | 182 | #define MAX_DEVICE_COUNT 16 183 | #define MAX_FRAME_LENGTH 264 184 | 185 | void ReadTag(NFCCard *tag) { 186 | unsigned long cc, n; 187 | char *bp, result[BUFSIZ]; 188 | const char *sp; 189 | 190 | tag->SetDeviceID(nfc_device_get_connstring(baton->pnd)); 191 | tag->SetName(nfc_device_get_name(baton->pnd)); 192 | 193 | cc = baton->nt.nti.nai.szUidLen; 194 | if (cc > sizeof baton->nt.nti.nai.abtUid) cc = sizeof baton->nt.nti.nai.abtUid; 195 | char uid[3 * sizeof baton->nt.nti.nai.abtUid]; 196 | bzero(uid, sizeof uid); 197 | 198 | for (n = 0, bp = uid, sp = ""; n < cc; n++, bp += strlen(bp), sp = ":") { 199 | snprintf(bp, sizeof uid - (bp - uid), "%s%02x", sp, baton->nt.nti.nai.abtUid[n]); 200 | } 201 | tag->SetUID(uid); 202 | tag->SetType(baton->nt.nti.nai.abtAtqa[1]); 203 | 204 | switch (baton->nt.nti.nai.abtAtqa[1]) { 205 | case 0x04: 206 | { 207 | tag->SetTag("mifare-classic"); 208 | 209 | // size guessing logic from nfc-mfclassic.c 210 | uint8_t uiBlocks = ((baton->nt.nti.nai.abtAtqa[1] & 0x02) == 0x02) ? 0xff // 4Kb 211 | : ((baton->nt.nti.nai.btSak & 0x01) == 0x01) ? 0x13 // 320b 212 | : 0x3f; // 1Kb/2Kb 213 | if (nfc_device_set_property_bool(baton->pnd, NP_EASY_FRAMING, false) < 0) { 214 | snprintf(result, sizeof result, "nfc_device_set_property_bool easyFraming=false: %s", 215 | nfc_strerror(baton->pnd)); 216 | tag->SetError(result); 217 | break; 218 | } 219 | uint8_t abtRats[2] = { 0xe0, 0x50 }; 220 | uint8_t abtRx[MAX_FRAME_LENGTH]; 221 | int res = nfc_initiator_transceive_bytes(baton->pnd, abtRats, sizeof abtRats, abtRx, sizeof abtRx, 0); 222 | if (res > 0) { 223 | int flip; 224 | 225 | for (flip = 0; flip < 2; flip++) { 226 | if (nfc_device_set_property_bool(baton->pnd, NP_ACTIVATE_FIELD, flip > 0) < 0) { 227 | snprintf(result, sizeof result, "nfc_device_set_property_bool activateField=%s: %s", 228 | flip > 0 ? "true" : "false", nfc_strerror(baton->pnd)); 229 | tag->SetError(result); 230 | break; 231 | } 232 | } 233 | if (flip != 2) break; 234 | 235 | if ((res >= 10) 236 | && (abtRx[5] == 0xc1) 237 | && (abtRx[6] == 0x05) 238 | && (abtRx[7] == 0x2f) 239 | && (abtRx[8] == 0x2f) 240 | && ((baton->nt.nti.nai.abtAtqa[1] & 0x02) == 0x00)) uiBlocks = 0x7f; 241 | } 242 | if (nfc_initiator_select_passive_target(baton->pnd, nmMifare, NULL, 0, &baton->nt) <= 0) { 243 | tag->SetError("unable to reselect tag"); 244 | break; 245 | } 246 | 247 | if (nfc_device_set_property_bool(baton->pnd, NP_EASY_FRAMING, true) < 0) { 248 | snprintf(result, sizeof result, "nfc_device_set_property_bool easyFraming=false: %s", 249 | nfc_strerror(baton->pnd)); 250 | tag->SetError(result); 251 | break; 252 | } 253 | 254 | int cnt, len; 255 | uint8_t command[MAX_FRAME_LENGTH], data[4 * 1024], *dp; 256 | len = (uiBlocks + 1) * 16; 257 | if (((unsigned long) len) > sizeof data) len = sizeof data; 258 | for (cnt = uiBlocks, dp = data + len - 16; 259 | cnt >= 0; 260 | cnt--, dp -= 16) { 261 | if (((cnt + 1) % (cnt < 128 ? 4 : 16)) == 0) { 262 | size_t key_index; 263 | struct mifare_param_auth auth_params; 264 | for (key_index = 0; key_index < num_keys; key_index++) { 265 | bzero(command, sizeof command); 266 | command[0] = MC_AUTH_B; 267 | command[1] = cnt; 268 | 269 | bzero(&auth_params, sizeof auth_params); 270 | memcpy(auth_params.abtKey, (uint8_t *) keys + (key_index * 6), 271 | sizeof auth_params.abtKey); 272 | memcpy(auth_params.abtAuthUid, baton->nt.nti.nai.abtUid + baton->nt.nti.nai.szUidLen - 4, 273 | sizeof auth_params.abtAuthUid); 274 | memcpy(command + 2, &auth_params, sizeof auth_params); 275 | 276 | res = nfc_initiator_transceive_bytes(baton->pnd, command, 2 + sizeof auth_params, abtRx, 277 | sizeof abtRx, -1); 278 | if (res >= 0) break; 279 | } 280 | if (key_index >= num_keys) { 281 | snprintf(result, sizeof result, "nfc_initiator_transceive_bytes: %s", nfc_strerror(baton->pnd)); 282 | tag->SetError(result); 283 | break; 284 | } 285 | } 286 | 287 | command[0] = MC_READ; 288 | command[1] = cnt; 289 | res = nfc_initiator_transceive_bytes(baton->pnd, command, 2, dp, 16, -1); 290 | if (res >= 0) continue; 291 | 292 | if (res != NFC_ERFTRANS) { 293 | snprintf(result, sizeof result, "nfc_initiator_transceive_bytes: %s", nfc_strerror(baton->pnd)); 294 | tag->SetError(result); 295 | } 296 | break; 297 | } 298 | if (cnt >= 0) break; 299 | 300 | tag->SetData(data, len); 301 | 302 | tag->SetOffset(16 * 4); 303 | break; 304 | } 305 | 306 | case 0x44: 307 | { 308 | tag->SetTag("mifare-ultralight"); 309 | 310 | if (nfc_device_set_property_bool(baton->pnd, NP_EASY_FRAMING, true) < 0) { 311 | snprintf(result, sizeof result, "nfc_device_set_property_bool easyFraming=false: %s", 312 | nfc_strerror(baton->pnd)); 313 | tag->SetError(result); 314 | break; 315 | } 316 | 317 | int cnt, len, res; 318 | uint8_t command[2], data[16 * 12], *dp; 319 | for (n = 0, cc = 0x0f, dp = data, cnt = sizeof data, len = 0; 320 | n < cc; 321 | n += 4, dp += res, cnt -= res, len += res) { 322 | command[0] = MC_READ; 323 | command[1] = n; 324 | res = nfc_initiator_transceive_bytes(baton->pnd, command, 2, dp, cnt, -1); 325 | if (res >= 0) continue; 326 | 327 | if (res != NFC_ERFTRANS) { 328 | snprintf(result, sizeof result, "nfc_initiator_transceive_bytes: %s", nfc_strerror(baton->pnd)); 329 | tag->SetError(result); 330 | } 331 | break; 332 | } 333 | if (n < cc) break; 334 | 335 | tag->SetData(data, len); 336 | 337 | tag->SetOffset(16); 338 | break; 339 | } 340 | 341 | default: 342 | break; 343 | } 344 | } 345 | 346 | void HandleProgressCallback(const char *_tag, size_t size) { 347 | Nan::HandleScope scope; 348 | 349 | Local object = Nan::New(); 350 | tag->AddToNodeObject(object); 351 | delete tag; 352 | tag = NULL; 353 | 354 | Local argv[2]; 355 | argv[0] = Nan::New("read").ToLocalChecked(); 356 | argv[1] = object; 357 | 358 | Local self = GetFromPersistent("self").As(); 359 | Nan::MakeCallback(self, "emit", 2, argv); 360 | } 361 | 362 | private: 363 | NFC *baton; 364 | NFCCard *tag; 365 | }; 366 | 367 | 368 | NAN_METHOD(NFC::New) { 369 | Nan::HandleScope scope; 370 | assert(info.IsConstructCall()); 371 | NFC* self = new NFC(); 372 | self->Wrap(info.This()); 373 | info.GetReturnValue().Set(info.This()); 374 | } 375 | 376 | NAN_METHOD(NFC::Stop) { 377 | Nan::HandleScope scope; 378 | NFC* nfc = ObjectWrap::Unwrap(info.This()); 379 | nfc->stop(); 380 | info.GetReturnValue().Set(info.This()); 381 | } 382 | 383 | NAN_METHOD(NFC::Start) { 384 | Nan::HandleScope scope; 385 | 386 | nfc_context *context; 387 | nfc_init(&context); 388 | if (context == NULL) return Nan::ThrowError("unable to init libfnc (malloc)."); 389 | 390 | nfc_device *pnd; 391 | if (info.Length() > 0) { 392 | if (!info[0]->IsString()) { 393 | nfc_exit(context); 394 | return Nan::ThrowError("deviceID parameter is not a string"); 395 | } 396 | nfc_connstring connstring; 397 | String::Utf8Value device(info[0]->ToString()); 398 | snprintf(connstring, sizeof connstring, "%s", *device); 399 | 400 | pnd = nfc_open(context, connstring); 401 | } else { 402 | pnd = nfc_open(context, NULL); 403 | } 404 | if (pnd == NULL) { 405 | nfc_exit(context); 406 | return Nan::ThrowError("unable open NFC device"); 407 | } 408 | 409 | char result[BUFSIZ]; 410 | if (nfc_initiator_init(pnd) < 0) { 411 | snprintf(result, sizeof result, "nfc_initiator_init: %s", nfc_strerror(pnd)); 412 | nfc_close(pnd); 413 | nfc_exit(context); 414 | return Nan::ThrowError(result); 415 | } 416 | 417 | NFC *baton = ObjectWrap::Unwrap(info.This()); 418 | baton->context = context; 419 | baton->pnd = pnd; 420 | 421 | NFCReadWorker* readWorker = new NFCReadWorker(baton, info.This()); 422 | Nan::AsyncQueueWorker(readWorker); 423 | 424 | Local object = Nan::New(); 425 | object->Set(Nan::New("deviceID").ToLocalChecked(), Nan::New(nfc_device_get_connstring(baton->pnd)).ToLocalChecked()); 426 | object->Set(Nan::New("name").ToLocalChecked(), Nan::New(nfc_device_get_name(baton->pnd)).ToLocalChecked()); 427 | 428 | info.GetReturnValue().Set(object); 429 | } 430 | 431 | NAN_METHOD(Scan) { 432 | Nan::HandleScope scope; 433 | 434 | nfc_context *context; 435 | nfc_init(&context); 436 | if (context == NULL) return Nan::ThrowError("unable to init libfnc (malloc)."); 437 | 438 | Local object = Nan::New(); 439 | 440 | nfc_connstring connstrings[MAX_DEVICE_COUNT]; 441 | size_t i, n = nfc_list_devices(context, connstrings, MAX_DEVICE_COUNT); 442 | for (i = 0; i < n; i++) { 443 | Local entry = Nan::New(); 444 | nfc_device *pnd = nfc_open(context, connstrings[i]); 445 | if (pnd == NULL) continue; 446 | 447 | entry->Set(Nan::New("name").ToLocalChecked(), Nan::New(nfc_device_get_name(pnd)).ToLocalChecked()); 448 | 449 | char *info; 450 | if (nfc_device_get_information_about(pnd, &info) >= 0) { 451 | entry->Set(Nan::New("info").ToLocalChecked(), Nan::New(info).ToLocalChecked()); 452 | nfc_free(info); 453 | } else { 454 | entry->Set(Nan::New("info").ToLocalChecked(), Nan::New("").ToLocalChecked()); 455 | } 456 | object->Set(Nan::New(nfc_device_get_connstring(pnd)).ToLocalChecked(), entry); 457 | 458 | nfc_close(pnd); 459 | } 460 | 461 | nfc_exit(context); 462 | 463 | info.GetReturnValue().Set(object); 464 | } 465 | 466 | NAN_METHOD(Version) { 467 | Nan::HandleScope scope; 468 | 469 | nfc_context *context; 470 | nfc_init(&context); 471 | if (context == NULL) return Nan::ThrowError("unable to init libnfc (malloc)."); 472 | 473 | Local object = Nan::New(); 474 | object->Set(Nan::New("name").ToLocalChecked(), Nan::New("libnfc").ToLocalChecked()); 475 | object->Set(Nan::New("version").ToLocalChecked(), Nan::New(nfc_version()).ToLocalChecked()); 476 | 477 | nfc_exit(context); 478 | 479 | info.GetReturnValue().Set(object); 480 | } 481 | NAN_MODULE_INIT(init) { 482 | Local tpl = Nan::New(NFC::New); 483 | tpl->SetClassName(Nan::New("NFC").ToLocalChecked()); 484 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 485 | 486 | SetPrototypeMethod(tpl, "start", NFC::Start); 487 | SetPrototypeMethod(tpl, "stop", NFC::Stop); 488 | 489 | Nan::Export(target, "version", Version); 490 | Nan::Export(target, "scan", Scan); 491 | Nan::Set(target, Nan::New("NFC").ToLocalChecked(), tpl->GetFunction()); 492 | }; 493 | } 494 | 495 | NODE_MODULE(nfc, init) 496 | --------------------------------------------------------------------------------