Key recovery via backdoor for static encrypted nonce cards (#263)

* Implement MF1_ENC_NESTED_ACQUIRE in firmware

* Implement MF1_ENC_NESTED_ACQUIRE in software + fix code formatter

* Remove xz and pthreads from sources, use CMake FetchContent

* Update changelog

* lzma.h

* Update CMakeLists.txt

* Update CMakeLists.txt

* Probably fix workflow taking wrong commit for building

* Fix CMake building tools into bin/Debug on Windows

* Added cmd for fetching all slots nicks (without 16 commands)

* Fix type and use temp directory instead cwd (https://github.com/RfidResearchGroup/ChameleonUltra/pull/261)

* Fix endian for mfu_read_emu_counter_data and mfu_write_emu_counter_data

* Fix  --key interpreted as list
This commit is contained in:
Foxushka
2025-08-01 22:12:07 +02:00
committed by GitHub
parent 7065011f22
commit cf00761902
295 changed files with 9127 additions and 72962 deletions
+2 -2
View File
@@ -10,12 +10,12 @@ jobs:
contents: read
uses: ./.github/workflows/build_firmware.yml
with:
checkout-sha: "${{ github.event.pull_request.merge_commit_sha }}"
checkout-sha: "${{ github.event.pull_request.head.sha }}"
client_pipeline:
name: Build Firmware
uses: ./.github/workflows/build_client.yml
with:
checkout-sha: "${{ github.event.pull_request.merge_commit_sha }}"
checkout-sha: "${{ github.event.pull_request.head.sha }}"
comment:
runs-on: ubuntu-latest
name: Comment on PR
+4
View File
@@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file.
This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log...
## [unreleased][unreleased]
- Added cmd for fetching all slots nicks (@Foxushka)
- Added `hf mf senested` for recovering keys from static encrypted cards via backdoor (https://eprint.iacr.org/2024/1275) (@Foxushka)
- Added cmd for faster bulk key checking on one block (~33 keys per second) (@Foxushka)
- Added cmd to acquire nonces for static encrypted cards via backdoor (@Foxushka)
- Added `firmware/docker-compose.yml` to build firmware in local docker (@taichunmin)
- Added cmd to acquire nonces for hardnested(Protocol doc need update) (@xianglin1998)
- Added command to check keys of multiple sectors at once (@taichunmin)
+126 -35
View File
@@ -330,6 +330,33 @@ static data_frame_tx_t *cmd_processor_mf1_nested_acquire(uint16_t cmd, uint16_t
return data_frame_make(cmd, STATUS_HF_TAG_OK, sizeof(ncs), (uint8_t *)(&ncs));
}
static data_frame_tx_t *cmd_processor_mf1_enc_nested_acquire(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
typedef struct {
uint8_t key[6];
uint8_t sector_count;
uint8_t starting_sector;
} PACKED payload_t;
if (length != sizeof(payload_t)) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
}
payload_t *payload = (payload_t *)data;
uint64_t ui64Key = bytes_to_num(payload->key, 6);
uint8_t sector_data[40][sizeof(mf1_static_nonce_sector_t)];
uint8_t sectors_acquired = 0;
uint32_t cuid = 0;
status = mf1_static_encrypted_nonces_acquire(ui64Key, payload->sector_count, payload->starting_sector, sector_data, &sectors_acquired, &cuid);
uint8_t response_data[sizeof(uint32_t) + sectors_acquired * sizeof(mf1_static_nonce_sector_t)];
num_to_bytes(cuid, 4, response_data);
memcpy(response_data + sizeof(uint32_t), sector_data, sectors_acquired * sizeof(mf1_static_nonce_sector_t));
return data_frame_make(cmd, status, sectors_acquired * sizeof(mf1_static_nonce_sector_t) + sizeof(uint32_t), response_data);
}
static data_frame_tx_t *cmd_processor_mf1_auth_one_key_block(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
typedef struct {
uint8_t type;
@@ -363,8 +390,30 @@ static data_frame_tx_t *cmd_processor_mf1_check_keys_of_sectors(uint16_t cmd, ui
return data_frame_make(cmd, status, sizeof(out), (uint8_t *)&out);
}
static data_frame_tx_t *cmd_processor_mf1_check_keys_on_block(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
if (length < 3 || (length - 3) % 6 != 0) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
}
mf1_toolbox_check_keys_on_block_in_t in = {
.block = data[0],
.key_type = data[1],
.keys_len = data[2],
.keys = (mf1_key_t *) &data[3]
};
if ((length - 3) / 6 != in.keys_len) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
}
mf1_toolbox_check_keys_on_block_out_t out;
status = mf1_toolbox_check_keys_on_block(&in, &out);
return data_frame_make(cmd, status, sizeof(out), (uint8_t *)&out);
}
static data_frame_tx_t *cmd_processor_mf1_hardnested_nonces_acquire(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
typedef struct {
typedef struct {
uint8_t slow;
uint8_t type_known;
uint8_t block_known;
@@ -376,7 +425,7 @@ static data_frame_tx_t *cmd_processor_mf1_hardnested_nonces_acquire(uint16_t cmd
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
}
payload_t *payload = (payload_t *)data;
// It is enough to collect 110 nonces at a time. The total transmitted data payload is 495 + 1 bytes
// Then, the total length can be controlled within 512, so that when encountering a BLE host that supports large packets, one communication can be completed.
// There is no need to send or receive packets in separate packets, which improves communication speed.
@@ -385,16 +434,16 @@ static data_frame_tx_t *cmd_processor_mf1_hardnested_nonces_acquire(uint16_t cmd
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
}
status = mf1_hardnested_nonces_acquire(
payload->slow,
payload->block_known,
payload->type_known,
bytes_to_num(payload->key_known, 6),
payload->block_target,
payload->type_target,
nonces + 1,
sizeof(nonces) - 1, // The upper limit of the buffer size. Here we take out the first byte to mark the number of collections.
&nonces[0] // The number of random numbers collected above
);
payload->slow,
payload->block_known,
payload->type_known,
bytes_to_num(payload->key_known, 6),
payload->block_target,
payload->type_target,
nonces + 1,
sizeof(nonces) - 1, // The upper limit of the buffer size. Here we take out the first byte to mark the number of collections.
&nonces[0] // The number of random numbers collected above
);
if (status != STATUS_HF_TAG_OK) {
return data_frame_make(cmd, status, 0, NULL);
}
@@ -733,27 +782,27 @@ static nfc_tag_14a_coll_res_reference_t *get_coll_res_data(bool write) {
tag_emulation_get_specific_types_by_slot(tag_emulation_get_slot(), &tag_types);
switch (tag_types.tag_hf) {
case TAG_TYPE_MIFARE_1024:
case TAG_TYPE_MIFARE_2048:
case TAG_TYPE_MIFARE_4096:
case TAG_TYPE_MIFARE_Mini:
info = write ? get_mifare_coll_res() : get_saved_mifare_coll_res();
break;
case TAG_TYPE_MF0ICU1:
case TAG_TYPE_MF0ICU2:
case TAG_TYPE_MF0UL11:
case TAG_TYPE_MF0UL21:
case TAG_TYPE_NTAG_210:
case TAG_TYPE_NTAG_212:
case TAG_TYPE_NTAG_213:
case TAG_TYPE_NTAG_215:
case TAG_TYPE_NTAG_216:
info = nfc_tag_mf0_ntag_get_coll_res();
break;
default:
// no collision resolution data for slot
info = NULL;
break;
case TAG_TYPE_MIFARE_1024:
case TAG_TYPE_MIFARE_2048:
case TAG_TYPE_MIFARE_4096:
case TAG_TYPE_MIFARE_Mini:
info = write ? get_mifare_coll_res() : get_saved_mifare_coll_res();
break;
case TAG_TYPE_MF0ICU1:
case TAG_TYPE_MF0ICU2:
case TAG_TYPE_MF0UL11:
case TAG_TYPE_MF0UL21:
case TAG_TYPE_NTAG_210:
case TAG_TYPE_NTAG_212:
case TAG_TYPE_NTAG_213:
case TAG_TYPE_NTAG_215:
case TAG_TYPE_NTAG_216:
info = nfc_tag_mf0_ntag_get_coll_res();
break;
default:
// no collision resolution data for slot
info = NULL;
break;
}
return info;
@@ -881,8 +930,8 @@ static data_frame_tx_t *cmd_processor_mf0_ntag_write_emu_page_data(uint16_t cmd,
if (pages_count == 0) return data_frame_make(cmd, STATUS_SUCCESS, 0, NULL);
else if (
(page_index >= ((int)nr_pages))
|| (pages_count > (((int)nr_pages) - page_index))
(page_index >= ((int)nr_pages))
|| (pages_count > (((int)nr_pages) - page_index))
|| (((int)length - 2) < byte_length)
) {
byte = nr_pages;
@@ -1090,6 +1139,45 @@ static data_frame_tx_t *cmd_processor_get_slot_tag_nick(uint16_t cmd, uint16_t s
return data_frame_make(cmd, STATUS_SUCCESS, buffer[0], &buffer[1]);
}
static data_frame_tx_t *cmd_processor_get_all_slot_nicks(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
uint8_t response_buffer[TAG_MAX_SLOT_NUM * 2 * 37]; // Max possible size: 8 slots * 2 sense types * (1 byte length + 36 bytes nick)
uint16_t response_length = 0;
for (uint8_t slot = 0; slot < TAG_MAX_SLOT_NUM; slot++) {
uint8_t hf_buffer[36];
fds_slot_record_map_t hf_map_info;
get_fds_map_by_slot_sense_type_for_nick(slot, TAG_SENSE_HF, &hf_map_info);
uint16_t hf_buffer_length = sizeof(hf_buffer);
bool hf_ret = fds_read_sync(hf_map_info.id, hf_map_info.key, &hf_buffer_length, hf_buffer);
if (hf_ret && hf_buffer_length > 0) {
response_buffer[response_length++] = hf_buffer[0];
for (uint8_t i = 1; i <= hf_buffer[0] && i < hf_buffer_length; i++) {
response_buffer[response_length++] = hf_buffer[i];
}
} else {
response_buffer[response_length++] = 0;
}
uint8_t lf_buffer[36];
fds_slot_record_map_t lf_map_info;
get_fds_map_by_slot_sense_type_for_nick(slot, TAG_SENSE_LF, &lf_map_info);
uint16_t lf_buffer_length = sizeof(lf_buffer);
bool lf_ret = fds_read_sync(lf_map_info.id, lf_map_info.key, &lf_buffer_length, lf_buffer);
if (lf_ret && lf_buffer_length > 0) {
response_buffer[response_length++] = lf_buffer[0];
for (uint8_t i = 1; i <= lf_buffer[0] && i < lf_buffer_length; i++) {
response_buffer[response_length++] = lf_buffer[i];
}
} else {
response_buffer[response_length++] = 0;
}
}
return data_frame_make(cmd, STATUS_SUCCESS, response_length, response_buffer);
}
static data_frame_tx_t *cmd_processor_delete_slot_tag_nick(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
if (length != 2) {
return data_frame_make(cmd, STATUS_PAR_ERR, 0, NULL);
@@ -1327,6 +1415,7 @@ static cmd_data_map_t m_data_cmd_map[] = {
{ DATA_CMD_GET_DEVICE_CAPABILITIES, NULL, cmd_processor_get_device_capabilities, NULL },
{ DATA_CMD_GET_BLE_PAIRING_ENABLE, NULL, cmd_processor_get_ble_pairing_enable, NULL },
{ DATA_CMD_SET_BLE_PAIRING_ENABLE, NULL, cmd_processor_set_ble_pairing_enable, NULL },
{ DATA_CMD_GET_ALL_SLOT_NICKS, NULL, cmd_processor_get_all_slot_nicks, NULL },
#if defined(PROJECT_CHAMELEON_ULTRA)
@@ -1337,6 +1426,7 @@ static cmd_data_map_t m_data_cmd_map[] = {
{ DATA_CMD_MF1_DARKSIDE_ACQUIRE, before_hf_reader_run, cmd_processor_mf1_darkside_acquire, after_hf_reader_run },
{ DATA_CMD_MF1_DETECT_NT_DIST, before_hf_reader_run, cmd_processor_mf1_detect_nt_dist, after_hf_reader_run },
{ DATA_CMD_MF1_NESTED_ACQUIRE, before_hf_reader_run, cmd_processor_mf1_nested_acquire, after_hf_reader_run },
{ DATA_CMD_MF1_ENC_NESTED_ACQUIRE, before_hf_reader_run, cmd_processor_mf1_enc_nested_acquire, after_hf_reader_run },
{ DATA_CMD_MF1_AUTH_ONE_KEY_BLOCK, before_hf_reader_run, cmd_processor_mf1_auth_one_key_block, after_hf_reader_run },
{ DATA_CMD_MF1_READ_ONE_BLOCK, before_hf_reader_run, cmd_processor_mf1_read_one_block, after_hf_reader_run },
@@ -1345,6 +1435,7 @@ static cmd_data_map_t m_data_cmd_map[] = {
{ DATA_CMD_MF1_MANIPULATE_VALUE_BLOCK, before_hf_reader_run, cmd_processor_mf1_manipulate_value_block, after_hf_reader_run },
{ DATA_CMD_MF1_CHECK_KEYS_OF_SECTORS, before_hf_reader_run, cmd_processor_mf1_check_keys_of_sectors, after_hf_reader_run },
{ DATA_CMD_MF1_HARDNESTED_ACQUIRE, before_hf_reader_run, cmd_processor_mf1_hardnested_nonces_acquire, after_hf_reader_run },
{ DATA_CMD_MF1_CHECK_KEYS_ON_BLOCK, before_hf_reader_run, cmd_processor_mf1_check_keys_on_block, after_hf_reader_run },
{ DATA_CMD_EM410X_SCAN, before_reader_run, cmd_processor_em410x_scan, NULL },
{ DATA_CMD_EM410X_WRITE_TO_T55XX, before_reader_run, cmd_processor_em410x_write_to_t55XX, NULL },
+3
View File
@@ -45,6 +45,7 @@
#define DATA_CMD_GET_DEVICE_CAPABILITIES (1035)
#define DATA_CMD_GET_BLE_PAIRING_ENABLE (1036)
#define DATA_CMD_SET_BLE_PAIRING_ENABLE (1037)
#define DATA_CMD_GET_ALL_SLOT_NICKS (1038)
//
// ******************************************************************
@@ -69,6 +70,8 @@
#define DATA_CMD_MF1_MANIPULATE_VALUE_BLOCK (2011)
#define DATA_CMD_MF1_CHECK_KEYS_OF_SECTORS (2012)
#define DATA_CMD_MF1_HARDNESTED_ACQUIRE (2013)
#define DATA_CMD_MF1_ENC_NESTED_ACQUIRE (2014)
#define DATA_CMD_MF1_CHECK_KEYS_ON_BLOCK (2015)
//
// ******************************************************************
+19 -19
View File
@@ -274,26 +274,26 @@ static const uint8_t TableC0[32] = {
static const uint8_t TableC7[32] = {
/* fc with Input {4,3,2,1,0} = (0,0,0,0,0) to (1,1,1,1,1) */
FC(0, 0, 0, 0, 0) << 7, FC(0, 0, 0, 0, 1) << 7, FC(0, 0, 0, 1, 0) << 7, FC(0, 0, 0, 1, 1) << 7,
FC(0, 0, 1, 0, 0) << 7, FC(0, 0, 1, 0, 1) << 7, FC(0, 0, 1, 1, 0) << 7, FC(0, 0, 1, 1, 1) << 7,
FC(0, 1, 0, 0, 0) << 7, FC(0, 1, 0, 0, 1) << 7, FC(0, 1, 0, 1, 0) << 7, FC(0, 1, 0, 1, 1) << 7,
FC(0, 1, 1, 0, 0) << 7, FC(0, 1, 1, 0, 1) << 7, FC(0, 1, 1, 1, 0) << 7, FC(0, 1, 1, 1, 1) << 7,
FC(1, 0, 0, 0, 0) << 7, FC(1, 0, 0, 0, 1) << 7, FC(1, 0, 0, 1, 0) << 7, FC(1, 0, 0, 1, 1) << 7,
FC(1, 0, 1, 0, 0) << 7, FC(1, 0, 1, 0, 1) << 7, FC(1, 0, 1, 1, 0) << 7, FC(1, 0, 1, 1, 1) << 7,
FC(1, 1, 0, 0, 0) << 7, FC(1, 1, 0, 0, 1) << 7, FC(1, 1, 0, 1, 0) << 7, FC(1, 1, 0, 1, 1) << 7,
FC(1, 1, 1, 0, 0) << 7, FC(1, 1, 1, 0, 1) << 7, FC(1, 1, 1, 1, 0) << 7, FC(1, 1, 1, 1, 1) << 7
FC(0, 0, 1, 0, 0) << 7, FC(0, 0, 1, 0, 1) << 7, FC(0, 0, 1, 1, 0) << 7, FC(0, 0, 1, 1, 1) << 7,
FC(0, 1, 0, 0, 0) << 7, FC(0, 1, 0, 0, 1) << 7, FC(0, 1, 0, 1, 0) << 7, FC(0, 1, 0, 1, 1) << 7,
FC(0, 1, 1, 0, 0) << 7, FC(0, 1, 1, 0, 1) << 7, FC(0, 1, 1, 1, 0) << 7, FC(0, 1, 1, 1, 1) << 7,
FC(1, 0, 0, 0, 0) << 7, FC(1, 0, 0, 0, 1) << 7, FC(1, 0, 0, 1, 0) << 7, FC(1, 0, 0, 1, 1) << 7,
FC(1, 0, 1, 0, 0) << 7, FC(1, 0, 1, 0, 1) << 7, FC(1, 0, 1, 1, 0) << 7, FC(1, 0, 1, 1, 1) << 7,
FC(1, 1, 0, 0, 0) << 7, FC(1, 1, 0, 0, 1) << 7, FC(1, 1, 0, 1, 0) << 7, FC(1, 1, 0, 1, 1) << 7,
FC(1, 1, 1, 0, 0) << 7, FC(1, 1, 1, 0, 1) << 7, FC(1, 1, 1, 1, 0) << 7, FC(1, 1, 1, 1, 1) << 7
};
/* Special table for nibble processing (e.g. ack), feedback at bit 3 */
static const uint8_t TableC3[32] = {
/* fc with Input {4,3,2,1,0} = (0,0,0,0,0) to (1,1,1,1,1) */
FC(0, 0, 0, 0, 0) << 3, FC(0, 0, 0, 0, 1) << 3, FC(0, 0, 0, 1, 0) << 3, FC(0, 0, 0, 1, 1) << 3,
FC(0, 0, 1, 0, 0) << 3, FC(0, 0, 1, 0, 1) << 3, FC(0, 0, 1, 1, 0) << 3, FC(0, 0, 1, 1, 1) << 3,
FC(0, 1, 0, 0, 0) << 3, FC(0, 1, 0, 0, 1) << 3, FC(0, 1, 0, 1, 0) << 3, FC(0, 1, 0, 1, 1) << 3,
FC(0, 1, 1, 0, 0) << 3, FC(0, 1, 1, 0, 1) << 3, FC(0, 1, 1, 1, 0) << 3, FC(0, 1, 1, 1, 1) << 3,
FC(1, 0, 0, 0, 0) << 3, FC(1, 0, 0, 0, 1) << 3, FC(1, 0, 0, 1, 0) << 3, FC(1, 0, 0, 1, 1) << 3,
FC(1, 0, 1, 0, 0) << 3, FC(1, 0, 1, 0, 1) << 3, FC(1, 0, 1, 1, 0) << 3, FC(1, 0, 1, 1, 1) << 3,
FC(1, 1, 0, 0, 0) << 3, FC(1, 1, 0, 0, 1) << 3, FC(1, 1, 0, 1, 0) << 3, FC(1, 1, 0, 1, 1) << 3,
FC(1, 1, 1, 0, 0) << 3, FC(1, 1, 1, 0, 1) << 3, FC(1, 1, 1, 1, 0) << 3, FC(1, 1, 1, 1, 1) << 3
FC(0, 0, 1, 0, 0) << 3, FC(0, 0, 1, 0, 1) << 3, FC(0, 0, 1, 1, 0) << 3, FC(0, 0, 1, 1, 1) << 3,
FC(0, 1, 0, 0, 0) << 3, FC(0, 1, 0, 0, 1) << 3, FC(0, 1, 0, 1, 0) << 3, FC(0, 1, 0, 1, 1) << 3,
FC(0, 1, 1, 0, 0) << 3, FC(0, 1, 1, 0, 1) << 3, FC(0, 1, 1, 1, 0) << 3, FC(0, 1, 1, 1, 1) << 3,
FC(1, 0, 0, 0, 0) << 3, FC(1, 0, 0, 0, 1) << 3, FC(1, 0, 0, 1, 0) << 3, FC(1, 0, 0, 1, 1) << 3,
FC(1, 0, 1, 0, 0) << 3, FC(1, 0, 1, 0, 1) << 3, FC(1, 0, 1, 1, 0) << 3, FC(1, 0, 1, 1, 1) << 3,
FC(1, 1, 0, 0, 0) << 3, FC(1, 1, 0, 0, 1) << 3, FC(1, 1, 0, 1, 0) << 3, FC(1, 1, 0, 1, 1) << 3,
FC(1, 1, 1, 0, 0) << 3, FC(1, 1, 1, 0, 1) << 3, FC(1, 1, 1, 1, 0) << 3, FC(1, 1, 1, 1, 1) << 3
};
/* Filter Output Macros */
@@ -340,11 +340,11 @@ void Crypto1GetState(uint8_t *pEven, uint8_t *pOdd) {
/* Proceed LFSR by one clock cycle */
/* Prototype to force inlining */
static __inline__ uint8_t Crypto1LFSRbyteFeedback(uint8_t E0,
uint8_t E1,
uint8_t E2,
uint8_t O0,
uint8_t O1,
uint8_t O2) __attribute__((always_inline));
uint8_t E1,
uint8_t E2,
uint8_t O0,
uint8_t O1,
uint8_t O2) __attribute__((always_inline));
static uint8_t Crypto1LFSRbyteFeedback(uint8_t E0,
uint8_t E1,
uint8_t E2,
File diff suppressed because it is too large Load Diff
@@ -126,8 +126,8 @@ uint64_t em410x_id_to_memory64(uint8_t id[5]) {
// Okay, it's the most critical time at present, and now you need to assign and calculate the Qiqi school inspection
// 1. First assign the front guide code
memory.bit.h00 = memory.bit.h01 = memory.bit.h02 =
memory.bit.h03 = memory.bit.h04 = memory.bit.h05 =
memory.bit.h06 = memory.bit.h07 = memory.bit.h08 = 1;
memory.bit.h03 = memory.bit.h04 = memory.bit.h05 =
memory.bit.h06 = memory.bit.h07 = memory.bit.h08 = 1;
//2. Assign the 8bit version or custom ID
memory.bit.d00 = GETBIT(id[0], 7);
memory.bit.d01 = GETBIT(id[0], 6);
@@ -261,7 +261,7 @@ void timer_ce_handler(nrf_timer_event_t event_type, void *p_context) {
if (m_is_send_first_edge == true) { // The first edge of the next sends next time
if (++m_bit_send_position >= LF_125KHZ_EM410X_BIT_SIZE) {
m_bit_send_position = 0; // The broadcast is successful once, and the BIT position is zero
if(!lf_is_field_exists()){ // To avoid stopping sending when the reader field is present
if (!lf_is_field_exists()) { // To avoid stopping sending when the reader field is present
m_send_id_count++;
}
if (m_send_id_count >= LF_125KHZ_BROADCAST_MAX) {
@@ -1014,18 +1014,18 @@ uint16_t auth_key_use_522_hw(uint8_t block, uint8_t type, uint8_t *key) {
return pcd_14a_reader_mf1_auth(p_tag_info, type, block, key);
}
inline void mf1_toolbox_antenna_restart () {
inline void mf1_toolbox_antenna_restart() {
pcd_14a_reader_reset();
pcd_14a_reader_antenna_on();
bsp_delay_ms(8);
}
inline void mf1_toolbox_report_healthy () {
inline void mf1_toolbox_report_healthy() {
bsp_wdt_feed();
while (NRF_LOG_PROCESS());
}
uint16_t mf1_toolbox_check_keys_of_sectors (
uint16_t mf1_toolbox_check_keys_of_sectors(
mf1_toolbox_check_keys_of_sectors_in_t *in,
mf1_toolbox_check_keys_of_sectors_out_t *out
) {
@@ -1068,7 +1068,7 @@ uint16_t mf1_toolbox_check_keys_of_sectors (
// try to read keyB from trailer of sector
status = pcd_14a_reader_mf1_read(trailerNo, trailer);
// key B not in trailer
if (status != STATUS_HF_TAG_OK || 0 == *(uint64_t*) &trailer[10]) break;
if (status != STATUS_HF_TAG_OK || 0 == *(uint64_t *) &trailer[10]) break;
// key B found
skipKeyB = true;
out->found.b[i / 4] |= 0b1 << maskShift;
@@ -1110,8 +1110,8 @@ uint16_t mf1_toolbox_check_keys_of_sectors (
* @retval : STATUS_HF_TAG_OK is returned if the acquisition is successful, and non-HF_TAG_OK is returned if the acquisition is unsuccessful Value
*
*/
uint8_t mf1_hardnested_nonces_acquire(bool slow, uint8_t blkKnown, uint8_t typKnown, uint64_t keyKnown,
uint8_t targetBlk, uint8_t targetTyp, uint8_t* nonces, uint16_t noncesMax, uint8_t* num_nonces) {
uint8_t mf1_hardnested_nonces_acquire(bool slow, uint8_t blkKnown, uint8_t typKnown, uint64_t keyKnown,
uint8_t targetBlk, uint8_t targetTyp, uint8_t *nonces, uint16_t noncesMax, uint8_t *num_nonces) {
struct Crypto1State mpcs = { 0, 0 };
struct Crypto1State *pcs = &mpcs;
uint8_t answer[] = { 0x00, 0x00, 0x00, 0x00 };
@@ -1185,7 +1185,139 @@ uint8_t mf1_hardnested_nonces_acquire(bool slow, uint8_t blkKnown, uint8_t typKn
tag_selected = true;
}
}
// OK!
return STATUS_HF_TAG_OK;
}
//-----------------------------------------------------------------------------
// acquire static encrypted nonces in order to perform the attack described in
// Philippe Teuwen, "MIFARE Classic: exposing the static encrypted nonce variant"
//-----------------------------------------------------------------------------
uint8_t mf1_static_encrypted_nonces_acquire(uint64_t keyKnown, uint8_t sector_count, uint8_t starting_sector, uint8_t sector_data[40][sizeof(mf1_static_nonce_sector_t)], uint8_t *sectors_acquired, uint32_t *cardUid) {
struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs = &mpcs;
uint8_t receivedAnswer[16] = {0x00};
uint8_t par_enc[4] = {0x00};
uint32_t cuid = 0;
bool have_uid = false;
*sectors_acquired = 0;
for (uint16_t sec = starting_sector; sec < sector_count && sec < 40; sec++) {
mf1_toolbox_report_healthy();
uint16_t blockNo = (sec < 32) ? sec * 4 + 3 : 128 + (sec - 32) * 16 + 15;
mf1_static_nonce_sector_t sector_nonces = {0};
for (uint8_t keyType = 0; keyType < 2; keyType++) {
memset(par_enc, 0, sizeof(par_enc));
if (have_uid == false) {
if (pcd_14a_reader_scan_auto(p_tag_info) != STATUS_HF_TAG_OK) {
return STATUS_HF_TAG_NO;
}
cuid = get_u32_tag_uid(p_tag_info);
have_uid = true;
} else {
if (pcd_14a_reader_fast_select(p_tag_info) != STATUS_HF_TAG_OK) {
return STATUS_HF_TAG_NO;
}
}
uint32_t nt1 = 0;
if (authex(pcs, cuid, blockNo, 0x60 + keyType + 4, keyKnown, AUTH_FIRST, &nt1) != STATUS_HF_TAG_OK) {
return STATUS_MF_ERR_AUTH;
}
uint8_t cmd_status;
uint8_t res = send_cmd(pcs, AUTH_NESTED, 0x60 + keyType + 4, blockNo, &cmd_status, receivedAnswer, par_enc, sizeof(receivedAnswer) * 8);
if (res != 32) {
return STATUS_MF_ERR_AUTH;
}
uint32_t nt_enc = bytes_to_num(receivedAnswer, 4);
crypto1_init(pcs, keyKnown);
uint32_t nt = crypto1_word(pcs, nt_enc ^ cuid, 1) ^ nt_enc;
uint16_t nt_first_half = nt >> 16;
if (pcd_14a_reader_fast_select(p_tag_info) != STATUS_HF_TAG_OK) {
return STATUS_HF_TAG_NO;
}
if (authex(pcs, cuid, blockNo, 0x60 + keyType + 4, keyKnown, AUTH_FIRST, &nt1) != STATUS_HF_TAG_OK) {
return STATUS_MF_ERR_AUTH;
}
res = send_cmd(pcs, AUTH_NESTED, 0x60 + keyType, blockNo, &cmd_status, receivedAnswer, par_enc, sizeof(receivedAnswer) * 8);
if (res != 32) {
return STATUS_MF_ERR_AUTH;
}
uint32_t nt_enc_final = bytes_to_num(receivedAnswer, 4);
uint8_t nt_par_err = ((par_enc[0] ^ oddparity8((nt_enc_final >> 24) & 0xFF)) << 3 |
(par_enc[1] ^ oddparity8((nt_enc_final >> 16) & 0xFF)) << 2 |
(par_enc[2] ^ oddparity8((nt_enc_final >> 8) & 0xFF)) << 1 |
(par_enc[3] ^ oddparity8((nt_enc_final >> 0) & 0xFF)));
if (keyType == 0) {
num_to_bytes(nt_first_half, 2, sector_nonces.key_a.nt_first_half);
sector_nonces.key_a.nt_par_err = nt_par_err;
num_to_bytes(nt_enc_final, 4, sector_nonces.key_a.nt_enc);
} else {
num_to_bytes(nt_first_half, 2, sector_nonces.key_b.nt_first_half);
sector_nonces.key_b.nt_par_err = nt_par_err;
num_to_bytes(nt_enc_final, 4, sector_nonces.key_b.nt_enc);
}
}
memcpy(sector_data[sec], &sector_nonces, sizeof(sector_nonces));
(*sectors_acquired)++;
}
crypto1_deinit(pcs);
*cardUid = cuid;
return STATUS_HF_TAG_OK;
}
uint16_t mf1_toolbox_check_keys_on_block(
mf1_toolbox_check_keys_on_block_in_t *in,
mf1_toolbox_check_keys_on_block_out_t *out
) {
memset(out, 0, sizeof(mf1_toolbox_check_keys_on_block_out_t));
struct Crypto1State mpcs = {0, 0};
struct Crypto1State *pcs = &mpcs;
uint32_t cuid = 0;
bool have_uid = false;
for (int i = 0; i < in->keys_len; i++) {
mf1_toolbox_report_healthy();
if (have_uid == false) {
if (pcd_14a_reader_scan_auto(p_tag_info) != STATUS_HF_TAG_OK) {
return STATUS_HF_TAG_NO;
}
cuid = get_u32_tag_uid(p_tag_info);
have_uid = true;
} else {
if (pcd_14a_reader_fast_select(p_tag_info) != STATUS_HF_TAG_OK) {
return STATUS_HF_TAG_NO;
}
}
uint32_t nt1 = 0;
uint64_t key_u64 = bytes_to_num(in->keys[i].key, 6);
if (authex(pcs, cuid, in->block, in->key_type, key_u64, AUTH_FIRST, &nt1) == STATUS_HF_TAG_OK) {
out->found = 1;
out->key = in->keys[i];
return STATUS_HF_TAG_OK;
}
}
return STATUS_HF_TAG_NO;
}
@@ -76,6 +76,29 @@ typedef struct {
mf1_key_t keys[40][2]; // 6 bytes * 2 keys * 40 sectors = 480 bytes
} PACKED mf1_toolbox_check_keys_of_sectors_out_t;
typedef struct {
uint8_t block;
uint8_t key_type;
uint8_t keys_len;
mf1_key_t *keys;
} mf1_toolbox_check_keys_on_block_in_t;
typedef struct {
uint8_t found;
mf1_key_t key;
} PACKED mf1_toolbox_check_keys_on_block_out_t;
typedef struct {
uint8_t nt_first_half[2];
uint8_t nt_par_err;
uint8_t nt_enc[4];
} PACKED mf1_static_nonce_keytype_t;
typedef struct {
mf1_static_nonce_keytype_t key_a;
mf1_static_nonce_keytype_t key_b;
} PACKED mf1_static_nonce_sector_t;
#ifdef __cplusplus
extern "C" {
#endif
@@ -113,13 +136,20 @@ uint8_t check_std_mifare_nt_support();
void antenna_switch_delay(uint32_t delay_ms);
uint16_t auth_key_use_522_hw(uint8_t block, uint8_t type, uint8_t *key);
uint16_t mf1_toolbox_check_keys_of_sectors (
uint16_t mf1_toolbox_check_keys_of_sectors(
mf1_toolbox_check_keys_of_sectors_in_t *in,
mf1_toolbox_check_keys_of_sectors_out_t *out
);
uint8_t mf1_hardnested_nonces_acquire(bool slow, uint8_t blkKnown, uint8_t typKnown, uint64_t keyKnown,
uint8_t targetBlk, uint8_t targetTyp, uint8_t* nonces, uint16_t noncesMax, uint8_t* num_nonces);
uint16_t mf1_toolbox_check_keys_on_block(
mf1_toolbox_check_keys_on_block_in_t *in,
mf1_toolbox_check_keys_on_block_out_t *out
);
uint8_t mf1_hardnested_nonces_acquire(bool slow, uint8_t blkKnown, uint8_t typKnown, uint64_t keyKnown,
uint8_t targetBlk, uint8_t targetTyp, uint8_t *nonces, uint16_t noncesMax, uint8_t *num_nonces);
uint8_t mf1_static_encrypted_nonces_acquire(uint64_t keyKnown, uint8_t sector_count, uint8_t starting_sector, uint8_t sector_data[40][sizeof(mf1_static_nonce_sector_t)], uint8_t *sectors_acquired, uint32_t *cardUid);
#ifdef __cplusplus
}
@@ -622,7 +622,7 @@ uint8_t pcd_14a_reader_bytes_transfer_flags(uint8_t Command, uint8_t *pIn, uint8
* @param :tag: tag info buffer
* @retval : if return STATUS_HF_TAG_OK, the tag is selected.
*/
uint8_t pcd_14a_reader_fast_select(picc_14a_tag_t *tag) {
uint8_t pcd_14a_reader_fast_select(picc_14a_tag_t *tag) {
uint8_t dat_buff[9] = { 0x00 };
uint8_t status = STATUS_HF_TAG_OK;
uint8_t cascade_level = 0;
@@ -1144,14 +1144,14 @@ uint8_t pcd_14a_reader_mf1_manipulate_value_block(uint8_t operator, uint8_t addr
// 2. Transfer the operand to complete the value block manipulation
status = pcd_14a_reader_bytes_transfer_flags(
PCD_TRANSCEIVE,
dat_buff,
6,
dat_buff,
&dat_len,
U8ARR_BIT_LEN(dat_buff),
PCD_TRANSMIT_FLAG_NO_RESET_MF_CRYPTO1_ON);
PCD_TRANSCEIVE,
dat_buff,
6,
dat_buff,
&dat_len,
U8ARR_BIT_LEN(dat_buff),
PCD_TRANSMIT_FLAG_NO_RESET_MF_CRYPTO1_ON);
// Operand Part of Increment/Decrement/Restore does not acknowledge, so Timeout means success
if (status != STATUS_HF_TAG_NO || dat_len != 0) {
return status == STATUS_HF_TAG_OK ? STATUS_HF_ERR_STAT : status;
@@ -196,12 +196,12 @@ uint8_t pcd_14a_reader_bytes_transfer(uint8_t Command,
uint16_t *pOutLenBit,
uint16_t maxOutLenBit);
uint8_t pcd_14a_reader_bytes_transfer_flags(uint8_t Command,
uint8_t *pIn,
uint8_t InLenByte,
uint8_t *pOut,
uint16_t *pOutLenBit,
uint16_t maxOutLenBit,
uint32_t flags);
uint8_t *pIn,
uint8_t InLenByte,
uint8_t *pOut,
uint16_t *pOutLenBit,
uint16_t maxOutLenBit,
uint32_t flags);
uint8_t pcd_14a_reader_bits_transfer(uint8_t *pTx,
uint16_t szTxBits,
uint8_t *pTxPar,
+1 -1
View File
@@ -20,7 +20,7 @@ find . \( -not -path "./.git/*" -and -not -path "./firmware/nrf52_sdk/*" -and -n
-name "*.[ch]" \) \
-exec astyle --formatted --mode=c --suffix=none \
--indent=spaces=4 --indent-switches \
--keep-one-line-blocks --max-instatement-indent=60 \
--keep-one-line-blocks \
--style=google --pad-oper --unpad-paren --pad-header \
--align-pointer=name {} \;
# Apply autopep8 on *py
File diff suppressed because it is too large Load Diff
+98 -10
View File
@@ -3,7 +3,7 @@ import ctypes
from typing import Union
import chameleon_com
from chameleon_utils import expect_response
from chameleon_utils import expect_response, reconstruct_full_nt, parity_to_str
from chameleon_enum import Command, SlotNumber, Status, TagSenseType, TagSpecificType
from chameleon_enum import ButtonPressFunction, ButtonType, MifareClassicDarksideStatus
from chameleon_enum import MfcKeyType, MfcValueBlockOperator
@@ -270,7 +270,7 @@ class ChameleonCMD:
raise ValueError(f'bitlen={bitlen} but missing data')
if not ((len(data) - 1) * 8 < bitlen <= len(data) * 8):
raise ValueError(f'bitlen={bitlen} incompatible with provided data ({len(data)} bytes), '
f'must be between {((len(data) - 1) * 8 )+1} and {len(data) * 8} included')
f'must be between {((len(data) - 1) * 8)+1} and {len(data) * 8} included')
data = bytes(cs)+struct.pack(f'!HH{len(data)}s', resp_timeout_ms, bitlen, bytearray(data))
resp = self.device.send_cmd_sync(Command.HF14A_RAW, data, timeout=(resp_timeout_ms // 1000) + 1)
@@ -312,16 +312,16 @@ class ChameleonCMD:
raise ValueError("Invalid len(keys)")
data = struct.pack(f'!10s{6*len(keys)}s', mask, b''.join(keys))
bitsCnt = 80 # maximum sectorKey_to_be_checked
bitsCnt = 80 # maximum sectorKey_to_be_checked
for b in mask:
while b > 0:
[bitsCnt, b] = [bitsCnt - (b & 0b1), b >> 1]
if bitsCnt < 1:
# All sectorKey is masked
return chameleon_com.Response(
cmd=Command.MF1_CHECK_KEYS_OF_SECTORS,
cmd=Command.MF1_CHECK_KEYS_OF_SECTORS,
status=Status.HF_TAG_OK,
parsed={ 'status': Status.HF_TAG_OK },
parsed={'status': Status.HF_TAG_OK},
)
# base timeout: 1s
# auth: len(keys) * sectorKey_to_be_checked * 0.1s
@@ -329,7 +329,7 @@ class ChameleonCMD:
timeout = 1 + (bitsCnt + 1) * len(keys) * 0.1
resp = self.device.send_cmd_sync(Command.MF1_CHECK_KEYS_OF_SECTORS, data, timeout=timeout)
resp.parsed = { 'status': resp.status }
resp.parsed = {'status': resp.status}
if len(resp.data) == 490:
found = ''.join([format(i, '08b') for i in resp.data[0:10]])
# print(f'{found = }')
@@ -339,6 +339,23 @@ class ChameleonCMD:
})
return resp
@expect_response([Status.HF_TAG_OK, Status.HF_TAG_NO])
def mf1_check_keys_on_block(self, block: int, key_type: int, keys: list[bytes]):
if key_type not in [0x60, 0x61]:
raise ValueError("Wrong key type")
if len(keys) < 1 or len(keys) > 83:
raise ValueError("Invalid len(keys)")
data = struct.pack(f'!BBB{6*len(keys)}s', block, key_type, len(keys), b''.join(keys))
resp = self.device.send_cmd_sync(Command.MF1_CHECK_KEYS_ON_BLOCK, data, timeout=10)
if resp.status == Status.HF_TAG_OK and len(resp.data) == 7:
found, key = struct.unpack('!B6s', resp.data)
if found:
resp.parsed = key
return resp
@expect_response(Status.HF_TAG_OK)
def mf1_static_nested_acquire(self, block_known, type_known, key_known, block_target, type_target):
"""
@@ -358,7 +375,7 @@ class ChameleonCMD:
]
}
return resp
@expect_response(Status.HF_TAG_OK)
def mf1_hard_nested_acquire(self, slow, block_known, type_known, key_known, block_target, type_target):
"""
@@ -366,11 +383,46 @@ class ChameleonCMD:
:return:
"""
data = struct.pack('!BBB6sBB', slow, type_known, block_known, key_known, type_target, block_target)
resp = self.device.send_cmd_sync(Command.DATA_CMD_MF1_HARDNESTED_ACQUIRE, data, timeout=30)
resp = self.device.send_cmd_sync(Command.MF1_HARDNESTED_ACQUIRE, data, timeout=30)
if resp.status == Status.HF_TAG_OK:
resp.parsed = resp.data # we can return the raw nonces bytes
return resp
@expect_response([Status.HF_TAG_OK, Status.HF_TAG_NO])
def mf1_static_encrypted_nested_acquire(self, backdoor_key, sector_count, starting_sector):
data = struct.pack('!6sBB', backdoor_key, sector_count, starting_sector)
resp = self.device.send_cmd_sync(Command.MF1_ENC_NESTED_ACQUIRE, data, timeout=30)
if resp.status == Status.HF_TAG_OK:
resp.parsed = {
'uid': struct.unpack('!I', resp.data[0:4])[0],
'nts': {
'a': [],
'b': []
}
}
i = 4
while i < len(resp.data):
resp.parsed['nts']['a'].append(
{
'nt': reconstruct_full_nt(resp.data, i),
'nt_enc': int.from_bytes(resp.data[i + 3: i + 7]),
'parity': parity_to_str(resp.data[i + 2])
}
)
resp.parsed['nts']['b'].append(
{
'nt': reconstruct_full_nt(resp.data, i + 7),
'nt_enc': int.from_bytes(resp.data[i + 10: i + 14]),
'parity': parity_to_str(resp.data[i + 9])
}
)
i += 14
return resp
@expect_response(Status.LF_TAG_OK)
def em410x_scan(self):
"""
@@ -626,7 +678,7 @@ class ChameleonCMD:
data = struct.pack('!B', index)
resp = self.device.send_cmd_sync(Command.MF0_NTAG_GET_COUNTER_DATA, data)
if resp.status == Status.SUCCESS:
resp.parsed = (((resp.data[0] << 16) | (resp.data[1] << 8) | resp.data[2]), resp.data[3] == 0xBD)
resp.parsed = (((resp.data[2] << 16) | (resp.data[1] << 8) | resp.data[0]), resp.data[3] == 0xBD)
return resp
@expect_response(Status.SUCCESS)
@@ -634,7 +686,8 @@ class ChameleonCMD:
"""
Sets data for selected counter
"""
data = struct.pack('!BBBB', index | (int(reset_tearing) << 7), (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF)
data = struct.pack('!BBBB', index | (int(reset_tearing) << 7),
value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF)
resp = self.device.send_cmd_sync(Command.MF0_NTAG_SET_COUNTER_DATA, data)
return resp
@@ -694,6 +747,41 @@ class ChameleonCMD:
resp.parsed = resp.data.decode(encoding="utf8")
return resp
@expect_response(Status.SUCCESS)
def get_all_slot_nicks(self):
resp = self.device.send_cmd_sync(Command.GET_ALL_SLOT_NICKS, b'')
slots = []
i = 0
slot_index = 0
while i < len(resp.data) and slot_index < 8:
slot_names = {'hf': '', 'lf': ''}
if i < len(resp.data):
hf_len = resp.data[i]
i += 1
if hf_len > 0 and i + hf_len <= len(resp.data):
slot_names['hf'] = resp.data[i:i + hf_len].decode(encoding="utf8", errors="ignore")
i += hf_len
else:
i += hf_len
if i < len(resp.data):
lf_len = resp.data[i]
i += 1
if lf_len > 0 and i + lf_len <= len(resp.data):
slot_names['lf'] = resp.data[i:i + lf_len].decode(encoding="utf8", errors="ignore")
i += lf_len
else:
i += lf_len
slots.append(slot_names)
slot_index += 1
resp.parsed = slots
return resp
@expect_response(Status.SUCCESS)
def delete_slot_tag_nick(self, slot: SlotNumber, sense_type: TagSenseType):
"""
+7 -1
View File
@@ -13,6 +13,7 @@ class Command(enum.IntEnum):
SET_SLOT_TAG_NICK = 1007
GET_SLOT_TAG_NICK = 1008
GET_ALL_SLOT_NICKS = 1038
SLOT_DATA_CONFIG_SAVE = 1009
@@ -69,7 +70,9 @@ class Command(enum.IntEnum):
HF14A_RAW = 2010
MF1_MANIPULATE_VALUE_BLOCK = 2011
MF1_CHECK_KEYS_OF_SECTORS = 2012
DATA_CMD_MF1_HARDNESTED_ACQUIRE = 2013
MF1_HARDNESTED_ACQUIRE = 2013
MF1_ENC_NESTED_ACQUIRE = 2014
MF1_CHECK_KEYS_ON_BLOCK = 2015
EM410X_SCAN = 3000
EM410X_WRITE_TO_T55XX = 3001
@@ -376,6 +379,7 @@ class MifareClassicWriteMode(enum.IntEnum):
return "Shadow requested"
return "None"
@enum.unique
class MifareUltralightWriteMode(enum.IntEnum):
# Normal write
@@ -408,6 +412,7 @@ class MifareUltralightWriteMode(enum.IntEnum):
return "Shadow requested"
return "None"
@enum.unique
class MifareClassicPrngType(enum.IntEnum):
# the random number of the card response is fixed
@@ -501,6 +506,7 @@ class ButtonPressFunction(enum.IntEnum):
return "Show Battery Level"
return "None"
@enum.unique
class MfcValueBlockOperator(enum.IntEnum):
DECREMENT = 0xC0
+104 -1
View File
@@ -1,4 +1,10 @@
import argparse
import subprocess
import sys
import tempfile
import os.path
from pathlib import Path
import colorama
from functools import wraps
# once Python3.10 is mainstream, we can replace Union[str, None] by str | None
@@ -18,6 +24,8 @@ CY = colorama.Fore.YELLOW
CM = colorama.Fore.MAGENTA
C0 = colorama.Style.RESET_ALL
default_cwd = Path.cwd() / Path(__file__).with_name("bin")
class ArgsParserError(Exception):
pass
@@ -102,6 +110,7 @@ class ArgumentParserNoExit(argparse.ArgumentParser):
print('')
self.help_requested = True
def print_mem_dump(bindata, blocksize):
hexadecimal_len = blocksize*3+1
@@ -114,10 +123,104 @@ def print_mem_dump(bindata, blocksize):
blk_index = 1
for b in blocks:
hexstr = ' '.join(b.hex()[i:i+2] for i in range(0, len(b.hex()), 2))
asciistr = ''.join([chr(b[i]) if (b[i] > 31 and b[i] < 127) else '.' for i in range(0,len(b),1)])
asciistr = ''.join([chr(b[i]) if (b[i] > 31 and b[i] < 127) else '.' for i in range(0, len(b), 1)])
print(f"[=] {blk_index:3} | {hexstr.upper()} | {asciistr} ")
blk_index += 1
def print_key_table(key_map):
key_width = max(
max(len(k) for k in key_map["A"].values()),
max(len(k) for k in key_map["B"].values()),
len("key A"),
len("key B"),
)
header_line = f"[=] {'-'*5}+{'-'*(key_width+2)}+{'-'*(key_width+2)}"
print(header_line)
print(f"[=] sec | key A{' '*(key_width-5)} | key B{' '*(key_width-5)}")
print(header_line)
for sec, (a, b) in enumerate(zip(key_map["A"].values(), key_map["B"].values())):
print(f"[=] {sec:02d} | {a:{key_width}} | {b:{key_width}}")
print(header_line)
def _swap_endian(x):
x = ((x >> 8) & 0x00ff00ff) | ((x & 0x00ff00ff) << 8)
x = (x >> 16) | (x << 16)
return x & 0xFFFFFFFF
def prng_successor(x, n):
x = _swap_endian(x)
while n > 0:
x = (x >> 1) | (
(((x >> 16) ^ (x >> 18) ^ (x >> 19) ^ (x >> 21)) & 0x1) << 31
)
x = x & 0xFFFFFFFF
n -= 1
return _swap_endian(x)
def reconstruct_full_nt(response_data, offset):
nt = int.from_bytes(response_data[offset: offset + 2])
return (nt << 16) | prng_successor(nt, 16)
def parity_to_str(nt_par_err):
return "".join(
[
str((nt_par_err >> 3) & 1),
str((nt_par_err >> 2) & 1),
str((nt_par_err >> 1) & 1),
str(nt_par_err & 1),
]
)
def execute_tool(tool_name, args):
if sys.platform == "win32":
tool_executable = f"{tool_name}.exe"
else:
tool_executable = f"./{tool_name}"
tool_path = os.path.join(default_cwd, tool_executable)
cmd_recover_list = [tool_path]
cmd_recover_list.extend(args)
# print(f"Executing: {' '.join(cmd_recover_list)}")
temp_output_file = tempfile.NamedTemporaryFile(
suffix=".log", prefix="output_", delete=True,
mode='w+', encoding='utf-8', errors='replace'
)
process = subprocess.Popen(
cmd_recover_list,
cwd=tempfile.gettempdir(),
stdout=temp_output_file,
stderr=subprocess.STDOUT,
)
ret_code = process.wait()
temp_output_file.seek(0)
if ret_code:
raise Exception('Failed to execute tool: ' + temp_output_file.read())
return temp_output_file.read()
def tqdm_if_exists(iterator):
try:
import tqdm
return tqdm.tqdm(iterator)
except ImportError:
return iterator
def expect_response(accepted_responses: Union[int, list[int]]) -> Callable[..., Any]:
"""
Decorator for wrapping a Chameleon CMD function to check its response
+3 -1
View File
@@ -4,6 +4,8 @@ import threading
import time
# From https://stackoverflow.com/a/29834357
class OutputGrabber(object):
"""
Class used to grab standard output or another stream.
@@ -72,7 +74,7 @@ class OutputGrabber(object):
and save the text in `captured_text`.
"""
while True:
char = os.read(self.pipe_out,1).decode(self.origstream.encoding)
char = os.read(self.pipe_out, 1).decode(self.origstream.encoding)
if not char or self.escape_char in char:
break
self.captured_text += char
+8 -8
View File
@@ -1,24 +1,22 @@
#!/usr/bin/env python3
import hardnested_utils
from chameleon_cmd import ChameleonCMD
from chameleon_com import ChameleonCom, OpenFailException
import sys
sys.path.append('..')
from chameleon_com import ChameleonCom, OpenFailException
from chameleon_cmd import ChameleonCMD
import hardnested_utils
def test_hardnested_acquire():
nonces_buffer = bytearray()
acquire_count = 0
# known key and target block
key = bytes.fromhex("FFFFFFFFFFFF") # <-- Your known key
key = bytes.fromhex("FFFFFFFFFFFF") # <-- Your known key
block_known = 0x00
type_known = 0x60
block_target = 0x00
type_target = 0x60
# Before acquire start, we need to reset history
hardnested_utils.reset()
@@ -32,7 +30,7 @@ def test_hardnested_acquire():
cml = ChameleonCom().open('/dev/ttyACM0')
cml_cmd = ChameleonCMD(cml)
# ------------------------ SET DEVICE MODE ------------------------
# ------------------------ SET DEVICE MODE ------------------------
print("Setting device mode to HF Reader...")
status = cml_cmd.set_device_reader_mode()
@@ -59,7 +57,8 @@ def test_hardnested_acquire():
while True:
# 1, acquire from device
acquire_datas = cml_cmd.mf1_hard_nested_acquire(0, block_known, type_known, key, block_target, type_target) # slow = 0 to fast acquire...
# slow = 0 to fast acquire...
acquire_datas = cml_cmd.mf1_hard_nested_acquire(0, block_known, type_known, key, block_target, type_target)
if acquire_datas is not None:
acquire_count += 1
print(f"Acquire success, count: {acquire_count}")
@@ -102,6 +101,7 @@ def test_hardnested_acquire():
# You can decrypt nonce bin by pm3 client, or any app if support pm3 nonce bin format.
# TODO If CU bin can decrypt, run cmd on here...
if __name__ == "__main__":
try:
test_hardnested_acquire()
+64 -108
View File
@@ -2,9 +2,20 @@ cmake_minimum_required (VERSION 3.5)
project (mifare C)
include(FetchContent)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../script/bin)
set(SRC_DIR ./) # Assuming source files are in the same directory as CMakeLists.txt
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})
if(CMAKE_CONFIGURATION_TYPES)
foreach(config ${CMAKE_CONFIGURATION_TYPES})
string(TOUPPER ${config} config_upper)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config_upper} ${EXECUTABLE_OUTPUT_PATH})
endforeach()
endif()
# Define a variable for the compatibility code directory
set(COMPAT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/compat)
@@ -25,76 +36,25 @@ set(
${SRC_DIR}/mfkey.c
)
# --- liblzma Build ---
# NOTE: Ensure the path 'xz' matches the actual directory name containing liblzma source
set(LIBLZMA_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/xz)
# Define the build directory *relative* to the liblzma source directory
set(LIBLZMA_BUILD_SUBDIR build)
set(LIBLZMA_BUILD_DIR ${LIBLZMA_SRC_DIR}/${LIBLZMA_BUILD_SUBDIR})
# Define CMake arguments for configuring liblzma
set(LIBLZMA_CMAKE_ARGS
-DXZ_TOOL_XZ=OFF
-DXZ_TOOL_XZDEC=OFF
-DXZ_TOOL_LZMADEC=OFF
-DXZ_TOOL_LZMAINFO=OFF
-DXZ_TOOL_SCRIPTS=OFF
-DXZ_DOC=OFF
-DXZ_NLS=OFF
-DXZ_DOXYGEN=OFF
-DBUILD_SHARED_LIBS=OFF # Ensure static lib is built
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
)
# Add platform-specific args
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
list(APPEND LIBLZMA_CMAKE_ARGS "-DXZ_SANDBOX=no")
endif()
# --- Define the expected path for the built liblzma library ---
if(CMAKE_SYSTEM_NAME MATCHES "Windows")
if(MSVC)
# Point to the Release directory as the build command uses --config Release
set(LIBLZMA_LIB_PATH "${LIBLZMA_BUILD_DIR}/Release/lzma.lib")
else() # MinGW / Ninja
# Assuming liblzma.a goes directly into build/ for non-MSVC Windows
set(LIBLZMA_LIB_PATH "${LIBLZMA_BUILD_DIR}/liblzma.a")
endif()
else()
# Single-config (Linux Makefiles/Ninja): Library is typically directly in the build directory
set(LIBLZMA_LIB_PATH "${LIBLZMA_BUILD_DIR}/liblzma.a")
endif()
message(STATUS "Expecting liblzma at: ${LIBLZMA_LIB_PATH}")
# --- Use add_custom_command to declare the output file and the commands to create it ---
add_custom_command(
OUTPUT ${LIBLZMA_LIB_PATH} # Declare the file that will be generated
# Command 1: Configure liblzma
COMMAND ${CMAKE_COMMAND} -B ${LIBLZMA_BUILD_SUBDIR} -S . ${LIBLZMA_CMAKE_ARGS} -G "${CMAKE_GENERATOR}" # Pass generator
# Command 2: Build liblzma (using CMake --build)
COMMAND ${CMAKE_COMMAND} --build ${LIBLZMA_BUILD_SUBDIR} --config Release # Force Release build for liblzma
WORKING_DIRECTORY ${LIBLZMA_SRC_DIR}
DEPENDS ${LIBLZMA_SRC_DIR}/CMakeLists.txt # Re-run if xz's CMakeLists changes
COMMENT "Configuring and building liblzma (${LIBLZMA_LIB_PATH})"
VERBATIM
USES_TERMINAL # Show output during build
FetchContent_Declare(
xz
GIT_REPOSITORY "https://github.com/tukaani-project/xz"
GIT_TAG "v5.8.1"
OVERRIDE_FIND_PACKAGE
EXCLUDE_FROM_ALL
)
# --- Custom target that DEPENDS on the output file ---
# This target ensures the add_custom_command runs.
# Add ALL so it runs as part of the default build.
add_custom_target(build_liblzma ALL
DEPENDS ${LIBLZMA_LIB_PATH} # Depend on the output file generated by add_custom_command
)
set(XZ_TOOL_XZ OFF CACHE BOOL "")
set(XZ_TOOL_XZDEC OFF CACHE BOOL "")
set(XZ_TOOL_LZMADEC OFF CACHE BOOL "")
set(XZ_TOOL_LZMAINFO OFF CACHE BOOL "")
set(XZ_TOOL_SCRIPTS OFF CACHE BOOL "")
set(XZ_DOC OFF CACHE BOOL "")
set(XZ_NLS OFF CACHE BOOL "")
set(XZ_DOXYGEN OFF CACHE BOOL "")
set(BUILD_SHARED_LIBS OFF CACHE BOOL "")
# --- Create an IMPORTED library target for liblzma ---
add_library(liblzma_imported STATIC IMPORTED GLOBAL)
set_target_properties(liblzma_imported PROPERTIES
IMPORTED_LOCATION "${LIBLZMA_LIB_PATH}"
INTERFACE_INCLUDE_DIRECTORIES "${LIBLZMA_SRC_DIR}/src/liblzma/api" # Public include path
)
# --- Ensure the IMPORTED target depends on the custom target ---
add_dependencies(liblzma_imported build_liblzma)
FetchContent_MakeAvailable(xz)
# --- Hardnested Recovery Sources ---
@@ -137,42 +97,16 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
endif()
endif()
# --- Pthread library handling for Windows ---
if(MSVC)
# MSVC: Find the specific pthreads-win32 library
message(STATUS "MSVC compiler detected. Looking for pthreads-win32 library.")
find_library(PTHREAD_LIB_PATH pthreadVC2.lib PATHS ${CMAKE_CURRENT_SOURCE_DIR}/lib/pthread/lib/x64/)
if (NOT PTHREAD_LIB_PATH)
message(FATAL_ERROR "pthreadVC2.lib not found in ${CMAKE_CURRENT_SOURCE_DIR}/lib/pthread/lib/x64/. Please provide pthreads-win32 for MSVC.")
endif()
# Create an imported library for pthread on Windows for consistency
add_library(pthread STATIC IMPORTED GLOBAL)
set_target_properties(pthread PROPERTIES
IMPORTED_LOCATION ${PTHREAD_LIB_PATH}
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/lib/pthread/include
)
set(LIBTHREAD pthread) # Use the imported target name
elseif(CMAKE_C_COMPILER_ID MATCHES "GNU" OR CMAKE_C_COMPILER_ID MATCHES "Clang") # Check for MinGW (GCC) or Clang on Windows
# MinGW or Clang on Windows: Use find_package(Threads) to find the bundled winpthreads
message(STATUS "MinGW (GCC) or Clang compiler detected on Windows. Using find_package(Threads).")
find_package(Threads REQUIRED)
if(Threads_FOUND)
set(LIBTHREAD Threads::Threads) # Use the modern CMake target
message(STATUS "Found MinGW pthreads using find_package(Threads).")
else()
# This shouldn't happen if Threads is REQUIRED, but good practice
message(FATAL_ERROR "Could not find pthreads using find_package(Threads) with MinGW/Clang. Check your toolchain installation.")
endif()
else()
message(FATAL_ERROR "Unsupported Windows compiler: ${CMAKE_C_COMPILER_ID}. Cannot determine how to find pthreads.")
endif()
# --- End Pthread library handling ---
FetchContent_Declare(
pthreads4w
GIT_REPOSITORY "https://github.com/GerHobbelt/pthread-win32"
OVERRIDE_FIND_PACKAGE
EXCLUDE_FROM_ALL
)
find_package(pthreads4w CONFIG REQUIRED)
set(LIBTHREAD pthreads4w::pthreadVC3)
set(LIBMATH "") # No separate math library needed on Windows
else()
# Handle other platforms or provide a default/error
MESSAGE(STATUS "Running on other platform: ${CMAKE_SYSTEM_NAME}")
@@ -256,17 +190,43 @@ if (CMAKE_SYSTEM_NAME MATCHES "Windows")
target_compile_definitions(mfkey64 PRIVATE HAVE_STRUCT_TIMESPEC)
endif()
add_executable(staticnested_1nt ${COMMON_FILES} staticnested_1nt.c)
target_include_directories(staticnested_1nt PRIVATE ${SRC_DIR})
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
target_compile_definitions(staticnested_1nt PRIVATE _GNU_SOURCE)
endif()
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
target_compile_definitions(staticnested_1nt PRIVATE HAVE_STRUCT_TIMESPEC)
endif()
add_executable(staticnested_2x1nt_rf08s ${COMMON_FILES} staticnested_2x1nt_rf08s.c)
target_include_directories(staticnested_2x1nt_rf08s PRIVATE ${SRC_DIR})
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
target_compile_definitions(staticnested_2x1nt_rf08s PRIVATE _GNU_SOURCE)
endif()
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
target_compile_definitions(staticnested_2x1nt_rf08s PRIVATE HAVE_STRUCT_TIMESPEC)
endif()
add_executable(staticnested_2x1nt_rf08s_1key ${COMMON_FILES} staticnested_2x1nt_rf08s_1key.c)
target_include_directories(staticnested_2x1nt_rf08s_1key PRIVATE ${SRC_DIR})
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
target_compile_definitions(staticnested_2x1nt_rf08s_1key PRIVATE _GNU_SOURCE)
endif()
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
target_compile_definitions(staticnested_2x1nt_rf08s_1key PRIVATE HAVE_STRUCT_TIMESPEC)
endif()
# --- hardnested Executable ---
add_executable(hardnested ${COMMON_FILES} ${HARDNESTED_SOURCES})
add_dependencies(hardnested liblzma_imported) # Ensure liblzma is built first
target_include_directories(hardnested PRIVATE
${SRC_DIR}
${HARDNESTED_RECOVERY_DIR}
${HARDNESTED_RECOVERY_DIR}/pm3
${HARDNESTED_RECOVERY_DIR}/hardnested
# liblzma include dir comes via INTERFACE property of liblzma_imported
${xz_SOURCE_DIR}/src/liblzma/api
)
target_compile_options(hardnested PRIVATE -Wall)
@@ -301,9 +261,5 @@ endif() # End Windows
target_link_libraries(hardnested PRIVATE
${LIBTHREAD} # Handles pthread correctly now for Linux, MSVC, MinGW
${LIBMATH} # Handles 'm' on Linux, empty on Windows
liblzma_imported # Link against the IMPORTED target name
liblzma
)
# Set the output directory for all executables at the end
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})
+56 -53
View File
@@ -42,19 +42,19 @@
#include "pm3/commonutil.h"
#include "pm3/util_posix.h"
#include "hardnested/tables.h"
#include <../../xz/src/liblzma/api/lzma.h>
#include <lzma.h>
#define NUM_CHECK_BITFLIPS_THREADS (num_CPUs())
#if defined(_MSC_VER)
#define NUM_CHECK_BITFLIPS_THREADS_ALLOC 128
#define NUM_CHECK_BITFLIPS_THREADS_ALLOC 128
#else
#define NUM_CHECK_BITFLIPS_THREADS_ALLOC (num_CPUs())
#define NUM_CHECK_BITFLIPS_THREADS_ALLOC (num_CPUs())
#endif
#define NUM_REDUCTION_WORKING_THREADS (num_CPUs())
#if defined(_MSC_VER)
#define NUM_REDUCTION_WORKING_THREADS_ALLOC 128
#define NUM_REDUCTION_WORKING_THREADS_ALLOC 128
#else
#define NUM_REDUCTION_WORKING_THREADS_ALLOC (num_CPUs())
#define NUM_REDUCTION_WORKING_THREADS_ALLOC (num_CPUs())
#endif
#define IGNORE_BITFLIP_THRESHOLD 0.99 // ignore bitflip arrays which have nearly only valid states
@@ -63,7 +63,8 @@
// possible sum property values
static uint16_t sums[NUM_SUMS] = {0, 32, 56, 64, 80, 96, 104, 112, 120, 128, 136, 144, 152, 160, 176, 192, 200, 224,
256};
256
};
// number of possible partial sum property values
#define NUM_PART_SUMS 9
@@ -104,9 +105,9 @@ static void get_SIMD_instruction_set(char *instruction_set) {
break;
#endif
#if defined(COMPILER_HAS_SIMD_NEON)
case SIMD_NEON:
strcpy(instruction_set, "NEON");
break;
case SIMD_NEON:
strcpy(instruction_set, "NEON");
break;
#endif
case SIMD_AUTO:
case SIMD_NONE:
@@ -121,7 +122,7 @@ static void print_progress_header(void) {
char instr_set[12] = "";
get_SIMD_instruction_set(instr_set);
snprintf(progress_text, sizeof(progress_text), "Start using "
_YELLOW_("%d")
_YELLOW_("%d")
" threads", num_CPUs());
PrintAndLogEx(INFO, "Hardnested attack starting...");
@@ -306,7 +307,7 @@ static void init_bitflip_bitarrays(void) {
qsort(all_effective_bitflip + num_1st_byte_effective_bitflips, num_all_effective_bitflips - num_1st_byte_effective_bitflips, sizeof(uint16_t), compare_count_bitflip_bitarrays);
char progress_text[80];
sprintf(progress_text, "Using %d precalculated bitflip state tables", num_all_effective_bitflips);
hardnested_print_progress(0, progress_text, (float) (1LL << 47), 0);
hardnested_print_progress(0, progress_text, (float)(1LL << 47), 0);
}
static void free_bitflip_bitarrays(void) {
@@ -440,8 +441,9 @@ static char failstr[250] = "";
#endif
static const float p_K0[NUM_SUMS] = { // the probability that a random nonce has a Sum Property K
0.0290, 0.0083, 0.0006, 0.0339, 0.0048, 0.0934, 0.0119, 0.0489, 0.0602, 0.4180, 0.0602, 0.0489, 0.0119, 0.0934,
0.0048, 0.0339, 0.0006, 0.0083, 0.0290};
0.0290, 0.0083, 0.0006, 0.0339, 0.0048, 0.0934, 0.0119, 0.0489, 0.0602, 0.4180, 0.0602, 0.0489, 0.0119, 0.0934,
0.0048, 0.0339, 0.0006, 0.0083, 0.0290
};
static float my_p_K[NUM_SUMS];
static const float *p_K;
@@ -641,7 +643,7 @@ static void update_allbitflips_array(void) {
if (nonces[i].all_bitflips_dirty[odd_even]) {
uint32_t old_count = num_all_bitflips_bitarray[odd_even];
num_all_bitflips_bitarray[odd_even] = count_bitarray_low20_AND(all_bitflips_bitarray[odd_even],
nonces[i].states_bitarray[odd_even]);
nonces[i].states_bitarray[odd_even]);
nonces[i].all_bitflips_dirty[odd_even] = false;
if (num_all_bitflips_bitarray[odd_even] != old_count) {
all_bitflips_bitarray_dirty[odd_even] = true;
@@ -658,7 +660,7 @@ estimated_num_states_part_sum_coarse(uint16_t part_sum_a0_idx, uint16_t part_sum
}
static uint32_t estimated_num_states_part_sum(uint8_t first_byte, uint16_t part_sum_a0_idx, uint16_t part_sum_a8_idx,
odd_even_t odd_even) {
odd_even_t odd_even) {
if (odd_even == ODD_STATE) {
return count_bitarray_AND3(part_sum_a0_bitarrays[odd_even][part_sum_a0_idx],
part_sum_a8_bitarrays[odd_even][part_sum_a8_idx],
@@ -746,7 +748,7 @@ static void update_sum_bitarrays(odd_even_t odd_even) {
}
for (uint16_t i = 0; i < 256; i++) {
nonces[i].num_states_bitarray[odd_even] = count_bitarray_AND(nonces[i].states_bitarray[odd_even],
all_bitflips_bitarray[odd_even]);
all_bitflips_bitarray[odd_even]);
}
for (uint8_t part_sum_a0 = 0; part_sum_a0 < NUM_PART_SUMS; part_sum_a0++) {
for (uint8_t part_sum_a8 = 0; part_sum_a8 < NUM_PART_SUMS; part_sum_a8++) {
@@ -807,10 +809,10 @@ static void update_expected_brute_force(uint8_t best_byte) {
nonces[best_byte].expected_num_brute_force = 0.0;
for (uint8_t i = 0; i < NUM_SUMS; i++) {
nonces[best_byte].expected_num_brute_force +=
nonces[best_byte].sum_a8_guess[i].prob * (float) nonces[best_byte].sum_a8_guess[i].num_states / 2.0;
nonces[best_byte].sum_a8_guess[i].prob * (float) nonces[best_byte].sum_a8_guess[i].num_states / 2.0;
prob_all_failed -= nonces[best_byte].sum_a8_guess[i].prob;
nonces[best_byte].expected_num_brute_force +=
prob_all_failed * (float) nonces[best_byte].sum_a8_guess[i].num_states / 2.0;
prob_all_failed * (float) nonces[best_byte].sum_a8_guess[i].num_states / 2.0;
}
return;
}
@@ -825,9 +827,9 @@ static float sort_best_first_bytes(void) {
nonces[i].expected_num_brute_force = 0.0;
for (uint8_t j = 0; j < NUM_SUMS; j++) {
nonces[i].sum_a8_guess[j].num_states = estimated_num_states_coarse(sums[first_byte_Sum],
sums[nonces[i].sum_a8_guess[j].sum_a8_idx]);
sums[nonces[i].sum_a8_guess[j].sum_a8_idx]);
nonces[i].expected_num_brute_force +=
nonces[i].sum_a8_guess[j].prob * (float) nonces[i].sum_a8_guess[j].num_states / 2.0;
nonces[i].sum_a8_guess[j].prob * (float) nonces[i].sum_a8_guess[j].num_states / 2.0;
prob_all_failed -= nonces[i].sum_a8_guess[j].prob;
nonces[i].expected_num_brute_force += prob_all_failed * (float) nonces[i].sum_a8_guess[j].num_states / 2.0;
}
@@ -844,7 +846,7 @@ static float sort_best_first_bytes(void) {
uint16_t first_byte = best_first_bytes[i];
for (uint8_t j = 0; j < NUM_SUMS && nonces[first_byte].sum_a8_guess[j].prob > 0.05; j++) {
nonces[first_byte].sum_a8_guess[j].num_states = estimated_num_states(first_byte, sums[first_byte_Sum],
sums[nonces[first_byte].sum_a8_guess[j].sum_a8_idx]);
sums[nonces[first_byte].sum_a8_guess[j].sum_a8_idx]);
}
// while (nonces[first_byte].sum_a8_guess[0].num_states == 0
// || nonces[first_byte].sum_a8_guess[1].num_states == 0
@@ -881,11 +883,11 @@ static float sort_best_first_bytes(void) {
nonces[first_byte].expected_num_brute_force = 0.0;
for (uint8_t j = 0; j < NUM_SUMS; j++) {
nonces[first_byte].expected_num_brute_force +=
nonces[first_byte].sum_a8_guess[j].prob * (float) nonces[first_byte].sum_a8_guess[j].num_states /
2.0;
nonces[first_byte].sum_a8_guess[j].prob * (float) nonces[first_byte].sum_a8_guess[j].num_states /
2.0;
prob_all_failed -= nonces[first_byte].sum_a8_guess[j].prob;
nonces[first_byte].expected_num_brute_force +=
prob_all_failed * (float) nonces[first_byte].sum_a8_guess[j].num_states / 2.0;
prob_all_failed * (float) nonces[first_byte].sum_a8_guess[j].num_states / 2.0;
}
}
@@ -915,13 +917,13 @@ static float update_reduction_rate(float last, bool init) {
for (uint16_t i = 0; i < QUEUE_LEN - 1; i++) {
if (init) {
queue[i] = (float) (1LL << 48);
queue[i] = (float)(1LL << 48);
} else {
queue[i] = queue[i + 1];
}
}
if (init) {
queue[QUEUE_LEN - 1] = (float) (1LL << 48);
queue[QUEUE_LEN - 1] = (float)(1LL << 48);
} else {
queue[QUEUE_LEN - 1] = last;
}
@@ -956,7 +958,7 @@ static bool shrink_key_space(float *brute_forces) {
PrintAndLogEx(INFO, "shrink_key_space() with stage = 0x%02x\n", hardnested_stage);
#endif
float brute_forces1 = check_smallest_bitflip_bitarrays();
float brute_forces2 = (float) (1LL << 47);
float brute_forces2 = (float)(1LL << 47);
if (hardnested_stage & CHECK_2ND_BYTES) {
brute_forces2 = sort_best_first_bytes();
}
@@ -1024,14 +1026,14 @@ __attribute__((force_align_arg_pointer))
for (uint16_t i = first_byte; i <= last_byte; i++) {
if (nonces[i].BitFlips[bitflip] == 0 && nonces[i].BitFlips[bitflip ^ 0x100] == 0 &&
nonces[i].first != NULL && nonces[i ^ (bitflip & 0xff)].first != NULL) {
nonces[i].first != NULL && nonces[i ^ (bitflip & 0xff)].first != NULL) {
uint8_t parity1 = (nonces[i].first->par_enc) >> 3; // parity of first byte
uint8_t parity2 =
(nonces[i ^ (bitflip & 0xff)].first->par_enc) >> 3; // parity of nonce with bits flipped
(nonces[i ^ (bitflip & 0xff)].first->par_enc) >> 3; // parity of nonce with bits flipped
if ((parity1 == parity2 && !(bitflip & 0x100)) // bitflip
|| (parity1 != parity2 && (bitflip & 0x100))) { // not bitflip
|| (parity1 != parity2 && (bitflip & 0x100))) { // not bitflip
nonces[i].BitFlips[bitflip] = 1;
@@ -1051,7 +1053,7 @@ __attribute__((force_align_arg_pointer))
}
}
((uint8_t *) args)[1] =
num_1st_byte_effective_bitflips - bitflip_idx - 1; // bitflips still to go in stage 1
num_1st_byte_effective_bitflips - bitflip_idx - 1; // bitflips still to go in stage 1
}
}
@@ -1059,7 +1061,7 @@ __attribute__((force_align_arg_pointer))
if (hardnested_stage & CHECK_2ND_BYTES) {
for (uint16_t bitflip_idx = num_1st_byte_effective_bitflips;
bitflip_idx < num_all_effective_bitflips; bitflip_idx++) {
bitflip_idx < num_all_effective_bitflips; bitflip_idx++) {
uint16_t bitflip = all_effective_bitflip[bitflip_idx];
if (time_budget && timeout()) {
#if defined (DEBUG_REDUCTION)
@@ -1077,7 +1079,7 @@ __attribute__((force_align_arg_pointer))
uint8_t parity1 = byte1->par_enc >> 2 & 0x01; // parity of 2nd byte
uint8_t parity2 = byte2->par_enc >> 2 & 0x01; // parity of 2nd byte with bits flipped
if ((parity1 == parity2 && !(bitflip & 0x100)) // bitflip
|| (parity1 != parity2 && (bitflip & 0x100))) { // not bitflip
|| (parity1 != parity2 && (bitflip & 0x100))) { // not bitflip
nonces[i].BitFlips[bitflip] = 1;
for (odd_even_t odd_even = EVEN_STATE; odd_even <= ODD_STATE; odd_even++) {
if (bitflip_bitarrays[odd_even][bitflip] != NULL) {
@@ -1154,13 +1156,13 @@ static void update_nonce_data(bool time_budget) {
static void apply_sum_a0(void) {
uint32_t old_count = num_all_bitflips_bitarray[EVEN_STATE];
num_all_bitflips_bitarray[EVEN_STATE] = count_bitarray_AND(all_bitflips_bitarray[EVEN_STATE],
sum_a0_bitarrays[EVEN_STATE][first_byte_Sum]);
sum_a0_bitarrays[EVEN_STATE][first_byte_Sum]);
if (num_all_bitflips_bitarray[EVEN_STATE] != old_count) {
all_bitflips_bitarray_dirty[EVEN_STATE] = true;
}
old_count = num_all_bitflips_bitarray[ODD_STATE];
num_all_bitflips_bitarray[ODD_STATE] = count_bitarray_AND(all_bitflips_bitarray[ODD_STATE],
sum_a0_bitarrays[ODD_STATE][first_byte_Sum]);
sum_a0_bitarrays[ODD_STATE][first_byte_Sum]);
if (num_all_bitflips_bitarray[ODD_STATE] != old_count) {
all_bitflips_bitarray_dirty[ODD_STATE] = true;
}
@@ -1275,14 +1277,14 @@ static inline bool invariant_holds(uint_fast8_t byte_diff, uint_fast32_t state1,
uint_fast8_t state_bit) {
uint_fast8_t j_1_bit_mask = 0x01 << (bit - 1);
uint_fast8_t bit_diff =
byte_diff & j_1_bit_mask; // difference of (j-1)th bit
byte_diff & j_1_bit_mask; // difference of (j-1)th bit
uint_fast8_t filter_diff =
filter(state1 >> (4 - state_bit)) ^ filter(state2 >> (4 - state_bit)); // difference in filter function
filter(state1 >> (4 - state_bit)) ^ filter(state2 >> (4 - state_bit)); // difference in filter function
uint_fast8_t mask_y12_y13 = (0xc0 >> state_bit);
uint_fast8_t state_bits_diff =
(state1 ^ state2) & mask_y12_y13; // difference in state bits 12 and 13
(state1 ^ state2) & mask_y12_y13; // difference in state bits 12 and 13
uint_fast8_t all_diff = evenparity8(
bit_diff ^ state_bits_diff ^ filter_diff); // use parity function to XOR all bits
bit_diff ^ state_bits_diff ^ filter_diff); // use parity function to XOR all bits
return !all_diff;
}
@@ -1290,12 +1292,12 @@ static inline bool invalid_state(uint_fast8_t byte_diff, uint_fast32_t state1, u
uint_fast8_t state_bit) {
uint_fast8_t j_bit_mask = (0x01 << bit);
uint_fast8_t bit_diff =
byte_diff & j_bit_mask; // difference of jth bit
byte_diff & j_bit_mask; // difference of jth bit
uint_fast8_t mask_y13_y16 = (0x48 >> state_bit);
uint_fast8_t state_bits_diff =
(state1 ^ state2) & mask_y13_y16; // difference in state bits 13 and 16
(state1 ^ state2) & mask_y13_y16; // difference in state bits 13 and 16
uint_fast8_t all_diff = evenparity8(
bit_diff ^ state_bits_diff); // use parity function to XOR all bits
bit_diff ^ state_bits_diff); // use parity function to XOR all bits
return all_diff;
}
@@ -1398,10 +1400,10 @@ static inline bool bitflips_match(uint8_t byte, uint32_t state, odd_even_t odd_e
#ifdef DEBUG_KEY_ELIMINATION
if (!quiet && known_target_key != -1 && state == test_state[odd_even]) {
PrintAndLogEx(INFO, "Initial state lists: "
_YELLOW_("%s")
_YELLOW_("%s")
" test state eliminated by bitflip property.", odd_even == EVEN_STATE ? "even" : "odd");
snprintf(failstr, sizeof(failstr), "Initial "
_YELLOW_("%s")
_YELLOW_("%s")
" byte Bitflip property", odd_even == EVEN_STATE ? "even" : "odd");
}
#endif
@@ -1417,7 +1419,8 @@ static uint_fast8_t reverse(uint_fast8_t b) {
static bool all_bitflips_match(uint8_t byte, uint32_t state, odd_even_t odd_even) {
uint32_t masks[2][8] = {{0x00fffff0, 0x00fffff8, 0x00fffff8, 0x00fffffc, 0x00fffffc, 0x00fffffe, 0x00fffffe, 0x00ffffff},
{0x00fffff0, 0x00fffff0, 0x00fffff8, 0x00fffff8, 0x00fffffc, 0x00fffffc, 0x00fffffe, 0x00fffffe}};
{0x00fffff0, 0x00fffff0, 0x00fffff8, 0x00fffff8, 0x00fffffc, 0x00fffffc, 0x00fffffe, 0x00fffffe}
};
for (uint16_t i = 1; i < 256; i++) {
uint_fast8_t bytes_diff = reverse(i); // start with most common bits
@@ -1431,7 +1434,7 @@ static bool all_bitflips_match(uint8_t byte, uint32_t state, odd_even_t odd_even
# ifdef DEBUG_KEY_ELIMINATION
if (bitflips_match(byte2, (state & mask) | remaining_bits, odd_even, true))
# else
if (bitflips_match(byte2, (state & mask) | remaining_bits, odd_even))
if (bitflips_match(byte2, (state & mask) | remaining_bits, odd_even))
# endif
{
found_match = true;
@@ -1601,15 +1604,15 @@ __attribute__((force_align_arg_pointer))
if (2 * r * (16 - 2 * s) + (16 - 2 * r) * 2 * s == sum_a8) {
pthread_mutex_lock(&book_of_work_mutex);
if (book_of_work[p][q][r][s] !=
TO_BE_DONE) { // this has been done or is currently been done by another thread. Look for some other work.
TO_BE_DONE) { // this has been done or is currently been done by another thread. Look for some other work.
pthread_mutex_unlock(&book_of_work_mutex);
continue;
}
pthread_mutex_lock(&statelist_cache_mutex);
if (sl_cache[p][r][ODD_STATE].cache_status == WORK_IN_PROGRESS ||
sl_cache[q][s][EVEN_STATE].cache_status ==
WORK_IN_PROGRESS) { // defer until not blocked by another thread.
sl_cache[q][s][EVEN_STATE].cache_status ==
WORK_IN_PROGRESS) { // defer until not blocked by another thread.
pthread_mutex_unlock(&statelist_cache_mutex);
pthread_mutex_unlock(&book_of_work_mutex);
there_might_be_more_work = true;
@@ -1860,7 +1863,7 @@ static void init_it_all(void) {
int
mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *trgkey,
bool nonce_file_read, bool nonce_file_write, bool slow, uint64_t *foundkey, char *filename, uint32_t uid, char* path) {
bool nonce_file_read, bool nonce_file_write, bool slow, uint64_t *foundkey, char *filename, uint32_t uid, char *path) {
char progress_text[80];
char instr_set[12] = {0};
@@ -1880,7 +1883,7 @@ mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo,
print_progress_header();
snprintf(progress_text, sizeof(progress_text), "Brute force benchmark: %1.0f million (2^%1.1f) keys/s",
brute_force_per_second / 1000000, log(brute_force_per_second) / log(2.0));
hardnested_print_progress(0, progress_text, (float) (1LL << 47), 0);
hardnested_print_progress(0, progress_text, (float)(1LL << 47), 0);
if (trgkey != NULL) {
known_target_key = bytes_to_num(trgkey, 6);
@@ -1968,10 +1971,10 @@ mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo,
return key_found;
}
char* run_hardnested(uint32_t uid, char* path) {
char *run_hardnested(uint32_t uid, char* path) {
uint64_t foundkey = 0;
if (mfnestedhard(0, 0, NULL, 0, 0, NULL, false, false, false, &foundkey, NULL, uid, path) == 1) {
char* keystr = malloc(14);
char *keystr = malloc(14);
snprintf(keystr, 14, "%012" PRIx64 ";", foundkey);
return keystr;
} else {

Some files were not shown because too many files have changed in this diff Show More