]>
Commit | Line | Data |
---|---|---|
1 | //----------------------------------------------------------------------------- | |
2 | // Copyright (C) 2018 Merlok | |
3 | // Copyright (C) 2018 drHatson | |
4 | // | |
5 | // This code is licensed to you under the terms of the GNU GPL, version 2 or, | |
6 | // at your option, any later version. See the LICENSE.txt file for the text of | |
7 | // the license. | |
8 | //----------------------------------------------------------------------------- | |
9 | // iso14443-4 mifare commands | |
10 | //----------------------------------------------------------------------------- | |
11 | ||
12 | #include "mifare4.h" | |
13 | #include <ctype.h> | |
14 | #include <string.h> | |
15 | #include "cmdhf14a.h" | |
16 | #include "util.h" | |
17 | #include "ui.h" | |
18 | #include "crypto/libpcrypto.h" | |
19 | ||
20 | AccessConditions_t MFAccessConditions[] = { | |
21 | {0x00, "read AB; write AB; increment AB; decrement transfer restore AB"}, | |
22 | {0x01, "read AB; decrement transfer restore AB"}, | |
23 | {0x02, "read AB"}, | |
24 | {0x03, "read B; write B"}, | |
25 | {0x04, "read AB; writeB"}, | |
26 | {0x05, "read B"}, | |
27 | {0x06, "read AB; write B; increment B; decrement transfer restore AB"}, | |
28 | {0x07, "none"} | |
29 | }; | |
30 | ||
31 | AccessConditions_t MFAccessConditionsTrailer[] = { | |
32 | {0x00, "read A by A; read ACCESS by A; read B by A; write B by A"}, | |
33 | {0x01, "write A by A; read ACCESS by A write ACCESS by A; read B by A; write B by A"}, | |
34 | {0x02, "read ACCESS by A; read B by A"}, | |
35 | {0x03, "write A by B; read ACCESS by AB; write ACCESS by B; write B by B"}, | |
36 | {0x04, "write A by B; read ACCESS by AB; write B by B"}, | |
37 | {0x05, "read ACCESS by AB; write ACCESS by B"}, | |
38 | {0x06, "read ACCESS by AB"}, | |
39 | {0x07, "read ACCESS by AB"} | |
40 | }; | |
41 | ||
42 | char *mfGetAccessConditionsDesc(uint8_t blockn, uint8_t *data) { | |
43 | static char StaticNone[] = "none"; | |
44 | ||
45 | uint8_t data1 = ((data[1] >> 4) & 0x0f) >> blockn; | |
46 | uint8_t data2 = ((data[2]) & 0x0f) >> blockn; | |
47 | uint8_t data3 = ((data[2] >> 4) & 0x0f) >> blockn; | |
48 | ||
49 | uint8_t cond = (data1 & 0x01) << 2 | (data2 & 0x01) << 1 | (data3 & 0x01); | |
50 | ||
51 | if (blockn == 3) { | |
52 | for (int i = 0; i < ARRAYLEN(MFAccessConditionsTrailer); i++) | |
53 | if (MFAccessConditionsTrailer[i].cond == cond) { | |
54 | return MFAccessConditionsTrailer[i].description; | |
55 | } | |
56 | } else { | |
57 | for (int i = 0; i < ARRAYLEN(MFAccessConditions); i++) | |
58 | if (MFAccessConditions[i].cond == cond) { | |
59 | return MFAccessConditions[i].description; | |
60 | } | |
61 | }; | |
62 | ||
63 | return StaticNone; | |
64 | }; | |
65 | ||
66 | int CalculateEncIVCommand(mf4Session *session, uint8_t *iv, bool verbose) { | |
67 | memcpy(&iv[0], session->TI, 4); | |
68 | memcpy(&iv[4], &session->R_Ctr, 2); | |
69 | memcpy(&iv[6], &session->W_Ctr, 2); | |
70 | memcpy(&iv[8], &session->R_Ctr, 2); | |
71 | memcpy(&iv[10], &session->W_Ctr, 2); | |
72 | memcpy(&iv[12], &session->R_Ctr, 2); | |
73 | memcpy(&iv[14], &session->W_Ctr, 2); | |
74 | ||
75 | return 0; | |
76 | } | |
77 | ||
78 | int CalculateEncIVResponse(mf4Session *session, uint8_t *iv, bool verbose) { | |
79 | memcpy(&iv[0], &session->R_Ctr, 2); | |
80 | memcpy(&iv[2], &session->W_Ctr, 2); | |
81 | memcpy(&iv[4], &session->R_Ctr, 2); | |
82 | memcpy(&iv[6], &session->W_Ctr, 2); | |
83 | memcpy(&iv[8], &session->R_Ctr, 2); | |
84 | memcpy(&iv[10], &session->W_Ctr, 2); | |
85 | memcpy(&iv[12], session->TI, 4); | |
86 | ||
87 | return 0; | |
88 | } | |
89 | ||
90 | ||
91 | int CalculateMAC(mf4Session *session, MACType_t mtype, uint8_t blockNum, uint8_t blockCount, uint8_t *data, int datalen, uint8_t *mac, bool verbose) { | |
92 | if (!session || !session->Authenticated || !mac || !data || !datalen || datalen < 1) | |
93 | return 1; | |
94 | ||
95 | memset(mac, 0x00, 8); | |
96 | ||
97 | uint16_t ctr = session->R_Ctr; | |
98 | switch(mtype) { | |
99 | case mtypWriteCmd: | |
100 | case mtypWriteResp: | |
101 | ctr = session->W_Ctr; | |
102 | break; | |
103 | case mtypReadCmd: | |
104 | case mtypReadResp: | |
105 | break; | |
106 | } | |
107 | ||
108 | uint8_t macdata[2049] = {data[0], (ctr & 0xFF), (ctr >> 8), 0}; | |
109 | int macdatalen = datalen; | |
110 | memcpy(&macdata[3], session->TI, 4); | |
111 | ||
112 | switch(mtype) { | |
113 | case mtypReadCmd: | |
114 | memcpy(&macdata[7], &data[1], datalen - 1); | |
115 | macdatalen = datalen + 6; | |
116 | break; | |
117 | case mtypReadResp: | |
118 | macdata[7] = blockNum; | |
119 | macdata[8] = 0; | |
120 | macdata[9] = blockCount; | |
121 | memcpy(&macdata[10], &data[1], datalen - 1); | |
122 | macdatalen = datalen + 9; | |
123 | break; | |
124 | case mtypWriteCmd: | |
125 | memcpy(&macdata[7], &data[1], datalen - 1); | |
126 | macdatalen = datalen + 6; | |
127 | break; | |
128 | case mtypWriteResp: | |
129 | macdatalen = 1 + 6; | |
130 | break; | |
131 | } | |
132 | ||
133 | if (verbose) | |
134 | PrintAndLog("MAC data[%d]: %s", macdatalen, sprint_hex(macdata, macdatalen)); | |
135 | ||
136 | return aes_cmac8(NULL, session->Kmac, macdata, mac, macdatalen); | |
137 | } | |
138 | ||
139 | int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool verbose) { | |
140 | uint8_t data[257] = {0}; | |
141 | int datalen = 0; | |
142 | ||
143 | uint8_t RndA[17] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00}; | |
144 | uint8_t RndB[17] = {0}; | |
145 | ||
146 | if (session) | |
147 | session->Authenticated = false; | |
148 | ||
149 | uint8_t cmd1[] = {0x70, keyn[1], keyn[0], 0x00}; | |
150 | int res = ExchangeRAW14a(cmd1, sizeof(cmd1), activateField, true, data, sizeof(data), &datalen); | |
151 | if (res) { | |
152 | PrintAndLog("ERROR exchande raw error: %d", res); | |
153 | DropField(); | |
154 | return 2; | |
155 | } | |
156 | ||
157 | if (verbose) | |
158 | PrintAndLog("<phase1: %s", sprint_hex(data, datalen)); | |
159 | ||
160 | if (datalen < 1) { | |
161 | PrintAndLog("ERROR: card response length: %d", datalen); | |
162 | DropField(); | |
163 | return 3; | |
164 | } | |
165 | ||
166 | if (data[0] != 0x90) { | |
167 | PrintAndLog("ERROR: card response error: %02x", data[2]); | |
168 | DropField(); | |
169 | return 3; | |
170 | } | |
171 | ||
172 | if (datalen != 19) { // code 1b + 16b + crc 2b | |
173 | PrintAndLog("ERROR: card response must be 19 bytes long instead of: %d", datalen); | |
174 | DropField(); | |
175 | return 3; | |
176 | } | |
177 | ||
178 | aes_decode(NULL, key, &data[1], RndB, 16); | |
179 | RndB[16] = RndB[0]; | |
180 | if (verbose) | |
181 | PrintAndLog("RndB: %s", sprint_hex(RndB, 16)); | |
182 | ||
183 | uint8_t cmd2[33] = {0}; | |
184 | cmd2[0] = 0x72; | |
185 | ||
186 | uint8_t raw[32] = {0}; | |
187 | memmove(raw, RndA, 16); | |
188 | memmove(&raw[16], &RndB[1], 16); | |
189 | ||
190 | aes_encode(NULL, key, raw, &cmd2[1], 32); | |
191 | if (verbose) | |
192 | PrintAndLog(">phase2: %s", sprint_hex(cmd2, 33)); | |
193 | ||
194 | res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, true, data, sizeof(data), &datalen); | |
195 | if (res) { | |
196 | PrintAndLog("ERROR exchande raw error: %d", res); | |
197 | DropField(); | |
198 | return 4; | |
199 | } | |
200 | ||
201 | if (verbose) | |
202 | PrintAndLog("<phase2: %s", sprint_hex(data, datalen)); | |
203 | ||
204 | aes_decode(NULL, key, &data[1], raw, 32); | |
205 | ||
206 | if (verbose) { | |
207 | PrintAndLog("res: %s", sprint_hex(raw, 32)); | |
208 | PrintAndLog("RndA`: %s", sprint_hex(&raw[4], 16)); | |
209 | } | |
210 | ||
211 | if (memcmp(&raw[4], &RndA[1], 16)) { | |
212 | PrintAndLog("\nERROR: Authentication FAILED. rnd not equal"); | |
213 | if (verbose) { | |
214 | PrintAndLog("RndA reader: %s", sprint_hex(&RndA[1], 16)); | |
215 | PrintAndLog("RndA card: %s", sprint_hex(&raw[4], 16)); | |
216 | } | |
217 | DropField(); | |
218 | return 5; | |
219 | } | |
220 | ||
221 | if (verbose) { | |
222 | PrintAndLog(" TI: %s", sprint_hex(raw, 4)); | |
223 | PrintAndLog("pic: %s", sprint_hex(&raw[20], 6)); | |
224 | PrintAndLog("pcd: %s", sprint_hex(&raw[26], 6)); | |
225 | } | |
226 | ||
227 | uint8_t kenc[16] = {0}; | |
228 | memcpy(&kenc[0], &RndA[11], 5); | |
229 | memcpy(&kenc[5], &RndB[11], 5); | |
230 | for(int i = 0; i < 5; i++) | |
231 | kenc[10 + i] = RndA[4 + i] ^ RndB[4 + i]; | |
232 | kenc[15] = 0x11; | |
233 | ||
234 | aes_encode(NULL, key, kenc, kenc, 16); | |
235 | if (verbose) { | |
236 | PrintAndLog("kenc: %s", sprint_hex(kenc, 16)); | |
237 | } | |
238 | ||
239 | uint8_t kmac[16] = {0}; | |
240 | memcpy(&kmac[0], &RndA[7], 5); | |
241 | memcpy(&kmac[5], &RndB[7], 5); | |
242 | for(int i = 0; i < 5; i++) | |
243 | kmac[10 + i] = RndA[0 + i] ^ RndB[0 + i]; | |
244 | kmac[15] = 0x22; | |
245 | ||
246 | aes_encode(NULL, key, kmac, kmac, 16); | |
247 | if (verbose) { | |
248 | PrintAndLog("kmac: %s", sprint_hex(kmac, 16)); | |
249 | } | |
250 | ||
251 | if (!leaveSignalON) | |
252 | DropField(); | |
253 | ||
254 | if (verbose) | |
255 | PrintAndLog(""); | |
256 | ||
257 | if (session) { | |
258 | session->Authenticated = true; | |
259 | session->R_Ctr = 0; | |
260 | session->W_Ctr = 0; | |
261 | session->KeyNum = keyn[1] + (keyn[0] << 8); | |
262 | memmove(session->RndA, RndA, 16); | |
263 | memmove(session->RndB, RndB, 16); | |
264 | memmove(session->Key, key, 16); | |
265 | memmove(session->TI, raw, 4); | |
266 | memmove(session->PICCap2, &raw[20], 6); | |
267 | memmove(session->PCDCap2, &raw[26], 6); | |
268 | memmove(session->Kenc, kenc, 16); | |
269 | memmove(session->Kmac, kmac, 16); | |
270 | } | |
271 | ||
272 | PrintAndLog("Authentication OK"); | |
273 | ||
274 | return 0; | |
275 | } | |
276 | ||
277 | // Mifare Memory Structure: up to 32 Sectors with 4 blocks each (1k and 2k cards), | |
278 | // plus evtl. 8 sectors with 16 blocks each (4k cards) | |
279 | uint8_t mfNumBlocksPerSector(uint8_t sectorNo) { | |
280 | if (sectorNo < 32) | |
281 | return 4; | |
282 | else | |
283 | return 16; | |
284 | } | |
285 | ||
286 | uint8_t mfFirstBlockOfSector(uint8_t sectorNo) { | |
287 | if (sectorNo < 32) | |
288 | return sectorNo * 4; | |
289 | else | |
290 | return 32 * 4 + (sectorNo - 32) * 16; | |
291 | } | |
292 | ||
293 | uint8_t mfSectorTrailer(uint8_t blockNo) { | |
294 | if (blockNo < 32*4) { | |
295 | return (blockNo | 0x03); | |
296 | } else { | |
297 | return (blockNo | 0x0f); | |
298 | } | |
299 | } | |
300 | ||
301 | bool mfIsSectorTrailer(uint8_t blockNo) { | |
302 | return (blockNo == mfSectorTrailer(blockNo)); | |
303 | } | |
304 | ||
305 | uint8_t mfSectorNum(uint8_t blockNo) { | |
306 | if (blockNo < 32 * 4) | |
307 | return blockNo / 4; | |
308 | else | |
309 | return 32 + (blockNo - 32 * 4) / 16; | |
310 | ||
311 | } |