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:
zeusricote
2025-04-18 23:53:25 +02:00
committed by GitHub
parent d4e27b6eee
commit 2b0d02f12e
258 changed files with 133101 additions and 81 deletions
+4 -3
View File
@@ -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
+441
View File
@@ -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):
+1 -1
View File
@@ -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
+14 -3
View File
@@ -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
View File
@@ -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})
+48
View File
@@ -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 $@.$$$$
File diff suppressed because it is too large Load Diff
+30
View File
@@ -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
+291
View File
@@ -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
+70
View File
@@ -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
+165
View File
@@ -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
File diff suppressed because it is too large Load Diff
+44
View File
@@ -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