read file works

This commit is contained in:
merlokk 2021-06-02 19:42:04 +03:00
commit 84ada9cc14
5 changed files with 232 additions and 9 deletions

View file

@ -27,6 +27,8 @@ CipurseContext cipurseContext;
static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint16_t Le, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) { static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu, bool IncludeLe, uint16_t Le, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
uint8_t data[APDU_RES_LEN] = {0}; uint8_t data[APDU_RES_LEN] = {0};
uint8_t securedata[APDU_RES_LEN] = {0};
sAPDU secapdu;
*ResultLen = 0; *ResultLen = 0;
if (sw) *sw = 0; if (sw) *sw = 0;
@ -47,7 +49,10 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
uint16_t xle = IncludeLe ? 0x100 : 0x00; uint16_t xle = IncludeLe ? 0x100 : 0x00;
if (xle == 0x100 && Le != 0) if (xle == 0x100 && Le != 0)
xle = Le; xle = Le;
if (APDUEncodeS(&apdu, false, xle, data, &datalen)) {
CipurseCAPDUReqEncode(&cipurseContext, &apdu, &secapdu, securedata, IncludeLe, Le);
if (APDUEncodeS(&secapdu, false, xle, data, &datalen)) {
PrintAndLogEx(ERR, "APDU encoding error."); PrintAndLogEx(ERR, "APDU encoding error.");
return 201; return 201;
} }
@ -62,14 +67,18 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
if (GetAPDULogging()) if (GetAPDULogging())
PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(Result, *ResultLen)); PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(Result, *ResultLen));
if (*ResultLen < 2) { if (*ResultLen < 2) {
return 200; return 200;
} }
*ResultLen -= 2; size_t rlen = 0;
isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1]; CipurseCAPDURespDecode(&cipurseContext, Result, *ResultLen, securedata, &rlen, &isw);
if (sw) memcpy(Result, securedata, rlen);
if (ResultLen != NULL)
*ResultLen = rlen;
if (sw != NULL)
*sw = isw; *sw = isw;
if (isw != 0x9000) { if (isw != 0x9000) {
@ -105,6 +114,20 @@ int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramsle
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw); return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw);
} }
int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
uint8_t fileIdBin[] = {fileID >> 8, fileID & 0xff};
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xa4, 0x00, 0x00, 02, fileIdBin}, true, 0, Result, MaxResultLen, ResultLen, sw);
}
int CIPURSEReadFileAttributes(uint8_t *data, uint16_t *datalen) {
//CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0x82, 0x00, keyIndex, paramslen, params}, true, 0x10, Result, MaxResultLen, ResultLen, sw);
return 2;
}
int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw) {
return CIPURSEExchangeEx(false, true, (sAPDU) {0x00, 0xb0, (offset >> 8) & 0x7f, offset & 0xff, 0, NULL}, true, 0, Result, MaxResultLen, ResultLen, sw);
}
bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) { bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
uint8_t buf[APDU_RES_LEN] = {0}; uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0; size_t len = 0;
@ -149,7 +172,8 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
if (verbose) if (verbose)
PrintAndLogEx(INFO, "Authentication " _GREEN_("OK")); PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed); //CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
CipurseCChannelSetSecurityLevels(&cpc, CPSPlain, CPSPlain);
memcpy(&cipurseContext, &cpc, sizeof(CipurseContext)); memcpy(&cipurseContext, &cpc, sizeof(CipurseContext));
return true; return true;
} else { } else {

View file

@ -14,10 +14,11 @@
#include "common.h" #include "common.h"
#include "emv/apduinfo.h" #include "emv/apduinfo.h"
#include <jansson.h> #include <jansson.h>
#include "emv/apduinfo.h" // sAPDU #include "emv/apduinfo.h" // sAPDU
#define CIPURSE_DEFAULT_KEY {0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73, 0x73}
int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); int CIPURSESelect(bool ActivateField, bool LeaveFieldON, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw); int CIPURSEChallenge(uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
@ -26,9 +27,9 @@ int CIPURSEMutalAuthenticate(uint8_t keyIndex, uint8_t *params, uint8_t paramsle
int CIPURSECreateFile(uint16_t fileID, uint8_t *fileAttr); int CIPURSECreateFile(uint16_t fileID, uint8_t *fileAttr);
int CIPURSEDeleteFile(uint16_t fileID); int CIPURSEDeleteFile(uint16_t fileID);
int CIPURSESelectFile(uint16_t fileID); int CIPURSESelectFile(uint16_t fileID, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
int CIPURSEReadFileAttributes(uint8_t *data, uint16_t *datalen); int CIPURSEReadFileAttributes(uint8_t *data, uint16_t *datalen);
int CIPURSEReadBinary(uint16_t offset, uint8_t *data, uint16_t *datalen); int CIPURSEReadBinary(uint16_t offset, uint8_t *Result, size_t MaxResultLen, size_t *ResultLen, uint16_t *sw);
int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen); int CIPURSEUpdateBinary(uint16_t offset, uint8_t *data, uint16_t datalen);
bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose); bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose);

