diff --git a/client/src/cipurse/cipursecore.c b/client/src/cipurse/cipursecore.c index 0a068bd1c..6da5f7849 100644 --- a/client/src/cipurse/cipursecore.c +++ b/client/src/cipurse/cipursecore.c @@ -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); +} diff --git a/client/src/cipurse/cipursecore.h b/client/src/cipurse/cipursecore.h index ee002f135..3a880326a 100644 --- a/client/src/cipurse/cipursecore.h +++ b/client/src/cipurse/cipursecore.h @@ -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__ */ diff --git a/client/src/cmdhfcipurse.c b/client/src/cmdhfcipurse.c index 379b88b1e..4d11d0ef9 100644 --- a/client/src/cmdhfcipurse.c +++ b/client/src/cmdhfcipurse.c @@ -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, "", "key ID"), arg_str0("k", "key", "", "Auth key"), + arg_str0(NULL, "aid", "", "application ID (AID)"), arg_str0(NULL, "fid", "", "file ID"), arg_int0("o", "offset", "", "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, "", "key ID"), arg_str0("k", "key", "", "Auth key"), + arg_str0(NULL, "aid", "", "application ID (AID)"), arg_str0(NULL, "fid", "", "file ID"), arg_int0("o", "offset", "", "offset for reading data from file"), arg_lit0(NULL, "noauth", "read file without authentication"), arg_str0(NULL, "sreq", "", "communication reader-PICC security level"), arg_str0(NULL, "sresp", "", "communication PICC-reader security level"), arg_str0("d", "data", "", "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, "", "key ID"), arg_str0("k", "key", "", "Auth key"), + arg_lit0(NULL, "mfd", "show info about master file"), + arg_str0(NULL, "aid", "", "select application ID (AID)"), arg_str0(NULL, "fid", "", "file ID"), + arg_str0(NULL, "chfid", "", "child file ID (EF under application/master file)"), arg_lit0(NULL, "noauth", "read file attributes without authentication"), arg_str0(NULL, "sreq", "", "communication reader-PICC security level"), arg_str0(NULL, "sresp", "", "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); diff --git a/doc/cipurse.md b/doc/cipurse.md index 4edc7ad58..365b7a2fb 100644 --- a/doc/cipurse.md +++ b/doc/cipurse.md @@ -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)