UNPKG

js-gp

Version:

implementation of globalplatform

503 lines 20.4 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const crypto = require("crypto"); const apducmd_1 = require("./apducmd"); const admzip = require("adm-zip"); const GP_DEFAULT_KEY = new Buffer("404142434445464748494A4B4C4D4E4F", "hex"); const GP_DEFAULT_ICV = new Buffer("0000000000000000", "hex"); var GP_SECURITY_LEVEL; (function (GP_SECURITY_LEVEL) { GP_SECURITY_LEVEL[GP_SECURITY_LEVEL["NO_SECURITY"] = 0] = "NO_SECURITY"; GP_SECURITY_LEVEL[GP_SECURITY_LEVEL["COMMAND_MAC"] = 1] = "COMMAND_MAC"; GP_SECURITY_LEVEL[GP_SECURITY_LEVEL["COMMAND_MAC_EN"] = 3] = "COMMAND_MAC_EN"; })(GP_SECURITY_LEVEL = exports.GP_SECURITY_LEVEL || (exports.GP_SECURITY_LEVEL = {})); exports.defaultkey = { key_enc: GP_DEFAULT_KEY, key_mac: GP_DEFAULT_KEY, key_dek: GP_DEFAULT_KEY, key_ver: 0 }; var GP_PROTOCOL; (function (GP_PROTOCOL) { GP_PROTOCOL[GP_PROTOCOL["SCP_01"] = 0] = "SCP_01"; GP_PROTOCOL[GP_PROTOCOL["SCP_02"] = 1] = "SCP_02"; })(GP_PROTOCOL = exports.GP_PROTOCOL || (exports.GP_PROTOCOL = {})); class jsgp { constructor(ap) { this.apdu = ap; this.key_enc = GP_DEFAULT_KEY; this.key_mac = GP_DEFAULT_KEY; this.key_dek = GP_DEFAULT_KEY; this.apducmd = new apducmd_1.apducmd(); this.capcomponents = []; this.capcomponents.push("Header.cap"); this.capcomponents.push("Directory.cap"); this.capcomponents.push("Import.cap"); this.capcomponents.push("Class.cap"); this.capcomponents.push("Method.cap"); this.capcomponents.push("StaticField.cap"); this.capcomponents.push("Export.cap"); this.capcomponents.push("ConstantPool.cap"); this.capcomponents.push("RefLocation.cap"); this.capcomponents.push("Descriptor.cap"); } gp_security_ch_02(security_level, key = exports.defaultkey) { this.key_enc = key.key_enc; this.key_mac = key.key_mac; this.key_dek = key.key_dek; let command = new Buffer(13); command.writeUInt16BE(0x8050, 0); command.writeInt8(security_level, 2); command.write("00081122334455667788", 3, 20, "hex"); return this.apdu.transmit(command).then((initresp) => { if (initresp.length != 30) return; if (initresp.readUInt16BE(initresp.length - 2) != 0x9000) return; let cardkeydiver = initresp.slice(0, 10); let cardkeyinfo = initresp.slice(10, 12); let cardseqnum = initresp.readUInt16BE(12); let cardchallenge = initresp.slice(14, 20); let cardcrypto = initresp.slice(20, 28); let data = new Buffer(16); data.writeUInt16BE(cardseqnum, 2); data.write("000000000000000000000000", 4, 14, "hex"); data.write("0182", 0, 4, "hex"); this.session_key_enc = this.crypt_des_cbc(data, this.key_enc); data.write("0101", 0, 4, "hex"); this.session_key_mac = this.crypt_des_cbc(data, this.key_mac); data.write("0181", 0, 4, "hex"); this.session_key_dek = this.crypt_des_cbc(data, this.key_dek); data = new Buffer(24); data.write("1122334455667788", 0, 16, "hex"); data.writeUInt16BE(cardseqnum, 8); cardchallenge.copy(data, 10); data.write("8000000000000000", 16, 16, "hex"); let hostcardcrypto = this.crypt_des_cbc(data, this.session_key_enc); hostcardcrypto = hostcardcrypto.slice(hostcardcrypto.length - 8); if (!hostcardcrypto.equals(cardcrypto)) return; data.writeUInt16BE(cardseqnum, 0); cardchallenge.copy(data, 2); data.write("1122334455667788", 8, 16, "hex"); data.write("8000000000000000", 16, 16, "hex"); let hostcrypto = this.crypt_des_cbc(data, this.session_key_enc); hostcrypto = hostcrypto.slice(hostcrypto.length - 8); data = new Buffer(21); data.writeUInt16BE(0x8482, 0); data.writeUInt8(2, security_level); data.writeUInt16BE(3, 0x010); hostcrypto.copy(data, 5); let mac = this.gp_data_mac(data.slice(0, 13)); mac.copy(data, 13); return this.apdu.transmit(data).then((recv) => { if (recv.length != 2) return; if (recv.readUInt16BE(0) != 0x9000) return; this.security_level = security_level; this.store_num = 0; return recv; }); }); } gp_select(p1p2, name) { let command = new Buffer(name.length + 5); command.writeUInt16BE(0x00A4, 0); command.writeUInt16BE(p1p2, 2); command.writeUInt8(name.length, 4); name.copy(command, 5); return this.apdu.transmit(command); } gp_select_aid(aid) { return this.gp_select(0x0400, aid); } gp_delete(p1p2, data) { let command = new Buffer(data.length + 5); command.writeUInt16BE(0x80E4, 0); command.writeUInt16BE(p1p2, 2); command.writeUInt8(data.length, 4); data.copy(command, 5); return this.gp_cmd_mac(command); } gp_delete_aid(p1p2, aid) { let data = new Buffer(aid.length + 2); data.writeUInt8(0x4f, 0); data.writeUInt8(aid.length, 1); return this.gp_delete(p1p2, data); } gp_manage_ch(p1p2) { let command = new Buffer(5); command.writeUInt8(0x00, 0); command.writeUInt8(0x70, 1); command.writeUInt8(p1p2, 2); command.writeUInt8(0, 4); if ((p1p2 & 0xff00) == 0x8000) command = command.slice(0, 4); return this.apdu.transmit(command); } gp_install(p1p2, data) { let command = new Buffer(data.length + 5); command.writeUInt16BE(0x80E6, 0); command.writeUInt16BE(p1p2, 2); command.writeUInt8(data.length, 4); data.copy(command, 5); return this.gp_cmd_mac(command); } gp_install_ex(p1p2, aid1, aid2, aid3, pri, para, token) { let command = new Buffer(265); let offset = 0; command.writeUInt16BE(0x80E6, 0); command.writeUInt16BE(p1p2, 2); offset = 5; command.writeUInt8(aid1.length, offset); offset += 1; aid1.copy(command, offset); offset += aid1.length; command.writeUInt8(aid2.length, offset); offset += 1; aid2.copy(command, offset); offset += aid2.length; command.writeUInt8(aid3.length, offset); offset += 1; aid3.copy(command, offset); offset += aid3.length; if ((p1p2 & 0x0200) == 0x0000) { if (pri > 0x010000) { command.writeUInt8(0x03, offset); offset += 1; command.writeUInt8(pri >> 16, offset); offset += 1; command.writeUInt8(pri >> 8, offset); offset += 1; command.writeUInt8(pri >> 0, offset); offset += 1; } else { command.writeUInt8(0x01, offset); offset += 1; command.writeUInt8(pri, offset); offset += 1; } } command.writeUInt8(para.length, offset); offset += 1; para.copy(command, offset); offset += para.length; command.writeUInt8(token.length, offset); offset += 1; token.copy(command, offset); offset += token.length; command.writeUInt8(4, offset - 5); return this.gp_cmd_mac(command.slice(0, offset)); } gp_load(p1p2, data) { let command = new Buffer(data.length + 5); command.writeUInt16BE(0x80E8, 0); command.writeUInt16BE(p1p2, 2); command.writeUInt8(data.length, 4); data.copy(command, 5); return this.gp_cmd_mac(command); } gp_storedata(p1p2, data) { let command = new Buffer(data.length + 5); command.writeUInt16BE(0x80E2, 0); command.writeUInt16BE(p1p2, 2); command.writeUInt8(data.length, 4); data.copy(command, 5); return this.gp_cmd_mac(command); } gp_storedgi(p1, dgi, dgidata) { return __awaiter(this, void 0, void 0, function* () { let command = new Buffer(265); let data = new Buffer(dgidata.length + 20); let offset = 0; let transoff = 0; let translen = 0; let recv; data.writeUInt16BE(dgi, offset); offset += 1; if (dgidata.length < 0xff) { data.writeUInt8(dgidata.length, offset); offset += 1; } else { data.writeUInt8(0xff, offset); offset += 1; data.writeUInt16BE(dgidata.length, offset); offset += 2; } dgidata.copy(data, offset); offset += dgidata.length; while (transoff < offset) { translen = offset - transoff; if (translen > 0xFE) translen = 0xFE; let cmddata = data.slice(transoff, transoff + translen); if ((p1 & 0x60) == 0x60) { let padded = this.data_padding_ex(cmddata); cmddata = this.crypt_des_ecb(padded, this.session_key_dek); } command.writeUInt16BE(0x80E2, 0); command.writeUInt8(p1, 2); command.writeUInt8(this.store_num, 3); command.writeUInt8(translen, 4); cmddata.copy(command, 5); recv = yield this.gp_cmd_mac(command.slice(0, translen + 5)); if (recv.readUInt16BE(recv.length - 2) != 0x9000) return recv; transoff += translen; this.store_num += 1; } return recv; }); } gp_putkey(p1p2, data) { let command = new Buffer(data.length + 5); command.writeUInt16BE(0x80D8, 0); command.writeUInt16BE(p1p2, 2); command.writeUInt8(data.length, 4); data.copy(command, 5); return this.gp_cmd_mac(command); } gp_getdata(p1p2, data) { let command = new Buffer(data.length + 5); command.writeUInt16BE(0x80CA, 0); command.writeUInt16BE(p1p2, 2); command.writeUInt8(data.length, 4); data.copy(command, 5); return this.gp_cmd_mac(command); } gp_getstatus(p1p2, data) { let command = new Buffer(data.length + 5); command.writeUInt16BE(0x80F2, 0); command.writeUInt16BE(p1p2, 2); command.writeUInt8(data.length, 4); data.copy(command, 5); return this.gp_cmd_mac(command); } gp_setstatus(p1p2, data) { let command = new Buffer(data.length + 5); command.writeUInt16BE(0x80F0, 0); command.writeUInt16BE(p1p2, 2); command.writeUInt8(data.length, 4); data.copy(command, 5); return this.gp_cmd_mac(command); } gp_cap_data(capfile) { let capdata = []; let zip = new admzip(capfile); let zipEntries = zip.getEntries(); this.capcomponents.forEach(comp => { zipEntries.forEach(zipEntry => { if (this.strendswith(zipEntry.entryName, comp)) { capdata.push(zipEntry.getCompressedData()); } }); }); return Buffer.concat(capdata); } gp_cap_pkg_aid(capfile) { let pkgaid; let zip = new admzip(capfile); let zipEntries = zip.getEntries(); this.capcomponents.forEach(comp => { zipEntries.forEach(zipEntry => { if (this.strendswith(zipEntry.entryName, "Header.cap")) { pkgaid = zipEntry.getCompressedData(); pkgaid = pkgaid.slice(13); } }); }); return pkgaid; } gp_load_cap(capdata) { return __awaiter(this, void 0, void 0, function* () { let caplen = capdata.length; let buff = new Buffer(caplen + 5); let offset = 0; let transoff = 0; let translen = 0; let recv; let load_num = 0; if (caplen > 65535) { buff.writeUInt16BE(0xc483, 0); buff.writeUInt16BE(caplen >> 8, 2); buff.writeUInt8(caplen, 4); offset += 5; } else if (caplen > 255) { buff.writeUInt16BE(0xc482, 0); buff.writeUInt16BE(caplen, 2); offset += 4; } else if (caplen > 127) { buff.writeUInt16BE(0xc481, 0); buff.writeUInt8(caplen, 2); offset += 3; } else { buff.writeUInt8(0xc4, 0); buff.writeUInt8(caplen, 1); offset += 2; } capdata.copy(buff, offset); offset += caplen; while (transoff < offset) { translen = offset - transoff; let P1 = 0x80; if (translen > 230) { translen = 230; P1 = 0x00; } let cmddata = buff.slice(transoff, transoff + translen); recv = yield this.gp_load((P1 << 8) | (load_num & 0xff), cmddata); if (recv.readUInt16BE(recv.length - 2) != 0x9000) return recv; transoff += translen; load_num += 1; } return recv; }); } gp_cmd_mac(cmd) { let command; this.apducmd.parse(cmd); if (this.security_level == GP_SECURITY_LEVEL.NO_SECURITY) { command = new Buffer(cmd.length); this.apducmd.CLA &= 0xFB; command.writeUInt8(this.apducmd.CLA, 0); cmd.slice(1).copy(command, 1); } else if (this.security_level == GP_SECURITY_LEVEL.COMMAND_MAC) { command = new Buffer(cmd.length + 16); this.apducmd.CLA |= 0x04; this.apducmd.LC += 8; this.command_mac.copy(command, 0); command.writeUInt8(this.apducmd.CLA, 8); cmd.slice(1, 4).copy(command, 9); command.writeUInt8(this.apducmd.LC, 12); this.apducmd.DATA.copy(command, 13); this.command_mac = this.gp_data_mac(command.slice(0, this.apducmd.DATA.length + 13)); this.command_mac.copy(command, this.apducmd.DATA.length + 13); if (this.apducmd.LE != undefined) command.writeUInt8(this.apducmd.LE, command[12] + 13); command = command.slice(8); } else if (this.security_level == GP_SECURITY_LEVEL.COMMAND_MAC_EN) { command = new Buffer(cmd.length + 24); this.apducmd.CLA |= 0x04; this.apducmd.LC += 8; this.command_mac.copy(command, 0); command.writeUInt8(this.apducmd.CLA, 8); cmd.slice(1, 4).copy(command, 9); command.writeUInt8(this.apducmd.LC, 12); this.apducmd.DATA.copy(command, 13); this.command_mac = this.gp_data_mac(command.slice(0, this.apducmd.DATA.length + 13)); let crypt = this.gp_data_encrypt(this.apducmd.DATA); crypt.copy(command, 13); this.command_mac.copy(command, crypt.length + 13); command.writeUInt8(crypt.length + 8, 12); if (this.apducmd.LE != undefined) { command.writeUInt8(this.apducmd.LE, command[12] + 13); command = command.slice(8, command[12] + 14); } else { command = command.slice(8, command[12] + 13); } } return this.apdu.transmit(command); } crypt_des_ecb(msg, key, en = true) { let cipher; let Keys = []; let Result = []; Keys.push(key); if (key.length == 16) Keys.push(key.subarray(0, 8)); if (en === true) if (key.length == 8) cipher = crypto.createCipheriv("des-ecb", Buffer.concat(Keys), ""); else cipher = crypto.createCipheriv("des-ede3", Buffer.concat(Keys), ""); else if (key.length == 8) cipher = crypto.createDecipheriv("des-ecb", Buffer.concat(Keys), ""); else cipher = crypto.createDecipheriv("des-ede3", Buffer.concat(Keys), ""); cipher.setAutoPadding(false); Result.push(cipher.update(msg)); Result.push(cipher.final()); return Buffer.concat(Result); } crypt_des_cbc(msg, key, iv = GP_DEFAULT_ICV, en = true) { let cipher; let Keys = []; let Results = []; Keys.push(key); if (key.length == 16) Keys.push(key.subarray(0, 8)); if (en === true) if (key.length == 8) cipher = crypto.createCipheriv("des-cbc", Buffer.concat(Keys), iv); else cipher = crypto.createCipheriv("des-ede3-cbc", Buffer.concat(Keys), iv); else if (key.length == 8) cipher = crypto.createDecipheriv("des-cbc", Buffer.concat(Keys), iv); else cipher = crypto.createDecipheriv("des-ede3-cbc", Buffer.concat(Keys), iv); cipher.setAutoPadding(false); Results.push(cipher.update(msg)); Results.push(cipher.final()); return Buffer.concat(Results); } gp_data_mac(data) { let mac0 = this.crypt_des_cbc(data, this.command_mac.slice(0, 8), GP_DEFAULT_ICV); let mac1 = new Buffer(mac0.subarray(mac0.length - 16)); let mac2 = this.crypt_des_ecb(mac1, this.command_mac.slice(16, 16), false); return this.crypt_des_ecb(mac2, this.command_mac.slice(0, 16)); } gp_data_encrypt(data) { let padded = this.data_padding(data); return this.crypt_des_cbc(padded, this.session_key_enc); } gp_sensitive_encrypt(data) { let padded = this.data_padding_ex(data); return this.crypt_des_ecb(padded, this.session_key_dek); } data_padding(data) { let padlen = data.length % 8; if (padlen == 0) padlen = 8; let result = new Buffer(data.length + padlen); data.copy(result); result.writeUInt8(0x80, data.length); result.fill(0x00, data.length + 1); return result; } data_padding_ex(data) { let padlen = data.length % 8; if (padlen == 0) return data; let result = new Buffer(data.length + padlen); data.copy(result); result.writeUInt8(0x80, data.length); result.fill(0x00, data.length + 1); return result; } strendswith(s, t) { return (s.lastIndexOf(t) == (s.length - t.length)); } strstartswith(s, t) { return (s.indexOf(t) == 0); } } exports.jsgp = jsgp; //# sourceMappingURL=index.js.map