]>
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 | int CalculateEncIVCommand(mf4Session *session, uint8_t *iv, bool verbose) { | |
21 | memcpy(&iv[0], session->TI, 4); | |
22 | memcpy(&iv[4], &session->R_Ctr, 2); | |
23 | memcpy(&iv[6], &session->W_Ctr, 2); | |
24 | memcpy(&iv[8], &session->R_Ctr, 2); | |
25 | memcpy(&iv[10], &session->W_Ctr, 2); | |
26 | memcpy(&iv[12], &session->R_Ctr, 2); | |
27 | memcpy(&iv[14], &session->W_Ctr, 2); | |
28 | ||
29 | return 0; | |
30 | } | |
31 | ||
32 | int CalculateEncIVResponse(mf4Session *session, uint8_t *iv, bool verbose) { | |
33 | memcpy(&iv[0], &session->R_Ctr, 2); | |
34 | memcpy(&iv[2], &session->W_Ctr, 2); | |
35 | memcpy(&iv[4], &session->R_Ctr, 2); | |
36 | memcpy(&iv[6], &session->W_Ctr, 2); | |
37 | memcpy(&iv[8], &session->R_Ctr, 2); | |
38 | memcpy(&iv[10], &session->W_Ctr, 2); | |
39 | memcpy(&iv[12], session->TI, 4); | |
40 | ||
41 | return 0; | |
42 | } | |
43 | ||
44 | ||
45 | int CalculateMAC(mf4Session *session, MACType_t mtype, uint8_t blockNum, uint8_t blockCount, uint8_t *data, int datalen, uint8_t *mac, bool verbose) { | |
46 | if (!session || !session->Authenticated || !mac || !data || !datalen || datalen < 1) | |
47 | return 1; | |
48 | ||
49 | memset(mac, 0x00, 8); | |
50 | ||
51 | uint16_t ctr = session->R_Ctr; | |
52 | switch(mtype) { | |
53 | case mtypWriteCmd: | |
54 | case mtypWriteResp: | |
55 | ctr = session->W_Ctr; | |
56 | break; | |
57 | case mtypReadCmd: | |
58 | case mtypReadResp: | |
59 | break; | |
60 | } | |
61 | ||
62 | uint8_t macdata[2049] = {data[0], (ctr & 0xFF), (ctr >> 8), 0}; | |
63 | int macdatalen = datalen; | |
64 | memcpy(&macdata[3], session->TI, 4); | |
65 | ||
66 | switch(mtype) { | |
67 | case mtypReadCmd: | |
68 | memcpy(&macdata[7], &data[1], datalen - 1); | |
69 | macdatalen = datalen + 6; | |
70 | break; | |
71 | case mtypReadResp: | |
72 | macdata[7] = blockNum; | |
73 | macdata[8] = 0; | |
74 | macdata[9] = blockCount; | |
75 | memcpy(&macdata[10], &data[1], datalen - 1); | |
76 | macdatalen = datalen + 9; | |
77 | break; | |
78 | case mtypWriteCmd: | |
79 | memcpy(&macdata[7], &data[1], datalen - 1); | |
80 | macdatalen = datalen + 6; | |
81 | break; | |
82 | case mtypWriteResp: | |
83 | macdatalen = 1 + 6; | |
84 | break; | |
85 | } | |
86 | ||
87 | if (verbose) | |
88 | PrintAndLog("MAC data[%d]: %s", macdatalen, sprint_hex(macdata, macdatalen)); | |
89 | ||
90 | return aes_cmac8(NULL, session->Kmac, macdata, mac, macdatalen); | |
91 | } | |
92 | ||
93 | int MifareAuth4(mf4Session *session, uint8_t *keyn, uint8_t *key, bool activateField, bool leaveSignalON, bool verbose) { | |
94 | uint8_t data[257] = {0}; | |
95 | int datalen = 0; | |
96 | ||
97 | uint8_t RndA[17] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00}; | |
98 | uint8_t RndB[17] = {0}; | |
99 | ||
100 | if (session) | |
101 | session->Authenticated = false; | |
102 | ||
103 | uint8_t cmd1[] = {0x70, keyn[1], keyn[0], 0x00}; | |
104 | int res = ExchangeRAW14a(cmd1, sizeof(cmd1), activateField, true, data, sizeof(data), &datalen); | |
105 | if (res) { | |
106 | PrintAndLog("ERROR exchande raw error: %d", res); | |
107 | DropField(); | |
108 | return 2; | |
109 | } | |
110 | ||
111 | if (verbose) | |
112 | PrintAndLog("<phase1: %s", sprint_hex(data, datalen)); | |
113 | ||
114 | if (datalen < 1) { | |
115 | PrintAndLog("ERROR: card response length: %d", datalen); | |
116 | DropField(); | |
117 | return 3; | |
118 | } | |
119 | ||
120 | if (data[0] != 0x90) { | |
121 | PrintAndLog("ERROR: card response error: %02x", data[2]); | |
122 | DropField(); | |
123 | return 3; | |
124 | } | |
125 | ||
126 | if (datalen != 19) { // code 1b + 16b + crc 2b | |
127 | PrintAndLog("ERROR: card response must be 19 bytes long instead of: %d", datalen); | |
128 | DropField(); | |
129 | return 3; | |
130 | } | |
131 | ||
132 | aes_decode(NULL, key, &data[1], RndB, 16); | |
133 | RndB[16] = RndB[0]; | |
134 | if (verbose) | |
135 | PrintAndLog("RndB: %s", sprint_hex(RndB, 16)); | |
136 | ||
137 | uint8_t cmd2[33] = {0}; | |
138 | cmd2[0] = 0x72; | |
139 | ||
140 | uint8_t raw[32] = {0}; | |
141 | memmove(raw, RndA, 16); | |
142 | memmove(&raw[16], &RndB[1], 16); | |
143 | ||
144 | aes_encode(NULL, key, raw, &cmd2[1], 32); | |
145 | if (verbose) | |
146 | PrintAndLog(">phase2: %s", sprint_hex(cmd2, 33)); | |
147 | ||
148 | res = ExchangeRAW14a(cmd2, sizeof(cmd2), false, true, data, sizeof(data), &datalen); | |
149 | if (res) { | |
150 | PrintAndLog("ERROR exchande raw error: %d", res); | |
151 | DropField(); | |
152 | return 4; | |
153 | } | |
154 | ||
155 | if (verbose) | |
156 | PrintAndLog("<phase2: %s", sprint_hex(data, datalen)); | |
157 | ||
158 | aes_decode(NULL, key, &data[1], raw, 32); | |
159 | ||
160 | if (verbose) { | |
161 | PrintAndLog("res: %s", sprint_hex(raw, 32)); | |
162 | PrintAndLog("RndA`: %s", sprint_hex(&raw[4], 16)); | |
163 | } | |
164 | ||
165 | if (memcmp(&raw[4], &RndA[1], 16)) { | |
166 | PrintAndLog("\nERROR: Authentication FAILED. rnd not equal"); | |
167 | if (verbose) { | |
168 | PrintAndLog("RndA reader: %s", sprint_hex(&RndA[1], 16)); | |
169 | PrintAndLog("RndA card: %s", sprint_hex(&raw[4], 16)); | |
170 | } | |
171 | DropField(); | |
172 | return 5; | |
173 | } | |
174 | ||
175 | if (verbose) { | |
176 | PrintAndLog(" TI: %s", sprint_hex(raw, 4)); | |
177 | PrintAndLog("pic: %s", sprint_hex(&raw[20], 6)); | |
178 | PrintAndLog("pcd: %s", sprint_hex(&raw[26], 6)); | |
179 | } | |
180 | ||
181 | uint8_t kenc[16] = {0}; | |
182 | memcpy(&kenc[0], &RndA[11], 5); | |
183 | memcpy(&kenc[5], &RndB[11], 5); | |
184 | for(int i = 0; i < 5; i++) | |
185 | kenc[10 + i] = RndA[4 + i] ^ RndB[4 + i]; | |
186 | kenc[15] = 0x11; | |
187 | ||
188 | aes_encode(NULL, key, kenc, kenc, 16); | |
189 | if (verbose) { | |
190 | PrintAndLog("kenc: %s", sprint_hex(kenc, 16)); | |
191 | } | |
192 | ||
193 | uint8_t kmac[16] = {0}; | |
194 | memcpy(&kmac[0], &RndA[7], 5); | |
195 | memcpy(&kmac[5], &RndB[7], 5); | |
196 | for(int i = 0; i < 5; i++) | |
197 | kmac[10 + i] = RndA[0 + i] ^ RndB[0 + i]; | |
198 | kmac[15] = 0x22; | |
199 | ||
200 | aes_encode(NULL, key, kmac, kmac, 16); | |
201 | if (verbose) { | |
202 | PrintAndLog("kmac: %s", sprint_hex(kmac, 16)); | |
203 | } | |
204 | ||
205 | if (!leaveSignalON) | |
206 | DropField(); | |
207 | ||
208 | if (verbose) | |
209 | PrintAndLog(""); | |
210 | ||
211 | if (session) { | |
212 | session->Authenticated = true; | |
213 | session->R_Ctr = 0; | |
214 | session->W_Ctr = 0; | |
215 | session->KeyNum = keyn[1] + (keyn[0] << 8); | |
216 | memmove(session->RndA, RndA, 16); | |
217 | memmove(session->RndB, RndB, 16); | |
218 | memmove(session->Key, key, 16); | |
219 | memmove(session->TI, raw, 4); | |
220 | memmove(session->PICCap2, &raw[20], 6); | |
221 | memmove(session->PCDCap2, &raw[26], 6); | |
222 | memmove(session->Kenc, kenc, 16); | |
223 | memmove(session->Kmac, kmac, 16); | |
224 | } | |
225 | ||
226 | PrintAndLog("Authentication OK"); | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
231 | // Mifare Memory Structure: up to 32 Sectors with 4 blocks each (1k and 2k cards), | |
232 | // plus evtl. 8 sectors with 16 blocks each (4k cards) | |
233 | uint8_t mfNumBlocksPerSector(uint8_t sectorNo) { | |
234 | if (sectorNo < 32) | |
235 | return 4; | |
236 | else | |
237 | return 16; | |
238 | } | |
239 | ||
240 | uint8_t mfFirstBlockOfSector(uint8_t sectorNo) { | |
241 | if (sectorNo < 32) | |
242 | return sectorNo * 4; | |
243 | else | |
244 | return 32 * 4 + (sectorNo - 32) * 16; | |
245 | } | |
246 | ||
247 | uint8_t mfSectorTrailer(uint8_t blockNo) { | |
248 | if (blockNo < 32*4) { | |
249 | return (blockNo | 0x03); | |
250 | } else { | |
251 | return (blockNo | 0x0f); | |
252 | } | |
253 | } | |
254 | ||
255 | bool mfIsSectorTrailer(uint8_t blockNo) { | |
256 | return (blockNo == mfSectorTrailer(blockNo)); | |
257 | } | |
258 | ||
259 | uint8_t mfSectorNum(uint8_t blockNo) { | |
260 | if (blockNo < 32 * 4) | |
261 | return blockNo / 4; | |
262 | else | |
263 | return 32 + (blockNo - 32 * 4) / 16; | |
264 | ||
265 | } |