Fixed parity bit collection

This commit is contained in:
noproto 2024-08-09 20:22:19 -04:00
parent 7d2cab5d77
commit 8dd3daf625
8 changed files with 252 additions and 110 deletions

View File

@ -177,3 +177,46 @@ void crypto1_encrypt_reader_nonce(
bit_buffer_set_byte_with_parity(out, i, byte, parity_bit);
}
}
static uint8_t lfsr_rollback_bit(Crypto1* crypto1, uint32_t in, int fb) {
int out;
uint8_t ret;
uint32_t t;
crypto1->odd &= 0xffffff;
t = crypto1->odd;
crypto1->odd = crypto1->even;
crypto1->even = t;
out = crypto1->even & 1;
out ^= LF_POLY_EVEN & (crypto1->even >>= 1);
out ^= LF_POLY_ODD & crypto1->odd;
out ^= !!in;
out ^= (ret = crypto1_filter(crypto1->odd)) & (!!fb);
crypto1->even |= (nfc_util_even_parity32(out)) << 23;
return ret;
}
uint32_t lfsr_rollback_word(Crypto1* crypto1, uint32_t in, int fb) {
uint32_t ret = 0;
for(int i = 31; i >= 0; i--) {
ret |= lfsr_rollback_bit(crypto1, BEBIT(in, i), fb) << (24 ^ i);
}
return ret;
}
// Return true if the nonce is invalid else return false
bool valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t parity) {
return ((nfc_util_odd_parity8((Nt >> 24) & 0xFF) ==
(((parity >> 3) & 1) ^ nfc_util_odd_parity8((NtEnc >> 24) & 0xFF) ^
FURI_BIT(Ks1, 16))) &&
(nfc_util_odd_parity8((Nt >> 16) & 0xFF) ==
(((parity >> 2) & 1) ^ nfc_util_odd_parity8((NtEnc >> 16) & 0xFF) ^
FURI_BIT(Ks1, 8))) &&
(nfc_util_odd_parity8((Nt >> 8) & 0xFF) ==
(((parity >> 1) & 1) ^ nfc_util_odd_parity8((NtEnc >> 8) & 0xFF) ^
FURI_BIT(Ks1, 0)))) ?
true :
false;
}

View File

@ -38,6 +38,10 @@ void crypto1_encrypt_reader_nonce(
BitBuffer* out,
bool is_nested);
uint32_t lfsr_rollback_word(Crypto1* crypto1, uint32_t in, int fb);
bool valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t parity);
uint32_t prng_successor(uint32_t x, uint32_t n);
#ifdef __cplusplus

View File

