From e45083b11bef915f713379fb4106dd2ebd897d03 Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Fri, 21 Jan 2011 22:11:29 -0800 Subject: Make MifareClassic methods more consistent. Remove method overloading for combinations of sector+block addressing. Instead provide methods that more closly match the raw commands, and more efficient helpers to convert between blocks and sectors. o fix off-by-one bug in getBlockCountInSector() o add BLOCK_SIZE o remove DESFIRE not operating in classic emulation (SAK 0x20) o hide isEmulated(), there is no use case, and the info is available elsewhere o getTotalBlockCount() -> getBlockCount() o getBlockCount(int) -> getBlockCountInSector(int) o introduce blockToSector() and sectorToBlock() o remove authenticateBlock() make it really clear that authentication is per sector, and reduce function explosion. blockToSector() allows you to use authenticateSector... o explicit authenticateSectorWithKeyA() / authenticateSectorWithKeyB() get rid of magic boolean o remove all (int sector, int block) parameters always address by absolute block. this makes the API crystal clear, and helps reduce function explosion o validation of all sector and block indices o dont & 0xff when converting to byte - its redundant o Remove TYPE_OTHER. Mifare Classic types are well-known and stable. Change-Id: I3c9f8254ff307f31b388b3d7592c862d5de6afa5 --- api/current.xml | 124 +++------ core/java/android/nfc/tech/MifareClassic.java | 375 +++++++++++++------------- 2 files changed, 229 insertions(+), 270 deletions(-) diff --git a/api/current.xml b/api/current.xml index 71ebbb533604..e612087cb500 100644 --- a/api/current.xml +++ b/api/current.xml @@ -101246,7 +101246,7 @@ deprecated="not deprecated" visibility="public" > - - + - - - - + - - - - + - - - - + + + - - + - - - + - - - + - - - - - + - - + - - + - - - + @@ -101495,32 +101476,24 @@ deprecated="not deprecated" visibility="public" > - + - - - - - - - - - - + - - - - Every sector has an A and B key with different access privileges, + * this method attempts to authenticate against the A key. + *

