diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index c90a13525..f8d00fd7e 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -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; } diff --git a/client/src/mifare/desfirecore.c b/client/src/mifare/desfirecore.c index ce7f8298f..147850852 100644 --- a/client/src/mifare/desfirecore.c +++ b/client/src/mifare/desfirecore.c @@ -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; diff --git a/client/src/mifare/desfirecore.h b/client/src/mifare/desfirecore.h index 456798504..f845bf334 100644 --- a/client/src/mifare/desfirecore.h +++ b/client/src/mifare/desfirecore.h @@ -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); diff --git a/client/src/mifare/desfirecrypto.c b/client/src/mifare/desfirecrypto.c index 391da348a..73913c449 100644 --- a/client/src/mifare/desfirecrypto.c +++ b/client/src/mifare/desfirecrypto.c @@ -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); @@ -219,7 +222,7 @@ void DesfireCryptoEncDecEx(DesfireContext *ctx, bool use_session_key, uint8_t *s if (ctx->secureChannel == DACd40) { memset(ctx->IV, 0, DESFIRE_MAX_CRYPTO_BLOCK_SIZE); } - + size_t block_size = desfire_get_key_block_length(ctx->keyType); if (iv == NULL) @@ -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); } diff --git a/client/src/mifare/desfirecrypto.h b/client/src/mifare/desfirecrypto.h index fddef64c9..2ba28bc46 100644 --- a/client/src/mifare/desfirecrypto.h +++ b/client/src/mifare/desfirecrypto.h @@ -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); diff --git a/client/src/mifare/desfiresecurechan.c b/client/src/mifare/desfiresecurechan.c index 7e20b9f1a..586c95285 100644 --- a/client/src/mifare/desfiresecurechan.c +++ b/client/src/mifare/desfiresecurechan.c @@ -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); diff --git a/client/src/mifare/desfiretest.c b/client/src/mifare/desfiretest.c index b89963352..5de1b312d 100644 --- a/client/src/mifare/desfiretest.c +++ b/client/src/mifare/desfiretest.c @@ -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)