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