This requires a that the tag be connected. + */ + public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException { + return authenticate(sectorIndex, key, true); + } + + /** + * Authenticate a sector. + *

Every sector has an A and B key with different access privileges, + * this method attempts to authenticate against the B key. *

This requires a that the tag be connected. */ - public boolean authenticateBlock(int block, byte[] key, boolean keyA) throws IOException { + public boolean authenticateSectorWithKeyB(int sectorIndex, byte[] key) throws IOException { + return authenticate(sectorIndex, key, false); + } + + private boolean authenticate(int sector, byte[] key, boolean keyA) throws IOException { + validateSector(sector); checkConnected(); byte[] cmd = new byte[12]; @@ -275,7 +264,9 @@ public final class MifareClassic extends BasicTagTechnology { } // Second byte is block address - cmd[1] = (byte) block; + // Authenticate command takes a block address. Authenticating a block + // of a sector will authenticate the entire sector. + cmd[1] = (byte) sectorToBlock(sector); // Next 4 bytes are last 4 bytes of UID byte[] uid = getTag().getId(); @@ -285,7 +276,7 @@ public final class MifareClassic extends BasicTagTechnology { System.arraycopy(key, 0, cmd, 6, 6); try { - if ((transceive(cmd, false) != null)) { + if (transceive(cmd, false) != null) { return true; } } catch (TagLostException e) { @@ -297,106 +288,92 @@ public final class MifareClassic extends BasicTagTechnology { } /** - * Authenticate for a given sector. + * Read 16-byte block. *

This requires a that the tag be connected. + * @throws IOException */ - public boolean authenticateSector(int sector, byte[] key, boolean keyA) throws IOException { + public byte[] readBlock(int blockIndex) throws IOException { + validateBlock(blockIndex); checkConnected(); - byte addr = (byte) ((firstBlockInSector(sector)) & 0xff); - - // Note that authenticating a block of a sector, will authenticate - // the entire sector. - return authenticateBlock(addr, key, keyA); + byte[] cmd = { 0x30, (byte) blockIndex }; + return transceive(cmd, false); } /** - * Sector indexing starts at 0. - * Block indexing starts at 0, and resets in each sector. + * Write 16-byte block. *

This requires a that the tag be connected. * @throws IOException */ - public byte[] readBlock(int sector, int block) throws IOException { + public void writeBlock(int blockIndex, byte[] data) throws IOException { + validateBlock(blockIndex); checkConnected(); + if (data.length != 16) { + throw new IllegalArgumentException("must write 16-bytes"); + } - byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff); - return readBlock(addr); + byte[] cmd = new byte[data.length + 2]; + cmd[0] = (byte) 0xA0; // MF write command + cmd[1] = (byte) blockIndex; + System.arraycopy(data, 0, cmd, 2, data.length); + + transceive(cmd, false); } /** - * Reads absolute block index. - *

This requires a that the tag be connected. + * Increment a value block, and store the result in temporary memory. + * @param block * @throws IOException */ - public byte[] readBlock(int block) throws IOException { + public void increment(int blockIndex) throws IOException { + validateBlock(blockIndex); checkConnected(); - byte addr = (byte) block; - byte[] blockread_cmd = { 0x30, addr }; + byte[] cmd = { (byte) 0xC1, (byte) blockIndex }; - return transceive(blockread_cmd, false); + transceive(cmd, false); } /** - * Writes absolute block index. - *

This requires a that the tag be connected. + * Decrement a value block, and store the result in temporary memory. + * @param block * @throws IOException */ - public void writeBlock(int block, byte[] data) throws IOException { + public void decrement(int blockIndex) throws IOException { + validateBlock(blockIndex); checkConnected(); - byte addr = (byte) block; - byte[] blockwrite_cmd = new byte[data.length + 2]; - blockwrite_cmd[0] = (byte) 0xA0; // MF write command - blockwrite_cmd[1] = addr; - System.arraycopy(data, 0, blockwrite_cmd, 2, data.length); + byte[] cmd = { (byte) 0xC0, (byte) blockIndex }; - transceive(blockwrite_cmd, false); + transceive(cmd, false); } /** - * Writes relative block in sector. - *

This requires a that the tag be connected. + * Copy from temporary memory to value block. + * @param block * @throws IOException */ - public void writeBlock(int sector, int block, byte[] data) throws IOException { - checkConnected(); - - byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff); - - writeBlock(addr, data); - } - - public void increment(int block) throws IOException { - checkConnected(); - - byte[] incr_cmd = { (byte) 0xC1, (byte) block }; - - transceive(incr_cmd, false); - } - - public void decrement(int block) throws IOException { - checkConnected(); - - byte[] decr_cmd = { (byte) 0xC0, (byte) block }; - - transceive(decr_cmd, false); - } - - public void transfer(int block) throws IOException { + public void transfer(int blockIndex) throws IOException { + validateBlock(blockIndex); checkConnected(); - byte[] trans_cmd = { (byte) 0xB0, (byte) block }; + byte[] cmd = { (byte) 0xB0, (byte) blockIndex }; - transceive(trans_cmd, false); + transceive(cmd, false); } - public void restore(int block) throws IOException { + /** + * Copy from value block to temporary memory. + * @param block + * @throws IOException + */ + public void restore(int blockIndex) throws IOException { + validateBlock(blockIndex); checkConnected(); - byte[] rest_cmd = { (byte) 0xC2, (byte) block }; + byte[] cmd = { (byte) 0xC2, (byte) blockIndex }; - transceive(rest_cmd, false); + transceive(cmd, false); } /** @@ -414,4 +391,24 @@ public final class MifareClassic extends BasicTagTechnology { public byte[] transceive(byte[] data) throws IOException { return transceive(data, true); } + + private void validateSector(int sector) { + // Do not be too strict on upper bounds checking, since some cards + // have more addressable memory than they report. For example, + // Mifare Plus 2k cards will appear as Mifare Classic 1k cards when in + // Mifare Classic compatibility mode. + // Note that issuing a command to an out-of-bounds block is safe - the + // tag should report error causing IOException. This validation is a + // helper to guard against obvious programming mistakes. + if (sector < 0 || sector >= MAX_SECTOR_COUNT) { + throw new IndexOutOfBoundsException("sector out of bounds: " + sector); + } + } + + private void validateBlock(int block) { + // Just looking for obvious out of bounds... + if (block < 0 || block >= MAX_BLOCK_COUNT) { + throw new IndexOutOfBoundsException("block out of bounds: " + block); + } + } } -- cgit v1.2.3-59-g8ed1b