mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-23 22:55:37 -07:00
read file works
This commit is contained in:
parent
99c4a3a9c0
commit
84ada9cc14
5 changed files with 232 additions and 9 deletions
|
@ -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) {
|
||||
uint8_t data[APDU_RES_LEN] = {0};
|
||||
uint8_t securedata[APDU_RES_LEN] = {0};
|
||||
sAPDU secapdu;
|
||||
|
||||
*ResultLen = 0;
|
||||
if (sw) *sw = 0;
|
||||
|
@ -47,7 +49,10 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
|
|||
uint16_t xle = IncludeLe ? 0x100 : 0x00;
|
||||
if (xle == 0x100 && Le != 0)
|
||||
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.");
|
||||
return 201;
|
||||
}
|
||||
|
@ -62,14 +67,18 @@ static int CIPURSEExchangeEx(bool ActivateField, bool LeaveFieldON, sAPDU apdu,
|
|||
|
||||
if (GetAPDULogging())
|
||||
PrintAndLogEx(SUCCESS, "<<<< %s", sprint_hex(Result, *ResultLen));
|
||||
|
||||
|
||||
if (*ResultLen < 2) {
|
||||
return 200;
|
||||
}
|
||||
|
||||
*ResultLen -= 2;
|
||||
isw = Result[*ResultLen] * 0x0100 + Result[*ResultLen + 1];
|
||||
if (sw)
|
||||
size_t rlen = 0;
|
||||
CipurseCAPDURespDecode(&cipurseContext, Result, *ResultLen, securedata, &rlen, &isw);
|
||||
memcpy(Result, securedata, rlen);
|
||||
if (ResultLen != NULL)
|
||||
*ResultLen = rlen;
|
||||
|
||||
if (sw != NULL)
|
||||
*sw = isw;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
uint8_t buf[APDU_RES_LEN] = {0};
|
||||
size_t len = 0;
|
||||
|
@ -149,7 +172,8 @@ bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose) {
|
|||
if (verbose)
|
||||
PrintAndLogEx(INFO, "Authentication " _GREEN_("OK"));
|
||||
|
||||
CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
|
||||
//CipurseCChannelSetSecurityLevels(&cpc, CPSMACed, CPSMACed);
|
||||
CipurseCChannelSetSecurityLevels(&cpc, CPSPlain, CPSPlain);
|
||||
memcpy(&cipurseContext, &cpc, sizeof(CipurseContext));
|
||||
return true;
|
||||
} else {
|
||||
|
|
|
@ -14,10 +14,11 @@
|
|||
#include "common.h"
|
||||
#include "emv/apduinfo.h"
|
||||
|
||||
|
||||
#include <jansson.h>
|
||||
#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 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 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 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);
|
||||
|
||||
bool CIPURSEChannelAuthenticate(uint8_t keyIndex, uint8_t *key, bool verbose);
|
||||
|
|
|
@ -167,6 +167,10 @@ void CipurseCChannelSetSecurityLevels(CipurseContext *ctx, CipurseChannelSecurit
|
|||
ctx->ResponseSecurity = resp;
|
||||
}
|
||||
|
||||
bool isCipurseCChannelSecuritySet(CipurseContext *ctx) {
|
||||
return ((ctx->RequestSecurity != CPSNone) && (ctx->ResponseSecurity != CPSNone));
|
||||
}
|
||||
|
||||
void CipurseCSetRandomFromPICC(CipurseContext *ctx, uint8_t *random) {
|
||||
if (ctx == NULL)
|
||||
return;
|
||||
|
@ -310,3 +314,88 @@ bool CipurseCCheckMACPadded(CipurseContext *ctx, uint8_t *data, size_t datalen,
|
|||
CipurseCCalcMACPadded(ctx, data, datalen, xmac);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#define __CIPURSECRYPTO_H__
|
||||
|
||||
#include "common.h"
|
||||
#include "emv/apduinfo.h" // sAPDU
|
||||
|
||||
#define CIPURSE_KVV_LENGTH 4
|
||||
#define CIPURSE_AES_KEY_LENGTH 16
|
||||
|
@ -61,6 +62,7 @@ void CipurseCAuthenticateHost(CipurseContext *ctx, uint8_t *authdata);
|
|||
bool CipurseCCheckCT(CipurseContext *ctx, uint8_t *CT);
|
||||
|
||||
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);
|
||||
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 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__ */
|
||||
|
|
|
@ -148,6 +148,108 @@ static int CmdHFCipurseAuth(const char *Cmd) {
|
|||
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."},
|
||||
{"info", CmdHFCipurseInfo, IfPm3Iso14443a, "Info about Cipurse tag."},
|
||||
{"auth", CmdHFCipurseAuth, IfPm3Iso14443a, "Authentication."},
|
||||
{"read", CmdHFCipurseReadFile, IfPm3Iso14443a, "Read file."},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue