Merge pull request #1359 from merlokk/desfire4

Desfire encoded channel for d40/ev1
This commit is contained in:
Oleg Moiseenko 2021-07-09 14:35:58 +03:00 committed by GitHub
commit 60d132fcc0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 157 additions and 53 deletions

View file

@ -976,6 +976,12 @@ void desfire_crc32_append(uint8_t *data, const size_t len) {
crc32_ex(data, len, data + len);
}
bool desfire_crc32_check(uint8_t *data, const size_t len, uint8_t *crc) {
uint8_t ccrc[4] = {0};
desfire_crc32(data, len, ccrc);
return (memcmp(ccrc, crc, 4) == 0);
}
void iso14443a_crc_append(uint8_t *data, size_t len) {
return compute_crc(CRC_14443_A, data, len, data + len, data + len + 1);
}
@ -983,3 +989,9 @@ void iso14443a_crc_append(uint8_t *data, size_t len) {
void iso14443a_crc(uint8_t *data, size_t len, uint8_t *pbtCrc) {
return compute_crc(CRC_14443_A, data, len, pbtCrc, pbtCrc + 1);
}
bool iso14443a_crc_check(uint8_t *data, const size_t len, uint8_t *crc) {
uint8_t ccrc[2] = {0};
iso14443a_crc(data, len, ccrc);
return (memcmp(ccrc, crc, 2) == 0);
}

View file

@ -149,6 +149,8 @@ void mifare_kdf_an10922(const desfirekey_t key, const uint8_t *data, size_t len)
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);
void iso14443a_crc_append(uint8_t *data, size_t len);
void iso14443a_crc(uint8_t *data, size_t len, uint8_t *pbtCrc);
bool iso14443a_crc_check(uint8_t *data, const size_t len, uint8_t *crc);
#endif

View file

