fix little endian vs big endian in the macros... again... but this time correct
[RRG-proxmark3.git] / client / src / cipurse / cipursecore.c
blob03ab558afec627b0013e4f49b68d3e4eb090b7d7
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2021 Merlok
3 //
4 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
5 // at your option, any later version. See the LICENSE.txt file for the text of
6 // the license.
7 //-----------------------------------------------------------------------------
8 // CIPURSE transport cards data and commands
9 //-----------------------------------------------------------------------------
11 #include "cipursecore.h"
13 #include "commonutil.h" // ARRAYLEN
14 #include "comms.h" // DropField
15 #include "util_posix.h" // msleep
16 #include <string.h> // memcpy memset
18 #include "cmdhf14a.h"
19 #include "emv/emvcore.h"
20 #include "emv/emvjson.h"
21 #include "ui.h"
22 #include "util.h"
24 // context for secure channel
25 CipurseContext cipurseContext;
27 static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint16_t Le, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
28 uint8_t data[APDU_RES_LEN] = {0};
29 uint8_t securedata[APDU_RES_LEN] = {0};
30 sAPDU secapdu;
32 *ResultLen = 0;
33 if (sw) *sw = 0;
34 uint16_t isw = 0;
35 int res = 0;
37 if (ActivateField) {
38 DropField();
39 msleep(50);
42 // long messages is not allowed
43 if (apdu.Lc > 228)
44 return 20;
46 // COMPUTE APDU
47 int datalen = 0;
48 uint16_t xle = IncludeLe ? 0x100 : 0x00;
49 if (xle == 0x100 && Le != 0)
50 xle = Le;
52 CipurseCAPDUReqEncode(&cipurseContext, &apdu, &secapdu, securedata, IncludeLe, Le);
54 if (APDUEncodeS(&secapdu, false, xle, data, &datalen)) {
55 PrintAndLogEx(ERR, "APDU encoding error.");
56 return 201;
59 if (GetAPDULogging())
60 PrintAndLogEx(SUCCESS, ">>>> %s", sprint_hex(data, datalen));
62 res = ExchangeAPDU14a(data, datalen, ActivateField, LeaveFieldON, Result, (int)MaxResultLen, (int *)ResultLen);
63 if (res) {
64 return res;
67 if (GetAPDULogging())
68 PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(Result, *ResultLen));
70 if (*ResultLen < 2) {
71 return 200;
74 size_t rlen = 0;
75 if (*ResultLen == 2) {
76 if (cipurseContext.RequestSecurity == CPSMACed || cipurseContext.RequestSecurity == CPSEncrypted)
77 CipurseCClearContext(&cipurseContext);
79 isw = Result[0] * 0x0100 + Result[1];
80 } else {
81 CipurseCAPDURespDecode(&cipurseContext, Result, *ResultLen, securedata, &rlen, &isw);
82 memcpy(Result, securedata, rlen);
85 if (ResultLen != NULL)
86 *ResultLen = rlen;
88 if (sw != NULL)
89 *sw = isw;
91 if (isw != 0x9000) {
92 if (GetAPDULogging()) {
93 if (*sw >> 8 == 0x61) {
94 PrintAndLogEx(ERR, "APDU chaining len:%02x -->", *sw & 0xff);
95 } else {
96 PrintAndLogEx(ERR, "APDU(%02x%02x) ERROR: [%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff));
97 return 5;
102 return PM3_SUCCESS;
105 static int CIPURSEExchange(sAPDU apdu, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
106 return CIPURSEExchangeEx(false, true, apdu, true, 0, Result, MaxResultLen, ResultLen, sw);
109 int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
110 uint8_t data[] = {0x41, 0x44, 0x20, 0x46, 0x31};
111 CipurseCClearContext(&cipurseContext);
113 return EMVSelect(CC_CONTACTLESS, ActivateField, LeaveFieldON, data, sizeof(data), Result, MaxResultLen, ResultLen, sw, NULL);
116 int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
117 return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x84, 0x00, 0x00, 0x00, NULL}, true, 0x16, Result, MaxResultLen, ResultLen, sw);
120 int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramslen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
121 return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw);
124 int CIPURSECreateFile(uint8_t *attr, uint16_t attrlen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
125 return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xe4, 0x00, 0x00, attrlen, attr}, false, 0, Result, MaxResultLen, ResultLen, sw);
128 int CIPURSEDeleteFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
129 uint8_t fileIdBin[] = {fileID >> 8, fileID & 0xff};
130 return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xe4, 0x00, 0x00, 02, fileIdBin}, false, 0, Result, MaxResultLen, ResultLen, sw);
133 int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
134 uint8_t fileIdBin[] = {fileID >> 8, fileID & 0xff};
135 return CIPURSEExchange((sAPDU) {0x00, 0xa4, 0x00, 0x00, 02, fileIdBin}, Result, MaxResultLen, ResultLen, sw);
138 int CIPURSESelectMFFile(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
139 return CIPURSEExchange((sAPDU) {0x00, 0xa4, 0x00, 0x00, 0, NULL}, Result, MaxResultLen, ResultLen, sw);
142 int CIPURSEReadFileAttributes(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
143 return CIPURSEExchange((sAPDU) {0x80, 0xce, 0x00, 0x00, 0, NULL}, Result, MaxResultLen, ResultLen, sw);
146 int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
147 return CIPURSEExchange((sAPDU) {0x00, 0xb0, (offset >> 8) & 0x7f, offset & 0xff, 0, NULL}, Result, MaxResultLen, ResultLen, sw);
150 int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
151 return CIPURSEExchange((sAPDU) {0x00, 0xd6, (offset >> 8) & 0x7f, offset & 0xff, datalen, data}, Result, MaxResultLen, ResultLen, sw);
154 bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
155 uint8_t buf[APDU_RES_LEN] = {0};
156 size_t len = 0;
157 uint16_t sw = 0;
159 CipurseContext cpc = {0};
160 CipurseCSetKey(&cpc, keyIndex, key);
162 // get RP, rP
163 int res = CIPURSEChallenge(buf, sizeof(buf), &len, &sw);
164 if (res != 0 || len != 0x16) {
165 if (verbose)
166 PrintAndLogEx(ERR, "Cipurse get challenge " _RED_("error") ". Card returns 0x%04x.", sw);
168 return false;
170 CipurseCSetRandomFromPICC(&cpc, buf);
172 // make auth data
173 uint8_t authparams[16 + 16 + 6] = {0};
174 CipurseCAuthenticateHost(&cpc, authparams);
176 // authenticate
177 res = CIPURSEMutalAuthenticate(keyIndex, authparams, sizeof(authparams), buf, sizeof(buf), &len, &sw);
178 if (res != 0 || sw != 0x9000 || len != 16) {
179 if (sw == 0x6988) {
180 if (verbose)
181 PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Wrong key.");
182 } else if (sw == 0x6A88) {
183 if (verbose)
184 PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Wrong key number.");
185 } else {
186 if (verbose)
187 PrintAndLogEx(ERR, "Cipurse authentication " _RED_("error") ". Card returns 0x%04x.", sw);
190 CipurseCClearContext(&cipurseContext);
191 return false;
194 if (CipurseCCheckCT(&cpc, buf)) {
195 if (verbose)
196 PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
198 CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
199 memcpy(&cipurseContext, &cpc, sizeof(CipurseContext));
200 return true;
201 } else {
202 if (verbose)
203 PrintAndLogEx(ERR, "Authentication " _RED_("ERROR") " card returned wrong CT");
205 CipurseCClearContext(&cipurseContext);
206 return false;
210 void CIPURSECSetActChannelSecurityLevels(CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp) {
211 CipurseCChannelSetSecurityLevels(&cipurseContext, req, resp);
214 static void CIPURSEPrintPersoMode(uint8_t data) {
215 if (data & 0x01)
216 PrintAndLogEx(INFO, "Perso: filesystem");
217 if (data & 0x02)
218 PrintAndLogEx(INFO, "Perso: EMV");
219 if (data & 0x04)
220 PrintAndLogEx(INFO, "Perso: transaction supported");
224 static void CIPURSEPrintProfileInfo(uint8_t data) {
225 if (data & 0x01)
226 PrintAndLogEx(INFO, "Profile: L");
227 if (data & 0x02)
228 PrintAndLogEx(INFO, "Profile: S");
229 if (data & 0x04)
230 PrintAndLogEx(INFO, "Profile: T");
233 static void CIPURSEPrintManufacturerInfo(uint8_t data) {
234 if (data == 0)
235 PrintAndLogEx(INFO, "Manufacturer: n/a");
236 else
237 PrintAndLogEx(INFO, "Manufacturer: %s", getTagInfo(data)); // getTagInfo from cmfhf14a.h
240 void CIPURSEPrintInfoFile(uint8_t *data, size_t len) {
241 if (len < 2) {
242 PrintAndLogEx(ERR, "Info file length " _RED_("ERROR"));
243 return;
246 PrintAndLogEx(INFO, "------------ INFO ------------");
247 PrintAndLogEx(INFO, "CIPURSE version %d revision %d", data[0], data[1]);
249 if (len >= 3)
250 CIPURSEPrintPersoMode(data[2]);
252 if (len >= 4)
253 CIPURSEPrintProfileInfo(data[3]);
255 if (len >= 9)
256 CIPURSEPrintManufacturerInfo(data[8]);
259 static void CIPURSEPrintFileDescriptor(uint8_t desc) {
260 if (desc == 0x01)
261 PrintAndLogEx(INFO, "Binary file");
262 else if (desc == 0x11)
263 PrintAndLogEx(INFO, "Binary file with transactions");
264 else if (desc == 0x02)
265 PrintAndLogEx(INFO, "Linear record file");
266 else if (desc == 0x12)
267 PrintAndLogEx(INFO, "Linear record file with transactions");
268 else if (desc == 0x06)
269 PrintAndLogEx(INFO, "Cyclic record file");
270 else if (desc == 0x16)
271 PrintAndLogEx(INFO, "Cyclic record file with transactions");
272 else if (desc == 0x1E)
273 PrintAndLogEx(INFO, "Linear value-record file");
274 else if (desc == 0x1F)
275 PrintAndLogEx(INFO, "Linear value-record file with transactions");
276 else
277 PrintAndLogEx(INFO, "Unknown file 0x%02x", desc);
280 static void CIPURSEPrintKeyAttrib(uint8_t *attr) {
281 PrintAndLogEx(INFO, "-------- KEY ATTRIBUTES --------");
282 PrintAndLogEx(INFO, "Additional info: 0x%02x", attr[0]);
283 PrintAndLogEx(INFO, "Key length: %d", attr[1]);
284 PrintAndLogEx(INFO, "Algorithm ID: 0x%02x", attr[2]);
285 PrintAndLogEx(INFO, "Security attr: 0x%02x", attr[3]);
286 PrintAndLogEx(INFO, "KVV: 0x%02x%02x%02x", attr[4], attr[5], attr[6]);
287 PrintAndLogEx(INFO, "-------------------------------");
290 void CIPURSEPrintFileAttr(uint8_t *fileAttr, size_t len) {
291 if (len < 7) {
292 PrintAndLogEx(ERR, "Attributes length " _RED_("ERROR"));
293 return;
296 PrintAndLogEx(INFO, "--------- FILE ATTRIBUTES ---------");
297 if (fileAttr[0] == 0x38) {
298 PrintAndLogEx(INFO, "Type: MF, ADF");
299 if (fileAttr[1] == 0x00) {
300 PrintAndLogEx(INFO, "Type: MF");
301 } else {
302 if ((fileAttr[1] & 0xe0) == 0x00)
303 PrintAndLogEx(INFO, "Type: Unknown");
304 if ((fileAttr[1] & 0xe0) == 0x20)
305 PrintAndLogEx(INFO, "Type: CIPURSE L");
306 if ((fileAttr[1] & 0xe0) == 0x40)
307 PrintAndLogEx(INFO, "Type: CIPURSE S");
308 if ((fileAttr[1] & 0xe0) == 0x60)
309 PrintAndLogEx(INFO, "Type: CIPURSE T");
310 if ((fileAttr[1] & 0x02) == 0x00)
311 PrintAndLogEx(INFO, "Autoselect on PxSE select OFF");
312 else
313 PrintAndLogEx(INFO, "Autoselect on PxSE select ON");
314 if ((fileAttr[1] & 0x01) == 0x00)
315 PrintAndLogEx(INFO, "PxSE select returns FCPTemplate OFF");
316 else
317 PrintAndLogEx(INFO, "PxSE select returns FCPTemplate ON");
320 PrintAndLogEx(INFO, "File ID: 0x%02x%02x", fileAttr[2], fileAttr[3]);
322 PrintAndLogEx(INFO, "Maximum number of custom EFs: %d", fileAttr[4]);
323 PrintAndLogEx(INFO, "Maximum number of EFs with SFID: %d", fileAttr[5]);
324 uint8_t keyNum = fileAttr[6];
325 PrintAndLogEx(INFO, "Keys assigned: %d", keyNum);
327 if (len >= 9) {
328 PrintAndLogEx(INFO, "SMR entries: %02x%02x", fileAttr[7], fileAttr[8]);
331 if (len >= 10 + keyNum + 1) {
332 PrintAndLogEx(INFO, "ART: %s", sprint_hex(&fileAttr[9], keyNum + 1));
335 if (len >= 11 + keyNum + 1 + keyNum * 7) {
336 for (int i = 0; i < keyNum; i++) {
337 PrintAndLogEx(INFO, "Key %d Attributes: %s", i, sprint_hex(&fileAttr[11 + keyNum + 1 + i * 7], 7));
338 CIPURSEPrintKeyAttrib(&fileAttr[11 + keyNum + 1 + i * 7]);
341 // MF
342 if (fileAttr[1] == 0x00) {
343 PrintAndLogEx(INFO, "Total memory size: %d", (fileAttr[len - 6] << 16) + (fileAttr[len - 1] << 5) + fileAttr[len - 4]);
344 PrintAndLogEx(INFO, "Free memory size: %d", (fileAttr[len - 3] << 16) + (fileAttr[len - 2] << 8) + fileAttr[len - 1]);
346 } else {
347 int ptr = 11 + keyNum + 1 + keyNum * 7;
348 if (len > ptr)
349 PrintAndLogEx(INFO, "TLV file control: %s", sprint_hex(&fileAttr[ptr], len - ptr));
351 } else {
352 PrintAndLogEx(INFO, "Type: EF");
353 CIPURSEPrintFileDescriptor(fileAttr[0]);
354 if (fileAttr[1] == 0)
355 PrintAndLogEx(INFO, "SFI: not assigned");
356 else
357 PrintAndLogEx(INFO, "SFI: 0x%02x", fileAttr[1]);
359 PrintAndLogEx(INFO, "File ID: 0x%02x%02x", fileAttr[2], fileAttr[3]);
361 if (fileAttr[0] == 0x01 || fileAttr[0] == 0x11)
362 PrintAndLogEx(INFO, "File size: %d", (fileAttr[4] << 8) + fileAttr[5]);
363 else
364 PrintAndLogEx(INFO, "Record num: %d record size: %d", fileAttr[4], fileAttr[5]);
366 PrintAndLogEx(INFO, "Keys assigned: %d", fileAttr[6]);
368 if (len >= 9) {
369 PrintAndLogEx(INFO, "SMR entries: %02x%02x", fileAttr[7], fileAttr[8]);
372 if (len >= 10) {
373 PrintAndLogEx(INFO, "ART: %s", sprint_hex(&fileAttr[9], len - 9));
374 if (fileAttr[6] + 1 != len - 9)
375 PrintAndLogEx(WARNING, "ART length is wrong");