Skip to content

Instantly share code, notes, and snippets.

@bouroo
Last active January 8, 2025 12:49
Show Gist options
  • Save bouroo/ca8a41ac876f713fa8c64cb37c7b36a9 to your computer and use it in GitHub Desktop.
Save bouroo/ca8a41ac876f713fa8c64cb37c7b36a9 to your computer and use it in GitHub Desktop.
Thai National ID Card reader in NodeJS
/* eslint-disable no-console */
/*
* Thai National ID Card reader in NodeJS
*
* @author Kawin Viriyaprasopsook <kawin.vir@zercle.tech>
* @requires smartcard legacy-encoding hex2imagebase64
* @since 11/06/2019
*
*/
const smartcard = require('smartcard');
const legacy = require('legacy-encoding');
const hex2imagebase64 = require('hex2imagebase64');
const Devices = smartcard.Devices;
let devices;
try {
devices = new Devices();
} catch (err) {
console.error(err);
process.exit();
}
let card;
const cmdReq = [0x00, 0xC0, 0x00, 0x00];
const commands = {
cmdSelectThaiCard: [0x00, 0xa4, 0x04, 0x00, 0x08, 0xa0, 0x00, 0x00, 0x00, 0x54, 0x48, 0x00, 0x01],
cmdCID: [0x80, 0xb0, 0x00, 0x04, 0x02, 0x00, 0x0d],
cmdTHFullname: [0x80, 0xb0, 0x00, 0x11, 0x02, 0x00, 0x64],
cmdENFullname: [0x80, 0xb0, 0x00, 0x75, 0x02, 0x00, 0x64],
cmdBirth: [0x80, 0xb0, 0x00, 0xd9, 0x02, 0x00, 0x08],
cmdGender: [0x80, 0xb0, 0x00, 0xe1, 0x02, 0x00, 0x01],
cmdIssuer: [0x80, 0xb0, 0x00, 0xf6, 0x02, 0x00, 0x64],
cmdIssueDate: [0x80, 0xb0, 0x01, 0x67, 0x02, 0x00, 0x08],
cmdExpireDate: [0x80, 0xb0, 0x01, 0x6f, 0x02, 0x00, 0x08],
cmdAddress: [0x80, 0xb0, 0x15, 0x79, 0x02, 0x00, 0x64],
cmdPhoto: Array.from({ length: 15 }, (_, i) => [0x80, 0xb0, i + 0x01, 0x7b - i, 0x02, 0x00, 0xff])
};
devices.on('device-activated', event => {
const { device, devices: currentDevices } = event;
console.log(`Device '${device}' activated, devices: ${currentDevices}`);
device.on('card-inserted', async (event) => {
card = event.card;
console.log(`Card '${card.getAtr()}' inserted into '${event.device}'`);
// Check Thai National ID Card Version
const cardAttr = card.getAtr();
let localCmdReq = cardAttr.startsWith('3b67') ? [0x00, 0xC0, 0x00, 0x01] : cmdReq;
try {
await card.issueCommand(commands.cmdSelectThaiCard);
await fetchAndLogData(commands.cmdCID, localCmdReq, "Get CID: ");
await fetchAndLogData(commands.cmdTHFullname, localCmdReq, "Get thFullname: ");
await fetchAndLogData(commands.cmdENFullname, localCmdReq, "Get cmdENFullname: ");
await fetchAndLogData(commands.cmdBirth, localCmdReq, "Get dateOfBirth: ");
await fetchAndLogData(commands.cmdGender, localCmdReq, "Get gender: ");
await fetchAndLogData(commands.cmdIssuer, localCmdReq, "Get issuer: ");
await fetchAndLogData(commands.cmdIssueDate, localCmdReq, "Get issueDate: ");
await fetchAndLogData(commands.cmdExpireDate, localCmdReq, "Get expireDate: ");
await fetchAndLogData(commands.cmdAddress, localCmdReq, "Get address: ");
await getPhoto();
console.log('Done');
} catch (err) {
console.error(err);
process.exit();
}
});
device.on('card-removed', event => {
console.log(`Card removed from '${event.name}'`);
});
});
devices.on('device-deactivated', event => {
console.log(`Device '${event.device}' deactivated, devices: '${event.devices}'`);
});
async function fetchAndLogData(cmd, req, message) {
const data = await getData(cmd, req);
console.log(message);
console.log(legacy.decode(data.slice(0, -2), 'iso-8859-11'));
}
async function getPhoto() {
let hexPhoto = '';
for (const cmd of commands.cmdPhoto) {
const result = await getData(cmd, cmdReq);
hexPhoto += result.toString('hex').slice(0, -4);
}
console.log('Get photo: ');
console.log('data:image/jpg;base64, ' + hex2imagebase64(hexPhoto));
}
function getData(cmd, req = cmdReq) {
return card.issueCommand(cmd).then(() =>
card.issueCommand([...req, ...cmd.slice(-1)])
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment