mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2025-09-30 22:16:26 +02:00
Static encrypted performance fixed
This commit is contained in:
@@ -8,5 +8,5 @@ After collecting nonces using the Extract MF Keys option, press the Start button
|
||||
|
||||
## Credits
|
||||
|
||||
Developers: noproto, AG, Flipper Devices, WillyJL, CavallUwU
|
||||
Developers: noproto, AG, Flipper Devices, WillyJL, CavallUwU, Ivisayan
|
||||
Thanks: AloneLiberty, Foxushka, bettse, Equip
|
||||
|
@@ -1,5 +1,5 @@
|
||||
## 3.1
|
||||
- Key recovery is 20% faster, added write buffering of Static Encrypted Nested key candidates
|
||||
- Key recovery is 20% faster, new write buffering of Static Encrypted Nested key candidates performs recovery 70x faster
|
||||
## 3.0
|
||||
- Added Static Encrypted Nested key recovery, added NFC app support, dropped FlipperNested support
|
||||
## 2.7
|
||||
|
@@ -11,8 +11,6 @@
|
||||
// TODO: Find ~1 KB memory leak
|
||||
// TODO: Use seednt16 to reduce static encrypted key candidates: https://gist.github.com/noproto/8102f8f32546564cd674256e62ff76ea
|
||||
// https://eprint.iacr.org/2024/1275.pdf section X
|
||||
// TODO: Static Encrypted: Minimum RAM for adding to keys dict (avoid crashes)
|
||||
// TODO: Static Encrypted: Optimize KeysDict or buffer keys to write in chunks
|
||||
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
@@ -40,6 +38,7 @@
|
||||
#define KEYS_DICT_USER_PATH EXT_PATH("nfc/assets/mf_classic_dict_user.nfc")
|
||||
#define MAX_NAME_LEN 32
|
||||
#define MAX_PATH_LEN 64
|
||||
#define STATIC_ENCRYPTED_RAM_THRESHOLD 4096
|
||||
|
||||
#define LF_POLY_ODD (0x29CE5C)
|
||||
#define LF_POLY_EVEN (0x870804)
|
||||
@@ -66,6 +65,50 @@ static int16_t eta_total_time = 705;
|
||||
// MSB_LIMIT: Chunk size (out of 256) - can be 8-bit as it's a small value
|
||||
static uint8_t MSB_LIMIT = 16;
|
||||
|
||||
static inline void flush_key_buffer(ProgramState *program_state)
|
||||
{
|
||||
if (program_state->key_buffer && program_state->key_buffer_count > 0 && program_state->cuid_dict)
|
||||
{
|
||||
// Pre-allocate exact size needed: 12 hex chars + 1 newline per key
|
||||
size_t total_size = program_state->key_buffer_count * 13;
|
||||
//FURI_LOG_I(TAG, "Flushing key buffer: %d keys", program_state->key_buffer_count);
|
||||
//FURI_LOG_I(TAG, "Total size: %d bytes", total_size);
|
||||
char* batch_buffer = malloc(total_size + 1); // +1 for null terminator
|
||||
|
||||
char* ptr = batch_buffer;
|
||||
const char hex_chars[] = "0123456789ABCDEF";
|
||||
|
||||
for (size_t i = 0; i < program_state->key_buffer_count; i++)
|
||||
{
|
||||
// Convert key to hex string directly into buffer
|
||||
for (size_t j = 0; j < sizeof(MfClassicKey); j++)
|
||||
{
|
||||
uint8_t byte = program_state->key_buffer[i].data[j];
|
||||
*ptr++ = hex_chars[byte >> 4];
|
||||
*ptr++ = hex_chars[byte & 0x0F];
|
||||
}
|
||||
*ptr++ = '\n';
|
||||
}
|
||||
*ptr = '\0';
|
||||
|
||||
// Write all keys at once by directly accessing the stream
|
||||
Stream* stream = program_state->cuid_dict->stream;
|
||||
uint32_t actual_pos = stream_tell(stream);
|
||||
|
||||
if (stream_seek(stream, 0, StreamOffsetFromEnd) &&
|
||||
stream_write(stream, (uint8_t*)batch_buffer, total_size) == total_size)
|
||||
{
|
||||
// Update total key count
|
||||
program_state->cuid_dict->total_keys += program_state->key_buffer_count;
|
||||
}
|
||||
|
||||
// May not be needed
|
||||
stream_seek(stream, actual_pos, StreamOffsetFromStart);
|
||||
free(batch_buffer);
|
||||
program_state->key_buffer_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
check_state(struct Crypto1State *t, MfClassicNonce *n, ProgramState *program_state)
|
||||
{
|
||||
@@ -115,7 +158,16 @@ check_state(struct Crypto1State *t, MfClassicNonce *n, ProgramState *program_sta
|
||||
// Found key candidate
|
||||
crypto1_get_lfsr(t, &(n->key));
|
||||
program_state->num_candidates++;
|
||||
keys_dict_add_key(program_state->cuid_dict, n->key.data, sizeof(MfClassicKey));
|
||||
|
||||
// Use key buffer - buffer is guaranteed to be available for static_encrypted
|
||||
program_state->key_buffer[program_state->key_buffer_count] = n->key;
|
||||
program_state->key_buffer_count++;
|
||||
|
||||
// Flush buffer when full
|
||||
if (program_state->key_buffer_count >= program_state->key_buffer_size)
|
||||
{
|
||||
flush_key_buffer(program_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -680,53 +732,102 @@ void **allocate_blocks(const size_t *block_sizes, int num_blocks)
|
||||
return block_pointers;
|
||||
}
|
||||
|
||||
// Inline function for checking if we're at full speed
|
||||
static inline bool is_full_speed()
|
||||
{
|
||||
return MSB_LIMIT == 16;
|
||||
}
|
||||
|
||||
bool recover(MfClassicNonce *n, int ks2, unsigned int in, ProgramState *program_state)
|
||||
{
|
||||
bool found = false;
|
||||
const size_t block_sizes[] = {49216, 49216, 5120, 5120, 4096};
|
||||
const size_t reduced_block_sizes[] = {24608, 24608, 5120, 5120, 4096};
|
||||
const int num_blocks = sizeof(block_sizes) / sizeof(block_sizes[0]);
|
||||
void **block_pointers = allocate_blocks(block_sizes, num_blocks);
|
||||
if (block_pointers == NULL)
|
||||
// Reset globals each nonce
|
||||
eta_round_time = 44;
|
||||
eta_total_time = 705;
|
||||
MSB_LIMIT = 16;
|
||||
|
||||
// Use half speed (reduced block sizes) for static encrypted nonces so we can buffer keys
|
||||
bool use_half_speed = (n->attack == static_encrypted);
|
||||
if (use_half_speed)
|
||||
{
|
||||
// System has less than the guaranteed amount of RAM (140 KB) - adjust some parameters to run anyway at half speed
|
||||
if (is_full_speed())
|
||||
//eta_round_time *= 2;
|
||||
eta_total_time *= 2;
|
||||
MSB_LIMIT /= 2;
|
||||
}
|
||||
|
||||
void **block_pointers = allocate_blocks(use_half_speed ? reduced_block_sizes : block_sizes, num_blocks);
|
||||
if (block_pointers == NULL) {
|
||||
if (n->attack != static_encrypted)
|
||||
{
|
||||
// System has less than the guaranteed amount of RAM (140 KB) - adjust some parameters to run anyway at half speed
|
||||
// eta_round_time *= 2;
|
||||
eta_total_time *= 2;
|
||||
MSB_LIMIT /= 2;
|
||||
}
|
||||
block_pointers = allocate_blocks(reduced_block_sizes, num_blocks);
|
||||
if (block_pointers == NULL)
|
||||
block_pointers = allocate_blocks(reduced_block_sizes, num_blocks);
|
||||
if (block_pointers == NULL)
|
||||
{
|
||||
// System has less than 70 KB of RAM - should never happen so we don't reduce speed further
|
||||
program_state->err = InsufficientRAM;
|
||||
program_state->mfkey_state = Error;
|
||||
return false;
|
||||
}
|
||||
} else
|
||||
{
|
||||
// System has less than 70 KB of RAM - should never happen so we don't reduce speed further
|
||||
program_state->err = InsufficientRAM;
|
||||
program_state->mfkey_state = Error;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Adjust estimates for static encrypted attacks
|
||||
if (n->attack == static_encrypted)
|
||||
{
|
||||
eta_round_time *= 4;
|
||||
eta_total_time *= 4;
|
||||
if (is_full_speed())
|
||||
{
|
||||
eta_round_time *= 4;
|
||||
eta_total_time *= 4;
|
||||
}
|
||||
}
|
||||
struct Msb *odd_msbs = block_pointers[0];
|
||||
struct Msb *even_msbs = block_pointers[1];
|
||||
unsigned int *temp_states_odd = block_pointers[2];
|
||||
unsigned int *temp_states_even = block_pointers[3];
|
||||
unsigned int *states_buffer = block_pointers[4];
|
||||
|
||||
// Allocate key buffer for static encrypted nonces
|
||||
if (n->attack == static_encrypted)
|
||||
{
|
||||
size_t available_ram = memmgr_heap_get_max_free_block();
|
||||
// Each key becomes 12 hex chars + 1 newline = 13 bytes in the batch string
|
||||
// Plus original 6 bytes in buffer = 19 bytes total per key
|
||||
// Add extra safety margin for string overhead and other allocations
|
||||
const size_t safety_threshold = STATIC_ENCRYPTED_RAM_THRESHOLD;
|
||||
const size_t bytes_per_key = sizeof(MfClassicKey) + 13; // buffer + string representation
|
||||
if (available_ram > safety_threshold)
|
||||
{
|
||||
program_state->key_buffer_size = (available_ram - safety_threshold) / bytes_per_key;
|
||||
program_state->key_buffer = malloc(program_state->key_buffer_size * sizeof(MfClassicKey));
|
||||
program_state->key_buffer_count = 0;
|
||||
if (!program_state->key_buffer)
|
||||
{
|
||||
// Free the allocated blocks before returning
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
{
|
||||
free(block_pointers[i]);
|
||||
}
|
||||
free(block_pointers);
|
||||
program_state->err = InsufficientRAM;
|
||||
program_state->mfkey_state = Error;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Free the allocated blocks before returning
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
{
|
||||
free(block_pointers[i]);
|
||||
}
|
||||
free(block_pointers);
|
||||
program_state->err = InsufficientRAM;
|
||||
program_state->mfkey_state = Error;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
program_state->key_buffer = NULL;
|
||||
program_state->key_buffer_size = 0;
|
||||
program_state->key_buffer_count = 0;
|
||||
}
|
||||
|
||||
int oks = 0, eks = 0;
|
||||
int i = 0, msb = 0;
|
||||
for (i = 31; i >= 0; i -= 2)
|
||||
@@ -768,6 +869,17 @@ bool recover(MfClassicNonce *n, int ks2, unsigned int in, ProgramState *program_
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Final flush and cleanup for key buffer
|
||||
if (n->attack == static_encrypted && program_state->key_buffer)
|
||||
{
|
||||
flush_key_buffer(program_state);
|
||||
free(program_state->key_buffer);
|
||||
program_state->key_buffer = NULL;
|
||||
program_state->key_buffer_size = 0;
|
||||
program_state->key_buffer_count = 0;
|
||||
}
|
||||
|
||||
// Free the allocated blocks
|
||||
for (int i = 0; i < num_blocks; i++)
|
||||
{
|
||||
@@ -946,16 +1058,33 @@ void mfkey(ProgramState *program_state)
|
||||
|
||||
if (!recover(&next_nonce, ks_enc, nt_xor_uid, program_state))
|
||||
{
|
||||
if ((next_nonce.attack == static_encrypted) && (program_state->cuid_dict))
|
||||
// Check for non-recoverable errors and break the loop
|
||||
if (program_state->mfkey_state == Error)
|
||||
{
|
||||
keys_dict_free(program_state->cuid_dict);
|
||||
if ((next_nonce.attack == static_encrypted) && (program_state->cuid_dict))
|
||||
{
|
||||
keys_dict_free(program_state->cuid_dict);
|
||||
program_state->cuid_dict = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (program_state->close_thread_please)
|
||||
{
|
||||
if ((next_nonce.attack == static_encrypted) && (program_state->cuid_dict))
|
||||
{
|
||||
keys_dict_free(program_state->cuid_dict);
|
||||
program_state->cuid_dict = NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// No key found in recover() or static encrypted
|
||||
(program_state->num_completed)++;
|
||||
// Free CUID dictionary after each static_encrypted nonce processing
|
||||
if ((next_nonce.attack == static_encrypted) && (program_state->cuid_dict))
|
||||
{
|
||||
keys_dict_free(program_state->cuid_dict);
|
||||
program_state->cuid_dict = NULL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
(program_state->cracked)++;
|
||||
|
@@ -58,6 +58,9 @@ typedef struct
|
||||
bool close_thread_please;
|
||||
FuriThread *mfkeythread;
|
||||
KeysDict *cuid_dict;
|
||||
MfClassicKey *key_buffer;
|
||||
size_t key_buffer_size;
|
||||
size_t key_buffer_count;
|
||||
} ProgramState;
|
||||
|
||||
typedef enum
|
||||
|
Reference in New Issue
Block a user