mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-03-30 19:05:47 +00:00
612 lines
23 KiB
Python
612 lines
23 KiB
Python
"""
|
||
DES Talk - A Proxmark3 DESFire Communication Tool
|
||
|
||
Copyright (C) 2025 Trigat
|
||
|
||
Description:
|
||
This script simplifies the creation and deletion of
|
||
DESFire applications and files. It supports USB, Bluetooth,
|
||
and Termux connections and has been tested on Android and Linux.
|
||
|
||
Note: Modify TCP_PORT below if using TCP app with Android or Termux
|
||
|
||
License: GNU General Public License v3.0
|
||
|
||
This program is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation, either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
Full license text: <https://www.gnu.org/licenses/gpl-3.0.html>
|
||
"""
|
||
|
||
|
||
import subprocess
|
||
import time
|
||
import sys
|
||
import os
|
||
import re
|
||
|
||
try:
|
||
import pm3 # Used when inside the pm3 environment
|
||
PM3_AVAILABLE = True
|
||
p = pm3.pm3()
|
||
except ImportError:
|
||
PM3_AVAILABLE = False # Use subprocess instead
|
||
|
||
# SPECIFY PORT IF USING TCP APP WITH ANDROID OR TERMUX
|
||
TCP_PORT = 4444
|
||
|
||
def detect_proxmark_device():
|
||
|
||
# Detect the Proxmark3 connection type (USB, Bluetooth, or TCP).
|
||
try:
|
||
# Try running `pm3 --list` to detect available Proxmark devices
|
||
result = subprocess.run(["pm3", "--list"], capture_output=True, text=True, check=True)
|
||
lines = result.stdout.splitlines()
|
||
|
||
for line in lines:
|
||
if ":" in line: # Expected format: "1: /dev/ttyACM0" or "1: bt:xx:xx:xx:xx:xx"
|
||
device = line.split(": ", 1)[1].strip()
|
||
print(f"✅ Using Proxmark device: {device}")
|
||
return device
|
||
except Exception:
|
||
print("⚠️ `pm3 --list` failed, falling back to manual detection.")
|
||
|
||
# If `pm3 --list` doesn't work, check manually (Android or fallback mode)
|
||
usb_devices = ["/dev/ttyACM0", "/dev/ttyACM1", "/dev/ttyUSB0", "/dev/ttyUSB1"]
|
||
for dev in usb_devices:
|
||
if os.path.exists(dev):
|
||
print(f"✅ Using USB device: {dev}")
|
||
return dev
|
||
|
||
# Default to TCP mode for Android Termux
|
||
print(f"⚠️ No USB or Bluetooth device found, defaulting to TCP (localhost:{TCP_PORT})")
|
||
return f"tcp:localhost:{TCP_PORT}"
|
||
|
||
def send_proxmark_command(command):
|
||
|
||
if PM3_AVAILABLE:
|
||
p.console(command)
|
||
return p.grabbed_output.strip()
|
||
|
||
else:
|
||
full_command = f"{command}\n"
|
||
host_device = detect_proxmark_device()
|
||
|
||
process = subprocess.Popen(
|
||
["proxmark3", "-p", host_device],
|
||
stdin=subprocess.PIPE,
|
||
stdout=subprocess.PIPE,
|
||
stderr=subprocess.PIPE,
|
||
text=True
|
||
)
|
||
|
||
output, error = process.communicate(full_command)
|
||
|
||
time.sleep(0.2) # Small delay to let Proxmark process fully
|
||
|
||
# Combine stdout and stderr
|
||
response_lines = (output or "").splitlines() + (error or "").splitlines()
|
||
|
||
# Remove the harmless "STDIN unexpected end" message
|
||
filtered_response = "\n".join(line for line in response_lines if "STDIN unexpected end" not in line)
|
||
|
||
return filtered_response.strip()
|
||
|
||
def authenticate_and_menu():
|
||
|
||
com_mode = input("Enter communication mode (PLAIN, MAC, ENCRYPT) (Default: PLAIN): ").strip() or "plain"
|
||
key_type = input("Enter key type (DES, 2TDEA, 3TDEA, AES): ").strip()
|
||
key = input("Enter 8, 16, or 24-byte hex key (no spaces): ").strip()
|
||
|
||
# Authenticate
|
||
auth_command = f"hf mfdes auth -t {key_type} -k {key} -m {com_mode}"
|
||
auth_response = send_proxmark_command(auth_command)
|
||
print(auth_response)
|
||
# print("DEBUG: Raw Proxmark response:\n", repr(auth_response))
|
||
|
||
# Check for Proxmark failure messages
|
||
if "error" in auth_response.lower() or "must have" in auth_response.lower():
|
||
print("❌ Authentication failed. Check your connection, mode, key type, and key.")
|
||
return
|
||
|
||
while True:
|
||
# Get AIDs
|
||
aids_command = f"hf mfdes getaids -n 0 -t {key_type} -k {key} -m {com_mode}"
|
||
aids_response = send_proxmark_command(aids_command)
|
||
|
||
# Check for communication mode errors
|
||
com_mode_error_match = re.search(r"Wrong communication mode", aids_response)
|
||
crc_error_match = re.search(r"CRC32 error", aids_response)
|
||
if com_mode_error_match or crc_error_match:
|
||
print("❌ Incorrect communication mode.\n")
|
||
return
|
||
|
||
print(aids_response)
|
||
|
||
# Regex to match valid 6-character hex AIDs
|
||
hex_pattern = re.compile(r"\b[0-9A-Fa-f]{6}\b")
|
||
|
||
aids = []
|
||
for line in aids_response.split("\n"):
|
||
if "PM3 UART serial baudrate" in line:
|
||
continue
|
||
match = hex_pattern.search(line)
|
||
if match:
|
||
aids.append(match.group(0))
|
||
|
||
if aids: # Check if there are any AIDs
|
||
print("\nAvailable AIDs:")
|
||
for i, aid in enumerate(aids):
|
||
print(f"{i + 1}. {aid}")
|
||
else:
|
||
print("\n❌ No AID found on the card.")
|
||
|
||
print("\nChoose an option:")
|
||
print("1. Select an AID")
|
||
print("2. Create a new AID")
|
||
print("3. Delete an AID")
|
||
print("4. Format PICC")
|
||
print("5. Show free memory")
|
||
print("6. Change Master and AID keys")
|
||
print("7. Exit")
|
||
|
||
choice = input("Enter your choice: ").strip()
|
||
|
||
if choice == "1":
|
||
selected_index = int(input("Select an available AID number (e.g., 1, 2, or 3): ")) - 1
|
||
if selected_index < 0 or selected_index >= len(aids):
|
||
print("\nInvalid selection.")
|
||
continue
|
||
|
||
selected_aid = aids[selected_index]
|
||
print(f"\nSelecting AID: {selected_aid}")
|
||
|
||
select_command = f"hf mfdes selectapp --aid {selected_aid} -t {key_type} -k {key} -m {com_mode}"
|
||
select_response = send_proxmark_command(select_command)
|
||
print(select_response)
|
||
|
||
# Retrieve AID key 0
|
||
aid_key_type = input(f"Enter AID encryption algorithm (DES, 2TDEA, 3TDEA, AES) (Default: {key_type.upper()}): ").strip() or key_type
|
||
aid_key = input(f"Enter AID key (Default: {key}): ").strip() or key
|
||
|
||
aid_file_menu(selected_aid, key_type, key, com_mode, aid_key_type, aid_key)
|
||
|
||
elif choice == "2":
|
||
create_aid(key_type, key, com_mode)
|
||
|
||
elif choice == "3":
|
||
delete_aid(key_type, key, com_mode)
|
||
|
||
elif choice == "4":
|
||
format_picc(key_type, key, com_mode)
|
||
|
||
elif choice == "5":
|
||
free_memory(key_type, key, com_mode)
|
||
|
||
elif choice == "6":
|
||
change_key(key_type, key, com_mode)
|
||
|
||
elif choice == "7":
|
||
print("Exiting...")
|
||
break
|
||
else:
|
||
print("Invalid choice, please try again.")
|
||
|
||
def aid_file_menu(selected_aid, key_type, key, com_mode, aid_key_type, aid_key):
|
||
|
||
while True:
|
||
print(f"\n[ AID {selected_aid} is open ]")
|
||
print("\nChoose an operation:")
|
||
print("1. List Files")
|
||
print("2. Read a File")
|
||
print("3. Create a File")
|
||
print("4. Write to a File")
|
||
print("5. Edit File Restrictions")
|
||
print("6. Delete a File")
|
||
print("7. Back")
|
||
|
||
choice = input("Enter your choice: ").strip()
|
||
|
||
if choice == "1":
|
||
list_files(selected_aid, key_type, key, com_mode, aid_key_type, aid_key)
|
||
elif choice == "2":
|
||
read_file(selected_aid, key_type, key, com_mode, aid_key_type, aid_key)
|
||
elif choice == "3":
|
||
create_file(selected_aid, key_type, key, com_mode, aid_key_type, aid_key)
|
||
elif choice == "4":
|
||
write_to_file(selected_aid, key_type, key, com_mode, aid_key_type, aid_key)
|
||
elif choice == "5":
|
||
edit_file_restriction(selected_aid, key_type, key, com_mode, aid_key_type, aid_key)
|
||
elif choice == "6":
|
||
delete_file(selected_aid, key_type, key, com_mode, aid_key_type, aid_key)
|
||
elif choice == "7":
|
||
print("Returning to AID selection...")
|
||
break
|
||
else:
|
||
print("Invalid choice, please try again.")
|
||
|
||
def create_aid(key_type, key, com_mode):
|
||
|
||
aid = input("Enter new AID (6 hex characters, e.g., 112233): ").strip()
|
||
iso_fid = input("Enter ISO File ID (4 hex characters, e.g., 1234): ").strip()
|
||
dstalgo = input(f"Enter encryption algorithm (DES, 2TDEA, 3TDEA, AES) (Default: {key_type.upper()}): ").strip() or key_type
|
||
create_command = f"hf mfdes createapp -n 0 --aid {aid} --fid {iso_fid} --dstalgo {dstalgo} -t {key_type} -k {key} -m {com_mode} -a"
|
||
response = send_proxmark_command(create_command)
|
||
print(response)
|
||
print("\n⚠️ DESFire assigns all-zero keys to new applications by default. Keys can be modified via the main menu.\n")
|
||
|
||
def delete_aid(key_type, key, com_mode):
|
||
|
||
aid = input("Enter AID to delete (6 hex characters): ").strip()
|
||
delete_command = f"hf mfdes deleteapp --aid {aid} -n 0 -t {key_type} -k {key} -m {com_mode}"
|
||
response = send_proxmark_command(delete_command)
|
||
print(response)
|
||
|
||
def format_picc(key_type, key, com_mode):
|
||
|
||
confirm = input("Are you sure you want to format the PICC? This will erase all data. (y/n): ").strip().lower()
|
||
|
||
if confirm == "y":
|
||
format_command = f"hf mfdes formatpicc -t {key_type} -k {key} -m {com_mode} -v"
|
||
response = send_proxmark_command(format_command)
|
||
print(response)
|
||
elif confirm == "n":
|
||
print("Formatting cancelled.")
|
||
else:
|
||
print("Invalid input. Please enter 'y' or 'n'.")
|
||
|
||
def free_memory(key_type, key, com_mode):
|
||
|
||
memory_command = f"hf mfdes freemem -t {key_type} -k {key} -m {com_mode}"
|
||
response = send_proxmark_command(memory_command)
|
||
|
||
for line in response.splitlines():
|
||
if "Free memory" in line:
|
||
print(f"\n✅ {line}")
|
||
return
|
||
|
||
print("❌ Unable to retrieve free memory information.")
|
||
|
||
def change_key(key_type, key, com_mode):
|
||
|
||
print("\nChange Key - Choose Target:")
|
||
print("1. PICC (Card Master Key)")
|
||
print("2. Application Key")
|
||
|
||
target = input("Change key for (1/2)? (Default: 1): ").strip() or "1"
|
||
aid = ""
|
||
|
||
if target == "2":
|
||
aid = input("Enter 6-digit AID (e.g., 010203): ").strip()
|
||
|
||
print("\n!! Verify and securely store the new key !!")
|
||
print("Key length guide:")
|
||
print(" DES : 8 bytes (16 hex chars)")
|
||
print(" 2TDEA : 16 bytes (32 hex chars)")
|
||
print(" 3TDEA : 24 bytes (48 hex chars)")
|
||
print(" AES : 16 bytes (32 hex chars)")
|
||
|
||
newalgo = input(f"Enter new key encryption algorithm (DES, 2TDEA, 3TDEA, AES) "
|
||
f"(Default: {key_type.upper()}): ").strip() or key_type
|
||
newkey = input(f"Enter new 8, 16, or 24-byte hex key (no spaces) (Default: {key}): ").strip() or key
|
||
|
||
confirm = input("Are you sure you want to change the key? (Key 0) (y or n): ").strip().lower()
|
||
|
||
if confirm == "y":
|
||
changekey_command = f"hf mfdes changekey -n 0 -t {key_type} -k {key} -m {com_mode} " \
|
||
f"--newalgo {newalgo} --newkey {newkey} --newver 00 -v"
|
||
if aid:
|
||
app_key_type = input(f"Enter original application encryption algorithm (DES, 2TDEA, 3TDEA, AES) "
|
||
f"(Default: DES): ").strip() or "DES"
|
||
app_key = input(f"Enter original application key "
|
||
f"(Default: 0000000000000000): ").strip() or "0000000000000000"
|
||
changekey_command = f"hf mfdes changekey -n 0 -t {app_key_type} -k {app_key} -m {com_mode} " \
|
||
f"--newalgo {newalgo} --newkey {newkey} --newver 00 --aid {aid} -v"
|
||
|
||
response = send_proxmark_command(changekey_command)
|
||
print(response)
|
||
print("\n⚠️ Reauthenticate with the master key.")
|
||
sys.exit()
|
||
|
||
elif confirm == "n":
|
||
print("Cancelled.")
|
||
else:
|
||
print("Invalid input. Please enter 'y' or 'n'.")
|
||
|
||
def list_files(aid, key_type, key, com_mode, aid_key_type, aid_key):
|
||
|
||
print("\nFetching file list...")
|
||
command = f"hf mfdes getfileids --aid {aid} -t {aid_key_type} -k {aid_key} -m {com_mode}"
|
||
response = send_proxmark_command(command)
|
||
|
||
# Extract file IDs by looking for "File ID:" regex
|
||
file_ids = []
|
||
for line in response.splitlines():
|
||
match = re.search(r"File ID:\s*([0-9A-Fa-f]{2})", line)
|
||
if match:
|
||
file_ids.append(match.group(1))
|
||
|
||
if file_ids:
|
||
print("\nAvailable File IDs:")
|
||
for i, file_id in enumerate(file_ids, 1):
|
||
print(f"{i}. {file_id}")
|
||
return file_ids
|
||
else:
|
||
print("No files found in this AID.\n")
|
||
print(response)
|
||
return []
|
||
|
||
def read_file(aid, key_type, key, com_mode, aid_key_type, aid_key):
|
||
|
||
file_id = input("Enter file ID to read: ").strip()
|
||
|
||
# Get offset and ensure it's a 3-byte hex value (default: 000000)
|
||
offset_input = input("Enter offset (default 0): ").strip() or "0"
|
||
offset_hex = format(int(offset_input), '06X') # Convert to 3-byte hex
|
||
|
||
# Get length and ensure it's a 3-byte hex value (default: 000000 for full read)
|
||
length_input = input("Enter length to read (e.g., 16 for 16 bytes, 64 for 64 bytes, default full read): ").strip() or "0"
|
||
length_hex = format(int(length_input), '06X') # Convert to 3-byte hex
|
||
|
||
text_only = input("Show only human-readable text? (y/N): ").strip().lower() == "y"
|
||
|
||
read_command = f"hf mfdes read --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} " \
|
||
f"--offset {offset_hex} --length {length_hex} -m {com_mode}"
|
||
response = send_proxmark_command(read_command)
|
||
|
||
if "authenticate error" in response.lower():
|
||
print("\n❌ Authentication Error")
|
||
return None
|
||
|
||
if text_only:
|
||
all_chars = []
|
||
for line in response.splitlines():
|
||
# Regex looks for hex column. e.g. | 65 20 70 69 7A ... |
|
||
m = re.search(r"\|\s*([0-9A-Fa-f\s]+)\s*\|", line)
|
||
if not m:
|
||
continue
|
||
|
||
# Split hex into individual bytes
|
||
hex_blob = m.group(1).strip()
|
||
for token in hex_blob.split():
|
||
try:
|
||
b = int(token, 16) # Convert each byte or token into a number
|
||
except ValueError:
|
||
continue
|
||
if b == 0: # Stop at NULL
|
||
break
|
||
# Check if byte falls in printable ASCII range
|
||
all_chars.append(chr(b) if 0x20 <= b <= 0x7E else '.')
|
||
|
||
if all_chars:
|
||
text = "".join(all_chars)
|
||
print("\nPlain text:\n")
|
||
print(text)
|
||
else:
|
||
print("\nNo printable text found in the response.")
|
||
|
||
return response
|
||
else:
|
||
print("\nFile Data:\n")
|
||
for line in response.splitlines():
|
||
if re.search(r"\| [0-9A-Fa-f]{2} .* \|", line):
|
||
print(line)
|
||
return response
|
||
|
||
def create_file(aid, key_type, key, com_mode, aid_key_type, aid_key):
|
||
|
||
file_id = input("Enter file ID (2 hex characters, e.g., 01, 02): ").strip()
|
||
# Prompt for file size in KB, allowing for decimal values like 0.2KB
|
||
file_size_kb_input = input("Enter file size in KB (e.g., .2 for 0.2KB, 1 for 1KB, 4 for 4KB, 16 for 16KB): ").strip()
|
||
# Prefixes "00" to file_id for ISO file ID
|
||
iso_file_id = f"00{file_id}"
|
||
|
||
try:
|
||
# Convert the input to a float
|
||
file_size_kb = float(file_size_kb_input)
|
||
|
||
if file_size_kb <= 0:
|
||
raise ValueError("File size must be greater than 0.")
|
||
|
||
# Convert KB to bytes (1 KB = 1024 bytes)
|
||
file_size_bytes = int(file_size_kb * 1024)
|
||
|
||
if file_size_bytes < 3:
|
||
print("⚠️ File size is too small. Setting to minimum size of 3 bytes.")
|
||
file_size_bytes = 3
|
||
|
||
# Convert bytes to hexadecimal
|
||
file_size_hex = format(file_size_bytes, 'X').upper().zfill(6)
|
||
|
||
print(f"File size in bytes: {file_size_bytes} bytes")
|
||
print(f"File size in hex: {file_size_hex}")
|
||
|
||
except ValueError as e:
|
||
print(f"⚠️Invalid file size: {e}")
|
||
return
|
||
|
||
create_command = f"hf mfdes createfile --aid {aid} --fid {file_id} --isofid {iso_file_id} " \
|
||
f"--size {file_size_hex} -t {aid_key_type} -k {aid_key} -m {com_mode}"
|
||
response = send_proxmark_command(create_command)
|
||
print(response)
|
||
|
||
def read_long_input(prompt=""):
|
||
|
||
"""
|
||
Read lengthy user input on POSIX systems.
|
||
|
||
Linux terminals normally run in canonical mode, where read() is buffered
|
||
and limited to ~4096 bytes. To support very long input (large hex strings for DESFire),
|
||
ICANON is temporarily disabled so input is passed through immediately without the
|
||
kernel line buffer limit.
|
||
|
||
Windows doesn't use termios, so this function transparently falls back to normal
|
||
input() on that platform.
|
||
"""
|
||
if os.name == "posix":
|
||
try:
|
||
import termios
|
||
except Exception:
|
||
return input(prompt)
|
||
|
||
fd = sys.stdin.fileno()
|
||
old_settings = termios.tcgetattr(fd) # Save current terminal settings
|
||
new_settings = termios.tcgetattr(fd)
|
||
new_settings[3] &= ~termios.ICANON # Disable canonical mode
|
||
try:
|
||
termios.tcsetattr(fd, termios.TCSANOW, new_settings)
|
||
return input(prompt)
|
||
finally:
|
||
# Restore original settings
|
||
termios.tcsetattr(fd, termios.TCSANOW, old_settings)
|
||
else:
|
||
return input(prompt)
|
||
|
||
def write_to_file(aid, key_type, key, com_mode, aid_key_type, aid_key):
|
||
|
||
file_id = input("Enter file ID to write to: ").strip()
|
||
|
||
file_size_command = (
|
||
f"hf mfdes getfilesettings --aid {aid} --fid {file_id} "
|
||
f"-t {aid_key_type} -k {aid_key} -m {com_mode}"
|
||
)
|
||
response = send_proxmark_command(file_size_command)
|
||
|
||
if "authenticate error" in response.lower():
|
||
print("\n❌ Authentication Error")
|
||
return None
|
||
|
||
file_size_match = re.search(
|
||
r"File size \(bytes\)... (\d+) / 0x([0-9A-Fa-f]+)", response
|
||
)
|
||
if not file_size_match:
|
||
print("❌ Unable to determine file size.")
|
||
return
|
||
|
||
file_size = int(file_size_match.group(1))
|
||
print(f"✅ File size detected: {file_size} bytes")
|
||
|
||
while True:
|
||
data_format = input("Enter data format (1 = plain text, 2 = hex): ").strip()
|
||
|
||
if data_format == "1":
|
||
write_data = read_long_input(
|
||
f"Enter text to write (up to {file_size} bytes): "
|
||
).strip()
|
||
write_data_hex = write_data.encode().hex().upper()
|
||
if len(write_data) > file_size:
|
||
print(f"❌ Data exceeds {file_size} bytes. Try again.")
|
||
continue
|
||
break
|
||
|
||
elif data_format == "2":
|
||
write_data_hex = read_long_input(
|
||
f"Enter hex data to write (up to {file_size * 2} hex chars): "
|
||
).strip()
|
||
|
||
if len(write_data_hex) % 2 != 0:
|
||
print("❌ Hex must contain an even number of characters.")
|
||
continue
|
||
|
||
if (len(write_data_hex) // 2) > file_size:
|
||
print(f"❌ Data exceeds {file_size} bytes. Try again.")
|
||
continue
|
||
break
|
||
|
||
else:
|
||
print("❌ Invalid choice. Select 1 or 2.")
|
||
|
||
# Split data into chunks Proxmark3 reliably accepts (128 hex chars = 64 bytes)
|
||
chunk_size = 128
|
||
chunks = [write_data_hex[i:i+chunk_size] for i in range(0, len(write_data_hex), chunk_size)]
|
||
|
||
if len(chunks) > 1:
|
||
print(f"Splitting data into {len(chunks)} chunks (max {chunk_size} hex chars each)")
|
||
|
||
for i, chunk in enumerate(chunks):
|
||
offset_bytes = i * (chunk_size // 2) # Convert hex char count to bytes
|
||
|
||
# Make sure card's file size is never exceeded on the last chunk
|
||
remaining_bytes = file_size - offset_bytes
|
||
if remaining_bytes <= 0:
|
||
break
|
||
|
||
chunk = chunk[:remaining_bytes * 2] # Trim to remaining space
|
||
|
||
if i == 0:
|
||
# First write: no offset
|
||
write_command = (
|
||
f"hf mfdes write --aid {aid} --fid {file_id} "
|
||
f"-t {aid_key_type} -k {aid_key} -d {chunk} -m {com_mode}"
|
||
)
|
||
offset_desc = "000000"
|
||
else:
|
||
# Remaining writes require 3-byte offset (6 hex digits)
|
||
offset_hex = f"{offset_bytes:06X}"
|
||
offset_desc = offset_hex
|
||
write_command = (
|
||
f"hf mfdes write --aid {aid} --fid {file_id} "
|
||
f"-t {aid_key_type} -k {aid_key} -d {chunk} -m {com_mode} "
|
||
f"--offset {offset_hex}"
|
||
)
|
||
|
||
if len(chunks) > 1:
|
||
print(f"\nWriting chunk {i+1}/{len(chunks)} (offset {offset_desc})")
|
||
|
||
response = send_proxmark_command(write_command)
|
||
time.sleep(0.10)
|
||
print(response)
|
||
|
||
def edit_file_restriction(aid, key_type, key, com_mode, aid_key_type, aid_key):
|
||
|
||
while True:
|
||
print("\nNOTE: This only works if you have changed the default keys.")
|
||
print("The Proxmark3 and other tools will automatically attempt to read files using DESFire default keys.")
|
||
print("\nWould you like to apply or remove a key from the file?")
|
||
print("1. Apply key 0 (Requires authentication for access)")
|
||
print("2. Remove key (Make file freely accessible)")
|
||
print("3. Back")
|
||
|
||
choice = input("Enter your choice (1, 2, or 3): ").strip()
|
||
|
||
if choice == "3":
|
||
print("Returning to the previous menu.")
|
||
break
|
||
|
||
file_id = input("Enter file ID to update: ").strip()
|
||
|
||
if choice == "1":
|
||
edit_file_command = f"hf mfdes chfilesettings --rawrights 0000 --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} -m {com_mode}"
|
||
print("Applying key 0 for read, write, and change access. This ensures authentication is required to access the file.")
|
||
|
||
elif choice == "2":
|
||
# Must use encrypt communications mode to remove restrictions
|
||
edit_file_command = f"hf mfdes chfilesettings --rawrights EEEE --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} -m encrypt"
|
||
print("Removing key restrictions. File will be freely accessible.")
|
||
|
||
else:
|
||
print("❌ Invalid choice. Please enter 1, 2, or 3.")
|
||
continue
|
||
|
||
response = send_proxmark_command(edit_file_command)
|
||
print(response)
|
||
break
|
||
|
||
def delete_file(aid, key_type, key, com_mode, aid_key_type, aid_key):
|
||
|
||
file_id = input("Enter file ID to delete: ").strip()
|
||
|
||
delete_command = f"hf mfdes deletefile --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} -m {com_mode}"
|
||
response = send_proxmark_command(delete_command)
|
||
print(response)
|
||
|
||
if __name__ == "__main__":
|
||
authenticate_and_menu()
|