Skip to content

Instantly share code, notes, and snippets.

@neilodiaz
Created November 12, 2021 08:02
Show Gist options
  • Save neilodiaz/682ca7bffc04caa5fbff571faff95e7f to your computer and use it in GitHub Desktop.
Save neilodiaz/682ca7bffc04caa5fbff571faff95e7f to your computer and use it in GitHub Desktop.
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