      1 /**
      2  *  ---------
      3  * |.##> <##.|  Open Smart Card Development Platform (www.openscdp.org)
      4  * |#       #|
      5  * |#       #|  Copyright (c) 1999-2018 CardContact Software & System Consulting
      6  * |'##> <##'|  Andreas Schwier, 32429 Minden, Germany (www.cardcontact.de)
      7  *  ---------
      8  *
      9  *  This file is part of OpenSCDP.
     10  *
     11  *  OpenSCDP is free software; you can redistribute it and/or modify
     12  *  it under the terms of the GNU General Public License version 2 as
     13  *  published by the Free Software Foundation.
     14  *
     15  *  OpenSCDP is distributed in the hope that it will be useful,
     16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     18  *  GNU General Public License for more details.
     19  *
     20  *  You should have received a copy of the GNU General Public License
     21  *  along with OpenSCDP; if not, write to the Free Software
     22  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     23  *
     24  * @fileoverview AuthenticationObject - Password, PIN or key container for external authentication
     25  */
     26
     27 var APDU                = require('scsh/cardsim/APDU').APDU;
     28
     29
     30
     31 /**
     32  * Create an adapter to decode a APDU for data unit handling
     33  *
     34  * @class Adapter class to decode APDUs for data unit handling
     35  * @constructor
     36  * @param {APDU} apdu the APDU to decode
     37  */
     38 function DataUnitAPDU(apdu) {
     39 	this.apdu = apdu;
     40
     41 	var p1 = apdu.getP1();
     42
     43 	if ((this.apdu.getINS() & 1) == 0) {		// Even instruction
     44 		if ((p1 & 0x80) == 0x80) {				// SFI in P1
     45 			this.offset = this.apdu.getP2();
     46 			this.sfi = p1 & 0x1F;
     47 		} else {
     48 			this.offset = (p1 << 8) + this.apdu.getP2();
     49 		}
     50 		this.data = apdu.getCData();
     51 	} else {									// Odd instruction
     52 		var p2 = apdu.getP2();
     53 		var fid = (p1 << 8) + p2;				// FID in P1 P2
     54 		// If bits b16 - b6 are all 0 and b5 - b1 are not all equal, then we have an SFI
     55 		if (((fid & 0xFFE0) == 0) && ((fid & 0x1F) >= 1) && ((fid & 0x1F) <= 30)) {
     56 			this.sfi = fid & 0x1F;
     57 		} else if (fid != 0) {					// FID = 0000 means current file
     58 			var bb = new ByteBuffer();
     59 			bb.append(p1);
     60 			bb.append(p2);
     61 			this.fid = bb.toByteString();
     62 		}
     63
     64 		var a = this.apdu.getCDataAsTLVList();
     65
     66 		if ((a.length < 1) || (a.length > 2)) {
     67 			throw new GPError("DataUnitAPDU", GPError.INVALID_DATA, APDU.SW_INVDATA, "Invalid data for odd instruction data handling command, less than one or more than two elements in TLV");
     68 		}
     69
     70 		var o = a.index(0);
     71 		if (o.getTag() != 0x54) {
     72 			throw new GPError("DataUnitAPDU", GPError.INVALID_DATA, APDU.SW_INVDATA, "Invalid data for odd instruction data handling command, first tag must be '54' offset");
     73 		}
     74
     75 		this.offset = o.getValue().toUnsigned();
     76
     77 		if (a.length == 2) {
     78 			var o = a.index(1);
     79 			var t = o.getTag();
     80 			if ((t != 0x53) && (t != 0x73)) {
     81 				throw new GPError("DataUnitAPDU", GPError.INVALID_DATA, APDU.SW_INVDATA, "Invalid data for odd instruction data handling command, second tag must be '53' or '73'");
     82 			}
     83
     84 			this.data = o.getValue();
     85 		}
     86 	}
     87 }
     88
     89 exports.DataUnitAPDU = DataUnitAPDU;
     90
     91
     92
     93 /**
     94  * Gets the short file identifier, if one defined
     95  *
     96  * @type Number
     97  * @return the short file identifier in the range 1 to 30 or -1 if not defined
     98  */
     99 DataUnitAPDU.prototype.getSFI = function() {
    100 	if (typeof(this.sfi) == "undefined") {
    101 		return -1;
    102 	}
    103 	return this.sfi;
    104 }
    105
    106
    107
    108 /**
    109  * Gets the file identifier, if one defined
    110  *
    111  * @type ByteString
    112  * @return the file identifier or null if not defined
    113  */
    114 DataUnitAPDU.prototype.getFID = function() {
    115 	if (typeof(this.fid) == "undefined") {
    116 		return null;
    117 	}
    118 	return this.fid;
    119 }
    120
    121
    122
    123 /**
    124  * Gets the offset
    125  *
    126  * @type Number
    127  * @return the offset to read from or write to
    128  */
    129 DataUnitAPDU.prototype.getOffset = function() {
    130 	return this.offset;
    131 }
    132
    133
    134
    135 /**
    136  * Get the command data
    137  *
    138  * @type ByteString
    139  * @return the command data
    140  */
    141 DataUnitAPDU.prototype.getCData = function() {
    142 	if (!this.hasCData()) {
    143 		throw new GPError("APDU", GPError.INVALID_DATA, APDU.SW_INVDATA, "No data in command APDU");
    144 	}
    145 	return this.data;
    146 }
    147
    148
    149
    150 /**
    151  * Returns true if command data in contained in the APDU
    152  *
    153  * @type boolean
    154  * @returns true if command data contained
    155  */
    156 DataUnitAPDU.prototype.hasCData = function() {
    157 	return ((typeof(this.data) != "undefined") && (this.data != null));
    158 }
    159
    160
    161
    162 /**
    163  * Simple Unit Test
    164  */
    165 DataUnitAPDU.test = function() {
    166 	var apdu = new APDU(0x00, 0xB0, 0, 0, 0);
    167 	var dh = new DataUnitAPDU(apdu);
    168 	assert(dh.getOffset() == 0);
    169 	assert(!dh.hasCData());
    170
    171 	var apdu = new APDU(0x00, 0xB0, 0x7F, 0xFF, 0);
    172 	var dh = new DataUnitAPDU(apdu);
    173 	assert(dh.getOffset() == 0x7FFF);
    174 	assert(!dh.hasCData());
    175
    176 	var apdu = new APDU(0x00, 0xB0, 0x80, 0, 0);
    177 	var dh = new DataUnitAPDU(apdu);
    178 	assert(dh.getOffset() == 0);
    179 	assert(!dh.hasCData());
    180
    181 	var apdu = new APDU(0x00, 0xB0, 0x80, 0xFF, 0);
    182 	var dh = new DataUnitAPDU(apdu);
    183 	assert(dh.getOffset() == 0xFF);
    184 	assert(!dh.hasCData());
    185
    186 	var apdu = new APDU(0x00, 0xB1, 0, 0, new ByteString("540100", HEX), 0);
    187 	var dh = new DataUnitAPDU(apdu);
    188 	assert(dh.getOffset() == 0);
    189 	assert(!dh.hasCData());
    190
    191 	var apdu = new APDU(0x00, 0xB1, 0, 0, new ByteString("5401FF", HEX), 0);
    192 	var dh = new DataUnitAPDU(apdu);
    193 	assert(dh.getOffset() == 0xFF);
    194 	assert(!dh.hasCData());
    195
    196 	var apdu = new APDU(0x00, 0xB1, 0, 0, new ByteString("540401000000", HEX), 0);
    197 	var dh = new DataUnitAPDU(apdu);
    198 	assert(dh.getOffset() == 0x01000000);
    199 	assert(!dh.hasCData());
    200
    201 	var data = new ByteString("1234", ASCII);
    202
    203 	var apdu = new APDU(0x00, 0xD6, 0, 0, data);
    204 	var dh = new DataUnitAPDU(apdu);
    205 	assert(dh.getOffset() == 0);
    206 	assert(dh.hasCData());
    207 	assert(dh.getCData().equals(data));
    208
    209 	var apdu = new APDU(0x00, 0xD6, 0x7F, 0xFF, data);
    210 	var dh = new DataUnitAPDU(apdu);
    211 	assert(dh.getOffset() == 0x7FFF);
    212 	assert(dh.hasCData());
    213 	assert(dh.getCData().equals(data));
    214
    215 	var apdu = new APDU(0x00, 0xD6, 0x80, 0, data);
    216 	var dh = new DataUnitAPDU(apdu);
    217 	assert(dh.getOffset() == 0);
    218 	assert(dh.hasCData());
    219 	assert(dh.getCData().equals(data));
    220
    221 	var apdu = new APDU(0x00, 0xD6, 0x80, 0xFF, data);
    222 	var dh = new DataUnitAPDU(apdu);
    223 	assert(dh.getOffset() == 0xFF);
    224 	assert(dh.hasCData());
    225 	assert(dh.getCData().equals(data));
    226
    227 	var apdu = new APDU(0x00, 0xD7, 0, 0, (new ByteString("5401005304", HEX)).concat(data), 0);
    228 	var dh = new DataUnitAPDU(apdu);
    229 	assert(dh.getOffset() == 0);
    230 	assert(dh.hasCData());
    231 	assert(dh.getCData().equals(data));
    232
    233 	var apdu = new APDU(0x00, 0xD7, 0, 0, (new ByteString("5401FF5304", HEX)).concat(data), 0);
    234 	var dh = new DataUnitAPDU(apdu);
    235 	assert(dh.getOffset() == 0xFF);
    236 	assert(dh.hasCData());
    237 	assert(dh.getCData().equals(data));
    238
    239 	var apdu = new APDU(0x00, 0xD7, 0, 0, (new ByteString("5404010000005304", HEX)).concat(data), 0);
    240 	var dh = new DataUnitAPDU(apdu);
    241 	assert(dh.getOffset() == 0x01000000);
    242 	assert(dh.hasCData());
    243 	assert(dh.getCData().equals(data));
    244 }
    245