Files
Momentum-Firmware/lib/toolbox/bit_buffer.c
Nathan N 8427ec0098 MIFARE Classic Key Recovery Improvements (#3822)
* Initial structure for nonce collection
* Nonce logging
* Dictionary attack structure
* Fix compilation
* Identified method to reduce candidate states
* Use EXT_PATH instead of ANY_PATH
* Use median calibrated distance, collect parity bits
* Modify parity collection
* Fixed parity bit collection
* Add note to fix nonce logging
* Fix nonce logging
* Clean redundant code
* Fix valid_nonce
* First attempt disambiguous nonce implementation
* FM11RF08S backdoor detection
* Initial accelerated dictionary attack for weak PRNGs
* Refactor to nested dictionary attack
* Renaming some variables
* Hard PRNG support for accelerated dictionary attack
* Update found keys, initial attempt
* Update found keys, second attempt
* Code cleanup
* Misc bugfixes
* Only use dicts in search_dicts_for_nonce_key if we have them
* Collect nonces again
* Should be detecting both backdoors now
* Relocate backdoor detection
* Hardnested support
* Fix regression for regular nested attack
* Backdoor read
* Backdoor working up to calibration
* Backdoor nested calibration
* Don't recalibrate hard PRNG tags
* Static encrypted nonce collection
* Update TODO
* NFC app UI updates, MVP
* Bump f18 API version (all functions are NFC related)
* Add new backdoor key, fix UI status update carrying over from previous read
* Clear TODO line
* Fix v1/v2 backdoor nonce collection
* Speed up backdoor detection, alert on new backdoor
* Add additional condition to backdoor check
* I'll try freeing memory, that's a good trick!
* Do not enter nested attack if card is already finished
* Do not reset the poller between collected nonces
* Clean up various issues
* Fix Hardnested sector/key type logging
* Add nested_target_key 64 to TODO
* Implement progress bar for upgraded attacks in NFC app
* Typo
* Zero nested_target_key and msb_count on exit
* Note TODO (malloc)
* Dismiss duplicate nonces
* Fix calibration (ensure values are within 3 standard deviations)
* Log static
* No nested dictionary attack re-entry
* Note minor inefficiency
* Uniformly use crypto1_ prefix for symbols in Crypto1 API
* Fix include paths
* Fix include paths cont
* Support CUID dictionary
* Fix log levels
* Avoid storage errors, clean up temporary files
* Handle invalid key candidates
* Fix memory leak in static encrypted attack
* Fix memory leak, use COUNT_OF macro
* Use single call to free FuriString
* Refactor enums to avoid redefinition
* Fix multiple crashes and state machine logic
* Fix inconsistent assignment of known key and known key type/sector
* Backdoor known key logic still needs the current key
* Larger data type for 4K support
* Fix typo
* Fix issue with resume logic
* Mark TODOs for next PR
* Remove redundant assignment
* Fix size_t format specifier
* Simplify auth_passed condition

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
Co-authored-by: gornekich <n.gorbadey@gmail.com>
2024-10-31 09:53:58 +09:00

337 lines
10 KiB
C

#include "bit_buffer.h"
#include <furi.h>
#define BITS_IN_BYTE (8)
struct BitBuffer {
uint8_t* data;
uint8_t* parity;
size_t capacity_bytes;
size_t size_bits;
};
BitBuffer* bit_buffer_alloc(size_t capacity_bytes) {
furi_check(capacity_bytes);
BitBuffer* buf = malloc(sizeof(BitBuffer));
buf->data = malloc(capacity_bytes);
size_t parity_buf_size = (capacity_bytes + BITS_IN_BYTE - 1) / BITS_IN_BYTE;
buf->parity = malloc(parity_buf_size);
buf->capacity_bytes = capacity_bytes;
buf->size_bits = 0;
return buf;
}
void bit_buffer_free(BitBuffer* buf) {
furi_check(buf);
free(buf->data);
free(buf->parity);
free(buf);
}
void bit_buffer_reset(BitBuffer* buf) {
furi_check(buf);
memset(buf->data, 0, buf->capacity_bytes);
size_t parity_buf_size = (buf->capacity_bytes + BITS_IN_BYTE - 1) / BITS_IN_BYTE;
memset(buf->parity, 0, parity_buf_size);
buf->size_bits = 0;
}
void bit_buffer_copy(BitBuffer* buf, const BitBuffer* other) {
furi_check(buf);
furi_check(other);
if(buf == other) return;
furi_check(buf->capacity_bytes * BITS_IN_BYTE >= other->size_bits);
memcpy(buf->data, other->data, bit_buffer_get_size_bytes(other));
buf->size_bits = other->size_bits;
}
void bit_buffer_copy_right(BitBuffer* buf, const BitBuffer* other, size_t start_index) {
furi_check(buf);
furi_check(other);
furi_check(bit_buffer_get_size_bytes(other) > start_index);
furi_check(buf->capacity_bytes >= bit_buffer_get_size_bytes(other) - start_index);
memcpy(buf->data, other->data + start_index, bit_buffer_get_size_bytes(other) - start_index);
buf->size_bits = other->size_bits - start_index * BITS_IN_BYTE;
}
void bit_buffer_copy_left(BitBuffer* buf, const BitBuffer* other, size_t end_index) {
furi_check(buf);
furi_check(other);
furi_check(bit_buffer_get_capacity_bytes(buf) >= end_index);
furi_check(bit_buffer_get_size_bytes(other) >= end_index);
memcpy(buf->data, other->data, end_index);
buf->size_bits = end_index * BITS_IN_BYTE;
}
void bit_buffer_copy_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes) {
furi_check(buf);
furi_check(data);
furi_check(buf->capacity_bytes >= size_bytes);
memcpy(buf->data, data, size_bytes);
buf->size_bits = size_bytes * BITS_IN_BYTE;
}
void bit_buffer_copy_bits(BitBuffer* buf, const uint8_t* data, size_t size_bits) {
furi_check(buf);
furi_check(data);
furi_check(buf->capacity_bytes * BITS_IN_BYTE >= size_bits);
size_t size_bytes = (size_bits + BITS_IN_BYTE - 1) / BITS_IN_BYTE;
memcpy(buf->data, data, size_bytes);
buf->size_bits = size_bits;
}
void bit_buffer_copy_bytes_with_parity(BitBuffer* buf, const uint8_t* data, size_t size_bits) {
furi_check(buf);
furi_check(data);
size_t bits_processed = 0;
size_t curr_byte = 0;
if(size_bits < BITS_IN_BYTE + 1) {
buf->size_bits = size_bits;
buf->data[0] = data[0];
} else {
furi_check(size_bits % (BITS_IN_BYTE + 1) == 0);
while(bits_processed < size_bits) {
buf->data[curr_byte] = data[bits_processed / BITS_IN_BYTE] >>
(bits_processed % BITS_IN_BYTE);
buf->data[curr_byte] |= data[bits_processed / BITS_IN_BYTE + 1]
<< (BITS_IN_BYTE - bits_processed % BITS_IN_BYTE);
uint8_t bit =
FURI_BIT(data[bits_processed / BITS_IN_BYTE + 1], 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);
}
bits_processed += BITS_IN_BYTE + 1;
curr_byte++;
}
buf->size_bits = curr_byte * BITS_IN_BYTE;
}
}
void bit_buffer_write_bytes(const BitBuffer* buf, void* dest, size_t size_bytes) {
furi_check(buf);
furi_check(dest);
furi_check(bit_buffer_get_size_bytes(buf) <= size_bytes);
memcpy(dest, buf->data, bit_buffer_get_size_bytes(buf));
}
void bit_buffer_write_bytes_with_parity(
const BitBuffer* buf,
void* dest,
size_t size_bytes,
size_t* bits_written) {
furi_check(buf);
furi_check(dest);
furi_check(bits_written);
size_t buf_size_bytes = bit_buffer_get_size_bytes(buf);
size_t buf_size_with_parity_bytes =
(buf_size_bytes * (BITS_IN_BYTE + 1) + BITS_IN_BYTE) / BITS_IN_BYTE;
furi_check(buf_size_with_parity_bytes <= size_bytes);
uint8_t next_par_bit = 0;
uint16_t curr_bit_pos = 0;
uint8_t* bitstream = dest;
for(size_t i = 0; i < buf_size_bytes; i++) {
next_par_bit = FURI_BIT(buf->parity[i / BITS_IN_BYTE], i % BITS_IN_BYTE);
if(curr_bit_pos % BITS_IN_BYTE == 0) {
bitstream[curr_bit_pos / BITS_IN_BYTE] = buf->data[i];
curr_bit_pos += BITS_IN_BYTE;
bitstream[curr_bit_pos / BITS_IN_BYTE] = next_par_bit;
curr_bit_pos++;
} else {
bitstream[curr_bit_pos / BITS_IN_BYTE] |= buf->data[i]
<< (curr_bit_pos % BITS_IN_BYTE);
bitstream[curr_bit_pos / BITS_IN_BYTE + 1] =
buf->data[i] >> (BITS_IN_BYTE - curr_bit_pos % BITS_IN_BYTE);
bitstream[curr_bit_pos / BITS_IN_BYTE + 1] |= next_par_bit
<< (curr_bit_pos % BITS_IN_BYTE);
curr_bit_pos += BITS_IN_BYTE + 1;
}
}
*bits_written = curr_bit_pos;
}
void bit_buffer_write_bytes_mid(
const BitBuffer* buf,
void* dest,
size_t start_index,
size_t size_bytes) {
furi_check(buf);
furi_check(dest);
furi_check(start_index + size_bytes <= bit_buffer_get_size_bytes(buf));
memcpy(dest, buf->data + start_index, size_bytes);
}
bool bit_buffer_has_partial_byte(const BitBuffer* buf) {
furi_check(buf);
return (buf->size_bits % BITS_IN_BYTE) != 0;
}
bool bit_buffer_starts_with_byte(const BitBuffer* buf, uint8_t byte) {
furi_check(buf);
return bit_buffer_get_size_bytes(buf) && (buf->data[0] == byte);
}
size_t bit_buffer_get_capacity_bytes(const BitBuffer* buf) {
furi_check(buf);
return buf->capacity_bytes;
}
size_t bit_buffer_get_size(const BitBuffer* buf) {
furi_check(buf);
return buf->size_bits;
}
size_t bit_buffer_get_size_bytes(const BitBuffer* buf) {
furi_check(buf);
return (buf->size_bits / BITS_IN_BYTE) + (buf->size_bits % BITS_IN_BYTE ? 1 : 0);
}
uint8_t bit_buffer_get_byte(const BitBuffer* buf, size_t index) {
furi_check(buf);
furi_check(buf->capacity_bytes > index);
return buf->data[index];
}
uint8_t bit_buffer_get_byte_from_bit(const BitBuffer* buf, size_t index_bits) {
furi_check(buf);
furi_check(buf->capacity_bytes * BITS_IN_BYTE > index_bits);
const size_t byte_index = index_bits / BITS_IN_BYTE;
const size_t bit_offset = index_bits % BITS_IN_BYTE;
const uint8_t lo = buf->data[byte_index] >> bit_offset;
const uint8_t hi = buf->data[byte_index + 1] << (BITS_IN_BYTE - bit_offset);
return lo | hi;
}
const uint8_t* bit_buffer_get_data(const BitBuffer* buf) {
furi_check(buf);
return buf->data;
}
const uint8_t* bit_buffer_get_parity(const BitBuffer* buf) {
furi_check(buf);
return buf->parity;
}
void bit_buffer_set_byte(BitBuffer* buf, size_t index, uint8_t byte) {
furi_check(buf);
const size_t size_bytes = bit_buffer_get_size_bytes(buf);
furi_check(size_bytes > index);
buf->data[index] = byte;
}
void bit_buffer_set_byte_with_parity(BitBuffer* buff, size_t index, uint8_t byte, bool parity) {
furi_check(buff);
furi_check(buff->size_bits / BITS_IN_BYTE > index);
buff->data[index] = byte;
if((index % BITS_IN_BYTE) == 0) {
buff->parity[index / BITS_IN_BYTE] = parity;
} else {
buff->parity[index / BITS_IN_BYTE] |= parity << (index % BITS_IN_BYTE);
}
}
void bit_buffer_set_size(BitBuffer* buf, size_t new_size) {
furi_check(buf);
furi_check(buf->capacity_bytes * BITS_IN_BYTE >= new_size);
buf->size_bits = new_size;
}
void bit_buffer_set_size_bytes(BitBuffer* buf, size_t new_size_bytes) {
furi_check(buf);
furi_check(buf->capacity_bytes >= new_size_bytes);
buf->size_bits = new_size_bytes * BITS_IN_BYTE;
}
void bit_buffer_append(BitBuffer* buf, const BitBuffer* other) {
bit_buffer_append_right(buf, other, 0);
}
void bit_buffer_append_right(BitBuffer* buf, const BitBuffer* other, size_t start_index) {
furi_check(buf);
furi_check(other);
const size_t size_bytes = bit_buffer_get_size_bytes(buf);
const size_t other_size_bytes = bit_buffer_get_size_bytes(other) - start_index;
furi_check(buf->capacity_bytes >= size_bytes + other_size_bytes);
memcpy(buf->data + size_bytes, other->data + start_index, other_size_bytes);
buf->size_bits += other->size_bits - start_index * BITS_IN_BYTE;
}
void bit_buffer_append_byte(BitBuffer* buf, uint8_t byte) {
furi_check(buf);
const size_t data_size_bytes = bit_buffer_get_size_bytes(buf);
const size_t new_data_size_bytes = data_size_bytes + 1;
furi_check(new_data_size_bytes <= buf->capacity_bytes);
buf->data[data_size_bytes] = byte;
buf->size_bits = new_data_size_bytes * BITS_IN_BYTE;
}
void bit_buffer_append_bytes(BitBuffer* buf, const uint8_t* data, size_t size_bytes) {
furi_check(buf);
furi_check(data);
const size_t buf_size_bytes = bit_buffer_get_size_bytes(buf);
furi_check(buf->capacity_bytes >= buf_size_bytes + size_bytes);
memcpy(&buf->data[buf_size_bytes], data, size_bytes);
buf->size_bits += size_bytes * BITS_IN_BYTE;
}
void bit_buffer_append_bit(BitBuffer* buf, bool bit) {
furi_check(buf);
furi_check(
bit_buffer_get_size_bytes(buf) <=
(buf->capacity_bytes - (bit_buffer_has_partial_byte(buf) ? 0 : 1)));
if(bit) {
const size_t byte_index = buf->size_bits / BITS_IN_BYTE;
const size_t bit_offset = (buf->size_bits % BITS_IN_BYTE);
buf->data[byte_index] |= 1U << bit_offset;
}
buf->size_bits++;
}