MifareClassic.java revision 46797ac098e90cbef5c266b75fb37fc06e9acc80
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 * Provides access to MIFARE Classic properties and I/O operations on a {@link Tag}.
29 *
30 * <p>Acquire a {@link MifareClassic} object using {@link #get}.
31 *
32 * <p>MIFARE Classic is also known as MIFARE Standard.
33 * <p>MIFARE Classic tags are divided into sectors, and each sector is sub-divided into
34 * blocks. Block size is always 16 bytes ({@link #BLOCK_SIZE}. Sector size varies.
35 * <ul>
36 * <li>MIFARE Classic Mini are 320 bytes ({@link #SIZE_MINI}), with 5 sectors each of 4 blocks.
37 * <li>MIFARE Classic 1k are 1024 bytes ({@link #SIZE_1K}), with 16 sectors each of 4 blocks.
38 * <li>MIFARE Classic 2k are 2048 bytes ({@link #SIZE_2K}), with 32 sectors each of 4 blocks.
39 * <li>MIFARE Classic 4k} are 4096 bytes ({@link #SIZE_4K}). The first 32 sectors contain 4 blocks
40 * and the last 8 sectors contain 16 blocks.
41 * </ul>
42 *
43 * <p>MIFARE Classic tags require authentication on a per-sector basis before any
44 * other I/O operations on that sector can be performed. There are two keys per sector,
45 * and ACL bits determine what I/O operations are allowed on that sector after
46 * authenticating with a key. {@see #authenticateSectorWithKeyA} and
47 * {@see #authenticateSectorWithKeyB}.
48 *
49 * <p>Three well-known authentication keys are defined in this class:
50 * {@link #KEY_DEFAULT}, {@link #KEY_MIFARE_APPLICATION_DIRECTORY},
51 * {@link #KEY_NFC_FORUM}.
52 * <ul>
53 * <li>{@link #KEY_DEFAULT} is the default factory key for MIFARE Classic.
54 * <li>{@link #KEY_MIFARE_APPLICATION_DIRECTORY} is the well-known key for
55 * MIFARE Classic cards that have been formatted according to the
56 * MIFARE Application Directory (MAD) specification.
57 * <li>{@link #KEY_NFC_FORUM} is the well-known key for MIFARE Classic cards that
58 * have been formatted according to the NFC
59 *
60 * <p>Implementation of this class on a Android NFC device is optional.
61 * If it is not implemented, then
62 * {@link MifareClassic} will never be enumerated in {@link Tag#getTechList}.
63 * If it is enumerated, then all {@link MifareClassic} I/O operations will be supported,
64 * and {@link Ndef#MIFARE_CLASSIC} NDEF tags will also be supported. In either case,
65 * {@link NfcA} will also be enumerated on the tag, because all MIFARE Classic tags are also
66 * {@link NfcA}.
67 */
68public final class MifareClassic extends BasicTagTechnology {
69    /**
70     * The default factory key.
71     */
72    public static final byte[] KEY_DEFAULT =
73            {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF};
74    /**
75     * The well-known key for tags formatted according to the
76     * MIFARE Application Directory (MAD) specification.
77     */
78    public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY =
79            {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5};
80    /**
81     * The well-known key for tags formatted according to the
82     * NDEF on Mifare Classic specification.
83     */
84    public static final byte[] KEY_NFC_FORUM =
85            {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7};
86
87    /** A Mifare Classic compatible card of unknown type */
88    public static final int TYPE_UNKNOWN = -1;
89    /** A MIFARE Classic tag */
90    public static final int TYPE_CLASSIC = 0;
91    /** A MIFARE Plus tag */
92    public static final int TYPE_PLUS = 1;
93    /** A MIFARE Pro tag */
94    public static final int TYPE_PRO = 2;
95
96    /** Tag contains 16 sectors, each with 4 blocks. */
97    public static final int SIZE_1K = 1024;
98    /** Tag contains 32 sectors, each with 4 blocks. */
99    public static final int SIZE_2K = 2048;
100    /**
101     * Tag contains 40 sectors. The first 32 sectors contain 4 blocks and the last 8 sectors
102     * contain 16 blocks.
103     */
104    public static final int SIZE_4K = 4096;
105    /** Tag contains 5 sectors, each with 4 blocks. */
106    public static final int SIZE_MINI = 320;
107
108    /** Size of a MIFARE Classic block (in bytes) */
109    public static final int BLOCK_SIZE = 16;
110
111    private static final int MAX_BLOCK_COUNT = 256;
112    private static final int MAX_SECTOR_COUNT = 40;
113
114    private boolean mIsEmulated;
115    private int mType;
116    private int mSize;
117
118    /**
119     * Get an instance of {@link MifareClassic} for the given tag.
120     * <p>Does not cause any RF activity and does not block.
121     * <p>Returns null if {@link MifareClassic} was not enumerated in {@link Tag#getTechList}.
122     * This indicates the tag is not MIFARE Classic compatible, or this Android
123     * device does not support MIFARE Classic.
124     *
125     * @param tag an MIFARE Classic compatible tag
126     * @return MIFARE Classic object
127     */
128    public static MifareClassic get(Tag tag) {
129        if (!tag.hasTech(TagTechnology.MIFARE_CLASSIC)) return null;
130        try {
131            return new MifareClassic(tag);
132        } catch (RemoteException e) {
133            return null;
134        }
135    }
136
137    /** @hide */
138    public MifareClassic(Tag tag) throws RemoteException {
139        super(tag, TagTechnology.MIFARE_CLASSIC);
140
141        NfcA a = NfcA.get(tag);  // Mifare Classic is always based on NFC a
142
143        mIsEmulated = false;
144
145        switch (a.getSak()) {
146        case 0x08:
147            mType = TYPE_CLASSIC;
148            mSize = SIZE_1K;
149            break;
150        case 0x09:
151            mType = TYPE_CLASSIC;
152            mSize = SIZE_MINI;
153            break;
154        case 0x10:
155            mType = TYPE_PLUS;
156            mSize = SIZE_2K;
157            // SecLevel = SL2
158            break;
159        case 0x11:
160            mType = TYPE_PLUS;
161            mSize = SIZE_4K;
162            // Seclevel = SL2
163            break;
164        case 0x18:
165            mType = TYPE_CLASSIC;
166            mSize = SIZE_4K;
167            break;
168        case 0x28:
169            mType = TYPE_CLASSIC;
170            mSize = SIZE_1K;
171            mIsEmulated = true;
172            break;
173        case 0x38:
174            mType = TYPE_CLASSIC;
175            mSize = SIZE_4K;
176            mIsEmulated = true;
177            break;
178        case 0x88:
179            mType = TYPE_CLASSIC;
180            mSize = SIZE_1K;
181            // NXP-tag: false
182            break;
183        case 0x98:
184        case 0xB8:
185            mType = TYPE_PRO;
186            mSize = SIZE_4K;
187            break;
188        default:
189            // Stack incorrectly reported a MifareClassic. We cannot handle this
190            // gracefully - we have no idea of the memory layout. Bail.
191            throw new RuntimeException(
192                    "Tag incorrectly enumerated as Mifare Classic, SAK = " + a.getSak());
193        }
194    }
195
196    /**
197     * Return the type of this MIFARE Classic compatible tag.
198     * <p>One of {@link #TYPE_UNKNOWN}, {@link #TYPE_CLASSIC}, {@link #TYPE_PLUS} or
199     * {@link #TYPE_PRO}.
200     * <p>Does not cause any RF activity and does not block.
201     *
202     * @return type
203     */
204    public int getType() {
205        return mType;
206    }
207
208    /**
209     * Return the size of the tag in bytes
210     * <p>One of {@link #SIZE_MINI}, {@link #SIZE_1K}, {@link #SIZE_2K}, {@link #SIZE_4K}.
211     * These constants are equal to their respective size in bytes.
212     * <p>Does not cause any RF activity and does not block.
213     * @return size in bytes
214     */
215    public int getSize() {
216        return mSize;
217    }
218
219    /**
220     * Return true if the tag is emulated, determined at discovery time.
221     * These are actually smart-cards that emulate a Mifare Classic interface.
222     * They can be treated identically to a Mifare Classic tag.
223     * @hide
224     */
225    public boolean isEmulated() {
226        return mIsEmulated;
227    }
228
229    /**
230     * Return the number of MIFARE Classic sectors.
231     * <p>Does not cause any RF activity and does not block.
232     * @return number of sectors
233     */
234    public int getSectorCount() {
235        switch (mSize) {
236        case SIZE_1K:
237            return 16;
238        case SIZE_2K:
239            return 32;
240        case SIZE_4K:
241            return 40;
242        case SIZE_MINI:
243            return 5;
244        default:
245            return 0;
246        }
247    }
248
249    /**
250     * Return the total number of MIFARE Classic blocks.
251     * <p>Does not cause any RF activity and does not block.
252     * @return total number of blocks
253     */
254    public int getBlockCount() {
255        return mSize / BLOCK_SIZE;
256    }
257
258    /**
259     * Return the number of blocks in the given sector.
260     * <p>Does not cause any RF activity and does not block.
261     *
262     * @param sectorIndex index of sector, starting from 0
263     * @return number of blocks in the sector
264     */
265    public int getBlockCountInSector(int sectorIndex) {
266        validateSector(sectorIndex);
267
268        if (sectorIndex < 32) {
269            return 4;
270        } else {
271            return 16;
272        }
273    }
274
275    /**
276     * Return the sector that contains a given block.
277     * <p>Does not cause any RF activity and does not block.
278     *
279     * @param blockIndex index of block to lookup, starting from 0
280     * @return sector index that contains the block
281     */
282    public int blockToSector(int blockIndex) {
283        validateBlock(blockIndex);
284
285        if (blockIndex < 32 * 4) {
286            return blockIndex / 4;
287        } else {
288            return 32 + (blockIndex - 32 * 4) / 16;
289        }
290    }
291
292    /**
293     * Return the first block of a given sector.
294     * <p>Does not cause any RF activity and does not block.
295     *
296     * @param sectorIndex index of sector to lookup, starting from 0
297     * @return block index of first block in sector
298     */
299    public int sectorToBlock(int sectorIndex) {
300        if (sectorIndex < 32) {
301            return sectorIndex * 4;
302        } else {
303            return 32 * 4 + (sectorIndex - 32) * 16;
304        }
305    }
306
307    /**
308     * Authenticate a sector with key A.
309     *
310     * <p>Successful authentication of a sector with key A enables other
311     * I/O operations on that sector. The set of operations granted by key A
312     * key depends on the ACL bits set in that sector. For more information
313     * see the MIFARE Classic specification on {@see http://www.nxp.com}.
314     *
315     * <p>A failed authentication attempt causes an implicit reconnection to the
316     * tag, so authentication to other sectors will be lost.
317     *
318     * <p>This is an I/O operation and will block until complete. It must
319     * not be called from the main application thread. A blocked call will be canceled with
320     * {@link IOException} if {@link #close} is called from another thread.
321     *
322     * @param sectorIndex index of sector to authenticate, starting from 0
323     * @param key 6-byte authentication key
324     * @return true on success, false on authentication failure
325     * @throws TagLostException if the tag leaves the field
326     * @throws IOException if there is an I/O failure, or the operation is canceled
327     */
328    public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException {
329        return authenticate(sectorIndex, key, true);
330    }
331
332    /**
333     * Authenticate a sector with key B.
334     *
335     * <p>Successful authentication of a sector with key B enables other
336     * I/O operations on that sector. The set of operations granted by key B
337     * depends on the ACL bits set in that sector. For more information
338     * see the MIFARE Classic specification on {@see http://www.nxp.com}.
339     *
340     * <p>A failed authentication attempt causes an implicit reconnection to the
341     * tag, so authentication to other sectors will be lost.
342     *
343     * <p>This is an I/O operation and will block until complete. It must
344     * not be called from the main application thread. A blocked call will be canceled with
345     * {@link IOException} if {@link #close} is called from another thread.
346     *
347     * @param sectorIndex index of sector to authenticate, starting from 0
348     * @param key 6-byte authentication key
349     * @return true on success, false on authentication failure
350     * @throws TagLostException if the tag leaves the field
351     * @throws IOException if there is an I/O failure, or the operation is canceled
352     */
353    public boolean authenticateSectorWithKeyB(int sectorIndex, byte[] key) throws IOException {
354        return authenticate(sectorIndex, key, false);
355    }
356
357    private boolean authenticate(int sector, byte[] key, boolean keyA) throws IOException {
358        validateSector(sector);
359        checkConnected();
360
361        byte[] cmd = new byte[12];
362
363        // First byte is the command
364        if (keyA) {
365            cmd[0] = 0x60; // phHal_eMifareAuthentA
366        } else {
367            cmd[0] = 0x61; // phHal_eMifareAuthentB
368        }
369
370        // Second byte is block address
371        // Authenticate command takes a block address. Authenticating a block
372        // of a sector will authenticate the entire sector.
373        cmd[1] = (byte) sectorToBlock(sector);
374
375        // Next 4 bytes are last 4 bytes of UID
376        byte[] uid = getTag().getId();
377        System.arraycopy(uid, uid.length - 4, cmd, 2, 4);
378
379        // Next 6 bytes are key
380        System.arraycopy(key, 0, cmd, 6, 6);
381
382        try {
383            if (transceive(cmd, false) != null) {
384                return true;
385            }
386        } catch (TagLostException e) {
387            throw e;
388        } catch (IOException e) {
389            // No need to deal with, will return false anyway
390        }
391        return false;
392    }
393
394    /**
395     * Read 16-byte block.
396     *
397     * <p>This is an I/O operation and will block until complete. It must
398     * not be called from the main application thread. A blocked call will be canceled with
399     * {@link IOException} if {@link #close} is called from another thread.
400     *
401     * @param blockIndex index of block to read, starting from 0
402     * @return 16 byte block
403     * @throws TagLostException if the tag leaves the field
404     * @throws IOException if there is an I/O failure, or the operation is canceled
405     */
406    public byte[] readBlock(int blockIndex) throws IOException {
407        validateBlock(blockIndex);
408        checkConnected();
409
410        byte[] cmd = { 0x30, (byte) blockIndex };
411        return transceive(cmd, false);
412    }
413
414    /**
415     * Write 16-byte block.
416     *
417     * <p>This is an I/O operation and will block until complete. It must
418     * not be called from the main application thread. A blocked call will be canceled with
419     * {@link IOException} if {@link #close} is called from another thread.
420     *
421     * @param blockIndex index of block to write, starting from 0
422     * @param data 16 bytes of data to write
423     * @throws TagLostException if the tag leaves the field
424     * @throws IOException if there is an I/O failure, or the operation is canceled
425     */
426    public void writeBlock(int blockIndex, byte[] data) throws IOException {
427        validateBlock(blockIndex);
428        checkConnected();
429        if (data.length != 16) {
430            throw new IllegalArgumentException("must write 16-bytes");
431        }
432
433        byte[] cmd = new byte[data.length + 2];
434        cmd[0] = (byte) 0xA0; // MF write command
435        cmd[1] = (byte) blockIndex;
436        System.arraycopy(data, 0, cmd, 2, data.length);
437
438        transceive(cmd, false);
439    }
440
441    /**
442     * Increment a value block, storing the result in the temporary block on the tag.
443     *
444     * <p>This is an I/O operation and will block until complete. It must
445     * not be called from the main application thread. A blocked call will be canceled with
446     * {@link IOException} if {@link #close} is called from another thread.
447     *
448     * @param blockIndex index of block to increment, starting from 0
449     * @param value non-negative to increment by
450     * @throws TagLostException if the tag leaves the field
451     * @throws IOException if there is an I/O failure, or the operation is canceled
452     */
453    public void increment(int blockIndex, int value) throws IOException {
454        validateBlock(blockIndex);
455        validateValueOperand(value);
456        checkConnected();
457
458        ByteBuffer cmd = ByteBuffer.allocate(6);
459        cmd.order(ByteOrder.LITTLE_ENDIAN);
460        cmd.put( (byte) 0xC1 );
461        cmd.put( (byte) blockIndex );
462        cmd.putInt(value);
463
464        transceive(cmd.array(), false);
465    }
466
467    /**
468     * Decrement a value block, storing the result in the temporary block on the tag.
469     *
470     * <p>This is an I/O operation and will block until complete. It must
471     * not be called from the main application thread. A blocked call will be canceled with
472     * {@link IOException} if {@link #close} is called from another thread.
473     *
474     * @param blockIndex index of block to decrement, starting from 0
475     * @param value non-negative to decrement by
476     * @throws TagLostException if the tag leaves the field
477     * @throws IOException if there is an I/O failure, or the operation is canceled
478     */
479    public void decrement(int blockIndex, int value) throws IOException {
480        validateBlock(blockIndex);
481        validateValueOperand(value);
482        checkConnected();
483
484        ByteBuffer cmd = ByteBuffer.allocate(6);
485        cmd.order(ByteOrder.LITTLE_ENDIAN);
486        cmd.put( (byte) 0xC0 );
487        cmd.put( (byte) blockIndex );
488        cmd.putInt(value);
489
490        transceive(cmd.array(), false);
491    }
492
493    /**
494     * Copy from the temporary block to a value block.
495     *
496     * <p>This is an I/O operation and will block until complete. It must
497     * not be called from the main application thread. A blocked call will be canceled with
498     * {@link IOException} if {@link #close} is called from another thread.
499     *
500     * @param blockIndex index of block to copy to
501     * @throws TagLostException if the tag leaves the field
502     * @throws IOException if there is an I/O failure, or the operation is canceled
503     */
504    public void transfer(int blockIndex) throws IOException {
505        validateBlock(blockIndex);
506        checkConnected();
507
508        byte[] cmd = { (byte) 0xB0, (byte) blockIndex };
509
510        transceive(cmd, false);
511    }
512
513    /**
514     * Copy from a value block to the temporary block.
515     *
516     * <p>This is an I/O operation and will block until complete. It must
517     * not be called from the main application thread. A blocked call will be canceled with
518     * {@link IOException} if {@link #close} is called from another thread.
519     *
520     * @param blockIndex index of block to copy from
521     * @throws TagLostException if the tag leaves the field
522     * @throws IOException if there is an I/O failure, or the operation is canceled
523     */
524    public void restore(int blockIndex) throws IOException {
525        validateBlock(blockIndex);
526        checkConnected();
527
528        byte[] cmd = { (byte) 0xC2, (byte) blockIndex };
529
530        transceive(cmd, false);
531    }
532
533    /**
534     * Send raw NfcA data to a tag and receive the response.
535     *
536     * <p>This is equivalent to connecting to this tag via {@link NfcA}
537     * and calling {@link NfcA#transceive}. Note that all MIFARE Classic
538     * tags are based on {@link NfcA} technology.
539     *
540     * <p>This is an I/O operation and will block until complete. It must
541     * not be called from the main application thread. A blocked call will be canceled with
542     * {@link IOException} if {@link #close} is called from another thread.
543     *
544     * @see NfcA#transceive
545     */
546    public byte[] transceive(byte[] data) throws IOException {
547        return transceive(data, true);
548    }
549
550    private static void validateSector(int sector) {
551        // Do not be too strict on upper bounds checking, since some cards
552        // have more addressable memory than they report. For example,
553        // Mifare Plus 2k cards will appear as Mifare Classic 1k cards when in
554        // Mifare Classic compatibility mode.
555        // Note that issuing a command to an out-of-bounds block is safe - the
556        // tag should report error causing IOException. This validation is a
557        // helper to guard against obvious programming mistakes.
558        if (sector < 0 || sector >= MAX_SECTOR_COUNT) {
559            throw new IndexOutOfBoundsException("sector out of bounds: " + sector);
560        }
561    }
562
563    private static void validateBlock(int block) {
564        // Just looking for obvious out of bounds...
565        if (block < 0 || block >= MAX_BLOCK_COUNT) {
566            throw new IndexOutOfBoundsException("block out of bounds: " + block);
567        }
568    }
569
570    private static void validateValueOperand(int value) {
571        if (value < 0) {
572            throw new IllegalArgumentException("value operand negative");
573        }
574    }
575}
576