View file

@ -167,6 +167,10 @@ void CipurseCChannelSetSecurityLevels(CipurseContext *ctx, CipurseChannelSecurit
ctx->ResponseSecurity = resp; ctx->ResponseSecurity = resp;
} }
bool isCipurseCChannelSecuritySet(CipurseContext *ctx) {
return ((ctx->RequestSecurity != CPSNone) && (ctx->ResponseSecurity != CPSNone));
}
void CipurseCSetRandomFromPICC(CipurseContext *ctx, uint8_t *random) { void CipurseCSetRandomFromPICC(CipurseContext *ctx, uint8_t *random) {
if (ctx == NULL) if (ctx == NULL)
return; return;
@ -310,3 +314,88 @@ bool CipurseCCheckMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen,
CipurseCCalcMACPadded(ctx, data, datalen, xmac); CipurseCCalcMACPadded(ctx, data, datalen, xmac);
return (memcmp(mac, xmac, CIPURSE_MAC_LENGTH) == 0); return (memcmp(mac, xmac, CIPURSE_MAC_LENGTH) == 0);
} }
void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu, uint8_t *dstdatabuf, bool includeLe, uint8_t Le) {
uint8_t mac[CIPURSE_MAC_LENGTH] = {0};
memcpy(dstapdu, srcapdu, sizeof(sAPDU));
if (isCipurseCChannelSecuritySet(ctx) == false)
return;
dstapdu->CLA |= 0x04;
dstapdu->data = dstdatabuf;
dstapdu->data[0] = CipurseCGetSMI(ctx, includeLe);
dstapdu->Lc++;
memcpy(&dstdatabuf[1], srcapdu->data, srcapdu->Lc);
if (includeLe) {
dstapdu->data[dstapdu->Lc] = Le;
dstapdu->Lc++;
}
switch (ctx->RequestSecurity) {
case CPSNone:
break;
case CPSPlain:
CipurseCCalcMACPadded(ctx, (uint8_t *)dstapdu, dstapdu->Lc + 5, mac);
break;
case CPSMACed:
dstapdu->Lc += CIPURSE_MAC_LENGTH;
CipurseCCalcMACPadded(ctx, (uint8_t *)dstapdu, dstapdu->Lc + 5, mac);
memcpy(&dstdatabuf[dstapdu->Lc - CIPURSE_MAC_LENGTH], mac, CIPURSE_MAC_LENGTH);
break;
case CPSEncrypted:
break;
default:
break;
}
}
void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen, uint16_t *sw) {
if (dstdatalen != NULL)
*dstdatalen = 0;
if (sw != NULL)
*sw = 0;
if (srcdatalen < 2)
return;
srcdatalen -= 2;
uint16_t xsw = srcdata[srcdatalen] * 0x0100 + srcdata[srcdatalen + 1];
if (sw)
*sw = xsw;
if (isCipurseCChannelSecuritySet(ctx) == false) {
memcpy(dstdata, srcdata, srcdatalen);
if (dstdatalen != NULL)
*dstdatalen = srcdatalen;
return;
}
switch (ctx->RequestSecurity) {
case CPSNone:
break;
case CPSPlain:
memcpy(dstdata, srcdata, srcdatalen);
if (dstdatalen != NULL)
*dstdatalen = srcdatalen;
break;
case CPSMACed:
if (srcdatalen < CIPURSE_MAC_LENGTH)
return;
srcdatalen -= CIPURSE_MAC_LENGTH;
if (CipurseCCheckMACPadded(ctx, srcdata, srcdatalen, &srcdata[srcdatalen]) == false) {
PrintAndLogEx(WARNING, "APDU MAC is not valid!");
}
memcpy(dstdata, srcdata, srcdatalen);
if (dstdatalen != NULL)
*dstdatalen = srcdatalen;
break;
case CPSEncrypted:
break;
default:
break;
}
}

