mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-05-13 15:58:36 -07:00
Merge branch 'nestednonces' into ofw-3822-nestednonces
This commit is contained in:
@@ -101,6 +101,8 @@ typedef struct {
|
|||||||
uint8_t nested_phase;
|
uint8_t nested_phase;
|
||||||
uint8_t prng_type;
|
uint8_t prng_type;
|
||||||
uint8_t backdoor;
|
uint8_t backdoor;
|
||||||
|
uint16_t nested_target_key;
|
||||||
|
uint16_t msb_count;
|
||||||
} NfcMfClassicDictAttackContext;
|
} NfcMfClassicDictAttackContext;
|
||||||
|
|
||||||
struct NfcApp {
|
struct NfcApp {
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
|
|
||||||
#define TAG "NfcMfClassicDictAttack"
|
#define TAG "NfcMfClassicDictAttack"
|
||||||
|
|
||||||
// TODO: Update progress bar with nested attacks
|
// TODO: Fix lag when leaving the dictionary attack view after Hardnested
|
||||||
|
// TODO: Re-enters backdoor detection between user and system dictionary if no backdoor is found
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
DictAttackStateUserDictInProgress,
|
DictAttackStateUserDictInProgress,
|
||||||
@@ -63,6 +64,8 @@ NfcCommand nfc_dict_attack_worker_callback(NfcGenericEvent event, void* context)
|
|||||||
instance->nfc_dict_context.nested_phase = data_update->nested_phase;
|
instance->nfc_dict_context.nested_phase = data_update->nested_phase;
|
||||||
instance->nfc_dict_context.prng_type = data_update->prng_type;
|
instance->nfc_dict_context.prng_type = data_update->prng_type;
|
||||||
instance->nfc_dict_context.backdoor = data_update->backdoor;
|
instance->nfc_dict_context.backdoor = data_update->backdoor;
|
||||||
|
instance->nfc_dict_context.nested_target_key = data_update->nested_target_key;
|
||||||
|
instance->nfc_dict_context.msb_count = data_update->msb_count;
|
||||||
view_dispatcher_send_custom_event(
|
view_dispatcher_send_custom_event(
|
||||||
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
|
instance->view_dispatcher, NfcCustomEventDictAttackDataUpdate);
|
||||||
} else if(mfc_event->type == MfClassicPollerEventTypeNextSector) {
|
} else if(mfc_event->type == MfClassicPollerEventTypeNextSector) {
|
||||||
@@ -125,6 +128,8 @@ static void nfc_scene_mf_classic_dict_attack_update_view(NfcApp* instance) {
|
|||||||
dict_attack_set_nested_phase(instance->dict_attack, mfc_dict->nested_phase);
|
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_prng_type(instance->dict_attack, mfc_dict->prng_type);
|
||||||
dict_attack_set_backdoor(instance->dict_attack, mfc_dict->backdoor);
|
dict_attack_set_backdoor(instance->dict_attack, mfc_dict->backdoor);
|
||||||
|
dict_attack_set_nested_target_key(instance->dict_attack, mfc_dict->nested_target_key);
|
||||||
|
dict_attack_set_msb_count(instance->dict_attack, mfc_dict->msb_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +219,9 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
|
|||||||
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack);
|
scene_manager_get_scene_state(instance->scene_manager, NfcSceneMfClassicDictAttack);
|
||||||
if(event.type == SceneManagerEventTypeCustom) {
|
if(event.type == SceneManagerEventTypeCustom) {
|
||||||
if(event.event == NfcCustomEventDictAttackComplete) {
|
if(event.event == NfcCustomEventDictAttackComplete) {
|
||||||
if(state == DictAttackStateUserDictInProgress) {
|
bool ran_nested_dict = instance->nfc_dict_context.nested_phase !=
|
||||||
|
MfClassicNestedPhaseNone;
|
||||||
|
if(state == DictAttackStateUserDictInProgress && !(ran_nested_dict)) {
|
||||||
nfc_poller_stop(instance->poller);
|
nfc_poller_stop(instance->poller);
|
||||||
nfc_poller_free(instance->poller);
|
nfc_poller_free(instance->poller);
|
||||||
keys_dict_free(instance->nfc_dict_context.dict);
|
keys_dict_free(instance->nfc_dict_context.dict);
|
||||||
@@ -243,7 +250,9 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
|
|||||||
} else if(event.event == NfcCustomEventDictAttackSkip) {
|
} else if(event.event == NfcCustomEventDictAttackSkip) {
|
||||||
const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller);
|
const MfClassicData* mfc_data = nfc_poller_get_data(instance->poller);
|
||||||
nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mfc_data);
|
nfc_device_set_data(instance->nfc_device, NfcProtocolMfClassic, mfc_data);
|
||||||
if(state == DictAttackStateUserDictInProgress) {
|
bool ran_nested_dict = instance->nfc_dict_context.nested_phase !=
|
||||||
|
MfClassicNestedPhaseNone;
|
||||||
|
if(state == DictAttackStateUserDictInProgress && !(ran_nested_dict)) {
|
||||||
if(instance->nfc_dict_context.is_card_present) {
|
if(instance->nfc_dict_context.is_card_present) {
|
||||||
nfc_poller_stop(instance->poller);
|
nfc_poller_stop(instance->poller);
|
||||||
nfc_poller_free(instance->poller);
|
nfc_poller_free(instance->poller);
|
||||||
@@ -261,7 +270,7 @@ bool nfc_scene_mf_classic_dict_attack_on_event(void* context, SceneManagerEvent
|
|||||||
dolphin_deed(DolphinDeedNfcReadSuccess);
|
dolphin_deed(DolphinDeedNfcReadSuccess);
|
||||||
}
|
}
|
||||||
consumed = true;
|
consumed = true;
|
||||||
} else if(state == DictAttackStateSystemDictInProgress) {
|
} else {
|
||||||
nfc_scene_mf_classic_dict_attack_notify_read(instance);
|
nfc_scene_mf_classic_dict_attack_notify_read(instance);
|
||||||
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
scene_manager_next_scene(instance->scene_manager, NfcSceneReadSuccess);
|
||||||
dolphin_deed(DolphinDeedNfcReadSuccess);
|
dolphin_deed(DolphinDeedNfcReadSuccess);
|
||||||
@@ -299,6 +308,8 @@ void nfc_scene_mf_classic_dict_attack_on_exit(void* context) {
|
|||||||
instance->nfc_dict_context.nested_phase = MfClassicNestedPhaseNone;
|
instance->nfc_dict_context.nested_phase = MfClassicNestedPhaseNone;
|
||||||
instance->nfc_dict_context.prng_type = MfClassicPrngTypeUnknown;
|
instance->nfc_dict_context.prng_type = MfClassicPrngTypeUnknown;
|
||||||
instance->nfc_dict_context.backdoor = MfClassicBackdoorUnknown;
|
instance->nfc_dict_context.backdoor = MfClassicBackdoorUnknown;
|
||||||
|
instance->nfc_dict_context.nested_target_key = 0;
|
||||||
|
instance->nfc_dict_context.msb_count = 0;
|
||||||
|
|
||||||
nfc_blink_stop(instance);
|
nfc_blink_stop(instance);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ typedef struct {
|
|||||||
MfClassicNestedPhase nested_phase;
|
MfClassicNestedPhase nested_phase;
|
||||||
MfClassicPrngType prng_type;
|
MfClassicPrngType prng_type;
|
||||||
MfClassicBackdoor backdoor;
|
MfClassicBackdoor backdoor;
|
||||||
|
uint16_t nested_target_key;
|
||||||
|
uint16_t msb_count;
|
||||||
} DictAttackViewModel;
|
} DictAttackViewModel;
|
||||||
|
|
||||||
static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
||||||
@@ -71,7 +73,12 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
|||||||
|
|
||||||
canvas_draw_str_aligned(
|
canvas_draw_str_aligned(
|
||||||
canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(m->header));
|
canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(m->header));
|
||||||
if(m->is_key_attack) {
|
if(m->nested_phase == MfClassicNestedPhaseCollectNtEnc) {
|
||||||
|
uint8_t nonce_sector =
|
||||||
|
m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 4 : 2);
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "Collecting from sector: %d", nonce_sector);
|
||||||
|
canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);
|
||||||
|
} else if(m->is_key_attack) {
|
||||||
snprintf(
|
snprintf(
|
||||||
draw_str,
|
draw_str,
|
||||||
sizeof(draw_str),
|
sizeof(draw_str),
|
||||||
@@ -81,21 +88,47 @@ static void dict_attack_draw_callback(Canvas* canvas, void* model) {
|
|||||||
snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->current_sector);
|
snprintf(draw_str, sizeof(draw_str), "Unlocking sector: %d", m->current_sector);
|
||||||
}
|
}
|
||||||
canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);
|
canvas_draw_str_aligned(canvas, 0, 10, AlignLeft, AlignTop, draw_str);
|
||||||
float dict_progress = m->dict_keys_total == 0 ?
|
float dict_progress = 0;
|
||||||
0 :
|
if(m->nested_phase == MfClassicNestedPhaseAnalyzePRNG ||
|
||||||
(float)(m->dict_keys_current) / (float)(m->dict_keys_total);
|
m->nested_phase == MfClassicNestedPhaseDictAttack ||
|
||||||
float progress = m->sectors_total == 0 ? 0 :
|
m->nested_phase == MfClassicNestedPhaseDictAttackResume) {
|
||||||
((float)(m->current_sector) + dict_progress) /
|
// Phase: Nested dictionary attack
|
||||||
(float)(m->sectors_total);
|
uint8_t target_sector =
|
||||||
if(progress > 1.0f) {
|
m->nested_target_key / (m->prng_type == MfClassicPrngTypeWeak ? 2 : 16);
|
||||||
progress = 1.0f;
|
dict_progress = (float)(target_sector) / (float)(m->sectors_total);
|
||||||
}
|
snprintf(draw_str, sizeof(draw_str), "%d/%d", target_sector, m->sectors_total);
|
||||||
if(m->dict_keys_current == 0) {
|
} else if(
|
||||||
// Cause when people see 0 they think it's broken
|
m->nested_phase == MfClassicNestedPhaseCalibrate ||
|
||||||
snprintf(draw_str, sizeof(draw_str), "%d/%zu", 1, m->dict_keys_total);
|
m->nested_phase == MfClassicNestedPhaseRecalibrate ||
|
||||||
|
m->nested_phase == MfClassicNestedPhaseCollectNtEnc) {
|
||||||
|
// Phase: Nonce collection
|
||||||
|
if(m->prng_type == MfClassicPrngTypeWeak) {
|
||||||
|
uint8_t target_sector = m->nested_target_key / 4;
|
||||||
|
dict_progress = (float)(target_sector) / (float)(m->sectors_total);
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "%d/%d", target_sector, m->sectors_total);
|
||||||
|
} else {
|
||||||
|
uint16_t max_msb = UINT8_MAX + 1;
|
||||||
|
dict_progress = (float)(m->msb_count) / (float)(max_msb);
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "%d/%d", m->msb_count, max_msb);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
snprintf(
|
dict_progress = m->dict_keys_total == 0 ?
|
||||||
draw_str, sizeof(draw_str), "%zu/%zu", m->dict_keys_current, m->dict_keys_total);
|
0 :
|
||||||
|
(float)(m->dict_keys_current) / (float)(m->dict_keys_total);
|
||||||
|
if(m->dict_keys_current == 0) {
|
||||||
|
// Cause when people see 0 they think it's broken
|
||||||
|
snprintf(draw_str, sizeof(draw_str), "%d/%zu", 1, m->dict_keys_total);
|
||||||
|
} else {
|
||||||
|
snprintf(
|
||||||
|
draw_str,
|
||||||
|
sizeof(draw_str),
|
||||||
|
"%zu/%zu",
|
||||||
|
m->dict_keys_current,
|
||||||
|
m->dict_keys_total);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(dict_progress > 1.0f) {
|
||||||
|
dict_progress = 1.0f;
|
||||||
}
|
}
|
||||||
elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str);
|
elements_progress_bar_with_text(canvas, 0, 20, 128, dict_progress, draw_str);
|
||||||
canvas_set_font(canvas, FontSecondary);
|
canvas_set_font(canvas, FontSecondary);
|
||||||
@@ -170,6 +203,8 @@ void dict_attack_reset(DictAttack* instance) {
|
|||||||
model->nested_phase = MfClassicNestedPhaseNone;
|
model->nested_phase = MfClassicNestedPhaseNone;
|
||||||
model->prng_type = MfClassicPrngTypeUnknown;
|
model->prng_type = MfClassicPrngTypeUnknown;
|
||||||
model->backdoor = MfClassicBackdoorUnknown;
|
model->backdoor = MfClassicBackdoorUnknown;
|
||||||
|
model->nested_target_key = 0;
|
||||||
|
model->msb_count = 0;
|
||||||
furi_string_reset(model->header);
|
furi_string_reset(model->header);
|
||||||
},
|
},
|
||||||
false);
|
false);
|
||||||
@@ -301,3 +336,20 @@ void dict_attack_set_backdoor(DictAttack* instance, uint8_t backdoor) {
|
|||||||
with_view_model(
|
with_view_model(
|
||||||
instance->view, DictAttackViewModel * model, { model->backdoor = backdoor; }, true);
|
instance->view, DictAttackViewModel * model, { model->backdoor = backdoor; }, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dict_attack_set_nested_target_key(DictAttack* instance, uint16_t nested_target_key) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
instance->view,
|
||||||
|
DictAttackViewModel * model,
|
||||||
|
{ model->nested_target_key = nested_target_key; },
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dict_attack_set_msb_count(DictAttack* instance, uint16_t msb_count) {
|
||||||
|
furi_assert(instance);
|
||||||
|
|
||||||
|
with_view_model(
|
||||||
|
instance->view, DictAttackViewModel * model, { model->msb_count = msb_count; }, true);
|
||||||
|
}
|
||||||
|
|||||||
@@ -77,6 +77,10 @@ void dict_attack_set_prng_type(DictAttack* instance, uint8_t prng_type);
|
|||||||
|
|
||||||
void dict_attack_set_backdoor(DictAttack* instance, uint8_t backdoor);
|
void dict_attack_set_backdoor(DictAttack* instance, uint8_t backdoor);
|
||||||
|
|
||||||
|
void dict_attack_set_nested_target_key(DictAttack* instance, uint16_t target_key);
|
||||||
|
|
||||||
|
void dict_attack_set_msb_count(DictAttack* instance, uint16_t msb_count);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ uint32_t crypto1_word(Crypto1* crypto1, uint32_t in, int is_encrypted) {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t prng_successor(uint32_t x, uint32_t n) {
|
uint32_t crypto1_prng_successor(uint32_t x, uint32_t n) {
|
||||||
SWAPENDIAN(x);
|
SWAPENDIAN(x);
|
||||||
while(n--)
|
while(n--)
|
||||||
x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
|
x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
|
||||||
@@ -169,9 +169,9 @@ void crypto1_encrypt_reader_nonce(
|
|||||||
nr[i] = byte;
|
nr[i] = byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
nt_num = prng_successor(nt_num, 32);
|
nt_num = crypto1_prng_successor(nt_num, 32);
|
||||||
for(size_t i = 4; i < 8; i++) {
|
for(size_t i = 4; i < 8; i++) {
|
||||||
nt_num = prng_successor(nt_num, 8);
|
nt_num = crypto1_prng_successor(nt_num, 8);
|
||||||
uint8_t byte = crypto1_byte(crypto, 0, 0) ^ (uint8_t)(nt_num);
|
uint8_t byte = crypto1_byte(crypto, 0, 0) ^ (uint8_t)(nt_num);
|
||||||
bool parity_bit = ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nt_num)) & 0x01);
|
bool parity_bit = ((crypto1_filter(crypto->odd) ^ nfc_util_odd_parity8(nt_num)) & 0x01);
|
||||||
bit_buffer_set_byte_with_parity(out, i, byte, parity_bit);
|
bit_buffer_set_byte_with_parity(out, i, byte, parity_bit);
|
||||||
@@ -198,7 +198,7 @@ static uint8_t lfsr_rollback_bit(Crypto1* crypto1, uint32_t in, int fb) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t lfsr_rollback_word(Crypto1* crypto1, uint32_t in, int fb) {
|
uint32_t crypto1_lfsr_rollback_word(Crypto1* crypto1, uint32_t in, int fb) {
|
||||||
uint32_t ret = 0;
|
uint32_t ret = 0;
|
||||||
for(int i = 31; i >= 0; i--) {
|
for(int i = 31; i >= 0; i--) {
|
||||||
ret |= lfsr_rollback_bit(crypto1, BEBIT(in, i), fb) << (24 ^ i);
|
ret |= lfsr_rollback_bit(crypto1, BEBIT(in, i), fb) << (24 ^ i);
|
||||||
@@ -206,7 +206,7 @@ uint32_t lfsr_rollback_word(Crypto1* crypto1, uint32_t in, int fb) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nonce_matches_encrypted_parity_bits(uint32_t nt, uint32_t ks, uint8_t nt_par_enc) {
|
bool crypto1_nonce_matches_encrypted_parity_bits(uint32_t nt, uint32_t ks, uint8_t nt_par_enc) {
|
||||||
return (nfc_util_even_parity8((nt >> 24) & 0xFF) ==
|
return (nfc_util_even_parity8((nt >> 24) & 0xFF) ==
|
||||||
(((nt_par_enc >> 3) & 1) ^ FURI_BIT(ks, 16))) &&
|
(((nt_par_enc >> 3) & 1) ^ FURI_BIT(ks, 16))) &&
|
||||||
(nfc_util_even_parity8((nt >> 16) & 0xFF) ==
|
(nfc_util_even_parity8((nt >> 16) & 0xFF) ==
|
||||||
@@ -215,7 +215,7 @@ bool nonce_matches_encrypted_parity_bits(uint32_t nt, uint32_t ks, uint8_t nt_pa
|
|||||||
(((nt_par_enc >> 1) & 1) ^ FURI_BIT(ks, 0)));
|
(((nt_par_enc >> 1) & 1) ^ FURI_BIT(ks, 0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_weak_prng_nonce(uint32_t nonce) {
|
bool crypto1_is_weak_prng_nonce(uint32_t nonce) {
|
||||||
if(nonce == 0) return false;
|
if(nonce == 0) return false;
|
||||||
uint16_t x = nonce >> 16;
|
uint16_t x = nonce >> 16;
|
||||||
x = (x & 0xff) << 8 | x >> 8;
|
x = (x & 0xff) << 8 | x >> 8;
|
||||||
@@ -226,11 +226,12 @@ bool is_weak_prng_nonce(uint32_t nonce) {
|
|||||||
return x == (nonce & 0xFFFF);
|
return x == (nonce & 0xFFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t decrypt_nt_enc(uint32_t cuid, uint32_t nt_enc, MfClassicKey known_key) {
|
uint32_t crypto1_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);
|
uint64_t known_key_int = bit_lib_bytes_to_num_be(known_key.data, 6);
|
||||||
Crypto1 crypto_temp;
|
Crypto1 crypto_temp;
|
||||||
crypto1_init(&crypto_temp, known_key_int);
|
crypto1_init(&crypto_temp, known_key_int);
|
||||||
crypto1_word(&crypto_temp, nt_enc ^ cuid, 1);
|
crypto1_word(&crypto_temp, nt_enc ^ cuid, 1);
|
||||||
uint32_t decrypted_nt_enc = (nt_enc ^ lfsr_rollback_word(&crypto_temp, nt_enc ^ cuid, 1));
|
uint32_t decrypted_nt_enc =
|
||||||
|
(nt_enc ^ crypto1_lfsr_rollback_word(&crypto_temp, nt_enc ^ cuid, 1));
|
||||||
return decrypted_nt_enc;
|
return decrypted_nt_enc;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "protocols/mf_classic/mf_classic.h"
|
#include <protocols/mf_classic/mf_classic.h>
|
||||||
#include <toolbox/bit_buffer.h>
|
#include <toolbox/bit_buffer.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@@ -39,15 +39,15 @@ void crypto1_encrypt_reader_nonce(
|
|||||||
BitBuffer* out,
|
BitBuffer* out,
|
||||||
bool is_nested);
|
bool is_nested);
|
||||||
|
|
||||||
uint32_t lfsr_rollback_word(Crypto1* crypto1, uint32_t in, int fb);
|
uint32_t crypto1_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 crypto1_nonce_matches_encrypted_parity_bits(uint32_t nt, uint32_t ks, uint8_t nt_par_enc);
|
||||||
|
|
||||||
bool is_weak_prng_nonce(uint32_t nonce);
|
bool crypto1_is_weak_prng_nonce(uint32_t nonce);
|
||||||
|
|
||||||
uint32_t decrypt_nt_enc(uint32_t cuid, uint32_t nt_enc, MfClassicKey known_key);
|
uint32_t crypto1_decrypt_nt_enc(uint32_t cuid, uint32_t nt_enc, MfClassicKey known_key);
|
||||||
|
|
||||||
uint32_t prng_successor(uint32_t x, uint32_t n);
|
uint32_t crypto1_prng_successor(uint32_t x, uint32_t n);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,14 +157,17 @@ static MfClassicListenerCommand
|
|||||||
uint32_t nt_num =
|
uint32_t nt_num =
|
||||||
bit_lib_bytes_to_num_be(instance->auth_context.nt.data, sizeof(MfClassicNt));
|
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);
|
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(
|
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;
|
command = MfClassicListenerCommandSleep;
|
||||||
break;
|
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_lib_num_to_bytes_be(at_num, sizeof(uint32_t), instance->auth_context.at.data);
|
||||||
bit_buffer_copy_bytes(
|
bit_buffer_copy_bytes(
|
||||||
instance->tx_plain_buffer, instance->auth_context.at.data, sizeof(MfClassicAr));
|
instance->tx_plain_buffer, instance->auth_context.at.data, sizeof(MfClassicAr));
|
||||||
|
|||||||
@@ -6,10 +6,10 @@
|
|||||||
|
|
||||||
#define TAG "MfClassicPoller"
|
#define TAG "MfClassicPoller"
|
||||||
|
|
||||||
// TODO: Reflect status in NFC app (second text line, progress bar)
|
|
||||||
// TODO: Buffer writes for Hardnested, set state to Log when finished and sum property matches
|
// TODO: Buffer writes for Hardnested, set state to Log when finished and sum property matches
|
||||||
// TODO: Load dictionaries specific to a CUID to not clutter the user dictionary
|
// TODO: Load dictionaries specific to a CUID to not clutter the user dictionary
|
||||||
// TODO: Fix rare nested_target_key 64 bug
|
// TODO: Fix rare nested_target_key 64 bug
|
||||||
|
// TODO: Dead code for malloc returning NULL?
|
||||||
|
|
||||||
#define MF_CLASSIC_MAX_BUFF_SIZE (64)
|
#define MF_CLASSIC_MAX_BUFF_SIZE (64)
|
||||||
|
|
||||||
@@ -97,6 +97,8 @@ static NfcCommand mf_classic_poller_handle_data_update(MfClassicPoller* instance
|
|||||||
data_update->nested_phase = instance->mode_ctx.dict_attack_ctx.nested_phase;
|
data_update->nested_phase = instance->mode_ctx.dict_attack_ctx.nested_phase;
|
||||||
data_update->prng_type = instance->mode_ctx.dict_attack_ctx.prng_type;
|
data_update->prng_type = instance->mode_ctx.dict_attack_ctx.prng_type;
|
||||||
data_update->backdoor = instance->mode_ctx.dict_attack_ctx.backdoor;
|
data_update->backdoor = instance->mode_ctx.dict_attack_ctx.backdoor;
|
||||||
|
data_update->nested_target_key = instance->mode_ctx.dict_attack_ctx.nested_target_key;
|
||||||
|
data_update->msb_count = instance->mode_ctx.dict_attack_ctx.msb_count;
|
||||||
instance->mfc_event.type = MfClassicPollerEventTypeDataUpdate;
|
instance->mfc_event.type = MfClassicPollerEventTypeDataUpdate;
|
||||||
return instance->callback(instance->general_event, instance->context);
|
return instance->callback(instance->general_event, instance->context);
|
||||||
}
|
}
|
||||||
@@ -1059,7 +1061,7 @@ NfcCommand mf_classic_poller_handler_nested_analyze_prng(MfClassicPoller* instan
|
|||||||
|
|
||||||
for(uint8_t i = 0; i < dict_attack_ctx->nested_nonce.count; i++) {
|
for(uint8_t i = 0; i < dict_attack_ctx->nested_nonce.count; i++) {
|
||||||
MfClassicNestedNonce* nonce = &dict_attack_ctx->nested_nonce.nonces[i];
|
MfClassicNestedNonce* nonce = &dict_attack_ctx->nested_nonce.nonces[i];
|
||||||
if(!is_weak_prng_nonce(nonce->nt)) hard_nt_count++;
|
if(!crypto1_is_weak_prng_nonce(nonce->nt)) hard_nt_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(hard_nt_count >= MF_CLASSIC_NESTED_NT_HARD_MINIMUM) {
|
if(hard_nt_count >= MF_CLASSIC_NESTED_NT_HARD_MINIMUM) {
|
||||||
@@ -1108,10 +1110,10 @@ NfcCommand mf_classic_poller_handler_nested_collect_nt(MfClassicPoller* instance
|
|||||||
}
|
}
|
||||||
|
|
||||||
NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance) {
|
NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance) {
|
||||||
// TODO: Discard outliers (e.g. greater than 3 standard deviations)
|
|
||||||
NfcCommand command = NfcCommandContinue;
|
NfcCommand command = NfcCommandContinue;
|
||||||
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
|
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
|
||||||
uint32_t nt_enc_temp_arr[MF_CLASSIC_NESTED_CALIBRATION_COUNT];
|
uint32_t nt_enc_temp_arr[MF_CLASSIC_NESTED_CALIBRATION_COUNT];
|
||||||
|
uint16_t distances[MF_CLASSIC_NESTED_CALIBRATION_COUNT - 1] = {0};
|
||||||
|
|
||||||
dict_attack_ctx->d_min = UINT16_MAX;
|
dict_attack_ctx->d_min = UINT16_MAX;
|
||||||
dict_attack_ctx->d_max = 0;
|
dict_attack_ctx->d_max = 0;
|
||||||
@@ -1172,7 +1174,7 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance)
|
|||||||
uint32_t nt_enc = bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));
|
uint32_t nt_enc = bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));
|
||||||
// Store the decrypted static encrypted nonce
|
// Store the decrypted static encrypted nonce
|
||||||
dict_attack_ctx->static_encrypted_nonce =
|
dict_attack_ctx->static_encrypted_nonce =
|
||||||
decrypt_nt_enc(cuid, nt_enc, dict_attack_ctx->nested_known_key);
|
crypto1_decrypt_nt_enc(cuid, nt_enc, dict_attack_ctx->nested_known_key);
|
||||||
|
|
||||||
dict_attack_ctx->calibrated = true;
|
dict_attack_ctx->calibrated = true;
|
||||||
|
|
||||||
@@ -1228,25 +1230,22 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance)
|
|||||||
// Find the distance between each nonce
|
// Find the distance between each nonce
|
||||||
FURI_LOG_E(TAG, "Calculating distance between nonces");
|
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);
|
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 < MF_CLASSIC_NESTED_CALIBRATION_COUNT;
|
uint8_t valid_distances = 0;
|
||||||
|
for(uint32_t collection_cycle = 1; collection_cycle < MF_CLASSIC_NESTED_CALIBRATION_COUNT;
|
||||||
collection_cycle++) {
|
collection_cycle++) {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
uint32_t decrypted_nt_enc = decrypt_nt_enc(
|
uint32_t decrypted_nt_enc = crypto1_decrypt_nt_enc(
|
||||||
cuid, nt_enc_temp_arr[collection_cycle], dict_attack_ctx->nested_known_key);
|
cuid, nt_enc_temp_arr[collection_cycle], dict_attack_ctx->nested_known_key);
|
||||||
for(int i = 0; i < 65535; i++) {
|
for(int i = 0; i < 65535; i++) {
|
||||||
uint32_t nth_successor = prng_successor(nt_prev, i);
|
uint32_t nth_successor = crypto1_prng_successor(nt_prev, i);
|
||||||
if(nth_successor != decrypted_nt_enc) {
|
if(nth_successor == decrypted_nt_enc) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if(collection_cycle > 0) {
|
|
||||||
FURI_LOG_E(TAG, "nt_enc (plain) %08lx", nth_successor);
|
FURI_LOG_E(TAG, "nt_enc (plain) %08lx", nth_successor);
|
||||||
FURI_LOG_E(TAG, "dist from nt prev: %i", i);
|
FURI_LOG_E(TAG, "dist from nt prev: %i", i);
|
||||||
if(i < dict_attack_ctx->d_min) dict_attack_ctx->d_min = i;
|
distances[valid_distances++] = i;
|
||||||
if(i > dict_attack_ctx->d_max) dict_attack_ctx->d_max = i;
|
nt_prev = nth_successor;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
nt_prev = nth_successor;
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if(!found) {
|
if(!found) {
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
@@ -1258,21 +1257,62 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some breathing room, doesn't account for overflows or static nested (FIXME)
|
// Calculate median and standard deviation
|
||||||
dict_attack_ctx->d_min -= 3;
|
if(valid_distances > 0) {
|
||||||
dict_attack_ctx->d_max += 3;
|
// Sort the distances array (bubble sort)
|
||||||
|
for(uint8_t i = 0; i < valid_distances - 1; i++) {
|
||||||
|
for(uint8_t j = 0; j < valid_distances - i - 1; j++) {
|
||||||
|
if(distances[j] > distances[j + 1]) {
|
||||||
|
uint16_t temp = distances[j];
|
||||||
|
distances[j] = distances[j + 1];
|
||||||
|
distances[j + 1] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate median
|
||||||
|
uint16_t median =
|
||||||
|
(valid_distances % 2 == 0) ?
|
||||||
|
(distances[valid_distances / 2 - 1] + distances[valid_distances / 2]) / 2 :
|
||||||
|
distances[valid_distances / 2];
|
||||||
|
|
||||||
|
// Calculate standard deviation
|
||||||
|
float sum = 0, sum_sq = 0;
|
||||||
|
for(uint8_t i = 0; i < valid_distances; i++) {
|
||||||
|
sum += distances[i];
|
||||||
|
sum_sq += (float)distances[i] * distances[i];
|
||||||
|
}
|
||||||
|
float mean = sum / valid_distances;
|
||||||
|
float variance = (sum_sq / valid_distances) - (mean * mean);
|
||||||
|
float std_dev = sqrtf(variance);
|
||||||
|
|
||||||
|
// Filter out values over 3 standard deviations away from the median
|
||||||
|
dict_attack_ctx->d_min = UINT16_MAX;
|
||||||
|
dict_attack_ctx->d_max = 0;
|
||||||
|
for(uint8_t i = 0; i < valid_distances; i++) {
|
||||||
|
if(fabsf((float)distances[i] - median) <= 3 * std_dev) {
|
||||||
|
if(distances[i] < dict_attack_ctx->d_min) dict_attack_ctx->d_min = distances[i];
|
||||||
|
if(distances[i] > dict_attack_ctx->d_max) dict_attack_ctx->d_max = distances[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some breathing room
|
||||||
|
dict_attack_ctx->d_min = (dict_attack_ctx->d_min > 3) ? dict_attack_ctx->d_min - 3 : 0;
|
||||||
|
dict_attack_ctx->d_max += 3;
|
||||||
|
}
|
||||||
|
|
||||||
furi_assert(dict_attack_ctx->d_min <= dict_attack_ctx->d_max);
|
furi_assert(dict_attack_ctx->d_min <= dict_attack_ctx->d_max);
|
||||||
dict_attack_ctx->calibrated = true;
|
dict_attack_ctx->calibrated = true;
|
||||||
instance->state = MfClassicPollerStateNestedController;
|
instance->state = MfClassicPollerStateNestedController;
|
||||||
|
|
||||||
mf_classic_poller_halt(instance);
|
mf_classic_poller_halt(instance);
|
||||||
|
uint16_t d_dist = dict_attack_ctx->d_max - dict_attack_ctx->d_min;
|
||||||
FURI_LOG_E(
|
FURI_LOG_E(
|
||||||
TAG,
|
TAG,
|
||||||
"Calibration completed: min=%u max=%u static=%s",
|
"Calibration completed: min=%u max=%u static=%s",
|
||||||
dict_attack_ctx->d_min,
|
dict_attack_ctx->d_min,
|
||||||
dict_attack_ctx->d_max,
|
dict_attack_ctx->d_max,
|
||||||
(dict_attack_ctx->d_min == dict_attack_ctx->d_max) ? "true" : "false");
|
((d_dist >= 3) && (d_dist <= 6)) ? "true" : "false");
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
@@ -1381,17 +1421,25 @@ NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* inst
|
|||||||
uint32_t nt_prev = 0, decrypted_nt_prev = 0, found_nt = 0;
|
uint32_t nt_prev = 0, decrypted_nt_prev = 0, found_nt = 0;
|
||||||
uint16_t dist = 0;
|
uint16_t dist = 0;
|
||||||
if(is_weak && !(dict_attack_ctx->static_encrypted)) {
|
if(is_weak && !(dict_attack_ctx->static_encrypted)) {
|
||||||
|
// Ensure this isn't the same nonce as the previous collection
|
||||||
|
if((dict_attack_ctx->nested_nonce.count == 1) &&
|
||||||
|
(dict_attack_ctx->nested_nonce.nonces[0].nt_enc == nt_enc)) {
|
||||||
|
FURI_LOG_E(TAG, "Duplicate nonce, dismissing collection attempt");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Decrypt the previous nonce
|
// Decrypt the previous nonce
|
||||||
nt_prev = nt_enc_temp_arr[nt_enc_collected - 1];
|
nt_prev = nt_enc_temp_arr[nt_enc_collected - 1];
|
||||||
decrypted_nt_prev = decrypt_nt_enc(cuid, nt_prev, dict_attack_ctx->nested_known_key);
|
decrypted_nt_prev =
|
||||||
|
crypto1_decrypt_nt_enc(cuid, nt_prev, dict_attack_ctx->nested_known_key);
|
||||||
|
|
||||||
// Find matching nt_enc plain at expected distance
|
// Find matching nt_enc plain at expected distance
|
||||||
found_nt = 0;
|
found_nt = 0;
|
||||||
uint8_t found_nt_cnt = 0;
|
uint8_t found_nt_cnt = 0;
|
||||||
uint16_t current_dist = dict_attack_ctx->d_min;
|
uint16_t current_dist = dict_attack_ctx->d_min;
|
||||||
while(current_dist <= dict_attack_ctx->d_max) {
|
while(current_dist <= dict_attack_ctx->d_max) {
|
||||||
uint32_t nth_successor = prng_successor(decrypted_nt_prev, current_dist);
|
uint32_t nth_successor = crypto1_prng_successor(decrypted_nt_prev, current_dist);
|
||||||
if(nonce_matches_encrypted_parity_bits(
|
if(crypto1_nonce_matches_encrypted_parity_bits(
|
||||||
nth_successor, nth_successor ^ nt_enc, parity)) {
|
nth_successor, nth_successor ^ nt_enc, parity)) {
|
||||||
found_nt_cnt++;
|
found_nt_cnt++;
|
||||||
if(found_nt_cnt > 1) {
|
if(found_nt_cnt > 1) {
|
||||||
@@ -1415,6 +1463,7 @@ NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* inst
|
|||||||
// Hardnested
|
// Hardnested
|
||||||
if(!is_byte_found(dict_attack_ctx->nt_enc_msb, (nt_enc >> 24) & 0xFF)) {
|
if(!is_byte_found(dict_attack_ctx->nt_enc_msb, (nt_enc >> 24) & 0xFF)) {
|
||||||
set_byte_found(dict_attack_ctx->nt_enc_msb, (nt_enc >> 24) & 0xFF);
|
set_byte_found(dict_attack_ctx->nt_enc_msb, (nt_enc >> 24) & 0xFF);
|
||||||
|
dict_attack_ctx->msb_count++;
|
||||||
// Add unique parity to sum
|
// Add unique parity to sum
|
||||||
dict_attack_ctx->msb_par_sum += nfc_util_even_parity32(parity & 0x08);
|
dict_attack_ctx->msb_par_sum += nfc_util_even_parity32(parity & 0x08);
|
||||||
}
|
}
|
||||||
@@ -1487,13 +1536,13 @@ static MfClassicKey* search_dicts_for_nonce_key(
|
|||||||
bool full_match = true;
|
bool full_match = true;
|
||||||
for(uint8_t j = 0; j < nonce_array->count; j++) {
|
for(uint8_t j = 0; j < nonce_array->count; j++) {
|
||||||
// Verify nonce matches encrypted parity bits for all nonces
|
// Verify nonce matches encrypted parity bits for all nonces
|
||||||
uint32_t nt_enc_plain = decrypt_nt_enc(
|
uint32_t nt_enc_plain = crypto1_decrypt_nt_enc(
|
||||||
nonce_array->nonces[j].cuid, nonce_array->nonces[j].nt_enc, stack_key);
|
nonce_array->nonces[j].cuid, nonce_array->nonces[j].nt_enc, stack_key);
|
||||||
if(is_weak) {
|
if(is_weak) {
|
||||||
full_match &= is_weak_prng_nonce(nt_enc_plain);
|
full_match &= crypto1_is_weak_prng_nonce(nt_enc_plain);
|
||||||
if(!full_match) break;
|
if(!full_match) break;
|
||||||
}
|
}
|
||||||
full_match &= nonce_matches_encrypted_parity_bits(
|
full_match &= crypto1_nonce_matches_encrypted_parity_bits(
|
||||||
nt_enc_plain,
|
nt_enc_plain,
|
||||||
nt_enc_plain ^ nonce_array->nonces[j].nt_enc,
|
nt_enc_plain ^ nonce_array->nonces[j].nt_enc,
|
||||||
nonce_array->nonces[j].par);
|
nonce_array->nonces[j].par);
|
||||||
@@ -1751,15 +1800,6 @@ bool mf_classic_nested_is_target_key_found(MfClassicPoller* instance, bool is_di
|
|||||||
return mf_classic_is_key_found(instance->data, target_sector, target_key_type);
|
return mf_classic_is_key_found(instance->data, target_sector, target_key_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool found_all_nt_enc_msb(const MfClassicPollerDictAttackContext* dict_attack_ctx) {
|
|
||||||
for(int i = 0; i < 32; i++) {
|
|
||||||
if(dict_attack_ctx->nt_enc_msb[i] != 0xFF) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_valid_sum(uint16_t sum) {
|
bool is_valid_sum(uint16_t sum) {
|
||||||
for(size_t i = 0; i < 19; i++) {
|
for(size_t i = 0; i < 19; i++) {
|
||||||
if(sum == valid_sums[i]) {
|
if(sum == valid_sums[i]) {
|
||||||
@@ -1964,7 +2004,7 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance
|
|||||||
}
|
}
|
||||||
// Target all remaining sectors, key A and B
|
// Target all remaining sectors, key A and B
|
||||||
if(dict_attack_ctx->nested_target_key < nonce_collect_key_max) {
|
if(dict_attack_ctx->nested_target_key < nonce_collect_key_max) {
|
||||||
if((!(is_weak)) && found_all_nt_enc_msb(dict_attack_ctx)) {
|
if((!(is_weak)) && (dict_attack_ctx->msb_count == (UINT8_MAX + 1))) {
|
||||||
if(is_valid_sum(dict_attack_ctx->msb_par_sum)) {
|
if(is_valid_sum(dict_attack_ctx->msb_par_sum)) {
|
||||||
// All Hardnested nonces collected
|
// All Hardnested nonces collected
|
||||||
dict_attack_ctx->nested_target_key++;
|
dict_attack_ctx->nested_target_key++;
|
||||||
@@ -1975,6 +2015,7 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance
|
|||||||
dict_attack_ctx->attempt_count++;
|
dict_attack_ctx->attempt_count++;
|
||||||
instance->state = MfClassicPollerStateNestedCollectNtEnc;
|
instance->state = MfClassicPollerStateNestedCollectNtEnc;
|
||||||
}
|
}
|
||||||
|
dict_attack_ctx->msb_count = 0;
|
||||||
dict_attack_ctx->msb_par_sum = 0;
|
dict_attack_ctx->msb_par_sum = 0;
|
||||||
memset(dict_attack_ctx->nt_enc_msb, 0, sizeof(dict_attack_ctx->nt_enc_msb));
|
memset(dict_attack_ctx->nt_enc_msb, 0, sizeof(dict_attack_ctx->nt_enc_msb));
|
||||||
return command;
|
return command;
|
||||||
@@ -2038,6 +2079,7 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance
|
|||||||
dict_attack_ctx->nested_target_key += 2;
|
dict_attack_ctx->nested_target_key += 2;
|
||||||
dict_attack_ctx->current_key_checked = false;
|
dict_attack_ctx->current_key_checked = false;
|
||||||
} else {
|
} else {
|
||||||
|
dict_attack_ctx->msb_count = 0;
|
||||||
dict_attack_ctx->msb_par_sum = 0;
|
dict_attack_ctx->msb_par_sum = 0;
|
||||||
memset(dict_attack_ctx->nt_enc_msb, 0, sizeof(dict_attack_ctx->nt_enc_msb));
|
memset(dict_attack_ctx->nt_enc_msb, 0, sizeof(dict_attack_ctx->nt_enc_msb));
|
||||||
dict_attack_ctx->nested_target_key++;
|
dict_attack_ctx->nested_target_key++;
|
||||||
|
|||||||
@@ -80,6 +80,9 @@ typedef struct {
|
|||||||
uint8_t nested_phase; /**< Nested attack phase. */
|
uint8_t nested_phase; /**< Nested attack phase. */
|
||||||
uint8_t prng_type; /**< PRNG (weak or hard). */
|
uint8_t prng_type; /**< PRNG (weak or hard). */
|
||||||
uint8_t backdoor; /**< Backdoor type. */
|
uint8_t 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;
|
} MfClassicPollerEventDataUpdate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,12 +3,12 @@
|
|||||||
#include "mf_classic_poller.h"
|
#include "mf_classic_poller.h"
|
||||||
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h>
|
#include <lib/nfc/protocols/iso14443_3a/iso14443_3a_poller_i.h>
|
||||||
#include <bit_lib/bit_lib.h>
|
#include <bit_lib/bit_lib.h>
|
||||||
#include "nfc/helpers/iso14443_crc.h"
|
#include <nfc/helpers/iso14443_crc.h>
|
||||||
#include <nfc/helpers/crypto1.h>
|
#include <nfc/helpers/crypto1.h>
|
||||||
#include <stream/stream.h>
|
#include <stream/stream.h>
|
||||||
#include <stream/buffered_file_stream.h>
|
#include <stream/buffered_file_stream.h>
|
||||||
#include "keys_dict.h"
|
#include "keys_dict.h"
|
||||||
#include "helpers/nfc_util.h"
|
#include <helpers/nfc_util.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@@ -179,6 +179,7 @@ typedef struct {
|
|||||||
uint8_t nt_enc_msb
|
uint8_t nt_enc_msb
|
||||||
[32]; // Bit-packed array to track which unique most significant bytes have been seen (256 bits = 32 bytes)
|
[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_par_sum; // Sum of parity bits for each unique most significant byte
|
||||||
|
uint16_t msb_count; // Number of unique most significant bytes seen
|
||||||
} MfClassicPollerDictAttackContext;
|
} MfClassicPollerDictAttackContext;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -908,10 +908,15 @@ Function,+,crypto1_alloc,Crypto1*,
|
|||||||
Function,+,crypto1_bit,uint8_t,"Crypto1*, uint8_t, int"
|
Function,+,crypto1_bit,uint8_t,"Crypto1*, uint8_t, int"
|
||||||
Function,+,crypto1_byte,uint8_t,"Crypto1*, uint8_t, int"
|
Function,+,crypto1_byte,uint8_t,"Crypto1*, uint8_t, int"
|
||||||
Function,+,crypto1_decrypt,void,"Crypto1*, const BitBuffer*, BitBuffer*"
|
Function,+,crypto1_decrypt,void,"Crypto1*, const BitBuffer*, BitBuffer*"
|
||||||
|
Function,+,crypto1_decrypt_nt_enc,uint32_t,"uint32_t, uint32_t, MfClassicKey"
|
||||||
Function,+,crypto1_encrypt,void,"Crypto1*, uint8_t*, const BitBuffer*, BitBuffer*"
|
Function,+,crypto1_encrypt,void,"Crypto1*, uint8_t*, const BitBuffer*, BitBuffer*"
|
||||||
Function,+,crypto1_encrypt_reader_nonce,void,"Crypto1*, uint64_t, uint32_t, uint8_t*, uint8_t*, BitBuffer*, _Bool"
|
Function,+,crypto1_encrypt_reader_nonce,void,"Crypto1*, uint64_t, uint32_t, uint8_t*, uint8_t*, BitBuffer*, _Bool"
|
||||||
Function,+,crypto1_free,void,Crypto1*
|
Function,+,crypto1_free,void,Crypto1*
|
||||||
Function,+,crypto1_init,void,"Crypto1*, uint64_t"
|
Function,+,crypto1_init,void,"Crypto1*, uint64_t"
|
||||||
|
Function,+,crypto1_is_weak_prng_nonce,_Bool,uint32_t
|
||||||
|
Function,+,crypto1_lfsr_rollback_word,uint32_t,"Crypto1*, uint32_t, int"
|
||||||
|
Function,+,crypto1_nonce_matches_encrypted_parity_bits,_Bool,"uint32_t, uint32_t, uint8_t"
|
||||||
|
Function,+,crypto1_prng_successor,uint32_t,"uint32_t, uint32_t"
|
||||||
Function,+,crypto1_reset,void,Crypto1*
|
Function,+,crypto1_reset,void,Crypto1*
|
||||||
Function,+,crypto1_word,uint32_t,"Crypto1*, uint32_t, int"
|
Function,+,crypto1_word,uint32_t,"Crypto1*, uint32_t, int"
|
||||||
Function,-,ctermid,char*,char*
|
Function,-,ctermid,char*,char*
|
||||||
@@ -922,7 +927,6 @@ Function,+,datetime_get_days_per_year,uint16_t,uint16_t
|
|||||||
Function,+,datetime_is_leap_year,_Bool,uint16_t
|
Function,+,datetime_is_leap_year,_Bool,uint16_t
|
||||||
Function,+,datetime_timestamp_to_datetime,void,"uint32_t, DateTime*"
|
Function,+,datetime_timestamp_to_datetime,void,"uint32_t, DateTime*"
|
||||||
Function,+,datetime_validate_datetime,_Bool,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_alloc,DialogEx*,
|
||||||
Function,+,dialog_ex_disable_extended_events,void,DialogEx*
|
Function,+,dialog_ex_disable_extended_events,void,DialogEx*
|
||||||
Function,+,dialog_ex_enable_extended_events,void,DialogEx*
|
Function,+,dialog_ex_enable_extended_events,void,DialogEx*
|
||||||
@@ -2112,7 +2116,6 @@ Function,-,initstate,char*,"unsigned, char*, size_t"
|
|||||||
Function,+,input_get_key_name,const char*,InputKey
|
Function,+,input_get_key_name,const char*,InputKey
|
||||||
Function,+,input_get_type_name,const char*,InputType
|
Function,+,input_get_type_name,const char*,InputType
|
||||||
Function,-,iprintf,int,"const char*, ..."
|
Function,-,iprintf,int,"const char*, ..."
|
||||||
Function,+,is_weak_prng_nonce,_Bool,uint32_t
|
|
||||||
Function,-,isalnum,int,int
|
Function,-,isalnum,int,int
|
||||||
Function,-,isalnum_l,int,"int, locale_t"
|
Function,-,isalnum_l,int,"int, locale_t"
|
||||||
Function,-,isalpha,int,int
|
Function,-,isalpha,int,int
|
||||||
@@ -2298,7 +2301,6 @@ Function,+,lfrfid_worker_stop,void,LFRFIDWorker*
|
|||||||
Function,+,lfrfid_worker_stop_thread,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_and_set_pass_start,void,"LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*"
|
||||||
Function,+,lfrfid_worker_write_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,double,double
|
||||||
Function,-,lgamma_r,double,"double, int*"
|
Function,-,lgamma_r,double,"double, int*"
|
||||||
Function,-,lgammaf,float,float
|
Function,-,lgammaf,float,float
|
||||||
@@ -2905,7 +2907,6 @@ Function,+,nfc_util_even_parity32,uint8_t,uint32_t
|
|||||||
Function,+,nfc_util_even_parity8,uint8_t,uint8_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_parity,void,"const uint8_t*, uint8_t*, uint8_t"
|
||||||
Function,+,nfc_util_odd_parity8,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,void,"NotificationApp*, const NotificationSequence*"
|
||||||
Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*"
|
Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*"
|
||||||
Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*"
|
Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*"
|
||||||
@@ -3019,7 +3020,6 @@ Function,+,powf,float,"float, float"
|
|||||||
Function,-,powl,long double,"long double, long double"
|
Function,-,powl,long double,"long double, long double"
|
||||||
Function,+,pretty_format_bytes_hex_canonical,void,"FuriString*, size_t, const char*, const uint8_t*, size_t"
|
Function,+,pretty_format_bytes_hex_canonical,void,"FuriString*, size_t, const char*, const uint8_t*, size_t"
|
||||||
Function,-,printf,int,"const char*, ..."
|
Function,-,printf,int,"const char*, ..."
|
||||||
Function,+,prng_successor,uint32_t,"uint32_t, uint32_t"
|
|
||||||
Function,+,process_favorite_launch,_Bool,char**
|
Function,+,process_favorite_launch,_Bool,char**
|
||||||
Function,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..."
|
Function,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..."
|
||||||
Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase**, size_t"
|
Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase**, size_t"
|
||||||
|
|||||||
|
Reference in New Issue
Block a user