Created
November 12, 2021 08:02
-
-
Save neilodiaz/682ca7bffc04caa5fbff571faff95e7f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const restify = require('restify'); | |
const events = require('events'); | |
const server = restify.createServer(); | |
const eventEmitter = new events.EventEmitter(); | |
const fs = require('fs'); | |
const path = require('path'); | |
const corsMiddleware = require('restify-cors-middleware'); | |
const nfcCard = require('./lib/nfccard-tool'); | |
const { NFC, TAG_ISO_14443_3, TAG_ISO_14443_4, KEY_TYPE_A, KEY_TYPE_B } = require('nfc-pcsc'); | |
const nfc = new NFC(); | |
const selfAddressed = require('self-addressed'); | |
const _emit = events.EventEmitter.prototype.emit; | |
const _on = events.EventEmitter.prototype.on; | |
eventEmitter.emit = function (name, data) { | |
function mailman(address, envelope) { | |
_emit.call(address, name, envelope); | |
} | |
return selfAddressed(mailman, this, data); // returns a promise | |
}; | |
eventEmitter.on = function (name, fn) { | |
function onSelfAddressedEnvelope(envelope) { | |
if (selfAddressed.is(envelope)) { | |
var result = fn(); | |
selfAddressed(envelope, result); | |
// there is nowhere to send the response envelope | |
// event emitters are unidirectional. | |
// so open the envelope right away! | |
envelope.replies = 1; | |
selfAddressed(envelope); // deliver | |
} | |
} | |
_on.call(this, name, onSelfAddressedEnvelope); | |
}; | |
const cors = corsMiddleware({ | |
preflightMaxAge: 5, | |
origins: ['*'], | |
allowHeaders: ['*','token'], | |
exposeHeaders: ['*','token'] | |
}) | |
var nfcReader = null | |
var cardUID = '' | |
var postMessage = '' | |
nfc.on('reader', reader => { | |
console.log(`${reader.reader.name} device attached`); | |
fs.writeFile("./cardreader.txt", reader.reader.name, function(err) { | |
if(err) { | |
return console.log(err); | |
} | |
}); | |
nfcReader = reader | |
reader.on('card', async card => { | |
// console.log(`card detected`, card); | |
let card_info = '' | |
cardUID = card.uid.toUpperCase() | |
console.log('card type', card) | |
// Check if card is MIFARE Classic or Ultralight by uid length | |
// MIFARE Classic has 8 characters | |
if (cardUID.length <= 8) { | |
card_info = cardUID + '/' | |
try { | |
const key = 'FFFFFFFFFFFF'; // key must be a HEX string on an instance of Buffer | |
const keyType = KEY_TYPE_B; | |
await Promise.all([ | |
reader.authenticate(1, keyType, key), | |
reader.authenticate(2, keyType, key) | |
]); | |
} catch (err) { | |
console.log(`error when authenticating blocks`, { reader: reader.name, card, err }); | |
return; | |
} | |
// READ TO CARD | |
try { | |
let data = await reader.read(1, 16, 16); // blockSize=16 must specified for MIFARE Classic cards | |
let payload = data.readInt16BE(0); // Points | |
card_info = card_info + payload + ';' | |
data = await reader.read(2, 16, 16); // blockSize=16 must specified for MIFARE Classic cards | |
payload = data.readInt16BE(0); // Amount | |
card_info = card_info + payload + ';' | |
} catch (err) { | |
console.log(`error when reading data`, { reader: reader.name, err }); | |
} | |
try { | |
const key = 'FFFFFFFFFFFF'; // key must be a HEX string on an instance of Buffer | |
const keyType = KEY_TYPE_B; | |
await Promise.all([ | |
reader.authenticate(4, keyType, key), | |
reader.authenticate(5, keyType, key), | |
reader.authenticate(6, keyType, key), | |
]); | |
} catch (err) { | |
console.log(`error when authenticating blocks`, { reader: reader.name, card, err }); | |
return; | |
} | |
// READ TO CARD | |
try { | |
let data = await reader.read(4, 16, 16); // blockSize=16 must specified for MIFARE Classic cards | |
let payload = data.readInt16BE(0); // Timestamp 1 | |
card_info = card_info + payload | |
data = await reader.read(5, 16, 16); // blockSize=16 must specified for MIFARE Classic cards | |
payload = data.readInt16BE(0); // Timestamp 2 | |
card_info = card_info + payload | |
data = await reader.read(6, 16, 16); // blockSize=16 must specified for MIFARE Classic cards | |
payload = data.readInt16BE(0); // Timestamp 3 | |
card_info = card_info + payload + ';' | |
} catch (err) { | |
console.log(`error when reading data`, { reader: reader.name, err }); | |
} | |
try { | |
const key = 'FFFFFFFFFFFF'; // key must be a HEX string on an instance of Buffer | |
const keyType = KEY_TYPE_B; | |
await Promise.all([ | |
reader.authenticate(8, keyType, key), | |
reader.authenticate(9, keyType, key), | |
reader.authenticate(10, keyType, key) | |
]); | |
} catch (err) { | |
console.log(`error when authenticating blocks`, { reader: reader.name, card, err }); | |
return; | |
} | |
// READ TO CARD | |
try { | |
let data = await reader.read(8, 16, 16); // blockSize=16 must specified for MIFARE Classic cards | |
let payload = data.readInt16BE(0); // Store ID | |
card_info = card_info + payload + ';' | |
data = await reader.read(9, 16, 16); // blockSize=16 must specified for MIFARE Classic cards | |
payload = data.readInt16BE(0); // Updated At Date 1 | |
card_info = card_info + payload | |
data = await reader.read(10, 16, 16); // blockSize=16 must specified for MIFARE Classic cards | |
payload = data.readInt16BE(0); // Updated At Date 2 | |
card_info = card_info + payload + ';' | |
} catch (err) { | |
console.log(`error when reading data`, { reader: reader.name, err }); | |
} | |
try { | |
const key = 'FFFFFFFFFFFF'; // key must be a HEX string on an instance of Buffer | |
const keyType = KEY_TYPE_B; | |
await Promise.all([ | |
reader.authenticate(12, keyType, key) | |
]); | |
} catch (err) { | |
console.log(`error when authenticating blocks`, { reader: reader.name, card, err }); | |
return; | |
} | |
// READ TO CARD | |
try { | |
let data = await reader.read(12, 16, 16); // blockSize=16 must specified for MIFARE Classic cards | |
let payload = data.readInt16BE(0); // PINCODE | |
card_info = card_info + payload | |
} catch (err) { | |
console.log(`error when reading data`, { reader: reader.name, err }); | |
} | |
fs.writeFile("./cardinfo.txt", card_info, function(err) { | |
if(err) { | |
return console.log(err); | |
} | |
}); | |
} else { | |
try { | |
/** | |
* READ MESSAGE AND ITS RECORDS | |
*/ | |
/** | |
* 1 - READ HEADER | |
* Read from block 0 to block 4 (20 bytes length) in order to parse tag information | |
*/ | |
const cardHeader = await reader.read(0, 20); | |
const tag = nfcCard.parseInfo(cardHeader); | |
// console.log('tag info:', JSON.stringify(tag)); | |
/** | |
* 2 - Read the NDEF message and parse it if it's supposed there is one | |
*/ | |
console.log('nfcCard.isFormatedAsNDEF():', nfcCard.isFormatedAsNDEF()) | |
console.log('nfcCard.hasReadPermissions():', nfcCard.hasReadPermissions()) | |
console.log('nfcCard.hasNDEFMessage():', nfcCard.hasNDEFMessage()) | |
if(nfcCard.isFormatedAsNDEF() && nfcCard.hasReadPermissions() && nfcCard.hasNDEFMessage()) { | |
const NDEFRawMessage = await reader.read(4, nfcCard.getNDEFMessageLengthToRead()); | |
const NDEFMessage = nfcCard.parseNDEF(NDEFRawMessage); | |
let card_message = "" | |
card_message = NDEFMessage[0].text | |
if (typeof NDEFMessage[1] !== 'undefined' && typeof NDEFMessage[1].uri !== 'undefined') { | |
card_message = card_message + '' + NDEFMessage[1].uri | |
console.log('with uri') | |
} | |
console.log('all card text: ', card_message) | |
const textFile = card.uid.toUpperCase() + '/' + card_message | |
fs.writeFile("./cardinfo.txt", textFile, function(err) { | |
if(err) { | |
return console.log(err); | |
} | |
}); | |
} else { | |
const textFile = card.uid.toUpperCase() + '/' | |
fs.writeFile("./cardinfo.txt", textFile, function(err) { | |
if(err) { | |
return console.log(err); | |
} | |
}); | |
console.log('Could not parse anything from this tag: \n The tag is either empty, locked, has a wrong NDEF format or is unreadable.') | |
} | |
} catch (err) { | |
console.error(`error when reading data`, err); | |
} | |
} | |
}); | |
reader.on('card.off', card => { | |
// console.log(`${reader.reader.name} card removed`, card); | |
fs.writeFile("./cardinfo.txt", "", function(err) { | |
if(err) { | |
return console.log(err); | |
} | |
}); | |
}); | |
reader.on('error', err => { | |
console.log(`${reader.reader.name} an error occurred`, err); | |
fs.writeFile("./cardreader.txt", "", function(err) { | |
if(err) { | |
return console.log(err); | |
} | |
}); | |
}); | |
reader.on('end', () => { | |
console.log(`${reader.reader.name} device removed`); | |
fs.writeFile("./cardreader.txt", "", function(err) { | |
if(err) { | |
return console.log(err); | |
} | |
}); | |
}); | |
}) | |
nfc.on('error', err => { | |
console.log('an error occurred', err); | |
}); | |
eventEmitter.on('writecard', async (data) => { | |
let reader = nfcReader | |
let cardmessage = postMessage | |
if (cardUID.length <= 8) { // For MiFare Classic | |
let message_data = postMessage.split(";") | |
const key = 'FFFFFFFFFFFF'; // key must be a HEX string on an instance of Buffer | |
const keyType = KEY_TYPE_B; | |
// ==== SECTOR 1 === // | |
try { | |
await Promise.all([ | |
reader.authenticate(1, keyType, key), | |
reader.authenticate(2, keyType, key) | |
]); | |
} catch (err) { | |
console.log(`error when authenticating blocks`, reader, err); | |
return; | |
} | |
// WRITE TO CARD | |
try { | |
let data = Buffer.allocUnsafe(16); | |
data.fill(0); | |
data.writeInt16BE(message_data[0], 0); | |
await reader.write(1, data, 16); // blockSize=16 must specified for MIFARE Classic cards | |
console.log(`points written`); | |
data = Buffer.allocUnsafe(16); | |
data.fill(0); | |
data.writeInt16BE(message_data[1], 0); | |
await reader.write(2, data, 16); // blockSize=16 must specified for MIFARE Classic cards | |
console.log(`amount written`); | |
} catch (err) { | |
console.log(`error when writing data`, reader, err); | |
} | |
// ==== SECTOR 1 === // | |
try { | |
await Promise.all([ | |
reader.authenticate(4, keyType, key), | |
reader.authenticate(5, keyType, key), | |
reader.authenticate(6, keyType, key), | |
]); | |
} catch (err) { | |
console.log(`error when authenticating blocks`, reader, err); | |
return; | |
} | |
// WRITE TO CARD | |
try { | |
let time_stamp1 = message_data[2].substring(0,4) | |
let time_stamp2 = message_data[2].substring(4,8) | |
let time_stamp3 = message_data[2].substring(8,10) | |
let data = Buffer.allocUnsafe(16); | |
data.fill(0); | |
data.writeInt16BE(time_stamp1, 0); | |
await reader.write(4, data, 16); // blockSize=16 must specified for MIFARE Classic cards | |
console.log(`time_stamp1 written`); | |
data = Buffer.allocUnsafe(16); | |
data.fill(0); | |
data.writeInt16BE(time_stamp2, 0); | |
await reader.write(5, data, 16); // blockSize=16 must specified for MIFARE Classic cards | |
console.log(`time_stamp2 written`); | |
data = Buffer.allocUnsafe(16); | |
data.fill(0); | |
data.writeInt16BE(time_stamp3, 0); | |
await reader.write(6, data, 16); // blockSize=16 must specified for MIFARE Classic cards | |
console.log(`time_stamp3 written`); | |
} catch (err) { | |
console.log(`error when writing data`, reader, err); | |
} | |
// ==== SECTOR 2 === // | |
try { | |
await Promise.all([ | |
reader.authenticate(8, keyType, key), | |
reader.authenticate(9, keyType, key), | |
reader.authenticate(10, keyType, key) | |
]); | |
} catch (err) { | |
console.log(`error when authenticating blocks`, reader, err); | |
return; | |
} | |
// WRITE TO CARD | |
try { | |
let data = Buffer.allocUnsafe(16); | |
data.fill(0); | |
data.writeInt16BE(message_data[3], 0); | |
await reader.write(8, data, 16); // blockSize=16 must specified for MIFARE Classic cards | |
console.log(`store_id written`); | |
let updated_at1 = message_data[4].substring(0,4) | |
let updated_at2 = message_data[4].substring(4,6) | |
data = Buffer.allocUnsafe(16); | |
data.fill(0); | |
data.writeInt16BE(updated_at1, 0); | |
await reader.write(9, data, 16); // blockSize=16 must specified for MIFARE Classic cards | |
console.log(`updated_at1 written`); | |
data = Buffer.allocUnsafe(16); | |
data.fill(0); | |
data.writeInt16BE(updated_at2, 0); | |
await reader.write(10, data, 16); // blockSize=16 must specified for MIFARE Classic cards | |
console.log(`updated_at2 written`); | |
} catch (err) { | |
console.log(`error when writing data`, reader, err); | |
} | |
// ==== SECTOR 3 === // | |
try { | |
await Promise.all([ | |
reader.authenticate(12, keyType, key) | |
]); | |
} catch (err) { | |
console.log(`error when authenticating blocks`, reader, err); | |
return; | |
} | |
// WRITE TO CARD | |
try { | |
let data = Buffer.allocUnsafe(16); | |
data.fill(0); | |
data.writeInt16BE(message_data[5], 0); | |
await reader.write(12, data, 16); // blockSize=16 must specified for MIFARE Classic cards | |
console.log(`pincode written`); | |
} catch (err) { | |
console.log(`error when writing data`, reader, err); | |
} | |
fs.writeFile("./cardinfo.txt", cardUID + '/' + cardmessage, function(err) { | |
if(err) { | |
return console.log(err); | |
} | |
}); | |
return 'success' | |
} else { | |
try { | |
/** | |
* 1 - READ HEADER | |
* Read header: we need to verify if we have read and `write` permissions | |
* and if prepared message length can fit onto the tag. | |
*/ | |
const cardHeader = await reader.read(0, 20); | |
const tag = nfcCard.parseInfo(cardHeader); | |
let half_index = cardmessage.length / 2 | |
let cardmessage_1 = cardmessage.substr(0, half_index); | |
let cardmessage_2 = cardmessage.substr(half_index); | |
/** | |
* 2 - WRITE A NDEF MESSAGE AND ITS RECORDS | |
*/ | |
const message = [ | |
{ type: 'text', text: cardmessage, language: 'en' }, | |
// { type: 'uri', uri: cardmessage_2 } | |
] | |
// Prepare the buffer to write on the card | |
const rawDataToWrite = nfcCard.prepareBytesToWrite(message); | |
// Write the buffer on the card starting at block 4 | |
const preparationWrite = await reader.write(4, rawDataToWrite.preparedData); | |
if (preparationWrite) { | |
// console.log('Data have been written successfully.') | |
var date_now = new Date() | |
var text_msg = date_now + " - " + cardUID + " - " + cardmessage + "\r\n"; | |
fs.appendFile("./write-history.txt", text_msg, function(err) { | |
if(err) { | |
return console.log(err); | |
} | |
}); | |
fs.writeFile("./cardinfo.txt", cardUID + '/' + cardmessage, function(err) { | |
if(err) { | |
return console.log(err); | |
} | |
}); | |
return 'success' | |
} | |
} catch (err) { | |
console.error(`error when writing data`, err); | |
return err; | |
} | |
} | |
}) | |
server.use(restify.plugins.bodyParser()) | |
server.pre(cors.preflight) | |
server.use(cors.actual) | |
server.get('/', function(req, res, next) { | |
res.send('Reading card...') | |
return next(); | |
}); | |
server.post('/write-card', function create(req, res, next) { | |
postMessage = req.body.message | |
console.log('POST: Writing to card...') | |
eventEmitter.emit('writecard').then(function (result) { | |
if (result === 'success') { | |
console.log('Data have been written successfully.') | |
} | |
res.send({result: result}) | |
return next(); | |
}); | |
}); | |
server.get('/checkcardreader', (req, res, next) => { | |
let data = '' | |
let readStream = fs.createReadStream(path.join(__dirname, './') + '/cardreader.txt', 'utf8'); | |
readStream.on('data', function(chunk) { | |
data += chunk; | |
}).on('end', function() { | |
res.send(data) | |
}); | |
return next(); | |
}) | |
server.get('/getcardinfo', (req, res, next) => { | |
let data = '' | |
let readStream = fs.createReadStream(path.join(__dirname, './') + '/cardinfo.txt', 'utf8'); | |
readStream.on('data', function(chunk) { | |
data += chunk; | |
}).on('end', function() { | |
res.send(data) | |
}); | |
return next(); | |
}) | |
server.listen(8090, function() { | |
console.log('%s listening at %s', server.name, server.url); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment