1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 12:42:30 +04:00

First attempt disambiguous nonce implementation

This commit is contained in:
noproto
2024-08-14 02:25:57 -04:00
parent 6332ec7478
commit 01b19483c5
5 changed files with 108 additions and 117 deletions

View File

@@ -206,8 +206,7 @@ uint32_t lfsr_rollback_word(Crypto1* crypto1, uint32_t in, int fb) {
return ret; return ret;
} }
// Return true if the nonce is invalid else return false bool nonce_matches_encrypted_parity_bits(uint32_t nt, uint32_t ks, uint8_t nt_par_enc) {
bool valid_nonce(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,3 +214,23 @@ bool valid_nonce(uint32_t nt, uint32_t ks, uint8_t nt_par_enc) {
(nfc_util_even_parity8((nt >> 8) & 0xFF) == (nfc_util_even_parity8((nt >> 8) & 0xFF) ==
(((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) {
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;
}

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "protocols/mf_classic/mf_classic.h"
#include <toolbox/bit_buffer.h> #include <toolbox/bit_buffer.h>
#ifdef __cplusplus #ifdef __cplusplus
@@ -40,7 +41,11 @@ void crypto1_encrypt_reader_nonce(
uint32_t lfsr_rollback_word(Crypto1* crypto1, uint32_t in, int fb); uint32_t lfsr_rollback_word(Crypto1* crypto1, uint32_t in, int fb);
bool valid_nonce(uint32_t nt, uint32_t ks, uint8_t nt_par_enc); 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); uint32_t prng_successor(uint32_t x, uint32_t n);

View File

@@ -3,8 +3,6 @@
#include <nfc/protocols/nfc_poller_base.h> #include <nfc/protocols/nfc_poller_base.h>
#include <furi.h> #include <furi.h>
#include <stream/stream.h>
#include <stream/buffered_file_stream.h>
#define TAG "MfClassicPoller" #define TAG "MfClassicPoller"
@@ -838,17 +836,6 @@ NfcCommand mf_classic_poller_handler_key_reuse_read_sector(MfClassicPoller* inst
return command; return command;
} }
bool validate_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);
}
// Helper function to add a nonce to the array // Helper function to add a nonce to the array
static bool add_nested_nonce( static bool add_nested_nonce(
MfClassicNestedNonceArray* array, MfClassicNestedNonceArray* array,
@@ -881,7 +868,7 @@ NfcCommand mf_classic_poller_handler_nested_analyze_prng(MfClassicPoller* instan
uint8_t nonce_limit = 5; uint8_t nonce_limit = 5;
if(dict_attack_ctx->nt_count > 0) { if(dict_attack_ctx->nt_count > 0) {
if(!validate_prng_nonce(dict_attack_ctx->nt_prev)) dict_attack_ctx->hard_nt_count++; if(!is_weak_prng_nonce(dict_attack_ctx->nt_prev)) dict_attack_ctx->hard_nt_count++;
} }
if(dict_attack_ctx->nt_count < nonce_limit) { if(dict_attack_ctx->nt_count < nonce_limit) {
@@ -958,35 +945,16 @@ NfcCommand mf_classic_poller_handler_nested_collect_nt(MfClassicPoller* instance
return command; return command;
} }
void bubble_sort(uint16_t arr[], int n) {
for(int i = 0; i < n - 1; i++) {
for(int j = 0; j < n - i - 1; j++) {
if(arr[j] > arr[j + 1]) {
uint16_t temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
uint16_t get_median(uint16_t arr[], int n) {
bubble_sort(arr, n);
return arr[n / 2];
}
NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance) { NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance) {
// TODO: Calibrate backdoored tags too // TODO: Calibrate backdoored tags too
// TODO: Check if we have already identified the tag as static encrypted // TODO: Check if we have already identified the tag as static encrypted
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;
uint8_t nt_enc_calibration_cnt = 11; uint8_t nt_enc_calibration_cnt = 21;
uint32_t nt_enc_temp_arr[nt_enc_calibration_cnt]; uint32_t nt_enc_temp_arr[nt_enc_calibration_cnt];
uint16_t d_min = UINT16_MAX; dict_attack_ctx->d_min = UINT16_MAX;
uint16_t d_max = 0; dict_attack_ctx->d_max = 0;
uint16_t d_all[nt_enc_calibration_cnt - 1];
uint8_t d_all_cnt = 0;
uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector); uint8_t block = mf_classic_get_first_block_num_of_sector(dict_attack_ctx->reuse_key_sector);
uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data); uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);
@@ -1068,24 +1036,21 @@ 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->current_key.data, 6); uint64_t known_key = bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, 6);
Crypto1 crypto;
crypto1_init(&crypto, known_key);
for(uint32_t collection_cycle = 0; collection_cycle < nt_enc_calibration_cnt; for(uint32_t collection_cycle = 0; collection_cycle < nt_enc_calibration_cnt;
collection_cycle++) { collection_cycle++) {
bool found = false; bool found = false;
uint32_t decrypted_nt_enc =
decrypt_nt_enc(cuid, nt_enc_temp_arr[collection_cycle], dict_attack_ctx->current_key);
for(int i = 0; i < 65535; i++) { for(int i = 0; i < 65535; i++) {
Crypto1 crypto_temp = {.odd = crypto.odd, .even = crypto.even};
uint32_t nth_successor = prng_successor(nt_prev, i); uint32_t nth_successor = prng_successor(nt_prev, i);
if((nth_successor ^ crypto1_word(&crypto_temp, cuid ^ nth_successor, 0)) != if(nth_successor != decrypted_nt_enc) {
nt_enc_temp_arr[collection_cycle]) {
continue; continue;
} }
if(collection_cycle > 0) { 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);
d_all[d_all_cnt++] = i; if(i < dict_attack_ctx->d_min) dict_attack_ctx->d_min = i;
if(i < d_min) d_min = i; if(i > dict_attack_ctx->d_max) dict_attack_ctx->d_max = i;
if(i > d_max) d_max = i;
} }
nt_prev = nth_successor; nt_prev = nth_successor;
found = true; found = true;
@@ -1101,27 +1066,30 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance)
} }
} }
dict_attack_ctx->d_median = get_median(d_all, d_all_cnt); // Some breathing room, doesn't account for overflows or static nested (FIXME)
dict_attack_ctx->d_min -= 3;
dict_attack_ctx->d_max += 3;
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);
FURI_LOG_E( FURI_LOG_E(
TAG, TAG,
"Calibration completed: med=%u min=%u max=%u static=%s", "Calibration completed: min=%u max=%u static=%s",
dict_attack_ctx->d_median, dict_attack_ctx->d_min,
d_min, dict_attack_ctx->d_max,
d_max, (dict_attack_ctx->d_min == dict_attack_ctx->d_max) ? "true" : "false");
(d_min == d_max) ? "true" : "false");
return command; return command;
} }
NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* instance) { NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* instance) {
// TODO: Handle when nonce is not collected (retry counter? Do not increment nested_target_key) // TODO: Handle when nonce is not collected (retry counter? Do not increment nested_target_key)
// TODO: Look into using MfClassicNt more
NfcCommand command = NfcCommandReset; NfcCommand command = NfcCommandReset;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx; MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
bool collection_success = false;
do { do {
if(dict_attack_ctx->prng_type == MfClassicPrngTypeHard) { if(dict_attack_ctx->prng_type == MfClassicPrngTypeHard) {
@@ -1163,7 +1131,7 @@ NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* inst
MfClassicError error; MfClassicError error;
uint8_t nonce_pair_index = dict_attack_ctx->nested_target_key % 2; uint8_t nonce_pair_index = dict_attack_ctx->nested_target_key % 2;
uint8_t nt_enc_per_collection = 2 + nonce_pair_index; uint8_t nt_enc_per_collection = (dict_attack_ctx->attempt_count + 2) + nonce_pair_index;
MfClassicKeyType target_key_type = ((dict_attack_ctx->nested_target_key & 0x03) < 2) ? MfClassicKeyType target_key_type = ((dict_attack_ctx->nested_target_key & 0x03) < 2) ?
MfClassicKeyTypeA : MfClassicKeyTypeA :
MfClassicKeyTypeB; MfClassicKeyTypeB;
@@ -1232,56 +1200,40 @@ NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* inst
} }
// Decrypt the previous nonce // Decrypt the previous nonce
uint32_t nt_prev = nt_enc_temp_arr[nt_enc_collected - 1]; uint32_t nt_prev = nt_enc_temp_arr[nt_enc_collected - 1];
uint64_t known_key = bit_lib_bytes_to_num_be(dict_attack_ctx->current_key.data, 6); uint32_t decrypted_nt_prev = decrypt_nt_enc(cuid, nt_prev, dict_attack_ctx->current_key);
Crypto1 crypto_temp;
crypto1_init(&crypto_temp, known_key);
crypto1_word(&crypto_temp, nt_prev ^ cuid, 1);
uint32_t decrypted_nt_prev =
(nt_prev ^ lfsr_rollback_word(&crypto_temp, nt_prev ^ cuid, 1));
// Find matching nt_enc plain at expected distance // Find matching nt_enc plain at expected distance
bool found_matching_nt = false;
uint32_t found_nt = 0; uint32_t found_nt = 0;
uint16_t current_dist = 0; uint8_t found_nt_cnt = 0;
const uint16_t max_dist = 16; // 32 would work too uint16_t current_dist = dict_attack_ctx->d_min;
// TODO: Better handling of overflows (allow wrap instead of stopping?) while(current_dist <= dict_attack_ctx->d_max) {
while(!found_matching_nt && current_dist < max_dist && uint32_t nth_successor = prng_successor(decrypted_nt_prev, current_dist);
((dict_attack_ctx->d_median - current_dist) != 0) && if(nonce_matches_encrypted_parity_bits(nth_successor, nth_successor ^ nt_enc, parity)) {
((dict_attack_ctx->d_median + current_dist) != UINT16_MAX)) { found_nt_cnt++;
uint32_t nth_successor_positive = if(found_nt_cnt > 1) {
prng_successor(decrypted_nt_prev, dict_attack_ctx->d_median + current_dist); FURI_LOG_E(TAG, "Ambiguous nonce, dismissing collection attempt");
if(valid_nonce(nth_successor_positive, nth_successor_positive ^ nt_enc, parity)) {
found_matching_nt = true;
found_nt = nth_successor_positive;
break;
}
if(current_dist > 0) {
uint32_t nth_successor_negative =
prng_successor(decrypted_nt_prev, dict_attack_ctx->d_median - current_dist);
if(valid_nonce(nth_successor_negative, nth_successor_negative ^ nt_enc, parity)) {
found_matching_nt = true;
found_nt = nth_successor_negative;
break; break;
} }
found_nt = nth_successor;
} }
current_dist++; current_dist++;
} }
if(found_nt_cnt != 1) {
break;
}
// Add the nonce to the array // Add the nonce to the array
if(found_matching_nt) { if(add_nested_nonce(
collection_success = add_nested_nonce(
&dict_attack_ctx->nested_nonce, &dict_attack_ctx->nested_nonce,
cuid, cuid,
dict_attack_ctx->nested_target_key, dict_attack_ctx->nested_target_key,
found_nt, found_nt,
nt_enc, nt_enc,
parity, parity,
0); 0)) {
if(!collection_success) { dict_attack_ctx->nested_state = MfClassicNestedStatePassed;
FURI_LOG_E(TAG, "Failed to add nested nonce to array. OOM?");
}
} else { } else {
FURI_LOG_E(TAG, "Failed to find matching nonce"); FURI_LOG_E(TAG, "Failed to add nested nonce to array. OOM?");
} }
FURI_LOG_E( FURI_LOG_E(
@@ -1304,13 +1256,6 @@ NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* inst
FURI_LOG_E(TAG, "nt_enc prev decrypted: %08lx", decrypted_nt_prev); FURI_LOG_E(TAG, "nt_enc prev decrypted: %08lx", decrypted_nt_prev);
} while(false); } while(false);
// Probably belongs in the controller
if(collection_success) {
dict_attack_ctx->nested_target_key++;
dict_attack_ctx->retry_counter = 0;
} else {
dict_attack_ctx->retry_counter++;
}
instance->state = MfClassicPollerStateNestedController; instance->state = MfClassicPollerStateNestedController;
mf_classic_poller_halt(instance); mf_classic_poller_halt(instance);
@@ -1409,16 +1354,17 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance
// Iterate through keys // Iterate through keys
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;
if((dict_attack_ctx->nested_nonce.count > 0) && if((dict_attack_ctx->prng_type == MfClassicPrngTypeWeak) &&
(dict_attack_ctx->nested_target_key % 2 == 0)) { (dict_attack_ctx->nested_nonce.count == 2)) {
if(dict_attack_ctx->prng_type == MfClassicPrngTypeWeak) {
instance->state = MfClassicPollerStateNestedDictAttack; instance->state = MfClassicPollerStateNestedDictAttack;
return command; return command;
} else if(dict_attack_ctx->prng_type == MfClassicPrngTypeHard) { } else if(
(dict_attack_ctx->prng_type == MfClassicPrngTypeHard) &&
(dict_attack_ctx->nested_nonce.count > 0)) {
// TODO: Need to think about the meaning of nested_target_key for hard PRNG
instance->state = MfClassicPollerStateNestedLog; instance->state = MfClassicPollerStateNestedLog;
return command; return command;
} }
}
if(dict_attack_ctx->backdoor == MfClassicBackdoorUnknown) { if(dict_attack_ctx->backdoor == MfClassicBackdoorUnknown) {
instance->state = MfClassicPollerStateNestedAnalyzeBackdoor; instance->state = MfClassicPollerStateNestedAnalyzeBackdoor;
return command; return command;
@@ -1430,15 +1376,24 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance
return command; return command;
} }
// Target all sectors, key A and B, first and second nonce // Target all sectors, key A and B, first and second nonce
// TODO: Missing weak condition, target_key logic doesn't apply the same to hard
if(dict_attack_ctx->nested_target_key < (instance->sectors_total * 4)) { if(dict_attack_ctx->nested_target_key < (instance->sectors_total * 4)) {
if(dict_attack_ctx->retry_counter > 3) { if(dict_attack_ctx->nested_state == MfClassicNestedStateFailed) {
// Bad sector, skip dict_attack_ctx->attempt_count++;
} else if(dict_attack_ctx->nested_state == MfClassicNestedStatePassed) {
dict_attack_ctx->nested_target_key++;
dict_attack_ctx->attempt_count = 0;
}
dict_attack_ctx->nested_state = MfClassicNestedStateNone;
if(dict_attack_ctx->attempt_count >= 20) {
// Unpredictable, skip
FURI_LOG_E(TAG, "Failed to collect nonce, skipping key");
if(dict_attack_ctx->nested_nonce.nonces) { if(dict_attack_ctx->nested_nonce.nonces) {
free(dict_attack_ctx->nested_nonce.nonces); free(dict_attack_ctx->nested_nonce.nonces);
dict_attack_ctx->nested_nonce.count = 0; dict_attack_ctx->nested_nonce.count = 0;
} }
dict_attack_ctx->nested_target_key += 4; dict_attack_ctx->nested_target_key += 2;
dict_attack_ctx->retry_counter = 0; dict_attack_ctx->attempt_count = 0;
} }
instance->state = MfClassicPollerStateNestedCollectNtEnc; instance->state = MfClassicPollerStateNestedCollectNtEnc;
return command; return command;

View File

@@ -4,6 +4,8 @@
#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/crypto1.h> #include <nfc/helpers/crypto1.h>
#include <stream/stream.h>
#include <stream/buffered_file_stream.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -24,6 +26,12 @@ typedef enum {
MfClassicCardStateLost, MfClassicCardStateLost,
} MfClassicCardState; } MfClassicCardState;
typedef enum {
MfClassicNestedStateNone,
MfClassicNestedStateFailed,
MfClassicNestedStatePassed,
} MfClassicNestedState;
typedef enum { typedef enum {
MfClassicPrngTypeUnknown, // Tag not yet tested MfClassicPrngTypeUnknown, // Tag not yet tested
MfClassicPrngTypeNoTag, // No tag detected during test MfClassicPrngTypeNoTag, // No tag detected during test
@@ -122,8 +130,10 @@ typedef struct {
MfClassicNestedNonceArray nested_nonce; MfClassicNestedNonceArray nested_nonce;
bool static_encrypted; bool static_encrypted;
bool calibrated; bool calibrated;
uint16_t d_median; uint16_t d_min;
uint8_t retry_counter; uint16_t d_max;
uint8_t attempt_count;
MfClassicNestedState nested_state;
} MfClassicPollerDictAttackContext; } MfClassicPollerDictAttackContext;
typedef struct { typedef struct {

View File

@@ -901,6 +901,7 @@ 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*
@@ -2028,6 +2029,7 @@ 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
@@ -2808,6 +2810,7 @@ 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*"
@@ -3527,7 +3530,6 @@ Function,-,ungetc,int,"int, FILE*"
Function,-,unsetenv,int,const char* Function,-,unsetenv,int,const char*
Function,-,usbd_poll,void,usbd_device* Function,-,usbd_poll,void,usbd_device*
Function,-,utoa,char*,"unsigned, char*, int" Function,-,utoa,char*,"unsigned, char*, int"
Function,+,valid_nonce,_Bool,"uint32_t, uint32_t, uint8_t"
Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*"
Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*"
Function,+,validator_is_file_free,void,ValidatorIsFile* Function,+,validator_is_file_free,void,ValidatorIsFile*
1 entry status name type params
901 Function + datetime_is_leap_year _Bool uint16_t
902 Function + datetime_timestamp_to_datetime void uint32_t, DateTime*
903 Function + datetime_validate_datetime _Bool DateTime*
904 Function + decrypt_nt_enc uint32_t uint32_t, uint32_t, MfClassicKey
905 Function + dialog_ex_alloc DialogEx*
906 Function + dialog_ex_disable_extended_events void DialogEx*
907 Function + dialog_ex_enable_extended_events void DialogEx*
2029 Function + input_get_key_name const char* InputKey
2030 Function + input_get_type_name const char* InputType
2031 Function - iprintf int const char*, ...
2032 Function + is_weak_prng_nonce _Bool uint32_t
2033 Function - isalnum int int
2034 Function - isalnum_l int int, locale_t
2035 Function - isalpha int int
2810 Function + nfc_util_even_parity8 uint8_t uint8_t
2811 Function + nfc_util_odd_parity void const uint8_t*, uint8_t*, uint8_t
2812 Function + nfc_util_odd_parity8 uint8_t uint8_t
2813 Function + nonce_matches_encrypted_parity_bits _Bool uint32_t, uint32_t, uint8_t
2814 Function + notification_internal_message void NotificationApp*, const NotificationSequence*
2815 Function + notification_internal_message_block void NotificationApp*, const NotificationSequence*
2816 Function + notification_message void NotificationApp*, const NotificationSequence*
3530 Function - unsetenv int const char*
3531 Function - usbd_poll void usbd_device*
3532 Function - utoa char* unsigned, char*, int
Function + valid_nonce _Bool uint32_t, uint32_t, uint8_t
3533 Function + validator_is_file_alloc_init ValidatorIsFile* const char*, const char*, const char*
3534 Function + validator_is_file_callback _Bool const char*, FuriString*, void*
3535 Function + validator_is_file_free void ValidatorIsFile*