Skip to content

Instantly share code, notes, and snippets.

@mika76
Forked from RomanKharin/foms_smartcard.js
Created October 18, 2021 12:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mika76/a73edbbbf3eb1a0470c9807c41aad17e to your computer and use it in GitHub Desktop.
Save mika76/a73edbbbf3eb1a0470c9807c41aad17e to your computer and use it in GitHub Desktop.
// Read FOMS (Russian Health Card) http://www.openscdp.org/scsh3/index.html
// with emulator
// Romam Kharin <romiq.kh@gmail.com>, 2014
// based on explore.js
function SimFOMS () {
this.fsel = 0; // 0 - none, num - num
this.data0201 = new ByteString("", HEX);
this.data0202 = new ByteString("", HEX);
}
SimFOMS.prototype.retBinary = function(capdu, data) {
print("Read " + capdu);
// calc offset
var offset = capdu.bytes(2, 2).toUnsigned();
if (offset & 0x8000) // short EF unsupported
return new ByteString("6B00", HEX); // Wrong parametres
var len = capdu.bytes(4, 1).toUnsigned();
print("Offset " + offset + ", len " + len);
return data.bytes(offset, len).concat(new ByteString("9000", HEX));
}
// The simulator needs a processAPDU method
SimFOMS.prototype.processAPDU = function(capdu) {
print("Recv: " + capdu);
if (new ByteString("00A4040C09666f6d735f726f6f74", HEX).equals(capdu))
return new ByteString("9000", HEX);
if (new ByteString("00A4040C07464f4d535f4944", HEX).equals(capdu))
return new ByteString("9000", HEX);
if (new ByteString("00A4020C020201", HEX).equals(capdu)) {
this.fsel = 0x0201;
return new ByteString("9000", HEX);
}
if (new ByteString("00A4020C020202", HEX).equals(capdu)) {
this.fsel = 0x0202;
return new ByteString("9000", HEX);
}
if (capdu.left(2).equals(new ByteString("00B0", HEX))) {
if (this.fsel == 0x0201) return this.retBinary(capdu, this.data0201);
if (this.fsel == 0x0202) return this.retBinary(capdu, this.data0202);
}
return new ByteString("6700", HEX); // Wrong length
}
// Reset
SimFOMS.prototype.reset = function(type) {
print("Reset type: " + type);
this.fsel = 0;
var atr = new ByteString("3BF71300008131FE45464F4D534F4D53A9",
HEX); // FOMSOMS
return atr;
}
// Uncomment following 3 lines to enable simulation
//var sa = new CardSimulationAdapter("JCOPSimulation", "8050");
//sa.setSimulationObject(new SimFOMS());
//sa.start();
// Start explore card
load("tools/OutlineCore.js");
function OutlineCard() {
this.Tags = {
"ICCID": [
["ICCID_ROOT", 0x0000, 0, ""],
["ISSUER_CODE", 0x0000, 1, ""],
["ICCID_ISSUER_DATA", 0x0000, 2, ""],
],
"OwnerInfo": [
["ROOT", 0x0201, 2, ""],
["Identity_1", 0x0201, 0x21, "utf8"],
["Identity_2", 0x0201, 0x22, "utf8"],
["Identity_3", 0x0201, 0x23, "utf8"],
["BirthDate", 0x0201, 0x24, "date"],
["Sex", 0x0201, 0x25, "sex"],
["PolicyNumber", 0x0201, 0x26, "utf8"],
["SNILS", 0x0201, 0x27, "utf8"],
["ExpireDate", 0x0201, 0x28, "date"],
["BirthPlace", 0x0201, 0x29, "utf8"],
["RegistrationDate", 0x0201, 0x2a, "date"],
],
"OwnerPhoto": [
["ROOT", 0x0000, 0x40, ""],
["Type", 0x0000, 0x41, ""],
["Image", 0x0000, 0x42, ""],
],
"CardID": [
["ROOT", 0x0000, 1, ""],
["UniqNumber", 0x0000, 0x11, ""],
["CardType", 0x0000, 0x12, ""],
["CardVersion", 0x0000, 0x13, ""],
["InstitutionID", 0x0000, 0x14, ""],
["AdditionalInfo", 0x0000, 0x15, ""],
],
"Citizenship": [
["ROOT", 0x0201, 0x30, ""],
["CountryCode", 0x0201, 0x31, "utf8"],
["CountryCyrillicName", 0x0201, 0x32, "utf8"],
],
"SMO": [
["Policy_ROOT", 0x0000, 2, ""],
["ROOT", 0x0000, 4, ""],
["OGRN", 0x0000, 0x51, ""],
["OKATO", 0x0000, 0x52, ""],
["InsuranceStartDate", 0x0000, 0x53, "date"],
["InsuranceExpireDate", 0x0000, 0x54, "date"],
["EDS", 0x0000, 0x60, ""],
["EDS_Value", 0x0000, 0x61, ""],
["EDS_Certificate", 0x0000, 0x62, ""],
],
}
this.tag_ref = {};
for (var sect in this.Tags) {
var lst = this.Tags[sect];
for (var i = 0; i < lst.length; i++) {
this.tag_ref[[lst[i][1], lst[i][2]]] = [sect, lst[i][0], lst[i][3]];
}
}
// Create card object
var card = new Card(_scsh3.reader);
this.atr = card.reset(Card.RESET_COLD);
this.card = card;
// Create OutlineNode object and register in object
var view = new OutlineNode("Card");
view.setUserObject(this);
this.view = view;
}
OutlineCard.prototype.printTLV = function(filenum, tlv, sub) {
var tt = tlv.getTag() >> 8;
var ta = tlv.getTag() & 0xFF;
var value = tlv.getValue();
if (this.tag_ref.hasOwnProperty([filenum, ta])) {
var namtp = this.tag_ref[[filenum, ta]];
var namsect = sub + namtp[0] + "_" + namtp[1];
if (namtp[2] == "utf8") {
print(namsect + ": " + value.toString(UTF8));
} else if (namtp[2] == "ascii") {
print(namsect + ": " + value.toString(ASCII));
} else if (namtp[2] == "sex") {
var s = "hex=" + value;
if (value.toString(HEX) == "01") s = "M";
if (value.toString(HEX) == "02") s = "F";
print(namsect + ": " + s);
} else if (namtp[2] == "date") {
var s = "" + value.toString(HEX);
print(namsect + ": " + s.substr(0, 2) + "." +
s.substr(2, 2) + "." + s.substr(4));
} else
print(namsect + ": " + tt + "," + ta + "(" + tlv.getL() + ")=" + tlv.getValue());
} else {
print(tt + "," + ta + "(" + tlv.getL() + ")=" + value.toString(HEX));
}
if (tt == 127) {
// subarray
var sublst = new TLVList(new ByteString(value.toString(ASCII),
ASCII), TLV.EMV);
for (var i = 0; i < sublst.length; i++) {
var stlv = sublst.index(i);
this.printTLV(filenum, stlv, sub + " ");
}
}
}
OutlineCard.prototype.printFile = function(name, filenum, data) {
print("Info: " + name + " (" + data.length + ")");
for (var i = 0; i < data.length; i++) {
var tlv = data.index(i);
this.printTLV(filenum, tlv, "");
}
}
OutlineCard.prototype.expandListener = function() {
if (this.expanded)
return;
var view = this.view;
var card = this.card;
// Display ATR
var atrnode = new OutlineATR(this.atr);
view.insert(atrnode.view);
this.expanded = true;
// build list
for (var key in this.Tags) {
var node = new OutlineNode(key);
view.insert(node);
}
// Select foms_root
try {
var fl = new CardFile(card, "#666f6d735f726f6f74"); // foms_root
this.foms_root = new OutlineNode("foms_root");
view.insert(this.foms_root);
}
catch(e) {
print("Card does not seem to have a foms_root\n" + e);
}
// Select FOMS_FD
try {
var fl = new CardFile(card, "#464f4d535f4944"); // FOMS_FD
this.foms_fd = new OutlineNode("FOMS_FD");
this.foms_root.insert(this.foms_fd);
}
catch(e) {
print("Card does not seem to have a FOMS_FD\n" + e);
}
// Select file 0x0201
try {
var fl = new CardFile(card, ":0201");
print("FCP: " + fl.getFCPBytes());
var info1 = fl.readBinary(0, 1);
print("Info 1: " + info1);
var info2 = fl.readBinary(1, 1);
print("Info 2: " + info2);
var infolen = fl.readBinary(2, 1).toUnsigned();
print("Info len: " + infolen);
var info = new TLVList(fl.readBinary(3, infolen), TLV.EMV);
}
catch(e) {
print("Card does not seem to have a info\n" + e);
}
this.printFile("0x0201", 0x0201, info);
try {
var fl = new CardFile(card, ":0202");
print("FCP: " + fl.getFCPBytes());
var info1 = fl.readBinary(0, 1);
print("Info 1: " + info1);
var info2 = fl.readBinary(1, 1);
print("Info 2: " + info2);
var infolen = fl.readBinary(2, 2).toUnsigned() + 4;
print("Info len: " + infolen);
var off = 0;
var data = new ByteString("", ASCII);
while (infolen > 0) {
var rlen = infolen;
if (rlen > 0xdc) {
rlen = 0xdc;
}
infolen -= rlen;
data = data.concat(fl.readBinary(off, rlen));
off += rlen;
}
//print("Info len: " + infolen);
//var info = new TLVList(data, TLV.EMV);
//this.printFile("0x0202", 0x0202, info);
}
catch(e) {
print("Card does not seem to have a info-2\n" + e);
}
}
// Outline root node
var cardoutline = new OutlineCard();
cardoutline.view.show();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment