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