js-gp
Version:
implementation of globalplatform
503 lines • 20.4 kB
JavaScript
"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