Merge pull request #1407 from merlokk/esf_isoauth

Desfire iso authentication
This commit is contained in:
Oleg Moiseenko 2021-07-31 12:57:35 +03:00 committed by GitHub
commit d490485920
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 284 additions and 16 deletions

View file

@ -3053,6 +3053,7 @@ static int CmdHF14ADesSelectApp(const char *Cmd) {
arg_str0("c", "ccset", "<native/niso/iso>", "Communicaton command set: native/niso/iso"),
arg_str0("s", "schann", "<d40/ev1/ev2>", "Secure channel: d40/ev1/ev2"),
arg_str0(NULL, "aid", "<app id hex>", "Application ID of application for some parameters (3 hex bytes, big endian)"),
arg_str0(NULL, "dfname", "<df name str>", "Application DF Name (string, max 16 chars). Selects application via ISO SELECT command"),
arg_param_end
};
CLIExecWithReturn(ctx, Cmd, argtable, false);
@ -3068,19 +3069,45 @@ static int CmdHF14ADesSelectApp(const char *Cmd) {
CLIParserFree(ctx);
return res;
}
uint8_t dfname[32] = {0};
int dfnamelen = 16;
CLIGetStrWithReturn(ctx, 12, dfname, &dfnamelen);
SetAPDULogging(APDULogging);
CLIParserFree(ctx);
res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, true, verbose);
if (res != PM3_SUCCESS) {
DropField();
PrintAndLogEx(FAILED, "Select application 0x%06x " _RED_("failed") " ", appid);
return res;
if (dctx.cmdSet == DCCISO || dfnamelen > 0) {
uint8_t resp[250] = {0};
size_t resplen = 0;
if (dfnamelen > 0)
res = DesfireISOSelectDF(&dctx, (char *)dfname, resp, &resplen);
else
res = DesfireISOSelect(&dctx, ISSMFDFEF, NULL, 0, resp, &resplen);
if (res != PM3_SUCCESS) {
DropField();
PrintAndLogEx(FAILED, "ISO Select application `%s` " _RED_("failed"), (char *)dfname);
return res;
}
if (resplen > 0)
PrintAndLogEx(FAILED, "Application " _CYAN_("FCI template") " [%zu]%s", resplen, sprint_hex(resp, resplen));
if (dfnamelen > 0)
PrintAndLogEx(SUCCESS, "Application `%s` selected " _GREEN_("succesfully"), (char *)dfname);
else
PrintAndLogEx(SUCCESS, "PICC MF selected " _GREEN_("succesfully"));
} else {
res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, true, verbose);
if (res != PM3_SUCCESS) {
DropField();
PrintAndLogEx(FAILED, "Select application 0x%06x " _RED_("failed") " ", appid);
return res;
}
PrintAndLogEx(SUCCESS, "Application 0x%06x selected " _GREEN_("succesfully") " ", appid);
}
PrintAndLogEx(SUCCESS, "Application 0x%06x selected " _GREEN_("succesfully") " ", appid);
DropField();
return res;
}
@ -6327,12 +6354,9 @@ int CmdHFMFDes(const char *Cmd) {
ISO/IEC 7816 Cmds
-----------------
'A4' Select
'B0' Read Binary
'D6' Update Binary
'B2' Read Records
'E2' Append Records
'84' Get Challenge
'88' Internal Authenticate
'82' External Authenticate
*/

View file

@ -19,6 +19,8 @@
#define APDUCODE_TYPE_ERROR 3
#define APDUCODE_TYPE_SECURITY 4
#define APDU_INCLUDE_LE_00 0x100
typedef struct {
const char *ID;
const uint8_t Type;

View file

@ -261,6 +261,22 @@ const char *DesfireAuthErrorToStr(int error) {
return "Can't select application.";
case 201:
return "Authentication retured no error but channel not authenticated.";
case 301:
return "ISO Get challenge error.";
case 302:
return "ISO Get challenge returned wrong length.";
case 303:
return "Crypto encode piccrnd1 error.";
case 304:
return "External authenticate error.";
case 305:
return "Internal authenticate error.";
case 306:
return "Internal authenticate returned wrong length.";
case 307:
return "Crypto decode piccrnd2 error.";
case 308:
return "Random numbers dont match. Authentication failed.";
default:
break;
}
@ -277,6 +293,34 @@ void DesfireAIDUintToByte(uint32_t aid, uint8_t *data) {
data[2] = (aid >> 16) & 0xff;
}
static uint8_t DesfireKeyToISOKey(DesfireCryptoAlgorythm keytype) {
switch (keytype) {
case T_DES:
return 0x02;
case T_3DES:
return 0x02;
case T_3K3DES:
return 0x04;
case T_AES:
return 0x09;
}
return 0x00;
}
static uint8_t DesfireGetRndLenForKey(DesfireCryptoAlgorythm keytype) {
switch (keytype) {
case T_DES:
return 0x08;
case T_3DES:
return 0x08;
case T_3K3DES:
return 0x10;
case T_AES:
return 0x10;
}
return 0x00;
}
void DesfirePrintContext(DesfireContext *ctx) {
PrintAndLogEx(INFO, "Key num: %d Key algo: %s Key[%d]: %s",
ctx->keyNum,
@ -311,7 +355,7 @@ void DesfirePrintContext(DesfireContext *ctx) {
}
}
static int DESFIRESendApdu(bool activate_field, sAPDU apdu, uint8_t *result, uint32_t max_result_len, uint32_t *result_len, uint16_t *sw) {
static int DESFIRESendApduEx(bool activate_field, sAPDU apdu, uint16_t le, uint8_t *result, uint32_t max_result_len, uint32_t *result_len, uint16_t *sw) {
if (result_len) *result_len = 0;
if (sw) *sw = 0;
@ -327,7 +371,7 @@ static int DESFIRESendApdu(bool activate_field, sAPDU apdu, uint8_t *result, uin
// COMPUTE APDU
int datalen = 0;
if (APDUEncodeS(&apdu, false, 0x100, data, &datalen)) { // 100 == with Le
if (APDUEncodeS(&apdu, false, le, data, &datalen)) { // 100 == with Le
PrintAndLogEx(ERR, "APDU encoding error.");
return PM3_EAPDU_ENCODEFAIL;
}
@ -370,6 +414,10 @@ static int DESFIRESendApdu(bool activate_field, sAPDU apdu, uint8_t *result, uin
return PM3_SUCCESS;
}
static int DESFIRESendApdu(bool activate_field, sAPDU apdu, uint8_t *result, uint32_t max_result_len, uint32_t *result_len, uint16_t *sw) {
return DESFIRESendApduEx(activate_field, apdu, APDU_INCLUDE_LE_00, result, max_result_len, result_len, sw);
}
static int DESFIRESendRaw(bool activate_field, uint8_t *data, size_t datalen, uint8_t *result, uint32_t max_result_len, uint32_t *result_len, uint8_t *respcode) {
*result_len = 0;
if (respcode) *respcode = 0xff;
@ -516,7 +564,7 @@ static int DesfireExchangeNative(bool activate_field, DesfireContext *ctx, uint8
return PM3_SUCCESS;
}
static int DesfireExchangeISO(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen, bool enable_chaining, size_t splitbysize) {
static int DesfireExchangeISONative(bool activate_field, DesfireContext *ctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *respcode, uint8_t *resp, size_t *resplen, bool enable_chaining, size_t splitbysize) {
if (resplen)
*resplen = 0;
if (respcode)
@ -622,6 +670,16 @@ static int DesfireExchangeISO(bool activate_field, DesfireContext *ctx, uint8_t
return PM3_SUCCESS;
}
static int DesfireExchangeISO(bool activate_field, DesfireContext *ctx, sAPDU apdu, uint16_t le, uint8_t *resp, size_t *resplen, uint16_t *sw) {
uint32_t rlen = 0;
int res = DESFIRESendApduEx(activate_field, apdu, le, resp, 255, &rlen, sw);
if (res == PM3_SUCCESS)
*resplen = rlen;
return res;
}
// move data from blockdata [format: <length, data><length, data>...] to single data block
static void DesfireJoinBlockToBytes(uint8_t *blockdata, size_t blockdatacount, size_t blockdatasize, uint8_t *dstdata, size_t *dstdatalen) {
*dstdatalen = 0;
@ -665,7 +723,7 @@ int DesfireExchangeEx(bool activate_field, DesfireContext *ctx, uint8_t cmd, uin
if (ctx->cmdSet == DCCNative)
res = DesfireExchangeNative(activate_field, ctx, cmd, databuf, databuflen, respcode, databuf, &databuflen, enable_chaining, splitbysize);
else
res = DesfireExchangeISO(activate_field, ctx, cmd, databuf, databuflen, respcode, databuf, &databuflen, enable_chaining, splitbysize);
res = DesfireExchangeISONative(activate_field, ctx, cmd, databuf, databuflen, respcode, databuf, &databuflen, enable_chaining, splitbysize);
if (splitbysize) {
uint8_t sdata[250 * 5] = {0};
@ -715,8 +773,12 @@ int DesfireSelectAID(DesfireContext *ctx, uint8_t *aid1, uint8_t *aid2) {
if (respcode != MFDES_S_OPERATION_OK)
return PM3_EAPDU_FAIL;
DesfireClearSession(ctx);
ctx->appSelected = (aid1[0] != 0x00 || aid1[1] != 0x00 || aid1[2] != 0x00);
return PM3_SUCCESS;
}
return res;
}
@ -755,6 +817,14 @@ int DesfireSelectAIDHexNoFieldOn(DesfireContext *ctx, uint32_t aid) {
int DesfireSelectAndAuthenticateEx(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool noauth, bool verbose) {
if (verbose)
DesfirePrintContext(dctx);
bool isosw = false;
if (dctx->cmdSet == DCCISO) {
dctx->cmdSet = DCCNativeISO;
isosw = true;
if (verbose)
PrintAndLogEx(INFO, "Switch to " _CYAN_("native") " for select");
}
int res = DesfireSelectAIDHex(dctx, aid, false, 0);
if (res != PM3_SUCCESS) {
@ -763,6 +833,9 @@ int DesfireSelectAndAuthenticateEx(DesfireContext *dctx, DesfireSecureChannel se
}
if (verbose)
PrintAndLogEx(INFO, "App %06x " _GREEN_("selected"), aid);
if (isosw)
dctx->cmdSet = DCCISO;
if (!noauth) {
res = DesfireAuthenticate(dctx, secureChannel, verbose);
@ -1201,7 +1274,67 @@ static int DesfireAuthenticateEV2(DesfireContext *dctx, DesfireSecureChannel sec
return PM3_SUCCESS;
}
static int DesfireAuthenticateISO(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose) {
uint8_t rndlen = DesfireGetRndLenForKey(dctx->keyType);
uint8_t hostrnd[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16};
uint8_t hostrnd2[] = {0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
uint8_t piccrnd[64];
size_t xlen = 0;
int res = DesfireISOGetChallenge(dctx, dctx->keyType, piccrnd, &xlen);
if (res != PM3_SUCCESS)
return 301;
if (xlen != rndlen)
return 302;
uint8_t both[32] = {0};
memcpy(both, hostrnd, rndlen);
memcpy(&both[rndlen], piccrnd, rndlen);
// encode
DesfireClearIV(dctx);
DesfireCryptoEncDec(dctx, false, both, rndlen * 2, both, true); // error 303
// external authenticate
res = DesfireISOExternalAuth(dctx, dctx->appSelected, dctx->keyNum, dctx->keyType, both);
if (res != PM3_SUCCESS)
return 304;
// internal authenticate
uint8_t rnddata[64] = {0};
xlen = 0;
res = DesfireISOInternalAuth(dctx, dctx->appSelected, dctx->keyNum, dctx->keyType, hostrnd2, rnddata, &xlen);
if (res != PM3_SUCCESS)
return 305;
if (xlen != rndlen * 2)
return 306;
// decode rnddata
uint8_t piccrnd2[64] = {0};
DesfireCryptoEncDec(dctx, false, rnddata, rndlen * 2, piccrnd2, false); // error 307
// check
if (memcmp(hostrnd2, &piccrnd2[rndlen], rndlen) != 0)
return 308;
DesfireGenSessionKeyEV1(hostrnd, piccrnd2, dctx->keyType, dctx->sessionKeyEnc);
DesfireClearIV(dctx);
memcpy(dctx->sessionKeyMAC, dctx->sessionKeyEnc, desfire_get_key_length(dctx->keyType));
dctx->secureChannel = secureChannel;
if (verbose)
PrintAndLogEx(INFO, "session key: %s", sprint_hex(dctx->sessionKeyEnc, desfire_get_key_length(dctx->keyType)));
return PM3_SUCCESS;
}
int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose) {
if (dctx->cmdSet == DCCISO && secureChannel != DACEV2)
return DesfireAuthenticateISO(dctx, secureChannel, verbose);
if (secureChannel == DACd40 || secureChannel == DACEV1)
return DesfireAuthenticateEV1(dctx, secureChannel, verbose);
@ -1382,7 +1515,6 @@ int DesfireFillFileList(DesfireContext *dctx, FileListS FileList, size_t *filesc
return res;
}
int DesfireCreateFile(DesfireContext *dctx, uint8_t ftype, uint8_t *fdata, size_t fdatalen, bool checklen) {
const DesfireCreateFileCommandsS *rcmd = GetDesfireFileCmdRec(ftype);
if (rcmd == NULL)
@ -2087,3 +2219,64 @@ int DesfireSetConfiguration(DesfireContext *dctx, uint8_t paramid, uint8_t *para
return res;
}
int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen) {
uint8_t xresp[250] = {0};
size_t xresplen = 0;
uint16_t sw = 0;
int res = DesfireExchangeISO(true, dctx, (sAPDU) {0x00, ISO7816_SELECT_FILE, cntr, ((resp == NULL) ? 0x0C : 0x00), datalen, data}, APDU_INCLUDE_LE_00, xresp, &xresplen, &sw);
if (res == PM3_SUCCESS && sw != 0x9000)
return PM3_ESOFT;
if (resp != NULL && resplen != NULL) {
*resplen = xresplen;
memcpy(resp, xresp, xresplen);
}
DesfireClearSession(dctx);
dctx->appSelected = !( (cntr == ISSMFDFEF && datalen == 0) || (cntr == ISSEFByFileID && datalen == 2 && data[0] == 0 && data[1] == 0) );
return res;
}
int DesfireISOSelectDF(DesfireContext *dctx, char *dfname, uint8_t *resp, size_t *resplen) {
return DesfireISOSelect(dctx, ISSDFName, (uint8_t *)dfname, strnlen(dfname, 16), resp, resplen);
}
int DesfireISOGetChallenge(DesfireContext *dctx, DesfireCryptoAlgorythm keytype, uint8_t *resp, size_t *resplen) {
uint16_t sw = 0;
int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_GET_CHALLENGE, 0x00, 0x00, 0x00, NULL}, DesfireGetRndLenForKey(keytype), resp, resplen, &sw);
if (res == PM3_SUCCESS && sw != 0x9000)
return PM3_ESOFT;
return res;
}
int DesfireISOExternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, DesfireCryptoAlgorythm keytype, uint8_t *data) {
uint8_t p1 = DesfireKeyToISOKey(keytype);
uint8_t p2 = ((app_level) ? 0x80 : 0x00) | keynum;
uint8_t resp[250] = {0};
size_t resplen = 0;
uint16_t sw = 0;
int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_EXTERNAL_AUTHENTICATION, p1, p2, DesfireGetRndLenForKey(keytype) * 2, data}, 0, resp, &resplen, &sw);
if (res == PM3_SUCCESS && sw != 0x9000)
return PM3_ESOFT;
return res;
}
int DesfireISOInternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, DesfireCryptoAlgorythm keytype, uint8_t *data, uint8_t *resp, size_t *resplen) {
uint8_t keylen = DesfireGetRndLenForKey(keytype);
uint8_t p1 = DesfireKeyToISOKey(keytype);
uint8_t p2 = ((app_level) ? 0x80 : 0x00) | keynum;
uint16_t sw = 0;
int res = DesfireExchangeISO(false, dctx, (sAPDU) {0x00, ISO7816_INTERNAL_AUTHENTICATION, p1, p2, keylen, data}, keylen * 2, resp, resplen, &sw);
if (res == PM3_SUCCESS && sw != 0x9000)
return PM3_ESOFT;
return res;
}

View file

@ -21,6 +21,15 @@
#define DESFIRE_TX_FRAME_MAX_LEN 54
enum DesfireISOSelectControlEnum {
ISSMFDFEF = 0x00,
ISSChildDF = 0x01,
ISSEFByFileID = 0x02,
ISSParentDF = 0x03,
ISSDFName = 0x04
};
typedef enum DesfireISOSelectControlEnum DesfireISOSelectControl;
typedef struct {
const uint8_t id;
const char *text;
@ -167,4 +176,10 @@ int DesfireReadRecords(DesfireContext *dctx, uint8_t fnum, uint32_t recnum, uint
int DesfireWriteRecord(DesfireContext *dctx, uint8_t fnum, uint32_t offset, uint32_t len, uint8_t *data);
int DesfireUpdateRecord(DesfireContext *dctx, uint8_t fnum, uint32_t recnum, uint32_t offset, uint32_t len, uint8_t *data);
int DesfireISOSelectDF(DesfireContext *dctx, char *dfname, uint8_t *resp, size_t *resplen);
int DesfireISOSelect(DesfireContext *dctx, DesfireISOSelectControl cntr, uint8_t *data, uint8_t datalen, uint8_t *resp, size_t *resplen);
int DesfireISOGetChallenge(DesfireContext *dctx, DesfireCryptoAlgorythm keytype, uint8_t *resp, size_t *resplen);
int DesfireISOExternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, DesfireCryptoAlgorythm keytype, uint8_t *data);
int DesfireISOInternalAuth(DesfireContext *dctx, bool app_level, uint8_t keynum, DesfireCryptoAlgorythm keytype, uint8_t *data, uint8_t *resp, size_t *resplen);
#endif // __DESFIRECORE_H

View file

@ -41,6 +41,8 @@ void DesfireClearContext(DesfireContext *ctx) {
ctx->secureChannel = DACNone;
ctx->cmdSet = DCCNative;
ctx->commMode = DCMNone;
ctx->appSelected = false;
ctx->kdfAlgo = 0;
ctx->kdfInputLen = 0;
@ -389,6 +391,35 @@ uint8_t DesfireCommModeToFileCommMode(DesfireCommunicationMode comm_mode) {
return fmode;
}
void DesfireGenSessionKeyEV1(const uint8_t rnda[], const uint8_t rndb[], DesfireCryptoAlgorythm keytype, uint8_t *key) {
switch (keytype) {
case T_DES:
memcpy(key, rnda, 4);
memcpy(key + 4, rndb, 4);
break;
case T_3DES:
memcpy(key, rnda, 4);
memcpy(key + 4, rndb, 4);
memcpy(key + 8, rnda + 4, 4);
memcpy(key + 12, rndb + 4, 4);
break;
case T_3K3DES:
memcpy(key, rnda, 4);
memcpy(key + 4, rndb, 4);
memcpy(key + 8, rnda + 6, 4);
memcpy(key + 12, rndb + 6, 4);
memcpy(key + 16, rnda + 12, 4);
memcpy(key + 20, rndb + 12, 4);
break;
case T_AES:
memcpy(key, rnda, 4);
memcpy(key + 4, rndb, 4);
memcpy(key + 8, rnda + 12, 4);
memcpy(key + 12, rndb + 12, 4);
break;
}
}
// https://www.nxp.com/docs/en/application-note/AN12343.pdf
// page 35
void DesfireGenSessionKeyEV2(uint8_t *key, uint8_t *rndA, uint8_t *rndB, bool enckey, uint8_t *sessionkey) {

View file

@ -75,6 +75,8 @@ typedef struct DesfireContextS {
DesfireSecureChannel secureChannel; // none/d40/ev1/ev2
DesfireCommandSet cmdSet; // native/nativeiso/iso
DesfireCommunicationMode commMode; // plain/mac/enc
bool appSelected; // for iso auth
uint8_t IV[DESFIRE_MAX_KEY_SIZE];
uint8_t sessionKeyMAC[DESFIRE_MAX_KEY_SIZE];
@ -108,6 +110,7 @@ uint8_t DesfireDESKeyGetVersion(uint8_t *key);
DesfireCommunicationMode DesfireFileCommModeToCommMode(uint8_t file_comm_mode);
uint8_t DesfireCommModeToFileCommMode(DesfireCommunicationMode comm_mode);
void DesfireGenSessionKeyEV1(const uint8_t rnda[], const uint8_t rndb[], DesfireCryptoAlgorythm keytype, uint8_t *key);
void DesfireGenSessionKeyEV2(uint8_t *key, uint8_t *rndA, uint8_t *rndB, bool enckey, uint8_t *sessionkey);
void DesfireEV2FillIV(DesfireContext *ctx, bool ivforcommand, uint8_t *iv);