mirror of
https://github.com/RfidResearchGroup/ChameleonUltra.git
synced 2026-05-12 11:22:59 -07:00
complete hardnested attack implementation (#254)
* hardnested test fixed, increased timeout on mf1_hard_nested_acquire to fix cmd exec timeout on clone * hardnested recovery(cli command not yet ready), based in noproto/HardnestedRecovery * removed compiled binary * Hardnested cli ready * removed some unnecesary files * removed unnecesary cached files * cmake now builds hardnested too * removed license.md * added liblzma source(should fix checks not passing) * i missed a line * trimmed xz sources * cmake now links local liblzma.a * third try(warning solved) * cmake now builds and links correcctly liblzma.a * xz-5.8.1 vfolder renamed to xz * fixed paths * runner test * removed wrong placed check * disable landlock under windows * missing files * missing files * windows strtok fix * corrected lzma path under windows * fix * set release config for liblzma * corrected path * trailing_zeros * msclock * msclock integer issue * msclock missing header * fallback if not using msvc * deleted include by accident * revert clock change * change custom target for custom command * windows fmemopen implementation * wrong path * wrong name * use fmemopen only when not using msvc * use fmemopen.h when building on windows, non msvc * re-add static link * wrong filename * pthread handling for mingw(proxspace) * cleanup
This commit is contained in:
+4
-3
@@ -123,7 +123,6 @@ dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
@@ -236,7 +235,7 @@ venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
.vscode/
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
@@ -707,4 +706,6 @@ FodyWeavers.xsd
|
||||
### VisualStudio Patch ###
|
||||
# Additional files built by Visual Studio
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/visualstudio,c++,c,python,visualstudiocode,macos,windows
|
||||
# End of https://www.toptal.com/developers/gitignore/api/visualstudio,c++,c,python,visualstudiocode,macos,windows
|
||||
software/script/tests/nonces.bin
|
||||
software/script/nonces.bin
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import binascii
|
||||
import os
|
||||
import tempfile
|
||||
import re
|
||||
import subprocess
|
||||
import argparse
|
||||
@@ -15,6 +16,7 @@ from typing import Union
|
||||
from pathlib import Path
|
||||
from platform import uname
|
||||
from datetime import datetime
|
||||
import hardnested_utils
|
||||
|
||||
import chameleon_com
|
||||
import chameleon_cmd
|
||||
@@ -889,6 +891,445 @@ class HFMFDarkside(ReaderRequiredUnit):
|
||||
print(" - Key recover fail.")
|
||||
return
|
||||
|
||||
@hf_mf.command('hardnested')
|
||||
class HFMFHardNested(ReaderRequiredUnit):
|
||||
def args_parser(self) -> ArgumentParserNoExit:
|
||||
parser = ArgumentParserNoExit()
|
||||
parser.description = 'Mifare Classic hardnested recover key '
|
||||
parser.add_argument('--blk', '--known-block', type=int, required=True, metavar="<dec>",
|
||||
help="Known key block number")
|
||||
srctype_group = parser.add_mutually_exclusive_group()
|
||||
srctype_group.add_argument('-a', '-A', action='store_true', help="Known key is A key (default)")
|
||||
srctype_group.add_argument('-b', '-B', action='store_true', help="Known key is B key")
|
||||
parser.add_argument('-k', '--key', type=str, required=True, metavar="<hex>", help="Known key")
|
||||
parser.add_argument('--tblk', '--target-block', type=int, required=True, metavar="<dec>",
|
||||
help="Target key block number")
|
||||
dsttype_group = parser.add_mutually_exclusive_group()
|
||||
dsttype_group.add_argument('--ta', '--tA', action='store_true', help="Target A key (default)")
|
||||
dsttype_group.add_argument('--tb', '--tB', action='store_true', help="Target B key")
|
||||
parser.add_argument('--slow', action='store_true', help="Use slower acquisition mode (more nonces)")
|
||||
parser.add_argument('--keep-nonce-file', action='store_true', help="Keep the generated nonce file (nonces.bin)")
|
||||
parser.add_argument('--max-runs', type=int, default=200, metavar="<dec>",
|
||||
help="Maximum acquisition runs per attempt before giving up (default: 200)")
|
||||
# Add max acquisition attempts
|
||||
parser.add_argument('--max-attempts', type=int, default=3, metavar="<dec>",
|
||||
help="Maximum acquisition attempts if MSB sum is invalid (default: 3)")
|
||||
return parser
|
||||
|
||||
def recover_key(self, slow_mode, block_known, type_known, key_known, block_target, type_target, keep_nonce_file, max_runs, max_attempts):
|
||||
"""
|
||||
Recover a key using the HardNested attack via a nonce file, with dynamic MSB-based acquisition and restart on invalid sum.
|
||||
|
||||
:param slow_mode: Boolean indicating if slow mode should be used.
|
||||
:param block_known: Known key block number.
|
||||
:param type_known: Known key type (A or B).
|
||||
:param key_known: Known key bytes.
|
||||
:param block_target: Target key block number.
|
||||
:param type_target: Target key type (A or B).
|
||||
:param keep_nonce_file: Boolean indicating whether to keep the nonce file.
|
||||
:param max_runs: Maximum number of acquisition runs per attempt.
|
||||
:param max_attempts: Maximum number of full acquisition attempts.
|
||||
:return: Recovered key as a hex string, or None if not found.
|
||||
"""
|
||||
print(f" - Starting HardNested attack...")
|
||||
nonces_buffer = bytearray() # This will hold the final data for the file
|
||||
uid_bytes = b'' # To store UID from the successful attempt
|
||||
|
||||
# --- Outer loop for acquisition attempts ---
|
||||
acquisition_success = False # Flag to indicate if any attempt was successful
|
||||
for attempt in range(max_attempts):
|
||||
print(f"\n--- Starting Acquisition Attempt {attempt + 1}/{max_attempts} ---")
|
||||
total_raw_nonces_bytes = bytearray() # Accumulator for raw nonces for THIS attempt
|
||||
nonces_buffer.clear() # Clear buffer for each new attempt
|
||||
|
||||
# --- MSB Tracking Initialization (Reset for each attempt) ---
|
||||
seen_msbs = [False] * 256
|
||||
unique_msb_count = 0
|
||||
msb_parity_sum = 0
|
||||
# --- End MSB Tracking Initialization ---
|
||||
|
||||
run_count = 0
|
||||
acquisition_goal_met = False
|
||||
|
||||
# 1. Scan for the tag to get UID and prepare file header (Done ONCE per attempt)
|
||||
print(" Scanning for tag...")
|
||||
try:
|
||||
scan_resp = self.cmd.hf14a_scan()
|
||||
except Exception as e:
|
||||
print(f"{CR} Error scanning tag: {e}{C0}")
|
||||
# Decide if we should retry or fail completely. Let's fail for now.
|
||||
print(f"{CR} Attack failed due to error during scanning.{C0}")
|
||||
return None
|
||||
|
||||
if scan_resp is None or len(scan_resp) == 0:
|
||||
print(f"{CR} Error: No tag found.{C0}")
|
||||
if attempt + 1 < max_attempts:
|
||||
print(f"{CY} Retrying scan in 1 second...{C0}")
|
||||
time.sleep(1)
|
||||
continue # Retry the outer loop (next attempt)
|
||||
else:
|
||||
print(f"{CR} Maximum attempts reached without finding tag. Attack failed.{C0}")
|
||||
return None
|
||||
if len(scan_resp) > 1:
|
||||
print(f"{CR} Error: Multiple tags found. Please present only one tag.{C0}")
|
||||
# Fail immediately if multiple tags are present
|
||||
return None
|
||||
|
||||
tag_info = scan_resp[0]
|
||||
uid_bytes = tag_info['uid'] # Store UID for later verification
|
||||
uid_len = len(uid_bytes)
|
||||
uid_for_file = b''
|
||||
if uid_len == 4:
|
||||
uid_for_file = uid_bytes[0: 4]
|
||||
elif uid_len == 7:
|
||||
uid_for_file = uid_bytes[3: 7]
|
||||
elif uid_len == 10:
|
||||
uid_for_file = uid_bytes[6: 10]
|
||||
else:
|
||||
print(f"{CR} Error: Unexpected UID length ({uid_len} bytes). Cannot create nonce file header.{C0}")
|
||||
return None # Fail if UID length is unexpected
|
||||
print(f" Tag found with UID: {uid_bytes.hex().upper()}")
|
||||
# Prepare header in the main buffer for this attempt
|
||||
nonces_buffer.extend(uid_for_file)
|
||||
nonces_buffer.extend(struct.pack('!BB', block_target, type_target.value & 0x01))
|
||||
print(f" Nonce file header prepared: {nonces_buffer.hex().upper()}")
|
||||
|
||||
|
||||
# 2. Acquire nonces dynamically based on MSB criteria (Inner loop for runs)
|
||||
print(f" Acquiring nonces (slow mode: {slow_mode}, max runs: {max_runs}). This may take a while...")
|
||||
while run_count < max_runs:
|
||||
run_count += 1
|
||||
print(f" Starting acquisition run {run_count}/{max_runs}...")
|
||||
try:
|
||||
# Check if tag is still present before each run
|
||||
current_scan = self.cmd.hf14a_scan()
|
||||
if current_scan is None or len(current_scan) == 0 or current_scan[0]['uid'] != uid_bytes:
|
||||
print(f"{CR} Error: Tag lost or changed before run {run_count}. Stopping acquisition attempt.{C0}")
|
||||
acquisition_goal_met = False # Mark as failed
|
||||
break # Exit inner run loop for this attempt
|
||||
|
||||
# Acquire nonces for this run
|
||||
raw_nonces_bytes_this_run = self.cmd.mf1_hard_nested_acquire(
|
||||
slow_mode, block_known, type_known, key_known, block_target, type_target
|
||||
)
|
||||
|
||||
if not raw_nonces_bytes_this_run:
|
||||
print(f"{CY} Run {run_count}: No nonces acquired in this run. Continuing...{C0}")
|
||||
time.sleep(0.1) # Small delay before retrying
|
||||
continue
|
||||
|
||||
# Append successfully acquired nonces to the total buffer for this attempt
|
||||
total_raw_nonces_bytes.extend(raw_nonces_bytes_this_run)
|
||||
|
||||
# --- Process acquired nonces for MSB tracking ---
|
||||
num_pairs_this_run = len(raw_nonces_bytes_this_run) // 9
|
||||
print(f" Run {run_count}: Acquired {num_pairs_this_run * 2} nonces ({len(raw_nonces_bytes_this_run)} bytes raw). Processing MSBs...")
|
||||
|
||||
new_msbs_found_this_run = 0
|
||||
for i in range(num_pairs_this_run):
|
||||
offset = i * 9
|
||||
try:
|
||||
nt, nt_enc, par = struct.unpack_from('!IIB', raw_nonces_bytes_this_run, offset)
|
||||
except struct.error as unpack_err:
|
||||
print(f"{CR} Error unpacking nonce data at offset {offset}: {unpack_err}. Skipping pair.{C0}")
|
||||
continue
|
||||
|
||||
msb = (nt_enc >> 24) & 0xFF
|
||||
|
||||
if not seen_msbs[msb]:
|
||||
seen_msbs[msb] = True
|
||||
unique_msb_count += 1
|
||||
new_msbs_found_this_run += 1
|
||||
parity_bit = hardnested_utils.evenparity32((nt_enc & 0xff000000) | (par & 0x08))
|
||||
msb_parity_sum += parity_bit
|
||||
print(f"\r Unique MSBs: {unique_msb_count}/256 | Current Sum: {msb_parity_sum} ", end="")
|
||||
|
||||
if new_msbs_found_this_run > 0:
|
||||
print() # Print a newline after progress update
|
||||
|
||||
# --- Check termination condition ---
|
||||
if unique_msb_count == 256:
|
||||
print(f"\n {CG}All 256 unique MSBs found.{C0} Final parity sum: {msb_parity_sum}")
|
||||
if msb_parity_sum in hardnested_utils.hardnested_sums:
|
||||
print(f" {CG}Parity sum {msb_parity_sum} is VALID. Stopping acquisition runs.{C0}")
|
||||
acquisition_goal_met = True
|
||||
acquisition_success = True # Mark attempt as successful
|
||||
break # Exit the inner run loop successfully
|
||||
else:
|
||||
print(f" {CR}Parity sum {msb_parity_sum} is INVALID (Expected one of {hardnested_utils.hardnested_sums}).{C0}")
|
||||
acquisition_goal_met = False # Mark as failed
|
||||
acquisition_success = False
|
||||
break # Exit the inner run loop to restart the attempt
|
||||
|
||||
except chameleon_com.CMDInvalidException:
|
||||
print(f"{CR} Error: Hardnested command not supported by this firmware version.{C0}")
|
||||
return None # Cannot proceed at all
|
||||
except UnexpectedResponseError as e:
|
||||
print(f"{CR} Error acquiring nonces during run {run_count}: {e}{C0}")
|
||||
print(f"{CY} Stopping acquisition runs for this attempt...{C0}")
|
||||
acquisition_goal_met = False
|
||||
break # Exit inner run loop
|
||||
except TimeoutError:
|
||||
print(f"{CR} Error: Timeout during nonce acquisition run {run_count}.{C0}")
|
||||
print(f"{CY} Stopping acquisition runs for this attempt...{C0}")
|
||||
acquisition_goal_met = False
|
||||
break # Exit inner run loop
|
||||
except Exception as e:
|
||||
print(f"{CR} Unexpected error during acquisition run {run_count}: {e}{C0}")
|
||||
print(f"{CY} Stopping acquisition runs for this attempt...{C0}")
|
||||
acquisition_goal_met = False
|
||||
break # Exit inner run loop
|
||||
# --- End of inner run loop (while run_count < max_runs) ---
|
||||
|
||||
# --- Post-Acquisition Summary for this attempt ---
|
||||
print(f"\n Finished acquisition phase for attempt {attempt + 1}.")
|
||||
if acquisition_success:
|
||||
print(f" {CG}Successfully acquired nonces meeting the MSB sum criteria in {run_count} runs.{C0}")
|
||||
# Append collected raw nonces to the main buffer for the file
|
||||
nonces_buffer.extend(total_raw_nonces_bytes)
|
||||
break # Exit the outer attempt loop successfully
|
||||
elif unique_msb_count == 256 and not acquisition_goal_met:
|
||||
print(f" {CR}Found all 256 MSBs, but the parity sum was invalid.{C0}")
|
||||
if attempt + 1 < max_attempts:
|
||||
print(f" {CY}Restarting acquisition process...{C0}")
|
||||
time.sleep(1) # Small delay before restarting
|
||||
continue # Continue to the next iteration of the outer attempt loop
|
||||
else:
|
||||
print(f" {CR}Maximum attempts ({max_attempts}) reached with invalid sum. Attack failed.{C0}")
|
||||
return None # Failed after max attempts
|
||||
elif run_count >= max_runs:
|
||||
print(f" {CY}Warning: Reached max runs ({max_runs}) for attempt {attempt + 1}. Found {unique_msb_count}/256 unique MSBs.{C0}")
|
||||
if attempt + 1 < max_attempts:
|
||||
print(f" {CY}Restarting acquisition process...{C0}")
|
||||
time.sleep(1)
|
||||
continue # Continue to the next iteration of the outer attempt loop
|
||||
else:
|
||||
print(f" {CR}Maximum attempts ({max_attempts}) reached without meeting criteria. Attack failed.{C0}")
|
||||
return None # Failed after max attempts
|
||||
else: # Acquisition stopped due to error or tag loss
|
||||
print(f" {CR}Acquisition attempt {attempt + 1} stopped prematurely due to an error after {run_count} runs.{C0}")
|
||||
# Decide if we should retry or fail completely. Let's fail for now.
|
||||
print(f" {CR}Attack failed due to error during acquisition.{C0}")
|
||||
return None # Failed due to error
|
||||
|
||||
# --- End of outer attempt loop ---
|
||||
|
||||
# If we exited the loop successfully (acquisition_success is True)
|
||||
if not acquisition_success:
|
||||
# This case should ideally be caught within the loop, but as a safeguard:
|
||||
print(f"{CR} Error: Acquisition failed after {max_attempts} attempts.{C0}")
|
||||
return None
|
||||
|
||||
# --- Proceed with the rest of the attack using the successfully collected nonces ---
|
||||
total_nonce_pairs = len(total_raw_nonces_bytes) // 9 # Use data from the successful attempt
|
||||
print(f"\n Proceeding with attack using {total_nonce_pairs * 2} nonces ({len(total_raw_nonces_bytes)} bytes raw).")
|
||||
print(f" Total nonce file size will be {len(nonces_buffer)} bytes.")
|
||||
|
||||
if total_nonce_pairs == 0:
|
||||
print(f"{CR} Error: No nonces were successfully acquired in the final attempt.{C0}")
|
||||
return None
|
||||
|
||||
# 3. Save nonces to a temporary file
|
||||
nonce_file_path = None
|
||||
temp_nonce_file = None
|
||||
temp_output_file = None # For hardnested output
|
||||
process = None # Define process here for finally block
|
||||
output_str = "" # To store the output read from the file
|
||||
output_log_path = "" # To store the path of the output log
|
||||
|
||||
try:
|
||||
# --- Nonce File Handling ---
|
||||
delete_nonce_on_close = not keep_nonce_file
|
||||
# Use delete_on_close=False to manage deletion manually in finally block
|
||||
temp_nonce_file = tempfile.NamedTemporaryFile(
|
||||
suffix=".bin", prefix="hardnested_nonces_", delete=False,
|
||||
mode='wb', dir='.'
|
||||
)
|
||||
temp_nonce_file.write(nonces_buffer) # Write the buffer from the successful attempt
|
||||
temp_nonce_file.flush()
|
||||
nonce_file_path = temp_nonce_file.name
|
||||
temp_nonce_file.close() # Close it so hardnested can access it
|
||||
temp_nonce_file = None # Clear variable after closing
|
||||
print(f" Nonces saved to {'temporary ' if delete_nonce_on_close else ''}file: {os.path.abspath(nonce_file_path)}")
|
||||
|
||||
# --- Output File Handling ---
|
||||
# Create a temporary file to capture hardnested's output
|
||||
# Keep it open while the subprocess runs, use delete=False for manual cleanup
|
||||
temp_output_file = tempfile.NamedTemporaryFile(
|
||||
suffix=".log", prefix="hardnested_output_", delete=False,
|
||||
mode='w+', encoding='utf-8', errors='replace', dir='.'
|
||||
)
|
||||
output_log_path = temp_output_file.name # Store path for potential error messages
|
||||
print(f" Redirecting hardnested output to temporary log file: {os.path.abspath(output_log_path)}")
|
||||
|
||||
|
||||
# 4. Prepare and run the external hardnested tool, redirecting output
|
||||
tool_name = "hardnested"
|
||||
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)
|
||||
# Use list for Popen, ensure paths are correct
|
||||
cmd_recover_list = [tool_path, os.path.abspath(nonce_file_path)]
|
||||
|
||||
print(f" Executing: {' '.join(cmd_recover_list)}")
|
||||
print(f"{CC}--- Running Hardnested Tool (Output redirected) ---{C0}")
|
||||
|
||||
# Run the process, redirecting stdout and stderr to the output file
|
||||
process = subprocess.Popen(
|
||||
cmd_recover_list,
|
||||
cwd=default_cwd, # Run from the bin directory
|
||||
stdout=temp_output_file, # Redirect stdout to file
|
||||
stderr=subprocess.STDOUT, # Redirect stderr to the same file as stdout
|
||||
)
|
||||
|
||||
# Wait for the process to complete
|
||||
ret_code = process.wait() # This blocks until the tool finishes
|
||||
|
||||
print(f"{CC}--- Hardnested Tool Finished (Exit Code: {ret_code}) ---{C0}")
|
||||
|
||||
# 5. Read the output from the temporary log file
|
||||
temp_output_file.seek(0) # Go back to the start of the file
|
||||
output_str = temp_output_file.read() # Read the entire content
|
||||
temp_output_file.close() # Close the file
|
||||
temp_output_file = None # Clear the variable
|
||||
|
||||
# Optional: Print the captured output if needed for debugging
|
||||
# print(f"{CY}--- Captured Hardnested Output ---{C0}\n{output_str}\n{CY}--- End Captured Output ---{C0}")
|
||||
|
||||
# 6. Process the result (using output_str read from the file)
|
||||
if ret_code != 0:
|
||||
print(f"{CR} Error: Hardnested exited with code {ret_code}. Check log: {os.path.abspath(output_log_path)}{C0}")
|
||||
if output_str:
|
||||
print(f"{CR} Output captured:\n{output_str}{C0}")
|
||||
return None
|
||||
|
||||
key_list = []
|
||||
key_prefix = "Key found: " # Define the specific prefix to look for
|
||||
for line in output_str.splitlines():
|
||||
line_stripped = line.strip() # Remove leading/trailing whitespace
|
||||
if line_stripped.startswith(key_prefix):
|
||||
# Found the target line, now extract the key using regex
|
||||
# Regex now looks for 12 hex chars specifically after the prefix
|
||||
sea_obj = re.search(r"([a-fA-F0-9]{12})", line_stripped[len(key_prefix):])
|
||||
if sea_obj:
|
||||
key_list.append(sea_obj.group(1))
|
||||
# Optional: Break if you only expect one "Key found:" line
|
||||
# break
|
||||
|
||||
if not key_list:
|
||||
print(f"{CY} No line starting with '{key_prefix}' found in the output file.{C0}")
|
||||
return None
|
||||
|
||||
# 7. Verify Keys (Same as before)
|
||||
print(f" [{len(key_list)} candidate key(s) found in output. Verifying...]")
|
||||
# Use the UID from the successful acquisition attempt
|
||||
uid_bytes_for_verify = uid_bytes # From the last successful scan in the outer loop
|
||||
|
||||
for key_hex in key_list:
|
||||
key_bytes = bytes.fromhex(key_hex)
|
||||
print(f" Trying key: {key_hex.upper()}...", end="")
|
||||
try:
|
||||
# Check tag presence before auth attempt
|
||||
scan_check = self.cmd.hf14a_scan()
|
||||
if scan_check is None or len(scan_check) == 0 or scan_check[0]['uid'] != uid_bytes_for_verify:
|
||||
print(f" {CR}Tag lost or changed during verification. Cannot verify.{C0}")
|
||||
return None # Stop verification if tag is gone
|
||||
|
||||
if self.cmd.mf1_auth_one_key_block(block_target, type_target, key_bytes):
|
||||
print(f" {CG}Success!{C0}")
|
||||
return key_hex # Return the verified key
|
||||
else:
|
||||
print(f" {CR}Auth failed.{C0}")
|
||||
except UnexpectedResponseError as e:
|
||||
print(f" {CR}Verification error: {e}{C0}")
|
||||
# Consider if we should continue trying other keys or stop
|
||||
except Exception as e:
|
||||
print(f" {CR}Unexpected error during verification: {e}{C0}")
|
||||
# Consider stopping here
|
||||
|
||||
print(f"{CY} Verification failed for all candidate keys.{C0}")
|
||||
return None
|
||||
|
||||
finally:
|
||||
# 8. Clean up nonce file
|
||||
if nonce_file_path and os.path.exists(nonce_file_path):
|
||||
if keep_nonce_file:
|
||||
final_nonce_filename = "nonces.bin"
|
||||
try:
|
||||
if os.path.exists(final_nonce_filename):
|
||||
os.remove(final_nonce_filename)
|
||||
# Use replace for atomicity if possible
|
||||
os.replace(nonce_file_path, final_nonce_filename)
|
||||
print(f" Nonce file kept as: {os.path.abspath(final_nonce_filename)}")
|
||||
except OSError as e:
|
||||
print(f"{CR} Error renaming/replacing temporary nonce file to {final_nonce_filename}: {e}{C0}")
|
||||
print(f" Temporary file might remain: {nonce_file_path}")
|
||||
else:
|
||||
try:
|
||||
os.remove(nonce_file_path)
|
||||
# print(f" Temporary nonce file deleted: {nonce_file_path}") # Optional confirmation
|
||||
except OSError as e:
|
||||
print(f"{CR} Error deleting temporary nonce file {nonce_file_path}: {e}{C0}")
|
||||
|
||||
# Ensure output file is closed and deleted if an error occurred before its closure
|
||||
if temp_output_file: # If it wasn't closed and cleared in the try block
|
||||
try:
|
||||
temp_output_file.close()
|
||||
except Exception:
|
||||
pass # Ignore errors during cleanup close
|
||||
|
||||
# Delete the output log file unless an error occurred and we want to keep it
|
||||
if output_log_path and os.path.exists(output_log_path):
|
||||
# Keep log if hardnested failed (ret_code != 0) or if verification failed?
|
||||
# For now, let's always delete it unless there was an exception *before* reading it.
|
||||
# If ret_code != 0, the path was already printed.
|
||||
try:
|
||||
os.remove(output_log_path)
|
||||
except OSError as e:
|
||||
print(f"{CR} Error deleting temporary output log file {output_log_path}: {e}{C0}")
|
||||
|
||||
|
||||
# Ensure process is terminated if something went wrong
|
||||
if process and process.poll() is None:
|
||||
try:
|
||||
print(f"{CY} Terminating hardnested process...{C0}")
|
||||
process.terminate() # Try graceful termination
|
||||
process.wait(timeout=0.5) # Wait briefly
|
||||
if process.poll() is None:
|
||||
process.kill() # Force kill if still running
|
||||
except Exception as kill_err:
|
||||
print(f"{CR} Error terminating process: {kill_err}{C0}")
|
||||
|
||||
|
||||
def on_exec(self, args: argparse.Namespace):
|
||||
block_known = args.blk
|
||||
type_known = MfcKeyType.B if args.b else MfcKeyType.A
|
||||
key_known_str: str = args.key
|
||||
if not re.match(r"^[a-fA-F0-9]{12}$", key_known_str):
|
||||
raise ArgsParserError("Known key must include 12 HEX symbols")
|
||||
key_known_bytes = bytes.fromhex(key_known_str)
|
||||
|
||||
block_target = args.tblk
|
||||
type_target = MfcKeyType.B if args.tb else MfcKeyType.A
|
||||
|
||||
if block_known == block_target and type_known == type_target:
|
||||
print(f"{CR}Target key is the same as the known key.{C0}")
|
||||
return
|
||||
|
||||
# Pass the max_runs and max_attempts arguments
|
||||
recovered_key = self.recover_key(
|
||||
args.slow, block_known, type_known, key_known_bytes, block_target, type_target,
|
||||
args.keep_nonce_file, args.max_runs, args.max_attempts
|
||||
)
|
||||
|
||||
if recovered_key:
|
||||
print(f" - Key Found: Block {block_target} Type {type_target.name} Key = {CG}{recovered_key.upper()}{C0}")
|
||||
else:
|
||||
print(f"{CR} - HardNested attack failed to recover the key.{C0}")
|
||||
|
||||
|
||||
@hf_mf.command('fchk')
|
||||
class HFMFFCHK(ReaderRequiredUnit):
|
||||
|
||||
@@ -366,7 +366,7 @@ 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)
|
||||
resp = self.device.send_cmd_sync(Command.DATA_CMD_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
|
||||
|
||||
@@ -12,10 +12,10 @@ def test_hardnested_acquire():
|
||||
acquire_count = 0
|
||||
|
||||
# known key and target block
|
||||
key = bytes.fromhex("????????????")
|
||||
key = bytes.fromhex("FFFFFFFFFFFF") # <-- Your known key
|
||||
block_known = 0x00
|
||||
type_known = 0x60
|
||||
block_target = 0x07
|
||||
block_target = 0x00
|
||||
type_target = 0x60
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@ def test_hardnested_acquire():
|
||||
cml = ChameleonCom().open('/dev/ttyACM0')
|
||||
cml_cmd = ChameleonCMD(cml)
|
||||
|
||||
# ------------------------ SET DEVICE MODE ------------------------
|
||||
print("Setting device mode to HF Reader...")
|
||||
status = cml_cmd.set_device_reader_mode()
|
||||
|
||||
# ------------------------ append tag info ------------------------
|
||||
|
||||
resp = cml_cmd.hf14a_scan()
|
||||
@@ -39,7 +43,8 @@ def test_hardnested_acquire():
|
||||
print("ISO14443-A Tag no found")
|
||||
return
|
||||
|
||||
uidbytes = bytearray.fromhex(resp['uid'])
|
||||
tag_info = resp[0]
|
||||
uidbytes = tag_info['uid']
|
||||
uid_len = len(uidbytes)
|
||||
if uid_len == 4:
|
||||
nonces_buffer.extend(uidbytes[0: 4])
|
||||
@@ -96,3 +101,9 @@ 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()
|
||||
except Exception as e:
|
||||
print(f"An error occurred: {e}")
|
||||
|
||||
+309
-74
@@ -1,74 +1,309 @@
|
||||
cmake_minimum_required (VERSION 3.5)
|
||||
|
||||
project (mifare C)
|
||||
|
||||
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../script/bin)
|
||||
set(SRC_DIR ./)
|
||||
|
||||
set(COMMON_FILES
|
||||
${SRC_DIR}/common.c
|
||||
${SRC_DIR}/crapto1.c
|
||||
${SRC_DIR}/crypto1.c
|
||||
${SRC_DIR}/bucketsort.c
|
||||
${SRC_DIR}/parity.c)
|
||||
|
||||
set(
|
||||
NESTED_UTIL
|
||||
${SRC_DIR}/nested_util.c
|
||||
)
|
||||
|
||||
set(
|
||||
MFKEY_UTIL
|
||||
${SRC_DIR}/mfkey.c
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${SRC_DIR}/
|
||||
)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
MESSAGE(STATUS "Run on linux.")
|
||||
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")
|
||||
endif()
|
||||
|
||||
add_compile_options(-D_GNU_SOURCE)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
set(LIBTHREAD pthread)
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
MESSAGE(STATUS "Run on Windows.")
|
||||
|
||||
# Add pthread header for Windows
|
||||
include_directories(lib/pthread/include)
|
||||
|
||||
# optimize
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Ox")
|
||||
endif()
|
||||
|
||||
# pthread need
|
||||
add_definitions(-DHAVE_STRUCT_TIMESPEC)
|
||||
|
||||
# find the pthread dep
|
||||
find_library(LIBTHREAD pthreadVC2.lib lib/pthread/lib/x64/)
|
||||
else()
|
||||
MESSAGE(STATUS "other platform: ${CMAKE_SYSTEM_NAME}")
|
||||
endif()
|
||||
|
||||
# ignore warning C4996
|
||||
add_compile_options(-D_CRT_SECURE_NO_WARNINGS)
|
||||
|
||||
# tools
|
||||
add_executable(nested ${COMMON_FILES} ${NESTED_UTIL} nested.c)
|
||||
target_link_libraries(nested ${LIBTHREAD})
|
||||
|
||||
add_executable(staticnested ${COMMON_FILES} ${NESTED_UTIL} staticnested.c)
|
||||
target_link_libraries(staticnested ${LIBTHREAD})
|
||||
|
||||
add_executable(darkside ${COMMON_FILES} ${MFKEY_UTIL} darkside.c)
|
||||
|
||||
add_executable(mfkey32 ${COMMON_FILES} mfkey32.c)
|
||||
add_executable(mfkey32v2 ${COMMON_FILES} mfkey32v2.c)
|
||||
add_executable(mfkey64 ${COMMON_FILES} mfkey64.c)
|
||||
cmake_minimum_required (VERSION 3.5)
|
||||
|
||||
project (mifare C)
|
||||
|
||||
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../script/bin)
|
||||
set(SRC_DIR ./) # Assuming source files are in the same directory as CMakeLists.txt
|
||||
|
||||
# Define a variable for the compatibility code directory
|
||||
set(COMPAT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/compat)
|
||||
|
||||
set(COMMON_FILES
|
||||
${SRC_DIR}/common.c
|
||||
${SRC_DIR}/crapto1.c
|
||||
${SRC_DIR}/crypto1.c
|
||||
${SRC_DIR}/bucketsort.c
|
||||
${SRC_DIR}/parity.c)
|
||||
|
||||
set(
|
||||
NESTED_UTIL
|
||||
${SRC_DIR}/nested_util.c
|
||||
)
|
||||
|
||||
set(
|
||||
MFKEY_UTIL
|
||||
${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
|
||||
)
|
||||
|
||||
# --- 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
|
||||
)
|
||||
|
||||
# --- 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)
|
||||
|
||||
|
||||
# --- Hardnested Recovery Sources ---
|
||||
set(HARDNESTED_RECOVERY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/HardnestedRecovery)
|
||||
|
||||
set(HARDNESTED_SOURCES
|
||||
${HARDNESTED_RECOVERY_DIR}/hardnested_main.c
|
||||
${HARDNESTED_RECOVERY_DIR}/pm3/ui.c
|
||||
${HARDNESTED_RECOVERY_DIR}/pm3/util.c
|
||||
${HARDNESTED_RECOVERY_DIR}/cmdhfmfhard.c
|
||||
${HARDNESTED_RECOVERY_DIR}/pm3/commonutil.c
|
||||
${HARDNESTED_RECOVERY_DIR}/hardnested/hardnested_bf_core.c
|
||||
${HARDNESTED_RECOVERY_DIR}/hardnested/hardnested_bruteforce.c
|
||||
${HARDNESTED_RECOVERY_DIR}/hardnested/hardnested_bitarray_core.c
|
||||
${HARDNESTED_RECOVERY_DIR}/hardnested/tables.c
|
||||
)
|
||||
if(NOT CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
list(APPEND HARDNESTED_SOURCES ${HARDNESTED_RECOVERY_DIR}/pm3/util_posix.c)
|
||||
endif()
|
||||
|
||||
|
||||
# --- Platform specific settings ---
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
MESSAGE(STATUS "Run on linux.")
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3")
|
||||
endif()
|
||||
find_package(Threads REQUIRED)
|
||||
set(LIBTHREAD Threads::Threads) # Use modern target
|
||||
set(LIBMATH m)
|
||||
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
MESSAGE(STATUS "Run on Windows.")
|
||||
if (CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
# Set optimization flags based on compiler
|
||||
if(MSVC)
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Ox")
|
||||
else() # Assuming MinGW or similar GCC-compatible
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3")
|
||||
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 ---
|
||||
|
||||
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}")
|
||||
set(LIBMATH "")
|
||||
# Attempt to find Threads anyway, might fail gracefully or error depending on REQUIRED
|
||||
find_package(Threads)
|
||||
if(Threads_FOUND)
|
||||
set(LIBTHREAD Threads::Threads)
|
||||
else()
|
||||
message(WARNING "Threads library not found for platform ${CMAKE_SYSTEM_NAME}. Linking might fail.")
|
||||
set(LIBTHREAD "") # Set to empty or handle error
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# --- Executable Definitions ---
|
||||
|
||||
add_executable(nested ${COMMON_FILES} ${NESTED_UTIL} nested.c)
|
||||
target_include_directories(nested PRIVATE ${SRC_DIR})
|
||||
target_link_libraries(nested PRIVATE ${LIBTHREAD}) # Link common thread lib
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
target_compile_definitions(nested PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
target_compile_definitions(nested PRIVATE HAVE_STRUCT_TIMESPEC)
|
||||
# No extra target_link_libraries needed here, ${LIBTHREAD} handles it
|
||||
endif()
|
||||
|
||||
|
||||
add_executable(staticnested ${COMMON_FILES} ${NESTED_UTIL} staticnested.c)
|
||||
target_include_directories(staticnested PRIVATE ${SRC_DIR})
|
||||
target_link_libraries(staticnested PRIVATE ${LIBTHREAD}) # Link common thread lib
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
target_compile_definitions(staticnested PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
target_compile_definitions(staticnested PRIVATE HAVE_STRUCT_TIMESPEC)
|
||||
# No extra target_link_libraries needed here, ${LIBTHREAD} handles it
|
||||
endif()
|
||||
|
||||
|
||||
add_executable(darkside ${COMMON_FILES} ${MFKEY_UTIL} darkside.c)
|
||||
target_include_directories(darkside PRIVATE ${SRC_DIR})
|
||||
# darkside doesn't seem to need pthreads based on original file
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
target_compile_definitions(darkside PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
target_compile_definitions(darkside PRIVATE HAVE_STRUCT_TIMESPEC)
|
||||
endif()
|
||||
|
||||
|
||||
add_executable(mfkey32 ${COMMON_FILES} mfkey32.c)
|
||||
target_include_directories(mfkey32 PRIVATE ${SRC_DIR})
|
||||
# mfkey32 doesn't seem to need pthreads based on original file
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
target_compile_definitions(mfkey32 PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
target_compile_definitions(mfkey32 PRIVATE HAVE_STRUCT_TIMESPEC)
|
||||
endif()
|
||||
|
||||
|
||||
add_executable(mfkey32v2 ${COMMON_FILES} mfkey32v2.c)
|
||||
target_include_directories(mfkey32v2 PRIVATE ${SRC_DIR})
|
||||
# mfkey32v2 doesn't seem to need pthreads based on original file
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
target_compile_definitions(mfkey32v2 PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
target_compile_definitions(mfkey32v2 PRIVATE HAVE_STRUCT_TIMESPEC)
|
||||
endif()
|
||||
|
||||
|
||||
add_executable(mfkey64 ${COMMON_FILES} mfkey64.c)
|
||||
target_include_directories(mfkey64 PRIVATE ${SRC_DIR})
|
||||
# mfkey64 doesn't seem to need pthreads based on original file
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
target_compile_definitions(mfkey64 PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
target_compile_definitions(mfkey64 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
|
||||
)
|
||||
target_compile_options(hardnested PRIVATE -Wall)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
target_compile_definitions(hardnested PRIVATE _GNU_SOURCE)
|
||||
endif()
|
||||
|
||||
# Platform-specific settings for Windows
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||
|
||||
# Settings common to all Windows builds (MSVC & MinGW)
|
||||
target_compile_definitions(hardnested PRIVATE
|
||||
HAVE_STRUCT_TIMESPEC
|
||||
LZMA_API_STATIC # Keep if needed for static linking of lzma
|
||||
)
|
||||
# No extra target_link_libraries needed here, ${LIBTHREAD} handles it below
|
||||
|
||||
# Add fmemopen compatibility layer ONLY for non-MSVC Windows builds (e.g., MinGW)
|
||||
if(NOT MSVC)
|
||||
message(STATUS "Non-MSVC Windows build detected, adding fmemopen compatibility layer.")
|
||||
target_sources(hardnested PRIVATE
|
||||
${COMPAT_DIR}/fmemopen/libfmemopen.c # Compile the source file
|
||||
)
|
||||
target_include_directories(hardnested PRIVATE
|
||||
${COMPAT_DIR}/fmemopen # Add include directory for fmemopen.h
|
||||
)
|
||||
endif() # End NOT MSVC
|
||||
|
||||
endif() # End Windows
|
||||
|
||||
# Link libraries common to all platforms (or handled by variables)
|
||||
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
|
||||
)
|
||||
|
||||
# Set the output directory for all executables at the end
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH})
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# Compiler and flags
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -fPIC -I. -I./pm3 -I./hardnested
|
||||
LDFLAGS = -llzma -lpthread -lm
|
||||
|
||||
HARDNESTED_DIR = .
|
||||
|
||||
# Source files
|
||||
HARDNESTED_SOURCES = $(HARDNESTED_DIR)/pm3/ui.c $(HARDNESTED_DIR)/pm3/util.c \
|
||||
$(HARDNESTED_DIR)/cmdhfmfhard.c $(HARDNESTED_DIR)/pm3/commonutil.c \
|
||||
$(HARDNESTED_DIR)/crapto1.c $(HARDNESTED_DIR)/crypto1.c \
|
||||
$(HARDNESTED_DIR)/hardnested/hardnested_bf_core.c \
|
||||
$(HARDNESTED_DIR)/hardnested/hardnested_bruteforce.c \
|
||||
$(HARDNESTED_DIR)/hardnested/hardnested_bitarray_core.c \
|
||||
$(HARDNESTED_DIR)/hardnested/tables.c \
|
||||
$(HARDNESTED_DIR)/pm3/util_posix.c
|
||||
|
||||
# Object files
|
||||
HARDNESTED_OBJECTS = $(HARDNESTED_SOURCES:.c=.o)
|
||||
|
||||
# Dependency files
|
||||
HARDNESTED_DEPS = $(HARDNESTED_SOURCES:.c=.d)
|
||||
|
||||
# Executable target
|
||||
EXECUTABLE = hardnested_main
|
||||
|
||||
# Targets
|
||||
all: $(EXECUTABLE)
|
||||
|
||||
$(EXECUTABLE): hardnested_main.c $(HARDNESTED_OBJECTS)
|
||||
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(HARDNESTED_OBJECTS) $(HARDNESTED_DEPS) $(EXECUTABLE)
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
# Include dependencies
|
||||
-include $(HARDNESTED_OBJECTS:.o=.d)
|
||||
|
||||
# Generate dependencies
|
||||
%.d: %.c
|
||||
@$(CC) -MM $(CFLAGS) $< > $@.$$$$; \
|
||||
sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
|
||||
rm -f $@.$$$$
|
||||
+1980
File diff suppressed because it is too large
Load Diff
+30
@@ -0,0 +1,30 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// See LICENSE.txt for the text of the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
// hf mf hardnested command
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef CMDHFMFHARD_H__
|
||||
#define CMDHFMFHARD_H__
|
||||
|
||||
#include "pm3/common.h"
|
||||
|
||||
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);
|
||||
void hardnested_print_progress(uint32_t nonces, const char *activity, float brute_force, uint64_t min_diff_print_time);
|
||||
|
||||
#endif
|
||||
|
||||
Executable
+291
@@ -0,0 +1,291 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2008-2014 bla <blapost@gmail.com>
|
||||
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// See LICENSE.txt for the text of the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "crapto1.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "parity.h"
|
||||
|
||||
/** update_contribution
|
||||
* helper, calculates the partial linear feedback contributions and puts in MSB
|
||||
*/
|
||||
static inline void update_contribution(uint32_t *item, const uint32_t mask1, const uint32_t mask2) {
|
||||
uint32_t p = *item >> 25;
|
||||
|
||||
p = p << 1 | (evenparity32(*item & mask1));
|
||||
p = p << 1 | (evenparity32(*item & mask2));
|
||||
*item = p << 24 | (*item & 0xffffff);
|
||||
}
|
||||
|
||||
/** extend_table
|
||||
* using a bit of the keystream extend the table of possible lfsr states
|
||||
*/
|
||||
static inline void extend_table(uint32_t *tbl, uint32_t **end, int bit, int m1, int m2, uint32_t in) {
|
||||
in <<= 24;
|
||||
for (*tbl <<= 1; tbl <= *end; *++tbl <<= 1)
|
||||
if (filter(*tbl) ^ filter(*tbl | 1)) {
|
||||
*tbl |= filter(*tbl) ^ bit;
|
||||
update_contribution(tbl, m1, m2);
|
||||
*tbl ^= in;
|
||||
} else if (filter(*tbl) == bit) {
|
||||
*++*end = tbl[1];
|
||||
tbl[1] = tbl[0] | 1;
|
||||
update_contribution(tbl, m1, m2);
|
||||
*tbl++ ^= in;
|
||||
update_contribution(tbl, m1, m2);
|
||||
*tbl ^= in;
|
||||
} else
|
||||
*tbl-- = *(*end)--;
|
||||
}
|
||||
/** extend_table_simple
|
||||
* using a bit of the keystream extend the table of possible lfsr states
|
||||
*/
|
||||
static inline void extend_table_simple(uint32_t *tbl, uint32_t **end, int bit) {
|
||||
for (*tbl <<= 1; tbl <= *end; *++tbl <<= 1) {
|
||||
if (filter(*tbl) ^ filter(*tbl | 1)) { // replace
|
||||
*tbl |= filter(*tbl) ^ bit;
|
||||
} else if (filter(*tbl) == bit) { // insert
|
||||
*++*end = *++tbl;
|
||||
*tbl = tbl[-1] | 1;
|
||||
} else { // drop
|
||||
*tbl-- = *(*end)--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** lfsr_rollback_bit
|
||||
* Rollback the shift register in order to get previous states
|
||||
*/
|
||||
uint8_t lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb) {
|
||||
int out;
|
||||
uint8_t ret;
|
||||
uint32_t t;
|
||||
|
||||
s->odd &= 0xffffff;
|
||||
t = s->odd, s->odd = s->even, s->even = t;
|
||||
|
||||
out = s->even & 1;
|
||||
out ^= LF_POLY_EVEN & (s->even >>= 1);
|
||||
out ^= LF_POLY_ODD & s->odd;
|
||||
out ^= !!in;
|
||||
out ^= (ret = filter(s->odd)) & (!!fb);
|
||||
|
||||
s->even |= (evenparity32(out)) << 23;
|
||||
return ret;
|
||||
}
|
||||
/** lfsr_rollback_byte
|
||||
* Rollback the shift register in order to get previous states
|
||||
*/
|
||||
uint8_t lfsr_rollback_byte(struct Crypto1State *s, uint32_t in, int fb) {
|
||||
uint8_t ret = 0;
|
||||
ret |= lfsr_rollback_bit(s, BIT(in, 7), fb) << 7;
|
||||
ret |= lfsr_rollback_bit(s, BIT(in, 6), fb) << 6;
|
||||
ret |= lfsr_rollback_bit(s, BIT(in, 5), fb) << 5;
|
||||
ret |= lfsr_rollback_bit(s, BIT(in, 4), fb) << 4;
|
||||
ret |= lfsr_rollback_bit(s, BIT(in, 3), fb) << 3;
|
||||
ret |= lfsr_rollback_bit(s, BIT(in, 2), fb) << 2;
|
||||
ret |= lfsr_rollback_bit(s, BIT(in, 1), fb) << 1;
|
||||
ret |= lfsr_rollback_bit(s, BIT(in, 0), fb) << 0;
|
||||
return ret;
|
||||
}
|
||||
/** lfsr_rollback_word
|
||||
* Rollback the shift register in order to get previous states
|
||||
*/
|
||||
uint32_t lfsr_rollback_word(struct Crypto1State *s, uint32_t in, int fb) {
|
||||
|
||||
uint32_t ret = 0;
|
||||
// note: xor args have been swapped because some compilers emit a warning
|
||||
// for 10^x and 2^x as possible misuses for exponentiation. No comment.
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 31), fb) << (24 ^ 31);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 30), fb) << (24 ^ 30);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 29), fb) << (24 ^ 29);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 28), fb) << (24 ^ 28);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 27), fb) << (24 ^ 27);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 26), fb) << (24 ^ 26);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 25), fb) << (24 ^ 25);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 24), fb) << (24 ^ 24);
|
||||
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 23), fb) << (24 ^ 23);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 22), fb) << (24 ^ 22);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 21), fb) << (24 ^ 21);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 20), fb) << (24 ^ 20);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 19), fb) << (24 ^ 19);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 18), fb) << (24 ^ 18);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 17), fb) << (24 ^ 17);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 16), fb) << (24 ^ 16);
|
||||
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 15), fb) << (24 ^ 15);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 14), fb) << (24 ^ 14);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 13), fb) << (24 ^ 13);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 12), fb) << (24 ^ 12);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 11), fb) << (24 ^ 11);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 10), fb) << (24 ^ 10);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 9), fb) << (24 ^ 9);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 8), fb) << (24 ^ 8);
|
||||
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 7), fb) << (24 ^ 7);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 6), fb) << (24 ^ 6);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 5), fb) << (24 ^ 5);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 4), fb) << (24 ^ 4);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 3), fb) << (24 ^ 3);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 2), fb) << (24 ^ 2);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 1), fb) << (24 ^ 1);
|
||||
ret |= lfsr_rollback_bit(s, BEBIT(in, 0), fb) << (24 ^ 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** nonce_distance
|
||||
* x,y valid tag nonces, then prng_successor(x, nonce_distance(x, y)) = y
|
||||
*/
|
||||
static uint16_t *dist = 0;
|
||||
int nonce_distance(uint32_t from, uint32_t to) {
|
||||
if (!dist) {
|
||||
// allocation 2bytes * 0xFFFF times.
|
||||
dist = calloc(2 << 16, sizeof(uint8_t));
|
||||
if (!dist)
|
||||
return -1;
|
||||
uint16_t x = 1;
|
||||
for (uint16_t i = 1; i; ++i) {
|
||||
dist[(x & 0xff) << 8 | x >> 8] = i;
|
||||
x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15;
|
||||
}
|
||||
}
|
||||
return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535;
|
||||
}
|
||||
|
||||
/** validate_prng_nonce
|
||||
* Determine if nonce is deterministic. ie: Suspectable to Darkside attack.
|
||||
* returns
|
||||
* true = weak prng
|
||||
* false = hardend prng
|
||||
*/
|
||||
bool validate_prng_nonce(uint32_t nonce) {
|
||||
// init prng table:
|
||||
if (nonce_distance(nonce, nonce) == -1)
|
||||
return false;
|
||||
return ((65535 - dist[nonce >> 16] + dist[nonce & 0xffff]) % 65535) == 16;
|
||||
}
|
||||
|
||||
static uint32_t fastfwd[2][8] = {
|
||||
{ 0, 0x4BC53, 0xECB1, 0x450E2, 0x25E29, 0x6E27A, 0x2B298, 0x60ECB},
|
||||
{ 0, 0x1D962, 0x4BC53, 0x56531, 0xECB1, 0x135D3, 0x450E2, 0x58980}
|
||||
};
|
||||
|
||||
/** lfsr_prefix_ks
|
||||
*
|
||||
* Is an exported helper function from the common prefix attack
|
||||
* Described in the "dark side" paper. It returns an -1 terminated array
|
||||
* of possible partial(21 bit) secret state.
|
||||
* The required keystream(ks) needs to contain the keystream that was used to
|
||||
* encrypt the NACK which is observed when varying only the 3 last bits of Nr
|
||||
* only correct iff [NR_3] ^ NR_3 does not depend on Nr_3
|
||||
*/
|
||||
uint32_t *lfsr_prefix_ks(const uint8_t ks[8], int isodd) {
|
||||
uint32_t *candidates = calloc(4 << 10, sizeof(uint8_t));
|
||||
if (!candidates) return 0;
|
||||
|
||||
int size = 0;
|
||||
|
||||
for (int i = 0; i < 1 << 21; ++i) {
|
||||
int good = 1;
|
||||
for (uint32_t c = 0; good && c < 8; ++c) {
|
||||
uint32_t entry = i ^ fastfwd[isodd][c];
|
||||
good &= (BIT(ks[c], isodd) == filter(entry >> 1));
|
||||
good &= (BIT(ks[c], isodd + 2) == filter(entry));
|
||||
}
|
||||
if (good)
|
||||
candidates[size++] = i;
|
||||
}
|
||||
|
||||
candidates[size] = -1;
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
/** check_pfx_parity
|
||||
* helper function which eliminates possible secret states using parity bits
|
||||
*/
|
||||
static struct Crypto1State *check_pfx_parity(uint32_t prefix, uint32_t rresp, uint8_t parities[8][8], uint32_t odd, uint32_t even, struct Crypto1State *sl, uint32_t no_par) {
|
||||
uint32_t good = 1;
|
||||
|
||||
for (uint32_t c = 0; good && c < 8; ++c) {
|
||||
sl->odd = odd ^ fastfwd[1][c];
|
||||
sl->even = even ^ fastfwd[0][c];
|
||||
|
||||
lfsr_rollback_bit(sl, 0, 0);
|
||||
lfsr_rollback_bit(sl, 0, 0);
|
||||
|
||||
uint32_t ks3 = lfsr_rollback_bit(sl, 0, 0);
|
||||
uint32_t ks2 = lfsr_rollback_word(sl, 0, 0);
|
||||
uint32_t ks1 = lfsr_rollback_word(sl, prefix | c << 5, 1);
|
||||
|
||||
if (no_par)
|
||||
break;
|
||||
|
||||
uint32_t nr = ks1 ^ (prefix | c << 5);
|
||||
uint32_t rr = ks2 ^ rresp;
|
||||
|
||||
good &= evenparity32(nr & 0x000000ff) ^ parities[c][3] ^ BIT(ks2, 24);
|
||||
good &= evenparity32(rr & 0xff000000) ^ parities[c][4] ^ BIT(ks2, 16);
|
||||
good &= evenparity32(rr & 0x00ff0000) ^ parities[c][5] ^ BIT(ks2, 8);
|
||||
good &= evenparity32(rr & 0x0000ff00) ^ parities[c][6] ^ BIT(ks2, 0);
|
||||
good &= evenparity32(rr & 0x000000ff) ^ parities[c][7] ^ ks3;
|
||||
}
|
||||
|
||||
return sl + good;
|
||||
}
|
||||
|
||||
#if !defined(__arm__) || defined(__linux__) || defined(_WIN32) || defined(__APPLE__) // bare metal ARM Proxmark lacks malloc()/free()
|
||||
/** lfsr_common_prefix
|
||||
* Implementation of the common prefix attack.
|
||||
* Requires the 28 bit constant prefix used as reader nonce (pfx)
|
||||
* The reader response used (rr)
|
||||
* The keystream used to encrypt the observed NACK's (ks)
|
||||
* The parity bits (par)
|
||||
* It returns a zero terminated list of possible cipher states after the
|
||||
* tag nonce was fed in
|
||||
*/
|
||||
|
||||
struct Crypto1State *lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8], uint32_t no_par) {
|
||||
struct Crypto1State *statelist, *s;
|
||||
uint32_t *odd, *even, *o, *e, top;
|
||||
|
||||
odd = lfsr_prefix_ks(ks, 1);
|
||||
even = lfsr_prefix_ks(ks, 0);
|
||||
|
||||
s = statelist = calloc(1, (sizeof * statelist) << 24); // was << 20. Need more for no_par special attack. Enough???
|
||||
if (!s || !odd || !even) {
|
||||
free(statelist);
|
||||
statelist = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (o = odd; *o + 1; ++o)
|
||||
for (e = even; *e + 1; ++e)
|
||||
for (top = 0; top < 64; ++top) {
|
||||
*o += 1 << 21;
|
||||
*e += (!(top & 7) + 1) << 21;
|
||||
s = check_pfx_parity(pfx, rr, par, *o, *e, s, no_par);
|
||||
}
|
||||
|
||||
s->odd = s->even = 0;
|
||||
out:
|
||||
free(odd);
|
||||
free(even);
|
||||
return statelist;
|
||||
}
|
||||
#endif
|
||||
Executable
+70
@@ -0,0 +1,70 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2008-2014 bla <blapost@gmail.com>
|
||||
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// See LICENSE.txt for the text of the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
#ifndef crypto01_INCLUDED
|
||||
#define crypto01_INCLUDED
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
struct Crypto1State {uint32_t odd, even;};
|
||||
void crypto1_init(struct Crypto1State *state, uint64_t key);
|
||||
void crypto1_deinit(struct Crypto1State *);
|
||||
struct Crypto1State *crypto1_create(uint64_t key);
|
||||
void crypto1_destroy(struct Crypto1State *);
|
||||
void crypto1_get_lfsr(struct Crypto1State *, uint64_t *);
|
||||
uint8_t crypto1_bit(struct Crypto1State *, uint8_t, int);
|
||||
uint8_t crypto1_byte(struct Crypto1State *, uint8_t, int);
|
||||
uint32_t crypto1_word(struct Crypto1State *, uint32_t, int);
|
||||
uint32_t prng_successor(uint32_t x, uint32_t n);
|
||||
|
||||
struct Crypto1State *
|
||||
lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8], uint32_t no_par);
|
||||
uint32_t *lfsr_prefix_ks(const uint8_t ks[8], int isodd);
|
||||
|
||||
|
||||
uint8_t lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb);
|
||||
uint8_t lfsr_rollback_byte(struct Crypto1State *s, uint32_t in, int fb);
|
||||
uint32_t lfsr_rollback_word(struct Crypto1State *s, uint32_t in, int fb);
|
||||
int nonce_distance(uint32_t from, uint32_t to);
|
||||
bool validate_prng_nonce(uint32_t nonce);
|
||||
#define FOREACH_VALID_NONCE(N, FILTER, FSIZE)\
|
||||
uint32_t __n = 0,__M = 0, N = 0;\
|
||||
int __i;\
|
||||
for(; __n < 1 << 16; N = prng_successor(__M = ++__n, 16))\
|
||||
for(__i = FSIZE - 1; __i >= 0; __i--)\
|
||||
if(BIT(FILTER, __i) ^ evenparity32(__M & 0xFF01))\
|
||||
break;\
|
||||
else if(__i)\
|
||||
__M = prng_successor(__M, (__i == 7) ? 48 : 8);\
|
||||
else
|
||||
|
||||
#define LF_POLY_ODD (0x29CE5C)
|
||||
#define LF_POLY_EVEN (0x870804)
|
||||
#define BIT(x, n) ((x) >> (n) & 1)
|
||||
#define BEBIT(x, n) BIT(x, (n) ^ 24)
|
||||
static inline int filter(uint32_t const x) {
|
||||
uint32_t f;
|
||||
|
||||
f = 0xf22c0 >> (x & 0xf) & 16;
|
||||
f |= 0x6c9c0 >> (x >> 4 & 0xf) & 8;
|
||||
f |= 0x3c8b0 >> (x >> 8 & 0xf) & 4;
|
||||
f |= 0x1e458 >> (x >> 12 & 0xf) & 2;
|
||||
f |= 0x0d938 >> (x >> 16 & 0xf) & 1;
|
||||
return BIT(0xEC57E80A, f);
|
||||
}
|
||||
#endif
|
||||
Executable
+165
@@ -0,0 +1,165 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2008-2014 bla <blapost@gmail.com>
|
||||
// Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// See LICENSE.txt for the text of the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
#include <stdlib.h>
|
||||
#include "crapto1.h"
|
||||
#include "parity.h"
|
||||
|
||||
#ifdef __OPTIMIZE_SIZE__
|
||||
int filter(uint32_t const x) {
|
||||
uint32_t f;
|
||||
|
||||
f = 0xf22c0 >> (x & 0xf) & 16;
|
||||
f |= 0x6c9c0 >> (x >> 4 & 0xf) & 8;
|
||||
f |= 0x3c8b0 >> (x >> 8 & 0xf) & 4;
|
||||
f |= 0x1e458 >> (x >> 12 & 0xf) & 2;
|
||||
f |= 0x0d938 >> (x >> 16 & 0xf) & 1;
|
||||
return BIT(0xEC57E80A, f);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SWAPENDIAN(x)\
|
||||
(x = (x >> 8 & 0xff00ff) | (x & 0xff00ff) << 8, x = x >> 16 | x << 16)
|
||||
|
||||
void crypto1_init(struct Crypto1State *state, uint64_t key) {
|
||||
if (state == NULL)
|
||||
return;
|
||||
state->odd = 0;
|
||||
state->even = 0;
|
||||
for (int i = 47; i > 0; i -= 2) {
|
||||
state->odd = state->odd << 1 | BIT(key, (i - 1) ^ 7);
|
||||
state->even = state->even << 1 | BIT(key, i ^ 7);
|
||||
}
|
||||
}
|
||||
|
||||
void crypto1_deinit(struct Crypto1State *state) {
|
||||
state->odd = 0;
|
||||
state->even = 0;
|
||||
}
|
||||
|
||||
#if !defined(__arm__) || defined(__linux__) || defined(_WIN32) || defined(__APPLE__) // bare metal ARM Proxmark lacks calloc()/free()
|
||||
|
||||
struct Crypto1State *crypto1_create(uint64_t key) {
|
||||
struct Crypto1State *state = calloc(sizeof(*state), sizeof(uint8_t));
|
||||
if (!state) return NULL;
|
||||
crypto1_init(state, key);
|
||||
return state;
|
||||
}
|
||||
|
||||
void crypto1_destroy(struct Crypto1State *state) {
|
||||
free(state);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void crypto1_get_lfsr(struct Crypto1State *state, uint64_t *lfsr) {
|
||||
int i;
|
||||
for (*lfsr = 0, i = 23; i >= 0; --i) {
|
||||
*lfsr = *lfsr << 1 | BIT(state->odd, i ^ 3);
|
||||
*lfsr = *lfsr << 1 | BIT(state->even, i ^ 3);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t crypto1_bit(struct Crypto1State *s, uint8_t in, int is_encrypted) {
|
||||
uint32_t feedin, t;
|
||||
uint8_t ret = filter(s->odd);
|
||||
|
||||
feedin = ret & (!!is_encrypted);
|
||||
feedin ^= !!in;
|
||||
feedin ^= LF_POLY_ODD & s->odd;
|
||||
feedin ^= LF_POLY_EVEN & s->even;
|
||||
s->even = s->even << 1 | (evenparity32(feedin));
|
||||
|
||||
t = s->odd;
|
||||
s->odd = s->even;
|
||||
s->even = t;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint8_t crypto1_byte(struct Crypto1State *s, uint8_t in, int is_encrypted) {
|
||||
uint8_t ret = 0;
|
||||
ret |= crypto1_bit(s, BIT(in, 0), is_encrypted) << 0;
|
||||
ret |= crypto1_bit(s, BIT(in, 1), is_encrypted) << 1;
|
||||
ret |= crypto1_bit(s, BIT(in, 2), is_encrypted) << 2;
|
||||
ret |= crypto1_bit(s, BIT(in, 3), is_encrypted) << 3;
|
||||
ret |= crypto1_bit(s, BIT(in, 4), is_encrypted) << 4;
|
||||
ret |= crypto1_bit(s, BIT(in, 5), is_encrypted) << 5;
|
||||
ret |= crypto1_bit(s, BIT(in, 6), is_encrypted) << 6;
|
||||
ret |= crypto1_bit(s, BIT(in, 7), is_encrypted) << 7;
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t crypto1_word(struct Crypto1State *s, uint32_t in, int is_encrypted) {
|
||||
uint32_t ret = 0;
|
||||
// note: xor args have been swapped because some compilers emit a warning
|
||||
// for 10^x and 2^x as possible misuses for exponentiation. No comment.
|
||||
ret |= crypto1_bit(s, BEBIT(in, 0), is_encrypted) << (24 ^ 0);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 1), is_encrypted) << (24 ^ 1);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 2), is_encrypted) << (24 ^ 2);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 3), is_encrypted) << (24 ^ 3);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 4), is_encrypted) << (24 ^ 4);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 5), is_encrypted) << (24 ^ 5);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 6), is_encrypted) << (24 ^ 6);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 7), is_encrypted) << (24 ^ 7);
|
||||
|
||||
ret |= crypto1_bit(s, BEBIT(in, 8), is_encrypted) << (24 ^ 8);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 9), is_encrypted) << (24 ^ 9);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 10), is_encrypted) << (24 ^ 10);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 11), is_encrypted) << (24 ^ 11);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 12), is_encrypted) << (24 ^ 12);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 13), is_encrypted) << (24 ^ 13);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 14), is_encrypted) << (24 ^ 14);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 15), is_encrypted) << (24 ^ 15);
|
||||
|
||||
ret |= crypto1_bit(s, BEBIT(in, 16), is_encrypted) << (24 ^ 16);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 17), is_encrypted) << (24 ^ 17);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 18), is_encrypted) << (24 ^ 18);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 19), is_encrypted) << (24 ^ 19);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 20), is_encrypted) << (24 ^ 20);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 21), is_encrypted) << (24 ^ 21);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 22), is_encrypted) << (24 ^ 22);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 23), is_encrypted) << (24 ^ 23);
|
||||
|
||||
ret |= crypto1_bit(s, BEBIT(in, 24), is_encrypted) << (24 ^ 24);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 25), is_encrypted) << (24 ^ 25);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 26), is_encrypted) << (24 ^ 26);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 27), is_encrypted) << (24 ^ 27);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 28), is_encrypted) << (24 ^ 28);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 29), is_encrypted) << (24 ^ 29);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 30), is_encrypted) << (24 ^ 30);
|
||||
ret |= crypto1_bit(s, BEBIT(in, 31), is_encrypted) << (24 ^ 31);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* prng_successor
|
||||
* helper used to obscure the keystream during authentication
|
||||
*/
|
||||
uint32_t prng_successor(uint32_t x, uint32_t n) {
|
||||
SWAPENDIAN(x);
|
||||
while (n--)
|
||||
x = x >> 1 | (x >> 16 ^ x >> 18 ^ x >> 19 ^ x >> 21) << 31;
|
||||
|
||||
return SWAPENDIAN(x);
|
||||
}
|
||||
|
||||
int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, const uint8_t *parity) {
|
||||
return (
|
||||
(oddparity8((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity8((NtEnc >> 24) & 0xFF) ^ BIT(Ks1, 16))) && \
|
||||
(oddparity8((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity8((NtEnc >> 16) & 0xFF) ^ BIT(Ks1, 8))) && \
|
||||
(oddparity8((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity8((NtEnc >> 8) & 0xFF) ^ BIT(Ks1, 0)))
|
||||
) ? 1 : 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,96 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2016, 2017 by piwi
|
||||
//
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
||||
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||
// the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Implements a card only attack based on crypto text (encrypted nonces
|
||||
// received during a nested authentication) only. Unlike other card only
|
||||
// attacks this doesn't rely on implementation errors but only on the
|
||||
// inherent weaknesses of the crypto1 cypher. Described in
|
||||
// Carlo Meijer, Roel Verdult, "Ciphertext-only Cryptanalysis on Hardened
|
||||
// Mifare Classic Cards" in Proceedings of the 22nd ACM SIGSAC Conference on
|
||||
// Computer and Communications Security, 2015
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// brute forcing is based on @aczids bitsliced brute forcer
|
||||
// https://github.com/aczid/crypto1_bs with some modifications. Mainly:
|
||||
// - don't rollback. Start with 2nd byte of nonce instead
|
||||
// - reuse results of filter subfunctions
|
||||
// - reuse results of previous nonces if some first bits are identical
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// aczid's Copyright notice:
|
||||
//
|
||||
// Bit-sliced Crypto-1 brute-forcing implementation
|
||||
// Builds on the data structures returned by CraptEV1 craptev1_get_space(nonces, threshold, uid)
|
||||
/*
|
||||
Copyright (c) 2015-2016 Aram Verstegen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef HARDNESTED_BF_CORE_H__
|
||||
#define HARDNESTED_BF_CORE_H__
|
||||
|
||||
#include "hardnested_bruteforce.h" // statelist_t
|
||||
|
||||
#if ( defined (__i386__) || defined (__x86_64__) ) && \
|
||||
( !defined(__APPLE__) || \
|
||||
(defined(__APPLE__) && (__clang_major__ > 8 || __clang_major__ == 8 && __clang_minor__ >= 1)) )
|
||||
# define COMPILER_HAS_SIMD_X86
|
||||
# if defined(COMPILER_HAS_SIMD_X86) && ((__GNUC__ >= 5) && (__GNUC__ > 5 || __GNUC_MINOR__ > 2))
|
||||
# define COMPILER_HAS_SIMD_AVX512
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// ARM64 mandates implementation of NEON
|
||||
#if defined(__arm64__) || defined(__aarch64__)
|
||||
#define COMPILER_HAS_SIMD_NEON
|
||||
#define arm_has_neon() (true)
|
||||
// ARMv7 or older, NEON is optional and autodetection is difficult
|
||||
#elif defined(__ARM_NEON)
|
||||
#define COMPILER_HAS_SIMD_NEON
|
||||
#define arm_has_neon() (false)
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
SIMD_AUTO,
|
||||
#if defined(COMPILER_HAS_SIMD_AVX512)
|
||||
SIMD_AVX512,
|
||||
#endif
|
||||
#if defined(COMPILER_HAS_SIMD_X86)
|
||||
SIMD_AVX2,
|
||||
SIMD_AVX,
|
||||
SIMD_SSE2,
|
||||
SIMD_MMX,
|
||||
#endif
|
||||
#if defined(COMPILER_HAS_SIMD_NEON)
|
||||
SIMD_NEON,
|
||||
#endif
|
||||
SIMD_NONE,
|
||||
} SIMDExecInstr;
|
||||
void SetSIMDInstr(SIMDExecInstr instr);
|
||||
SIMDExecInstr GetSIMDInstrAuto(void);
|
||||
|
||||
uint64_t crack_states_bitsliced(uint32_t cuid, uint8_t *best_first_bytes, statelist_t *p, uint32_t *keys_found, uint64_t *num_keys_tested, uint32_t nonces_to_bruteforce, uint8_t *bf_test_nonce_2nd_byte, noncelist_t *nonces);
|
||||
void bitslice_test_nonces(uint32_t nonces_to_bruteforce, uint32_t *bf_test_nonce, uint8_t *bf_test_nonce_par);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,69 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2016, 2017 by piwi
|
||||
//
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
||||
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||
// the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Implements a card only attack based on crypto text (encrypted nonces
|
||||
// received during a nested authentication) only. Unlike other card only
|
||||
// attacks this doesn't rely on implementation errors but only on the
|
||||
// inherent weaknesses of the crypto1 cypher. Described in
|
||||
// Carlo Meijer, Roel Verdult, "Ciphertext-only Cryptanalysis on Hardened
|
||||
// Mifare Classic Cards" in Proceedings of the 22nd ACM SIGSAC Conference on
|
||||
// Computer and Communications Security, 2015
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// brute forcing is based on @aczids bitsliced brute forcer
|
||||
// https://github.com/aczid/crypto1_bs with some modifications. Mainly:
|
||||
// - don't rollback. Start with 2nd byte of nonce instead
|
||||
// - reuse results of filter subfunctions
|
||||
// - reuse results of previous nonces if some first bits are identical
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// aczid's Copyright notice:
|
||||
//
|
||||
// Bit-sliced Crypto-1 brute-forcing implementation
|
||||
// Builds on the data structures returned by CraptEV1 craptev1_get_space(nonces, threshold, uid)
|
||||
/*
|
||||
Copyright (c) 2015-2016 Aram Verstegen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef HARDNESTED_BITARRAY_CORE_H__
|
||||
#define HARDNESTED_BITARRAY_CORE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t *malloc_bitarray(uint32_t x);
|
||||
void free_bitarray(uint32_t *x);
|
||||
uint32_t bitcount(uint32_t a);
|
||||
uint32_t count_states(uint32_t *A);
|
||||
void bitarray_AND(uint32_t *A, uint32_t *B);
|
||||
void bitarray_low20_AND(uint32_t *A, uint32_t *B);
|
||||
uint32_t count_bitarray_AND(uint32_t *A, uint32_t *B);
|
||||
uint32_t count_bitarray_low20_AND(uint32_t *A, uint32_t *B);
|
||||
void bitarray_AND4(uint32_t *A, uint32_t *B, uint32_t *C, uint32_t *D);
|
||||
void bitarray_OR(uint32_t *A, uint32_t *B);
|
||||
uint32_t count_bitarray_AND2(uint32_t *A, uint32_t *B);
|
||||
uint32_t count_bitarray_AND3(uint32_t *A, uint32_t *B, uint32_t *C);
|
||||
uint32_t count_bitarray_AND4(uint32_t *A, uint32_t *B, uint32_t *C, uint32_t *D);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,477 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2016, 2017 by piwi
|
||||
//
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
||||
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||
// the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Implements a card only attack based on crypto text (encrypted nonces
|
||||
// received during a nested authentication) only. Unlike other card only
|
||||
// attacks this doesn't rely on implementation errors but only on the
|
||||
// inherent weaknesses of the crypto1 cypher. Described in
|
||||
// Carlo Meijer, Roel Verdult, "Ciphertext-only Cryptanalysis on Hardened
|
||||
// Mifare Classic Cards" in Proceedings of the 22nd ACM SIGSAC Conference on
|
||||
// Computer and Communications Security, 2015
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// brute forcing is based on @aczids bitsliced brute forcer
|
||||
// https://github.com/aczid/crypto1_bs with some modifications. Mainly:
|
||||
// - don't rollback. Start with 2nd byte of nonce instead
|
||||
// - reuse results of filter subfunctions
|
||||
// - reuse results of previous nonces if some first bits are identical
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
// aczid's Copyright notice:
|
||||
//
|
||||
// Bit-sliced Crypto-1 brute-forcing implementation
|
||||
// Builds on the data structures returned by CraptEV1 craptev1_get_space(nonces, threshold, uid)
|
||||
/*
|
||||
Copyright (c) 2015-2016 Aram Verstegen
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "hardnested_bruteforce.h"
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "hardnested_bf_core.h"
|
||||
#include "../pm3/ui.h"
|
||||
#include "../pm3/util_posix.h"
|
||||
#include "../crapto1.h"
|
||||
#include "../parity.h"
|
||||
#include "../cmdhfmfhard.h"
|
||||
#include "hardnested_benchmark_data.h"
|
||||
|
||||
#define NUM_BRUTE_FORCE_THREADS (num_CPUs())
|
||||
#ifdef _WIN32
|
||||
#define NUM_BRUTE_FORCE_THREADS_ALLOC 128
|
||||
#else
|
||||
#define NUM_BRUTE_FORCE_THREADS_ALLOC (num_CPUs())
|
||||
#endif
|
||||
#define DEFAULT_BRUTE_FORCE_RATE (120000000.0) // if benchmark doesn't succeed
|
||||
#define TEST_BENCH_SIZE (6000) // number of odd and even states for brute force benchmark
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <Windows.h>
|
||||
#include <share.h>
|
||||
#include <io.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#define atomic_add(num, val) (InterlockedExchangeAdd64(num, val) + val)
|
||||
FILE *fmemopen(void *buf, size_t len, const char *type)
|
||||
{
|
||||
int fd;
|
||||
FILE *fp;
|
||||
char tp[MAX_PATH - 13];
|
||||
char fn[MAX_PATH + 1];
|
||||
int * pfd = &fd;
|
||||
int retner = -1;
|
||||
char tfname[] = "MemTF_";
|
||||
if (!GetTempPathA(sizeof(tp), tp))
|
||||
return NULL;
|
||||
if (!GetTempFileNameA(tp, tfname, 0, fn))
|
||||
return NULL;
|
||||
retner = _sopen_s(pfd, fn, _O_CREAT | _O_SHORT_LIVED | _O_TEMPORARY | _O_RDWR | _O_BINARY | _O_NOINHERIT, _SH_DENYRW, _S_IREAD | _S_IWRITE);
|
||||
if (retner != 0)
|
||||
return NULL;
|
||||
if (fd == -1)
|
||||
return NULL;
|
||||
fp = _fdopen(fd, "wb+");
|
||||
if (!fp) {
|
||||
_close(fd);
|
||||
return NULL;
|
||||
}
|
||||
/*File descriptors passed into _fdopen are owned by the returned FILE * stream.If _fdopen is successful, do not call _close on the file descriptor.Calling fclose on the returned FILE * also closes the file descriptor.*/
|
||||
fwrite(buf, len, 1, fp);
|
||||
rewind(fp);
|
||||
return fp;
|
||||
}
|
||||
#else
|
||||
#define atomic_add __sync_fetch_and_add
|
||||
#ifdef _WIN32 // Non-MSVC Windows (MinGW, etc.)
|
||||
// Include the compatibility header provided via CMake
|
||||
#include "../../compat/fmemopen/libfmemopen.h"
|
||||
#endif // _WIN32 (Non-MSVC)
|
||||
|
||||
#endif
|
||||
// debugging options
|
||||
#define DEBUG_KEY_ELIMINATION 1
|
||||
// #define DEBUG_BRUTE_FORCE
|
||||
|
||||
typedef enum {
|
||||
EVEN_STATE = 0,
|
||||
ODD_STATE = 1
|
||||
} odd_even_t;
|
||||
|
||||
static uint32_t nonces_to_bruteforce = 0;
|
||||
static uint32_t bf_test_nonce[256];
|
||||
static uint8_t bf_test_nonce_2nd_byte[256];
|
||||
static uint8_t bf_test_nonce_par[256];
|
||||
static uint32_t bucket_count = 0;
|
||||
static statelist_t *buckets[128];
|
||||
static uint32_t keys_found = 0;
|
||||
static uint64_t num_keys_tested;
|
||||
static uint64_t found_bs_key = 0;
|
||||
|
||||
uint8_t trailing_zeros(uint8_t byte) {
|
||||
static const uint8_t trailing_zeros_LUT[256] = {
|
||||
8, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
|
||||
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
|
||||
};
|
||||
|
||||
return trailing_zeros_LUT[byte];
|
||||
}
|
||||
|
||||
|
||||
bool verify_key(uint32_t cuid, noncelist_t *nonces, const uint8_t *best_first_bytes, uint32_t odd, uint32_t even) {
|
||||
struct Crypto1State pcs;
|
||||
if (best_first_bytes == NULL) {
|
||||
return false;
|
||||
}
|
||||
for (uint16_t test_first_byte = 1; test_first_byte < 256; test_first_byte++) {
|
||||
noncelistentry_t *test_nonce = nonces[best_first_bytes[test_first_byte]].first;
|
||||
while (test_nonce != NULL) {
|
||||
pcs.odd = odd;
|
||||
pcs.even = even;
|
||||
lfsr_rollback_byte(&pcs, (cuid >> 24) ^ best_first_bytes[0], true);
|
||||
for (int8_t byte_pos = 3; byte_pos >= 0; byte_pos--) {
|
||||
uint8_t test_par_enc_bit = (test_nonce->par_enc >> byte_pos) & 0x01; // the encoded parity bit
|
||||
uint8_t test_byte_enc = (test_nonce->nonce_enc >> (8 * byte_pos)) & 0xff; // the encoded nonce byte
|
||||
uint8_t test_byte_dec = crypto1_byte(&pcs, test_byte_enc /* ^ (cuid >> (8*byte_pos)) */, true) ^ test_byte_enc; // decode the nonce byte
|
||||
uint8_t ks_par = filter(pcs.odd); // the keystream bit to encode/decode the parity bit
|
||||
uint8_t test_par_enc2 = ks_par ^ evenparity8(test_byte_dec); // determine the decoded byte's parity and encode it
|
||||
if (test_par_enc_bit != test_par_enc2) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
test_nonce = test_nonce->next;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static void *
|
||||
#ifdef __has_attribute
|
||||
#if __has_attribute(force_align_arg_pointer)
|
||||
__attribute__((force_align_arg_pointer))
|
||||
#endif
|
||||
#endif
|
||||
crack_states_thread(void *x) {
|
||||
struct arg {
|
||||
bool silent;
|
||||
int thread_ID;
|
||||
uint32_t cuid;
|
||||
uint32_t num_acquired_nonces;
|
||||
uint64_t maximum_states;
|
||||
noncelist_t *nonces;
|
||||
uint8_t *best_first_bytes;
|
||||
} *thread_arg;
|
||||
|
||||
thread_arg = (struct arg *)x;
|
||||
const int thread_id = thread_arg->thread_ID;
|
||||
uint32_t current_bucket = thread_id;
|
||||
while (current_bucket < bucket_count) {
|
||||
statelist_t *bucket = buckets[current_bucket];
|
||||
if (bucket) {
|
||||
#if defined (DEBUG_BRUTE_FORCE)
|
||||
PrintAndLogEx(INFO, "Thread " _YELLOW_("%u") " starts working on bucket " _YELLOW_("%u") "\n", thread_id, current_bucket);
|
||||
#endif
|
||||
const uint64_t key = crack_states_bitsliced(thread_arg->cuid, thread_arg->best_first_bytes, bucket, &keys_found, &num_keys_tested, nonces_to_bruteforce, bf_test_nonce_2nd_byte, thread_arg->nonces);
|
||||
if (key != -1) {
|
||||
atomic_add(&keys_found, 1);
|
||||
atomic_add(&found_bs_key, key);
|
||||
|
||||
char progress_text[80];
|
||||
char keystr[19];
|
||||
snprintf(keystr, sizeof(keystr), "%012" PRIX64 " ", key);
|
||||
snprintf(progress_text, sizeof(progress_text), "Brute force phase completed. Key found: " _GREEN_("%s"), keystr);
|
||||
hardnested_print_progress(thread_arg->num_acquired_nonces, progress_text, 0.0, 0);
|
||||
break;
|
||||
} else if (keys_found) {
|
||||
break;
|
||||
} else {
|
||||
if (!thread_arg->silent) {
|
||||
char progress_text[80];
|
||||
snprintf(progress_text, sizeof(progress_text), "Brute force phase: %6.02f%%", 100.0 * (float)num_keys_tested / (float)(thread_arg->maximum_states));
|
||||
float remaining_bruteforce = thread_arg->nonces[thread_arg->best_first_bytes[0]].expected_num_brute_force - (float)num_keys_tested / 2;
|
||||
hardnested_print_progress(thread_arg->num_acquired_nonces, progress_text, remaining_bruteforce, 5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
current_bucket += NUM_BRUTE_FORCE_THREADS;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void prepare_bf_test_nonces(noncelist_t *nonces, uint8_t best_first_byte) {
|
||||
// we do bitsliced brute forcing with best_first_bytes[0] only.
|
||||
// Extract the corresponding 2nd bytes
|
||||
noncelistentry_t *test_nonce = nonces[best_first_byte].first;
|
||||
uint32_t i = 0;
|
||||
while (test_nonce != NULL) {
|
||||
bf_test_nonce[i] = test_nonce->nonce_enc;
|
||||
bf_test_nonce_par[i] = test_nonce->par_enc;
|
||||
bf_test_nonce_2nd_byte[i] = (test_nonce->nonce_enc >> 16) & 0xff;
|
||||
test_nonce = test_nonce->next;
|
||||
i++;
|
||||
}
|
||||
nonces_to_bruteforce = i;
|
||||
|
||||
// printf("Nonces to bruteforce: %d\n", nonces_to_bruteforce);
|
||||
// printf("Common bits of first 4 2nd nonce bytes (before sorting): %u %u %u\n",
|
||||
// trailing_zeros(bf_test_nonce_2nd_byte[1] ^ bf_test_nonce_2nd_byte[0]),
|
||||
// trailing_zeros(bf_test_nonce_2nd_byte[2] ^ bf_test_nonce_2nd_byte[1]),
|
||||
// trailing_zeros(bf_test_nonce_2nd_byte[3] ^ bf_test_nonce_2nd_byte[2]));
|
||||
|
||||
uint8_t best_4[4] = {0};
|
||||
int sum_best = -1;
|
||||
for (uint32_t n1 = 0; n1 < nonces_to_bruteforce; n1++) {
|
||||
for (uint32_t n2 = 0; n2 < nonces_to_bruteforce; n2++) {
|
||||
if (n2 != n1) {
|
||||
for (uint32_t n3 = 0; n3 < nonces_to_bruteforce; n3++) {
|
||||
if ((n3 != n2 && n3 != n1) || nonces_to_bruteforce < 3
|
||||
// && trailing_zeros(bf_test_nonce_2nd_byte[n1] ^ bf_test_nonce_2nd_byte[n2])
|
||||
// > trailing_zeros(bf_test_nonce_2nd_byte[n2] ^ bf_test_nonce_2nd_byte[n3])
|
||||
) {
|
||||
for (uint32_t n4 = 0; n4 < nonces_to_bruteforce; n4++) {
|
||||
if ((n4 != n3 && n4 != n2 && n4 != n1) || nonces_to_bruteforce < 4
|
||||
// && trailing_zeros(bf_test_nonce_2nd_byte[n2] ^ bf_test_nonce_2nd_byte[n3])
|
||||
// > trailing_zeros(bf_test_nonce_2nd_byte[n3] ^ bf_test_nonce_2nd_byte[n4])
|
||||
) {
|
||||
int sum = nonces_to_bruteforce > 1 ? trailing_zeros(bf_test_nonce_2nd_byte[n1] ^ bf_test_nonce_2nd_byte[n2]) : 0.0
|
||||
+ nonces_to_bruteforce > 2 ? trailing_zeros(bf_test_nonce_2nd_byte[n2] ^ bf_test_nonce_2nd_byte[n3]) : 0.0
|
||||
+ nonces_to_bruteforce > 3 ? trailing_zeros(bf_test_nonce_2nd_byte[n3] ^ bf_test_nonce_2nd_byte[n4]) : 0.0;
|
||||
if (sum > sum_best) {
|
||||
sum_best = sum;
|
||||
best_4[0] = n1;
|
||||
best_4[1] = n2;
|
||||
best_4[2] = n3;
|
||||
best_4[3] = n4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t bf_test_nonce_temp[4];
|
||||
uint8_t bf_test_nonce_par_temp[4];
|
||||
uint8_t bf_test_nonce_2nd_byte_temp[4];
|
||||
for (uint32_t j = 0; j < 4 && j < nonces_to_bruteforce; j++) {
|
||||
bf_test_nonce_temp[j] = bf_test_nonce[best_4[j]];
|
||||
|
||||
bf_test_nonce_par_temp[j] = bf_test_nonce_par[best_4[j]];
|
||||
bf_test_nonce_2nd_byte_temp[j] = bf_test_nonce_2nd_byte[best_4[j]];
|
||||
}
|
||||
for (uint32_t j = 0; j < 4 && j < nonces_to_bruteforce; j++) {
|
||||
bf_test_nonce[j] = bf_test_nonce_temp[j];
|
||||
bf_test_nonce_par[j] = bf_test_nonce_par_temp[j];
|
||||
bf_test_nonce_2nd_byte[j] = bf_test_nonce_2nd_byte_temp[j];
|
||||
}
|
||||
}
|
||||
|
||||
bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint32_t num_acquired_nonces, uint64_t maximum_states, noncelist_t *nonces, uint8_t *best_first_bytes, uint64_t *found_key) {
|
||||
#if defined (WRITE_BENCH_FILE)
|
||||
write_benchfile(candidates);
|
||||
#endif
|
||||
bool silent = (bf_rate != NULL);
|
||||
|
||||
keys_found = 0;
|
||||
num_keys_tested = 0;
|
||||
found_bs_key = 0;
|
||||
|
||||
bitslice_test_nonces(nonces_to_bruteforce, bf_test_nonce, bf_test_nonce_par);
|
||||
|
||||
// count number of states to go
|
||||
bucket_count = 0;
|
||||
for (statelist_t *p = candidates; p != NULL; p = p->next) {
|
||||
if (p->states[ODD_STATE] != NULL && p->states[EVEN_STATE] != NULL) {
|
||||
buckets[bucket_count] = p;
|
||||
bucket_count++;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t start_time = msclock();
|
||||
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
if (NUM_BRUTE_FORCE_THREADS < 0)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
pthread_t threads[NUM_BRUTE_FORCE_THREADS_ALLOC];
|
||||
struct args {
|
||||
bool silent;
|
||||
int thread_ID;
|
||||
uint32_t cuid;
|
||||
uint32_t num_acquired_nonces;
|
||||
uint64_t maximum_states;
|
||||
noncelist_t *nonces;
|
||||
uint8_t *best_first_bytes;
|
||||
} thread_args[NUM_BRUTE_FORCE_THREADS_ALLOC];
|
||||
|
||||
for (uint32_t i = 0; i < NUM_BRUTE_FORCE_THREADS; i++) {
|
||||
thread_args[i].thread_ID = i;
|
||||
thread_args[i].silent = silent;
|
||||
thread_args[i].cuid = cuid;
|
||||
thread_args[i].num_acquired_nonces = num_acquired_nonces;
|
||||
thread_args[i].maximum_states = maximum_states;
|
||||
thread_args[i].nonces = nonces;
|
||||
thread_args[i].best_first_bytes = best_first_bytes;
|
||||
pthread_create(&threads[i], NULL, crack_states_thread, (void *)&thread_args[i]);
|
||||
}
|
||||
for (uint32_t i = 0; i < NUM_BRUTE_FORCE_THREADS; i++) {
|
||||
pthread_join(threads[i], 0);
|
||||
}
|
||||
|
||||
uint64_t elapsed_time = msclock() - start_time;
|
||||
|
||||
if (bf_rate != NULL)
|
||||
*bf_rate = (float)num_keys_tested / ((float)elapsed_time / 1000.0);
|
||||
|
||||
if (keys_found > 0)
|
||||
*found_key = found_bs_key;
|
||||
|
||||
return (keys_found != 0);
|
||||
}
|
||||
|
||||
|
||||
static bool read_bench_data(statelist_t *test_candidates) {
|
||||
size_t bytes_read = 0;
|
||||
uint32_t temp = 0;
|
||||
uint32_t num_states = 0;
|
||||
uint32_t states_read = 0;
|
||||
|
||||
FILE *benchfile = fmemopen(client_resources_hardnested_bf_bench_data_bin, client_resources_hardnested_bf_bench_data_bin_len, "rb");
|
||||
if (benchfile == NULL) {
|
||||
return false;
|
||||
}
|
||||
bytes_read = fread(&nonces_to_bruteforce, 1, sizeof(nonces_to_bruteforce), benchfile);
|
||||
if (bytes_read != sizeof(nonces_to_bruteforce)) {
|
||||
fclose(benchfile);
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0; i < nonces_to_bruteforce && i < 256; i++) {
|
||||
bytes_read = fread(&bf_test_nonce[i], 1, sizeof(uint32_t), benchfile);
|
||||
if (bytes_read != sizeof(uint32_t)) {
|
||||
fclose(benchfile);
|
||||
return false;
|
||||
}
|
||||
bf_test_nonce_2nd_byte[i] = (bf_test_nonce[i] >> 16) & 0xff;
|
||||
bytes_read = fread(&bf_test_nonce_par[i], 1, sizeof(uint8_t), benchfile);
|
||||
if (bytes_read != sizeof(uint8_t)) {
|
||||
fclose(benchfile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bytes_read = fread(&num_states, 1, sizeof(uint32_t), benchfile);
|
||||
if (bytes_read != sizeof(uint32_t)) {
|
||||
fclose(benchfile);
|
||||
return false;
|
||||
}
|
||||
for (states_read = 0; states_read < MIN(num_states, TEST_BENCH_SIZE); states_read++) {
|
||||
bytes_read = fread(test_candidates->states[EVEN_STATE] + states_read, 1, sizeof(uint32_t), benchfile);
|
||||
if (bytes_read != sizeof(uint32_t)) {
|
||||
fclose(benchfile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (uint32_t i = states_read; i < TEST_BENCH_SIZE; i++) {
|
||||
test_candidates->states[EVEN_STATE][i] = test_candidates->states[EVEN_STATE][i - states_read];
|
||||
}
|
||||
for (uint32_t i = states_read; i < num_states; i++) {
|
||||
bytes_read = fread(&temp, 1, sizeof(uint32_t), benchfile);
|
||||
if (bytes_read != sizeof(uint32_t)) {
|
||||
fclose(benchfile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (states_read = 0; states_read < MIN(num_states, TEST_BENCH_SIZE); states_read++) {
|
||||
bytes_read = fread(test_candidates->states[ODD_STATE] + states_read, 1, sizeof(uint32_t), benchfile);
|
||||
if (bytes_read != sizeof(uint32_t)) {
|
||||
fclose(benchfile);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (uint32_t i = states_read; i < TEST_BENCH_SIZE; i++) {
|
||||
test_candidates->states[ODD_STATE][i] = test_candidates->states[ODD_STATE][i - states_read];
|
||||
}
|
||||
|
||||
fclose(benchfile);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
float brute_force_benchmark(void) {
|
||||
statelist_t test_candidates[NUM_BRUTE_FORCE_THREADS_ALLOC];
|
||||
|
||||
test_candidates[0].states[ODD_STATE] = calloc(1, (TEST_BENCH_SIZE + 1) * sizeof(uint32_t));
|
||||
test_candidates[0].states[EVEN_STATE] = calloc(1, (TEST_BENCH_SIZE + 1) * sizeof(uint32_t));
|
||||
for (uint32_t i = 0; i < NUM_BRUTE_FORCE_THREADS - 1; i++) {
|
||||
test_candidates[i].next = test_candidates + i + 1;
|
||||
test_candidates[i + 1].states[ODD_STATE] = test_candidates[0].states[ODD_STATE];
|
||||
test_candidates[i + 1].states[EVEN_STATE] = test_candidates[0].states[EVEN_STATE];
|
||||
}
|
||||
test_candidates[NUM_BRUTE_FORCE_THREADS - 1].next = NULL;
|
||||
|
||||
if (!read_bench_data(test_candidates)) {
|
||||
return DEFAULT_BRUTE_FORCE_RATE;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < NUM_BRUTE_FORCE_THREADS; i++) {
|
||||
test_candidates[i].len[ODD_STATE] = TEST_BENCH_SIZE;
|
||||
test_candidates[i].len[EVEN_STATE] = TEST_BENCH_SIZE;
|
||||
test_candidates[i].states[ODD_STATE][TEST_BENCH_SIZE] = -1;
|
||||
test_candidates[i].states[EVEN_STATE][TEST_BENCH_SIZE] = -1;
|
||||
}
|
||||
|
||||
uint64_t maximum_states = TEST_BENCH_SIZE * TEST_BENCH_SIZE * (uint64_t)NUM_BRUTE_FORCE_THREADS;
|
||||
|
||||
float bf_rate;
|
||||
uint64_t found_key = 0;
|
||||
brute_force_bs(&bf_rate, test_candidates, 0, 0, maximum_states, NULL, 0, &found_key);
|
||||
|
||||
free(test_candidates[0].states[ODD_STATE]);
|
||||
free(test_candidates[0].states[EVEN_STATE]);
|
||||
test_candidates[0].len[ODD_STATE] = 0;
|
||||
test_candidates[0].len[EVEN_STATE] = 0;
|
||||
return bf_rate;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2016, 2017 by piwi
|
||||
//
|
||||
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
||||
// at your option, any later version. See the LICENSE.txt file for the text of
|
||||
// the license.
|
||||
//-----------------------------------------------------------------------------
|
||||
// Implements a card only attack based on crypto text (encrypted nonces
|
||||
// received during a nested authentication) only. Unlike other card only
|
||||
// attacks this doesn't rely on implementation errors but only on the
|
||||
// inherent weaknesses of the crypto1 cypher. Described in
|
||||
// Carlo Meijer, Roel Verdult, "Ciphertext-only Cryptanalysis on Hardened
|
||||
// Mifare Classic Cards" in Proceedings of the 22nd ACM SIGSAC Conference on
|
||||
// Computer and Communications Security, 2015
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef HARDNESTED_BRUTEFORCE_H__
|
||||
#define HARDNESTED_BRUTEFORCE_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define NUM_SUMS 19 // number of possible sum property values
|
||||
|
||||
typedef struct guess_sum_a8 {
|
||||
float prob;
|
||||
uint64_t num_states;
|
||||
uint16_t sum_a8_idx;
|
||||
} guess_sum_a8_t;
|
||||
|
||||
typedef struct noncelistentry {
|
||||
uint32_t nonce_enc;
|
||||
uint8_t par_enc;
|
||||
void *next;
|
||||
} noncelistentry_t;
|
||||
|
||||
typedef struct noncelist {
|
||||
uint16_t num;
|
||||
uint16_t Sum;
|
||||
guess_sum_a8_t sum_a8_guess[NUM_SUMS];
|
||||
bool sum_a8_guess_dirty;
|
||||
float expected_num_brute_force;
|
||||
uint16_t BitFlips[0x400];
|
||||
uint32_t *states_bitarray[2];
|
||||
uint32_t num_states_bitarray[2];
|
||||
bool all_bitflips_dirty[2];
|
||||
noncelistentry_t *first;
|
||||
} noncelist_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t *states[2];
|
||||
uint32_t len[2];
|
||||
void *next;
|
||||
} statelist_t;
|
||||
|
||||
void prepare_bf_test_nonces(noncelist_t *nonces, uint8_t best_first_byte);
|
||||
bool brute_force_bs(float *bf_rate, statelist_t *candidates, uint32_t cuid, uint32_t num_acquired_nonces, uint64_t maximum_states, noncelist_t *nonces, uint8_t *best_first_bytes, uint64_t *found_key);
|
||||
float brute_force_benchmark(void);
|
||||
uint8_t trailing_zeros(uint8_t byte);
|
||||
bool verify_key(uint32_t cuid, noncelist_t *nonces, const uint8_t *best_first_bytes, uint32_t odd, uint32_t even);
|
||||
|
||||
#endif
|
||||
+67315
File diff suppressed because it is too large
Load Diff
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
/*
|
||||
* File: tables.h
|
||||
* Author: vk496
|
||||
*
|
||||
* Created on 15 de noviembre de 2018, 17:42
|
||||
*/
|
||||
|
||||
#ifndef TABLES_H
|
||||
#define TABLES_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <../../xz/src/liblzma/api/lzma.h>
|
||||
#include "../cmdhfmfhard.h"
|
||||
|
||||
typedef struct bitflip_info {
|
||||
uint32_t len;
|
||||
uint8_t *input_buffer;
|
||||
} bitflip_info;
|
||||
|
||||
typedef enum {
|
||||
EVEN_STATE = 0,
|
||||
ODD_STATE = 1
|
||||
} odd_even_t;
|
||||
|
||||
|
||||
bitflip_info get_bitflip(odd_even_t odd_num, uint16_t id);
|
||||
bool decompress(lzma_stream* strm);
|
||||
void lzma_init_inflate(lzma_stream *strm, uint8_t *inbuf, uint32_t inbuf_len, uint8_t *outbuf, uint32_t outbuf_len);
|
||||
void lzma_init_decoder(lzma_stream *strm);
|
||||
|
||||
#endif /* TABLES_H */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user