Grundaufbau der App
[DHBWCampusApp.git] / app / src / main / java / com / codebutler / farebot / card / desfire / DesfireProtocol.java
1 /*
2  * DesfireProtocol.java
3  *
4  * Copyright (C) 2011 Eric Butler
5  *
6  * Authors:
7  * Eric Butler <eric@codebutler.com>
8  *
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  */
22
23 package com.codebutler.farebot.card.desfire;
24
25 import android.nfc.tech.IsoDep;
26 import com.codebutler.farebot.Utils;
27
28 import java.io.ByteArrayOutputStream;
29 import java.io.IOException;
30
31 import org.apache.commons.lang3.ArrayUtils;
32
33 public class DesfireProtocol {
34     /* Commands */
35     static final byte GET_MANUFACTURING_DATA    = (byte) 0x60;
36     static final byte GET_APPLICATION_DIRECTORY = (byte) 0x6A;
37     static final byte GET_ADDITIONAL_FRAME      = (byte) 0xAF;
38     static final byte SELECT_APPLICATION        = (byte) 0x5A;
39     static final byte READ_DATA                 = (byte) 0xBD;
40     static final byte READ_RECORD               = (byte) 0xBB;
41     static final byte READ_VALUE                = (byte) 0x6C;
42     static final byte GET_FILES                 = (byte) 0x6F;
43     static final byte GET_FILE_SETTINGS         = (byte) 0xF5;
44
45     /* Status codes */
46     static final byte OPERATION_OK      = (byte) 0x00;
47     static final byte PERMISSION_DENIED = (byte) 0x9D;
48     static final byte ADDITIONAL_FRAME  = (byte) 0xAF;
49
50     private IsoDep mTagTech;
51
52     public DesfireProtocol(IsoDep tagTech) {
53         mTagTech = tagTech;
54     }
55
56     public DesfireManufacturingData getManufacturingData() throws DesfireException {
57         byte[] respBuffer = sendRequest(GET_MANUFACTURING_DATA);
58         
59         if (respBuffer.length != 28)
60             throw new DesfireException("Invalid response");
61
62         return new DesfireManufacturingData(respBuffer);
63     }
64
65     public int[] getAppList() throws DesfireException {
66         byte[] appDirBuf = sendRequest(GET_APPLICATION_DIRECTORY);
67
68         int[] appIds = new int[appDirBuf.length / 3];
69
70         for (int app = 0; app < appDirBuf.length; app += 3) {
71             byte[] appId = new byte[3];
72             System.arraycopy(appDirBuf, app, appId, 0, 3);
73
74             appIds[app / 3] = Utils.byteArrayToInt(appId);
75         }
76
77         return appIds;
78     }
79
80     public void selectApp (int appId) throws DesfireException {
81         byte[] appIdBuff = new byte[3];
82         appIdBuff[0] = (byte) ((appId & 0xFF0000) >> 16);
83         appIdBuff[1] = (byte) ((appId & 0xFF00) >> 8);
84         appIdBuff[2] = (byte) (appId & 0xFF);
85
86         sendRequest(SELECT_APPLICATION, appIdBuff);
87     }
88
89     public int[] getFileList() throws DesfireException {
90         byte[] buf = sendRequest(GET_FILES);
91         int[] fileIds = new int[buf.length];
92         for (int x = 0; x < buf.length; x++) {
93             fileIds[x] = (int)buf[x];
94         }
95         return fileIds;
96     }
97
98     public DesfireFileSettings getFileSettings (int fileNo) throws DesfireException {
99                 byte[] data = new byte[0];
100                 data = sendRequest(GET_FILE_SETTINGS, new byte[] { (byte) fileNo });
101                 return DesfireFileSettings.Create(data);
102     }
103
104     public byte[] readFile (int fileNo) throws DesfireException {
105         return sendRequest(READ_DATA, new byte[] {
106             (byte) fileNo,
107             (byte) 0x0, (byte) 0x0, (byte) 0x0,
108             (byte) 0x0, (byte) 0x0, (byte) 0x0
109         });
110     }
111
112     public byte[] readRecord (int fileNum) throws DesfireException {
113         return sendRequest(READ_RECORD, new byte[]{
114                 (byte) fileNum,
115                 (byte) 0x0, (byte) 0x0, (byte) 0x0,
116                 (byte) 0x0, (byte) 0x0, (byte) 0x0
117         });
118     }
119
120         public int readValue(int fileNum) throws DesfireException  {
121         byte[] buf = sendRequest(READ_VALUE, new byte[]{
122                 (byte) fileNum
123         });
124         ArrayUtils.reverse(buf);
125         return Utils.byteArrayToInt(buf);
126         }
127     
128
129     private byte[] sendRequest (byte command) throws DesfireException {
130         return sendRequest(command, null);
131     }
132
133     private byte[] sendRequest (byte command, byte[] parameters) throws DesfireException {
134         ByteArrayOutputStream output = new ByteArrayOutputStream();
135
136                 byte[] recvBuffer = new byte[0];
137                 try {
138                         recvBuffer = mTagTech.transceive(wrapMessage(command, parameters));
139                 } catch (IOException e) {
140                         throw new DesfireException(e);
141                 }
142
143                 while (true) {
144             if (recvBuffer[recvBuffer.length - 2] != (byte) 0x91)
145                 throw new DesfireException("Invalid response");
146
147             output.write(recvBuffer, 0, recvBuffer.length - 2);
148
149             byte status = recvBuffer[recvBuffer.length - 1];
150             if (status == OPERATION_OK) {
151                 break;
152             } else if (status == ADDITIONAL_FRAME) {
153                                 try {
154                                         recvBuffer = mTagTech.transceive(wrapMessage(GET_ADDITIONAL_FRAME, null));
155                                 } catch (IOException e) {
156                                         throw new DesfireException(e);
157                                 }
158                         } else if (status == PERMISSION_DENIED) {
159                 throw new DesfireException("Permission denied");
160             } else {
161                 throw new DesfireException("Unknown status code: " + Integer.toHexString(status & 0xFF));
162             }
163         }
164         
165         return output.toByteArray();
166     }
167
168     private byte[] wrapMessage (byte command, byte[] parameters) throws DesfireException {
169         ByteArrayOutputStream stream = new ByteArrayOutputStream();
170
171         stream.write((byte) 0x90);
172         stream.write(command);
173         stream.write((byte) 0x00);
174         stream.write((byte) 0x00);
175         if (parameters != null) {
176             stream.write((byte) parameters.length);
177                         try {
178                                 stream.write(parameters);
179                         } catch (IOException e) {
180                                 throw new DesfireException(e);
181                         }
182                 }
183         stream.write((byte) 0x00);
184
185         return stream.toByteArray();
186     }
187
188 }