From 3f6f5ac9c15c3dba7b329755c66fbc4568114696 Mon Sep 17 00:00:00 2001 From: dxl <64101226@qq.com> Date: Wed, 2 Apr 2025 12:32:27 +0800 Subject: [PATCH 1/6] Hardnested nonces acquire implemented. --- CHANGELOG.md | 1 + firmware/application/src/app_cmd.c | 41 +++++++- firmware/application/src/data_cmd.h | 1 + .../src/rfid/reader/hf/mf1_toolbox.c | 97 +++++++++++++++++- .../src/rfid/reader/hf/mf1_toolbox.h | 3 + software/script/chameleon_cmd.py | 12 +++ software/script/chameleon_enum.py | 1 + software/script/hardnested_utils.py | 41 ++++++++ software/script/tests/test_hard_acquire.py | 98 +++++++++++++++++++ 9 files changed, 292 insertions(+), 3 deletions(-) create mode 100644 software/script/hardnested_utils.py create mode 100644 software/script/tests/test_hard_acquire.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 398b0b3..a81d7f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ 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 to acquire nonces for hardnested(Protocol doc need update) (@xianglin1998) - Added command to check keys of multiple sectors at once (@taichunmin) - Fixed unused target key type parameter for nested (@petepriority) - Skip already used items `hf mf elog --decrypt` (@p-l-) diff --git a/firmware/application/src/app_cmd.c b/firmware/application/src/app_cmd.c index cff94f1..072cb3f 100644 --- a/firmware/application/src/app_cmd.c +++ b/firmware/application/src/app_cmd.c @@ -363,6 +363,44 @@ 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_hardnested_nonces_acquire(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { + typedef struct { + uint8_t slow; + uint8_t type_known; + uint8_t block_known; + uint8_t key_known[6]; + uint8_t type_target; + uint8_t block_target; + } PACKED payload_t; + if (length != sizeof(payload_t)) { + 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. + uint8_t nonces[500] = { 0x00 }; + if (length < 11) { + 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 + ); + if (status != STATUS_HF_TAG_OK) { + return data_frame_make(cmd, status, 0, NULL); + } + return data_frame_make(cmd, status, nonces[0] * 4.5, (uint8_t *)(nonces + 1)); +} + static data_frame_tx_t *cmd_processor_mf1_read_one_block(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) { typedef struct { uint8_t type; @@ -1295,7 +1333,8 @@ static cmd_data_map_t m_data_cmd_map[] = { { DATA_CMD_HF14A_RAW, before_reader_run, cmd_processor_hf14a_raw, NULL }, { 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_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 }, diff --git a/firmware/application/src/data_cmd.h b/firmware/application/src/data_cmd.h index 6b0ff58..7d6d076 100644 --- a/firmware/application/src/data_cmd.h +++ b/firmware/application/src/data_cmd.h @@ -68,6 +68,7 @@ #define DATA_CMD_HF14A_RAW (2010) #define DATA_CMD_MF1_MANIPULATE_VALUE_BLOCK (2011) #define DATA_CMD_MF1_CHECK_KEYS_OF_SECTORS (2012) +#define DATA_CMD_MF1_HARDNESTED_ACQUIRE (2013) // // ****************************************************************** diff --git a/firmware/application/src/rfid/reader/hf/mf1_toolbox.c b/firmware/application/src/rfid/reader/hf/mf1_toolbox.c index 32e2bf4..6658af8 100644 --- a/firmware/application/src/rfid/reader/hf/mf1_toolbox.c +++ b/firmware/application/src/rfid/reader/hf/mf1_toolbox.c @@ -17,7 +17,7 @@ // The default delay of the antenna reset static uint32_t g_ant_reset_delay = 100; -// Label information used for global operations +// tag information used for this module. static picc_14a_tag_t m_tag_info; static picc_14a_tag_t *p_tag_info = &m_tag_info; @@ -1095,4 +1095,97 @@ uint16_t mf1_toolbox_check_keys_of_sectors ( } return STATUS_HF_TAG_OK; -} \ No newline at end of file +} + +/** +* @brief : HardNested random number acquisition implementation +* @param :slow : Is it a low-speed acquisition mode? Low-speed acquisition is suitable for some non-standard cards +* @param :keyKnown : The known secret key of the card +* @param :blkKnown : The sector to which the known secret key of the card belongs +* @param :typKnown : The type of the known secret key of the card, 0x60 (A secret key) or 0x61 (B secret key) +* @param :targetBlk : The target sector for nested attack +* @param :targetTyp : The target secret key type for nested attack +* @param :nonces : The buffer for storing random numbers +* @param :noncesMax : The upper limit of the random number buffer in bytes +* @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) { + struct Crypto1State mpcs = { 0, 0 }; + struct Crypto1State *pcs = &mpcs; + uint8_t answer[] = { 0x00, 0x00, 0x00, 0x00 }; + uint8_t parity[] = { 0x00, 0x00, 0x00, 0x00 }; + uint8_t nt_par_enc = 0; + uint8_t status = STATUS_HF_TAG_NO; + uint32_t cuid = 0; // cuid can be fixed when selecting card + uint16_t len = 0; + *num_nonces = 0; // The number of random numbers currently counted must be reset + bool tag_selected = false; + uint8_t err_count = 0; + + for (uint16_t i = 0; i <= noncesMax - 9;) { + // NRF_LOG_INFO("AcquireEncryptedNonces: %d\r\n", i); + if (tag_selected) { + mf1_toolbox_report_healthy(); + if (pcd_14a_reader_fast_select(p_tag_info) != STATUS_HF_TAG_OK) { + NRF_LOG_INFO("AcquireEncryptedNonces: Tag lost\r\n"); + if (++err_count >= 15) { + return STATUS_HF_TAG_NO; + } + continue; + } + // Slow mode, delay some time? + if (slow) { + bsp_delay_us(400); + } + // First auth + if (authex(pcs, cuid, blkKnown, typKnown, keyKnown, AUTH_FIRST, NULL) != STATUS_HF_TAG_OK) { + NRF_LOG_INFO("AcquireEncryptedNonces: Auth1 error\r\n"); + if (++err_count >= 15) { + return STATUS_MF_ERR_AUTH; + } + continue; + } + // Nested auth + len = send_cmd(pcs, AUTH_NESTED, targetTyp, targetBlk, &status, answer, parity, U8ARR_BIT_LEN(answer)); + if (len != 32) { + NRF_LOG_INFO("AcquireEncryptedNonces: Auth2 error len=%d\r\n", len); + if (++err_count >= 15) { + return STATUS_HF_ERR_STAT; + } + continue; + } + // Reset err count + err_count = 0; + // merge parity + uint8_t par_enc = 0; + par_enc |= parity[3] << 4; + par_enc |= parity[2] << 5; + par_enc |= parity[1] << 6; + par_enc |= parity[0] << 7; + // copy to buffer + *num_nonces = *num_nonces + 1; + if (*num_nonces % 2) { + memcpy(nonces + i, answer, 4); + nt_par_enc = par_enc & 0xf0; + } else { + nt_par_enc |= par_enc >> 4; + memcpy(nonces + i + 4, answer, 4); + memcpy(nonces + i + 8, &nt_par_enc, 1); + i += 9; + } + } else { + // scan the tag to fixed cuid. + status = pcd_14a_reader_scan_auto(p_tag_info); + if (status != STATUS_HF_TAG_OK) { + return STATUS_HF_TAG_NO; + } + cuid = get_u32_tag_uid(p_tag_info); + tag_selected = true; + } + } + + // OK! + return STATUS_HF_TAG_OK; +} diff --git a/firmware/application/src/rfid/reader/hf/mf1_toolbox.h b/firmware/application/src/rfid/reader/hf/mf1_toolbox.h index c26fe47..128e82e 100644 --- a/firmware/application/src/rfid/reader/hf/mf1_toolbox.h +++ b/firmware/application/src/rfid/reader/hf/mf1_toolbox.h @@ -118,6 +118,9 @@ uint16_t mf1_toolbox_check_keys_of_sectors ( 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); + #ifdef __cplusplus } #endif diff --git a/software/script/chameleon_cmd.py b/software/script/chameleon_cmd.py index 9c9f2c2..47c062f 100644 --- a/software/script/chameleon_cmd.py +++ b/software/script/chameleon_cmd.py @@ -358,6 +358,18 @@ 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): + """ + Collect the NT_ENC list for HardNested decryption + :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) + if resp.status == Status.HF_TAG_OK: + resp.parsed = resp.data # we can return the raw nonces bytes + return resp @expect_response(Status.LF_TAG_OK) def em410x_scan(self): diff --git a/software/script/chameleon_enum.py b/software/script/chameleon_enum.py index 3bb9868..ff5f693 100644 --- a/software/script/chameleon_enum.py +++ b/software/script/chameleon_enum.py @@ -69,6 +69,7 @@ class Command(enum.IntEnum): HF14A_RAW = 2010 MF1_MANIPULATE_VALUE_BLOCK = 2011 MF1_CHECK_KEYS_OF_SECTORS = 2012 + DATA_CMD_MF1_HARDNESTED_ACQUIRE = 2013 EM410X_SCAN = 3000 EM410X_WRITE_TO_T55XX = 3001 diff --git a/software/script/hardnested_utils.py b/software/script/hardnested_utils.py new file mode 100644 index 0000000..81bec0f --- /dev/null +++ b/software/script/hardnested_utils.py @@ -0,0 +1,41 @@ +hardnested_sums = [0, 32, 56, 64, 80, 96, 104, 112, 120, 128, 136, 144, 152, 160, 176, 192, 200, 224, 256] +hardnested_nonces_sum_map = [] +hardnested_first_byte_num = 0 +hardnested_first_byte_sum = 0 + + +def evenparity32(n): + """ + calc evenparity32, can replace to any fast native impl... + @param n - NT_ENC + """ + ret = 0 + for i in range(32): + if (n & (1 << i)) != 0: + ret += 1 + return ret % 2 + + +def check_nonce_unique_sum(nt, par): + """ + Check nt_enc is unique and calc first byte sum + Pay attention: thread unsafe!!! + @param nt - NT_ENC + @param par - parity of NT_ENC + """ + global hardnested_first_byte_sum, hardnested_first_byte_num + first_byte = nt >> 24 + if not hardnested_nonces_sum_map[first_byte]: + hardnested_first_byte_sum += evenparity32((nt & 0xff000000) | (par & 0x08)) + hardnested_nonces_sum_map[first_byte] = True + hardnested_first_byte_num += 1 + + +def reset(): + global hardnested_first_byte_sum, hardnested_first_byte_num, hardnested_nonces_sum_map + # clear the history + hardnested_nonces_sum_map = list() + for i in range(256): + hardnested_nonces_sum_map.append(False) + hardnested_first_byte_sum = 0 + hardnested_first_byte_num = 0 diff --git a/software/script/tests/test_hard_acquire.py b/software/script/tests/test_hard_acquire.py new file mode 100644 index 0000000..b1f0909 --- /dev/null +++ b/software/script/tests/test_hard_acquire.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +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("????????????") + block_known = 0x00 + type_known = 0x60 + block_target = 0x07 + type_target = 0x60 + + + # Before acquire start, we need to reset history + hardnested_utils.reset() + + # The nonces file format required by PM3: + # (4byte uid of card) - (block_target 1byte) - (type_target 1byte) - (nonces from device Nbytes) + + # ------------------------ open the device ------------------------ + try: + cml = ChameleonCom().open('com19') + except OpenFailException: + cml = ChameleonCom().open('/dev/ttyACM0') + cml_cmd = ChameleonCMD(cml) + + # ------------------------ append tag info ------------------------ + + resp = cml_cmd.hf14a_scan() + if resp is None or len(resp) == 0: + print("ISO14443-A Tag no found") + return + + uidbytes = bytearray.fromhex(resp['uid']) + uid_len = len(uidbytes) + if uid_len == 4: + nonces_buffer.extend(uidbytes[0: 4]) + if uid_len == 7: + nonces_buffer.extend(uidbytes[3: 7]) + if uid_len == 10: + nonces_buffer.extend(uidbytes[6: 10]) + + nonces_buffer.extend([block_target, type_target & 0x01]) + + # ------------------------ append nonces from device ------------------------ + + 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... + if acquire_datas is not None: + acquire_count += 1 + print(f"Acquire success, count: {acquire_count}") + else: + raise Exception(f"acquire failed") + # 2. check data + data_check_index = 0 + while data_check_index < len(acquire_datas): + # Memory Layout: nt_enc1(4byte) - nt_enc2(4byte) - par(1byte)... + # To integer + nt_enc1 = int.from_bytes(acquire_datas[data_check_index + 0: data_check_index + 0 + 4]) + nt_enc2 = int.from_bytes(acquire_datas[data_check_index + 4: data_check_index + 4 + 4]) + par_enc = acquire_datas[data_check_index + 8] + # check unique and sum + hardnested_utils.check_nonce_unique_sum(nt_enc1, par_enc >> 4) + hardnested_utils.check_nonce_unique_sum(nt_enc2, par_enc & 0x0F) + data_check_index += 9 # The two ciphertext random numbers have a total of 8 bytes, and the parity bits corresponding to the two ciphertext random numbers occupy one byte + # 3. store data + nonces_buffer.extend(acquire_datas) + # 4. After collecting 256 possible different groups, determine whether the collected data is summed correctly. If not, it may not be an EV1 tag. + if hardnested_utils.hardnested_first_byte_num == 256: + got_match = False + for i in range(len(hardnested_utils.hardnested_sums)): + if hardnested_utils.hardnested_first_byte_sum == hardnested_utils.hardnested_sums[i]: + got_match = True # Sum matches successfully, and we can try to decrypt it next. + break + if got_match: + print(f"Acquire finish, save to file [nonces.bin], size is {len(nonces_buffer)}bytes") + break + else: + print( + f"hardnested_first_byte_num exceeds the limit but got_match is false: {hardnested_utils.hardnested_first_byte_sum}") + else: + continue # Continue acquire + + # ------------------------ write nonces to bin ------------------------ + with open("nonces.bin", mode="wb+") as fd: + fd.write(nonces_buffer) + + # 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... From 8bb60d15f5a7ac8f01ff63842e68da6adcbcac4b Mon Sep 17 00:00:00 2001 From: dxl <64101226@qq.com> Date: Wed, 2 Apr 2025 12:38:42 +0800 Subject: [PATCH 2/6] Fix workflow no working. updated to v4 See: https://github.com/actions/upload-artifact/issues/635 --- .github/workflows/build_client.yml | 4 ++-- .github/workflows/build_firmware.yml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_client.yml b/.github/workflows/build_client.yml index 2034820..973bb21 100644 --- a/.github/workflows/build_client.yml +++ b/.github/workflows/build_client.yml @@ -61,14 +61,14 @@ jobs: cd software pyinstaller pyinstaller.spec - name: Upload built client - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: client-${{ matrix.name }} path: software/dist/* - name: Zip up client for release run: ${{ matrix.bundle_command }} - name: Upload release artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release-artifacts path: client-${{ matrix.name }}.zip diff --git a/.github/workflows/build_firmware.yml b/.github/workflows/build_firmware.yml index 8639bce..8d87fd1 100644 --- a/.github/workflows/build_firmware.yml +++ b/.github/workflows/build_firmware.yml @@ -64,7 +64,7 @@ jobs: run: | docker run --rm -v ${PWD}:/workdir -e CURRENT_DEVICE_TYPE=${{ matrix.device_type }} ghcr.io/${repo,,}-fw-builder@${{ needs.build_fw_builder.outputs.image_hash }} firmware/build.sh - name: Upload built binaries - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.device_type }}-firmware path: firmware/objects/*.hex @@ -76,17 +76,17 @@ jobs: unzip firmware/objects/${{ matrix.device_type }}-dfu-app.zip -d firmware/objects/${{ matrix.device_type }}-dfu-app unzip firmware/objects/${{ matrix.device_type }}-dfu-full.zip -d firmware/objects/${{ matrix.device_type }}-dfu-full - name: Upload dfu app image - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.device_type }}-dfu-app path: firmware/objects/${{ matrix.device_type }}-dfu-app/* - name: Upload dfu full image - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.device_type }}-dfu-full path: firmware/objects/${{ matrix.device_type }}-dfu-full/* - name: Upload release artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: release-artifacts path: firmware/objects/*.zip From dd688e7b3f3355c50a269686d77cc39dfeb78a51 Mon Sep 17 00:00:00 2001 From: dxl <64101226@qq.com> Date: Wed, 2 Apr 2025 13:19:35 +0800 Subject: [PATCH 3/6] Fix workflow artifact conflict 409-Error By: https://github.com/ChameleonUltra/ChameleonUltra/commit/18d725b9c3156f623d1abaeef5e4b34d5becbdbd --- .github/workflows/build_client.yml | 2 +- .github/workflows/build_firmware.yml | 2 +- .github/workflows/on_push.yml | 10 ++++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_client.yml b/.github/workflows/build_client.yml index 973bb21..792b21a 100644 --- a/.github/workflows/build_client.yml +++ b/.github/workflows/build_client.yml @@ -70,5 +70,5 @@ jobs: - name: Upload release artifacts uses: actions/upload-artifact@v4 with: - name: release-artifacts + name: release-artifacts-${{ matrix.name }} path: client-${{ matrix.name }}.zip diff --git a/.github/workflows/build_firmware.yml b/.github/workflows/build_firmware.yml index 8d87fd1..43483e2 100644 --- a/.github/workflows/build_firmware.yml +++ b/.github/workflows/build_firmware.yml @@ -88,5 +88,5 @@ jobs: - name: Upload release artifacts uses: actions/upload-artifact@v4 with: - name: release-artifacts + name: release-artifacts-${{ matrix.device_type }} path: firmware/objects/*.zip diff --git a/.github/workflows/on_push.yml b/.github/workflows/on_push.yml index 08c228e..1f41f3a 100644 --- a/.github/workflows/on_push.yml +++ b/.github/workflows/on_push.yml @@ -26,9 +26,10 @@ jobs: - name: Check out the repo uses: actions/checkout@v3 - name: Download release artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: - name: release-artifacts + name: release-artifacts-* + merge-multiple: true path: release-artifacts - name: Upload to dev release uses: softprops/action-gh-release@v1 @@ -62,9 +63,10 @@ jobs: - name: Check out the repo uses: actions/checkout@v3 - name: Download release artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: - name: release-artifacts + name: release-artifacts-* + merge-multiple: true path: release-artifacts - name: Upload to tagged release uses: softprops/action-gh-release@v1 From 5784966c3a4174eecab95f38f87a5abc9b76c9b0 Mon Sep 17 00:00:00 2001 From: dxl <64101226@qq.com> Date: Wed, 2 Apr 2025 13:23:53 +0800 Subject: [PATCH 4/6] cmake_minimum_required from 3.1 updated to 3.5 --- software/src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/software/src/CMakeLists.txt b/software/src/CMakeLists.txt index 7f64487..73f97d2 100644 --- a/software/src/CMakeLists.txt +++ b/software/src/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.1) +cmake_minimum_required (VERSION 3.5) project (mifare C) From e4648b593d9daa6597f2f6f0865e3a211b2a1319 Mon Sep 17 00:00:00 2001 From: dxl <64101226@qq.com> Date: Wed, 2 Apr 2025 13:43:21 +0800 Subject: [PATCH 5/6] fix workflow download-artifact using 'name' instead of 'pattern' resulted in non working. --- .github/workflows/on_push.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/on_push.yml b/.github/workflows/on_push.yml index 1f41f3a..4e7a13e 100644 --- a/.github/workflows/on_push.yml +++ b/.github/workflows/on_push.yml @@ -28,7 +28,7 @@ jobs: - name: Download release artifacts uses: actions/download-artifact@v4 with: - name: release-artifacts-* + pattern: release-artifacts-* merge-multiple: true path: release-artifacts - name: Upload to dev release @@ -65,7 +65,7 @@ jobs: - name: Download release artifacts uses: actions/download-artifact@v4 with: - name: release-artifacts-* + pattern: release-artifacts-* merge-multiple: true path: release-artifacts - name: Upload to tagged release From 63bd0cb22e57afd97b6c160e40fa8e60fef9d7e8 Mon Sep 17 00:00:00 2001 From: Niel Nielsen Date: Thu, 3 Apr 2025 13:55:38 +0200 Subject: [PATCH 6/6] Update chameleon_cli_unit.py Defined a working function load_key_file(import_key, keys) for command hf mf fchk --key function load_dic_file(import_dic, keys) is empty for now, to prevent an error when executing hf mf fchk --dic --- software/script/chameleon_cli_unit.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/software/script/chameleon_cli_unit.py b/software/script/chameleon_cli_unit.py index 6bcf9e9..fc58144 100644 --- a/software/script/chameleon_cli_unit.py +++ b/software/script/chameleon_cli_unit.py @@ -42,7 +42,18 @@ type_id_SAK_dict = {0x00: "MIFARE Ultralight Classic/C/EV1/Nano | NTAG 2xx", default_cwd = Path.cwd() / Path(__file__).with_name("bin") +def load_key_file(import_key, keys): + """ + Load key file and append its content to the provided set of keys. + Each key is expected to be on a new line in the file. + """ + with open(import_key.name, 'rb') as file: + keys.update(line.encode('utf-8') for line in file.read().decode('utf-8').splitlines()) + return keys +def load_dic_file(import_dic, keys): + return keys + def check_tools(): tools = ['staticnested', 'nested', 'darkside', 'mfkey32v2'] if sys.platform == "win32":