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