MifareClassic.java revision b134223f91c8801d577cb72e92a37cb65fec717a
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.nfc.tech; 18 19import android.nfc.Tag; 20import android.nfc.TagLostException; 21import android.os.RemoteException; 22 23import java.io.IOException; 24import java.nio.ByteBuffer; 25import java.nio.ByteOrder; 26 27/** 28 * Technology class representing MIFARE Classic tags (also known as MIFARE Standard). 29 * 30 * <p>Support for this technology type is optional. If the NFC stack doesn't support this technology 31 * MIFARE Classic tags will still be scanned, but will only show the NfcA technology. 32 * 33 * <p>MIFARE Classic tags have sectors that each contain blocks. The block size is constant at 34 * 16 bytes, but the number of sectors and the sector size varies by product. MIFARE has encryption 35 * built in and each sector has two keys associated with it, as well as ACLs to determine what 36 * level acess each key grants. Before operating on a sector you must call either 37 * {@link #authenticateSectorWithKeyA(int, byte[])} or 38 * {@link #authenticateSectorWithKeyB(int, byte[])} to gain authorization for your request. 39 */ 40public final class MifareClassic extends BasicTagTechnology { 41 /** 42 * The well-known default MIFARE read key. All keys are set to this at the factory. 43 * Using this key will effectively make the payload in the sector public. 44 */ 45 public static final byte[] KEY_DEFAULT = 46 {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF}; 47 /** 48 * The well-known, default MIFARE Application Directory read key. 49 */ 50 public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY = 51 {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5}; 52 /** 53 * The well-known, default read key for NDEF data on a MIFARE Classic 54 */ 55 public static final byte[] KEY_NFC_FORUM = 56 {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7}; 57 58 /** A MIFARE Classic tag */ 59 public static final int TYPE_CLASSIC = 0; 60 /** A MIFARE Plus tag */ 61 public static final int TYPE_PLUS = 1; 62 /** A MIFARE Pro tag */ 63 public static final int TYPE_PRO = 2; 64 /** A Mifare Classic compatible card that does not match the other types */ 65 public static final int TYPE_OTHER = -1; 66 67 /** The tag contains 16 sectors, each holding 4 blocks. */ 68 public static final int SIZE_1K = 1024; 69 /** The tag contains 32 sectors, each holding 4 blocks. */ 70 public static final int SIZE_2K = 2048; 71 /** 72 * The tag contains 40 sectors. The first 32 sectors contain 4 blocks and the last 8 sectors 73 * contain 16 blocks. 74 */ 75 public static final int SIZE_4K = 4096; 76 /** The tag contains 5 sectors, each holding 4 blocks. */ 77 public static final int SIZE_MINI = 320; 78 79 /** Size of a Mifare Classic block (in bytes) */ 80 public static final int BLOCK_SIZE = 16; 81 82 private static final int MAX_BLOCK_COUNT = 256; 83 private static final int MAX_SECTOR_COUNT = 40; 84 85 private boolean mIsEmulated; 86 private int mType; 87 private int mSize; 88 89 /** 90 * Returns an instance of this tech for the given tag. If the tag doesn't support 91 * this tech type null is returned. 92 * 93 * @param tag The tag to get the tech from 94 */ 95 public static MifareClassic get(Tag tag) { 96 if (!tag.hasTech(TagTechnology.MIFARE_CLASSIC)) return null; 97 try { 98 return new MifareClassic(tag); 99 } catch (RemoteException e) { 100 return null; 101 } 102 } 103 104 /** @hide */ 105 public MifareClassic(Tag tag) throws RemoteException { 106 super(tag, TagTechnology.MIFARE_CLASSIC); 107 108 NfcA a = NfcA.get(tag); // Mifare Classic is always based on NFC a 109 110 mIsEmulated = false; 111 112 switch (a.getSak()) { 113 case 0x08: 114 mType = TYPE_CLASSIC; 115 mSize = SIZE_1K; 116 break; 117 case 0x09: 118 mType = TYPE_CLASSIC; 119 mSize = SIZE_MINI; 120 break; 121 case 0x10: 122 mType = TYPE_PLUS; 123 mSize = SIZE_2K; 124 // SecLevel = SL2 125 break; 126 case 0x11: 127 mType = TYPE_PLUS; 128 mSize = SIZE_4K; 129 // Seclevel = SL2 130 break; 131 case 0x18: 132 mType = TYPE_CLASSIC; 133 mSize = SIZE_4K; 134 break; 135 case 0x28: 136 mType = TYPE_CLASSIC; 137 mSize = SIZE_1K; 138 mIsEmulated = true; 139 break; 140 case 0x38: 141 mType = TYPE_CLASSIC; 142 mSize = SIZE_4K; 143 mIsEmulated = true; 144 break; 145 case 0x88: 146 mType = TYPE_CLASSIC; 147 mSize = SIZE_1K; 148 // NXP-tag: false 149 break; 150 case 0x98: 151 case 0xB8: 152 mType = TYPE_PRO; 153 mSize = SIZE_4K; 154 break; 155 default: 156 // Stack incorrectly reported a MifareClassic. We cannot handle this 157 // gracefully - we have no idea of the memory layout. Bail. 158 throw new RuntimeException( 159 "Tag incorrectly enumerated as Mifare Classic, SAK = " + a.getSak()); 160 } 161 } 162 163 /** Returns the type of the tag, determined at discovery time */ 164 public int getType() { 165 return mType; 166 } 167 168 /** Returns the size of the tag in bytes, determined at discovery time */ 169 public int getSize() { 170 return mSize; 171 } 172 173 /** Returns true if the tag is emulated, determined at discovery time. 174 * These are actually smart-cards that emulate a Mifare Classic interface. 175 * They can be treated identically to a Mifare Classic tag. 176 * @hide 177 */ 178 public boolean isEmulated() { 179 return mIsEmulated; 180 } 181 182 /** Returns the number of sectors on this tag, determined at discovery time */ 183 public int getSectorCount() { 184 switch (mSize) { 185 case SIZE_1K: 186 return 16; 187 case SIZE_2K: 188 return 32; 189 case SIZE_4K: 190 return 40; 191 case SIZE_MINI: 192 return 5; 193 default: 194 return 0; 195 } 196 } 197 198 /** Returns the total block count, determined at discovery time */ 199 public int getBlockCount() { 200 return mSize / BLOCK_SIZE; 201 } 202 203 /** Returns the block count for the given sector, determined at discovery time */ 204 public int getBlockCountInSector(int sectorIndex) { 205 validateSector(sectorIndex); 206 207 if (sectorIndex < 32) { 208 return 4; 209 } else { 210 return 16; 211 } 212 } 213 214 /** Return the sector index of a given block */ 215 public int blockToSector(int blockIndex) { 216 validateBlock(blockIndex); 217 218 if (blockIndex < 32 * 4) { 219 return blockIndex / 4; 220 } else { 221 return 32 + (blockIndex - 32 * 4) / 16; 222 } 223 } 224 225 /** Return the first block of a given sector */ 226 public int sectorToBlock(int sectorIndex) { 227 if (sectorIndex < 32) { 228 return sectorIndex * 4; 229 } else { 230 return 32 * 4 + (sectorIndex - 32) * 16; 231 } 232 } 233 234 // Methods that require connect() 235 /** 236 * Authenticate a sector. 237 * <p>Every sector has an A and B key with different access privileges, 238 * this method attempts to authenticate against the A key. 239 * <p>This requires a that the tag be connected. 240 */ 241 public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException { 242 return authenticate(sectorIndex, key, true); 243 } 244 245 /** 246 * Authenticate a sector. 247 * <p>Every sector has an A and B key with different access privileges, 248 * this method attempts to authenticate against the B key. 249 * <p>This requires a that the tag be connected. 250 */ 251 public boolean authenticateSectorWithKeyB(int sectorIndex, byte[] key) throws IOException { 252 return authenticate(sectorIndex, key, false); 253 } 254 255 private boolean authenticate(int sector, byte[] key, boolean keyA) throws IOException { 256 validateSector(sector); 257 checkConnected(); 258 259 byte[] cmd = new byte[12]; 260 261 // First byte is the command 262 if (keyA) { 263 cmd[0] = 0x60; // phHal_eMifareAuthentA 264 } else { 265 cmd[0] = 0x61; // phHal_eMifareAuthentB 266 } 267 268 // Second byte is block address 269 // Authenticate command takes a block address. Authenticating a block 270 // of a sector will authenticate the entire sector. 271 cmd[1] = (byte) sectorToBlock(sector); 272 273 // Next 4 bytes are last 4 bytes of UID 274 byte[] uid = getTag().getId(); 275 System.arraycopy(uid, uid.length - 4, cmd, 2, 4); 276 277 // Next 6 bytes are key 278 System.arraycopy(key, 0, cmd, 6, 6); 279 280 try { 281 if (transceive(cmd, false) != null) { 282 return true; 283 } 284 } catch (TagLostException e) { 285 throw e; 286 } catch (IOException e) { 287 // No need to deal with, will return false anyway 288 } 289 return false; 290 } 291 292 /** 293 * Read 16-byte block. 294 * <p>This requires a that the tag be connected. 295 * @throws IOException 296 */ 297 public byte[] readBlock(int blockIndex) throws IOException { 298 validateBlock(blockIndex); 299 checkConnected(); 300 301 byte[] cmd = { 0x30, (byte) blockIndex }; 302 return transceive(cmd, false); 303 } 304 305 /** 306 * Write 16-byte block. 307 * <p>This requires a that the tag be connected. 308 * @throws IOException 309 */ 310 public void writeBlock(int blockIndex, byte[] data) throws IOException { 311 validateBlock(blockIndex); 312 checkConnected(); 313 if (data.length != 16) { 314 throw new IllegalArgumentException("must write 16-bytes"); 315 } 316 317 byte[] cmd = new byte[data.length + 2]; 318 cmd[0] = (byte) 0xA0; // MF write command 319 cmd[1] = (byte) blockIndex; 320 System.arraycopy(data, 0, cmd, 2, data.length); 321 322 transceive(cmd, false); 323 } 324 325 /** 326 * Increment a value block, and store the result in temporary memory. 327 * @param blockIndex 328 * @throws IOException 329 */ 330 public void increment(int blockIndex, int value) throws IOException { 331 validateBlock(blockIndex); 332 validateValueOperand(value); 333 checkConnected(); 334 335 ByteBuffer cmd = ByteBuffer.allocate(6); 336 cmd.order(ByteOrder.LITTLE_ENDIAN); 337 cmd.put( (byte) 0xC1 ); 338 cmd.put( (byte) blockIndex ); 339 cmd.putInt(value); 340 341 transceive(cmd.array(), false); 342 } 343 344 /** 345 * Decrement a value block, and store the result in temporary memory. 346 * @param blockIndex 347 * @throws IOException 348 */ 349 public void decrement(int blockIndex, int value) throws IOException { 350 validateBlock(blockIndex); 351 validateValueOperand(value); 352 checkConnected(); 353 354 ByteBuffer cmd = ByteBuffer.allocate(6); 355 cmd.order(ByteOrder.LITTLE_ENDIAN); 356 cmd.put( (byte) 0xC0 ); 357 cmd.put( (byte) blockIndex ); 358 cmd.putInt(value); 359 360 transceive(cmd.array(), false); 361 } 362 363 private void validateValueOperand(int value) { 364 if (value < 0) { 365 throw new IllegalArgumentException("value operand negative"); 366 } 367 } 368 369 /** 370 * Copy from temporary memory to value block. 371 * @param blockIndex 372 * @throws IOException 373 */ 374 public void transfer(int blockIndex) throws IOException { 375 validateBlock(blockIndex); 376 checkConnected(); 377 378 byte[] cmd = { (byte) 0xB0, (byte) blockIndex }; 379 380 transceive(cmd, false); 381 } 382 383 /** 384 * Copy from value block to temporary memory. 385 * @param blockIndex 386 * @throws IOException 387 */ 388 public void restore(int blockIndex) throws IOException { 389 validateBlock(blockIndex); 390 checkConnected(); 391 392 byte[] cmd = { (byte) 0xC2, (byte) blockIndex }; 393 394 transceive(cmd, false); 395 } 396 397 /** 398 * Send raw NfcA data to a tag and receive the response. 399 * <p> 400 * This method will block until the response is received. It can be canceled 401 * with {@link #close}. 402 * <p>Requires {@link android.Manifest.permission#NFC} permission. 403 * <p>This requires a that the tag be connected. 404 * 405 * @param data bytes to send 406 * @return bytes received in response 407 * @throws IOException if the target is lost or connection closed 408 */ 409 public byte[] transceive(byte[] data) throws IOException { 410 return transceive(data, true); 411 } 412 413 private void validateSector(int sector) { 414 // Do not be too strict on upper bounds checking, since some cards 415 // have more addressable memory than they report. For example, 416 // Mifare Plus 2k cards will appear as Mifare Classic 1k cards when in 417 // Mifare Classic compatibility mode. 418 // Note that issuing a command to an out-of-bounds block is safe - the 419 // tag should report error causing IOException. This validation is a 420 // helper to guard against obvious programming mistakes. 421 if (sector < 0 || sector >= MAX_SECTOR_COUNT) { 422 throw new IndexOutOfBoundsException("sector out of bounds: " + sector); 423 } 424 } 425 426 private void validateBlock(int block) { 427 // Just looking for obvious out of bounds... 428 if (block < 0 || block >= MAX_BLOCK_COUNT) { 429 throw new IndexOutOfBoundsException("block out of bounds: " + block); 430 } 431 } 432} 433