diff --git a/.github/ISSUE_TEMPLATE/checklist-for-release.md b/.github/ISSUE_TEMPLATE/checklist-for-release.md index aae8ab053..01709e9ab 100644 --- a/.github/ISSUE_TEMPLATE/checklist-for-release.md +++ b/.github/ISSUE_TEMPLATE/checklist-for-release.md @@ -22,14 +22,14 @@ assignees: doegox, iceman1001 # OS compilation and tests ```bash -make clean && make -j PLATFORM=PM3OTHER && tools/pm3test.sh -make clean && make -j PLATFORM=PM3RDV4 && tools/pm3test.sh -make clean && make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS=BTADDON && tools/pm3test.sh -make install; pushd /tmp; proxmark3 -c 'data load em4x05.pm3;lf search 1'; popd; make uninstall +make clean && make -j PLATFORM=PM3OTHER && tools/pm3_tests.sh +make clean && make -j PLATFORM=PM3RDV4 && tools/pm3_tests.sh +make clean && make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS=BTADDON && tools/pm3_tests.sh +make install; pushd /tmp; proxmark3 -c 'data load -f em4x05.pm3;lf search 1'; popd; make uninstall -( cd client; rm -rf build; mkdir build;cd build;cmake .. && make -j PLATFORM=PM3OTHER && PM3BIN=./proxmark3 ../../tools/pm3test.sh client ) -( cd client; rm -rf build; mkdir build;cd build;cmake .. && make -j PLATFORM=PM3RDV4 && PM3BIN=./proxmark3 ../../tools/pm3test.sh client ) -( cd client; rm -rf build; mkdir build;cd build;cmake .. && make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS=BTADDON && PM3BIN=./proxmark3 ../../tools/pm3test.sh client ) +( cd client; rm -rf build; mkdir build;cd build;cmake .. && make -j PLATFORM=PM3OTHER && PM3BIN=./proxmark3 ../../tools/pm3_tests.sh client ) +( cd client; rm -rf build; mkdir build;cd build;cmake .. && make -j PLATFORM=PM3RDV4 && PM3BIN=./proxmark3 ../../tools/pm3_tests.sh client ) +( cd client; rm -rf build; mkdir build;cd build;cmake .. && make -j PLATFORM=PM3RDV4 PLATFORM_EXTRAS=BTADDON && PM3BIN=./proxmark3 ../../tools/pm3_tests.sh client ) ``` - [ ] RPI Zero diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 000000000..27e53d9a5 --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,133 @@ +name: MacOS Build and Test + +on: [push, pull_request] + +jobs: + macos-make: + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set Git http.postBuffer to something high + run: git config --global http.postBuffer 524288000 + + - name: Handle homebrew quirks + run: rm -rf /usr/local/bin/2to3 + + - name: Update brew repos + run: brew update + continue-on-error: true + + - name: Tap RfidResearchGroup/proxmark3 + run: brew tap RfidResearchGroup/proxmark3 + + - name: Install dependencies + run: brew install readline qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc + + - name: Install Python dependencies + run: | + python3 -m pip install --upgrade pip + python3 -m pip install setuptools ansicolors sslcrypto + if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi + + - name: make clean + run: make clean + + - name: Build + env: + V: 1 + run: make + + - name: Test + run: make check + + macos-make-btaddon: + if: always() + needs: [macos-make] + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set Git http.postBuffer to something high + run: git config --global http.postBuffer 524288000 + + - name: Handle homebrew quirks + run: rm -rf /usr/local/bin/2to3 + + - name: Update brew repos + run: brew update + continue-on-error: true + + - name: Tap RfidResearchGroup/proxmark3 + run: brew tap RfidResearchGroup/proxmark3 + + - name: Install dependencies + run: brew install readline qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc + + - name: Install Python dependencies + run: | + python3 -m pip install --upgrade pip + python3 -m pip install setuptools ansicolors sslcrypto + if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi + + - name: make clean + run: make clean + + - name: Build + env: + V: 1 + PLATFORM_EXTRAS: BTADDON + run: make + + - name: Test + run: make check + + macos-cmake: + if: always() + needs: [macos-make, macos-make-btaddon] + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set Git http.postBuffer to something high + run: git config --global http.postBuffer 524288000 + + - name: Handle homebrew quirks + run: rm -rf /usr/local/bin/2to3 + + - name: Update brew repos + run: brew update + continue-on-error: true + + - name: Tap RfidResearchGroup/proxmark3 + run: brew tap RfidResearchGroup/proxmark3 + + - name: Install dependencies + run: brew install readline qt5 RfidResearchGroup/proxmark3/arm-none-eabi-gcc + + - name: Install Python dependencies + run: | + python3 -m pip install --upgrade pip + python3 -m pip install setuptools ansicolors sslcrypto + if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi + + - name: Prepare Build Folders + run: mkdir -p client/build + + - name: Initiate cmake environment + run: cmake .. + working-directory: client/build/ + + - name: Build + env: + VERBOSE: 1 + run: make + working-directory: client/build/ + + - name: Test + env: + CHECKARGS: "--clientbin ./client/build/proxmark3" + run: make client/check diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml new file mode 100644 index 000000000..2d2b461e3 --- /dev/null +++ b/.github/workflows/ubuntu.yml @@ -0,0 +1,106 @@ +name: Ubuntu Build and Test + +on: [push, pull_request] + +jobs: + ubuntu-make: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Update apt repos + run: sudo apt-get update + + - name: Install dependencies + run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0-dbg liblua5.2-0 lua5.2 sed + + - name: Install Python dependencies + run: | + python3 -m pip install --upgrade pip + python3 -m pip install setuptools + python3 -m pip install ansicolors sslcrypto + if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi + + - name: make clean + run: make clean + + - name: Build + env: + V: 1 + run: make + + - name: Test + run: make check + + ubuntu-make-btaddon: + if: always() + needs: [ubuntu-make] + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Update apt repos + run: sudo apt-get update + + - name: Install dependencies + run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0-dbg liblua5.2-0 lua5.2 sed + + - name: Install Python dependencies + run: | + python3 -m pip install --upgrade pip + python3 -m pip install setuptools + python3 -m pip install ansicolors sslcrypto + if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi + + - name: make clean + run: make clean + + - name: Build + env: + V: 1 + PLATFORM_EXTRAS: BTADDON + run: make + + - name: Test + run: make check + + ubuntu-cmake: + if: always() + needs: [ubuntu-make, ubuntu-make-btaddon] + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Update apt repos + run: sudo apt-get update + + - name: Install dependencies + run: sudo apt-get install -yqq make autoconf build-essential ca-certificates pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev libbz2-dev libbluetooth-dev libpython3-dev python3 python3-dev libpython3-all-dev liblua5.2-dev liblua5.2-0-dbg liblua5.2-0 lua5.2 sed + + - name: Install Python dependencies + run: | + python3 -m pip install --upgrade pip + python3 -m pip install setuptools + python3 -m pip install ansicolors sslcrypto + if [ -f requirements.txt ]; then python3 -m pip install -r requirements.txt; fi + + - name: Prepare Build Folders + run: mkdir -p client/build + + - name: Initiate cmake environment + run: cmake .. + working-directory: client/build/ + + - name: Build + env: + VERBOSE: 1 + run: make + working-directory: client/build/ + + - name: Test + env: + CHECKARGS: "--clientbin ./client/build/proxmark3" + run: make client/check diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 000000000..78ee6c3cf --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,152 @@ +name: Windows Build and Test + +on: [push, pull_request] + +jobs: + proxspace: + runs-on: windows-latest + defaults: + run: + shell: pwsh + env: + PATH: C:/ProxSpace/msys2/mingw64/bin;C:/ProxSpace/msys2/usr/local/bin;C:/ProxSpace/msys2/usr/bin;C:/ProxSpace/msys2/bin + MSYSTEM: MINGW64 + + steps: + - name: ProxSpace download + run: Invoke-WebRequest "https://github.com/Gator96100/ProxSpace/archive/master.zip" -outfile "C:\proxspace.zip" -Passthru + + - name: ProxSpace extract + run: Expand-Archive -LiteralPath "C:\proxspace.zip" -DestinationPath "C:\" + + - name: ProxSpace delete zip + run: Remove-Item "C:\proxspace.zip" + + - name: ProxSpace rename folder + run: Get-ChildItem -Path "C:\ProxSpace-*" | Rename-Item -NewName (Split-Path C:\ProxSpace -Leaf) + + - name: ProxSpace version + run: | + $psversion = (Select-String -Pattern 'PSVERSION=' -SimpleMatch -Path "C:\ProxSpace\msys2\ps\09-proxspace_setup.post").Line.Split("""")[1] + Write-Host "ProxSpace version: $psversion" + + - name: ProxSpace initial startup + working-directory: C:\ProxSpace + run: ./runme64.bat -c "exit" + + - uses: actions/checkout@v2 + + - name: make clean + run: make clean + + - name: Build + run: make V=1 + + - name: Test + run: make check + + - name: make clean + run: make clean + + - name: Build btaddon + run: make V=1 PLATFORM_EXTRAS=BTADDON + + - name: Test btaddon + run: make check + + - name: make clean + run: make clean + + - name: Prepare cmake build folders + run: mkdir -p client/build + + - name: Initiate cmake environment + run: cmake -G"MSYS Makefiles" .. + working-directory: client/build/ + + - name: Build cmake + run: make VERBOSE=1 + working-directory: client/build/ + + - name: Test cmake + run: make client/check CHECKARGS="--clientbin ./client/build/proxmark3" + + wsl: + runs-on: windows-latest + defaults: + run: + shell: wsl-bash {0} + + steps: + - name: WSL setup + uses: Vampire/setup-wsl@v1 + with: + distribution: Ubuntu-20.04 + update: "true" + additional-packages: git + ca-certificates + build-essential + pkg-config + libreadline-dev + gcc-arm-none-eabi + libnewlib-dev + libbz2-dev + qtbase5-dev + cmake + libpython3-dev + python3 + python3-pip + python3-dev + libpython3-all-dev + + - name: Install Python dependencies + run: | + python3 -m pip install --upgrade pip + python3 -m pip install setuptools + python3 -m pip install ansicolors sslcrypto + + - name: WSL QT fix + run: sudo strip --remove-section=.note.ABI-tag /usr/lib/x86_64-linux-gnu/libQt5Core.so.5 + + - name: Set git to use LF + shell: bash + run: | + git config --global core.autocrlf false + git config --global core.eol lf + + - uses: actions/checkout@v2 + + - name: make clean + run: make clean + + - name: Build + run: make V=1 + + - name: Test + run: make check + + - name: make clean + run: make clean + + - name: Build btaddon + run: make V=1 PLATFORM_EXTRAS=BTADDON + + - name: Test btaddon + run: make check + + - name: make clean + run: make clean + + - name: Prepare cmake build folders + run: mkdir -p client/build + + - name: Initiate cmake environment + run: cmake .. + working-directory: client/build/ + + - name: Build cmake + run: make VERBOSE=1 + working-directory: client/build/ + + - name: Test cmake + run: make client/check CHECKARGS="--clientbin ./client/build/proxmark3" diff --git a/.gitignore b/.gitignore index 081c0f8e8..543624a4f 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ version.c *.json.bak *.pyc *.bmp +originalitysig.csv # new build file for add-ons. Makefile.platform @@ -71,6 +72,7 @@ tools/cryptorf/cm tools/cryptorf/sm tools/cryptorf/sma tools/cryptorf/sma_multi +tools/mf_nonce_brute/mf_nonce_brute fpga/* !fpga/tests @@ -93,18 +95,21 @@ client/traces/* client/dumps/* *.ice *.new -tools/mf_nonce_brute/mf_nonce_brute -tools/andrew/* -tools/jtag_openocd/openocd_configuration -ppls patches/* *- Copy.* +/EF_* client/lualibs/mfc_default_keys.lua client/lualibs/pm3_cmd.lua # recompiled fpga_version_info.c +# log / history .proxmark3/* # .tmp files are created during compilation *.tmp + +# VSCode files +!.vscode/templates/*.json +!.vscode/extensions.json +!.vscode/tasks.json diff --git a/.travis.yml b/.travis.yml.old similarity index 100% rename from .travis.yml rename to .travis.yml.old diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..85b3997e2 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "ms-vscode.cpptools", + "austin.code-gnu-global", + "marus25.cortex-debug", + "augustocdias.tasks-shell-input" + ] +} diff --git a/.vscode/setup.sh b/.vscode/setup.sh new file mode 100755 index 000000000..70672b924 --- /dev/null +++ b/.vscode/setup.sh @@ -0,0 +1,145 @@ +#!/bin/bash +############################### +# Linux # +# Uncomment to override # +############################### +#export SerialPort="/dev/ttyACM0" +#export DebuggerPath="/usr/bin/gdb" +#export JLinkServerPath="/opt/SEGGER/JLink/JLinkGDBServerCLExe" + +############################### +# WSL # +# Uncomment to override # +############################### +#export SerialPort="/dev/ttyS4" +#export DebuggerPath="/usr/bin/gdb" +#export JLinkServerPath="/mnt/c/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe" + +############################### +# ProxSpace # +# Uncomment to override # +############################### +#export SerialPort="COM5" +#export JLinkServerPath="C:/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe" + +#Debugging on 256KB systems is not recommended +#This option does not override PLATFORM_SIZE +export DeviceMem="512" + + +VSCODEPATH=$(dirname "$0") + +function print_config { + echo "Updating with following configuration:" + echo "SerialPort: $SerialPort" + echo "DebuggerPath: $DebuggerPath" + echo "JLinkServerPath: $JLinkServerPath" +} + +function setup_serial_port { + if [ -z "$SerialPort" ]; then + pm3list=$($VSCODEPATH/../pm3 --list 2>/dev/null) + #Use first port listed + export SerialPort=$(echo $pm3list | head -n 1 | cut -c 4-) + if [ -z "$SerialPort" ]; then + echo >&2 "[!!] No serial port found, please set SerialPort manually" + exit 1 + fi + fi +} + +function setup_gdb_linux { + if [ -z "$DebuggerPath" ]; then + export DebuggerPath="/usr/bin/gdb" + fi + if [ ! -x "$DebuggerPath" ]; then + echo >&2 "[!!] gdb not found, please set DebuggerPath manually" + exit 1 + fi +} + +function setup_jlink_linux { + if [ -z "$JLinkServerPath" ]; then + export JLinkServerPath="/opt/SEGGER/JLink/JLinkGDBServerCLExe" + fi + if [ ! -x "$JLinkServerPath" ]; then + echo >&2 "[!!] JLinkGDBServerCLExe not found, please set JLinkServerPath manually" + exit 1 + fi + +} + +function setup_jlink_wsl { + if [ -z "$JLinkServerPath" ]; then + export JLinkServerPath="/mnt/c/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe" + fi + if [ ! -x "$JLinkServerPath" ]; then + echo >&2 "[!!] JLinkGDBServerCL.exe not found, please set JLinkServerPath manually" + exit 1 + fi +} + +function setup_jlink_ps { + if [ -z "$JLinkServerPath" ]; then + export JLinkServerPath="c:/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe" + fi + jlinkpath=$(cygpath "$JLinkServerPath") + if [ ! -x "$jlinkpath" ]; then + echo >&2 "[!!] JLinkGDBServerCL.exe not found, please set JLinkServerPath manually" + exit 1 + fi +} + +function setup_wsl { + setup_serial_port + setup_gdb_linux + setup_jlink_wsl + print_config + envsubst '${SerialPort} ${DebuggerPath} ${JLinkServerPath} ${DeviceMem}' <"$VSCODEPATH/templates/launch_wsl.json" > "$VSCODEPATH/launch.json" +} + +function setup_linux { + setup_serial_port + setup_gdb_linux + setup_jlink_linux + print_config + envsubst '${SerialPort} ${DebuggerPath} ${JLinkServerPath} ${DeviceMem}' <"$VSCODEPATH/templates/launch_linux.json" > "$VSCODEPATH/launch.json" +} + +function setup_ps { + setup_serial_port + setup_jlink_ps + export DebuggerPath="Using ProxSpace gbd" + print_config + envsubst '${SerialPort} ${JLinkServerPath} ${DeviceMem}' <"$VSCODEPATH/templates/launch_ps.json" > "$VSCODEPATH/launch.json" +} + +if [ -f "$VSCODEPATH/launch.json" ]; then + read -p "Existing configuration found, do you want to override it? " -n 1 -r + if [[ $REPLY =~ ^[Yy]$ ]] + then + rm "$VSCODEPATH/launch.json.bak" 2> /dev/null + mv "$VSCODEPATH/launch.json" "$VSCODEPATH/launch.json.bak" 2> /dev/null + else + echo >&2 "[!!] user abort" + exit 1 + fi + +fi + +HOSTOS=$(uname | awk '{print toupper($0)}') +if [ "$HOSTOS" = "LINUX" ]; then + if uname -a|grep -q Microsoft; then + setup_wsl + else + setup_linux + fi +elif [ "$HOSTOS" = "DARWIN" ]; then + echo >&2 "[!!] MacOS not supported, sorry!" + exit 1 +elif [[ "$HOSTOS" =~ MINGW(32|64)_NT* ]]; then + setup_ps +else + echo >&2 "[!!] Host OS not recognized, abort: $HOSTOS" + exit 1 +fi \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 7354a8a2d..67f0f235e 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,23 +2,20 @@ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format "version": "2.0.0", + "windows": { + "options": { + "cwd": "${workspaceFolder}", + "env": { + "PATH": "${workspaceFolder}/../../msys2/mingw64/bin;${workspaceFolder}/../../msys2/usr/local/bin;${workspaceFolder}/../../msys2/usr/bin;${workspaceFolder}/../../msys2/bin", + "MSYSTEM": "MINGW64" + } + } + }, "tasks": [ { "label": "all: Make & run", "type": "shell", "command": "make -j && ./pm3", - "windows": { - "options": { - "cwd": "${workspaceFolder}/../..", - "shell": { - "executable": "${workspaceFolder}/../../runme64.bat", - "args": [ - "-c \"cd ${workspaceFolderBasename} &&" - ], - - } - } - }, "problemMatcher": [ "$gcc" ], @@ -31,18 +28,6 @@ "label": "choose: Make", "type": "shell", "command": "make ${input:componentType} -j", - "windows": { - "options": { - "cwd": "${workspaceFolder}/../..", - "shell": { - "executable": "${workspaceFolder}/../../runme64.bat", - "args": [ - "-c \"cd ${workspaceFolderBasename} &&" - ], - - } - } - }, "problemMatcher": [ "$gcc" ], @@ -52,18 +37,6 @@ "label": "client: Debug: make", "type": "shell", "command": "make client -j DEBUG=1", - "windows": { - "options": { - "cwd": "${workspaceFolder}/../..", - "shell": { - "executable": "${workspaceFolder}/../../runme64.bat", - "args": [ - "-c \"cd ${workspaceFolderBasename} &&" - ], - - } - } - }, "problemMatcher": [ "$gcc" ], @@ -73,18 +46,6 @@ "label": "client: Debug: clean & make", "type": "shell", "command": "make client/clean && make client -j DEBUG=1", - "windows": { - "options": { - "cwd": "${workspaceFolder}/../..", - "shell": { - "executable": "${workspaceFolder}/../../runme64.bat", - "args": [ - "-c \"cd ${workspaceFolderBasename} &&" - ], - - } - } - }, "problemMatcher": [ "$gcc" ], @@ -94,55 +55,27 @@ "label": "fullimage: Make & Flash", "type": "shell", "command": "make fullimage && ./pm3-flash-fullimage", - "windows": { - "options": { - "cwd": "${workspaceFolder}/../..", - "shell": { - "executable": "${workspaceFolder}/../../runme64.bat", - "args": [ - "-c \"cd ${workspaceFolderBasename} &&" - ], - - } - } - }, "problemMatcher": [] }, { "label": "BOOTROM: Make & Flash", "type": "shell", "command": "make bootrom && ./pm3-flash-bootrom", - "windows": { - "options": { - "cwd": "${workspaceFolder}/../..", - "shell": { - "executable": "${workspaceFolder}/../../runme64.bat", - "args": [ - "-c \"cd ${workspaceFolderBasename} &&" - ], - - } - } - }, "problemMatcher": [] }, { "label": "Run client", "type": "shell", "command": "./pm3", - "windows": { - "options": { - "cwd": "${workspaceFolder}/../..", - "shell": { - "executable": "${workspaceFolder}/../../runme64.bat", - "args": [ - "-c \"cd ${workspaceFolderBasename} &&" - ], - - } - } - }, "problemMatcher": [] + },{ + "label": "fullimage: clean & make debug", + "type": "shell", + "command": "make armsrc/clean && make armsrc/all DEBUG_ARM=1", + "problemMatcher": [ + "$gcc" + ], + "group": "build", } ], "inputs": [ diff --git a/.vscode/launch.json b/.vscode/templates/launch_linux.json similarity index 52% rename from .vscode/launch.json rename to .vscode/templates/launch_linux.json index f6a030c7c..5239c6bda 100644 --- a/.vscode/launch.json +++ b/.vscode/templates/launch_linux.json @@ -5,34 +5,11 @@ "version": "0.2.0", "configurations": [ { - "name": "(gdb) Attach", - "type": "cppdbg", - "request": "attach", - "program": "${cwd}/client/proxmark3", - //"processId": "${command:pickProcess}", - "processId": "${input:ProcessID}", - "MIMode": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ], - "windows": { - "processId": "${input:ProcessIDWindows}", - "miDebuggerPath": "${workspaceFolder}/../../msys2/mingw64/bin/gdb.exe", - "externalConsole": true,//for ProxSpace externalConsole=true is required, because the internal cosole stops updating after a while - "environment": [{ - "name": "PATH","value": "${workspaceFolder}/../../msys2/mingw64/bin;${workspaceFolder}/../../msys2/usr/local/bin;${workspaceFolder}/../../msys2/usr/bin;${workspaceFolder}/../../msys2/bin" - }] - } - },{ - "name": "(gdb) Build & Launch", + "name": "Client: (gdb) Build & Launch", "type": "cppdbg", "request": "launch", "program": "${cwd}/client/proxmark3", - "args": ["/dev/ttyACM0"], + "args": ["${SerialPort}"], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [], @@ -46,15 +23,36 @@ } ], "preLaunchTask": "client: Debug: clean & make", - "miDebuggerPath": "/usr/bin/gdb", - "windows": { - "args": ["COM5"], - "miDebuggerPath": "${workspaceFolder}/../../msys2/mingw64/bin/gdb.exe", - "externalConsole": true,//for ProxSpace externalConsole=true is required, because the internal cosole stops updating after a while - "environment": [{ - "name": "PATH","value": "${workspaceFolder}/../../msys2/mingw64/bin;${workspaceFolder}/../../msys2/usr/local/bin;${workspaceFolder}/../../msys2/usr/bin;${workspaceFolder}/../../msys2/bin" - }] - } + "miDebuggerPath": "${DebuggerPath}" + },{ + "name": "Client: (gdb) Attach", + "type": "cppdbg", + "request": "attach", + "program": "${cwd}/client/proxmark3", + //"processId": "${command:pickProcess}", + "processId": "${input:ProcessID}", + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + },{ + "name": "Firmware: (J-Link) Build & Launch", + "type": "cortex-debug", + "request": "launch", + "cwd": "${workspaceRoot}", + "preLaunchTask": "fullimage: clean & make debug", + "executable": "${workspaceRoot}/armsrc/obj/fullimage.elf", + "serverpath": "${JLinkServerPath}", + "servertype": "jlink", + "device": "AT91SAM7S${DeviceMem}", + "interface": "jtag", + "serialNumber": "", //If you have more than one J-Link probe, add the serial number here. + "runToMain": false, + "armToolchainPath": "/usr/bin/" } ], "inputs": [ @@ -67,14 +65,6 @@ "command": "pgrep -n proxmark3", } - },{ - "id": "ProcessIDWindows", - "type": "command", - "command": "shellCommand.execute", - "args": { - "command": "${workspaceFolder}/../../runme64.bat -c \"cat /proc/$(pgrep -n proxmark3)/winpid\"", - } - } ] } \ No newline at end of file diff --git a/.vscode/templates/launch_ps.json b/.vscode/templates/launch_ps.json new file mode 100644 index 000000000..045b3d6c5 --- /dev/null +++ b/.vscode/templates/launch_ps.json @@ -0,0 +1,49 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "options": { + "cwd": "${workspaceFolder}", + "env": { + "PATH": "${workspaceFolder}/../../msys2/mingw64/bin;${workspaceFolder}/../../msys2/usr/local/bin;${workspaceFolder}/../../msys2/usr/bin;${workspaceFolder}/../../msys2/bin", + "MSYSTEM": "MINGW64" + } + }, + "configurations": [ + { + "name": "Client: (gdb) Build & Launch", + "type": "cppdbg", + "request": "launch", + "cwd": "${workspaceFolder}", + "program": "${cwd}/client/proxmark3", + "args": ["${SerialPort}"], + "stopAtEntry": false, + "externalConsole": true,//for ProxSpace externalConsole=true is required, because the internal cosole stops updating after a while + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "client: Debug: clean & make", + "miDebuggerPath": "${workspaceFolder}/../../msys2/mingw64/bin/gdb.exe" + },{ + "name": "Firmware: (J-Link) Build & Launch", + "type": "cortex-debug", + "request": "launch", + "cwd": "${workspaceRoot}", + "preLaunchTask": "fullimage: clean & make debug", + "executable": "${workspaceRoot}/armsrc/obj/fullimage.elf", + "serverpath": "${JLinkServerPath}", + "servertype": "jlink", + "device": "AT91SAM7S${DeviceMem}", + "interface": "jtag", + "serialNumber": "", //If you have more than one J-Link probe, add the serial number here. + "runToMain": false, + "armToolchainPath": "${workspaceFolder}/../../msys2/mingw64/bin" + } + ] +} \ No newline at end of file diff --git a/.vscode/templates/launch_wsl.json b/.vscode/templates/launch_wsl.json new file mode 100644 index 000000000..5239c6bda --- /dev/null +++ b/.vscode/templates/launch_wsl.json @@ -0,0 +1,70 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Client: (gdb) Build & Launch", + "type": "cppdbg", + "request": "launch", + "program": "${cwd}/client/proxmark3", + "args": ["${SerialPort}"], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "client: Debug: clean & make", + "miDebuggerPath": "${DebuggerPath}" + },{ + "name": "Client: (gdb) Attach", + "type": "cppdbg", + "request": "attach", + "program": "${cwd}/client/proxmark3", + //"processId": "${command:pickProcess}", + "processId": "${input:ProcessID}", + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + },{ + "name": "Firmware: (J-Link) Build & Launch", + "type": "cortex-debug", + "request": "launch", + "cwd": "${workspaceRoot}", + "preLaunchTask": "fullimage: clean & make debug", + "executable": "${workspaceRoot}/armsrc/obj/fullimage.elf", + "serverpath": "${JLinkServerPath}", + "servertype": "jlink", + "device": "AT91SAM7S${DeviceMem}", + "interface": "jtag", + "serialNumber": "", //If you have more than one J-Link probe, add the serial number here. + "runToMain": false, + "armToolchainPath": "/usr/bin/" + } + ], + "inputs": [ + { + // Using Extension "Tasks Shell Input" https://marketplace.visualstudio.com/items?itemName=augustocdias.tasks-shell-input + "id": "ProcessID", + "type": "command", + "command": "shellCommand.execute", + "args": { + "command": "pgrep -n proxmark3", + } + + } + ] +} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c6cba7e2..bced7ef1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,29 +3,67 @@ All notable changes to this project will be documented in this file. This project uses the changelog in accordance with [keepchangelog](http://keepachangelog.com/). Please use this to write notable changes, which is not the same as git commit log... ## [unreleased][unreleased] + - Added a script to dump originality signatures from MFU EV1s and NTAGs (@aveao) + - Added parsing of EF_CardAccess to display PACE algorithm, version and parameter in `hf emrtd info` (@aveao) + - Change, numerous commands more uses cliparser (@tcprst, @iceman1001) + - Added more originality public keys (@anon) + - Added `hf 14a info` - now also verify MFC Ev1 signatures (@iceman1001) + - Added `LF_THAREXDE` standalone mode which simulates and reads EM4x50 cards (@tharexde) + - Added `hf jooki` commands (@iceman1001) + - Changed `lf hid clone` - also accepts binary wiegand (@iceman1001) + - Changed `wiegand encode` - format param is now optional, w/o it will try encode all formats (@iceman1001) + - Fix cppchecker warnings (@iceman1001) + - Added `trace list -t mf` - now can use external dictionary keys file (@McEloff) + - Fix `lf gallagher read` - now correctly decodes card data + - Add support to `lf gallagher clone` and `lf gallagher sim` for writing specific card region, facility, card & issue numbers (@DarkMatterMatt) + - Added support for older vid/pid detection (@Gator96100) + - Added `hf mfdes bruteaid` - proper bruteforce of DESFire AID when no reading of files is possible (@craftbyte) + - Added support for bidirectional communication for `lf em 4x50 sim` (@tharexde) + - Change `PLATFORM=PM3OTHER` to `PLATFORM=PM3GENERIC` (@iceman1001) + - Added `tools/hitag2crack/crack5opencl`, an optimized version of `crack5gpu` (@matrix) + - Fixed Makefile to account for changes when running on Apple Silicon (@tcprst) + - Added support for debugging ARM with JTAG & VSCode (@Gator96100) + - Added MFUL "Gen1b" suport to `hf_mfu_setuid.lua` (@iceman1001) + - Added possibility to get bargraph in `lf tune` and `hf tune` (@iceman1001, @doegox) + - Added `hf emrtd` ePassport dumping and parsing (@aveao) + - Added `aidsearch` to `hf 14b info` (@iceman1001) + - Added `ICE_STATE_DUMP_SIM` - standalone mode for dumping/simming one iClass tag (@iconicsec) + - Added `lf em 4x50 eview` - show uploaded EM4x50 data in emul memory (@tharexde) + - Fix `data rawdemod` parsing for psk2 and user defined clock (@cyberpunk-re) + - Added `hf iclass encode` - encode a wiegand binary to a encrypted credential (@iceman1001) + - Changed `recoverpk.py` - now tests more ECDSA curves (@doegox) + - Added `hf 14a apdufuzz`- a naive apdu cla/ins/p1p2/len fuzzer (@iceman1001) + - Improved `hf 14a apdufuzz/apdufind` to find hidden APDUs (@ikarus23) + - Fix mixed up INC/DEC in MIFARE protocol defs (@vortixdev) + - Added `lf em 4x70 info` - new support for ID48 transponders (@cmolson) + - Fix multiple coverity scan issues (@iceman1001) + - Added a SIO item (@iceman1001) + - Fix `lf hid brute` - param (@iceman1001) + - Changed `lf em` layouts (@iceman1001) + - Change many commands to cliparser (@iceman1001, @tcprst, ...) - Fix issue #844 - `lf t55xx config` => recompute block0 (@cyberpunk-re) - - EM4x50: changed cli parameter from w (word) to d (data) (@tharexde) - - EM4x50: new function 4x50 login: authenticate against tag (@tharexde) - - EM4x50: new function 4x50 brute: guess password within a given password range (@tharexde) - - EM4x50: new function 4x50 chk: try passwords from dictionary (without option -> T55xx default dictionary or -f user dictionary) (@tharexde) - - EM4x50: new function 4x50 reader: read data from tag (configured data -> standard read mode), incl. option -@ (@tharexde) - - EM4x50: new function 4x50 sim: simulate dump from file or emulator/flash (@tharexde) - - EM4x50: new function 4x50 restore: restore dump file (bin, eml, json) onto tag (@tharexde) - - EM4x50: new function 4x50 esave: dump em4x50 content in emulator memory to file (bin + eml + json) (@tharexde) - - EM4x50: new function 4x50 eload: upload em4x50 file content (bin, eml, json) to emulator memory (@tharexde) - - EM4x50: added LED signals (@tharexde) - - EM4x50: added json format for 4x50 dump (@tharexde) - - EM4x50: relocated write requests in function 4x50 wipe from device to client (@tharexde) - - EM4x50: renamed 4x50_write_password to 4x50 writepwd (@tharexde) - - EM4x50: all hex input parameters now have to be given in lsb format (output is still msb + lsb) (@tharexde) - - EM4x50: changed cli parameter from a (address) to b (block) (@tharexde) - - EM4x50: switched to cliparser for all functions (@tharexde) - - EM4x50: stabilized and accelerated tag detection (@tharexde) - - EM4x50: removed global tag structure on device side (@tharexde) + - Change `lf em 4x50` - changed cli parameter from w (word) to d (data) (@tharexde) + - Added `lf em 4x50 login` - authenticate against tag (@tharexde) + - Added `lf em 4x50 brute` - guess password within a given password range (@tharexde) + - Added `lf em 4x50 chk` - try passwords from dictionary (without option -> T55xx default dictionary or -f user dictionary) (@tharexde) + - Added `lf em 4x50 reader` - read data from tag (configured data -> standard read mode), incl. option -@ (@tharexde) + - Added `lf em 4x50 sim` - simulate dump from file or emulator/flash (@tharexde) + - Added `lf em 4x50 restore` - restore dump file (bin, eml, json) onto tag (@tharexde) + - Added `lf em 4x50 esave` - dump em4x50 content in emulator memory to file (bin + eml + json) (@tharexde) + - Added `lf em 4x50 eload` - upload em4x50 file content (bin, eml, json) to emulator memory (@tharexde) + - Changed EM4x50 - added LED signals (@tharexde) + - Change `lf em 4x50 dump` - now support json format (@tharexde) + - Change `lf em 4x50 wipe` - relocated write requests from device to client (@tharexde) + - Renamed `lf em 4x50 write_passwordd` -> `writepwd` (@tharexde) + - Change `lf em 4x50` - all hex input parameters now have to be given in lsb format (output is still msb + lsb) (@tharexde) + - Change `lf em 4x50` - changed cli parameter from a (address) to b (block) (@tharexde) + - Change `lf em 4x50` - now supports cliparser (@tharexde) + - Change EM4x50 - stabilized and accelerated tag detection (@tharexde) + - Change EM4x50 - removed global tag structure on device side (@tharexde) - Fix `hf 15 sim` - Added basic response to GET_SYSTEM_INFO and READBLOCK requests in order to fix iso15693 tag sim (@cyberpunk-re) - Added `mf mfu sim t 7 n ` - MFU emulation now supports automatic exit after blocks read. (@cyberpunk-re) - Added T55xx Guide to assist in learning how to use the T55xx chip (@mwalker33) - - Fix 'hf iclass wrbl' - dealing with tags in unsecured vs secured pagemode now is correct (@iceman1001) + - Fix `hf iclass wrbl` - dealing with tags in unsecured vs secured pagemode now is correct (@iceman1001) - Change many commands to cliparser (@iceman1001, @tcprst, @mwalker33,...) - ... - Change `hf iclass chk/lookup/loclass` speedups (@iceman1001) diff --git a/Makefile b/Makefile index b7e942f58..e42c0caa9 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ ifneq (,$(INSTALLTOOLS)) endif ifneq (,$(INSTALLSIMFW)) $(Q)$(MKDIR) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH) - $(Q)$(CP) $(foreach fw,$(INSTALLSIMFW),tools/simmodule/$(fw)) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH) + $(Q)$(CP) $(foreach fw,$(INSTALLSIMFW),client/resources/$(fw)) $(DESTDIR)$(PREFIX)$(PATHSEP)$(INSTALLFWRELPATH) endif ifeq ($(platform),Linux) $(Q)$(MKDIR) $(DESTDIR)$(UDEV_PREFIX) @@ -169,7 +169,7 @@ help: @echo "+ fpga_compress - Make tools/fpga_compress" @echo @echo "+ style - Apply some automated source code formatting rules" - @echo "+ cliparser - Generate cliparser TODO + @echo "+ cliparser - Generate cliparser TODO" @echo "+ check - Run offline tests. Set CHECKARGS to pass arguments to the test script" @echo "+ .../check - Run offline tests against specific target. See above." @echo "+ miscchecks - Detect various encoding issues in source code" diff --git a/Makefile.defs b/Makefile.defs index 48567214b..23dab5b32 100644 --- a/Makefile.defs +++ b/Makefile.defs @@ -57,6 +57,11 @@ else RANLIB= ranlib endif +# For detection of Apple Silicon +ifeq ($(platform),Darwin) + BREW_PREFIX = $(shell brew --prefix) +endif + ifeq ($(DEBUG),1) DEFCXXFLAGS = -g -O0 -pipe DEFCFLAGS = -g -O0 -fstrict-aliasing -pipe @@ -66,6 +71,11 @@ else DEFCFLAGS = -Wall -Werror -O3 -fstrict-aliasing -pipe DEFLDFLAGS = endif + +ifeq ($(DEBUG_ARM),1) + APP_CFLAGS += -g + SKIP_COMPRESSION=1 +endif # Next ones are activated only if SANITIZE=1 ifeq ($(SANITIZE),1) DEFCFLAGS += -g -fsanitize=address -fno-omit-frame-pointer diff --git a/Makefile.platform.sample b/Makefile.platform.sample index 8a31a0e9c..4ecb3b9f6 100644 --- a/Makefile.platform.sample +++ b/Makefile.platform.sample @@ -2,7 +2,7 @@ # Run 'make PLATFORM=' to get an exhaustive list of possible parameters for this file. PLATFORM=PM3RDV4 -#PLATFORM=PM3OTHER +#PLATFORM=PM3GENERIC # If you want more than one PLATFORM_EXTRAS option, separate them by spaces: #PLATFORM_EXTRAS=BTADDON #STANDALONE=HF_MSDSAL diff --git a/README.md b/README.md index bc7e88854..224e822af 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,18 @@ -# RRG / Iceman repo - Proxmark3 +# RRG / Iceman - Proxmark3 + + +| Releases | Coverity | Contributors | +| ------------------- | -------------------:| -------------------:| +| [![Latest release](https://img.shields.io/github/v/release/rfidresearchgroup/proxmark3)](https://github.com/RfidResearchGroup/proxmark3/releases/latest) | [![Coverity Status](https://scan.coverity.com/projects/19334/badge.svg)](https://scan.coverity.com/projects/proxmark3-rrg-iceman-repo)| ![GitHub contributors](https://img.shields.io/github/contributors/rfidresearchgroup/proxmark3) | + + +| Actions OSX CI | Actions Ubuntu CI | Windows CI | +| ------------------- | -------------------:| -------------------:| +| ![MacOS Build and Test](https://github.com/RfidResearchGroup/proxmark3/workflows/MacOS%20Build%20and%20Test/badge.svg?branch=master) | ![Ubuntu Build and Test](https://github.com/RfidResearchGroup/proxmark3/workflows/Ubuntu%20Build%20and%20Test/badge.svg?branch=master) | [![Build status](https://ci.appveyor.com/api/projects/status/b4gwrhq3nc876cuu/branch/master?svg=true)](https://ci.appveyor.com/project/RfidResearchGroup/proxmark3/branch/master) | - -| Releases | Linux & OSX CI | Windows CI | Coverity | Contributors | -| ------------------- |:-------------------:| -------------------:| -------------------:| -------------------:| -| [![Latest release](https://img.shields.io/github/v/release/rfidresearchgroup/proxmark3)](https://github.com/RfidResearchGroup/proxmark3/releases/latest) | [![Build status](https://api.travis-ci.com/RfidResearchGroup/proxmark3.svg?branch=master)](https://travis-ci.com/RfidResearchGroup/proxmark3) | [![Build status](https://ci.appveyor.com/api/projects/status/b4gwrhq3nc876cuu/branch/master?svg=true)](https://ci.appveyor.com/project/RfidResearchGroup/proxmark3/branch/master) | [![Coverity Status](https://scan.coverity.com/projects/19334/badge.svg)](https://scan.coverity.com/projects/proxmark3-rrg-iceman-repo)| ![GitHub contributors](https://img.shields.io/github/contributors/rfidresearchgroup/proxmark3) | - - - -# PROXMARK INSTALLATION AND OVERVIEW +# PROXMARK3 INSTALLATION AND OVERVIEW | FAQ's & Updates | Installation | Use of the Proxmark | | ------------------- |:-------------------:| -------------------:| @@ -36,10 +39,25 @@ |[Notes on Color usage](/doc/colors_notes.md)|[Makefile vs CMake](/doc/md/Development/Makefile-vs-CMake.md)|[Notes on Cloner guns](/doc/cloner_notes.md)| |[Notes on cliparser usage](/doc/cliparser.md)|[Notes on clocks](/doc/clocks.md)|| -## Build for non-RDV4 Proxmark3 platforms -In order to build this repo for other Proxmark3 platforms we urge you to read [Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md) +## Build for Proxmark3 RDV4 +See the instruction links in the tables above to build, flash and run for your Proxmark3 RDV4 device. +## Build for generic Proxmark3 platforms +In order to build this repo for generic Proxmark3 platforms we urge you to read [Advanced compilation parameters](/doc/md/Use_of_Proxmark/4_Advanced-compilation-parameters.md) + +With generic Proxmark3 platforms we mean: + - RDV1 + - RDV2 + - RDV3 easy + - Proxmark Evolution (needs extra care) + - Radiowar black PCB version + - Ryscorp green PCB version + - Ryscorp Pm3Pro + - VX + - numerous Chinese adapted versions of the RDV3 easy (kkmoon, pisworks etc) + +> ⚠ **Note**: About flash memory size of other Proxmark3 platforms. You need to keep a eye on how large your ARM chip built-in flash memory is. With 512kb you are fine but if its 256kb you need to compile this repo with even less functionality. When running the `./pm3-flash-all` you can see which size your device have if you have the bootloader from this repo installed. Otherwise you will find the size reported in the start message when running the Proxmark3 client `./pm3`. ## What has changed? @@ -49,7 +67,9 @@ On the hardware side: * added smart card module * added FPC connector -On the software side: quite a lot, see the [Changelog file](CHANGELOG.md). +On the software side: + +quite a lot, see the [Changelog file](CHANGELOG.md) which we try to keep updated. ## Development @@ -57,13 +77,13 @@ On the software side: quite a lot, see the [Changelog file](CHANGELOG.md). This repo compiles nicely on - Proxspace v3.x - - [latest release v3.7.1](https://github.com/Gator96100/ProxSpace/releases) + - [latest release v3.8](https://github.com/Gator96100/ProxSpace/releases) - Windows/mingw environment with Qt5.6.1 & GCC 4.9 - Ubuntu 16.04 -> 20.04 - ParrotOS, Gentoo, Pentoo, Kali, Nethunter, Archlinux, Fedora, Debian - Rasbian - Android / Termux - - Mac OS X / Homebrew + - Mac OS X / Homebrew / Apple Silicon - WSL1 (Windows subsystem linux) on Windows 10 - Docker container - [ RRG / Iceman repo based ubuntu 18.04 container ](https://hub.docker.com/r/secopsconsult/proxmark3) @@ -72,26 +92,22 @@ This repo compiles nicely on Hardware to run client on - PC - Android - - Raspberry Pi & Raspberry Pi Zero + - Raspberry Pi, Raspberry Pi Zero - Nvidia Jetson Nano ## Precompiled binaries -We don't maintain any precompiled binaries in this repo. There is community effort over at the Proxmark3 forum where @gator96100 has set up a google drive with many mingw binaries which is up-to-date. We link to these files here as to make it easier for users. -If you are having troubles with these files, contact the package maintainer @gator96100 and read the [sticky thread at forum](http://www.proxmark.org/forum/viewtopic.php?pid=24763#p24763) where known issues has been documented. +We don't maintain any precompiled binaries in this repo. There is community effort over at the Proxmark3 forum where [@gator96100](https://github.com/gator96100) has set up a AWS bucket with precompiled Proxspace (Mingw) binaries which is recompiled every night and with that also up-to-date. We link to these files here as to make it easier for users. +_If you use his pre-compiled Proxspace binaries do consider buy him a coffee for his efforts. Remember nothing says thank you as good as a donation._ -Ref: -For Proxmark3 RDV4 -- [Precompiled builds for RDV40 dedicated x86](https://drive.google.com/open?id=13zUs-aiQkYaSl5KWrBtuW5IWCoHJPsue) -- [Precompiled builds for RDV40 dedicated x64](https://drive.google.com/open?id=1SyPB8t5Vo8O0Lh7PjNm3Kv-mO4BNbxjX) +If you are having troubles with these files, contact the package maintainer [@gator96100](https://github.com/gator96100) and read the [homepage of his proxmark builds](https://www.proxmarkbuilds.org/) or read the [sticky thread at forum](http://www.proxmark.org/forum/viewtopic.php?pid=24763#p24763) where known issues has been documented with regards to the precompiled builds. -For Proxmark3 RDV4 with blueshark addon -- [Precompiled builds for RDV40 dedicated with Bluetooth addon x86](https://drive.google.com/open?id=1TqWYctkRvkLshQ1ZRBHPLDzYHR-asuMO) -- [Precompiled builds for RDV40 dedicated with Bluetooth addon x64](https://drive.google.com/open?id=17ful7u2QyYmMQzQzc5fAf8nJvyoDJfSL) +### Proxmark3 RDV4 devices +- [Precompiled builds for RDV40 dedicated x64](https://www.proxmarkbuilds.org/#rdv40-64/) +- [Precompiled builds for RDV40 dedicated with Bluetooth addon x64](https://www.proxmarkbuilds.org/#rdv40_bt-64/) -Generice Proxmark3 devices (non RDV4), for Proxmark3 Easy, RDV1, RDV2, RDV3, etc etc -- [Precompiled builds for RRG / Iceman repository x86](https://drive.google.com/open?id=1PI3Xr1mussPBPnYGu4ZjWzGPARK4N7JR) -- [Precompiled builds for RRG / Iceman repository x64](https://drive.google.com/open?id=1uX9RtYGinuFrpHybu4xq_BE3HrobI20e) +### Generic Proxmark3 devices +- [Precompiled builds for RRG / Iceman repository x64](https://www.proxmarkbuilds.org/#rrg_other-64/) ## Roadmap @@ -103,28 +119,40 @@ We usually merge your contributions fast since we do like the idea of getting a ## Issues & Troubleshooting Please search the [issues](https://github.com/rfidresearchgroup/proxmark3/issues) page here and see if your issue is listed in the first instance. -Read the [Troubleshooting](/doc/md/Installation_Instructions/Troubleshooting.md) guide to weed out most known problems. +Read the [Troubleshooting guide](/doc/md/Installation_Instructions/Troubleshooting.md) to weed out most known problems. -Next place to visit is the [Proxmark Forum](http://www.proxmark.org/forum/index.php). Learn to search it well and finally Google / duckduckgo is your friend :) You will find many blogposts, youtube videos, tweets, reddit +Next place to visit is the [Proxmark3 Forum](http://www.proxmark.org/forum/index.php). Learn to search it well and finally Google / duckduckgo is your friend :) +You will find many blogposts, youtube videos, tweets, reddit ### Offical channels + - [RFID Hacking community discord server](https://discord.gg/QfPvGFRQxH) - [Proxmark3 IRC channel](http://webchat.freenode.net/?channels=#proxmark3) - [Proxmark3 sub reddit](https://www.reddit.com/r/proxmark3/) - - [Twitter](https://twitter.com/proxmark3/) - - [Proxmark3 community discord server](https://discord.gg/zjxc8ZB) - - _no slack channel_ + - [Proxmark3 Twitter](https://twitter.com/proxmark3/) + - [Proxmark3 forum](http://www.proxmark.org/forum/index.php) + - _no slack channel_ -Iceman has quite a few videos on his [youtube channel](https://www.youtube.com/c/ChrisHerrmann1001) + +### Youtube channels +Iceman has quite a few videos on his channel and Quentyn has risen up the last year with good informative videos. We suggest you check them out and smash that subscribe buttons! + + - [Iceman channel](https://www.youtube.com/c/ChrisHerrmann1001) + - [Quentyn Taylor](https://www.youtube.com/channel/UCL91C3IZDv3wfj2ABhdRIrw) + - [Hacker warehouse channel](https://www.youtube.com/channel/UCimS6P854cQ23j6c_xst7EQ) + +_if you think of some more good youtube channels to be on this list, let us know!_ ## Cheat sheet -Thanks to Alex Dibs, you can enjoy a [command cheat sheet](/doc/cheatsheet.md) +You can enjoy a [command cheat sheet](/doc/cheatsheet.md) and we are trying to keep it updated. +[Thanks to Alex Dib!](https://github.com/scund00r) ## Maintainers ( package, distro ) -To all distro, package maintainers, we tried to make your life easier. `make install` is now available and if you want to know more. -- [Maintainers](/doc/md/Development/Maintainers.md) +To all distro, package maintainers, we tried to make your life easier. + +`make install` is now available and if you want to know more. +- [Notes for maintainers](/doc/md/Development/Maintainers.md) ## Why didn't you base it on official Proxmark3 Master? @@ -132,14 +160,9 @@ The separation from official Proxmark3 repo gives us a lot of freedom to create ## Proxmark3 GUI -The official PM3-GUI from Gaucho will not work. -The new [Proxmark3 Universal GUI](https://github.com/burma69/PM3UniversalGUI) will work more or less. Change is needed in order to show helptext when client isn't connected to a device. We don't know how active the maintainers. +The official PM3-GUI from Gaucho will not work. Not to mention is quite old and not maintained any longer. -## The end - -- July 2018 [@herrmann1001](https://mobile.twitter.com/herrmann1001) -- updated Feb 2019 [@5w0rdfish](https://mobile.twitter.com/5w0rdFish) -- updated 2019 [@doegox](https://mobile.twitter.com/doegox) +The new [Proxmark3 Universal GUI](https://github.com/burma69/PM3UniversalGUI) will work more or less. Change is needed in order to show helptext when client isn't connected to a device. We don't know how active the maintainers are. There has been brought to our attention that there is quite a few Chinese Windows GUI available. Usually you find them on alibaba / taobao ads but we have no idea which fw/client they are compatible with. Proceed with caution if you decide to go down that road. # Donations diff --git a/appveyor.yml b/appveyor.yml index 44e6b6040..899202bf1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -64,8 +64,12 @@ clone_script: WSLExec "WSL update..." "sudo apt-get update 1>/dev/null" WSLExec "WSL upgrade..." "sudo apt-get upgrade -y 1>/dev/null" WSLExec "WSL cleanup..." "sudo apt-get auto-remove -y 1>/dev/null" - WSLExec "WSL install..." "sudo apt-get -y install --reinstall --no-install-recommends git ca-certificates build-essential pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev qtbase5-dev cmake 1>/dev/null" + WSLExec "WSL install..." "sudo apt-get -y install --reinstall --no-install-recommends git ca-certificates build-essential pkg-config libreadline-dev gcc-arm-none-eabi libnewlib-dev libbz2-dev qtbase5-dev cmake libpython3-dev python3 python3-pip python3-dev libpython3-all-dev 1>/dev/null" WSLExec "WSL QT fix..." "sudo strip --remove-section=.note.ABI-tag /usr/lib/x86_64-linux-gnu/libQt5Core.so.5" + WSLExec "WSL install python dependencies..." "sudo python3 -m pip install --upgrade pip 1>/dev/null" + WSLExec "WSL install pip..." "sudo python3 -m pip install setuptools 1>/dev/null" + WSLExec "WSL install pip continue..." "sudo python3 -m pip install ansicolors sslcrypto 1>/dev/null" + Add-AppveyorMessage -Message "WSL setup took $(([System.Environment]::TickCount-$WSLInstallTime) / 1000) sec" -Category Information New-Item -ItemType "file" -Path "C:\WSL-Finished.txt" -Force | Out-Null } @@ -279,7 +283,7 @@ build_script: cd $env:proxspace_path ./runme64.bat -c "yes | pacman -Sc > /dev/null 2>&1" - + Remove-Item -Recurse -Force -Path "$env:proxspace_cache_path" -ErrorAction SilentlyContinue Move-Item -Path "$env:proxspace_path\msys2\var\cache" -Destination "$env:proxspace_cache_path" -Force diff --git a/armsrc/Makefile b/armsrc/Makefile index 46ba4f027..019d10820 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -217,8 +217,12 @@ $(OBJDIR)/fullimage.data.o: $(OBJDIR)/fullimage.data.bin.z $(Q)$(OBJCOPY) -O elf32-littlearm -I binary -B arm --rename-section .data=compressed_data $^ $@ $(OBJDIR)/fullimage.elf: $(OBJDIR)/fullimage.nodata.o $(OBJDIR)/fullimage.data.o +ifeq (,$(findstring WITH_NO_COMPRESSION,$(APP_CFLAGS))) $(info [=] LD $@) $(Q)$(CC) $(CROSS_LDFLAGS) -Wl,-T,ldscript,-e,_osimage_entry,-Map,$(patsubst %.elf,%.map,$@) -o $@ $^ +else + $(Q)$(CP) $(OBJDIR)/fullimage.stage1.elf $@ +endif tarbin: $(OBJS) $(info TAR $@) diff --git a/armsrc/Standalone/Makefile.hal b/armsrc/Standalone/Makefile.hal index d5414f661..552267611 100644 --- a/armsrc/Standalone/Makefile.hal +++ b/armsrc/Standalone/Makefile.hal @@ -35,6 +35,9 @@ define KNOWN_STANDALONE_DEFINITIONS | LF_SAMYRUN | HID26 read/clone/sim | | | - Samy Kamkar | +----------------------------------------------------------+ +| LF_THAREXDE | Simulate/read EM4x50 tags | +| (RDV4 only) | storing in flashmem | ++----------------------------------------------------------+ | HF_14ASNIFF | 14a sniff to flashmem | | (RDV4 only) | | +----------------------------------------------------------+ @@ -67,10 +70,10 @@ define KNOWN_STANDALONE_DEFINITIONS +----------------------------------------------------------+ endef -STANDALONE_MODES := LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RWC LF_HIDBRUTE LF_ICEHID LF_PROXBRUTE LF_SAMYRUN +STANDALONE_MODES := LF_SKELETON LF_EM4100EMUL LF_EM4100RSWB LF_EM4100RWC LF_HIDBRUTE LF_ICEHID LF_PROXBRUTE LF_SAMYRUN LF_THAREXDE STANDALONE_MODES += HF_14ASNIFF HF_AVEFUL HF_BOG HF_COLIN HF_ICECLASS HF_LEGIC HF_MATTYRUN HF_MSDSAL HF_TCPRST HF_YOUNG STANDALONE_MODES_REQ_SMARTCARD := -STANDALONE_MODES_REQ_FLASH := LF_ICEHID HF_14ASNIFF HF_BOG HF_COLIN HF_ICECLASS +STANDALONE_MODES_REQ_FLASH := LF_ICEHID LF_THAREXDE HF_14ASNIFF HF_BOG HF_COLIN HF_ICECLASS ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES)),) STANDALONE_PLATFORM_DEFS += -DWITH_STANDALONE_$(STANDALONE) ifneq ($(filter $(STANDALONE),$(STANDALONE_MODES_REQ_SMARTCARD)),) diff --git a/armsrc/Standalone/Makefile.inc b/armsrc/Standalone/Makefile.inc index 087a4d927..519dc2087 100644 --- a/armsrc/Standalone/Makefile.inc +++ b/armsrc/Standalone/Makefile.inc @@ -73,3 +73,7 @@ endif ifneq (,$(findstring WITH_STANDALONE_HF_ICECLASS,$(APP_CFLAGS))) SRC_STANDALONE = hf_iceclass.c endif +# WITH_STANDALONE_LF_THAREXDE +ifneq (,$(findstring WITH_STANDALONE_LF_THAREXDE,$(APP_CFLAGS))) + SRC_STANDALONE = lf_tharexde.c +endif diff --git a/armsrc/Standalone/hf_aveful.c b/armsrc/Standalone/hf_aveful.c index c9464e3d1..dc481fc47 100644 --- a/armsrc/Standalone/hf_aveful.c +++ b/armsrc/Standalone/hf_aveful.c @@ -68,11 +68,11 @@ uint16_t get_ev1_version(iso14a_card_select_t card, uint8_t *version) { return mifare_sendcmd(MIFARE_ULEV1_VERSION, NULL, 0, version, NULL, NULL); } -uint16_t get_ev1_signature(iso14a_card_select_t card, uint8_t *response) { +uint16_t get_ev1_signature(iso14a_card_select_t card, uint8_t *signature) { uint8_t cmd[4] = {MIFARE_ULEV1_READSIG, 0x00, 0x00, 0x00}; AddCrc14A(cmd, 2); ReaderTransmit(cmd, sizeof(cmd), NULL); - return ReaderReceive(response, NULL); + return ReaderReceive(signature, NULL); } uint16_t get_ev1_counter(iso14a_card_select_t card, uint8_t counter, uint8_t *response) { @@ -114,7 +114,6 @@ int get_block_count(iso14a_card_select_t card, uint8_t version[], uint16_t versi else if (memcmp(version, "\x00\x04\x03\x01\x01\x00\x0E", 7) == 0) { block_count = MAX_ULEV1b_BLOCKS; } else if (memcmp(version, "\x00\x04\x03\x02\x01\x00\x0E", 7) == 0) { block_count = MAX_ULEV1b_BLOCKS; } else if (memcmp(version, "\x00\x34\x21\x01\x01\x00\x0E", 7) == 0) { block_count = MAX_ULEV1b_BLOCKS; } // Mikron JSC Russia EV1 41 pages tag - else if (memcmp(version, "\x00\x34\x21\x01\x01\x00\x0E", 7) == 0) { block_count = MAX_UL_BLOCKS; } else if (version[2] == 0x03) { block_count = MAX_ULEV1a_BLOCKS; } } } @@ -191,7 +190,7 @@ void RunMod(void) { read_successful = false; break; } - // We're skipping 14 blocks (56 bytes) here, as that "[...] has version/signature/counter data here" according to comments on hf_mfu_dumptoemulator + // We're skipping 14 blocks (56 bytes) here, as that "[...] has version/signature/counter data here" according to comments on data_mfu_bin2eml // When converting a bin, it's almost all 0 other than one 0x0F byte, and functionality seems to be unaffected if that byte is set to 0x00. emlSetMem_xt(dataout, 14 + i, 1, 4); Dbhexdump(4, dataout, 0); diff --git a/armsrc/Standalone/hf_iceclass.c b/armsrc/Standalone/hf_iceclass.c index 8490ea628..27e1bf5ab 100644 --- a/armsrc/Standalone/hf_iceclass.c +++ b/armsrc/Standalone/hf_iceclass.c @@ -37,15 +37,19 @@ #define ICE_STATE_ATTACK 2 #define ICE_STATE_READER 3 #define ICE_STATE_CONFIGCARD 4 +#define ICE_STATE_DUMP_SIM 5 + +#define HF_ICLASS_NUM_MODES 6 // ==================================================== // Select which standalone function to be active. -// 4 possiblities. Uncomment the one you wanna use. +// 5 possiblities. Uncomment the one you wanna use. #define ICE_USE ICE_STATE_FULLSIM //#define ICE_USE ICE_STATE_ATTACK //#define ICE_USE ICE_STATE_READER //#define ICE_USE ICE_STATE_CONFIGCARD +//#define ICE_USE ICE_STATE_DUMP_SIM // ==================================================== @@ -53,6 +57,8 @@ #define NUM_CSNS 9 #define MAC_RESPONSES_SIZE (16 * NUM_CSNS) #define HF_ICLASS_FULLSIM_ORIG_BIN "iceclass-orig.bin" +#define HF_ICALSSS_READSIM_TEMP_BIN "iceclass-temp.bin" +#define HF_ICALSSS_READSIM_TEMP_MOD_BIN "iceclass-temp-mod.bin" #define HF_ICLASS_FULLSIM_MOD "iceclass-modified" #define HF_ICLASS_FULLSIM_MOD_BIN HF_ICLASS_FULLSIM_MOD".bin" #define HF_ICLASS_FULLSIM_MOD_EML HF_ICLASS_FULLSIM_MOD".eml" @@ -141,20 +147,33 @@ static void download_instructions(uint8_t t) { DbpString("2. " _YELLOW_("mem spiffs dump h")); break; } + case ICE_STATE_DUMP_SIM: { + DbpString("The found tag will be dumped to " HF_ICALSSS_READSIM_TEMP_BIN); + DbpString("1. " _YELLOW_("mem spiffs tree")); + DbpString("2. " _YELLOW_("mem spiffs dump h")); + break; + } } } // Save to flash if file doesn't exist. // Write over file if size of flash file is less than new datalen -static void save_to_flash(uint8_t *data, uint16_t datalen) { +static void save_to_flash(uint8_t *data, uint16_t datalen, char *filename) { rdv40_spiffs_lazy_mount(); char fn[SPIFFS_OBJ_NAME_LEN]; - sprintf(fn, "iclass-%02X%02X%02X%02X%02X%02X%02X%02X.bin", - data[0], data[1], data[2], data[3], - data[4], data[5], data[6], data[7] - ); + + if (filename == NULL) { + sprintf(fn, "iclass-%02X%02X%02X%02X%02X%02X%02X%02X.bin", + data[0], data[1], data[2], data[3], + data[4], data[5], data[6], data[7] + ); + } else { + int fnlen = MIN(strlen(filename), SPIFFS_OBJ_NAME_LEN); + // if the given name len longer than buffer allows, cut it down to size + memcpy(fn, filename, fnlen); + } int res; if (exists_in_spiffs(fn) == false) { @@ -400,13 +419,180 @@ static int reader_dump_mode(void) { } } switch_off(); - save_to_flash(card_data, (start_block + dumped) * 8); + save_to_flash(card_data, (start_block + dumped) * 8, NULL); Dbprintf("%u bytes saved", (start_block + dumped) * 8); } DbpString("-=[ exiting " _CYAN_("`read & dump`") " mode ]=-"); return PM3_SUCCESS; } +static int dump_sim_mode(void) { + + DbpString("this mode has no tracelog"); + if (have_aa2()) + DbpString("dumping of " _YELLOW_("AA2 enabled")); + + for (;;) { + + BigBuf_free(); + + uint8_t *card_data = BigBuf_malloc(ICLASS_16KS_SIZE); + memset(card_data, 0xFF, ICLASS_16KS_SIZE); + + if (BUTTON_PRESS()) { + DbpString("button pressed"); + break; + } + + // setup authenticate AA1 + iclass_auth_req_t auth = { + .use_raw = false, + .use_elite = false, + .use_credit_key = false, + .do_auth = true, + .send_reply = false, + }; + memcpy(auth.key, legacy_aa1_key, sizeof(auth.key)); + + Iso15693InitReader(); + set_tracing(false); + + + picopass_hdr *hdr = (picopass_hdr *)card_data; + + // select tag. + uint32_t eof_time = 0; + bool res = select_iclass_tag(hdr, auth.use_credit_key, &eof_time); + if (res == false) { + switch_off(); + continue; + } + + // sanity check of CSN. + if (hdr->csn[7] != 0xE0 && hdr->csn[6] != 0x12) { + switch_off(); + continue; + } + + uint32_t start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + // get 3 config bits + uint8_t type = (hdr->conf.chip_config & 0x10) >> 2; + type |= (hdr->conf.mem_config & 0x80) >> 6; + type |= (hdr->conf.mem_config & 0x20) >> 5; + + Dbprintf(_GREEN_("%s") ", dumping...", card_types[type]); + + uint8_t pagemap = get_pagemap(hdr); + uint8_t app1_limit, app2_limit, start_block; + + // tags configured for NON SECURE PAGE, acts different + if (pagemap == PICOPASS_NON_SECURE_PAGEMODE) { + app1_limit = card_app2_limit[type]; + app2_limit = 0; + start_block = 3; + } else { + + app1_limit = hdr->conf.app_limit; + app2_limit = card_app2_limit[type]; + start_block = 5; + + res = authenticate_iclass_tag(&auth, hdr, &start_time, &eof_time, NULL); + if (res == false) { + switch_off(); + Dbprintf(_RED_("failed AA1 auth") ", skipping "); + continue; + } + + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + } + + uint16_t dumped = 0; + + // main read loop + for (uint16_t i = start_block; i <= app1_limit; i++) { + if (iclass_read_block(i, card_data + (8 * i), &start_time, &eof_time)) { + dumped++; + } + } + + if (pagemap != PICOPASS_NON_SECURE_PAGEMODE && have_aa2()) { + + // authenticate AA2 + auth.use_raw = false; + auth.use_credit_key = true; + memcpy(auth.key, aa2_key, sizeof(auth.key)); + + res = select_iclass_tag(hdr, auth.use_credit_key, &eof_time); + if (res) { + + // sanity check of CSN. + if (hdr->csn[7] != 0xE0 && hdr->csn[6] != 0x12) { + switch_off(); + continue; + } + + res = authenticate_iclass_tag(&auth, hdr, &start_time, &eof_time, NULL); + if (res) { + start_time = eof_time + DELAY_ICLASS_VICC_TO_VCD_READER; + + for (uint16_t i = app1_limit + 1; i <= app2_limit; i++) { + if (iclass_read_block(i, card_data + (8 * i), &start_time, &eof_time)) { + dumped++; + } + } + } else { + DbpString(_RED_("failed AA2 auth")); + } + } else { + DbpString(_RED_("failed selecting AA2")); + + // sanity check of CSN. + if (hdr->csn[7] != 0xE0 && hdr->csn[6] != 0x12) { + switch_off(); + continue; + } + } + } + switch_off(); + char *temp_file = HF_ICALSSS_READSIM_TEMP_BIN; + save_to_flash(card_data, (start_block + dumped) * 8, temp_file); + Dbprintf("%u bytes saved", (start_block + dumped) * 8); + + if (((start_block + dumped) * 8) > 0) { + break; //switch to sim mode + } + } + + rdv40_spiffs_lazy_mount(); + + SpinOff(0); + uint8_t *emul = BigBuf_get_EM_addr(); + uint32_t fsize = size_in_spiffs(HF_ICALSSS_READSIM_TEMP_BIN); + int res = rdv40_spiffs_read_as_filetype(HF_ICALSSS_READSIM_TEMP_BIN, emul, fsize, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_lazy_unmount(); + if (res == SPIFFS_OK) { + Dbprintf("loaded " _GREEN_(HF_ICALSSS_READSIM_TEMP_BIN) " (%u bytes)", fsize); + } + + Dbprintf("simming " _GREEN_(HF_ICALSSS_READSIM_TEMP_BIN)); + iclass_simulate(ICLASS_SIM_MODE_FULL, 0, false, NULL, NULL, NULL); + + LED_B_ON(); + rdv40_spiffs_lazy_mount(); + res = rdv40_spiffs_write(HF_ICALSSS_READSIM_TEMP_BIN, emul, fsize, RDV40_SPIFFS_SAFETY_SAFE); + rdv40_spiffs_lazy_unmount(); + LED_B_OFF(); + if (res == SPIFFS_OK) { + Dbprintf("wrote emulator memory to " _GREEN_(HF_ICALSSS_READSIM_TEMP_MOD_BIN)); + } else { + Dbprintf(_RED_("error") " writing "HF_ICALSSS_READSIM_TEMP_MOD_BIN" to flash ( %d )", res); + } + + DbpString("-=[ exiting " _CYAN_("`dump & sim`") " mode ]=-"); + return PM3_SUCCESS; +} + static int config_sim_mode(void) { uint8_t *emul = BigBuf_get_EM_addr(); @@ -436,7 +622,7 @@ void RunMod(void) { uint8_t mode = ICE_USE; uint8_t *bb = BigBuf_get_EM_addr(); - if (bb[0] > 0 && bb[0] < 5) { + if (bb[0] > 0 && bb[0] < HF_ICLASS_NUM_MODES) { mode = bb[0]; } @@ -512,6 +698,16 @@ void RunMod(void) { if (mode == ICE_STATE_CONFIGCARD) config_sim_mode(); + mode = ICE_STATE_NONE; + break; + } + case ICE_STATE_DUMP_SIM: { + DbpString("-=[ enter " _CYAN_("`dump & sim`") " mode, read 1 card and sim it ]=-"); + res = dump_sim_mode(); + if (res == PM3_SUCCESS) { + download_instructions(mode); + } + mode = ICE_STATE_NONE; break; } diff --git a/armsrc/Standalone/hf_tcprst.c b/armsrc/Standalone/hf_tcprst.c index 11b2b1935..f9d8b0b59 100644 --- a/armsrc/Standalone/hf_tcprst.c +++ b/armsrc/Standalone/hf_tcprst.c @@ -248,11 +248,9 @@ void RunMod(void) { dynamic_response_info.response[0] = receivedCmd[0]; if (memcmp("\x02\xa2\xb0\x00\x00\x1d\x51\x69", receivedCmd, 8) == 0) { - dynamic_response_info.response[0] = receivedCmd[0]; memcpy(dynamic_response_info.response + 1, ndef, 31); dynamic_response_info.response_n = 32; } else if (memcmp("\x02\x00\x20\x00\x01\x00\x6e\xa9", receivedCmd, 8) == 0) { - dynamic_response_info.response[0] = receivedCmd[0]; dynamic_response_info.response[1] = 0x63; dynamic_response_info.response[2] = 0x00; dynamic_response_info.response_n = 3; @@ -260,14 +258,12 @@ void RunMod(void) { memcpy(verify_pwd + 5, receivedCmd + 6, 16); DbpString("Reader sent password: "); Dbhexdump(16, verify_pwd + 5, 0); - dynamic_response_info.response[0] = receivedCmd[0]; dynamic_response_info.response[1] = 0x90; dynamic_response_info.response[2] = 0x00; dynamic_response_info.response_n = 3; gotkey = true; state = STATE_DUMP; } else { - dynamic_response_info.response[0] = receivedCmd[0]; dynamic_response_info.response[1] = 0x90; dynamic_response_info.response[2] = 0x00; dynamic_response_info.response_n = 3; @@ -321,7 +317,7 @@ void RunMod(void) { LED_B_ON(); uint8_t apdulen = iso14_apdu(apdus[i], (uint16_t) apdusLen[i], false, apdubuffer, NULL); - if (apdulen > 0) { + if (apdulen > 2) { DbpString(_YELLOW_("[ ") "Proxmark command" _YELLOW_(" ]")); Dbhexdump(apdusLen[i], apdus[i], false); DbpString(_GREEN_("[ ") "Card answer" _GREEN_(" ]")); @@ -429,11 +425,9 @@ void RunMod(void) { dynamic_response_info.response[0] = receivedCmd[0]; if (memcmp("\x02\xa2\xb0\x00\x00\x1d\x51\x69", receivedCmd, 8) == 0) { - dynamic_response_info.response[0] = receivedCmd[0]; memcpy(dynamic_response_info.response + 1, ndef, 31); dynamic_response_info.response_n = 32; } else if (memcmp("\x02\x00\x20\x00\x01\x00\x6e\xa9", receivedCmd, 8) == 0) { - dynamic_response_info.response[0] = receivedCmd[0]; dynamic_response_info.response[1] = 0x63; dynamic_response_info.response[2] = 0x00; dynamic_response_info.response_n = 3; @@ -441,12 +435,10 @@ void RunMod(void) { memcpy(verify_pwd + 5, receivedCmd + 6, 16); DbpString("Reader sent password: "); Dbhexdump(16, verify_pwd + 5, 0); - dynamic_response_info.response[0] = receivedCmd[0]; dynamic_response_info.response[1] = 0x90; dynamic_response_info.response[2] = 0x00; dynamic_response_info.response_n = 3; } else { - dynamic_response_info.response[0] = receivedCmd[0]; dynamic_response_info.response[1] = 0x90; dynamic_response_info.response[2] = 0x00; dynamic_response_info.response_n = 3; diff --git a/armsrc/Standalone/lf_em4100emul.c b/armsrc/Standalone/lf_em4100emul.c index e6ac2f6ff..b77f5e2e1 100644 --- a/armsrc/Standalone/lf_em4100emul.c +++ b/armsrc/Standalone/lf_em4100emul.c @@ -21,19 +21,19 @@ #include "commonutil.h" #define MAX_IND 16 // 4 LEDs - 2^4 combinations -#define CLOCK 64 //for 125kHz +#define LF_CLOCK 64 // for 125kHz // low & high - array for storage IDs. Its length must be equal. // Predefined IDs must be stored in low[]. static uint64_t low[] = {0x565A1140BE, 0x365A398149, 0x5555555555, 0xFFFFFFFFFF}; -static uint8_t *bba, slots_count; +static uint8_t slots_count; static int buflen; void ModInfo(void) { DbpString(" LF EM4100 simulator standalone mode"); } -static uint64_t ReversQuads(uint64_t bits) { +static uint64_t rev_quads(uint64_t bits) { uint64_t result = 0; for (int i = 0; i < 16; i++) { result += ((bits >> (60 - 4 * i)) & 0xf) << (4 * i); @@ -41,32 +41,40 @@ static uint64_t ReversQuads(uint64_t bits) { return result >> 24; } -static void FillBuff(uint8_t bit) { - memset(bba + buflen, bit, CLOCK / 2); - buflen += (CLOCK / 2); - memset(bba + buflen, bit ^ 1, CLOCK / 2); - buflen += (CLOCK / 2); +static void fill_buff(uint8_t bit) { + uint8_t *bba = BigBuf_get_addr(); + memset(bba + buflen, bit, LF_CLOCK / 2); + buflen += (LF_CLOCK / 2); + memset(bba + buflen, bit ^ 1, LF_CLOCK / 2); + buflen += (LF_CLOCK / 2); } -static void ConstructEM410xEmulBuf(uint64_t id) { +static void construct_EM410x_emul(uint64_t id) { - int i, j, binary[4], parity[4]; + int i, j; + int binary[4] = {0, 0, 0, 0}; + int parity[4] = {0, 0, 0, 0}; buflen = 0; + for (i = 0; i < 9; i++) - FillBuff(1); - parity[0] = parity[1] = parity[2] = parity[3] = 0; + fill_buff(1); + for (i = 0; i < 10; i++) { for (j = 3; j >= 0; j--, id /= 2) binary[j] = id % 2; + for (j = 0; j < 4; j++) - FillBuff(binary[j]); - FillBuff(binary[0] ^ binary[1] ^ binary[2] ^ binary[3]); + fill_buff(binary[j]); + + fill_buff(binary[0] ^ binary[1] ^ binary[2] ^ binary[3]); for (j = 0; j < 4; j++) parity[j] ^= binary[j]; } + for (j = 0; j < 4; j++) - FillBuff(parity[j]); - FillBuff(0); + fill_buff(parity[j]); + + fill_buff(0); } static void LED_Slot(int i) { @@ -85,14 +93,14 @@ void RunMod(void) { int selected = 0; //selected slot after start slots_count = ARRAYLEN(low); - bba = BigBuf_get_addr(); for (;;) { WDT_HIT(); if (data_available()) break; + SpinDelay(100); SpinUp(100); LED_Slot(selected); - ConstructEM410xEmulBuf(ReversQuads(low[selected])); + construct_EM410x_emul(rev_quads(low[selected])); SimulateTagLowFrequency(buflen, 0, true); selected = (selected + 1) % slots_count; } diff --git a/armsrc/Standalone/lf_em4100rswb.c b/armsrc/Standalone/lf_em4100rswb.c index 14bab8357..aaf82b5b5 100644 --- a/armsrc/Standalone/lf_em4100rswb.c +++ b/armsrc/Standalone/lf_em4100rswb.c @@ -53,8 +53,8 @@ #include "flashmem.h" #endif -#define LF_CLOCK 64 //for 125kHz -#define LF_RWSB_T55XX_TYPE 1 //Tag type: 0 - T5555, 1-T55x7 +#define LF_CLOCK 64 // for 125kHz +#define LF_RWSB_T55XX_TYPE 1 // Tag type: 0 - T5555, 1-T55x7 #define LF_RWSB_UNKNOWN_RESULT 0 #define LF_RWSB_BRUTE_STOPED 1 @@ -76,14 +76,13 @@ static int bruteforceSpeed[] = {10, 12, 14, 16}; // In high[] must be nulls static uint64_t low[] = {0, 0, 0, 0}; static uint32_t high[] = {0, 0, 0, 0}; -static uint8_t *bba; static int buflen; void ModInfo(void) { DbpString(" LF EM4100 read/sim/write/brute mode"); } -static uint64_t ReversQuads(uint64_t bits) { +static uint64_t rev_quads(uint64_t bits) { uint64_t result = 0; for (int i = 0; i < 16; i++) { result += ((bits >> (60 - 4 * i)) & 0xf) << (4 * i); @@ -91,33 +90,39 @@ static uint64_t ReversQuads(uint64_t bits) { return result >> 24; } -static void FillBuff(uint8_t bit) { +static void fill_buff(uint8_t bit) { + uint8_t *bba = BigBuf_get_addr(); memset(bba + buflen, bit, LF_CLOCK / 2); buflen += (LF_CLOCK / 2); memset(bba + buflen, bit ^ 1, LF_CLOCK / 2); buflen += (LF_CLOCK / 2); } -static void ConstructEM410xEmulBuf(uint64_t id) { - bba = BigBuf_get_addr(); - - int i, j, binary[4], parity[4]; +static void construct_EM410x_emul(uint64_t id) { + int i, j; + int binary[4] = {0, 0, 0, 0}; + int parity[4] = {0, 0, 0, 0}; buflen = 0; + for (i = 0; i < 9; i++) - FillBuff(1); - parity[0] = parity[1] = parity[2] = parity[3] = 0; + fill_buff(1); + for (i = 0; i < 10; i++) { for (j = 3; j >= 0; j--, id /= 2) binary[j] = id % 2; + for (j = 0; j < 4; j++) - FillBuff(binary[j]); - FillBuff(binary[0] ^ binary[1] ^ binary[2] ^ binary[3]); + fill_buff(binary[j]); + + fill_buff(binary[0] ^ binary[1] ^ binary[2] ^ binary[3]); for (j = 0; j < 4; j++) parity[j] ^= binary[j]; } + for (j = 0; j < 4; j++) - FillBuff(parity[j]); - FillBuff(0); + fill_buff(parity[j]); + + fill_buff(0); } static void LED_Update(int mode, int slot) { @@ -188,8 +193,8 @@ static uint64_t PackEmID(uint64_t original, int newCardNum) { buf &= ~(1 << i); } buf |= (newCardNum & 0xFFFF) << 1; - buf |= oddparity32((buf >> 1) & 0xFFF) & 1; - buf |= (evenparity32((buf >> 13) & 0xFFF) & 1) << 25; + buf |= oddparity32((buf >> 1) & 0xFFF); + buf |= (evenparity32((buf >> 13) & 0xFFF)) << 25; uint32_t cardnumNew = (buf >> 1) & 0xFFFF; uint32_t fcNew = (buf >> 17) & 0xFF; @@ -197,7 +202,6 @@ static uint64_t PackEmID(uint64_t original, int newCardNum) { return buf; } - static void PrintFcAndCardNum(uint64_t lowData) { // Calculate Facility Code and Card Number from high and low uint32_t fc = (lowData >> 17) & 0xFF; @@ -222,7 +226,7 @@ static int BruteEMTag(uint64_t originalCard, int slot) { cardnum = cardnum + direction; uint64_t currentCard = PackEmID(originalCard, cardnum); Dbprintf("[=] >> Simulating card id %"PRIx64" <<", currentCard); - ConstructEM410xEmulBuf(ReversQuads(currentCard)); + construct_EM410x_emul(rev_quads(currentCard)); SimulateTagLowFrequencyEx(buflen, 0, 1, bruteforceSpeed[bruteforceSpeedCurrent] * 10000); int button_pressed = BUTTON_CLICKED(1000); @@ -267,7 +271,7 @@ static int ExecuteMode(int mode, int slot) { return LF_RWSB_UNKNOWN_RESULT; case LF_RWSB_MODE_SIM: Dbprintf("[=] >> Sim mode started <<"); - ConstructEM410xEmulBuf(ReversQuads(low[slot])); + construct_EM410x_emul(rev_quads(low[slot])); SimulateTagLowFrequency(buflen, 0, 1); return LF_RWSB_UNKNOWN_RESULT; case LF_RWSB_MODE_WRITE: @@ -310,7 +314,6 @@ void RunMod() { int slot = 0; mode = SwitchMode(mode, slot); - bba = BigBuf_get_addr(); for (;;) { WDT_HIT(); if (data_available()) break; diff --git a/armsrc/Standalone/lf_em4100rwc.c b/armsrc/Standalone/lf_em4100rwc.c index d095b944b..5df6e1a90 100644 --- a/armsrc/Standalone/lf_em4100rwc.c +++ b/armsrc/Standalone/lf_em4100rwc.c @@ -41,7 +41,7 @@ // In high[] must be nulls static uint64_t low[] = {0x565AF781C7, 0x540053E4E2, 0x1234567890, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; static uint32_t high[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; -static uint8_t *bba, slots_count; +static uint8_t slots_count; static int buflen; void ModInfo(void) { @@ -56,7 +56,8 @@ static uint64_t rev_quads(uint64_t bits) { return result >> 24; } -static void fillbuff(uint8_t bit) { +static void fill_buff(uint8_t bit) { + uint8_t *bba = BigBuf_get_addr(); memset(bba + buflen, bit, LF_CLOCK / 2); buflen += (LF_CLOCK / 2); memset(bba + buflen, bit ^ 1, LF_CLOCK / 2); @@ -66,29 +67,29 @@ static void fillbuff(uint8_t bit) { static void construct_EM410x_emul(uint64_t id) { int i, j; - int binary[4] = {0}; - int parity[4] = {0}; + int binary[4] = {0, 0, 0, 0}; + int parity[4] = {0, 0, 0, 0}; buflen = 0; for (i = 0; i < 9; i++) - fillbuff(1); + fill_buff(1); for (i = 0; i < 10; i++) { for (j = 3; j >= 0; j--, id /= 2) binary[j] = id % 2; for (j = 0; j < 4; j++) - fillbuff(binary[j]); + fill_buff(binary[j]); - fillbuff(binary[0] ^ binary[1] ^ binary[2] ^ binary[3]); + fill_buff(binary[0] ^ binary[1] ^ binary[2] ^ binary[3]); for (j = 0; j < 4; j++) parity[j] ^= binary[j]; } for (j = 0; j < 4; j++) - fillbuff(parity[j]); + fill_buff(parity[j]); - fillbuff(0); + fill_buff(0); } static void led_slot(int i) { @@ -138,7 +139,6 @@ void RunMod(void) { // 3 - write to T5555 tag uint8_t state = 0; slots_count = ARRAYLEN(low); - bba = BigBuf_get_addr(); led_slot(selected); for (;;) { diff --git a/armsrc/Standalone/lf_tharexde.c b/armsrc/Standalone/lf_tharexde.c index 887100855..2a7a686fb 100644 --- a/armsrc/Standalone/lf_tharexde.c +++ b/armsrc/Standalone/lf_tharexde.c @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Tharexde, 2020 +// tharexde, 2021 // // 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 @@ -8,10 +8,12 @@ // main code for EM4x50 simulator and collector aka THAREXDE //----------------------------------------------------------------------------- #include +#include "ticks.h" #include "standalone.h" #include "proxmark3_arm.h" #include "appmain.h" #include "BigBuf.h" +#include "commonutil.h" #include "fpgaloader.h" #include "util.h" #include "dbprint.h" @@ -19,68 +21,75 @@ #include "../em4x50.h" /* - * `lf_tharexde` simulates hardcoded words/blocks, reads words of standard read - * mode of EM4x50 tags and stores them in internal flash. + * `lf_tharexde` simulates EM4x50 dumps uploaded to flash, reads words + * transmitted by EM4x50 tags in standard read mode and stores them in + * internal flash. * It requires RDV4 hardware (for flash and battery). * - * On entering stand-alone mode, this module will start reading/record EM4x50 data. - * Every found / collected data will be written/appended to the logfile in flash - * as a text string. + * On entering stand-alone mode, this module will start simulating EM4x50 data. + * Data is read from eml dump file uploaded to flash memory (lf_em4x50_simulate.eml). + * If reader sends password different from dump file password, it is saved in + * file lf_em4x50_passwords.log in flash memory. + * + * On switching to read/record mode by pressing pm3 button, module will start + * reading EM4x50 data. Each collected data set will be written/appended to the + * logfile in flash (lf_em4x50_collect.log) as a text string. * * LEDs: * - LED A: simulating - * - LED B: reading / record - * - LED C: writing to flash + * - LED A blinking: no simulation data or read error + * - LED B: reading/recording * - LED D: unmounting/sync'ing flash (normally < 100ms) * + * To upload input file (eml format) to flash: + * - mem spiffs load f o lf_em4x50_simulate.eml + * + * To retrieve password file from flash: + * - mem spiffs dump o lf_em4x50_passwords.log f + * * To retrieve log file from flash: - * - * 1. mem spiffs dump o lf_em4x50collect.log f lf_em4x50collect.log - * Copies log file from flash to your client. - * - * 2. exit the Proxmark3 client - * - * 3. more lf_tharexdecollect.log + * - mem spiffs dump o lf_em4x50_collect.log f * * This module emits debug strings during normal operation -- so try it out in * the lab connected to PM3 client before taking it into the field. * - * To delete the log file from flash: + * To delete the input file from flash: + * - mem spiffs remove lf_em4x50_simulate.eml * - * 1. mem spiffs remove lf_tharexdecollect.log + * To delete the log file from flash: + * - mem spiffs remove lf_em4x50_passwords.log + * + * To delete the log file from flash: + * - mem spiffs remove lf_em4x50_collect.log */ -#define STATE_SIM 0 -#define STATE_READ 1 -#define STATE_BRUTE 2 -#define EM4X50_TAG_WORD 45 -#define EM4X50_PWD_SPEED 27 -#define LF_EM4X50SIMULATE_INPUTFILE "lf_em4x50simulate.eml" -#define LF_EM4X50COLLECT_LOGFILE "lf_em4x50collect.log" -#define LF_EM4X50BRUTE_INPUTFILE "lf_em4x50brute.eml" -#define LF_EM4X50BRUTE_LOGFILE "lf_em4x50brute.log" +#define STATE_SIM 0 +#define STATE_READ 1 +#define LF_EM4X50_INPUTFILE_SIM "lf_em4x50_simulate.eml" +#define LF_EM4X50_LOGFILE_SIM "lf_em4x50_passwords.log" +#define LF_EM4X50_LOGFILE_COLLECT "lf_em4x50_collect.log" +#define MAX_NO_PWDS_TO_SAVE 50 -bool input_exists; -bool log_exists; +uint32_t gPassword; static void LoadDataInstructions(const char *inputfile) { Dbprintf(""); - Dbprintf("To load datafile into flash and display it:"); - Dbprintf(_YELLOW_("1.") " edit inputfile %s", inputfile); - Dbprintf(_YELLOW_("2.") " start proxmark3 client"); - Dbprintf(_YELLOW_("3.") " mem spiffs load f %s o %s", inputfile, inputfile); - Dbprintf(_YELLOW_("4.") " start standalone mode"); + Dbprintf("To load datafile to flash and display it:"); + Dbprintf("1. edit input file %s", inputfile); + Dbprintf("2. start proxmark3 client"); + Dbprintf("3. mem spiffs load f o %s", inputfile); + Dbprintf("4. start standalone mode"); } static void DownloadLogInstructions(const char *logfile) { Dbprintf(""); Dbprintf("To get the logfile from flash and display it:"); - Dbprintf(_YELLOW_("1.") " mem spiffs dump o %s f %s", logfile, logfile); - Dbprintf(_YELLOW_("2.") " exit proxmark3 client"); - Dbprintf(_YELLOW_("3.") " cat %s", logfile); + Dbprintf("1. mem spiffs dump o %s f ", logfile); + Dbprintf("2. exit proxmark3 client"); + Dbprintf("3. cat "); } -static int get_input_data_from_file(uint32_t *words, char *inputfile) { +static bool get_input_data_from_file(uint32_t *tag, char *inputfile) { size_t now = 0; @@ -88,92 +97,92 @@ static int get_input_data_from_file(uint32_t *words, char *inputfile) { uint32_t size = size_in_spiffs(inputfile); uint8_t *mem = BigBuf_malloc(size); - + Dbprintf(_YELLOW_("found input file %s"), inputfile); rdv40_spiffs_read_as_filetype(inputfile, mem, size, RDV40_SPIFFS_SAFETY_SAFE); now = size / 9; - for (int i = 0; i < now; i++) - for (int j = 0; j < 4; j++) - words[i] |= (hex2int(mem[2 * j + 9 * i]) << 4 | hex2int(mem[2 * j + 1 + 9 * i])) << ((3 - j) * 8); + for (int i = 0; i < now; i++) { + for (int j = 0; j < 4; j++) { + tag[i] |= (hex2int(mem[2 * j + 9 * i]) << 4 | hex2int(mem[2 * j + 1 + 9 * i])) << ((3 - j) * 8); + } + } - Dbprintf(_YELLOW_("read data from input file")); + Dbprintf(_YELLOW_("read tag data from input file")); + } else { + Dbprintf(_RED_("no input file %s"), inputfile); } BigBuf_free(); - return (now > 0) ? now : 0; + return ((now == EM4X50_NO_WORDS) && (tag[EM4X50_DEVICE_SERIAL] != tag[EM4X50_DEVICE_ID])); } static void append(const char *filename, uint8_t *entry, size_t entry_len) { - - LED_D_ON(); - if (log_exists == false) { - rdv40_spiffs_write(filename, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE); - log_exists = true; - } else { + if (exists_in_spiffs(filename)) { rdv40_spiffs_append(filename, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE); + } else { + rdv40_spiffs_write(filename, entry, entry_len, RDV40_SPIFFS_SAFETY_SAFE); + } +} + +static void save_pwds(uint32_t *pwdlist, size_t no_pwd) { + uint8_t entry[10] = {0}; + + if (no_pwd > 0) { + Dbprintf(""); + for (int i = 0; i < no_pwd; i++) { + sprintf((char *)entry, "%08"PRIx32"\n", pwdlist[i]); + append(LF_EM4X50_LOGFILE_SIM, entry, strlen((char *)entry)); + Dbprintf("received password: %08"PRIx32"", pwdlist[i]); + } } - LED_D_OFF(); } void ModInfo(void) { - DbpString(_YELLOW_(" LF EM4x50 sim/collector/bruteforce mode") " - a.k.a tharexde"); + DbpString(_YELLOW_(" LF EM4x50 sim/collector mode") " - a.k.a tharexde"); } void RunMod(void) { - bool state_change = true;//, password_found = false; - int pwd_found = false; - uint8_t state = STATE_SIM; - // declarations for simulating - uint32_t words[33] = {0x0}; - uint32_t pwd = 0x0; - uint32_t passwords[2] = {0x0}; - size_t now = 0; - // declarations for reading - int no_words = 0; - //uint32_t words[EM4X50_TAG_WORD]; - uint8_t entry[81]; + bool state_change = true, read_ok = false; + int no_words = 0, command = 0, no_pwd = 0; + uint8_t entry[400], state = STATE_SIM; + uint32_t tag[EM4X50_NO_WORDS] = {0x0}, pwdlist[MAX_NO_PWDS_TO_SAVE]; rdv40_spiffs_lazy_mount(); - StandAloneMode(); Dbprintf(_YELLOW_("Standalone mode THAREXDE started")); for (;;) { WDT_HIT(); - if (data_available()) break; + if (data_available()) { + break; + } - // press button - toggle between SIM, READ and BRUTE + // press button - toggle between SIM and READ // hold button - exit int button_pressed = BUTTON_CLICKED(1000); if (button_pressed == BUTTON_SINGLE_CLICK) { - SpinUp(100); - switch (state) { - case STATE_SIM: + // save and display passwords + save_pwds(pwdlist, no_pwd); state = STATE_READ; break; case STATE_READ: - state = STATE_BRUTE; - break; - case STATE_BRUTE: state = STATE_SIM; break; default: break; } - + state_change = true; } else if (button_pressed == BUTTON_HOLD) { - - SpinDown(100); break; } @@ -181,170 +190,102 @@ void RunMod(void) { if (state_change) { - FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); - FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); - - AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT | GPIO_SSC_CLK; - AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; - AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_CLK; - + // initialize simulation mode LEDsoff(); LED_A_ON(); Dbprintf(""); Dbprintf(_YELLOW_("switched to EM4x50 simulating mode")); - now = get_input_data_from_file(words, LF_EM4X50SIMULATE_INPUTFILE); - if (now > 0) { - Dbprintf(_YELLOW_("simulating %i blocks"), now); - for (int i = 0; i < now; i++) - Dbprintf("%2i -> %lx", i + 1, words[i]); - + read_ok = get_input_data_from_file(tag, LF_EM4X50_INPUTFILE_SIM); + if (read_ok) { + Dbprintf(_YELLOW_("tag data ok")); } else { - Dbprintf(_RED_("error in input data")); + Dbprintf(_RED_("error in tag data")); + LoadDataInstructions(LF_EM4X50_INPUTFILE_SIM); } + LED_D_OFF(); + gLogin = false; + gPassword = reflect32(tag[0]); + gWritePasswordProcess = false; + command = EM4X50_COMMAND_STANDARD_READ; + no_pwd = 0; + memset(pwdlist, 0, sizeof(pwdlist)); + + em4x50_setup_sim(); state_change = false; } - em4x50_sim_send_listen_window(); - for (int i = 0; i < now; i++) { - em4x50_sim_send_listen_window(); - em4x50_sim_send_word(words[i]); + // if no data or read error -> blink + if (read_ok == false) { + LED(LED_A, 200); + SpinDelay(200); + } + + em4x50_handle_commands(&command, tag); + + // check if new password was found + if (gPassword != reflect32(tag[EM4X50_DEVICE_PASSWORD])) { + if (no_pwd < MAX_NO_PWDS_TO_SAVE) { + pwdlist[no_pwd] = gPassword; + no_pwd++; + } + gPassword = reflect32(tag[EM4X50_DEVICE_PASSWORD]); + } + + // if timeout (e.g. no reader field) continue with standard read + // mode and reset former authentication + if (command == PM3_ETIMEOUT) { + command = EM4X50_COMMAND_STANDARD_READ; + gLogin = false; + LED_D_OFF(); } } else if (state == STATE_READ) { if (state_change) { + // initialize read mode LEDsoff(); LED_B_ON(); Dbprintf(""); Dbprintf(_YELLOW_("switched to EM4x50 reading mode")); - memset(entry, 0, sizeof(entry)); - memset(words, 0, sizeof(words)); - - log_exists = exists_in_spiffs(LF_EM4X50COLLECT_LOGFILE); - + em4x50_setup_read(); state_change = false; } - no_words = em4x50_standalone_read(words); + no_words = 0; + memset(tag, 0, sizeof(tag)); + standard_read(&no_words, tag); if (no_words > 0) { memset(entry, 0, sizeof(entry)); - sprintf((char *)entry, "found new EM4x50 tag:"); - Dbprintf("%s", entry); - strcat((char *)entry, "\n"); - append(LF_EM4X50COLLECT_LOGFILE, entry, strlen((char *)entry)); - + sprintf((char *)entry, "found EM4x50 tag:\n"); for (int i = 0; i < no_words; i++) { - - sprintf((char *)entry, " %2i -> 0x%08"PRIx32"", i + 1, words[i]); - Dbprintf("%s", entry); - strcat((char *)entry, "\n"); - append(LF_EM4X50COLLECT_LOGFILE, entry, strlen((char *)entry)); + sprintf((char *)entry + strlen((char *)entry), "%08"PRIx32"\n", tag[i]); } - } - - } else if (state == STATE_BRUTE) { - - if (state_change) { - - LEDsoff(); - LED_C_ON(); - Dbprintf(""); - Dbprintf(_YELLOW_("switched to EM4x50 brute force mode")); - - log_exists = exists_in_spiffs(LF_EM4X50BRUTE_LOGFILE); - now = get_input_data_from_file(passwords, LF_EM4X50BRUTE_INPUTFILE); - - if (now == 2) { - - // print some information - int no_iter = passwords[1] - passwords[0] + 1; - int dur_s = no_iter / EM4X50_PWD_SPEED; - int dur_h = dur_s / 3600; - int dur_m = (dur_s - dur_h * 3600) / 60; - dur_s -= dur_h * 3600 + dur_m * 60; - - //iterprint = no_iter/10; - - Dbprintf(_YELLOW_("trying %i passwords in range [0x%08x, 0x%08x]"), - no_iter, passwords[0], passwords[1]); - Dbprintf(_YELLOW_("estimated duration: %ih%im%is"), - dur_h, dur_m, dur_s); - - } else { - Dbprintf(_RED_("error in input data")); - break; - } - - state_change = false; - } - - pwd_found = em4x50_standalone_brute(passwords[0], passwords[1], &pwd); - - if (pwd_found == PM3_ETIMEOUT) { - - // timeout -> no EM4x50 tag on reader? - Dbprintf(_YELLOW_("timeout - no EM4x50 tag detected")); - - } else if (pwd_found == true) { - - // password found -> write to logfile - sprintf((char *)entry, "password found: 0x%08"PRIx32, pwd); - Dbprintf(_YELLOW_("%s"), entry); - strcat((char *)entry, "\n"); - append(LF_EM4X50BRUTE_LOGFILE, entry, strlen((char *)entry)); - - break; - - } else { - - if (pwd == passwords[1] + 1) { - - // finished without success -> write to logfile - sprintf((char *)entry, "no password found"); - Dbprintf(_YELLOW_("%s"), entry); - strcat((char *)entry, "\n"); - append(LF_EM4X50BRUTE_LOGFILE, entry, strlen((char *)entry)); - - - } else { - - // stopped -> write to logfile - sprintf((char *)entry, "stopped search - last password: 0x%08"PRIx32, pwd); - Dbprintf(_YELLOW_("%s"), entry); - strcat((char *)entry, "\n"); - append(LF_EM4X50BRUTE_LOGFILE, entry, strlen((char *)entry)); - - // replace start password by last tested password in - // inputfile (spiffs) so that brute forcing process will - // be continued when envoking brute force mode again - sprintf((char *)entry, "%08"PRIx32"\n%08"PRIx32"\n", pwd, passwords[1]); - rdv40_spiffs_write(LF_EM4X50BRUTE_INPUTFILE, - entry, - strlen((char *)entry), - RDV40_SPIFFS_SAFETY_SAFE); - - } - - break; + Dbprintf("%s", entry); + sprintf((char *)entry + strlen((char *)entry), "\n"); + append(LF_EM4X50_LOGFILE_COLLECT, entry, strlen((char *)entry)); } } + + // reset timer + AT91C_BASE_TC1->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // re-enable timer and wait for TC0 + AT91C_BASE_TC0->TC_RC = 0; // set TIOA (carry bit) on overflow, return to zero + AT91C_BASE_TC0->TC_RA = 1; // clear carry bit on next clock cycle + AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; // reset and re-enable timer } if (state == STATE_READ) { - DownloadLogInstructions(LF_EM4X50COLLECT_LOGFILE); - } else if (state == STATE_BRUTE) { - LoadDataInstructions(LF_EM4X50BRUTE_INPUTFILE); - DownloadLogInstructions(LF_EM4X50BRUTE_LOGFILE); + DownloadLogInstructions(LF_EM4X50_LOGFILE_COLLECT); } else { - LoadDataInstructions(LF_EM4X50SIMULATE_INPUTFILE); + // save and display passwords + save_pwds(pwdlist, no_pwd); + DownloadLogInstructions(LF_EM4X50_LOGFILE_SIM); } LED_D_ON(); @@ -352,8 +293,8 @@ void RunMod(void) { LED_D_OFF(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); Dbprintf(""); Dbprintf(_YELLOW_("[=] Standalone mode THAREXDE stopped")); - } diff --git a/armsrc/appmain.c b/armsrc/appmain.c index c4cc6aaf2..6a6058d3b 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -50,7 +50,7 @@ #include "crc16.h" #ifdef WITH_LCD -#include "LCD.h" +#include "LCD_disabled.h" #endif #ifdef WITH_SMARTCARD @@ -257,6 +257,9 @@ void ReadMem(int addr) { /* osimage version information is linked in, cf commonutil.h */ /* bootrom version information is pointed to from _bootphase1_version_pointer */ extern char *_bootphase1_version_pointer, _flash_start, _flash_end, __data_src_start__; +#ifdef WITH_NO_COMPRESSION +extern char *_bootrom_end, _bootrom_start, __os_size__; +#endif static void SendVersion(void) { char temp[PM3_CMD_DATA_SIZE - 12]; /* Limited data payload in USB packets */ char VersionString[PM3_CMD_DATA_SIZE - 12] = { '\0' }; @@ -295,9 +298,11 @@ static void SendVersion(void) { strncat(VersionString, "\n ", sizeof(VersionString) - strlen(VersionString) - 1); } } +#ifndef WITH_NO_COMPRESSION // Send Chip ID and used flash memory uint32_t text_and_rodata_section_size = (uint32_t)&__data_src_start__ - (uint32_t)&_flash_start; uint32_t compressed_data_section_size = common_area.arg1; +#endif struct p { uint32_t id; @@ -308,7 +313,11 @@ static void SendVersion(void) { struct p payload; payload.id = *(AT91C_DBGU_CIDR); +#ifdef WITH_NO_COMPRESSION + payload.section_size = (uint32_t)&_bootrom_end - (uint32_t)&_bootrom_start + (uint32_t)&__os_size__; +#else payload.section_size = text_and_rodata_section_size + compressed_data_section_size; +#endif payload.versionstr_len = strlen(VersionString) + 1; memcpy(payload.versionstr, VersionString, payload.versionstr_len); @@ -1136,7 +1145,7 @@ static void PacketReceived(PacketCommandNG *packet) { // destroy the Emulator Memory. //----------------------------------------------------------------------------- FpgaDownloadAndGo(FPGA_BITSTREAM_LF); - em4x50_sim((uint8_t *)packet->data.asBytes); + em4x50_sim((uint32_t *)packet->data.asBytes); break; } case CMD_LF_EM4X50_READER: { @@ -1170,6 +1179,26 @@ static void PacketReceived(PacketCommandNG *packet) { em4x70_info((em4x70_data_t *)packet->data.asBytes); break; } + case CMD_LF_EM4X70_WRITE: { + em4x70_write((em4x70_data_t *)packet->data.asBytes); + break; + } + case CMD_LF_EM4X70_UNLOCK: { + em4x70_unlock((em4x70_data_t *)packet->data.asBytes); + break; + } + case CMD_LF_EM4X70_AUTH: { + em4x70_auth((em4x70_data_t *)packet->data.asBytes); + break; + } + case CMD_LF_EM4X70_WRITEPIN: { + em4x70_write_pin((em4x70_data_t *)packet->data.asBytes); + break; + } + case CMD_LF_EM4X70_WRITEKEY: { + em4x70_write_key((em4x70_data_t *)packet->data.asBytes); + break; + } #endif #ifdef WITH_ISO15693 @@ -1637,10 +1666,6 @@ static void PacketReceived(PacketCommandNG *packet) { iClass_ReadBlock(packet->data.asBytes); break; } - case CMD_HF_ICLASS_AUTH: { //check - iClass_Authentication(packet->data.asBytes); - break; - } case CMD_HF_ICLASS_CHKKEYS: { iClass_Authentication_fast(packet->oldarg[0], packet->oldarg[1], packet->data.asBytes); break; diff --git a/armsrc/desfire_crypto.c b/armsrc/desfire_crypto.c index cafa98de1..d414835c0 100644 --- a/armsrc/desfire_crypto.c +++ b/armsrc/desfire_crypto.c @@ -242,7 +242,6 @@ void Desfire_session_key_new(const uint8_t rnda[], const uint8_t rndb[], desfire } } -static void xor(const uint8_t *ivect, uint8_t *data, const size_t len); static size_t key_macing_length(desfirekey_t key); // iceman, see memxor inside string.c, dest/src swapped.. @@ -264,20 +263,20 @@ void cmac_generate_subkeys(desfirekey_t key) { mifare_cypher_blocks_chained(NULL, key, ivect, l, kbs, MCD_RECEIVE, MCO_ENCYPHER); - bool xor = false; + bool txor = false; // Used to compute CMAC on complete blocks memcpy(key->cmac_sk1, l, kbs); - xor = l[0] & 0x80; + txor = l[0] & 0x80; lsl(key->cmac_sk1, kbs); - if (xor) + if (txor) key->cmac_sk1[kbs - 1] ^= R; // Used to compute CMAC on the last block if non-complete memcpy(key->cmac_sk2, key->cmac_sk1, kbs); - xor = key->cmac_sk1[0] & 0x80; + txor = key->cmac_sk1[0] & 0x80; lsl(key->cmac_sk2, kbs); - if (xor) + if (txor) key->cmac_sk2[kbs - 1] ^= R; } diff --git a/armsrc/em4x50.c b/armsrc/em4x50.c index 36923b6a5..eae16059f 100644 --- a/armsrc/em4x50.c +++ b/armsrc/em4x50.c @@ -11,6 +11,7 @@ #include "fpgaloader.h" #include "ticks.h" #include "dbprint.h" +#include "lfsampling.h" #include "lfadc.h" #include "lfdemod.h" #include "commonutil.h" @@ -23,60 +24,73 @@ // TIMER_CLOCK1 = MCK/2, MCK is running at 48 MHz, Timer is running at 48/2 = 24 MHz // EM4x50 units (T0) have duration of 8 microseconds (us), which is 1/125000 per second (carrier) // T0 = TIMER_CLOCK1 / 125000 = 192 - #ifndef T0 #define T0 192 #endif +// conversions (carrier frequency 125 kHz): +// 1 us = 1.5 ticks +// 1 cycle = 1 period = 8 us = 12 ticks +// 1 bit = 64 cycles = 768 ticks = 512 us (for Opt64) +#define CYCLES2TICKS 12 +#define CYCLES2MUSEC 8 + +// given in cycles/periods #define EM4X50_T_TAG_QUARTER_PERIOD 16 #define EM4X50_T_TAG_HALF_PERIOD 32 #define EM4X50_T_TAG_THREE_QUARTER_PERIOD 48 #define EM4X50_T_TAG_FULL_PERIOD 64 #define EM4X50_T_TAG_TPP 64 #define EM4X50_T_TAG_TWA 64 +#define EM4X50_T_TAG_TINIT 2112 +#define EM4X50_T_TAG_TWEE 3200 #define EM4X50_T_TAG_WAITING_FOR_SIGNAL 75 #define EM4X50_T_WAITING_FOR_DBLLIW 1550 -#define EM4X50_T_WAITING_FOR_SNGLLIW 140 // this value seems to be - // critical; - // if it's too low - // (e.g. < 120) some cards - // are no longer readable - // although they're ok +#define EM4X50_T_WAITING_FOR_ACK 4 +#define EM4X50_T_TOLERANCE 8 +#define EM4X50_T_ZERO_DETECTION 3 -#define EM4X50_TAG_TOLERANCE 8 +// timeout values (empirical) for simulation mode (may vary with regard to reader) +#define EM4X50_T_SIMULATION_TIMEOUT_READ 600 +#define EM4X50_T_SIMULATION_TIMEOUT_WAIT 50 + +// the following value (pulses) seems to be critical; if it's too low +//(e.g. < 120) some cards are no longer readable although they're ok +#define EM4X50_T_WAITING_FOR_SNGLLIW 140 + +// div #define EM4X50_TAG_WORD 45 - -#define EM4X50_COMMAND_LOGIN 0x01 -#define EM4X50_COMMAND_RESET 0x80 -#define EM4X50_COMMAND_WRITE 0x12 -#define EM4X50_COMMAND_WRITE_PASSWORD 0x11 -#define EM4X50_COMMAND_SELECTIVE_READ 0x0A +#define EM4X50_TAG_MAX_NO_BYTES 136 +#define EM4X50_TIMEOUT_PULSE_EVAL 2500 int gHigh = 190; int gLow = 60; -// do nothing for using timer0 -static void wait_timer(uint32_t period) { - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (AT91C_BASE_TC0->TC_CV < period); -} - +// indication whether a previous login has been successful, so operations +// that require authentication can be handled +bool gLogin = false; +// WritePassword process in simulation mode is handled in a different way +// compared to operations like read, write, login, so it is necessary to +// to be able to identfiy it +bool gWritePasswordProcess = false; +// if reader sends a different password than "expected" -> save it +uint32_t gPassword = 0; // extract and check parities // return result of parity check and extracted plain data static bool extract_parities(uint64_t word, uint32_t *data) { - + uint8_t row_parities = 0x0, col_parities = 0x0; uint8_t row_parities_calculated = 0x0, col_parities_calculated = 0x0; - + *data = 0x0; - + // extract plain data (32 bits) from raw word (45 bits) for (int i = 0; i < 4; i++) { *data <<= 8; *data |= (word >> ((4 - i) * 9 + 1)) & 0xFF; } - + // extract row parities (4 bits + stop bit) from raw word (45 bits) for (int i = 0; i < 5; i++) { row_parities <<= 1; @@ -106,20 +120,22 @@ static bool extract_parities(uint64_t word, uint32_t *data) { col_parities_calculated ^= (*data >> ((3 - j) * 8 + (7 - i))) & 0x1; } } - + if ((row_parities == row_parities_calculated) && (col_parities == col_parities_calculated)) return true; return false; } -static void em4x50_setup_read(void) { +void em4x50_setup_read(void) { FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); + StartTicks(); + // 50ms for the resonant antenna to settle. - SpinDelay(50); + WaitMS(50); // Now set up the SSC to get the ADC samples that are now streaming at us. FpgaSetupSsc(FPGA_MAJOR_MODE_LF_READER); @@ -137,30 +153,11 @@ static void em4x50_setup_read(void) { // Disable modulation at default, which means enable the field LOW(GPIO_SSC_DOUT); - // Enable Peripheral Clock for - // TIMER_CLOCK0, used to measure exact timing before answering - AT91C_BASE_PMC->PMC_PCER |= (1 << AT91C_ID_TC0);// | (1 << AT91C_ID_TC1); - AT91C_BASE_PIOA->PIO_BSR = GPIO_SSC_FRAME; - - // Disable timer during configuration - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKDIS; - - // TC0: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), no triggers - AT91C_BASE_TC0->TC_CMR = AT91C_TC_CLKS_TIMER_DIV1_CLOCK; - - // TC1: Capture mode, default timer source = MCK/2 (TIMER_CLOCK1), no triggers - - // Enable and reset counters - AT91C_BASE_TC0->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; - - // synchronized startup procedure - while (AT91C_BASE_TC0->TC_CV > 0) {}; // wait until TC1 returned to zero - // Watchdog hit WDT_HIT(); } -static void em4x50_setup_sim(void) { +void em4x50_setup_sim(void) { FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_EDGE_DETECT); FpgaSendCommand(FPGA_CMD_SET_DIVISOR, LF_DIVISOR_125); @@ -168,6 +165,11 @@ static void em4x50_setup_sim(void) { AT91C_BASE_PIOA->PIO_PER = GPIO_SSC_DOUT | GPIO_SSC_CLK; AT91C_BASE_PIOA->PIO_OER = GPIO_SSC_DOUT; AT91C_BASE_PIOA->PIO_ODR = GPIO_SSC_CLK; + + StartTicks(); + + // Watchdog hit + WDT_HIT(); } // calculate signal properties (mean amplitudes) from measured data: @@ -182,16 +184,14 @@ static bool get_signalproperties(void) { uint32_t sample_max_sum = 0; memset(sample_max, 0x00, sizeof(sample_max)); - LED_A_ON(); - // wait until signal/noise > 1 (max. 32 periods) for (int i = 0; i < EM4X50_T_TAG_WAITING_FOR_SIGNAL; i++) { if (BUTTON_PRESS()) return false; // about 2 samples per bit period - wait_timer(T0 * EM4X50_T_TAG_HALF_PERIOD); - + WaitUS(EM4X50_T_TAG_HALF_PERIOD * CYCLES2MUSEC); + // ignore first samples if ((i > SIGNAL_IGNORE_FIRST_SAMPLES) && (AT91C_BASE_SSC->SSC_RHR > noise)) { signal_found = true; @@ -200,7 +200,6 @@ static bool get_signalproperties(void) { } if (signal_found == false) { - LED_A_OFF(); return false; } @@ -208,8 +207,8 @@ static bool get_signalproperties(void) { // 3 single "full periods" to eliminate the influence of a listen window for (int i = 0; i < no_periods; i++) { - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (AT91C_BASE_TC0->TC_CV < T0 * 3 * EM4X50_T_TAG_FULL_PERIOD) { + uint32_t tval = GetTicks(); + while (GetTicks() - tval < 12 * 3 * EM4X50_T_TAG_FULL_PERIOD) { if (BUTTON_PRESS()) return false; @@ -228,8 +227,6 @@ static bool get_signalproperties(void) { gHigh = sample_ref + pct * (sample_max_mean - sample_ref) / 100; gLow = sample_ref - pct * (sample_max_mean - sample_ref) / 100; - LED_A_OFF(); - return true; } @@ -240,12 +237,12 @@ static bool get_signalproperties(void) { static bool invalid_bit(void) { // get sample at 3/4 of bit period - wait_timer(T0 * EM4X50_T_TAG_THREE_QUARTER_PERIOD); + WaitUS(EM4X50_T_TAG_THREE_QUARTER_PERIOD * CYCLES2MUSEC); uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; // wait until end of bit period - wait_timer(T0 * EM4X50_T_TAG_QUARTER_PERIOD); + WaitUS(EM4X50_T_TAG_QUARTER_PERIOD * CYCLES2MUSEC); // bit in "undefined" state? if (sample <= gHigh && sample >= gLow) @@ -256,69 +253,71 @@ static bool invalid_bit(void) { static uint32_t get_pulse_length(void) { - int32_t timeout = (T0 * 3 * EM4X50_T_TAG_FULL_PERIOD); + int32_t timeout = EM4X50_TIMEOUT_PULSE_EVAL, tval = 0; - // iterates pulse length (low -> high -> low) + // iterates pulse lengths (low -> high -> low) volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; while (sample > gLow && (timeout--)) sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - if (timeout == 0) + if (timeout <= 0) return 0; - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - timeout = (T0 * 3 * EM4X50_T_TAG_FULL_PERIOD); + tval = GetTicks(); + timeout = EM4X50_TIMEOUT_PULSE_EVAL; while (sample < gHigh && (timeout--)) sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - if (timeout == 0) + if (timeout <= 0) return 0; - timeout = (T0 * 3 * EM4X50_T_TAG_FULL_PERIOD); + timeout = EM4X50_TIMEOUT_PULSE_EVAL; while (sample > gLow && (timeout--)) sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - if (timeout == 0) + if (timeout <= 0) return 0; - return (uint32_t)AT91C_BASE_TC0->TC_CV; + return GetTicks() - tval; } // check if pulse length corresponds to given length static bool check_pulse_length(uint32_t pl, int length) { - return ((pl >= T0 * (length - EM4X50_TAG_TOLERANCE)) && (pl <= T0 * (length + EM4X50_TAG_TOLERANCE))); + return ((pl >= (length - EM4X50_T_TOLERANCE) * CYCLES2TICKS) && + (pl <= (length + EM4X50_T_TOLERANCE) * CYCLES2TICKS)); } // send single bit according to EM4x50 application note and datasheet static void em4x50_reader_send_bit(int bit) { + // reset clock for the next bit - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; + uint32_t tval = GetTicks(); if (bit == 0) { - // disable modulation (drops the field) for 7 cycles of carrier + // disable modulation (activate the field) for 7 cycles of carrier // period (Opt64) LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * 7); + while (GetTicks() - tval < 7 * CYCLES2TICKS); - // enable modulation (activates the field) for remaining first + // enable modulation (drop the field) for remaining first // half of bit period HIGH(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * EM4X50_T_TAG_HALF_PERIOD); + while (GetTicks() - tval < EM4X50_T_TAG_HALF_PERIOD * CYCLES2TICKS); // disable modulation for second half of bit period LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * EM4X50_T_TAG_FULL_PERIOD); + while (GetTicks() - tval < EM4X50_T_TAG_FULL_PERIOD * CYCLES2TICKS); } else { // bit = "1" means disable modulation for full bit period LOW(GPIO_SSC_DOUT); - while (AT91C_BASE_TC0->TC_CV < T0 * EM4X50_T_TAG_FULL_PERIOD); + while (GetTicks() - tval < EM4X50_T_TAG_FULL_PERIOD * CYCLES2TICKS); } } @@ -329,12 +328,12 @@ static void em4x50_reader_send_byte(uint8_t byte) { } } -// send byte followed by its (equal) parity bit +// send byte followed by its (even) parity bit static void em4x50_reader_send_byte_with_parity(uint8_t byte) { - int parity = 0, bit = 0; + int parity = 0; for (int i = 0; i < 8; i++) { - bit = (byte >> (7 - i)) & 1; + int bit = (byte >> (7 - i)) & 1; em4x50_reader_send_bit(bit); parity ^= bit; } @@ -346,12 +345,12 @@ static void em4x50_reader_send_byte_with_parity(uint8_t byte) { // word hast be sent in msb notation static void em4x50_reader_send_word(const uint32_t word) { uint8_t bytes[4] = {0x0, 0x0, 0x0, 0x0}; - + for (int i = 0; i < 4; i++) { bytes[i] = (word >> (24 - (8 * i))) & 0xFF; em4x50_reader_send_byte_with_parity(bytes[i]); } - + // send column parities em4x50_reader_send_byte(bytes[0] ^ bytes[1] ^ bytes[2] ^ bytes[3]); @@ -362,8 +361,6 @@ static void em4x50_reader_send_word(const uint32_t word) { // find single listen window static bool find_single_listen_window(void) { int cnt_pulses = 0; - - LED_B_ON(); while (cnt_pulses < EM4X50_T_WAITING_FOR_SNGLLIW) { @@ -374,14 +371,12 @@ static bool find_single_listen_window(void) { if (check_pulse_length(get_pulse_length(), 2 * EM4X50_T_TAG_FULL_PERIOD)) { // found listen window - LED_B_OFF(); return true; } } cnt_pulses++; } - LED_B_OFF(); return false; } @@ -392,13 +387,12 @@ static bool find_single_listen_window(void) { // -> 34 words + 34 single listen windows -> about 1600 pulses static int find_double_listen_window(bool bcommand) { int cnt_pulses = 0; - - LED_B_ON(); while (cnt_pulses < EM4X50_T_WAITING_FOR_DBLLIW) { - if (BUTTON_PRESS()) + if (BUTTON_PRESS()) { return PM3_EOPABORTED; + } // identification of listen window is done via evaluation of // pulse lengths @@ -410,8 +404,6 @@ static int find_double_listen_window(bool bcommand) { if (bcommand) { -// SpinDelay(10); - // data transmission from card has to be stopped, because // a commamd shall be issued @@ -420,7 +412,7 @@ static int find_double_listen_window(bool bcommand) { // second window follows - sync on this to issue a command // skip the next bit... - wait_timer(T0 * EM4X50_T_TAG_FULL_PERIOD); + WaitUS(EM4X50_T_TAG_FULL_PERIOD * CYCLES2MUSEC); // ...and check if the following bit does make sense // (if not it is the correct position within the second @@ -431,8 +423,6 @@ static int find_double_listen_window(bool bcommand) { em4x50_reader_send_bit(0); em4x50_reader_send_bit(0); - LED_B_OFF(); - return PM3_SUCCESS; } @@ -440,8 +430,6 @@ static int find_double_listen_window(bool bcommand) { if (check_pulse_length(get_pulse_length(), 3 * EM4X50_T_TAG_FULL_PERIOD)) { - LED_B_OFF(); - // return although second listen window consists of one // more bit period but this period is necessary for // evaluating further pulse lengths @@ -452,7 +440,6 @@ static int find_double_listen_window(bool bcommand) { cnt_pulses++; } - LED_B_OFF(); return PM3_EFAILED; } @@ -475,12 +462,11 @@ static int request_receive_mode(void) { // If is true then within the single listen window right after the // ack signal a RM request has to be sent. static bool check_ack(bool bliw) { - AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (AT91C_BASE_TC0->TC_CV < T0 * 4 * EM4X50_T_TAG_FULL_PERIOD) { - + int count_cycles = 0; + while (count_cycles < EM4X50_T_WAITING_FOR_ACK) { if (BUTTON_PRESS()) return false; - + if (check_pulse_length(get_pulse_length(), 2 * EM4X50_T_TAG_FULL_PERIOD)) { // The received signal is either ACK or NAK. @@ -499,7 +485,7 @@ static bool check_ack(bool bliw) { // wait for 2 bits (remaining "bit" of ACK signal + first // "bit" of listen window) - wait_timer(T0 * 2 * EM4X50_T_TAG_FULL_PERIOD); + WaitUS(2 * EM4X50_T_TAG_FULL_PERIOD * CYCLES2MUSEC); // check for listen window (if first bit cannot be interpreted // as a valid bit it must belong to a listen window) @@ -518,6 +504,7 @@ static bool check_ack(bool bliw) { break; } } + count_cycles++; } return false; @@ -531,9 +518,7 @@ static int get_word_from_bitstream(uint32_t *data) { int cnt = 0; uint32_t pl = 0; uint64_t word = 0x0; - - LED_C_ON(); - + *data = 0x0; // initial bit value depends on last pulse length of listen window @@ -561,7 +546,7 @@ static int get_word_from_bitstream(uint32_t *data) { cnt++; word <<= 1; - + pl = get_pulse_length(); if (check_pulse_length(pl, EM4X50_T_TAG_FULL_PERIOD)) { @@ -604,171 +589,19 @@ static int get_word_from_bitstream(uint32_t *data) { } else if (check_pulse_length(pl, 3 * EM4X50_T_TAG_FULL_PERIOD)) { - LED_C_OFF(); - // pulse length of 3 indicates listen window -> clear last // bit (= 0) and return (without parities) word >>= 2; return (extract_parities(word, data)) ? --cnt : 0; } } - - LED_C_OFF(); - + return PM3_EOPABORTED; } -static bool em4x50_sim_send_bit(uint8_t bit) { - - uint16_t check = 0; - - for (int t = 0; t < EM4X50_T_TAG_FULL_PERIOD; t++) { - - // wait until SSC_CLK goes HIGH - // used as a simple detection of a reader field? - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)) { - WDT_HIT(); - if (check == 1000) { - if (BUTTON_PRESS()) - return false; - check = 0; - } - ++check; - } - - if (bit) - OPEN_COIL(); - else - SHORT_COIL(); - - check = 0; - - //wait until SSC_CLK goes LOW - while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK) { - WDT_HIT(); - if (check == 1000) { - if (BUTTON_PRESS()) - return false; - check = 0; - } - ++check; - } - - if (t == EM4X50_T_TAG_HALF_PERIOD) - bit ^= 1; - - } - - return true; -} - -static bool em4x50_sim_send_byte(uint8_t byte) { - - // send byte - for (int i = 0; i < 8; i++) - if (!em4x50_sim_send_bit((byte >> (7 - i)) & 1)) - return false; - - return true; - -} - -static bool em4x50_sim_send_byte_with_parity(uint8_t byte) { - - uint8_t parity = 0x0; - - // send byte with parity (even) - for (int i = 0; i < 8; i++) - parity ^= (byte >> i) & 1; - - if (em4x50_sim_send_byte(byte) == false) - return false;; - - if (em4x50_sim_send_bit(parity) == false) - return false; - - return true; -} - -bool em4x50_sim_send_word(uint32_t word) { - - uint8_t cparity = 0x00; - - // word has tobe sent in msb, not lsb - word = reflect32(word); - - // 4 bytes each with even row parity bit - for (int i = 0; i < 4; i++) { - if (em4x50_sim_send_byte_with_parity((word >> ((3 - i) * 8)) & 0xFF) == false) { - return false; - } - } - - // column parity - for (int i = 0; i < 8; i++) { - cparity <<= 1; - for (int j = 0; j < 4; j++) { - cparity ^= (((word >> ((3 - j) * 8)) & 0xFF) >> (7 - i)) & 1; - } - } - if (em4x50_sim_send_byte(cparity) == false) - return false; - - // stop bit - if (em4x50_sim_send_bit(0) == false) - return false; - - return true; -} - -bool em4x50_sim_send_listen_window(void) { - - uint16_t check = 0; - - for (int t = 0; t < 5 * EM4X50_T_TAG_FULL_PERIOD; t++) { - - // wait until SSC_CLK goes HIGH - while (!(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)) { - WDT_HIT(); - if (check == 1000) { - if (BUTTON_PRESS()) - return false; - check = 0; - } - ++check; - } - - if (t >= 4 * EM4X50_T_TAG_FULL_PERIOD) - SHORT_COIL(); - else if (t >= 3 * EM4X50_T_TAG_FULL_PERIOD) - OPEN_COIL(); - else if (t >= EM4X50_T_TAG_FULL_PERIOD) - SHORT_COIL(); - else if (t >= EM4X50_T_TAG_HALF_PERIOD) - OPEN_COIL(); - else - SHORT_COIL(); - - check = 0; - - // wait until SSC_CLK goes LOW - while (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK) { - WDT_HIT(); - if (check == 1000) { - if (BUTTON_PRESS()) - return false; - check = 0; - } - ++check; - } - } - - return true; -} - // simple login to EM4x50, // used in operations that require authentication -static bool login(uint32_t password) { +static int login(uint32_t password) { if (request_receive_mode() == PM3_SUCCESS) { // send login command @@ -776,8 +609,8 @@ static bool login(uint32_t password) { // send password em4x50_reader_send_word(password); - - wait_timer(T0 * EM4X50_T_TAG_TPP); + + WaitUS(EM4X50_T_TAG_TPP * CYCLES2MUSEC); // check if ACK is returned if (check_ack(false)) @@ -799,7 +632,7 @@ static bool brute(uint32_t start, uint32_t stop, uint32_t *pwd) { for (*pwd = start; *pwd <= stop; (*pwd)++) { if (login(*pwd) == PM3_SUCCESS) { - + pwd_found = true; // to be safe login 5 more times @@ -809,11 +642,11 @@ static bool brute(uint32_t start, uint32_t stop, uint32_t *pwd) { break; } } - + if (pwd_found) break; } - + // print password every 500 iterations if ((++cnt % 500) == 0) { @@ -827,10 +660,10 @@ static bool brute(uint32_t start, uint32_t stop, uint32_t *pwd) { // print data Dbprintf("|%8i | 0x%08x | 0x%08x |", cnt, reflect32(*pwd), *pwd); } - + if (BUTTON_PRESS()) break; - + } // print footer @@ -844,23 +677,33 @@ static bool brute(uint32_t start, uint32_t stop, uint32_t *pwd) { void em4x50_login(uint32_t *password) { em4x50_setup_read(); - uint8_t status = PM3_EFAILED; - if (get_signalproperties() && find_em4x50_tag()) + int status = PM3_EFAILED; + LED_C_ON(); + if (get_signalproperties() && find_em4x50_tag()) { + LED_C_OFF(); + LED_D_ON(); status = login(*password); + } + LEDsoff(); lf_finalize(); reply_ng(CMD_LF_EM4X50_LOGIN, status, NULL, 0); } -// envoke password search +// envoke password search void em4x50_brute(em4x50_data_t *etd) { em4x50_setup_read(); bool bsuccess = false; uint32_t pwd = 0x0; - if (get_signalproperties() && find_em4x50_tag()) + LED_C_ON(); + if (get_signalproperties() && find_em4x50_tag()) { + LED_C_OFF(); + LED_D_ON(); bsuccess = brute(etd->password1, etd->password2, &pwd); + } + LEDsoff(); lf_finalize(); reply_ng(CMD_LF_EM4X50_BRUTE, bsuccess ? PM3_SUCCESS : PM3_EFAILED, (uint8_t *)(&pwd), sizeof(pwd)); } @@ -888,8 +731,12 @@ void em4x50_chk(uint8_t *filename) { em4x50_setup_read(); // set gHigh and gLow + LED_C_ON(); if (get_signalproperties() && find_em4x50_tag()) { + LED_C_OFF(); + LED_D_ON(); + // try to login with current password for (int i = 0; i < pwd_count; i++) { @@ -903,9 +750,12 @@ void em4x50_chk(uint8_t *filename) { pwd = 0x0; for (int j = 0; j < 4; j++) pwd |= (*(pwds + 4 * i + j)) << ((3 - j) * 8); - - if ((status = login(pwd)) == PM3_SUCCESS) + + if ((status = login(pwd)) == PM3_SUCCESS) { + SpinUp(50); + SpinDown(50); break; + } } } @@ -913,6 +763,7 @@ void em4x50_chk(uint8_t *filename) { #endif + LEDsoff(); lf_finalize(); reply_ng(CMD_LF_EM4X50_CHK, status, (uint8_t *)&pwd, sizeof(pwd)); } @@ -937,7 +788,7 @@ static int reset(void) { // reads data that tag transmits when exposed to reader field // (standard read mode); number of read words is saved in -static int standard_read(int *now, uint32_t *words) { +int standard_read(int *now, uint32_t *words) { int fwr = *now, res = PM3_EFAILED; @@ -945,8 +796,11 @@ static int standard_read(int *now, uint32_t *words) { if ((res = find_double_listen_window(false)) == PM3_SUCCESS) { // read and save words until following double listen window is detected - while ((res = get_word_from_bitstream(&words[*now])) == EM4X50_TAG_WORD) + res = get_word_from_bitstream(&words[*now]); + while (res == EM4X50_TAG_WORD) { (*now)++; + res = get_word_from_bitstream(&words[*now]); + } // number of detected words *now -= fwr; @@ -994,15 +848,20 @@ static int selective_read(uint32_t addresses, uint32_t *words) { // reads by using "selective read mode" -> bidirectional communication void em4x50_read(em4x50_data_t *etd) { - bool blogin = true; int status = PM3_EFAILED; uint32_t words[EM4X50_NO_WORDS] = {0x0}; em4x50_setup_read(); // set gHigh and gLow + LED_C_ON(); if (get_signalproperties() && find_em4x50_tag()) { + LED_C_OFF(); + LED_D_ON(); + + bool blogin = true; + // try to login with given password if (etd->pwd_given) blogin = (login(etd->password1) == PM3_SUCCESS); @@ -1012,37 +871,38 @@ void em4x50_read(em4x50_data_t *etd) { status = selective_read(etd->addresses, words); } + LEDsoff(); LOW(GPIO_SSC_DOUT); lf_finalize(); - - // iceman: this hardcoded 136 value.... - reply_ng(CMD_LF_EM4X50_READ, status, (uint8_t *)words, 136); + reply_ng(CMD_LF_EM4X50_READ, status, (uint8_t *)words, EM4X50_TAG_MAX_NO_BYTES); } // collects as much information as possible via selective read mode void em4x50_info(em4x50_data_t *etd) { - - bool blogin = true; int status = PM3_EFAILED; - uint32_t addresses = 0x00002100; // read from fwr = 0 to lwr = 33 (0x21) uint32_t words[EM4X50_NO_WORDS] = {0x0}; em4x50_setup_read(); + LED_C_ON(); if (get_signalproperties() && find_em4x50_tag()) { + LED_C_OFF(); + LED_D_ON(); + bool blogin = true; // login with given password if (etd->pwd_given) blogin = (login(etd->password1) == PM3_SUCCESS); - if (blogin) - status = selective_read(addresses, words); + if (blogin) { + // read addresses from fwr = 0 to lwr = 33 (0x21) + status = selective_read(0x00002100, words); + } } + LEDsoff(); lf_finalize(); - - // iceman: this hardcoded 136 value.... - reply_ng(CMD_LF_EM4X50_INFO, status, (uint8_t *)words, 136); + reply_ng(CMD_LF_EM4X50_INFO, status, (uint8_t *)words, EM4X50_TAG_MAX_NO_BYTES); } // reads data that tag transmits "voluntarily" -> standard read mode @@ -1053,9 +913,14 @@ void em4x50_reader(void) { em4x50_setup_read(); - if (get_signalproperties() && find_em4x50_tag()) + LED_C_ON(); + if (get_signalproperties() && find_em4x50_tag()) { + LED_C_OFF(); + LED_D_ON(); standard_read(&now, words); + } + LEDsoff(); LOW(GPIO_SSC_DOUT); lf_finalize(); reply_ng(CMD_LF_EM4X50_READER, now, (uint8_t *)words, 4 * now); @@ -1063,7 +928,7 @@ void em4x50_reader(void) { // writes to specified static int write(uint32_t word, uint32_t addresses) { - + if (request_receive_mode() == PM3_SUCCESS) { // send write command @@ -1079,15 +944,19 @@ static int write(uint32_t word, uint32_t addresses) { reply_ng(CMD_LF_EM4X50_WRITE, PM3_ETEAROFF, NULL, 0); return PM3_ETEAROFF; } else { - + // wait for T0 * EM4X50_T_TAG_TWA (write access time) - wait_timer(T0 * EM4X50_T_TAG_TWA); + WaitUS(EM4X50_T_TAG_TWA * CYCLES2MUSEC); // look for ACK sequence if (check_ack(false)) { - // now EM4x50 needs T0 * EM4X50_T_TAG_TWEE (EEPROM write time) + // now EM4x50 needs T0 * EM4X50_T_TAG_TWEE (EEPROM write time = 3.2ms = 50 * 64 periods) // for saving data and should return with ACK + for (int i = 0; i < 50; i++) { + WaitUS(EM4X50_T_TAG_FULL_PERIOD * CYCLES2MUSEC); + } + if (check_ack(false)) return PM3_SUCCESS; } @@ -1116,7 +985,7 @@ static int write_password(uint32_t password, uint32_t new_password) { } else { // wait for T0 * EM4x50_T_TAG_TPP (processing pause time) - wait_timer(T0 * EM4X50_T_TAG_TPP); + WaitUS(EM4X50_T_TAG_TPP * CYCLES2MUSEC); // look for ACK sequence and send rm request // during following listen window @@ -1126,11 +995,19 @@ static int write_password(uint32_t password, uint32_t new_password) { em4x50_reader_send_word(new_password); // wait for T0 * EM4X50_T_TAG_TWA (write access time) - wait_timer(T0 * EM4X50_T_TAG_TWA); + WaitUS(EM4X50_T_TAG_TWA * CYCLES2MUSEC); + + if (check_ack(false)) { + + // now EM4x50 needs T0 * EM4X50_T_TAG_TWEE (EEPROM write time = 3.2ms = 50 * 64 periods) + // for saving data and should return with ACK + for (int i = 0; i < 50; i++) { + WaitUS(EM4X50_T_TAG_FULL_PERIOD * CYCLES2MUSEC); + } - if (check_ack(false)) if (check_ack(false)) return PM3_SUCCESS; + } } } } else { @@ -1150,8 +1027,12 @@ void em4x50_write(em4x50_data_t *etd) { em4x50_setup_read(); + LED_C_ON(); if (get_signalproperties() && find_em4x50_tag()) { + LED_C_OFF(); + LED_D_ON(); + // if password is given try to login first status = PM3_SUCCESS; if (etd->pwd_given) @@ -1175,7 +1056,7 @@ void em4x50_write(em4x50_data_t *etd) { // if password is given renew login after reset if (etd->pwd_given) status = login(etd->password1); - + if (status == PM3_SUCCESS) { // call a selective read @@ -1192,8 +1073,9 @@ void em4x50_write(em4x50_data_t *etd) { } } + LEDsoff(); lf_finalize(); - reply_ng(CMD_LF_EM4X50_WRITE, status, (uint8_t *)words, 136); + reply_ng(CMD_LF_EM4X50_WRITE, status, (uint8_t *)words, EM4X50_TAG_MAX_NO_BYTES); } // simple change of password @@ -1202,8 +1084,12 @@ void em4x50_writepwd(em4x50_data_t *etd) { em4x50_setup_read(); + LED_C_ON(); if (get_signalproperties() && find_em4x50_tag()) { + LED_C_OFF(); + LED_D_ON(); + // login and change password if (login(etd->password1) == PM3_SUCCESS) { @@ -1215,70 +1101,722 @@ void em4x50_writepwd(em4x50_data_t *etd) { } } + LEDsoff(); lf_finalize(); reply_ng(CMD_LF_EM4X50_WRITEPWD, status, NULL, 0); } -// simulate uploaded data in emulator memory -// (currently simulation allows only a one-way communication) -void em4x50_sim(uint8_t *filename) { - int status = PM3_SUCCESS; - uint8_t *em4x50_mem = BigBuf_get_EM_addr(); - uint32_t words[EM4X50_NO_WORDS] = {0x0}; - -#ifdef WITH_FLASH +// send bit in receive mode by counting carrier cycles +static void em4x50_sim_send_bit(uint8_t bit) { - if (strlen((char *)filename) != 0) { - - BigBuf_free(); + uint16_t timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; - int changed = rdv40_spiffs_lazy_mount(); - uint32_t size = size_in_spiffs((char *)filename); - em4x50_mem = BigBuf_malloc(size); + for (int t = 0; t < EM4X50_T_TAG_FULL_PERIOD; t++) { - rdv40_spiffs_read_as_filetype((char *)filename, em4x50_mem, size, RDV40_SPIFFS_SAFETY_SAFE); + // wait until SSC_CLK goes HIGH + // used as a simple detection of a reader field? + while ((timeout--) && !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); - if (changed) - rdv40_spiffs_lazy_unmount(); + if (timeout == 0) { + return; + } + timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; + + if (bit) + OPEN_COIL(); + else + SHORT_COIL(); + + //wait until SSC_CLK goes LOW + while ((timeout--) && (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); + if (timeout == 0) { + return; + } + timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; + + if (t == EM4X50_T_TAG_HALF_PERIOD) + bit ^= 1; + + } +} + +// send byte in receive mode either with or without parity check (even) +static void em4x50_sim_send_byte(uint8_t byte, bool paritycheck) { + + // send byte + for (int i = 0; i < 8; i++) { + em4x50_sim_send_bit((byte >> (7 - i)) & 1); } -#endif + if (paritycheck) { - for (int i = 0; i < EM4X50_NO_WORDS; i++) - words[i] = reflect32(bytes_to_num(em4x50_mem + (i * 4), 4)); - - // only if valid em4x50 data (e.g. uid == serial) - if (words[EM4X50_DEVICE_SERIAL] != words[EM4X50_DEVICE_ID]) { + uint8_t parity = 0x0; - // extract control data - int fwr = words[CONFIG_BLOCK] & 0xFF; // first word read - int lwr = (words[CONFIG_BLOCK] >> 8) & 0xFF; // last word read - // extract protection data - int fwrp = words[EM4X50_PROTECTION] & 0xFF; // first word read protected - int lwrp = (words[EM4X50_PROTECTION] >> 8) & 0xFF; // last word read protected + for (int i = 0; i < 8; i++) { + parity ^= (byte >> i) & 1; + } - em4x50_setup_sim(); + em4x50_sim_send_bit(parity); + } +} - // iceman, will need a usb cmd check to break as well - while (BUTTON_PRESS() == false) { - - WDT_HIT(); - em4x50_sim_send_listen_window(); - for (int i = fwr; i <= lwr; i++) { +// send complete word in receive mode (including all parity checks) +static void em4x50_sim_send_word(uint32_t word) { - em4x50_sim_send_listen_window(); + uint8_t cparity = 0x00; - if ((i >= fwrp) && (i <= lwrp)) - em4x50_sim_send_word(0x00); - else - em4x50_sim_send_word(words[i]); + // word has tobe sent in msb, not lsb + word = reflect32(word); + + // 4 bytes each with even row parity bit + for (int i = 0; i < 4; i++) { + em4x50_sim_send_byte((word >> ((3 - i) * 8)) & 0xFF, true); + } + + // column parity + for (int i = 0; i < 8; i++) { + cparity <<= 1; + for (int j = 0; j < 4; j++) { + cparity ^= (((word >> ((3 - j) * 8)) & 0xFF) >> (7 - i)) & 1; + } + } + em4x50_sim_send_byte(cparity, false); + + // stop bit + em4x50_sim_send_bit(0); +} + +// wait for pulses of carrier frequency +static void wait_cycles(int maxperiods) { + + int period = 0, timeout = EM4X50_T_SIMULATION_TIMEOUT_WAIT; + + while (period < maxperiods) { + + while ((timeout--) && !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); + if (timeout <= 0) { + return; + } + timeout = EM4X50_T_SIMULATION_TIMEOUT_WAIT; + + while ((timeout--) && (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); + if (timeout <= 0) { + return; + } + timeout = EM4X50_T_SIMULATION_TIMEOUT_WAIT; + + period++; + } +} + +// read single bit in simulation mode +static int em4x50_sim_read_bit(void) { + + int cycles = 0; + int timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; + + while (cycles < EM4X50_T_TAG_FULL_PERIOD) { + + // wait until reader field disappears + while ((timeout--) && !(AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); + if (timeout <= 0) { + return PM3_ETIMEOUT; + } + timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; + + // now check until reader switches on carrier field + uint32_t tval = GetTicks(); + while ((timeout--) && (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)) { + + if (timeout <= 0) { + return PM3_ETIMEOUT; + } + + // check if current cycle takes longer than "usual"" + if (GetTicks() - tval > EM4X50_T_ZERO_DETECTION * CYCLES2TICKS) { + + // gap detected; wait until reader field is switched on again + while ((timeout--) && (AT91C_BASE_PIOA->PIO_PDSR & GPIO_SSC_CLK)); + + if (timeout <= 0) { + return PM3_ETIMEOUT; + } + + // now we have a reference "position", from here it will take + // slightly less than 32 cycles until the end of the bit period + wait_cycles(28); + + // end of bit period is reached; return with bit value "0" + // (cf. datasheet) + return 0; } } - } else { - status = PM3_ENODATA; + timeout = EM4X50_T_SIMULATION_TIMEOUT_READ; + + // no gap detected, i.e. reader field is still up; + // continue with counting cycles + cycles++; } - + + // reached 64 cycles (= EM4X50_T_TAG_FULL_PERIOD) -> return bit value "1" + return 1; +} + +// read byte in simulation mode either with or without parity check (even) +static bool em4x50_sim_read_byte(uint8_t *byte, bool paritycheck) { + + for (int i = 0; i < 8; i++) { + *byte <<= 1; + *byte |= em4x50_sim_read_bit(); + } + + if (paritycheck) { + + int pval = em4x50_sim_read_bit(); + uint8_t parity = 0; + + for (int i = 0; i < 8; i++) { + parity ^= ((*byte) >> i) & 1; + } + + if (parity != pval) { + return false; + } + } + + return true; +} + +// read complete word in simulation mode +static bool em4x50_sim_read_word(uint32_t *word) { + + uint8_t stop_bit = 0; + uint8_t parities = 0, parities_calculated = 0; + uint8_t bytes[4] = {0}; + + // read plain data + for (int i = 0; i < 4; i++) { + em4x50_sim_read_byte(&bytes[i], true); + } + + // read column parities and stop bit + em4x50_sim_read_byte(&parities, false); + stop_bit = em4x50_sim_read_bit(); + + // calculate column parities from data + for (int i = 0; i < 8; i++) { + parities_calculated <<= 1; + for (int j = 0; j < 4; j++) { + parities_calculated ^= (bytes[j] >> (7 - i)) & 1; + } + } + + *word = BYTES2UINT32(bytes); + + // check parities + if ((parities == parities_calculated) && (stop_bit == 0)) { + return true; + } + + return false; +} + +// check if reader requests receive mode (rm) by sending two zeros +static int check_rm_request(uint32_t *tag) { + + // look for first zero + int bit = em4x50_sim_read_bit(); + if (bit == 0) { + + // look for second zero + bit = em4x50_sim_read_bit(); + if (bit == 0) { + + LED_C_ON(); + + // if command before was EM4X50_COMMAND_WRITE_PASSWORD + // switch to separate process + if (gWritePasswordProcess) { + return EM4X50_COMMAND_WRITE_PASSWORD; + } else { + // read mode request detected, get command from reader + uint8_t command = 0; + em4x50_sim_read_byte(&command, true); + return command; + } + } + } + + return (bit != PM3_ETIMEOUT) ? PM3_SUCCESS : PM3_ETIMEOUT; +} + +// send single listen window in simulation mode +static int em4x50_sim_send_listen_window(uint32_t *tag) { + + SHORT_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + OPEN_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + SHORT_COIL(); + wait_cycles(2 * EM4X50_T_TAG_FULL_PERIOD); + + OPEN_COIL(); + int command = check_rm_request(tag); + if (command != PM3_SUCCESS) { + return command; + } + + SHORT_COIL(); + wait_cycles(EM4X50_T_TAG_FULL_PERIOD); + + return PM3_SUCCESS; +} + +// send ack +static void em4x50_sim_send_ack(void) { + + SHORT_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + OPEN_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + SHORT_COIL(); + wait_cycles(3 * EM4X50_T_TAG_HALF_PERIOD); + + OPEN_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + SHORT_COIL(); + wait_cycles(3 * EM4X50_T_TAG_HALF_PERIOD); + + OPEN_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + SHORT_COIL(); +} + +// send nak +static void em4x50_sim_send_nak(void) { + + SHORT_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + OPEN_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + SHORT_COIL(); + wait_cycles(3 * EM4X50_T_TAG_HALF_PERIOD); + + OPEN_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + SHORT_COIL(); + wait_cycles(EM4X50_T_TAG_FULL_PERIOD); + + OPEN_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); + + SHORT_COIL(); + wait_cycles(EM4X50_T_TAG_HALF_PERIOD); +} + +// standard read mode process (simulation mode) +static int em4x50_sim_handle_standard_read_command(uint32_t *tag) { + + // extract control data + int fwr = reflect32(tag[EM4X50_CONTROL]) & 0xFF; // first word read + int lwr = (reflect32(tag[EM4X50_CONTROL]) >> 8) & 0xFF; // last word read + // extract protection data: + // first word read protected + int fwrp = reflect32(tag[EM4X50_PROTECTION]) & 0xFF; + // last word read protected + int lwrp = (reflect32(tag[EM4X50_PROTECTION]) >> 8) & 0xFF; + + while ((BUTTON_PRESS() == false) && (data_available() == false)) { + + WDT_HIT(); + + int res = em4x50_sim_send_listen_window(tag); + + if (res != PM3_SUCCESS) { + return res; + } + + for (int i = fwr; i <= lwr; i++) { + + res = em4x50_sim_send_listen_window(tag); + if (res != PM3_SUCCESS) { + return res; + } + + if ((gLogin == false) && (i >= fwrp) && (i <= lwrp)) { + em4x50_sim_send_word(0x00); + } else { + em4x50_sim_send_word(reflect32(tag[i])); + } + } + } + + return PM3_EOPABORTED; +} + +// selective read mode process (simulation mode) +static int em4x50_sim_handle_selective_read_command(uint32_t *tag) { + + // read password + uint32_t address = 0; + bool addr = em4x50_sim_read_word(&address); + + // processing pause time (corresponds to a "1" bit) + em4x50_sim_send_bit(1); + + if (addr) { + em4x50_sim_send_ack(); + } else { + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + } + + // extract control data + int fwr = address & 0xFF; // first word read + int lwr = (address >> 8) & 0xFF; // last word read + + // extract protection data: + // first word read protected + int fwrp = reflect32(tag[EM4X50_PROTECTION]) & 0xFF; + // last word read protected + int lwrp = (reflect32(tag[EM4X50_PROTECTION]) >> 8) & 0xFF; + + while ((BUTTON_PRESS() == false) && (data_available() == false)) { + + WDT_HIT(); + + int command = em4x50_sim_send_listen_window(tag); + if (command != PM3_SUCCESS) { + return command; + } + + for (int i = fwr; i <= lwr; i++) { + + command = em4x50_sim_send_listen_window(tag); + if (command != PM3_SUCCESS) { + return command; + } + + // if not authenticated do not send read protected words + if ((gLogin == false) && (i >= fwrp) && (i <= lwrp)) { + em4x50_sim_send_word(0x00); + } else { + em4x50_sim_send_word(reflect32(tag[i])); + } + } + } + + return PM3_EOPABORTED; +} + +// login process (simulation mode) +static int em4x50_sim_handle_login_command(uint32_t *tag) { + + // read password + uint32_t password = 0; + bool pwd = em4x50_sim_read_word(&password); + + // processing pause time (corresponds to a "1" bit) + em4x50_sim_send_bit(1); + + if (pwd && (password == reflect32(tag[EM4X50_DEVICE_PASSWORD]))) { + em4x50_sim_send_ack(); + gLogin = true; + LED_D_ON(); + } else { + em4x50_sim_send_nak(); + gLogin = false; + LED_D_OFF(); + + // save transmitted password (to be used in standalone mode) + gPassword = password; + } + // continue with standard read mode + return EM4X50_COMMAND_STANDARD_READ; +} + +// reset process (simulation mode) +static int em4x50_sim_handle_reset_command(uint32_t *tag) { + + // processing pause time (corresponds to a "1" bit) + em4x50_sim_send_bit(1); + + // send ACK + em4x50_sim_send_ack(); + gLogin = false; + LED_D_OFF(); + + // wait for initialization (tinit) + wait_cycles(EM4X50_T_TAG_TINIT); + + // continue with standard read mode + return EM4X50_COMMAND_STANDARD_READ; +} + +// write process (simulation mode) +static int em4x50_sim_handle_write_command(uint32_t *tag) { + + // read address + uint8_t address = 0; + bool addr = em4x50_sim_read_byte(&address, true); + // read data + uint32_t data = 0; + bool word = em4x50_sim_read_word(&data); + + // write access time + wait_cycles(EM4X50_T_TAG_TWA); + + if ((addr == false) || (word == false)) { + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + } + + // extract necessary control data + bool raw = (tag[EM4X50_CONTROL] >> CONFIG_BLOCK) & READ_AFTER_WRITE; + // extract protection data: + // first word write protected + int fwwp = reflect8((tag[EM4X50_PROTECTION] >> 24) & 0xFF); + // last word write protected + int lwwp = reflect8((tag[EM4X50_PROTECTION] >> 16) & 0xFF); + + switch (address) { + + case EM4X50_DEVICE_PASSWORD: + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + break; + + case EM4X50_PROTECTION: + if (gLogin) { + tag[address] = reflect32(data); + em4x50_sim_send_ack(); + } else { + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + } + break; + + case EM4X50_CONTROL: + if (gLogin) { + tag[address] = reflect32(data); + em4x50_sim_send_ack(); + } else { + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + } + break; + + case EM4X50_DEVICE_SERIAL: + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + break; + + case EM4X50_DEVICE_ID: + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + break; + + default: + if ((address >= fwwp) && (address <= lwwp)) { + if (gLogin) { + tag[address] = reflect32(data); + em4x50_sim_send_ack(); + } else { + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + } + } else { + tag[address] = reflect32(data); + em4x50_sim_send_ack(); + } + break; + } + + // EEPROM write time + // strange: need some sort of 'waveform correction', otherwise ack signal + // will not be detected; sending a single "1" as last "bit" of Twee + // seems to solve the problem + wait_cycles(EM4X50_T_TAG_TWEE - EM4X50_T_TAG_FULL_PERIOD); + em4x50_sim_send_bit(1); + em4x50_sim_send_ack(); + + // if "read after write" (raw) bit is set, send written data once + if (raw) { + int command = em4x50_sim_send_listen_window(tag); + if (command != PM3_SUCCESS) { + return command; + } + + command = em4x50_sim_send_listen_window(tag); + if (command != PM3_SUCCESS) { + return command; + } + + em4x50_sim_send_word(tag[address]); + } + + // continue with standard read mode + return EM4X50_COMMAND_STANDARD_READ; +} + +// write password process (simulation mode) +static int em4x50_sim_handle_writepwd_command(uint32_t *tag) { + + bool pwd = false; + + gWritePasswordProcess = true; + + // read password + uint32_t act_password = 0; + pwd = em4x50_sim_read_word(&act_password); + + // processing pause time tpp (corresponds to a "1" bit) + em4x50_sim_send_bit(1); + + if (pwd && (act_password == reflect32(tag[EM4X50_DEVICE_PASSWORD]))) { + em4x50_sim_send_ack(); + gLogin = true; + } else { + em4x50_sim_send_nak(); + gLogin = false; + gWritePasswordProcess = false; + + // save transmitted password (to be used in standalone mode) + gPassword = act_password; + + return EM4X50_COMMAND_STANDARD_READ; + } + + int command = em4x50_sim_send_listen_window(tag); + gWritePasswordProcess = false; + if (command != EM4X50_COMMAND_WRITE_PASSWORD) { + return command; + } + + // read new password + uint32_t new_password = 0; + pwd = em4x50_sim_read_word(&new_password); + + // write access time twa + wait_cycles(EM4X50_T_TAG_TWA); + + if (pwd) { + em4x50_sim_send_ack(); + tag[EM4X50_DEVICE_PASSWORD] = reflect32(new_password); + gPassword = new_password; + } else { + em4x50_sim_send_nak(); + return EM4X50_COMMAND_STANDARD_READ; + } + + // EEPROM write time + // strange: need some sort of 'waveform correction', otherwise ack signal + // will not be detected; sending a single "1" as last part of Twee + // seems to solve the problem + wait_cycles(EM4X50_T_TAG_TWEE - EM4X50_T_TAG_FULL_PERIOD); + em4x50_sim_send_bit(1); + em4x50_sim_send_ack(); + + // continue with standard read mode + return EM4X50_COMMAND_STANDARD_READ; +} + +void em4x50_handle_commands(int *command, uint32_t *tag) { + + switch (*command) { + + case EM4X50_COMMAND_LOGIN: + *command = em4x50_sim_handle_login_command(tag); + break; + + case EM4X50_COMMAND_RESET: + *command = em4x50_sim_handle_reset_command(tag); + break; + + case EM4X50_COMMAND_WRITE: + *command = em4x50_sim_handle_write_command(tag); + break; + + case EM4X50_COMMAND_WRITE_PASSWORD: + *command = em4x50_sim_handle_writepwd_command(tag); + break; + + case EM4X50_COMMAND_SELECTIVE_READ: + *command = em4x50_sim_handle_selective_read_command(tag); + break; + + case EM4X50_COMMAND_STANDARD_READ: + LED_C_OFF(); + *command = em4x50_sim_handle_standard_read_command(tag); + break; + + // bit errors during reading may lead to unknown commands + // -> continue with standard read mode + default: + *command = EM4X50_COMMAND_STANDARD_READ; + break; + } +} + +// simulate uploaded data in emulator memory +// LED C -> reader command has been detected +// LED D -> operations that require authentication are possible +void em4x50_sim(uint32_t *password) { + + int command = PM3_ENODATA; + + uint8_t *em4x50_mem = BigBuf_get_EM_addr(); + uint32_t tag[EM4X50_NO_WORDS] = {0x0}; + + for (int i = 0; i < EM4X50_NO_WORDS; i++) + tag[i] = bytes_to_num(em4x50_mem + (i * 4), 4); + + // via eload uploaded dump usually does not contain a password + if (tag[EM4X50_DEVICE_PASSWORD] == 0) { + tag[EM4X50_DEVICE_PASSWORD] = reflect32(*password); + } + + // only if valid em4x50 data (e.g. uid == serial) + if (tag[EM4X50_DEVICE_SERIAL] != tag[EM4X50_DEVICE_ID]) { + + // init + LEDsoff(); + em4x50_setup_sim(); + gLogin = false; + gWritePasswordProcess = false; + + // start with inital command = standard read mode + command = EM4X50_COMMAND_STANDARD_READ; + + for (;;) { + + em4x50_handle_commands(&command, tag); + + // stop if key (pm3 button or enter key) has been pressed + if (command == PM3_EOPABORTED) { + break; + } + + // if timeout (e.g. no reader field) continue with standard read + // mode and reset former authentication + if (command == PM3_ETIMEOUT) { + command = EM4X50_COMMAND_STANDARD_READ; + gLogin = false; + LED_D_OFF(); + } + } + } + BigBuf_free(); lf_finalize(); - reply_ng(CMD_LF_EM4X50_SIM, status, NULL, 0); + reply_ng(CMD_LF_EM4X50_SIM, command, NULL, 0); } diff --git a/armsrc/em4x50.h b/armsrc/em4x50.h index 12f0a64d8..d47579dcc 100644 --- a/armsrc/em4x50.h +++ b/armsrc/em4x50.h @@ -13,10 +13,11 @@ #include "../include/em4x50.h" -int em4x50_standalone_read(uint32_t *words); -int em4x50_standalone_brute(uint32_t start, uint32_t stop, uint32_t *pwd); -bool em4x50_sim_send_listen_window(void); -bool em4x50_sim_send_word(uint32_t word); +void em4x50_setup_read(void); +int standard_read(int *now, uint32_t *words); + +void em4x50_setup_sim(void); +void em4x50_handle_commands(int *command, uint32_t *tag); void em4x50_info(em4x50_data_t *etd); void em4x50_write(em4x50_data_t *etd); @@ -24,7 +25,7 @@ void em4x50_writepwd(em4x50_data_t *etd); void em4x50_read(em4x50_data_t *etd); void em4x50_brute(em4x50_data_t *etd); void em4x50_login(uint32_t *password); -void em4x50_sim(uint8_t *filename); +void em4x50_sim(uint32_t *password); void em4x50_reader(void); void em4x50_chk(uint8_t *filename); diff --git a/armsrc/em4x70.c b/armsrc/em4x70.c index 1feb448c7..0ea6392d1 100644 --- a/armsrc/em4x70.c +++ b/armsrc/em4x70.c @@ -21,28 +21,35 @@ static em4x70_tag_t tag = { 0 }; // EM4170 requires a parity bit on commands, other variants do not. static bool command_parity = true; -#define EM4X70_T_TAG_QUARTER_PERIOD 8 -#define EM4X70_T_TAG_HALF_PERIOD 16 -#define EM4X70_T_TAG_THREE_QUARTER_PERIOD 24 -#define EM4X70_T_TAG_FULL_PERIOD 32 -#define EM4X70_T_TAG_TWA 128 // Write Access Time -#define EM4X70_T_TAG_DIV 224 // Divergency Time -#define EM4X70_T_TAG_AUTH 4224 // Authentication Time -#define EM4X70_T_TAG_WEE 3072 // EEPROM write Time -#define EM4X70_T_TAG_TWALB 128 // Write Access Time of Lock Bits +// Conversion from Ticks to RF periods +// 1 us = 1.5 ticks +// 1RF Period = 8us = 12 Ticks +#define TICKS_PER_FC 12 -#define EM4X70_T_WAITING_FOR_SNGLLIW 160 // Unsure +// Chip timing from datasheet +// Converted into Ticks for timing functions +#define EM4X70_T_TAG_QUARTER_PERIOD (8 * TICKS_PER_FC) +#define EM4X70_T_TAG_HALF_PERIOD (16 * TICKS_PER_FC) +#define EM4X70_T_TAG_THREE_QUARTER_PERIOD (24 * TICKS_PER_FC) +#define EM4X70_T_TAG_FULL_PERIOD (32 * TICKS_PER_FC) // 1 Bit Period +#define EM4X70_T_TAG_TWA (128 * TICKS_PER_FC) // Write Access Time +#define EM4X70_T_TAG_DIV (224 * TICKS_PER_FC) // Divergency Time +#define EM4X70_T_TAG_AUTH (4224 * TICKS_PER_FC) // Authentication Time +#define EM4X70_T_TAG_WEE (3072 * TICKS_PER_FC) // EEPROM write Time +#define EM4X70_T_TAG_TWALB (672 * TICKS_PER_FC) // Write Access Time of Lock Bits +#define EM4X70_T_TAG_BITMOD (4 * TICKS_PER_FC) // Initial time to stop modulation when sending 0 +#define EM4X70_T_TAG_TOLERANCE (8 * TICKS_PER_FC) // Tolerance in RF periods for receive/LIW -#define TICKS_PER_FC 12 // 1 fc = 8us, 1.5us per tick = 12 ticks -#define EM4X70_MIN_AMPLITUDE 10 // Minimum difference between a high and low signal - -#define EM4X70_TAG_TOLERANCE 10 -#define EM4X70_TAG_WORD 48 +#define EM4X70_T_TAG_TIMEOUT (4 * EM4X70_T_TAG_FULL_PERIOD) // Timeout if we ever get a pulse longer than this +#define EM4X70_T_WAITING_FOR_LIW 50 // Pulses to wait for listen window +#define EM4X70_T_READ_HEADER_LEN 16 // Read header length (16 bit periods) +#define EM4X70_COMMAND_RETRIES 5 // Attempts to send/read command +#define EM4X70_MAX_RECEIVE_LENGTH 96 // Maximum bits to expect from any command /** * These IDs are from the EM4170 datasheet - * Some versions of the chip require a fourth + * Some versions of the chip require a * (even) parity bit, others do not */ #define EM4X70_COMMAND_ID 0x01 @@ -52,24 +59,28 @@ static bool command_parity = true; #define EM4X70_COMMAND_WRITE 0x05 #define EM4X70_COMMAND_UM2 0x07 -static uint8_t gHigh = 0; -static uint8_t gLow = 0; +// Constants used to determing high/low state of signal +#define EM4X70_NOISE_THRESHOLD 13 // May depend on noise in environment +#define HIGH_SIGNAL_THRESHOLD (127 + EM4X70_NOISE_THRESHOLD) +#define LOW_SIGNAL_THRESHOLD (127 - EM4X70_NOISE_THRESHOLD) -#define IS_HIGH(sample) (sample>gLow ? true : false) -#define IS_LOW(sample) (sample LOW_SIGNAL_THRESHOLD ? true : false) +#define IS_LOW(sample) (sample < HIGH_SIGNAL_THRESHOLD ? true : false) + +// Timing related macros #define IS_TIMEOUT(timeout_ticks) (GetTicks() > timeout_ticks) +#define TICKS_ELAPSED(start_ticks) (GetTicks() - start_ticks) - -static uint8_t bits2byte(uint8_t *bits, int length); -static void bits2bytes(uint8_t *bits, int length, uint8_t *out); -static int em4x70_receive(uint8_t *bits); +static uint8_t bits2byte(const uint8_t *bits, int length); +static void bits2bytes(const uint8_t *bits, int length, uint8_t *out); +static int em4x70_receive(uint8_t *bits, size_t length); static bool find_listen_window(bool command); static void init_tag(void) { - memset(tag.data, 0x00, sizeof(tag.data)/sizeof(tag.data[0])); + memset(tag.data, 0x00, sizeof(tag.data) / sizeof(tag.data[0])); } -static void EM4170_setup_read(void) { +static void em4x70_setup_read(void) { FpgaDownloadAndGo(FPGA_BITSTREAM_LF); FpgaWriteConfWord(FPGA_MAJOR_MODE_LF_ADC | FPGA_LF_ADC_READER_FIELD); @@ -102,208 +113,312 @@ static void EM4170_setup_read(void) { static bool get_signalproperties(void) { - // calculate signal properties (mean amplitudes) from measured data: - // 32 amplitudes (maximum values) -> mean amplitude value -> gHigh -> gLow - bool signal_found = false; - int no_periods = 32, pct = 50, noise = 140; // pct originally 75, found 50 was working better for me - uint8_t sample_ref = 127; - uint8_t sample_max_mean = 0; - uint8_t sample_max[no_periods]; - uint32_t sample_max_sum = 0; - - memset(sample_max, 0x00, sizeof(sample_max)); + // Simple check to ensure we see a signal above the noise threshold + uint32_t no_periods = 32; // wait until signal/noise > 1 (max. 32 periods) - for (int i = 0; i < TICKS_PER_FC * EM4X70_T_TAG_FULL_PERIOD * no_periods; i++) { + for (int i = 0; i < EM4X70_T_TAG_FULL_PERIOD * no_periods; i++) { // about 2 samples per bit period - WaitTicks(TICKS_PER_FC * EM4X70_T_TAG_HALF_PERIOD); + WaitTicks(EM4X70_T_TAG_HALF_PERIOD); - if (AT91C_BASE_SSC->SSC_RHR > noise) { - signal_found = true; - break; + if (AT91C_BASE_SSC->SSC_RHR > HIGH_SIGNAL_THRESHOLD) { + return true; } - } - - if (signal_found == false) - return false; - - // calculate mean maximum value of 32 periods, each period has a length of - // 3 single "full periods" to eliminate the influence of a listen window - for (int i = 0; i < no_periods; i++) { - - uint32_t start_ticks = GetTicks(); - //AT91C_BASE_TC0->TC_CCR = AT91C_TC_SWTRG; - while (GetTicks() - start_ticks < TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD) { - - volatile uint8_t sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - - if (sample > sample_max[i]) - sample_max[i] = sample; - - } - - sample_max_sum += sample_max[i]; - } - - sample_max_mean = sample_max_sum / no_periods; - - // set global envelope variables - gHigh = sample_ref + pct * (sample_max_mean - sample_ref) / 100; - gLow = sample_ref - pct * (sample_max_mean - sample_ref) / 100; - - // Basic sanity check - if(gHigh - gLow < EM4X70_MIN_AMPLITUDE) { - return false; - } - - Dbprintf("%s: gHigh %d gLow: %d", __func__, gHigh, gLow); - return true; + return false; } /** - * get_pulse_length - * - * Times falling edge pulses - */ -static uint32_t get_pulse_length(void) { + * get_falling_pulse_length + * + * Returns time between falling edge pulse in ticks + */ +static uint32_t get_falling_pulse_length(void) { - uint8_t sample; - uint32_t timeout = GetTicks() + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD); + uint32_t timeout = GetTicks() + EM4X70_T_TAG_TIMEOUT; - do { - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - }while (IS_HIGH(sample) && !IS_TIMEOUT(timeout)); + while (IS_HIGH(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout)); if (IS_TIMEOUT(timeout)) return 0; uint32_t start_ticks = GetTicks(); - timeout = start_ticks + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD); - do { - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - }while (IS_LOW(sample) && !IS_TIMEOUT(timeout)); + while (IS_LOW(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout)); if (IS_TIMEOUT(timeout)) return 0; - timeout = (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD) + GetTicks(); - do { - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - }while (IS_HIGH(sample) && !IS_TIMEOUT(timeout)); + while (IS_HIGH(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout)); if (IS_TIMEOUT(timeout)) return 0; - return GetTicks() - start_ticks; + return TICKS_ELAPSED(start_ticks); } /** - * get_pulse_invert_length - * - * Times rising edge pules - * TODO: convert to single function with get_pulse_length() - */ -static uint32_t get_pulse_invert_length(void) { + * get_rising_pulse_length + * + * Returns time between rising edge pulse in ticks + */ +static uint32_t get_rising_pulse_length(void) { - uint8_t sample; - uint32_t timeout = GetTicks() + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD); + uint32_t timeout = GetTicks() + EM4X70_T_TAG_TIMEOUT; - do { - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - }while (IS_LOW(sample) && !IS_TIMEOUT(timeout)); + while (IS_LOW(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout)); if (IS_TIMEOUT(timeout)) return 0; uint32_t start_ticks = GetTicks(); - timeout = start_ticks + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD); - do { - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - }while (IS_HIGH(sample) && !IS_TIMEOUT(timeout)); + while (IS_HIGH(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout)); if (IS_TIMEOUT(timeout)) return 0; - timeout = GetTicks() + (TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD); - do { - sample = (uint8_t)AT91C_BASE_SSC->SSC_RHR; - }while (IS_LOW(sample) && !IS_TIMEOUT(timeout)); + while (IS_LOW(AT91C_BASE_SSC->SSC_RHR) && !IS_TIMEOUT(timeout)); if (IS_TIMEOUT(timeout)) return 0; - return GetTicks() - start_ticks; + return TICKS_ELAPSED(start_ticks); } -static bool check_pulse_length(uint32_t pl, int length, int margin) { +static uint32_t get_pulse_length(edge_detection_t edge) { + + if (edge == RISING_EDGE) + return get_rising_pulse_length(); + else if (edge == FALLING_EDGE) + return get_falling_pulse_length(); + + return 0; +} + +static bool check_pulse_length(uint32_t pl, uint32_t length) { // check if pulse length corresponds to given length - //Dbprintf("%s: pulse length %d vs %d", __func__, pl, length * TICKS_PER_FC); - return ((pl >= TICKS_PER_FC * (length - margin)) & (pl <= TICKS_PER_FC * (length + margin))); + return ((pl >= (length - EM4X70_T_TAG_TOLERANCE)) && (pl <= (length + EM4X70_T_TAG_TOLERANCE))); } -static void em4x70_send_bit(int bit) { +static void em4x70_send_bit(bool bit) { // send single bit according to EM4170 application note and datasheet - uint32_t start_ticks = GetTicks(); if (bit == 0) { - // disable modulation (drop the field) for 4 cycles of carrier + // disable modulation (drop the field) n cycles of carrier LOW(GPIO_SSC_DOUT); - while (GetTicks() - start_ticks <= TICKS_PER_FC * 4); + while (TICKS_ELAPSED(start_ticks) <= EM4X70_T_TAG_BITMOD); // enable modulation (activates the field) for remaining first // half of bit period HIGH(GPIO_SSC_DOUT); - while (GetTicks() - start_ticks <= TICKS_PER_FC * EM4X70_T_TAG_HALF_PERIOD); + while (TICKS_ELAPSED(start_ticks) <= EM4X70_T_TAG_HALF_PERIOD); // disable modulation for second half of bit period LOW(GPIO_SSC_DOUT); - while (GetTicks() - start_ticks <= TICKS_PER_FC * EM4X70_T_TAG_FULL_PERIOD); + while (TICKS_ELAPSED(start_ticks) <= EM4X70_T_TAG_FULL_PERIOD); } else { // bit = "1" means disable modulation for full bit period LOW(GPIO_SSC_DOUT); - while (GetTicks() - start_ticks <= TICKS_PER_FC * EM4X70_T_TAG_FULL_PERIOD); + while (TICKS_ELAPSED(start_ticks) <= EM4X70_T_TAG_FULL_PERIOD); } - } - /** - * em4x70_send_command + * em4x70_send_nibble + * + * sends 4 bits of data + 1 bit of parity (with_parity) + * */ -static void em4170_send_command(uint8_t command) { +static void em4x70_send_nibble(uint8_t nibble, bool with_parity) { int parity = 0; int msb_bit = 0; // Non automotive EM4x70 based tags are 3 bits + 1 parity. // So drop the MSB and send a parity bit instead after the command - if(command_parity) + if (command_parity) msb_bit = 1; - + for (int i = msb_bit; i < 4; i++) { - int bit = (command >> (3 - i)) & 1; + int bit = (nibble >> (3 - i)) & 1; em4x70_send_bit(bit); parity ^= bit; } - if(command_parity) + if (with_parity) em4x70_send_bit(parity); - } +static void em4x70_send_byte(uint8_t byte) { + // Send byte msb first + for (int i = 0; i < 8; i++) + em4x70_send_bit((byte >> (7 - i)) & 1); +} + +static void em4x70_send_word(const uint16_t word) { + + // Split into nibbles + uint8_t nibbles[4]; + uint8_t j = 0; + for (int i = 0; i < 2; i++) { + uint8_t byte = (word >> (8 * i)) & 0xff; + nibbles[j++] = (byte >> 4) & 0xf; + nibbles[j++] = byte & 0xf; + } + + // send 16 bit word with parity bits according to EM4x70 datasheet + // sent as 4 x nibbles (4 bits + parity) + for (int i = 0; i < 4; i++) { + em4x70_send_nibble(nibbles[i], true); + } + + // send column parities (4 bit) + em4x70_send_nibble(nibbles[0] ^ nibbles[1] ^ nibbles[2] ^ nibbles[3], false); + + // send final stop bit (always "0") + em4x70_send_bit(0); +} + +static bool check_ack(void) { + // returns true if signal structue corresponds to ACK, anything else is + // counted as NAK (-> false) + // ACK 64 + 64 + // NACK 64 + 48 + if (check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD) && + check_pulse_length(get_pulse_length(FALLING_EDGE), 2 * EM4X70_T_TAG_FULL_PERIOD)) { + // ACK + return true; + } + + // Othewise it was a NACK or Listen Window + return false; +} + +static int authenticate(const uint8_t *rnd, const uint8_t *frnd, uint8_t *response) { + + if (find_listen_window(true)) { + + em4x70_send_nibble(EM4X70_COMMAND_AUTH, true); + + // Send 56-bit Random number + for (int i = 0; i < 7; i++) { + em4x70_send_byte(rnd[i]); + } + + // Send 7 x 0's (Diversity bits) + for (int i = 0; i < 7; i++) { + em4x70_send_bit(0); + } + + // Send 28-bit f(RN) + + // Send first 24 bits + for (int i = 0; i < 3; i++) { + em4x70_send_byte(frnd[i]); + } + + // Send last 4 bits (no parity) + em4x70_send_nibble((frnd[3] >> 4) & 0xf, false); + + // Receive header, 20-bit g(RN), LIW + uint8_t grnd[EM4X70_MAX_RECEIVE_LENGTH] = {0}; + int num = em4x70_receive(grnd, 20); + if (num < 20) { + Dbprintf("Auth failed"); + return PM3_ESOFT; + } + bits2bytes(grnd, 24, response); + return PM3_SUCCESS; + } + + return PM3_ESOFT; +} + +static int send_pin(const uint32_t pin) { + + // sends pin code for unlocking + if (find_listen_window(true)) { + + // send PIN command + em4x70_send_nibble(EM4X70_COMMAND_PIN, true); + + // --> Send TAG ID (bytes 4-7) + for (int i = 0; i < 4; i++) { + em4x70_send_byte(tag.data[7 - i]); + } + + // --> Send PIN + for (int i = 0; i < 4 ; i++) { + em4x70_send_byte((pin >> (i * 8)) & 0xff); + } + + // Wait TWALB (write access lock bits) + WaitTicks(EM4X70_T_TAG_TWALB); + + // <-- Receive ACK + if (check_ack()) { + + // Writes Lock Bits + WaitTicks(EM4X70_T_TAG_WEE); + // <-- Receive header + ID + uint8_t tag_id[EM4X70_MAX_RECEIVE_LENGTH]; + int num = em4x70_receive(tag_id, 32); + if (num < 32) { + Dbprintf("Invalid ID Received"); + return PM3_ESOFT; + } + bits2bytes(tag_id, num, &tag.data[4]); + return PM3_SUCCESS; + } + } + + return PM3_ESOFT; +} + +static int write(const uint16_t word, const uint8_t address) { + + // writes to specified
+ if (find_listen_window(true)) { + + // send write command + em4x70_send_nibble(EM4X70_COMMAND_WRITE, true); + + // send address data with parity bit + em4x70_send_nibble(address, true); + + // send data word + em4x70_send_word(word); + + // Wait TWA + WaitTicks(EM4X70_T_TAG_TWA); + + // look for ACK sequence + if (check_ack()) { + + // now EM4x70 needs EM4X70_T_TAG_TWEE (EEPROM write time) + // for saving data and should return with ACK + WaitTicks(EM4X70_T_TAG_WEE); + if (check_ack()) { + + return PM3_SUCCESS; + } + } + } + return PM3_ESOFT; +} + + static bool find_listen_window(bool command) { - + int cnt = 0; - while(cnt < EM4X70_T_WAITING_FOR_SNGLLIW) { + while (cnt < EM4X70_T_WAITING_FOR_LIW) { /* 80 ( 64 + 16 ) 80 ( 64 + 16 ) @@ -311,26 +426,24 @@ static bool find_listen_window(bool command) { 96 ( 64 + 32 ) 64 ( 32 + 16 +16 )*/ - if (check_pulse_length(get_pulse_invert_length(), 80, EM4X70_TAG_TOLERANCE)) { - if (check_pulse_length(get_pulse_invert_length(), 80, EM4X70_TAG_TOLERANCE)) { - if (check_pulse_length(get_pulse_length(), 96, EM4X70_TAG_TOLERANCE)) { - if (check_pulse_length(get_pulse_length(), 64, EM4X70_TAG_TOLERANCE)) { - if(command) { - /* Here we are after the 64 duration edge. - * em4170 says we need to wait about 48 RF clock cycles. - * depends on the delay between tag and us - * - * I've found between 4-5 quarter periods (32-40) works best - */ - WaitTicks(TICKS_PER_FC * 5 * EM4X70_T_TAG_QUARTER_PERIOD); - // Send RM Command - em4x70_send_bit(0); - em4x70_send_bit(0); - } - return true; - } - } + if (check_pulse_length(get_pulse_length(RISING_EDGE), (2 * EM4X70_T_TAG_FULL_PERIOD) + EM4X70_T_TAG_HALF_PERIOD) && + check_pulse_length(get_pulse_length(RISING_EDGE), (2 * EM4X70_T_TAG_FULL_PERIOD) + EM4X70_T_TAG_HALF_PERIOD) && + check_pulse_length(get_pulse_length(FALLING_EDGE), (2 * EM4X70_T_TAG_FULL_PERIOD) + EM4X70_T_TAG_FULL_PERIOD) && + check_pulse_length(get_pulse_length(FALLING_EDGE), EM4X70_T_TAG_FULL_PERIOD + (2 * EM4X70_T_TAG_HALF_PERIOD))) { + + if (command) { + /* Here we are after the 64 duration edge. + * em4170 says we need to wait about 48 RF clock cycles. + * depends on the delay between tag and us + * + * I've found between 4-5 quarter periods (32-40) works best + */ + WaitTicks(4 * EM4X70_T_TAG_QUARTER_PERIOD); + // Send RM Command + em4x70_send_bit(0); + em4x70_send_bit(0); } + return true; } cnt++; } @@ -338,22 +451,21 @@ static bool find_listen_window(bool command) { return false; } -static void bits2bytes(uint8_t *bits, int length, uint8_t *out) { - - if(length%8 != 0) { +static void bits2bytes(const uint8_t *bits, int length, uint8_t *out) { + + if (length % 8 != 0) { Dbprintf("Should have a multiple of 8 bits, was sent %d", length); } - + int num_bytes = length / 8; // We should have a multiple of 8 here - for(int i=1; i <= num_bytes; i++) { - out[num_bytes-i] = bits2byte(bits, 8); - bits+=8; - //Dbprintf("Read: %02X", out[num_bytes-i]); - } + for (int i = 1; i <= num_bytes; i++) { + out[num_bytes - i] = bits2byte(bits, 8); + bits += 8; + } } -static uint8_t bits2byte(uint8_t *bits, int length) { +static uint8_t bits2byte(const uint8_t *bits, int length) { // converts separate bits into a single "byte" uint8_t byte = 0; @@ -368,161 +480,134 @@ static uint8_t bits2byte(uint8_t *bits, int length) { return byte; } -/*static void print_array(uint8_t *bits, int len) { +static bool send_command_and_read(uint8_t command, uint8_t *bytes, size_t length) { - if(len%8 != 0) { - Dbprintf("Should have a multiple of 8 bits, was sent %d", len); - } - - int num_bytes = len / 8; // We should have a multiple of 8 here + int retries = EM4X70_COMMAND_RETRIES; + while (retries) { + retries--; - uint8_t bytes[8]; - - for(int i=0;i speed up "lf search" process return find_listen_window(false); } -static int em4x70_receive(uint8_t *bits) { +static int em4x70_receive(uint8_t *bits, size_t length) { uint32_t pl; int bit_pos = 0; - uint8_t edge = 0; - - + edge_detection_t edge = RISING_EDGE; bool foundheader = false; // Read out the header // 12 Manchester 1's (may miss some during settle period) // 4 Manchester 0's - + // Skip a few leading 1's as it could be noisy - WaitTicks(TICKS_PER_FC * 3 * EM4X70_T_TAG_FULL_PERIOD); + WaitTicks(6 * EM4X70_T_TAG_FULL_PERIOD); // wait until we get the transition from 1's to 0's which is 1.5 full windows - int pulse_count = 0; - while(pulse_count < 12){ - pl = get_pulse_invert_length(); - pulse_count++; - if(check_pulse_length(pl, 3 * EM4X70_T_TAG_HALF_PERIOD, EM4X70_TAG_TOLERANCE)) { + for (int i = 0; i < EM4X70_T_READ_HEADER_LEN; i++) { + pl = get_pulse_length(edge); + if (check_pulse_length(pl, 3 * EM4X70_T_TAG_HALF_PERIOD)) { foundheader = true; break; } } - if(!foundheader) { + if (!foundheader) { Dbprintf("Failed to find read header"); return 0; } // Skip next 3 0's, header check consumes the first 0 - for(int i = 0; i < 3; i++) { - get_pulse_invert_length(); + for (int i = 0; i < 3; i++) { + // If pulse length is not 1 bit, then abort early + if (!check_pulse_length(get_pulse_length(edge), EM4X70_T_TAG_FULL_PERIOD)) { + return 0; + } } // identify remaining bits based on pulse lengths - // between two listen windows only pulse lengths of 1, 1.5 and 2 are possible - while (true) { + // between listen windows only pulse lengths of 1, 1.5 and 2 are possible + while (bit_pos < length) { - if(edge) - pl = get_pulse_length(); - else - pl = get_pulse_invert_length(); + pl = get_pulse_length(edge); - if (check_pulse_length(pl, EM4X70_T_TAG_FULL_PERIOD, EM4X70_T_TAG_QUARTER_PERIOD)) { + if (check_pulse_length(pl, EM4X70_T_TAG_FULL_PERIOD)) { - // pulse length = 1 - bits[bit_pos++] = edge; + // pulse length 1 -> assign bit + bits[bit_pos++] = edge == FALLING_EDGE ? 1 : 0; - } else if (check_pulse_length(pl, 3 * EM4X70_T_TAG_HALF_PERIOD, EM4X70_T_TAG_QUARTER_PERIOD)) { + } else if (check_pulse_length(pl, 3 * EM4X70_T_TAG_HALF_PERIOD)) { - // pulse length = 1.5 -> flip edge detection - if(edge) { + // pulse length 1.5 -> 2 bits + flip edge detection + if (edge == FALLING_EDGE) { bits[bit_pos++] = 0; bits[bit_pos++] = 0; - edge = 0; + edge = RISING_EDGE; } else { bits[bit_pos++] = 1; bits[bit_pos++] = 1; - edge = 1; + edge = FALLING_EDGE; } - } else if (check_pulse_length(pl, 2 * EM4X70_T_TAG_FULL_PERIOD, EM4X70_T_TAG_QUARTER_PERIOD)) { + } else if (check_pulse_length(pl, 2 * EM4X70_T_TAG_FULL_PERIOD)) { - // pulse length of 2 - if(edge) { + // pulse length of 2 -> two bits + if (edge == FALLING_EDGE) { bits[bit_pos++] = 0; bits[bit_pos++] = 1; } else { @@ -530,29 +615,27 @@ static int em4x70_receive(uint8_t *bits) { bits[bit_pos++] = 0; } - } else if ( (edge && check_pulse_length(pl, 3 * EM4X70_T_TAG_FULL_PERIOD, EM4X70_T_TAG_QUARTER_PERIOD)) || - (!edge && check_pulse_length(pl, 80, EM4X70_T_TAG_QUARTER_PERIOD))) { - - // LIW detected (either invert or normal) - return --bit_pos; + } else { + // Listen Window, or invalid bit + break; } } - return bit_pos; + return bit_pos; } void em4x70_info(em4x70_data_t *etd) { uint8_t status = 0; - + // Support tags with and without command parity bits command_parity = etd->parity; init_tag(); - EM4170_setup_read(); + em4x70_setup_read(); // Find the Tag - if (get_signalproperties() && find_EM4X70_Tag()) { + if (get_signalproperties() && find_em4x70_tag()) { // Read ID, UM1 and UM2 status = em4x70_read_id() && em4x70_read_um1() && em4x70_read_um2(); } @@ -561,3 +644,166 @@ void em4x70_info(em4x70_data_t *etd) { lf_finalize(); reply_ng(CMD_LF_EM4X70_INFO, status, tag.data, sizeof(tag.data)); } + +void em4x70_write(em4x70_data_t *etd) { + + uint8_t status = 0; + + command_parity = etd->parity; + + init_tag(); + em4x70_setup_read(); + + // Find the Tag + if (get_signalproperties() && find_em4x70_tag()) { + + // Write + status = write(etd->word, etd->address) == PM3_SUCCESS; + + if (status) { + // Read Tag after writing + if (em4x70_read_id()) { + em4x70_read_um1(); + em4x70_read_um2(); + } + } + + } + + StopTicks(); + lf_finalize(); + reply_ng(CMD_LF_EM4X70_WRITE, status, tag.data, sizeof(tag.data)); +} + +void em4x70_unlock(em4x70_data_t *etd) { + + uint8_t status = 0; + + command_parity = etd->parity; + + init_tag(); + em4x70_setup_read(); + + // Find the Tag + if (get_signalproperties() && find_em4x70_tag()) { + + // Read ID (required for send_pin command) + if (em4x70_read_id()) { + + // Send PIN + status = send_pin(etd->pin) == PM3_SUCCESS; + + // If the write succeeded, read the rest of the tag + if (status) { + // Read Tag + // ID doesn't change + em4x70_read_um1(); + em4x70_read_um2(); + } + } + } + + StopTicks(); + lf_finalize(); + reply_ng(CMD_LF_EM4X70_UNLOCK, status, tag.data, sizeof(tag.data)); +} + +void em4x70_auth(em4x70_data_t *etd) { + + uint8_t status = 0; + uint8_t response[3] = {0}; + + command_parity = etd->parity; + + init_tag(); + em4x70_setup_read(); + + // Find the Tag + if (get_signalproperties() && find_em4x70_tag()) { + + // Authenticate and get tag response + status = authenticate(etd->rnd, etd->frnd, response) == PM3_SUCCESS; + } + + StopTicks(); + lf_finalize(); + reply_ng(CMD_LF_EM4X70_AUTH, status, response, sizeof(response)); +} + +void em4x70_write_pin(em4x70_data_t *etd) { + + uint8_t status = 0; + + command_parity = etd->parity; + + init_tag(); + em4x70_setup_read(); + + // Find the Tag + if (get_signalproperties() && find_em4x70_tag()) { + + // Read ID (required for send_pin command) + if (em4x70_read_id()) { + + // Write new PIN + if ((write(etd->pin & 0xFFFF, EM4X70_PIN_WORD_UPPER) == PM3_SUCCESS) && + (write((etd->pin >> 16) & 0xFFFF, EM4X70_PIN_WORD_LOWER) == PM3_SUCCESS)) { + + // Now Try to authenticate using the new PIN + + // Send PIN + status = send_pin(etd->pin) == PM3_SUCCESS; + + // If the write succeeded, read the rest of the tag + if (status) { + // Read Tag + // ID doesn't change + em4x70_read_um1(); + em4x70_read_um2(); + } + } + } + } + + StopTicks(); + lf_finalize(); + reply_ng(CMD_LF_EM4X70_WRITEPIN, status, tag.data, sizeof(tag.data)); +} + +void em4x70_write_key(em4x70_data_t *etd) { + + uint8_t status = 0; + + command_parity = etd->parity; + + init_tag(); + em4x70_setup_read(); + + // Find the Tag + if (get_signalproperties() && find_em4x70_tag()) { + + // Read ID to ensure we can write to card + if (em4x70_read_id()) { + status = 1; + + // Write each crypto block + for (int i = 0; i < 6; i++) { + + uint16_t key_word = (etd->crypt_key[(i * 2) + 1] << 8) + etd->crypt_key[i * 2]; + // Write each word, abort if any failure occurs + if (write(key_word, 9 - i) != PM3_SUCCESS) { + status = 0; + break; + } + } + // TODO: Ideally here we would perform a test authentication + // to ensure the new key was written correctly. This is + // what the datasheet suggests. We can't do that until + // we have the crypto algorithm implemented. + } + } + + StopTicks(); + lf_finalize(); + reply_ng(CMD_LF_EM4X70_WRITEKEY, status, tag.data, sizeof(tag.data)); +} diff --git a/armsrc/em4x70.h b/armsrc/em4x70.h index 80fd977a9..5bb1a2a37 100644 --- a/armsrc/em4x70.h +++ b/armsrc/em4x70.h @@ -17,6 +17,16 @@ typedef struct { uint8_t data[32]; } em4x70_tag_t; +typedef enum { + RISING_EDGE, + FALLING_EDGE +} edge_detection_t; + void em4x70_info(em4x70_data_t *etd); +void em4x70_write(em4x70_data_t *etd); +void em4x70_unlock(em4x70_data_t *etd); +void em4x70_auth(em4x70_data_t *etd); +void em4x70_write_pin(em4x70_data_t *etd); +void em4x70_write_key(em4x70_data_t *etd); #endif /* EM4x70_H */ diff --git a/armsrc/iclass.c b/armsrc/iclass.c index 5cb5d71ae..4d5975324 100644 --- a/armsrc/iclass.c +++ b/armsrc/iclass.c @@ -408,9 +408,9 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { // Tag CSN uint8_t *modulated_response = NULL; - int modulated_response_size = 0; + int modulated_response_size; uint8_t *trace_data = NULL; - int trace_data_size = 0; + int trace_data_size; // Respond SOF -- takes 1 bytes uint8_t *resp_sof = BigBuf_malloc(1); @@ -498,10 +498,9 @@ int do_iclass_simulation(int simulationMode, uint8_t *reader_mac_buf) { bool button_pressed = false; uint8_t cmd, options, block; - int len = 0; + int len, kc_attempt = 0; bool exit_loop = false; bool using_kc = false; - int kc_attempt = 0; while (exit_loop == false) { WDT_HIT(); @@ -1364,7 +1363,9 @@ static bool select_iclass_tag_ex(picopass_hdr *hdr, bool use_credit_key, uint32_ return false; memcpy(hdr->epurse, resp, sizeof(hdr->epurse)); - *status |= FLAG_ICLASS_CC; + + if (status) + *status |= FLAG_ICLASS_CC; } else { @@ -1469,16 +1470,9 @@ void ReaderIClass(uint8_t flags) { switch_off(); } -// used with function select_and_auth (cmdhficlass.c) -// which needs to authenticate before doing more things like read/write -// selects and authenticate to a card, sends back div_key and mac to client. -void iClass_Authentication(uint8_t *msg) { -} - bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr *hdr, uint32_t *start_time, uint32_t *eof_time, uint8_t *mac_out) { uint8_t cmd_check[9] = { ICLASS_CMD_CHECK }; - uint8_t div_key[8] = {0}; uint8_t mac[4] = {0}; uint8_t resp_auth[4] = {0}; uint8_t ccnr[12] = {0}; @@ -1495,6 +1489,8 @@ bool authenticate_iclass_tag(iclass_auth_req_t *payload, picopass_hdr *hdr, uint memcpy(cmd_check + 1, payload->key, 8); } else { + + uint8_t div_key[8] = {0}; if (payload->use_raw) memcpy(div_key, payload->key, 8); else @@ -1792,7 +1788,7 @@ static bool iclass_writeblock_ext(uint8_t blockno, uint8_t *data, uint8_t *mac, uint8_t resp[10] = {0}; uint32_t eof_time = 0, start_time = 0; - bool isOK = iclass_send_cmd_with_retries(write, sizeof(write), resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_UPDATE, &eof_time); + bool isOK = iclass_send_cmd_with_retries(write, write_len, resp, sizeof(resp), 10, 3, &start_time, ICLASS_READER_TIMEOUT_UPDATE, &eof_time); if (isOK == false) { return false; } @@ -1833,7 +1829,7 @@ void iClass_WriteBlock(uint8_t *msg) { // select tag. uint32_t eof_time = 0; picopass_hdr hdr = {0}; - bool res = select_iclass_tag(&hdr, payload->req.use_credit_key, &eof_time); + uint8_t res = select_iclass_tag(&hdr, payload->req.use_credit_key, &eof_time); if (res == false) { goto out; } diff --git a/armsrc/iclass.h b/armsrc/iclass.h index 1895ebbf6..b41b79ce8 100644 --- a/armsrc/iclass.h +++ b/armsrc/iclass.h @@ -16,7 +16,7 @@ #include "pm3_cmd.h" void SniffIClass(uint8_t jam_search_len, uint8_t *jam_search_string); -void ReaderIClass(uint8_t arg0); +void ReaderIClass(uint8_t flags); void iClass_WriteBlock(uint8_t *msg); void iClass_Dump(uint8_t *msg); @@ -29,7 +29,6 @@ void SimulateIClass(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain void iclass_simulate(uint8_t sim_type, uint8_t num_csns, bool send_reply, uint8_t *datain, uint8_t *dataout, uint16_t *dataoutlen); void iClass_Authentication_fast(uint64_t arg0, uint64_t arg1, uint8_t *datain); -void iClass_Authentication(uint8_t *bytes); bool iclass_auth(iclass_auth_req_t *payload, uint8_t *out); void iClass_ReadBlock(uint8_t *msg); diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index db00f5494..cd07a4007 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -133,35 +133,30 @@ static hf14a_config hf14aconfig = { 0, 0, 0, 0, 0 } ; void printHf14aConfig(void) { DbpString(_CYAN_("HF 14a config")); - Dbprintf(" [a] Anticol override....%i %s%s%s", - hf14aconfig.forceanticol, - (hf14aconfig.forceanticol == 0) ? "( " _GREEN_("No") " ) follow standard " : "", - (hf14aconfig.forceanticol == 1) ? "( " _RED_("Yes") " ) always do anticol" : "", - (hf14aconfig.forceanticol == 2) ? "( " _RED_("Yes") " ) always skip anticol" : "" + Dbprintf(" [a] Anticol override....%s%s%s", + (hf14aconfig.forceanticol == 0) ? _GREEN_("std") " : follow standard " : "", + (hf14aconfig.forceanticol == 1) ? _RED_("force") " : always do anticol" : "", + (hf14aconfig.forceanticol == 2) ? _RED_("skip") " : always skip anticol" : "" ); - Dbprintf(" [b] BCC override........%i %s%s%s", - hf14aconfig.forcebcc, - (hf14aconfig.forcebcc == 0) ? "( " _GREEN_("No") " ) follow standard" : "", - (hf14aconfig.forcebcc == 1) ? "( " _RED_("Yes") " ) always do CL2" : "", - (hf14aconfig.forcebcc == 2) ? "( " _RED_("Yes") " ) always use card BCC" : "" + Dbprintf(" [b] BCC override........%s%s%s", + (hf14aconfig.forcebcc == 0) ? _GREEN_("std") " : follow standard" : "", + (hf14aconfig.forcebcc == 1) ? _RED_("fix") " : fix bad BCC" : "", + (hf14aconfig.forcebcc == 2) ? _RED_("ignore") " : ignore bad BCC, always use card BCC" : "" ); - Dbprintf(" [2] CL2 override........%i %s%s%s", - hf14aconfig.forcecl2, - (hf14aconfig.forcecl2 == 0) ? "( " _GREEN_("No") " ) follow standard" : "", - (hf14aconfig.forcecl2 == 1) ? "( " _RED_("Yes") " ) always do CL2" : "", - (hf14aconfig.forcecl2 == 2) ? "( " _RED_("Yes") " ) always skip CL2" : "" + Dbprintf(" [2] CL2 override........%s%s%s", + (hf14aconfig.forcecl2 == 0) ? _GREEN_("std") " : follow standard" : "", + (hf14aconfig.forcecl2 == 1) ? _RED_("force") " : always do CL2" : "", + (hf14aconfig.forcecl2 == 2) ? _RED_("skip") " : always skip CL2" : "" ); - Dbprintf(" [3] CL3 override........%i %s%s%s", - hf14aconfig.forcecl3, - (hf14aconfig.forcecl3 == 0) ? "( " _GREEN_("No") " ) follow standard" : "", - (hf14aconfig.forcecl3 == 1) ? "( " _RED_("Yes") " ) always do CL3" : "", - (hf14aconfig.forcecl3 == 2) ? "( " _RED_("Yes") " ) always skip CL3" : "" + Dbprintf(" [3] CL3 override........%s%s%s", + (hf14aconfig.forcecl3 == 0) ? _GREEN_("std") " : follow standard" : "", + (hf14aconfig.forcecl3 == 1) ? _RED_("force") " : always do CL3" : "", + (hf14aconfig.forcecl3 == 2) ? _RED_("skip") " : always skip CL3" : "" ); - Dbprintf(" [r] RATS override.......%i %s%s%s", - hf14aconfig.forcerats, - (hf14aconfig.forcerats == 0) ? "( " _GREEN_("No") " ) follow standard " : "", - (hf14aconfig.forcerats == 1) ? "( " _RED_("Yes") " ) always do RATS" : "", - (hf14aconfig.forcerats == 2) ? "( " _RED_("Yes") " ) always skip RATS" : "" + Dbprintf(" [r] RATS override.......%s%s%s", + (hf14aconfig.forcerats == 0) ? _GREEN_("std") " : follow standard " : "", + (hf14aconfig.forcerats == 1) ? _RED_("force") " : always do RATS" : "", + (hf14aconfig.forcerats == 2) ? _RED_("skip") " : always skip RATS" : "" ); } @@ -921,18 +916,25 @@ bool GetIso14443aCommandFromReader(uint8_t *received, uint8_t *par, int *len) { uint8_t b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; (void)b; - uint16_t check = 0; - + uint8_t flip = 0; + uint16_t checker = 0; for (;;) { - if (check == 4000) { -// if (BUTTON_PRESS() || data_available()) + WDT_HIT(); + if (flip == 3) { + if (data_available()) + return false; + + flip = 0; + } + + if (checker >= 3000) { if (BUTTON_PRESS()) return false; - check = 0; - WDT_HIT(); + flip++; + checker = 0; } - ++check; + ++checker; if (AT91C_BASE_SSC->SSC_SR & (AT91C_SSC_RXRDY)) { b = (uint8_t)AT91C_BASE_SSC->SSC_RHR; @@ -1340,7 +1342,6 @@ void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data, uint8_t LED_A_ON(); // main loop - //for (;;) { bool finished = false; bool button_pushed = BUTTON_PRESS(); while (!button_pushed && !finished) { diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 325eed056..54764d223 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -129,7 +129,7 @@ RAMFUNC bool MillerDecoding(uint8_t bit, uint32_t non_real_time); RAMFUNC int ManchesterDecoding(uint8_t bit, uint16_t offset, uint32_t non_real_time); void RAMFUNC SniffIso14443a(uint8_t param); -void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data, uint8_t numReads); +void SimulateIso14443aTag(uint8_t tagType, uint8_t flags, uint8_t *data, uint8_t exitAfterNReads); bool SimulateIso14443aInit(int tagType, int flags, uint8_t *data, tag_response_info_t **responses, uint32_t *cuid, uint32_t counters[3], uint8_t tearings[3], uint8_t *pages); bool GetIso14443aCommandFromReader(uint8_t *received, uint8_t *par, int *len); void iso14443a_antifuzz(uint32_t flags); diff --git a/armsrc/iso14443b.c b/armsrc/iso14443b.c index f170be153..c3c61e980 100644 --- a/armsrc/iso14443b.c +++ b/armsrc/iso14443b.c @@ -1201,7 +1201,7 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, int timeo if (Handle14443bSamplesFromTag(ci, cq)) { - *eof_time = dma_start_time + (samples) - DELAY_TAG_TO_ARM; // end of EOF + *eof_time = GetCountSspClkDelta(dma_start_time) - (DELAY_TAG_TO_ARM * 128); // end of EOF if (Demod.len > Demod.max_len) { ret = -2; // overflow @@ -1209,7 +1209,7 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, int timeo break; } - if (samples > timeout && Demod.state < DEMOD_PHASE_REF_TRAINING) { + if (((GetCountSspClkDelta(dma_start_time) >> 7) > timeout) && Demod.state < DEMOD_PHASE_REF_TRAINING) { ret = -1; break; } @@ -1225,7 +1225,7 @@ static int Get14443bAnswerFromTag(uint8_t *response, uint16_t max_len, int timeo - (Demod.len * (8 + 2)) // time for byte transfers - (12) // time for SOF transfer - (12); // time for EOF transfer - LogTrace(Demod.output, Demod.len, (sof_time * 4), (*eof_time * 4), NULL, false); + LogTrace(Demod.output, Demod.len, sof_time, *eof_time, NULL, false); } return Demod.len; } @@ -1400,7 +1400,7 @@ int iso14443b_apdu(uint8_t const *msg, size_t msg_len, bool send_chaining, void CodeAndTransmit14443bAsReader(real_cmd, msg_len + 3, &start_time, &eof_time); eof_time += DELAY_ISO14443B_VCD_TO_VICC_READER; - int len = Get14443bAnswerFromTag(rxdata, rxmaxlen, ISO14443B_READER_TIMEOUT, &eof_time); + int len = Get14443bAnswerFromTag(rxdata, rxmaxlen, iso14b_timeout, &eof_time); FpgaDisableTracing(); uint8_t *data_bytes = (uint8_t *) rxdata; diff --git a/armsrc/iso15693.c b/armsrc/iso15693.c index b6d1b7ed8..de7a409bd 100644 --- a/armsrc/iso15693.c +++ b/armsrc/iso15693.c @@ -1602,7 +1602,7 @@ void ReaderIso15693(uint32_t parameter) { reply_mix(CMD_ACK, recvlen, 0, 0, NULL, 0); } else { - start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + //start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; // we should do a better check than this if (recvlen >= 12) { @@ -1686,7 +1686,7 @@ void SimTagIso15693(uint8_t *uid) { enum { NO_FIELD, IDLE, ACTIVATED, SELECTED, HALTED } chip_state = NO_FIELD; bool button_pressed = false; - int vHf = 0; // in mV + int vHf; // in mV bool exit_loop = false; while (exit_loop == false) { @@ -1719,7 +1719,6 @@ void SimTagIso15693(uint8_t *uid) { int cmd_len = GetIso15693CommandFromReader(cmd, sizeof(cmd), &reader_eof_time); if (cmd_len < 0) { button_pressed = true; - exit_loop = true; break; } @@ -1727,7 +1726,7 @@ void SimTagIso15693(uint8_t *uid) { if ((cmd_len >= 5) && (cmd[0] & ISO15_REQ_INVENTORY) && (cmd[1] == ISO15_CMD_INVENTORY)) { bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; - + // Build INVENTORY command uint8_t resp_inv[CMD_INV_RESP] = {0}; @@ -1743,30 +1742,30 @@ void SimTagIso15693(uint8_t *uid) { resp_inv[7] = uid[2]; resp_inv[8] = uid[1]; resp_inv[9] = uid[0]; - + // CRC AddCrc15(resp_inv, 10); CodeIso15693AsTag(resp_inv, CMD_INV_RESP); - + tosend_t *ts = get_tosend(); - + TransmitTo15693Reader(ts->buf, ts->max, &response_time, 0, slow); LogTrace_ISO15693(resp_inv, CMD_INV_RESP, response_time * 32, (response_time * 32) + (ts->max * 32 * 64), NULL, false); chip_state = SELECTED; } - + // GET_SYSTEM_INFO if ((cmd[1] == ISO15_CMD_SYSINFO)) { bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; - + // Build GET_SYSTEM_INFO command uint8_t resp_sysinfo[CMD_SYSINFO_RESP] = {0}; - + resp_sysinfo[0] = 0; // Response flags. resp_sysinfo[1] = 0x0F; // Information flags (0x0F - DSFID, AFI, Mem size, IC) - + // 64-bit UID resp_sysinfo[2] = uid[7]; resp_sysinfo[3] = uid[6]; @@ -1776,42 +1775,42 @@ void SimTagIso15693(uint8_t *uid) { resp_sysinfo[7] = uid[2]; resp_sysinfo[8] = uid[1]; resp_sysinfo[9] = uid[0]; - + resp_sysinfo[10] = 0; // DSFID resp_sysinfo[11] = 0; // AFI resp_sysinfo[12] = 0x1B; // Memory size. resp_sysinfo[13] = 0x03; // Memory size. resp_sysinfo[14] = 0x01; // IC reference. - + // CRC AddCrc15(resp_sysinfo, 15); CodeIso15693AsTag(resp_sysinfo, CMD_SYSINFO_RESP); - + tosend_t *ts = get_tosend(); - + TransmitTo15693Reader(ts->buf, ts->max, &response_time, 0, slow); LogTrace_ISO15693(resp_sysinfo, CMD_SYSINFO_RESP, response_time * 32, (response_time * 32) + (ts->max * 32 * 64), NULL, false); } - + // READ_BLOCK if ((cmd[1] == ISO15_CMD_READ)) { bool slow = !(cmd[0] & ISO15_REQ_DATARATE_HIGH); uint32_t response_time = reader_eof_time + DELAY_ISO15693_VCD_TO_VICC_SIM; - + // Build GET_SYSTEM_INFO command uint8_t resp_readblock[CMD_READBLOCK_RESP] = {0}; - + resp_readblock[0] = 0; // Response flags. resp_readblock[1] = 0; // Block data. resp_readblock[2] = 0; // Block data. resp_readblock[3] = 0; // Block data. resp_readblock[4] = 0; // Block data. - + // CRC AddCrc15(resp_readblock, 5); CodeIso15693AsTag(resp_readblock, CMD_READBLOCK_RESP); - + tosend_t *ts = get_tosend(); TransmitTo15693Reader(ts->buf, ts->max, &response_time, 0, slow); @@ -1845,9 +1844,8 @@ void BruteforceIso15693Afi(uint32_t speed) { int datalen = 5; uint32_t eof_time = 0; - uint32_t start_time = GetCountSspClk(); int recvlen = SendDataTag(data, datalen, true, speed, recv, sizeof(recv), 0, ISO15693_READER_TIMEOUT, &eof_time); - start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; + uint32_t start_time = eof_time + DELAY_ISO15693_VICC_TO_VCD_READER; WDT_HIT(); diff --git a/armsrc/lfops.c b/armsrc/lfops.c index 32c9568d1..b4daed774 100644 --- a/armsrc/lfops.c +++ b/armsrc/lfops.c @@ -154,7 +154,7 @@ t55xx_configurations_t T55xx_Timing = { { 29 * 8, 17 * 8, 15 * 8, 40 * 8, 15 * 8, 0, 0 }, // Leading 0 { 29 * 8, 17 * 8, 15 * 8, 31 * 8, 15 * 8, 47 * 8, 63 * 8 } // 1 of 4 #else -// PM3OTHER or like offical repo +// PM3GENERIC or like official repo { 31 * 8, 20 * 8, 18 * 8, 50 * 8, 15 * 8, 0, 0 }, // Default Fixed { 31 * 8, 20 * 8, 18 * 8, 50 * 8, 15 * 8, 0, 0 }, // Long Leading Ref. { 31 * 8, 20 * 8, 18 * 8, 40 * 8, 15 * 8, 0, 0 }, // Leading 0 @@ -1009,7 +1009,7 @@ void CmdFSKsimTAGEx(uint8_t fchigh, uint8_t fclow, uint8_t separator, uint8_t cl WDT_HIT(); - Dbprintf("Simulating with fcHigh: %d, fcLow: %d, clk: %d, STT: %d, n: %d", fchigh, fclow, clk, separator, n); + Dbprintf("FSK simulating with rf/%d, fc high %d, fc low %d, STT %d, n %d", clk, fchigh, fclow, separator, n); if (ledcontrol) LED_A_ON(); SimulateTagLowFrequencyEx(n, 0, ledcontrol, numcycles); @@ -1122,10 +1122,10 @@ void CmdASKsimTAG(uint8_t encoding, uint8_t invert, uint8_t separator, uint8_t c WDT_HIT(); - Dbprintf("Simulating with clk: %d, invert: %d, encoding: %s (%d), separator: %d, n: %d" + Dbprintf("ASK simulating with rf/%d, invert %d, encoding %s (%d), separator %d, n %d" , clk , invert - , (encoding == 2) ? "BI" : (encoding == 1) ? "ASK" : "RAW" + , (encoding == 2) ? "ASK/BI" : (encoding == 1) ? "ASK/MAN" : "RAW/MAN" , encoding , separator , n @@ -1176,7 +1176,7 @@ void CmdPSKsimTAG(uint8_t carrier, uint8_t invert, uint8_t clk, uint16_t size, u WDT_HIT(); - Dbprintf("Simulating with Carrier: %d, clk: %d, invert: %d, n: %d", carrier, clk, invert, n); + Dbprintf("PSK simulating with rf/%d, fc/%d, invert %d, n %d", clk, carrier, invert, n); if (ledcontrol) LED_A_ON(); SimulateTagLowFrequency(n, 0, ledcontrol); @@ -1220,7 +1220,7 @@ void CmdNRZsimTAG(uint8_t invert, uint8_t separator, uint8_t clk, uint16_t size, WDT_HIT(); - Dbprintf("Simulating with clk: %d, invert: %d, separator: %d, n: %d" + Dbprintf("NRZ simulating with rf/%d, invert %d, separator %d, n %d" , clk , invert , separator @@ -1251,20 +1251,13 @@ int lf_hid_watch(int findone, uint32_t *high, uint32_t *low) { BigBuf_Clear_keep_EM(); int res = PM3_SUCCESS; - uint16_t interval = 0; - while (BUTTON_PRESS() == false) { + for (;;) { WDT_HIT(); - // cancel w usb command. - if (interval == 4000) { - if (data_available()) { - res = PM3_EOPABORTED; - break; - } - interval = 0; - } else { - interval++; + if (data_available() || BUTTON_PRESS()) { + res = PM3_EOPABORTED; + break; } DoAcquisition_default(-1, false); @@ -1360,20 +1353,13 @@ int lf_awid_watch(int findone, uint32_t *high, uint32_t *low) { LFSetupFPGAForADC(LF_DIVISOR_125, true); int res = PM3_SUCCESS; - uint16_t interval = 0; - while (BUTTON_PRESS() == false) { + for (;;) { WDT_HIT(); - // cancel w usb command. - if (interval == 4000) { - if (data_available()) { - res = PM3_EOPABORTED; - break; - } - interval = 0; - } else { - interval++; + if (data_available() || BUTTON_PRESS()) { + res = PM3_EOPABORTED; + break; } DoAcquisition_default(-1, false); @@ -1465,19 +1451,12 @@ int lf_em410x_watch(int findone, uint32_t *high, uint64_t *low) { LFSetupFPGAForADC(LF_DIVISOR_125, true); int res = PM3_SUCCESS; - uint16_t interval = 0; - while (BUTTON_PRESS() == false) { + for (;;) { WDT_HIT(); - // cancel w usb command. - if (interval == 4000) { - if (data_available()) { - res = PM3_EOPABORTED; - break; - } - interval = 0; - } else { - interval++; + if (data_available() || BUTTON_PRESS()) { + res = PM3_EOPABORTED; + break; } DoAcquisition_default(-1, false); @@ -1541,20 +1520,13 @@ int lf_io_watch(int findone, uint32_t *high, uint32_t *low) { LFSetupFPGAForADC(LF_DIVISOR_125, true); int res = PM3_SUCCESS; - uint16_t interval = 0; - while (BUTTON_PRESS() == false) { + for (;;) { WDT_HIT(); - // cancel w usb command. - if (interval == 4000) { - if (data_available()) { - res = PM3_EOPABORTED; - break; - } - interval = 0; - } else { - interval++; + if (data_available() || BUTTON_PRESS()) { + res = PM3_EOPABORTED; + break; } DoAcquisition_default(-1, false); diff --git a/armsrc/lfsampling.c b/armsrc/lfsampling.c index 7a42367dc..f45fe8920 100644 --- a/armsrc/lfsampling.c +++ b/armsrc/lfsampling.c @@ -73,11 +73,11 @@ void printSamples(void) { void setSamplingConfig(sample_config *sc) { // decimation (1-8) how many bits of adc sample value to save - if (sc->decimation > 0 && sc->decimation < 8) + if (sc->decimation > 0 && sc->decimation < 9) config.decimation = sc->decimation; // bits per sample (1-8) - if (sc->bits_per_sample > 0 && sc->bits_per_sample < 8) + if (sc->bits_per_sample > 0 && sc->bits_per_sample < 9) config.bits_per_sample = sc->bits_per_sample; // @@ -515,7 +515,7 @@ void doCotagAcquisition(void) { if (BUTTON_PRESS()) break; - + if (checker == 4000) { if (data_available()) break; diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 7faa0c268..2ea7aa1b8 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -662,9 +662,11 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain) { // Return 1 if the nonce is invalid else return 0 static int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, 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; + 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; } void MifareAcquireNonces(uint32_t arg0, uint32_t flags) { @@ -696,7 +698,7 @@ void MifareAcquireNonces(uint32_t arg0, uint32_t flags) { LED_C_ON(); - for (uint16_t i = 0; i <= PM3_CMD_DATA_SIZE - 4; i += 4) { + while (num_nonces < PM3_CMD_DATA_SIZE / 4) { // Test if the action was cancelled if (BUTTON_PRESS()) { @@ -746,18 +748,14 @@ void MifareAcquireNonces(uint32_t arg0, uint32_t flags) { continue; } - num_nonces++; - // Save the tag nonce (nt) - buf[i] = answer[0]; - buf[i + 1] = answer[1]; - buf[i + 2] = answer[2]; - buf[i + 3] = answer[3]; + memcpy(buf + num_nonces * 4, answer, 4); + num_nonces++; } LED_C_OFF(); LED_B_ON(); - reply_old(CMD_ACK, isOK, cuid, num_nonces - 1, buf, sizeof(buf)); + reply_old(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf)); LED_B_OFF(); if (DBGLEVEL >= 3) DbpString("AcquireNonces finished"); @@ -826,7 +824,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, if (!have_uid) { // need a full select cycle to get the uid first iso14a_card_select_t card_info; if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireNonces: Can't select card (ALL)"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireEncryptedNonces: Can't select card (ALL)"); continue; } switch (card_info.uidlen) { @@ -845,7 +843,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, have_uid = true; } else { // no need for anticollision. We can directly select the card if (!iso14443a_fast_select_card(uid, cascade_levels)) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireNonces: Can't select card (UID)"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireEncryptedNonces: Can't select card (UID)"); continue; } } @@ -855,7 +853,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint32_t nt1; if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, NULL)) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireNonces: Auth1 error"); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireEncryptedNonces: Auth1 error"); continue; } @@ -866,7 +864,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, CHK_TIMEOUT(); if (len != 4) { - if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireNonces: Auth2 error len=%d", len); + if (DBGLEVEL >= DBG_ERROR) Dbprintf("AcquireEncryptedNonces: Auth2 error len=%d", len); continue; } @@ -1015,7 +1013,8 @@ void MifareNested(uint8_t blockNo, uint8_t keyType, uint8_t targetBlockNo, uint8 } } - davg = (davg + (rtr - 1) / 2) / (rtr - 1); + if (rtr > 1) + davg = (davg + (rtr - 1) / 2) / (rtr - 1); if (DBGLEVEL >= DBG_DEBUG) Dbprintf("rtr=%d isOK=%d min=%d max=%d avg=%d, delta_time=%d", rtr, isOK, dmin, dmax, davg, delta_time); @@ -2714,8 +2713,8 @@ void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain) { // // Tear-off attack against MFU. // - Moebius et al -void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t tearoff_time, uint8_t *datain) { - uint8_t blockNo = arg0; +void MifareU_Otp_Tearoff(uint8_t blno, uint32_t tearoff_time, uint8_t *datain) { + uint8_t blockNo = blno; uint8_t data_fullwrite[4] = {0x00}; uint8_t data_testwrite[4] = {0x00}; memcpy(data_fullwrite, datain, 4); diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index 92defcaf5..757a5efbb 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -63,6 +63,6 @@ void Mifare_DES_Auth1(uint8_t arg0, uint8_t *datain); void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain); // Tear-off test for MFU -void MifareU_Otp_Tearoff(uint8_t arg0, uint32_t arg1, uint8_t *datain); +void MifareU_Otp_Tearoff(uint8_t blno, uint32_t tearoff_time, uint8_t *datain); void MifareU_Counter_Tearoff(uint8_t counter, uint32_t tearoff_time, uint8_t *datain); #endif diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 07cf7b098..e22ab64c7 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -68,9 +68,12 @@ uint8_t mf_crypto1_encrypt4bit(struct Crypto1State *pcs, uint8_t data) { // send X byte basic commands int mifare_sendcmd(uint8_t cmd, uint8_t *data, uint8_t data_size, uint8_t *answer, uint8_t *answer_parity, uint32_t *timing) { + uint8_t dcmd[data_size + 3]; dcmd[0] = cmd; - memcpy(dcmd + 1, data, data_size); + if (data_size > 0) + memcpy(dcmd + 1, data, data_size); + AddCrc14A(dcmd, data_size + 1); ReaderTransmit(dcmd, sizeof(dcmd), timing); int len = ReaderReceive(answer, answer_parity); diff --git a/armsrc/start.c b/armsrc/start.c index 70eee5063..062a83eb9 100644 --- a/armsrc/start.c +++ b/armsrc/start.c @@ -14,14 +14,16 @@ #include "proxmark3_arm.h" #include "appmain.h" +#ifndef WITH_NO_COMPRESSION #include "lz4.h" +#endif #include "BigBuf.h" #include "string.h" extern struct common_area common_area; extern char __data_src_start__, __data_start__, __data_end__, __bss_start__, __bss_end__; - +#ifndef WITH_NO_COMPRESSION static void uncompress_data_section(void) { int avail_in; memcpy(&avail_in, &__data_src_start__, sizeof(int)); @@ -35,6 +37,7 @@ static void uncompress_data_section(void) { // save the size of the compressed data section common_area.arg1 = avail_in; } +#endif void __attribute__((section(".startos"))) Vector(void); void Vector(void) { @@ -48,12 +51,20 @@ void Vector(void) { } common_area.flags.osimage_present = 1; + /* Set up data segment: Copy from flash to ram */ +#ifdef WITH_NO_COMPRESSION + char *data_src = &__data_src_start__; + char *data_dst = &__data_start__; + char *data_end = &__data_end__; + while (data_dst < data_end) *data_dst++ = *data_src++; +#else uncompress_data_section(); +#endif /* Set up (that is: clear) BSS. */ - char *dst = &__bss_start__; - char *end = &__bss_end__; - while (dst < end) *dst++ = 0; + char *bss_dst = &__bss_start__; + char *bss_end = &__bss_end__; + while (bss_dst < bss_end) *bss_dst++ = 0; AppMain(); } diff --git a/armsrc/usart.c b/armsrc/usart.c index b3ed42978..75b005b6d 100644 --- a/armsrc/usart.c +++ b/armsrc/usart.c @@ -121,7 +121,7 @@ uint16_t usart_rxdata_available(void) { uint32_t usart_read_ng(uint8_t *data, size_t len) { if (len == 0) return 0; - uint32_t nbBytesRcv = 0; + uint32_t bytes_rcv = 0; uint32_t try = 0; // uint32_t highest_observed_try = 0; // Empirical max try observed: 3000000 / USART_BAUD_RATE @@ -146,7 +146,7 @@ uint32_t usart_read_ng(uint8_t *data, size_t len) { } len -= packetSize; while (packetSize--) { - data[nbBytesRcv++] = us_rxfifo[us_rxfifo_low++]; + data[bytes_rcv++] = us_rxfifo[us_rxfifo_low++]; if (us_rxfifo_low == sizeof(us_rxfifo)) us_rxfifo_low = 0; } @@ -157,7 +157,7 @@ uint32_t usart_read_ng(uint8_t *data, size_t len) { } // highest_observed_try = MAX(highest_observed_try, try); // Dbprintf_usb("Dbg USART max observed try %i", highest_observed_try); - return nbBytesRcv; + return bytes_rcv; } // transfer from device to client diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 4ea7acf36..b7c56f881 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -32,7 +32,7 @@ find_package(PkgConfig) if (NOT SKIPQT EQUAL 1) if(APPLE AND EXISTS /usr/local/opt/qt5) # Homebrew installs Qt5 (up to at least 5.11.0) in - # /usr/local/qt5. Ensure that it can be found by CMake + # /usr/local/opt/qt5. Ensure that it can be found by CMake # since it is not in the default /usr/local prefix. # Add it to PATHS so that it doesn't override the # CMAKE_PREFIX_PATH environment variable. @@ -40,6 +40,16 @@ if (NOT SKIPQT EQUAL 1) # e.g. find_package(Qt5Core ${QT_FIND_PACKAGE_OPTIONS}) list(APPEND QT_FIND_PACKAGE_OPTIONS PATHS /usr/local/opt/qt5) endif(APPLE AND EXISTS /usr/local/opt/qt5) + if(APPLE AND EXISTS /opt/homebrew/opt/qt5) + # Homebrew on Apple Silicon installs Qt5 in + # /opt/homebrew/opt/qt5. Ensure that it can be found by CMake + # since it is not in the default /usr/local prefix. + # Add it to PATHS so that it doesn't override the + # CMAKE_PREFIX_PATH environment variable. + # QT_FIND_PACKAGE_OPTIONS should be passed to find_package, + # e.g. find_package(Qt5Core ${QT_FIND_PACKAGE_OPTIONS}) + list(APPEND QT_FIND_PACKAGE_OPTIONS PATHS /opt/homebrew/opt/qt5) + endif(APPLE AND EXISTS /opt/homebrew/opt/qt5) set(QT_PACKAGELIST Qt5Core Qt5Widgets @@ -77,12 +87,12 @@ endif (EMBED_READLINE OR EMBED_BZIP2) if (NOT SKIPREADLINE EQUAL 1) if (APPLE) - find_path(READLINE_INCLUDE_DIRS readline/readline.h /usr/local/opt/readline/include /opt/local/include /opt/include /usr/local/include /usr/include NO_DEFAULT_PATH) - find_library(READLINE_LIBRARIES readline /usr/local/opt/readline/lib /opt/local/lib /opt/lib /usr/local/lib /usr/lib NO_DEFAULT_PATH) + find_path(READLINE_INCLUDE_DIRS readline/readline.h /usr/local/opt/readline/include /opt/local/include /opt/include /usr/local/include /usr/include /opt/homebrew/opt/readline/include NO_DEFAULT_PATH) + find_library(READLINE_LIBRARIES readline /usr/local/opt/readline/lib /opt/local/lib /opt/lib /usr/local/lib /usr/lib /opt/homebrew/opt/readline/lib NO_DEFAULT_PATH) endif (APPLE) if (EMBED_READLINE) ExternalProject_Add(ncurses - URL http://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.0.tar.gz + URL http://ftp.gnu.org/pub/gnu/ncurses/ncurses-6.1.tar.gz PREFIX deps/ncurses DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/ncurses CONFIGURE_COMMAND ./configure CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} LD=${CMAKE_C_COMPILER} AR=${CMAKE_AR} RANLIB=${CMAKE_RANLIB} ${CFLAGS_EXTERNAL_LIB} --host=arm --disable-database --with-fallbacks=ansi-generic,ansi-mini,color_xterm,dtterm,dumb,Eterm,Eterm-256color,Eterm-88color,eterm-color,gnome,gnome-256color,guru,hurd,iTerm.app,konsole,konsole-16color,konsole-256color,konsole-base,konsole-linux,konsole-solaris,konsole-vt100,kterm,kterm-color,linux,linux-16color,linux-basic,mac,mlterm,mlterm-256color,mrxvt,mrxvt-256color,mterm,mterm-ansi,mvterm,nsterm,nsterm-16color,nsterm-256color,pty,putty,putty-256color,putty-vt100,rxvt,rxvt-16color,rxvt-256color,rxvt-88color,rxvt-basic,rxvt-color,screen,screen-16color,screen-256color,simpleterm,st-16color,st-256color,st52,st52-color,stv52,tt,tt52,unknown,vt100,vt102,vte,vte-256color,xterm,xterm-16color,xterm-256color,xterm-88color,xterm-basic,xterm-bold,xterm-color,xterm-utf8,xterm-vt220,xterm-vt52,xterm1,xtermc,xtermm --enable-termcap --without-ada --without-debug --without-dlsym --without-gpm --without-develop --without-tests --without-cxx-binding --with-termlib @@ -94,7 +104,7 @@ if (NOT SKIPREADLINE EQUAL 1) ExternalProject_Add_StepTargets(ncurses configure build install) ExternalProject_Add(readline - URL ftp://ftp.gnu.org/gnu/readline/readline-7.0.tar.gz + URL ftp://ftp.gnu.org/gnu/readline/readline-8.1.tar.gz PREFIX deps/readline DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/deps/readline CONFIGURE_COMMAND ./configure CC=${CMAKE_C_COMPILER} CXX=${CMAKE_CXX_COMPILER} LD=${CMAKE_C_COMPILER} AR=${CMAKE_AR} RANLIB=${CMAKE_RANLIB} ${CFLAGS_EXTERNAL_LIB} --host=arm --enable-static @@ -228,9 +238,11 @@ set (TARGET_SOURCES ${PM3_ROOT}/client/src/cmdhf15.c ${PM3_ROOT}/client/src/cmdhfcryptorf.c ${PM3_ROOT}/client/src/cmdhfepa.c + ${PM3_ROOT}/client/src/cmdhfemrtd.c ${PM3_ROOT}/client/src/cmdhffelica.c ${PM3_ROOT}/client/src/cmdhffido.c ${PM3_ROOT}/client/src/cmdhficlass.c + ${PM3_ROOT}/client/src/cmdhfjooki.c ${PM3_ROOT}/client/src/cmdhflegic.c ${PM3_ROOT}/client/src/cmdhflist.c ${PM3_ROOT}/client/src/cmdhflto.c diff --git a/client/Makefile b/client/Makefile index 9c624722c..ab26f970e 100644 --- a/client/Makefile +++ b/client/Makefile @@ -17,8 +17,7 @@ vpath %.dic dictionaries OBJDIR = obj ifeq ($(platform),Darwin) - # cf brew info qt: qt not symlinked anymore - PKG_CONFIG_ENV := PKG_CONFIG_PATH=/usr/local/opt/qt/lib/pkgconfig + PKG_CONFIG_ENV := PKG_CONFIG_PATH=$(BREW_PREFIX)/opt/qt/lib/pkgconfig endif ################### @@ -115,21 +114,6 @@ STATICLIBS += $(HARDNESTEDLIB) LDLIBS +=$(HARDNESTEDLIBLD) INCLUDES += $(HARDNESTEDLIBINC) -## Jansson -ifneq ($(SKIPJANSSONSYSTEM),1) - JANSSONINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags jansson 2>/dev/null) - JANSSONLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs jansson 2>/dev/null) - ifneq ($(JANSSONLDLIBS),) - JANSSONLIB = - JANSSONLIBLD = $(JANSSONLDLIBS) - JANSSONLIBINC = $(JANSSONINCLUDES) - JANSSON_FOUND = 1 - endif -endif -STATICLIBS += $(JANSSONLIB) -LDLIBS += $(JANSSONLIBLD) -INCLUDES += $(JANSSONLIBINC) - ## Lua ifneq ($(SKIPLUASYSTEM),1) LUAINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags lua5.2 2>/dev/null) @@ -145,6 +129,22 @@ STATICLIBS += $(LUALIB) LDLIBS += $(LUALIBLD) INCLUDES += $(LUALIBINC) +## Jansson +# Jansson section needs to be after Lua to avoid interferences on macOS if a locally incompatible Lua was available, see PR 1155 +ifneq ($(SKIPJANSSONSYSTEM),1) + JANSSONINCLUDES = $(shell $(PKG_CONFIG_ENV) pkg-config --cflags jansson 2>/dev/null) + JANSSONLDLIBS = $(shell $(PKG_CONFIG_ENV) pkg-config --libs jansson 2>/dev/null) + ifneq ($(JANSSONLDLIBS),) + JANSSONLIB = + JANSSONLIBLD = $(JANSSONLDLIBS) + JANSSONLIBINC = $(JANSSONINCLUDES) + JANSSON_FOUND = 1 + endif +endif +STATICLIBS += $(JANSSONLIB) +LDLIBS += $(JANSSONLIBLD) +INCLUDES += $(JANSSONLIBINC) + ## mbed TLS # system library cannot be used because it is compiled by default without CMAC support STATICLIBS += $(MBEDTLSLIB) @@ -278,8 +278,8 @@ CXXINCLUDES += $(QTINCLUDES) ## Readline ifneq ($(SKIPREADLINE),1) ifeq ($(platform),Darwin) - LDLIBS += -L/usr/local/opt/readline/lib - INCLUDES += -I/usr/local/opt/readline/include + LDLIBS += -L$(BREW_PREFIX)/opt/readline/lib + INCLUDES += -I$(BREW_PREFIX)/opt/readline/include endif LDLIBS += -lreadline READLINE_FOUND = 1 @@ -469,10 +469,12 @@ SRCS = aiddesfire.c \ cmdhf15.c \ cmdhfcryptorf.c \ cmdhfepa.c \ + cmdhfemrtd.c \ cmdhffelica.c \ cmdhffido.c \ cmdhficlass.c \ cmdhflegic.c \ + cmdhfjooki.c \ cmdhflist.c \ cmdhflto.c \ cmdhfmf.c \ diff --git a/client/android/CMakeLists.txt b/client/android/CMakeLists.txt index f459748b2..3f45f1073 100644 --- a/client/android/CMakeLists.txt +++ b/client/android/CMakeLists.txt @@ -106,6 +106,7 @@ add_library(pm3rrg_rdv4 SHARED ${PM3_ROOT}/client/src/cmdhf15.c ${PM3_ROOT}/client/src/cmdhfcryptorf.c ${PM3_ROOT}/client/src/cmdhfepa.c + ${PM3_ROOT}/client/src/cmdhfemrtd.c ${PM3_ROOT}/client/src/cmdhffelica.c ${PM3_ROOT}/client/src/cmdhffido.c ${PM3_ROOT}/client/src/cmdhficlass.c @@ -126,9 +127,11 @@ add_library(pm3rrg_rdv4 SHARED ${PM3_ROOT}/client/src/cmdlfawid.c ${PM3_ROOT}/client/src/cmdlfcotag.c ${PM3_ROOT}/client/src/cmdlfdestron.c - ${PM3_ROOT}/client/src/cmdlfem4x.c + ${PM3_ROOT}/client/src/cmdlfem.c + ${PM3_ROOT}/client/src/cmdlfem410x.c ${PM3_ROOT}/client/src/cmdlfem4x05.c ${PM3_ROOT}/client/src/cmdlfem4x50.c + ${PM3_ROOT}/client/src/cmdlfem4x70.c ${PM3_ROOT}/client/src/cmdlffdxb.c ${PM3_ROOT}/client/src/cmdlfgallagher.c ${PM3_ROOT}/client/src/cmdlfguard.c diff --git a/client/android/pm3_main.c b/client/android/pm3_main.c index 3d2ef917d..1fb1cc95f 100644 --- a/client/android/pm3_main.c +++ b/client/android/pm3_main.c @@ -74,13 +74,13 @@ static bool OpenPm3(void) { jint Console(JNIEnv *env, jobject instance, jstring cmd_) { if (!conn.run) { - if (OpenPm3() && TestProxmark() == PM3_SUCCESS) { + if (OpenPm3() && TestProxmark(session.current_device) == PM3_SUCCESS) { LOGD("Connected to device"); PrintAndLogEx(SUCCESS, "Connected to device"); } else { LOGD("Failed to connect to device"); PrintAndLogEx(ERR, "Failed to connect to device"); - CloseProxmark(); + CloseProxmark(session.current_device); } } @@ -110,10 +110,10 @@ jboolean IsClientRunning(JNIEnv *env, jobject instance) { * */ jboolean TestPm3(JNIEnv *env, jobject instance) { if (open() == false) { - CloseProxmark(); + CloseProxmark(session.current_device); return false; } - bool ret = (TestProxmark() == PM3_SUCCESS); + bool ret = (TestProxmark(session.current_device) == PM3_SUCCESS); return (jboolean)(ret); } @@ -121,7 +121,7 @@ jboolean TestPm3(JNIEnv *env, jobject instance) { * stop pm3 client * */ void ClosePm3(JNIEnv *env, jobject instance) { - CloseProxmark(); + CloseProxmark(session.current_device); } /* diff --git a/client/cmdscripts/rdv4_init_extflash.cmd b/client/cmdscripts/rdv4_init_extflash.cmd index 6fe6de3ba..da0447ce4 100755 --- a/client/cmdscripts/rdv4_init_extflash.cmd +++ b/client/cmdscripts/rdv4_init_extflash.cmd @@ -3,4 +3,4 @@ mem load -f mfc_default_keys --mfc mem load -f t55xx_default_pwds --t55xx mem load -f iclass_default_keys --iclass -lf t55xx deviceconfig z p +lf t55xx deviceconfig -z -p diff --git a/client/deps/amiibo.cmake b/client/deps/amiibo.cmake index 491d90d60..c946c0682 100644 --- a/client/deps/amiibo.cmake +++ b/client/deps/amiibo.cmake @@ -14,10 +14,18 @@ add_library(pm3rrg_rdv4_amiibo STATIC if (NOT TARGET pm3rrg_rdv4_mbedtls) include(mbedtls.cmake) endif() + target_link_libraries(pm3rrg_rdv4_amiibo PRIVATE m pm3rrg_rdv4_mbedtls) -target_include_directories(pm3rrg_rdv4_amiibo PRIVATE ../../include ../../common) -target_include_directories(pm3rrg_rdv4_amiibo INTERFACE amiitool) + target_compile_options(pm3rrg_rdv4_amiibo PRIVATE -Wall -Werror -O3) set_property(TARGET pm3rrg_rdv4_amiibo PROPERTY POSITION_INDEPENDENT_CODE ON) + +target_include_directories(pm3rrg_rdv4_amiibo PRIVATE amiitool + ../../common + ../../include + ../src + jansson) + +target_include_directories(pm3rrg_rdv4_amiibo INTERFACE amiitool) \ No newline at end of file diff --git a/client/deps/amiitool/Makefile b/client/deps/amiitool/Makefile index dd064227e..f72a675d8 100644 --- a/client/deps/amiitool/Makefile +++ b/client/deps/amiitool/Makefile @@ -1,5 +1,5 @@ MYSRCPATHS = -MYINCLUDES = -I. -I.. -I../jansson -I../../../common -I../../../common/mbedtls -I../../../include +MYINCLUDES = -I. -I.. -I../../../common -I../../../common/mbedtls -I../../../include -I../../src -I../../../include -I../jansson MYCFLAGS = MYDEFS = MYSRCS = \ diff --git a/client/deps/amiitool/amiibo.c b/client/deps/amiitool/amiibo.c index cbe3a0ba0..edf7eb45f 100644 --- a/client/deps/amiitool/amiibo.c +++ b/client/deps/amiitool/amiibo.c @@ -9,6 +9,7 @@ #include "md.h" #include "aes.h" #include "commonutil.h" +#include "../src/fileutils.h" #define HMAC_POS_DATA 0x008 #define HMAC_POS_TAG 0x1B4 @@ -131,24 +132,29 @@ void nfc3d_amiibo_pack(const nfc3d_amiibo_keys *amiiboKeys, const uint8_t *plain nfc3d_amiibo_internal_to_tag(cipher, tag); } -bool nfc3d_amiibo_load_keys(nfc3d_amiibo_keys *amiiboKeys, const char *path) { - FILE *f = fopen(path, "rb"); - if (!f) { +bool nfc3d_amiibo_load_keys(nfc3d_amiibo_keys *amiiboKeys) { + +#define amiboo_key_fn "key_retail.bin" + + uint8_t *dump = NULL; + size_t bytes_read = 0; + if (loadFile_safe(amiboo_key_fn, "", (void **)&dump, &bytes_read) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", amiboo_key_fn); return false; } - size_t len = fread(amiiboKeys, sizeof(*amiiboKeys), 1, f); - fclose(f); - - if (len != sizeof(*amiiboKeys)) { + if (bytes_read != sizeof(*amiiboKeys)) { + free(dump); return false; } - if ((amiiboKeys->data.magicBytesSize > 16) || - (amiiboKeys->tag.magicBytesSize > 16)) { + if ((amiiboKeys->data.magicBytesSize > 16) || (amiiboKeys->tag.magicBytesSize > 16)) { + free(dump); return false; } + memcpy(amiiboKeys, dump, bytes_read); + free(dump); return true; } diff --git a/client/deps/amiitool/amiibo.h b/client/deps/amiitool/amiibo.h index 47d544875..4ba0db7cd 100644 --- a/client/deps/amiitool/amiibo.h +++ b/client/deps/amiitool/amiibo.h @@ -25,7 +25,7 @@ typedef struct { bool nfc3d_amiibo_unpack(const nfc3d_amiibo_keys *amiiboKeys, const uint8_t *tag, uint8_t *plain); void nfc3d_amiibo_pack(const nfc3d_amiibo_keys *amiiboKeys, const uint8_t *plain, uint8_t *tag); -bool nfc3d_amiibo_load_keys(nfc3d_amiibo_keys *amiiboKeys, const char *path); +bool nfc3d_amiibo_load_keys(nfc3d_amiibo_keys *amiiboKeys); void nfc3d_amiibo_copy_app_data(const uint8_t *src, uint8_t *dst); #endif diff --git a/client/deps/amiitool/amiitool.c b/client/deps/amiitool/amiitool.c index d7cd08351..61f4abd81 100644 --- a/client/deps/amiitool/amiitool.c +++ b/client/deps/amiitool/amiitool.c @@ -33,15 +33,6 @@ void amiitool_usage(void) { ); } -static bool LoadAmiikey(nfc3d_amiibo_keys keys, char *keyfile) { - - if (!nfc3d_amiibo_load_keys(&keys, keyfile)) { - PrintAndLogEx(ERR, "Could not load keys from '%s'", keyfile); - return false; - } - return true; -} - int main(int argc, char **argv) { self = argv[0]; diff --git a/client/deps/cliparser/argtable3.c b/client/deps/cliparser/argtable3.c index 2fe32b082..783bd303b 100644 --- a/client/deps/cliparser/argtable3.c +++ b/client/deps/cliparser/argtable3.c @@ -830,7 +830,7 @@ static void arg_date_errorfn( /* make argval NULL safe */ argval = argval ? argval : ""; - fprintf(fp, "%s: ", progname); + fprintf(fp, "[!] %s: ", progname); switch (errorcode) { case EMINCOUNT: fputs("missing option ", fp); @@ -846,11 +846,11 @@ static void arg_date_errorfn( struct tm tm; char buff[200]; - fprintf(fp, "illegal timestamp format \"%s\"\n", argval); + fprintf(fp, "[!] illegal timestamp format \"%s\"\n", argval); memset(&tm, 0, sizeof(tm)); arg_strptime("1999-12-31 23:59:59", "%F %H:%M:%S", &tm); strftime(buff, sizeof(buff), parent->format, &tm); - printf("correct format is \"%s\"\n", buff); + printf("[+] correct format is \"%s\"\n", buff); break; } } @@ -1442,7 +1442,7 @@ static void arg_dbl_errorfn( /* make argval NULL safe */ argval = argval ? argval : ""; - fprintf(fp, "%s: ", progname); + fprintf(fp, "[!] %s: ", progname); switch (errorcode) { case EMINCOUNT: fputs("missing option ", fp); @@ -1455,7 +1455,7 @@ static void arg_dbl_errorfn( break; case EBADDOUBLE: - fprintf(fp, "invalid argument \"%s\" to option ", argval); + fprintf(fp, "[!] invalid argument \"%s\" to option ", argval); arg_print_option(fp, shortopts, longopts, datatype, "\n"); break; } @@ -1805,7 +1805,7 @@ static void arg_file_errorfn( /* make argval NULL safe */ argval = argval ? argval : ""; - fprintf(fp, "%s: ", progname); + fprintf(fp, "[!] %s: ", progname); switch (errorcode) { case EMINCOUNT: fputs("missing option ", fp); @@ -1818,7 +1818,7 @@ static void arg_file_errorfn( break; default: - fprintf(fp, "unknown error at \"%s\"\n", argval); + fprintf(fp, "[!] unknown error at \"%s\"\n", argval); } } @@ -2136,7 +2136,7 @@ static void arg_int_errorfn( /* make argval NULL safe */ argval = argval ? argval : ""; - fprintf(fp, "%s: ", progname); + fprintf(fp, "[!] %s: ", progname); switch (errorcode) { case EMINCOUNT: fputs("missing option ", fp); @@ -2149,7 +2149,7 @@ static void arg_int_errorfn( break; case EBADINT: - fprintf(fp, "invalid argument \"%s\" to option ", argval); + fprintf(fp, "[!] invalid argument \"%s\" to option ", argval); arg_print_option(fp, shortopts, longopts, datatype, "\n"); break; @@ -2394,7 +2394,7 @@ static void arg_u64_errorfn( /* make argval NULL safe */ argval = argval ? argval : ""; - fprintf(fp, "%s: ", progname); + fprintf(fp, "[!] %s: ", progname); switch (errorcode) { case EMINCOUNT: fputs("missing option ", fp); @@ -2407,7 +2407,7 @@ static void arg_u64_errorfn( break; case EBADINT: - fprintf(fp, "invalid argument \"%s\" to option ", argval); + fprintf(fp, "[!] invalid argument \"%s\" to option ", argval); arg_print_option(fp, shortopts, longopts, datatype, "\n"); break; @@ -2554,18 +2554,18 @@ static void arg_lit_errorfn( switch (errorcode) { case EMINCOUNT: - fprintf(fp, "%s: missing option ", progname); + fprintf(fp, "[!] %s: missing option ", progname); arg_print_option(fp, shortopts, longopts, datatype, "\n"); fprintf(fp, "\n"); break; case EMAXCOUNT: - fprintf(fp, "%s: extraneous option ", progname); + fprintf(fp, "[!] %s: extraneous option ", progname); arg_print_option(fp, shortopts, longopts, datatype, "\n"); break; } - ARG_TRACE(("%s:errorfn(%p, %p, %d, %s, %s)\n", __FILE__, parent, fp, + ARG_TRACE(("[!] %s:errorfn(%p, %p, %d, %s, %s)\n", __FILE__, parent, fp, errorcode, argval, progname)); } @@ -2859,7 +2859,7 @@ static void arg_rex_errorfn(struct arg_rex *parent, /* make argval NULL safe */ argval = argval ? argval : ""; - fprintf(fp, "%s: ", progname); + fprintf(fp, "[!] %s: ", progname); switch (errorcode) { case EMINCOUNT: fputs("missing option ", fp); @@ -3790,7 +3790,7 @@ static void arg_str_errorfn( /* make argval NULL safe */ argval = argval ? argval : ""; - fprintf(fp, "%s: ", progname); + fprintf(fp, "[!] %s: ", progname); switch (errorcode) { case EMINCOUNT: fputs("missing option ", fp); diff --git a/client/deps/cliparser/cliparser.c b/client/deps/cliparser/cliparser.c index f55b7a8f7..f3f3c581e 100644 --- a/client/deps/cliparser/cliparser.c +++ b/client/deps/cliparser/cliparser.c @@ -118,7 +118,7 @@ int CLIParserParseArg(CLIParserContext *ctx, int argc, char **argv, void *vargta if (nerrors > 0) { /* Display the error details contained in the arg_end struct.*/ arg_print_errors(stdout, ((struct arg_end *)(ctx->argtable)[vargtableLen - 1]), ctx->programName); - PrintAndLogEx(WARNING, "Try '%s --help' for more information.\n", ctx->programName); + PrintAndLogEx(WARNING, "Try " _YELLOW_("'%s --help'") " for more information.\n", ctx->programName); fflush(stdout); return 3; } @@ -269,15 +269,19 @@ int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int return 0; } +// hexstr -> u64, w optional len input and default value fallback. +// 0 = failed +// 1 = OK +// 3 = optional param - not set uint64_t arg_get_u64_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint64_t def) { uint64_t rv = 0; - uint8_t data[8]; - int datalen = 0; - int res = CLIParamHexToBuf(arg_get_str(ctx, paramnum), data, sizeof(data), &datalen); - if (res == 0 && datalen > 0) { - for (uint8_t i = 0; i < datalen; i++) { + uint8_t d[8]; + int dlen = 0; + int res = CLIParamHexToBuf(arg_get_str(ctx, paramnum), d, sizeof(d), &dlen); + if (res == 0 && dlen > 0) { + for (uint8_t i = 0; i < dlen; i++) { rv <<= 8; - rv |= data[i]; + rv |= d[i]; } } else { rv = def; @@ -285,4 +289,56 @@ uint64_t arg_get_u64_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint64_ return rv; } +// hexstr -> u64, w optional len input and default value fallback. +// 0 = failed +// 1 = OK +// 2 = wrong len param, use default +// 3 = optional param, if fail, use default. +int arg_get_u64_hexstr_def_nlen(CLIParserContext *ctx, uint8_t paramnum, uint64_t def, uint64_t *out, uint8_t nlen, bool optional) { + int n = 0; + uint8_t d[nlen]; + int res = CLIParamHexToBuf(arg_get_str(ctx, paramnum), d, sizeof(d), &n); + if (res == 0 && n == nlen) { + uint64_t rv = 0; + for (uint8_t i = 0; i < n; i++) { + rv <<= 8; + rv |= d[i]; + } + *out = rv; + return 1; + } else if (res == 0 && n) { + *out = def; + return 2; + } else if (res == 0 && n == 0 && optional) { + *out = def; + return 3; + } + return 0; +} + +int arg_get_u32_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint32_t def, uint32_t *out) { + return arg_get_u32_hexstr_def_nlen(ctx, paramnum, def, out, 4, false); +} + +int arg_get_u32_hexstr_def_nlen(CLIParserContext *ctx, uint8_t paramnum, uint32_t def, uint32_t *out, uint8_t nlen, bool optional) { + int n = 0; + uint8_t d[nlen]; + int res = CLIParamHexToBuf(arg_get_str(ctx, paramnum), d, sizeof(d), &n); + if (res == 0 && n == nlen) { + uint32_t rv = 0; + for (uint8_t i = 0; i < n; i++) { + rv <<= 8; + rv |= d[i]; + } + *out = rv; + return 1; + } else if (res == 0 && n) { + *out = def; + return 2; + } else if (res == 0 && n == 0 && optional) { + *out = def; + return 3; + } + return 0; +} diff --git a/client/deps/cliparser/cliparser.h b/client/deps/cliparser/cliparser.h index 996ad81ef..78bb59f32 100644 --- a/client/deps/cliparser/cliparser.h +++ b/client/deps/cliparser/cliparser.h @@ -68,4 +68,13 @@ int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen); uint64_t arg_get_u64_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint64_t def); +int arg_get_u64_hexstr_def_nlen(CLIParserContext *ctx, uint8_t paramnum, uint64_t def, uint64_t *out, uint8_t nlen, bool optional); +int arg_get_u32_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint32_t def, uint32_t *out); +int arg_get_u32_hexstr_def_nlen(CLIParserContext *ctx, uint8_t paramnum, uint32_t def, uint32_t *out, uint8_t nlen, bool optional); + +#define CP_SUCCESS_OPTIONAL 1 +#define CP_SUCCESS 0 +#define CP_ENOPARAM -1 +#define CP_WRONGLEN -2 + #endif diff --git a/client/deps/hardnested/hardnested_bf_core.c b/client/deps/hardnested/hardnested_bf_core.c index 719578331..0f9eb3da8 100644 --- a/client/deps/hardnested/hardnested_bf_core.c +++ b/client/deps/hardnested/hardnested_bf_core.c @@ -143,7 +143,7 @@ bitslice_test_nonces_t bitslice_test_nonces_NOSIMD; bitslice_test_nonces_t bitslice_test_nonces_dispatch; #if defined (_WIN32) -#define malloc_bitslice(x) __builtin_assume_aligned(_aligned_malloc((x), MAX_BITSLICES/8), MAX_BITSLICES/8) +#define malloc_bitslice(x) __builtin_assume_aligned(_aligned_malloc((x), MAX_BITSLICES / 8), MAX_BITSLICES / 8) #define free_bitslice(x) _aligned_free(x) #elif defined (__APPLE__) static void *malloc_bitslice(size_t x) { @@ -156,7 +156,7 @@ static void *malloc_bitslice(size_t x) { } #define free_bitslice(x) free(x) #else -#define malloc_bitslice(x) memalign(MAX_BITSLICES/8, (x)) +#define malloc_bitslice(x) memalign(MAX_BITSLICES / 8, (x)) #define free_bitslice(x) free(x) #endif @@ -559,6 +559,10 @@ void SetSIMDInstr(SIMDExecInstr instr) { static SIMDExecInstr GetSIMDInstr(void) { SIMDExecInstr instr; +#if defined(COMPILER_HAS_SIMD) + __builtin_cpu_init(); +#endif + #if defined(COMPILER_HAS_SIMD_AVX512) if (__builtin_cpu_supports("avx512f")) instr = SIMD_AVX512; diff --git a/client/deps/hardnested/hardnested_bruteforce.c b/client/deps/hardnested/hardnested_bruteforce.c index 0c768f400..a589040ac 100644 --- a/client/deps/hardnested/hardnested_bruteforce.c +++ b/client/deps/hardnested/hardnested_bruteforce.c @@ -465,7 +465,8 @@ float brute_force_benchmark(void) { 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; } diff --git a/client/deps/hardnested/hardnested_bruteforce.h b/client/deps/hardnested/hardnested_bruteforce.h index 8766362d7..a8e6786d5 100644 --- a/client/deps/hardnested/hardnested_bruteforce.h +++ b/client/deps/hardnested/hardnested_bruteforce.h @@ -25,7 +25,7 @@ typedef struct guess_sum_a8 { float prob; uint64_t num_states; - uint8_t sum_a8_idx; + uint16_t sum_a8_idx; } guess_sum_a8_t; typedef struct noncelistentry { @@ -40,7 +40,7 @@ typedef struct noncelist { guess_sum_a8_t sum_a8_guess[NUM_SUMS]; bool sum_a8_guess_dirty; float expected_num_brute_force; - uint8_t BitFlips[0x400]; + uint16_t BitFlips[0x400]; uint32_t *states_bitarray[2]; uint32_t num_states_bitarray[2]; bool all_bitflips_dirty[2]; diff --git a/client/deps/hardnested/hardnested_tables.c b/client/deps/hardnested/hardnested_tables.c index d3c620fe5..f0155946f 100644 --- a/client/deps/hardnested/hardnested_tables.c +++ b/client/deps/hardnested/hardnested_tables.c @@ -372,10 +372,11 @@ static void precalculate_bit0_bitflip_bitarrays(uint8_t const bitflip, uint16_t for (odd_even_t odd_even = EVEN_STATE; odd_even <= ODD_STATE; odd_even++) { count[odd_even] = count_states(test_bitarray[odd_even]); if (count[odd_even] != 1 << 24) { - printf("Writing %u possible %s states for bitflip property %03x (%d (%1.2f%%) states eliminated)\n", + printf("Writing %u possible %s states for bitflip property %03x (%u (%1.2f%%) states eliminated)\n", count[odd_even], odd_even == EVEN_STATE ? "even" : "odd", - bitflip, (1 << 24) - count[odd_even], + bitflip, + (1 << 24) - count[odd_even], (float)((1 << 24) - count[odd_even]) / (1 << 24) * 100.0); #ifndef TEST_RUN write_bitflips_file(odd_even, bitflip, sum_a0, test_bitarray[odd_even], count[odd_even]); @@ -399,10 +400,11 @@ static void precalculate_bit0_bitflip_bitarrays(uint8_t const bitflip, uint16_t } count[odd_even] = count_states(test_bitarray_2nd); if (count[odd_even] != 1 << 24) { - printf("Writing %u possible %s states for bitflip property %03x (%d (%1.2f%%) states eliminated)\n", + printf("Writing %u possible %s states for bitflip property %03x (%u (%1.2f%%) states eliminated)\n", count[odd_even], odd_even == EVEN_STATE ? "even" : "odd", - bitflip | BITFLIP_2ND_BYTE, (1 << 24) - count[odd_even], + bitflip | BITFLIP_2ND_BYTE, + (1 << 24) - count[odd_even], (float)((1 << 24) - count[odd_even]) / (1 << 24) * 100.0); #ifndef TEST_RUN write_bitflips_file(odd_even, bitflip | BITFLIP_2ND_BYTE, sum_a0, test_bitarray_2nd, count[odd_even]); @@ -484,10 +486,11 @@ static void precalculate_bit0_bitflip_bitarrays(uint8_t const bitflip, uint16_t for (odd_even_t odd_even = EVEN_STATE; odd_even <= ODD_STATE; odd_even++) { count[odd_even] = count_states(test_not_bitarray[odd_even]); if (count[odd_even] != 1 << 24) { - printf("Writing %u possible %s states for bitflip property %03x (%d (%1.2f%%) states eliminated)\n", + printf("Writing %u possible %s states for bitflip property %03x (%u (%1.2f%%) states eliminated)\n", count[odd_even], odd_even == EVEN_STATE ? "even" : "odd", - bitflip | 0x100, (1 << 24) - count[odd_even], + bitflip | 0x100, + (1 << 24) - count[odd_even], (float)((1 << 24) - count[odd_even]) / (1 << 24) * 100.0); #ifndef TEST_RUN write_bitflips_file(odd_even, bitflip | 0x100, sum_a0, test_not_bitarray[odd_even], count[odd_even]); @@ -511,10 +514,11 @@ static void precalculate_bit0_bitflip_bitarrays(uint8_t const bitflip, uint16_t } count[odd_even] = count_states(test_bitarray_2nd); if (count[odd_even] != 1 << 24) { - printf("Writing %u possible %s states for bitflip property %03x (%d (%1.2f%%) states eliminated)\n", + printf("Writing %u possible %s states for bitflip property %03x (%u (%1.2f%%) states eliminated)\n", count[odd_even], odd_even == EVEN_STATE ? "even" : "odd", - bitflip | 0x100 | BITFLIP_2ND_BYTE, (1 << 24) - count[odd_even], + bitflip | 0x100 | BITFLIP_2ND_BYTE, + (1 << 24) - count[odd_even], (float)((1 << 24) - count[odd_even]) / (1 << 24) * 100.0); #ifndef TEST_RUN write_bitflips_file(odd_even, bitflip | 0x100 | BITFLIP_2ND_BYTE, sum_a0, test_bitarray_2nd, count[odd_even]); @@ -532,7 +536,6 @@ static void precalculate_bit0_bitflip_bitarrays(uint8_t const bitflip, uint16_t free_bitarray(test_not_bitarray[EVEN_STATE]); free_bitarray(test_bitarray[ODD_STATE]); free_bitarray(test_bitarray[EVEN_STATE]); - exit(0); } diff --git a/client/dictionaries/mfc_default_keys.dic b/client/dictionaries/mfc_default_keys.dic index 9f716608a..21319bc3e 100644 --- a/client/dictionaries/mfc_default_keys.dic +++ b/client/dictionaries/mfc_default_keys.dic @@ -1283,4 +1283,13 @@ AABAFFCC7612 17D071403C20 # 534F4C415249 -534f4c303232 \ No newline at end of file +534f4c303232 +# +# Nespresso, smart card +# key-gen algo, these keys are for one card +ff9a84635bd2 +6f30126ee7e4 +6039abb101bb +f1a1239a4487 +# +b882fd4a9f78 diff --git a/client/dictionaries/mfdes_default_keys.dic b/client/dictionaries/mfdes_default_keys.dic index 4c863c3f5..a26d13d32 100644 --- a/client/dictionaries/mfdes_default_keys.dic +++ b/client/dictionaries/mfdes_default_keys.dic @@ -8,6 +8,7 @@ 43464F494D48504E4C4359454E528841 #NHIF 6AC292FAA1315B4D858AB3A3D7D5933A 404142434445464748494a4b4c4d4e4f +3112B738D8862CCD34302EB299AAB456 # Gallagher AES (https://pastebin.com/GkbGLz8r) 00112233445566778899aabbccddeeff 2b7e151628aed2a6abf7158809cf4f3c fbeed618357133667c85e08f7236a8de @@ -43,4 +44,4 @@ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee 404142434445464748494a4b4c4d4e4f 303132333435363738393a3b3c3d3e3f 9CABF398358405AE2F0E2B3D31C99A8A # Default key -605F5E5D5C5B5A59605F5E5D5C5B5A59 # access control \ No newline at end of file +605F5E5D5C5B5A59605F5E5D5C5B5A59 # access control diff --git a/client/dictionaries/t55xx_default_pwds.dic b/client/dictionaries/t55xx_default_pwds.dic index 348c79e0d..eaff04048 100644 --- a/client/dictionaries/t55xx_default_pwds.dic +++ b/client/dictionaries/t55xx_default_pwds.dic @@ -27,6 +27,8 @@ A5B4C3D2 00434343 44B44CAE 88661858 +# MKF fobs +E9920427 # paxton bullit? 575F4F4B # diff --git a/client/gen_pm3mfsim_script.sh b/client/gen_pm3mfsim_script.sh index c3726d69c..6947c9bda 100644 --- a/client/gen_pm3mfsim_script.sh +++ b/client/gen_pm3mfsim_script.sh @@ -32,4 +32,4 @@ rm $2 echo "hf mf eclr" >> $2 echo "hf mf eload" $1 >> $2 echo "hf mf ekeyprn" >> $2 -echo "hf mf sim u" `cat $1.eml | (read -n 8 uid; echo $uid)` >> $2 \ No newline at end of file +echo "hf mf sim -u" `cat $1.eml | (read -n 8 uid; echo $uid)` >> $2 diff --git a/client/luascripts/data_mf_bin2eml.lua b/client/luascripts/data_mf_bin2eml.lua index ef48ead16..5f9354e55 100644 --- a/client/luascripts/data_mf_bin2eml.lua +++ b/client/luascripts/data_mf_bin2eml.lua @@ -6,7 +6,7 @@ local ansicolors = require('ansicolors') copyright = '' author = 'Martin Holst Swende' -version = 'v1.0.2' +version = 'v1.0.3' desc = [[ This script takes a dumpfile from 'hf mf dump' and converts it to a format that can be used by the emulator @@ -46,7 +46,7 @@ end --- -- This is only meant to be used when errors occur local function oops(err) - print('ERROR:', err) + print('[!!] ERROR:', err) core.clearCommandBuffer() return nil, err end @@ -115,8 +115,7 @@ local function main(args) local dumpdata = readdump(infile) -- The hex-data is now in ascii-format, - if dumpdata == NIL then return oops('Dumpfle not loaded') end - + if dumpdata == nil then return oops('Dumpfle not loaded') end -- But first, check the uid local uid = string.sub(dumpdata, 1, 8) @@ -124,8 +123,7 @@ local function main(args) -- Format some linebreaks dumpdata = convert_to_emulform(dumpdata) - if dumpdata == NIL then return oops('Dumpfle not loaded') end - + if dumpdata == nil then return oops('Dumpfle not loaded') end local outfile = io.open(output, 'w') if outfile == nil then @@ -134,7 +132,7 @@ local function main(args) outfile:write(dumpdata:lower()) io.close(outfile) - print(('Wrote an emulator-dump to the file %s'):format(output)) + print(('[+] Wrote an emulator-dump to the file %s'):format(output)) end diff --git a/client/luascripts/data_mf_bin2html.lua b/client/luascripts/data_mf_bin2html.lua index c33b4782b..f412a35e7 100644 --- a/client/luascripts/data_mf_bin2html.lua +++ b/client/luascripts/data_mf_bin2html.lua @@ -7,7 +7,7 @@ local ansicolors = require('ansicolors') copyright = '' author = 'Martin Holst Swende' -version = 'v1.0.2' +version = 'v1.0.3' desc =[[ This script takes a dumpfile and produces a html based dump, which is a bit more easily analyzed. @@ -45,7 +45,7 @@ end --- -- This is only meant to be used when errors occur local function oops(err) - print('ERROR:', err) + print('[!!] ERROR:', err) core.clearCommandBuffer() return nil, err end @@ -76,7 +76,7 @@ local function main(args) local filename, err = dumplib.convert_bin_to_html(input,output, 16) if err then return oops(err) end - print(('Wrote a HTML dump to the file %s'):format(filename)) + print(('[+] Wrote a HTML dump to the file %s'):format(filename)) end --[[ diff --git a/client/luascripts/data_mf_eml2bin.lua b/client/luascripts/data_mf_eml2bin.lua index d31b58418..4291f13b9 100644 --- a/client/luascripts/data_mf_eml2bin.lua +++ b/client/luascripts/data_mf_eml2bin.lua @@ -5,7 +5,7 @@ local ansicolors = require('ansicolors') copyright = '' author = 'Iceman' -version = 'v1.0.2' +version = 'v1.0.3' desc =[[ This script takes an dumpfile in EML (ASCII) format and converts it to the PM3 dumpbin file to be used with `hf mf restore` ]] @@ -40,7 +40,7 @@ end --- -- This is only meant to be used when errors occur local function oops(err) - print('ERROR:', err) + print('[!!] ERROR:', err) core.clearCommandBuffer() return nil, err end @@ -82,7 +82,7 @@ local function main(args) local filename, err = dumplib.convert_eml_to_bin(input,output) if err then return oops(err) end - ExitMsg(('Wrote a BIN dump to the file %s'):format(filename)) + ExitMsg(('[+] Wrote a BIN dump to the file %s'):format(filename)) end main(args) diff --git a/client/luascripts/data_mf_eml2html.lua b/client/luascripts/data_mf_eml2html.lua index 9b2edbe87..a77e7a6d3 100644 --- a/client/luascripts/data_mf_eml2html.lua +++ b/client/luascripts/data_mf_eml2html.lua @@ -7,7 +7,7 @@ local ansicolors = require('ansicolors') copyright = '' author = 'Martin Holst Swende' -version = 'v1.0.2' +version = 'v1.0.3' desc = [[ This script takes a dumpfile on EML (ASCII) format and produces a html based dump, which is a bit more easily analyzed. @@ -44,7 +44,7 @@ end --- -- This is only meant to be used when errors occur local function oops(err) - print('ERROR:', err) + print('[!!] ERROR:', err) core.clearCommandBuffer() return nil, err end @@ -75,7 +75,7 @@ local function main(args) local filename, err = dumplib.convert_eml_to_html(input,output) if err then return oops(err) end - print(('Wrote a HTML dump to the file %s'):format(filename)) + print(('[+] Wrote a HTML dump to the file %s'):format(filename)) end --[[ diff --git a/client/luascripts/hf_mfu_dumptoemulator.lua b/client/luascripts/data_mfu_bin2eml.lua similarity index 94% rename from client/luascripts/hf_mfu_dumptoemulator.lua rename to client/luascripts/data_mfu_bin2eml.lua index 47f52e135..eeece3b7c 100644 --- a/client/luascripts/hf_mfu_dumptoemulator.lua +++ b/client/luascripts/data_mfu_bin2eml.lua @@ -6,16 +6,16 @@ local ansicolors = require('ansicolors') copyright = '' author = "Martin Holst Swende \n @Marshmellow \n @iceman" -version = 'v1.0.2' +version = 'v1.0.4' desc =[[ This script takes a dumpfile from 'hf mfu dump' and converts it to a format that can be used by the emulator ]] example = [[ - script run hf_mfu_dumptoemulator -i dumpdata-foobar.bin + script run data_mfu_bin2eml -i dumpdata-foobar.bin ]] usage = [[ -script run hf_mfu_dumptoemulator [-i ] [-o ] +script run data_mfu_bin2eml [-i ] [-o ] ]] arguments = [[ -h This help @@ -43,7 +43,7 @@ end --- -- This is only meant to be used when errors occur local function oops(err) - print('ERROR:', err) + print('[!!] ERROR:', err) core.clearCommandBuffer() return nil, err end @@ -133,7 +133,7 @@ local function main(args) outfile:write(dumpdata:lower()) io.close(outfile) - print(('Wrote an emulator-dump to the file %s'):format(output)) + print(('[+] Wrote an emulator-dump to the file %s'):format(output)) end --[[ diff --git a/client/luascripts/hf_legic.lua b/client/luascripts/hf_legic.lua index 1715bf400..ca06ae8c5 100644 --- a/client/luascripts/hf_legic.lua +++ b/client/luascripts/hf_legic.lua @@ -582,14 +582,14 @@ function writeToTag(tag) -- write pm3-buffer to Tag for i=1, WriteBytes do if (i > 7) then - cmd = ("hf legic wrbl o %02x d %s "):format(i-1, padString(bytes[i])) + cmd = ("hf legic wrbl -o %d -d %s "):format(i-1, padString(bytes[i])) print(acgreen..cmd..acoff) core.console(cmd) core.clearCommandBuffer() elseif (i == 7) then if (writeDCF) then -- write DCF in reverse order (requires 'mosci-patch') - cmd = ('hf legic wrbl o 05 d %s%s'):format(padString(bytes[i-1]), padString(bytes[i])) + cmd = ('hf legic wrbl -o 5 -d %s%s'):format(padString(bytes[i-1]), padString(bytes[i])) print(acgreen..cmd..acoff) core.console(cmd) core.clearCommandBuffer() @@ -704,13 +704,32 @@ function writeFile(bytes, filename) return true end +function getRandomTempName() + local upperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + local lowerCase = "abcdefghijklmnopqrstuvwxyz" + + local characterSet = upperCase .. lowerCase + + local keyLength = 8 + local output = "" + + for i = 1, keyLength do + local rand = math.random(#characterSet) + output = output .. string.sub(characterSet, rand, rand) + end + + output = "hf-legic-temp-" .. output + + return output +end + --- -- read from pm3 into virtual-tag function readFromPM3() local tag, bytes, infile --infile="legic.temp" - infile=os.tmpname() - core.console("hf legic dump f "..infile) + infile=getRandomTempName() + core.console("hf legic dump -f "..infile) tag=readFile(infile..".bin") os.remove(infile) os.remove(infile..".bin") diff --git a/client/luascripts/hf_legic_clone.lua b/client/luascripts/hf_legic_clone.lua index 28a95bca7..757d65fa0 100644 --- a/client/luascripts/hf_legic_clone.lua +++ b/client/luascripts/hf_legic_clone.lua @@ -456,7 +456,7 @@ local function writeToTag(plainBytes) -- write data to file if (writeOutputBytes(bytes, "hf-legic-UID-dump.bin")) then -- write pm3-buffer to Tag - cmd = ('hf legic restore f hf-legic-UID-dump') + cmd = ('hf legic restore -f hf-legic-UID-dump') core.console(cmd) end end @@ -530,13 +530,13 @@ local function main(args) res = res .."\ncreated clone_dump from\n\t"..infile.." crc: "..oldcrc.."\ndump_file:" res = res .."\n\t"..outfile.." crc: "..string.sub(newcrc, -2) res = res .."\nyou may load the new file with:" - res = res ..ansicolors.yellow.."hf legic eload f "..outfile..ansicolors.reset + res = res ..ansicolors.yellow.."hf legic eload -f "..outfile..ansicolors.reset res = res .."\n\nif you don't write to tag immediately ('-w' switch) you will need to recalculate each segmentCRC" res = res .."\nafter writing this dump to a tag!" res = res .."\n\na segmentCRC gets calculated over MCD,MSN0..3, Segment-Header0..3" res = res .."\ne.g. (based on Segment00 of the data from "..infile.."):" res = res .."\n" - res = res ..ansicolors.yellow.."hf legic crc d "..bytes[1]..bytes[2]..bytes[3]..bytes[4]..bytes[23]..bytes[24]..bytes[25]..bytes[26].." u "..newcrc.." c 8"..ansicolors.reset + res = res ..ansicolors.yellow.."hf legic crc -d "..bytes[1]..bytes[2]..bytes[3]..bytes[4]..bytes[23]..bytes[24]..bytes[25]..bytes[26].." --mcc "..newcrc.." -t 8"..ansicolors.reset -- this can not be calculated without knowing the new MCD, MSN0..2 print(res) end diff --git a/client/luascripts/hf_mf_autopwn.lua b/client/luascripts/hf_mf_autopwn.lua index f7ea2a832..029d16d7a 100644 --- a/client/luascripts/hf_mf_autopwn.lua +++ b/client/luascripts/hf_mf_autopwn.lua @@ -99,7 +99,7 @@ local function nested(key,sak) else print("I don't know how many sectors there are on this type of card, defaulting to 16") end - local cmd = string.format('hf mf nested %d 0 A %s d', typ, key) + local cmd = string.format('hf mf nested -t %d -b 0 --keya -k %s --dumpkeys', typ, key) core.console(cmd) end @@ -123,7 +123,7 @@ local function dump_tag(uid, numsectors) local dumpfile = 'hf-mf-'..uid..'-dump' - local dmp = ('hf mf dump %s f %s'):format(typ, dumpfile) + local dmp = ('hf mf dump -t %s -f %s'):format(typ, dumpfile) core.console(dmp) -- Save the global args, those are *our* arguments diff --git a/client/luascripts/hf_mf_format.lua b/client/luascripts/hf_mf_format.lua index 0bde3c2d1..7aed6b757 100644 --- a/client/luascripts/hf_mf_format.lua +++ b/client/luascripts/hf_mf_format.lua @@ -14,7 +14,7 @@ This script will generate 'hf mf wrbl' commands for each block to format a Mifar Alla datablocks gets 0x00 As default the script sets the keys A/B to 0xFFFFFFFFFFFF and the access bytes will become 0x78,0x77,0x88 -The GDB will become 0x00 +The GPB will become 0x00 The script will skip the manufactoring block 0. ]] @@ -169,7 +169,7 @@ local function main(args) GetCardInfo() -- Show info - print( string.format('Estimating number of blocks: %d', numBlocks)) + print( string.format('Estimating number of blocks: %d', numBlocks + 1)) print( string.format('Old key: %s', OldKey)) print( string.format('New key: %s', NewKey)) print( string.format('New Access: %s', Accessbytes)) diff --git a/client/luascripts/hf_mf_tnp3_dump.lua b/client/luascripts/hf_mf_tnp3_dump.lua index ee59046eb..54a19da82 100644 --- a/client/luascripts/hf_mf_tnp3_dump.lua +++ b/client/luascripts/hf_mf_tnp3_dump.lua @@ -147,7 +147,7 @@ local function main(args) --Trying to find the other keys if useNested then - core.console( ('hf mf nested 1 0 A %s d'):format(keyA) ) + core.console( ('hf mf nested -t 1 -b 0 --keya -k %s --dumpkeys'):format(keyA) ) end core.clearCommandBuffer() diff --git a/client/luascripts/hf_mf_tnp3_sim.lua b/client/luascripts/hf_mf_tnp3_sim.lua index dded9327a..33f82b2d4 100644 --- a/client/luascripts/hf_mf_tnp3_sim.lua +++ b/client/luascripts/hf_mf_tnp3_sim.lua @@ -496,7 +496,7 @@ local function main(args) err = LoadEmulator(uid, blocks) if err then return oops(err) end core.clearCommandBuffer() - print('The simulation is now prepared.\n --> run \"hf mf sim u '..uid..'\" <--') + print('The simulation is now prepared.\n --> run \"hf mf sim -u '..uid..'\" <--') end end main(args) diff --git a/client/luascripts/hf_mf_uidbruteforce.lua b/client/luascripts/hf_mf_uidbruteforce.lua index fc85b63bb..548898606 100644 --- a/client/luascripts/hf_mf_uidbruteforce.lua +++ b/client/luascripts/hf_mf_uidbruteforce.lua @@ -99,10 +99,10 @@ local function main(args) local command = '' if mftype == 'mfc' then - command = 'hf 14a sim t 1 u %014x' + command = 'hf 14a sim -t 1 -u %014x' msg('Bruteforcing Mifare Classic card numbers') elseif mftype == 'mfu' then - command = 'hf 14a sim t 2 u %014x' + command = 'hf 14a sim -t 2 -u %014x' msg('Bruteforcing Mifare Ultralight card numbers') else return print(usage) diff --git a/client/luascripts/hf_mfu_magicwrite.lua b/client/luascripts/hf_mfu_magicwrite.lua index 2961bd54c..c01f043fe 100644 --- a/client/luascripts/hf_mfu_magicwrite.lua +++ b/client/luascripts/hf_mfu_magicwrite.lua @@ -44,8 +44,8 @@ arguments = [[ -c read magic configuration -u UID (14 hexsymbols), set UID on tag -t tag type to impersonate - 1 = UL_EV1 48k - 2 = UL_EV1 128k + 1 = UL EV1 48b + 2 = UL EV1 128b 3 = NTAG 210 4 = NTAG 212 5 = NTAG 213 (true) @@ -60,7 +60,7 @@ arguments = [[ -p password (8 hexsymbols), set password on tag. -a pack ( 4 hexsymbols), set pack on tag. -s signature data (64 hexsymbols), set signature data on tag. - -o OTP data (8 hexsymbols), set one-time-pad data on tag. + -o OTP data (8 hexsymbols), set `One-Time Programmable` data on tag. -v version data (16 hexsymbols), set version data on tag. -w wipe tag. You can specify password if the tag has been locked down. Fills tag with zeros and put default values for NTAG213 (like -t 5) -k pwd to use with the wipe option diff --git a/client/luascripts/hf_mfu_setuid.lua b/client/luascripts/hf_mfu_setuid.lua index 86bef09c3..921563ed6 100644 --- a/client/luascripts/hf_mfu_setuid.lua +++ b/client/luascripts/hf_mfu_setuid.lua @@ -4,26 +4,32 @@ local ansicolors = require('ansicolors') copyright = '' author = "Iceman" -version = 'v1.0.2' +version = 'v1.0.3' desc = [[ This script tries to set UID on a mifare Ultralight magic card which either - answers to chinese backdoor commands - brickable magic tag (must write in one session) + + It defaults to GEN1A type of uid changeable card. ]] example = [[ - -- backdoor magic tag + -- backdoor magic tag (gen1a) script run hf_mfu_setuid -u 11223344556677 - -- brickable magic tag + -- backdoor magic tag (gen1b) script run hf_mfu_setuid -b -u 11223344556677 + + -- brickable magic tag (gen2) + script run hf_mfu_setuid -2 -u 11223344556677 ]] usage = [[ -script run hf_mfu_setuid [-h] [-b] [-u ] +script run hf_mfu_setuid [-h] [-b] [-2] [-u ] ]] arguments = [[ -h : this help -u : UID (14 hexsymbols) - -b : write to brickable magic tag + -b : write to magic tag GEN1B + -2 : write to brickable magic tag GEN2 ]] local DEBUG = true @@ -65,23 +71,33 @@ local function help() end -- --- Set UID on magic command enabled -function magicUID(b0, b1, b2) +function magicUID(b0, b1, b2, isgen1a) - print('Using backdoor Magic tag function') + if isgen1a then + print('Using backdoor Magic tag (gen1a) function') + else + print('Using backdoor Magic tag (gen1b) function') + end -- write block 0 core.console('hf 14a raw -k -a -b 7 40') - core.console('hf 14a raw -k -a 43') + if isgen1a then + core.console('hf 14a raw -k -a 43') + end core.console('hf 14a raw -c -a A200'..b0) -- write block 1 core.console('hf 14a raw -k -a -b 7 40') - core.console('hf 14a raw -k -a 43') + if isgen1a then + core.console('hf 14a raw -k -a 43') + end core.console('hf 14a raw -c -a A201'..b1) -- write block 2 core.console('hf 14a raw -k -a -b 7 40') - core.console('hf 14a raw -k -a 43') + if isgen1a then + core.console('hf 14a raw -k -a 43') + end core.console('hf 14a raw -c -a A202'..b2) end -- @@ -113,10 +129,11 @@ function main(args) local tagtype = 1 -- Read the parameters - for o, a in getopt.getopt(args, 'hu:b') do + for o, a in getopt.getopt(args, 'hu:b2') do if o == 'h' then return help() end if o == 'u' then uid = a end if o == 'b' then tagtype = 2 end + if o == '2' then tagtype = 3 end end -- uid string checks @@ -137,10 +154,11 @@ function main(args) core.clearCommandBuffer() - if tagtype == 2 then + if tagtype == 3 then brickableUID(block0, block1, block2) else - magicUID(block0, block1, block2) + local is_gen1a = (tagtype == 1) + magicUID(block0, block1, block2, is_gen1a) end --halt diff --git a/client/luascripts/init_rdv4.lua b/client/luascripts/init_rdv4.lua index a724ed189..08574a2fe 100644 --- a/client/luascripts/init_rdv4.lua +++ b/client/luascripts/init_rdv4.lua @@ -1,9 +1,10 @@ local getopt = require('getopt') local ansicolors = require('ansicolors') +local utils = require('utils') copyright = 'Copyright (c) 2019 IceSQL AB. All rights reserved.' author = 'Christian Herrmann' -version = 'v1.0.1' +version = 'v1.0.3' desc = [[ This script initialize a Proxmark3 RDV4.0 with - uploading dictionary files to flashmem @@ -82,10 +83,14 @@ function main(args) -- T55x7 Device configuration print('Configure T55XX device side to match RDV4') print(dash) - core.console('lf t55xx deviceconfig r 0 a 29 b 17 c 15 d 47 e 15 p') - core.console('lf t55xx deviceconfig r 1 a 29 b 17 c 18 d 50 e 15 p') - core.console('lf t55xx deviceconfig r 2 a 29 b 17 c 18 d 40 e 15 p') - core.console('lf t55xx deviceconfig r 3 a 29 b 17 c 15 d 31 e 15 f 47 g 63 p') + core.console('lf t55xx deviceconfig --r0 -a 29 -b 17 -c 15 -d 47 -e 15 -p') + utils.Sleep(1) + core.console('lf t55xx deviceconfig --r1 -a 29 -b 17 -c 18 -d 50 -e 15 -p') + utils.Sleep(1) + core.console('lf t55xx deviceconfig --r2 -a 29 -b 17 -c 18 -d 40 -e 15 -p') + utils.Sleep(1) + core.console('lf t55xx deviceconfig --r3 -a 29 -b 17 -c 15 -d 31 -e 15 -f 47 -g 63 -p') + utils.Sleep(1) print('') print('') diff --git a/client/luascripts/lf_em4100_bulk.lua b/client/luascripts/lf_em4100_bulk.lua new file mode 100644 index 000000000..d4cdfdd8d --- /dev/null +++ b/client/luascripts/lf_em4100_bulk.lua @@ -0,0 +1,212 @@ +local getopt = require('getopt') +local utils = require('utils') +local ac = require('ansicolors') + +copyright = '' +author = "Christian Herrmann" +version = 'v1.0.2' +desc = [[ +Perform bulk EM410x enrollment of T5577 RFID tags. It keeps track of last card id used. +If called with -s, this value resets "session". + +if press it defaults to Y, which writes a ID. +Any other input char will exit the script. + +You can supply a password, which will set the config block / block 7 on the T5577. + +The verify option will issue a 'lf em 410x reader' command, so you can manually verify +that the write worked. + +]] +example = [[ + -- resets and start enrolling EM410x id 11CC334455 + script run lf_em4100_bulk.lua -s 11CC334455 + + -- continue enrolling from where last iteration + script run lf_em4100_bulk.lua -c + + -- reset and start enrolling from 11223344, + -- protecting the tag with password 010203 + -- and verify the em id write. + script run lf_em4100_bulk.lua -s 1122334455 -p 01020304 -v +]] +usage = [[ +script run lf_en4100_bulk.lua [-h] [-c] [-p password] [-s ] [-v] +]] +arguments = [[ + -h : this help + -c : continue from last card number used + -p : Password protecting the T5577. + -s : starting card number + -v : verify write by executing a `lf em 410x reader` + ]] + + -- Some globals +local DEBUG = false +local ENROLL_STATUS_FN = 'lf_em4100_status.txt' +--- +-- A debug printout-function +local function dbg(args) + if not DEBUG then return end + if type(args) == 'table' then + local i = 1 + while args[i] do + dbg(args[i]) + i = i+1 + end + else + print('###', args) + end +end +--- +-- This is only meant to be used when errors occur +local function oops(err) + print('ERROR:', err) + core.clearCommandBuffer() + return nil, errr +end +--- +-- Usage help +local function help() + print(copyright) + print(author) + print(version) + print(desc) + print(ac.cyan..'Usage'..ac.reset) + print(usage) + print(ac.cyan..'Arguments'..ac.reset) + print(arguments) + print(ac.cyan..'Example usage'..ac.reset) + print(example) +end +--- +-- Exit message +local function exitMsg(msg) + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print(msg) + print() +end +--- +-- +local function readfile() + local f = io.open(ENROLL_STATUS_FN, "r") + if f == nil then + return nil, string.format("Could not read file %s", ENROLL_STATUS_FN) + end + local t = f:read("*all") + f:close() + local cn_hi = tonumber(t:sub(1, 2), 16) + local cn_low = tonumber(t:sub(3, 10), 16) + print(('Using EM4100 ID '..ac.green..'%02X%08X'..ac.reset..' from `'..ac.yellow..'%s'..ac.reset..'`'):format(cn_hi, cn_low, ENROLL_STATUS_FN)) + return cn_hi, cn_low +end +--- +-- +local function writefile(cn_hi, cn_low) + local f = io.open(ENROLL_STATUS_FN, "w") + if f == nil then + return nil, string.format("Could not write to file %s", ENROLL_STATUS_FN) + end + f:write(("%02X%08X\n"):format(cn_hi, cn_low)) + f:close() + print(('Wrote EM4100 ID '..ac.green..'%02X%08X'..ac.reset..' to `'..ac.yellow..'%s'..ac.reset..'`'):format(cn_hi, cn_low, ENROLL_STATUS_FN)) + return true, 'Ok' +end + +--- +-- main +local function main(args) + + print( string.rep('--',20) ) + print( string.rep('--',20) ) + print() + + if #args == 0 then return help() end + + local shall_verify = false + local shall_continue = false + local got_pwd = false + local startid = '' + local ipwd = '' + + + for o, a in getopt.getopt(args, 'cp:s:hv') do + if o == 'h' then return help() end + if o == 'c' then shall_continue = true end + if o == 's' then startid = a end + if o == 'p' then + ipwd = a + got_pwd = true + end + if o == 'v' then shall_verify = true end + end + + -- if reset/start over, check -s + if not shall_continue then + if startid == nil then return oops('empty card number string') end + if #startid == 0 then return oops('empty card number string') end + if #startid ~= 10 then return oops('card number wrong length. Must be 5 hex bytes') end + end + + if got_pwd then + if ipwd == nil then return oops('empty password') end + if #ipwd == 0 then return oops('empty password') end + if #ipwd ~= 8 then return oops('password wrong length. Must be 4 hex bytes') end + end + + core.console('clear') + print(ac.red..'disable hints for less output'..ac.reset) + core.console('pref set hint --off') + print('') + + local hi = tonumber(startid:sub(1, 2), 16) + local low = tonumber(startid:sub(3, 10), 16) + local pwd = tonumber(ipwd, 16) + + if got_pwd then + print(('Will protect T5577 with password '..ac.green..'%08X'..ac.reset):format(pwd)) + end + + if shall_verify then + print('Will verify write afterwards') + end + + if shall_continue then + print('Continue enrolling from last save') + hi, low = readfile() + else + print('reset & starting enrolling from refresh') + end + + local template = 'EM4100 ID '..ac.green..'%02X%08X'..ac.reset + for i = low, low + 10000, 1 do + print('') + print( string.rep('--',20) ) + local msg = (template):format(hi, i) + local ans = utils.input(msg, 'y'):lower() + if ans == 'y' then + core.console( ('lf em 410x clone --id %02X%08X'):format(hi, i) ) + -- print ( ('lf em 410x clone --id %02X%08X'):format(hi, i) ) + + if got_pwd then + core.console('lf t55 detect') + core.console(('lf t55 protect -n %08x'):format(pwd)) + end + + if shall_verify then + core.console('lf em 410x reader') + end + else + print(ac.red..'User aborted'..ac.reset) + low = i + break + end + end + writefile(hi, low) + + print('enabling hints again') + core.console('pref set hint --on') +end + +main(args) diff --git a/client/luascripts/ntag_getsig.lua b/client/luascripts/ntag_getsig.lua new file mode 100644 index 000000000..a70cb8623 --- /dev/null +++ b/client/luascripts/ntag_getsig.lua @@ -0,0 +1,104 @@ +local getopt = require('getopt') +local lib14a = require('read14a') +local cmds = require('commands') +local ansicolors = require('ansicolors') + +copyright = 'Copyright 2021 A. Ozkal, released under GPLv2+.' +author = 'Ave' +version = 'v1.0.0' +desc = [[ +This script attempts to grab signatures from an NTAG or MFULEV1 card and print it in a machine parsable way + ]] +example = [[ + script run ntag_getsig +]] +usage = [[ + script run ntag_getsig [-h] +]] +arguments = [[ + -h : This help +]] + +local function help() + print(author) + print(version) + print(desc) + print(ansicolors.cyan..'Usage'..ansicolors.reset) + print(usage) + print(ansicolors.cyan..'Arguments'..ansicolors.reset) + print(arguments) + print(ansicolors.cyan..'Example usage'..ansicolors.reset) + print(example) +end + +-- Used to send raw data to the firmware to subsequently forward the data to the card. +-- from mifareplus.lua +local function sendRaw(rawdata, crc, power) + -- print((": %s"):format(rawdata)) + + local flags = lib14a.ISO14A_COMMAND.ISO14A_RAW + if crc then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_APPEND_CRC + end + if power then + flags = flags + lib14a.ISO14A_COMMAND.ISO14A_NO_DISCONNECT + end + + local command = Command:newMIX{cmd = cmds.CMD_HF_ISO14443A_READER, + arg1 = flags, -- Send raw + arg2 = string.len(rawdata) / 2, -- arg2 contains the length, which is half the length of the ASCII-string rawdata + data = rawdata + } + local ignore_response = false + local result, err = command:sendMIX(ignore_response) + if result then + --unpack the first 4 parts of the result as longs, and the last as an extremely long string to later be cut down based on arg1, the number of bytes returned + local count,cmd,arg1,arg2,arg3,data = bin.unpack('LLLLH512',result) + + returned_bytes = string.sub(data, 1, arg1 * 2) + if #returned_bytes > 0 then + -- print((": %s"):format(returned_bytes)) -- need to multiply by 2 because the hex digits are actually two bytes when they are strings + return returned_bytes + else + return nil + end + else + print("Error sending the card raw data.") + return nil + end +end + +--- +-- The main entry point +function main(args) + -- Read the parameters + for o, a in getopt.getopt(args, 'h') do + if o == 'h' then return help() end + end + + local tag, err = lib14a.read(true, false) + + if not err then + local sig = sendRaw("3C00", true, true) + local ver = sendRaw("60", true, false) + if sig and ver then -- if false, that's a fail right there + sig = string.sub(sig, 0, -5) + ver = string.sub(ver, 0, -5) + local text = tag.name..","..ver..","..tag.uid..","..sig + print(text) + + local filename = "originalitysig.csv" + local outfile = io.open(filename, "a") + if outfile ~= nil then + outfile:write(text.."\n") + io.close(outfile) + else + print(ansicolors.red.."Couldn't open file originalitysig.csv."..ansicolors.reset) + end + else + print(ansicolors.red.."Read FAILED."..ansicolors.reset) + end + end +end + +main(args) diff --git a/client/luascripts/tests/data_tracetest.lua b/client/luascripts/tests/data_tracetest.lua index 1e75b707b..5773e0bcf 100644 --- a/client/luascripts/tests/data_tracetest.lua +++ b/client/luascripts/tests/data_tracetest.lua @@ -7,11 +7,11 @@ local ansicolors = require('ansicolors') copyright = '' author = 'Iceman' -version = 'v1.0.3' +version = 'v1.0.4' desc = [[ This script will load several traces files in current working directory/traces/ folder and do "data load" -"lf search 1 u" +"lf search -1u" The following tracefiles will be loaded: em*.pm3 @@ -109,7 +109,7 @@ local function main(args) end p.close(); - local cmdLFSEARCH = 'lf search 1 u' + local cmdLFSEARCH = 'lf search -1u' -- main loop io.write('Starting to test traces > ') diff --git a/client/deps/amiitool/key_retail.bin b/client/resources/key_retail.bin similarity index 100% rename from client/deps/amiitool/key_retail.bin rename to client/resources/key_retail.bin diff --git a/client/resources/mad.json b/client/resources/mad.json index d5c413d73..aae377af8 100644 --- a/client/resources/mad.json +++ b/client/resources/mad.json @@ -4749,14 +4749,14 @@ "application": "Security and Access Control", "company": "PEC (New Zealand) Ltd.", "mad": "0x4811", - "service_provider": "Cardax", + "service_provider": "Cardax / Gallagher", "system_integrator": "" }, { "application": "Security and Access Control", "company": "PEC (New Zealand) Ltd.", "mad": "0x4812", - "service_provider": "Cardax", + "service_provider": "Cardax / Gallagher", "system_integrator": "" }, { @@ -6096,6 +6096,13 @@ "service_provider": "CDVI", "system_integrator": "CDVI" }, + { + "application": "(access control and security) VIGIK", + "company": "NORALSY", + "mad": "0x4980", + "service_provider": "NORALSY", + "system_integrator": "NORALSY" + }, { "application": "Card Administratin, cardholder adminstration, access control & security, company services, miscellaneous applications", "company": "Ministry of Defense", diff --git a/tools/simmodule/sim011.bin b/client/resources/sim011.bin similarity index 100% rename from tools/simmodule/sim011.bin rename to client/resources/sim011.bin diff --git a/tools/simmodule/sim011.sha512.txt b/client/resources/sim011.sha512.txt similarity index 100% rename from tools/simmodule/sim011.sha512.txt rename to client/resources/sim011.sha512.txt diff --git a/client/src/aidsearch.c b/client/src/aidsearch.c index 47c64e77b..3a9d55ace 100644 --- a/client/src/aidsearch.c +++ b/client/src/aidsearch.c @@ -36,7 +36,7 @@ static int openAIDFile(json_t **root, bool verbose) { goto out; } - if (verbose) PrintAndLogEx(SUCCESS, "Loaded file (%s) OK. %zu records.", path, json_array_size(*root)); + PrintAndLogEx(DEBUG, "Loaded file " _YELLOW_("%s") " " _GREEN_("%zu") " records ( " _GREEN_("ok") " )", path, json_array_size(*root)); out: free(path); return retval; @@ -145,22 +145,22 @@ int PrintAIDDescription(json_t *xroot, char *aid, bool verbose) { const char *description = jsonStrGet(elm, "Description"); const char *type = jsonStrGet(elm, "Type"); - if (!verbose) { - PrintAndLogEx(SUCCESS, "AID %s | %s | %s", vaid, vendor, name); + if (verbose == false) { + PrintAndLogEx(SUCCESS, "AID : " _YELLOW_("%s") " | %s | %s", vaid, vendor, name); } else { - PrintAndLogEx(SUCCESS, "Input AID: %s", aid); + PrintAndLogEx(SUCCESS, "Input AID..... " _YELLOW_("%s"), aid); if (aid) - PrintAndLogEx(SUCCESS, "Found AID: %s", vaid); + PrintAndLogEx(SUCCESS, "Found AID..... " _YELLOW_("%s"), vaid); if (vendor) - PrintAndLogEx(SUCCESS, "Vendor: %s", vendor); + PrintAndLogEx(SUCCESS, "Vendor........ " _YELLOW_("%s"), vendor); if (type) - PrintAndLogEx(SUCCESS, "Type: %s", type); + PrintAndLogEx(SUCCESS, "Type.......... " _YELLOW_("%s"), type); if (name) - PrintAndLogEx(SUCCESS, "Name: %s", name); + PrintAndLogEx(SUCCESS, "Name.......... " _YELLOW_("%s"), name); if (country) - PrintAndLogEx(SUCCESS, "Country: %s", country); + PrintAndLogEx(SUCCESS, "Country....... %s", country); if (description) - PrintAndLogEx(SUCCESS, "Description: %s", description); + PrintAndLogEx(SUCCESS, "Description... %s", description); } out: diff --git a/client/src/cmdanalyse.c b/client/src/cmdanalyse.c index ecf8e50de..eb83fdc9c 100644 --- a/client/src/cmdanalyse.c +++ b/client/src/cmdanalyse.c @@ -12,98 +12,25 @@ #include // size_t #include #include // tolower -//#include // printf +#include +#include // PRIx64 macro #include "commonutil.h" // reflect... #include "comms.h" // clearCommandBuffer #include "cmdparser.h" // command_t #include "ui.h" // PrintAndLog #include "crc.h" #include "crc16.h" // crc16 ccitt +#include "crc32.h" // crc32_ex #include "tea.h" #include "legic_prng.h" #include "cmddata.h" // demodbuffer +#include "graph.h" +#include "proxgui.h" +#include "cliparser.h" +#include "generator.h" // generate nuid static int CmdHelp(const char *Cmd); -static int usage_analyse_lcr(void) { - PrintAndLogEx(NORMAL, "Specifying the bytes of a UID with a known LRC will find the last byte value"); - PrintAndLogEx(NORMAL, "needed to generate that LRC with a rolling XOR. All bytes should be specified in HEX."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: analyse lcr [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " bytes to calc missing XOR in a LCR"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " analyse lcr 04008064BA"); - PrintAndLogEx(NORMAL, "expected output: Target (BA) requires final LRC XOR byte value: 5A"); - return PM3_SUCCESS; -} -static int usage_analyse_checksum(void) { - PrintAndLogEx(NORMAL, "The bytes will be added with eachother and than limited with the applied mask"); - PrintAndLogEx(NORMAL, "Finally compute ones' complement of the least significant bytes"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: analyse chksum [h] [v] b m "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " v suppress header"); - PrintAndLogEx(NORMAL, " b bytes to calc missing XOR in a LCR"); - PrintAndLogEx(NORMAL, " m bit mask to limit the outpuyt"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " analyse chksum b 137AF00A0A0D m FF"); - PrintAndLogEx(NORMAL, "expected output: 0x61"); - return PM3_SUCCESS; -} -static int usage_analyse_crc(void) { - PrintAndLogEx(NORMAL, "A stub method to test different crc implementations inside the PM3 sourcecode. Just because you figured out the poly, doesn't mean you get the desired output"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: analyse crc [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " bytes to calc crc"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " analyse crc 137AF00A0A0D"); - return PM3_SUCCESS; -} -static int usage_analyse_nuid(void) { - PrintAndLogEx(NORMAL, "Generate 4byte NUID from 7byte UID"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: analyse hid [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " input bytes (14 hexsymbols)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " analyse nuid 11223344556677"); - return PM3_SUCCESS; -} -static int usage_analyse_a(void) { - PrintAndLogEx(NORMAL, "Iceman's personal garbage test command"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: analyse a [h] d "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " d bytes to send to device"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " analyse a d 137AF00A0A0D"); - return PM3_SUCCESS; -} -static int usage_analyse_demodbuffer(void) { - PrintAndLogEx(NORMAL, "loads a binary string into demod buffer"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: analyse demodbuff [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " Binary string to load"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " analyse demodbuff 0011101001001011"); - return PM3_SUCCESS; -} - static uint8_t calculateLRC(uint8_t *bytes, uint8_t len) { uint8_t LRC = 0; for (uint8_t i = 0; i < len; i++) @@ -245,228 +172,341 @@ static uint16_t calcBSDchecksum4(uint8_t *bytes, uint8_t len, uint32_t mask) { return sum; } +// 0xFF - ( n1 ^ n... ) +static uint16_t calcXORchecksum(uint8_t *bytes, uint8_t len, uint32_t mask) { + return 0xFF - calcSumByteXor(bytes, len, mask); +} + + +//2148050707DB0A0E000001C4000000 + // measuring LFSR maximum length static int CmdAnalyseLfsr(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse lfsr", + "looks at LEGIC Prime's lfsr, iterates the first 48 values", + "analyse lfsr --iv 55" + ); - uint8_t iv = param_get8ex(Cmd, 0, 0, 16); - uint8_t find = param_get8ex(Cmd, 1, 0, 16); + void *argtable[] = { + arg_param_begin, + arg_str1(NULL, "iv", "", "init vector data (1 hex byte)"), + arg_str0(NULL, "find", "", "lfsr data to find (1 hex byte)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int iv_len = 0; + uint8_t idata[1] = {0}; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), idata, sizeof(idata), &iv_len); - PrintAndLogEx(NORMAL, "LEGIC LFSR IV 0x%02X: \n", iv); - PrintAndLogEx(NORMAL, " bit# | lfsr | ^0x40 | 0x%02X ^ lfsr \n", find); + if (res) { + CLIParserFree(ctx); + PrintAndLogEx(FAILED, "Error parsing IV byte"); + return PM3_EINVARG; + } + + int f_len = 0; + uint8_t fdata[1] = {0}; + res = CLIParamHexToBuf(arg_get_str(ctx, 2), fdata, sizeof(fdata), &f_len); + CLIParserFree(ctx); + + if (res) { + PrintAndLogEx(FAILED, "Error parsing FIND byte"); + return PM3_EINVARG; + } + + uint8_t iv = idata[0]; + uint8_t find = fdata[0]; + + PrintAndLogEx(INFO, "LEGIC Prime lfsr"); + PrintAndLogEx(INFO, "iv..... 0x%02X", iv); + PrintAndLogEx(INFO, "----+------+-------+--------------"); + PrintAndLogEx(INFO, " i# | lfsr | ^0x40 | 0x%02X ^ lfsr", find); + PrintAndLogEx(INFO, "----+------+-------+--------------"); for (uint8_t i = 0x01; i < 0x30; i += 1) { legic_prng_init(iv); legic_prng_forward(i); uint16_t lfsr = legic_prng_get_bits(12); /* Any nonzero start state will work. */ - PrintAndLogEx(NORMAL, " %02X | %03X | %03X | %03X \n", i, lfsr, 0x40 ^ lfsr, find ^ lfsr); + PrintAndLogEx(INFO, " %02X | %03X | %03X | %03X", i, lfsr, 0x40 ^ lfsr, find ^ lfsr); } - return 0; + PrintAndLogEx(INFO, "----+------+-------+--------------"); + return PM3_SUCCESS; } + static int CmdAnalyseLCR(const char *Cmd) { - uint8_t data[50]; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_analyse_lcr(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse lcr", + "Specifying the bytes of a UID with a known LRC will find the last byte value\n" + "needed to generate that LRC with a rolling XOR. All bytes should be specified in HEX.", + "analyse lcr -d 04008064BA -> Target (BA) requires final LRC XOR byte value: 5A" + ); - int len = 0; - switch (param_gethex_to_eol(Cmd, 0, data, sizeof(data), &len)) { - case 1: - PrintAndLogEx(WARNING, "Invalid HEX value."); - return 1; - case 2: - PrintAndLogEx(WARNING, "Too many bytes. Max %zu bytes", sizeof(data)); - return 1; - case 3: - PrintAndLogEx(WARNING, "Hex must have even number of digits."); - return 1; + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "bytes to calc missing XOR in a LCR"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int dlen = 0; + uint8_t data[100] = {0x00}; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), data, sizeof(data), &dlen); + CLIParserFree(ctx); + + if (res) { + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; } - uint8_t finalXor = calculateLRC(data, len); - PrintAndLogEx(NORMAL, "Target [%02X] requires final LRC XOR byte value: 0x%02X", data[len - 1], finalXor); - return 0; + + uint8_t finalXor = calculateLRC(data, dlen); + PrintAndLogEx(SUCCESS, "Target [%02X] requires final LRC XOR byte value: " _YELLOW_("0x%02X"), data[dlen - 1], finalXor); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; } + static int CmdAnalyseCRC(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse crc", + "A stub method to test different crc implementations inside the PM3 sourcecode.\n" + "Just because you figured out the poly, doesn't mean you get the desired output", + "analyse crc -d 137AF00A0A0D" + ); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_analyse_crc(); + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "bytes to calc crc"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int dlen = 0; + uint8_t data[1024] = {0x00}; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), data, sizeof(data), &dlen); + CLIParserFree(ctx); - int len = strlen(Cmd); - if (len & 1) return usage_analyse_crc(); - - // add 1 for null terminator. - uint8_t *data = calloc(len + 1, sizeof(uint8_t)); - if (!data) return 1; - - if (param_gethex(Cmd, 0, data, len)) { - free(data); - return usage_analyse_crc(); + if (res) { + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; } - len >>= 1; - PrintAndLogEx(NORMAL, "\nTests with (%d) | %s", len, sprint_hex(data, len)); + PrintAndLogEx(INFO, "\nTests with (%d) | %s", dlen, sprint_hex(data, dlen)); // 51 f5 7a d6 uint8_t uid[] = {0x51, 0xf5, 0x7a, 0xd6}; //12 34 56 init_table(CRC_LEGIC); uint8_t legic8 = CRC8Legic(uid, sizeof(uid)); - PrintAndLogEx(NORMAL, "Legic 16 | %X (EF6F expected) [legic8 = %02x]", crc16_legic(data, len, legic8), legic8); + PrintAndLogEx(INFO, "Legic 16 | %X (EF6F expected) [legic8 = %02x]", crc16_legic(data, dlen, legic8), legic8); init_table(CRC_FELICA); - PrintAndLogEx(NORMAL, "FeliCa | %X ", crc16_xmodem(data, len)); + PrintAndLogEx(INFO, "FeliCa | %X ", crc16_xmodem(data, dlen)); - PrintAndLogEx(NORMAL, "\nTests of reflection. Current methods in source code"); - PrintAndLogEx(NORMAL, " reflect(0x3e23L,3) is %04X == 0x3e26", reflect(0x3e23L, 3)); - PrintAndLogEx(NORMAL, " reflect8(0x80) is %02X == 0x01", reflect8(0x80)); - PrintAndLogEx(NORMAL, " reflect16(0x8000) is %04X == 0x0001", reflect16(0xc6c6)); + PrintAndLogEx(INFO, "\nTests of reflection. Current methods in source code"); + PrintAndLogEx(INFO, " reflect(0x3e23L,3) is %04X == 0x3e26", reflect(0x3e23L, 3)); + PrintAndLogEx(INFO, " reflect8(0x80) is %02X == 0x01", reflect8(0x80)); + PrintAndLogEx(INFO, " reflect16(0x8000) is %04X == 0x0001", reflect16(0xc6c6)); uint8_t b1, b2; // ISO14443 crc B - compute_crc(CRC_14443_B, data, len, &b1, &b2); + compute_crc(CRC_14443_B, data, dlen, &b1, &b2); uint16_t crcBB_1 = b1 << 8 | b2; - uint16_t bbb = Crc16ex(CRC_14443_B, data, len); - PrintAndLogEx(NORMAL, "ISO14443 crc B | %04x == %04x \n", crcBB_1, bbb); + uint16_t bbb = Crc16ex(CRC_14443_B, data, dlen); + PrintAndLogEx(INFO, "ISO14443 crc B | %04x == %04x \n", crcBB_1, bbb); // Test of CRC16, '123456789' string. // - PrintAndLogEx(NORMAL, "\n\nStandard test with 31 32 33 34 35 36 37 38 39 '123456789'\n\n"); + PrintAndLogEx(INFO, "\n\nStandard test with 31 32 33 34 35 36 37 38 39 '123456789'\n\n"); uint8_t dataStr[] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 }; legic8 = CRC8Legic(dataStr, sizeof(dataStr)); //these below has been tested OK. - PrintAndLogEx(NORMAL, "Confirmed CRC Implementations"); - PrintAndLogEx(NORMAL, "-------------------------------------\n"); - PrintAndLogEx(NORMAL, "CRC 8 based\n\n"); - PrintAndLogEx(NORMAL, "LEGIC: CRC8 : %X (C6 expected)", legic8); - PrintAndLogEx(NORMAL, "MAXIM: CRC8 : %X (A1 expected)", CRC8Maxim(dataStr, sizeof(dataStr))); - PrintAndLogEx(NORMAL, "-------------------------------------\n"); - PrintAndLogEx(NORMAL, "CRC16 based\n\n"); + PrintAndLogEx(INFO, "Confirmed CRC Implementations"); + PrintAndLogEx(INFO, "-------------------------------------\n"); + PrintAndLogEx(INFO, "CRC 8 based\n\n"); + PrintAndLogEx(INFO, "LEGIC: CRC8 : %X (C6 expected)", legic8); + PrintAndLogEx(INFO, "MAXIM: CRC8 : %X (A1 expected)", CRC8Maxim(dataStr, sizeof(dataStr))); + PrintAndLogEx(INFO, "-------------------------------------\n"); + PrintAndLogEx(INFO, "CRC16 based\n\n"); // input from commandline - PrintAndLogEx(NORMAL, "CCITT | %X (29B1 expected)", Crc16ex(CRC_CCITT, dataStr, sizeof(dataStr))); + PrintAndLogEx(INFO, "CCITT | %X (29B1 expected)", Crc16ex(CRC_CCITT, dataStr, sizeof(dataStr))); uint8_t poll[] = {0xb2, 0x4d, 0x12, 0x01, 0x01, 0x2e, 0x3d, 0x17, 0x26, 0x47, 0x80, 0x95, 0x00, 0xf1, 0x00, 0x00, 0x00, 0x01, 0x43, 0x00, 0xb3, 0x7f}; - PrintAndLogEx(NORMAL, "FeliCa | %04X (B37F expected)", Crc16ex(CRC_FELICA, poll + 2, sizeof(poll) - 4)); - PrintAndLogEx(NORMAL, "FeliCa | %04X (0000 expected)", Crc16ex(CRC_FELICA, poll + 2, sizeof(poll) - 2)); + PrintAndLogEx(INFO, "FeliCa | %04X (B37F expected)", Crc16ex(CRC_FELICA, poll + 2, sizeof(poll) - 4)); + PrintAndLogEx(INFO, "FeliCa | %04X (0000 expected)", Crc16ex(CRC_FELICA, poll + 2, sizeof(poll) - 2)); uint8_t sel_corr[] = { 0x40, 0xe1, 0xe1, 0xff, 0xfe, 0x5f, 0x02, 0x3c, 0x43, 0x01}; - PrintAndLogEx(NORMAL, "iCLASS | %04x (0143 expected)", Crc16ex(CRC_ICLASS, sel_corr, sizeof(sel_corr) - 2)); - PrintAndLogEx(NORMAL, "---------------------------------------------------------------\n\n\n"); + PrintAndLogEx(INFO, "iCLASS | %04x (0143 expected)", Crc16ex(CRC_ICLASS, sel_corr, sizeof(sel_corr) - 2)); + PrintAndLogEx(INFO, "---------------------------------------------------------------\n\n\n"); // ISO14443 crc A compute_crc(CRC_14443_A, dataStr, sizeof(dataStr), &b1, &b2); uint16_t crcAA = b1 << 8 | b2; - PrintAndLogEx(NORMAL, "ISO14443 crc A | %04x or %04x (BF05 expected)\n", crcAA, Crc16ex(CRC_14443_A, dataStr, sizeof(dataStr))); + PrintAndLogEx(INFO, "ISO14443 crc A | %04x or %04x (BF05 expected)\n", crcAA, Crc16ex(CRC_14443_A, dataStr, sizeof(dataStr))); // ISO14443 crc B compute_crc(CRC_14443_B, dataStr, sizeof(dataStr), &b1, &b2); uint16_t crcBB = b1 << 8 | b2; - PrintAndLogEx(NORMAL, "ISO14443 crc B | %04x or %04x (906E expected)\n", crcBB, Crc16ex(CRC_14443_B, dataStr, sizeof(dataStr))); + PrintAndLogEx(INFO, "ISO14443 crc B | %04x or %04x (906E expected)\n", crcBB, Crc16ex(CRC_14443_B, dataStr, sizeof(dataStr))); // ISO15693 crc (x.25) compute_crc(CRC_15693, dataStr, sizeof(dataStr), &b1, &b2); uint16_t crcCC = b1 << 8 | b2; - PrintAndLogEx(NORMAL, "ISO15693 crc X25| %04x or %04x (906E expected)\n", crcCC, Crc16ex(CRC_15693, dataStr, sizeof(dataStr))); + PrintAndLogEx(INFO, "ISO15693 crc X25| %04x or %04x (906E expected)\n", crcCC, Crc16ex(CRC_15693, dataStr, sizeof(dataStr))); // ICLASS compute_crc(CRC_ICLASS, dataStr, sizeof(dataStr), &b1, &b2); uint16_t crcDD = b1 << 8 | b2; - PrintAndLogEx(NORMAL, "ICLASS crc | %04x or %04x\n", crcDD, Crc16ex(CRC_ICLASS, dataStr, sizeof(dataStr))); + PrintAndLogEx(INFO, "ICLASS crc | %04x or %04x\n", crcDD, Crc16ex(CRC_ICLASS, dataStr, sizeof(dataStr))); // FeliCa compute_crc(CRC_FELICA, dataStr, sizeof(dataStr), &b1, &b2); uint16_t crcEE = b1 << 8 | b2; - PrintAndLogEx(NORMAL, "FeliCa | %04x or %04x (31C3 expected)\n", crcEE, Crc16ex(CRC_FELICA, dataStr, sizeof(dataStr))); + PrintAndLogEx(INFO, "FeliCa | %04x or %04x (31C3 expected)\n", crcEE, Crc16ex(CRC_FELICA, dataStr, sizeof(dataStr))); - free(data); - return 0; + + uint32_t crc32 = 0; + crc32_ex(dataStr, sizeof(dataStr), (uint8_t *)&crc32); + PrintAndLogEx(INFO, "CRC32 (desfire) | %08x ( expected)", crc32); + PrintAndLogEx(INFO, "---------------------------------------------------------------\n\n\n"); + + return PM3_SUCCESS; } + static int CmdAnalyseCHKSUM(const char *Cmd) { - uint8_t data[50]; - uint8_t cmdp = 0; - uint32_t mask = 0xFFFF; - bool errors = false; - bool useHeader = false; - int len = 0; - memset(data, 0x0, sizeof(data)); + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse chksum", + "The bytes will be added with eachother and than limited with the applied mask\n" + "Finally compute ones' complement of the least significant bytes.", + "analyse chksum -d 137AF00A0A0D -> expected output: 0x61\n" + "analyse chksum -d 137AF00A0A0D -m FF" + ); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (param_getchar(Cmd, cmdp)) { - case 'b': - case 'B': - param_gethex_ex(Cmd, cmdp + 1, data, &len); - if (len % 2) errors = true; - len >>= 1; - cmdp += 2; - break; - case 'm': - case 'M': - mask = param_get32ex(Cmd, cmdp + 1, 0, 16); - cmdp += 2; - break; - case 'v': - case 'V': - useHeader = true; - cmdp++; - break; - case 'h': - case 'H': - return usage_analyse_checksum(); - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "bytes to calc checksum"), + arg_str0("m", "mask", "", "bit mask to limit the output (4 hex bytes max)"), + arg_lit0("v", "verbose", "verbose"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int dlen = 0; + uint8_t data[100] = {0x00}; + memset(data, 0x0, sizeof(data)); + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), data, sizeof(data), &dlen); + if (res) { + CLIParserFree(ctx); + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; + } + const char *s = arg_get_str(ctx, 2)->sval[0]; + bool verbose = arg_get_lit(ctx, 3); + CLIParserFree(ctx); + + uint32_t mlen = 0; + if (s) + mlen = strlen(s); + + if (mlen > 8) { + PrintAndLogEx(FAILED, "Mask value is max 4 hex bytes"); + return PM3_EINVARG; + } + + uint32_t mask = 0; + if (mlen == 0) { + mask = 0xFFFF; + } else { + for (int i = 0; i < mlen; i++) { + char c = s[i]; + // capitalize + if (c >= 'a' && c <= 'f') + c -= 32; + // convert to numeric value + if (c >= '0' && c <= '9') + c -= '0'; + else if (c >= 'A' && c <= 'F') + c -= 'A' - 10; + else + continue; + + mask <<= 4; + mask |= c; } } - //Validations - if (errors || cmdp == 0) return usage_analyse_checksum(); - if (useHeader) { - PrintAndLogEx(NORMAL, " add | sub | add 1's compl | sub 1's compl | xor"); - PrintAndLogEx(NORMAL, "byte nibble crumb | byte nibble | byte nibble cumb | byte nibble | byte nibble cumb | BSD |"); - PrintAndLogEx(NORMAL, "------------------+-------------+------------------+-----------------+--------------------"); + PrintAndLogEx(INFO, "Mask value 0x%x", mask); + + if (verbose) { + PrintAndLogEx(INFO, "------------------+-------------+------------------+-----------------+------------------+-----------+-------------"); + PrintAndLogEx(INFO, " add | sub | add 1's compl | sub 1's compl | xor | |"); + PrintAndLogEx(INFO, "byte nibble crumb | byte nibble | byte nibble cumb | byte nibble | byte nibble cumb | BSD | 0xFF - (n^n)"); + PrintAndLogEx(INFO, "------------------+-------------+------------------+-----------------+------------------+-----------+-------------"); } - PrintAndLogEx(NORMAL, "0x%X 0x%X 0x%X | 0x%X 0x%X | 0x%X 0x%X 0x%X | 0x%X 0x%X | 0x%X 0x%X 0x%X | 0x%X 0x%X |\n", - calcSumByteAdd(data, len, mask) - , calcSumNibbleAdd(data, len, mask) - , calcSumCrumbAdd(data, len, mask) - , calcSumByteSub(data, len, mask) - , calcSumNibbleSub(data, len, mask) - , calcSumByteAddOnes(data, len, mask) - , calcSumNibbleAddOnes(data, len, mask) - , calcSumCrumbAddOnes(data, len, mask) - , calcSumByteSubOnes(data, len, mask) - , calcSumNibbleSubOnes(data, len, mask) - , calcSumByteXor(data, len, mask) - , calcSumNibbleXor(data, len, mask) - , calcSumCrumbXor(data, len, mask) - , calcBSDchecksum8(data, len, mask) - , calcBSDchecksum4(data, len, mask) + PrintAndLogEx(INFO, "0x%X 0x%X 0x%X | 0x%X 0x%X | 0x%X 0x%X 0x%X | 0x%X 0x%X | 0x%X 0x%X 0x%X | 0x%X 0x%X | 0x%X\n", + calcSumByteAdd(data, dlen, mask) + , calcSumNibbleAdd(data, dlen, mask) + , calcSumCrumbAdd(data, dlen, mask) + , calcSumByteSub(data, dlen, mask) + , calcSumNibbleSub(data, dlen, mask) + , calcSumByteAddOnes(data, dlen, mask) + , calcSumNibbleAddOnes(data, dlen, mask) + , calcSumCrumbAddOnes(data, dlen, mask) + , calcSumByteSubOnes(data, dlen, mask) + , calcSumNibbleSubOnes(data, dlen, mask) + , calcSumByteXor(data, dlen, mask) + , calcSumNibbleXor(data, dlen, mask) + , calcSumCrumbXor(data, dlen, mask) + , calcBSDchecksum8(data, dlen, mask) + , calcBSDchecksum4(data, dlen, mask) + , calcXORchecksum(data, dlen, mask) ); - return 0; + return PM3_SUCCESS; } static int CmdAnalyseDates(const char *Cmd) { - (void)Cmd; // Cmd is not used so far - // look for datestamps in a given array of bytes - PrintAndLogEx(NORMAL, "To be implemented. Feel free to contribute!"); - return 0; -} -static int CmdAnalyseTEASelfTest(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse dates", + "Tool to look for date/time stamps in a given array of bytes", + "analyse dates" + ); - uint8_t v[8], v_le[8]; - memset(v, 0x00, sizeof(v)); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + PrintAndLogEx(NORMAL, "To be implemented. Feel free to contribute!"); + return PM3_SUCCESS; +} + +static int CmdAnalyseTEASelfTest(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse tea", + "Crypto TEA self tests", + "analyse tea -d 1122334455667788" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "bytes to encrypt ( 8 hex bytes )"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int dlen = 0; + uint8_t data[8] = {0x00}; + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), data, sizeof(data), &dlen); + CLIParserFree(ctx); + if (res) { + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; + } + + uint8_t v_le[8]; memset(v_le, 0x00, sizeof(v_le)); uint8_t *v_ptr = v_le; - uint8_t cmdlen = strlen(Cmd); - cmdlen = (sizeof(v) << 2 < cmdlen) ? sizeof(v) << 2 : cmdlen; - - if (param_gethex(Cmd, 0, v, cmdlen) > 0) { - PrintAndLogEx(WARNING, "Can't read hex chars, uneven? :: %u", cmdlen); - return 1; - } - - SwapEndian64ex(v, 8, 4, v_ptr); + SwapEndian64ex(data, 8, 4, v_ptr); // ENCRYPTION KEY: uint8_t key[16] = {0x55, 0xFE, 0xF6, 0x30, 0x62, 0xBF, 0x0B, 0xC1, 0xC9, 0xB3, 0x7C, 0x34, 0x97, 0x3E, 0x29, 0xFB }; @@ -474,49 +514,54 @@ static int CmdAnalyseTEASelfTest(const char *Cmd) { uint8_t *key_ptr = keyle; SwapEndian64ex(key, sizeof(key), 4, key_ptr); - PrintAndLogEx(NORMAL, "TEST LE enc| %s", sprint_hex(v_ptr, 8)); + PrintAndLogEx(INFO, "TEA crypto testing"); + PrintAndLogEx(INFO, "-----------------------------------+---------"); + PrintAndLogEx(INFO, "LE enc.... %s", sprint_hex_ascii(v_ptr, 8)); tea_decrypt(v_ptr, key_ptr); - PrintAndLogEx(NORMAL, "TEST LE dec | %s", sprint_hex_ascii(v_ptr, 8)); + PrintAndLogEx(INFO, "LE dec.... %s", sprint_hex_ascii(v_ptr, 8)); tea_encrypt(v_ptr, key_ptr); + PrintAndLogEx(INFO, "enc1...... %s", sprint_hex_ascii(v_ptr, 8)); tea_encrypt(v_ptr, key_ptr); - PrintAndLogEx(NORMAL, "TEST enc2 | %s", sprint_hex_ascii(v_ptr, 8)); - - return 0; + PrintAndLogEx(INFO, "enc2...... %s", sprint_hex_ascii(v_ptr, 8)); + PrintAndLogEx(NORMAL, ""); + return PM3_SUCCESS; } -/* -static char *pb(uint32_t b) { - static char buf1[33] = {0}; - static char buf2[33] = {0}; - static char *s; - - if (s != buf1) - s = buf1; - else - s = buf2; - - memset(s, 0, sizeof(buf1)); - - uint32_t mask = 0x80000000; - for (uint8_t i = 0; i < 32; i++) { - s[i] = (mask & b) ? '1' : '0'; - mask >>= 1; - } - return s; -} -*/ - static int CmdAnalyseA(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse a", + "Iceman's personal garbage test command", + "analyse a -d 137AF00A0A0D" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "bytes to manipulate"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int dlen = 0; + uint8_t data[100] = {0x00}; + memset(data, 0x0, sizeof(data)); + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), data, sizeof(data), &dlen); + if (res) { + CLIParserFree(ctx); + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; + } + + uint64_t key = 0; + res = mfc_algo_touch_one(data, 0, 0, &key); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "KEY A | %012" PRIx64, key); + } + + CLIParserFree(ctx); + return PM3_SUCCESS; - return usage_analyse_a(); /* - PrintAndLogEx(NORMAL, "-- " _BLUE_("its my message") "\n"); - PrintAndLogEx(NORMAL, "-- " _RED_("its my message") "\n"); - PrintAndLogEx(NORMAL, "-- " _YELLOW_("its my message") "\n"); - PrintAndLogEx(NORMAL, "-- " _GREEN_("its my message") "\n"); - //uint8_t syncBit = 99; // The start bit is one ore more Sequence Y followed by a Sequence Z (... 11111111 00x11111). We need to distinguish from // Sequence X followed by Sequence Y followed by Sequence Z (111100x1 11111111 00x11111) @@ -528,28 +573,22 @@ static int CmdAnalyseA(const char *Cmd) { uint8_t byte_offset = 99; // reverse byte uint8_t rev = reflect8(bt); - PrintAndLogEx(NORMAL, "input %02x | %02x \n", bt, rev); + PrintAndLogEx(INFO, "input %02x | %02x \n", bt, rev); // add byte to shift register shiftReg = shiftReg << 8 | rev; - PrintAndLogEx(NORMAL, "shiftreg after %08x | pattern %08x \n", shiftReg, SYNC_16BIT); + PrintAndLogEx(INFO, "shiftreg after %08x | pattern %08x \n", shiftReg, SYNC_16BIT); uint8_t n0 = 0, n1 = 0; n0 = (rev & (uint8_t)(~(0xFF >> (8 - 4)))) >> 4; n1 = (n1 << 4) | (rev & (uint8_t)(~(0xFF << 4))); - PrintAndLogEx(NORMAL, "rev %02X | %02X %s | %02X %s |\n", rev, n0, pb(n0), n1, pb(n1)); + PrintAndLogEx(INFO, "rev %02X | %02X %s | %02X %s |\n", rev, n0, pb(n0), n1, pb(n1)); */ - /* - hex(0xb24d shr 0) 0xB24D 0b1011001001001101 - hex(0xb24d shr 1) 0x5926 - hex(0xb24d shr 2) 0x2C93 - */ - /* for (int i = 0; i < 16; i++) { - PrintAndLogEx(NORMAL, " (shiftReg >> %d) & 0xFFFF == %08x ---", i, ((shiftReg >> i) & 0xFFFF)); + PrintAndLogEx(INFO, " (shiftReg >> %d) & 0xFFFF == %08x ---", i, ((shiftReg >> i) & 0xFFFF)); // kolla om SYNC_PATTERN finns. if (((shiftReg >> 7) & 0xFFFF) == SYNC_16BIT) byte_offset = 7; @@ -561,7 +600,7 @@ static int CmdAnalyseA(const char *Cmd) { else if (((shiftReg >> 1) & 0xFFFF) == SYNC_16BIT) byte_offset = 1; else if (((shiftReg >> 0) & 0xFFFF) == SYNC_16BIT) byte_offset = 0; - PrintAndLogEx(NORMAL, "Offset %u \n", byte_offset); + PrintAndLogEx(INFO, "Offset %u \n", byte_offset); if (byte_offset != 99) break; @@ -569,14 +608,14 @@ static int CmdAnalyseA(const char *Cmd) { } uint8_t p1 = (rev & (uint8_t)(~(0xFF << byte_offset))); - PrintAndLogEx(NORMAL, "Offset %u | leftovers %02x %s \n", byte_offset, p1, pb(p1)); + PrintAndLogEx(INFO, "Offset %u | leftovers %02x %s \n", byte_offset, p1, pb(p1)); */ /* - pm3 --> da hex2bin 4db2 0100110110110010 + pm3 --> da hex2bin 4db2 0100110110110010 */ - //return 0; + //return PM3_SUCCESS; /* // split byte into two parts. uint8_t offset = 3, n0 = 0, n1 = 0; @@ -586,23 +625,23 @@ static int CmdAnalyseA(const char *Cmd) { n0 = (rev & (uint8_t)(~(0xFF >> (8-offset)))) >> offset; n1 = (n1 << offset) | (rev & (uint8_t)(~(0xFF << offset))); - PrintAndLogEx(NORMAL, "rev %02X | %02X %s | %02X %s |\n", rev, n0, pb(n0), n1, pb(n1) ); + PrintAndLogEx(INFO, "rev %02X | %02X %s | %02X %s |\n", rev, n0, pb(n0), n1, pb(n1) ); n0 = 0, n1 = 0; - // PrintAndLogEx(NORMAL, " (0xFF >> offset) == %s |\n", pb( (0xFF >> offset)) ); - //PrintAndLogEx(NORMAL, "~(0xFF >> (8-offset)) == %s |\n", pb( (uint8_t)(~(0xFF >> (8-offset))) ) ); - //PrintAndLogEx(NORMAL, " rev & xxx == %s\n\n", pb( (rev & (uint8_t)(~(0xFF << offset))) )); + // PrintAndLogEx(INFO, " (0xFF >> offset) == %s |\n", pb( (0xFF >> offset)) ); + //PrintAndLogEx(INFO, "~(0xFF >> (8-offset)) == %s |\n", pb( (uint8_t)(~(0xFF >> (8-offset))) ) ); + //PrintAndLogEx(INFO, " rev & xxx == %s\n\n", pb( (rev & (uint8_t)(~(0xFF << offset))) )); } - return 0; + return PM3_SUCCESS; // from A -- x bits into B and the rest into C. for ( uint8_t i=0; i<8; i++){ - PrintAndLogEx(NORMAL, "%u | %02X %s | %02X %s |\n", i, a, pb(a), b, pb(b) ); + PrintAndLogEx(INFO, "%u | %02X %s | %02X %s |\n", i, a, pb(a), b, pb(b) ); b = a & (a & (0xFF >> (8-i))); a >>=1; } */ -// return 0; +// return PM3_SUCCESS; /* // 14443-A @@ -613,7 +652,7 @@ static int CmdAnalyseA(const char *Cmd) { // 14443-B uint8_t u14b[] = {0x05, 0x00, 0x08, 0x39, 0x73}; - PrintAndLogEx(NORMAL, "14b check crc | %s\n", (check_crc(CRC_14443_B, u14b, sizeof(u14b))) ? "YES" : "NO"); + PrintAndLogEx(INFO, "14b check crc | %s\n", (check_crc(CRC_14443_B, u14b, sizeof(u14b))) ? "YES" : "NO"); // 15693 test uint8_t u15_c[] = {0x05, 0x00, 0x08, 0x39, 0x73}; // correct @@ -633,28 +672,10 @@ static int CmdAnalyseA(const char *Cmd) { PrintAndLogEx(FAILED, "FeliCa check wrong crc | %s\n", (check_crc(CRC_FELICA, felica_w, sizeof(felica_w))) ? "YES" : "NO"); PrintAndLogEx(SUCCESS, "FeliCa check correct crc | %s\n", (check_crc(CRC_FELICA, felica_c, sizeof(felica_c))) ? "YES" : "NO"); - PrintAndLogEx(NORMAL, "\n\n"); + PrintAndLogEx(NORMAL, "\n"); - return 0; + return PM3_SUCCESS; */ - /* - bool term = !isatty(STDIN_FILENO); - if (!term) { - char star[4]; - star[0] = '-'; - star[1] = '\\'; - star[2] = '|'; - star[3] = '/'; - - for (uint8_t k=0; k<4; k = (k+1) % 4 ) { - PrintAndLogEx(NORMAL, "\e[s%c\e[u", star[k]); - fflush(stdout); - if (kbd_enter_pressed()) { - break; - } - } - } - */ //piwi // uid(2e086b1a) nt(230736f6) ks(0b0008000804000e) nr(000000000) @@ -789,7 +810,7 @@ static int CmdAnalyseA(const char *Cmd) { /* for (uint8_t i=0; i<31; i++){ uint64_t a = keys[i] ^ keys[i+1]; - PrintAndLogEx(NORMAL, "%u | %012" PRIX64 " | \n", i, a); + PrintAndLogEx(INFO, "%u | %012" PRIX64 " | \n", i, a); } */ @@ -807,8 +828,8 @@ static int CmdAnalyseA(const char *Cmd) { 0x2F }; - PrintAndLogEx(NORMAL, "UID | %s\n", sprint_hex(uid,4 )); - PrintAndLogEx(NORMAL, "KEY A | %s\n", sprint_hex(key_s0a, 6)); + PrintAndLogEx(INFO, "UID | %s\n", sprint_hex(uid,4 )); + PrintAndLogEx(INFO, "KEY A | %s\n", sprint_hex(key_s0a, 6)); // arrays w all keys uint64_t foo[32] = {0}; @@ -828,7 +849,7 @@ static int CmdAnalyseA(const char *Cmd) { uint64_t a = foo[i]; uint64_t b = foo[i+16]; - PrintAndLogEx(NORMAL, "%02u | %012" PRIX64 " %s | %012" PRIX64 " %s\n", + PrintAndLogEx(INFO, "%02u | %012" PRIX64 " %s | %012" PRIX64 " %s\n", i, a, ( a == keya[i])?"ok":"err", @@ -837,78 +858,105 @@ static int CmdAnalyseA(const char *Cmd) { ); } */ -// return 0; -} - -static void generate4bNUID(uint8_t *uid, uint8_t *nuid) { - uint16_t crc; - uint8_t b1, b2; - - compute_crc(CRC_14443_A, uid, 3, &b1, &b2); - nuid[0] = (b2 & 0xE0) | 0xF; - nuid[1] = b1; - crc = b1; - crc |= b2 << 8; - crc = crc16_fast(&uid[3], 4, reflect16(crc), true, true); - nuid[2] = (crc >> 8) & 0xFF ; - nuid[3] = crc & 0xFF; +// return PM3_SUCCESS; } static int CmdAnalyseNuid(const char *Cmd) { - uint8_t nuid[4] = {0}; + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse nuid", + "Generate 4byte NUID from 7byte UID", + "analyse nuid -d 11223344556677" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("d", "data", "", "bytes to send"), + arg_lit0("t", "test", "self test"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int uidlen = 0; uint8_t uid[7] = {0}; - int len = 0; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_analyse_nuid(); + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), uid, sizeof(uid), &uidlen); + bool selftest = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + if (res) { + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; + } + + uint8_t nuid[4] = {0}; /* src: https://www.nxp.com/docs/en/application-note/AN10927.pdf */ /* selftest1 UID 040D681AB52281 -> NUID 8F430FEF */ /* selftest2 UID 04183F09321B85 -> NUID 4F505D7D */ - if (cmdp == 't') { + if (selftest) { uint8_t uid_test1[] = {0x04, 0x0d, 0x68, 0x1a, 0xb5, 0x22, 0x81}; uint8_t nuid_test1[] = {0x8f, 0x43, 0x0f, 0xef}; uint8_t uid_test2[] = {0x04, 0x18, 0x3f, 0x09, 0x32, 0x1b, 0x85}; uint8_t nuid_test2[] = {0x4f, 0x50, 0x5d, 0x7d}; memcpy(uid, uid_test1, sizeof(uid)); - generate4bNUID(uid, nuid); + mfc_generate4b_nuid(uid, nuid); + PrintAndLogEx(INFO, "Self tests"); bool test1 = (0 == memcmp(nuid, nuid_test1, sizeof(nuid))); - PrintAndLogEx(SUCCESS, "Selftest1 %s\n", test1 ? _GREEN_("OK") : _RED_("Fail")); + PrintAndLogEx((test1) ? SUCCESS : FAILED, "1. %s -> %s ( %s )" + , sprint_hex_inrow(uid_test1, sizeof(uid_test1)) + , sprint_hex(nuid, sizeof(nuid)) + , test1 ? _GREEN_("ok") : _RED_("fail") + ); memcpy(uid, uid_test2, sizeof(uid)); - generate4bNUID(uid, nuid); + mfc_generate4b_nuid(uid, nuid); bool test2 = (0 == memcmp(nuid, nuid_test2, sizeof(nuid))); - PrintAndLogEx(SUCCESS, "Selftest2 %s\n", test2 ? _GREEN_("OK") : _RED_("Fail")); - return 0; + PrintAndLogEx((test2) ? SUCCESS : FAILED, "2. %s -> %s ( %s )\n" + , sprint_hex_inrow(uid_test2, sizeof(uid_test2)) + , sprint_hex(nuid, sizeof(nuid)) + , test2 ? _GREEN_("ok") : _RED_("fail") + ); + + return PM3_SUCCESS; } - param_gethex_ex(Cmd, 0, uid, &len); - if (len % 2 || len != 14) return usage_analyse_nuid(); + if (uidlen != 7) { + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; + } - generate4bNUID(uid, nuid); + mfc_generate4b_nuid(uid, nuid); - PrintAndLogEx(NORMAL, "UID | %s \n", sprint_hex(uid, 7)); - PrintAndLogEx(NORMAL, "NUID | %s \n", sprint_hex(nuid, 4)); - return 0; + PrintAndLogEx(INFO, "UID | %s \n", sprint_hex(uid, 7)); + PrintAndLogEx(INFO, "NUID | %s \n", sprint_hex(nuid, 4)); + return PM3_SUCCESS; } static int CmdAnalyseDemodBuffer(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse demodbuff", + "loads a binary string into demod buffer", + "analyse demodbuff -d 0011101001001011" + ); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_analyse_demodbuffer(); - - int bg = 0, en = 0; - if (param_getptr(Cmd, &bg, &en, 0)) - return usage_analyse_demodbuffer(); - - int len = MIN((en - bg + 1), MAX_DEMOD_BUF_LEN); + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "binary string to load"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + const char *s = arg_get_str(ctx, 1)->sval[0]; + int len = MIN(strlen(s), MAX_DEMOD_BUF_LEN); // add 1 for null terminator. uint8_t *data = calloc(len + 1, sizeof(uint8_t)); - if (!data) return PM3_EMALLOC; + if (data == NULL) { + CLIParserFree(ctx); + return PM3_EMALLOC; + } - for (int i = 0; bg <= en; bg++, i++) { - char c = Cmd[bg]; + for (int i = 0; i <= strlen(s); i++) { + char c = s[i]; if (c == '1') DemodBuffer[i] = 1; if (c == '0') @@ -917,27 +965,116 @@ static int CmdAnalyseDemodBuffer(const char *Cmd) { PrintAndLogEx(NORMAL, "%c" NOLF, c); } - PrintAndLogEx(NORMAL, ""); + CLIParserFree(ctx); + PrintAndLogEx(NORMAL, ""); DemodBufferLen = len; free(data); + PrintAndLogEx(HINT, "Use `" _YELLOW_("data print") "` to view demod buffer"); return PM3_SUCCESS; } static int CmdAnalyseFreq(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyse freq", + "calc wave lengths", + "analyse freq" + ); -// char cmdp = tolower(param_getchar(Cmd, 0)); -// if (strlen(Cmd) == 0 || cmdp == 'h') return usage_analyse_freq(); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); const double c = 299792458; double len_125 = c / 125000; double len_134 = c / 134000; double len_1356 = c / 13560000; + double rf_range_125 = len_125 / (M_PI * 2); + double rf_range_134 = len_134 / (M_PI * 2); + double rf_range_1356 = len_1356 / (M_PI * 2); + PrintAndLogEx(INFO, "Wavelengths"); - PrintAndLogEx(INFO, " 125 kHz has %f meters", len_125); - PrintAndLogEx(INFO, " 134 kHz has %f meters", len_134); - PrintAndLogEx(INFO, " 13.56 mHz has %f meters", len_1356); + PrintAndLogEx(INFO, " 125 kHz has %f m, rf range %f m", len_125, rf_range_125); + PrintAndLogEx(INFO, " 134 kHz has %f m, rf range %f m", len_134, rf_range_134); + PrintAndLogEx(INFO, " 13.56 mHz has %f m, rf range %f m", len_1356, rf_range_1356); + return PM3_SUCCESS; +} + +static int CmdAnalyseFoo(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "analyze foo", + "experiments of cliparse", + "analyse foo -r a0000000a0002021" + ); + + void *argtable[] = { + arg_param_begin, + arg_strx0("r", "raw", "", "raw bytes (strx)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + // raw param + int datalen = 256; + uint8_t data[256]; + CLIGetHexWithReturn(ctx, 1, data, &datalen); + + int data3len = 512; + uint8_t data3[512]; + CLIGetStrWithReturn(ctx, 1, data3, &data3len); + + CLIParserFree(ctx); + + PrintAndLogEx(INFO, "-r"); + PrintAndLogEx(INFO, "Got: %s", sprint_hex_inrow(data, datalen)); + PrintAndLogEx(INFO, "Got: %s", data3); + + ClearGraph(false); + GraphTraceLen = 15000; + + for (int i = 0; i < 4095; i++) { + int o = 0; + + // 0010 0000 + if (i & 0x2000) o |= 0x80; // corr_i_accum[13] + // 0001 1100 + if (i & 0x1C00) o |= 0x40; // corr_i_accum[12] | corr_i_accum[11] | corr_i_accum[10] + // 0000 1110 + if (i & 0x0E00) o |= 0x20; // corr_i_accum[12] | corr_i_accum[11] | corr_i_accum[9], + o |= (i & 0x1F0) >> 4; // corr_i_accum[8:4] + + GraphBuffer[i] = o; + } + + for (int i = 0; i < 4095; i++) { + int o = 0; + + // Send 8 bits of in phase tag signal + //if (corr_i_accum[13:11] == 3'b000 || corr_i_accum[13:11] == 3'b111) + if ((i & 0x3800) == 0 || (i & 0x3800) == 0x3800) { + o |= (i & 0xFF0) >> 4; // corr_i_out <= corr_i_accum[11:4]; + } else { + // truncate to maximum value + //if (corr_i_accum[13] == 1'b0) + if ((i & 0x2000) == 0) { + o |= 0x7f; // corr_i_out <= 8'b01111111; + } + } + GraphBuffer[i + 5000] = o; + } + + for (int i = 0; i < 4095; i++) { + int o = i >> 5; + GraphBuffer[i + 10000] = o; + } + + RepaintGraphWindow(); + ShowGraphWindow(); return PM3_SUCCESS; } @@ -953,6 +1090,7 @@ static command_t CommandTable[] = { {"nuid", CmdAnalyseNuid, AlwaysAvailable, "create NUID from 7byte UID"}, {"demodbuff", CmdAnalyseDemodBuffer, AlwaysAvailable, "Load binary string to demodbuffer"}, {"freq", CmdAnalyseFreq, AlwaysAvailable, "Calc wave lengths"}, + {"foo", CmdAnalyseFoo, AlwaysAvailable, "muxer"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmddata.c b/client/src/cmddata.c index daff73722..82f05bb59 100644 --- a/client/src/cmddata.c +++ b/client/src/cmddata.c @@ -769,22 +769,22 @@ static int CmdBiphaseDecodeRaw(const char *Cmd) { int ASKbiphaseDemod(int offset, int clk, int invert, int maxErr, bool verbose) { //ask raw demod GraphBuffer first - uint8_t BitStream[MAX_DEMOD_BUF_LEN]; - size_t size = getFromGraphBuf(BitStream); + uint8_t bs[MAX_DEMOD_BUF_LEN]; + size_t size = getFromGraphBuf(bs); if (size == 0) { PrintAndLogEx(DEBUG, "DEBUG: no data in graphbuf"); return PM3_ESOFT; } int startIdx = 0; //invert here inverts the ask raw demoded bits which has no effect on the demod, but we need the pointer - int errCnt = askdemod_ext(BitStream, &size, &clk, &invert, maxErr, 0, 0, &startIdx); + int errCnt = askdemod_ext(bs, &size, &clk, &invert, maxErr, 0, 0, &startIdx); if (errCnt < 0 || errCnt > maxErr) { PrintAndLogEx(DEBUG, "DEBUG: no data or error found %d, clock: %d", errCnt, clk); return PM3_ESOFT; } //attempt to Biphase decode BitStream - errCnt = BiphaseRawDecode(BitStream, &size, &offset, invert); + errCnt = BiphaseRawDecode(bs, &size, &offset, invert); if (errCnt < 0) { if (g_debugMode || verbose) PrintAndLogEx(DEBUG, "DEBUG: Error BiphaseRawDecode: %d", errCnt); return PM3_ESOFT; @@ -795,7 +795,7 @@ int ASKbiphaseDemod(int offset, int clk, int invert, int maxErr, bool verbose) { } //success set DemodBuffer and return - setDemodBuff(BitStream, size, 0); + setDemodBuff(bs, size, 0); setClockGrid(clk, startIdx + clk * offset / 2); if (g_debugMode || verbose) { PrintAndLogEx(DEBUG, "Biphase Decoded using offset %d | clock %d | #errors %d | start index %d\ndata\n", offset, clk, errCnt, (startIdx + clk * offset / 2)); @@ -1428,7 +1428,7 @@ static int CmdRawDemod(const char *Cmd) { else if (str_startswith(Cmd, "am")) ans = Cmdaskmandemod(Cmd + 2); else if (str_startswith(Cmd, "ar")) ans = Cmdaskrawdemod(Cmd + 2); else if (str_startswith(Cmd, "nr") || Cmd[0] == 'n') ans = CmdNRZrawDemod(Cmd + 2); - else if (str_startswith(Cmd, "p1") || Cmd[0] == 'p') ans = CmdPSK1rawDemod(Cmd + 2); + else if (str_startswith(Cmd, "p1")) ans = CmdPSK1rawDemod(Cmd + 2); else if (str_startswith(Cmd, "p2")) ans = CmdPSK2rawDemod(Cmd + 2); else PrintAndLogEx(WARNING, "Unknown modulation entered - see help ('h') for parameter structure"); @@ -1576,7 +1576,7 @@ int getSamplesEx(uint32_t start, uint32_t end, bool verbose) { uint32_t n = end - start; - if (n <= 0 || n > pm3_capabilities.bigbuf_size - 1) + if (n == 0 || n > pm3_capabilities.bigbuf_size - 1) n = pm3_capabilities.bigbuf_size - 1; if (verbose) diff --git a/client/src/cmdflashmem.c b/client/src/cmdflashmem.c index 7f663c45b..23acad47d 100644 --- a/client/src/cmdflashmem.c +++ b/client/src/cmdflashmem.c @@ -11,7 +11,7 @@ #include #include "cmdparser.h" // command_t #include "cliparser.h" -#include "pmflash.h" +#include "pmflash.h" // rdv40validation_t #include "fileutils.h" // saveFile #include "comms.h" // getfromdevice #include "cmdflashmemspiffs.h" // spiffs commands @@ -26,6 +26,123 @@ static int CmdHelp(const char *Cmd); +//------------------------------------------------------------------------------------- +// RRG Public RSA Key +#define RRG_RSA_KEY_LEN 128 + +// public key Exponent E +#define RRG_RSA_E "010001" + +// public key modulus N +#define RRG_RSA_N "E28D809BF323171D11D1ACA4C32A5B7E0A8974FD171E75AD120D60E9B76968FF" \ + "4B0A6364AE50583F9555B8EE1A725F279E949246DF0EFCE4C02B9F3ACDCC623F" \ + "9337F21C0C066FFB703D8BFCB5067F309E056772096642C2B1A8F50305D5EC33" \ + "DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5" + +// Following example RSA-1024 keypair, for test purposes (from common/polarssl/rsa.c) +// private key - Exponent D +#define RSA_D "24BF6185468786FDD303083D25E64EFC" \ + "66CA472BC44D253102F8B4A9D3BFA750" \ + "91386C0077937FE33FA3252D28855837" \ + "AE1B484A8A9A45F7EE8C0C634F99E8CD" \ + "DF79C5CE07EE72C7F123142198164234" \ + "CABB724CF78B8173B9F880FC86322407" \ + "AF1FEDFDDE2BEB674CA15F3E81A1521E" \ + "071513A1E85B5DFA031F21ECAE91A34D" + +// prime P +#define RSA_P "C36D0EB7FCD285223CFB5AABA5BDA3D8" \ + "2C01CAD19EA484A87EA4377637E75500" \ + "FCB2005C5C7DD6EC4AC023CDA285D796" \ + "C3D9E75E1EFC42488BB4F1D13AC30A57" + +// prime Q +#define RSA_Q "C000DF51A7C77AE8D7C7370C1FF55B69" \ + "E211C2B9E5DB1ED0BF61D0D9899620F4" \ + "910E4168387E3C30AA1E00C339A79508" \ + "8452DD96A9A5EA5D9DCA68DA636032AF" + +#define RSA_DP "C1ACF567564274FB07A0BBAD5D26E298" \ + "3C94D22288ACD763FD8E5600ED4A702D" \ + "F84198A5F06C2E72236AE490C93F07F8" \ + "3CC559CD27BC2D1CA488811730BB5725" + +#define RSA_DQ "4959CBF6F8FEF750AEE6977C155579C7" \ + "D8AAEA56749EA28623272E4F7D0592AF" \ + "7C1F1313CAC9471B5C523BFE592F517B" \ + "407A1BD76C164B93DA2D32A383E58357" + +#define RSA_QP "9AE7FBC99546432DF71896FC239EADAE" \ + "F38D18D2B2F0E2DD275AA977E2BF4411" \ + "F5A3B2A5D33605AEBBCCBA7FEB9F2D2F" \ + "A74206CEC169D74BF5A8C50D6F48EA08" + +int rdv4_get_signature(rdv40_validation_t *out) { + if (out == NULL) { + return PM3_EINVARG; + } + + clearCommandBuffer(); + SendCommandNG(CMD_FLASHMEM_INFO, NULL, 0); + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { + PrintAndLogEx(WARNING, "timeout while waiting for reply"); + return PM3_ETIMEOUT; + } + + uint8_t isok = resp.oldarg[0] & 0xFF; + if (isok == false) { + PrintAndLogEx(FAILED, "fail reading from flashmemory"); + return PM3_EFLASH; + } + + //rdv40_validation_t mem; + memcpy(out, (rdv40_validation_t *)resp.data.asBytes, sizeof(rdv40_validation_t)); + return PM3_SUCCESS; +} + +// validate signature +int rdv4_validate(rdv40_validation_t *mem) { + + // Flash ID hash (sha1) + uint8_t sha_hash[20] = {0}; + mbedtls_sha1(mem->flashid, sizeof(mem->flashid), sha_hash); + + // set up RSA + mbedtls_rsa_context rsa; + mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0); + rsa.len = RRG_RSA_KEY_LEN; + mbedtls_mpi_read_string(&rsa.N, 16, RRG_RSA_N); + mbedtls_mpi_read_string(&rsa.E, 16, RRG_RSA_E); + + // Verify (public key) + int is_verified = mbedtls_rsa_pkcs1_verify(&rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, 20, sha_hash, mem->signature); + mbedtls_rsa_free(&rsa); + + if (is_verified == 0) { + return PM3_SUCCESS; + } + return PM3_EFAILED; +} + +static int rdv4_sign_write(uint8_t *signature, uint8_t slen){ + // save to mem + clearCommandBuffer(); + PacketResponseNG resp; + SendCommandOLD(CMD_FLASHMEM_WRITE, FLASH_MEM_SIGNATURE_OFFSET, FLASH_MEM_SIGNATURE_LEN, 0, signature, slen); + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { + PrintAndLogEx(WARNING, "timeout while waiting for reply."); + } else { + if (!resp.oldarg[0]) { + PrintAndLogEx(FAILED, "Writing signature ( "_RED_("fail") ")"); + } else { + PrintAndLogEx(SUCCESS, "Writing signature ( "_GREEN_("ok") " ) at offset %u", FLASH_MEM_SIGNATURE_OFFSET); + return PM3_SUCCESS; + } + } + return PM3_EFAILED; +} + static int CmdFlashmemSpiBaudrate(const char *Cmd) { CLIParserContext *ctx; @@ -344,23 +461,14 @@ static int CmdFlashMemInfo(const char *Cmd) { // shall_write = arg_get_lit(ctx, 2); CLIParserFree(ctx); - clearCommandBuffer(); - SendCommandNG(CMD_FLASHMEM_INFO, NULL, 0); - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 2500) == false) { - PrintAndLogEx(WARNING, "timeout while waiting for reply"); - return PM3_ETIMEOUT; - } - - uint8_t isok = resp.oldarg[0] & 0xFF; - if (isok == false) { - PrintAndLogEx(FAILED, "failed"); - return PM3_EFLASH; - } - - // validate signature here + // validate signature data rdv40_validation_t mem; - memcpy(&mem, (rdv40_validation_t *)resp.data.asBytes, sizeof(rdv40_validation_t)); + int res = rdv4_get_signature(&mem); + if (res != PM3_SUCCESS) { + return res; + } + + res = rdv4_validate(&mem); // Flash ID hash (sha1) uint8_t sha_hash[20] = {0}; @@ -369,7 +477,6 @@ static int CmdFlashMemInfo(const char *Cmd) { // print header PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Flash memory Information") " ---------"); -// PrintAndLogEx(INFO, "-----------------------------------------------------------------"); PrintAndLogEx(INFO, "ID................... %s", sprint_hex_inrow(mem.flashid, sizeof(mem.flashid))); PrintAndLogEx(INFO, "SHA1................. %s", sprint_hex_inrow(sha_hash, sizeof(sha_hash))); PrintAndLogEx(NORMAL, ""); @@ -378,69 +485,16 @@ static int CmdFlashMemInfo(const char *Cmd) { PrintAndLogEx(INFO, " %s", sprint_hex_inrow(mem.signature + (i * 32), 32)); } -//------------------------------------------------------------------------------- -// RRG Public RSA Key -// - -// public key Exponent E -#define RSA_E "010001" - -// public key modulus N -#define RSA_N "E28D809BF323171D11D1ACA4C32A5B7E0A8974FD171E75AD120D60E9B76968FF" \ - "4B0A6364AE50583F9555B8EE1A725F279E949246DF0EFCE4C02B9F3ACDCC623F" \ - "9337F21C0C066FFB703D8BFCB5067F309E056772096642C2B1A8F50305D5EC33" \ - "DB7FB5A3C8AC42EB635AE3C148C910750ABAA280CE82DC2F180F49F30A1393B5" - -//------------------------------------------------------------------------------- -// Example RSA-1024 keypair, for test purposes (from common/polarssl/rsa.c) -// - -// private key Exponent D -#define RSA_D "24BF6185468786FDD303083D25E64EFC" \ - "66CA472BC44D253102F8B4A9D3BFA750" \ - "91386C0077937FE33FA3252D28855837" \ - "AE1B484A8A9A45F7EE8C0C634F99E8CD" \ - "DF79C5CE07EE72C7F123142198164234" \ - "CABB724CF78B8173B9F880FC86322407" \ - "AF1FEDFDDE2BEB674CA15F3E81A1521E" \ - "071513A1E85B5DFA031F21ECAE91A34D" - -// prime P -#define RSA_P "C36D0EB7FCD285223CFB5AABA5BDA3D8" \ - "2C01CAD19EA484A87EA4377637E75500" \ - "FCB2005C5C7DD6EC4AC023CDA285D796" \ - "C3D9E75E1EFC42488BB4F1D13AC30A57" - -// prime Q -#define RSA_Q "C000DF51A7C77AE8D7C7370C1FF55B69" \ - "E211C2B9E5DB1ED0BF61D0D9899620F4" \ - "910E4168387E3C30AA1E00C339A79508" \ - "8452DD96A9A5EA5D9DCA68DA636032AF" - -#define RSA_DP "C1ACF567564274FB07A0BBAD5D26E298" \ - "3C94D22288ACD763FD8E5600ED4A702D" \ - "F84198A5F06C2E72236AE490C93F07F8" \ - "3CC559CD27BC2D1CA488811730BB5725" - -#define RSA_DQ "4959CBF6F8FEF750AEE6977C155579C7" \ - "D8AAEA56749EA28623272E4F7D0592AF" \ - "7C1F1313CAC9471B5C523BFE592F517B" \ - "407A1BD76C164B93DA2D32A383E58357" - -#define RSA_QP "9AE7FBC99546432DF71896FC239EADAE" \ - "F38D18D2B2F0E2DD275AA977E2BF4411" \ - "F5A3B2A5D33605AEBBCCBA7FEB9F2D2F" \ - "A74206CEC169D74BF5A8C50D6F48EA08" - -#define KEY_LEN 128 - mbedtls_rsa_context rsa; mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0); - rsa.len = KEY_LEN; + rsa.len = RRG_RSA_KEY_LEN; - mbedtls_mpi_read_string(&rsa.N, 16, RSA_N); - mbedtls_mpi_read_string(&rsa.E, 16, RSA_E); + // add public key + mbedtls_mpi_read_string(&rsa.N, 16, RRG_RSA_N); + mbedtls_mpi_read_string(&rsa.E, 16, RRG_RSA_E); + + // add private key mbedtls_mpi_read_string(&rsa.D, 16, RSA_D); mbedtls_mpi_read_string(&rsa.P, 16, RSA_P); mbedtls_mpi_read_string(&rsa.Q, 16, RSA_Q); @@ -448,8 +502,6 @@ static int CmdFlashMemInfo(const char *Cmd) { mbedtls_mpi_read_string(&rsa.DQ, 16, RSA_DQ); mbedtls_mpi_read_string(&rsa.QP, 16, RSA_QP); - bool is_keyok = (mbedtls_rsa_check_pubkey(&rsa) == 0 || mbedtls_rsa_check_privkey(&rsa) == 0); - PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("RDV4 RSA Public key") " --------------"); @@ -466,48 +518,35 @@ static int CmdFlashMemInfo(const char *Cmd) { PrintAndLogEx(INFO, " %.64s", str_pk + 64); PrintAndLogEx(INFO, " %.64s", str_pk + 128); PrintAndLogEx(INFO, " %.64s", str_pk + 192); - PrintAndLogEx(NORMAL, ""); - const char *msgkey = "RSA key validation... "; - if (is_keyok) - PrintAndLogEx(SUCCESS, "%s( " _GREEN_("ok") " )", msgkey); - else - PrintAndLogEx(FAILED, "%s( " _RED_("failed") " )", msgkey); - // - uint8_t from_device[KEY_LEN]; - uint8_t sign[KEY_LEN]; + bool is_keyok = (mbedtls_rsa_check_pubkey(&rsa) == 0 || mbedtls_rsa_check_privkey(&rsa) == 0); + PrintAndLogEx( + (is_keyok) ? SUCCESS : FAILED, + "RSA key validation... ( %s )", + (is_keyok) ? _GREEN_("ok") : _RED_("fail") + ); // to be verified - memcpy(from_device, mem.signature, KEY_LEN); + uint8_t from_device[RRG_RSA_KEY_LEN]; + memcpy(from_device, mem.signature, RRG_RSA_KEY_LEN); - // to be signed (all zeros - memset(sign, 0, KEY_LEN); + // to be signed + uint8_t sign[RRG_RSA_KEY_LEN]; + memset(sign, 0, RRG_RSA_KEY_LEN); // Signing (private key) if (shall_sign) { int is_signed = mbedtls_rsa_pkcs1_sign(&rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE, MBEDTLS_MD_SHA1, 20, sha_hash, sign); - const char *msgsign = "RSA signing.......... "; - if (is_signed == 0) - PrintAndLogEx(SUCCESS, "%s( " _GREEN_("ok") " )", msgsign); - else - PrintAndLogEx(FAILED, "%s( " _RED_("failed") " )", msgsign); - + PrintAndLogEx( + (is_signed == 0) ? SUCCESS : FAILED, + "RSA signing.......... ( %s )", + (is_signed == 0) ? _GREEN_("ok") : _RED_("fail") + ); + if (shall_write) { - // save to mem - clearCommandBuffer(); - SendCommandOLD(CMD_FLASHMEM_WRITE, FLASH_MEM_SIGNATURE_OFFSET, FLASH_MEM_SIGNATURE_LEN, 0, sign, sizeof(sign)); - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { - PrintAndLogEx(WARNING, "timeout while waiting for reply."); - } else { - - if (!resp.oldarg[0]) - PrintAndLogEx(FAILED, "Writing signature failed"); - else - PrintAndLogEx(SUCCESS, "Writing signature ok [offset: %u]", FLASH_MEM_SIGNATURE_OFFSET); - - } + rdv4_sign_write(sign, RRG_RSA_KEY_LEN); } PrintAndLogEx(INFO, "Signed"); for (int i = 0; i < (sizeof(sign) / 32); i++) { @@ -517,14 +556,15 @@ static int CmdFlashMemInfo(const char *Cmd) { // Verify (public key) int is_verified = mbedtls_rsa_pkcs1_verify(&rsa, NULL, NULL, MBEDTLS_RSA_PUBLIC, MBEDTLS_MD_SHA1, 20, sha_hash, from_device); - const char *msgverify = "RSA verification..... "; - if (is_verified == 0) - PrintAndLogEx(SUCCESS, "%s( " _GREEN_("ok") " )", msgverify); - else - PrintAndLogEx(FAILED, "%s( " _RED_("failed") " )", msgverify); + mbedtls_rsa_free(&rsa); + + PrintAndLogEx( + (is_verified == 0) ? SUCCESS : FAILED, + "RSA verification..... ( %s )", + (is_verified == 0) ? _GREEN_("ok") : _RED_("fail") + ); PrintAndLogEx(NORMAL, ""); - mbedtls_rsa_free(&rsa); return PM3_SUCCESS; } diff --git a/client/src/cmdflashmem.h b/client/src/cmdflashmem.h index 011b57a14..5a8f6eebc 100644 --- a/client/src/cmdflashmem.h +++ b/client/src/cmdflashmem.h @@ -12,6 +12,7 @@ #define CMDFLASHMEM_H__ #include "common.h" +#include "pmflash.h" // rdv40validation_t typedef enum { DICTIONARY_NONE = 0, @@ -21,5 +22,6 @@ typedef enum { } Dictionary_t; int CmdFlashMem(const char *Cmd); - +int rdv4_get_signature(rdv40_validation_t *out); +int rdv4_validate(rdv40_validation_t *mem); #endif diff --git a/client/src/cmdhf.c b/client/src/cmdhf.c index fa100e13d..2bc6edbfe 100644 --- a/client/src/cmdhf.c +++ b/client/src/cmdhf.c @@ -14,17 +14,18 @@ #include "cmdhf.h" #include // tolower - #include "cmdparser.h" // command_t -#include "cliparser.h" // parse +#include "cliparser.h" // parse #include "comms.h" // clearCommandBuffer #include "lfdemod.h" // computeSignalProperties #include "cmdhf14a.h" // ISO14443-A #include "cmdhf14b.h" // ISO14443-B #include "cmdhf15.h" // ISO15693 #include "cmdhfepa.h" +#include "cmdhfemrtd.h" // eMRTD #include "cmdhflegic.h" // LEGIC #include "cmdhficlass.h" // ICLASS +#include "cmdhfjooki.h" // MFU based Jooki #include "cmdhfmf.h" // CLASSIC #include "cmdhfmfu.h" // ULTRALIGHT/NTAG etc #include "cmdhfmfp.h" // Mifare Plus @@ -46,50 +47,20 @@ static int CmdHelp(const char *Cmd); -static int usage_hf_search(void) { - PrintAndLogEx(NORMAL, "Usage: hf search"); - PrintAndLogEx(NORMAL, "Will try to find a HF read out of the unknown tag."); - PrintAndLogEx(NORMAL, "Continues to search for all different HF protocols"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - This help"); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_sniff(void) { - PrintAndLogEx(NORMAL, "The high frequency sniffer will assign all available memory on device for sniffed data"); - PrintAndLogEx(NORMAL, "Use " _YELLOW_("'data samples'")" command to download from device, and " _YELLOW_("'data plot'")" to look at it"); - PrintAndLogEx(NORMAL, "Press button to quit the sniffing.\n"); - PrintAndLogEx(NORMAL, "Usage: hf sniff "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - This help"); - PrintAndLogEx(NORMAL, " - skip sample pairs"); - PrintAndLogEx(NORMAL, " - skip number of triggers"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf sniff")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf sniff 1000 0")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_tune(void) { - PrintAndLogEx(NORMAL, "Continuously measure HF antenna tuning."); - PrintAndLogEx(NORMAL, "Press button or `enter` to interrupt."); - PrintAndLogEx(NORMAL, "Usage: hf tune [h] []"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - This help"); - PrintAndLogEx(NORMAL, " - number of iterations (default: 0=infinite)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf tune 1")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - int CmdHFSearch(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_hf_search(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf search", + "Will try to find a HF read out of the unknown tag.\n" + "Continues to search for all different HF protocols.", + "hf search" + ); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); int res = PM3_ESOFT; @@ -141,7 +112,7 @@ int CmdHFSearch(const char *Cmd) { PROMPT_CLEARLINE; PrintAndLogEx(INPLACE, " Searching for LEGIC tag..."); if (IfPm3Legicrf()) { - if (readLegicUid(false) == PM3_SUCCESS) { + if (readLegicUid(false, false) == PM3_SUCCESS) { PrintAndLogEx(SUCCESS, "\nValid " _GREEN_("LEGIC Prime tag") " found\n"); res = PM3_SUCCESS; } @@ -189,18 +160,51 @@ int CmdHFSearch(const char *Cmd) { PROMPT_CLEARLINE; if (res != PM3_SUCCESS) { - PrintAndLogEx(WARNING, _RED_("No known/supported 13.56 MHz tags found")); res = PM3_ESOFT; } + DropField(); return res; } int CmdHFTune(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_hf_tune(); - int iter = param_get32ex(Cmd, 0, 0, 10); + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf tune", + "Continuously measure HF antenna tuning.\n" + "Press button or to interrupt.", + "hf tune\n" + "hf tune --mix" + ); + + void *argtable[] = { + arg_param_begin, + arg_u64_0("n", "iter", "", "number of iterations (default: 0=infinite)"), + arg_lit0(NULL, "bar", "bar style"), + arg_lit0(NULL, "mix", "mixed style"), + arg_lit0(NULL, "value", "values style"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + uint32_t iter = arg_get_u32_def(ctx, 1, 0); + bool is_bar = arg_get_lit(ctx, 2); + bool is_mix = arg_get_lit(ctx, 3); + bool is_value = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + + if ((is_bar + is_mix + is_value) > 1) { + PrintAndLogEx(ERR, "Select only one output style"); + return PM3_EINVARG; + } + + barMode_t style = session.bar_mode; + if (is_bar) + style = STYLE_BAR; + if (is_mix) + style = STYLE_MIXED; + if (is_value) + style = STYLE_VALUE; PrintAndLogEx(INFO, "Measuring HF antenna, click " _GREEN_("pm3 button") " or press " _GREEN_("Enter") " to exit"); PacketResponseNG resp; @@ -214,6 +218,12 @@ int CmdHFTune(const char *Cmd) { } mode[0] = 2; + + uint32_t max = 0xFFFF; + bool first = true; + + print_progress(0, max, style); + // loop forever (till button pressed) if iter = 0 (default) for (uint8_t i = 0; iter == 0 || i < iter; i++) { if (kbd_enter_pressed()) { @@ -224,15 +234,23 @@ int CmdHFTune(const char *Cmd) { if (!WaitForResponseTimeout(CMD_MEASURE_ANTENNA_TUNING_HF, &resp, 1000)) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(WARNING, "Timeout while waiting for Proxmark HF measure, aborting"); - return PM3_ETIMEOUT; + break; } if ((resp.status == PM3_EOPABORTED) || (resp.length != sizeof(uint16_t))) { + PrintAndLogEx(NORMAL, ""); break; } uint16_t volt = resp.data.asDwords[0] & 0xFFFF; - PrintAndLogEx(INPLACE, " %u mV / %2u V", volt, (uint16_t)(volt / 1000)); + if (first) { + max = (volt * 1.03); + first = false; + } + if (volt > max) { + max = (volt * 1.03); + } + print_progress(volt, max, style); } mode[0] = 3; @@ -241,7 +259,7 @@ int CmdHFTune(const char *Cmd) { PrintAndLogEx(WARNING, "Timeout while waiting for Proxmark HF shutdown, aborting"); return PM3_ETIMEOUT; } - PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "\x1b%c[2K\r", 30); PrintAndLogEx(INFO, "Done."); return PM3_SUCCESS; } @@ -251,16 +269,31 @@ int CmdHFTune(const char *Cmd) { // Takes all available bigbuff memory // data sample to download? Not sure what we can do with the data. int CmdHFSniff(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_hf_sniff(); + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf sniff", + "The high frequency sniffer will assign all available memory on device for sniffed data.\n" + "Use `data samples` to download from device and `data plot` to visualize it.\n" + "Press button to quit the sniffing.", + "hf sniff\n" + "hf sniff --sp 1000 --st 0 -> skip 1000 pairs, skip 0 triggers" + ); + void *argtable[] = { + arg_param_begin, + arg_u64_0(NULL, "sp", "", "skip sample pairs"), + arg_u64_0(NULL, "st", "", "skip number of triggers"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); struct { uint32_t samplesToSkip; uint32_t triggersToSkip; } PACKED params; - params.samplesToSkip = param_get32ex(Cmd, 0, 0, 10); - params.triggersToSkip = param_get32ex(Cmd, 1, 0, 10); + params.samplesToSkip = arg_get_u32_def(ctx, 1, 0); + params.triggersToSkip = arg_get_u32_def(ctx, 2, 0); + CLIParserFree(ctx); clearCommandBuffer(); SendCommandNG(CMD_HF_SNIFF, (uint8_t *)¶ms, sizeof(params)); @@ -357,31 +390,33 @@ int CmdHFPlot(const char *Cmd) { static command_t CommandTable[] = { {"--------", CmdHelp, AlwaysAvailable, "----------------------- " _CYAN_("High Frequency") " -----------------------"}, - {"14a", CmdHF14A, AlwaysAvailable, "{ ISO14443A RFIDs... }"}, - {"14b", CmdHF14B, AlwaysAvailable, "{ ISO14443B RFIDs... }"}, - {"15", CmdHF15, AlwaysAvailable, "{ ISO15693 RFIDs... }"}, -// {"cryptorf", CmdHFCryptoRF, AlwaysAvailable, "{ CryptoRF RFIDs... }"}, - {"epa", CmdHFEPA, AlwaysAvailable, "{ German Identification Card... }"}, - {"felica", CmdHFFelica, AlwaysAvailable, "{ ISO18092 / FeliCa RFIDs... }"}, - {"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"}, - {"iclass", CmdHFiClass, AlwaysAvailable, "{ ICLASS RFIDs... }"}, - {"legic", CmdHFLegic, AlwaysAvailable, "{ LEGIC RFIDs... }"}, - {"lto", CmdHFLTO, AlwaysAvailable, "{ LTO Cartridge Memory RFIDs... }"}, - {"mf", CmdHFMF, AlwaysAvailable, "{ MIFARE RFIDs... }"}, - {"mfp", CmdHFMFP, AlwaysAvailable, "{ MIFARE Plus RFIDs... }"}, - {"mfu", CmdHFMFUltra, AlwaysAvailable, "{ MIFARE Ultralight RFIDs... }"}, - {"mfdes", CmdHFMFDes, AlwaysAvailable, "{ MIFARE Desfire RFIDs... }"}, - {"st", CmdHF_ST, AlwaysAvailable, "{ ST Rothult RFIDs... }"}, - {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, - {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, - {"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"}, + {"14a", CmdHF14A, AlwaysAvailable, "{ ISO14443A RFIDs... }"}, + {"14b", CmdHF14B, AlwaysAvailable, "{ ISO14443B RFIDs... }"}, + {"15", CmdHF15, AlwaysAvailable, "{ ISO15693 RFIDs... }"}, +// {"cryptorf", CmdHFCryptoRF, AlwaysAvailable, "{ CryptoRF RFIDs... }"}, + {"epa", CmdHFEPA, AlwaysAvailable, "{ German Identification Card... }"}, + {"emrtd", CmdHFeMRTD, AlwaysAvailable, "{ Machine Readable Travel Document... }"}, + {"felica", CmdHFFelica, AlwaysAvailable, "{ ISO18092 / FeliCa RFIDs... }"}, + {"fido", CmdHFFido, AlwaysAvailable, "{ FIDO and FIDO2 authenticators... }"}, + {"jooki", CmdHF_Jooki, AlwaysAvailable, "{ Jooki RFIDs... }"}, + {"iclass", CmdHFiClass, AlwaysAvailable, "{ ICLASS RFIDs... }"}, + {"legic", CmdHFLegic, AlwaysAvailable, "{ LEGIC RFIDs... }"}, + {"lto", CmdHFLTO, AlwaysAvailable, "{ LTO Cartridge Memory RFIDs... }"}, + {"mf", CmdHFMF, AlwaysAvailable, "{ MIFARE RFIDs... }"}, + {"mfp", CmdHFMFP, AlwaysAvailable, "{ MIFARE Plus RFIDs... }"}, + {"mfu", CmdHFMFUltra, AlwaysAvailable, "{ MIFARE Ultralight RFIDs... }"}, + {"mfdes", CmdHFMFDes, AlwaysAvailable, "{ MIFARE Desfire RFIDs... }"}, + {"st", CmdHF_ST, AlwaysAvailable, "{ ST Rothult RFIDs... }"}, + {"thinfilm", CmdHFThinfilm, AlwaysAvailable, "{ Thinfilm RFIDs... }"}, + {"topaz", CmdHFTopaz, AlwaysAvailable, "{ TOPAZ (NFC Type 1) RFIDs... }"}, + {"waveshare", CmdHFWaveshare, AlwaysAvailable, "{ Waveshare NFC ePaper... }"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("General") " ---------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"list", CmdTraceList, AlwaysAvailable, "List protocol data in trace buffer"}, + {"list", CmdTraceList, AlwaysAvailable, "List protocol data in trace buffer"}, {"plot", CmdHFPlot, IfPm3Hfplot, "Plot signal"}, {"tune", CmdHFTune, IfPm3Present, "Continuously measure HF antenna tuning"}, {"search", CmdHFSearch, AlwaysAvailable, "Search for known HF tags"}, - {"sniff", CmdHFSniff, IfPm3Hfsniff, " Generic HF Sniff"}, + {"sniff", CmdHFSniff, IfPm3Hfsniff, "Generic HF Sniff"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhf14a.c b/client/src/cmdhf14a.c index 576979aad..2b1631963 100644 --- a/client/src/cmdhf14a.c +++ b/client/src/cmdhf14a.c @@ -26,9 +26,8 @@ #include "util_posix.h" // msclock #include "aidsearch.h" #include "cmdhf.h" // handle HF plot -#include "protocols.h" // MAGIC_GEN_1A #include "cliparser.h" -#include "protocols.h" // definitions of ISO14A/7816 protocol +#include "protocols.h" // definitions of ISO14A/7816 protocol, MAGIC_GEN_1A #include "emv/apduinfo.h" // GetAPDUCodeDescription bool APDUInFramingEnable = true; @@ -171,93 +170,6 @@ const char *getTagInfo(uint8_t uid) { static uint16_t frameLength = 0; uint16_t atsFSC[] = {16, 24, 32, 40, 48, 64, 96, 128, 256}; -static int usage_hf_14a_config(void) { - PrintAndLogEx(NORMAL, "Usage: hf 14a config [a 0|1|2] [b 0|1|2] [2 0|1|2] [3 0|1|2]"); - PrintAndLogEx(NORMAL, "\nOptions:"); - PrintAndLogEx(NORMAL, " h This help"); - PrintAndLogEx(NORMAL, " a 0|1|2 ATQA<>anticollision: 0=follow standard 1=execute anticol 2=skip anticol"); - PrintAndLogEx(NORMAL, " b 0|1|2 BCC: 0=follow standard 1=use fixed BCC 2=use card BCC"); - PrintAndLogEx(NORMAL, " 2 0|1|2 SAK<>CL2: 0=follow standard 1=execute CL2 2=skip CL2"); - PrintAndLogEx(NORMAL, " 3 0|1|2 SAK<>CL3: 0=follow standard 1=execute CL3 2=skip CL3"); - PrintAndLogEx(NORMAL, " r 0|1|2 SAK<>ATS: 0=follow standard 1=execute RATS 2=skip RATS"); - PrintAndLogEx(NORMAL, "\nExamples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config ")" Print current configuration"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 ")" Force execution of anticollision"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 ")" Restore ATQA interpretation"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config b 1 ")" Force fix of bad BCC in anticollision"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config b 0 ")" Restore BCC check"); - PrintAndLogEx(NORMAL, "\nExamples to revive Gen2/DirectWrite magic cards failing at anticollision:"); - PrintAndLogEx(NORMAL, _CYAN_(" MFC 1k 4b UID")":"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 b 2 2 2 r 2")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 11223344440804006263646566676869")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 b 0 2 0 r 0")); - PrintAndLogEx(NORMAL, _CYAN_(" MFC 4k 4b UID")":"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 b 2 2 2 r 2")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 11223344441802006263646566676869")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 b 0 2 0 r 0")); - PrintAndLogEx(NORMAL, _CYAN_(" MFC 1k 7b UID")":"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 b 2 2 1 3 2 r 2")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 04112233445566084400626364656667")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 b 0 2 0 3 0 r 0")); - PrintAndLogEx(NORMAL, _CYAN_(" MFC 4k 7b UID")":"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 b 2 2 1 3 2 r 2")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 04112233445566184200626364656667")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 b 0 2 0 3 0 r 0")); - PrintAndLogEx(NORMAL, _CYAN_(" MFUL ")"/" _CYAN_(" MFUL EV1 ")"/" _CYAN_(" MFULC")":"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 1 b 2 2 1 3 2 r 2")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu setuid 04112233445566")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config a 0 b 0 2 0 3 0 r 0")); - return PM3_SUCCESS; -} - -static int usage_hf_14a_sim(void) { - PrintAndLogEx(NORMAL, "\n Emulating ISO/IEC 14443 type A tag with 4,7 or 10 byte UID\n"); - PrintAndLogEx(NORMAL, "Usage: hf 14a sim [h] t u [n ] [x] [e] [v]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : This help"); - PrintAndLogEx(NORMAL, " t : 1 = MIFARE Classic 1k"); - PrintAndLogEx(NORMAL, " 2 = MIFARE Ultralight"); - PrintAndLogEx(NORMAL, " 3 = MIFARE Desfire"); - PrintAndLogEx(NORMAL, " 4 = ISO/IEC 14443-4"); - PrintAndLogEx(NORMAL, " 5 = MIFARE Tnp3xxx"); - PrintAndLogEx(NORMAL, " 6 = MIFARE Mini"); - PrintAndLogEx(NORMAL, " 7 = AMIIBO (NTAG 215), pack 0x8080"); - PrintAndLogEx(NORMAL, " 8 = MIFARE Classic 4k"); - PrintAndLogEx(NORMAL, " 9 = FM11RF005SH Shanghai Metro"); - PrintAndLogEx(NORMAL, " 10 = JCOP 31/41 Rothult"); - PrintAndLogEx(NORMAL, " u : 4, 7 or 10 byte UID"); - PrintAndLogEx(NORMAL, " n : (Optional) Exit simulation after blocks have been read by reader. 0 = infinite"); - PrintAndLogEx(NORMAL, " x : (Optional) Performs the 'reader attack', nr/ar attack against a reader"); - PrintAndLogEx(NORMAL, " e : (Optional) Fill simulator keys from found keys"); - PrintAndLogEx(NORMAL, " v : (Optional) Verbose"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a sim t 1 u 11223344 x")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a sim t 1 u 11223344")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a sim t 1 u 11223344556677")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a sim t 1 u 112233445566778899AA")); - return PM3_SUCCESS; -} -static int usage_hf_14a_sniff(void) { - PrintAndLogEx(NORMAL, "Collect data from the field and save into command buffer."); - PrintAndLogEx(NORMAL, "Buffer accessible from command 'hf 14a list'"); - PrintAndLogEx(NORMAL, "Usage: hf 14a sniff [c][r]"); - PrintAndLogEx(NORMAL, "c - triggered by first data from card"); - PrintAndLogEx(NORMAL, "r - triggered by first 7-bit request from reader (REQ,WUP,...)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a sniff c r")); - return PM3_SUCCESS; -} - -static int usage_hf_14a_reader(void) { - PrintAndLogEx(NORMAL, "Usage: hf 14a reader [k|s|x] [3]"); - PrintAndLogEx(NORMAL, " k keep the field active after command executed"); - PrintAndLogEx(NORMAL, " s silent (no messages)"); - PrintAndLogEx(NORMAL, " x just drop the signal field"); - PrintAndLogEx(NORMAL, " 3 ISO14443-3 select only (skip RATS)"); - PrintAndLogEx(NORMAL, " @ continuous mode. Updates hf plot as well"); - return PM3_SUCCESS; -} - static int CmdHF14AList(const char *Cmd) { char args[128] = {0}; if (strlen(Cmd) == 0) { @@ -286,142 +198,168 @@ int hf14a_getconfig(hf14a_config *config) { return PM3_SUCCESS; } -int hf14a_setconfig(hf14a_config *config) { +int hf14a_setconfig(hf14a_config *config, bool verbose) { if (!session.pm3_present) return PM3_ENOTTY; clearCommandBuffer(); - if (config != NULL) + if (config != NULL) { SendCommandNG(CMD_HF_ISO14443A_SET_CONFIG, (uint8_t *)config, sizeof(hf14a_config)); - else + if (verbose) { + SendCommandNG(CMD_HF_ISO14443A_PRINT_CONFIG, NULL, 0); + } + } else { SendCommandNG(CMD_HF_ISO14443A_PRINT_CONFIG, NULL, 0); + } return PM3_SUCCESS; } +static int hf_14a_config_example(void) { + PrintAndLogEx(NORMAL, "\nExamples to revive Gen2/DirectWrite magic cards failing at anticollision:"); + PrintAndLogEx(NORMAL, _CYAN_(" MFC 1k 4b UID")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa force --bcc ignore --cl2 skip --rats skip")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 11223344440804006263646566676869")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); + PrintAndLogEx(NORMAL, _CYAN_(" MFC 4k 4b UID")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa force --bcc ignore --cl2 skip --rats skip")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 11223344441802006263646566676869")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); + PrintAndLogEx(NORMAL, _CYAN_(" MFC 1k 7b UID")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa force --bcc ignore --cl2 force --cl3 skip --rats skip")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 04112233445566084400626364656667")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); + PrintAndLogEx(NORMAL, _CYAN_(" MFC 4k 7b UID")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa forcce --bcc ignore --cl2 force --cl3 skip --rats skip")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mf wrbl 0 A FFFFFFFFFFFF 04112233445566184200626364656667")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); + PrintAndLogEx(NORMAL, _CYAN_(" MFUL ")"/" _CYAN_(" MFUL EV1 ")"/" _CYAN_(" MFULC")":"); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --atqa force --bcc ignore --cl2 force --cl3 skip -rats skip")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu setuid 04112233445566")); + PrintAndLogEx(NORMAL, _YELLOW_(" hf 14a config --std")); + return PM3_SUCCESS; +} static int CmdHf14AConfig(const char *Cmd) { - if (!session.pm3_present) return PM3_ENOTTY; - // if called with no params, just print the device config - if (strlen(Cmd) == 0) { - return hf14a_setconfig(NULL); - } + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14a config", + "Configure 14a settings (use with caution)", + "hf 14a config -> Print current configuration\n" + "hf 14a config --std -> Reset default configuration (follow standard)\n" + "hf 14a config --atqa std -> Follow standard\n" + "hf 14a config --atqa force -> Force execution of anticollision\n" + "hf 14a config --atqa skip -> Skip anticollision\n" + "hf 14a config --bcc std -> Follow standard\n" + "hf 14a config --bcc fix -> Fix bad BCC in anticollision\n" + "hf 14a config --bcc ignore -> Ignore bad BCC and use it as such\n" + "hf 14a config --cl2 std -> Follow standard\n" + "hf 14a config --cl2 force -> Execute CL2\n" + "hf 14a config --cl2 skip -> Skip CL2\n" + "hf 14a config --cl3 std -> Follow standard\n" + "hf 14a config --cl3 force -> Execute CL3\n" + "hf 14a config --cl3 skip -> Skip CL3\n" + "hf 14a config --rats std -> Follow standard\n" + "hf 14a config --rats force -> Execute RATS\n" + "hf 14a config --rats skip -> Skip RATS"); - hf14a_config config = { - .forceanticol = -1, - .forcebcc = -1, - .forcecl2 = -1, - .forcecl3 = -1, - .forcerats = -1 + void *argtable[] = { + arg_param_begin, + arg_str0(NULL, "atqa", "", "Configure ATQA<>anticollision behavior"), + arg_str0(NULL, "bcc", "", "Configure BCC behavior"), + arg_str0(NULL, "cl2", "", "Configure SAK<>CL2 behavior"), + arg_str0(NULL, "cl3", "", "Configure SAK<>CL3 behavior"), + arg_str0(NULL, "rats", "", "Configure RATS behavior"), + arg_lit0(NULL, "std", "Reset default configuration: follow all standard"), + arg_lit0("v", "verbose", "verbose output, also prints examples for reviving Gen2 cards"), + arg_param_end }; - - bool errors = false; - uint8_t cmdp = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (param_getchar(Cmd, cmdp)) { - case 'h': - return usage_hf_14a_config(); - case 'a': - switch (param_getchar(Cmd, cmdp + 1)) { - case '0': - config.forceanticol = 0; - break; - case '1': - config.forceanticol = 1; - break; - case '2': - config.forceanticol = 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown value '%c'", param_getchar(Cmd, cmdp + 1)); - errors = 1; - break; - } - cmdp += 2; - break; - case 'b': - switch (param_getchar(Cmd, cmdp + 1)) { - case '0': - config.forcebcc = 0; - break; - case '1': - config.forcebcc = 1; - break; - case '2': - config.forcebcc = 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown value '%c'", param_getchar(Cmd, cmdp + 1)); - errors = 1; - break; - } - cmdp += 2; - break; - case '2': - switch (param_getchar(Cmd, cmdp + 1)) { - case '0': - config.forcecl2 = 0; - break; - case '1': - config.forcecl2 = 1; - break; - case '2': - config.forcecl2 = 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown value '%c'", param_getchar(Cmd, cmdp + 1)); - errors = 1; - break; - } - cmdp += 2; - break; - case '3': - switch (param_getchar(Cmd, cmdp + 1)) { - case '0': - config.forcecl3 = 0; - break; - case '1': - config.forcecl3 = 1; - break; - case '2': - config.forcecl3 = 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown value '%c'", param_getchar(Cmd, cmdp + 1)); - errors = 1; - break; - } - cmdp += 2; - break; - case 'r': - switch (param_getchar(Cmd, cmdp + 1)) { - case '0': - config.forcerats = 0; - break; - case '1': - config.forcerats = 1; - break; - case '2': - config.forcerats = 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown value '%c'", param_getchar(Cmd, cmdp + 1)); - errors = 1; - break; - } - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = 1; - break; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool defaults = arg_get_lit(ctx, 6); + int vlen = 0; + char value[10]; + int atqa = defaults ? 0 : -1; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)value, sizeof(value), &vlen); + if (vlen > 0) { + if (strcmp(value, "std") == 0) atqa = 0; + else if (strcmp(value, "force") == 0) atqa = 1; + else if (strcmp(value, "skip") == 0) atqa = 2; + else { + PrintAndLogEx(ERR, "atqa argument must be 'std', 'force', or 'skip'"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + int bcc = defaults ? 0 : -1; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)value, sizeof(value), &vlen); + if (vlen > 0) { + if (strcmp(value, "std") == 0) bcc = 0; + else if (strcmp(value, "fix") == 0) bcc = 1; + else if (strcmp(value, "ignore") == 0) bcc = 2; + else { + PrintAndLogEx(ERR, "bcc argument must be 'std', 'fix', or 'ignore'"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + int cl2 = defaults ? 0 : -1; + CLIParamStrToBuf(arg_get_str(ctx, 3), (uint8_t *)value, sizeof(value), &vlen); + if (vlen > 0) { + if (strcmp(value, "std") == 0) cl2 = 0; + else if (strcmp(value, "force") == 0) cl2 = 1; + else if (strcmp(value, "skip") == 0) cl2 = 2; + else { + PrintAndLogEx(ERR, "cl2 argument must be 'std', 'force', or 'skip'"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + int cl3 = defaults ? 0 : -1; + CLIParamStrToBuf(arg_get_str(ctx, 4), (uint8_t *)value, sizeof(value), &vlen); + if (vlen > 0) { + if (strcmp(value, "std") == 0) cl3 = 0; + else if (strcmp(value, "force") == 0) cl3 = 1; + else if (strcmp(value, "skip") == 0) cl3 = 2; + else { + PrintAndLogEx(ERR, "cl3 argument must be 'std', 'force', or 'skip'"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + int rats = defaults ? 0 : -1; + CLIParamStrToBuf(arg_get_str(ctx, 5), (uint8_t *)value, sizeof(value), &vlen); + if (vlen > 0) { + if (strcmp(value, "std") == 0) rats = 0; + else if (strcmp(value, "force") == 0) rats = 1; + else if (strcmp(value, "skip") == 0) rats = 2; + else { + PrintAndLogEx(ERR, "rats argument must be 'std', 'force', or 'skip'"); + CLIParserFree(ctx); + return PM3_EINVARG; } } - // validations - if (errors) return usage_hf_14a_config(); + bool verbose = arg_get_lit(ctx, 7); - return hf14a_setconfig(&config); + CLIParserFree(ctx); + + // validations + if (strlen(Cmd) == 0) { + return hf14a_setconfig(NULL, verbose); + } + + if (verbose) { + hf_14a_config_example(); + } + + hf14a_config config = { + .forceanticol = atqa, + .forcebcc = bcc, + .forcecl2 = cl2, + .forcecl3 = cl3, + .forcerats = rats + }; + + return hf14a_setconfig(&config, verbose); } int Hf14443_4aGetCardData(iso14a_card_select_t *card) { @@ -463,37 +401,44 @@ int Hf14443_4aGetCardData(iso14a_card_select_t *card) { } static int CmdHF14AReader(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14a reader", + "Reader for ISO 14443A based tags", + "hf 14a reader -@ <- Continuous mode"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("k", "keep", "keep the field active after command executed"), + arg_lit0("s", "silent", "silent (no messages)"), + arg_lit0(NULL, "drop", "just drop the signal field"), + arg_lit0(NULL, "skip", "ISO14443-3 select only (skip RATS)"), + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + bool disconnectAfter = true; + if (arg_get_lit(ctx, 1)) { + disconnectAfter = false; + } + + bool silent = arg_get_lit(ctx, 2); uint32_t cm = ISO14A_CONNECT; - bool disconnectAfter = true, silent = false, continuous = false; - int cmdp = 0; - int res = PM3_SUCCESS; - while (param_getchar(Cmd, cmdp) != 0x00) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_14a_reader(); - case '3': - cm |= ISO14A_NO_RATS; - break; - case 'k': - disconnectAfter = false; - break; - case 's': - silent = true; - break; - case 'x': - cm &= ~ISO14A_CONNECT; - break; - case '@': - continuous = true; - break; - default: - PrintAndLogEx(WARNING, "Unknown command."); - return PM3_EINVARG; - } - cmdp++; + if (arg_get_lit(ctx, 3)) { + cm &= ~ISO14A_CONNECT; } + if (arg_get_lit(ctx, 4)) { + cm |= ISO14A_NO_RATS; + } + + bool continuous = arg_get_lit(ctx, 5); + + CLIParserFree(ctx); + + int res = PM3_SUCCESS; + if (!disconnectAfter) cm |= ISO14A_NO_DISCONNECT; if (continuous) { @@ -609,10 +554,23 @@ static int CmdHF14AInfo(const char *Cmd) { // Collect ISO14443 Type A UIDs static int CmdHF14ACUIDs(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14a cuids", + "Collect n>0 ISO14443-a UIDs in one go", + "hf 14a cuids -n 5 <-- Collect 5 UIDs"); + + void *argtable[] = { + arg_param_begin, + arg_int0("n", "num", "", "Number of UIDs to collect"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + // requested number of UIDs - int n = atoi(Cmd); // collect at least 1 (e.g. if no parameter was given) - n = n > 0 ? n : 1; + int n = arg_get_int_def(ctx, 1, 1); + + CLIParserFree(ctx); uint64_t t1 = msclock(); PrintAndLogEx(SUCCESS, "collecting %d UIDs", n); @@ -647,83 +605,81 @@ static int CmdHF14ACUIDs(const char *Cmd) { PrintAndLogEx(SUCCESS, "end: %" PRIu64 " seconds", (msclock() - t1) / 1000); return 1; } + // ## simulate iso14443a tag int CmdHF14ASim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14a sim", + "Simulate ISO/IEC 14443 type A tag with 4,7 or 10 byte UID", + "hf 14a sim -t 1 --uid 11223344 -> MIFARE Classic 1k\n" + "hf 14a sim -t 2 -> MIFARE Ultralight\n" + "hf 14a sim -t 3 -> MIFARE Desfire\n" + "hf 14a sim -t 4 -> ISO/IEC 14443-4\n" + "hf 14a sim -t 5 -> MIFARE Tnp3xxx\n" + "hf 14a sim -t 6 -> MIFARE Mini\n" + "hf 14a sim -t 7 -> AMIIBO (NTAG 215), pack 0x8080\n" + "hf 14a sim -t 8 -> MIFARE Classic 4k\n" + "hf 14a sim -t 9 -> FM11RF005SH Shanghai Metro\n" + "hf 14a sim -t 10 -> ST25TA IKEA Rothult\n"); - int uidlen = 0; - uint8_t flags = 0, tagtype = 1, cmdp = 0; - uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + void *argtable[] = { + arg_param_begin, + arg_int1("t", "type", "<1-10> ", "Simulation type to use"), + arg_str0("u", "uid", "", "4, 7 or 10 byte UID"), + arg_int0("n", "num", "", "Exit simulation after blocks have been read by reader. 0 = infinite"), + arg_lit0(NULL, "nr", "Performs the 'reader attack', nr/ar attack against a reader"), + arg_lit0(NULL, "sk", "Fill simulator keys from found keys"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int tagtype = arg_get_int(ctx, 1); + + int uid_len = 0; + uint8_t uid[10] = {0}; + CLIGetHexWithReturn(ctx, 2, uid, &uid_len); + + uint8_t flags = 0; bool useUIDfromEML = true; - bool setEmulatorMem = false; - bool verbose = false; - bool errors = false; - sector_t *k_sector = NULL; - uint8_t k_sectorsCount = 40; - uint8_t exitAfterNReads = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf_14a_sim(); - case 't': - // Retrieve the tag type - tagtype = param_get8ex(Cmd, cmdp + 1, 0, 10); - if (tagtype == 0) - errors = true; - cmdp += 2; + if (uid_len > 0) { + switch (uid_len) { + case 10: + flags |= FLAG_10B_UID_IN_DATA; break; - case 'u': - // Retrieve the full 4,7,10 byte long uid - param_gethex_ex(Cmd, cmdp + 1, uid, &uidlen); - uidlen >>= 1; - switch (uidlen) { - case 10: - flags |= FLAG_10B_UID_IN_DATA; - break; - case 7: - flags |= FLAG_7B_UID_IN_DATA; - break; - case 4: - flags |= FLAG_4B_UID_IN_DATA; - break; - default: - errors = true; - break; - } - if (!errors) { - PrintAndLogEx(SUCCESS, "Emulating " _YELLOW_("ISO/IEC 14443 type A tag")" with " _GREEN_("%d byte UID (%s)"), uidlen, sprint_hex(uid, uidlen)); - useUIDfromEML = false; - } - cmdp += 2; + case 7: + flags |= FLAG_7B_UID_IN_DATA; break; - case 'n': - exitAfterNReads = param_get8(Cmd, cmdp + 1); - cmdp += 2; - break; - case 'v': - verbose = true; - cmdp++; - break; - case 'x': - flags |= FLAG_NR_AR_ATTACK; - cmdp++; - break; - case 'e': - setEmulatorMem = true; - cmdp++; + case 4: + flags |= FLAG_4B_UID_IN_DATA; break; default: - PrintAndLogEx(WARNING, "Unknown parameter " _RED_("'%c'"), param_getchar(Cmd, cmdp)); - errors = true; - break; + PrintAndLogEx(ERR, "Please specify a 4, 7, or 10 byte UID"); + CLIParserFree(ctx); + return PM3_EINVARG; } + PrintAndLogEx(SUCCESS, "Emulating " _YELLOW_("ISO/IEC 14443 type A tag")" with " _GREEN_("%d byte UID (%s)"), uid_len, sprint_hex(uid, uid_len)); + useUIDfromEML = false; } - //Validations - if (errors || cmdp == 0) return usage_hf_14a_sim(); + uint8_t exitAfterNReads = arg_get_int(ctx, 3); - if (useUIDfromEML) + if (arg_get_lit(ctx, 4)) { + flags |= FLAG_NR_AR_ATTACK; + } + + bool setEmulatorMem = arg_get_lit(ctx, 5); + bool verbose = arg_get_lit(ctx, 6); + + CLIParserFree(ctx); + + sector_t *k_sector = NULL; + uint8_t k_sectorsCount = 40; + + if (useUIDfromEML) { flags |= FLAG_UID_IN_EMUL; + } struct { uint8_t tagtype; @@ -735,7 +691,7 @@ int CmdHF14ASim(const char *Cmd) { payload.tagtype = tagtype; payload.flags = flags; payload.exitAfter = exitAfterNReads; - memcpy(payload.uid, uid, uidlen); + memcpy(payload.uid, uid, uid_len); clearCommandBuffer(); SendCommandNG(CMD_HF_ISO14443A_SIMULATE, (uint8_t *)&payload, sizeof(payload)); @@ -769,13 +725,32 @@ int CmdHF14ASim(const char *Cmd) { } int CmdHF14ASniff(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14a sniff", + "Collect data from the field and save into command buffer.\n" + "Buffer accessible from command 'hf 14a list'", + " hf 14a sniff -c -r"); + + void *argtable[] = { + arg_param_begin, + arg_lit0("c", "card", "triggered by first data from card"), + arg_lit0("r", "reader", "triggered by first 7-bit request from reader (REQ,WUP,...)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + uint8_t param = 0; - for (uint8_t i = 0; i < 2; i++) { - uint8_t ctmp = tolower(param_getchar(Cmd, i)); - if (ctmp == 'h') return usage_hf_14a_sniff(); - if (ctmp == 'c') param |= 0x01; - if (ctmp == 'r') param |= 0x02; + + if (arg_get_lit(ctx, 1)) { + param |= 0x01; } + + if (arg_get_lit(ctx, 2)) { + param |= 0x02; + } + + CLIParserFree(ctx); + clearCommandBuffer(); SendCommandNG(CMD_HF_ISO14443A_SNIFF, (uint8_t *)¶m, sizeof(uint8_t)); return PM3_SUCCESS; @@ -1447,7 +1422,8 @@ typedef enum { MTDESFIRE = 4, MTPLUS = 8, MTULTRALIGHT = 16, - MTOTHER = 32 + HID_SEOS = 32, + MTOTHER = 64 } nxp_mifare_type_t; // Based on NXP AN10833 Rev 3.6 and NXP AN10834 Rev 4.1 @@ -1540,6 +1516,7 @@ static int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status) { printTag("MIFARE DESFire CL2"); printTag("MIFARE DESFire EV1 256B/2K/4K/8K CL2"); printTag("MIFARE DESFire EV2 2K/4K/8K/16K/32K"); + printTag("MIFARE DESFire EV3 2K/4K/8K"); printTag("MIFARE DESFire Light 640B"); } else { printTag("MIFARE Plus EV1 2K/4K CL2 in SL3"); @@ -1549,11 +1526,17 @@ static int detect_nxp_card(uint8_t sak, uint16_t atqa, uint64_t select_status) { type |= MTPLUS; } } else { - printTag("MIFARE Plus EV1 2K/4K in SL3"); - printTag("MIFARE Plus S 2K/4K in SL3"); - printTag("MIFARE Plus X 2K/4K in SL3"); - printTag("MIFARE Plus SE 1K"); - type |= MTPLUS; + + if ((atqa & 0x0001) == 0x0001) { + printTag("HID SEOS (smartmx / javacard)"); + type |= HID_SEOS; + } else { + printTag("MIFARE Plus EV1 2K/4K in SL3"); + printTag("MIFARE Plus S 2K/4K in SL3"); + printTag("MIFARE Plus X 2K/4K in SL3"); + printTag("MIFARE Plus SE 1K"); + type |= MTPLUS; + } } printTag("NTAG 4xx"); @@ -1658,6 +1641,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { bool isMifarePlus = false; bool isMifareUltralight = false; bool isST = false; + bool isEMV = false; int nxptype = MTNONE; if (card.uidlen <= 4) { @@ -1674,7 +1658,7 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } else { // Double & triple sized UID, can be mapped to a manufacturer. - PrintAndLogEx(SUCCESS, "MANUFACTURER: " _YELLOW_("%s"), getTagInfo(card.uid[0])); + PrintAndLogEx(SUCCESS, "MANUFACTURER: " _YELLOW_("%s"), getTagInfo(card.uid[0])); switch (card.uid[0]) { case 0x02: // ST @@ -2001,11 +1985,21 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } if (do_aid_search) { + + + PrintAndLogEx(INFO, "-------------------- " _CYAN_("AID Search") " --------------------"); + + bool found = false; int elmindx = 0; json_t *root = AIDSearchInit(verbose); if (root != NULL) { bool ActivateField = true; for (elmindx = 0; elmindx < json_array_size(root); elmindx++) { + + if (kbd_enter_pressed()) { + break; + } + json_t *data = AIDSearchGetElm(root, elmindx); uint8_t vaid[200] = {0}; int vaidlen = 0; @@ -2037,9 +2031,9 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if (sw == 0x9000 || sw == 0x6283 || sw == 0x6285) { if (sw == 0x9000) { - if (verbose) PrintAndLogEx(SUCCESS, "------------- Application OK -----------"); + if (verbose) PrintAndLogEx(SUCCESS, "Application ( " _GREEN_("ok") " )"); } else { - if (verbose) PrintAndLogEx(WARNING, "----------- Application blocked --------"); + if (verbose) PrintAndLogEx(WARNING, "Application ( " _RED_("blocked") " )"); } PrintAIDDescriptionBuf(root, vaid, vaidlen, verbose); @@ -2059,10 +2053,16 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { } else { if (verbose) PrintAndLogEx(INFO, "(DF) Name not found"); } + + if (verbose) PrintAndLogEx(SUCCESS, "----------------------------------------------------"); + found = true; + isEMV = true; } } DropField(); + if (verbose == false && found) + PrintAndLogEx(INFO, "----------------------------------------------------"); } } } else { @@ -2101,172 +2101,235 @@ int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search) { if (do_nack_test) detect_classic_nackbug(false); } + + uint8_t signature[32] = {0}; + res = detect_mfc_ev1_signature(signature); + if (res == PM3_SUCCESS) { + mfc_ev1_print_signature(card.uid, card.uidlen, signature, sizeof(signature)); + } + + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mf`") " commands"); } if (isMifareUltralight) PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfu info`")); - if (isMifarePlus && isMagic == 0) + if (isMifarePlus && isMagic == 0 && isEMV == false) PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfp info`")); - if (isMifareDESFire && isMagic == 0) + if (isMifareDESFire && isMagic == 0 && isEMV == false) PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf mfdes info`")); if (isST) PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`hf st info`")); + if (isEMV) + PrintAndLogEx(HINT, "Hint: try " _YELLOW_("`emv search -s`")); + PrintAndLogEx(NORMAL, ""); DropField(); return select_status; } static uint16_t get_sw(uint8_t *d, uint8_t n) { - if (n < 2) + if (n < 2) { return 0; - + } n -= 2; return d[n] * 0x0100 + d[n + 1]; } -static int CmdHf14AFuzzapdu(const char *Cmd) { + +static uint64_t inc_sw_error_occurence(uint16_t sw, uint64_t all_sw[256][256]) { + uint8_t sw1 = (uint8_t)(sw >> 8); + uint8_t sw2 = (uint8_t)(0xff & sw); + if (sw1 == 0x90 && sw2 == 0x00) { + return 0; // Don't count successes. + } + if (sw1 == 0x6d && sw2 == 0x00) { + return 0xffffffffffffffffULL; // Always max "Instruction not supported". + } + return ++all_sw[sw1][sw2]; +} + +static int CmdHf14AFindapdu(const char *Cmd) { + // TODO: Option to select AID/File (and skip INS 0xA4). + // TODO: Check all instructions with extended APDUs if the card support it. + // TODO: Option to reset tag before every command. CLIParserContext *ctx; - CLIParserInit(&ctx, "hf 14a apdufuzz", - "Fuzz APDU's of ISO7816 protocol to find valid CLS/INS/P1P2/LE commands.\n" + CLIParserInit(&ctx, "hf 14a apdufind", + "Enumerate APDU's of ISO7816 protocol to find valid CLS/INS/P1/P2 commands.\n" "It loops all 256 possible values for each byte.\n" + "The loop oder is INS -> P1/P2 (alternating) -> CLA.\n" "Tag must be on antenna before running.", - "hf 14a apdufuzz\n" - "hf 14a apdufuzz --cla 80\n" - ); + "hf 14a apdufind\n" + "hf 14a apdufind --cla 80\n" + "hf 14a apdufind --cla 80 --error-limit 20 --skip-ins a4 --skip-ins b0 --with-le\n" + ); void *argtable[] = { arg_param_begin, - arg_str0(NULL, "cla", "", "start CLASS value (1 hex byte)"), - arg_str0(NULL, "ins", "", "start INSTRUCTION value (1 hex byte)"), - arg_str0(NULL, "p1", "", "start P1 value (1 hex byte)"), - arg_str0(NULL, "p2", "", "start P2 value (1 hex byte)"), - arg_str0(NULL, "le", "", "start LENGTH value (1 hex byte)"), - arg_lit0("v", "verbose", "verbose output"), + arg_str0("c", "cla", "", "Start value of CLASS (1 hex byte)"), + arg_str0("i", "ins", "", "Start value of INSTRUCTION (1 hex byte)"), + arg_str0(NULL, "p1", "", "Start value of P1 (1 hex byte)"), + arg_str0(NULL, "p2", "", "Start value of P2 (1 hex byte)"), + arg_u64_0("r", "reset", "", "Minimum secondes before resetting the tag (to prevent timeout issues). Default is 5 minutes"), + arg_u64_0("e", "error-limit", "", "Maximum times an status word other than 0x9000 or 0x6D00 is shown. Default is 512."), + arg_strx0("s", "skip-ins", "", "Do not test an instructions (can be specifed multiple times)"), + arg_lit0("l", "with-le", "Serach for APDUs with Le=0 (case 2S) as well"), + arg_lit0("v", "verbose", "Verbose output"), arg_param_end }; - CLIExecWithReturn(ctx, Cmd, argtable, false); + CLIExecWithReturn(ctx, Cmd, argtable, true); int cla_len = 0; - uint8_t cla[1] = {0}; - CLIGetHexWithReturn(ctx, 1, cla, &cla_len); - + uint8_t cla_arg[1] = {0}; + CLIGetHexWithReturn(ctx, 1, cla_arg, &cla_len); int ins_len = 0; - uint8_t ins[1] = {0}; - CLIGetHexWithReturn(ctx, 2, ins, &ins_len); - + uint8_t ins_arg[1] = {0}; + CLIGetHexWithReturn(ctx, 2, ins_arg, &ins_len); int p1_len = 0; - uint8_t p1[1] = {0}; - CLIGetHexWithReturn(ctx, 3, p1, &p1_len); - + uint8_t p1_arg[1] = {0}; + CLIGetHexWithReturn(ctx, 3, p1_arg, &p1_len); int p2_len = 0; - uint8_t p2[1] = {0}; - CLIGetHexWithReturn(ctx, 4, p2, &p2_len); + uint8_t p2_arg[1] = {0}; + CLIGetHexWithReturn(ctx, 4, p2_arg, &p2_len); + uint64_t reset_time = arg_get_u64_def(ctx, 5, 5 * 60); + uint64_t error_limit = arg_get_u64_def(ctx, 6, 512); + int ignore_ins_len = 0; + uint8_t ignore_ins_arg[250] = {0}; + CLIGetHexWithReturn(ctx, 7, ignore_ins_arg, &ignore_ins_len); + bool with_le = arg_get_lit(ctx, 8); + bool verbose = arg_get_lit(ctx, 9); - int le_len = 0; - uint8_t le[1] = {0}; - CLIGetHexWithReturn(ctx, 5, le, &le_len); - - bool verbose = arg_get_lit(ctx, 6); CLIParserFree(ctx); bool activate_field = true; bool keep_field_on = true; - - uint8_t a = cla[0]; - uint8_t b = ins[0]; - uint8_t c = p1[0]; - uint8_t d = p2[0]; - uint8_t e = le[0]; - - PrintAndLogEx(SUCCESS, "Starting the apdu fuzzer [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " LE " _GREEN_("%02x")" ]", a,b,c,d,e); - PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); - + uint8_t cla = cla_arg[0]; + uint8_t ins = ins_arg[0]; + uint8_t p1 = p1_arg[0]; + uint8_t p2 = p2_arg[0]; uint8_t response[PM3_CMD_DATA_SIZE]; - int resplen = 0; - + int response_n = 0; uint8_t aSELECT_AID[80]; int aSELECT_AID_n = 0; + + // Check if the tag reponds to APDUs. + PrintAndLogEx(INFO, "Sending a test APDU (select file command) to check if the tag is responding to APDU"); param_gethex_to_eol("00a404000aa000000440000101000100", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); - int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + int res = ExchangeAPDU14a(aSELECT_AID, aSELECT_AID_n, true, false, response, sizeof(response), &response_n); if (res) { - DropField(); + PrintAndLogEx(FAILED, "Tag did not responde to a test APDU (select file command). Aborting"); return res; } + PrintAndLogEx(SUCCESS, "Got response. Starting the APDU finder [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla, ins, p1, p2); + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); - if (activate_field) - activate_field = false; + bool inc_p1 = true; + bool skip_ins = false; + uint64_t all_sw[256][256] = { { 0 } }; + uint64_t sw_occurences = 0; + uint64_t t_start = msclock(); + uint64_t t_last_reset = msclock(); - uint64_t t1 = msclock(); + // Enumerate APDUs. do { do { do { - do { - do { - if (kbd_enter_pressed()) { - goto out; - } +retry_ins: + // Exit (was the Enter key pressed)? + if (kbd_enter_pressed()) { + PrintAndLogEx(INFO, "User interrupted detected. Aborting"); + goto out; + } - uint8_t foo[5] = {a, b, c, d, e}; - int foo_n = sizeof(foo); + // Skip/Ignore this instrctuion? + for (int i = 0; i < ignore_ins_len; i++) { + if (ins == ignore_ins_arg[i]) { + skip_ins = true; + break; + } + } + if (skip_ins) { + skip_ins = false; + continue; + } - if (verbose) { - PrintAndLogEx(INFO, "%s", sprint_hex(foo, sizeof(foo))); - } - res = ExchangeAPDU14a(foo, foo_n, activate_field, keep_field_on, response, sizeof(response), &resplen); - if (res) { - e++; - continue; - } + if (verbose) { + PrintAndLogEx(INFO, "Status: [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla, ins, p1, p2); + } - uint16_t sw = get_sw(response, resplen); - if (sw != 0x6a86 && - sw != 0x6986 && - sw != 0x6d00 - ) { - PrintAndLogEx(INFO, "%02X %02X %02X %02X %02X (%04x - %s)", a,b,c,d,e, sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); - } - e++; - if (verbose) { - PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e); - } + // Send APDU without Le (case 1) and with Le = 0 (case 2S), if "with-le" was set. + uint8_t command[5] = {cla, ins, p1, p2, 0x00}; + int command_n = 4; + for (int i = 0; i < 1 + with_le; i++) { + // Send APDU. + res = ExchangeAPDU14a(command, command_n + i, activate_field, keep_field_on, response, sizeof(response), &response_n); + if (res) { + DropField(); + activate_field = true; + goto retry_ins; + } + uint16_t sw = get_sw(response, response_n); + sw_occurences = inc_sw_error_occurence(sw, all_sw); - } while (e); - d++; - PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e); - } while (d); - c++; - PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e); - } while (c); - b++; - PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e); - } while (b); - a++; - PrintAndLogEx(INFO, "Status: %02X %02X %02X %02X %02X", a,b,c,d,e); - } while(a); + // Show response. + if (sw_occurences < error_limit) { + logLevel_t log_level = INFO; + if (sw == 0x9000) { + log_level = SUCCESS; + } + PrintAndLogEx(log_level, "Got response for APDU \"%s\": %04X (%s)", sprint_hex_inrow(command, command_n + i), + sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + if (response_n > 2) { + PrintAndLogEx(SUCCESS, "Response data is: %s | %s", sprint_hex_inrow(response, response_n - 2), + sprint_ascii(response, response_n - 2)); + } + } + } + activate_field = false; // Do not reativate the filed until the next reset. + } while (++ins != ins_arg[0]); + // Increment P1/P2 in an alternating fashion. + if (inc_p1) { + p1++; + } else { + p2++; + } + inc_p1 = !inc_p1; + // Check if re-selecting the card is needed. + uint64_t t_since_last_reset = ((msclock() - t_last_reset) / 1000); + if (t_since_last_reset > reset_time) { + DropField(); + activate_field = true; + t_last_reset = msclock(); + PrintAndLogEx(INFO, "Last reset was %" PRIu64 " seconds ago. Reseting the tag to prevent timeout issues", t_since_last_reset); + } + PrintAndLogEx(INFO, "Status: [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla, ins, p1, p2); + } while (p1 != p1_arg[0] || p2 != p2_arg[0]); + cla++; + PrintAndLogEx(INFO, "Status: [ CLA " _GREEN_("%02X") " INS " _GREEN_("%02X") " P1 " _GREEN_("%02X") " P2 " _GREEN_("%02X") " ]", cla, ins, p1, p2); + } while (cla != cla_arg[0]); out: - PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock() - t1) / 1000); + PrintAndLogEx(SUCCESS, "Runtime: %" PRIu64 " seconds\n", (msclock() - t_start) / 1000); DropField(); return PM3_SUCCESS; } static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"list", CmdHF14AList, AlwaysAvailable, "List ISO 14443-a history"}, + {"list", CmdHF14AList, AlwaysAvailable, "List ISO 14443-a history"}, {"info", CmdHF14AInfo, IfPm3Iso14443a, "Tag information"}, {"reader", CmdHF14AReader, IfPm3Iso14443a, "Act like an ISO14443-a reader"}, - {"cuids", CmdHF14ACUIDs, IfPm3Iso14443a, " Collect n>0 ISO14443-a UIDs in one go"}, - {"sim", CmdHF14ASim, IfPm3Iso14443a, " -- Simulate ISO 14443-a tag"}, + {"cuids", CmdHF14ACUIDs, IfPm3Iso14443a, "Collect n>0 ISO14443-a UIDs in one go"}, + {"sim", CmdHF14ASim, IfPm3Iso14443a, "Simulate ISO 14443-a tag"}, {"sniff", CmdHF14ASniff, IfPm3Iso14443a, "sniff ISO 14443-a traffic"}, {"apdu", CmdHF14AAPDU, IfPm3Iso14443a, "Send ISO 14443-4 APDU to tag"}, {"chaining", CmdHF14AChaining, IfPm3Iso14443a, "Control ISO 14443-4 input chaining"}, {"raw", CmdHF14ACmdRaw, IfPm3Iso14443a, "Send raw hex data to tag"}, {"antifuzz", CmdHF14AAntiFuzz, IfPm3Iso14443a, "Fuzzing the anticollision phase. Warning! Readers may react strange"}, {"config", CmdHf14AConfig, IfPm3Iso14443a, "Configure 14a settings (use with caution)"}, - {"apdufuzz", CmdHf14AFuzzapdu, IfPm3Iso14443a, "Fuzz APDU - CLA/INS/P1P2"}, + {"apdufind", CmdHf14AFindapdu, IfPm3Iso14443a, "Enumerate APDUs - CLA/INS/P1P2"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhf14a.h b/client/src/cmdhf14a.h index 925bf8bef..a1a2279a0 100644 --- a/client/src/cmdhf14a.h +++ b/client/src/cmdhf14a.h @@ -27,7 +27,7 @@ int CmdHF14ASniff(const char *Cmd); // used by hf topaz sniff int CmdHF14ASim(const char *Cmd); // used by hf mfu sim int hf14a_getconfig(hf14a_config *config); -int hf14a_setconfig(hf14a_config *config); +int hf14a_setconfig(hf14a_config *config, bool verbose); int infoHF14A(bool verbose, bool do_nack_test, bool do_aid_search); const char *getTagInfo(uint8_t uid); int Hf14443_4aGetCardData(iso14a_card_select_t *card); diff --git a/client/src/cmdhf14b.c b/client/src/cmdhf14b.c index 3f4890581..a9524abb4 100644 --- a/client/src/cmdhf14b.c +++ b/client/src/cmdhf14b.c @@ -24,8 +24,11 @@ #include "protocols.h" // definitions of ISO14B/7816 protocol #include "emv/apduinfo.h" // GetAPDUCodeDescription #include "mifare/ndef.h" // NDEFRecordsDecodeAndPrint +#include "aidsearch.h" +#define MAX_14B_TIMEOUT 40542464U // = (2^32-1) * (8*16) / 13560000Hz * 1000ms/s #define TIMEOUT 2000 +#define APDU_TIMEOUT 2000 // iso14b apdu input frame length static uint16_t apdu_frame_length = 0; @@ -34,21 +37,6 @@ bool apdu_in_framing_enable = true; static int CmdHelp(const char *Cmd); -static int usage_hf_14b_write_srx(void) { - PrintAndLogEx(NORMAL, "Usage: hf 14b [h] sriwrite <1|2> "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " <1|2> 1 = SRIX4K , 2 = SRI512"); - PrintAndLogEx(NORMAL, " (hex) block number depends on tag, special block == FF"); - PrintAndLogEx(NORMAL, " hex bytes of data to be written"); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sriwrite 1 7F 11223344")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sriwrite 1 FF 11223344")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sriwrite 2 15 11223344")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf 14b sriwrite 2 FF 11223344")); - return PM3_SUCCESS; -} - static int switch_off_field_14b(void) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_DISCONNECT, 0, 0, NULL, 0); @@ -63,6 +51,103 @@ static uint16_t get_sw(uint8_t *d, uint8_t n) { return d[n] * 0x0100 + d[n + 1]; } +static void hf14b_aid_search(bool verbose) { + + int elmindx = 0; + json_t *root = AIDSearchInit(verbose); + if (root == NULL) { + switch_off_field_14b(); + return; + } + + PrintAndLogEx(INFO, "-------------------- " _CYAN_("AID Search") " --------------------"); + + bool found = false; + bool leave_signal_on = true; + bool activate_field = true; + for (elmindx = 0; elmindx < json_array_size(root); elmindx++) { + + if (kbd_enter_pressed()) { + break; + } + + json_t *data = AIDSearchGetElm(root, elmindx); + uint8_t vaid[200] = {0}; + int vaidlen = 0; + if (!AIDGetFromElm(data, vaid, sizeof(vaid), &vaidlen) || !vaidlen) + continue; + + + // COMPUTE APDU + uint8_t apdu_data[PM3_CMD_DATA_SIZE] = {0}; + int apdu_len = 0; + sAPDU apdu = (sAPDU) {0x00, 0xa4, 0x04, 0x00, vaidlen, vaid}; + + if (APDUEncodeS(&apdu, false, 0x00, apdu_data, &apdu_len)) { + PrintAndLogEx(ERR, "APDU encoding error."); + return; + } + + PrintAndLogEx(DEBUG, ">>>> %s", sprint_hex(apdu_data, apdu_len)); + + int resultlen = 0; + uint8_t result[1024] = {0}; + int res = exchange_14b_apdu(apdu_data, apdu_len, activate_field, leave_signal_on, result, sizeof(result), &resultlen, -1); + activate_field = false; + if (res) + continue; + + uint16_t sw = get_sw(result, resultlen); + + uint8_t dfname[200] = {0}; + size_t dfnamelen = 0; + if (resultlen > 3) { + struct tlvdb *tlv = tlvdb_parse_multi(result, resultlen); + if (tlv) { + // 0x84 Dedicated File (DF) Name + const struct tlv *dfnametlv = tlvdb_get_tlv(tlvdb_find_full(tlv, 0x84)); + if (dfnametlv) { + dfnamelen = dfnametlv->len; + memcpy(dfname, dfnametlv->value, dfnamelen); + } + tlvdb_free(tlv); + } + } + + if (sw == 0x9000 || sw == 0x6283 || sw == 0x6285) { + if (sw == 0x9000) { + if (verbose) PrintAndLogEx(SUCCESS, "Application ( " _GREEN_("ok") " )"); + } else { + if (verbose) PrintAndLogEx(WARNING, "Application ( " _RED_("blocked") " )"); + } + + PrintAIDDescriptionBuf(root, vaid, vaidlen, verbose); + + if (dfnamelen) { + if (dfnamelen == vaidlen) { + if (memcmp(dfname, vaid, vaidlen) == 0) { + if (verbose) PrintAndLogEx(INFO, "(DF) Name found and equal to AID"); + } else { + PrintAndLogEx(INFO, "(DF) Name not equal to AID: %s :", sprint_hex(dfname, dfnamelen)); + PrintAIDDescriptionBuf(root, dfname, dfnamelen, verbose); + } + } else { + PrintAndLogEx(INFO, "(DF) Name not equal to AID: %s :", sprint_hex(dfname, dfnamelen)); + PrintAIDDescriptionBuf(root, dfname, dfnamelen, verbose); + } + } else { + if (verbose) PrintAndLogEx(INFO, "(DF) Name not found"); + } + + if (verbose) PrintAndLogEx(SUCCESS, "----------------------------------------------------"); + found = true; + } + } + switch_off_field_14b(); + if (verbose == false && found) + PrintAndLogEx(INFO, "----------------------------------------------------"); +} + static bool wait_cmd_14b(bool verbose, bool is_select) { PacketResponseNG resp; @@ -102,8 +187,7 @@ static bool wait_cmd_14b(bool verbose, bool is_select) { (crc) ? _GREEN_("ok") : _RED_("fail") ); } else if (len == 0) { - if (verbose) - PrintAndLogEx(INFO, "no response from tag"); + PrintAndLogEx(INFO, "no response from tag"); } else { PrintAndLogEx(SUCCESS, "%s", sprint_hex(data, len)); } @@ -187,7 +271,7 @@ static int CmdHF14BCmdRaw(const char *Cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf 14b raw", - "Sends raw bytes to card ", + "Sends raw bytes to card", "hf 14b raw -cks --data 0200a40400 -> standard select\n" "hf 14b raw -ck --sr --data 0200a40400 -> SRx select\n" "hf 14b raw -ck --cts --data 0200a40400 -> C-ticket select\n" @@ -200,7 +284,7 @@ static int CmdHF14BCmdRaw(const char *Cmd) { arg_lit0(NULL, "sr", "activate field, use SRx ST select"), arg_lit0(NULL, "cts", "activate field, use ASK C-ticket select"), arg_lit0("c", "crc", "calculate and append CRC"), - arg_lit0("r", "noresponse", "do not read response from card"), + arg_lit0(NULL, "noresponse", "do not read response from card"), arg_int0("t", "timeout", "", "timeout in ms"), arg_lit0("v", "verbose", "verbose"), arg_strx0("d", "data", "", "data, bytes to send"), @@ -244,18 +328,20 @@ static int CmdHF14BCmdRaw(const char *Cmd) { } CLIParserFree(ctx); + uint32_t time_wait = 0; if (user_timeout > 0) { -#define MAX_14B_TIMEOUT 40542464 // = (2^32-1) * (8*16) / 13560000Hz * 1000ms/s flags |= ISO14B_SET_TIMEOUT; - if (user_timeout > MAX_14B_TIMEOUT) { - user_timeout = MAX_14B_TIMEOUT; + + uint32_t max_timeout = user_timeout; + if (max_timeout > MAX_14B_TIMEOUT) { + max_timeout = MAX_14B_TIMEOUT; PrintAndLogEx(INFO, "set timeout to 40542 seconds (11.26 hours). The max we can wait for response"); } - time_wait = 13560000 / 1000 / (8 * 16) * user_timeout; // timeout in ETUs (time to transfer 1 bit, approx. 9.4 us) + time_wait = ((13560000 / 1000 / (8 * 16)) * max_timeout); // timeout in ETUs (time to transfer 1 bit, approx. 9.4 us) if (verbose) - PrintAndLogEx(INFO, "using timeout %u", user_timeout); + PrintAndLogEx(INFO, "using timeout %u", max_timeout); } if (keep_field_on == 0) @@ -308,8 +394,7 @@ static bool get_14b_UID(iso14b_card_select_t *card) { if (card == NULL) return false; - int status = 0; - + int status; PacketResponseNG resp; clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); @@ -628,12 +713,12 @@ static void print_ct_general_info(void *vcard) { memcpy(&card, (iso14b_cts_card_select_t *)vcard, sizeof(iso14b_cts_card_select_t)); uint32_t uid32 = (card.uid[0] | card.uid[1] << 8 | card.uid[2] << 16 | card.uid[3] << 24); + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(SUCCESS, "ASK C-Ticket"); PrintAndLogEx(SUCCESS, " UID: " _GREEN_("%s") " ( " _YELLOW_("%010u") " )", sprint_hex(card.uid, sizeof(card.uid)), uid32); PrintAndLogEx(SUCCESS, " Product Code: %02X", card.pc); PrintAndLogEx(SUCCESS, " Facility Code: %02X", card.fc); PrintAndLogEx(NORMAL, ""); - } // iceman, calypso? @@ -649,7 +734,7 @@ static void print_ct_general_info(void *vcard) { // 0200a4040010a000000018300301000000000000000000 (resp 02 6a 82 [4b 4c]) // 14b get and print Full Info (as much as we know) -static bool HF14B_Std_Info(bool verbose) { +static bool HF14B_Std_Info(bool verbose, bool do_aid_search) { bool is_success = false; @@ -670,14 +755,21 @@ static bool HF14B_Std_Info(bool verbose) { int status = resp.oldarg[0]; switch (status) { - case 0: + case 0: { PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "-------------------- " _CYAN_("Tag information") " --------------------"); PrintAndLogEx(SUCCESS, " UID : " _GREEN_("%s"), sprint_hex(card.uid, card.uidlen)); PrintAndLogEx(SUCCESS, " ATQB : %s", sprint_hex(card.atqb, sizeof(card.atqb))); PrintAndLogEx(SUCCESS, " CHIPID : %02X", card.chipid); print_atqb_resp(card.atqb, card.cid); + + if (do_aid_search) { + hf14b_aid_search(verbose); + } + is_success = true; break; + } case -1: if (verbose) PrintAndLogEx(FAILED, "ISO 14443-3 ATTRIB fail"); break; @@ -693,7 +785,7 @@ static bool HF14B_Std_Info(bool verbose) { } // SRx get and print full info (needs more info...) -static bool HF14B_ST_Info(bool verbose) { +static bool HF14B_ST_Info(bool verbose, bool do_aid_search) { clearCommandBuffer(); PacketResponseNG resp; SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_SR | ISO14B_DISCONNECT, 0, 0, NULL, 0); @@ -711,6 +803,10 @@ static bool HF14B_ST_Info(bool verbose) { return false; print_st_general_info(card.uid, card.uidlen); + + if (do_aid_search) { + hf14b_aid_search(verbose); + } return true; } @@ -724,13 +820,15 @@ static int CmdHF14Binfo(const char *Cmd) { void *argtable[] = { arg_param_begin, + arg_lit0("s", "aidsearch", "checks if AIDs from aidlist.json is present on the card and prints information about found AIDs"), arg_lit0("v", "verbose", "verbose"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - bool verbose = arg_get_lit(ctx, 1); + bool do_aid_search = arg_get_lit(ctx, 1); + bool verbose = arg_get_lit(ctx, 2); CLIParserFree(ctx); - return infoHF14B(verbose); + return infoHF14B(verbose, do_aid_search); } static bool HF14B_st_reader(bool verbose) { @@ -1025,56 +1123,78 @@ static int CmdHF14BWriteSri(const char *Cmd) { * Special block FF = otp_lock_reg block. * Data len 4 bytes- */ - char cmdp = tolower(param_getchar(Cmd, 0)); - uint8_t blockno = -1; - uint8_t data[4] = {0x00}; - bool isSrix4k = true; - char str[30]; - memset(str, 0x00, sizeof(str)); - if (strlen(Cmd) < 1 || cmdp == 'h') return usage_hf_14b_write_srx(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf 14b sriwrite", + "Write data to a SRI512 | SRIX4K block", + "hf 14b sriwrite --4k -b 100 -d 11223344\n" + "hf 14b sriwrite --4k --sb -d 11223344 --> special block write\n" + "hf 14b sriwrite --512 -b 15 -d 11223344\n" + "hf 14b sriwrite --512 --sb -d 11223344 --> special block write\n" + ); - if (cmdp == '2') - isSrix4k = false; - - //blockno = param_get8(Cmd, 1); - - if (param_gethex(Cmd, 1, &blockno, 2)) { - PrintAndLogEx(WARNING, "block number must include 2 HEX symbols"); - return 0; + void *argtable[] = { + arg_param_begin, + arg_int0("b", "block", "", "block number"), + arg_str1("d", "data", "", "4 hex bytes"), + arg_lit0(NULL, "512", "target SRI 512 tag"), + arg_lit0(NULL, "4k", "target SRIX 4k tag"), + arg_lit0(NULL, "sb", "special block write at end of memory (0xFF)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int blockno = arg_get_int_def(ctx, 1, -1); + int dlen = 0; + uint8_t data[4] = {0,0,0,0}; + int res = CLIParamHexToBuf(arg_get_str(ctx, 2), data, sizeof(data), &dlen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; } - if (isSrix4k) { - if (blockno > 0x7f && blockno != 0xff) { - PrintAndLogEx(FAILED, "block number out of range"); - return PM3_ESOFT; - } - } else { - if (blockno > 0x0f && blockno != 0xff) { - PrintAndLogEx(FAILED, "block number out of range"); - return PM3_ESOFT; - } + bool use_sri512 = arg_get_lit(ctx, 3); + bool use_srix4k = arg_get_lit(ctx, 4); + bool special = arg_get_lit(ctx, 5); + CLIParserFree(ctx); + + if (dlen != sizeof(data)) { + PrintAndLogEx(FAILED, "data must be 4 hex bytes, got %d", dlen); + return PM3_EINVARG; } - if (param_gethex(Cmd, 2, data, 8)) { - PrintAndLogEx(WARNING, "data must include 8 HEX symbols"); - return PM3_ESOFT; + if (use_sri512 + use_srix4k > 1) { + PrintAndLogEx(FAILED, "Select only one card type"); + return PM3_EINVARG; } - if (blockno == 0xff) { + if (use_srix4k && blockno > 0x7F) { + PrintAndLogEx(FAILED, "block number out of range, max 127 (0x7F)"); + return PM3_EINVARG; + } + + if (use_sri512 && blockno > 0x0F) { + PrintAndLogEx(FAILED, "block number out of range, max 15 (0x0F)"); + return PM3_EINVARG; + } + + // special block at end of memory + if (special) { + blockno = 0xFF; PrintAndLogEx(SUCCESS, "[%s] Write special block %02X [ " _YELLOW_("%s")" ]", - (isSrix4k) ? "SRIX4K" : "SRI512", + (use_srix4k) ? "SRIX4K" : "SRI512", blockno, - sprint_hex(data, 4) + sprint_hex(data, sizeof(data)) ); } else { PrintAndLogEx(SUCCESS, "[%s] Write block %02X [ " _YELLOW_("%s")" ]", - (isSrix4k) ? "SRIX4K" : "SRI512", + (use_srix4k) ? "SRIX4K" : "SRI512", blockno, - sprint_hex(data, 4) + sprint_hex(data, sizeof(data)) ); } + char str[36]; + memset(str, 0x00, sizeof(str)); sprintf(str, "--sr -c --data %02x%02x%02x%02x%02x%02x", ISO14443B_WRITE_BLK, blockno, data[0], data[1], data[2], data[3]); return CmdHF14BCmdRaw(str); } @@ -1414,7 +1534,7 @@ static int select_card_14443b_4(bool disconnect, iso14b_card_select_t *card) { return PM3_SUCCESS; } -static int handle_14b_apdu(bool chainingin, uint8_t *datain, int datainlen, bool activateField, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool *chainingout) { +static int handle_14b_apdu(bool chainingin, uint8_t *datain, int datainlen, bool activateField, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, bool *chainingout, int user_timeout) { *chainingout = false; if (activateField) { @@ -1429,16 +1549,27 @@ static int handle_14b_apdu(bool chainingin, uint8_t *datain, int datainlen, bool if (chainingin) flags = ISO14B_SEND_CHAINING; + uint32_t time_wait = 0; + if (user_timeout > 0) { + + flags |= ISO14B_SET_TIMEOUT; + if (user_timeout > MAX_14B_TIMEOUT) { + user_timeout = MAX_14B_TIMEOUT; + PrintAndLogEx(INFO, "set timeout to 40542 seconds (11.26 hours). The max we can wait for response"); + } + time_wait = (uint32_t)((13560000 / 1000 / (8 * 16)) * user_timeout); // timeout in ETUs (time to transfer 1 bit, approx. 9.4 us) + } + // "Command APDU" length should be 5+255+1, but javacard's APDU buffer might be smaller - 133 bytes // https://stackoverflow.com/questions/32994936/safe-max-java-card-apdu-data-command-and-respond-size // here length PM3_CMD_DATA_SIZE=512 if (datain) - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_APDU | flags, (datainlen & 0xFFFF), 0, datain, datainlen & 0xFFFF); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_APDU | flags, (datainlen & 0xFFFF), time_wait, datain, datainlen & 0xFFFF); else - SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_APDU | flags, 0, 0, NULL, 0); + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_APDU | flags, 0, time_wait, NULL, 0); PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, TIMEOUT)) { + if (WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, MAX(APDU_TIMEOUT, user_timeout))) { uint8_t *recv = resp.data.asBytes; int rlen = resp.oldarg[0]; uint8_t res = resp.oldarg[1]; @@ -1488,7 +1619,7 @@ static int handle_14b_apdu(bool chainingin, uint8_t *datain, int datainlen, bool return PM3_SUCCESS; } -static int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen) { +int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, int user_timeout) { *dataoutlen = 0; bool chaining = false; int res; @@ -1505,7 +1636,7 @@ static int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field bool chainBlockNotLast = ((clen + vlen) < datainlen); *dataoutlen = 0; - res = handle_14b_apdu(chainBlockNotLast, &datain[clen], vlen, v_activate_field, dataout, maxdataoutlen, dataoutlen, &chaining); + res = handle_14b_apdu(chainBlockNotLast, &datain[clen], vlen, v_activate_field, dataout, maxdataoutlen, dataoutlen, &chaining, user_timeout); if (res) { if (leave_signal_on == false) switch_off_field_14b(); @@ -1534,7 +1665,7 @@ static int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field } else { - res = handle_14b_apdu(false, datain, datainlen, activate_field, dataout, maxdataoutlen, dataoutlen, &chaining); + res = handle_14b_apdu(false, datain, datainlen, activate_field, dataout, maxdataoutlen, dataoutlen, &chaining, user_timeout); if (res != PM3_SUCCESS) { if (leave_signal_on == false) { switch_off_field_14b(); @@ -1545,7 +1676,7 @@ static int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field while (chaining) { // I-block with chaining - res = handle_14b_apdu(false, NULL, 0, false, &dataout[*dataoutlen], maxdataoutlen, dataoutlen, &chaining); + res = handle_14b_apdu(false, NULL, 0, false, &dataout[*dataoutlen], maxdataoutlen, dataoutlen, &chaining, user_timeout); if (res != PM3_SUCCESS) { if (leave_signal_on == false) { switch_off_field_14b(); @@ -1593,6 +1724,7 @@ static int CmdHF14BAPDU(const char *Cmd) { arg_lit0("e", "extended", "make extended length apdu if `m` parameter included"), arg_int0("l", "le", "", "Le apdu parameter if `m` parameter included"), arg_strx1(NULL, "hex", "", " if `m` parameter included"), + arg_int0(NULL, "timeout", "", "timeout in ms"), arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, false); @@ -1652,6 +1784,7 @@ static int CmdHF14BAPDU(const char *Cmd) { // len = data + PCB(1b) + CRC(2b) CLIGetHexBLessWithReturn(ctx, 8, data, &datalen, 1 + 2); } + int user_timeout = arg_get_int_def(ctx, 9, -1); CLIParserFree(ctx); PrintAndLogEx(NORMAL, ">>>>[%s%s%s] %s", @@ -1669,7 +1802,7 @@ static int CmdHF14BAPDU(const char *Cmd) { PrintAndLogEx(WARNING, "can't decode APDU."); } - int res = exchange_14b_apdu(data, datalen, activate_field, leave_signal_on, data, PM3_CMD_DATA_SIZE, &datalen); + int res = exchange_14b_apdu(data, datalen, activate_field, leave_signal_on, data, PM3_CMD_DATA_SIZE, &datalen, user_timeout); if (res != PM3_SUCCESS) { return res; } @@ -1708,7 +1841,7 @@ static int CmdHF14BNdef(const char *Cmd) { uint8_t aSELECT_AID[80]; int aSELECT_AID_n = 0; param_gethex_to_eol("00a4040007d276000085010100", 0, aSELECT_AID, sizeof(aSELECT_AID), &aSELECT_AID_n); - int res = exchange_14b_apdu(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + int res = exchange_14b_apdu(aSELECT_AID, aSELECT_AID_n, activate_field, keep_field_on, response, sizeof(response), &resplen, -1); if (res) { goto out; } @@ -1734,7 +1867,7 @@ static int CmdHF14BNdef(const char *Cmd) { uint8_t aSELECT_FILE_NDEF[30]; int aSELECT_FILE_NDEF_n = 0; param_gethex_to_eol("00a4000c020001", 0, aSELECT_FILE_NDEF, sizeof(aSELECT_FILE_NDEF), &aSELECT_FILE_NDEF_n); - res = exchange_14b_apdu(aSELECT_FILE_NDEF, aSELECT_FILE_NDEF_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + res = exchange_14b_apdu(aSELECT_FILE_NDEF, aSELECT_FILE_NDEF_n, activate_field, keep_field_on, response, sizeof(response), &resplen, -1); if (res) goto out; @@ -1749,7 +1882,7 @@ static int CmdHF14BNdef(const char *Cmd) { uint8_t aREAD_NDEF[30]; int aREAD_NDEF_n = 0; param_gethex_to_eol("00b0000002", 0, aREAD_NDEF, sizeof(aREAD_NDEF), &aREAD_NDEF_n); - res = exchange_14b_apdu(aREAD_NDEF, aREAD_NDEF_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + res = exchange_14b_apdu(aREAD_NDEF, aREAD_NDEF_n, activate_field, keep_field_on, response, sizeof(response), &resplen, -1); if (res) { goto out; } @@ -1768,7 +1901,7 @@ static int CmdHF14BNdef(const char *Cmd) { aREAD_NDEF_n = 0; param_gethex_to_eol("00b00002", 0, aREAD_NDEF, sizeof(aREAD_NDEF), &aREAD_NDEF_n); aREAD_NDEF[4] = offset; - res = exchange_14b_apdu(aREAD_NDEF, aREAD_NDEF_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + res = exchange_14b_apdu(aREAD_NDEF, aREAD_NDEF_n, activate_field, keep_field_on, response, sizeof(response), &resplen, -1); if (res) { goto out; } @@ -1816,14 +1949,14 @@ int CmdHF14B(const char *Cmd) { } // get and print all info known about any known 14b tag -int infoHF14B(bool verbose) { +int infoHF14B(bool verbose, bool do_aid_search) { // try std 14b (atqb) - if (HF14B_Std_Info(verbose)) + if (HF14B_Std_Info(verbose, do_aid_search)) return 1; // try ST 14b - if (HF14B_ST_Info(verbose)) + if (HF14B_ST_Info(verbose, do_aid_search)) return 1; // try unknown 14b read commands (to be identified later) diff --git a/client/src/cmdhf14b.h b/client/src/cmdhf14b.h index 2058ea8ba..bd97bbf5c 100644 --- a/client/src/cmdhf14b.h +++ b/client/src/cmdhf14b.h @@ -15,6 +15,8 @@ int CmdHF14B(const char *Cmd); -int infoHF14B(bool verbose); +int exchange_14b_apdu(uint8_t *datain, int datainlen, bool activate_field, bool leave_signal_on, uint8_t *dataout, int maxdataoutlen, int *dataoutlen, int user_timeout); + +int infoHF14B(bool verbose, bool do_aid_search); int readHF14B(bool verbose); #endif diff --git a/client/src/cmdhf15.c b/client/src/cmdhf15.c index 3eebbce74..1fd1fb034 100644 --- a/client/src/cmdhf15.c +++ b/client/src/cmdhf15.c @@ -459,6 +459,10 @@ static int nxp_15693_print_signature(uint8_t *uid, uint8_t *signature) { // uid[8] tag uid // returns description of the best match static const char *getTagInfo_15(uint8_t *uid) { + if (uid == NULL) { + return ""; + } + uint64_t myuid, mask; int i = 0, best = -1; memcpy(&myuid, uid, sizeof(uint64_t)); @@ -571,6 +575,10 @@ bool readHF15Uid(bool loop, bool verbose) { * **cmd command line */ static bool prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t *req, uint8_t iso15cmd) { // reqlen arg0 + + if (*cmd == NULL || strlen(*cmd) == 0) + return false; + int temp; uint8_t uid[8] = {0x00}; uint32_t tmpreqlen = 0; @@ -594,13 +602,12 @@ static bool prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t // strip while (**cmd == ' ' || **cmd == '\t')(*cmd)++; - switch (**cmd) { + char c = tolower(**cmd); + switch (c) { case 0: PrintAndLogEx(WARNING, "missing addr"); return false; - break; case 'u': - case 'U': // unaddressed mode may not be supported by all vendors req[tmpreqlen++] |= ISO15_REQ_SUBCARRIER_SINGLE | ISO15_REQ_DATARATE_HIGH | ISO15_REQ_NONINVENTORY; req[tmpreqlen++] = iso15cmd; @@ -633,9 +640,9 @@ static bool prepareHF15Cmd(char **cmd, uint16_t *reqlen, uint8_t *arg1, uint8_t break; } // skip to next space - while (**cmd != ' ' && **cmd != '\t')(*cmd)++; + while (**cmd != '\0' && **cmd != ' ' && **cmd != '\t')(*cmd)++; // skip over the space - while (**cmd == ' ' || **cmd == '\t')(*cmd)++; + while (**cmd != '\0' && (**cmd == ' ' || **cmd == '\t'))(*cmd)++; *reqlen = tmpreqlen; return true; @@ -1348,7 +1355,8 @@ static int CmdHF15Dump(const char *Cmd) { // copy uid to read command memcpy(req + 2, uid, sizeof(uid)); - + + PrintAndLogEx(INFO, "." NOLF); for (int retry = 0; retry < 5; retry++) { req[10] = blocknum; @@ -1364,6 +1372,7 @@ static int CmdHF15Dump(const char *Cmd) { continue; } if (len < 2) { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(FAILED, "iso15693 command failed"); continue; } @@ -1371,11 +1380,13 @@ static int CmdHF15Dump(const char *Cmd) { recv = resp.data.asBytes; if (CheckCrc15(recv, len) == false) { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(FAILED, "crc (" _RED_("fail") ")"); continue; } if ((recv[0] & ISO15_RES_ERROR) == ISO15_RES_ERROR) { + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(FAILED, "Tag returned Error %i: %s", recv[1], TagErrorStr(recv[1])); break; } @@ -1395,12 +1406,12 @@ static int CmdHF15Dump(const char *Cmd) { DropField(); PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "block# | data |lck| ascii"); - PrintAndLogEx(NORMAL, "---------+--------------+---+----------"); + PrintAndLogEx(INFO, "block# | data |lck| ascii"); + PrintAndLogEx(INFO, "---------+--------------+---+----------"); for (int i = 0; i < blocknum; i++) { - PrintAndLogEx(NORMAL, "%3d/0x%02X | %s | %d | %s", i, i, sprint_hex(mem[i].block, 4), mem[i].lock, sprint_ascii(mem[i].block, 4)); + PrintAndLogEx(INFO, "%3d/0x%02X | %s | %d | %s", i, i, sprint_hex(mem[i].block, 4), mem[i].lock, sprint_ascii(mem[i].block, 4)); } - PrintAndLogEx(NORMAL, "\n"); + PrintAndLogEx(NORMAL, ""); size_t datalen = blocknum * 4; saveFile(filename, ".bin", data, datalen); @@ -1591,14 +1602,15 @@ static int CmdHF15Readmulti(const char *Cmd) { PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "block# | data |lck| ascii"); - PrintAndLogEx(NORMAL, "---------+--------------+---+----------"); + PrintAndLogEx(INFO, "block# | data |lck| ascii"); + PrintAndLogEx(INFO, "---------+--------------+---+----------"); for (int i = start; i < stop; i += 5) { - PrintAndLogEx(NORMAL, "%3d/0x%02X | %s | %d | %s", currblock, currblock, sprint_hex(data + i + 1, 4), data[i], sprint_ascii(data + i + 1, 4)); + PrintAndLogEx(INFO, "%3d/0x%02X | %s | %d | %s", currblock, currblock, sprint_hex(data + i + 1, 4), data[i], sprint_ascii(data + i + 1, 4)); currblock++; } + PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } @@ -1672,9 +1684,9 @@ static int CmdHF15Read(const char *Cmd) { // print response PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "block #%3d |lck| ascii", blocknum); - PrintAndLogEx(NORMAL, "------------+---+------"); - PrintAndLogEx(NORMAL, "%s| %d | %s", sprint_hex(data + 2, status - 4), data[1], sprint_ascii(data + 2, status - 4)); + PrintAndLogEx(INFO, "block #%3d |lck| ascii", blocknum); + PrintAndLogEx(INFO, "------------+---+------"); + PrintAndLogEx(INFO, "%s| %d | %s", sprint_hex(data + 2, status - 4), data[1], sprint_ascii(data + 2, status - 4)); PrintAndLogEx(NORMAL, ""); return PM3_SUCCESS; } diff --git a/client/src/cmdhfemrtd.c b/client/src/cmdhfemrtd.c new file mode 100644 index 000000000..feb8806e7 --- /dev/null +++ b/client/src/cmdhfemrtd.c @@ -0,0 +1,2268 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2020 A. Ozkal +// +// 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. +//----------------------------------------------------------------------------- +// High frequency Electronic Machine Readable Travel Document commands +//----------------------------------------------------------------------------- + +// This code is heavily based on mrpkey.py of RFIDIOt + +#include "cmdhfemrtd.h" +#include +#include "fileutils.h" // saveFile +#include "cmdparser.h" // command_t +#include "cmdtrace.h" // CmdTraceList +#include "cliparser.h" // CLIParserContext etc +#include "cmdhf14a.h" // ExchangeAPDU14a +#include "protocols.h" // definitions of ISO14A/7816 protocol +#include "emv/apduinfo.h" // GetAPDUCodeDescription +#include "crypto/libpcrypto.h" // Hash calculation (sha1, sha256, sha512) +#include "mifare/desfire_crypto.h" // des_encrypt/des_decrypt +#include "des.h" // mbedtls_des_key_set_parity +#include "cmdhf14b.h" // exchange_14b_apdu +#include "iso14b.h" // ISO14B_CONNECT etc +#include "crapto1/crapto1.h" // prng_successor +#include "commonutil.h" // num_to_bytes +#include "util_posix.h" // msclock + +// Max file size in bytes. Used in several places. +// Average EF_DG2 seems to be around 20-25kB or so, but ICAO doesn't set an upper limit +// Iris data seems to be suggested to be around 35kB per eye (Presumably bumping up the file size to around 70kB) +// but as we cannot read that until we implement PACE, 35k seems to be a safe point. +#define EMRTD_MAX_FILE_SIZE 35000 + +// ISO7816 commands +#define EMRTD_SELECT "A4" +#define EMRTD_EXTERNAL_AUTHENTICATE "82" +#define EMRTD_GET_CHALLENGE "84" +#define EMRTD_READ_BINARY "B0" +#define EMRTD_P1_SELECT_BY_EF "02" +#define EMRTD_P1_SELECT_BY_NAME "04" +#define EMRTD_P2_PROPRIETARY "0C" + +// App IDs +#define EMRTD_AID_MRTD "A0000002471001" + +// DESKey Types +const uint8_t KENC_type[4] = {0x00, 0x00, 0x00, 0x01}; +const uint8_t KMAC_type[4] = {0x00, 0x00, 0x00, 0x02}; + +static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const char *path); +static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length, const char *path); +static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const char *path); +static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const char *path); +static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen); +static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen); +static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen); +static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen); +static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen); + +typedef enum { // list must match dg_table + EF_COM = 0, + EF_DG1, + EF_DG2, + EF_DG3, + EF_DG4, + EF_DG5, + EF_DG6, + EF_DG7, + EF_DG8, + EF_DG9, + EF_DG10, + EF_DG11, + EF_DG12, + EF_DG13, + EF_DG14, + EF_DG15, + EF_DG16, + EF_SOD, + EF_CardAccess, + EF_CardSecurity, +} emrtd_dg_enum; + +static emrtd_dg_t dg_table[] = { +// tag dg# fileid filename desc pace eac req fast parser dumper + {0x60, 0, "011E", "EF_COM", "Header and Data Group Presence Information", false, false, true, true, emrtd_print_ef_com_info, NULL}, + {0x61, 1, "0101", "EF_DG1", "Details recorded in MRZ", false, false, true, true, emrtd_print_ef_dg1_info, NULL}, + {0x75, 2, "0102", "EF_DG2", "Encoded Face", false, false, true, false, NULL, emrtd_dump_ef_dg2}, + {0x63, 3, "0103", "EF_DG3", "Encoded Finger(s)", false, true, false, false, NULL, NULL}, + {0x76, 4, "0104", "EF_DG4", "Encoded Eye(s)", false, true, false, false, NULL, NULL}, + {0x65, 5, "0105", "EF_DG5", "Displayed Portrait", false, false, false, false, NULL, emrtd_dump_ef_dg5}, + {0x66, 6, "0106", "EF_DG6", "Reserved for Future Use", false, false, false, false, NULL, NULL}, + {0x67, 7, "0107", "EF_DG7", "Displayed Signature or Usual Mark", false, false, false, false, NULL, emrtd_dump_ef_dg7}, + {0x68, 8, "0108", "EF_DG8", "Data Feature(s)", false, false, false, true, NULL, NULL}, + {0x69, 9, "0109", "EF_DG9", "Structure Feature(s)", false, false, false, true, NULL, NULL}, + {0x6a, 10, "010A", "EF_DG10", "Substance Feature(s)", false, false, false, true, NULL, NULL}, + {0x6b, 11, "010B", "EF_DG11", "Additional Personal Detail(s)", false, false, false, true, emrtd_print_ef_dg11_info, NULL}, + {0x6c, 12, "010C", "EF_DG12", "Additional Document Detail(s)", false, false, false, true, emrtd_print_ef_dg12_info, NULL}, + {0x6d, 13, "010D", "EF_DG13", "Optional Detail(s)", false, false, false, true, NULL, NULL}, + {0x6e, 14, "010E", "EF_DG14", "Security Options", false, false, false, true, NULL, NULL}, + {0x6f, 15, "010F", "EF_DG15", "Active Authentication Public Key Info", false, false, false, true, NULL, NULL}, + {0x70, 16, "0110", "EF_DG16", "Person(s) to Notify", false, false, false, true, NULL, NULL}, + {0x77, 0, "011D", "EF_SOD", "Document Security Object", false, false, false, false, NULL, emrtd_dump_ef_sod}, + {0xff, 0, "011C", "EF_CardAccess", "PACE SecurityInfos", true, false, true, true, emrtd_print_ef_cardaccess_info, NULL}, + {0xff, 0, "011D", "EF_CardSecurity", "PACE SecurityInfos for Chip Authentication Mapping", true, false, false, true, NULL, NULL}, + {0x00, 0, NULL, NULL, NULL, false, false, false, false, NULL, NULL} +}; + +// https://security.stackexchange.com/questions/131241/where-do-magic-constants-for-signature-algorithms-come-from +// https://tools.ietf.org/html/rfc3447#page-43 +static emrtd_hashalg_t hashalg_table[] = { +// name hash func len len descriptor + {"SHA-1", sha1hash, 20, 7, {0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A}}, + {"SHA-256", sha256hash, 32, 11, {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}}, + {"SHA-512", sha512hash, 64, 11, {0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03}}, + {NULL, NULL, 0, 0, {}} +}; + +static emrtd_pacealg_t pacealg_table[] = { +// name keygen descriptor + {"DH, Generic Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x01}}, + {"DH, Generic Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x02}}, + {"DH, Generic Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x03}}, + {"DH, Generic Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x01, 0x04}}, + {"ECDH, Generic Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x01}}, + {"ECDH, Generic Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x02}}, + {"ECDH, Generic Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x03}}, + {"ECDH, Generic Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x02, 0x04}}, + {"DH, Integrated Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x01}}, + {"DH, Integrated Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x02}}, + {"DH, Integrated Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x03}}, + {"DH, Integrated Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x03, 0x04}}, + {"ECDH, Integrated Mapping, 3DES-CBC-CBC", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x01}}, + {"ECDH, Integrated Mapping, AES-CMAC-128", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x02}}, + {"ECDH, Integrated Mapping, AES-CMAC-192", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x03}}, + {"ECDH, Integrated Mapping, AES-CMAC-256", NULL, {0x04, 0x00, 0x7F, 0x00, 0x07, 0x02, 0x02, 0x04, 0x04, 0x04}}, + {NULL, NULL, {}} +}; + +static emrtd_pacesdp_t pacesdp_table[] = { +// id name size + {0, "1024-bit MODP Group with 160-bit Prime Order Subgroup", 1024}, + {1, "2048-bit MODP Group with 224-bit Prime Order Subgroup", 2048}, + {2, "2048-bit MODP Group with 256-bit Prime Order Subgroup", 2048}, + {8, "NIST P-192 (secp192r1)", 192}, + {10, "NIST P-224 (secp224r1)", 224}, + {12, "NIST P-256 (secp256r1)", 256}, + {15, "NIST P-384 (secp384r1)", 384}, + {18, "NIST P-521 (secp521r1)", 521}, + {9, "BrainpoolP192r1", 192}, + {11, "BrainpoolP224r1", 224}, + {13, "BrainpoolP256r1", 256}, + {14, "BrainpoolP320r1", 320}, + {16, "BrainpoolP384r1", 384}, + {17, "BrainpoolP521r1", 521}, + {32, NULL, 0} +}; + +static emrtd_dg_t *emrtd_tag_to_dg(uint8_t tag) { + for (int dgi = 0; dg_table[dgi].filename != NULL; dgi++) { + if (dg_table[dgi].tag == tag) { + return &dg_table[dgi]; + } + } + return NULL; +} +static emrtd_dg_t *emrtd_fileid_to_dg(const char *file_id) { + for (int dgi = 0; dg_table[dgi].filename != NULL; dgi++) { + if (strcmp(dg_table[dgi].fileid, file_id) == 0) { + return &dg_table[dgi]; + } + } + return NULL; +} + +static int CmdHelp(const char *Cmd); + +static uint16_t get_sw(uint8_t *d, uint8_t n) { + if (n < 2) + return 0; + + n -= 2; + return d[n] * 0x0100 + d[n + 1]; +} + +static bool emrtd_exchange_commands(const char *cmd, uint8_t *dataout, int *dataoutlen, bool activate_field, bool keep_field_on, bool use_14b) { + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + PrintAndLogEx(DEBUG, "Sending: %s", cmd); + + uint8_t aCMD[PM3_CMD_DATA_SIZE]; + int aCMD_n = 0; + param_gethex_to_eol(cmd, 0, aCMD, sizeof(aCMD), &aCMD_n); + int res; + if (use_14b) { + // need to add a long timeout for passports with activated anti-bruteforce measure + res = exchange_14b_apdu(aCMD, aCMD_n, activate_field, keep_field_on, response, sizeof(response), &resplen, 15000); + } else { + res = ExchangeAPDU14a(aCMD, aCMD_n, activate_field, keep_field_on, response, sizeof(response), &resplen); + } + if (res) { + DropField(); + return false; + } + + if (resplen < 2) { + return false; + } + PrintAndLogEx(DEBUG, "Response: %s", sprint_hex(response, resplen)); + + // drop sw + memcpy(dataout, &response, resplen - 2); + *dataoutlen = (resplen - 2); + + uint16_t sw = get_sw(response, resplen); + if (sw != 0x9000) { + PrintAndLogEx(DEBUG, "Command %s failed (%04x - %s).", cmd, sw, GetAPDUCodeDescription(sw >> 8, sw & 0xff)); + return false; + } + return true; +} + +static int emrtd_exchange_commands_noout(const char *cmd, bool activate_field, bool keep_field_on, bool use_14b) { + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + return emrtd_exchange_commands(cmd, response, &resplen, activate_field, keep_field_on, use_14b); +} + +static char emrtd_calculate_check_digit(char *data) { + int mrz_weight[] = {7, 3, 1}; + int value, cd = 0; + + for (int i = 0; i < strlen(data); i++) { + char d = data[i]; + if ('A' <= d && d <= 'Z') { + value = d - 55; + } else if ('a' <= d && d <= 'z') { + value = d - 87; + } else if (d == '<') { + value = 0; + } else { // Numbers + value = d - 48; + } + cd += value * mrz_weight[i % 3]; + } + return cd % 10; +} + +static int emrtd_get_asn1_data_length(uint8_t *datain, int datainlen, int offset) { + PrintAndLogEx(DEBUG, "asn1 datalength, datain: %s", sprint_hex_inrow(datain, datainlen)); + int lenfield = (int) * (datain + offset); + PrintAndLogEx(DEBUG, "asn1 datalength, lenfield: %02X", lenfield); + if (lenfield <= 0x7f) { + return lenfield; + } else if (lenfield == 0x80) { + // TODO: 0x80 means indeterminate, and this impl is a workaround. + // Giving rest of the file is a workaround, nothing more, nothing less. + // https://wf.lavatech.top/ave-but-random/emrtd-data-quirks#EF_SOD + return datainlen; + } else if (lenfield == 0x81) { + return ((int) * (datain + offset + 1)); + } else if (lenfield == 0x82) { + return ((int) * (datain + offset + 1) << 8) | ((int) * (datain + offset + 2)); + } else if (lenfield == 0x83) { + return (((int) * (datain + offset + 1) << 16) | ((int) * (datain + offset + 2)) << 8) | ((int) * (datain + offset + 3)); + } + return false; +} + +static int emrtd_get_asn1_field_length(uint8_t *datain, int datainlen, int offset) { + PrintAndLogEx(DEBUG, "asn1 fieldlength, datain: %s", sprint_hex_inrow(datain, datainlen)); + int lenfield = (int) * (datain + offset); + PrintAndLogEx(DEBUG, "asn1 fieldlength, lenfield: %02X", lenfield); + if (lenfield <= 0x80) { + return 1; + } else if (lenfield == 0x81) { + return 2; + } else if (lenfield == 0x82) { + return 3; + } else if (lenfield == 0x83) { + return 4; + } + return false; +} + +static void des_encrypt_ecb(uint8_t *key, uint8_t *input, uint8_t *output) { + mbedtls_des_context ctx_enc; + mbedtls_des_setkey_enc(&ctx_enc, key); + mbedtls_des_crypt_ecb(&ctx_enc, input, output); + mbedtls_des_free(&ctx_enc); +} + +static void des_decrypt_ecb(uint8_t *key, uint8_t *input, uint8_t *output) { + mbedtls_des_context ctx_dec; + mbedtls_des_setkey_dec(&ctx_dec, key); + mbedtls_des_crypt_ecb(&ctx_dec, input, output); + mbedtls_des_free(&ctx_dec); +} + +static void des3_encrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) { + mbedtls_des3_context ctx; + mbedtls_des3_set2key_enc(&ctx, key); + + mbedtls_des3_crypt_cbc(&ctx // des3_context + , MBEDTLS_DES_ENCRYPT // int mode + , inputlen // length + , iv // iv[8] + , input // input + , output // output + ); + mbedtls_des3_free(&ctx); +} + +static void des3_decrypt_cbc(uint8_t *iv, uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) { + mbedtls_des3_context ctx; + mbedtls_des3_set2key_dec(&ctx, key); + + mbedtls_des3_crypt_cbc(&ctx // des3_context + , MBEDTLS_DES_DECRYPT // int mode + , inputlen // length + , iv // iv[8] + , input // input + , output // output + ); + mbedtls_des3_free(&ctx); +} + +static int pad_block(uint8_t *input, int inputlen, uint8_t *output) { + uint8_t padding[8] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + memcpy(output, input, inputlen); + + int to_pad = (8 - (inputlen % 8)); + + for (int i = 0; i < to_pad; i++) { + output[inputlen + i] = padding[i]; + } + + return inputlen + to_pad; +} + +static void retail_mac(uint8_t *key, uint8_t *input, int inputlen, uint8_t *output) { + // This code assumes blocklength (n) = 8, and input len of up to 240 or so chars + // This code takes inspirations from https://github.com/devinvenable/iso9797algorithm3 + uint8_t k0[8]; + uint8_t k1[8]; + uint8_t intermediate[8] = {0x00}; + uint8_t intermediate_des[256]; + uint8_t block[8]; + uint8_t message[256]; + + // Populate keys + memcpy(k0, key, 8); + memcpy(k1, key + 8, 8); + + // Prepare message + int blocksize = pad_block(input, inputlen, message); + + // Do chaining and encryption + for (int i = 0; i < (blocksize / 8); i++) { + memcpy(block, message + (i * 8), 8); + + // XOR + for (int x = 0; x < 8; x++) { + intermediate[x] = intermediate[x] ^ block[x]; + } + + des_encrypt_ecb(k0, intermediate, intermediate_des); + memcpy(intermediate, intermediate_des, 8); + } + + + des_decrypt_ecb(k1, intermediate, intermediate_des); + memcpy(intermediate, intermediate_des, 8); + + des_encrypt_ecb(k0, intermediate, intermediate_des); + memcpy(output, intermediate_des, 8); +} + +static void emrtd_deskey(uint8_t *seed, const uint8_t *type, int length, uint8_t *dataout) { + PrintAndLogEx(DEBUG, "seed.............. %s", sprint_hex_inrow(seed, 16)); + + // combine seed and type + uint8_t data[50]; + memcpy(data, seed, length); + memcpy(data + length, type, 4); + PrintAndLogEx(DEBUG, "data.............. %s", sprint_hex_inrow(data, length + 4)); + + // SHA1 the key + unsigned char key[64]; + sha1hash(data, length + 4, key); + PrintAndLogEx(DEBUG, "key............... %s", sprint_hex_inrow(key, length + 4)); + + // Set parity bits + for (int i = 0; i < ((length + 4) / 8); i++) { + mbedtls_des_key_set_parity(key + (i * 8)); + } + PrintAndLogEx(DEBUG, "post-parity key... %s", sprint_hex_inrow(key, 20)); + + memcpy(dataout, &key, length); +} + +static int emrtd_select_file(const char *select_by, const char *file_id, bool use_14b) { + int file_id_len = strlen(file_id) / 2; + + char cmd[50]; + sprintf(cmd, "00%s%s0C%02X%s", EMRTD_SELECT, select_by, file_id_len, file_id); + + return emrtd_exchange_commands_noout(cmd, false, true, use_14b); +} + +static int emrtd_get_challenge(int length, uint8_t *dataout, int *dataoutlen, bool use_14b) { + char cmd[50]; + sprintf(cmd, "00%s0000%02X", EMRTD_GET_CHALLENGE, length); + + return emrtd_exchange_commands(cmd, dataout, dataoutlen, false, true, use_14b); +} + +static int emrtd_external_authenticate(uint8_t *data, int length, uint8_t *dataout, int *dataoutlen, bool use_14b) { + char cmd[100]; + sprintf(cmd, "00%s0000%02X%s%02X", EMRTD_EXTERNAL_AUTHENTICATE, length, sprint_hex_inrow(data, length), length); + return emrtd_exchange_commands(cmd, dataout, dataoutlen, false, true, use_14b); +} + +static int _emrtd_read_binary(int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen, bool use_14b) { + char cmd[50]; + sprintf(cmd, "00%s%04X%02X", EMRTD_READ_BINARY, offset, bytes_to_read); + + return emrtd_exchange_commands(cmd, dataout, dataoutlen, false, true, use_14b); +} + +static void emrtd_bump_ssc(uint8_t *ssc) { + PrintAndLogEx(DEBUG, "ssc-b: %s", sprint_hex_inrow(ssc, 8)); + for (int i = 7; i > 0; i--) { + if ((*(ssc + i)) == 0xFF) { + // Set anything already FF to 0, we'll do + 1 on num to left anyways + (*(ssc + i)) = 0; + } else { + (*(ssc + i)) += 1; + PrintAndLogEx(DEBUG, "ssc-a: %s", sprint_hex_inrow(ssc, 8)); + return; + } + } +} + +static bool emrtd_check_cc(uint8_t *ssc, uint8_t *key, uint8_t *rapdu, int rapdulength) { + // https://elixi.re/i/clarkson.png + uint8_t k[500]; + uint8_t cc[500]; + + emrtd_bump_ssc(ssc); + + memcpy(k, ssc, 8); + int length = 0; + int length2 = 0; + + if (*(rapdu) == 0x87) { + length += 2 + (*(rapdu + 1)); + memcpy(k + 8, rapdu, length); + PrintAndLogEx(DEBUG, "len1: %i", length); + } + + if ((*(rapdu + length)) == 0x99) { + length2 += 2 + (*(rapdu + (length + 1))); + memcpy(k + length + 8, rapdu + length, length2); + PrintAndLogEx(DEBUG, "len2: %i", length2); + } + + int klength = length + length2 + 8; + + retail_mac(key, k, klength, cc); + PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, 8)); + PrintAndLogEx(DEBUG, "rapdu: %s", sprint_hex_inrow(rapdu, rapdulength)); + PrintAndLogEx(DEBUG, "rapdu cut: %s", sprint_hex_inrow(rapdu + (rapdulength - 8), 8)); + PrintAndLogEx(DEBUG, "k: %s", sprint_hex_inrow(k, klength)); + + return memcmp(cc, rapdu + (rapdulength - 8), 8) == 0; +} + +static void _emrtd_convert_filename(const char *file, uint8_t *dataout) { + char temp[3] = {0x00}; + memcpy(temp, file, 2); + dataout[0] = (int)strtol(temp, NULL, 16); + memcpy(temp, file + 2, 2); + dataout[1] = (int)strtol(temp, NULL, 16); +} + +static bool emrtd_secure_select_file(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, const char *select_by, const char *file, bool use_14b) { + uint8_t response[PM3_CMD_DATA_SIZE]; + int resplen = 0; + + // convert filename of string to bytes + uint8_t file_id[2]; + _emrtd_convert_filename(file, file_id); + + uint8_t iv[8] = { 0x00 }; + char command[PM3_CMD_DATA_SIZE]; + uint8_t cmd[8]; + uint8_t data[21]; + uint8_t temp[8] = {0x0c, 0xa4, strtol(select_by, NULL, 16), 0x0c}; + + int cmdlen = pad_block(temp, 4, cmd); + int datalen = pad_block(file_id, 2, data); + PrintAndLogEx(DEBUG, "cmd: %s", sprint_hex_inrow(cmd, cmdlen)); + PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, datalen)); + + des3_encrypt_cbc(iv, kenc, data, datalen, temp); + PrintAndLogEx(DEBUG, "temp: %s", sprint_hex_inrow(temp, datalen)); + uint8_t do87[11] = {0x87, 0x09, 0x01}; + memcpy(do87 + 3, temp, datalen); + PrintAndLogEx(DEBUG, "do87: %s", sprint_hex_inrow(do87, datalen + 3)); + + uint8_t m[19]; + memcpy(m, cmd, cmdlen); + memcpy(m + cmdlen, do87, (datalen + 3)); + PrintAndLogEx(DEBUG, "m: %s", sprint_hex_inrow(m, datalen + cmdlen + 3)); + + emrtd_bump_ssc(ssc); + + uint8_t n[27]; + memcpy(n, ssc, 8); + memcpy(n + 8, m, (cmdlen + datalen + 3)); + PrintAndLogEx(DEBUG, "n: %s", sprint_hex_inrow(n, (cmdlen + datalen + 11))); + + uint8_t cc[8]; + retail_mac(kmac, n, (cmdlen + datalen + 11), cc); + PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, 8)); + + uint8_t do8e[10] = {0x8E, 0x08}; + memcpy(do8e + 2, cc, 8); + PrintAndLogEx(DEBUG, "do8e: %s", sprint_hex_inrow(do8e, 10)); + + int lc = datalen + 3 + 10; + PrintAndLogEx(DEBUG, "lc: %i", lc); + + memcpy(data, do87, datalen + 3); + memcpy(data + (datalen + 3), do8e, 10); + PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc)); + + sprintf(command, "0C%s%s0C%02X%s00", EMRTD_SELECT, select_by, lc, sprint_hex_inrow(data, lc)); + PrintAndLogEx(DEBUG, "command: %s", command); + + if (emrtd_exchange_commands(command, response, &resplen, false, true, use_14b) == false) { + return false; + } + + return emrtd_check_cc(ssc, kmac, response, resplen); +} + +static bool _emrtd_secure_read_binary(uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen, bool use_14b) { + char command[54]; + uint8_t cmd[8]; + uint8_t data[21]; + uint8_t temp[8] = {0x0c, 0xb0}; + + PrintAndLogEx(DEBUG, "kmac: %s", sprint_hex_inrow(kmac, 20)); + + // Set p1 and p2 + temp[2] = (uint8_t)(offset >> 8); + temp[3] = (uint8_t)(offset >> 0); + + int cmdlen = pad_block(temp, 4, cmd); + PrintAndLogEx(DEBUG, "cmd: %s", sprint_hex_inrow(cmd, cmdlen)); + + uint8_t do97[3] = {0x97, 0x01, bytes_to_read}; + + uint8_t m[11]; + memcpy(m, cmd, 8); + memcpy(m + 8, do97, 3); + + emrtd_bump_ssc(ssc); + + uint8_t n[19]; + memcpy(n, ssc, 8); + memcpy(n + 8, m, 11); + PrintAndLogEx(DEBUG, "n: %s", sprint_hex_inrow(n, 19)); + + uint8_t cc[8]; + retail_mac(kmac, n, 19, cc); + PrintAndLogEx(DEBUG, "cc: %s", sprint_hex_inrow(cc, 8)); + + uint8_t do8e[10] = {0x8E, 0x08}; + memcpy(do8e + 2, cc, 8); + PrintAndLogEx(DEBUG, "do8e: %s", sprint_hex_inrow(do8e, 10)); + + int lc = 13; + PrintAndLogEx(DEBUG, "lc: %i", lc); + + memcpy(data, do97, 3); + memcpy(data + 3, do8e, 10); + PrintAndLogEx(DEBUG, "data: %s", sprint_hex_inrow(data, lc)); + + sprintf(command, "0C%s%04X%02X%s00", EMRTD_READ_BINARY, offset, lc, sprint_hex_inrow(data, lc)); + PrintAndLogEx(DEBUG, "command: %s", command); + + if (emrtd_exchange_commands(command, dataout, dataoutlen, false, true, use_14b) == false) { + return false; + } + + return emrtd_check_cc(ssc, kmac, dataout, *dataoutlen); +} + +static bool _emrtd_secure_read_binary_decrypt(uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, int offset, int bytes_to_read, uint8_t *dataout, int *dataoutlen, bool use_14b) { + uint8_t response[500]; + uint8_t temp[500]; + int resplen, cutat = 0; + uint8_t iv[8] = { 0x00 }; + + if (_emrtd_secure_read_binary(kmac, ssc, offset, bytes_to_read, response, &resplen, use_14b) == false) { + return false; + } + + PrintAndLogEx(DEBUG, "secreadbindec, offset %i on read %i: encrypted: %s", offset, bytes_to_read, sprint_hex_inrow(response, resplen)); + + cutat = ((int) response[1]) - 1; + + des3_decrypt_cbc(iv, kenc, response + 3, cutat, temp); + memcpy(dataout, temp, bytes_to_read); + PrintAndLogEx(DEBUG, "secreadbindec, offset %i on read %i: decrypted: %s", offset, bytes_to_read, sprint_hex_inrow(temp, cutat)); + PrintAndLogEx(DEBUG, "secreadbindec, offset %i on read %i: decrypted and cut: %s", offset, bytes_to_read, sprint_hex_inrow(dataout, bytes_to_read)); + *dataoutlen = bytes_to_read; + return true; +} + +static int emrtd_read_file(uint8_t *dataout, int *dataoutlen, uint8_t *kenc, uint8_t *kmac, uint8_t *ssc, bool use_secure, bool use_14b) { + uint8_t response[EMRTD_MAX_FILE_SIZE]; + int resplen = 0; + uint8_t tempresponse[500]; + int tempresplen = 0; + int toread = 4; + int offset = 0; + + if (use_secure) { + if (_emrtd_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, response, &resplen, use_14b) == false) { + return false; + } + } else { + if (_emrtd_read_binary(offset, toread, response, &resplen, use_14b) == false) { + return false; + } + } + + int datalen = emrtd_get_asn1_data_length(response, resplen, 1); + int readlen = datalen - (3 - emrtd_get_asn1_field_length(response, resplen, 1)); + offset = 4; + + while (readlen > 0) { + toread = readlen; + if (readlen > 118) { + toread = 118; + } + + if (use_secure) { + if (_emrtd_secure_read_binary_decrypt(kenc, kmac, ssc, offset, toread, tempresponse, &tempresplen, use_14b) == false) { + return false; + } + } else { + if (_emrtd_read_binary(offset, toread, tempresponse, &tempresplen, use_14b) == false) { + return false; + } + } + + memcpy(response + resplen, tempresponse, tempresplen); + offset += toread; + readlen -= toread; + resplen += tempresplen; + } + + memcpy(dataout, &response, resplen); + *dataoutlen = resplen; + return true; +} + +static int emrtd_lds_determine_tag_length(uint8_t tag) { + if ((tag == 0x5F) || (tag == 0x7F)) { + return 2; + } + return 1; +} + +static bool emrtd_lds_get_data_by_tag(uint8_t *datain, size_t datainlen, uint8_t *dataout, size_t *dataoutlen, int tag1, int tag2, bool twobytetag, bool entertoptag, size_t skiptagcount) { + int offset = 0; + int skipcounter = 0; + + if (entertoptag) { + offset += emrtd_lds_determine_tag_length(*datain); + offset += emrtd_get_asn1_field_length(datain, datainlen, offset); + } + + while (offset < datainlen) { + PrintAndLogEx(DEBUG, "emrtd_lds_get_data_by_tag, offset: %i, data: %X", offset, *(datain + offset)); + // Determine element ID length to set as offset on asn1datalength + int e_idlen = emrtd_lds_determine_tag_length(*(datain + offset)); + + // Get the length of the element + int e_datalen = emrtd_get_asn1_data_length(datain + offset, datainlen - offset, e_idlen); + + // Get the length of the element's length + int e_fieldlen = emrtd_get_asn1_field_length(datain + offset, datainlen - offset, e_idlen); + + PrintAndLogEx(DEBUG, "emrtd_lds_get_data_by_tag, e_idlen: %02X, e_datalen: %02X, e_fieldlen: %02X", e_idlen, e_datalen, e_fieldlen); + + // If the element is what we're looking for, get the data and return true + if (*(datain + offset) == tag1 && (!twobytetag || *(datain + offset + 1) == tag2)) { + if (skipcounter < skiptagcount) { + skipcounter += 1; + } else if (datainlen > e_datalen) { + *dataoutlen = e_datalen; + memcpy(dataout, datain + offset + e_idlen + e_fieldlen, e_datalen); + return true; + } else { + PrintAndLogEx(ERR, "error (emrtd_lds_get_data_by_tag) e_datalen out-of-bounds"); + return false; + } + } + offset += e_idlen + e_datalen + e_fieldlen; + } + // Return false if we can't find the relevant element + return false; +} + +static bool emrtd_select_and_read(uint8_t *dataout, int *dataoutlen, const char *file, uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, bool use_secure, bool use_14b) { + if (use_secure) { + if (emrtd_secure_select_file(ks_enc, ks_mac, ssc, EMRTD_P1_SELECT_BY_EF, file, use_14b) == false) { + PrintAndLogEx(ERR, "Failed to secure select %s.", file); + return false; + } + } else { + if (emrtd_select_file(EMRTD_P1_SELECT_BY_EF, file, use_14b) == false) { + PrintAndLogEx(ERR, "Failed to select %s.", file); + return false; + } + } + + if (emrtd_read_file(dataout, dataoutlen, ks_enc, ks_mac, ssc, use_secure, use_14b) == false) { + PrintAndLogEx(ERR, "Failed to read %s.", file); + return false; + } + return true; +} + +const uint8_t jpeg_header[4] = { 0xFF, 0xD8, 0xFF, 0xE0 }; +const uint8_t jpeg2k_header[6] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50 }; + +static int emrtd_dump_ef_dg2(uint8_t *file_contents, size_t file_length, const char *path) { + int offset, datalen = 0; + + // This is a hacky impl that just looks for the image header. I'll improve it eventually. + // based on mrpkey.py + // Note: Doing file_length - 6 to account for the longest data we're checking. + // Checks first byte before the rest to reduce overhead + for (offset = 0; offset < file_length - 6; offset++) { + if ((file_contents[offset] == 0xFF && memcmp(jpeg_header, file_contents + offset, 4) == 0) || + (file_contents[offset] == 0x00 && memcmp(jpeg2k_header, file_contents + offset, 6) == 0)) { + datalen = file_length - offset; + break; + } + } + + // If we didn't get any data, return false. + if (datalen == 0) { + return PM3_ESOFT; + } + + char *filepath = calloc(strlen(path) + 100, sizeof(char)); + if (filepath == NULL) + return PM3_EMALLOC; + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg_table[EF_DG2].filename); + + saveFile(filepath, file_contents[offset] == 0xFF ? ".jpg" : ".jp2", file_contents + offset, datalen); + + free(filepath); + return PM3_SUCCESS; +} + +static int emrtd_dump_ef_dg5(uint8_t *file_contents, size_t file_length, const char *path) { + uint8_t data[EMRTD_MAX_FILE_SIZE]; + size_t datalen = 0; + + // If we can't find image in EF_DG5, return false. + if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x40, true, true, 0) == false) { + return PM3_ESOFT; + } + + if (datalen < EMRTD_MAX_FILE_SIZE) { + char *filepath = calloc(strlen(path) + 100, sizeof(char)); + if (filepath == NULL) + return PM3_EMALLOC; + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg_table[EF_DG5].filename); + + saveFile(filepath, data[0] == 0xFF ? ".jpg" : ".jp2", data, datalen); + + free(filepath); + } else { + PrintAndLogEx(ERR, "error (emrtd_dump_ef_dg5) datalen out-of-bounds"); + return PM3_ESOFT; + } + return PM3_SUCCESS; +} + +static int emrtd_dump_ef_dg7(uint8_t *file_contents, size_t file_length, const char *path) { + uint8_t data[EMRTD_MAX_FILE_SIZE]; + size_t datalen = 0; + + // If we can't find image in EF_DG7, return false. + if (emrtd_lds_get_data_by_tag(file_contents, file_length, data, &datalen, 0x5F, 0x42, true, true, 0) == false) { + return PM3_ESOFT; + } + + if (datalen < EMRTD_MAX_FILE_SIZE) { + char *filepath = calloc(strlen(path) + 100, sizeof(char)); + if (filepath == NULL) + return PM3_EMALLOC; + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg_table[EF_DG7].filename); + + saveFile(filepath, data[0] == 0xFF ? ".jpg" : ".jp2", data, datalen); + + free(filepath); + } else { + PrintAndLogEx(ERR, "error (emrtd_dump_ef_dg7) datalen out-of-bounds"); + return PM3_ESOFT; + } + return PM3_SUCCESS; +} + +static int emrtd_dump_ef_sod(uint8_t *file_contents, size_t file_length, const char *path) { + int fieldlen = emrtd_get_asn1_field_length(file_contents, file_length, 1); + int datalen = emrtd_get_asn1_data_length(file_contents, file_length, 1); + + if (fieldlen + 1 > EMRTD_MAX_FILE_SIZE) { + PrintAndLogEx(ERR, "error (emrtd_dump_ef_sod) fieldlen out-of-bounds"); + return PM3_SUCCESS; + } + + char *filepath = calloc(strlen(path) + 100, sizeof(char)); + if (filepath == NULL) + return PM3_EMALLOC; + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg_table[EF_SOD].filename); + + saveFile(filepath, ".p7b", file_contents + fieldlen + 1, datalen); + free(filepath); + return PM3_ESOFT; +} + +static bool emrtd_dump_file(uint8_t *ks_enc, uint8_t *ks_mac, uint8_t *ssc, const char *file, const char *name, bool use_secure, bool use_14b, const char *path) { + uint8_t response[EMRTD_MAX_FILE_SIZE]; + int resplen = 0; + + if (emrtd_select_and_read(response, &resplen, file, ks_enc, ks_mac, ssc, use_secure, use_14b) == false) { + return false; + } + + char *filepath = calloc(strlen(path) + 100, sizeof(char)); + if (filepath == NULL) + return false; + + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, name); + + PrintAndLogEx(INFO, "Read %s, len: %i.", name, resplen); + PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); + saveFile(filepath, ".BIN", response, resplen); + emrtd_dg_t *dg = emrtd_fileid_to_dg(file); + if ((dg != NULL) && (dg->dumper != NULL)) { + dg->dumper(response, resplen, path); + } + + free(filepath); + return true; +} + +static void rng(int length, uint8_t *dataout) { + // Do very very secure prng operations + //for (int i = 0; i < (length / 4); i++) { + // num_to_bytes(prng_successor(msclock() + i, 32), 4, &dataout[i * 4]); + //} + memset(dataout, 0x00, length); +} + +static bool emrtd_do_bac(char *documentnumber, char *dob, char *expiry, uint8_t *ssc, uint8_t *ks_enc, uint8_t *ks_mac, bool use_14b) { + uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + int resplen = 0; + + uint8_t rnd_ic[8] = { 0x00 }; + uint8_t kenc[50] = { 0x00 }; + uint8_t kmac[50] = { 0x00 }; + uint8_t k_icc[16] = { 0x00 }; + uint8_t S[32] = { 0x00 }; + + uint8_t rnd_ifd[8], k_ifd[16]; + rng(8, rnd_ifd); + rng(16, k_ifd); + + PrintAndLogEx(DEBUG, "doc............... " _GREEN_("%s"), documentnumber); + PrintAndLogEx(DEBUG, "dob............... " _GREEN_("%s"), dob); + PrintAndLogEx(DEBUG, "exp............... " _GREEN_("%s"), expiry); + + char documentnumbercd = emrtd_calculate_check_digit(documentnumber); + char dobcd = emrtd_calculate_check_digit(dob); + char expirycd = emrtd_calculate_check_digit(expiry); + + char kmrz[25]; + sprintf(kmrz, "%s%i%s%i%s%i", documentnumber, documentnumbercd, dob, dobcd, expiry, expirycd); + PrintAndLogEx(DEBUG, "kmrz.............. " _GREEN_("%s"), kmrz); + + uint8_t kseed[20] = { 0x00 }; + sha1hash((unsigned char *)kmrz, strlen(kmrz), kseed); + PrintAndLogEx(DEBUG, "kseed (sha1)...... %s ", sprint_hex_inrow(kseed, 16)); + + emrtd_deskey(kseed, KENC_type, 16, kenc); + emrtd_deskey(kseed, KMAC_type, 16, kmac); + PrintAndLogEx(DEBUG, "kenc.............. %s", sprint_hex_inrow(kenc, 16)); + PrintAndLogEx(DEBUG, "kmac.............. %s", sprint_hex_inrow(kmac, 16)); + + // Get Challenge + if (emrtd_get_challenge(8, rnd_ic, &resplen, use_14b) == false) { + PrintAndLogEx(ERR, "Couldn't get challenge."); + return false; + } + PrintAndLogEx(DEBUG, "rnd_ic............ %s", sprint_hex_inrow(rnd_ic, 8)); + + memcpy(S, rnd_ifd, 8); + memcpy(S + 8, rnd_ic, 8); + memcpy(S + 16, k_ifd, 16); + + PrintAndLogEx(DEBUG, "S................. %s", sprint_hex_inrow(S, 32)); + + uint8_t iv[8] = { 0x00 }; + uint8_t e_ifd[32] = { 0x00 }; + + des3_encrypt_cbc(iv, kenc, S, sizeof(S), e_ifd); + PrintAndLogEx(DEBUG, "e_ifd............. %s", sprint_hex_inrow(e_ifd, 32)); + + uint8_t m_ifd[8] = { 0x00 }; + + retail_mac(kmac, e_ifd, 32, m_ifd); + PrintAndLogEx(DEBUG, "m_ifd............. %s", sprint_hex_inrow(m_ifd, 8)); + + uint8_t cmd_data[40]; + memcpy(cmd_data, e_ifd, 32); + memcpy(cmd_data + 32, m_ifd, 8); + + // Do external authentication + if (emrtd_external_authenticate(cmd_data, sizeof(cmd_data), response, &resplen, use_14b) == false) { + PrintAndLogEx(ERR, "Couldn't do external authentication. Did you supply the correct MRZ info?"); + return false; + } + PrintAndLogEx(INFO, "External authentication with BAC successful."); + + uint8_t dec_output[32] = { 0x00 }; + des3_decrypt_cbc(iv, kenc, response, 32, dec_output); + PrintAndLogEx(DEBUG, "dec_output........ %s", sprint_hex_inrow(dec_output, 32)); + + if (memcmp(rnd_ifd, dec_output + 8, 8) != 0) { + PrintAndLogEx(ERR, "Challenge failed, rnd_ifd does not match."); + return false; + } + + memcpy(k_icc, dec_output + 16, 16); + + // Calculate session keys + for (int x = 0; x < 16; x++) { + kseed[x] = k_ifd[x] ^ k_icc[x]; + } + + PrintAndLogEx(DEBUG, "kseed............ %s", sprint_hex_inrow(kseed, 16)); + + emrtd_deskey(kseed, KENC_type, 16, ks_enc); + emrtd_deskey(kseed, KMAC_type, 16, ks_mac); + + PrintAndLogEx(DEBUG, "ks_enc........ %s", sprint_hex_inrow(ks_enc, 16)); + PrintAndLogEx(DEBUG, "ks_mac........ %s", sprint_hex_inrow(ks_mac, 16)); + + memcpy(ssc, rnd_ic + 4, 4); + memcpy(ssc + 4, rnd_ifd + 4, 4); + + PrintAndLogEx(DEBUG, "ssc........... %s", sprint_hex_inrow(ssc, 8)); + + return true; +} + +static bool emrtd_connect(bool *use_14b) { + // Try to 14a + SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT | ISO14A_NO_DISCONNECT, 0, 0, NULL, 0); + PacketResponseNG resp; + bool failed_14a = false; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { + DropField(); + failed_14a = true; + } + + if (failed_14a || resp.oldarg[0] == 0) { + PrintAndLogEx(INFO, "No eMRTD spotted with 14a, trying 14b."); + // If not 14a, try to 14b + SendCommandMIX(CMD_HF_ISO14443B_COMMAND, ISO14B_CONNECT | ISO14B_SELECT_STD, 0, 0, NULL, 0); + if (!WaitForResponseTimeout(CMD_HF_ISO14443B_COMMAND, &resp, 2500)) { + PrintAndLogEx(INFO, "No eMRTD spotted with 14b, exiting."); + return false; + } + + if (resp.oldarg[0] != 0) { + PrintAndLogEx(INFO, "No eMRTD spotted with 14b, exiting."); + return false; + } + *use_14b = true; + } + return true; +} + +static bool emrtd_do_auth(char *documentnumber, char *dob, char *expiry, bool BAC_available, bool *BAC, uint8_t *ssc, uint8_t *ks_enc, uint8_t *ks_mac, bool *use_14b) { + uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + int resplen = 0; + + // Select MRTD applet + if (emrtd_select_file(EMRTD_P1_SELECT_BY_NAME, EMRTD_AID_MRTD, *use_14b) == false) { + PrintAndLogEx(ERR, "Couldn't select the MRTD application."); + return false; + } + + // Select EF_COM + if (emrtd_select_file(EMRTD_P1_SELECT_BY_EF, dg_table[EF_COM].fileid, *use_14b) == false) { + *BAC = true; + PrintAndLogEx(INFO, "Authentication is enforced. Will attempt external authentication."); + } else { + *BAC = false; + // Select EF_DG1 + emrtd_select_file(EMRTD_P1_SELECT_BY_EF, dg_table[EF_DG1].fileid, *use_14b); + + if (emrtd_read_file(response, &resplen, NULL, NULL, NULL, false, *use_14b) == false) { + *BAC = true; + PrintAndLogEx(INFO, "Authentication is enforced. Will attempt external authentication."); + } else { + *BAC = false; + } + } + + // Do Basic Access Aontrol + if (*BAC) { + // If BAC isn't available, exit out and warn user. + if (!BAC_available) { + PrintAndLogEx(ERR, "This eMRTD enforces authentication, but you didn't supply MRZ data. Cannot proceed."); + PrintAndLogEx(HINT, "Check out hf emrtd info/dump --help, supply data with -n -d and -e."); + return false; + } + + if (emrtd_do_bac(documentnumber, dob, expiry, ssc, ks_enc, ks_mac, *use_14b) == false) { + return false; + } + } + + return true; +} + +int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available, const char *path) { + uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + int resplen = 0; + uint8_t ssc[8] = { 0x00 }; + uint8_t ks_enc[16] = { 0x00 }; + uint8_t ks_mac[16] = { 0x00 }; + bool BAC = false; + bool use_14b = false; + + // Select the eMRTD + if (!emrtd_connect(&use_14b)) { + DropField(); + return PM3_ESOFT; + } + + // Dump EF_CardAccess (if available) + if (!emrtd_dump_file(ks_enc, ks_mac, ssc, dg_table[EF_CardAccess].fileid, dg_table[EF_CardAccess].filename, BAC, use_14b, path)) { + PrintAndLogEx(INFO, "Couldn't dump EF_CardAccess, card does not support PACE."); + PrintAndLogEx(HINT, "This is expected behavior for cards without PACE, and isn't something to be worried about."); + } + + // Authenticate with the eMRTD + if (!emrtd_do_auth(documentnumber, dob, expiry, BAC_available, &BAC, ssc, ks_enc, ks_mac, &use_14b)) { + DropField(); + return PM3_ESOFT; + } + + // Select EF_COM + if (!emrtd_select_and_read(response, &resplen, dg_table[EF_COM].fileid, ks_enc, ks_mac, ssc, BAC, use_14b)) { + PrintAndLogEx(ERR, "Failed to read EF_COM."); + DropField(); + return PM3_ESOFT; + } + + + char *filepath = calloc(strlen(path) + 100, sizeof(char)); + if (filepath == NULL) + return PM3_EMALLOC; + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg_table[EF_COM].filename); + + PrintAndLogEx(INFO, "Read EF_COM, len: %i.", resplen); + PrintAndLogEx(DEBUG, "Contents (may be incomplete over 2k chars): %s", sprint_hex_inrow(response, resplen)); + saveFile(filepath, ".BIN", response, resplen); + + free(filepath); + + uint8_t filelist[50]; + size_t filelistlen = 0; + + if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0)) { + PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); + DropField(); + return PM3_ESOFT; + } + + PrintAndLogEx(DEBUG, "File List: %s", sprint_hex_inrow(filelist, filelistlen)); + // Add EF_SOD to the list + filelist[filelistlen++] = 0x77; + // Dump all files in the file list + for (int i = 0; i < filelistlen; i++) { + emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]); + if (dg == NULL) { + PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); + continue; + } + PrintAndLogEx(DEBUG, "Current file: %s", dg->filename); + if (!dg->pace && !dg->eac) { + emrtd_dump_file(ks_enc, ks_mac, ssc, dg->fileid, dg->filename, BAC, use_14b, path); + } + } + DropField(); + return PM3_SUCCESS; +} + +static bool emrtd_compare_check_digit(char *datain, int datalen, char expected_check_digit) { + char tempdata[90] = { 0x00 }; + memcpy(tempdata, datain, datalen); + + uint8_t check_digit = emrtd_calculate_check_digit(tempdata) + 0x30; + bool res = check_digit == expected_check_digit; + PrintAndLogEx(DEBUG, "emrtd_compare_check_digit, expected %c == %c calculated ( %s )" + , expected_check_digit + , check_digit + , (res) ? _GREEN_("ok") : _RED_("fail")); + return res; +} + +static bool emrtd_mrz_verify_check_digit(char *mrz, int offset, int datalen) { + char tempdata[90] = { 0x00 }; + memcpy(tempdata, mrz + offset, datalen); + return emrtd_compare_check_digit(tempdata, datalen, mrz[offset + datalen]); +} + +static void emrtd_print_legal_sex(char *legal_sex) { + char sex[12] = { 0x00 }; + switch (*legal_sex) { + case 'M': + strncpy(sex, "Male", 5); + break; + case 'F': + strncpy(sex, "Female", 7); + break; + case '<': + strncpy(sex, "Unspecified", 12); + break; + } + PrintAndLogEx(SUCCESS, "Legal Sex Marker......: " _YELLOW_("%s"), sex); +} + +static int emrtd_mrz_determine_length(char *mrz, int offset, int max_length) { + int i; + for (i = max_length; i >= 0; i--) { + if (mrz[offset + i - 1] != '<') { + break; + } + } + return i; +} + +static int emrtd_mrz_determine_separator(char *mrz, int offset, int max_length) { + // Note: this function does not account for len=0 + int i; + for (i = max_length - 1; i > 0; i--) { + if (mrz[offset + i] == '<' && mrz[offset + i + 1] == '<') { + break; + } + } + return i; +} + +static void emrtd_mrz_replace_pad(char *data, int datalen, char newchar) { + for (int i = 0; i < datalen; i++) { + if (data[i] == '<') { + data[i] = newchar; + } + } +} + +static void emrtd_print_optional_elements(char *mrz, int offset, int length, bool verify_check_digit) { + int i = emrtd_mrz_determine_length(mrz, offset, length); + + // Only print optional elements if they're available + if (i != 0) { + PrintAndLogEx(SUCCESS, "Optional elements.....: " _YELLOW_("%.*s"), i, mrz + offset); + } + + if (verify_check_digit && !emrtd_mrz_verify_check_digit(mrz, offset, length)) { + PrintAndLogEx(SUCCESS, _RED_("Optional element check digit is invalid.")); + } +} + +static void emrtd_print_document_number(char *mrz, int offset) { + int i = emrtd_mrz_determine_length(mrz, offset, 9); + + PrintAndLogEx(SUCCESS, "Document Number.......: " _YELLOW_("%.*s"), i, mrz + offset); + + if (!emrtd_mrz_verify_check_digit(mrz, offset, 9)) { + PrintAndLogEx(SUCCESS, _RED_("Document number check digit is invalid.")); + } +} + +static void emrtd_print_name(char *mrz, int offset, int max_length, bool localized) { + char final_name[100] = { 0x00 }; + int namelen = emrtd_mrz_determine_length(mrz, offset, max_length); + int sep = emrtd_mrz_determine_separator(mrz, offset, namelen); + + // Account for mononyms + if (sep != 0) { + int firstnamelen = (namelen - (sep + 2)); + + memcpy(final_name, mrz + offset + sep + 2, firstnamelen); + final_name[firstnamelen] = ' '; + memcpy(final_name + firstnamelen + 1, mrz + offset, sep); + } else { + memcpy(final_name, mrz + offset, namelen); + } + + // Replace < characters with spaces + emrtd_mrz_replace_pad(final_name, namelen, ' '); + + if (localized) { + PrintAndLogEx(SUCCESS, "Legal Name (Localized): " _YELLOW_("%s"), final_name); + } else { + PrintAndLogEx(SUCCESS, "Legal Name............: " _YELLOW_("%s"), final_name); + } +} + +static void emrtd_mrz_convert_date(char *mrz, int offset, char *final_date, bool is_expiry, bool is_full, bool is_ascii) { + char work_date[9] = { 0x00 }; + int len = is_full ? 8 : 6; + + // Copy the data to a working array in the right format + if (!is_ascii) { + memcpy(work_date, sprint_hex_inrow((uint8_t *)mrz + offset, len / 2), len); + } else { + memcpy(work_date, mrz + offset, len); + } + + // Set offset to 0 as we've now copied data. + offset = 0; + + if (is_full) { + // If we get the full date, use the first two characters from that for year + memcpy(final_date, work_date, 2); + // and do + 2 on offset so that rest of code uses the right data + offset += 2; + } else { + char temp_year[3] = { 0x00 }; + memcpy(temp_year, work_date, 2); + // If it's > 20, assume 19xx. + if (strtol(temp_year, NULL, 10) < 20 || is_expiry) { + final_date[0] = '2'; + final_date[1] = '0'; + } else { + final_date[0] = '1'; + final_date[1] = '9'; + } + } + + memcpy(final_date + 2, work_date + offset, 2); + final_date[4] = '-'; + memcpy(final_date + 5, work_date + offset + 2, 2); + final_date[7] = '-'; + memcpy(final_date + 8, work_date + offset + 4, 2); +} + +static void emrtd_print_dob(char *mrz, int offset, bool full, bool ascii) { + char final_date[12] = { 0x00 }; + emrtd_mrz_convert_date(mrz, offset, final_date, false, full, ascii); + + PrintAndLogEx(SUCCESS, "Date of birth.........: " _YELLOW_("%s"), final_date); + + if (!full && !emrtd_mrz_verify_check_digit(mrz, offset, 6)) { + PrintAndLogEx(SUCCESS, _RED_("Date of Birth check digit is invalid.")); + } +} + +static void emrtd_print_expiry(char *mrz, int offset) { + char final_date[12] = { 0x00 }; + emrtd_mrz_convert_date(mrz, offset, final_date, true, false, true); + + PrintAndLogEx(SUCCESS, "Date of expiry........: " _YELLOW_("%s"), final_date); + + if (!emrtd_mrz_verify_check_digit(mrz, offset, 6)) { + PrintAndLogEx(SUCCESS, _RED_("Date of expiry check digit is invalid.")); + } +} + +static void emrtd_print_issuance(char *data, bool ascii) { + char final_date[12] = { 0x00 }; + emrtd_mrz_convert_date(data, 0, final_date, true, true, ascii); + + PrintAndLogEx(SUCCESS, "Date of issue.........: " _YELLOW_("%s"), final_date); +} + +static void emrtd_print_personalization_timestamp(uint8_t *data) { + char str_date[0x0F] = { 0x00 }; + strcpy(str_date, sprint_hex_inrow(data, 0x0E)); + char final_date[20] = { 0x00 }; + sprintf(final_date, "%.4s-%.2s-%.2s %.2s:%.2s:%.2s", str_date, str_date + 4, str_date + 6, str_date + 8, str_date + 10, str_date + 12); + + PrintAndLogEx(SUCCESS, "Personalization at....: " _YELLOW_("%s"), final_date); +} + +static void emrtd_print_unknown_timestamp_5f85(uint8_t *data) { + char final_date[20] = { 0x00 }; + sprintf(final_date, "%.4s-%.2s-%.2s %.2s:%.2s:%.2s", data, data + 4, data + 6, data + 8, data + 10, data + 12); + + PrintAndLogEx(SUCCESS, "Unknown timestamp 5F85: " _YELLOW_("%s"), final_date); + PrintAndLogEx(HINT, "This is very likely the personalization timestamp, but it is using an undocumented tag."); +} + +static int emrtd_print_ef_com_info(uint8_t *data, size_t datalen) { + uint8_t filelist[50]; + size_t filelistlen = 0; + int res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0); + if (!res) { + PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); + return PM3_ESOFT; + } + + // List files in the file list + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_COM") " --------------------"); + for (int i = 0; i < filelistlen; i++) { + emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]); + if (dg == NULL) { + PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); + continue; + } + PrintAndLogEx(SUCCESS, "%-7s...............: " _YELLOW_("%s"), dg->filename, dg->desc); + } + return PM3_SUCCESS; +} + +static int emrtd_print_ef_dg1_info(uint8_t *data, size_t datalen) { + int td_variant = 0; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG1") " --------------------"); + + // MRZ on TD1 is 90 characters, 30 on each row. + // MRZ on TD3 is 88 characters, 44 on each row. + char mrz[90] = { 0x00 }; + size_t mrzlen = 0; + + if (!emrtd_lds_get_data_by_tag(data, datalen, (uint8_t *) mrz, &mrzlen, 0x5f, 0x1f, true, true, 0)) { + PrintAndLogEx(ERR, "Failed to read MRZ from EF_DG1."); + return PM3_ESOFT; + } + + // Determine and print the document type + if (mrz[0] == 'I' && mrz[1] == 'P') { + PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Passport Card")); + } else if (mrz[0] == 'I') { + PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("ID Card")); + } else if (mrz[0] == 'P') { + PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Passport")); + } else if (mrz[0] == 'A') { + PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("German Residency Permit")); + } else { + PrintAndLogEx(SUCCESS, "Document Type.........: " _YELLOW_("Unknown")); + } + + if (mrzlen == 90) { + td_variant = 1; + } else if (mrzlen == 88) { + td_variant = 3; + } else { + PrintAndLogEx(ERR, "MRZ length (%zu) is wrong.", mrzlen); + return PM3_ESOFT; + } + + PrintAndLogEx(SUCCESS, "Document Form Factor..: " _YELLOW_("TD%i"), td_variant); + + // Print the MRZ + if (td_variant == 1) { + PrintAndLogEx(DEBUG, "MRZ Row 1: " _YELLOW_("%.30s"), mrz); + PrintAndLogEx(DEBUG, "MRZ Row 2: " _YELLOW_("%.30s"), mrz + 30); + PrintAndLogEx(DEBUG, "MRZ Row 3: " _YELLOW_("%.30s"), mrz + 60); + } else if (td_variant == 3) { + PrintAndLogEx(DEBUG, "MRZ Row 1: " _YELLOW_("%.44s"), mrz); + PrintAndLogEx(DEBUG, "MRZ Row 2: " _YELLOW_("%.44s"), mrz + 44); + } + + PrintAndLogEx(SUCCESS, "Issuing state.........: " _YELLOW_("%.3s"), mrz + 2); + + if (td_variant == 3) { + // Passport form factor + PrintAndLogEx(SUCCESS, "Nationality...........: " _YELLOW_("%.3s"), mrz + 44 + 10); + emrtd_print_name(mrz, 5, 38, false); + emrtd_print_document_number(mrz, 44); + emrtd_print_dob(mrz, 44 + 13, false, true); + emrtd_print_legal_sex(&mrz[44 + 20]); + emrtd_print_expiry(mrz, 44 + 21); + emrtd_print_optional_elements(mrz, 44 + 28, 14, true); + + // Calculate and verify composite check digit + char composite_check_data[50] = { 0x00 }; + memcpy(composite_check_data, mrz + 44, 10); + memcpy(composite_check_data + 10, mrz + 44 + 13, 7); + memcpy(composite_check_data + 17, mrz + 44 + 21, 23); + + if (!emrtd_compare_check_digit(composite_check_data, 39, mrz[87])) { + PrintAndLogEx(SUCCESS, _RED_("Composite check digit is invalid.")); + } + } else if (td_variant == 1) { + // ID form factor + PrintAndLogEx(SUCCESS, "Nationality...........: " _YELLOW_("%.3s"), mrz + 30 + 15); + emrtd_print_name(mrz, 60, 30, false); + emrtd_print_document_number(mrz, 5); + emrtd_print_dob(mrz, 30, false, true); + emrtd_print_legal_sex(&mrz[30 + 7]); + emrtd_print_expiry(mrz, 30 + 8); + emrtd_print_optional_elements(mrz, 15, 15, false); + emrtd_print_optional_elements(mrz, 30 + 18, 11, false); + + // Calculate and verify composite check digit + if (!emrtd_compare_check_digit(mrz, 59, mrz[59])) { + PrintAndLogEx(SUCCESS, _RED_("Composite check digit is invalid.")); + } + } + + return PM3_SUCCESS; +} + +static int emrtd_print_ef_dg11_info(uint8_t *data, size_t datalen) { + uint8_t taglist[100] = { 0x00 }; + size_t taglistlen = 0; + uint8_t tagdata[1000] = { 0x00 }; + size_t tagdatalen = 0; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG11") " -------------------"); + + if (!emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true, 0)) { + PrintAndLogEx(ERR, "Failed to read file list from EF_DG11."); + return PM3_ESOFT; + } + + for (int i = 0; i < taglistlen; i++) { + emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f, true, 0); + // Don't bother with empty tags + if (tagdatalen == 0) { + continue; + } + // Special behavior for two char tags + if (taglist[i] == 0x5f) { + switch (taglist[i + 1]) { + case 0x0e: + emrtd_print_name((char *) tagdata, 0, tagdatalen, true); + break; + case 0x0f: + emrtd_print_name((char *) tagdata, 0, tagdatalen, false); + break; + case 0x10: + PrintAndLogEx(SUCCESS, "Personal Number.......: " _YELLOW_("%.*s"), tagdatalen, tagdata); + break; + case 0x11: + // TODO: acc for < separation + PrintAndLogEx(SUCCESS, "Place of Birth........: " _YELLOW_("%.*s"), tagdatalen, tagdata); + break; + case 0x42: + // TODO: acc for < separation + PrintAndLogEx(SUCCESS, "Permanent Address.....: " _YELLOW_("%.*s"), tagdatalen, tagdata); + break; + case 0x12: + PrintAndLogEx(SUCCESS, "Telephone.............: " _YELLOW_("%.*s"), tagdatalen, tagdata); + break; + case 0x13: + PrintAndLogEx(SUCCESS, "Profession............: " _YELLOW_("%.*s"), tagdatalen, tagdata); + break; + case 0x14: + PrintAndLogEx(SUCCESS, "Title.................: " _YELLOW_("%.*s"), tagdatalen, tagdata); + break; + case 0x15: + PrintAndLogEx(SUCCESS, "Personal Summary......: " _YELLOW_("%.*s"), tagdatalen, tagdata); + break; + case 0x16: + saveFile("ProofOfCitizenship", tagdata[0] == 0xFF ? ".jpg" : ".jp2", tagdata, tagdatalen); + break; + case 0x17: + // TODO: acc for < separation + PrintAndLogEx(SUCCESS, "Other valid TDs nums..: " _YELLOW_("%.*s"), tagdatalen, tagdata); + break; + case 0x18: + PrintAndLogEx(SUCCESS, "Custody Information...: " _YELLOW_("%.*s"), tagdatalen, tagdata); + break; + case 0x2b: + emrtd_print_dob((char *) tagdata, 0, true, tagdatalen != 4); + break; + default: + PrintAndLogEx(SUCCESS, "Unknown Field %02X%02X....: %s", taglist[i], taglist[i + 1], sprint_hex_inrow(tagdata, tagdatalen)); + break; + } + + i += 1; + } else { + // TODO: Account for A0 + PrintAndLogEx(SUCCESS, "Unknown Field %02X......: %s", taglist[i], sprint_hex_inrow(tagdata, tagdatalen)); + } + } + return PM3_SUCCESS; +} + +static int emrtd_print_ef_dg12_info(uint8_t *data, size_t datalen) { + uint8_t taglist[100] = { 0x00 }; + size_t taglistlen = 0; + uint8_t tagdata[1000] = { 0x00 }; + size_t tagdatalen = 0; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_DG12") " -------------------"); + + if (!emrtd_lds_get_data_by_tag(data, datalen, taglist, &taglistlen, 0x5c, 0x00, false, true, 0)) { + PrintAndLogEx(ERR, "Failed to read file list from EF_DG12."); + return PM3_ESOFT; + } + + for (int i = 0; i < taglistlen; i++) { + emrtd_lds_get_data_by_tag(data, datalen, tagdata, &tagdatalen, taglist[i], taglist[i + 1], taglist[i] == 0x5f, true, 0); + // Don't bother with empty tags + if (tagdatalen == 0) { + continue; + } + // Special behavior for two char tags + if (taglist[i] == 0x5f) { + // Several things here are longer than the rest but I can't think of a way to shorten them + // ...and I doubt many states are using them. + switch (taglist[i + 1]) { + case 0x19: + PrintAndLogEx(SUCCESS, "Issuing Authority.....: " _YELLOW_("%.*s"), tagdatalen, tagdata); + break; + case 0x26: + emrtd_print_issuance((char *) tagdata, tagdatalen != 4); + break; + case 0x1b: + PrintAndLogEx(SUCCESS, "Endorsements & Observations: " _YELLOW_("%.*s"), tagdatalen, tagdata); + break; + case 0x1c: + PrintAndLogEx(SUCCESS, "Tax/Exit Requirements.: " _YELLOW_("%.*s"), tagdatalen, tagdata); + break; + case 0x1d: + saveFile("FrontOfDocument", tagdata[0] == 0xFF ? ".jpg" : ".jp2", tagdata, tagdatalen); + break; + case 0x1e: + saveFile("BackOfDocument", tagdata[0] == 0xFF ? ".jpg" : ".jp2", tagdata, tagdatalen); + break; + case 0x55: + emrtd_print_personalization_timestamp(tagdata); + break; + case 0x56: + PrintAndLogEx(SUCCESS, "Serial of Personalization System: " _YELLOW_("%.*s"), tagdatalen, tagdata); + break; + case 0x85: + emrtd_print_unknown_timestamp_5f85(tagdata); + break; + default: + PrintAndLogEx(SUCCESS, "Unknown Field %02X%02X....: %s", taglist[i], taglist[i + 1], sprint_hex_inrow(tagdata, tagdatalen)); + break; + } + + i += 1; + } else { + // TODO: Account for A0 + PrintAndLogEx(SUCCESS, "Unknown Field %02X......: %s", taglist[i], sprint_hex_inrow(tagdata, tagdatalen)); + } + } + return PM3_SUCCESS; +} + +static int emrtd_ef_sod_extract_signatures(uint8_t *data, size_t datalen, uint8_t *dataout, size_t *dataoutlen) { + uint8_t top[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t signeddata[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t emrtdsigcontainer[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t emrtdsigtext[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + size_t toplen, signeddatalen, emrtdsigcontainerlen, emrtdsiglen, emrtdsigtextlen = 0; + + if (!emrtd_lds_get_data_by_tag(data, datalen, top, &toplen, 0x30, 0x00, false, true, 0)) { + PrintAndLogEx(ERR, "Failed to read top from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "top: %s.", sprint_hex_inrow(top, toplen)); + + if (!emrtd_lds_get_data_by_tag(top, toplen, signeddata, &signeddatalen, 0xA0, 0x00, false, false, 0)) { + PrintAndLogEx(ERR, "Failed to read signedData from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "signeddata: %s.", sprint_hex_inrow(signeddata, signeddatalen)); + + // Do true on reading into the tag as it's a "sequence" + if (!emrtd_lds_get_data_by_tag(signeddata, signeddatalen, emrtdsigcontainer, &emrtdsigcontainerlen, 0x30, 0x00, false, true, 0)) { + PrintAndLogEx(ERR, "Failed to read eMRTDSignature container from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "emrtdsigcontainer: %s.", sprint_hex_inrow(emrtdsigcontainer, emrtdsigcontainerlen)); + + if (!emrtd_lds_get_data_by_tag(emrtdsigcontainer, emrtdsigcontainerlen, emrtdsig, &emrtdsiglen, 0xA0, 0x00, false, false, 0)) { + PrintAndLogEx(ERR, "Failed to read eMRTDSignature from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "emrtdsig: %s.", sprint_hex_inrow(emrtdsig, emrtdsiglen)); + + // TODO: Not doing memcpy here, it didn't work, fix it somehow + if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, emrtdsigtext, &emrtdsigtextlen, 0x04, 0x00, false, false, 0)) { + PrintAndLogEx(ERR, "Failed to read eMRTDSignature (text) from EF_SOD."); + return false; + } + memcpy(dataout, emrtdsigtext, emrtdsigtextlen); + *dataoutlen = emrtdsigtextlen; + return PM3_SUCCESS; +} + +static int emrtd_parse_ef_sod_hash_algo(uint8_t *data, size_t datalen, int *hashalgo) { + uint8_t hashalgoset[64] = { 0x00 }; + size_t hashalgosetlen = 0; + + // We'll return hash algo -1 if we can't find anything + *hashalgo = -1; + + if (!emrtd_lds_get_data_by_tag(data, datalen, hashalgoset, &hashalgosetlen, 0x30, 0x00, false, true, 0)) { + PrintAndLogEx(ERR, "Failed to read hash algo set from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "hash algo set: %s", sprint_hex_inrow(hashalgoset, hashalgosetlen)); + + // If last two bytes are 05 00, ignore them. + // https://wf.lavatech.top/ave-but-random/emrtd-data-quirks#EF_SOD + if (hashalgoset[hashalgosetlen - 2] == 0x05 && hashalgoset[hashalgosetlen - 1] == 0x00) { + hashalgosetlen -= 2; + } + + for (int hashi = 0; hashalg_table[hashi].name != NULL; hashi++) { + PrintAndLogEx(DEBUG, "trying: %s", hashalg_table[hashi].name); + // We're only interested in checking if the length matches to avoid memory shenanigans + if (hashalg_table[hashi].descriptorlen != hashalgosetlen) { + PrintAndLogEx(DEBUG, "len mismatch: %i", hashalgosetlen); + continue; + } + + if (memcmp(hashalg_table[hashi].descriptor, hashalgoset, hashalgosetlen) == 0) { + *hashalgo = hashi; + return PM3_SUCCESS; + } + } + + PrintAndLogEx(ERR, "Failed to parse hash list (Unknown algo: %s). Hash verification won't be available.", sprint_hex_inrow(hashalgoset, hashalgosetlen)); + return PM3_ESOFT; +} + +static int emrtd_parse_ef_sod_hashes(uint8_t *data, size_t datalen, uint8_t *hashes, int *hashalgo) { + uint8_t emrtdsig[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t hashlist[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + uint8_t hash[64] = { 0x00 }; + size_t hashlen = 0; + + uint8_t hashidstr[4] = { 0x00 }; + size_t hashidstrlen = 0; + + size_t emrtdsiglen = 0; + size_t hashlistlen = 0; + size_t offset = 0; + + if (emrtd_ef_sod_extract_signatures(data, datalen, emrtdsig, &emrtdsiglen) != PM3_SUCCESS) { + return false; + } + + PrintAndLogEx(DEBUG, "hash data: %s", sprint_hex_inrow(emrtdsig, emrtdsiglen)); + + emrtd_parse_ef_sod_hash_algo(emrtdsig, emrtdsiglen, hashalgo); + + if (!emrtd_lds_get_data_by_tag(emrtdsig, emrtdsiglen, hashlist, &hashlistlen, 0x30, 0x00, false, true, 1)) { + PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD."); + return false; + } + + PrintAndLogEx(DEBUG, "hash list: %s", sprint_hex_inrow(hashlist, hashlistlen)); + + while (offset < hashlistlen) { + // Get the length of the element + int e_datalen = emrtd_get_asn1_data_length(hashlist + offset, hashlistlen - offset, 1); + + // Get the length of the element's length + int e_fieldlen = emrtd_get_asn1_field_length(hashlist + offset, hashlistlen - offset, 1); + + switch (hashlist[offset]) { + case 0x30: + emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hashidstr, &hashidstrlen, 0x02, 0x00, false, false, 0); + emrtd_lds_get_data_by_tag(hashlist + offset + e_fieldlen + 1, e_datalen, hash, &hashlen, 0x04, 0x00, false, false, 0); + if (hashlen <= 64) { + memcpy(hashes + (hashidstr[0] * 64), hash, hashlen); + } else { + PrintAndLogEx(ERR, "error (emrtd_parse_ef_sod_hashes) hashlen out-of-bounds"); + } + break; + } + // + 1 for length of ID + offset += 1 + e_datalen + e_fieldlen; + } + + return PM3_SUCCESS; +} + +static int emrtd_print_ef_sod_info(uint8_t *dg_hashes_calc, uint8_t *dg_hashes_sod, int hash_algo) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "-------------------- " _CYAN_("EF_SOD") " --------------------"); + + if (hash_algo == -1) { + PrintAndLogEx(SUCCESS, "Hash algorithm: " _YELLOW_("Unknown")); + } else { + PrintAndLogEx(SUCCESS, "Hash algorithm: " _YELLOW_("%s"), hashalg_table[hash_algo].name); + + uint8_t all_zeroes[64] = { 0x00 }; + for (int i = 1; i <= 16; i++) { + bool calc_all_zero = (memcmp(dg_hashes_calc + (i * 64), all_zeroes, hashalg_table[hash_algo].hashlen) == 0); + bool sod_all_zero = (memcmp(dg_hashes_sod + (i * 64), all_zeroes, hashalg_table[hash_algo].hashlen) == 0); + bool hash_matches = (memcmp(dg_hashes_sod + (i * 64), dg_hashes_calc + (i * 64), hashalg_table[hash_algo].hashlen) == 0); + // Ignore files we don't haven't read and lack hashes to + if (calc_all_zero == true && sod_all_zero == true) { + continue; + } else if (calc_all_zero == true) { + PrintAndLogEx(SUCCESS, "EF_DG%i: " _YELLOW_("File couldn't be read, but is in EF_SOD."), i); + } else if (sod_all_zero == true) { + PrintAndLogEx(SUCCESS, "EF_DG%i: " _YELLOW_("File is not in EF_SOD."), i); + } else if (hash_matches == false) { + PrintAndLogEx(SUCCESS, "EF_DG%i: " _RED_("Invalid"), i); + } else { + PrintAndLogEx(SUCCESS, "EF_DG%i: " _GREEN_("Valid"), i); + } + } + } + + return PM3_SUCCESS; +} + +static int emrtd_print_ef_cardaccess_info(uint8_t *data, size_t datalen) { + uint8_t dataset[100] = { 0x00 }; + size_t datasetlen = 0; + uint8_t datafromtag[100] = { 0x00 }; + size_t datafromtaglen = 0; + uint8_t parsednum = 0; + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "----------------- " _CYAN_("EF_CardAccess") " ----------------"); + + if (!emrtd_lds_get_data_by_tag(data, datalen, dataset, &datasetlen, 0x30, 0x00, false, true, 0)) { + PrintAndLogEx(ERR, "Failed to read set from EF_CardAccess."); + return PM3_ESOFT; + } + + // Get PACE version + if (!emrtd_lds_get_data_by_tag(dataset, datasetlen, datafromtag, &datafromtaglen, 0x02, 0x00, false, false, 0)) { + PrintAndLogEx(ERR, "Failed to read PACE version from EF_CardAccess."); + return PM3_ESOFT; + } + // TODO: hack!!! + memcpy(&parsednum, datafromtag, datafromtaglen); + PrintAndLogEx(SUCCESS, "PACE version..........: " _YELLOW_("%i"), parsednum); + + // Get PACE algorithm + if (!emrtd_lds_get_data_by_tag(dataset, datasetlen, datafromtag, &datafromtaglen, 0x06, 0x00, false, false, 0)) { + PrintAndLogEx(ERR, "Failed to read PACE algorithm from EF_CardAccess."); + return PM3_ESOFT; + } + + for (int pacei = 0; pacealg_table[pacei].name != NULL; pacei++) { + PrintAndLogEx(DEBUG, "Trying: %s", pacealg_table[pacei].name); + + if (memcmp(pacealg_table[pacei].descriptor, datafromtag, datafromtaglen) == 0) { + PrintAndLogEx(SUCCESS, "PACE algorithm........: " _YELLOW_("%s"), pacealg_table[pacei].name); + } + } + + // Get PACE parameter ID + if (!emrtd_lds_get_data_by_tag(dataset, datasetlen, datafromtag, &datafromtaglen, 0x02, 0x00, false, false, 1)) { + PrintAndLogEx(ERR, "Failed to read PACE parameter ID from EF_CardAccess."); + return PM3_ESOFT; + } + + // TODO: hack!!! + memcpy(&parsednum, datafromtag, datafromtaglen); + for (int pacepari = 0; pacesdp_table[pacepari].id != 32; pacepari++) { + PrintAndLogEx(DEBUG, "Trying: %s", pacesdp_table[pacepari].name); + + if (pacesdp_table[pacepari].id == parsednum) { + PrintAndLogEx(SUCCESS, "PACE parameter........: " _YELLOW_("%s"), pacesdp_table[pacepari].name); + } + // TODO: account for RFU + } + + return PM3_SUCCESS; +} + +int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available) { + uint8_t response[EMRTD_MAX_FILE_SIZE] = { 0x00 }; + int resplen = 0; + uint8_t ssc[8] = { 0x00 }; + uint8_t ks_enc[16] = { 0x00 }; + uint8_t ks_mac[16] = { 0x00 }; + bool BAC = false; + bool PACE_available = true; + bool use_14b = false; + + // Select the eMRTD + if (!emrtd_connect(&use_14b)) { + DropField(); + return PM3_ESOFT; + } + + // Read EF_CardAccess + if (!emrtd_select_and_read(response, &resplen, dg_table[EF_CardAccess].fileid, ks_enc, ks_mac, ssc, BAC, use_14b)) { + PACE_available = false; + PrintAndLogEx(HINT, "The error above this is normal. It just means that your eMRTD lacks PACE."); + } + + // Select and authenticate with the eMRTD + bool auth_result = emrtd_do_auth(documentnumber, dob, expiry, BAC_available, &BAC, ssc, ks_enc, ks_mac, &use_14b); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(INFO, "------------------ " _CYAN_("Basic Info") " ------------------"); + PrintAndLogEx(SUCCESS, "Communication standard: %s", use_14b ? _YELLOW_("ISO/IEC 14443(B)") : _YELLOW_("ISO/IEC 14443(A)")); + PrintAndLogEx(SUCCESS, "Authentication........: %s", BAC ? _GREEN_("Enforced") : _RED_("Not enforced")); + PrintAndLogEx(SUCCESS, "PACE..................: %s", PACE_available ? _GREEN_("Available") : _YELLOW_("Not available")); + PrintAndLogEx(SUCCESS, "Authentication result.: %s", auth_result ? _GREEN_("Successful") : _RED_("Failed")); + + if (PACE_available) { + emrtd_print_ef_cardaccess_info(response, resplen); + } + + if (!auth_result) { + DropField(); + return PM3_ESOFT; + } + + // Read EF_COM to get file list + if (!emrtd_select_and_read(response, &resplen, dg_table[EF_COM].fileid, ks_enc, ks_mac, ssc, BAC, use_14b)) { + PrintAndLogEx(ERR, "Failed to read EF_COM."); + DropField(); + return PM3_ESOFT; + } + + int res = emrtd_print_ef_com_info(response, resplen); + if (res != PM3_SUCCESS) { + DropField(); + return res; + } + + uint8_t filelist[50]; + size_t filelistlen = 0; + + if (!emrtd_lds_get_data_by_tag(response, resplen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0)) { + PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); + DropField(); + return PM3_ESOFT; + } + + // Grab the hash list from EF_SOD + uint8_t dg_hashes_sod[17][64] = { { 0 } }; + uint8_t dg_hashes_calc[17][64] = { { 0 } }; + int hash_algo = 0; + + if (!emrtd_select_and_read(response, &resplen, dg_table[EF_SOD].fileid, ks_enc, ks_mac, ssc, BAC, use_14b)) { + PrintAndLogEx(ERR, "Failed to read EF_SOD."); + DropField(); + return PM3_ESOFT; + } + + res = emrtd_parse_ef_sod_hashes(response, resplen, *dg_hashes_sod, &hash_algo); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail."); + } + + // Dump all files in the file list + for (int i = 0; i < filelistlen; i++) { + emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]); + if (dg == NULL) { + PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); + continue; + } + if (dg->fastdump && !dg->pace && !dg->eac) { + if (emrtd_select_and_read(response, &resplen, dg->fileid, ks_enc, ks_mac, ssc, BAC, use_14b)) { + if (dg->parser != NULL) + dg->parser(response, resplen); + + PrintAndLogEx(DEBUG, "EF_DG%i hash algo: %i", dg->dgnum, hash_algo); + // Check file hash + if (hash_algo != -1) { + PrintAndLogEx(DEBUG, "EF_DG%i hash on EF_SOD: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_sod[dg->dgnum], hashalg_table[hash_algo].hashlen)); + hashalg_table[hash_algo].hasher(response, resplen, dg_hashes_calc[dg->dgnum]); + PrintAndLogEx(DEBUG, "EF_DG%i hash calc: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_calc[dg->dgnum], hashalg_table[hash_algo].hashlen)); + } + } + } + } + DropField(); + + emrtd_print_ef_sod_info(*dg_hashes_calc, *dg_hashes_sod, hash_algo); + + return PM3_SUCCESS; +} + +int infoHF_EMRTD_offline(const char *path) { + uint8_t *data; + size_t datalen = 0; + char *filepath = calloc(strlen(path) + 100, sizeof(char)); + if (filepath == NULL) + return PM3_EMALLOC; + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg_table[EF_COM].filename); + + if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to read EF_COM."); + free(filepath); + return PM3_ESOFT; + } + + int res = emrtd_print_ef_com_info(data, datalen); + if (res != PM3_SUCCESS) { + free(data); + free(filepath); + return res; + } + + uint8_t filelist[50]; + size_t filelistlen = 0; + res = emrtd_lds_get_data_by_tag(data, datalen, filelist, &filelistlen, 0x5c, 0x00, false, true, 0); + if (!res) { + PrintAndLogEx(ERR, "Failed to read file list from EF_COM."); + free(data); + free(filepath); + return PM3_ESOFT; + } + free(data); + + // Grab the hash list + uint8_t dg_hashes_sod[17][64] = { { 0 } }; + uint8_t dg_hashes_calc[17][64] = { { 0 } }; + int hash_algo = 0; + + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg_table[EF_CardAccess].filename); + + if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) { + emrtd_print_ef_cardaccess_info(data, datalen); + } else { + PrintAndLogEx(HINT, "The error above this is normal. It just means that your eMRTD lacks PACE."); + } + + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg_table[EF_SOD].filename); + + if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to read EF_SOD."); + free(filepath); + return PM3_ESOFT; + } + + res = emrtd_parse_ef_sod_hashes(data, datalen, *dg_hashes_sod, &hash_algo); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to read hash list from EF_SOD. Hash checks will fail."); + } + free(data); + + // Read files in the file list + for (int i = 0; i < filelistlen; i++) { + emrtd_dg_t *dg = emrtd_tag_to_dg(filelist[i]); + if (dg == NULL) { + PrintAndLogEx(INFO, "File tag not found, skipping: %02X", filelist[i]); + continue; + } + if (!dg->pace && !dg->eac) { + strcpy(filepath, path); + strncat(filepath, PATHSEP, 2); + strcat(filepath, dg->filename); + if (loadFile_safeEx(filepath, ".BIN", (void **)&data, (size_t *)&datalen, false) == PM3_SUCCESS) { + // we won't halt on parsing errors + if (dg->parser != NULL) + dg->parser(data, datalen); + + PrintAndLogEx(DEBUG, "EF_DG%i hash algo: %i", dg->dgnum, hash_algo); + // Check file hash + if (hash_algo != -1) { + PrintAndLogEx(DEBUG, "EF_DG%i hash on EF_SOD: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_sod[dg->dgnum], hashalg_table[hash_algo].hashlen)); + hashalg_table[hash_algo].hasher(data, datalen, dg_hashes_calc[dg->dgnum]); + PrintAndLogEx(DEBUG, "EF_DG%i hash calc: %s", dg->dgnum, sprint_hex_inrow(dg_hashes_calc[dg->dgnum], hashalg_table[hash_algo].hashlen)); + } + free(data); + } + } + } + free(filepath); + + emrtd_print_ef_sod_info(*dg_hashes_calc, *dg_hashes_sod, hash_algo); + + return PM3_SUCCESS; +} + +static void text_to_upper(uint8_t *data, int datalen) { + // Loop over text to make lowercase text uppercase + for (int i = 0; i < datalen; i++) { + data[i] = toupper(data[i]); + } +} + +static bool validate_date(uint8_t *data, int datalen) { + // Date has to be 6 chars + if (datalen != 6) { + return false; + } + + // Check for valid date and month numbers + char temp[4] = { 0x00 }; + memcpy(temp, data + 2, 2); + int month = (int) strtol(temp, NULL, 10); + memcpy(temp, data + 4, 2); + int day = (int) strtol(temp, NULL, 10); + + return !(day <= 0 || day > 31 || month <= 0 || month > 12); +} + +static int cmd_hf_emrtd_dump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf emrtd dump", + "Dump all files on an eMRTD", + "hf emrtd dump" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("n", "documentnumber", "", "document number, up to 9 chars"), + arg_str0("d", "dateofbirth", "", "date of birth in YYMMDD format"), + arg_str0("e", "expiry", "", "expiry in YYMMDD format"), + arg_str0("m", "mrz", "<[0-9A-Z<]>", "2nd line of MRZ, 44 chars"), + arg_str0(NULL, "path", "", "save dump to the given dirpath"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t mrz[45] = { 0x00 }; + uint8_t docnum[10] = { 0x00 }; + uint8_t dob[7] = { 0x00 }; + uint8_t expiry[7] = { 0x00 }; + bool BAC = true; + bool error = false; + int slen = 0; + // Go through all args, if even one isn't supplied, mark BAC as unavailable + if (CLIParamStrToBuf(arg_get_str(ctx, 1), docnum, 9, &slen) != 0 || slen == 0) { + BAC = false; + } else { + text_to_upper(docnum, slen); + if (slen != 9) { + // Pad to 9 with < + memset(docnum + slen, '<', 9 - slen); + } + } + + if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) { + BAC = false; + } else { + if (!validate_date(dob, slen)) { + PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue."); + PrintAndLogEx(HINT, "Use the format YYMMDD."); + error = true; + } + } + + if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) { + BAC = false; + } else { + if (!validate_date(expiry, slen)) { + PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue."); + PrintAndLogEx(HINT, "Use the format YYMMDD."); + error = true; + } + } + + if (CLIParamStrToBuf(arg_get_str(ctx, 4), mrz, 44, &slen) == 0 && slen != 0) { + if (slen != 44) { + PrintAndLogEx(ERR, "MRZ length is incorrect, it should be 44, not %i", slen); + error = true; + } else { + BAC = true; + text_to_upper(mrz, slen); + memcpy(docnum, &mrz[0], 9); + memcpy(dob, &mrz[13], 6); + memcpy(expiry, &mrz[21], 6); + // TODO check MRZ checksums? + if (!validate_date(dob, 6)) { + PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue."); + PrintAndLogEx(HINT, "Use the format YYMMDD."); + error = true; + } + if (!validate_date(expiry, 6)) { + PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue."); + PrintAndLogEx(HINT, "Use the format YYMMDD."); + error = true; + } + } + } + + uint8_t path[FILENAME_MAX] = { 0x00 }; + if (CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) != 0 || slen == 0) { + path[0] = '.'; + } + + CLIParserFree(ctx); + if (error) { + return PM3_ESOFT; + } + return dumpHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC, (const char *)path); +} + +static int cmd_hf_emrtd_info(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf emrtd info", + "Display info about an eMRTD", + "hf emrtd info" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("n", "documentnumber", "", "document number, up to 9 chars"), + arg_str0("d", "dateofbirth", "", "date of birth in YYMMDD format"), + arg_str0("e", "expiry", "", "expiry in YYMMDD format"), + arg_str0("m", "mrz", "<[0-9A-Z<]>", "2nd line of MRZ, 44 chars (passports only)"), + arg_str0(NULL, "path", "", "display info from offline dump stored in dirpath"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + uint8_t mrz[45] = { 0x00 }; + uint8_t docnum[10] = { 0x00 }; + uint8_t dob[7] = { 0x00 }; + uint8_t expiry[7] = { 0x00 }; + bool BAC = true; + bool error = false; + int slen = 0; + // Go through all args, if even one isn't supplied, mark BAC as unavailable + if (CLIParamStrToBuf(arg_get_str(ctx, 1), docnum, 9, &slen) != 0 || slen == 0) { + BAC = false; + } else { + text_to_upper(docnum, slen); + if (slen != 9) { + memset(docnum + slen, '<', 9 - slen); + } + } + + if (CLIParamStrToBuf(arg_get_str(ctx, 2), dob, 6, &slen) != 0 || slen == 0) { + BAC = false; + } else { + if (!validate_date(dob, slen)) { + PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue."); + PrintAndLogEx(HINT, "Use the format YYMMDD."); + error = true; + } + } + + if (CLIParamStrToBuf(arg_get_str(ctx, 3), expiry, 6, &slen) != 0 || slen == 0) { + BAC = false; + } else { + if (!validate_date(expiry, slen)) { + PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue."); + PrintAndLogEx(HINT, "Use the format YYMMDD."); + error = true; + } + } + + if (CLIParamStrToBuf(arg_get_str(ctx, 4), mrz, 44, &slen) == 0 && slen != 0) { + if (slen != 44) { + PrintAndLogEx(ERR, "MRZ length is incorrect, it should be 44, not %i", slen); + error = true; + } else { + BAC = true; + text_to_upper(mrz, slen); + memcpy(docnum, &mrz[0], 9); + memcpy(dob, &mrz[13], 6); + memcpy(expiry, &mrz[21], 6); + // TODO check MRZ checksums? + if (!validate_date(dob, 6)) { + PrintAndLogEx(ERR, "Date of birth date format is incorrect, cannot continue."); + PrintAndLogEx(HINT, "Use the format YYMMDD."); + error = true; + } + if (!validate_date(expiry, 6)) { + PrintAndLogEx(ERR, "Expiry date format is incorrect, cannot continue."); + PrintAndLogEx(HINT, "Use the format YYMMDD."); + error = true; + } + } + } + uint8_t path[FILENAME_MAX] = { 0x00 }; + bool offline = CLIParamStrToBuf(arg_get_str(ctx, 5), path, sizeof(path), &slen) == 0 && slen > 0; + CLIParserFree(ctx); + if (error) { + return PM3_ESOFT; + } + if (offline) { + return infoHF_EMRTD_offline((const char *)path); + } else { + return infoHF_EMRTD((char *)docnum, (char *)dob, (char *)expiry, BAC); + } +} + +static int cmd_hf_emrtd_list(const char *Cmd) { + char args[128] = {0}; + if (strlen(Cmd) == 0) { + snprintf(args, sizeof(args), "-t 7816"); + } else { + strncpy(args, Cmd, sizeof(args) - 1); + } + return CmdTraceList(args); +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"dump", cmd_hf_emrtd_dump, IfPm3Iso14443, "Dump eMRTD files to binary files"}, + {"info", cmd_hf_emrtd_info, AlwaysAvailable, "Display info about an eMRTD"}, + {"list", cmd_hf_emrtd_list, AlwaysAvailable, "List ISO 14443A/7816 history"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdHFeMRTD(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/src/cmdhfemrtd.h b/client/src/cmdhfemrtd.h new file mode 100644 index 000000000..35429a568 --- /dev/null +++ b/client/src/cmdhfemrtd.h @@ -0,0 +1,56 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2020 A. Ozkal +// +// 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. +//----------------------------------------------------------------------------- +// High frequency Electronic Machine Readable Travel Document commands +//----------------------------------------------------------------------------- + +#ifndef CMDHFEMRTD_H__ +#define CMDHFEMRTD_H__ + +#include "common.h" + +typedef struct emrtd_dg_s { + uint8_t tag; + uint8_t dgnum; + const char *fileid; + const char *filename; + const char *desc; + bool pace; + bool eac; // EAC only (we can't dump these) + bool required; // some are required only if PACE + bool fastdump; // fast to dump + int (*parser)(uint8_t *data, size_t datalen); + int (*dumper)(uint8_t *data, size_t datalen, const char *path); +} emrtd_dg_t; + +typedef struct emrtd_hashalg_s { + const char *name; + int (*hasher)(uint8_t *datain, int datainlen, uint8_t *dataout); + size_t hashlen; + size_t descriptorlen; + const uint8_t descriptor[15]; +} emrtd_hashalg_t; + +typedef struct emrtd_pacealg_s { + const char *name; + int (*keygenerator)(uint8_t *datain, int datainlen, uint8_t *dataout); + const uint8_t descriptor[10]; +} emrtd_pacealg_t; + +// Standardized Domain Parameters +typedef struct emrtd_pacesdp_s { + uint8_t id; + const char *name; + size_t size; +} emrtd_pacesdp_t; + +int CmdHFeMRTD(const char *Cmd); + +int dumpHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available, const char *path); +int infoHF_EMRTD(char *documentnumber, char *dob, char *expiry, bool BAC_available); +int infoHF_EMRTD_offline(const char *path); +#endif diff --git a/client/src/cmdhfepa.c b/client/src/cmdhfepa.c index 83f347604..21b896032 100644 --- a/client/src/cmdhfepa.c +++ b/client/src/cmdhfepa.c @@ -15,7 +15,7 @@ #include #include #include // tolower - +#include "cliparser.h" #include "cmdparser.h" // command_t #include "commonutil.h" // ARRAYLEN #include "comms.h" // clearCommandBuffer @@ -24,35 +24,28 @@ static int CmdHelp(const char *Cmd); -static int usage_epa_collect(void) { - PrintAndLogEx(NORMAL, "Tries to collect nonces when doing part of PACE protocol.\n" - "\n" - "Usage: hf epa cnonces \n" - "Options:\n" - "\t nonce size\n" - "\t number of nonces to collect\n" - "\t delay between\n" - "\n" - "Example:\n" - _YELLOW_("\thf epa cnonces 4 4 1") - ); - return PM3_SUCCESS; -} - // Perform (part of) the PACE protocol static int CmdHFEPACollectPACENonces(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf epa cnonces", + "Tries to collect nonces when doing part of PACE protocol.", + "hf epa cnonces --size 4 --num 4 --delay 1"); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_epa_collect(); + void *argtable[] = { + arg_param_begin, + arg_int1(NULL, "size", "", "nonce size"), + arg_int1(NULL, "num", "", "number of nonces to collect"), + arg_int1("d", "delay", "", "delay between attempts"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - // requested nonce size - uint32_t m = 0; - // requested number of Nonces - uint32_t n = 0; - // delay between requests - uint32_t d = 0; - sscanf(Cmd, "%u %u %u", &m, &n, &d); + int m = arg_get_int_def(ctx, 1, 0); + int n = arg_get_int_def(ctx, 2, 0); + int d = arg_get_int_def(ctx, 3, 0); + + CLIParserFree(ctx); // values are expected to be > 0 m = m > 0 ? m : 1; @@ -99,54 +92,51 @@ static int CmdHFEPACollectPACENonces(const char *Cmd) { // perform the PACE protocol by replaying APDUs static int CmdHFEPAPACEReplay(const char *Cmd) { - // the 4 APDUs which are replayed + their lengths - uint8_t msesa_apdu[41] = {0}, gn_apdu[8] = {0}, map_apdu[75] = {0}; - uint8_t pka_apdu[75] = {0}, ma_apdu[18] = {0}, apdu_lengths[5] = {0}; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf epa preplay", + "Perform PACE protocol by replaying given APDUs", + "hf epa preplay --mse 0022C1A4 --get 1068000000 --map 1086000002 --pka 1234ABCDEF --ma 1A2B3C4D"); + + void *argtable[] = { + arg_param_begin, + arg_str1(NULL, "mse", "", "msesa APDU"), + arg_str1(NULL, "get", "", "gn APDU"), + arg_str1(NULL, "map", "", "map APDU"), + arg_str1(NULL, "pka", "", "pka APDU"), + arg_str1(NULL, "ma", "", "ma APDU"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int msesa_len = 0; + uint8_t msesa_apdu[41] = {0}; + CLIGetHexWithReturn(ctx, 1, msesa_apdu, &msesa_len); + + int gn_len = 0; + uint8_t gn_apdu[8] = {0}; + CLIGetHexWithReturn(ctx, 2, gn_apdu, &gn_len); + + int map_len = 0; + uint8_t map_apdu[75] = {0}; + CLIGetHexWithReturn(ctx, 3, map_apdu, &map_len); + + int pka_len = 0; + uint8_t pka_apdu[75] = {0}; + CLIGetHexWithReturn(ctx, 4, pka_apdu, &pka_len); + + int ma_len = 0; + uint8_t ma_apdu[18] = {0}; + CLIGetHexWithReturn(ctx, 5, ma_apdu, &ma_len); + + CLIParserFree(ctx); + + uint8_t apdu_lengths[5] = {msesa_len, gn_len, map_len, pka_len, ma_len}; // pointers to the arrays to be able to iterate uint8_t *apdus[] = {msesa_apdu, gn_apdu, map_apdu, pka_apdu, ma_apdu}; - // usage message - static const char *usage_msg = - "Please specify 5 APDUs separated by spaces. " - "Example:\n preplay 0022C1A4 1068000000 1086000002 1234ABCDEF 1A2B3C4D"; - // Proxmark response PacketResponseNG resp; - int skip = 0, skip_add = 0, scan_return; - // for each APDU - for (int i = 0; i < ARRAYLEN(apdu_lengths); i++) { - // scan to next space or end of string - while (Cmd[skip] != ' ' && Cmd[skip] != '\0') { - // convert - scan_return = sscanf(Cmd + skip, - "%2" SCNx8 "%n", - apdus[i] + apdu_lengths[i], - &skip_add - ); - - if (scan_return < 1) { - PrintAndLogEx(INFO, (char *)usage_msg); - PrintAndLogEx(WARNING, "Not enough APDUs! Try again!"); - return PM3_SUCCESS; - } - skip += skip_add; - apdu_lengths[i]++; - } - - // break on EOF - if (Cmd[skip] == '\0') { - if (i < ARRAYLEN(apdu_lengths) - 1) { - - PrintAndLogEx(INFO, (char *)usage_msg); - return PM3_SUCCESS; - } - break; - } - // skip the space - skip++; - } - // transfer the APDUs to the Proxmark uint8_t data[PM3_CMD_DATA_SIZE]; // fast push mode @@ -205,8 +195,8 @@ static int CmdHFEPAPACEReplay(const char *Cmd) { static command_t CommandTable[] = { {"help", CmdHelp, AlwaysAvailable, "This help"}, - {"cnonces", CmdHFEPACollectPACENonces, IfPm3Iso14443, " Acquire n>0 encrypted PACE nonces of size m>0 with d sec pauses"}, - {"preplay", CmdHFEPAPACEReplay, IfPm3Iso14443, " Perform PACE protocol by replaying given APDUs"}, + {"cnonces", CmdHFEPACollectPACENonces, IfPm3Iso14443, "Acquire encrypted PACE nonces of specific size"}, + {"preplay", CmdHFEPAPACEReplay, IfPm3Iso14443, "Perform PACE protocol by replaying given APDUs"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhffido.c b/client/src/cmdhffido.c index 7535a5ef4..d3ea8e1cb 100644 --- a/client/src/cmdhffido.c +++ b/client/src/cmdhffido.c @@ -48,10 +48,18 @@ static int cmd_hf_fido_list(const char *Cmd) { return CmdTraceList(args); } -static int cmd_hf_fido_info(const char *cmd) { +static int cmd_hf_fido_info(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf fido info", + "Get info from Fido tags", + "hf fido info"); - if (cmd && strlen(cmd) > 0) - PrintAndLogEx(WARNING, "WARNING: command doesn't have any parameters.\n"); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); // info about 14a part infoHF14A(false, false, false); diff --git a/client/src/cmdhficlass.c b/client/src/cmdhficlass.c index d884e7184..2fda649b8 100644 --- a/client/src/cmdhficlass.c +++ b/client/src/cmdhficlass.c @@ -18,7 +18,6 @@ #include "cmdparser.h" // command_t #include "commonutil.h" // ARRAYLEN #include "cmdtrace.h" -#include "cliparser.h" #include "util_posix.h" #include "comms.h" #include "des.h" @@ -69,7 +68,7 @@ bool check_known_default(uint8_t *csn, uint8_t *epurse, uint8_t *rmac, uint8_t * iclass_prekey_t *prekey = calloc(ICLASS_KEYS_MAX, sizeof(iclass_prekey_t)); if (prekey == false) { - return PM3_EMALLOC; + return false; } uint8_t ccnr[12]; @@ -624,7 +623,7 @@ static int CmdHFiClassReader(const char *Cmd) { CLIParserInit(&ctx, "hf iclass reader", "Act as a iCLASS reader. Look for iCLASS tags until Enter or the pm3 button is pressed", "hf iclass reader -@ -> continuous reader mode" - ); + ); void *argtable[] = { arg_param_begin, @@ -941,22 +940,28 @@ static int CmdHFiClassDecrypt(const char *Cmd) { } bool verbose = arg_get_lit(clictx, 4); - CLIParserFree(clictx); - size_t keylen = 0; uint8_t dec_data[8] = {0}; - - bool use_sc = IsCryptoHelperPresent(verbose); - - if (have_key == false && use_sc == false) { - int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen); - if (res != PM3_SUCCESS) { - PrintAndLogEx(INFO, "Couldn't find any decryption methods"); - return PM3_EINVARG; + bool use_sc = false; + if (have_key == false) { + use_sc = IsCryptoHelperPresent(verbose); + if (use_sc == false) { + size_t keylen = 0; + int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(INFO, "Couldn't find any decryption methods"); + return PM3_EINVARG; + } + + if (keylen != 16) { + PrintAndLogEx(ERR, "Failed to load transport key from file"); + free(keyptr); + return PM3_EINVARG; + } + memcpy(key, keyptr, sizeof(key)); + free(keyptr); } - memcpy(key, keyptr, sizeof(key)); - free(keyptr); } // tripledes @@ -1076,10 +1081,8 @@ static int CmdHFiClassDecrypt(const char *Cmd) { PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "Block 9 decoder"); - uint8_t pinsize = 0; if (use_sc) { - pinsize = GetPinSize(decrypted + (8 * 6)); - + uint8_t pinsize = GetPinSize(decrypted + (8 * 6)); if (pinsize > 0) { uint64_t pin = bytes_to_num(decrypted + (8 * 9), 5); @@ -1149,7 +1152,7 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { if (key_len > 0) { if (key_len != 16) { - PrintAndLogEx(ERR, "Transport key must be 16 hex ytes (32 HEX characters)"); + PrintAndLogEx(ERR, "Transport key must be 16 hex bytes (32 HEX characters)"); CLIParserFree(clictx); return PM3_EINVARG; } @@ -1160,16 +1163,25 @@ static int CmdHFiClassEncryptBlk(const char *Cmd) { CLIParserFree(clictx); - bool use_sc = IsCryptoHelperPresent(verbose); + bool use_sc = false; + if (have_key == false) { + use_sc = IsCryptoHelperPresent(verbose); + if (use_sc == false) { + size_t keylen = 0; + int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to find any encryption methods"); + return PM3_EINVARG; + } - if (have_key == false && use_sc == false) { - size_t keylen = 0; - int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&keyptr, &keylen); - if (res != PM3_SUCCESS) - return PM3_EINVARG; - - memcpy(key, keyptr, sizeof(key)); - free(keyptr); + if (keylen != 16) { + PrintAndLogEx(ERR, "Failed to load transport key from file"); + free(keyptr); + return PM3_EINVARG; + } + memcpy(key, keyptr, sizeof(key)); + free(keyptr); + } } if (use_sc) { @@ -1698,7 +1710,7 @@ static int CmdHFiClassRestore(const char *Cmd) { "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 --ki 0\n" "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 --ki 0 --elite\n" "hf iclass restore -f hf-iclass-AA162D30F8FF12F1-dump.bin --first 6 --last 18 -k 1122334455667788 --elite\n" - ); + ); void *argtable[] = { arg_param_begin, @@ -2075,7 +2087,7 @@ static int CmdHFiClass_loclass(const char *Cmd) { " <8 byte CSN><8 byte CC><4 byte NR><4 byte MAC>\n" " <8 byte CSN><8 byte CC><4 byte NR><4 byte MAC>\n" " ... totalling N*24 bytes", - "hf iclass loclass -f iclass-dump.bin\n" + "hf iclass loclass -f iclass_dump.bin\n" "hf iclass loclass --test"); void *argtable[] = { @@ -3314,6 +3326,153 @@ static int CmdHFiClassPermuteKey(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHFiClassEncode(const char *Cmd) { + + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf iclass encode", + "Encode binary wiegand to block 7", + "hf iclass encode --bin 10001111100000001010100011 --ki 0 -> FC 31 CN 337\n" + "hf iclass encode --bin 10001111100000001010100011 --ki 0 --elite -> FC 31 CN 337, writing w elite key" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1(NULL, "bin", "", "Binary string i.e 0001001001"), + arg_int1(NULL, "ki", "", "Key index to select key from memory 'hf iclass managekeys'"), + arg_lit0(NULL, "credit", "key is assumed to be the credit key"), + arg_lit0(NULL, "elite", "elite computations applied to key"), + arg_lit0(NULL, "raw", "no computations applied to key"), + arg_str0(NULL, "enckey", "", "3DES transport key, 16 hex bytes"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int bin_len = 63; + uint8_t bin[70] = {0}; + CLIGetStrWithReturn(ctx, 1, bin, &bin_len); + + int key_nr = arg_get_int_def(ctx, 2, -1); + bool auth = false; + + uint8_t key[8] = {0}; + if (key_nr >= 0) { + if (key_nr < ICLASS_KEYS_MAX) { + auth = true; + memcpy(key, iClass_Key_Table[key_nr], 8); + PrintAndLogEx(SUCCESS, "Using key[%d] " _GREEN_("%s"), key_nr, sprint_hex(iClass_Key_Table[key_nr], 8)); + } else { + PrintAndLogEx(ERR, "Key number is invalid"); + CLIParserFree(ctx); + return PM3_EINVARG; + } + } + + bool use_credit_key = arg_get_lit(ctx, 3); + bool elite = arg_get_lit(ctx, 4); + bool rawkey = arg_get_lit(ctx, 5); + + int enc_key_len = 0; + uint8_t enc_key[16] = {0}; + uint8_t *enckeyptr = NULL; + bool have_enc_key = false; + bool use_sc = false; + CLIGetHexWithReturn(ctx, 6, enc_key, &enc_key_len); + + CLIParserFree(ctx); + + if ((rawkey + elite) > 1) { + PrintAndLogEx(ERR, "Can not use a combo of 'elite', 'raw'"); + return PM3_EINVARG; + } + + if (enc_key_len > 0) { + if (enc_key_len != 16) { + PrintAndLogEx(ERR, "Transport key must be 16 hex bytes (32 HEX characters)"); + return PM3_EINVARG; + } + have_enc_key = true; + } + + if (bin_len > 127) { + PrintAndLogEx(ERR, "Binary wiegand string must be less than 128 bits"); + return PM3_EINVARG; + } + + if (have_enc_key == false) { + use_sc = IsCryptoHelperPresent(false); + if (use_sc == false) { + size_t keylen = 0; + int res = loadFile_safe(ICLASS_DECRYPTION_BIN, "", (void **)&enckeyptr, &keylen); + if (res != PM3_SUCCESS) { + PrintAndLogEx(ERR, "Failed to find the transport key"); + return PM3_EINVARG; + } + if (keylen != 16) { + PrintAndLogEx(ERR, "Failed to load transport key from file"); + return PM3_EINVARG; + } + memcpy(enc_key, enckeyptr, sizeof(enc_key)); + free(enckeyptr); + } + } + + uint8_t credential[] = { + 0x03, 0x03, 0x03, 0x03, 0x00, 0x03, 0xE0, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + uint8_t data[8]; + memset(data, 0, sizeof(data)); + BitstreamOut bout = {data, 0, 0 }; + + for (int i = 0; i < 64 - bin_len - 1; i++) { + pushBit(&bout, 0); + } + // add binary sentinel bit. + pushBit(&bout, 1); + + // convert binary string to hex bytes + for (int i = 0; i < bin_len; i++) { + char c = bin[i]; + if (c == '1') + pushBit(&bout, 1); + else if (c == '0') + pushBit(&bout, 0); + else { + PrintAndLogEx(WARNING, "Ignoring '%c'", c); + } + } + memcpy(credential + 8, data, sizeof(data)); + + // encrypt with transport key + if (use_sc) { + Encrypt(credential + 8, credential + 8); + Encrypt(credential + 16, credential + 16); + Encrypt(credential + 24, credential + 24); + } else { + iclass_encrypt_block_data(credential + 8, enc_key); + iclass_encrypt_block_data(credential + 16, enc_key); + iclass_encrypt_block_data(credential + 24, enc_key); + } + + int isok = PM3_SUCCESS; + // write + for (uint8_t i = 0; i < 4; i++) { + isok = iclass_write_block(6 + i, credential + (i * 8), key, use_credit_key, elite, rawkey, false, false, auth); + switch (isok) { + case PM3_SUCCESS: + PrintAndLogEx(SUCCESS, "Write block %d/0x0%x ( " _GREEN_("ok") " ) --> " _YELLOW_("%s"), 6 + i, 6 + i, sprint_hex_inrow(credential + (i * 8), 8)); + break; + default: + PrintAndLogEx(SUCCESS, "Write block %d/0x0%x ( " _RED_("fail") " )", 6 + i, 6 + i); + break; + } + } + return isok; +} + /* static int CmdHFiClassAutopwn(const char *Cmd) { @@ -3341,34 +3500,35 @@ static int CmdHFiClassAutopwn(const char *Cmd) { static command_t CommandTable[] = { {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("operations") " ---------------------"}, {"help", CmdHelp, AlwaysAvailable, "This help"}, -// {"clone", CmdHFiClassClone, IfPm3Iclass, "[options..] Create a HID credential to Picopass / iCLASS tag"}, - {"dump", CmdHFiClassDump, IfPm3Iclass, "[options..] Dump Picopass / iCLASS tag to file"}, - {"info", CmdHFiClassInfo, AlwaysAvailable, " Tag information"}, - {"list", CmdHFiClassList, AlwaysAvailable, " List iclass history"}, - {"rdbl", CmdHFiClass_ReadBlock, IfPm3Iclass, "[options..] Read Picopass / iCLASS block"}, - {"reader", CmdHFiClassReader, IfPm3Iclass, " Act like an Picopass / iCLASS reader"}, - {"restore", CmdHFiClassRestore, IfPm3Iclass, "[options..] Restore a dump file onto a Picopass / iCLASS tag"}, - {"sniff", CmdHFiClassSniff, IfPm3Iclass, " Eavesdrop Picopass / iCLASS communication"}, - {"wrbl", CmdHFiClass_WriteBlock, IfPm3Iclass, "[options..] Write Picopass / iCLASS block"}, +// {"clone", CmdHFiClassClone, IfPm3Iclass, "Create a HID credential to Picopass / iCLASS tag"}, + {"dump", CmdHFiClassDump, IfPm3Iclass, "Dump Picopass / iCLASS tag to file"}, + {"info", CmdHFiClassInfo, AlwaysAvailable, "Tag information"}, + {"list", CmdHFiClassList, AlwaysAvailable, "List iclass history"}, + {"rdbl", CmdHFiClass_ReadBlock, IfPm3Iclass, "Read Picopass / iCLASS block"}, + {"reader", CmdHFiClassReader, IfPm3Iclass, "Act like an Picopass / iCLASS reader"}, + {"restore", CmdHFiClassRestore, IfPm3Iclass, "Restore a dump file onto a Picopass / iCLASS tag"}, + {"sniff", CmdHFiClassSniff, IfPm3Iclass, "Eavesdrop Picopass / iCLASS communication"}, + {"wrbl", CmdHFiClass_WriteBlock, IfPm3Iclass, "Write Picopass / iCLASS block"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("recovery") " ---------------------"}, -// {"autopwn", CmdHFiClassAutopwn, IfPm3Iclass, "[options..] Automatic key recovery tool for iCLASS"}, - {"chk", CmdHFiClassCheckKeys, IfPm3Iclass, "[options..] Check keys"}, - {"loclass", CmdHFiClass_loclass, AlwaysAvailable, "[options..] Use loclass to perform bruteforce reader attack"}, - {"lookup", CmdHFiClassLookUp, AlwaysAvailable, "[options..] Uses authentication trace to check for key in dictionary file"}, +// {"autopwn", CmdHFiClassAutopwn, IfPm3Iclass, "Automatic key recovery tool for iCLASS"}, + {"chk", CmdHFiClassCheckKeys, IfPm3Iclass, "Check keys"}, + {"loclass", CmdHFiClass_loclass, AlwaysAvailable, "Use loclass to perform bruteforce reader attack"}, + {"lookup", CmdHFiClassLookUp, AlwaysAvailable, "Uses authentication trace to check for key in dictionary file"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("simulation") " ---------------------"}, - {"sim", CmdHFiClassSim, IfPm3Iclass, "[options..] Simulate iCLASS tag"}, - {"eload", CmdHFiClassELoad, IfPm3Iclass, "[f ] Load Picopass / iCLASS dump file into emulator memory"}, - {"esave", CmdHFiClassESave, IfPm3Iclass, "[f ] Save emulator memory to file"}, - {"eview", CmdHFiClassEView, IfPm3Iclass, "[options..] View emulator memory"}, + {"sim", CmdHFiClassSim, IfPm3Iclass, "Simulate iCLASS tag"}, + {"eload", CmdHFiClassELoad, IfPm3Iclass, "Load Picopass / iCLASS dump file into emulator memory"}, + {"esave", CmdHFiClassESave, IfPm3Iclass, "Save emulator memory to file"}, + {"eview", CmdHFiClassEView, IfPm3Iclass, "View emulator memory"}, {"-----------", CmdHelp, AlwaysAvailable, "--------------------- " _CYAN_("utils") " ---------------------"}, - {"calcnewkey", CmdHFiClassCalcNewKey, AlwaysAvailable, "[options..] Calc diversified keys (blocks 3 & 4) to write new keys"}, - {"encrypt", CmdHFiClassEncryptBlk, AlwaysAvailable, "[options..] Encrypt given block data"}, - {"decrypt", CmdHFiClassDecrypt, AlwaysAvailable, "[options..] Decrypt given block data or tag dump file" }, - {"managekeys", CmdHFiClassManageKeys, AlwaysAvailable, "[options..] Manage keys to use with iclass commands"}, - {"permutekey", CmdHFiClassPermuteKey, IfPm3Iclass, " Permute function from 'heart of darkness' paper"}, - {"view", CmdHFiClassView, AlwaysAvailable, "[options..] Display content from tag dump file"}, + {"calcnewkey", CmdHFiClassCalcNewKey, AlwaysAvailable, "Calc diversified keys (blocks 3 & 4) to write new keys"}, + {"encode", CmdHFiClassEncode, AlwaysAvailable, "Encode binary wiegand to block 7"}, + {"encrypt", CmdHFiClassEncryptBlk, AlwaysAvailable, "Encrypt given block data"}, + {"decrypt", CmdHFiClassDecrypt, AlwaysAvailable, "Decrypt given block data or tag dump file" }, + {"managekeys", CmdHFiClassManageKeys, AlwaysAvailable, "Manage keys to use with iclass commands"}, + {"permutekey", CmdHFiClassPermuteKey, IfPm3Iclass, "Permute function from 'heart of darkness' paper"}, + {"view", CmdHFiClassView, AlwaysAvailable, "Display content from tag dump file"}, {NULL, NULL, NULL, NULL} }; diff --git a/client/src/cmdhfjooki.c b/client/src/cmdhfjooki.c new file mode 100644 index 000000000..39a4d4286 --- /dev/null +++ b/client/src/cmdhfjooki.c @@ -0,0 +1,676 @@ +//----------------------------------------------------------------------------- +// Ultralight Code (c) 2021 Iceman +// 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. +//----------------------------------------------------------------------------- +// High frequency MIFARE ULTRALIGHT / Jooki commands +//----------------------------------------------------------------------------- +#include "cmdhfjooki.h" +#include +#include // memset +#include "commonutil.h" // ARRAYLEN +#include "ui.h" // PrintAndLog +#include "cmdparser.h" +#include "generator.h" +#include "base64.h" +#include "mifare/ndef.h" // print decode ndef +#include "mifare/mifarehost.h" // mfemlsetmem_xt +#include "cliparser.h" +#include "cmdhfmfu.h" +#include "cmdmain.h" +#include "fileutils.h" // convert_mfu.. + +static int CmdHelp(const char *Cmd); + +typedef struct { + uint8_t typeid; + uint8_t figureid; + const char figdesc[40]; + const char typedesc[12]; +} PACKED jooki_figure_t; + +typedef struct { + uint8_t uid[7]; + const char b64[17]; + uint8_t typeid; + uint8_t figureid; +} PACKED jooki_test_t; + +// sample set for selftest. +jooki_test_t jooks[] = { + { {0x04, 0xDA, 0xB7, 0x6A, 0xE7, 0x4C, 0x80}, "ruxow8lnn88uyeX+", 0x01, 0x00}, + { {0x04, 0xf0, 0x22, 0xc2, 0x33, 0x5e, 0x80}, "\0", 0x01, 0x00}, + { {0x04, 0x8C, 0xEC, 0xDA, 0xF0, 0x4A, 0x80}, "ONrsVf7jX6IaSNV6", 0x01, 0x01}, + { {0x04, 0x92, 0xA7, 0x6A, 0xE7, 0x4C, 0x81}, "Hjjpcx/mZwuveTF+", 0x01, 0x02}, + { {0x04, 0xD0, 0xB0, 0x3A, 0xD3, 0x63, 0x80}, "\0", 0x01, 0x02}, + { {0x04, 0x96, 0x42, 0xDA, 0xF0, 0x4A, 0x80}, "vEWy0WO9wZNEzEok", 0x01, 0x03}, + { {0x04, 0x33, 0xb5, 0x62, 0x39, 0x4d, 0x80}, "\0", 0x01, 0x03}, + { {0x04, 0x17, 0xB7, 0x3A, 0xD3, 0x63, 0x81}, "f0axEma+g2WnLGAm", 0x01, 0x05}, + { {0x04, 0x84, 0x27, 0x6A, 0xE7, 0x4C, 0x80}, "VZB/OLBwOiM5Mpnp", 0x01, 0x05}, + { {0x04, 0x28, 0xF4, 0xDA, 0xF0, 0x4A, 0x81}, "7WzlgEzqLgwTnWNy", 0x01, 0x05}, +}; + +jooki_figure_t jooks_figures[] = { + {0x01, 0x00, "Dragon", "Figurine"}, + {0x01, 0x01, "Fox", "Figurine"}, + {0x01, 0x02, "Ghost", "Figurine"}, + {0x01, 0x03, "Knight", "Figurine"}, + {0x01, 0x04, "ThankYou", "Figurine"}, + {0x01, 0x05, "Whale", "Figurine"}, + {0x01, 0x06, "Black Dragon", "Figurine"}, + {0x01, 0x07, "Black Fox", "Figurine"}, + {0x01, 0x08, "Black Knight", "Figurine"}, + {0x01, 0x09, "Black Whale", "Figurine"}, + {0x01, 0x0A, "White Dragon", "Figurine"}, + {0x01, 0x0B, "White Fox", "Figurine"}, + {0x01, 0x0C, "White Knight", "Figurine"}, + {0x01, 0x0D, "White Whale", "Figurine"}, + + {0x02, 0x00, "Generic Flat", "Stone"}, + + {0x03, 0x00, "record", "Sys"}, + {0x03, 0x01, "factory_mode_on", "Sys"}, + {0x03, 0x02, "factory_mode_off", "Sys"}, + {0x03, 0x03, "airplane_mode_on", "Sys"}, + {0x03, 0x04, "airplane_mode_off", "Sys"}, + {0x03, 0x05, "toy_safe_on", "Sys"}, + {0x03, 0x06, "toy_safe_off", "Sys"}, + {0x03, 0x07, "wifi_on", "Sys"}, + {0x03, 0x08, "wifi_off", "Sys"}, + {0x03, 0x09, "bt_on", "Sys"}, + {0x03, 0x0A, "bt_off", "Sys"}, + {0x03, 0x0B, "production_finished", "Sys"}, + + {0x04, 0x00, "test.0", "Test"}, + {0x04, 0x01, "test.1", "Test"}, + {0x04, 0x02, "test.2", "Test"}, + {0x04, 0x03, "test.3", "Test"}, + {0x04, 0x04, "test.4", "Test"}, + {0x04, 0x05, "test.5", "Test"}, + {0x04, 0x06, "test.6", "Test"}, + {0x04, 0x07, "test.7", "Test"}, + {0x04, 0x08, "test.8", "Test"}, + {0x04, 0x09, "test.9", "Test"}, + {0x04, 0x10, "test.10", "Test"}, + {0x04, 0x11, "test.11", "Test"}, + {0x04, 0x12, "test.12", "Test"}, + {0x04, 0x13, "test.13", "Test"}, + {0x04, 0x14, "test.14", "Test"}, + {0x04, 0x15, "test.15", "Test"}, + {0x04, 0x16, "test.16", "Test"}, + {0x04, 0x17, "test.17", "Test"}, + {0x04, 0x18, "test.18", "Test"}, + {0x04, 0x19, "test.19", "Test"}, + {0x04, 0x20, "test.20", "Test"}, +}; + +static int jooki_lookup(uint8_t tid, uint8_t fid) { + for (int i = 0; i < ARRAYLEN(jooks_figures); i++) { + jooki_figure_t tmp = jooks_figures[i]; + if (tmp.typeid == tid && tmp.figureid == fid) { + return i; + } + } + return -1; +} + +const uint8_t jooki_secret[] = {0x20, 0x20, 0x20, 0x6D, 0x24, 0x0B, 0xEB, 0x94, 0x2C, 0x80, 0x45, 0x16}; +const uint8_t NFC_SECRET[] = { 0x03, 0x9c, 0x25, 0x6f, 0xb9, 0x2e, 0xe8, 0x08, 0x09, 0x83, 0xd9, 0x33, 0x56}; + +#define JOOKI_UID_LEN 7 +#define JOOKI_IV_LEN 3 +#define JOOKI_B64_LEN (16 + 1) +#define JOOKI_PLAIN_LEN 12 + +static int jooki_encode(uint8_t *iv, uint8_t tid, uint8_t fid, uint8_t *uid, uint8_t *out) { + if (out == NULL) { + PrintAndLogEx(ERR, "(encode jooki) base64ndef param is NULL"); + return PM3_EINVARG; + } + + out[0] = 0x00; + if (iv == NULL || uid == NULL) { + PrintAndLogEx(ERR, "(encode jooki) iv or uid param is NULL"); + return PM3_EINVARG; + } + + uint8_t d[JOOKI_PLAIN_LEN] = {iv[0], iv[1], iv[2], tid, fid, uid[0], uid[1], uid[2], uid[3], uid[4], uid[5], uid[6]}; + uint8_t enc[JOOKI_PLAIN_LEN] = {0}; + for (uint8_t i = 0; i < JOOKI_PLAIN_LEN; i++) { + + if (i < 3) + enc[i] = d[i] ^ NFC_SECRET[i]; + else + enc[i] = d[i] ^ NFC_SECRET[i] ^ d[i % 3]; + } + + PrintAndLogEx(DEBUG, "encoded result.... %s", sprint_hex(enc, sizeof(enc))); + + size_t b64len = 0; + uint8_t b64[20]; + memset(b64, 0, 20); + mbedtls_base64_encode(b64, sizeof(b64), &b64len, (const unsigned char *)enc, sizeof(enc)); + memcpy(out, b64, b64len); + return PM3_SUCCESS; +} + +static int jooki_decode(uint8_t *b64, uint8_t *result) { + uint8_t ndef[JOOKI_PLAIN_LEN] = {0}; + size_t outputlen = 0; + mbedtls_base64_decode(ndef, sizeof(ndef), &outputlen, (const unsigned char *)b64, 16); + + PrintAndLogEx(DEBUG, "(decode_jooki) raw encoded... " _GREEN_("%s"), sprint_hex(ndef, sizeof(ndef))); + + for (uint8_t i = 0; i < JOOKI_PLAIN_LEN; i++) { + if (i < 3) + result[i] = ndef[i] ^ NFC_SECRET[i]; + else + result[i] = ndef[i] ^ NFC_SECRET[i] ^ ndef[i % 3] ^ NFC_SECRET[i % 3]; + } + PrintAndLogEx(DEBUG, "(decode_jooki) plain......... %s", sprint_hex(result, sizeof(ndef))); + return PM3_SUCCESS; +} + +static int jooki_create_ndef(uint8_t *b64ndef, uint8_t *ndefrecord) { + // sample of url: https://s.jooki.rocks/s/?s=ONrsVf7jX6IaSNV6 + if (ndefrecord == NULL) { + PrintAndLogEx(ERR, "(jooki_create_ndef) ndefrecord param is NULL"); + return PM3_EINVARG; + } + memcpy(ndefrecord, + "\x01\x03\xa0\x0c\x34\x03\x29\xd1" + "\x01\x25\x55\x04\x73\x2e\x6a\x6f" + "\x6f\x6b\x69\x2e\x72\x6f\x63\x6b" + "\x73\x2f\x73\x2f\x3f\x73\x3d", 31); + memcpy(ndefrecord + 31, b64ndef, 16); + memcpy(ndefrecord + 47, "\x0a\xFE\x00\x00\x00", 5); + return PM3_SUCCESS; +} + +static void jooki_printEx(uint8_t *b64, uint8_t *iv, uint8_t tid, uint8_t fid, uint8_t *uid, bool verbose) { + int idx = jooki_lookup(tid, fid); + + PrintAndLogEx(INFO, "Encoded URL.. %s ( %s )", sprint_hex(b64, 12), b64); + PrintAndLogEx(INFO, "Figurine..... %02x %02x - " _GREEN_("%s, %s") + , tid + , fid + , (idx != -1) ? jooks_figures[idx].typedesc : "n/a" + , (idx != -1) ? jooks_figures[idx].figdesc : "n/a" + ); + PrintAndLogEx(INFO, "iv........... %s", sprint_hex(iv, JOOKI_IV_LEN)); + PrintAndLogEx(INFO, "uid.......... %s", sprint_hex(uid, JOOKI_UID_LEN)); + + uint8_t ndefmsg[52] = {0}; + jooki_create_ndef(b64, ndefmsg); + PrintAndLogEx(INFO, "NDEF raw..... %s", sprint_hex_inrow(ndefmsg, sizeof(ndefmsg))); + + if (verbose) { + int res = NDEFRecordsDecodeAndPrint(ndefmsg, sizeof(ndefmsg)); + if (res != PM3_SUCCESS) { + NDEFDecodeAndPrint(ndefmsg, sizeof(ndefmsg), verbose); + } + } +} + +static void jooki_print(uint8_t *b64, uint8_t *result, bool verbose) { + if (b64 == NULL || result == NULL) + return; + + uint8_t iv[JOOKI_IV_LEN] = {0}; + uint8_t uid[JOOKI_UID_LEN] = {0}; + memcpy(iv, result, JOOKI_IV_LEN); + uint8_t tid = result[3]; + uint8_t fid = result[4]; + memcpy(uid, result + 5, JOOKI_UID_LEN); + + jooki_printEx(b64, iv, tid, fid, uid, verbose); +} + +static int jooki_selftest(void) { + + PrintAndLogEx(INFO, "======== " _CYAN_("selftest") " ==========================================="); + for (int i = 0; i < ARRAYLEN(jooks); i++) { + if (strlen(jooks[i].b64) == 0) + continue; + + uint8_t iv[JOOKI_IV_LEN] = {0}; + uint8_t uid[JOOKI_UID_LEN] = {0}; + uint8_t result[JOOKI_PLAIN_LEN] = {0}; + jooki_decode((uint8_t *)jooks[i].b64, result); + + memcpy(iv, result, JOOKI_IV_LEN); + uint8_t tid = result[3]; + uint8_t fid = result[4]; + memcpy(uid, result + 5, sizeof(uid)); + + bool tid_ok = (tid == jooks[i].typeid); + bool fid_ok = (fid == jooks[i].figureid); + bool uid_ok = (memcmp(uid, jooks[i].uid, sizeof(uid)) == 0); + + int idx = jooki_lookup(tid, fid); + + PrintAndLogEx(INFO, "Encoded URL.. %s ( %s )", sprint_hex((const uint8_t *)jooks[i].b64, 12), jooks[i].b64); + PrintAndLogEx(INFO, "Type......... %02x - " _GREEN_("%s") " ( %s )", tid, (idx != -1) ? jooks_figures[idx].typedesc : "n/a", tid_ok ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx(INFO, "Figurine..... %02x - " _GREEN_("%s") " ( %s )", fid, (idx != -1) ? jooks_figures[idx].figdesc : "n/a", fid_ok ? _GREEN_("ok") : _RED_("fail")); + PrintAndLogEx(INFO, "iv........... %s", sprint_hex(iv, sizeof(iv))); + PrintAndLogEx(INFO, "uid.......... %s ( %s )", sprint_hex(uid, sizeof(uid)), uid_ok ? _GREEN_("ok") : _RED_("fail")); + + uint8_t b64[JOOKI_B64_LEN] = {0}; + memset(b64, 0, sizeof(b64)); + jooki_encode(iv, tid, fid, uid, b64); + + uint8_t ndefmsg[52] = {0}; + jooki_create_ndef(b64, ndefmsg); + PrintAndLogEx(INFO, "NDEF raw .... %s", sprint_hex(ndefmsg, sizeof(ndefmsg))); + + int status = NDEFRecordsDecodeAndPrint(ndefmsg, sizeof(ndefmsg)); + if (status != PM3_SUCCESS) { + status = NDEFDecodeAndPrint(ndefmsg, sizeof(ndefmsg), true); + } + PrintAndLogEx(INFO, "=================================================================="); + } + return PM3_SUCCESS; +} + +static int CmdHF14AJookiEncode(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf jooki encode", + "Encode a Jooki token to base64 NDEF URI format", + "hf jooki encode -t --> selftest\n" + "hf jooki encode -r --dragon --> read uid from tag and use for encoding\n" + "hf jooki encode --uid 04010203040506 --dragon\n" + "hf jooki encode --uid 04010203040506 --tid 1 --fid 1" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("u", "uid", "", "uid bytes"), + arg_lit0("r", NULL, "read uid from tag instead"), + arg_lit0("t", NULL, "selftest"), + arg_lit0("v", "verbose", "verbose output"), + arg_lit0(NULL, "dragon", "figurine type"), + arg_lit0(NULL, "fox", "figurine type"), + arg_lit0(NULL, "ghost", "figurine type"), + arg_lit0(NULL, "knight", "figurine type"), + arg_lit0(NULL, "whale", "figurine type"), + arg_lit0(NULL, "blackdragon", "figurine type"), + arg_lit0(NULL, "blackfox", "figurine type"), + arg_lit0(NULL, "blackknight", "figurine type"), + arg_lit0(NULL, "blackwhale", "figurine type"), + arg_lit0(NULL, "whitedragon", "figurine type"), + arg_lit0(NULL, "whitefox", "figurine type"), + arg_lit0(NULL, "whiteknight", "figurine type"), + arg_lit0(NULL, "whitewhale", "figurine type"), + arg_u64_0(NULL, "tid", "", "figurine type id"), + arg_u64_0(NULL, "fid", "", "figurine id"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int ulen = 0; + uint8_t uid[JOOKI_UID_LEN] = {0x00}; + memset(uid, 0x0, sizeof(uid)); + int res = CLIParamHexToBuf(arg_get_str(ctx, 1), uid, sizeof(uid), &ulen); + if (res) { + CLIParserFree(ctx); + return PM3_EINVARG; + } + + bool use_tag = arg_get_lit(ctx, 2); + bool selftest = arg_get_lit(ctx, 3); + bool verbose = arg_get_lit(ctx, 4); + bool t0 = arg_get_lit(ctx, 5); + bool t1 = arg_get_lit(ctx, 6); + bool t2 = arg_get_lit(ctx, 7); + bool t3 = arg_get_lit(ctx, 8); + bool t5 = arg_get_lit(ctx, 9); + bool t6 = arg_get_lit(ctx, 10); + bool t7 = arg_get_lit(ctx, 11); + bool t8 = arg_get_lit(ctx, 12); + bool t9 = arg_get_lit(ctx, 13); + bool ta = arg_get_lit(ctx, 14); + bool tb = arg_get_lit(ctx, 15); + bool tc = arg_get_lit(ctx, 16); + bool td = arg_get_lit(ctx, 17); + + uint8_t ftid = arg_get_u32_def(ctx, 18, 0); + uint8_t ffid = arg_get_u32_def(ctx, 19, 0); + + bool figure_abbr = true; + + CLIParserFree(ctx); + + if (selftest) { + return jooki_selftest(); + } + + uint8_t tid, fid; + + if (ftid || ffid) { + figure_abbr = false; + } + + if (ftid > 0x04 || ffid > 0x20) { + PrintAndLogEx(ERR, "Use a valid Figure Type ID and Figure ID"); + return PM3_EINVARG; + } + + uint8_t figure_abbr_val = t0 + t1 + t2 + t3 + t5 + t6 + t7 + t8 + t9 + ta + tb + tc + td; + + if (figure_abbr_val > 1) { + PrintAndLogEx(ERR, "Select one tag type or use figurine type id and figurine id"); + return PM3_EINVARG; + } + + if (figure_abbr_val == 1 && !figure_abbr) { + PrintAndLogEx(ERR, "Use either --tid and --fid or one of the figurine types"); + return PM3_EINVARG; + } + + if (figure_abbr) { + tid = 0x01; + } else { + tid = ftid; + } + fid = ffid; + + if (t1) + fid = 0x01; + if (t2) + fid = 0x02; + if (t3) + fid = 0x03; + if (t5) + fid = 0x05; + if (t6) + fid = 0x06; + if (t7) + fid = 0x07; + if (t8) + fid = 0x08; + if (t9) + fid = 0x09; + if (ta) + fid = 0x0a; + if (tb) + fid = 0x0b; + if (tc) + fid = 0x0c; + if (td) + fid = 0x0d; + + uint8_t iv[JOOKI_IV_LEN] = {0x80, 0x77, 0x51}; + if (use_tag) { + res = ul_read_uid(uid); + if (res != PM3_SUCCESS) { + return res; + } + } else { + if (ulen != JOOKI_UID_LEN) { + PrintAndLogEx(ERR, "Wrong length of UID, expect %u, got %d", JOOKI_UID_LEN, ulen); + return PM3_EINVARG; + } + } + + uint8_t b64[JOOKI_B64_LEN] = {0}; + memset(b64, 0, sizeof(b64)); + jooki_encode(iv, tid, fid, uid, b64); + jooki_printEx(b64, iv, tid, fid, uid, verbose); + return PM3_SUCCESS; +} + +static int CmdHF14AJookiDecode(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf jooki decode", + "Decode a base64-encode Jooki token in NDEF URI format", + "hf jooki decode -d 7WzlgEzqLgwTnWNy" + ); + + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "base64 url parameter"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int dlen = 16; + uint8_t b64[JOOKI_B64_LEN] = {0x00}; + memset(b64, 0x0, sizeof(b64)); + CLIGetStrWithReturn(ctx, 1, b64, &dlen); + bool verbose = arg_get_lit(ctx, 2); + CLIParserFree(ctx); + + uint8_t result[JOOKI_PLAIN_LEN] = {0}; + int res = jooki_decode(b64, result); + if (res == PM3_SUCCESS) { + jooki_print(b64, result, verbose); + } + return PM3_SUCCESS; +} + +static int CmdHF14AJookiSim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf jooki sim", + "Simulate a Jooki token. Either `hf mfu eload` before or use `-d` param", + "hf jooki sim --> use token in emulator memory\n" + "hf jooki sim -b 7WzlgEzqLgwTnWNy --> using base64 url parameter" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("b", "b64", "", "base64 url parameter"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + int dlen = 16; + uint8_t b64[JOOKI_B64_LEN] = {0x00}; + memset(b64, 0x0, sizeof(b64)); + CLIGetStrWithReturn(ctx, 1, b64, &dlen); + CLIParserFree(ctx); + + uint8_t result[JOOKI_PLAIN_LEN] = {0}; + int res = jooki_decode(b64, result); + if (res != PM3_SUCCESS) { + return res; + } + + jooki_print(b64, result, false); + + // copy UID from base64 url parameter + uint8_t uid[7] = {0}; + memcpy(uid, result + 5, 7); + + // hf mfu sim... + uint8_t *data = calloc(144, sizeof(uint8_t)); + + memcpy(data, uid, 3); + memcpy(data + (1 * 4), uid + 3, 4); + + // bbc0 + data[3] = 0x88 ^ data[0] ^ data[1] ^ data[2]; + + // bbc1 + data[8] = data[4] ^ data[5] ^ data[6] ^ data[7]; + + // copy NDEF magic firs, skip BBC1 + memcpy(data + (2 * 4) + 1, "\x48\x00\x00\xE1\x10\x12\x00", 7); + + // copy raw NDEF + jooki_create_ndef(b64, data + (4 * 4)); + + // convert plain or old mfu format to new format + size_t datalen = 144; + res = convert_mfu_dump_format(&data, &datalen, true); + if (res != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Failed convert on load to new Ultralight/NTAG format"); + free(data); + return res; + } + + mfu_dump_t *mfu_dump = (mfu_dump_t *)data; + memcpy(mfu_dump->version, "\x00\x04\x04\x02\x01\x00\x0F\x03", 8); + mfu_dump->counter_tearing[2][3] = 0xBD; + mfu_dump->pages = 0x2c; + + printMFUdumpEx(mfu_dump, mfu_dump->pages + 1, 0); + + // upload to emulator memory + PrintAndLogEx(INFO, "Uploading to emulator memory"); + + PrintAndLogEx(INFO, "." NOLF); + // fast push mode + conn.block_after_ACK = true; + uint8_t blockwidth = 4, counter = 0, blockno = 0; + while (datalen) { + if (datalen == blockwidth) { + // Disable fast mode on last packet + conn.block_after_ACK = false; + } + + if (mfEmlSetMem_xt(data + counter, blockno, 1, blockwidth) != PM3_SUCCESS) { + PrintAndLogEx(FAILED, "Cant set emul block: %3d", blockno); + free(data); + return PM3_ESOFT; + } + PrintAndLogEx(NORMAL, "." NOLF); + fflush(stdout); + blockno++; + counter += blockwidth; + datalen -= blockwidth; + } + PrintAndLogEx(NORMAL, "\n"); + + struct { + uint8_t tagtype; + uint8_t flags; + uint8_t uid[10]; + uint8_t exitAfter; + } PACKED payload; + + // NTAG, 7 byte UID in eloaded data. + payload.tagtype = 7; + payload.flags = FLAG_UID_IN_EMUL; + payload.exitAfter = 0; + memcpy(payload.uid, uid, sizeof(uid)); + + clearCommandBuffer(); + SendCommandNG(CMD_HF_ISO14443A_SIMULATE, (uint8_t *)&payload, sizeof(payload)); + PacketResponseNG resp; + + PrintAndLogEx(INFO, "Press " _GREEN_("") " or pm3-button to abort simulation"); + for (;;) { + if (kbd_enter_pressed()) { + SendCommandNG(CMD_BREAK_LOOP, NULL, 0); + PrintAndLogEx(DEBUG, "User aborted"); + break; + } + + if (WaitForResponseTimeout(CMD_HF_MIFARE_SIMULATE, &resp, 1500) == 0) + continue; + + if (resp.status != PM3_SUCCESS) + break; + } + free(data); + PrintAndLogEx(INFO, "Done"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf 14a list") "` to view trace log"); + return PM3_SUCCESS; +} + +static int CmdHF14AJookiClone(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf jooki clone", + "Write a Jooki token to a Ultralight or NTAG tag", + "hf jooki clone -d --> where hex is raw NDEF\n" + "hf jooki clone --b64 7WzlgEzqLgwTnWNy --> using base64 url parameter" + ); + + void *argtable[] = { + arg_param_begin, + arg_str0("b", "b64", "", "base64 url parameter"), + arg_str0("d", "data", "", "raw NDEF bytes"), + arg_str0("p", "pwd", "", "password for authentication (EV1/NTAG 4 bytes)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + int blen = 16; + uint8_t b64[JOOKI_B64_LEN] = {0x00}; + memset(b64, 0x0, sizeof(b64)); + CLIGetStrWithReturn(ctx, 1, b64, &blen); + + int dlen = 0; + uint8_t data[52] = {0x00}; + memset(data, 0x0, sizeof(data)); + int res = CLIParamHexToBuf(arg_get_str(ctx, 2), data, sizeof(data), &dlen); + if (res) { + CLIParserFree(ctx); + PrintAndLogEx(FAILED, "Error parsing bytes"); + return PM3_EINVARG; + } + + int plen = 0; + uint8_t pwd[4] = {0x00}; + CLIGetHexWithReturn(ctx, 3, pwd, &plen); + CLIParserFree(ctx); + + if (dlen != 52) { + PrintAndLogEx(ERR, "Wrong data length. Expected 52 got %d", dlen); + return PM3_EINVARG; + } + + bool has_pwd = false; + if (plen == 4) { + has_pwd = true; + } + + // 0 - no authentication + // 2 - pwd (4 bytes) + uint8_t keytype = 0, blockno = 4, i = 0; + + while ((i * 4) < dlen) { + + uint8_t cmddata[8] = {0}; + memcpy(cmddata, data + (i * 4), 4); + if (has_pwd) { + memcpy(cmddata + 4, pwd, 4); + keytype = 2; + } + clearCommandBuffer(); + SendCommandMIX(CMD_HF_MIFAREU_WRITEBL, blockno, keytype, 0, cmddata, sizeof(cmddata)); + + PacketResponseNG resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + uint8_t isOK = resp.oldarg[0] & 0xff; + PrintAndLogEx(SUCCESS, "Write block %d ( %s )", blockno, isOK ? _GREEN_("ok") : _RED_("fail")); + } else { + PrintAndLogEx(WARNING, "Command execute timeout"); + } + + blockno++; + i++; + } + + PrintAndLogEx(INFO, "Done"); + PrintAndLogEx(HINT, "Try `" _YELLOW_("hf mfu ndef") "` to view"); + return PM3_SUCCESS; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, AlwaysAvailable, "This help"}, + {"clone", CmdHF14AJookiClone, IfPm3Iso14443a, "Write a Jooki token"}, + {"decode", CmdHF14AJookiDecode, AlwaysAvailable, "Decode Jooki token"}, + {"encode", CmdHF14AJookiEncode, AlwaysAvailable, "Encode Jooki token"}, + {"sim", CmdHF14AJookiSim, IfPm3Iso14443a, "Simulate Jooki token"}, + {NULL, NULL, NULL, NULL} +}; + +static int CmdHelp(const char *Cmd) { + (void)Cmd; // Cmd is not used so far + CmdsHelp(CommandTable); + return PM3_SUCCESS; +} + +int CmdHF_Jooki(const char *Cmd) { + clearCommandBuffer(); + return CmdsParse(CommandTable, Cmd); +} diff --git a/client/src/cmdhfjooki.h b/client/src/cmdhfjooki.h new file mode 100644 index 000000000..ac3ecc3af --- /dev/null +++ b/client/src/cmdhfjooki.h @@ -0,0 +1,16 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2021 iceman1001 +// +// 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. +//----------------------------------------------------------------------------- +// High frequency ISO14443A / MFU / Jooki commands +//----------------------------------------------------------------------------- + +#ifndef CMDHFJOOKI_H__ +#define CMDHFJOOKI_H__ + +#include "common.h" +int CmdHF_Jooki(const char *Cmd); +#endif diff --git a/client/src/cmdhflegic.c b/client/src/cmdhflegic.c index 39a542cc9..709cf9390 100644 --- a/client/src/cmdhflegic.c +++ b/client/src/cmdhflegic.c @@ -16,6 +16,7 @@ #include #endif +#include "cliparser.h" #include "cmdparser.h" // command_t #include "comms.h" // clearCommandBuffer #include "cmdtrace.h" @@ -27,154 +28,6 @@ static int CmdHelp(const char *Cmd); #define MAX_LENGTH 1024 -static int usage_legic_calccrc(void) { - PrintAndLogEx(NORMAL, "Calculates the legic crc8/crc16 on the given data."); - PrintAndLogEx(NORMAL, "There must be an even number of hexsymbols as input.\n"); - PrintAndLogEx(NORMAL, "Usage: hf legic crc [h] d u c <8|16>\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " d : (hex symbols) bytes to calculate crc over"); - PrintAndLogEx(NORMAL, " u : MCC hexbyte"); - PrintAndLogEx(NORMAL, " c <8|16> : Crc type"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic crc d deadbeef1122")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic crc d deadbeef1122 u 9A c 16")); - return PM3_SUCCESS; -} -static int usage_legic_rdbl(void) { - PrintAndLogEx(NORMAL, "Read data from a LEGIC Prime tag\n"); - PrintAndLogEx(NORMAL, "Usage: hf legic rdbl [h] [o ] [l ] [iv ]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " o : (hex) offset in data array to start download from"); - PrintAndLogEx(NORMAL, " l : (hex) number of bytes to read"); - PrintAndLogEx(NORMAL, " i : (hex) (optional) Initialization vector to use. Must be odd and 7bits max"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic rdbl o 0 l 16 - reads from byte[0] 0x16 bytes(system header)")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic rdbl o 0 l 4 iv 55 - reads from byte[0] 0x4 bytes with IV 0x55")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic rdbl o 0 l 100 iv 55 - reads 0x100 bytes with IV 0x55")); - return PM3_SUCCESS; -} -static int usage_legic_sim(void) { - PrintAndLogEx(NORMAL, "Simulates a LEGIC Prime tag. MIM22, MIM256, MIM1024 types can be emulated"); - PrintAndLogEx(NORMAL, "Use " _YELLOW_("`hf legic eload`") " to upload a dump into emulator memory\n"); - PrintAndLogEx(NORMAL, "Usage: hf legic sim [h] \n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " : 0 = MIM22"); - PrintAndLogEx(NORMAL, " : 1 = MIM256 (default)"); - PrintAndLogEx(NORMAL, " : 2 = MIM1024"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic sim 2")); - return PM3_SUCCESS; -} -static int usage_legic_wrbl(void) { - PrintAndLogEx(NORMAL, "Write data to a LEGIC Prime tag. It autodetects tagsize to make sure size\n"); - PrintAndLogEx(NORMAL, "Usage: hf legic wrbl [h] [o ] [d ] [y]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " o : (hex) offset in data array to start writing"); - //PrintAndLogEx(NORMAL, " : (optional) Initialization vector to use (ODD and 7bits)"); - PrintAndLogEx(NORMAL, " d : (hex symbols) bytes to write "); - PrintAndLogEx(NORMAL, " y : Auto-confirm dangerous operations "); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic wrbl o 10 d 11223344 - Write 0x11223344 starting from offset 0x10")); - return PM3_SUCCESS; -} -static int usage_legic_reader(void) { - PrintAndLogEx(NORMAL, "Read UID and type information from a LEGIC Prime tag\n"); - PrintAndLogEx(NORMAL, "Usage: hf legic reader [h]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic reader")); - return PM3_SUCCESS; -} -static int usage_legic_info(void) { - PrintAndLogEx(NORMAL, "Reads information from a LEGIC Prime tag like systemarea, user areas etc\n"); - PrintAndLogEx(NORMAL, "Usage: hf legic info [h]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic info")); - return PM3_SUCCESS; -} -static int usage_legic_dump(void) { - PrintAndLogEx(NORMAL, "Read all memory from LEGIC Prime MIM22, MIM256, MIM1024"); - PrintAndLogEx(NORMAL, "and saves bin/eml/json dump file"); - PrintAndLogEx(NORMAL, "It autodetects card type.\n"); - PrintAndLogEx(NORMAL, "Usage: hf legic dump [h] [x] [f ]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " f : filename w/o '.bin' to dump bytes"); - PrintAndLogEx(NORMAL, " x : deobfuscate dump data (xor with MCC)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic dump - uses UID as filename")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic dump f myfile")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic dump x")); - return PM3_SUCCESS; -} -static int usage_legic_restore(void) { - PrintAndLogEx(NORMAL, "Reads binary file and it autodetects card type and verifies that the file has the same size"); - PrintAndLogEx(NORMAL, "Then write the data back to card. All bytes except the first 7bytes [UID(4) MCC(1) DCF(2)]\n"); - PrintAndLogEx(NORMAL, "Usage: hf legic restore [h] [x] [f ]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " f : filename w/o '.bin' to restore bytes on to card from"); - PrintAndLogEx(NORMAL, " x : obfuscate dump data (xor with MCC)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic restore f myfile")); - return PM3_SUCCESS; -} -static int usage_legic_eload(void) { - PrintAndLogEx(NORMAL, "It loads a binary dump into emulator memory\n"); - PrintAndLogEx(NORMAL, "Usage: hf legic eload [h] [card memory] [f ]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " [card memory] : 0 = MIM22"); - PrintAndLogEx(NORMAL, " : 1 = MIM256 (default)"); - PrintAndLogEx(NORMAL, " : 2 = MIM1024"); - PrintAndLogEx(NORMAL, " f : filename w/o .bin to load"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic eload 2 f myfile")); - return PM3_SUCCESS; -} -static int usage_legic_esave(void) { - PrintAndLogEx(NORMAL, "It saves bin/eml/json dump file of emulator memory\n"); - PrintAndLogEx(NORMAL, "Usage: hf legic esave [h] [card memory] f \n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " [card memory] : 0 = MIM22"); - PrintAndLogEx(NORMAL, " : 1 = MIM256 (default)"); - PrintAndLogEx(NORMAL, " : 2 = MIM1024"); - PrintAndLogEx(NORMAL, " f : filename w/o .bin to load"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic esave 2 - uses UID as filename")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic esave 2 f myfile")); - return PM3_SUCCESS; -} -static int usage_legic_wipe(void) { - PrintAndLogEx(NORMAL, "Fills a LEGIC Prime tags memory with zeros. From byte7 and to the end"); - PrintAndLogEx(NORMAL, "It autodetects card type\n"); - PrintAndLogEx(NORMAL, "Usage: hf legic wipe [h]\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf legic wipe")); - return PM3_SUCCESS; -} - static bool legic_xor(uint8_t *data, uint16_t cardsize) { if (cardsize <= 22) { @@ -203,9 +56,17 @@ static bool legic_xor(uint8_t *data, uint16_t cardsize) { * by Henryk Ploetz and Karsten Nohl at 26c3 */ static int CmdLegicInfo(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf legic info", + "Gets information from a LEGIC Prime tag like systemarea, user areas, etc", + "hf legic info"); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_legic_info(); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); int i = 0, k = 0, segmentNum = 0, segment_len = 0, segment_flag = 0; int crc = 0, wrp = 0, wrc = 0; @@ -526,34 +387,32 @@ out: // offset in data memory // number of bytes to read static int CmdLegicRdbl(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf legic rdbl", + "Read data from a LEGIC Prime tag", + "hf legic rdbl -o 0 -l 16 <- reads from byte[0] 16 bytes(system header)\n" + "hf legic rdbl -o 0 -l 4 --iv 55 <- reads from byte[0] 4 bytes with IV 0x55\n" + "hf legic rdbl -o 0 -l 256 --iv 55 <- reads from byte[0] 256 bytes with IV 0x55"); - uint32_t offset = 0, len = 0, iv = 1; - bool errors = false; - uint8_t cmdp = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h' : - return usage_legic_rdbl(); - case 'o' : - offset = param_get32ex(Cmd, cmdp + 1, 0, 16); - cmdp += 2; - break; - case 'l' : - len = param_get32ex(Cmd, cmdp + 1, 0, 16); - cmdp += 2; - break; - case 'i' : - iv = param_get32ex(Cmd, cmdp + 1, 1, 16); - cmdp += 2; - break; - default : - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - //Validations - if (errors || strlen(Cmd) == 0) return usage_legic_rdbl(); + void *argtable[] = { + arg_param_begin, + arg_int1("o", "offset", "", "offset in data array to start download from"), + arg_int1("l", "length", "", "number of bytes to read"), + arg_str0(NULL, "iv", "", "Initialization vector to use. Must be odd and 7bits max"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int offset = arg_get_int_def(ctx, 1, 0); + + int len = arg_get_int_def(ctx, 2, 0); + + int iv_len = 0; + uint8_t iv[1] = {0x01}; // formerly uidcrc + + CLIGetHexWithReturn(ctx, 3, iv, &iv_len); + + CLIParserFree(ctx); // sanity checks if (len + offset >= MAX_LENGTH) { @@ -571,9 +430,9 @@ static int CmdLegicRdbl(const char *Cmd) { } uint16_t datalen = 0; - int status = legic_read_mem(offset, len, iv, data, &datalen); + int status = legic_read_mem(offset, len, iv[0], data, &datalen); if (status == PM3_SUCCESS) { - PrintAndLogEx(NORMAL, "\n ## | 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F"); + PrintAndLogEx(NORMAL, " ## | 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F"); PrintAndLogEx(NORMAL, "-----+------------------------------------------------------------------------------------------------"); print_hex_break(data, datalen, 32); } @@ -582,9 +441,19 @@ static int CmdLegicRdbl(const char *Cmd) { } static int CmdLegicSim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf legic sim", + "Simulates a LEGIC Prime tag. MIM22, MIM256, MIM1024 types can be emulated", + "hf legic sim -t 0 <- Simulate Type MIM22\n" + "hf legic sim -t 1 <- Simulate Type MIM256 (default)\n" + "hf legic sim -t 2 <- Simulate Type MIM1024"); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_legic_sim(); + void *argtable[] = { + arg_param_begin, + arg_int0("t", "type", "", "Tag type to simulate."), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); struct { uint8_t tagtype; @@ -592,9 +461,13 @@ static int CmdLegicSim(const char *Cmd) { } PACKED payload; payload.send_reply = true; - payload.tagtype = param_get8ex(Cmd, 0, 1, 10); + payload.tagtype = arg_get_int_def(ctx, 1, 1); + + CLIParserFree(ctx); + if (payload.tagtype > 2) { - return usage_legic_sim(); + PrintAndLogEx(ERR, "Invalid tag type selected."); + return PM3_EINVARG; } clearCommandBuffer(); @@ -619,94 +492,37 @@ static int CmdLegicSim(const char *Cmd) { } static int CmdLegicWrbl(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf legic wrbl", + "Write data to a LEGIC Prime tag. It autodetects tagsize to ensure proper write", + "hf legic wrbl -o 0 -d 11223344 <- Write 0x11223344 starting from offset 0)\n" + "hf legic wrbl -o 10 -d DEADBEEF <- Write 0xdeadbeef starting from offset 10"); - uint8_t *data = NULL; - uint8_t cmdp = 0; - bool errors = false; - bool autoconfirm = false; - int len = 0, bg, en; - uint32_t offset = 0, IV = 0x55; + void *argtable[] = { + arg_param_begin, + arg_int1("o", "offset", "", "offset in data array to start writing"), + arg_str1("d", "data", "", "data to write"), + arg_lit0(NULL, "danger", "Auto-confirm dangerous operations"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': { - errors = true; - break; - } - case 'd': { - // peek at length of the input string so we can - // figure out how many elements to malloc in "data" - bg = en = 0; - if (param_getptr(Cmd, &bg, &en, cmdp + 1)) { - errors = true; - break; - } - len = (en - bg + 1); + int offset = arg_get_int_def(ctx, 1, 0); - // check that user entered even number of characters - // for hex data string - if (len & 1) { - errors = true; - break; - } + int data_len = 0; + uint8_t data[MAX_LENGTH] = {0}; - // limit number of bytes to write. This is not a 'restore' command. - if ((len >> 1) > 100) { - PrintAndLogEx(WARNING, "Max bound on 100bytes to write a one time."); - PrintAndLogEx(WARNING, "Use the 'hf legic restore' command if you want to write the whole tag at once"); - errors = true; - } + CLIGetHexWithReturn(ctx, 2, data, &data_len); - // it's possible for user to accidentally enter "b" parameter - // more than once - we have to clean previous malloc - if (data) - free(data); + bool autoconfirm = arg_get_lit(ctx, 3); - data = calloc(len >> 1, sizeof(uint8_t)); - if (data == NULL) { - PrintAndLogEx(WARNING, "Can't allocate memory. exiting"); - errors = true; - break; - } + CLIParserFree(ctx); - if (param_gethex(Cmd, cmdp + 1, data, len)) { - errors = true; - break; - } - - len >>= 1; - cmdp += 2; - break; - } - case 'o': { - offset = param_get32ex(Cmd, cmdp + 1, 4, 16); - cmdp += 2; - break; - } - case 'y': { - autoconfirm = true; - break; - } - default: { - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - } - - //Validations - if (errors || cmdp == 0) { - if (data) - free(data); - return usage_legic_wrbl(); - } + uint32_t IV = 0x55; // OUT-OF-BOUNDS checks // UID 4+1 bytes can't be written to. if (offset < 5) { - if (data) - free(data); PrintAndLogEx(WARNING, "Out-of-bounds, bytes 0-1-2-3-4 can't be written to. Offset = %d", offset); return PM3_EOUTOFBOUND; } @@ -720,8 +536,8 @@ static int CmdLegicWrbl(const char *Cmd) { legic_print_type(card.cardsize, 0); - if (len + offset > card.cardsize) { - PrintAndLogEx(WARNING, "Out-of-bounds, Cardsize = %d, [offset+len = %d ]", card.cardsize, len + offset); + if (data_len + offset > card.cardsize) { + PrintAndLogEx(WARNING, "Out-of-bounds, Cardsize = %d, [offset+len = %d ]", card.cardsize, data_len + offset); return PM3_EOUTOFBOUND; } @@ -756,7 +572,7 @@ static int CmdLegicWrbl(const char *Cmd) { PacketResponseNG resp; clearCommandBuffer(); - SendCommandOLD(CMD_HF_LEGIC_WRITER, offset, len, IV, data, len); + SendCommandOLD(CMD_HF_LEGIC_WRITER, offset, data_len, IV, data, data_len); uint8_t timeout = 0; while (!WaitForResponseTimeout(CMD_ACK, &resp, 2000)) { @@ -779,84 +595,45 @@ static int CmdLegicWrbl(const char *Cmd) { } static int CmdLegicCalcCrc(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf legic crc", + "Calculates the legic crc8/crc16 on the given data", + "hf legic crc -d deadbeef1122\n" + "hf legic crc -d deadbeef1122 --mcc 9A -t 16 <- CRC Type 16"); - uint8_t *data = NULL; - uint8_t cmdp = 0, uidcrc = 0, type = 0; - bool errors = false; - int len = 0; - int bg, en; + void *argtable[] = { + arg_param_begin, + arg_str1("d", "data", "", "bytes to calculate crc over"), + arg_str0(NULL, "mcc", "", "MCC hex byte (UID CRC)"), + arg_int0("t", "type", "", "CRC Type (default: 8)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'd': - // peek at length of the input string so we can - // figure out how many elements to malloc in "data" - bg = en = 0; - if (param_getptr(Cmd, &bg, &en, cmdp + 1)) { - errors = true; - break; - } - len = (en - bg + 1); + int data_len = 0; + uint8_t data[4096] = {0}; - // check that user entered even number of characters - // for hex data string - if (len & 1) { - errors = true; - break; - } + CLIGetHexWithReturn(ctx, 1, data, &data_len); - // it's possible for user to accidentally enter "b" parameter - // more than once - we have to clean previous malloc - if (data) free(data); - data = calloc(len >> 1, sizeof(uint8_t)); - if (data == NULL) { - PrintAndLogEx(WARNING, "Can't allocate memory. exiting"); - errors = true; - break; - } + int mcc_len = 0; + uint8_t mcc[1] = {0}; // formerly uidcrc - if (param_gethex(Cmd, cmdp + 1, data, len)) { - errors = true; - break; - } + CLIGetHexWithReturn(ctx, 2, mcc, &mcc_len); - len >>= 1; - cmdp += 2; - break; - case 'u': - uidcrc = param_get8ex(Cmd, cmdp + 1, 0, 16); - cmdp += 2; - break; - case 'c': - type = param_get8ex(Cmd, cmdp + 1, 0, 10); - cmdp += 2; - break; - case 'h': - errors = true; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - //Validations - if (errors || cmdp == 0) { - if (data) free(data); - return usage_legic_calccrc(); - } + int type = arg_get_int_def(ctx, 3, 0); + + CLIParserFree(ctx); switch (type) { case 16: init_table(CRC_LEGIC); - PrintAndLogEx(SUCCESS, "Legic crc16: %X", crc16_legic(data, len, uidcrc)); + PrintAndLogEx(SUCCESS, "Legic crc16: %X", crc16_legic(data, data_len, mcc[0])); break; default: - PrintAndLogEx(SUCCESS, "Legic crc8: %X", CRC8Legic(data, len)); + PrintAndLogEx(SUCCESS, "Legic crc8: %X", CRC8Legic(data, data_len)); break; } - if (data) free(data); return PM3_SUCCESS; } @@ -954,47 +731,52 @@ void legic_seteml(uint8_t *src, uint32_t offset, uint32_t numofbytes) { SendCommandOLD(CMD_HF_LEGIC_ESET, i, len, 0, src + i, len); } } - static int CmdLegicReader(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_legic_reader(); + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf legic reader", + "Read UID and type information from a LEGIC Prime tag", + "hf legic reader"); - return readLegicUid(true); + void *argtable[] = { + arg_param_begin, + arg_lit0("@", NULL, "optional - continuous reader mode"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + bool cm = arg_get_lit(ctx, 1); + CLIParserFree(ctx); + + if (cm) { + PrintAndLogEx(INFO, "Press " _GREEN_("") " to exit"); + } + + return readLegicUid(cm, true); } static int CmdLegicDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf legic dump", + "Read all memory from LEGIC Prime MIM22, MIM256, MIM1024 and saves bin/eml/json dump file\n" + "It autodetects card type.", + "hf legic dump <-- use UID as filename\n" + "hf legic dump -f myfile <-- use user specified filename\n" + "hf legic dump --deobfuscate <-- use UID as filename and deobfuscate data"); - int fileNameLen = 0; - char filename[FILE_PATH_SIZE] = {0x00}; - char *fptr = filename; - bool errors = false, shall_deobsfuscate = false; - uint16_t dumplen; - uint8_t cmdp = 0; + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "specify a filename for dump file"), + arg_lit0(NULL, "deobfuscate", "deobfuscate dump data (xor with MCC)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_legic_dump(); - case 'f': - fileNameLen = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); - if (!fileNameLen) - errors = true; - if (fileNameLen > FILE_PATH_SIZE - 5) - fileNameLen = FILE_PATH_SIZE - 5; - cmdp += 2; - break; - case 'x': - shall_deobsfuscate = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - //Validations - if (errors) return usage_legic_dump(); + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + bool shall_deobsfuscate = arg_get_lit(ctx, 2); + + CLIParserFree(ctx); // tagtype legic_card_select_t card; @@ -1002,7 +784,7 @@ static int CmdLegicDump(const char *Cmd) { PrintAndLogEx(WARNING, "Failed to identify tagtype"); return PM3_ESOFT; } - dumplen = card.cardsize; + uint16_t dumplen = card.cardsize; legic_print_type(dumplen, 0); PrintAndLogEx(SUCCESS, "Reading tag memory %d b...", dumplen); @@ -1046,10 +828,10 @@ static int CmdLegicDump(const char *Cmd) { } // user supplied filename? - if (fileNameLen < 1) { + if (fnlen < 1) { PrintAndLogEx(INFO, "Using UID as filename"); - fptr += snprintf(fptr, sizeof(filename), "hf-legic-"); - FillFileNameByUID(fptr, data, "-dump", 4); + strcat(filename, "hf-legic-"); + FillFileNameByUID(filename, data, "-dump", 4); } if (shall_deobsfuscate) { @@ -1066,44 +848,28 @@ static int CmdLegicDump(const char *Cmd) { } static int CmdLegicRestore(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf legic restore", + "Reads binary file and it autodetects card type and verifies that the file has the same size\n" + "Then write the data back to card. All bytes except the first 7bytes [UID(4) MCC(1) DCF(2)]", + "hf legic restore -f myfile <-- use user specified filename\n" + "hf legic restore -f myfile --obfuscate <-- use UID as filename and deobfuscate data"); - char filename[FILE_PATH_SIZE] = {0x00}; - bool errors = false, shall_obsfuscate = false, have_filename = false; - size_t numofbytes; - uint8_t cmdp = 0; + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "specify a filename to restore"), + arg_lit0(NULL, "obfuscate", "obfuscate dump data (xor with MCC)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': { - errors = true; - break; - } - case 'f': { - if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { - PrintAndLogEx(FAILED, "Filename too long"); - break; - } - have_filename = true; - cmdp += 2; - break; - } - case 'x': { - shall_obsfuscate = true; - cmdp++; - break; - } - default: { - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - } - if (have_filename == false) - errors = true; + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - //Validations - if (errors || cmdp == 0) return usage_legic_restore(); + bool shall_obsfuscate = arg_get_lit(ctx, 2); + + CLIParserFree(ctx); // tagtype legic_card_select_t card; @@ -1121,6 +887,7 @@ static int CmdLegicRestore(const char *Cmd) { return PM3_EMALLOC; } + size_t numofbytes; if (loadFile_safe(filename, ".bin", (void **)&data, &numofbytes) != PM3_SUCCESS) { free(data); PrintAndLogEx(WARNING, "Error, reading file"); @@ -1181,58 +948,47 @@ static int CmdLegicRestore(const char *Cmd) { } static int CmdLegicELoad(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf legic eload", + "Loads a LEGIC binary dump into emulator memory", + "hf legic eload -f myfile -t 0 <- Simulate Type MIM22\n" + "hf legic eload -f myfile -t 1 <- Simulate Type MIM256 (default)\n" + "hf legic eload -f myfile -t 2 <- Simulate Type MIM1024"); - size_t numofbytes = 256; - char filename[FILE_PATH_SIZE] = {0x00}; - bool errors = false, shall_obsfuscate = false, have_filename = false; - uint8_t cmdp = 0; + void *argtable[] = { + arg_param_begin, + arg_str1("f", "file", "", "Specify a filename to restore"), + arg_int0("t", "type", "", "Tag type to simulate."), + arg_lit0(NULL, "obfuscate", "Obfuscate dump data (xor with MCC)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h' : { - return usage_legic_eload(); - } - case 'f' : { - if (param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { - PrintAndLogEx(FAILED, "Filename too long"); - break; - } - have_filename = true; - cmdp += 2; - break; - } - case 'x': { - shall_obsfuscate = true; - cmdp++; - break; - } - case '0' : { - numofbytes = 22; - cmdp++; - break; - } - case '1' : { - numofbytes = 256; - cmdp++; - break; - } - case '2' : { - numofbytes = 1024; - cmdp++; - break; - } - default : { - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + size_t numofbytes = 0; + + switch (arg_get_int_def(ctx, 2, 1)) { + case 0: + numofbytes = 22; + break; + case 1: + numofbytes = 256; + break; + case 2: + numofbytes = 1024; + break; + default: + PrintAndLogEx(ERR, "Unknown tag type"); + CLIParserFree(ctx); + return PM3_EINVARG; } - if (have_filename == false) - errors = true; - //Validations - if (errors || strlen(Cmd) == 0) return usage_legic_eload(); + bool shall_obsfuscate = arg_get_lit(ctx, 3); + + CLIParserFree(ctx); // set up buffer uint8_t *data = calloc(numofbytes, sizeof(uint8_t)); @@ -1260,49 +1016,48 @@ static int CmdLegicELoad(const char *Cmd) { } static int CmdLegicESave(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf legic esave", + "Saves bin/eml/json dump file of emulator memory", + "hf legic esave <- uses UID as filename\n" + "hf legic esave -f myfile -t 0 <- Type MIM22\n" + "hf legic esave -f myfile -t 1 <- Type MIM256 (default)\n" + "hf legic esave -f myfile -t 2 <- Type MIM1024"); + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "Specify a filename to save"), + arg_int0("t", "type", "", "Tag type"), + arg_lit0(NULL, "deobfuscate", "De-obfuscate dump data (xor with MCC)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - char *fptr = filename; - int fileNameLen = 0; - size_t numofbytes = 256; - bool errors = false, shall_deobsfuscate = false; - uint8_t cmdp = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h' : - return usage_legic_esave(); - case 'f' : - fileNameLen = param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); - if (!fileNameLen) - errors = true; - if (fileNameLen > FILE_PATH_SIZE - 5) - fileNameLen = FILE_PATH_SIZE - 5; - cmdp += 2; - break; - case 'x': - shall_deobsfuscate = true; - cmdp++; - break; - case '0' : - numofbytes = 22; - cmdp++; - break; - case '1' : - numofbytes = 256; - cmdp++; - break; - case '2' : - numofbytes = 1024; - cmdp++; - break; - default : - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + size_t numofbytes = 0; + + switch (arg_get_int_def(ctx, 2, 1)) { + case 0: + numofbytes = 22; + break; + case 1: + numofbytes = 256; + break; + case 2: + numofbytes = 1024; + break; + default: + PrintAndLogEx(ERR, "Unknown tag type"); + CLIParserFree(ctx); + return PM3_EINVARG; } - //Validations - if (errors || strlen(Cmd) == 0) return usage_legic_esave(); + + bool shall_deobsfuscate = arg_get_lit(ctx, 3); + + CLIParserFree(ctx); // set up buffer uint8_t *data = calloc(numofbytes, sizeof(uint8_t)); @@ -1320,10 +1075,10 @@ static int CmdLegicESave(const char *Cmd) { } // user supplied filename? - if (fileNameLen < 1) { + if (fnlen < 1) { PrintAndLogEx(INFO, "Using UID as filename"); - fptr += snprintf(fptr, sizeof(filename), "hf-legic-"); - FillFileNameByUID(fptr, data, "-dump", 4); + strcat(filename, "hf-legic-"); + FillFileNameByUID(filename, data, "-dump", 4); } if (shall_deobsfuscate) { @@ -1337,10 +1092,18 @@ static int CmdLegicESave(const char *Cmd) { } static int CmdLegicWipe(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf legic wipe", + "Fills a LEGIC Prime tags memory with zeros. From byte7 and to the end\n" + "It autodetects card type", + "hf legic wipe"); - char cmdp = tolower(param_getchar(Cmd, 0)); - - if (cmdp == 'h') return usage_legic_wipe(); + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); // tagtype legic_card_select_t card; @@ -1438,24 +1201,38 @@ int CmdHFLegic(const char *Cmd) { return CmdsParse(CommandTable, Cmd); } -int readLegicUid(bool verbose) { +int readLegicUid(bool loop, bool verbose) { + + do { + legic_card_select_t card; + + int resp = legic_get_type(&card); + + if (loop) { + if (resp != PM3_SUCCESS) { + continue; + } + } else { + switch (resp) { + case PM3_EINVARG: + return PM3_EINVARG; + case PM3_ETIMEOUT: + if (verbose) PrintAndLogEx(WARNING, "command execution time out"); + return PM3_ETIMEOUT; + case PM3_ESOFT: + if (verbose) PrintAndLogEx(WARNING, "legic card select failed"); + return PM3_ESOFT; + default: + break; + } + } + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(SUCCESS, " MCD: " _GREEN_("%02X"), card.uid[0]); + PrintAndLogEx(SUCCESS, " MSN: " _GREEN_("%s"), sprint_hex(card.uid + 1, sizeof(card.uid) - 1)); + legic_print_type(card.cardsize, 0); + + } while (loop && kbd_enter_pressed() == false); - legic_card_select_t card; - switch (legic_get_type(&card)) { - case PM3_EINVARG: - return PM3_EINVARG; - case PM3_ETIMEOUT: - if (verbose) PrintAndLogEx(WARNING, "command execution time out"); - return PM3_ETIMEOUT; - case PM3_ESOFT: - if (verbose) PrintAndLogEx(WARNING, "legic card select failed"); - return PM3_ESOFT; - default: - break; - } - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(SUCCESS, " MCD: " _GREEN_("%02X"), card.uid[0]); - PrintAndLogEx(SUCCESS, " MSN: " _GREEN_("%s"), sprint_hex(card.uid + 1, sizeof(card.uid) - 1)); - legic_print_type(card.cardsize, 0); return PM3_SUCCESS; } diff --git a/client/src/cmdhflegic.h b/client/src/cmdhflegic.h index 9e9411a33..379ae13c7 100644 --- a/client/src/cmdhflegic.h +++ b/client/src/cmdhflegic.h @@ -17,7 +17,7 @@ int CmdHFLegic(const char *Cmd); -int readLegicUid(bool verbose); +int readLegicUid(bool loop, bool verbose); int legic_print_type(uint32_t tagtype, uint8_t spaces); int legic_get_type(legic_card_select_t *card); void legic_chk_iv(uint32_t *iv); diff --git a/client/src/cmdhflist.c b/client/src/cmdhflist.c index 945edda06..6e292ad6f 100644 --- a/client/src/cmdhflist.c +++ b/client/src/cmdhflist.c @@ -17,7 +17,6 @@ #include "commonutil.h" // ARRAYLEN #include "mifare/mifarehost.h" -#include "mifare/mifaredefault.h" #include "parity.h" // oddparity #include "ui.h" #include "crc16.h" @@ -59,10 +58,11 @@ void ClearAuthData(void) { uint8_t iso14443A_CRC_check(bool isResponse, uint8_t *d, uint8_t n) { if (n < 3) return 2; if (isResponse && (n < 6)) return 2; - if (n > 2 && d[1] == 0x50 && - d[0] >= ISO14443A_CMD_ANTICOLL_OR_SELECT && - d[0] <= ISO14443A_CMD_ANTICOLL_OR_SELECT_3) + if (d[1] == 0x50 && + d[0] >= ISO14443A_CMD_ANTICOLL_OR_SELECT && + d[0] <= ISO14443A_CMD_ANTICOLL_OR_SELECT_3) { return 2; + } return check_crc(CRC_14443_A, d, n); } @@ -311,7 +311,7 @@ int applyIso14443a(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize) { case MIFARE_ULNANO_WRITESIG: snprintf(exp, size, "WRITE SIG"); break; - case MIFARE_ULNANO_LOCKSIF: { + case MIFARE_ULNANO_LOCKSIG: { if (cmd[1] == 0) snprintf(exp, size, "UNLOCK SIG"); else if (cmd[1] == 2) @@ -1335,7 +1335,7 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, uint8 } -bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen) { +bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen, const uint64_t *dicKeys, uint32_t dicKeysCount) { static struct Crypto1State *traceCrypto1; *mfDataLen = 0; @@ -1383,12 +1383,12 @@ bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isRes } // check default keys - if (!traceCrypto1) { - for (int i = 0; i < ARRAYLEN(g_mifare_default_keys); i++) { - if (NestedCheckKey(g_mifare_default_keys[i], &AuthData, cmd, cmdsize, parity)) { - PrintAndLogEx(NORMAL, " | | * |%61s " _GREEN_("%012" PRIX64) "| |", "key", g_mifare_default_keys[i]); + if (!traceCrypto1 && dicKeys != NULL && dicKeysCount > 0) { + for (int i = 0; i < dicKeysCount; i++) { + if (NestedCheckKey(dicKeys[i], &AuthData, cmd, cmdsize, parity)) { + PrintAndLogEx(NORMAL, " | | * |%60s " _GREEN_("%012" PRIX64) "| |", "key", dicKeys[i]); - mfLastKey = g_mifare_default_keys[i]; + mfLastKey = dicKeys[i]; traceCrypto1 = lfsr_recovery64(AuthData.ks2, AuthData.ks3); break; }; diff --git a/client/src/cmdhflist.h b/client/src/cmdhflist.h index f12f2a61d..ff7f7f799 100644 --- a/client/src/cmdhflist.h +++ b/client/src/cmdhflist.h @@ -51,7 +51,7 @@ void annotateMifare(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize, uint8 void annotateLTO(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); void annotateCryptoRF(char *exp, size_t size, uint8_t *cmd, uint8_t cmdsize); -bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen); +bool DecodeMifareData(uint8_t *cmd, uint8_t cmdsize, uint8_t *parity, bool isResponse, uint8_t *mfData, size_t *mfDataLen, const uint64_t *dicKeys, uint32_t dicKeysCount); bool NTParityChk(TAuthData *ad, uint32_t ntx); bool NestedCheckKey(uint64_t key, TAuthData *ad, uint8_t *cmd, uint8_t cmdsize, uint8_t *parity); bool CheckCrypto1Parity(uint8_t *cmd_enc, uint8_t cmdsize, uint8_t *cmd, uint8_t *parity_enc); diff --git a/client/src/cmdhfmf.c b/client/src/cmdhfmf.c index 481a5ce14..cb338826b 100644 --- a/client/src/cmdhfmf.c +++ b/client/src/cmdhfmf.c @@ -27,6 +27,7 @@ #include "des.h" // des ecb #include "crapto1/crapto1.h" // prng_successor #include "cmdhf14a.h" // exchange APDU +#include "crypto/libpcrypto.h" #define MFBLOCK_SIZE 16 @@ -42,66 +43,6 @@ static int CmdHelp(const char *Cmd); -static int usage_hf14_ice(void) { - PrintAndLogEx(NORMAL, "Usage: hf mf ice [l ] [f ]"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " l nonces to be collected"); - PrintAndLogEx(NORMAL, " f save nonces to instead of hf-mf--nonces.bin"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf ice")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf ice f nonces.bin")); - return PM3_SUCCESS; -} - -static int usage_hf14_dump(void) { - PrintAndLogEx(NORMAL, "Usage: hf mf dump [card memory] [k ] [f ]"); - PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (MIFARE Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); - PrintAndLogEx(NORMAL, " k : key filename, if no given, UID will be used as filename"); - PrintAndLogEx(NORMAL, " f : data filename, if no given, UID will be used as filename"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf dump")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf dump 4")); - return PM3_SUCCESS; -} - -static int usage_hf14_mifare(void) { - PrintAndLogEx(NORMAL, "Usage: hf mf darkside "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " (Optional) target other block"); - PrintAndLogEx(NORMAL, " (optional) target key type"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf darkside")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf darkside 16")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf darkside 16 B")); - return PM3_SUCCESS; -} -static int usage_hf14_mfsim(void) { - PrintAndLogEx(NORMAL, "Usage: hf mf sim [u ] [n ] [t] [a ] [s ] [i] [x] [e] [v]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " u (Optional) UID 4,7 or 10bytes. If not specified, the UID 4b/7b from emulator memory will be used"); - PrintAndLogEx(NORMAL, " t (Optional) Enforce ATQA/SAK:"); - PrintAndLogEx(NORMAL, " 0 = MIFARE Mini"); - PrintAndLogEx(NORMAL, " 1 = MIFARE Classic 1k (Default)"); - PrintAndLogEx(NORMAL, " 2 = MIFARE Classic 2k plus in SL0 mode"); - PrintAndLogEx(NORMAL, " 4 = MIFARE Classic 4k"); - PrintAndLogEx(NORMAL, " a (Optional) Provide explicitly ATQA (2 bytes, override option t)"); - PrintAndLogEx(NORMAL, " s (Optional) Provide explicitly SAK (1 byte, override option t)"); - PrintAndLogEx(NORMAL, " n (Optional) Automatically exit simulation after blocks have been read by reader. 0 = infinite"); - PrintAndLogEx(NORMAL, " i (Optional) Interactive, means that console will not be returned until simulation finishes or is aborted"); - PrintAndLogEx(NORMAL, " x (Optional) Crack, performs the 'reader attack', nr/ar attack against a reader"); - PrintAndLogEx(NORMAL, " e (Optional) Fill simulator keys from found keys"); - PrintAndLogEx(NORMAL, " v (Optional) Verbose"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf sim u 0a0a0a0a")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf sim u 11223344556677")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf sim u 112233445566778899AA")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf sim u 11223344 i x")); - return PM3_SUCCESS; -} /* * static int usage_hf14_sniff(void) { PrintAndLogEx(NORMAL, "It continuously gets data from the field and saves it to: log, emulator, emulator file."); @@ -117,38 +58,6 @@ static int usage_hf14_mfsim(void) { return PM3_SUCCESS; } */ -static int usage_hf14_nested(void) { - PrintAndLogEx(NORMAL, "Usage:"); - PrintAndLogEx(NORMAL, " all sectors: hf mf nested [t,d]"); - PrintAndLogEx(NORMAL, " one sector: hf mf nested o [t]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K"); - PrintAndLogEx(NORMAL, " t transfer keys into emulator memory"); - PrintAndLogEx(NORMAL, " d write keys to binary file `hf-mf--key.bin`"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf nested 1 0 A FFFFFFFFFFFF")" -- key recovery against 1K, block 0, Key A using key FFFFFFFFFFFF"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf nested 1 0 A FFFFFFFFFFFF t")" -- and transfer keys into emulator memory"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf nested 1 0 A FFFFFFFFFFFF d")" -- or write keys to binary file "); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf nested o 0 A FFFFFFFFFFFF 4 A")" -- one sector key recovery. Use block 0 Key A to find block 4 Key A"); - return PM3_SUCCESS; -} -static int usage_hf14_staticnested(void) { - PrintAndLogEx(NORMAL, "Usage:"); - PrintAndLogEx(NORMAL, " all sectors: hf mf staticnested [t,d]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K"); - PrintAndLogEx(NORMAL, " t transfer keys into emulator memory"); - PrintAndLogEx(NORMAL, " d write keys to binary file `hf-mf--key.bin`"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf staticnested 1 0 A FFFFFFFFFFFF")" -- key recovery against 1K, block 0, Key A using key FFFFFFFFFFFF"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf staticnested 1 0 A FFFFFFFFFFFF t")" -- and transfer keys into emulator memory"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf staticnested 1 0 A FFFFFFFFFFFF d")" -- or write keys to binary file "); - return PM3_SUCCESS; -} static int usage_hf14_hardnested(void) { PrintAndLogEx(NORMAL, "Usage:"); PrintAndLogEx(NORMAL, " hf mf hardnested "); @@ -228,48 +137,7 @@ static int usage_hf14_autopwn(void) { PrintAndLogEx(NORMAL, _YELLOW_(" hf mf autopwn k 0 A FFFFFFFFFFFF * 1 f mfc_default_keys")" -- this command combines the two above (reduce the need for nested / hardnested attacks, by using a dictionary)"); return PM3_SUCCESS; } -static int usage_hf14_chk(void) { - PrintAndLogEx(NORMAL, "Usage: hf mf chk [h] |<*card memory> [t|d] [] []"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " * all sectors based on card memory, other values then below defaults to 1k"); - PrintAndLogEx(NORMAL, " 0 - MINI(320 bytes)"); - PrintAndLogEx(NORMAL, " 1 - 1K"); - PrintAndLogEx(NORMAL, " 2 - 2K"); - PrintAndLogEx(NORMAL, " 4 - 4K"); - PrintAndLogEx(NORMAL, " d write keys to binary file"); - PrintAndLogEx(NORMAL, " t write keys to emulator memory\n"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf chk 0 A 1234567890ab")" -- target block 0, Key A using key 1234567890ab"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf chk 0 A mfc_default_keys.dic")" -- target block 0, Key A using default dictionary file"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf chk *1 ? t")" -- target all blocks, all keys, 1K, write to emulator memory"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf chk *1 ? d")" -- target all blocks, all keys, 1K, write to file"); - return PM3_SUCCESS; -} -static int usage_hf14_chk_fast(void) { - PrintAndLogEx(NORMAL, "This is a improved checkkeys method speedwise. It checks MIFARE Classic tags sector keys against a dictionary file with keys"); - PrintAndLogEx(NORMAL, "Usage: hf mf fchk [h] [t|d|f] [] []"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " all sectors based on card memory, other values than below defaults to 1k"); - PrintAndLogEx(NORMAL, " 0 - MINI(320 bytes)"); - PrintAndLogEx(NORMAL, " 1 - 1K "); - PrintAndLogEx(NORMAL, " 2 - 2K"); - PrintAndLogEx(NORMAL, " 4 - 4K"); - PrintAndLogEx(NORMAL, " d write keys to binary file"); - PrintAndLogEx(NORMAL, " t write keys to emulator memory"); - PrintAndLogEx(NORMAL, " m use dictionary from flashmemory\n"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf fchk 1 1234567890ab")" -- target 1K using key 1234567890ab"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf fchk 1 mfc_default_keys.dic")" -- target 1K using default dictionary file"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf fchk 1 t")" -- target 1K, write to emulator memory"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf fchk 1 d")" -- target 1K, write to file"); - if (IfPm3Flash()) - PrintAndLogEx(NORMAL, _YELLOW_(" hf mf fchk 1 m")" -- target 1K, use dictionary from flashmemory"); - return PM3_SUCCESS; -} + /* static int usage_hf14_keybrute(void) { PrintAndLogEx(NORMAL, "J_Run's 2nd phase of multiple sector nested authentication key recovery"); @@ -558,6 +426,46 @@ static int usage_hf14_gen3freeze(void) { return PM3_SUCCESS; } +int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len) { + + // ref: MIFARE Classic EV1 Originality Signature Validation +#define PUBLIC_MFCEV1_ECDA_KEYLEN 33 + const ecdsa_publickey_t nxp_mfc_public_keys[] = { + {"NXP Mifare Classic MFC1C14_x", "044F6D3F294DEA5737F0F46FFEE88A356EED95695DD7E0C27A591E6F6F65962BAF"}, + }; + + uint8_t i; + bool is_valid = false; + + for (i = 0; i < ARRAYLEN(nxp_mfc_public_keys); i++) { + + int dl = 0; + uint8_t key[PUBLIC_MFCEV1_ECDA_KEYLEN]; + param_gethex_to_eol(nxp_mfc_public_keys[i].value, 0, key, PUBLIC_MFCEV1_ECDA_KEYLEN, &dl); + + int res = ecdsa_signature_r_s_verify(MBEDTLS_ECP_DP_SECP128R1, key, uid, uidlen, signature, signature_len, false); + is_valid = (res == 0); + if (is_valid) + break; + } + + PrintAndLogEx(INFO, ""); + PrintAndLogEx(INFO, "--- " _CYAN_("Tag Signature")); + if (is_valid == false || i == ARRAYLEN(nxp_mfc_public_keys)) { + PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); + PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32)); + PrintAndLogEx(SUCCESS, " Signature verification: " _RED_("failed")); + return PM3_ESOFT; + } + + PrintAndLogEx(INFO, " IC signature public key name: %s", nxp_mfc_public_keys[i].desc); + PrintAndLogEx(INFO, "IC signature public key value: %s", nxp_mfc_public_keys[i].value); + PrintAndLogEx(INFO, " Elliptic curve parameters: NID_secp128r1"); + PrintAndLogEx(INFO, " TAG IC Signature: %s", sprint_hex_inrow(signature, 32)); + PrintAndLogEx(SUCCESS, " Signature verification: " _GREEN_("successful")); + return PM3_SUCCESS; +} + static int GetHFMF14AUID(uint8_t *uid, int *uidlen) { clearCommandBuffer(); SendCommandMIX(CMD_HF_ISO14443A_READER, ISO14A_CONNECT, 0, 0, NULL, 0); @@ -576,6 +484,9 @@ static int GetHFMF14AUID(uint8_t *uid, int *uidlen) { } static char *GenerateFilename(const char *prefix, const char *suffix) { + if (! IfPm3Iso14443a()) { + return NULL; + } uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int uidlen = 0; char *fptr = calloc(sizeof(char) * (strlen(prefix) + strlen(suffix)) + sizeof(uid) * 2 + 1, sizeof(uint8_t)); @@ -698,17 +609,33 @@ static char GetFormatFromSector(uint8_t sectorNo) { } static int CmdHF14AMfDarkside(const char *Cmd) { - uint8_t blockno = 0, key_type = MIFARE_AUTH_KEYA; - uint64_t key = 0; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf darkside", + "Darkside attack", + "hf mf darkside\n" + "hf mf darkside --blk 16\n" + "hf mf darkside --blk 16 -b\n"); - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_hf14_mifare(); + void *argtable[] = { + arg_param_begin, + arg_int0(NULL, "blk", " ", "Simulation type to use"), + arg_lit0("b", NULL, "Target key B instead of default key A"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); - blockno = param_get8(Cmd, 0); + uint8_t blockno = arg_get_u32_def(ctx, 1, 0); - cmdp = tolower(param_getchar(Cmd, 1)); - if (cmdp == 'b') + uint8_t key_type = MIFARE_AUTH_KEYA; + + if (arg_get_lit(ctx, 2)) { + PrintAndLogEx(INFO, "Targeting key B"); key_type = MIFARE_AUTH_KEYB; + } + + CLIParserFree(ctx); + + uint64_t key = 0; int isOK = mfDarkside(blockno, key_type, &key); PrintAndLogEx(NORMAL, ""); @@ -748,14 +675,14 @@ static int CmdHF14AMfWrBl(const char *Cmd) { PrintAndLogEx(NORMAL, "Usage: hf mf wrbl "); PrintAndLogEx(NORMAL, "Examples:"); PrintAndLogEx(NORMAL, " hf mf wrbl 1 A FFFFFFFFFFFF 000102030405060708090A0B0C0D0E0F"); - return 0; + return PM3_SUCCESS; } blockNo = param_get8(Cmd, 0); cmdp = tolower(param_getchar(Cmd, 1)); if (cmdp == 0x00) { PrintAndLogEx(NORMAL, "Key type must be A or B"); - return 1; + return PM3_EINVARG; } if (cmdp != 'a') @@ -763,12 +690,12 @@ static int CmdHF14AMfWrBl(const char *Cmd) { if (param_gethex(Cmd, 2, key, 12)) { PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols"); - return 1; + return PM3_EINVARG; } if (param_gethex(Cmd, 3, bldata, 32)) { PrintAndLogEx(NORMAL, "Block data must include 32 HEX symbols"); - return 1; + return PM3_EINVARG; } PrintAndLogEx(NORMAL, "--block no %d, key %c - %s", blockNo, keyType ? 'B' : 'A', sprint_hex(key, 6)); @@ -788,7 +715,7 @@ static int CmdHF14AMfWrBl(const char *Cmd) { PrintAndLogEx(NORMAL, "Command execute timeout"); } - return 0; + return PM3_SUCCESS; } static int CmdHF14AMfRdBl(const char *Cmd) { @@ -820,34 +747,15 @@ static int CmdHF14AMfRdBl(const char *Cmd) { } PrintAndLogEx(NORMAL, "--block no %d, key %c - %s", blockNo, keyType ? 'B' : 'A', sprint_hex(key, 6)); - mf_readblock_t payload; - payload.blockno = blockNo; - payload.keytype = keyType; - memcpy(payload.key, key, sizeof(payload.key)); - - clearCommandBuffer(); - SendCommandNG(CMD_HF_MIFARE_READBL, (uint8_t *)&payload, sizeof(mf_readblock_t)); - - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_HF_MIFARE_READBL, &resp, 1500)) { - uint8_t *data = resp.data.asBytes; - - if (resp.status == PM3_SUCCESS) { - PrintAndLogEx(NORMAL, "data: %s", sprint_hex(data, 16)); - } else { - PrintAndLogEx(FAILED, "failed reading block"); - return PM3_ESOFT; - } - + uint8_t data[16] = {0}; + int res = mfReadBlock(blockNo, keyType, key, data); + if (res == PM3_SUCCESS) { + PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data, 16)); if ((data[6] || data[7] || data[8])) { decode_print_st(blockNo, data); } - } else { - PrintAndLogEx(WARNING, "Command execute timeout"); - return PM3_ETIMEOUT; } - - return 0; + return PM3_SUCCESS; } static int CmdHF14AMfRdSc(const char *Cmd) { @@ -883,30 +791,26 @@ static int CmdHF14AMfRdSc(const char *Cmd) { } PrintAndLogEx(NORMAL, "--sector no %d, key %c - %s ", sectorNo, keyType ? 'B' : 'A', sprint_hex(key, 6)); - clearCommandBuffer(); - SendCommandMIX(CMD_HF_MIFARE_READSC, sectorNo, keyType, 0, key, 6); - PrintAndLogEx(NORMAL, ""); - - PacketResponseNG resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - uint8_t isOK = resp.oldarg[0] & 0xff; - uint8_t *data = resp.data.asBytes; - - PrintAndLogEx(NORMAL, "isOk:%02x", isOK); - if (isOK) { - - uint8_t blocks = NumBlocksPerSector(sectorNo); - uint8_t start = FirstBlockOfSector(sectorNo); - - for (int i = 0; i < blocks; i++) { - PrintAndLogEx(NORMAL, "%3d | %s", start + i, sprint_hex(data + (i * 16), 16)); - } - decode_print_st(start + blocks - 1, data + ((blocks - 1) * 16)); - } - } else { - PrintAndLogEx(WARNING, "Command execute timeout"); + uint8_t sc_size = mfNumBlocksPerSector(sectorNo) * 16; + uint8_t *data = calloc(sc_size, sizeof(uint8_t)); + if (data == NULL) { + PrintAndLogEx(ERR, "failed to allocate memory"); + return PM3_EMALLOC; } + int res = mfReadSector(sectorNo, keyType, key, data); + if (res == PM3_SUCCESS) { + + uint8_t blocks = NumBlocksPerSector(sectorNo); + uint8_t start = FirstBlockOfSector(sectorNo); + + for (int i = 0; i < blocks; i++) { + PrintAndLogEx(NORMAL, "%3d | %s", start + i, sprint_hex(data + (i * 16), 16)); + } + decode_print_st(start + blocks - 1, data + ((blocks - 1) * 16)); + + } + free(data); return PM3_SUCCESS; } @@ -950,6 +854,42 @@ static int FastDumpWithEcFill(uint8_t numsectors) { } static int CmdHF14AMfDump(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf dump", + "Dump MIFARE Classic tag to binary file\n" + "If no given, UID will be used as filename", + "hf mf dump --mini --> MIFARE Mini\n" + "hf mf dump --1k --> MIFARE Classic 1k\n" + "hf mf dump --2k --> MIFARE 2k\n" + "hf mf dump --4k --> MIFARE 4k\n" + "hf mf dump -f hf-mf-066C8B78-key-5.bin --> MIFARE 1k with keys from specified file\n"); + + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "filename of dump"), + arg_str0("k", "keys", "", "filename of keys"), + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (default)"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + + int datafnlen = 0; + char dataFilename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)dataFilename, FILE_PATH_SIZE, &datafnlen); + + int keyfnlen = 0; + char keyFilename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 2), (uint8_t *)keyFilename, FILE_PATH_SIZE, &keyfnlen); + + bool m0 = arg_get_lit(ctx, 3); + bool m1 = arg_get_lit(ctx, 4); + bool m2 = arg_get_lit(ctx, 5); + bool m4 = arg_get_lit(ctx, 6); + + CLIParserFree(ctx); uint64_t t1 = msclock(); @@ -958,43 +898,35 @@ static int CmdHF14AMfDump(const char *Cmd) { uint8_t keyB[40][6]; uint8_t rights[40][4]; uint8_t carddata[256][16]; - uint8_t numSectors = 16; - uint8_t cmdp = 0; - - char keyFilename[FILE_PATH_SIZE] = {0}; - char dataFilename[FILE_PATH_SIZE]; - char *fptr; - - memset(keyFilename, 0, sizeof(keyFilename)); - memset(dataFilename, 0, sizeof(dataFilename)); FILE *f; PacketResponseNG resp; - while (param_getchar(Cmd, cmdp) != 0x00) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf14_dump(); - case 'k': - param_getstr(Cmd, cmdp + 1, keyFilename, FILE_PATH_SIZE); - cmdp += 2; - break; - case 'f': - param_getstr(Cmd, cmdp + 1, dataFilename, FILE_PATH_SIZE); - cmdp += 2; - break; - default: - if (cmdp == 0) { - numSectors = NumOfSectors(param_getchar(Cmd, cmdp)); - if (numSectors == 0) return usage_hf14_dump(); - cmdp++; - } else { - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - return usage_hf14_dump(); - } - } + // validations + if ((m0 + m1 + m2 + m4) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); + return PM3_EINVARG; + } else if ((m0 + m1 + m2 + m4) == 0) { + m1 = true; } + uint8_t numSectors = 1; + + if (m0) { + numSectors = MIFARE_MINI_MAXSECTOR; + } else if (m1) { + numSectors = MIFARE_1K_MAXSECTOR; + } else if (m2) { + numSectors = MIFARE_2K_MAXSECTOR; + } else if (m4) { + numSectors = MIFARE_4K_MAXSECTOR; + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + + char *fptr; + if (keyFilename[0] == 0x00) { fptr = GenerateFilename("hf-mf-", "-key.bin"); if (fptr == NULL) @@ -1035,6 +967,7 @@ static int CmdHF14AMfDump(const char *Cmd) { fclose(f); PrintAndLogEx(INFO, "Reading sector access bits..."); + PrintAndLogEx(INFO, "." NOLF); uint8_t tries; mf_readblock_t payload; @@ -1319,62 +1252,107 @@ static int CmdHF14AMfRestore(const char *Cmd) { } static int CmdHF14AMfNested(const char *Cmd) { - sector_t *e_sector = NULL; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf nested", + "Execute Nested attack against MIFARE Classic card for key recovery", + "hf mf nested --single --blk 0 -a FFFFFFFFFFFF --tblk 4 --tkeya --> Single sector key recovery. Use block 0 Key A to find block 4 Key A\n" + "hf mf nested --mini --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE Mini\n" + "hf mf nested --1k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE Classic 1k\n" + "hf mf nested --2k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE 2k\n" + "hf mf nested --4k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE 4k"); + + void *argtable[] = { + arg_param_begin, + arg_str0("k", "key", "", "Key specified as 12 hex symbols"), + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_int0(NULL, "blk", "", "Input block number"), + arg_lit0("a", NULL, "Input key specified is A key (default)"), + arg_lit0("b", NULL, "Input key specified is B key"), + arg_int0(NULL, "tblk", "", "Target block number"), + arg_lit0(NULL, "tkeya", "Target A key (default)"), + arg_lit0(NULL, "tkeyb", "Target B key"), + arg_lit0(NULL, "emu", "Fill simulator keys from found keys"), + arg_lit0(NULL, "dump", "Dump found keys to file"), + arg_lit0(NULL, "single", "Single sector (defaults to All)"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 1, key, &keylen); + + bool m0 = arg_get_lit(ctx, 2); + bool m1 = arg_get_lit(ctx, 3); + bool m2 = arg_get_lit(ctx, 4); + bool m4 = arg_get_lit(ctx, 5); + + uint8_t blockNo = arg_get_u32_def(ctx, 6, 0); + uint8_t keyType = 0; - uint8_t trgBlockNo = 0; - uint8_t trgKeyType = 0; - uint8_t SectorsCnt = 0; - uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - uint8_t keyBlock[(ARRAYLEN(g_mifare_default_keys) + 1) * 6]; - uint64_t key64 = 0; - bool transferToEml = false; - bool createDumpFile = false; - if (strlen(Cmd) < 3) return usage_hf14_nested(); - - char cmdp, ctmp; - cmdp = tolower(param_getchar(Cmd, 0)); - uint8_t blockNo = param_get8(Cmd, 1); - ctmp = tolower(param_getchar(Cmd, 2)); - - if (ctmp != 'a' && ctmp != 'b') { - PrintAndLogEx(WARNING, "key type must be A or B"); + if (arg_get_lit(ctx, 7) && arg_get_lit(ctx, 8)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Input key type must be A or B"); return PM3_EINVARG; - } - - if (ctmp != 'a') + } else if (arg_get_lit(ctx, 8)) { keyType = 1; + } - if (param_gethex(Cmd, 3, key, 12)) { - PrintAndLogEx(WARNING, "key must include 12 HEX symbols"); + uint8_t trgBlockNo = arg_get_u32_def(ctx, 9, 0); + + uint8_t trgKeyType = 0; + + if (arg_get_lit(ctx, 10) && arg_get_lit(ctx, 11)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Target key type must be A or B"); + return PM3_EINVARG; + } else if (arg_get_lit(ctx, 11)) { + trgKeyType = 1; + } + + bool transferToEml = arg_get_lit(ctx, 12); + bool createDumpFile = arg_get_lit(ctx, 13); + bool singleSector = arg_get_lit(ctx, 14); + + CLIParserFree(ctx); + + //validations + if ((m0 + m1 + m2 + m4) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); return PM3_EINVARG; } - if (cmdp == 'o') { - trgBlockNo = param_get8(Cmd, 4); - ctmp = tolower(param_getchar(Cmd, 5)); - if (ctmp != 'a' && ctmp != 'b') { - PrintAndLogEx(WARNING, "target key type must be A or B"); + uint8_t SectorsCnt = 1; + if (m0) { + SectorsCnt = MIFARE_MINI_MAXSECTOR; + } else if (m1) { + SectorsCnt = MIFARE_1K_MAXSECTOR; + } else if (m2) { + SectorsCnt = MIFARE_2K_MAXSECTOR; + } else if (m4) { + SectorsCnt = MIFARE_4K_MAXSECTOR; + } + + if (singleSector == false) { + if (SectorsCnt == 0) { + PrintAndLogEx(WARNING, "Invalid MIFARE Type"); return PM3_EINVARG; } - if (ctmp != 'a') { - trgKeyType = 1; - } - } else { - SectorsCnt = NumOfSectors(cmdp); - if (SectorsCnt == 0) return usage_hf14_nested(); } - uint8_t j = 4; - while (ctmp != 0x00) { - - ctmp = tolower(param_getchar(Cmd, j)); - transferToEml |= (ctmp == 't'); - createDumpFile |= (ctmp == 'd'); - - j++; + if (keylen != 6) { + PrintAndLogEx(WARNING, "Input key must include 12 HEX symbols"); + return PM3_EINVARG; } + sector_t *e_sector = NULL; + uint8_t keyBlock[(ARRAYLEN(g_mifare_default_keys) + 1) * 6]; + uint64_t key64 = 0; + // check if tag doesn't have static nonce if (detect_classic_static_nonce() == NONCE_STATIC) { PrintAndLogEx(WARNING, "Static nonce detected. Quitting..."); @@ -1388,7 +1366,7 @@ static int CmdHF14AMfNested(const char *Cmd) { return PM3_EOPABORTED; } - if (cmdp == 'o') { + if (singleSector) { int16_t isOK = mfnested(blockNo, keyType, key, trgBlockNo, trgKeyType, keyBlock, true); switch (isOK) { case PM3_ETIMEOUT: @@ -1584,48 +1562,87 @@ jumptoend: } static int CmdHF14AMfNestedStatic(const char *Cmd) { - sector_t *e_sector = NULL; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf staticnested", + "Execute Nested attack against MIFARE Classic card with static nonce for key recovery", + "hf mf staticnested --mini --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE Mini\n" + "hf mf staticnested --1k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE Classic 1k\n" + "hf mf staticnested --2k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE 2k\n" + "hf mf staticnested --4k --blk 0 -a -k FFFFFFFFFFFF --> Key recovery against MIFARE 4k\n"); + + void *argtable[] = { + arg_param_begin, + arg_str0("k", "key", "", "Key specified as 12 hex symbols"), + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_int0(NULL, "blk", "", "Input block number"), + arg_lit0("a", NULL, "Input key specified is A key (default)"), + arg_lit0("b", NULL, "Input key specified is B key"), + arg_lit0("e", "emukeys", "Fill simulator keys from found keys"), + arg_lit0(NULL, "dumpkeys", "Dump found keys to file"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, false); + + int keylen = 0; + uint8_t key[6] = {0}; + CLIGetHexWithReturn(ctx, 1, key, &keylen); + + bool m0 = arg_get_lit(ctx, 2); + bool m1 = arg_get_lit(ctx, 3); + bool m2 = arg_get_lit(ctx, 4); + bool m4 = arg_get_lit(ctx, 5); + + uint8_t blockNo = arg_get_u32_def(ctx, 6, 0); + uint8_t keyType = 0; + + if (arg_get_lit(ctx, 7) && arg_get_lit(ctx, 8)) { + CLIParserFree(ctx); + PrintAndLogEx(WARNING, "Input key type must be A or B"); + return PM3_EINVARG; + } else if (arg_get_lit(ctx, 8)) { + keyType = 1; + } + + bool transferToEml = arg_get_lit(ctx, 9); + bool createDumpFile = arg_get_lit(ctx, 10); + + CLIParserFree(ctx); + + //validations + if ((m0 + m1 + m2 + m4) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); + return PM3_EINVARG; + } + + uint8_t SectorsCnt = 1; + if (m0) { + SectorsCnt = MIFARE_MINI_MAXSECTOR; + } else if (m1) { + SectorsCnt = MIFARE_1K_MAXSECTOR; + } else if (m2) { + SectorsCnt = MIFARE_2K_MAXSECTOR; + } else if (m4) { + SectorsCnt = MIFARE_4K_MAXSECTOR; + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + + if (keylen != 6) { + PrintAndLogEx(WARNING, "Input key must include 12 HEX symbols"); + return PM3_EINVARG; + } + + sector_t *e_sector = NULL; + uint8_t trgKeyType = 0; - uint8_t SectorsCnt = 0; - uint8_t key[6] = {0, 0, 0, 0, 0, 0}; + uint8_t keyBlock[(ARRAYLEN(g_mifare_default_keys) + 1) * 6]; uint64_t key64 = 0; - bool transferToEml = false; - bool createDumpFile = false; - - if (strlen(Cmd) < 3) return usage_hf14_staticnested(); - - char cmdp, ctmp; - cmdp = tolower(param_getchar(Cmd, 0)); - uint8_t blockNo = param_get8(Cmd, 1); - ctmp = tolower(param_getchar(Cmd, 2)); - - if (ctmp != 'a' && ctmp != 'b') { - PrintAndLogEx(WARNING, "key type must be A or B"); - return PM3_EINVARG; - } - - if (ctmp != 'a') - keyType = 1; - - if (param_gethex(Cmd, 3, key, 12)) { - PrintAndLogEx(WARNING, "key must include 12 HEX symbols"); - return PM3_EINVARG; - } - - SectorsCnt = NumOfSectors(cmdp); - if (SectorsCnt == 0) return usage_hf14_staticnested(); - - uint8_t j = 4; - while (ctmp != 0x00) { - - ctmp = tolower(param_getchar(Cmd, j)); - transferToEml |= (ctmp == 't'); - createDumpFile |= (ctmp == 'd'); - - j++; - } // check if tag have static nonce if (detect_classic_static_nonce() != NONCE_STATIC) { @@ -1972,8 +1989,9 @@ static int CmdHF14AMfNestedHard(const char *Cmd) { uint64_t foundkey = 0; int16_t isOK = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, know_target_key ? trgkey : NULL, nonce_file_read, nonce_file_write, slow, tests, &foundkey, filename); - if (tests == 0) + if ((tests == 0) && IfPm3Iso14443a()) { DropField(); + } if (isOK) { switch (isOK) { @@ -2280,6 +2298,7 @@ static int CmdHF14AMfAutoPWN(const char *Cmd) { if (verbose) PrintAndLogEx(INFO, "======================= " _YELLOW_("START DICTIONARY ATTACK") " ======================="); if (legacy_mfchk) { + PrintAndLogEx(INFO, "." NOLF); // Check all the sectors for (int i = 0; i < sectors_cnt; i++) { for (int j = 0; j < 2; j++) { @@ -2752,132 +2771,161 @@ static void shuffle(uint8_t *array, uint16_t len) { */ static int CmdHF14AMfChk_fast(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf fchk", + "This is a improved checkkeys method speedwise. It checks MIFARE Classic tags sector keys against a dictionary file with keys", + "hf mf fchk --mini -k FFFFFFFFFFFF --> Key recovery against MIFARE Mini\n" + "hf mf fchk --1k -k FFFFFFFFFFFF --> Key recovery against MIFARE Classic 1k\n" + "hf mf fchk --2k -k FFFFFFFFFFFF --> Key recovery against MIFARE 2k\n" + "hf mf fchk --4k -k FFFFFFFFFFFF --> Key recovery against MIFARE 4k\n" + "hf mf fchk --1k -f mfc_default_keys.dic --> Target 1K using default dictionary file\n" + "hf mf fchk --1k --emu --> Target 1K, write to emulator memory\n" + "hf mf fchk --1k --dump --> Target 1K, write to file\n" + "hf mf fchk --1k --mem --> Target 1K, use dictionary from flashmemory"); - char ctmp = 0x00; - ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_chk_fast(); + void *argtable[] = { + arg_param_begin, + arg_strx0("k", "key", "", "Key specified as 12 hex symbols"), + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (default)"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_lit0(NULL, "emu", "Fill simulator keys from found keys"), + arg_lit0(NULL, "dump", "Dump found keys to binary file"), + arg_lit0(NULL, "mem", "Use dictionary from flashmemory"), + arg_str0("f", "file", "", "filename of dictionary"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); - FILE *f; + int keylen = 0; + uint8_t key[255 * 6] = {0}; + CLIGetHexWithReturn(ctx, 1, key, &keylen); + + bool m0 = arg_get_lit(ctx, 2); + bool m1 = arg_get_lit(ctx, 3); + bool m2 = arg_get_lit(ctx, 4); + bool m4 = arg_get_lit(ctx, 5); + + bool transferToEml = arg_get_lit(ctx, 6); + bool createDumpFile = arg_get_lit(ctx, 7); + bool use_flashmemory = arg_get_lit(ctx, 8); + + int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - char buf[13]; - uint8_t *keyBlock, *p; + CLIParamStrToBuf(arg_get_str(ctx, 9), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + CLIParserFree(ctx); + + //validations + + if ((m0 + m1 + m2 + m4) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); + return PM3_EINVARG; + } else if ((m0 + m1 + m2 + m4) == 0) { + m1 = true; + } + uint8_t sectorsCnt = 1; - int i, keycnt = 0; - int clen = 0; - int transferToEml = 0, createDumpFile = 0; + if (m0) { + sectorsCnt = MIFARE_MINI_MAXSECTOR; + } else if (m1) { + sectorsCnt = MIFARE_1K_MAXSECTOR; + } else if (m2) { + sectorsCnt = MIFARE_2K_MAXSECTOR; + } else if (m4) { + sectorsCnt = MIFARE_4K_MAXSECTOR; + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + + // Handle Keys + int keycnt = 0; uint32_t keyitems = ARRAYLEN(g_mifare_default_keys); - bool use_flashmemory = false; - - sector_t *e_sector = NULL; - + uint8_t *keyBlock, *p; + // Allocate memory for keys to be tested keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6); if (keyBlock == NULL) return PM3_EMALLOC; + // Copy default keys to list for (int cnt = 0; cnt < ARRAYLEN(g_mifare_default_keys); cnt++) - num_to_bytes(g_mifare_default_keys[cnt], 6, keyBlock + cnt * 6); + num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t *)(keyBlock + cnt * 6)); + + // Handle user supplied key + if (keylen >= 6) { + int numKeys = keylen / 6; + + p = realloc(keyBlock, 6 * (keyitems + numKeys)); + if (!p) { + PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); + free(keyBlock); + return PM3_EMALLOC; + } + keyBlock = p; + + memcpy(keyBlock + 6 * keycnt, key, 6 * numKeys); + + for (int i = 0; i < numKeys; i++) { + PrintAndLogEx(NORMAL, "[%2d] key %s", keycnt, sprint_hex((keyBlock + 6 * keycnt), 6)); + keycnt++; + } - // sectors - switch (ctmp) { - case '0': - sectorsCnt = MIFARE_MINI_MAXSECTOR; - break; - case '1': - sectorsCnt = MIFARE_1K_MAXSECTOR; - break; - case '2': - sectorsCnt = MIFARE_2K_MAXSECTOR; - break; - case '4': - sectorsCnt = MIFARE_4K_MAXSECTOR; - break; - default: - sectorsCnt = MIFARE_1K_MAXSECTOR; } - for (i = 1; param_getchar(Cmd, i); i++) { + // Handle user supplied dictionary file + FILE *f; + char buf[13]; + if (fnlen > 0) { + char *dict_path; + int res = searchFile(&dict_path, DICTIONARIES_SUBDIR, filename, ".dic", false); + if (res != PM3_SUCCESS) { + free(keyBlock); + return PM3_EFILE; + } + f = fopen(dict_path, "r"); + if (!f) { + PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", dict_path); + free(dict_path); + free(keyBlock); + return PM3_EFILE; + } + free(dict_path); + // load keys from dictionary file + while (fgets(buf, sizeof(buf), f)) { + if (strlen(buf) < 12 || buf[11] == '\n') + continue; - ctmp = tolower(param_getchar(Cmd, i)); - clen = param_getlength(Cmd, i); + while (fgetc(f) != '\n' && !feof(f)) ; //goto next line - if (clen == 12) { + if (buf[0] == '#') continue; //The line start with # is comment, skip - if (param_gethex(Cmd, i, keyBlock + 6 * keycnt, 12)) { - PrintAndLogEx(FAILED, "not hex, skipping"); + // codesmell, only checks first char? + if (!isxdigit(buf[0])) { + PrintAndLogEx(FAILED, "File content error. '" _YELLOW_("%s")"' must include 12 HEX symbols", buf); continue; } + buf[12] = 0; + if (keyitems - keycnt < 2) { p = realloc(keyBlock, 6 * (keyitems += 64)); if (!p) { - PrintAndLogEx(FAILED, "Cannot allocate memory for Keys"); + PrintAndLogEx(FAILED, "Cannot allocate memory for defKeys"); free(keyBlock); + fclose(f); return PM3_EMALLOC; } keyBlock = p; } - PrintAndLogEx(NORMAL, "[%2d] key %s", keycnt, sprint_hex((keyBlock + 6 * keycnt), 6)); + memset(keyBlock + 6 * keycnt, 0, 6); + num_to_bytes(strtoll(buf, NULL, 16), 6, keyBlock + 6 * keycnt); + //PrintAndLogEx(NORMAL, "check key[%2d] %012" PRIx64, keycnt, bytes_to_num(keyBlock + 6*keycnt, 6)); keycnt++; - } else if (clen == 1) { - if (ctmp == 't') { transferToEml = 1; continue; } - if (ctmp == 'd') { createDumpFile = 1; continue; } - if ((ctmp == 'm') && (IfPm3Flash())) { use_flashmemory = true; continue; } - } else { - // May be a dic file - if (param_getstr(Cmd, i, filename, FILE_PATH_SIZE) >= FILE_PATH_SIZE) { - PrintAndLogEx(FAILED, "Filename too long"); - free(keyBlock); - return PM3_EINVARG; - } - - char *dict_path; - int res = searchFile(&dict_path, DICTIONARIES_SUBDIR, filename, ".dic", false); - if (res != PM3_SUCCESS) { - free(keyBlock); - return res; - } - f = fopen(dict_path, "r"); - if (!f) { - PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", dict_path); - free(dict_path); - free(keyBlock); - return PM3_EFILE; - } - free(dict_path); - - // read file - while (fgets(buf, sizeof(buf), f)) { - if (strlen(buf) < 12 || buf[11] == '\n') - continue; - - while (fgetc(f) != '\n' && !feof(f)) ; //goto next line - - if (buf[0] == '#') continue; //The line start with # is comment, skip - - if (!isxdigit(buf[0])) { - PrintAndLogEx(FAILED, "File content error. '" _YELLOW_("%s")"' must include 12 HEX symbols", buf); - continue; - } - - buf[12] = 0; - if (keyitems - keycnt < 2) { - p = realloc(keyBlock, 6 * (keyitems += 64)); - if (!p) { - PrintAndLogEx(FAILED, "Cannot allocate memory for default keys"); - free(keyBlock); - fclose(f); - return PM3_EMALLOC; - } - keyBlock = p; - } - int pos = 6 * keycnt; - memset(keyBlock + pos, 0, 6); - num_to_bytes(strtoll(buf, NULL, 16), 6, keyBlock + pos); - keycnt++; - memset(buf, 0, sizeof(buf)); - } - fclose(f); - PrintAndLogEx(SUCCESS, "Loaded %2d keys from " _YELLOW_("%s"), keycnt, filename); + memset(buf, 0, sizeof(buf)); } + fclose(f); + PrintAndLogEx(SUCCESS, "Loaded %2d keys from " _YELLOW_("%s"), keycnt, filename); } if (keycnt == 0 && !use_flashmemory) { @@ -2889,15 +2937,18 @@ static int CmdHF14AMfChk_fast(const char *Cmd) { } // create/initialize key storage structure + sector_t *e_sector = NULL; int32_t res = initSectorTable(&e_sector, sectorsCnt); if (res != sectorsCnt) { free(keyBlock); return PM3_EMALLOC; } + uint32_t chunksize = keycnt > (PM3_CMD_DATA_SIZE / 6) ? (PM3_CMD_DATA_SIZE / 6) : keycnt; bool firstChunk = true, lastChunk = false; + int i = 0; // time uint64_t t1 = msclock(); @@ -3012,55 +3063,101 @@ out: } static int CmdHF14AMfChk(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf chk", + "Check keys on MIFARE Classic card", + "hf mf chk --mini -k FFFFFFFFFFFF --> Check all sectors, all keys against MIFARE Mini\n" + "hf mf chk --1k -k FFFFFFFFFFFF --> Check all sectors, all keys against MIFARE Classic 1k\n" + "hf mf chk --2k -k FFFFFFFFFFFF --> Check all sectors, all keys against MIFARE 2k\n" + "hf mf chk --4k -k FFFFFFFFFFFF --> Check all sectors, all keys against MIFARE 4k\n" + "hf mf chk --1k --emu --> Check all sectors, all keys, 1K, and write to emulator memory\n" + "hf mf chk --1k --dump --> Check all sectors, all keys, 1K, and write to file\n" + "hf mf chk -a --blk 0 -f mfc_default_keys.dic --> Check dictionary against block 0, key A"); - char ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 3 || ctmp == 'h') return usage_hf14_chk(); + void *argtable[] = { + arg_param_begin, + arg_strx0("k", "key", "", "Key specified as 12 hex symbols"), + arg_int0(NULL, "blk", "", "Input block number"), + arg_lit0("a", NULL, "Target Key A, if found also check Key B for duplicate"), + arg_lit0("b", NULL, "Target Key B"), + arg_lit0("*", "all", "Target both key A & B (default)"), + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50 (default)"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_lit0(NULL, "emu", "Fill simulator keys from found keys"), + arg_lit0(NULL, "dump", "Dump found keys to binary file"), + arg_str0("f", "file", "", "filename of dictionary"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); - FILE *f; + int keylen = 0; + uint8_t key[255 * 6] = {0}; + CLIGetHexWithReturn(ctx, 1, key, &keylen); + + int blockNo = arg_get_int_def(ctx, 2, -1); + + uint8_t keyType = 2; + + if ((arg_get_lit(ctx, 3) && arg_get_lit(ctx, 4)) || arg_get_lit(ctx, 5)) { + keyType = 2; + } else if (arg_get_lit(ctx, 3)) { + keyType = 0; + } else if (arg_get_lit(ctx, 4)) { + keyType = 1; + } + + bool m0 = arg_get_lit(ctx, 6); + bool m1 = arg_get_lit(ctx, 7); + bool m2 = arg_get_lit(ctx, 8); + bool m4 = arg_get_lit(ctx, 9); + + bool transferToEml = arg_get_lit(ctx, 10); + bool createDumpFile = arg_get_lit(ctx, 11); + + int fnlen = 0; char filename[FILE_PATH_SIZE] = {0}; - char buf[13]; - uint8_t *keyBlock, *p; - sector_t *e_sector = NULL; + CLIParamStrToBuf(arg_get_str(ctx, 12), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); + + CLIParserFree(ctx); + + //validations + + if ((m0 + m1 + m2 + m4) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); + return PM3_EINVARG; + } - uint8_t blockNo = 0; uint8_t SectorsCnt = 1; - uint8_t keyType = 0; - uint32_t keyitems = ARRAYLEN(g_mifare_default_keys); - uint64_t key64 = 0; - int clen = 0; - int transferToEml = 0; - int createDumpFile = 0; - int i, keycnt = 0; + if (m0) { + SectorsCnt = MIFARE_MINI_MAXSECTOR; + } else if (m1) { + SectorsCnt = MIFARE_1K_MAXSECTOR; + } else if (m2) { + SectorsCnt = MIFARE_2K_MAXSECTOR; + } else if (m4) { + SectorsCnt = MIFARE_4K_MAXSECTOR; + } - if (param_getchar(Cmd, 0) == '*') { - blockNo = 3; - SectorsCnt = NumOfSectors(param_getchar(Cmd + 1, 0)); + if ((blockNo == -1) && (SectorsCnt == 1)) { + // Default to 1K if block number not specified + SectorsCnt = MIFARE_1K_MAXSECTOR; + } + + if (blockNo > -1) { if (SectorsCnt == 0) { - return usage_hf14_chk(); + PrintAndLogEx(WARNING, "Invalid MIFARE Type"); + return PM3_EINVARG; } } else { - blockNo = param_get8(Cmd, 0); - } - - ctmp = tolower(param_getchar(Cmd, 1)); - clen = param_getlength(Cmd, 1); - if (clen == 1) { - switch (ctmp) { - case 'a': - keyType = 0; - break; - case 'b': - keyType = 1; - break; - case '?': - keyType = 2; - break; - default: - PrintAndLogEx(FAILED, "Key type must be A , B or ?"); - return PM3_ESOFT; - }; + blockNo = 3; } + // Handle Keys + int keycnt = 0; + uint32_t keyitems = ARRAYLEN(g_mifare_default_keys); + uint8_t *keyBlock, *p; // Allocate memory for keys to be tested keyBlock = calloc(ARRAYLEN(g_mifare_default_keys), 6); if (keyBlock == NULL) return PM3_EMALLOC; @@ -3069,93 +3166,84 @@ static int CmdHF14AMfChk(const char *Cmd) { for (int cnt = 0; cnt < ARRAYLEN(g_mifare_default_keys); cnt++) num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t *)(keyBlock + cnt * 6)); - for (i = 2; param_getchar(Cmd, i); i++) { + // Handle user supplied key + if (keylen >= 6) { + int numKeys = keylen / 6; - ctmp = tolower(param_getchar(Cmd, i)); - clen = param_getlength(Cmd, i); + p = realloc(keyBlock, 6 * (keyitems + numKeys)); + if (!p) { + PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); + free(keyBlock); + return PM3_EMALLOC; + } + keyBlock = p; - if (clen == 12) { + memcpy(keyBlock + 6 * keycnt, key, 6 * numKeys); - if (param_gethex(Cmd, i, keyBlock + 6 * keycnt, 12)) { - PrintAndLogEx(FAILED, "not hex, skipping"); + for (int i = 0; i < numKeys; i++) { + PrintAndLogEx(NORMAL, "[%2d] key %s", keycnt, sprint_hex((keyBlock + 6 * keycnt), 6)); + keycnt++; + } + + } + + // Handle user supplied dictionary file + FILE *f; + char buf[13]; + if (fnlen > 0) { + char *dict_path; + int res = searchFile(&dict_path, DICTIONARIES_SUBDIR, filename, ".dic", false); + if (res != PM3_SUCCESS) { + free(keyBlock); + return PM3_EFILE; + } + f = fopen(dict_path, "r"); + if (!f) { + PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", dict_path); + free(dict_path); + free(keyBlock); + return PM3_EFILE; + } + free(dict_path); + // load keys from dictionary file + while (fgets(buf, sizeof(buf), f)) { + if (strlen(buf) < 12 || buf[11] == '\n') + continue; + + while (fgetc(f) != '\n' && !feof(f)) ; //goto next line + + if (buf[0] == '#') continue; //The line start with # is comment, skip + + // codesmell, only checks first char? + if (!isxdigit(buf[0])) { + PrintAndLogEx(FAILED, "File content error. '" _YELLOW_("%s")"' must include 12 HEX symbols", buf); continue; } + buf[12] = 0; + if (keyitems - keycnt < 2) { p = realloc(keyBlock, 6 * (keyitems += 64)); if (!p) { - PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); + PrintAndLogEx(FAILED, "Cannot allocate memory for defKeys"); free(keyBlock); + fclose(f); return PM3_EMALLOC; } keyBlock = p; } - PrintAndLogEx(NORMAL, "[%2d] key %s", keycnt, sprint_hex((keyBlock + 6 * keycnt), 6)); + memset(keyBlock + 6 * keycnt, 0, 6); + num_to_bytes(strtoll(buf, NULL, 16), 6, keyBlock + 6 * keycnt); + //PrintAndLogEx(NORMAL, "check key[%2d] %012" PRIx64, keycnt, bytes_to_num(keyBlock + 6*keycnt, 6)); keycnt++; - } else if (clen == 1) { - if (ctmp == 't') { transferToEml = 1; continue; } - if (ctmp == 'd') { createDumpFile = 1; continue; } - } else { - // May be a dic file - if (param_getstr(Cmd, i, filename, sizeof(filename)) >= FILE_PATH_SIZE) { - PrintAndLogEx(FAILED, "File name too long"); - free(keyBlock); - return PM3_EINVARG; - } - - char *dict_path; - int res = searchFile(&dict_path, DICTIONARIES_SUBDIR, filename, ".dic", false); - if (res != PM3_SUCCESS) { - free(keyBlock); - return PM3_EFILE; - } - f = fopen(dict_path, "r"); - if (!f) { - PrintAndLogEx(FAILED, "File: " _YELLOW_("%s") ": not found or locked.", dict_path); - free(dict_path); - free(keyBlock); - return PM3_EFILE; - } - free(dict_path); - - // load keys from dictionary file - while (fgets(buf, sizeof(buf), f)) { - if (strlen(buf) < 12 || buf[11] == '\n') - continue; - - while (fgetc(f) != '\n' && !feof(f)) ; //goto next line - - if (buf[0] == '#') continue; //The line start with # is comment, skip - - // codesmell, only checks first char? - if (!isxdigit(buf[0])) { - PrintAndLogEx(FAILED, "File content error. '" _YELLOW_("%s")"' must include 12 HEX symbols", buf); - continue; - } - - buf[12] = 0; - - if (keyitems - keycnt < 2) { - p = realloc(keyBlock, 6 * (keyitems += 64)); - if (!p) { - PrintAndLogEx(FAILED, "Cannot allocate memory for defKeys"); - free(keyBlock); - fclose(f); - return PM3_EMALLOC; - } - keyBlock = p; - } - memset(keyBlock + 6 * keycnt, 0, 6); - num_to_bytes(strtoll(buf, NULL, 16), 6, keyBlock + 6 * keycnt); - //PrintAndLogEx(NORMAL, "check key[%2d] %012" PRIx64, keycnt, bytes_to_num(keyBlock + 6*keycnt, 6)); - keycnt++; - memset(buf, 0, sizeof(buf)); - } - fclose(f); - PrintAndLogEx(SUCCESS, "Loaded %2d keys from " _YELLOW_("%s"), keycnt, filename); + memset(buf, 0, sizeof(buf)); } + fclose(f); + PrintAndLogEx(SUCCESS, "Loaded %2d keys from " _YELLOW_("%s"), keycnt, filename); } + uint64_t key64 = 0; + if (keycnt == 0) { PrintAndLogEx(INFO, "No key specified, trying default keys"); for (; keycnt < ARRAYLEN(g_mifare_default_keys); keycnt++) @@ -3170,6 +3258,7 @@ static int CmdHF14AMfChk(const char *Cmd) { } // create/initialize key storage structure + sector_t *e_sector = NULL; int32_t res = initSectorTable(&e_sector, SectorsCnt); if (res != SectorsCnt) { free(keyBlock); @@ -3179,8 +3268,8 @@ static int CmdHF14AMfChk(const char *Cmd) { uint8_t trgKeyType = 0; uint16_t max_keys = keycnt > KEYS_IN_BLOCK ? KEYS_IN_BLOCK : keycnt; - // time - uint64_t t1 = msclock(); + PrintAndLogEx(INFO, "Start check for keys..."); + PrintAndLogEx(INFO, "." NOLF); // fast push mode conn.block_after_ACK = true; @@ -3188,12 +3277,15 @@ static int CmdHF14AMfChk(const char *Cmd) { // clear trace log by first check keys call only bool clearLog = true; + // time + uint64_t t1 = msclock(); + // check keys. for (trgKeyType = (keyType == 2) ? 0 : keyType; trgKeyType < 2; (keyType == 2) ? (++trgKeyType) : (trgKeyType = 2)) { // loop sectors but block is used as to keep track of from which blocks to test int b = blockNo; - for (i = 0; i < SectorsCnt; ++i) { + for (int i = 0; i < SectorsCnt; ++i) { // skip already found keys. if (e_sector[i].foundKey[trgKeyType]) continue; @@ -3230,7 +3322,7 @@ static int CmdHF14AMfChk(const char *Cmd) { // loop sectors but block is used as to keep track of from which blocks to test int b = blockNo; - for (i = 0; i < SectorsCnt; i++) { + for (int i = 0; i < SectorsCnt; i++) { // KEY A but not KEY B if (e_sector[i].foundKey[0] && !e_sector[i].foundKey[1]) { @@ -3281,7 +3373,7 @@ out: // fast push mode conn.block_after_ACK = true; uint8_t block[16] = {0x00}; - for (i = 0; i < SectorsCnt; ++i) { + for (int i = 0; i < SectorsCnt; ++i) { uint8_t blockno = FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1; mfEmlGetMem(block, blockno, 1); @@ -3375,131 +3467,146 @@ void readerAttack(sector_t *k_sector, uint8_t k_sectorsCount, nonces_t data, boo } static int CmdHF14AMfSim(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf sim", + "Simulate MIFARE card", + "hf mf sim --mini --> MIFARE Mini\n" + "hf mf sim --1k --> MIFARE Classic 1k (default)\n" + "hf mf sim --1k -u 0a0a0a0a --> MIFARE Classic 1k with 4b UID\n" + "hf mf sim --1k -u 11223344556677 --> MIFARE Classic 1k with 7b UID\n" + "hf mf sim --1k -u 11223344 -i --crack --> Perform reader attack in interactive mode\n" + "hf mf sim --2k --> MIFARE 2k\n" + "hf mf sim --4k --> MIFARE 4k"); + + void *argtable[] = { + arg_param_begin, + arg_str0("u", "uid", "", "UID 4,7 or 10bytes. If not specified, the UID 4b/7b from emulator memory will be used"), + arg_lit0(NULL, "mini", "MIFARE Classic Mini / S20"), + arg_lit0(NULL, "1k", "MIFARE Classic 1k / S50"), + arg_lit0(NULL, "2k", "MIFARE Classic/Plus 2k"), + arg_lit0(NULL, "4k", "MIFARE Classic 4k / S70"), + arg_str0(NULL, "atqa", "", "Provide explicit ATQA (2 bytes, overrides option t)"), + arg_str0(NULL, "sak", "", "Provide explicit SAK (1 bytes, overrides option t)"), + arg_int0("n", "num", " ", "Automatically exit simulation after blocks have been read by reader. 0 = infinite"), + arg_lit0("i", "interactive", "Console will not be returned until simulation finishes or is aborted"), + arg_lit0(NULL, "crack", "Performs the 'reader attack', nr/ar attack against a reader"), + arg_lit0("e", "emukeys", "Fill simulator keys from found keys"), + arg_lit0("v", "verbose", "verbose output"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); - uint8_t uid[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - uint8_t atqa[2] = {0, 0}; - int atqalen = 0; - uint8_t sak[1] = {0}; - int saklen = 0; - uint8_t exitAfterNReads = 0; uint16_t flags = 0; - int uidlen = 0; - uint8_t cmdp = 0; - bool errors = false, verbose = false, setEmulatorMem = false; - nonces_t data[1]; - char csize[13] = { 0 }; - char uidsize[8] = { 0 }; - sector_t *k_sector = NULL; - uint8_t k_sectorsCount = 40; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'e': - setEmulatorMem = true; - cmdp++; + int uidlen = 0; + uint8_t uid[10] = {0}; + CLIGetHexWithReturn(ctx, 1, uid, &uidlen); + + char uidsize[8] = {0}; + if (uidlen > 0) { + switch (uidlen) { + case 10: + flags |= FLAG_10B_UID_IN_DATA; + snprintf(uidsize, sizeof(uidsize), "10 byte"); break; - case 'h': - return usage_hf14_mfsim(); - case 'i': - flags |= FLAG_INTERACTIVE; - cmdp++; + case 7: + flags |= FLAG_7B_UID_IN_DATA; + snprintf(uidsize, sizeof(uidsize), "7 byte"); break; - case 'n': - exitAfterNReads = param_get8(Cmd, cmdp + 1); - cmdp += 2; - break; - case 't': - switch (param_get8(Cmd, cmdp + 1)) { - case 0: - flags |= FLAG_MF_MINI; - snprintf(csize, sizeof(csize), "MINI"); - k_sectorsCount = MIFARE_MINI_MAXSECTOR; - break; - case 1: - flags |= FLAG_MF_1K; - snprintf(csize, sizeof(csize), "1K"); - k_sectorsCount = MIFARE_1K_MAXSECTOR; - break; - case 2: - flags |= FLAG_MF_2K; - snprintf(csize, sizeof(csize), "2K with RATS"); - k_sectorsCount = MIFARE_2K_MAXSECTOR; - break; - case 4: - flags |= FLAG_MF_4K; - snprintf(csize, sizeof(csize), "4K"); - k_sectorsCount = MIFARE_4K_MAXSECTOR; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter for option t"); - errors = true; - break; - } - cmdp += 2; - break; - case 'a': - param_gethex_ex(Cmd, cmdp + 1, atqa, &atqalen); - if (atqalen >> 1 != 2) { - PrintAndLogEx(WARNING, "Wrong ATQA length"); - errors = true; - break; - } - flags |= FLAG_FORCED_ATQA; - cmdp += 2; - break; - case 's': - param_gethex_ex(Cmd, cmdp + 1, sak, &saklen); - if (saklen >> 1 != 1) { - PrintAndLogEx(WARNING, "Wrong SAK length"); - errors = true; - break; - } - flags |= FLAG_FORCED_SAK; - cmdp += 2; - break; - case 'u': - param_gethex_ex(Cmd, cmdp + 1, uid, &uidlen); - uidlen >>= 1; - switch (uidlen) { - case 10: - flags |= FLAG_10B_UID_IN_DATA; - snprintf(uidsize, sizeof(uidsize), "10 byte"); - break; - case 7: - flags |= FLAG_7B_UID_IN_DATA; - snprintf(uidsize, sizeof(uidsize), "7 byte"); - break; - case 4: - flags |= FLAG_4B_UID_IN_DATA; - snprintf(uidsize, sizeof(uidsize), "4 byte"); - break; - default: - return usage_hf14_mfsim(); - } - cmdp += 2; - break; - case 'v': - verbose = true; - cmdp++; - break; - case 'x': - flags |= FLAG_NR_AR_ATTACK; - cmdp++; + case 4: + flags |= FLAG_4B_UID_IN_DATA; + snprintf(uidsize, sizeof(uidsize), "4 byte"); break; default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; + PrintAndLogEx(WARNING, "Invalid parameter for UID"); + return PM3_EINVARG; } } + + bool m0 = arg_get_lit(ctx, 2); + bool m1 = arg_get_lit(ctx, 3); + bool m2 = arg_get_lit(ctx, 4); + bool m4 = arg_get_lit(ctx, 5); + + int atqalen = 0; + uint8_t atqa[2] = {0}; + CLIGetHexWithReturn(ctx, 6, atqa, &atqalen); + + int saklen = 0; + uint8_t sak[1] = {0}; + CLIGetHexWithReturn(ctx, 7, sak, &saklen); + + uint8_t exitAfterNReads = arg_get_u32_def(ctx, 8, 0); + + if (arg_get_lit(ctx, 9)) { + flags |= FLAG_INTERACTIVE; + } + + if (arg_get_lit(ctx, 10)) { + flags |= FLAG_NR_AR_ATTACK; + } + + bool setEmulatorMem = arg_get_lit(ctx, 11); + bool verbose = arg_get_lit(ctx, 12); + + CLIParserFree(ctx); + + nonces_t data[1]; + + sector_t *k_sector = NULL; + //Validations - if (errors) return usage_hf14_mfsim(); + if (atqalen > 0) { + if (atqalen != 2) { + PrintAndLogEx(WARNING, "Wrong ATQA length"); + return PM3_EINVARG; + + } + flags |= FLAG_FORCED_ATQA; + } + if (saklen > 0) { + if (saklen != 1) { + PrintAndLogEx(WARNING, "Wrong SAK length"); + return PM3_EINVARG; + + } + flags |= FLAG_FORCED_SAK; + } // Use UID, SAK, ATQA from EMUL, if uid not defined if ((flags & (FLAG_4B_UID_IN_DATA | FLAG_7B_UID_IN_DATA | FLAG_10B_UID_IN_DATA)) == 0) { flags |= FLAG_UID_IN_EMUL; } + uint8_t k_sectorsCount = 40; + char csize[13] = { 0 }; + + if ((m0 + m1 + m2 + m4) > 1) { + PrintAndLogEx(WARNING, "Only specify one MIFARE Type"); + return PM3_EINVARG; + } + + if (m0) { + flags |= FLAG_MF_MINI; + snprintf(csize, sizeof(csize), "MINI"); + k_sectorsCount = MIFARE_MINI_MAXSECTOR; + } else if (m1) { + flags |= FLAG_MF_1K; + snprintf(csize, sizeof(csize), "1K"); + k_sectorsCount = MIFARE_1K_MAXSECTOR; + } else if (m2) { + flags |= FLAG_MF_2K; + snprintf(csize, sizeof(csize), "2K with RATS"); + k_sectorsCount = MIFARE_2K_MAXSECTOR; + } else if (m4) { + flags |= FLAG_MF_4K; + snprintf(csize, sizeof(csize), "4K"); + k_sectorsCount = MIFARE_4K_MAXSECTOR; + } else { + PrintAndLogEx(WARNING, "Please specify a MIFARE Type"); + return PM3_EINVARG; + } + PrintAndLogEx(INFO, _YELLOW_("MIFARE %s") " | %s UID " _YELLOW_("%s") "" , csize , uidsize @@ -3542,8 +3649,6 @@ static int CmdHF14AMfSim(const char *Cmd) { } showSectorTable(k_sector, k_sectorsCount); } - - k_sectorsCount = MIFARE_4K_MAXSECTOR; return PM3_SUCCESS; } @@ -3796,12 +3901,11 @@ int CmdHF14AMfELoad(const char *Cmd) { printMFUdumpEx(mfu_dump, mfu_dump->pages + 1, 0); // update expected blocks to match converted data. - if (numBlocks != datalen / 4) { - numBlocks = datalen / 4; - } + numBlocks = datalen / 4; } PrintAndLogEx(INFO, "Uploading to emulator memory"); + PrintAndLogEx(INFO, "." NOLF); // fast push mode conn.block_after_ACK = true; @@ -3835,7 +3939,7 @@ int CmdHF14AMfELoad(const char *Cmd) { return PM3_SUCCESS; } } else { - PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf mf sim h`")); + PrintAndLogEx(HINT, "You are ready to simulate. See " _YELLOW_("`hf mf sim -h`")); // MFC if ((blockNum != numBlocks)) { PrintAndLogEx(WARNING, "Error, file content, Only loaded %d blocks, must be %d blocks into emulator memory", blockNum, numBlocks); @@ -4139,8 +4243,8 @@ static int CmdHF14AMfCWipe(const char *cmd) { CLIParserContext *ctx; CLIParserInit(&ctx, "hf mf cwipe", "Wipe gen1 magic chinese card. Set UID/ATQA/SAK/Data/Keys/Access to default values.", - "hf mf cwipe -> wipe card\n" - "hf mf cwipe -u 09080706 -a 0004 -s 18 -> set UID, ATQA and SAK and wipe card"); + "hf mf cwipe --> wipe card\n" + "hf mf cwipe -u 09080706 -a 0004 -s 18 --> set UID, ATQA and SAK and wipe card"); void *argtable[] = { arg_param_begin, @@ -4226,6 +4330,10 @@ static int CmdHF14AMfCLoad(const char *Cmd) { if (fillFromEmulator) { + + PrintAndLogEx(INFO, "Start upload to emulator memory"); + PrintAndLogEx(INFO, "." NOLF); + for (int blockNum = 0; blockNum < 16 * 4; blockNum += 1) { int flags = 0; uint8_t buf8[16] = {0x00}; @@ -4268,8 +4376,7 @@ static int CmdHF14AMfCLoad(const char *Cmd) { } if (res) { - if (data) - free(data); + free(data); return PM3_EFILE; } @@ -4281,6 +4388,7 @@ static int CmdHF14AMfCLoad(const char *Cmd) { } PrintAndLogEx(INFO, "Copying to magic card"); + PrintAndLogEx(INFO, "." NOLF); int blockNum = 0; int flags = 0; @@ -4494,6 +4602,7 @@ static int CmdHF14AMfCSave(const char *Cmd) { if (fillEmulator) { PrintAndLogEx(INFO, "uploading to emulator memory"); + PrintAndLogEx(INFO, "." NOLF); // fast push mode conn.block_after_ACK = true; for (i = 0; i < numblocks; i += 5) { @@ -4550,6 +4659,7 @@ static int CmdHF14AMfCView(const char *Cmd) { if (errors) return usage_hf14_cview(); PrintAndLogEx(SUCCESS, "View magic MIFARE " _GREEN_("%cK"), ctmp); + PrintAndLogEx(INFO, "." NOLF); uint8_t *dump = calloc(bytes, sizeof(uint8_t)); if (!dump) { @@ -4707,44 +4817,30 @@ static int CmdHf14AMfNack(const char *Cmd) { } static int CmdHF14AMfice(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mf ice", + "Collect MIFARE Classic nonces to file", + "hf mf ice\n" + "hf mf ice -f nonces.bin"); - uint8_t blockNo = 0; - uint8_t keyType = 0; - uint8_t trgBlockNo = 0; - uint8_t trgKeyType = 1; - bool slow = false; - bool initialize = true; - bool acquisition_completed = false; - uint8_t cmdp = 0; - uint32_t flags = 0; - uint32_t total_num_nonces = 0; - char ctmp; - char filename[FILE_PATH_SIZE], *fptr; - FILE *fnonces = NULL; - PacketResponseNG resp; + void *argtable[] = { + arg_param_begin, + arg_str0("f", "file", "", "filename of nonce dump"), + arg_u64_0(NULL, "limit", "", "nonces to be collected"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); - uint32_t part_limit = 3000; - uint32_t limit = 50000; + int fnlen = 0; + char filename[FILE_PATH_SIZE] = {0}; + CLIParamStrToBuf(arg_get_str(ctx, 1), (uint8_t *)filename, FILE_PATH_SIZE, &fnlen); - while ((ctmp = param_getchar(Cmd, cmdp))) { - switch (tolower(ctmp)) { - case 'h': - return usage_hf14_ice(); - case 'f': - param_getstr(Cmd, cmdp + 1, filename, FILE_PATH_SIZE); - cmdp++; - break; - case 'l': - limit = param_get32ex(Cmd, cmdp + 1, 50000, 10); - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", ctmp); - usage_hf14_ice(); - return PM3_ESOFT; - } - cmdp++; - } + uint32_t limit = arg_get_u32_def(ctx, 2, 50000); + + CLIParserFree(ctx); + + // Validations + char *fptr; if (filename[0] == '\0') { fptr = GenerateFilename("hf-mf-", "-nonces.bin"); @@ -4754,8 +4850,21 @@ static int CmdHF14AMfice(const char *Cmd) { free(fptr); } + uint8_t blockNo = 0; + uint8_t keyType = 0; + uint8_t trgBlockNo = 0; + uint8_t trgKeyType = 1; + bool slow = false; + bool initialize = true; + bool acquisition_completed = false; + uint32_t total_num_nonces = 0; + PacketResponseNG resp; + + uint32_t part_limit = 3000; + PrintAndLogEx(NORMAL, "Collecting "_YELLOW_("%u")" nonces \n", limit); + FILE *fnonces = NULL; if ((fnonces = fopen(filename, "wb")) == NULL) { PrintAndLogEx(WARNING, "Could not create file " _YELLOW_("%s"), filename); return PM3_EFILE; @@ -4771,7 +4880,7 @@ static int CmdHF14AMfice(const char *Cmd) { break; } - flags = 0; + uint32_t flags = 0; flags |= initialize ? 0x0001 : 0; flags |= slow ? 0x0002 : 0; clearCommandBuffer(); @@ -4869,25 +4978,41 @@ static int CmdHF14AMfMAD(const char *Cmd) { CLIExecWithReturn(ctx, Cmd, argtable, true); bool verbose = arg_get_lit(ctx, 1); uint8_t aid[2] = {0}; - int aidlen; + int aidlen = 0; CLIGetHexWithReturn(ctx, 2, aid, &aidlen); - uint8_t key[6] = {0}; - int keylen; - CLIGetHexWithReturn(ctx, 3, key, &keylen); + uint8_t userkey[6] = {0}; + int keylen = 0; + CLIGetHexWithReturn(ctx, 3, userkey, &keylen); bool keyB = arg_get_lit(ctx, 4); bool swapmad = arg_get_lit(ctx, 5); bool decodeholder = arg_get_lit(ctx, 6); CLIParserFree(ctx); - if (aidlen != 2 && !decodeholder && keylen > 0) { - PrintAndLogEx(WARNING, "Using default MAD keys instead"); - } - uint8_t sector0[16 * 4] = {0}; uint8_t sector10[16 * 4] = {0}; - if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0)) { - PrintAndLogEx(ERR, "error, read sector 0. card don't have MAD or don't have MAD on default keys"); + + bool got_first = true; + if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0) != PM3_SUCCESS) { + PrintAndLogEx(WARNING, "error, read sector 0. card don't have MAD or don't have MAD on default keys"); + got_first = false; + } else { + PrintAndLogEx(INFO, "Authentication ( " _GREEN_("OK") " )"); + } + + // User supplied key + if (got_first == false && keylen == 6) { + PrintAndLogEx(INFO, "Trying user specified key..."); + if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, userkey, sector0) != PM3_SUCCESS) { + PrintAndLogEx(ERR, "error, read sector 0. card don't have MAD or don't the custom key is wrong"); + } else { + PrintAndLogEx(INFO, "Authentication ( " _GREEN_("OK") " )"); + got_first = true; + } + } + + // Both default and user supplied key failed + if (got_first == false) { return PM3_ESOFT; } @@ -4921,7 +5046,7 @@ static int CmdHF14AMfMAD(const char *Cmd) { // user specified key if (keylen == 6) { - memcpy(akey, key, 6); + memcpy(akey, userkey, 6); } uint16_t aaid = 0x0004; diff --git a/client/src/cmdhfmf.h b/client/src/cmdhfmf.h index 5325a7956..a4258bc67 100644 --- a/client/src/cmdhfmf.h +++ b/client/src/cmdhfmf.h @@ -24,4 +24,6 @@ void readerAttack(sector_t *k_sector, uint8_t k_sectorsCount, nonces_t data, boo void printKeyTable(uint8_t sectorscnt, sector_t *e_sector); void printKeyTableEx(uint8_t sectorscnt, sector_t *e_sector, uint8_t start_sector); void printKeyTable_fast(uint8_t sectorscnt, icesector_t *e_sector, uint64_t bar, uint64_t foo); + +int mfc_ev1_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature, int signature_len); #endif diff --git a/client/src/cmdhfmfdes.c b/client/src/cmdhfmfdes.c index f2282dbfd..c208367bc 100644 --- a/client/src/cmdhfmfdes.c +++ b/client/src/cmdhfmfdes.c @@ -1306,11 +1306,12 @@ static int desfire_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signat const ecdsa_publickey_t nxp_desfire_public_keys[] = { {"NTAG424DNA, DESFire EV2", "048A9B380AF2EE1B98DC417FECC263F8449C7625CECE82D9B916C992DA209D68422B81EC20B65A66B5102A61596AF3379200599316A00A1410"}, {"NTAG413DNA, DESFire EV1", "04BB5D514F7050025C7D0F397310360EEC91EAF792E96FC7E0F496CB4E669D414F877B7B27901FE67C2E3B33CD39D1C797715189AC951C2ADD"}, - {"DESFire EV2", "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A"}, - {"DESFire EV3", "041DB46C145D0A36539C6544BD6D9B0AA62FF91EC48CBC6ABAE36E0089A46F0D08C8A715EA40A63313B92E90DDC1730230E0458A33276FB743"}, + {"DESFire EV2", "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3A"}, + {"DESFire EV3", "041DB46C145D0A36539C6544BD6D9B0AA62FF91EC48CBC6ABAE36E0089A46F0D08C8A715EA40A63313B92E90DDC1730230E0458A33276FB743"}, {"NTAG424DNA, NTAG424DNATT, DESFire Light EV2", "04B304DC4C615F5326FE9383DDEC9AA892DF3A57FA7FFB3276192BC0EAA252ED45A865E3B093A3D0DCE5BE29E92F1392CE7DE321E3E5C52B3B"}, - {"DESFire Light", "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D"}, - {"MIFARE Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"} + {"DESFire Light", "040E98E117AAA36457F43173DC920A8757267F44CE4EC5ADD3C54075571AEBBF7B942A9774A1D94AD02572427E5AE0A2DD36591B1FB34FCF3D"}, + {"MIFARE Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"}, + {"MIFARE Pluc Evx", "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"}, }; @@ -2256,7 +2257,17 @@ static int desfire_authenticate(int cmdAuthMode, int cmdAuthAlgo, uint8_t *aid, } static int CmdHF14ADesGetUID(const char *Cmd) { - (void)Cmd; + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes getuid", + "Get UID from a MIFARE DESfire tag", + "hf mfdes getuid"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); uint8_t uid[16] = {0}; int res = handler_desfire_getuid(uid); @@ -3438,7 +3449,18 @@ static int CmdHF14ADesFormatPICC(const char *Cmd) { } static int CmdHF14ADesInfo(const char *Cmd) { - (void)Cmd; // Cmd is not used so far + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes info", + "Get info from MIFARE DESfire tags", + "hf mfdes info"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); + DropFieldDesfire(); mfdes_info_res_t info; @@ -3862,8 +3884,18 @@ static int CmdHF14ADesDump(const char *Cmd) { } static int CmdHF14ADesEnumApplications(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes enum", + "Enumerate all AID's on MIFARE DESfire tag", + "hf mfdes enum"); + + void *argtable[] = { + arg_param_begin, + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + CLIParserFree(ctx); - (void)Cmd; // Cmd is not used so far DropFieldDesfire(); uint8_t aid[3] = {0}; @@ -3952,6 +3984,63 @@ static int CmdHF14ADesEnumApplications(const char *Cmd) { return PM3_SUCCESS; } +static int CmdHF14ADesBruteApps(const char *Cmd) { + CLIParserContext *ctx; + CLIParserInit(&ctx, "hf mfdes bruteaid", + "Recover AIDs by bruteforce.\n" + "WARNING: This command takes a long time", + "hf mfdes bruteaid -> Search all apps\n" + "hf mfdes bruteaid -s F0000F -i 16 -> Search MAD range manually"); + + void *argtable[] = { + arg_param_begin, + arg_strx0("s", "start", "", "Starting App ID as hex bytes (3 bytes,big endian)"), + arg_strx0("e", "end", "", "Last App ID as hex bytes (3 bytes,big endian)"), + arg_int0("i", "step", "", "Increment step when bruteforcing (decimal integer)"), + arg_lit0("m", "mad", "Only bruteforce the MAD range"), + arg_param_end + }; + CLIExecWithReturn(ctx, Cmd, argtable, true); + uint8_t startAid[3] = {0}; + uint8_t endAid[3] = {0xFF, 0xFF, 0xFF}; + int startLen = 0; + int endLen = 0; + CLIGetHexWithReturn(ctx, 1, startAid, &startLen); + CLIGetHexWithReturn(ctx, 2, endAid, &endLen); + uint32_t idIncrement = arg_get_int_def(ctx, 3, 1); + bool mad = arg_get_lit(ctx, 4); + CLIParserFree(ctx); + // TODO: We need to check the tag version, EV1 should stop after 26 apps are found + if (mad) { + idIncrement = 0x10; + startAid[0] = 0xF0; + startAid[1] = 0x00; + startAid[2] = 0x0F; + } + uint32_t idStart = le24toh(startAid); + uint32_t idEnd = le24toh(endAid); + PrintAndLogEx(INFO, "Enumerating through all AIDs manually, this will take a while!"); + for (uint32_t id = idStart; id <= idEnd && id >= idStart; id += idIncrement) { + if (kbd_enter_pressed()) break; + int progress = ((id - idStart) * 100) / ((idEnd - idStart)); + PrintAndLogEx(INPLACE, "Progress: %d %%, current AID: %06X", progress, id); + uint8_t appId[3] = {0}; + htole24(id, appId); + sAPDU apdu = {0x90, MFDES_SELECT_APPLICATION, 0x00, 0x00, 0x03, appId}; //0x5a + uint16_t sw = 0; + uint8_t data[255 * 5] = {0x00}; + uint32_t resplen = 0; + DESFIRESendApdu(!tag->rf_field_on, true, apdu, data, sizeof(data), &resplen, &sw); + if (sw == status(MFDES_S_OPERATION_OK)) { + printf("\33[2K\r"); // clear current line before printing + PrintAndLogEx(SUCCESS, "Got new APPID %06X", id); + } + } + PrintAndLogEx(SUCCESS, "Done"); + DropFieldDesfire(); + return PM3_SUCCESS; +} + static int CmdHF14ADesChangeKey(const char *Cmd) { //DropFieldDesfire(); // NR DESC KEYLENGHT @@ -4893,6 +4982,7 @@ static command_t CommandTable[] = { // {"ndef", CmdHF14aDesNDEF, IfPm3Iso14443a, "Prints NDEF records from card"}, // {"mad", CmdHF14aDesMAD, IfPm3Iso14443a, "Prints MAD records from card"}, {"-----------", CmdHelp, IfPm3Iso14443a, "----------------------- " _CYAN_("AID") " -----------------------"}, + {"bruteaid", CmdHF14ADesBruteApps, IfPm3Iso14443a, "Recover AIDs by bruteforce"}, {"createaid", CmdHF14ADesCreateApp, IfPm3Iso14443a, "Create Application ID"}, {"deleteaid", CmdHF14ADesDeleteApp, IfPm3Iso14443a, "Delete Application ID"}, {"selectaid", CmdHF14ADesSelectApp, IfPm3Iso14443a, "Select Application ID"}, diff --git a/client/src/cmdhfmfhard.c b/client/src/cmdhfmfhard.c index d75e6b0a3..fc9e00476 100644 --- a/client/src/cmdhfmfhard.c +++ b/client/src/cmdhfmfhard.c @@ -575,7 +575,7 @@ static void init_nonce_memory(void) { nonces[i].num = 0; nonces[i].Sum = 0; nonces[i].first = NULL; - for (uint16_t j = 0; j < NUM_SUMS; j++) { + for (uint8_t j = 0; j < NUM_SUMS; j++) { nonces[i].sum_a8_guess[j].sum_a8_idx = j; nonces[i].sum_a8_guess[j].prob = 0.0; } @@ -660,10 +660,14 @@ static double p_hypergeometric(uint16_t i_K, uint16_t n, uint16_t k) { if (n - k == N - K) { // special case. The published recursion below would fail with a divide by zero exception double log_result = 0.0; for (int16_t i = k + 1; i <= n; i++) { - log_result += log(i); + if (i) { + log_result += log(i); + } } for (int16_t i = K + 1; i <= N; i++) { - log_result -= log(i); + if (i) { + log_result -= log(i); + } } return exp(log_result); } else { // recursion @@ -679,7 +683,7 @@ static float sum_probability(uint16_t i_K, uint16_t n, uint16_t k) { double p_T_is_k_when_S_is_K = p_hypergeometric(i_K, n, k); double p_S_is_K = p_K[i_K]; double p_T_is_k = 0; - for (uint16_t i = 0; i < NUM_SUMS; i++) { + for (uint8_t i = 0; i < NUM_SUMS; i++) { p_T_is_k += p_K[i] * p_hypergeometric(i, n, k); } return (p_T_is_k_when_S_is_K * p_S_is_K / p_T_is_k); @@ -1044,7 +1048,7 @@ static void estimate_sum_a8(void) { if (first_byte_num == 256) { for (uint16_t i = 0; i < 256; i++) { if (nonces[i].sum_a8_guess_dirty) { - for (uint16_t j = 0; j < NUM_SUMS; j++) { + for (uint8_t j = 0; j < NUM_SUMS; j++) { uint16_t sum_a8_idx = nonces[i].sum_a8_guess[j].sum_a8_idx; nonces[i].sum_a8_guess[j].prob = sum_probability(sum_a8_idx, nonces[i].num, nonces[i].Sum); } @@ -1102,7 +1106,7 @@ static int read_nonce_file(char *filename) { sprintf(progress_string, "Target Block=%d, Keytype=%c", trgBlockNo, trgKeyType == 0 ? 'A' : 'B'); hardnested_print_progress(num_acquired_nonces, progress_string, (float)(1LL << 47), 0); - for (uint16_t i = 0; i < NUM_SUMS; i++) { + for (uint8_t i = 0; i < NUM_SUMS; i++) { if (first_byte_Sum == sums[i]) { first_byte_Sum = i; break; @@ -1145,21 +1149,27 @@ __attribute__((force_align_arg_pointer)) // for (uint16_t bitflip = 0x001; bitflip < 0x200; bitflip++) { for (uint16_t bitflip_idx = 0; bitflip_idx < num_1st_byte_effective_bitflips; bitflip_idx++) { uint16_t bitflip = all_effective_bitflip[bitflip_idx]; - if (time_budget & timeout()) { + if (time_budget && timeout()) { #if defined (DEBUG_REDUCTION) PrintAndLogEx(NORMAL, "break at bitflip_idx %d...", bitflip_idx); #endif return NULL; } for (uint16_t i = first_byte; i <= last_byte; i++) { + if (nonces[i].BitFlips[bitflip] == 0 && nonces[i].BitFlips[bitflip ^ 0x100] == 0 && nonces[i].first != NULL && nonces[i ^ (bitflip & 0xff)].first != NULL) { + uint8_t parity1 = (nonces[i].first->par_enc) >> 3; // parity of first byte uint8_t parity2 = (nonces[i ^ (bitflip & 0xff)].first->par_enc) >> 3; // parity of nonce with bits flipped + if ((parity1 == parity2 && !(bitflip & 0x100)) // bitflip || (parity1 != parity2 && (bitflip & 0x100))) { // not bitflip + nonces[i].BitFlips[bitflip] = 1; + for (odd_even_t odd_even = EVEN_STATE; odd_even <= ODD_STATE; odd_even++) { + if (bitflip_bitarrays[odd_even][bitflip] != NULL) { uint32_t old_count = nonces[i].num_states_bitarray[odd_even]; nonces[i].num_states_bitarray[odd_even] = count_bitarray_AND(nonces[i].states_bitarray[odd_even], bitflip_bitarrays[odd_even][bitflip]); @@ -1181,7 +1191,7 @@ __attribute__((force_align_arg_pointer)) if (hardnested_stage & CHECK_2ND_BYTES) { for (uint16_t bitflip_idx = num_1st_byte_effective_bitflips; bitflip_idx < num_all_effective_bitflips; bitflip_idx++) { uint16_t bitflip = all_effective_bitflip[bitflip_idx]; - if (time_budget & timeout()) { + if (time_budget && timeout()) { #if defined (DEBUG_REDUCTION) PrintAndLogEx(NORMAL, "break at bitflip_idx %d...", bitflip_idx); #endif @@ -1344,7 +1354,7 @@ static void simulate_acquire_nonces(void) { if (first_byte_num == 256) { if (hardnested_stage == CHECK_1ST_BYTES) { - for (uint16_t i = 0; i < NUM_SUMS; i++) { + for (uint8_t i = 0; i < NUM_SUMS; i++) { if (first_byte_Sum == sums[i]) { first_byte_Sum = i; break; @@ -1471,7 +1481,7 @@ static int acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ if (first_byte_num == 256) { if (hardnested_stage == CHECK_1ST_BYTES) { - for (uint16_t i = 0; i < NUM_SUMS; i++) { + for (uint8_t i = 0; i < NUM_SUMS; i++) { if (first_byte_Sum == sums[i]) { first_byte_Sum = i; break; @@ -1662,7 +1672,7 @@ static inline bool bitflips_match(uint8_t byte, uint32_t state, odd_even_t odd_e if (!possible) { #ifdef DEBUG_KEY_ELIMINATION if (!quiet && known_target_key != -1 && state == test_state[odd_even]) { - PrintAndLogEx(NORMAL, "Initial state lists: %s test state eliminated by bitflip property.\n", odd_even == EVEN_STATE ? "even" : "odd"); + PrintAndLogEx(INFO, "Initial state lists: %s test state eliminated by bitflip property.", odd_even == EVEN_STATE ? "even" : "odd"); sprintf(failstr, "Initial %s Byte Bitflip property", odd_even == EVEN_STATE ? "even" : "odd"); } #endif @@ -1785,14 +1795,14 @@ static void add_matching_states(statelist_t *cands, uint8_t part_sum_a0, uint8_t static statelist_t *add_more_candidates(void) { statelist_t *new_candidates; if (candidates == NULL) { - candidates = (statelist_t *)malloc(sizeof(statelist_t)); + candidates = (statelist_t *)calloc(sizeof(statelist_t), sizeof(uint8_t)); new_candidates = candidates; } else { new_candidates = candidates; while (new_candidates->next != NULL) { new_candidates = new_candidates->next; } - new_candidates = new_candidates->next = (statelist_t *)malloc(sizeof(statelist_t)); + new_candidates = new_candidates->next = (statelist_t *)calloc(sizeof(statelist_t), sizeof(uint8_t)); } new_candidates->next = NULL; new_candidates->len[ODD_STATE] = 0; @@ -1807,14 +1817,15 @@ static void add_bitflip_candidates(uint8_t byte) { for (odd_even_t odd_even = EVEN_STATE; odd_even <= ODD_STATE; odd_even++) { uint32_t worstcase_size = nonces[byte].num_states_bitarray[odd_even] + 1; - candidates1->states[odd_even] = (uint32_t *)malloc(sizeof(uint32_t) * worstcase_size); + candidates1->states[odd_even] = (uint32_t *)calloc(worstcase_size, sizeof(uint32_t)); if (candidates1->states[odd_even] == NULL) { - PrintAndLogEx(ERR, "Out of memory error in add_bitflip_candidates().\n"); + PrintAndLogEx(ERR, "Out of memory error in add_bitflip_candidates()"); exit(4); } bitarray_to_list(byte, nonces[byte].states_bitarray[odd_even], candidates1->states[odd_even], &(candidates1->len[odd_even]), odd_even); + // slim down the allocated memory. if (candidates1->len[odd_even] + 1 < worstcase_size) { candidates1->states[odd_even] = realloc(candidates1->states[odd_even], sizeof(uint32_t) * (candidates1->len[odd_even] + 1)); } @@ -2073,6 +2084,8 @@ static void free_candidates_memory(statelist_t *sl) { return; free_candidates_memory(sl->next); + sl->len[0] = 0; + sl->len[1] = 0; free(sl); } @@ -2170,6 +2183,9 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc get_SIMD_instruction_set(instr_set); PrintAndLogEx(SUCCESS, "Using %s SIMD core.", instr_set); + memset(part_sum_count, 0, sizeof(part_sum_count)); + real_sum_a8 = 0; + srand((unsigned) time(NULL)); brute_force_per_second = brute_force_benchmark(); write_stats = false; diff --git a/client/src/cmdhfmfp.c b/client/src/cmdhfmfp.c index 46d7bf0cf..33991bb27 100644 --- a/client/src/cmdhfmfp.c +++ b/client/src/cmdhfmfp.c @@ -163,7 +163,8 @@ static int plus_print_signature(uint8_t *uid, uint8_t uidlen, uint8_t *signature // ref: MIFARE Plus EV1 Originality Signature Validation #define PUBLIC_PLUS_ECDA_KEYLEN 57 const ecdsa_publickey_t nxp_plus_public_keys[] = { - {"Mifare Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"} + {"MIFARE Plus EV1", "044409ADC42F91A8394066BA83D872FB1D16803734E911170412DDF8BAD1A4DADFD0416291AFE1C748253925DA39A5F39A1C557FFACD34C62E"}, + {"MIFARE Pluc Ev_x", "04BB49AE4447E6B1B6D21C098C1538B594A11A4A1DBF3D5E673DEACDEB3CC512D1C08AFA1A2768CE20A200BACD2DC7804CD7523A0131ABF607"} }; uint8_t i; @@ -276,7 +277,7 @@ static int CmdHFMFPInfo(const char *Cmd) { arg_param_end }; CLIExecWithReturn(ctx, Cmd, argtable, true); - + PrintAndLogEx(NORMAL, ""); PrintAndLogEx(INFO, "--- " _CYAN_("Tag Information") " ---------------------------"); PrintAndLogEx(INFO, "-------------------------------------------------------------"); @@ -1157,7 +1158,7 @@ static int CmdHFMFPChk(const char *Cmd) { int vpatternlen = 0; CLIGetHexWithReturn(ctx, 9, vpattern, &vpatternlen); if (vpatternlen > 0) { - if (vpatternlen > 0 && vpatternlen <= 2) { + if (vpatternlen <= 2) { startPattern = (vpattern[0] << 8) + vpattern[1]; } else { PrintAndLogEx(ERR, "Pattern must be 2-byte length."); diff --git a/client/src/cmdhfmfu.c b/client/src/cmdhfmfu.c index c69de053b..8227a55c0 100644 --- a/client/src/cmdhfmfu.c +++ b/client/src/cmdhfmfu.c @@ -13,6 +13,7 @@ #include "commonutil.h" #include "crypto/libpcrypto.h" #include "des.h" +#include "aes.h" #include "cmdhfmf.h" #include "cmdhf14a.h" #include "comms.h" @@ -23,7 +24,6 @@ #include "cliparser.h" #include "cmdmain.h" - #define MAX_UL_BLOCKS 0x0F #define MAX_ULC_BLOCKS 0x2F #define MAX_ULEV1a_BLOCKS 0x13 @@ -42,220 +42,6 @@ static int CmdHelp(const char *Cmd); -static int usage_hf_mfu_info(void) { - PrintAndLogEx(NORMAL, "It gathers information about the tag and tries to detect what kind it is."); - PrintAndLogEx(NORMAL, "Sometimes the tags are locked down, and you may need a key to be able to read the information"); - PrintAndLogEx(NORMAL, "The following tags can be identified:\n"); - PrintAndLogEx(NORMAL, "Ultralight, Ultralight-C, Ultralight EV1, NTAG 203, NTAG 210,"); - PrintAndLogEx(NORMAL, "NTAG 212, NTAG 213, NTAG 215, NTAG 216, NTAG I2C 1K & 2K"); - PrintAndLogEx(NORMAL, "my-d, my-d NFC, my-d move, my-d move NFC\n"); - PrintAndLogEx(NORMAL, "Usage: hf mfu info k l"); - PrintAndLogEx(NORMAL, " Options : "); - PrintAndLogEx(NORMAL, " k : (optional) key for authentication [UL-C 16bytes, EV1/NTAG 4bytes]"); - PrintAndLogEx(NORMAL, " l : (optional) swap entered key's endianness"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu info")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu info k 00112233445566778899AABBCCDDEEFF")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu info k AABBCCDD")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_dump(void) { - PrintAndLogEx(NORMAL, "Reads all pages from Ultralight, Ultralight-C, Ultralight EV1"); - PrintAndLogEx(NORMAL, "NTAG 203, NTAG 210, NTAG 212, NTAG 213, NTAG 215, NTAG 216"); - PrintAndLogEx(NORMAL, "and saves binary dump into the file " _YELLOW_("`filename.bin`") " or " _YELLOW_("`cardUID.bin`")); - PrintAndLogEx(NORMAL, "It autodetects card type.\n"); - PrintAndLogEx(NORMAL, "Usage: hf mfu dump k l f p q <#pages>"); - PrintAndLogEx(NORMAL, " Options :"); - PrintAndLogEx(NORMAL, " k : (optional) key for authentication [UL-C 16bytes, EV1/NTAG 4bytes]"); - PrintAndLogEx(NORMAL, " l : (optional) swap entered key's endianness"); - PrintAndLogEx(NORMAL, " f : " _YELLOW_("filename w/o .bin") " to save the dump as"); - PrintAndLogEx(NORMAL, " p : starting Page number to manually set a page to start the dump at"); - PrintAndLogEx(NORMAL, " q : number of Pages to manually set how many pages to dump"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu dump")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu dump f myfile")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu dump k 00112233445566778899AABBCCDDEEFF")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu dump k AABBCCDD")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_restore(void) { - PrintAndLogEx(NORMAL, "Restore dumpfile onto card."); - PrintAndLogEx(NORMAL, "Usage: hf mfu restore [h] [l] [s] k n "); - PrintAndLogEx(NORMAL, " Options :"); - PrintAndLogEx(NORMAL, " k : (optional) key for authentication [UL-C 16bytes, EV1/NTAG 4bytes]"); - PrintAndLogEx(NORMAL, " l : (optional) swap entered key's endianness"); - PrintAndLogEx(NORMAL, " s : (optional) enable special write UID " _BLUE_("-MAGIC TAG ONLY-")); - PrintAndLogEx(NORMAL, " e : (optional) enable special write version/signature " _BLUE_("-MAGIC NTAG 21* ONLY-")); - PrintAndLogEx(NORMAL, " r : (optional) use the password found in dumpfile to configure tag. requires " _YELLOW_("'e'") " parameter to work"); - PrintAndLogEx(NORMAL, " f : " _YELLOW_("filename w .bin") " to restore"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu restore s f myfile")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu restore k AABBCCDD s f myfile")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu restore k AABBCCDD s e r f myfile")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_rdbl(void) { - PrintAndLogEx(NORMAL, "Read a block and print. It autodetects card type.\n"); - PrintAndLogEx(NORMAL, "Usage: hf mfu rdbl b k l\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " b : block to read"); - PrintAndLogEx(NORMAL, " k : (optional) key for authentication [UL-C 16bytes, EV1/NTAG 4bytes]"); - PrintAndLogEx(NORMAL, " l : (optional) swap entered key's endianness"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu rdbl b 0")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu rdbl b 0 k 00112233445566778899AABBCCDDEEFF")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu rdbl b 0 k AABBCCDD")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_wrbl(void) { - PrintAndLogEx(NORMAL, "Write a block. It autodetects card type.\n"); - PrintAndLogEx(NORMAL, "Usage: hf mfu wrbl b d k l\n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " b : block to write"); - PrintAndLogEx(NORMAL, " d : block data - (8 or 32 hex symbols, 32 hex symbols will do a compatibility write)"); - PrintAndLogEx(NORMAL, " k : (optional) key for authentication [UL-C 16bytes, EV1/NTAG 4bytes]"); - PrintAndLogEx(NORMAL, " l : (optional) swap entered key's endianness"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu wrbl b 0 d 01234567")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu wrbl b 0 d 01234567 k AABBCCDD")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_eload(void) { - PrintAndLogEx(NORMAL, "It loads emul dump from the file " _YELLOW_("`filename.eml`")); - PrintAndLogEx(NORMAL, "Hint: See " _YELLOW_("`script run hf_mfu_dumptoemulator`") " to convert the .bin to the eml"); - PrintAndLogEx(NORMAL, "Usage: hf mfu eload u [numblocks]"); - PrintAndLogEx(NORMAL, " Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " u : UL (required)"); - PrintAndLogEx(NORMAL, " [filename] : without `.eml` (required)"); - PrintAndLogEx(NORMAL, " numblocks : number of blocks to load from eml file (optional)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu eload u filename")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu eload u filename 57")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_sim(void) { - PrintAndLogEx(NORMAL, "\nEmulating Ultralight tag from emulator memory\n"); - PrintAndLogEx(NORMAL, "\nBe sure to load the emulator memory first!\n"); - PrintAndLogEx(NORMAL, "Usage: hf mfu sim t 7 u [n ]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " t 7 : 7 = NTAG or Ultralight sim (required)"); - PrintAndLogEx(NORMAL, " n : exit simulation after blocks have been read by reader. 0 = infinite (optional)"); - PrintAndLogEx(NORMAL, " u : 4 or 7 byte UID (optional)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu sim t 7")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu sim t 7 u 1122344556677")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu sim t 7 u 1122344556677 n 5")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_ucauth(void) { - PrintAndLogEx(NORMAL, "Tests 3DES password on Mifare Ultralight-C tag."); - PrintAndLogEx(NORMAL, "If password is not specified, a set of known defaults will be tested."); - PrintAndLogEx(NORMAL, "Usage: hf mfu cauth [k] "); - PrintAndLogEx(NORMAL, " k - keep field on (only if a password is provided too)"); - PrintAndLogEx(NORMAL, " [password] - (32 hex symbols)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu cauth")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu cauth 000102030405060708090a0b0c0d0e0f")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_ucsetpwd(void) { - PrintAndLogEx(NORMAL, "Set 3DES password on Mifare Ultralight-C tag."); - PrintAndLogEx(NORMAL, "Usage: hf mfu setpwd "); - PrintAndLogEx(NORMAL, " [password] - (32 hex symbols)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu setpwd 000102030405060708090a0b0c0d0e0f")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_ucsetuid(void) { - PrintAndLogEx(NORMAL, "Usage: hf mfu setuid "); - PrintAndLogEx(NORMAL, " [uid] - (14 hex symbols)"); - PrintAndLogEx(NORMAL, "\n"); - PrintAndLogEx(NORMAL, "This only works for " _BLUE_("Magic Ultralight") " tags."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu setuid 11223344556677")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_gendiverse(void) { - PrintAndLogEx(NORMAL, "Usage: hf mfu gen [h] [r] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " r : read uid from tag"); - PrintAndLogEx(NORMAL, " : 4 byte UID (optional)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu gen r")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu gen 11223344")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_pwdgen(void) { - PrintAndLogEx(NORMAL, "Usage: hf mfu pwdgen [h|t] [r] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h : this help"); - PrintAndLogEx(NORMAL, " t : selftest"); - PrintAndLogEx(NORMAL, " r : read uid from tag"); - PrintAndLogEx(NORMAL, " : 7 byte UID (optional)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu pwdgen r")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu pwdgen 11223344556677")); - PrintAndLogEx(NORMAL, _YELLOW_(" hf mfu pwdgen t")); - PrintAndLogEx(NORMAL, ""); - return PM3_SUCCESS; -} - -static int usage_hf_mfu_otp_tearoff(void) { - PrintAndLogEx(NORMAL, "Tear-off test against OTP block (no 3) on MFU tags - More help sooner or later\n"); - PrintAndLogEx(NORMAL, "Usage: hf mfu otptear b i l s d t \n"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " b : (optional) block to run the test - default block: 8 (not OTP for safety)"); - PrintAndLogEx(NORMAL, " i