/** * Set the key files for {@link #buildNextKeyMapPart()}. Key duplicates from the key file will be * removed. * * @param keyFiles One or more key files. These files are simple text files with one key per line. * Empty lines and lines STARTING with "#" will not be interpreted. * @param context The context in which the possible "Out of memory"-Toast will be shown. * @return True if the key files are correctly loaded. False on error (out of memory). */ public boolean setKeyFile(File[] keyFiles, Context context) { HashSet<byte[]> keys = new HashSet<byte[]>(); for (File file : keyFiles) { String[] lines = Common.readFileLineByLine(file, false, context); if (lines != null) { for (String line : lines) { if (!line.equals("") && line.length() == 12 && line.matches("[0-9A-Fa-f]+")) { try { keys.add(Common.hexStringToByteArray(line)); } catch (OutOfMemoryError e) { // Error. Too many keys (out of memory). Toast.makeText(context, R.string.info_to_many_keys, Toast.LENGTH_LONG).show(); return false; } } } } } if (keys.size() > 0) { mKeysWithOrder = new ArrayList<byte[]>(keys); } return true; }
/** * Read a as much as possible from a sector with the given key. Best results are gained from a * valid key B (except key B is marked as readable in the access conditions). * * @param sectorIndex Index of the Sector to read. (For Mifare Classic 1K: 0-63) * @param key Key for the authentication. * @param useAsKeyB If true, key will be treated as key B for authentication. * @return Array of blocks (index 0-3 or 0-15). If a block or a key is marked with {@link * #NO_DATA} or {@link #NO_KEY} it means that this data could be read or found. On * authentication error "null" will be returned. * @throws TagLostException When tag is lost. * @see #mergeSectorData(String[], String[]) */ public String[] readSector(int sectorIndex, byte[] key, boolean useAsKeyB) throws TagLostException { boolean auth = authenticate(sectorIndex, key, useAsKeyB); String[] ret = null; // Read sector. if (auth) { // Read all blocks. ArrayList<String> blocks = new ArrayList<String>(); int firstBlock = mMFC.sectorToBlock(sectorIndex); int lastBlock = firstBlock + 4; if (mMFC.getSize() == MifareClassic.SIZE_4K && sectorIndex > 31) { lastBlock = firstBlock + 16; } for (int i = firstBlock; i < lastBlock; i++) { try { byte blockBytes[] = mMFC.readBlock(i); // mMFC.readBlock(i) must return 16 bytes or throw an error. // At least this is what the documentation says. // On Samsungs Galaxy S5 however, it sometimes // returns < 16 bytes for unknown reasons. if (blockBytes.length != 16) { throw new IOException(); } blocks.add(Common.byte2HexString(blockBytes)); } catch (TagLostException e) { throw e; } catch (IOException e) { // Could not read block. // (Maybe due to key/authentication method.) Log.d(LOG_TAG, "Error while reading block " + i + " from tag."); blocks.add(NO_DATA); if (!mMFC.isConnected()) { throw new TagLostException("Tag removed during readSector(...)"); } // After error reauthentication is needed. auth = authenticate(sectorIndex, key, useAsKeyB); } } ret = blocks.toArray(new String[blocks.size()]); int last = ret.length - 1; // Merge key in last block (sector trailer). if (!useAsKeyB) { if (isKeyBReadable(Common.hexStringToByteArray(ret[last].substring(12, 20)))) { ret[last] = Common.byte2HexString(key) + ret[last].substring(12, 32); } else { ret[last] = Common.byte2HexString(key) + ret[last].substring(12, 20) + NO_KEY; } } else { if (ret[0].equals(NO_DATA)) { // If Key B may be read in the corresponding Sector Trailer, // it cannot serve for authentication (according to NXP). // What they mean is that you can authenticate successfully, // but can not read data. In this case the // readBlock() result is 0 for each block. ret = null; } else { ret[last] = NO_KEY + ret[last].substring(12, 20) + Common.byte2HexString(key); } } } return ret; }