MifareClassic.java revision 4e21e1d21a877cce4db5ec8c5786604cc10f2d7e
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;
24
25/**
26 * Technology class representing MIFARE Classic tags (also known as MIFARE Standard).
27 *
28 * <p>Support for this technology type is optional. If the NFC stack doesn't support this technology
29 * MIFARE Classic tags will still be scanned, but will only show the NfcA technology.
30 *
31 * <p>MIFARE Classic tags have sectors that each contain blocks. The block size is constant at
32 * 16 bytes, but the number of sectors and the sector size varies by product. MIFARE has encryption
33 * built in and each sector has two keys associated with it, as well as ACLs to determine what
34 * level acess each key grants. Before operating on a sector you must call either
35 * {@link #authenticateSector(int, byte[], boolean)} or
36 * {@link #authenticateBlock(int, byte[], boolean)} to gain authorize your request.
37 */
38public final class MifareClassic extends BasicTagTechnology {
39    /**
40     * The well-known default MIFARE read key. All keys are set to this at the factory.
41     * Using this key will effectively make the payload in the sector public.
42     */
43    public static final byte[] KEY_DEFAULT =
44            {(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF,(byte)0xFF};
45    /**
46     * The well-known, default MIFARE Application Directory read key.
47     */
48    public static final byte[] KEY_MIFARE_APPLICATION_DIRECTORY =
49            {(byte)0xA0,(byte)0xA1,(byte)0xA2,(byte)0xA3,(byte)0xA4,(byte)0xA5};
50    /**
51     * The well-known, default read key for NDEF data on a MIFARE Classic
52     */
53    public static final byte[] KEY_NFC_FORUM =
54            {(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7,(byte)0xD3,(byte)0xF7};
55
56    /** A MIFARE Classic tag */
57    public static final int TYPE_CLASSIC = 0;
58    /** A MIFARE Plus tag */
59    public static final int TYPE_PLUS = 1;
60    /** A MIFARE Pro tag */
61    public static final int TYPE_PRO = 2;
62    /** The tag type is unknown */
63    public static final int TYPE_UNKNOWN = 5;
64
65    /** The tag contains 16 sectors, each holding 4 blocks. */
66    public static final int SIZE_1K = 1024;
67    /** The tag contains 32 sectors, each holding 4 blocks. */
68    public static final int SIZE_2K = 2048;
69    /**
70     * The tag contains 40 sectors. The first 32 sectors contain 4 blocks and the last 8 sectors
71     * contain 16 blocks.
72     */
73    public static final int SIZE_4K = 4096;
74    /** The tag contains 5 sectors, each holding 4 blocks. */
75    public static final int SIZE_MINI = 320;
76    /** The capacity is unknown */
77    public static final int SIZE_UNKNOWN = 0;
78
79    private boolean mIsEmulated;
80    private int mType;
81    private int mSize;
82
83    /**
84     * Returns an instance of this tech for the given tag. If the tag doesn't support
85     * this tech type null is returned.
86     *
87     * @param tag The tag to get the tech from
88     */
89    public static MifareClassic get(Tag tag) {
90        if (!tag.hasTech(TagTechnology.MIFARE_CLASSIC)) return null;
91        try {
92            return new MifareClassic(tag);
93        } catch (RemoteException e) {
94            return null;
95        }
96    }
97
98    /** @hide */
99    public MifareClassic(Tag tag) throws RemoteException {
100        super(tag, TagTechnology.MIFARE_CLASSIC);
101
102        // Check if this could actually be a MIFARE Classic
103        NfcA a = NfcA.get(tag);
104
105        mIsEmulated = false;
106        mType = TYPE_UNKNOWN;
107        mSize = SIZE_UNKNOWN;
108
109        switch (a.getSak()) {
110            case 0x08:
111                // Type == classic
112                // Size = 1K
113                mType = TYPE_CLASSIC;
114                mSize = SIZE_1K;
115                break;
116            case 0x09:
117                // Type == classic mini
118                // Size == ?
119                mType = TYPE_CLASSIC;
120                mSize = SIZE_MINI;
121                break;
122            case 0x10:
123                // Type == MF+
124                // Size == 2K
125                // SecLevel = SL2
126                mType = TYPE_PLUS;
127                mSize = SIZE_2K;
128                break;
129            case 0x11:
130                // Type == MF+
131                // Size == 4K
132                // Seclevel = SL2
133                mType = TYPE_PLUS;
134                mSize = SIZE_4K;
135                break;
136            case 0x18:
137                // Type == classic
138                // Size == 4k
139                mType = TYPE_CLASSIC;
140                mSize = SIZE_4K;
141                break;
142            case 0x20:
143                // TODO this really should be a short, not byte
144                if (a.getAtqa()[0] == 0x03) {
145                    // Type == DESFIRE
146                    break;
147                } else {
148                    // Type == MF+
149                    // SL = SL3
150                    mType = TYPE_PLUS;
151                    mSize = SIZE_UNKNOWN;
152                }
153                break;
154            case 0x28:
155                // Type == MF Classic
156                // Size == 1K
157                // Emulated == true
158                mType = TYPE_CLASSIC;
159                mSize = SIZE_1K;
160                mIsEmulated = true;
161                break;
162            case 0x38:
163                // Type == MF Classic
164                // Size == 4K
165                // Emulated == true
166                mType = TYPE_CLASSIC;
167                mSize = SIZE_4K;
168                mIsEmulated = true;
169                break;
170            case 0x88:
171                // Type == MF Classic
172                // Size == 1K
173                // NXP-tag: false
174                mType = TYPE_CLASSIC;
175                mSize = SIZE_1K;
176                break;
177            case 0x98:
178            case 0xB8:
179                // Type == MF Pro
180                // Size == 4K
181                mType = TYPE_PRO;
182                mSize = SIZE_4K;
183                break;
184        }
185    }
186
187    /** Returns the size of the tag, determined at discovery time */
188    public int getSize() {
189        return mSize;
190    }
191
192    /** Returns the size of the tag, determined at discovery time */
193    public int getType() {
194        return mType;
195    }
196
197    /** Returns true if the tag is emulated, determined at discovery time */
198    public boolean isEmulated() {
199        return mIsEmulated;
200    }
201
202    /** Returns the number of sectors on this tag, determined at discovery time */
203    public int getSectorCount() {
204        switch (mSize) {
205            case SIZE_1K: {
206                return 16;
207            }
208            case SIZE_2K: {
209                return 32;
210            }
211            case SIZE_4K: {
212                return 40;
213            }
214            case SIZE_MINI: {
215                return 5;
216            }
217            default: {
218                return 0;
219            }
220        }
221    }
222
223    /** Returns the sector size, determined at discovery time */
224    public int getSectorSize(int sector) {
225        return getBlockCount(sector) * 16;
226    }
227
228    /** Returns the total block count, determined at discovery time */
229    public int getTotalBlockCount() {
230        int totalBlocks = 0;
231        for (int sec = 0; sec < getSectorCount(); sec++) {
232            totalBlocks += getSectorSize(sec);
233        }
234
235        return totalBlocks;
236    }
237
238    /** Returns the block count for the given sector, determined at discovery time */
239    public int getBlockCount(int sector) {
240        if (sector >= getSectorCount()) {
241            throw new IllegalArgumentException("this card only has " + getSectorCount() +
242                    " sectors");
243        }
244
245        if (sector <= 32) {
246            return 4;
247        } else {
248            return 16;
249        }
250    }
251
252    private byte firstBlockInSector(int sector) {
253        if (sector < 32) {
254            return (byte) ((sector * 4) & 0xff);
255        } else {
256            return (byte) ((32 * 4 + ((sector - 32) * 16)) & 0xff);
257        }
258    }
259
260    // Methods that require connect()
261    /**
262     * Authenticate the entire sector that the given block resides in.
263     * <p>This requires a that the tag be connected.
264     */
265    public boolean authenticateBlock(int block, byte[] key, boolean keyA) throws IOException {
266        checkConnected();
267
268        byte[] cmd = new byte[12];
269
270        // First byte is the command
271        if (keyA) {
272            cmd[0] = 0x60; // phHal_eMifareAuthentA
273        } else {
274            cmd[0] = 0x61; // phHal_eMifareAuthentB
275        }
276
277        // Second byte is block address
278        cmd[1] = (byte) block;
279
280        // Next 4 bytes are last 4 bytes of UID
281        byte[] uid = getTag().getId();
282        System.arraycopy(uid, uid.length - 4, cmd, 2, 4);
283
284        // Next 6 bytes are key
285        System.arraycopy(key, 0, cmd, 6, 6);
286
287        try {
288            if ((transceive(cmd, false) != null)) {
289                return true;
290            }
291        } catch (TagLostException e) {
292            throw e;
293        } catch (IOException e) {
294            // No need to deal with, will return false anyway
295        }
296        return false;
297    }
298
299    /**
300     * Authenticate for a given sector.
301     * <p>This requires a that the tag be connected.
302     */
303    public boolean authenticateSector(int sector, byte[] key, boolean keyA) throws IOException {
304        checkConnected();
305
306        byte addr = (byte) ((firstBlockInSector(sector)) & 0xff);
307
308        // Note that authenticating a block of a sector, will authenticate
309        // the entire sector.
310        return authenticateBlock(addr, key, keyA);
311    }
312
313    /**
314     * Sector indexing starts at 0.
315     * Block indexing starts at 0, and resets in each sector.
316     * <p>This requires a that the tag be connected.
317     * @throws IOException
318     */
319    public byte[] readBlock(int sector, int block) throws IOException {
320        checkConnected();
321
322        byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff);
323        return readBlock(addr);
324    }
325
326    /**
327     * Reads absolute block index.
328     * <p>This requires a that the tag be connected.
329     * @throws IOException
330     */
331    public byte[] readBlock(int block) throws IOException {
332        checkConnected();
333
334        byte addr = (byte) block;
335        byte[] blockread_cmd = { 0x30, addr };
336
337        return transceive(blockread_cmd, false);
338    }
339
340    /**
341     * Writes absolute block index.
342     * <p>This requires a that the tag be connected.
343     * @throws IOException
344     */
345    public void writeBlock(int block, byte[] data) throws IOException {
346        checkConnected();
347
348        byte addr = (byte) block;
349        byte[] blockwrite_cmd = new byte[data.length + 2];
350        blockwrite_cmd[0] = (byte) 0xA0; // MF write command
351        blockwrite_cmd[1] = addr;
352        System.arraycopy(data, 0, blockwrite_cmd, 2, data.length);
353
354        transceive(blockwrite_cmd, false);
355    }
356
357    /**
358     * Writes relative block in sector.
359     * <p>This requires a that the tag be connected.
360     * @throws IOException
361     */
362    public void writeBlock(int sector, int block, byte[] data) throws IOException {
363        checkConnected();
364
365        byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff);
366
367        writeBlock(addr, data);
368    }
369
370    public void increment(int block) throws IOException {
371        checkConnected();
372
373        byte[] incr_cmd = { (byte) 0xC1, (byte) block };
374
375        transceive(incr_cmd, false);
376    }
377
378    public void decrement(int block) throws IOException {
379        checkConnected();
380
381        byte[] decr_cmd = { (byte) 0xC0, (byte) block };
382
383        transceive(decr_cmd, false);
384    }
385
386    public void transfer(int block) throws IOException {
387        checkConnected();
388
389        byte[] trans_cmd = { (byte) 0xB0, (byte) block };
390
391        transceive(trans_cmd, false);
392    }
393
394    public void restore(int block) throws IOException {
395        checkConnected();
396
397        byte[] rest_cmd = { (byte) 0xC2, (byte) block };
398
399        transceive(rest_cmd, false);
400    }
401
402    /**
403     * Send raw NfcA data to a tag and receive the response.
404     * <p>
405     * This method will block until the response is received. It can be canceled
406     * with {@link #close}.
407     * <p>Requires {@link android.Manifest.permission#NFC} permission.
408     * <p>This requires a that the tag be connected.
409     *
410     * @param data bytes to send
411     * @return bytes received in response
412     * @throws IOException if the target is lost or connection closed
413     */
414    public byte[] transceive(byte[] data) throws IOException {
415        return transceive(data, true);
416    }
417}
418