Relocate backdoor detection

This commit is contained in:
noproto 2024-08-28 09:26:59 -04:00
parent c0331ba2e2
commit 4c14594ebb
7 changed files with 144 additions and 147 deletions

View File

@ -496,7 +496,7 @@ NfcCommand mf_classic_poller_send_frame_callback(NfcGenericEventEx event, void*
MfClassicKey key = {
.data = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
};
error = mf_classic_poller_auth(instance, 0, &key, MfClassicKeyTypeA, NULL);
error = mf_classic_poller_auth(instance, 0, &key, MfClassicKeyTypeA, NULL, false);
frame_test->state = (error == MfClassicErrorNone) ?
NfcTestMfClassicSendFrameTestStateReadBlock :
NfcTestMfClassicSendFrameTestStateFail;

View File

@ -8,6 +8,9 @@
#define MF_CLASSIC_MAX_BUFF_SIZE (64)
const MfClassicKey auth1_backdoor_key = {.data = {0xa3, 0x16, 0x67, 0xa8, 0xce, 0xc1}};
const MfClassicKey auth2_backdoor_key = {.data = {0xa3, 0x96, 0xef, 0xa4, 0xe2, 0x4f}};
typedef NfcCommand (*MfClassicPollerReadHandler)(MfClassicPoller* instance);
MfClassicPoller* mf_classic_poller_alloc(Iso14443_3aPoller* iso14443_3a_poller) {
@ -86,7 +89,8 @@ NfcCommand mf_classic_poller_handler_detect_type(MfClassicPoller* instance) {
iso14443_3a_copy(
instance->data->iso14443_3a_data,
iso14443_3a_poller_get_data(instance->iso14443_3a_poller));
MfClassicError error = mf_classic_poller_get_nt(instance, 254, MfClassicKeyTypeA, NULL);
MfClassicError error =
mf_classic_poller_get_nt(instance, 254, MfClassicKeyTypeA, NULL, false);
if(error == MfClassicErrorNone) {
instance->data->type = MfClassicType4k;
instance->state = MfClassicPollerStateStart;
@ -96,7 +100,8 @@ NfcCommand mf_classic_poller_handler_detect_type(MfClassicPoller* instance) {
instance->current_type_check = MfClassicType1k;
}
} else if(instance->current_type_check == MfClassicType1k) {
MfClassicError error = mf_classic_poller_get_nt(instance, 62, MfClassicKeyTypeA, NULL);
MfClassicError error =
mf_classic_poller_get_nt(instance, 62, MfClassicKeyTypeA, NULL, false);
if(error == MfClassicErrorNone) {
instance->data->type = MfClassicType1k;
FURI_LOG_D(TAG, "1K detected");
@ -122,7 +127,7 @@ NfcCommand mf_classic_poller_handler_start(MfClassicPoller* instance) {
if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeDictAttack) {
mf_classic_copy(instance->data, instance->mfc_event_data.poller_mode.data);
instance->state = MfClassicPollerStateRequestKey;
instance->state = MfClassicPollerStateAnalyzeBackdoor;
} else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeRead) {
instance->state = MfClassicPollerStateRequestReadSector;
} else if(instance->mfc_event_data.poller_mode.mode == MfClassicPollerModeWrite) {
@ -236,7 +241,7 @@ NfcCommand mf_classic_poller_handler_read_block(MfClassicPoller* instance) {
do {
// Authenticate to sector
error = mf_classic_poller_auth(
instance, write_ctx->current_block, auth_key, write_ctx->key_type_read, NULL);
instance, write_ctx->current_block, auth_key, write_ctx->key_type_read, NULL, false);
if(error != MfClassicErrorNone) {
FURI_LOG_D(TAG, "Failed to auth to block %d", write_ctx->current_block);
instance->state = MfClassicPollerStateFail;
@ -294,7 +299,12 @@ NfcCommand mf_classic_poller_handler_write_block(MfClassicPoller* instance) {
// Reauth if necessary
if(write_ctx->need_halt_before_write) {
error = mf_classic_poller_auth(
instance, write_ctx->current_block, auth_key, write_ctx->key_type_write, NULL);
instance,
write_ctx->current_block,
auth_key,
write_ctx->key_type_write,
NULL,
false);
if(error != MfClassicErrorNone) {
FURI_LOG_D(
TAG, "Failed to auth to block %d for writing", write_ctx->current_block);
@ -403,8 +413,8 @@ NfcCommand mf_classic_poller_handler_write_value_block(MfClassicPoller* instance
MfClassicKey* key = (auth_key_type == MfClassicKeyTypeA) ? &write_ctx->sec_tr.key_a :
&write_ctx->sec_tr.key_b;
MfClassicError error =
mf_classic_poller_auth(instance, write_ctx->current_block, key, auth_key_type, NULL);
MfClassicError error = mf_classic_poller_auth(
instance, write_ctx->current_block, key, auth_key_type, NULL, false);
if(error != MfClassicErrorNone) break;
error = mf_classic_poller_value_cmd(instance, write_ctx->current_block, value_cmd, diff);
@ -468,7 +478,8 @@ NfcCommand mf_classic_poller_handler_request_read_sector_blocks(MfClassicPoller*
sec_read_ctx->current_block,
&sec_read_ctx->key,
sec_read_ctx->key_type,
NULL);
NULL,
false);
if(error != MfClassicErrorNone) break;
sec_read_ctx->auth_passed = true;
@ -505,6 +516,42 @@ NfcCommand mf_classic_poller_handler_request_read_sector_blocks(MfClassicPoller*
return command;
}
NfcCommand mf_classic_poller_handler_analyze_backdoor(MfClassicPoller* instance) {
NfcCommand command = NfcCommandReset;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
bool current_key_is_auth1 =
memcmp(dict_attack_ctx->current_key.data, auth1_backdoor_key.data, sizeof(MfClassicKey)) ==
0;
bool current_key_is_auth2 =
memcmp(dict_attack_ctx->current_key.data, auth2_backdoor_key.data, sizeof(MfClassicKey)) ==
0;
if(!current_key_is_auth1) {
dict_attack_ctx->current_key = auth2_backdoor_key;
} else if(current_key_is_auth2) {
dict_attack_ctx->current_key = auth1_backdoor_key;
}
// Attempt backdoor authentication
MfClassicError error = mf_classic_poller_auth(
instance, 0, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL, true);
bool backdoor_found = (error == MfClassicErrorNone) ? true : false;
if(backdoor_found) {
FURI_LOG_E(TAG, "Backdoor identified");
if(!current_key_is_auth1) {
dict_attack_ctx->backdoor = MfClassicBackdoorAuth2;
} else {
dict_attack_ctx->backdoor = MfClassicBackdoorAuth1;
}
instance->state = MfClassicPollerStateRequestKey;
} else if(current_key_is_auth2) {
dict_attack_ctx->backdoor = MfClassicBackdoorNone;
instance->state = MfClassicPollerStateRequestKey;
}
return command;
}
NfcCommand mf_classic_poller_handler_request_key(MfClassicPoller* instance) {
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
@ -535,7 +582,7 @@ NfcCommand mf_classic_poller_handler_auth_a(MfClassicPoller* instance) {
FURI_LOG_D(TAG, "Auth to block %d with key A: %06llx", block, key);
MfClassicError error = mf_classic_poller_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL);
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL, false);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key A found");
mf_classic_set_key_found(
@ -574,7 +621,7 @@ NfcCommand mf_classic_poller_handler_auth_b(MfClassicPoller* instance) {
FURI_LOG_D(TAG, "Auth to block %d with key B: %06llx", block, key);
MfClassicError error = mf_classic_poller_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL);
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL, false);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key B found");
mf_classic_set_key_found(
@ -629,7 +676,8 @@ NfcCommand mf_classic_poller_handler_read_sector(MfClassicPoller* instance) {
block_num,
&dict_attack_ctx->current_key,
dict_attack_ctx->current_key_type,
NULL);
NULL,
false);
if(error != MfClassicErrorNone) {
instance->state = MfClassicPollerStateNextSector;
FURI_LOG_W(TAG, "Failed to re-auth. Go to next sector");
@ -742,7 +790,7 @@ NfcCommand mf_classic_poller_handler_key_reuse_auth_key_a(MfClassicPoller* insta
FURI_LOG_D(TAG, "Key attack auth to block %d with key A: %06llx", block, key);
MfClassicError error = mf_classic_poller_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL);
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeA, NULL, false);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key A found");
mf_classic_set_key_found(
@ -778,7 +826,7 @@ NfcCommand mf_classic_poller_handler_key_reuse_auth_key_b(MfClassicPoller* insta
FURI_LOG_D(TAG, "Key attack auth to block %d with key B: %06llx", block, key);
MfClassicError error = mf_classic_poller_auth(
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL);
instance, block, &dict_attack_ctx->current_key, MfClassicKeyTypeB, NULL, false);
if(error == MfClassicErrorNone) {
FURI_LOG_I(TAG, "Key B found");
mf_classic_set_key_found(
@ -817,7 +865,8 @@ NfcCommand mf_classic_poller_handler_key_reuse_read_sector(MfClassicPoller* inst
block_num,
&dict_attack_ctx->current_key,
dict_attack_ctx->current_key_type,
NULL);
NULL,
false);
if(error != MfClassicErrorNone) {
instance->state = MfClassicPollerStateKeyReuseStart;
break;
@ -905,92 +954,12 @@ NfcCommand mf_classic_poller_handler_nested_analyze_prng(MfClassicPoller* instan
return command;
}
NfcCommand mf_classic_poller_handler_nested_analyze_backdoor(MfClassicPoller* instance) {
// Can use on more than S variant as a free key for Nested
NfcCommand command = NfcCommandReset;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
uint8_t block =
mf_classic_get_first_block_num_of_sector(dict_attack_ctx->nested_known_key_sector);
uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);
MfClassicAuthContext auth_ctx = {};
MfClassicNt nt = {};
MfClassicKey auth_backdoor_key;
if(dict_attack_ctx->nested_target_key == 1) {
auth_backdoor_key = (MfClassicKey){
.data = {0xa3, 0x96, 0xef, 0xa4, 0xe2, 0x4f}}; // auth2 backdoor key, more common
} else {
auth_backdoor_key =
(MfClassicKey){.data = {0xa3, 0x16, 0x67, 0xa8, 0xce, 0xc1}}; // auth1 backdoor key
}
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->nested_known_key,
dict_attack_ctx->nested_known_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->nested_known_key_type == MfClassicKeyTypeB) ?
MF_CLASSIC_CMD_BACKDOOR_AUTH_KEY_B :
MF_CLASSIC_CMD_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, auth_backdoor_key);
backdoor_found = is_weak_prng_nonce(decrypted_nt_enc);
} while(false);
if(backdoor_found) {
FURI_LOG_E(TAG, "Backdoor identified");
if(dict_attack_ctx->nested_target_key == 1) {
dict_attack_ctx->backdoor = MfClassicBackdoorAuth2;
} else {
dict_attack_ctx->backdoor = MfClassicBackdoorAuth1;
}
} else if(dict_attack_ctx->nested_target_key == 0) {
dict_attack_ctx->backdoor = MfClassicBackdoorNone;
}
instance->state = MfClassicPollerStateNestedController;
return command;
}
NfcCommand mf_classic_poller_handler_nested_collect_nt(MfClassicPoller* instance) {
NfcCommand command = NfcCommandReset;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
MfClassicNt nt = {};
MfClassicError error = mf_classic_poller_get_nt(instance, 0, MfClassicKeyTypeA, &nt);
MfClassicError error = mf_classic_poller_get_nt(instance, 0, MfClassicKeyTypeA, &nt, false);
if(error != MfClassicErrorNone) {
dict_attack_ctx->prng_type = MfClassicPrngTypeNoTag;
@ -1021,8 +990,7 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance)
// TODO: Check if we have already identified the tag as static encrypted
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
uint8_t nt_enc_calibration_cnt = 21;
uint32_t nt_enc_temp_arr[nt_enc_calibration_cnt];
uint32_t nt_enc_temp_arr[MF_CLASSIC_NESTED_CALIBRATION_COUNT];
dict_attack_ctx->d_min = UINT16_MAX;
dict_attack_ctx->d_max = 0;
@ -1044,7 +1012,8 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance)
block,
&dict_attack_ctx->nested_known_key,
dict_attack_ctx->nested_known_key_type,
&auth_ctx);
&auth_ctx,
false);
if(error != MfClassicErrorNone) {
FURI_LOG_E(TAG, "Failed to perform full authentication");
@ -1057,7 +1026,7 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance)
nt_prev = bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));
// Step 2: Perform nested authentication multiple times
for(uint8_t collection_cycle = 0; collection_cycle < nt_enc_calibration_cnt;
for(uint8_t collection_cycle = 0; collection_cycle < MF_CLASSIC_NESTED_CALIBRATION_COUNT;
collection_cycle++) {
error = mf_classic_poller_auth_nested(
instance,
@ -1065,6 +1034,7 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance)
&dict_attack_ctx->nested_known_key,
dict_attack_ctx->nested_known_key_type,
&auth_ctx,
false,
false);
if(error != MfClassicErrorNone) {
@ -1108,7 +1078,7 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance)
// Find the distance between each nonce
FURI_LOG_E(TAG, "Calculating distance between nonces");
uint64_t known_key = bit_lib_bytes_to_num_be(dict_attack_ctx->nested_known_key.data, 6);
for(uint32_t collection_cycle = 0; collection_cycle < nt_enc_calibration_cnt;
for(uint32_t collection_cycle = 0; collection_cycle < MF_CLASSIC_NESTED_CALIBRATION_COUNT;
collection_cycle++) {
bool found = false;
uint32_t decrypted_nt_enc = decrypt_nt_enc(
@ -1220,7 +1190,8 @@ NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* inst
block,
&dict_attack_ctx->nested_known_key,
dict_attack_ctx->nested_known_key_type,
&auth_ctx);
&auth_ctx,
false);
if(error != MfClassicErrorNone) {
FURI_LOG_E(TAG, "Failed to perform full authentication");
@ -1242,6 +1213,7 @@ NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* inst
&dict_attack_ctx->nested_known_key,
dict_attack_ctx->nested_known_key_type,
&auth_ctx,
false,
false);
if(error != MfClassicErrorNone) {
@ -1259,6 +1231,7 @@ NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* inst
&dict_attack_ctx->nested_known_key,
target_key_type,
&auth_ctx,
false,
true);
if(nt_enc_collected != (nt_enc_per_collection - 1)) {
@ -1422,7 +1395,8 @@ NfcCommand mf_classic_poller_handler_nested_dict_attack(MfClassicPoller* instanc
block,
&dict_attack_ctx->nested_known_key,
dict_attack_ctx->nested_known_key_type,
&auth_ctx);
&auth_ctx,
false);
if(error != MfClassicErrorNone) {
FURI_LOG_E(TAG, "Failed to perform full authentication");
@ -1439,6 +1413,7 @@ NfcCommand mf_classic_poller_handler_nested_dict_attack(MfClassicPoller* instanc
&dict_attack_ctx->nested_known_key,
target_key_type,
&auth_ctx,
false,
true);
if(error != MfClassicErrorNone) {
@ -1719,16 +1694,15 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance
if(dict_attack_ctx->mf_classic_user_dict) {
keys_dict_free(dict_attack_ctx->mf_classic_user_dict);
}
dict_attack_ctx->nested_target_key = 0;
if(mf_classic_is_card_read(instance->data)) {
// All keys have been collected
FURI_LOG_E(TAG, "All keys collected and sectors read");
dict_attack_ctx->nested_target_key = 0;
dict_attack_ctx->nested_phase = MfClassicNestedPhaseFinished;
instance->state = MfClassicPollerStateSuccess;
return command;
}
dict_attack_ctx->nested_target_key = 2; // Backdoor keys
dict_attack_ctx->nested_phase = MfClassicNestedPhaseAnalyzeBackdoor;
dict_attack_ctx->nested_phase = MfClassicNestedPhaseCalibrate;
instance->state = MfClassicPollerStateNestedController;
return command;
}
@ -1749,15 +1723,6 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance
instance->state = MfClassicPollerStateNestedDictAttack;
return command;
}
// Analyze tag for NXP/Fudan backdoor
if(dict_attack_ctx->backdoor == MfClassicBackdoorUnknown) {
dict_attack_ctx->nested_target_key--;
instance->state = MfClassicPollerStateNestedAnalyzeBackdoor;
return command;
} else if(dict_attack_ctx->nested_phase == MfClassicNestedPhaseAnalyzeBackdoor) {
dict_attack_ctx->nested_target_key = 0;
dict_attack_ctx->nested_phase = MfClassicNestedPhaseCalibrate;
}
// TODO: Need to think about how this works for NXP/Fudan backdoored tags.
// We could reset the .calibration field every sector to re-calibrate. Calibration function handles backdoor calibration too.
// Calibration
@ -1843,6 +1808,7 @@ static const MfClassicPollerReadHandler
[MfClassicPollerStateWriteBlock] = mf_classic_poller_handler_write_block,
[MfClassicPollerStateWriteValueBlock] = mf_classic_poller_handler_write_value_block,
[MfClassicPollerStateNextSector] = mf_classic_poller_handler_next_sector,
[MfClassicPollerStateAnalyzeBackdoor] = mf_classic_poller_handler_analyze_backdoor,
[MfClassicPollerStateRequestKey] = mf_classic_poller_handler_request_key,
[MfClassicPollerStateRequestReadSector] = mf_classic_poller_handler_request_read_sector,
[MfClassicPollerStateReadSectorBlocks] =
@ -1857,8 +1823,6 @@ static const MfClassicPollerReadHandler
[MfClassicPollerStateKeyReuseAuthKeyB] = mf_classic_poller_handler_key_reuse_auth_key_b,
[MfClassicPollerStateKeyReuseReadSector] = mf_classic_poller_handler_key_reuse_read_sector,
[MfClassicPollerStateNestedAnalyzePRNG] = mf_classic_poller_handler_nested_analyze_prng,
[MfClassicPollerStateNestedAnalyzeBackdoor] =
mf_classic_poller_handler_nested_analyze_backdoor,
[MfClassicPollerStateNestedCalibrate] = mf_classic_poller_handler_nested_calibrate,
[MfClassicPollerStateNestedCollectNt] = mf_classic_poller_handler_nested_collect_nt,
[MfClassicPollerStateNestedController] = mf_classic_poller_handler_nested_controller,

View File

@ -170,13 +170,15 @@ typedef struct {
* @param[in] block_num block number for authentication.
* @param[in] key_type key type to be used for authentication.
* @param[out] nt pointer to the MfClassicNt structure to be filled with nonce data.
* @param[in] backdoor_auth flag indicating if backdoor authentication is used.
* @return MfClassicErrorNone on success, an error code on failure.
*/
MfClassicError mf_classic_poller_get_nt(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt);
MfClassicNt* nt,
bool backdoor_auth);
/**
* @brief Collect tag nonce during nested authentication.
@ -189,13 +191,15 @@ MfClassicError mf_classic_poller_get_nt(
* @param[in] block_num block number for authentication.
* @param[in] key_type key type to be used for authentication.
* @param[out] nt pointer to the MfClassicNt structure to be filled with nonce data.
* @param[in] backdoor_auth flag indicating if backdoor authentication is used.
* @return MfClassicErrorNone on success, an error code on failure.
*/
MfClassicError mf_classic_poller_get_nt_nested(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt);
MfClassicNt* nt,
bool backdoor_auth);
/**
* @brief Perform authentication.
@ -210,6 +214,7 @@ MfClassicError mf_classic_poller_get_nt_nested(
* @param[in] key key to be used for authentication.
* @param[in] key_type key type to be used for authentication.
* @param[out] data pointer to MfClassicAuthContext structure to be filled with authentication data.
* @param[in] backdoor_auth flag indicating if backdoor authentication is used.
* @return MfClassicErrorNone on success, an error code on failure.
*/
MfClassicError mf_classic_poller_auth(
@ -217,20 +222,22 @@ MfClassicError mf_classic_poller_auth(
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data);
MfClassicAuthContext* data,
bool backdoor_auth);
/**
* @brief Perform nested authentication.
*
* Must ONLY be used inside the callback function.
*
* Perform nested authentication as specified in Mf Classic protocol.
* Perform nested authentication as specified in Mf Classic protocol.
*
* @param[in, out] instance pointer to the instance to be used in the transaction.
* @param[in] block_num block number for authentication.
* @param[in] key key to be used for authentication.
* @param[in] key_type key type to be used for authentication.
* @param[out] data pointer to MfClassicAuthContext structure to be filled with authentication data.
* @param[in] backdoor_auth flag indicating if backdoor authentication is used.
* @param[in] early_ret return immediately after receiving encrypted nonce.
* @return MfClassicErrorNone on success, an error code on failure.
*/
@ -240,6 +247,7 @@ MfClassicError mf_classic_poller_auth_nested(
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data,
bool backdoor_auth,
bool early_ret);
/**

View File

@ -38,13 +38,20 @@ static MfClassicError mf_classic_poller_get_nt_common(
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt,
bool is_nested) {
bool is_nested,
bool backdoor_auth) {
MfClassicError ret = MfClassicErrorNone;
Iso14443_3aError error = Iso14443_3aErrorNone;
do {
uint8_t auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_AUTH_KEY_B :
MF_CLASSIC_CMD_AUTH_KEY_A;
uint8_t auth_type;
if(!backdoor_auth) {
auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_AUTH_KEY_B :
MF_CLASSIC_CMD_AUTH_KEY_A;
} else {
auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_BACKDOOR_AUTH_KEY_B :
MF_CLASSIC_CMD_BACKDOOR_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));
@ -89,29 +96,33 @@ MfClassicError mf_classic_poller_get_nt(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt) {
MfClassicNt* nt,
bool backdoor_auth) {
furi_check(instance);
return mf_classic_poller_get_nt_common(instance, block_num, key_type, nt, false);
return mf_classic_poller_get_nt_common(
instance, block_num, key_type, nt, false, backdoor_auth);
}
MfClassicError mf_classic_poller_get_nt_nested(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKeyType key_type,
MfClassicNt* nt) {
MfClassicNt* nt,
bool backdoor_auth) {
furi_check(instance);
return mf_classic_poller_get_nt_common(instance, block_num, key_type, nt, true);
return mf_classic_poller_get_nt_common(instance, block_num, key_type, nt, true, backdoor_auth);
}
static MfClassicError mf_classic_poller_auth_common(
MfClassicError mf_classic_poller_auth_common(
MfClassicPoller* instance,
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data,
bool is_nested,
bool backdoor_auth,
bool early_ret) {
MfClassicError ret = MfClassicErrorNone;
Iso14443_3aError error = Iso14443_3aErrorNone;
@ -123,9 +134,10 @@ static MfClassicError mf_classic_poller_auth_common(
MfClassicNt nt = {};
if(is_nested) {
ret = mf_classic_poller_get_nt_nested(instance, block_num, key_type, &nt);
ret =
mf_classic_poller_get_nt_nested(instance, block_num, key_type, &nt, backdoor_auth);
} else {
ret = mf_classic_poller_get_nt(instance, block_num, key_type, &nt);
ret = mf_classic_poller_get_nt(instance, block_num, key_type, &nt, backdoor_auth);
}
if(ret != MfClassicErrorNone) break;
if(data) {
@ -184,10 +196,12 @@ MfClassicError mf_classic_poller_auth(
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data) {
MfClassicAuthContext* data,
bool backdoor_auth) {
furi_check(instance);
furi_check(key);
return mf_classic_poller_auth_common(instance, block_num, key, key_type, data, false, false);
return mf_classic_poller_auth_common(
instance, block_num, key, key_type, data, false, backdoor_auth, false);
}
MfClassicError mf_classic_poller_auth_nested(
@ -196,11 +210,12 @@ MfClassicError mf_classic_poller_auth_nested(
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data,
bool backdoor_auth,
bool early_ret) {
furi_check(instance);
furi_check(key);
return mf_classic_poller_auth_common(
instance, block_num, key, key_type, data, true, early_ret);
instance, block_num, key, key_type, data, true, backdoor_auth, early_ret);
}
MfClassicError mf_classic_poller_halt(MfClassicPoller* instance) {

View File

@ -19,6 +19,7 @@ extern "C" {
#define MF_CLASSIC_NESTED_ANALYZE_NT_COUNT (5)
#define MF_CLASSIC_NESTED_HARD_MINIMUM (3)
#define MF_CLASSIC_NESTED_RETRY_MAXIMUM (20)
#define MF_CLASSIC_NESTED_CALIBRATION_COUNT (21)
#define MF_CLASSIC_NESTED_LOGS_FILE_NAME ".nested.log"
#define MF_CLASSIC_NESTED_SYSTEM_DICT_FILE_NAME "mf_classic_dict_nested.nfc"
#define MF_CLASSIC_NESTED_USER_DICT_FILE_NAME "mf_classic_dict_user_nested.nfc"
@ -28,6 +29,9 @@ extern "C" {
#define MF_CLASSIC_NESTED_USER_DICT_PATH \
(NFC_ASSETS_FOLDER "/" MF_CLASSIC_NESTED_USER_DICT_FILE_NAME)
extern const MfClassicKey auth1_backdoor_key;
extern const MfClassicKey auth2_backdoor_key;
typedef enum {
MfClassicAuthStateIdle,
MfClassicAuthStatePassed,
@ -94,6 +98,7 @@ typedef enum {
// Dict attack states
MfClassicPollerStateNextSector,
MfClassicPollerStateAnalyzeBackdoor,
MfClassicPollerStateRequestKey,
MfClassicPollerStateReadSector,
MfClassicPollerStateAuthKeyA,
@ -108,7 +113,6 @@ typedef enum {
// Enhanced dictionary attack states
MfClassicPollerStateNestedAnalyzePRNG,
MfClassicPollerStateNestedAnalyzeBackdoor,
MfClassicPollerStateNestedCalibrate,
MfClassicPollerStateNestedCollectNt,
MfClassicPollerStateNestedController,
@ -137,6 +141,7 @@ typedef struct {
bool auth_passed;
uint16_t current_block;
uint8_t reuse_key_sector;
MfClassicBackdoor backdoor;
// Enhanced dictionary attack and nested nonce collection
MfClassicNestedPhase nested_phase;
MfClassicKey nested_known_key;
@ -145,7 +150,6 @@ typedef struct {
uint16_t nested_target_key;
MfClassicNestedNonceArray nested_nonce;
MfClassicPrngType prng_type;
MfClassicBackdoor backdoor;
bool static_encrypted;
bool calibrated;
uint16_t d_min;

View File

@ -37,7 +37,8 @@ static MfClassicError mf_classic_poller_collect_nt_handler(
poller,
data->collect_nt_context.block,
data->collect_nt_context.key_type,
&data->collect_nt_context.nt);
&data->collect_nt_context.nt,
false);
}
static MfClassicError
@ -47,7 +48,8 @@ static MfClassicError
data->auth_context.block_num,
&data->auth_context.key,
data->auth_context.key_type,
&data->auth_context);
&data->auth_context,
false);
}
static MfClassicError mf_classic_poller_read_block_handler(
@ -61,7 +63,8 @@ static MfClassicError mf_classic_poller_read_block_handler(
data->read_block_context.block_num,
&data->read_block_context.key,
data->read_block_context.key_type,
NULL);
NULL,
false);
if(error != MfClassicErrorNone) break;
error = mf_classic_poller_read_block(
@ -87,7 +90,8 @@ static MfClassicError mf_classic_poller_write_block_handler(
data->read_block_context.block_num,
&data->read_block_context.key,
data->read_block_context.key_type,
NULL);
NULL,
false);
if(error != MfClassicErrorNone) break;
error = mf_classic_poller_write_block(
@ -113,7 +117,8 @@ static MfClassicError mf_classic_poller_read_value_handler(
data->read_value_context.block_num,
&data->read_value_context.key,
data->read_value_context.key_type,
NULL);
NULL,
false);
if(error != MfClassicErrorNone) break;
MfClassicBlock block = {};
@ -144,7 +149,8 @@ static MfClassicError mf_classic_poller_change_value_handler(
data->change_value_context.block_num,
&data->change_value_context.key,
data->change_value_context.key_type,
NULL);
NULL,
false);
if(error != MfClassicErrorNone) break;
error = mf_classic_poller_value_cmd(

View File

@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,72.1,,
Version,+,73.0,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
@ -2515,10 +2515,10 @@ Function,+,mf_classic_is_sector_read,_Bool,"const MfClassicData*, uint8_t"
Function,+,mf_classic_is_sector_trailer,_Bool,uint8_t
Function,+,mf_classic_is_value_block,_Bool,"MfClassicSectorTrailer*, uint8_t"
Function,+,mf_classic_load,_Bool,"MfClassicData*, FlipperFormat*, uint32_t"
Function,+,mf_classic_poller_auth,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*"
Function,+,mf_classic_poller_auth_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*, _Bool"
Function,+,mf_classic_poller_get_nt,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*"
Function,+,mf_classic_poller_get_nt_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*"
Function,+,mf_classic_poller_auth,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*, _Bool"
Function,+,mf_classic_poller_auth_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*, _Bool, _Bool"
Function,+,mf_classic_poller_get_nt,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*, _Bool"
Function,+,mf_classic_poller_get_nt_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*, _Bool"
Function,+,mf_classic_poller_halt,MfClassicError,MfClassicPoller*
Function,+,mf_classic_poller_read_block,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicBlock*"
Function,+,mf_classic_poller_send_custom_parity_frame,MfClassicError,"MfClassicPoller*, const BitBuffer*, BitBuffer*, uint32_t"

1 entry status name type params
2 Version + 72.1 73.0
3 Header + applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h
4 Header + applications/services/bt/bt_service/bt.h
5 Header + applications/services/bt/bt_service/bt_keys_storage.h
2515 Function + mf_classic_is_sector_trailer _Bool uint8_t
2516 Function + mf_classic_is_value_block _Bool MfClassicSectorTrailer*, uint8_t
2517 Function + mf_classic_load _Bool MfClassicData*, FlipperFormat*, uint32_t
2518 Function + mf_classic_poller_auth MfClassicError MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext* MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*, _Bool
2519 Function + mf_classic_poller_auth_nested MfClassicError MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*, _Bool MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*, _Bool, _Bool
2520 Function + mf_classic_poller_get_nt MfClassicError MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt* MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*, _Bool
2521 Function + mf_classic_poller_get_nt_nested MfClassicError MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt* MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*, _Bool
2522 Function + mf_classic_poller_halt MfClassicError MfClassicPoller*
2523 Function + mf_classic_poller_read_block MfClassicError MfClassicPoller*, uint8_t, MfClassicBlock*
2524 Function + mf_classic_poller_send_custom_parity_frame MfClassicError MfClassicPoller*, const BitBuffer*, BitBuffer*, uint32_t