tools.js
Summary
No overview generated for 'tools.js'
function calculateBACKey(crypto, mrz2, keyno) {
var strbin = new ByteString(mrz2, ASCII);
var hash_input = strbin.bytes(0, 10);
hash_input = hash_input.concat(strbin.bytes(13, 7));
hash_input = hash_input.concat(strbin.bytes(21, 7));
print("Hash Input : " + hash_input.toString(ASCII));
var mrz_hash = crypto.digest(Crypto.SHA_1, hash_input);
print("MRZ Hash : " + mrz_hash);
var bb = new ByteBuffer(mrz_hash.bytes(0, 16));
bb.append(new ByteString("000000", HEX));
bb.append(keyno);
var keyval = crypto.digest(Crypto.SHA_1, bb.toByteString());
keyval = keyval.bytes(0, 16);
print("Value of Key : " + keyval);
var key = new Key();
key.setComponent(Key.DES, keyval);
return key;
}
function calculateBACKeyFrom3LineMRZ(crypto, mrz, keyno) {
var strbin = new ByteString(mrz, ASCII);
var hash_input = strbin.bytes(5, 10);
hash_input = hash_input.concat(strbin.bytes(30, 7));
hash_input = hash_input.concat(strbin.bytes(38, 7));
print("Hash Input : " + hash_input.toString(ASCII));
var mrz_hash = crypto.digest(Crypto.SHA_1, hash_input);
print("MRZ Hash : " + mrz_hash);
var bb = new ByteBuffer(mrz_hash.bytes(0, 16));
bb.append(new ByteString("000000", HEX));
bb.append(keyno);
var keyval = crypto.digest(Crypto.SHA_1, bb.toByteString());
keyval = keyval.bytes(0, 16);
print("Value of Key : " + keyval);
var key = new Key();
key.setComponent(Key.DES, keyval);
return key;
}
function SecureChannel(crypto, kenc, kmac, ssc) {
this.crypto = crypto;
this.kenc = kenc;
this.kmac = kmac;
this.ssc = ssc;
this.trace = false;
}
SecureChannel.prototype.enableTrace = function () {
this.trace = true;
}
SecureChannel.prototype.incssc = function () {
var c = this.ssc.bytes(4, 4).toUnsigned() + 1;
bb = new ByteBuffer(this.ssc.bytes(0, 4));
bb.append((c >> 24) & 0xFF);
bb.append((c >> 16) & 0xFF);
bb.append((c >> 8) & 0xFF);
bb.append((c ) & 0xFF);
this.ssc = bb.toByteString();
}
SecureChannel.prototype.wrap = function(apduToWrap) {
if (this.trace) {
print("Command-APDU to wrap :");
print(apduToWrap);
}
var b = new ByteBuffer();
var macb = new ByteBuffer();
var cla = apduToWrap.byteAt(0);
cla |= 0x0C;
b.append(cla);
b.append(apduToWrap.bytes(1, 3));
this.incssc();
macb.append(this.ssc);
macb.append(b.toByteString().pad(Crypto.ISO9797_METHOD_2));
var do87 = null;
var le = apduToWrap.bytes(apduToWrap.length - 1, 1);
if (apduToWrap.length > 5) {
var lc = apduToWrap.byteAt(4);
var plain = apduToWrap.bytes(5, lc);
plain = plain.pad(Crypto.ISO9797_METHOD_2);
if (this.trace) {
print("Input to cipher:");
print(plain);
}
var cipher = this.crypto.encrypt(this.kenc, Crypto.DES_CBC, plain, new ByteString("0000000000000000", HEX));
do87 = new ByteString("01", HEX);
do87 = do87.concat(cipher);
do87 = new TLV(0x87, do87, TLV.EMV);
do87 = do87.getTLV();
macb.append(do87);
if (apduToWrap.length == 5 + lc) {
le = new ByteString("", HEX);
}
} else if (apduToWrap.length == 4) {
le = new ByteString("", HEX);
}
var do97;
if (le.length > 0) {
do97 = new ByteString("9701", HEX);
do97 = do97.concat(le);
macb.append(do97);
} else {
do97 = new ByteString("", HEX);
}
if (this.trace) {
print("Input to MAC calculation :");
}
var macinput = macb.toByteString().pad(Crypto.ISO9797_METHOD_2);
if (this.trace) {
print(macinput);
}
var mac = this.crypto.sign(this.kmac, Crypto.DES_MAC_EMV, macinput);
if (this.trace) {
print("Calculated MAC :");
print(mac);
}
var macdo = new ByteString("8E08", HEX);
macdo = macdo.concat(mac);
if (do87 != null) {
b.append(do87.length + do97.length + macdo.length);
b.append(do87);
} else {
b.append(do97.length + macdo.length);
}
b.append(do97);
b.append(macdo);
if (le.length > 0) {
b.append(0);
}
if (this.trace) {
print("Wrapped Command-APDU :");
print(b.toByteString());
}
return(b.toByteString());
}
SecureChannel.prototype.unwrap = function(apduToUnwrap) {
if (this.trace) {
print("Response-APDU to unwrap :");
print(apduToUnwrap);
}
if (apduToUnwrap.length == 2) {
return(apduToUnwrap);
}
var b = new ByteBuffer();
var macb = new ByteBuffer();
this.incssc();
macb.append(this.ssc);
var tl = new TLVList(apduToUnwrap.left(apduToUnwrap.length - 2), TLV.EMV);
var mac = null;
for (i = 0; i < tl.length; i++) {
var t = tl.index(i);
if (t.getTag() == 0x8E) {
mac = t.getValue();
} else {
macb.append(t.getTLV());
}
}
if (mac == null) {
throw new GPError("SecureChannelCredential", GPError.OBJECT_NOT_FOUND, 0, "MAC data object missing");
}
if (this.trace) {
print(macb.toByteString());
}
if (!this.crypto.verify(this.kmac, Crypto.DES_MAC_EMV, macb.toByteString().pad(Crypto.ISO9797_METHOD_2), mac)) {
throw new GPError("SecureChannelCredential", GPError.CRYPTO_FAILED, 0, "MAC verification failed");
}
var t = tl.find(0x87);
if (t != null) {
var cryptogram = t.getValue();
var padding = cryptogram.byteAt(0);
cryptogram = cryptogram.right(cryptogram.length - 1);
if (padding != 0x01) {
throw new GPError("SecureChannelCredential", GPError.INVALID_MECH, padding, "Unsupported padding mode " + padding + " in cryptogram");
}
var plain = this.crypto.decrypt(this.kenc, Crypto.DES_CBC, cryptogram, new ByteString("0000000000000000", HEX));
for (i = plain.length - 1; (i > 0) && (plain.byteAt(i) != 0x80); i--);
b.append(plain.left(i));
}
var t = tl.find(0x81);
if (t != null) {
b.append(t.getValue());
}
var t = tl.find(0x99);
if (t == null) {
b.append(apduToUnwrap.right(2));
} else {
b.append(t.getValue());
}
if (this.trace) {
print("Unwrapped Response-APDU :");
print(b.toByteString());
}
return(b.toByteString());
}
function openSecureChannel(card, crypto, kenc, kmac) {
print("Performing mutual authentication");
var rndicc = card.sendApdu(0x00, 0x84, 0x00, 0x00, 0x08, [0x9000]);
var rndifd = crypto.generateRandom(8);
var kifd = crypto.generateRandom(16);
var plain = rndifd.concat(rndicc).concat(kifd);
print("Plain Block : " + plain);
var cryptogram = crypto.encrypt(kenc, Crypto.DES_CBC, plain, new ByteString("0000000000000000", HEX));
print("Cryptogram : " + cryptogram);
var mac = crypto.sign(kmac, Crypto.DES_MAC_EMV, cryptogram.pad(Crypto.ISO9797_METHOD_2));
print("MAC : " + mac);
var autresp = card.sendApdu(0x00, 0x82, 0x00, 0x00, cryptogram.concat(mac), 0);
if (card.SW != 0x9000) {
print("Mutual authenticate failed with " + card.SW.toString(16) + " \"" + card.SWMSG + "\". MRZ correct ?");
throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card did not accept MAC");
}
print("Response : " + autresp);
cryptogram = autresp.bytes(0, 32);
mac = autresp.bytes(32, 8);
if (!crypto.verify(kmac, Crypto.DES_MAC_EMV, cryptogram.pad(Crypto.ISO9797_METHOD_2), mac)) {
throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card MAC did not verify correctly");
}
plain = crypto.decrypt(kenc, Crypto.DES_CBC, cryptogram, new ByteString("0000000000000000", HEX));
print("Plain Block : " + plain);
if (!plain.bytes(0, 8).equals(rndicc)) {
throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card response does not contain matching RND.ICC");
}
if (!plain.bytes(8, 8).equals(rndifd)) {
throw new GPError("MutualAuthentication", GPError.CRYPTO_FAILED, 0, "Card response does not contain matching RND.IFD");
}
var kicc = plain.bytes(16, 16);
keyinp = kicc.xor(kifd);
var hashin = keyinp.concat(new ByteString("00000001", HEX));
var kencval = crypto.digest(Crypto.SHA_1, hashin);
kencval = kencval.bytes(0, 16);
print("Kenc : " + kencval);
var kenc = new Key();
kenc.setComponent(Key.DES, kencval);
var hashin = keyinp.concat(new ByteString("00000002", HEX));
var kmacval = crypto.digest(Crypto.SHA_1, hashin);
kmacval = kmacval.bytes(0, 16);
print("Kmac : " + kmacval);
var kmac = new Key();
kmac.setComponent(Key.DES, kmacval);
var ssc = rndicc.bytes(4, 4).concat(rndifd.bytes(4, 4));
print("SSC : " + ssc);
var sc = new IsoSecureChannel(crypto);
sc.setEncKey(kenc);
sc.setMacKey(kmac);
sc.setSendSequenceCounter(ssc);
return sc;
}
function writeFileOnDisk(name, content) {
var filename = GPSystem.mapFilename(name, GPSystem.USR);
print("Writing " + filename);
var file = new java.io.FileOutputStream(filename);
file.write(content);
file.close();
}
function readFileFromDisk(name) {
var filename = GPSystem.mapFilename(name, GPSystem.USR);
print("Reading " + filename);
var file = new java.io.FileInputStream(filename);
var content = new ByteBuffer();
var buffer = new ByteString(" ", ASCII);
var len;
while ((len = file.read(buffer)) >= 0) {
content.append(buffer.bytes(0, len));
}
file.close();
return(content.toByteString());
}
function lengthFromHeader(header) {
var value;
value = header.byteAt(1);
if (value > 0x82) {
throw new GPError("lengthfromheader()", GPError.INVALID_DATA, value, "");
}
switch(value) {
case 0x81:
value = header.byteAt(2) + 1;
break;
case 0x82:
value = (header.byteAt(2) << 8) + header.byteAt(3) + 2;
break;
}
return value + 2;
}
Documentation generated by
JSDoc on Tue Sep 3 22:29:38 2013