1 //-----------------------------------------------------------------------------
2 // Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
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.
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
25 #include "../emv/emvcore.h"
26 #include "../emv/emvjson.h"
27 #include "../iso7816/apduinfo.h" // sAPDU_t
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");
56 // long messages is not allowed
62 uint16_t xle
= include_le
? 0x100 : 0x00;
63 if (xle
== 0x100 && le
!= 0) {
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.");
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
);
86 if (GetAPDULogging()) {
87 PrintAndLogEx(SUCCESS
, "<<<< %s", sprint_hex(result
, *result_len
));
90 if (*result_len
< 2) {
95 if (*result_len
== 2) {
96 if (cipurseContext
.RequestSecurity
== CPSMACed
|| cipurseContext
.RequestSecurity
== CPSEncrypted
) {
97 CipurseCClearContext(&cipurseContext
);
100 isw
= result
[0] * 0x0100 + result
[1];
103 CipurseCAPDURespDecode(&cipurseContext
, result
, *result_len
, securedata
, &rlen
, &isw
);
104 memcpy(result
, securedata
, rlen
);
113 if (isw
!= ISO7816_OK
) {
114 if (GetAPDULogging()) {
115 if (*sw
>> 8 == 0x61) {
116 PrintAndLogEx(ERR
, "APDU chaining len:%02x -->", *sw
& 0xff);
118 PrintAndLogEx(ERR
, "APDU(%02x%02x) ERROR: [%4X] %s", apdu
.CLA
, apdu
.INS
, isw
, GetAPDUCodeDescription(*sw
>> 8, *sw
& 0xff));
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};
237 CipurseContext_t cpc
= {0};
238 CipurseCSetKey(&cpc
, keyindex
, key
);
241 int res
= CIPURSEChallenge(buf
, sizeof(buf
), &len
, &sw
);
242 if (res
!= 0 || len
!= 0x16) {
244 PrintAndLogEx(ERR
, "Cipurse get challenge ( " _RED_("fail") " ). Card returns 0x%04x", sw
);
248 CipurseCSetRandomFromPICC(&cpc
, buf
);
251 uint8_t authparams
[16 + 16 + 6] = {0};
252 CipurseCAuthenticateHost(&cpc
, authparams
);
255 res
= CIPURSEMutualAuthenticate(keyindex
, authparams
, sizeof(authparams
), buf
, sizeof(buf
), &len
, &sw
);
256 if (res
!= 0 || sw
!= ISO7816_OK
|| len
!= 16) {
259 PrintAndLogEx(WARNING
, "Authentication ( " _RED_("fail") " ). Wrong key");
261 } else if (sw
== 0x6A88) {
263 PrintAndLogEx(WARNING
, "Authentication ( " _RED_("fail") " ). Wrong key number");
267 PrintAndLogEx(WARNING
, "Authentication ( " _RED_("fail") " ). Card returns 0x%04x", sw
);
271 CipurseCClearContext(&cipurseContext
);
275 if (CipurseCCheckCT(&cpc
, buf
)) {
277 PrintAndLogEx(SUCCESS
, "Authentication ( " _GREEN_("ok") " )");
280 CipurseCChannelSetSecurityLevels(&cpc
, CPSMACed
, CPSMACed
);
281 memcpy(&cipurseContext
, &cpc
, sizeof(CipurseContext_t
));
285 PrintAndLogEx(WARNING
, "Authentication ( " _RED_("fail") " ) card returned wrong CT");
288 CipurseCClearContext(&cipurseContext
);
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
) {
321 PrintAndLogEx(INFO
, "Manufacturer... n/a");
323 PrintAndLogEx(INFO
, "Manufacturer... %s", getTagInfo(data
)); // getTagInfo from cmfhf14a.h
326 void CIPURSEPrintInfoFile(uint8_t *data
, size_t len
) {
328 PrintAndLogEx(FAILED
, "Info file length too short");
332 PrintAndLogEx(INFO
, "--- " _CYAN_("CIPURSE Information") "---------------------");
333 PrintAndLogEx(INFO
, "Version........ " _YELLOW_("v%d.%d"), data
[0], data
[1]);
336 CIPURSEPrintPersoMode(data
[2]);
339 CIPURSEPrintProfileInfo(data
[3]);
342 CIPURSEPrintManufacturerInfo(data
[8]);
345 void CIPURSEPrintFileDescriptor(uint8_t desc
) {
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");
363 PrintAndLogEx(INFO
, "Unknown file 0x%02x", desc
);
366 void CIPURSEPrintDGIArray(uint8_t *dgi
, size_t dgilen
) {
368 PrintAndLogEx(WARNING
, "DGI too small. Length: %zu", dgilen
);
372 uint8_t *dgiptr
= dgi
;
374 while (dgilen
> reslen
+ 2) {
375 uint8_t len
= dgiptr
[2];
376 CIPURSEPrintDGI(dgiptr
, len
+ 3);
383 void CIPURSEPrintDGI(uint8_t *dgi
, size_t dgilen
) {
385 PrintAndLogEx(WARNING
, "DGI too small. Length: %zu", dgilen
);
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
);
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");
408 PrintAndLogEx(ERR
, "Key values size must be array of 20-bite record. ADF size: %d", len
);
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
, "");
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
) {
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)
481 for (int i
= 0; i
< artlen
; i
++) {
483 PrintAndLogEx(INFO
, "always: " NOLF
);
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);
491 PrintAndLogEx(NORMAL
, " " NOLF
);
493 PrintAndLogEx(NORMAL
, "");
497 void CIPURSEPrintEFFileAttr(uint8_t *attr
, size_t len
) {
498 CIPURSEPrintFileDescriptor(attr
[0]);
501 PrintAndLogEx(INFO
, "SFI.... not assigned");
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]);
510 PrintAndLogEx(INFO
, "Record num " _YELLOW_("%d") " record size " _YELLOW_("%d"), attr
[4], attr
[5]);
512 PrintAndLogEx(INFO
, "Keys assigned... %d", attr
[6]);
515 PrintAndLogEx(INFO
, "SMR entries... %02x%02x", attr
[7], attr
[8]);
516 CIPURSEPrintSMR(&attr
[7]);
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
) {
531 PrintAndLogEx(FAILED
, "Attributes length too short");
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) {
541 PrintAndLogEx(INFO
, "Type... PxSE");
543 PrintAndLogEx(INFO
, "Type... MF");
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");
560 PrintAndLogEx(INFO
, "Autoselect on PxSE select ON");
562 if ((attr
[1] & 0x01) == 0x00)
563 PrintAndLogEx(INFO
, "PxSE select returns FCPTemplate OFF");
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
);
577 if (len
>= idx
+ 2) {
578 PrintAndLogEx(INFO
, "SMR entries... %02x%02x", attr
[idx
], attr
[idx
+ 1]);
579 CIPURSEPrintSMR(&attr
[idx
]);
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
, "");
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
));
595 CIPURSEPrintKeyAttribDGI(&attr
[idx
+ i
* reclen
]);
597 CIPURSEPrintKeyAttrib(&attr
[idx
+ i
* reclen
]);
600 idx
+= keynum
* reclen
;
603 if (len
>= idx
+ 1) {
604 int xlen
= len
- idx
;
606 if (attr
[1] == 0x00 && attr
[0] != 0x3F)
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
, "");
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]);
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
);
638 if (len
>= idx
+ 2) {
639 PrintAndLogEx(INFO
, "SMR entries... %02x%02x", attr
[idx
], attr
[idx
+ 1]);
640 CIPURSEPrintSMR(&attr
[idx
]);
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
, "");
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
, "");