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