mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 04:34:43 +04:00
MIFARE Classic Key Recovery Improvements (#3822)
* Initial structure for nonce collection * Nonce logging * Dictionary attack structure * Fix compilation * Identified method to reduce candidate states * Use EXT_PATH instead of ANY_PATH * Use median calibrated distance, collect parity bits * Modify parity collection * Fixed parity bit collection * Add note to fix nonce logging * Fix nonce logging * Clean redundant code * Fix valid_nonce * First attempt disambiguous nonce implementation * FM11RF08S backdoor detection * Initial accelerated dictionary attack for weak PRNGs * Refactor to nested dictionary attack * Renaming some variables * Hard PRNG support for accelerated dictionary attack * Update found keys, initial attempt * Update found keys, second attempt * Code cleanup * Misc bugfixes * Only use dicts in search_dicts_for_nonce_key if we have them * Collect nonces again * Should be detecting both backdoors now * Relocate backdoor detection * Hardnested support * Fix regression for regular nested attack * Backdoor read * Backdoor working up to calibration * Backdoor nested calibration * Don't recalibrate hard PRNG tags * Static encrypted nonce collection * Update TODO * NFC app UI updates, MVP * Bump f18 API version (all functions are NFC related) * Add new backdoor key, fix UI status update carrying over from previous read * Clear TODO line * Fix v1/v2 backdoor nonce collection * Speed up backdoor detection, alert on new backdoor * Add additional condition to backdoor check * I'll try freeing memory, that's a good trick! * Do not enter nested attack if card is already finished * Do not reset the poller between collected nonces * Clean up various issues * Fix Hardnested sector/key type logging * Add nested_target_key 64 to TODO * Implement progress bar for upgraded attacks in NFC app * Typo * Zero nested_target_key and msb_count on exit * Note TODO (malloc) * Dismiss duplicate nonces * Fix calibration (ensure values are within 3 standard deviations) * Log static * No nested dictionary attack re-entry * Note minor inefficiency * Uniformly use crypto1_ prefix for symbols in Crypto1 API * Fix include paths * Fix include paths cont * Support CUID dictionary * Fix log levels * Avoid storage errors, clean up temporary files * Handle invalid key candidates * Fix memory leak in static encrypted attack * Fix memory leak, use COUNT_OF macro * Use single call to free FuriString * Refactor enums to avoid redefinition * Fix multiple crashes and state machine logic * Fix inconsistent assignment of known key and known key type/sector * Backdoor known key logic still needs the current key * Larger data type for 4K support * Fix typo * Fix issue with resume logic * Mark TODOs for next PR * Remove redundant assignment * Fix size_t format specifier * Simplify auth_passed condition Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com> Co-authored-by: gornekich <n.gorbadey@gmail.com>
This commit is contained in:
@@ -543,6 +543,22 @@ void mf_classic_set_key_not_found(
|
||||
}
|
||||
}
|
||||
|
||||
MfClassicKey
|
||||
mf_classic_get_key(const MfClassicData* data, uint8_t sector_num, MfClassicKeyType key_type) {
|
||||
furi_check(data);
|
||||
furi_check(sector_num < mf_classic_get_total_sectors_num(data->type));
|
||||
furi_check(key_type == MfClassicKeyTypeA || key_type == MfClassicKeyTypeB);
|
||||
|
||||
const MfClassicSectorTrailer* sector_trailer =
|
||||
mf_classic_get_sector_trailer_by_sector(data, sector_num);
|
||||
|
||||
if(key_type == MfClassicKeyTypeA) {
|
||||
return sector_trailer->key_a;
|
||||
} else {
|
||||
return sector_trailer->key_b;
|
||||
}
|
||||
}
|
||||
|
||||
bool mf_classic_is_block_read(const MfClassicData* data, uint8_t block_num) {
|
||||
furi_check(data);
|
||||
|
||||
|
||||
@@ -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_BACKDOOR_AUTH_KEY_A (0x64U)
|
||||
#define MF_CLASSIC_CMD_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)
|
||||
@@ -211,6 +213,9 @@ void mf_classic_set_key_not_found(
|
||||
uint8_t sector_num,
|
||||
MfClassicKeyType key_type);
|
||||
|
||||
MfClassicKey
|
||||
mf_classic_get_key(const MfClassicData* data, uint8_t sector_num, MfClassicKeyType key_type);
|
||||
|
||||
bool mf_classic_is_block_read(const MfClassicData* data, uint8_t block_num);
|
||||
|
||||
void mf_classic_set_block_read(MfClassicData* data, uint8_t block_num, MfClassicBlock* block_data);
|
||||
|
||||
@@ -157,14 +157,17 @@ static MfClassicListenerCommand
|
||||
uint32_t nt_num =
|
||||
bit_lib_bytes_to_num_be(instance->auth_context.nt.data, sizeof(MfClassicNt));
|
||||
uint32_t secret_poller = ar_num ^ crypto1_word(instance->crypto, 0, 0);
|
||||
if(secret_poller != prng_successor(nt_num, 64)) {
|
||||
if(secret_poller != crypto1_prng_successor(nt_num, 64)) {
|
||||
FURI_LOG_T(
|
||||
TAG, "Wrong reader key: %08lX != %08lX", secret_poller, prng_successor(nt_num, 64));
|
||||
TAG,
|
||||
"Wrong reader key: %08lX != %08lX",
|
||||
secret_poller,
|
||||
crypto1_prng_successor(nt_num, 64));
|
||||
command = MfClassicListenerCommandSleep;
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t at_num = prng_successor(nt_num, 96);
|
||||
uint32_t at_num = crypto1_prng_successor(nt_num, 96);
|
||||
bit_lib_num_to_bytes_be(at_num, sizeof(uint32_t), instance->auth_context.at.data);
|
||||
bit_buffer_copy_bytes(
|
||||
instance->tx_plain_buffer, instance->auth_context.at.data, sizeof(MfClassicAr));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -44,9 +44,46 @@ typedef enum {
|
||||
typedef enum {
|
||||
MfClassicPollerModeRead, /**< Poller reading mode. */
|
||||
MfClassicPollerModeWrite, /**< Poller writing mode. */
|
||||
MfClassicPollerModeDictAttack, /**< Poller dictionary attack mode. */
|
||||
MfClassicPollerModeDictAttackStandard, /**< Poller dictionary attack mode. */
|
||||
MfClassicPollerModeDictAttackEnhanced, /**< Poller enhanced dictionary attack mode. */
|
||||
} MfClassicPollerMode;
|
||||
|
||||
/**
|
||||
* @brief MfClassic poller nested attack phase.
|
||||
*/
|
||||
typedef enum {
|
||||
MfClassicNestedPhaseNone, /**< No nested attack has taken place yet. */
|
||||
MfClassicNestedPhaseAnalyzePRNG, /**< Analyze nonces produced by the PRNG to determine if they fit a weak PRNG */
|
||||
MfClassicNestedPhaseDictAttack, /**< Search keys which match the expected PRNG properties and parity for collected nonces */
|
||||
MfClassicNestedPhaseDictAttackVerify, /**< Verify candidate keys by authenticating to the sector with the key */
|
||||
MfClassicNestedPhaseDictAttackResume, /**< Resume nested dictionary attack from the last tested (invalid) key */
|
||||
MfClassicNestedPhaseCalibrate, /**< Perform necessary calculations to recover the plaintext nonce during later collection phase (weak PRNG tags only) */
|
||||
MfClassicNestedPhaseRecalibrate, /**< Collect the next plaintext static encrypted nonce for backdoor static encrypted nonce nested attack */
|
||||
MfClassicNestedPhaseCollectNtEnc, /**< Log nonces collected during nested authentication for key recovery */
|
||||
MfClassicNestedPhaseFinished, /**< Nested attack has finished */
|
||||
} MfClassicNestedPhase;
|
||||
|
||||
/**
|
||||
* @brief MfClassic pseudorandom number generator (PRNG) type.
|
||||
*/
|
||||
typedef enum {
|
||||
MfClassicPrngTypeUnknown, // Tag not yet tested
|
||||
MfClassicPrngTypeNoTag, // No tag detected during test
|
||||
MfClassicPrngTypeWeak, // Weak PRNG, standard Nested
|
||||
MfClassicPrngTypeHard, // Hard PRNG, Hardnested
|
||||
} MfClassicPrngType;
|
||||
|
||||
/**
|
||||
* @brief MfClassic authentication backdoor type.
|
||||
*/
|
||||
typedef enum {
|
||||
MfClassicBackdoorUnknown, // Tag not yet tested
|
||||
MfClassicBackdoorNone, // No observed backdoor
|
||||
MfClassicBackdoorAuth1, // Tag responds to v1 auth backdoor
|
||||
MfClassicBackdoorAuth2, // Tag responds to v2 auth backdoor (sometimes static encrypted)
|
||||
MfClassicBackdoorAuth3, // Tag responds to v3 auth backdoor (static encrypted nonce)
|
||||
} MfClassicBackdoor;
|
||||
|
||||
/**
|
||||
* @brief MfClassic poller request mode event data.
|
||||
*
|
||||
@@ -77,6 +114,12 @@ typedef struct {
|
||||
uint8_t sectors_read; /**< Number of sectors read. */
|
||||
uint8_t keys_found; /**< Number of keys found. */
|
||||
uint8_t current_sector; /**< Current sector number. */
|
||||
MfClassicNestedPhase nested_phase; /**< Nested attack phase. */
|
||||
MfClassicPrngType prng_type; /**< PRNG (weak or hard). */
|
||||
MfClassicBackdoor backdoor; /**< Backdoor type. */
|
||||
uint16_t nested_target_key; /**< Target key for nested attack. */
|
||||
uint16_t
|
||||
msb_count; /**< Number of unique most significant bytes seen during Hardnested attack. */
|
||||
} MfClassicPollerEventDataUpdate;
|
||||
|
||||
/**
|
||||
@@ -170,13 +213,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 +234,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 +257,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 +265,23 @@ 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.
|
||||
*/
|
||||
MfClassicError mf_classic_poller_auth_nested(
|
||||
@@ -238,7 +289,9 @@ MfClassicError mf_classic_poller_auth_nested(
|
||||
uint8_t block_num,
|
||||
MfClassicKey* key,
|
||||
MfClassicKeyType key_type,
|
||||
MfClassicAuthContext* data);
|
||||
MfClassicAuthContext* data,
|
||||
bool backdoor_auth,
|
||||
bool early_ret);
|
||||
|
||||
/**
|
||||
* @brief Halt the tag.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include <nfc/helpers/iso14443_crc.h>
|
||||
|
||||
#define TAG "MfCLassicPoller"
|
||||
#define TAG "MfClassicPoller"
|
||||
|
||||
MfClassicError mf_classic_process_error(Iso14443_3aError error) {
|
||||
MfClassicError ret = MfClassicErrorNone;
|
||||
@@ -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,34 @@ 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 is_nested,
|
||||
bool backdoor_auth,
|
||||
bool early_ret) {
|
||||
MfClassicError ret = MfClassicErrorNone;
|
||||
Iso14443_3aError error = Iso14443_3aErrorNone;
|
||||
|
||||
@@ -122,14 +134,16 @@ 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) {
|
||||
data->nt = nt;
|
||||
}
|
||||
if(early_ret) break;
|
||||
|
||||
uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);
|
||||
uint64_t key_num = bit_lib_bytes_to_num_be(key->data, sizeof(MfClassicKey));
|
||||
@@ -182,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);
|
||||
return mf_classic_poller_auth_common(
|
||||
instance, block_num, key, key_type, data, false, backdoor_auth, false);
|
||||
}
|
||||
|
||||
MfClassicError mf_classic_poller_auth_nested(
|
||||
@@ -193,10 +209,13 @@ MfClassicError mf_classic_poller_auth_nested(
|
||||
uint8_t block_num,
|
||||
MfClassicKey* key,
|
||||
MfClassicKeyType key_type,
|
||||
MfClassicAuthContext* data) {
|
||||
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);
|
||||
return mf_classic_poller_auth_common(
|
||||
instance, block_num, key, key_type, data, true, backdoor_auth, early_ret);
|
||||
}
|
||||
|
||||
MfClassicError mf_classic_poller_halt(MfClassicPoller* instance) {
|
||||
|
||||
@@ -3,13 +3,40 @@
|
||||
#include "mf_classic_poller.h"
|
||||
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h>
|
||||
#include <bit_lib/bit_lib.h>
|
||||
#include <nfc/helpers/iso14443_crc.h>
|
||||
#include <nfc/helpers/crypto1.h>
|
||||
#include <stream/stream.h>
|
||||
#include <stream/buffered_file_stream.h>
|
||||
#include <toolbox/keys_dict.h>
|
||||
#include <helpers/nfc_util.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MF_CLASSIC_FWT_FC (60000)
|
||||
#define MF_CLASSIC_FWT_FC (60000)
|
||||
#define NFC_FOLDER EXT_PATH("nfc")
|
||||
#define NFC_ASSETS_FOLDER EXT_PATH("nfc/assets")
|
||||
#define MF_CLASSIC_NESTED_ANALYZE_NT_COUNT (5)
|
||||
#define MF_CLASSIC_NESTED_NT_HARD_MINIMUM (3)
|
||||
#define MF_CLASSIC_NESTED_RETRY_MAXIMUM (60)
|
||||
#define MF_CLASSIC_NESTED_HARD_RETRY_MAXIMUM (3)
|
||||
#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"
|
||||
#define MF_CLASSIC_NESTED_LOGS_FILE_PATH (NFC_FOLDER "/" MF_CLASSIC_NESTED_LOGS_FILE_NAME)
|
||||
#define MF_CLASSIC_NESTED_SYSTEM_DICT_PATH \
|
||||
(NFC_ASSETS_FOLDER "/" MF_CLASSIC_NESTED_SYSTEM_DICT_FILE_NAME)
|
||||
#define MF_CLASSIC_NESTED_USER_DICT_PATH \
|
||||
(NFC_ASSETS_FOLDER "/" MF_CLASSIC_NESTED_USER_DICT_FILE_NAME)
|
||||
#define SET_PACKED_BIT(arr, bit) ((arr)[(bit) / 8] |= (1 << ((bit) % 8)))
|
||||
#define GET_PACKED_BIT(arr, bit) ((arr)[(bit) / 8] & (1 << ((bit) % 8)))
|
||||
|
||||
extern const MfClassicKey auth1_backdoor_key;
|
||||
extern const MfClassicKey auth2_backdoor_key;
|
||||
extern const MfClassicKey auth3_backdoor_key;
|
||||
extern const uint16_t valid_sums[19];
|
||||
|
||||
typedef enum {
|
||||
MfClassicAuthStateIdle,
|
||||
@@ -21,6 +48,28 @@ typedef enum {
|
||||
MfClassicCardStateLost,
|
||||
} MfClassicCardState;
|
||||
|
||||
typedef struct {
|
||||
MfClassicKey key;
|
||||
MfClassicBackdoor type;
|
||||
} MfClassicBackdoorKeyPair;
|
||||
|
||||
extern const MfClassicBackdoorKeyPair mf_classic_backdoor_keys[];
|
||||
extern const size_t mf_classic_backdoor_keys_count;
|
||||
|
||||
typedef struct {
|
||||
uint32_t cuid; // Card UID
|
||||
uint8_t key_idx; // Key index
|
||||
uint32_t nt; // Nonce
|
||||
uint32_t nt_enc; // Encrypted nonce
|
||||
uint8_t par; // Parity
|
||||
uint16_t dist; // Distance
|
||||
} MfClassicNestedNonce;
|
||||
|
||||
typedef struct {
|
||||
MfClassicNestedNonce* nonces;
|
||||
size_t count;
|
||||
} MfClassicNestedNonceArray;
|
||||
|
||||
typedef enum {
|
||||
MfClassicPollerStateDetectType,
|
||||
MfClassicPollerStateStart,
|
||||
@@ -38,17 +87,29 @@ typedef enum {
|
||||
|
||||
// Dict attack states
|
||||
MfClassicPollerStateNextSector,
|
||||
MfClassicPollerStateAnalyzeBackdoor,
|
||||
MfClassicPollerStateBackdoorReadSector,
|
||||
MfClassicPollerStateRequestKey,
|
||||
MfClassicPollerStateReadSector,
|
||||
MfClassicPollerStateAuthKeyA,
|
||||
MfClassicPollerStateAuthKeyB,
|
||||
MfClassicPollerStateKeyReuseStart,
|
||||
MfClassicPollerStateKeyReuseStartNoOffset,
|
||||
MfClassicPollerStateKeyReuseAuthKeyA,
|
||||
MfClassicPollerStateKeyReuseAuthKeyB,
|
||||
MfClassicPollerStateKeyReuseReadSector,
|
||||
MfClassicPollerStateSuccess,
|
||||
MfClassicPollerStateFail,
|
||||
|
||||
// Enhanced dictionary attack states
|
||||
MfClassicPollerStateNestedAnalyzePRNG,
|
||||
MfClassicPollerStateNestedCalibrate,
|
||||
MfClassicPollerStateNestedCollectNt,
|
||||
MfClassicPollerStateNestedController,
|
||||
MfClassicPollerStateNestedCollectNtEnc,
|
||||
MfClassicPollerStateNestedDictAttack,
|
||||
MfClassicPollerStateNestedLog,
|
||||
|
||||
MfClassicPollerStateNum,
|
||||
} MfClassicPollerState;
|
||||
|
||||
@@ -70,6 +131,30 @@ typedef struct {
|
||||
bool auth_passed;
|
||||
uint16_t current_block;
|
||||
uint8_t reuse_key_sector;
|
||||
MfClassicBackdoor backdoor;
|
||||
// Enhanced dictionary attack and nested nonce collection
|
||||
bool enhanced_dict;
|
||||
MfClassicNestedPhase nested_phase;
|
||||
MfClassicKey nested_known_key;
|
||||
MfClassicKeyType nested_known_key_type;
|
||||
bool current_key_checked;
|
||||
uint8_t nested_known_key_sector;
|
||||
uint16_t nested_target_key;
|
||||
MfClassicNestedNonceArray nested_nonce;
|
||||
MfClassicPrngType prng_type;
|
||||
bool static_encrypted;
|
||||
uint32_t static_encrypted_nonce;
|
||||
bool calibrated;
|
||||
uint16_t d_min;
|
||||
uint16_t d_max;
|
||||
uint8_t attempt_count;
|
||||
KeysDict* mf_classic_system_dict;
|
||||
KeysDict* mf_classic_user_dict;
|
||||
// Hardnested
|
||||
uint8_t nt_enc_msb
|
||||
[32]; // Bit-packed array to track which unique most significant bytes have been seen (256 bits = 32 bytes)
|
||||
uint16_t msb_par_sum; // Sum of parity bits for each unique most significant byte
|
||||
uint16_t msb_count; // Number of unique most significant bytes seen
|
||||
} MfClassicPollerDictAttackContext;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user