mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-05-12 11:22:59 -07:00
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:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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, §ors_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 },
|
||||
|
||||
@@ -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)
|
||||
|
||||
//
|
||||
// ******************************************************************
|
||||
|
||||
@@ -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], §or_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,
|
||||
|
||||
@@ -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
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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})
|
||||
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user