MifareClassic.java revision 74fe6c6b245ebe7d3b3d96962c32980d88dca4f5
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    public int getBlockCount() {
254        return mSize / BLOCK_SIZE;
255    }
256
257    /**
258     * Return the number of blocks in the given sector.
259     * <p>Does not cause any RF activity and does not block.
260     *
261     * @param sectorIndex index of sector, starting from 0
262     * @return number of blocks in the sector
263     */
264    public int getBlockCountInSector(int sectorIndex) {
265        validateSector(sectorIndex);
266
267        if (sectorIndex < 32) {
268            return 4;
269        } else {
270            return 16;
271        }
272    }
273
274    /**
275     * Return the sector that contains a given block.
276     * <p>Does not cause any RF activity and does not block.
277     *
278     * @param blockIndex index of block to lookup, starting from 0
279     * @return sector index that contains the block
280     */
281    public int blockToSector(int blockIndex) {
282        validateBlock(blockIndex);
283
284        if (blockIndex < 32 * 4) {
285            return blockIndex / 4;
286        } else {
287            return 32 + (blockIndex - 32 * 4) / 16;
288        }
289    }
290
291    /**
292     * Return the first block of a given sector.
293     * <p>Does not cause any RF activity and does not block.
294     *
295     * @param sectorIndex index of sector to lookup, starting from 0
296     * @return block index of first block in sector
297     */
298    public int sectorToBlock(int sectorIndex) {
299        if (sectorIndex < 32) {
300            return sectorIndex * 4;
301        } else {
302            return 32 * 4 + (sectorIndex - 32) * 16;
303        }
304    }
305
306    /**
307     * Authenticate a sector with key A.
308     *
309     * <p>Successful authentication of a sector with key A enables other
310     * I/O operations on that sector. The set of operations granted by key A
311     * key depends on the ACL bits set in that sector. For more information
312     * see the MIFARE Classic specification on {@see http://www.nxp.com}.
313     *
314     * <p>A failed authentication attempt causes an implicit reconnection to the
315     * tag, so authentication to other sectors will be lost.
316     *
317     * <p>This is an I/O operation and will block until complete. It must
318     * not be called from the main application thread. A blocked call will be canceled with
319     * {@link IOException} if {@link #close} is called from another thread.
320     *
321     * @param sectorIndex index of sector to authenticate, starting from 0
322     * @param key 6-byte authentication key
323     * @return true on success, false on authentication failure
324     * @throws TagLostException if the tag leaves the field
325     * @throws IOException if there is an I/O failure, or the operation is canceled
326     */
327    public boolean authenticateSectorWithKeyA(int sectorIndex, byte[] key) throws IOException {
328        return authenticate(sectorIndex, key, true);
329    }
330
331    /**
332     * Authenticate a sector with key B.
333     *
334     * <p>Successful authentication of a sector with key B enables other
335     * I/O operations on that sector. The set of operations granted by key B
336     * depends on the ACL bits set in that sector. For more information
337     * see the MIFARE Classic specification on {@see http://www.nxp.com}.
338     *
339     * <p>A failed authentication attempt causes an implicit reconnection to the
340     * tag, so authentication to other sectors will be lost.
341     *
342     * <p>This is an I/O operation and will block until complete. It must
343     * not be called from the main application thread. A blocked call will be canceled with
344     * {@link IOException} if {@link #close} is called from another thread.
345     *
346     * @param sectorIndex index of sector to authenticate, starting from 0
347     * @param key 6-byte authentication key
348     * @return true on success, false on authentication failure
349     * @throws TagLostException if the tag leaves the field
350     * @throws IOException if there is an I/O failure, or the operation is canceled
351     */
352    public boolean authenticateSectorWithKeyB(int sectorIndex, byte[] key) throws IOException {
353        return authenticate(sectorIndex, key, false);
354    }
355
356    private boolean authenticate(int sector, byte[] key, boolean keyA) throws IOException {
357        validateSector(sector);
358        checkConnected();
359
360        byte[] cmd = new byte[12];
361
362        // First byte is the command
363        if (keyA) {
364            cmd[0] = 0x60; // phHal_eMifareAuthentA
365        } else {
366            cmd[0] = 0x61; // phHal_eMifareAuthentB
367        }
368
369        // Second byte is block address
370        // Authenticate command takes a block address. Authenticating a block
371        // of a sector will authenticate the entire sector.
372        cmd[1] = (byte) sectorToBlock(sector);
373
374        // Next 4 bytes are last 4 bytes of UID
375        byte[] uid = getTag().getId();
376        System.arraycopy(uid, uid.length - 4, cmd, 2, 4);
377
378        // Next 6 bytes are key
379        System.arraycopy(key, 0, cmd, 6, 6);
380
381        try {
382            if (transceive(cmd, false) != null) {
383                return true;
384            }
385        } catch (TagLostException e) {
386            throw e;
387        } catch (IOException e) {
388            // No need to deal with, will return false anyway
389        }
390        return false;
391    }
392
393    /**
394     * Read 16-byte block.
395     *
396     * <p>This is an I/O operation and will block until complete. It must
397     * not be called from the main application thread. A blocked call will be canceled with
398     * {@link IOException} if {@link #close} is called from another thread.
399     *
400     * @param blockIndex index of block to read, starting from 0
401     * @return 16 byte block
402     * @throws TagLostException if the tag leaves the field
403     * @throws IOException if there is an I/O failure, or the operation is canceled
404     */
405    public byte[] readBlock(int blockIndex) throws IOException {
406        validateBlock(blockIndex);
407        checkConnected();
408
409        byte[] cmd = { 0x30, (byte) blockIndex };
410        return transceive(cmd, false);
411    }
412
413    /**
414     * Write 16-byte block.
415     *
416     * <p>This is an I/O operation and will block until complete. It must
417     * not be called from the main application thread. A blocked call will be canceled with
418     * {@link IOException} if {@link #close} is called from another thread.
419     *
420     * @param blockIndex index of block to write, starting from 0
421     * @param data 16 bytes of data to write
422     * @throws TagLostException if the tag leaves the field
423     * @throws IOException if there is an I/O failure, or the operation is canceled
424     */
425    public void writeBlock(int blockIndex, byte[] data) throws IOException {
426        validateBlock(blockIndex);
427        checkConnected();
428        if (data.length != 16) {
429            throw new IllegalArgumentException("must write 16-bytes");
430        }
431
432        byte[] cmd = new byte[data.length + 2];
433        cmd[0] = (byte) 0xA0; // MF write command
434        cmd[1] = (byte) blockIndex;
435        System.arraycopy(data, 0, cmd, 2, data.length);
436
437        transceive(cmd, false);
438    }
439
440    /**
441     * Increment a value block, storing the result in the temporary block on the tag.
442     *
443     * <p>This is an I/O operation and will block until complete. It must
444     * not be called from the main application thread. A blocked call will be canceled with
445     * {@link IOException} if {@link #close} is called from another thread.
446     *
447     * @param blockIndex index of block to increment, starting from 0
448     * @param value non-negative to increment by
449     * @throws TagLostException if the tag leaves the field
450     * @throws IOException if there is an I/O failure, or the operation is canceled
451     */
452    public void increment(int blockIndex, int value) throws IOException {
453        validateBlock(blockIndex);
454        validateValueOperand(value);
455        checkConnected();
456
457        ByteBuffer cmd = ByteBuffer.allocate(6);
458        cmd.order(ByteOrder.LITTLE_ENDIAN);
459        cmd.put( (byte) 0xC1 );
460        cmd.put( (byte) blockIndex );
461        cmd.putInt(value);
462
463        transceive(cmd.array(), false);
464    }
465
466    /**
467     * Decrement a value block, storing the result in the temporary block on the tag.
468     *
469     * <p>This is an I/O operation and will block until complete. It must
470     * not be called from the main application thread. A blocked call will be canceled with
471     * {@link IOException} if {@link #close} is called from another thread.
472     *
473     * @param blockIndex index of block to decrement, starting from 0
474     * @param value non-negative to decrement by
475     * @throws TagLostException if the tag leaves the field
476     * @throws IOException if there is an I/O failure, or the operation is canceled
477     */
478    public void decrement(int blockIndex, int value) throws IOException {
479        validateBlock(blockIndex);
480        validateValueOperand(value);
481        checkConnected();
482
483        ByteBuffer cmd = ByteBuffer.allocate(6);
484        cmd.order(ByteOrder.LITTLE_ENDIAN);
485        cmd.put( (byte) 0xC0 );
486        cmd.put( (byte) blockIndex );
487        cmd.putInt(value);
488
489        transceive(cmd.array(), false);
490    }
491
492    /**
493     * Copy from the temporary block to a value block.
494     *
495     * <p>This is an I/O operation and will block until complete. It must
496     * not be called from the main application thread. A blocked call will be canceled with
497     * {@link IOException} if {@link #close} is called from another thread.
498     *
499     * @param blockIndex index of block to copy to
500     * @throws TagLostException if the tag leaves the field
501     * @throws IOException if there is an I/O failure, or the operation is canceled
502     */
503    public void transfer(int blockIndex) throws IOException {
504        validateBlock(blockIndex);
505        checkConnected();
506
507        byte[] cmd = { (byte) 0xB0, (byte) blockIndex };
508
509        transceive(cmd, false);
510    }
511
512    /**
513     * Copy from a value block to the temporary block.
514     *
515     * <p>This is an I/O operation and will block until complete. It must
516     * not be called from the main application thread. A blocked call will be canceled with
517     * {@link IOException} if {@link #close} is called from another thread.
518     *
519     * @param blockIndex index of block to copy from
520     * @throws TagLostException if the tag leaves the field
521     * @throws IOException if there is an I/O failure, or the operation is canceled
522     */
523    public void restore(int blockIndex) throws IOException {
524        validateBlock(blockIndex);
525        checkConnected();
526
527        byte[] cmd = { (byte) 0xC2, (byte) blockIndex };
528
529        transceive(cmd, false);
530    }
531
532    /**
533     * Send raw NfcA data to a tag and receive the response.
534     *
535     * <p>This is equivalent to connecting to this tag via {@link NfcA}
536     * and calling {@link NfcA#transceive}. Note that all MIFARE Classic
537     * tags are based on {@link NfcA} technology.
538     *
539     * <p>This is an I/O operation and will block until complete. It must
540     * not be called from the main application thread. A blocked call will be canceled with
541     * {@link IOException} if {@link #close} is called from another thread.
542     *
543     * @see NfcA#transceive
544     */
545    public byte[] transceive(byte[] data) throws IOException {
546        return transceive(data, true);
547    }
548
549    private static void validateSector(int sector) {
550        // Do not be too strict on upper bounds checking, since some cards
551        // have more addressable memory than they report. For example,
552        // Mifare Plus 2k cards will appear as Mifare Classic 1k cards when in
553        // Mifare Classic compatibility mode.
554        // Note that issuing a command to an out-of-bounds block is safe - the
555        // tag should report error causing IOException. This validation is a
556        // helper to guard against obvious programming mistakes.
557        if (sector < 0 || sector >= MAX_SECTOR_COUNT) {
558            throw new IndexOutOfBoundsException("sector out of bounds: " + sector);
559        }
560    }
561
562    private static void validateBlock(int block) {
563        // Just looking for obvious out of bounds...
564        if (block < 0 || block >= MAX_BLOCK_COUNT) {
565            throw new IndexOutOfBoundsException("block out of bounds: " + block);
566        }
567    }
568
569    private static void validateValueOperand(int value) {
570        if (value < 0) {
571            throw new IllegalArgumentException("value operand negative");
572        }
573    }
574}
575