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