@ -872,6 +872,16 @@ int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel
return PM3_SUCCESS;
}
int DesfireGetUID(DesfireContext *dctx, uint8_t *resp, size_t *resplen) {
uint8_t respcode = 0xff;
int res = DesfireExchange(dctx, MFDES_GET_UID, NULL, 0, &respcode, resp, resplen);
if (res != PM3_SUCCESS)
return res;
if (respcode != MFDES_S_OPERATION_OK)
return PM3_EAPDU_FAIL;
return PM3_SUCCESS;
}
int DesfireGetAIDList(DesfireContext *dctx, uint8_t *resp, size_t *resplen) {
uint8_t respcode = 0xff;
int res = DesfireExchange(dctx, MFDES_GET_APPLICATION_IDS, NULL, 0, &respcode, resp, resplen);

View file

@ -39,6 +39,7 @@ int DesfireSelectAIDHex(DesfireContext *ctx, uint32_t aid1, bool select_two, uin
int DesfireAuthenticate(DesfireContext *dctx, DesfireSecureChannel secureChannel);
int DesfireGetUID(DesfireContext *dctx, uint8_t *resp, size_t *resplen);
int DesfireGetAIDList(DesfireContext *dctx, uint8_t *resp, size_t *resplen);
int DesfireGetDFList(DesfireContext *dctx, uint8_t *resp, size_t *resplen);

View file

@ -56,6 +56,8 @@ void DesfireClearSession(DesfireContext *ctx) {
memset(ctx->sessionKeyMAC, 0, sizeof(ctx->sessionKeyMAC));
memset(ctx->sessionKeyEnc, 0, sizeof(ctx->sessionKeyEnc));
memset(ctx->lastIV, 0, sizeof(ctx->lastIV));
ctx->lastCommand = 0;
ctx->lastRequestZeroLen = false;
ctx->cntrTx = 0;
ctx->cntrRx = 0;
memset(ctx->TI, 0, sizeof(ctx->TI));
@ -107,6 +109,42 @@ size_t DesfireGetMACLength(DesfireContext *ctx) {
return mac_length;
}
size_t DesfireSearchCRCPos(uint8_t *data, size_t datalen, uint8_t respcode, uint8_t crclen) {
size_t crcpos = datalen - 1;
while (crcpos > 0)
if (data[crcpos] == 0)
crcpos--;
else
break;
crcpos++; // crc may be 0x00000000 or 0x0000
if (crcpos < crclen) {
PrintAndLogEx(WARNING, "No space for crc. pos: %d", crcpos);
return 0;
}
uint8_t crcdata[1024] = {0};
size_t crcposfound = 0;
for (int i = 0; i < crclen + 1; i++) {
if (crcpos - i == 0)
break;
if (crcpos - i + crclen > datalen)
continue;
memcpy(crcdata, data, crcpos - i);
crcdata[crcpos - i] = respcode;
bool res;
if (crclen == 4)
res = desfire_crc32_check(crcdata, crcpos - i + 1, &data[crcpos - i]);
else
res = iso14443a_crc_check(data, crcpos - i, &data[crcpos - i]);
if (res) {
crcposfound = crcpos - i;
}
}
return crcposfound;
}
static void DesfireCryptoEncDecSingleBlock(uint8_t *key, DesfireCryptoAlgorythm keyType, uint8_t *data, uint8_t *dstdata, uint8_t *ivect, bool dir_to_send, bool encode) {
size_t block_size = desfire_get_key_block_length(keyType);
uint8_t sdata[MAX_CRYPTO_BLOCK_SIZE] = {0};
@ -163,13 +201,14 @@ static void DesfireCryptoEncDecSingleBlock(uint8_t *key, DesfireCryptoAlgorythm
break;
}
memcpy(dstdata, edata, block_size);
if (dir_to_send) {
memcpy(ivect, edata, block_size);
} else {
bin_xor(edata, ivect, block_size);
memcpy(ivect, data, block_size);
}
memcpy(dstdata, edata, block_size);
}
void DesfireCryptoEncDecEx(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode, uint8_t *iv) {
@ -189,9 +228,9 @@ void DesfireCryptoEncDecEx(DesfireContext *ctx, bool use_session_key, uint8_t *s
size_t offset = 0;
while (offset < srcdatalen) {
if (use_session_key)
DesfireCryptoEncDecSingleBlock(ctx->sessionKeyMAC, ctx->keyType, srcdata + offset, data, xiv, encode, encode);
DesfireCryptoEncDecSingleBlock(ctx->sessionKeyMAC, ctx->keyType, srcdata + offset, data + offset, xiv, encode, encode);
else
DesfireCryptoEncDecSingleBlock(ctx->key, ctx->keyType, srcdata + offset, data, xiv, encode, encode);
DesfireCryptoEncDecSingleBlock(ctx->key, ctx->keyType, srcdata + offset, data + offset, xiv, encode, encode);
offset += block_size;
}
@ -265,6 +304,7 @@ void DesfireCryptoCMAC(DesfireContext *ctx, uint8_t *data, size_t len, uint8_t *
DesfireCryptoEncDec(ctx, true, buffer, len, NULL, true);
memcpy(cmac, ctx->IV, kbs);
if (cmac != NULL)
memcpy(cmac, ctx->IV, kbs);
}

View file

@ -78,6 +78,8 @@ typedef struct DesfireContextS {
uint8_t sessionKeyMAC[DESFIRE_MAX_KEY_SIZE];
uint8_t sessionKeyEnc[DESFIRE_MAX_KEY_SIZE]; // look at mifare4.h - mf4Session_t
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
@ -93,6 +95,7 @@ void DesfireSetKdf(DesfireContext *ctx, uint8_t kdfAlgo, uint8_t *kdfInput, uint
bool DesfireIsAuthenticated(DesfireContext *dctx);
size_t DesfireGetMACLength(DesfireContext *ctx);
size_t DesfireSearchCRCPos(uint8_t *data, size_t datalen, uint8_t respcode, uint8_t crclen);
void DesfireCryptoEncDec(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode);
void DesfireCryptoEncDecEx(DesfireContext *ctx, bool use_session_key, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, bool encode, uint8_t *iv);

View file

@ -40,15 +40,20 @@ static void DesfireSecureChannelEncodeD40(DesfireContext *ctx, uint8_t cmd, uint
rlen = padded_data_length(srcdatalen, desfire_get_key_block_length(ctx->keyType));
memcpy(data, srcdata, srcdatalen);
memset(ctx->IV, 0, sizeof(ctx->IV));
DesfireCryptoEncDec(ctx, true, data, rlen, NULL, true);
memcpy(dstdata, srcdata, srcdatalen);
memcpy(&dstdata[srcdatalen], ctx->IV, 4);
*dstdatalen = rlen;
break;
case DCMEncrypted:
if (srcdatalen == 0)
break;
rlen = padded_data_length(srcdatalen + 2, desfire_get_key_block_length(ctx->keyType)); // 2 - crc16
memcpy(data, srcdata, srcdatalen);
compute_crc(CRC_14443_A, data, srcdatalen, &data[srcdatalen], &data[srcdatalen + 1]);
memset(ctx->IV, 0, sizeof(ctx->IV));
DesfireCryptoEncDec(ctx, true, data, rlen, dstdata, true);
*dstdatalen = rlen;
break;
@ -60,31 +65,33 @@ static void DesfireSecureChannelEncodeD40(DesfireContext *ctx, uint8_t cmd, uint
static void DesfireSecureChannelEncodeEV1(DesfireContext *ctx, uint8_t cmd, uint8_t *srcdata, size_t srcdatalen, uint8_t *dstdata, size_t *dstdatalen) {
uint8_t data[1024] = {0};
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
// we calc MAC anyway
// if encypted channel and no data - we only calc MAC
if (ctx->commMode == DCMPlain || ctx->commMode == DCMMACed || (ctx->commMode == DCMEncrypted && srcdatalen == 0)) {
data[0] = cmd;
memcpy(&data[1], srcdata, srcdatalen);
uint8_t cmac[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0};
DesfireCryptoCMAC(ctx, data, srcdatalen + 1, cmac);
switch (ctx->commMode) {
case DCMPlain:
case DCMMACed:
data[0] = cmd;
memcpy(&data[1], srcdata, srcdatalen);
uint8_t cmac[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0};
DesfireCryptoCMAC(ctx, data, srcdatalen + 1, cmac);
memcpy(dstdata, srcdata, srcdatalen);
if (srcdatalen != 0 && ctx->commMode == DCMMACed) {
memcpy(&dstdata[srcdatalen], cmac, DesfireGetMACLength(ctx));
*dstdatalen = srcdatalen + DesfireGetMACLength(ctx);
}
break;
case DCMEncrypted:
break;
case DCMNone:
;
memcpy(dstdata, srcdata, srcdatalen);
if (srcdatalen != 0 && ctx->commMode == DCMMACed) {
memcpy(&dstdata[srcdatalen], cmac, DesfireGetMACLength(ctx));
*dstdatalen = srcdatalen + DesfireGetMACLength(ctx);
}
} else if (ctx->commMode == DCMEncrypted) {
} else {
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 == 0);
switch (ctx->secureChannel) {
case DACd40:
DesfireSecureChannelEncodeD40(ctx, cmd, srcdata, srcdatalen, dstdata, dstdatalen);
@ -110,6 +117,22 @@ static void DesfireSecureChannelDecodeD40(DesfireContext *ctx, uint8_t *srcdata,
break;
case DCMEncrypted:
if (srcdatalen < desfire_get_key_block_length(ctx->keyType)) {
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
return;
}
DesfireCryptoEncDec(ctx, true, srcdata, srcdatalen, dstdata, false);
//PrintAndLogEx(INFO, "decoded[%d]: %s", srcdatalen, sprint_hex(dstdata, srcdatalen));
size_t puredatalen = DesfireSearchCRCPos(dstdata, srcdatalen, respcode, 2);
if (puredatalen != 0) {
*dstdatalen = puredatalen;
} else {
PrintAndLogEx(WARNING, "CRC16 error.");
*dstdatalen = srcdatalen;
}
break;
case DCMPlain:
case DACNone:
@ -122,36 +145,49 @@ static void DesfireSecureChannelDecodeD40(DesfireContext *ctx, uint8_t *srcdata,
static void DesfireSecureChannelDecodeEV1(DesfireContext *ctx, uint8_t *srcdata, size_t srcdatalen, uint8_t respcode, uint8_t *dstdata, size_t *dstdatalen) {
uint8_t data[1024] = {0};
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
switch (ctx->commMode) {
case DCMPlain:
case DCMMACed:
if (srcdatalen < DesfireGetMACLength(ctx))
break;
memcpy(dstdata, srcdata, srcdatalen - DesfireGetMACLength(ctx));
*dstdatalen = srcdatalen - DesfireGetMACLength(ctx);
memcpy(data, srcdata, *dstdatalen);
data[*dstdatalen] = respcode;
uint8_t cmac[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0};
DesfireCryptoCMAC(ctx, data, *dstdatalen + 1, cmac);
if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) {
PrintAndLogEx(WARNING, "Received MAC is not match with calculated");
PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], desfire_get_key_block_length(ctx->keyType)));
PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, desfire_get_key_block_length(ctx->keyType)));
}
break;
case DCMEncrypted:
break;
case DACNone:
// if comm mode = plain --> response with MAC
// if request is not zero length --> response MAC
if (ctx->commMode == DCMPlain || ctx->commMode == DCMMACed || (ctx->commMode == DCMEncrypted && !ctx->lastRequestZeroLen)) {
if (srcdatalen < DesfireGetMACLength(ctx)) {
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
break;
return;
}
memcpy(dstdata, srcdata, srcdatalen - DesfireGetMACLength(ctx));
*dstdatalen = srcdatalen - DesfireGetMACLength(ctx);
memcpy(data, srcdata, *dstdatalen);
data[*dstdatalen] = respcode;
uint8_t cmac[DESFIRE_MAX_CRYPTO_BLOCK_SIZE] = {0};
DesfireCryptoCMAC(ctx, data, *dstdatalen + 1, cmac);
if (memcmp(&srcdata[*dstdatalen], cmac, DesfireGetMACLength(ctx)) != 0) {
PrintAndLogEx(WARNING, "Received MAC is not match with calculated");
PrintAndLogEx(INFO, " received MAC: %s", sprint_hex(&srcdata[*dstdatalen], desfire_get_key_block_length(ctx->keyType)));
PrintAndLogEx(INFO, " calculated MAC: %s", sprint_hex(cmac, desfire_get_key_block_length(ctx->keyType)));
}
} else if (ctx->commMode == DCMEncrypted) {
if (srcdatalen < desfire_get_key_block_length(ctx->keyType)) {
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
return;
}
DesfireCryptoEncDec(ctx, true, srcdata, srcdatalen, dstdata, false);
//PrintAndLogEx(INFO, "decoded[%d]: %s", srcdatalen, sprint_hex(dstdata, srcdatalen));
size_t puredatalen = DesfireSearchCRCPos(dstdata, srcdatalen, respcode, 4);
if (puredatalen != 0) {
*dstdatalen = puredatalen;
} else {
PrintAndLogEx(WARNING, "CRC32 error.");
*dstdatalen = srcdatalen;
}
} else {
memcpy(dstdata, srcdata, srcdatalen);
*dstdatalen = srcdatalen;
}
}