View file

@ -12,6 +12,7 @@
#define __CIPURSECRYPTO_H__ #define __CIPURSECRYPTO_H__
#include "common.h" #include "common.h"
#include "emv/apduinfo.h" // sAPDU
#define CIPURSE_KVV_LENGTH 4 #define CIPURSE_KVV_LENGTH 4
#define CIPURSE_AES_KEY_LENGTH 16 #define CIPURSE_AES_KEY_LENGTH 16
@ -61,6 +62,7 @@ void CipurseCAuthenticateHost(CipurseContext *ctx, uint8_t *authdata);
bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT); bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT);
void CipurseCChannelSetSecurityLevels(CipurseContext *ctx, CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp); void CipurseCChannelSetSecurityLevels(CipurseContext *ctx, CipurseChannelSecurityLevel req, CipurseChannelSecurityLevel resp);
bool isCipurseCChannelSecuritySet(CipurseContext *ctx);
void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen); void AddISO9797M2Padding(uint8_t *ddata, size_t *ddatalen, uint8_t *sdata, size_t sdatalen, size_t blocklen);
size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen); size_t FindISO9797M2PaddingDataLen(uint8_t *data, size_t datalen);
@ -73,4 +75,8 @@ void CipurseCChannelEncrypt(CipurseContext *ctx, uint8_t *data, size_t datalen,
void CipurseCChannelDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *plaindata, size_t *plaindatalen); void CipurseCChannelDecrypt(CipurseContext *ctx, uint8_t *data, size_t datalen, uint8_t *plaindata, size_t *plaindatalen);
void CipurseCGetKVV(uint8_t *key, uint8_t *kvv); void CipurseCGetKVV(uint8_t *key, uint8_t *kvv);
void CipurseCAPDUReqEncode(CipurseContext *ctx, sAPDU *srcapdu, sAPDU *dstapdu, uint8_t *dstdatabuf, bool includeLe, uint8_t Le);
void CipurseCAPDURespDecode(CipurseContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen, uint16_t *sw);
#endif /* __CIPURSECRYPTO_H__ */ #endif /* __CIPURSECRYPTO_H__ */

View file

