1 /** 2 * --------- 3 * |.##> <##.| Open Smart Card Development Platform (www.openscdp.org) 4 * |# #| 5 * |# #| Copyright (c) 1999-2011 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 Script classes to access Mifare cards 25 */ 26 27 28 29 /** 30 * Create a Mifare card object 31 * @class Class encapsulating access to a Mifare classic 1K/4K card 32 * @constructor 33 * @param {card} card the card object 34 */ 35 function Mifare(card) { 36 this.card = card; 37 } 38 39 40 /** 41 * Identifier for Key A 42 */ 43 Mifare.KEY_A = 0x60; 44 45 /** 46 * Identifier for Key B 47 */ 48 Mifare.KEY_B = 0x61; 49 50 /** 51 * Mifare Public Key Values 52 */ 53 Mifare.PUBLICKEYS = [ 54 new ByteString("FFFFFFFFFFFF", HEX), // Empty cards 55 new ByteString("A0A1A2A3A4A5", HEX), // MAD Key A 56 new ByteString("B0B1B2B3B4B5", HEX), // Default Key B 57 new ByteString("D3F7D3F7D3F7", HEX), // NDEF Key A 58 ]; 59 60 61 62 /** 63 * Calculate CRC-8 checksum 64 * 65 * Based on code from nfc-tools. 66 * 67 * @param {ByteString} data the data to calculate the checksum for 68 * @type Number 69 * @return the crc checksum 70 */ 71 Mifare.crc8 = function(data) { 72 var polynom = 0x1d; // x8 + x4 + x3 + x2 + 1 = 110001101 73 var crc = 0xC7; // start value 0xE3 mirrored is 0xC7 74 75 for (var i = 0; i < data.length; i++) { 76 crc ^= data.byteAt(i); 77 for (var b = 0; b < 8; b++) { 78 var msb = crc & 0x80; 79 crc = (crc << 1) & 0xFF; 80 if (msb) { 81 crc ^= polynom; 82 } 83 } 84 } 85 return crc; 86 } 87 88 // var ref = new ByteString("01010801080108000000000000040003100310021002100000000000001130", HEX); 89 // assert(ref.length = 31); 90 // assert(Mifare.crc8(ref) == 0x89); 91 92 93 94 /** 95 * Read UID using Get Data command as defined in PCSC Part 3, chapter 3.2.2.1.3 96 * 97 * <p>FEIG readers require Le='04' to automatically switch to Mifare if the card supports both T=CL and Mifare.</p> 98 * 99 * @type ByteString 100 * @return the 4 byte UID 101 */ 102 Mifare.prototype.getUID = function() { 103 return this.card.sendApdu(0xFF, 0xCA, 0x00, 0x00, 4, [0x9000]); 104 } 105 106 107 108 /** 109 * Load key value into reader using Load Key command as defined in PCSC Part 3, chapter 3.2.2.1.4 110 * 111 * <p>The ACR 122U contactless reader supports key ids 0x00 and 0x01</p> 112 * 113 * <p>The Omnikey cardman 5321 reader supports key ids 0x00 to 0x1F</p> 114 * 115 * <p>The ACR 122U contactless reader supports key ids 0x00 and 0x01</p> 116 * 117 * <p>The method supports the SCM SDI010 contactless reader which uses a proprietary LOAD KEY APDU with 118 * preset key identifier 0x60 and 0x61. This command is activated if keyid is 0x60 or 0x61.</p> 119 * 120 * @param {Number} keyid the key identifier under which the key should be refered to in the reader 121 * @param {ByteString} key the 6 byte key value 122 */ 123 Mifare.prototype.loadKey = function(keyid, key) { 124 assert(typeof(keyid) == "number"); 125 assert(key.length == 6); 126 127 if ((keyid == 0x60) || (keyid == 0x61)) { 128 this.card.sendApdu(0xFF, 0x82, 0x00, keyid, key, [0x9000]); // Load key command for SDI010 129 } else { 130 this.card.sendApdu(0xFF, 0x82, 0x20, keyid, key, [0x9000]); 131 } 132 } 133 134 135 136 /** 137 * Read a block using the Read Binary command as defined in PCSC Part 3, chapter 3.2.2.1.8 138 * 139 * @param {Number} block the block to read, starting at 0 for the first block in the first sector. 140 * @type ByteString 141 * @return the 16 byte block content read from the card 142 */ 143 Mifare.prototype.readBlock = function(block) { 144 return this.card.sendApdu(0xFF, 0xB0, block >> 8, block & 0xFF, 16, [0x9000]); 145 } 146 147 148 149 /** 150 * Update a block using the Update Binary command as defined in PCSC Part 3, chapter 3.2.2.1.9 151 * 152 * @param {Number} block the block to read, starting at 0 for the first block in the first sector. 153 * @param {ByteString} data the 16 bytes of the data block to write 154 */ 155 Mifare.prototype.updateBlock = function(block, data) { 156 assert(data.length == 16); 157 return this.card.sendApdu(0xFF, 0xD6, block >> 8, block & 0xFF, data, [0x9000]); 158 } 159 160 161 162 /** 163 * Perform authentication procedure using General Authenticate command as defined in PCSC Part 3, chapter 3.2.2.1.6 164 * 165 * @param {Number} block the block to authenticate against 166 * @param {Number} keytype must be either Mifare.KEY_A or Mifare.KEY_B 167 * @param {Number} keyid the key id of the key in the reader 168 * @type boolean 169 * @return true if authentication successfull 170 */ 171 Mifare.prototype.authenticate = function(block, keytype, keyid) { 172 var bb = new ByteBuffer(); 173 bb.append(0x01); // Version 174 bb.append(ByteString.valueOf(block, 2)); 175 bb.append(keytype); 176 if ((keyid != 0x60) && (keyid != 0x61)) { 177 bb.append(keyid); 178 } else { 179 bb.append(0x01); // Support for SCM SDI 010 180 } 181 this.card.sendApdu(0xFF,0x86,0x00,0x00, bb.toByteString()); 182 183 return this.card.SW == 0x9000; 184 } 185 186 187 188 /** 189 * Create a sector object bound to the current Mifare instance 190 * 191 * @param {Number} no the sector number 192 */ 193 Mifare.prototype.newSector = function(no) { 194 return new Sector(this, no); 195 } 196 197 198 199 /** 200 * Create an object representing an on card sector. Do not call directly but use Mifare.prototype.newSector() instead. 201 * 202 * @class Class representing a sector on a Mifare card 203 * @constructor 204 * @param {Mifare} mifare the card 205 * @param {Number} no the sector number 206 */ 207 function Sector(mifare, no) { 208 this.mifare = mifare; 209 this.no = no; 210 this.blocks = []; 211 this.keyid = [0, 1]; 212 } 213 214 215 Sector.MASK = [ 0x00E0EE, 0x00D0DD, 0x00B0BB, 0x007077 ]; 216 217 Sector.AC_TRAILER = [ 218 "000 - Key A: Write Key A | AC: Write Never | Key B: Read Key A / Write Key A", // 000 219 "001 - Key A: Write Key A | AC: Write Key A | Key B: Read Key A / Write Key A", // 001 220 "010 - Key A: Write Never | AC: Write Never | Key B: Read Key A / Write Never", // 010 221 "011 - Key A: Write Key B | AC: Write Key B | Key B: Read Never / Write Key B", // 011 222 "100 - Key A: Write Key B | AC: Write Never | Key B: Read Never / Write Key B", // 100 223 "101 - Key A: Write Never | AC: Write Key B | Key B: Read Never / Write Never", // 101 224 "110 - Key A: Write Never | AC: Write Never | Key B: Read Never / Write Never", // 110 225 "111 - Key A: Write Never | AC: Write Never | Key B: Read Never / Write Never", // 111 226 ]; 227 228 // Key A is never readable 229 // Access Conditions are always readable with Key A or Key AB if Key B is used for writing 230 231 Sector.AC_FIXED_AC_NOKEY_B = 0; 232 Sector.AC_UPDATE_AC_NOKEY_B = 1; // Transport configuration 233 Sector.AC_READONLY_NOKEY_B = 2; 234 Sector.AC_UPDATE_WITH_KEYB = 3; 235 Sector.AC_FIXED_AC_UPDATE_WITH_KEYB = 4; 236 Sector.AC_UPDATE_AC_FIXED_KEYS = 5; 237 Sector.AC_NEVER2 = 6; 238 239 Sector.AC_DATA = [ 240 "000 - Read: Key AB | Write: Key AB | Inc: Key AB | Dec: Key AB", // 000 241 "001 - Read: Key AB | Write: Never | Inc: Never | Dec: Key AB", // 001 242 "010 - Read: Key AB | Write: Never | Inc: Never | Dec: Never ", // 010 243 "011 - Read: Key B | Write: Key B | Inc: Never | Dec: Never ", // 011 244 "100 - Read: Key AB | Write: Key B | Inc: Never | Dec: Never ", // 100 245 "101 - Read: Key B | Write: Never | Inc: Never | Dec: Never ", // 101 246 "110 - Read: Key AB | Write: Key B | Inc: Key B | Dec: Key AB", // 110 247 "111 - Read: Never | Write: Never | Inc: Never | Dec: Never ", // 111 248 ]; 249 250 Sector.AC_ALWAYS = 0; // All conditions with Key A or Key B - Transport configuration 251 Sector.AC_NONRECHARGEABLE = 1; // Only decrement on read only application 252 Sector.AC_READONLY = 2; // Read only application 253 Sector.AC_KEYBONLY = 3; // Only using Key B 254 Sector.AC_UPDATEKEYB = 4; // Use Key B to update 255 Sector.AC_KEYBREADONLY = 5; // Read only application with only Key B 256 Sector.AC_RECHARGEABLE = 6; // Rechargable counter 257 Sector.AC_NEVER = 7; // No access at all 258 259 260 261 /** 262 * Overwrite internal key id 263 * @param {Number} keyId the key id for the Mifare key 264 * @param {Number} keytype either Mifare.KEY_A (Default) or Mifare.KEY_B. 265 */ 266 Sector.prototype.setKeyId = function(keyid, keytype) { 267 if (typeof(keytype) == "undefined") { 268 keytype = Mifare.KEY_A; 269 } 270 this.keyid[keytype - Mifare.KEY_A] = keyid; 271 } 272 273 274 275 /** 276 * Read a block within the sector 277 * 278 * @param {Number} block the block number between 0 and 3 279 * @type ByteString 280 * @return the data read from the block 281 */ 282 Sector.prototype.read = function(block) { 283 assert(block >= 0); 284 assert(block <= 3); 285 var blockoffs = (this.no << 2) + block; 286 this.blocks[block] = this.mifare.readBlock(blockoffs); 287 return this.blocks[block]; 288 } 289 290 291 292 /** 293 * Update a block within the sector 294 * 295 * @param {Number} block the block number between 0 and 3 296 * @param {ByteString} data the data to write (Optional for sector trailer) 297 */ 298 Sector.prototype.update = function(block, data) { 299 if (typeof(data) == "undefined") { 300 data = this.blocks[block]; 301 } else { 302 this.blocks[block] = data 303 } 304 var blockoffs = (this.no << 2) + block; 305 this.mifare.updateBlock(blockoffs, data); 306 } 307 308 309 310 /** 311 * Authenticate against block 312 * <p>Uses the internal key id for this sector for key A and the internal key id + 1 for key B.</p> 313 * @param {Number} block the block number between 0 and 3 314 * @param {Number} keytype must be either Mifare.KEY_A or Mifare.KEY_B 315 * @type boolean 316 * @return true if authentication successfull 317 */ 318 Sector.prototype.authenticate = function(block, keytype) { 319 return this.mifare.authenticate((this.no << 2) + block, keytype, this.keyid[keytype - Mifare.KEY_A]); 320 } 321 322 323 324 /** 325 * Authenticate against block using list from public key table 326 * 327 * @param {Number} block the block number between 0 and 3 328 * @param {Number} keytype must be either Mifare.KEY_A or Mifare.KEY_B 329 * @type Number 330 * @return The key index in Mifare.PUBLICKEYS or -1 if not authenticated 331 */ 332 Sector.prototype.authenticatePublic = function(block, keytype) { 333 var i = 0; 334 var authenticated = false; 335 336 for (var i = 0; i < Mifare.PUBLICKEYS.length; i++) { 337 this.mifare.loadKey(this.keyid[keytype - Mifare.KEY_A], Mifare.PUBLICKEYS[i]); 338 if (this.authenticate(block, keytype)) { 339 return i; 340 } 341 } 342 return -1; 343 } 344 345 346 347 /** 348 * Read all blocks from a sector 349 * 350 * @param {Number} keytype key type to use for authentication (Mifare.KEY_A or Mifare.KEY_B. Defaults to key B. 351 */ 352 Sector.prototype.readAll = function(keytype) { 353 if (typeof(keytype) == "undefined") { 354 keytype = Mifare.KEY_A; 355 } 356 var bb = new ByteBuffer(); 357 this.authenticate(0, keytype); 358 for (var i = 0; i < 4; i++) { 359 bb.append(this.read(i)); 360 } 361 return bb.toByteString(); 362 } 363 364 365 366 /** 367 * Return access conditions for a block within the sector 368 * 369 * @param {Number} block the block number between 0 and 3 370 * @type Number 371 * @return one of the Sector.AC_ constants 372 */ 373 Sector.prototype.getACforBlock = function(block) { 374 var c = this.blocks[3].bytes(6, 3).toUnsigned(); 375 return ((((c >> (12 + block)) & 0x01) << 2) + 376 (((c >> ( 0 + block)) & 0x01) << 1) + 377 ((c >> (4 + block)) & 0x01)); 378 } 379 380 381 382 /** 383 * Set the access condition for a block within the sector 384 * 385 * @param {Number} block the block number between 0 and 3 386 * @param {Number} ac one of the Sector.AC_ constants 387 */ 388 Sector.prototype.setACforBlock = function(block, ac) { 389 var c = this.blocks[3].bytes(6, 3).toUnsigned(); 390 c &= Sector.MASK[block]; 391 392 c |= ((((ac >> 2) & 0x01) << (12 + block)) + 393 (((ac >> 1) & 0x01) << ( 0 + block)) + 394 (( ac & 0x01) << ( 4 + block))); 395 396 c |= (((~c & 0xF) << 20) + 397 ((~c & 0xF0) << 4) + 398 ((~c & 0xF000) << 4)); 399 400 var d = this.blocks[3]; 401 this.blocks[3] = d.bytes(0, 6).concat(ByteString.valueOf(c, 3).concat(d.bytes(9))); 402 } 403 404 405 406 /** 407 * Set the value for Key A 408 * 409 * @param {ByteString} key the key value (6 bytes) 410 */ 411 Sector.prototype.setKeyA = function(key) { 412 var d = this.blocks[3]; 413 this.blocks[3] = key.concat(d.bytes(6)); 414 } 415 416 417 418 /** 419 * Set the value for Key B 420 * 421 * @param {ByteString} key the key value (6 bytes) 422 */ 423 Sector.prototype.setKeyB = function(key) { 424 var d = this.blocks[3]; 425 this.blocks[3] = d.bytes(0, 10).concat(key); 426 } 427 428 429 430 /** 431 * Set the data byte in the sector trailer 432 * 433 * @param {ByteString} db the data byte (1 bytes) 434 */ 435 Sector.prototype.setHeaderDataByte = function(db) { 436 var d = this.blocks[3]; 437 this.blocks[3] = d.bytes(0, 9).concat(db).concat(d.bytes(10)); 438 } 439 440 441 442 /** 443 * Convert binary data to ASCII code if within the range 0x20 to 0x7E 444 * 445 * @param {ByteString} data the input data 446 * @type String 447 * @return the ASCII string 448 */ 449 Sector.toASCII = function(data) { 450 var str = ""; 451 for (var i = 0; i < data.length; i++) { 452 var c = data.byteAt(i); 453 if ((c >= 0x20) && (c < 0x7F)) { 454 str += String.fromCharCode(c); 455 } else { 456 str += "."; 457 } 458 } 459 return str; 460 } 461 462 463 464 /** 465 * Return a human readable presentation of the sector 466 */ 467 Sector.prototype.toString = function() { 468 var str = ""; 469 for (var i = 0; i < 4; i++) { 470 str += "Sec" + this.no + " Blk" + i + " - "; 471 472 var ac = this.getACforBlock(i); 473 if (i == 3) { 474 str += Sector.AC_TRAILER[ac]; 475 } else { 476 str += Sector.AC_DATA[ac]; 477 } 478 479 str += "\n"; 480 481 if (typeof(this.blocks[i]) != "undefined") { 482 str += " " + this.blocks[i].toString(HEX) + " " + Sector.toASCII(this.blocks[i]) + "\n"; 483 } 484 } 485 return str; 486 } 487