Merge pull request #1579 from merlokk/cip_rdwr

Cipurse read and write commands update
This commit is contained in:
Oleg Moiseenko 2022-02-04 14:28:04 +02:00 committed by GitHub
commit 3e7d32fee5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 343 additions and 118 deletions

View file

@ -189,6 +189,7 @@ int CIPURSESelectFile(uint16_t fileid, uint8_t *result, size_t max_result_len, s
}
int CIPURSESelectMFDefaultFileEx(bool activate_field, bool leave_field_on, uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
CipurseCClearContext(&cipurseContext);
return CIPURSEExchangeEx(activate_field, leave_field_on, (sAPDU_t) {0x00, 0xa4, 0x00, 0x00, 0, NULL}, true, 0, result, max_result_len, result_len, sw);
}
int CIPURSESelectMFDefaultFile(uint8_t *result, size_t max_result_len, size_t *result_len, uint16_t *sw) {
@ -351,28 +352,177 @@ void CIPURSEPrintFileDescriptor(uint8_t desc) {
PrintAndLogEx(INFO, "Unknown file 0x%02x", desc);
}
void CIPURSEPrintDGIArray(uint8_t *dgi, size_t dgilen) {
if (dgilen < 3) {
PrintAndLogEx(WARNING, "DGI too small. Length: %zu", dgilen);
return;
}
uint8_t *dgiptr = dgi;
size_t reslen = 0;
while (dgilen > reslen + 2) {
uint8_t len = dgiptr[2];
CIPURSEPrintDGI(dgiptr, len + 3);
dgiptr += len + 3;
reslen += len + 3;
}
}
void CIPURSEPrintDGI(uint8_t *dgi, size_t dgilen) {
if (dgilen < 3) {
PrintAndLogEx(WARNING, "DGI too small. Length: %zu", dgilen);
return;
}
uint8_t len = dgi[2];
if (len + 3 != dgilen) {
PrintAndLogEx(ERR, "DGI size does not match with record size. Length of record: %zu, DGI size: %d", dgilen, len);
return;
}
// check DGI
if (dgi[0] == 0x92 && dgi[1] == 0x00) {
PrintAndLogEx(INFO, "DGI 9200 - ADF file attributes");
CIPURSEPrintFileAttrEx(&dgi[3], len, true);
} else if (dgi[0] == 0x92 && dgi[1] == 0x01) {
PrintAndLogEx(INFO, "DGI 9201 - EF file attributes");
CIPURSEPrintFileAttrEx(&dgi[3], len, true);
} else if (dgi[0] == 0xa0 && dgi[1] == 0x0f) {
PrintAndLogEx(INFO, "DGI a00f - All key values");
if (len % 20 != 0) {
PrintAndLogEx(ERR, "Key values size must be array of 20-bite record. ADF size: %d", len);
return;
}
for (int i = 0; i < len / 20; i++) {
PrintAndLogEx(INFO, "Key[%d]............ %s", i + 1, sprint_hex_inrow(&dgi[3 + i * 20 + 0], 16));
PrintAndLogEx(INFO, " Additional info.. 0x%02x", dgi[3 + i * 20 + 16]);
uint8_t kvv[CIPURSE_KVV_LENGTH] = {0};
CipurseCGetKVV(&dgi[3 + i * 20 + 0], kvv);
bool kvvvalid = (memcmp(kvv, &dgi[3 + i * 20 + 17], 3) == 0);
PrintAndLogEx(INFO, " KVV.............. %s (%s)", sprint_hex_inrow(&dgi[3 + i * 20 + 17], 3), (kvvvalid) ? _GREEN_("valid") : _RED_("invalid"));
}
PrintAndLogEx(NORMAL, "");
} else {
PrintAndLogEx(WARNING, "Unknown DGI %02x%02x", dgi[0], dgi[1]);
}
}
static void CIPURSEPrintKeySecurityAttributes(uint8_t attr) {
PrintAndLogEx(INFO, " Update right: %s", (attr & 0x01) ? "self" : "any");
PrintAndLogEx(INFO, " Change key and rights: %s", (attr & 0x02) ? "ok" : "frozen");
PrintAndLogEx(INFO, " Use as key encryption key: %s", (attr & 0x04) ? "blocked" : "ok");
PrintAndLogEx(INFO, " Key validity: %s", (attr & 0x80) ? "invalid" : "valid");
}
static void CIPURSEPrintKeyAttrib(uint8_t *attr) {
PrintAndLogEx(INFO, "--- " _CYAN_("Key Attributes") "---------------------");
PrintAndLogEx(INFO, "Additional info... 0x%02x", attr[0]);
PrintAndLogEx(INFO, "Key length........ %d", attr[1]);
PrintAndLogEx(INFO, "Algorithm ID...... 0x%02x", attr[2]);
PrintAndLogEx(INFO, "Algorithm ID...... 0x%02x (%s)", attr[2], (attr[2] == 0x09) ? "AES" : "unknown");
PrintAndLogEx(INFO, "Security attr..... 0x%02x", attr[3]);
CIPURSEPrintKeySecurityAttributes(attr[3]);
PrintAndLogEx(INFO, "KVV............... 0x%02x%02x%02x", attr[4], attr[5], attr[6]);
PrintAndLogEx(NORMAL, "");
}
void CIPURSEPrintFileAttr(uint8_t *attr, size_t len) {
static void CIPURSEPrintKeyAttribDGI(uint8_t *attr) {
PrintAndLogEx(INFO, "--- " _CYAN_("DGI Key Attributes") "---------------------");
PrintAndLogEx(INFO, "Security attr..... 0x%02x", attr[0]);
CIPURSEPrintKeySecurityAttributes(attr[0]);
PrintAndLogEx(INFO, "Key length........ %d", attr[1]);
PrintAndLogEx(INFO, "Algorithm ID...... 0x%02x (%s)", attr[2], (attr[2] == 0x09) ? "AES" : "unknown");
PrintAndLogEx(NORMAL, "");
}
const char *CIPURSEGetSMR(uint8_t smr) {
switch (smr) {
case 0x00: return "plain";
case 0x01: return "mac";
case 0x02: return "enc";
default: return "unknown";
}
return "unknown";
}
void CIPURSEPrintSMR(uint8_t *smrrec) {
PrintAndLogEx(INFO, "1. %s/%s", CIPURSEGetSMR((smrrec[0] >> 6) & 0x03), CIPURSEGetSMR((smrrec[0] >> 4) & 0x03));
PrintAndLogEx(INFO, "2. %s/%s", CIPURSEGetSMR((smrrec[0] >> 2) & 0x03), CIPURSEGetSMR((smrrec[0] >> 0) & 0x03));
PrintAndLogEx(INFO, "3. %s/%s", CIPURSEGetSMR((smrrec[1] >> 6) & 0x03), CIPURSEGetSMR((smrrec[1] >> 4) & 0x03));
PrintAndLogEx(INFO, "4. %s/%s", CIPURSEGetSMR((smrrec[1] >> 2) & 0x03), CIPURSEGetSMR((smrrec[1] >> 0) & 0x03));
}
void CIPURSEPrintART(uint8_t *artrec, size_t artlen) {
if (artlen < 1 || artlen > 9)
return;
for (int i = 0; i < artlen; i++) {
if (i == 0)
PrintAndLogEx(INFO, "always: " NOLF);
else
PrintAndLogEx(INFO, "key %d : " NOLF, i);
for (int n = 7; n >= 0; n--)
if ((artrec[i] >> n) & 0x01)
PrintAndLogEx(NORMAL, "%d " NOLF, n + 1);
else
PrintAndLogEx(NORMAL, " " NOLF);
PrintAndLogEx(NORMAL, "");
}
}
void CIPURSEPrintEFFileAttr(uint8_t *attr, size_t len) {
CIPURSEPrintFileDescriptor(attr[0]);
if (attr[1] == 0)
PrintAndLogEx(INFO, "SFI.... not assigned");
else
PrintAndLogEx(INFO, "SFI.... 0x%02x", attr[1]);
PrintAndLogEx(INFO, "File ID... 0x%02x%02x", attr[2], attr[3]);
if (attr[0] == 0x01 || attr[0] == 0x11)
PrintAndLogEx(INFO, "File size... %d", (attr[4] << 8) + attr[5]);
else
PrintAndLogEx(INFO, "Record num " _YELLOW_("%d") " record size " _YELLOW_("%d"), attr[4], attr[5]);
PrintAndLogEx(INFO, "Keys assigned... %d", attr[6]);
if (len >= 9) {
PrintAndLogEx(INFO, "SMR entries... %02x%02x", attr[7], attr[8]);
CIPURSEPrintSMR(&attr[7]);
}
if (len >= 10) {
PrintAndLogEx(INFO, "ART... %s", sprint_hex(&attr[9], len - 9));
CIPURSEPrintART(&attr[9], len - 9);
if (attr[6] + 1 != len - 9) {
PrintAndLogEx(WARNING, "ART length is wrong");
}
}
}
void CIPURSEPrintFileAttrEx(uint8_t *attr, size_t len, bool isDGI) {
if (len < 7) {
PrintAndLogEx(FAILED, "Attributes length too short");
return;
}
PrintAndLogEx(INFO, "--- " _CYAN_("File Attributes") "---------------------");
if (attr[0] == 0x38) {
if (attr[0] == 0x38 || attr[0] == 0x3F) {
PrintAndLogEx(INFO, "Type... MF, ADF");
if (attr[1] == 0x00) {
PrintAndLogEx(INFO, "Type... MF");
if (attr[0] == 0x3F)
PrintAndLogEx(INFO, "Type... PxSE");
else
PrintAndLogEx(INFO, "Type... MF");
} else {
if ((attr[1] & 0xe0) == 0x00)
PrintAndLogEx(INFO, "Type... Unknown");
@ -404,64 +554,59 @@ void CIPURSEPrintFileAttr(uint8_t *attr, size_t len) {
uint8_t keynum = attr[6];
PrintAndLogEx(INFO, "Keys assigned... %d", keynum);
if (len >= 9) {
PrintAndLogEx(INFO, "SMR entries... %02x%02x", attr[7], attr[8]);
}
int idx = 7;
if ( keynum > 0) {
if (len >= idx + 2) {
PrintAndLogEx(INFO, "SMR entries... %02x%02x", attr[idx], attr[idx + 1]);
CIPURSEPrintSMR(&attr[idx]);
}
idx += 2;
if (len >= 10 + keynum + 1) {
PrintAndLogEx(INFO, "ART... %s", sprint_hex(&attr[9], keynum + 1));
}
if (len >= idx + keynum + 1) {
PrintAndLogEx(INFO, "ART... %s", sprint_hex(&attr[idx], keynum + 1));
CIPURSEPrintART(&attr[idx], keynum + 1);
PrintAndLogEx(NORMAL, "");
}
idx += keynum + 1;
if (len >= 11 + keynum + 1 + keynum * 7) {
for (int i = 0; i < keynum; i++) {
PrintAndLogEx(INFO, "Key %d Attributes... %s", i, sprint_hex(&attr[11 + keynum + 1 + i * 7], 7));
CIPURSEPrintKeyAttrib(&attr[11 + keynum + 1 + i * 7]);
size_t reclen = (isDGI) ? 3 : 7;
if (len >= idx + keynum * reclen) {
for (int i = 0; i < keynum; i++) {
PrintAndLogEx(INFO, "Key %d Attributes... %s", i + 1, sprint_hex(&attr[idx + i * reclen], reclen));
if (isDGI)
CIPURSEPrintKeyAttribDGI(&attr[idx + i * reclen]);
else
CIPURSEPrintKeyAttrib(&attr[idx + i * reclen]);
}
}
idx += keynum * reclen;
}
// FCP
if (len >= idx + 1) {
int xlen = len - idx;
// for MF only
if (attr[1] == 0x00 && attr[0] != 0x3F)
xlen = xlen - 6;
if (xlen > 0 && xlen < 200) {
PrintAndLogEx(INFO, "TLV file control parameters... [%d] %s", xlen, sprint_hex(&attr[idx], xlen));
TLVPrintFromBuffer(&attr[idx], xlen);
PrintAndLogEx(NORMAL, "");
}
}
// MF
if (attr[1] == 0x00) {
PrintAndLogEx(INFO, "Total memory size... %d", (attr[len - 6] << 16) + (attr[len - 1] << 5) + attr[len - 4]);
// MF only
if (attr[1] == 0x00 && attr[0] != 0x3F) {
PrintAndLogEx(INFO, "Total memory size... %d", (attr[len - 6] << 16) + (attr[len - 5] << 8) + attr[len - 4]);
PrintAndLogEx(INFO, "Free memory size.... %d", (attr[len - 3] << 16) + (attr[len - 2] << 8) + attr[len - 1]);
} else {
int ptr = 11 + keynum + 1 + keynum * 7;
if (len > ptr) {
PrintAndLogEx(INFO, "TLV file control... %s", sprint_hex(&attr[ptr], len - ptr));
}
}
} else {
PrintAndLogEx(INFO, "Type... EF");
CIPURSEPrintFileDescriptor(attr[0]);
if (attr[1] == 0)
PrintAndLogEx(INFO, "SFI.... not assigned");
else
PrintAndLogEx(INFO, "SFI.... 0x%02x", attr[1]);
PrintAndLogEx(INFO, "File ID... 0x%02x%02x", attr[2], attr[3]);
if (attr[0] == 0x01 || attr[0] == 0x11)
PrintAndLogEx(INFO, "File size... %d", (attr[4] << 8) + attr[5]);
else
PrintAndLogEx(INFO, "Record num " _YELLOW_("%d") " record size " _YELLOW_("%d"), attr[4], attr[5]);
PrintAndLogEx(INFO, "Keys assigned... %d", attr[6]);
if (len >= 9) {
PrintAndLogEx(INFO, "SMR entries... %02x%02x", attr[7], attr[8]);
}
if (len >= 10) {
PrintAndLogEx(INFO, "ART... %s", sprint_hex(&attr[9], len - 9));
if (attr[6] + 1 != len - 9) {
PrintAndLogEx(WARNING, "ART length is wrong");
}
}
CIPURSEPrintEFFileAttr(attr, len);
PrintAndLogEx(NORMAL, "");
}
}
void CIPURSEPrintFileAttr(uint8_t *attr, size_t len) {
return CIPURSEPrintFileAttrEx(attr, len, false);
}

View file

@ -60,7 +60,14 @@ int CIPURSECancelTransaction(uint16_t *sw);
bool CIPURSEChannelAuthenticate(uint8_t keyindex, uint8_t *key, bool verbose);
void CIPURSECSetActChannelSecurityLevels(CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp);
const char *CIPURSEGetSMR(uint8_t smr);
void CIPURSEPrintSMR(uint8_t *smrrec);
void CIPURSEPrintART(uint8_t *artrec, size_t artlen);
void CIPURSEPrintEFFileAttr(uint8_t *attr, size_t len);
void CIPURSEPrintFileAttrEx(uint8_t *attr, size_t len, bool isDGI);
void CIPURSEPrintFileAttr(uint8_t *attr, size_t len);
void CIPURSEPrintFileDescriptor(uint8_t desc);
void CIPURSEPrintDGIArray(uint8_t *dgi, size_t dgilen);
void CIPURSEPrintDGI(uint8_t *dgi, size_t dgilen);
#endif /* __CIPURSECORE_H__ */

View file

@ -531,9 +531,10 @@ static int CmdHFCipurseAuth(const char *Cmd) {
static int CmdHFCipurseReadFile(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf cipurse read",
"Read file by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used",
"Read file in the application by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used",
"hf cipurse read --fid 2ff7 -> Authenticate with keyID 1, read file with id 2ff7\n"
"hf cipurse read -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2 and read file\n");
"hf cipurse read -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2 and read file\n"
"hf cipurse read --aid 4144204631 --fid 0102 -> read file with id 0102 from application 4144204631\n");
void *argtable[] = {
arg_param_begin,
@ -541,6 +542,7 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
arg_lit0("v", "verbose", "show technical data"),
arg_int0("n", NULL, "<dec>", "key ID"),
arg_str0("k", "key", "<hex>", "Auth key"),
arg_str0(NULL, "aid", "<hex 1..16 bytes>", "application ID (AID)"),
arg_str0(NULL, "fid", "<hex>", "file ID"),
arg_int0("o", "offset", "<dec>", "offset for reading data from file"),
arg_lit0(NULL, "noauth", "read file without authentication"),
@ -559,17 +561,20 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
CipurseChannelSecurityLevel sresp = CPSMACed;
uint8_t key[CIPURSE_AES_KEY_LENGTH] = {0};
uint8_t aid[16] = {0};
size_t aidLen = 0;
bool useAID = false;
uint16_t fileId = defaultFileId;
bool useFID = false;
int res = CLIParseCommandParameters(ctx, 4, 0, 5, 8, 9, key, NULL, NULL, NULL, &fileId, &useFID, &sreq, &sresp);
int res = CLIParseCommandParameters(ctx, 4, 5, 6, 9, 10, key, aid, &aidLen, &useAID, &fileId, &useFID, &sreq, &sresp);
if (res || useFID == false) {
CLIParserFree(ctx);
return PM3_EINVARG;
}
size_t offset = arg_get_int_def(ctx, 6, 0);
size_t offset = arg_get_int_def(ctx, 7, 0);
bool noAuth = arg_get_lit(ctx, 7);
bool noAuth = arg_get_lit(ctx, 8);
SetAPDULogging(APDULogging);
@ -579,15 +584,17 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
uint16_t sw = 0;
uint8_t buf[APDU_RES_LEN] = {0};
res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
res = CIPURSESelectAID(true, true, aid, aidLen, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x", sw);
PrintAndLogEx(ERR, "Cipurse select application " _CYAN_("%s") " ( " _RED_("error") " ). Card returns 0x%04x", sprint_hex_inrow(aid, aidLen), sw);
DropField();
return PM3_ESOFT;
}
if (verbose)
if (verbose) {
PrintAndLogEx(INFO, "Cipurse select application " _CYAN_("%s") " ( " _GREEN_("ok") " )", sprint_hex_inrow(aid, aidLen));
PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " offset " _YELLOW_("%zu") " key id " _YELLOW_("%d") " key " _YELLOW_("%s"), fileId, offset, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
}
if (noAuth == false) {
bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
@ -605,7 +612,7 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
if (verbose == false)
PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x", sw);
PrintAndLogEx(ERR, "File select ( " _RED_("error") " ). Card returns 0x%04x", sw);
DropField();
return PM3_ESOFT;
}
@ -633,9 +640,11 @@ static int CmdHFCipurseReadFile(const char *Cmd) {
static int CmdHFCipurseWriteFile(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf cipurse write",
"Write file by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used",
"hf cipurse write --fid 2ff7 -> Authenticate with keyID 1, write file with id 2ff7\n"
"hf cipurse write -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2 and write file\n");
"Write file in the application by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used",
"hf cipurse write --fid 2ff7 -d aabb -> Authenticate with keyID 1, write file with id 2ff7\n"
"hf cipurse write -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -d aabb -> Authenticate keyID 2 and write file\n"
"hf cipurse write --aid 4144204631 --fid 0102 -d aabb -> write file with id 0102 in the 4144204631 application\n"
"hf cipurse write --fid 0102 -d aabb --commit -> write file with id 0102 and perform commit after write\n");
void *argtable[] = {
arg_param_begin,
@ -643,12 +652,14 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
arg_lit0("v", "verbose", "show technical data"),
arg_int0("n", NULL, "<dec>", "key ID"),
arg_str0("k", "key", "<hex>", "Auth key"),
arg_str0(NULL, "aid", "<hex 1..16 bytes>", "application ID (AID)"),
arg_str0(NULL, "fid", "<hex>", "file ID"),
arg_int0("o", "offset", "<dec>", "offset for reading data from file"),
arg_lit0(NULL, "noauth", "read file without authentication"),
arg_str0(NULL, "sreq", "<plain|mac(default)|encode>", "communication reader-PICC security level"),
arg_str0(NULL, "sresp", "<plain|mac(default)|encode>", "communication PICC-reader security level"),
arg_str0("d", "data", "<hex>", "hex data to write to new file"),
arg_lit0(NULL, "commit", "need commit after write"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
@ -662,43 +673,49 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
uint8_t key[CIPURSE_AES_KEY_LENGTH] = {0};
uint8_t aid[16] = {0};
size_t aidLen = 0;
bool useAID = false;
uint16_t fileId = defaultFileId;
bool useFID = false;
int res = CLIParseCommandParameters(ctx, 4, 0, 5, 8, 9, key, NULL, NULL, NULL, &fileId, &useFID, &sreq, &sresp);
int res = CLIParseCommandParameters(ctx, 4, 5, 6, 9, 10, key, aid, &aidLen, &useAID, &fileId, &useFID, &sreq, &sresp);
if (res || useFID == false) {
CLIParserFree(ctx);
return PM3_EINVARG;
}
size_t offset = arg_get_int_def(ctx, 6, 0);
size_t offset = arg_get_int_def(ctx, 7, 0);
bool noAuth = arg_get_lit(ctx, 7);
bool noAuth = arg_get_lit(ctx, 8);
uint8_t hdata[250] = {0};
int hdatalen = sizeof(hdata);
CLIGetHexWithReturn(ctx, 10, hdata, &hdatalen);
CLIGetHexWithReturn(ctx, 11, hdata, &hdatalen);
if (hdatalen == 0) {
PrintAndLogEx(ERR, _RED_("ERROR:") " file content length must be more 0");
CLIParserFree(ctx);
return PM3_EINVARG;
}
SetAPDULogging(APDULogging);
bool needCommit = arg_get_lit(ctx, 12);
CLIParserFree(ctx);
SetAPDULogging(APDULogging);
size_t len = 0;
uint16_t sw = 0;
uint8_t buf[APDU_RES_LEN] = {0};
res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
res = CIPURSESelectAID(true, true, aid, aidLen, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x", sw);
PrintAndLogEx(ERR, "Cipurse select application " _CYAN_("%s") " ( " _RED_("error") " ). Card returns 0x%04x", sprint_hex_inrow(aid, aidLen), sw);
DropField();
return PM3_ESOFT;
}
if (verbose) {
PrintAndLogEx(INFO, "Cipurse select application " _CYAN_("%s") " ( " _GREEN_("ok") " )", sprint_hex_inrow(aid, aidLen));
PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " offset " _YELLOW_("%zu") " key id " _YELLOW_("%d") " key " _YELLOW_("%s")
, fileId
, offset
@ -742,6 +759,16 @@ static int CmdHFCipurseWriteFile(const char *Cmd) {
PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " successfully written", fileId);
if (needCommit) {
sw = 0;
res = CIPURSECommitTransaction(&sw);
if (res != 0 || sw != 0x9000)
PrintAndLogEx(WARNING, "Commit " _YELLOW_("ERROR") ". Card returns 0x%04x", sw);
if (verbose)
PrintAndLogEx(INFO, "Commit ( " _GREEN_("ok") " )");
}
DropField();
return PM3_SUCCESS;
}
@ -750,7 +777,10 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf cipurse aread",
"Read file attributes by file ID with key ID and key. If no key is supplied, default key of 737373...7373 will be used",
"hf cipurse aread --fid 2ff7 -> Authenticate with keyID 1, read file attributes with id 2ff7\n"
"hf cipurse aread --fid 2ff7 -> Select MF, Authenticate with keyID 1, read file attributes with id 2ff7\n"
"hf cipurse aread --mfd -> read file attributes for master file (MF)\n"
"hf cipurse aread --chfid 0102 -> read file 0102 attributes in the default application\n"
"hf cipurse aread --aid 4144204632 --chfid 0102 -> read file 0102 attributes in the 4144204632 application\n"
"hf cipurse aread -n 2 -k 65656565656565656565656565656565 --fid 2ff7 -> Authenticate keyID 2, read file attributes\n");
void *argtable[] = {
@ -759,12 +789,13 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
arg_lit0("v", "verbose", "show technical data"),
arg_int0("n", NULL, "<dec>", "key ID"),
arg_str0("k", "key", "<hex>", "Auth key"),
arg_lit0(NULL, "mfd", "show info about master file"),
arg_str0(NULL, "aid", "<hex 1..16 bytes>", "select application ID (AID)"),
arg_str0(NULL, "fid", "<hex>", "file ID"),
arg_str0(NULL, "chfid", "<hex 2 bytes>", "child file ID (EF under application/master file)"),
arg_lit0(NULL, "noauth", "read file attributes without authentication"),
arg_str0(NULL, "sreq", "<plain|mac(default)|encode>", "communication reader-PICC security level"),
arg_str0(NULL, "sresp", "<plain|mac(default)|encode>", "communication PICC-reader security level"),
arg_lit0(NULL, "sel-adf", "show info about ADF itself"),
arg_lit0(NULL, "sel-mf", "show info about master file"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
@ -772,44 +803,58 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
bool APDULogging = arg_get_lit(ctx, 1);
bool verbose = arg_get_lit(ctx, 2);
uint8_t keyId = arg_get_int_def(ctx, 3, defaultKeyId);
bool selmfd = arg_get_lit(ctx, 5);
CipurseChannelSecurityLevel sreq = CPSMACed;
CipurseChannelSecurityLevel sresp = CPSMACed;
uint8_t key[CIPURSE_AES_KEY_LENGTH] = {0};
uint8_t aid[16] = {0};
size_t aidLen = 0;
bool useAID = false;
uint16_t fileId = defaultFileId;
bool useFID = false;
int res = CLIParseCommandParameters(ctx, 4, 0, 5, 7, 8, key, NULL, NULL, NULL, &fileId, &useFID, &sreq, &sresp);
if (res || useFID == false) {
uint16_t childFileId = defaultFileId;
bool useChildFID = false;
int res = CLIParseCommandParametersEx(ctx, 4, 6, 7, 8, 10, 11, key, aid, &aidLen, &useAID, &fileId, &useFID, &childFileId, &useChildFID, &sreq, &sresp);
if (res) {
CLIParserFree(ctx);
return PM3_EINVARG;
}
bool noAuth = arg_get_lit(ctx, 6);
bool seladf = arg_get_lit(ctx, 9);
bool selmf = arg_get_lit(ctx, 10);
SetAPDULogging(APDULogging);
bool noAuth = arg_get_lit(ctx, 9);
CLIParserFree(ctx);
SetAPDULogging(APDULogging);
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
res = SelectCommandEx(selmfd, useAID, aid, aidLen, useFID, fileId, useChildFID, childFileId, verbose, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x", sw);
PrintAndLogEx(WARNING, "useaid=%d res=%d sw=%x", useAID, res, sw);
DropField();
return PM3_ESOFT;
}
if (verbose) {
PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " key id " _YELLOW_("%d") " key " _YELLOW_("%s")
, fileId
, keyId
, sprint_hex(key, CIPURSE_AES_KEY_LENGTH)
);
if (selmfd)
PrintAndLogEx(INFO, "File " _CYAN_("Master File"));
else if (useFID)
PrintAndLogEx(INFO, "File id " _CYAN_("%04x"), fileId);
else
PrintAndLogEx(INFO, "Application ID " _CYAN_("%s"), sprint_hex_inrow(aid, aidLen));
if (useChildFID)
PrintAndLogEx(INFO, "Child file id " _CYAN_("%04x"), childFileId);
if (!noAuth)
PrintAndLogEx(INFO, "Key id " _YELLOW_("%d") " key " _YELLOW_("%s")
, keyId
, sprint_hex(key, CIPURSE_AES_KEY_LENGTH)
);
}
if (noAuth == false) {
@ -825,23 +870,6 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
CIPURSECSetActChannelSecurityLevels(sreq, sresp);
}
if (seladf == false) {
if (selmf)
res = CIPURSESelectMFDefaultFile(buf, sizeof(buf), &len, &sw);
else
res = CIPURSESelectFile(fileId, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
if (verbose == false)
PrintAndLogEx(ERR, "File select " _RED_("ERROR") ". Card returns 0x%04x", sw);
DropField();
return PM3_ESOFT;
}
}
if (verbose)
PrintAndLogEx(INFO, "Select file 0x%x ( " _GREEN_("ok") " )", fileId);
res = CIPURSEReadFileAttributes(buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
if (verbose == false)
@ -851,13 +879,13 @@ static int CmdHFCipurseReadFileAttr(const char *Cmd) {
}
if (len == 0) {
PrintAndLogEx(WARNING, "File id " _YELLOW_("%x") " attributes is empty", fileId);
PrintAndLogEx(WARNING, "File attributes is empty");
DropField();
return PM3_SUCCESS;
}
if (verbose)
PrintAndLogEx(INFO, "File id " _YELLOW_("%x") " attributes[%zu]: %s", fileId, len, sprint_hex(buf, len));
PrintAndLogEx(INFO, "Attributes raw data [%zu]: %s", len, sprint_hex(buf, len));
CIPURSEPrintFileAttr(buf, len);
@ -1014,17 +1042,8 @@ static int CmdHFCipurseCreateDGI(const char *Cmd) {
CLIParserFree(ctx);
SetAPDULogging(APDULogging);
if (verbose && hdatalen > 3) {
if (hdata[0] == 0x92 && hdata[1] == 0x00)
PrintAndLogEx(INFO, "DGI 9200 - ADF file attributes");
if (hdata[0] == 0x92 && hdata[1] == 0x01) {
PrintAndLogEx(INFO, "DGI 9201 - EF file attributes");
PrintAndLogEx(INFO, "File type:");
CIPURSEPrintFileDescriptor(hdata[3]);
}
if (hdata[0] == 0xa0 && hdata[1] == 0x0f)
PrintAndLogEx(INFO, "DGI a00f - All key values");
}
if (verbose && hdatalen > 3)
CIPURSEPrintDGIArray(hdata, hdatalen);
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
@ -1280,6 +1299,7 @@ static int CmdHFCipurseDefault(const char *Cmd) {
memcpy(defaultKey, ckey, CIPURSE_AES_KEY_LENGTH);
uint8_t aid[CIPURSE_MAX_AID_LENGTH] = CIPURSE_DEFAULT_AID;
memcpy(defaultAID, aid, CIPURSE_MAX_AID_LENGTH);
defaultAIDLength = 5;
}
defaultKeyId = arg_get_int_def(ctx, 2, defaultKeyId);

View file

@ -120,6 +120,59 @@ select it with display output in raw and tlv views options
```hf cipurse delete --aid 4144204631 --chfid 0102```
### How read file
^[Top](#top)
with default key and aid
```hf cipurse read --fid 0102```
with default key and specified aid
```hf cipurse read --aid a0000005070100```
with default key and aid without authentication
```hf cipurse read --fid 0102 --no-auth```
### How write file
^[Top](#top)
with default key and aid
```hf cipurse read --fid 0102 -d abbbccdd```
with default key and specified aid
```hf cipurse read --aid a0000005070100 -d abbbccdd```
with default key and aid without authentication
```hf cipurse read --fid 0102 -d abbbccdd --no-auth```
with default key and aid, perform commit (works for files with transactions mechanism switched on)
```hf cipurse read --fid 0102 -d abbbccdd --commit```
### How read file attributes
^[Top](#top)
read master file attributes
```hf cipurse aread --mfd```
read EF.ID_INFO root file attributes
```hf cipurse aread --fid 2ff7```
read PxSE application attributes
```hf cipurse aread --aid a0000005070100```
read application attributes
```hf cipurse aread --aid 4144204632```
read file (EF) attributes
```hf cipurse aread --aid 4144204632 --chfid 0102```
or with default application
```hf cipurse aread --aid 4144204632 --chfid 0102```
### How to personalize card
^[Top](#top)