Connecting to an RFID/NFC (ACR122) reader from a JavaScript script
I'm using a script that reads the data off the e-Passport. However I need some help.
The script I have is from another program called SmartCardShell 3. This is the code:
/**
* ---------
* |.##> <##.| Open Smart Card Development Platform (www.openscdp.org)
* |# #|
* |# #| Copyright (c) 1999-2006 CardContact Software & System Consulting
* |'##> <##'| Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
* ---------
*
* This file is part of OpenSCDP.
*
* OpenSCDP is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* OpenSCDP is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenSCDP; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* @fileoverview Dump the content of a passport with Basic Access Control to file
*
* Before running this script, please make sure that the variable mrz2 is set
* to the second line of the machine readable zone on your passport.
*/
// MRZ of silver data set
//
// P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<
// L898902C<3UTO6908061F9406236ZE184226B<<<<<14
// '-DocNo--' '-DoB-' '-DoE-'
//var mrz2 = "L898902C<3UTO6908061F9406236ZE184226B<<<<<14";
// Harvinder Khaira MRZ:
// P<GBRKHAIRA<<HARVINDER<SINGH<<<<<<<<<<<<<<<<
// 4604503228GBR8711164M1711291<<<<<<<<<<<<<<06
var mrz2 = "4604503228GBR8711164M1711291<<<<<<<<<<<<<<06";
// MRZ of Tsukuba data set
//
// WG30004036UTO6007078M0511014<<<<<<<<<<<<<<06
// '-DocNo--' '-DoB-' '-DoE-'
// var mrz2 = "WG30004036UTO6007078M0511014<<<<<<<<<<<<<<06";
//============================================tools.js=========================
// Import some tools
/*
* ---------
* |.##> <##.| Open Smart Card Development Platform (www.openscdp.org)
* |# #|
* |# #| Copyright (c) 1999-2006 CardContact Software & System Consulting
* |'##> <##'| Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
* ---------
*
* This file is part of OpenSCDP.
*
* OpenSCDP is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* OpenSCDP is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenSCDP; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Tools for accessing a ICAO conform Machine Readable Travel Document
*/
/*
* Calculate a single Basic Access Control (BAC) key from the second
* line of the Machine Readable Zone (MRZ).
*
* The function extracts the Document Number, Date of Birth and Date of Expiration
* from the second line of the machine readable zone
*
* E.g. MRZ of Silver Data Set
* P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<
* L898902C<3UTO6908061F9406236ZE184226B<<<<<14
* '-DocNo--' '-DoB-' '-DoE-'
*
* This extract is then hashed, concatenated with a key number and
* hashed again.
* crypto Crypto object used for hashing
* mrz2 String containing second line of MRZ
* keyno Number of key to calculate (1 for Kenc and 2 for Kmac)
*
* Returns Key object
*/
function calculateBACKey(crypto, mrz2, keyno) {
// Convert to byte string
var strbin = new ByteString(mrz2, ASCII);
// Extract Document Number, Date of Birth and Date of Expiration
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));
printlnln("Hash Input : " + hash_input.toString(ASCII));
// Hash input
var mrz_hash = crypto.digest(Crypto.SHA_1, hash_input);
printlnln("MRZ Hash : " + mrz_hash);
// Extract first 16 byte and append 00000001 or 00000002
var bb = new ByteBuffer(mrz_hash.bytes(0, 16));
bb.append(new ByteString("000000", HEX));
bb.append(keyno);
// Hash again to calculate key value
var keyval = crypto.digest(Crypto.SHA_1, bb.toByteString());
keyval = keyval.bytes(0, 16);
printlnln("Value of Key : " + keyval);
var key = new Key();
key.setComponent(Key.DES, keyval);
return key;
}
// The SecureChannel object is required as a credential for the CardFile objects
// SecureChannel objects must at least implement a wrap() method. The unwrap()
// method is optional, but called when defined
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;
}
//
// Increment send sequence counter
//
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();
}
//
// Wrap command-APDU with secure messaging
//
SecureChannel.prototype.wrap = function(apduToWrap) {
if (this.trace) {
printlnln("Command-APDU to wrap :");
printlnln(apduToWrap);
}
var b = new ByteBuffer();
var macb = new ByteBuffer();
// Transform CLA byte and add header
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) {
printlnln("Input to cipher:");
printlnln(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) {
printlnln("Input to MAC calculation :");
}
var macinput = macb.toByteString().pad(Crypto.ISO9797_METHOD_2);
if (this.trace) {
printlnln(macinput);
}
var mac = this.crypto.sign(this.kmac, Crypto.DES_MAC_EMV, macinput);
if (this.trace) {
printlnln("Calculated MAC :");
printlnln(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) {
printlnln("Wrapped Command-APDU :");
printlnln(b.toByteString());
}
return(b.toByteString());
}
//
// Unwrap response-APDU with secure messaging
//
SecureChannel.prototype.unwrap = function(apduToUnwrap) {
if (this.trace) {
printlnln("Response-APDU to unwrap :");
printlnln(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) {
printlnln(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) {
printlnln("Unwrapped Response-APDU :");
printlnln(b.toByteString());
}
return(b.toByteString());
}
/*
* Open secure channel using basic access control keys
*
* card Card object for access to passport
* crypto Crypto object to be used for cryptographic operations
* kenc Kenc key
* kmac Kmac key
*
* Returns Open secure channel object
*/
function openSecureChannel(card, crypto, kenc, kmac) {
// Perform mutual authentication procedure
printlnln("Performing mutual authentication");
var rndicc = card.sendApdu(0x00, 0x84, 0x00, 0x00, 0x08, [0x9000]);
//printlnln("======RNDicc : " + rndicc);
var rndifd = crypto.generateRandom(8);
//println("======RNDifd : " + rndifd);
var kifd = crypto.generateRandom(16);
var plain = rndifd.concat(rndicc).concat(kifd);
println("Plain Block : " + plain);
var cryptogram = crypto.encrypt(kenc, Crypto.DES_CBC, plain, new ByteString("0000000000000000", HEX));
println("Cryptogram : " + cryptogram);
var mac = crypto.sign(kmac, Crypto.DES_MAC_EMV, cryptogram.pad(Crypto.ISO9797_METHOD_2));
println("MAC : " + mac);
var autresp = card.sendApdu(0x00, 0x82, 0x00, 0x00, cryptogram.concat(mac), 0);
if (card.SW != 0x9000) {
println("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");
}
println("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));
println("Plain Block : " + plain);
//var iccifd = rndicc.concat(rndifd);
//println ("==RNDicc + RNDifd : " + iccifd);
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);
println("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);
println("Kmac : " + kmacval);
var kmac = new Key();
kmac.setComponent(Key.DES, kmacval);
var ssc = rndicc.bytes(4, 4).concat(rndifd.bytes(4, 4));
println("SSC : " + ssc);
// Disable to use script-secure messaging secure messaging
var sc = new IsoSecureChannel(crypto);
sc.setEncKey(kenc);
sc.setMacKey(kmac);
sc.setSendSequenceCounter(ssc);
return sc;
//
// Enable to use script-secure messaging secure messaging
// return new SecureChannel(crypto, kenc, kmac, ssc);
//
}
/*
* Write a byte string object to file
*
* The filename is mapped to the location of the script
*
* name Name of file
* content ByteString content for file
*
*/
function writeFileOnDisk(name, content) {
// Map filename
var filename = GPSystem.mapFilename(name, GPSystem.USR);
println("Writing " + filename);
var file = new java.io.FileOutputStream(filename);
file.wr开发者_如何学Cite(content);
file.close();
}
/*
* Read a byte string object from file
*
* The filename is mapped to the location of the script
*
* name Name of file
*
*/
function readFileFromDisk(name) {
// Map filename
var filename = GPSystem.mapFilename(name, GPSystem.USR);
println("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());
}
/*
* Extract the length of the file from the TLV encoding at the beginning of the
* file
*
* header First bytes read from file
*
* Return Total length of TLV object
*/
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;
}
//===========================================end of tools.js===================
/*
* Read file from passport and save to disk
*
*/
function handleFile(secureChannel, lds, name, fid) {
printlnln("Reading " + name + " (" + fid + ")...");
// Select file
var ef = new CardFile(lds, ":" + fid);
if (secureChannel) {
// Set secure channel as credential for read access
ef.setCredential(CardFile.READ, Card.ALL, secureChannel);
}
// Read first 4 bytes of file
var res = ef.readBinary(0, 4);
// Determine file length from TLV header
var len = lengthFromHeader(res);
// Read complete file
var res = ef.readBinary(0, len);
printlnln("Content");
printlnln(res);
writeFileOnDisk(name + ".bin", res);
return res;
}
/*
* Save picture from DG2
*
*/
function savePicture(dg2) {
// Save picture to .jpeg file
var tlv = new ASN1(dg2);
var bin = tlv.get(0).get(1).get(1).value;
var offset = bin.find(new ByteString("FFD8", HEX));
if (offset >= 0) {
writeFileOnDisk("face.jpg", bin.bytes(offset));
}
}
// Create card and crypto object
var card = new Card( /* somehow connect to ACR122 reader */);
card.reset(Card.RESET_COLD);
var crypto = new Crypto();
// Select LDS application
var lds = new CardFile(card, "#A0000002471001");
var secureChannel = null;
// Try reading EF_COM to figure out if BAC is needed
card.sendApdu(0x00, 0xB0, 0x9E, 0x00, 0x01);
if (card.SW != 0x9000) {
// Calculate kenc and kmac for mutual authentication from the MRZ data
printlnln("Trying BAC with MRZ2=" + mrz2);
var kenc = calculateBACKey(crypto, mrz2, 1);
var kmac = calculateBACKey(crypto, mrz2, 2);
// Dummy to load crypto libraries (Saves some time later)
crypto.encrypt(kenc, Crypto.DES_CBC, new ByteString("0000000000000000", HEX), new ByteString("0000000000000000", HEX));
secureChannel = openSecureChannel(card, crypto, kenc, kmac);
/* Only works with script based secure messaging. See tools.js for details
secureChannel.enableTrace();
*/
// Enable SELECT commands to be send in secure messaging
// lds.setCredential(CardFile.SELECT, CardFile.ALL, secureChannel);
/*
card.setCredential(secureChannel);
var resp = card.sendSecMsgApdu(Card.ALL, 0x00, 0xA4, 0x02, 0x0C, new ByteString("011E", HEX));
println(resp);
*/
}
handleFile(secureChannel, lds, "EF_COM", "1E");
handleFile(secureChannel, lds, "EF_DG1", "01");
var dg2 = handleFile(secureChannel, lds, "EF_DG2", "02");
savePicture(dg2);
handleFile(secureChannel, lds, "EF_SOD", "1D");
I have incorporated 2 scripts (dumpmrtd.js and tools.js) together.
The issue is that towards the bottom where:
// Create card and crypto object
var card = new Card( /* somehow connect to ACR122 reader */);
card.reset(Card.RESET_COLD);
In order to create a new Card object, it requires a String reader. This is my problem! I do not know how to obtain this "reader".
Anyone have any ideas?
Thanks in advance
**PS. sorry for the terrible indentation/code quotation. This is the first time im actually posting on this website
精彩评论