mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2025-08-22 06:13:51 -07:00
commit
0e07e86e45
7 changed files with 346 additions and 62 deletions
|
@ -916,46 +916,6 @@ static int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rp
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*static void AuthToError(int error) {
|
|
||||||
switch (error) {
|
|
||||||
case 1:
|
|
||||||
PrintAndLogEx(SUCCESS, "Sending auth command failed");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
PrintAndLogEx(ERR, "Authentication failed. No data received");
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
PrintAndLogEx(ERR, "Authentication failed. Invalid key number.");
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
PrintAndLogEx(ERR, "Authentication failed. Length of answer doesn't match algo length");
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
PrintAndLogEx(ERR, "mbedtls_aes_setkey_dec failed");
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
PrintAndLogEx(ERR, "mbedtls_aes_setkey_enc failed");
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
PrintAndLogEx(SUCCESS, "Sending auth command failed");
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
PrintAndLogEx(ERR, "Authentication failed. Card timeout.");
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
PrintAndLogEx(ERR, "Authentication failed.");
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
PrintAndLogEx(ERR, "mbedtls_aes_setkey_dec failed");
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
PrintAndLogEx(ERR, "Authentication failed. Cannot verify Session Key.");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
// -- test if card supports 0x0A
|
// -- test if card supports 0x0A
|
||||||
static int test_desfire_authenticate(void) {
|
static int test_desfire_authenticate(void) {
|
||||||
uint8_t data[] = {0x00};
|
uint8_t data[] = {0x00};
|
||||||
|
@ -3260,7 +3220,7 @@ static int CmdHF14ADesAuth(const char *Cmd) {
|
||||||
res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, false, verbose);
|
res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, false, verbose);
|
||||||
if (res != PM3_SUCCESS) {
|
if (res != PM3_SUCCESS) {
|
||||||
DropField();
|
DropField();
|
||||||
PrintAndLogEx(FAILED, "Select or authentication 0x%06x " _RED_("failed") " ", appid);
|
PrintAndLogEx(FAILED, "Select or authentication 0x%06x " _RED_("failed") ". Result [%d] %s", appid, res, DesfireAuthErrorToStr(res));
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -231,6 +231,42 @@ const char *DesfireGetErrorString(int res, uint16_t *sw) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *DesfireAuthErrorToStr(int error) {
|
||||||
|
switch (error) {
|
||||||
|
case 1:
|
||||||
|
return "Sending auth command failed";
|
||||||
|
case 2:
|
||||||
|
return "Authentication failed. No data received";
|
||||||
|
case 3:
|
||||||
|
return "Authentication failed. Invalid key number.";
|
||||||
|
case 4:
|
||||||
|
return "Authentication failed. Length of answer doesn't match algo length";
|
||||||
|
case 5:
|
||||||
|
return "mbedtls_aes_setkey_dec failed";
|
||||||
|
case 6:
|
||||||
|
return "mbedtls_aes_setkey_enc failed";
|
||||||
|
case 7:
|
||||||
|
return "Sending auth command failed";
|
||||||
|
case 8:
|
||||||
|
return "Authentication failed. Card timeout.";
|
||||||
|
case 9:
|
||||||
|
return "Authentication failed.";
|
||||||
|
case 10:
|
||||||
|
return "mbedtls_aes_setkey_dec failed";
|
||||||
|
case 11:
|
||||||
|
return "Authentication failed. Cannot verify Session Key.";
|
||||||
|
case 100:
|
||||||
|
return "Can't find auth method for provided channel parameters.";
|
||||||
|
case 200:
|
||||||
|
return "Can't select application.";
|
||||||
|
case 201:
|
||||||
|
return "Authentication retured no error but channel not authenticated.";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t DesfireAIDByteToUint(uint8_t *data) {
|
uint32_t DesfireAIDByteToUint(uint8_t *data) {
|
||||||
return data[0] + (data[1] << 8) + (data[2] << 16);
|
return data[0] + (data[1] << 8) + (data[2] << 16);
|
||||||
}
|
}
|
||||||
|
@ -266,6 +302,11 @@ void DesfirePrintContext(DesfireContext *ctx) {
|
||||||
PrintAndLogEx(INFO, " IV [%zu]: %s",
|
PrintAndLogEx(INFO, " IV [%zu]: %s",
|
||||||
desfire_get_key_block_length(ctx->keyType),
|
desfire_get_key_block_length(ctx->keyType),
|
||||||
sprint_hex(ctx->IV, desfire_get_key_block_length(ctx->keyType)));
|
sprint_hex(ctx->IV, desfire_get_key_block_length(ctx->keyType)));
|
||||||
|
if (ctx->secureChannel == DACEV2) {
|
||||||
|
PrintAndLogEx(INFO, " TI: %s cmdCntr: 0x%08x",
|
||||||
|
sprint_hex(ctx->TI, 4),
|
||||||
|
ctx->cmdCntr);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -718,7 +759,7 @@ int DesfireSelectAndAuthenticateEx(DesfireContext *dctx, DesfireSecureChannel se
|
||||||
int res = DesfireSelectAIDHex(dctx, aid, false, 0);
|
int res = DesfireSelectAIDHex(dctx, aid, false, 0);
|
||||||
if (res != PM3_SUCCESS) {
|
if (res != PM3_SUCCESS) {
|
||||||
PrintAndLogEx(ERR, "Desfire select " _RED_("error") ".");
|
PrintAndLogEx(ERR, "Desfire select " _RED_("error") ".");
|
||||||
return PM3_ESOFT;
|
return 200;
|
||||||
}
|
}
|
||||||
if (verbose)
|
if (verbose)
|
||||||
PrintAndLogEx(INFO, "App %06x " _GREEN_("selected"), aid);
|
PrintAndLogEx(INFO, "App %06x " _GREEN_("selected"), aid);
|
||||||
|
@ -726,15 +767,15 @@ int DesfireSelectAndAuthenticateEx(DesfireContext *dctx, DesfireSecureChannel se
|
||||||
if (!noauth) {
|
if (!noauth) {
|
||||||
res = DesfireAuthenticate(dctx, secureChannel, verbose);
|
res = DesfireAuthenticate(dctx, secureChannel, verbose);
|
||||||
if (res != PM3_SUCCESS) {
|
if (res != PM3_SUCCESS) {
|
||||||
PrintAndLogEx(ERR, "Desfire authenticate " _RED_("error") ". Result: %d", res);
|
PrintAndLogEx(ERR, "Desfire authenticate " _RED_("error") ". Result: [%d] %s", res, DesfireAuthErrorToStr(res));
|
||||||
return PM3_ESOFT;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DesfireIsAuthenticated(dctx)) {
|
if (DesfireIsAuthenticated(dctx)) {
|
||||||
if (verbose)
|
if (verbose)
|
||||||
PrintAndLogEx(INFO, "Desfire " _GREEN_("authenticated"));
|
PrintAndLogEx(INFO, "Desfire " _GREEN_("authenticated"));
|
||||||
} else {
|
} else {
|
||||||
return PM3_ESOFT;
|
return 201;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -745,7 +786,7 @@ int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secu
|
||||||
return DesfireSelectAndAuthenticateEx(dctx, secureChannel, aid, false, verbose);
|
return DesfireSelectAndAuthenticateEx(dctx, secureChannel, aid, false, verbose);
|
||||||
}
|
}
|
||||||
|
|
||||||
int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose) {
|
static int DesfireAuthenticateEV1(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose) {
|
||||||
// 3 different way to authenticate AUTH (CRC16) , AUTH_ISO (CRC32) , AUTH_AES (CRC32)
|
// 3 different way to authenticate AUTH (CRC16) , AUTH_ISO (CRC32) , AUTH_AES (CRC32)
|
||||||
// 4 different crypto arg1 DES, 3DES, 3K3DES, AES
|
// 4 different crypto arg1 DES, 3DES, 3K3DES, AES
|
||||||
// 3 different communication modes, PLAIN,MAC,CRYPTO
|
// 3 different communication modes, PLAIN,MAC,CRYPTO
|
||||||
|
@ -1033,6 +1074,143 @@ int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel
|
||||||
return PM3_SUCCESS;
|
return PM3_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int DesfireAuthenticateEV2(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool firstauth, bool verbose) {
|
||||||
|
// Crypt constants
|
||||||
|
uint8_t IV[16] = {0};
|
||||||
|
uint8_t RndA[CRYPTO_AES_BLOCK_SIZE] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16};
|
||||||
|
uint8_t RndB[CRYPTO_AES_BLOCK_SIZE] = {0};
|
||||||
|
uint8_t encRndB[CRYPTO_AES_BLOCK_SIZE] = {0};
|
||||||
|
uint8_t rotRndB[CRYPTO_AES_BLOCK_SIZE] = {0}; //RndB'
|
||||||
|
uint8_t both[CRYPTO_AES_BLOCK_SIZE * 2 + 1] = {0}; // ek/dk_keyNo(RndA+RndB')
|
||||||
|
|
||||||
|
uint8_t subcommand = firstauth ? MFDES_AUTHENTICATE_EV2F : MFDES_AUTHENTICATE_EV2NF;
|
||||||
|
uint8_t *key = dctx->key;
|
||||||
|
|
||||||
|
size_t recv_len = 0;
|
||||||
|
uint8_t respcode = 0;
|
||||||
|
uint8_t recv_data[256] = {0};
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
PrintAndLogEx(INFO, _CYAN_("Auth %s:") " cmd: 0x%02x keynum: 0x%02x key: %s", (firstauth) ? "first" : "non-first", subcommand, dctx->keyNum, sprint_hex(key, 16));
|
||||||
|
|
||||||
|
// Let's send our auth command
|
||||||
|
uint8_t cdata[2] = {dctx->keyNum, 0x00};
|
||||||
|
int res = DesfireExchangeEx(false, dctx, subcommand, cdata, (firstauth) ? sizeof(cdata) : 1, &respcode, recv_data, &recv_len, false, 0);
|
||||||
|
if (res != PM3_SUCCESS) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!recv_len) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (respcode != MFDES_ADDITIONAL_FRAME) {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recv_len != CRYPTO_AES_BLOCK_SIZE) {
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part 2
|
||||||
|
memcpy(encRndB, recv_data, 16);
|
||||||
|
|
||||||
|
// Part 3
|
||||||
|
if (aes_decode(IV, key, encRndB, RndB, CRYPTO_AES_BLOCK_SIZE))
|
||||||
|
return 5;
|
||||||
|
|
||||||
|
if (g_debugMode > 1) {
|
||||||
|
PrintAndLogEx(DEBUG, "encRndB: %s", sprint_hex(encRndB, CRYPTO_AES_BLOCK_SIZE));
|
||||||
|
PrintAndLogEx(DEBUG, "RndB: %s", sprint_hex(RndB, CRYPTO_AES_BLOCK_SIZE));
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Rotate RndB by 8 bits
|
||||||
|
memcpy(rotRndB, RndB, CRYPTO_AES_BLOCK_SIZE);
|
||||||
|
rol(rotRndB, CRYPTO_AES_BLOCK_SIZE);
|
||||||
|
|
||||||
|
uint8_t encRndA[16] = {0x00};
|
||||||
|
|
||||||
|
// - Encrypt our response
|
||||||
|
uint8_t tmp[32] = {0x00};
|
||||||
|
memcpy(tmp, RndA, CRYPTO_AES_BLOCK_SIZE);
|
||||||
|
memcpy(tmp + CRYPTO_AES_BLOCK_SIZE, rotRndB, CRYPTO_AES_BLOCK_SIZE);
|
||||||
|
if (g_debugMode > 1) {
|
||||||
|
PrintAndLogEx(DEBUG, "rotRndB: %s", sprint_hex(rotRndB, CRYPTO_AES_BLOCK_SIZE));
|
||||||
|
PrintAndLogEx(DEBUG, "Both: %s", sprint_hex(tmp, CRYPTO_AES_BLOCK_SIZE * 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aes_encode(IV, key, tmp, both, CRYPTO_AES_BLOCK_SIZE * 2))
|
||||||
|
return 6;
|
||||||
|
if (g_debugMode > 1) {
|
||||||
|
PrintAndLogEx(DEBUG, "EncBoth: %s", sprint_hex(both, CRYPTO_AES_BLOCK_SIZE * 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
res = DesfireExchangeEx(false, dctx, MFDES_ADDITIONAL_FRAME, both, CRYPTO_AES_BLOCK_SIZE * 2, &respcode, recv_data, &recv_len, false, 0);
|
||||||
|
if (res != PM3_SUCCESS) {
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!recv_len) {
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (respcode != MFDES_S_OPERATION_OK) {
|
||||||
|
return 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Part 4
|
||||||
|
memcpy(encRndA, recv_data, CRYPTO_AES_BLOCK_SIZE);
|
||||||
|
|
||||||
|
uint8_t data[32] = {0};
|
||||||
|
|
||||||
|
if (aes_decode(IV, key, recv_data, data, recv_len))
|
||||||
|
return 10;
|
||||||
|
|
||||||
|
rol(RndA, CRYPTO_AES_BLOCK_SIZE);
|
||||||
|
uint8_t *recRndA = (firstauth) ? &data[4] : data;
|
||||||
|
|
||||||
|
if (memcmp(RndA, recRndA, CRYPTO_AES_BLOCK_SIZE) != 0) {
|
||||||
|
if (g_debugMode > 1) {
|
||||||
|
PrintAndLogEx(DEBUG, "Expected_RndA : %s", sprint_hex(RndA, CRYPTO_AES_BLOCK_SIZE));
|
||||||
|
PrintAndLogEx(DEBUG, "Generated_RndA : %s", sprint_hex(recRndA, CRYPTO_AES_BLOCK_SIZE));
|
||||||
|
}
|
||||||
|
return 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (firstauth) {
|
||||||
|
dctx->cmdCntr = 0;
|
||||||
|
memcpy(dctx->TI, data, 4);
|
||||||
|
}
|
||||||
|
DesfireClearIV(dctx);
|
||||||
|
DesfireGenSessionKeyEV2(dctx->key, RndA, RndB, true, dctx->sessionKeyEnc);
|
||||||
|
DesfireGenSessionKeyEV2(dctx->key, RndA, RndB, false, dctx->sessionKeyMAC);
|
||||||
|
dctx->secureChannel = secureChannel;
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
if (firstauth) {
|
||||||
|
PrintAndLogEx(INFO, "TI : %s", sprint_hex(data, 4));
|
||||||
|
PrintAndLogEx(INFO, "pic : %s", sprint_hex(&data[20], 6));
|
||||||
|
PrintAndLogEx(INFO, "pcd : %s", sprint_hex(&data[26], 6));
|
||||||
|
} else {
|
||||||
|
PrintAndLogEx(INFO, "TI : %s", sprint_hex(dctx->TI, 4));
|
||||||
|
}
|
||||||
|
PrintAndLogEx(INFO, "session key ENC: %s", sprint_hex(dctx->sessionKeyEnc, 16));
|
||||||
|
PrintAndLogEx(INFO, "session key MAC: %s", sprint_hex(dctx->sessionKeyMAC, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
return PM3_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose) {
|
||||||
|
if (secureChannel == DACd40 || secureChannel == DACEV1)
|
||||||
|
return DesfireAuthenticateEV1(dctx, secureChannel, verbose);
|
||||||
|
|
||||||
|
if (secureChannel == DACEV2)
|
||||||
|
return DesfireAuthenticateEV2(dctx, secureChannel, (DesfireIsAuthenticated(dctx) == false), verbose); // non first auth if there is a working secure channel
|
||||||
|
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
static int DesfireCommandEx(DesfireContext *dctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *resp, size_t *resplen, int checklength, size_t splitbysize) {
|
static int DesfireCommandEx(DesfireContext *dctx, uint8_t cmd, uint8_t *data, size_t datalen, uint8_t *resp, size_t *resplen, int checklength, size_t splitbysize) {
|
||||||
if (resplen)
|
if (resplen)
|
||||||
*resplen = 0;
|
*resplen = 0;
|
||||||
|
|
|
@ -106,6 +106,7 @@ int DesfireSelectAID(DesfireContext *ctx, uint8_t *aid1, uint8_t *aid2);
|
||||||
int DesfireSelectAIDHex(DesfireContext *ctx, uint32_t aid1, bool select_two, uint32_t aid2);
|
int DesfireSelectAIDHex(DesfireContext *ctx, uint32_t aid1, bool select_two, uint32_t aid2);
|
||||||
int DesfireSelectAIDHexNoFieldOn(DesfireContext *ctx, uint32_t aid);
|
int DesfireSelectAIDHexNoFieldOn(DesfireContext *ctx, uint32_t aid);
|
||||||
|
|
||||||
|
const char *DesfireAuthErrorToStr(int error);
|
||||||
int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool verbose);
|
int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool verbose);
|
||||||
int DesfireSelectAndAuthenticateEx(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool noauth, bool verbose);
|
int DesfireSelectAndAuthenticateEx(DesfireContext *dctx, DesfireSecureChannel secureChannel, uint32_t aid, bool noauth, bool verbose);
|
||||||
int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose);
|
int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose);
|
||||||
|
|
|
@ -58,11 +58,14 @@ void DesfireClearSession(DesfireContext *ctx) {
|
||||||
memset(ctx->lastIV, 0, sizeof(ctx->lastIV));
|
memset(ctx->lastIV, 0, sizeof(ctx->lastIV));
|
||||||
ctx->lastCommand = 0;
|
ctx->lastCommand = 0;
|
||||||
ctx->lastRequestZeroLen = false;
|
ctx->lastRequestZeroLen = false;
|
||||||
ctx->cntrTx = 0;
|
ctx->cmdCntr = 0;
|
||||||
ctx->cntrRx = 0;
|
|
||||||
memset(ctx->TI, 0, sizeof(ctx->TI));
|
memset(ctx->TI, 0, sizeof(ctx->TI));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DesfireClearIV(DesfireContext *ctx) {
|
||||||
|
memset(ctx->IV, 0, sizeof(ctx->IV));
|
||||||
|
}
|
||||||
|
|
||||||
void DesfireSetKey(DesfireContext *ctx, uint8_t keyNum, enum DESFIRE_CRYPTOALGO keyType, uint8_t *key) {
|
void DesfireSetKey(DesfireContext *ctx, uint8_t keyNum, enum DESFIRE_CRYPTOALGO keyType, uint8_t *key) {
|
||||||
DesfireClearContext(ctx);
|
DesfireClearContext(ctx);
|
||||||
|
|
||||||
|
@ -386,6 +389,61 @@ uint8_t DesfireCommModeToFileCommMode(DesfireCommunicationMode comm_mode) {
|
||||||
return fmode;
|
return fmode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
uint8_t data[64] = {0};
|
||||||
|
memset(sessionkey, 0, CRYPTO_AES_BLOCK_SIZE);
|
||||||
|
|
||||||
|
if (enckey) {
|
||||||
|
data[0] = 0xa5;
|
||||||
|
data[1] = 0x5a;
|
||||||
|
} else {
|
||||||
|
data[0] = 0x5a;
|
||||||
|
data[1] = 0xa5;
|
||||||
|
}
|
||||||
|
data[3] = 0x01;
|
||||||
|
data[5] = 0x80;
|
||||||
|
|
||||||
|
// data+6 - start of rnd part
|
||||||
|
memcpy(data + 6, rndA, 8);
|
||||||
|
bin_xor(data + 8, rndB, 6); // xor rndb 6b
|
||||||
|
memcpy(data + 14, rndB + 6, 10);
|
||||||
|
memcpy(data + 24, rndA + 8, 8);
|
||||||
|
|
||||||
|
uint8_t cmac[CRYPTO_AES_BLOCK_SIZE] = {0};
|
||||||
|
DesfireContext ctx = {0};
|
||||||
|
ctx.keyType = T_AES;
|
||||||
|
memcpy(ctx.key, key, 16); // aes-128
|
||||||
|
DesfireCryptoCMAC(&ctx, data, 32, cmac);
|
||||||
|
|
||||||
|
memcpy(sessionkey, cmac, CRYPTO_AES_BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DesfireEV2FillIV(DesfireContext *ctx, bool ivforcommand, uint8_t *iv) {
|
||||||
|
uint8_t xiv[CRYPTO_AES_BLOCK_SIZE] = {0};
|
||||||
|
|
||||||
|
if (ivforcommand) {
|
||||||
|
xiv[0] = 0xa5;
|
||||||
|
xiv[1] = 0x5a;
|
||||||
|
} else {
|
||||||
|
xiv[0] = 0x5a;
|
||||||
|
xiv[1] = 0xa5;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(xiv + 2, ctx->TI, 4);
|
||||||
|
Uint2byteToMemLe(xiv + 2 + 4, ctx->cmdCntr);
|
||||||
|
|
||||||
|
if (aes_encode(NULL, ctx->sessionKeyEnc, xiv, xiv, CRYPTO_AES_BLOCK_SIZE))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (iv == NULL)
|
||||||
|
memcpy(ctx->IV, xiv, CRYPTO_AES_BLOCK_SIZE);
|
||||||
|
else
|
||||||
|
memcpy(iv, xiv, CRYPTO_AES_BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc) {
|
void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc) {
|
||||||
crc32_ex(data, len, crc);
|
crc32_ex(data, len, crc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "mifare/mifare4.h"
|
#include "mifare/mifare4.h"
|
||||||
|
|
||||||
|
#define CRYPTO_AES_BLOCK_SIZE 16
|
||||||
#define MAX_CRYPTO_BLOCK_SIZE 16
|
#define MAX_CRYPTO_BLOCK_SIZE 16
|
||||||
#define DESFIRE_MAX_CRYPTO_BLOCK_SIZE 16
|
#define DESFIRE_MAX_CRYPTO_BLOCK_SIZE 16
|
||||||
#define DESFIRE_MAX_KEY_SIZE 24
|
#define DESFIRE_MAX_KEY_SIZE 24
|
||||||
|
@ -81,14 +82,13 @@ typedef struct DesfireContextS {
|
||||||
uint8_t lastIV[DESFIRE_MAX_KEY_SIZE];
|
uint8_t lastIV[DESFIRE_MAX_KEY_SIZE];
|
||||||
uint8_t lastCommand;
|
uint8_t lastCommand;
|
||||||
bool lastRequestZeroLen;
|
bool lastRequestZeroLen;
|
||||||
//mf4Session_t AESSession;
|
uint16_t cmdCntr; // for AES
|
||||||
uint16_t cntrTx; // for AES
|
|
||||||
uint16_t cntrRx; // for AES
|
|
||||||
uint8_t TI[4]; // for AES
|
uint8_t TI[4]; // for AES
|
||||||
} DesfireContext;
|
} DesfireContext;
|
||||||
|
|
||||||
void DesfireClearContext(DesfireContext *ctx);
|
void DesfireClearContext(DesfireContext *ctx);
|
||||||
void DesfireClearSession(DesfireContext *ctx);
|
void DesfireClearSession(DesfireContext *ctx);
|
||||||
|
void DesfireClearIV(DesfireContext *ctx);
|
||||||
void DesfireSetKey(DesfireContext *ctx, uint8_t keyNum, enum DESFIRE_CRYPTOALGO keyType, uint8_t *key);
|
void DesfireSetKey(DesfireContext *ctx, uint8_t keyNum, enum DESFIRE_CRYPTOALGO keyType, uint8_t *key);
|
||||||
void DesfireSetCommandSet(DesfireContext *ctx, DesfireCommandSet cmdSet);
|
void DesfireSetCommandSet(DesfireContext *ctx, DesfireCommandSet cmdSet);
|
||||||
void DesfireSetCommMode(DesfireContext *ctx, DesfireCommunicationMode commMode);
|
void DesfireSetCommMode(DesfireContext *ctx, DesfireCommunicationMode commMode);
|
||||||
|
@ -108,6 +108,9 @@ uint8_t DesfireDESKeyGetVersion(uint8_t *key);
|
||||||
DesfireCommunicationMode DesfireFileCommModeToCommMode(uint8_t file_comm_mode);
|
DesfireCommunicationMode DesfireFileCommModeToCommMode(uint8_t file_comm_mode);
|
||||||
uint8_t DesfireCommModeToFileCommMode(DesfireCommunicationMode comm_mode);
|
uint8_t DesfireCommModeToFileCommMode(DesfireCommunicationMode comm_mode);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc);
|
void desfire_crc32(const uint8_t *data, const size_t len, uint8_t *crc);
|
||||||
void desfire_crc32_append(uint8_t *data, const size_t len);
|
void desfire_crc32_append(uint8_t *data, const size_t len);
|
||||||
bool desfire_crc32_check(uint8_t *data, const size_t len, uint8_t *crc);
|
bool desfire_crc32_check(uint8_t *data, const size_t len, uint8_t *crc);
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "mifare/desfire_crypto.h"
|
#include "mifare/desfire_crypto.h"
|
||||||
|
|
||||||
static const uint8_t CommandsCanUseAnyChannel[] = {
|
static const uint8_t CommandsCanUseAnyChannel[] = {
|
||||||
|
MFDES_S_ADDITIONAL_FRAME,
|
||||||
MFDES_READ_DATA,
|
MFDES_READ_DATA,
|
||||||
MFDES_WRITE_DATA,
|
MFDES_WRITE_DATA,
|
||||||
MFDES_GET_VALUE,
|
MFDES_GET_VALUE,
|
||||||
|
@ -116,6 +117,9 @@ static const AllowedChannelModesS AllowedChannelModes[] = {
|
||||||
|
|
||||||
{MFDES_CHANGE_KEY, DACEV1, DCCNative, DCMEncryptedPlain},
|
{MFDES_CHANGE_KEY, DACEV1, DCCNative, DCMEncryptedPlain},
|
||||||
{MFDES_CHANGE_KEY_EV2, DACEV1, DCCNative, DCMEncryptedPlain},
|
{MFDES_CHANGE_KEY_EV2, DACEV1, DCCNative, DCMEncryptedPlain},
|
||||||
|
|
||||||
|
{MFDES_AUTHENTICATE_EV2F, DACEV2, DCCNative, DCMPlain},
|
||||||
|
{MFDES_AUTHENTICATE_EV2NF, DACEV2, DCCNative, DCMPlain},
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CMD_HEADER_LEN_ALL 0xffff
|
#define CMD_HEADER_LEN_ALL 0xffff
|
||||||
|
@ -239,6 +243,12 @@ static void DesfireSecureChannelEncodeEV1(DesfireContext *ctx, uint8_t cmd, uint
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void DesfireSecureChannelEncodeEV2(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) {
|
||||||
|
memcpy(dstdata, srcdata, srcdatalen);
|
||||||
|
*dstdatalen = srcdatalen;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void DesfireSecureChannelEncode(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) {
|
void DesfireSecureChannelEncode(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) {
|
||||||
ctx->lastCommand = cmd;
|
ctx->lastCommand = cmd;
|
||||||
ctx->lastRequestZeroLen = (srcdatalen <= DesfireGetCmdHeaderLen(cmd));
|
ctx->lastRequestZeroLen = (srcdatalen <= DesfireGetCmdHeaderLen(cmd));
|
||||||
|
@ -251,6 +261,7 @@ void DesfireSecureChannelEncode(DesfireContext *ctx, uint8_t cmd, uint8_t *srcda
|
||||||
DesfireSecureChannelEncodeEV1(ctx, cmd, srcdata, srcdatalen, dstdata, dstdatalen);
|
DesfireSecureChannelEncodeEV1(ctx, cmd, srcdata, srcdatalen, dstdata, dstdatalen);
|
||||||
break;
|
break;
|
||||||
case DACEV2:
|
case DACEV2:
|
||||||
|
DesfireSecureChannelEncodeEV2(ctx, cmd, srcdata, srcdatalen, dstdata, dstdatalen);
|
||||||
break;
|
break;
|
||||||
case DACNone:
|
case DACNone:
|
||||||
memcpy(dstdata, srcdata, srcdatalen);
|
memcpy(dstdata, srcdata, srcdatalen);
|
||||||
|
@ -361,6 +372,13 @@ static void DesfireSecureChannelDecodeEV1(DesfireContext *ctx, uint8_t *srcdata,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void DesfireSecureChannelDecodeEV2(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) {
|
||||||
|
ctx->cmdCntr++;
|
||||||
|
|
||||||
|
memcpy(dstdata, srcdata, srcdatalen);
|
||||||
|
*dstdatalen = srcdatalen;
|
||||||
|
}
|
||||||
|
|
||||||
void DesfireSecureChannelDecode(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) {
|
void DesfireSecureChannelDecode(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) {
|
||||||
switch (ctx->secureChannel) {
|
switch (ctx->secureChannel) {
|
||||||
case DACd40:
|
case DACd40:
|
||||||
|
@ -370,6 +388,7 @@ void DesfireSecureChannelDecode(DesfireContext *ctx, uint8_t *srcdata, size_t sr
|
||||||
DesfireSecureChannelDecodeEV1(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen);
|
DesfireSecureChannelDecodeEV1(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen);
|
||||||
break;
|
break;
|
||||||
case DACEV2:
|
case DACEV2:
|
||||||
|
DesfireSecureChannelDecodeEV2(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen);
|
||||||
break;
|
break;
|
||||||
case DACNone:
|
case DACNone:
|
||||||
memcpy(dstdata, srcdata, srcdatalen);
|
memcpy(dstdata, srcdata, srcdatalen);
|
||||||
|
|
|
@ -44,9 +44,9 @@ static bool TestCRC16(void) {
|
||||||
res = res && (len == 0);
|
res = res && (len == 0);
|
||||||
|
|
||||||
if (res)
|
if (res)
|
||||||
PrintAndLogEx(INFO, "crc16............ " _GREEN_("passed"));
|
PrintAndLogEx(INFO, "crc16............. " _GREEN_("passed"));
|
||||||
else
|
else
|
||||||
PrintAndLogEx(ERR, "crc16............ " _RED_("fail"));
|
PrintAndLogEx(ERR, "crc16............. " _RED_("fail"));
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -71,9 +71,9 @@ static bool TestCRC32(void) {
|
||||||
res = res && (len == 0);
|
res = res && (len == 0);
|
||||||
|
|
||||||
if (res)
|
if (res)
|
||||||
PrintAndLogEx(INFO, "crc32............ " _GREEN_("passed"));
|
PrintAndLogEx(INFO, "crc32............. " _GREEN_("passed"));
|
||||||
else
|
else
|
||||||
PrintAndLogEx(ERR, "crc32............ " _RED_("fail"));
|
PrintAndLogEx(ERR, "crc32............. " _RED_("fail"));
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -115,9 +115,9 @@ static bool TestCMAC3TDEA(void) {
|
||||||
res = res && (memcmp(cmac, cmac4, sizeof(cmac1)) == 0);
|
res = res && (memcmp(cmac, cmac4, sizeof(cmac1)) == 0);
|
||||||
|
|
||||||
if (res)
|
if (res)
|
||||||
PrintAndLogEx(INFO, "CMAC 3TDEA....... " _GREEN_("passed"));
|
PrintAndLogEx(INFO, "CMAC 3TDEA........ " _GREEN_("passed"));
|
||||||
else
|
else
|
||||||
PrintAndLogEx(ERR, "CMAC 3TDEA....... " _RED_("fail"));
|
PrintAndLogEx(ERR, "CMAC 3TDEA........ " _RED_("fail"));
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -159,9 +159,9 @@ static bool TestCMAC2TDEA(void) {
|
||||||
res = res && (memcmp(cmac, cmac4, sizeof(cmac1)) == 0);
|
res = res && (memcmp(cmac, cmac4, sizeof(cmac1)) == 0);
|
||||||
|
|
||||||
if (res)
|
if (res)
|
||||||
PrintAndLogEx(INFO, "CMAC 2TDEA....... " _GREEN_("passed"));
|
PrintAndLogEx(INFO, "CMAC 2TDEA........ " _GREEN_("passed"));
|
||||||
else
|
else
|
||||||
PrintAndLogEx(ERR, "CMAC 2TDEA....... " _RED_("fail"));
|
PrintAndLogEx(ERR, "CMAC 2TDEA........ " _RED_("fail"));
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -199,9 +199,72 @@ static bool TestCMACDES(void) {
|
||||||
res = res && (memcmp(cmac, cmac4, sizeof(cmac1)) == 0);
|
res = res && (memcmp(cmac, cmac4, sizeof(cmac1)) == 0);
|
||||||
|
|
||||||
if (res)
|
if (res)
|
||||||
PrintAndLogEx(INFO, "CMAC DES......... " _GREEN_("passed"));
|
PrintAndLogEx(INFO, "CMAC DES.......... " _GREEN_("passed"));
|
||||||
else
|
else
|
||||||
PrintAndLogEx(ERR, "CMAC DES......... " _RED_("fail"));
|
PrintAndLogEx(ERR, "CMAC DES.......... " _RED_("fail"));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.nxp.com/docs/en/application-note/AN12343.pdf
|
||||||
|
// page 33-34
|
||||||
|
static bool TestEV2SessionKeys(void) {
|
||||||
|
bool res = true;
|
||||||
|
|
||||||
|
uint8_t key[16] = {0};
|
||||||
|
uint8_t rnda[] = {0xB0, 0x4D, 0x07, 0x87, 0xC9, 0x3E, 0xE0, 0xCC, 0x8C, 0xAC, 0xC8, 0xE8, 0x6F, 0x16, 0xC6, 0xFE};
|
||||||
|
uint8_t rndb[] = {0xFA, 0x65, 0x9A, 0xD0, 0xDC, 0xA7, 0x38, 0xDD, 0x65, 0xDC, 0x7D, 0xC3, 0x86, 0x12, 0xAD, 0x81};
|
||||||
|
uint8_t sessionkeyauth[] = {0x63, 0xDC, 0x07, 0x28, 0x62, 0x89, 0xA7, 0xA6, 0xC0, 0x33, 0x4C, 0xA3, 0x1C, 0x31, 0x4A, 0x04};
|
||||||
|
uint8_t sessionkeymac[] = {0x77, 0x4F, 0x26, 0x74, 0x3E, 0xCE, 0x6A, 0xF5, 0x03, 0x3B, 0x6A, 0xE8, 0x52, 0x29, 0x46, 0xF6};
|
||||||
|
|
||||||
|
uint8_t sessionkey[16] = {0};
|
||||||
|
DesfireGenSessionKeyEV2(key, rnda, rndb, true, sessionkey);
|
||||||
|
res = res && (memcmp(sessionkey, sessionkeyauth, sizeof(sessionkeyauth)) == 0);
|
||||||
|
|
||||||
|
memset(sessionkey, 0, sizeof(sessionkey));
|
||||||
|
DesfireGenSessionKeyEV2(key, rnda, rndb, false, sessionkey);
|
||||||
|
res = res && (memcmp(sessionkey, sessionkeymac, sizeof(sessionkeymac)) == 0);
|
||||||
|
|
||||||
|
if (res)
|
||||||
|
PrintAndLogEx(INFO, "EV2 session keys.. " _GREEN_("passed"));
|
||||||
|
else
|
||||||
|
PrintAndLogEx(ERR, "EV2 session keys.. " _RED_("fail"));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool TestEV2IVEncode(void) {
|
||||||
|
bool res = true;
|
||||||
|
|
||||||
|
uint8_t key[] = {0x66, 0xA8, 0xCB, 0x93, 0x26, 0x9D, 0xC9, 0xBC, 0x28, 0x85, 0xB7, 0xA9, 0x1B, 0x9C, 0x69, 0x7B};
|
||||||
|
uint8_t ti[] = {0xED, 0x56, 0xF6, 0xE6};
|
||||||
|
uint8_t ivres[] = {0xDA, 0x0F, 0x64, 0x4A, 0x49, 0x86, 0x27, 0x59, 0x57, 0xCF, 0x1E, 0xC3, 0xAF, 0x4C, 0xCE, 0x53};
|
||||||
|
|
||||||
|
DesfireContext ctx = {0};
|
||||||
|
ctx.keyType = T_AES;
|
||||||
|
memcpy(ctx.sessionKeyEnc, key, 16);
|
||||||
|
memcpy(ctx.TI, ti, 4);
|
||||||
|
ctx.cmdCntr = 0;
|
||||||
|
|
||||||
|
uint8_t iv[16] = {0};
|
||||||
|
DesfireEV2FillIV(&ctx, true, iv);
|
||||||
|
res = res && (memcmp(iv, ivres, sizeof(ivres)) == 0);
|
||||||
|
|
||||||
|
uint8_t key2[] = {0x44, 0x5A, 0x86, 0x26, 0xB3, 0x33, 0x84, 0x59, 0x32, 0x12, 0x32, 0xfA, 0xDf, 0x6a, 0xDe, 0x2B};
|
||||||
|
uint8_t ti2[] = {0x11, 0x22, 0x33, 0x44};
|
||||||
|
uint8_t ivres2[] = {0x17, 0x74, 0x94, 0xFC, 0xC4, 0xF1, 0xDA, 0xB2, 0xAF, 0xBE, 0x8F, 0xAE, 0x20, 0x57, 0xA9, 0xD2};
|
||||||
|
memcpy(ctx.sessionKeyEnc, key2, 16);
|
||||||
|
memcpy(ctx.TI, ti2, 4);
|
||||||
|
ctx.cmdCntr = 5;
|
||||||
|
|
||||||
|
memset(iv, 0, 16);
|
||||||
|
DesfireEV2FillIV(&ctx, true, iv);
|
||||||
|
res = res && (memcmp(iv, ivres2, sizeof(ivres2)) == 0);
|
||||||
|
|
||||||
|
if (res)
|
||||||
|
PrintAndLogEx(INFO, "EV2 IV calc....... " _GREEN_("passed"));
|
||||||
|
else
|
||||||
|
PrintAndLogEx(ERR, "EV2 IV calc....... " _RED_("fail"));
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -216,6 +279,8 @@ bool DesfireTest(bool verbose) {
|
||||||
res = res && TestCMAC3TDEA();
|
res = res && TestCMAC3TDEA();
|
||||||
res = res && TestCMAC2TDEA();
|
res = res && TestCMAC2TDEA();
|
||||||
res = res && TestCMACDES();
|
res = res && TestCMACDES();
|
||||||
|
res = res && TestEV2SessionKeys();
|
||||||
|
res = res && TestEV2IVEncode();
|
||||||
|
|
||||||
PrintAndLogEx(INFO, "---------------------------");
|
PrintAndLogEx(INFO, "---------------------------");
|
||||||
if (res)
|
if (res)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue