1 /** 2 * --------- 3 * |.##> <##.| Open Smart Card Development Platform (www.openscdp.org) 4 * |# #| 5 * |# #| Copyright (c) 1999-2009 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 Implementation of ISO 7816-4 APDU processing 25 */ 26 27 28 29 /** 30 * Create an APDU 31 * 32 * <p>This constructor supports the signatures</p> 33 * <ul> 34 * <li>APDU(ByteString command)</li> 35 * <li>APDU(Number cla, Number ins, Number p1, Number p2)</li> 36 * <li>APDU(Number cla, Number ins, Number p1, Number p2, data)</li> 37 * <li>APDU(Number cla, Number ins, Number p1, Number p2, data, Ne)</li> 38 * </ul> 39 * @class Class implementing support for command and response APDUs 40 * @constructor 41 * @param {ByteString} command the command APDU 42 * @param {Number} cla the class byte 43 * @param {Number} ins the instruction byte 44 * @param {Number} p1 the first parameter 45 * @param {Number} p2 the second parameter 46 * @param {ByteString} data the data field (optional) 47 * @param {Number} Ne the number of expected bytes (optional) 48 */ 49 function APDU() { 50 if (arguments.length > 0) { 51 var arg = arguments[0]; 52 if (arg instanceof ByteString) { 53 if (arguments.length != 1) { 54 throw new GPError("APDU", GPError.INVALID_ARGUMENTS, APDU.SW_GENERALERROR, "Only one argument of type ByteString expected"); 55 } 56 this.fromByteString(arg); 57 } else { 58 if ((arguments.length < 4) || (arguments.length > 6)) { 59 throw new GPError("APDU", GPError.INVALID_ARGUMENTS, APDU.SW_GENERALERROR, "4 to 6 arguments expected"); 60 } 61 62 for (var i = 0; i < 4; i++) { 63 if (typeof(arguments[i]) != "number") { 64 throw new GPError("APDU", GPError.INVALID_TYPE, APDU.SW_GENERALERROR, "Argument must be of type Number"); 65 } 66 } 67 this.cla = arguments[0]; 68 this.ins = arguments[1]; 69 this.p1 = arguments[2]; 70 this.p2 = arguments[3]; 71 72 var i = 4; 73 if (arguments.length > i) { 74 if (arguments[i] instanceof ByteString) { 75 this.cdata = arguments[i]; 76 i++; 77 } 78 } 79 80 if (arguments.length > i) { 81 if (typeof(arguments[i]) != "number") { 82 throw new GPError("APDU", GPError.INVALID_TYPE, APDU.SW_GENERALERROR, "Argument must be of type Number"); 83 } 84 this.ne = arguments[i]; 85 } 86 } 87 } 88 this.rapdu = null; 89 this.SW = APDU.SW_GENERALERROR; 90 } 91 92 APDU.INS_DEACTIVATE = 0x04; 93 APDU.INS_VERIFY = 0x20; 94 APDU.INS_MANAGE_SE = 0x22; 95 APDU.INS_CHANGE_REFERENCE_DATA = 0x24; 96 APDU.INS_PSO = 0x2A; 97 APDU.INS_RESET_RETRY_COUNTER = 0x2C; 98 APDU.INS_ACTIVATE = 0x44; 99 APDU.INS_GENERATE_KEY_PAIR = 0x46; 100 APDU.INS_EXTERNAL_AUTHENTICATE = 0x82; 101 APDU.INS_GET_CHALLENGE = 0x84; 102 APDU.INS_GENERAL_AUTHENTICATE = 0x86; 103 APDU.INS_COMPUTE_DIGITAL_SIGN = 0x9E; 104 APDU.INS_SELECT = 0xA4; 105 APDU.INS_READBINARY = 0xB0; 106 APDU.INS_READ_BINARY = 0xB0; 107 APDU.INS_READ_RECORD = 0xB2; 108 APDU.INS_VERIFY_CERTIFICATE = 0xBE; 109 APDU.INS_UPDATE_BINARY = 0xD6; 110 APDU.INS_TERMINATE = 0xE6; 111 112 APDU.SW_OK = 0x9000; /* Process completed */ 113 114 APDU.SW_TIMEOUT = 0x6401; /* Exec error: Command timeout */ 115 116 APDU.SW_OKMOREDATA = 0x6100; /*-Process completed, more data available*/ 117 APDU.SW_WARNING = 0x6200; /*-Warning: NV-Ram not changed */ 118 APDU.SW_WARNING1 = 0x6201; /*-Warning: NV-Ram not changed 1 */ 119 APDU.SW_DATAINV = 0x6281; /*-Warning: Part of data corrupted */ 120 APDU.SW_EOF = 0x6282; /*-Warning: End of file reached */ 121 APDU.SW_INVFILE = 0x6283; /* Warning: Invalidated file */ 122 APDU.SW_INVFORMAT = 0x6284; /* Warning: Invalid file control */ 123 APDU.SW_WARNINGNVCHG = 0x6300; /*-Warning: NV-Ram changed */ 124 APDU.SW_WARNINGCOUNT = 0x63C0; /*-Warning: Warning with counter */ 125 APDU.SW_WARNING0LEFT = 0x63C0; /*-Warning: Verify fail, no try left */ 126 APDU.SW_WARNING1LEFT = 0x63C1; /*-Warning: Verify fail, 1 try left */ 127 APDU.SW_WARNING2LEFT = 0x63C2; /*-Warning: Verify fail, 2 tries left*/ 128 APDU.SW_WARNING3LEFT = 0x63C3; /*-Warning: Verify fail, 3 tries left*/ 129 APDU.SW_EXECERR = 0x6400; /*-Exec error: NV-Ram not changed */ 130 APDU.SW_MEMERR = 0x6501; /*-Exec error: Memory failure */ 131 APDU.SW_MEMERRWRITE = 0x6581; /*-Exec error: Memory failure */ 132 APDU.SW_WRONGLENGTH = 0x6700; /*-Checking error: Wrong length */ 133 134 APDU.SW_CLANOTSUPPORTED = 0x6800; /*-Checking error: Function in CLA byte not supported */ 135 APDU.SW_LCNOTSUPPORTED = 0x6881; /*-Checking error: Logical channel not supported */ 136 APDU.SW_SMNOTSUPPORTED = 0x6882; /*-Checking error: Secure Messaging not supported */ 137 APDU.SW_LASTCMDEXPECTED = 0x6883; /*-Checking error: Last command of the chain expected */ 138 APDU.SW_CHAINNOTSUPPORTED = 0x6884; /*-Checking error: Command chaining not supported */ 139 140 APDU.SW_COMNOTALLOWED = 0x6900; /*-Checking error: Command not allowed */ 141 APDU.SW_COMINCOMPATIBLE = 0x6981; /*-Checking error: Command incompatible with file structure */ 142 APDU.SW_SECSTATNOTSAT = 0x6982; /*-Checking error: Security condition not satisfied */ 143 APDU.SW_AUTHMETHLOCKED = 0x6983; /*-Checking error: Authentication method locked */ 144 APDU.SW_REFDATANOTUSABLE = 0x6984; /*-Checking error: Reference data not usable */ 145 APDU.SW_CONDOFUSENOTSAT = 0x6985; /*-Checking error: Condition of use not satisfied */ 146 APDU.SW_COMNOTALLOWNOEF = 0x6986; /*-Checking error: Command not allowed (no current EF) */ 147 APDU.SW_SMOBJMISSING = 0x6987; /*-Checking error: Expected secure messaging object missing */ 148 APDU.SW_INCSMDATAOBJECT = 0x6988; /*-Checking error: Incorrect secure messaging data object */ 149 150 APDU.SW_INVPARA = 0x6A00; /*-Checking error: Wrong parameter P1-P2 */ 151 APDU.SW_INVDATA = 0x6A80; /*-Checking error: Incorrect parameter in the command data field*/ 152 APDU.SW_FUNCNOTSUPPORTED = 0x6A81; /*-Checking error: Function not supported */ 153 APDU.SW_NOAPPL = 0x6A82; /*-Checking error: File not found */ 154 APDU.SW_FILENOTFOUND = 0x6A82; /*-Checking error: File not found */ 155 APDU.SW_RECORDNOTFOUND = 0x6A83; /*-Checking error: Record not found */ 156 APDU.SW_OUTOFMEMORY = 0x6A84; /*-Checking error: Not enough memory space in the file */ 157 APDU.SW_INVLCTLV = 0x6A85; /*-Checking error: Nc inconsistent with TLV structure */ 158 APDU.SW_INVACC = 0x6A85; /*-Checking error: Access cond. n/f */ 159 APDU.SW_INCP1P2 = 0x6A86; /*-Checking error: Incorrect P1-P2 */ 160 APDU.SW_INVLC = 0x6A87; /*-Checking error: Lc inconsistent with P1-P2 */ 161 APDU.SW_RDNOTFOUND = 0x6A88; /*-Checking error: Reference data not found*/ 162 APDU.SW_FILEEXISTS = 0x6A89; /*-Checking error: File already exists */ 163 APDU.SW_DFNAMEEXISTS = 0x6A8A; /*-Checking error: DF name already exists */ 164 165 APDU.SW_INVP1P2 = 0x6B00; /*-Checking error: Wrong parameter P1-P2 */ 166 APDU.SW_INVLE = 0x6C00; /*-Checking error: Invalid Le */ 167 APDU.SW_INVINS = 0x6D00; /*-Checking error: Wrong instruction */ 168 APDU.SW_INVCLA = 0x6E00; /*-Checking error: Class not supported */ 169 APDU.SW_ACNOTSATISFIED = 0x9804; /* Access conditions not satisfied */ 170 APDU.SW_NOMORESTORAGE = 0x9210; /* No more storage available */ 171 APDU.SW_GENERALERROR = 0x6F00; /*-Checking error: No precise diagnosis */ 172 173 174 /** 175 * Create an APDU object from the encoded form (Called internally) 176 * 177 * @param {ByteString} bs 178 */ 179 APDU.prototype.fromByteString = function(bs) { 180 if (bs.length < 4) { 181 throw new GPError("APDU", GPError.INVALID_DATA, APDU.SW_GENERALERROR, "Command APDU must be at least 4 bytes long"); 182 } 183 this.cla = bs.byteAt(0); 184 this.ins = bs.byteAt(1); 185 this.p1 = bs.byteAt(2); 186 this.p2 = bs.byteAt(3); 187 188 if (bs.length > 4) { 189 var extended = false; 190 191 var i = 4; 192 var l = bs.length - i; 193 var n = bs.byteAt(i++); 194 l--; 195 196 if ((n == 0) && (l > 0)) { 197 extended = true; 198 if (l < 2) { 199 throw new GPError("APDU", GPError.INVALID_DATA, APDU.SW_WRONGLENGTH, "Extended length APDU too short"); 200 } 201 n = (bs.byteAt(i) << 8) + bs.byteAt(i + 1); 202 i += 2; 203 l -= 2; 204 } 205 206 if (l > 0) { // Case 3s / Case 3e / Case 4s / Case 4e 207 if (l < n) { 208 throw new GPError("APDU", GPError.INVALID_DATA, APDU.SW_WRONGLENGTH, "Invalid Lc in APDU"); 209 } 210 this.cdata = bs.bytes(i, n); 211 i += n; 212 l -= n; 213 214 if (l > 0) { // Case 4s / Case 4e 215 n = bs.byteAt(i++); 216 l--; 217 if (extended) { 218 if (l < 1) { 219 throw new GPError("APDU", GPError.INVALID_DATA, APDU.SW_WRONGLENGTH, "Invalid Le in extended APDU"); 220 } 221 n = (n << 8) + bs.byteAt(i++); 222 l--; 223 } 224 this.ne = (extended && (n == 0) ? 65536 : n); 225 } 226 } else { 227 this.ne = (extended && (n == 0) ? 65536 : n); 228 } 229 230 if (l > 0) { 231 throw new GPError("APDU", GPError.INVALID_DATA, APDU.SW_WRONGLENGTH, "Too many bytes in APDU"); 232 } 233 } 234 } 235 236 237 238 /** 239 * Get encoded command APDU 240 * 241 * @type ByteString 242 * @return the encoded command APDU 243 */ 244 APDU.prototype.getCommandAPDU = function() { 245 var bb = new ByteBuffer(); 246 247 bb.append(this.cla); 248 bb.append(this.ins); 249 bb.append(this.p1); 250 bb.append(this.p2); 251 252 var hasCData = (typeof(this.cdata) != "undefined"); 253 var hasNe = (typeof(this.ne) != "undefined"); 254 255 var extended = ((hasCData && this.cdata.length > 255) || 256 (hasNe && this.ne > 256)); 257 258 if (extended) { 259 bb.append(0); 260 } 261 262 if (hasCData && this.cdata.length > 0) { 263 if (extended) { 264 bb.append(this.cdata.length >> 8); 265 } 266 bb.append(this.cdata.length & 0xFF); 267 bb.append(this.cdata); 268 } 269 270 if (hasNe) { 271 if (extended) { 272 bb.append(this.ne >> 8); 273 } 274 bb.append(this.ne & 0xFF); 275 } 276 277 return bb.toByteString(); 278 } 279 280 281 282 /** 283 * Get encoded response APDU 284 * 285 * @type ByteString 286 * @return the encoded response APDU 287 */ 288 APDU.prototype.getResponseAPDU = function() { 289 var bb = new ByteBuffer(); 290 291 if (this.rdata) { 292 bb.append(this.rdata); 293 } 294 295 bb.append(this.SW >> 8); 296 bb.append(this.SW & 0xFF); 297 298 return bb.toByteString(); 299 } 300 301 302 303 /** 304 * Gets the class byte 305 * 306 * @type Number 307 * @return the class byte 308 */ 309 APDU.prototype.getCLA = function() { 310 return this.cla; 311 } 312 313 314 315 /** 316 * Test if command is an ISO command 317 * 318 * @type boolean 319 * @return true if command has ISO class byte 320 */ 321 APDU.prototype.isISO = function() { 322 return (this.cla & 0x80) == 0x00; 323 } 324 325 326 327 /** 328 * Test if command chaining is indicated 329 * 330 * @type boolean 331 * @return true if chaining bit is set 332 */ 333 APDU.prototype.isChained = function() { 334 return (this.cla & 0x10) == 0x10; 335 } 336 337 338 339 /** 340 * Test if command is send using secure messaging 341 * 342 * @type boolean 343 * @return true if secure messaging is indicated in CLA byte 344 */ 345 APDU.prototype.isSecureMessaging = function() { 346 return (this.cla & 0x08) == 0x08; 347 } 348 349 350 351 /** 352 * Test if command is send using secure messaging 353 * 354 * @type boolean 355 * @return true if secure messaging is using an authenticated header 356 */ 357 APDU.prototype.isAuthenticatedHeader = function() { 358 return (this.cla & 0x0C) == 0x0C; 359 } 360 361 362 363 /** 364 * Gets the instruction byte 365 * 366 * @type Number 367 * @return the instruction byte 368 */ 369 APDU.prototype.getINS = function() { 370 return this.ins; 371 } 372 373 374 375 /** 376 * Gets the P1 byte 377 * 378 * @type Number 379 * @return the P1 byte 380 */ 381 APDU.prototype.getP1 = function() { 382 return this.p1; 383 } 384 385 386 387 /** 388 * Gets the P2 byte 389 * 390 * @type Number 391 * @return the P2 byte 392 */ 393 APDU.prototype.getP2 = function() { 394 return this.p2; 395 } 396 397 398 399 /** 400 * Set the command data 401 * 402 * @param {ByteString} cdata the command data 403 */ 404 APDU.prototype.setCData = function(cdata) { 405 this.cdata = cdata; 406 } 407 408 409 410 /** 411 * Gets the command data 412 * 413 * @type ByteString 414 * @return the command data, if any else undefined 415 */ 416 APDU.prototype.getCData = function() { 417 return this.cdata; 418 } 419 420 421 422 /** 423 * Check if APDU has command data 424 * 425 * @type boolean 426 * @return true if command APDU has data field 427 */ 428 APDU.prototype.hasCData = function() { 429 return ((typeof(this.cdata) != "undefined") && (this.cdata != null)); 430 } 431 432 433 434 /** 435 * Gets the command data as a list of TLV objects 436 * 437 * @type TLVList 438 * @return the command data as TLV list, if any else undefined 439 */ 440 APDU.prototype.getCDataAsTLVList = function() { 441 if (typeof(this.cdata) == "undefined") { 442 throw new GPError("APDU", GPError.INVALID_DATA, APDU.SW_INVDATA, "No data in command APDU"); 443 } 444 445 try { 446 var a = new TLVList(this.cdata, TLV.EMV); 447 } 448 catch(e) { 449 throw new GPError("APDU", GPError.INVALID_DATA, APDU.SW_INVDATA, "Invalid TLV data in command APDU"); 450 } 451 452 return a; 453 } 454 455 456 457 /** 458 * Gets the number of expected bytes 459 * 460 * @type Number 461 * @return the number of expected bytes or undefined 462 */ 463 APDU.prototype.getNe = function() { 464 return this.ne; 465 } 466 467 468 469 /** 470 * Check if APDU has Le field 471 * 472 * @type boolean 473 * @return true if command APDU has Le field 474 */ 475 APDU.prototype.hasLe = function() { 476 return typeof(this.ne) != "undefined"; 477 } 478 479 480 481 /** 482 * Set secure channel object to be used in wrap and unwrap methods 483 * 484 * @param {SecureChannel} secureChannel the channel 485 */ 486 APDU.prototype.setSecureChannel = function(secureChannel) { 487 this.secureChannel = secureChannel; 488 } 489 490 491 492 /** 493 * Return the secure channel, if any 494 * 495 * @type SecureChannel 496 * @return the secure channel 497 */ 498 APDU.prototype.getSecureChannel = function() { 499 return this.secureChannel; 500 } 501 502 503 504 /** 505 * Test if a secure channel is defined for this APDU 506 * 507 * @type boolean 508 * @return true, if secure channel is set 509 */ 510 APDU.prototype.hasSecureChannel = function() { 511 return (typeof(this.secureChannel) != "undefined") && (this.secureChannel != null); 512 } 513 514 515 516 /** 517 * Wrap APDU using secure channel 518 */ 519 APDU.prototype.wrap = function() { 520 if (this.hasSecureChannel()) { 521 this.secureChannel.wrap(this); 522 } 523 } 524 525 526 527 /** 528 * Unwrap APDU using secure channel 529 */ 530 APDU.prototype.unwrap = function() { 531 if (this.hasSecureChannel()) { 532 this.secureChannel.unwrap(this); 533 } 534 } 535 536 537 538 /** 539 * Sets the response data field for the response APDU 540 * 541 * @param {ByteString} data the response data field 542 */ 543 APDU.prototype.setRData = function(data) { 544 this.rdata = data; 545 } 546 547 548 549 /** 550 * Get the response data 551 * 552 * @type ByteString 553 * @return the response data 554 */ 555 APDU.prototype.getRData = function() { 556 return this.rdata; 557 } 558 559 560 561 /** 562 * Check if APDU has response data 563 * 564 * @type boolean 565 * @return true if response APDU has data field 566 */ 567 APDU.prototype.hasRData = function() { 568 return ((typeof(this.rdata) != "undefined") && (this.rdata != null)); 569 } 570 571 572 573 /** 574 * Sets the status word for the response ADPU 575 * 576 * @param {Number} sw the status word 577 */ 578 APDU.prototype.setSW = function(sw) { 579 this.SW = sw; 580 } 581 582 583 584 /** 585 * Get the status word 586 * 587 * @type Number 588 * @return the status word 589 */ 590 APDU.prototype.getSW = function() { 591 return this.SW; 592 } 593 594 595 596 /** 597 * Return a human readable form of this object 598 */ 599 APDU.prototype.toString = function() { 600 return this.getCommandAPDU().toString(HEX) + " : " + this.getResponseAPDU().toString(HEX); 601 } 602 603 604 605 /** 606 * Simple unit test 607 */ 608 APDU.test = function() { 609 // Case 1 610 var a = new APDU(0x00, 0xA4, 0x00, 0x0C); 611 print(a); 612 var b = a.getCommandAPDU(); 613 assert(b.toString(HEX) == "00A4000C"); 614 var c = new APDU(b); 615 assert(a.toString() == c.toString()); 616 617 // Case 2 Short 618 var a = new APDU(0x00, 0xA4, 0x00, 0x0C, 0); 619 print(a); 620 var b = a.getCommandAPDU(); 621 assert(b.toString(HEX) == "00A4000C00"); 622 var c = new APDU(b); 623 assert(a.toString() == c.toString()); 624 625 // Case 2 Extended 626 var a = new APDU(0x00, 0xA4, 0x00, 0x0C, 65536); 627 print(a); 628 var b = a.getCommandAPDU(); 629 assert(b.toString(HEX) == "00A4000C000000"); 630 var c = new APDU(b); 631 print(c); 632 assert(a.toString() == c.toString()); 633 634 // Case 3 Short 635 var a = new APDU(0x00, 0xA4, 0x00, 0x0C, new ByteString("3F00", HEX)); 636 print(a); 637 var b = a.getCommandAPDU(); 638 assert(b.toString(HEX) == "00A4000C023F00"); 639 var c = new APDU(b); 640 assert(a.toString() == c.toString()); 641 642 // Case 3 Extended 643 var data = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"; 644 var a = new APDU(0x00, 0xA4, 0x00, 0x0C, new ByteString(data, HEX)); 645 print(a); 646 var b = a.getCommandAPDU(); 647 assert(b.toString(HEX) == "00A4000C000100" + data); 648 var c = new APDU(b); 649 assert(a.toString() == c.toString()); 650 651 // Case 4 Short 652 var a = new APDU(0x00, 0xA4, 0x00, 0x0C, new ByteString("3F00", HEX), 0); 653 print(a); 654 var b = a.getCommandAPDU(); 655 assert(b.toString(HEX) == "00A4000C023F0000"); 656 var c = new APDU(b); 657 assert(a.toString() == c.toString()); 658 659 // Case 4b Extended 660 var a = new APDU(0x00, 0xA4, 0x00, 0x0C, new ByteString(data, HEX), 0); 661 print(a); 662 var b = a.getCommandAPDU(); 663 assert(b.toString(HEX) == "00A4000C000100" + data + "0000"); 664 var c = new APDU(b); 665 assert(a.toString() == c.toString()); 666 667 // Case 4b Extended 668 var a = new APDU(0x00, 0xA4, 0x00, 0x0C, new ByteString("3F00", HEX), 65536); 669 print(a); 670 var b = a.getCommandAPDU(); 671 assert(b.toString(HEX) == "00A4000C0000023F000000"); 672 var c = new APDU(b); 673 assert(a.toString() == c.toString()); 674 675 } 676 677 678 679 /** 680 * Create an adapter to decode a APDU for data unit handling 681 * 682 * @class Adapter class to decode APDUs for data unit handling 683 * @constructor 684 * @param {APDU} apdu the APDU to decode 685 */ 686 function DataUnitAPDU(apdu) { 687 this.apdu = apdu; 688 689 var p1 = apdu.getP1(); 690 691 if ((this.apdu.getINS() & 1) == 0) { // Even instruction 692 if ((p1 & 0x80) == 0x80) { // SFI in P1 693 this.offset = this.apdu.getP2(); 694 this.sfi = p1 & 0x1F; 695 } else { 696 this.offset = (p1 << 8) + this.apdu.getP2(); 697 } 698 this.data = apdu.getCData(); 699 } else { // Odd instruction 700 var p2 = apdu.getP2(); 701 var fid = (p1 << 8) + p2; // FID in P1 P2 702 // If bits b16 - b6 are all 0 and b5 - b1 are not all equal, then we have an SFI 703 if (((fid & 0xFFE0) == 0) && ((fid & 0x1F) >= 1) && ((fid & 0x1F) <= 30)) { 704 this.sfi = fid & 0x1F; 705 } else if (fid != 0) { // FID = 0000 means current file 706 var bb = new ByteBuffer(); 707 bb.append(p1); 708 bb.append(p2); 709 this.fid = bb.toByteString(); 710 } 711 712 var a = this.apdu.getCDataAsTLVList(); 713 714 if ((a.length < 1) || (a.length > 2)) { 715 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"); 716 } 717 718 var o = a.index(0); 719 if (o.getTag() != 0x54) { 720 throw new GPError("DataUnitAPDU", GPError.INVALID_DATA, APDU.SW_INVDATA, "Invalid data for odd instruction data handling command, first tag must be '54' offset"); 721 } 722 723 this.offset = o.getValue().toUnsigned(); 724 725 if (a.length == 2) { 726 var o = a.index(1); 727 var t = o.getTag(); 728 if ((t != 0x53) && (t != 0x73)) { 729 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'"); 730 } 731 732 this.data = o.getValue(); 733 } 734 } 735 } 736 737 738 739 /** 740 * Gets the short file identifier, if one defined 741 * 742 * @type Number 743 * @return the short file identifier in the range 1 to 30 or -1 if not defined 744 */ 745 DataUnitAPDU.prototype.getSFI = function() { 746 if (typeof(this.sfi) == "undefined") { 747 return -1; 748 } 749 return this.sfi; 750 } 751 752 753 754 /** 755 * Gets the file identifier, if one defined 756 * 757 * @type ByteString 758 * @return the file identifier or null if not defined 759 */ 760 DataUnitAPDU.prototype.getFID = function() { 761 if (typeof(this.fid) == "undefined") { 762 return null; 763 } 764 return this.fid; 765 } 766 767 768 769 /** 770 * Gets the offset 771 * 772 * @type Number 773 * @return the offset to read from or write to 774 */ 775 DataUnitAPDU.prototype.getOffset = function() { 776 return this.offset; 777 } 778 779 780 781 /** 782 * Get the command data 783 * 784 * @type ByteString 785 * @return the command data 786 */ 787 DataUnitAPDU.prototype.getCData = function() { 788 if (!this.hasCData()) { 789 throw new GPError("APDU", GPError.INVALID_DATA, APDU.SW_INVDATA, "No data in command APDU"); 790 } 791 return this.data; 792 } 793 794 795 796 /** 797 * Returns true if command data in contained in the APDU 798 * 799 * @type boolean 800 * @returns true if command data contained 801 */ 802 DataUnitAPDU.prototype.hasCData = function() { 803 return ((typeof(this.data) != "undefined") && (this.data != null)); 804 } 805 806 807 808 /** 809 * Simple Unit Test 810 */ 811 DataUnitAPDU.test = function() { 812 var apdu = new APDU(0x00, 0xB0, 0, 0, 0); 813 var dh = new DataUnitAPDU(apdu); 814 assert(dh.getOffset() == 0); 815 assert(!dh.hasCData()); 816 817 var apdu = new APDU(0x00, 0xB0, 0x7F, 0xFF, 0); 818 var dh = new DataUnitAPDU(apdu); 819 assert(dh.getOffset() == 0x7FFF); 820 assert(!dh.hasCData()); 821 822 var apdu = new APDU(0x00, 0xB0, 0x80, 0, 0); 823 var dh = new DataUnitAPDU(apdu); 824 assert(dh.getOffset() == 0); 825 assert(!dh.hasCData()); 826 827 var apdu = new APDU(0x00, 0xB0, 0x80, 0xFF, 0); 828 var dh = new DataUnitAPDU(apdu); 829 assert(dh.getOffset() == 0xFF); 830 assert(!dh.hasCData()); 831 832 var apdu = new APDU(0x00, 0xB1, 0, 0, new ByteString("540100", HEX), 0); 833 var dh = new DataUnitAPDU(apdu); 834 assert(dh.getOffset() == 0); 835 assert(!dh.hasCData()); 836 837 var apdu = new APDU(0x00, 0xB1, 0, 0, new ByteString("5401FF", HEX), 0); 838 var dh = new DataUnitAPDU(apdu); 839 assert(dh.getOffset() == 0xFF); 840 assert(!dh.hasCData()); 841 842 var apdu = new APDU(0x00, 0xB1, 0, 0, new ByteString("540401000000", HEX), 0); 843 var dh = new DataUnitAPDU(apdu); 844 assert(dh.getOffset() == 0x01000000); 845 assert(!dh.hasCData()); 846 847 var data = new ByteString("1234", ASCII); 848 849 var apdu = new APDU(0x00, 0xD6, 0, 0, data); 850 var dh = new DataUnitAPDU(apdu); 851 assert(dh.getOffset() == 0); 852 assert(dh.hasCData()); 853 assert(dh.getCData().equals(data)); 854 855 var apdu = new APDU(0x00, 0xD6, 0x7F, 0xFF, data); 856 var dh = new DataUnitAPDU(apdu); 857 assert(dh.getOffset() == 0x7FFF); 858 assert(dh.hasCData()); 859 assert(dh.getCData().equals(data)); 860 861 var apdu = new APDU(0x00, 0xD6, 0x80, 0, data); 862 var dh = new DataUnitAPDU(apdu); 863 assert(dh.getOffset() == 0); 864 assert(dh.hasCData()); 865 assert(dh.getCData().equals(data)); 866 867 var apdu = new APDU(0x00, 0xD6, 0x80, 0xFF, data); 868 var dh = new DataUnitAPDU(apdu); 869 assert(dh.getOffset() == 0xFF); 870 assert(dh.hasCData()); 871 assert(dh.getCData().equals(data)); 872 873 var apdu = new APDU(0x00, 0xD7, 0, 0, (new ByteString("5401005304", HEX)).concat(data), 0); 874 var dh = new DataUnitAPDU(apdu); 875 assert(dh.getOffset() == 0); 876 assert(dh.hasCData()); 877 assert(dh.getCData().equals(data)); 878 879 var apdu = new APDU(0x00, 0xD7, 0, 0, (new ByteString("5401FF5304", HEX)).concat(data), 0); 880 var dh = new DataUnitAPDU(apdu); 881 assert(dh.getOffset() == 0xFF); 882 assert(dh.hasCData()); 883 assert(dh.getCData().equals(data)); 884 885 var apdu = new APDU(0x00, 0xD7, 0, 0, (new ByteString("5404010000005304", HEX)).concat(data), 0); 886 var dh = new DataUnitAPDU(apdu); 887 assert(dh.getOffset() == 0x01000000); 888 assert(dh.hasCData()); 889 assert(dh.getCData().equals(data)); 890 } 891