diff --git a/lib/nfc/protocols/mf_classic/mf_classic.h b/lib/nfc/protocols/mf_classic/mf_classic.h index 801ec1764..53dcf1a54 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic.h +++ b/lib/nfc/protocols/mf_classic/mf_classic.h @@ -6,14 +6,16 @@ extern "C" { #endif -#define MF_CLASSIC_CMD_AUTH_KEY_A (0x60U) -#define MF_CLASSIC_CMD_AUTH_KEY_B (0x61U) -#define MF_CLASSIC_CMD_READ_BLOCK (0x30U) -#define MF_CLASSIC_CMD_WRITE_BLOCK (0xA0U) -#define MF_CLASSIC_CMD_VALUE_DEC (0xC0U) -#define MF_CLASSIC_CMD_VALUE_INC (0xC1U) -#define MF_CLASSIC_CMD_VALUE_RESTORE (0xC2U) -#define MF_CLASSIC_CMD_VALUE_TRANSFER (0xB0U) +#define MF_CLASSIC_CMD_AUTH_KEY_A (0x60U) +#define MF_CLASSIC_CMD_AUTH_KEY_B (0x61U) +#define MF_CLASSIC_CMD_SE_BACKDOOR_AUTH_KEY_A (0x64U) +#define MF_CLASSIC_CMD_SE_BACKDOOR_AUTH_KEY_B (0x65U) +#define MF_CLASSIC_CMD_READ_BLOCK (0x30U) +#define MF_CLASSIC_CMD_WRITE_BLOCK (0xA0U) +#define MF_CLASSIC_CMD_VALUE_DEC (0xC0U) +#define MF_CLASSIC_CMD_VALUE_INC (0xC1U) +#define MF_CLASSIC_CMD_VALUE_RESTORE (0xC2U) +#define MF_CLASSIC_CMD_VALUE_TRANSFER (0xB0U) #define MF_CLASSIC_CMD_HALT_MSB (0x50) #define MF_CLASSIC_CMD_HALT_LSB (0x00) diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller.c b/lib/nfc/protocols/mf_classic/mf_classic_poller.c index f96590344..519958325 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller.c +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller.c @@ -891,32 +891,69 @@ NfcCommand mf_classic_poller_handler_nested_analyze_prng(MfClassicPoller* instan } NfcCommand mf_classic_poller_handler_nested_analyze_backdoor(MfClassicPoller* instance) { - NfcCommand command = NfcCommandContinue; - MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; - // TODO: Check for Fudan backdoor // Can use on more than S variant as a free key for Nested - /* - do { - uint8_t auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_AUTH_KEY_B : - MF_CLASSIC_CMD_AUTH_KEY_A; - uint8_t auth_cmd[2] = {auth_type, block_num}; - bit_buffer_copy_bytes(instance->tx_plain_buffer, auth_cmd, sizeof(auth_cmd)); + NfcCommand command = NfcCommandReset; + MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; - if(is_nested) { - iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); - crypto1_encrypt( - instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); - error = iso14443_3a_poller_txrx_custom_parity( - instance->iso14443_3a_poller, - instance->tx_encrypted_buffer, - instance->rx_plain_buffer, // NT gets decrypted by mf_classic_async_auth - MF_CLASSIC_FWT_FC); - if(error != Iso14443_3aErrorNone) { - ret = mf_classic_process_error(error); - break; - } - */ - dict_attack_ctx->backdoor = MfClassicBackdoorNone; + uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector); + uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data); + + MfClassicAuthContext auth_ctx = {}; + MfClassicNt nt = {}; + MfClassicKey fm11rf08s_backdoor_key = {.data = {0xa3, 0x96, 0xef, 0xa4, 0xe2, 0x4f}}; + MfClassicError error; + Iso14443_3aError iso_error; + bool backdoor_found = false; + + do { + // Step 1: Perform full authentication once + error = mf_classic_poller_auth( + instance, + block, + &dict_attack_ctx->current_key, + dict_attack_ctx->current_key_type, + &auth_ctx); + + if(error != MfClassicErrorNone) { + FURI_LOG_E(TAG, "Failed to perform full authentication"); + break; + } + + FURI_LOG_E(TAG, "Full authentication successful"); + + // Step 2: Attempt backdoor authentication + uint8_t auth_type = (dict_attack_ctx->current_key_type == MfClassicKeyTypeB) ? + MF_CLASSIC_CMD_SE_BACKDOOR_AUTH_KEY_B : + MF_CLASSIC_CMD_SE_BACKDOOR_AUTH_KEY_A; + uint8_t auth_cmd[2] = {auth_type, block}; + bit_buffer_copy_bytes(instance->tx_plain_buffer, auth_cmd, sizeof(auth_cmd)); + iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer); + crypto1_encrypt( + instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer); + iso_error = iso14443_3a_poller_txrx_custom_parity( + instance->iso14443_3a_poller, + instance->tx_encrypted_buffer, + instance->rx_plain_buffer, + MF_CLASSIC_FWT_FC); + if(iso_error != Iso14443_3aErrorNone) { + FURI_LOG_E(TAG, "Error during nested authentication"); + break; + } + if(bit_buffer_get_size_bytes(instance->rx_plain_buffer) != sizeof(MfClassicNt)) { + break; + } + bit_buffer_write_bytes(instance->rx_plain_buffer, nt.data, sizeof(MfClassicNt)); + uint32_t nt_enc = bit_lib_bytes_to_num_be(nt.data, sizeof(MfClassicNt)); + // Ensure the encrypted nt can be generated by the backdoor + uint32_t decrypted_nt_enc = decrypt_nt_enc(cuid, nt_enc, fm11rf08s_backdoor_key); + backdoor_found = is_weak_prng_nonce(decrypted_nt_enc); + } while(false); + if(backdoor_found) { + FURI_LOG_E(TAG, "Backdoor identified"); + dict_attack_ctx->backdoor = MfClassicBackdoorFM11RF08S; + } else { + dict_attack_ctx->backdoor = MfClassicBackdoorNone; + } instance->state = MfClassicPollerStateNestedController; return command; } @@ -1041,6 +1078,7 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance) bool found = false; uint32_t decrypted_nt_enc = decrypt_nt_enc(cuid, nt_enc_temp_arr[collection_cycle], dict_attack_ctx->current_key); + // TODO: Make sure we're not off-by-one here for(int i = 0; i < 65535; i++) { uint32_t nth_successor = prng_successor(nt_prev, i); if(nth_successor != decrypted_nt_enc) { diff --git a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h index d10401e76..0d797b1dc 100644 --- a/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h +++ b/lib/nfc/protocols/mf_classic/mf_classic_poller_i.h @@ -3,6 +3,7 @@ #include "mf_classic_poller.h" #include #include +#include "nfc/helpers/iso14443_crc.h" #include #include #include