fix one too small
[RRG-proxmark3.git] / client / src / cipurse / cipursecore.c
blobff75cc20eb117ec6d8ba39c3c1877298115524a2
1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // See LICENSE.txt for the text of the license.
15 //-----------------------------------------------------------------------------
16 // CIPURSE transport cards data and commands
17 //-----------------------------------------------------------------------------
19 #include "cipursecore.h"
20 #include <string.h> // memcpy memset
21 #include "commonutil.h" // ARRAYLEN
22 #include "comms.h" // DropField
23 #include "util_posix.h" // msleep
24 #include "cmdhf14a.h"
25 #include "../emv/emvcore.h"
26 #include "../emv/emvjson.h"
27 #include "../iso7816/apduinfo.h" // sAPDU_t
28 #include "ui.h"
29 #include "util.h"
30 #include "protocols.h" // ISO7816 APDU return codes
32 // context for secure channel
33 CipurseContext_t cipurseContext;
35 static int CIPURSEExchangeEx(bool activate_field, bool leave_field_on, sAPDU_t apdu, bool include_le,
36 uint16_t le, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
38 if (result_len == NULL) {
39 PrintAndLogEx(FAILED, "CIPURSEExchangeEx, result_len is NULL");
40 return PM3_EINVARG;
43 *result_len = 0;
45 if (sw) {
46 *sw = 0;
48 uint16_t isw = 0;
49 int res = 0;
51 if (activate_field) {
52 DropField();
53 msleep(50);
56 // long messages is not allowed
57 if (apdu.Lc > 228)
58 return 20;
60 // COMPUTE APDU
61 int datalen = 0;
62 uint16_t xle = include_le ? 0x100 : 0x00;
63 if (xle == 0x100 && le != 0) {
64 xle = le;
67 sAPDU_t secapdu;
68 uint8_t securedata[APDU_RES_LEN] = {0};
69 CipurseCAPDUReqEncode(&cipurseContext, &apdu, &secapdu, securedata, include_le, le);
71 uint8_t data[APDU_RES_LEN] = {0};
72 if (APDUEncodeS(&secapdu, false, xle, data, &datalen)) {
73 PrintAndLogEx(ERR, "APDU encoding error.");
74 return 201;
77 if (GetAPDULogging()) {
78 PrintAndLogEx(SUCCESS, ">>>> %s", sprint_hex(data, datalen));
81 res = ExchangeAPDU14a(data, datalen, activate_field, leave_field_on, result, (int)max_result_len, (int *)result_len);
82 if (res) {
83 return res;
86 if (GetAPDULogging()) {
87 PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(result, *result_len));
90 if (*result_len < 2) {
91 return 200;
94 size_t rlen = 0;
95 if (*result_len == 2) {
96 if (cipurseContext.RequestSecurity == CPSMACed || cipurseContext.RequestSecurity == CPSEncrypted) {
97 CipurseCClearContext(&cipurseContext);
100 isw = result[0] * 0x0100 + result[1];
102 } else {
103 CipurseCAPDURespDecode(&cipurseContext, result, *result_len, securedata, &rlen, &isw);
104 memcpy(result, securedata, rlen);
107 *result_len = rlen;
109 if (sw != NULL) {
110 *sw = isw;
113 if (isw != ISO7816_OK) {
114 if (GetAPDULogging()) {
115 if (*sw >> 8 == 0x61) {
116 PrintAndLogEx(ERR, "APDU chaining len:%02x -->", *sw & 0xff);
117 } else {
118 PrintAndLogEx(ERR, "APDU(%02x%02x) ERROR: [%4X] %s", apdu.CLA, apdu.INS, isw, GetAPDUCodeDescription(*sw >> 8, *sw & 0xff));
119 return 5;
124 return PM3_SUCCESS;
127 static int CIPURSEExchange(sAPDU_t apdu, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
128 return CIPURSEExchangeEx(false, true, apdu, true, 0, result, max_result_len, result_len, sw);
131 int CIPURSESelectAID(bool activate_field, bool leave_field_on, uint8_t *aid, size_t aidlen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
132 CipurseCClearContext(&cipurseContext);
134 return EMVSelect(CC_CONTACTLESS, activate_field, leave_field_on, aid, aidlen, result, max_result_len, result_len, sw, NULL);
137 int CIPURSESelect(bool activate_field, bool leave_field_on, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
138 uint8_t aid[] = {0x41, 0x44, 0x20, 0x46, 0x31};
140 return CIPURSESelectAID(activate_field, leave_field_on, aid, sizeof(aid), result, max_result_len, result_len, sw);
143 int CIPURSEChallenge(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
144 return CIPURSEExchangeEx(false, true, (sAPDU_t) {0x00, 0x84, 0x00, 0x00, 0x00, NULL}, true, 0x16, result, max_result_len, result_len, sw);
147 int CIPURSEMutualAuthenticate(uint8_t keyindex, uint8_t *params, uint8_t paramslen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
148 return CIPURSEExchangeEx(false, true, (sAPDU_t) {0x00, 0x82, 0x00, keyindex, paramslen, params}, true, 0x10, result, max_result_len, result_len, sw);
151 int CIPURSECreateFile(uint8_t *attr, uint16_t attrlen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
152 return CIPURSEExchangeEx(false, true, (sAPDU_t) {0x00, 0xe0, 0x00, 0x00, attrlen, attr}, false, 0, result, max_result_len, result_len, sw);
155 int CIPURSEDeleteFile(uint16_t fileid, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
156 uint8_t fileIdBin[] = {fileid >> 8, fileid & 0xff};
157 return CIPURSEExchangeEx(false, true, (sAPDU_t) {0x00, 0xe4, 0x00, 0x00, 02, fileIdBin}, false, 0, result, max_result_len, result_len, sw);
160 int CIPURSEDeleteFileAID(uint8_t *aid, size_t aidLen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
161 return CIPURSEExchangeEx(false, true, (sAPDU_t) {0x00, 0xe4, 0x04, 0x00, aidLen, aid}, false, 0, result, max_result_len, result_len, sw);
164 int CIPURSESelectMFEx(bool activate_field, bool leave_field_on, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
165 return CIPURSESelectFileEx(activate_field, leave_field_on, 0x3f00, result, max_result_len, result_len, sw);
168 int CIPURSESelectMF(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
169 return CIPURSESelectMFEx(false, true, result, max_result_len, result_len, sw);
172 int CIPURSEFormatAll(uint16_t *sw) {
173 uint8_t result[APDU_RES_LEN] = {0};
174 size_t result_len = 0;
175 return CIPURSEExchange((sAPDU_t) {0x80, 0xfc, 0x00, 0x00, 7, (uint8_t *)"ConfirM"}, result, sizeof(result), &result_len, sw);
178 int CIPURSESelectFileEx(bool activate_field, bool leave_field_on, uint16_t fileid, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
179 CipurseCClearContext(&cipurseContext);
180 uint8_t fileIdBin[] = {fileid >> 8, fileid & 0xff};
181 return CIPURSEExchangeEx(activate_field, leave_field_on, (sAPDU_t) {0x00, 0xa4, 0x00, 0x00, 02, fileIdBin}, true, 0, result, max_result_len, result_len, sw);
184 int CIPURSESelectFile(uint16_t fileid, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
185 return CIPURSESelectFileEx(false, true, fileid, result, max_result_len, result_len, sw);
188 int CIPURSESelectMFDefaultFileEx(bool activate_field, bool leave_field_on, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
189 CipurseCClearContext(&cipurseContext);
190 return CIPURSEExchangeEx(activate_field, leave_field_on, (sAPDU_t) {0x00, 0xa4, 0x00, 0x00, 0, NULL}, true, 0, result, max_result_len, result_len, sw);
192 int CIPURSESelectMFDefaultFile(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
193 return CIPURSESelectMFDefaultFileEx(false, true, result, max_result_len, result_len, sw);
196 int CIPURSEReadFileAttributes(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
197 return CIPURSEExchange((sAPDU_t) {0x80, 0xce, 0x00, 0x00, 0, NULL}, result, max_result_len, result_len, sw);
200 int CIPURSEUpdateFileAttributes(uint8_t *data, uint16_t datalen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
201 return CIPURSEExchange((sAPDU_t) {0x80, 0xde, 0x00, 0x00, datalen, data}, result, max_result_len, result_len, sw);
204 int CIPURSEReadBinary(uint16_t offset, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
205 return CIPURSEExchange((sAPDU_t) {0x00, 0xb0, (offset >> 8) & 0x7f, offset & 0xff, 0, NULL}, result, max_result_len, result_len, sw);
208 int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
209 return CIPURSEExchange((sAPDU_t) {0x00, 0xd6, (offset >> 8) & 0x7f, offset & 0xff, datalen, data}, result, max_result_len, result_len, sw);
212 int CIPURSEUpdateKeyAttrib(uint8_t key_num, uint8_t key_attrib, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
213 return CIPURSEExchangeEx(false, true, (sAPDU_t) {0x80, 0x4e, 0x00, key_num, 1, &key_attrib}, false, 0, result, max_result_len, result_len, sw);
216 int CIPURSEUpdateKey(uint8_t encrypt_key_num, uint8_t key_num, uint8_t *key, uint16_t key_len, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
217 return CIPURSEExchangeEx(false, true, (sAPDU_t) {0x80, 0x52, encrypt_key_num, key_num, key_len, key}, false, 0, result, max_result_len, result_len, sw);
220 int CIPURSECommitTransaction(uint16_t *sw) {
221 uint8_t result[APDU_RES_LEN] = {0};
222 size_t result_len = 0;
223 return CIPURSEExchange((sAPDU_t) {0x80, 0x7e, 0x00, 0x00, 0, NULL}, result, sizeof(result), &result_len, sw);
226 int CIPURSECancelTransaction(uint16_t *sw) {
227 uint8_t result[APDU_RES_LEN] = {0};
228 size_t result_len = 0;
229 return CIPURSEExchange((sAPDU_t) {0x80, 0x7c, 0x00, 0x00, 0, NULL}, result, sizeof(result), &result_len, sw);
232 bool CIPURSEChannelAuthenticate(uint8_t keyindex, uint8_t *key, bool verbose) {
233 uint8_t buf[APDU_RES_LEN] = {0};
234 size_t len = 0;
235 uint16_t sw = 0;
237 CipurseContext_t cpc = {0};
238 CipurseCSetKey(&cpc, keyindex, key);
240 // get RP, rP
241 int res = CIPURSEChallenge(buf, sizeof(buf), &len, &sw);
242 if (res != 0 || len != 0x16) {
243 if (verbose) {
244 PrintAndLogEx(ERR, "Cipurse get challenge ( " _RED_("fail") " ). Card returns 0x%04x", sw);
246 return false;
248 CipurseCSetRandomFromPICC(&cpc, buf);
250 // make auth data
251 uint8_t authparams[16 + 16 + 6] = {0};
252 CipurseCAuthenticateHost(&cpc, authparams);
254 // authenticate
255 res = CIPURSEMutualAuthenticate(keyindex, authparams, sizeof(authparams), buf, sizeof(buf), &len, &sw);
256 if (res != 0 || sw != ISO7816_OK || len != 16) {
257 if (sw == 0x6988) {
258 if (verbose) {
259 PrintAndLogEx(WARNING, "Authentication ( " _RED_("fail") " ). Wrong key");
261 } else if (sw == 0x6A88) {
262 if (verbose) {
263 PrintAndLogEx(WARNING, "Authentication ( " _RED_("fail") " ). Wrong key number");
265 } else {
266 if (verbose) {
267 PrintAndLogEx(WARNING, "Authentication ( " _RED_("fail") " ). Card returns 0x%04x", sw);
271 CipurseCClearContext(&cipurseContext);
272 return false;
275 if (CipurseCCheckCT(&cpc, buf)) {
276 if (verbose) {
277 PrintAndLogEx(SUCCESS, "Authentication ( " _GREEN_("ok") " )");
280 CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
281 memcpy(&cipurseContext, &cpc, sizeof(CipurseContext_t));
282 return true;
283 } else {
284 if (verbose) {
285 PrintAndLogEx(WARNING, "Authentication ( " _RED_("fail") " ) card returned wrong CT");
288 CipurseCClearContext(&cipurseContext);
289 return false;
293 void CIPURSECSetActChannelSecurityLevels(CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp) {
294 CipurseCChannelSetSecurityLevels(&cipurseContext, req, resp);
297 static void CIPURSEPrintPersoMode(uint8_t data) {
298 if ((data & 0x01) == 0x01)
299 PrintAndLogEx(INFO, "Perso.......... " _YELLOW_("filesystem"));
300 if ((data & 0x02) == 0x02)
301 PrintAndLogEx(INFO, "Perso.......... " _YELLOW_("EMV"));
302 if ((data & 0x04) == 0x04)
303 PrintAndLogEx(INFO, "Perso.......... " _YELLOW_("transaction supported"));
306 // 2021 iceman: what is the description text of profile L,S,T ?
307 static void CIPURSEPrintProfileInfo(uint8_t data) {
309 PrintAndLogEx(INFO, "Profile........" NOLF);
310 if ((data & 0x01) == 0x01)
311 PrintAndLogEx(NORMAL, " L" NOLF);
312 if ((data & 0x02) == 0x02)
313 PrintAndLogEx(NORMAL, ", S" NOLF);
314 if ((data & 0x04) == 0x04)
315 PrintAndLogEx(NORMAL, ", T" NOLF);
316 PrintAndLogEx(NORMAL, "");
319 static void CIPURSEPrintManufacturerInfo(uint8_t data) {
320 if (data == 0)
321 PrintAndLogEx(INFO, "Manufacturer... n/a");
322 else
323 PrintAndLogEx(INFO, "Manufacturer... %s", getTagInfo(data)); // getTagInfo from cmfhf14a.h
326 void CIPURSEPrintInfoFile(uint8_t *data, size_t len) {
327 if (len < 2) {
328 PrintAndLogEx(FAILED, "Info file length too short");
329 return;
332 PrintAndLogEx(INFO, "--- " _CYAN_("CIPURSE Information") "---------------------");
333 PrintAndLogEx(INFO, "Version........ " _YELLOW_("v%d.%d"), data[0], data[1]);
335 if (len >= 3)
336 CIPURSEPrintPersoMode(data[2]);
338 if (len >= 4)
339 CIPURSEPrintProfileInfo(data[3]);
341 if (len >= 9)
342 CIPURSEPrintManufacturerInfo(data[8]);
345 void CIPURSEPrintFileDescriptor(uint8_t desc) {
346 if (desc == 0x01)
347 PrintAndLogEx(INFO, "Binary file");
348 else if (desc == 0x11)
349 PrintAndLogEx(INFO, "Binary file with transactions");
350 else if (desc == 0x02)
351 PrintAndLogEx(INFO, "Linear record file");
352 else if (desc == 0x12)
353 PrintAndLogEx(INFO, "Linear record file with transactions");
354 else if (desc == 0x06)
355 PrintAndLogEx(INFO, "Cyclic record file");
356 else if (desc == 0x16)
357 PrintAndLogEx(INFO, "Cyclic record file with transactions");
358 else if (desc == 0x1E)
359 PrintAndLogEx(INFO, "Linear value-record file");
360 else if (desc == 0x1F)
361 PrintAndLogEx(INFO, "Linear value-record file with transactions");
362 else
363 PrintAndLogEx(INFO, "Unknown file 0x%02x", desc);
366 void CIPURSEPrintDGIArray(uint8_t *dgi, size_t dgilen) {
367 if (dgilen < 3) {
368 PrintAndLogEx(WARNING, "DGI too small. Length: %zu", dgilen);
369 return;
372 uint8_t *dgiptr = dgi;
373 size_t reslen = 0;
374 while (dgilen > reslen + 2) {
375 uint8_t len = dgiptr[2];
376 CIPURSEPrintDGI(dgiptr, len + 3);
378 dgiptr += len + 3;
379 reslen += len + 3;
383 void CIPURSEPrintDGI(uint8_t *dgi, size_t dgilen) {
384 if (dgilen < 3) {
385 PrintAndLogEx(WARNING, "DGI too small. Length: %zu", dgilen);
386 return;
389 uint8_t len = dgi[2];
390 if (len + 3 != dgilen) {
391 PrintAndLogEx(ERR, "DGI size does not match with record size. Length of record: %zu, DGI size: %d", dgilen, len);
392 return;
395 // check DGI
396 if (dgi[0] == 0x92 && dgi[1] == 0x00) {
397 PrintAndLogEx(INFO, "DGI 9200 - ADF file attributes");
398 CIPURSEPrintFileAttrEx(&dgi[3], len, true);
400 } else if (dgi[0] == 0x92 && dgi[1] == 0x01) {
401 PrintAndLogEx(INFO, "DGI 9201 - EF file attributes");
402 CIPURSEPrintFileAttrEx(&dgi[3], len, true);
404 } else if (dgi[0] == 0xa0 && dgi[1] == 0x0f) {
405 PrintAndLogEx(INFO, "DGI a00f - All key values");
407 if (len % 20 != 0) {
408 PrintAndLogEx(ERR, "Key values size must be array of 20-bite record. ADF size: %d", len);
409 return;
412 for (int i = 0; i < len / 20; i++) {
414 uint8_t kvv[CIPURSE_KVV_LENGTH] = {0};
415 uint8_t aeskey[16] = {0};
416 memcpy(aeskey, &dgi[3 + i * 20 + 0], sizeof(aeskey));
418 PrintAndLogEx(INFO, "Key[%d]............ %s", i + 1, sprint_hex_inrow(aeskey, sizeof(aeskey)));
419 PrintAndLogEx(INFO, " Additional info.. 0x%02x", dgi[3 + i * 20 + 16]);
420 CipurseCGetKVV(aeskey, kvv);
421 bool kvvvalid = (memcmp(kvv, &dgi[3 + i * 20 + 17], 3) == 0);
422 PrintAndLogEx(INFO, " KVV.............. %s (%s)", sprint_hex_inrow(&dgi[3 + i * 20 + 17], 3), (kvvvalid) ? _GREEN_("valid") : _RED_("invalid"));
424 PrintAndLogEx(NORMAL, "");
426 } else {
427 PrintAndLogEx(WARNING, "Unknown DGI %02x%02x", dgi[0], dgi[1]);
431 void CIPURSEPrintKeySecurityAttributes(uint8_t attr) {
432 PrintAndLogEx(INFO, " Update right: %s", (attr & 0x01) ? "self" : "any");
433 PrintAndLogEx(INFO, " Change key and rights: %s", (attr & 0x02) ? "ok" : "frozen");
434 PrintAndLogEx(INFO, " Use as key encryption key: %s", (attr & 0x04) ? "blocked" : "ok");
435 PrintAndLogEx(INFO, " Key validity: %s", (attr & 0x80) ? "invalid" : "valid");
438 static void CIPURSEPrintKeyAttrib(uint8_t *attr) {
439 PrintAndLogEx(INFO, "--- " _CYAN_("Key Attributes") "---------------------");
440 PrintAndLogEx(INFO, "Additional info... 0x%02x", attr[0]);
441 PrintAndLogEx(INFO, "Key length........ %d", attr[1]);
442 PrintAndLogEx(INFO, "Algorithm ID...... 0x%02x (%s)", attr[2], (attr[2] == 0x09) ? "AES" : "unknown");
443 PrintAndLogEx(INFO, "Security attr..... 0x%02x", attr[3]);
444 CIPURSEPrintKeySecurityAttributes(attr[3]);
445 PrintAndLogEx(INFO, "KVV............... 0x%02x%02x%02x", attr[4], attr[5], attr[6]);
446 PrintAndLogEx(NORMAL, "");
449 static void CIPURSEPrintKeyAttribDGI(uint8_t *attr) {
450 PrintAndLogEx(INFO, "--- " _CYAN_("DGI Key Attributes") "---------------------");
451 PrintAndLogEx(INFO, "Security attr..... 0x%02x", attr[0]);
452 CIPURSEPrintKeySecurityAttributes(attr[0]);
453 PrintAndLogEx(INFO, "Key length........ %d", attr[1]);
454 PrintAndLogEx(INFO, "Algorithm ID...... 0x%02x (%s)", attr[2], (attr[2] == 0x09) ? "AES" : "unknown");
455 PrintAndLogEx(NORMAL, "");
458 const char *CIPURSEGetSMR(uint8_t smr) {
459 switch (smr) {
460 case 0x00:
461 return "plain";
462 case 0x01:
463 return "mac";
464 case 0x02:
465 return "enc";
466 default:
467 return "unknown";
471 void CIPURSEPrintSMR(const uint8_t *smrrec) {
472 PrintAndLogEx(INFO, "1. %s/%s", CIPURSEGetSMR((smrrec[0] >> 6) & 0x03), CIPURSEGetSMR((smrrec[0] >> 4) & 0x03));
473 PrintAndLogEx(INFO, "2. %s/%s", CIPURSEGetSMR((smrrec[0] >> 2) & 0x03), CIPURSEGetSMR((smrrec[0] >> 0) & 0x03));
474 PrintAndLogEx(INFO, "3. %s/%s", CIPURSEGetSMR((smrrec[1] >> 6) & 0x03), CIPURSEGetSMR((smrrec[1] >> 4) & 0x03));
475 PrintAndLogEx(INFO, "4. %s/%s", CIPURSEGetSMR((smrrec[1] >> 2) & 0x03), CIPURSEGetSMR((smrrec[1] >> 0) & 0x03));
478 void CIPURSEPrintART(const uint8_t *artrec, size_t artlen) {
479 if (artlen < 1 || artlen > 9)
480 return;
481 for (int i = 0; i < artlen; i++) {
482 if (i == 0)
483 PrintAndLogEx(INFO, "always: " NOLF);
484 else
485 PrintAndLogEx(INFO, "key %d : " NOLF, i);
487 for (int n = 7; n >= 0; n--)
488 if ((artrec[i] >> n) & 0x01)
489 PrintAndLogEx(NORMAL, "%d " NOLF, n + 1);
490 else
491 PrintAndLogEx(NORMAL, " " NOLF);
493 PrintAndLogEx(NORMAL, "");
497 void CIPURSEPrintEFFileAttr(uint8_t *attr, size_t len) {
498 CIPURSEPrintFileDescriptor(attr[0]);
500 if (attr[1] == 0)
501 PrintAndLogEx(INFO, "SFI.... not assigned");
502 else
503 PrintAndLogEx(INFO, "SFI.... 0x%02x", attr[1]);
505 PrintAndLogEx(INFO, "File ID... 0x%02x%02x", attr[2], attr[3]);
507 if (attr[0] == 0x01 || attr[0] == 0x11)
508 PrintAndLogEx(INFO, "File size... %d", (attr[4] << 8) + attr[5]);
509 else
510 PrintAndLogEx(INFO, "Record num " _YELLOW_("%d") " record size " _YELLOW_("%d"), attr[4], attr[5]);
512 PrintAndLogEx(INFO, "Keys assigned... %d", attr[6]);
514 if (len >= 9) {
515 PrintAndLogEx(INFO, "SMR entries... %02x%02x", attr[7], attr[8]);
516 CIPURSEPrintSMR(&attr[7]);
519 if (len >= 10) {
520 PrintAndLogEx(INFO, "ART... %s", sprint_hex(&attr[9], len - 9));
521 CIPURSEPrintART(&attr[9], len - 9);
523 if (attr[6] + 1 != len - 9) {
524 PrintAndLogEx(WARNING, "ART length is wrong");
529 void CIPURSEPrintFileAttrEx(uint8_t *attr, size_t len, bool isDGI) {
530 if (len < 7) {
531 PrintAndLogEx(FAILED, "Attributes length too short");
532 return;
535 PrintAndLogEx(INFO, "--- " _CYAN_("File Attributes") "---------------------");
536 if (attr[0] == 0x38 || attr[0] == 0x3F) {
537 PrintAndLogEx(INFO, "Type... MF, ADF");
539 if (attr[1] == 0x00) {
540 if (attr[0] == 0x3F)
541 PrintAndLogEx(INFO, "Type... PxSE");
542 else
543 PrintAndLogEx(INFO, "Type... MF");
544 } else {
545 if ((attr[1] & 0xe0) == 0x00)
546 PrintAndLogEx(INFO, "Type... Unknown");
548 if ((attr[1] & 0xe0) == 0x20)
549 PrintAndLogEx(INFO, "Type... CIPURSE L");
551 if ((attr[1] & 0xe0) == 0x40)
552 PrintAndLogEx(INFO, "Type... CIPURSE S");
554 if ((attr[1] & 0xe0) == 0x60)
555 PrintAndLogEx(INFO, "Type... CIPURSE T");
557 if ((attr[1] & 0x02) == 0x00)
558 PrintAndLogEx(INFO, "Autoselect on PxSE select OFF");
559 else
560 PrintAndLogEx(INFO, "Autoselect on PxSE select ON");
562 if ((attr[1] & 0x01) == 0x00)
563 PrintAndLogEx(INFO, "PxSE select returns FCPTemplate OFF");
564 else
565 PrintAndLogEx(INFO, "PxSE select returns FCPTemplate ON");
568 PrintAndLogEx(INFO, "File ID................... 0x%02x%02x", attr[2], attr[3]);
569 PrintAndLogEx(INFO, "Maximum # custom EFs...... %d", attr[4]);
570 PrintAndLogEx(INFO, "Maximum # EFs with SFID... %d", attr[5]);
572 uint8_t keynum = attr[6];
573 PrintAndLogEx(INFO, "Keys assigned... %d", keynum);
575 int idx = 7;
576 if (keynum > 0) {
577 if (len >= idx + 2) {
578 PrintAndLogEx(INFO, "SMR entries... %02x%02x", attr[idx], attr[idx + 1]);
579 CIPURSEPrintSMR(&attr[idx]);
581 idx += 2;
583 if (len >= idx + keynum + 1) {
584 PrintAndLogEx(INFO, "ART... %s", sprint_hex(&attr[idx], keynum + 1));
585 CIPURSEPrintART(&attr[idx], keynum + 1);
586 PrintAndLogEx(NORMAL, "");
588 idx += keynum + 1;
590 size_t reclen = (isDGI) ? 3 : 7;
591 if (len >= idx + keynum * reclen) {
592 for (int i = 0; i < keynum; i++) {
593 PrintAndLogEx(INFO, "Key %d Attributes... %s", i + 1, sprint_hex(&attr[idx + i * reclen], reclen));
594 if (isDGI)
595 CIPURSEPrintKeyAttribDGI(&attr[idx + i * reclen]);
596 else
597 CIPURSEPrintKeyAttrib(&attr[idx + i * reclen]);
600 idx += keynum * reclen;
602 // FCP
603 if (len >= idx + 1) {
604 int xlen = len - idx;
605 // for MF only
606 if (attr[1] == 0x00 && attr[0] != 0x3F)
607 xlen = xlen - 6;
608 if (xlen > 0 && xlen < 200) {
609 PrintAndLogEx(INFO, "TLV file control parameters... [%d] %s", xlen, sprint_hex(&attr[idx], xlen));
610 TLVPrintFromBuffer(&attr[idx], xlen);
611 PrintAndLogEx(NORMAL, "");
614 // MF only
615 if (attr[1] == 0x00 && attr[0] != 0x3F) {
616 PrintAndLogEx(INFO, "Total memory size... %d", (attr[len - 6] << 16) + (attr[len - 5] << 8) + attr[len - 4]);
617 PrintAndLogEx(INFO, "Free memory size.... %d", (attr[len - 3] << 16) + (attr[len - 2] << 8) + attr[len - 1]);
620 } else {
621 PrintAndLogEx(INFO, "Type... EF");
622 CIPURSEPrintEFFileAttr(attr, len);
623 PrintAndLogEx(NORMAL, "");
628 void CIPURSEPrintFileAttr(uint8_t *attr, size_t len) {
629 CIPURSEPrintFileAttrEx(attr, len, false);
632 void CIPURSEPrintFileUpdateAttr(uint8_t *attr, size_t len) {
633 uint8_t keynum = attr[0];
634 PrintAndLogEx(INFO, "Keys assigned... %d", keynum);
636 size_t idx = 1;
637 if (keynum > 0) {
638 if (len >= idx + 2) {
639 PrintAndLogEx(INFO, "SMR entries... %02x%02x", attr[idx], attr[idx + 1]);
640 CIPURSEPrintSMR(&attr[idx]);
642 idx += 2;
644 if (len >= idx + keynum + 1) {
645 PrintAndLogEx(INFO, "ART... %s", sprint_hex(&attr[idx], keynum + 1));
646 CIPURSEPrintART(&attr[idx], keynum + 1);
647 PrintAndLogEx(NORMAL, "");
649 idx += keynum + 1;
652 // FCI
653 if (len >= idx + 1) {
654 int xlen = len - idx;
655 if (xlen > 0 && xlen < 200) {
656 PrintAndLogEx(INFO, "TLV file control parameters... [%d] %s", xlen, sprint_hex(&attr[idx], xlen));
657 TLVPrintFromBuffer(&attr[idx], xlen);
658 PrintAndLogEx(NORMAL, "");