Merge pull request #1405 from merlokk/dauth

Desfire auth ev2
This commit is contained in:
Oleg Moiseenko 2021-07-30 16:54:17 +03:00 committed by GitHub
commit 0e07e86e45
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 346 additions and 62 deletions

View file

@ -916,46 +916,6 @@ static int handler_desfire_auth(mfdes_authinput_t *payload, mfdes_auth_res_t *rp
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
static int test_desfire_authenticate(void) {
uint8_t data[] = {0x00};
@ -3260,7 +3220,7 @@ static int CmdHF14ADesAuth(const char *Cmd) {
res = DesfireSelectAndAuthenticateEx(&dctx, securechann, appid, false, verbose);
if (res != PM3_SUCCESS) {
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;
}

View file

@ -231,6 +231,42 @@ const char *DesfireGetErrorString(int res, uint16_t *sw) {
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) {
return data[0] + (data[1] << 8) + (data[2] << 16);
}
@ -266,6 +302,11 @@ void DesfirePrintContext(DesfireContext *ctx) {
PrintAndLogEx(INFO, " IV [%zu]: %s",
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);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire select " _RED_("error") ".");
return PM3_ESOFT;
return 200;
}
if (verbose)
PrintAndLogEx(INFO, "App %06x " _GREEN_("selected"), aid);
@ -726,15 +767,15 @@ int DesfireSelectAndAuthenticateEx(DesfireContext *dctx, DesfireSecureChannel se
if (!noauth) {
res = DesfireAuthenticate(dctx, secureChannel, verbose);
if (res != PM3_SUCCESS) {
PrintAndLogEx(ERR, "Desfire authenticate " _RED_("error") ". Result: %d", res);
return PM3_ESOFT;
PrintAndLogEx(ERR, "Desfire authenticate " _RED_("error") ". Result: [%d] %s", res, DesfireAuthErrorToStr(res));
return res;
}
if (DesfireIsAuthenticated(dctx)) {
if (verbose)
PrintAndLogEx(INFO, "Desfire " _GREEN_("authenticated"));
} else {
return PM3_ESOFT;
return 201;
}
}
@ -745,7 +786,7 @@ int DesfireSelectAndAuthenticate(DesfireContext *dctx, DesfireSecureChannel secu
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)
// 4 different crypto arg1 DES, 3DES, 3K3DES, AES
// 3 different communication modes, PLAIN,MAC,CRYPTO
@ -1033,6 +1074,143 @@ int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel
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) {
if (resplen)
*resplen = 0;

View file

@ -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 DesfireSelectAIDHexNoFieldOn(DesfireContext *ctx, uint32_t aid);
const char *DesfireAuthErrorToStr(int error);
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 DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel, bool verbose);

View file

@ -58,11 +58,14 @@ void DesfireClearSession(DesfireContext *ctx) {
memset(ctx->lastIV, 0, sizeof(ctx->lastIV));
ctx->lastCommand = 0;
ctx->lastRequestZeroLen = false;
ctx->cntrTx = 0;
ctx->cntrRx = 0;
ctx->cmdCntr = 0;
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) {
DesfireClearContext(ctx);
@ -386,6 +389,61 @@ uint8_t DesfireCommModeToFileCommMode(DesfireCommunicationMode comm_mode) {
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) {
crc32_ex(data, len, crc);
}

View file

@ -24,6 +24,7 @@
#include "common.h"
#include "mifare/mifare4.h"
#define CRYPTO_AES_BLOCK_SIZE 16
#define MAX_CRYPTO_BLOCK_SIZE 16
#define DESFIRE_MAX_CRYPTO_BLOCK_SIZE 16
#define DESFIRE_MAX_KEY_SIZE 24
@ -81,14 +82,13 @@ typedef struct DesfireContextS {
uint8_t lastIV[DESFIRE_MAX_KEY_SIZE];
uint8_t lastCommand;
bool lastRequestZeroLen;
//mf4Session_t AESSession;
uint16_t cntrTx; // for AES
uint16_t cntrRx; // for AES
uint16_t cmdCntr; // for AES
uint8_t TI[4]; // for AES
} DesfireContext;
void DesfireClearContext(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 DesfireSetCommandSet(DesfireContext *ctx, DesfireCommandSet cmdSet);
void DesfireSetCommMode(DesfireContext *ctx, DesfireCommunicationMode commMode);
@ -108,6 +108,9 @@ uint8_t DesfireDESKeyGetVersion(uint8_t *key);
DesfireCommunicationMode DesfireFileCommModeToCommMode(uint8_t file_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_append(uint8_t *data, const size_t len);
bool desfire_crc32_check(uint8_t *data, const size_t len, uint8_t *crc);

View file

@ -24,6 +24,7 @@
#include "mifare/desfire_crypto.h"
static const uint8_t CommandsCanUseAnyChannel[] = {
MFDES_S_ADDITIONAL_FRAME,
MFDES_READ_DATA,
MFDES_WRITE_DATA,
MFDES_GET_VALUE,
@ -116,6 +117,9 @@ static const AllowedChannelModesS AllowedChannelModes[] = {
{MFDES_CHANGE_KEY, 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
@ -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) {
ctx->lastCommand = 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);
break;
case DACEV2:
DesfireSecureChannelEncodeEV2(ctx, cmd, srcdata, srcdatalen, dstdata, dstdatalen);
break;
case DACNone:
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) {
switch (ctx->secureChannel) {
case DACd40:
@ -370,6 +388,7 @@ void DesfireSecureChannelDecode(DesfireContext *ctx, uint8_t *srcdata, size_t sr
DesfireSecureChannelDecodeEV1(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen);
break;
case DACEV2:
DesfireSecureChannelDecodeEV2(ctx, srcdata, srcdatalen, respcode, dstdata, dstdatalen);
break;
case DACNone:
memcpy(dstdata, srcdata, srcdatalen);

View file

@ -44,9 +44,9 @@ static bool TestCRC16(void) {
res = res && (len == 0);
if (res)
PrintAndLogEx(INFO, "crc16............ " _GREEN_("passed"));
PrintAndLogEx(INFO, "crc16............. " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "crc16............ " _RED_("fail"));
PrintAndLogEx(ERR, "crc16............. " _RED_("fail"));
return res;
}
@ -71,9 +71,9 @@ static bool TestCRC32(void) {
res = res && (len == 0);
if (res)
PrintAndLogEx(INFO, "crc32............ " _GREEN_("passed"));
PrintAndLogEx(INFO, "crc32............. " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "crc32............ " _RED_("fail"));
PrintAndLogEx(ERR, "crc32............. " _RED_("fail"));
return res;
}
@ -115,9 +115,9 @@ static bool TestCMAC3TDEA(void) {
res = res && (memcmp(cmac, cmac4, sizeof(cmac1)) == 0);
if (res)
PrintAndLogEx(INFO, "CMAC 3TDEA....... " _GREEN_("passed"));
PrintAndLogEx(INFO, "CMAC 3TDEA........ " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "CMAC 3TDEA....... " _RED_("fail"));
PrintAndLogEx(ERR, "CMAC 3TDEA........ " _RED_("fail"));
return res;
}
@ -159,9 +159,9 @@ static bool TestCMAC2TDEA(void) {
res = res && (memcmp(cmac, cmac4, sizeof(cmac1)) == 0);
if (res)
PrintAndLogEx(INFO, "CMAC 2TDEA....... " _GREEN_("passed"));
PrintAndLogEx(INFO, "CMAC 2TDEA........ " _GREEN_("passed"));
else
PrintAndLogEx(ERR, "CMAC 2TDEA....... " _RED_("fail"));
PrintAndLogEx(ERR, "CMAC 2TDEA........ " _RED_("fail"));
return res;
}
@ -199,9 +199,72 @@ static bool TestCMACDES(void) {
res = res && (memcmp(cmac, cmac4, sizeof(cmac1)) == 0);
if (res)
PrintAndLogEx(INFO, "CMAC DES......... " _GREEN_("passed"));
PrintAndLogEx(INFO, "CMAC DES.......... " _GREEN_("passed"));
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;
}
@ -216,6 +279,8 @@ bool DesfireTest(bool verbose) {
res = res && TestCMAC3TDEA();
res = res && TestCMAC2TDEA();
res = res && TestCMACDES();
res = res && TestEV2SessionKeys();
res = res && TestEV2IVEncode();
PrintAndLogEx(INFO, "---------------------------");
if (res)