@ -849,17 +849,6 @@ bool validate_prng_nonce(uint32_t nonce) {
return x == (nonce & 0xFFFF);
}
// Return 1 if the nonce is invalid else return 0
/*
uint8_t valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t *parity) {
return (
(oddparity8((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity8((NtEnc >> 24) & 0xFF) ^ BIT(Ks1, 16))) && \
(oddparity8((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity8((NtEnc >> 16) & 0xFF) ^ BIT(Ks1, 8))) && \
(oddparity8((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity8((NtEnc >> 8) & 0xFF) ^ BIT(Ks1, 0)))
) ? 1 : 0;
}
*/
// Helper function to add a nonce to the array
static bool add_nested_nonce(
MfClassicNestedNonceArray* array,
@ -918,6 +907,28 @@ NfcCommand mf_classic_poller_handler_nested_analyze_backdoor(MfClassicPoller* in
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
// TODO: Check for Fudan backdoor
// Can use on more than S variant as a free key for Nested
/*
do {
uint8_t auth_type = (key_type == MfClassicKeyTypeB) ? MF_CLASSIC_CMD_AUTH_KEY_B :
MF_CLASSIC_CMD_AUTH_KEY_A;
uint8_t auth_cmd[2] = {auth_type, block_num};
bit_buffer_copy_bytes(instance->tx_plain_buffer, auth_cmd, sizeof(auth_cmd));
if(is_nested) {
iso14443_crc_append(Iso14443CrcTypeA, instance->tx_plain_buffer);
crypto1_encrypt(
instance->crypto, NULL, instance->tx_plain_buffer, instance->tx_encrypted_buffer);
error = iso14443_3a_poller_txrx_custom_parity(
instance->iso14443_3a_poller,
instance->tx_encrypted_buffer,
instance->rx_plain_buffer, // NT gets decrypted by mf_classic_async_auth
MF_CLASSIC_FWT_FC);
if(error != Iso14443_3aErrorNone) {
ret = mf_classic_process_error(error);
break;
}
*/
dict_attack_ctx->backdoor = MfClassicBackdoorNone;
instance->state = MfClassicPollerStateNestedController;
return command;
@ -966,6 +977,7 @@ uint16_t get_median(uint16_t arr[], int n) {
NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance) {
// TODO: Calibrate backdoored tags too
// TODO: Check if we have already identified the tag as static encrypted
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
uint8_t nt_enc_calibration_cnt = 11;
@ -984,7 +996,7 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance)
uint32_t nt_prev = 0;
uint32_t nt_enc_prev = 0;
uint32_t same_nt_enc_cnt = 0;
bool static_encrypted = false;
uint8_t nt_enc_collected = 0;
// Step 1: Perform full authentication once
error = mf_classic_poller_auth(
@ -1012,28 +1024,33 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance)
block,
&dict_attack_ctx->current_key,
dict_attack_ctx->current_key_type,
&auth_ctx);
&auth_ctx,
false);
if(error != MfClassicErrorNone) {
FURI_LOG_E(TAG, "Failed to perform nested authentication %u", collection_cycle);
continue;
}
uint32_t nt_enc = bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));
if(nt_enc == nt_enc_prev) {
nt_enc_temp_arr[collection_cycle] =
bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));
nt_enc_collected++;
}
for(int i = 0; i < nt_enc_collected; i++) {
if(nt_enc_temp_arr[i] == nt_enc_prev) {
same_nt_enc_cnt++;
if(same_nt_enc_cnt > 3) {
static_encrypted = true;
dict_attack_ctx->static_encrypted = true;
break;
}
} else {
same_nt_enc_cnt = 0;
nt_enc_prev = nt_enc;
nt_enc_prev = nt_enc_temp_arr[i];
}
nt_enc_temp_arr[collection_cycle] = nt_enc;
}
if(static_encrypted) {
if(dict_attack_ctx->static_encrypted) {
FURI_LOG_E(TAG, "Static encrypted nonce detected");
if(dict_attack_ctx->backdoor == MfClassicBackdoorFM11RF08S) {
// TODO: Backdoor static nested attack calibration
@ -1101,23 +1118,10 @@ NfcCommand mf_classic_poller_handler_nested_calibrate(MfClassicPoller* instance)
}
NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* instance) {
// TODO: Use d_median value and parity to get candidate states. d_median is the center, keep adding +/- 1 until a parity match is found.
/*
uint32_t ncount = 0;
uint32_t nttest = prng_successor(nt1, dmin - 1);
for(j = dmin; j < dmax + 1; j++) {
nttest = prng_successor(nttest, 1);
ks1 = nt2 ^ nttest;
if(valid_nonce(nttest, nt2, ks1, par_array)) {
if(ncount > 0) { // we are only interested in disambiguous nonces, try again
FURI_LOG_D(TAG, "Nonce#%lu: dismissed (ambiguous), ntdist=%lu", i + 1, j);
(..)
*/
NfcCommand command = NfcCommandContinue;
// TODO: Handle when nonce is not collected (retry counter? Do not increment nested_target_key)
NfcCommand command = NfcCommandReset;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
MfClassicNestedNonceArray result = {NULL, 0};
bool collection_success = false;
do {
if(dict_attack_ctx->prng_type == MfClassicPrngTypeHard) {
@ -1127,6 +1131,30 @@ NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* inst
break;
}
if(dict_attack_ctx->static_encrypted) {
FURI_LOG_E(TAG, "Static encrypted nonce detected");
if(dict_attack_ctx->backdoor == MfClassicBackdoorFM11RF08S) {
// TODO: Backdoor static nested attack with calibrated distance
break;
} else {
// TODO: If not present, just log nonces with parity bits, e.g.
/*
bool success = add_nested_nonce(
&result,
cuid,
dict_attack_ctx->reuse_key_sector,
nt_prev,
nt_enc_prev,
parity,
UINT16_MAX);
if(!success) {
FURI_LOG_E(TAG, "Failed to add nested nonce to array. OOM?");
}
*/
break;
}
}
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);
@ -1134,14 +1162,15 @@ NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* inst
MfClassicAuthContext auth_ctx = {};
MfClassicError error;
uint8_t nt_enc_per_collection = 2;
uint32_t nt_enc_arr[nt_enc_per_collection];
uint8_t par_arr[nt_enc_per_collection];
uint8_t nonce_pair_index = dict_attack_ctx->nested_target_key % 2;
uint8_t nt_enc_per_collection = 2 + nonce_pair_index;
MfClassicKeyType target_key_type = ((dict_attack_ctx->nested_target_key & 0x03) < 2) ?
MfClassicKeyTypeA :
MfClassicKeyTypeB;
uint8_t target_block = (4 * (dict_attack_ctx->nested_target_key / 4)) + 3;
uint32_t nt_enc_temp_arr[nt_enc_per_collection];
uint8_t nt_enc_collected = 0;
uint8_t parity = 0;
uint32_t nt_prev = 0;
uint32_t nt_enc_prev = 0;
uint32_t same_nt_enc_cnt = 0;
bool static_encrypted = false;
// Step 1: Perform full authentication once
error = mf_classic_poller_auth(
@ -1158,84 +1187,132 @@ NfcCommand mf_classic_poller_handler_nested_collect_nt_enc(MfClassicPoller* inst
FURI_LOG_E(TAG, "Full authentication successful");
nt_prev = bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));
// Step 2: Perform nested authentication a variable number of times to get nt_enc at a different PRNG offset
// eg. Collect most commonly observed nonce from 3 auths to known sector and 4th to target, then separately the
// most commonly observed nonce from 4 auths to known sector and 5th to target. This gets us a nonce pair,
// at a known distance (confirmed by parity bits) telling us the nt_enc plain.
// We also have backup values for the distance in case of rare issues.
//printf("ks: %08x\n", nth_successor ^ nt_enc);
//MfClassicKeyType target_key_type =
// (dict_attack_ctx->nested_target_key % 2 == 0) ? MfClassicKeyTypeA : MfClassicKeyTypeB;
//uint8_t target_block = (4 * (dict_attack_ctx->nested_target_key / 2)) + 3;
for(uint32_t round_no = 0; round_no < nt_enc_per_collection; round_no++) {
for(uint8_t collection_cycle = 0; collection_cycle < (nt_enc_per_collection - 1);
collection_cycle++) {
// This loop must match the calibrated loop
error = mf_classic_poller_auth_nested(
instance,
block,
&dict_attack_ctx->current_key,
dict_attack_ctx->current_key_type,
&auth_ctx);
&auth_ctx,
false);
if(error != MfClassicErrorNone) {
FURI_LOG_E(TAG, "Failed to perform nested authentication %lu", round_no);
continue;
FURI_LOG_E(TAG, "Failed to perform nested authentication %u", collection_cycle);
break;
}
uint32_t nt_enc = bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));
if(nt_enc == nt_enc_prev) {
same_nt_enc_cnt++;
if(same_nt_enc_cnt > 3) {
static_encrypted = true;
nt_enc_temp_arr[collection_cycle] =
bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));
nt_enc_collected++;
}
error = mf_classic_poller_auth_nested(
instance,
target_block,
&dict_attack_ctx->current_key,
target_key_type,
&auth_ctx,
true);
if(nt_enc_collected != (nt_enc_per_collection - 1)) {
FURI_LOG_E(TAG, "Failed to collect sufficient nt_enc values");
break;
}
uint32_t nt_enc = bit_lib_bytes_to_num_be(auth_ctx.nt.data, sizeof(MfClassicNt));
// Collect parity bits
const uint8_t* parity_data = bit_buffer_get_parity(instance->rx_plain_buffer);
for(int i = 0; i < 4; i++) {
parity = (parity << 1) | (((parity_data[0] >> i) & 0x01) ^ 0x01);
}
// Decrypt the previous nonce
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);
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
bool found_matching_nt = false;
uint32_t found_nt = 0;
uint16_t current_dist = 0;
const uint16_t max_dist = 16; // 32 would work too
// TODO: Better handling of overflows (allow wrap instead of stopping?)
while(!found_matching_nt && current_dist < max_dist &&
((dict_attack_ctx->d_median - current_dist) != 0) &&
((dict_attack_ctx->d_median + current_dist) != UINT16_MAX)) {
uint32_t nth_successor_positive =
prng_successor(decrypted_nt_prev, dict_attack_ctx->d_median + current_dist);
if(valid_nonce(
nth_successor_positive, nt_enc, 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, nt_enc, nth_successor_negative ^ nt_enc, parity)) {
found_matching_nt = true;
found_nt = nth_successor_negative;
break;
}
} else {
same_nt_enc_cnt = 0;
nt_enc_prev = nt_enc;
}
// Collect parity bits
const uint8_t* parity_data = bit_buffer_get_parity(instance->rx_encrypted_buffer);
for(int i = 0; i < 4; i++) {
parity |= ((parity_data[i] & 0x01) << (3 - i));
}
par_arr[round_no] = parity;
nt_enc_arr[round_no] = nt_enc;
current_dist++;
}
if(static_encrypted) {
FURI_LOG_E(TAG, "Static encrypted nonce detected");
if(dict_attack_ctx->backdoor == MfClassicBackdoorFM11RF08S) {
// TODO: Backdoor static nested attack
break;
} else {
// TODO: If not present, just log nonces with parity bits
bool success = add_nested_nonce(
&result,
cuid,
dict_attack_ctx->reuse_key_sector,
nt_prev,
nt_enc_prev,
parity,
UINT16_MAX);
if(!success) {
FURI_LOG_E(TAG, "Failed to add nested nonce to array. OOM?");
}
break;
// Add the nonce to the array
if(found_matching_nt) {
collection_success = add_nested_nonce(
&dict_attack_ctx->nested_nonce,
cuid,
dict_attack_ctx->nested_target_key,
found_nt,
nt_enc,
parity,
0);
if(!collection_success) {
FURI_LOG_E(TAG, "Failed to add nested nonce to array. OOM?");
}
} else {
FURI_LOG_E(TAG, "Failed to find matching nonce");
}
for(uint8_t i = 0; i < nt_enc_per_collection; i++) {
FURI_LOG_E(TAG, "nt_enc: %08lx", nt_enc_arr[i]);
FURI_LOG_E(
TAG,
"parity: %u%u%u%u",
((par_arr[i] >> 3) & 1),
((par_arr[i] >> 2) & 1),
((par_arr[i] >> 1) & 1),
(par_arr[i] & 1));
}
FURI_LOG_E(
TAG,
"Target: %u (nonce pair %u, key type %s, block %u)",
dict_attack_ctx->nested_target_key,
nonce_pair_index,
(target_key_type == MfClassicKeyTypeA) ? "A" : "B",
target_block);
FURI_LOG_E(TAG, "cuid: %08lx", cuid);
FURI_LOG_E(TAG, "nt_enc: %08lx", nt_enc);
FURI_LOG_E(
TAG,
"parity: %u%u%u%u",
((parity >> 3) & 1),
((parity >> 2) & 1),
((parity >> 1) & 1),
(parity & 1));
FURI_LOG_E(TAG, "nt_enc prev: %08lx", nt_prev);
FURI_LOG_E(TAG, "nt_enc prev decrypted: %08lx", decrypted_nt_prev);
} 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;
mf_classic_poller_halt(instance);
@ -1327,10 +1404,10 @@ NfcCommand mf_classic_poller_handler_nested_log(MfClassicPoller* instance) {
NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance) {
// Iterate through keys
// TODO: Fix infinite loop somewhere
NfcCommand command = NfcCommandContinue;
MfClassicPollerDictAttackContext* dict_attack_ctx = &instance->mode_ctx.dict_attack_ctx;
if(dict_attack_ctx->nested_nonce.count > 0) {
if((dict_attack_ctx->nested_nonce.count > 0) &&
(dict_attack_ctx->nested_target_key % 2 == 0)) {
if(dict_attack_ctx->prng_type == MfClassicPrngTypeWeak) {
instance->state = MfClassicPollerStateNestedDictAttack;
return command;
@ -1349,9 +1426,17 @@ NfcCommand mf_classic_poller_handler_nested_controller(MfClassicPoller* instance
instance->state = MfClassicPollerStateNestedCalibrate;
return command;
}
// Target all sectors, key A and B
for(uint8_t key_idx = 0; key_idx <= (instance->sectors_total * 2); key_idx++) {
dict_attack_ctx->nested_target_key = key_idx;
// Target all sectors, key A and B, first and second nonce
if(dict_attack_ctx->nested_target_key < (instance->sectors_total * 4)) {
if(dict_attack_ctx->retry_counter > 3) {
// Bad sector, skip
if(dict_attack_ctx->nested_nonce.nonces) {
free(dict_attack_ctx->nested_nonce.nonces);
dict_attack_ctx->nested_nonce.count = 0;
}
dict_attack_ctx->nested_target_key += 4;
dict_attack_ctx->retry_counter = 0;
}
instance->state = MfClassicPollerStateNestedCollectNtEnc;
return command;
}

View File

@ -231,6 +231,7 @@ MfClassicError mf_classic_poller_auth(
* @param[in] key key to be used for authentication.
* @param[in] key_type key type to be used for authentication.
* @param[out] data pointer to MfClassicAuthContext structure to be filled with authentication data.
* @param[in] early_ret return immediately after receiving encrypted nonce.
* @return MfClassicErrorNone on success, an error code on failure.
*/
MfClassicError mf_classic_poller_auth_nested(
@ -238,7 +239,8 @@ MfClassicError mf_classic_poller_auth_nested(
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data);
MfClassicAuthContext* data,
bool early_ret);
/**
* @brief Halt the tag.

View File

@ -111,7 +111,8 @@ static MfClassicError mf_classic_poller_auth_common(
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data,
bool is_nested) {
bool is_nested,
bool early_ret) {
MfClassicError ret = MfClassicErrorNone;
Iso14443_3aError error = Iso14443_3aErrorNone;
@ -130,6 +131,7 @@ static MfClassicError mf_classic_poller_auth_common(
if(data) {
data->nt = nt;
}
if(early_ret) break;
uint32_t cuid = iso14443_3a_get_cuid(instance->data->iso14443_3a_data);
uint64_t key_num = bit_lib_bytes_to_num_be(key->data, sizeof(MfClassicKey));
@ -185,7 +187,7 @@ MfClassicError mf_classic_poller_auth(
MfClassicAuthContext* data) {
furi_check(instance);
furi_check(key);
return mf_classic_poller_auth_common(instance, block_num, key, key_type, data, false);
return mf_classic_poller_auth_common(instance, block_num, key, key_type, data, false, false);
}
MfClassicError mf_classic_poller_auth_nested(
@ -193,10 +195,12 @@ MfClassicError mf_classic_poller_auth_nested(
uint8_t block_num,
MfClassicKey* key,
MfClassicKeyType key_type,
MfClassicAuthContext* data) {
MfClassicAuthContext* data,
bool early_ret) {
furi_check(instance);
furi_check(key);
return mf_classic_poller_auth_common(instance, block_num, key, key_type, data, true);
return mf_classic_poller_auth_common(
instance, block_num, key, key_type, data, true, early_ret);
}
MfClassicError mf_classic_poller_halt(MfClassicPoller* instance) {

View File

@ -120,8 +120,10 @@ typedef struct {
uint8_t hard_nt_count;
uint8_t nested_target_key;
MfClassicNestedNonceArray nested_nonce;
bool static_encrypted;
bool calibrated;
uint16_t d_median;
uint8_t retry_counter;
} MfClassicPollerDictAttackContext;
typedef struct {

View File

@ -113,7 +113,7 @@ void bit_buffer_copy_bytes_with_parity(BitBuffer* buf, const uint8_t* data, size
uint8_t bit =
FURI_BIT(data[bits_processed / BITS_IN_BYTE + 1], bits_processed % BITS_IN_BYTE);
if(bits_processed % BITS_IN_BYTE) {
if((bits_processed % BITS_IN_BYTE) == 0) {
buf->parity[curr_byte / BITS_IN_BYTE] = bit;
} else {
buf->parity[curr_byte / BITS_IN_BYTE] |= bit << (bits_processed % BITS_IN_BYTE);

View File

@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,71.0,,
Version,+,72.0,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
@ -2211,6 +2211,7 @@ Function,+,lfrfid_worker_start_thread,void,LFRFIDWorker*
Function,+,lfrfid_worker_stop,void,LFRFIDWorker*
Function,+,lfrfid_worker_stop_thread,void,LFRFIDWorker*
Function,+,lfrfid_worker_write_start,void,"LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*"
Function,+,lfsr_rollback_word,uint32_t,"Crypto1*, uint32_t, int"
Function,-,lgamma,double,double
Function,-,lgamma_r,double,"double, int*"
Function,-,lgammaf,float,float
@ -2506,7 +2507,7 @@ Function,+,mf_classic_is_sector_trailer,_Bool,uint8_t
Function,+,mf_classic_is_value_block,_Bool,"MfClassicSectorTrailer*, uint8_t"
Function,+,mf_classic_load,_Bool,"MfClassicData*, FlipperFormat*, uint32_t"
Function,+,mf_classic_poller_auth,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*"
Function,+,mf_classic_poller_auth_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*"
Function,+,mf_classic_poller_auth_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*, _Bool"
Function,+,mf_classic_poller_get_nt,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*"
Function,+,mf_classic_poller_get_nt_nested,MfClassicError,"MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*"
Function,+,mf_classic_poller_halt,MfClassicError,MfClassicPoller*
@ -3525,6 +3526,7 @@ Function,-,ungetc,int,"int, FILE*"
Function,-,unsetenv,int,const char*
Function,-,usbd_poll,void,usbd_device*
Function,-,utoa,char*,"unsigned, char*, int"
Function,+,valid_nonce,_Bool,"uint32_t, uint32_t, uint32_t, uint8_t"
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_free,void,ValidatorIsFile*

1 entry status name type params
2 Version + 71.0 72.0
3 Header + applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h
4 Header + applications/services/bt/bt_service/bt.h
5 Header + applications/services/bt/bt_service/bt_keys_storage.h
2211 Function + lfrfid_worker_stop void LFRFIDWorker*
2212 Function + lfrfid_worker_stop_thread void LFRFIDWorker*
2213 Function + lfrfid_worker_write_start void LFRFIDWorker*, LFRFIDProtocol, LFRFIDWorkerWriteCallback, void*
2214 Function + lfsr_rollback_word uint32_t Crypto1*, uint32_t, int
2215 Function - lgamma double double
2216 Function - lgamma_r double double, int*
2217 Function - lgammaf float float
2507 Function + mf_classic_is_value_block _Bool MfClassicSectorTrailer*, uint8_t
2508 Function + mf_classic_load _Bool MfClassicData*, FlipperFormat*, uint32_t
2509 Function + mf_classic_poller_auth MfClassicError MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*
2510 Function + mf_classic_poller_auth_nested MfClassicError MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext* MfClassicPoller*, uint8_t, MfClassicKey*, MfClassicKeyType, MfClassicAuthContext*, _Bool
2511 Function + mf_classic_poller_get_nt MfClassicError MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*
2512 Function + mf_classic_poller_get_nt_nested MfClassicError MfClassicPoller*, uint8_t, MfClassicKeyType, MfClassicNt*
2513 Function + mf_classic_poller_halt MfClassicError MfClassicPoller*
3526 Function - unsetenv int const char*
3527 Function - usbd_poll void usbd_device*
3528 Function - utoa char* unsigned, char*, int
3529 Function + valid_nonce _Bool uint32_t, uint32_t, uint32_t, uint8_t
3530 Function + validator_is_file_alloc_init ValidatorIsFile* const char*, const char*, const char*
3531 Function + validator_is_file_callback _Bool const char*, FuriString*, void*
3532 Function + validator_is_file_free void ValidatorIsFile*