Merge branch 'nestednonces' into ofw-3822-nestednonces

This commit is contained in:
Willy-JL
2024-09-24 05:56:21 +01:00
11 changed files with 198 additions and 79 deletions

View File

@@ -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 {

View File

@@ -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);
} }

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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;
} }

View File

@@ -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
} }

View File

@@ -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));

View File

@@ -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++;

View File

@@ -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;
/** /**

View File

@@ -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 {

View File

@@ -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"
1 entry status name type params
908 Function + crypto1_bit uint8_t Crypto1*, uint8_t, int
909 Function + crypto1_byte uint8_t Crypto1*, uint8_t, int
910 Function + crypto1_decrypt void Crypto1*, const BitBuffer*, BitBuffer*
911 Function + crypto1_decrypt_nt_enc uint32_t uint32_t, uint32_t, MfClassicKey
912 Function + crypto1_encrypt void Crypto1*, uint8_t*, const BitBuffer*, BitBuffer*
913 Function + crypto1_encrypt_reader_nonce void Crypto1*, uint64_t, uint32_t, uint8_t*, uint8_t*, BitBuffer*, _Bool
914 Function + crypto1_free void Crypto1*
915 Function + crypto1_init void Crypto1*, uint64_t
916 Function + crypto1_is_weak_prng_nonce _Bool uint32_t
917 Function + crypto1_lfsr_rollback_word uint32_t Crypto1*, uint32_t, int
918 Function + crypto1_nonce_matches_encrypted_parity_bits _Bool uint32_t, uint32_t, uint8_t
919 Function + crypto1_prng_successor uint32_t uint32_t, uint32_t
920 Function + crypto1_reset void Crypto1*
921 Function + crypto1_word uint32_t Crypto1*, uint32_t, int
922 Function - ctermid char* char*
927 Function + datetime_is_leap_year _Bool uint16_t
928 Function + datetime_timestamp_to_datetime void uint32_t, DateTime*
929 Function + datetime_validate_datetime _Bool DateTime*
Function + decrypt_nt_enc uint32_t uint32_t, uint32_t, MfClassicKey
930 Function + dialog_ex_alloc DialogEx*
931 Function + dialog_ex_disable_extended_events void DialogEx*
932 Function + dialog_ex_enable_extended_events void DialogEx*
2116 Function + input_get_key_name const char* InputKey
2117 Function + input_get_type_name const char* InputType
2118 Function - iprintf int const char*, ...
Function + is_weak_prng_nonce _Bool uint32_t
2119 Function - isalnum int int
2120 Function - isalnum_l int int, locale_t
2121 Function - isalpha int int
2301 Function + lfrfid_worker_stop_thread void LFRFIDWorker*
2302 Function + lfrfid_worker_write_and_set_pass_start void LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*
2303 Function + lfrfid_worker_write_start void LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*
Function + lfsr_rollback_word uint32_t Crypto1*, uint32_t, int
2304 Function - lgamma double double
2305 Function - lgamma_r double double, int*
2306 Function - lgammaf float float
2907 Function + nfc_util_even_parity8 uint8_t uint8_t
2908 Function + nfc_util_odd_parity void const uint8_t*, uint8_t*, uint8_t
2909 Function + nfc_util_odd_parity8 uint8_t uint8_t
Function + nonce_matches_encrypted_parity_bits _Bool uint32_t, uint32_t, uint8_t
2910 Function + notification_internal_message void NotificationApp*, const NotificationSequence*
2911 Function + notification_internal_message_block void NotificationApp*, const NotificationSequence*
2912 Function + notification_message void NotificationApp*, const NotificationSequence*
3020 Function - powl long double long double, long double
3021 Function + pretty_format_bytes_hex_canonical void FuriString*, size_t, const char*, const uint8_t*, size_t
3022 Function - printf int const char*, ...
Function + prng_successor uint32_t uint32_t, uint32_t
3023 Function + process_favorite_launch _Bool char**
3024 Function + property_value_out void PropertyValueContext*, const char*, unsigned int, ...
3025 Function + protocol_dict_alloc ProtocolDict* const ProtocolBase**, size_t