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