Merge branch 'nestednonces' into ofw-3822-nestednonces

This commit is contained in:
Willy-JL
2024-09-04 01:30:16 +02:00
17 changed files with 1581 additions and 73 deletions
@@ -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;
+8 -1
View File
@@ -75,8 +75,12 @@
#define NFC_APP_MFKEY32_LOGS_FILE_NAME ".mfkey32.log"
#define NFC_APP_MFKEY32_LOGS_FILE_PATH (NFC_APP_FOLDER "/" NFC_APP_MFKEY32_LOGS_FILE_NAME)
#define NFC_APP_MF_CLASSIC_DICT_USER_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict_user.nfc")
#define NFC_APP_MF_CLASSIC_DICT_USER_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict_user.nfc")
#define NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH \
(NFC_APP_FOLDER "/assets/mf_classic_dict_user_nested.nfc")
#define NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH (NFC_APP_FOLDER "/assets/mf_classic_dict.nfc")
#define NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH \
(NFC_APP_FOLDER "/assets/mf_classic_dict_nested.nfc")
typedef enum {
NfcRpcStateIdle,
@@ -94,6 +98,9 @@ typedef struct {
bool is_key_attack;
uint8_t key_attack_current_sector;
bool is_card_present;
uint8_t nested_phase;
uint8_t prng_type;
uint8_t backdoor;
} NfcMfClassicDictAttackContext;
struct NfcApp {
@@ -5,6 +5,8 @@
#define TAG "NfcMfClassicDictAttack"
// TODO: Update progress bar with nested attacks
typedef enum {
DictAttackStateUserDictInProgress,
DictAttackStateSystemDictInProgress,
@@ -58,6 +60,9 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
instance->nfc_dict_context.sectors_read = data_update->sectors_read;
instance->nfc_dict_context.keys_found = data_update->keys_found;
instance->nfc_dict_context.current_sector = data_update->current_sector;
instance->nfc_dict_context.nested_phase = data_update->nested_phase;
instance->nfc_dict_context.prng_type = data_update->prng_type;
instance->nfc_dict_context.backdoor = data_update->backdoor;
view_dispatcher_send_custom_event(
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
} else if(mfc_event->type == MfClassicPollerEventTypeNextSector) {
@@ -117,6 +122,9 @@ static void nfc_scene_mf_classic_dict_attack_update_view(NfcApp* instance) {
dict_attack_set_keys_found(instance->dict_attack, mfc_dict->keys_found);
dict_attack_set_current_dict_key(instance->dict_attack, mfc_dict->dict_keys_current);
dict_attack_set_current_sector(instance->dict_attack, mfc_dict->current_sector);
dict_attack_set_nested_phase(instance->dict_attack, mfc_dict->nested_phase);
dict_attack_set_prng_type(instance->dict_attack, mfc_dict->prng_type);
dict_attack_set_backdoor(instance->dict_attack, mfc_dict->backdoor);
}
}
@@ -125,11 +133,25 @@ static void nfc_scene_mf_classic_dict_attack_prepare_view(NfcApp* instance) {
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack);
if(state == DictAttackStateUserDictInProgress) {
do {
// TODO: Check for errors
storage_common_remove(instance->storage, NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH);
storage_common_copy(
instance->storage,
NFC_APP_MF_CLASSIC_DICT_SYSTEM_PATH,
NFC_APP_MF_CLASSIC_DICT_SYSTEM_NESTED_PATH);
if(!keys_dict_check_presence(NFC_APP_MF_CLASSIC_DICT_USER_PATH)) {
state = DictAttackStateSystemDictInProgress;
break;
}
// TODO: Check for errors
storage_common_remove(instance->storage, NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH);
storage_common_copy(
instance->storage,
NFC_APP_MF_CLASSIC_DICT_USER_PATH,
NFC_APP_MF_CLASSIC_DICT_USER_NESTED_PATH);
instance->nfc_dict_context.dict = keys_dict_alloc(
NFC_APP_MF_CLASSIC_DICT_USER_PATH, KeysDictModeOpenAlways, sizeof(MfClassicKey));
if(keys_dict_get_total_keys(instance->nfc_dict_context.dict) == 0) {
+83 -1
View File
@@ -10,6 +10,30 @@ struct DictAttack {
void* context;
};
typedef enum {
MfClassicNestedPhaseNone,
MfClassicNestedPhaseAnalyzePRNG,
MfClassicNestedPhaseDictAttack,
MfClassicNestedPhaseDictAttackResume,
MfClassicNestedPhaseCalibrate,
MfClassicNestedPhaseCollectNtEnc,
MfClassicNestedPhaseFinished,
} MfClassicNestedPhase;
typedef enum {
MfClassicPrngTypeUnknown, // Tag not yet tested
MfClassicPrngTypeNoTag, // No tag detected during test
MfClassicPrngTypeWeak, // Weak PRNG, standard Nested
MfClassicPrngTypeHard, // Hard PRNG, Hardnested
} MfClassicPrngType;
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 (static encrypted nonce)
} MfClassicBackdoor;
typedef struct {
FuriString* header;
bool card_detected;
@@ -21,6 +45,9 @@ typedef struct {
size_t dict_keys_current;
bool is_key_attack;
uint8_t key_attack_current_sector;
MfClassicNestedPhase nested_phase;
MfClassicPrngType prng_type;
MfClassicBackdoor backdoor;
} DictAttackViewModel;
static void dict_attack_draw_callback(Canvas* canvas, void* model) {
@@ -34,8 +61,39 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
} else {
char draw_str[32] = {};
canvas_set_font(canvas, FontSecondary);
switch(m->nested_phase) {
case MfClassicNestedPhaseAnalyzePRNG:
furi_string_set(m->header, "PRNG Analysis");
break;
case MfClassicNestedPhaseDictAttack:
case MfClassicNestedPhaseDictAttackResume:
furi_string_set(m->header, "Nested Dictionary");
break;
case MfClassicNestedPhaseCalibrate:
furi_string_set(m->header, "Calibration");
break;
case MfClassicNestedPhaseCollectNtEnc:
furi_string_set(m->header, "Nonce Collection");
break;
default:
break;
}
if(m->prng_type == MfClassicPrngTypeHard) {
furi_string_cat(m->header, " (Hard)");
}
if(m->backdoor != MfClassicBackdoorNone && m->backdoor != MfClassicBackdoorUnknown) {
if(m->nested_phase != MfClassicNestedPhaseNone) {
furi_string_cat(m->header, " (Backdoor)");
} else {
furi_string_set(m->header, "Backdoor Read");
}
}
canvas_draw_str_aligned(
canvas, 64, 0, AlignCenter, AlignTop, furi_string_get_cstr(m->header));
canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(m->header));
if(m->is_key_attack) {
snprintf(
draw_str,
@@ -132,6 +190,9 @@ void dict_attack_reset(DictAttack* instance) {
model->dict_keys_total = 0;
model->dict_keys_current = 0;
model->is_key_attack = false;
model->nested_phase = MfClassicNestedPhaseNone;
model->prng_type = MfClassicPrngTypeUnknown;
model->backdoor = MfClassicBackdoorUnknown;
furi_string_reset(model->header);
},
false);
@@ -242,3 +303,24 @@ void dict_attack_reset_key_attack(DictAttack* instance) {
with_view_model(
instance->view, DictAttackViewModel * model, { model->is_key_attack = false; }, true);
}
void dict_attack_set_nested_phase(DictAttack* instance, uint8_t nested_phase) {
furi_assert(instance);
with_view_model(
instance->view, DictAttackViewModel * model, { model->nested_phase = nested_phase; }, true);
}
void dict_attack_set_prng_type(DictAttack* instance, uint8_t prng_type) {
furi_assert(instance);
with_view_model(
instance->view, DictAttackViewModel * model, { model->prng_type = prng_type; }, true);
}
void dict_attack_set_backdoor(DictAttack* instance, uint8_t backdoor) {
furi_assert(instance);
with_view_model(
instance->view, DictAttackViewModel * model, { model->backdoor = backdoor; }, true);
}
@@ -45,6 +45,12 @@ void dict_attack_set_key_attack(DictAttack* instance, uint8_t sector);
void dict_attack_reset_key_attack(DictAttack* instance);
void dict_attack_set_nested_phase(DictAttack* instance, uint8_t nested_phase);
void dict_attack_set_prng_type(DictAttack* instance, uint8_t prng_type);
void dict_attack_set_backdoor(DictAttack* instance, uint8_t backdoor);
#ifdef __cplusplus
}
#endif
+57
View File
@@ -177,3 +177,60 @@ void crypto1_encrypt_reader_nonce(
bit_buffer_set_byte_with_parity(out, i, byte, parity_bit);
}
}
static uint8_t lfsr_rollback_bit(Crypto1* crypto1, uint32_t in, int fb) {
int out;
uint8_t ret;
uint32_t t;
crypto1->odd &= 0xffffff;
t = crypto1->odd;
crypto1->odd = crypto1->even;
crypto1->even = t;
out = crypto1->even & 1;
out ^= LF_POLY_EVEN & (crypto1->even >>= 1);
out ^= LF_POLY_ODD & crypto1->odd;
out ^= !!in;
out ^= (ret = crypto1_filter(crypto1->odd)) & (!!fb);
crypto1->even |= (nfc_util_even_parity32(out)) << 23;
return ret;
}
uint32_t lfsr_rollback_word(Crypto1* crypto1, uint32_t in, int fb) {
uint32_t ret = 0;
for(int i = 31; i >= 0; i--) {
ret |= lfsr_rollback_bit(crypto1, BEBIT(in, i), fb) << (24 ^ i);
}
return ret;
}
bool nonce_matches_encrypted_parity_bits(uint32_t nt, uint32_t ks, uint8_t nt_par_enc) {
return (nfc_util_even_parity8((nt >> 24) & 0xFF) ==
(((nt_par_enc >> 3) & 1) ^ FURI_BIT(ks, 16))) &&
(nfc_util_even_parity8((nt >> 16) & 0xFF) ==
(((nt_par_enc >> 2) & 1) ^ FURI_BIT(ks, 8))) &&
(nfc_util_even_parity8((nt >> 8) & 0xFF) ==
(((nt_par_enc >> 1) & 1) ^ FURI_BIT(ks, 0)));
}
bool is_weak_prng_nonce(uint32_t nonce) {
if(nonce == 0) return false;
uint16_t x = nonce >> 16;
x = (x & 0xff) << 8 | x >> 8;
for(uint8_t i = 0; i < 16; i++) {
x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15;
}
x = (x & 0xff) << 8 | x >> 8;
return x == (nonce & 0xFFFF);
}
uint32_t decrypt_nt_enc(uint32_t cuid, uint32_t nt_enc, MfClassicKey known_key) {
uint64_t known_key_int = bit_lib_bytes_to_num_be(known_key.data, 6);
Crypto1 crypto_temp;
crypto1_init(&crypto_temp, known_key_int);
crypto1_word(&crypto_temp, nt_enc ^ cuid, 1);
uint32_t decrypted_nt_enc = (nt_enc ^ lfsr_rollback_word(&crypto_temp, nt_enc ^ cuid, 1));
return decrypted_nt_enc;
}
+9
View File
@@ -1,5 +1,6 @@
#pragma once
#include "protocols/mf_classic/mf_classic.h"
#include <toolbox/bit_buffer.h>
#ifdef __cplusplus
@@ -38,6 +39,14 @@ void crypto1_encrypt_reader_nonce(
BitBuffer* out,
bool is_nested);
uint32_t lfsr_rollback_word(Crypto1* crypto1, uint32_t in, int fb);
bool nonce_matches_encrypted_parity_bits(uint32_t nt, uint32_t ks, uint8_t nt_par_enc);
bool is_weak_prng_nonce(uint32_t nonce);
uint32_t decrypt_nt_enc(uint32_t cuid, uint32_t nt_enc, MfClassicKey known_key);
uint32_t prng_successor(uint32_t x, uint32_t n);
#ifdef __cplusplus
+4
View File
@@ -13,6 +13,10 @@ static const uint8_t nfc_util_odd_byte_parity[256] = {
0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1};
uint8_t nfc_util_even_parity8(uint8_t data) {
return !nfc_util_odd_byte_parity[data];
}
uint8_t nfc_util_even_parity32(uint32_t data) {
// data ^= data >> 16;
// data ^= data >> 8;
+2
View File
@@ -6,6 +6,8 @@
extern "C" {
#endif
uint8_t nfc_util_even_parity8(uint8_t data);
uint8_t nfc_util_even_parity32(uint32_t data);
uint8_t nfc_util_odd_parity8(uint8_t data);
+10 -8
View File
@@ -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)
File diff suppressed because it is too large Load Diff
@@ -77,6 +77,9 @@ 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. */
uint8_t nested_phase; /**< Nested attack phase. */
uint8_t prng_type; /**< PRNG (weak or hard). */
uint8_t backdoor; /**< Backdoor type. */
} MfClassicPollerEventDataUpdate;
/**
@@ -170,13 +173,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 +194,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 +217,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 +225,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 +249,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.
@@ -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,39 @@
#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 "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 uint16_t valid_sums[19];
typedef enum {
MfClassicAuthStateIdle,
@@ -21,6 +47,44 @@ typedef enum {
MfClassicCardStateLost,
} MfClassicCardState;
typedef enum {
MfClassicNestedPhaseNone,
MfClassicNestedPhaseAnalyzePRNG,
MfClassicNestedPhaseDictAttack,
MfClassicNestedPhaseDictAttackResume,
MfClassicNestedPhaseCalibrate,
MfClassicNestedPhaseCollectNtEnc,
MfClassicNestedPhaseFinished,
} MfClassicNestedPhase;
typedef enum {
MfClassicPrngTypeUnknown, // Tag not yet tested
MfClassicPrngTypeNoTag, // No tag detected during test
MfClassicPrngTypeWeak, // Weak PRNG, standard Nested
MfClassicPrngTypeHard, // Hard PRNG, Hardnested
} MfClassicPrngType;
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 (static encrypted nonce)
} MfClassicBackdoor;
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 +102,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 +146,28 @@ 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;
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
} 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(
+1 -1
View File
@@ -113,7 +113,7 @@ void bit_buffer_copy_bytes_with_parity(BitBuffer* buf, const uint8_t* data, size
uint8_t bit =
FURI_BIT(data[bits_processed / BITS_IN_BYTE + 1], bits_processed % BITS_IN_BYTE);
if(bits_processed % BITS_IN_BYTE) {
if((bits_processed % BITS_IN_BYTE) == 0) {
buf->parity[curr_byte / BITS_IN_BYTE] = bit;
} else {
buf->parity[curr_byte / BITS_IN_BYTE] |= bit << (bits_processed % BITS_IN_BYTE);
+10 -5
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/main/archive/helpers/archive_helpers_ext.h,,
Header,+,applications/main/subghz/subghz_fap.h,,
@@ -921,6 +921,7 @@ Function,+,datetime_get_days_per_year,uint16_t,uint16_t
Function,+,datetime_is_leap_year,_Bool,uint16_t
Function,+,datetime_timestamp_to_datetime,void,"uint32_t, DateTime*"
Function,+,datetime_validate_datetime,_Bool,DateTime*
Function,+,decrypt_nt_enc,uint32_t,"uint32_t, uint32_t, MfClassicKey"
Function,+,dialog_ex_alloc,DialogEx*,
Function,+,dialog_ex_disable_extended_events,void,DialogEx*
Function,+,dialog_ex_enable_extended_events,void,DialogEx*
@@ -2103,6 +2104,7 @@ Function,-,initstate,char*,"unsigned, char*, size_t"
Function,+,input_get_key_name,const char*,InputKey
Function,+,input_get_type_name,const char*,InputType
Function,-,iprintf,int,"const char*, ..."
Function,+,is_weak_prng_nonce,_Bool,uint32_t
Function,-,isalnum,int,int
Function,-,isalnum_l,int,"int, locale_t"
Function,-,isalpha,int,int
@@ -2288,6 +2290,7 @@ Function,+,lfrfid_worker_stop,void,LFRFIDWorker*
Function,+,lfrfid_worker_stop_thread,void,LFRFIDWorker*
Function,+,lfrfid_worker_write_and_set_pass_start,void,"LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*"
Function,+,lfrfid_worker_write_start,void,"LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*"
Function,+,lfsr_rollback_word,uint32_t,"Crypto1*, uint32_t, int"
Function,-,lgamma,double,double
Function,-,lgamma_r,double,"double, int*"
Function,-,lgammaf,float,float
@@ -2584,10 +2587,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*"
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"
@@ -2888,8 +2891,10 @@ Function,+,nfc_set_mask_receive_time_fc,void,"Nfc*, uint32_t"
Function,+,nfc_start,void,"Nfc*, NfcEventCallback, void*"
Function,+,nfc_stop,void,Nfc*
Function,+,nfc_util_even_parity32,uint8_t,uint32_t
Function,+,nfc_util_even_parity8,uint8_t,uint8_t
Function,+,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t"
Function,+,nfc_util_odd_parity8,uint8_t,uint8_t
Function,+,nonce_matches_encrypted_parity_bits,_Bool,"uint32_t, uint32_t, uint8_t"
Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*"
Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*"
Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*"
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/main/archive/helpers/archive_helpers_ext.h
5 Header + applications/main/subghz/subghz_fap.h
921 Function + datetime_is_leap_year _Bool uint16_t
922 Function + datetime_timestamp_to_datetime void uint32_t, DateTime*
923 Function + datetime_validate_datetime _Bool DateTime*
924 Function + decrypt_nt_enc uint32_t uint32_t, uint32_t, MfClassicKey
925 Function + dialog_ex_alloc DialogEx*
926 Function + dialog_ex_disable_extended_events void DialogEx*
927 Function + dialog_ex_enable_extended_events void DialogEx*
2104 Function + input_get_key_name const char* InputKey
2105 Function + input_get_type_name const char* InputType
2106 Function - iprintf int const char*, ...
2107 Function + is_weak_prng_nonce _Bool uint32_t
2108 Function - isalnum int int
2109 Function - isalnum_l int int, locale_t
2110 Function - isalpha int int
2290 Function + lfrfid_worker_stop_thread void LFRFIDWorker*
2291 Function + lfrfid_worker_write_and_set_pass_start void LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*
2292 Function + lfrfid_worker_write_start void LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*
2293 Function + lfsr_rollback_word uint32_t Crypto1*, uint32_t, int
2294 Function - lgamma double double
2295 Function - lgamma_r double double, int*
2296 Function - lgammaf float float
2587 Function + mf_classic_is_sector_trailer _Bool uint8_t
2588 Function + mf_classic_is_value_block _Bool MfClassicSectorTrailer*, uint8_t
2589 Function + mf_classic_load _Bool MfClassicData*, FlipperFormat*, uint32_t
2590 Function + mf_classic_poller_auth MfClassicError MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext* MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*, _Bool
2591 Function + mf_classic_poller_auth_nested MfClassicError MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext* MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*, _Bool, _Bool
2592 Function + mf_classic_poller_get_nt MfClassicError MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt* MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*, _Bool
2593 Function + mf_classic_poller_get_nt_nested MfClassicError MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt* MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*, _Bool
2594 Function + mf_classic_poller_halt MfClassicError MfClassicPoller*
2595 Function + mf_classic_poller_read_block MfClassicError MfClassicPoller*, uint8_t, MfClassicBlock*
2596 Function + mf_classic_poller_send_custom_parity_frame MfClassicError MfClassicPoller*, const BitBuffer*, BitBuffer*, uint32_t
2891 Function + nfc_start void Nfc*, NfcEventCallback, void*
2892 Function + nfc_stop void Nfc*
2893 Function + nfc_util_even_parity32 uint8_t uint32_t
2894 Function + nfc_util_even_parity8 uint8_t uint8_t
2895 Function + nfc_util_odd_parity void const uint8_t*, uint8_t*, uint8_t
2896 Function + nfc_util_odd_parity8 uint8_t uint8_t
2897 Function + nonce_matches_encrypted_parity_bits _Bool uint32_t, uint32_t, uint8_t
2898 Function + notification_internal_message void NotificationApp*, const NotificationSequence*
2899 Function + notification_internal_message_block void NotificationApp*, const NotificationSequence*
2900 Function + notification_message void NotificationApp*, const NotificationSequence*