@ -148,6 +148,108 @@ static int CmdHFCipurseAuth(const char *Cmd) {
return bres ? PM3_SUCCESS : PM3_ESOFT; return bres ? PM3_SUCCESS : PM3_ESOFT;
} }
static int CmdHFCipurseReadFile(const char *Cmd) {
uint8_t buf[APDU_RES_LEN] = {0};
size_t len = 0;
uint16_t sw = 0;
uint8_t key[] = CIPURSE_DEFAULT_KEY;
CLIParserContext *ctx;
CLIParserInit(&ctx, "hf cipurse read",
"Read file by file ID with key ID and key",
"hf cipurse read -f 2ff7 -> Authenticate with keyID=1 and key = 7373...7373 and read file with id 2ff7\n"
"hf cipurse auth -n 2 -k 65656565656565656565656565656565 -f 2ff7 -> Authenticate with specified key and read file\n");
void *argtable[] = {
arg_param_begin,
arg_lit0("a", "apdu", "show APDU requests and responses"),
arg_lit0("v", "verbose", "show technical data"),
arg_int0("n", "keyid", "<dec>", "key id"),
arg_str0("k", "key", "<hex>", "key for authenticate"),
arg_str0("f", "file", "<hex>", "file ID"),
arg_int0("o", "offset", "<dec>", "offset for reading data from file"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, true);
bool APDULogging = arg_get_lit(ctx, 1);
bool verbose = arg_get_lit(ctx, 2);
uint8_t keyId = arg_get_int_def(ctx, 3, 1);
uint8_t hdata[250] = {0};
int hdatalen = sizeof(hdata);
CLIGetHexWithReturn(ctx, 4, hdata, &hdatalen);
if (hdatalen && hdatalen != 16) {
PrintAndLogEx(ERR, _RED_("ERROR:") " key length for AES128 must be 16 bytes only.");
CLIParserFree(ctx);
return PM3_EINVARG;
}
if (hdatalen)
memcpy(key, hdata, CIPURSE_AES_KEY_LENGTH);
uint16_t fileId = 0x2ff7;
hdatalen = sizeof(hdata);
CLIGetHexWithReturn(ctx, 5, hdata, &hdatalen);
if (hdatalen && hdatalen != 2) {
PrintAndLogEx(ERR, _RED_("ERROR:") " file id length must be 2 bytes only.");
CLIParserFree(ctx);
return PM3_EINVARG;
}
if (hdatalen)
fileId = (hdata[0] << 8) + hdata[1];
size_t offset = arg_get_int_def(ctx, 6, 0);
SetAPDULogging(APDULogging);
CLIParserFree(ctx);
int res = CIPURSESelect(true, true, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
PrintAndLogEx(ERR, "Cipurse select " _RED_("error") ". Card returns 0x%04x.", sw);
DropField();
return PM3_ESOFT;
}
if (verbose)
PrintAndLogEx(INFO, "File id: %x offset %d key id: %d key: %s", fileId, offset, keyId, sprint_hex(key, CIPURSE_AES_KEY_LENGTH));
bool bres = CIPURSEChannelAuthenticate(keyId, key, verbose);
if (bres == false) {
if (verbose == false)
PrintAndLogEx(ERR, "Authentication " _RED_("ERROR"));
DropField();
return PM3_ESOFT;
}
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 = CIPURSEReadBinary(offset, buf, sizeof(buf), &len, &sw);
if (res != 0 || sw != 0x9000) {
if (verbose == false)
PrintAndLogEx(ERR, "File read " _RED_("ERROR") ". Card returns 0x%04x.", sw);
DropField();
return PM3_ESOFT;
}
if (len == 0)
PrintAndLogEx(INFO, "File id: %x is empty", fileId);
else
PrintAndLogEx(INFO, "File id: %x data[%d]: %s", fileId, len, sprint_hex(buf, len));
DropField();
return PM3_SUCCESS;
}
@ -169,6 +271,7 @@ static command_t CommandTable[] = {
{"help", CmdHelp, AlwaysAvailable, "This help."}, {"help", CmdHelp, AlwaysAvailable, "This help."},
{"info", CmdHFCipurseInfo, IfPm3Iso14443a, "Info about Cipurse tag."}, {"info", CmdHFCipurseInfo, IfPm3Iso14443a, "Info about Cipurse tag."},
{"auth", CmdHFCipurseAuth, IfPm3Iso14443a, "Authentication."}, {"auth", CmdHFCipurseAuth, IfPm3Iso14443a, "Authentication."},
{"read", CmdHFCipurseReadFile, IfPm3Iso14443a, "Read file."},
{NULL, NULL, 0, NULL} {NULL, NULL, 0, NULL}
}; };