Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 111 additions & 32 deletions client/pyscripts/des_talk.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ def authenticate_and_menu():
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

# Show file menu
aid_file_menu(selected_aid, key_type, key, com_mode, aid_key_type, aid_key)

elif choice == "2":
Expand Down Expand Up @@ -241,7 +240,7 @@ def create_aid(key_type, key, com_mode):
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")
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):

Expand Down Expand Up @@ -276,6 +275,7 @@ def free_memory(key_type, key, com_mode):
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")
Expand Down Expand Up @@ -312,7 +312,7 @@ def change_key(key_type, key, com_mode):

response = send_proxmark_command(changekey_command)
print(response)
print("\nReauthenticate with the master key.")
print("\n⚠️ Reauthenticate with the master key.")
sys.exit()

elif confirm == "n":
Expand All @@ -321,6 +321,7 @@ def change_key(key_type, key, com_mode):
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)
Expand Down Expand Up @@ -361,7 +362,7 @@ def read_file(aid, key_type, key, com_mode, aid_key_type, aid_key):
response = send_proxmark_command(read_command)

if "authenticate error" in response.lower():
print("\nAuthentication Error")
print("\n❌ Authentication Error")
return None

if text_only:
Expand Down Expand Up @@ -401,7 +402,6 @@ def read_file(aid, key_type, key, com_mode, aid_key_type, aid_key):

def create_file(aid, key_type, key, com_mode, aid_key_type, aid_key):

# Prompt for file ID in hex format
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()
Expand Down Expand Up @@ -429,64 +429,143 @@ def create_file(aid, key_type, key, com_mode, aid_key_type, aid_key):
print(f"File size in hex: {file_size_hex}")

except ValueError as e:
print(f"Invalid file size: {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()

# Get file size
file_size_command = f"hf mfdes getfilesettings --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} -m {com_mode}"
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)

# Check for authentication error
if "authenticate error" in response.lower():
print("\nAuthentication Error")
print("\n❌ Authentication Error")
return None

# Extract the file size from the response
file_size_match = re.search(r"File size \(bytes\)... (\d+) / 0x([0-9A-Fa-f]+)", response)
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)) # Decimal file size

file_size = int(file_size_match.group(1))
print(f"✅ File size detected: {file_size} bytes")

# Prompt user for data format choice (plain text or hex)
while True:
data_format = input("Enter data format (Type 1 for plain text, 2 for hex): ").strip()
data_format = input("Enter data format (1 = plain text, 2 = hex): ").strip()

if data_format == "1":
# Text input (no hex)
write_data = input(f"Enter text to write (up to {file_size} bytes, no need for hex): ").strip()
write_data_hex = write_data.encode().hex().upper() # Convert to hex
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":
# Hex input
write_data_hex = input(f"Enter hex data to write (up to {file_size * 2} hex chars): ").strip()
if len(write_data_hex) % 2 != 0: # Ensure it's a valid hex string
print("❌ Invalid hex input. Please enter an even number of characters.")
elif len(write_data_hex) // 2 > file_size:
print(f"❌ Data exceeds file size limit of {file_size} bytes. Try again.")
else:
break
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. Please choose 1 for text or 2 for hex.")
print("❌ Invalid choice. Select 1 or 2.")

write_command = f"hf mfdes write --aid {aid} --fid {file_id} -t {aid_key_type} -k {aid_key} -d {write_data_hex} -m {com_mode}"
response = send_proxmark_command(write_command)
print(response)
# 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.")
Expand Down Expand Up @@ -529,4 +608,4 @@ def delete_file(aid, key_type, key, com_mode, aid_key_type, aid_key):
print(response)

if __name__ == "__main__":
authenticate_and_menu()
authenticate_and_menu()
Loading